diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/net/quic | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) |
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/net/quic')
328 files changed, 30539 insertions, 16679 deletions
diff --git a/chromium/net/quic/congestion_control/available_channel_estimator.cc b/chromium/net/quic/congestion_control/available_channel_estimator.cc deleted file mode 100644 index ef091abce43..00000000000 --- a/chromium/net/quic/congestion_control/available_channel_estimator.cc +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/congestion_control/available_channel_estimator.h" - -static const int kNumberOfSamples = 9; - -namespace net { - -AvailableChannelEstimator::AvailableChannelEstimator( - QuicPacketSequenceNumber sequence_number, - QuicTime first_send_time, - QuicTime first_receive_time) - : first_sequence_number_(sequence_number), - first_send_time_(first_send_time), - first_receive_time_(first_receive_time), - last_incorporated_sequence_number_(sequence_number), - last_time_sent_(QuicTime::Zero()), - last_receive_time_(QuicTime::Zero()), - number_of_sequence_numbers_(0), - received_bytes_(0) { -} - -void AvailableChannelEstimator::OnIncomingFeedback( - QuicPacketSequenceNumber sequence_number, - QuicByteCount packet_size, - QuicTime sent_time, - QuicTime receive_time) { - if (sequence_number <= first_sequence_number_) { - // Ignore pre-probe feedback. - return; - } - if (sequence_number <= last_incorporated_sequence_number_) { - // Ignore old feedback; will remove duplicates. - return; - } - // Remember the highest received sequence number. - last_incorporated_sequence_number_ = sequence_number; - if (number_of_sequence_numbers_ < kNumberOfSamples) { - // We don't care how many sequence numbers we have after we pass - // kNumberOfSamples. - number_of_sequence_numbers_++; - } - last_receive_time_ = receive_time; - last_time_sent_ = sent_time; - received_bytes_ += packet_size; - // TODO(pwestin): the variance here should give us information about accuracy. -} - -AvailableChannelEstimateState - AvailableChannelEstimator::GetAvailableChannelEstimate( - QuicBandwidth* bandwidth) const { - if (number_of_sequence_numbers_ < 2) { - return kAvailableChannelEstimateUnknown; - } - QuicTime::Delta send_delta = last_time_sent_.Subtract(first_send_time_); - QuicTime::Delta receive_delta = - last_receive_time_.Subtract(first_receive_time_); - - // TODO(pwestin): room for improvement here. Keeping it simple for now. - *bandwidth = QuicBandwidth::FromBytesAndTimeDelta(received_bytes_, - receive_delta); - - QuicTime::Delta diff = receive_delta.Subtract(send_delta); - QuicTime::Delta ten_percent_of_send_time = - QuicTime::Delta::FromMicroseconds(send_delta.ToMicroseconds() / 10); - - if (diff < ten_percent_of_send_time) { - return kAvailableChannelEstimateSenderLimited; - } - if (number_of_sequence_numbers_ < kNumberOfSamples) { - return kAvailableChannelEstimateUncertain; - } - return kAvailableChannelEstimateGood; -} - -} // namespace net diff --git a/chromium/net/quic/congestion_control/available_channel_estimator.h b/chromium/net/quic/congestion_control/available_channel_estimator.h deleted file mode 100644 index e2ad19a8b66..00000000000 --- a/chromium/net/quic/congestion_control/available_channel_estimator.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Based on the inter arrival time of the received packets relative to the time -// those packets where sent we can estimate the available capacity of the -// channel. -// We can only use packet trains that are sent out faster than the acctual -// available channel capacity. -// Note 1: this is intended to be a temporary class created when you send out a -// channel probing burst. Once the last packet have arrived you ask it for an -// estimate. -// Note 2: During this phase we should not update the overuse detector. -#ifndef NET_QUIC_CONGESTION_CONTROL_AVAILABLE_CHANNEL_ESTIMATOR_H_ -#define NET_QUIC_CONGESTION_CONTROL_AVAILABLE_CHANNEL_ESTIMATOR_H_ - -#include "base/basictypes.h" -#include "net/base/net_export.h" -#include "net/quic/quic_bandwidth.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_time.h" - -namespace net { - -enum NET_EXPORT_PRIVATE AvailableChannelEstimateState { - kAvailableChannelEstimateUnknown = 0, - kAvailableChannelEstimateUncertain = 1, - kAvailableChannelEstimateGood = 2, - kAvailableChannelEstimateSenderLimited = 3, -}; - -class NET_EXPORT_PRIVATE AvailableChannelEstimator { - public: - explicit AvailableChannelEstimator( - QuicPacketSequenceNumber first_sequence_number, - QuicTime first_send_time, - QuicTime first_receive_time); - - // Update the statistics with each receive time, for every packet we get a - // feedback message for. - void OnIncomingFeedback(QuicPacketSequenceNumber sequence_number, - QuicByteCount packet_size, - QuicTime sent_time, - QuicTime receive_time); - - // Get the current estimated available channel capacity. - // bandwidth_estimate is invalid if kAvailableChannelEstimateUnknown - // is returned. - AvailableChannelEstimateState GetAvailableChannelEstimate( - QuicBandwidth* bandwidth_estimate) const; - - private: - const QuicPacketSequenceNumber first_sequence_number_; - const QuicTime first_send_time_; - const QuicTime first_receive_time_; - QuicPacketSequenceNumber last_incorporated_sequence_number_; - QuicTime last_time_sent_; - QuicTime last_receive_time_; - int number_of_sequence_numbers_; - QuicByteCount received_bytes_; -}; - -} // namespace net -#endif // NET_QUIC_CONGESTION_CONTROL_AVAILABLE_CHANNEL_ESTIMATOR_H_ diff --git a/chromium/net/quic/congestion_control/available_channel_estimator_test.cc b/chromium/net/quic/congestion_control/available_channel_estimator_test.cc deleted file mode 100644 index b4a4b9c341c..00000000000 --- a/chromium/net/quic/congestion_control/available_channel_estimator_test.cc +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "net/quic/congestion_control/available_channel_estimator.h" -#include "net/quic/test_tools/mock_clock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { -namespace test { - -class AvailableChannelEstimatorTest : public ::testing::Test { - protected: - virtual void SetUp() { - srand(1234); - packet_size_ = 1200; - sequence_number_ = 1; - QuicTime receive_time = receive_clock_.Now(); - QuicTime sent_time = send_clock_.Now(); - estimator_.reset(new AvailableChannelEstimator(sequence_number_, - sent_time, - receive_time)); - } - - MockClock send_clock_; - MockClock receive_clock_; - QuicPacketSequenceNumber sequence_number_; - QuicByteCount packet_size_; - scoped_ptr<AvailableChannelEstimator> estimator_; -}; - -TEST_F(AvailableChannelEstimatorTest, SimpleBasic) { - QuicBandwidth bandwidth = QuicBandwidth::Zero(); - QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10); - QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(1); - receive_clock_.AdvanceTime(received_delta); - send_clock_.AdvanceTime(send_delta); - QuicTime receive_time = receive_clock_.Now(); - QuicTime sent_time = send_clock_.Now(); - estimator_->OnIncomingFeedback(++sequence_number_, - packet_size_, - sent_time, - receive_time); - EXPECT_EQ(kAvailableChannelEstimateUnknown, - estimator_->GetAvailableChannelEstimate(&bandwidth)); - - receive_clock_.AdvanceTime(received_delta); - receive_time = receive_clock_.Now(); - send_clock_.AdvanceTime(send_delta); - sent_time = send_clock_.Now(); - - estimator_->OnIncomingFeedback(++sequence_number_, - packet_size_, - sent_time, - receive_time); - EXPECT_EQ(kAvailableChannelEstimateUncertain, - estimator_->GetAvailableChannelEstimate(&bandwidth)); - - EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta), - bandwidth); -} - -// TODO(pwestin): simulate cross traffic. -TEST_F(AvailableChannelEstimatorTest, SimpleUncertainEstimate) { - QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10); - QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(1); - - for (int i = 0; i < 8; ++i) { - receive_clock_.AdvanceTime(received_delta); - QuicTime receive_time = receive_clock_.Now(); - send_clock_.AdvanceTime(send_delta); - QuicTime sent_time = send_clock_.Now(); - estimator_->OnIncomingFeedback(++sequence_number_, - packet_size_, - sent_time, - receive_time); - } - QuicBandwidth bandwidth = QuicBandwidth::Zero(); - EXPECT_EQ(kAvailableChannelEstimateUncertain, - estimator_->GetAvailableChannelEstimate(&bandwidth)); - EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta), - bandwidth); -} - -TEST_F(AvailableChannelEstimatorTest, SimpleGoodEstimate) { - QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10); - QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(1); - - for (int i = 0; i < 100; ++i) { - receive_clock_.AdvanceTime(received_delta); - QuicTime receive_time = receive_clock_.Now(); - send_clock_.AdvanceTime(send_delta); - QuicTime sent_time = send_clock_.Now(); - estimator_->OnIncomingFeedback(++sequence_number_, - packet_size_, - sent_time, - receive_time); - } - QuicBandwidth bandwidth = QuicBandwidth::Zero(); - EXPECT_EQ(kAvailableChannelEstimateGood, - estimator_->GetAvailableChannelEstimate(&bandwidth)); - EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta), - bandwidth); -} - -} // namespace test -} // namespace net diff --git a/chromium/net/quic/congestion_control/channel_estimator.cc b/chromium/net/quic/congestion_control/channel_estimator.cc deleted file mode 100644 index a012b666a39..00000000000 --- a/chromium/net/quic/congestion_control/channel_estimator.cc +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/congestion_control/channel_estimator.h" - -// To get information about bandwidth, our send rate for a pair of packets must -// be much faster (ideally back to back) than the receive rate. In that -// scenario, the arriving packet pair will tend to arrive at the max bandwidth -// of the channel. Said another way, when our inter-departure time is a small -// fraction of the inter-arrival time for the same pair of packets, then we can -// get an estimate of bandwidth from that interarrival time. The following -// constant is the threshold ratio for deriving bandwidth information. -static const int kInterarrivalRatioThresholdForBandwidthEstimation = 5; -static const size_t kMinNumberOfSamples = 10; -static const size_t kMaxNumberOfSamples = 100; - -namespace net { - -ChannelEstimator::ChannelEstimator() - : last_sequence_number_(0), - last_send_time_(QuicTime::Zero()), - last_receive_time_(QuicTime::Zero()), - sorted_bitrate_estimates_(kMaxNumberOfSamples) { -} - -ChannelEstimator::~ChannelEstimator() { -} - -void ChannelEstimator::OnAcknowledgedPacket( - QuicPacketSequenceNumber sequence_number, - QuicByteCount packet_size, - QuicTime send_time, - QuicTime receive_time) { - if (last_sequence_number_ > sequence_number) { - // Old packet. The sequence_number use the full 64 bits even though it's - // less on the wire. - return; - } - if (last_sequence_number_ != sequence_number - 1) { - DVLOG(1) << "Skip channel estimator due to lost packet(s)"; - } else if (last_send_time_.IsInitialized()) { - QuicTime::Delta sent_delta = send_time.Subtract(last_send_time_); - QuicTime::Delta received_delta = receive_time.Subtract(last_receive_time_); - if (received_delta.ToMicroseconds() > - kInterarrivalRatioThresholdForBandwidthEstimation * - sent_delta.ToMicroseconds()) { - UpdateFilter(received_delta, packet_size, sequence_number); - } - } - last_sequence_number_ = sequence_number; - last_send_time_ = send_time; - last_receive_time_ = receive_time; -} - -ChannelEstimateState ChannelEstimator::GetChannelEstimate( - QuicBandwidth* estimate) const { - if (sorted_bitrate_estimates_.Size() < kMinNumberOfSamples) { - // Not enough data to make an estimate. - return kChannelEstimateUnknown; - } - // Our data is stored in a sorted map, we need to iterate through our map to - // find the estimated bitrates at our targeted percentiles. - - // Calculate 25th percentile. - size_t beginning_window = sorted_bitrate_estimates_.Size() / 4; - // Calculate 50th percentile. - size_t median = sorted_bitrate_estimates_.Size() / 2; - // Calculate 75th percentile. - size_t end_window = sorted_bitrate_estimates_.Size() - beginning_window; - - QuicBandwidth bitrate_25th_percentile = QuicBandwidth::Zero(); - QuicBandwidth median_bitrate = QuicBandwidth::Zero(); - QuicBandwidth bitrate_75th_percentile = QuicBandwidth::Zero(); - QuicMaxSizedMap<QuicBandwidth, QuicPacketSequenceNumber>::ConstIterator it = - sorted_bitrate_estimates_.Begin(); - - for (size_t i = 0; i <= end_window; ++i, ++it) { - DCHECK(it != sorted_bitrate_estimates_.End()); - if (i == beginning_window) { - bitrate_25th_percentile = it->first; - } - if (i == median) { - median_bitrate = it->first; - } - if (i == end_window) { - bitrate_75th_percentile = it->first; - } - } - *estimate = median_bitrate; - DVLOG(1) << "Channel estimate is:" - << median_bitrate.ToKBitsPerSecond() << " Kbit/s"; - // If the bitrates in our 25th to 75th percentile window varies more than - // 25% of the median bitrate we consider the estimate to be uncertain. - if (bitrate_75th_percentile.Subtract(bitrate_25th_percentile) > - median_bitrate.Scale(0.25f)) { - return kChannelEstimateUncertain; - } - return kChannelEstimateGood; -} - -void ChannelEstimator::UpdateFilter(QuicTime::Delta received_delta, - QuicByteCount size_delta, - QuicPacketSequenceNumber sequence_number) { - QuicBandwidth estimate = - QuicBandwidth::FromBytesAndTimeDelta(size_delta, received_delta); - sorted_bitrate_estimates_.Insert(estimate, sequence_number); -} - -} // namespace net diff --git a/chromium/net/quic/congestion_control/channel_estimator.h b/chromium/net/quic/congestion_control/channel_estimator.h deleted file mode 100644 index 9be8031145b..00000000000 --- a/chromium/net/quic/congestion_control/channel_estimator.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Based on the inter arrival time of the received packets relative to the time -// those packets where sent we can estimate the max capacity of the channel. -// We can only use packet pair that are sent out faster than the acctual -// channel capacity. -#ifndef NET_QUIC_CONGESTION_CONTROL_CHANNEL_ESTIMATOR_H_ -#define NET_QUIC_CONGESTION_CONTROL_CHANNEL_ESTIMATOR_H_ - -#include <list> -#include <map> - -#include "base/basictypes.h" -#include "net/base/net_export.h" -#include "net/quic/congestion_control/quic_max_sized_map.h" -#include "net/quic/quic_bandwidth.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_time.h" - -namespace net { - -enum ChannelEstimateState { - kChannelEstimateUnknown = 0, - kChannelEstimateUncertain = 1, - kChannelEstimateGood = 2 -}; - -class NET_EXPORT_PRIVATE ChannelEstimator { - public: - ChannelEstimator(); - ~ChannelEstimator(); - - // This method should be called each time we acquire a receive time for a - // packet we previously sent. It calculates deltas between consecutive - // receive times, and may use that to update the channel bandwidth estimate. - void OnAcknowledgedPacket(QuicPacketSequenceNumber sequence_number, - QuicByteCount packet_size, - QuicTime send_time, - QuicTime receive_time); - - // Get the current estimated state and channel capacity. - // Note: estimate will not be valid when kChannelEstimateUnknown is returned. - ChannelEstimateState GetChannelEstimate(QuicBandwidth* estimate) const; - - private: - void UpdateFilter(QuicTime::Delta received_delta, QuicByteCount size_delta, - QuicPacketSequenceNumber sequence_number); - - QuicPacketSequenceNumber last_sequence_number_; - QuicTime last_send_time_; - QuicTime last_receive_time_; - QuicMaxSizedMap<QuicBandwidth, QuicPacketSequenceNumber> - sorted_bitrate_estimates_; - - DISALLOW_COPY_AND_ASSIGN(ChannelEstimator); -}; - -} // namespace net -#endif // NET_QUIC_CONGESTION_CONTROL_CHANNEL_ESTIMATOR_H_ diff --git a/chromium/net/quic/congestion_control/channel_estimator_test.cc b/chromium/net/quic/congestion_control/channel_estimator_test.cc deleted file mode 100644 index 460e436589e..00000000000 --- a/chromium/net/quic/congestion_control/channel_estimator_test.cc +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/logging.h" -#include "net/quic/congestion_control/channel_estimator.h" -#include "net/quic/test_tools/mock_clock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { -namespace test { - -class ChannelEstimatorTest : public ::testing::Test { - protected: - virtual void SetUp() { - srand(1234); - packet_size_ = 1200; - sequence_number_ = 1; - } - - QuicPacketSequenceNumber sequence_number_; - QuicByteCount packet_size_; - MockClock send_clock_; - MockClock receive_clock_; - ChannelEstimator channel_estimator_; -}; - -TEST_F(ChannelEstimatorTest, SimpleNonDetect) { - // In this test, the send times differ by the same delta as the receive times, - // so we haven't sent packets closely enough to detect "spreading," or - // effective bandwidth. - QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10); - - for (int i = 0; i < 1000; ++i) { - QuicTime send_time = send_clock_.ApproximateNow(); - QuicTime receive_time = receive_clock_.ApproximateNow(); - channel_estimator_.OnAcknowledgedPacket(sequence_number_++, - packet_size_, - send_time, - receive_time); - send_clock_.AdvanceTime(delta); - receive_clock_.AdvanceTime(delta); - } - QuicBandwidth estimate = QuicBandwidth::Zero(); - EXPECT_EQ(kChannelEstimateUnknown, - channel_estimator_.GetChannelEstimate(&estimate)); - EXPECT_TRUE(estimate.IsZero()); -} - -TEST_F(ChannelEstimatorTest, SimplePacketPairDetect) { - // In this test, we start by sending packet pairs back-to-back and - // add a receive side spreading that indicate an effective bandwidth. - // We do 2 testes with different effective bandwidth to make sure that we - // detect the new effective bandwidth. - QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(5); - QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10); - - for (int i = 0; i < 100; ++i) { - receive_clock_.AdvanceTime(received_delta); - QuicTime receive_time = receive_clock_.ApproximateNow(); - QuicTime send_time = send_clock_.ApproximateNow(); - - channel_estimator_.OnAcknowledgedPacket(sequence_number_++, - packet_size_, - send_time, - receive_time); - receive_clock_.AdvanceTime(received_delta); - receive_time = receive_clock_.ApproximateNow(); - channel_estimator_.OnAcknowledgedPacket(sequence_number_++, - packet_size_, - send_time, - receive_time); - send_clock_.AdvanceTime(send_delta); - } - QuicBandwidth estimate = QuicBandwidth::Zero(); - EXPECT_EQ(kChannelEstimateGood, - channel_estimator_.GetChannelEstimate(&estimate)); - EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta), - estimate); - received_delta = QuicTime::Delta::FromMilliseconds(1); - for (int i = 0; i < 100; ++i) { - receive_clock_.AdvanceTime(received_delta); - QuicTime receive_time = receive_clock_.ApproximateNow(); - QuicTime send_time = send_clock_.ApproximateNow(); - channel_estimator_.OnAcknowledgedPacket(sequence_number_++, - packet_size_, - send_time, - receive_time); - receive_clock_.AdvanceTime(received_delta); - receive_time = receive_clock_.ApproximateNow(); - channel_estimator_.OnAcknowledgedPacket(sequence_number_++, - packet_size_, - send_time, - receive_time); - send_clock_.AdvanceTime(send_delta); - } - EXPECT_EQ(kChannelEstimateGood, - channel_estimator_.GetChannelEstimate(&estimate)); - EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta), - estimate); -} - -TEST_F(ChannelEstimatorTest, SimpleFlatSlope) { - // In this test, we send packet pairs back-to-back and add a slowly increasing - // receive side spreading. We expect the estimate to be good and that our - // mean receive side spreading is returned as the estimate. - QuicTime::Delta initial_received_delta = QuicTime::Delta::FromMilliseconds(5); - QuicTime::Delta received_delta = initial_received_delta; - QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10); - - for (int i = 0; i < 100; ++i) { - receive_clock_.AdvanceTime(received_delta); - QuicTime receive_time = receive_clock_.ApproximateNow(); - QuicTime send_time = send_clock_.ApproximateNow(); - channel_estimator_.OnAcknowledgedPacket(sequence_number_++, - packet_size_, - send_time, - receive_time); - receive_clock_.AdvanceTime(received_delta); - receive_time = receive_clock_.ApproximateNow(); - channel_estimator_.OnAcknowledgedPacket(sequence_number_++, - packet_size_, - send_time, - receive_time); - send_clock_.AdvanceTime(send_delta); - received_delta = received_delta.Add(QuicTime::Delta::FromMicroseconds(10)); - } - QuicBandwidth estimate = QuicBandwidth::Zero(); - EXPECT_EQ(kChannelEstimateGood, - channel_estimator_.GetChannelEstimate(&estimate)); - - // Calculate our mean receive delta. - QuicTime::Delta increased_received_delta = - received_delta.Subtract(initial_received_delta); - QuicTime::Delta mean_received_delta = initial_received_delta.Add( - QuicTime::Delta::FromMicroseconds( - increased_received_delta.ToMicroseconds() / 2)); - - EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, - mean_received_delta), estimate); -} - -TEST_F(ChannelEstimatorTest, SimpleMediumSlope) { - // In this test, we send packet pairs back-to-back and add an increasing - // receive side spreading. We expect the estimate to be uncertaint and that - // our mean receive side spreading is returned as the estimate. - QuicTime::Delta initial_received_delta = QuicTime::Delta::FromMilliseconds(5); - QuicTime::Delta received_delta = initial_received_delta; - QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10); - - for (int i = 0; i < 100; ++i) { - receive_clock_.AdvanceTime(received_delta); - QuicTime receive_time = receive_clock_.ApproximateNow(); - QuicTime send_time = send_clock_.ApproximateNow(); - channel_estimator_.OnAcknowledgedPacket(sequence_number_++, - packet_size_, - send_time, - receive_time); - receive_clock_.AdvanceTime(received_delta); - receive_time = receive_clock_.ApproximateNow(); - channel_estimator_.OnAcknowledgedPacket(sequence_number_++, - packet_size_, - send_time, - receive_time); - send_clock_.AdvanceTime(send_delta); - received_delta = received_delta.Add(QuicTime::Delta::FromMicroseconds(50)); - } - QuicBandwidth estimate = QuicBandwidth::Zero(); - EXPECT_EQ(kChannelEstimateUncertain, - channel_estimator_.GetChannelEstimate(&estimate)); - - // Calculate our mean receive delta. - QuicTime::Delta increased_received_delta = - received_delta.Subtract(initial_received_delta); - QuicTime::Delta mean_received_delta = initial_received_delta.Add( - QuicTime::Delta::FromMicroseconds( - increased_received_delta.ToMicroseconds() / 2)); - - EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, - mean_received_delta), estimate); -} - -TEST_F(ChannelEstimatorTest, SimpleSteepSlope) { - // In this test, we send packet pairs back-to-back and add a rapidly - // increasing receive side spreading. We expect the estimate to be uncertain - // and that our mean receive side spreading is returned as the estimate. - QuicTime::Delta initial_received_delta = QuicTime::Delta::FromMilliseconds(5); - QuicTime::Delta received_delta = initial_received_delta; - QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10); - - for (int i = 0; i < 100; ++i) { - receive_clock_.AdvanceTime(received_delta); - QuicTime receive_time = receive_clock_.ApproximateNow(); - QuicTime send_time = send_clock_.ApproximateNow(); - channel_estimator_.OnAcknowledgedPacket(sequence_number_++, - packet_size_, - send_time, - receive_time); - receive_clock_.AdvanceTime(received_delta); - receive_time = receive_clock_.ApproximateNow(); - channel_estimator_.OnAcknowledgedPacket(sequence_number_++, - packet_size_, - send_time, - receive_time); - send_clock_.AdvanceTime(send_delta); - received_delta = received_delta.Add(QuicTime::Delta::FromMicroseconds(100)); - } - QuicBandwidth estimate = QuicBandwidth::Zero(); - EXPECT_EQ(kChannelEstimateUncertain, - channel_estimator_.GetChannelEstimate(&estimate)); - - // Calculate our mean receive delta. - QuicTime::Delta increased_received_delta = - received_delta.Subtract(initial_received_delta); - QuicTime::Delta mean_received_delta = initial_received_delta.Add( - QuicTime::Delta::FromMicroseconds( - increased_received_delta.ToMicroseconds() / 2)); - - EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, - mean_received_delta), estimate); -} - -} // namespace test -} // namespace net diff --git a/chromium/net/quic/congestion_control/cube_root.h b/chromium/net/quic/congestion_control/cube_root.h index 3b3736c1c59..293a7199f86 100644 --- a/chromium/net/quic/congestion_control/cube_root.h +++ b/chromium/net/quic/congestion_control/cube_root.h @@ -15,6 +15,9 @@ class NET_EXPORT_PRIVATE CubeRoot { // Calculates the cube root using a table lookup followed by one Newton- // Raphson iteration. static uint32 Root(uint64 a); + + private: + DISALLOW_COPY_AND_ASSIGN(CubeRoot); }; } // namespace net diff --git a/chromium/net/quic/congestion_control/cubic.cc b/chromium/net/quic/congestion_control/cubic.cc index 3d03b9f60d1..f64e58b05b9 100644 --- a/chromium/net/quic/congestion_control/cubic.cc +++ b/chromium/net/quic/congestion_control/cubic.cc @@ -9,9 +9,15 @@ #include "base/basictypes.h" #include "base/logging.h" #include "base/time/time.h" +#include "net/quic/congestion_control/cube_root.h" +#include "net/quic/quic_protocol.h" + +using std::max; namespace net { +namespace { + // Constants based on TCP defaults. // The following constants are in 2^10 fractions of a second instead of ms to // allow a 10 shift right to divide. @@ -21,93 +27,37 @@ const int kCubeScale = 40; // 1024*1024^3 (first 1024 is from 0.100^3) const int kCubeCongestionWindowScale = 410; const uint64 kCubeFactor = (GG_UINT64_C(1) << kCubeScale) / kCubeCongestionWindowScale; -const uint32 kBetaSPDY = 939; // Back off factor after loss for SPDY, reduces - // the CWND by 1/12th. -const uint32 kBetaLastMax = 871; // Additional back off factor after loss for - // the stored max value. -namespace { -// Find last bit in a 64-bit word. -int FindMostSignificantBit(uint64 x) { - if (!x) { - return 0; - } - int r = 0; - if (x & 0xffffffff00000000ull) { - x >>= 32; - r += 32; - } - if (x & 0xffff0000u) { - x >>= 16; - r += 16; - } - if (x & 0xff00u) { - x >>= 8; - r += 8; - } - if (x & 0xf0u) { - x >>= 4; - r += 4; - } - if (x & 0xcu) { - x >>= 2; - r += 2; - } - if (x & 0x02u) { - x >>= 1; - r++; - } - if (x & 0x01u) { - r++; - } - return r; -} +const uint32 kNumConnections = 2; +const float kBeta = 0.7f; // Default Cubic backoff factor. +// Additional backoff factor when loss occurs in the concave part of the Cubic +// curve. This additional backoff factor is expected to give up bandwidth to +// new concurrent flows and speed up convergence. +const float kBetaLastMax = 0.85f; + +// kNConnectionBeta is the backoff factor after loss for our N-connection +// emulation, which emulates the effective backoff of an ensemble of N TCP-Reno +// connections on a single loss event. The effective multiplier is computed as: +const float kNConnectionBeta = (kNumConnections - 1 + kBeta) / kNumConnections; + +// TCPFriendly alpha is described in Section 3.3 of the CUBIC paper. Note that +// kBeta here is a cwnd multiplier, and is equal to 1-beta from the CUBIC paper. +// We derive the equivalent kNConnectionAlpha for an N-connection emulation as: +const float kNConnectionAlpha = 3 * kNumConnections * kNumConnections * + (1 - kNConnectionBeta) / (1 + kNConnectionBeta); +// TODO(jri): Compute kNConnectionBeta and kNConnectionAlpha from +// number of active streams. -// 6 bits table [0..63] -const uint32 cube_root_table[] = { - 0, 54, 54, 54, 118, 118, 118, 118, 123, 129, 134, 138, 143, 147, 151, - 156, 157, 161, 164, 168, 170, 173, 176, 179, 181, 185, 187, 190, 192, 194, - 197, 199, 200, 202, 204, 206, 209, 211, 213, 215, 217, 219, 221, 222, 224, - 225, 227, 229, 231, 232, 234, 236, 237, 239, 240, 242, 244, 245, 246, 248, - 250, 251, 252, 254 -}; } // namespace -Cubic::Cubic(const QuicClock* clock) +Cubic::Cubic(const QuicClock* clock, QuicConnectionStats* stats) : clock_(clock), epoch_(QuicTime::Zero()), - last_update_time_(QuicTime::Zero()) { + last_update_time_(QuicTime::Zero()), + stats_(stats) { Reset(); } -// Calculate the cube root using a table lookup followed by one Newton-Raphson -// iteration. -uint32 Cubic::CubeRoot(uint64 a) { - uint32 msb = FindMostSignificantBit(a); - DCHECK_LE(msb, 64u); - - if (msb < 7) { - // MSB in our table. - return ((cube_root_table[static_cast<uint32>(a)]) + 31) >> 6; - } - // MSB 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, ... - // cubic_shift 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, ... - uint32 cubic_shift = (msb - 4); - cubic_shift = ((cubic_shift * 342) >> 10); // Div by 3, biased high. - - // 4 to 6 bits accuracy depending on MSB. - uint32 down_shifted_to_6bit = (a >> (cubic_shift * 3)); - uint64 root = ((cube_root_table[down_shifted_to_6bit] + 10) << cubic_shift) - >> 6; - - // Make one Newton-Raphson iteration. - // Since x has an error (inaccuracy due to the use of fix point) we get a - // more accurate result by doing x * (x - 1) instead of x * x. - root = 2 * root + (a / (root * (root - 1))); - root = ((root * 341) >> 10); // Div by 3, biased low. - return static_cast<uint32>(root); -} - void Cubic::Reset() { epoch_ = QuicTime::Zero(); // Reset time. last_update_time_ = QuicTime::Zero(); // Reset time. @@ -120,18 +70,36 @@ void Cubic::Reset() { last_target_congestion_window_ = 0; } +void Cubic::UpdateCongestionControlStats( + QuicTcpCongestionWindow new_cubic_mode_cwnd, + QuicTcpCongestionWindow new_reno_mode_cwnd) { + + QuicTcpCongestionWindow highest_new_cwnd = std::max(new_cubic_mode_cwnd, + new_reno_mode_cwnd); + if (last_congestion_window_ < highest_new_cwnd) { + // cwnd will increase to highest_new_cwnd. + stats_->cwnd_increase_congestion_avoidance += + highest_new_cwnd - last_congestion_window_; + if (new_cubic_mode_cwnd > new_reno_mode_cwnd) { + // This cwnd increase is due to cubic mode. + stats_->cwnd_increase_cubic_mode += + new_cubic_mode_cwnd - last_congestion_window_; + } + } +} + QuicTcpCongestionWindow Cubic::CongestionWindowAfterPacketLoss( QuicTcpCongestionWindow current_congestion_window) { if (current_congestion_window < last_max_congestion_window_) { // We never reached the old max, so assume we are competing with another // flow. Use our extra back off factor to allow the other flow to go up. last_max_congestion_window_ = - (kBetaLastMax * current_congestion_window) >> 10; + static_cast<int>(kBetaLastMax * current_congestion_window); } else { last_max_congestion_window_ = current_congestion_window; } epoch_ = QuicTime::Zero(); // Reset time. - return (current_congestion_window * kBetaSPDY) >> 10; + return static_cast<int>(current_congestion_window * kNConnectionBeta); } QuicTcpCongestionWindow Cubic::CongestionWindowAfterAck( @@ -143,8 +111,8 @@ QuicTcpCongestionWindow Cubic::CongestionWindowAfterAck( // Cubic is "independent" of RTT, the update is limited by the time elapsed. if (last_congestion_window_ == current_congestion_window && (current_time.Subtract(last_update_time_) <= MaxCubicTimeInterval())) { - return std::max(last_target_congestion_window_, - estimated_tcp_congestion_window_); + return max(last_target_congestion_window_, + estimated_tcp_congestion_window_); } last_congestion_window_ = current_congestion_window; last_update_time_ = current_time; @@ -160,7 +128,7 @@ QuicTcpCongestionWindow Cubic::CongestionWindowAfterAck( time_to_origin_point_ = 0; origin_point_congestion_window_ = current_congestion_window; } else { - time_to_origin_point_ = CubeRoot(kCubeFactor * + time_to_origin_point_ = CubeRoot::Root(kCubeFactor * (last_max_congestion_window_ - current_congestion_window)); origin_point_congestion_window_ = last_max_congestion_window_; @@ -180,22 +148,35 @@ QuicTcpCongestionWindow Cubic::CongestionWindowAfterAck( QuicTcpCongestionWindow target_congestion_window = origin_point_congestion_window_ - delta_congestion_window; + DCHECK_LT(0u, estimated_tcp_congestion_window_); + // With dynamic beta/alpha based on number of active streams, it is possible + // for the required_ack_count to become much lower than acked_packets_count_ + // suddenly, leading to more than one iteration through the following loop. + while (true) { + // Update estimated TCP congestion_window. + uint32 required_ack_count = + estimated_tcp_congestion_window_ / kNConnectionAlpha; + if (acked_packets_count_ < required_ack_count) { + break; + } + acked_packets_count_ -= required_ack_count; + estimated_tcp_congestion_window_++; + } + + // Update cubic mode and reno mode stats in QuicConnectionStats. + UpdateCongestionControlStats(target_congestion_window, + estimated_tcp_congestion_window_); + // We have a new cubic congestion window. last_target_congestion_window_ = target_congestion_window; - // Update estimated TCP congestion_window. - // Note: we do a normal Reno congestion avoidance calculation not the - // calculation described in section 3.3 TCP-friendly region of the document. - while (acked_packets_count_ >= estimated_tcp_congestion_window_) { - acked_packets_count_ -= estimated_tcp_congestion_window_; - estimated_tcp_congestion_window_++; - } // Compute target congestion_window based on cubic target and estimated TCP // congestion_window, use highest (fastest). if (target_congestion_window < estimated_tcp_congestion_window_) { target_congestion_window = estimated_tcp_congestion_window_; } - DVLOG(1) << "Target congestion_window:" << target_congestion_window; + + DVLOG(1) << "Target congestion_window: " << target_congestion_window; return target_congestion_window; } diff --git a/chromium/net/quic/congestion_control/cubic.h b/chromium/net/quic/congestion_control/cubic.h index e365bbe0ed7..da41ddf8ab2 100644 --- a/chromium/net/quic/congestion_control/cubic.h +++ b/chromium/net/quic/congestion_control/cubic.h @@ -11,6 +11,7 @@ #include "base/basictypes.h" #include "net/base/net_export.h" #include "net/quic/quic_clock.h" +#include "net/quic/quic_connection_stats.h" #include "net/quic/quic_time.h" namespace net { @@ -20,7 +21,7 @@ typedef uint32 QuicTcpCongestionWindow; class NET_EXPORT_PRIVATE Cubic { public: - explicit Cubic(const QuicClock* clock); + Cubic(const QuicClock* clock, QuicConnectionStats* stats); // Call after a timeout to reset the cubic state. void Reset(); @@ -39,16 +40,14 @@ class NET_EXPORT_PRIVATE Cubic { QuicTcpCongestionWindow current, QuicTime::Delta delay_min); - protected: - // Calculates the cubic root using a table lookup followed by one Newton- - // Raphson iteration. - uint32 CubeRoot(uint64 a); - private: static const QuicTime::Delta MaxCubicTimeInterval() { return QuicTime::Delta::FromMilliseconds(30); } + // Update congestion control variables in QuicConnectionStats. + void UpdateCongestionControlStats(QuicTcpCongestionWindow new_cubic_mode_cwnd, + QuicTcpCongestionWindow new_reno_mode_cwnd); const QuicClock* clock_; // Time when this cycle started, after last loss event. @@ -80,8 +79,12 @@ class NET_EXPORT_PRIVATE Cubic { // Last congestion window in packets computed by cubic function. QuicTcpCongestionWindow last_target_congestion_window_; + // QuicConnectionStats includes congestion control related stats. + QuicConnectionStats* stats_; + DISALLOW_COPY_AND_ASSIGN(Cubic); }; } // namespace net + #endif // NET_QUIC_CONGESTION_CONTROL_CUBIC_H_ diff --git a/chromium/net/quic/congestion_control/cubic_test.cc b/chromium/net/quic/congestion_control/cubic_test.cc index c01bb050643..915274a13d3 100644 --- a/chromium/net/quic/congestion_control/cubic_test.cc +++ b/chromium/net/quic/congestion_control/cubic_test.cc @@ -4,66 +4,35 @@ #include "base/basictypes.h" #include "base/logging.h" -#include "base/memory/scoped_ptr.h" #include "net/quic/congestion_control/cubic.h" +#include "net/quic/quic_connection_stats.h" #include "net/quic/test_tools/mock_clock.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { namespace test { -class CubicPeer : public Cubic { - public: - explicit CubicPeer(QuicClock* clock) - : Cubic(clock) { - } - using Cubic::CubeRoot; -}; +const float kBeta = 0.7f; // Default Cubic backoff factor. +const uint32 kNumConnections = 2; +const float kNConnectionBeta = (kNumConnections - 1 + kBeta) / kNumConnections; +const float kNConnectionAlpha = 3 * kNumConnections * kNumConnections * + (1 - kNConnectionBeta) / (1 + kNConnectionBeta); class CubicTest : public ::testing::Test { protected: CubicTest() : one_ms_(QuicTime::Delta::FromMilliseconds(1)), - hundred_ms_(QuicTime::Delta::FromMilliseconds(100)) { - } - virtual void SetUp() { - cubic_.reset(new CubicPeer(&clock_)); + hundred_ms_(QuicTime::Delta::FromMilliseconds(100)), + cubic_(&clock_, &stats_) { } const QuicTime::Delta one_ms_; const QuicTime::Delta hundred_ms_; MockClock clock_; - scoped_ptr<CubicPeer> cubic_; + QuicConnectionStats stats_; + Cubic cubic_; }; -TEST_F(CubicTest, CubeRootLow) { - for (uint32 i = 1; i < 256; ++i) { - uint64 cube = i * i * i; - uint8 cube_root = cubic_->CubeRoot(cube); - EXPECT_EQ(i, cube_root); - } -} - -TEST_F(CubicTest, CubeRootHigh) { - // Test the range we will opperate in, 1300 to 130 000. - // We expect some loss in accuracy, accepting +-0.2%. - for (uint64 i = 1300; i < 20000; i += 100) { - uint64 cube = i * i * i; - uint32 cube_root = cubic_->CubeRoot(cube); - uint32 margin = cube_root >> 9; // Calculate 0.2% roughly by - // dividing by 512. - EXPECT_LE(i - margin, cube_root); - EXPECT_GE(i + margin, cube_root); - } - for (uint64 i = 20000; i < 130000; i *= 2) { - uint64 cube = i * i * i; - uint32 cube_root = cubic_->CubeRoot(cube); - uint32 margin = cube_root >> 9; - EXPECT_LE(i - margin, cube_root); - EXPECT_GE(i + margin, cube_root); - } -} - -TEST_F(CubicTest, AboveOrgin) { +TEST_F(CubicTest, AboveOrigin) { // Convex growth. const QuicTime::Delta rtt_min = hundred_ms_; uint32 current_cwnd = 10; @@ -71,36 +40,84 @@ TEST_F(CubicTest, AboveOrgin) { // Initialize the state. clock_.AdvanceTime(one_ms_); EXPECT_EQ(expected_cwnd, - cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min)); + cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min)); current_cwnd = expected_cwnd; // Normal TCP phase. for (int i = 0; i < 48; ++i) { - for (uint32 n = 1; n < current_cwnd; ++n) { + for (uint32 n = 1; n < current_cwnd / kNConnectionAlpha; ++n) { // Call once per ACK. - EXPECT_EQ(current_cwnd, - cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min)); + EXPECT_NEAR(current_cwnd, + cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min), 1); } clock_.AdvanceTime(hundred_ms_); - current_cwnd = cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min); - EXPECT_EQ(expected_cwnd, current_cwnd); + current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min); + EXPECT_NEAR(expected_cwnd, current_cwnd, 1); expected_cwnd++; } // Cubic phase. - for (int j = 48; j < 100; ++j) { + for (int i = 0; i < 52; ++i) { for (uint32 n = 1; n < current_cwnd; ++n) { // Call once per ACK. EXPECT_EQ(current_cwnd, - cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min)); + cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min)); } clock_.AdvanceTime(hundred_ms_); - current_cwnd = cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min); + current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min); } - float elapsed_time_s = 10.0f + 0.1f; // We need to add the RTT here. + // Total time elapsed so far; add min_rtt (0.1s) here as well. + float elapsed_time_s = 10.0f + 0.1f; + // |expected_cwnd| is initial value of cwnd + K * t^3, where K = 0.4. expected_cwnd = 11 + (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410) / 1024; EXPECT_EQ(expected_cwnd, current_cwnd); } +TEST_F(CubicTest, CwndIncreaseStatsDuringConvexRegion) { + const QuicTime::Delta rtt_min = hundred_ms_; + uint32 current_cwnd = 10; + uint32 expected_cwnd = current_cwnd + 1; + // Initialize controller state. + clock_.AdvanceTime(one_ms_); + expected_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min); + current_cwnd = expected_cwnd; + // Testing Reno mode increase. + for (int i = 0; i < 48; ++i) { + for (uint32 n = 1; n < current_cwnd / kNConnectionAlpha; ++n) { + // Call once per ACK, causing cwnd growth in Reno mode. + cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min); + } + // Advance current time so that cwnd update is allowed to happen by Cubic. + clock_.AdvanceTime(hundred_ms_); + current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min); + EXPECT_NEAR(expected_cwnd - 10, stats_.cwnd_increase_congestion_avoidance, + 1); + EXPECT_NEAR(1u, stats_.cwnd_increase_cubic_mode, 1); + expected_cwnd++; + } + uint32 old_cwnd = current_cwnd; + stats_.cwnd_increase_cubic_mode = 0; + stats_.cwnd_increase_congestion_avoidance = 0; + + // Testing Cubic mode increase. + for (int i = 0; i < 52; ++i) { + for (uint32 n = 1; n < current_cwnd; ++n) { + // Call once per ACK. + cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min); + } + clock_.AdvanceTime(hundred_ms_); + current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min); + } + // Total time elapsed so far; add min_rtt (0.1s) here as well. + float elapsed_time_s = 10.0f + 0.1f; + // |expected_cwnd| is initial value of cwnd + K * t^3, where K = 0.4. + expected_cwnd = 11 + (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410) + / 1024; + EXPECT_EQ(expected_cwnd - old_cwnd, stats_.cwnd_increase_cubic_mode); + EXPECT_EQ(expected_cwnd - old_cwnd, + stats_.cwnd_increase_congestion_avoidance); +} + + TEST_F(CubicTest, LossEvents) { const QuicTime::Delta rtt_min = hundred_ms_; uint32 current_cwnd = 422; @@ -108,16 +125,16 @@ TEST_F(CubicTest, LossEvents) { // Initialize the state. clock_.AdvanceTime(one_ms_); EXPECT_EQ(expected_cwnd, - cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min)); - expected_cwnd = current_cwnd * 939 / 1024; + cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min)); + expected_cwnd = static_cast<int>(current_cwnd * kNConnectionBeta); EXPECT_EQ(expected_cwnd, - cubic_->CongestionWindowAfterPacketLoss(current_cwnd)); - expected_cwnd = current_cwnd * 939 / 1024; + cubic_.CongestionWindowAfterPacketLoss(current_cwnd)); + expected_cwnd = static_cast<int>(current_cwnd * kNConnectionBeta); EXPECT_EQ(expected_cwnd, - cubic_->CongestionWindowAfterPacketLoss(current_cwnd)); + cubic_.CongestionWindowAfterPacketLoss(current_cwnd)); } -TEST_F(CubicTest, BelowOrgin) { +TEST_F(CubicTest, BelowOrigin) { // Concave growth. const QuicTime::Delta rtt_min = hundred_ms_; uint32 current_cwnd = 422; @@ -125,26 +142,27 @@ TEST_F(CubicTest, BelowOrgin) { // Initialize the state. clock_.AdvanceTime(one_ms_); EXPECT_EQ(expected_cwnd, - cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min)); - expected_cwnd = current_cwnd * 939 / 1024; + cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min)); + expected_cwnd = static_cast<int>(current_cwnd * kNConnectionBeta); EXPECT_EQ(expected_cwnd, - cubic_->CongestionWindowAfterPacketLoss(current_cwnd)); + cubic_.CongestionWindowAfterPacketLoss(current_cwnd)); current_cwnd = expected_cwnd; - // First update after epoch. - current_cwnd = cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min); + // First update after loss to initialize the epoch. + current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min); + uint32 old_cwnd = current_cwnd; // Cubic phase. - for (int i = 0; i < 54; ++i) { - for (uint32 n = 1; n < current_cwnd; ++n) { - // Call once per ACK. - EXPECT_EQ(current_cwnd, - cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min)); - } + stats_.cwnd_increase_cubic_mode = 0; + stats_.cwnd_increase_congestion_avoidance = 0; + for (int i = 0; i < 40 ; ++i) { clock_.AdvanceTime(hundred_ms_); - current_cwnd = cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min); + current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min); } - expected_cwnd = 440; + expected_cwnd = 422; EXPECT_EQ(expected_cwnd, current_cwnd); + EXPECT_EQ(expected_cwnd - old_cwnd, stats_.cwnd_increase_cubic_mode); + EXPECT_EQ(expected_cwnd - old_cwnd, + stats_.cwnd_increase_congestion_avoidance); } -} // namespace testing +} // namespace test } // namespace net diff --git a/chromium/net/quic/congestion_control/fix_rate_receiver.cc b/chromium/net/quic/congestion_control/fix_rate_receiver.cc index 950b49c0d6b..a13362ff226 100644 --- a/chromium/net/quic/congestion_control/fix_rate_receiver.cc +++ b/chromium/net/quic/congestion_control/fix_rate_receiver.cc @@ -27,8 +27,7 @@ bool FixRateReceiver::GenerateCongestionFeedback( void FixRateReceiver::RecordIncomingPacket( QuicByteCount /*bytes*/, QuicPacketSequenceNumber /*sequence_number*/, - QuicTime /*timestamp*/, - bool /*recovered*/) { + QuicTime /*timestamp*/) { // Nothing to do for this simple implementation. } diff --git a/chromium/net/quic/congestion_control/fix_rate_receiver.h b/chromium/net/quic/congestion_control/fix_rate_receiver.h index 690f13880f1..4ed24b993d4 100644 --- a/chromium/net/quic/congestion_control/fix_rate_receiver.h +++ b/chromium/net/quic/congestion_control/fix_rate_receiver.h @@ -30,8 +30,7 @@ class NET_EXPORT_PRIVATE FixRateReceiver : public ReceiveAlgorithmInterface { // Implements ReceiveAlgorithmInterface. virtual void RecordIncomingPacket(QuicByteCount bytes, QuicPacketSequenceNumber sequence_number, - QuicTime timestamp, - bool recovered) OVERRIDE; + QuicTime timestamp) OVERRIDE; private: friend class test::FixRateReceiverPeer; diff --git a/chromium/net/quic/congestion_control/fix_rate_sender.cc b/chromium/net/quic/congestion_control/fix_rate_sender.cc index 8280f1eeb31..c346a63d6ff 100644 --- a/chromium/net/quic/congestion_control/fix_rate_sender.cc +++ b/chromium/net/quic/congestion_control/fix_rate_sender.cc @@ -9,8 +9,11 @@ #include <algorithm> #include "base/logging.h" +#include "net/quic/congestion_control/rtt_stats.h" #include "net/quic/quic_protocol.h" +using std::max; + namespace { const int kInitialBitrate = 100000; // In bytes per second. const uint64 kWindowSizeUs = 10000; // 10 ms. @@ -18,12 +21,11 @@ namespace { namespace net { -FixRateSender::FixRateSender(const QuicClock* clock) - : bitrate_(QuicBandwidth::FromBytesPerSecond(kInitialBitrate)), +FixRateSender::FixRateSender(const RttStats* rtt_stats) + : rtt_stats_(rtt_stats), + bitrate_(QuicBandwidth::FromBytesPerSecond(kInitialBitrate)), max_segment_size_(kDefaultMaxPacketSize), fix_rate_leaky_bucket_(bitrate_), - paced_sender_(bitrate_, max_segment_size_), - data_in_flight_(0), latest_rtt_(QuicTime::Delta::Zero()) { DVLOG(1) << "FixRateSender"; } @@ -34,101 +36,55 @@ FixRateSender::~FixRateSender() { void FixRateSender::SetFromConfig(const QuicConfig& config, bool is_server) { } -void FixRateSender::SetMaxPacketSize(QuicByteCount max_packet_size) { - max_segment_size_ = max_packet_size; - paced_sender_.set_max_segment_size(max_segment_size_); -} - void FixRateSender::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, - QuicTime feedback_receive_time, - const SentPacketsMap& /*sent_packets*/) { - if (feedback.type != kFixRate) { - LOG(DFATAL) << "Invalid incoming CongestionFeedbackType:" << feedback.type; - } + QuicTime feedback_receive_time) { + LOG_IF(DFATAL, feedback.type != kFixRate) << + "Invalid incoming CongestionFeedbackType:" << feedback.type; if (feedback.type == kFixRate) { bitrate_ = feedback.fix_rate.bitrate; fix_rate_leaky_bucket_.SetDrainingRate(feedback_receive_time, bitrate_); - paced_sender_.UpdateBandwidthEstimate(feedback_receive_time, bitrate_); } // Silently ignore invalid messages in release mode. } -void FixRateSender::OnPacketAcked( - QuicPacketSequenceNumber /*acked_sequence_number*/, - QuicByteCount bytes_acked, - QuicTime::Delta rtt) { - // RTT can't be negative. - DCHECK_LE(0, rtt.ToMicroseconds()); - - data_in_flight_ -= bytes_acked; - if (rtt.IsInfinite()) { - return; - } - latest_rtt_ = rtt; -} - -void FixRateSender::OnPacketLost(QuicPacketSequenceNumber /*sequence_number*/, - QuicTime /*ack_receive_time*/) { - // Ignore losses for fix rate sender. +void FixRateSender::OnCongestionEvent(bool rtt_updated, + QuicByteCount bytes_in_flight, + const CongestionMap& acked_packets, + const CongestionMap& lost_packets) { } bool FixRateSender::OnPacketSent( QuicTime sent_time, + QuicByteCount /*bytes_in_flight*/, QuicPacketSequenceNumber /*sequence_number*/, QuicByteCount bytes, - TransmissionType transmission_type, HasRetransmittableData /*has_retransmittable_data*/) { fix_rate_leaky_bucket_.Add(sent_time, bytes); - paced_sender_.OnPacketSent(sent_time, bytes); - if (transmission_type == NOT_RETRANSMISSION) { - data_in_flight_ += bytes; - } + return true; } -void FixRateSender::OnRetransmissionTimeout() { } - -void FixRateSender::OnPacketAbandoned( - QuicPacketSequenceNumber /*sequence_number*/, - QuicByteCount /*abandoned_bytes*/) { -} +void FixRateSender::OnRetransmissionTimeout(bool packets_retransmitted) { } QuicTime::Delta FixRateSender::TimeUntilSend( QuicTime now, - TransmissionType /* transmission_type */, - HasRetransmittableData /*has_retransmittable_data*/, - IsHandshake /*handshake*/) { - if (CongestionWindow() > fix_rate_leaky_bucket_.BytesPending(now)) { - if (CongestionWindow() <= data_in_flight_) { - // We need an ack before we send more. - return QuicTime::Delta::Infinite(); - } - return paced_sender_.TimeUntilSend(now, QuicTime::Delta::Zero()); - } - QuicTime::Delta time_remaining = fix_rate_leaky_bucket_.TimeRemaining(now); - if (time_remaining.IsZero()) { - // We need an ack before we send more. - return QuicTime::Delta::Infinite(); - } - return paced_sender_.TimeUntilSend(now, time_remaining); + QuicByteCount /*bytes_in_flight*/, + HasRetransmittableData /*has_retransmittable_data*/) const { + return fix_rate_leaky_bucket_.TimeRemaining(now); } -QuicByteCount FixRateSender::CongestionWindow() { +QuicByteCount FixRateSender::CongestionWindow() const { QuicByteCount window_size_bytes = bitrate_.ToBytesPerPeriod( QuicTime::Delta::FromMicroseconds(kWindowSizeUs)); // Make sure window size is not less than a packet. - return std::max(kDefaultMaxPacketSize, window_size_bytes); + return max(kDefaultMaxPacketSize, window_size_bytes); } QuicBandwidth FixRateSender::BandwidthEstimate() const { return bitrate_; } -QuicTime::Delta FixRateSender::SmoothedRtt() const { - // TODO(satyamshekhar): Calculate and return smoothed rtt. - return latest_rtt_; -} QuicTime::Delta FixRateSender::RetransmissionDelay() const { // TODO(pwestin): Calculate and return retransmission delay. diff --git a/chromium/net/quic/congestion_control/fix_rate_sender.h b/chromium/net/quic/congestion_control/fix_rate_sender.h index d3d48c7e3da..3cb07ff4ddf 100644 --- a/chromium/net/quic/congestion_control/fix_rate_sender.h +++ b/chromium/net/quic/congestion_control/fix_rate_sender.h @@ -11,58 +11,52 @@ #include "base/compiler_specific.h" #include "net/base/net_export.h" #include "net/quic/quic_clock.h" +#include "net/quic/quic_connection_stats.h" #include "net/quic/quic_time.h" #include "net/quic/congestion_control/leaky_bucket.h" -#include "net/quic/congestion_control/paced_sender.h" #include "net/quic/congestion_control/send_algorithm_interface.h" namespace net { +class RttStats; + class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface { public: - explicit FixRateSender(const QuicClock* clock); + explicit FixRateSender(const RttStats* rtt_stats); virtual ~FixRateSender(); // Start implementation of SendAlgorithmInterface. virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE; - virtual void SetMaxPacketSize(QuicByteCount max_packet_size) OVERRIDE; virtual void OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, - QuicTime feedback_receive_time, - const SentPacketsMap& sent_packets) OVERRIDE; - virtual void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number, - QuicByteCount acked_bytes, - QuicTime::Delta rtt) OVERRIDE; - virtual void OnPacketLost(QuicPacketSequenceNumber sequence_number, - QuicTime ack_receive_time) OVERRIDE; + QuicTime feedback_receive_time) OVERRIDE; + virtual void OnCongestionEvent(bool rtt_updated, + QuicByteCount bytes_in_flight, + const CongestionMap& acked_packets, + const CongestionMap& lost_packets) OVERRIDE; virtual bool OnPacketSent( QuicTime sent_time, + QuicByteCount bytes_in_flight, QuicPacketSequenceNumber sequence_number, QuicByteCount bytes, - TransmissionType transmission_type, HasRetransmittableData has_retransmittable_data) OVERRIDE; - virtual void OnRetransmissionTimeout() OVERRIDE; - virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number, - QuicByteCount abandoned_bytes) OVERRIDE; + virtual void OnRetransmissionTimeout(bool packets_retransmitted) OVERRIDE; virtual QuicTime::Delta TimeUntilSend( QuicTime now, - TransmissionType transmission_type, - HasRetransmittableData has_retransmittable_data, - IsHandshake handshake) OVERRIDE; + QuicByteCount bytes_in_flight, + HasRetransmittableData has_retransmittable_data) const OVERRIDE; virtual QuicBandwidth BandwidthEstimate() const OVERRIDE; - virtual QuicTime::Delta SmoothedRtt() const OVERRIDE; virtual QuicTime::Delta RetransmissionDelay() const OVERRIDE; virtual QuicByteCount GetCongestionWindow() const OVERRIDE; // End implementation of SendAlgorithmInterface. private: - QuicByteCount CongestionWindow(); + QuicByteCount CongestionWindow() const; + const RttStats* rtt_stats_; QuicBandwidth bitrate_; QuicByteCount max_segment_size_; LeakyBucket fix_rate_leaky_bucket_; - PacedSender paced_sender_; - QuicByteCount data_in_flight_; QuicTime::Delta latest_rtt_; DISALLOW_COPY_AND_ASSIGN(FixRateSender); diff --git a/chromium/net/quic/congestion_control/fix_rate_test.cc b/chromium/net/quic/congestion_control/fix_rate_test.cc index 752d37fe641..c82bb60d823 100644 --- a/chromium/net/quic/congestion_control/fix_rate_test.cc +++ b/chromium/net/quic/congestion_control/fix_rate_test.cc @@ -8,6 +8,7 @@ #include "base/memory/scoped_ptr.h" #include "net/quic/congestion_control/fix_rate_receiver.h" #include "net/quic/congestion_control/fix_rate_sender.h" +#include "net/quic/congestion_control/rtt_stats.h" #include "net/quic/quic_protocol.h" #include "net/quic/test_tools/mock_clock.h" #include "testing/gmock/include/gmock/gmock.h" @@ -16,6 +17,9 @@ namespace net { namespace test { +// bytes_in_flight is unused by FixRateSender's OnPacketSent. +QuicByteCount kUnused = 0; + class FixRateReceiverPeer : public FixRateReceiver { public: FixRateReceiverPeer() @@ -29,16 +33,14 @@ class FixRateReceiverPeer : public FixRateReceiver { class FixRateTest : public ::testing::Test { protected: FixRateTest() - : rtt_(QuicTime::Delta::FromMilliseconds(30)), - sender_(new FixRateSender(&clock_)), + : sender_(new FixRateSender(&rtt_stats_)), receiver_(new FixRateReceiverPeer()), start_(clock_.Now()) { // Make sure clock does not start at 0. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2)); } - const QuicTime::Delta rtt_; + RttStats rtt_stats_; MockClock clock_; - SendAlgorithmInterface::SentPacketsMap unused_packet_map_; scoped_ptr<FixRateSender> sender_; scoped_ptr<FixRateReceiverPeer> receiver_; const QuicTime start_; @@ -48,7 +50,7 @@ TEST_F(FixRateTest, ReceiverAPI) { QuicCongestionFeedbackFrame feedback; QuicTime timestamp(QuicTime::Zero()); receiver_->SetBitrate(QuicBandwidth::FromKBytesPerSecond(300)); - receiver_->RecordIncomingPacket(1, 1, timestamp, false); + receiver_->RecordIncomingPacket(1, 1, timestamp); ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); EXPECT_EQ(kFixRate, feedback.type); EXPECT_EQ(300000u, feedback.fix_rate.bitrate.ToBytesPerSecond()); @@ -58,65 +60,41 @@ TEST_F(FixRateTest, SenderAPI) { QuicCongestionFeedbackFrame feedback; feedback.type = kFixRate; feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(300); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), - unused_packet_map_); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); EXPECT_EQ(300000, sender_->BandwidthEstimate().ToBytesPerSecond()); EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - sender_->OnPacketSent(clock_.Now(), 1, kDefaultMaxPacketSize, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); + 0, + HAS_RETRANSMITTABLE_DATA).IsZero()); + + sender_->OnPacketSent(clock_.Now(), kUnused, 1, kDefaultMaxPacketSize, + HAS_RETRANSMITTABLE_DATA); + EXPECT_FALSE(sender_->TimeUntilSend(clock_.Now(), + kDefaultMaxPacketSize, + HAS_RETRANSMITTABLE_DATA).IsZero()); + clock_.AdvanceTime(sender_->TimeUntilSend(clock_.Now(), + kDefaultMaxPacketSize, + HAS_RETRANSMITTABLE_DATA)); EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - sender_->OnPacketSent(clock_.Now(), 2, kDefaultMaxPacketSize, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); - sender_->OnPacketSent(clock_.Now(), 3, 600, NOT_RETRANSMISSION, + kDefaultMaxPacketSize, + HAS_RETRANSMITTABLE_DATA).IsZero()); + sender_->OnPacketSent(clock_.Now(), kUnused, 2, kDefaultMaxPacketSize, HAS_RETRANSMITTABLE_DATA); - EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), - sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2)); - EXPECT_EQ(QuicTime::Delta::Infinite(), sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8)); - sender_->OnPacketAcked(1, kDefaultMaxPacketSize, rtt_); - sender_->OnPacketAcked(2, kDefaultMaxPacketSize, rtt_); - sender_->OnPacketAcked(3, 600, rtt_); + EXPECT_FALSE(sender_->TimeUntilSend(clock_.Now(), + kDefaultMaxPacketSize, + HAS_RETRANSMITTABLE_DATA).IsZero()); + // Advance the time twice as much and expect only one packet to be sent. + clock_.AdvanceTime(sender_->TimeUntilSend( + clock_.Now(), + kDefaultMaxPacketSize, + HAS_RETRANSMITTABLE_DATA).Multiply(2)); EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); -} - -TEST_F(FixRateTest, FixRatePacing) { - const QuicByteCount packet_size = 1200; - const QuicBandwidth bitrate = QuicBandwidth::FromKBytesPerSecond(240); - const int64 num_packets = 200; - QuicCongestionFeedbackFrame feedback; - receiver_->SetBitrate(QuicBandwidth::FromKBytesPerSecond(240)); - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), - unused_packet_map_); - QuicTime acc_advance_time(QuicTime::Zero()); - QuicPacketSequenceNumber sequence_number = 0; - for (int i = 0; i < num_packets; i += 2) { - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, - NOT_HANDSHAKE).IsZero()); - sender_->OnPacketSent(clock_.Now(), sequence_number++, packet_size, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, - NOT_HANDSHAKE).IsZero()); - sender_->OnPacketSent(clock_.Now(), sequence_number++, packet_size, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); - QuicTime::Delta advance_time = sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); - clock_.AdvanceTime(advance_time); - sender_->OnPacketAcked(sequence_number - 1, packet_size, rtt_); - sender_->OnPacketAcked(sequence_number - 2, packet_size, rtt_); - acc_advance_time = acc_advance_time.Add(advance_time); - } - EXPECT_EQ(num_packets * packet_size * 1000000 / bitrate.ToBytesPerSecond(), - static_cast<uint64>(acc_advance_time.Subtract(start_) - .ToMicroseconds())); + kDefaultMaxPacketSize, + HAS_RETRANSMITTABLE_DATA).IsZero()); + sender_->OnPacketSent(clock_.Now(), kUnused, 3, kDefaultMaxPacketSize, + HAS_RETRANSMITTABLE_DATA); + EXPECT_FALSE(sender_->TimeUntilSend(clock_.Now(), + kDefaultMaxPacketSize, + HAS_RETRANSMITTABLE_DATA).IsZero()); } } // namespace test diff --git a/chromium/net/quic/congestion_control/hybrid_slow_start.cc b/chromium/net/quic/congestion_control/hybrid_slow_start.cc index 0edb10e86ea..2ae3b503265 100644 --- a/chromium/net/quic/congestion_control/hybrid_slow_start.cc +++ b/chromium/net/quic/congestion_control/hybrid_slow_start.cc @@ -6,104 +6,133 @@ #include <algorithm> +using std::max; +using std::min; + namespace net { // Note(pwestin): the magic clamping numbers come from the original code in // tcp_cubic.c. +const int64 kHybridStartLowWindow = 16; // Number of delay samples for detecting the increase of delay. -const int kHybridStartMinSamples = 8; +const uint32 kHybridStartMinSamples = 8; const int kHybridStartDelayFactorExp = 4; // 2^4 = 16 -const int kHybridStartDelayMinThresholdUs = 2000; +// The original paper specifies 2 and 8ms, but those have changed over time. +const int kHybridStartDelayMinThresholdUs = 4000; const int kHybridStartDelayMaxThresholdUs = 16000; HybridSlowStart::HybridSlowStart(const QuicClock* clock) : clock_(clock), started_(false), - found_ack_train_(false), - found_delay_(false), + hystart_found_(NOT_FOUND), + last_sent_sequence_number_(0), round_start_(QuicTime::Zero()), end_sequence_number_(0), - last_time_(QuicTime::Zero()), - sample_count_(0), - current_rtt_(QuicTime::Delta::Zero()) { + last_close_ack_pair_time_(QuicTime::Zero()), + rtt_sample_count_(0), + current_min_rtt_(QuicTime::Delta::Zero()) { +} + +void HybridSlowStart::OnPacketAcked( + QuicPacketSequenceNumber acked_sequence_number, bool in_slow_start) { + // OnPacketAcked gets invoked after ShouldExitSlowStart, so it's best to end + // the round when the final packet of the burst is received and start it on + // the next incoming ack. + if (in_slow_start && IsEndOfRound(acked_sequence_number)) { + started_ = false; + } +} + +void HybridSlowStart::OnPacketSent(QuicPacketSequenceNumber sequence_number) { + last_sent_sequence_number_ = sequence_number; } void HybridSlowStart::Restart() { - found_ack_train_ = false; - found_delay_ = false; + started_ = false; + hystart_found_ = NOT_FOUND; } -void HybridSlowStart::Reset(QuicPacketSequenceNumber end_sequence_number) { - DVLOG(1) << "Reset hybrid slow start @" << end_sequence_number; - round_start_ = last_time_ = clock_->ApproximateNow(); - end_sequence_number_ = end_sequence_number; - current_rtt_ = QuicTime::Delta::Zero(); - sample_count_ = 0; +void HybridSlowStart::StartReceiveRound(QuicPacketSequenceNumber last_sent) { + DVLOG(1) << "Reset hybrid slow start @" << last_sent; + round_start_ = last_close_ack_pair_time_ = clock_->ApproximateNow(); + end_sequence_number_ = last_sent; + current_min_rtt_ = QuicTime::Delta::Zero(); + rtt_sample_count_ = 0; started_ = true; } -bool HybridSlowStart::EndOfRound(QuicPacketSequenceNumber ack) { +bool HybridSlowStart::IsEndOfRound(QuicPacketSequenceNumber ack) const { return end_sequence_number_ <= ack; } -void HybridSlowStart::Update(QuicTime::Delta rtt, QuicTime::Delta delay_min) { - // The original code doesn't invoke this until we hit 16 packet per burst. - // Since the code handles lower than 16 grecefully and I removed that - // limit. - if (found_ack_train_ || found_delay_) { - return; +bool HybridSlowStart::ShouldExitSlowStart(QuicTime::Delta latest_rtt, + QuicTime::Delta min_rtt, + int64 congestion_window) { + if (!started_) { + // Time to start the hybrid slow start. + StartReceiveRound(last_sent_sequence_number_); + } + if (hystart_found_ != NOT_FOUND) { + return true; } QuicTime current_time = clock_->ApproximateNow(); // First detection parameter - ack-train detection. // Since slow start burst out packets we can indirectly estimate the inter- // arrival time by looking at the arrival time of the ACKs if the ACKs are - // spread out more then half the minimum RTT packets are beeing spread out + // spread out more then half the minimum RTT packets are being spread out // more than the capacity. - // This first trigger will not come into play until we hit roughly 4.8 Mbit/s. + // This first trigger will not come into play until we hit roughly 9.6 Mbps + // with delayed acks (or 4.8Mbps without delayed acks) + // TODO(ianswett): QUIC always uses delayed acks, even at the beginning, so + // this should likely be at least 4ms. // TODO(pwestin): we need to make sure our pacing don't trigger this detector. - if (current_time.Subtract(last_time_).ToMicroseconds() <= - kHybridStartDelayMinThresholdUs) { - last_time_ = current_time; + // TODO(ianswett): Pacing or other cases could be handled by checking the send + // time of the first acked packet in a receive round. + if (current_time.Subtract(last_close_ack_pair_time_).ToMicroseconds() <= + kHybridStartDelayMinThresholdUs) { + last_close_ack_pair_time_ = current_time; if (current_time.Subtract(round_start_).ToMicroseconds() >= - (delay_min.ToMicroseconds() >> 1)) { - found_ack_train_ = true; + min_rtt.ToMicroseconds() >> 1) { + hystart_found_ = ACK_TRAIN; } + } else if (last_close_ack_pair_time_ == round_start_) { + // If the previous ack wasn't close, then move forward the round start time + // to the incoming ack. + last_close_ack_pair_time_ = round_start_ = current_time; } // Second detection parameter - delay increase detection. - // Compare the minimum delay (current_rtt_) of the current + // Compare the minimum delay (current_min_rtt_) of the current // burst of packets relative to the minimum delay during the session. // Note: we only look at the first few(8) packets in each burst, since we // only want to compare the lowest RTT of the burst relative to previous // bursts. - sample_count_++; - if (sample_count_ <= kHybridStartMinSamples) { - if (current_rtt_.IsZero() || current_rtt_ > rtt) { - current_rtt_ = rtt; + rtt_sample_count_++; + if (rtt_sample_count_ <= kHybridStartMinSamples) { + if (current_min_rtt_.IsZero() || current_min_rtt_ > latest_rtt) { + current_min_rtt_ = latest_rtt; } } - // We only need to check this once. - if (sample_count_ == kHybridStartMinSamples) { - int accepted_variance_us = delay_min.ToMicroseconds() >> + // We only need to check this once per round. + if (rtt_sample_count_ == kHybridStartMinSamples) { + // Divide min_rtt by 16 to get a rtt increase threshold for exiting. + int min_rtt_increase_threshold_us = min_rtt.ToMicroseconds() >> kHybridStartDelayFactorExp; - accepted_variance_us = std::min(accepted_variance_us, - kHybridStartDelayMaxThresholdUs); - QuicTime::Delta accepted_variance = QuicTime::Delta::FromMicroseconds( - std::max(accepted_variance_us, kHybridStartDelayMinThresholdUs)); + // Ensure the rtt threshold is never less than 2ms or more than 16ms. + min_rtt_increase_threshold_us = min(min_rtt_increase_threshold_us, + kHybridStartDelayMaxThresholdUs); + QuicTime::Delta min_rtt_increase_threshold = + QuicTime::Delta::FromMicroseconds(max(min_rtt_increase_threshold_us, + kHybridStartDelayMinThresholdUs)); - if (current_rtt_ > delay_min.Add(accepted_variance)) { - found_delay_ = true; + if (current_min_rtt_ > min_rtt.Add(min_rtt_increase_threshold)) { + hystart_found_= DELAY; } } -} - -bool HybridSlowStart::Exit() { - // If either one of the two conditions are met we exit from slow start - // immediately. - if (found_ack_train_ || found_delay_) { - return true; - } - return false; + // Exit from slow start if the cwnd is greater than 16 and an ack train or + // increasing delay are found. + return congestion_window >= kHybridStartLowWindow && + hystart_found_ != NOT_FOUND; } } // namespace net diff --git a/chromium/net/quic/congestion_control/hybrid_slow_start.h b/chromium/net/quic/congestion_control/hybrid_slow_start.h index cee9c731257..5d36c53a334 100644 --- a/chromium/net/quic/congestion_control/hybrid_slow_start.h +++ b/chromium/net/quic/congestion_control/hybrid_slow_start.h @@ -26,36 +26,61 @@ class NET_EXPORT_PRIVATE HybridSlowStart { public: explicit HybridSlowStart(const QuicClock* clock); + void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number, + bool in_slow_start); + + void OnPacketSent(QuicPacketSequenceNumber sequence_number); + + // ShouldExitSlowStart should be called on every new ack frame, since a new + // RTT measurement can be made then. + // rtt: the RTT for this ack packet. + // min_rtt: is the lowest delay (RTT) we have seen during the session. + // congestion_window: the congestion window in packets. + bool ShouldExitSlowStart(QuicTime::Delta rtt, + QuicTime::Delta min_rtt, + int64 congestion_window); + // Start a new slow start phase. void Restart(); + // TODO(ianswett): The following methods should be private, but that requires + // a follow up CL to update the unit test. // Returns true if this ack the last sequence number of our current slow start // round. // Call Reset if this returns true. - bool EndOfRound(QuicPacketSequenceNumber ack); - - // Call for each round (burst) in the slow start phase. - void Reset(QuicPacketSequenceNumber end_sequence_number); + bool IsEndOfRound(QuicPacketSequenceNumber ack) const; - // rtt: it the RTT for this ack packet. - // delay_min: is the lowest delay (RTT) we have seen during the session. - void Update(QuicTime::Delta rtt, QuicTime::Delta delay_min); + // Call for the start of each receive round (burst) in the slow start phase. + void StartReceiveRound(QuicPacketSequenceNumber last_sent); - // Returns true when we should exit slow start. - bool Exit(); - - bool started() { return started_; } + // Whether slow start has started. + bool started() const { + return started_; + } private: + // Whether a condition for exiting slow start has been found. + enum HystartState { + NOT_FOUND, + ACK_TRAIN, // A closely spaced ack train is too long. + DELAY, // Too much increase in the round's min_rtt was observed. + }; + const QuicClock* clock_; + // Whether the hybrid slow start has been started. bool started_; - bool found_ack_train_; - bool found_delay_; - QuicTime round_start_; // Beginning of each slow start round. - QuicPacketSequenceNumber end_sequence_number_; // End of slow start round. - 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. + HystartState hystart_found_; + // Last sequence number sent which was CWND limited. + QuicPacketSequenceNumber last_sent_sequence_number_; + + // Variables for tracking acks received during a slow start round. + QuicTime round_start_; // Beginning of each slow start receive round. + QuicPacketSequenceNumber end_sequence_number_; // End of the receive round. + // Last time when the spacing between ack arrivals was less than 2 ms. + // Defaults to the beginning of the round. + QuicTime last_close_ack_pair_time_; + uint32 rtt_sample_count_; // Number of rtt samples in the current round. + QuicTime::Delta current_min_rtt_; // The minimum rtt of current round. DISALLOW_COPY_AND_ASSIGN(HybridSlowStart); }; diff --git a/chromium/net/quic/congestion_control/hybrid_slow_start_test.cc b/chromium/net/quic/congestion_control/hybrid_slow_start_test.cc index 0a9f91fd3f0..91c5d9f2d65 100644 --- a/chromium/net/quic/congestion_control/hybrid_slow_start_test.cc +++ b/chromium/net/quic/congestion_control/hybrid_slow_start_test.cc @@ -18,38 +18,40 @@ class HybridSlowStartTest : public ::testing::Test { rtt_(QuicTime::Delta::FromMilliseconds(60)) { } virtual void SetUp() { - slowStart_.reset(new HybridSlowStart(&clock_)); + slow_start_.reset(new HybridSlowStart(&clock_)); } const QuicTime::Delta one_ms_; const QuicTime::Delta rtt_; MockClock clock_; - scoped_ptr<HybridSlowStart> slowStart_; + scoped_ptr<HybridSlowStart> slow_start_; }; TEST_F(HybridSlowStartTest, Simple) { QuicPacketSequenceNumber sequence_number = 1; QuicPacketSequenceNumber end_sequence_number = 3; - slowStart_->Reset(end_sequence_number); + slow_start_->StartReceiveRound(end_sequence_number); - EXPECT_FALSE(slowStart_->EndOfRound(sequence_number++)); + EXPECT_FALSE(slow_start_->IsEndOfRound(sequence_number++)); // Test duplicates. - EXPECT_FALSE(slowStart_->EndOfRound(sequence_number)); + EXPECT_FALSE(slow_start_->IsEndOfRound(sequence_number)); - EXPECT_FALSE(slowStart_->EndOfRound(sequence_number++)); - EXPECT_TRUE(slowStart_->EndOfRound(sequence_number++)); + EXPECT_FALSE(slow_start_->IsEndOfRound(sequence_number++)); + EXPECT_TRUE(slow_start_->IsEndOfRound(sequence_number++)); // Test without a new registered end_sequence_number; - EXPECT_TRUE(slowStart_->EndOfRound(sequence_number++)); + EXPECT_TRUE(slow_start_->IsEndOfRound(sequence_number++)); end_sequence_number = 20; - slowStart_->Reset(end_sequence_number); + slow_start_->StartReceiveRound(end_sequence_number); while (sequence_number < end_sequence_number) { - EXPECT_FALSE(slowStart_->EndOfRound(sequence_number++)); + EXPECT_FALSE(slow_start_->IsEndOfRound(sequence_number++)); } - EXPECT_TRUE(slowStart_->EndOfRound(sequence_number++)); + EXPECT_TRUE(slow_start_->IsEndOfRound(sequence_number++)); } +// 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, // we expect to be able to send a burst of 30 packet before we trigger the @@ -58,24 +60,22 @@ TEST_F(HybridSlowStartTest, AckTrain) { QuicPacketSequenceNumber sequence_number = 2; QuicPacketSequenceNumber end_sequence_number = 2; for (int burst = 0; burst < kMaxLoopCount; ++burst) { - slowStart_->Reset(end_sequence_number); + slow_start_->StartReceiveRound(end_sequence_number); do { clock_.AdvanceTime(one_ms_); - slowStart_->Update(rtt_, rtt_); - EXPECT_FALSE(slowStart_->Exit()); - } while (!slowStart_->EndOfRound(sequence_number++)); + EXPECT_FALSE(slow_start_->ShouldExitSlowStart(rtt_, rtt_, 100)); + } while (!slow_start_->IsEndOfRound(sequence_number++)); end_sequence_number *= 2; // Exponential growth. } - slowStart_->Reset(end_sequence_number); + slow_start_->StartReceiveRound(end_sequence_number); - for (int n = 0; n < 29 && !slowStart_->EndOfRound(sequence_number++); ++n) { + for (int n = 0; + n < 29 && !slow_start_->IsEndOfRound(sequence_number++); ++n) { clock_.AdvanceTime(one_ms_); - slowStart_->Update(rtt_, rtt_); - EXPECT_FALSE(slowStart_->Exit()); + EXPECT_FALSE(slow_start_->ShouldExitSlowStart(rtt_, rtt_, 100)); } clock_.AdvanceTime(one_ms_); - slowStart_->Update(rtt_, rtt_); - EXPECT_TRUE(slowStart_->Exit()); + EXPECT_TRUE(slow_start_->ShouldExitSlowStart(rtt_, rtt_, 100)); } TEST_F(HybridSlowStartTest, Delay) { @@ -84,24 +84,23 @@ TEST_F(HybridSlowStartTest, Delay) { const int kHybridStartMinSamples = 8; // Number of acks required to trigger. QuicPacketSequenceNumber end_sequence_number = 1; - slowStart_->Reset(end_sequence_number++); + slow_start_->StartReceiveRound(end_sequence_number++); // Will not trigger since our lowest RTT in our burst is the same as the long // term RTT provided. for (int n = 0; n < kHybridStartMinSamples; ++n) { - slowStart_->Update(rtt_.Add(QuicTime::Delta::FromMilliseconds(n)), rtt_); - EXPECT_FALSE(slowStart_->Exit()); + EXPECT_FALSE(slow_start_->ShouldExitSlowStart( + rtt_.Add(QuicTime::Delta::FromMilliseconds(n)), rtt_, 100)); } - slowStart_->Reset(end_sequence_number++); + slow_start_->StartReceiveRound(end_sequence_number++); for (int n = 1; n < kHybridStartMinSamples; ++n) { - slowStart_->Update(rtt_.Add(QuicTime::Delta::FromMilliseconds(n + 4)), - rtt_); - EXPECT_FALSE(slowStart_->Exit()); + EXPECT_FALSE(slow_start_->ShouldExitSlowStart( + rtt_.Add(QuicTime::Delta::FromMilliseconds(n + 5)), rtt_, 100)); } // Expect to trigger since all packets in this burst was above the long term // RTT provided. - slowStart_->Update(rtt_.Add(QuicTime::Delta::FromMilliseconds(4)), rtt_); - EXPECT_TRUE(slowStart_->Exit()); + EXPECT_TRUE(slow_start_->ShouldExitSlowStart( + rtt_.Add(QuicTime::Delta::FromMilliseconds(5)), rtt_, 100)); } } // namespace test diff --git a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc b/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc deleted file mode 100644 index 2438493cc0b..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h" - -#include <algorithm> - -#include "base/basictypes.h" -#include "base/logging.h" -#include "net/quic/congestion_control/cube_root.h" -#include "net/quic/quic_protocol.h" - -namespace { -// The following constants are in 2^10 fractions of a second instead of ms to -// allow a 10 shift right to divide. -const int kCubeScale = 40; // 1024*1024^3 (first 1024 is from 0.100^3) - // where 0.100 is 100 ms which is the scaling - // round trip time. -// TODO(pwestin): Tuning parameter, currently close to TCP cubic at 100ms RTT. -const int kPacedCubeScale = 6000; -const uint64 kCubeFactor = (GG_UINT64_C(1) << kCubeScale) / kPacedCubeScale; -} // namespace - -namespace net { - -InterArrivalBitrateRampUp::InterArrivalBitrateRampUp(const QuicClock* clock) - : clock_(clock), - current_rate_(QuicBandwidth::Zero()), - channel_estimate_(QuicBandwidth::Zero()), - available_channel_estimate_(QuicBandwidth::Zero()), - halfway_point_(QuicBandwidth::Zero()), - epoch_(QuicTime::Zero()), - last_update_time_(QuicTime::Zero()) { -} - -void InterArrivalBitrateRampUp::Reset(QuicBandwidth new_rate, - QuicBandwidth available_channel_estimate, - QuicBandwidth channel_estimate) { - epoch_ = clock_->ApproximateNow(); - last_update_time_ = epoch_; - available_channel_estimate_ = std::max(new_rate, available_channel_estimate); - channel_estimate_ = std::max(channel_estimate, available_channel_estimate_); - - halfway_point_ = available_channel_estimate_.Add( - (channel_estimate_.Subtract(available_channel_estimate_)).Scale(0.5f)); - - if (new_rate < available_channel_estimate_) { - time_to_origin_point_ = CalcuateTimeToOriginPoint( - available_channel_estimate_.Subtract(new_rate)); - } else if (new_rate >= channel_estimate_) { - time_to_origin_point_ = 0; - } else if (new_rate >= halfway_point_) { - time_to_origin_point_ = - CalcuateTimeToOriginPoint(channel_estimate_.Subtract(new_rate)); - } else { - time_to_origin_point_ = CalcuateTimeToOriginPoint( - new_rate.Subtract(available_channel_estimate_)); - } - current_rate_ = new_rate; - DVLOG(1) << "Reset; time to origin point:" << time_to_origin_point_; -} - -void InterArrivalBitrateRampUp::UpdateChannelEstimate( - QuicBandwidth channel_estimate) { - if (available_channel_estimate_ > channel_estimate || - current_rate_ > channel_estimate || - channel_estimate_ == channel_estimate) { - // Ignore, because one of the following reasons: - // 1) channel estimate is bellow our current available estimate which we - // value higher that this estimate. - // 2) channel estimate is bellow our current send rate. - // 3) channel estimate has not changed. - return; - } - if (available_channel_estimate_ == halfway_point_ && - channel_estimate_ == halfway_point_) { - // First time we get a usable channel estimate. - channel_estimate_ = channel_estimate; - halfway_point_ = available_channel_estimate_.Add( - (channel_estimate_.Subtract(available_channel_estimate_).Scale(0.5f))); - DVLOG(1) << "UpdateChannelEstimate; first usable value:" - << channel_estimate.ToKBitsPerSecond() << " Kbits/s"; - return; - } - if (current_rate_ < halfway_point_) { - // Update channel estimate without recalculating if we are bellow the - // halfway point. - channel_estimate_ = channel_estimate; - return; - } - // We are between halfway point and our channel_estimate. - epoch_ = clock_->ApproximateNow(); - last_update_time_ = epoch_; - channel_estimate_ = channel_estimate; - - time_to_origin_point_ = - CalcuateTimeToOriginPoint(channel_estimate_.Subtract(current_rate_)); - - DVLOG(1) << "UpdateChannelEstimate; time to origin point:" - << time_to_origin_point_; -} - -QuicBandwidth InterArrivalBitrateRampUp::GetNewBitrate( - QuicBandwidth sent_bitrate) { - DCHECK(epoch_.IsInitialized()); - QuicTime current_time = clock_->ApproximateNow(); - // Cubic is "independent" of RTT, the update is limited by the time elapsed. - if (current_time.Subtract(last_update_time_) <= MaxCubicTimeInterval()) { - return current_rate_; - } - QuicTime::Delta time_from_last_update = - current_time.Subtract(last_update_time_); - - last_update_time_ = current_time; - - if (!sent_bitrate.IsZero() && - sent_bitrate.Add(sent_bitrate) < current_rate_) { - // Don't go up in bitrate when we are not sending. - // We need to update the epoch to reflect this state. - epoch_ = epoch_.Add(time_from_last_update); - DVLOG(1) << "Don't increase; our sent bitrate is:" - << sent_bitrate.ToKBitsPerSecond() << " Kbits/s" - << " current target rate is:" - << current_rate_.ToKBitsPerSecond() << " Kbits/s"; - return current_rate_; - } - QuicTime::Delta time_from_epoch = current_time.Subtract(epoch_); - - // Change the time unit from microseconds to 2^10 fractions per second. This - // is done to allow us to use shift as a divide operator. - int64 elapsed_time = (time_from_epoch.ToMicroseconds() << 10) / - kNumMicrosPerSecond; - - int64 offset = time_to_origin_point_ - elapsed_time; - // Note: using int64 since QuicBandwidth can't be negative - int64 delta_pace_kbps = (kPacedCubeScale * offset * offset * offset) >> - kCubeScale; - - bool start_bellow_halfway_point = false; - if (current_rate_ < halfway_point_) { - start_bellow_halfway_point = true; - - // available_channel_estimate_ is the orgin of the cubic function. - QuicBandwidth current_rate = QuicBandwidth::FromBytesPerSecond( - available_channel_estimate_.ToBytesPerSecond() - - (delta_pace_kbps << 10)); - - if (start_bellow_halfway_point && current_rate >= halfway_point_) { - // We passed the halfway point, recalculate with new orgin. - epoch_ = clock_->ApproximateNow(); - // channel_estimate_ is the new orgin of the cubic function. - if (current_rate >= channel_estimate_) { - time_to_origin_point_ = 0; - } else { - time_to_origin_point_ = - CalcuateTimeToOriginPoint(channel_estimate_.Subtract(current_rate)); - } - DVLOG(1) << "Passed the halfway point; time to origin point:" - << time_to_origin_point_; - } - current_rate_ = current_rate; - } else { - // channel_estimate_ is the orgin of the cubic function. - current_rate_ = QuicBandwidth::FromBytesPerSecond( - channel_estimate_.ToBytesPerSecond() - (delta_pace_kbps << 10)); - } - return current_rate_; -} - -uint32 InterArrivalBitrateRampUp::CalcuateTimeToOriginPoint( - QuicBandwidth rate_difference) const { - return CubeRoot::Root(kCubeFactor * rate_difference.ToKBytesPerSecond()); -} - -} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h b/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h deleted file mode 100644 index 931992391a5..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Ramp up bitrate from a start point normally our "current_rate" as long as we -// have no packet loss or delay events. -// The first half of the ramp up curve follows a cubic function with its orgin -// at the estimated available bandwidth, onece the bitrate pass the halfway -// point between the estimated available bandwidth and the estimated max -// bandwidth it will follw a new cubic function with its orgin at the estimated -// max bandwidth. -#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_BITRATE_RAMP_UP_H_ -#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_BITRATE_RAMP_UP_H_ - -#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_time.h" - -namespace net { - -class NET_EXPORT_PRIVATE InterArrivalBitrateRampUp { - public: - explicit InterArrivalBitrateRampUp(const QuicClock* clock); - - // Call after a decision to lower the bitrate and after a probe. - void Reset(QuicBandwidth current_rate, - QuicBandwidth available_channel_estimate, - QuicBandwidth channel_estimate); - - // Call everytime we get a new channel estimate. - void UpdateChannelEstimate(QuicBandwidth channel_estimate); - - // Compute a new send pace to use. - QuicBandwidth GetNewBitrate(QuicBandwidth sent_bitrate); - - private: - uint32 CalcuateTimeToOriginPoint(QuicBandwidth rate_difference) const; - - static const QuicTime::Delta MaxCubicTimeInterval() { - return QuicTime::Delta::FromMilliseconds(30); - } - - const QuicClock* clock_; - - QuicBandwidth current_rate_; - QuicBandwidth channel_estimate_; - QuicBandwidth available_channel_estimate_; - QuicBandwidth halfway_point_; - - // Time when this cycle started, after a Reset. - QuicTime epoch_; - - // Time when we updated current_rate_. - QuicTime last_update_time_; - - // Time to origin point of cubic function in 2^10 fractions of a second. - uint32 time_to_origin_point_; - - DISALLOW_COPY_AND_ASSIGN(InterArrivalBitrateRampUp); -}; - -} // namespace net -#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_BITRATE_RAMP_UP_H_ diff --git a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc b/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc deleted file mode 100644 index acae78d2056..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/basictypes.h" -#include "base/logging.h" -#include "net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h" -#include "net/quic/test_tools/mock_clock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { -namespace test { - -class InterArrivalBitrateRampUpTest : public ::testing::Test { - protected: - InterArrivalBitrateRampUpTest() - : one_ms_(QuicTime::Delta::FromMilliseconds(1)), - hundred_ms_(QuicTime::Delta::FromMilliseconds(100)), - bitrate_ramp_up_(&clock_) { - } - virtual void SetUp() { - clock_.AdvanceTime(one_ms_); - } - const QuicTime::Delta one_ms_; - const QuicTime::Delta hundred_ms_; - MockClock clock_; - InterArrivalBitrateRampUp bitrate_ramp_up_; -}; - -TEST_F(InterArrivalBitrateRampUpTest, GoodEstimates) { - QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100); - QuicBandwidth available_channel_estimate = - QuicBandwidth::FromKBytesPerSecond(200); - QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(400); - QuicBandwidth halfway_point = available_channel_estimate.Add( - channel_estimate.Subtract(available_channel_estimate).Scale(0.5f)); - QuicBandwidth sent_bitrate = QuicBandwidth::Zero(); - bitrate_ramp_up_.Reset(start_rate, - available_channel_estimate, - channel_estimate); - - // First concave growth, towards available_channel_estimate. - for (int i = 0; i < 25; ++i) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_GE(available_channel_estimate, sent_bitrate); - EXPECT_LE(start_rate, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(available_channel_estimate, sent_bitrate); - - // First convex growth, from available_channel_estimate. - for (int j = 0; j < 25; ++j) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(available_channel_estimate, sent_bitrate); - EXPECT_GE(halfway_point, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(halfway_point, sent_bitrate); - - // Second concave growth, towards channel_estimate. - for (int i = 0; i < 24; ++i) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_GE(channel_estimate, sent_bitrate); - EXPECT_LE(halfway_point, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(channel_estimate, sent_bitrate); - - // Second convex growth, from channel_estimate. - for (int j = 0; j < 25; ++j) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(channel_estimate, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 100000, - sent_bitrate.ToBytesPerSecond(), 10000); - - // Verify that we increase cubic. - for (int j = 0; j < 23; ++j) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(channel_estimate, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 750000, - sent_bitrate.ToBytesPerSecond(), 10000); -} - -TEST_F(InterArrivalBitrateRampUpTest, GoodEstimatesLimitedSendRate) { - QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100); - QuicBandwidth available_channel_estimate = - QuicBandwidth::FromKBytesPerSecond(200); - QuicBandwidth max_sent_rate = - QuicBandwidth::FromKBytesPerSecond(125); - QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(400); - QuicBandwidth halfway_point = available_channel_estimate.Add( - channel_estimate.Subtract(available_channel_estimate).Scale(0.5f)); - QuicBandwidth sent_bitrate = QuicBandwidth::Zero(); - bitrate_ramp_up_.Reset(start_rate, - available_channel_estimate, - channel_estimate); - - // First concave growth, towards available_channel_estimate. - // Should pass without being affected by the max_sent_rate. - for (int i = 0; i < 25; ++i) { - clock_.AdvanceTime(hundred_ms_); - // Cap our previus sent rate. - sent_bitrate = std::min(sent_bitrate, max_sent_rate); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_GE(available_channel_estimate, sent_bitrate); - EXPECT_LE(start_rate, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - // Cap our previus sent rate. - sent_bitrate = std::min(sent_bitrate, max_sent_rate); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(available_channel_estimate, sent_bitrate); - - // First convex growth, from available_channel_estimate. - for (int j = 0; j < 25; ++j) { - clock_.AdvanceTime(hundred_ms_); - // Cap our previus sent rate. - sent_bitrate = std::min(sent_bitrate, max_sent_rate); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(available_channel_estimate, sent_bitrate); - EXPECT_GE(halfway_point, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = std::min(sent_bitrate, max_sent_rate); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - // We expect 2 * sent_bitrate to cap the rate. - EXPECT_LE(max_sent_rate.Add(max_sent_rate), sent_bitrate); - // Remove our sent cap. - // Expect bitrate to continue to ramp from its previous rate. - for (int j = 0; j < 5; ++j) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(available_channel_estimate, sent_bitrate); - EXPECT_LE(max_sent_rate.Add(max_sent_rate), sent_bitrate); - EXPECT_GE(halfway_point, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(halfway_point, sent_bitrate); -} - -TEST_F(InterArrivalBitrateRampUpTest, GoodEstimatesCloseToChannelEstimate) { - QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100); - QuicBandwidth available_channel_estimate = - QuicBandwidth::FromKBytesPerSecond(200); - QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(250); - QuicBandwidth halfway_point = available_channel_estimate.Add( - channel_estimate.Subtract(available_channel_estimate).Scale(0.5f)); - QuicBandwidth sent_bitrate = QuicBandwidth::Zero(); - bitrate_ramp_up_.Reset(start_rate, - available_channel_estimate, - channel_estimate); - - // First concave growth, towards available_channel_estimate. - for (int i = 0; i < 25; ++i) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_GE(available_channel_estimate, sent_bitrate); - EXPECT_LE(start_rate, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(available_channel_estimate, sent_bitrate); - - // First convex growth, from available_channel_estimate. - for (int j = 0; j < 15; ++j) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(available_channel_estimate, sent_bitrate); - EXPECT_GE(halfway_point, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(halfway_point, sent_bitrate); - - // Second concave growth, towards channel_estimate. - for (int i = 0; i < 14; ++i) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_GE(channel_estimate, sent_bitrate); - EXPECT_LE(halfway_point, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(channel_estimate, sent_bitrate); - - // Second convex growth, from channel_estimate. - for (int j = 0; j < 25; ++j) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(channel_estimate, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 100000, - sent_bitrate.ToBytesPerSecond(), 10000); - - // Verify that we increase cubic. - for (int j = 0; j < 24; ++j) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(channel_estimate, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 780000, - sent_bitrate.ToBytesPerSecond(), 20000); -} - -TEST_F(InterArrivalBitrateRampUpTest, UncertainEstimates) { - QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100); - QuicBandwidth available_channel_estimate = - QuicBandwidth::FromKBytesPerSecond(200); - QuicBandwidth channel_estimate = - QuicBandwidth::FromKBytesPerSecond(400 * 0.7f); - QuicBandwidth halfway_point = available_channel_estimate.Add( - channel_estimate.Subtract(available_channel_estimate).Scale(0.5f)); - QuicBandwidth sent_bitrate = QuicBandwidth::Zero(); - bitrate_ramp_up_.Reset(start_rate, - available_channel_estimate, - channel_estimate); - - // First concave growth, towards available_channel_estimate. - for (int i = 0; i < 20; ++i) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_GE(available_channel_estimate, sent_bitrate); - EXPECT_LE(start_rate, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(available_channel_estimate, sent_bitrate); - - // First convex growth, from available_channel_estimate. - for (int j = 0; j < 23; ++j) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(available_channel_estimate, sent_bitrate); - EXPECT_GE(halfway_point, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(halfway_point, sent_bitrate); - - // Second concave growth, towards channel_estimate. - for (int i = 0; i < 12; ++i) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_GE(channel_estimate, sent_bitrate); - EXPECT_LE(halfway_point, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(channel_estimate, sent_bitrate); - - // Second convex growth, from channel_estimate. - for (int j = 0; j < 30; ++j) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(channel_estimate, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 100000, - sent_bitrate.ToBytesPerSecond(), 10000); - - // Verify that we increase cubic. - for (int j = 0; j < 23; ++j) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(channel_estimate, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 750000, - sent_bitrate.ToBytesPerSecond(), 20000); -} - -TEST_F(InterArrivalBitrateRampUpTest, UnknownEstimates) { - QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100); - QuicBandwidth available_channel_estimate = - QuicBandwidth::FromKBytesPerSecond(200); - QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(400); - QuicBandwidth sent_bitrate = QuicBandwidth::Zero(); - bitrate_ramp_up_.Reset(start_rate, - available_channel_estimate, - available_channel_estimate); - - // First convex growth, from start_rate. - for (int j = 0; j < 20; ++j) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(start_rate, sent_bitrate); - EXPECT_GE(available_channel_estimate, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_NEAR(available_channel_estimate.ToBytesPerSecond(), - sent_bitrate.ToBytesPerSecond(), 10000); - - // Verify that we increase cubic. - for (int j = 0; j < 31; ++j) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_GE(channel_estimate, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_NEAR(channel_estimate.ToBytesPerSecond(), - sent_bitrate.ToBytesPerSecond(), 10000); -} - -TEST_F(InterArrivalBitrateRampUpTest, UpdatingChannelEstimateHigher) { - QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(200); - QuicBandwidth available_channel_estimate = start_rate; - QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(250); - QuicBandwidth halfway_point = available_channel_estimate.Add( - channel_estimate.Subtract(available_channel_estimate).Scale(0.5f)); - QuicBandwidth sent_bitrate = QuicBandwidth::Zero(); - bitrate_ramp_up_.Reset(start_rate, - available_channel_estimate, - channel_estimate); - - // Convex growth, from available_channel_estimate. - for (int j = 0; j < 16; ++j) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(available_channel_estimate, sent_bitrate); - EXPECT_GE(halfway_point, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(halfway_point, sent_bitrate); - - // Increse channel estimate. - channel_estimate = QuicBandwidth::FromKBytesPerSecond(300); - bitrate_ramp_up_.UpdateChannelEstimate(channel_estimate); - - // Concave growth, towards channel_estimate. - for (int i = 0; i < 22; ++i) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_GE(channel_estimate, sent_bitrate); - EXPECT_LE(halfway_point, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(channel_estimate, sent_bitrate); -} - -TEST_F(InterArrivalBitrateRampUpTest, UpdatingChannelEstimateLower) { - QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(200); - QuicBandwidth available_channel_estimate = start_rate; - QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(250); - QuicBandwidth halfway_point = available_channel_estimate.Add( - channel_estimate.Subtract(available_channel_estimate).Scale(0.5f)); - QuicBandwidth sent_bitrate = QuicBandwidth::Zero(); - bitrate_ramp_up_.Reset(start_rate, - available_channel_estimate, - channel_estimate); - - // Convex growth, from available_channel_estimate. - for (int j = 0; j < 16; ++j) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(available_channel_estimate, sent_bitrate); - EXPECT_GE(halfway_point, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(halfway_point, sent_bitrate); - - // Decrese channel estimate. - channel_estimate = QuicBandwidth::FromKBytesPerSecond(240); - bitrate_ramp_up_.UpdateChannelEstimate(channel_estimate); - - // Concave growth, towards channel_estimate. - for (int i = 0; i < 11; ++i) { - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_GE(channel_estimate, sent_bitrate); - EXPECT_LE(halfway_point, sent_bitrate); - } - clock_.AdvanceTime(hundred_ms_); - sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); - EXPECT_LE(channel_estimate, sent_bitrate); -} - -} // namespace test -} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc deleted file mode 100644 index 5e500b371bc..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/congestion_control/inter_arrival_overuse_detector.h" - -#include <math.h> -#include <stdlib.h> - -#include <algorithm> - -// Initial noise variance, equal to a standard deviation of 1 millisecond. -static const float kInitialVarianceNoise = 1000000.0; - -// Minimum variance of the time delta. -static const int kMinVarianceDelta = 10000; - -// Threshold for accumulated delta. -static const int kThresholdAccumulatedDeltasUs = 1000; - -// Same as above, described as numerator and denominator. -static const int kBetaNumerator = 49; -static const int kBetaDenominator = 50; - -// Trigger a signal when the accumulated time drift is larger than -// 5 x standard deviation. -// A lower value triggers earlier with more false detect as a side effect. -static const int kDetectDriftStandardDeviation = 5; - -// Trigger an overuse when the time difference between send time and receive -// is larger than 7 x standard deviation. -// A lower value triggers earlier with more false detect as a side effect. -static const float kDetectTimeDiffStandardDeviation = 7; - -// Trigger an overuse when the mean of the time difference diverges too far -// from 0. -// A higher value trigger earlier with more false detect as a side effect. -static const int kDetectSlopeFactor = 14; - -// We need to get some initial statistics before the detection can start. -static const int kMinSamplesBeforeDetect = 10; - -namespace net { - -InterArrivalOveruseDetector::InterArrivalOveruseDetector() - : last_sequence_number_(0), - num_of_deltas_(0), - accumulated_deltas_(QuicTime::Delta::Zero()), - delta_mean_(0.0), - delta_variance_(kInitialVarianceNoise), - delta_overuse_counter_(0), - delta_estimate_(kBandwidthSteady), - slope_overuse_counter_(0), - slope_estimate_(kBandwidthSteady), - send_receive_offset_(QuicTime::Delta::Infinite()), - estimated_congestion_delay_(QuicTime::Delta::Zero()) { -} - -void InterArrivalOveruseDetector::OnAcknowledgedPacket( - QuicPacketSequenceNumber sequence_number, - QuicTime send_time, - bool last_of_send_time, - QuicTime receive_time) { - if (last_sequence_number_ >= sequence_number) { - // This is an old packet and should be ignored. Note that we are called - // with a full 64 bit sequence number, even if the wire format may only - // convey some low-order bits of that number. - DVLOG(1) << "Skip old packet"; - return; - } - - last_sequence_number_ = sequence_number; - - if (current_packet_group_.send_time != send_time) { - // First value in this group. If the last packet of a group is lost we - // overwrite the old value and start over with a new measurement. - current_packet_group_.send_time = send_time; - // The receive_time value might not be first in a packet burst if that - // packet was lost, however we will still use it in this calculation. - UpdateSendReceiveTimeOffset(receive_time.Subtract(send_time)); - } - if (!last_of_send_time) { - // We expect more packet with this send time. - return; - } - // First packet of a later group, the previous group sample is ready. - if (previous_packet_group_.send_time.IsInitialized()) { - QuicTime::Delta sent_delta = send_time.Subtract( - previous_packet_group_.send_time); - QuicTime::Delta receive_delta = receive_time.Subtract( - previous_packet_group_.last_receive_time); - // We assume that groups of packets are sent together as bursts (tagged - // with identical send times) because it is too computationally expensive - // to pace them out individually. The received_delta is then the total - // delta between the receipt of the last packet of the previous group, and - // the last packet of the current group. Assuming we are transmitting - // these bursts on average at the available bandwidth rate, there should be - // no change in overall spread (both deltas should be the same). - UpdateFilter(receive_delta, sent_delta); - } - // Save current as previous. - previous_packet_group_ = current_packet_group_; - previous_packet_group_.last_receive_time = receive_time; -} - -void InterArrivalOveruseDetector::UpdateSendReceiveTimeOffset( - QuicTime::Delta offset) { - // Note the send and receive time can have a randomly large offset, however - // they are stable in relation to each other, hence no or extremely low clock - // drift relative to the duration of our stream. - if (offset.ToMicroseconds() < send_receive_offset_.ToMicroseconds()) { - send_receive_offset_ = offset; - } - estimated_congestion_delay_ = offset.Subtract(send_receive_offset_); -} - -BandwidthUsage InterArrivalOveruseDetector::GetState( - QuicTime::Delta* estimated_congestion_delay) { - *estimated_congestion_delay = estimated_congestion_delay_; - int64 sigma_delta = sqrt(static_cast<double>(delta_variance_)); - DetectSlope(sigma_delta); - DetectDrift(sigma_delta); - return std::max(slope_estimate_, delta_estimate_); -} - -void InterArrivalOveruseDetector::UpdateFilter(QuicTime::Delta received_delta, - QuicTime::Delta sent_delta) { - ++num_of_deltas_; - QuicTime::Delta time_diff = received_delta.Subtract(sent_delta); - UpdateDeltaEstimate(time_diff); - accumulated_deltas_ = accumulated_deltas_.Add(time_diff); -} - -void InterArrivalOveruseDetector::UpdateDeltaEstimate( - QuicTime::Delta residual) { - DCHECK_EQ(1, kBetaDenominator - kBetaNumerator); - int64 residual_us = residual.ToMicroseconds(); - delta_mean_ = - (kBetaNumerator * delta_mean_ + residual_us) / kBetaDenominator; - delta_variance_ = - (kBetaNumerator * delta_variance_ + - (delta_mean_ - residual_us) * (delta_mean_ - residual_us)) / - kBetaDenominator; - - if (delta_variance_ < kMinVarianceDelta) { - delta_variance_ = kMinVarianceDelta; - } -} - -void InterArrivalOveruseDetector::DetectDrift(int64 sigma_delta) { - // We have 2 drift detectors. The accumulate of deltas and the absolute time - // differences. - if (num_of_deltas_ < kMinSamplesBeforeDetect) { - return; - } - if (delta_overuse_counter_ > 0 && - accumulated_deltas_.ToMicroseconds() > kThresholdAccumulatedDeltasUs) { - if (delta_estimate_ != kBandwidthDraining) { - DVLOG(1) << "Bandwidth estimate drift: Draining buffer(s) " - << accumulated_deltas_.ToMilliseconds() << " ms"; - delta_estimate_ = kBandwidthDraining; - } - return; - } - if ((sigma_delta * kDetectTimeDiffStandardDeviation > - estimated_congestion_delay_.ToMicroseconds()) && - (sigma_delta * kDetectDriftStandardDeviation > - abs(accumulated_deltas_.ToMicroseconds()))) { - if (delta_estimate_ != kBandwidthSteady) { - DVLOG(1) << "Bandwidth estimate drift: Steady" - << " mean:" << delta_mean_ - << " sigma:" << sigma_delta - << " offset:" << send_receive_offset_.ToMicroseconds() - << " delta:" << estimated_congestion_delay_.ToMicroseconds() - << " drift:" << accumulated_deltas_.ToMicroseconds(); - delta_estimate_ = kBandwidthSteady; - // Reset drift counter. - accumulated_deltas_ = QuicTime::Delta::Zero(); - delta_overuse_counter_ = 0; - } - return; - } - if (accumulated_deltas_.ToMicroseconds() > 0) { - if (delta_estimate_ != kBandwidthOverUsing) { - ++delta_overuse_counter_; - DVLOG(1) << "Bandwidth estimate drift: Over using" - << " mean:" << delta_mean_ - << " sigma:" << sigma_delta - << " offset:" << send_receive_offset_.ToMicroseconds() - << " delta:" << estimated_congestion_delay_.ToMicroseconds() - << " drift:" << accumulated_deltas_.ToMicroseconds(); - delta_estimate_ = kBandwidthOverUsing; - } - } else { - if (delta_estimate_ != kBandwidthUnderUsing) { - --delta_overuse_counter_; - DVLOG(1) << "Bandwidth estimate drift: Under using" - << " mean:" << delta_mean_ - << " sigma:" << sigma_delta - << " offset:" << send_receive_offset_.ToMicroseconds() - << " delta:" << estimated_congestion_delay_.ToMicroseconds() - << " drift:" << accumulated_deltas_.ToMicroseconds(); - delta_estimate_ = kBandwidthUnderUsing; - } - // Adding decay of negative accumulated_deltas_ since it could be caused by - // a starting with full buffers. This way we will always converge to 0. - accumulated_deltas_ = accumulated_deltas_.Add( - QuicTime::Delta::FromMicroseconds(sigma_delta >> 3)); - } -} - -void InterArrivalOveruseDetector::DetectSlope(int64 sigma_delta) { - // We use the mean change since it has a constant expected mean 0 - // regardless of number of packets and spread. It is also safe to use during - // packet loss, since a lost packet only results in a missed filter update - // not a drift. - if (num_of_deltas_ < kMinSamplesBeforeDetect) { - return; - } - if (slope_overuse_counter_ > 0 && delta_mean_ > 0) { - if (slope_estimate_ != kBandwidthDraining) { - DVLOG(1) << "Bandwidth estimate slope: Draining buffer(s)"; - } - slope_estimate_ = kBandwidthDraining; - return; - } - if (sigma_delta > abs(delta_mean_) * kDetectSlopeFactor) { - if (slope_estimate_ != kBandwidthSteady) { - DVLOG(1) << "Bandwidth estimate slope: Steady" - << " mean:" << delta_mean_ - << " sigma:" << sigma_delta; - slope_overuse_counter_ = 0; - slope_estimate_ = kBandwidthSteady; - } - return; - } - if (delta_mean_ > 0) { - if (slope_estimate_ != kBandwidthOverUsing) { - ++slope_overuse_counter_; - DVLOG(1) << "Bandwidth estimate slope: Over using" - << " mean:" << delta_mean_ - << " sigma:" << sigma_delta; - slope_estimate_ = kBandwidthOverUsing; - } - } else { - if (slope_estimate_ != kBandwidthUnderUsing) { - --slope_overuse_counter_; - DVLOG(1) << "Bandwidth estimate slope: Under using" - << " mean:" << delta_mean_ - << " sigma:" << sigma_delta; - slope_estimate_ = kBandwidthUnderUsing; - } - } -} - -} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.h b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.h deleted file mode 100644 index 185236279bb..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.h +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This class is a helper class to the inter arrival congestion control. It -// provide a signal to the inter arrival congestion control of the estimated -// state of our transport channel. The estimate is based on the inter arrival -// time of the received packets relative to the time those packets were sent; -// we can estimate the build up of buffers on the network before packets are -// lost. -// -// Note: this class is not thread-safe. - -#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_OVERUSE_DETECTOR_H_ -#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_OVERUSE_DETECTOR_H_ - -#include "base/basictypes.h" -#include "net/base/net_export.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_time.h" - -namespace net { - -enum NET_EXPORT_PRIVATE RateControlRegion { - kRateControlRegionUnknown = 0, - kRateControlRegionUnderMax = 1, - kRateControlRegionNearMax = 2 -}; - -// Note: Order is important. -enum NET_EXPORT_PRIVATE BandwidthUsage { - kBandwidthSteady = 0, - kBandwidthUnderUsing = 1, - kBandwidthDraining = 2, - kBandwidthOverUsing = 3, -}; - -// Normal state transition diagram -// -// kBandwidthUnderUsing -// | -// | -// kBandwidthSteady -// | ^ -// | | -// kBandwidthOverUsing | -// | | -// | | -// kBandwidthDraining -// -// The above transitions is in normal operation, with extreme values we don't -// enforce the state transitions, hence you could in extreme scenarios go -// between any states. -// -// kBandwidthSteady When the packets arrive in the same pace as we sent -// them. In this state we can increase our send pace. -// -// kBandwidthOverUsing When the packets arrive slower than the pace we sent -// them. In this state we should decrease our send pace. -// When we enter into this state we will also get an -// estimate on how much delay we have built up. The -// reduction in send pace should be chosen to drain the -// built up delay within reasonable time. -// -// kBandwidthUnderUsing When the packets arrive faster than the pace we sent -// them. In this state another stream disappeared from -// a shared link leaving us more available bandwidth. -// In this state we should hold our pace to make sure we -// fully drain the buffers before we start increasing -// our send rate. We do this to avoid operating with -// semi-full buffers. -// -// kBandwidthDraining We can only be in this state after we have been in a -// overuse state. In this state we should hold our pace -// to make sure we fully drain the buffers before we -// start increasing our send rate. We do this to avoid -// operating with semi-full buffers. - -class NET_EXPORT_PRIVATE InterArrivalOveruseDetector { - public: - InterArrivalOveruseDetector(); - - // Update the statistics with the received delta times, call for every - // received delta time. This function assumes that there is no re-orderings. - // If multiple packets are sent at the same time (identical send_time) - // last_of_send_time should be set to false for all but the last calls to - // this function. If there is only one packet sent at a given time - // last_of_send_time must be true. - // received_delta is the time difference between receiving this packet and the - // previously received packet. - void OnAcknowledgedPacket(QuicPacketSequenceNumber sequence_number, - QuicTime send_time, - bool last_of_send_time, - QuicTime receive_time); - - // Get the current estimated state and update the estimated congestion delay. - // |estimated_congestion_delay| will be updated with the estimated built up - // buffer delay; it must not be NULL as it will be updated with the estimate. - // Note 1: estimated_buffer_delay will only be valid when kBandwidthOverUsing - // is returned. - // Note 2: it's assumed that the pacer lower its send pace to drain the - // built up buffer within reasonable time. The pacer should use the - // estimated_buffer_delay as a guidance on how much to back off. - // Note 3: The absolute value of estimated_congestion_delay is less reliable - // than the state itself. It is also biased to low since we can't know - // how full the buffers are when the flow starts. - BandwidthUsage GetState(QuicTime::Delta* estimated_congestion_delay); - - private: - struct PacketGroup { - PacketGroup() - : send_time(QuicTime::Zero()), - last_receive_time(QuicTime::Zero()) { - } - QuicTime send_time; - QuicTime last_receive_time; - }; - - // Update the statistics with the absolute receive time relative to the - // absolute send time. - void UpdateSendReceiveTimeOffset(QuicTime::Delta offset); - - // Update the filter with this new data point. - void UpdateFilter(QuicTime::Delta received_delta, - QuicTime::Delta sent_delta); - - // Update the estimate with this residual. - void UpdateDeltaEstimate(QuicTime::Delta residual); - - // Estimate the state based on the slope of the changes. - void DetectSlope(int64 sigma_delta); - - // Estimate the state based on the accumulated drift of the changes. - void DetectDrift(int64 sigma_delta); - - // Current grouping of packets that were sent at the same time. - PacketGroup current_packet_group_; - // Grouping of packets that were sent at the same time, just before the - // current_packet_group_ above. - PacketGroup previous_packet_group_; - // Sequence number of the last acknowledged packet. - QuicPacketSequenceNumber last_sequence_number_; - // Number of received delta times with unique send time. - int num_of_deltas_; - // Estimated accumulation of received delta times. - // Note: Can be negative and can drift over time which is why we bias it - // towards 0 and reset it given some triggers. - QuicTime::Delta accumulated_deltas_; - // Current running mean of our received delta times. - int delta_mean_; - // Current running variance of our received delta times. - int64 delta_variance_; - // Number of overuse signals currently triggered in this state. - // Note: negative represent underuse. - int delta_overuse_counter_; - // State estimated by the delta times. - BandwidthUsage delta_estimate_; - // Number of overuse signals currently triggered in this state. - // Note: negative represent underuse. - int slope_overuse_counter_; - // State estimated by the slope of the delta times. - BandwidthUsage slope_estimate_; - // Lowest offset between send and receive time ever received in this session. - QuicTime::Delta send_receive_offset_; - // Last received time difference between our normalized send and receive time. - QuicTime::Delta estimated_congestion_delay_; - - DISALLOW_COPY_AND_ASSIGN(InterArrivalOveruseDetector); -}; - -} // namespace net - -#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_OVERUSE_DETECTOR_H_ diff --git a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc deleted file mode 100644 index 8d37749a577..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc +++ /dev/null @@ -1,1114 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stdlib.h> - -#include <cmath> - -#include "base/logging.h" -#include "base/rand_util.h" -#include "net/quic/congestion_control/inter_arrival_overuse_detector.h" -#include "net/quic/test_tools/mock_clock.h" -#include "testing/gtest/include/gtest/gtest.h" - -static const double kPi = 3.14159265; - -namespace net { -namespace test { - -class InterArrivalOveruseDetectorTest : public ::testing::Test { - protected: - InterArrivalOveruseDetectorTest(); - - QuicTime::Delta GaussianRandom(QuicTime::Delta mean, - QuicTime::Delta standard_deviation); - - int Run100000Samples(int packets_per_burst, - QuicTime::Delta mean, - QuicTime::Delta standard_deviation); - - int RunUntilOveruse(int packets_per_burst, - QuicTime::Delta mean, - QuicTime::Delta standard_deviation, - QuicTime::Delta drift_per_burst, - QuicTime::Delta *estimated_buffer_delay); - - int RunUntilSteady(int packets_per_burst, - QuicTime::Delta mean, - QuicTime::Delta standard_deviation, - QuicTime::Delta drift_per_burst); - - int RunUntilNotDraining(int packets_per_burst, - QuicTime::Delta mean, - QuicTime::Delta standard_deviation, - QuicTime::Delta drift_per_burst); - - int RunUntilUnderusing(int packets_per_burst, - QuicTime::Delta mean, - QuicTime::Delta standard_deviation, - QuicTime::Delta drift_per_burst); - - void RunXBursts(int bursts, - int packets_per_burst, - QuicTime::Delta mean, - QuicTime::Delta standard_deviation, - QuicTime::Delta drift_per_burst); - - QuicPacketSequenceNumber sequence_number_; - MockClock send_clock_; - MockClock receive_clock_; - QuicTime::Delta drift_from_mean_; - InterArrivalOveruseDetector overuse_detector_; - unsigned int seed_; -}; - -InterArrivalOveruseDetectorTest::InterArrivalOveruseDetectorTest() - : sequence_number_(1), - drift_from_mean_(QuicTime::Delta::Zero()), - seed_(1234) { -} - -QuicTime::Delta InterArrivalOveruseDetectorTest::GaussianRandom( - QuicTime::Delta mean, - QuicTime::Delta standard_deviation) { - // Creating a Normal distribution variable from two independent uniform - // variables based on the Box-Muller transform. - double uniform1 = base::RandDouble(); - double uniform2 = base::RandDouble(); - - QuicTime::Delta random = QuicTime::Delta::FromMicroseconds( - static_cast<int>(standard_deviation.ToMicroseconds() * - sqrt(-2 * log(uniform1)) * cos(2 * kPi * uniform2))). - Add(mean).Subtract(drift_from_mean_); - if (random < QuicTime::Delta::Zero()) { - // Don't do negative deltas. - drift_from_mean_ = drift_from_mean_.Subtract(mean); - return QuicTime::Delta::Zero(); - } - drift_from_mean_ = drift_from_mean_.Add(random).Subtract(mean); - return random; -} - -int InterArrivalOveruseDetectorTest::Run100000Samples( - int packets_per_burst, - QuicTime::Delta mean, - QuicTime::Delta standard_deviation) { - int unique_overuse = 0; - int last_overuse = -1; - for (int i = 0; i < 100000; ++i) { - // Assume that we send out the packets with perfect pacing. - send_clock_.AdvanceTime(mean); - QuicTime send_time = send_clock_.ApproximateNow(); - // Do only one random delta for all packets in a burst. - receive_clock_.AdvanceTime(GaussianRandom(mean, standard_deviation)); - QuicTime receive_time = receive_clock_.ApproximateNow(); - for (int j = 0; j <= packets_per_burst; ++j) { - overuse_detector_.OnAcknowledgedPacket(sequence_number_++, - send_time, - (j == packets_per_burst), - receive_time); - } - // We expect to randomly hit a few false detects, count the unique - // overuse events, hence not multiple signals in a row. - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - if (kBandwidthOverUsing == overuse_detector_.GetState( - &estimated_buffer_delay)) { - if (last_overuse + 1 != i) { - unique_overuse++; - } - last_overuse = i; - } - } - return unique_overuse; -} - -int InterArrivalOveruseDetectorTest::RunUntilOveruse( - int packets_per_burst, - QuicTime::Delta mean, - QuicTime::Delta standard_deviation, - QuicTime::Delta drift_per_burst, - QuicTime::Delta *estimated_buffer_delay) { - // Simulate a higher send pace, that is too high. - for (int i = 0; i < 1000; ++i) { - send_clock_.AdvanceTime(mean); - QuicTime send_time = send_clock_.ApproximateNow(); - // Do only one random delta for all packets in a burst. - receive_clock_.AdvanceTime(GaussianRandom(mean.Add(drift_per_burst), - standard_deviation)); - QuicTime receive_time = receive_clock_.ApproximateNow(); - for (int j = 0; j <= packets_per_burst; ++j) { - overuse_detector_.OnAcknowledgedPacket(sequence_number_++, - send_time, - (j == packets_per_burst), - receive_time); - } - if (kBandwidthOverUsing == overuse_detector_.GetState( - estimated_buffer_delay)) { - return i + 1; - } - } - return -1; -} - -int InterArrivalOveruseDetectorTest::RunUntilSteady( - int packets_per_burst, - QuicTime::Delta mean, - QuicTime::Delta standard_deviation, - QuicTime::Delta drift_per_burst) { - // Simulate a lower send pace, that is lower than the capacity. - for (int i = 0; i < 1000; ++i) { - send_clock_.AdvanceTime(mean); - QuicTime send_time = send_clock_.ApproximateNow(); - // Do only one random delta for all packets in a burst. - receive_clock_.AdvanceTime(GaussianRandom(mean.Subtract(drift_per_burst), - standard_deviation)); - QuicTime receive_time = receive_clock_.ApproximateNow(); - for (int j = 0; j <= packets_per_burst; ++j) { - overuse_detector_.OnAcknowledgedPacket(sequence_number_++, - send_time, - (j == packets_per_burst), - receive_time); - } - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - if (kBandwidthSteady == - overuse_detector_.GetState(&estimated_buffer_delay)) { - return i + 1; - } - } - return -1; -} - -int InterArrivalOveruseDetectorTest::RunUntilNotDraining( - int packets_per_burst, - QuicTime::Delta mean, - QuicTime::Delta standard_deviation, - QuicTime::Delta drift_per_burst) { - // Simulate a lower send pace, that is lower than the capacity. - for (int i = 0; i < 1000; ++i) { - send_clock_.AdvanceTime(mean); - QuicTime send_time = send_clock_.ApproximateNow(); - // Do only one random delta for all packets in a burst. - receive_clock_.AdvanceTime(GaussianRandom(mean.Subtract(drift_per_burst), - standard_deviation)); - QuicTime receive_time = receive_clock_.ApproximateNow(); - for (int j = 0; j <= packets_per_burst; ++j) { - overuse_detector_.OnAcknowledgedPacket(sequence_number_++, - send_time, - (j == packets_per_burst), - receive_time); - } - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - if (kBandwidthDraining > - overuse_detector_.GetState(&estimated_buffer_delay)) { - return i + 1; - } - } - return -1; -} - -int InterArrivalOveruseDetectorTest::RunUntilUnderusing( - int packets_per_burst, - QuicTime::Delta mean, - QuicTime::Delta standard_deviation, - QuicTime::Delta drift_per_burst) { - // Simulate a lower send pace, that is lower than the capacity. - for (int i = 0; i < 1000; ++i) { - send_clock_.AdvanceTime(mean); - QuicTime send_time = send_clock_.ApproximateNow(); - // Do only one random delta for all packets in a burst. - receive_clock_.AdvanceTime(GaussianRandom(mean.Subtract(drift_per_burst), - standard_deviation)); - QuicTime receive_time = receive_clock_.ApproximateNow(); - for (int j = 0; j <= packets_per_burst; ++j) { - overuse_detector_.OnAcknowledgedPacket(sequence_number_++, - send_time, - (j == packets_per_burst), - receive_time); - } - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - if (kBandwidthUnderUsing == overuse_detector_.GetState( - &estimated_buffer_delay)) { - return i + 1; - } - } - return -1; -} - -void InterArrivalOveruseDetectorTest::RunXBursts( - int bursts, - int packets_per_burst, - QuicTime::Delta mean, - QuicTime::Delta standard_deviation, - QuicTime::Delta drift_per_burst) { - for (int i = 0; i < bursts; ++i) { - send_clock_.AdvanceTime(mean); - QuicTime send_time = send_clock_.ApproximateNow(); - // Do only one random delta for all packets in a burst. - receive_clock_.AdvanceTime(GaussianRandom(mean.Add(drift_per_burst), - standard_deviation)); - QuicTime receive_time = receive_clock_.ApproximateNow(); - for (int j = 0; j <= packets_per_burst; ++j) { - overuse_detector_.OnAcknowledgedPacket(sequence_number_++, - send_time, - (j == packets_per_burst), - receive_time); - } - } -} - -// TODO(pwestin): test packet loss impact on accuracy. -// TODO(pwestin): test colored noise by dropping late frames. - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_TestNoise) { - int count[100]; - memset(count, 0, sizeof(count)); - for (int i = 0; i < 10000; ++i) { - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(30); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(10); - count[GaussianRandom(mean, standard_deviation).ToMilliseconds()]++; - } - for (int j = 0; j < 100; ++j) { - DLOG(INFO) << j << ":" << count[j]; - } -} - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_SimpleNonOveruse) { - QuicPacketSequenceNumber sequence_number = 1; - QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10); - - for (int i = 0; i < 1000; ++i) { - QuicTime send_time = send_clock_.ApproximateNow(); - QuicTime receive_time = receive_clock_.ApproximateNow(); - overuse_detector_.OnAcknowledgedPacket(sequence_number++, - send_time, - true, - receive_time); - send_clock_.AdvanceTime(delta); - receive_clock_.AdvanceTime(delta); - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - EXPECT_EQ(kBandwidthSteady, - overuse_detector_.GetState(&estimated_buffer_delay)); - } -} - -TEST_F(InterArrivalOveruseDetectorTest, - DISABLED_SimpleNonOveruseSendClockAhead) { - QuicPacketSequenceNumber sequence_number = 1; - QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10); - send_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1234)); - - for (int i = 0; i < 1000; ++i) { - QuicTime send_time = send_clock_.ApproximateNow(); - QuicTime receive_time = receive_clock_.ApproximateNow(); - overuse_detector_.OnAcknowledgedPacket(sequence_number++, - send_time, - true, - receive_time); - send_clock_.AdvanceTime(delta); - receive_clock_.AdvanceTime(delta); - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - EXPECT_EQ(kBandwidthSteady, - overuse_detector_.GetState(&estimated_buffer_delay)); - } -} - -TEST_F(InterArrivalOveruseDetectorTest, - DISABLED_SimpleNonOveruseSendClockBehind) { - QuicPacketSequenceNumber sequence_number = 1; - QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10); - receive_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1234)); - - for (int i = 0; i < 1000; ++i) { - QuicTime send_time = send_clock_.ApproximateNow(); - QuicTime receive_time = receive_clock_.ApproximateNow(); - overuse_detector_.OnAcknowledgedPacket(sequence_number++, - send_time, - true, - receive_time); - send_clock_.AdvanceTime(delta); - receive_clock_.AdvanceTime(delta); - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - EXPECT_EQ(kBandwidthSteady, - overuse_detector_.GetState(&estimated_buffer_delay)); - } -} - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_SimpleNonOveruseWithVariance) { - QuicPacketSequenceNumber sequence_number = 1; - for (int i = 0; i < 1000; ++i) { - if (i % 2) { - receive_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); - } else { - receive_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(15)); - } - QuicTime send_time = send_clock_.ApproximateNow(); - QuicTime receive_time = receive_clock_.ApproximateNow(); - overuse_detector_.OnAcknowledgedPacket(sequence_number++, - send_time, - true, - receive_time); - send_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - EXPECT_EQ(kBandwidthSteady, - overuse_detector_.GetState(&estimated_buffer_delay)); - } -} - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_SimpleOveruse) { - QuicPacketSequenceNumber sequence_number = 1; - QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10); - QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(5); - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - - for (int i = 0; i < 100000; ++i) { - send_clock_.AdvanceTime(send_delta); - receive_clock_.AdvanceTime(received_delta); - QuicTime send_time = send_clock_.ApproximateNow(); - QuicTime receive_time = receive_clock_.ApproximateNow(); - // Sending 2 packets the same time as that is what we expect to do. - overuse_detector_.OnAcknowledgedPacket(sequence_number++, - send_time, - false, - receive_time); - receive_clock_.AdvanceTime(received_delta); - receive_time = receive_clock_.ApproximateNow(); - overuse_detector_.OnAcknowledgedPacket(sequence_number++, - send_time, - true, - receive_time); - - EXPECT_EQ(kBandwidthSteady, - overuse_detector_.GetState(&estimated_buffer_delay)); - } - // Simulate a higher send pace, that is too high by receiving 1 millisecond - // late per packet. - received_delta = QuicTime::Delta::FromMilliseconds(6); - send_clock_.AdvanceTime(send_delta); - receive_clock_.AdvanceTime(received_delta); - QuicTime send_time = send_clock_.ApproximateNow(); - QuicTime receive_time = receive_clock_.ApproximateNow(); - overuse_detector_.OnAcknowledgedPacket(sequence_number++, - send_time, - false, - receive_time); - receive_clock_.AdvanceTime(received_delta); - receive_time = receive_clock_.ApproximateNow(); - overuse_detector_.OnAcknowledgedPacket(sequence_number++, - send_time, - true, - receive_time); - EXPECT_EQ(kBandwidthOverUsing, - overuse_detector_.GetState(&estimated_buffer_delay)); -} - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance10Kbit) { - int packets_per_burst = 1; - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1000); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5); - QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5); - - int overuse_signals = Run100000Samples(packets_per_burst, - mean, - standard_deviation); - EXPECT_GE(1, overuse_signals); - - // Simulate a higher send pace, that is too high. - // With current tuning we require 6 updates with 5 milliseconds before - // detection. - // Resulting in a minimal buffer build up of 30 milliseconds. - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - int bursts_until_overuse = RunUntilOveruse(packets_per_burst, - mean, - standard_deviation, - drift_per_burst, - &estimated_buffer_delay); - EXPECT_GE(6, bursts_until_overuse); - EXPECT_NEAR(40, estimated_buffer_delay.ToMilliseconds(), 15); - - // After draining the buffers we are back in a normal state. - int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(6, bursts_until_not_draining); - - // After draining the buffer additionally we detect an underuse. - int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(7, bursts_until_underusing); -} - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance10Kbit) { - int packets_per_burst = 1; - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1000); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(50); - QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(50); - - int overuse_signals = Run100000Samples(packets_per_burst, - mean, - standard_deviation); - EXPECT_GE(1, overuse_signals); - - // Simulate a higher send pace, that is too high. - // With current tuning we require 6 updates with 50 milliseconds before - // detection. - // Resulting in a minimal buffer build up of 300 milliseconds. - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - int bursts_until_overuse = RunUntilOveruse(packets_per_burst, - mean, - standard_deviation, - drift_per_burst, - &estimated_buffer_delay); - EXPECT_GE(6, bursts_until_overuse); - EXPECT_NEAR(400, estimated_buffer_delay.ToMilliseconds(), 150); - - // After draining the buffers we are back in a normal state. - int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(6, bursts_until_not_draining); - - // After draining the buffer additionally we detect an underuse. - int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(7, bursts_until_underusing); -} - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance100Kbit) { - int packets_per_burst = 1; - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(100); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5); - QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5); - - int overuse_signals = Run100000Samples(packets_per_burst, - mean, - standard_deviation); - EXPECT_GE(1, overuse_signals); - - // Simulate a higher send pace, that is too high. - // With current tuning we require 6 updates with 5 milliseconds - // before detection. - // Resulting in a minimal buffer build up of 30 ms. - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - int bursts_until_overuse = RunUntilOveruse(packets_per_burst, - mean, - standard_deviation, - drift_per_burst, - &estimated_buffer_delay); - EXPECT_GE(6, bursts_until_overuse); - EXPECT_NEAR(40, estimated_buffer_delay.ToMilliseconds(), 15); - - // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before - // detection. - RunXBursts(1, - packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - - // After draining the buffers we are back in a normal state. - int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(7, bursts_until_not_draining); - - // After draining the buffer additionally we detect an underuse. - int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(5, bursts_until_underusing); -} - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance100Kbit) { - int packets_per_burst = 1; - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(100); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(50); - QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(50); - - int overuse_signals = Run100000Samples(packets_per_burst, - mean, - standard_deviation); - EXPECT_GE(1, overuse_signals); - - // Simulate a higher send pace, that is too high. - // With current tuning we require 4 updates with 50 milliseconds - // before detection. - // Resulting in a minimal buffer build up of 200 ms. - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - int bursts_until_overuse = RunUntilOveruse(packets_per_burst, - mean, - standard_deviation, - drift_per_burst, - &estimated_buffer_delay); - EXPECT_GE(4, bursts_until_overuse); - EXPECT_NEAR(300, estimated_buffer_delay.ToMilliseconds(), 150); - - // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before - // detection. - RunXBursts(1, - packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - - // After draining the buffers we are back in a normal state. - int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(5, bursts_until_not_draining); - - // After draining the buffer additionally we detect an underuse. - int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(5, bursts_until_underusing); -} - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance1Mbit) { - int packets_per_burst = 1; - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5); - QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5); - - int overuse_signals = Run100000Samples(packets_per_burst, - mean, - standard_deviation); - EXPECT_GE(1, overuse_signals); - - // Simulate a higher send pace, that is too high. - // With current tuning we require 4 updates with 5 millisecond - // before detection. - // Resulting in a minimal buffer build up of 20 ms. - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - int bursts_until_overuse = RunUntilOveruse(packets_per_burst, - mean, - standard_deviation, - drift_per_burst, - &estimated_buffer_delay); - EXPECT_GE(4, bursts_until_overuse); - EXPECT_NEAR(30, estimated_buffer_delay.ToMilliseconds(), 15); - - // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before - // detection. - RunXBursts(10, - packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - - // After draining the buffers we are back in a normal state. - int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(14, bursts_until_not_draining); - - // After draining the buffer additionally we detect an underuse. - int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(4, bursts_until_underusing); -} - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1Mbit) { - int packets_per_burst = 1; - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1); - QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(1); - - int overuse_signals = Run100000Samples(packets_per_burst, - mean, - standard_deviation); - EXPECT_GE(1, overuse_signals); - - // Simulate a higher send pace, that is too high. - // With current tuning we require 6 updates with 1 millisecond - // before detection. - // Resulting in a minimal buffer build up of 6 ms. - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - int bursts_until_overuse = RunUntilOveruse(packets_per_burst, - mean, - standard_deviation, - drift_per_burst, - &estimated_buffer_delay); - EXPECT_GE(6, bursts_until_overuse); - EXPECT_NEAR(8, estimated_buffer_delay.ToMilliseconds(), 3); - - // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before - // detection. - RunXBursts(10, - packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - - // After draining the buffers we are back in a normal state. - int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(16, bursts_until_not_draining); - - // After draining the buffer additionally we detect an underuse. - int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(4, bursts_until_underusing); -} - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance20Mbit) { - int packets_per_burst = 2; - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMicroseconds(500); - QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(500); - - int overuse_signals = Run100000Samples(packets_per_burst, - mean, - standard_deviation); - EXPECT_GE(1, overuse_signals); - - // Simulate a higher send pace, that is too high. - // With current tuning we require 4 updates with 500 microsecond - // before detection. - // Resulting in a minimal buffer build up of 2 ms. - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - int bursts_until_overuse = RunUntilOveruse(packets_per_burst, - mean, - standard_deviation, - drift_per_burst, - &estimated_buffer_delay); - EXPECT_GE(4, bursts_until_overuse); - EXPECT_NEAR(3, estimated_buffer_delay.ToMilliseconds(), 2); - - // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before - // detection. - RunXBursts(100, - packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - - // After draining the buffers we are back in a normal state. - int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_NEAR(100, bursts_until_not_draining, 10); - - // After draining the buffer additionally we detect an underuse. - int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(1, bursts_until_underusing); -} - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance100Mbit) { - int packets_per_burst = 10; - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMicroseconds(500); - QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(500); - - int overuse_signals = Run100000Samples(packets_per_burst, - mean, - standard_deviation); - EXPECT_GE(1, overuse_signals); - - // Simulate a higher send pace, that is too high. - // With current tuning we require 4 updates with 500 microsecond - // before detection. - // Resulting in a minimal buffer build up of 2 ms. - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - int bursts_until_overuse = RunUntilOveruse(packets_per_burst, - mean, - standard_deviation, - drift_per_burst, - &estimated_buffer_delay); - EXPECT_GE(4, bursts_until_overuse); - EXPECT_NEAR(3, estimated_buffer_delay.ToMilliseconds(), 2); - - // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before - // detection. - RunXBursts(100, - packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - - // After draining the buffers we are back in a normal state. - int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_NEAR(100, bursts_until_not_draining, 10); - - // After draining the buffer additionally we detect an underuse. - int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(1, bursts_until_underusing); -} - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_VeryHighVariance100Mbit) { - int packets_per_burst = 10; - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5); - QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(500); - - // We get false overuse in this scenario due to that the standard deviation is - // higher than our mean and the fact that a delta time can't be negative. This - // results in an under estimated standard deviation in the estimator causing - // false detects. - int overuse_signals = Run100000Samples(packets_per_burst, - mean, - standard_deviation); - EXPECT_GE(2000, overuse_signals); - - // Simulate a higher send pace, that is too high. - // With current tuning we require 25 updates with 500 microsecond - // before detection. - // Resulting in a minimal buffer build up of 12.5 ms. - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - int bursts_until_overuse = RunUntilOveruse(packets_per_burst, - mean, - standard_deviation, - drift_per_burst, - &estimated_buffer_delay); - EXPECT_GE(17, bursts_until_overuse); - EXPECT_NEAR(22, estimated_buffer_delay.ToMilliseconds(), 15); - - // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before - // detection. - RunXBursts(100, - packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - - // After draining the buffers we are back in a normal state. - int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(117, bursts_until_not_draining); - - // After draining the buffer additionally we detect an underuse. - int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(1, bursts_until_underusing); -} - -// -// Tests simulating big drop in bitrate. -// - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1MbitTo100Kbit) { - int packets_per_burst = 1; - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1); - QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(100); - - int overuse_signals = Run100000Samples(packets_per_burst, - mean, - standard_deviation); - EXPECT_GE(1, overuse_signals); - - // Simulate a higher send pace, that is too high. - // With current tuning we require 1 update with 100 millisecond - // before detection. - // Resulting in a minimal buffer build up of 100 ms. - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - int bursts_until_overuse = RunUntilOveruse(packets_per_burst, - mean, - standard_deviation, - drift_per_burst, - &estimated_buffer_delay); - EXPECT_GE(1, bursts_until_overuse); - EXPECT_NEAR(104, estimated_buffer_delay.ToMilliseconds(), 3); - - // Back off 20% lower than estimate to drain. - mean = QuicTime::Delta::FromMilliseconds(100); - drift_per_burst = QuicTime::Delta::FromMilliseconds(20); - - // After draining the buffers we are back in a normal state. - int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(5, bursts_until_not_draining); - - // After draining the buffer additionally we detect an underuse. - int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(3, bursts_until_underusing); -} - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance20MbitTo1Mbit) { - int packets_per_burst = 2; - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMicroseconds(500); - QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(10); - - int overuse_signals = Run100000Samples(packets_per_burst, - mean, - standard_deviation); - EXPECT_GE(1, overuse_signals); - - // Simulate a higher send pace, that is too high. - // With current tuning we require 1 update with 10 milliseconds - // before detection. - // Resulting in a minimal buffer build up of 10 ms. - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - int bursts_until_overuse = RunUntilOveruse(packets_per_burst, - mean, - standard_deviation, - drift_per_burst, - &estimated_buffer_delay); - EXPECT_GE(1, bursts_until_overuse); - EXPECT_NEAR(12, estimated_buffer_delay.ToMilliseconds(), 2); - - // Back off 20% lower than estimate to drain. - mean = QuicTime::Delta::FromMilliseconds(10); - drift_per_burst = QuicTime::Delta::FromMilliseconds(2); - - // After draining the buffers we are back in a normal state. - int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(5, bursts_until_not_draining); - - // After draining the buffer additionally we detect an underuse. - int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(3, bursts_until_underusing); -} - -// -// Tests that we can detect slow drifts. -// - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1MbitSmallSteps) { - int packets_per_burst = 1; - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1); - QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(100); - - int overuse_signals = Run100000Samples(packets_per_burst, - mean, - standard_deviation); - EXPECT_GE(1, overuse_signals); - - // Simulate a higher send pace, that is too high. - // With current tuning we require 41 updates with 100 microseconds before - // detection. - // Resulting in a minimal buffer build up of 4.1 ms. - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - int bursts_until_overuse = RunUntilOveruse(packets_per_burst, - mean, - standard_deviation, - drift_per_burst, - &estimated_buffer_delay); - EXPECT_GE(41, bursts_until_overuse); - EXPECT_NEAR(7, estimated_buffer_delay.ToMilliseconds(), 3); - - // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before - // detection. - RunXBursts(10, - packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - - // After draining the buffers we are back in a normal state. - int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(29, bursts_until_not_draining); - - // After draining the buffer additionally we detect an underuse. - int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(71, bursts_until_underusing); -} - -TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1MbitTinySteps) { - int packets_per_burst = 1; - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1); - QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(10); - - int overuse_signals = Run100000Samples(packets_per_burst, - mean, - standard_deviation); - EXPECT_GE(1, overuse_signals); - - // Simulate a higher send pace, that is too high. - // With current tuning we require 345 updates with 10 microseconds before - // detection. - // Resulting in a minimal buffer build up of 3.45 ms. - QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); - int bursts_until_overuse = RunUntilOveruse(packets_per_burst, - mean, - standard_deviation, - drift_per_burst, - &estimated_buffer_delay); - EXPECT_GE(345, bursts_until_overuse); - EXPECT_NEAR(7, estimated_buffer_delay.ToMilliseconds(), 3); - - // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before - // detection. - RunXBursts(10, - packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - - // After draining the buffers we are back in a normal state. - int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(18, bursts_until_not_draining); - - // After draining the buffer additionally we detect an underuse. - int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(683, bursts_until_underusing); -} - -// -// Tests simulating starting with full buffers. -// - -TEST_F(InterArrivalOveruseDetectorTest, - DISABLED_StartedWithFullBuffersHighVariance1Mbit) { - int packets_per_burst = 1; - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5); - QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5); - - int overuse_signals = Run100000Samples(packets_per_burst, - mean, - standard_deviation); - EXPECT_GE(1, overuse_signals); - - // Simulate a lower send pace. - // Draining the buffer until we detect an underuse. - int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(6, bursts_until_underusing); - - // After draining the buffers we are back in a normal state. - drift_per_burst = QuicTime::Delta::FromMilliseconds(0); - int bursts_until_steady = RunUntilSteady(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(6, bursts_until_steady); -} - -TEST_F(InterArrivalOveruseDetectorTest, - DISABLED_StartedWithFullBuffersHighVariance1MbitSlowDrift) { - int packets_per_burst = 1; - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5); - QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(1); - - int overuse_signals = Run100000Samples(packets_per_burst, - mean, - standard_deviation); - EXPECT_GE(1, overuse_signals); - - // Simulate a faster receive pace. - // Draining the buffer until we detect an underuse. - int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(21, bursts_until_underusing); - - // Simulate an RTT of 100 ms. Hence underusing for additional 100 ms before - // detection. - drift_per_burst = QuicTime::Delta::FromMilliseconds(-1); - RunXBursts(10, - packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - - // After draining the buffers we are back in a normal state. - drift_per_burst = QuicTime::Delta::FromMilliseconds(0); - int bursts_until_steady = RunUntilSteady(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(4, bursts_until_steady); -} - -TEST_F(InterArrivalOveruseDetectorTest, - DISABLED_StartedWithFullBuffersLowVariance1Mbit) { - int packets_per_burst = 1; - QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10); - QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1); - QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(1); - - int overuse_signals = Run100000Samples(packets_per_burst, - mean, - standard_deviation); - EXPECT_GE(1, overuse_signals); - - // Simulate a lower send pace. - // Draining the buffer until we detect an underuse. - int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(5, bursts_until_underusing); - - // Simulate an RTT of 100 ms. Hence underusing for additional 100 ms before - // detection. - drift_per_burst = QuicTime::Delta::FromMilliseconds(-1); - RunXBursts(10, - packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - - // After draining the buffers we are back in a normal state. - drift_per_burst = QuicTime::Delta::FromMilliseconds(0); - int bursts_until_steady = RunUntilSteady(packets_per_burst, - mean, - standard_deviation, - drift_per_burst); - EXPECT_GE(41, bursts_until_steady); -} - -} // namespace test -} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_probe.cc b/chromium/net/quic/congestion_control/inter_arrival_probe.cc deleted file mode 100644 index 6d3af3208fe..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_probe.cc +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/congestion_control/inter_arrival_probe.h" - -#include "base/basictypes.h" -#include "base/logging.h" - -namespace { -const int kProbeSizePackets = 10; -const net::QuicByteCount kMinPacketSize = 500; -const int64 kDefaultBytesPerSecond = 40000; -const float kUncertainScaleFactor = 0.5; // TODO(pwestin): revisit this factor. -} - -namespace net { - -InterArrivalProbe::InterArrivalProbe(QuicByteCount max_segment_size) - : max_segment_size_(max_segment_size), - estimate_available_(false), - available_channel_estimate_(QuicBandwidth::Zero()), - unacked_data_(0) { -} - -InterArrivalProbe::~InterArrivalProbe() { -} - -void InterArrivalProbe::set_max_segment_size(QuicByteCount max_segment_size) { - max_segment_size_ = max_segment_size; -} - -bool InterArrivalProbe::GetEstimate(QuicBandwidth* available_channel_estimate) { - if (!estimate_available_) { - return false; - } - *available_channel_estimate = available_channel_estimate_; - return true; -} - -void InterArrivalProbe::OnPacketSent(QuicByteCount bytes) { - if (!estimate_available_) { - unacked_data_ += bytes; - } -} - -void InterArrivalProbe::OnAcknowledgedPacket(QuicByteCount bytes) { - if (!estimate_available_) { - DCHECK_LE(bytes, unacked_data_); - unacked_data_ -= bytes; - } -} - -QuicByteCount InterArrivalProbe::GetAvailableCongestionWindow() { - if (estimate_available_) { - return 0; - } - return (kProbeSizePackets * max_segment_size_) - unacked_data_; -} - -void InterArrivalProbe::OnIncomingFeedback( - QuicPacketSequenceNumber sequence_number, - QuicByteCount bytes_sent, - QuicTime time_sent, - QuicTime time_received) { - if (estimate_available_) { - return; - } - - if (available_channel_estimator_.get() == NULL) { - if (bytes_sent < kMinPacketSize) { - // Packet too small to start the probe phase. - return; - } - first_sequence_number_ = sequence_number; - available_channel_estimator_.reset(new AvailableChannelEstimator( - sequence_number, time_sent, time_received)); - return; - } - - available_channel_estimator_->OnIncomingFeedback(sequence_number, - bytes_sent, - time_sent, - time_received); - if (sequence_number < kProbeSizePackets - 1 + first_sequence_number_) { - // We need more feedback before we have a probe estimate. - return; - } - // Get the current estimated available channel capacity. - // available_channel_estimate is invalid if kAvailableChannelEstimateUnknown - // is returned. - QuicBandwidth available_channel_estimate = QuicBandwidth::Zero(); - AvailableChannelEstimateState available_channel_estimate_state = - available_channel_estimator_->GetAvailableChannelEstimate( - &available_channel_estimate); - switch (available_channel_estimate_state) { - case kAvailableChannelEstimateUnknown: - // Backup when we miss our probe. - available_channel_estimate_ = - QuicBandwidth::FromBytesPerSecond(kDefaultBytesPerSecond); - break; - case kAvailableChannelEstimateUncertain: - available_channel_estimate_ = - available_channel_estimate.Scale(kUncertainScaleFactor); - break; - case kAvailableChannelEstimateGood: - available_channel_estimate_ = available_channel_estimate; - break; - case kAvailableChannelEstimateSenderLimited: - available_channel_estimate_ = - std::max(available_channel_estimate, - QuicBandwidth::FromBytesPerSecond(kDefaultBytesPerSecond)); - break; - } - estimate_available_ = true; - available_channel_estimator_.reset(NULL); - DVLOG(1) << "Probe estimate:" - << available_channel_estimate_.ToKBitsPerSecond() - << " Kbits/s"; -} - -} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_probe.h b/chromium/net/quic/congestion_control/inter_arrival_probe.h deleted file mode 100644 index 0cba7eec8db..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_probe.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Class that handle the initial probing phase of inter arrival congestion -// control. -#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_PROBE_H_ -#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_PROBE_H_ - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "net/base/net_export.h" -#include "net/quic/congestion_control/available_channel_estimator.h" -#include "net/quic/quic_bandwidth.h" - -namespace net { - -class NET_EXPORT_PRIVATE InterArrivalProbe { - public: - explicit InterArrivalProbe(QuicByteCount max_segment_size); - ~InterArrivalProbe(); - - void set_max_segment_size(QuicByteCount max_segment_size); - - // Call every time a packet is sent to the network. - void OnPacketSent(QuicByteCount bytes); - - // Call once for each sent packet that we receive an acknowledgement from - // the peer for. - void OnAcknowledgedPacket(QuicByteCount bytes); - - // Call to get the number of bytes that can be sent as part of this probe. - QuicByteCount GetAvailableCongestionWindow(); - - // Call once for each sent packet we receive a congestion feedback from the - // peer for. - // If a peer sends both and ack and feedback for a sent packet, both - // OnAcknowledgedPacket and OnIncomingFeedback should be called. - void OnIncomingFeedback(QuicPacketSequenceNumber sequence_number, - QuicByteCount bytes_sent, - QuicTime time_sent, - QuicTime time_received); - - // Returns false as long as we are probing, available_channel_estimate is - // invalid during that time. When the probe is completed this function return - // true and available_channel_estimate contains the estimate. - bool GetEstimate(QuicBandwidth* available_channel_estimate); - - private: - QuicByteCount max_segment_size_; - scoped_ptr<AvailableChannelEstimator> available_channel_estimator_; - QuicPacketSequenceNumber first_sequence_number_; - bool estimate_available_; - QuicBandwidth available_channel_estimate_; - QuicByteCount unacked_data_; - - DISALLOW_COPY_AND_ASSIGN(InterArrivalProbe); -}; - -} // namespace net -#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_PROBE_H_ diff --git a/chromium/net/quic/congestion_control/inter_arrival_probe_test.cc b/chromium/net/quic/congestion_control/inter_arrival_probe_test.cc deleted file mode 100644 index d242a6caf4e..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_probe_test.cc +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/basictypes.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "net/quic/congestion_control/inter_arrival_probe.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { -namespace test { - -class InterArrivalProbeTest : public ::testing::Test { - protected: - InterArrivalProbeTest() - : probe_(kDefaultMaxPacketSize), - start_(QuicTime::Zero()) { - } - - InterArrivalProbe probe_; - QuicTime start_; -}; - -TEST_F(InterArrivalProbeTest, CongestionWindow) { - for (size_t i = 0; i < 10; i++) { - probe_.OnPacketSent(kDefaultMaxPacketSize); - EXPECT_EQ((9 - i) * kDefaultMaxPacketSize, - probe_.GetAvailableCongestionWindow()); - } - probe_.OnAcknowledgedPacket(kDefaultMaxPacketSize); - EXPECT_EQ(kDefaultMaxPacketSize, probe_.GetAvailableCongestionWindow()); - - probe_.OnPacketSent(kDefaultMaxPacketSize); - EXPECT_EQ(0u, probe_.GetAvailableCongestionWindow()); -} - -TEST_F(InterArrivalProbeTest, Estimate) { - QuicPacketSequenceNumber sequence_number = 1; - QuicByteCount bytes_sent = kDefaultMaxPacketSize; - QuicTime time_received = start_.Add(QuicTime::Delta::FromMilliseconds(10)); - QuicTime time_sent = start_.Add(QuicTime::Delta::FromMilliseconds(1)); - QuicBandwidth available_channel_estimate = QuicBandwidth::Zero(); - - for (size_t i = 0; i < 10; ++i) { - EXPECT_FALSE(probe_.GetEstimate(&available_channel_estimate)); - - probe_.OnIncomingFeedback(sequence_number++, - bytes_sent, - time_sent, - time_received); - time_sent = time_sent.Add(QuicTime::Delta::FromMilliseconds(1)); - time_received = time_received.Add(QuicTime::Delta::FromMilliseconds(10)); - } - EXPECT_TRUE(probe_.GetEstimate(&available_channel_estimate)); - EXPECT_EQ(kDefaultMaxPacketSize * 100, - static_cast<uint64>(available_channel_estimate.ToBytesPerSecond())); -} - -TEST_F(InterArrivalProbeTest, EstimateWithLoss) { - QuicPacketSequenceNumber sequence_number = 1; - QuicByteCount bytes_sent = kDefaultMaxPacketSize; - QuicTime time_received = start_.Add(QuicTime::Delta::FromMilliseconds(10)); - QuicTime time_sent = start_.Add(QuicTime::Delta::FromMilliseconds(1)); - QuicBandwidth available_channel_estimate = QuicBandwidth::Zero(); - - for (size_t i = 0; i < 6; ++i) { - EXPECT_FALSE(probe_.GetEstimate(&available_channel_estimate)); - - probe_.OnIncomingFeedback(sequence_number, - bytes_sent, - time_sent, - time_received); - sequence_number += 2; - time_sent = time_sent.Add(QuicTime::Delta::FromMilliseconds(1)); - time_received = time_received.Add(QuicTime::Delta::FromMilliseconds(10)); - } - EXPECT_TRUE(probe_.GetEstimate(&available_channel_estimate)); - EXPECT_EQ(kDefaultMaxPacketSize * 50, - static_cast<uint64>(available_channel_estimate.ToBytesPerSecond())); -} - -} // namespace test -} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_receiver.cc b/chromium/net/quic/congestion_control/inter_arrival_receiver.cc deleted file mode 100644 index 770b287db0b..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_receiver.cc +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/congestion_control/inter_arrival_receiver.h" - -#include "base/basictypes.h" - -namespace net { - -InterArrivalReceiver::InterArrivalReceiver() - : accumulated_number_of_recoverd_lost_packets_(0) { -} - -InterArrivalReceiver::~InterArrivalReceiver() { -} - -bool InterArrivalReceiver::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 = kInterArrival; - feedback->inter_arrival.accumulated_number_of_lost_packets = - accumulated_number_of_recoverd_lost_packets_; - - // Copy our current receive set to our feedback message, we will not resend - // this data if it is lost. - feedback->inter_arrival.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 InterArrivalReceiver::RecordIncomingPacket( - QuicByteCount /*bytes*/, - QuicPacketSequenceNumber sequence_number, - QuicTime timestamp, - bool revived) { - if (revived) { - ++accumulated_number_of_recoverd_lost_packets_; - } - received_packet_times_.insert(std::make_pair(sequence_number, timestamp)); -} - -} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_receiver.h b/chromium/net/quic/congestion_control/inter_arrival_receiver.h deleted file mode 100644 index a9de62cb1ea..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_receiver.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_RECEIVER_H_ -#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_RECEIVER_H_ - -#include "base/basictypes.h" -#include "base/compiler_specific.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 InterArrivalReceiver - : public ReceiveAlgorithmInterface { - public: - InterArrivalReceiver(); - virtual ~InterArrivalReceiver(); - - // Start implementation of ReceiveAlgorithmInterface. - virtual bool GenerateCongestionFeedback( - QuicCongestionFeedbackFrame* feedback) OVERRIDE; - - virtual void RecordIncomingPacket(QuicByteCount bytes, - QuicPacketSequenceNumber sequence_number, - QuicTime timestamp, - bool revived) OVERRIDE; - // End implementation of ReceiveAlgorithmInterface. - - private: - // We need to keep track of FEC recovered packets. - int accumulated_number_of_recoverd_lost_packets_; - - // The set of received packets since the last feedback was sent, along with - // their arrival times. - TimeMap received_packet_times_; - - DISALLOW_COPY_AND_ASSIGN(InterArrivalReceiver); -}; - -} // namespace net - -#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_RECEIVER_H_ diff --git a/chromium/net/quic/congestion_control/inter_arrival_receiver_test.cc b/chromium/net/quic/congestion_control/inter_arrival_receiver_test.cc deleted file mode 100644 index 927fb6d92f7..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_receiver_test.cc +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/logging.h" -#include "net/quic/congestion_control/inter_arrival_receiver.h" -#include "net/quic/test_tools/mock_clock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { -namespace test { - -class InterArrivalReceiverTest : public ::testing::Test { - protected: - InterArrivalReceiver receiver_; - MockClock clock_; -}; - -TEST_F(InterArrivalReceiverTest, 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, false); - - QuicCongestionFeedbackFrame feedback; - 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, true); - clock_.AdvanceTime(received_delta); - receive_timestamp = clock_.ApproximateNow(); - receiver_.RecordIncomingPacket(1, 3, receive_timestamp, false); - - ASSERT_TRUE(receiver_.GenerateCongestionFeedback(&feedback)); - - EXPECT_EQ(kInterArrival, feedback.type); - EXPECT_EQ(1, feedback.inter_arrival.accumulated_number_of_lost_packets); - EXPECT_EQ(3u, feedback.inter_arrival.received_packet_times.size()); - TimeMap::iterator it = feedback.inter_arrival.received_packet_times.begin(); - EXPECT_EQ(1u, it->first); - EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), it->second.Subtract(start)); - it = feedback.inter_arrival.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/chromium/net/quic/congestion_control/inter_arrival_sender.cc b/chromium/net/quic/congestion_control/inter_arrival_sender.cc deleted file mode 100644 index 632a41612be..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_sender.cc +++ /dev/null @@ -1,529 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/congestion_control/inter_arrival_sender.h" - -namespace net { - -namespace { -const int64 kProbeBitrateKBytesPerSecond = 1200; // 9.6 Mbit/s -const float kPacketLossBitrateReduction = 0.7f; -const float kUncertainSafetyMargin = 0.7f; -const float kMaxBitrateReduction = 0.9f; -const float kMinBitrateReduction = 0.05f; -const uint64 kMinBitrateKbit = 10; -const int kInitialRttMs = 60; // At a typical RTT 60 ms. -const float kAlpha = 0.125f; -const float kOneMinusAlpha = 1 - kAlpha; - -static const int kBitrateSmoothingPeriodMs = 1000; -static const int kMinBitrateSmoothingPeriodMs = 500; - -} // namespace - -InterArrivalSender::InterArrivalSender(const QuicClock* clock) - : probing_(true), - max_segment_size_(kDefaultMaxPacketSize), - current_bandwidth_(QuicBandwidth::Zero()), - smoothed_rtt_(QuicTime::Delta::Zero()), - channel_estimator_(new ChannelEstimator()), - bitrate_ramp_up_(new InterArrivalBitrateRampUp(clock)), - overuse_detector_(new InterArrivalOveruseDetector()), - probe_(new InterArrivalProbe(max_segment_size_)), - state_machine_(new InterArrivalStateMachine(clock)), - paced_sender_(new PacedSender(QuicBandwidth::FromKBytesPerSecond( - kProbeBitrateKBytesPerSecond), max_segment_size_)), - accumulated_number_of_lost_packets_(0), - bandwidth_usage_state_(kBandwidthSteady), - back_down_time_(QuicTime::Zero()), - back_down_bandwidth_(QuicBandwidth::Zero()), - back_down_congestion_delay_(QuicTime::Delta::Zero()) { -} - -InterArrivalSender::~InterArrivalSender() { -} - -void InterArrivalSender::SetFromConfig(const QuicConfig& config, - bool is_server) { -} - -void InterArrivalSender::SetMaxPacketSize(QuicByteCount max_packet_size) { - max_segment_size_ = max_packet_size; - paced_sender_->set_max_segment_size(max_segment_size_); - probe_->set_max_segment_size(max_segment_size_); -} - -// TODO(pwestin): this is really inefficient (4% CPU on the GFE loadtest). -// static -QuicBandwidth InterArrivalSender::CalculateSentBandwidth( - const SendAlgorithmInterface::SentPacketsMap& sent_packets_map, - QuicTime feedback_receive_time) { - const QuicTime::Delta kBitrateSmoothingPeriod = - QuicTime::Delta::FromMilliseconds(kBitrateSmoothingPeriodMs); - const QuicTime::Delta kMinBitrateSmoothingPeriod = - QuicTime::Delta::FromMilliseconds(kMinBitrateSmoothingPeriodMs); - - QuicByteCount sum_bytes_sent = 0; - - // Sum packet from new until they are kBitrateSmoothingPeriod old. - SendAlgorithmInterface::SentPacketsMap::const_reverse_iterator history_rit = - sent_packets_map.rbegin(); - - QuicTime::Delta max_diff = QuicTime::Delta::Zero(); - for (; history_rit != sent_packets_map.rend(); ++history_rit) { - QuicTime::Delta diff = - feedback_receive_time.Subtract(history_rit->second->send_timestamp()); - if (diff > kBitrateSmoothingPeriod) { - break; - } - sum_bytes_sent += history_rit->second->bytes_sent(); - max_diff = diff; - } - if (max_diff < kMinBitrateSmoothingPeriod) { - // No estimate. - return QuicBandwidth::Zero(); - } - return QuicBandwidth::FromBytesAndTimeDelta(sum_bytes_sent, max_diff); -} - -void InterArrivalSender::OnIncomingQuicCongestionFeedbackFrame( - const QuicCongestionFeedbackFrame& feedback, - QuicTime feedback_receive_time, - const SentPacketsMap& sent_packets) { - DCHECK(feedback.type == kInterArrival); - - if (feedback.type != kInterArrival) { - return; - } - - QuicBandwidth sent_bandwidth = CalculateSentBandwidth(sent_packets, - feedback_receive_time); - - TimeMap::const_iterator received_it; - for (received_it = feedback.inter_arrival.received_packet_times.begin(); - received_it != feedback.inter_arrival.received_packet_times.end(); - ++received_it) { - QuicPacketSequenceNumber sequence_number = received_it->first; - - SentPacketsMap::const_iterator sent_it = sent_packets.find(sequence_number); - if (sent_it == sent_packets.end()) { - // Too old data; ignore and move forward. - DVLOG(1) << "Too old feedback move forward, sequence_number:" - << sequence_number; - continue; - } - QuicTime time_received = received_it->second; - QuicTime time_sent = sent_it->second->send_timestamp(); - QuicByteCount bytes_sent = sent_it->second->bytes_sent(); - - channel_estimator_->OnAcknowledgedPacket( - sequence_number, bytes_sent, time_sent, time_received); - if (probing_) { - probe_->OnIncomingFeedback( - sequence_number, bytes_sent, time_sent, time_received); - } else { - bool last_of_send_time = false; - SentPacketsMap::const_iterator next_sent_it = ++sent_it; - if (next_sent_it == sent_packets.end()) { - // No more sent packets; hence this must be the last. - last_of_send_time = true; - } else { - if (time_sent != next_sent_it->second->send_timestamp()) { - // Next sent packet have a different send time. - last_of_send_time = true; - } - } - overuse_detector_->OnAcknowledgedPacket( - sequence_number, time_sent, last_of_send_time, time_received); - } - } - if (probing_) { - probing_ = ProbingPhase(feedback_receive_time); - return; - } - - bool packet_loss_event = false; - if (accumulated_number_of_lost_packets_ != - feedback.inter_arrival.accumulated_number_of_lost_packets) { - accumulated_number_of_lost_packets_ = - feedback.inter_arrival.accumulated_number_of_lost_packets; - packet_loss_event = true; - } - InterArrivalState state = state_machine_->GetInterArrivalState(); - - if (state == kInterArrivalStatePacketLoss || - state == kInterArrivalStateCompetingTcpFLow) { - if (packet_loss_event) { - if (!state_machine_->PacketLossEvent()) { - // Less than one RTT since last PacketLossEvent. - return; - } - EstimateBandwidthAfterLossEvent(feedback_receive_time); - } else { - EstimateNewBandwidth(feedback_receive_time, sent_bandwidth); - } - return; - } - EstimateDelayBandwidth(feedback_receive_time, sent_bandwidth); -} - -bool InterArrivalSender::ProbingPhase(QuicTime feedback_receive_time) { - QuicBandwidth available_channel_estimate = QuicBandwidth::Zero(); - if (!probe_->GetEstimate(&available_channel_estimate)) { - // Continue probing phase. - return true; - } - QuicBandwidth channel_estimate = QuicBandwidth::Zero(); - ChannelEstimateState channel_estimator_state = - channel_estimator_->GetChannelEstimate(&channel_estimate); - - QuicBandwidth new_rate = - available_channel_estimate.Scale(kUncertainSafetyMargin); - - switch (channel_estimator_state) { - case kChannelEstimateUnknown: - channel_estimate = available_channel_estimate; - break; - case kChannelEstimateUncertain: - channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin); - break; - case kChannelEstimateGood: - // Do nothing. - break; - } - new_rate = std::max(new_rate, - QuicBandwidth::FromKBitsPerSecond(kMinBitrateKbit)); - - bitrate_ramp_up_->Reset(new_rate, available_channel_estimate, - channel_estimate); - - current_bandwidth_ = new_rate; - paced_sender_->UpdateBandwidthEstimate(feedback_receive_time, new_rate); - DVLOG(1) << "Probe result; new rate:" - << new_rate.ToKBitsPerSecond() << " Kbits/s " - << " available estimate:" - << available_channel_estimate.ToKBitsPerSecond() << " Kbits/s " - << " channel estimate:" - << channel_estimate.ToKBitsPerSecond() << " Kbits/s "; - return false; -} - -void InterArrivalSender::OnPacketAcked( - QuicPacketSequenceNumber /*acked_sequence_number*/, - QuicByteCount acked_bytes, - QuicTime::Delta rtt) { - // RTT can't be negative. - DCHECK_LE(0, rtt.ToMicroseconds()); - - if (probing_) { - probe_->OnAcknowledgedPacket(acked_bytes); - } - - if (rtt.IsInfinite()) { - return; - } - - if (smoothed_rtt_.IsZero()) { - smoothed_rtt_ = rtt; - } else { - smoothed_rtt_ = QuicTime::Delta::FromMicroseconds( - kOneMinusAlpha * smoothed_rtt_.ToMicroseconds() + - kAlpha * rtt.ToMicroseconds()); - } - state_machine_->set_rtt(smoothed_rtt_); -} - -void InterArrivalSender::OnPacketLost( - QuicPacketSequenceNumber /*sequence_number*/, - QuicTime ack_receive_time) { - // Packet loss was reported. - if (!probing_) { - if (!state_machine_->PacketLossEvent()) { - // Less than one RTT since last PacketLossEvent. - return; - } - // Calculate new pace rate. - EstimateBandwidthAfterLossEvent(ack_receive_time); - } -} - -bool InterArrivalSender::OnPacketSent( - QuicTime sent_time, - QuicPacketSequenceNumber sequence_number, - QuicByteCount bytes, - TransmissionType /*transmission_type*/, - HasRetransmittableData /*has_retransmittable_data*/) { - if (probing_) { - probe_->OnPacketSent(bytes); - } - paced_sender_->OnPacketSent(sent_time, bytes); - return true; -} - -void InterArrivalSender::OnRetransmissionTimeout() { - // TODO(ianswett): Decrease the available bandwidth. -} - -void InterArrivalSender::OnPacketAbandoned( - QuicPacketSequenceNumber /*sequence_number*/, - QuicByteCount abandoned_bytes) { - // TODO(pwestin): use for out outer_congestion_window_ logic. - if (probing_) { - probe_->OnAcknowledgedPacket(abandoned_bytes); - } -} - -QuicTime::Delta InterArrivalSender::TimeUntilSend( - QuicTime now, - TransmissionType /*transmission_type*/, - HasRetransmittableData has_retransmittable_data, - IsHandshake /*handshake*/) { - // TODO(pwestin): implement outer_congestion_window_ logic. - QuicTime::Delta outer_window = QuicTime::Delta::Zero(); - - if (probing_) { - if (has_retransmittable_data == HAS_RETRANSMITTABLE_DATA && - probe_->GetAvailableCongestionWindow() == 0) { - outer_window = QuicTime::Delta::Infinite(); - } - } - return paced_sender_->TimeUntilSend(now, outer_window); -} - -void InterArrivalSender::EstimateDelayBandwidth(QuicTime feedback_receive_time, - QuicBandwidth sent_bandwidth) { - QuicTime::Delta estimated_congestion_delay = QuicTime::Delta::Zero(); - BandwidthUsage new_bandwidth_usage_state = - overuse_detector_->GetState(&estimated_congestion_delay); - - switch (new_bandwidth_usage_state) { - case kBandwidthDraining: - case kBandwidthUnderUsing: - // Hold our current bitrate. - break; - case kBandwidthOverUsing: - if (!state_machine_->IncreasingDelayEvent()) { - // Less than one RTT since last IncreasingDelayEvent. - return; - } - EstimateBandwidthAfterDelayEvent(feedback_receive_time, - estimated_congestion_delay); - break; - case kBandwidthSteady: - // Calculate new pace rate. - if (bandwidth_usage_state_ == kBandwidthDraining || - bandwidth_usage_state_ == kBandwidthOverUsing) { - EstimateNewBandwidthAfterDraining(feedback_receive_time, - estimated_congestion_delay); - } else { - EstimateNewBandwidth(feedback_receive_time, sent_bandwidth); - } - break; - } - bandwidth_usage_state_ = new_bandwidth_usage_state; -} - -QuicBandwidth InterArrivalSender::BandwidthEstimate() const { - return current_bandwidth_; -} - -QuicTime::Delta InterArrivalSender::SmoothedRtt() const { - if (smoothed_rtt_.IsZero()) { - return QuicTime::Delta::FromMilliseconds(kInitialRttMs); - } - return smoothed_rtt_; -} - -QuicTime::Delta InterArrivalSender::RetransmissionDelay() const { - // TODO(pwestin): Calculate and return retransmission delay. - // Use 2 * the smoothed RTT for now. - return smoothed_rtt_.Add(smoothed_rtt_); -} - -QuicByteCount InterArrivalSender::GetCongestionWindow() const { - return 0; -} - -void InterArrivalSender::EstimateNewBandwidth(QuicTime feedback_receive_time, - QuicBandwidth sent_bandwidth) { - QuicBandwidth new_bandwidth = bitrate_ramp_up_->GetNewBitrate(sent_bandwidth); - if (current_bandwidth_ == new_bandwidth) { - return; - } - current_bandwidth_ = new_bandwidth; - state_machine_->IncreaseBitrateDecision(); - - QuicBandwidth channel_estimate = QuicBandwidth::Zero(); - ChannelEstimateState channel_estimator_state = - channel_estimator_->GetChannelEstimate(&channel_estimate); - - if (channel_estimator_state == kChannelEstimateGood) { - bitrate_ramp_up_->UpdateChannelEstimate(channel_estimate); - } - paced_sender_->UpdateBandwidthEstimate(feedback_receive_time, - current_bandwidth_); - DVLOG(1) << "New bandwidth estimate in steady state:" - << current_bandwidth_.ToKBitsPerSecond() - << " Kbits/s"; -} - -// Did we drain the network buffers in our expected pace? -void InterArrivalSender::EstimateNewBandwidthAfterDraining( - QuicTime feedback_receive_time, - QuicTime::Delta estimated_congestion_delay) { - if (current_bandwidth_ > back_down_bandwidth_) { - // Do nothing, our current bandwidth is higher than our bandwidth at the - // previous back down. - DVLOG(1) << "Current bandwidth estimate is higher than before draining"; - return; - } - if (estimated_congestion_delay >= back_down_congestion_delay_) { - // Do nothing, our estimated delay have increased. - DVLOG(1) << "Current delay estimate is higher than before draining"; - return; - } - DCHECK(back_down_time_.IsInitialized()); - QuicTime::Delta buffer_reduction = - back_down_congestion_delay_.Subtract(estimated_congestion_delay); - QuicTime::Delta elapsed_time = - feedback_receive_time.Subtract(back_down_time_).Subtract(SmoothedRtt()); - - QuicBandwidth new_estimate = QuicBandwidth::Zero(); - if (buffer_reduction >= elapsed_time) { - // We have drained more than the elapsed time... go back to our old rate. - new_estimate = back_down_bandwidth_; - } else { - float fraction_of_rate = - static_cast<float>(buffer_reduction.ToMicroseconds()) / - elapsed_time.ToMicroseconds(); // < 1.0 - - QuicBandwidth draining_rate = back_down_bandwidth_.Scale(fraction_of_rate); - QuicBandwidth max_estimated_draining_rate = - back_down_bandwidth_.Subtract(current_bandwidth_); - if (draining_rate > max_estimated_draining_rate) { - // We drained faster than our old send rate, go back to our old rate. - new_estimate = back_down_bandwidth_; - } else { - // Use our drain rate and our kMinBitrateReduction to go to our - // new estimate. - new_estimate = std::max(current_bandwidth_, - current_bandwidth_.Add(draining_rate).Scale( - 1.0f - kMinBitrateReduction)); - DVLOG(1) << "Draining calculation; current rate:" - << current_bandwidth_.ToKBitsPerSecond() << " Kbits/s " - << "draining rate:" - << draining_rate.ToKBitsPerSecond() << " Kbits/s " - << "new estimate:" - << new_estimate.ToKBitsPerSecond() << " Kbits/s " - << " buffer reduction:" - << buffer_reduction.ToMicroseconds() << " us " - << " elapsed time:" - << elapsed_time.ToMicroseconds() << " us "; - } - } - if (new_estimate == current_bandwidth_) { - return; - } - - QuicBandwidth channel_estimate = QuicBandwidth::Zero(); - ChannelEstimateState channel_estimator_state = - channel_estimator_->GetChannelEstimate(&channel_estimate); - - // TODO(pwestin): we need to analyze channel_estimate too. - switch (channel_estimator_state) { - case kChannelEstimateUnknown: - channel_estimate = current_bandwidth_; - break; - case kChannelEstimateUncertain: - channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin); - break; - case kChannelEstimateGood: - // Do nothing, estimate is accurate. - break; - } - bitrate_ramp_up_->Reset(new_estimate, back_down_bandwidth_, channel_estimate); - state_machine_->IncreaseBitrateDecision(); - paced_sender_->UpdateBandwidthEstimate(feedback_receive_time, new_estimate); - current_bandwidth_ = new_estimate; - DVLOG(1) << "New bandwidth estimate after draining:" - << new_estimate.ToKBitsPerSecond() << " Kbits/s"; -} - -void InterArrivalSender::EstimateBandwidthAfterDelayEvent( - QuicTime feedback_receive_time, - QuicTime::Delta estimated_congestion_delay) { - QuicByteCount estimated_byte_buildup = - current_bandwidth_.ToBytesPerPeriod(estimated_congestion_delay); - - // To drain all build up buffer within one RTT we need to reduce the - // bitrate with the following. - // TODO(pwestin): this is a crude first implementation. - int64 draining_rate_per_rtt = (estimated_byte_buildup * - kNumMicrosPerSecond) / SmoothedRtt().ToMicroseconds(); - - float decrease_factor = - draining_rate_per_rtt / current_bandwidth_.ToBytesPerSecond(); - - decrease_factor = std::max(decrease_factor, kMinBitrateReduction); - decrease_factor = std::min(decrease_factor, kMaxBitrateReduction); - back_down_congestion_delay_ = estimated_congestion_delay; - QuicBandwidth new_target_bitrate = - current_bandwidth_.Scale(1.0f - decrease_factor); - - // While in delay sensing mode send at least one packet per RTT. - QuicBandwidth min_delay_bitrate = - QuicBandwidth::FromBytesAndTimeDelta(max_segment_size_, SmoothedRtt()); - new_target_bitrate = std::max(new_target_bitrate, min_delay_bitrate); - - ResetCurrentBandwidth(feedback_receive_time, new_target_bitrate); - - DVLOG(1) << "New bandwidth estimate after delay event:" - << current_bandwidth_.ToKBitsPerSecond() - << " Kbits/s min delay bitrate:" - << min_delay_bitrate.ToKBitsPerSecond() - << " Kbits/s RTT:" - << SmoothedRtt().ToMicroseconds() - << " us"; -} - -void InterArrivalSender::EstimateBandwidthAfterLossEvent( - QuicTime feedback_receive_time) { - ResetCurrentBandwidth(feedback_receive_time, - current_bandwidth_.Scale(kPacketLossBitrateReduction)); - DVLOG(1) << "New bandwidth estimate after loss event:" - << current_bandwidth_.ToKBitsPerSecond() - << " Kbits/s"; -} - -void InterArrivalSender::ResetCurrentBandwidth(QuicTime feedback_receive_time, - QuicBandwidth new_rate) { - new_rate = std::max(new_rate, - QuicBandwidth::FromKBitsPerSecond(kMinBitrateKbit)); - QuicBandwidth channel_estimate = QuicBandwidth::Zero(); - ChannelEstimateState channel_estimator_state = - channel_estimator_->GetChannelEstimate(&channel_estimate); - - switch (channel_estimator_state) { - case kChannelEstimateUnknown: - channel_estimate = current_bandwidth_; - break; - case kChannelEstimateUncertain: - channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin); - break; - case kChannelEstimateGood: - // Do nothing. - break; - } - back_down_time_ = feedback_receive_time; - back_down_bandwidth_ = current_bandwidth_; - bitrate_ramp_up_->Reset(new_rate, current_bandwidth_, channel_estimate); - if (new_rate != current_bandwidth_) { - current_bandwidth_ = new_rate; - paced_sender_->UpdateBandwidthEstimate(feedback_receive_time, - current_bandwidth_); - state_machine_->DecreaseBitrateDecision(); - } -} - -} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_sender.h b/chromium/net/quic/congestion_control/inter_arrival_sender.h deleted file mode 100644 index fabc7b65e34..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_sender.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_SENDER_H_ -#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_SENDER_H_ - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "net/base/net_export.h" -#include "net/quic/congestion_control/channel_estimator.h" -#include "net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h" -#include "net/quic/congestion_control/inter_arrival_overuse_detector.h" -#include "net/quic/congestion_control/inter_arrival_probe.h" -#include "net/quic/congestion_control/inter_arrival_state_machine.h" -#include "net/quic/congestion_control/paced_sender.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 { - -class NET_EXPORT_PRIVATE InterArrivalSender : public SendAlgorithmInterface { - public: - explicit InterArrivalSender(const QuicClock* clock); - virtual ~InterArrivalSender(); - - static QuicBandwidth CalculateSentBandwidth( - const SendAlgorithmInterface::SentPacketsMap& sent_packets_map, - QuicTime feedback_receive_time); - - // Start implementation of SendAlgorithmInterface. - virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE; - virtual void SetMaxPacketSize(QuicByteCount max_packet_size) OVERRIDE; - virtual void OnIncomingQuicCongestionFeedbackFrame( - const QuicCongestionFeedbackFrame& feedback, - QuicTime feedback_receive_time, - const SentPacketsMap& sent_packets) OVERRIDE; - virtual void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number, - QuicByteCount acked_bytes, - QuicTime::Delta rtt) OVERRIDE; - virtual void OnPacketLost(QuicPacketSequenceNumber sequence_number, - QuicTime ack_receive_time) OVERRIDE; - virtual bool OnPacketSent( - QuicTime sent_time, - QuicPacketSequenceNumber sequence_number, - QuicByteCount bytes, - TransmissionType transmission_type, - HasRetransmittableData has_retransmittable_data) OVERRIDE; - virtual void OnRetransmissionTimeout() OVERRIDE; - virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number, - QuicByteCount abandoned_bytes) OVERRIDE; - virtual QuicTime::Delta TimeUntilSend( - QuicTime now, - TransmissionType transmission_type, - HasRetransmittableData has_retransmittable_data, - IsHandshake handshake) OVERRIDE; - virtual QuicBandwidth BandwidthEstimate() const OVERRIDE; - virtual QuicTime::Delta SmoothedRtt() const OVERRIDE; - virtual QuicTime::Delta RetransmissionDelay() const OVERRIDE; - virtual QuicByteCount GetCongestionWindow() const OVERRIDE; - // End implementation of SendAlgorithmInterface. - - private: - void EstimateDelayBandwidth(QuicTime feedback_receive_time, - QuicBandwidth sent_bandwidth); - void EstimateNewBandwidth(QuicTime feedback_receive_time, - QuicBandwidth sent_bandwidth); - void EstimateNewBandwidthAfterDraining( - QuicTime feedback_receive_time, - QuicTime::Delta estimated_congestion_delay); - void EstimateBandwidthAfterLossEvent(QuicTime feedback_receive_time); - void EstimateBandwidthAfterDelayEvent( - QuicTime feedback_receive_time, - QuicTime::Delta estimated_congestion_delay); - void ResetCurrentBandwidth(QuicTime feedback_receive_time, - QuicBandwidth new_rate); - bool ProbingPhase(QuicTime feedback_receive_time); - - bool probing_; // Are we currently in the probing phase? - QuicByteCount max_segment_size_; - QuicBandwidth current_bandwidth_; - QuicTime::Delta smoothed_rtt_; - scoped_ptr<ChannelEstimator> channel_estimator_; - scoped_ptr<InterArrivalBitrateRampUp> bitrate_ramp_up_; - scoped_ptr<InterArrivalOveruseDetector> overuse_detector_; - scoped_ptr<InterArrivalProbe> probe_; - scoped_ptr<InterArrivalStateMachine> state_machine_; - scoped_ptr<PacedSender> paced_sender_; - int accumulated_number_of_lost_packets_; - BandwidthUsage bandwidth_usage_state_; - QuicTime back_down_time_; // Time when we decided to back down. - QuicBandwidth back_down_bandwidth_; // Bandwidth before backing down. - QuicTime::Delta back_down_congestion_delay_; // Delay when backing down. - - DISALLOW_COPY_AND_ASSIGN(InterArrivalSender); -}; - -} // namespace net -#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_SENDER_H_ diff --git a/chromium/net/quic/congestion_control/inter_arrival_sender_test.cc b/chromium/net/quic/congestion_control/inter_arrival_sender_test.cc deleted file mode 100644 index fe8dd3cf29f..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_sender_test.cc +++ /dev/null @@ -1,569 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/congestion_control/inter_arrival_sender.h" - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/stl_util.h" -#include "net/quic/test_tools/mock_clock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using std::pair; - -namespace net { -namespace test { - -class InterArrivalSenderTest : public ::testing::Test { - protected: - InterArrivalSenderTest() - : rtt_(QuicTime::Delta::FromMilliseconds(60)), - one_ms_(QuicTime::Delta::FromMilliseconds(1)), - one_s_(QuicTime::Delta::FromMilliseconds(1000)), - nine_ms_(QuicTime::Delta::FromMilliseconds(9)), - send_start_time_(send_clock_.Now()), - sender_(&send_clock_), - sequence_number_(1), - acked_sequence_number_(1), - feedback_sequence_number_(1) { - send_clock_.AdvanceTime(one_ms_); - receive_clock_.AdvanceTime(one_ms_); - } - - virtual ~InterArrivalSenderTest() { - STLDeleteValues(&sent_packets_); - } - - void SendAvailableCongestionWindow() { - while (sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()) { - QuicByteCount bytes_in_packet = kDefaultMaxPacketSize; - sent_packets_[sequence_number_] = - new class SendAlgorithmInterface::SentPacket( - bytes_in_packet, send_clock_.Now(), HAS_RETRANSMITTABLE_DATA); - - sender_.OnPacketSent(send_clock_.Now(), sequence_number_, bytes_in_packet, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); - sequence_number_++; - } - EXPECT_FALSE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - } - - void AckNPackets(int n) { - for (int i = 0; i < n; ++i) { - sender_.OnPacketAcked( - acked_sequence_number_++, kDefaultMaxPacketSize, rtt_); - } - } - - void SendDelaySpikeFeedbackMessage(QuicTime::Delta spike_time) { - QuicCongestionFeedbackFrame feedback; - feedback.type = kInterArrival; - feedback.inter_arrival.accumulated_number_of_lost_packets = 0; - receive_clock_.AdvanceTime(spike_time); - QuicTime receive_time = receive_clock_.ApproximateNow(); - feedback.inter_arrival.received_packet_times.insert( - pair<QuicPacketSequenceNumber, QuicTime>( - feedback_sequence_number_, receive_time)); - feedback_sequence_number_++; - - // We need to send feedback for 2 packets since they where sent at the - // same time. - feedback.inter_arrival.received_packet_times.insert( - pair<QuicPacketSequenceNumber, QuicTime>( - feedback_sequence_number_, receive_time)); - feedback_sequence_number_++; - - sender_.OnIncomingQuicCongestionFeedbackFrame(feedback, send_clock_.Now(), - sent_packets_); - } - - void SendFeedbackMessageNPackets(int n, - QuicTime::Delta delta_odd, - QuicTime::Delta delta_even) { - QuicCongestionFeedbackFrame feedback; - feedback.type = kInterArrival; - feedback.inter_arrival.accumulated_number_of_lost_packets = 0; - for (int i = 0; i < n; ++i) { - if (feedback_sequence_number_ % 2) { - receive_clock_.AdvanceTime(delta_even); - } else { - receive_clock_.AdvanceTime(delta_odd); - } - QuicTime receive_time = receive_clock_.ApproximateNow(); - feedback.inter_arrival.received_packet_times.insert( - pair<QuicPacketSequenceNumber, QuicTime>( - feedback_sequence_number_, receive_time)); - feedback_sequence_number_++; - } - sender_.OnIncomingQuicCongestionFeedbackFrame(feedback, send_clock_.Now(), - sent_packets_); - } - - QuicTime::Delta SenderDeltaSinceStart() { - return send_clock_.ApproximateNow().Subtract(send_start_time_); - } - - const QuicTime::Delta rtt_; - const QuicTime::Delta one_ms_; - const QuicTime::Delta one_s_; - const QuicTime::Delta nine_ms_; - MockClock send_clock_; - MockClock receive_clock_; - const QuicTime send_start_time_; - InterArrivalSender sender_; - QuicPacketSequenceNumber sequence_number_; - QuicPacketSequenceNumber acked_sequence_number_; - QuicPacketSequenceNumber feedback_sequence_number_; - SendAlgorithmInterface::SentPacketsMap sent_packets_; -}; - -TEST_F(InterArrivalSenderTest, ProbeFollowedByFullRampUpCycle) { - QuicCongestionFeedbackFrame feedback; - // At startup make sure we can send. - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - - // Send 5 bursts. - for (int i = 0; i < 4; ++i) { - SendAvailableCongestionWindow(); - send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - } - SendAvailableCongestionWindow(); - - // We have now sent our probe. - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite()); - - AckNPackets(10); - SendFeedbackMessageNPackets(10, one_ms_, nine_ms_); - send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - - // We should now have our probe rate. - QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41); - int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond / - acc_arrival_time.ToMicroseconds(); - EXPECT_NEAR(0.7f * probe_rate, - sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); - DLOG(INFO) << "After probe"; - // Send 50 bursts, make sure that we move fast in the beginning. - for (int i = 0; i < 50; ++i) { - SendAvailableCongestionWindow(); - QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); - send_clock_.AdvanceTime(time_until_send); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - AckNPackets(2); - SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); - } - EXPECT_NEAR(0.875f * probe_rate, - sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); - EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 600, 10); - - // Send 50 bursts, make sure that we slow down towards the probe rate. - for (int i = 0; i < 50; ++i) { - SendAvailableCongestionWindow(); - QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); - send_clock_.AdvanceTime(time_until_send); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - AckNPackets(2); - SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); - } - EXPECT_NEAR(0.95f * probe_rate, - sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); - EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 1100, 10); - - // Send 50 bursts, make sure that we move very slow close to the probe rate. - for (int i = 0; i < 50; ++i) { - SendAvailableCongestionWindow(); - QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); - send_clock_.AdvanceTime(time_until_send); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - AckNPackets(2); - SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); - } - EXPECT_NEAR(0.99f * probe_rate, - sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); - EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 1560, 10); - DLOG(INFO) << "Near available channel estimate"; - - // Send 50 bursts, make sure that we move very slow close to the probe rate. - for (int i = 0; i < 50; ++i) { - SendAvailableCongestionWindow(); - QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); - send_clock_.AdvanceTime(time_until_send); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - AckNPackets(2); - SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); - } - EXPECT_NEAR(1.00f * probe_rate, - sender_.BandwidthEstimate().ToBytesPerSecond(), 2000); - EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2000, 100); - DLOG(INFO) << "At available channel estimate"; - - // Send 50 bursts, make sure that we move very slow close to the probe rate. - for (int i = 0; i < 50; ++i) { - SendAvailableCongestionWindow(); - QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); - send_clock_.AdvanceTime(time_until_send); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - AckNPackets(2); - SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); - } - EXPECT_NEAR(1.01f * probe_rate, - sender_.BandwidthEstimate().ToBytesPerSecond(), 2000); - EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2500, 100); - - // Send 50 bursts, make sure that we accelerate after the probe rate. - for (int i = 0; i < 50; ++i) { - SendAvailableCongestionWindow(); - QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); - send_clock_.AdvanceTime(time_until_send); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - AckNPackets(2); - SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); - } - EXPECT_NEAR(1.01f * probe_rate, - sender_.BandwidthEstimate().ToBytesPerSecond(), 2000); - EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2900, 100); - - // Send 50 bursts, make sure that we accelerate after the probe rate. - for (int i = 0; i < 50; ++i) { - SendAvailableCongestionWindow(); - QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); - send_clock_.AdvanceTime(time_until_send); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - AckNPackets(2); - SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); - } - EXPECT_NEAR(1.03f * probe_rate, - sender_.BandwidthEstimate().ToBytesPerSecond(), 2000); - EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 3400, 100); - - int64 max_rate = kDefaultMaxPacketSize * kNumMicrosPerSecond / - one_ms_.ToMicroseconds(); - - int64 halfway_rate = probe_rate + (max_rate - probe_rate) / 2; - - // Send until we reach halfway point. - for (int i = 0; i < 570; ++i) { - SendAvailableCongestionWindow(); - QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); - send_clock_.AdvanceTime(time_until_send); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - AckNPackets(2); - SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); - } - EXPECT_NEAR(halfway_rate, - sender_.BandwidthEstimate().ToBytesPerSecond(), 5000); - EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 6600, 100); - DLOG(INFO) << "Near halfway point"; - - // Send until we reach max channel capacity. - for (int i = 0; i < 1500; ++i) { - SendAvailableCongestionWindow(); - QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); - send_clock_.AdvanceTime(time_until_send); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - AckNPackets(2); - SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); - } - EXPECT_NEAR(max_rate, - sender_.BandwidthEstimate().ToBytesPerSecond(), 5000); - EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 10000, 200); -} - -TEST_F(InterArrivalSenderTest, DelaySpikeFollowedBySlowDrain) { - QuicCongestionFeedbackFrame feedback; - // At startup make sure we can send. - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - - // Send 5 bursts. - for (int i = 0; i < 4; ++i) { - SendAvailableCongestionWindow(); - send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - } - SendAvailableCongestionWindow(); - - // We have now sent our probe. - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite()); - - AckNPackets(10); - SendFeedbackMessageNPackets(10, one_ms_, nine_ms_); - send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - - // We should now have our probe rate. - QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41); - int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond / - acc_arrival_time.ToMicroseconds(); - EXPECT_NEAR(0.7f * probe_rate, - sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); - - // Send 50 bursts, make sure that we move fast in the beginning. - for (int i = 0; i < 50; ++i) { - SendAvailableCongestionWindow(); - QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); - send_clock_.AdvanceTime(time_until_send); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - AckNPackets(2); - SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); - } - EXPECT_NEAR(0.875f * probe_rate, - sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); - EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 600, 10); - - SendAvailableCongestionWindow(); - send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - AckNPackets(2); - - int64 rate_at_introduced_delay_spike = 0.875f * probe_rate; - QuicTime::Delta spike_time = QuicTime::Delta::FromMilliseconds(100); - SendDelaySpikeFeedbackMessage(spike_time); - - // Backing as much as we can, currently 90%. - EXPECT_NEAR(0.1f * rate_at_introduced_delay_spike, - sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); - EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 610, 10); - - // Run until we are catched up after our introduced delay spike. - while (send_clock_.Now() < receive_clock_.Now()) { - SendAvailableCongestionWindow(); - send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - AckNPackets(2); - SendFeedbackMessageNPackets(2, one_ms_, one_ms_); - } - // Expect that we go back to 67% of the rate before the spike. - EXPECT_NEAR(0.67f * rate_at_introduced_delay_spike, - sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); - EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 820, 10); - - // Send 100 bursts, make sure that we slow down towards the rate we had - // before the spike. - for (int i = 0; i < 100; ++i) { - SendAvailableCongestionWindow(); - QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); - send_clock_.AdvanceTime(time_until_send); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - AckNPackets(2); - SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); - } - EXPECT_NEAR(0.97f * rate_at_introduced_delay_spike, - sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); - EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2100, 10); -} - -TEST_F(InterArrivalSenderTest, DelaySpikeFollowedByImmediateDrain) { - QuicCongestionFeedbackFrame feedback; - // At startup make sure we can send. - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - - // Send 5 bursts. - for (int i = 0; i < 4; ++i) { - SendAvailableCongestionWindow(); - send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - } - SendAvailableCongestionWindow(); - - // We have now sent our probe. - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite()); - - AckNPackets(10); - SendFeedbackMessageNPackets(10, one_ms_, nine_ms_); - send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - - // We should now have our probe rate. - QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41); - int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond / - acc_arrival_time.ToMicroseconds(); - EXPECT_NEAR(0.7f * probe_rate, - sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); - - // Send 50 bursts, make sure that we move fast in the beginning. - for (int i = 0; i < 50; ++i) { - SendAvailableCongestionWindow(); - QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); - send_clock_.AdvanceTime(time_until_send); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - AckNPackets(2); - SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); - } - EXPECT_NEAR(0.875f * probe_rate, - sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); - EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 600, 10); - - SendAvailableCongestionWindow(); - send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - AckNPackets(2); - - int64 rate_at_introduced_delay_spike = 0.875f * probe_rate; - QuicTime::Delta spike_time = QuicTime::Delta::FromMilliseconds(100); - SendDelaySpikeFeedbackMessage(spike_time); - - // Backing as much as we can, currently 90%. - EXPECT_NEAR(0.1f * rate_at_introduced_delay_spike, - sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); - EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 610, 10); - - // Move send time forward. - send_clock_.AdvanceTime(spike_time); - // Make sure our clocks are aligned again. - receive_clock_.AdvanceTime(send_clock_.Now().Subtract(receive_clock_.Now())); - - SendAvailableCongestionWindow(); - AckNPackets(2); - SendFeedbackMessageNPackets(2, one_ms_, one_ms_); - // We should now be back where we introduced the delay spike. - EXPECT_NEAR(rate_at_introduced_delay_spike, - sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); - EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 710, 10); -} - -TEST_F(InterArrivalSenderTest, MinBitrateDueToDelay) { - QuicBandwidth expected_min_bitrate = QuicBandwidth::FromKBitsPerSecond(10); - QuicCongestionFeedbackFrame feedback; - // At startup make sure we can send. - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - - // Send 5 bursts. - for (int i = 0; i < 4; ++i) { - SendAvailableCongestionWindow(); - send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - } - SendAvailableCongestionWindow(); - - AckNPackets(10); - - // One second spread per packet is expected to result in an estimate at - // our minimum bitrate. - SendFeedbackMessageNPackets(10, one_s_, one_s_); - send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - EXPECT_EQ(expected_min_bitrate, sender_.BandwidthEstimate()); -} - -TEST_F(InterArrivalSenderTest, MinBitrateDueToLoss) { - QuicBandwidth expected_min_bitrate = QuicBandwidth::FromKBitsPerSecond(10); - QuicCongestionFeedbackFrame feedback; - // At startup make sure we can send. - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - - // Send 5 bursts. - for (int i = 0; i < 4; ++i) { - SendAvailableCongestionWindow(); - send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - } - SendAvailableCongestionWindow(); - - AckNPackets(10); - SendFeedbackMessageNPackets(10, nine_ms_, nine_ms_); - send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - - QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(81); - int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond / - acc_arrival_time.ToMicroseconds(); - EXPECT_NEAR(0.7f * probe_rate, - sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); - - for (int i = 0; i < 15; ++i) { - SendAvailableCongestionWindow(); - QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); - send_clock_.AdvanceTime(time_until_send); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - sender_.OnPacketLost(acked_sequence_number_ - 1, send_clock_.Now()); - sender_.OnPacketAcked(acked_sequence_number_, kDefaultMaxPacketSize, rtt_); - acked_sequence_number_ += 2; // Create a loss by not acking both packets. - SendFeedbackMessageNPackets(2, nine_ms_, nine_ms_); - } - // Test that our exponentail back off stop at expected_min_bitrate. - EXPECT_EQ(expected_min_bitrate, sender_.BandwidthEstimate()); - - for (int i = 0; i < 50; ++i) { - SendAvailableCongestionWindow(); - QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); - send_clock_.AdvanceTime(time_until_send); - EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - sender_.OnPacketLost(acked_sequence_number_ - 1, send_clock_.Now()); - sender_.OnPacketAcked(acked_sequence_number_, kDefaultMaxPacketSize, rtt_); - acked_sequence_number_ += 2; // Create a loss by not acking both packets. - SendFeedbackMessageNPackets(2, nine_ms_, nine_ms_); - - // Make sure our bitrate is fixed at the expected_min_bitrate. - EXPECT_EQ(expected_min_bitrate, sender_.BandwidthEstimate()); - } -} - -} // namespace test -} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_state_machine.cc b/chromium/net/quic/congestion_control/inter_arrival_state_machine.cc deleted file mode 100644 index 7095e60306b..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_state_machine.cc +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/congestion_control/inter_arrival_state_machine.h" - -#include "base/logging.h" - -namespace { -const int kIncreaseEventsBeforeDowngradingState = 5; -const int kDecreaseEventsBeforeUpgradingState = 2; -// Note: Can not be higher than kDecreaseEventsBeforeUpgradingState; -const int kLossEventsBeforeUpgradingState = 2; -// Timeout old loss and delay events after this time. -const int kEventTimeoutMs = 10000; -// A reasonable arbitrary chosen value for initial round trip time. -const int kInitialRttMs = 80; -} - -namespace net { - -InterArrivalStateMachine::InterArrivalStateMachine(const QuicClock* clock) - : clock_(clock), - current_state_(kInterArrivalStateStable), - smoothed_rtt_(QuicTime::Delta::FromMilliseconds(kInitialRttMs)), - decrease_event_count_(0), - last_decrease_event_(QuicTime::Zero()), - increase_event_count_(0), - last_increase_event_(QuicTime::Zero()), - loss_event_count_(0), - last_loss_event_(QuicTime::Zero()), - delay_event_count_(0), - last_delay_event_(QuicTime::Zero()) { -} - -InterArrivalState InterArrivalStateMachine::GetInterArrivalState() { - return current_state_; -} - -void InterArrivalStateMachine::IncreaseBitrateDecision() { - // Multiple increase event without packet loss or delay events will drive - // state back to stable. - QuicTime current_time = clock_->ApproximateNow(); - if (current_time.Subtract(last_increase_event_) < smoothed_rtt_) { - // Less than one RTT have passed; ignore this event. - return; - } - last_increase_event_ = current_time; - increase_event_count_++; - decrease_event_count_ = 0; // Reset previous decrease events. - - if (increase_event_count_ < kIncreaseEventsBeforeDowngradingState) { - // Not enough increase events to change state. - return; - } - increase_event_count_ = 0; // Reset increase events. - - switch (current_state_) { - case kInterArrivalStateStable: - // Keep this state. - break; - case kInterArrivalStatePacketLoss: - current_state_ = kInterArrivalStateStable; - break; - case kInterArrivalStateDelay: - current_state_ = kInterArrivalStateStable; - break; - case kInterArrivalStateCompetingFlow: - current_state_ = kInterArrivalStateDelay; - break; - case kInterArrivalStateCompetingTcpFLow: - current_state_ = kInterArrivalStateDelay; - break; - } -} - -void InterArrivalStateMachine::DecreaseBitrateDecision() { - DCHECK(kDecreaseEventsBeforeUpgradingState >= - kLossEventsBeforeUpgradingState); - - QuicTime current_time = clock_->ApproximateNow(); - if (current_time.Subtract(last_decrease_event_) < smoothed_rtt_) { - // Less than one RTT have passed; ignore this event. - return; - } - last_decrease_event_ = current_time; - decrease_event_count_++; - increase_event_count_ = 0; // Reset previous increase events. - if (decrease_event_count_ < kDecreaseEventsBeforeUpgradingState) { - // Not enough decrease events to change state. - return; - } - decrease_event_count_ = 0; // Reset decrease events. - - switch (current_state_) { - case kInterArrivalStateStable: - if (delay_event_count_ == 0 && loss_event_count_ > 0) { - // No recent delay events; only packet loss events. - current_state_ = kInterArrivalStatePacketLoss; - } else { - current_state_ = kInterArrivalStateDelay; - } - break; - case kInterArrivalStatePacketLoss: - // Keep this state. - break; - case kInterArrivalStateDelay: - if (loss_event_count_ >= kLossEventsBeforeUpgradingState) { - // We have packet loss events. Assume fighting with TCP. - current_state_ = kInterArrivalStateCompetingTcpFLow; - } else { - current_state_ = kInterArrivalStateCompetingFlow; - } - break; - case kInterArrivalStateCompetingFlow: - if (loss_event_count_ >= kLossEventsBeforeUpgradingState) { - // We have packet loss events. Assume fighting with TCP. - current_state_ = kInterArrivalStateCompetingTcpFLow; - } - break; - case kInterArrivalStateCompetingTcpFLow: - // Keep this state. - break; - } -} - -void InterArrivalStateMachine::set_rtt(QuicTime::Delta rtt) { - smoothed_rtt_ = rtt; -} - -bool InterArrivalStateMachine::PacketLossEvent() { - QuicTime current_time = clock_->ApproximateNow(); - if (current_time.Subtract(last_loss_event_) < smoothed_rtt_) { - // Less than one RTT have passed; ignore this event. - return false; - } - last_loss_event_ = current_time; - loss_event_count_++; - if (current_time.Subtract(last_delay_event_) > - QuicTime::Delta::FromMilliseconds(kEventTimeoutMs)) { - // Delay event have timed out. - delay_event_count_ = 0; - } - return true; -} - -bool InterArrivalStateMachine::IncreasingDelayEvent() { - QuicTime current_time = clock_->ApproximateNow(); - if (current_time.Subtract(last_delay_event_) < smoothed_rtt_) { - // Less than one RTT have passed; ignore this event. - return false; - } - last_delay_event_ = current_time; - delay_event_count_++; - if (current_time.Subtract(last_loss_event_) > - QuicTime::Delta::FromMilliseconds(kEventTimeoutMs)) { - // Loss event have timed out. - loss_event_count_ = 0; - } - return true; -} - -} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_state_machine.h b/chromium/net/quic/congestion_control/inter_arrival_state_machine.h deleted file mode 100644 index 829aa29938a..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_state_machine.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// State machine for congestion control. The state is updated by calls from -// other modules as they detect events, or decide on taking specific actions. -// Events include things like packet loss, or growing delay, while decisions -// include decisions to increase or decrease bitrates. -// This class should be called for every event and decision made by the -// congestion control, this class will coalesce all calls relative to the -// smoothed RTT. - -#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_STATE_MACHINE_H_ -#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_STATE_MACHINE_H_ - -#include "net/base/net_export.h" -#include "net/quic/quic_clock.h" -#include "net/quic/quic_time.h" - -namespace net { - -// State transition diagram. -// -// kInterArrivalStatePacketLoss -// | -// kInterArrivalStateStable -// | -// kInterArrivalStateDelay -// | | -// kInterArrivalStateCompetingFlow -> kInterArrivalStateCompetingTcpFLow - -enum NET_EXPORT_PRIVATE InterArrivalState { - // We are on a network with a delay that is too small to be reliably detected, - // such as a local ethernet. - kInterArrivalStatePacketLoss = 1, - // We are on an underutilized network operating with low delay and low loss. - kInterArrivalStateStable = 2, - // We are on a network where we can detect delay changes and suffer only - // low loss. Nothing indicates that we are competing with another flow. - kInterArrivalStateDelay = 3, - // We are on a network where we can detect delay changes and suffer only - // low loss. We have indications that we are compete with another flow. - kInterArrivalStateCompetingFlow = 4, - // We are on a network where we can detect delay changes, however we suffer - // packet loss due to sharing the bottleneck link with another flow, which is - // most likely a TCP flow. - kInterArrivalStateCompetingTcpFLow = 5, -}; - -class NET_EXPORT_PRIVATE InterArrivalStateMachine { - public: - explicit InterArrivalStateMachine(const QuicClock* clock); - - InterArrivalState GetInterArrivalState(); - - // Inter arrival congestion control decided to increase bitrate. - void IncreaseBitrateDecision(); - - // Inter arrival congestion control decided to decrease bitrate. - void DecreaseBitrateDecision(); - - // Estimated smoothed round trip time. - // This should be called whenever the smoothed RTT estimate is updated. - void set_rtt(QuicTime::Delta rtt); - - // This method is called when a packet loss was reported. - bool PacketLossEvent(); - - // This method is called when we believe that packet transit delay is - // increasing, presumably due to a growing queue along the path. - bool IncreasingDelayEvent(); - - private: - const QuicClock* clock_; - InterArrivalState current_state_; - QuicTime::Delta smoothed_rtt_; - - int decrease_event_count_; - QuicTime last_decrease_event_; - - int increase_event_count_; - QuicTime last_increase_event_; - - int loss_event_count_; - QuicTime last_loss_event_; - - int delay_event_count_; - QuicTime last_delay_event_; - - DISALLOW_COPY_AND_ASSIGN(InterArrivalStateMachine); -}; - -} // namespace net -#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_STATE_MACHINE_H_ diff --git a/chromium/net/quic/congestion_control/inter_arrival_state_machine_test.cc b/chromium/net/quic/congestion_control/inter_arrival_state_machine_test.cc deleted file mode 100644 index 1a4bc8523ea..00000000000 --- a/chromium/net/quic/congestion_control/inter_arrival_state_machine_test.cc +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/basictypes.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "net/quic/congestion_control/inter_arrival_state_machine.h" -#include "net/quic/test_tools/mock_clock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { -namespace test { - -class InterArrivalStateMachineTest : public ::testing::Test { - protected: - InterArrivalStateMachineTest() { - } - - virtual void SetUp() { - state_machine_.reset(new InterArrivalStateMachine(&clock_)); - } - - MockClock clock_; - scoped_ptr<InterArrivalStateMachine> state_machine_; -}; - -TEST_F(InterArrivalStateMachineTest, SimplePacketLoss) { - QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100); - state_machine_->set_rtt(rtt); - state_machine_->IncreaseBitrateDecision(); - - clock_.AdvanceTime(rtt); - state_machine_->PacketLossEvent(); - state_machine_->DecreaseBitrateDecision(); - EXPECT_EQ(kInterArrivalStateStable, - state_machine_->GetInterArrivalState()); - - // Make sure we switch to state packet loss. - clock_.AdvanceTime(rtt); - state_machine_->PacketLossEvent(); - state_machine_->DecreaseBitrateDecision(); - EXPECT_EQ(kInterArrivalStatePacketLoss, - state_machine_->GetInterArrivalState()); - - // Make sure we stay in state packet loss. - clock_.AdvanceTime(rtt); - state_machine_->PacketLossEvent(); - state_machine_->DecreaseBitrateDecision(); - EXPECT_EQ(kInterArrivalStatePacketLoss, - state_machine_->GetInterArrivalState()); - - clock_.AdvanceTime(rtt); - state_machine_->PacketLossEvent(); - state_machine_->DecreaseBitrateDecision(); - EXPECT_EQ(kInterArrivalStatePacketLoss, - state_machine_->GetInterArrivalState()); -} - -TEST_F(InterArrivalStateMachineTest, SimpleDelay) { - QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100); - state_machine_->set_rtt(rtt); - state_machine_->IncreaseBitrateDecision(); - - clock_.AdvanceTime(rtt); - state_machine_->IncreasingDelayEvent(); - state_machine_->DecreaseBitrateDecision(); - EXPECT_EQ(kInterArrivalStateStable, - state_machine_->GetInterArrivalState()); - - // Make sure we switch to state delay. - clock_.AdvanceTime(rtt); - state_machine_->IncreasingDelayEvent(); - state_machine_->DecreaseBitrateDecision(); - EXPECT_EQ(kInterArrivalStateDelay, - state_machine_->GetInterArrivalState()); - - clock_.AdvanceTime(rtt); - state_machine_->IncreasingDelayEvent(); - state_machine_->DecreaseBitrateDecision(); - EXPECT_EQ(kInterArrivalStateDelay, - state_machine_->GetInterArrivalState()); - - // Make sure we switch to state competing flow(s). - clock_.AdvanceTime(rtt); - state_machine_->IncreasingDelayEvent(); - state_machine_->DecreaseBitrateDecision(); - EXPECT_EQ(kInterArrivalStateCompetingFlow, - state_machine_->GetInterArrivalState()); - - // Make sure we stay in state competing flow(s). - clock_.AdvanceTime(rtt); - state_machine_->IncreasingDelayEvent(); - state_machine_->DecreaseBitrateDecision(); - EXPECT_EQ(kInterArrivalStateCompetingFlow, - state_machine_->GetInterArrivalState()); - - clock_.AdvanceTime(rtt); - state_machine_->PacketLossEvent(); - state_machine_->DecreaseBitrateDecision(); - EXPECT_EQ(kInterArrivalStateCompetingFlow, - state_machine_->GetInterArrivalState()); - - clock_.AdvanceTime(rtt); - state_machine_->PacketLossEvent(); - state_machine_->DecreaseBitrateDecision(); - EXPECT_EQ(kInterArrivalStateCompetingFlow, - state_machine_->GetInterArrivalState()); - - // Make sure we switch to state competing TCP flow(s). - clock_.AdvanceTime(rtt); - state_machine_->PacketLossEvent(); - state_machine_->DecreaseBitrateDecision(); - EXPECT_EQ(kInterArrivalStateCompetingTcpFLow, - state_machine_->GetInterArrivalState()); - - // Make sure we stay in state competing TCP flow(s). - clock_.AdvanceTime(rtt); - state_machine_->PacketLossEvent(); - state_machine_->DecreaseBitrateDecision(); - EXPECT_EQ(kInterArrivalStateCompetingTcpFLow, - state_machine_->GetInterArrivalState()); -} - -} // namespace test -} // namespace net diff --git a/chromium/net/quic/congestion_control/leaky_bucket.cc b/chromium/net/quic/congestion_control/leaky_bucket.cc index 06c1f87f3ae..f3972f62eac 100644 --- a/chromium/net/quic/congestion_control/leaky_bucket.cc +++ b/chromium/net/quic/congestion_control/leaky_bucket.cc @@ -24,11 +24,15 @@ void LeakyBucket::Add(QuicTime now, QuicByteCount bytes) { bytes_ += bytes; } -QuicTime::Delta LeakyBucket::TimeRemaining(QuicTime now) { - Update(now); - return QuicTime::Delta::FromMicroseconds( +QuicTime::Delta LeakyBucket::TimeRemaining(QuicTime now) const { + QuicTime::Delta time_since_last_update = now.Subtract(time_last_updated_); + QuicTime::Delta send_delay = QuicTime::Delta::FromMicroseconds( (bytes_ * base::Time::kMicrosecondsPerSecond) / draining_rate_.ToBytesPerSecond()); + if (send_delay < time_since_last_update) { + return QuicTime::Delta::Zero(); + } + return send_delay.Subtract(time_since_last_update); } QuicByteCount LeakyBucket::BytesPending(QuicTime now) { diff --git a/chromium/net/quic/congestion_control/leaky_bucket.h b/chromium/net/quic/congestion_control/leaky_bucket.h index 63ef8102a39..eb4cdb0c636 100644 --- a/chromium/net/quic/congestion_control/leaky_bucket.h +++ b/chromium/net/quic/congestion_control/leaky_bucket.h @@ -13,7 +13,6 @@ #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" @@ -30,7 +29,7 @@ class NET_EXPORT_PRIVATE LeakyBucket { void Add(QuicTime now, QuicByteCount bytes); // Time until the buffer is empty. - QuicTime::Delta TimeRemaining(QuicTime now); + QuicTime::Delta TimeRemaining(QuicTime now) const; // Number of bytes in the buffer. QuicByteCount BytesPending(QuicTime now); diff --git a/chromium/net/quic/congestion_control/loss_detection_interface.cc b/chromium/net/quic/congestion_control/loss_detection_interface.cc new file mode 100644 index 00000000000..035848c9c01 --- /dev/null +++ b/chromium/net/quic/congestion_control/loss_detection_interface.cc @@ -0,0 +1,25 @@ +// 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/loss_detection_interface.h" + +#include "net/quic/congestion_control/tcp_loss_algorithm.h" +#include "net/quic/congestion_control/time_loss_algorithm.h" + +namespace net { + +// Factory for loss detection algorithm. +LossDetectionInterface* LossDetectionInterface::Create( + LossDetectionType loss_type) { + switch (loss_type) { + case kNack: + return new TCPLossAlgorithm(); + case kTime: + return new TimeLossAlgorithm(); + } + LOG(DFATAL) << "Unknown loss detection algorithm:" << loss_type; + return NULL; +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/loss_detection_interface.h b/chromium/net/quic/congestion_control/loss_detection_interface.h new file mode 100644 index 00000000000..5aaa51d46f6 --- /dev/null +++ b/chromium/net/quic/congestion_control/loss_detection_interface.h @@ -0,0 +1,42 @@ +// 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. +// +// The pure virtual class for send side loss detection algorithm. + +#ifndef NET_QUIC_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_ +#define NET_QUIC_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_ + +#include "base/basictypes.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_time.h" + +namespace net { + +class QuicUnackedPacketMap; +class RttStats; + +class NET_EXPORT_PRIVATE LossDetectionInterface { + public: + // Creates a TCP loss detector. + static LossDetectionInterface* Create(LossDetectionType loss_type); + + virtual ~LossDetectionInterface() {} + + virtual LossDetectionType GetLossDetectionType() const = 0; + + // Called when a new ack arrives or the loss alarm fires. + virtual SequenceNumberSet DetectLostPackets( + const QuicUnackedPacketMap& unacked_packets, + const QuicTime& time, + QuicPacketSequenceNumber largest_observed, + const RttStats& rtt_stats) = 0; + + // Get the time the LossDetectionAlgorithm wants to re-evaluate losses. + // Returns QuicTime::Zero if no alarm needs to be set. + virtual QuicTime GetLossTimeout() const = 0; +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_ diff --git a/chromium/net/quic/congestion_control/paced_sender.cc b/chromium/net/quic/congestion_control/paced_sender.cc deleted file mode 100644 index 9a5cc448962..00000000000 --- a/chromium/net/quic/congestion_control/paced_sender.cc +++ /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. - -#include "net/quic/congestion_control/paced_sender.h" - -#include <algorithm> - -#include "net/quic/quic_protocol.h" - -namespace net { - -// To prevent too aggressive pacing we allow the following packet burst size. -const int64 kMinPacketBurstSize = 2; -// Max estimated time between calls to TimeUntilSend and -// AvailableCongestionWindow. -const int64 kMaxSchedulingDelayUs = 2000; - -PacedSender::PacedSender(QuicBandwidth estimate, QuicByteCount max_segment_size) - : leaky_bucket_(estimate), - pace_(estimate), - max_segment_size_(kDefaultMaxPacketSize) { -} - -void PacedSender::set_max_segment_size(QuicByteCount max_segment_size) { - max_segment_size_ = max_segment_size; -} - -void PacedSender::UpdateBandwidthEstimate(QuicTime now, - QuicBandwidth estimate) { - leaky_bucket_.SetDrainingRate(now, estimate); - pace_ = estimate; -} - -void PacedSender::OnPacketSent(QuicTime now, QuicByteCount bytes) { - leaky_bucket_.Add(now, bytes); -} - -QuicTime::Delta PacedSender::TimeUntilSend(QuicTime now, - QuicTime::Delta time_until_send) { - if (time_until_send.ToMicroseconds() >= kMaxSchedulingDelayUs) { - return time_until_send; - } - // Pace the data. - QuicByteCount pacing_window = pace_.ToBytesPerPeriod( - QuicTime::Delta::FromMicroseconds(kMaxSchedulingDelayUs)); - QuicByteCount min_window_size = kMinPacketBurstSize * max_segment_size_; - pacing_window = std::max(pacing_window, min_window_size); - - if (pacing_window > leaky_bucket_.BytesPending(now)) { - // We have not filled our pacing window yet. - return time_until_send; - } - return leaky_bucket_.TimeRemaining(now); -} - -} // namespace net diff --git a/chromium/net/quic/congestion_control/paced_sender.h b/chromium/net/quic/congestion_control/paced_sender.h deleted file mode 100644 index 6d7c42919d0..00000000000 --- a/chromium/net/quic/congestion_control/paced_sender.h +++ /dev/null @@ -1,44 +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. -// -// Helper class that limits the congestion window to pace the packets. - -#ifndef NET_QUIC_CONGESTION_CONTROL_PACED_SENDER_H_ -#define NET_QUIC_CONGESTION_CONTROL_PACED_SENDER_H_ - -#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_time.h" - -namespace net { - -class NET_EXPORT_PRIVATE PacedSender { - public: - PacedSender(QuicBandwidth bandwidth_estimate, QuicByteCount max_segment_size); - - void set_max_segment_size(QuicByteCount max_segment_size); - - // The estimated bandidth from the congestion algorithm changed. - void UpdateBandwidthEstimate(QuicTime now, QuicBandwidth bandwidth_estimate); - - // A packet of size bytes was sent. - void OnPacketSent(QuicTime now, QuicByteCount bytes); - - // Return time until we can send based on the pacing. - QuicTime::Delta TimeUntilSend(QuicTime now, QuicTime::Delta time_until_send); - - private: - // Helper object to track the rate data can leave the buffer for pacing. - LeakyBucket leaky_bucket_; - QuicBandwidth pace_; - QuicByteCount max_segment_size_; - - DISALLOW_COPY_AND_ASSIGN(PacedSender); -}; - -} // namespace net - -#endif // NET_QUIC_CONGESTION_CONTROL_PACED_SENDER_H_ diff --git a/chromium/net/quic/congestion_control/paced_sender_test.cc b/chromium/net/quic/congestion_control/paced_sender_test.cc deleted file mode 100644 index fa42c2ce579..00000000000 --- a/chromium/net/quic/congestion_control/paced_sender_test.cc +++ /dev/null @@ -1,78 +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/paced_sender.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/test_tools/mock_clock.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { -namespace test { - -const int kHundredKBytesPerS = 100; - -class PacedSenderTest : public ::testing::Test { - protected: - PacedSenderTest() - : zero_time_(QuicTime::Delta::Zero()), - paced_sender_(new PacedSender( - QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS), - kDefaultMaxPacketSize)) { - } - - const QuicTime::Delta zero_time_; - MockClock clock_; - scoped_ptr<PacedSender> paced_sender_; -}; - -TEST_F(PacedSenderTest, Basic) { - paced_sender_->UpdateBandwidthEstimate(clock_.Now(), - QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS * 10)); - EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); - paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); - EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); - paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); - EXPECT_EQ(static_cast<int64>(kDefaultMaxPacketSize * 2), - paced_sender_->TimeUntilSend( - clock_.Now(), zero_time_).ToMicroseconds()); - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(24)); - EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); -} - -TEST_F(PacedSenderTest, LowRate) { - paced_sender_->UpdateBandwidthEstimate(clock_.Now(), - QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS)); - EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); - paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); - EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); - paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); - EXPECT_EQ(static_cast<int64>(kDefaultMaxPacketSize * 20), - paced_sender_->TimeUntilSend( - clock_.Now(), zero_time_).ToMicroseconds()); - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(24)); - EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); -} - -TEST_F(PacedSenderTest, HighRate) { - QuicBandwidth bandwidth_estimate = QuicBandwidth::FromKBytesPerSecond( - kHundredKBytesPerS * 100); - paced_sender_->UpdateBandwidthEstimate(clock_.Now(), bandwidth_estimate); - EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); - for (int i = 0; i < 16; ++i) { - paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); - EXPECT_TRUE(paced_sender_->TimeUntilSend( - clock_.Now(), zero_time_).IsZero()); - } - paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); - EXPECT_EQ(2040, paced_sender_->TimeUntilSend( - clock_.Now(), zero_time_).ToMicroseconds()); - clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(20400)); - EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); -} - -} // namespace test -} // namespace net diff --git a/chromium/net/quic/congestion_control/pacing_sender.cc b/chromium/net/quic/congestion_control/pacing_sender.cc index a20e7522750..97138f767d0 100644 --- a/chromium/net/quic/congestion_control/pacing_sender.cc +++ b/chromium/net/quic/congestion_control/pacing_sender.cc @@ -10,50 +10,44 @@ PacingSender::PacingSender(SendAlgorithmInterface* sender, QuicTime::Delta alarm_granularity) : sender_(sender), alarm_granularity_(alarm_granularity), + last_delayed_packet_sent_time_(QuicTime::Zero()), next_packet_send_time_(QuicTime::Zero()), was_last_send_delayed_(false), - max_segment_size_(kDefaultMaxPacketSize) { + has_valid_rtt_(false) { } PacingSender::~PacingSender() {} -void PacingSender::SetMaxPacketSize(QuicByteCount max_packet_size) { - max_segment_size_ = max_packet_size; - sender_->SetMaxPacketSize(max_packet_size); -} - void PacingSender::SetFromConfig(const QuicConfig& config, bool is_server) { sender_->SetFromConfig(config, is_server); } void PacingSender::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, - QuicTime feedback_receive_time, - const SendAlgorithmInterface::SentPacketsMap& sent_packets) { + QuicTime feedback_receive_time) { sender_->OnIncomingQuicCongestionFeedbackFrame( - feedback, feedback_receive_time, sent_packets); -} - -void PacingSender::OnPacketAcked( - QuicPacketSequenceNumber acked_sequence_number, - QuicByteCount acked_bytes, - QuicTime::Delta rtt) { - sender_->OnPacketAcked(acked_sequence_number, acked_bytes, rtt); + feedback, feedback_receive_time); } -void PacingSender::OnPacketLost(QuicPacketSequenceNumber sequence_number, - QuicTime ack_receive_time) { - sender_->OnPacketLost(sequence_number, ack_receive_time); +void PacingSender::OnCongestionEvent(bool rtt_updated, + QuicByteCount bytes_in_flight, + const CongestionMap& acked_packets, + const CongestionMap& lost_packets) { + if (rtt_updated) { + has_valid_rtt_ = true; + } + sender_->OnCongestionEvent( + rtt_updated, bytes_in_flight, acked_packets, lost_packets); } bool PacingSender::OnPacketSent( QuicTime sent_time, + QuicByteCount bytes_in_flight, QuicPacketSequenceNumber sequence_number, QuicByteCount bytes, - TransmissionType transmission_type, HasRetransmittableData has_retransmittable_data) { - // Only pace data packets. - if (has_retransmittable_data == HAS_RETRANSMITTABLE_DATA) { + // Only pace data packets once we have an updated RTT. + if (has_retransmittable_data == HAS_RETRANSMITTABLE_DATA && has_valid_rtt_) { // The next packet should be sent as soon as the current packets has // been transferred. We pace at twice the rate of the underlying // sender's bandwidth estimate to help ensure that pacing doesn't become @@ -61,29 +55,50 @@ bool PacingSender::OnPacketSent( const float kPacingAggression = 2; QuicTime::Delta delay = BandwidthEstimate().Scale(kPacingAggression).TransferTime(bytes); - next_packet_send_time_ = next_packet_send_time_.Add(delay); + // If the last send was delayed, and the alarm took a long time to get + // invoked, allow the connection to make up for lost time. + if (was_last_send_delayed_) { + next_packet_send_time_ = next_packet_send_time_.Add(delay); + // The send was application limited if it takes longer than the + // pacing delay between sent packets. + const bool application_limited = + last_delayed_packet_sent_time_.IsInitialized() && + sent_time > last_delayed_packet_sent_time_.Add(delay); + const bool making_up_for_lost_time = next_packet_send_time_ <= sent_time; + // As long as we're making up time and not application limited, + // continue to consider the packets delayed, allowing the packets to be + // sent immediately. + if (making_up_for_lost_time && !application_limited) { + last_delayed_packet_sent_time_ = sent_time; + } else { + was_last_send_delayed_ = false; + last_delayed_packet_sent_time_ = QuicTime::Zero(); + } + } else { + next_packet_send_time_ = + QuicTime::Max(next_packet_send_time_.Add(delay), + sent_time.Add(delay).Subtract(alarm_granularity_)); + } } - return sender_->OnPacketSent(sent_time, sequence_number, bytes, - transmission_type, has_retransmittable_data); -} - -void PacingSender::OnRetransmissionTimeout() { - sender_->OnRetransmissionTimeout(); + return sender_->OnPacketSent(sent_time, bytes_in_flight, sequence_number, + bytes, has_retransmittable_data); } -void PacingSender::OnPacketAbandoned(QuicPacketSequenceNumber sequence_number, - QuicByteCount abandoned_bytes) { - sender_->OnPacketAbandoned(sequence_number, abandoned_bytes); +void PacingSender::OnRetransmissionTimeout(bool packets_retransmitted) { + sender_->OnRetransmissionTimeout(packets_retransmitted); } QuicTime::Delta PacingSender::TimeUntilSend( QuicTime now, - TransmissionType transmission_type, - HasRetransmittableData has_retransmittable_data, - IsHandshake handshake) { + QuicByteCount bytes_in_flight, + HasRetransmittableData has_retransmittable_data) const { QuicTime::Delta time_until_send = - sender_->TimeUntilSend(now, transmission_type, - has_retransmittable_data, handshake); + sender_->TimeUntilSend(now, bytes_in_flight, has_retransmittable_data); + if (!has_valid_rtt_) { + // Don't pace if we don't have an updated RTT estimate. + return time_until_send; + } + if (!time_until_send.IsZero()) { DCHECK(time_until_send.IsInfinite()); // The underlying sender prevents sending. @@ -96,25 +111,14 @@ QuicTime::Delta PacingSender::TimeUntilSend( return QuicTime::Delta::Zero(); } - if (!was_last_send_delayed_ && - (!next_packet_send_time_.IsInitialized() || - now > next_packet_send_time_.Add(alarm_granularity_))) { - // An alarm did not go off late, instead the application is "slow" - // delivering data. In this case, we restrict the amount of lost time - // that we can make up for. - next_packet_send_time_ = now.Subtract(alarm_granularity_); - } - - // If the end of the epoch is far enough in the future, delay the send. + // If the next send time is within the alarm granularity, send immediately. if (next_packet_send_time_ > now.Add(alarm_granularity_)) { - was_last_send_delayed_ = true; DVLOG(1) << "Delaying packet: " << next_packet_send_time_.Subtract(now).ToMicroseconds(); + was_last_send_delayed_ = true; return next_packet_send_time_.Subtract(now); } - // Sent it immediately. The epoch end will be adjusted in OnPacketSent. - was_last_send_delayed_ = false; DVLOG(1) << "Sending packet now"; return QuicTime::Delta::Zero(); } @@ -123,10 +127,6 @@ QuicBandwidth PacingSender::BandwidthEstimate() const { return sender_->BandwidthEstimate(); } -QuicTime::Delta PacingSender::SmoothedRtt() const { - return sender_->SmoothedRtt(); -} - QuicTime::Delta PacingSender::RetransmissionDelay() const { return sender_->RetransmissionDelay(); } diff --git a/chromium/net/quic/congestion_control/pacing_sender.h b/chromium/net/quic/congestion_control/pacing_sender.h index 92970b93ce9..c74177125d2 100644 --- a/chromium/net/quic/congestion_control/pacing_sender.h +++ b/chromium/net/quic/congestion_control/pacing_sender.h @@ -17,7 +17,6 @@ #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_clock.h" #include "net/quic/quic_config.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_time.h" @@ -32,42 +31,35 @@ class NET_EXPORT_PRIVATE PacingSender : public SendAlgorithmInterface { // SendAlgorithmInterface methods. virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE; - virtual void SetMaxPacketSize(QuicByteCount max_packet_size) OVERRIDE; virtual void OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, - QuicTime feedback_receive_time, - const SendAlgorithmInterface::SentPacketsMap& sent_packets) OVERRIDE; - virtual void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number, - QuicByteCount acked_bytes, - QuicTime::Delta rtt) OVERRIDE; - virtual void OnPacketLost(QuicPacketSequenceNumber sequence_number, - QuicTime ack_receive_time) OVERRIDE; + QuicTime feedback_receive_time) OVERRIDE; + virtual void OnCongestionEvent(bool rtt_updated, + QuicByteCount bytes_in_flight, + const CongestionMap& acked_packets, + const CongestionMap& lost_packets) OVERRIDE; virtual bool OnPacketSent(QuicTime sent_time, + QuicByteCount bytes_in_flight, QuicPacketSequenceNumber sequence_number, QuicByteCount bytes, - TransmissionType transmission_type, HasRetransmittableData is_retransmittable) OVERRIDE; - virtual void OnRetransmissionTimeout() OVERRIDE; - virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number, - QuicByteCount abandoned_bytes) OVERRIDE; + virtual void OnRetransmissionTimeout(bool packets_retransmitted) OVERRIDE; virtual QuicTime::Delta TimeUntilSend( QuicTime now, - TransmissionType transmission_type, - HasRetransmittableData has_retransmittable_data, - IsHandshake handshake) OVERRIDE; + QuicByteCount bytes_in_flight, + HasRetransmittableData has_retransmittable_data) const OVERRIDE; virtual QuicBandwidth BandwidthEstimate() const OVERRIDE; - virtual QuicTime::Delta SmoothedRtt() const OVERRIDE; virtual QuicTime::Delta RetransmissionDelay() const OVERRIDE; virtual QuicByteCount GetCongestionWindow() const OVERRIDE; private: - QuicTime::Delta GetTransferTime(QuicByteCount bytes); - scoped_ptr<SendAlgorithmInterface> sender_; // Underlying sender. QuicTime::Delta alarm_granularity_; + // Send time of the last packet considered delayed. + QuicTime last_delayed_packet_sent_time_; QuicTime next_packet_send_time_; // When can the next packet be sent. - bool was_last_send_delayed_; // True when the last send was delayed. - QuicByteCount max_segment_size_; + mutable bool was_last_send_delayed_; // True when the last send was delayed. + bool has_valid_rtt_; // True if we have at least one RTT update. DISALLOW_COPY_AND_ASSIGN(PacingSender); }; diff --git a/chromium/net/quic/congestion_control/pacing_sender_test.cc b/chromium/net/quic/congestion_control/pacing_sender_test.cc index dfc78b84ece..2dc696098ff 100644 --- a/chromium/net/quic/congestion_control/pacing_sender_test.cc +++ b/chromium/net/quic/congestion_control/pacing_sender_test.cc @@ -13,10 +13,13 @@ using testing::Return; using testing::StrictMock; +using testing::_; namespace net { namespace test { +const QuicByteCount kBytesInFlight = 1024; + class PacingSenderTest : public ::testing::Test { protected: PacingSenderTest() @@ -35,23 +38,24 @@ class PacingSenderTest : public ::testing::Test { void CheckPacketIsSentImmediately() { // In order for the packet to be sendable, the underlying sender must // permit it to be sent immediately. - EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA, - NOT_HANDSHAKE)) - .WillOnce(Return(zero_time_)); - // Verify that the packet can be sent immediately. - EXPECT_EQ(zero_time_, - pacing_sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA, - NOT_HANDSHAKE)); + for (int i = 0; i < 2; ++i) { + EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(), + kBytesInFlight, + HAS_RETRANSMITTABLE_DATA)) + .WillOnce(Return(zero_time_)); + // Verify that the packet can be sent immediately. + EXPECT_EQ(zero_time_, + pacing_sender_->TimeUntilSend(clock_.Now(), + kBytesInFlight, + HAS_RETRANSMITTABLE_DATA)); + } // Actually send the packet. EXPECT_CALL(*mock_sender_, - OnPacketSent(clock_.Now(), sequence_number_, kMaxPacketSize, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA)); - pacing_sender_->OnPacketSent(clock_.Now(), sequence_number_++, - kMaxPacketSize, NOT_RETRANSMISSION, + OnPacketSent(clock_.Now(), kBytesInFlight, sequence_number_, + kMaxPacketSize, HAS_RETRANSMITTABLE_DATA)); + pacing_sender_->OnPacketSent(clock_.Now(), kBytesInFlight, + sequence_number_++, kMaxPacketSize, HAS_RETRANSMITTABLE_DATA); } @@ -59,38 +63,38 @@ class PacingSenderTest : public ::testing::Test { // In order for the ack to be sendable, the underlying sender must // permit it to be sent immediately. EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, - NO_RETRANSMITTABLE_DATA, - NOT_HANDSHAKE)) + 0, + NO_RETRANSMITTABLE_DATA)) .WillOnce(Return(zero_time_)); // Verify that the ACK can be sent immediately. EXPECT_EQ(zero_time_, - pacing_sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, - NO_RETRANSMITTABLE_DATA, - NOT_HANDSHAKE)); + pacing_sender_->TimeUntilSend(clock_.Now(), + 0, + NO_RETRANSMITTABLE_DATA)); // Actually send the packet. EXPECT_CALL(*mock_sender_, - OnPacketSent(clock_.Now(), sequence_number_, kMaxPacketSize, - NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA)); - pacing_sender_->OnPacketSent(clock_.Now(), sequence_number_++, - kMaxPacketSize, NOT_RETRANSMISSION, + OnPacketSent(clock_.Now(), 0, sequence_number_, + kMaxPacketSize, NO_RETRANSMITTABLE_DATA)); + pacing_sender_->OnPacketSent(clock_.Now(), 0, + sequence_number_++, kMaxPacketSize, NO_RETRANSMITTABLE_DATA); } void CheckPacketIsDelayed(QuicTime::Delta delay) { // In order for the packet to be sendable, the underlying sender must // permit it to be sent immediately. - EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA, - NOT_HANDSHAKE)) - .WillOnce(Return(zero_time_)); - // Verify that the packet is delayed. - EXPECT_EQ(delay.ToMicroseconds(), - pacing_sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA, - NOT_HANDSHAKE).ToMicroseconds()); + for (int i = 0; i < 2; ++i) { + EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(), + kBytesInFlight, + HAS_RETRANSMITTABLE_DATA)) + .WillOnce(Return(zero_time_)); + // Verify that the packet is delayed. + EXPECT_EQ(delay.ToMicroseconds(), + pacing_sender_->TimeUntilSend( + clock_.Now(), kBytesInFlight, + HAS_RETRANSMITTABLE_DATA).ToMicroseconds()); + } } const QuicTime::Delta zero_time_; @@ -102,27 +106,29 @@ class PacingSenderTest : public ::testing::Test { }; TEST_F(PacingSenderTest, NoSend) { - EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA, - NOT_HANDSHAKE)) - .WillOnce(Return(infinite_time_)); - EXPECT_EQ(infinite_time_, - pacing_sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA, - NOT_HANDSHAKE)); + for (int i = 0; i < 2; ++i) { + EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(), + kBytesInFlight, + HAS_RETRANSMITTABLE_DATA)) + .WillOnce(Return(infinite_time_)); + EXPECT_EQ(infinite_time_, + pacing_sender_->TimeUntilSend(clock_.Now(), + kBytesInFlight, + HAS_RETRANSMITTABLE_DATA)); + } } TEST_F(PacingSenderTest, SendNow) { - EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA, - NOT_HANDSHAKE)) - .WillOnce(Return(zero_time_)); - EXPECT_EQ(zero_time_, - pacing_sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA, - NOT_HANDSHAKE)); + for (int i = 0; i < 2; ++i) { + EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(), + kBytesInFlight, + HAS_RETRANSMITTABLE_DATA)) + .WillOnce(Return(zero_time_)); + EXPECT_EQ(zero_time_, + pacing_sender_->TimeUntilSend(clock_.Now(), + kBytesInFlight, + HAS_RETRANSMITTABLE_DATA)); + } } TEST_F(PacingSenderTest, VariousSending) { @@ -132,6 +138,16 @@ TEST_F(PacingSenderTest, VariousSending) { .WillRepeatedly(Return(QuicBandwidth::FromBytesAndTimeDelta( kMaxPacketSize, QuicTime::Delta::FromMilliseconds(2)))); + // Send a whole pile of packets, and verify that they are not paced. + for (int i = 0 ; i < 1000; ++i) { + CheckPacketIsSentImmediately(); + } + + // Now update the RTT and verify that packets are actually paced. + EXPECT_CALL(*mock_sender_, OnCongestionEvent(true, kBytesInFlight, _, _)); + SendAlgorithmInterface::CongestionMap empty_map; + pacing_sender_->OnCongestionEvent(true, kBytesInFlight, empty_map, empty_map); + CheckPacketIsSentImmediately(); CheckPacketIsSentImmediately(); CheckPacketIsSentImmediately(); @@ -155,6 +171,29 @@ TEST_F(PacingSenderTest, VariousSending) { CheckPacketIsSentImmediately(); CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); + // Wake up really late. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8)); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); + + // Wake up really late again, but application pause partway through. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8)); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100)); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsSentImmediately(); + CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); + // Wake up too early. CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2)); diff --git a/chromium/net/quic/congestion_control/quic_max_sized_map.h b/chromium/net/quic/congestion_control/quic_max_sized_map.h deleted file mode 100644 index a4ed7769d61..00000000000 --- a/chromium/net/quic/congestion_control/quic_max_sized_map.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Simple max sized map. Automatically deletes the oldest element when the -// max limit is reached. -// Note: the ConstIterator will NOT be valid after an Insert or RemoveAll. -#ifndef NET_QUIC_CONGESTION_CONTROL_QUIC_MAX_SIZED_MAP_H_ -#define NET_QUIC_CONGESTION_CONTROL_QUIC_MAX_SIZED_MAP_H_ - -#include <stdlib.h> - -#include <list> -#include <map> - -#include "base/basictypes.h" - -namespace net { - -template <class Key, class Value> -class QuicMaxSizedMap { - public: - typedef typename std::multimap<Key, Value>::const_iterator ConstIterator; - - explicit QuicMaxSizedMap(size_t max_numer_of_items) - : max_numer_of_items_(max_numer_of_items) { - } - - size_t MaxSize() const { - return max_numer_of_items_; - } - - size_t Size() const { - return table_.size(); - } - - void Insert(const Key& k, const Value& value) { - if (Size() == MaxSize()) { - ListIterator list_it = insert_order_.begin(); - table_.erase(*list_it); - insert_order_.pop_front(); - } - TableIterator it = table_.insert(std::pair<Key, Value>(k, value)); - insert_order_.push_back(it); - } - - void RemoveAll() { - table_.clear(); - insert_order_.clear(); - } - - // STL style const_iterator support. - ConstIterator Find(const Key& k) const { - return table_.find(k); - } - - ConstIterator Begin() const { - return ConstIterator(table_.begin()); - } - - ConstIterator End() const { - return ConstIterator(table_.end()); - } - - private: - typedef typename std::multimap<Key, Value>::iterator TableIterator; - typedef typename std::list<TableIterator>::iterator ListIterator; - - const size_t max_numer_of_items_; - std::multimap<Key, Value> table_; - std::list<TableIterator> insert_order_; - - DISALLOW_COPY_AND_ASSIGN(QuicMaxSizedMap); -}; - -} // namespace net -#endif // NET_QUIC_CONGESTION_CONTROL_QUIC_MAX_SIZED_MAP_H_ diff --git a/chromium/net/quic/congestion_control/quic_max_sized_map_test.cc b/chromium/net/quic/congestion_control/quic_max_sized_map_test.cc deleted file mode 100644 index 89c05cccbaf..00000000000 --- a/chromium/net/quic/congestion_control/quic_max_sized_map_test.cc +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/logging.h" -#include "net/quic/congestion_control/quic_max_sized_map.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { -namespace test { - -class QuicMaxSizedMapTest : public ::testing::Test { -}; - -TEST_F(QuicMaxSizedMapTest, Basic) { - QuicMaxSizedMap<int, int> test_map(100); - EXPECT_EQ(100u, test_map.MaxSize()); - EXPECT_EQ(0u, test_map.Size()); - test_map.Insert(1, 2); - test_map.Insert(1, 3); - EXPECT_EQ(100u, test_map.MaxSize()); - EXPECT_EQ(2u, test_map.Size()); - test_map.RemoveAll(); - EXPECT_EQ(100u, test_map.MaxSize()); - EXPECT_EQ(0u, test_map.Size()); -} - -TEST_F(QuicMaxSizedMapTest, Find) { - QuicMaxSizedMap<int, int> test_map(100); - test_map.Insert(1, 2); - test_map.Insert(1, 3); - test_map.Insert(2, 4); - test_map.Insert(3, 5); - QuicMaxSizedMap<int, int>::ConstIterator it = test_map.Find(2); - EXPECT_TRUE(it != test_map.End()); - EXPECT_EQ(4, it->second); - it = test_map.Find(1); - EXPECT_TRUE(it != test_map.End()); - EXPECT_EQ(2, it->second); - ++it; - EXPECT_TRUE(it != test_map.End()); - EXPECT_EQ(3, it->second); -} - -TEST_F(QuicMaxSizedMapTest, Sort) { - QuicMaxSizedMap<int, int> test_map(100); - test_map.Insert(9, 9); - test_map.Insert(8, 8); - test_map.Insert(7, 7); - test_map.Insert(6, 6); - test_map.Insert(2, 2); - test_map.Insert(4, 4); - test_map.Insert(5, 5); - test_map.Insert(3, 3); - test_map.Insert(0, 0); - test_map.Insert(1, 1); - QuicMaxSizedMap<int, int>::ConstIterator it = test_map.Begin(); - for (int i = 0; i < 10; ++i, ++it) { - EXPECT_TRUE(it != test_map.End()); - EXPECT_EQ(i, it->first); - EXPECT_EQ(i, it->second); - } -} - -} // namespace test -} // namespace net diff --git a/chromium/net/quic/congestion_control/receive_algorithm_interface.cc b/chromium/net/quic/congestion_control/receive_algorithm_interface.cc index a49f64f160a..5384bdcddf7 100644 --- a/chromium/net/quic/congestion_control/receive_algorithm_interface.cc +++ b/chromium/net/quic/congestion_control/receive_algorithm_interface.cc @@ -15,8 +15,12 @@ ReceiveAlgorithmInterface* ReceiveAlgorithmInterface::Create( switch (type) { case kTCP: return new TcpReceiver(); + case kTCPBBR: + LOG(DFATAL) << "TCPBBR is not yet supported."; + return NULL; case kInterArrival: - break; // TODO(pwestin) Implement. + LOG(DFATAL) << "InterArrivalSendAlgorithm no longer supported."; + return NULL; case kFixRate: return new FixRateReceiver(); } diff --git a/chromium/net/quic/congestion_control/receive_algorithm_interface.h b/chromium/net/quic/congestion_control/receive_algorithm_interface.h index 17e7793749a..cd96b022db2 100644 --- a/chromium/net/quic/congestion_control/receive_algorithm_interface.h +++ b/chromium/net/quic/congestion_control/receive_algorithm_interface.h @@ -30,12 +30,9 @@ class NET_EXPORT_PRIVATE ReceiveAlgorithmInterface { // bytes: is the packet size in bytes including IP headers. // sequence_number: is the unique sequence number from the QUIC packet header. // 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(QuicByteCount bytes, QuicPacketSequenceNumber sequence_number, - QuicTime timestamp, - bool revived) = 0; + QuicTime timestamp) = 0; }; } // namespace net diff --git a/chromium/net/quic/congestion_control/rtt_stats.cc b/chromium/net/quic/congestion_control/rtt_stats.cc new file mode 100644 index 00000000000..745deb58cb3 --- /dev/null +++ b/chromium/net/quic/congestion_control/rtt_stats.cc @@ -0,0 +1,129 @@ +// 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/rtt_stats.h" + +namespace net { + +namespace { + +// Default initial rtt used before any samples are received. +const int kInitialRttMs = 100; +const float kAlpha = 0.125f; +const float kOneMinusAlpha = (1 - kAlpha); +const float kBeta = 0.25f; +const float kOneMinusBeta = (1 - kBeta); +const float kHalfWindow = 0.5f; +const float kQuarterWindow = 0.25f; + +} // namespace + +RttStats::RttStats() + : latest_rtt_(QuicTime::Delta::Zero()), + min_rtt_(QuicTime::Delta::Zero()), + smoothed_rtt_(QuicTime::Delta::Zero()), + mean_deviation_(QuicTime::Delta::Zero()), + initial_rtt_us_(kInitialRttMs * base::Time::kMicrosecondsPerMillisecond), + num_min_rtt_samples_remaining_(0), + recent_min_rtt_window_(QuicTime::Delta::Infinite()) {} + +bool RttStats::HasUpdates() const { + return !smoothed_rtt_.IsZero(); +} + +void RttStats::SampleNewRecentMinRtt(uint32 num_samples) { + num_min_rtt_samples_remaining_ = num_samples; + new_min_rtt_ = RttSample(); +} + +// Updates the RTT based on a new sample. +void RttStats::UpdateRtt(QuicTime::Delta send_delta, + QuicTime::Delta ack_delay, + QuicTime now) { + QuicTime::Delta rtt_sample(QuicTime::Delta::Zero()); + if (send_delta > ack_delay) { + rtt_sample = send_delta.Subtract(ack_delay); + } else if (!HasUpdates()) { + // Even though we received information from the peer suggesting + // an invalid (negative) RTT, we can use the send delta as an + // approximation until we get a better estimate. + rtt_sample = send_delta; + } + + if (rtt_sample.IsInfinite() || rtt_sample.IsZero()) { + DVLOG(1) << "Ignoring rtt, because it's " + << (rtt_sample.IsZero() ? "Zero" : "Infinite"); + return; + } + // RTT can't be negative. + DCHECK_LT(0, rtt_sample.ToMicroseconds()); + + latest_rtt_ = rtt_sample; + // First time call or link delay decreases. + if (min_rtt_.IsZero() || min_rtt_ > rtt_sample) { + min_rtt_ = rtt_sample; + } + UpdateRecentMinRtt(rtt_sample, now); + // First time call. + if (!HasUpdates()) { + smoothed_rtt_ = rtt_sample; + mean_deviation_ = QuicTime::Delta::FromMicroseconds( + rtt_sample.ToMicroseconds() / 2); + } else { + mean_deviation_ = QuicTime::Delta::FromMicroseconds( + kOneMinusBeta * mean_deviation_.ToMicroseconds() + + kBeta * std::abs(smoothed_rtt_.Subtract(rtt_sample).ToMicroseconds())); + smoothed_rtt_ = smoothed_rtt_.Multiply(kOneMinusAlpha).Add( + rtt_sample.Multiply(kAlpha)); + DVLOG(1) << "Cubic; smoothed_rtt(us):" << smoothed_rtt_.ToMicroseconds() + << " mean_deviation(us):" << mean_deviation_.ToMicroseconds(); + } +} + +void RttStats::UpdateRecentMinRtt(QuicTime::Delta rtt_sample, QuicTime now) { + // Recent min_rtt update. + if (num_min_rtt_samples_remaining_ > 0) { + --num_min_rtt_samples_remaining_; + if (new_min_rtt_.rtt.IsZero() || rtt_sample <= new_min_rtt_.rtt) { + new_min_rtt_ = RttSample(rtt_sample, now); + } + if (num_min_rtt_samples_remaining_ == 0) { + quarter_window_rtt_ = half_window_rtt_ = recent_min_rtt_ = new_min_rtt_; + } + } + + // Update the three recent rtt samples. + if (recent_min_rtt_.rtt.IsZero() || rtt_sample <= recent_min_rtt_.rtt) { + recent_min_rtt_ = RttSample(rtt_sample, now); + quarter_window_rtt_ = half_window_rtt_ = recent_min_rtt_; + } else if (rtt_sample <= half_window_rtt_.rtt) { + half_window_rtt_ = RttSample(rtt_sample, now); + quarter_window_rtt_ = half_window_rtt_; + } else if (rtt_sample <= quarter_window_rtt_.rtt) { + quarter_window_rtt_ = RttSample(rtt_sample, now); + } + + // Expire old min rtt samples. + if (recent_min_rtt_.time < now.Subtract(recent_min_rtt_window_)) { + recent_min_rtt_ = half_window_rtt_; + half_window_rtt_ = quarter_window_rtt_; + quarter_window_rtt_ = RttSample(rtt_sample, now); + } else if (half_window_rtt_.time < + now.Subtract(recent_min_rtt_window_.Multiply(kHalfWindow))) { + half_window_rtt_ = quarter_window_rtt_; + quarter_window_rtt_ = RttSample(rtt_sample, now); + } else if (quarter_window_rtt_.time < + now.Subtract(recent_min_rtt_window_.Multiply(kQuarterWindow))) { + quarter_window_rtt_ = RttSample(rtt_sample, now); + } +} + +QuicTime::Delta RttStats::SmoothedRtt() const { + if (!HasUpdates()) { + return QuicTime::Delta::FromMicroseconds(initial_rtt_us_); + } + return smoothed_rtt_; +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/rtt_stats.h b/chromium/net/quic/congestion_control/rtt_stats.h new file mode 100644 index 00000000000..ee8d1809086 --- /dev/null +++ b/chromium/net/quic/congestion_control/rtt_stats.h @@ -0,0 +1,112 @@ +// 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. +// +// A convenience class to store rtt samples and calculate smoothed rtt. + +#ifndef NET_QUIC_CONGESTION_CONTROL_RTT_STATS_H_ +#define NET_QUIC_CONGESTION_CONTROL_RTT_STATS_H_ + +#include <algorithm> + +#include "base/basictypes.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_time.h" + +namespace net { + +namespace test { +class RttStatsPeer; +} // namespace test + +class NET_EXPORT_PRIVATE RttStats { + public: + RttStats(); + + // Returns true if any RTT measurements have been made. + bool HasUpdates() const; + + // Updates the RTT from an incoming ack which is received |send_delta| after + // the packet is sent and the peer reports the ack being delayed |ack_delay|. + void UpdateRtt(QuicTime::Delta send_delta, + QuicTime::Delta ack_delay, + QuicTime now); + + // Forces RttStats to sample a new recent min rtt within the next + // |num_samples| UpdateRtt calls. + void SampleNewRecentMinRtt(uint32 num_samples); + + QuicTime::Delta SmoothedRtt() const; + + int64 initial_rtt_us() const { + return initial_rtt_us_; + } + + // Sets an initial RTT to be used for SmoothedRtt before any RTT updates. + void set_initial_rtt_us(int64 initial_rtt_us) { + initial_rtt_us_ = initial_rtt_us; + } + + QuicTime::Delta latest_rtt() const { + return latest_rtt_; + } + + // Returns the min_rtt for the entire connection. + QuicTime::Delta min_rtt() const { + return min_rtt_; + } + + // Returns the min_rtt since SampleNewRecentMinRtt has been called, or the + // min_rtt for the entire connection if SampleNewMinRtt was never called. + QuicTime::Delta recent_min_rtt() const { + return recent_min_rtt_.rtt; + } + + QuicTime::Delta mean_deviation() const { + return mean_deviation_; + } + + // Sets how old a recent min rtt sample can be. + void set_recent_min_rtt_window(QuicTime::Delta recent_min_rtt_window) { + recent_min_rtt_window_ = recent_min_rtt_window; + } + + private: + friend class test::RttStatsPeer; + + // Used to track a sampled RTT window. + struct RttSample { + RttSample() : rtt(QuicTime::Delta::Zero()), time(QuicTime::Zero()) { } + RttSample(QuicTime::Delta rtt, QuicTime time) : rtt(rtt), time(time) { } + + QuicTime::Delta rtt; + QuicTime time; // Time the rtt sample was recorded. + }; + + // Implements the resampling algorithm and the windowed min rtt algorithm. + void UpdateRecentMinRtt(QuicTime::Delta rtt_sample, QuicTime now); + + QuicTime::Delta latest_rtt_; + QuicTime::Delta min_rtt_; + QuicTime::Delta smoothed_rtt_; + // Mean RTT deviation during this session. + // Approximation of standard deviation, the error is roughly 1.25 times + // larger than the standard deviation, for a normally distributed signal. + QuicTime::Delta mean_deviation_; + int64 initial_rtt_us_; + + RttSample new_min_rtt_; + uint32 num_min_rtt_samples_remaining_; + + // State variables for Kathleen Nichols MinRTT algorithm. + QuicTime::Delta recent_min_rtt_window_; + RttSample recent_min_rtt_; // a in the windowed algorithm. + RttSample half_window_rtt_; // b in the sampled algorithm. + RttSample quarter_window_rtt_; // c in the sampled algorithm. + + DISALLOW_COPY_AND_ASSIGN(RttStats); +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_RTT_STATS_H_ diff --git a/chromium/net/quic/congestion_control/rtt_stats_test.cc b/chromium/net/quic/congestion_control/rtt_stats_test.cc new file mode 100644 index 00000000000..f1c0fc1ce98 --- /dev/null +++ b/chromium/net/quic/congestion_control/rtt_stats_test.cc @@ -0,0 +1,160 @@ +// 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/rtt_stats.h" + +#include "base/logging.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +class RttStatsPeer { + public: + static QuicTime::Delta GetHalfWindowRtt(const RttStats* rtt_stats) { + return rtt_stats->half_window_rtt_.rtt; + } + + static QuicTime::Delta GetQuarterWindowRtt(const RttStats* rtt_stats) { + return rtt_stats->quarter_window_rtt_.rtt; + } +}; + +class RttStatsTest : public ::testing::Test { + protected: + RttStats rtt_stats_; +}; + +TEST_F(RttStatsTest, MinRtt) { + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100), + QuicTime::Delta::Zero(), + QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.min_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), + rtt_stats_.recent_min_rtt()); + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(10), + QuicTime::Delta::Zero(), + QuicTime::Zero().Add( + QuicTime::Delta::FromMilliseconds(10))); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt()); + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50), + QuicTime::Delta::Zero(), + QuicTime::Zero().Add( + QuicTime::Delta::FromMilliseconds(20))); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt()); + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50), + QuicTime::Delta::Zero(), + QuicTime::Zero().Add( + QuicTime::Delta::FromMilliseconds(30))); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt()); + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50), + QuicTime::Delta::Zero(), + QuicTime::Zero().Add( + QuicTime::Delta::FromMilliseconds(40))); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt()); +} + +TEST_F(RttStatsTest, RecentMinRtt) { + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(10), + QuicTime::Delta::Zero(), + QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt()); + + rtt_stats_.SampleNewRecentMinRtt(4); + for (int i = 0; i < 3; ++i) { + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50), + QuicTime::Delta::Zero(), + QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), + rtt_stats_.recent_min_rtt()); + } + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50), + QuicTime::Delta::Zero(), + QuicTime::Zero()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(50), rtt_stats_.recent_min_rtt()); +} + +TEST_F(RttStatsTest, WindowedRecentMinRtt) { + // Set the window to 99ms, so 25ms is more than a quarter rtt. + rtt_stats_.set_recent_min_rtt_window(QuicTime::Delta::FromMilliseconds(99)); + + QuicTime now = QuicTime::Zero(); + QuicTime::Delta rtt_sample = QuicTime::Delta::FromMilliseconds(10); + rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt()); + + // Gradually increase the rtt samples and ensure the recent_min_rtt starts + // rising. + for (int i = 0; i < 8; ++i) { + now = now.Add(QuicTime::Delta::FromMilliseconds(25)); + rtt_sample = rtt_sample.Add(QuicTime::Delta::FromMilliseconds(10)); + rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt()); + EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_)); + EXPECT_EQ(rtt_sample.Subtract(QuicTime::Delta::FromMilliseconds(10)), + RttStatsPeer::GetHalfWindowRtt(&rtt_stats_)); + if (i < 3) { + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), + rtt_stats_.recent_min_rtt()); + } else if (i < 5) { + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(30), + rtt_stats_.recent_min_rtt()); + } else if (i < 7) { + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(50), + rtt_stats_.recent_min_rtt()); + } else { + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(70), + rtt_stats_.recent_min_rtt()); + } + } + + // A new quarter rtt low sets that, but nothing else. + rtt_sample = rtt_sample.Subtract(QuicTime::Delta::FromMilliseconds(5)); + rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt()); + EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_)); + EXPECT_EQ(rtt_sample.Subtract(QuicTime::Delta::FromMilliseconds(5)), + RttStatsPeer::GetHalfWindowRtt(&rtt_stats_)); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(70), + rtt_stats_.recent_min_rtt()); + + // A new half rtt low sets that and the quarter rtt low. + rtt_sample = rtt_sample.Subtract(QuicTime::Delta::FromMilliseconds(15)); + rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt()); + EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_)); + EXPECT_EQ(rtt_sample, RttStatsPeer::GetHalfWindowRtt(&rtt_stats_)); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(70), + rtt_stats_.recent_min_rtt()); + + // A new full window loss sets the recent_min_rtt, but not min_rtt. + rtt_sample = QuicTime::Delta::FromMilliseconds(65); + rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt()); + EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_)); + EXPECT_EQ(rtt_sample, RttStatsPeer::GetHalfWindowRtt(&rtt_stats_)); + EXPECT_EQ(rtt_sample, rtt_stats_.recent_min_rtt()); + + // A new all time low sets both the min_rtt and the recent_min_rtt. + rtt_sample = QuicTime::Delta::FromMilliseconds(5); + rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now); + + EXPECT_EQ(rtt_sample, rtt_stats_.min_rtt()); + EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_)); + EXPECT_EQ(rtt_sample, RttStatsPeer::GetHalfWindowRtt(&rtt_stats_)); + EXPECT_EQ(rtt_sample, rtt_stats_.recent_min_rtt()); +} + + + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/send_algorithm_interface.cc b/chromium/net/quic/congestion_control/send_algorithm_interface.cc index 493d84e88ef..58b92bce081 100644 --- a/chromium/net/quic/congestion_control/send_algorithm_interface.cc +++ b/chromium/net/quic/congestion_control/send_algorithm_interface.cc @@ -12,17 +12,26 @@ namespace net { const bool kUseReno = false; +class RttStats; + // Factory for send side congestion control algorithm. SendAlgorithmInterface* SendAlgorithmInterface::Create( const QuicClock* clock, - CongestionFeedbackType type) { + const RttStats* rtt_stats, + CongestionFeedbackType type, + QuicConnectionStats* stats) { switch (type) { case kTCP: - return new TcpCubicSender(clock, kUseReno, kMaxTcpCongestionWindow); + return new TcpCubicSender(clock, rtt_stats, kUseReno, + kMaxTcpCongestionWindow, stats); case kInterArrival: - break; // TODO(pwestin) Implement. + LOG(DFATAL) << "InterArrivalSendAlgorithm no longer supported."; + return NULL; case kFixRate: - return new FixRateSender(clock); + return new FixRateSender(rtt_stats); + case kTCPBBR: + LOG(DFATAL) << "BbrTcpSender is not supported."; + return NULL; } return NULL; } diff --git a/chromium/net/quic/congestion_control/send_algorithm_interface.h b/chromium/net/quic/congestion_control/send_algorithm_interface.h index 87a5f9c03fc..6b1c75597f7 100644 --- a/chromium/net/quic/congestion_control/send_algorithm_interface.h +++ b/chromium/net/quic/congestion_control/send_algorithm_interface.h @@ -15,101 +15,68 @@ #include "net/quic/quic_bandwidth.h" #include "net/quic/quic_clock.h" #include "net/quic/quic_config.h" +#include "net/quic/quic_connection_stats.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_time.h" namespace net { +class RttStats; + class NET_EXPORT_PRIVATE SendAlgorithmInterface { public: - class SentPacket { - public: - SentPacket(QuicByteCount bytes, - QuicTime timestamp, - HasRetransmittableData has_retransmittable_data) - : bytes_sent_(bytes), - send_timestamp_(timestamp), - has_retransmittable_data_(has_retransmittable_data), - nack_count_(0) { - } - QuicByteCount bytes_sent() const { return bytes_sent_; } - const QuicTime& send_timestamp() const { return send_timestamp_; } - HasRetransmittableData has_retransmittable_data() const { - return has_retransmittable_data_; - } - size_t nack_count() const { return nack_count_; } - - void Nack(size_t min_nacks) { - nack_count_ = std::max(min_nacks, nack_count_ + 1); - } - - private: - QuicByteCount bytes_sent_; - QuicTime send_timestamp_; - HasRetransmittableData has_retransmittable_data_; - size_t nack_count_; - }; - - typedef std::map<QuicPacketSequenceNumber, SentPacket*> SentPacketsMap; + typedef std::map<QuicPacketSequenceNumber, TransmissionInfo> CongestionMap; static SendAlgorithmInterface* Create(const QuicClock* clock, - CongestionFeedbackType type); + const RttStats* rtt_stats, + CongestionFeedbackType type, + QuicConnectionStats* stats); virtual ~SendAlgorithmInterface() {} virtual void SetFromConfig(const QuicConfig& config, bool is_server) = 0; - // Sets the maximum size of packets that will be sent. - virtual void SetMaxPacketSize(QuicByteCount max_packet_size) = 0; - // Called when we receive congestion feedback from remote peer. virtual void OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, - QuicTime feedback_receive_time, - const SentPacketsMap& sent_packets) = 0; - - // Called for each received ACK, with sequence number from remote peer. - virtual void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number, - QuicByteCount acked_bytes, - QuicTime::Delta rtt) = 0; - - // Indicates a loss event of one packet. |sequence_number| is the - // sequence number of the lost packet. - virtual void OnPacketLost(QuicPacketSequenceNumber sequence_number, - QuicTime ack_receive_time) = 0; - - // Inform that we sent x bytes to the wire, and if that was a retransmission. - // Returns true if the packet should be tracked by the congestion manager, - // false otherwise. This is used by implementations such as tcp_cubic_sender - // that do not count outgoing ACK packets against the congestion window. + QuicTime feedback_receive_time) = 0; + + // Indicates an update to the congestion state, caused either by an incoming + // ack or loss event timeout. |rtt_updated| indicates whether a new + // latest_rtt sample has been taken, |byte_in_flight| the bytes in flight + // prior to the congestion event. |acked_packets| and |lost_packets| are + // any packets considered acked or lost as a result of the congestion event. + virtual void OnCongestionEvent(bool rtt_updated, + QuicByteCount bytes_in_flight, + const CongestionMap& acked_packets, + const CongestionMap& lost_packets) = 0; + + // Inform that we sent |bytes| to the wire, and if the packet is + // retransmittable. Returns true if the packet should be tracked by the + // congestion manager and included in bytes_in_flight, false otherwise. + // |bytes_in_flight| is the number of bytes in flight before the packet was + // sent. // Note: this function must be called for every packet sent to the wire. virtual bool OnPacketSent(QuicTime sent_time, + QuicByteCount bytes_in_flight, QuicPacketSequenceNumber sequence_number, QuicByteCount bytes, - TransmissionType transmission_type, HasRetransmittableData is_retransmittable) = 0; - // Called when the retransmission timeout fires. - virtual void OnRetransmissionTimeout() = 0; - - // Called when a packet is timed out. - virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number, - QuicByteCount abandoned_bytes) = 0; + // Called when the retransmission timeout fires. Neither OnPacketAbandoned + // nor OnPacketLost will be called for these packets. + virtual void OnRetransmissionTimeout(bool packets_retransmitted) = 0; // Calculate the time until we can send the next packet. virtual QuicTime::Delta TimeUntilSend( QuicTime now, - TransmissionType transmission_type, - HasRetransmittableData has_retransmittable_data, - IsHandshake handshake) = 0; + QuicByteCount bytes_in_flight, + HasRetransmittableData has_retransmittable_data) const = 0; // What's the current estimated bandwidth in bytes per second. // Returns 0 when it does not have an estimate. virtual QuicBandwidth BandwidthEstimate() const = 0; - // TODO(satyamshekhar): Monitor MinRtt. - virtual QuicTime::Delta SmoothedRtt() const = 0; - // Get the send algorithm specific retransmission delay, called RTO in TCP, // Note 1: the caller is responsible for sanity checking this value. // Note 2: this will return zero if we don't have enough data for an estimate. diff --git a/chromium/net/quic/congestion_control/send_algorithm_simulator.cc b/chromium/net/quic/congestion_control/send_algorithm_simulator.cc new file mode 100644 index 00000000000..d539013c10e --- /dev/null +++ b/chromium/net/quic/congestion_control/send_algorithm_simulator.cc @@ -0,0 +1,273 @@ +// 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/send_algorithm_simulator.h" + +#include <limits> + +#include "base/logging.h" +#include "base/rand_util.h" +#include "net/quic/crypto/quic_random.h" + +using std::list; +using std::max; +using std::min; + +namespace net { + +namespace { + +const QuicByteCount kPacketSize = 1200; + +} // namespace + +SendAlgorithmSimulator::SendAlgorithmSimulator( + SendAlgorithmInterface* send_algorithm, + MockClock* clock, + RttStats* rtt_stats, + QuicBandwidth bandwidth, + QuicTime::Delta rtt) + : send_algorithm_(send_algorithm), + clock_(clock), + rtt_stats_(rtt_stats), + next_sent_(1), + last_acked_(0), + next_acked_(1), + lose_next_ack_(false), + bytes_in_flight_(0), + forward_loss_rate_(0), + reverse_loss_rate_(0), + loss_correlation_(0), + bandwidth_(bandwidth), + rtt_(rtt), + buffer_size_(1000000), + max_cwnd_(0), + min_cwnd_(100000), + max_cwnd_drop_(0), + last_cwnd_(0) { + uint32 seed = base::RandInt(0, std::numeric_limits<int32>::max()); + DVLOG(1) << "Seeding SendAlgorithmSimulator with " << seed; + simple_random_.set_seed(seed); +} + +SendAlgorithmSimulator::~SendAlgorithmSimulator() {} + +// Sends the specified number of bytes as quickly as possible and returns the +// average bandwidth in bytes per second. The time elapsed is based on +// waiting for all acks to arrive. +QuicBandwidth SendAlgorithmSimulator::SendBytes(size_t num_bytes) { + const QuicTime start_time = clock_->Now(); + size_t bytes_acked = 0; + while (bytes_acked < num_bytes) { + DVLOG(1) << "bytes_acked:" << bytes_acked << " bytes_in_flight_:" + << bytes_in_flight_ << " CWND(bytes):" + << send_algorithm_->GetCongestionWindow(); + // Determine the times of next send and of the next ack arrival. + QuicTime::Delta send_delta = send_algorithm_->TimeUntilSend( + clock_->Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA); + // If we've already sent enough bytes, wait for them to be acked. + if (bytes_acked + bytes_in_flight_ >= num_bytes) { + send_delta = QuicTime::Delta::Infinite(); + } + QuicTime::Delta ack_delta = NextAckDelta(); + // If both times are infinite, fire a TLP. + if (ack_delta.IsInfinite() && send_delta.IsInfinite()) { + DVLOG(1) << "Both times are infinite, simulating a TLP."; + // TODO(ianswett): Use a more sophisticated TLP timer. + clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(100)); + SendDataNow(); + } else if (ack_delta < send_delta) { + DVLOG(1) << "Handling ack, advancing time:" + << ack_delta.ToMicroseconds() << "us"; + // Ack data all the data up to ack time and lose any missing sequence + // numbers. + clock_->AdvanceTime(ack_delta); + bytes_acked += HandlePendingAck(); + } else { + DVLOG(1) << "Sending, advancing time:" + << send_delta.ToMicroseconds() << "us"; + clock_->AdvanceTime(send_delta); + SendDataNow(); + } + RecordStats(); + } + return QuicBandwidth::FromBytesAndTimeDelta( + num_bytes, clock_->Now().Subtract(start_time)); +} + +// NextAck takes into account packet loss in both forward and reverse +// direction, as well as correlated losses. And it assumes the receiver acks +// every other packet when there is no loss. +QuicTime::Delta SendAlgorithmSimulator::NextAckDelta() { + if (sent_packets_.empty() || AllPacketsLost()) { + DVLOG(1) << "No outstanding packets to cause acks. sent_packets_.size():" + << sent_packets_.size(); + return QuicTime::Delta::Infinite(); + } + + // If necessary, determine next_acked_. + // This is only done once to ensure multiple calls return the same time. + FindNextAcked(); + + // If only one packet is acked, simulate a delayed ack. + if (next_acked_ - last_acked_ == 1) { + return sent_packets_.front().ack_time.Add( + QuicTime::Delta::FromMilliseconds(100)).Subtract(clock_->Now()); + } + for (list<SentPacket>::const_iterator it = sent_packets_.begin(); + it != sent_packets_.end(); ++it) { + if (next_acked_ == it->sequence_number) { + return it->ack_time.Subtract(clock_->Now()); + } + } + LOG(DFATAL) << "Error, next_acked_: " << next_acked_ + << " should have been found in sent_packets_"; + return QuicTime::Delta::Infinite(); +} + +bool SendAlgorithmSimulator::AllPacketsLost() { + for (list<SentPacket>::const_iterator it = sent_packets_.begin(); + it != sent_packets_.end(); ++it) { + if (it->ack_time.IsInitialized()) { + return false; + } + } + return true; +} + +void SendAlgorithmSimulator::FindNextAcked() { + // TODO(ianswett): Add a simpler mode which acks every packet. + bool packets_lost = false; + if (next_acked_ == last_acked_) { + // Determine if the next ack is lost only once, to ensure determinism. + lose_next_ack_ = + reverse_loss_rate_ * kuint64max > simple_random_.RandUint64(); + } + bool two_acks_remaining = lose_next_ack_; + next_acked_ = last_acked_; + // Remove any packets that are simulated as lost. + for (list<SentPacket>::const_iterator it = sent_packets_.begin(); + it != sent_packets_.end(); ++it) { + // 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 (next_acked_ < it->sequence_number - 1) { + packets_lost = true; + } + next_acked_ = it->sequence_number; + if (packets_lost || (next_acked_ - last_acked_) % 2 == 0) { + if (two_acks_remaining) { + two_acks_remaining = false; + } else { + break; + } + } + } + DVLOG(1) << "FindNextAcked found next_acked_:" << next_acked_ + << " last_acked:" << last_acked_; +} + +int SendAlgorithmSimulator::HandlePendingAck() { + DCHECK_LT(last_acked_, next_acked_); + SendAlgorithmInterface::CongestionMap acked_packets; + SendAlgorithmInterface::CongestionMap lost_packets; + // Some entries may be missing from the sent_packets_ array, if they were + // dropped due to buffer overruns. + SentPacket largest_observed = sent_packets_.front(); + while (last_acked_ < next_acked_) { + ++last_acked_; + TransmissionInfo info = TransmissionInfo(); + info.bytes_sent = kPacketSize; + info.in_flight = true; + // If it's missing from the array, it's a loss. + if (sent_packets_.front().sequence_number > last_acked_) { + DVLOG(1) << "Lost packet:" << last_acked_ + << " dropped by buffer overflow."; + lost_packets[last_acked_] = info; + continue; + } + if (sent_packets_.front().ack_time.IsInitialized()) { + acked_packets[last_acked_] = info; + } else { + lost_packets[last_acked_] = info; + } + // Remove all packets from the front to next_acked_. + largest_observed = sent_packets_.front(); + sent_packets_.pop_front(); + } + + DCHECK(largest_observed.ack_time.IsInitialized()); + rtt_stats_->UpdateRtt( + largest_observed.ack_time.Subtract(largest_observed.send_time), + QuicTime::Delta::Zero(), + clock_->Now()); + send_algorithm_->OnCongestionEvent( + true, bytes_in_flight_, acked_packets, lost_packets); + DCHECK_LE(kPacketSize * (acked_packets.size() + lost_packets.size()), + bytes_in_flight_); + bytes_in_flight_ -= + kPacketSize * (acked_packets.size() + lost_packets.size()); + return acked_packets.size() * kPacketSize; +} + +void SendAlgorithmSimulator::SendDataNow() { + DVLOG(1) << "Sending packet:" << next_sent_ << " bytes_in_flight:" + << bytes_in_flight_; + send_algorithm_->OnPacketSent( + clock_->Now(), bytes_in_flight_, + next_sent_, kPacketSize, HAS_RETRANSMITTABLE_DATA); + // Lose the packet immediately if the buffer is full. + if (sent_packets_.size() * kPacketSize < buffer_size_) { + // TODO(ianswett): This buffer simulation is an approximation. + // An ack time of zero means loss. + bool packet_lost = + forward_loss_rate_ * kuint64max > simple_random_.RandUint64(); + // Handle correlated loss. + if (!sent_packets_.empty() && + !sent_packets_.back().ack_time.IsInitialized() && + loss_correlation_ * kuint64max > simple_random_.RandUint64()) { + packet_lost = true; + } + + 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() * kPacketSize > bdp) { + QuicByteCount qsize = sent_packets_.size() * kPacketSize - bdp; + ack_time = ack_time.Add(bandwidth_.TransferTime(qsize)); + } + // If the packet is lost, give it an ack time of Zero. + sent_packets_.push_back(SentPacket( + next_sent_, clock_->Now(), packet_lost ? QuicTime::Zero() : ack_time)); + } + ++next_sent_; + bytes_in_flight_ += kPacketSize; +} + +void SendAlgorithmSimulator::RecordStats() { + QuicByteCount cwnd = send_algorithm_->GetCongestionWindow(); + max_cwnd_ = max(max_cwnd_, cwnd); + min_cwnd_ = min(min_cwnd_, cwnd); + if (last_cwnd_ > cwnd) { + max_cwnd_drop_ = max(max_cwnd_drop_, last_cwnd_ - cwnd); + } + last_cwnd_ = cwnd; +} + +// Advance the time by |delta| without sending anything. +void SendAlgorithmSimulator::AdvanceTime(QuicTime::Delta delta) { + clock_->AdvanceTime(delta); +} + +// Elapsed time from the start of the connection. +QuicTime SendAlgorithmSimulator::ElapsedTime() { + return clock_->Now(); +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/send_algorithm_simulator.h b/chromium/net/quic/congestion_control/send_algorithm_simulator.h new file mode 100644 index 00000000000..691b0c82dbd --- /dev/null +++ b/chromium/net/quic/congestion_control/send_algorithm_simulator.h @@ -0,0 +1,133 @@ +// 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. + +// A test only class to enable simulations of send algorithms. + +#ifndef NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_SIMULATOR_H_ +#define NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_SIMULATOR_H_ + +#include <algorithm> + +#include "base/basictypes.h" +#include "net/quic/congestion_control/send_algorithm_interface.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_time.h" +#include "net/quic/test_tools/mock_clock.h" +#include "net/quic/test_tools/quic_test_utils.h" + +namespace net { + +class SendAlgorithmSimulator { + public: + struct SentPacket { + SentPacket(QuicPacketSequenceNumber sequence_number, + QuicTime send_time, + QuicTime ack_time) + : sequence_number(sequence_number), + send_time(send_time), + ack_time(ack_time) {} + QuicPacketSequenceNumber sequence_number; + QuicTime send_time; + QuicTime ack_time; + }; + + // |rtt_stats| should be the same RttStats used by the |send_algorithm|. + SendAlgorithmSimulator(SendAlgorithmInterface* send_algorithm, + MockClock* clock_, + RttStats* rtt_stats, + QuicBandwidth bandwidth, + QuicTime::Delta rtt); + ~SendAlgorithmSimulator(); + + void set_forward_loss_rate(float loss_rate) { + DCHECK_LT(loss_rate, 1.0f); + forward_loss_rate_ = loss_rate; + } + + void set_reverse_loss_rate(float loss_rate) { + DCHECK_LT(loss_rate, 1.0f); + reverse_loss_rate_ = loss_rate; + } + + void set_loss_correlation(float loss_correlation) { + DCHECK_LT(loss_correlation, 1.0f); + loss_correlation_ = loss_correlation; + } + + void set_buffer_size(size_t buffer_size_bytes) { + buffer_size_ = buffer_size_bytes; + } + + // Sends the specified number of bytes as quickly as possible and returns the + // average bandwidth in bytes per second. The time elapsed is based on + // waiting for all acks to arrive. + QuicBandwidth SendBytes(size_t num_bytes); + + const RttStats* rtt_stats() const { return rtt_stats_; } + + QuicByteCount max_cwnd() const { return max_cwnd_; } + QuicByteCount min_cwnd() const { return min_cwnd_; } + QuicByteCount max_cwnd_drop() const { return max_cwnd_drop_; } + QuicByteCount last_cwnd() const { return last_cwnd_; } + + private: + // NextAckTime takes into account packet loss in both forward and reverse + // direction, as well as delayed ack behavior. + QuicTime::Delta NextAckDelta(); + + // Whether all packets in sent_packets_ are lost. + bool AllPacketsLost(); + + // Sets the next acked. + void FindNextAcked(); + + // 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. + int HandlePendingAck(); + + void SendDataNow(); + void RecordStats(); + + // Advance the time by |delta| without sending anything. + void AdvanceTime(QuicTime::Delta delta); + + // Elapsed time from the start of the connection. + QuicTime ElapsedTime(); + + SendAlgorithmInterface* send_algorithm_; + MockClock* clock_; + RttStats* rtt_stats_; + // Next packet sequence number to send. + QuicPacketSequenceNumber next_sent_; + // Last packet sequence number acked. + QuicPacketSequenceNumber last_acked_; + // Packet sequence number to ack up to. + QuicPacketSequenceNumber next_acked_; + // Whether the next ack should be lost. + bool lose_next_ack_; + QuicByteCount bytes_in_flight_; + // The times acks are expected, assuming acks are not lost and every packet + // is acked. + std::list<SentPacket> sent_packets_; + + test::SimpleRandom simple_random_; + float forward_loss_rate_; // Loss rate on the forward path. + float reverse_loss_rate_; // Loss rate on the reverse path. + float loss_correlation_; // Likelihood the subsequent packet is lost. + QuicBandwidth bandwidth_; + QuicTime::Delta rtt_; + size_t buffer_size_; // In bytes. + + // Stats collected for understanding the congestion control. + QuicByteCount max_cwnd_; + QuicByteCount min_cwnd_; + QuicByteCount max_cwnd_drop_; + QuicByteCount last_cwnd_; + + DISALLOW_COPY_AND_ASSIGN(SendAlgorithmSimulator); +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_SIMULATOR_H_ diff --git a/chromium/net/quic/congestion_control/tcp_cubic_sender.cc b/chromium/net/quic/congestion_control/tcp_cubic_sender.cc index 50fc3937b99..19f07d40e55 100644 --- a/chromium/net/quic/congestion_control/tcp_cubic_sender.cc +++ b/chromium/net/quic/congestion_control/tcp_cubic_sender.cc @@ -7,8 +7,10 @@ #include <algorithm> #include "base/metrics/histogram.h" +#include "net/quic/congestion_control/rtt_stats.h" using std::max; +using std::min; namespace net { @@ -17,311 +19,306 @@ namespace { // The minimum cwnd based on RFC 3782 (TCP NewReno) for cwnd reductions on a // fast retransmission. The cwnd after a timeout is still 1. const QuicTcpCongestionWindow kMinimumCongestionWindow = 2; -const int64 kHybridStartLowWindow = 16; const QuicByteCount kMaxSegmentSize = kDefaultTCPMSS; const QuicByteCount kDefaultReceiveWindow = 64000; const int64 kInitialCongestionWindow = 10; const int kMaxBurstLength = 3; -// Constants used for RTT calculation. -const int kInitialRttMs = 60; // At a typical RTT 60 ms. -const float kAlpha = 0.125f; -const float kOneMinusAlpha = (1 - kAlpha); -const float kBeta = 0.25f; -const float kOneMinusBeta = (1 - kBeta); }; // namespace TcpCubicSender::TcpCubicSender( const QuicClock* clock, + const RttStats* rtt_stats, bool reno, - QuicTcpCongestionWindow max_tcp_congestion_window) + QuicTcpCongestionWindow max_tcp_congestion_window, + QuicConnectionStats* stats) : hybrid_slow_start_(clock), - cubic_(clock), + cubic_(clock, stats), + rtt_stats_(rtt_stats), + stats_(stats), reno_(reno), congestion_window_count_(0), receive_window_(kDefaultReceiveWindow), - last_received_accumulated_number_of_lost_packets_(0), - bytes_in_flight_(0), - update_end_sequence_number_(true), - end_sequence_number_(0), + prr_out_(0), + prr_delivered_(0), + ack_count_since_loss_(0), + bytes_in_flight_before_loss_(0), largest_sent_sequence_number_(0), largest_acked_sequence_number_(0), largest_sent_at_last_cutback_(0), congestion_window_(kInitialCongestionWindow), slowstart_threshold_(max_tcp_congestion_window), - max_tcp_congestion_window_(max_tcp_congestion_window), - delay_min_(QuicTime::Delta::Zero()), - smoothed_rtt_(QuicTime::Delta::Zero()), - mean_deviation_(QuicTime::Delta::Zero()) { + last_cutback_exited_slowstart_(false), + max_tcp_congestion_window_(max_tcp_congestion_window) { } TcpCubicSender::~TcpCubicSender() { UMA_HISTOGRAM_COUNTS("Net.QuicSession.FinalTcpCwnd", congestion_window_); } -void TcpCubicSender::SetMaxPacketSize(QuicByteCount /*max_packet_size*/) { +bool TcpCubicSender::InSlowStart() const { + return congestion_window_ < slowstart_threshold_; } void TcpCubicSender::SetFromConfig(const QuicConfig& config, bool is_server) { - if (is_server) { + if (is_server && config.HasReceivedInitialCongestionWindow()) { // Set the initial window size. - congestion_window_ = config.server_initial_congestion_window(); + congestion_window_ = min(kMaxInitialWindow, + config.ReceivedInitialCongestionWindow()); } } void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, - QuicTime feedback_receive_time, - const SentPacketsMap& /*sent_packets*/) { - if (last_received_accumulated_number_of_lost_packets_ != - feedback.tcp.accumulated_number_of_lost_packets) { - int recovered_lost_packets = - last_received_accumulated_number_of_lost_packets_ - - feedback.tcp.accumulated_number_of_lost_packets; - last_received_accumulated_number_of_lost_packets_ = - feedback.tcp.accumulated_number_of_lost_packets; - if (recovered_lost_packets > 0) { - // Assume the loss could be as late as the last acked packet. - OnPacketLost(largest_acked_sequence_number_, feedback_receive_time); - } - } + QuicTime feedback_receive_time) { receive_window_ = feedback.tcp.receive_window; } +void TcpCubicSender::OnCongestionEvent( + bool rtt_updated, + QuicByteCount bytes_in_flight, + const CongestionMap& acked_packets, + const CongestionMap& lost_packets) { + if (rtt_updated && InSlowStart() && + hybrid_slow_start_.ShouldExitSlowStart(rtt_stats_->latest_rtt(), + rtt_stats_->min_rtt(), + congestion_window_)) { + slowstart_threshold_ = congestion_window_; + } + for (CongestionMap::const_iterator it = lost_packets.begin(); + it != lost_packets.end(); ++it) { + OnPacketLost(it->first, bytes_in_flight); + } + for (CongestionMap::const_iterator it = acked_packets.begin(); + it != acked_packets.end(); ++it) { + OnPacketAcked(it->first, it->second.bytes_sent, bytes_in_flight); + } +} + void TcpCubicSender::OnPacketAcked( QuicPacketSequenceNumber acked_sequence_number, QuicByteCount acked_bytes, - QuicTime::Delta rtt) { - DCHECK_GE(bytes_in_flight_, acked_bytes); - bytes_in_flight_ -= acked_bytes; + QuicByteCount bytes_in_flight) { largest_acked_sequence_number_ = max(acked_sequence_number, largest_acked_sequence_number_); - CongestionAvoidance(acked_sequence_number); - AckAccounting(rtt); - if (end_sequence_number_ == acked_sequence_number) { - DVLOG(1) << "Start update end sequence number @" << acked_sequence_number; - update_end_sequence_number_ = true; + if (InRecovery()) { + PrrOnPacketAcked(acked_bytes); + return; } + MaybeIncreaseCwnd(acked_sequence_number, bytes_in_flight); + // TODO(ianswett): Should this even be called when not in slow start? + hybrid_slow_start_.OnPacketAcked(acked_sequence_number, InSlowStart()); } void TcpCubicSender::OnPacketLost(QuicPacketSequenceNumber sequence_number, - QuicTime /*ack_receive_time*/) { + QuicByteCount bytes_in_flight) { // TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets // already sent should be treated as a single loss event, since it's expected. if (sequence_number <= largest_sent_at_last_cutback_) { + if (last_cutback_exited_slowstart_) { + ++stats_->slowstart_packets_lost; + } DVLOG(1) << "Ignoring loss for largest_missing:" << sequence_number - << " because it was sent prior to the last CWND cutback."; + << " because it was sent prior to the last CWND cutback."; return; } + ++stats_->tcp_loss_events; + last_cutback_exited_slowstart_ = InSlowStart(); + if (InSlowStart()) { + ++stats_->slowstart_packets_lost; + } + PrrOnPacketLost(bytes_in_flight); - // In a normal TCP we would need to know the lowest missing packet to detect - // if we receive 3 missing packets. Here we get a missing packet for which we - // enter TCP Fast Retransmit immediately. if (reno_) { congestion_window_ = congestion_window_ >> 1; - slowstart_threshold_ = congestion_window_; } else { congestion_window_ = cubic_.CongestionWindowAfterPacketLoss(congestion_window_); - slowstart_threshold_ = congestion_window_; } - // Enforce TCP's minimimum congestion window of 2*MSS. + slowstart_threshold_ = congestion_window_; + // Enforce TCP's minimum congestion window of 2*MSS. if (congestion_window_ < kMinimumCongestionWindow) { congestion_window_ = kMinimumCongestionWindow; } largest_sent_at_last_cutback_ = largest_sent_sequence_number_; - DVLOG(1) << "Incoming loss; congestion window:" << congestion_window_; + // reset packet count from congestion avoidance mode. We start + // counting again when we're out of recovery. + congestion_window_count_ = 0; + DVLOG(1) << "Incoming loss; congestion window: " << congestion_window_ + << " slowstart threshold: " << slowstart_threshold_; } bool TcpCubicSender::OnPacketSent(QuicTime /*sent_time*/, + QuicByteCount /*bytes_in_flight*/, QuicPacketSequenceNumber sequence_number, QuicByteCount bytes, - TransmissionType transmission_type, HasRetransmittableData is_retransmittable) { // Only update bytes_in_flight_ for data packets. if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) { return false; } - bytes_in_flight_ += bytes; + prr_out_ += bytes; if (largest_sent_sequence_number_ < sequence_number) { // TODO(rch): Ensure that packets are really sent in order. // DCHECK_LT(largest_sent_sequence_number_, sequence_number); largest_sent_sequence_number_ = sequence_number; } - if (transmission_type == NOT_RETRANSMISSION && update_end_sequence_number_) { - end_sequence_number_ = sequence_number; - if (AvailableSendWindow() == 0) { - update_end_sequence_number_ = false; - DVLOG(1) << "Stop update end sequence number @" << sequence_number; - } - } + hybrid_slow_start_.OnPacketSent(sequence_number); return true; } -void TcpCubicSender::OnPacketAbandoned(QuicPacketSequenceNumber sequence_number, - QuicByteCount abandoned_bytes) { - DCHECK_GE(bytes_in_flight_, abandoned_bytes); - bytes_in_flight_ -= abandoned_bytes; -} - QuicTime::Delta TcpCubicSender::TimeUntilSend( QuicTime /* now */, - TransmissionType transmission_type, - HasRetransmittableData has_retransmittable_data, - IsHandshake handshake) { - if (transmission_type == NACK_RETRANSMISSION || - has_retransmittable_data == NO_RETRANSMITTABLE_DATA || - handshake == IS_HANDSHAKE) { + QuicByteCount bytes_in_flight, + HasRetransmittableData has_retransmittable_data) const { + if (has_retransmittable_data == NO_RETRANSMITTABLE_DATA) { // For TCP we can always send an ACK immediately. - // We also immediately send any handshake packet (CHLO, etc.). We provide - // this special dispensation for handshake messages in QUIC, although the - // concept is not present in TCP. return QuicTime::Delta::Zero(); } - if (AvailableSendWindow() == 0) { - return QuicTime::Delta::Infinite(); + if (InRecovery()) { + return PrrTimeUntilSend(bytes_in_flight); } - return QuicTime::Delta::Zero(); -} - -QuicByteCount TcpCubicSender::AvailableSendWindow() { - if (bytes_in_flight_ > SendWindow()) { - return 0; + if (SendWindow() > bytes_in_flight) { + return QuicTime::Delta::Zero(); } - return SendWindow() - bytes_in_flight_; + return QuicTime::Delta::Infinite(); } -QuicByteCount TcpCubicSender::SendWindow() { +QuicByteCount TcpCubicSender::SendWindow() const { // What's the current send window in bytes. - return std::min(receive_window_, GetCongestionWindow()); + return min(receive_window_, GetCongestionWindow()); } QuicBandwidth TcpCubicSender::BandwidthEstimate() const { return QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(), - SmoothedRtt()); -} - -QuicTime::Delta TcpCubicSender::SmoothedRtt() const { - if (smoothed_rtt_.IsZero()) { - return QuicTime::Delta::FromMilliseconds(kInitialRttMs); - } - return smoothed_rtt_; + rtt_stats_->SmoothedRtt()); } QuicTime::Delta TcpCubicSender::RetransmissionDelay() const { + if (!rtt_stats_->HasUpdates()) { + return QuicTime::Delta::Zero(); + } return QuicTime::Delta::FromMicroseconds( - smoothed_rtt_.ToMicroseconds() + 4 * mean_deviation_.ToMicroseconds()); + rtt_stats_->SmoothedRtt().ToMicroseconds() + + 4 * rtt_stats_->mean_deviation().ToMicroseconds()); } QuicByteCount TcpCubicSender::GetCongestionWindow() const { return congestion_window_ * kMaxSegmentSize; } -void TcpCubicSender::Reset() { - delay_min_ = QuicTime::Delta::Zero(); - hybrid_slow_start_.Restart(); -} - -bool TcpCubicSender::IsCwndLimited() const { +bool TcpCubicSender::IsCwndLimited(QuicByteCount bytes_in_flight) const { const QuicByteCount congestion_window_bytes = congestion_window_ * kMaxSegmentSize; - if (bytes_in_flight_ >= congestion_window_bytes) { + if (bytes_in_flight >= congestion_window_bytes) { return true; } - const QuicByteCount tcp_max_burst = kMaxBurstLength * kMaxSegmentSize; - const QuicByteCount left = congestion_window_bytes - bytes_in_flight_; - return left <= tcp_max_burst; + const QuicByteCount max_burst = kMaxBurstLength * kMaxSegmentSize; + const QuicByteCount available_bytes = + congestion_window_bytes - bytes_in_flight; + const bool slow_start_limited = InSlowStart() && + bytes_in_flight > congestion_window_bytes / 2; + return slow_start_limited || available_bytes <= max_burst; +} + +bool TcpCubicSender::InRecovery() const { + return largest_acked_sequence_number_ <= largest_sent_at_last_cutback_ && + largest_acked_sequence_number_ != 0; } // Called when we receive an ack. Normal TCP tracks how many packets one ack // represents, but quic has a separate ack for each packet. -void TcpCubicSender::CongestionAvoidance(QuicPacketSequenceNumber ack) { - if (!IsCwndLimited()) { +void TcpCubicSender::MaybeIncreaseCwnd( + QuicPacketSequenceNumber acked_sequence_number, + QuicByteCount bytes_in_flight) { + LOG_IF(DFATAL, InRecovery()) << "Never increase the CWND during recovery."; + if (!IsCwndLimited(bytes_in_flight)) { // We don't update the congestion window unless we are close to using the // window we have available. return; } - if (congestion_window_ < slowstart_threshold_) { - // Slow start. - if (hybrid_slow_start_.EndOfRound(ack)) { - hybrid_slow_start_.Reset(end_sequence_number_); - } + if (InSlowStart()) { // congestion_window_cnt is the number of acks since last change of snd_cwnd if (congestion_window_ < max_tcp_congestion_window_) { // TCP slow start, exponential growth, increase by one for each ACK. - congestion_window_++; + ++congestion_window_; } - DVLOG(1) << "Slow start; congestion window:" << congestion_window_; - } else { - if (congestion_window_ < max_tcp_congestion_window_) { - if (reno_) { - // Classic Reno congestion avoidance provided for testing. - if (congestion_window_count_ >= congestion_window_) { - congestion_window_++; - congestion_window_count_ = 0; - } else { - congestion_window_count_++; - } - DVLOG(1) << "Reno; congestion window:" << congestion_window_; - } else { - congestion_window_ = std::min( - max_tcp_congestion_window_, - cubic_.CongestionWindowAfterAck(congestion_window_, delay_min_)); - DVLOG(1) << "Cubic; congestion window:" << congestion_window_; - } + DVLOG(1) << "Slow start; congestion window: " << congestion_window_ + << " slowstart threshold: " << slowstart_threshold_; + return; + } + if (congestion_window_ >= max_tcp_congestion_window_) { + return; + } + // Congestion avoidance + if (reno_) { + // Classic Reno congestion avoidance provided for testing. + + ++congestion_window_count_; + if (congestion_window_count_ >= congestion_window_) { + ++congestion_window_; + congestion_window_count_ = 0; } + + DVLOG(1) << "Reno; congestion window: " << congestion_window_ + << " slowstart threshold: " << slowstart_threshold_ + << " congestion window count: " << congestion_window_count_; + } else { + congestion_window_ = min(max_tcp_congestion_window_, + cubic_.CongestionWindowAfterAck( + congestion_window_, rtt_stats_->min_rtt())); + DVLOG(1) << "Cubic; congestion window: " << congestion_window_ + << " slowstart threshold: " << slowstart_threshold_; } } -void TcpCubicSender::OnRetransmissionTimeout() { - cubic_.Reset(); - congestion_window_ = kMinimumCongestionWindow; +void TcpCubicSender::OnRetransmissionTimeout(bool packets_retransmitted) { + largest_sent_at_last_cutback_ = 0; + if (packets_retransmitted) { + cubic_.Reset(); + hybrid_slow_start_.Restart(); + congestion_window_ = kMinimumCongestionWindow; + } } -void TcpCubicSender::AckAccounting(QuicTime::Delta rtt) { - if (rtt.IsInfinite() || rtt.IsZero()) { - DVLOG(1) << "Ignoring rtt, because it's " - << (rtt.IsZero() ? "Zero" : "Infinite"); - return; - } - // RTT can't be negative. - DCHECK_LT(0, rtt.ToMicroseconds()); +void TcpCubicSender::PrrOnPacketLost(QuicByteCount bytes_in_flight) { + prr_out_ = 0; + bytes_in_flight_before_loss_ = bytes_in_flight; + prr_delivered_ = 0; + ack_count_since_loss_ = 0; +} - // TODO(pwestin): Discard delay samples right after fast recovery, - // during 1 second?. +void TcpCubicSender::PrrOnPacketAcked(QuicByteCount acked_bytes) { + prr_delivered_ += acked_bytes; + ++ack_count_since_loss_; +} - // First time call or link delay decreases. - if (delay_min_.IsZero() || delay_min_ > rtt) { - delay_min_ = rtt; - } - // First time call. - if (smoothed_rtt_.IsZero()) { - smoothed_rtt_ = rtt; - mean_deviation_ = QuicTime::Delta::FromMicroseconds( - rtt.ToMicroseconds() / 2); - } else { - mean_deviation_ = QuicTime::Delta::FromMicroseconds( - kOneMinusBeta * mean_deviation_.ToMicroseconds() + - kBeta * abs(smoothed_rtt_.ToMicroseconds() - rtt.ToMicroseconds())); - smoothed_rtt_ = QuicTime::Delta::FromMicroseconds( - kOneMinusAlpha * smoothed_rtt_.ToMicroseconds() + - kAlpha * rtt.ToMicroseconds()); - DVLOG(1) << "Cubic; smoothed_rtt_:" << smoothed_rtt_.ToMicroseconds() - << " mean_deviation_:" << mean_deviation_.ToMicroseconds(); +QuicTime::Delta TcpCubicSender::PrrTimeUntilSend( + QuicByteCount bytes_in_flight) const { + DCHECK(InRecovery()); + // Return QuicTime::Zero In order to ensure limited transmit always works. + if (prr_out_ == 0) { + return QuicTime::Delta::Zero(); } - - // Hybrid start triggers when cwnd is larger than some threshold. - if (congestion_window_ <= slowstart_threshold_ && - congestion_window_ >= kHybridStartLowWindow) { - if (!hybrid_slow_start_.started()) { - // Time to start the hybrid slow start. - hybrid_slow_start_.Reset(end_sequence_number_); - } - hybrid_slow_start_.Update(rtt, delay_min_); - if (hybrid_slow_start_.Exit()) { - slowstart_threshold_ = congestion_window_; + if (SendWindow() > bytes_in_flight) { + // During PRR-SSRB, limit outgoing packets to 1 extra MSS per ack, instead + // of sending the entire available window. This prevents burst retransmits + // when more packets are lost than the CWND reduction. + // limit = MAX(prr_delivered - prr_out, DeliveredData) + MSS + if (prr_delivered_ + ack_count_since_loss_ * kMaxSegmentSize <= prr_out_) { + return QuicTime::Delta::Infinite(); } + return QuicTime::Delta::Zero(); + } + // Implement Proportional Rate Reduction (RFC6937) + // Checks a simplified version of the PRR formula that doesn't use division: + // AvailableSendWindow = + // CEIL(prr_delivered * ssthresh / BytesInFlightAtLoss) - prr_sent + if (prr_delivered_ * slowstart_threshold_ * kMaxSegmentSize > + prr_out_ * bytes_in_flight_before_loss_) { + return QuicTime::Delta::Zero(); } + return QuicTime::Delta::Infinite(); } } // namespace net diff --git a/chromium/net/quic/congestion_control/tcp_cubic_sender.h b/chromium/net/quic/congestion_control/tcp_cubic_sender.h index f026fd56b3f..f9234c92dcc 100644 --- a/chromium/net/quic/congestion_control/tcp_cubic_sender.h +++ b/chromium/net/quic/congestion_control/tcp_cubic_sender.h @@ -15,13 +15,13 @@ #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_connection_stats.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_time.h" namespace net { -// Default maximum packet size used in Linux TCP implementations. -const QuicByteCount kDefaultTCPMSS = 1460; +class RttStats; namespace test { class TcpCubicSenderPeer; @@ -31,37 +31,34 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface { public: // Reno option and max_tcp_congestion_window are provided for testing. TcpCubicSender(const QuicClock* clock, + const RttStats* rtt_stats, bool reno, - QuicTcpCongestionWindow max_tcp_congestion_window); + QuicTcpCongestionWindow max_tcp_congestion_window, + QuicConnectionStats* stats); virtual ~TcpCubicSender(); + bool InSlowStart() const; + // Start implementation of SendAlgorithmInterface. virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE; - virtual void SetMaxPacketSize(QuicByteCount max_packet_size) OVERRIDE; virtual void OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, - QuicTime feedback_receive_time, - const SentPacketsMap& sent_packets) OVERRIDE; - virtual void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number, - QuicByteCount acked_bytes, - QuicTime::Delta rtt) OVERRIDE; - virtual void OnPacketLost(QuicPacketSequenceNumber largest_loss, - QuicTime ack_receive_time) OVERRIDE; + QuicTime feedback_receive_time) OVERRIDE; + virtual void OnCongestionEvent(bool rtt_updated, + QuicByteCount bytes_in_flight, + const CongestionMap& acked_packets, + const CongestionMap& lost_packets) OVERRIDE; virtual bool OnPacketSent(QuicTime sent_time, + QuicByteCount bytes_in_flight, QuicPacketSequenceNumber sequence_number, QuicByteCount bytes, - TransmissionType transmission_type, HasRetransmittableData is_retransmittable) OVERRIDE; - virtual void OnRetransmissionTimeout() OVERRIDE; - virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number, - QuicByteCount abandoned_bytes) OVERRIDE; + virtual void OnRetransmissionTimeout(bool packets_retransmitted) OVERRIDE; virtual QuicTime::Delta TimeUntilSend( QuicTime now, - TransmissionType transmission_type, - HasRetransmittableData has_retransmittable_data, - IsHandshake handshake) OVERRIDE; + QuicByteCount bytes_in_flight, + HasRetransmittableData has_retransmittable_data) const OVERRIDE; virtual QuicBandwidth BandwidthEstimate() const OVERRIDE; - virtual QuicTime::Delta SmoothedRtt() const OVERRIDE; virtual QuicTime::Delta RetransmissionDelay() const OVERRIDE; virtual QuicByteCount GetCongestionWindow() const OVERRIDE; // End implementation of SendAlgorithmInterface. @@ -69,16 +66,28 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface { private: friend class test::TcpCubicSenderPeer; - QuicByteCount AvailableSendWindow(); - QuicByteCount SendWindow(); - void Reset(); - void AckAccounting(QuicTime::Delta rtt); - void CongestionAvoidance(QuicPacketSequenceNumber ack); - bool IsCwndLimited() const; - void OnTimeOut(); + // TODO(ianswett): Remove these and migrate to OnCongestionEvent. + void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number, + QuicByteCount acked_bytes, + QuicByteCount bytes_in_flight); + void OnPacketLost(QuicPacketSequenceNumber largest_loss, + QuicByteCount bytes_in_flight); + + QuicByteCount SendWindow() const; + void MaybeIncreaseCwnd(QuicPacketSequenceNumber acked_sequence_number, + QuicByteCount bytes_in_flight); + bool IsCwndLimited(QuicByteCount bytes_in_flight) const; + bool InRecovery() const; + // Methods for isolating PRR from the rest of TCP Cubic. + void PrrOnPacketLost(QuicByteCount bytes_in_flight); + void PrrOnPacketAcked(QuicByteCount acked_bytes); + QuicTime::Delta PrrTimeUntilSend(QuicByteCount bytes_in_flight) const; + HybridSlowStart hybrid_slow_start_; Cubic cubic_; + const RttStats* rtt_stats_; + QuicConnectionStats* stats_; // Reno provided for testing. const bool reno_; @@ -89,15 +98,13 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface { // Receiver side advertised window. QuicByteCount receive_window_; - // Receiver side advertised packet loss. - int last_received_accumulated_number_of_lost_packets_; - - // Bytes in flight, aka bytes on the wire. - QuicByteCount bytes_in_flight_; + // Bytes sent and acked since the last loss event. Used for PRR. + QuicByteCount prr_out_; + QuicByteCount prr_delivered_; + size_t ack_count_since_loss_; - // We need to keep track of the end sequence number of each RTT "burst". - bool update_end_sequence_number_; - QuicPacketSequenceNumber end_sequence_number_; + // The congestion window before the last loss event. + QuicByteCount bytes_in_flight_before_loss_; // Track the largest packet that has been sent. QuicPacketSequenceNumber largest_sent_sequence_number_; @@ -111,23 +118,16 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface { // Congestion window in packets. QuicTcpCongestionWindow congestion_window_; - // Slow start congestion window in packets. + // Slow start congestion window in packets, aka ssthresh. QuicTcpCongestionWindow slowstart_threshold_; + // Whether the last loss event caused us to exit slowstart. + // Used for stats collection of slowstart_packets_lost + bool last_cutback_exited_slowstart_; + // Maximum number of outstanding packets for tcp. QuicTcpCongestionWindow max_tcp_congestion_window_; - // Min RTT during this session. - QuicTime::Delta delay_min_; - - // Smoothed RTT during this session. - QuicTime::Delta smoothed_rtt_; - - // Mean RTT deviation during this session. - // Approximation of standard deviation, the error is roughly 1.25 times - // larger than the standard deviation, for a normally distributed signal. - QuicTime::Delta mean_deviation_; - DISALLOW_COPY_AND_ASSIGN(TcpCubicSender); }; diff --git a/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc b/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc index 20dd6a6d316..c5d991a2a30 100644 --- a/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc +++ b/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc @@ -2,14 +2,20 @@ // 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/tcp_cubic_sender.h" +#include <algorithm> #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#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/quic_utils.h" #include "net/quic/test_tools/mock_clock.h" +#include "net/quic/test_tools/quic_config_peer.h" #include "testing/gtest/include/gtest/gtest.h" +using std::min; + namespace net { namespace test { @@ -23,107 +29,176 @@ class TcpCubicSenderPeer : public TcpCubicSender { TcpCubicSenderPeer(const QuicClock* clock, bool reno, QuicTcpCongestionWindow max_tcp_congestion_window) - : TcpCubicSender(clock, reno, max_tcp_congestion_window) { + : TcpCubicSender( + clock, &rtt_stats_, reno, max_tcp_congestion_window, &stats_) { } QuicTcpCongestionWindow congestion_window() { return congestion_window_; } - using TcpCubicSender::AvailableSendWindow; + const HybridSlowStart& hybrid_slow_start() const { + return hybrid_slow_start_; + } + + RttStats rtt_stats_; + QuicConnectionStats stats_; + using TcpCubicSender::SendWindow; - using TcpCubicSender::AckAccounting; }; class TcpCubicSenderTest : public ::testing::Test { protected: TcpCubicSenderTest() - : rtt_(QuicTime::Delta::FromMilliseconds(60)), - one_ms_(QuicTime::Delta::FromMilliseconds(1)), + : one_ms_(QuicTime::Delta::FromMilliseconds(1)), sender_(new TcpCubicSenderPeer(&clock_, true, kDefaultMaxCongestionWindowTCP)), receiver_(new TcpReceiver()), sequence_number_(1), - acked_sequence_number_(0) { + acked_sequence_number_(0), + bytes_in_flight_(0) { + standard_packet_.bytes_sent = kDefaultTCPMSS; } - void SendAvailableSendWindow() { - QuicByteCount bytes_to_send = sender_->AvailableSendWindow(); - while (bytes_to_send > 0) { - QuicByteCount bytes_in_packet = std::min(kDefaultTCPMSS, bytes_to_send); - sender_->OnPacketSent(clock_.Now(), sequence_number_++, bytes_in_packet, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); - bytes_to_send -= bytes_in_packet; - if (bytes_to_send > 0) { - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - } + int SendAvailableSendWindow() { + // Send as long as TimeUntilSend returns Zero. + int packets_sent = 0; + bool can_send = sender_->TimeUntilSend( + clock_.Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA).IsZero(); + while (can_send) { + sender_->OnPacketSent(clock_.Now(), bytes_in_flight_, sequence_number_++, + kDefaultTCPMSS, HAS_RETRANSMITTABLE_DATA); + ++packets_sent; + bytes_in_flight_ += kDefaultTCPMSS; + can_send = sender_->TimeUntilSend( + clock_.Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA).IsZero(); } + return packets_sent; } + // Normal is that TCP acks every other segment. void AckNPackets(int n) { + sender_->rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(60), + QuicTime::Delta::Zero(), + clock_.Now()); + SendAlgorithmInterface::CongestionMap acked_packets; + SendAlgorithmInterface::CongestionMap lost_packets; + for (int i = 0; i < n; ++i) { + ++acked_sequence_number_; + acked_packets[acked_sequence_number_] = standard_packet_; + } + sender_->OnCongestionEvent( + true, bytes_in_flight_, acked_packets, lost_packets); + bytes_in_flight_ -= n * kDefaultTCPMSS; + clock_.AdvanceTime(one_ms_); + } + + void LoseNPackets(int n) { + SendAlgorithmInterface::CongestionMap acked_packets; + SendAlgorithmInterface::CongestionMap lost_packets; for (int i = 0; i < n; ++i) { - acked_sequence_number_++; - sender_->OnPacketAcked(acked_sequence_number_, kDefaultTCPMSS, rtt_); + ++acked_sequence_number_; + lost_packets[acked_sequence_number_] = standard_packet_; } - clock_.AdvanceTime(one_ms_); // 1 millisecond. + sender_->OnCongestionEvent( + false, bytes_in_flight_, acked_packets, lost_packets); + bytes_in_flight_ -= n * kDefaultTCPMSS; + } + + // Does not increment acked_sequence_number_. + void LosePacket(QuicPacketSequenceNumber sequence_number) { + SendAlgorithmInterface::CongestionMap acked_packets; + SendAlgorithmInterface::CongestionMap lost_packets; + lost_packets[sequence_number] = standard_packet_; + sender_->OnCongestionEvent( + false, bytes_in_flight_, acked_packets, lost_packets); + bytes_in_flight_ -= kDefaultTCPMSS; } - const QuicTime::Delta rtt_; const QuicTime::Delta one_ms_; MockClock clock_; - SendAlgorithmInterface::SentPacketsMap not_used_; scoped_ptr<TcpCubicSenderPeer> sender_; scoped_ptr<TcpReceiver> receiver_; QuicPacketSequenceNumber sequence_number_; QuicPacketSequenceNumber acked_sequence_number_; + QuicByteCount bytes_in_flight_; + TransmissionInfo standard_packet_; }; TEST_F(TcpCubicSenderTest, SimpleSender) { QuicCongestionFeedbackFrame feedback; // At startup make sure we are at the default. - EXPECT_EQ(kDefaultWindowTCP, sender_->AvailableSendWindow()); EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); // At startup make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + 0, + HAS_RETRANSMITTABLE_DATA).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), - not_used_); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); // Make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + 0, + HAS_RETRANSMITTABLE_DATA).IsZero()); // And that window is un-affected. - EXPECT_EQ(kDefaultWindowTCP, sender_->AvailableSendWindow()); EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); - // A retransmit should always return 0. + // Fill the send window with data, then verify that we can't send. + SendAvailableSendWindow(); + EXPECT_FALSE(sender_->TimeUntilSend(clock_.Now(), + sender_->GetCongestionWindow(), + HAS_RETRANSMITTABLE_DATA).IsZero()); +} + +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(), - NACK_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + 0, + HAS_RETRANSMITTABLE_DATA).IsZero()); + + SendAvailableSendWindow(); + for (int i = 0; i < kNumberOfAcks; ++i) { + AckNPackets(2); + } + QuicByteCount bytes_to_send = sender_->SendWindow(); + // It's expected 2 acks will arrive when the bytes_in_flight are greater than + // half the CWND. + EXPECT_EQ(kDefaultWindowTCP + kDefaultTCPMSS * 2 * 2, + bytes_to_send); } TEST_F(TcpCubicSenderTest, ExponentialSlowStart) { - const int kNumberOfAck = 20; + const int kNumberOfAcks = 20; QuicCongestionFeedbackFrame feedback; // At startup make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + 0, + HAS_RETRANSMITTABLE_DATA).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), - not_used_); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); // Make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + 0, + HAS_RETRANSMITTABLE_DATA).IsZero()); - for (int n = 0; n < kNumberOfAck; ++n) { + for (int i = 0; i < kNumberOfAcks; ++i) { // Send our full send window. SendAvailableSendWindow(); AckNPackets(2); } QuicByteCount bytes_to_send = sender_->SendWindow(); - EXPECT_EQ(kDefaultWindowTCP + kDefaultTCPMSS * 2 * kNumberOfAck, + EXPECT_EQ(kDefaultWindowTCP + kDefaultTCPMSS * 2 * kNumberOfAcks, bytes_to_send); } @@ -133,123 +208,243 @@ TEST_F(TcpCubicSenderTest, SlowStartAckTrain) { // Ack2Packets in one round. // 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 kNumberOfAck = 65; + const int kNumberOfAcks = 65; QuicCongestionFeedbackFrame feedback; - // At startup make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), - not_used_); - // Make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - for (int n = 0; n < kNumberOfAck; ++n) { + for (int i = 0; i < kNumberOfAcks; ++i) { // Send our full send window. SendAvailableSendWindow(); AckNPackets(2); } QuicByteCount expected_send_window = - kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAck); - EXPECT_EQ(expected_send_window, sender_->SendWindow()); + kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks); EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); - // We should now have fallen out of slow start. - SendAvailableSendWindow(); - AckNPackets(2); - expected_send_window += kDefaultTCPMSS; - EXPECT_EQ(expected_send_window, sender_->SendWindow()); + // We should now have fallen out of slow start. // Testing Reno phase. - // We should need 141(65*2+1+10) ACK:ed packets before increasing window by + // We should need 140(65*2+10) ACK:ed packets before increasing window by // one. - for (int m = 0; m < 70; ++m) { + for (int i = 0; i < 69; ++i) { SendAvailableSendWindow(); AckNPackets(2); - EXPECT_EQ(expected_send_window, sender_->SendWindow()); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); } SendAvailableSendWindow(); AckNPackets(2); expected_send_window += kDefaultTCPMSS; - EXPECT_EQ(expected_send_window, sender_->SendWindow()); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Now RTO and ensure slow start gets reset. + EXPECT_TRUE(sender_->hybrid_slow_start().started()); + sender_->OnRetransmissionTimeout(true); + EXPECT_FALSE(sender_->hybrid_slow_start().started()); } 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; - // At startup make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), - not_used_); - // Make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - for (int i = 0; i < kNumberOfAck; ++i) { + const int kNumberOfAcks = 10; + for (int i = 0; i < kNumberOfAcks; ++i) { // Send our full send window. SendAvailableSendWindow(); AckNPackets(2); } SendAvailableSendWindow(); QuicByteCount expected_send_window = kDefaultWindowTCP + - (kDefaultTCPMSS * 2 * kNumberOfAck); - EXPECT_EQ(expected_send_window, sender_->SendWindow()); - - sender_->OnPacketLost(acked_sequence_number_ + 1, clock_.Now()); + (kDefaultTCPMSS * 2 * kNumberOfAcks); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); - // Make sure that we should not send right now. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite()); + // Lose a packet to exit slow start. + LoseNPackets(1); // We should now have fallen out of slow start. - // We expect window to be cut in half. + // We expect window to be cut in half by Reno. expected_send_window /= 2; - EXPECT_EQ(expected_send_window, sender_->SendWindow()); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); // Testing Reno phase. // We need to ack half of the pending packet before we can send again. - int number_of_packets_in_window = expected_send_window / kDefaultTCPMSS; + size_t number_of_packets_in_window = expected_send_window / kDefaultTCPMSS; AckNPackets(number_of_packets_in_window); - EXPECT_EQ(expected_send_window, sender_->SendWindow()); - EXPECT_EQ(0u, sender_->AvailableSendWindow()); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // We need to ack every packet in the window before we exit recovery. + for (size_t i = 0; i < number_of_packets_in_window; ++i) { + AckNPackets(1); + SendAvailableSendWindow(); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + } + + // We need to ack another window before we increase CWND by 1. + for (size_t i = 0; i < number_of_packets_in_window - 2; ++i) { + AckNPackets(1); + SendAvailableSendWindow(); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + } AckNPackets(1); expected_send_window += kDefaultTCPMSS; - number_of_packets_in_window++; - EXPECT_EQ(expected_send_window, sender_->SendWindow()); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Now RTO and ensure slow start gets reset. + EXPECT_TRUE(sender_->hybrid_slow_start().started()); + sender_->OnRetransmissionTimeout(true); + EXPECT_FALSE(sender_->hybrid_slow_start().started()); +} + +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()); - // We should need number_of_packets_in_window ACK:ed packets before - // increasing window by one. - for (int k = 0; k < number_of_packets_in_window; ++k) { + // 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) { + // Send our full send window. SendAvailableSendWindow(); - AckNPackets(1); - EXPECT_EQ(expected_send_window, sender_->SendWindow()); + AckNPackets(2); } SendAvailableSendWindow(); + QuicByteCount expected_send_window = kDefaultWindowTCP + + (kDefaultTCPMSS * 2 * kNumberOfAcks); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + LoseNPackets(1); + + // We should now have fallen out of slow start. + // We expect window to be cut in half by Reno. + expected_send_window /= 2; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Testing TCP proportional rate reduction. + // We should send one packet for every two received acks over the remaining + // 18 outstanding packets. + size_t number_of_packets_in_window = expected_send_window / kDefaultTCPMSS; + // The number of packets before we exit recovery is the original CWND minus + // the packet that has been lost and the one which triggered the loss. + size_t remaining_packets_in_recovery = number_of_packets_in_window * 2 - 1; + for (size_t i = 0; i < remaining_packets_in_recovery - 1; i += 2) { + AckNPackets(2); + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA).IsZero()); + EXPECT_EQ(1, SendAvailableSendWindow()); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + } + + // We need to ack another window before we increase CWND by 1. + for (size_t i = 0; i < number_of_packets_in_window; ++i) { + AckNPackets(1); + EXPECT_EQ(1, SendAvailableSendWindow()); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + } + AckNPackets(1); expected_send_window += kDefaultTCPMSS; - EXPECT_EQ(expected_send_window, sender_->SendWindow()); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); +} + +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) { + // Send our full send window. + SendAvailableSendWindow(); + AckNPackets(2); + } + SendAvailableSendWindow(); + QuicByteCount expected_send_window = kDefaultWindowTCP + + (kDefaultTCPMSS * 2 * kNumberOfAcks); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Ack a packet with a 15 packet gap, losing 13 of them due to FACK. + LoseNPackets(13); + // Immediately after the loss, ensure at least one packet can be sent. + // Losses without subsequent acks can occur with timer based loss detection. + EXPECT_TRUE(sender_->TimeUntilSend( + clock_.Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA).IsZero()); + AckNPackets(1); + + // We should now have fallen out of slow start. + // We expect window to be cut in half by Reno. + expected_send_window /= 2; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Only 2 packets should be allowed to be sent, per PRR-SSRB + EXPECT_EQ(2, SendAvailableSendWindow()); + + // Ack the next packet, which triggers another loss. + LoseNPackets(1); + AckNPackets(1); + + // Send 2 packets to simulate PRR-SSRB. + EXPECT_EQ(2, SendAvailableSendWindow()); + + // Ack the next packet, which triggers another loss. + LoseNPackets(1); + AckNPackets(1); + + // Send 2 packets to simulate PRR-SSRB. + EXPECT_EQ(2, SendAvailableSendWindow()); + + AckNPackets(1); + EXPECT_EQ(2, SendAvailableSendWindow()); + + AckNPackets(1); + EXPECT_EQ(2, SendAvailableSendWindow()); + + // The window should not have changed. + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // Exit recovery and return to sending at the new rate. + for (int i = 0; i < kNumberOfAcks; ++i) { + AckNPackets(1); + EXPECT_EQ(1, SendAvailableSendWindow()); + } } TEST_F(TcpCubicSenderTest, RTOCongestionWindow) { EXPECT_EQ(kDefaultWindowTCP, sender_->SendWindow()); // Expect the window to decrease to the minimum once the RTO fires. - sender_->OnRetransmissionTimeout(); + sender_->OnRetransmissionTimeout(true); EXPECT_EQ(2 * kDefaultTCPMSS, sender_->SendWindow()); } +TEST_F(TcpCubicSenderTest, RTOCongestionWindowNoRetransmission) { + EXPECT_EQ(kDefaultWindowTCP, sender_->SendWindow()); + + // Expect the window to remain unchanged if the RTO fires but no + // packets are retransmitted. + sender_->OnRetransmissionTimeout(false); + EXPECT_EQ(kDefaultWindowTCP, sender_->SendWindow()); +} + TEST_F(TcpCubicSenderTest, RetransmissionDelay) { const int64 kRttMs = 10; const int64 kDeviationMs = 3; EXPECT_EQ(QuicTime::Delta::Zero(), sender_->RetransmissionDelay()); - sender_->AckAccounting(QuicTime::Delta::FromMilliseconds(kRttMs)); + sender_->rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(kRttMs), + QuicTime::Delta::Zero(), clock_.Now()); // Initial value is to set the median deviation to half of the initial // rtt, the median in then multiplied by a factor of 4 and finally the @@ -260,75 +455,63 @@ TEST_F(TcpCubicSenderTest, RetransmissionDelay) { for (int i = 0; i < 100; ++i) { // Run to make sure that we converge. - sender_->AckAccounting( - QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs)); - sender_->AckAccounting( - QuicTime::Delta::FromMilliseconds(kRttMs - kDeviationMs)); + sender_->rtt_stats_.UpdateRtt( + QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs), + QuicTime::Delta::Zero(), clock_.Now()); + sender_->rtt_stats_.UpdateRtt( + QuicTime::Delta::FromMilliseconds(kRttMs - kDeviationMs), + QuicTime::Delta::Zero(), clock_.Now()); } expected_delay = QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs * 4); - EXPECT_NEAR(kRttMs, sender_->SmoothedRtt().ToMilliseconds(), 1); + EXPECT_NEAR(kRttMs, sender_->rtt_stats_.SmoothedRtt().ToMilliseconds(), 1); EXPECT_NEAR(expected_delay.ToMilliseconds(), sender_->RetransmissionDelay().ToMilliseconds(), 1); EXPECT_EQ(static_cast<int64>( sender_->GetCongestionWindow() * kNumMicrosPerSecond / - sender_->SmoothedRtt().ToMicroseconds()), + sender_->rtt_stats_.SmoothedRtt().ToMicroseconds()), sender_->BandwidthEstimate().ToBytesPerSecond()); } TEST_F(TcpCubicSenderTest, SlowStartMaxSendWindow) { const QuicTcpCongestionWindow kMaxCongestionWindowTCP = 50; - const int kNumberOfAck = 100; + const int kNumberOfAcks = 100; sender_.reset( new TcpCubicSenderPeer(&clock_, false, kMaxCongestionWindowTCP)); QuicCongestionFeedbackFrame feedback; - // At startup make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), - not_used_); - // Make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - for (int i = 0; i < kNumberOfAck; ++i) { + for (int i = 0; i < kNumberOfAcks; ++i) { // Send our full send window. SendAvailableSendWindow(); AckNPackets(2); } QuicByteCount expected_send_window = kMaxCongestionWindowTCP * kDefaultTCPMSS; - EXPECT_EQ(expected_send_window, sender_->SendWindow()); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); } TEST_F(TcpCubicSenderTest, TcpRenoMaxCongestionWindow) { const QuicTcpCongestionWindow kMaxCongestionWindowTCP = 50; - const int kNumberOfAck = 1000; + const int kNumberOfAcks = 1000; sender_.reset( new TcpCubicSenderPeer(&clock_, true, kMaxCongestionWindowTCP)); QuicCongestionFeedbackFrame feedback; - // At startup make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), - not_used_); - // Make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); SendAvailableSendWindow(); AckNPackets(2); // Make sure we fall out of slow start. - sender_->OnPacketLost(acked_sequence_number_ + 1, clock_.Now()); + LoseNPackets(1); - for (int i = 0; i < kNumberOfAck; ++i) { + for (int i = 0; i < kNumberOfAcks; ++i) { // Send our full send window. SendAvailableSendWindow(); AckNPackets(2); @@ -336,33 +519,28 @@ TEST_F(TcpCubicSenderTest, TcpRenoMaxCongestionWindow) { QuicByteCount expected_send_window = kMaxCongestionWindowTCP * kDefaultTCPMSS; - EXPECT_EQ(expected_send_window, sender_->SendWindow()); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); } TEST_F(TcpCubicSenderTest, TcpCubicMaxCongestionWindow) { const QuicTcpCongestionWindow kMaxCongestionWindowTCP = 50; - const int kNumberOfAck = 1000; + // Set to 10000 to compensate for small cubic alpha. + const int kNumberOfAcks = 10000; + sender_.reset( new TcpCubicSenderPeer(&clock_, false, kMaxCongestionWindowTCP)); QuicCongestionFeedbackFrame feedback; - // At startup make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), - not_used_); - // Make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); SendAvailableSendWindow(); AckNPackets(2); // Make sure we fall out of slow start. - sender_->OnPacketLost(acked_sequence_number_ + 1, clock_.Now()); + LoseNPackets(1); - for (int i = 0; i < kNumberOfAck; ++i) { + for (int i = 0; i < kNumberOfAcks; ++i) { // Send our full send window. SendAvailableSendWindow(); AckNPackets(2); @@ -370,52 +548,93 @@ TEST_F(TcpCubicSenderTest, TcpCubicMaxCongestionWindow) { QuicByteCount expected_send_window = kMaxCongestionWindowTCP * kDefaultTCPMSS; - EXPECT_EQ(expected_send_window, sender_->SendWindow()); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); } TEST_F(TcpCubicSenderTest, MultipleLossesInOneWindow) { SendAvailableSendWindow(); const QuicByteCount initial_window = sender_->GetCongestionWindow(); - sender_->OnPacketLost(acked_sequence_number_ + 1, clock_.Now()); + LosePacket(acked_sequence_number_ + 1); const QuicByteCount post_loss_window = sender_->GetCongestionWindow(); EXPECT_GT(initial_window, post_loss_window); - sender_->OnPacketLost(acked_sequence_number_ + 3, clock_.Now()); + LosePacket(acked_sequence_number_ + 3); EXPECT_EQ(post_loss_window, sender_->GetCongestionWindow()); - sender_->OnPacketLost(sequence_number_ - 1, clock_.Now()); + LosePacket(sequence_number_ - 1); EXPECT_EQ(post_loss_window, sender_->GetCongestionWindow()); // Lose a later packet and ensure the window decreases. - sender_->OnPacketLost(sequence_number_, clock_.Now()); + LosePacket(sequence_number_); EXPECT_GT(post_loss_window, sender_->GetCongestionWindow()); } -TEST_F(TcpCubicSenderTest, SendWindowNotAffectedByAcks) { - QuicByteCount send_window = sender_->AvailableSendWindow(); - - // Send a packet with no retransmittable data, and ensure that the congestion - // window doesn't change. - QuicByteCount bytes_in_packet = std::min(kDefaultTCPMSS, send_window); - sender_->OnPacketSent(clock_.Now(), sequence_number_++, bytes_in_packet, - NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA); - EXPECT_EQ(send_window, sender_->AvailableSendWindow()); - - // Send a data packet with retransmittable data, and ensure that the - // congestion window has shrunk. - sender_->OnPacketSent(clock_.Now(), sequence_number_++, bytes_in_packet, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); - EXPECT_GT(send_window, sender_->AvailableSendWindow()); +TEST_F(TcpCubicSenderTest, DontTrackAckPackets) { + // Send a packet with no retransmittable data, and ensure it's not tracked. + EXPECT_FALSE(sender_->OnPacketSent(clock_.Now(), bytes_in_flight_, + sequence_number_++, kDefaultTCPMSS, + NO_RETRANSMITTABLE_DATA)); + + // Send a data packet with retransmittable data, and ensure it is tracked. + EXPECT_TRUE(sender_->OnPacketSent(clock_.Now(), bytes_in_flight_, + sequence_number_++, kDefaultTCPMSS, + HAS_RETRANSMITTABLE_DATA)); } TEST_F(TcpCubicSenderTest, ConfigureMaxInitialWindow) { QuicTcpCongestionWindow congestion_window = sender_->congestion_window(); QuicConfig config; - config.set_server_initial_congestion_window(2 * congestion_window, - 2 * congestion_window); - EXPECT_EQ(2 * congestion_window, config.server_initial_congestion_window()); + QuicConfigPeer::SetReceivedInitialWindow(&config, 2 * congestion_window); sender_->SetFromConfig(config, true); EXPECT_EQ(2 * 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) { + // Send our full send window. + SendAvailableSendWindow(); + AckNPackets(2); + } + SendAvailableSendWindow(); + QuicByteCount expected_send_window = kDefaultWindowTCP + + (kDefaultTCPMSS * 2 * kNumberOfAcks); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + LoseNPackets(1); + + // We should now have fallen out of slow start, and window should be cut in + // half by Reno. New cwnd should be 10. + expected_send_window /= 2; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + + // No congestion window growth should occur in recovery phase, i.e., + // until the currently outstanding 20 packets are acked. + for (int i = 0; i < 10; ++i) { + // Send our full send window. + SendAvailableSendWindow(); + AckNPackets(2); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + } + + // Out of recovery now. Congestion window should not grow during RTT. + for (int i = 0; i < 4; ++i) { + // Send our full send window. + SendAvailableSendWindow(); + AckNPackets(2); + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); + } + + // Next ack should cause congestion window to grow by 1MSS. + AckNPackets(2); + expected_send_window += kDefaultTCPMSS; + EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow()); +} + } // namespace test } // namespace net diff --git a/chromium/net/quic/congestion_control/tcp_loss_algorithm.cc b/chromium/net/quic/congestion_control/tcp_loss_algorithm.cc new file mode 100644 index 00000000000..8dad3f18429 --- /dev/null +++ b/chromium/net/quic/congestion_control/tcp_loss_algorithm.cc @@ -0,0 +1,79 @@ +// 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/tcp_loss_algorithm.h" + +#include "net/quic/congestion_control/rtt_stats.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +namespace { + +// TCP retransmits after 3 nacks. +static const size_t kNumberOfNacksBeforeRetransmission = 3; + +// How many RTTs the algorithm waits before determining a packet is lost due +// to early retransmission. +static const double kEarlyRetransmitLossDelayMultiplier = 1.25; + +} + +TCPLossAlgorithm::TCPLossAlgorithm() + : loss_detection_timeout_(QuicTime::Zero()) { } + +LossDetectionType TCPLossAlgorithm::GetLossDetectionType() const { + return kNack; +} + +// Uses nack counts to decide when packets are lost. +SequenceNumberSet TCPLossAlgorithm::DetectLostPackets( + const QuicUnackedPacketMap& unacked_packets, + const QuicTime& time, + QuicPacketSequenceNumber largest_observed, + const RttStats& rtt_stats) { + SequenceNumberSet lost_packets; + loss_detection_timeout_ = QuicTime::Zero(); + QuicTime::Delta loss_delay = + rtt_stats.SmoothedRtt().Multiply(kEarlyRetransmitLossDelayMultiplier); + + for (QuicUnackedPacketMap::const_iterator it = unacked_packets.begin(); + it != unacked_packets.end() && it->first <= largest_observed; ++it) { + if (!it->second.in_flight) { + continue; + } + + LOG_IF(DFATAL, it->second.nack_count == 0) + << "All packets less than largest observed should have been nacked."; + if (it->second.nack_count >= kNumberOfNacksBeforeRetransmission) { + lost_packets.insert(it->first); + continue; + } + + // Only early retransmit(RFC5827) when the last packet gets acked and + // there are retransmittable packets in flight. + // This also implements a timer-protected variant of FACK. + if (it->second.retransmittable_frames && + unacked_packets.largest_sent_packet() == largest_observed) { + // Early retransmit marks the packet as lost once 1.25RTTs have passed + // since the packet was sent and otherwise sets an alarm. + if (time >= it->second.sent_time.Add(loss_delay)) { + lost_packets.insert(it->first); + } else { + // Set the timeout for the earliest retransmittable packet where early + // retransmit applies. + loss_detection_timeout_ = it->second.sent_time.Add(loss_delay); + break; + } + } + } + + return lost_packets; +} + +QuicTime TCPLossAlgorithm::GetLossTimeout() const { + return loss_detection_timeout_; +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/tcp_loss_algorithm.h b/chromium/net/quic/congestion_control/tcp_loss_algorithm.h new file mode 100644 index 00000000000..5f0bcfb523f --- /dev/null +++ b/chromium/net/quic/congestion_control/tcp_loss_algorithm.h @@ -0,0 +1,46 @@ +// 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_TCP_LOSS_ALGORITHM_H_ +#define NET_QUIC_CONGESTION_CONTROL_TCP_LOSS_ALGORITHM_H_ + +#include <algorithm> +#include <map> + +#include "base/basictypes.h" +#include "net/quic/congestion_control/loss_detection_interface.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_time.h" +#include "net/quic/quic_unacked_packet_map.h" + +namespace net { + +// Class which implement's TCP's approach of detecting loss when 3 nacks have +// been received for a packet. Also implements TCP's early retransmit(RFC5827). +class NET_EXPORT_PRIVATE TCPLossAlgorithm : public LossDetectionInterface { + public: + TCPLossAlgorithm(); + virtual ~TCPLossAlgorithm() {} + + virtual LossDetectionType GetLossDetectionType() const OVERRIDE; + + // Uses nack counts to decide when packets are lost. + virtual SequenceNumberSet DetectLostPackets( + const QuicUnackedPacketMap& unacked_packets, + const QuicTime& time, + QuicPacketSequenceNumber largest_observed, + const RttStats& rtt_stats) OVERRIDE; + + // Returns a non-zero value when the early retransmit timer is active. + virtual QuicTime GetLossTimeout() const OVERRIDE; + + private: + QuicTime loss_detection_timeout_; + + DISALLOW_COPY_AND_ASSIGN(TCPLossAlgorithm); +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_TCP_LOSS_ALGORITHM_H_ diff --git a/chromium/net/quic/congestion_control/tcp_loss_algorithm_test.cc b/chromium/net/quic/congestion_control/tcp_loss_algorithm_test.cc new file mode 100644 index 00000000000..eccda4a81c5 --- /dev/null +++ b/chromium/net/quic/congestion_control/tcp_loss_algorithm_test.cc @@ -0,0 +1,183 @@ +// 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 <algorithm> + +#include "base/logging.h" +#include "base/stl_util.h" +#include "net/quic/congestion_control/rtt_stats.h" +#include "net/quic/congestion_control/tcp_loss_algorithm.h" +#include "net/quic/quic_unacked_packet_map.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +class TcpLossAlgorithmTest : public ::testing::Test { + protected: + TcpLossAlgorithmTest() + : unacked_packets_() { + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100), + QuicTime::Delta::Zero(), + clock_.Now()); + } + + void SendDataPacket(QuicPacketSequenceNumber sequence_number) { + SerializedPacket packet(sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER, + NULL, 0, new RetransmittableFrames()); + unacked_packets_.AddPacket(packet); + unacked_packets_.SetSent(sequence_number, clock_.Now(), 1000, true); + } + + void VerifyLosses(QuicPacketSequenceNumber largest_observed, + QuicPacketSequenceNumber* losses_expected, + size_t num_losses) { + SequenceNumberSet lost_packets = + loss_algorithm_.DetectLostPackets( + unacked_packets_, clock_.Now(), largest_observed, rtt_stats_); + EXPECT_EQ(num_losses, lost_packets.size()); + for (size_t i = 0; i < num_losses; ++i) { + EXPECT_TRUE(ContainsKey(lost_packets, losses_expected[i])); + } + } + + QuicUnackedPacketMap unacked_packets_; + TCPLossAlgorithm loss_algorithm_; + RttStats rtt_stats_; + MockClock clock_; +}; + +TEST_F(TcpLossAlgorithmTest, NackRetransmit1Packet) { + const size_t kNumSentPackets = 5; + // Transmit 5 packets. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + // No loss on one ack. + unacked_packets_.RemoveFromInFlight(2); + unacked_packets_.NackPacket(1, 1); + VerifyLosses(2, NULL, 0); + // No loss on two acks. + unacked_packets_.RemoveFromInFlight(3); + unacked_packets_.NackPacket(1, 2); + VerifyLosses(3, NULL, 0); + // Loss on three acks. + unacked_packets_.RemoveFromInFlight(4); + unacked_packets_.NackPacket(1, 3); + QuicPacketSequenceNumber lost[] = { 1 }; + VerifyLosses(4, lost, arraysize(lost)); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +// A stretch ack is an ack that covers more than 1 packet of previously +// unacknowledged data. +TEST_F(TcpLossAlgorithmTest, NackRetransmit1PacketWith1StretchAck) { + const size_t kNumSentPackets = 10; + // Transmit 10 packets. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + + // Nack the first packet 3 times in a single StretchAck. + unacked_packets_.NackPacket(1, 3); + unacked_packets_.RemoveFromInFlight(2); + unacked_packets_.RemoveFromInFlight(3); + unacked_packets_.RemoveFromInFlight(4); + QuicPacketSequenceNumber lost[] = { 1 }; + VerifyLosses(4, lost, arraysize(lost)); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +// Ack a packet 3 packets ahead, causing a retransmit. +TEST_F(TcpLossAlgorithmTest, NackRetransmit1PacketSingleAck) { + const size_t kNumSentPackets = 10; + // Transmit 10 packets. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + + // Nack the first packet 3 times in an AckFrame with three missing packets. + unacked_packets_.NackPacket(1, 3); + unacked_packets_.NackPacket(2, 2); + unacked_packets_.NackPacket(3, 1); + unacked_packets_.RemoveFromInFlight(4); + QuicPacketSequenceNumber lost[] = { 1 }; + VerifyLosses(4, lost, arraysize(lost)); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +TEST_F(TcpLossAlgorithmTest, EarlyRetransmit1Packet) { + const size_t kNumSentPackets = 2; + // Transmit 2 packets. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + // Early retransmit when the final packet gets acked and the first is nacked. + unacked_packets_.RemoveFromInFlight(2); + unacked_packets_.NackPacket(1, 1); + VerifyLosses(2, NULL, 0); + EXPECT_EQ(clock_.Now().Add(rtt_stats_.SmoothedRtt().Multiply(1.25)), + loss_algorithm_.GetLossTimeout()); + + clock_.AdvanceTime(rtt_stats_.latest_rtt().Multiply(1.25)); + QuicPacketSequenceNumber lost[] = { 1 }; + VerifyLosses(2, lost, arraysize(lost)); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +TEST_F(TcpLossAlgorithmTest, EarlyRetransmitAllPackets) { + const size_t kNumSentPackets = 5; + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + // Advance the time 1/4 RTT between 3 and 4. + if (i == 3) { + clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25)); + } + } + + // Early retransmit when the final packet gets acked and 1.25 RTTs have + // elapsed since the packets were sent. + unacked_packets_.RemoveFromInFlight(kNumSentPackets); + // This simulates a single ack following multiple missing packets with FACK. + for (size_t i = 1; i < kNumSentPackets; ++i) { + unacked_packets_.NackPacket(i, kNumSentPackets - i); + } + QuicPacketSequenceNumber lost[] = { 1, 2 }; + VerifyLosses(kNumSentPackets, lost, arraysize(lost)); + // The time has already advanced 1/4 an RTT, so ensure the timeout is set + // 1.25 RTTs after the earliest pending packet(3), not the last(4). + EXPECT_EQ(clock_.Now().Add(rtt_stats_.SmoothedRtt()), + loss_algorithm_.GetLossTimeout()); + + clock_.AdvanceTime(rtt_stats_.SmoothedRtt()); + QuicPacketSequenceNumber lost2[] = { 1, 2, 3 }; + VerifyLosses(kNumSentPackets, lost2, arraysize(lost2)); + EXPECT_EQ(clock_.Now().Add(rtt_stats_.SmoothedRtt().Multiply(0.25)), + loss_algorithm_.GetLossTimeout()); + clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25)); + QuicPacketSequenceNumber lost3[] = { 1, 2, 3, 4 }; + VerifyLosses(kNumSentPackets, lost3, arraysize(lost3)); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +TEST_F(TcpLossAlgorithmTest, DontEarlyRetransmitNeuteredPacket) { + const size_t kNumSentPackets = 2; + // Transmit 2 packets. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + // Neuter packet 1. + unacked_packets_.RemoveRetransmittability(1); + + // Early retransmit when the final packet gets acked and the first is nacked. + unacked_packets_.IncreaseLargestObserved(2); + unacked_packets_.RemoveFromInFlight(2); + unacked_packets_.NackPacket(1, 1); + VerifyLosses(2, NULL, 0); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/tcp_receiver.cc b/chromium/net/quic/congestion_control/tcp_receiver.cc index ecff130b003..a8c5489b604 100644 --- a/chromium/net/quic/congestion_control/tcp_receiver.cc +++ b/chromium/net/quic/congestion_control/tcp_receiver.cc @@ -7,31 +7,24 @@ namespace net { +// Originally 64K bytes, but increased it to 256K to support higher bitrates. // static -// Originally 64K bytes for TCP, setting it to 256K to support higher bitrates. const QuicByteCount TcpReceiver::kReceiveWindowTCP = 256000; TcpReceiver::TcpReceiver() - : accumulated_number_of_recoverd_lost_packets_(0), - receive_window_(kReceiveWindowTCP) { + : receive_window_(kReceiveWindowTCP) { } bool TcpReceiver::GenerateCongestionFeedback( QuicCongestionFeedbackFrame* feedback) { feedback->type = kTCP; - feedback->tcp.accumulated_number_of_lost_packets = - accumulated_number_of_recoverd_lost_packets_; feedback->tcp.receive_window = receive_window_; return true; } void TcpReceiver::RecordIncomingPacket(QuicByteCount bytes, QuicPacketSequenceNumber sequence_number, - QuicTime timestamp, - bool revived) { - if (revived) { - ++accumulated_number_of_recoverd_lost_packets_; - } + QuicTime timestamp) { } } // namespace net diff --git a/chromium/net/quic/congestion_control/tcp_receiver.h b/chromium/net/quic/congestion_control/tcp_receiver.h index 99cf93c4933..a5e5b1b7c7d 100644 --- a/chromium/net/quic/congestion_control/tcp_receiver.h +++ b/chromium/net/quic/congestion_control/tcp_receiver.h @@ -29,12 +29,9 @@ class NET_EXPORT_PRIVATE TcpReceiver : public ReceiveAlgorithmInterface { virtual void RecordIncomingPacket(QuicByteCount bytes, QuicPacketSequenceNumber sequence_number, - QuicTime timestamp, - bool revived) OVERRIDE; + QuicTime timestamp) OVERRIDE; private: - // We need to keep track of FEC recovered packets. - int accumulated_number_of_recoverd_lost_packets_; QuicByteCount receive_window_; DISALLOW_COPY_AND_ASSIGN(TcpReceiver); diff --git a/chromium/net/quic/congestion_control/tcp_receiver_test.cc b/chromium/net/quic/congestion_control/tcp_receiver_test.cc index 305ff75eb8b..e074fdfe6c7 100644 --- a/chromium/net/quic/congestion_control/tcp_receiver_test.cc +++ b/chromium/net/quic/congestion_control/tcp_receiver_test.cc @@ -22,16 +22,14 @@ class QuicTcpReceiverTest : public ::testing::Test { TEST_F(QuicTcpReceiverTest, SimpleReceiver) { QuicCongestionFeedbackFrame feedback; QuicTime timestamp(QuicTime::Zero()); - receiver_->RecordIncomingPacket(1, 1, timestamp, false); + receiver_->RecordIncomingPacket(1, 1, timestamp); ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); EXPECT_EQ(kTCP, feedback.type); EXPECT_EQ(256000u, feedback.tcp.receive_window); - EXPECT_EQ(0, feedback.tcp.accumulated_number_of_lost_packets); - receiver_->RecordIncomingPacket(1, 2, timestamp, true); + receiver_->RecordIncomingPacket(1, 2, timestamp); ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); EXPECT_EQ(kTCP, feedback.type); EXPECT_EQ(256000u, feedback.tcp.receive_window); - EXPECT_EQ(1, feedback.tcp.accumulated_number_of_lost_packets); } } // namespace test diff --git a/chromium/net/quic/congestion_control/time_loss_algorithm.cc b/chromium/net/quic/congestion_control/time_loss_algorithm.cc new file mode 100644 index 00000000000..d23deadf118 --- /dev/null +++ b/chromium/net/quic/congestion_control/time_loss_algorithm.cc @@ -0,0 +1,69 @@ +// 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/time_loss_algorithm.h" + +#include "net/quic/congestion_control/rtt_stats.h" +#include "net/quic/quic_protocol.h" + +namespace net { +namespace { + +// The minimum delay before a packet will be considered lost, +// regardless of SRTT. Half of the minimum TLP, since the loss algorithm only +// triggers when a nack has been receieved for the packet. +static const size_t kMinLossDelayMs = 5; + +// How many RTTs the algorithm waits before determining a packet is lost. +static const double kLossDelayMultiplier = 1.25; + +} // namespace + +TimeLossAlgorithm::TimeLossAlgorithm() + : loss_detection_timeout_(QuicTime::Zero()) { } + +LossDetectionType TimeLossAlgorithm::GetLossDetectionType() const { + return kTime; +} + +SequenceNumberSet TimeLossAlgorithm::DetectLostPackets( + const QuicUnackedPacketMap& unacked_packets, + const QuicTime& time, + QuicPacketSequenceNumber largest_observed, + const RttStats& rtt_stats) { + SequenceNumberSet lost_packets; + loss_detection_timeout_ = QuicTime::Zero(); + QuicTime::Delta loss_delay = QuicTime::Delta::Max( + QuicTime::Delta::FromMilliseconds(kMinLossDelayMs), + QuicTime::Delta::Max(rtt_stats.SmoothedRtt(), rtt_stats.latest_rtt()) + .Multiply(kLossDelayMultiplier)); + + for (QuicUnackedPacketMap::const_iterator it = unacked_packets.begin(); + it != unacked_packets.end() && it->first <= largest_observed; ++it) { + if (!it->second.in_flight) { + continue; + } + LOG_IF(DFATAL, it->second.nack_count == 0) + << "All packets less than largest observed should have been nacked."; + + // Packets are sent in order, so break when we haven't waited long enough + // to lose any more packets and leave the loss_time_ set for the timeout. + QuicTime when_lost = it->second.sent_time.Add(loss_delay); + if (time < when_lost) { + loss_detection_timeout_ = when_lost; + break; + } + lost_packets.insert(it->first); + } + + return lost_packets; +} + +// loss_time_ is updated in DetectLostPackets, which must be called every time +// an ack is received or the timeout expires. +QuicTime TimeLossAlgorithm::GetLossTimeout() const { + return loss_detection_timeout_; +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/time_loss_algorithm.h b/chromium/net/quic/congestion_control/time_loss_algorithm.h new file mode 100644 index 00000000000..ae37e1e3258 --- /dev/null +++ b/chromium/net/quic/congestion_control/time_loss_algorithm.h @@ -0,0 +1,52 @@ +// 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_TIME_LOSS_ALGORITHM_H_ +#define NET_QUIC_CONGESTION_CONTROL_TIME_LOSS_ALGORITHM_H_ + +#include <algorithm> +#include <map> + +#include "base/basictypes.h" +#include "net/quic/congestion_control/loss_detection_interface.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_time.h" +#include "net/quic/quic_unacked_packet_map.h" + +namespace net { + +// A loss detection algorithm which avoids spurious losses and retransmissions +// by waiting 1.25 RTTs after a packet was sent instead of nack count. +class NET_EXPORT_PRIVATE TimeLossAlgorithm : public LossDetectionInterface { + public: + TimeLossAlgorithm(); + virtual ~TimeLossAlgorithm() {} + + virtual LossDetectionType GetLossDetectionType() const OVERRIDE; + + // Declares pending packets less than the largest observed lost when it has + // been 1.25 RTT since they were sent. Packets larger than the largest + // observed are retransmitted via TLP. + virtual SequenceNumberSet DetectLostPackets( + const QuicUnackedPacketMap& unacked_packets, + const QuicTime& time, + QuicPacketSequenceNumber largest_observed, + const RttStats& rtt_stats) OVERRIDE; + + // Returns the time the next packet will be lost, or zero if there + // are no nacked pending packets outstanding. + // TODO(ianswett): Ideally the RTT variance and the RTT would be used to + // determine the time a packet is considered lost. + // TODO(ianswett): Consider using Max(1.25 * srtt, 1.125 * last_rtt). + virtual QuicTime GetLossTimeout() const OVERRIDE; + + private: + QuicTime loss_detection_timeout_; + + DISALLOW_COPY_AND_ASSIGN(TimeLossAlgorithm); +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_TIME_LOSS_ALGORITHM_H_ diff --git a/chromium/net/quic/congestion_control/time_loss_algorithm_test.cc b/chromium/net/quic/congestion_control/time_loss_algorithm_test.cc new file mode 100644 index 00000000000..bd03e608bcc --- /dev/null +++ b/chromium/net/quic/congestion_control/time_loss_algorithm_test.cc @@ -0,0 +1,138 @@ +// 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 <algorithm> + +#include "base/logging.h" +#include "base/stl_util.h" +#include "net/quic/congestion_control/rtt_stats.h" +#include "net/quic/congestion_control/time_loss_algorithm.h" +#include "net/quic/quic_unacked_packet_map.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +class TimeLossAlgorithmTest : public ::testing::Test { + protected: + TimeLossAlgorithmTest() + : unacked_packets_() { + rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100), + QuicTime::Delta::Zero(), + clock_.Now()); + } + + void SendDataPacket(QuicPacketSequenceNumber sequence_number) { + SerializedPacket packet(sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER, + NULL, 0, new RetransmittableFrames()); + unacked_packets_.AddPacket(packet); + unacked_packets_.SetSent(sequence_number, clock_.Now(), 1000, true); + } + + void VerifyLosses(QuicPacketSequenceNumber largest_observed, + QuicPacketSequenceNumber* losses_expected, + size_t num_losses) { + SequenceNumberSet lost_packets = + loss_algorithm_.DetectLostPackets( + unacked_packets_, clock_.Now(), largest_observed, rtt_stats_); + EXPECT_EQ(num_losses, lost_packets.size()); + for (size_t i = 0; i < num_losses; ++i) { + EXPECT_TRUE(ContainsKey(lost_packets, losses_expected[i])); + } + } + + QuicUnackedPacketMap unacked_packets_; + TimeLossAlgorithm loss_algorithm_; + RttStats rtt_stats_; + MockClock clock_; +}; + +TEST_F(TimeLossAlgorithmTest, NoLossFor500Nacks) { + const size_t kNumSentPackets = 5; + // Transmit 5 packets. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + unacked_packets_.RemoveFromInFlight(2); + for (size_t i = 1; i < 500; ++i) { + unacked_packets_.NackPacket(1, i); + VerifyLosses(2, NULL, 0); + } + EXPECT_EQ(rtt_stats_.SmoothedRtt().Multiply(1.25), + loss_algorithm_.GetLossTimeout().Subtract(clock_.Now())); +} + +TEST_F(TimeLossAlgorithmTest, NoLossUntilTimeout) { + const size_t kNumSentPackets = 10; + // Transmit 10 packets at 1/10th an RTT interval. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.1)); + } + // Expect the timer to not be set. + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); + // The packet should not be lost until 1.25 RTTs pass. + unacked_packets_.NackPacket(1, 1); + unacked_packets_.RemoveFromInFlight(2); + VerifyLosses(2, NULL, 0); + // Expect the timer to be set to 0.25 RTT's in the future. + EXPECT_EQ(rtt_stats_.SmoothedRtt().Multiply(0.25), + loss_algorithm_.GetLossTimeout().Subtract(clock_.Now())); + unacked_packets_.NackPacket(1, 5); + VerifyLosses(2, NULL, 0); + clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25)); + QuicPacketSequenceNumber lost[] = { 1 }; + VerifyLosses(2, lost, arraysize(lost)); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +TEST_F(TimeLossAlgorithmTest, NoLossWithoutNack) { + const size_t kNumSentPackets = 10; + // Transmit 10 packets at 1/10th an RTT interval. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.1)); + } + // Expect the timer to not be set. + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); + // The packet should not be lost without a nack. + unacked_packets_.RemoveFromInFlight(1); + VerifyLosses(1, NULL, 0); + // The timer should still not be set. + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); + clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25)); + VerifyLosses(1, NULL, 0); + clock_.AdvanceTime(rtt_stats_.SmoothedRtt()); + VerifyLosses(1, NULL, 0); + + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +TEST_F(TimeLossAlgorithmTest, MultipleLossesAtOnce) { + const size_t kNumSentPackets = 10; + // Transmit 10 packets at once and then go forward an RTT. + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); + } + clock_.AdvanceTime(rtt_stats_.SmoothedRtt()); + // Expect the timer to not be set. + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); + // The packet should not be lost until 1.25 RTTs pass. + for (size_t i = 1; i < kNumSentPackets; ++i) { + unacked_packets_.NackPacket(i, 1); + } + unacked_packets_.RemoveFromInFlight(10); + VerifyLosses(10, NULL, 0); + // Expect the timer to be set to 0.25 RTT's in the future. + EXPECT_EQ(rtt_stats_.SmoothedRtt().Multiply(0.25), + loss_algorithm_.GetLossTimeout().Subtract(clock_.Now())); + clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25)); + QuicPacketSequenceNumber lost[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + VerifyLosses(10, lost, arraysize(lost)); + EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/crypto/aead_base_decrypter.h b/chromium/net/quic/crypto/aead_base_decrypter.h new file mode 100644 index 00000000000..6257409f9fb --- /dev/null +++ b/chromium/net/quic/crypto/aead_base_decrypter.h @@ -0,0 +1,107 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CRYPTO_AEAD_BASE_DECRYPTER_H_ +#define NET_QUIC_CRYPTO_AEAD_BASE_DECRYPTER_H_ + +#include "base/compiler_specific.h" +#include "net/quic/crypto/quic_decrypter.h" + +#if defined(USE_OPENSSL) +#include "net/quic/crypto/scoped_evp_aead_ctx.h" +#else +#include <pkcs11t.h> +#include <seccomon.h> +typedef struct PK11SymKeyStr PK11SymKey; +typedef SECStatus (*PK11_DecryptFunction)( + PK11SymKey* symKey, CK_MECHANISM_TYPE mechanism, SECItem* param, + unsigned char* out, unsigned int* outLen, unsigned int maxLen, + const unsigned char* enc, unsigned encLen); +#endif + +namespace net { + +// AeadBaseDecrypter is the base class of AEAD QuicDecrypter subclasses. +class NET_EXPORT_PRIVATE AeadBaseDecrypter : public QuicDecrypter { + public: +#if defined(USE_OPENSSL) + AeadBaseDecrypter(const EVP_AEAD* aead_alg, + size_t key_size, + size_t auth_tag_size, + size_t nonce_prefix_size); +#else + AeadBaseDecrypter(CK_MECHANISM_TYPE aead_mechanism, + PK11_DecryptFunction pk11_decrypt, + size_t key_size, + size_t auth_tag_size, + size_t nonce_prefix_size); +#endif + virtual ~AeadBaseDecrypter(); + + // QuicDecrypter implementation + virtual bool SetKey(base::StringPiece key) OVERRIDE; + virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE; + virtual bool Decrypt(base::StringPiece nonce, + base::StringPiece associated_data, + base::StringPiece ciphertext, + unsigned char* output, + size_t* output_length) OVERRIDE; + virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number, + base::StringPiece associated_data, + base::StringPiece ciphertext) OVERRIDE; + virtual base::StringPiece GetKey() const OVERRIDE; + virtual base::StringPiece GetNoncePrefix() const OVERRIDE; + + protected: + // Make these constants available to the subclasses so that the subclasses + // can assert at compile time their key_size_ and nonce_prefix_size_ do not + // exceed the maximum. + static const size_t kMaxKeySize = 32; + static const size_t kMaxNoncePrefixSize = 4; + +#if !defined(USE_OPENSSL) + struct AeadParams { + unsigned int len; + union { + CK_GCM_PARAMS gcm_params; +#if !defined(USE_NSS) + // USE_NSS means we are using system NSS rather than our copy of NSS. + // The system NSS <pkcs11n.h> header doesn't define this type yet. + CK_NSS_AEAD_PARAMS nss_aead_params; +#endif + } data; + }; + + virtual void FillAeadParams(base::StringPiece nonce, + base::StringPiece associated_data, + size_t auth_tag_size, + AeadParams* aead_params) const = 0; +#endif // !defined(USE_OPENSSL) + + private: +#if defined(USE_OPENSSL) + const EVP_AEAD* const aead_alg_; +#else + const CK_MECHANISM_TYPE aead_mechanism_; + const PK11_DecryptFunction pk11_decrypt_; +#endif + const size_t key_size_; + const size_t auth_tag_size_; + const size_t nonce_prefix_size_; + + // The key. + unsigned char key_[kMaxKeySize]; + // The nonce prefix. + unsigned char nonce_prefix_[kMaxNoncePrefixSize]; + +#if defined(USE_OPENSSL) + ScopedEVPAEADCtx ctx_; +#endif + + DISALLOW_COPY_AND_ASSIGN(AeadBaseDecrypter); +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_AEAD_BASE_DECRYPTER_H_ diff --git a/chromium/net/quic/crypto/aead_base_decrypter_nss.cc b/chromium/net/quic/crypto/aead_base_decrypter_nss.cc new file mode 100644 index 00000000000..bbf30244f87 --- /dev/null +++ b/chromium/net/quic/crypto/aead_base_decrypter_nss.cc @@ -0,0 +1,154 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/aead_base_decrypter.h" + +#include <pk11pub.h> + +#include "base/memory/scoped_ptr.h" +#include "crypto/scoped_nss_types.h" + +using base::StringPiece; + +namespace net { + +AeadBaseDecrypter::AeadBaseDecrypter(CK_MECHANISM_TYPE aead_mechanism, + PK11_DecryptFunction pk11_decrypt, + size_t key_size, + size_t auth_tag_size, + size_t nonce_prefix_size) + : aead_mechanism_(aead_mechanism), + pk11_decrypt_(pk11_decrypt), + key_size_(key_size), + auth_tag_size_(auth_tag_size), + nonce_prefix_size_(nonce_prefix_size) { + DCHECK_LE(key_size_, sizeof(key_)); + DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_)); +} + +AeadBaseDecrypter::~AeadBaseDecrypter() {} + +bool AeadBaseDecrypter::SetKey(StringPiece key) { + DCHECK_EQ(key.size(), key_size_); + if (key.size() != key_size_) { + return false; + } + memcpy(key_, key.data(), key.size()); + return true; +} + +bool AeadBaseDecrypter::SetNoncePrefix(StringPiece nonce_prefix) { + DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_); + if (nonce_prefix.size() != nonce_prefix_size_) { + return false; + } + memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size()); + return true; +} + +bool AeadBaseDecrypter::Decrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece ciphertext, + uint8* output, + size_t* output_length) { + if (ciphertext.length() < auth_tag_size_ || + nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) { + return false; + } + // NSS 3.14.x incorrectly requires an output buffer at least as long as + // the ciphertext (NSS bug + // https://bugzilla.mozilla.org/show_bug.cgi?id= 853674). Fortunately + // QuicDecrypter::Decrypt() specifies that |output| must be as long as + // |ciphertext| on entry. + size_t plaintext_size = ciphertext.length() - auth_tag_size_; + + // Import key_ into NSS. + SECItem key_item; + key_item.type = siBuffer; + key_item.data = key_; + key_item.len = key_size_; + PK11SlotInfo* slot = PK11_GetInternalSlot(); + + // TODO(wtc): For an AES-GCM key, the correct value for |key_mechanism| is + // CKM_AES_GCM, but because of NSS bug + // https://bugzilla.mozilla.org/show_bug.cgi?id=853285, use CKM_AES_ECB as a + // workaround. Remove this when we require NSS 3.15. + CK_MECHANISM_TYPE key_mechanism = aead_mechanism_; + if (key_mechanism == CKM_AES_GCM) { + key_mechanism = CKM_AES_ECB; + } + + // The exact value of the |origin| argument doesn't matter to NSS as long as + // it's not PK11_OriginFortezzaHack, so pass PK11_OriginUnwrap as a + // placeholder. + crypto::ScopedPK11SymKey aead_key(PK11_ImportSymKey( + slot, key_mechanism, PK11_OriginUnwrap, CKA_DECRYPT, &key_item, NULL)); + PK11_FreeSlot(slot); + slot = NULL; + if (!aead_key) { + DVLOG(1) << "PK11_ImportSymKey failed"; + return false; + } + + AeadParams aead_params = {0}; + FillAeadParams(nonce, associated_data, auth_tag_size_, &aead_params); + + SECItem param; + param.type = siBuffer; + param.data = reinterpret_cast<unsigned char*>(&aead_params.data); + param.len = aead_params.len; + + unsigned int output_len; + if (pk11_decrypt_(aead_key.get(), aead_mechanism_, ¶m, + output, &output_len, ciphertext.length(), + reinterpret_cast<const unsigned char*>(ciphertext.data()), + ciphertext.length()) != SECSuccess) { + return false; + } + + if (output_len != plaintext_size) { + DVLOG(1) << "Wrong output length"; + return false; + } + *output_length = output_len; + return true; +} + +QuicData* AeadBaseDecrypter::DecryptPacket( + QuicPacketSequenceNumber sequence_number, + StringPiece associated_data, + StringPiece ciphertext) { + if (ciphertext.length() < auth_tag_size_) { + return NULL; + } + size_t plaintext_size; + scoped_ptr<char[]> plaintext(new char[ciphertext.length()]); + + uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)]; + const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number); + DCHECK_LE(nonce_size, sizeof(nonce)); + memcpy(nonce, nonce_prefix_, nonce_prefix_size_); + memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number)); + if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size), + associated_data, ciphertext, + reinterpret_cast<uint8*>(plaintext.get()), + &plaintext_size)) { + return NULL; + } + return new QuicData(plaintext.release(), plaintext_size, true); +} + +StringPiece AeadBaseDecrypter::GetKey() const { + return StringPiece(reinterpret_cast<const char*>(key_), key_size_); +} + +StringPiece AeadBaseDecrypter::GetNoncePrefix() const { + if (nonce_prefix_size_ == 0) { + return StringPiece(); + } + return StringPiece(reinterpret_cast<const char*>(nonce_prefix_), + nonce_prefix_size_); +} + +} // namespace net diff --git a/chromium/net/quic/crypto/aead_base_decrypter_openssl.cc b/chromium/net/quic/crypto/aead_base_decrypter_openssl.cc new file mode 100644 index 00000000000..2190bf6966e --- /dev/null +++ b/chromium/net/quic/crypto/aead_base_decrypter_openssl.cc @@ -0,0 +1,143 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/aead_base_decrypter.h" + +#include <openssl/err.h> +#include <openssl/evp.h> + +#include "base/memory/scoped_ptr.h" + +using base::StringPiece; + +namespace net { + +namespace { + +// Clear OpenSSL error stack. +void ClearOpenSslErrors() { + while (ERR_get_error()) {} +} + +// In debug builds only, log OpenSSL error stack. Then clear OpenSSL error +// stack. +void DLogOpenSslErrors() { +#ifdef NDEBUG + ClearOpenSslErrors(); +#else + while (unsigned long error = ERR_get_error()) { + char buf[120]; + ERR_error_string_n(error, buf, arraysize(buf)); + DLOG(ERROR) << "OpenSSL error: " << buf; + } +#endif +} + +} // namespace + +AeadBaseDecrypter::AeadBaseDecrypter(const EVP_AEAD* aead_alg, + size_t key_size, + size_t auth_tag_size, + size_t nonce_prefix_size) + : aead_alg_(aead_alg), + key_size_(key_size), + auth_tag_size_(auth_tag_size), + nonce_prefix_size_(nonce_prefix_size) { + DCHECK_LE(key_size_, sizeof(key_)); + DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_)); +} + +AeadBaseDecrypter::~AeadBaseDecrypter() {} + +bool AeadBaseDecrypter::SetKey(StringPiece key) { + DCHECK_EQ(key.size(), key_size_); + if (key.size() != key_size_) { + return false; + } + memcpy(key_, key.data(), key.size()); + + EVP_AEAD_CTX_cleanup(ctx_.get()); + if (!EVP_AEAD_CTX_init(ctx_.get(), aead_alg_, key_, key_size_, + auth_tag_size_, NULL)) { + DLogOpenSslErrors(); + return false; + } + + return true; +} + +bool AeadBaseDecrypter::SetNoncePrefix(StringPiece nonce_prefix) { + DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_); + if (nonce_prefix.size() != nonce_prefix_size_) { + return false; + } + memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size()); + return true; +} + +bool AeadBaseDecrypter::Decrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece ciphertext, + uint8* output, + size_t* output_length) { + if (ciphertext.length() < auth_tag_size_ || + nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) { + return false; + } + + ssize_t len = EVP_AEAD_CTX_open( + ctx_.get(), output, ciphertext.size(), + reinterpret_cast<const uint8_t*>(nonce.data()), nonce.size(), + reinterpret_cast<const uint8_t*>(ciphertext.data()), ciphertext.size(), + reinterpret_cast<const uint8_t*>(associated_data.data()), + associated_data.size()); + + if (len < 0) { + // Because QuicFramer does trial decryption, decryption errors are expected + // when encryption level changes. So we don't log decryption errors. + ClearOpenSslErrors(); + return false; + } + + *output_length = len; + return true; +} + +QuicData* AeadBaseDecrypter::DecryptPacket( + QuicPacketSequenceNumber sequence_number, + StringPiece associated_data, + StringPiece ciphertext) { + if (ciphertext.length() < auth_tag_size_) { + return NULL; + } + size_t plaintext_size = ciphertext.length(); + scoped_ptr<char[]> plaintext(new char[plaintext_size]); + + uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)]; + const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number); + DCHECK_LE(nonce_size, sizeof(nonce)); + memcpy(nonce, nonce_prefix_, nonce_prefix_size_); + memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number)); + if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size), + associated_data, ciphertext, + reinterpret_cast<uint8*>(plaintext.get()), + &plaintext_size)) { + return NULL; + } + return new QuicData(plaintext.release(), plaintext_size, true); +} + +StringPiece AeadBaseDecrypter::GetKey() const { + return StringPiece(reinterpret_cast<const char*>(key_), key_size_); +} + +StringPiece AeadBaseDecrypter::GetNoncePrefix() const { + if (nonce_prefix_size_ == 0) { + return StringPiece(); + } + return StringPiece(reinterpret_cast<const char*>(nonce_prefix_), + nonce_prefix_size_); +} + +} // namespace net diff --git a/chromium/net/quic/crypto/aead_base_encrypter.h b/chromium/net/quic/crypto/aead_base_encrypter.h new file mode 100644 index 00000000000..713813150ec --- /dev/null +++ b/chromium/net/quic/crypto/aead_base_encrypter.h @@ -0,0 +1,110 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CRYPTO_AEAD_BASE_ENCRYPTER_H_ +#define NET_QUIC_CRYPTO_AEAD_BASE_ENCRYPTER_H_ + +#include "base/compiler_specific.h" +#include "net/quic/crypto/quic_encrypter.h" + +#if defined(USE_OPENSSL) +#include "net/quic/crypto/scoped_evp_aead_ctx.h" +#else +#include <pkcs11t.h> +#include <seccomon.h> +typedef struct PK11SymKeyStr PK11SymKey; +typedef SECStatus (*PK11_EncryptFunction)( + PK11SymKey* symKey, CK_MECHANISM_TYPE mechanism, SECItem* param, + unsigned char* out, unsigned int* outLen, unsigned int maxLen, + const unsigned char* data, unsigned int dataLen); +#endif + +namespace net { + +// AeadBaseEncrypter is the base class of AEAD QuicEncrypter subclasses. +class NET_EXPORT_PRIVATE AeadBaseEncrypter : public QuicEncrypter { + public: +#if defined(USE_OPENSSL) + AeadBaseEncrypter(const EVP_AEAD* aead_alg, + size_t key_size, + size_t auth_tag_size, + size_t nonce_prefix_size); +#else + AeadBaseEncrypter(CK_MECHANISM_TYPE aead_mechanism, + PK11_EncryptFunction pk11_encrypt, + size_t key_size, + size_t auth_tag_size, + size_t nonce_prefix_size); +#endif + virtual ~AeadBaseEncrypter(); + + // QuicEncrypter implementation + virtual bool SetKey(base::StringPiece key) OVERRIDE; + virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE; + virtual bool Encrypt(base::StringPiece nonce, + base::StringPiece associated_data, + base::StringPiece plaintext, + unsigned char* output) OVERRIDE; + virtual QuicData* EncryptPacket(QuicPacketSequenceNumber sequence_number, + base::StringPiece associated_data, + base::StringPiece plaintext) OVERRIDE; + virtual size_t GetKeySize() const OVERRIDE; + virtual size_t GetNoncePrefixSize() const OVERRIDE; + virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const OVERRIDE; + virtual size_t GetCiphertextSize(size_t plaintext_size) const OVERRIDE; + virtual base::StringPiece GetKey() const OVERRIDE; + virtual base::StringPiece GetNoncePrefix() const OVERRIDE; + + protected: + // Make these constants available to the subclasses so that the subclasses + // can assert at compile time their key_size_ and nonce_prefix_size_ do not + // exceed the maximum. + static const size_t kMaxKeySize = 32; + static const size_t kMaxNoncePrefixSize = 4; + +#if !defined(USE_OPENSSL) + struct AeadParams { + unsigned int len; + union { + CK_GCM_PARAMS gcm_params; +#if !defined(USE_NSS) + // USE_NSS means we are using system NSS rather than our copy of NSS. + // The system NSS <pkcs11n.h> header doesn't define this type yet. + CK_NSS_AEAD_PARAMS nss_aead_params; +#endif + } data; + }; + + virtual void FillAeadParams(base::StringPiece nonce, + base::StringPiece associated_data, + size_t auth_tag_size, + AeadParams* aead_params) const = 0; +#endif + + private: +#if defined(USE_OPENSSL) + const EVP_AEAD* const aead_alg_; +#else + const CK_MECHANISM_TYPE aead_mechanism_; + const PK11_EncryptFunction pk11_encrypt_; +#endif + const size_t key_size_; + const size_t auth_tag_size_; + const size_t nonce_prefix_size_; + + // The key. + unsigned char key_[kMaxKeySize]; + // The nonce prefix. + unsigned char nonce_prefix_[kMaxNoncePrefixSize]; + +#if defined(USE_OPENSSL) + ScopedEVPAEADCtx ctx_; +#endif + + DISALLOW_COPY_AND_ASSIGN(AeadBaseEncrypter); +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_AEAD_BASE_ENCRYPTER_H_ diff --git a/chromium/net/quic/crypto/aead_base_encrypter_nss.cc b/chromium/net/quic/crypto/aead_base_encrypter_nss.cc new file mode 100644 index 00000000000..fd4d888554c --- /dev/null +++ b/chromium/net/quic/crypto/aead_base_encrypter_nss.cc @@ -0,0 +1,162 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/aead_base_encrypter.h" + +#include <pk11pub.h> + +#include "base/memory/scoped_ptr.h" +#include "crypto/scoped_nss_types.h" + +using base::StringPiece; + +namespace net { + +AeadBaseEncrypter::AeadBaseEncrypter(CK_MECHANISM_TYPE aead_mechanism, + PK11_EncryptFunction pk11_encrypt, + size_t key_size, + size_t auth_tag_size, + size_t nonce_prefix_size) + : aead_mechanism_(aead_mechanism), + pk11_encrypt_(pk11_encrypt), + key_size_(key_size), + auth_tag_size_(auth_tag_size), + nonce_prefix_size_(nonce_prefix_size) { + DCHECK_LE(key_size_, sizeof(key_)); + DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_)); +} + +AeadBaseEncrypter::~AeadBaseEncrypter() {} + +bool AeadBaseEncrypter::SetKey(StringPiece key) { + DCHECK_EQ(key.size(), key_size_); + if (key.size() != key_size_) { + return false; + } + memcpy(key_, key.data(), key.size()); + return true; +} + +bool AeadBaseEncrypter::SetNoncePrefix(StringPiece nonce_prefix) { + DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_); + if (nonce_prefix.size() != nonce_prefix_size_) { + return false; + } + memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size()); + return true; +} + +bool AeadBaseEncrypter::Encrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece plaintext, + unsigned char* output) { + if (nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) { + return false; + } + + size_t ciphertext_size = GetCiphertextSize(plaintext.length()); + + // Import key_ into NSS. + SECItem key_item; + key_item.type = siBuffer; + key_item.data = key_; + key_item.len = key_size_; + PK11SlotInfo* slot = PK11_GetInternalSlot(); + + // TODO(wtc): For an AES-GCM key, the correct value for |key_mechanism| is + // CKM_AES_GCM, but because of NSS bug + // https://bugzilla.mozilla.org/show_bug.cgi?id=853285, use CKM_AES_ECB as a + // workaround. Remove this when we require NSS 3.15. + CK_MECHANISM_TYPE key_mechanism = aead_mechanism_; + if (key_mechanism == CKM_AES_GCM) { + key_mechanism = CKM_AES_ECB; + } + + // The exact value of the |origin| argument doesn't matter to NSS as long as + // it's not PK11_OriginFortezzaHack, so we pass PK11_OriginUnwrap as a + // placeholder. + crypto::ScopedPK11SymKey aead_key(PK11_ImportSymKey( + slot, key_mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL)); + PK11_FreeSlot(slot); + slot = NULL; + if (!aead_key) { + DVLOG(1) << "PK11_ImportSymKey failed"; + return false; + } + + AeadParams aead_params = {0}; + FillAeadParams(nonce, associated_data, auth_tag_size_, &aead_params); + + SECItem param; + param.type = siBuffer; + param.data = reinterpret_cast<unsigned char*>(&aead_params.data); + param.len = aead_params.len; + + unsigned int output_len; + if (pk11_encrypt_(aead_key.get(), aead_mechanism_, ¶m, + output, &output_len, ciphertext_size, + reinterpret_cast<const unsigned char*>(plaintext.data()), + plaintext.size()) != SECSuccess) { + DVLOG(1) << "pk11_encrypt_ failed"; + return false; + } + + if (output_len != ciphertext_size) { + DVLOG(1) << "Wrong output length"; + return false; + } + + return true; +} + +QuicData* AeadBaseEncrypter::EncryptPacket( + QuicPacketSequenceNumber sequence_number, + StringPiece associated_data, + StringPiece plaintext) { + size_t ciphertext_size = GetCiphertextSize(plaintext.length()); + scoped_ptr<char[]> ciphertext(new char[ciphertext_size]); + + // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the + // same sequence number twice. + uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)]; + const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number); + DCHECK_LE(nonce_size, sizeof(nonce)); + memcpy(nonce, nonce_prefix_, nonce_prefix_size_); + memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number)); + if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size), + associated_data, plaintext, + reinterpret_cast<unsigned char*>(ciphertext.get()))) { + return NULL; + } + + return new QuicData(ciphertext.release(), ciphertext_size, true); +} + +size_t AeadBaseEncrypter::GetKeySize() const { return key_size_; } + +size_t AeadBaseEncrypter::GetNoncePrefixSize() const { + return nonce_prefix_size_; +} + +size_t AeadBaseEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { + return ciphertext_size - auth_tag_size_; +} + +size_t AeadBaseEncrypter::GetCiphertextSize(size_t plaintext_size) const { + return plaintext_size + auth_tag_size_; +} + +StringPiece AeadBaseEncrypter::GetKey() const { + return StringPiece(reinterpret_cast<const char*>(key_), key_size_); +} + +StringPiece AeadBaseEncrypter::GetNoncePrefix() const { + if (nonce_prefix_size_ == 0) { + return StringPiece(); + } + return StringPiece(reinterpret_cast<const char*>(nonce_prefix_), + nonce_prefix_size_); +} + +} // namespace net diff --git a/chromium/net/quic/crypto/aead_base_encrypter_openssl.cc b/chromium/net/quic/crypto/aead_base_encrypter_openssl.cc new file mode 100644 index 00000000000..9f053abf932 --- /dev/null +++ b/chromium/net/quic/crypto/aead_base_encrypter_openssl.cc @@ -0,0 +1,148 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/aead_base_encrypter.h" + +#include <openssl/err.h> +#include <openssl/evp.h> +#include <string.h> + +#include "base/memory/scoped_ptr.h" + +using base::StringPiece; + +namespace net { + +namespace { + +// In debug builds only, log OpenSSL error stack. Then clear OpenSSL error +// stack. +void DLogOpenSslErrors() { +#ifdef NDEBUG + while (ERR_get_error()) {} +#else + while (unsigned long error = ERR_get_error()) { + char buf[120]; + ERR_error_string_n(error, buf, arraysize(buf)); + DLOG(ERROR) << "OpenSSL error: " << buf; + } +#endif +} + +} // namespace + +AeadBaseEncrypter::AeadBaseEncrypter(const EVP_AEAD* aead_alg, + size_t key_size, + size_t auth_tag_size, + size_t nonce_prefix_size) + : aead_alg_(aead_alg), + key_size_(key_size), + auth_tag_size_(auth_tag_size), + nonce_prefix_size_(nonce_prefix_size) { + DCHECK_LE(key_size_, sizeof(key_)); + DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_)); +} + +AeadBaseEncrypter::~AeadBaseEncrypter() {} + +bool AeadBaseEncrypter::SetKey(StringPiece key) { + DCHECK_EQ(key.size(), key_size_); + if (key.size() != key_size_) { + return false; + } + memcpy(key_, key.data(), key.size()); + + EVP_AEAD_CTX_cleanup(ctx_.get()); + + if (!EVP_AEAD_CTX_init(ctx_.get(), aead_alg_, key_, key_size_, + auth_tag_size_, NULL)) { + DLogOpenSslErrors(); + return false; + } + + return true; +} + +bool AeadBaseEncrypter::SetNoncePrefix(StringPiece nonce_prefix) { + DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_); + if (nonce_prefix.size() != nonce_prefix_size_) { + return false; + } + memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size()); + return true; +} + +bool AeadBaseEncrypter::Encrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece plaintext, + unsigned char* output) { + if (nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) { + return false; + } + + ssize_t len = EVP_AEAD_CTX_seal( + ctx_.get(), output, plaintext.size() + auth_tag_size_, + reinterpret_cast<const uint8_t*>(nonce.data()), nonce.size(), + reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size(), + reinterpret_cast<const uint8_t*>(associated_data.data()), + associated_data.size()); + + if (len < 0) { + DLogOpenSslErrors(); + return false; + } + + return true; +} + +QuicData* AeadBaseEncrypter::EncryptPacket( + QuicPacketSequenceNumber sequence_number, + StringPiece associated_data, + StringPiece plaintext) { + size_t ciphertext_size = GetCiphertextSize(plaintext.length()); + scoped_ptr<char[]> ciphertext(new char[ciphertext_size]); + + // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the + // same sequence number twice. + uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)]; + const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number); + DCHECK_LE(nonce_size, sizeof(nonce)); + memcpy(nonce, nonce_prefix_, nonce_prefix_size_); + memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number)); + if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size), + associated_data, plaintext, + reinterpret_cast<unsigned char*>(ciphertext.get()))) { + return NULL; + } + + return new QuicData(ciphertext.release(), ciphertext_size, true); +} + +size_t AeadBaseEncrypter::GetKeySize() const { return key_size_; } + +size_t AeadBaseEncrypter::GetNoncePrefixSize() const { + return nonce_prefix_size_; +} + +size_t AeadBaseEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { + return ciphertext_size - auth_tag_size_; +} + +size_t AeadBaseEncrypter::GetCiphertextSize(size_t plaintext_size) const { + return plaintext_size + auth_tag_size_; +} + +StringPiece AeadBaseEncrypter::GetKey() const { + return StringPiece(reinterpret_cast<const char*>(key_), key_size_); +} + +StringPiece AeadBaseEncrypter::GetNoncePrefix() const { + if (nonce_prefix_size_ == 0) { + return StringPiece(); + } + return StringPiece(reinterpret_cast<const char*>(nonce_prefix_), + nonce_prefix_size_); +} + +} // namespace net diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h index aba610f25df..0511c8b7268 100644 --- a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h +++ b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h @@ -5,28 +5,17 @@ #ifndef NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_ #define NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_ -#include <string> - -#include "base/compiler_specific.h" -#include "net/quic/crypto/quic_decrypter.h" - -#if defined(USE_OPENSSL) -#include "net/quic/crypto/scoped_evp_cipher_ctx.h" -#endif +#include "net/quic/crypto/aead_base_decrypter.h" namespace net { -namespace test { -class Aes128Gcm12DecrypterPeer; -} // namespace test - // An Aes128Gcm12Decrypter is a QuicDecrypter that implements the // AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by // calling QuicDecrypter::Create(kAESG). // // It uses an authentication tag of 12 bytes (96 bits). The fixed prefix // of the nonce is four bytes. -class NET_EXPORT_PRIVATE Aes128Gcm12Decrypter : public QuicDecrypter { +class NET_EXPORT_PRIVATE Aes128Gcm12Decrypter : public AeadBaseDecrypter { public: enum { // Authentication tags are truncated to 96 bits. @@ -36,34 +25,17 @@ class NET_EXPORT_PRIVATE Aes128Gcm12Decrypter : public QuicDecrypter { Aes128Gcm12Decrypter(); virtual ~Aes128Gcm12Decrypter(); - // Returns true if the underlying crypto library supports AES GCM. - static bool IsSupported(); - - // QuicDecrypter implementation - virtual bool SetKey(base::StringPiece key) OVERRIDE; - virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE; - virtual bool Decrypt(base::StringPiece nonce, - base::StringPiece associated_data, - base::StringPiece ciphertext, - unsigned char* output, - size_t* output_length) OVERRIDE; - virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number, - base::StringPiece associated_data, - base::StringPiece ciphertext) OVERRIDE; - virtual base::StringPiece GetKey() const OVERRIDE; - virtual base::StringPiece GetNoncePrefix() const OVERRIDE; +#if !defined(USE_OPENSSL) + protected: + // AeadBaseDecrypter methods: + virtual void FillAeadParams(base::StringPiece nonce, + base::StringPiece associated_data, + size_t auth_tag_size, + AeadParams* aead_params) const OVERRIDE; +#endif private: - // The 128-bit AES key. - unsigned char key_[16]; - // The nonce prefix. - unsigned char nonce_prefix_[4]; - -#if defined(USE_OPENSSL) - // TODO(rtenneti): when Chromium's version of OpenSSL has EVP_AEAD_CTX, merge - // internal CL 53267501. - ScopedEVPCipherCtx ctx_; -#endif + DISALLOW_COPY_AND_ASSIGN(Aes128Gcm12Decrypter); }; } // namespace net diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc index e0b42378021..0ba82698a86 100644 --- a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc +++ b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc @@ -4,12 +4,10 @@ #include "net/quic/crypto/aes_128_gcm_12_decrypter.h" -#include <nss.h> #include <pk11pub.h> #include <secerr.h> #include "base/lazy_instance.h" -#include "base/memory/scoped_ptr.h" #include "crypto/ghash.h" #include "crypto/scoped_nss_types.h" @@ -23,31 +21,8 @@ namespace net { namespace { -// The pkcs11t.h header in NSS versions older than 3.14 does not have the CTR -// and GCM types, so define them here. -#if !defined(CKM_AES_CTR) -#define CKM_AES_CTR 0x00001086 -#define CKM_AES_GCM 0x00001087 - -struct CK_AES_CTR_PARAMS { - CK_ULONG ulCounterBits; - CK_BYTE cb[16]; -}; - -struct CK_GCM_PARAMS { - CK_BYTE_PTR pIv; - CK_ULONG ulIvLen; - CK_BYTE_PTR pAAD; - CK_ULONG ulAADLen; - CK_ULONG ulTagBits; -}; -#endif // CKM_AES_CTR - -typedef SECStatus -(*PK11_DecryptFunction)( - PK11SymKey* symKey, CK_MECHANISM_TYPE mechanism, SECItem* param, - unsigned char* out, unsigned int* outLen, unsigned int maxLen, - const unsigned char* enc, unsigned encLen); +const size_t kKeySize = 16; +const size_t kNoncePrefixSize = 4; // On Linux, dynamically link against the system version of libnss3.so. In // order to continue working on systems without up-to-date versions of NSS, @@ -61,10 +36,6 @@ class GcmSupportChecker { return pk11_decrypt_func_; } - static CK_MECHANISM_TYPE aes_key_mechanism() { - return aes_key_mechanism_; - } - private: friend struct base::DefaultLazyInstanceTraits<GcmSupportChecker>; @@ -80,33 +51,19 @@ class GcmSupportChecker { // AES-GCM directly. This was introduced in NSS 3.15. pk11_decrypt_func_ = (PK11_DecryptFunction)dlsym(RTLD_DEFAULT, "PK11_Decrypt"); - if (pk11_decrypt_func_ == NULL) { - aes_key_mechanism_ = CKM_AES_ECB; - } #endif } // |pk11_decrypt_func_| stores the runtime symbol resolution of PK11_Decrypt. static PK11_DecryptFunction pk11_decrypt_func_; - - // The correct value for |aes_key_mechanism_| is CKM_AES_GCM, but because of - // NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=853285 (to be fixed in - // NSS 3.15), use CKM_AES_ECB for NSS versions older than 3.15. - static CK_MECHANISM_TYPE aes_key_mechanism_; }; // static PK11_DecryptFunction GcmSupportChecker::pk11_decrypt_func_ = NULL; -// static -CK_MECHANISM_TYPE GcmSupportChecker::aes_key_mechanism_ = CKM_AES_GCM; - base::LazyInstance<GcmSupportChecker>::Leaky g_gcm_support_checker = LAZY_INSTANCE_INITIALIZER; -const size_t kNoncePrefixSize = 4; -const size_t kAESNonceSize = 12; - // Calls PK11_Decrypt if it's available. Otherwise, emulates CKM_AES_GCM using // CKM_AES_CTR and the GaloisHash class. SECStatus My_Decrypt(PK11SymKey* key, @@ -250,134 +207,30 @@ SECStatus My_Decrypt(PK11SymKey* key, } // namespace -Aes128Gcm12Decrypter::Aes128Gcm12Decrypter() { +Aes128Gcm12Decrypter::Aes128Gcm12Decrypter() + : AeadBaseDecrypter(CKM_AES_GCM, My_Decrypt, kKeySize, kAuthTagSize, + kNoncePrefixSize) { + COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big); + COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize, + nonce_prefix_size_too_big); ignore_result(g_gcm_support_checker.Get()); } Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {} -// static -bool Aes128Gcm12Decrypter::IsSupported() { - // NSS 3.15 supports CKM_AES_GCM directly. - // NSS 3.14 supports CKM_AES_CTR, which can be used to emulate CKM_AES_GCM. - // Versions earlier than NSS 3.14 are not supported. - return NSS_VersionCheck("3.14") != PR_FALSE; -} - -bool Aes128Gcm12Decrypter::SetKey(StringPiece key) { - DCHECK_EQ(key.size(), sizeof(key_)); - if (key.size() != sizeof(key_)) { - return false; - } - memcpy(key_, key.data(), key.size()); - return true; -} - -bool Aes128Gcm12Decrypter::SetNoncePrefix(StringPiece nonce_prefix) { - DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize); - if (nonce_prefix.size() != kNoncePrefixSize) { - return false; - } - COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length); - memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size()); - return true; -} - -bool Aes128Gcm12Decrypter::Decrypt(StringPiece nonce, - StringPiece associated_data, - StringPiece ciphertext, - uint8* output, - size_t* output_length) { - if (ciphertext.length() < kAuthTagSize || - nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) { - return false; - } - // NSS 3.14.x incorrectly requires an output buffer at least as long as - // the ciphertext (NSS bug - // https://bugzilla.mozilla.org/show_bug.cgi?id= 853674). Fortunately - // QuicDecrypter::Decrypt() specifies that |output| must be as long as - // |ciphertext| on entry. - size_t plaintext_size = ciphertext.length() - kAuthTagSize; - - // Import key_ into NSS. - SECItem key_item; - key_item.type = siBuffer; - key_item.data = key_; - key_item.len = sizeof(key_); - PK11SlotInfo* slot = PK11_GetInternalSlot(); - // The exact value of the |origin| argument doesn't matter to NSS as long as - // it's not PK11_OriginFortezzaHack, so pass PK11_OriginUnwrap as a - // placeholder. - crypto::ScopedPK11SymKey aes_key(PK11_ImportSymKey( - slot, GcmSupportChecker::aes_key_mechanism(), PK11_OriginUnwrap, - CKA_DECRYPT, &key_item, NULL)); - PK11_FreeSlot(slot); - slot = NULL; - if (!aes_key) { - DVLOG(1) << "PK11_ImportSymKey failed"; - return false; - } - - CK_GCM_PARAMS gcm_params = {0}; - gcm_params.pIv = +void Aes128Gcm12Decrypter::FillAeadParams(StringPiece nonce, + StringPiece associated_data, + size_t auth_tag_size, + AeadParams* aead_params) const { + aead_params->len = sizeof(aead_params->data.gcm_params); + CK_GCM_PARAMS* gcm_params = &aead_params->data.gcm_params; + gcm_params->pIv = reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data())); - gcm_params.ulIvLen = nonce.size(); - gcm_params.pAAD = + gcm_params->ulIvLen = nonce.size(); + gcm_params->pAAD = reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data())); - gcm_params.ulAADLen = associated_data.size(); - gcm_params.ulTagBits = kAuthTagSize * 8; - - SECItem param; - param.type = siBuffer; - param.data = reinterpret_cast<unsigned char*>(&gcm_params); - param.len = sizeof(gcm_params); - - unsigned int output_len; - if (My_Decrypt(aes_key.get(), CKM_AES_GCM, ¶m, - output, &output_len, ciphertext.length(), - reinterpret_cast<const unsigned char*>(ciphertext.data()), - ciphertext.length()) != SECSuccess) { - return false; - } - - if (output_len != plaintext_size) { - DVLOG(1) << "Wrong output length"; - return false; - } - *output_length = output_len; - return true; -} - -QuicData* Aes128Gcm12Decrypter::DecryptPacket( - QuicPacketSequenceNumber sequence_number, - StringPiece associated_data, - StringPiece ciphertext) { - if (ciphertext.length() < kAuthTagSize) { - return NULL; - } - size_t plaintext_size; - scoped_ptr<char[]> plaintext(new char[ciphertext.length()]); - - uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)]; - COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size); - memcpy(nonce, nonce_prefix_, kNoncePrefixSize); - memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number)); - if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)), - associated_data, ciphertext, - reinterpret_cast<uint8*>(plaintext.get()), - &plaintext_size)) { - return NULL; - } - return new QuicData(plaintext.release(), plaintext_size, true); -} - -StringPiece Aes128Gcm12Decrypter::GetKey() const { - return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); -} - -StringPiece Aes128Gcm12Decrypter::GetNoncePrefix() const { - return StringPiece(reinterpret_cast<const char*>(nonce_prefix_), - kNoncePrefixSize); + gcm_params->ulAADLen = associated_data.size(); + gcm_params->ulTagBits = auth_tag_size * 8; } } // namespace net diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc index 56317fdd3c4..109d2dae85d 100644 --- a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc +++ b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc @@ -6,142 +6,23 @@ #include <openssl/evp.h> -#include "base/memory/scoped_ptr.h" - -using base::StringPiece; - namespace net { -const size_t kNoncePrefixSize = 4; -const size_t kAESNonceSize = 12; - -Aes128Gcm12Decrypter::Aes128Gcm12Decrypter() {} - -Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {} - -// static -bool Aes128Gcm12Decrypter::IsSupported() { return true; } - -bool Aes128Gcm12Decrypter::SetKey(StringPiece key) { - DCHECK_EQ(key.size(), sizeof(key_)); - if (key.size() != sizeof(key_)) { - return false; - } - memcpy(key_, key.data(), key.size()); - - // Set the cipher type and the key. - if (EVP_EncryptInit_ex(ctx_.get(), EVP_aes_128_gcm(), NULL, key_, - NULL) == 0) { - return false; - } - - // Set the IV (nonce) length. - if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_SET_IVLEN, kAESNonceSize, - NULL) == 0) { - return false; - } - - return true; -} - -bool Aes128Gcm12Decrypter::SetNoncePrefix(StringPiece nonce_prefix) { - DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize); - if (nonce_prefix.size() != kNoncePrefixSize) { - return false; - } - COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length); - memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size()); - return true; -} +namespace { -bool Aes128Gcm12Decrypter::Decrypt(StringPiece nonce, - StringPiece associated_data, - StringPiece ciphertext, - uint8* output, - size_t* output_length) { - if (ciphertext.length() < kAuthTagSize || - nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) { - return false; - } - const size_t plaintext_size = ciphertext.length() - kAuthTagSize; - - // Set the IV (nonce). - if (EVP_DecryptInit_ex( - ctx_.get(), NULL, NULL, NULL, - reinterpret_cast<const uint8*>(nonce.data())) == 0) { - return false; - } - - // Set the authentication tag. - if (EVP_CIPHER_CTX_ctrl( - ctx_.get(), EVP_CTRL_GCM_SET_TAG, kAuthTagSize, - const_cast<char*>(ciphertext.data()) + plaintext_size) == 0) { - return false; - } - - // If we pass a NULL, zero-length associated data to OpenSSL then it breaks. - // Thus we only set non-empty associated data. - if (!associated_data.empty()) { - // Set the associated data. The second argument (output buffer) must be - // NULL. - int unused_len; - if (EVP_DecryptUpdate( - ctx_.get(), NULL, &unused_len, - reinterpret_cast<const uint8*>(associated_data.data()), - associated_data.size()) == 0) { - return false; - } - } - - int len; - if (EVP_DecryptUpdate( - ctx_.get(), output, &len, - reinterpret_cast<const uint8*>(ciphertext.data()), - plaintext_size) == 0) { - return false; - } - output += len; - - if (EVP_DecryptFinal_ex(ctx_.get(), output, &len) == 0) { - return false; - } - output += len; - - *output_length = plaintext_size; - - return true; -} - -QuicData* Aes128Gcm12Decrypter::DecryptPacket( - QuicPacketSequenceNumber sequence_number, - StringPiece associated_data, - StringPiece ciphertext) { - if (ciphertext.length() < kAuthTagSize) { - return NULL; - } - size_t plaintext_size; - scoped_ptr<char[]> plaintext(new char[ciphertext.length()]); +const size_t kKeySize = 16; +const size_t kNoncePrefixSize = 4; - uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)]; - COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size); - memcpy(nonce, nonce_prefix_, kNoncePrefixSize); - memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number)); - if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)), - associated_data, ciphertext, - reinterpret_cast<uint8*>(plaintext.get()), - &plaintext_size)) { - return NULL; - } - return new QuicData(plaintext.release(), plaintext_size, true); -} +} // namespace -StringPiece Aes128Gcm12Decrypter::GetKey() const { - return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); +Aes128Gcm12Decrypter::Aes128Gcm12Decrypter() + : AeadBaseDecrypter(EVP_aead_aes_128_gcm(), kKeySize, kAuthTagSize, + kNoncePrefixSize) { + COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big); + COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize, + nonce_prefix_size_too_big); } -StringPiece Aes128Gcm12Decrypter::GetNoncePrefix() const { - return StringPiece(reinterpret_cast<const char*>(nonce_prefix_), - kNoncePrefixSize); -} +Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {} } // namespace net diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc index dd37085894c..5a719a05a41 100644 --- a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc +++ b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc @@ -268,69 +268,65 @@ QuicData* DecryptWithNonce(Aes128Gcm12Decrypter* decrypter, } TEST(Aes128Gcm12DecrypterTest, Decrypt) { - if (!Aes128Gcm12Decrypter::IsSupported()) { - LOG(INFO) << "AES GCM not supported. Test skipped."; - return; - } - - string key; - string iv; - string ct; - string aad; - string tag; - string pt; - for (size_t i = 0; i < arraysize(test_group_array); i++) { SCOPED_TRACE(i); - const TestVector* test_vector = test_group_array[i]; + const TestVector* test_vectors = test_group_array[i]; const TestGroupInfo& test_info = test_group_info[i]; - for (size_t j = 0; test_vector[j].key != NULL; j++) { + for (size_t j = 0; test_vectors[j].key != NULL; j++) { // If not present then decryption is expected to fail. - bool has_pt = test_vector[j].pt; + bool has_pt = test_vectors[j].pt; // Decode the test vector. - ASSERT_TRUE(DecodeHexString(test_vector[j].key, &key)); - ASSERT_TRUE(DecodeHexString(test_vector[j].iv, &iv)); - ASSERT_TRUE(DecodeHexString(test_vector[j].ct, &ct)); - ASSERT_TRUE(DecodeHexString(test_vector[j].aad, &aad)); - ASSERT_TRUE(DecodeHexString(test_vector[j].tag, &tag)); - if (has_pt) - ASSERT_TRUE(DecodeHexString(test_vector[j].pt, &pt)); + string key; + string iv; + string ct; + string aad; + string tag; + string pt; + ASSERT_TRUE(DecodeHexString(test_vectors[j].key, &key)); + ASSERT_TRUE(DecodeHexString(test_vectors[j].iv, &iv)); + ASSERT_TRUE(DecodeHexString(test_vectors[j].ct, &ct)); + ASSERT_TRUE(DecodeHexString(test_vectors[j].aad, &aad)); + ASSERT_TRUE(DecodeHexString(test_vectors[j].tag, &tag)); + if (has_pt) { + ASSERT_TRUE(DecodeHexString(test_vectors[j].pt, &pt)); + } // The test vector's lengths should look sane. Note that the lengths // in |test_info| are in bits. - EXPECT_EQ(test_info.key_len, key.size() * 8); - EXPECT_EQ(test_info.iv_len, iv.size() * 8); - EXPECT_EQ(test_info.pt_len, pt.size() * 8); - EXPECT_EQ(test_info.aad_len, aad.size() * 8); - EXPECT_EQ(test_info.tag_len, tag.size() * 8); - if (has_pt) - EXPECT_EQ(test_info.pt_len, pt.size() * 8); + EXPECT_EQ(test_info.key_len, key.length() * 8); + EXPECT_EQ(test_info.iv_len, iv.length() * 8); + EXPECT_EQ(test_info.pt_len, ct.length() * 8); + EXPECT_EQ(test_info.aad_len, aad.length() * 8); + EXPECT_EQ(test_info.tag_len, tag.length() * 8); + if (has_pt) { + EXPECT_EQ(test_info.pt_len, pt.length() * 8); + } // The test vectors have 16 byte authenticators but this code only uses // the first 12. ASSERT_LE(static_cast<size_t>(Aes128Gcm12Decrypter::kAuthTagSize), - tag.size()); - string ciphertext = - ct + tag.substr(0, Aes128Gcm12Decrypter::kAuthTagSize); + tag.length()); + tag.resize(Aes128Gcm12Decrypter::kAuthTagSize); + string ciphertext = ct + tag; Aes128Gcm12Decrypter decrypter; ASSERT_TRUE(decrypter.SetKey(key)); scoped_ptr<QuicData> decrypted(DecryptWithNonce( &decrypter, iv, - // OpenSSL fails if NULL is set as the AAD, as opposed to a - // zero-length, non-NULL pointer. - aad.size() ? aad : StringPiece(), ciphertext)); + // This deliberately tests that the decrypter can handle an AAD that + // is set to NULL, as opposed to a zero-length, non-NULL pointer. + aad.length() ? aad : StringPiece(), ciphertext)); if (!decrypted.get()) { EXPECT_FALSE(has_pt); continue; } EXPECT_TRUE(has_pt); - ASSERT_EQ(pt.size(), decrypted->length()); + ASSERT_EQ(pt.length(), decrypted->length()); test::CompareCharArraysWithHexError("plaintext", decrypted->data(), - pt.size(), pt.data(), pt.size()); + pt.length(), pt.data(), pt.length()); } } } diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h index a2d1dc9a088..1d8f32149bf 100644 --- a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h +++ b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h @@ -5,28 +5,17 @@ #ifndef NET_QUIC_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_ #define NET_QUIC_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_ -#include <string> - -#include "base/compiler_specific.h" -#include "net/quic/crypto/quic_encrypter.h" - -#if defined(USE_OPENSSL) -#include "net/quic/crypto/scoped_evp_cipher_ctx.h" -#endif +#include "net/quic/crypto/aead_base_encrypter.h" namespace net { -namespace test { -class Aes128Gcm12EncrypterPeer; -} // namespace test - // An Aes128Gcm12Encrypter is a QuicEncrypter that implements the // AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by // calling QuicEncrypter::Create(kAESG). // // It uses an authentication tag of 12 bytes (96 bits). The fixed prefix // of the nonce is four bytes. -class NET_EXPORT_PRIVATE Aes128Gcm12Encrypter : public QuicEncrypter { +class NET_EXPORT_PRIVATE Aes128Gcm12Encrypter : public AeadBaseEncrypter { public: enum { // Authentication tags are truncated to 96 bits. @@ -36,34 +25,17 @@ class NET_EXPORT_PRIVATE Aes128Gcm12Encrypter : public QuicEncrypter { Aes128Gcm12Encrypter(); virtual ~Aes128Gcm12Encrypter(); - // QuicEncrypter implementation - virtual bool SetKey(base::StringPiece key) OVERRIDE; - virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE; - virtual bool Encrypt(base::StringPiece nonce, - base::StringPiece associated_data, - base::StringPiece plaintext, - unsigned char* output) OVERRIDE; - virtual QuicData* EncryptPacket(QuicPacketSequenceNumber sequence_number, - base::StringPiece associated_data, - base::StringPiece plaintext) OVERRIDE; - virtual size_t GetKeySize() const OVERRIDE; - virtual size_t GetNoncePrefixSize() const OVERRIDE; - virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const OVERRIDE; - virtual size_t GetCiphertextSize(size_t plaintext_size) const OVERRIDE; - virtual base::StringPiece GetKey() const OVERRIDE; - virtual base::StringPiece GetNoncePrefix() const OVERRIDE; +#if !defined(USE_OPENSSL) + protected: + // AeadBaseEncrypter methods: + virtual void FillAeadParams(base::StringPiece nonce, + base::StringPiece associated_data, + size_t auth_tag_size, + AeadParams* aead_params) const OVERRIDE; +#endif private: - // The 128-bit AES key. - unsigned char key_[16]; - // The nonce prefix. - unsigned char nonce_prefix_[4]; - -#if defined(USE_OPENSSL) - // TODO(rtenneti): when Chromium's version of OpenSSL has EVP_AEAD_CTX, merge - // internal CL 53267501. - ScopedEVPCipherCtx ctx_; -#endif + DISALLOW_COPY_AND_ASSIGN(Aes128Gcm12Encrypter); }; } // namespace net diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc index 2cd072af461..09901a47672 100644 --- a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc +++ b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc @@ -4,12 +4,10 @@ #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" -#include <nss.h> #include <pk11pub.h> #include <secerr.h> #include "base/lazy_instance.h" -#include "base/memory/scoped_ptr.h" #include "crypto/ghash.h" #include "crypto/scoped_nss_types.h" @@ -23,31 +21,8 @@ namespace net { namespace { -// The pkcs11t.h header in NSS versions older than 3.14 does not have the CTR -// and GCM types, so define them here. -#if !defined(CKM_AES_CTR) -#define CKM_AES_CTR 0x00001086 -#define CKM_AES_GCM 0x00001087 - -struct CK_AES_CTR_PARAMS { - CK_ULONG ulCounterBits; - CK_BYTE cb[16]; -}; - -struct CK_GCM_PARAMS { - CK_BYTE_PTR pIv; - CK_ULONG ulIvLen; - CK_BYTE_PTR pAAD; - CK_ULONG ulAADLen; - CK_ULONG ulTagBits; -}; -#endif // CKM_AES_CTR - -typedef SECStatus -(*PK11_EncryptFunction)( - PK11SymKey* symKey, CK_MECHANISM_TYPE mechanism, SECItem* param, - unsigned char* out, unsigned int* outLen, unsigned int maxLen, - const unsigned char* data, unsigned int dataLen); +const size_t kKeySize = 16; +const size_t kNoncePrefixSize = 4; // On Linux, dynamically link against the system version of libnss3.so. In // order to continue working on systems without up-to-date versions of NSS, @@ -61,10 +36,6 @@ class GcmSupportChecker { return pk11_encrypt_func_; } - static CK_MECHANISM_TYPE aes_key_mechanism() { - return aes_key_mechanism_; - } - private: friend struct base::DefaultLazyInstanceTraits<GcmSupportChecker>; @@ -80,34 +51,19 @@ class GcmSupportChecker { // AES-GCM directly. This was introduced in NSS 3.15. pk11_encrypt_func_ = (PK11_EncryptFunction)dlsym(RTLD_DEFAULT, "PK11_Encrypt"); - if (pk11_encrypt_func_ == NULL) { - aes_key_mechanism_ = CKM_AES_ECB; - } #endif } // |pk11_encrypt_func_| stores the runtime symbol resolution of PK11_Encrypt. static PK11_EncryptFunction pk11_encrypt_func_; - - // The correct value for |aes_key_mechanism_| is CKM_AES_GCM, but because of - // NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=853285 (to be fixed in - // NSS 3.15), use CKM_AES_ECB for NSS versions older than 3.15. - static CK_MECHANISM_TYPE aes_key_mechanism_; }; // static PK11_EncryptFunction GcmSupportChecker::pk11_encrypt_func_ = NULL; -// static -CK_MECHANISM_TYPE GcmSupportChecker::aes_key_mechanism_ = CKM_AES_GCM; - base::LazyInstance<GcmSupportChecker>::Leaky g_gcm_support_checker = LAZY_INSTANCE_INITIALIZER; -const size_t kKeySize = 16; -const size_t kNoncePrefixSize = 4; -const size_t kAESNonceSize = 12; - // Calls PK11_Encrypt if it's available. Otherwise, emulates CKM_AES_GCM using // CKM_AES_CTR and the GaloisHash class. SECStatus My_Encrypt(PK11SymKey* key, @@ -250,136 +206,30 @@ SECStatus My_Encrypt(PK11SymKey* key, } // namespace -Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() { +Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() + : AeadBaseEncrypter(CKM_AES_GCM, My_Encrypt, kKeySize, kAuthTagSize, + kNoncePrefixSize) { + COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big); + COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize, + nonce_prefix_size_too_big); ignore_result(g_gcm_support_checker.Get()); } Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {} -bool Aes128Gcm12Encrypter::SetKey(StringPiece key) { - DCHECK_EQ(key.size(), sizeof(key_)); - if (key.size() != sizeof(key_)) { - return false; - } - memcpy(key_, key.data(), key.size()); - return true; -} - -bool Aes128Gcm12Encrypter::SetNoncePrefix(StringPiece nonce_prefix) { - DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize); - if (nonce_prefix.size() != kNoncePrefixSize) { - return false; - } - COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length); - memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size()); - return true; -} - -bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce, - StringPiece associated_data, - StringPiece plaintext, - unsigned char* output) { - if (nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) { - return false; - } - - size_t ciphertext_size = GetCiphertextSize(plaintext.length()); - - // Import key_ into NSS. - SECItem key_item; - key_item.type = siBuffer; - key_item.data = key_; - key_item.len = sizeof(key_); - PK11SlotInfo* slot = PK11_GetInternalSlot(); - // The exact value of the |origin| argument doesn't matter to NSS as long as - // it's not PK11_OriginFortezzaHack, so we pass PK11_OriginUnwrap as a - // placeholder. - crypto::ScopedPK11SymKey aes_key(PK11_ImportSymKey( - slot, GcmSupportChecker::aes_key_mechanism(), PK11_OriginUnwrap, - CKA_ENCRYPT, &key_item, NULL)); - PK11_FreeSlot(slot); - slot = NULL; - if (!aes_key) { - DVLOG(1) << "PK11_ImportSymKey failed"; - return false; - } - - CK_GCM_PARAMS gcm_params = {0}; - gcm_params.pIv = +void Aes128Gcm12Encrypter::FillAeadParams(StringPiece nonce, + StringPiece associated_data, + size_t auth_tag_size, + AeadParams* aead_params) const { + aead_params->len = sizeof(aead_params->data.gcm_params); + CK_GCM_PARAMS* gcm_params = &aead_params->data.gcm_params; + gcm_params->pIv = reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data())); - gcm_params.ulIvLen = nonce.size(); - gcm_params.pAAD = + gcm_params->ulIvLen = nonce.size(); + gcm_params->pAAD = reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data())); - gcm_params.ulAADLen = associated_data.size(); - gcm_params.ulTagBits = kAuthTagSize * 8; - - SECItem param; - param.type = siBuffer; - param.data = reinterpret_cast<unsigned char*>(&gcm_params); - param.len = sizeof(gcm_params); - - unsigned int output_len; - if (My_Encrypt(aes_key.get(), CKM_AES_GCM, ¶m, - output, &output_len, ciphertext_size, - reinterpret_cast<const unsigned char*>(plaintext.data()), - plaintext.size()) != SECSuccess) { - DVLOG(1) << "My_Encrypt failed"; - return false; - } - - if (output_len != ciphertext_size) { - DVLOG(1) << "Wrong output length"; - return false; - } - - return true; -} - -QuicData* Aes128Gcm12Encrypter::EncryptPacket( - QuicPacketSequenceNumber sequence_number, - StringPiece associated_data, - StringPiece plaintext) { - size_t ciphertext_size = GetCiphertextSize(plaintext.length()); - scoped_ptr<char[]> ciphertext(new char[ciphertext_size]); - - // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the - // same sequence number twice. - uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)]; - COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size); - memcpy(nonce, nonce_prefix_, kNoncePrefixSize); - memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number)); - if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)), - associated_data, plaintext, - reinterpret_cast<unsigned char*>(ciphertext.get()))) { - return NULL; - } - - return new QuicData(ciphertext.release(), ciphertext_size, true); -} - -size_t Aes128Gcm12Encrypter::GetKeySize() const { return kKeySize; } - -size_t Aes128Gcm12Encrypter::GetNoncePrefixSize() const { - return kNoncePrefixSize; -} - -size_t Aes128Gcm12Encrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { - return ciphertext_size - kAuthTagSize; -} - -// An AEAD_AES_128_GCM_12 ciphertext is exactly 12 bytes longer than its -// corresponding plaintext. -size_t Aes128Gcm12Encrypter::GetCiphertextSize(size_t plaintext_size) const { - return plaintext_size + kAuthTagSize; -} - -StringPiece Aes128Gcm12Encrypter::GetKey() const { - return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); -} - -StringPiece Aes128Gcm12Encrypter::GetNoncePrefix() const { - return StringPiece(reinterpret_cast<const char*>(nonce_prefix_), - kNoncePrefixSize); + gcm_params->ulAADLen = associated_data.size(); + gcm_params->ulTagBits = auth_tag_size * 8; } } // namespace net diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc index 394971a7382..6489528fd37 100644 --- a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc +++ b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc @@ -4,13 +4,7 @@ #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" -#include <openssl/err.h> #include <openssl/evp.h> -#include <string.h> - -#include "base/memory/scoped_ptr.h" - -using base::StringPiece; namespace net { @@ -18,161 +12,17 @@ namespace { const size_t kKeySize = 16; const size_t kNoncePrefixSize = 4; -const size_t kAESNonceSize = 12; - -void ClearOpenSslErrors() { -#ifdef NDEBUG - while (ERR_get_error()) {} -#else - while (long error = ERR_get_error()) { - char buf[120]; - ERR_error_string_n(error, buf, arraysize(buf)); - DLOG(ERROR) << "OpenSSL error: " << buf; - } -#endif -} } // namespace -Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() {} - -Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {} - -bool Aes128Gcm12Encrypter::SetKey(StringPiece key) { - DCHECK_EQ(key.size(), sizeof(key_)); - if (key.size() != sizeof(key_)) { - return false; - } - memcpy(key_, key.data(), key.size()); - - // Set the cipher type and the key. - if (EVP_EncryptInit_ex(ctx_.get(), EVP_aes_128_gcm(), NULL, key_, - NULL) == 0) { - ClearOpenSslErrors(); - return false; - } - - // Set the IV (nonce) length. - if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_SET_IVLEN, kAESNonceSize, - NULL) == 0) { - ClearOpenSslErrors(); - return false; - } - - return true; -} - -bool Aes128Gcm12Encrypter::SetNoncePrefix(StringPiece nonce_prefix) { - DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize); - if (nonce_prefix.size() != kNoncePrefixSize) { - return false; - } - COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length); - memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size()); - return true; +Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() + : AeadBaseEncrypter(EVP_aead_aes_128_gcm(), kKeySize, kAuthTagSize, + kNoncePrefixSize) { + COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big); + COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize, + nonce_prefix_size_too_big); } -bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce, - StringPiece associated_data, - StringPiece plaintext, - unsigned char* output) { - if (nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) { - return false; - } - - // Set the IV (nonce). - if (EVP_EncryptInit_ex( - ctx_.get(), NULL, NULL, NULL, - reinterpret_cast<const unsigned char*>(nonce.data())) == 0) { - ClearOpenSslErrors(); - return false; - } - - // If we pass a NULL, zero-length associated data to OpenSSL then it breaks. - // Thus we only set non-empty associated data. - if (!associated_data.empty()) { - // Set the associated data. The second argument (output buffer) must be - // NULL. - int unused_len; - if (EVP_EncryptUpdate( - ctx_.get(), NULL, &unused_len, - reinterpret_cast<const unsigned char*>(associated_data.data()), - associated_data.size()) == 0) { - ClearOpenSslErrors(); - return false; - } - } - - int len; - if (EVP_EncryptUpdate( - ctx_.get(), output, &len, - reinterpret_cast<const unsigned char*>(plaintext.data()), - plaintext.size()) == 0) { - ClearOpenSslErrors(); - return false; - } - output += len; - - if (EVP_EncryptFinal_ex(ctx_.get(), output, &len) == 0) { - ClearOpenSslErrors(); - return false; - } - output += len; - - if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_GET_TAG, kAuthTagSize, - output) == 0) { - ClearOpenSslErrors(); - return false; - } - - return true; -} - -QuicData* Aes128Gcm12Encrypter::EncryptPacket( - QuicPacketSequenceNumber sequence_number, - StringPiece associated_data, - StringPiece plaintext) { - size_t ciphertext_size = GetCiphertextSize(plaintext.length()); - scoped_ptr<char[]> ciphertext(new char[ciphertext_size]); - - // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the - // same sequence number twice. - uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)]; - COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size); - memcpy(nonce, nonce_prefix_, kNoncePrefixSize); - memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number)); - if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)), - associated_data, plaintext, - reinterpret_cast<unsigned char*>(ciphertext.get()))) { - return NULL; - } - - return new QuicData(ciphertext.release(), ciphertext_size, true); -} - -size_t Aes128Gcm12Encrypter::GetKeySize() const { return kKeySize; } - -size_t Aes128Gcm12Encrypter::GetNoncePrefixSize() const { - return kNoncePrefixSize; -} - -size_t Aes128Gcm12Encrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { - return ciphertext_size - kAuthTagSize; -} - -// An AEAD_AES_128_GCM_12 ciphertext is exactly 12 bytes longer than its -// corresponding plaintext. -size_t Aes128Gcm12Encrypter::GetCiphertextSize(size_t plaintext_size) const { - return plaintext_size + kAuthTagSize; -} - -StringPiece Aes128Gcm12Encrypter::GetKey() const { - return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); -} - -StringPiece Aes128Gcm12Encrypter::GetNoncePrefix() const { - return StringPiece(reinterpret_cast<const char*>(nonce_prefix_), - kNoncePrefixSize); -} +Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {} } // namespace net diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc index bfbf93cd716..afe431002e8 100644 --- a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc +++ b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc @@ -224,57 +224,55 @@ QuicData* EncryptWithNonce(Aes128Gcm12Encrypter* encrypter, } TEST(Aes128Gcm12EncrypterTest, Encrypt) { - string key; - string iv; - string pt; - string aad; - string ct; - string tag; - for (size_t i = 0; i < arraysize(test_group_array); i++) { SCOPED_TRACE(i); - const TestVector* test_vector = test_group_array[i]; + const TestVector* test_vectors = test_group_array[i]; const TestGroupInfo& test_info = test_group_info[i]; - for (size_t j = 0; test_vector[j].key != NULL; j++) { + for (size_t j = 0; test_vectors[j].key != NULL; j++) { // Decode the test vector. - ASSERT_TRUE(DecodeHexString(test_vector[j].key, &key)); - ASSERT_TRUE(DecodeHexString(test_vector[j].iv, &iv)); - ASSERT_TRUE(DecodeHexString(test_vector[j].pt, &pt)); - ASSERT_TRUE(DecodeHexString(test_vector[j].aad, &aad)); - ASSERT_TRUE(DecodeHexString(test_vector[j].ct, &ct)); - ASSERT_TRUE(DecodeHexString(test_vector[j].tag, &tag)); + string key; + string iv; + string pt; + string aad; + string ct; + string tag; + ASSERT_TRUE(DecodeHexString(test_vectors[j].key, &key)); + ASSERT_TRUE(DecodeHexString(test_vectors[j].iv, &iv)); + ASSERT_TRUE(DecodeHexString(test_vectors[j].pt, &pt)); + ASSERT_TRUE(DecodeHexString(test_vectors[j].aad, &aad)); + ASSERT_TRUE(DecodeHexString(test_vectors[j].ct, &ct)); + ASSERT_TRUE(DecodeHexString(test_vectors[j].tag, &tag)); // The test vector's lengths should look sane. Note that the lengths // in |test_info| are in bits. - EXPECT_EQ(test_info.key_len, key.size() * 8); - EXPECT_EQ(test_info.iv_len, iv.size() * 8); - EXPECT_EQ(test_info.pt_len, pt.size() * 8); - EXPECT_EQ(test_info.aad_len, aad.size() * 8); - EXPECT_EQ(test_info.pt_len, ct.size() * 8); - EXPECT_EQ(test_info.tag_len, tag.size() * 8); + EXPECT_EQ(test_info.key_len, key.length() * 8); + EXPECT_EQ(test_info.iv_len, iv.length() * 8); + EXPECT_EQ(test_info.pt_len, pt.length() * 8); + EXPECT_EQ(test_info.aad_len, aad.length() * 8); + EXPECT_EQ(test_info.pt_len, ct.length() * 8); + EXPECT_EQ(test_info.tag_len, tag.length() * 8); Aes128Gcm12Encrypter encrypter; ASSERT_TRUE(encrypter.SetKey(key)); scoped_ptr<QuicData> encrypted(EncryptWithNonce( &encrypter, iv, - // OpenSSL fails if NULL is set as the AAD, as opposed to a - // zero-length, non-NULL pointer. This deliberately tests that we - // handle this case. - aad.size() ? aad : StringPiece(), pt)); + // This deliberately tests that the encrypter can handle an AAD that + // is set to NULL, as opposed to a zero-length, non-NULL pointer. + aad.length() ? aad : StringPiece(), pt)); ASSERT_TRUE(encrypted.get()); // The test vectors have 16 byte authenticators but this code only uses // the first 12. ASSERT_LE(static_cast<size_t>(Aes128Gcm12Encrypter::kAuthTagSize), - tag.size()); - size_t tag_len = Aes128Gcm12Encrypter::kAuthTagSize; + tag.length()); + tag.resize(Aes128Gcm12Encrypter::kAuthTagSize); - ASSERT_EQ(ct.size() + tag_len, encrypted->length()); + ASSERT_EQ(ct.length() + tag.length(), encrypted->length()); test::CompareCharArraysWithHexError("ciphertext", encrypted->data(), - ct.size(), ct.data(), ct.size()); + ct.length(), ct.data(), ct.length()); test::CompareCharArraysWithHexError( - "authentication tag", encrypted->data() + ct.size(), tag_len, - tag.data(), tag_len); + "authentication tag", encrypted->data() + ct.length(), tag.length(), + tag.data(), tag.length()); } } } diff --git a/chromium/net/quic/crypto/cert_compressor.h b/chromium/net/quic/crypto/cert_compressor.h index 7b7e2f0eb05..d95c5bce540 100644 --- a/chromium/net/quic/crypto/cert_compressor.h +++ b/chromium/net/quic/crypto/cert_compressor.h @@ -48,6 +48,9 @@ class NET_EXPORT_PRIVATE CertCompressor { const std::vector<std::string>& cached_certs, const CommonCertSets* common_sets, std::vector<std::string>* out_certs); + + private: + DISALLOW_COPY_AND_ASSIGN(CertCompressor); }; } // namespace net diff --git a/chromium/net/quic/crypto/chacha20_poly1305_decrypter.h b/chromium/net/quic/crypto/chacha20_poly1305_decrypter.h new file mode 100644 index 00000000000..9d24ba2d13e --- /dev/null +++ b/chromium/net/quic/crypto/chacha20_poly1305_decrypter.h @@ -0,0 +1,47 @@ +// 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_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_ +#define NET_QUIC_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_ + +#include "net/quic/crypto/aead_base_decrypter.h" + +namespace net { + +// A ChaCha20Poly1305Decrypter is a QuicDecrypter that implements the +// AEAD_CHACHA20_POLY1305 algorithm specified in +// draft-agl-tls-chacha20poly1305-04, except that it truncates the Poly1305 +// authenticator to 12 bytes. Create an instance by calling +// QuicDecrypter::Create(kCC12). +// +// It uses an authentication tag of 16 bytes (128 bits). There is no +// fixed nonce prefix. +class NET_EXPORT_PRIVATE ChaCha20Poly1305Decrypter : public AeadBaseDecrypter { + public: + enum { + kAuthTagSize = 12, + }; + + ChaCha20Poly1305Decrypter(); + virtual ~ChaCha20Poly1305Decrypter(); + + // Returns true if the underlying crypto library supports ChaCha20+Poly1305. + static bool IsSupported(); + +#if !defined(USE_OPENSSL) + protected: + // AeadBaseDecrypter methods: + virtual void FillAeadParams(base::StringPiece nonce, + base::StringPiece associated_data, + size_t auth_tag_size, + AeadParams* aead_params) const OVERRIDE; +#endif + + private: + DISALLOW_COPY_AND_ASSIGN(ChaCha20Poly1305Decrypter); +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_ diff --git a/chromium/net/quic/crypto/chacha20_poly1305_decrypter_nss.cc b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_nss.cc new file mode 100644 index 00000000000..e8100af49f6 --- /dev/null +++ b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_nss.cc @@ -0,0 +1,80 @@ +// 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/crypto/chacha20_poly1305_decrypter.h" + +#include <pk11pub.h> + +#include "base/logging.h" + +using base::StringPiece; + +namespace net { + +namespace { + +const size_t kKeySize = 32; +const size_t kNoncePrefixSize = 0; + +} // namespace + +#if defined(USE_NSS) + +// System NSS doesn't support ChaCha20+Poly1305 yet. + +ChaCha20Poly1305Decrypter::ChaCha20Poly1305Decrypter() + : AeadBaseDecrypter(CKM_INVALID_MECHANISM, NULL, kKeySize, + kAuthTagSize, kNoncePrefixSize) { + NOTIMPLEMENTED(); +} + +ChaCha20Poly1305Decrypter::~ChaCha20Poly1305Decrypter() {} + +// static +bool ChaCha20Poly1305Decrypter::IsSupported() { + return false; +} + +void ChaCha20Poly1305Decrypter::FillAeadParams(StringPiece nonce, + StringPiece associated_data, + size_t auth_tag_size, + AeadParams* aead_params) const { + NOTIMPLEMENTED(); +} + +#else // defined(USE_NSS) + +ChaCha20Poly1305Decrypter::ChaCha20Poly1305Decrypter() + : AeadBaseDecrypter(CKM_NSS_CHACHA20_POLY1305, PK11_Decrypt, kKeySize, + kAuthTagSize, kNoncePrefixSize) { + COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big); + COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize, + nonce_prefix_size_too_big); +} + +ChaCha20Poly1305Decrypter::~ChaCha20Poly1305Decrypter() {} + +// static +bool ChaCha20Poly1305Decrypter::IsSupported() { + return true; +} + +void ChaCha20Poly1305Decrypter::FillAeadParams(StringPiece nonce, + StringPiece associated_data, + size_t auth_tag_size, + AeadParams* aead_params) const { + aead_params->len = sizeof(aead_params->data.nss_aead_params); + CK_NSS_AEAD_PARAMS* nss_aead_params = &aead_params->data.nss_aead_params; + nss_aead_params->pIv = + reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data())); + nss_aead_params->ulIvLen = nonce.size(); + nss_aead_params->pAAD = + reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data())); + nss_aead_params->ulAADLen = associated_data.size(); + nss_aead_params->ulTagLen = auth_tag_size; +} + +#endif // defined(USE_NSS) + +} // namespace net diff --git a/chromium/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc new file mode 100644 index 00000000000..7f0e24da704 --- /dev/null +++ b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc @@ -0,0 +1,31 @@ +// 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/crypto/chacha20_poly1305_decrypter.h" + +#include <openssl/evp.h> + +namespace net { + +namespace { + +const size_t kKeySize = 32; +const size_t kNoncePrefixSize = 0; + +} // namespace + +ChaCha20Poly1305Decrypter::ChaCha20Poly1305Decrypter() + : AeadBaseDecrypter(EVP_aead_chacha20_poly1305(), kKeySize, kAuthTagSize, + kNoncePrefixSize) { + COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big); + COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize, + nonce_prefix_size_too_big); +} + +ChaCha20Poly1305Decrypter::~ChaCha20Poly1305Decrypter() {} + +// static +bool ChaCha20Poly1305Decrypter::IsSupported() { return true; } + +} // namespace net diff --git a/chromium/net/quic/crypto/chacha20_poly1305_decrypter_test.cc b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_test.cc new file mode 100644 index 00000000000..1825187bb33 --- /dev/null +++ b/chromium/net/quic/crypto/chacha20_poly1305_decrypter_test.cc @@ -0,0 +1,132 @@ +// 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/crypto/chacha20_poly1305_decrypter.h" + +#include "net/quic/test_tools/quic_test_utils.h" + +using base::StringPiece; + +namespace { + +// The test vectors come from draft-agl-tls-chacha20poly1305-04 Section 7. + +// Each test vector consists of six strings of lowercase hexadecimal digits. +// The strings may be empty (zero length). A test vector with a NULL |key| +// marks the end of an array of test vectors. +struct TestVector { + // Input: + const char* key; + const char* iv; + const char* aad; + const char* ct; + + // Expected output: + const char* pt; // An empty string "" means decryption succeeded and + // the plaintext is zero-length. NULL means decryption + // failed. +}; + +const TestVector test_vectors[] = { + { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110" + "0a1007", + "cd7cf67be39c794a", + "87e229d4500845a079c0", + "e3e446f7ede9a19b62a4677dabf4e3d24b876bb28475", // "3896e1d6" truncated. + "86d09974840bded2a5ca" + }, + // Modify the ciphertext (ChaCha20 encryption output). + { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110" + "0a1007", + "cd7cf67be39c794a", + "87e229d4500845a079c0", + "f3e446f7ede9a19b62a4677dabf4e3d24b876bb28475", // "3896e1d6" truncated. + NULL // FAIL + }, + // Modify the ciphertext (Poly1305 authenticator). + { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110" + "0a1007", + "cd7cf67be39c794a", + "87e229d4500845a079c0", + "e3e446f7ede9a19b62a4677dabf4e3d24b876bb28476", // "3896e1d6" truncated. + NULL // FAIL + }, + // Modify the associated data. + { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110" + "0a1007", + "dd7cf67be39c794a", + "87e229d4500845a079c0", + "e3e446f7ede9a19b62a4677dabf4e3d24b876bb28475", // "3896e1d6" truncated. + NULL // FAIL + }, + { NULL } +}; + +} // namespace + +namespace net { +namespace test { + +// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing +// in an nonce and also to allocate the buffer needed for the plaintext. +QuicData* DecryptWithNonce(ChaCha20Poly1305Decrypter* decrypter, + StringPiece nonce, + StringPiece associated_data, + StringPiece ciphertext) { + size_t plaintext_size = ciphertext.length(); + scoped_ptr<char[]> plaintext(new char[plaintext_size]); + + if (!decrypter->Decrypt(nonce, associated_data, ciphertext, + reinterpret_cast<unsigned char*>(plaintext.get()), + &plaintext_size)) { + return NULL; + } + return new QuicData(plaintext.release(), plaintext_size, true); +} + +TEST(ChaCha20Poly1305DecrypterTest, Decrypt) { + if (!ChaCha20Poly1305Decrypter::IsSupported()) { + LOG(INFO) << "ChaCha20+Poly1305 not supported. Test skipped."; + return; + } + + for (size_t i = 0; test_vectors[i].key != NULL; i++) { + // If not present then decryption is expected to fail. + bool has_pt = test_vectors[i].pt; + + // Decode the test vector. + string key; + string iv; + string aad; + string ct; + string pt; + ASSERT_TRUE(DecodeHexString(test_vectors[i].key, &key)); + ASSERT_TRUE(DecodeHexString(test_vectors[i].iv, &iv)); + ASSERT_TRUE(DecodeHexString(test_vectors[i].aad, &aad)); + ASSERT_TRUE(DecodeHexString(test_vectors[i].ct, &ct)); + if (has_pt) { + ASSERT_TRUE(DecodeHexString(test_vectors[i].pt, &pt)); + } + + ChaCha20Poly1305Decrypter decrypter; + ASSERT_TRUE(decrypter.SetKey(key)); + scoped_ptr<QuicData> decrypted(DecryptWithNonce( + &decrypter, iv, + // This deliberately tests that the decrypter can handle an AAD that + // is set to NULL, as opposed to a zero-length, non-NULL pointer. + StringPiece(aad.length() ? aad.data() : NULL, aad.length()), ct)); + if (!decrypted.get()) { + EXPECT_FALSE(has_pt); + continue; + } + EXPECT_TRUE(has_pt); + + ASSERT_EQ(pt.length(), decrypted->length()); + test::CompareCharArraysWithHexError("plaintext", decrypted->data(), + pt.length(), pt.data(), pt.length()); + } +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/crypto/chacha20_poly1305_encrypter.h b/chromium/net/quic/crypto/chacha20_poly1305_encrypter.h new file mode 100644 index 00000000000..4a68caa531b --- /dev/null +++ b/chromium/net/quic/crypto/chacha20_poly1305_encrypter.h @@ -0,0 +1,47 @@ +// 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_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_ +#define NET_QUIC_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_ + +#include "net/quic/crypto/aead_base_encrypter.h" + +namespace net { + +// A ChaCha20Poly1305Encrypter is a QuicEncrypter that implements the +// AEAD_CHACHA20_POLY1305 algorithm specified in +// draft-agl-tls-chacha20poly1305-04, except that it truncates the Poly1305 +// authenticator to 12 bytes. Create an instance by calling +// QuicEncrypter::Create(kCC12). +// +// It uses an authentication tag of 16 bytes (128 bits). There is no +// fixed nonce prefix. +class NET_EXPORT_PRIVATE ChaCha20Poly1305Encrypter : public AeadBaseEncrypter { + public: + enum { + kAuthTagSize = 12, + }; + + ChaCha20Poly1305Encrypter(); + virtual ~ChaCha20Poly1305Encrypter(); + + // Returns true if the underlying crypto library supports ChaCha20+Poly1305. + static bool IsSupported(); + +#if !defined(USE_OPENSSL) + protected: + // AeadBaseEncrypter methods: + virtual void FillAeadParams(base::StringPiece nonce, + base::StringPiece associated_data, + size_t auth_tag_size, + AeadParams* aead_params) const OVERRIDE; +#endif + + private: + DISALLOW_COPY_AND_ASSIGN(ChaCha20Poly1305Encrypter); +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_ diff --git a/chromium/net/quic/crypto/chacha20_poly1305_encrypter_nss.cc b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_nss.cc new file mode 100644 index 00000000000..fd6f76ca52f --- /dev/null +++ b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_nss.cc @@ -0,0 +1,80 @@ +// 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/crypto/chacha20_poly1305_encrypter.h" + +#include <pk11pub.h> + +#include "base/logging.h" + +using base::StringPiece; + +namespace net { + +namespace { + +const size_t kKeySize = 32; +const size_t kNoncePrefixSize = 0; + +} // namespace + +#if defined(USE_NSS) + +// System NSS doesn't support ChaCha20+Poly1305 yet. + +ChaCha20Poly1305Encrypter::ChaCha20Poly1305Encrypter() + : AeadBaseEncrypter(CKM_INVALID_MECHANISM, NULL, kKeySize, + kAuthTagSize, kNoncePrefixSize) { + NOTIMPLEMENTED(); +} + +ChaCha20Poly1305Encrypter::~ChaCha20Poly1305Encrypter() {} + +// static +bool ChaCha20Poly1305Encrypter::IsSupported() { + return false; +} + +void ChaCha20Poly1305Encrypter::FillAeadParams(StringPiece nonce, + StringPiece associated_data, + size_t auth_tag_size, + AeadParams* aead_params) const { + NOTIMPLEMENTED(); +} + +#else // defined(USE_NSS) + +ChaCha20Poly1305Encrypter::ChaCha20Poly1305Encrypter() + : AeadBaseEncrypter(CKM_NSS_CHACHA20_POLY1305, PK11_Encrypt, kKeySize, + kAuthTagSize, kNoncePrefixSize) { + COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big); + COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize, + nonce_prefix_size_too_big); +} + +ChaCha20Poly1305Encrypter::~ChaCha20Poly1305Encrypter() {} + +// static +bool ChaCha20Poly1305Encrypter::IsSupported() { + return true; +} + +void ChaCha20Poly1305Encrypter::FillAeadParams(StringPiece nonce, + StringPiece associated_data, + size_t auth_tag_size, + AeadParams* aead_params) const { + aead_params->len = sizeof(aead_params->data.nss_aead_params); + CK_NSS_AEAD_PARAMS* nss_aead_params = &aead_params->data.nss_aead_params; + nss_aead_params->pIv = + reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data())); + nss_aead_params->ulIvLen = nonce.size(); + nss_aead_params->pAAD = + reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data())); + nss_aead_params->ulAADLen = associated_data.size(); + nss_aead_params->ulTagLen = auth_tag_size; +} + +#endif // defined(USE_NSS) + +} // namespace net diff --git a/chromium/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc new file mode 100644 index 00000000000..e256c2a5807 --- /dev/null +++ b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc @@ -0,0 +1,31 @@ +// 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/crypto/chacha20_poly1305_encrypter.h" + +#include <openssl/evp.h> + +namespace net { + +namespace { + +const size_t kKeySize = 32; +const size_t kNoncePrefixSize = 0; + +} // namespace + +ChaCha20Poly1305Encrypter::ChaCha20Poly1305Encrypter() + : AeadBaseEncrypter(EVP_aead_chacha20_poly1305(), kKeySize, kAuthTagSize, + kNoncePrefixSize) { + COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big); + COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize, + nonce_prefix_size_too_big); +} + +ChaCha20Poly1305Encrypter::~ChaCha20Poly1305Encrypter() {} + +// static +bool ChaCha20Poly1305Encrypter::IsSupported() { return true; } + +} // namespace net diff --git a/chromium/net/quic/crypto/chacha20_poly1305_encrypter_test.cc b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_test.cc new file mode 100644 index 00000000000..0273b27933c --- /dev/null +++ b/chromium/net/quic/crypto/chacha20_poly1305_encrypter_test.cc @@ -0,0 +1,108 @@ +// 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/crypto/chacha20_poly1305_encrypter.h" + +#include "net/quic/test_tools/quic_test_utils.h" + +using base::StringPiece; + +namespace { + +// The test vectors come from draft-agl-tls-chacha20poly1305-04 Section 7. + +// Each test vector consists of five strings of lowercase hexadecimal digits. +// The strings may be empty (zero length). A test vector with a NULL |key| +// marks the end of an array of test vectors. +struct TestVector { + const char* key; + const char* pt; + const char* iv; + const char* aad; + const char* ct; +}; + +const TestVector test_vectors[] = { + { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110" + "0a1007", + "86d09974840bded2a5ca", + "cd7cf67be39c794a", + "87e229d4500845a079c0", + "e3e446f7ede9a19b62a4677dabf4e3d24b876bb28475" // "3896e1d6" truncated. + }, + { NULL } +}; + +} // namespace + +namespace net { +namespace test { + +// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing +// in an nonce and also to allocate the buffer needed for the ciphertext. +QuicData* EncryptWithNonce(ChaCha20Poly1305Encrypter* encrypter, + StringPiece nonce, + StringPiece associated_data, + StringPiece plaintext) { + size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length()); + scoped_ptr<char[]> ciphertext(new char[ciphertext_size]); + + if (!encrypter->Encrypt(nonce, associated_data, plaintext, + reinterpret_cast<unsigned char*>(ciphertext.get()))) { + return NULL; + } + + return new QuicData(ciphertext.release(), ciphertext_size, true); +} + +TEST(ChaCha20Poly1305EncrypterTest, Encrypt) { + if (!ChaCha20Poly1305Encrypter::IsSupported()) { + LOG(INFO) << "ChaCha20+Poly1305 not supported. Test skipped."; + return; + } + + for (size_t i = 0; test_vectors[i].key != NULL; i++) { + // Decode the test vector. + string key; + string pt; + string iv; + string aad; + string ct; + ASSERT_TRUE(DecodeHexString(test_vectors[i].key, &key)); + ASSERT_TRUE(DecodeHexString(test_vectors[i].pt, &pt)); + ASSERT_TRUE(DecodeHexString(test_vectors[i].iv, &iv)); + ASSERT_TRUE(DecodeHexString(test_vectors[i].aad, &aad)); + ASSERT_TRUE(DecodeHexString(test_vectors[i].ct, &ct)); + + ChaCha20Poly1305Encrypter encrypter; + ASSERT_TRUE(encrypter.SetKey(key)); + scoped_ptr<QuicData> encrypted(EncryptWithNonce( + &encrypter, iv, + // This deliberately tests that the encrypter can handle an AAD that + // is set to NULL, as opposed to a zero-length, non-NULL pointer. + StringPiece(aad.length() ? aad.data() : NULL, aad.length()), pt)); + ASSERT_TRUE(encrypted.get()); + + test::CompareCharArraysWithHexError("ciphertext", encrypted->data(), + encrypted->length(), ct.data(), + ct.length()); + } +} + +TEST(ChaCha20Poly1305EncrypterTest, GetMaxPlaintextSize) { + ChaCha20Poly1305Encrypter encrypter; + EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012)); + EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112)); + EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22)); +} + +TEST(ChaCha20Poly1305EncrypterTest, GetCiphertextSize) { + ChaCha20Poly1305Encrypter encrypter; + EXPECT_EQ(1012u, encrypter.GetCiphertextSize(1000)); + EXPECT_EQ(112u, encrypter.GetCiphertextSize(100)); + EXPECT_EQ(22u, encrypter.GetCiphertextSize(10)); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/crypto/channel_id.h b/chromium/net/quic/crypto/channel_id.h index 2d0c29de25a..7d29932406b 100644 --- a/chromium/net/quic/crypto/channel_id.h +++ b/chromium/net/quic/crypto/channel_id.h @@ -7,28 +7,59 @@ #include <string> +#include "base/memory/scoped_ptr.h" #include "base/strings/string_piece.h" #include "net/base/net_export.h" +#include "net/quic/quic_types.h" namespace net { -// ChannelIDSigner is an abstract interface that implements signing by -// ChannelID keys. -class NET_EXPORT_PRIVATE ChannelIDSigner { +// ChannelIDKey is an interface that supports signing with and serializing a +// ChannelID key. +class NET_EXPORT_PRIVATE ChannelIDKey { public: - virtual ~ChannelIDSigner() { } - - // Sign signs |signed_data| using the ChannelID key for |hostname| and puts - // the serialized public key into |out_key| and the signature into - // |out_signature|. It returns true on success. - virtual bool Sign(const std::string& hostname, - base::StringPiece signed_data, - std::string* out_key, - std::string* out_signature) = 0; - - // GetKeyForHostname returns the ChannelID key that |ChannelIDSigner| will use - // for the given hostname. - virtual std::string GetKeyForHostname(const std::string& hostname) = 0; + virtual ~ChannelIDKey() {} + + // Sign signs |signed_data| using the ChannelID private key and puts the + // signature into |out_signature|. It returns true on success. + virtual bool Sign(base::StringPiece signed_data, + std::string* out_signature) const = 0; + + // SerializeKey returns the serialized ChannelID public key. + virtual std::string SerializeKey() const = 0; +}; + +// ChannelIDSourceCallback provides a generic mechanism for a ChannelIDSource +// to call back after an asynchronous GetChannelIDKey operation. +class ChannelIDSourceCallback { + public: + virtual ~ChannelIDSourceCallback() {} + + // Run is called on the original thread to mark the completion of an + // asynchonous GetChannelIDKey operation. If |*channel_id_key| is not NULL + // then the channel ID lookup is successful. |Run| may take ownership of + // |*channel_id_key| by calling |release| on it. + virtual void Run(scoped_ptr<ChannelIDKey>* channel_id_key) = 0; +}; + +// ChannelIDSource is an abstract interface by which a QUIC client can obtain +// a ChannelIDKey for a given hostname. +class NET_EXPORT_PRIVATE ChannelIDSource { + public: + virtual ~ChannelIDSource() {} + + // GetChannelIDKey looks up the ChannelIDKey for |hostname|. On success it + // returns QUIC_SUCCESS and stores the ChannelIDKey in |*channel_id_key|, + // which the caller takes ownership of. On failure, it returns QUIC_FAILURE. + // + // This function may also return QUIC_PENDING, in which case the + // ChannelIDSource will call back, on the original thread, via |callback| + // when complete. In this case, the ChannelIDSource will take ownership of + // |callback|. + virtual QuicAsyncStatus GetChannelIDKey( + const std::string& hostname, + scoped_ptr<ChannelIDKey>* channel_id_key, + ChannelIDSourceCallback* callback) = 0; }; // ChannelIDVerifier verifies ChannelID signatures. @@ -57,6 +88,9 @@ class NET_EXPORT_PRIVATE ChannelIDVerifier { base::StringPiece signed_data, base::StringPiece signature, bool is_channel_id_signature); + + private: + DISALLOW_COPY_AND_ASSIGN(ChannelIDVerifier); }; } // namespace net diff --git a/chromium/net/quic/crypto/channel_id_test.cc b/chromium/net/quic/crypto/channel_id_test.cc index 48319ec334b..711060c9d7f 100644 --- a/chromium/net/quic/crypto/channel_id_test.cc +++ b/chromium/net/quic/crypto/channel_id_test.cc @@ -220,16 +220,20 @@ TEST(ChannelIDTest, VerifyKnownAnswerTest) { } TEST(ChannelIDTest, SignAndVerify) { - scoped_ptr<ChannelIDSigner> signer( - CryptoTestUtils::ChannelIDSignerForTesting()); + scoped_ptr<ChannelIDSource> source( + CryptoTestUtils::ChannelIDSourceForTesting()); const string signed_data = "signed data"; const string hostname = "foo.example.com"; - string key, signature; - ASSERT_TRUE(signer->Sign(hostname, signed_data, &key, &signature)); + scoped_ptr<ChannelIDKey> channel_id_key; + QuicAsyncStatus status = + source->GetChannelIDKey(hostname, &channel_id_key, NULL); + ASSERT_EQ(QUIC_SUCCESS, status); - EXPECT_EQ(key, signer->GetKeyForHostname(hostname)); + string signature; + ASSERT_TRUE(channel_id_key->Sign(signed_data, &signature)); + string key = channel_id_key->SerializeKey(); EXPECT_TRUE(ChannelIDVerifier::Verify(key, signed_data, signature)); EXPECT_FALSE(ChannelIDVerifier::Verify("a" + key, signed_data, signature)); diff --git a/chromium/net/quic/crypto/crypto_framer.cc b/chromium/net/quic/crypto/crypto_framer.cc index dd5d24f9ae2..5cb16743e69 100644 --- a/chromium/net/quic/crypto/crypto_framer.cc +++ b/chromium/net/quic/crypto/crypto_framer.cc @@ -4,7 +4,7 @@ #include "net/quic/crypto/crypto_framer.h" -#include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/crypto/crypto_protocol.h" #include "net/quic/quic_data_reader.h" #include "net/quic/quic_data_writer.h" @@ -24,22 +24,21 @@ const size_t kNumEntriesSize = sizeof(uint16); // OneShotVisitor is a framer visitor that records a single handshake message. class OneShotVisitor : public CryptoFramerVisitorInterface { public: - explicit OneShotVisitor(CryptoHandshakeMessage* out) - : out_(out), - error_(false) { - } + OneShotVisitor() : error_(false) {} virtual void OnError(CryptoFramer* framer) OVERRIDE { error_ = true; } virtual void OnHandshakeMessage( const CryptoHandshakeMessage& message) OVERRIDE { - *out_ = message; + out_.reset(new CryptoHandshakeMessage(message)); } bool error() const { return error_; } + CryptoHandshakeMessage* release() { return out_.release(); } + private: - CryptoHandshakeMessage* const out_; + scoped_ptr<CryptoHandshakeMessage> out_; bool error_; }; @@ -56,8 +55,7 @@ CryptoFramer::~CryptoFramer() {} // static CryptoHandshakeMessage* CryptoFramer::ParseMessage(StringPiece in) { - scoped_ptr<CryptoHandshakeMessage> msg(new CryptoHandshakeMessage); - OneShotVisitor visitor(msg.get()); + OneShotVisitor visitor; CryptoFramer framer; framer.set_visitor(&visitor); @@ -66,7 +64,7 @@ CryptoHandshakeMessage* CryptoFramer::ParseMessage(StringPiece in) { return NULL; } - return msg.release(); + return visitor.release(); } bool CryptoFramer::ProcessInput(StringPiece input) { diff --git a/chromium/net/quic/crypto/crypto_framer.h b/chromium/net/quic/crypto/crypto_framer.h index ea69f3a5724..7ce714a666b 100644 --- a/chromium/net/quic/crypto/crypto_framer.h +++ b/chromium/net/quic/crypto/crypto_framer.h @@ -10,11 +10,9 @@ #include "base/basictypes.h" #include "base/logging.h" -#include "base/memory/scoped_ptr.h" #include "base/strings/string_piece.h" #include "net/base/net_export.h" -#include "net/quic/crypto/crypto_handshake.h" -#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/crypto/crypto_handshake_message.h" #include "net/quic/quic_protocol.h" namespace net { diff --git a/chromium/net/quic/crypto/crypto_handshake.cc b/chromium/net/quic/crypto/crypto_handshake.cc index 056272e9c94..408b76bdecf 100644 --- a/chromium/net/quic/crypto/crypto_handshake.cc +++ b/chromium/net/quic/crypto/crypto_handshake.cc @@ -4,323 +4,13 @@ #include "net/quic/crypto/crypto_handshake.h" -#include <ctype.h> - -#include "base/memory/scoped_ptr.h" -#include "base/strings/stringprintf.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_split.h" -#include "crypto/secure_hash.h" -#include "net/base/net_util.h" #include "net/quic/crypto/common_cert_set.h" -#include "net/quic/crypto/crypto_framer.h" #include "net/quic/crypto/key_exchange.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" -#include "net/quic/crypto/quic_random.h" -#include "net/quic/quic_protocol.h" -#include "net/quic/quic_utils.h" - -using base::StringPiece; -using base::StringPrintf; -using std::string; -using std::vector; namespace net { -CryptoHandshakeMessage::CryptoHandshakeMessage() - : tag_(0), - minimum_size_(0) {} - -CryptoHandshakeMessage::CryptoHandshakeMessage( - const CryptoHandshakeMessage& other) - : tag_(other.tag_), - tag_value_map_(other.tag_value_map_), - minimum_size_(other.minimum_size_) { - // Don't copy serialized_. scoped_ptr doesn't have a copy constructor. - // The new object can lazily reconstruct serialized_. -} - -CryptoHandshakeMessage::~CryptoHandshakeMessage() {} - -CryptoHandshakeMessage& CryptoHandshakeMessage::operator=( - const CryptoHandshakeMessage& other) { - tag_ = other.tag_; - tag_value_map_ = other.tag_value_map_; - // Don't copy serialized_. scoped_ptr doesn't have an assignment operator. - // However, invalidate serialized_. - serialized_.reset(); - minimum_size_ = other.minimum_size_; - return *this; -} - -void CryptoHandshakeMessage::Clear() { - tag_ = 0; - tag_value_map_.clear(); - minimum_size_ = 0; - serialized_.reset(); -} - -const QuicData& CryptoHandshakeMessage::GetSerialized() const { - if (!serialized_.get()) { - serialized_.reset(CryptoFramer::ConstructHandshakeMessage(*this)); - } - return *serialized_.get(); -} - -void CryptoHandshakeMessage::MarkDirty() { - serialized_.reset(); -} - -void CryptoHandshakeMessage::SetTaglist(QuicTag tag, ...) { - // Warning, if sizeof(QuicTag) > sizeof(int) then this function will break - // because the terminating 0 will only be promoted to int. - COMPILE_ASSERT(sizeof(QuicTag) <= sizeof(int), - crypto_tag_may_not_be_larger_than_int_or_varargs_will_break); - - vector<QuicTag> tags; - va_list ap; - - va_start(ap, tag); - for (;;) { - QuicTag list_item = va_arg(ap, QuicTag); - if (list_item == 0) { - break; - } - tags.push_back(list_item); - } - - // Because of the way that we keep tags in memory, we can copy the contents - // of the vector and get the correct bytes in wire format. See - // crypto_protocol.h. This assumes that the system is little-endian. - SetVector(tag, tags); - - va_end(ap); -} - -void CryptoHandshakeMessage::SetStringPiece(QuicTag tag, StringPiece value) { - tag_value_map_[tag] = value.as_string(); -} - -void CryptoHandshakeMessage::Erase(QuicTag tag) { - tag_value_map_.erase(tag); -} - -QuicErrorCode CryptoHandshakeMessage::GetTaglist(QuicTag tag, - const QuicTag** out_tags, - size_t* out_len) const { - QuicTagValueMap::const_iterator it = tag_value_map_.find(tag); - QuicErrorCode ret = QUIC_NO_ERROR; - - if (it == tag_value_map_.end()) { - ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; - } else if (it->second.size() % sizeof(QuicTag) != 0) { - ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - if (ret != QUIC_NO_ERROR) { - *out_tags = NULL; - *out_len = 0; - return ret; - } - - *out_tags = reinterpret_cast<const QuicTag*>(it->second.data()); - *out_len = it->second.size() / sizeof(QuicTag); - return ret; -} - -bool CryptoHandshakeMessage::GetStringPiece(QuicTag tag, - StringPiece* out) const { - QuicTagValueMap::const_iterator it = tag_value_map_.find(tag); - if (it == tag_value_map_.end()) { - return false; - } - *out = it->second; - return true; -} - -QuicErrorCode CryptoHandshakeMessage::GetNthValue24(QuicTag tag, - unsigned index, - StringPiece* out) const { - StringPiece value; - if (!GetStringPiece(tag, &value)) { - return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; - } - - for (unsigned i = 0;; i++) { - if (value.empty()) { - return QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND; - } - if (value.size() < 3) { - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - const unsigned char* data = - reinterpret_cast<const unsigned char*>(value.data()); - size_t size = static_cast<size_t>(data[0]) | - (static_cast<size_t>(data[1]) << 8) | - (static_cast<size_t>(data[2]) << 16); - value.remove_prefix(3); - - if (value.size() < size) { - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - if (i == index) { - *out = StringPiece(value.data(), size); - return QUIC_NO_ERROR; - } - - value.remove_prefix(size); - } -} - -QuicErrorCode CryptoHandshakeMessage::GetUint16(QuicTag tag, - uint16* out) const { - return GetPOD(tag, out, sizeof(uint16)); -} - -QuicErrorCode CryptoHandshakeMessage::GetUint32(QuicTag tag, - uint32* out) const { - return GetPOD(tag, out, sizeof(uint32)); -} - -QuicErrorCode CryptoHandshakeMessage::GetUint64(QuicTag tag, - uint64* out) const { - return GetPOD(tag, out, sizeof(uint64)); -} - -size_t CryptoHandshakeMessage::size() const { - size_t ret = sizeof(QuicTag) + - sizeof(uint16) /* number of entries */ + - sizeof(uint16) /* padding */; - ret += (sizeof(QuicTag) + sizeof(uint32) /* end offset */) * - tag_value_map_.size(); - for (QuicTagValueMap::const_iterator i = tag_value_map_.begin(); - i != tag_value_map_.end(); ++i) { - ret += i->second.size(); - } - - return ret; -} - -void CryptoHandshakeMessage::set_minimum_size(size_t min_bytes) { - if (min_bytes == minimum_size_) { - return; - } - serialized_.reset(); - minimum_size_ = min_bytes; -} - -size_t CryptoHandshakeMessage::minimum_size() const { - return minimum_size_; -} - -string CryptoHandshakeMessage::DebugString() const { - return DebugStringInternal(0); -} - -QuicErrorCode CryptoHandshakeMessage::GetPOD( - QuicTag tag, void* out, size_t len) const { - QuicTagValueMap::const_iterator it = tag_value_map_.find(tag); - QuicErrorCode ret = QUIC_NO_ERROR; - - if (it == tag_value_map_.end()) { - ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; - } else if (it->second.size() != len) { - ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - if (ret != QUIC_NO_ERROR) { - memset(out, 0, len); - return ret; - } - - memcpy(out, it->second.data(), len); - return ret; -} - -string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const { - string ret = string(2 * indent, ' ') + QuicUtils::TagToString(tag_) + "<\n"; - ++indent; - for (QuicTagValueMap::const_iterator it = tag_value_map_.begin(); - it != tag_value_map_.end(); ++it) { - ret += string(2 * indent, ' ') + QuicUtils::TagToString(it->first) + ": "; - - bool done = false; - switch (it->first) { - case kICSL: - case kIRTT: - case kKATO: - case kMSPC: - case kSWND: - // uint32 value - if (it->second.size() == 4) { - uint32 value; - memcpy(&value, it->second.data(), sizeof(value)); - ret += base::UintToString(value); - done = true; - } - break; - case kVERS: - // uint16 value - if (it->second.size() == 2) { - uint16 value; - memcpy(&value, it->second.data(), sizeof(value)); - ret += base::UintToString(value); - done = true; - } - break; - case kKEXS: - case kAEAD: - case kCGST: - case kPDMD: - case kVER: - // tag lists - if (it->second.size() % sizeof(QuicTag) == 0) { - for (size_t j = 0; j < it->second.size(); j += sizeof(QuicTag)) { - QuicTag tag; - memcpy(&tag, it->second.data() + j, sizeof(tag)); - if (j > 0) { - ret += ","; - } - ret += "'" + QuicUtils::TagToString(tag) + "'"; - } - done = true; - } - break; - case kSCFG: - // nested messages. - if (!it->second.empty()) { - scoped_ptr<CryptoHandshakeMessage> msg( - CryptoFramer::ParseMessage(it->second)); - if (msg.get()) { - ret += "\n"; - ret += msg->DebugStringInternal(indent + 1); - - done = true; - } - } - break; - case kPAD: - ret += StringPrintf("(%d bytes of padding)", - static_cast<int>(it->second.size())); - done = true; - break; - } - - if (!done) { - // If there's no specific format for this tag, or the value is invalid, - // then just use hex. - ret += "0x" + base::HexEncode(it->second.data(), it->second.size()); - } - ret += "\n"; - } - --indent; - ret += string(2 * indent, ' ') + ">"; - return ret; -} - QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters() : key_exchange(0), aead(0) { diff --git a/chromium/net/quic/crypto/crypto_handshake.h b/chromium/net/quic/crypto/crypto_handshake.h index 962e13336ef..fec53935326 100644 --- a/chromium/net/quic/crypto/crypto_handshake.h +++ b/chromium/net/quic/crypto/crypto_handshake.h @@ -5,143 +5,19 @@ #ifndef NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_H_ #define NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_H_ -#include <map> #include <string> #include <vector> #include "base/memory/scoped_ptr.h" -#include "base/strings/string_piece.h" #include "net/base/net_export.h" -#include "net/cert/cert_verify_result.h" -#include "net/cert/x509_certificate.h" -#include "net/quic/crypto/crypto_protocol.h" -#include "net/quic/crypto/proof_verifier.h" #include "net/quic/quic_protocol.h" namespace net { -class ChannelIDSigner; class CommonCertSets; class KeyExchange; -class ProofVerifier; class QuicDecrypter; class QuicEncrypter; -class QuicRandom; - -// An intermediate format of a handshake message that's convenient for a -// CryptoFramer to serialize from or parse into. -class NET_EXPORT_PRIVATE CryptoHandshakeMessage { - public: - CryptoHandshakeMessage(); - CryptoHandshakeMessage(const CryptoHandshakeMessage& other); - ~CryptoHandshakeMessage(); - - CryptoHandshakeMessage& operator=(const CryptoHandshakeMessage& other); - - // Clears state. - void Clear(); - - // GetSerialized returns the serialized form of this message and caches the - // result. Subsequently altering the message does not invalidate the cache. - const QuicData& GetSerialized() const; - - // MarkDirty invalidates the cache created by |GetSerialized|. - void MarkDirty(); - - // SetValue sets an element with the given tag to the raw, memory contents of - // |v|. - template<class T> void SetValue(QuicTag tag, const T& v) { - tag_value_map_[tag] = - std::string(reinterpret_cast<const char*>(&v), sizeof(v)); - } - - // SetVector sets an element with the given tag to the raw contents of an - // array of elements in |v|. - template<class T> void SetVector(QuicTag tag, const std::vector<T>& v) { - if (v.empty()) { - tag_value_map_[tag] = std::string(); - } else { - tag_value_map_[tag] = std::string(reinterpret_cast<const char*>(&v[0]), - v.size() * sizeof(T)); - } - } - - // Returns the message tag. - QuicTag tag() const { return tag_; } - // Sets the message tag. - void set_tag(QuicTag tag) { tag_ = tag; } - - const QuicTagValueMap& tag_value_map() const { return tag_value_map_; } - - // SetTaglist sets an element with the given tag to contain a list of tags, - // passed as varargs. The argument list must be terminated with a 0 element. - void SetTaglist(QuicTag tag, ...); - - void SetStringPiece(QuicTag tag, base::StringPiece value); - - // Erase removes a tag/value, if present, from the message. - void Erase(QuicTag tag); - - // GetTaglist finds an element with the given tag containing zero or more - // tags. If such a tag doesn't exist, it returns false. Otherwise it sets - // |out_tags| and |out_len| to point to the array of tags and returns true. - // The array points into the CryptoHandshakeMessage and is valid only for as - // long as the CryptoHandshakeMessage exists and is not modified. - QuicErrorCode GetTaglist(QuicTag tag, const QuicTag** out_tags, - size_t* out_len) const; - - bool GetStringPiece(QuicTag tag, base::StringPiece* out) const; - - // GetNthValue24 interprets the value with the given tag to be a series of - // 24-bit, length prefixed values and it returns the subvalue with the given - // index. - QuicErrorCode GetNthValue24(QuicTag tag, - unsigned index, - base::StringPiece* out) const; - QuicErrorCode GetUint16(QuicTag tag, uint16* out) const; - QuicErrorCode GetUint32(QuicTag tag, uint32* out) const; - QuicErrorCode GetUint64(QuicTag tag, uint64* out) const; - - // size returns 4 (message tag) + 2 (uint16, number of entries) + - // (4 (tag) + 4 (end offset))*tag_value_map_.size() + ∑ value sizes. - size_t size() const; - - // set_minimum_size sets the minimum number of bytes that the message should - // consume. The CryptoFramer will add a PAD tag as needed when serializing in - // order to ensure this. Setting a value of 0 disables padding. - // - // Padding is useful in order to ensure that messages are a minimum size. A - // QUIC server can require a minimum size in order to reduce the - // amplification factor of any mirror DoS attack. - void set_minimum_size(size_t min_bytes); - - size_t minimum_size() const; - - // DebugString returns a multi-line, string representation of the message - // suitable for including in debug output. - std::string DebugString() const; - - private: - // GetPOD is a utility function for extracting a plain-old-data value. If - // |tag| exists in the message, and has a value of exactly |len| bytes then - // it copies |len| bytes of data into |out|. Otherwise |len| bytes at |out| - // are zeroed out. - // - // If used to copy integers then this assumes that the machine is - // little-endian. - QuicErrorCode GetPOD(QuicTag tag, void* out, size_t len) const; - - std::string DebugStringInternal(size_t indent) const; - - QuicTag tag_; - QuicTagValueMap tag_value_map_; - - size_t minimum_size_; - - // The serialized form of the handshake message. This member is constructed - // lasily. - mutable scoped_ptr<QuicData> serialized_; -}; // A CrypterPair contains the encrypter and decrypter for an encryption level. struct NET_EXPORT_PRIVATE CrypterPair { @@ -167,10 +43,10 @@ struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters { std::string sni; std::string client_nonce; std::string server_nonce; - // hkdf_input_suffix contains the HKDF input following the label: the GUID, - // client hello and server config. This is only populated in the client - // because only the client needs to derive the forward secure keys at a later - // time from the initial keys. + // hkdf_input_suffix contains the HKDF input following the label: the + // ConnectionId, client hello and server config. This is only populated in the + // client because only the client needs to derive the forward secure keys at a + // later time from the initial keys. std::string hkdf_input_suffix; // cached_certs contains the cached certificates that a client used when // sending a client hello. diff --git a/chromium/net/quic/crypto/crypto_handshake_message.cc b/chromium/net/quic/crypto/crypto_handshake_message.cc new file mode 100644 index 00000000000..77f7c525cf3 --- /dev/null +++ b/chromium/net/quic/crypto/crypto_handshake_message.cc @@ -0,0 +1,324 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/crypto_handshake_message.h" + +#include "base/strings/stringprintf.h" +#include "base/strings/string_number_conversions.h" +#include "net/quic/crypto/crypto_framer.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_socket_address_coder.h" +#include "net/quic/quic_utils.h" + +using base::StringPiece; +using base::StringPrintf; +using std::string; +using std::vector; + +namespace net { + +CryptoHandshakeMessage::CryptoHandshakeMessage() + : tag_(0), + minimum_size_(0) {} + +CryptoHandshakeMessage::CryptoHandshakeMessage( + const CryptoHandshakeMessage& other) + : tag_(other.tag_), + tag_value_map_(other.tag_value_map_), + minimum_size_(other.minimum_size_) { + // Don't copy serialized_. scoped_ptr doesn't have a copy constructor. + // The new object can lazily reconstruct serialized_. +} + +CryptoHandshakeMessage::~CryptoHandshakeMessage() {} + +CryptoHandshakeMessage& CryptoHandshakeMessage::operator=( + const CryptoHandshakeMessage& other) { + tag_ = other.tag_; + tag_value_map_ = other.tag_value_map_; + // Don't copy serialized_. scoped_ptr doesn't have an assignment operator. + // However, invalidate serialized_. + serialized_.reset(); + minimum_size_ = other.minimum_size_; + return *this; +} + +void CryptoHandshakeMessage::Clear() { + tag_ = 0; + tag_value_map_.clear(); + minimum_size_ = 0; + serialized_.reset(); +} + +const QuicData& CryptoHandshakeMessage::GetSerialized() const { + if (!serialized_.get()) { + serialized_.reset(CryptoFramer::ConstructHandshakeMessage(*this)); + } + return *serialized_.get(); +} + +void CryptoHandshakeMessage::MarkDirty() { + serialized_.reset(); +} + +void CryptoHandshakeMessage::SetTaglist(QuicTag tag, ...) { + // Warning, if sizeof(QuicTag) > sizeof(int) then this function will break + // because the terminating 0 will only be promoted to int. + COMPILE_ASSERT(sizeof(QuicTag) <= sizeof(int), + crypto_tag_may_not_be_larger_than_int_or_varargs_will_break); + + vector<QuicTag> tags; + va_list ap; + + va_start(ap, tag); + for (;;) { + QuicTag list_item = va_arg(ap, QuicTag); + if (list_item == 0) { + break; + } + tags.push_back(list_item); + } + + // Because of the way that we keep tags in memory, we can copy the contents + // of the vector and get the correct bytes in wire format. See + // crypto_protocol.h. This assumes that the system is little-endian. + SetVector(tag, tags); + + va_end(ap); +} + +void CryptoHandshakeMessage::SetStringPiece(QuicTag tag, StringPiece value) { + tag_value_map_[tag] = value.as_string(); +} + +void CryptoHandshakeMessage::Erase(QuicTag tag) { + tag_value_map_.erase(tag); +} + +QuicErrorCode CryptoHandshakeMessage::GetTaglist(QuicTag tag, + const QuicTag** out_tags, + size_t* out_len) const { + QuicTagValueMap::const_iterator it = tag_value_map_.find(tag); + QuicErrorCode ret = QUIC_NO_ERROR; + + if (it == tag_value_map_.end()) { + ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } else if (it->second.size() % sizeof(QuicTag) != 0) { + ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (ret != QUIC_NO_ERROR) { + *out_tags = NULL; + *out_len = 0; + return ret; + } + + *out_tags = reinterpret_cast<const QuicTag*>(it->second.data()); + *out_len = it->second.size() / sizeof(QuicTag); + return ret; +} + +bool CryptoHandshakeMessage::GetStringPiece(QuicTag tag, + StringPiece* out) const { + QuicTagValueMap::const_iterator it = tag_value_map_.find(tag); + if (it == tag_value_map_.end()) { + return false; + } + *out = it->second; + return true; +} + +QuicErrorCode CryptoHandshakeMessage::GetNthValue24(QuicTag tag, + unsigned index, + StringPiece* out) const { + StringPiece value; + if (!GetStringPiece(tag, &value)) { + return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } + + for (unsigned i = 0;; i++) { + if (value.empty()) { + return QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND; + } + if (value.size() < 3) { + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + const unsigned char* data = + reinterpret_cast<const unsigned char*>(value.data()); + size_t size = static_cast<size_t>(data[0]) | + (static_cast<size_t>(data[1]) << 8) | + (static_cast<size_t>(data[2]) << 16); + value.remove_prefix(3); + + if (value.size() < size) { + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (i == index) { + *out = StringPiece(value.data(), size); + return QUIC_NO_ERROR; + } + + value.remove_prefix(size); + } +} + +QuicErrorCode CryptoHandshakeMessage::GetUint16(QuicTag tag, + uint16* out) const { + return GetPOD(tag, out, sizeof(uint16)); +} + +QuicErrorCode CryptoHandshakeMessage::GetUint32(QuicTag tag, + uint32* out) const { + return GetPOD(tag, out, sizeof(uint32)); +} + +QuicErrorCode CryptoHandshakeMessage::GetUint64(QuicTag tag, + uint64* out) const { + return GetPOD(tag, out, sizeof(uint64)); +} + +size_t CryptoHandshakeMessage::size() const { + size_t ret = sizeof(QuicTag) + + sizeof(uint16) /* number of entries */ + + sizeof(uint16) /* padding */; + ret += (sizeof(QuicTag) + sizeof(uint32) /* end offset */) * + tag_value_map_.size(); + for (QuicTagValueMap::const_iterator i = tag_value_map_.begin(); + i != tag_value_map_.end(); ++i) { + ret += i->second.size(); + } + + return ret; +} + +void CryptoHandshakeMessage::set_minimum_size(size_t min_bytes) { + if (min_bytes == minimum_size_) { + return; + } + serialized_.reset(); + minimum_size_ = min_bytes; +} + +size_t CryptoHandshakeMessage::minimum_size() const { + return minimum_size_; +} + +string CryptoHandshakeMessage::DebugString() const { + return DebugStringInternal(0); +} + +QuicErrorCode CryptoHandshakeMessage::GetPOD( + QuicTag tag, void* out, size_t len) const { + QuicTagValueMap::const_iterator it = tag_value_map_.find(tag); + QuicErrorCode ret = QUIC_NO_ERROR; + + if (it == tag_value_map_.end()) { + ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } else if (it->second.size() != len) { + ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (ret != QUIC_NO_ERROR) { + memset(out, 0, len); + return ret; + } + + memcpy(out, it->second.data(), len); + return ret; +} + +string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const { + string ret = string(2 * indent, ' ') + QuicUtils::TagToString(tag_) + "<\n"; + ++indent; + for (QuicTagValueMap::const_iterator it = tag_value_map_.begin(); + it != tag_value_map_.end(); ++it) { + ret += string(2 * indent, ' ') + QuicUtils::TagToString(it->first) + ": "; + + bool done = false; + switch (it->first) { + case kICSL: + case kIFCW: + case kCFCW: + case kSFCW: + case kIRTT: + case kKATO: + case kMSPC: + case kSWND: + // uint32 value + if (it->second.size() == 4) { + uint32 value; + memcpy(&value, it->second.data(), sizeof(value)); + ret += base::UintToString(value); + done = true; + } + break; + case kKEXS: + case kAEAD: + case kCGST: + case kCOPT: + case kLOSS: + case kPDMD: + case kVER: + // tag lists + if (it->second.size() % sizeof(QuicTag) == 0) { + for (size_t j = 0; j < it->second.size(); j += sizeof(QuicTag)) { + QuicTag tag; + memcpy(&tag, it->second.data() + j, sizeof(tag)); + if (j > 0) { + ret += ","; + } + ret += "'" + QuicUtils::TagToString(tag) + "'"; + } + done = true; + } + break; + case kCADR: + // IP address and port + if (!it->second.empty()) { + QuicSocketAddressCoder decoder; + if (decoder.Decode(it->second.data(), it->second.size())) { + ret += IPAddressToStringWithPort(decoder.ip(), decoder.port()); + done = true; + } + } + break; + case kSCFG: + // nested messages. + if (!it->second.empty()) { + scoped_ptr<CryptoHandshakeMessage> msg( + CryptoFramer::ParseMessage(it->second)); + if (msg.get()) { + ret += "\n"; + ret += msg->DebugStringInternal(indent + 1); + + done = true; + } + } + break; + case kPAD: + ret += StringPrintf("(%d bytes of padding)", + static_cast<int>(it->second.size())); + done = true; + break; + case kUAID: + ret += it->second; + done = true; + break; + } + + if (!done) { + // If there's no specific format for this tag, or the value is invalid, + // then just use hex. + ret += "0x" + base::HexEncode(it->second.data(), it->second.size()); + } + ret += "\n"; + } + --indent; + ret += string(2 * indent, ' ') + ">"; + return ret; +} + +} // namespace net diff --git a/chromium/net/quic/crypto/crypto_handshake_message.h b/chromium/net/quic/crypto/crypto_handshake_message.h new file mode 100644 index 00000000000..fcb39307b1c --- /dev/null +++ b/chromium/net/quic/crypto/crypto_handshake_message.h @@ -0,0 +1,135 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_ +#define NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_ + +#include <string> +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "net/base/net_export.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +// An intermediate format of a handshake message that's convenient for a +// CryptoFramer to serialize from or parse into. +class NET_EXPORT_PRIVATE CryptoHandshakeMessage { + public: + CryptoHandshakeMessage(); + CryptoHandshakeMessage(const CryptoHandshakeMessage& other); + ~CryptoHandshakeMessage(); + + CryptoHandshakeMessage& operator=(const CryptoHandshakeMessage& other); + + // Clears state. + void Clear(); + + // GetSerialized returns the serialized form of this message and caches the + // result. Subsequently altering the message does not invalidate the cache. + const QuicData& GetSerialized() const; + + // MarkDirty invalidates the cache created by |GetSerialized|. + void MarkDirty(); + + // SetValue sets an element with the given tag to the raw, memory contents of + // |v|. + template<class T> void SetValue(QuicTag tag, const T& v) { + tag_value_map_[tag] = + std::string(reinterpret_cast<const char*>(&v), sizeof(v)); + } + + // SetVector sets an element with the given tag to the raw contents of an + // array of elements in |v|. + template<class T> void SetVector(QuicTag tag, const std::vector<T>& v) { + if (v.empty()) { + tag_value_map_[tag] = std::string(); + } else { + tag_value_map_[tag] = std::string(reinterpret_cast<const char*>(&v[0]), + v.size() * sizeof(T)); + } + } + + // Returns the message tag. + QuicTag tag() const { return tag_; } + // Sets the message tag. + void set_tag(QuicTag tag) { tag_ = tag; } + + const QuicTagValueMap& tag_value_map() const { return tag_value_map_; } + + // SetTaglist sets an element with the given tag to contain a list of tags, + // passed as varargs. The argument list must be terminated with a 0 element. + void SetTaglist(QuicTag tag, ...); + + void SetStringPiece(QuicTag tag, base::StringPiece value); + + // Erase removes a tag/value, if present, from the message. + void Erase(QuicTag tag); + + // GetTaglist finds an element with the given tag containing zero or more + // tags. If such a tag doesn't exist, it returns false. Otherwise it sets + // |out_tags| and |out_len| to point to the array of tags and returns true. + // The array points into the CryptoHandshakeMessage and is valid only for as + // long as the CryptoHandshakeMessage exists and is not modified. + QuicErrorCode GetTaglist(QuicTag tag, const QuicTag** out_tags, + size_t* out_len) const; + + bool GetStringPiece(QuicTag tag, base::StringPiece* out) const; + + // GetNthValue24 interprets the value with the given tag to be a series of + // 24-bit, length prefixed values and it returns the subvalue with the given + // index. + QuicErrorCode GetNthValue24(QuicTag tag, + unsigned index, + base::StringPiece* out) const; + QuicErrorCode GetUint16(QuicTag tag, uint16* out) const; + QuicErrorCode GetUint32(QuicTag tag, uint32* out) const; + QuicErrorCode GetUint64(QuicTag tag, uint64* out) const; + + // size returns 4 (message tag) + 2 (uint16, number of entries) + + // (4 (tag) + 4 (end offset))*tag_value_map_.size() + ∑ value sizes. + size_t size() const; + + // set_minimum_size sets the minimum number of bytes that the message should + // consume. The CryptoFramer will add a PAD tag as needed when serializing in + // order to ensure this. Setting a value of 0 disables padding. + // + // Padding is useful in order to ensure that messages are a minimum size. A + // QUIC server can require a minimum size in order to reduce the + // amplification factor of any mirror DoS attack. + void set_minimum_size(size_t min_bytes); + + size_t minimum_size() const; + + // DebugString returns a multi-line, string representation of the message + // suitable for including in debug output. + std::string DebugString() const; + + private: + // GetPOD is a utility function for extracting a plain-old-data value. If + // |tag| exists in the message, and has a value of exactly |len| bytes then + // it copies |len| bytes of data into |out|. Otherwise |len| bytes at |out| + // are zeroed out. + // + // If used to copy integers then this assumes that the machine is + // little-endian. + QuicErrorCode GetPOD(QuicTag tag, void* out, size_t len) const; + + std::string DebugStringInternal(size_t indent) const; + + QuicTag tag_; + QuicTagValueMap tag_value_map_; + + size_t minimum_size_; + + // The serialized form of the handshake message. This member is constructed + // lasily. + mutable scoped_ptr<QuicData> serialized_; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_ diff --git a/chromium/net/quic/crypto/crypto_protocol.h b/chromium/net/quic/crypto/crypto_protocol.h index 1971b58c543..f2e7a16399f 100644 --- a/chromium/net/quic/crypto/crypto_protocol.h +++ b/chromium/net/quic/crypto/crypto_protocol.h @@ -5,9 +5,7 @@ #ifndef NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_ #define NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_ -#include <map> #include <string> -#include <vector> #include "net/base/net_export.h" #include "net/quic/quic_protocol.h" @@ -26,71 +24,98 @@ namespace net { typedef std::string ServerConfigID; -typedef std::map<QuicTag, std::string> QuicTagValueMap; -const QuicTag kCHLO = TAG('C', 'H', 'L', 'O'); // Client hello -const QuicTag kSHLO = TAG('S', 'H', 'L', 'O'); // Server hello -const QuicTag kSCFG = TAG('S', 'C', 'F', 'G'); // Server config -const QuicTag kREJ = TAG('R', 'E', 'J', '\0'); // Reject -const QuicTag kCETV = TAG('C', 'E', 'T', 'V'); // Client encrypted tag-value - // pairs +const QuicTag kCHLO = TAG('C', 'H', 'L', 'O'); // Client hello +const QuicTag kSHLO = TAG('S', 'H', 'L', 'O'); // Server hello +const QuicTag kSCFG = TAG('S', 'C', 'F', 'G'); // Server config +const QuicTag kREJ = TAG('R', 'E', 'J', '\0'); // Reject +const QuicTag kCETV = TAG('C', 'E', 'T', 'V'); // Client encrypted tag-value + // pairs +const QuicTag kPRST = TAG('P', 'R', 'S', 'T'); // Public reset // Key exchange methods -const QuicTag kP256 = TAG('P', '2', '5', '6'); // ECDH, Curve P-256 -const QuicTag kC255 = TAG('C', '2', '5', '5'); // ECDH, Curve25519 +const QuicTag kP256 = TAG('P', '2', '5', '6'); // ECDH, Curve P-256 +const QuicTag kC255 = TAG('C', '2', '5', '5'); // ECDH, Curve25519 // AEAD algorithms -const QuicTag kNULL = TAG('N', 'U', 'L', 'N'); // null algorithm -const QuicTag kAESG = TAG('A', 'E', 'S', 'G'); // AES128 + GCM-12 +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 // 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 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 + +// Congestion control options +const QuicTag kTBBR = TAG('T', 'B', 'B', 'R'); // Reduced Buffer Bloat TCP + +// Loss detection algorithm types +const QuicTag kNACK = TAG('N', 'A', 'C', 'K'); // TCP style nack counting +const QuicTag kTIME = TAG('T', 'I', 'M', 'E'); // Time based // Proof types (i.e. certificate types) // NOTE: although it would be silly to do so, specifying both kX509 and kX59R // is allowed and is equivalent to specifying only kX509. -const QuicTag kX509 = TAG('X', '5', '0', '9'); // X.509 certificate, all key - // types -const QuicTag kX59R = TAG('X', '5', '9', 'R'); // X.509 certificate, RSA keys - // only -const QuicTag kCHID = TAG('C', 'H', 'I', 'D'); // Channel ID. +const QuicTag kX509 = TAG('X', '5', '0', '9'); // X.509 certificate, all key + // types +const QuicTag kX59R = TAG('X', '5', '9', 'R'); // X.509 certificate, RSA keys + // only +const QuicTag kCHID = TAG('C', 'H', 'I', 'D'); // Channel ID. // Client hello tags -// TODO(rch): Remove once we remove QUIC_VERSION_12. -const QuicTag kVERS = TAG('V', 'E', 'R', 'S'); // Version (obsolete) -const QuicTag kVER = TAG('V', 'E', 'R', '\0'); // Version (new) -const QuicTag kNONC = TAG('N', 'O', 'N', 'C'); // The client's nonce -const QuicTag kKEXS = TAG('K', 'E', 'X', 'S'); // Key exchange methods -const QuicTag kAEAD = TAG('A', 'E', 'A', 'D'); // Authenticated - // encryption algorithms -const QuicTag kCGST = TAG('C', 'G', 'S', 'T'); // Congestion control - // feedback types -const QuicTag kICSL = TAG('I', 'C', 'S', 'L'); // Idle connection state - // lifetime -const QuicTag kKATO = TAG('K', 'A', 'T', 'O'); // Keepalive timeout -const QuicTag kMSPC = TAG('M', 'S', 'P', 'C'); // Max streams per connection. -const QuicTag kIRTT = TAG('I', 'R', 'T', 'T'); // Estimated initial RTT in us. -const QuicTag kSWND = TAG('S', 'W', 'N', 'D'); // Server's Initial congestion - // window. -const QuicTag kSNI = TAG('S', 'N', 'I', '\0'); // Server name - // indication -const QuicTag kPUBS = TAG('P', 'U', 'B', 'S'); // Public key values -const QuicTag kSCID = TAG('S', 'C', 'I', 'D'); // Server config id -const QuicTag kORBT = TAG('O', 'B', 'I', 'T'); // Server orbit. -const QuicTag kPDMD = TAG('P', 'D', 'M', 'D'); // Proof demand. -const QuicTag kPROF = TAG('P', 'R', 'O', 'F'); // Proof (signature). -const QuicTag kCCS = TAG('C', 'C', 'S', 0); // Common certificate set -const QuicTag kCCRT = TAG('C', 'C', 'R', 'T'); // Cached certificate -const QuicTag kEXPY = TAG('E', 'X', 'P', 'Y'); // Expiry +const QuicTag kVER = TAG('V', 'E', 'R', '\0'); // Version (new) +const QuicTag kNONC = TAG('N', 'O', 'N', 'C'); // The client's nonce +const QuicTag kKEXS = TAG('K', 'E', 'X', 'S'); // Key exchange methods +const QuicTag kAEAD = TAG('A', 'E', 'A', 'D'); // Authenticated + // encryption algorithms +const QuicTag kCGST = TAG('C', 'G', 'S', 'T'); // Congestion control + // feedback types +const QuicTag kCOPT = TAG('C', 'O', 'P', 'T'); // Congestion control options +// kLOSS was 'L', 'O', 'S', 'S', but was changed from a tag vector to a tag. +const QuicTag kLOSS = TAG('L', 'O', 'S', 'A'); // Loss detection algorithms +const QuicTag kICSL = TAG('I', 'C', 'S', 'L'); // Idle connection state + // lifetime +const QuicTag kKATO = TAG('K', 'A', 'T', 'O'); // Keepalive timeout +const QuicTag kMSPC = TAG('M', 'S', 'P', 'C'); // Max streams per connection. +const QuicTag kIRTT = TAG('I', 'R', 'T', 'T'); // Estimated initial RTT in us. +const QuicTag kSWND = TAG('S', 'W', 'N', 'D'); // Server's Initial congestion + // window. +const QuicTag kSNI = TAG('S', 'N', 'I', '\0'); // Server name + // indication +const QuicTag kPUBS = TAG('P', 'U', 'B', 'S'); // Public key values +const QuicTag kSCID = TAG('S', 'C', 'I', 'D'); // Server config id +const QuicTag kORBT = TAG('O', 'B', 'I', 'T'); // Server orbit. +const QuicTag kPDMD = TAG('P', 'D', 'M', 'D'); // Proof demand. +const QuicTag kPROF = TAG('P', 'R', 'O', 'F'); // Proof (signature). +const QuicTag kCCS = TAG('C', 'C', 'S', 0); // Common certificate set +const QuicTag kCCRT = TAG('C', 'C', 'R', 'T'); // Cached certificate +const QuicTag kEXPY = TAG('E', 'X', 'P', 'Y'); // Expiry +// TODO(rjshade): Remove kIFCW when removing QUIC_VERSION_19. +const QuicTag kIFCW = TAG('I', 'F', 'C', 'W'); // Initial flow control receive + // window. +const QuicTag kSFCW = TAG('S', 'F', 'C', 'W'); // Initial stream flow control + // receive window. +const QuicTag kCFCW = TAG('C', 'F', 'C', 'W'); // Initial session/connection + // flow control receive window. +const QuicTag kUAID = TAG('U', 'A', 'I', 'D'); // Client's User Agent ID. + +// Server hello tags +const QuicTag kCADR = TAG('C', 'A', 'D', 'R'); // Client IP address and port // CETV tags -const QuicTag kCIDK = TAG('C', 'I', 'D', 'K'); // ChannelID key -const QuicTag kCIDS = TAG('C', 'I', 'D', 'S'); // ChannelID signature +const QuicTag kCIDK = TAG('C', 'I', 'D', 'K'); // ChannelID key +const QuicTag kCIDS = TAG('C', 'I', 'D', 'S'); // ChannelID signature + +// Public reset tags +const QuicTag kRNON = TAG('R', 'N', 'O', 'N'); // Public reset nonce proof +const QuicTag kRSEQ = TAG('R', 'S', 'E', 'Q'); // Rejected sequence number // Universal tags -const QuicTag kPAD = TAG('P', 'A', 'D', '\0'); // Padding +const QuicTag kPAD = TAG('P', 'A', 'D', '\0'); // Padding + +// Reasons for server sending rejection message tag. +const QuicTag kRREJ = TAG('R', 'R', 'E', 'J'); // These tags have a special form so that they appear either at the beginning // or the end of a handshake message. Since handshake messages are sorted by @@ -135,12 +160,6 @@ const char kProofSignatureLabel[] = "QUIC server config signature"; // rejection message. const size_t kClientHelloMinimumSize = 1024; -// kClientHelloMinimumSizeOld is the previous value of kClientHelloMinimumSize. -// To support old clients, the server only enforces this size. -// TODO(wtc): Replace it with kClientHelloMinimumSize when we drop support for -// QUIC_VERSION_12 clients. -const size_t kClientHelloMinimumSizeOld = 512; - } // namespace net #endif // NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_ diff --git a/chromium/net/quic/crypto/crypto_secret_boxer.cc b/chromium/net/quic/crypto/crypto_secret_boxer.cc index 73562c638bc..5d8a11b27a3 100644 --- a/chromium/net/quic/crypto/crypto_secret_boxer.cc +++ b/chromium/net/quic/crypto/crypto_secret_boxer.cc @@ -6,8 +6,9 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" -#include "crypto/secure_hash.h" -#include "crypto/sha2.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/crypto/quic_decrypter.h" +#include "net/quic/crypto/quic_encrypter.h" #include "net/quic/crypto/quic_random.h" using base::StringPiece; @@ -19,21 +20,36 @@ namespace net { static const size_t kKeySize = 16; // kBoxNonceSize contains the number of bytes of nonce that we use in each box. -static const size_t kBoxNonceSize = 16; +// TODO(rtenneti): Add support for kBoxNonceSize to be 16 bytes. +// +// From agl@: +// 96-bit nonces are on the edge. An attacker who can collect 2^41 +// source-address tokens has a 1% chance of finding a duplicate. +// +// The "average" DDoS is now 32.4M PPS. That's 2^25 source-address tokens +// per second. So one day of that DDoS botnot would reach the 1% mark. +// +// It's not terrible, but it's not a "forget about it" margin. +static const size_t kBoxNonceSize = 12; // static size_t CryptoSecretBoxer::GetKeySize() { return kKeySize; } void CryptoSecretBoxer::SetKey(StringPiece key) { - DCHECK_EQ(static_cast<size_t>(kKeySize), key.size()); + DCHECK_EQ(kKeySize, key.size()); key_ = key.as_string(); } -// TODO(rtenneti): Delete sha256 based code. Use Aes128Gcm12Encrypter to Box the -// plaintext. This is temporary solution for tests to pass. string CryptoSecretBoxer::Box(QuicRandom* rand, StringPiece plaintext) const { + scoped_ptr<QuicEncrypter> encrypter(QuicEncrypter::Create(kAESG)); + if (!encrypter->SetKey(key_)) { + DLOG(DFATAL) << "CryptoSecretBoxer's encrypter->SetKey failed."; + return string(); + } + size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length()); + string ret; - const size_t len = kBoxNonceSize + plaintext.size() + crypto::kSHA256Length; + const size_t len = kBoxNonceSize + ciphertext_size; ret.resize(len); char* data = &ret[0]; @@ -41,49 +57,43 @@ string CryptoSecretBoxer::Box(QuicRandom* rand, StringPiece plaintext) const { rand->RandBytes(data, kBoxNonceSize); memcpy(data + kBoxNonceSize, plaintext.data(), plaintext.size()); - // Compute sha256 for nonce + plaintext. - scoped_ptr<crypto::SecureHash> sha256(crypto::SecureHash::Create( - crypto::SecureHash::SHA256)); - sha256->Update(data, kBoxNonceSize + plaintext.size()); - sha256->Finish(data + kBoxNonceSize + plaintext.size(), - crypto::kSHA256Length); + if (!encrypter->Encrypt(StringPiece(data, kBoxNonceSize), StringPiece(), + plaintext, reinterpret_cast<unsigned char*>( + data + kBoxNonceSize))) { + DLOG(DFATAL) << "CryptoSecretBoxer's Encrypt failed."; + return string(); + } return ret; } -// TODO(rtenneti): Delete sha256 based code. Use Aes128Gcm12Decrypter to Unbox -// the plaintext. This is temporary solution for tests to pass. bool CryptoSecretBoxer::Unbox(StringPiece ciphertext, string* out_storage, StringPiece* out) const { - if (ciphertext.size() < kBoxNonceSize + crypto::kSHA256Length) { + if (ciphertext.size() < kBoxNonceSize) { return false; } - const size_t plaintext_len = - ciphertext.size() - kBoxNonceSize - crypto::kSHA256Length; - out_storage->resize(plaintext_len); + char nonce[kBoxNonceSize]; + memcpy(nonce, ciphertext.data(), kBoxNonceSize); + ciphertext.remove_prefix(kBoxNonceSize); + + size_t len = ciphertext.size(); + out_storage->resize(len); char* data = const_cast<char*>(out_storage->data()); - // Copy plaintext from ciphertext. - if (plaintext_len != 0) { - memcpy(data, ciphertext.data() + kBoxNonceSize, plaintext_len); + scoped_ptr<QuicDecrypter> decrypter(QuicDecrypter::Create(kAESG)); + if (!decrypter->SetKey(key_)) { + DLOG(DFATAL) << "CryptoSecretBoxer's decrypter->SetKey failed."; + return false; } - - // Compute sha256 for nonce + plaintext. - scoped_ptr<crypto::SecureHash> sha256(crypto::SecureHash::Create( - crypto::SecureHash::SHA256)); - sha256->Update(ciphertext.data(), ciphertext.size() - crypto::kSHA256Length); - char sha256_bytes[crypto::kSHA256Length]; - sha256->Finish(sha256_bytes, sizeof(sha256_bytes)); - - // Verify sha256. - if (0 != memcmp(ciphertext.data() + ciphertext.size() - crypto::kSHA256Length, - sha256_bytes, crypto::kSHA256Length)) { + if (!decrypter->Decrypt(StringPiece(nonce, kBoxNonceSize), StringPiece(), + ciphertext, reinterpret_cast<unsigned char*>(data), + &len)) { return false; } - out->set(data, plaintext_len); + out->set(data, len); return true; } diff --git a/chromium/net/quic/crypto/crypto_secret_boxer.h b/chromium/net/quic/crypto/crypto_secret_boxer.h index ba9baf2bb2a..38b8fb339ec 100644 --- a/chromium/net/quic/crypto/crypto_secret_boxer.h +++ b/chromium/net/quic/crypto/crypto_secret_boxer.h @@ -19,6 +19,8 @@ class QuicRandom; // thread-safe. class NET_EXPORT_PRIVATE CryptoSecretBoxer { public: + CryptoSecretBoxer() {} + // GetKeySize returns the number of bytes in a key. static size_t GetKeySize(); @@ -42,6 +44,8 @@ class NET_EXPORT_PRIVATE CryptoSecretBoxer { private: std::string key_; + + DISALLOW_COPY_AND_ASSIGN(CryptoSecretBoxer); }; } // namespace net diff --git a/chromium/net/quic/crypto/crypto_server_config_protobuf.cc b/chromium/net/quic/crypto/crypto_server_config_protobuf.cc index a3418e4228a..d292f9e9db9 100644 --- a/chromium/net/quic/crypto/crypto_server_config_protobuf.cc +++ b/chromium/net/quic/crypto/crypto_server_config_protobuf.cc @@ -10,7 +10,8 @@ namespace net { QuicServerConfigProtobuf::QuicServerConfigProtobuf() - : primary_time_(QuicWallTime::Zero().ToUNIXSeconds()) { + : primary_time_(QuicWallTime::Zero().ToUNIXSeconds()), + priority_(0) { } QuicServerConfigProtobuf::~QuicServerConfigProtobuf() { diff --git a/chromium/net/quic/crypto/crypto_server_config_protobuf.h b/chromium/net/quic/crypto/crypto_server_config_protobuf.h index 6340ae023c4..57ebfb04847 100644 --- a/chromium/net/quic/crypto/crypto_server_config_protobuf.h +++ b/chromium/net/quic/crypto/crypto_server_config_protobuf.h @@ -61,7 +61,7 @@ class NET_EXPORT_PRIVATE QuicServerConfigProtobuf { } void set_config(base::StringPiece config) { - config_ = config.as_string(); + config.CopyToString(&config_); } QuicServerConfigProtobuf::PrivateKey* add_key() { @@ -85,6 +85,32 @@ class NET_EXPORT_PRIVATE QuicServerConfigProtobuf { primary_time_ = primary_time; } + bool has_priority() const { + return priority_ > 0; + } + + uint64 priority() const { + return priority_; + } + + void set_priority(int64 priority) { + priority_ = priority; + } + + bool has_source_address_token_secret_override() const { + return !source_address_token_secret_override_.empty(); + } + + std::string source_address_token_secret_override() const { + return source_address_token_secret_override_; + } + + void set_source_address_token_secret_override( + base::StringPiece source_address_token_secret_override) { + source_address_token_secret_override.CopyToString( + &source_address_token_secret_override_); + } + private: std::vector<PrivateKey*> keys_; @@ -94,6 +120,18 @@ class NET_EXPORT_PRIVATE QuicServerConfigProtobuf { // primary_time_ contains a UNIX epoch seconds value that indicates when this // config should become primary. int64 primary_time_; + + // Relative priority of this config vs other configs with the same + // primary time. For use as a secondary sort key when selecting the + // primary config. + uint64 priority_; + + // Optional override to the secret used to box/unbox source address + // tokens when talking to clients that select this server config. + // It can be of any length as it is fed into a KDF before use. + std::string source_address_token_secret_override_; + + DISALLOW_COPY_AND_ASSIGN(QuicServerConfigProtobuf); }; } // namespace net diff --git a/chromium/net/quic/crypto/crypto_server_test.cc b/chromium/net/quic/crypto/crypto_server_test.cc index 4eec2a86a28..7cb7e990fa2 100644 --- a/chromium/net/quic/crypto/crypto_server_test.cc +++ b/chromium/net/quic/crypto/crypto_server_test.cc @@ -2,33 +2,94 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <ostream> +#include <vector> + +#include "base/basictypes.h" #include "base/strings/string_number_conversions.h" #include "crypto/secure_hash.h" #include "net/quic/crypto/crypto_utils.h" #include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/crypto/quic_random.h" +#include "net/quic/quic_flags.h" +#include "net/quic/quic_socket_address_coder.h" #include "net/quic/quic_utils.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/delayed_verify_strike_register_client.h" #include "net/quic/test_tools/mock_clock.h" #include "net/quic/test_tools/mock_random.h" +#include "net/quic/test_tools/quic_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" using base::StringPiece; +using std::ostream; using std::string; +using std::vector; namespace net { namespace test { -class CryptoServerTest : public ::testing::Test { +class QuicCryptoServerConfigPeer { + public: + explicit QuicCryptoServerConfigPeer(QuicCryptoServerConfig* server_config) + : server_config_(server_config) {} + + base::Lock* GetStrikeRegisterClientLock() { + return &server_config_->strike_register_client_lock_; + } + + private: + QuicCryptoServerConfig* server_config_; +}; + +// Run tests with combinations of +// {FLAGS_use_early_return_when_verifying_chlo, +// FLAGS_send_quic_crypto_reject_reason}. +struct TestParams { + TestParams(bool use_early_return_when_verifying_chlo, + bool send_quic_crypto_reject_reason) + : use_early_return_when_verifying_chlo( + use_early_return_when_verifying_chlo), + send_quic_crypto_reject_reason(send_quic_crypto_reject_reason) { + } + + friend ostream& operator<<(ostream& os, const TestParams& p) { + os << "{ use_early_return_when_verifying_chlo: " + << p.use_early_return_when_verifying_chlo + << " send_quic_crypto_reject_reason: " + << p.send_quic_crypto_reject_reason << " }"; + return os; + } + + bool use_early_return_when_verifying_chlo; + bool send_quic_crypto_reject_reason; +}; + +// Constructs various test permutations. +vector<TestParams> GetTestParams() { + vector<TestParams> params; + params.push_back(TestParams(false, false)); + params.push_back(TestParams(false, true)); + params.push_back(TestParams(true, false)); + params.push_back(TestParams(true, true)); + return params; +} + +class CryptoServerTest : public ::testing::TestWithParam<TestParams> { public: CryptoServerTest() : rand_(QuicRandom::GetInstance()), - config_(QuicCryptoServerConfig::TESTING, rand_), - addr_(ParseIPLiteralToNumber("192.0.2.33", &ip_) ? - ip_ : IPAddressNumber(), 1) { + client_address_(Loopback4(), 1234), + config_(QuicCryptoServerConfig::TESTING, rand_) { config_.SetProofSource(CryptoTestUtils::ProofSourceForTesting()); supported_versions_ = QuicSupportedVersions(); + client_version_ = QuicUtils::TagToString( + QuicVersionToQuicTag(supported_versions_.front())); + + FLAGS_use_early_return_when_verifying_chlo = + GetParam().use_early_return_when_verifying_chlo; + FLAGS_send_quic_crypto_reject_reason = + GetParam().send_quic_crypto_reject_reason; } virtual void SetUp() { @@ -54,12 +115,17 @@ class CryptoServerTest : public ::testing::Test { "KEXS", "C255", "PUBS", pub_hex_.c_str(), "NONC", nonce_hex_.c_str(), + "VER\0", client_version_.data(), "$padding", static_cast<int>(kClientHelloMinimumSize), NULL); ShouldSucceed(client_hello); // The message should be rejected because the source-address token is // missing. ASSERT_EQ(kREJ, out_.tag()); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE + }; + CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); StringPiece srct; ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &srct)); @@ -91,6 +157,15 @@ class CryptoServerTest : public ::testing::Test { virtual void RunImpl(const CryptoHandshakeMessage& client_hello, const Result& result) OVERRIDE { + { + // Ensure that the strike register client lock is not held. + QuicCryptoServerConfigPeer peer(&test_->config_); + base::Lock* m = peer.GetStrikeRegisterClientLock(); + // In Chromium, we will dead lock if the lock is held by the current + // thread. Chromium doesn't have AssertNotHeld API call. + // m->AssertNotHeld(); + base::AutoLock lock(*m); + } ASSERT_FALSE(*called_); test_->ProcessValidationResult( client_hello, result, should_succeed_, error_substr_); @@ -104,17 +179,33 @@ class CryptoServerTest : public ::testing::Test { bool* called_; }; + void CheckServerHello(const CryptoHandshakeMessage& server_hello) { + const QuicTag* versions; + size_t num_versions; + server_hello.GetTaglist(kVER, &versions, &num_versions); + ASSERT_EQ(QuicSupportedVersions().size(), num_versions); + for (size_t i = 0; i < num_versions; ++i) { + EXPECT_EQ(QuicVersionToQuicTag(QuicSupportedVersions()[i]), versions[i]); + } + + StringPiece address; + ASSERT_TRUE(server_hello.GetStringPiece(kCADR, &address)); + QuicSocketAddressCoder decoder; + ASSERT_TRUE(decoder.Decode(address.data(), address.size())); + EXPECT_EQ(client_address_.address(), decoder.ip()); + EXPECT_EQ(client_address_.port(), decoder.port()); + } + void ShouldSucceed(const CryptoHandshakeMessage& message) { bool called = false; - ShouldSucceed(message, &called); + RunValidate(message, new ValidateCallback(this, true, "", &called)); EXPECT_TRUE(called); } - void ShouldSucceed(const CryptoHandshakeMessage& message, - bool* called) { - config_.ValidateClientHello( - message, addr_, &clock_, - new ValidateCallback(this, true, "", called)); + void RunValidate( + const CryptoHandshakeMessage& message, + ValidateClientHelloResultCallback* cb) { + config_.ValidateClientHello(message, client_address_, &clock_, cb); } void ShouldFailMentioning(const char* error_substr, @@ -128,7 +219,7 @@ class CryptoServerTest : public ::testing::Test { const CryptoHandshakeMessage& message, bool* called) { config_.ValidateClientHello( - message, addr_, &clock_, + message, client_address_, &clock_, new ValidateCallback(this, false, error_substr, called)); } @@ -138,7 +229,7 @@ class CryptoServerTest : public ::testing::Test { const char* error_substr) { string error_details; QuicErrorCode error = config_.ProcessClientHello( - result, 1 /* GUID */, addr_, + result, 1 /* ConnectionId */, client_address_, supported_versions_.front(), supported_versions_, &clock_, rand_, ¶ms_, &out_, &error_details); @@ -176,16 +267,39 @@ class CryptoServerTest : public ::testing::Test { return nonce; } + void CheckRejectReasons( + const HandshakeFailureReason* expected_handshake_failures, + size_t expected_count) { + const QuicTag* reject_reason_tags; + size_t num_reject_reasons; + QuicErrorCode error_code = out_.GetTaglist(kRREJ, &reject_reason_tags, + &num_reject_reasons); + if (!FLAGS_send_quic_crypto_reject_reason) { + ASSERT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error_code); + return; + } + ASSERT_EQ(QUIC_NO_ERROR, error_code); + + if (FLAGS_use_early_return_when_verifying_chlo) { + EXPECT_EQ(1u, num_reject_reasons); + } else { + EXPECT_EQ(expected_count, num_reject_reasons); + } + for (size_t i = 0; i < num_reject_reasons; ++i) { + EXPECT_EQ(expected_handshake_failures[i], reject_reason_tags[i]); + } + } + protected: QuicRandom* const rand_; MockClock clock_; + const IPEndPoint client_address_; QuicVersionVector supported_versions_; + string client_version_; QuicCryptoServerConfig config_; QuicCryptoServerConfig::ConfigOptions config_options_; QuicCryptoNegotiatedParameters params_; CryptoHandshakeMessage out_; - IPAddressNumber ip_; - IPEndPoint addr_; uint8 orbit_[kOrbitSize]; // These strings contain hex escaped values from the server suitable for @@ -194,7 +308,14 @@ class CryptoServerTest : public ::testing::Test { scoped_ptr<CryptoHandshakeMessage> server_config_; }; -TEST_F(CryptoServerTest, BadSNI) { +// Run all CryptoServerTest with all combinations of +// FLAGS_use_early_return_when_verifying_chlo and +// FLAGS_send_quic_crypto_reject_reason. +INSTANTIATE_TEST_CASE_P(CryptoServerTests, + CryptoServerTest, + ::testing::ValuesIn(GetTestParams())); + +TEST_P(CryptoServerTest, BadSNI) { static const char* kBadSNIs[] = { "", "foo", @@ -204,11 +325,19 @@ TEST_F(CryptoServerTest, BadSNI) { "ffee::1", }; + string client_version = QuicUtils::TagToString( + QuicVersionToQuicTag(supported_versions_.front())); + for (size_t i = 0; i < arraysize(kBadSNIs); i++) { ShouldFailMentioning("SNI", InchoateClientHello( "CHLO", "SNI", kBadSNIs[i], + "VER\0", client_version.data(), NULL)); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE + }; + CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); } } @@ -226,6 +355,7 @@ TEST_F(CryptoServerTest, DISABLED_DefaultCert) { "NONC", nonce_hex_.c_str(), "$padding", static_cast<int>(kClientHelloMinimumSize), "PDMD", "X509", + "VER\0", client_version_.data(), NULL)); StringPiece cert, proof; @@ -233,15 +363,24 @@ TEST_F(CryptoServerTest, DISABLED_DefaultCert) { EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof)); EXPECT_NE(0u, cert.size()); EXPECT_NE(0u, proof.size()); + const HandshakeFailureReason kRejectReasons[] = { + CLIENT_NONCE_UNKNOWN_FAILURE + }; + CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); } -TEST_F(CryptoServerTest, TooSmall) { +TEST_P(CryptoServerTest, TooSmall) { ShouldFailMentioning("too small", CryptoTestUtils::Message( "CHLO", + "VER\0", client_version_.data(), NULL)); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE + }; + CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); } -TEST_F(CryptoServerTest, BadSourceAddressToken) { +TEST_P(CryptoServerTest, BadSourceAddressToken) { // Invalid source-address tokens should be ignored. static const char* kBadSourceAddressTokens[] = { "", @@ -254,11 +393,16 @@ TEST_F(CryptoServerTest, BadSourceAddressToken) { ShouldSucceed(InchoateClientHello( "CHLO", "STK", kBadSourceAddressTokens[i], + "VER\0", client_version_.data(), NULL)); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE + }; + CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); } } -TEST_F(CryptoServerTest, BadClientNonce) { +TEST_P(CryptoServerTest, BadClientNonce) { // Invalid nonces should be ignored. static const char* kBadNonces[] = { "", @@ -270,27 +414,124 @@ TEST_F(CryptoServerTest, BadClientNonce) { ShouldSucceed(InchoateClientHello( "CHLO", "NONC", kBadNonces[i], + "VER\0", client_version_.data(), NULL)); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE + }; + CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); } } -TEST_F(CryptoServerTest, DowngradeAttack) { +TEST_P(CryptoServerTest, DowngradeAttack) { if (supported_versions_.size() == 1) { // No downgrade attack is possible if the server only supports one version. return; } // Set the client's preferred version to a supported version that // is not the "current" version (supported_versions_.front()). - string client_version = QuicUtils::TagToString( + string bad_version = QuicUtils::TagToString( QuicVersionToQuicTag(supported_versions_.back())); ShouldFailMentioning("Downgrade", InchoateClientHello( "CHLO", - "VER\0", client_version.data(), + "VER\0", bad_version.data(), NULL)); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_INCHOATE_HELLO_FAILURE + }; + CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); +} + +TEST_P(CryptoServerTest, CorruptServerConfig) { + // This tests corrupted server config. + CryptoHandshakeMessage msg = CryptoTestUtils::Message( + "CHLO", + "AEAD", "AESG", + "KEXS", "C255", + "SCID", (string(1, 'X') + scid_hex_).c_str(), + "#004b5453", srct_hex_.c_str(), + "PUBS", pub_hex_.c_str(), + "NONC", nonce_hex_.c_str(), + "VER\0", client_version_.data(), + "$padding", static_cast<int>(kClientHelloMinimumSize), + NULL); + ShouldSucceed(msg); + ASSERT_EQ(kREJ, out_.tag()); + const HandshakeFailureReason kRejectReasons[] = { + SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE + }; + CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); } -TEST_F(CryptoServerTest, ReplayProtection) { +TEST_P(CryptoServerTest, CorruptSourceAddressToken) { + // This tests corrupted source address token. + CryptoHandshakeMessage msg = CryptoTestUtils::Message( + "CHLO", + "AEAD", "AESG", + "KEXS", "C255", + "SCID", scid_hex_.c_str(), + "#004b5453", (string(1, 'X') + srct_hex_).c_str(), + "PUBS", pub_hex_.c_str(), + "NONC", nonce_hex_.c_str(), + "VER\0", client_version_.data(), + "$padding", static_cast<int>(kClientHelloMinimumSize), + NULL); + ShouldSucceed(msg); + ASSERT_EQ(kREJ, out_.tag()); + const HandshakeFailureReason kRejectReasons[] = { + SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE + }; + CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); +} + +TEST_P(CryptoServerTest, CorruptClientNonceAndSourceAddressToken) { + // This test corrupts client nonce and source address token. + CryptoHandshakeMessage msg = CryptoTestUtils::Message( + "CHLO", + "AEAD", "AESG", + "KEXS", "C255", + "SCID", scid_hex_.c_str(), + "#004b5453", (string(1, 'X') + srct_hex_).c_str(), + "PUBS", pub_hex_.c_str(), + "NONC", (string(1, 'X') + nonce_hex_).c_str(), + "VER\0", client_version_.data(), + "$padding", static_cast<int>(kClientHelloMinimumSize), + NULL); + ShouldSucceed(msg); + ASSERT_EQ(kREJ, out_.tag()); + const HandshakeFailureReason kRejectReasons[] = { + SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, + CLIENT_NONCE_INVALID_FAILURE + }; + CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); +} + +TEST_P(CryptoServerTest, CorruptMultipleTags) { + // This test corrupts client nonce, server nonce and source address token. + CryptoHandshakeMessage msg = CryptoTestUtils::Message( + "CHLO", + "AEAD", "AESG", + "KEXS", "C255", + "SCID", scid_hex_.c_str(), + "#004b5453", (string(1, 'X') + srct_hex_).c_str(), + "PUBS", pub_hex_.c_str(), + "NONC", (string(1, 'X') + nonce_hex_).c_str(), + "SNO\0", (string(1, 'X') + nonce_hex_).c_str(), + "VER\0", client_version_.data(), + "$padding", static_cast<int>(kClientHelloMinimumSize), + NULL); + ShouldSucceed(msg); + ASSERT_EQ(kREJ, out_.tag()); + const HandshakeFailureReason kRejectReasons[] = { + SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, + CLIENT_NONCE_INVALID_FAILURE, + SERVER_NONCE_DECRYPTION_FAILURE, + }; + CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); +} + +TEST_P(CryptoServerTest, ReplayProtection) { // This tests that disabling replay protection works. CryptoHandshakeMessage msg = CryptoTestUtils::Message( "CHLO", @@ -300,6 +541,7 @@ TEST_F(CryptoServerTest, ReplayProtection) { "#004b5453", srct_hex_.c_str(), "PUBS", pub_hex_.c_str(), "NONC", nonce_hex_.c_str(), + "VER\0", client_version_.data(), "$padding", static_cast<int>(kClientHelloMinimumSize), NULL); ShouldSucceed(msg); @@ -307,22 +549,22 @@ TEST_F(CryptoServerTest, ReplayProtection) { // quiescent. ASSERT_EQ(kREJ, out_.tag()); + const HandshakeFailureReason kRejectReasons[] = { + CLIENT_NONCE_UNKNOWN_FAILURE + }; + CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); + config_.set_replay_protection(false); ShouldSucceed(msg); // The message should be accepted now. ASSERT_EQ(kSHLO, out_.tag()); + CheckServerHello(out_); ShouldSucceed(msg); // The message should accepted twice when replay protection is off. ASSERT_EQ(kSHLO, out_.tag()); - const QuicTag* versions; - size_t num_versions; - out_.GetTaglist(kVER, &versions, &num_versions); - ASSERT_EQ(QuicSupportedVersions().size(), num_versions); - for (size_t i = 0; i < num_versions; ++i) { - EXPECT_EQ(QuicVersionToQuicTag(QuicSupportedVersions()[i]), versions[i]); - } + CheckServerHello(out_); } TEST(CryptoServerConfigGenerationTest, Determinism) { @@ -392,7 +634,7 @@ TEST(CryptoServerConfigGenerationTest, SCIDIsHashOfServerConfig) { hash->Finish(digest, sizeof(digest)); ASSERT_EQ(scid.size(), sizeof(digest)); - EXPECT_TRUE(0 == memcmp(digest, scid_str.data(), sizeof(digest))); + EXPECT_EQ(0, memcmp(digest, scid_str.data(), sizeof(digest))); } class CryptoServerTestNoConfig : public CryptoServerTest { @@ -402,10 +644,16 @@ class CryptoServerTestNoConfig : public CryptoServerTest { } }; -TEST_F(CryptoServerTestNoConfig, DontCrash) { - ShouldFailMentioning("No config", InchoateClientHello( - "CHLO", - NULL)); +TEST_P(CryptoServerTestNoConfig, DontCrash) { + ShouldFailMentioning("No config", InchoateClientHello( + "CHLO", + "VER\0", client_version_.data(), + NULL)); + + const HandshakeFailureReason kRejectReasons[] = { + CLIENT_NONCE_UNKNOWN_FAILURE + }; + CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons)); } class AsyncStrikeServerVerificationTest : public CryptoServerTest { @@ -430,7 +678,7 @@ class AsyncStrikeServerVerificationTest : public CryptoServerTest { DelayedVerifyStrikeRegisterClient* strike_register_client_; }; -TEST_F(AsyncStrikeServerVerificationTest, AsyncReplayProtection) { +TEST_P(AsyncStrikeServerVerificationTest, AsyncReplayProtection) { // This tests async validation with a strike register works. CryptoHandshakeMessage msg = CryptoTestUtils::Message( "CHLO", @@ -440,6 +688,7 @@ TEST_F(AsyncStrikeServerVerificationTest, AsyncReplayProtection) { "#004b5453", srct_hex_.c_str(), "PUBS", pub_hex_.c_str(), "NONC", nonce_hex_.c_str(), + "VER\0", client_version_.data(), "$padding", static_cast<int>(kClientHelloMinimumSize), NULL); @@ -447,7 +696,7 @@ TEST_F(AsyncStrikeServerVerificationTest, AsyncReplayProtection) { out_.set_tag(0); bool called = false; - ShouldSucceed(msg, &called); + RunValidate(msg, new ValidateCallback(this, true, "", &called)); // The verification request was queued. ASSERT_FALSE(called); EXPECT_EQ(0u, out_.tag()); @@ -461,7 +710,7 @@ TEST_F(AsyncStrikeServerVerificationTest, AsyncReplayProtection) { EXPECT_EQ(kSHLO, out_.tag()); // Rejected if replayed. - ShouldSucceed(msg, &called); + RunValidate(msg, new ValidateCallback(this, true, "", &called)); // The verification request was queued. ASSERT_FALSE(called); EXPECT_EQ(1, strike_register_client_->PendingVerifications()); diff --git a/chromium/net/quic/crypto/crypto_utils.cc b/chromium/net/quic/crypto/crypto_utils.cc index eec8a9d05a2..29433960489 100644 --- a/chromium/net/quic/crypto/crypto_utils.cc +++ b/chromium/net/quic/crypto/crypto_utils.cc @@ -51,7 +51,7 @@ bool CryptoUtils::IsValidSNI(StringPiece sni) { // based on the above spec, we may be losing some hostnames that windows // would consider valid. By far the most common hostname character NOT // accepted by the above spec is '_'. - url_canon::CanonHostInfo host_info; + url::CanonHostInfo host_info; string canonicalized_host(CanonicalizeHost(sni.as_string(), &host_info)); return !host_info.IsIPAddress() && IsCanonicalizedHostCompliant(canonicalized_host, std::string()) && @@ -60,7 +60,7 @@ bool CryptoUtils::IsValidSNI(StringPiece sni) { // static string CryptoUtils::NormalizeHostname(const char* hostname) { - url_canon::CanonHostInfo host_info; + url::CanonHostInfo host_info; string host(CanonicalizeHost(hostname, &host_info)); // Walk backwards over the string, stopping at the first trailing dot. diff --git a/chromium/net/quic/crypto/crypto_utils.h b/chromium/net/quic/crypto/crypto_utils.h index 147e41436f8..ec6384c131e 100644 --- a/chromium/net/quic/crypto/crypto_utils.h +++ b/chromium/net/quic/crypto/crypto_utils.h @@ -62,6 +62,9 @@ class NET_EXPORT_PRIVATE CryptoUtils { const std::string& hkdf_input, Perspective perspective, CrypterPair* out); + + private: + DISALLOW_COPY_AND_ASSIGN(CryptoUtils); }; } // namespace net diff --git a/chromium/net/quic/crypto/curve25519_key_exchange.cc b/chromium/net/quic/crypto/curve25519_key_exchange.cc index 3b8880453a3..8ed95aa26bc 100644 --- a/chromium/net/quic/crypto/curve25519_key_exchange.cc +++ b/chromium/net/quic/crypto/curve25519_key_exchange.cc @@ -4,6 +4,7 @@ #include "net/quic/crypto/curve25519_key_exchange.h" +#include "base/basictypes.h" #include "base/logging.h" #include "crypto/curve25519.h" #include "net/quic/crypto/quic_random.h" diff --git a/chromium/net/quic/crypto/local_strike_register_client.cc b/chromium/net/quic/crypto/local_strike_register_client.cc index c3751450397..1f78d9833d5 100644 --- a/chromium/net/quic/crypto/local_strike_register_client.cc +++ b/chromium/net/quic/crypto/local_strike_register_client.cc @@ -21,10 +21,12 @@ LocalStrikeRegisterClient::LocalStrikeRegisterClient( orbit, startup) { } -string LocalStrikeRegisterClient::orbit() { +bool LocalStrikeRegisterClient::IsKnownOrbit(StringPiece orbit) const { base::AutoLock lock(m_); - return string(reinterpret_cast<const char*>(strike_register_.orbit()), - kOrbitSize); + if (orbit.length() != kOrbitSize) { + return false; + } + return memcmp(orbit.data(), strike_register_.orbit(), kOrbitSize) == 0; } void LocalStrikeRegisterClient::VerifyNonceIsValidAndUnique( diff --git a/chromium/net/quic/crypto/local_strike_register_client.h b/chromium/net/quic/crypto/local_strike_register_client.h index 37020b7b420..fe8ae93d3f4 100644 --- a/chromium/net/quic/crypto/local_strike_register_client.h +++ b/chromium/net/quic/crypto/local_strike_register_client.h @@ -5,6 +5,7 @@ #ifndef NET_QUIC_CRYPTO_LOCAL_STRIKE_REGISTER_CLIENT_H_ #define NET_QUIC_CRYPTO_LOCAL_STRIKE_REGISTER_CLIENT_H_ +#include "base/basictypes.h" #include "base/strings/string_piece.h" #include "base/synchronization/lock.h" #include "net/base/net_export.h" @@ -25,13 +26,13 @@ class NET_EXPORT_PRIVATE LocalStrikeRegisterClient const uint8 orbit[8], StrikeRegister::StartupType startup); - virtual std::string orbit() OVERRIDE; + virtual bool IsKnownOrbit(base::StringPiece orbit) const OVERRIDE; virtual void VerifyNonceIsValidAndUnique(base::StringPiece nonce, QuicWallTime now, ResultCallback* cb) OVERRIDE; private: - base::Lock m_; + mutable base::Lock m_; StrikeRegister strike_register_; DISALLOW_COPY_AND_ASSIGN(LocalStrikeRegisterClient); diff --git a/chromium/net/quic/crypto/local_strike_register_client_test.cc b/chromium/net/quic/crypto/local_strike_register_client_test.cc index 0227e92c6dc..268e714d612 100644 --- a/chromium/net/quic/crypto/local_strike_register_client_test.cc +++ b/chromium/net/quic/crypto/local_strike_register_client_test.cc @@ -26,7 +26,8 @@ class RecordResultCallback : public StrikeRegisterClient::ResultCallback { // |*saved_value| and sets |*called| to true. The callback is self // deleting. RecordResultCallback(bool* called, bool* saved_value) - : called_(called), saved_value_(saved_value) { + : called_(called), + saved_value_(saved_value) { *called_ = false; } @@ -63,15 +64,21 @@ class LocalStrikeRegisterClientTest : public ::testing::Test { }; TEST_F(LocalStrikeRegisterClientTest, CheckOrbit) { - EXPECT_EQ(StringPiece(reinterpret_cast<const char*>(kOrbit), kOrbitSize), - strike_register_->orbit()); + EXPECT_TRUE(strike_register_->IsKnownOrbit( + StringPiece(reinterpret_cast<const char*>(kOrbit), kOrbitSize))); + EXPECT_FALSE(strike_register_->IsKnownOrbit( + StringPiece(reinterpret_cast<const char*>(kOrbit), kOrbitSize - 1))); + EXPECT_FALSE(strike_register_->IsKnownOrbit( + StringPiece(reinterpret_cast<const char*>(kOrbit), kOrbitSize + 1))); + EXPECT_FALSE(strike_register_->IsKnownOrbit( + StringPiece(reinterpret_cast<const char*>(kOrbit) + 1, kOrbitSize))); } TEST_F(LocalStrikeRegisterClientTest, IncorrectNonceLength) { string valid_nonce; uint32 norder = htonl(kCurrentTimeExternalSecs); valid_nonce.assign(reinterpret_cast<const char*>(&norder), sizeof(norder)); - valid_nonce.append(strike_register_->orbit()); + valid_nonce.append(string(reinterpret_cast<const char*>(kOrbit), kOrbitSize)); valid_nonce.append(string(20, '\x17')); // 20 'random' bytes. { diff --git a/chromium/net/quic/crypto/null_decrypter.h b/chromium/net/quic/crypto/null_decrypter.h index e85e1247582..2bc2fe8cd8a 100644 --- a/chromium/net/quic/crypto/null_decrypter.h +++ b/chromium/net/quic/crypto/null_decrypter.h @@ -38,6 +38,8 @@ class NET_EXPORT_PRIVATE NullDecrypter : public QuicDecrypter { private: bool ReadHash(QuicDataReader* reader, uint128* hash); uint128 ComputeHash(const std::string& data) const; + + DISALLOW_COPY_AND_ASSIGN(NullDecrypter); }; } // namespace net diff --git a/chromium/net/quic/crypto/null_encrypter.h b/chromium/net/quic/crypto/null_encrypter.h index 44e6f5555e6..1bcdff5a2c2 100644 --- a/chromium/net/quic/crypto/null_encrypter.h +++ b/chromium/net/quic/crypto/null_encrypter.h @@ -38,6 +38,8 @@ class NET_EXPORT_PRIVATE NullEncrypter : public QuicEncrypter { private: size_t GetHashLength() const; + + DISALLOW_COPY_AND_ASSIGN(NullEncrypter); }; } // namespace net diff --git a/chromium/net/quic/crypto/p256_key_exchange.h b/chromium/net/quic/crypto/p256_key_exchange.h index 8145cc05249..49a66cec3de 100644 --- a/chromium/net/quic/crypto/p256_key_exchange.h +++ b/chromium/net/quic/crypto/p256_key_exchange.h @@ -73,6 +73,8 @@ class NET_EXPORT_PRIVATE P256KeyExchange : public KeyExchange { #endif // The public key stored as an uncompressed P-256 point. uint8 public_key_[kUncompressedP256PointBytes]; + + DISALLOW_COPY_AND_ASSIGN(P256KeyExchange); }; } // namespace net diff --git a/chromium/net/quic/crypto/proof_test.cc b/chromium/net/quic/crypto/proof_test.cc index cc9e0992c1a..56e9abcfb61 100644 --- a/chromium/net/quic/crypto/proof_test.cc +++ b/chromium/net/quic/crypto/proof_test.cc @@ -24,74 +24,7 @@ using std::vector; namespace net { namespace test { - -TEST(ProofTest, Verify) { - // TODO(rtenneti): Enable testing of ProofVerifier. -#if 0 - scoped_ptr<ProofSource> source(CryptoTestUtils::ProofSourceForTesting()); - scoped_ptr<ProofVerifier> verifier( - CryptoTestUtils::ProofVerifierForTesting()); - - const string server_config = "server config bytes"; - const string hostname = "test.example.com"; - const vector<string>* certs; - const vector<string>* first_certs; - string error_details, signature, first_signature; - CertVerifyResult cert_verify_result; - - ASSERT_TRUE(source->GetProof(hostname, server_config, false /* no ECDSA */, - &first_certs, &first_signature)); - ASSERT_TRUE(source->GetProof(hostname, server_config, false /* no ECDSA */, - &certs, &signature)); - - // Check that the proof source is caching correctly: - ASSERT_EQ(first_certs, certs); - ASSERT_EQ(signature, first_signature); - - int rv; - TestCompletionCallback callback; - rv = verifier->VerifyProof(hostname, server_config, *certs, signature, - &error_details, &cert_verify_result, - callback.callback()); - rv = callback.GetResult(rv); - ASSERT_EQ(OK, rv); - ASSERT_EQ("", error_details); - ASSERT_FALSE(IsCertStatusError(cert_verify_result.cert_status)); - - rv = verifier->VerifyProof("foo.com", server_config, *certs, signature, - &error_details, &cert_verify_result, - callback.callback()); - rv = callback.GetResult(rv); - ASSERT_EQ(ERR_FAILED, rv); - ASSERT_NE("", error_details); - - rv = verifier->VerifyProof(hostname, server_config.substr(1, string::npos), - *certs, signature, &error_details, - &cert_verify_result, callback.callback()); - rv = callback.GetResult(rv); - ASSERT_EQ(ERR_FAILED, rv); - ASSERT_NE("", error_details); - - const string corrupt_signature = "1" + signature; - rv = verifier->VerifyProof(hostname, server_config, *certs, - corrupt_signature, &error_details, - &cert_verify_result, callback.callback()); - rv = callback.GetResult(rv); - ASSERT_EQ(ERR_FAILED, rv); - ASSERT_NE("", error_details); - - vector<string> wrong_certs; - for (size_t i = 1; i < certs->size(); i++) { - wrong_certs.push_back((*certs)[i]); - } - rv = verifier->VerifyProof("foo.com", server_config, wrong_certs, signature, - &error_details, &cert_verify_result, - callback.callback()); - rv = callback.GetResult(rv); - ASSERT_EQ(ERR_FAILED, rv); - ASSERT_NE("", error_details); -#endif // 0 -} +namespace { // TestProofVerifierCallback is a simple callback for a ProofVerifier that // signals a TestCompletionCallback when called and stores the results from the @@ -100,13 +33,13 @@ class TestProofVerifierCallback : public ProofVerifierCallback { public: TestProofVerifierCallback(TestCompletionCallback* comp_callback, bool* ok, - std::string* error_details) + string* error_details) : comp_callback_(comp_callback), ok_(ok), error_details_(error_details) {} virtual void Run(bool ok, - const std::string& error_details, + const string& error_details, scoped_ptr<ProofVerifyDetails>* details) OVERRIDE { *ok_ = ok; *error_details_ = error_details; @@ -117,48 +50,54 @@ class TestProofVerifierCallback : public ProofVerifierCallback { private: TestCompletionCallback* const comp_callback_; bool* const ok_; - std::string* const error_details_; + string* const error_details_; }; // RunVerification runs |verifier->VerifyProof| and asserts that the result // matches |expected_ok|. -static void RunVerification(ProofVerifier* verifier, - const std::string& hostname, - const std::string& server_config, - const vector<std::string>& certs, - const std::string& proof, - bool expected_ok) { +void RunVerification(ProofVerifier* verifier, + const string& hostname, + const string& server_config, + const vector<string>& certs, + const string& proof, + bool expected_ok) { scoped_ptr<ProofVerifyDetails> details; TestCompletionCallback comp_callback; bool ok; - std::string error_details; + string error_details; + scoped_ptr<ProofVerifyContext> verify_context( + CryptoTestUtils::ProofVerifyContextForTesting()); TestProofVerifierCallback* callback = new TestProofVerifierCallback(&comp_callback, &ok, &error_details); - ProofVerifier::Status status = verifier->VerifyProof( - hostname, server_config, certs, proof, &error_details, &details, - callback); + QuicAsyncStatus status = verifier->VerifyProof( + hostname, server_config, certs, proof, verify_context.get(), + &error_details, &details, callback); switch (status) { - case ProofVerifier::FAILURE: + case QUIC_FAILURE: + delete callback; ASSERT_FALSE(expected_ok); ASSERT_NE("", error_details); return; - case ProofVerifier::SUCCESS: + case QUIC_SUCCESS: + delete callback; ASSERT_TRUE(expected_ok); ASSERT_EQ("", error_details); return; - case ProofVerifier::PENDING: + case QUIC_PENDING: comp_callback.WaitForResult(); ASSERT_EQ(expected_ok, ok); break; } } -static string PEMCertFileToDER(const string& file_name) { +// Reads the certificate named "quic_" + |file_name| in the test data directory. +// The certificate must be PEM encoded. Returns the DER-encoded certificate. +string LoadTestCert(const string& file_name) { base::FilePath certs_dir = GetTestCertsDirectory(); scoped_refptr<X509Certificate> cert = - ImportCertFromFile(certs_dir, file_name); + ImportCertFromFile(certs_dir, "quic_" + file_name); CHECK_NE(static_cast<X509Certificate*>(NULL), cert); string der_bytes; @@ -166,12 +105,58 @@ static string PEMCertFileToDER(const string& file_name) { return der_bytes; } +} // namespace + +// TODO(rtenneti): Enable testing of ProofVerifier. +TEST(ProofTest, DISABLED_Verify) { + scoped_ptr<ProofSource> source(CryptoTestUtils::ProofSourceForTesting()); + scoped_ptr<ProofVerifier> verifier( + CryptoTestUtils::ProofVerifierForTesting()); + + const string server_config = "server config bytes"; + const string hostname = "test.example.com"; + const vector<string>* certs; + const vector<string>* first_certs; + string error_details, signature, first_signature; + + ASSERT_TRUE(source->GetProof(hostname, server_config, false /* no ECDSA */, + &first_certs, &first_signature)); + ASSERT_TRUE(source->GetProof(hostname, server_config, false /* no ECDSA */, + &certs, &signature)); + + // Check that the proof source is caching correctly: + ASSERT_EQ(first_certs, certs); + ASSERT_EQ(signature, first_signature); + + RunVerification( + verifier.get(), hostname, server_config, *certs, signature, true); + + RunVerification( + verifier.get(), "foo.com", server_config, *certs, signature, false); + + RunVerification( + verifier.get(), server_config.substr(1, string::npos), server_config, + *certs, signature, false); + + const string corrupt_signature = "1" + signature; + RunVerification( + verifier.get(), hostname, server_config, *certs, corrupt_signature, + false); + + vector<string> wrong_certs; + for (size_t i = 1; i < certs->size(); i++) { + wrong_certs.push_back((*certs)[i]); + } + RunVerification( + verifier.get(), "foo.com", server_config, wrong_certs, corrupt_signature, + false); +} + // A known answer test that allows us to test ProofVerifier without a working // ProofSource. TEST(ProofTest, VerifyRSAKnownAnswerTest) { // These sample signatures were generated by running the Proof.Verify test // and dumping the bytes of the |signature| output of ProofSource::GetProof(). - // sLen = special value -2 used by OpenSSL. static const unsigned char signature_data_0[] = { 0x31, 0xd5, 0xfb, 0x40, 0x30, 0x75, 0xd2, 0x7d, 0x61, 0xf9, 0xd7, 0x54, 0x30, 0x06, 0xaf, 0x54, 0x0d, 0xb0, 0x0a, 0xda, 0x63, 0xca, 0x7e, 0x9e, @@ -250,11 +235,10 @@ TEST(ProofTest, VerifyRSAKnownAnswerTest) { const string server_config = "server config bytes"; const string hostname = "test.example.com"; - CertVerifyResult cert_verify_result; vector<string> certs(2); - certs[0] = PEMCertFileToDER("quic_test.example.com.crt"); - certs[1] = PEMCertFileToDER("quic_intermediate.crt"); + certs[0] = LoadTestCert("test.example.com.crt"); + certs[1] = LoadTestCert("intermediate.crt"); // Signatures are nondeterministic, so we test multiple signatures on the // same server_config. @@ -333,11 +317,10 @@ TEST(ProofTest, VerifyECDSAKnownAnswerTest) { const string server_config = "server config bytes"; const string hostname = "test.example.com"; - CertVerifyResult cert_verify_result; vector<string> certs(2); - certs[0] = PEMCertFileToDER("quic_test_ecc.example.com.crt"); - certs[1] = PEMCertFileToDER("quic_intermediate.crt"); + certs[0] = LoadTestCert("test_ecc.example.com.crt"); + certs[1] = LoadTestCert("intermediate.crt"); // Signatures are nondeterministic, so we test multiple signatures on the // same server_config. diff --git a/chromium/net/quic/crypto/proof_verifier.cc b/chromium/net/quic/crypto/proof_verifier.cc deleted file mode 100644 index 7bccba2bdb8..00000000000 --- a/chromium/net/quic/crypto/proof_verifier.cc +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/crypto/proof_verifier.h" - -namespace net { - -ProofVerifyDetails::~ProofVerifyDetails() {} - -ProofVerifierCallback::~ProofVerifierCallback() {} - -ProofVerifier::~ProofVerifier() {} - -} // namespace net diff --git a/chromium/net/quic/crypto/proof_verifier.h b/chromium/net/quic/crypto/proof_verifier.h index f469c552959..83f7c4366fd 100644 --- a/chromium/net/quic/crypto/proof_verifier.h +++ b/chromium/net/quic/crypto/proof_verifier.h @@ -8,30 +8,36 @@ #include <string> #include <vector> -#include "net/base/completion_callback.h" +#include "base/memory/scoped_ptr.h" #include "net/base/net_export.h" +#include "net/quic/quic_types.h" namespace net { -class CertVerifyResult; - // ProofVerifyDetails is an abstract class that acts as a container for any // implementation specific details that a ProofVerifier wishes to return. These -// details are saved in the CachedInfo for the origin in question. -class ProofVerifyDetails { +// details are saved in the CachedState for the origin in question. +class NET_EXPORT_PRIVATE ProofVerifyDetails { + public: + virtual ~ProofVerifyDetails() {} +}; + +// ProofVerifyContext is an abstract class that acts as a container for any +// implementation specific context that a ProofVerifier needs. +class NET_EXPORT_PRIVATE ProofVerifyContext { public: - virtual ~ProofVerifyDetails(); + virtual ~ProofVerifyContext() {} }; // ProofVerifierCallback provides a generic mechanism for a ProofVerifier to // call back after an asynchronous verification. class NET_EXPORT_PRIVATE ProofVerifierCallback { public: - virtual ~ProofVerifierCallback(); + virtual ~ProofVerifierCallback() {} // Run is called on the original thread to mark the completion of an // asynchonous verification. If |ok| is true then the certificate is valid - // and |*error_details| is unused. Otherwise, |*error_details| contains a + // and |error_details| is unused. Otherwise, |error_details| contains a // description of the error. |details| contains implementation-specific // details of the verification. |Run| may take ownership of |details| by // calling |release| on it. @@ -44,39 +50,33 @@ class NET_EXPORT_PRIVATE ProofVerifierCallback { // chain that backs the public key. class NET_EXPORT_PRIVATE ProofVerifier { public: - // Status enumerates the possible results of verifying a proof. - enum Status { - SUCCESS = 0, - FAILURE = 1, - // PENDING results from a verification which will occur asynchonously. When - // the verification is complete, |callback|'s |Run| method will be called. - PENDING = 2, - }; - - virtual ~ProofVerifier(); + virtual ~ProofVerifier() {} // VerifyProof checks that |signature| is a valid signature of // |server_config| by the public key in the leaf certificate of |certs|, and // that |certs| is a valid chain for |hostname|. On success, it returns - // SUCCESS. On failure, it returns ERROR and sets |*error_details| to a - // description of the problem. In either case it may set |*details|, which the - // caller takes ownership of. + // QUIC_SUCCESS. On failure, it returns QUIC_FAILURE and sets |*error_details| + // to a description of the problem. In either case it may set |*details|, + // which the caller takes ownership of. // - // This function may also return PENDING, in which case the ProofVerifier - // will call back, on the original thread, via |callback| when complete. + // |context| specifies an implementation specific struct (which may be NULL + // for some implementations) that provides useful information for the + // verifier, e.g. logging handles. // - // This function takes ownership of |callback|. It will be deleted even if - // the call returns immediately. + // This function may also return QUIC_PENDING, in which case the ProofVerifier + // will call back, on the original thread, via |callback| when complete. + // In this case, the ProofVerifier will take ownership of |callback|. // // The signature uses SHA-256 as the hash function and PSS padding in the // case of RSA. - virtual Status VerifyProof(const std::string& hostname, - const std::string& server_config, - const std::vector<std::string>& certs, - const std::string& signature, - std::string* error_details, - scoped_ptr<ProofVerifyDetails>* details, - ProofVerifierCallback* callback) = 0; + virtual QuicAsyncStatus VerifyProof(const std::string& hostname, + const std::string& server_config, + const std::vector<std::string>& certs, + const std::string& signature, + const ProofVerifyContext* context, + std::string* error_details, + scoped_ptr<ProofVerifyDetails>* details, + ProofVerifierCallback* callback) = 0; }; } // namespace net diff --git a/chromium/net/quic/crypto/proof_verifier_chromium.cc b/chromium/net/quic/crypto/proof_verifier_chromium.cc index 68ad25ca12d..4b118da198c 100644 --- a/chromium/net/quic/crypto/proof_verifier_chromium.cc +++ b/chromium/net/quic/crypto/proof_verifier_chromium.cc @@ -9,6 +9,7 @@ #include "base/callback_helpers.h" #include "base/compiler_specific.h" #include "base/logging.h" +#include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "crypto/signature_verifier.h" #include "net/base/net_errors.h" @@ -30,37 +31,91 @@ using std::vector; namespace net { -ProofVerifierChromium::ProofVerifierChromium(CertVerifier* cert_verifier, - const BoundNetLog& net_log) - : cert_verifier_(cert_verifier), - next_state_(STATE_NONE), - net_log_(net_log) { -} +// A Job handles the verification of a single proof. It is owned by the +// ProofVerifier. If the verification can not complete synchronously, it +// will notify the ProofVerifier upon completion. +class ProofVerifierChromium::Job { + public: + Job(ProofVerifierChromium* proof_verifier, + CertVerifier* cert_verifier, + const BoundNetLog& net_log); -ProofVerifierChromium::~ProofVerifierChromium() { - verifier_.reset(); + // Starts the proof verification. If |QUIC_PENDING| is returned, then + // |callback| will be invoked asynchronously when the verification completes. + QuicAsyncStatus VerifyProof(const std::string& hostname, + const std::string& server_config, + const std::vector<std::string>& certs, + const std::string& signature, + std::string* error_details, + scoped_ptr<ProofVerifyDetails>* verify_details, + ProofVerifierCallback* callback); + + private: + enum State { + STATE_NONE, + STATE_VERIFY_CERT, + STATE_VERIFY_CERT_COMPLETE, + }; + + int DoLoop(int last_io_result); + void OnIOComplete(int result); + int DoVerifyCert(int result); + int DoVerifyCertComplete(int result); + + bool VerifySignature(const std::string& signed_data, + const std::string& signature, + const std::string& cert); + + // Proof verifier to notify when this jobs completes. + ProofVerifierChromium* proof_verifier_; + + // The underlying verifier used for verifying certificates. + scoped_ptr<SingleRequestCertVerifier> verifier_; + + // |hostname| specifies the hostname for which |certs| is a valid chain. + std::string hostname_; + + scoped_ptr<ProofVerifierCallback> callback_; + scoped_ptr<ProofVerifyDetailsChromium> verify_details_; + std::string error_details_; + + // X509Certificate from a chain of DER encoded certificates. + scoped_refptr<X509Certificate> cert_; + + State next_state_; + + BoundNetLog net_log_; + + DISALLOW_COPY_AND_ASSIGN(Job); +}; + +ProofVerifierChromium::Job::Job(ProofVerifierChromium* proof_verifier, + CertVerifier* cert_verifier, + const BoundNetLog& net_log) + : proof_verifier_(proof_verifier), + verifier_(new SingleRequestCertVerifier(cert_verifier)), + next_state_(STATE_NONE), + net_log_(net_log) { } -ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof( +QuicAsyncStatus ProofVerifierChromium::Job::VerifyProof( const string& hostname, const string& server_config, const vector<string>& certs, const string& signature, std::string* error_details, - scoped_ptr<ProofVerifyDetails>* details, + scoped_ptr<ProofVerifyDetails>* verify_details, ProofVerifierCallback* callback) { DCHECK(error_details); - DCHECK(details); + DCHECK(verify_details); DCHECK(callback); - callback_.reset(callback); error_details->clear(); - DCHECK_EQ(STATE_NONE, next_state_); if (STATE_NONE != next_state_) { *error_details = "Certificate is already set and VerifyProof has begun"; - DLOG(WARNING) << *error_details; - return FAILURE; + DLOG(DFATAL) << *error_details; + return QUIC_FAILURE; } verify_details_.reset(new ProofVerifyDetailsChromium); @@ -69,8 +124,8 @@ ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof( *error_details = "Failed to create certificate chain. Certs are empty."; DLOG(WARNING) << *error_details; verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID; - details->reset(verify_details_.release()); - return FAILURE; + verify_details->reset(verify_details_.release()); + return QUIC_FAILURE; } // Convert certs to X509Certificate. @@ -83,8 +138,8 @@ ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof( *error_details = "Failed to create certificate chain"; DLOG(WARNING) << *error_details; verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID; - details->reset(verify_details_.release()); - return FAILURE; + verify_details->reset(verify_details_.release()); + return QUIC_FAILURE; } // We call VerifySignature first to avoid copying of server_config and @@ -93,8 +148,8 @@ ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof( *error_details = "Failed to verify signature of server config"; DLOG(WARNING) << *error_details; verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID; - details->reset(verify_details_.release()); - return FAILURE; + verify_details->reset(verify_details_.release()); + return QUIC_FAILURE; } hostname_ = hostname; @@ -102,18 +157,19 @@ ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof( next_state_ = STATE_VERIFY_CERT; switch (DoLoop(OK)) { case OK: - details->reset(verify_details_.release()); - return SUCCESS; + verify_details->reset(verify_details_.release()); + return QUIC_SUCCESS; case ERR_IO_PENDING: - return PENDING; + callback_.reset(callback); + return QUIC_PENDING; default: *error_details = error_details_; - details->reset(verify_details_.release()); - return FAILURE; + verify_details->reset(verify_details_.release()); + return QUIC_FAILURE; } } -int ProofVerifierChromium::DoLoop(int last_result) { +int ProofVerifierChromium::Job::DoLoop(int last_result) { int rv = last_result; do { State state = next_state_; @@ -136,32 +192,34 @@ int ProofVerifierChromium::DoLoop(int last_result) { return rv; } -void ProofVerifierChromium::OnIOComplete(int result) { +void ProofVerifierChromium::Job::OnIOComplete(int result) { int rv = DoLoop(result); if (rv != ERR_IO_PENDING) { - scoped_ptr<ProofVerifyDetails> scoped_details(verify_details_.release()); - callback_->Run(rv == OK, error_details_, &scoped_details); - callback_.reset(); + scoped_ptr<ProofVerifierCallback> callback(callback_.release()); + // Callback expects ProofVerifyDetails not ProofVerifyDetailsChromium. + scoped_ptr<ProofVerifyDetails> verify_details(verify_details_.release()); + callback->Run(rv == OK, error_details_, &verify_details); + // Will delete |this|. + proof_verifier_->OnJobComplete(this); } } -int ProofVerifierChromium::DoVerifyCert(int result) { +int ProofVerifierChromium::Job::DoVerifyCert(int result) { next_state_ = STATE_VERIFY_CERT_COMPLETE; int flags = 0; - verifier_.reset(new SingleRequestCertVerifier(cert_verifier_)); return verifier_->Verify( cert_.get(), hostname_, flags, SSLConfigService::GetCRLSet().get(), &verify_details_->cert_verify_result, - base::Bind(&ProofVerifierChromium::OnIOComplete, + base::Bind(&ProofVerifierChromium::Job::OnIOComplete, base::Unretained(this)), net_log_); } -int ProofVerifierChromium::DoVerifyCertComplete(int result) { +int ProofVerifierChromium::Job::DoVerifyCertComplete(int result) { verifier_.reset(); if (result <= ERR_FAILED) { @@ -176,7 +234,7 @@ int ProofVerifierChromium::DoVerifyCertComplete(int result) { return result; } -bool ProofVerifierChromium::VerifySignature(const string& signed_data, +bool ProofVerifierChromium::Job::VerifySignature(const string& signed_data, const string& signature, const string& cert) { StringPiece spki; @@ -252,4 +310,41 @@ bool ProofVerifierChromium::VerifySignature(const string& signed_data, return true; } +ProofVerifierChromium::ProofVerifierChromium(CertVerifier* cert_verifier) + : cert_verifier_(cert_verifier) {} + +ProofVerifierChromium::~ProofVerifierChromium() { + STLDeleteElements(&active_jobs_); +} + +QuicAsyncStatus ProofVerifierChromium::VerifyProof( + const std::string& hostname, + const std::string& server_config, + const std::vector<std::string>& certs, + const std::string& signature, + const ProofVerifyContext* verify_context, + std::string* error_details, + scoped_ptr<ProofVerifyDetails>* verify_details, + ProofVerifierCallback* callback) { + if (!verify_context) { + *error_details = "Missing context"; + return QUIC_FAILURE; + } + const ProofVerifyContextChromium* chromium_context = + reinterpret_cast<const ProofVerifyContextChromium*>(verify_context); + scoped_ptr<Job> job(new Job(this, cert_verifier_, chromium_context->net_log)); + QuicAsyncStatus status = job->VerifyProof(hostname, server_config, certs, + signature, error_details, + verify_details, callback); + if (status == QUIC_PENDING) { + active_jobs_.insert(job.release()); + } + return status; +} + +void ProofVerifierChromium::OnJobComplete(Job* job) { + active_jobs_.erase(job); + delete job; +} + } // namespace net diff --git a/chromium/net/quic/crypto/proof_verifier_chromium.h b/chromium/net/quic/crypto/proof_verifier_chromium.h index 4969cc8aa55..6f8a23131f3 100644 --- a/chromium/net/quic/crypto/proof_verifier_chromium.h +++ b/chromium/net/quic/crypto/proof_verifier_chromium.h @@ -5,13 +5,13 @@ #ifndef NET_QUIC_CRYPTO_PROOF_VERIFIER_CHROMIUM_H_ #define NET_QUIC_CRYPTO_PROOF_VERIFIER_CHROMIUM_H_ +#include <set> #include <string> #include <vector> #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" -#include "net/base/completion_callback.h" #include "net/base/net_export.h" #include "net/base/net_log.h" #include "net/cert/cert_verify_result.h" @@ -30,56 +30,45 @@ struct ProofVerifyDetailsChromium : public ProofVerifyDetails { CertVerifyResult cert_verify_result; }; -// ProofVerifierChromium implements the QUIC ProofVerifier interface. -// TODO(rtenneti): Add support for multiple requests for one ProofVerifier. +// ProofVerifyContextChromium is the implementation-specific information that a +// ProofVerifierChromium needs in order to log correctly. +struct ProofVerifyContextChromium : public ProofVerifyContext { + public: + explicit ProofVerifyContextChromium(const BoundNetLog& net_log) + : net_log(net_log) {} + + BoundNetLog net_log; +}; + +// ProofVerifierChromium implements the QUIC ProofVerifier interface. It is +// capable of handling multiple simultaneous requests. class NET_EXPORT_PRIVATE ProofVerifierChromium : public ProofVerifier { public: - ProofVerifierChromium(CertVerifier* cert_verifier, - const BoundNetLog& net_log); + explicit ProofVerifierChromium(CertVerifier* cert_verifier); virtual ~ProofVerifierChromium(); // ProofVerifier interface - virtual Status VerifyProof(const std::string& hostname, - const std::string& server_config, - const std::vector<std::string>& certs, - const std::string& signature, - std::string* error_details, - scoped_ptr<ProofVerifyDetails>* details, - ProofVerifierCallback* callback) OVERRIDE; + virtual QuicAsyncStatus VerifyProof( + const std::string& hostname, + const std::string& server_config, + const std::vector<std::string>& certs, + const std::string& signature, + const ProofVerifyContext* verify_context, + std::string* error_details, + scoped_ptr<ProofVerifyDetails>* verify_details, + ProofVerifierCallback* callback) OVERRIDE; private: - enum State { - STATE_NONE, - STATE_VERIFY_CERT, - STATE_VERIFY_CERT_COMPLETE, - }; - - int DoLoop(int last_io_result); - void OnIOComplete(int result); - int DoVerifyCert(int result); - int DoVerifyCertComplete(int result); - - bool VerifySignature(const std::string& signed_data, - const std::string& signature, - const std::string& cert); - - // |cert_verifier_| and |verifier_| are used for verifying certificates. - CertVerifier* const cert_verifier_; - scoped_ptr<SingleRequestCertVerifier> verifier_; - - // |hostname| specifies the hostname for which |certs| is a valid chain. - std::string hostname_; + class Job; - scoped_ptr<ProofVerifierCallback> callback_; - scoped_ptr<ProofVerifyDetailsChromium> verify_details_; - std::string error_details_; + void OnJobComplete(Job* job); - // X509Certificate from a chain of DER encoded certificates. - scoped_refptr<X509Certificate> cert_; + // Set owning pointers to active jobs. + typedef std::set<Job*> JobSet; + JobSet active_jobs_; - State next_state_; - - BoundNetLog net_log_; + // Underlying verifier used to verify certificates. + CertVerifier* const cert_verifier_; DISALLOW_COPY_AND_ASSIGN(ProofVerifierChromium); }; diff --git a/chromium/net/quic/crypto/quic_crypto_client_config.cc b/chromium/net/quic/crypto/quic_crypto_client_config.cc index a3eeeb62d22..9d5e704e1df 100644 --- a/chromium/net/quic/crypto/quic_crypto_client_config.cc +++ b/chromium/net/quic/crypto/quic_crypto_client_config.cc @@ -5,7 +5,9 @@ #include "net/quic/crypto/quic_crypto_client_config.h" #include "base/stl_util.h" +#include "base/strings/string_util.h" #include "net/quic/crypto/cert_compressor.h" +#include "net/quic/crypto/chacha20_poly1305_encrypter.h" #include "net/quic/crypto/channel_id.h" #include "net/quic/crypto/common_cert_set.h" #include "net/quic/crypto/crypto_framer.h" @@ -17,18 +19,17 @@ #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/quic_utils.h" -#if defined(OS_WIN) -#include "base/win/windows_version.h" -#endif - using base::StringPiece; +using std::find; +using std::make_pair; using std::map; using std::string; using std::vector; namespace net { -QuicCryptoClientConfig::QuicCryptoClientConfig() {} +QuicCryptoClientConfig::QuicCryptoClientConfig() + : disable_ecdsa_(false) {} QuicCryptoClientConfig::~QuicCryptoClientConfig() { STLDeleteValues(&cached_states_); @@ -61,6 +62,10 @@ bool QuicCryptoClientConfig::CachedState::IsComplete(QuicWallTime now) const { return true; } +bool QuicCryptoClientConfig::CachedState::IsEmpty() const { + return server_config_.empty(); +} + const CryptoHandshakeMessage* QuicCryptoClientConfig::CachedState::GetServerConfig() const { if (server_config_.empty()) { @@ -144,6 +149,17 @@ void QuicCryptoClientConfig::CachedState::SetProof(const vector<string>& certs, server_config_sig_ = signature.as_string(); } +void QuicCryptoClientConfig::CachedState::Clear() { + server_config_.clear(); + source_address_token_.clear(); + certs_.clear(); + server_config_sig_.clear(); + server_config_valid_ = false; + proof_verify_details_.reset(); + scfg_.reset(); + ++generation_counter_; +} + void QuicCryptoClientConfig::CachedState::ClearProof() { SetProofInvalid(); certs_.clear(); @@ -159,6 +175,32 @@ void QuicCryptoClientConfig::CachedState::SetProofInvalid() { ++generation_counter_; } +bool QuicCryptoClientConfig::CachedState::Initialize( + StringPiece server_config, + StringPiece source_address_token, + const vector<string>& certs, + StringPiece signature, + QuicWallTime now) { + DCHECK(server_config_.empty()); + + if (server_config.empty()) { + return false; + } + + string error_details; + QuicErrorCode error = SetServerConfig(server_config, now, + &error_details); + if (error != QUIC_NO_ERROR) { + DVLOG(1) << "SetServerConfig failed with " << error_details; + return false; + } + + signature.CopyToString(&server_config_sig_); + source_address_token.CopyToString(&source_address_token_); + certs_ = certs; + return true; +} + const string& QuicCryptoClientConfig::CachedState::server_config() const { return server_config_; } @@ -208,6 +250,7 @@ void QuicCryptoClientConfig::CachedState::InitializeFrom( certs_ = other.certs_; server_config_sig_ = other.server_config_sig_; server_config_valid_ = other.server_config_valid_; + ++generation_counter_; } void QuicCryptoClientConfig::SetDefaults() { @@ -216,26 +259,38 @@ void QuicCryptoClientConfig::SetDefaults() { kexs[0] = kC255; kexs[1] = kP256; - // Authenticated encryption algorithms. - aead.resize(1); - aead[0] = kAESG; + // Authenticated encryption algorithms. Prefer ChaCha20 by default. + aead.clear(); + if (ChaCha20Poly1305Encrypter::IsSupported()) { + aead.push_back(kCC12); + } + aead.push_back(kAESG); + + disable_ecdsa_ = false; } QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::LookupOrCreate( - const string& server_hostname) { - map<string, CachedState*>::const_iterator it = - cached_states_.find(server_hostname); + const QuicServerId& server_id) { + CachedStateMap::const_iterator it = cached_states_.find(server_id); if (it != cached_states_.end()) { return it->second; } CachedState* cached = new CachedState; - cached_states_.insert(make_pair(server_hostname, cached)); + cached_states_.insert(make_pair(server_id, cached)); + PopulateFromCanonicalConfig(server_id, cached); return cached; } +void QuicCryptoClientConfig::ClearCachedStates() { + for (CachedStateMap::const_iterator it = cached_states_.begin(); + it != cached_states_.end(); ++it) { + it->second->Clear(); + } +} + void QuicCryptoClientConfig::FillInchoateClientHello( - const string& server_hostname, + const QuicServerId& server_id, const QuicVersion preferred_version, const CachedState* cached, QuicCryptoNegotiatedParameters* out_params, @@ -245,26 +300,21 @@ void QuicCryptoClientConfig::FillInchoateClientHello( // Server name indication. We only send SNI if it's a valid domain name, as // per the spec. - if (CryptoUtils::IsValidSNI(server_hostname)) { - out->SetStringPiece(kSNI, server_hostname); + if (CryptoUtils::IsValidSNI(server_id.host())) { + out->SetStringPiece(kSNI, server_id.host()); } - // TODO(rch): Remove once we remove QUIC_VERSION_12. - out->SetValue(kVERS, static_cast<uint16>(0)); out->SetValue(kVER, QuicVersionToQuicTag(preferred_version)); + if (!user_agent_id_.empty()) { + out->SetStringPiece(kUAID, user_agent_id_); + } + if (!cached->source_address_token().empty()) { out->SetStringPiece(kSourceAddressTokenTag, cached->source_address_token()); } - if (proof_verifier_.get()) { - // Don't request ECDSA proofs on platforms that do not support ECDSA - // certificates. - bool disableECDSA = false; -#if defined(OS_WIN) - if (base::win::GetVersion() < base::win::VERSION_VISTA) - disableECDSA = true; -#endif - if (disableECDSA) { + if (server_id.is_https()) { + if (disable_ecdsa_) { out->SetTaglist(kPDMD, kX59R, 0); } else { out->SetTaglist(kPDMD, kX509, 0); @@ -293,18 +343,19 @@ void QuicCryptoClientConfig::FillInchoateClientHello( } QuicErrorCode QuicCryptoClientConfig::FillClientHello( - const string& server_hostname, - QuicGuid guid, + const QuicServerId& server_id, + QuicConnectionId connection_id, const QuicVersion preferred_version, const CachedState* cached, QuicWallTime now, QuicRandom* rand, + const ChannelIDKey* channel_id_key, QuicCryptoNegotiatedParameters* out_params, CryptoHandshakeMessage* out, string* error_details) const { DCHECK(error_details != NULL); - FillInchoateClientHello(server_hostname, preferred_version, cached, + FillInchoateClientHello(server_id, preferred_version, cached, out_params, out); const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); @@ -333,13 +384,18 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } + // AEAD: the work loads on the client and server are symmetric. Since the + // client is more likely to be CPU-constrained, break the tie by favoring + // the client's preference. + // Key exchange: the client does more work than the server, so favor the + // client's preference. size_t key_exchange_index; if (!QuicUtils::FindMutualTag( - aead, their_aeads, num_their_aeads, QuicUtils::PEER_PRIORITY, + aead, their_aeads, num_their_aeads, QuicUtils::LOCAL_PRIORITY, &out_params->aead, NULL) || !QuicUtils::FindMutualTag( kexs, their_key_exchanges, num_their_key_exchanges, - QuicUtils::PEER_PRIORITY, &out_params->key_exchange, + QuicUtils::LOCAL_PRIORITY, &out_params->key_exchange, &key_exchange_index)) { *error_details = "Unsupported AEAD or KEXS"; return QUIC_CRYPTO_NO_SUPPORT; @@ -388,22 +444,7 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( } out->SetStringPiece(kPUBS, out_params->client_key_exchange->public_value()); - bool do_channel_id = false; - if (channel_id_signer_.get()) { - const QuicTag* their_proof_demands; - size_t num_their_proof_demands; - if (scfg->GetTaglist(kPDMD, &their_proof_demands, - &num_their_proof_demands) == QUIC_NO_ERROR) { - for (size_t i = 0; i < num_their_proof_demands; i++) { - if (their_proof_demands[i] == kCHID) { - do_channel_id = true; - break; - } - } - } - } - - if (do_channel_id) { + if (channel_id_key) { // In order to calculate the encryption key for the CETV block we need to // serialise the client hello as it currently is (i.e. without the CETV // block). For this, the client hello is serialized without padding. @@ -417,14 +458,15 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( const QuicData& client_hello_serialized = out->GetSerialized(); hkdf_input.append(QuicCryptoConfig::kCETVLabel, strlen(QuicCryptoConfig::kCETVLabel) + 1); - hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid)); + hkdf_input.append(reinterpret_cast<char*>(&connection_id), + sizeof(connection_id)); hkdf_input.append(client_hello_serialized.data(), client_hello_serialized.length()); hkdf_input.append(cached->server_config()); - string key, signature; - if (!channel_id_signer_->Sign(server_hostname, hkdf_input, - &key, &signature)) { + string key = channel_id_key->SerializeKey(); + string signature; + if (!channel_id_key->Sign(hkdf_input, &signature)) { *error_details = "Channel ID signature failed"; return QUIC_INVALID_CHANNEL_ID_SIGNATURE; } @@ -457,9 +499,13 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( out->set_minimum_size(orig_min_size); } + // Derive the symmetric keys and set up the encrypters and decrypters. + // Set the following members of out_params: + // out_params->hkdf_input_suffix + // out_params->initial_crypters out_params->hkdf_input_suffix.clear(); - out_params->hkdf_input_suffix.append(reinterpret_cast<char*>(&guid), - sizeof(guid)); + out_params->hkdf_input_suffix.append(reinterpret_cast<char*>(&connection_id), + sizeof(connection_id)); const QuicData& client_hello_serialized = out->GetSerialized(); out_params->hkdf_input_suffix.append(client_hello_serialized.data(), client_hello_serialized.length()); @@ -541,12 +587,23 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection( } } + const QuicTag* reject_reasons; + size_t num_reject_reasons; + if (rej.GetTaglist(kRREJ, &reject_reasons, + &num_reject_reasons) == QUIC_NO_ERROR) { +#if defined(DEBUG) + for (size_t i = 0; i < num_reject_reasons; ++i) { + DVLOG(1) << "Reasons for rejection: " << reject_reasons[i]; + } +#endif + } + return QUIC_NO_ERROR; } QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( const CryptoHandshakeMessage& server_hello, - QuicGuid guid, + QuicConnectionId connection_id, const QuicVersionVector& negotiated_versions, CachedState* cached, QuicCryptoNegotiatedParameters* out_params, @@ -560,23 +617,24 @@ QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( const QuicTag* supported_version_tags; size_t num_supported_versions; - // TODO(rch): Once QUIC_VERSION_12 is removed, then make it a failure - // if the server does not have a version list. + if (server_hello.GetTaglist(kVER, &supported_version_tags, - &num_supported_versions) == QUIC_NO_ERROR) { - if (!negotiated_versions.empty()) { - bool mismatch = num_supported_versions != negotiated_versions.size(); - for (size_t i = 0; i < num_supported_versions && !mismatch; ++i) { - mismatch = QuicTagToQuicVersion(supported_version_tags[i]) != - negotiated_versions[i]; - } - // The server sent a list of supported versions, and the connection - // reports that there was a version negotiation during the handshake. + &num_supported_versions) != QUIC_NO_ERROR) { + *error_details = "server hello missing version list"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + if (!negotiated_versions.empty()) { + bool mismatch = num_supported_versions != negotiated_versions.size(); + for (size_t i = 0; i < num_supported_versions && !mismatch; ++i) { + mismatch = QuicTagToQuicVersion(supported_version_tags[i]) != + negotiated_versions[i]; + } + // The server sent a list of supported versions, and the connection + // reports that there was a version negotiation during the handshake. // Ensure that these two lists are identical. - if (mismatch) { - *error_details = "Downgrade attack detected"; - return QUIC_VERSION_NEGOTIATION_MISMATCH; - } + if (mismatch) { + *error_details = "Downgrade attack detected"; + return QUIC_VERSION_NEGOTIATION_MISMATCH; } } @@ -626,25 +684,81 @@ void QuicCryptoClientConfig::SetProofVerifier(ProofVerifier* verifier) { proof_verifier_.reset(verifier); } -ChannelIDSigner* QuicCryptoClientConfig::channel_id_signer() const { - return channel_id_signer_.get(); +ChannelIDSource* QuicCryptoClientConfig::channel_id_source() const { + return channel_id_source_.get(); } -void QuicCryptoClientConfig::SetChannelIDSigner(ChannelIDSigner* signer) { - channel_id_signer_.reset(signer); +void QuicCryptoClientConfig::SetChannelIDSource(ChannelIDSource* source) { + channel_id_source_.reset(source); } void QuicCryptoClientConfig::InitializeFrom( - const std::string& server_hostname, - const std::string& canonical_server_hostname, + const QuicServerId& server_id, + const QuicServerId& canonical_server_id, QuicCryptoClientConfig* canonical_crypto_config) { CachedState* canonical_cached = - canonical_crypto_config->LookupOrCreate(canonical_server_hostname); + canonical_crypto_config->LookupOrCreate(canonical_server_id); if (!canonical_cached->proof_valid()) { return; } - CachedState* cached = LookupOrCreate(server_hostname); + CachedState* cached = LookupOrCreate(server_id); cached->InitializeFrom(*canonical_cached); } +void QuicCryptoClientConfig::AddCanonicalSuffix(const string& suffix) { + canoncial_suffixes_.push_back(suffix); +} + +void QuicCryptoClientConfig::PreferAesGcm() { + DCHECK(!aead.empty()); + if (aead.size() <= 1) { + return; + } + QuicTagVector::iterator pos = find(aead.begin(), aead.end(), kAESG); + if (pos != aead.end()) { + aead.erase(pos); + aead.insert(aead.begin(), kAESG); + } +} + +void QuicCryptoClientConfig::DisableEcdsa() { + disable_ecdsa_ = true; +} + +void QuicCryptoClientConfig::PopulateFromCanonicalConfig( + const QuicServerId& server_id, + CachedState* server_state) { + DCHECK(server_state->IsEmpty()); + size_t i = 0; + for (; i < canoncial_suffixes_.size(); ++i) { + if (EndsWith(server_id.host(), canoncial_suffixes_[i], false)) { + break; + } + } + if (i == canoncial_suffixes_.size()) + return; + + QuicServerId suffix_server_id(canoncial_suffixes_[i], server_id.port(), + server_id.is_https(), + server_id.privacy_mode()); + if (!ContainsKey(canonical_server_map_, suffix_server_id)) { + // This is the first host we've seen which matches the suffix, so make it + // canonical. + canonical_server_map_[suffix_server_id] = server_id; + return; + } + + const QuicServerId& canonical_server_id = + canonical_server_map_[suffix_server_id]; + CachedState* canonical_state = cached_states_[canonical_server_id]; + if (!canonical_state->proof_valid()) { + return; + } + + // Update canonical version to point at the "most recent" entry. + canonical_server_map_[suffix_server_id] = server_id; + + server_state->InitializeFrom(*canonical_state); +} + } // namespace net diff --git a/chromium/net/quic/crypto/quic_crypto_client_config.h b/chromium/net/quic/crypto/quic_crypto_client_config.h index 30f4cf6f09e..8aa4527bd73 100644 --- a/chromium/net/quic/crypto/quic_crypto_client_config.h +++ b/chromium/net/quic/crypto/quic_crypto_client_config.h @@ -14,9 +14,17 @@ #include "net/base/net_export.h" #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/quic_protocol.h" +#include "net/quic/quic_server_id.h" namespace net { +class ChannelIDKey; +class ChannelIDSource; +class CryptoHandshakeMessage; +class ProofVerifier; +class ProofVerifyDetails; +class QuicRandom; + // QuicCryptoClientConfig contains crypto-related configuration settings for a // client. Note that this object isn't thread-safe. It's designed to be used on // a single thread at a time. @@ -35,6 +43,9 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { // cached server config has expired. bool IsComplete(QuicWallTime now) const; + // IsEmpty returns true if |server_config_| is empty. + bool IsEmpty() const; + // GetServerConfig returns the parsed contents of |server_config|, or NULL // if |server_config| is empty. The return value is owned by this object // and is destroyed when this object is. @@ -54,6 +65,9 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { void SetProof(const std::vector<std::string>& certs, base::StringPiece signature); + // Clears all the data. + void Clear(); + // Clears the certificate chain and signature and invalidates the proof. void ClearProof(); @@ -86,8 +100,15 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { // unchanged. void InitializeFrom(const CachedState& other); + // Initializes this cached state based on the arguments provided. + // Returns false if there is a problem parsing the server config. + bool Initialize(base::StringPiece server_config, + base::StringPiece source_address_token, + const std::vector<std::string>& certs, + base::StringPiece signature, + QuicWallTime now); + private: - std::string server_config_id_; // An opaque id from the server. std::string server_config_; // A serialized handshake message. std::string source_address_token_; // An opaque proof of IP ownership. std::vector<std::string> certs_; // A list of certificates in leaf-first @@ -115,18 +136,21 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { // Sets the members to reasonable, default values. void SetDefaults(); - // LookupOrCreate returns a CachedState for the given hostname. If no such + // LookupOrCreate returns a CachedState for the given |server_id|. If no such // CachedState currently exists, it will be created and cached. - CachedState* LookupOrCreate(const std::string& server_hostname); + CachedState* LookupOrCreate(const QuicServerId& server_id); + + // Delete all CachedState objects from cached_states_. + void ClearCachedStates(); // FillInchoateClientHello sets |out| to be a CHLO message that elicits a // source-address token or SCFG from a server. If |cached| is non-NULL, the // source-address token will be taken from it. |out_params| is used in order // to store the cached certs that were sent as hints to the server in - // |out_params->cached_certs|. |preferred_version| is the version of the QUIC - // protocol that this client chose to use initially. This allows the server to - // detect downgrade attacks. - void FillInchoateClientHello(const std::string& server_hostname, + // |out_params->cached_certs|. |preferred_version| is the version of the + // QUIC protocol that this client chose to use initially. This allows the + // server to detect downgrade attacks. + void FillInchoateClientHello(const QuicServerId& server_id, const QuicVersion preferred_version, const CachedState* cached, QuicCryptoNegotiatedParameters* out_params, @@ -134,20 +158,25 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { // FillClientHello sets |out| to be a CHLO message based on the configuration // of this object. This object must have cached enough information about - // |server_hostname| in order to perform a handshake. This can be checked + // the server's hostname in order to perform a handshake. This can be checked // with the |IsComplete| member of |CachedState|. // - // |clock| and |rand| are used to generate the nonce and |out_params| is + // |now| and |rand| are used to generate the nonce and |out_params| is // filled with the results of the handshake that the server is expected to // accept. |preferred_version| is the version of the QUIC protocol that this // client chose to use initially. This allows the server to detect downgrade // attacks. - QuicErrorCode FillClientHello(const std::string& server_hostname, - QuicGuid guid, + // + // If |channel_id_key| is not null, it is used to sign a secret value derived + // from the client and server's keys, and the Channel ID public key and the + // signature are placed in the CETV value of the CHLO. + QuicErrorCode FillClientHello(const QuicServerId& server_id, + QuicConnectionId connection_id, const QuicVersion preferred_version, const CachedState* cached, QuicWallTime now, QuicRandom* rand, + const ChannelIDKey* channel_id_key, QuicCryptoNegotiatedParameters* out_params, CryptoHandshakeMessage* out, std::string* error_details) const; @@ -173,7 +202,7 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { // server. The contents of this list will be compared against the list of // versions provided in the VER tag of the server hello. QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, - QuicGuid guid, + QuicConnectionId connection_id, const QuicVersionVector& negotiated_versions, CachedState* cached, QuicCryptoNegotiatedParameters* out_params, @@ -187,28 +216,73 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { // the server. void SetProofVerifier(ProofVerifier* verifier); - ChannelIDSigner* channel_id_signer() const; + ChannelIDSource* channel_id_source() const; - // SetChannelIDSigner sets a ChannelIDSigner that will be called when the - // server supports channel IDs to sign a message proving possession of the - // given ChannelID. This object takes ownership of |signer|. - void SetChannelIDSigner(ChannelIDSigner* signer); + // SetChannelIDSource sets a ChannelIDSource that will be called, when the + // server supports channel IDs, to obtain a channel ID for signing a message + // proving possession of the channel ID. This object takes ownership of + // |source|. + void SetChannelIDSource(ChannelIDSource* source); // Initialize the CachedState from |canonical_crypto_config| for the - // |canonical_server_hostname| as the initial CachedState for - // |server_hostname|. We will copy config data only if - // |canonical_crypto_config| has valid proof. - void InitializeFrom(const std::string& server_hostname, - const std::string& canonical_server_hostname, + // |canonical_server_id| as the initial CachedState for |server_id|. We will + // copy config data only if |canonical_crypto_config| has valid proof. + void InitializeFrom(const QuicServerId& server_id, + const QuicServerId& canonical_server_id, QuicCryptoClientConfig* canonical_crypto_config); + // Adds |suffix| as a domain suffix for which the server's crypto config + // is expected to be shared among servers with the domain suffix. If a server + // matches this suffix, then the server config from another server with the + // suffix will be used to initialize the cached state for this server. + void AddCanonicalSuffix(const std::string& suffix); + + // Prefers AES-GCM (kAESG) over other AEAD algorithms. Call this method if + // the CPU has hardware acceleration for AES-GCM. This method can only be + // called after SetDefaults(). + void PreferAesGcm(); + + // Disables the use of ECDSA for proof verification. + // Call this method on platforms that do not support ECDSA. + // TODO(rch): remove this method when we drop support for Windows XP. + void DisableEcdsa(); + + // Saves the |user_agent_id| that will be passed in QUIC's CHLO message. + void set_user_agent_id(const std::string& user_agent_id) { + user_agent_id_ = user_agent_id; + } + private: - // cached_states_ maps from the server hostname to the cached information - // about that server. - std::map<std::string, CachedState*> cached_states_; + typedef std::map<QuicServerId, CachedState*> CachedStateMap; + + // If the suffix of the hostname in |server_id| is in |canoncial_suffixes_|, + // then populate |cached| with the canonical cached state from + // |canonical_server_map_| for that suffix. + void PopulateFromCanonicalConfig(const QuicServerId& server_id, + CachedState* cached); + + // cached_states_ maps from the server_id to the cached information about + // that server. + CachedStateMap cached_states_; + + // Contains a map of servers which could share the same server config. Map + // from a canonical host suffix/port/scheme to a representative server with + // the canonical suffix, which has a plausible set of initial certificates + // (or at least server public key). + std::map<QuicServerId, QuicServerId> canonical_server_map_; + + // Contains list of suffixes (for exmaple ".c.youtube.com", + // ".googlevideo.com") of canoncial hostnames. + std::vector<std::string> canoncial_suffixes_; scoped_ptr<ProofVerifier> proof_verifier_; - scoped_ptr<ChannelIDSigner> channel_id_signer_; + scoped_ptr<ChannelIDSource> channel_id_source_; + + // True if ECDSA should be disabled. + bool disable_ecdsa_; + + // The |user_agent_id_| passed in QUIC's CHLO message. + std::string user_agent_id_; DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientConfig); }; diff --git a/chromium/net/quic/crypto/quic_crypto_client_config_test.cc b/chromium/net/quic/crypto/quic_crypto_client_config_test.cc index 2893372dd1a..fdce91c1fc7 100644 --- a/chromium/net/quic/crypto/quic_crypto_client_config_test.cc +++ b/chromium/net/quic/crypto/quic_crypto_client_config_test.cc @@ -4,30 +4,131 @@ #include "net/quic/crypto/quic_crypto_client_config.h" +#include "net/quic/crypto/proof_verifier.h" +#include "net/quic/quic_server_id.h" +#include "net/quic/test_tools/mock_random.h" #include "net/quic/test_tools/quic_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" using std::string; +using std::vector; namespace net { namespace test { +TEST(QuicCryptoClientConfigTest, CachedState_IsEmpty) { + QuicCryptoClientConfig::CachedState state; + EXPECT_TRUE(state.IsEmpty()); +} + +TEST(QuicCryptoClientConfigTest, CachedState_IsComplete) { + QuicCryptoClientConfig::CachedState state; + EXPECT_FALSE(state.IsComplete(QuicWallTime::FromUNIXSeconds(0))); +} + +TEST(QuicCryptoClientConfigTest, CachedState_GenerationCounter) { + QuicCryptoClientConfig::CachedState state; + EXPECT_EQ(0u, state.generation_counter()); + state.SetProofInvalid(); + EXPECT_EQ(1u, state.generation_counter()); +} + +TEST(QuicCryptoClientConfigTest, CachedState_SetProofVerifyDetails) { + QuicCryptoClientConfig::CachedState state; + EXPECT_TRUE(state.proof_verify_details() == NULL); + ProofVerifyDetails* details = new ProofVerifyDetails; + state.SetProofVerifyDetails(details); + EXPECT_EQ(details, state.proof_verify_details()); +} + +TEST(QuicCryptoClientConfigTest, CachedState_InitializeFrom) { + QuicCryptoClientConfig::CachedState state; + QuicCryptoClientConfig::CachedState other; + state.set_source_address_token("TOKEN"); + // TODO(rch): Populate other fields of |state|. + other.InitializeFrom(state); + EXPECT_EQ(state.server_config(), other.server_config()); + EXPECT_EQ(state.source_address_token(), other.source_address_token()); + EXPECT_EQ(state.certs(), other.certs()); + EXPECT_EQ(1u, other.generation_counter()); +} + TEST(QuicCryptoClientConfigTest, InchoateChlo) { QuicCryptoClientConfig::CachedState state; QuicCryptoClientConfig config; QuicCryptoNegotiatedParameters params; CryptoHandshakeMessage msg; - config.FillInchoateClientHello("www.google.com", QuicVersionMax(), &state, + QuicServerId server_id("www.google.com", 80, false, PRIVACY_MODE_DISABLED); + config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, ¶ms, &msg); QuicTag cver; EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kVER, &cver)); EXPECT_EQ(QuicVersionToQuicTag(QuicVersionMax()), cver); +} + +TEST(QuicCryptoClientConfigTest, PreferAesGcm) { + QuicCryptoClientConfig config; + config.SetDefaults(); + if (config.aead.size() > 1) + EXPECT_NE(kAESG, config.aead[0]); + config.PreferAesGcm(); + EXPECT_EQ(kAESG, config.aead[0]); +} + +TEST(QuicCryptoClientConfigTest, InchoateChloSecure) { + QuicCryptoClientConfig::CachedState state; + QuicCryptoClientConfig config; + QuicCryptoNegotiatedParameters params; + CryptoHandshakeMessage msg; + QuicServerId server_id("www.google.com", 443, true, PRIVACY_MODE_DISABLED); + config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, + ¶ms, &msg); + + QuicTag pdmd; + EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kPDMD, &pdmd)); + EXPECT_EQ(kX509, pdmd); +} + +TEST(QuicCryptoClientConfigTest, InchoateChloSecureNoEcdsa) { + QuicCryptoClientConfig::CachedState state; + QuicCryptoClientConfig config; + config.DisableEcdsa(); + QuicCryptoNegotiatedParameters params; + CryptoHandshakeMessage msg; + QuicServerId server_id("www.google.com", 443, true, PRIVACY_MODE_DISABLED); + config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, + ¶ms, &msg); + + QuicTag pdmd; + EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kPDMD, &pdmd)); + EXPECT_EQ(kX59R, pdmd); +} + +TEST(QuicCryptoClientConfigTest, FillClientHello) { + QuicCryptoClientConfig::CachedState state; + QuicCryptoClientConfig config; + QuicCryptoNegotiatedParameters params; + QuicConnectionId kConnectionId = 1234; + string error_details; + MockRandom rand; + CryptoHandshakeMessage chlo; + QuicServerId server_id("www.google.com", 80, false, PRIVACY_MODE_DISABLED); + config.FillClientHello(server_id, + kConnectionId, + QuicVersionMax(), + &state, + QuicWallTime::Zero(), + &rand, + NULL, // channel_id_key + ¶ms, + &chlo, + &error_details); - // TODO(rch): Remove once we remove QUIC_VERSION_12. - uint16 vers; - EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint16(kVERS, &vers)); - EXPECT_EQ(0u, vers); + // Verify that certain QuicTags have been set correctly in the CHLO. + QuicTag cver; + EXPECT_EQ(QUIC_NO_ERROR, chlo.GetUint32(kVER, &cver)); + EXPECT_EQ(QuicVersionToQuicTag(QuicVersionMax()), cver); } TEST(QuicCryptoClientConfigTest, ProcessServerDowngradeAttack) { @@ -55,5 +156,104 @@ TEST(QuicCryptoClientConfigTest, ProcessServerDowngradeAttack) { EXPECT_EQ("Downgrade attack detected", error); } +TEST(QuicCryptoClientConfigTest, InitializeFrom) { + QuicCryptoClientConfig config; + QuicServerId canonical_server_id("www.google.com", 80, false, + PRIVACY_MODE_DISABLED); + QuicCryptoClientConfig::CachedState* state = + config.LookupOrCreate(canonical_server_id); + // TODO(rch): Populate other fields of |state|. + state->set_source_address_token("TOKEN"); + state->SetProofValid(); + + QuicServerId other_server_id("mail.google.com", 80, false, + PRIVACY_MODE_DISABLED); + config.InitializeFrom(other_server_id, canonical_server_id, &config); + QuicCryptoClientConfig::CachedState* other = + config.LookupOrCreate(other_server_id); + + EXPECT_EQ(state->server_config(), other->server_config()); + EXPECT_EQ(state->source_address_token(), other->source_address_token()); + EXPECT_EQ(state->certs(), other->certs()); + EXPECT_EQ(1u, other->generation_counter()); +} + +TEST(QuicCryptoClientConfigTest, Canonical) { + QuicCryptoClientConfig config; + config.AddCanonicalSuffix(".google.com"); + QuicServerId canonical_id1("www.google.com", 80, false, + PRIVACY_MODE_DISABLED); + QuicServerId canonical_id2("mail.google.com", 80, false, + PRIVACY_MODE_DISABLED); + QuicCryptoClientConfig::CachedState* state = + config.LookupOrCreate(canonical_id1); + // TODO(rch): Populate other fields of |state|. + state->set_source_address_token("TOKEN"); + state->SetProofValid(); + + QuicCryptoClientConfig::CachedState* other = + config.LookupOrCreate(canonical_id2); + + EXPECT_TRUE(state->IsEmpty()); + EXPECT_EQ(state->server_config(), other->server_config()); + EXPECT_EQ(state->source_address_token(), other->source_address_token()); + EXPECT_EQ(state->certs(), other->certs()); + EXPECT_EQ(1u, other->generation_counter()); + + QuicServerId different_id("mail.google.org", 80, false, + PRIVACY_MODE_DISABLED); + EXPECT_TRUE(config.LookupOrCreate(different_id)->IsEmpty()); +} + +TEST(QuicCryptoClientConfigTest, CanonicalNotUsedIfNotValid) { + QuicCryptoClientConfig config; + config.AddCanonicalSuffix(".google.com"); + QuicServerId canonical_id1("www.google.com", 80, false, + PRIVACY_MODE_DISABLED); + QuicServerId canonical_id2("mail.google.com", 80, false, + PRIVACY_MODE_DISABLED); + QuicCryptoClientConfig::CachedState* state = + config.LookupOrCreate(canonical_id1); + // TODO(rch): Populate other fields of |state|. + state->set_source_address_token("TOKEN"); + + // Do not set the proof as valid, and check that it is not used + // as a canonical entry. + EXPECT_TRUE(config.LookupOrCreate(canonical_id2)->IsEmpty()); +} + +TEST(QuicCryptoClientConfigTest, ClearCachedStates) { + QuicCryptoClientConfig config; + QuicServerId server_id("www.google.com", 80, false, PRIVACY_MODE_DISABLED); + QuicCryptoClientConfig::CachedState* state = config.LookupOrCreate(server_id); + // TODO(rch): Populate other fields of |state|. + vector<string> certs(1); + certs[0] = "Hello Cert"; + state->SetProof(certs, "signature"); + state->set_source_address_token("TOKEN"); + state->SetProofValid(); + EXPECT_EQ(1u, state->generation_counter()); + + // Verify LookupOrCreate returns the same data. + QuicCryptoClientConfig::CachedState* other = config.LookupOrCreate(server_id); + + EXPECT_EQ(state, other); + EXPECT_EQ(1u, other->generation_counter()); + + // Clear the cached states. + config.ClearCachedStates(); + + // Verify LookupOrCreate doesn't have any data. + QuicCryptoClientConfig::CachedState* cleared_cache = + config.LookupOrCreate(server_id); + + EXPECT_EQ(state, cleared_cache); + EXPECT_FALSE(cleared_cache->proof_valid()); + EXPECT_TRUE(cleared_cache->server_config().empty()); + EXPECT_TRUE(cleared_cache->certs().empty()); + EXPECT_TRUE(cleared_cache->signature().empty()); + EXPECT_EQ(2u, cleared_cache->generation_counter()); +} + } // namespace test } // namespace net diff --git a/chromium/net/quic/crypto/quic_crypto_server_config.cc b/chromium/net/quic/crypto/quic_crypto_server_config.cc index 31651dbf633..8e48fbb32ca 100644 --- a/chromium/net/quic/crypto/quic_crypto_server_config.cc +++ b/chromium/net/quic/crypto/quic_crypto_server_config.cc @@ -15,6 +15,7 @@ #include "net/quic/crypto/aes_128_gcm_12_decrypter.h" #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/cert_compressor.h" +#include "net/quic/crypto/chacha20_poly1305_encrypter.h" #include "net/quic/crypto/channel_id.h" #include "net/quic/crypto/crypto_framer.h" #include "net/quic/crypto/crypto_server_config_protobuf.h" @@ -32,17 +33,33 @@ #include "net/quic/crypto/strike_register.h" #include "net/quic/crypto/strike_register_client.h" #include "net/quic/quic_clock.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_protocol.h" +#include "net/quic/quic_socket_address_coder.h" #include "net/quic/quic_utils.h" using base::StringPiece; using crypto::SecureHash; using std::map; +using std::sort; using std::string; using std::vector; namespace net { +namespace { + +string DeriveSourceAddressTokenKey(StringPiece source_address_token_secret) { + crypto::HKDF hkdf(source_address_token_secret, + StringPiece() /* no salt */, + "QUIC source address token key", + CryptoSecretBoxer::GetKeySize(), + 0 /* no fixed IV needed */); + return hkdf.server_write_key().as_string(); +} + +} // namespace + // ClientHelloInfo contains information about a client hello message that is // only kept for as long as it's being processed. struct ClientHelloInfo { @@ -64,6 +81,10 @@ struct ClientHelloInfo { StringPiece sni; StringPiece client_nonce; StringPiece server_nonce; + StringPiece user_agent_id; + + // Errors from EvaluateClientHello. + vector<uint32> reject_reasons; }; struct ValidateClientHelloResultCallback::Result { @@ -89,10 +110,8 @@ class ValidateClientHelloHelper { } ~ValidateClientHelloHelper() { - if (done_cb_ != NULL) { - LOG(DFATAL) << - "Deleting ValidateClientHelloHelper with a pending callback."; - } + LOG_IF(DFATAL, done_cb_ != NULL) + << "Deleting ValidateClientHelloHelper with a pending callback."; } void ValidationComplete(QuicErrorCode error_code, const char* error_details) { @@ -108,9 +127,7 @@ class ValidateClientHelloHelper { private: void DetachCallback() { - if (done_cb_ == NULL) { - LOG(DFATAL) << "Callback already detached."; - } + LOG_IF(DFATAL, done_cb_ == NULL) << "Callback already detached."; done_cb_ = NULL; } @@ -133,6 +150,12 @@ class VerifyNonceIsValidAndUniqueCallback virtual void RunImpl(bool nonce_is_valid_and_unique) OVERRIDE { DVLOG(1) << "Using client nonce, unique: " << nonce_is_valid_and_unique; result_->info.unique = nonce_is_valid_and_unique; + // TODO(rtenneti): Implement capturing of error from strike register. + // Temporarily treat them as CLIENT_NONCE_UNKNOWN_FAILURE. + if (!nonce_is_valid_and_unique) { + result_->info.reject_reasons.push_back( + static_cast<uint32>(CLIENT_NONCE_UNKNOWN_FAILURE)); + } done_cb_->Run(result_); } @@ -146,6 +169,11 @@ class VerifyNonceIsValidAndUniqueCallback // static const char QuicCryptoServerConfig::TESTING[] = "secret string for testing"; +PrimaryConfigChangedCallback::PrimaryConfigChangedCallback() { +} + +PrimaryConfigChangedCallback::~PrimaryConfigChangedCallback() { +} ValidateClientHelloResultCallback::ValidateClientHelloResultCallback() { } @@ -179,11 +207,8 @@ QuicCryptoServerConfig::QuicCryptoServerConfig( source_address_token_lifetime_secs_(86400), server_nonce_strike_register_max_entries_(1 << 10), server_nonce_strike_register_window_secs_(120) { - crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */, - "QUIC source address token key", - CryptoSecretBoxer::GetKeySize(), - 0 /* no fixed IV needed */); - source_address_token_boxer_.SetKey(hkdf.server_write_key()); + default_source_address_token_boxer_.SetKey( + DeriveSourceAddressTokenKey(source_address_token_secret)); // Generate a random key and orbit for server nonces. rand->RandBytes(server_nonce_orbit_, sizeof(server_nonce_orbit_)); @@ -200,7 +225,7 @@ QuicCryptoServerConfig::~QuicCryptoServerConfig() { } // static -QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( +QuicServerConfigProtobuf* QuicCryptoServerConfig::GenerateConfig( QuicRandom* rand, const QuicClock* clock, const ConfigOptions& options) { @@ -239,9 +264,11 @@ QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( } else { msg.SetTaglist(kKEXS, kC255, 0); } - msg.SetTaglist(kAEAD, kAESG, 0); - // TODO(rch): Remove once we remove QUIC_VERSION_12. - msg.SetValue(kVERS, static_cast<uint16>(0)); + if (ChaCha20Poly1305Encrypter::IsSupported()) { + msg.SetTaglist(kAEAD, kAESG, kCC12, 0); + } else { + msg.SetTaglist(kAEAD, kAESG, 0); + } msg.SetStringPiece(kPUBS, encoded_public_values); if (options.expiry_time.IsZero()) { @@ -331,6 +358,7 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( configs_[config->id] = config; SelectNewPrimaryConfig(now); DCHECK(primary_config_.get()); + DCHECK_EQ(configs_.find(primary_config_->id)->second, primary_config_); } return msg.release(); @@ -341,14 +369,14 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig( const QuicClock* clock, const ConfigOptions& options) { scoped_ptr<QuicServerConfigProtobuf> config( - DefaultConfig(rand, clock, options)); + GenerateConfig(rand, clock, options)); return AddConfig(config.get(), clock->WallNow()); } bool QuicCryptoServerConfig::SetConfigs( const vector<QuicServerConfigProtobuf*>& protobufs, const QuicWallTime now) { - vector<scoped_refptr<Config> > new_configs; + vector<scoped_refptr<Config> > parsed_configs; bool ok = true; for (vector<QuicServerConfigProtobuf*>::const_iterator i = protobufs.begin(); @@ -358,95 +386,109 @@ bool QuicCryptoServerConfig::SetConfigs( ok = false; break; } - new_configs.push_back(config); + + parsed_configs.push_back(config); + } + + if (parsed_configs.empty()) { + LOG(WARNING) << "New config list is empty."; + ok = false; } if (!ok) { LOG(WARNING) << "Rejecting QUIC configs because of above errors"; } else { - base::AutoLock locked(configs_lock_); - typedef ConfigMap::iterator ConfigMapIterator; - vector<ConfigMapIterator> to_delete; - - DCHECK_EQ(protobufs.size(), new_configs.size()); - - // First, look for any configs that have been removed. - for (ConfigMapIterator i = configs_.begin(); - i != configs_.end(); ++i) { - const scoped_refptr<Config> old_config = i->second; - bool found = false; - - for (vector<scoped_refptr<Config> >::const_iterator j = - new_configs.begin(); - j != new_configs.end(); ++j) { - if ((*j)->id == old_config->id) { - found = true; - break; - } - } + VLOG(1) << "Updating configs:"; - if (!found) { - // We cannot remove the primary config. This has probably happened - // because our source of config information failed for a time and we're - // suddenly seeing a jump in time. No matter - we'll configure a new - // primary config and then we'll be able to delete it next time. - if (!old_config->is_primary) { - to_delete.push_back(i); - } - } - } + base::AutoLock locked(configs_lock_); + ConfigMap new_configs; - for (vector<ConfigMapIterator>::const_iterator i = to_delete.begin(); - i != to_delete.end(); ++i) { - configs_.erase(*i); - } + for (vector<scoped_refptr<Config> >::const_iterator i = + parsed_configs.begin(); + i != parsed_configs.end(); ++i) { + scoped_refptr<Config> config = *i; - // Find any configs that need to be added. - for (vector<scoped_refptr<Config> >::const_iterator i = new_configs.begin(); - i != new_configs.end(); ++i) { - const scoped_refptr<Config> new_config = *i; - if (configs_.find(new_config->id) != configs_.end()) { - continue; + ConfigMap::iterator it = configs_.find(config->id); + if (it != configs_.end()) { + VLOG(1) + << "Keeping scid: " << base::HexEncode( + config->id.data(), config->id.size()) + << " orbit: " << base::HexEncode( + reinterpret_cast<const char *>(config->orbit), kOrbitSize) + << " new primary_time " << config->primary_time.ToUNIXSeconds() + << " old primary_time " << it->second->primary_time.ToUNIXSeconds() + << " new priority " << config->priority + << " old priority " << it->second->priority; + // Update primary_time and priority. + it->second->primary_time = config->primary_time; + it->second->priority = config->priority; + new_configs.insert(*it); + } else { + VLOG(1) << "Adding scid: " << base::HexEncode( + config->id.data(), config->id.size()) + << " orbit: " << base::HexEncode( + reinterpret_cast<const char *>(config->orbit), kOrbitSize) + << " primary_time " << config->primary_time.ToUNIXSeconds() + << " priority " << config->priority; + new_configs.insert(make_pair(config->id, config)); } - - configs_[new_config->id] = new_config; } + configs_.swap(new_configs); SelectNewPrimaryConfig(now); + DCHECK(primary_config_); + DCHECK_EQ(configs_.find(primary_config_->id)->second, primary_config_); } return ok; } +void QuicCryptoServerConfig::GetConfigIds(vector<string>* scids) const { + base::AutoLock locked(configs_lock_); + for (ConfigMap::const_iterator it = configs_.begin(); + it != configs_.end(); ++it) { + scids->push_back(it->first); + } +} + void QuicCryptoServerConfig::ValidateClientHello( const CryptoHandshakeMessage& client_hello, IPEndPoint client_ip, const QuicClock* clock, ValidateClientHelloResultCallback* done_cb) const { const QuicWallTime now(clock->WallNow()); + ValidateClientHelloResultCallback::Result* result = new ValidateClientHelloResultCallback::Result( client_hello, client_ip, now); + StringPiece requested_scid; + client_hello.GetStringPiece(kSCID, &requested_scid); + uint8 primary_orbit[kOrbitSize]; + scoped_refptr<Config> requested_config; { base::AutoLock locked(configs_lock_); - if (!primary_config_) { + if (!primary_config_.get()) { result->error_code = QUIC_CRYPTO_INTERNAL_ERROR; result->error_details = "No configurations loaded"; } else { if (!next_config_promotion_time_.IsZero() && next_config_promotion_time_.IsAfter(now)) { SelectNewPrimaryConfig(now); + DCHECK(primary_config_); + DCHECK(configs_.find(primary_config_->id)->second == primary_config_); } memcpy(primary_orbit, primary_config_->orbit, sizeof(primary_orbit)); } + + requested_config = GetConfigWithScid(requested_scid); } if (result->error_code == QUIC_NO_ERROR) { - EvaluateClientHello(primary_orbit, result, done_cb); + EvaluateClientHello(primary_orbit, requested_config, result, done_cb); } else { done_cb->Run(result); } @@ -454,8 +496,8 @@ void QuicCryptoServerConfig::ValidateClientHello( QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( const ValidateClientHelloResultCallback::Result& validate_chlo_result, - QuicGuid guid, - IPEndPoint client_ip, + QuicConnectionId connection_id, + IPEndPoint client_address, QuicVersion version, const QuicVersionVector& supported_versions, const QuicClock* clock, @@ -474,18 +516,19 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( // case, we need to make sure that we actually do not support this version // and that it wasn't a downgrade attack. QuicTag client_version_tag; - // TODO(rch): Make this check mandatory when we remove QUIC_VERSION_12. - if (client_hello.GetUint32(kVER, &client_version_tag) == QUIC_NO_ERROR) { - QuicVersion client_version = QuicTagToQuicVersion(client_version_tag); - if (client_version != version) { - // Just because client_version is a valid version enum doesn't mean that - // this server actually supports that version, so we check to see if - // it's actually in the supported versions list. - for (size_t i = 0; i < supported_versions.size(); ++i) { - if (client_version == supported_versions[i]) { - *error_details = "Downgrade attack detected"; - return QUIC_VERSION_NEGOTIATION_MISMATCH; - } + if (client_hello.GetUint32(kVER, &client_version_tag) != QUIC_NO_ERROR) { + *error_details = "client hello missing version list"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + QuicVersion client_version = QuicTagToQuicVersion(client_version_tag); + if (client_version != version) { + // Just because client_version is a valid version enum doesn't mean that + // this server actually supports that version, so we check to see if + // it's actually in the supported versions list. + for (size_t i = 0; i < supported_versions.size(); ++i) { + if (client_version == supported_versions[i]) { + *error_details = "Downgrade attack detected"; + return QUIC_VERSION_NEGOTIATION_MISMATCH; } } } @@ -507,19 +550,16 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( if (!next_config_promotion_time_.IsZero() && next_config_promotion_time_.IsAfter(now)) { SelectNewPrimaryConfig(now); + DCHECK(primary_config_); + DCHECK(configs_.find(primary_config_->id)->second == primary_config_); } + // We'll use the config that the client requested in order to do + // key-agreement. Otherwise we'll give it a copy of |primary_config_| + // to use. primary_config = primary_config_; - if (!requested_scid.empty()) { - ConfigMap::const_iterator it = configs_.find(requested_scid.as_string()); - if (it != configs_.end()) { - // We'll use the config that the client requested in order to do - // key-agreement. Otherwise we'll give it a copy of |primary_config_| - // to use. - requested_config = it->second; - } - } + requested_config = GetConfigWithScid(requested_scid); } if (validate_chlo_result.error_code != QUIC_NO_ERROR) { @@ -533,7 +573,7 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( !info.client_nonce_well_formed || !info.unique || !requested_config.get()) { - BuildRejection(primary_config.get(), client_hello, info, rand, out); + BuildRejection(*primary_config, client_hello, info, rand, out); return QUIC_NO_ERROR; } @@ -585,9 +625,10 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( string hkdf_suffix; const QuicData& client_hello_serialized = client_hello.GetSerialized(); - hkdf_suffix.reserve(sizeof(guid) + client_hello_serialized.length() + + hkdf_suffix.reserve(sizeof(connection_id) + client_hello_serialized.length() + requested_config->serialized.size()); - hkdf_suffix.append(reinterpret_cast<char*>(&guid), sizeof(guid)); + hkdf_suffix.append(reinterpret_cast<char*>(&connection_id), + sizeof(connection_id)); hkdf_suffix.append(client_hello_serialized.data(), client_hello_serialized.length()); hkdf_suffix.append(requested_config->serialized); @@ -603,7 +644,8 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( string hkdf_input; hkdf_input.append(QuicCryptoConfig::kCETVLabel, strlen(QuicCryptoConfig::kCETVLabel) + 1); - hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid)); + hkdf_input.append(reinterpret_cast<char*>(&connection_id), + sizeof(connection_id)); hkdf_input.append(client_hello_serialized.data(), client_hello_serialized.length()); hkdf_input.append(requested_config->serialized); @@ -698,65 +740,92 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( } out->SetVector(kVER, supported_version_tags); out->SetStringPiece(kSourceAddressTokenTag, - NewSourceAddressToken(client_ip, rand, info.now)); + NewSourceAddressToken( + *requested_config, + client_address, rand, + info.now)); + QuicSocketAddressCoder address_coder(client_address); + out->SetStringPiece(kCADR, address_coder.Encode()); out->SetStringPiece(kPUBS, forward_secure_public_value); + return QUIC_NO_ERROR; } +scoped_refptr<QuicCryptoServerConfig::Config> +QuicCryptoServerConfig::GetConfigWithScid(StringPiece requested_scid) const { + // In Chromium, we will dead lock if the lock is held by the current thread. + // Chromium doesn't have AssertReaderHeld API call. + // configs_lock_.AssertReaderHeld(); + + if (!requested_scid.empty()) { + ConfigMap::const_iterator it = configs_.find(requested_scid.as_string()); + if (it != configs_.end()) { + // We'll use the config that the client requested in order to do + // key-agreement. + return scoped_refptr<Config>(it->second); + } + } + + return scoped_refptr<Config>(); +} + // ConfigPrimaryTimeLessThan is a comparator that implements "less than" for // Config's based on their primary_time. // static bool QuicCryptoServerConfig::ConfigPrimaryTimeLessThan( const scoped_refptr<Config>& a, const scoped_refptr<Config>& b) { - return a->primary_time.IsBefore(b->primary_time); + if (a->primary_time.IsBefore(b->primary_time) || + b->primary_time.IsBefore(a->primary_time)) { + // Primary times differ. + return a->primary_time.IsBefore(b->primary_time); + } else if (a->priority != b->priority) { + // Primary times are equal, sort backwards by priority. + return a->priority < b->priority; + } else { + // Primary times and priorities are equal, sort by config id. + return a->id < b->id; + } } void QuicCryptoServerConfig::SelectNewPrimaryConfig( const QuicWallTime now) const { vector<scoped_refptr<Config> > configs; configs.reserve(configs_.size()); - scoped_refptr<Config> first_config = NULL; for (ConfigMap::const_iterator it = configs_.begin(); it != configs_.end(); ++it) { - const scoped_refptr<Config> config(it->second); - if (!first_config.get()) { - first_config = config; - } - if (config->primary_time.IsZero()) { - continue; - } + // TODO(avd) Exclude expired configs? configs.push_back(it->second); } if (configs.empty()) { - // Tests don't set |primary_time_|. For that case we promote the first - // Config and leave it as primary forever. - if (!primary_config_.get() && first_config.get()) { - primary_config_ = first_config; - primary_config_->is_primary = true; + if (primary_config_.get()) { + LOG(DFATAL) << "No valid QUIC server config. Keeping the current config."; + } else { + LOG(DFATAL) << "No valid QUIC server config."; } return; } - std::sort(configs.begin(), configs.end(), ConfigPrimaryTimeLessThan); + sort(configs.begin(), configs.end(), ConfigPrimaryTimeLessThan); + + Config* best_candidate = configs[0]; for (size_t i = 0; i < configs.size(); ++i) { const scoped_refptr<Config> config(configs[i]); - if (!config->primary_time.IsAfter(now)) { + if (config->primary_time.IsAfter(best_candidate->primary_time)) { + best_candidate = config; + } continue; } // This is the first config with a primary_time in the future. Thus the // previous Config should be the primary and this one should determine the // next_config_promotion_time_. - scoped_refptr<Config> new_primary; + scoped_refptr<Config> new_primary(best_candidate); if (i == 0) { - // There was no previous Config, so this will have to be primary. - new_primary = config; - // We need the primary_time of the next config. if (configs.size() > 1) { next_config_promotion_time_ = configs[1]->primary_time; @@ -764,7 +833,6 @@ void QuicCryptoServerConfig::SelectNewPrimaryConfig( next_config_promotion_time_ = QuicWallTime::Zero(); } } else { - new_primary = configs[i - 1]; next_config_promotion_time_ = config->primary_time; } @@ -773,23 +841,40 @@ void QuicCryptoServerConfig::SelectNewPrimaryConfig( } primary_config_ = new_primary; new_primary->is_primary = true; + DVLOG(1) << "New primary config. orbit: " + << base::HexEncode( + reinterpret_cast<const char*>(primary_config_->orbit), + kOrbitSize); + if (primary_config_changed_cb_.get() != NULL) { + primary_config_changed_cb_->Run(primary_config_->id); + } return; } // All config's primary times are in the past. We should make the most recent - // primary. - scoped_refptr<Config> new_primary = configs[configs.size() - 1]; + // and highest priority candidate primary. + scoped_refptr<Config> new_primary(best_candidate); if (primary_config_.get()) { primary_config_->is_primary = false; } primary_config_ = new_primary; new_primary->is_primary = true; + DVLOG(1) << "New primary config. orbit: " + << base::HexEncode( + reinterpret_cast<const char*>(primary_config_->orbit), + kOrbitSize) + << " scid: " << base::HexEncode(primary_config_->id.data(), + primary_config_->id.size()); next_config_promotion_time_ = QuicWallTime::Zero(); + if (primary_config_changed_cb_.get() != NULL) { + primary_config_changed_cb_->Run(primary_config_->id); + } } void QuicCryptoServerConfig::EvaluateClientHello( const uint8* primary_orbit, + scoped_refptr<Config> requested_config, ValidateClientHelloResultCallback::Result* client_hello_state, ValidateClientHelloResultCallback* done_cb) const { ValidateClientHelloHelper helper(client_hello_state, done_cb); @@ -798,7 +883,7 @@ void QuicCryptoServerConfig::EvaluateClientHello( client_hello_state->client_hello; ClientHelloInfo* info = &(client_hello_state->info); - if (client_hello.size() < kClientHelloMinimumSizeOld) { + if (client_hello.size() < kClientHelloMinimumSize) { helper.ValidationComplete(QUIC_CRYPTO_INVALID_VALUE_LENGTH, "Client hello too small"); return; @@ -811,28 +896,67 @@ void QuicCryptoServerConfig::EvaluateClientHello( return; } + client_hello.GetStringPiece(kUAID, &info->user_agent_id); + + if (!requested_config.get()) { + StringPiece requested_scid; + if (client_hello.GetStringPiece(kSCID, &requested_scid)) { + info->reject_reasons.push_back( + static_cast<uint32>(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE)); + } else { + info->reject_reasons.push_back( + static_cast<uint32>(SERVER_CONFIG_INCHOATE_HELLO_FAILURE)); + } + // No server config with the requested ID. + helper.ValidationComplete(QUIC_NO_ERROR, ""); + return; + } + + HandshakeFailureReason source_address_token_error; StringPiece srct; - if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct) && - ValidateSourceAddressToken(srct, info->client_ip, info->now)) { - info->valid_source_address_token = true; + if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct)) { + source_address_token_error = + ValidateSourceAddressToken(*requested_config, + srct, + info->client_ip, + info->now); + info->valid_source_address_token = + (source_address_token_error == HANDSHAKE_OK); } else { + source_address_token_error = SOURCE_ADDRESS_TOKEN_INVALID_FAILURE; + } + + bool found_error = false; + if (source_address_token_error != HANDSHAKE_OK) { + info->reject_reasons.push_back( + static_cast<uint32>(source_address_token_error)); // No valid source address token. - helper.ValidationComplete(QUIC_NO_ERROR, ""); - return; + if (FLAGS_use_early_return_when_verifying_chlo) { + helper.ValidationComplete(QUIC_NO_ERROR, ""); + return; + } + found_error = true; } if (client_hello.GetStringPiece(kNONC, &info->client_nonce) && info->client_nonce.size() == kNonceSize) { info->client_nonce_well_formed = true; } else { + info->reject_reasons.push_back( + static_cast<uint32>(CLIENT_NONCE_INVALID_FAILURE)); // Invalid client nonce. DVLOG(1) << "Invalid client nonce."; - helper.ValidationComplete(QUIC_NO_ERROR, ""); - return; + if (FLAGS_use_early_return_when_verifying_chlo) { + helper.ValidationComplete(QUIC_NO_ERROR, ""); + return; + } + found_error = true; } if (!replay_protection_) { - info->unique = true; + if (!found_error) { + info->unique = true; + } DVLOG(1) << "No replay protection."; helper.ValidationComplete(QUIC_NO_ERROR, ""); return; @@ -841,27 +965,45 @@ void QuicCryptoServerConfig::EvaluateClientHello( client_hello.GetStringPiece(kServerNonceTag, &info->server_nonce); if (!info->server_nonce.empty()) { // If the server nonce is present, use it to establish uniqueness. - info->unique = ValidateServerNonce(info->server_nonce, info->now); + HandshakeFailureReason server_nonce_error = + ValidateServerNonce(info->server_nonce, info->now); + if (server_nonce_error == HANDSHAKE_OK) { + info->unique = true; + } else { + info->reject_reasons.push_back(static_cast<uint32>(server_nonce_error)); + info->unique = false; + } DVLOG(1) << "Using server nonce, unique: " << info->unique; helper.ValidationComplete(QUIC_NO_ERROR, ""); return; } + // We want to contact strike register if there are no errors because it is + // a RPC call and is expensive. + if (found_error) { + helper.ValidationComplete(QUIC_NO_ERROR, ""); + return; + } + // Use the client nonce to establish uniqueness. - base::AutoLock locked(strike_register_client_lock_); + StrikeRegisterClient* strike_register_client; + { + base::AutoLock locked(strike_register_client_lock_); - if (strike_register_client_.get() == NULL) { - strike_register_client_.reset(new LocalStrikeRegisterClient( - strike_register_max_entries_, - static_cast<uint32>(info->now.ToUNIXSeconds()), - strike_register_window_secs_, - primary_orbit, - strike_register_no_startup_period_ ? - StrikeRegister::NO_STARTUP_PERIOD_NEEDED : - StrikeRegister::DENY_REQUESTS_AT_STARTUP)); + if (strike_register_client_.get() == NULL) { + strike_register_client_.reset(new LocalStrikeRegisterClient( + strike_register_max_entries_, + static_cast<uint32>(info->now.ToUNIXSeconds()), + strike_register_window_secs_, + primary_orbit, + strike_register_no_startup_period_ ? + StrikeRegister::NO_STARTUP_PERIOD_NEEDED : + StrikeRegister::DENY_REQUESTS_AT_STARTUP)); + } + strike_register_client = strike_register_client_.get(); } - strike_register_client_->VerifyNonceIsValidAndUnique( + strike_register_client->VerifyNonceIsValidAndUnique( info->client_nonce, info->now, new VerifyNonceIsValidAndUniqueCallback(client_hello_state, done_cb)); @@ -869,19 +1011,29 @@ void QuicCryptoServerConfig::EvaluateClientHello( } void QuicCryptoServerConfig::BuildRejection( - const scoped_refptr<Config>& config, + const Config& config, const CryptoHandshakeMessage& client_hello, const ClientHelloInfo& info, QuicRandom* rand, CryptoHandshakeMessage* out) const { out->set_tag(kREJ); - out->SetStringPiece(kSCFG, config->serialized); + out->SetStringPiece(kSCFG, config.serialized); out->SetStringPiece(kSourceAddressTokenTag, - NewSourceAddressToken(info.client_ip, rand, info.now)); + NewSourceAddressToken( + config, + info.client_ip, + rand, + info.now)); if (replay_protection_) { out->SetStringPiece(kServerNonceTag, NewServerNonce(rand, info.now)); } + if (FLAGS_send_quic_crypto_reject_reason) { + // Send client the reject reason for debugging purposes. + DCHECK_LT(0u, info.reject_reasons.size()); + out->SetVector(kRREJ, info.reject_reasons); + } + // The client may have requested a certificate chain. const QuicTag* their_proof_demands; size_t num_their_proof_demands; @@ -912,7 +1064,7 @@ void QuicCryptoServerConfig::BuildRejection( const vector<string>* certs; string signature; - if (!proof_source_->GetProof(info.sni.as_string(), config->serialized, + if (!proof_source_->GetProof(info.sni.as_string(), config.serialized, x509_ecdsa_supported, &certs, &signature)) { return; } @@ -924,7 +1076,7 @@ void QuicCryptoServerConfig::BuildRejection( const string compressed = CertCompressor::CompressChain( *certs, their_common_set_hashes, their_cached_cert_hashes, - config->common_cert_sets); + config.common_cert_sets); // kREJOverheadBytes is a very rough estimate of how much of a REJ // message is taken up by things other than the certificates. @@ -943,7 +1095,7 @@ void QuicCryptoServerConfig::BuildRejection( // token. const size_t max_unverified_size = client_hello.size() * kMultiplier - kREJOverheadBytes; - COMPILE_ASSERT(kClientHelloMinimumSizeOld * kMultiplier >= kREJOverheadBytes, + COMPILE_ASSERT(kClientHelloMinimumSize * kMultiplier >= kREJOverheadBytes, overhead_calculation_may_underflow); if (info.valid_source_address_token || signature.size() + compressed.size() < max_unverified_size) { @@ -967,11 +1119,25 @@ QuicCryptoServerConfig::ParseConfigProtobuf( scoped_refptr<Config> config(new Config); config->serialized = protobuf->config(); + if (!protobuf->has_source_address_token_secret_override()) { + // Use the default boxer. + config->source_address_token_boxer = &default_source_address_token_boxer_; + } else { + // Create override boxer instance. + CryptoSecretBoxer* boxer = new CryptoSecretBoxer; + boxer->SetKey(DeriveSourceAddressTokenKey( + protobuf->source_address_token_secret_override())); + config->source_address_token_boxer_storage.reset(boxer); + config->source_address_token_boxer = boxer; + } + if (protobuf->has_primary_time()) { config->primary_time = QuicWallTime::FromUNIXSeconds(protobuf->primary_time()); } + config->priority = protobuf->priority(); + StringPiece scid; if (!msg->GetStringPiece(kSCID, &scid)) { LOG(WARNING) << "Server config message is missing SCID"; @@ -996,7 +1162,7 @@ QuicCryptoServerConfig::ParseConfigProtobuf( StringPiece orbit; if (!msg->GetStringPiece(kORBT, &orbit)) { - LOG(WARNING) << "Server config message is missing OBIT"; + LOG(WARNING) << "Server config message is missing ORBT"; return NULL; } @@ -1009,15 +1175,18 @@ QuicCryptoServerConfig::ParseConfigProtobuf( memcpy(config->orbit, orbit.data(), sizeof(config->orbit)); { - base::AutoLock locked(strike_register_client_lock_); - if (strike_register_client_.get()) { - const string& orbit = strike_register_client_->orbit(); - if (0 != memcmp(orbit.data(), config->orbit, kOrbitSize)) { - LOG(WARNING) - << "Server config has different orbit than current config. " - "Switching orbits at run-time is not supported."; - return NULL; - } + StrikeRegisterClient* strike_register_client; + { + base::AutoLock locked(strike_register_client_lock_); + strike_register_client = strike_register_client_.get(); + } + + if (strike_register_client != NULL && + !strike_register_client->IsKnownOrbit(orbit)) { + LOG(WARNING) + << "Rejecting server config with orbit that the strike register " + "client doesn't know about."; + return NULL; } } @@ -1160,37 +1329,52 @@ void QuicCryptoServerConfig::set_server_nonce_strike_register_window_secs( server_nonce_strike_register_window_secs_ = window_secs; } -string QuicCryptoServerConfig::NewSourceAddressToken( - const IPEndPoint& ip, - QuicRandom* rand, - QuicWallTime now) const { +void QuicCryptoServerConfig::AcquirePrimaryConfigChangedCb( + PrimaryConfigChangedCallback* cb) { + base::AutoLock locked(configs_lock_); + primary_config_changed_cb_.reset(cb); +} + +string QuicCryptoServerConfig::NewSourceAddressToken(const Config& config, + const IPEndPoint& ip, + QuicRandom* rand, + QuicWallTime now) const { SourceAddressToken source_address_token; - source_address_token.set_ip(IPAddressToPackedString(ip.address())); + IPAddressNumber ip_address = ip.address(); + if (ip.GetSockAddrFamily() == AF_INET) { + ip_address = ConvertIPv4NumberToIPv6Number(ip_address); + } + source_address_token.set_ip(IPAddressToPackedString(ip_address)); source_address_token.set_timestamp(now.ToUNIXSeconds()); - return source_address_token_boxer_.Box( + return config.source_address_token_boxer->Box( rand, source_address_token.SerializeAsString()); } -bool QuicCryptoServerConfig::ValidateSourceAddressToken( +HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressToken( + const Config& config, StringPiece token, const IPEndPoint& ip, QuicWallTime now) const { string storage; StringPiece plaintext; - if (!source_address_token_boxer_.Unbox(token, &storage, &plaintext)) { - return false; + if (!config.source_address_token_boxer->Unbox(token, &storage, &plaintext)) { + return SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE; } SourceAddressToken source_address_token; if (!source_address_token.ParseFromArray(plaintext.data(), plaintext.size())) { - return false; + return SOURCE_ADDRESS_TOKEN_PARSE_FAILURE; } - if (source_address_token.ip() != IPAddressToPackedString(ip.address())) { + IPAddressNumber ip_address = ip.address(); + if (ip.GetSockAddrFamily() == AF_INET) { + ip_address = ConvertIPv4NumberToIPv6Number(ip_address); + } + if (source_address_token.ip() != IPAddressToPackedString(ip_address)) { // It's for a different IP address. - return false; + return SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE; } const QuicWallTime timestamp( @@ -1199,15 +1383,15 @@ bool QuicCryptoServerConfig::ValidateSourceAddressToken( if (now.IsBefore(timestamp) && delta.ToSeconds() > source_address_token_future_secs_) { - return false; + return SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE; } if (now.IsAfter(timestamp) && delta.ToSeconds() > source_address_token_lifetime_secs_) { - return false; + return SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE; } - return true; + return HANDSHAKE_OK; } // kServerNoncePlaintextSize is the number of bytes in an unencrypted server @@ -1233,12 +1417,13 @@ string QuicCryptoServerConfig::NewServerNonce(QuicRandom* rand, StringPiece(reinterpret_cast<char*>(server_nonce), sizeof(server_nonce))); } -bool QuicCryptoServerConfig::ValidateServerNonce(StringPiece token, - QuicWallTime now) const { +HandshakeFailureReason QuicCryptoServerConfig::ValidateServerNonce( + StringPiece token, + QuicWallTime now) const { string storage; StringPiece plaintext; if (!server_nonce_boxer_.Unbox(token, &storage, &plaintext)) { - return false; + return SERVER_NONCE_DECRYPTION_FAILURE; } // plaintext contains: @@ -1248,7 +1433,7 @@ bool QuicCryptoServerConfig::ValidateServerNonce(StringPiece token, if (plaintext.size() != kServerNoncePlaintextSize) { // This should never happen because the value decrypted correctly. LOG(DFATAL) << "Seemingly valid server nonce had incorrect length."; - return false; + return SERVER_NONCE_INVALID_FAILURE; } uint8 server_nonce[32]; @@ -1273,13 +1458,15 @@ bool QuicCryptoServerConfig::ValidateServerNonce(StringPiece token, server_nonce, static_cast<uint32>(now.ToUNIXSeconds())); } - return is_unique; + return is_unique ? HANDSHAKE_OK : SERVER_NONCE_NOT_UNIQUE_FAILURE; } QuicCryptoServerConfig::Config::Config() : channel_id_enabled(false), is_primary(false), - primary_time(QuicWallTime::Zero()) {} + primary_time(QuicWallTime::Zero()), + priority(0), + source_address_token_boxer(NULL) {} QuicCryptoServerConfig::Config::~Config() { STLDeleteElements(&key_exchanges); } diff --git a/chromium/net/quic/crypto/quic_crypto_server_config.h b/chromium/net/quic/crypto/quic_crypto_server_config.h index 6d59bff6a9f..3ece064376f 100644 --- a/chromium/net/quic/crypto/quic_crypto_server_config.h +++ b/chromium/net/quic/crypto/quic_crypto_server_config.h @@ -22,6 +22,7 @@ namespace net { +class CryptoHandshakeMessage; class EphemeralKeySource; class KeyExchange; class ProofSource; @@ -39,6 +40,43 @@ namespace test { class QuicCryptoServerConfigPeer; } // namespace test +enum HandshakeFailureReason { + HANDSHAKE_OK = 0, + + // Failure reasons for an invalid client nonce. + // TODO(rtenneti): Implement capturing of error from strike register. + CLIENT_NONCE_UNKNOWN_FAILURE = 100, + CLIENT_NONCE_INVALID_FAILURE, + + // Failure reasons for an invalid server nonce. + SERVER_NONCE_INVALID_FAILURE = 200, + SERVER_NONCE_DECRYPTION_FAILURE, + SERVER_NONCE_NOT_UNIQUE_FAILURE, + + // Failure reasons for an invalid server config. + SERVER_CONFIG_INCHOATE_HELLO_FAILURE = 300, + SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE, + + // Failure reasons for an invalid source adddress token. + SOURCE_ADDRESS_TOKEN_INVALID_FAILURE = 400, + SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, + SOURCE_ADDRESS_TOKEN_PARSE_FAILURE, + SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, + SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE, + SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE, +}; + +// Hook that allows application code to subscribe to primary config changes. +class PrimaryConfigChangedCallback { + public: + PrimaryConfigChangedCallback(); + virtual ~PrimaryConfigChangedCallback(); + virtual void Run(const std::string& scid) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(PrimaryConfigChangedCallback); +}; + // Callback used to accept the result of the |client_hello| validation step. class NET_EXPORT_PRIVATE ValidateClientHelloResultCallback { public: @@ -101,9 +139,9 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // TESTING is a magic parameter for passing to the constructor in tests. static const char TESTING[]; - // DefaultConfig generates a QuicServerConfigProtobuf protobuf suitable for - // using in tests. - static QuicServerConfigProtobuf* DefaultConfig( + // Generates a QuicServerConfigProtobuf protobuf suitable for + // AddConfig and SetConfigs. + static QuicServerConfigProtobuf* GenerateConfig( QuicRandom* rand, const QuicClock* clock, const ConfigOptions& options); @@ -134,6 +172,9 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { bool SetConfigs(const std::vector<QuicServerConfigProtobuf*>& protobufs, QuicWallTime now); + // Get the server config ids for all known configs. + void GetConfigIds(std::vector<std::string>* scids) const; + // Checks |client_hello| for gross errors and determines whether it // can be shown to be fresh (i.e. not a replay). The result of the // validation step must be interpreted by calling @@ -167,12 +208,15 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // validate_chlo_result: Output from the asynchronous call to // ValidateClientHello. Contains the client hello message and // information about it. - // guid: the GUID for the connection, which is used in key derivation. - // client_ip: the IP address of the client, which is used to generate and - // validate source-address tokens. + // connection_id: the ConnectionId for the connection, which is used in key + // derivation. + // client_address: the IP address and port of the client. The IP address is + // used to generate and validate source-address tokens. // version: version of the QUIC protocol in use for this connection // supported_versions: versions of the QUIC protocol that this server // supports. + // initial_flow_control_window: size of initial flow control window this + // server uses for new streams. // clock: used to validate client nonces and ephemeral keys. // rand: an entropy source // params: the state of the handshake. This may be updated with a server @@ -182,8 +226,8 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // error_details: used to store a string describing any error. QuicErrorCode ProcessClientHello( const ValidateClientHelloResultCallback::Result& validate_chlo_result, - QuicGuid guid, - IPEndPoint client_ip, + QuicConnectionId connection_id, + IPEndPoint client_address, QuicVersion version, const QuicVersionVector& supported_versions, const QuicClock* clock, @@ -254,6 +298,9 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // uniqueness. void set_server_nonce_strike_register_window_secs(uint32 window_secs); + // Set and take ownership of the callback to invoke on primary config changes. + void AcquirePrimaryConfigChangedCb(PrimaryConfigChangedCallback* cb); + private: friend class test::QuicCryptoServerConfigPeer; @@ -296,8 +343,25 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // will not be promoted at a specific time. QuicWallTime primary_time; + // Secondary sort key for use when selecting primary configs and + // there are multiple configs with the same primary time. + // Smaller numbers mean higher priority. + uint64 priority; + + // source_address_token_boxer_ is used to protect the + // source-address tokens that are given to clients. + // Points to either source_address_token_boxer_storage or the + // default boxer provided by QuicCryptoServerConfig. + const CryptoSecretBoxer* source_address_token_boxer; + + // Holds the override source_address_token_boxer instance if the + // Config is not using the default source address token boxer + // instance provided by QuicCryptoServerConfig. + scoped_ptr<CryptoSecretBoxer> source_address_token_boxer_storage; + private: friend class base::RefCounted<Config>; + virtual ~Config(); DISALLOW_COPY_AND_ASSIGN(Config); @@ -305,6 +369,10 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { typedef std::map<ServerConfigID, scoped_refptr<Config> > ConfigMap; + // Get a ref to the config with a given server config id. + scoped_refptr<Config> GetConfigWithScid( + base::StringPiece requested_scid) const; + // ConfigPrimaryTimeLessThan returns true if a->primary_time < // b->primary_time. static bool ConfigPrimaryTimeLessThan(const scoped_refptr<Config>& a, @@ -319,12 +387,13 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // written to |info|. void EvaluateClientHello( const uint8* primary_orbit, + scoped_refptr<Config> requested_config, ValidateClientHelloResultCallback::Result* client_hello_state, ValidateClientHelloResultCallback* done_cb) const; // BuildRejection sets |out| to be a REJ message in reply to |client_hello|. void BuildRejection( - const scoped_refptr<Config>& config, + const Config& config, const CryptoHandshakeMessage& client_hello, const ClientHelloInfo& info, QuicRandom* rand, @@ -337,16 +406,18 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // NewSourceAddressToken returns a fresh source address token for the given // IP address. - std::string NewSourceAddressToken(const IPEndPoint& ip, + std::string NewSourceAddressToken(const Config& config, + const IPEndPoint& ip, QuicRandom* rand, QuicWallTime now) const; - // ValidateSourceAddressToken returns true if the source address token in - // |token| is a valid and timely token for the IP address |ip| given that the - // current time is |now|. - bool ValidateSourceAddressToken(base::StringPiece token, - const IPEndPoint& ip, - QuicWallTime now) const; + // ValidateSourceAddressToken returns HANDSHAKE_OK if the source address token + // in |token| is a valid and timely token for the IP address |ip| given that + // the current time is |now|. Otherwise it returns the reason for failure. + HandshakeFailureReason ValidateSourceAddressToken(const Config& config, + base::StringPiece token, + const IPEndPoint& ip, + QuicWallTime now) const; // NewServerNonce generates and encrypts a random nonce. std::string NewServerNonce(QuicRandom* rand, QuicWallTime now) const; @@ -354,10 +425,11 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // ValidateServerNonce decrypts |token| and verifies that it hasn't been // previously used and is recent enough that it is plausible that it was part // of a very recently provided rejection ("recent" will be on the order of - // 10-30 seconds). If so, it records that it has been used and returns true. - // Otherwise it returns false. - bool ValidateServerNonce(base::StringPiece echoed_server_nonce, - QuicWallTime now) const; + // 10-30 seconds). If so, it records that it has been used and returns + // HANDSHAKE_OK. Otherwise it returns the reason for failure. + HandshakeFailureReason ValidateServerNonce( + base::StringPiece echoed_server_nonce, + QuicWallTime now) const; // replay_protection_ controls whether the server enforces that handshakes // aren't replays. @@ -377,15 +449,19 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // next_config_promotion_time_ contains the nearest, future time when an // active config will be promoted to primary. mutable QuicWallTime next_config_promotion_time_; + // Callback to invoke when the primary config changes. + scoped_ptr<PrimaryConfigChangedCallback> primary_config_changed_cb_; + // Protects access to the pointer held by strike_register_client_. mutable base::Lock strike_register_client_lock_; // strike_register_ contains a data structure that keeps track of previously // observed client nonces in order to prevent replay attacks. mutable scoped_ptr<StrikeRegisterClient> strike_register_client_; - // source_address_token_boxer_ is used to protect the source-address tokens - // that are given to clients. - CryptoSecretBoxer source_address_token_boxer_; + // Default source_address_token_boxer_ used to protect the + // source-address tokens that are given to clients. Individual + // configs may use boxers with alternate secrets. + CryptoSecretBoxer default_source_address_token_boxer_; // server_nonce_boxer_ is used to encrypt and validate suggested server // nonces. @@ -419,6 +495,8 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { uint32 source_address_token_lifetime_secs_; uint32 server_nonce_strike_register_max_entries_; uint32 server_nonce_strike_register_window_secs_; + + DISALLOW_COPY_AND_ASSIGN(QuicCryptoServerConfig); }; } // namespace net diff --git a/chromium/net/quic/crypto/quic_crypto_server_config_test.cc b/chromium/net/quic/crypto/quic_crypto_server_config_test.cc index fc8882cd85e..22bfd2c6cc6 100644 --- a/chromium/net/quic/crypto/quic_crypto_server_config_test.cc +++ b/chromium/net/quic/crypto/quic_crypto_server_config_test.cc @@ -8,11 +8,14 @@ #include "base/stl_util.h" #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" -#include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/crypto/crypto_handshake_message.h" +#include "net/quic/crypto/crypto_secret_boxer.h" #include "net/quic/crypto/crypto_server_config_protobuf.h" #include "net/quic/crypto/quic_random.h" +#include "net/quic/crypto/strike_register_client.h" #include "net/quic/quic_time.h" #include "net/quic/test_tools/mock_clock.h" +#include "net/quic/test_tools/quic_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -31,16 +34,50 @@ class QuicCryptoServerConfigPeer { explicit QuicCryptoServerConfigPeer(QuicCryptoServerConfig* server_config) : server_config_(server_config) {} - string NewSourceAddressToken(IPEndPoint ip, - QuicRandom* rand, - QuicWallTime now) { - return server_config_->NewSourceAddressToken(ip, rand, now); + scoped_refptr<QuicCryptoServerConfig::Config> GetConfig(string config_id) { + base::AutoLock locked(server_config_->configs_lock_); + if (config_id == "<primary>") { + return scoped_refptr<QuicCryptoServerConfig::Config>( + server_config_->primary_config_); + } else { + return server_config_->GetConfigWithScid(config_id); + } + } + + bool ConfigHasDefaultSourceAddressTokenBoxer(string config_id) { + scoped_refptr<QuicCryptoServerConfig::Config> config = GetConfig(config_id); + return config->source_address_token_boxer == + &(server_config_->default_source_address_token_boxer_); + } + + string NewSourceAddressToken( + string config_id, + IPEndPoint ip, + QuicRandom* rand, + QuicWallTime now) { + return server_config_->NewSourceAddressToken( + *GetConfig(config_id), ip, rand, now); + } + + HandshakeFailureReason ValidateSourceAddressToken(string config_id, + StringPiece srct, + IPEndPoint ip, + QuicWallTime now) { + return server_config_->ValidateSourceAddressToken( + *GetConfig(config_id), srct, ip, now); + } + + string NewServerNonce(QuicRandom* rand, QuicWallTime now) const { + return server_config_->NewServerNonce(rand, now); + } + + HandshakeFailureReason ValidateServerNonce(StringPiece token, + QuicWallTime now) { + return server_config_->ValidateServerNonce(token, now); } - bool ValidateSourceAddressToken(StringPiece srct, - IPEndPoint ip, - QuicWallTime now) { - return server_config_->ValidateSourceAddressToken(srct, ip, now); + base::Lock* GetStrikeRegisterClientLock() { + return &server_config_->strike_register_client_lock_; } // CheckConfigs compares the state of the Configs in |server_config_| to the @@ -144,6 +181,40 @@ class QuicCryptoServerConfigPeer { const QuicCryptoServerConfig* server_config_; }; +class TestStrikeRegisterClient : public StrikeRegisterClient { + public: + explicit TestStrikeRegisterClient(QuicCryptoServerConfig* config) + : config_(config), + is_known_orbit_called_(false) { + } + + virtual bool IsKnownOrbit(StringPiece orbit) const OVERRIDE { + // Ensure that the strike register client lock is not held. + QuicCryptoServerConfigPeer peer(config_); + base::Lock* m = peer.GetStrikeRegisterClientLock(); + // In Chromium, we will dead lock if the lock is held by the current thread. + // Chromium doesn't have AssertNotHeld API call. + // m->AssertNotHeld(); + base::AutoLock lock(*m); + + is_known_orbit_called_ = true; + return true; + } + + virtual void VerifyNonceIsValidAndUnique( + StringPiece nonce, + QuicWallTime now, + ResultCallback* cb) OVERRIDE { + LOG(FATAL) << "Not implemented"; + } + + bool is_known_orbit_called() { return is_known_orbit_called_; } + + private: + QuicCryptoServerConfig* config_; + mutable bool is_known_orbit_called_; +}; + TEST(QuicCryptoServerConfigTest, ServerConfig) { QuicRandom* rand = QuicRandom::GetInstance(); QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand); @@ -154,32 +225,139 @@ TEST(QuicCryptoServerConfigTest, ServerConfig) { QuicCryptoServerConfig::ConfigOptions())); } -TEST(QuicCryptoServerConfigTest, SourceAddressTokens) { +TEST(QuicCryptoServerConfigTest, GetOrbitIsCalledWithoutTheStrikeRegisterLock) { QuicRandom* rand = QuicRandom::GetInstance(); QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand); - IPAddressNumber ip; - CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); - IPEndPoint ip4 = IPEndPoint(ip, 1); - CHECK(ParseIPLiteralToNumber("2001:db8:0::42", &ip)); - IPEndPoint ip6 = IPEndPoint(ip, 2); + MockClock clock; + + TestStrikeRegisterClient* strike_register = + new TestStrikeRegisterClient(&server); + server.SetStrikeRegisterClient(strike_register); + + QuicCryptoServerConfig::ConfigOptions options; + scoped_ptr<CryptoHandshakeMessage>( + server.AddDefaultConfig(rand, &clock, options)); + EXPECT_TRUE(strike_register->is_known_orbit_called()); +} + +TEST(QuicCryptoServerConfigTest, SourceAddressTokens) { + const string kPrimary = "<primary>"; + const string kOverride = "Config with custom source address token key"; + MockClock clock; clock.AdvanceTime(QuicTime::Delta::FromSeconds(1000000)); - QuicCryptoServerConfigPeer peer(&server); QuicWallTime now = clock.WallNow(); const QuicWallTime original_time = now; - const string token4 = peer.NewSourceAddressToken(ip4, rand, now); - const string token6 = peer.NewSourceAddressToken(ip6, rand, now); - EXPECT_TRUE(peer.ValidateSourceAddressToken(token4, ip4, now)); - EXPECT_FALSE(peer.ValidateSourceAddressToken(token4, ip6, now)); - EXPECT_TRUE(peer.ValidateSourceAddressToken(token6, ip6, now)); + QuicRandom* rand = QuicRandom::GetInstance(); + QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand); + QuicCryptoServerConfigPeer peer(&server); + + scoped_ptr<CryptoHandshakeMessage>( + server.AddDefaultConfig(rand, &clock, + QuicCryptoServerConfig::ConfigOptions())); + // Add a config that overrides the default boxer. + QuicCryptoServerConfig::ConfigOptions options; + options.id = kOverride; + scoped_ptr<QuicServerConfigProtobuf> protobuf( + QuicCryptoServerConfig::GenerateConfig(rand, &clock, options)); + protobuf->set_source_address_token_secret_override("a secret key"); + // Lower priority than the default config. + protobuf->set_priority(1); + scoped_ptr<CryptoHandshakeMessage>( + server.AddConfig(protobuf.get(), now)); + + EXPECT_TRUE(peer.ConfigHasDefaultSourceAddressTokenBoxer(kPrimary)); + EXPECT_FALSE(peer.ConfigHasDefaultSourceAddressTokenBoxer(kOverride)); + + IPEndPoint ip4 = IPEndPoint(Loopback4(), 1); + IPEndPoint ip4d = IPEndPoint(ConvertIPv4NumberToIPv6Number(ip4.address()), 1); + IPEndPoint ip6 = IPEndPoint(Loopback6(), 2); + + // Primary config generates configs that validate successfully. + const string token4 = peer.NewSourceAddressToken(kPrimary, ip4, rand, now); + const string token4d = peer.NewSourceAddressToken(kPrimary, ip4d, rand, now); + const string token6 = peer.NewSourceAddressToken(kPrimary, ip6, rand, now); + EXPECT_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken( + kPrimary, token4, ip4, now)); + DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken( + kPrimary, token4, ip4d, now)); + DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, + peer.ValidateSourceAddressToken(kPrimary, token4, ip6, now)); + DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken( + kPrimary, token4d, ip4, now)); + DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken( + kPrimary, token4d, ip4d, now)); + DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, + peer.ValidateSourceAddressToken(kPrimary, token4d, ip6, now)); + DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken( + kPrimary, token6, ip6, now)); + + // Override config generates configs that validate successfully. + const string override_token4 = peer.NewSourceAddressToken( + kOverride, ip4, rand, now); + const string override_token6 = peer.NewSourceAddressToken( + kOverride, ip6, rand, now); + DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken( + kOverride, override_token4, ip4, now)); + DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, + peer.ValidateSourceAddressToken(kOverride, override_token4, ip6, + now)); + DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken( + kOverride, override_token6, ip6, now)); + + // Tokens generated by the primary config do not validate + // successfully against the override config, and vice versa. + DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, + peer.ValidateSourceAddressToken(kOverride, token4, ip4, now)); + DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, + peer.ValidateSourceAddressToken(kOverride, token6, ip6, now)); + DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, + peer.ValidateSourceAddressToken(kPrimary, override_token4, ip4, + now)); + DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, + peer.ValidateSourceAddressToken(kPrimary, override_token6, ip6, + now)); + + // Validation fails after tokens expire. now = original_time.Add(QuicTime::Delta::FromSeconds(86400 * 7)); - EXPECT_FALSE(peer.ValidateSourceAddressToken(token4, ip4, now)); + DCHECK_EQ(SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE, + peer.ValidateSourceAddressToken(kPrimary, token4, ip4, now)); now = original_time.Subtract(QuicTime::Delta::FromSeconds(3600 * 2)); - EXPECT_FALSE(peer.ValidateSourceAddressToken(token4, ip4, now)); + DCHECK_EQ(SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE, + peer.ValidateSourceAddressToken(kPrimary, token4, ip4, now)); +} + +TEST(QuicCryptoServerConfigTest, ValidateServerNonce) { + QuicRandom* rand = QuicRandom::GetInstance(); + QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand); + QuicCryptoServerConfigPeer peer(&server); + + StringPiece message("hello world"); + const size_t key_size = CryptoSecretBoxer::GetKeySize(); + scoped_ptr<uint8[]> key(new uint8[key_size]); + memset(key.get(), 0x11, key_size); + + CryptoSecretBoxer boxer; + boxer.SetKey(StringPiece(reinterpret_cast<char*>(key.get()), key_size)); + const string box = boxer.Box(rand, message); + MockClock clock; + QuicWallTime now = clock.WallNow(); + const QuicWallTime original_time = now; + EXPECT_EQ(SERVER_NONCE_DECRYPTION_FAILURE, + peer.ValidateServerNonce(box, now)); + + string server_nonce = peer.NewServerNonce(rand, now); + EXPECT_EQ(HANDSHAKE_OK, peer.ValidateServerNonce(server_nonce, now)); + EXPECT_EQ(SERVER_NONCE_NOT_UNIQUE_FAILURE, + peer.ValidateServerNonce(server_nonce, now)); + + now = original_time.Add(QuicTime::Delta::FromSeconds(1000 * 7)); + server_nonce = peer.NewServerNonce(rand, now); + EXPECT_EQ(HANDSHAKE_OK, peer.ValidateServerNonce(server_nonce, now)); } class CryptoServerConfigsTest : public ::testing::Test { @@ -202,18 +380,23 @@ class CryptoServerConfigsTest : public ::testing::Test { // SetConfigs(NULL); // calls |config_.SetConfigs| with no protobufs. // // // Calls |config_.SetConfigs| with two protobufs: one for a Config with - // // a |primary_time| of 900, and another with a |primary_time| of 1000. + // // a |primary_time| of 900 and priority 1, and another with + // // a |primary_time| of 1000 and priority 2. + // CheckConfigs( - // "id1", 900, - // "id2", 1000, + // "id1", 900, 1, + // "id2", 1000, 2, // NULL); // // If the server config id starts with "INVALID" then the generated protobuf // will be invalid. void SetConfigs(const char* server_config_id1, ...) { + const char kOrbit[] = "12345678"; + va_list ap; va_start(ap, server_config_id1); bool has_invalid = false; + bool is_empty = true; vector<QuicServerConfigProtobuf*> protobufs; bool first = true; @@ -230,13 +413,17 @@ class CryptoServerConfigsTest : public ::testing::Test { break; } + is_empty = false; int primary_time = va_arg(ap, int); + int priority = va_arg(ap, int); QuicCryptoServerConfig::ConfigOptions options; options.id = server_config_id; + options.orbit = kOrbit; QuicServerConfigProtobuf* protobuf( - QuicCryptoServerConfig::DefaultConfig(rand_, &clock_, options)); + QuicCryptoServerConfig::GenerateConfig(rand_, &clock_, options)); protobuf->set_primary_time(primary_time); + protobuf->set_priority(priority); if (string(server_config_id).find("INVALID") == 0) { protobuf->clear_key(); has_invalid = true; @@ -244,7 +431,8 @@ class CryptoServerConfigsTest : public ::testing::Test { protobufs.push_back(protobuf); } - ASSERT_EQ(!has_invalid, config_.SetConfigs(protobufs, clock_.WallNow())); + ASSERT_EQ(!has_invalid && !is_empty, + config_.SetConfigs(protobufs, clock_.WallNow())); STLDeleteElements(&protobufs); } @@ -261,8 +449,8 @@ TEST_F(CryptoServerConfigsTest, NoConfigs) { TEST_F(CryptoServerConfigsTest, MakePrimaryFirst) { // Make sure that "b" is primary even though "a" comes first. - SetConfigs("a", 1100, - "b", 900, + SetConfigs("a", 1100, 1, + "b", 900, 1, NULL); test_peer_.CheckConfigs( "a", false, @@ -272,8 +460,8 @@ TEST_F(CryptoServerConfigsTest, MakePrimaryFirst) { TEST_F(CryptoServerConfigsTest, MakePrimarySecond) { // Make sure that a remains primary after b is added. - SetConfigs("a", 900, - "b", 1100, + SetConfigs("a", 900, 1, + "b", 1100, 1, NULL); test_peer_.CheckConfigs( "a", true, @@ -283,12 +471,17 @@ TEST_F(CryptoServerConfigsTest, MakePrimarySecond) { TEST_F(CryptoServerConfigsTest, Delete) { // Ensure that configs get deleted when removed. - SetConfigs("a", 800, - "b", 900, - "c", 1100, + SetConfigs("a", 800, 1, + "b", 900, 1, + "c", 1100, 1, NULL); - SetConfigs("b", 900, - "c", 1100, + test_peer_.CheckConfigs( + "a", false, + "b", true, + "c", false, + NULL); + SetConfigs("b", 900, 1, + "c", 1100, 1, NULL); test_peer_.CheckConfigs( "b", true, @@ -296,26 +489,148 @@ TEST_F(CryptoServerConfigsTest, Delete) { NULL); } -TEST_F(CryptoServerConfigsTest, DontDeletePrimary) { - // Ensure that the primary config isn't deleted when removed. - SetConfigs("a", 800, - "b", 900, - "c", 1100, +TEST_F(CryptoServerConfigsTest, DeletePrimary) { + // Ensure that deleting the primary config works. + SetConfigs("a", 800, 1, + "b", 900, 1, + "c", 1100, 1, NULL); - SetConfigs("a", 800, - "c", 1100, + test_peer_.CheckConfigs( + "a", false, + "b", true, + "c", false, + NULL); + SetConfigs("a", 800, 1, + "c", 1100, 1, + NULL); + test_peer_.CheckConfigs( + "a", true, + "c", false, + NULL); +} + +TEST_F(CryptoServerConfigsTest, FailIfDeletingAllConfigs) { + // Ensure that configs get deleted when removed. + SetConfigs("a", 800, 1, + "b", 900, 1, NULL); test_peer_.CheckConfigs( "a", false, "b", true, + NULL); + SetConfigs(NULL); + // Config change is rejected, still using old configs. + test_peer_.CheckConfigs( + "a", false, + "b", true, + NULL); +} + +TEST_F(CryptoServerConfigsTest, ChangePrimaryTime) { + // Check that updates to primary time get picked up. + SetConfigs("a", 400, 1, + "b", 800, 1, + "c", 1200, 1, + NULL); + test_peer_.SelectNewPrimaryConfig(500); + test_peer_.CheckConfigs( + "a", true, + "b", false, + "c", false, + NULL); + SetConfigs("a", 1200, 1, + "b", 800, 1, + "c", 400, 1, + NULL); + test_peer_.SelectNewPrimaryConfig(500); + test_peer_.CheckConfigs( + "a", false, + "b", false, + "c", true, + NULL); +} + +TEST_F(CryptoServerConfigsTest, AllConfigsInThePast) { + // Check that the most recent config is selected. + SetConfigs("a", 400, 1, + "b", 800, 1, + "c", 1200, 1, + NULL); + test_peer_.SelectNewPrimaryConfig(1500); + test_peer_.CheckConfigs( + "a", false, + "b", false, + "c", true, + NULL); +} + +TEST_F(CryptoServerConfigsTest, AllConfigsInTheFuture) { + // Check that the first config is selected. + SetConfigs("a", 400, 1, + "b", 800, 1, + "c", 1200, 1, + NULL); + test_peer_.SelectNewPrimaryConfig(100); + test_peer_.CheckConfigs( + "a", true, + "b", false, "c", false, NULL); } +TEST_F(CryptoServerConfigsTest, SortByPriority) { + // Check that priority is used to decide on a primary config when + // configs have the same primary time. + SetConfigs("a", 900, 1, + "b", 900, 2, + "c", 900, 3, + NULL); + test_peer_.CheckConfigs( + "a", true, + "b", false, + "c", false, + NULL); + test_peer_.SelectNewPrimaryConfig(800); + test_peer_.CheckConfigs( + "a", true, + "b", false, + "c", false, + NULL); + test_peer_.SelectNewPrimaryConfig(1000); + test_peer_.CheckConfigs( + "a", true, + "b", false, + "c", false, + NULL); + + // Change priorities and expect sort order to change. + SetConfigs("a", 900, 2, + "b", 900, 1, + "c", 900, 0, + NULL); + test_peer_.CheckConfigs( + "a", false, + "b", false, + "c", true, + NULL); + test_peer_.SelectNewPrimaryConfig(800); + test_peer_.CheckConfigs( + "a", false, + "b", false, + "c", true, + NULL); + test_peer_.SelectNewPrimaryConfig(1000); + test_peer_.CheckConfigs( + "a", false, + "b", false, + "c", true, + NULL); +} + TEST_F(CryptoServerConfigsTest, AdvancePrimary) { // Check that a new primary config is enabled at the right time. - SetConfigs("a", 900, - "b", 1100, + SetConfigs("a", 900, 1, + "b", 1100, 1, NULL); test_peer_.SelectNewPrimaryConfig(1000); test_peer_.CheckConfigs( @@ -331,18 +646,18 @@ TEST_F(CryptoServerConfigsTest, AdvancePrimary) { TEST_F(CryptoServerConfigsTest, InvalidConfigs) { // Ensure that invalid configs don't change anything. - SetConfigs("a", 800, - "b", 900, - "c", 1100, + SetConfigs("a", 800, 1, + "b", 900, 1, + "c", 1100, 1, NULL); test_peer_.CheckConfigs( "a", false, "b", true, "c", false, NULL); - SetConfigs("a", 800, - "c", 1100, - "INVALID1", 1000, + SetConfigs("a", 800, 1, + "c", 1100, 1, + "INVALID1", 1000, 1, NULL); test_peer_.CheckConfigs( "a", false, diff --git a/chromium/net/quic/crypto/quic_decrypter.cc b/chromium/net/quic/crypto/quic_decrypter.cc index fb19d4c0d70..df1192a85af 100644 --- a/chromium/net/quic/crypto/quic_decrypter.cc +++ b/chromium/net/quic/crypto/quic_decrypter.cc @@ -5,6 +5,8 @@ #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/aes_128_gcm_12_decrypter.h" +#include "net/quic/crypto/chacha20_poly1305_decrypter.h" +#include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/null_decrypter.h" namespace net { @@ -14,6 +16,8 @@ QuicDecrypter* QuicDecrypter::Create(QuicTag algorithm) { switch (algorithm) { case kAESG: return new Aes128Gcm12Decrypter(); + case kCC12: + return new ChaCha20Poly1305Decrypter(); case kNULL: return new NullDecrypter(); default: diff --git a/chromium/net/quic/crypto/quic_decrypter.h b/chromium/net/quic/crypto/quic_decrypter.h index 124d98fea8d..30983493096 100644 --- a/chromium/net/quic/crypto/quic_decrypter.h +++ b/chromium/net/quic/crypto/quic_decrypter.h @@ -6,7 +6,6 @@ #define NET_QUIC_CRYPTO_QUIC_DECRYPTER_H_ #include "net/base/net_export.h" -#include "net/quic/crypto/crypto_protocol.h" #include "net/quic/quic_protocol.h" namespace net { diff --git a/chromium/net/quic/crypto/quic_encrypter.cc b/chromium/net/quic/crypto/quic_encrypter.cc index 489da8ed2ec..a817870174b 100644 --- a/chromium/net/quic/crypto/quic_encrypter.cc +++ b/chromium/net/quic/crypto/quic_encrypter.cc @@ -5,6 +5,8 @@ #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" +#include "net/quic/crypto/chacha20_poly1305_encrypter.h" +#include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/null_encrypter.h" namespace net { @@ -14,6 +16,8 @@ QuicEncrypter* QuicEncrypter::Create(QuicTag algorithm) { switch (algorithm) { case kAESG: return new Aes128Gcm12Encrypter(); + case kCC12: + return new ChaCha20Poly1305Encrypter(); case kNULL: return new NullEncrypter(); default: diff --git a/chromium/net/quic/crypto/quic_encrypter.h b/chromium/net/quic/crypto/quic_encrypter.h index edddf3628b6..37bba1212d1 100644 --- a/chromium/net/quic/crypto/quic_encrypter.h +++ b/chromium/net/quic/crypto/quic_encrypter.h @@ -6,7 +6,6 @@ #define NET_QUIC_CRYPTO_QUIC_ENCRYPTER_H_ #include "net/base/net_export.h" -#include "net/quic/crypto/crypto_protocol.h" #include "net/quic/quic_protocol.h" namespace net { diff --git a/chromium/net/quic/crypto/quic_random.cc b/chromium/net/quic/crypto/quic_random.cc index 73fce2ef2cf..6f460130660 100644 --- a/chromium/net/quic/crypto/quic_random.cc +++ b/chromium/net/quic/crypto/quic_random.cc @@ -23,7 +23,7 @@ class DefaultRandom : public QuicRandom { size_t entropy_len) OVERRIDE; private: - DefaultRandom(); + DefaultRandom() {}; virtual ~DefaultRandom() {} friend struct DefaultSingletonTraits<DefaultRandom>; @@ -48,9 +48,6 @@ void DefaultRandom::Reseed(const void* additional_entropy, size_t entropy_len) { // No such function exists in crypto/random.h. } -DefaultRandom::DefaultRandom() { -} - } // namespace // static diff --git a/chromium/net/quic/crypto/quic_server_info.cc b/chromium/net/quic/crypto/quic_server_info.cc new file mode 100644 index 00000000000..519d7b5fd60 --- /dev/null +++ b/chromium/net/quic/crypto/quic_server_info.cc @@ -0,0 +1,141 @@ +// 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/crypto/quic_server_info.h" + +#include <limits> + +#include "base/pickle.h" + +using std::string; + +namespace { + +const int kQuicCryptoConfigVersion = 1; + +} // namespace + +namespace net { + +QuicServerInfo::State::State() {} + +QuicServerInfo::State::~State() {} + +void QuicServerInfo::State::Clear() { + server_config.clear(); + source_address_token.clear(); + server_config_sig.clear(); + certs.clear(); +} + +QuicServerInfo::QuicServerInfo(const QuicServerId& server_id) + : server_id_(server_id) { +} + +QuicServerInfo::~QuicServerInfo() { +} + +const QuicServerInfo::State& QuicServerInfo::state() const { + return state_; +} + +QuicServerInfo::State* QuicServerInfo::mutable_state() { + return &state_; +} + +bool QuicServerInfo::Parse(const string& data) { + State* state = mutable_state(); + + state->Clear(); + + bool r = ParseInner(data); + if (!r) + state->Clear(); + return r; +} + +bool QuicServerInfo::ParseInner(const string& data) { + State* state = mutable_state(); + + // No data was read from the disk cache. + if (data.empty()) { + return false; + } + + Pickle p(data.data(), data.size()); + PickleIterator iter(p); + + int version = -1; + if (!p.ReadInt(&iter, &version)) { + DVLOG(1) << "Missing version"; + return false; + } + + if (version != kQuicCryptoConfigVersion) { + DVLOG(1) << "Unsupported version"; + return false; + } + + if (!p.ReadString(&iter, &state->server_config)) { + DVLOG(1) << "Malformed server_config"; + return false; + } + if (!p.ReadString(&iter, &state->source_address_token)) { + DVLOG(1) << "Malformed source_address_token"; + return false; + } + if (!p.ReadString(&iter, &state->server_config_sig)) { + DVLOG(1) << "Malformed server_config_sig"; + return false; + } + + // Read certs. + uint32 num_certs; + if (!p.ReadUInt32(&iter, &num_certs)) { + DVLOG(1) << "Malformed num_certs"; + return false; + } + + for (uint32 i = 0; i < num_certs; i++) { + string cert; + if (!p.ReadString(&iter, &cert)) { + DVLOG(1) << "Malformed cert"; + return false; + } + state->certs.push_back(cert); + } + + return true; +} + +string QuicServerInfo::Serialize() { + string pickled_data = SerializeInner(); + state_.Clear(); + return pickled_data; +} + +string QuicServerInfo::SerializeInner() const { + Pickle p(sizeof(Pickle::Header)); + + if (!p.WriteInt(kQuicCryptoConfigVersion) || + !p.WriteString(state_.server_config) || + !p.WriteString(state_.source_address_token) || + !p.WriteString(state_.server_config_sig) || + state_.certs.size() > std::numeric_limits<uint32>::max() || + !p.WriteUInt32(state_.certs.size())) { + return string(); + } + + for (size_t i = 0; i < state_.certs.size(); i++) { + if (!p.WriteString(state_.certs[i])) { + return string(); + } + } + + return string(reinterpret_cast<const char *>(p.data()), p.size()); +} + +QuicServerInfoFactory::~QuicServerInfoFactory() {} + +} // namespace net diff --git a/chromium/net/quic/crypto/quic_server_info.h b/chromium/net/quic/crypto/quic_server_info.h new file mode 100644 index 00000000000..fd54b94ad92 --- /dev/null +++ b/chromium/net/quic/crypto/quic_server_info.h @@ -0,0 +1,118 @@ +// 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_CRYPTO_QUIC_SERVER_INFO_H_ +#define NET_QUIC_CRYPTO_QUIC_SERVER_INFO_H_ + +#include <string> +#include <vector> + +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/time/time.h" +#include "net/base/completion_callback.h" +#include "net/base/net_export.h" +#include "net/quic/quic_server_id.h" + +namespace net { + +class X509Certificate; + +// QuicServerInfo is an interface for fetching information about a QUIC server. +// This information may be stored on disk so does not include keys or other +// sensitive information. Primarily it's intended for caching the QUIC server's +// crypto config. +class NET_EXPORT_PRIVATE QuicServerInfo { + public: + QuicServerInfo(const QuicServerId& server_id); + virtual ~QuicServerInfo(); + + // Start will commence the lookup. This must be called before any other + // methods. By opportunistically calling this early, it may be possible to + // overlap this object's lookup and reduce latency. + virtual void Start() = 0; + + // WaitForDataReady returns OK if the fetch of the requested data has + // completed. Otherwise it returns ERR_IO_PENDING and will call |callback| on + // the current thread when ready. + // + // Only a single callback can be outstanding at a given time and, in the + // event that WaitForDataReady returns OK, it's the caller's responsibility + // to delete |callback|. + // + // |callback| may be NULL, in which case ERR_IO_PENDING may still be returned + // but, obviously, a callback will never be made. + virtual int WaitForDataReady(const CompletionCallback& callback) = 0; + + // Returns true if data is loaded from disk cache and ready (WaitForDataReady + // doesn't have a pending callback). + virtual bool IsDataReady() = 0; + + // Returns true if the object is ready to persist data, in other words, if + // data is loaded from disk cache and ready and there are no pending writes. + virtual bool IsReadyToPersist() = 0; + + // Persist allows for the server information to be updated for future users. + // This is a fire and forget operation: the caller may drop its reference + // from this object and the store operation will still complete. This can + // only be called once WaitForDataReady has returned OK or called its + // callback. + virtual void Persist() = 0; + + struct State { + State(); + ~State(); + + void Clear(); + + // This class matches QuicClientCryptoConfig::CachedState. + std::string server_config; // A serialized handshake message. + std::string source_address_token; // An opaque proof of IP ownership. + std::vector<std::string> certs; // A list of certificates in leaf-first + // order. + std::string server_config_sig; // A signature of |server_config_|. + + private: + DISALLOW_COPY_AND_ASSIGN(State); + }; + + // Once the data is ready, it can be read using the following members. These + // members can then be updated before calling |Persist|. + const State& state() const; + State* mutable_state(); + + protected: + // Parse parses pickled data and fills out the public member fields of this + // object. It returns true iff the parse was successful. The public member + // fields will be set to something sane in any case. + bool Parse(const std::string& data); + std::string Serialize(); + State state_; + + private: + // ParseInner is a helper function for Parse. + bool ParseInner(const std::string& data); + + // SerializeInner is a helper function for Serialize. + std::string SerializeInner() const; + + // This is the QUIC server (hostname, port, is_https, privacy_mode) tuple for + // which we restore the crypto_config. + const QuicServerId server_id_; + + DISALLOW_COPY_AND_ASSIGN(QuicServerInfo); +}; + +class QuicServerInfoFactory { + public: + virtual ~QuicServerInfoFactory(); + + // GetForServer returns a fresh, allocated QuicServerInfo for the given + // |server_id| or NULL on failure. + virtual QuicServerInfo* GetForServer(const QuicServerId& server_id) = 0; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_QUIC_SERVER_INFO_H_ diff --git a/chromium/net/quic/crypto/scoped_evp_aead_ctx.cc b/chromium/net/quic/crypto/scoped_evp_aead_ctx.cc new file mode 100644 index 00000000000..2b5feb37fca --- /dev/null +++ b/chromium/net/quic/crypto/scoped_evp_aead_ctx.cc @@ -0,0 +1,23 @@ +// 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/crypto/scoped_evp_aead_ctx.h" + +namespace net { + +ScopedEVPAEADCtx::ScopedEVPAEADCtx() { + ctx_.aead = NULL; +} + +ScopedEVPAEADCtx::~ScopedEVPAEADCtx() { + if (ctx_.aead != NULL) { + EVP_AEAD_CTX_cleanup(&ctx_); + } +} + +EVP_AEAD_CTX* ScopedEVPAEADCtx::get() { + return &ctx_; +} + +} // namespace net diff --git a/chromium/net/quic/crypto/scoped_evp_aead_ctx.h b/chromium/net/quic/crypto/scoped_evp_aead_ctx.h new file mode 100644 index 00000000000..f0f04c1caaf --- /dev/null +++ b/chromium/net/quic/crypto/scoped_evp_aead_ctx.h @@ -0,0 +1,31 @@ +// 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_CRYPTO_SCOPED_EVP_AEAD_CTX_H_ +#define NET_QUIC_CRYPTO_SCOPED_EVP_AEAD_CTX_H_ + +#include <openssl/evp.h> + +#include "base/basictypes.h" + +namespace net { + +// ScopedEVPAEADCtx manages an EVP_AEAD_CTX object and calls the needed cleanup +// functions when it goes out of scope. +class ScopedEVPAEADCtx { + public: + ScopedEVPAEADCtx(); + ~ScopedEVPAEADCtx(); + + EVP_AEAD_CTX* get(); + + private: + EVP_AEAD_CTX ctx_; + + DISALLOW_COPY_AND_ASSIGN(ScopedEVPAEADCtx); +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_SCOPED_EVP_AEAD_CTX_H_ diff --git a/chromium/net/quic/crypto/scoped_evp_cipher_ctx.cc b/chromium/net/quic/crypto/scoped_evp_cipher_ctx.cc deleted file mode 100644 index b904f870fdd..00000000000 --- a/chromium/net/quic/crypto/scoped_evp_cipher_ctx.cc +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/crypto/scoped_evp_cipher_ctx.h" - -#include <openssl/evp.h> - -namespace net { - -ScopedEVPCipherCtx::ScopedEVPCipherCtx() - : ctx_(EVP_CIPHER_CTX_new()) { } - -ScopedEVPCipherCtx::~ScopedEVPCipherCtx() { - EVP_CIPHER_CTX_free(ctx_); -} - -EVP_CIPHER_CTX* ScopedEVPCipherCtx::get() const { - return ctx_; -} - -} // namespace net diff --git a/chromium/net/quic/crypto/scoped_evp_cipher_ctx.h b/chromium/net/quic/crypto/scoped_evp_cipher_ctx.h deleted file mode 100644 index ec0fd51b92a..00000000000 --- a/chromium/net/quic/crypto/scoped_evp_cipher_ctx.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef NET_QUIC_CRYPTO_SCOPED_EVP_CIPHER_CTX_H_ -#define NET_QUIC_CRYPTO_SCOPED_EVP_CIPHER_CTX_H_ - -typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX; - -namespace net { - -// TODO(wtc): this is the same as the ScopedCipherCTX class defined in -// crypto/encryptor_openssl.cc. Eliminate the duplicate code. -// crypto::ScopedOpenSSL is not suitable for EVP_CIPHER_CTX because -// there are no EVP_CIPHER_CTX_create and EVP_CIPHER_CTX_destroy -// functions. -class ScopedEVPCipherCtx { - public: - ScopedEVPCipherCtx(); - ~ScopedEVPCipherCtx(); - - EVP_CIPHER_CTX* get() const; - - private: - EVP_CIPHER_CTX* const ctx_; -}; - -} // namespace net - -#endif // NET_QUIC_CRYPTO_SCOPED_EVP_CIPHER_CTX_H_ diff --git a/chromium/net/quic/crypto/source_address_token.h b/chromium/net/quic/crypto/source_address_token.h index 8c509651e28..fbb50b1028e 100644 --- a/chromium/net/quic/crypto/source_address_token.h +++ b/chromium/net/quic/crypto/source_address_token.h @@ -41,6 +41,8 @@ class SourceAddressToken { private: std::string ip_; int64 timestamp_; + + DISALLOW_COPY_AND_ASSIGN(SourceAddressToken); }; } // namespace net diff --git a/chromium/net/quic/crypto/strike_register.cc b/chromium/net/quic/crypto/strike_register.cc index f45bfabd9f0..9aec6ffb812 100644 --- a/chromium/net/quic/crypto/strike_register.cc +++ b/chromium/net/quic/crypto/strike_register.cc @@ -60,6 +60,13 @@ class StrikeRegister::InternalNode { // to consider times that are before the creation time. static const uint32 kCreationTimeFromInternalEpoch = 63115200.0; // 2 years. +void StrikeRegister::ValidateStrikeRegisterConfig(unsigned max_entries) { + // We only have 23 bits of index available. + CHECK_LT(max_entries, 1u << 23); + CHECK_GT(max_entries, 1u); // There must be at least two entries. + CHECK_EQ(sizeof(InternalNode), 8u); // in case of compiler changes. +} + StrikeRegister::StrikeRegister(unsigned max_entries, uint32 current_time, uint32 window_secs, @@ -78,10 +85,7 @@ StrikeRegister::StrikeRegister(unsigned max_entries, horizon_valid_(startup == DENY_REQUESTS_AT_STARTUP) { memcpy(orbit_, orbit, sizeof(orbit_)); - // We only have 23 bits of index available. - CHECK_LT(max_entries, 1u << 23); - CHECK_GT(max_entries, 1u); // There must be at least two entries. - CHECK_EQ(sizeof(InternalNode), 8u); // in case of compiler changes. + ValidateStrikeRegisterConfig(max_entries); internal_nodes_ = new InternalNode[max_entries]; external_nodes_.reset(new uint8[kExternalNodeSize * max_entries]); diff --git a/chromium/net/quic/crypto/strike_register.h b/chromium/net/quic/crypto/strike_register.h index fda62a802b2..3f49e92e3d5 100644 --- a/chromium/net/quic/crypto/strike_register.h +++ b/chromium/net/quic/crypto/strike_register.h @@ -81,6 +81,9 @@ class NET_EXPORT_PRIVATE StrikeRegister { // external node. We flag the 24th bit to mark a pointer as external. static const uint32 kExternalFlag; + // Allows early validation before a strike register is created. + static void ValidateStrikeRegisterConfig(unsigned max_entries); + // Construct a new set which can hold, at most, |max_entries| (which must be // less than 2**23). See the comments around StartupType about initial // behaviour. Otherwise, all nonces that are outside +/- |window_secs| from @@ -178,6 +181,8 @@ class NET_EXPORT_PRIVATE StrikeRegister { // this header. InternalNode* internal_nodes_; scoped_ptr<uint8[]> external_nodes_; + + DISALLOW_COPY_AND_ASSIGN(StrikeRegister); }; } // namespace net diff --git a/chromium/net/quic/crypto/strike_register_client.h b/chromium/net/quic/crypto/strike_register_client.h index 4a27bd7b85c..e37827a899d 100644 --- a/chromium/net/quic/crypto/strike_register_client.h +++ b/chromium/net/quic/crypto/strike_register_client.h @@ -7,6 +7,7 @@ #include <string> +#include "base/basictypes.h" #include "base/strings/string_piece.h" #include "net/base/net_export.h" #include "net/quic/quic_time.h" @@ -38,8 +39,8 @@ class NET_EXPORT_PRIVATE StrikeRegisterClient { StrikeRegisterClient() {} virtual ~StrikeRegisterClient() {} - // Returns the strike server orbit if known, else empty string. - virtual std::string orbit() = 0; + // Returns true iff the strike register knows about the given orbit. + virtual bool IsKnownOrbit(base::StringPiece orbit) const = 0; // Validate a nonce for freshness and uniqueness. // Will invoke cb->Run(ValidateResponse::nonce_is_valid_and_unique()) // once the asynchronous operation is complete. diff --git a/chromium/net/quic/iovector.h b/chromium/net/quic/iovector.h index 74fcbef3087..22d2cc9a8cd 100644 --- a/chromium/net/quic/iovector.h +++ b/chromium/net/quic/iovector.h @@ -57,7 +57,6 @@ inline size_t TotalIovecLength(const struct iovec* iov, size_t iovcnt) { // to avoid accidentally change an entry that is assembled by two or more // Append()'s by simply an index access. // - class NET_EXPORT_PRIVATE IOVector { public: // Provide a default constructor so it'll never be inhibited by adding other diff --git a/chromium/net/quic/quic_ack_notifier.cc b/chromium/net/quic/quic_ack_notifier.cc index 5eaf38581dc..3bd65e50a35 100644 --- a/chromium/net/quic/quic_ack_notifier.cc +++ b/chromium/net/quic/quic_ack_notifier.cc @@ -9,39 +9,54 @@ #include "base/logging.h" #include "base/stl_util.h" +using base::hash_map; +using std::make_pair; + namespace net { +QuicAckNotifier::PacketInfo::PacketInfo() : packet_payload_size(0) { +} + +QuicAckNotifier::PacketInfo::PacketInfo(int payload_size) + : packet_payload_size(payload_size) { +} + QuicAckNotifier::DelegateInterface::DelegateInterface() {} QuicAckNotifier::DelegateInterface::~DelegateInterface() {} QuicAckNotifier::QuicAckNotifier(DelegateInterface* delegate) - : delegate_(delegate) { - DCHECK(delegate_); + : delegate_(delegate), + original_packet_count_(0), + original_byte_count_(0), + retransmitted_packet_count_(0), + retransmitted_byte_count_(0) { + DCHECK(delegate); } -QuicAckNotifier::~QuicAckNotifier() {} - -void QuicAckNotifier::AddSequenceNumber( - const QuicPacketSequenceNumber& sequence_number) { - sequence_numbers_.insert(sequence_number); +QuicAckNotifier::~QuicAckNotifier() { } -void QuicAckNotifier::AddSequenceNumbers( - const SequenceNumberSet& sequence_numbers) { - for (SequenceNumberSet::const_iterator it = sequence_numbers.begin(); - it != sequence_numbers.end(); ++it) { - AddSequenceNumber(*it); - } +void QuicAckNotifier::AddSequenceNumber( + const QuicPacketSequenceNumber& sequence_number, + int packet_payload_size) { + sequence_numbers_.insert(make_pair(sequence_number, + PacketInfo(packet_payload_size))); + ++original_packet_count_; + original_byte_count_ += packet_payload_size; } -bool QuicAckNotifier::OnAck(QuicPacketSequenceNumber sequence_number) { +bool QuicAckNotifier::OnAck(QuicPacketSequenceNumber sequence_number, + QuicTime::Delta delta_largest_observed) { DCHECK(ContainsKey(sequence_numbers_, sequence_number)); sequence_numbers_.erase(sequence_number); if (IsEmpty()) { // We have seen all the sequence numbers we were waiting for, trigger // callback notification. - delegate_->OnAckNotification(); + delegate_->OnAckNotification( + original_packet_count_, original_byte_count_, + retransmitted_packet_count_, retransmitted_byte_count_, + delta_largest_observed); return true; } return false; @@ -50,8 +65,21 @@ bool QuicAckNotifier::OnAck(QuicPacketSequenceNumber sequence_number) { void QuicAckNotifier::UpdateSequenceNumber( QuicPacketSequenceNumber old_sequence_number, QuicPacketSequenceNumber new_sequence_number) { - sequence_numbers_.erase(old_sequence_number); - sequence_numbers_.insert(new_sequence_number); + DCHECK(!ContainsKey(sequence_numbers_, new_sequence_number)); + + PacketInfo packet_info; + hash_map<QuicPacketSequenceNumber, PacketInfo>::iterator it = + sequence_numbers_.find(old_sequence_number); + if (it != sequence_numbers_.end()) { + packet_info = it->second; + sequence_numbers_.erase(it); + } else { + DLOG(DFATAL) << "Old sequence number not found."; + } + + ++retransmitted_packet_count_; + retransmitted_byte_count_ += packet_info.packet_payload_size; + sequence_numbers_.insert(make_pair(new_sequence_number, packet_info)); } }; // namespace net diff --git a/chromium/net/quic/quic_ack_notifier.h b/chromium/net/quic/quic_ack_notifier.h index 5ec92134e78..f19d0f8c6b9 100644 --- a/chromium/net/quic/quic_ack_notifier.h +++ b/chromium/net/quic/quic_ack_notifier.h @@ -5,6 +5,7 @@ #ifndef NET_QUIC_QUIC_ACK_NOTIFIER_H_ #define NET_QUIC_QUIC_ACK_NOTIFIER_H_ +#include "base/memory/ref_counted.h" #include "net/quic/quic_protocol.h" namespace net { @@ -16,22 +17,35 @@ namespace net { // trigger a call to a provided Closure. class NET_EXPORT_PRIVATE QuicAckNotifier { public: - class NET_EXPORT_PRIVATE DelegateInterface { + class NET_EXPORT_PRIVATE DelegateInterface + : public base::RefCounted<DelegateInterface> { public: DelegateInterface(); + // Args: + // num_original_packets - Number of packets in the original transmission. + // num_original_bytes - Number of packets in the original transmission. + // num_retransmitted_packets - Number of packets that had to be + // retransmitted. + // num_retransmitted_bytes - Number of bytes that had to be retransmitted. + virtual void OnAckNotification(int num_original_packets, + int num_original_bytes, + int num_retransmitted_packets, + int num_retransmitted_bytes, + QuicTime::Delta delta_largest_observed) = 0; + protected: + friend class base::RefCounted<DelegateInterface>; + + // Delegates are ref counted. virtual ~DelegateInterface(); - virtual void OnAckNotification() = 0; }; + // QuicAckNotifier is expected to keep its own reference to the delegate. explicit QuicAckNotifier(DelegateInterface* delegate); virtual ~QuicAckNotifier(); // Register a sequence number that this AckNotifier should be interested in. - void AddSequenceNumber(const QuicPacketSequenceNumber& sequence_number); - - // Register a set of sequence numbers that this AckNotifier should be - // interested in. - void AddSequenceNumbers(const SequenceNumberSet& sequence_numbers); + void AddSequenceNumber(const QuicPacketSequenceNumber& sequence_number, + int packet_payload_size); // Called by the QuicConnection on receipt of new ACK frame, with the sequence // number referenced by the ACK frame. @@ -41,7 +55,8 @@ class NET_EXPORT_PRIVATE QuicAckNotifier { // // Returns true if the provided sequence_number caused the delegate to be // called, false otherwise. - bool OnAck(QuicPacketSequenceNumber sequence_number); + bool OnAck(QuicPacketSequenceNumber sequence_number, + QuicTime::Delta delta_largest_observed); bool IsEmpty() { return sequence_numbers_.empty(); } @@ -52,14 +67,33 @@ class NET_EXPORT_PRIVATE QuicAckNotifier { QuicPacketSequenceNumber new_sequence_number); private: + struct PacketInfo { + PacketInfo(); + explicit PacketInfo(int payload_size); + + int packet_payload_size; + }; + // The delegate's OnAckNotification() method will be called once we have been // notified of ACKs for all the sequence numbers we are tracking. // This is not owned by OnAckNotifier and must outlive it. - DelegateInterface* delegate_; + scoped_refptr<DelegateInterface> delegate_; + + // Sequence numbers this notifier is waiting to hear about. The + // delegate will not be called until this is empty. + base::hash_map<QuicPacketSequenceNumber, PacketInfo> sequence_numbers_; + + // Transmission and retransmission stats. + // Number of packets in the original transmission. + int original_packet_count_; + // Number of packets in the original transmission. + int original_byte_count_; + // Number of packets that had to be retransmitted. + int retransmitted_packet_count_; + // Number of bytes that had to be retransmitted. + int retransmitted_byte_count_; - // Set of sequence numbers this notifier is waiting to hear about. The - // delegate will not be called until this is an empty set. - SequenceNumberSet sequence_numbers_; + DISALLOW_COPY_AND_ASSIGN(QuicAckNotifier); }; }; // namespace net diff --git a/chromium/net/quic/quic_ack_notifier_manager.cc b/chromium/net/quic/quic_ack_notifier_manager.cc index 8676315d22f..70cb927a86d 100644 --- a/chromium/net/quic/quic_ack_notifier_manager.cc +++ b/chromium/net/quic/quic_ack_notifier_manager.cc @@ -23,7 +23,8 @@ AckNotifierManager::~AckNotifierManager() { } void AckNotifierManager::OnPacketAcked( - QuicPacketSequenceNumber sequence_number) { + QuicPacketSequenceNumber sequence_number, + QuicTime::Delta delta_largest_observed) { // Inform all the registered AckNotifiers of the new ACK. AckNotifierMap::iterator map_it = ack_notifier_map_.find(sequence_number); if (map_it == ack_notifier_map_.end()) { @@ -36,7 +37,7 @@ void AckNotifierManager::OnPacketAcked( for (AckNotifierSet::iterator set_it = map_it->second.begin(); set_it != map_it->second.end(); ++set_it) { QuicAckNotifier* ack_notifier = *set_it; - ack_notifier->OnAck(sequence_number); + ack_notifier->OnAck(sequence_number, delta_largest_observed); // If this has resulted in an empty AckNotifer, erase it. if (ack_notifier->IsEmpty()) { @@ -90,7 +91,8 @@ void AckNotifierManager::OnSerializedPacket( // The AckNotifier needs to know it is tracking this packet's sequence // number. - notifier->AddSequenceNumber(serialized_packet.sequence_number); + notifier->AddSequenceNumber(serialized_packet.sequence_number, + serialized_packet.packet->length()); // Update the mapping in the other direction, from sequence // number to AckNotifier. diff --git a/chromium/net/quic/quic_ack_notifier_manager.h b/chromium/net/quic/quic_ack_notifier_manager.h index 5ddf794e7bb..bf5b345d106 100644 --- a/chromium/net/quic/quic_ack_notifier_manager.h +++ b/chromium/net/quic/quic_ack_notifier_manager.h @@ -31,7 +31,6 @@ class QuicAckNotifier; // a set of AckNotifiers and a map from sequence number to AckNotifier the sake // of efficiency - we can quickly check the map to see if any AckNotifiers are // interested in a given sequence number. - class NET_EXPORT_PRIVATE AckNotifierManager { public: AckNotifierManager(); @@ -40,7 +39,8 @@ class NET_EXPORT_PRIVATE AckNotifierManager { // Called when the connection receives a new AckFrame. If |sequence_number| // exists in ack_notifier_map_ then the corresponding AckNotifiers will have // their OnAck method called. - void OnPacketAcked(QuicPacketSequenceNumber sequence_number); + void OnPacketAcked(QuicPacketSequenceNumber sequence_number, + QuicTime::Delta delta_largest_observed); // If a packet has been retransmitted with a new sequence number, then this // will be called. It updates the mapping in ack_notifier_map_, and also @@ -71,6 +71,8 @@ class NET_EXPORT_PRIVATE AckNotifierManager { // number, call OnAck for all mapped AckNotifiers. // Does not own the AckNotifiers. AckNotifierMap ack_notifier_map_; + + DISALLOW_COPY_AND_ASSIGN(AckNotifierManager); }; } // namespace net diff --git a/chromium/net/quic/quic_ack_notifier_test.cc b/chromium/net/quic/quic_ack_notifier_test.cc index 8facee340f0..63bc25405b4 100644 --- a/chromium/net/quic/quic_ack_notifier_test.cc +++ b/chromium/net/quic/quic_ack_notifier_test.cc @@ -8,40 +8,44 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using testing::_; + namespace net { namespace test { namespace { class QuicAckNotifierTest : public ::testing::Test { protected: + QuicAckNotifierTest() : zero_(QuicTime::Delta::Zero()) {} + virtual void SetUp() { - notifier_.reset(new QuicAckNotifier(&delegate_)); + delegate_ = new MockAckNotifierDelegate; + notifier_.reset(new QuicAckNotifier(delegate_)); - sequence_numbers_.insert(26); - sequence_numbers_.insert(99); - sequence_numbers_.insert(1234); - notifier_->AddSequenceNumbers(sequence_numbers_); + notifier_->AddSequenceNumber(26, 100); + notifier_->AddSequenceNumber(99, 20); + notifier_->AddSequenceNumber(1234, 3); } - SequenceNumberSet sequence_numbers_; - MockAckNotifierDelegate delegate_; + MockAckNotifierDelegate* delegate_; scoped_ptr<QuicAckNotifier> notifier_; + QuicTime::Delta zero_; }; // Should trigger callback when we receive acks for all the registered seqnums. TEST_F(QuicAckNotifierTest, TriggerCallback) { - EXPECT_CALL(delegate_, OnAckNotification()).Times(1); - EXPECT_FALSE(notifier_->OnAck(26)); - EXPECT_FALSE(notifier_->OnAck(99)); - EXPECT_TRUE(notifier_->OnAck(1234)); + EXPECT_CALL(*delegate_, OnAckNotification(3, 123, 0, 0, zero_)).Times(1); + EXPECT_FALSE(notifier_->OnAck(26, zero_)); + EXPECT_FALSE(notifier_->OnAck(99, zero_)); + EXPECT_TRUE(notifier_->OnAck(1234, zero_)); } // Should not trigger callback if we never provide all the seqnums. TEST_F(QuicAckNotifierTest, DoesNotTrigger) { // Should not trigger callback as not all packets have been seen. - EXPECT_CALL(delegate_, OnAckNotification()).Times(0); - EXPECT_FALSE(notifier_->OnAck(26)); - EXPECT_FALSE(notifier_->OnAck(99)); + EXPECT_CALL(*delegate_, OnAckNotification(_, _, _, _, _)).Times(0); + EXPECT_FALSE(notifier_->OnAck(26, zero_)); + EXPECT_FALSE(notifier_->OnAck(99, zero_)); } // Should trigger even after updating sequence numbers and receiving ACKs for @@ -51,10 +55,24 @@ TEST_F(QuicAckNotifierTest, UpdateSeqNums) { notifier_->UpdateSequenceNumber(99, 3000); notifier_->UpdateSequenceNumber(1234, 3001); - EXPECT_CALL(delegate_, OnAckNotification()).Times(1); - EXPECT_FALSE(notifier_->OnAck(26)); // original - EXPECT_FALSE(notifier_->OnAck(3000)); // updated - EXPECT_TRUE(notifier_->OnAck(3001)); // updated + EXPECT_CALL(*delegate_, OnAckNotification(3, 123, 2, 20 + 3, _)).Times(1); + EXPECT_FALSE(notifier_->OnAck(26, zero_)); // original + EXPECT_FALSE(notifier_->OnAck(3000, zero_)); // updated + EXPECT_TRUE(notifier_->OnAck(3001, zero_)); // updated +} + +// Make sure the delegate is called with the delta time from the last ACK. +TEST_F(QuicAckNotifierTest, DeltaTime) { + const QuicTime::Delta first_delta = QuicTime::Delta::FromSeconds(5); + const QuicTime::Delta second_delta = QuicTime::Delta::FromSeconds(33); + const QuicTime::Delta third_delta = QuicTime::Delta::FromSeconds(10); + + EXPECT_CALL(*delegate_, + OnAckNotification(3, 123, 0, 0, third_delta)) + .Times(1); + EXPECT_FALSE(notifier_->OnAck(26, first_delta)); + EXPECT_FALSE(notifier_->OnAck(99, second_delta)); + EXPECT_TRUE(notifier_->OnAck(1234, third_delta)); } } // namespace diff --git a/chromium/net/quic/quic_address_mismatch.cc b/chromium/net/quic/quic_address_mismatch.cc new file mode 100644 index 00000000000..96aaef41030 --- /dev/null +++ b/chromium/net/quic/quic_address_mismatch.cc @@ -0,0 +1,52 @@ +// 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/quic_address_mismatch.h" + +#include "base/logging.h" +#include "net/base/ip_endpoint.h" + +namespace net { + +int GetAddressMismatch(const IPEndPoint& first_address, + const IPEndPoint& second_address) { + if (first_address.address().empty() || second_address.address().empty()) { + return -1; + } + IPAddressNumber first_ip_address = first_address.address(); + if (IsIPv4Mapped(first_ip_address)) { + first_ip_address = ConvertIPv4MappedToIPv4(first_ip_address); + } + IPAddressNumber second_ip_address = second_address.address(); + if (IsIPv4Mapped(second_ip_address)) { + second_ip_address = ConvertIPv4MappedToIPv4(second_ip_address); + } + + int sample; + if (first_ip_address != second_ip_address) { + sample = QUIC_ADDRESS_MISMATCH_BASE; + } else if (first_address.port() != second_address.port()) { + sample = QUIC_PORT_MISMATCH_BASE; + } else { + sample = QUIC_ADDRESS_AND_PORT_MATCH_BASE; + } + + // Add an offset to |sample|: + // V4_V4: add 0 + // V6_V6: add 1 + // V4_V6: add 2 + // V6_V4: add 3 + bool first_ipv4 = (first_ip_address.size() == kIPv4AddressSize); + bool second_ipv4 = (second_ip_address.size() == kIPv4AddressSize); + if (first_ipv4 != second_ipv4) { + CHECK_EQ(sample, QUIC_ADDRESS_MISMATCH_BASE); + sample += 2; + } + if (!first_ipv4) { + sample += 1; + } + return sample; +} + +} // namespace net diff --git a/chromium/net/quic/quic_address_mismatch.h b/chromium/net/quic/quic_address_mismatch.h new file mode 100644 index 00000000000..3179c20a93a --- /dev/null +++ b/chromium/net/quic/quic_address_mismatch.h @@ -0,0 +1,44 @@ +// 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_QUIC_ADDRESS_MISMATCH_H_ +#define NET_QUIC_QUIC_ADDRESS_MISMATCH_H_ + +#include "net/base/net_export.h" + +namespace net { + +class IPEndPoint; + +enum QuicAddressMismatch { + // The addresses don't match. + QUIC_ADDRESS_MISMATCH_BASE = 0, + QUIC_ADDRESS_MISMATCH_V4_V4 = 0, + QUIC_ADDRESS_MISMATCH_V6_V6 = 1, + QUIC_ADDRESS_MISMATCH_V4_V6 = 2, + QUIC_ADDRESS_MISMATCH_V6_V4 = 3, + + // The addresses match, but the ports don't match. + QUIC_PORT_MISMATCH_BASE = 4, + QUIC_PORT_MISMATCH_V4_V4 = 4, + QUIC_PORT_MISMATCH_V6_V6 = 5, + + QUIC_ADDRESS_AND_PORT_MATCH_BASE = 6, + QUIC_ADDRESS_AND_PORT_MATCH_V4_V4 = 6, + QUIC_ADDRESS_AND_PORT_MATCH_V6_V6 = 7, + + QUIC_ADDRESS_MISMATCH_MAX, +}; + +// Returns a value of the QuicAddressMismatch enum type that indicates how +// |first_address| differs from |second_address|. Returns -1 if either address +// is empty. +// +// Only used by the Net.QuicSession.PublicResetAddressMismatch histogram. +NET_EXPORT_PRIVATE int GetAddressMismatch(const IPEndPoint& first_address, + const IPEndPoint& second_address); + +} // namespace net + +#endif // NET_QUIC_QUIC_ADDRESS_MISMATCH_H_ diff --git a/chromium/net/quic/quic_address_mismatch_test.cc b/chromium/net/quic/quic_address_mismatch_test.cc new file mode 100644 index 00000000000..b7c683b5a48 --- /dev/null +++ b/chromium/net/quic/quic_address_mismatch_test.cc @@ -0,0 +1,113 @@ +// 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/quic_address_mismatch.h" + +#include "net/base/ip_endpoint.h" +#include "net/base/net_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +// Test all cases of the GetAddressMismatch function. +TEST(QuicAddressMismatchTest, GetAddressMismatch) { + IPAddressNumber ip4_1; + IPAddressNumber ip4_2; + IPAddressNumber ip6_1; + IPAddressNumber ip6_2; + IPAddressNumber ip4_mapped_1; + IPAddressNumber ip4_mapped_2; + ASSERT_TRUE(ParseIPLiteralToNumber("1.2.3.4", &ip4_1)); + ASSERT_TRUE(ParseIPLiteralToNumber("5.6.7.8", &ip4_2)); + ASSERT_TRUE(ParseIPLiteralToNumber("1234::1", &ip6_1)); + ASSERT_TRUE(ParseIPLiteralToNumber("1234::2", &ip6_2)); + ip4_mapped_1 = ConvertIPv4NumberToIPv6Number(ip4_1); + ip4_mapped_2 = ConvertIPv4NumberToIPv6Number(ip4_2); + ASSERT_NE(ip4_1, ip4_2); + ASSERT_NE(ip6_1, ip6_2); + ASSERT_NE(ip4_mapped_1, ip4_mapped_2); + + EXPECT_EQ(-1, GetAddressMismatch(IPEndPoint(), IPEndPoint())); + EXPECT_EQ(-1, GetAddressMismatch(IPEndPoint(), IPEndPoint(ip4_1, 443))); + EXPECT_EQ(-1, GetAddressMismatch(IPEndPoint(ip4_1, 443), IPEndPoint())); + + EXPECT_EQ(QUIC_ADDRESS_AND_PORT_MATCH_V4_V4, + GetAddressMismatch(IPEndPoint(ip4_1, 443), + IPEndPoint(ip4_1, 443))); + EXPECT_EQ(QUIC_ADDRESS_AND_PORT_MATCH_V4_V4, + GetAddressMismatch(IPEndPoint(ip4_1, 443), + IPEndPoint(ip4_mapped_1, 443))); + EXPECT_EQ(QUIC_ADDRESS_AND_PORT_MATCH_V4_V4, + GetAddressMismatch(IPEndPoint(ip4_mapped_1, 443), + IPEndPoint(ip4_mapped_1, 443))); + EXPECT_EQ(QUIC_ADDRESS_AND_PORT_MATCH_V6_V6, + GetAddressMismatch(IPEndPoint(ip6_1, 443), + IPEndPoint(ip6_1, 443))); + + EXPECT_EQ(QUIC_PORT_MISMATCH_V4_V4, + GetAddressMismatch(IPEndPoint(ip4_1, 80), + IPEndPoint(ip4_1, 443))); + EXPECT_EQ(QUIC_PORT_MISMATCH_V4_V4, + GetAddressMismatch(IPEndPoint(ip4_1, 80), + IPEndPoint(ip4_mapped_1, 443))); + EXPECT_EQ(QUIC_PORT_MISMATCH_V4_V4, + GetAddressMismatch(IPEndPoint(ip4_mapped_1, 80), + IPEndPoint(ip4_mapped_1, 443))); + EXPECT_EQ(QUIC_PORT_MISMATCH_V6_V6, + GetAddressMismatch(IPEndPoint(ip6_1, 80), + IPEndPoint(ip6_1, 443))); + + EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4, + GetAddressMismatch(IPEndPoint(ip4_1, 443), + IPEndPoint(ip4_2, 443))); + EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4, + GetAddressMismatch(IPEndPoint(ip4_1, 443), + IPEndPoint(ip4_mapped_2, 443))); + EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4, + GetAddressMismatch(IPEndPoint(ip4_mapped_1, 443), + IPEndPoint(ip4_mapped_2, 443))); + EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4, + GetAddressMismatch(IPEndPoint(ip4_1, 80), + IPEndPoint(ip4_2, 443))); + EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4, + GetAddressMismatch(IPEndPoint(ip4_1, 80), + IPEndPoint(ip4_mapped_2, 443))); + EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4, + GetAddressMismatch(IPEndPoint(ip4_mapped_1, 80), + IPEndPoint(ip4_mapped_2, 443))); + EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V6, + GetAddressMismatch(IPEndPoint(ip6_1, 443), + IPEndPoint(ip6_2, 443))); + EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V6, + GetAddressMismatch(IPEndPoint(ip6_1, 80), + IPEndPoint(ip6_2, 443))); + EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V6, + GetAddressMismatch(IPEndPoint(ip4_1, 443), + IPEndPoint(ip6_1, 443))); + EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V6, + GetAddressMismatch(IPEndPoint(ip4_mapped_1, 443), + IPEndPoint(ip6_1, 443))); + EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V6, + GetAddressMismatch(IPEndPoint(ip4_1, 80), + IPEndPoint(ip6_1, 443))); + EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V6, + GetAddressMismatch(IPEndPoint(ip4_mapped_1, 80), + IPEndPoint(ip6_1, 443))); + EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V4, + GetAddressMismatch(IPEndPoint(ip6_1, 443), + IPEndPoint(ip4_1, 443))); + EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V4, + GetAddressMismatch(IPEndPoint(ip6_1, 443), + IPEndPoint(ip4_mapped_1, 443))); + EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V4, + GetAddressMismatch(IPEndPoint(ip6_1, 80), + IPEndPoint(ip4_1, 443))); + EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V4, + GetAddressMismatch(IPEndPoint(ip6_1, 80), + IPEndPoint(ip4_mapped_1, 443))); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_bandwidth.h b/chromium/net/quic/quic_bandwidth.h index 6412aa5c68d..4ae953d00ec 100644 --- a/chromium/net/quic/quic_bandwidth.h +++ b/chromium/net/quic/quic_bandwidth.h @@ -15,7 +15,6 @@ namespace net { typedef uint64 QuicByteCount; class NET_EXPORT_PRIVATE QuicBandwidth { - public: // Creates a new QuicBandwidth with an internal value of 0. static QuicBandwidth Zero(); diff --git a/chromium/net/quic/quic_blocked_writer_interface.h b/chromium/net/quic/quic_blocked_writer_interface.h index a694193fe31..a6f6faf8175 100644 --- a/chromium/net/quic/quic_blocked_writer_interface.h +++ b/chromium/net/quic/quic_blocked_writer_interface.h @@ -18,9 +18,8 @@ class NET_EXPORT_PRIVATE QuicBlockedWriterInterface { virtual ~QuicBlockedWriterInterface() {} // Called by the PacketWriter when the underlying socket becomes writable - // so that the BlockedWriter can go ahead and try writing. This methods - // should return false if the socket has become blocked while writing. - virtual bool OnCanWrite() = 0; + // so that the BlockedWriter can go ahead and try writing. + virtual void OnCanWrite() = 0; }; } // namespace net diff --git a/chromium/net/quic/quic_client_session.cc b/chromium/net/quic/quic_client_session.cc index ab70e7f445c..745692c2e12 100644 --- a/chromium/net/quic/quic_client_session.cc +++ b/chromium/net/quic/quic_client_session.cc @@ -13,10 +13,14 @@ #include "base/values.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" +#include "net/quic/crypto/proof_verifier_chromium.h" +#include "net/quic/crypto/quic_server_info.h" #include "net/quic/quic_connection_helper.h" #include "net/quic/quic_crypto_client_stream_factory.h" #include "net/quic/quic_default_packet_writer.h" +#include "net/quic/quic_server_id.h" #include "net/quic/quic_stream_factory.h" +#include "net/ssl/ssl_connection_status_flags.h" #include "net/ssl/ssl_info.h" #include "net/udp/datagram_client_socket.h" @@ -24,6 +28,53 @@ namespace net { namespace { +// The length of time to wait for a 0-RTT handshake to complete +// before allowing the requests to possibly proceed over TCP. +const int k0RttHandshakeTimeoutMs = 300; + +// Histograms for tracking down the crashes from http://crbug.com/354669 +// Note: these values must be kept in sync with the corresponding values in: +// tools/metrics/histograms/histograms.xml +enum Location { + DESTRUCTOR = 0, + ADD_OBSERVER = 1, + TRY_CREATE_STREAM = 2, + CREATE_OUTGOING_RELIABLE_STREAM = 3, + NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER = 4, + NOTIFY_FACTORY_OF_SESSION_CLOSED = 5, + NUM_LOCATIONS = 6, +}; + +void RecordUnexpectedOpenStreams(Location location) { + UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedOpenStreams", location, + NUM_LOCATIONS); +} + +void RecordUnexpectedObservers(Location location) { + UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedObservers", location, + NUM_LOCATIONS); +} + +void RecordUnexpectedNotGoingAway(Location location) { + UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedNotGoingAway", location, + NUM_LOCATIONS); +} + +// Histogram for recording the different reasons that a QUIC session is unable +// to complete the handshake. +enum HandshakeFailureReason { + HANDSHAKE_FAILURE_UNKNOWN = 0, + HANDSHAKE_FAILURE_BLACK_HOLE = 1, + HANDSHAKE_FAILURE_PUBLIC_RESET = 2, + NUM_HANDSHAKE_FAILURE_REASONS = 3, +}; + +void RecordHandshakeFailureReason(HandshakeFailureReason reason) { + UMA_HISTOGRAM_ENUMERATION( + "Net.QuicSession.ConnectionClose.HandshakeNotConfirmed.Reason", + reason, NUM_HANDSHAKE_FAILURE_REASONS); +} + // Note: these values must be kept in sync with the corresponding values in: // tools/metrics/histograms/histograms.xml enum HandshakeState { @@ -86,49 +137,74 @@ QuicClientSession::QuicClientSession( scoped_ptr<QuicDefaultPacketWriter> writer, QuicStreamFactory* stream_factory, QuicCryptoClientStreamFactory* crypto_client_stream_factory, - const string& server_hostname, + scoped_ptr<QuicServerInfo> server_info, + const QuicServerId& server_id, const QuicConfig& config, QuicCryptoClientConfig* crypto_config, + base::TaskRunner* task_runner, NetLog* net_log) - : QuicSession(connection, config), + : QuicClientSessionBase(connection, + config), require_confirmation_(false), stream_factory_(stream_factory), socket_(socket.Pass()), writer_(writer.Pass()), read_buffer_(new IOBufferWithSize(kMaxPacketSize)), + server_info_(server_info.Pass()), read_pending_(false), num_total_streams_(0), + task_runner_(task_runner), net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_QUIC_SESSION)), logger_(net_log_), num_packets_read_(0), + going_away_(false), weak_factory_(this) { crypto_stream_.reset( crypto_client_stream_factory ? crypto_client_stream_factory->CreateQuicCryptoClientStream( - server_hostname, this, crypto_config) : - new QuicCryptoClientStream(server_hostname, this, crypto_config)); + server_id, this, crypto_config) : + new QuicCryptoClientStream(server_id, this, + new ProofVerifyContextChromium(net_log_), + crypto_config)); connection->set_debug_visitor(&logger_); // TODO(rch): pass in full host port proxy pair net_log_.BeginEvent( NetLog::TYPE_QUIC_SESSION, - NetLog::StringCallback("host", &server_hostname)); + NetLog::StringCallback("host", &server_id.host())); } QuicClientSession::~QuicClientSession() { - // The session must be closed before it is destroyed. - DCHECK(streams()->empty()); - CloseAllStreams(ERR_UNEXPECTED); - DCHECK(observers_.empty()); - CloseAllObservers(ERR_UNEXPECTED); - - connection()->set_debug_visitor(NULL); - net_log_.EndEvent(NetLog::TYPE_QUIC_SESSION); + if (!streams()->empty()) + RecordUnexpectedOpenStreams(DESTRUCTOR); + if (!observers_.empty()) + RecordUnexpectedObservers(DESTRUCTOR); + if (!going_away_) + RecordUnexpectedNotGoingAway(DESTRUCTOR); + + while (!streams()->empty() || + !observers_.empty() || + !stream_requests_.empty()) { + // The session must be closed before it is destroyed. + DCHECK(streams()->empty()); + CloseAllStreams(ERR_UNEXPECTED); + DCHECK(observers_.empty()); + CloseAllObservers(ERR_UNEXPECTED); + + connection()->set_debug_visitor(NULL); + net_log_.EndEvent(NetLog::TYPE_QUIC_SESSION); + + while (!stream_requests_.empty()) { + StreamRequest* request = stream_requests_.front(); + stream_requests_.pop_front(); + request->OnRequestCompleteFailure(ERR_ABORTED); + } + } - while (!stream_requests_.empty()) { - StreamRequest* request = stream_requests_.front(); - stream_requests_.pop_front(); - request->OnRequestCompleteFailure(ERR_ABORTED); + if (connection()->connected()) { + // Ensure that the connection is closed by the time the session is + // destroyed. + connection()->CloseConnection(QUIC_INTERNAL_ERROR, false); } if (IsEncryptionEstablished()) @@ -138,17 +214,59 @@ QuicClientSession::~QuicClientSession() { else RecordHandshakeState(STATE_FAILED); + UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumTotalStreams", num_total_streams_); UMA_HISTOGRAM_COUNTS("Net.QuicNumSentClientHellos", crypto_stream_->num_sent_client_hellos()); - if (IsCryptoHandshakeConfirmed()) { - UMA_HISTOGRAM_COUNTS("Net.QuicNumSentClientHellosCryptoHandshakeConfirmed", - crypto_stream_->num_sent_client_hellos()); - } + if (!IsCryptoHandshakeConfirmed()) + return; - UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumTotalStreams", num_total_streams_); + // Sending one client_hello means we had zero handshake-round-trips. + int round_trip_handshakes = crypto_stream_->num_sent_client_hellos() - 1; + + // Don't bother with these histogram during tests, which mock out + // num_sent_client_hellos(). + if (round_trip_handshakes < 0 || !stream_factory_) + return; + + bool port_selected = stream_factory_->enable_port_selection(); + SSLInfo ssl_info; + if (!GetSSLInfo(&ssl_info) || !ssl_info.cert) { + if (port_selected) { + UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectSelectPortForHTTP", + round_trip_handshakes, 0, 3, 4); + } else { + UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectRandomPortForHTTP", + round_trip_handshakes, 0, 3, 4); + } + } else { + if (port_selected) { + UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectSelectPortForHTTPS", + round_trip_handshakes, 0, 3, 4); + } else { + UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectRandomPortForHTTPS", + round_trip_handshakes, 0, 3, 4); + } + } + const QuicConnectionStats stats = connection()->GetStats(); + if (stats.max_sequence_reordering == 0) + return; + const uint64 kMaxReordering = 100; + uint64 reordering = kMaxReordering; + if (stats.min_rtt_us > 0 ) { + reordering = + GG_UINT64_C(100) * stats.max_time_reordering_us / stats.min_rtt_us; + } + UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.MaxReorderingTime", + reordering, 0, kMaxReordering, 50); + if (stats.min_rtt_us > 100 * 1000) { + UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.MaxReorderingTimeLongRtt", + reordering, 0, kMaxReordering, 50); + } + UMA_HISTOGRAM_COUNTS("Net.QuicSession.MaxReordering", + stats.max_sequence_reordering); } -bool QuicClientSession::OnStreamFrames( +void QuicClientSession::OnStreamFrames( const std::vector<QuicStreamFrame>& frames) { // Record total number of stream frames. UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesInPacket", frames.size()); @@ -169,6 +287,12 @@ bool QuicClientSession::OnStreamFrames( } void QuicClientSession::AddObserver(Observer* observer) { + if (going_away_) { + RecordUnexpectedObservers(ADD_OBSERVER); + observer->OnSessionClosed(ERR_UNEXPECTED); + return; + } + DCHECK(!ContainsKey(observers_, observer)); observers_.insert(observer); } @@ -195,6 +319,11 @@ int QuicClientSession::TryCreateStream(StreamRequest* request, return ERR_CONNECTION_CLOSED; } + if (going_away_) { + RecordUnexpectedOpenStreams(TRY_CREATE_STREAM); + return ERR_CONNECTION_CLOSED; + } + if (GetNumOpenStreams() < get_max_open_streams()) { *stream = CreateOutgoingReliableStreamImpl(); return OK; @@ -221,15 +350,18 @@ QuicReliableClientStream* QuicClientSession::CreateOutgoingDataStream() { } if (GetNumOpenStreams() >= get_max_open_streams()) { DVLOG(1) << "Failed to create a new outgoing stream. " - << "Already " << GetNumOpenStreams() << " open."; + << "Already " << GetNumOpenStreams() << " open."; return NULL; } if (goaway_received()) { DVLOG(1) << "Failed to create a new outgoing stream. " - << "Already received goaway."; + << "Already received goaway."; + return NULL; + } + if (going_away_) { + RecordUnexpectedOpenStreams(CREATE_OUTGOING_RELIABLE_STREAM); return NULL; } - return CreateOutgoingReliableStreamImpl(); } @@ -248,14 +380,60 @@ QuicCryptoClientStream* QuicClientSession::GetCryptoStream() { return crypto_stream_.get(); }; -bool QuicClientSession::GetSSLInfo(SSLInfo* ssl_info) { - DCHECK(crypto_stream_.get()); - return crypto_stream_->GetSSLInfo(ssl_info); +// TODO(rtenneti): Add unittests for GetSSLInfo which exercise the various ways +// we learn about SSL info (sync vs async vs cached). +bool QuicClientSession::GetSSLInfo(SSLInfo* ssl_info) const { + ssl_info->Reset(); + if (!cert_verify_result_) { + return false; + } + + ssl_info->cert_status = cert_verify_result_->cert_status; + ssl_info->cert = cert_verify_result_->verified_cert; + + // TODO(wtc): Define QUIC "cipher suites". + // Report the TLS cipher suite that most closely resembles the crypto + // parameters of the QUIC connection. + QuicTag aead = crypto_stream_->crypto_negotiated_params().aead; + int cipher_suite; + int security_bits; + switch (aead) { + case kAESG: + cipher_suite = 0xc02f; // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + security_bits = 128; + break; + case kCC12: + cipher_suite = 0xcc13; // TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + security_bits = 256; + break; + default: + NOTREACHED(); + return false; + } + int ssl_connection_status = 0; + ssl_connection_status |= + (cipher_suite & SSL_CONNECTION_CIPHERSUITE_MASK) << + SSL_CONNECTION_CIPHERSUITE_SHIFT; + ssl_connection_status |= + (SSL_CONNECTION_VERSION_QUIC & SSL_CONNECTION_VERSION_MASK) << + SSL_CONNECTION_VERSION_SHIFT; + + ssl_info->public_key_hashes = cert_verify_result_->public_key_hashes; + ssl_info->is_issued_by_known_root = + cert_verify_result_->is_issued_by_known_root; + + ssl_info->connection_status = ssl_connection_status; + ssl_info->client_cert_sent = false; + ssl_info->channel_id_sent = false; + ssl_info->security_bits = security_bits; + ssl_info->handshake_type = SSLInfo::HANDSHAKE_FULL; + return true; } int QuicClientSession::CryptoConnect(bool require_confirmation, const CompletionCallback& callback) { require_confirmation_ = require_confirmation; + handshake_start_ = base::TimeTicks::Now(); RecordHandshakeState(STATE_STARTED); if (!crypto_stream_->CryptoConnect()) { // TODO(wtc): change crypto_stream_.CryptoConnect() to return a @@ -263,20 +441,64 @@ int QuicClientSession::CryptoConnect(bool require_confirmation, return ERR_CONNECTION_FAILED; } - bool can_notify = require_confirmation_ ? - IsCryptoHandshakeConfirmed() : IsEncryptionEstablished(); - if (can_notify) { + if (IsCryptoHandshakeConfirmed()) + return OK; + + // Unless we require handshake confirmation, activate the session if + // we have established initial encryption. + if (!require_confirmation_ && IsEncryptionEstablished()) { + // To mitigate the effects of hanging 0-RTT connections, set up a timer to + // cancel any requests, if the handshake takes too long. + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind(&QuicClientSession::OnConnectTimeout, + weak_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds(k0RttHandshakeTimeoutMs)); return OK; + } callback_ = callback; return ERR_IO_PENDING; } +int QuicClientSession::ResumeCryptoConnect(const CompletionCallback& callback) { + + if (IsCryptoHandshakeConfirmed()) + return OK; + + if (!connection()->connected()) + return ERR_QUIC_HANDSHAKE_FAILED; + + callback_ = callback; + return ERR_IO_PENDING; +} + int QuicClientSession::GetNumSentClientHellos() const { return crypto_stream_->num_sent_client_hellos(); } +bool QuicClientSession::CanPool(const std::string& hostname) const { + // TODO(rch): When QUIC supports channel ID or client certificates, this + // logic will need to be revised. + DCHECK(connection()->connected()); + SSLInfo ssl_info; + if (!GetSSLInfo(&ssl_info) || !ssl_info.cert) { + // We can always pool with insecure QUIC sessions. + return true; + } + + // Disable pooling for secure sessions. + // TODO(rch): re-enable this. + return false; + +#if 0 + bool unused = false; + // Only pool secure QUIC sessions if the cert matches the new hostname. + return ssl_info.cert->VerifyNameMatch(hostname, &unused); +#endif +} + QuicDataStream* QuicClientSession::CreateIncomingDataStream( QuicStreamId id) { DLOG(ERROR) << "Server push not supported"; @@ -284,13 +506,20 @@ QuicDataStream* QuicClientSession::CreateIncomingDataStream( } void QuicClientSession::CloseStream(QuicStreamId stream_id) { + ReliableQuicStream* stream = GetStream(stream_id); + if (stream) { + logger_.UpdateReceivedFrameCounts( + stream_id, stream->num_frames_received(), + stream->num_duplicate_frames_received()); + } QuicSession::CloseStream(stream_id); OnClosedStream(); } void QuicClientSession::SendRstStream(QuicStreamId id, - QuicRstStreamErrorCode error) { - QuicSession::SendRstStream(id, error); + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written) { + QuicSession::SendRstStream(id, error, bytes_written); OnClosedStream(); } @@ -299,6 +528,7 @@ void QuicClientSession::OnClosedStream() { !stream_requests_.empty() && crypto_stream_->encryption_established() && !goaway_received() && + !going_away_ && connection()->connected()) { StreamRequest* request = stream_requests_.front(); stream_requests_.pop_front(); @@ -320,6 +550,8 @@ void QuicClientSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { base::ResetAndReturn(&callback_).Run(OK); } if (event == HANDSHAKE_CONFIRMED) { + UMA_HISTOGRAM_TIMES("Net.QuicSession.HandshakeConfirmedTime", + base::TimeTicks::Now() - handshake_start_); ObserverSet::iterator it = observers_.begin(); while (it != observers_.end()) { Observer* observer = *it; @@ -357,14 +589,31 @@ void QuicClientSession::OnConnectionClosed(QuicErrorCode error, "Net.QuicSession.ConnectionClose.NumOpenStreams.TimedOut", GetNumOpenStreams()); if (!IsCryptoHandshakeConfirmed()) { - // If there have been any streams created, they were 0-RTT speculative - // requests that have not be serviced. + UMA_HISTOGRAM_COUNTS( + "Net.QuicSession.ConnectionClose.NumOpenStreams.HandshakeTimedOut", + GetNumOpenStreams()); UMA_HISTOGRAM_COUNTS( "Net.QuicSession.ConnectionClose.NumTotalStreams.HandshakeTimedOut", num_total_streams_); } } + if (!IsCryptoHandshakeConfirmed()) { + if (error == QUIC_PUBLIC_RESET) { + RecordHandshakeFailureReason(HANDSHAKE_FAILURE_PUBLIC_RESET); + } else if (connection()->GetStats().packets_received == 0) { + RecordHandshakeFailureReason(HANDSHAKE_FAILURE_BLACK_HOLE); + UMA_HISTOGRAM_SPARSE_SLOWLY( + "Net.QuicSession.ConnectionClose.HandshakeFailureBlackHole.QuicError", + error); + } else { + RecordHandshakeFailureReason(HANDSHAKE_FAILURE_UNKNOWN); + UMA_HISTOGRAM_SPARSE_SLOWLY( + "Net.QuicSession.ConnectionClose.HandshakeFailureUnknown.QuicError", + error); + } + } + UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.QuicVersion", connection()->version()); NotifyFactoryOfSessionGoingAway(); @@ -385,6 +634,34 @@ void QuicClientSession::OnSuccessfulVersionNegotiation( QuicSession::OnSuccessfulVersionNegotiation(version); } +void QuicClientSession::OnProofValid( + const QuicCryptoClientConfig::CachedState& cached) { + DCHECK(cached.proof_valid()); + + if (!server_info_ || !server_info_->IsReadyToPersist()) { + return; + } + + QuicServerInfo::State* state = server_info_->mutable_state(); + + state->server_config = cached.server_config(); + state->source_address_token = cached.source_address_token(); + state->server_config_sig = cached.signature(); + state->certs = cached.certs(); + + server_info_->Persist(); +} + +void QuicClientSession::OnProofVerifyDetailsAvailable( + const ProofVerifyDetails& verify_details) { + const CertVerifyResult* cert_verify_result_other = + &(reinterpret_cast<const ProofVerifyDetailsChromium*>( + &verify_details))->cert_verify_result; + CertVerifyResult* result_copy = new CertVerifyResult; + result_copy->CopyFrom(*cert_verify_result_other); + cert_verify_result_.reset(result_copy); +} + void QuicClientSession::StartReading() { if (read_pending_) { return; @@ -402,8 +679,8 @@ void QuicClientSession::StartReading() { if (++num_packets_read_ > 32) { num_packets_read_ = 0; // Data was read, process it. - // Schedule the work through the message loop to avoid recursive - // callbacks. + // Schedule the work through the message loop to 1) prevent infinite + // recursion and 2) avoid blocking the thread for too long. base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&QuicClientSession::OnReadComplete, @@ -430,7 +707,8 @@ void QuicClientSession::CloseSessionOnErrorInner(int net_error, NetLog::TYPE_QUIC_SESSION_CLOSE_ON_ERROR, NetLog::IntegerCallback("net_error", net_error)); - connection()->CloseConnection(quic_error, false); + if (connection()->connected()) + connection()->CloseConnection(quic_error, false); DCHECK(!connection()->connected()); } @@ -451,15 +729,41 @@ void QuicClientSession::CloseAllObservers(int net_error) { } } -base::Value* QuicClientSession::GetInfoAsValue(const HostPortPair& pair) const { +base::Value* QuicClientSession::GetInfoAsValue( + const std::set<HostPortPair>& aliases) { base::DictionaryValue* dict = new base::DictionaryValue(); - dict->SetString("host_port_pair", pair.ToString()); + // TODO(rch): remove "host_port_pair" when Chrome 34 is stable. + dict->SetString("host_port_pair", aliases.begin()->ToString()); dict->SetString("version", QuicVersionToString(connection()->version())); dict->SetInteger("open_streams", GetNumOpenStreams()); + base::ListValue* stream_list = new base::ListValue(); + for (base::hash_map<QuicStreamId, QuicDataStream*>::const_iterator it + = streams()->begin(); + it != streams()->end(); + ++it) { + stream_list->Append(new base::StringValue( + base::Uint64ToString(it->second->id()))); + } + dict->Set("active_streams", stream_list); + dict->SetInteger("total_streams", num_total_streams_); dict->SetString("peer_address", peer_address().ToString()); - dict->SetString("guid", base::Uint64ToString(guid())); + dict->SetString("connection_id", base::Uint64ToString(connection_id())); dict->SetBoolean("connected", connection()->connected()); + const QuicConnectionStats& stats = connection()->GetStats(); + dict->SetInteger("packets_sent", stats.packets_sent); + dict->SetInteger("packets_received", stats.packets_received); + dict->SetInteger("packets_lost", stats.packets_lost); + SSLInfo ssl_info; + dict->SetBoolean("secure", GetSSLInfo(&ssl_info) && ssl_info.cert); + + base::ListValue* alias_list = new base::ListValue(); + for (std::set<HostPortPair>::const_iterator it = aliases.begin(); + it != aliases.end(); it++) { + alias_list->Append(new base::StringValue(it->ToString())); + } + dict->Set("aliases", alias_list); + return dict; } @@ -481,9 +785,7 @@ void QuicClientSession::OnReadComplete(int result) { return; } - scoped_refptr<IOBufferWithSize> buffer(read_buffer_); - read_buffer_ = new IOBufferWithSize(kMaxPacketSize); - QuicEncryptedPacket packet(buffer->data(), result); + QuicEncryptedPacket packet(read_buffer_->data(), result); IPEndPoint local_address; IPEndPoint peer_address; socket_->GetLocalAddress(&local_address); @@ -492,18 +794,26 @@ void QuicClientSession::OnReadComplete(int result) { // use a weak pointer to be safe. connection()->ProcessUdpPacket(local_address, peer_address, packet); if (!connection()->connected()) { - stream_factory_->OnSessionClosed(this); + NotifyFactoryOfSessionClosedLater(); return; } StartReading(); } void QuicClientSession::NotifyFactoryOfSessionGoingAway() { + going_away_ = true; if (stream_factory_) stream_factory_->OnSessionGoingAway(this); } void QuicClientSession::NotifyFactoryOfSessionClosedLater() { + if (!streams()->empty()) + RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER); + + if (!going_away_) + RecordUnexpectedNotGoingAway(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER); + + going_away_ = true; DCHECK_EQ(0u, GetNumOpenStreams()); DCHECK(!connection()->connected()); base::MessageLoop::current()->PostTask( @@ -513,10 +823,31 @@ void QuicClientSession::NotifyFactoryOfSessionClosedLater() { } void QuicClientSession::NotifyFactoryOfSessionClosed() { + if (!streams()->empty()) + RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED); + + if (!going_away_) + RecordUnexpectedNotGoingAway(NOTIFY_FACTORY_OF_SESSION_CLOSED); + + going_away_ = true; DCHECK_EQ(0u, GetNumOpenStreams()); // Will delete |this|. if (stream_factory_) stream_factory_->OnSessionClosed(this); } +void QuicClientSession::OnConnectTimeout() { + DCHECK(callback_.is_null()); + DCHECK(IsEncryptionEstablished()); + + if (IsCryptoHandshakeConfirmed()) + return; + + // TODO(rch): re-enable this code once beta is cut. + // if (stream_factory_) + // stream_factory_->OnSessionConnectTimeout(this); + // CloseAllStreams(ERR_QUIC_HANDSHAKE_FAILED); + // DCHECK_EQ(0u, GetNumOpenStreams()); +} + } // namespace net diff --git a/chromium/net/quic/quic_client_session.h b/chromium/net/quic/quic_client_session.h index 07da96aa96f..7fbe3db5cd1 100644 --- a/chromium/net/quic/quic_client_session.h +++ b/chromium/net/quic/quic_client_session.h @@ -12,20 +12,27 @@ #include <string> +#include "base/basictypes.h" #include "base/containers/hash_tables.h" #include "base/memory/scoped_ptr.h" +#include "base/time/time.h" #include "net/base/completion_callback.h" +#include "net/proxy/proxy_server.h" +#include "net/quic/quic_client_session_base.h" #include "net/quic/quic_connection_logger.h" #include "net/quic/quic_crypto_client_stream.h" +#include "net/quic/quic_protocol.h" #include "net/quic/quic_reliable_client_stream.h" -#include "net/quic/quic_session.h" namespace net { +class CertVerifyResult; class DatagramClientSocket; class QuicConnectionHelper; class QuicCryptoClientStreamFactory; class QuicDefaultPacketWriter; +class QuicServerId; +class QuicServerInfo; class QuicStreamFactory; class SSLInfo; @@ -33,7 +40,7 @@ namespace test { class QuicClientSessionPeer; } // namespace test -class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession { +class NET_EXPORT_PRIVATE QuicClientSession : public QuicClientSessionBase { public: // An interface for observing events on a session. class NET_EXPORT_PRIVATE Observer { @@ -88,11 +95,12 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession { scoped_ptr<QuicDefaultPacketWriter> writer, QuicStreamFactory* stream_factory, QuicCryptoClientStreamFactory* crypto_client_stream_factory, - const std::string& server_hostname, + scoped_ptr<QuicServerInfo> server_info, + const QuicServerId& server_id, const QuicConfig& config, QuicCryptoClientConfig* crypto_config, + base::TaskRunner* task_runner, NetLog* net_log); - virtual ~QuicClientSession(); void AddObserver(Observer* observer); @@ -112,19 +120,26 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession { void CancelRequest(StreamRequest* request); // QuicSession methods: - virtual bool OnStreamFrames( + virtual void OnStreamFrames( const std::vector<QuicStreamFrame>& frames) OVERRIDE; virtual QuicReliableClientStream* CreateOutgoingDataStream() OVERRIDE; virtual QuicCryptoClientStream* GetCryptoStream() OVERRIDE; virtual void CloseStream(QuicStreamId stream_id) OVERRIDE; virtual void SendRstStream(QuicStreamId id, - QuicRstStreamErrorCode error) OVERRIDE; + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written) OVERRIDE; virtual void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) OVERRIDE; virtual void OnCryptoHandshakeMessageSent( const CryptoHandshakeMessage& message) OVERRIDE; virtual void OnCryptoHandshakeMessageReceived( const CryptoHandshakeMessage& message) OVERRIDE; - virtual bool GetSSLInfo(SSLInfo* ssl_info) OVERRIDE; + virtual bool GetSSLInfo(SSLInfo* ssl_info) const OVERRIDE; + + // QuicClientSessionBase methods: + virtual void OnProofValid( + const QuicCryptoClientConfig::CachedState& cached) OVERRIDE; + virtual void OnProofVerifyDetailsAvailable( + const ProofVerifyDetails& verify_details) OVERRIDE; // QuicConnectionVisitorInterface methods: virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE; @@ -135,6 +150,9 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession { int CryptoConnect(bool require_confirmation, const CompletionCallback& callback); + // Resumes a crypto handshake with the server after a timeout. + int ResumeCryptoConnect(const CompletionCallback& callback); + // Causes the QuicConnectionHelper to start reading from the socket // and passing the data along to the QuicConnection. void StartReading(); @@ -143,7 +161,7 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession { // that this session has been closed, which will delete the session. void CloseSessionOnError(int error); - base::Value* GetInfoAsValue(const HostPortPair& pair) const; + base::Value* GetInfoAsValue(const std::set<HostPortPair>& aliases); const BoundNetLog& net_log() const { return net_log_; } @@ -154,6 +172,11 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession { // than the number of round-trips needed for the handshake. int GetNumSentClientHellos() const; + // Returns true if |hostname| may be pooled onto this session. If this + // is a secure QUIC session, then |hostname| must match the certificate + // presented during the handshake. + bool CanPool(const std::string& hostname) const; + protected: // QuicSession methods: virtual QuicDataStream* CreateIncomingDataStream(QuicStreamId id) OVERRIDE; @@ -194,21 +217,30 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession { // delete |this|. void NotifyFactoryOfSessionClosed(); + void OnConnectTimeout(); + bool require_confirmation_; scoped_ptr<QuicCryptoClientStream> crypto_stream_; QuicStreamFactory* stream_factory_; scoped_ptr<DatagramClientSocket> socket_; scoped_ptr<QuicDefaultPacketWriter> writer_; scoped_refptr<IOBufferWithSize> read_buffer_; + scoped_ptr<QuicServerInfo> server_info_; + scoped_ptr<CertVerifyResult> cert_verify_result_; ObserverSet observers_; StreamRequestQueue stream_requests_; bool read_pending_; CompletionCallback callback_; size_t num_total_streams_; + base::TaskRunner* task_runner_; BoundNetLog net_log_; + base::TimeTicks handshake_start_; // Time the handshake was started. QuicConnectionLogger logger_; // Number of packets read in the current read loop. size_t num_packets_read_; + // True when the session is going away, and streams may no longer be created + // on this session. Existing stream will continue to be processed. + bool going_away_; base::WeakPtrFactory<QuicClientSession> weak_factory_; DISALLOW_COPY_AND_ASSIGN(QuicClientSession); diff --git a/chromium/net/quic/quic_client_session_base.cc b/chromium/net/quic/quic_client_session_base.cc new file mode 100644 index 00000000000..4343011437e --- /dev/null +++ b/chromium/net/quic/quic_client_session_base.cc @@ -0,0 +1,16 @@ +// 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/quic_client_session_base.h" + +namespace net { + +QuicClientSessionBase::QuicClientSessionBase( + QuicConnection* connection, + const QuicConfig& config) + : QuicSession(connection, config) {} + +QuicClientSessionBase::~QuicClientSessionBase() {} + +} // namespace net diff --git a/chromium/net/quic/quic_client_session_base.h b/chromium/net/quic/quic_client_session_base.h new file mode 100644 index 00000000000..eab5d08df98 --- /dev/null +++ b/chromium/net/quic/quic_client_session_base.h @@ -0,0 +1,41 @@ +// 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_QUIC_CLIENT_SESSION_BASE_H_ +#define NET_QUIC_QUIC_CLIENT_SESSION_BASE_H_ + +#include "net/quic/quic_crypto_client_stream.h" +#include "net/quic/quic_session.h" + +namespace net { + +// Base class for all client-specific QuicSession subclasses. +class NET_EXPORT_PRIVATE QuicClientSessionBase : public QuicSession { + public: + QuicClientSessionBase(QuicConnection* connection, + const QuicConfig& config); + + virtual ~QuicClientSessionBase(); + + // Called when the proof in |cached| is marked valid. If this is a secure + // QUIC session, then this will happen only after the proof verifier + // completes. If this is an insecure QUIC connection, this will happen + // as soon as a valid config is discovered (either from the cache or + // from the server). + virtual void OnProofValid( + const QuicCryptoClientConfig::CachedState& cached) = 0; + + // Called when proof verification details become available, either because + // proof verification is complete, or when cached details are used. This + // will only be called for secure QUIC connections. + virtual void OnProofVerifyDetailsAvailable( + const ProofVerifyDetails& verify_details) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(QuicClientSessionBase); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_CLIENT_SESSION_BASE_H_ diff --git a/chromium/net/quic/quic_client_session_test.cc b/chromium/net/quic/quic_client_session_test.cc index f0b4dd4ecab..264fbfa5270 100644 --- a/chromium/net/quic/quic_client_session_test.cc +++ b/chromium/net/quic/quic_client_session_test.cc @@ -13,10 +13,12 @@ #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/crypto/quic_server_info.h" #include "net/quic/quic_default_packet_writer.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/quic_client_session_peer.h" #include "net/quic/test_tools/quic_test_utils.h" +#include "net/quic/test_tools/simple_quic_framer.h" #include "net/socket/socket_test_util.h" #include "net/udp/datagram_client_socket.h" @@ -27,24 +29,21 @@ namespace test { namespace { const char kServerHostname[] = "www.example.com"; +const uint16 kServerPort = 80; class TestPacketWriter : public QuicDefaultPacketWriter { public: - TestPacketWriter() { - } + TestPacketWriter(QuicVersion version) : version_(version) {} // QuicPacketWriter virtual WriteResult WritePacket( const char* buffer, size_t buf_len, const IPAddressNumber& self_address, - const IPEndPoint& peer_address, - QuicBlockedWriterInterface* blocked_writer) OVERRIDE { - QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), true); - FramerVisitorCapturingFrames visitor; - framer.set_visitor(&visitor); + const IPEndPoint& peer_address) OVERRIDE { + SimpleQuicFramer framer(SupportedVersions(version_)); QuicEncryptedPacket packet(buffer, buf_len); EXPECT_TRUE(framer.ProcessPacket(packet)); - header_ = *visitor.header(); + header_ = framer.header(); return WriteResult(WRITE_STATUS_OK, packet.length()); } @@ -58,16 +57,22 @@ class TestPacketWriter : public QuicDefaultPacketWriter { const QuicPacketHeader& header() { return header_; } private: + QuicVersion version_; QuicPacketHeader header_; }; -class QuicClientSessionTest : public ::testing::Test { +class QuicClientSessionTest : public ::testing::TestWithParam<QuicVersion> { protected: QuicClientSessionTest() - : writer_(new TestPacketWriter()), - connection_(new PacketSavingConnection(false)), + : writer_(new TestPacketWriter(GetParam())), + connection_( + new PacketSavingConnection(false, SupportedVersions(GetParam()))), session_(connection_, GetSocket().Pass(), writer_.Pass(), NULL, NULL, - kServerHostname, DefaultQuicConfig(), &crypto_config_, + make_scoped_ptr((QuicServerInfo*)NULL), + QuicServerId(kServerHostname, kServerPort, false, + PRIVACY_MODE_DISABLED), + DefaultQuicConfig(), &crypto_config_, + base::MessageLoop::current()->message_loop_proxy().get(), &net_log_) { session_.config()->SetDefaults(); crypto_config_.SetDefaults(); @@ -105,11 +110,14 @@ class QuicClientSessionTest : public ::testing::Test { QuicCryptoClientConfig crypto_config_; }; -TEST_F(QuicClientSessionTest, CryptoConnect) { +INSTANTIATE_TEST_CASE_P(Tests, QuicClientSessionTest, + ::testing::ValuesIn(QuicSupportedVersions())); + +TEST_P(QuicClientSessionTest, CryptoConnect) { CompleteCryptoHandshake(); } -TEST_F(QuicClientSessionTest, MaxNumStreams) { +TEST_P(QuicClientSessionTest, MaxNumStreams) { CompleteCryptoHandshake(); std::vector<QuicReliableClientStream*> streams; @@ -125,7 +133,7 @@ TEST_F(QuicClientSessionTest, MaxNumStreams) { EXPECT_TRUE(session_.CreateOutgoingDataStream()); } -TEST_F(QuicClientSessionTest, MaxNumStreamsViaRequest) { +TEST_P(QuicClientSessionTest, MaxNumStreamsViaRequest) { CompleteCryptoHandshake(); std::vector<QuicReliableClientStream*> streams; @@ -149,7 +157,7 @@ TEST_F(QuicClientSessionTest, MaxNumStreamsViaRequest) { EXPECT_TRUE(stream != NULL); } -TEST_F(QuicClientSessionTest, GoAwayReceived) { +TEST_P(QuicClientSessionTest, GoAwayReceived) { CompleteCryptoHandshake(); // After receiving a GoAway, I should no longer be able to create outgoing diff --git a/chromium/net/quic/quic_clock.cc b/chromium/net/quic/quic_clock.cc index 865562369fa..9d5ac6d17ab 100644 --- a/chromium/net/quic/quic_clock.cc +++ b/chromium/net/quic/quic_clock.cc @@ -14,7 +14,8 @@ QuicClock::QuicClock() { QuicClock::~QuicClock() {} QuicTime QuicClock::ApproximateNow() const { - // Chrome does not have a distinct notion of ApproximateNow(). + // At the moment, Chrome does not have a distinct notion of ApproximateNow(). + // We should consider implementing this using MessageLoop::recent_time_. return Now(); } diff --git a/chromium/net/quic/quic_clock.h b/chromium/net/quic/quic_clock.h index e6135a930d0..4e96d4c781e 100644 --- a/chromium/net/quic/quic_clock.h +++ b/chromium/net/quic/quic_clock.h @@ -30,6 +30,9 @@ class NET_EXPORT_PRIVATE QuicClock { // WallNow returns the current wall-time - a time that is consistent across // different clocks. virtual QuicWallTime WallNow() const; + + private: + DISALLOW_COPY_AND_ASSIGN(QuicClock); }; } // namespace net diff --git a/chromium/net/quic/quic_config.cc b/chromium/net/quic/quic_config.cc index 152da46a80f..f69aa379494 100644 --- a/chromium/net/quic/quic_config.cc +++ b/chromium/net/quic/quic_config.cc @@ -7,23 +7,68 @@ #include <algorithm> #include "base/logging.h" +#include "net/quic/crypto/crypto_handshake_message.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_sent_packet_manager.h" +#include "net/quic/quic_utils.h" +using std::min; using std::string; namespace net { -QuicNegotiableValue::QuicNegotiableValue(QuicTag tag, Presence presence) +// Reads the value corresponding to |name_| from |msg| into |out|. If the +// |name_| is absent in |msg| and |presence| is set to OPTIONAL |out| is set +// to |default_value|. +QuicErrorCode ReadUint32(const CryptoHandshakeMessage& msg, + QuicTag tag, + QuicConfigPresence presence, + uint32 default_value, + uint32* out, + string* error_details) { + DCHECK(error_details != NULL); + QuicErrorCode error = msg.GetUint32(tag, out); + switch (error) { + case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: + if (presence == PRESENCE_REQUIRED) { + *error_details = "Missing " + QuicUtils::TagToString(tag); + break; + } + error = QUIC_NO_ERROR; + *out = default_value; + break; + case QUIC_NO_ERROR: + break; + default: + *error_details = "Bad " + QuicUtils::TagToString(tag); + break; + } + return error; +} + + +QuicConfigValue::QuicConfigValue(QuicTag tag, + QuicConfigPresence presence) : tag_(tag), - presence_(presence), + presence_(presence) { +} +QuicConfigValue::~QuicConfigValue() {} + +QuicNegotiableValue::QuicNegotiableValue(QuicTag tag, + QuicConfigPresence presence) + : QuicConfigValue(tag, presence), negotiated_(false) { } +QuicNegotiableValue::~QuicNegotiableValue() {} -QuicNegotiableUint32::QuicNegotiableUint32(QuicTag tag, Presence presence) +QuicNegotiableUint32::QuicNegotiableUint32(QuicTag tag, + QuicConfigPresence presence) : QuicNegotiableValue(tag, presence), max_value_(0), default_value_(0) { } +QuicNegotiableUint32::~QuicNegotiableUint32() {} void QuicNegotiableUint32::set(uint32 max, uint32 default_value) { DCHECK_LE(default_value, max); @@ -47,70 +92,34 @@ void QuicNegotiableUint32::ToHandshakeMessage( } } -QuicErrorCode QuicNegotiableUint32::ReadUint32( - const CryptoHandshakeMessage& msg, - uint32* out, - string* error_details) const { - DCHECK(error_details != NULL); - QuicErrorCode error = msg.GetUint32(tag_, out); - switch (error) { - case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: - if (presence_ == QuicNegotiableValue::PRESENCE_REQUIRED) { - *error_details = "Missing " + QuicUtils::TagToString(tag_); - break; - } - error = QUIC_NO_ERROR; - *out = default_value_; - - case QUIC_NO_ERROR: - break; - default: - *error_details = "Bad " + QuicUtils::TagToString(tag_); - break; - } - return error; -} - -QuicErrorCode QuicNegotiableUint32::ProcessClientHello( - const CryptoHandshakeMessage& client_hello, - string* error_details) { - DCHECK(!negotiated_); - DCHECK(error_details != NULL); - uint32 value; - QuicErrorCode error = ReadUint32(client_hello, &value, error_details); - if (error != QUIC_NO_ERROR) { - return error; - } - - negotiated_ = true; - negotiated_value_ = std::min(value, max_value_); - - return QUIC_NO_ERROR; -} - -QuicErrorCode QuicNegotiableUint32::ProcessServerHello( - const CryptoHandshakeMessage& server_hello, +QuicErrorCode QuicNegotiableUint32::ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, string* error_details) { DCHECK(!negotiated_); DCHECK(error_details != NULL); uint32 value; - QuicErrorCode error = ReadUint32(server_hello, &value, error_details); + QuicErrorCode error = ReadUint32(peer_hello, + tag_, + presence_, + default_value_, + &value, + error_details); if (error != QUIC_NO_ERROR) { return error; } - - if (value > max_value_) { - *error_details = "Invalid value received for " + - QuicUtils::TagToString(tag_); + if (hello_type == SERVER && value > max_value_) { + *error_details = + "Invalid value received for " + QuicUtils::TagToString(tag_); return QUIC_INVALID_NEGOTIATED_VALUE; } negotiated_ = true; - negotiated_value_ = value; + negotiated_value_ = min(value, max_value_); return QUIC_NO_ERROR; } -QuicNegotiableTag::QuicNegotiableTag(QuicTag tag, Presence presence) +QuicNegotiableTag::QuicNegotiableTag(QuicTag tag, QuicConfigPresence presence) : QuicNegotiableValue(tag, presence), negotiated_tag_(0), default_value_(0) { @@ -120,8 +129,7 @@ QuicNegotiableTag::~QuicNegotiableTag() {} void QuicNegotiableTag::set(const QuicTagVector& possible, QuicTag default_value) { - DCHECK(std::find(possible.begin(), possible.end(), default_value) != - possible.end()); + DCHECK(ContainsQuicTag(possible, default_value)); possible_values_ = possible; default_value_ = default_value; } @@ -169,85 +177,309 @@ QuicErrorCode QuicNegotiableTag::ReadVector( return error; } -QuicErrorCode QuicNegotiableTag::ProcessClientHello( - const CryptoHandshakeMessage& client_hello, +QuicErrorCode QuicNegotiableTag::ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, string* error_details) { DCHECK(!negotiated_); DCHECK(error_details != NULL); const QuicTag* received_tags; size_t received_tags_length; - QuicErrorCode error = ReadVector(client_hello, &received_tags, + QuicErrorCode error = ReadVector(peer_hello, &received_tags, &received_tags_length, error_details); if (error != QUIC_NO_ERROR) { return error; } - QuicTag negotiated_tag; - if (!QuicUtils::FindMutualTag(possible_values_, - received_tags, - received_tags_length, - QuicUtils::LOCAL_PRIORITY, - &negotiated_tag, - NULL)) { - *error_details = "Unsuported " + QuicUtils::TagToString(tag_); - return QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP; + if (hello_type == SERVER) { + if (received_tags_length != 1 || + !ContainsQuicTag(possible_values_, *received_tags)) { + *error_details = "Invalid " + QuicUtils::TagToString(tag_); + return QUIC_INVALID_NEGOTIATED_VALUE; + } + negotiated_tag_ = *received_tags; + } else { + QuicTag negotiated_tag; + if (!QuicUtils::FindMutualTag(possible_values_, + received_tags, + received_tags_length, + QuicUtils::LOCAL_PRIORITY, + &negotiated_tag, + NULL)) { + *error_details = "Unsupported " + QuicUtils::TagToString(tag_); + return QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP; + } + negotiated_tag_ = negotiated_tag; } negotiated_ = true; - negotiated_tag_ = negotiated_tag; return QUIC_NO_ERROR; } -QuicErrorCode QuicNegotiableTag::ProcessServerHello( - const CryptoHandshakeMessage& server_hello, +QuicFixedUint32::QuicFixedUint32(QuicTag tag, QuicConfigPresence presence) + : QuicConfigValue(tag, presence), + has_send_value_(false), + has_receive_value_(false) { +} +QuicFixedUint32::~QuicFixedUint32() {} + +bool QuicFixedUint32::HasSendValue() const { + return has_send_value_; +} + +uint32 QuicFixedUint32::GetSendValue() const { + LOG_IF(DFATAL, !has_send_value_) + << "No send value to get for tag:" << QuicUtils::TagToString(tag_); + return send_value_; +} + +void QuicFixedUint32::SetSendValue(uint32 value) { + has_send_value_ = true; + send_value_ = value; +} + +bool QuicFixedUint32::HasReceivedValue() const { + return has_receive_value_; +} + +uint32 QuicFixedUint32::GetReceivedValue() const { + LOG_IF(DFATAL, !has_receive_value_) + << "No receive value to get for tag:" << QuicUtils::TagToString(tag_); + return receive_value_; +} + +void QuicFixedUint32::SetReceivedValue(uint32 value) { + has_receive_value_ = true; + receive_value_ = value; +} + +void QuicFixedUint32::ToHandshakeMessage(CryptoHandshakeMessage* out) const { + if (has_send_value_) { + out->SetValue(tag_, send_value_); + } +} + +QuicErrorCode QuicFixedUint32::ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, string* error_details) { - DCHECK(!negotiated_); DCHECK(error_details != NULL); - const QuicTag* received_tags; - size_t received_tags_length; - QuicErrorCode error = ReadVector(server_hello, &received_tags, - &received_tags_length, error_details); - if (error != QUIC_NO_ERROR) { - return error; + QuicErrorCode error = peer_hello.GetUint32(tag_, &receive_value_); + switch (error) { + case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: + if (presence_ == PRESENCE_OPTIONAL) { + return QUIC_NO_ERROR; + } + *error_details = "Missing " + QuicUtils::TagToString(tag_); + break; + case QUIC_NO_ERROR: + has_receive_value_ = true; + break; + default: + *error_details = "Bad " + QuicUtils::TagToString(tag_); + break; } + return error; +} - if (received_tags_length != 1 || - std::find(possible_values_.begin(), possible_values_.end(), - *received_tags) == possible_values_.end()) { - *error_details = "Invalid " + QuicUtils::TagToString(tag_); - return QUIC_INVALID_NEGOTIATED_VALUE; +QuicFixedTag::QuicFixedTag(QuicTag name, + QuicConfigPresence presence) + : QuicConfigValue(name, presence), + has_send_value_(false), + has_receive_value_(false) { +} + +QuicFixedTag::~QuicFixedTag() {} + +bool QuicFixedTag::HasSendValue() const { + return has_send_value_; +} + +uint32 QuicFixedTag::GetSendValue() const { + LOG_IF(DFATAL, !has_send_value_) + << "No send value to get for tag:" << QuicUtils::TagToString(tag_); + return send_value_; +} + +void QuicFixedTag::SetSendValue(uint32 value) { + has_send_value_ = true; + send_value_ = value; +} + +bool QuicFixedTag::HasReceivedValue() const { + return has_receive_value_; +} + +uint32 QuicFixedTag::GetReceivedValue() const { + LOG_IF(DFATAL, !has_receive_value_) + << "No receive value to get for tag:" << QuicUtils::TagToString(tag_); + return receive_value_; +} + +void QuicFixedTag::SetReceivedValue(uint32 value) { + has_receive_value_ = true; + receive_value_ = value; +} + +void QuicFixedTag::ToHandshakeMessage(CryptoHandshakeMessage* out) const { + if (has_send_value_) { + out->SetValue(tag_, send_value_); } +} - negotiated_ = true; - negotiated_tag_ = *received_tags; - return QUIC_NO_ERROR; +QuicErrorCode QuicFixedTag::ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + string* error_details) { + DCHECK(error_details != NULL); + QuicErrorCode error = peer_hello.GetUint32(tag_, &receive_value_); + switch (error) { + case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: + if (presence_ == PRESENCE_OPTIONAL) { + return QUIC_NO_ERROR; + } + *error_details = "Missing " + QuicUtils::TagToString(tag_); + break; + case QUIC_NO_ERROR: + has_receive_value_ = true; + break; + default: + *error_details = "Bad " + QuicUtils::TagToString(tag_); + break; + } + return error; } -QuicConfig::QuicConfig() : - congestion_control_(kCGST, QuicNegotiableValue::PRESENCE_REQUIRED), - idle_connection_state_lifetime_seconds_( - kICSL, QuicNegotiableValue::PRESENCE_REQUIRED), - keepalive_timeout_seconds_(kKATO, QuicNegotiableValue::PRESENCE_OPTIONAL), - max_streams_per_connection_(kMSPC, QuicNegotiableValue::PRESENCE_REQUIRED), - max_time_before_crypto_handshake_(QuicTime::Delta::Zero()), - server_initial_congestion_window_( - kSWND, QuicNegotiableValue::PRESENCE_OPTIONAL), - initial_round_trip_time_us_(kIRTT, QuicNegotiableValue::PRESENCE_OPTIONAL) { - // All optional non-zero parameters should be initialized here. - server_initial_congestion_window_.set(kMaxInitialWindow, - kDefaultInitialWindow); +QuicFixedTagVector::QuicFixedTagVector(QuicTag name, + QuicConfigPresence presence) + : QuicConfigValue(name, presence), + has_send_values_(false), + has_receive_values_(false) { +} + +QuicFixedTagVector::~QuicFixedTagVector() {} + +bool QuicFixedTagVector::HasSendValues() const { + return has_send_values_; +} + +QuicTagVector QuicFixedTagVector::GetSendValues() const { + LOG_IF(DFATAL, !has_send_values_) + << "No send values to get for tag:" << QuicUtils::TagToString(tag_); + return send_values_; +} + +void QuicFixedTagVector::SetSendValues(const QuicTagVector& values) { + has_send_values_ = true; + send_values_ = values; +} + +bool QuicFixedTagVector::HasReceivedValues() const { + return has_receive_values_; +} + +QuicTagVector QuicFixedTagVector::GetReceivedValues() const { + LOG_IF(DFATAL, !has_receive_values_) + << "No receive value to get for tag:" << QuicUtils::TagToString(tag_); + return receive_values_; +} + +void QuicFixedTagVector::SetReceivedValues(const QuicTagVector& values) { + has_receive_values_ = true; + receive_values_ = values; +} + +void QuicFixedTagVector::ToHandshakeMessage(CryptoHandshakeMessage* out) const { + if (has_send_values_) { + out->SetVector(tag_, send_values_); + } +} + +QuicErrorCode QuicFixedTagVector::ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + string* error_details) { + DCHECK(error_details != NULL); + const QuicTag* received_tags; + size_t received_tags_length; + QuicErrorCode error = + peer_hello.GetTaglist(tag_, &received_tags, &received_tags_length); + switch (error) { + case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: + if (presence_ == PRESENCE_OPTIONAL) { + return QUIC_NO_ERROR; + } + *error_details = "Missing " + QuicUtils::TagToString(tag_); + break; + case QUIC_NO_ERROR: + has_receive_values_ = true; + for (size_t i = 0; i < received_tags_length; ++i) { + receive_values_.push_back(received_tags[i]); + } + break; + default: + *error_details = "Bad " + QuicUtils::TagToString(tag_); + break; + } + return error; +} + +QuicConfig::QuicConfig() + : congestion_feedback_(kCGST, PRESENCE_REQUIRED), + congestion_options_(kCOPT, PRESENCE_OPTIONAL), + loss_detection_(kLOSS, PRESENCE_OPTIONAL), + idle_connection_state_lifetime_seconds_(kICSL, PRESENCE_REQUIRED), + keepalive_timeout_seconds_(kKATO, PRESENCE_OPTIONAL), + max_streams_per_connection_(kMSPC, PRESENCE_REQUIRED), + max_time_before_crypto_handshake_(QuicTime::Delta::Zero()), + initial_congestion_window_(kSWND, PRESENCE_OPTIONAL), + initial_round_trip_time_us_(kIRTT, PRESENCE_OPTIONAL), + // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring + // QUIC_VERSION_17. + initial_flow_control_window_bytes_(kIFCW, PRESENCE_OPTIONAL), + // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring + // QUIC_VERSION_19. + 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) { } QuicConfig::~QuicConfig() {} -void QuicConfig::set_congestion_control( - const QuicTagVector& congestion_control, - QuicTag default_congestion_control) { - congestion_control_.set(congestion_control, default_congestion_control); +void QuicConfig::set_congestion_feedback( + const QuicTagVector& congestion_feedback, + QuicTag default_congestion_feedback) { + congestion_feedback_.set(congestion_feedback, default_congestion_feedback); +} + +QuicTag QuicConfig::congestion_feedback() const { + return congestion_feedback_.GetTag(); +} + +void QuicConfig::SetCongestionOptionsToSend( + const QuicTagVector& congestion_options) { + congestion_options_.SetSendValues(congestion_options); } -QuicTag QuicConfig::congestion_control() const { - return congestion_control_.GetTag(); +bool QuicConfig::HasReceivedCongestionOptions() const { + return congestion_options_.HasReceivedValues(); +} + +QuicTagVector QuicConfig::ReceivedCongestionOptions() const { + return congestion_options_.GetReceivedValues(); +} + +void QuicConfig::SetLossDetectionToSend(QuicTag loss_detection) { + loss_detection_.SetSendValue(loss_detection); +} + +bool QuicConfig::HasReceivedLossDetection() const { + return loss_detection_.HasReceivedValue(); +} + +QuicTag QuicConfig::ReceivedLossDetection() const { + return loss_detection_.GetReceivedValue(); } void QuicConfig::set_idle_connection_state_lifetime( @@ -286,44 +518,113 @@ QuicTime::Delta QuicConfig::max_time_before_crypto_handshake() const { return max_time_before_crypto_handshake_; } -void QuicConfig::set_server_initial_congestion_window(size_t max_initial_window, - size_t default_initial_window) { - server_initial_congestion_window_.set(max_initial_window, - default_initial_window); +void QuicConfig::SetInitialCongestionWindowToSend(size_t initial_window) { + initial_congestion_window_.SetSendValue(initial_window); +} + +bool QuicConfig::HasReceivedInitialCongestionWindow() const { + return initial_congestion_window_.HasReceivedValue(); +} + +uint32 QuicConfig::ReceivedInitialCongestionWindow() const { + return initial_congestion_window_.GetReceivedValue(); +} + +void QuicConfig::SetInitialRoundTripTimeUsToSend(size_t rtt) { + initial_round_trip_time_us_.SetSendValue(rtt); +} + +bool QuicConfig::HasReceivedInitialRoundTripTimeUs() const { + return initial_round_trip_time_us_.HasReceivedValue(); +} + +uint32 QuicConfig::ReceivedInitialRoundTripTimeUs() const { + return initial_round_trip_time_us_.GetReceivedValue(); +} + +void QuicConfig::SetInitialFlowControlWindowToSend(uint32 window_bytes) { + if (window_bytes < kDefaultFlowControlSendWindow) { + LOG(DFATAL) << "Initial flow control receive window (" << window_bytes + << ") cannot be set lower than default (" + << kDefaultFlowControlSendWindow << ")."; + window_bytes = kDefaultFlowControlSendWindow; + } + initial_flow_control_window_bytes_.SetSendValue(window_bytes); +} + +uint32 QuicConfig::GetInitialFlowControlWindowToSend() const { + return initial_flow_control_window_bytes_.GetSendValue(); +} + +bool QuicConfig::HasReceivedInitialFlowControlWindowBytes() const { + return initial_flow_control_window_bytes_.HasReceivedValue(); +} + +uint32 QuicConfig::ReceivedInitialFlowControlWindowBytes() const { + return initial_flow_control_window_bytes_.GetReceivedValue(); +} + +void QuicConfig::SetInitialStreamFlowControlWindowToSend(uint32 window_bytes) { + if (window_bytes < kDefaultFlowControlSendWindow) { + LOG(DFATAL) << "Initial stream flow control receive window (" + << window_bytes << ") cannot be set lower than default (" + << kDefaultFlowControlSendWindow << ")."; + window_bytes = kDefaultFlowControlSendWindow; + } + initial_stream_flow_control_window_bytes_.SetSendValue(window_bytes); +} + +uint32 QuicConfig::GetInitialStreamFlowControlWindowToSend() const { + return initial_stream_flow_control_window_bytes_.GetSendValue(); +} + +bool QuicConfig::HasReceivedInitialStreamFlowControlWindowBytes() const { + return initial_stream_flow_control_window_bytes_.HasReceivedValue(); +} + +uint32 QuicConfig::ReceivedInitialStreamFlowControlWindowBytes() const { + return initial_stream_flow_control_window_bytes_.GetReceivedValue(); +} + +void QuicConfig::SetInitialSessionFlowControlWindowToSend(uint32 window_bytes) { + if (window_bytes < kDefaultFlowControlSendWindow) { + LOG(DFATAL) << "Initial session flow control receive window (" + << window_bytes << ") cannot be set lower than default (" + << kDefaultFlowControlSendWindow << ")."; + window_bytes = kDefaultFlowControlSendWindow; + } + initial_session_flow_control_window_bytes_.SetSendValue(window_bytes); } -uint32 QuicConfig::server_initial_congestion_window() const { - return server_initial_congestion_window_.GetUint32(); +uint32 QuicConfig::GetInitialSessionFlowControlWindowToSend() const { + return initial_session_flow_control_window_bytes_.GetSendValue(); } -void QuicConfig::set_initial_round_trip_time_us(size_t max_rtt, - size_t default_rtt) { - initial_round_trip_time_us_.set(max_rtt, default_rtt); +bool QuicConfig::HasReceivedInitialSessionFlowControlWindowBytes() const { + return initial_session_flow_control_window_bytes_.HasReceivedValue(); } -uint32 QuicConfig::initial_round_trip_time_us() const { - return initial_round_trip_time_us_.GetUint32(); +uint32 QuicConfig::ReceivedInitialSessionFlowControlWindowBytes() const { + return initial_session_flow_control_window_bytes_.GetReceivedValue(); } bool QuicConfig::negotiated() { // TODO(ianswett): Add the negotiated parameters once and iterate over all // of them in negotiated, ToHandshakeMessage, ProcessClientHello, and // ProcessServerHello. - return congestion_control_.negotiated() && + return congestion_feedback_.negotiated() && idle_connection_state_lifetime_seconds_.negotiated() && keepalive_timeout_seconds_.negotiated() && - max_streams_per_connection_.negotiated() && - server_initial_congestion_window_.negotiated() && - initial_round_trip_time_us_.negotiated(); + max_streams_per_connection_.negotiated(); } void QuicConfig::SetDefaults() { - QuicTagVector congestion_control; + QuicTagVector congestion_feedback; if (FLAGS_enable_quic_pacing) { - congestion_control.push_back(kPACE); + congestion_feedback.push_back(kPACE); } - congestion_control.push_back(kQBIC); - congestion_control_.set(congestion_control, kQBIC); + congestion_feedback.push_back(kQBIC); + congestion_feedback_.set(congestion_feedback, kQBIC); idle_connection_state_lifetime_seconds_.set(kDefaultTimeoutSecs, kDefaultInitialTimeoutSecs); // kKATO is optional. Return 0 if not negotiated. @@ -332,80 +633,85 @@ void QuicConfig::SetDefaults() { kDefaultMaxStreamsPerConnection); max_time_before_crypto_handshake_ = QuicTime::Delta::FromSeconds( kDefaultMaxTimeForCryptoHandshakeSecs); - server_initial_congestion_window_.set(kDefaultInitialWindow, - kDefaultInitialWindow); + + SetInitialFlowControlWindowToSend(kDefaultFlowControlSendWindow); + SetInitialStreamFlowControlWindowToSend(kDefaultFlowControlSendWindow); + SetInitialSessionFlowControlWindowToSend(kDefaultFlowControlSendWindow); +} + +void QuicConfig::EnablePacing(bool enable_pacing) { + QuicTagVector congestion_feedback; + if (enable_pacing) { + congestion_feedback.push_back(kPACE); + } + congestion_feedback.push_back(kQBIC); + congestion_feedback_.set(congestion_feedback, kQBIC); } void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { - congestion_control_.ToHandshakeMessage(out); + congestion_feedback_.ToHandshakeMessage(out); idle_connection_state_lifetime_seconds_.ToHandshakeMessage(out); keepalive_timeout_seconds_.ToHandshakeMessage(out); max_streams_per_connection_.ToHandshakeMessage(out); - server_initial_congestion_window_.ToHandshakeMessage(out); - // TODO(ianswett): Don't transmit parameters which are optional and not set. + initial_congestion_window_.ToHandshakeMessage(out); initial_round_trip_time_us_.ToHandshakeMessage(out); + loss_detection_.ToHandshakeMessage(out); + initial_flow_control_window_bytes_.ToHandshakeMessage(out); + initial_stream_flow_control_window_bytes_.ToHandshakeMessage(out); + initial_session_flow_control_window_bytes_.ToHandshakeMessage(out); + congestion_options_.ToHandshakeMessage(out); } -QuicErrorCode QuicConfig::ProcessClientHello( - const CryptoHandshakeMessage& client_hello, +QuicErrorCode QuicConfig::ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, string* error_details) { DCHECK(error_details != NULL); QuicErrorCode error = QUIC_NO_ERROR; if (error == QUIC_NO_ERROR) { - error = congestion_control_.ProcessClientHello(client_hello, error_details); + error = congestion_feedback_.ProcessPeerHello( + peer_hello, hello_type, error_details); } if (error == QUIC_NO_ERROR) { - error = idle_connection_state_lifetime_seconds_.ProcessClientHello( - client_hello, error_details); + error = idle_connection_state_lifetime_seconds_.ProcessPeerHello( + peer_hello, hello_type, error_details); } if (error == QUIC_NO_ERROR) { - error = keepalive_timeout_seconds_.ProcessClientHello( - client_hello, error_details); + error = keepalive_timeout_seconds_.ProcessPeerHello( + peer_hello, hello_type, error_details); } if (error == QUIC_NO_ERROR) { - error = max_streams_per_connection_.ProcessClientHello( - client_hello, error_details); + error = max_streams_per_connection_.ProcessPeerHello( + peer_hello, hello_type, error_details); } if (error == QUIC_NO_ERROR) { - error = server_initial_congestion_window_.ProcessClientHello( - client_hello, error_details); + error = initial_congestion_window_.ProcessPeerHello( + peer_hello, hello_type, error_details); } if (error == QUIC_NO_ERROR) { - error = initial_round_trip_time_us_.ProcessClientHello( - client_hello, error_details); - } - return error; -} - -QuicErrorCode QuicConfig::ProcessServerHello( - const CryptoHandshakeMessage& server_hello, - string* error_details) { - DCHECK(error_details != NULL); - - QuicErrorCode error = QUIC_NO_ERROR; - if (error == QUIC_NO_ERROR) { - error = congestion_control_.ProcessServerHello(server_hello, error_details); + error = initial_round_trip_time_us_.ProcessPeerHello( + peer_hello, hello_type, error_details); } if (error == QUIC_NO_ERROR) { - error = idle_connection_state_lifetime_seconds_.ProcessServerHello( - server_hello, error_details); + error = initial_flow_control_window_bytes_.ProcessPeerHello( + peer_hello, hello_type, error_details); } if (error == QUIC_NO_ERROR) { - error = keepalive_timeout_seconds_.ProcessServerHello( - server_hello, error_details); + error = initial_stream_flow_control_window_bytes_.ProcessPeerHello( + peer_hello, hello_type, error_details); } if (error == QUIC_NO_ERROR) { - error = max_streams_per_connection_.ProcessServerHello( - server_hello, error_details); + error = initial_session_flow_control_window_bytes_.ProcessPeerHello( + peer_hello, hello_type, error_details); } if (error == QUIC_NO_ERROR) { - error = server_initial_congestion_window_.ProcessServerHello( - server_hello, error_details); + error = loss_detection_.ProcessPeerHello( + peer_hello, hello_type, error_details); } if (error == QUIC_NO_ERROR) { - error = initial_round_trip_time_us_.ProcessServerHello( - server_hello, error_details); + error = congestion_options_.ProcessPeerHello( + peer_hello, hello_type, error_details); } return error; } diff --git a/chromium/net/quic/quic_config.h b/chromium/net/quic/quic_config.h index 6c0561b1ca4..8f46917450e 100644 --- a/chromium/net/quic/quic_config.h +++ b/chromium/net/quic/quic_config.h @@ -8,40 +8,74 @@ #include <string> #include "base/basictypes.h" -#include "net/quic/crypto/crypto_handshake.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_time.h" -#include "net/quic/quic_utils.h" namespace net { -class NET_EXPORT_PRIVATE QuicNegotiableValue { +namespace test { +class QuicConfigPeer; +} // namespace test + +class CryptoHandshakeMessage; + +// Describes whether or not a given QuicTag is required or optional in the +// handshake message. +enum QuicConfigPresence { + // This negotiable value can be absent from the handshake message. Default + // value is selected as the negotiated value in such a case. + PRESENCE_OPTIONAL, + // This negotiable value is required in the handshake message otherwise the + // Process*Hello function returns an error. + PRESENCE_REQUIRED, +}; + +// Whether the CryptoHandshakeMessage is from the client or server. +enum HelloType { + CLIENT, + SERVER, +}; + +// An abstract base class that stores a value that can be sent in CHLO/SHLO +// message. These values can be OPTIONAL or REQUIRED, depending on |presence_|. +class NET_EXPORT_PRIVATE QuicConfigValue { public: - enum Presence { - // This negotiable value can be absent from the handshake message. Default - // value is selected as the negotiated value in such a case. - PRESENCE_OPTIONAL, - // This negotiable value is required in the handshake message otherwise the - // Process*Hello function returns an error. - PRESENCE_REQUIRED, - }; + QuicConfigValue(QuicTag tag, QuicConfigPresence presence); + virtual ~QuicConfigValue(); + + // Serialises tag name and value(s) to |out|. + virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const = 0; + + // Selects a mutually acceptable value from those offered in |peer_hello| + // and those defined in the subclass. + virtual QuicErrorCode ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + std::string* error_details) = 0; - QuicNegotiableValue(QuicTag tag, Presence presence); + protected: + const QuicTag tag_; + const QuicConfigPresence presence_; +}; + +class NET_EXPORT_PRIVATE QuicNegotiableValue : public QuicConfigValue { + public: + QuicNegotiableValue(QuicTag tag, QuicConfigPresence presence); + virtual ~QuicNegotiableValue(); bool negotiated() const { return negotiated_; } protected: - const QuicTag tag_; - const Presence presence_; bool negotiated_; }; class NET_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue { public: // Default and max values default to 0. - QuicNegotiableUint32(QuicTag name, Presence presence); + QuicNegotiableUint32(QuicTag name, QuicConfigPresence presence); + virtual ~QuicNegotiableUint32(); // Sets the maximum possible value that can be achieved after negotiation and // also the default values to be assumed if PRESENCE_OPTIONAL and the *HLO msg @@ -55,30 +89,18 @@ class NET_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue { // Serialises |name_| and value to |out|. If |negotiated_| is true then // |negotiated_value_| is serialised, otherwise |max_value_| is serialised. - void ToHandshakeMessage(CryptoHandshakeMessage* out) const; + virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE; // Sets |negotiated_value_| to the minimum of |max_value_| and the - // corresponding value from |client_hello|. If the corresponding value is + // corresponding value from |peer_hello|. If the corresponding value is // missing and PRESENCE_OPTIONAL then |negotiated_value_| is set to // |default_value_|. - QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello, - std::string* error_details); - - // Sets the |negotiated_value_| to the corresponding value from - // |server_hello|. Returns error if the value received in |server_hello| is - // greater than |max_value_|. If the corresponding value is missing and - // PRESENCE_OPTIONAL then |negotiated_value_| is set to |0|, - QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, - std::string* error_details); + virtual QuicErrorCode ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + std::string* error_details) OVERRIDE; private: - // Reads the value corresponding to |name_| from |msg| into |out|. If the - // |name_| is absent in |msg| and |presence_| is set to OPTIONAL |out| is set - // to |max_value_|. - QuicErrorCode ReadUint32(const CryptoHandshakeMessage& msg, - uint32* out, - std::string* error_details) const; - uint32 max_value_; uint32 default_value_; uint32 negotiated_value_; @@ -86,8 +108,8 @@ class NET_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue { class NET_EXPORT_PRIVATE QuicNegotiableTag : public QuicNegotiableValue { public: - QuicNegotiableTag(QuicTag name, Presence presence); - ~QuicNegotiableTag(); + QuicNegotiableTag(QuicTag name, QuicConfigPresence presence); + virtual ~QuicNegotiableTag(); // Sets the possible values that |negotiated_tag_| can take after negotiation // and the default value that |negotiated_tag_| takes if OPTIONAL and *HLO @@ -101,19 +123,15 @@ class NET_EXPORT_PRIVATE QuicNegotiableTag : public QuicNegotiableValue { // Serialises |name_| and vector (either possible or negotiated) to |out|. If // |negotiated_| is true then |negotiated_tag_| is serialised, otherwise // |possible_values_| is serialised. - void ToHandshakeMessage(CryptoHandshakeMessage* out) const; + virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE; // Selects the tag common to both tags in |client_hello| for |name_| and // |possible_values_| with preference to tag in |possible_values_|. The // selected tag is set as |negotiated_tag_|. - QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello, - std::string* error_details); - - // Sets the value for |name_| tag in |server_hello| as |negotiated_value_|. - // Returns error if the value received in |server_hello| isn't present in - // |possible_values_|. - QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, - std::string* error_details); + virtual QuicErrorCode ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + std::string* error_details) OVERRIDE; private: // Reads the vector corresponding to |name_| from |msg| into |out|. If the @@ -129,6 +147,110 @@ class NET_EXPORT_PRIVATE QuicNegotiableTag : public QuicNegotiableValue { QuicTag default_value_; }; +// Stores uint32 from CHLO or SHLO messages that are not negotiated. +class NET_EXPORT_PRIVATE QuicFixedUint32 : public QuicConfigValue { + public: + QuicFixedUint32(QuicTag name, QuicConfigPresence presence); + virtual ~QuicFixedUint32(); + + bool HasSendValue() const; + + uint32 GetSendValue() const; + + void SetSendValue(uint32 value); + + bool HasReceivedValue() const; + + uint32 GetReceivedValue() const; + + void SetReceivedValue(uint32 value); + + // If has_send_value is true, serialises |tag_| and |send_value_| to |out|. + virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE; + + // Sets |value_| to the corresponding value from |peer_hello_| if it exists. + virtual QuicErrorCode ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + std::string* error_details) OVERRIDE; + + private: + uint32 send_value_; + bool has_send_value_; + uint32 receive_value_; + bool has_receive_value_; +}; + +// Stores tag from CHLO or SHLO messages that are not negotiated. +class NET_EXPORT_PRIVATE QuicFixedTag : public QuicConfigValue { + public: + QuicFixedTag(QuicTag name, QuicConfigPresence presence); + virtual ~QuicFixedTag(); + + bool HasSendValue() const; + + QuicTag GetSendValue() const; + + void SetSendValue(QuicTag value); + + bool HasReceivedValue() const; + + QuicTag GetReceivedValue() const; + + void SetReceivedValue(QuicTag value); + + // If has_send_value is true, serialises |tag_| and |send_value_| to |out|. + virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE; + + // Sets |value_| to the corresponding value from |client_hello_| if it exists. + virtual QuicErrorCode ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + std::string* error_details) OVERRIDE; + + private: + QuicTag send_value_; + bool has_send_value_; + QuicTag receive_value_; + bool has_receive_value_; +}; + +// Stores tag from CHLO or SHLO messages that are not negotiated. +class NET_EXPORT_PRIVATE QuicFixedTagVector : public QuicConfigValue { + public: + QuicFixedTagVector(QuicTag name, QuicConfigPresence presence); + virtual ~QuicFixedTagVector(); + + bool HasSendValues() const; + + QuicTagVector GetSendValues() const; + + void SetSendValues(const QuicTagVector& values); + + bool HasReceivedValues() const; + + QuicTagVector GetReceivedValues() const; + + void SetReceivedValues(const QuicTagVector& values); + + // If has_send_value is true, serialises |tag_vector_| and |send_value_| to + // |out|. + virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE; + + // Sets |receive_values_| to the corresponding value from |client_hello_| if + // it exists. + virtual QuicErrorCode ProcessPeerHello( + const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + std::string* error_details) OVERRIDE; + + private: + QuicTagVector send_values_; + bool has_send_values_; + QuicTagVector receive_values_; + bool has_receive_values_; +}; + // QuicConfig contains non-crypto configuration options that are negotiated in // the crypto handshake. class NET_EXPORT_PRIVATE QuicConfig { @@ -136,10 +258,22 @@ class NET_EXPORT_PRIVATE QuicConfig { QuicConfig(); ~QuicConfig(); - void set_congestion_control(const QuicTagVector& congestion_control, - QuicTag default_congestion_control); + void set_congestion_feedback(const QuicTagVector& congestion_feedback, + QuicTag default_congestion_feedback); + + QuicTag congestion_feedback() const; + + void SetCongestionOptionsToSend(const QuicTagVector& congestion_options); + + bool HasReceivedCongestionOptions() const; + + QuicTagVector ReceivedCongestionOptions() const; + + void SetLossDetectionToSend(QuicTag loss_detection); - QuicTag congestion_control() const; + bool HasReceivedLossDetection() const; + + QuicTag ReceivedLossDetection() const; void set_idle_connection_state_lifetime( QuicTime::Delta max_idle_connection_state_lifetime, @@ -159,40 +293,76 @@ class NET_EXPORT_PRIVATE QuicConfig { QuicTime::Delta max_time_before_crypto_handshake() const; - // Sets the server's TCP sender's max and default initial congestion window - // in packets. - void set_server_initial_congestion_window(size_t max_initial_window, - size_t default_initial_window); + // Sets the peer's default initial congestion window in packets. + void SetInitialCongestionWindowToSend(size_t initial_window); + + bool HasReceivedInitialCongestionWindow() const; - uint32 server_initial_congestion_window() const; + uint32 ReceivedInitialCongestionWindow() const; // Sets an estimated initial round trip time in us. - void set_initial_round_trip_time_us(size_t max_rtt, size_t default_rtt); + void SetInitialRoundTripTimeUsToSend(size_t rtt_us); + + bool HasReceivedInitialRoundTripTimeUs() const; + + uint32 ReceivedInitialRoundTripTimeUs() const; + + // TODO(rjshade): Remove all InitialFlowControlWindow methods when removing + // QUIC_VERSION_19. + // Sets an initial stream flow control window size to transmit to the peer. + void SetInitialFlowControlWindowToSend(uint32 window_bytes); + + uint32 GetInitialFlowControlWindowToSend() const; + + bool HasReceivedInitialFlowControlWindowBytes() const; + + uint32 ReceivedInitialFlowControlWindowBytes() const; + + // Sets an initial stream flow control window size to transmit to the peer. + void SetInitialStreamFlowControlWindowToSend(uint32 window_bytes); + + uint32 GetInitialStreamFlowControlWindowToSend() const; + + bool HasReceivedInitialStreamFlowControlWindowBytes() const; - uint32 initial_round_trip_time_us() const; + uint32 ReceivedInitialStreamFlowControlWindowBytes() const; + + // Sets an initial session flow control window size to transmit to the peer. + void SetInitialSessionFlowControlWindowToSend(uint32 window_bytes); + + uint32 GetInitialSessionFlowControlWindowToSend() const; + + bool HasReceivedInitialSessionFlowControlWindowBytes() const; + + uint32 ReceivedInitialSessionFlowControlWindowBytes() const; bool negotiated(); // SetDefaults sets the members to sensible, default values. void SetDefaults(); - // ToHandshakeMessage serializes the settings in this object as a series of + // Enabled pacing. + void EnablePacing(bool enable_pacing); + + // ToHandshakeMessage serialises the settings in this object as a series of // tags /value pairs and adds them to |out|. void ToHandshakeMessage(CryptoHandshakeMessage* out) const; - // Calls ProcessClientHello on each negotiable parameter. On failure returns + // Calls ProcessPeerHello on each negotiable parameter. On failure returns // the corresponding QuicErrorCode and sets detailed error in |error_details|. - QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello, - std::string* error_details); - - // Calls ProcessServerHello on each negotiable parameter. On failure returns - // the corresponding QuicErrorCode and sets detailed error in |error_details|. - QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, - std::string* error_details); + QuicErrorCode ProcessPeerHello(const CryptoHandshakeMessage& peer_hello, + HelloType hello_type, + std::string* error_details); private: + friend class test::QuicConfigPeer; + // Congestion control feedback type. - QuicNegotiableTag congestion_control_; + QuicNegotiableTag congestion_feedback_; + // Congestion control option. + QuicFixedTagVector congestion_options_; + // Loss detection feedback type. + QuicFixedTag loss_detection_; // Idle connection state lifetime QuicNegotiableUint32 idle_connection_state_lifetime_seconds_; // Keepalive timeout, or 0 to turn off keepalive probes @@ -203,9 +373,18 @@ class NET_EXPORT_PRIVATE QuicConfig { // finished. (Not negotiated). QuicTime::Delta max_time_before_crypto_handshake_; // Initial congestion window in packets. - QuicNegotiableUint32 server_initial_congestion_window_; + QuicFixedUint32 initial_congestion_window_; // Initial round trip time estimate in microseconds. - QuicNegotiableUint32 initial_round_trip_time_us_; + QuicFixedUint32 initial_round_trip_time_us_; + + // TODO(rjshade): Remove when removing QUIC_VERSION_19. + // Initial flow control receive window in bytes. + QuicFixedUint32 initial_flow_control_window_bytes_; + + // Initial stream flow control receive window in bytes. + QuicFixedUint32 initial_stream_flow_control_window_bytes_; + // Initial session flow control receive window in bytes. + QuicFixedUint32 initial_session_flow_control_window_bytes_; }; } // namespace net diff --git a/chromium/net/quic/quic_config_test.cc b/chromium/net/quic/quic_config_test.cc index 2434cd772fd..3cb2dd26d3a 100644 --- a/chromium/net/quic/quic_config_test.cc +++ b/chromium/net/quic/quic_config_test.cc @@ -4,12 +4,15 @@ #include "net/quic/quic_config.h" -#include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/crypto/crypto_handshake_message.h" #include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_sent_packet_manager.h" #include "net/quic/quic_time.h" +#include "net/quic/quic_utils.h" #include "net/quic/test_tools/quic_test_utils.h" +#include "net/test/gtest_util.h" #include "testing/gtest/include/gtest/gtest.h" using std::string; @@ -22,15 +25,20 @@ class QuicConfigTest : public ::testing::Test { protected: QuicConfigTest() { config_.SetDefaults(); - config_.set_initial_round_trip_time_us(kMaxInitialRoundTripTimeUs, 0); } QuicConfig config_; }; TEST_F(QuicConfigTest, ToHandshakeMessage) { - FLAGS_enable_quic_pacing = false; + ValueRestore<bool> old_flag(&FLAGS_enable_quic_pacing, false); config_.SetDefaults(); + config_.SetInitialFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + config_.SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + config_.SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); config_.set_idle_connection_state_lifetime(QuicTime::Delta::FromSeconds(5), QuicTime::Delta::FromSeconds(2)); config_.set_max_streams_per_connection(4, 2); @@ -46,6 +54,18 @@ TEST_F(QuicConfigTest, ToHandshakeMessage) { EXPECT_EQ(QUIC_NO_ERROR, error); EXPECT_EQ(4u, value); + error = msg.GetUint32(kIFCW, &value); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, value); + + error = msg.GetUint32(kSFCW, &value); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_EQ(kInitialStreamFlowControlWindowForTest, value); + + error = msg.GetUint32(kCFCW, &value); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, value); + const QuicTag* out; size_t out_len; error = msg.GetTaglist(kCGST, &out, &out_len); @@ -73,65 +93,130 @@ TEST_F(QuicConfigTest, ProcessClientHello) { QuicTagVector cgst; cgst.push_back(kINAR); cgst.push_back(kQBIC); - client_config.set_congestion_control(cgst, kQBIC); + client_config.set_congestion_feedback(cgst, kQBIC); client_config.set_idle_connection_state_lifetime( QuicTime::Delta::FromSeconds(2 * kDefaultTimeoutSecs), QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs)); client_config.set_max_streams_per_connection( 2 * kDefaultMaxStreamsPerConnection, kDefaultMaxStreamsPerConnection); - client_config.set_initial_round_trip_time_us( - 10 * base::Time::kMicrosecondsPerMillisecond, + client_config.SetInitialRoundTripTimeUsToSend( 10 * base::Time::kMicrosecondsPerMillisecond); - + client_config.SetInitialFlowControlWindowToSend( + 2 * kInitialSessionFlowControlWindowForTest); + client_config.SetInitialStreamFlowControlWindowToSend( + 2 * kInitialStreamFlowControlWindowForTest); + client_config.SetInitialSessionFlowControlWindowToSend( + 2 * kInitialSessionFlowControlWindowForTest); + QuicTagVector copt; + copt.push_back(kTBBR); + client_config.SetCongestionOptionsToSend(copt); CryptoHandshakeMessage msg; client_config.ToHandshakeMessage(&msg); string error_details; - const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details); + const QuicErrorCode error = + config_.ProcessPeerHello(msg, CLIENT, &error_details); EXPECT_EQ(QUIC_NO_ERROR, error); EXPECT_TRUE(config_.negotiated()); - EXPECT_EQ(kQBIC, config_.congestion_control()); + EXPECT_EQ(kQBIC, config_.congestion_feedback()); EXPECT_EQ(QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs), config_.idle_connection_state_lifetime()); EXPECT_EQ(kDefaultMaxStreamsPerConnection, config_.max_streams_per_connection()); EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.keepalive_timeout()); EXPECT_EQ(10 * base::Time::kMicrosecondsPerMillisecond, - config_.initial_round_trip_time_us()); + config_.ReceivedInitialRoundTripTimeUs()); + EXPECT_FALSE(config_.HasReceivedLossDetection()); + EXPECT_TRUE(config_.HasReceivedCongestionOptions()); + EXPECT_EQ(1u, config_.ReceivedCongestionOptions().size()); + EXPECT_EQ(config_.ReceivedCongestionOptions()[0], kTBBR); + EXPECT_EQ(config_.ReceivedInitialFlowControlWindowBytes(), + 2 * kInitialSessionFlowControlWindowForTest); + EXPECT_EQ(config_.ReceivedInitialStreamFlowControlWindowBytes(), + 2 * kInitialStreamFlowControlWindowForTest); + EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(), + 2 * kInitialSessionFlowControlWindowForTest); } TEST_F(QuicConfigTest, ProcessServerHello) { QuicConfig server_config; QuicTagVector cgst; cgst.push_back(kQBIC); - server_config.set_congestion_control(cgst, kQBIC); + server_config.set_congestion_feedback(cgst, kQBIC); server_config.set_idle_connection_state_lifetime( QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2), QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2)); server_config.set_max_streams_per_connection( kDefaultMaxStreamsPerConnection / 2, kDefaultMaxStreamsPerConnection / 2); - server_config.set_server_initial_congestion_window(kDefaultInitialWindow / 2, - kDefaultInitialWindow / 2); - server_config.set_initial_round_trip_time_us( - 10 * base::Time::kMicrosecondsPerMillisecond, + server_config.SetInitialCongestionWindowToSend(kDefaultInitialWindow / 2); + server_config.SetInitialRoundTripTimeUsToSend( 10 * base::Time::kMicrosecondsPerMillisecond); - + server_config.SetInitialFlowControlWindowToSend( + 2 * kInitialSessionFlowControlWindowForTest); + server_config.SetInitialStreamFlowControlWindowToSend( + 2 * kInitialStreamFlowControlWindowForTest); + server_config.SetInitialSessionFlowControlWindowToSend( + 2 * kInitialSessionFlowControlWindowForTest); CryptoHandshakeMessage msg; server_config.ToHandshakeMessage(&msg); string error_details; - const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details); + const QuicErrorCode error = + config_.ProcessPeerHello(msg, SERVER, &error_details); EXPECT_EQ(QUIC_NO_ERROR, error); EXPECT_TRUE(config_.negotiated()); - EXPECT_EQ(kQBIC, config_.congestion_control()); + EXPECT_EQ(kQBIC, config_.congestion_feedback()); EXPECT_EQ(QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2), config_.idle_connection_state_lifetime()); EXPECT_EQ(kDefaultMaxStreamsPerConnection / 2, config_.max_streams_per_connection()); EXPECT_EQ(kDefaultInitialWindow / 2, - config_.server_initial_congestion_window()); + config_.ReceivedInitialCongestionWindow()); EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.keepalive_timeout()); EXPECT_EQ(10 * base::Time::kMicrosecondsPerMillisecond, - config_.initial_round_trip_time_us()); + config_.ReceivedInitialRoundTripTimeUs()); + EXPECT_FALSE(config_.HasReceivedLossDetection()); + EXPECT_EQ(config_.ReceivedInitialFlowControlWindowBytes(), + 2 * kInitialSessionFlowControlWindowForTest); + EXPECT_EQ(config_.ReceivedInitialStreamFlowControlWindowBytes(), + 2 * kInitialStreamFlowControlWindowForTest); + EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(), + 2 * kInitialSessionFlowControlWindowForTest); +} + +TEST_F(QuicConfigTest, MissingOptionalValuesInCHLO) { + CryptoHandshakeMessage msg; + msg.SetValue(kICSL, 1); + msg.SetVector(kCGST, QuicTagVector(1, kQBIC)); + + // Set all REQUIRED tags. + msg.SetValue(kICSL, 1); + msg.SetVector(kCGST, QuicTagVector(1, kQBIC)); + msg.SetValue(kMSPC, 1); + + // No error, as rest are optional. + string error_details; + const QuicErrorCode error = + config_.ProcessPeerHello(msg, CLIENT, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + + EXPECT_FALSE(config_.HasReceivedInitialFlowControlWindowBytes()); +} + +TEST_F(QuicConfigTest, MissingOptionalValuesInSHLO) { + CryptoHandshakeMessage msg; + + // Set all REQUIRED tags. + msg.SetValue(kICSL, 1); + msg.SetVector(kCGST, QuicTagVector(1, kQBIC)); + msg.SetValue(kMSPC, 1); + + // No error, as rest are optional. + string error_details; + const QuicErrorCode error = + config_.ProcessPeerHello(msg, SERVER, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + + EXPECT_FALSE(config_.HasReceivedInitialFlowControlWindowBytes()); } TEST_F(QuicConfigTest, MissingValueInCHLO) { @@ -140,7 +225,8 @@ TEST_F(QuicConfigTest, MissingValueInCHLO) { msg.SetVector(kCGST, QuicTagVector(1, kQBIC)); // Missing kMSPC. KATO is optional. string error_details; - const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details); + const QuicErrorCode error = + config_.ProcessPeerHello(msg, CLIENT, &error_details); EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error); } @@ -150,7 +236,8 @@ TEST_F(QuicConfigTest, MissingValueInSHLO) { msg.SetValue(kMSPC, 3); // Missing CGST. KATO is optional. string error_details; - const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details); + const QuicErrorCode error = + config_.ProcessPeerHello(msg, SERVER, &error_details); EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error); } @@ -163,7 +250,8 @@ TEST_F(QuicConfigTest, OutOfBoundSHLO) { CryptoHandshakeMessage msg; server_config.ToHandshakeMessage(&msg); string error_details; - const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details); + const QuicErrorCode error = + config_.ProcessPeerHello(msg, SERVER, &error_details); EXPECT_EQ(QUIC_INVALID_NEGOTIATED_VALUE, error); } @@ -172,12 +260,13 @@ TEST_F(QuicConfigTest, MultipleNegotiatedValuesInVectorTag) { QuicTagVector cgst; cgst.push_back(kQBIC); cgst.push_back(kINAR); - server_config.set_congestion_control(cgst, kQBIC); + server_config.set_congestion_feedback(cgst, kQBIC); CryptoHandshakeMessage msg; server_config.ToHandshakeMessage(&msg); string error_details; - const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details); + const QuicErrorCode error = + config_.ProcessPeerHello(msg, SERVER, &error_details); EXPECT_EQ(QUIC_INVALID_NEGOTIATED_VALUE, error); } @@ -186,16 +275,29 @@ TEST_F(QuicConfigTest, NoOverLapInCGST) { server_config.SetDefaults(); QuicTagVector cgst; cgst.push_back(kINAR); - server_config.set_congestion_control(cgst, kINAR); + server_config.set_congestion_feedback(cgst, kINAR); CryptoHandshakeMessage msg; string error_details; server_config.ToHandshakeMessage(&msg); - const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details); - LOG(INFO) << QuicUtils::ErrorToString(error); + const QuicErrorCode error = + config_.ProcessPeerHello(msg, CLIENT, &error_details); + DVLOG(1) << QuicUtils::ErrorToString(error); EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP, error); } +TEST_F(QuicConfigTest, InvalidFlowControlWindow) { + // QuicConfig should not accept an invalid flow control window to send to the + // peer: the receive window must be at least the default of 16 Kb. + QuicConfig config; + const uint64 kInvalidWindow = kDefaultFlowControlSendWindow - 1; + EXPECT_DFATAL(config.SetInitialFlowControlWindowToSend(kInvalidWindow), + "Initial flow control receive window"); + + EXPECT_EQ(kDefaultFlowControlSendWindow, + config.GetInitialFlowControlWindowToSend()); +} + } // namespace } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_connection.cc b/chromium/net/quic/quic_connection.cc index 51a83cc41a0..a977b3fdcdb 100644 --- a/chromium/net/quic/quic_connection.cc +++ b/chromium/net/quic/quic_connection.cc @@ -13,6 +13,7 @@ #include <set> #include <utility> +#include "base/debug/stack_trace.h" #include "base/logging.h" #include "base/stl_util.h" #include "net/base/net_errors.h" @@ -21,26 +22,20 @@ #include "net/quic/iovector.h" #include "net/quic/quic_bandwidth.h" #include "net/quic/quic_config.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_utils.h" +using base::StringPiece; using base::hash_map; using base::hash_set; -using base::StringPiece; using std::list; using std::make_pair; -using std::min; using std::max; +using std::min; using std::numeric_limits; -using std::vector; using std::set; using std::string; - -int FLAGS_fake_packet_loss_percentage = 0; - -// If true, then QUIC connections will bundle acks with any outgoing packet when -// an ack is being delayed. This is an optimization to reduce ack latency and -// packet count of pure ack packets. -bool FLAGS_bundle_ack_with_outgoing_packet = false; +using std::vector; namespace net { @@ -66,7 +61,6 @@ bool Near(QuicPacketSequenceNumber a, QuicPacketSequenceNumber b) { return delta <= kMaxPacketGap; } - // An alarm that is scheduled to send an ack if a timeout occurs. class AckAlarm : public QuicAlarm::Delegate { public: @@ -81,6 +75,8 @@ class AckAlarm : public QuicAlarm::Delegate { private: QuicConnection* connection_; + + DISALLOW_COPY_AND_ASSIGN(AckAlarm); }; // This alarm will be scheduled any time a data-bearing packet is sent out. @@ -99,6 +95,8 @@ class RetransmissionAlarm : public QuicAlarm::Delegate { private: QuicConnection* connection_; + + DISALLOW_COPY_AND_ASSIGN(RetransmissionAlarm); }; // An alarm that is scheduled when the sent scheduler requires a @@ -111,12 +109,14 @@ class SendAlarm : public QuicAlarm::Delegate { virtual QuicTime OnAlarm() OVERRIDE { connection_->WriteIfNotBlocked(); - // Never reschedule the alarm, since OnCanWrite does that. + // Never reschedule the alarm, since CanWrite does that. return QuicTime::Zero(); } private: QuicConnection* connection_; + + DISALLOW_COPY_AND_ASSIGN(SendAlarm); }; class TimeoutAlarm : public QuicAlarm::Delegate { @@ -133,90 +133,119 @@ class TimeoutAlarm : public QuicAlarm::Delegate { private: QuicConnection* connection_; + + DISALLOW_COPY_AND_ASSIGN(TimeoutAlarm); }; -// Indicates if any of the frames are intended to be sent with FORCE. -// Returns FORCE when one of the frames is a CONNECTION_CLOSE_FRAME. -net::QuicConnection::Force HasForcedFrames( - const RetransmittableFrames* retransmittable_frames) { - if (!retransmittable_frames) { - return net::QuicConnection::NO_FORCE; +class PingAlarm : public QuicAlarm::Delegate { + public: + explicit PingAlarm(QuicConnection* connection) + : connection_(connection) { } - for (size_t i = 0; i < retransmittable_frames->frames().size(); ++i) { - if (retransmittable_frames->frames()[i].type == CONNECTION_CLOSE_FRAME) { - return net::QuicConnection::FORCE; - } + + virtual QuicTime OnAlarm() OVERRIDE { + connection_->SendPing(); + return QuicTime::Zero(); } - return net::QuicConnection::NO_FORCE; -} -net::IsHandshake HasCryptoHandshake( + private: + QuicConnection* connection_; + + DISALLOW_COPY_AND_ASSIGN(PingAlarm); +}; + +QuicConnection::PacketType GetPacketType( const RetransmittableFrames* retransmittable_frames) { if (!retransmittable_frames) { - return net::NOT_HANDSHAKE; + return QuicConnection::NORMAL; } for (size_t i = 0; i < retransmittable_frames->frames().size(); ++i) { - if (retransmittable_frames->frames()[i].type == STREAM_FRAME && - retransmittable_frames->frames()[i].stream_frame->stream_id == - kCryptoStreamId) { - return net::IS_HANDSHAKE; + if (retransmittable_frames->frames()[i].type == CONNECTION_CLOSE_FRAME) { + return QuicConnection::CONNECTION_CLOSE; } } - return net::NOT_HANDSHAKE; + return QuicConnection::NORMAL; } } // namespace +QuicConnection::QueuedPacket::QueuedPacket(SerializedPacket packet, + EncryptionLevel level, + TransmissionType transmission_type) + : sequence_number(packet.sequence_number), + packet(packet.packet), + encryption_level(level), + transmission_type(transmission_type), + retransmittable((transmission_type != NOT_RETRANSMISSION || + packet.retransmittable_frames != NULL) ? + HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA), + handshake(packet.retransmittable_frames == NULL ? + NOT_HANDSHAKE : packet.retransmittable_frames->HasCryptoHandshake()), + type(GetPacketType(packet.retransmittable_frames)), + length(packet.packet->length()) { +} + #define ENDPOINT (is_server_ ? "Server: " : " Client: ") -QuicConnection::QuicConnection(QuicGuid guid, +QuicConnection::QuicConnection(QuicConnectionId connection_id, IPEndPoint address, QuicConnectionHelperInterface* helper, QuicPacketWriter* writer, bool is_server, const QuicVersionVector& supported_versions) - : framer_(supported_versions, - helper->GetClock()->ApproximateNow(), + : framer_(supported_versions, helper->GetClock()->ApproximateNow(), is_server), helper_(helper), writer_(writer), encryption_level_(ENCRYPTION_NONE), clock_(helper->GetClock()), random_generator_(helper->GetRandomGenerator()), - guid_(guid), + connection_id_(connection_id), peer_address_(address), + migrating_peer_port_(0), + last_packet_revived_(false), + last_size_(0), + last_decrypted_packet_level_(ENCRYPTION_NONE), largest_seen_packet_with_ack_(0), + largest_seen_packet_with_stop_waiting_(0), pending_version_negotiation_packet_(false), - write_blocked_(false), - received_packet_manager_(kTCP), + received_packet_manager_(kTCP, &stats_), + ack_queued_(false), + stop_waiting_count_(0), ack_alarm_(helper->CreateAlarm(new AckAlarm(this))), retransmission_alarm_(helper->CreateAlarm(new RetransmissionAlarm(this))), send_alarm_(helper->CreateAlarm(new SendAlarm(this))), resume_writes_alarm_(helper->CreateAlarm(new SendAlarm(this))), timeout_alarm_(helper->CreateAlarm(new TimeoutAlarm(this))), + ping_alarm_(helper->CreateAlarm(new PingAlarm(this))), debug_visitor_(NULL), - packet_creator_(guid_, &framer_, random_generator_, is_server), - packet_generator_(this, NULL, &packet_creator_), + packet_generator_(connection_id_, &framer_, random_generator_, this), idle_network_timeout_( QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs)), overall_connection_timeout_(QuicTime::Delta::Infinite()), - creation_time_(clock_->ApproximateNow()), time_of_last_received_packet_(clock_->ApproximateNow()), - time_of_last_sent_packet_(clock_->ApproximateNow()), - sequence_number_of_last_inorder_packet_(0), - sent_packet_manager_(is_server, this, clock_, kTCP), + time_of_last_sent_new_packet_(clock_->ApproximateNow()), + sequence_number_of_last_sent_packet_(0), + sent_packet_manager_( + is_server, clock_, &stats_, kTCP, + FLAGS_quic_use_time_loss_detection ? kTime : kNack), version_negotiation_state_(START_NEGOTIATION), is_server_(is_server), connected_(true), - address_migrating_(false) { + peer_ip_changed_(false), + peer_port_changed_(false), + self_ip_changed_(false), + self_port_changed_(false) { if (!is_server_) { // Pacing will be enabled if the client negotiates it. sent_packet_manager_.MaybeEnablePacing(); } - DVLOG(1) << ENDPOINT << "Created connection with guid: " << guid; + DVLOG(1) << ENDPOINT << "Created connection with connection_id: " + << connection_id; timeout_alarm_->Set(clock_->ApproximateNow().Add(idle_network_timeout_)); framer_.set_visitor(this); framer_.set_received_entropy_calculator(&received_packet_manager_); + stats_.connection_creation_time = clock_->ApproximateNow(); } QuicConnection::~QuicConnection() { @@ -229,7 +258,6 @@ QuicConnection::~QuicConnection() { } void QuicConnection::SetFromConfig(const QuicConfig& config) { - DCHECK_LT(0u, config.server_initial_congestion_window()); SetIdleNetworkTimeout(config.idle_connection_state_lifetime()); sent_packet_manager_.SetFromConfig(config); // TODO(satyamshekhar): Set congestion control and ICSL also. @@ -265,9 +293,12 @@ void QuicConnection::OnError(QuicFramer* framer) { void QuicConnection::OnPacket() { DCHECK(last_stream_frames_.empty() && last_goaway_frames_.empty() && + last_window_update_frames_.empty() && + last_blocked_frames_.empty() && last_rst_frames_.empty() && last_ack_frames_.empty() && - last_congestion_frames_.empty()); + last_congestion_frames_.empty() && + last_stop_waiting_frames_.empty()); } void QuicConnection::OnPublicResetPacket( @@ -280,7 +311,7 @@ void QuicConnection::OnPublicResetPacket( bool QuicConnection::OnProtocolVersionMismatch(QuicVersion received_version) { DVLOG(1) << ENDPOINT << "Received packet with mismatched version " - << received_version; + << received_version; // TODO(satyamshekhar): Implement no server state in this mode. if (!is_server_) { LOG(DFATAL) << ENDPOINT << "Framer called OnProtocolVersionMismatch. " @@ -375,10 +406,19 @@ void QuicConnection::OnVersionNegotiationPacket( void QuicConnection::OnRevivedPacket() { } +bool QuicConnection::OnUnauthenticatedPublicHeader( + const QuicPacketPublicHeader& header) { + return true; +} + bool QuicConnection::OnUnauthenticatedHeader(const QuicPacketHeader& header) { return true; } +void QuicConnection::OnDecryptedPacket(EncryptionLevel level) { + last_decrypted_packet_level_ = level; +} + bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { if (debug_visitor_) { debug_visitor_->OnPacketHeader(header); @@ -391,16 +431,17 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { // Will be decrement below if we fall through to return true; ++stats_.packets_dropped; - if (header.public_header.guid != guid_) { - DVLOG(1) << ENDPOINT << "Ignoring packet from unexpected GUID: " - << header.public_header.guid << " instead of " << guid_; + if (header.public_header.connection_id != connection_id_) { + DVLOG(1) << ENDPOINT << "Ignoring packet from unexpected ConnectionId: " + << header.public_header.connection_id << " instead of " + << connection_id_; return false; } if (!Near(header.packet_sequence_number, last_header_.packet_sequence_number)) { DVLOG(1) << ENDPOINT << "Packet " << header.packet_sequence_number - << " out of bounds. Discarding"; + << " out of bounds. Discarding"; SendConnectionCloseWithDetails(QUIC_INVALID_PACKET_HEADER, "Packet sequence number out of bounds"); return false; @@ -410,14 +451,18 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { // has told us will not be retransmitted, then stop processing the packet. if (!received_packet_manager_.IsAwaitingPacket( header.packet_sequence_number)) { + DVLOG(1) << ENDPOINT << "Packet " << header.packet_sequence_number + << " no longer being waited for. Discarding."; + // TODO(jri): Log reception of duplicate packets or packets the peer has + // told us to stop waiting for. return false; } if (version_negotiation_state_ != NEGOTIATED_VERSION) { if (is_server_) { if (!header.public_header.version_flag) { - DLOG(WARNING) << ENDPOINT << "Got packet without version flag before " - << "version negotiated."; + DLOG(WARNING) << ENDPOINT << "Packet " << header.packet_sequence_number + << " without version flag before version negotiated."; // Packets should have the version flag till version negotiation is // done. CloseConnection(QUIC_INVALID_VERSION, false); @@ -432,7 +477,7 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { DCHECK(!header.public_header.version_flag); // If the client gets a packet without the version flag from the server // it should stop sending version since the version negotiation is done. - packet_creator_.StopSendingVersion(); + packet_generator_.StopSendingVersion(); version_negotiation_state_ = NEGOTIATED_VERSION; visitor_->OnSuccessfulVersionNegotiation(version()); } @@ -452,7 +497,7 @@ void QuicConnection::OnFecProtectedPayload(StringPiece payload) { DCHECK_NE(0u, last_header_.fec_group); QuicFecGroup* group = GetFecGroup(); if (group != NULL) { - group->Update(last_header_, payload); + group->Update(last_decrypted_packet_level_, last_header_, payload); } } @@ -461,6 +506,13 @@ bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) { if (debug_visitor_) { debug_visitor_->OnStreamFrame(frame); } + if (frame.stream_id != kCryptoStreamId && + last_decrypted_packet_level_ == ENCRYPTION_NONE) { + DLOG(WARNING) << ENDPOINT + << "Received an unencrypted data frame: closing connection"; + SendConnectionClose(QUIC_UNENCRYPTED_STREAM_DATA); + return false; + } last_stream_frames_.push_back(frame); return true; } @@ -488,34 +540,39 @@ bool QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) { void QuicConnection::ProcessAckFrame(const QuicAckFrame& incoming_ack) { largest_seen_packet_with_ack_ = last_header_.packet_sequence_number; - - received_packet_manager_.UpdatePacketInformationReceivedByPeer(incoming_ack); - received_packet_manager_.UpdatePacketInformationSentByPeer(incoming_ack); - // Possibly close any FecGroups which are now irrelevant. - CloseFecGroupsBefore(incoming_ack.sent_info.least_unacked + 1); + received_packet_manager_.UpdatePacketInformationReceivedByPeer( + incoming_ack.received_info); + if (version() <= QUIC_VERSION_15) { + ProcessStopWaitingFrame(incoming_ack.sent_info); + } sent_entropy_manager_.ClearEntropyBefore( received_packet_manager_.least_packet_awaited_by_peer() - 1); - bool reset_retransmission_alarm = - sent_packet_manager_.OnIncomingAck(incoming_ack.received_info, - time_of_last_received_packet_); + sent_packet_manager_.OnIncomingAck(incoming_ack.received_info, + time_of_last_received_packet_); if (sent_packet_manager_.HasPendingRetransmissions()) { WriteIfNotBlocked(); } - if (reset_retransmission_alarm) { - retransmission_alarm_->Cancel(); - // Reset the RTO and FEC alarms if the are unacked packets. - if (sent_packet_manager_.HasUnackedPackets()) { - QuicTime::Delta retransmission_delay = - sent_packet_manager_.GetRetransmissionDelay(); - retransmission_alarm_->Set( - clock_->ApproximateNow().Add(retransmission_delay)); - } + // Always reset the retransmission alarm when an ack comes in, since we now + // have a better estimate of the current rtt than when it was set. + retransmission_alarm_->Cancel(); + QuicTime retransmission_time = + sent_packet_manager_.GetRetransmissionTime(); + if (retransmission_time != QuicTime::Zero()) { + retransmission_alarm_->Set(retransmission_time); } } +void QuicConnection::ProcessStopWaitingFrame( + const QuicStopWaitingFrame& stop_waiting) { + largest_seen_packet_with_stop_waiting_ = last_header_.packet_sequence_number; + received_packet_manager_.UpdatePacketInformationSentByPeer(stop_waiting); + // Possibly close any FecGroups which are now irrelevant. + CloseFecGroupsBefore(stop_waiting.least_unacked + 1); +} + bool QuicConnection::OnCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback) { DCHECK(connected_); @@ -526,12 +583,42 @@ bool QuicConnection::OnCongestionFeedbackFrame( return connected_; } +bool QuicConnection::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) { + DCHECK(connected_); + + if (last_header_.packet_sequence_number <= + largest_seen_packet_with_stop_waiting_) { + DVLOG(1) << ENDPOINT << "Received an old stop waiting frame: ignoring"; + return true; + } + + if (!ValidateStopWaitingFrame(frame)) { + SendConnectionClose(QUIC_INVALID_STOP_WAITING_DATA); + return false; + } + + if (debug_visitor_) { + debug_visitor_->OnStopWaitingFrame(frame); + } + + last_stop_waiting_frames_.push_back(frame); + return connected_; +} + +bool QuicConnection::OnPingFrame(const QuicPingFrame& frame) { + DCHECK(connected_); + if (debug_visitor_) { + debug_visitor_->OnPingFrame(frame); + } + return true; +} + bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { if (incoming_ack.received_info.largest_observed > - packet_creator_.sequence_number()) { + packet_generator_.sequence_number()) { DLOG(ERROR) << ENDPOINT << "Peer's observed unsent packet:" << incoming_ack.received_info.largest_observed << " vs " - << packet_creator_.sequence_number(); + << packet_generator_.sequence_number(); // We got an error for data we have not sent. Error out. return false; } @@ -546,22 +633,10 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { return false; } - if (incoming_ack.sent_info.least_unacked < - received_packet_manager_.peer_least_packet_awaiting_ack()) { - DLOG(ERROR) << ENDPOINT << "Peer's sent low least_unacked: " - << incoming_ack.sent_info.least_unacked << " vs " - << received_packet_manager_.peer_least_packet_awaiting_ack(); - // We never process old ack frames, so this number should only increase. - return false; - } - - if (incoming_ack.sent_info.least_unacked > - last_header_.packet_sequence_number) { - DLOG(ERROR) << ENDPOINT << "Peer sent least_unacked:" - << incoming_ack.sent_info.least_unacked - << " greater than the enclosing packet sequence number:" - << last_header_.packet_sequence_number; - return false; + if (version() <= QUIC_VERSION_15) { + if (!ValidateStopWaitingFrame(incoming_ack.sent_info)) { + return false; + } } if (!incoming_ack.received_info.missing_packets.empty() && @@ -592,6 +667,38 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { return false; } + for (SequenceNumberSet::const_iterator iter = + incoming_ack.received_info.revived_packets.begin(); + iter != incoming_ack.received_info.revived_packets.end(); ++iter) { + if (!ContainsKey(incoming_ack.received_info.missing_packets, *iter)) { + DLOG(ERROR) << ENDPOINT + << "Peer specified revived packet which was not missing."; + return false; + } + } + return true; +} + +bool QuicConnection::ValidateStopWaitingFrame( + const QuicStopWaitingFrame& stop_waiting) { + if (stop_waiting.least_unacked < + received_packet_manager_.peer_least_packet_awaiting_ack()) { + DLOG(ERROR) << ENDPOINT << "Peer's sent low least_unacked: " + << stop_waiting.least_unacked << " vs " + << received_packet_manager_.peer_least_packet_awaiting_ack(); + // We never process old ack frames, so this number should only increase. + return false; + } + + if (stop_waiting.least_unacked > + last_header_.packet_sequence_number) { + DLOG(ERROR) << ENDPOINT << "Peer sent least_unacked:" + << stop_waiting.least_unacked + << " greater than the enclosing packet sequence number:" + << last_header_.packet_sequence_number; + return false; + } + return true; } @@ -600,8 +707,8 @@ void QuicConnection::OnFecData(const QuicFecData& fec) { DCHECK_NE(0u, last_header_.fec_group); QuicFecGroup* group = GetFecGroup(); if (group != NULL) { - group->UpdateFec(last_header_.packet_sequence_number, - last_header_.entropy_flag, fec); + group->UpdateFec(last_decrypted_packet_level_, + last_header_.packet_sequence_number, fec); } } @@ -611,7 +718,7 @@ bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) { debug_visitor_->OnRstStreamFrame(frame); } DVLOG(1) << ENDPOINT << "Stream reset with error " - << QuicUtils::StreamErrorToString(frame.error_code); + << QuicUtils::StreamErrorToString(frame.error_code); last_rst_frames_.push_back(frame); return connected_; } @@ -622,22 +729,48 @@ bool QuicConnection::OnConnectionCloseFrame( if (debug_visitor_) { debug_visitor_->OnConnectionCloseFrame(frame); } - DVLOG(1) << ENDPOINT << "Connection " << guid() << " closed with error " - << QuicUtils::ErrorToString(frame.error_code) - << " " << frame.error_details; + DVLOG(1) << ENDPOINT << "Connection " << connection_id() + << " closed with error " + << QuicUtils::ErrorToString(frame.error_code) + << " " << frame.error_details; last_close_frames_.push_back(frame); return connected_; } bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) { DCHECK(connected_); + if (debug_visitor_) { + debug_visitor_->OnGoAwayFrame(frame); + } DVLOG(1) << ENDPOINT << "Go away received with error " - << QuicUtils::ErrorToString(frame.error_code) - << " and reason:" << frame.reason_phrase; + << QuicUtils::ErrorToString(frame.error_code) + << " and reason:" << frame.reason_phrase; last_goaway_frames_.push_back(frame); return connected_; } +bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { + DCHECK(connected_); + if (debug_visitor_) { + debug_visitor_->OnWindowUpdateFrame(frame); + } + DVLOG(1) << ENDPOINT << "WindowUpdate received for stream: " + << frame.stream_id << " with byte offset: " << frame.byte_offset; + last_window_update_frames_.push_back(frame); + return connected_; +} + +bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) { + DCHECK(connected_); + if (debug_visitor_) { + debug_visitor_->OnBlockedFrame(frame); + } + DVLOG(1) << ENDPOINT << "Blocked frame received for stream: " + << frame.stream_id; + last_blocked_frames_.push_back(frame); + return connected_; +} + void QuicConnection::OnPacketComplete() { // Don't do anything if this packet closed the connection. if (!connected_) { @@ -646,38 +779,51 @@ void QuicConnection::OnPacketComplete() { } DVLOG(1) << ENDPOINT << (last_packet_revived_ ? "Revived" : "Got") - << " packet " << last_header_.packet_sequence_number - << " with " << last_ack_frames_.size() << " acks, " - << last_congestion_frames_.size() << " congestions, " - << last_goaway_frames_.size() << " goaways, " - << last_rst_frames_.size() << " rsts, " - << last_close_frames_.size() << " closes, " - << last_stream_frames_.size() - << " stream frames for " << last_header_.public_header.guid; - - // Must called before ack processing, because processing acks removes entries - // from unacket_packets_, increasing the least_unacked. - const bool last_packet_should_instigate_ack = ShouldLastPacketInstigateAck(); + << " packet " << last_header_.packet_sequence_number + << " with " << last_ack_frames_.size() << " acks, " + << last_congestion_frames_.size() << " congestions, " + << last_stop_waiting_frames_.size() << " stop_waiting, " + << last_goaway_frames_.size() << " goaways, " + << last_window_update_frames_.size() << " window updates, " + << last_blocked_frames_.size() << " blocked, " + << last_rst_frames_.size() << " rsts, " + << last_close_frames_.size() << " closes, " + << last_stream_frames_.size() + << " stream frames for " + << last_header_.public_header.connection_id; + + // Call MaybeQueueAck() before recording the received packet, since we want + // to trigger an ack if the newly received packet was previously missing. + MaybeQueueAck(); + + // Record received or revived packet to populate ack info correctly before + // processing stream frames, since the processing may result in a response + // packet with a bundled ack. + if (last_packet_revived_) { + received_packet_manager_.RecordPacketRevived( + last_header_.packet_sequence_number); + } else { + received_packet_manager_.RecordPacketReceived( + last_size_, last_header_, time_of_last_received_packet_); + } - // If the incoming packet was missing, send an ack immediately. - bool send_ack_immediately = received_packet_manager_.IsMissing( - last_header_.packet_sequence_number); + if (!last_stream_frames_.empty()) { + visitor_->OnStreamFrames(last_stream_frames_); + } - // Ensure the visitor can process the stream frames before recording and - // processing the rest of the packet. - if (last_stream_frames_.empty() || - visitor_->OnStreamFrames(last_stream_frames_)) { - received_packet_manager_.RecordPacketReceived(last_size_, - last_header_, - time_of_last_received_packet_, - last_packet_revived_); - for (size_t i = 0; i < last_stream_frames_.size(); ++i) { - stats_.stream_bytes_received += - last_stream_frames_[i].data.TotalBufferSize(); - } + for (size_t i = 0; i < last_stream_frames_.size(); ++i) { + stats_.stream_bytes_received += + last_stream_frames_[i].data.TotalBufferSize(); } - // Process stream resets, then acks, then congestion feedback. + // Process window updates, blocked, stream resets, acks, then congestion + // feedback. + if (!last_window_update_frames_.empty()) { + visitor_->OnWindowUpdateFrames(last_window_update_frames_); + } + if (!last_blocked_frames_.empty()) { + visitor_->OnBlockedFrames(last_blocked_frames_); + } for (size_t i = 0; i < last_goaway_frames_.size(); ++i) { visitor_->OnGoAway(last_goaway_frames_[i]); } @@ -691,6 +837,9 @@ void QuicConnection::OnPacketComplete() { sent_packet_manager_.OnIncomingQuicCongestionFeedbackFrame( last_congestion_frames_[i], time_of_last_received_packet_); } + for (size_t i = 0; i < last_stop_waiting_frames_.size(); ++i) { + ProcessStopWaitingFrame(last_stop_waiting_frames_[i]); + } if (!last_close_frames_.empty()) { CloseConnection(last_close_frames_[0].error_code, true); DCHECK(!connected_); @@ -698,20 +847,48 @@ void QuicConnection::OnPacketComplete() { // If there are new missing packets to report, send an ack immediately. if (received_packet_manager_.HasNewMissingPackets()) { - send_ack_immediately = true; + ack_queued_ = true; + ack_alarm_->Cancel(); } - MaybeSendInResponseToPacket(send_ack_immediately, - last_packet_should_instigate_ack); + UpdateStopWaitingCount(); ClearLastFrames(); } +void QuicConnection::MaybeQueueAck() { + // If the incoming packet was missing, send an ack immediately. + ack_queued_ = received_packet_manager_.IsMissing( + last_header_.packet_sequence_number); + + if (!ack_queued_ && ShouldLastPacketInstigateAck()) { + if (ack_alarm_->IsSet()) { + ack_queued_ = true; + } else { + // Send an ack much more quickly for crypto handshake packets. + QuicTime::Delta delayed_ack_time = sent_packet_manager_.DelayedAckTime(); + if (last_stream_frames_.size() == 1 && + last_stream_frames_[0].stream_id == kCryptoStreamId) { + delayed_ack_time = QuicTime::Delta::Zero(); + } + ack_alarm_->Set(clock_->ApproximateNow().Add(delayed_ack_time)); + DVLOG(1) << "Ack timer set; next packet or timer will trigger ACK."; + } + } + + if (ack_queued_) { + ack_alarm_->Cancel(); + } +} + void QuicConnection::ClearLastFrames() { last_stream_frames_.clear(); last_goaway_frames_.clear(); + last_window_update_frames_.clear(); + last_blocked_frames_.clear(); last_rst_frames_.clear(); last_ack_frames_.clear(); + last_stop_waiting_frames_.clear(); last_congestion_frames_.clear(); } @@ -719,7 +896,7 @@ QuicAckFrame* QuicConnection::CreateAckFrame() { QuicAckFrame* outgoing_ack = new QuicAckFrame(); received_packet_manager_.UpdateReceivedPacketInfo( &(outgoing_ack->received_info), clock_->ApproximateNow()); - UpdateSentPacketInfo(&(outgoing_ack->sent_info)); + UpdateStopWaiting(&(outgoing_ack->sent_info)); DVLOG(1) << ENDPOINT << "Creating ack frame: " << *outgoing_ack; return outgoing_ack; } @@ -728,82 +905,91 @@ QuicCongestionFeedbackFrame* QuicConnection::CreateFeedbackFrame() { return new QuicCongestionFeedbackFrame(outgoing_congestion_feedback_); } -bool QuicConnection::ShouldLastPacketInstigateAck() { +QuicStopWaitingFrame* QuicConnection::CreateStopWaitingFrame() { + QuicStopWaitingFrame stop_waiting; + UpdateStopWaiting(&stop_waiting); + return new QuicStopWaitingFrame(stop_waiting); +} + +bool QuicConnection::ShouldLastPacketInstigateAck() const { if (!last_stream_frames_.empty() || !last_goaway_frames_.empty() || - !last_rst_frames_.empty()) { + !last_rst_frames_.empty() || + !last_window_update_frames_.empty() || + !last_blocked_frames_.empty()) { return true; } - // If the peer is still waiting for a packet that we are no - // longer planning to send, we should send an ack to raise - // the high water mark. if (!last_ack_frames_.empty() && - !last_ack_frames_.back().received_info.missing_packets.empty()) { - return sent_packet_manager_.GetLeastUnackedSentPacket() > - *last_ack_frames_.back().received_info.missing_packets.begin(); + last_ack_frames_.back().received_info.is_truncated) { + return true; } return false; } -void QuicConnection::MaybeSendInResponseToPacket( - bool send_ack_immediately, - bool last_packet_should_instigate_ack) { - // |include_ack| is false since we decide about ack bundling below. - ScopedPacketBundler bundler(this, false); +void QuicConnection::UpdateStopWaitingCount() { + if (last_ack_frames_.empty()) { + return; + } - if (last_packet_should_instigate_ack) { - // In general, we ack every second packet. When we don't ack the first - // packet, we set the delayed ack alarm. Thus, if the ack alarm is set - // then we know this is the second packet, and we should send an ack. - if (send_ack_immediately || ack_alarm_->IsSet()) { - SendAck(); - DCHECK(!ack_alarm_->IsSet()); - } else { - ack_alarm_->Set(clock_->ApproximateNow().Add( - sent_packet_manager_.DelayedAckTime())); - DVLOG(1) << "Ack timer set; next packet or timer will trigger ACK."; - } + // If the peer is still waiting for a packet that we are no longer planning to + // send, send an ack to raise the high water mark. + if (!last_ack_frames_.back().received_info.missing_packets.empty() && + GetLeastUnacked() > + *last_ack_frames_.back().received_info.missing_packets.begin()) { + ++stop_waiting_count_; + } else { + stop_waiting_count_ = 0; } +} - if (!last_ack_frames_.empty()) { - // Now the we have received an ack, we might be able to send packets which - // are queued locally, or drain streams which are blocked. - QuicTime::Delta delay = sent_packet_manager_.TimeUntilSend( - time_of_last_received_packet_, NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); - if (delay.IsZero()) { - send_alarm_->Cancel(); - WriteIfNotBlocked(); - } else if (!delay.IsInfinite()) { - send_alarm_->Cancel(); - send_alarm_->Set(time_of_last_received_packet_.Add(delay)); - } +QuicPacketSequenceNumber QuicConnection::GetLeastUnacked() const { + return sent_packet_manager_.HasUnackedPackets() ? + sent_packet_manager_.GetLeastUnackedSentPacket() : + packet_generator_.sequence_number() + 1; +} + +void QuicConnection::MaybeSendInResponseToPacket() { + if (!connected_) { + return; + } + ScopedPacketBundler bundler(this, ack_queued_ ? SEND_ACK : NO_ACK); + + // Now that we have received an ack, we might be able to send packets which + // are queued locally, or drain streams which are blocked. + if (CanWrite(HAS_RETRANSMITTABLE_DATA)) { + OnCanWrite(); } } void QuicConnection::SendVersionNegotiationPacket() { - scoped_ptr<QuicEncryptedPacket> version_packet( - packet_creator_.SerializeVersionNegotiationPacket( - framer_.supported_versions())); - // TODO(satyamshekhar): implement zero server state negotiation. - WriteResult result = - writer_->WritePacket(version_packet->data(), version_packet->length(), - self_address().address(), peer_address(), this); - if (result.status == WRITE_STATUS_BLOCKED) { - write_blocked_ = true; - } - if (result.status == WRITE_STATUS_OK || - (result.status == WRITE_STATUS_BLOCKED && - writer_->IsWriteBlockedDataBuffered())) { - pending_version_negotiation_packet_ = false; + // TODO(alyssar): implement zero server state negotiation. + pending_version_negotiation_packet_ = true; + if (writer_->IsWriteBlocked()) { + visitor_->OnWriteBlocked(); return; } + scoped_ptr<QuicEncryptedPacket> version_packet( + packet_generator_.SerializeVersionNegotiationPacket( + framer_.supported_versions())); + WriteResult result = writer_->WritePacket( + version_packet->data(), version_packet->length(), + self_address().address(), peer_address()); + if (result.status == WRITE_STATUS_ERROR) { // We can't send an error as the socket is presumably borked. CloseConnection(QUIC_PACKET_WRITE_ERROR, false); + return; } - pending_version_negotiation_packet_ = true; + if (result.status == WRITE_STATUS_BLOCKED) { + visitor_->OnWriteBlocked(); + if (writer_->IsWriteBlockedDataBuffered()) { + pending_version_negotiation_packet_ = false; + } + return; + } + + pending_version_negotiation_packet_ = false; } QuicConsumedData QuicConnection::SendStreamData( @@ -811,6 +997,7 @@ QuicConsumedData QuicConnection::SendStreamData( const IOVector& data, QuicStreamOffset offset, bool fin, + FecProtection fec_protection, QuicAckNotifier::DelegateInterface* delegate) { if (!fin && data.Empty()) { LOG(DFATAL) << "Attempt to send empty stream frame"; @@ -823,11 +1010,23 @@ QuicConsumedData QuicConnection::SendStreamData( notifier = new QuicAckNotifier(delegate); } - // Opportunistically bundle an ack with this outgoing packet, unless it's the - // crypto stream. - ScopedPacketBundler ack_bundler(this, id != kCryptoStreamId); + // Opportunistically bundle an ack with every outgoing packet. + // Particularly, we want to bundle with handshake packets since we don't know + // which decrypter will be used on an ack packet following a handshake + // packet (a handshake packet from client to server could result in a REJ or a + // SHLO from the server, leading to two different decrypters at the server.) + // + // TODO(jri): Note that ConsumeData may cause a response packet to be sent. + // We may end up sending stale ack information if there are undecryptable + // packets hanging around and/or there are revivable packets which may get + // handled after this packet is sent. Change ScopedPacketBundler to do the + // right thing: check ack_queued_, and then check undecryptable packets and + // also if there is possibility of revival. Only bundle an ack if there's no + // processing left that may cause received_info_ to change. + ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK); QuicConsumedData consumed_data = - packet_generator_.ConsumeData(id, data, offset, fin, notifier); + packet_generator_.ConsumeData(id, data, offset, fin, fec_protection, + notifier); if (notifier && (consumed_data.bytes_consumed == 0 && !consumed_data.fin_consumed)) { @@ -839,19 +1038,38 @@ QuicConsumedData QuicConnection::SendStreamData( } void QuicConnection::SendRstStream(QuicStreamId id, - QuicRstStreamErrorCode error) { - DVLOG(1) << "Sending RST_STREAM: " << id << " code: " << error; + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written) { // Opportunistically bundle an ack with this outgoing packet. - ScopedPacketBundler ack_bundler(this, true); + ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK); + packet_generator_.AddControlFrame(QuicFrame(new QuicRstStreamFrame( + id, AdjustErrorForVersion(error, version()), bytes_written))); +} + +void QuicConnection::SendWindowUpdate(QuicStreamId id, + QuicStreamOffset byte_offset) { + // Opportunistically bundle an ack with this outgoing packet. + ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK); packet_generator_.AddControlFrame( - QuicFrame(new QuicRstStreamFrame(id, error))); + QuicFrame(new QuicWindowUpdateFrame(id, byte_offset))); +} + +void QuicConnection::SendBlocked(QuicStreamId id) { + // Opportunistically bundle an ack with this outgoing packet. + ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK); + packet_generator_.AddControlFrame(QuicFrame(new QuicBlockedFrame(id))); } const QuicConnectionStats& QuicConnection::GetStats() { // Update rtt and estimated bandwidth. - stats_.rtt = sent_packet_manager_.SmoothedRtt().ToMicroseconds(); + stats_.min_rtt_us = + sent_packet_manager_.GetRttStats()->min_rtt().ToMicroseconds(); + stats_.srtt_us = + sent_packet_manager_.GetRttStats()->SmoothedRtt().ToMicroseconds(); stats_.estimated_bandwidth = sent_packet_manager_.BandwidthEstimate().ToBytesPerSecond(); + stats_.congestion_window = sent_packet_manager_.GetCongestionWindow(); + stats_.max_packet_size = packet_generator_.max_packet_length(); return stats_; } @@ -867,18 +1085,7 @@ void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address, last_packet_revived_ = false; last_size_ = packet.length(); - address_migrating_ = false; - - if (peer_address_.address().empty()) { - peer_address_ = peer_address; - } - if (self_address_.address().empty()) { - self_address_ = self_address; - } - - if (!(peer_address == peer_address_ && self_address == self_address_)) { - address_migrating_ = true; - } + CheckForAddressMigration(self_address, peer_address); stats_.bytes_received += packet.length(); ++stats_.packets_received; @@ -895,101 +1102,128 @@ void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address, << last_header_.packet_sequence_number; return; } + + ++stats_.packets_processed; MaybeProcessUndecryptablePackets(); MaybeProcessRevivedPacket(); + MaybeSendInResponseToPacket(); + SetPingAlarm(); } -bool QuicConnection::OnCanWrite() { - write_blocked_ = false; - return DoWrite(); -} +void QuicConnection::CheckForAddressMigration( + const IPEndPoint& self_address, const IPEndPoint& peer_address) { + peer_ip_changed_ = false; + peer_port_changed_ = false; + self_ip_changed_ = false; + self_port_changed_ = false; -bool QuicConnection::WriteIfNotBlocked() { - if (write_blocked_) { - return false; + if (peer_address_.address().empty()) { + peer_address_ = peer_address; + } + if (self_address_.address().empty()) { + self_address_ = self_address; + } + + if (!peer_address.address().empty() && !peer_address_.address().empty()) { + peer_ip_changed_ = (peer_address.address() != peer_address_.address()); + peer_port_changed_ = (peer_address.port() != peer_address_.port()); + + // Store in case we want to migrate connection in ProcessValidatedPacket. + migrating_peer_port_ = peer_address.port(); + } + + if (!self_address.address().empty() && !self_address_.address().empty()) { + self_ip_changed_ = (self_address.address() != self_address_.address()); + self_port_changed_ = (self_address.port() != self_address_.port()); } - return DoWrite(); } -bool QuicConnection::DoWrite() { - DCHECK(!write_blocked_); - WriteQueuedPackets(); +void QuicConnection::OnCanWrite() { + DCHECK(!writer_->IsWriteBlocked()); + WriteQueuedPackets(); WritePendingRetransmissions(); - IsHandshake pending_handshake = visitor_->HasPendingHandshake() ? - IS_HANDSHAKE : NOT_HANDSHAKE; // Sending queued packets may have caused the socket to become write blocked, // or the congestion manager to prohibit sending. If we've sent everything // we had queued and we're still not blocked, let the visitor know it can // write more. - if (CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, - pending_handshake)) { + if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) { + return; + } + + { // Limit the scope of the bundler. // Set |include_ack| to false in bundler; ack inclusion happens elsewhere. - scoped_ptr<ScopedPacketBundler> bundler( - new ScopedPacketBundler(this, false)); - bool all_bytes_written = visitor_->OnCanWrite(); - bundler.reset(); - // After the visitor writes, it may have caused the socket to become write - // blocked or the congestion manager to prohibit sending, so check again. - pending_handshake = visitor_->HasPendingHandshake() ? IS_HANDSHAKE - : NOT_HANDSHAKE; - if (!all_bytes_written && !resume_writes_alarm_->IsSet() && - CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, - pending_handshake)) { - // We're not write blocked, but some stream didn't write out all of its - // bytes. Register for 'immediate' resumption so we'll keep writing after - // other quic connections have had a chance to use the socket. - resume_writes_alarm_->Set(clock_->ApproximateNow()); - } + ScopedPacketBundler bundler(this, NO_ACK); + visitor_->OnCanWrite(); + } + + // After the visitor writes, it may have caused the socket to become write + // blocked or the congestion manager to prohibit sending, so check again. + if (visitor_->WillingAndAbleToWrite() && + !resume_writes_alarm_->IsSet() && + CanWrite(HAS_RETRANSMITTABLE_DATA)) { + // We're not write blocked, but some stream didn't write out all of its + // bytes. Register for 'immediate' resumption so we'll keep writing after + // other connections and events have had a chance to use the thread. + resume_writes_alarm_->Set(clock_->ApproximateNow()); } +} - return !write_blocked_; +void QuicConnection::WriteIfNotBlocked() { + if (!writer_->IsWriteBlocked()) { + OnCanWrite(); + } } bool QuicConnection::ProcessValidatedPacket() { - if (address_migrating_) { + if ((!FLAGS_quic_allow_port_migration && peer_port_changed_) || + peer_ip_changed_ || self_ip_changed_ || self_port_changed_) { SendConnectionCloseWithDetails( QUIC_ERROR_MIGRATING_ADDRESS, - "Address migration is not yet a supported feature"); + "Neither IP address migration, nor self port migration are supported."); return false; } + + // Port migration is supported, do it now if port has changed. + if (FLAGS_quic_allow_port_migration && + peer_port_changed_) { + DVLOG(1) << ENDPOINT << "Peer's port changed from " + << peer_address_.port() << " to " << migrating_peer_port_ + << ", migrating connection."; + peer_address_ = IPEndPoint(peer_address_.address(), migrating_peer_port_); + } + time_of_last_received_packet_ = clock_->Now(); DVLOG(1) << ENDPOINT << "time of last received packet: " << time_of_last_received_packet_.ToDebuggingValue(); if (is_server_ && encryption_level_ == ENCRYPTION_NONE && - last_size_ > options()->max_packet_length) { - options()->max_packet_length = last_size_; + last_size_ > packet_generator_.max_packet_length()) { + packet_generator_.set_max_packet_length(last_size_); } return true; } -bool QuicConnection::WriteQueuedPackets() { - DCHECK(!write_blocked_); +void QuicConnection::WriteQueuedPackets() { + DCHECK(!writer_->IsWriteBlocked()); if (pending_version_negotiation_packet_) { SendVersionNegotiationPacket(); } QueuedPacketList::iterator packet_iterator = queued_packets_.begin(); - while (!write_blocked_ && packet_iterator != queued_packets_.end()) { - if (WritePacket(packet_iterator->encryption_level, - packet_iterator->sequence_number, - packet_iterator->packet, - packet_iterator->transmission_type, - packet_iterator->retransmittable, - packet_iterator->handshake, - packet_iterator->forced)) { + while (!writer_->IsWriteBlocked() && + packet_iterator != queued_packets_.end()) { + if (WritePacket(*packet_iterator)) { + delete packet_iterator->packet; packet_iterator = queued_packets_.erase(packet_iterator); } else { // Continue, because some queued packets may still be writable. - // This can happen if a retransmit send fail. + // This can happen if a retransmit send fails. ++packet_iterator; } } - - return !write_blocked_; } void QuicConnection::WritePendingRetransmissions() { @@ -998,9 +1232,8 @@ void QuicConnection::WritePendingRetransmissions() { while (sent_packet_manager_.HasPendingRetransmissions()) { const QuicSentPacketManager::PendingRetransmission pending = sent_packet_manager_.NextPendingRetransmission(); - if (HasForcedFrames(&pending.retransmittable_frames) == NO_FORCE && - !CanWrite(pending.transmission_type, HAS_RETRANSMITTABLE_DATA, - HasCryptoHandshake(&pending.retransmittable_frames))) { + if (GetPacketType(&pending.retransmittable_frames) == NORMAL && + !CanWrite(HAS_RETRANSMITTABLE_DATA)) { break; } @@ -1008,22 +1241,23 @@ void QuicConnection::WritePendingRetransmissions() { // Retransmitted data packets do not use FEC, even when it's enabled. // Retransmitted packets use the same sequence number length as the // original. - // Flush the packet creator before making a new packet. + // Flush the packet generator before making a new packet. // TODO(ianswett): Implement ReserializeAllFrames as a separate path that // does not require the creator to be flushed. - Flush(); - SerializedPacket serialized_packet = packet_creator_.ReserializeAllFrames( + packet_generator_.FlushAllQueuedFrames(); + SerializedPacket serialized_packet = packet_generator_.ReserializeAllFrames( pending.retransmittable_frames.frames(), pending.sequence_number_length); DVLOG(1) << ENDPOINT << "Retransmitting " << pending.sequence_number - << " as " << serialized_packet.sequence_number; + << " as " << serialized_packet.sequence_number; if (debug_visitor_) { debug_visitor_->OnPacketRetransmitted( pending.sequence_number, serialized_packet.sequence_number); } sent_packet_manager_.OnRetransmittedPacket( - pending.sequence_number, serialized_packet.sequence_number); + pending.sequence_number, + serialized_packet.sequence_number); SendOrQueuePacket(pending.retransmittable_frames.encryption_level(), serialized_packet, @@ -1038,6 +1272,16 @@ void QuicConnection::RetransmitUnackedPackets( WriteIfNotBlocked(); } +void QuicConnection::NeuterUnencryptedPackets() { + sent_packet_manager_.NeuterUnencryptedPackets(); + // This may have changed the retransmission timer, so re-arm it. + retransmission_alarm_->Cancel(); + QuicTime retransmission_time = sent_packet_manager_.GetRetransmissionTime(); + if (retransmission_time != QuicTime::Zero()) { + retransmission_alarm_->Set(retransmission_time); + } +} + bool QuicConnection::ShouldGeneratePacket( TransmissionType transmission_type, HasRetransmittableData retransmittable, @@ -1048,35 +1292,25 @@ bool QuicConnection::ShouldGeneratePacket( return true; } - return CanWrite(transmission_type, retransmittable, handshake); + return CanWrite(retransmittable); } -bool QuicConnection::CanWrite(TransmissionType transmission_type, - HasRetransmittableData retransmittable, - IsHandshake handshake) { - if (write_blocked_) { - return false; - } - - // TODO(rch): consider removing this check so that if an ACK comes in - // before the alarm goes it, we might be able send out a packet. - // This check assumes that if the send alarm is set, it applies equally to all - // types of transmissions. - if (send_alarm_->IsSet()) { - DVLOG(1) << "Send alarm set. Not sending."; +bool QuicConnection::CanWrite(HasRetransmittableData retransmittable) { + if (writer_->IsWriteBlocked()) { + visitor_->OnWriteBlocked(); return false; } + send_alarm_->Cancel(); QuicTime now = clock_->Now(); QuicTime::Delta delay = sent_packet_manager_.TimeUntilSend( - now, transmission_type, retransmittable, handshake); + now, retransmittable); if (delay.IsInfinite()) { return false; } // If the scheduler requires a delay, then we can not send this packet now. if (!delay.IsZero()) { - send_alarm_->Cancel(); send_alarm_->Set(now.Add(delay)); DVLOG(1) << "Delaying sending."; return false; @@ -1084,122 +1318,103 @@ bool QuicConnection::CanWrite(TransmissionType transmission_type, return true; } -void QuicConnection::SetupRetransmissionAlarm( - QuicPacketSequenceNumber sequence_number) { - if (!sent_packet_manager_.HasRetransmittableFrames(sequence_number)) { - DVLOG(1) << ENDPOINT << "Will not retransmit packet " << sequence_number; - return; - } - - // Do not set the retransmission alarm if we're already handling one, since - // it will be reset when OnRetransmissionTimeout completes. - if (retransmission_alarm_->IsSet()) { - return; - } - - QuicTime::Delta retransmission_delay = - sent_packet_manager_.GetRetransmissionDelay(); - retransmission_alarm_->Set( - clock_->ApproximateNow().Add(retransmission_delay)); -} - -bool QuicConnection::WritePacket(EncryptionLevel level, - QuicPacketSequenceNumber sequence_number, - QuicPacket* packet, - TransmissionType transmission_type, - HasRetransmittableData retransmittable, - IsHandshake handshake, - Force forced) { - if (ShouldDiscardPacket(level, sequence_number, retransmittable)) { - delete packet; +bool QuicConnection::WritePacket(QueuedPacket packet) { + QuicPacketSequenceNumber sequence_number = packet.sequence_number; + if (ShouldDiscardPacket(packet.encryption_level, + sequence_number, + packet.retransmittable)) { + ++stats_.packets_discarded; return true; } - // If we're write blocked, we know we can't write. - if (write_blocked_) { - return false; - } - - // If we are not forced and we can't write, then simply return false; - if (forced == NO_FORCE && - !CanWrite(transmission_type, retransmittable, handshake)) { + // If the packet is CONNECTION_CLOSE, we need to try to send it immediately + // and encrypt it to hand it off to TimeWaitListManager. + // If the packet is QUEUED, we don't re-consult the congestion control. + // This ensures packets are sent in sequence number order. + // TODO(ianswett): The congestion control should have been consulted before + // serializing the packet, so this could be turned into a LOG_IF(DFATAL). + if (packet.type == NORMAL && !CanWrite(packet.retransmittable)) { return false; } // Some encryption algorithms require the packet sequence numbers not be // repeated. - DCHECK_LE(sequence_number_of_last_inorder_packet_, sequence_number); - // Only increase this when packets have not been queued. Once they're queued - // due to a write block, there is the chance of sending forced and other - // higher priority packets out of order. - if (queued_packets_.empty()) { - sequence_number_of_last_inorder_packet_ = sequence_number; - } + DCHECK_LE(sequence_number_of_last_sent_packet_, sequence_number); + sequence_number_of_last_sent_packet_ = sequence_number; - scoped_ptr<QuicEncryptedPacket> encrypted( - framer_.EncryptPacket(level, sequence_number, *packet)); - if (encrypted.get() == NULL) { + QuicEncryptedPacket* encrypted = framer_.EncryptPacket( + packet.encryption_level, sequence_number, *packet.packet); + if (encrypted == NULL) { LOG(DFATAL) << ENDPOINT << "Failed to encrypt packet number " << sequence_number; + // CloseConnection does not send close packet, so no infinite loop here. CloseConnection(QUIC_ENCRYPTION_FAILURE, false); return false; } - // If it's the ConnectionClose packet, the only FORCED frame type, - // clone a copy for resending later by the TimeWaitListManager. - if (forced == FORCE) { + // Connection close packets are eventually owned by TimeWaitListManager. + // Others are deleted at the end of this call. + scoped_ptr<QuicEncryptedPacket> encrypted_deleter; + if (packet.type == CONNECTION_CLOSE) { DCHECK(connection_close_packet_.get() == NULL); - connection_close_packet_.reset(encrypted->Clone()); - } - - if (encrypted->length() > options()->max_packet_length) { - LOG(DFATAL) << "Writing an encrypted packet larger than max_packet_length:" - << options()->max_packet_length << " encrypted length: " - << encrypted->length(); - } - DVLOG(1) << ENDPOINT << "Sending packet number " << sequence_number - << " : " << (packet->is_fec_packet() ? "FEC " : - (retransmittable == HAS_RETRANSMITTABLE_DATA - ? "data bearing " : " ack only ")) - << ", encryption level: " - << QuicUtils::EncryptionLevelToString(level) - << ", length:" << packet->length() << ", encrypted length:" - << encrypted->length(); + connection_close_packet_.reset(encrypted); + // This assures we won't try to write *forced* packets when blocked. + // Return true to stop processing. + if (writer_->IsWriteBlocked()) { + visitor_->OnWriteBlocked(); + return true; + } + } else { + encrypted_deleter.reset(encrypted); + } + + LOG_IF(DFATAL, encrypted->length() > + packet_generator_.max_packet_length()) + << "Writing an encrypted packet larger than max_packet_length:" + << packet_generator_.max_packet_length() << " encrypted length: " + << encrypted->length(); + DVLOG(1) << ENDPOINT << "Sending packet " << sequence_number + << " : " << (packet.packet->is_fec_packet() ? "FEC " : + (packet.retransmittable == HAS_RETRANSMITTABLE_DATA + ? "data bearing " : " ack only ")) + << ", encryption level: " + << QuicUtils::EncryptionLevelToString(packet.encryption_level) + << ", length:" << packet.packet->length() << ", encrypted length:" + << encrypted->length(); DVLOG(2) << ENDPOINT << "packet(" << sequence_number << "): " << std::endl - << QuicUtils::StringToHexASCIIDump(packet->AsStringPiece()); + << QuicUtils::StringToHexASCIIDump(packet.packet->AsStringPiece()); - DCHECK(encrypted->length() <= kMaxPacketSize) + DCHECK(encrypted->length() <= kMaxPacketSize || + FLAGS_quic_allow_oversized_packets_for_test) << "Packet " << sequence_number << " will not be read; too large: " - << packet->length() << " " << encrypted->length() << " " - << " forced: " << (forced == FORCE ? "yes" : "no"); + << packet.packet->length() << " " << encrypted->length() << " " + << " close: " << (packet.type == CONNECTION_CLOSE ? "yes" : "no"); DCHECK(pending_write_.get() == NULL); - pending_write_.reset(new PendingWrite(sequence_number, transmission_type, - retransmittable, level, - packet->is_fec_packet(), - packet->length())); - - WriteResult result = - writer_->WritePacket(encrypted->data(), encrypted->length(), - self_address().address(), peer_address(), this); + pending_write_.reset(new QueuedPacket(packet)); + + WriteResult result = writer_->WritePacket(encrypted->data(), + encrypted->length(), + self_address().address(), + peer_address()); if (result.error_code == ERR_IO_PENDING) { DCHECK_EQ(WRITE_STATUS_BLOCKED, result.status); } if (debug_visitor_) { // Pass the write result to the visitor. - debug_visitor_->OnPacketSent(sequence_number, level, *encrypted, result); + debug_visitor_->OnPacketSent(sequence_number, + packet.encryption_level, + packet.transmission_type, + *encrypted, + result); } if (result.status == WRITE_STATUS_BLOCKED) { - // TODO(satyashekhar): It might be more efficient (fewer system calls), if - // all connections share this variable i.e this becomes a part of - // PacketWriterInterface. - write_blocked_ = true; + visitor_->OnWriteBlocked(); // If the socket buffers the the data, then the packet should not // be queued and sent again, which would result in an unnecessary // duplicate packet being sent. The helper must call OnPacketSent // when the packet is actually sent. if (writer_->IsWriteBlockedDataBuffered()) { - delete packet; return true; } pending_write_.reset(); @@ -1207,7 +1422,6 @@ bool QuicConnection::WritePacket(EncryptionLevel level, } if (OnPacketSent(result)) { - delete packet; return true; } return false; @@ -1219,7 +1433,15 @@ bool QuicConnection::ShouldDiscardPacket( HasRetransmittableData retransmittable) { if (!connected_) { DVLOG(1) << ENDPOINT - << "Not sending packet as connection is disconnected."; + << "Not sending packet as connection is disconnected."; + return true; + } + + // If the packet has been discarded before sending, don't send it. + // This occurs if a packet gets serialized, queued, then discarded. + if (!sent_packet_manager_.IsUnacked(sequence_number)) { + DVLOG(1) << ENDPOINT << "Dropping packet before sending: " + << sequence_number << " since it has already been discarded."; return true; } @@ -1227,43 +1449,20 @@ bool QuicConnection::ShouldDiscardPacket( level == ENCRYPTION_NONE) { // Drop packets that are NULL encrypted since the peer won't accept them // anymore. - DVLOG(1) << ENDPOINT << "Dropping packet: " << sequence_number - << " since the packet is NULL encrypted."; - sent_packet_manager_.DiscardUnackedPacket(sequence_number); + DVLOG(1) << ENDPOINT << "Dropping NULL encrypted packet: " + << sequence_number << " since the connection is forward secure."; + LOG_IF(DFATAL, + sent_packet_manager_.HasRetransmittableFrames(sequence_number)) + << "Once forward secure, all NULL encrypted packets should be " + << "neutered."; return true; } - if (retransmittable == HAS_RETRANSMITTABLE_DATA) { - if (!sent_packet_manager_.IsUnacked(sequence_number)) { - // This is a crazy edge case, but if we retransmit a packet, - // (but have to queue it for some reason) then receive an ack - // for the previous transmission (but not the retransmission) - // then receive a truncated ACK which causes us to raise the - // high water mark, all before we're able to send the packet - // then we can simply drop it. - DVLOG(1) << ENDPOINT << "Dropping packet: " << sequence_number - << " since it has already been acked."; - return true; - } - - if (sent_packet_manager_.IsPreviousTransmission(sequence_number)) { - // If somehow we have already retransmitted this packet *before* - // we actually send it for the first time (I think this is probably - // impossible in the real world), then don't bother sending it. - // We don't want to call DiscardUnackedPacket because in this case - // the peer has not yet ACK'd the data. We need the subsequent - // retransmission to be sent. - DVLOG(1) << ENDPOINT << "Dropping packet: " << sequence_number - << " since it has already been retransmitted."; - return true; - } - - if (!sent_packet_manager_.HasRetransmittableFrames(sequence_number)) { - DVLOG(1) << ENDPOINT << "Dropping packet: " << sequence_number - << " since a previous transmission has been acked."; - sent_packet_manager_.DiscardUnackedPacket(sequence_number); - return true; - } + if (retransmittable == HAS_RETRANSMITTABLE_DATA && + !sent_packet_manager_.HasRetransmittableFrames(sequence_number)) { + DVLOG(1) << ENDPOINT << "Dropping unacked packet: " << sequence_number + << " A previous transmission was acked while write blocked."; + return true; } return false; @@ -1279,12 +1478,12 @@ bool QuicConnection::OnPacketSent(WriteResult result) { QuicPacketSequenceNumber sequence_number = pending_write_->sequence_number; TransmissionType transmission_type = pending_write_->transmission_type; HasRetransmittableData retransmittable = pending_write_->retransmittable; - bool is_fec_packet = pending_write_->is_fec_packet; size_t length = pending_write_->length; pending_write_.reset(); if (result.status == WRITE_STATUS_ERROR) { - DVLOG(1) << "Write failed with error code: " << result.error_code; + DVLOG(1) << "Write failed with error: " << result.error_code << " (" + << ErrorToString(result.error_code) << ")"; // We can't send an error as the socket is presumably borked. CloseConnection(QUIC_PACKET_WRITE_ERROR, false); return false; @@ -1292,33 +1491,34 @@ bool QuicConnection::OnPacketSent(WriteResult result) { QuicTime now = clock_->Now(); if (transmission_type == NOT_RETRANSMISSION) { - time_of_last_sent_packet_ = now; + time_of_last_sent_new_packet_ = now; } + SetPingAlarm(); DVLOG(1) << ENDPOINT << "time of last sent packet: " << now.ToDebuggingValue(); - // 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. - if (retransmittable == HAS_RETRANSMITTABLE_DATA || is_fec_packet) { - SetupRetransmissionAlarm(sequence_number); - } - // TODO(ianswett): Change the sequence number length and other packet creator // options by a more explicit API than setting a struct value directly. - packet_creator_.UpdateSequenceNumberLength( + packet_generator_.UpdateSequenceNumberLength( received_packet_manager_.least_packet_awaited_by_peer(), - sent_packet_manager_.BandwidthEstimate().ToBytesPerPeriod( - sent_packet_manager_.SmoothedRtt())); + sent_packet_manager_.GetCongestionWindow()); + + bool reset_retransmission_alarm = + sent_packet_manager_.OnPacketSent(sequence_number, now, length, + transmission_type, retransmittable); - sent_packet_manager_.OnPacketSent(sequence_number, now, length, - transmission_type, retransmittable); + if (reset_retransmission_alarm || !retransmission_alarm_->IsSet()) { + retransmission_alarm_->Cancel(); + QuicTime retransmission_time = sent_packet_manager_.GetRetransmissionTime(); + if (retransmission_time != QuicTime::Zero()) { + retransmission_alarm_->Set(retransmission_time); + } + } stats_.bytes_sent += result.bytes_written; ++stats_.packets_sent; - if (transmission_type == NACK_RETRANSMISSION || - transmission_type == RTO_RETRANSMISSION) { + if (transmission_type != NOT_RETRANSMISSION) { stats_.bytes_retransmitted += result.bytes_written; ++stats_.packets_retransmitted; } @@ -1340,39 +1540,59 @@ bool QuicConnection::OnSerializedPacket( NOT_RETRANSMISSION); } -QuicPacketSequenceNumber QuicConnection::GetNextPacketSequenceNumber() { - return packet_creator_.sequence_number() + 1; -} - bool QuicConnection::SendOrQueuePacket(EncryptionLevel level, const SerializedPacket& packet, TransmissionType transmission_type) { - IsHandshake handshake = HasCryptoHandshake(packet.retransmittable_frames); - Force forced = HasForcedFrames(packet.retransmittable_frames); - HasRetransmittableData retransmittable = - (transmission_type != NOT_RETRANSMISSION || - packet.retransmittable_frames != NULL) ? - HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA; + if (packet.packet == NULL) { + LOG(DFATAL) << "NULL packet passed in to SendOrQueuePacket"; + return true; + } + sent_entropy_manager_.RecordPacketEntropyHash(packet.sequence_number, packet.entropy_hash); - if (WritePacket(level, packet.sequence_number, packet.packet, - transmission_type, retransmittable, handshake, forced)) { + QueuedPacket queued_packet(packet, level, transmission_type); + // If there are already queued packets, put this at the end, + // unless it's ConnectionClose, in which case it is written immediately. + if ((queued_packet.type == CONNECTION_CLOSE || queued_packets_.empty()) && + WritePacket(queued_packet)) { + delete packet.packet; return true; } - queued_packets_.push_back(QueuedPacket(packet.sequence_number, packet.packet, - level, transmission_type, - retransmittable, handshake, forced)); + queued_packet.type = QUEUED; + queued_packets_.push_back(queued_packet); return false; } -void QuicConnection::UpdateSentPacketInfo(SentPacketInfo* sent_info) { - sent_info->least_unacked = sent_packet_manager_.GetLeastUnackedSentPacket(); - sent_info->entropy_hash = sent_entropy_manager_.EntropyHash( - sent_info->least_unacked - 1); +void QuicConnection::UpdateStopWaiting(QuicStopWaitingFrame* stop_waiting) { + stop_waiting->least_unacked = GetLeastUnacked(); + stop_waiting->entropy_hash = sent_entropy_manager_.EntropyHash( + stop_waiting->least_unacked - 1); +} + +void QuicConnection::SendPing() { + if (retransmission_alarm_->IsSet()) { + return; + } + if (version() <= QUIC_VERSION_17) { + // TODO(rch): remove this when we remove version 17. + // This is a horrible hideous hack which we should not support. + IOVector data; + char c_data[] = "C"; + data.Append(c_data, 1); + QuicConsumedData consumed_data = + packet_generator_.ConsumeData(kCryptoStreamId, data, 0, false, + MAY_FEC_PROTECT, NULL); + if (consumed_data.bytes_consumed == 0) { + DLOG(ERROR) << "Unable to send ping!?"; + } + } else { + packet_generator_.AddControlFrame(QuicFrame(new QuicPingFrame)); + } } void QuicConnection::SendAck() { ack_alarm_->Cancel(); + stop_waiting_count_ = 0; // TODO(rch): delay this until the CreateFeedbackFrame // method is invoked. This requires changes SetShouldSendAck // to be a no-arg method, and re-jiggering its implementation. @@ -1384,7 +1604,8 @@ void QuicConnection::SendAck() { send_feedback = true; } - packet_generator_.SetShouldSendAck(send_feedback); + packet_generator_.SetShouldSendAck(send_feedback, + version() > QUIC_VERSION_15); } void QuicConnection::OnRetransmissionTimeout() { @@ -1392,18 +1613,22 @@ void QuicConnection::OnRetransmissionTimeout() { return; } - ++stats_.rto_count; - sent_packet_manager_.OnRetransmissionTimeout(); - WriteIfNotBlocked(); + // In the TLP case, the SentPacketManager gives the connection the opportunity + // to send new data before retransmitting. + if (sent_packet_manager_.MaybeRetransmitTailLossProbe()) { + // Send the pending retransmission now that it's been queued. + WriteIfNotBlocked(); + } - // Ensure the retransmission alarm is always set if there are unacked packets. - if (sent_packet_manager_.HasUnackedPackets() && !HasQueuedData() && - !retransmission_alarm_->IsSet()) { - QuicTime rto_timeout = clock_->ApproximateNow().Add( - sent_packet_manager_.GetRetransmissionDelay()); - retransmission_alarm_->Set(rto_timeout); + // Ensure the retransmission alarm is always set if there are unacked packets + // and nothing waiting to be sent. + if (!HasQueuedData() && !retransmission_alarm_->IsSet()) { + QuicTime rto_timeout = sent_packet_manager_.GetRetransmissionTime(); + if (rto_timeout != QuicTime::Zero()) { + retransmission_alarm_->Set(rto_timeout); + } } } @@ -1416,18 +1641,20 @@ const QuicEncrypter* QuicConnection::encrypter(EncryptionLevel level) const { return framer_.encrypter(level); } -void QuicConnection::SetDefaultEncryptionLevel( - EncryptionLevel level) { +void QuicConnection::SetDefaultEncryptionLevel(EncryptionLevel level) { encryption_level_ = level; + packet_generator_.set_encryption_level(level); } -void QuicConnection::SetDecrypter(QuicDecrypter* decrypter) { - framer_.SetDecrypter(decrypter); +void QuicConnection::SetDecrypter(QuicDecrypter* decrypter, + EncryptionLevel level) { + framer_.SetDecrypter(decrypter, level); } void QuicConnection::SetAlternativeDecrypter(QuicDecrypter* decrypter, + EncryptionLevel level, bool latch_once_used) { - framer_.SetAlternativeDecrypter(decrypter, latch_once_used); + framer_.SetAlternativeDecrypter(decrypter, level, latch_once_used); } const QuicDecrypter* QuicConnection::decrypter() const { @@ -1445,8 +1672,7 @@ void QuicConnection::QueueUndecryptablePacket( } void QuicConnection::MaybeProcessUndecryptablePackets() { - if (undecryptable_packets_.empty() || - encryption_level_ == ENCRYPTION_NONE) { + if (undecryptable_packets_.empty() || encryption_level_ == ENCRYPTION_NONE) { return; } @@ -1459,6 +1685,7 @@ void QuicConnection::MaybeProcessUndecryptablePackets() { break; } DVLOG(1) << ENDPOINT << "Processed undecryptable packet!"; + ++stats_.packets_processed; delete packet; undecryptable_packets_.pop_front(); } @@ -1479,13 +1706,19 @@ void QuicConnection::MaybeProcessRevivedPacket() { QuicPacketHeader revived_header; char revived_payload[kMaxPacketSize]; size_t len = group->Revive(&revived_header, revived_payload, kMaxPacketSize); - revived_header.public_header.guid = guid_; + revived_header.public_header.connection_id = connection_id_; + revived_header.public_header.connection_id_length = + last_header_.public_header.connection_id_length; revived_header.public_header.version_flag = false; revived_header.public_header.reset_flag = false; + revived_header.public_header.sequence_number_length = + last_header_.public_header.sequence_number_length; revived_header.fec_flag = false; revived_header.is_in_fec_group = NOT_IN_FEC_GROUP; revived_header.fec_group = 0; group_map_.erase(last_header_.fec_group); + last_decrypted_packet_level_ = group->effective_encryption_level(); + DCHECK_LT(last_decrypted_packet_level_, NUM_ENCRYPTION_LEVELS); delete group; last_packet_revived_ = true; @@ -1526,27 +1759,36 @@ void QuicConnection::SendConnectionClose(QuicErrorCode error) { void QuicConnection::SendConnectionCloseWithDetails(QuicErrorCode error, const string& details) { - if (!write_blocked_) { - SendConnectionClosePacket(error, details); + // If we're write blocked, WritePacket() will not send, but will capture the + // serialized packet. + SendConnectionClosePacket(error, details); + if (connected_) { + // It's possible that while sending the connection close packet, we get a + // socket error and disconnect right then and there. Avoid a double + // disconnect in that case. + CloseConnection(error, false); } - CloseConnection(error, false); } void QuicConnection::SendConnectionClosePacket(QuicErrorCode error, const string& details) { - DVLOG(1) << ENDPOINT << "Force closing " << guid() << " with error " - << QuicUtils::ErrorToString(error) << " (" << error << ") " - << details; - ScopedPacketBundler ack_bundler(this, true); + DVLOG(1) << ENDPOINT << "Force closing " << connection_id() + << " with error " << QuicUtils::ErrorToString(error) + << " (" << error << ") " << details; + ScopedPacketBundler ack_bundler(this, SEND_ACK); QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame(); frame->error_code = error; frame->error_details = details; packet_generator_.AddControlFrame(QuicFrame(frame)); - Flush(); + packet_generator_.FlushAllQueuedFrames(); } void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) { - DCHECK(connected_); + if (!connected_) { + DLOG(DFATAL) << "Error: attempt to close an already closed connection" + << base::debug::StackTrace().ToString(); + return; + } connected_ = false; visitor_->OnConnectionClosed(error, from_peer); // Cancel the alarms so they don't trigger any action now that the @@ -1562,11 +1804,11 @@ void QuicConnection::SendGoAway(QuicErrorCode error, QuicStreamId last_good_stream_id, const string& reason) { DVLOG(1) << ENDPOINT << "Going away with error " - << QuicUtils::ErrorToString(error) - << " (" << error << ")"; + << QuicUtils::ErrorToString(error) + << " (" << error << ")"; // Opportunistically bundle an ack with this outgoing packet. - ScopedPacketBundler ack_bundler(this, true); + ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK); packet_generator_.AddControlFrame( QuicFrame(new QuicGoAwayFrame(error, last_good_stream_id, reason))); } @@ -1592,8 +1834,12 @@ void QuicConnection::CloseFecGroupsBefore( } } -void QuicConnection::Flush() { - packet_generator_.FlushAllQueuedFrames(); +size_t QuicConnection::max_packet_length() const { + return packet_generator_.max_packet_length(); +} + +void QuicConnection::set_max_packet_length(size_t length) { + return packet_generator_.set_max_packet_length(length); } bool QuicConnection::HasQueuedData() const { @@ -1601,6 +1847,23 @@ bool QuicConnection::HasQueuedData() const { !queued_packets_.empty() || packet_generator_.HasQueuedFrames(); } +bool QuicConnection::CanWriteStreamData() { + // Don't write stream data if there are negotiation or queued data packets + // to send. Otherwise, continue and bundle as many frames as possible. + if (pending_version_negotiation_packet_ || !queued_packets_.empty()) { + return false; + } + + IsHandshake pending_handshake = visitor_->HasPendingHandshake() ? + IS_HANDSHAKE : NOT_HANDSHAKE; + // Sending queued packets may have caused the socket to become write blocked, + // or the congestion manager to prohibit sending. If we've sent everything + // we had queued and we're still not blocked, let the visitor know it can + // write more. + return ShouldGeneratePacket(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, + pending_handshake); +} + void QuicConnection::SetIdleNetworkTimeout(QuicTime::Delta timeout) { if (timeout < idle_network_timeout_) { idle_network_timeout_ = timeout; @@ -1621,8 +1884,8 @@ void QuicConnection::SetOverallConnectionTimeout(QuicTime::Delta timeout) { bool QuicConnection::CheckForTimeout() { QuicTime now = clock_->ApproximateNow(); - QuicTime time_of_last_packet = std::max(time_of_last_received_packet_, - time_of_last_sent_packet_); + QuicTime time_of_last_packet = max(time_of_last_received_packet_, + time_of_last_sent_new_packet_); // |delta| can be < 0 as |now| is approximate time but |time_of_last_packet| // is accurate time. However, this should not change the behavior of @@ -1643,7 +1906,8 @@ bool QuicConnection::CheckForTimeout() { QuicTime::Delta timeout = idle_network_timeout_.Subtract(delta); if (!overall_connection_timeout_.IsInfinite()) { - QuicTime::Delta connected_time = now.Subtract(creation_time_); + QuicTime::Delta connected_time = + now.Subtract(stats_.connection_creation_time); DVLOG(1) << ENDPOINT << "connection time: " << connected_time.ToMilliseconds() << " overall timeout: " << overall_connection_timeout_.ToMilliseconds(); @@ -1667,24 +1931,49 @@ bool QuicConnection::CheckForTimeout() { return false; } +void QuicConnection::SetPingAlarm() { + if (is_server_) { + // Only clients send pings. + return; + } + ping_alarm_->Cancel(); + if (!visitor_->HasOpenDataStreams()) { + // Don't send a ping unless there are open streams. + return; + } + QuicTime::Delta ping_timeout = QuicTime::Delta::FromSeconds(kPingTimeoutSecs); + ping_alarm_->Set(clock_->ApproximateNow().Add(ping_timeout)); +} + QuicConnection::ScopedPacketBundler::ScopedPacketBundler( QuicConnection* connection, - bool include_ack) + AckBundling send_ack) : connection_(connection), - already_in_batch_mode_(connection->packet_generator_.InBatchMode()) { + already_in_batch_mode_(connection != NULL && + connection->packet_generator_.InBatchMode()) { + if (connection_ == NULL) { + return; + } // Move generator into batch mode. If caller wants us to include an ack, // check the delayed-ack timer to see if there's ack info to be sent. if (!already_in_batch_mode_) { DVLOG(1) << "Entering Batch Mode."; connection_->packet_generator_.StartBatchOperations(); } - if (include_ack && connection_->ack_alarm_->IsSet()) { + // Bundle an ack if the alarm is set or with every second packet if we need to + // raise the peer's least unacked. + bool ack_pending = + connection_->ack_alarm_->IsSet() || connection_->stop_waiting_count_ > 1; + if (send_ack == SEND_ACK || (send_ack == BUNDLE_PENDING_ACK && ack_pending)) { DVLOG(1) << "Bundling ack with outgoing packet."; connection_->SendAck(); } } QuicConnection::ScopedPacketBundler::~ScopedPacketBundler() { + if (connection_ == NULL) { + return; + } // If we changed the generator's batch state, restore original batch state. if (!already_in_batch_mode_) { DVLOG(1) << "Leaving Batch Mode."; diff --git a/chromium/net/quic/quic_connection.h b/chromium/net/quic/quic_connection.h index beb8985d4d1..fec6c6882e5 100644 --- a/chromium/net/quic/quic_connection.h +++ b/chromium/net/quic/quic_connection.h @@ -40,9 +40,7 @@ #include "net/quic/quic_received_packet_manager.h" #include "net/quic/quic_sent_entropy_manager.h" #include "net/quic/quic_sent_packet_manager.h" - -NET_EXPORT_PRIVATE extern int FLAGS_fake_packet_loss_percentage; -NET_EXPORT_PRIVATE extern bool FLAGS_bundle_ack_with_outgoing_packet; +#include "net/quic/quic_types.h" namespace net { @@ -64,11 +62,18 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface { public: virtual ~QuicConnectionVisitorInterface() {} - // A simple visitor interface for dealing with data frames. The session - // should determine if all frames will be accepted, and return true if so. - // If any frames can't be processed or buffered, none of the data should - // be used, and the callee should return false. - virtual bool OnStreamFrames(const std::vector<QuicStreamFrame>& frames) = 0; + // A simple visitor interface for dealing with data frames. + virtual void OnStreamFrames(const std::vector<QuicStreamFrame>& frames) = 0; + + // The session should process all WINDOW_UPDATE frames, adjusting both stream + // and connection level flow control windows. + virtual void OnWindowUpdateFrames( + const std::vector<QuicWindowUpdateFrame>& frames) = 0; + + // BLOCKED frames tell us that the peer believes it is flow control blocked on + // a specified stream. If the session at this end disagrees, something has + // gone wrong with our flow control accounting. + virtual void OnBlockedFrames(const std::vector<QuicBlockedFrame>& frames) = 0; // Called when the stream is reset by the peer. virtual void OnRstStream(const QuicRstStreamFrame& frame) = 0; @@ -78,85 +83,110 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface { // Called when the connection is closed either locally by the framer, or // remotely by the peer. - virtual void OnConnectionClosed(QuicErrorCode error, - bool from_peer) = 0; + virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) = 0; + + // Called when the connection failed to write because the socket was blocked. + virtual void OnWriteBlocked() = 0; // Called once a specific QUIC version is agreed by both endpoints. virtual void OnSuccessfulVersionNegotiation(const QuicVersion& version) = 0; - // Indicates a new QuicConfig has been negotiated. - virtual void OnConfigNegotiated() = 0; + // Called when a blocked socket becomes writable. + virtual void OnCanWrite() = 0; - // Called when a blocked socket becomes writable. If all pending bytes for - // this visitor are consumed by the connection successfully this should - // return true, otherwise it should return false. - virtual bool OnCanWrite() = 0; + // Called to ask if the visitor wants to schedule write resumption as it both + // has pending data to write, and is able to write (e.g. based on flow control + // limits). + // Writes may be pending because they were write-blocked, congestion-throttled + // or yielded to other connections. + virtual bool WillingAndAbleToWrite() const = 0; // Called to ask if any handshake messages are pending in this visitor. virtual bool HasPendingHandshake() const = 0; + + // Called to ask if any streams are open in this visitor, excluding the + // reserved crypto and headers stream. + virtual bool HasOpenDataStreams() const = 0; }; // Interface which gets callbacks from the QuicConnection at interesting // points. Implementations must not mutate the state of the connection // as a result of these callbacks. -class NET_EXPORT_PRIVATE QuicConnectionDebugVisitorInterface - : public QuicPacketGenerator::DebugDelegateInterface { +class NET_EXPORT_PRIVATE QuicConnectionDebugVisitor + : public QuicPacketGenerator::DebugDelegate, + public QuicSentPacketManager::DebugDelegate { public: - virtual ~QuicConnectionDebugVisitorInterface() {} + virtual ~QuicConnectionDebugVisitor() {} // Called when a packet has been sent. virtual void OnPacketSent(QuicPacketSequenceNumber sequence_number, EncryptionLevel level, + TransmissionType transmission_type, const QuicEncryptedPacket& packet, - WriteResult result) = 0; + WriteResult result) {} // Called when the contents of a packet have been retransmitted as // a new packet. virtual void OnPacketRetransmitted( QuicPacketSequenceNumber old_sequence_number, - QuicPacketSequenceNumber new_sequence_number) = 0; + QuicPacketSequenceNumber new_sequence_number) {} // Called when a packet has been received, but before it is // validated or parsed. virtual void OnPacketReceived(const IPEndPoint& self_address, const IPEndPoint& peer_address, - const QuicEncryptedPacket& packet) = 0; + const QuicEncryptedPacket& packet) {} // Called when the protocol version on the received packet doensn't match // current protocol version of the connection. - virtual void OnProtocolVersionMismatch(QuicVersion version) = 0; + virtual void OnProtocolVersionMismatch(QuicVersion version) {} // Called when the complete header of a packet has been parsed. - virtual void OnPacketHeader(const QuicPacketHeader& header) = 0; + virtual void OnPacketHeader(const QuicPacketHeader& header) {} // Called when a StreamFrame has been parsed. - virtual void OnStreamFrame(const QuicStreamFrame& frame) = 0; + virtual void OnStreamFrame(const QuicStreamFrame& frame) {} // Called when a AckFrame has been parsed. - virtual void OnAckFrame(const QuicAckFrame& frame) = 0; + virtual void OnAckFrame(const QuicAckFrame& frame) {} // Called when a CongestionFeedbackFrame has been parsed. virtual void OnCongestionFeedbackFrame( - const QuicCongestionFeedbackFrame& frame) = 0; + const QuicCongestionFeedbackFrame& frame) {} + + // Called when a StopWaitingFrame has been parsed. + virtual void OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {} + + // Called when a Ping has been parsed. + virtual void OnPingFrame(const QuicPingFrame& frame) {} + + // Called when a GoAway has been parsed. + virtual void OnGoAwayFrame(const QuicGoAwayFrame& frame) {} // Called when a RstStreamFrame has been parsed. - virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) = 0; + virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) {} // Called when a ConnectionCloseFrame has been parsed. virtual void OnConnectionCloseFrame( - const QuicConnectionCloseFrame& frame) = 0; + const QuicConnectionCloseFrame& frame) {} + + // Called when a WindowUpdate has been parsed. + virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {} + + // Called when a BlockedFrame has been parsed. + virtual void OnBlockedFrame(const QuicBlockedFrame& frame) {} // Called when a public reset packet has been received. - virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) = 0; + virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) {} // Called when a version negotiation packet has been received. virtual void OnVersionNegotiationPacket( - const QuicVersionNegotiationPacket& packet) = 0; + const QuicVersionNegotiationPacket& packet) {} // Called after a packet has been successfully parsed which results // in the revival of a packet via FEC. virtual void OnRevivedPacket(const QuicPacketHeader& revived_header, - base::StringPiece payload) = 0; + base::StringPiece payload) {} }; class NET_EXPORT_PRIVATE QuicConnectionHelperInterface { @@ -178,17 +208,23 @@ class NET_EXPORT_PRIVATE QuicConnectionHelperInterface { class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface, public QuicBlockedWriterInterface, - public QuicPacketGenerator::DelegateInterface, - public QuicSentPacketManager::HelperInterface { + public QuicPacketGenerator::DelegateInterface { public: - enum Force { - NO_FORCE, - FORCE + enum PacketType { + NORMAL, + QUEUED, + CONNECTION_CLOSE }; - // Constructs a new QuicConnection for the specified |guid| and |address|. + enum AckBundling { + NO_ACK = 0, + SEND_ACK = 1, + BUNDLE_PENDING_ACK = 2, + }; + + // Constructs a new QuicConnection for |connection_id| and |address|. // |helper| and |writer| must outlive this connection. - QuicConnection(QuicGuid guid, + QuicConnection(QuicConnectionId connection_id, IPEndPoint address, QuicConnectionHelperInterface* helper, QuicPacketWriter* writer, @@ -203,7 +239,10 @@ class NET_EXPORT_PRIVATE QuicConnection // Returns a pair with the number of bytes consumed from data, and a boolean // indicating if the fin bit was consumed. This does not indicate the data // has been sent on the wire: it may have been turned into a packet and queued - // if the socket was unexpectedly blocked. + // if the socket was unexpectedly blocked. |fec_protection| indicates if + // data is to be FEC protected. Note that data that is sent immediately + // following MUST_FEC_PROTECT data may get protected by falling within the + // same FEC group. // If |delegate| is provided, then it will be informed once ACKs have been // received for all the packets written in this call. // The |delegate| is not owned by the QuicConnection and must outlive it. @@ -211,11 +250,20 @@ class NET_EXPORT_PRIVATE QuicConnection const IOVector& data, QuicStreamOffset offset, bool fin, + FecProtection fec_protection, QuicAckNotifier::DelegateInterface* delegate); - // Send a stream reset frame to the peer. + // Send a RST_STREAM frame to the peer. virtual void SendRstStream(QuicStreamId id, - QuicRstStreamErrorCode error); + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written); + + // Send a BLOCKED frame to the peer. + virtual void SendBlocked(QuicStreamId id); + + // Send a WINDOW_UPDATE frame to the peer. + virtual void SendWindowUpdate(QuicStreamId id, + QuicStreamOffset byte_offset); // Sends the connection close packet without affecting the state of the // connection. This should only be called if the session is actively being @@ -246,15 +294,14 @@ class NET_EXPORT_PRIVATE QuicConnection // QuicBlockedWriterInterface // Called when the underlying connection becomes writable to allow queued - // writes to happen. Returns false if the socket has become blocked. - virtual bool OnCanWrite() OVERRIDE; + // writes to happen. + virtual void OnCanWrite() OVERRIDE; // Called when a packet has been finally sent to the network. bool OnPacketSent(WriteResult result); - // If the socket is not blocked, this allows queued writes to happen. Returns - // false if the socket has become blocked. - bool WriteIfNotBlocked(); + // If the socket is not blocked, writes queued packets. + void WriteIfNotBlocked(); // Do any work which logically would be done in OnPacket but can not be // safely done until the packet is validated. Returns true if the packet @@ -278,17 +325,24 @@ class NET_EXPORT_PRIVATE QuicConnection virtual void OnVersionNegotiationPacket( const QuicVersionNegotiationPacket& packet) OVERRIDE; virtual void OnRevivedPacket() OVERRIDE; + virtual bool OnUnauthenticatedPublicHeader( + const QuicPacketPublicHeader& header) OVERRIDE; virtual bool OnUnauthenticatedHeader(const QuicPacketHeader& header) OVERRIDE; + virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE; virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE; virtual void OnFecProtectedPayload(base::StringPiece payload) OVERRIDE; virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE; virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE; virtual bool OnCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& frame) OVERRIDE; + virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) OVERRIDE; + virtual bool OnPingFrame(const QuicPingFrame& frame) OVERRIDE; virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE; virtual bool OnConnectionCloseFrame( const QuicConnectionCloseFrame& frame) OVERRIDE; virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE; + virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) OVERRIDE; + virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE; virtual void OnFecData(const QuicFecData& fec) OVERRIDE; virtual void OnPacketComplete() OVERRIDE; @@ -298,26 +352,25 @@ class NET_EXPORT_PRIVATE QuicConnection IsHandshake handshake) OVERRIDE; virtual QuicAckFrame* CreateAckFrame() OVERRIDE; virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() OVERRIDE; + virtual QuicStopWaitingFrame* CreateStopWaitingFrame() OVERRIDE; virtual bool OnSerializedPacket(const SerializedPacket& packet) OVERRIDE; - // QuicSentPacketManager::HelperInterface - virtual QuicPacketSequenceNumber GetNextPacketSequenceNumber() OVERRIDE; - // Accessors void set_visitor(QuicConnectionVisitorInterface* visitor) { visitor_ = visitor; } - void set_debug_visitor(QuicConnectionDebugVisitorInterface* debug_visitor) { + void set_debug_visitor(QuicConnectionDebugVisitor* debug_visitor) { debug_visitor_ = debug_visitor; packet_generator_.set_debug_delegate(debug_visitor); + sent_packet_manager_.set_debug_delegate(debug_visitor); } const IPEndPoint& self_address() const { return self_address_; } const IPEndPoint& peer_address() const { return peer_address_; } - QuicGuid guid() const { return guid_; } + QuicConnectionId connection_id() const { return connection_id_; } const QuicClock* clock() const { return clock_; } QuicRandom* random_generator() const { return random_generator_; } - - QuicPacketCreator::Options* options() { return packet_creator_.options(); } + size_t max_packet_length() const; + void set_max_packet_length(size_t length); bool connected() const { return connected_; } @@ -336,9 +389,10 @@ class NET_EXPORT_PRIVATE QuicConnection return connection_close_packet_.release(); } - // Flush any queued frames immediately. Preserves the batch write mode and - // does nothing if there are no pending frames. - void Flush(); + // Returns true if the underlying UDP socket is writable, there is + // no queued data and the connection is not congestion-control + // blocked. + bool CanWriteStreamData(); // Returns true if the connection has queued packets or frames. bool HasQueuedData() const; @@ -356,6 +410,9 @@ class NET_EXPORT_PRIVATE QuicConnection // true. Otherwise, it will return false and will reset the timeout alarm. bool CheckForTimeout(); + // Sends a ping, and resets the ping alarm. + void SendPing(); + // Sets up a packet with an QuicAckFrame and sends it out. void SendAck(); @@ -370,6 +427,10 @@ class NET_EXPORT_PRIVATE QuicConnection // initially encrypted packets when the initial encrypter changes. void RetransmitUnackedPackets(RetransmissionType retransmission_type); + // Calls |sent_packet_manager_|'s NeuterUnencryptedPackets. Used when the + // connection becomes forward secure and hasn't received acks for all packets. + void NeuterUnencryptedPackets(); + // Changes the encrypter used for level |level| to |encrypter|. The function // takes ownership of |encrypter|. void SetEncrypter(EncryptionLevel level, QuicEncrypter* encrypter); @@ -383,15 +444,17 @@ class NET_EXPORT_PRIVATE QuicConnection // and takes ownership. If an alternative decrypter is in place then the // function DCHECKs. This is intended for cases where one knows that future // packets will be using the new decrypter and the previous decrypter is now - // obsolete. - void SetDecrypter(QuicDecrypter* decrypter); + // obsolete. |level| indicates the encryption level of the new decrypter. + void SetDecrypter(QuicDecrypter* decrypter, EncryptionLevel level); // SetAlternativeDecrypter sets a decrypter that may be used to decrypt - // future packets and takes ownership of it. If |latch_once_used| is true, - // then the first time that the decrypter is successful it will replace the - // primary decrypter. Otherwise both decrypters will remain active and the - // primary decrypter will be the one last used. + // future packets and takes ownership of it. |level| indicates the encryption + // level of the decrypter. If |latch_once_used| is true, then the first time + // that the decrypter is successful it will replace the primary decrypter. + // Otherwise both decrypters will remain active and the primary decrypter + // will be the one last used. void SetAlternativeDecrypter(QuicDecrypter* decrypter, + EncryptionLevel level, bool latch_once_used); const QuicDecrypter* decrypter() const; @@ -404,16 +467,34 @@ class NET_EXPORT_PRIVATE QuicConnection return sent_packet_manager_; } - bool CanWrite(TransmissionType transmission_type, - HasRetransmittableData retransmittable, - IsHandshake handshake); + bool CanWrite(HasRetransmittableData retransmittable); + + // Stores current batch state for connection, puts the connection + // into batch mode, and destruction restores the stored batch state. + // While the bundler is in scope, any generated frames are bundled + // as densely as possible into packets. In addition, this bundler + // can be configured to ensure that an ACK frame is included in the + // first packet created, if there's new ack information to be sent. + class ScopedPacketBundler { + public: + // In addition to all outgoing frames being bundled when the + // bundler is in scope, setting |include_ack| to true ensures that + // an ACK frame is opportunistically bundled with the first + // outgoing packet. + ScopedPacketBundler(QuicConnection* connection, AckBundling send_ack); + ~ScopedPacketBundler(); + + private: + QuicConnection* connection_; + bool already_in_batch_mode_; + }; protected: // Send a packet to the peer using encryption |level|. 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. Updates the + // Deletes |packet| if WritePacket call succeeds, or transfers ownership to + // QueuedPacket, ultimately deleted in WriteQueuedPackets. Updates the // entropy map corresponding to |sequence_number| using |entropy_hash|. // |transmission_type| and |retransmittable| are supplied to the congestion // manager, and when |forced| is true, it bypasses the congestion manager. @@ -422,25 +503,6 @@ class NET_EXPORT_PRIVATE QuicConnection const SerializedPacket& packet, TransmissionType transmission_type); - // Writes the given packet to socket, encrypted with |level|, 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 FORCE, then the packet will be - // sent immediately and the send scheduler will not be consulted. - bool WritePacket(EncryptionLevel level, - QuicPacketSequenceNumber sequence_number, - QuicPacket* packet, - TransmissionType transmission_type, - HasRetransmittableData retransmittable, - IsHandshake handshake, - Force force); - - // Make sure an ack we got from our peer is sane. - bool ValidateAckFrame(const QuicAckFrame& incoming_ack); - QuicConnectionHelperInterface* helper() { return helper_; } // Selects and updates the version of the protocol being used by selecting a @@ -448,50 +510,17 @@ class NET_EXPORT_PRIVATE QuicConnection // such a version exists, false otherwise. bool SelectMutualVersion(const QuicVersionVector& available_versions); - QuicFramer framer_; + QuicPacketWriter* writer() { return writer_; } private: - // Stores current batch state for connection, puts the connection - // into batch mode, and destruction restores the stored batch state. - // While the bundler is in scope, any generated frames are bundled - // as densely as possible into packets. In addition, this bundler - // can be configured to ensure that an ACK frame is included in the - // first packet created, if there's new ack information to be sent. - class ScopedPacketBundler { - public: - // In addition to all outgoing frames being bundled when the - // bundler is in scope, setting |include_ack| to true ensures that - // an ACK frame is opportunistically bundled with the first - // outgoing packet. - ScopedPacketBundler(QuicConnection* connection, bool include_ack); - ~ScopedPacketBundler(); - - private: - QuicConnection* connection_; - bool already_in_batch_mode_; - }; - - friend class ScopedPacketBundler; 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, + QueuedPacket(SerializedPacket packet, EncryptionLevel level, - TransmissionType transmission_type, - HasRetransmittableData retransmittable, - IsHandshake handshake, - Force forced) - : sequence_number(sequence_number), - packet(packet), - encryption_level(level), - transmission_type(transmission_type), - retransmittable(retransmittable), - handshake(handshake), - forced(forced) { - } + TransmissionType transmission_type); QuicPacketSequenceNumber sequence_number; QuicPacket* packet; @@ -499,109 +528,37 @@ class NET_EXPORT_PRIVATE QuicConnection TransmissionType transmission_type; HasRetransmittableData retransmittable; IsHandshake handshake; - Force forced; + PacketType type; + QuicByteCount length; }; - struct RetransmissionInfo { - RetransmissionInfo(QuicPacketSequenceNumber sequence_number, - QuicSequenceNumberLength sequence_number_length, - QuicTime sent_time) - : sequence_number(sequence_number), - sequence_number_length(sequence_number_length), - sent_time(sent_time), - number_nacks(0), - number_retransmissions(0) { - } - - QuicPacketSequenceNumber sequence_number; - QuicSequenceNumberLength sequence_number_length; - QuicTime sent_time; - size_t number_nacks; - size_t number_retransmissions; - }; - - struct RetransmissionTime { - RetransmissionTime(QuicPacketSequenceNumber sequence_number, - const QuicTime& scheduled_time, - bool for_fec) - : sequence_number(sequence_number), - scheduled_time(scheduled_time), - for_fec(for_fec) { } - - QuicPacketSequenceNumber sequence_number; - QuicTime scheduled_time; - bool for_fec; - }; + typedef std::list<QueuedPacket> QueuedPacketList; + typedef std::map<QuicFecGroupNumber, QuicFecGroup*> FecGroupMap; - struct PendingWrite { - PendingWrite(QuicPacketSequenceNumber sequence_number, - TransmissionType transmission_type, - HasRetransmittableData retransmittable, - EncryptionLevel level, - bool is_fec_packet, - size_t length) - : sequence_number(sequence_number), - transmission_type(transmission_type), - retransmittable(retransmittable), - level(level), - is_fec_packet(is_fec_packet), - length(length) { } + // Writes the given packet to socket, encrypted with packet's + // encryption_level. Returns true on successful write. Behavior is undefined + // if connection is not established or broken. A return value of true means + // the packet was transmitted and may be destroyed. If the packet is + // retransmittable, WritePacket sets up retransmission for a successful write. + // If packet is FORCE, then it will be sent immediately and the send scheduler + // will not be consulted. + bool WritePacket(QueuedPacket packet); - QuicPacketSequenceNumber sequence_number; - TransmissionType transmission_type; - HasRetransmittableData retransmittable; - EncryptionLevel level; - bool is_fec_packet; - size_t length; - }; - - class RetransmissionTimeComparator { - public: - bool operator()(const RetransmissionTime& lhs, - const RetransmissionTime& rhs) const { - DCHECK(lhs.scheduled_time.IsInitialized() && - rhs.scheduled_time.IsInitialized()); - return lhs.scheduled_time > rhs.scheduled_time; - } - }; + // Make sure an ack we got from our peer is sane. + bool ValidateAckFrame(const QuicAckFrame& incoming_ack); - typedef std::list<QueuedPacket> QueuedPacketList; - typedef std::map<QuicFecGroupNumber, QuicFecGroup*> FecGroupMap; - typedef std::priority_queue<RetransmissionTime, - std::vector<RetransmissionTime>, - RetransmissionTimeComparator> - RetransmissionTimeouts; + // Make sure a stop waiting we got from our peer is sane. + bool ValidateStopWaitingFrame(const QuicStopWaitingFrame& stop_waiting); // Sends a version negotiation packet to the peer. void SendVersionNegotiationPacket(); - void SetupRetransmissionAlarm(QuicPacketSequenceNumber sequence_number); - bool IsRetransmission(QuicPacketSequenceNumber sequence_number); - - void SetupAbandonFecTimer(QuicPacketSequenceNumber sequence_number); - // Clears any accumulated frames from the last received packet. void ClearLastFrames(); - // Called from OnCanWrite and WriteIfNotBlocked to write queued packets. - // Returns false if the socket has become blocked. - bool DoWrite(); - - // Calculates the smallest sequence number length that can also represent four - // times the maximum of the congestion window and the difference between the - // least_packet_awaited_by_peer_ and |sequence_number|. - QuicSequenceNumberLength CalculateSequenceNumberLength( - QuicPacketSequenceNumber sequence_number); - - // Drop packet corresponding to |sequence_number| by deleting entries from - // |unacked_packets_| and |retransmission_map_|, if present. We need to drop - // all packets with encryption level NONE after the default level has been set - // to FORWARD_SECURE. - void DropPacket(QuicPacketSequenceNumber sequence_number); - // Writes as many queued packets as possible. The connection must not be // blocked when this is called. - bool WriteQueuedPackets(); + void WriteQueuedPackets(); // Writes as many pending retransmissions as possible. void WritePendingRetransmissions(); @@ -624,16 +581,29 @@ class NET_EXPORT_PRIVATE QuicConnection void ProcessAckFrame(const QuicAckFrame& incoming_ack); - // Update the |sent_info| for an outgoing ack. - void UpdateSentPacketInfo(SentPacketInfo* sent_info); + void ProcessStopWaitingFrame(const QuicStopWaitingFrame& stop_waiting); + + // Update |stop_waiting| for an outgoing ack. + void UpdateStopWaiting(QuicStopWaitingFrame* stop_waiting); + + // Queues an ack or sets the ack alarm when an incoming packet arrives that + // should be acked. + void MaybeQueueAck(); // Checks if the last packet should instigate an ack. - bool ShouldLastPacketInstigateAck(); + bool ShouldLastPacketInstigateAck() const; + + // Checks if the peer is waiting for packets that have been given up on, and + // therefore an ack frame should be sent with a larger least_unacked. + void UpdateStopWaitingCount(); // Sends any packets which are a response to the last packet, including both // acks and pending writes if an ack opened the congestion window. - void MaybeSendInResponseToPacket(bool send_ack_immediately, - bool last_packet_should_instigate_ack); + void MaybeSendInResponseToPacket(); + + // Gets the least unacked sequence number, which is the next sequence number + // to be sent if there are no outstanding packets. + QuicPacketSequenceNumber GetLeastUnacked() const; // Get the FEC group associate with the last processed packet or NULL, if the // group has already been deleted. @@ -642,26 +612,41 @@ class NET_EXPORT_PRIVATE QuicConnection // Closes any FEC groups protecting packets before |sequence_number|. void CloseFecGroupsBefore(QuicPacketSequenceNumber sequence_number); + // Sets the ping alarm to the appropriate value, if any. + void SetPingAlarm(); + + // On arrival of a new packet, checks to see if the socket addresses have + // changed since the last packet we saw on this connection. + void CheckForAddressMigration(const IPEndPoint& self_address, + const IPEndPoint& peer_address); + + QuicFramer framer_; QuicConnectionHelperInterface* helper_; // Not owned. QuicPacketWriter* writer_; // Not owned. EncryptionLevel encryption_level_; const QuicClock* clock_; QuicRandom* random_generator_; - const QuicGuid guid_; + const QuicConnectionId connection_id_; // Address on the last successfully processed packet received from the // client. IPEndPoint self_address_; IPEndPoint peer_address_; + // Used to store latest peer port to possibly migrate to later. + int migrating_peer_port_; bool last_packet_revived_; // True if the last packet was revived from FEC. size_t last_size_; // Size of the last received packet. + EncryptionLevel last_decrypted_packet_level_; QuicPacketHeader last_header_; std::vector<QuicStreamFrame> last_stream_frames_; std::vector<QuicAckFrame> last_ack_frames_; std::vector<QuicCongestionFeedbackFrame> last_congestion_frames_; + std::vector<QuicStopWaitingFrame> last_stop_waiting_frames_; std::vector<QuicRstStreamFrame> last_rst_frames_; std::vector<QuicGoAwayFrame> last_goaway_frames_; + std::vector<QuicWindowUpdateFrame> last_window_update_frames_; + std::vector<QuicBlockedFrame> last_blocked_frames_; std::vector<QuicConnectionCloseFrame> last_close_frames_; QuicCongestionFeedbackFrame outgoing_congestion_feedback_; @@ -670,6 +655,9 @@ class NET_EXPORT_PRIVATE QuicConnection // Largest sequence sent by the peer which had an ack frame (latest ack info). QuicPacketSequenceNumber largest_seen_packet_with_ack_; + // Largest sequence number sent by the peer which had a stop waiting frame. + QuicPacketSequenceNumber largest_seen_packet_with_stop_waiting_; + // Collection of packets which were received before encryption was // established, but which could not be decrypted. We buffer these on // the assumption that they could not be processed because they were @@ -686,19 +674,22 @@ class NET_EXPORT_PRIVATE QuicConnection QueuedPacketList queued_packets_; // Contains information about the current write in progress, if any. - scoped_ptr<PendingWrite> pending_write_; + scoped_ptr<QueuedPacket> pending_write_; // Contains the connection close packet if the connection has been closed. scoped_ptr<QuicEncryptedPacket> connection_close_packet_; - // True when the socket becomes unwritable. - bool write_blocked_; - FecGroupMap group_map_; QuicReceivedPacketManager received_packet_manager_; QuicSentEntropyManager sent_entropy_manager_; + // Indicates whether an ack should be sent the next time we try to write. + bool ack_queued_; + // Indicates how many consecutive times an ack has arrived which indicates + // the peer needs to stop waiting for some packets. + int stop_waiting_count_; + // An alarm that fires when an ACK should be sent to the peer. scoped_ptr<QuicAlarm> ack_alarm_; // An alarm that fires when a packet needs to be retransmitted. @@ -711,18 +702,17 @@ class NET_EXPORT_PRIVATE QuicConnection scoped_ptr<QuicAlarm> resume_writes_alarm_; // An alarm that fires when the connection may have timed out. scoped_ptr<QuicAlarm> timeout_alarm_; + // An alarm that fires when a ping should be sent. + scoped_ptr<QuicAlarm> ping_alarm_; QuicConnectionVisitorInterface* visitor_; - QuicConnectionDebugVisitorInterface* debug_visitor_; - QuicPacketCreator packet_creator_; + QuicConnectionDebugVisitor* debug_visitor_; QuicPacketGenerator packet_generator_; // Network idle time before we kill of this connection. QuicTime::Delta idle_network_timeout_; // Overall connection timeout. QuicTime::Delta overall_connection_timeout_; - // Connection creation time. - QuicTime creation_time_; // Statistics for this session. QuicConnectionStats stats_; @@ -731,13 +721,13 @@ class NET_EXPORT_PRIVATE QuicConnection // This is used for timeouts, and does not indicate the packet was processed. QuicTime time_of_last_received_packet_; - // The time that we last sent a packet for this connection. - QuicTime time_of_last_sent_packet_; + // The last time a new (non-retransmitted) packet was sent for this + // connection. + QuicTime time_of_last_sent_new_packet_; - // Sequence number of the last packet guaranteed to be sent in packet sequence - // number order. Not set when packets are queued, since that may cause - // re-ordering. - QuicPacketSequenceNumber sequence_number_of_last_inorder_packet_; + // Sequence number of the last sent packet. Packets are guaranteed to be sent + // in sequence number order. + QuicPacketSequenceNumber sequence_number_of_last_sent_packet_; // Sent packet manager which tracks the status of packets sent by this // connection and contains the send and receive algorithms to determine when @@ -754,14 +744,21 @@ class NET_EXPORT_PRIVATE QuicConnection // close. bool connected_; - // Set to true if the udp packet headers have a new self or peer address. - // This is checked later on validating a data or version negotiation packet. - bool address_migrating_; + // Set to true if the UDP packet headers have a new IP address for the peer. + // If true, do not perform connection migration. + bool peer_ip_changed_; + + // Set to true if the UDP packet headers have a new port for the peer. + // If true, and the IP has not changed, then we can migrate the connection. + bool peer_port_changed_; + + // Set to true if the UDP packet headers are addressed to a different IP. + // We do not support connection migration when the self IP changed. + bool self_ip_changed_; - // An AckNotifier can register to be informed when ACKs have been received for - // all packets that a given block of data was sent in. The AckNotifierManager - // maintains the currently active notifiers. - AckNotifierManager ack_notifier_manager_; + // Set to true if the UDP packet headers are addressed to a different port. + // We do not support connection migration when the self port changed. + bool self_port_changed_; // If non-empty this contains the set of versions received in a // version negotiation packet. diff --git a/chromium/net/quic/quic_connection_helper.h b/chromium/net/quic/quic_connection_helper.h index 7098039c9eb..fa866f296af 100644 --- a/chromium/net/quic/quic_connection_helper.h +++ b/chromium/net/quic/quic_connection_helper.h @@ -12,6 +12,7 @@ #include <set> +#include "base/basictypes.h" #include "base/memory/weak_ptr.h" #include "net/base/ip_endpoint.h" #include "net/quic/quic_protocol.h" @@ -33,7 +34,6 @@ class NET_EXPORT_PRIVATE QuicConnectionHelper QuicConnectionHelper(base::TaskRunner* task_runner, const QuicClock* clock, QuicRandom* random_generator); - virtual ~QuicConnectionHelper(); // QuicConnectionHelperInterface diff --git a/chromium/net/quic/quic_connection_logger.cc b/chromium/net/quic/quic_connection_logger.cc index 174d99a58c6..6ac4f5563d9 100644 --- a/chromium/net/quic/quic_connection_logger.cc +++ b/chromium/net/quic/quic_connection_logger.cc @@ -4,6 +4,9 @@ #include "net/quic/quic_connection_logger.h" +#include <algorithm> +#include <string> + #include "base/bind.h" #include "base/callback.h" #include "base/metrics/histogram.h" @@ -11,14 +14,24 @@ #include "base/strings/string_number_conversions.h" #include "base/values.h" #include "net/base/net_log.h" -#include "net/quic/crypto/crypto_handshake.h" +#include "net/base/net_util.h" +#include "net/quic/crypto/crypto_handshake_message.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_address_mismatch.h" +#include "net/quic/quic_socket_address_coder.h" +using base::StringPiece; using std::string; namespace net { namespace { +// We have ranges-of-buckets in the cumulative histogram (covering 21 packet +// sequences) of length 2, 3, 4, ... 22. +// Hence the largest sample is bounded by the sum of those numbers. +const int kBoundingSampleInCumulativeHistogram = ((2 + 22) * 21) / 2; + base::Value* NetLogQuicPacketCallback(const IPEndPoint* self_address, const IPEndPoint* peer_address, size_t packet_size, @@ -33,11 +46,13 @@ base::Value* NetLogQuicPacketCallback(const IPEndPoint* self_address, base::Value* NetLogQuicPacketSentCallback( QuicPacketSequenceNumber sequence_number, EncryptionLevel level, + TransmissionType transmission_type, size_t packet_size, WriteResult result, NetLog::LogLevel /* log_level */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("encryption_level", level); + dict->SetInteger("transmission_type", transmission_type); dict->SetString("packet_sequence_number", base::Uint64ToString(sequence_number)); dict->SetInteger("size", packet_size); @@ -51,19 +66,19 @@ base::Value* NetLogQuicPacketRetransmittedCallback( QuicPacketSequenceNumber old_sequence_number, QuicPacketSequenceNumber new_sequence_number, NetLog::LogLevel /* log_level */) { - base::DictionaryValue* dict = new base::DictionaryValue(); - dict->SetString("old_packet_sequence_number", - base::Uint64ToString(old_sequence_number)); - dict->SetString("new_packet_sequence_number", - base::Uint64ToString(new_sequence_number)); - return dict; + base::DictionaryValue* dict = new base::DictionaryValue(); + dict->SetString("old_packet_sequence_number", + base::Uint64ToString(old_sequence_number)); + dict->SetString("new_packet_sequence_number", + base::Uint64ToString(new_sequence_number)); + return dict; } base::Value* NetLogQuicPacketHeaderCallback(const QuicPacketHeader* header, NetLog::LogLevel /* log_level */) { base::DictionaryValue* dict = new base::DictionaryValue(); - dict->SetString("guid", - base::Uint64ToString(header->public_header.guid)); + dict->SetString("connection_id", + base::Uint64ToString(header->public_header.connection_id)); dict->SetInteger("reset_flag", header->public_header.reset_flag); dict->SetInteger("version_flag", header->public_header.version_flag); dict->SetString("packet_sequence_number", @@ -87,15 +102,12 @@ base::Value* NetLogQuicStreamFrameCallback(const QuicStreamFrame* frame, base::Value* NetLogQuicAckFrameCallback(const QuicAckFrame* frame, NetLog::LogLevel /* log_level */) { base::DictionaryValue* dict = new base::DictionaryValue(); - base::DictionaryValue* sent_info = new base::DictionaryValue(); - dict->Set("sent_info", sent_info); - sent_info->SetString("least_unacked", - base::Uint64ToString(frame->sent_info.least_unacked)); base::DictionaryValue* received_info = new base::DictionaryValue(); dict->Set("received_info", received_info); received_info->SetString( "largest_observed", base::Uint64ToString(frame->received_info.largest_observed)); + received_info->SetBoolean("truncated", frame->received_info.is_truncated); base::ListValue* missing = new base::ListValue(); received_info->Set("missing_packets", missing); const SequenceNumberSet& missing_packets = @@ -114,14 +126,12 @@ base::Value* NetLogQuicCongestionFeedbackFrameCallback( switch (frame->type) { case kInterArrival: { dict->SetString("type", "InterArrival"); - dict->SetInteger("accumulated_number_of_lost_packets", - frame->inter_arrival.accumulated_number_of_lost_packets); 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) { - std::string value = base::Uint64ToString(it->first) + "@" + + string value = base::Uint64ToString(it->first) + "@" + base::Uint64ToString(it->second.ToDebuggingValue()); received->AppendString(value); } @@ -134,10 +144,12 @@ base::Value* NetLogQuicCongestionFeedbackFrameCallback( break; case kTCP: dict->SetString("type", "TCP"); - dict->SetInteger("accumulated_number_of_lost_packets", - frame->tcp.accumulated_number_of_lost_packets); dict->SetInteger("receive_window", frame->tcp.receive_window); break; + case kTCPBBR: + dict->SetString("type", "TCPBBR"); + // TODO(rtenneti): Add support for BBR. + break; } return dict; @@ -162,6 +174,44 @@ base::Value* NetLogQuicConnectionCloseFrameCallback( return dict; } +base::Value* NetLogQuicWindowUpdateFrameCallback( + const QuicWindowUpdateFrame* frame, + NetLog::LogLevel /* log_level */) { + base::DictionaryValue* dict = new base::DictionaryValue(); + dict->SetInteger("stream_id", frame->stream_id); + dict->SetString("byte_offset", base::Uint64ToString(frame->byte_offset)); + return dict; +} + +base::Value* NetLogQuicBlockedFrameCallback( + const QuicBlockedFrame* frame, + NetLog::LogLevel /* log_level */) { + base::DictionaryValue* dict = new base::DictionaryValue(); + dict->SetInteger("stream_id", frame->stream_id); + return dict; +} + +base::Value* NetLogQuicGoAwayFrameCallback( + const QuicGoAwayFrame* frame, + NetLog::LogLevel /* log_level */) { + base::DictionaryValue* dict = new base::DictionaryValue(); + dict->SetInteger("quic_error", frame->error_code); + dict->SetInteger("last_good_stream_id", frame->last_good_stream_id); + dict->SetString("reason_phrase", frame->reason_phrase); + return dict; +} + +base::Value* NetLogQuicStopWaitingFrameCallback( + const QuicStopWaitingFrame* frame, + NetLog::LogLevel /* log_level */) { + base::DictionaryValue* dict = new base::DictionaryValue(); + base::DictionaryValue* sent_info = new base::DictionaryValue(); + dict->Set("sent_info", sent_info); + sent_info->SetString("least_unacked", + base::Uint64ToString(frame->least_unacked)); + return dict; +} + base::Value* NetLogQuicVersionNegotiationPacketCallback( const QuicVersionNegotiationPacket* packet, NetLog::LogLevel /* log_level */) { @@ -198,19 +248,111 @@ void UpdatePacketGapSentHistogram(size_t num_consecutive_missing_packets) { num_consecutive_missing_packets); } +void UpdatePublicResetAddressMismatchHistogram( + const IPEndPoint& server_hello_address, + const IPEndPoint& public_reset_address) { + int sample = GetAddressMismatch(server_hello_address, public_reset_address); + // We are seemingly talking to an older server that does not support the + // feature, so we can't report the results in the histogram. + if (sample < 0) { + return; + } + UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.PublicResetAddressMismatch2", + sample, QUIC_ADDRESS_MISMATCH_MAX); +} + +const char* GetConnectionDescriptionString() { + NetworkChangeNotifier::ConnectionType type = + NetworkChangeNotifier::GetConnectionType(); + const char* description = NetworkChangeNotifier::ConnectionTypeToString(type); + // Most platforms don't distingish Wifi vs Etherenet, and call everything + // CONNECTION_UNKNOWN :-(. We'll tease out some details when we are on WiFi, + // and hopefully leave only ethernet (with no WiFi available) in the + // CONNECTION_UNKNOWN category. This *might* err if there is both ethernet, + // as well as WiFi, where WiFi was not being used that much. + // This function only seems usefully defined on Windows currently. + if (type == NetworkChangeNotifier::CONNECTION_UNKNOWN || + type == NetworkChangeNotifier::CONNECTION_WIFI) { + WifiPHYLayerProtocol wifi_type = GetWifiPHYLayerProtocol(); + switch (wifi_type) { + case WIFI_PHY_LAYER_PROTOCOL_NONE: + // No wifi support or no associated AP. + break; + case WIFI_PHY_LAYER_PROTOCOL_ANCIENT: + // An obsolete modes introduced by the original 802.11, e.g. IR, FHSS. + description = "CONNECTION_WIFI_ANCIENT"; + break; + case WIFI_PHY_LAYER_PROTOCOL_A: + // 802.11a, OFDM-based rates. + description = "CONNECTION_WIFI_802.11a"; + break; + case WIFI_PHY_LAYER_PROTOCOL_B: + // 802.11b, DSSS or HR DSSS. + description = "CONNECTION_WIFI_802.11b"; + break; + case WIFI_PHY_LAYER_PROTOCOL_G: + // 802.11g, same rates as 802.11a but compatible with 802.11b. + description = "CONNECTION_WIFI_802.11g"; + break; + case WIFI_PHY_LAYER_PROTOCOL_N: + // 802.11n, HT rates. + description = "CONNECTION_WIFI_802.11n"; + break; + case WIFI_PHY_LAYER_PROTOCOL_UNKNOWN: + // Unclassified mode or failure to identify. + break; + } + } + return description; +} + +// If |address| is an IPv4-mapped IPv6 address, returns ADDRESS_FAMILY_IPV4 +// instead of ADDRESS_FAMILY_IPV6. Othewise, behaves like GetAddressFamily(). +AddressFamily GetRealAddressFamily(const IPAddressNumber& address) { + return IsIPv4Mapped(address) ? ADDRESS_FAMILY_IPV4 : + GetAddressFamily(address); +} + } // namespace QuicConnectionLogger::QuicConnectionLogger(const BoundNetLog& net_log) : net_log_(net_log), last_received_packet_sequence_number_(0), + last_received_packet_size_(0), largest_received_packet_sequence_number_(0), largest_received_missing_packet_sequence_number_(0), - out_of_order_recieved_packet_count_(0) { + num_out_of_order_received_packets_(0), + num_packets_received_(0), + num_truncated_acks_sent_(0), + num_truncated_acks_received_(0), + num_frames_received_(0), + num_duplicate_frames_received_(0), + connection_description_(GetConnectionDescriptionString()) { } QuicConnectionLogger::~QuicConnectionLogger() { UMA_HISTOGRAM_COUNTS("Net.QuicSession.OutOfOrderPacketsReceived", - out_of_order_recieved_packet_count_); + num_out_of_order_received_packets_); + UMA_HISTOGRAM_COUNTS("Net.QuicSession.TruncatedAcksSent", + num_truncated_acks_sent_); + UMA_HISTOGRAM_COUNTS("Net.QuicSession.TruncatedAcksReceived", + num_truncated_acks_received_); + if (num_frames_received_ > 0) { + int duplicate_stream_frame_per_thousand = + num_duplicate_frames_received_ * 1000 / num_frames_received_; + if (num_packets_received_ < 100) { + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Net.QuicSession.StreamFrameDuplicatedShortConnection", + duplicate_stream_frame_per_thousand, 1, 1000, 75); + } else { + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Net.QuicSession.StreamFrameDuplicatedLongConnection", + duplicate_stream_frame_per_thousand, 1, 1000, 75); + + } + } + + RecordLossHistograms(); } void QuicConnectionLogger::OnFrameAddedToPacket(const QuicFrame& frame) { @@ -226,6 +368,8 @@ void QuicConnectionLogger::OnFrameAddedToPacket(const QuicFrame& frame) { net_log_.AddEvent( NetLog::TYPE_QUIC_SESSION_ACK_FRAME_SENT, base::Bind(&NetLogQuicAckFrameCallback, frame.ack_frame)); + if (frame.ack_frame->received_info.is_truncated) + ++num_truncated_acks_sent_; break; case CONGESTION_FEEDBACK_FRAME: net_log_.AddEvent( @@ -248,6 +392,32 @@ void QuicConnectionLogger::OnFrameAddedToPacket(const QuicFrame& frame) { frame.connection_close_frame)); break; case GOAWAY_FRAME: + net_log_.AddEvent( + NetLog::TYPE_QUIC_SESSION_GOAWAY_FRAME_SENT, + base::Bind(&NetLogQuicGoAwayFrameCallback, + frame.goaway_frame)); + break; + case WINDOW_UPDATE_FRAME: + net_log_.AddEvent( + NetLog::TYPE_QUIC_SESSION_WINDOW_UPDATE_FRAME_SENT, + base::Bind(&NetLogQuicWindowUpdateFrameCallback, + frame.window_update_frame)); + break; + case BLOCKED_FRAME: + net_log_.AddEvent( + NetLog::TYPE_QUIC_SESSION_BLOCKED_FRAME_SENT, + base::Bind(&NetLogQuicBlockedFrameCallback, + frame.blocked_frame)); + break; + case STOP_WAITING_FRAME: + net_log_.AddEvent( + NetLog::TYPE_QUIC_SESSION_STOP_WAITING_FRAME_SENT, + base::Bind(&NetLogQuicStopWaitingFrameCallback, + frame.stop_waiting_frame)); + break; + case PING_FRAME: + // PingFrame has no contents to log, so just record that it was sent. + net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_PING_FRAME_SENT); break; default: DCHECK(false) << "Illegal frame type: " << frame.type; @@ -257,12 +427,13 @@ void QuicConnectionLogger::OnFrameAddedToPacket(const QuicFrame& frame) { void QuicConnectionLogger::OnPacketSent( QuicPacketSequenceNumber sequence_number, EncryptionLevel level, + TransmissionType transmission_type, const QuicEncryptedPacket& packet, WriteResult result) { net_log_.AddEvent( NetLog::TYPE_QUIC_SESSION_PACKET_SENT, base::Bind(&NetLogQuicPacketSentCallback, sequence_number, level, - packet.length(), result)); + transmission_type, packet.length(), result)); } void QuicConnectionLogger:: OnPacketRetransmitted( @@ -277,6 +448,14 @@ void QuicConnectionLogger:: OnPacketRetransmitted( void QuicConnectionLogger::OnPacketReceived(const IPEndPoint& self_address, const IPEndPoint& peer_address, const QuicEncryptedPacket& packet) { + if (local_address_from_self_.GetFamily() == ADDRESS_FAMILY_UNSPECIFIED) { + local_address_from_self_ = self_address; + UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.ConnectionTypeFromSelf", + GetRealAddressFamily(self_address.address()), + ADDRESS_FAMILY_LAST); + } + + last_received_packet_size_ = packet.length(); net_log_.AddEvent( NetLog::TYPE_QUIC_SESSION_PACKET_RECEIVED, base::Bind(&NetLogQuicPacketCallback, &self_address, &peer_address, @@ -292,6 +471,7 @@ void QuicConnectionLogger::OnPacketHeader(const QuicPacketHeader& header) { net_log_.AddEvent( NetLog::TYPE_QUIC_SESSION_PACKET_HEADER_RECEIVED, base::Bind(&NetLogQuicPacketHeaderCallback, &header)); + ++num_packets_received_; if (largest_received_packet_sequence_number_ < header.packet_sequence_number) { QuicPacketSequenceNumber delta = header.packet_sequence_number - @@ -304,8 +484,10 @@ void QuicConnectionLogger::OnPacketHeader(const QuicPacketHeader& header) { } largest_received_packet_sequence_number_ = header.packet_sequence_number; } + if (header.packet_sequence_number < received_packets_.size()) + received_packets_[header.packet_sequence_number] = true; if (header.packet_sequence_number < last_received_packet_sequence_number_) { - ++out_of_order_recieved_packet_count_; + ++num_out_of_order_received_packets_; UMA_HISTOGRAM_COUNTS("Net.QuicSession.OutOfOrderGapReceived", last_received_packet_sequence_number_ - header.packet_sequence_number); @@ -324,6 +506,14 @@ void QuicConnectionLogger::OnAckFrame(const QuicAckFrame& frame) { NetLog::TYPE_QUIC_SESSION_ACK_FRAME_RECEIVED, base::Bind(&NetLogQuicAckFrameCallback, &frame)); + const size_t kApproximateLargestSoloAckBytes = 100; + if (last_received_packet_sequence_number_ < received_acks_.size() && + last_received_packet_size_ < kApproximateLargestSoloAckBytes) + received_acks_[last_received_packet_sequence_number_] = true; + + if (frame.received_info.is_truncated) + ++num_truncated_acks_received_; + if (frame.received_info.missing_packets.empty()) return; @@ -367,6 +557,13 @@ void QuicConnectionLogger::OnCongestionFeedbackFrame( base::Bind(&NetLogQuicCongestionFeedbackFrameCallback, &frame)); } +void QuicConnectionLogger::OnStopWaitingFrame( + const QuicStopWaitingFrame& frame) { + net_log_.AddEvent( + NetLog::TYPE_QUIC_SESSION_STOP_WAITING_FRAME_RECEIVED, + base::Bind(&NetLogQuicStopWaitingFrameCallback, &frame)); +} + void QuicConnectionLogger::OnRstStreamFrame(const QuicRstStreamFrame& frame) { UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.RstStreamErrorCodeServer", frame.error_code); @@ -382,9 +579,35 @@ void QuicConnectionLogger::OnConnectionCloseFrame( base::Bind(&NetLogQuicConnectionCloseFrameCallback, &frame)); } +void QuicConnectionLogger::OnWindowUpdateFrame( + const QuicWindowUpdateFrame& frame) { + net_log_.AddEvent( + NetLog::TYPE_QUIC_SESSION_WINDOW_UPDATE_FRAME_RECEIVED, + base::Bind(&NetLogQuicWindowUpdateFrameCallback, &frame)); +} + +void QuicConnectionLogger::OnBlockedFrame(const QuicBlockedFrame& frame) { + net_log_.AddEvent( + NetLog::TYPE_QUIC_SESSION_BLOCKED_FRAME_RECEIVED, + base::Bind(&NetLogQuicBlockedFrameCallback, &frame)); +} + +void QuicConnectionLogger::OnGoAwayFrame(const QuicGoAwayFrame& frame) { + net_log_.AddEvent( + NetLog::TYPE_QUIC_SESSION_GOAWAY_FRAME_RECEIVED, + base::Bind(&NetLogQuicGoAwayFrameCallback, &frame)); +} + +void QuicConnectionLogger::OnPingFrame(const QuicPingFrame& frame) { + // PingFrame has no contents to log, so just record that it was received. + net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_PING_FRAME_RECEIVED); +} + void QuicConnectionLogger::OnPublicResetPacket( const QuicPublicResetPacket& packet) { net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_PUBLIC_RESET_PACKET_RECEIVED); + UpdatePublicResetAddressMismatchHistogram(local_address_from_shlo_, + packet.client_address); } void QuicConnectionLogger::OnVersionNegotiationPacket( @@ -407,6 +630,19 @@ void QuicConnectionLogger::OnCryptoHandshakeMessageReceived( net_log_.AddEvent( NetLog::TYPE_QUIC_SESSION_CRYPTO_HANDSHAKE_MESSAGE_RECEIVED, base::Bind(&NetLogQuicCryptoHandshakeMessageCallback, &message)); + + if (message.tag() == kSHLO) { + StringPiece address; + QuicSocketAddressCoder decoder; + if (message.GetStringPiece(kCADR, &address) && + decoder.Decode(address.data(), address.size())) { + local_address_from_shlo_ = IPEndPoint(decoder.ip(), decoder.port()); + UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.ConnectionTypeFromPeer", + GetRealAddressFamily( + local_address_from_shlo_.address()), + ADDRESS_FAMILY_LAST); + } + } } void QuicConnectionLogger::OnCryptoHandshakeMessageSent( @@ -430,4 +666,172 @@ void QuicConnectionLogger::OnSuccessfulVersionNegotiation( NetLog::StringCallback("version", &quic_version)); } +void QuicConnectionLogger::UpdateReceivedFrameCounts( + QuicStreamId stream_id, + int num_frames_received, + int num_duplicate_frames_received) { + if (stream_id != kCryptoStreamId) { + num_frames_received_ += num_frames_received; + num_duplicate_frames_received_ += num_duplicate_frames_received; + } +} + +base::HistogramBase* QuicConnectionLogger::GetPacketSequenceNumberHistogram( + const char* statistic_name) const { + string prefix("Net.QuicSession.PacketReceived_"); + return base::LinearHistogram::FactoryGet( + prefix + statistic_name + connection_description_, + 1, received_packets_.size(), received_packets_.size() + 1, + base::HistogramBase::kUmaTargetedHistogramFlag); +} + +base::HistogramBase* QuicConnectionLogger::Get6PacketHistogram( + const char* which_6) const { + // This histogram takes a binary encoding of the 6 consecutive packets + // received. As a result, there are 64 possible sample-patterns. + string prefix("Net.QuicSession.6PacketsPatternsReceived_"); + return base::LinearHistogram::FactoryGet( + prefix + which_6 + connection_description_, 1, 64, 65, + base::HistogramBase::kUmaTargetedHistogramFlag); +} + +base::HistogramBase* QuicConnectionLogger::Get21CumulativeHistogram( + const char* which_21) const { + // This histogram contains, for each sequence of 21 packets, the results from + // 21 distinct questions about that sequence. Conceptually the histogtram is + // broken into 21 distinct ranges, and one sample is added into each of those + // ranges whenever we process a set of 21 packets. + // There is a little rendundancy, as each "range" must have the same number + // of samples, all told, but the histogram is a tad easier to read this way. + // The questions are: + // Was the first packet present (bucket 0==>no; bucket 1==>yes) + // Of the first two packets, how many were present? (bucket 2==> none; + // bucket 3==> 1 of 2; bucket 4==> 2 of 2) + // Of the first three packets, how many were present? (bucket 5==>none; + // bucket 6==> 1 of 3; bucket 7==> 2 of 3; bucket 8==> 3 of 3). + // etc. + string prefix("Net.QuicSession.21CumulativePacketsReceived_"); + return base::LinearHistogram::FactoryGet( + prefix + which_21 + connection_description_, + 1, kBoundingSampleInCumulativeHistogram, + kBoundingSampleInCumulativeHistogram + 1, + base::HistogramBase::kUmaTargetedHistogramFlag); +} + +// static +void QuicConnectionLogger::AddTo21CumulativeHistogram( + base::HistogramBase* histogram, + int bit_mask_of_packets, + int valid_bits_in_mask) { + DCHECK_LE(valid_bits_in_mask, 21); + DCHECK_LT(bit_mask_of_packets, 1 << 21); + const int blank_bits_in_mask = 21 - valid_bits_in_mask; + DCHECK_EQ(bit_mask_of_packets & ((1 << blank_bits_in_mask) - 1), 0); + bit_mask_of_packets >>= blank_bits_in_mask; + int bits_so_far = 0; + int range_start = 0; + for (int i = 1; i <= valid_bits_in_mask; ++i) { + bits_so_far += bit_mask_of_packets & 1; + bit_mask_of_packets >>= 1; + DCHECK_LT(range_start + bits_so_far, kBoundingSampleInCumulativeHistogram); + histogram->Add(range_start + bits_so_far); + range_start += i + 1; + } +} + +void QuicConnectionLogger::RecordAggregatePacketLossRate() const { + // For short connections under 22 packets in length, we'll rely on the + // Net.QuicSession.21CumulativePacketsReceived_* histogram to indicate packet + // loss rates. This way we avoid tremendously anomalous contributions to our + // histogram. (e.g., if we only got 5 packets, but lost 1, we'd otherwise + // record a 20% loss in this histogram!). We may still get some strange data + // (1 loss in 22 is still high :-/). + if (largest_received_packet_sequence_number_ <= 21) + return; + + QuicPacketSequenceNumber divisor = largest_received_packet_sequence_number_; + QuicPacketSequenceNumber numerator = divisor - num_packets_received_; + if (divisor < 100000) + numerator *= 1000; + else + divisor /= 1000; + string prefix("Net.QuicSession.PacketLossRate_"); + base::HistogramBase* histogram = base::Histogram::FactoryGet( + prefix + connection_description_, 1, 1000, 75, + base::HistogramBase::kUmaTargetedHistogramFlag); + histogram->Add(numerator / divisor); +} + +void QuicConnectionLogger::RecordLossHistograms() const { + if (largest_received_packet_sequence_number_ == 0) + return; // Connection was never used. + RecordAggregatePacketLossRate(); + + base::HistogramBase* is_not_ack_histogram = + GetPacketSequenceNumberHistogram("IsNotAck_"); + base::HistogramBase* is_an_ack_histogram = + GetPacketSequenceNumberHistogram("IsAnAck_"); + base::HistogramBase* packet_arrived_histogram = + GetPacketSequenceNumberHistogram("Ack_"); + base::HistogramBase* packet_missing_histogram = + GetPacketSequenceNumberHistogram("Nack_"); + base::HistogramBase* ongoing_cumulative_packet_histogram = + Get21CumulativeHistogram("Some21s_"); + base::HistogramBase* first_cumulative_packet_histogram = + Get21CumulativeHistogram("First21_"); + base::HistogramBase* six_packet_histogram = Get6PacketHistogram("Some6s_"); + + DCHECK_EQ(received_packets_.size(), received_acks_.size()); + const QuicPacketSequenceNumber last_index = + std::min<QuicPacketSequenceNumber>(received_packets_.size() - 1, + largest_received_packet_sequence_number_); + const QuicPacketSequenceNumber index_of_first_21_contribution = + std::min<QuicPacketSequenceNumber>(21, last_index); + // Bit pattern of consecutively received packets that is maintained as we scan + // through the received_packets_ vector. Less significant bits correspond to + // less recent packets, and only the low order 21 bits are ever defined. + // Bit is 1 iff corresponding packet was received. + int packet_pattern_21 = 0; + // Zero is an invalid packet sequence number. + DCHECK(!received_packets_[0]); + for (size_t i = 1; i <= last_index; ++i) { + if (received_acks_[i]) + is_an_ack_histogram->Add(i); + else + is_not_ack_histogram->Add(i); + + packet_pattern_21 >>= 1; + if (received_packets_[i]) { + packet_arrived_histogram->Add(i); + packet_pattern_21 |= (1 << 20); // Turn on the 21st bit. + } else { + packet_missing_histogram->Add(i); + } + + if (i == index_of_first_21_contribution) { + AddTo21CumulativeHistogram(first_cumulative_packet_histogram, + packet_pattern_21, i); + } + // We'll just record for non-overlapping ranges, to reduce histogramming + // cost for now. Each call does 21 separate histogram additions. + if (i > 21 || i % 21 == 0) { + AddTo21CumulativeHistogram(ongoing_cumulative_packet_histogram, + packet_pattern_21, 21); + } + + if (i < 6) + continue; // Not enough packets to do any pattern recording. + int recent_6_mask = packet_pattern_21 >> 15; + DCHECK_LT(recent_6_mask, 64); + if (i == 6) { + Get6PacketHistogram("First6_")->Add(recent_6_mask); + continue; + } + // Record some overlapping patterns, to get a better picture, since this is + // not very expensive. + if (i % 3 == 0) + six_packet_histogram->Add(recent_6_mask); + } +} + } // namespace net diff --git a/chromium/net/quic/quic_connection_logger.h b/chromium/net/quic/quic_connection_logger.h index d162282204a..2110f9ef6d3 100644 --- a/chromium/net/quic/quic_connection_logger.h +++ b/chromium/net/quic/quic_connection_logger.h @@ -5,18 +5,22 @@ #ifndef NET_QUIC_QUIC_CONNECTION_LOGGER_H_ #define NET_QUIC_QUIC_CONNECTION_LOGGER_H_ +#include <bitset> + +#include "net/base/ip_endpoint.h" +#include "net/base/net_log.h" +#include "net/base/network_change_notifier.h" #include "net/quic/quic_connection.h" #include "net/quic/quic_protocol.h" namespace net { -class BoundNetLog; class CryptoHandshakeMessage; // This class is a debug visitor of a QuicConnection which logs // events to |net_log|. class NET_EXPORT_PRIVATE QuicConnectionLogger - : public QuicConnectionDebugVisitorInterface { + : public QuicConnectionDebugVisitor { public: explicit QuicConnectionLogger(const BoundNetLog& net_log); @@ -28,6 +32,7 @@ class NET_EXPORT_PRIVATE QuicConnectionLogger // QuicConnectionDebugVisitorInterface virtual void OnPacketSent(QuicPacketSequenceNumber sequence_number, EncryptionLevel level, + TransmissionType transmission_type, const QuicEncryptedPacket& packet, WriteResult result) OVERRIDE; virtual void OnPacketRetransmitted( @@ -42,9 +47,14 @@ class NET_EXPORT_PRIVATE QuicConnectionLogger virtual void OnAckFrame(const QuicAckFrame& frame) OVERRIDE; virtual void OnCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& frame) OVERRIDE; + virtual void OnStopWaitingFrame(const QuicStopWaitingFrame& frame) OVERRIDE; virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE; virtual void OnConnectionCloseFrame( const QuicConnectionCloseFrame& frame) OVERRIDE; + virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) OVERRIDE; + virtual void OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE; + virtual void OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE; + virtual void OnPingFrame(const QuicPingFrame& frame) OVERRIDE; virtual void OnPublicResetPacket( const QuicPublicResetPacket& packet) OVERRIDE; virtual void OnVersionNegotiationPacket( @@ -58,20 +68,87 @@ class NET_EXPORT_PRIVATE QuicConnectionLogger const CryptoHandshakeMessage& message); void OnConnectionClosed(QuicErrorCode error, bool from_peer); void OnSuccessfulVersionNegotiation(const QuicVersion& version); + void UpdateReceivedFrameCounts(QuicStreamId stream_id, + int num_frames_received, + int num_duplicate_frames_received); private: + // Do a factory get for a histogram for recording data, about individual + // packet sequence numbers, that was gathered in the vectors + // received_packets_ and received_acks_. |statistic_name| identifies which + // element of data is recorded, and is used to form the histogram name. + base::HistogramBase* GetPacketSequenceNumberHistogram( + const char* statistic_name) const; + // Do a factory get for a histogram to record a 6-packet loss-sequence as a + // sample. The histogram will record the 64 distinct possible combinations. + // |which_6| is used to adjust the name of the histogram to distinguish the + // first 6 packets in a connection, vs. some later 6 packets. + base::HistogramBase* Get6PacketHistogram(const char* which_6) const; + // Do a factory get for a histogram to record cumulative stats across a 21 + // packet sequence. |which_21| is used to adjust the name of the histogram + // to distinguish the first 21 packets' loss data, vs. some later 21 packet + // sequences' loss data. + base::HistogramBase* Get21CumulativeHistogram(const char* which_21) const; + // Add samples associated with a |bit_mask_of_packets| to the given histogram + // that was provided by Get21CumulativeHistogram(). The LSB of that mask + // corresponds to the oldest packet sequence number in the series of packets, + // and the bit in the 2^20 position corresponds to the most recently received + // packet. Of the maximum of 21 bits that are valid (correspond to packets), + // only the most significant |valid_bits_in_mask| are processed. + // A bit value of 0 indicates that a packet was never received, and a 1 + // indicates the packet was received. + static void AddTo21CumulativeHistogram(base::HistogramBase* histogram, + int bit_mask_of_packets, + int valid_bits_in_mask); + // For connections longer than 21 received packets, this call will calculate + // the overall packet loss rate, and record it into a histogram. + void RecordAggregatePacketLossRate() const; + // At destruction time, this records results of |pacaket_received_| into + // histograms for specific connection types. + void RecordLossHistograms() const; + BoundNetLog net_log_; // The last packet sequence number received. QuicPacketSequenceNumber last_received_packet_sequence_number_; - // The largest packet sequence number received. In case of that a packet is - // received late, this value will not be updated. + // The size of the most recently received packet. + size_t last_received_packet_size_; + // The largest packet sequence number received. In the case where a packet is + // received late (out of order), this value will not be updated. QuicPacketSequenceNumber largest_received_packet_sequence_number_; // The largest packet sequence number which the peer has failed to // receive, according to the missing packet set in their ack frames. QuicPacketSequenceNumber largest_received_missing_packet_sequence_number_; // Number of times that the current received packet sequence number is // smaller than the last received packet sequence number. - size_t out_of_order_recieved_packet_count_; + size_t num_out_of_order_received_packets_; + // The number of times that OnPacketHeader was called. + // If the network replicates packets, then this number may be slightly + // different from the real number of distinct packets received. + QuicPacketSequenceNumber num_packets_received_; + // Number of times a truncated ACK frame was sent. + size_t num_truncated_acks_sent_; + // Number of times a truncated ACK frame was received. + size_t num_truncated_acks_received_; + // The kCADR value provided by the server in ServerHello. + IPEndPoint local_address_from_shlo_; + // The first local address from which a packet was received. + IPEndPoint local_address_from_self_; + // Count of the number of frames received. + int num_frames_received_; + // Count of the number of duplicate frames received. + int num_duplicate_frames_received_; + // Vector of inital packets status' indexed by packet sequence numbers, where + // false means never received. Zero is not a valid packet sequence number, so + // that offset is never used, and we'll track 150 packets. + std::bitset<151> received_packets_; + // Vector to indicate which of the initial 150 received packets turned out to + // contain solo ACK frames. An element is true iff an ACK frame was in the + // corresponding packet, and there was very little else. + std::bitset<151> received_acks_; + // The available type of connection (WiFi, 3G, etc.) when connection was first + // used. + const char* const connection_description_; + DISALLOW_COPY_AND_ASSIGN(QuicConnectionLogger); }; diff --git a/chromium/net/quic/quic_connection_stats.cc b/chromium/net/quic/quic_connection_stats.cc index 500b5f39166..cf235cd751e 100644 --- a/chromium/net/quic/quic_connection_stats.cc +++ b/chromium/net/quic/quic_connection_stats.cc @@ -12,16 +12,33 @@ QuicConnectionStats::QuicConnectionStats() : bytes_sent(0), packets_sent(0), stream_bytes_sent(0), + packets_discarded(0), bytes_received(0), packets_received(0), + packets_processed(0), stream_bytes_received(0), bytes_retransmitted(0), packets_retransmitted(0), + bytes_spuriously_retransmitted(0), + packets_spuriously_retransmitted(0), + packets_lost(0), + slowstart_packets_lost(0), packets_revived(0), packets_dropped(0), + crypto_retransmit_count(0), + loss_timeout_count(0), + tlp_count(0), rto_count(0), - rtt(0), - estimated_bandwidth(0) { + min_rtt_us(0), + srtt_us(0), + estimated_bandwidth(0), + packets_reordered(0), + max_sequence_reordering(0), + max_time_reordering_us(0), + tcp_loss_events(0), + cwnd_increase_congestion_avoidance(0), + cwnd_increase_cubic_mode(0), + connection_creation_time(QuicTime::Zero()) { } QuicConnectionStats::~QuicConnectionStats() {} @@ -30,16 +47,36 @@ ostream& operator<<(ostream& os, const QuicConnectionStats& s) { os << "{ bytes sent: " << s.bytes_sent << ", packets sent:" << s.packets_sent << ", stream bytes sent: " << s.stream_bytes_sent + << ", packets discarded: " << s.packets_discarded << ", bytes received: " << s.bytes_received << ", packets received: " << s.packets_received + << ", packets processed: " << s.packets_processed << ", stream bytes received: " << s.stream_bytes_received << ", bytes retransmitted: " << s.bytes_retransmitted << ", packets retransmitted: " << s.packets_retransmitted + << ", bytes spuriously retransmitted: " << s.bytes_spuriously_retransmitted + << ", packets spuriously retransmitted: " + << s.packets_spuriously_retransmitted + << ", packets lost: " << s.packets_lost + << ", slowstart packets lost: " << s.slowstart_packets_lost << ", packets revived: " << s.packets_revived << ", packets dropped:" << s.packets_dropped + << ", crypto retransmit count: " << s.crypto_retransmit_count << ", rto count: " << s.rto_count - << ", rtt(us): " << s.rtt - << ", estimated_bandwidth: " << s.estimated_bandwidth + << ", tlp count: " << s.tlp_count + << ", min_rtt(us): " << s.min_rtt_us + << ", srtt(us): " << s.srtt_us + << ", max packet size: " << s.max_packet_size + << ", estimated bandwidth: " << s.estimated_bandwidth + << ", congestion window: " << s.congestion_window + << ", tcp_loss_events: " << s.tcp_loss_events + << ", packets reordered: " << s.packets_reordered + << ", max sequence reordering: " << s.max_sequence_reordering + << ", max time reordering(us): " << s.max_time_reordering_us + << ", total amount of cwnd increase in TCPCubic, in congestion avoidance: " + << s.cwnd_increase_congestion_avoidance + << ", amount of cwnd increase in TCPCubic, in cubic mode: " + << s.cwnd_increase_cubic_mode << "}\n"; return os; } diff --git a/chromium/net/quic/quic_connection_stats.h b/chromium/net/quic/quic_connection_stats.h index b0761d9ba8f..0bad8569029 100644 --- a/chromium/net/quic/quic_connection_stats.h +++ b/chromium/net/quic/quic_connection_stats.h @@ -9,19 +9,9 @@ #include "base/basictypes.h" #include "net/base/net_export.h" +#include "net/quic/quic_time.h" namespace net { -// TODO(satyamshekhar): Add more interesting stats: -// 1. (C/S)HLO retransmission count. -// 2. SHLO received to first stream packet processed time. -// 3. CHLO sent to SHLO received time. -// 4. Number of migrations. -// 5. Number of out of order packets. -// 6. Number of connections that require more that 1-RTT. -// 7. Avg number of streams / session. -// 8. Number of duplicates received. -// 9. Fraction of data transferred that was padding. - // Structure to hold stats for a QuicConnection. struct NET_EXPORT_PRIVATE QuicConnectionStats { QuicConnectionStats(); @@ -30,24 +20,61 @@ struct NET_EXPORT_PRIVATE QuicConnectionStats { NET_EXPORT_PRIVATE friend std::ostream& operator<<( std::ostream& os, const QuicConnectionStats& s); - uint64 bytes_sent; // includes retransmissions, fec. + uint64 bytes_sent; // Includes retransmissions, fec. uint32 packets_sent; uint64 stream_bytes_sent; // non-retransmitted bytes sent in a stream frame. + uint32 packets_discarded; // Packets serialized and discarded before sending. - uint64 bytes_received; // includes duplicate data for a stream, fec. - uint32 packets_received; // includes dropped packets - uint64 stream_bytes_received; // bytes received in a stream frame. + // These include version negotiation and public reset packets, which do not + // have sequence numbers or frame data. + uint64 bytes_received; // Includes duplicate data for a stream, fec. + uint32 packets_received; // Includes packets which were not processable. + uint32 packets_processed; // Excludes packets which were not processable. + uint64 stream_bytes_received; // Bytes received in a stream frame. uint64 bytes_retransmitted; uint32 packets_retransmitted; + uint64 bytes_spuriously_retransmitted; + uint32 packets_spuriously_retransmitted; + // Number of packets abandoned as lost by the loss detection algorithm. + uint32 packets_lost; + uint32 slowstart_packets_lost; // Number of packets lost exiting slow start. + uint32 packets_revived; - uint32 packets_dropped; // duplicate or less than least unacked. - uint32 rto_count; + uint32 packets_dropped; // Duplicate or less than least unacked. + uint32 crypto_retransmit_count; + // Count of times the loss detection alarm fired. At least one packet should + // be lost when the alarm fires. + uint32 loss_timeout_count; + uint32 tlp_count; + uint32 rto_count; // Count of times the rto timer fired. + + uint32 min_rtt_us; // Minimum RTT in microseconds. + uint32 srtt_us; // Smoothed RTT in microseconds. + uint32 max_packet_size; // In bytes. + uint64 estimated_bandwidth; // In bytes per second. + uint32 congestion_window; // In bytes + + // Reordering stats for received packets. + // Number of packets received out of sequence number order. + uint32 packets_reordered; + // Maximum reordering observed in sequence space. + uint32 max_sequence_reordering; + // Maximum reordering observed in microseconds + uint32 max_time_reordering_us; + + // The following stats are used only in TcpCubicSender. + // The number of loss events from TCP's perspective. Each loss event includes + // one or more lost packets. + uint32 tcp_loss_events; + // Total amount of cwnd increase by TCPCubic in congestion avoidance. + uint32 cwnd_increase_congestion_avoidance; + // Total amount of cwnd increase by TCPCubic in cubic mode. + uint32 cwnd_increase_cubic_mode; - uint32 rtt; // In microseconds - uint64 estimated_bandwidth; - // TODO(satyamshekhar): Add window_size, mss and mtu. + // Creation time, as reported by the QuicClock. + QuicTime connection_creation_time; }; } // namespace net diff --git a/chromium/net/quic/quic_connection_test.cc b/chromium/net/quic/quic_connection_test.cc index 39655d0fe2a..38ead0ca1c0 100644 --- a/chromium/net/quic/quic_connection_test.cc +++ b/chromium/net/quic/quic_connection_test.cc @@ -8,29 +8,33 @@ #include "base/bind.h" #include "base/stl_util.h" #include "net/base/net_errors.h" +#include "net/quic/congestion_control/loss_detection_interface.h" #include "net/quic/congestion_control/receive_algorithm_interface.h" #include "net/quic/congestion_control/send_algorithm_interface.h" #include "net/quic/crypto/null_encrypter.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_protocol.h" -#include "net/quic/quic_sent_packet_manager.h" #include "net/quic/quic_utils.h" #include "net/quic/test_tools/mock_clock.h" #include "net/quic/test_tools/mock_random.h" #include "net/quic/test_tools/quic_connection_peer.h" #include "net/quic/test_tools/quic_framer_peer.h" #include "net/quic/test_tools/quic_packet_creator_peer.h" +#include "net/quic/test_tools/quic_sent_packet_manager_peer.h" #include "net/quic/test_tools/quic_test_utils.h" +#include "net/quic/test_tools/simple_quic_framer.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using base::StringPiece; using std::map; using std::vector; -using testing::_; using testing::AnyNumber; +using testing::AtLeast; using testing::ContainerEq; +using testing::Contains; using testing::DoAll; using testing::InSequence; using testing::InvokeWithoutArgs; @@ -38,6 +42,7 @@ using testing::Ref; using testing::Return; using testing::SaveArg; using testing::StrictMock; +using testing::_; namespace net { namespace test { @@ -54,11 +59,6 @@ const QuicPacketEntropyHash kTestEntropyHash = 76; const int kDefaultRetransmissionTimeMs = 500; const int kMinRetransmissionTimeMs = 200; -// Used by TestConnection::SendStreamData3. -const QuicStreamId kStreamId3 = 3; -// Used by TestConnection::SendStreamData5. -const QuicStreamId kStreamId5 = 5; - class TestReceiveAlgorithm : public ReceiveAlgorithmInterface { public: explicit TestReceiveAlgorithm(QuicCongestionFeedbackFrame* feedback) @@ -74,8 +74,8 @@ class TestReceiveAlgorithm : public ReceiveAlgorithmInterface { return true; } - MOCK_METHOD4(RecordIncomingPacket, - void(QuicByteCount, QuicPacketSequenceNumber, QuicTime, bool)); + MOCK_METHOD3(RecordIncomingPacket, + void(QuicByteCount, QuicPacketSequenceNumber, QuicTime)); private: QuicCongestionFeedbackFrame* feedback_; @@ -142,6 +142,8 @@ class TaggingEncrypter : public QuicEncrypter { }; const uint8 tag_; + + DISALLOW_COPY_AND_ASSIGN(TaggingEncrypter); }; // TaggingDecrypter ensures that the final kTagSize bytes of the message all @@ -269,23 +271,24 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { class TestPacketWriter : public QuicPacketWriter { public: - TestPacketWriter() - : last_packet_size_(0), - blocked_(false), + explicit TestPacketWriter(QuicVersion version) + : version_(version), + framer_(SupportedVersions(version_)), + last_packet_size_(0), + write_blocked_(false), + block_on_next_write_(false), is_write_blocked_data_buffered_(false), - is_server_(true), final_bytes_of_last_packet_(0), final_bytes_of_previous_packet_(0), use_tagging_decrypter_(false), packets_write_attempts_(0) { } - // QuicPacketWriter + // QuicPacketWriter interface virtual WriteResult WritePacket( const char* buffer, size_t buf_len, const IPAddressNumber& self_address, - const IPEndPoint& peer_address, - QuicBlockedWriterInterface* blocked_writer) OVERRIDE { + const IPEndPoint& peer_address) OVERRIDE { QuicEncryptedPacket packet(buffer, buf_len); ++packets_write_attempts_; @@ -295,14 +298,15 @@ class TestPacketWriter : public QuicPacketWriter { sizeof(final_bytes_of_last_packet_)); } - QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), !is_server_); if (use_tagging_decrypter_) { - framer.SetDecrypter(new TaggingDecrypter); + framer_.framer()->SetDecrypter(new TaggingDecrypter, ENCRYPTION_NONE); } - visitor_.Reset(); - framer.set_visitor(&visitor_); - EXPECT_TRUE(framer.ProcessPacket(packet)); - if (blocked_) { + EXPECT_TRUE(framer_.ProcessPacket(packet)); + if (block_on_next_write_) { + write_blocked_ = true; + block_on_next_write_ = false; + } + if (IsWriteBlocked()) { return WriteResult(WRITE_STATUS_BLOCKED, -1); } last_packet_size_ = packet.length(); @@ -313,40 +317,57 @@ class TestPacketWriter : public QuicPacketWriter { return is_write_blocked_data_buffered_; } - // Resets the visitor's state by clearing out the headers and frames. - void Reset() { - visitor_.Reset(); - } + virtual bool IsWriteBlocked() const OVERRIDE { return write_blocked_; } + + virtual void SetWritable() OVERRIDE { write_blocked_ = false; } + + void BlockOnNextWrite() { block_on_next_write_ = true; } - QuicPacketHeader* header() { return visitor_.header(); } + const QuicPacketHeader& header() { return framer_.header(); } - size_t frame_count() const { return visitor_.frame_count(); } + size_t frame_count() const { return framer_.num_frames(); } - QuicAckFrame* ack() { return visitor_.ack(); } + const vector<QuicAckFrame>& ack_frames() const { + return framer_.ack_frames(); + } + + const vector<QuicCongestionFeedbackFrame>& feedback_frames() const { + return framer_.feedback_frames(); + } + + const vector<QuicStopWaitingFrame>& stop_waiting_frames() const { + return framer_.stop_waiting_frames(); + } - QuicCongestionFeedbackFrame* feedback() { return visitor_.feedback(); } + const vector<QuicConnectionCloseFrame>& connection_close_frames() const { + return framer_.connection_close_frames(); + } - QuicConnectionCloseFrame* close() { return visitor_.close(); } + const vector<QuicStreamFrame>& stream_frames() const { + return framer_.stream_frames(); + } - const vector<QuicStreamFrame>* stream_frames() const { - return visitor_.stream_frames(); + const vector<QuicPingFrame>& ping_frames() const { + return framer_.ping_frames(); } size_t last_packet_size() { return last_packet_size_; } - QuicVersionNegotiationPacket* version_negotiation_packet() { - return visitor_.version_negotiation_packet(); + const QuicVersionNegotiationPacket* version_negotiation_packet() { + return framer_.version_negotiation_packet(); } - void set_blocked(bool blocked) { blocked_ = blocked; } - void set_is_write_blocked_data_buffered(bool buffered) { is_write_blocked_data_buffered_ = buffered; } - void set_is_server(bool is_server) { is_server_ = is_server; } + void set_is_server(bool is_server) { + // We invert is_server here, because the framer needs to parse packets + // we send. + QuicFramerPeer::SetIsServer(framer_.framer(), !is_server); + } // final_bytes_of_last_packet_ returns the last four bytes of the previous // packet as a little-endian, uint32. This is intended to be used with a @@ -365,12 +386,19 @@ class TestPacketWriter : public QuicPacketWriter { uint32 packets_write_attempts() { return packets_write_attempts_; } + void Reset() { framer_.Reset(); } + + void SetSupportedVersions(const QuicVersionVector& versions) { + framer_.SetSupportedVersions(versions); + } + private: - FramerVisitorCapturingFrames visitor_; + QuicVersion version_; + SimpleQuicFramer framer_; size_t last_packet_size_; - bool blocked_; + bool write_blocked_; + bool block_on_next_write_; bool is_write_blocked_data_buffered_; - bool is_server_; uint32 final_bytes_of_last_packet_; uint32 final_bytes_of_previous_packet_; bool use_tagging_decrypter_; @@ -381,15 +409,18 @@ class TestPacketWriter : public QuicPacketWriter { class TestConnection : public QuicConnection { public: - TestConnection(QuicGuid guid, + TestConnection(QuicConnectionId connection_id, IPEndPoint address, TestConnectionHelper* helper, TestPacketWriter* writer, - bool is_server) - : QuicConnection(guid, address, helper, writer, is_server, - QuicSupportedVersions()), - helper_(helper), + bool is_server, + QuicVersion version) + : QuicConnection(connection_id, address, helper, writer, is_server, + SupportedVersions(version)), writer_(writer) { + // Disable tail loss probes for most tests. + QuicSentPacketManagerPeer::SetMaxTailLossProbes( + QuicConnectionPeer::GetSentPacketManager(this), 0); writer_->set_is_server(is_server); } @@ -405,6 +436,11 @@ class TestConnection : public QuicConnection { QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm); } + void SetLossAlgorithm(LossDetectionInterface* loss_algorithm) { + QuicSentPacketManagerPeer::SetLossAlgorithm( + QuicConnectionPeer::GetSentPacketManager(this), loss_algorithm); + } + void SendPacket(EncryptionLevel level, QuicPacketSequenceNumber sequence_number, QuicPacket* packet, @@ -424,19 +460,58 @@ class TestConnection : public QuicConnection { QuicStreamOffset offset, bool fin, QuicAckNotifier::DelegateInterface* delegate) { + return SendStreamDataWithStringHelper(id, data, offset, fin, + MAY_FEC_PROTECT, delegate); + } + + QuicConsumedData SendStreamDataWithStringWithFec( + QuicStreamId id, + StringPiece data, + QuicStreamOffset offset, + bool fin, + QuicAckNotifier::DelegateInterface* delegate) { + return SendStreamDataWithStringHelper(id, data, offset, fin, + MUST_FEC_PROTECT, delegate); + } + + QuicConsumedData SendStreamDataWithStringHelper( + QuicStreamId id, + StringPiece data, + QuicStreamOffset offset, + bool fin, + FecProtection fec_protection, + QuicAckNotifier::DelegateInterface* delegate) { IOVector data_iov; if (!data.empty()) { data_iov.Append(const_cast<char*>(data.data()), data.size()); } - return QuicConnection::SendStreamData(id, data_iov, offset, fin, delegate); + return QuicConnection::SendStreamData(id, data_iov, offset, fin, + fec_protection, delegate); } QuicConsumedData SendStreamData3() { - return SendStreamDataWithString(kStreamId3, "food", 0, !kFin, NULL); + return SendStreamDataWithString(kClientDataStreamId1, "food", 0, !kFin, + NULL); + } + + QuicConsumedData SendStreamData3WithFec() { + return SendStreamDataWithStringWithFec(kClientDataStreamId1, "food", 0, + !kFin, NULL); } QuicConsumedData SendStreamData5() { - return SendStreamDataWithString(kStreamId5, "food2", 0, !kFin, NULL); + return SendStreamDataWithString(kClientDataStreamId2, "food2", 0, + !kFin, NULL); + } + + QuicConsumedData SendStreamData5WithFec() { + return SendStreamDataWithStringWithFec(kClientDataStreamId2, "food2", 0, + !kFin, NULL); + } + // Ensures the connection can write stream data before writing. + QuicConsumedData EnsureWritableAndSendStreamData5() { + EXPECT_TRUE(CanWriteStreamData()); + return SendStreamData5(); } // The crypto stream has special semantics so that it is not blocked by a @@ -445,11 +520,7 @@ class TestConnection : public QuicConnection { // split needlessly across packet boundaries). As a result, we have separate // tests for some cases for this stream. QuicConsumedData SendCryptoStreamData() { - this->Flush(); - QuicConsumedData consumed = - SendStreamDataWithString(kCryptoStreamId, "chlo", 0, !kFin, NULL); - this->Flush(); - return consumed; + return SendStreamDataWithString(kCryptoStreamId, "chlo", 0, !kFin, NULL); } bool is_server() { @@ -457,13 +528,16 @@ class TestConnection : public QuicConnection { } void set_version(QuicVersion version) { - framer_.set_version(version); + QuicConnectionPeer::GetFramer(this)->set_version(version); + } + + void SetSupportedVersions(const QuicVersionVector& versions) { + QuicConnectionPeer::GetFramer(this)->SetSupportedVersions(versions); + writer_->SetSupportedVersions(versions); } void set_is_server(bool is_server) { writer_->set_is_server(is_server); - QuicPacketCreatorPeer::SetIsServer( - QuicConnectionPeer::GetPacketCreator(this), is_server); QuicConnectionPeer::SetIsServer(this, is_server); } @@ -472,6 +546,16 @@ class TestConnection : public QuicConnection { QuicConnectionPeer::GetAckAlarm(this)); } + TestConnectionHelper::TestAlarm* GetPingAlarm() { + return reinterpret_cast<TestConnectionHelper::TestAlarm*>( + QuicConnectionPeer::GetPingAlarm(this)); + } + + TestConnectionHelper::TestAlarm* GetResumeWritesAlarm() { + return reinterpret_cast<TestConnectionHelper::TestAlarm*>( + QuicConnectionPeer::GetResumeWritesAlarm(this)); + } + TestConnectionHelper::TestAlarm* GetRetransmissionAlarm() { return reinterpret_cast<TestConnectionHelper::TestAlarm*>( QuicConnectionPeer::GetRetransmissionAlarm(this)); @@ -482,11 +566,6 @@ class TestConnection : public QuicConnection { QuicConnectionPeer::GetSendAlarm(this)); } - TestConnectionHelper::TestAlarm* GetResumeWritesAlarm() { - return reinterpret_cast<TestConnectionHelper::TestAlarm*>( - QuicConnectionPeer::GetResumeWritesAlarm(this)); - } - TestConnectionHelper::TestAlarm* GetTimeoutAlarm() { return reinterpret_cast<TestConnectionHelper::TestAlarm*>( QuicConnectionPeer::GetTimeoutAlarm(this)); @@ -495,81 +574,95 @@ class TestConnection : public QuicConnection { using QuicConnection::SelectMutualVersion; private: - TestConnectionHelper* helper_; TestPacketWriter* writer_; DISALLOW_COPY_AND_ASSIGN(TestConnection); }; -class QuicConnectionTest : public ::testing::TestWithParam<bool> { +// Used for testing packets revived from FEC packets. +class FecQuicConnectionDebugVisitor + : public QuicConnectionDebugVisitor { + public: + virtual void OnRevivedPacket(const QuicPacketHeader& header, + StringPiece data) OVERRIDE { + revived_header_ = header; + } + + // Public accessor method. + QuicPacketHeader revived_header() const { + return revived_header_; + } + + private: + QuicPacketHeader revived_header_; +}; + +class QuicConnectionTest : public ::testing::TestWithParam<QuicVersion> { protected: QuicConnectionTest() - : guid_(42), - framer_(QuicSupportedVersions(), QuicTime::Zero(), false), - creator_(guid_, &framer_, &random_generator_, false), + : connection_id_(42), + framer_(SupportedVersions(version()), QuicTime::Zero(), false), + peer_creator_(connection_id_, &framer_, &random_generator_), send_algorithm_(new StrictMock<MockSendAlgorithm>), + loss_algorithm_(new MockLossAlgorithm()), helper_(new TestConnectionHelper(&clock_, &random_generator_)), - writer_(new TestPacketWriter()), - connection_(guid_, IPEndPoint(), helper_.get(), writer_.get(), false), + writer_(new TestPacketWriter(version())), + connection_(connection_id_, IPEndPoint(), helper_.get(), + writer_.get(), false, version()), frame1_(1, false, 0, MakeIOVector(data1)), frame2_(1, false, 3, MakeIOVector(data2)), - accept_packet_(true) { + sequence_number_length_(PACKET_6BYTE_SEQUENCE_NUMBER), + connection_id_length_(PACKET_8BYTE_CONNECTION_ID) { connection_.set_visitor(&visitor_); connection_.SetSendAlgorithm(send_algorithm_); + connection_.SetLossAlgorithm(loss_algorithm_); framer_.set_received_entropy_calculator(&entropy_calculator_); // Simplify tests by not sending feedback unless specifically configured. SetFeedback(NULL); EXPECT_CALL( - *send_algorithm_, TimeUntilSend(_, _, _, _)).WillRepeatedly(Return( + *send_algorithm_, TimeUntilSend(_, _, _)).WillRepeatedly(Return( QuicTime::Delta::Zero())); EXPECT_CALL(*receive_algorithm_, - RecordIncomingPacket(_, _, _, _)).Times(AnyNumber()); + RecordIncomingPacket(_, _, _)).Times(AnyNumber()); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) .Times(AnyNumber()); EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly( Return(QuicTime::Delta::Zero())); - EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillRepeatedly(Return( - QuicBandwidth::FromKBitsPerSecond(100))); - EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillRepeatedly(Return( - QuicTime::Delta::FromMilliseconds(100))); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly( + Return(kMaxPacketSize)); ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) .WillByDefault(Return(true)); + EXPECT_CALL(visitor_, WillingAndAbleToWrite()).Times(AnyNumber()); EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber()); - EXPECT_CALL(visitor_, OnCanWrite()).Times(AnyNumber()).WillRepeatedly( - Return(true)); - } - - QuicAckFrame* outgoing_ack() { - outgoing_ack_.reset(QuicConnectionPeer::CreateAckFrame(&connection_)); - return outgoing_ack_.get(); - } + EXPECT_CALL(visitor_, OnCanWrite()).Times(AnyNumber()); + EXPECT_CALL(visitor_, HasOpenDataStreams()).WillRepeatedly(Return(false)); - QuicAckFrame* last_ack() { - return writer_->ack(); + EXPECT_CALL(*loss_algorithm_, GetLossTimeout()) + .WillRepeatedly(Return(QuicTime::Zero())); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillRepeatedly(Return(SequenceNumberSet())); } - QuicCongestionFeedbackFrame* last_feedback() { - return writer_->feedback(); + QuicVersion version() { + return GetParam(); } - QuicConnectionCloseFrame* last_close() { - return writer_->close(); - } - - QuicPacketHeader* last_header() { - return writer_->header(); - } - - size_t last_sent_packet_size() { - return writer_->last_packet_size(); - } - - uint32 final_bytes_of_last_packet() { - return writer_->final_bytes_of_last_packet(); + QuicAckFrame* outgoing_ack() { + outgoing_ack_.reset(QuicConnectionPeer::CreateAckFrame(&connection_)); + return outgoing_ack_.get(); } - uint32 final_bytes_of_previous_packet() { - return writer_->final_bytes_of_previous_packet(); + QuicPacketSequenceNumber least_unacked() { + if (version() <= QUIC_VERSION_15) { + if (writer_->ack_frames().empty()) { + return 0; + } + return writer_->ack_frames()[0].sent_info.least_unacked; + } + if (writer_->stop_waiting_frames().empty()) { + return 0; + } + return writer_->stop_waiting_frames()[0].least_unacked; } void use_tagging_decrypter() { @@ -577,16 +670,17 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> { } void ProcessPacket(QuicPacketSequenceNumber number) { - EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_)); + EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1); ProcessDataPacket(number, 0, !kEntropyFlag); } QuicPacketEntropyHash ProcessFramePacket(QuicFrame frame) { QuicFrames frames; frames.push_back(QuicFrame(frame)); - QuicPacketCreatorPeer::SetSendVersionInPacket(&creator_, + QuicPacketCreatorPeer::SetSendVersionInPacket(&peer_creator_, connection_.is_server()); - SerializedPacket serialized_packet = creator_.SerializeAllFrames(frames); + SerializedPacket serialized_packet = + peer_creator_.SerializeAllFrames(frames); scoped_ptr<QuicPacket> packet(serialized_packet.packet); scoped_ptr<QuicEncryptedPacket> encrypted( framer_.EncryptPacket(ENCRYPTION_NONE, @@ -623,12 +717,12 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> { } size_t ProcessFecProtectedPacket(QuicPacketSequenceNumber number, - bool expect_revival, bool entropy_flag) { + bool expect_revival, bool entropy_flag) { if (expect_revival) { - EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_)); + EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1); } - EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_)) - .RetiresOnSaturation(); + EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1). + RetiresOnSaturation(); return ProcessDataPacket(number, 1, entropy_flag); } @@ -640,7 +734,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> { bool entropy_flag, QuicPacket* packet) { if (expect_revival) { - EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_)); + EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1); } // Construct the decrypted data packet so we can compute the correct @@ -653,12 +747,14 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> { data_packet.reset(ConstructDataPacket(number, 1, !kEntropyFlag)); } - header_.public_header.guid = guid_; + header_.public_header.connection_id = connection_id_; header_.public_header.reset_flag = false; header_.public_header.version_flag = false; + header_.public_header.sequence_number_length = sequence_number_length_; + header_.public_header.connection_id_length = connection_id_length_; + header_.packet_sequence_number = number; header_.entropy_flag = entropy_flag; header_.fec_flag = true; - header_.packet_sequence_number = number; header_.is_in_fec_group = IN_FEC_GROUP; header_.fec_group = min_protected_packet; QuicFecData fec_data; @@ -669,7 +765,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> { // with itself, depending on the number of packets. if (((number - min_protected_packet) % 2) == 0) { for (size_t i = GetStartOfFecProtectedData( - header_.public_header.guid_length, + header_.public_header.connection_id_length, header_.public_header.version_flag, header_.public_header.sequence_number_length); i < data_packet->length(); ++i) { @@ -694,7 +790,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> { QuicPacketSequenceNumber* last_packet) { QuicByteCount packet_size; EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) - .WillOnce(DoAll(SaveArg<2>(&packet_size), Return(true))); + .WillOnce(DoAll(SaveArg<3>(&packet_size), Return(true))); connection_.SendStreamDataWithString(id, data, offset, fin, NULL); if (last_packet != NULL) { *last_packet = @@ -716,6 +812,10 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> { return ProcessFramePacket(QuicFrame(frame)); } + QuicPacketEntropyHash ProcessStopWaitingPacket(QuicStopWaitingFrame* frame) { + return ProcessFramePacket(QuicFrame(frame)); + } + QuicPacketEntropyHash ProcessGoAwayPacket(QuicGoAwayFrame* frame) { return ProcessFramePacket(QuicFrame(frame)); } @@ -727,9 +827,11 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> { QuicPacket* ConstructDataPacket(QuicPacketSequenceNumber number, QuicFecGroupNumber fec_group, bool entropy_flag) { - header_.public_header.guid = guid_; + header_.public_header.connection_id = connection_id_; header_.public_header.reset_flag = false; header_.public_header.version_flag = false; + header_.public_header.sequence_number_length = sequence_number_length_; + header_.public_header.connection_id_length = connection_id_length_; header_.entropy_flag = entropy_flag; header_.fec_flag = false; header_.packet_sequence_number = number; @@ -740,14 +842,14 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> { QuicFrame frame(&frame1_); frames.push_back(frame); QuicPacket* packet = - framer_.BuildUnsizedDataPacket(header_, frames).packet; + BuildUnsizedDataPacket(&framer_, header_, frames).packet; EXPECT_TRUE(packet != NULL); return packet; } QuicPacket* ConstructClosePacket(QuicPacketSequenceNumber number, QuicFecGroupNumber fec_group) { - header_.public_header.guid = guid_; + header_.public_header.connection_id = connection_id_; header_.packet_sequence_number = number; header_.public_header.reset_flag = false; header_.public_header.version_flag = false; @@ -763,7 +865,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> { QuicFrame frame(&qccf); frames.push_back(frame); QuicPacket* packet = - framer_.BuildUnsizedDataPacket(header_, frames).packet; + BuildUnsizedDataPacket(&framer_, header_, frames).packet; EXPECT_TRUE(packet != NULL); return packet; } @@ -781,12 +883,81 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> { return QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs/2); } - QuicGuid guid_; + // Initialize a frame acknowledging all packets up to largest_observed. + const QuicAckFrame InitAckFrame(QuicPacketSequenceNumber largest_observed, + QuicPacketSequenceNumber least_unacked) { + QuicAckFrame frame(MakeAckFrame(largest_observed, least_unacked)); + if (largest_observed > 0) { + frame.received_info.entropy_hash = + QuicConnectionPeer::GetSentEntropyHash(&connection_, largest_observed); + } + return frame; + } + + const QuicStopWaitingFrame InitStopWaitingFrame( + QuicPacketSequenceNumber least_unacked) { + QuicStopWaitingFrame frame; + frame.least_unacked = least_unacked; + return frame; + } + // Explicitly nack a packet. + void NackPacket(QuicPacketSequenceNumber missing, QuicAckFrame* frame) { + frame->received_info.missing_packets.insert(missing); + frame->received_info.entropy_hash ^= + QuicConnectionPeer::GetSentEntropyHash(&connection_, missing); + if (missing > 1) { + frame->received_info.entropy_hash ^= + QuicConnectionPeer::GetSentEntropyHash(&connection_, missing - 1); + } + } + + // Undo nacking a packet within the frame. + void AckPacket(QuicPacketSequenceNumber arrived, QuicAckFrame* frame) { + EXPECT_THAT(frame->received_info.missing_packets, Contains(arrived)); + frame->received_info.missing_packets.erase(arrived); + frame->received_info.entropy_hash ^= + QuicConnectionPeer::GetSentEntropyHash(&connection_, arrived); + if (arrived > 1) { + frame->received_info.entropy_hash ^= + QuicConnectionPeer::GetSentEntropyHash(&connection_, arrived - 1); + } + } + + void TriggerConnectionClose() { + // Send an erroneous packet to close the connection. + EXPECT_CALL(visitor_, + OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false)); + // Call ProcessDataPacket rather than ProcessPacket, as we should not get a + // packet call to the visitor. + ProcessDataPacket(6000, 0, !kEntropyFlag); + EXPECT_FALSE( + QuicConnectionPeer::GetConnectionClosePacket(&connection_) == NULL); + } + + void BlockOnNextWrite() { + writer_->BlockOnNextWrite(); + EXPECT_CALL(visitor_, OnWriteBlocked()).Times(AtLeast(1)); + } + + void CongestionBlockWrites() { + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, _, _)).WillRepeatedly( + testing::Return(QuicTime::Delta::FromSeconds(1))); + } + + void CongestionUnblockWrites() { + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, _, _)).WillRepeatedly( + testing::Return(QuicTime::Delta::Zero())); + } + + QuicConnectionId connection_id_; QuicFramer framer_; - QuicPacketCreator creator_; + QuicPacketCreator peer_creator_; MockEntropyCalculator entropy_calculator_; MockSendAlgorithm* send_algorithm_; + MockLossAlgorithm* loss_algorithm_; TestReceiveAlgorithm* receive_algorithm_; MockClock clock_; MockRandom random_generator_; @@ -799,13 +970,19 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> { QuicStreamFrame frame1_; QuicStreamFrame frame2_; scoped_ptr<QuicAckFrame> outgoing_ack_; - bool accept_packet_; + QuicSequenceNumberLength sequence_number_length_; + QuicConnectionIdLength connection_id_length_; private: DISALLOW_COPY_AND_ASSIGN(QuicConnectionTest); }; -TEST_F(QuicConnectionTest, PacketsInOrder) { +// Run all end to end tests with all supported versions. +INSTANTIATE_TEST_CASE_P(SupportedVersion, + QuicConnectionTest, + ::testing::ValuesIn(QuicSupportedVersions())); + +TEST_P(QuicConnectionTest, PacketsInOrder) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessPacket(1); @@ -821,21 +998,7 @@ TEST_F(QuicConnectionTest, PacketsInOrder) { EXPECT_EQ(0u, outgoing_ack()->received_info.missing_packets.size()); } -TEST_F(QuicConnectionTest, PacketsRejected) { - EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - - ProcessPacket(1); - EXPECT_EQ(1u, outgoing_ack()->received_info.largest_observed); - EXPECT_EQ(0u, outgoing_ack()->received_info.missing_packets.size()); - - accept_packet_ = false; - ProcessPacket(2); - // We should not have an ack for two. - EXPECT_EQ(1u, outgoing_ack()->received_info.largest_observed); - EXPECT_EQ(0u, outgoing_ack()->received_info.missing_packets.size()); -} - -TEST_F(QuicConnectionTest, PacketsOutOfOrder) { +TEST_P(QuicConnectionTest, PacketsOutOfOrder) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessPacket(3); @@ -854,7 +1017,7 @@ TEST_F(QuicConnectionTest, PacketsOutOfOrder) { EXPECT_FALSE(IsMissing(1)); } -TEST_F(QuicConnectionTest, DuplicatePacket) { +TEST_P(QuicConnectionTest, DuplicatePacket) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessPacket(3); @@ -870,7 +1033,7 @@ TEST_F(QuicConnectionTest, DuplicatePacket) { EXPECT_TRUE(IsMissing(1)); } -TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) { +TEST_P(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessPacket(3); @@ -891,8 +1054,9 @@ TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) { // packet the peer will not retransmit. It indicates this by sending 'least // awaiting' is 4. The connection should then realize 1 will not be // retransmitted, and will remove it from the missing list. - creator_.set_sequence_number(5); - QuicAckFrame frame(0, QuicTime::Zero(), 4); + peer_creator_.set_sequence_number(5); + QuicAckFrame frame = InitAckFrame(1, 4); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _)); ProcessAckPacket(&frame); // Force an ack to be sent. @@ -900,31 +1064,53 @@ TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) { EXPECT_TRUE(IsMissing(4)); } -TEST_F(QuicConnectionTest, RejectPacketTooFarOut) { +TEST_P(QuicConnectionTest, RejectPacketTooFarOut) { + EXPECT_CALL(visitor_, + OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false)); // Call ProcessDataPacket rather than ProcessPacket, as we should not get a // packet call to the visitor. - EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false)); ProcessDataPacket(6000, 0, !kEntropyFlag); + EXPECT_FALSE( + QuicConnectionPeer::GetConnectionClosePacket(&connection_) == NULL); } -TEST_F(QuicConnectionTest, TruncatedAck) { +TEST_P(QuicConnectionTest, RejectUnencryptedStreamData) { + // Process an unencrypted packet from the non-crypto stream. + frame1_.stream_id = 3; + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_UNENCRYPTED_STREAM_DATA, + false)); + ProcessDataPacket(1, 0, !kEntropyFlag); + EXPECT_FALSE( + QuicConnectionPeer::GetConnectionClosePacket(&connection_) == NULL); + const vector<QuicConnectionCloseFrame>& connection_close_frames = + writer_->connection_close_frames(); + EXPECT_EQ(1u, connection_close_frames.size()); + EXPECT_EQ(QUIC_UNENCRYPTED_STREAM_DATA, + connection_close_frames[0].error_code); +} + +TEST_P(QuicConnectionTest, TruncatedAck) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); QuicPacketSequenceNumber num_packets = 256 * 2 + 1; for (QuicPacketSequenceNumber i = 0; i < num_packets; ++i) { - SendStreamDataToPeer(1, "foo", i * 3, !kFin, NULL); + SendStreamDataToPeer(3, "foo", i * 3, !kFin, NULL); } - QuicAckFrame frame(num_packets, QuicTime::Zero(), 1); + QuicAckFrame frame = InitAckFrame(num_packets, 1); + SequenceNumberSet lost_packets; // Create an ack with 256 nacks, none adjacent to one another. for (QuicPacketSequenceNumber i = 1; i <= 256; ++i) { - frame.received_info.missing_packets.insert(i * 2); + NackPacket(i * 2, &frame); + if (i < 256) { // Last packet is nacked, but not lost. + lost_packets.insert(i * 2); + } } - frame.received_info.entropy_hash = 0; + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(lost_packets)); EXPECT_CALL(entropy_calculator_, EntropyHash(511)).WillOnce(testing::Return(0)); - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(256); - EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); ProcessAckPacket(&frame); QuicReceivedPacketManager* received_packet_manager = @@ -933,40 +1119,40 @@ TEST_F(QuicConnectionTest, TruncatedAck) { EXPECT_GT(num_packets, received_packet_manager->peer_largest_observed_packet()); - frame.received_info.missing_packets.erase(192); - frame.received_info.entropy_hash = 2; + AckPacket(192, &frame); - // Removing one missing packet allows us to ack 192 and one more range. - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(2); - EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2); + // Removing one missing packet allows us to ack 192 and one more range, but + // 192 has already been declared lost, so it doesn't register as an ack. + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(SequenceNumberSet())); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); ProcessAckPacket(&frame); EXPECT_EQ(num_packets, received_packet_manager->peer_largest_observed_packet()); } -TEST_F(QuicConnectionTest, AckReceiptCausesAckSendBadEntropy) { +TEST_P(QuicConnectionTest, AckReceiptCausesAckSendBadEntropy) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessPacket(1); // Delay sending, then queue up an ack. EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + TimeUntilSend(_, _, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(1))); QuicConnectionPeer::SendAck(&connection_); // Process an ack with a least unacked of the received ack. // This causes an ack to be sent when TimeUntilSend returns 0. EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly( + TimeUntilSend(_, _, _)).WillRepeatedly( testing::Return(QuicTime::Delta::Zero())); // Skip a packet and then record an ack. - creator_.set_sequence_number(2); - QuicAckFrame frame(0, QuicTime::Zero(), 3); + peer_creator_.set_sequence_number(2); + QuicAckFrame frame = InitAckFrame(0, 3); ProcessAckPacket(&frame); } -TEST_F(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) { +TEST_P(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessPacket(3); @@ -986,52 +1172,70 @@ TEST_F(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) { EXPECT_EQ(3u, writer_->packets_write_attempts()); } -TEST_F(QuicConnectionTest, AckReceiptCausesAckSend) { +TEST_P(QuicConnectionTest, AckReceiptCausesAckSend) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1); + QuicPacketSequenceNumber original; QuicByteCount packet_size; - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)) - .WillOnce(DoAll(SaveArg<1>(&original), SaveArg<2>(&packet_size), + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(DoAll(SaveArg<2>(&original), SaveArg<3>(&packet_size), Return(true))); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1); connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL); - QuicAckFrame frame(original, QuicTime::Zero(), 1); - frame.received_info.missing_packets.insert(original); - frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash( - &connection_, original - 1); + QuicAckFrame frame = InitAckFrame(original, 1); + NackPacket(original, &frame); // First nack triggers early retransmit. + SequenceNumberSet lost_packets; + lost_packets.insert(1); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); QuicPacketSequenceNumber retransmission; EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, packet_size - kQuicVersionSize, - NACK_RETRANSMISSION, _)) - .WillOnce(DoAll(SaveArg<1>(&retransmission), Return(true))); + OnPacketSent(_, _, _, packet_size - kQuicVersionSize, _)) + .WillOnce(DoAll(SaveArg<2>(&retransmission), Return(true))); ProcessAckPacket(&frame); - QuicAckFrame frame2(retransmission, QuicTime::Zero(), 1); - frame2.received_info.missing_packets.insert(original); - frame2.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, retransmission) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, original); - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)); - + QuicAckFrame frame2 = InitAckFrame(retransmission, 1); + NackPacket(original, &frame2); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(SequenceNumberSet())); ProcessAckPacket(&frame2); + // Now if the peer sends an ack which still reports the retransmitted packet - // as missing, then that will count as a packet which instigates an ack. - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)); - ProcessAckPacket(&frame2); + // as missing, that will bundle an ack with data after two acks in a row + // indicate the high water mark needs to be raised. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, + HAS_RETRANSMITTABLE_DATA)); + connection_.SendStreamDataWithString(3, "foo", 3, !kFin, NULL); + // No ack sent. + EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_EQ(1u, writer_->stream_frames().size()); + + // No more packet loss for the rest of the test. + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillRepeatedly(Return(SequenceNumberSet())); ProcessAckPacket(&frame2); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, + HAS_RETRANSMITTABLE_DATA)); + connection_.SendStreamDataWithString(3, "foo", 3, !kFin, NULL); + // Ack bundled. + if (version() > QUIC_VERSION_15) { + EXPECT_EQ(3u, writer_->frame_count()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + } + EXPECT_EQ(1u, writer_->stream_frames().size()); + EXPECT_FALSE(writer_->ack_frames().empty()); // But an ack with no missing packets will not send an ack. - frame2.received_info.missing_packets.clear(); - frame2.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, retransmission); + AckPacket(original, &frame2); ProcessAckPacket(&frame2); ProcessAckPacket(&frame2); } -TEST_F(QuicConnectionTest, LeastUnackedLower) { +TEST_P(QuicConnectionTest, LeastUnackedLower) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); @@ -1039,355 +1243,372 @@ TEST_F(QuicConnectionTest, LeastUnackedLower) { SendStreamDataToPeer(1, "eep", 6, !kFin, NULL); // Start out saying the least unacked is 2. - creator_.set_sequence_number(5); - QuicAckFrame frame(0, QuicTime::Zero(), 2); - ProcessAckPacket(&frame); + peer_creator_.set_sequence_number(5); + if (version() > QUIC_VERSION_15) { + QuicStopWaitingFrame frame = InitStopWaitingFrame(2); + ProcessStopWaitingPacket(&frame); + } else { + QuicAckFrame frame = InitAckFrame(0, 2); + ProcessAckPacket(&frame); + } // Change it to 1, but lower the sequence number to fake out-of-order packets. // This should be fine. - creator_.set_sequence_number(1); - QuicAckFrame frame2(0, QuicTime::Zero(), 1); - // The scheduler will not process out of order acks. - EXPECT_CALL(visitor_, OnCanWrite()).Times(0); - ProcessAckPacket(&frame2); + peer_creator_.set_sequence_number(1); + // The scheduler will not process out of order acks, but all packet processing + // causes the connection to try to write. + EXPECT_CALL(visitor_, OnCanWrite()); + if (version() > QUIC_VERSION_15) { + QuicStopWaitingFrame frame2 = InitStopWaitingFrame(1); + ProcessStopWaitingPacket(&frame2); + } else { + QuicAckFrame frame2 = InitAckFrame(0, 1); + 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_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false)); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); - creator_.set_sequence_number(7); - ProcessAckPacket(&frame2); + peer_creator_.set_sequence_number(7); + if (version() > QUIC_VERSION_15) { + EXPECT_CALL(visitor_, + OnConnectionClosed(QUIC_INVALID_STOP_WAITING_DATA, false)); + QuicStopWaitingFrame frame2 = InitStopWaitingFrame(1); + ProcessStopWaitingPacket(&frame2); + } else { + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false)); + QuicAckFrame frame2 = InitAckFrame(0, 1); + ProcessAckPacket(&frame2); + } } -TEST_F(QuicConnectionTest, LargestObservedLower) { +TEST_P(QuicConnectionTest, LargestObservedLower) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); SendStreamDataToPeer(1, "bar", 3, !kFin, NULL); SendStreamDataToPeer(1, "eep", 6, !kFin, NULL); - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(2); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); // Start out saying the largest observed is 2. - QuicAckFrame frame(2, QuicTime::Zero(), 0); - frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash( - &connection_, 2); - ProcessAckPacket(&frame); + QuicAckFrame frame1 = InitAckFrame(1, 0); + QuicAckFrame frame2 = InitAckFrame(2, 0); + ProcessAckPacket(&frame2); // Now change it to 1, and it should cause a connection error. - QuicAckFrame frame2(1, QuicTime::Zero(), 0); EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false)); EXPECT_CALL(visitor_, OnCanWrite()).Times(0); - ProcessAckPacket(&frame2); + ProcessAckPacket(&frame1); } -TEST_F(QuicConnectionTest, AckUnsentData) { +TEST_P(QuicConnectionTest, AckUnsentData) { // Ack a packet which has not been sent. EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false)); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); - QuicAckFrame frame(1, QuicTime::Zero(), 0); + QuicAckFrame frame(MakeAckFrame(1, 0)); EXPECT_CALL(visitor_, OnCanWrite()).Times(0); ProcessAckPacket(&frame); } -TEST_F(QuicConnectionTest, AckAll) { +TEST_P(QuicConnectionTest, AckAll) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessPacket(1); - creator_.set_sequence_number(1); - QuicAckFrame frame1(0, QuicTime::Zero(), 1); + peer_creator_.set_sequence_number(1); + QuicAckFrame frame1 = InitAckFrame(0, 1); ProcessAckPacket(&frame1); } -TEST_F(QuicConnectionTest, SendingDifferentSequenceNumberLengthsBandwidth) { - EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(Return( - QuicBandwidth::FromKBitsPerSecond(1000))); - +TEST_P(QuicConnectionTest, SendingDifferentSequenceNumberLengthsBandwidth) { QuicPacketSequenceNumber last_packet; + QuicPacketCreator* creator = + QuicConnectionPeer::GetPacketCreator(&connection_); SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); EXPECT_EQ(1u, last_packet); EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, - connection_.options()->send_sequence_number_length); + creator->next_sequence_number_length()); EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, - last_header()->public_header.sequence_number_length); + writer_->header().public_header.sequence_number_length); - EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(Return( - QuicBandwidth::FromKBitsPerSecond(1000 * 256))); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly( + Return(kMaxPacketSize * 256)); SendStreamDataToPeer(1, "bar", 3, !kFin, &last_packet); EXPECT_EQ(2u, last_packet); EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER, - connection_.options()->send_sequence_number_length); + creator->next_sequence_number_length()); // The 1 packet lag is due to the sequence number length being recalculated in // QuicConnection after a packet is sent. EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, - last_header()->public_header.sequence_number_length); + writer_->header().public_header.sequence_number_length); - EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(Return( - QuicBandwidth::FromKBitsPerSecond(1000 * 256 * 256))); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly( + Return(kMaxPacketSize * 256 * 256)); SendStreamDataToPeer(1, "foo", 6, !kFin, &last_packet); EXPECT_EQ(3u, last_packet); EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, - connection_.options()->send_sequence_number_length); + creator->next_sequence_number_length()); EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER, - last_header()->public_header.sequence_number_length); + writer_->header().public_header.sequence_number_length); - EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(Return( - QuicBandwidth::FromKBitsPerSecond(1000ll * 256 * 256 * 256))); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly( + Return(kMaxPacketSize * 256 * 256 * 256)); SendStreamDataToPeer(1, "bar", 9, !kFin, &last_packet); EXPECT_EQ(4u, last_packet); EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, - connection_.options()->send_sequence_number_length); + creator->next_sequence_number_length()); EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, - last_header()->public_header.sequence_number_length); + writer_->header().public_header.sequence_number_length); - EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(Return( - QuicBandwidth::FromKBitsPerSecond(1000ll * 256 * 256 * 256 * 256))); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly( + Return(kMaxPacketSize * 256 * 256 * 256 * 256)); SendStreamDataToPeer(1, "foo", 12, !kFin, &last_packet); EXPECT_EQ(5u, last_packet); EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER, - connection_.options()->send_sequence_number_length); + creator->next_sequence_number_length()); EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, - last_header()->public_header.sequence_number_length); + writer_->header().public_header.sequence_number_length); } -TEST_F(QuicConnectionTest, SendingDifferentSequenceNumberLengthsUnackedDelta) { +TEST_P(QuicConnectionTest, SendingDifferentSequenceNumberLengthsUnackedDelta) { QuicPacketSequenceNumber last_packet; + QuicPacketCreator* creator = + QuicConnectionPeer::GetPacketCreator(&connection_); SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); EXPECT_EQ(1u, last_packet); EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, - connection_.options()->send_sequence_number_length); + creator->next_sequence_number_length()); EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, - last_header()->public_header.sequence_number_length); + writer_->header().public_header.sequence_number_length); - QuicConnectionPeer::GetPacketCreator(&connection_)->set_sequence_number(100); + creator->set_sequence_number(100); SendStreamDataToPeer(1, "bar", 3, !kFin, &last_packet); EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER, - connection_.options()->send_sequence_number_length); + creator->next_sequence_number_length()); EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, - last_header()->public_header.sequence_number_length); + writer_->header().public_header.sequence_number_length); - QuicConnectionPeer::GetPacketCreator(&connection_)->set_sequence_number( - 100 * 256); + creator->set_sequence_number(100 * 256); SendStreamDataToPeer(1, "foo", 6, !kFin, &last_packet); EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, - connection_.options()->send_sequence_number_length); + creator->next_sequence_number_length()); EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER, - last_header()->public_header.sequence_number_length); + writer_->header().public_header.sequence_number_length); - QuicConnectionPeer::GetPacketCreator(&connection_)->set_sequence_number( - 100 * 256 * 256); + creator->set_sequence_number(100 * 256 * 256); SendStreamDataToPeer(1, "bar", 9, !kFin, &last_packet); EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, - connection_.options()->send_sequence_number_length); + creator->next_sequence_number_length()); EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, - last_header()->public_header.sequence_number_length); + writer_->header().public_header.sequence_number_length); - QuicConnectionPeer::GetPacketCreator(&connection_)->set_sequence_number( - 100 * 256 * 256 * 256); + creator->set_sequence_number(100 * 256 * 256 * 256); SendStreamDataToPeer(1, "foo", 12, !kFin, &last_packet); EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER, - connection_.options()->send_sequence_number_length); + creator->next_sequence_number_length()); EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, - last_header()->public_header.sequence_number_length); + writer_->header().public_header.sequence_number_length); } -TEST_F(QuicConnectionTest, BasicSending) { +TEST_P(QuicConnectionTest, BasicSending) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(6); QuicPacketSequenceNumber last_packet; SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1 EXPECT_EQ(1u, last_packet); SendAckPacketToPeer(); // Packet 2 - EXPECT_EQ(1u, last_ack()->sent_info.least_unacked); + EXPECT_EQ(1u, least_unacked()); SendAckPacketToPeer(); // Packet 3 - EXPECT_EQ(1u, last_ack()->sent_info.least_unacked); + EXPECT_EQ(1u, least_unacked()); SendStreamDataToPeer(1, "bar", 3, !kFin, &last_packet); // Packet 4 EXPECT_EQ(4u, last_packet); SendAckPacketToPeer(); // Packet 5 - EXPECT_EQ(1u, last_ack()->sent_info.least_unacked); + EXPECT_EQ(1u, least_unacked()); + + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); // Peer acks up to packet 3. - QuicAckFrame frame(3, QuicTime::Zero(), 0); - frame.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 3); + QuicAckFrame frame = InitAckFrame(3, 0); ProcessAckPacket(&frame); SendAckPacketToPeer(); // Packet 6 // As soon as we've acked one, we skip ack packets 2 and 3 and note lack of // ack for 4. - EXPECT_EQ(4u, last_ack()->sent_info.least_unacked); + EXPECT_EQ(4u, least_unacked()); + + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); // Peer acks up to packet 4, the last packet. - QuicAckFrame frame2(6, QuicTime::Zero(), 0); - frame2.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 6); + QuicAckFrame frame2 = InitAckFrame(6, 0); ProcessAckPacket(&frame2); // Acks don't instigate acks. // Verify that we did not send an ack. - EXPECT_EQ(6u, last_header()->packet_sequence_number); + EXPECT_EQ(6u, writer_->header().packet_sequence_number); // So the last ack has not changed. - EXPECT_EQ(4u, last_ack()->sent_info.least_unacked); + EXPECT_EQ(4u, least_unacked()); // If we force an ack, we shouldn't change our retransmit state. SendAckPacketToPeer(); // Packet 7 - EXPECT_EQ(7u, last_ack()->sent_info.least_unacked); + EXPECT_EQ(7u, least_unacked()); // But if we send more data it should. SendStreamDataToPeer(1, "eep", 6, !kFin, &last_packet); // Packet 8 EXPECT_EQ(8u, last_packet); SendAckPacketToPeer(); // Packet 9 - EXPECT_EQ(8u, last_ack()->sent_info.least_unacked); + EXPECT_EQ(7u, least_unacked()); } -TEST_F(QuicConnectionTest, FECSending) { +TEST_P(QuicConnectionTest, FECSending) { // All packets carry version info till version is negotiated. + QuicPacketCreator* creator = + QuicConnectionPeer::GetPacketCreator(&connection_); size_t payload_length; - connection_.options()->max_packet_length = - GetPacketLengthForOneStream( + // GetPacketLengthForOneStream() assumes a stream offset of 0 in determining + // packet length. The size of the offset field in a stream frame is 0 for + // offset 0, and 2 for non-zero offsets up through 64K. Increase + // max_packet_length by 2 so that subsequent packets containing subsequent + // stream frames with non-zero offets will fit within the packet length. + size_t length = 2 + GetPacketLengthForOneStream( connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER, IN_FEC_GROUP, &payload_length); - // And send FEC every two packets. - connection_.options()->max_packets_per_fec_group = 2; + creator->set_max_packet_length(length); + + // Enable FEC. + creator->set_max_packets_per_fec_group(2); - // Send 4 data packets and 2 FEC packets. + // Send 4 protected data packets, which will also trigger 2 FEC packets. EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(6); - // The first stream frame will consume 2 fewer bytes than the other three. - const string payload(payload_length * 4 - 6, 'a'); - connection_.SendStreamDataWithString(1, payload, 0, !kFin, NULL); + // The first stream frame will have 2 fewer overhead bytes than the other 3. + const string payload(payload_length * 4 + 2, 'a'); + connection_.SendStreamDataWithStringWithFec(1, payload, 0, !kFin, NULL); // Expect the FEC group to be closed after SendStreamDataWithString. - EXPECT_FALSE(creator_.ShouldSendFec(true)); + EXPECT_FALSE(creator->IsFecGroupOpen()); + EXPECT_FALSE(creator->IsFecProtected()); } -TEST_F(QuicConnectionTest, FECQueueing) { +TEST_P(QuicConnectionTest, FECQueueing) { // All packets carry version info till version is negotiated. size_t payload_length; - connection_.options()->max_packet_length = - GetPacketLengthForOneStream( - connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER, - IN_FEC_GROUP, &payload_length); - // And send FEC every two packets. - connection_.options()->max_packets_per_fec_group = 2; + QuicPacketCreator* creator = + QuicConnectionPeer::GetPacketCreator(&connection_); + size_t length = GetPacketLengthForOneStream( + connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER, + IN_FEC_GROUP, &payload_length); + creator->set_max_packet_length(length); + // Enable FEC. + creator->set_max_packets_per_fec_group(1); EXPECT_EQ(0u, connection_.NumQueuedPackets()); - writer_->set_blocked(true); + BlockOnNextWrite(); const string payload(payload_length, 'a'); - connection_.SendStreamDataWithString(1, payload, 0, !kFin, NULL); - EXPECT_FALSE(creator_.ShouldSendFec(true)); + connection_.SendStreamDataWithStringWithFec(1, payload, 0, !kFin, NULL); + EXPECT_FALSE(creator->IsFecGroupOpen()); + EXPECT_FALSE(creator->IsFecProtected()); // Expect the first data packet and the fec packet to be queued. EXPECT_EQ(2u, connection_.NumQueuedPackets()); } -TEST_F(QuicConnectionTest, AbandonFECFromCongestionWindow) { - connection_.options()->max_packets_per_fec_group = 1; +TEST_P(QuicConnectionTest, AbandonFECFromCongestionWindow) { + // Enable FEC. + QuicConnectionPeer::GetPacketCreator( + &connection_)->set_max_packets_per_fec_group(1); + // 1 Data and 1 FEC packet. EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); - connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL); + connection_.SendStreamDataWithStringWithFec(3, "foo", 0, !kFin, NULL); const QuicTime::Delta retransmission_time = QuicTime::Delta::FromMilliseconds(5000); clock_.AdvanceTime(retransmission_time); // Abandon FEC packet and data packet. - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2); - EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout()); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); EXPECT_CALL(visitor_, OnCanWrite()); connection_.OnRetransmissionTimeout(); } -TEST_F(QuicConnectionTest, DontAbandonAckedFEC) { +TEST_P(QuicConnectionTest, DontAbandonAckedFEC) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - connection_.options()->max_packets_per_fec_group = 1; + // Enable FEC. + QuicConnectionPeer::GetPacketCreator( + &connection_)->set_max_packets_per_fec_group(1); // 1 Data and 1 FEC packet. EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(6); - connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL); + connection_.SendStreamDataWithStringWithFec(3, "foo", 0, !kFin, NULL); // Send some more data afterwards to ensure early retransmit doesn't trigger. - connection_.SendStreamDataWithString(1, "foo", 3, !kFin, NULL); - connection_.SendStreamDataWithString(1, "foo", 6, !kFin, NULL); + connection_.SendStreamDataWithStringWithFec(3, "foo", 3, !kFin, NULL); + connection_.SendStreamDataWithStringWithFec(3, "foo", 6, !kFin, NULL); - QuicAckFrame ack_fec(2, QuicTime::Zero(), 1); + QuicAckFrame ack_fec = InitAckFrame(2, 1); // Data packet missing. // TODO(ianswett): Note that this is not a sensible ack, since if the FEC was // received, it would cause the covered packet to be acked as well. - ack_fec.received_info.missing_packets.insert(1); - ack_fec.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); - - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1); - + NackPacket(1, &ack_fec); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); ProcessAckPacket(&ack_fec); - clock_.AdvanceTime(DefaultRetransmissionTime()); // Don't abandon the acked FEC packet, but it will abandon 2 the subsequent // FEC packets. - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(5); - EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout()); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3); connection_.GetRetransmissionAlarm()->Fire(); } -TEST_F(QuicConnectionTest, DontAbandonAllFEC) { +TEST_P(QuicConnectionTest, AbandonAllFEC) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - connection_.options()->max_packets_per_fec_group = 1; + // Enable FEC. + QuicConnectionPeer::GetPacketCreator( + &connection_)->set_max_packets_per_fec_group(1); // 1 Data and 1 FEC packet. EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(6); - connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL); + connection_.SendStreamDataWithStringWithFec(3, "foo", 0, !kFin, NULL); // Send some more data afterwards to ensure early retransmit doesn't trigger. - connection_.SendStreamDataWithString(1, "foo", 3, !kFin, NULL); + connection_.SendStreamDataWithStringWithFec(3, "foo", 3, !kFin, NULL); // Advance the time so not all the FEC packets are abandoned. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); - connection_.SendStreamDataWithString(1, "foo", 6, !kFin, NULL); + connection_.SendStreamDataWithStringWithFec(3, "foo", 6, !kFin, NULL); - QuicAckFrame ack_fec(5, QuicTime::Zero(), 1); + QuicAckFrame ack_fec = InitAckFrame(5, 1); // Ack all data packets, but no fec packets. - ack_fec.received_info.missing_packets.insert(2); - ack_fec.received_info.missing_packets.insert(4); - ack_fec.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 5) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 4) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); + NackPacket(2, &ack_fec); + NackPacket(4, &ack_fec); // Lose the first FEC packet and ack the three data packets. - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(3); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(2, _)); - EXPECT_CALL(*send_algorithm_, OnPacketLost(2, _)); + SequenceNumberSet lost_packets; + lost_packets.insert(2); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); ProcessAckPacket(&ack_fec); clock_.AdvanceTime(DefaultRetransmissionTime().Subtract( QuicTime::Delta::FromMilliseconds(1))); - // Don't abandon the acked FEC packet, but it will abandon 1 of the subsequent - // FEC packets. - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(4, _)); + // Abandon all packets + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(false)); connection_.GetRetransmissionAlarm()->Fire(); - // Ensure the connection's alarm is still set, in order to abandon the third - // FEC packet. - EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + // Ensure the alarm is not set since all packets have been abandoned. + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); } -TEST_F(QuicConnectionTest, FramePacking) { - // Block the connection. - connection_.GetSendAlarm()->Set( - clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1))); +TEST_P(QuicConnectionTest, FramePacking) { + CongestionBlockWrites(); // Send an ack and two stream frames in 1 packet by queueing them. connection_.SendAck(); @@ -1395,30 +1616,30 @@ TEST_F(QuicConnectionTest, FramePacking) { IgnoreResult(InvokeWithoutArgs(&connection_, &TestConnection::SendStreamData3)), IgnoreResult(InvokeWithoutArgs(&connection_, - &TestConnection::SendStreamData5)), - Return(true))); + &TestConnection::SendStreamData5)))); - EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)) - .Times(1); - // Unblock the connection. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + CongestionUnblockWrites(); connection_.GetSendAlarm()->Fire(); EXPECT_EQ(0u, connection_.NumQueuedPackets()); EXPECT_FALSE(connection_.HasQueuedData()); // Parse the last packet and ensure it's an ack and two stream frames from // two different streams. - EXPECT_EQ(3u, writer_->frame_count()); - EXPECT_TRUE(writer_->ack()); - EXPECT_EQ(2u, writer_->stream_frames()->size()); - EXPECT_EQ(kStreamId3, (*writer_->stream_frames())[0].stream_id); - EXPECT_EQ(kStreamId5, (*writer_->stream_frames())[1].stream_id); + if (version() > QUIC_VERSION_15) { + EXPECT_EQ(4u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(3u, writer_->frame_count()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + ASSERT_EQ(2u, writer_->stream_frames().size()); + EXPECT_EQ(kClientDataStreamId1, writer_->stream_frames()[0].stream_id); + EXPECT_EQ(kClientDataStreamId2, writer_->stream_frames()[1].stream_id); } -TEST_F(QuicConnectionTest, FramePackingNonCryptoThenCrypto) { - // Block the connection. - connection_.GetSendAlarm()->Set( - clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1))); +TEST_P(QuicConnectionTest, FramePackingNonCryptoThenCrypto) { + CongestionBlockWrites(); // Send an ack and two stream frames (one non-crypto, then one crypto) in 2 // packets by queueing them. @@ -1427,100 +1648,88 @@ TEST_F(QuicConnectionTest, FramePackingNonCryptoThenCrypto) { IgnoreResult(InvokeWithoutArgs(&connection_, &TestConnection::SendStreamData3)), IgnoreResult(InvokeWithoutArgs(&connection_, - &TestConnection::SendCryptoStreamData)), - Return(true))); + &TestConnection::SendCryptoStreamData)))); - EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)) - .Times(2); - // Unblock the connection. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); + CongestionUnblockWrites(); connection_.GetSendAlarm()->Fire(); EXPECT_EQ(0u, connection_.NumQueuedPackets()); EXPECT_FALSE(connection_.HasQueuedData()); // Parse the last packet and ensure it's the crypto stream frame. EXPECT_EQ(1u, writer_->frame_count()); - EXPECT_EQ(1u, writer_->stream_frames()->size()); - EXPECT_EQ(kCryptoStreamId, (*writer_->stream_frames())[0].stream_id); + ASSERT_EQ(1u, writer_->stream_frames().size()); + EXPECT_EQ(kCryptoStreamId, writer_->stream_frames()[0].stream_id); } -TEST_F(QuicConnectionTest, FramePackingCryptoThenNonCrypto) { - // Block the connection. - connection_.GetSendAlarm()->Set( - clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1))); +TEST_P(QuicConnectionTest, FramePackingCryptoThenNonCrypto) { + CongestionBlockWrites(); - // Send an ack and two stream frames (one crypto, then one non-crypto) in 3 + // Send an ack and two stream frames (one crypto, then one non-crypto) in 2 // packets by queueing them. connection_.SendAck(); EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll( IgnoreResult(InvokeWithoutArgs(&connection_, &TestConnection::SendCryptoStreamData)), IgnoreResult(InvokeWithoutArgs(&connection_, - &TestConnection::SendStreamData3)), - Return(true))); + &TestConnection::SendStreamData3)))); - EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)) - .Times(3); - // Unblock the connection. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); + CongestionUnblockWrites(); connection_.GetSendAlarm()->Fire(); EXPECT_EQ(0u, connection_.NumQueuedPackets()); EXPECT_FALSE(connection_.HasQueuedData()); // Parse the last packet and ensure it's the stream frame from stream 3. EXPECT_EQ(1u, writer_->frame_count()); - EXPECT_EQ(1u, writer_->stream_frames()->size()); - EXPECT_EQ(kStreamId3, (*writer_->stream_frames())[0].stream_id); + ASSERT_EQ(1u, writer_->stream_frames().size()); + EXPECT_EQ(kClientDataStreamId1, writer_->stream_frames()[0].stream_id); } -TEST_F(QuicConnectionTest, FramePackingFEC) { - // Enable fec. - connection_.options()->max_packets_per_fec_group = 6; - // Block the connection. - connection_.GetSendAlarm()->Set( - clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1))); +TEST_P(QuicConnectionTest, FramePackingFEC) { + // Enable FEC. + QuicConnectionPeer::GetPacketCreator( + &connection_)->set_max_packets_per_fec_group(6); - // Send an ack and two stream frames in 1 packet by queueing them. - connection_.SendAck(); + CongestionBlockWrites(); + + // Queue an ack and two stream frames. Ack gets flushed when FEC is turned on + // for sending protected data; two stream frames are packing in 1 packet. EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll( - IgnoreResult(InvokeWithoutArgs(&connection_, - &TestConnection::SendStreamData3)), - IgnoreResult(InvokeWithoutArgs(&connection_, - &TestConnection::SendStreamData5)), - Return(true))); + IgnoreResult(InvokeWithoutArgs( + &connection_, &TestConnection::SendStreamData3WithFec)), + IgnoreResult(InvokeWithoutArgs( + &connection_, &TestConnection::SendStreamData5WithFec)))); + connection_.SendAck(); - EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)).Times(2); - // Unblock the connection. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3); + CongestionUnblockWrites(); connection_.GetSendAlarm()->Fire(); EXPECT_EQ(0u, connection_.NumQueuedPackets()); EXPECT_FALSE(connection_.HasQueuedData()); // Parse the last packet and ensure it's in an fec group. - EXPECT_EQ(1u, writer_->header()->fec_group); + EXPECT_EQ(2u, writer_->header().fec_group); EXPECT_EQ(0u, writer_->frame_count()); } -TEST_F(QuicConnectionTest, FramePackingAckResponse) { +TEST_P(QuicConnectionTest, FramePackingAckResponse) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); // Process a data packet to queue up a pending ack. - EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(true)); + EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1); ProcessDataPacket(1, 1, kEntropyFlag); EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll( IgnoreResult(InvokeWithoutArgs(&connection_, &TestConnection::SendStreamData3)), IgnoreResult(InvokeWithoutArgs(&connection_, - &TestConnection::SendStreamData5)), - Return(true))); + &TestConnection::SendStreamData5)))); - EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)) - .Times(1); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); // Process an ack to cause the visitor's OnCanWrite to be invoked. - creator_.set_sequence_number(2); - QuicAckFrame ack_one(0, QuicTime::Zero(), 0); + peer_creator_.set_sequence_number(2); + QuicAckFrame ack_one = InitAckFrame(0, 0); ProcessAckPacket(&ack_one); EXPECT_EQ(0u, connection_.NumQueuedPackets()); @@ -1528,23 +1737,28 @@ TEST_F(QuicConnectionTest, FramePackingAckResponse) { // Parse the last packet and ensure it's an ack and two stream frames from // two different streams. - EXPECT_EQ(3u, writer_->frame_count()); - EXPECT_TRUE(writer_->ack()); - ASSERT_EQ(2u, writer_->stream_frames()->size()); - EXPECT_EQ(kStreamId3, (*writer_->stream_frames())[0].stream_id); - EXPECT_EQ(kStreamId5, (*writer_->stream_frames())[1].stream_id); + if (version() > QUIC_VERSION_15) { + EXPECT_EQ(4u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(3u, writer_->frame_count()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + ASSERT_EQ(2u, writer_->stream_frames().size()); + EXPECT_EQ(kClientDataStreamId1, writer_->stream_frames()[0].stream_id); + EXPECT_EQ(kClientDataStreamId2, writer_->stream_frames()[1].stream_id); } -TEST_F(QuicConnectionTest, FramePackingSendv) { +TEST_P(QuicConnectionTest, FramePackingSendv) { // Send data in 1 packet by writing multiple blocks in a single iovector // using writev. - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); char data[] = "ABCD"; IOVector data_iov; data_iov.AppendNoCoalesce(data, 2); data_iov.AppendNoCoalesce(data + 2, 2); - connection_.SendStreamData(1, data_iov, 0, !kFin, NULL); + connection_.SendStreamData(1, data_iov, 0, !kFin, MAY_FEC_PROTECT, NULL); EXPECT_EQ(0u, connection_.NumQueuedPackets()); EXPECT_FALSE(connection_.HasQueuedData()); @@ -1552,87 +1766,78 @@ TEST_F(QuicConnectionTest, FramePackingSendv) { // Parse the last packet and ensure multiple iovector blocks have // been packed into a single stream frame from one stream. EXPECT_EQ(1u, writer_->frame_count()); - EXPECT_EQ(1u, writer_->stream_frames()->size()); - QuicStreamFrame frame = (*writer_->stream_frames())[0]; + EXPECT_EQ(1u, writer_->stream_frames().size()); + QuicStreamFrame frame = writer_->stream_frames()[0]; EXPECT_EQ(1u, frame.stream_id); EXPECT_EQ("ABCD", string(static_cast<char*> (frame.data.iovec()[0].iov_base), (frame.data.iovec()[0].iov_len))); } -TEST_F(QuicConnectionTest, FramePackingSendvQueued) { +TEST_P(QuicConnectionTest, FramePackingSendvQueued) { // Try to send two stream frames in 1 packet by using writev. - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); - writer_->set_blocked(true); + BlockOnNextWrite(); char data[] = "ABCD"; IOVector data_iov; data_iov.AppendNoCoalesce(data, 2); data_iov.AppendNoCoalesce(data + 2, 2); - connection_.SendStreamData(1, data_iov, 0, !kFin, NULL); + connection_.SendStreamData(1, data_iov, 0, !kFin, MAY_FEC_PROTECT, NULL); EXPECT_EQ(1u, connection_.NumQueuedPackets()); EXPECT_TRUE(connection_.HasQueuedData()); - // Attempt to send all packets, but since we're actually still - // blocked, they should all remain queued. - EXPECT_FALSE(connection_.OnCanWrite()); - EXPECT_EQ(1u, connection_.NumQueuedPackets()); - // Unblock the writes and actually send. - writer_->set_blocked(false); - EXPECT_TRUE(connection_.OnCanWrite()); + writer_->SetWritable(); + connection_.OnCanWrite(); EXPECT_EQ(0u, connection_.NumQueuedPackets()); // Parse the last packet and ensure it's one stream frame from one stream. EXPECT_EQ(1u, writer_->frame_count()); - EXPECT_EQ(1u, writer_->stream_frames()->size()); - EXPECT_EQ(1u, (*writer_->stream_frames())[0].stream_id); + EXPECT_EQ(1u, writer_->stream_frames().size()); + EXPECT_EQ(1u, writer_->stream_frames()[0].stream_id); } -TEST_F(QuicConnectionTest, SendingZeroBytes) { +TEST_P(QuicConnectionTest, SendingZeroBytes) { // Send a zero byte write with a fin using writev. - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); IOVector empty_iov; - connection_.SendStreamData(1, empty_iov, 0, kFin, NULL); + connection_.SendStreamData(1, empty_iov, 0, kFin, MAY_FEC_PROTECT, NULL); EXPECT_EQ(0u, connection_.NumQueuedPackets()); EXPECT_FALSE(connection_.HasQueuedData()); // Parse the last packet and ensure it's one stream frame from one stream. EXPECT_EQ(1u, writer_->frame_count()); - EXPECT_EQ(1u, writer_->stream_frames()->size()); - EXPECT_EQ(1u, (*writer_->stream_frames())[0].stream_id); - EXPECT_TRUE((*writer_->stream_frames())[0].fin); + EXPECT_EQ(1u, writer_->stream_frames().size()); + EXPECT_EQ(1u, writer_->stream_frames()[0].stream_id); + EXPECT_TRUE(writer_->stream_frames()[0].fin); } -TEST_F(QuicConnectionTest, OnCanWrite) { - // Visitor's OnCanWill send data, but will return false. +TEST_P(QuicConnectionTest, OnCanWrite) { + // Visitor's OnCanWrite will send data, but will have more pending writes. EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll( IgnoreResult(InvokeWithoutArgs(&connection_, &TestConnection::SendStreamData3)), IgnoreResult(InvokeWithoutArgs(&connection_, - &TestConnection::SendStreamData5)), - Return(false))); - + &TestConnection::SendStreamData5)))); + EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillOnce(Return(true)); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly( + TimeUntilSend(_, _, _)).WillRepeatedly( testing::Return(QuicTime::Delta::Zero())); - // Unblock the connection. connection_.OnCanWrite(); + // Parse the last packet and ensure it's the two stream frames from // two different streams. EXPECT_EQ(2u, writer_->frame_count()); - EXPECT_EQ(2u, writer_->stream_frames()->size()); - EXPECT_EQ(kStreamId3, (*writer_->stream_frames())[0].stream_id); - EXPECT_EQ(kStreamId5, (*writer_->stream_frames())[1].stream_id); + EXPECT_EQ(2u, writer_->stream_frames().size()); + EXPECT_EQ(kClientDataStreamId1, writer_->stream_frames()[0].stream_id); + EXPECT_EQ(kClientDataStreamId2, writer_->stream_frames()[1].stream_id); } -TEST_F(QuicConnectionTest, RetransmitOnNack) { - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(2); - EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(2, _)).Times(1); +TEST_P(QuicConnectionTest, RetransmitOnNack) { QuicPacketSequenceNumber last_packet; QuicByteCount second_packet_size; SendStreamDataToPeer(3, "foo", 0, !kFin, &last_packet); // Packet 1 @@ -1642,34 +1847,26 @@ TEST_F(QuicConnectionTest, RetransmitOnNack) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - // Peer acks one but not two or three. Right now we only retransmit on - // explicit nack, so it should not trigger a retransmission. - QuicAckFrame ack_one(1, QuicTime::Zero(), 0); - ack_one.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); - ProcessAckPacket(&ack_one); - ProcessAckPacket(&ack_one); + // Don't lose a packet on an ack, and nothing is retransmitted. + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + QuicAckFrame ack_one = InitAckFrame(1, 0); ProcessAckPacket(&ack_one); - // Peer acks up to 3 with two explicitly missing. - // Early retransmit causes 2 to be retransmitted on the first ack. - QuicAckFrame nack_two(3, QuicTime::Zero(), 0); - nack_two.received_info.missing_packets.insert(2); - nack_two.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); - // The third nack should trigger a retransmission. + // Lose a packet and ensure it triggers retransmission. + QuicAckFrame nack_two = InitAckFrame(3, 0); + NackPacket(2, &nack_two); + SequenceNumberSet lost_packets; + lost_packets.insert(2); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, second_packet_size - kQuicVersionSize, - NACK_RETRANSMISSION, _)).Times(1); + OnPacketSent(_, _, _, second_packet_size - kQuicVersionSize, _)). + Times(1); ProcessAckPacket(&nack_two); } -TEST_F(QuicConnectionTest, DiscardRetransmit) { - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(2); - EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(2, _)).Times(1); +TEST_P(QuicConnectionTest, DiscardRetransmit) { QuicPacketSequenceNumber last_packet; SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1 SendStreamDataToPeer(1, "foos", 3, !kFin, &last_packet); // Packet 2 @@ -1677,34 +1874,24 @@ TEST_F(QuicConnectionTest, DiscardRetransmit) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - // Peer acks one but not two or three. Right now we only retransmit on - // explicit nack, so it should not trigger a retransmission. - QuicAckFrame ack_one(1, QuicTime::Zero(), 0); - ack_one.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); - ProcessAckPacket(&ack_one); - ProcessAckPacket(&ack_one); - ProcessAckPacket(&ack_one); - - // Peer acks up to 3 with two explicitly missing. Two nacks should cause no - // change. - QuicAckFrame nack_two(3, QuicTime::Zero(), 0); - nack_two.received_info.missing_packets.insert(2); - nack_two.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); + // Instigate a loss with an ack. + QuicAckFrame nack_two = InitAckFrame(3, 0); + NackPacket(2, &nack_two); // The first nack should trigger a fast retransmission, but we'll be // write blocked, so the packet will be queued. - writer_->set_blocked(true); - + BlockOnNextWrite(); + SequenceNumberSet lost_packets; + lost_packets.insert(2); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); ProcessAckPacket(&nack_two); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // Now, ack the previous transmission. - QuicAckFrame ack_all(3, QuicTime::Zero(), 0); - ack_all.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 3); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(SequenceNumberSet())); + QuicAckFrame ack_all = InitAckFrame(3, 0); ProcessAckPacket(&ack_all); // Unblock the socket and attempt to send the queued packets. However, @@ -1713,50 +1900,50 @@ TEST_F(QuicConnectionTest, DiscardRetransmit) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); - writer_->set_blocked(false); + writer_->SetWritable(); connection_.OnCanWrite(); EXPECT_EQ(0u, connection_.NumQueuedPackets()); } -TEST_F(QuicConnectionTest, RetransmitNackedLargestObserved) { +TEST_P(QuicConnectionTest, RetransmitNackedLargestObserved) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1); QuicPacketSequenceNumber largest_observed; QuicByteCount packet_size; - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)) - .WillOnce(DoAll(SaveArg<1>(&largest_observed), SaveArg<2>(&packet_size), + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(DoAll(SaveArg<2>(&largest_observed), SaveArg<3>(&packet_size), Return(true))); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1); connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL); - QuicAckFrame frame(1, QuicTime::Zero(), largest_observed); - frame.received_info.missing_packets.insert(largest_observed); - frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash( - &connection_, largest_observed - 1); + + QuicAckFrame frame = InitAckFrame(1, largest_observed); + NackPacket(largest_observed, &frame); // The first nack should retransmit the largest observed packet. + SequenceNumberSet lost_packets; + lost_packets.insert(1); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, packet_size - kQuicVersionSize, - NACK_RETRANSMISSION, _)); + OnPacketSent(_, _, _, packet_size - kQuicVersionSize, _)); ProcessAckPacket(&frame); } -TEST_F(QuicConnectionTest, QueueAfterTwoRTOs) { +TEST_P(QuicConnectionTest, QueueAfterTwoRTOs) { for (int i = 0; i < 10; ++i) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); - connection_.SendStreamDataWithString(1, "foo", i * 3, !kFin, NULL); + connection_.SendStreamDataWithString(3, "foo", i * 3, !kFin, NULL); } // Block the congestion window and ensure they're queued. - writer_->set_blocked(true); + BlockOnNextWrite(); clock_.AdvanceTime(DefaultRetransmissionTime()); // Only one packet should be retransmitted. - EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout()); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(10); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); connection_.GetRetransmissionAlarm()->Fire(); EXPECT_TRUE(connection_.HasQueuedData()); // Unblock the congestion window. - writer_->set_blocked(false); + writer_->SetWritable(); clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds( 2 * DefaultRetransmissionTime().ToMicroseconds())); // Retransmit already retransmitted packets event though the sequence number @@ -1766,9 +1953,8 @@ TEST_F(QuicConnectionTest, QueueAfterTwoRTOs) { connection_.OnCanWrite(); } -TEST_F(QuicConnectionTest, WriteBlockedThenSent) { - writer_->set_blocked(true); - +TEST_P(QuicConnectionTest, WriteBlockedThenSent) { + BlockOnNextWrite(); writer_->set_is_write_blocked_data_buffered(true); connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL); EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); @@ -1778,22 +1964,66 @@ TEST_F(QuicConnectionTest, WriteBlockedThenSent) { EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); } -TEST_F(QuicConnectionTest, ResumptionAlarmThenWriteBlocked) { - // Set the send and resumption alarm, then block the connection. +TEST_P(QuicConnectionTest, WriteBlockedAckedThenSent) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + BlockOnNextWrite(); + writer_->set_is_write_blocked_data_buffered(true); + connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + + // Ack the sent packet before the callback returns, which happens in + // rare circumstances with write blocked sockets. + QuicAckFrame ack = InitAckFrame(1, 0); + ProcessAckPacket(&ack); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + connection_.OnPacketSent(WriteResult(WRITE_STATUS_OK, 0)); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, RetransmitWriteBlockedAckedOriginalThenSent) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + + BlockOnNextWrite(); + writer_->set_is_write_blocked_data_buffered(true); + // Simulate the retransmission alarm firing. + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(_)); + clock_.AdvanceTime(DefaultRetransmissionTime()); + connection_.GetRetransmissionAlarm()->Fire(); + + // Ack the sent packet before the callback returns, which happens in + // rare circumstances with write blocked sockets. + QuicAckFrame ack = InitAckFrame(1, 0); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + ProcessAckPacket(&ack); + + connection_.OnPacketSent(WriteResult(WRITE_STATUS_OK, 0)); + // There is now a pending packet, but with no retransmittable frames. + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + EXPECT_FALSE(connection_.sent_packet_manager().HasRetransmittableFrames(2)); +} + +TEST_P(QuicConnectionTest, AlarmsWhenWriteBlocked) { + // Block the connection. + BlockOnNextWrite(); + connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL); + EXPECT_EQ(1u, writer_->packets_write_attempts()); + EXPECT_TRUE(writer_->IsWriteBlocked()); + + // Set the send and resumption alarms. Fire the alarms and ensure they don't + // attempt to write. connection_.GetResumeWritesAlarm()->Set(clock_.ApproximateNow()); connection_.GetSendAlarm()->Set(clock_.ApproximateNow()); - QuicConnectionPeer::SetIsWriteBlocked(&connection_, true); - - // Fire the alarms and ensure the connection is still write blocked. connection_.GetResumeWritesAlarm()->Fire(); connection_.GetSendAlarm()->Fire(); - EXPECT_TRUE(QuicConnectionPeer::IsWriteBlocked(&connection_)); + EXPECT_TRUE(writer_->IsWriteBlocked()); + EXPECT_EQ(1u, writer_->packets_write_attempts()); } -TEST_F(QuicConnectionTest, LimitPacketsPerNack) { +TEST_P(QuicConnectionTest, NoLimitPacketsPerNack) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_CALL(*send_algorithm_, OnPacketAcked(15, _, _)).Times(1); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(4); int offset = 0; // Send packets 1 to 15. for (int i = 0; i < 15; ++i) { @@ -1802,29 +2032,24 @@ TEST_F(QuicConnectionTest, LimitPacketsPerNack) { } // Ack 15, nack 1-14. - QuicAckFrame nack(15, QuicTime::Zero(), 0); + SequenceNumberSet lost_packets; + QuicAckFrame nack = InitAckFrame(15, 0); for (int i = 1; i < 15; ++i) { - nack.received_info.missing_packets.insert(i); + NackPacket(i, &nack); + lost_packets.insert(i); } - nack.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 15) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 14); - - // 13 packets have been NACK'd 3 times, but we limit retransmissions to 2. - EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); - ProcessAckPacket(&nack); - - // The next call should trigger retransmitting 2 more packets. - EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); + // 14 packets have been NACK'd and lost. In TCP cubic, PRR limits + // the retransmission rate in the case of burst losses. + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(14); ProcessAckPacket(&nack); } // Test sending multiple acks from the connection to the session. -TEST_F(QuicConnectionTest, MultipleAcks) { - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(6); +TEST_P(QuicConnectionTest, MultipleAcks) { QuicPacketSequenceNumber last_packet; SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1 EXPECT_EQ(1u, last_packet); @@ -1839,55 +2064,79 @@ TEST_F(QuicConnectionTest, MultipleAcks) { EXPECT_EQ(6u, last_packet); // Client will ack packets 1, 2, [!3], 4, 5. - QuicAckFrame frame1(5, QuicTime::Zero(), 0); - frame1.received_info.missing_packets.insert(3); - frame1.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 5) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 2); - + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + QuicAckFrame frame1 = InitAckFrame(5, 0); + NackPacket(3, &frame1); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - ProcessAckPacket(&frame1); // Now the client implicitly acks 3, and explicitly acks 6. - QuicAckFrame frame2(6, QuicTime::Zero(), 0); - frame2.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 6); - + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + QuicAckFrame frame2 = InitAckFrame(6, 0); ProcessAckPacket(&frame2); } -TEST_F(QuicConnectionTest, DontLatchUnackedPacket) { - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1); +TEST_P(QuicConnectionTest, DontLatchUnackedPacket) { SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); // Packet 1; + // From now on, we send acks, so the send algorithm won't mark them pending. + ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillByDefault(Return(false)); SendAckPacketToPeer(); // Packet 2 EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - QuicAckFrame frame(1, QuicTime::Zero(), 0); - frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash( - &connection_, 1); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + QuicAckFrame frame = InitAckFrame(1, 0); ProcessAckPacket(&frame); - // Verify that our internal state has least-unacked as 3. + // Verify that our internal state has least-unacked as 2, because we're still + // waiting for a potential ack for 2. + EXPECT_EQ(2u, outgoing_ack()->sent_info.least_unacked); + + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + frame = InitAckFrame(2, 0); + ProcessAckPacket(&frame); EXPECT_EQ(3u, outgoing_ack()->sent_info.least_unacked); // When we send an ack, we make sure our least-unacked makes sense. In this // case since we're not waiting on an ack for 2 and all packets are acked, we // set it to 3. SendAckPacketToPeer(); // Packet 3 - // Since this was an ack packet, we set least_unacked to 4. - EXPECT_EQ(4u, outgoing_ack()->sent_info.least_unacked); + // Least_unacked remains at 3 until another ack is received. + EXPECT_EQ(3u, outgoing_ack()->sent_info.least_unacked); // Check that the outgoing ack had its sequence number as least_unacked. - EXPECT_EQ(3u, last_ack()->sent_info.least_unacked); + EXPECT_EQ(3u, least_unacked()); + // Ack the ack, which updates the rtt and raises the least unacked. + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + frame = InitAckFrame(3, 0); + ProcessAckPacket(&frame); + + ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillByDefault(Return(true)); SendStreamDataToPeer(1, "bar", 3, false, NULL); // Packet 4 EXPECT_EQ(4u, outgoing_ack()->sent_info.least_unacked); + ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillByDefault(Return(false)); SendAckPacketToPeer(); // Packet 5 - EXPECT_EQ(4u, last_ack()->sent_info.least_unacked); + EXPECT_EQ(4u, least_unacked()); + + // Send two data packets at the end, and ensure if the last one is acked, + // the least unacked is raised above the ack packets. + ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillByDefault(Return(true)); + SendStreamDataToPeer(1, "bar", 6, false, NULL); // Packet 6 + SendStreamDataToPeer(1, "bar", 9, false, NULL); // Packet 7 + + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + frame = InitAckFrame(7, 0); + NackPacket(5, &frame); + NackPacket(6, &frame); + ProcessAckPacket(&frame); + + EXPECT_EQ(6u, outgoing_ack()->sent_info.least_unacked); } -TEST_F(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) { +TEST_P(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); // Don't send missing packet 1. @@ -1896,7 +2145,63 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) { EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2)); } -TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) { +TEST_P(QuicConnectionTest, ReviveMissingPacketWithVaryingSeqNumLengths) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + // Set up a debug visitor to the connection. + scoped_ptr<FecQuicConnectionDebugVisitor> + fec_visitor(new FecQuicConnectionDebugVisitor); + connection_.set_debug_visitor(fec_visitor.get()); + + QuicPacketSequenceNumber fec_packet = 0; + QuicSequenceNumberLength lengths[] = {PACKET_6BYTE_SEQUENCE_NUMBER, + PACKET_4BYTE_SEQUENCE_NUMBER, + PACKET_2BYTE_SEQUENCE_NUMBER, + PACKET_1BYTE_SEQUENCE_NUMBER}; + // For each sequence number length size, revive a packet and check sequence + // number length in the revived packet. + for (size_t i = 0; i < arraysize(lengths); ++i) { + // Set sequence_number_length_ (for data and FEC packets). + sequence_number_length_ = lengths[i]; + fec_packet += 2; + // Don't send missing packet, but send fec packet right after it. + ProcessFecPacket(fec_packet, fec_packet - 1, true, !kEntropyFlag, NULL); + // Sequence number length in the revived header should be the same as + // in the original data/fec packet headers. + EXPECT_EQ(sequence_number_length_, fec_visitor->revived_header(). + public_header.sequence_number_length); + } +} + +TEST_P(QuicConnectionTest, ReviveMissingPacketWithVaryingConnectionIdLengths) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + // Set up a debug visitor to the connection. + scoped_ptr<FecQuicConnectionDebugVisitor> + fec_visitor(new FecQuicConnectionDebugVisitor); + connection_.set_debug_visitor(fec_visitor.get()); + + QuicPacketSequenceNumber fec_packet = 0; + QuicConnectionIdLength lengths[] = {PACKET_8BYTE_CONNECTION_ID, + PACKET_4BYTE_CONNECTION_ID, + PACKET_1BYTE_CONNECTION_ID, + PACKET_0BYTE_CONNECTION_ID}; + // For each connection id length size, revive a packet and check connection + // id length in the revived packet. + for (size_t i = 0; i < arraysize(lengths); ++i) { + // Set connection id length (for data and FEC packets). + connection_id_length_ = lengths[i]; + fec_packet += 2; + // Don't send missing packet, but send fec packet right after it. + ProcessFecPacket(fec_packet, fec_packet - 1, true, !kEntropyFlag, NULL); + // Connection id length in the revived header should be the same as + // in the original data/fec packet headers. + EXPECT_EQ(connection_id_length_, + fec_visitor->revived_header().public_header.connection_id_length); + } +} + +TEST_P(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessFecProtectedPacket(1, false, kEntropyFlag); @@ -1906,18 +2211,19 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) { EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2)); } -TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketsThenFecPacket) { +TEST_P(QuicConnectionTest, ReviveMissingPacketAfterDataPacketsThenFecPacket) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessFecProtectedPacket(1, false, !kEntropyFlag); // Don't send missing packet 2. ProcessFecProtectedPacket(3, false, !kEntropyFlag); ProcessFecPacket(4, 1, true, kEntropyFlag, NULL); - // Entropy flag should be true, so entropy should not be 0. - EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2)); + // Ensure QUIC no longer revives entropy for lost packets. + EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2)); + EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 4)); } -TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) { +TEST_P(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); // Don't send missing packet 1. @@ -1928,7 +2234,7 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) { EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2)); } -TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) { +TEST_P(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessFecProtectedPacket(1, false, !kEntropyFlag); @@ -1937,31 +2243,52 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) { ProcessFecProtectedPacket(3, false, kEntropyFlag); ProcessFecProtectedPacket(4, false, kEntropyFlag); ProcessFecProtectedPacket(5, true, !kEntropyFlag); - // Entropy flag should be true, so entropy should be 0. - EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2)); + // Ensure entropy is not revived for the missing packet. + EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2)); + EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 3)); } -TEST_F(QuicConnectionTest, RTO) { +TEST_P(QuicConnectionTest, TLP) { + QuicSentPacketManagerPeer::SetMaxTailLossProbes( + QuicConnectionPeer::GetSentPacketManager(&connection_), 1); + + SendStreamDataToPeer(3, "foo", 0, !kFin, NULL); + EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked); + QuicTime retransmission_time = + connection_.GetRetransmissionAlarm()->deadline(); + EXPECT_NE(QuicTime::Zero(), retransmission_time); + + EXPECT_EQ(1u, writer_->header().packet_sequence_number); + // Simulate the retransmission alarm firing and sending a tlp, + // so send algorithm's OnRetransmissionTimeout is not called. + clock_.AdvanceTime(retransmission_time.Subtract(clock_.Now())); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2u, _, _)); + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_EQ(2u, writer_->header().packet_sequence_number); + // We do not raise the high water mark yet. + EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked); +} + +TEST_P(QuicConnectionTest, RTO) { QuicTime default_retransmission_time = clock_.ApproximateNow().Add( DefaultRetransmissionTime()); - SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); + SendStreamDataToPeer(3, "foo", 0, !kFin, NULL); EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked); - EXPECT_EQ(1u, last_header()->packet_sequence_number); + EXPECT_EQ(1u, writer_->header().packet_sequence_number); EXPECT_EQ(default_retransmission_time, connection_.GetRetransmissionAlarm()->deadline()); // Simulate the retransmission alarm firing. clock_.AdvanceTime(DefaultRetransmissionTime()); - EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout()); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1u, _)); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 2u, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2u, _, _)); connection_.GetRetransmissionAlarm()->Fire(); - EXPECT_EQ(2u, last_header()->packet_sequence_number); + EXPECT_EQ(2u, writer_->header().packet_sequence_number); // We do not raise the high water mark yet. EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked); } -TEST_F(QuicConnectionTest, RTOWithSameEncryptionLevel) { +TEST_P(QuicConnectionTest, RTOWithSameEncryptionLevel) { QuicTime default_retransmission_time = clock_.ApproximateNow().Add( DefaultRetransmissionTime()); use_tagging_decrypter(); @@ -1969,23 +2296,21 @@ TEST_F(QuicConnectionTest, RTOWithSameEncryptionLevel) { // A TaggingEncrypter puts kTagSize copies of the given byte (0x01 here) at // the end of the packet. We can test this to check which encrypter was used. connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01)); - SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); - EXPECT_EQ(0x01010101u, final_bytes_of_last_packet()); + SendStreamDataToPeer(3, "foo", 0, !kFin, NULL); + EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet()); connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(0x02)); connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); - SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); - EXPECT_EQ(0x02020202u, final_bytes_of_last_packet()); + SendStreamDataToPeer(3, "foo", 0, !kFin, NULL); + EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet()); EXPECT_EQ(default_retransmission_time, connection_.GetRetransmissionAlarm()->deadline()); { InSequence s; - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(2, _)); - EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout()); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 3, _, RTO_RETRANSMISSION, _)); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 4, _, RTO_RETRANSMISSION, _)); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 3, _, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 4, _, _)); } // Simulate the retransmission alarm firing. @@ -1993,23 +2318,23 @@ TEST_F(QuicConnectionTest, RTOWithSameEncryptionLevel) { connection_.GetRetransmissionAlarm()->Fire(); // Packet should have been sent with ENCRYPTION_NONE. - EXPECT_EQ(0x01010101u, final_bytes_of_previous_packet()); + EXPECT_EQ(0x01010101u, writer_->final_bytes_of_previous_packet()); // Packet should have been sent with ENCRYPTION_INITIAL. - EXPECT_EQ(0x02020202u, final_bytes_of_last_packet()); + EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet()); } -TEST_F(QuicConnectionTest, SendHandshakeMessages) { +TEST_P(QuicConnectionTest, SendHandshakeMessages) { use_tagging_decrypter(); // A TaggingEncrypter puts kTagSize copies of the given byte (0x01 here) at // the end of the packet. We can test this to check which encrypter was used. connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01)); - // Attempt to send a handshake message while the congestion manager - // does not permit sending. + // Attempt to send a handshake message and have the socket block. EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, _, _, IS_HANDSHAKE)).WillRepeatedly( - testing::Return(QuicTime::Delta::Infinite())); + TimeUntilSend(_, _, _)).WillRepeatedly( + testing::Return(QuicTime::Delta::Zero())); + BlockOnNextWrite(); connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL); // The packet should be serialized, but not queued. EXPECT_EQ(1u, connection_.NumQueuedPackets()); @@ -2019,43 +2344,43 @@ TEST_F(QuicConnectionTest, SendHandshakeMessages) { connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); // Now become writeable and flush the packets. - EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, _, _, IS_HANDSHAKE)).WillRepeatedly( - testing::Return(QuicTime::Delta::Zero())); + writer_->SetWritable(); EXPECT_CALL(visitor_, OnCanWrite()); connection_.OnCanWrite(); EXPECT_EQ(0u, connection_.NumQueuedPackets()); // Verify that the handshake packet went out at the null encryption. - EXPECT_EQ(0x01010101u, final_bytes_of_last_packet()); + EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet()); } -TEST_F(QuicConnectionTest, +TEST_P(QuicConnectionTest, DropRetransmitsForNullEncryptedPacketAfterForwardSecure) { use_tagging_decrypter(); connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01)); QuicPacketSequenceNumber sequence_number; - SendStreamDataToPeer(1, "foo", 0, !kFin, &sequence_number); + SendStreamDataToPeer(3, "foo", 0, !kFin, &sequence_number); + // Simulate the retransmission alarm firing and the socket blocking. + BlockOnNextWrite(); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + clock_.AdvanceTime(DefaultRetransmissionTime()); + connection_.GetRetransmissionAlarm()->Fire(); + + // Go forward secure. connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, new TaggingEncrypter(0x02)); connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + connection_.NeuterUnencryptedPackets(); - EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout()); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(sequence_number, _)).Times(1); - - QuicTime default_retransmission_time = clock_.ApproximateNow().Add( - DefaultRetransmissionTime()); - - EXPECT_EQ(default_retransmission_time, + EXPECT_EQ(QuicTime::Zero(), connection_.GetRetransmissionAlarm()->deadline()); - // Simulate the retransmission alarm firing. - clock_.AdvanceTime(DefaultRetransmissionTime()); - connection_.GetRetransmissionAlarm()->Fire(); + // Unblock the socket and ensure that no packets are sent. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + writer_->SetWritable(); + connection_.OnCanWrite(); } -TEST_F(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) { +TEST_P(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) { use_tagging_decrypter(); connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01)); connection_.SetDefaultEncryptionLevel(ENCRYPTION_NONE); @@ -2066,14 +2391,12 @@ TEST_F(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) { connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); SendStreamDataToPeer(2, "bar", 0, !kFin, NULL); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(1); connection_.RetransmitUnackedPackets(INITIAL_ENCRYPTION_ONLY); } -TEST_F(QuicConnectionTest, BufferNonDecryptablePackets) { +TEST_P(QuicConnectionTest, BufferNonDecryptablePackets) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); use_tagging_decrypter(); @@ -2082,68 +2405,66 @@ TEST_F(QuicConnectionTest, BufferNonDecryptablePackets) { // Process an encrypted packet which can not yet be decrypted // which should result in the packet being buffered. - ProcessDataPacketAtLevel(1, false, kEntropyFlag, ENCRYPTION_INITIAL); + ProcessDataPacketAtLevel(1, 0, kEntropyFlag, ENCRYPTION_INITIAL); // Transition to the new encryption state and process another // encrypted packet which should result in the original packet being // processed. - connection_.SetDecrypter(new StrictTaggingDecrypter(tag)); + connection_.SetDecrypter(new StrictTaggingDecrypter(tag), + ENCRYPTION_INITIAL); connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag)); - EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(2).WillRepeatedly( - Return(true)); - ProcessDataPacketAtLevel(2, false, kEntropyFlag, ENCRYPTION_INITIAL); + EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(2); + ProcessDataPacketAtLevel(2, 0, kEntropyFlag, ENCRYPTION_INITIAL); // Finally, process a third packet and note that we do not // reprocess the buffered packet. - EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(true)); - ProcessDataPacketAtLevel(3, false, kEntropyFlag, ENCRYPTION_INITIAL); + EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1); + ProcessDataPacketAtLevel(3, 0, kEntropyFlag, ENCRYPTION_INITIAL); } -TEST_F(QuicConnectionTest, TestRetransmitOrder) { +TEST_P(QuicConnectionTest, TestRetransmitOrder) { QuicByteCount first_packet_size; EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).WillOnce( - DoAll(SaveArg<2>(&first_packet_size), Return(true))); + DoAll(SaveArg<3>(&first_packet_size), Return(true))); connection_.SendStreamDataWithString(3, "first_packet", 0, !kFin, NULL); QuicByteCount second_packet_size; EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).WillOnce( - DoAll(SaveArg<2>(&second_packet_size), Return(true))); + DoAll(SaveArg<3>(&second_packet_size), Return(true))); connection_.SendStreamDataWithString(3, "second_packet", 12, !kFin, NULL); 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)); - EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout()); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); { InSequence s; EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, first_packet_size, _, _)); + OnPacketSent(_, _, _, first_packet_size, _)); EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, second_packet_size, _, _)); + OnPacketSent(_, _, _, second_packet_size, _)); } connection_.GetRetransmissionAlarm()->Fire(); // Advance again and expect the packets to be sent again in the same order. clock_.AdvanceTime(QuicTime::Delta::FromSeconds(20)); - EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout()); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); { InSequence s; EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, first_packet_size, _, _)); + OnPacketSent(_, _, _, first_packet_size, _)); EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, second_packet_size, _, _)); + OnPacketSent(_, _, _, second_packet_size, _)); } connection_.GetRetransmissionAlarm()->Fire(); } -TEST_F(QuicConnectionTest, RetransmissionCountCalculation) { +TEST_P(QuicConnectionTest, RetransmissionCountCalculation) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); QuicPacketSequenceNumber original_sequence_number; - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)) - .WillOnce(DoAll(SaveArg<1>(&original_sequence_number), Return(true))); - connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(DoAll(SaveArg<2>(&original_sequence_number), Return(true))); + connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL); EXPECT_TRUE(QuicConnectionPeer::IsSavedForRetransmission( &connection_, original_sequence_number)); @@ -2151,12 +2472,10 @@ TEST_F(QuicConnectionTest, RetransmissionCountCalculation) { &connection_, original_sequence_number)); // Force retransmission due to RTO. clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10)); - EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout()); - EXPECT_CALL(*send_algorithm_, - OnPacketAbandoned(original_sequence_number, _)).Times(1); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); QuicPacketSequenceNumber rto_sequence_number; - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, RTO_RETRANSMISSION, _)) - .WillOnce(DoAll(SaveArg<1>(&rto_sequence_number), Return(true))); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(DoAll(SaveArg<2>(&rto_sequence_number), Return(true))); connection_.GetRetransmissionAlarm()->Fire(); EXPECT_FALSE(QuicConnectionPeer::IsSavedForRetransmission( &connection_, original_sequence_number)); @@ -2165,28 +2484,24 @@ TEST_F(QuicConnectionTest, RetransmissionCountCalculation) { EXPECT_TRUE(QuicConnectionPeer::IsRetransmission( &connection_, rto_sequence_number)); // Once by explicit nack. - EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1); - EXPECT_CALL(*send_algorithm_, - OnPacketAbandoned(rto_sequence_number, _)).Times(1); + SequenceNumberSet lost_packets; + lost_packets.insert(rto_sequence_number); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); QuicPacketSequenceNumber nack_sequence_number = 0; // Ack packets might generate some other packets, which are not // retransmissions. (More ack packets). - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)) + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) .Times(AnyNumber()); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NACK_RETRANSMISSION, _)) - .WillOnce(DoAll(SaveArg<1>(&nack_sequence_number), Return(true))); - QuicAckFrame ack(rto_sequence_number, QuicTime::Zero(), 0); - // Ack the retransmitted packet. - ack.received_info.missing_packets.insert(original_sequence_number); - ack.received_info.missing_packets.insert(rto_sequence_number); - ack.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, - rto_sequence_number - 1) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, - original_sequence_number); - for (int i = 0; i < 3; i++) { - ProcessAckPacket(&ack); - } + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(DoAll(SaveArg<2>(&nack_sequence_number), Return(true))); + QuicAckFrame ack = InitAckFrame(rto_sequence_number, 0); + // Nack the retransmitted packet. + NackPacket(original_sequence_number, &ack); + NackPacket(rto_sequence_number, &ack); + ProcessAckPacket(&ack); + ASSERT_NE(0u, nack_sequence_number); EXPECT_FALSE(QuicConnectionPeer::IsSavedForRetransmission( &connection_, rto_sequence_number)); @@ -2196,48 +2511,46 @@ TEST_F(QuicConnectionTest, RetransmissionCountCalculation) { &connection_, nack_sequence_number)); } -TEST_F(QuicConnectionTest, SetRTOAfterWritingToSocket) { - writer_->set_blocked(true); +TEST_P(QuicConnectionTest, SetRTOAfterWritingToSocket) { + BlockOnNextWrite(); connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL); // Make sure that RTO is not started when the packet is queued. EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); // Test that RTO is started once we write to the socket. - writer_->set_blocked(false); + writer_->SetWritable(); connection_.OnCanWrite(); EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); } -TEST_F(QuicConnectionTest, DelayRTOWithAckReceipt) { +TEST_P(QuicConnectionTest, DelayRTOWithAckReceipt) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)) + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) .Times(2); - connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL); - connection_.SendStreamDataWithString(2, "bar", 0, !kFin, NULL); + connection_.SendStreamDataWithString(2, "foo", 0, !kFin, NULL); + connection_.SendStreamDataWithString(3, "bar", 0, !kFin, NULL); QuicAlarm* retransmission_alarm = connection_.GetRetransmissionAlarm(); EXPECT_TRUE(retransmission_alarm->IsSet()); + EXPECT_EQ(clock_.Now().Add(DefaultRetransmissionTime()), + retransmission_alarm->deadline()); // Advance the time right before the RTO, then receive an ack for the first // packet to delay the RTO. clock_.AdvanceTime(DefaultRetransmissionTime()); - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1); - QuicAckFrame ack(1, QuicTime::Zero(), 0); - ack.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + QuicAckFrame ack = InitAckFrame(1, 0); ProcessAckPacket(&ack); EXPECT_TRUE(retransmission_alarm->IsSet()); + EXPECT_GT(retransmission_alarm->deadline(), clock_.Now()); // Move forward past the original RTO and ensure the RTO is still pending. - clock_.AdvanceTime(DefaultRetransmissionTime()); + clock_.AdvanceTime(DefaultRetransmissionTime().Multiply(2)); // Ensure the second packet gets retransmitted when it finally fires. EXPECT_TRUE(retransmission_alarm->IsSet()); - EXPECT_GE(retransmission_alarm->deadline(), clock_.ApproximateNow()); - clock_.AdvanceTime(DefaultRetransmissionTime()); EXPECT_LT(retransmission_alarm->deadline(), clock_.ApproximateNow()); - EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout()); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, RTO_RETRANSMISSION, _)); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); // Manually cancel the alarm to simulate a real test. connection_.GetRetransmissionAlarm()->Fire(); @@ -2245,29 +2558,24 @@ TEST_F(QuicConnectionTest, DelayRTOWithAckReceipt) { // than previously. EXPECT_TRUE(retransmission_alarm->IsSet()); QuicTime next_rto_time = retransmission_alarm->deadline(); - QuicTime::Delta expected_rto = - connection_.sent_packet_manager().GetRetransmissionDelay(); - EXPECT_EQ(next_rto_time, clock_.ApproximateNow().Add(expected_rto)); + QuicTime expected_rto_time = + connection_.sent_packet_manager().GetRetransmissionTime(); + EXPECT_EQ(next_rto_time, expected_rto_time); } -TEST_F(QuicConnectionTest, TestQueued) { +TEST_P(QuicConnectionTest, TestQueued) { EXPECT_EQ(0u, connection_.NumQueuedPackets()); - writer_->set_blocked(true); + BlockOnNextWrite(); connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL); EXPECT_EQ(1u, connection_.NumQueuedPackets()); - // Attempt to send all packets, but since we're actually still - // blocked, they should all remain queued. - EXPECT_FALSE(connection_.OnCanWrite()); - EXPECT_EQ(1u, connection_.NumQueuedPackets()); - // Unblock the writes and actually send. - writer_->set_blocked(false); - EXPECT_TRUE(connection_.OnCanWrite()); + writer_->SetWritable(); + connection_.OnCanWrite(); EXPECT_EQ(0u, connection_.NumQueuedPackets()); } -TEST_F(QuicConnectionTest, CloseFecGroup) { +TEST_P(QuicConnectionTest, CloseFecGroup) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); // Don't send missing packet 1. // Don't send missing packet 2. @@ -2276,45 +2584,52 @@ TEST_F(QuicConnectionTest, CloseFecGroup) { ASSERT_EQ(1u, connection_.NumFecGroups()); // Now send non-fec protected ack packet and close the group. - QuicAckFrame frame(0, QuicTime::Zero(), 5); - creator_.set_sequence_number(4); - ProcessAckPacket(&frame); + peer_creator_.set_sequence_number(4); + if (version() > QUIC_VERSION_15) { + QuicStopWaitingFrame frame = InitStopWaitingFrame(5); + ProcessStopWaitingPacket(&frame); + } else { + QuicAckFrame frame = InitAckFrame(0, 5); + ProcessAckPacket(&frame); + } ASSERT_EQ(0u, connection_.NumFecGroups()); } -TEST_F(QuicConnectionTest, NoQuicCongestionFeedbackFrame) { +TEST_P(QuicConnectionTest, NoQuicCongestionFeedbackFrame) { SendAckPacketToPeer(); - EXPECT_TRUE(last_feedback() == NULL); + EXPECT_TRUE(writer_->feedback_frames().empty()); } -TEST_F(QuicConnectionTest, WithQuicCongestionFeedbackFrame) { +TEST_P(QuicConnectionTest, WithQuicCongestionFeedbackFrame) { QuicCongestionFeedbackFrame info; info.type = kFixRate; info.fix_rate.bitrate = QuicBandwidth::FromBytesPerSecond(123); SetFeedback(&info); SendAckPacketToPeer(); - EXPECT_EQ(kFixRate, last_feedback()->type); - EXPECT_EQ(info.fix_rate.bitrate, last_feedback()->fix_rate.bitrate); + ASSERT_FALSE(writer_->feedback_frames().empty()); + ASSERT_EQ(kFixRate, writer_->feedback_frames()[0].type); + ASSERT_EQ(info.fix_rate.bitrate, + writer_->feedback_frames()[0].fix_rate.bitrate); } -TEST_F(QuicConnectionTest, UpdateQuicCongestionFeedbackFrame) { +TEST_P(QuicConnectionTest, UpdateQuicCongestionFeedbackFrame) { SendAckPacketToPeer(); - EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _)); + EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _)); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessPacket(1); } -TEST_F(QuicConnectionTest, DontUpdateQuicCongestionFeedbackFrameForRevived) { +TEST_P(QuicConnectionTest, DontUpdateQuicCongestionFeedbackFrameForRevived) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); SendAckPacketToPeer(); // Process an FEC packet, and revive the missing data packet // but only contact the receive_algorithm once. - EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _)); + EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _)); ProcessFecPacket(2, 1, true, !kEntropyFlag, NULL); } -TEST_F(QuicConnectionTest, InitialTimeout) { +TEST_P(QuicConnectionTest, InitialTimeout) { EXPECT_TRUE(connection_.connected()); EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_CONNECTION_TIMED_OUT, false)); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); @@ -2331,13 +2646,59 @@ TEST_F(QuicConnectionTest, InitialTimeout) { EXPECT_FALSE(connection_.connected()); EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); EXPECT_FALSE(connection_.GetResumeWritesAlarm()->IsSet()); EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); } -TEST_F(QuicConnectionTest, TimeoutAfterSend) { +TEST_P(QuicConnectionTest, PingAfterSend) { + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, HasOpenDataStreams()).WillRepeatedly(Return(true)); + EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); + + // Advance to 5ms, and send a packet to the peer, which will set + // the ping alarm. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + SendStreamDataToPeer(1, "GET /", 0, kFin, NULL); + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + EXPECT_EQ(clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(15)), + connection_.GetPingAlarm()->deadline()); + + // Now recevie and ACK of the previous packet, which will move the + // ping alarm forward. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + QuicAckFrame frame = InitAckFrame(1, 0); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + ProcessAckPacket(&frame); + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + EXPECT_EQ(clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(15)), + connection_.GetPingAlarm()->deadline()); + + writer_->Reset(); + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(15)); + connection_.GetPingAlarm()->Fire(); + EXPECT_EQ(1u, writer_->frame_count()); + if (version() > QUIC_VERSION_17) { + ASSERT_EQ(1u, writer_->ping_frames().size()); + } else { + ASSERT_EQ(1u, writer_->stream_frames().size()); + EXPECT_EQ(kCryptoStreamId, writer_->stream_frames()[0].stream_id); + EXPECT_EQ(0u, writer_->stream_frames()[0].offset); + } + writer_->Reset(); + + EXPECT_CALL(visitor_, HasOpenDataStreams()).WillRepeatedly(Return(false)); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + SendAckPacketToPeer(); + + EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, TimeoutAfterSend) { EXPECT_TRUE(connection_.connected()); QuicTime default_timeout = clock_.ApproximateNow().Add( @@ -2373,12 +2734,11 @@ TEST_F(QuicConnectionTest, TimeoutAfterSend) { EXPECT_FALSE(connection_.connected()); } -// TODO(ianswett): Add scheduler tests when should_retransmit is false. -TEST_F(QuicConnectionTest, SendScheduler) { +TEST_P(QuicConnectionTest, SendScheduler) { // Test that if we send a packet without delay, it is not queued. QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + TimeUntilSend(_, _, _)).WillOnce( testing::Return(QuicTime::Delta::Zero())); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); connection_.SendPacket( @@ -2386,47 +2746,35 @@ TEST_F(QuicConnectionTest, SendScheduler) { EXPECT_EQ(0u, connection_.NumQueuedPackets()); } -TEST_F(QuicConnectionTest, SendSchedulerDelay) { +TEST_P(QuicConnectionTest, SendSchedulerDelay) { // Test that if we send a packet with a delay, it ends up queued. QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + TimeUntilSend(_, _, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(1))); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 1, _, _, _)).Times(0); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(0); connection_.SendPacket( ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(1u, connection_.NumQueuedPackets()); } -TEST_F(QuicConnectionTest, SendSchedulerForce) { - // Test that if we force send a packet, it is not queued. +TEST_P(QuicConnectionTest, SendSchedulerEAGAIN) { QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); + BlockOnNextWrite(); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, NACK_RETRANSMISSION, _, _)).Times(0); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); - connection_.SendPacket( - ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); - // XXX: fixme. was: connection_.SendPacket(1, packet, kForce); - EXPECT_EQ(0u, connection_.NumQueuedPackets()); -} - -TEST_F(QuicConnectionTest, SendSchedulerEAGAIN) { - QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); - writer_->set_blocked(true); - EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + TimeUntilSend(_, _, _)).WillOnce( testing::Return(QuicTime::Delta::Zero())); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 1, _, _, _)).Times(0); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(0); connection_.SendPacket( ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(1u, connection_.NumQueuedPackets()); } -TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) { +TEST_P(QuicConnectionTest, SendSchedulerDelayThenSend) { // Test that if we send a packet with a delay, it ends up queued. QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + TimeUntilSend(_, _, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(1))); connection_.SendPacket( ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); @@ -2435,7 +2783,7 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) { // Advance the clock to fire the alarm, and configure the scheduler // to permit the packet to be sent. EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly( + TimeUntilSend(_, _, _)).WillRepeatedly( testing::Return(QuicTime::Delta::Zero())); clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1)); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); @@ -2443,43 +2791,35 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) { EXPECT_EQ(0u, connection_.NumQueuedPackets()); } -TEST_F(QuicConnectionTest, SendSchedulerDelayThenRetransmit) { - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, NOT_RETRANSMISSION, _, _)) - .WillRepeatedly(testing::Return(QuicTime::Delta::Zero())); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1); - EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, 1, _, NOT_RETRANSMISSION, _)); - connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL); +TEST_P(QuicConnectionTest, SendSchedulerDelayThenRetransmit) { + CongestionUnblockWrites(); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)); + connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL); 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 in the // sent packet manager, but not yet serialized. - EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout()); - EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, RTO_RETRANSMISSION, _, _)).WillOnce( - testing::Return(QuicTime::Delta::FromMicroseconds(1))); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + CongestionBlockWrites(); connection_.GetRetransmissionAlarm()->Fire(); EXPECT_EQ(0u, connection_.NumQueuedPackets()); // Advance the clock to fire the alarm, and configure the scheduler // to permit the packet to be sent. - EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, RTO_RETRANSMISSION, _, _)).Times(2). - WillRepeatedly(testing::Return(QuicTime::Delta::Zero())); + CongestionUnblockWrites(); // Ensure the scheduler is notified this is a retransmit. - EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, _, RTO_RETRANSMISSION, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1)); connection_.GetSendAlarm()->Fire(); EXPECT_EQ(0u, connection_.NumQueuedPackets()); } -TEST_F(QuicConnectionTest, SendSchedulerDelayAndQueue) { +TEST_P(QuicConnectionTest, SendSchedulerDelayAndQueue) { QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + TimeUntilSend(_, _, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(1))); connection_.SendPacket( ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); @@ -2492,11 +2832,11 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayAndQueue) { EXPECT_EQ(2u, connection_.NumQueuedPackets()); } -TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) { +TEST_P(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + TimeUntilSend(_, _, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(10))); connection_.SendPacket( ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); @@ -2504,9 +2844,9 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) { // Now send non-retransmitting information, that we're not going to // retransmit 3. The far end should stop waiting for it. - QuicAckFrame frame(0, QuicTime::Zero(), 1); + QuicAckFrame frame = InitAckFrame(0, 1); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly( + TimeUntilSend(_, _, _)).WillRepeatedly( testing::Return(QuicTime::Delta::Zero())); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); @@ -2517,11 +2857,11 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) { EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); } -TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) { +TEST_P(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + TimeUntilSend(_, _, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(10))); connection_.SendPacket( ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); @@ -2529,41 +2869,42 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) { // Now send non-retransmitting information, that we're not going to // retransmit 3. The far end should stop waiting for it. - QuicAckFrame frame(0, QuicTime::Zero(), 1); + QuicAckFrame frame = InitAckFrame(0, 1); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + TimeUntilSend(_, _, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(1))); ProcessAckPacket(&frame); EXPECT_EQ(1u, connection_.NumQueuedPackets()); } -TEST_F(QuicConnectionTest, SendSchedulerDelayThenOnCanWrite) { +TEST_P(QuicConnectionTest, SendSchedulerDelayThenOnCanWrite) { + // TODO(ianswett): This test is unrealistic, because we would not serialize + // new data if the send algorithm said not to. QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); - EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( - testing::Return(QuicTime::Delta::FromMicroseconds(10))); + CongestionBlockWrites(); connection_.SendPacket( ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(1u, connection_.NumQueuedPackets()); - // OnCanWrite should not send the packet (because of the delay) - // but should still return true. - EXPECT_TRUE(connection_.OnCanWrite()); - EXPECT_EQ(1u, connection_.NumQueuedPackets()); + // OnCanWrite should send the packet, because it won't consult the send + // algorithm for queued packets. + connection_.OnCanWrite(); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); } -TEST_F(QuicConnectionTest, TestQueueLimitsOnSendStreamData) { +TEST_P(QuicConnectionTest, TestQueueLimitsOnSendStreamData) { // All packets carry version info till version is negotiated. size_t payload_length; - connection_.options()->max_packet_length = - GetPacketLengthForOneStream( - connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER, - NOT_IN_FEC_GROUP, &payload_length); + size_t length = GetPacketLengthForOneStream( + connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER, + NOT_IN_FEC_GROUP, &payload_length); + QuicConnectionPeer::GetPacketCreator(&connection_)->set_max_packet_length( + length); // Queue the first packet. EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + TimeUntilSend(_, _, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(10))); const string payload(payload_length, 'a'); EXPECT_EQ(0u, @@ -2572,27 +2913,66 @@ TEST_F(QuicConnectionTest, TestQueueLimitsOnSendStreamData) { EXPECT_EQ(0u, connection_.NumQueuedPackets()); } -TEST_F(QuicConnectionTest, LoopThroughSendingPackets) { +TEST_P(QuicConnectionTest, LoopThroughSendingPackets) { // All packets carry version info till version is negotiated. size_t payload_length; - connection_.options()->max_packet_length = - GetPacketLengthForOneStream( + // GetPacketLengthForOneStream() assumes a stream offset of 0 in determining + // packet length. The size of the offset field in a stream frame is 0 for + // offset 0, and 2 for non-zero offsets up through 16K. Increase + // max_packet_length by 2 so that subsequent packets containing subsequent + // stream frames with non-zero offets will fit within the packet length. + size_t length = 2 + GetPacketLengthForOneStream( connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP, &payload_length); + QuicConnectionPeer::GetPacketCreator(&connection_)->set_max_packet_length( + length); // Queue the first packet. EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(7); - // The first stream frame will consume 2 fewer bytes than the other six. - const string payload(payload_length * 7 - 12, 'a'); + // The first stream frame will have 2 fewer overhead bytes than the other six. + const string payload(payload_length * 7 + 2, 'a'); EXPECT_EQ(payload.size(), connection_.SendStreamDataWithString(1, payload, 0, !kFin, NULL).bytes_consumed); } -TEST_F(QuicConnectionTest, SendDelayedAckOnTimer) { +TEST_P(QuicConnectionTest, SendDelayedAck) { QuicTime ack_time = clock_.ApproximateNow().Add(DefaultDelayedAckTime()); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + const uint8 tag = 0x07; + connection_.SetDecrypter(new StrictTaggingDecrypter(tag), + ENCRYPTION_INITIAL); + framer_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag)); + // Process a packet from the non-crypto stream. + frame1_.stream_id = 3; + + // The same as ProcessPacket(1) except that ENCRYPTION_INITIAL is used + // instead of ENCRYPTION_NONE. + EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1); + ProcessDataPacketAtLevel(1, 0, !kEntropyFlag, ENCRYPTION_INITIAL); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + // Simulate delayed ack alarm firing. + connection_.GetAckAlarm()->Fire(); + // Check that ack is sent and that delayed ack alarm is reset. + if (version() > QUIC_VERSION_15) { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(1u, writer_->frame_count()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, SendEarlyDelayedAckForCrypto) { + QuicTime ack_time = clock_.ApproximateNow(); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + // Process a packet from the crypto stream, which is frame1_'s default. ProcessPacket(1); // Check if delayed ack timer is running for the expected interval. EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); @@ -2600,90 +2980,185 @@ TEST_F(QuicConnectionTest, SendDelayedAckOnTimer) { // Simulate delayed ack alarm firing. connection_.GetAckAlarm()->Fire(); // Check that ack is sent and that delayed ack alarm is reset. - EXPECT_EQ(1u, writer_->frame_count()); - EXPECT_TRUE(writer_->ack()); + if (version() > QUIC_VERSION_15) { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(1u, writer_->frame_count()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); } -TEST_F(QuicConnectionTest, SendDelayedAckOnSecondPacket) { +TEST_P(QuicConnectionTest, SendDelayedAckOnSecondPacket) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessPacket(1); ProcessPacket(2); // Check that ack is sent and that delayed ack alarm is reset. - EXPECT_EQ(1u, writer_->frame_count()); - EXPECT_TRUE(writer_->ack()); + if (version() > QUIC_VERSION_15) { + EXPECT_EQ(2u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(1u, writer_->frame_count()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); } -TEST_F(QuicConnectionTest, NoAckOnOldNacks) { +TEST_P(QuicConnectionTest, NoAckOnOldNacks) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); // Drop one packet, triggering a sequence of acks. ProcessPacket(2); - EXPECT_EQ(1u, writer_->frame_count()); - EXPECT_TRUE(writer_->ack()); + size_t frames_per_ack = version() > QUIC_VERSION_15 ? 2 : 1; + EXPECT_EQ(frames_per_ack, writer_->frame_count()); + EXPECT_FALSE(writer_->ack_frames().empty()); writer_->Reset(); ProcessPacket(3); - EXPECT_EQ(1u, writer_->frame_count()); - EXPECT_TRUE(writer_->ack()); + EXPECT_EQ(frames_per_ack, writer_->frame_count()); + EXPECT_FALSE(writer_->ack_frames().empty()); writer_->Reset(); ProcessPacket(4); - EXPECT_EQ(1u, writer_->frame_count()); - EXPECT_TRUE(writer_->ack()); + EXPECT_EQ(frames_per_ack, writer_->frame_count()); + EXPECT_FALSE(writer_->ack_frames().empty()); writer_->Reset(); ProcessPacket(5); - EXPECT_EQ(1u, writer_->frame_count()); - EXPECT_TRUE(writer_->ack()); - // Now only set the timer on the 6th packet, instead of sending another ack. + EXPECT_EQ(frames_per_ack, writer_->frame_count()); + EXPECT_FALSE(writer_->ack_frames().empty()); writer_->Reset(); + // Now only set the timer on the 6th packet, instead of sending another ack. ProcessPacket(6); EXPECT_EQ(0u, writer_->frame_count()); EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); } -TEST_F(QuicConnectionTest, SendDelayedAckOnOutgoingPacket) { +TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingPacket) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessPacket(1); - connection_.SendStreamDataWithString(kStreamId3, "foo", 0, !kFin, NULL); + connection_.SendStreamDataWithString(kClientDataStreamId1, "foo", 0, + !kFin, NULL); // Check that ack is bundled with outgoing data and that delayed ack // alarm is reset. - EXPECT_EQ(2u, writer_->frame_count()); - EXPECT_TRUE(writer_->ack()); + if (version() > QUIC_VERSION_15) { + EXPECT_EQ(3u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); } -TEST_F(QuicConnectionTest, DontSendDelayedAckOnOutgoingCryptoPacket) { +TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingCryptoPacket) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessPacket(1); connection_.SendStreamDataWithString(kCryptoStreamId, "foo", 0, !kFin, NULL); - // Check that ack is not bundled with outgoing data. + // Check that ack is bundled with outgoing crypto data. + EXPECT_EQ(version() <= QUIC_VERSION_15 ? 2u : 3u, writer_->frame_count()); + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, BundleAckForSecondCHLO) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_CALL(visitor_, OnCanWrite()).WillOnce( + IgnoreResult(InvokeWithoutArgs(&connection_, + &TestConnection::SendCryptoStreamData))); + // Process a packet from the crypto stream, which is frame1_'s default. + // Receiving the CHLO as packet 2 first will cause the connection to + // immediately send an ack, due to the packet gap. + ProcessPacket(2); + // Check that ack is sent and that delayed ack alarm is reset. + if (version() > QUIC_VERSION_15) { + EXPECT_EQ(3u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + } + EXPECT_EQ(1u, writer_->stream_frames().size()); + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, BundleAckWithDataOnIncomingAck) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + connection_.SendStreamDataWithString(kClientDataStreamId1, "foo", 0, + !kFin, NULL); + connection_.SendStreamDataWithString(kClientDataStreamId1, "foo", 3, + !kFin, NULL); + // Ack the second packet, which will retransmit the first packet. + QuicAckFrame ack = InitAckFrame(2, 0); + NackPacket(1, &ack); + SequenceNumberSet lost_packets; + lost_packets.insert(1); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + ProcessAckPacket(&ack); EXPECT_EQ(1u, writer_->frame_count()); - EXPECT_FALSE(writer_->ack()); - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(1u, writer_->stream_frames().size()); + writer_->Reset(); + + // Now ack the retransmission, which will both raise the high water mark + // and see if there is more data to send. + ack = InitAckFrame(3, 0); + NackPacket(1, &ack); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(SequenceNumberSet())); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + ProcessAckPacket(&ack); + + // Check that no packet is sent and the ack alarm isn't set. + EXPECT_EQ(0u, writer_->frame_count()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + writer_->Reset(); + + // Send the same ack, but send both data and an ack together. + ack = InitAckFrame(3, 0); + NackPacket(1, &ack); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(SequenceNumberSet())); + EXPECT_CALL(visitor_, OnCanWrite()).WillOnce( + IgnoreResult(InvokeWithoutArgs( + &connection_, + &TestConnection::EnsureWritableAndSendStreamData5))); + ProcessAckPacket(&ack); + + // Check that ack is bundled with outgoing data and the delayed ack + // alarm is reset. + if (version() > QUIC_VERSION_15) { + EXPECT_EQ(3u, writer_->frame_count()); + EXPECT_FALSE(writer_->stop_waiting_frames().empty()); + } else { + EXPECT_EQ(2u, writer_->frame_count()); + } + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_EQ(1u, writer_->stream_frames().size()); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); } -TEST_F(QuicConnectionTest, NoAckForClose) { +TEST_P(QuicConnectionTest, NoAckSentForClose) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessPacket(1); - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(0); EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, true)); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); ProcessClosePacket(2, 0); } -TEST_F(QuicConnectionTest, SendWhenDisconnected) { +TEST_P(QuicConnectionTest, SendWhenDisconnected) { EXPECT_TRUE(connection_.connected()); EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, false)); connection_.CloseConnection(QUIC_PEER_GOING_AWAY, false); EXPECT_FALSE(connection_.connected()); QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 1, _, _, _)).Times(0); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(0); connection_.SendPacket( ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); } -TEST_F(QuicConnectionTest, PublicReset) { +TEST_P(QuicConnectionTest, PublicReset) { QuicPublicResetPacket header; - header.public_header.guid = guid_; + header.public_header.connection_id = connection_id_; header.public_header.reset_flag = true; header.public_header.version_flag = false; header.rejected_sequence_number = 10101; @@ -2693,7 +3168,7 @@ TEST_F(QuicConnectionTest, PublicReset) { connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *packet); } -TEST_F(QuicConnectionTest, GoAway) { +TEST_P(QuicConnectionTest, GoAway) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); QuicGoAwayFrame goaway; @@ -2704,27 +3179,52 @@ TEST_F(QuicConnectionTest, GoAway) { ProcessGoAwayPacket(&goaway); } -TEST_F(QuicConnectionTest, InvalidPacket) { +TEST_P(QuicConnectionTest, WindowUpdate) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + QuicWindowUpdateFrame window_update; + window_update.stream_id = 3; + window_update.byte_offset = 1234; + EXPECT_CALL(visitor_, OnWindowUpdateFrames(_)); + ProcessFramePacket(QuicFrame(&window_update)); +} + +TEST_P(QuicConnectionTest, Blocked) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + QuicBlockedFrame blocked; + blocked.stream_id = 3; + EXPECT_CALL(visitor_, OnBlockedFrames(_)); + ProcessFramePacket(QuicFrame(&blocked)); +} + +TEST_P(QuicConnectionTest, InvalidPacket) { EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false)); QuicEncryptedPacket encrypted(NULL, 0); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), encrypted); // The connection close packet should have error details. - ASSERT_TRUE(last_close() != NULL); - EXPECT_EQ("Unable to read public flags.", last_close()->error_details); + ASSERT_FALSE(writer_->connection_close_frames().empty()); + EXPECT_EQ("Unable to read public flags.", + writer_->connection_close_frames()[0].error_details); } -TEST_F(QuicConnectionTest, MissingPacketsBeforeLeastUnacked) { - QuicAckFrame ack(0, QuicTime::Zero(), 4); +TEST_P(QuicConnectionTest, MissingPacketsBeforeLeastUnacked) { // Set the sequence number of the ack packet to be least unacked (4). - creator_.set_sequence_number(3); + peer_creator_.set_sequence_number(3); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - ProcessAckPacket(&ack); + if (version() > QUIC_VERSION_15) { + QuicStopWaitingFrame frame = InitStopWaitingFrame(4); + ProcessStopWaitingPacket(&frame); + } else { + QuicAckFrame ack = InitAckFrame(0, 4); + ProcessAckPacket(&ack); + } EXPECT_TRUE(outgoing_ack()->received_info.missing_packets.empty()); } -TEST_F(QuicConnectionTest, ReceivedEntropyHashCalculation) { - EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true)); +TEST_P(QuicConnectionTest, ReceivedEntropyHashCalculation) { + EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1)); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessDataPacket(1, 1, kEntropyFlag); ProcessDataPacket(4, 1, kEntropyFlag); @@ -2733,40 +3233,66 @@ TEST_F(QuicConnectionTest, ReceivedEntropyHashCalculation) { EXPECT_EQ(146u, outgoing_ack()->received_info.entropy_hash); } -TEST_F(QuicConnectionTest, UpdateEntropyForReceivedPackets) { - EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true)); +TEST_P(QuicConnectionTest, ReceivedEntropyHashCalculationHalfFEC) { + // FEC packets should not change the entropy hash calculation. + EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1)); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + ProcessDataPacket(1, 1, kEntropyFlag); + ProcessFecPacket(4, 1, false, kEntropyFlag, NULL); + ProcessDataPacket(3, 3, !kEntropyFlag); + ProcessFecPacket(7, 3, false, kEntropyFlag, NULL); + EXPECT_EQ(146u, outgoing_ack()->received_info.entropy_hash); +} + +TEST_P(QuicConnectionTest, UpdateEntropyForReceivedPackets) { + EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1)); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessDataPacket(1, 1, kEntropyFlag); ProcessDataPacket(5, 1, kEntropyFlag); ProcessDataPacket(4, 1, !kEntropyFlag); EXPECT_EQ(34u, outgoing_ack()->received_info.entropy_hash); // Make 4th packet my least unacked, and update entropy for 2, 3 packets. - QuicAckFrame ack(0, QuicTime::Zero(), 4); - QuicPacketEntropyHash kRandomEntropyHash = 129u; - ack.sent_info.entropy_hash = kRandomEntropyHash; - creator_.set_sequence_number(5); + peer_creator_.set_sequence_number(5); QuicPacketEntropyHash six_packet_entropy_hash = 0; - if (ProcessAckPacket(&ack)) { - six_packet_entropy_hash = 1 << 6; + QuicPacketEntropyHash kRandomEntropyHash = 129u; + if (version() > QUIC_VERSION_15) { + QuicStopWaitingFrame frame = InitStopWaitingFrame(4); + frame.entropy_hash = kRandomEntropyHash; + if (ProcessStopWaitingPacket(&frame)) { + six_packet_entropy_hash = 1 << 6; + } + } else { + QuicAckFrame ack = InitAckFrame(0, 4); + ack.sent_info.entropy_hash = kRandomEntropyHash; + if (ProcessAckPacket(&ack)) { + six_packet_entropy_hash = 1 << 6; + } } EXPECT_EQ((kRandomEntropyHash + (1 << 5) + six_packet_entropy_hash), outgoing_ack()->received_info.entropy_hash); } -TEST_F(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) { - EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true)); +TEST_P(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) { + EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1)); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessDataPacket(1, 1, kEntropyFlag); ProcessDataPacket(5, 1, !kEntropyFlag); ProcessDataPacket(22, 1, kEntropyFlag); EXPECT_EQ(66u, outgoing_ack()->received_info.entropy_hash); - creator_.set_sequence_number(22); + peer_creator_.set_sequence_number(22); QuicPacketEntropyHash kRandomEntropyHash = 85u; // Current packet is the least unacked packet. - QuicAckFrame ack(0, QuicTime::Zero(), 23); - ack.sent_info.entropy_hash = kRandomEntropyHash; - QuicPacketEntropyHash ack_entropy_hash = ProcessAckPacket(&ack); + QuicPacketEntropyHash ack_entropy_hash; + if (version() > QUIC_VERSION_15) { + QuicStopWaitingFrame frame = InitStopWaitingFrame(23); + frame.entropy_hash = kRandomEntropyHash; + ack_entropy_hash = ProcessStopWaitingPacket(&frame); + } else { + QuicAckFrame ack = InitAckFrame(0, 23); + ack.sent_info.entropy_hash = kRandomEntropyHash; + ack_entropy_hash = ProcessAckPacket(&ack); + } EXPECT_EQ((kRandomEntropyHash + ack_entropy_hash), outgoing_ack()->received_info.entropy_hash); ProcessDataPacket(25, 1, kEntropyFlag); @@ -2774,13 +3300,13 @@ TEST_F(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) { outgoing_ack()->received_info.entropy_hash); } -TEST_F(QuicConnectionTest, EntropyCalculationForTruncatedAck) { - EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true)); +TEST_P(QuicConnectionTest, EntropyCalculationForTruncatedAck) { + EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1)); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); QuicPacketEntropyHash entropy[51]; entropy[0] = 0; for (int i = 1; i < 51; ++i) { - bool should_send = i % 10 != 0; + bool should_send = i % 10 != 1; bool entropy_flag = (i & (i - 1)) != 0; if (!should_send) { entropy[i] = entropy[i - 1]; @@ -2793,15 +3319,14 @@ TEST_F(QuicConnectionTest, EntropyCalculationForTruncatedAck) { } ProcessDataPacket(i, 1, entropy_flag); } - // Till 50 since 50th packet is not sent. for (int i = 1; i < 50; ++i) { EXPECT_EQ(entropy[i], QuicConnectionPeer::ReceivedEntropyHash( &connection_, i)); } } -TEST_F(QuicConnectionTest, CheckSentEntropyHash) { - creator_.set_sequence_number(1); +TEST_P(QuicConnectionTest, CheckSentEntropyHash) { + peer_creator_.set_sequence_number(1); SequenceNumberSet missing_packets; QuicPacketEntropyHash entropy_hash = 0; QuicPacketSequenceNumber max_sequence_number = 51; @@ -2829,11 +3354,12 @@ TEST_F(QuicConnectionTest, CheckSentEntropyHash) { << ""; } -TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacket) { +TEST_P(QuicConnectionTest, ServerSendsVersionNegotiationPacket) { + connection_.SetSupportedVersions(QuicSupportedVersions()); framer_.set_version_for_tests(QUIC_VERSION_UNSUPPORTED); QuicPacketHeader header; - header.public_header.guid = guid_; + header.public_header.connection_id = connection_id_; header.public_header.reset_flag = false; header.public_header.version_flag = true; header.entropy_flag = false; @@ -2845,17 +3371,17 @@ TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacket) { QuicFrame frame(&frame1_); frames.push_back(frame); scoped_ptr<QuicPacket> packet( - framer_.BuildUnsizedDataPacket(header, frames).packet); + BuildUnsizedDataPacket(&framer_, header, frames).packet); scoped_ptr<QuicEncryptedPacket> encrypted( framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet)); - framer_.set_version(QuicVersionMax()); + framer_.set_version(version()); connection_.set_is_server(true); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); EXPECT_TRUE(writer_->version_negotiation_packet() != NULL); size_t num_versions = arraysize(kSupportedQuicVersions); - EXPECT_EQ(num_versions, + ASSERT_EQ(num_versions, writer_->version_negotiation_packet()->versions.size()); // We expect all versions in kSupportedQuicVersions to be @@ -2866,11 +3392,12 @@ TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacket) { } } -TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlocked) { +TEST_P(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlocked) { + connection_.SetSupportedVersions(QuicSupportedVersions()); framer_.set_version_for_tests(QUIC_VERSION_UNSUPPORTED); QuicPacketHeader header; - header.public_header.guid = guid_; + header.public_header.connection_id = connection_id_; header.public_header.reset_flag = false; header.public_header.version_flag = true; header.entropy_flag = false; @@ -2882,24 +3409,23 @@ TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlocked) { QuicFrame frame(&frame1_); frames.push_back(frame); scoped_ptr<QuicPacket> packet( - framer_.BuildUnsizedDataPacket(header, frames).packet); + BuildUnsizedDataPacket(&framer_, header, frames).packet); scoped_ptr<QuicEncryptedPacket> encrypted( framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet)); - framer_.set_version(QuicVersionMax()); + framer_.set_version(version()); connection_.set_is_server(true); - writer_->set_blocked(true); + BlockOnNextWrite(); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); EXPECT_EQ(0u, writer_->last_packet_size()); EXPECT_TRUE(connection_.HasQueuedData()); - EXPECT_TRUE(QuicConnectionPeer::IsWriteBlocked(&connection_)); - writer_->set_blocked(false); + writer_->SetWritable(); connection_.OnCanWrite(); EXPECT_TRUE(writer_->version_negotiation_packet() != NULL); size_t num_versions = arraysize(kSupportedQuicVersions); - EXPECT_EQ(num_versions, + ASSERT_EQ(num_versions, writer_->version_negotiation_packet()->versions.size()); // We expect all versions in kSupportedQuicVersions to be @@ -2910,12 +3436,13 @@ TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlocked) { } } -TEST_F(QuicConnectionTest, +TEST_P(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlockedDataBuffered) { + connection_.SetSupportedVersions(QuicSupportedVersions()); framer_.set_version_for_tests(QUIC_VERSION_UNSUPPORTED); QuicPacketHeader header; - header.public_header.guid = guid_; + header.public_header.connection_id = connection_id_; header.public_header.reset_flag = false; header.public_header.version_flag = true; header.entropy_flag = false; @@ -2927,27 +3454,26 @@ TEST_F(QuicConnectionTest, QuicFrame frame(&frame1_); frames.push_back(frame); scoped_ptr<QuicPacket> packet( - framer_.BuildUnsizedDataPacket(header, frames).packet); + BuildUnsizedDataPacket(&framer_, header, frames).packet); scoped_ptr<QuicEncryptedPacket> encrypted( framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet)); - framer_.set_version(QuicVersionMax()); + framer_.set_version(version()); connection_.set_is_server(true); - writer_->set_blocked(true); + BlockOnNextWrite(); writer_->set_is_write_blocked_data_buffered(true); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); EXPECT_EQ(0u, writer_->last_packet_size()); EXPECT_FALSE(connection_.HasQueuedData()); - EXPECT_TRUE(QuicConnectionPeer::IsWriteBlocked(&connection_)); } -TEST_F(QuicConnectionTest, ClientHandlesVersionNegotiation) { +TEST_P(QuicConnectionTest, ClientHandlesVersionNegotiation) { // Start out with some unsupported version. QuicConnectionPeer::GetFramer(&connection_)->set_version_for_tests( QUIC_VERSION_UNSUPPORTED); QuicPacketHeader header; - header.public_header.guid = guid_; + header.public_header.connection_id = connection_id_; header.public_header.reset_flag = false; header.public_header.version_flag = true; header.entropy_flag = false; @@ -2973,7 +3499,7 @@ TEST_F(QuicConnectionTest, ClientHandlesVersionNegotiation) { QuicFrame frame(&frame1_); frames.push_back(frame); scoped_ptr<QuicPacket> packet( - framer_.BuildUnsizedDataPacket(header, frames).packet); + BuildUnsizedDataPacket(&framer_, header, frames).packet); encrypted.reset(framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet)); EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); @@ -2983,9 +3509,9 @@ TEST_F(QuicConnectionTest, ClientHandlesVersionNegotiation) { QuicConnectionPeer::GetPacketCreator(&connection_))); } -TEST_F(QuicConnectionTest, BadVersionNegotiation) { +TEST_P(QuicConnectionTest, BadVersionNegotiation) { QuicPacketHeader header; - header.public_header.guid = guid_; + header.public_header.connection_id = connection_id_; header.public_header.reset_flag = false; header.public_header.version_flag = true; header.entropy_flag = false; @@ -3009,51 +3535,37 @@ TEST_F(QuicConnectionTest, BadVersionNegotiation) { connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); } -TEST_F(QuicConnectionTest, CheckSendStats) { - EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)); +TEST_P(QuicConnectionTest, CheckSendStats) { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); connection_.SendStreamDataWithString(3, "first", 0, !kFin, NULL); - size_t first_packet_size = last_sent_packet_size(); + size_t first_packet_size = writer_->last_packet_size(); - EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, _, NOT_RETRANSMISSION, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); connection_.SendStreamDataWithString(5, "second", 0, !kFin, NULL); - size_t second_packet_size = last_sent_packet_size(); + size_t second_packet_size = writer_->last_packet_size(); // 2 retransmissions due to rto, 1 due to explicit nack. - EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout()); - EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, _, RTO_RETRANSMISSION, _)).Times(2); - EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, _, NACK_RETRANSMISSION, _)); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(3); - EXPECT_CALL(visitor_, OnCanWrite()).WillRepeatedly(Return(true)); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3); // Retransmit due to RTO. clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10)); connection_.GetRetransmissionAlarm()->Fire(); // Retransmit due to explicit nacks. - QuicAckFrame nack_three(4, QuicTime::Zero(), 0); - nack_three.received_info.missing_packets.insert(3); - nack_three.received_info.missing_packets.insert(1); - nack_three.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 4) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); - QuicFrame frame(&nack_three); - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1); - EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(1); - EXPECT_CALL(visitor_, OnCanWrite()).Times(4).WillRepeatedly(Return(true)); - EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - - ProcessFramePacket(frame); - ProcessFramePacket(frame); - ProcessFramePacket(frame); - - EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillOnce( - Return(QuicTime::Delta::Zero())); + QuicAckFrame nack_three = InitAckFrame(4, 0); + NackPacket(3, &nack_three); + NackPacket(1, &nack_three); + SequenceNumberSet lost_packets; + lost_packets.insert(1); + lost_packets.insert(3); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + EXPECT_CALL(visitor_, OnCanWrite()).Times(2); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + ProcessAckPacket(&nack_three); + EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce( Return(QuicBandwidth::Zero())); @@ -3067,7 +3579,7 @@ TEST_F(QuicConnectionTest, CheckSendStats) { EXPECT_EQ(1u, stats.rto_count); } -TEST_F(QuicConnectionTest, CheckReceiveStats) { +TEST_P(QuicConnectionTest, CheckReceiveStats) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); size_t received_bytes = 0; @@ -3077,8 +3589,6 @@ TEST_F(QuicConnectionTest, CheckReceiveStats) { received_bytes += ProcessDataPacket(3, 1, !kEntropyFlag); received_bytes += ProcessFecPacket(4, 1, true, !kEntropyFlag, NULL); - EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillOnce( - Return(QuicTime::Delta::Zero())); EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce( Return(QuicBandwidth::Zero())); @@ -3090,7 +3600,7 @@ TEST_F(QuicConnectionTest, CheckReceiveStats) { EXPECT_EQ(1u, stats.packets_dropped); } -TEST_F(QuicConnectionTest, TestFecGroupLimits) { +TEST_P(QuicConnectionTest, TestFecGroupLimits) { // Create and return a group for 1. ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 1) != NULL); @@ -3112,9 +3622,9 @@ TEST_F(QuicConnectionTest, TestFecGroupLimits) { ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 3) == NULL); } -TEST_F(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) { +TEST_P(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) { // Construct a packet with stream frame and connection close frame. - header_.public_header.guid = guid_; + header_.public_header.connection_id = connection_id_; header_.packet_sequence_number = 1; header_.public_header.reset_flag = false; header_.public_header.version_flag = false; @@ -3131,7 +3641,7 @@ TEST_F(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) { frames.push_back(stream_frame); frames.push_back(close_frame); scoped_ptr<QuicPacket> packet( - framer_.BuildUnsizedDataPacket(header_, frames).packet); + BuildUnsizedDataPacket(&framer_, header_, frames).packet); EXPECT_TRUE(NULL != packet.get()); scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket( ENCRYPTION_NONE, 1, *packet)); @@ -3143,7 +3653,8 @@ TEST_F(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) { connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); } -TEST_F(QuicConnectionTest, SelectMutualVersion) { +TEST_P(QuicConnectionTest, SelectMutualVersion) { + connection_.SetSupportedVersions(QuicSupportedVersions()); // Set the connection to speak the lowest quic version. connection_.set_version(QuicVersionMin()); EXPECT_EQ(QuicVersionMin(), connection_.version()); @@ -3172,74 +3683,61 @@ TEST_F(QuicConnectionTest, SelectMutualVersion) { EXPECT_FALSE(connection_.SelectMutualVersion(unsupported_version)); } -TEST_F(QuicConnectionTest, ConnectionCloseWhenNotWriteBlocked) { - writer_->set_blocked(false); // Already default. +TEST_P(QuicConnectionTest, ConnectionCloseWhenWritable) { + EXPECT_FALSE(writer_->IsWriteBlocked()); - // Send a packet (but write will not block). + // Send a packet. connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL); EXPECT_EQ(0u, connection_.NumQueuedPackets()); EXPECT_EQ(1u, writer_->packets_write_attempts()); - // Send an erroneous packet to close the connection. - EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false)); - ProcessDataPacket(6000, 0, !kEntropyFlag); + TriggerConnectionClose(); EXPECT_EQ(2u, writer_->packets_write_attempts()); } -TEST_F(QuicConnectionTest, ConnectionCloseWhenWriteBlocked) { - EXPECT_EQ(0u, connection_.NumQueuedPackets()); - writer_->set_blocked(true); +TEST_P(QuicConnectionTest, ConnectionCloseGettingWriteBlocked) { + BlockOnNextWrite(); + TriggerConnectionClose(); + EXPECT_EQ(1u, writer_->packets_write_attempts()); + EXPECT_TRUE(writer_->IsWriteBlocked()); +} - // Send a packet to so that write will really block. +TEST_P(QuicConnectionTest, ConnectionCloseWhenWriteBlocked) { + BlockOnNextWrite(); connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL); EXPECT_EQ(1u, connection_.NumQueuedPackets()); EXPECT_EQ(1u, writer_->packets_write_attempts()); - - // Send an erroneous packet to close the connection. - EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false)); - ProcessDataPacket(6000, 0, !kEntropyFlag); - EXPECT_EQ(1u, writer_->packets_write_attempts()); -} - -TEST_F(QuicConnectionTest, ConnectionCloseWhenNothingPending) { - writer_->set_blocked(true); - - // Send an erroneous packet to close the connection. - EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false)); - ProcessDataPacket(6000, 0, !kEntropyFlag); + EXPECT_TRUE(writer_->IsWriteBlocked()); + TriggerConnectionClose(); EXPECT_EQ(1u, writer_->packets_write_attempts()); } -TEST_F(QuicConnectionTest, AckNotifierTriggerCallback) { +TEST_P(QuicConnectionTest, AckNotifierTriggerCallback) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); // Create a delegate which we expect to be called. - MockAckNotifierDelegate delegate; - EXPECT_CALL(delegate, OnAckNotification()).Times(1);; + scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate); + EXPECT_CALL(*delegate, OnAckNotification(_, _, _, _, _)).Times(1); // Send some data, which will register the delegate to be notified. - connection_.SendStreamDataWithString(1, "foo", 0, !kFin, &delegate); + connection_.SendStreamDataWithString(1, "foo", 0, !kFin, delegate.get()); // Process an ACK from the server which should trigger the callback. - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1); - QuicAckFrame frame(1, QuicTime::Zero(), 0); - frame.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + QuicAckFrame frame = InitAckFrame(1, 0); ProcessAckPacket(&frame); } -TEST_F(QuicConnectionTest, AckNotifierFailToTriggerCallback) { +TEST_P(QuicConnectionTest, AckNotifierFailToTriggerCallback) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); // Create a delegate which we don't expect to be called. - MockAckNotifierDelegate delegate; - EXPECT_CALL(delegate, OnAckNotification()).Times(0);; - - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(2); + scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate); + EXPECT_CALL(*delegate, OnAckNotification(_, _, _, _, _)).Times(0); // Send some data, which will register the delegate to be notified. This will // not be ACKed and so the delegate should never be called. - connection_.SendStreamDataWithString(1, "foo", 0, !kFin, &delegate); + connection_.SendStreamDataWithString(1, "foo", 0, !kFin, delegate.get()); // Send some other data which we will ACK. connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL); @@ -3247,82 +3745,183 @@ TEST_F(QuicConnectionTest, AckNotifierFailToTriggerCallback) { // Now we receive ACK for packets 2 and 3, but importantly missing packet 1 // which we registered to be notified about. - QuicAckFrame frame(3, QuicTime::Zero(), 0); - frame.received_info.missing_packets.insert(1); - frame.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); - EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)); + QuicAckFrame frame = InitAckFrame(3, 0); + NackPacket(1, &frame); + SequenceNumberSet lost_packets; + lost_packets.insert(1); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); ProcessAckPacket(&frame); } -TEST_F(QuicConnectionTest, AckNotifierCallbackAfterRetransmission) { +TEST_P(QuicConnectionTest, AckNotifierCallbackAfterRetransmission) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); // Create a delegate which we expect to be called. - MockAckNotifierDelegate delegate; - EXPECT_CALL(delegate, OnAckNotification()).Times(1);; - - // In total expect ACKs for all 4 packets. - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(4); + scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate); + EXPECT_CALL(*delegate, OnAckNotification(_, _, _, _, _)).Times(1); // Send four packets, and register to be notified on ACK of packet 2. - connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL); - connection_.SendStreamDataWithString(1, "bar", 0, !kFin, &delegate); - connection_.SendStreamDataWithString(1, "baz", 0, !kFin, NULL); - connection_.SendStreamDataWithString(1, "qux", 0, !kFin, NULL); - - // Now we receive ACK for packets 1, 3, and 4, which invokes fast retransmit. - QuicAckFrame frame(4, QuicTime::Zero(), 0); - frame.received_info.missing_packets.insert(2); - frame.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 4) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^ - QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); - EXPECT_CALL(*send_algorithm_, OnPacketLost(2, _)); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(2, _)); + connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL); + connection_.SendStreamDataWithString(3, "bar", 0, !kFin, delegate.get()); + connection_.SendStreamDataWithString(3, "baz", 0, !kFin, NULL); + connection_.SendStreamDataWithString(3, "qux", 0, !kFin, NULL); + + // Now we receive ACK for packets 1, 3, and 4 and lose 2. + QuicAckFrame frame = InitAckFrame(4, 0); + NackPacket(2, &frame); + SequenceNumberSet lost_packets; + lost_packets.insert(2); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); ProcessAckPacket(&frame); // Now we get an ACK for packet 5 (retransmitted packet 2), which should // trigger the callback. - QuicAckFrame second_ack_frame(5, QuicTime::Zero(), 0); - second_ack_frame.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 5); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillRepeatedly(Return(SequenceNumberSet())); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + QuicAckFrame second_ack_frame = InitAckFrame(5, 0); + ProcessAckPacket(&second_ack_frame); +} + +// AckNotifierCallback is triggered by the ack of a packet that timed +// out and was retransmitted, even though the retransmission has a +// different sequence number. +TEST_P(QuicConnectionTest, AckNotifierCallbackForAckAfterRTO) { + InSequence s; + + // Create a delegate which we expect to be called. + scoped_refptr<MockAckNotifierDelegate> delegate( + new StrictMock<MockAckNotifierDelegate>); + + QuicTime default_retransmission_time = clock_.ApproximateNow().Add( + DefaultRetransmissionTime()); + connection_.SendStreamDataWithString(3, "foo", 0, !kFin, delegate.get()); + EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked); + + EXPECT_EQ(1u, writer_->header().packet_sequence_number); + EXPECT_EQ(default_retransmission_time, + connection_.GetRetransmissionAlarm()->deadline()); + // Simulate the retransmission alarm firing. + clock_.AdvanceTime(DefaultRetransmissionTime()); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2u, _, _)); + connection_.GetRetransmissionAlarm()->Fire(); + EXPECT_EQ(2u, writer_->header().packet_sequence_number); + // We do not raise the high water mark yet. + EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked); + + // Ack the original packet. + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*delegate, OnAckNotification(1, _, 1, _, _)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + QuicAckFrame ack_frame = InitAckFrame(1, 0); + ProcessAckPacket(&ack_frame); + + // Delegate is not notified again when the retransmit is acked. + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + QuicAckFrame second_ack_frame = InitAckFrame(2, 0); + ProcessAckPacket(&second_ack_frame); +} + +// AckNotifierCallback is triggered by the ack of a packet that was +// previously nacked, even though the retransmission has a different +// sequence number. +TEST_P(QuicConnectionTest, AckNotifierCallbackForAckOfNackedPacket) { + InSequence s; + + // Create a delegate which we expect to be called. + scoped_refptr<MockAckNotifierDelegate> delegate( + new StrictMock<MockAckNotifierDelegate>); + + // Send four packets, and register to be notified on ACK of packet 2. + connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL); + connection_.SendStreamDataWithString(3, "bar", 0, !kFin, delegate.get()); + connection_.SendStreamDataWithString(3, "baz", 0, !kFin, NULL); + connection_.SendStreamDataWithString(3, "qux", 0, !kFin, NULL); + + // Now we receive ACK for packets 1, 3, and 4 and lose 2. + QuicAckFrame frame = InitAckFrame(4, 0); + NackPacket(2, &frame); + SequenceNumberSet lost_packets; + lost_packets.insert(2); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); + ProcessAckPacket(&frame); + + // Now we get an ACK for packet 2, which was previously nacked. + SequenceNumberSet no_lost_packets; + EXPECT_CALL(*delegate, OnAckNotification(1, _, 1, _, _)); + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(no_lost_packets)); + QuicAckFrame second_ack_frame = InitAckFrame(4, 0); ProcessAckPacket(&second_ack_frame); + + // Verify that the delegate is not notified again when the + // retransmit is acked. + EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(no_lost_packets)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + QuicAckFrame third_ack_frame = InitAckFrame(5, 0); + ProcessAckPacket(&third_ack_frame); } -// TODO(rjshade): Add a similar test that FEC recovery on peer (and resulting -// ACK) triggers notification on our end. -TEST_F(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) { +TEST_P(QuicConnectionTest, AckNotifierFECTriggerCallback) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_CALL(visitor_, OnCanWrite()).Times(1).WillOnce(Return(true)); // Create a delegate which we expect to be called. - MockAckNotifierDelegate delegate; - EXPECT_CALL(delegate, OnAckNotification()).Times(1);; + scoped_refptr<MockAckNotifierDelegate> delegate( + new MockAckNotifierDelegate); + EXPECT_CALL(*delegate, OnAckNotification(_, _, _, _, _)).Times(1); + + // Send some data, which will register the delegate to be notified. + connection_.SendStreamDataWithString(1, "foo", 0, !kFin, delegate.get()); + connection_.SendStreamDataWithString(2, "bar", 0, !kFin, NULL); + + // Process an ACK from the server with a revived packet, which should trigger + // the callback. + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + QuicAckFrame frame = InitAckFrame(2, 0); + NackPacket(1, &frame); + frame.received_info.revived_packets.insert(1); + ProcessAckPacket(&frame); + // If the ack is processed again, the notifier should not be called again. + ProcessAckPacket(&frame); +} + +TEST_P(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(visitor_, OnCanWrite()); + + // Create a delegate which we expect to be called. + scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate); + EXPECT_CALL(*delegate, OnAckNotification(_, _, _, _, _)).Times(1); // Expect ACKs for 1 packet. - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(1); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); // Send one packet, and register to be notified on ACK. - connection_.SendStreamDataWithString(1, "foo", 0, !kFin, &delegate); + connection_.SendStreamDataWithString(1, "foo", 0, !kFin, delegate.get()); // Ack packet gets dropped, but we receive an FEC packet that covers it. // Should recover the Ack packet and trigger the notification callback. QuicFrames frames; - QuicAckFrame ack_frame(1, QuicTime::Zero(), 0); - ack_frame.received_info.entropy_hash = - QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); + QuicAckFrame ack_frame = InitAckFrame(1, 0); frames.push_back(QuicFrame(&ack_frame)); // Dummy stream frame to satisfy expectations set elsewhere. frames.push_back(QuicFrame(&frame1_)); QuicPacketHeader ack_header; - ack_header.public_header.guid = guid_; + ack_header.public_header.connection_id = connection_id_; ack_header.public_header.reset_flag = false; ack_header.public_header.version_flag = false; ack_header.entropy_flag = !kEntropyFlag; @@ -3332,7 +3931,7 @@ TEST_F(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) { ack_header.fec_group = 1; QuicPacket* packet = - framer_.BuildUnsizedDataPacket(ack_header, frames).packet; + BuildUnsizedDataPacket(&framer_, ack_header, frames).packet; // Take the packet which contains the ACK frame, and construct and deliver an // FEC packet which allows the ACK packet to be recovered. @@ -3340,14 +3939,15 @@ TEST_F(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) { } class MockQuicConnectionDebugVisitor - : public QuicConnectionDebugVisitorInterface { + : public QuicConnectionDebugVisitor { public: MOCK_METHOD1(OnFrameAddedToPacket, void(const QuicFrame&)); - MOCK_METHOD4(OnPacketSent, + MOCK_METHOD5(OnPacketSent, void(QuicPacketSequenceNumber, EncryptionLevel, + TransmissionType, const QuicEncryptedPacket&, WriteResult)); @@ -3375,6 +3975,9 @@ class MockQuicConnectionDebugVisitor MOCK_METHOD1(OnCongestionFeedbackFrame, void(const QuicCongestionFeedbackFrame&)); + MOCK_METHOD1(OnStopWaitingFrame, + void(const QuicStopWaitingFrame&)); + MOCK_METHOD1(OnRstStreamFrame, void(const QuicRstStreamFrame&)); @@ -3391,7 +3994,7 @@ class MockQuicConnectionDebugVisitor void(const QuicPacketHeader&, StringPiece payload)); }; -TEST_F(QuicConnectionTest, OnPacketHeaderDebugVisitor) { +TEST_P(QuicConnectionTest, OnPacketHeaderDebugVisitor) { QuicPacketHeader header; scoped_ptr<MockQuicConnectionDebugVisitor> @@ -3401,17 +4004,40 @@ TEST_F(QuicConnectionTest, OnPacketHeaderDebugVisitor) { connection_.OnPacketHeader(header); } -TEST_F(QuicConnectionTest, Pacing) { +TEST_P(QuicConnectionTest, Pacing) { ValueRestore<bool> old_flag(&FLAGS_enable_quic_pacing, true); - TestConnection server(guid_, IPEndPoint(), helper_.get(), writer_.get(), - true); - TestConnection client(guid_, IPEndPoint(), helper_.get(), writer_.get(), - false); + TestConnection server(connection_id_, IPEndPoint(), helper_.get(), + writer_.get(), true, version()); + TestConnection client(connection_id_, IPEndPoint(), helper_.get(), + writer_.get(), false, version()); EXPECT_TRUE(client.sent_packet_manager().using_pacing()); EXPECT_FALSE(server.sent_packet_manager().using_pacing()); } +TEST_P(QuicConnectionTest, ControlFramesInstigateAcks) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + // Send a WINDOW_UPDATE frame. + QuicWindowUpdateFrame window_update; + window_update.stream_id = 3; + window_update.byte_offset = 1234; + EXPECT_CALL(visitor_, OnWindowUpdateFrames(_)); + ProcessFramePacket(QuicFrame(&window_update)); + + // Ensure that this has caused the ACK alarm to be set. + QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_); + EXPECT_TRUE(ack_alarm->IsSet()); + + // Cancel alarm, and try again with BLOCKED frame. + ack_alarm->Cancel(); + QuicBlockedFrame blocked; + blocked.stream_id = 3; + EXPECT_CALL(visitor_, OnBlockedFrames(_)); + ProcessFramePacket(QuicFrame(&blocked)); + EXPECT_TRUE(ack_alarm->IsSet()); +} + } // namespace } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_crypto_client_stream.cc b/chromium/net/quic/quic_crypto_client_stream.cc index 3e5f84059fc..3993637e104 100644 --- a/chromium/net/quic/quic_crypto_client_stream.cc +++ b/chromium/net/quic/quic_crypto_client_stream.cc @@ -4,42 +4,23 @@ #include "net/quic/quic_crypto_client_stream.h" -#include "net/base/completion_callback.h" -#include "net/base/net_errors.h" +#include "net/quic/crypto/channel_id.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/crypto_utils.h" #include "net/quic/crypto/null_encrypter.h" #include "net/quic/crypto/proof_verifier.h" -#include "net/quic/crypto/proof_verifier_chromium.h" +#include "net/quic/quic_client_session_base.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_session.h" -#include "net/ssl/ssl_connection_status_flags.h" -#include "net/ssl/ssl_info.h" namespace net { -namespace { - -// Copies CertVerifyResult from |verify_details| to |cert_verify_result|. -void CopyCertVerifyResult( - const ProofVerifyDetails* verify_details, - scoped_ptr<CertVerifyResult>* cert_verify_result) { - const CertVerifyResult* cert_verify_result_other = - &(reinterpret_cast<const ProofVerifyDetailsChromium*>( - verify_details))->cert_verify_result; - CertVerifyResult* result_copy = new CertVerifyResult; - result_copy->CopyFrom(*cert_verify_result_other); - cert_verify_result->reset(result_copy); -} - -} // namespace - QuicCryptoClientStream::ProofVerifierCallbackImpl::ProofVerifierCallbackImpl( QuicCryptoClientStream* stream) : stream_(stream) {} QuicCryptoClientStream::ProofVerifierCallbackImpl:: - ~ProofVerifierCallbackImpl() {} +~ProofVerifierCallbackImpl() {} void QuicCryptoClientStream::ProofVerifierCallbackImpl::Run( bool ok, @@ -63,18 +44,19 @@ void QuicCryptoClientStream::ProofVerifierCallbackImpl::Cancel() { stream_ = NULL; } - QuicCryptoClientStream::QuicCryptoClientStream( - const string& server_hostname, - QuicSession* session, + const QuicServerId& server_id, + QuicClientSessionBase* session, + ProofVerifyContext* verify_context, QuicCryptoClientConfig* crypto_config) : QuicCryptoStream(session), next_state_(STATE_IDLE), num_client_hellos_(0), crypto_config_(crypto_config), - server_hostname_(server_hostname), + server_id_(server_id), generation_counter_(0), - proof_verify_callback_(NULL) { + proof_verify_callback_(NULL), + verify_context_(verify_context) { } QuicCryptoClientStream::~QuicCryptoClientStream() { @@ -91,7 +73,7 @@ void QuicCryptoClientStream::OnHandshakeMessage( } bool QuicCryptoClientStream::CryptoConnect() { - next_state_ = STATE_SEND_CHLO; + next_state_ = STATE_INITIALIZE; DoHandshakeLoop(NULL); return true; } @@ -100,42 +82,6 @@ int QuicCryptoClientStream::num_sent_client_hellos() const { return num_client_hellos_; } -// TODO(rtenneti): Add unittests for GetSSLInfo which exercise the various ways -// we learn about SSL info (sync vs async vs cached). -bool QuicCryptoClientStream::GetSSLInfo(SSLInfo* ssl_info) { - ssl_info->Reset(); - if (!cert_verify_result_) { - return false; - } - - ssl_info->cert_status = cert_verify_result_->cert_status; - ssl_info->cert = cert_verify_result_->verified_cert; - - // TODO(rtenneti): Figure out what to set for the following. - // Temporarily hard coded cipher_suite as 0xc031 to represent - // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (from - // net/ssl/ssl_cipher_suite_names.cc) and encryption as 256. - int cipher_suite = 0xc02f; - int ssl_connection_status = 0; - ssl_connection_status |= - (cipher_suite & SSL_CONNECTION_CIPHERSUITE_MASK) << - SSL_CONNECTION_CIPHERSUITE_SHIFT; - ssl_connection_status |= - (SSL_CONNECTION_VERSION_TLS1_2 & SSL_CONNECTION_VERSION_MASK) << - SSL_CONNECTION_VERSION_SHIFT; - - ssl_info->public_key_hashes = cert_verify_result_->public_key_hashes; - ssl_info->is_issued_by_known_root = - cert_verify_result_->is_issued_by_known_root; - - ssl_info->connection_status = ssl_connection_status; - ssl_info->client_cert_sent = false; - ssl_info->channel_id_sent = false; - ssl_info->security_bits = 256; - ssl_info->handshake_type = SSLInfo::HANDSHAKE_FULL; - return true; -} - // kMaxClientHellos is the maximum number of times that we'll send a client // hello. The value 3 accounts for: // * One failure due to an incorrect or missing source-address token. @@ -149,7 +95,7 @@ void QuicCryptoClientStream::DoHandshakeLoop( QuicErrorCode error; string error_details; QuicCryptoClientConfig::CachedState* cached = - crypto_config_->LookupOrCreate(server_hostname_); + crypto_config_->LookupOrCreate(server_id_); if (in != NULL) { DVLOG(1) << "Client: Received " << in->DebugString(); @@ -159,6 +105,17 @@ void QuicCryptoClientStream::DoHandshakeLoop( const State state = next_state_; next_state_ = STATE_IDLE; switch (state) { + case STATE_INITIALIZE: { + if (!cached->IsEmpty() && !cached->signature().empty() && + server_id_.is_https()) { + DCHECK(crypto_config_->proof_verifier()); + // If the cached state needs to be verified, do it now. + next_state_ = STATE_VERIFY_PROOF; + } else { + next_state_ = STATE_SEND_CHLO; + } + break; + } case STATE_SEND_CHLO: { // Send the client hello in plaintext. session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_NONE); @@ -170,13 +127,13 @@ void QuicCryptoClientStream::DoHandshakeLoop( if (!cached->IsComplete(session()->connection()->clock()->WallNow())) { crypto_config_->FillInchoateClientHello( - server_hostname_, + server_id_, session()->connection()->supported_versions().front(), cached, &crypto_negotiated_params_, &out); // Pad the inchoate client hello to fill up a packet. const size_t kFramingOverhead = 50; // A rough estimate. const size_t max_packet_size = - session()->connection()->options()->max_packet_length; + session()->connection()->max_packet_length(); if (max_packet_size <= kFramingOverhead) { DLOG(DFATAL) << "max_packet_length (" << max_packet_size << ") has no room for framing overhead."; @@ -195,13 +152,43 @@ void QuicCryptoClientStream::DoHandshakeLoop( return; } session()->config()->ToHandshakeMessage(&out); + + scoped_ptr<ChannelIDKey> channel_id_key; + bool do_channel_id = false; + if (crypto_config_->channel_id_source()) { + const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); + DCHECK(scfg); + const QuicTag* their_proof_demands; + size_t num_their_proof_demands; + if (scfg->GetTaglist(kPDMD, &their_proof_demands, + &num_their_proof_demands) == QUIC_NO_ERROR) { + for (size_t i = 0; i < num_their_proof_demands; i++) { + if (their_proof_demands[i] == kCHID) { + do_channel_id = true; + break; + } + } + } + } + if (do_channel_id) { + QuicAsyncStatus status = + crypto_config_->channel_id_source()->GetChannelIDKey( + server_id_.host(), &channel_id_key, NULL); + if (status != QUIC_SUCCESS) { + CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE, + "Channel ID lookup failed"); + return; + } + } + error = crypto_config_->FillClientHello( - server_hostname_, - session()->connection()->guid(), + server_id_, + session()->connection()->connection_id(), session()->connection()->supported_versions().front(), cached, session()->connection()->clock()->WallNow(), session()->connection()->random_generator(), + channel_id_key.get(), &crypto_negotiated_params_, &out, &error_details); @@ -213,10 +200,8 @@ void QuicCryptoClientStream::DoHandshakeLoop( return; } if (cached->proof_verify_details()) { - CopyCertVerifyResult(cached->proof_verify_details(), - &cert_verify_result_); - } else { - cert_verify_result_.reset(); + client_session()->OnProofVerifyDetailsAvailable( + *cached->proof_verify_details()); } next_state_ = STATE_RECV_SHLO; DVLOG(1) << "Client: Sending " << out.DebugString(); @@ -224,6 +209,7 @@ void QuicCryptoClientStream::DoHandshakeLoop( // Be prepared to decrypt with the new server write key. session()->connection()->SetAlternativeDecrypter( crypto_negotiated_params_.initial_crypters.decrypter.release(), + ENCRYPTION_INITIAL, true /* latch once used */); // Send subsequent packets under encryption on the assumption that the // server will accept the handshake. @@ -260,10 +246,9 @@ void QuicCryptoClientStream::DoHandshakeLoop( return; } if (!cached->proof_valid()) { - ProofVerifier* verifier = crypto_config_->proof_verifier(); - if (!verifier) { - // If no verifier is set then we don't check the certificates. - cached->SetProofValid(); + if (!server_id_.is_https()) { + // We don't check the certificates for insecure QUIC connections. + SetCachedProofValid(cached); } else if (!cached->signature().empty()) { next_state_ = STATE_VERIFY_PROOF; break; @@ -282,23 +267,26 @@ void QuicCryptoClientStream::DoHandshakeLoop( verify_ok_ = false; - ProofVerifier::Status status = verifier->VerifyProof( - server_hostname_, + QuicAsyncStatus status = verifier->VerifyProof( + server_id_.host(), cached->server_config(), cached->certs(), cached->signature(), + verify_context_.get(), &verify_error_details_, &verify_details_, proof_verify_callback); switch (status) { - case ProofVerifier::PENDING: + case QUIC_PENDING: proof_verify_callback_ = proof_verify_callback; DVLOG(1) << "Doing VerifyProof"; return; - case ProofVerifier::FAILURE: + case QUIC_FAILURE: + delete proof_verify_callback; break; - case ProofVerifier::SUCCESS: + case QUIC_SUCCESS: + delete proof_verify_callback; verify_ok_ = true; break; } @@ -306,7 +294,7 @@ void QuicCryptoClientStream::DoHandshakeLoop( } case STATE_VERIFY_PROOF_COMPLETE: if (!verify_ok_) { - CopyCertVerifyResult(verify_details_.get(), &cert_verify_result_); + client_session()->OnProofVerifyDetailsAvailable(*verify_details_); CloseConnectionWithDetails( QUIC_PROOF_INVALID, "Proof invalid: " + verify_error_details_); return; @@ -316,7 +304,7 @@ void QuicCryptoClientStream::DoHandshakeLoop( if (generation_counter_ != cached->generation_counter()) { next_state_ = STATE_VERIFY_PROOF; } else { - cached->SetProofValid(); + SetCachedProofValid(cached); cached->SetProofVerifyDetails(verify_details_.release()); next_state_ = STATE_SEND_CHLO; } @@ -352,7 +340,7 @@ void QuicCryptoClientStream::DoHandshakeLoop( return; } error = crypto_config_->ProcessServerHello( - *in, session()->connection()->guid(), + *in, session()->connection()->connection_id(), session()->connection()->server_supported_versions(), cached, &crypto_negotiated_params_, &error_details); @@ -361,7 +349,8 @@ void QuicCryptoClientStream::DoHandshakeLoop( error, "Server hello invalid: " + error_details); return; } - error = session()->config()->ProcessServerHello(*in, &error_details); + error = + session()->config()->ProcessPeerHello(*in, SERVER, &error_details); if (error != QUIC_NO_ERROR) { CloseConnectionWithDetails( error, "Server hello invalid: " + error_details); @@ -376,7 +365,8 @@ void QuicCryptoClientStream::DoHandshakeLoop( // with the FORWARD_SECURE key until it receives a FORWARD_SECURE // packet from the client. session()->connection()->SetAlternativeDecrypter( - crypters->decrypter.release(), false /* don't latch */); + crypters->decrypter.release(), ENCRYPTION_FORWARD_SECURE, + false /* don't latch */); session()->connection()->SetEncrypter( ENCRYPTION_FORWARD_SECURE, crypters->encrypter.release()); session()->connection()->SetDefaultEncryptionLevel( @@ -394,4 +384,14 @@ void QuicCryptoClientStream::DoHandshakeLoop( } } +void QuicCryptoClientStream::SetCachedProofValid( + QuicCryptoClientConfig::CachedState* cached) { + cached->SetProofValid(); + client_session()->OnProofValid(*cached); +} + +QuicClientSessionBase* QuicCryptoClientStream::client_session() { + return reinterpret_cast<QuicClientSessionBase*>(session()); +} + } // namespace net diff --git a/chromium/net/quic/quic_crypto_client_stream.h b/chromium/net/quic/quic_crypto_client_stream.h index ca2fea74d75..5bb50ef1506 100644 --- a/chromium/net/quic/quic_crypto_client_stream.h +++ b/chromium/net/quic/quic_crypto_client_stream.h @@ -7,18 +7,15 @@ #include <string> -#include "net/cert/cert_verify_result.h" -#include "net/cert/x509_certificate.h" #include "net/quic/crypto/proof_verifier.h" #include "net/quic/crypto/quic_crypto_client_config.h" #include "net/quic/quic_config.h" #include "net/quic/quic_crypto_stream.h" +#include "net/quic/quic_server_id.h" namespace net { -class ProofVerifyDetails; -class QuicSession; -class SSLInfo; +class QuicClientSessionBase; namespace test { class CryptoTestUtils; @@ -26,8 +23,9 @@ class CryptoTestUtils; class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { public: - QuicCryptoClientStream(const string& server_hostname, - QuicSession* session, + QuicCryptoClientStream(const QuicServerId& server_id, + QuicClientSessionBase* session, + ProofVerifyContext* verify_context, QuicCryptoClientConfig* crypto_config); virtual ~QuicCryptoClientStream(); @@ -45,9 +43,6 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { // than the number of round-trips needed for the handshake. int num_sent_client_hellos() const; - // Gets the SSL connection information. - bool GetSSLInfo(SSLInfo* ssl_info); - private: // ProofVerifierCallbackImpl is passed as the callback method to VerifyProof. // The ProofVerifier calls this class with the result of proof verification @@ -75,6 +70,7 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { enum State { STATE_IDLE, + STATE_INITIALIZE, STATE_SEND_CHLO, STATE_RECV_REJ, STATE_VERIFY_PROOF, @@ -83,9 +79,15 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { }; // DoHandshakeLoop performs a step of the handshake state machine. Note that - // |in| may be NULL if the call did not result from a received message + // |in| may be NULL if the call did not result from a received message. void DoHandshakeLoop(const CryptoHandshakeMessage* in); + // Called to set the proof of |cached| valid. Also invokes the session's + // OnProofValid() method. + void SetCachedProofValid(QuicCryptoClientConfig::CachedState* cached); + + QuicClientSessionBase* client_session(); + State next_state_; // num_client_hellos_ contains the number of client hello messages that this // connection has sent. @@ -95,8 +97,8 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { // Client's connection nonce (4-byte timestamp + 28 random bytes) std::string nonce_; - // Server's hostname - std::string server_hostname_; + // Server's (hostname, port, is_https, privacy_mode) tuple. + const QuicServerId server_id_; // Generation counter from QuicCryptoClientConfig's CachedState. uint64 generation_counter_; @@ -111,9 +113,7 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { bool verify_ok_; string verify_error_details_; scoped_ptr<ProofVerifyDetails> verify_details_; - - // The result of certificate verification. - scoped_ptr<CertVerifyResult> cert_verify_result_; + scoped_ptr<ProofVerifyContext> verify_context_; DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientStream); }; diff --git a/chromium/net/quic/quic_crypto_client_stream_factory.h b/chromium/net/quic/quic_crypto_client_stream_factory.h index 4fa5f573ea0..293fa8217b3 100644 --- a/chromium/net/quic/quic_crypto_client_stream_factory.h +++ b/chromium/net/quic/quic_crypto_client_stream_factory.h @@ -11,8 +11,9 @@ namespace net { +class QuicClientSession; class QuicCryptoClientStream; -class QuicSession; +class QuicServerId; // An interface used to instantiate QuicCryptoClientStream objects. Used to // facilitate testing code with mock implementations. @@ -21,8 +22,8 @@ class NET_EXPORT QuicCryptoClientStreamFactory { virtual ~QuicCryptoClientStreamFactory() {} virtual QuicCryptoClientStream* CreateQuicCryptoClientStream( - const string& server_hostname, - QuicSession* session, + const QuicServerId& server_id, + QuicClientSession* session, QuicCryptoClientConfig* crypto_config) = 0; }; diff --git a/chromium/net/quic/quic_crypto_client_stream_test.cc b/chromium/net/quic/quic_crypto_client_stream_test.cc index 8154d177988..3515d32aac7 100644 --- a/chromium/net/quic/quic_crypto_client_stream_test.cc +++ b/chromium/net/quic/quic_crypto_client_stream_test.cc @@ -8,7 +8,9 @@ #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_protocol.h" +#include "net/quic/quic_server_id.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/quic/test_tools/simple_quic_framer.h" @@ -20,15 +22,18 @@ namespace test { namespace { const char kServerHostname[] = "example.com"; +const uint16 kServerPort = 80; class QuicCryptoClientStreamTest : public ::testing::Test { public: QuicCryptoClientStreamTest() : connection_(new PacketSavingConnection(false)), - session_(new TestSession(connection_, DefaultQuicConfig())), - stream_(new QuicCryptoClientStream(kServerHostname, session_.get(), - &crypto_config_)) { + session_(new TestClientSession(connection_, DefaultQuicConfig())), + server_id_(kServerHostname, kServerPort, false, PRIVACY_MODE_DISABLED), + stream_(new QuicCryptoClientStream( + server_id_, session_.get(), NULL, &crypto_config_)) { session_->SetCryptoStream(stream_.get()); + session_->config()->SetDefaults(); crypto_config_.SetDefaults(); } @@ -43,7 +48,8 @@ class QuicCryptoClientStreamTest : public ::testing::Test { } PacketSavingConnection* connection_; - scoped_ptr<TestSession> session_; + scoped_ptr<TestClientSession> session_; + QuicServerId server_id_; scoped_ptr<QuicCryptoClientStream> stream_; CryptoHandshakeMessage message_; scoped_ptr<QuicData> message_data_; @@ -87,7 +93,7 @@ TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) { const QuicConfig* config = session_->config(); EXPECT_EQ(FLAGS_enable_quic_pacing ? kPACE : kQBIC, - config->congestion_control()); + config->congestion_feedback()); EXPECT_EQ(kDefaultTimeoutSecs, config->idle_connection_state_lifetime().ToSeconds()); EXPECT_EQ(kDefaultMaxStreamsPerConnection, @@ -96,12 +102,13 @@ TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) { const QuicCryptoNegotiatedParameters& crypto_params( stream_->crypto_negotiated_params()); - EXPECT_EQ(kAESG, crypto_params.aead); - EXPECT_EQ(kC255, crypto_params.key_exchange); + EXPECT_EQ(crypto_config_.aead[0], crypto_params.aead); + EXPECT_EQ(crypto_config_.kexs[0], crypto_params.key_exchange); } TEST_F(QuicCryptoClientStreamTest, InvalidHostname) { - stream_.reset(new QuicCryptoClientStream("invalid", session_.get(), + QuicServerId server_id("invalid", 80, false, PRIVACY_MODE_DISABLED); + stream_.reset(new QuicCryptoClientStream(server_id, session_.get(), NULL, &crypto_config_)); session_->SetCryptoStream(stream_.get()); @@ -115,8 +122,8 @@ TEST_F(QuicCryptoClientStreamTest, ExpiredServerConfig) { CompleteCryptoHandshake(); connection_ = new PacketSavingConnection(true); - session_.reset(new TestSession(connection_, DefaultQuicConfig())); - stream_.reset(new QuicCryptoClientStream(kServerHostname, session_.get(), + session_.reset(new TestClientSession(connection_, DefaultQuicConfig())); + stream_.reset(new QuicCryptoClientStream(server_id_, session_.get(), NULL, &crypto_config_)); session_->SetCryptoStream(stream_.get()); diff --git a/chromium/net/quic/quic_crypto_server_stream.cc b/chromium/net/quic/quic_crypto_server_stream.cc index c0ae1f19b92..7bd2c034d93 100644 --- a/chromium/net/quic/quic_crypto_server_stream.cc +++ b/chromium/net/quic/quic_crypto_server_stream.cc @@ -20,11 +20,16 @@ QuicCryptoServerStream::QuicCryptoServerStream( QuicSession* session) : QuicCryptoStream(session), crypto_config_(crypto_config), - validate_client_hello_cb_(NULL) { + validate_client_hello_cb_(NULL), + num_handshake_messages_(0) { } QuicCryptoServerStream::~QuicCryptoServerStream() { - // Detach from the validation callback. + CancelOutstandingCallbacks(); +} + +void QuicCryptoServerStream::CancelOutstandingCallbacks() { + // Detach from the validation callback. Calling this multiple times is safe. if (validate_client_hello_cb_ != NULL) { validate_client_hello_cb_->Cancel(); } @@ -33,6 +38,7 @@ QuicCryptoServerStream::~QuicCryptoServerStream() { void QuicCryptoServerStream::OnHandshakeMessage( const CryptoHandshakeMessage& message) { QuicCryptoStream::OnHandshakeMessage(message); + ++num_handshake_messages_; // Do not process handshake messages after the handshake is confirmed. if (handshake_confirmed_) { @@ -85,7 +91,8 @@ void QuicCryptoServerStream::FinishProcessingHandshakeMessage( // If we are returning a SHLO then we accepted the handshake. QuicConfig* config = session()->config(); - error = config->ProcessClientHello(message, &error_details); + OverrideQuicConfigDefaults(config); + error = config->ProcessPeerHello(message, CLIENT, &error_details); if (error != QUIC_NO_ERROR) { CloseConnectionWithDetails(error, error_details); return; @@ -107,7 +114,8 @@ void QuicCryptoServerStream::FinishProcessingHandshakeMessage( // Set the decrypter immediately so that we no longer accept unencrypted // packets. session()->connection()->SetDecrypter( - crypto_negotiated_params_.initial_crypters.decrypter.release()); + crypto_negotiated_params_.initial_crypters.decrypter.release(), + ENCRYPTION_INITIAL); SendHandshakeMessage(reply); session()->connection()->SetEncrypter( @@ -117,7 +125,7 @@ void QuicCryptoServerStream::FinishProcessingHandshakeMessage( ENCRYPTION_FORWARD_SECURE); session()->connection()->SetAlternativeDecrypter( crypto_negotiated_params_.forward_secure_crypters.decrypter.release(), - false /* don't latch */); + ENCRYPTION_FORWARD_SECURE, false /* don't latch */); encryption_established_ = true; handshake_confirmed_ = true; @@ -161,7 +169,7 @@ QuicErrorCode QuicCryptoServerStream::ProcessClientHello( string* error_details) { return crypto_config_.ProcessClientHello( result, - session()->connection()->guid(), + session()->connection()->connection_id(), session()->connection()->peer_address(), session()->connection()->version(), session()->connection()->supported_versions(), @@ -170,6 +178,9 @@ QuicErrorCode QuicCryptoServerStream::ProcessClientHello( &crypto_negotiated_params_, reply, error_details); } +void QuicCryptoServerStream::OverrideQuicConfigDefaults(QuicConfig* config) { +} + QuicCryptoServerStream::ValidateCallback::ValidateCallback( QuicCryptoServerStream* parent) : parent_(parent) { } diff --git a/chromium/net/quic/quic_crypto_server_stream.h b/chromium/net/quic/quic_crypto_server_stream.h index 43924d54d93..ee4013120e4 100644 --- a/chromium/net/quic/quic_crypto_server_stream.h +++ b/chromium/net/quic/quic_crypto_server_stream.h @@ -29,6 +29,10 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { explicit QuicCryptoServerStream(QuicSession* session); virtual ~QuicCryptoServerStream(); + // Cancel any outstanding callbacks, such as asynchronous validation of client + // hello. + void CancelOutstandingCallbacks(); + // CryptoFramerVisitorInterface implementation virtual void OnHandshakeMessage( const CryptoHandshakeMessage& message) OVERRIDE; @@ -38,6 +42,8 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { // presented a ChannelID. Otherwise it returns false. bool GetBase64SHA256ClientChannelID(std::string* output) const; + uint8 num_handshake_messages() const { return num_handshake_messages_; } + protected: virtual QuicErrorCode ProcessClientHello( const CryptoHandshakeMessage& message, @@ -45,6 +51,10 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { CryptoHandshakeMessage* reply, std::string* error_details); + // Hook that allows the server to set QuicConfig defaults just + // before going through the parameter negotiation step. + virtual void OverrideQuicConfigDefaults(QuicConfig* config); + private: friend class test::CryptoTestUtils; @@ -79,6 +89,10 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { // FinishProcessingHandshakeMessage for processing. NULL if no // handshake message is being validated. ValidateCallback* validate_client_hello_cb_; + + uint8 num_handshake_messages_; + + DISALLOW_COPY_AND_ASSIGN(QuicCryptoServerStream); }; } // namespace net diff --git a/chromium/net/quic/quic_crypto_server_stream_test.cc b/chromium/net/quic/quic_crypto_server_stream_test.cc index 9897e22f738..800c245a0c4 100644 --- a/chromium/net/quic/quic_crypto_server_stream_test.cc +++ b/chromium/net/quic/quic_crypto_server_stream_test.cc @@ -49,7 +49,10 @@ class QuicCryptoServerConfigPeer { namespace { -class QuicCryptoServerStreamTest : public testing::TestWithParam<bool> { +const char kServerHostname[] = "test.example.com"; +const uint16 kServerPort = 80; + +class QuicCryptoServerStreamTest : public ::testing::TestWithParam<bool> { public: QuicCryptoServerStreamTest() : connection_(new PacketSavingConnection(true)), @@ -102,7 +105,7 @@ class QuicCryptoServerStreamTest : public testing::TestWithParam<bool> { protected: PacketSavingConnection* connection_; - TestSession session_; + TestClientSession session_; QuicConfig config_; QuicCryptoServerConfig crypto_config_; QuicCryptoServerStream stream_; @@ -137,13 +140,15 @@ TEST_P(QuicCryptoServerStreamTest, ZeroRTT) { QuicConfig client_config; client_config.SetDefaults(); - scoped_ptr<TestSession> client_session( - new TestSession(client_conn, client_config)); + scoped_ptr<TestClientSession> client_session( + new TestClientSession(client_conn, client_config)); QuicCryptoClientConfig client_crypto_config; client_crypto_config.SetDefaults(); + QuicServerId server_id(kServerHostname, kServerPort, false, + PRIVACY_MODE_DISABLED); scoped_ptr<QuicCryptoClientStream> client(new QuicCryptoClientStream( - "test.example.com", client_session.get(), &client_crypto_config)); + server_id, client_session.get(), NULL, &client_crypto_config)); client_session->SetCryptoStream(client.get()); // Do a first handshake in order to prime the client config with the server's @@ -173,10 +178,10 @@ TEST_P(QuicCryptoServerStreamTest, ZeroRTT) { // This causes the client's nonce to be different and thus stops the // strike-register from rejecting the repeated nonce. reinterpret_cast<MockRandom*>(client_conn->random_generator())->ChangeValue(); - client_session.reset(new TestSession(client_conn, client_config)); + client_session.reset(new TestClientSession(client_conn, client_config)); server_session.reset(new TestSession(server_conn, config_)); client.reset(new QuicCryptoClientStream( - "test.example.com", client_session.get(), &client_crypto_config)); + server_id, client_session.get(), NULL, &client_crypto_config)); client_session->SetCryptoStream(client.get()); server.reset(new QuicCryptoServerStream(crypto_config_, @@ -249,7 +254,6 @@ TEST_P(QuicCryptoServerStreamTest, WithoutCertificates) { TEST_P(QuicCryptoServerStreamTest, ChannelID) { client_options_.channel_id_enabled = true; - // TODO(rtenneti): Enable testing of ProofVerifier. // CompleteCryptoHandshake verifies // stream_.crypto_negotiated_params().channel_id is correct. EXPECT_EQ(2, CompleteCryptoHandshake()); diff --git a/chromium/net/quic/quic_crypto_stream.cc b/chromium/net/quic/quic_crypto_stream.cc index d79fa735786..0e447127af5 100644 --- a/chromium/net/quic/quic_crypto_stream.cc +++ b/chromium/net/quic/quic_crypto_stream.cc @@ -10,6 +10,7 @@ #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/quic_connection.h" #include "net/quic/quic_session.h" +#include "net/quic/quic_utils.h" using std::string; using base::StringPiece; @@ -21,6 +22,7 @@ QuicCryptoStream::QuicCryptoStream(QuicSession* session) encryption_established_(false), handshake_confirmed_(false) { crypto_framer_.set_visitor(this); + DisableFlowControl(); } void QuicCryptoStream::OnError(CryptoFramer* framer) { @@ -55,12 +57,8 @@ void QuicCryptoStream::SendHandshakeMessage( const CryptoHandshakeMessage& message) { session()->OnCryptoHandshakeMessageSent(message); const QuicData& data = message.GetSerialized(); - // To make reasoning about crypto frames easier, we don't combine them with - // any other frames in a single packet. - session()->connection()->Flush(); // TODO(wtc): check the return value. - WriteOrBufferData(string(data.data(), data.length()), false); - session()->connection()->Flush(); + WriteOrBufferData(string(data.data(), data.length()), false, NULL); } const QuicCryptoNegotiatedParameters& diff --git a/chromium/net/quic/quic_crypto_stream.h b/chromium/net/quic/quic_crypto_stream.h index a082d50dbf2..2c61a18fe78 100644 --- a/chromium/net/quic/quic_crypto_stream.h +++ b/chromium/net/quic/quic_crypto_stream.h @@ -5,6 +5,7 @@ #ifndef NET_QUIC_QUIC_CRYPTO_STREAM_H_ #define NET_QUIC_QUIC_CRYPTO_STREAM_H_ +#include "base/basictypes.h" #include "net/quic/crypto/crypto_framer.h" #include "net/quic/crypto/crypto_utils.h" #include "net/quic/quic_config.h" @@ -45,8 +46,8 @@ class NET_EXPORT_PRIVATE QuicCryptoStream // TODO(wtc): return a success/failure status. void SendHandshakeMessage(const CryptoHandshakeMessage& message); - bool encryption_established() { return encryption_established_; } - bool handshake_confirmed() { return handshake_confirmed_; } + bool encryption_established() const { return encryption_established_; } + bool handshake_confirmed() const { return handshake_confirmed_; } const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const; diff --git a/chromium/net/quic/quic_crypto_stream_test.cc b/chromium/net/quic/quic_crypto_stream_test.cc index c8dbab51453..2e4b34fe34a 100644 --- a/chromium/net/quic/quic_crypto_stream_test.cc +++ b/chromium/net/quic/quic_crypto_stream_test.cc @@ -10,6 +10,7 @@ #include "base/memory/scoped_ptr.h" #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_flags.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/quic_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" @@ -101,6 +102,11 @@ TEST_F(QuicCryptoStreamTest, ProcessBadData) { EXPECT_EQ(0u, stream_.ProcessRawData(bad.data(), bad.length())); } +TEST_F(QuicCryptoStreamTest, NoFlowControl) { + ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true); + EXPECT_FALSE(stream_.flow_controller()->IsEnabled()); +} + } // namespace } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_data_reader.h b/chromium/net/quic/quic_data_reader.h index 9effa1cc896..1686de028cc 100644 --- a/chromium/net/quic/quic_data_reader.h +++ b/chromium/net/quic/quic_data_reader.h @@ -124,6 +124,8 @@ class NET_EXPORT_PRIVATE QuicDataReader { // The location of the next read from our data buffer. size_t pos_; + + DISALLOW_COPY_AND_ASSIGN(QuicDataReader); }; } // namespace net diff --git a/chromium/net/quic/quic_data_stream.cc b/chromium/net/quic/quic_data_stream.cc index 3c992a777b0..7963f4f41eb 100644 --- a/chromium/net/quic/quic_data_stream.cc +++ b/chromium/net/quic/quic_data_stream.cc @@ -6,8 +6,8 @@ #include "base/logging.h" #include "net/quic/quic_session.h" -#include "net/quic/quic_spdy_decompressor.h" -#include "net/spdy/write_blocked_list.h" +#include "net/quic/quic_utils.h" +#include "net/quic/quic_write_blocked_list.h" using base::StringPiece; using std::min; @@ -24,26 +24,6 @@ namespace { // priority in the middle. QuicPriority kDefaultPriority = 3; -// Appends bytes from data into partial_data_buffer. Once partial_data_buffer -// reaches 4 bytes, copies the data into 'result' and clears -// partial_data_buffer. -// Returns the number of bytes consumed. -uint32 StripUint32(const char* data, uint32 data_len, - string* partial_data_buffer, - uint32* result) { - DCHECK_GT(4u, partial_data_buffer->length()); - size_t missing_size = 4 - partial_data_buffer->length(); - if (data_len < missing_size) { - StringPiece(data, data_len).AppendToString(partial_data_buffer); - return data_len; - } - StringPiece(data, missing_size).AppendToString(partial_data_buffer); - DCHECK_EQ(4u, partial_data_buffer->length()); - memcpy(result, partial_data_buffer->data(), 4); - partial_data_buffer->clear(); - return missing_size; -} - } // namespace QuicDataStream::QuicDataStream(QuicStreamId id, @@ -52,15 +32,31 @@ QuicDataStream::QuicDataStream(QuicStreamId id, visitor_(NULL), headers_decompressed_(false), priority_(kDefaultPriority), - headers_id_(0), decompression_failed_(false), priority_parsed_(false) { DCHECK_NE(kCryptoStreamId, id); + // Don't receive any callbacks from the sequencer until headers + // are complete. + sequencer()->SetBlockedUntilFlush(); } QuicDataStream::~QuicDataStream() { } +size_t QuicDataStream::WriteHeaders( + const SpdyHeaderBlock& header_block, + bool fin, + QuicAckNotifier::DelegateInterface* ack_notifier_delegate) { + size_t bytes_written = session()->WriteHeaders( + id(), header_block, fin, ack_notifier_delegate); + if (fin) { + // TODO(rch): Add test to ensure fin_sent_ is set whenever a fin is sent. + set_fin_sent(true); + CloseWriteSide(); + } + return bytes_written; +} + size_t QuicDataStream::Readv(const struct iovec* iov, size_t iov_len) { if (FinishedReadingHeaders()) { // If the headers have been read, simply delegate to the sequencer's @@ -81,6 +77,9 @@ size_t QuicDataStream::Readv(const struct iovec* iov, size_t iov_len) { ++iov_index; } decompressed_headers_.erase(0, bytes_consumed); + if (FinishedReadingHeaders()) { + sequencer()->FlushBufferedFrames(); + } return bytes_consumed; } @@ -118,105 +117,17 @@ QuicPriority QuicDataStream::EffectivePriority() const { } uint32 QuicDataStream::ProcessRawData(const char* data, uint32 data_len) { - DCHECK_NE(0u, data_len); - - uint32 total_bytes_consumed = 0; - if (headers_id_ == 0u) { - total_bytes_consumed += StripPriorityAndHeaderId(data, data_len); - data += total_bytes_consumed; - data_len -= total_bytes_consumed; - if (data_len == 0 || total_bytes_consumed == 0) { - return total_bytes_consumed; - } - } - DCHECK_NE(0u, headers_id_); - - // Once the headers are finished, we simply pass the data through. - if (headers_decompressed_) { - // Some buffered header data remains. - if (!decompressed_headers_.empty()) { - ProcessHeaderData(); - } - if (decompressed_headers_.empty()) { - DVLOG(1) << "Delegating procesing to ProcessData"; - total_bytes_consumed += ProcessData(data, data_len); - } - return total_bytes_consumed; - } - - QuicHeaderId current_header_id = - session()->decompressor()->current_header_id(); - // Ensure that this header id looks sane. - if (headers_id_ < current_header_id || - headers_id_ > kMaxHeaderIdDelta + current_header_id) { - DVLOG(1) << ENDPOINT - << "Invalid headers for stream: " << id() - << " header_id: " << headers_id_ - << " current_header_id: " << current_header_id; - session()->connection()->SendConnectionClose(QUIC_INVALID_HEADER_ID); - return total_bytes_consumed; - } - - // If we are head-of-line blocked on decompression, then back up. - if (current_header_id != headers_id_) { - session()->MarkDecompressionBlocked(headers_id_, id()); - DVLOG(1) << ENDPOINT - << "Unable to decompress header data for stream: " << id() - << " header_id: " << headers_id_; - return total_bytes_consumed; - } - - // Decompressed data will be delivered to decompressed_headers_. - size_t bytes_consumed = session()->decompressor()->DecompressData( - StringPiece(data, data_len), this); - DCHECK_NE(0u, bytes_consumed); - if (bytes_consumed > data_len) { - DCHECK(false) << "DecompressData returned illegal value"; - OnDecompressionError(); - return total_bytes_consumed; - } - total_bytes_consumed += bytes_consumed; - data += bytes_consumed; - data_len -= bytes_consumed; - - if (decompression_failed_) { - // The session will have been closed in OnDecompressionError. - return total_bytes_consumed; - } - - // Headers are complete if the decompressor has moved on to the - // next stream. - headers_decompressed_ = - session()->decompressor()->current_header_id() != headers_id_; - if (!headers_decompressed_) { - DCHECK_EQ(0u, data_len); - } - - ProcessHeaderData(); - - if (!headers_decompressed_ || !decompressed_headers_.empty()) { - return total_bytes_consumed; - } - - // We have processed all of the decompressed data but we might - // have some more raw data to process. - if (data_len > 0) { - total_bytes_consumed += ProcessData(data, data_len); + if (!FinishedReadingHeaders()) { + LOG(DFATAL) << "ProcessRawData called before headers have been finished"; + return 0; } - - // The sequencer will push any additional buffered frames if this data - // has been completely consumed. - return total_bytes_consumed; + return ProcessData(data, data_len); } const IPEndPoint& QuicDataStream::GetPeerAddress() { return session()->peer_address(); } -QuicSpdyCompressor* QuicDataStream::compressor() { - return session()->compressor(); -} - bool QuicDataStream::GetSSLInfo(SSLInfo* ssl_info) { return session()->GetSSLInfo(ssl_info); } @@ -236,53 +147,27 @@ uint32 QuicDataStream::ProcessHeaderData() { return bytes_processed; } -void QuicDataStream::OnDecompressorAvailable() { - DCHECK_EQ(headers_id_, - session()->decompressor()->current_header_id()); - DCHECK(!headers_decompressed_); - DCHECK(!decompression_failed_); - DCHECK_EQ(0u, decompressed_headers_.length()); - - while (!headers_decompressed_) { - struct iovec iovec; - if (sequencer()->GetReadableRegions(&iovec, 1) == 0) { - return; - } +void QuicDataStream::OnStreamHeaders(StringPiece headers_data) { + headers_data.AppendToString(&decompressed_headers_); + ProcessHeaderData(); +} - size_t bytes_consumed = session()->decompressor()->DecompressData( - StringPiece(static_cast<char*>(iovec.iov_base), - iovec.iov_len), - this); - DCHECK_LE(bytes_consumed, iovec.iov_len); - if (decompression_failed_) { - return; - } - sequencer()->MarkConsumed(bytes_consumed); +void QuicDataStream::OnStreamHeadersPriority(QuicPriority priority) { + DCHECK(session()->connection()->is_server()); + set_priority(priority); +} - headers_decompressed_ = - session()->decompressor()->current_header_id() != headers_id_; +void QuicDataStream::OnStreamHeadersComplete(bool fin, size_t frame_len) { + headers_decompressed_ = true; + if (fin) { + sequencer()->OnStreamFrame(QuicStreamFrame(id(), fin, 0, IOVector())); } - - // Either the headers are complete, or the all data as been consumed. - ProcessHeaderData(); // Unprocessed headers remain in decompressed_headers_. - if (IsDoneReading()) { - OnFinRead(); - } else if (FinishedReadingHeaders()) { + ProcessHeaderData(); + if (FinishedReadingHeaders()) { sequencer()->FlushBufferedFrames(); } } -bool QuicDataStream::OnDecompressedData(StringPiece data) { - data.AppendToString(&decompressed_headers_); - return true; -} - -void QuicDataStream::OnDecompressionError() { - DCHECK(!decompression_failed_); - decompression_failed_ = true; - session()->connection()->SendConnectionClose(QUIC_DECOMPRESSION_FAILURE); -} - void QuicDataStream::OnClose() { ReliableQuicStream::OnClose(); @@ -295,37 +180,6 @@ void QuicDataStream::OnClose() { } } -uint32 QuicDataStream::StripPriorityAndHeaderId( - const char* data, uint32 data_len) { - uint32 total_bytes_parsed = 0; - - if (!priority_parsed_ && session()->connection()->is_server()) { - QuicPriority temporary_priority = priority_; - total_bytes_parsed = StripUint32( - data, data_len, &headers_id_and_priority_buffer_, &temporary_priority); - if (total_bytes_parsed > 0 && headers_id_and_priority_buffer_.size() == 0) { - priority_parsed_ = true; - - // Spdy priorities are inverted, so the highest numerical value is the - // lowest legal priority. - if (temporary_priority > QuicUtils::LowestPriority()) { - session()->connection()->SendConnectionClose(QUIC_INVALID_PRIORITY); - return 0; - } - priority_ = temporary_priority; - } - data += total_bytes_parsed; - data_len -= total_bytes_parsed; - } - if (data_len > 0 && headers_id_ == 0u) { - // The headers ID has not yet been read. Strip it from the beginning of - // the data stream. - total_bytes_parsed += StripUint32( - data, data_len, &headers_id_and_priority_buffer_, &headers_id_); - } - return total_bytes_parsed; -} - bool QuicDataStream::FinishedReadingHeaders() { return headers_decompressed_ && decompressed_headers_.empty(); } diff --git a/chromium/net/quic/quic_data_stream.h b/chromium/net/quic/quic_data_stream.h index 84990439e55..1af9004ef33 100644 --- a/chromium/net/quic/quic_data_stream.h +++ b/chromium/net/quic/quic_data_stream.h @@ -13,14 +13,15 @@ #include <list> +#include "base/basictypes.h" #include "base/strings/string_piece.h" #include "net/base/iovec.h" #include "net/base/net_export.h" #include "net/quic/quic_ack_notifier.h" -#include "net/quic/quic_spdy_compressor.h" -#include "net/quic/quic_spdy_decompressor.h" +#include "net/quic/quic_protocol.h" #include "net/quic/quic_stream_sequencer.h" #include "net/quic/reliable_quic_stream.h" +#include "net/spdy/spdy_framer.h" namespace net { @@ -34,8 +35,7 @@ class QuicSession; class SSLInfo; // All this does right now is send data to subclasses via the sequencer. -class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream, - public QuicSpdyDecompressor::Visitor { +class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream { public: // Visitor receives callbacks from the stream. class Visitor { @@ -58,19 +58,39 @@ class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream, // ReliableQuicStream implementation virtual void OnClose() OVERRIDE; + virtual uint32 ProcessRawData(const char* data, uint32 data_len) OVERRIDE; // By default, this is the same as priority(), however it allows streams // to temporarily alter effective priority. For example if a SPDY stream has // compressed but not written headers it can write the headers with a higher // priority. virtual QuicPriority EffectivePriority() const OVERRIDE; - virtual uint32 ProcessRawData(const char* data, uint32 data_len) OVERRIDE; - - // QuicSpdyDecompressor::Visitor implementation. - virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE; - virtual void OnDecompressionError() OVERRIDE; + // Overridden by subclasses to process data. The headers will be delivered + // via OnStreamHeaders, so only data will be delivered through this method. virtual uint32 ProcessData(const char* data, uint32 data_len) = 0; + // Called by the session when decompressed headers data is received + // for this stream. + // May be called multiple times, with each call providing additional headers + // data until OnStreamHeadersComplete is called. + virtual void OnStreamHeaders(base::StringPiece headers_data); + + // Called by the session when headers with a priority have been received + // for this stream. This method will only be called for server streams. + virtual void OnStreamHeadersPriority(QuicPriority priority); + + // Called by the session when decompressed headers have been completely + // delilvered to this stream. If |fin| is true, then this stream + // should be closed; no more data will be sent by the peer. + virtual void OnStreamHeadersComplete(bool fin, size_t frame_len); + + // Writes the headers contained in |header_block| to the dedicated + // headers stream. + virtual size_t WriteHeaders( + const SpdyHeaderBlock& header_block, + bool fin, + QuicAckNotifier::DelegateInterface* ack_notifier_delegate); + // This block of functions wraps the sequencer's functions of the same // name. These methods return uncompressed data until that has // been fully processed. Then they simply delegate to the sequencer. @@ -80,18 +100,12 @@ class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream, virtual bool IsDoneReading() const; virtual bool HasBytesToRead() const; - // Called by the session when a decompression blocked stream - // becomes unblocked. - virtual void OnDecompressorAvailable(); - void set_visitor(Visitor* visitor) { visitor_ = visitor; } bool headers_decompressed() const { return headers_decompressed_; } const IPEndPoint& GetPeerAddress(); - QuicSpdyCompressor* compressor(); - // Gets the SSL connection information. bool GetSSLInfo(SSLInfo* ssl_info); @@ -110,8 +124,6 @@ class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream, uint32 ProcessHeaderData(); - uint32 StripPriorityAndHeaderId(const char* data, uint32 data_len); - bool FinishedReadingHeaders(); Visitor* visitor_; @@ -119,11 +131,6 @@ class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream, bool headers_decompressed_; // The priority of the stream, once parsed. QuicPriority priority_; - // ID of the header block sent by the peer, once parsed. - QuicHeaderId headers_id_; - // Buffer into which we write bytes from priority_ and headers_id_ - // until each is fully parsed. - string headers_id_and_priority_buffer_; // Contains a copy of the decompressed headers until they are consumed // via ProcessData or Readv. string decompressed_headers_; diff --git a/chromium/net/quic/quic_data_stream_test.cc b/chromium/net/quic/quic_data_stream_test.cc index 551ef445715..b72f3cebf06 100644 --- a/chromium/net/quic/quic_data_stream_test.cc +++ b/chromium/net/quic/quic_data_stream_test.cc @@ -6,28 +6,30 @@ #include "net/quic/quic_ack_notifier.h" #include "net/quic/quic_connection.h" -#include "net/quic/quic_spdy_compressor.h" -#include "net/quic/quic_spdy_decompressor.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_utils.h" +#include "net/quic/quic_write_blocked_list.h" #include "net/quic/spdy_utils.h" +#include "net/quic/test_tools/quic_flow_controller_peer.h" #include "net/quic/test_tools/quic_session_peer.h" #include "net/quic/test_tools/quic_test_utils.h" +#include "net/quic/test_tools/reliable_quic_stream_peer.h" +#include "net/test/gtest_util.h" #include "testing/gmock/include/gmock/gmock.h" using base::StringPiece; using std::min; -using testing::_; +using testing::AnyNumber; using testing::InSequence; using testing::Return; using testing::SaveArg; -using testing::StrEq; using testing::StrictMock; +using testing::_; namespace net { namespace test { namespace { -const QuicGuid kStreamId = 3; const bool kIsServer = true; const bool kShouldProcessData = true; @@ -57,7 +59,7 @@ class TestStream : public QuicDataStream { string data_; }; -class QuicDataStreamTest : public ::testing::TestWithParam<bool> { +class QuicDataStreamTest : public ::testing::TestWithParam<QuicVersion> { public: QuicDataStreamTest() { headers_[":host"] = "www.google.com"; @@ -90,16 +92,15 @@ class QuicDataStreamTest : public ::testing::TestWithParam<bool> { } void Initialize(bool stream_should_process_data) { - connection_ = new StrictMock<MockConnection>(kIsServer); - session_.reset(new StrictMock<MockSession>(connection_)); - stream_.reset(new TestStream(kStreamId, session_.get(), - stream_should_process_data)); - stream2_.reset(new TestStream(kStreamId + 2, session_.get(), + connection_ = new testing::StrictMock<MockConnection>( + kIsServer, SupportedVersions(GetParam())); + session_.reset(new testing::StrictMock<MockSession>(connection_)); + stream_.reset(new TestStream(kClientDataStreamId1, session_.get(), stream_should_process_data)); - compressor_.reset(new QuicSpdyCompressor()); - decompressor_.reset(new QuicSpdyDecompressor); + stream2_.reset(new TestStream(kClientDataStreamId2, session_.get(), + stream_should_process_data)); write_blocked_list_ = - QuicSessionPeer::GetWriteblockedStreams(session_.get()); + QuicSessionPeer::GetWriteBlockedStreams(session_.get()); } protected: @@ -107,172 +108,160 @@ class QuicDataStreamTest : public ::testing::TestWithParam<bool> { scoped_ptr<MockSession> session_; scoped_ptr<TestStream> stream_; scoped_ptr<TestStream> stream2_; - scoped_ptr<QuicSpdyCompressor> compressor_; - scoped_ptr<QuicSpdyDecompressor> decompressor_; SpdyHeaderBlock headers_; - WriteBlockedList<QuicStreamId>* write_blocked_list_; + QuicWriteBlockedList* write_blocked_list_; }; -TEST_F(QuicDataStreamTest, ProcessHeaders) { - Initialize(kShouldProcessData); - - string compressed_headers = compressor_->CompressHeadersWithPriority( - QuicUtils::HighestPriority(), headers_); - QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(compressed_headers)); - - stream_->OnStreamFrame(frame); - EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_), stream_->data()); - EXPECT_EQ(QuicUtils::HighestPriority(), stream_->EffectivePriority()); -} +INSTANTIATE_TEST_CASE_P(Tests, QuicDataStreamTest, + ::testing::ValuesIn(QuicSupportedVersions())); -TEST_F(QuicDataStreamTest, ProcessHeadersWithInvalidHeaderId) { +TEST_P(QuicDataStreamTest, ProcessHeaders) { Initialize(kShouldProcessData); - string compressed_headers = compressor_->CompressHeadersWithPriority( - QuicUtils::HighestPriority(), headers_); - compressed_headers[4] = '\xFF'; // Illegal header id. - QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(compressed_headers)); - - EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_HEADER_ID)); - stream_->OnStreamFrame(frame); -} - -TEST_F(QuicDataStreamTest, ProcessHeadersWithInvalidPriority) { - Initialize(kShouldProcessData); - - string compressed_headers = compressor_->CompressHeadersWithPriority( - QuicUtils::HighestPriority(), headers_); - compressed_headers[0] = '\xFF'; // Illegal priority. - QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(compressed_headers)); - - EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_PRIORITY)); - stream_->OnStreamFrame(frame); + string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); + stream_->OnStreamHeadersPriority(QuicUtils::HighestPriority()); + stream_->OnStreamHeaders(headers); + EXPECT_EQ(headers, stream_->data()); + stream_->OnStreamHeadersComplete(false, headers.size()); + EXPECT_EQ(QuicUtils::HighestPriority(), stream_->EffectivePriority()); + EXPECT_EQ(headers, stream_->data()); + EXPECT_FALSE(stream_->IsDoneReading()); } -TEST_F(QuicDataStreamTest, ProcessHeadersAndBody) { +TEST_P(QuicDataStreamTest, ProcessHeadersAndBody) { Initialize(kShouldProcessData); - string compressed_headers = compressor_->CompressHeadersWithPriority( - QuicUtils::HighestPriority(), headers_); + string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); string body = "this is the body"; - string data = compressed_headers + body; - QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(data)); + stream_->OnStreamHeaders(headers); + EXPECT_EQ(headers, stream_->data()); + stream_->OnStreamHeadersComplete(false, headers.size()); + QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body)); stream_->OnStreamFrame(frame); - EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_) + body, - stream_->data()); -} -TEST_F(QuicDataStreamTest, ProcessHeadersAndBodyFragments) { - Initialize(kShouldProcessData); + EXPECT_EQ(headers + body, stream_->data()); +} - string compressed_headers = compressor_->CompressHeadersWithPriority( - QuicUtils::LowestPriority(), headers_); +TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyFragments) { + string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); string body = "this is the body"; - string data = compressed_headers + body; - for (size_t fragment_size = 1; fragment_size < data.size(); ++fragment_size) { + for (size_t fragment_size = 1; fragment_size < body.size(); + ++fragment_size) { Initialize(kShouldProcessData); - for (size_t offset = 0; offset < data.size(); offset += fragment_size) { - size_t remaining_data = data.length() - offset; - StringPiece fragment(data.data() + offset, + for (size_t offset = 0; offset < headers.size(); + offset += fragment_size) { + size_t remaining_data = headers.size() - offset; + StringPiece fragment(headers.data() + offset, min(fragment_size, remaining_data)); - QuicStreamFrame frame(kStreamId, false, offset, MakeIOVector(fragment)); - + stream_->OnStreamHeaders(fragment); + } + stream_->OnStreamHeadersComplete(false, headers.size()); + for (size_t offset = 0; offset < body.size(); offset += fragment_size) { + size_t remaining_data = body.size() - offset; + StringPiece fragment(body.data() + offset, + min(fragment_size, remaining_data)); + QuicStreamFrame frame(kClientDataStreamId1, false, offset, + MakeIOVector(fragment)); stream_->OnStreamFrame(frame); } - ASSERT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_) + body, + ASSERT_EQ(headers + body, stream_->data()) << "fragment_size: " << fragment_size; } +} - for (size_t split_point = 1; split_point < data.size() - 1; ++split_point) { +TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyFragmentsSplit) { + string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); + string body = "this is the body"; + + for (size_t split_point = 1; split_point < body.size() - 1; ++split_point) { Initialize(kShouldProcessData); + StringPiece headers1(headers.data(), split_point); + stream_->OnStreamHeaders(headers1); + + StringPiece headers2(headers.data() + split_point, + headers.size() - split_point); + stream_->OnStreamHeaders(headers2); + stream_->OnStreamHeadersComplete(false, headers.size()); - StringPiece fragment1(data.data(), split_point); - QuicStreamFrame frame1(kStreamId, false, 0, MakeIOVector(fragment1)); + StringPiece fragment1(body.data(), split_point); + QuicStreamFrame frame1(kClientDataStreamId1, false, 0, + MakeIOVector(fragment1)); stream_->OnStreamFrame(frame1); - StringPiece fragment2(data.data() + split_point, data.size() - split_point); - QuicStreamFrame frame2( - kStreamId, false, split_point, MakeIOVector(fragment2)); + StringPiece fragment2(body.data() + split_point, + body.size() - split_point); + QuicStreamFrame frame2(kClientDataStreamId1, false, split_point, + MakeIOVector(fragment2)); stream_->OnStreamFrame(frame2); - ASSERT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_) + body, + ASSERT_EQ(headers + body, stream_->data()) << "split_point: " << split_point; } - EXPECT_EQ(QuicUtils::LowestPriority(), stream_->EffectivePriority()); } -TEST_F(QuicDataStreamTest, ProcessHeadersAndBodyReadv) { +TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyReadv) { Initialize(!kShouldProcessData); - string compressed_headers = compressor_->CompressHeadersWithPriority( - QuicUtils::HighestPriority(), headers_); + string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); string body = "this is the body"; - string data = compressed_headers + body; - QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(data)); - string uncompressed_headers = - SpdyUtils::SerializeUncompressedHeaders(headers_); - string uncompressed_data = uncompressed_headers + body; + stream_->OnStreamHeaders(headers); + EXPECT_EQ(headers, stream_->data()); + stream_->OnStreamHeadersComplete(false, headers.size()); + QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body)); stream_->OnStreamFrame(frame); - EXPECT_EQ(uncompressed_headers, stream_->data()); char buffer[2048]; - ASSERT_LT(data.length(), arraysize(buffer)); + ASSERT_LT(headers.length() + body.length(), arraysize(buffer)); struct iovec vec; vec.iov_base = buffer; vec.iov_len = arraysize(buffer); size_t bytes_read = stream_->Readv(&vec, 1); - EXPECT_EQ(uncompressed_headers.length(), bytes_read); - EXPECT_EQ(uncompressed_headers, string(buffer, bytes_read)); + EXPECT_EQ(headers.length(), bytes_read); + EXPECT_EQ(headers, string(buffer, bytes_read)); bytes_read = stream_->Readv(&vec, 1); EXPECT_EQ(body.length(), bytes_read); EXPECT_EQ(body, string(buffer, bytes_read)); } -TEST_F(QuicDataStreamTest, ProcessHeadersAndBodyIncrementalReadv) { +TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyIncrementalReadv) { Initialize(!kShouldProcessData); - string compressed_headers = compressor_->CompressHeadersWithPriority( - QuicUtils::HighestPriority(), headers_); + string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); string body = "this is the body"; - string data = compressed_headers + body; - QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(data)); - string uncompressed_headers = - SpdyUtils::SerializeUncompressedHeaders(headers_); - string uncompressed_data = uncompressed_headers + body; - + stream_->OnStreamHeaders(headers); + EXPECT_EQ(headers, stream_->data()); + stream_->OnStreamHeadersComplete(false, headers.size()); + QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body)); stream_->OnStreamFrame(frame); - EXPECT_EQ(uncompressed_headers, stream_->data()); char buffer[1]; struct iovec vec; vec.iov_base = buffer; vec.iov_len = arraysize(buffer); - for (size_t i = 0; i < uncompressed_data.length(); ++i) { + + string data = headers + body; + for (size_t i = 0; i < data.length(); ++i) { size_t bytes_read = stream_->Readv(&vec, 1); ASSERT_EQ(1u, bytes_read); - EXPECT_EQ(uncompressed_data.data()[i], buffer[0]); + EXPECT_EQ(data.data()[i], buffer[0]); } } -TEST_F(QuicDataStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) { +TEST_P(QuicDataStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) { Initialize(!kShouldProcessData); - string compressed_headers = compressor_->CompressHeadersWithPriority( - QuicUtils::HighestPriority(), headers_); + string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); string body = "this is the body"; - string data = compressed_headers + body; - QuicStreamFrame frame(kStreamId, false, 0, MakeIOVector(data)); - string uncompressed_headers = - SpdyUtils::SerializeUncompressedHeaders(headers_); - string uncompressed_data = uncompressed_headers + body; - + stream_->OnStreamHeaders(headers); + EXPECT_EQ(headers, stream_->data()); + stream_->OnStreamHeadersComplete(false, headers.size()); + QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body)); stream_->OnStreamFrame(frame); - EXPECT_EQ(uncompressed_headers, stream_->data()); char buffer1[1]; char buffer2[1]; @@ -281,168 +270,302 @@ TEST_F(QuicDataStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) { vec[0].iov_len = arraysize(buffer1); vec[1].iov_base = buffer2; vec[1].iov_len = arraysize(buffer2); - for (size_t i = 0; i < uncompressed_data.length(); i += 2) { + string data = headers + body; + for (size_t i = 0; i < data.length(); i += 2) { size_t bytes_read = stream_->Readv(vec, 2); ASSERT_EQ(2u, bytes_read) << i; - ASSERT_EQ(uncompressed_data.data()[i], buffer1[0]) << i; - ASSERT_EQ(uncompressed_data.data()[i + 1], buffer2[0]) << i; + ASSERT_EQ(data.data()[i], buffer1[0]) << i; + ASSERT_EQ(data.data()[i + 1], buffer2[0]) << i; } } -TEST_F(QuicDataStreamTest, ProcessCorruptHeadersEarly) { +TEST_P(QuicDataStreamTest, StreamFlowControlBlocked) { + // Tests that we send a BLOCKED frame to the peer when we attempt to write, + // but are flow control blocked. + if (GetParam() < QUIC_VERSION_17) { + return; + } + ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true); + Initialize(kShouldProcessData); - string compressed_headers1 = compressor_->CompressHeadersWithPriority( - QuicUtils::HighestPriority(), headers_); - QuicStreamFrame frame1( - stream_->id(), false, 0, MakeIOVector(compressed_headers1)); - string decompressed_headers1 = - SpdyUtils::SerializeUncompressedHeaders(headers_); - - headers_["content-type"] = "text/plain"; - string compressed_headers2 = compressor_->CompressHeadersWithPriority( - QuicUtils::HighestPriority(), headers_); - // Corrupt the compressed data. - compressed_headers2[compressed_headers2.length() - 1] ^= 0xA1; - QuicStreamFrame frame2( - stream2_->id(), false, 0, MakeIOVector(compressed_headers2)); - string decompressed_headers2 = - SpdyUtils::SerializeUncompressedHeaders(headers_); - - // Deliver frame2 to stream2 out of order. The decompressor is not - // available yet, so no data will be processed. The compressed data - // will be buffered until OnDecompressorAvailable() is called - // to process it. - stream2_->OnStreamFrame(frame2); - EXPECT_EQ("", stream2_->data()); + // Set a small flow control limit. + const uint64 kWindow = 36; + QuicFlowControllerPeer::SetSendWindowOffset(stream_->flow_controller(), + kWindow); + EXPECT_EQ(kWindow, QuicFlowControllerPeer::SendWindowOffset( + stream_->flow_controller())); + + // Try to send more data than the flow control limit allows. + string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); + string body; + const uint64 kOverflow = 15; + GenerateBody(&body, kWindow + kOverflow); + + EXPECT_CALL(*connection_, SendBlocked(kClientDataStreamId1)); + EXPECT_CALL(*session_, WritevData(kClientDataStreamId1, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(kWindow, true))); + stream_->WriteOrBufferData(body, false, NULL); + + // Should have sent as much as possible, resulting in no send window left. + EXPECT_EQ(0u, + QuicFlowControllerPeer::SendWindowSize(stream_->flow_controller())); + + // And we should have queued the overflowed data. + EXPECT_EQ(kOverflow, + ReliableQuicStreamPeer::SizeOfQueuedData(stream_.get())); +} + +TEST_P(QuicDataStreamTest, StreamFlowControlNoWindowUpdateIfNotConsumed) { + // The flow control receive window decreases whenever we add new bytes to the + // sequencer, whether they are consumed immediately or buffered. However we + // only send WINDOW_UPDATE frames based on increasing number of bytes + // consumed. + if (GetParam() < QUIC_VERSION_17) { + return; + } + ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true); + + // Don't process data - it will be buffered instead. + Initialize(!kShouldProcessData); - // Now deliver frame1 to stream1. The decompressor is available so - // the data will be processed, and the decompressor will become - // available for stream2. + // Expect no WINDOW_UPDATE frames to be sent. + EXPECT_CALL(*connection_, SendWindowUpdate(_, _)).Times(0); + + // Set a small flow control receive window. + const uint64 kWindow = 36; + QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), + kWindow); + QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(), + kWindow); + EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowOffset( + stream_->flow_controller())); + + // Stream receives enough data to fill a fraction of the receive window. + string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); + string body; + GenerateBody(&body, kWindow / 3); + stream_->OnStreamHeaders(headers); + EXPECT_EQ(headers, stream_->data()); + stream_->OnStreamHeadersComplete(false, headers.size()); + + QuicStreamFrame frame1(kClientDataStreamId1, false, 0, MakeIOVector(body)); stream_->OnStreamFrame(frame1); - EXPECT_EQ(decompressed_headers1, stream_->data()); - - // Verify that the decompressor is available, and inform stream2 - // that it can now decompress the buffered compressed data. Since - // the compressed data is corrupt, the stream will shutdown the session. - EXPECT_EQ(2u, session_->decompressor()->current_header_id()); - EXPECT_CALL(*connection_, SendConnectionClose(QUIC_DECOMPRESSION_FAILURE)); - stream2_->OnDecompressorAvailable(); - EXPECT_EQ("", stream2_->data()); + EXPECT_EQ(kWindow - (kWindow / 3), QuicFlowControllerPeer::ReceiveWindowSize( + stream_->flow_controller())); + + // Now receive another frame which results in the receive window being over + // half full. This should all be buffered, decreasing the receive window but + // not sending WINDOW_UPDATE. + QuicStreamFrame frame2(kClientDataStreamId1, false, kWindow / 3, + MakeIOVector(body)); + stream_->OnStreamFrame(frame2); + EXPECT_EQ( + kWindow - (2 * kWindow / 3), + QuicFlowControllerPeer::ReceiveWindowSize(stream_->flow_controller())); } -TEST_F(QuicDataStreamTest, ProcessPartialHeadersEarly) { - Initialize(kShouldProcessData); +TEST_P(QuicDataStreamTest, StreamFlowControlWindowUpdate) { + // Tests that on receipt of data, the stream updates its receive window offset + // appropriately, and sends WINDOW_UPDATE frames when its receive window drops + // too low. + if (GetParam() < QUIC_VERSION_17) { + return; + } + ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true); - string compressed_headers1 = compressor_->CompressHeadersWithPriority( - QuicUtils::HighestPriority(), headers_); - QuicStreamFrame frame1( - stream_->id(), false, 0, MakeIOVector(compressed_headers1)); - string decompressed_headers1 = - SpdyUtils::SerializeUncompressedHeaders(headers_); - - headers_["content-type"] = "text/plain"; - string compressed_headers2 = compressor_->CompressHeadersWithPriority( - QuicUtils::HighestPriority(), headers_); - string partial_compressed_headers = - compressed_headers2.substr(0, compressed_headers2.length() / 2); - QuicStreamFrame frame2( - stream2_->id(), false, 0, MakeIOVector(partial_compressed_headers)); - string decompressed_headers2 = - SpdyUtils::SerializeUncompressedHeaders(headers_); - - // Deliver frame2 to stream2 out of order. The decompressor is not - // available yet, so no data will be processed. The compressed data - // will be buffered until OnDecompressorAvailable() is called - // to process it. - stream2_->OnStreamFrame(frame2); - EXPECT_EQ("", stream2_->data()); + Initialize(kShouldProcessData); - // Now deliver frame1 to stream1. The decompressor is available so - // the data will be processed, and the decompressor will become - // available for stream2. + // Set a small flow control limit. + const uint64 kWindow = 36; + QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), + kWindow); + QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(), + kWindow); + EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowOffset( + stream_->flow_controller())); + + // Stream receives enough data to fill a fraction of the receive window. + string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); + string body; + GenerateBody(&body, kWindow / 3); + stream_->OnStreamHeaders(headers); + EXPECT_EQ(headers, stream_->data()); + stream_->OnStreamHeadersComplete(false, headers.size()); + + QuicStreamFrame frame1(kClientDataStreamId1, false, 0, MakeIOVector(body)); stream_->OnStreamFrame(frame1); - EXPECT_EQ(decompressed_headers1, stream_->data()); - - // Verify that the decompressor is available, and inform stream2 - // that it can now decompress the buffered compressed data. Since - // the compressed data is incomplete it will not be passed to - // the stream. - EXPECT_EQ(2u, session_->decompressor()->current_header_id()); - stream2_->OnDecompressorAvailable(); - EXPECT_EQ("", stream2_->data()); - - // Now send remaining data and verify that we have now received the - // compressed headers. - string remaining_compressed_headers = - compressed_headers2.substr(partial_compressed_headers.length()); - - QuicStreamFrame frame3(stream2_->id(), false, - partial_compressed_headers.length(), - MakeIOVector(remaining_compressed_headers)); - stream2_->OnStreamFrame(frame3); - EXPECT_EQ(decompressed_headers2, stream2_->data()); + EXPECT_EQ(kWindow - (kWindow / 3), QuicFlowControllerPeer::ReceiveWindowSize( + stream_->flow_controller())); + + // Now receive another frame which results in the receive window being over + // half full. This will trigger the stream to increase its receive window + // offset and send a WINDOW_UPDATE. The result will be again an available + // window of kWindow bytes. + QuicStreamFrame frame2(kClientDataStreamId1, false, kWindow / 3, + MakeIOVector(body)); + EXPECT_CALL(*connection_, + SendWindowUpdate(kClientDataStreamId1, + QuicFlowControllerPeer::ReceiveWindowOffset( + stream_->flow_controller()) + + 2 * kWindow / 3)); + stream_->OnStreamFrame(frame2); + EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowSize( + stream_->flow_controller())); } -TEST_F(QuicDataStreamTest, ProcessHeadersEarly) { +TEST_P(QuicDataStreamTest, ConnectionFlowControlWindowUpdate) { + // Tests that on receipt of data, the connection updates its receive window + // offset appropriately, and sends WINDOW_UPDATE frames when its receive + // window drops too low. + if (GetParam() < QUIC_VERSION_19) { + return; + } + ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true); + ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2, + true); + Initialize(kShouldProcessData); - string compressed_headers1 = compressor_->CompressHeadersWithPriority( - QuicUtils::HighestPriority(), headers_); - QuicStreamFrame frame1( - stream_->id(), false, 0, MakeIOVector(compressed_headers1)); - string decompressed_headers1 = - SpdyUtils::SerializeUncompressedHeaders(headers_); - - headers_["content-type"] = "text/plain"; - string compressed_headers2 = compressor_->CompressHeadersWithPriority( - QuicUtils::HighestPriority(), headers_); - QuicStreamFrame frame2( - stream2_->id(), false, 0, MakeIOVector(compressed_headers2)); - string decompressed_headers2 = - SpdyUtils::SerializeUncompressedHeaders(headers_); - - // Deliver frame2 to stream2 out of order. The decompressor is not - // available yet, so no data will be processed. The compressed data - // will be buffered until OnDecompressorAvailable() is called - // to process it. + // Set a small flow control limit for streams and connection. + const uint64 kWindow = 36; + QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), + kWindow); + QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(), + kWindow); + QuicFlowControllerPeer::SetReceiveWindowOffset(stream2_->flow_controller(), + kWindow); + QuicFlowControllerPeer::SetMaxReceiveWindow(stream2_->flow_controller(), + kWindow); + QuicFlowControllerPeer::SetReceiveWindowOffset(session_->flow_controller(), + kWindow); + QuicFlowControllerPeer::SetMaxReceiveWindow(session_->flow_controller(), + kWindow); + + // Supply headers to both streams so that they are happy to receive data. + string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); + stream_->OnStreamHeaders(headers); + stream_->OnStreamHeadersComplete(false, headers.size()); + stream2_->OnStreamHeaders(headers); + stream2_->OnStreamHeadersComplete(false, headers.size()); + + // Each stream gets a quarter window of data. This should not trigger a + // WINDOW_UPDATE for either stream, nor for the connection. + string body; + GenerateBody(&body, kWindow / 4); + QuicStreamFrame frame1(kClientDataStreamId1, false, 0, MakeIOVector(body)); + stream_->OnStreamFrame(frame1); + QuicStreamFrame frame2(kClientDataStreamId2, false, 0, MakeIOVector(body)); stream2_->OnStreamFrame(frame2); - EXPECT_EQ("", stream2_->data()); - // Now deliver frame1 to stream1. The decompressor is available so - // the data will be processed, and the decompressor will become - // available for stream2. - stream_->OnStreamFrame(frame1); - EXPECT_EQ(decompressed_headers1, stream_->data()); + // Now receive a further single byte on one stream - again this does not + // trigger a stream WINDOW_UPDATE, but now the connection flow control window + // is over half full and thus a connection WINDOW_UPDATE is sent. + EXPECT_CALL(*connection_, SendWindowUpdate(kClientDataStreamId1, _)).Times(0); + EXPECT_CALL(*connection_, SendWindowUpdate(kClientDataStreamId2, _)).Times(0); + EXPECT_CALL(*connection_, + SendWindowUpdate(0, QuicFlowControllerPeer::ReceiveWindowOffset( + session_->flow_controller()) + + 1 + kWindow / 2)); + QuicStreamFrame frame3(kClientDataStreamId1, false, (kWindow / 4), + MakeIOVector("a")); + stream_->OnStreamFrame(frame3); +} + +TEST_P(QuicDataStreamTest, StreamFlowControlViolation) { + // Tests that on if the peer sends too much data (i.e. violates the flow + // control protocol), then we terminate the connection. + if (GetParam() < QUIC_VERSION_17) { + return; + } + ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true); + + // Stream should not process data, so that data gets buffered in the + // sequencer, triggering flow control limits. + Initialize(!kShouldProcessData); - // Verify that the decompressor is available, and inform stream2 - // that it can now decompress the buffered compressed data. - EXPECT_EQ(2u, session_->decompressor()->current_header_id()); - stream2_->OnDecompressorAvailable(); - EXPECT_EQ(decompressed_headers2, stream2_->data()); + // Set a small flow control limit. + const uint64 kWindow = 50; + QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), + kWindow); + + string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); + stream_->OnStreamHeaders(headers); + EXPECT_EQ(headers, stream_->data()); + stream_->OnStreamHeadersComplete(false, headers.size()); + + // Receive data to overflow the window, violating flow control. + string body; + GenerateBody(&body, kWindow + 1); + QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body)); + EXPECT_CALL(*connection_, + SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA)); + stream_->OnStreamFrame(frame); } -TEST_F(QuicDataStreamTest, ProcessHeadersDelay) { +TEST_P(QuicDataStreamTest, ConnectionFlowControlViolation) { + // Tests that on if the peer sends too much data (i.e. violates the flow + // control protocol), at the connection level (rather than the stream level) + // then we terminate the connection. + if (GetParam() < QUIC_VERSION_19) { + return; + } + ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true); + ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2, + true); + + // Stream should not process data, so that data gets buffered in the + // sequencer, triggering flow control limits. Initialize(!kShouldProcessData); - string compressed_headers = compressor_->CompressHeadersWithPriority( - QuicUtils::HighestPriority(), headers_); - QuicStreamFrame frame1( - stream_->id(), false, 0, MakeIOVector(compressed_headers)); - string decompressed_headers = - SpdyUtils::SerializeUncompressedHeaders(headers_); + // Set a small flow control window on streams, and connection. + const uint64 kStreamWindow = 50; + const uint64 kConnectionWindow = 10; + QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), + kStreamWindow); + QuicFlowControllerPeer::SetReceiveWindowOffset(session_->flow_controller(), + kConnectionWindow); + + string headers = SpdyUtils::SerializeUncompressedHeaders(headers_); + stream_->OnStreamHeaders(headers); + EXPECT_EQ(headers, stream_->data()); + stream_->OnStreamHeadersComplete(false, headers.size()); + + // Send enough data to overflow the connection level flow control window. + string body; + GenerateBody(&body, kConnectionWindow + 1); + EXPECT_LT(body.size(), kStreamWindow); + QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body)); + + EXPECT_CALL(*connection_, + SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA)); + stream_->OnStreamFrame(frame); +} - // Send the headers to the stream and verify they were decompressed. - stream_->OnStreamFrame(frame1); - EXPECT_EQ(2u, session_->decompressor()->current_header_id()); - - // Verify that we are now able to handle the body data, - // even though the stream has not processed the headers. - EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_HEADER_ID)) - .Times(0); - QuicStreamFrame frame2(stream_->id(), false, compressed_headers.length(), - MakeIOVector("body data")); - stream_->OnStreamFrame(frame2); +TEST_P(QuicDataStreamTest, StreamFlowControlFinNotBlocked) { + // An attempt to write a FIN with no data should not be flow control blocked, + // even if the send window is 0. + if (GetParam() < QUIC_VERSION_17) { + return; + } + ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true); + + Initialize(kShouldProcessData); + + // Set a flow control limit of zero. + QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), 0); + EXPECT_EQ(0u, QuicFlowControllerPeer::ReceiveWindowOffset( + stream_->flow_controller())); + + // Send a frame with a FIN but no data. This should not be blocked. + string body = ""; + bool fin = true; + + EXPECT_CALL(*connection_, SendBlocked(kClientDataStreamId1)).Times(0); + EXPECT_CALL(*session_, WritevData(kClientDataStreamId1, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(0, fin))); + + stream_->WriteOrBufferData(body, fin, NULL); } } // namespace diff --git a/chromium/net/quic/quic_data_writer.cc b/chromium/net/quic/quic_data_writer.cc index b7aff26fe01..70b0fdaed31 100644 --- a/chromium/net/quic/quic_data_writer.cc +++ b/chromium/net/quic/quic_data_writer.cc @@ -167,7 +167,10 @@ void QuicDataWriter::WritePadding() { } bool QuicDataWriter::WriteUInt8ToOffset(uint8 value, size_t offset) { - DCHECK_LT(offset, capacity_); + if (offset >= capacity_) { + LOG(DFATAL) << "offset: " << offset << " >= capacity: " << capacity_; + return false; + } size_t latched_length = length_; length_ = offset; bool success = WriteUInt8(value); diff --git a/chromium/net/quic/quic_data_writer.h b/chromium/net/quic/quic_data_writer.h index f8a3751bf1b..0e14e87c692 100644 --- a/chromium/net/quic/quic_data_writer.h +++ b/chromium/net/quic/quic_data_writer.h @@ -74,6 +74,8 @@ class NET_EXPORT_PRIVATE QuicDataWriter { char* buffer_; size_t capacity_; // Allocation size of payload (or -1 if buffer is const). size_t length_; // Current length of the buffer. + + DISALLOW_COPY_AND_ASSIGN(QuicDataWriter); }; } // namespace net diff --git a/chromium/net/quic/quic_data_writer_test.cc b/chromium/net/quic/quic_data_writer_test.cc index 9106947b52e..07346c78891 100644 --- a/chromium/net/quic/quic_data_writer_test.cc +++ b/chromium/net/quic/quic_data_writer_test.cc @@ -6,6 +6,7 @@ #include "base/memory/scoped_ptr.h" #include "net/quic/quic_data_reader.h" +#include "net/test/gtest_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -32,13 +33,8 @@ TEST(QuicDataWriterTest, WriteUInt8ToOffset) { TEST(QuicDataWriterDeathTest, WriteUInt8ToOffset) { QuicDataWriter writer(4); -#if !defined(WIN32) && defined(GTEST_HAS_DEATH_TEST) -#if !defined(DCHECK_ALWAYS_ON) - EXPECT_DEBUG_DEATH(writer.WriteUInt8ToOffset(5, 4), "Check failed"); -#else - EXPECT_DEATH(writer.WriteUInt8ToOffset(5, 4), "Check failed"); -#endif -#endif + EXPECT_DFATAL(EXPECT_FALSE(writer.WriteUInt8ToOffset(5, 4)), + "offset: 4 >= capacity: 4"); } TEST(QuicDataWriterTest, SanityCheckUFloat16Consts) { diff --git a/chromium/net/quic/quic_default_packet_writer.cc b/chromium/net/quic/quic_default_packet_writer.cc index 74b86af2a98..5dd80c6fd3d 100644 --- a/chromium/net/quic/quic_default_packet_writer.cc +++ b/chromium/net/quic/quic_default_packet_writer.cc @@ -17,7 +17,8 @@ QuicDefaultPacketWriter::QuicDefaultPacketWriter() : weak_factory_(this) { QuicDefaultPacketWriter::QuicDefaultPacketWriter(DatagramClientSocket* socket) : weak_factory_(this), - socket_(socket) { + socket_(socket), + write_blocked_(false) { } QuicDefaultPacketWriter::~QuicDefaultPacketWriter() {} @@ -25,10 +26,10 @@ QuicDefaultPacketWriter::~QuicDefaultPacketWriter() {} WriteResult QuicDefaultPacketWriter::WritePacket( const char* buffer, size_t buf_len, const net::IPAddressNumber& self_address, - const net::IPEndPoint& peer_address, - QuicBlockedWriterInterface* blocked_writer) { + const net::IPEndPoint& peer_address) { scoped_refptr<StringIOBuffer> buf( new StringIOBuffer(std::string(buffer, buf_len))); + DCHECK(!IsWriteBlocked()); int rv = socket_->Write(buf.get(), buf_len, base::Bind(&QuicDefaultPacketWriter::OnWriteComplete, @@ -40,6 +41,7 @@ WriteResult QuicDefaultPacketWriter::WritePacket( status = WRITE_STATUS_ERROR; } else { status = WRITE_STATUS_BLOCKED; + write_blocked_ = true; } } @@ -52,8 +54,17 @@ bool QuicDefaultPacketWriter::IsWriteBlockedDataBuffered() const { return true; } +bool QuicDefaultPacketWriter::IsWriteBlocked() const { + return write_blocked_; +} + +void QuicDefaultPacketWriter::SetWritable() { + write_blocked_ = false; +} + void QuicDefaultPacketWriter::OnWriteComplete(int rv) { DCHECK_NE(rv, ERR_IO_PENDING); + write_blocked_ = false; WriteResult result(rv < 0 ? WRITE_STATUS_ERROR : WRITE_STATUS_OK, rv); connection_->OnPacketSent(result); connection_->OnCanWrite(); diff --git a/chromium/net/quic/quic_default_packet_writer.h b/chromium/net/quic/quic_default_packet_writer.h index f60c76ea9be..ed3b5975cee 100644 --- a/chromium/net/quic/quic_default_packet_writer.h +++ b/chromium/net/quic/quic_default_packet_writer.h @@ -15,7 +15,7 @@ namespace net { -class QuicBlockedWriterInterface; +struct WriteResult; // Chrome specific packet writer which uses a DatagramClientSocket for writing // data. @@ -26,23 +26,31 @@ class NET_EXPORT_PRIVATE QuicDefaultPacketWriter : public QuicPacketWriter { virtual ~QuicDefaultPacketWriter(); // QuicPacketWriter - virtual WriteResult WritePacket( - const char* buffer, size_t buf_len, - const net::IPAddressNumber& self_address, - const net::IPEndPoint& peer_address, - QuicBlockedWriterInterface* blocked_writer) OVERRIDE; - + virtual WriteResult WritePacket(const char* buffer, + size_t buf_len, + const IPAddressNumber& self_address, + const IPEndPoint& peer_address) OVERRIDE; virtual bool IsWriteBlockedDataBuffered() const OVERRIDE; + virtual bool IsWriteBlocked() const OVERRIDE; + virtual void SetWritable() OVERRIDE; void OnWriteComplete(int rv); void SetConnection(QuicConnection* connection) { connection_ = connection; } + protected: + void set_write_blocked(bool is_blocked) { + write_blocked_ = is_blocked; + } + private: base::WeakPtrFactory<QuicDefaultPacketWriter> weak_factory_; DatagramClientSocket* socket_; QuicConnection* connection_; + bool write_blocked_; + + DISALLOW_COPY_AND_ASSIGN(QuicDefaultPacketWriter); }; } // namespace net diff --git a/chromium/net/quic/quic_dispatcher.cc b/chromium/net/quic/quic_dispatcher.cc new file mode 100644 index 00000000000..280622da381 --- /dev/null +++ b/chromium/net/quic/quic_dispatcher.cc @@ -0,0 +1,426 @@ +// 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/quic_dispatcher.h" + +#include <errno.h> + +#include "base/debug/stack_trace.h" +#include "base/logging.h" +#include "base/stl_util.h" +#include "net/quic/quic_blocked_writer_interface.h" +#include "net/quic/quic_connection_helper.h" +#include "net/quic/quic_flags.h" +#include "net/quic/quic_time_wait_list_manager.h" +#include "net/quic/quic_utils.h" + +namespace net { + +using base::StringPiece; +using std::make_pair; +using std::find; + +class DeleteSessionsAlarm : public QuicAlarm::Delegate { + public: + explicit DeleteSessionsAlarm(QuicDispatcher* dispatcher) + : dispatcher_(dispatcher) { + } + + virtual QuicTime OnAlarm() OVERRIDE { + dispatcher_->DeleteSessions(); + return QuicTime::Zero(); + } + + private: + QuicDispatcher* dispatcher_; +}; + +class QuicDispatcher::QuicFramerVisitor : public QuicFramerVisitorInterface { + public: + explicit QuicFramerVisitor(QuicDispatcher* dispatcher) + : dispatcher_(dispatcher), + connection_id_(0) {} + + // QuicFramerVisitorInterface implementation + virtual void OnPacket() OVERRIDE {} + virtual bool OnUnauthenticatedPublicHeader( + const QuicPacketPublicHeader& header) OVERRIDE { + connection_id_ = header.connection_id; + return dispatcher_->OnUnauthenticatedPublicHeader(header); + } + virtual bool OnUnauthenticatedHeader( + const QuicPacketHeader& header) OVERRIDE { + dispatcher_->OnUnauthenticatedHeader(header); + return false; + } + virtual void OnError(QuicFramer* framer) OVERRIDE { + DVLOG(1) << QuicUtils::ErrorToString(framer->error()); + } + + virtual bool OnProtocolVersionMismatch( + QuicVersion /*received_version*/) OVERRIDE { + if (dispatcher_->time_wait_list_manager()->IsConnectionIdInTimeWait( + connection_id_)) { + // Keep processing after protocol mismatch - this will be dealt with by + // the TimeWaitListManager. + return true; + } else { + DLOG(DFATAL) << "Version mismatch, connection ID (" << connection_id_ + << ") not in time wait list."; + return false; + } + } + + // The following methods should never get called because we always return + // false from OnUnauthenticatedHeader(). As a result, we never process the + // payload of the packet. + virtual void OnPublicResetPacket( + const QuicPublicResetPacket& /*packet*/) OVERRIDE { + DCHECK(false); + } + virtual void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& /*packet*/) OVERRIDE { + DCHECK(false); + } + virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE { + DCHECK(false); + } + virtual bool OnPacketHeader(const QuicPacketHeader& /*header*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual void OnRevivedPacket() OVERRIDE { + DCHECK(false); + } + virtual void OnFecProtectedPayload(StringPiece /*payload*/) OVERRIDE { + DCHECK(false); + } + virtual bool OnStreamFrame(const QuicStreamFrame& /*frame*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnAckFrame(const QuicAckFrame& /*frame*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnCongestionFeedbackFrame( + const QuicCongestionFeedbackFrame& /*frame*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnStopWaitingFrame( + const QuicStopWaitingFrame& /*frame*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnPingFrame(const QuicPingFrame& /*frame*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnRstStreamFrame(const QuicRstStreamFrame& /*frame*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnConnectionCloseFrame( + const QuicConnectionCloseFrame & /*frame*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnGoAwayFrame(const QuicGoAwayFrame& /*frame*/) OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& /*frame*/) + OVERRIDE { + DCHECK(false); + return false; + } + virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE { + DCHECK(false); + return false; + } + virtual void OnFecData(const QuicFecData& /*fec*/) OVERRIDE { + DCHECK(false); + } + virtual void OnPacketComplete() OVERRIDE { + DCHECK(false); + } + + private: + QuicDispatcher* dispatcher_; + + // Latched in OnUnauthenticatedPublicHeader for use later. + QuicConnectionId connection_id_; +}; + +QuicDispatcher::QuicDispatcher(const QuicConfig& config, + const QuicCryptoServerConfig& crypto_config, + const QuicVersionVector& supported_versions, + QuicConnectionHelperInterface* helper) + : config_(config), + crypto_config_(crypto_config), + helper_(helper), + delete_sessions_alarm_( + helper_->CreateAlarm(new DeleteSessionsAlarm(this))), + supported_versions_(supported_versions), + supported_versions_no_flow_control_(supported_versions), + supported_versions_no_connection_flow_control_(supported_versions), + current_packet_(NULL), + framer_(supported_versions, /*unused*/ QuicTime::Zero(), true), + framer_visitor_(new QuicFramerVisitor(this)) { + framer_.set_visitor(framer_visitor_.get()); +} + +QuicDispatcher::~QuicDispatcher() { + STLDeleteValues(&session_map_); + STLDeleteElements(&closed_session_list_); +} + +void QuicDispatcher::Initialize(QuicPacketWriter* writer) { + DCHECK(writer_ == NULL); + writer_.reset(writer); + time_wait_list_manager_.reset(CreateQuicTimeWaitListManager()); + + // Remove all versions > QUIC_VERSION_16 from the + // supported_versions_no_flow_control_ vector. + QuicVersionVector::iterator it = + find(supported_versions_no_flow_control_.begin(), + supported_versions_no_flow_control_.end(), QUIC_VERSION_17); + if (it != supported_versions_no_flow_control_.end()) { + supported_versions_no_flow_control_.erase( + supported_versions_no_flow_control_.begin(), it + 1); + } + CHECK(!supported_versions_no_flow_control_.empty()); + + // Remove all versions > QUIC_VERSION_18 from the + // supported_versions_no_connection_flow_control_ vector. + QuicVersionVector::iterator connection_it = + find(supported_versions_no_connection_flow_control_.begin(), + supported_versions_no_connection_flow_control_.end(), + QUIC_VERSION_19); + if (connection_it != supported_versions_no_connection_flow_control_.end()) { + supported_versions_no_connection_flow_control_.erase( + supported_versions_no_connection_flow_control_.begin(), + connection_it + 1); + } + CHECK(!supported_versions_no_connection_flow_control_.empty()); +} + +void QuicDispatcher::ProcessPacket(const IPEndPoint& server_address, + const IPEndPoint& client_address, + const QuicEncryptedPacket& packet) { + current_server_address_ = server_address; + current_client_address_ = client_address; + current_packet_ = &packet; + // ProcessPacket will cause the packet to be dispatched in + // OnUnauthenticatedPublicHeader, or sent to the time wait list manager + // in OnAuthenticatedHeader. + framer_.ProcessPacket(packet); + // TODO(rjshade): Return a status describing if/why a packet was dropped, + // and log somehow. Maybe expose as a varz. +} + +bool QuicDispatcher::OnUnauthenticatedPublicHeader( + const QuicPacketPublicHeader& header) { + QuicSession* session = NULL; + + QuicConnectionId connection_id = header.connection_id; + SessionMap::iterator it = session_map_.find(connection_id); + if (it == session_map_.end()) { + if (header.reset_flag) { + return false; + } + if (time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)) { + return HandlePacketForTimeWait(header); + } + + // Ensure the packet has a version negotiation bit set before creating a new + // session for it. All initial packets for a new connection are required to + // have the flag set. Otherwise it may be a stray packet. + if (header.version_flag) { + session = CreateQuicSession(connection_id, current_server_address_, + current_client_address_); + } + + if (session == NULL) { + DVLOG(1) << "Failed to create session for " << connection_id; + // Add this connection_id fo the time-wait state, to safely reject future + // packets. + + if (header.version_flag && + !framer_.IsSupportedVersion(header.versions.front())) { + // TODO(ianswett): Produce a no-version version negotiation packet. + return false; + } + + // Use the version in the packet if possible, otherwise assume the latest. + QuicVersion version = header.version_flag ? header.versions.front() : + supported_versions_.front(); + time_wait_list_manager_->AddConnectionIdToTimeWait( + connection_id, version, NULL); + DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)); + return HandlePacketForTimeWait(header); + } + DVLOG(1) << "Created new session for " << connection_id; + session_map_.insert(make_pair(connection_id, session)); + } else { + session = it->second; + } + + session->connection()->ProcessUdpPacket( + current_server_address_, current_client_address_, *current_packet_); + + // Do not parse the packet further. The session will process it completely. + return false; +} + +void QuicDispatcher::OnUnauthenticatedHeader(const QuicPacketHeader& header) { + DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait( + header.public_header.connection_id)); + time_wait_list_manager_->ProcessPacket(current_server_address_, + current_client_address_, + header.public_header.connection_id, + header.packet_sequence_number, + *current_packet_); +} + +void QuicDispatcher::CleanUpSession(SessionMap::iterator it) { + QuicConnection* connection = it->second->connection(); + QuicEncryptedPacket* connection_close_packet = + connection->ReleaseConnectionClosePacket(); + write_blocked_list_.erase(connection); + time_wait_list_manager_->AddConnectionIdToTimeWait(it->first, + connection->version(), + connection_close_packet); + session_map_.erase(it); +} + +void QuicDispatcher::DeleteSessions() { + STLDeleteElements(&closed_session_list_); +} + +void QuicDispatcher::OnCanWrite() { + // We got an EPOLLOUT: the socket should not be blocked. + writer_->SetWritable(); + + // Give each writer one attempt to write. + int num_writers = write_blocked_list_.size(); + for (int i = 0; i < num_writers; ++i) { + if (write_blocked_list_.empty()) { + return; + } + QuicBlockedWriterInterface* blocked_writer = + write_blocked_list_.begin()->first; + write_blocked_list_.erase(write_blocked_list_.begin()); + blocked_writer->OnCanWrite(); + if (writer_->IsWriteBlocked()) { + // We were unable to write. Wait for the next EPOLLOUT. The writer is + // responsible for adding itself to the blocked list via OnWriteBlocked(). + return; + } + } +} + +bool QuicDispatcher::HasPendingWrites() const { + return !write_blocked_list_.empty(); +} + +void QuicDispatcher::Shutdown() { + while (!session_map_.empty()) { + QuicSession* session = session_map_.begin()->second; + session->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY); + // Validate that the session removes itself from the session map on close. + DCHECK(session_map_.empty() || session_map_.begin()->second != session); + } + DeleteSessions(); +} + +void QuicDispatcher::OnConnectionClosed(QuicConnectionId connection_id, + QuicErrorCode error) { + SessionMap::iterator it = session_map_.find(connection_id); + if (it == session_map_.end()) { + LOG(DFATAL) << "ConnectionId " << connection_id + << " does not exist in the session map. " + << "Error: " << QuicUtils::ErrorToString(error); + LOG(DFATAL) << base::debug::StackTrace().ToString(); + return; + } + DVLOG_IF(1, error != QUIC_NO_ERROR) << "Closing connection (" + << connection_id + << ") due to error: " + << QuicUtils::ErrorToString(error); + if (closed_session_list_.empty()) { + delete_sessions_alarm_->Set(helper_->GetClock()->ApproximateNow()); + } + closed_session_list_.push_back(it->second); + CleanUpSession(it); +} + +void QuicDispatcher::OnWriteBlocked(QuicBlockedWriterInterface* writer) { + DCHECK(writer_->IsWriteBlocked()); + write_blocked_list_.insert(make_pair(writer, true)); +} + +QuicSession* QuicDispatcher::CreateQuicSession( + QuicConnectionId connection_id, + const IPEndPoint& server_address, + const IPEndPoint& client_address) { + QuicServerSession* session = new QuicServerSession( + config_, + CreateQuicConnection(connection_id, server_address, client_address), + this); + session->InitializeSession(crypto_config_); + return session; +} + +QuicConnection* QuicDispatcher::CreateQuicConnection( + QuicConnectionId connection_id, + const IPEndPoint& server_address, + const IPEndPoint& client_address) { + if (FLAGS_enable_quic_stream_flow_control_2 && + FLAGS_enable_quic_connection_flow_control_2) { + DVLOG(1) << "Creating QuicDispatcher with all versions."; + return new QuicConnection(connection_id, client_address, helper_, + writer_.get(), true, supported_versions_); + } + + if (FLAGS_enable_quic_stream_flow_control_2 && + !FLAGS_enable_quic_connection_flow_control_2) { + DVLOG(1) << "Connection flow control disabled, creating QuicDispatcher " + << "WITHOUT version 19 or higher."; + return new QuicConnection(connection_id, client_address, helper_, + writer_.get(), true, + supported_versions_no_connection_flow_control_); + } + + DVLOG(1) << "Flow control disabled, creating QuicDispatcher WITHOUT " + << "version 17 or higher."; + return new QuicConnection(connection_id, client_address, helper_, + writer_.get(), true, + supported_versions_no_flow_control_); +} + +QuicTimeWaitListManager* QuicDispatcher::CreateQuicTimeWaitListManager() { + return new QuicTimeWaitListManager( + writer_.get(), this, helper_, supported_versions()); +} + +bool QuicDispatcher::HandlePacketForTimeWait( + const QuicPacketPublicHeader& header) { + if (header.reset_flag) { + // Public reset packets do not have sequence numbers, so ignore the packet. + return false; + } + + // Switch the framer to the correct version, so that the sequence number can + // be parsed correctly. + framer_.set_version(time_wait_list_manager_->GetQuicVersionFromConnectionId( + header.connection_id)); + + // Continue parsing the packet to extract the sequence number. Then + // send it to the time wait manager in OnUnathenticatedHeader. + return true; +} + +} // namespace net diff --git a/chromium/net/quic/quic_dispatcher.h b/chromium/net/quic/quic_dispatcher.h new file mode 100644 index 00000000000..1f666742e3f --- /dev/null +++ b/chromium/net/quic/quic_dispatcher.h @@ -0,0 +1,230 @@ +// 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. +// +// A server side dispatcher which dispatches a given client's data to their +// stream. + +#ifndef NET_QUIC_QUIC_DISPATCHER_H_ +#define NET_QUIC_QUIC_DISPATCHER_H_ + +#include <list> + +#include "base/basictypes.h" +#include "base/containers/hash_tables.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/ip_endpoint.h" +#include "net/base/linked_hash_map.h" +#include "net/quic/quic_blocked_writer_interface.h" +#include "net/quic/quic_connection_helper.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_server_session.h" +#include "net/quic/quic_time_wait_list_manager.h" + +#if defined(COMPILER_GCC) +namespace BASE_HASH_NAMESPACE { +template <> +struct hash<net::QuicBlockedWriterInterface*> { + std::size_t operator()(const net::QuicBlockedWriterInterface* ptr) const { + return hash<size_t>()(reinterpret_cast<size_t>(ptr)); + } +}; +} +#endif + +namespace net { + +class QuicConfig; +class QuicCryptoServerConfig; +class QuicPacketWriterWrapper; +class QuicSession; +class UDPServerSocket; + +namespace test { +class QuicDispatcherPeer; +} // namespace test + +class DeleteSessionsAlarm; + +class QuicDispatcher : public QuicServerSessionVisitor { + public: + // Ideally we'd have a linked_hash_set: the boolean is unused. + typedef linked_hash_map<QuicBlockedWriterInterface*, bool> WriteBlockedList; + + // Due to the way delete_sessions_closure_ is registered, the Dispatcher + // must live until epoll_server Shutdown. |supported_versions| specifies the + // list of supported QUIC versions. + QuicDispatcher(const QuicConfig& config, + const QuicCryptoServerConfig& crypto_config, + const QuicVersionVector& supported_versions, + QuicConnectionHelperInterface* helper); + + virtual ~QuicDispatcher(); + + // Takes ownership of the packet writer + virtual void Initialize(QuicPacketWriter* writer); + + // Process the incoming packet by creating a new session, passing it to + // an existing session, or passing it to the TimeWaitListManager. + virtual void ProcessPacket(const IPEndPoint& server_address, + const IPEndPoint& client_address, + const QuicEncryptedPacket& packet); + + // Called when the socket becomes writable to allow queued writes to happen. + virtual void OnCanWrite(); + + // Returns true if there's anything in the blocked writer list. + virtual bool HasPendingWrites() const; + + // Sends ConnectionClose frames to all connected clients. + void Shutdown(); + + // QuicServerSessionVisitor interface implementation: + // Ensure that the closed connection is cleaned up asynchronously. + virtual void OnConnectionClosed(QuicConnectionId connection_id, + QuicErrorCode error) OVERRIDE; + + // Queues the blocked writer for later resumption. + virtual void OnWriteBlocked(QuicBlockedWriterInterface* writer) OVERRIDE; + + typedef base::hash_map<QuicConnectionId, QuicSession*> SessionMap; + + // Deletes all sessions on the closed session list and clears the list. + void DeleteSessions(); + + const SessionMap& session_map() const { return session_map_; } + + WriteBlockedList* write_blocked_list() { return &write_blocked_list_; } + + protected: + virtual QuicSession* CreateQuicSession(QuicConnectionId connection_id, + const IPEndPoint& server_address, + const IPEndPoint& client_address); + + virtual QuicConnection* CreateQuicConnection( + QuicConnectionId connection_id, + const IPEndPoint& server_address, + const IPEndPoint& client_address); + + // Called by |framer_visitor_| when the public header has been parsed. + virtual bool OnUnauthenticatedPublicHeader( + const QuicPacketPublicHeader& header); + + // Create and return the time wait list manager for this dispatcher, which + // will be owned by the dispatcher as time_wait_list_manager_ + virtual QuicTimeWaitListManager* CreateQuicTimeWaitListManager(); + + // Replaces the packet writer with |writer|. Takes ownership of |writer|. + void set_writer(QuicPacketWriter* writer) { + writer_.reset(writer); + } + + QuicTimeWaitListManager* time_wait_list_manager() { + return time_wait_list_manager_.get(); + } + + const QuicVersionVector& supported_versions() const { + return supported_versions_; + } + + const QuicVersionVector& supported_versions_no_flow_control() const { + return supported_versions_no_flow_control_; + } + + const QuicVersionVector& supported_versions_no_connection_flow_control() + const { + return supported_versions_no_connection_flow_control_; + } + + const IPEndPoint& current_server_address() { + return current_server_address_; + } + const IPEndPoint& current_client_address() { + return current_client_address_; + } + const QuicEncryptedPacket& current_packet() { + return *current_packet_; + } + + const QuicConfig& config() const { return config_; } + + const QuicCryptoServerConfig& crypto_config() const { return crypto_config_; } + + QuicFramer* framer() { return &framer_; } + + QuicConnectionHelperInterface* helper() { return helper_; } + + QuicPacketWriter* writer() { return writer_.get(); } + + private: + class QuicFramerVisitor; + friend class net::test::QuicDispatcherPeer; + + // Called by |framer_visitor_| when the private header has been parsed + // of a data packet that is destined for the time wait manager. + void OnUnauthenticatedHeader(const QuicPacketHeader& header); + + // Removes the session from the session map and write blocked list, and + // adds the ConnectionId to the time-wait list. + void CleanUpSession(SessionMap::iterator it); + + bool HandlePacketForTimeWait(const QuicPacketPublicHeader& header); + + const QuicConfig& config_; + + const QuicCryptoServerConfig& crypto_config_; + + // The list of connections waiting to write. + WriteBlockedList write_blocked_list_; + + SessionMap session_map_; + + // Entity that manages connection_ids in time wait state. + scoped_ptr<QuicTimeWaitListManager> time_wait_list_manager_; + + // The helper used for all connections. Owned by the server. + QuicConnectionHelperInterface* helper_; + + // An alarm which deletes closed sessions. + scoped_ptr<QuicAlarm> delete_sessions_alarm_; + + // The list of closed but not-yet-deleted sessions. + std::list<QuicSession*> closed_session_list_; + + // The writer to write to the socket with. + scoped_ptr<QuicPacketWriter> writer_; + + // This vector contains QUIC versions which we currently support. + // This should be ordered such that the highest supported version is the first + // element, with subsequent elements in descending order (versions can be + // skipped as necessary). + const QuicVersionVector supported_versions_; + + // Versions which do not support flow control (introduced in QUIC_VERSION_17). + // This is used to construct new QuicConnections when flow control is disabled + // via flag. + // TODO(rjshade): Remove this when + // FLAGS_enable_quic_stream_flow_control_2 is removed. + QuicVersionVector supported_versions_no_flow_control_; + // Versions which do not support *connection* flow control (introduced in + // QUIC_VERSION_19). + // This is used to construct new QuicConnections when connection flow control + // is disabled via flag. + // TODO(rjshade): Remove this when + // FLAGS_enable_quic_connection_flow_control_2 is removed. + QuicVersionVector supported_versions_no_connection_flow_control_; + + // Information about the packet currently being handled. + IPEndPoint current_client_address_; + IPEndPoint current_server_address_; + const QuicEncryptedPacket* current_packet_; + + QuicFramer framer_; + scoped_ptr<QuicFramerVisitor> framer_visitor_; + + DISALLOW_COPY_AND_ASSIGN(QuicDispatcher); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_DISPATCHER_H_ diff --git a/chromium/net/quic/quic_end_to_end_unittest.cc b/chromium/net/quic/quic_end_to_end_unittest.cc index 90c670bfbeb..89622b0483d 100644 --- a/chromium/net/quic/quic_end_to_end_unittest.cc +++ b/chromium/net/quic/quic_end_to_end_unittest.cc @@ -17,20 +17,21 @@ #include "net/http/http_network_session.h" #include "net/http/http_network_transaction.h" #include "net/http/http_server_properties_impl.h" -#include "net/http/http_transaction_unittest.h" +#include "net/http/http_transaction_test_util.h" #include "net/http/transport_security_state.h" #include "net/proxy/proxy_service.h" +#include "net/quic/test_tools/quic_test_utils.h" #include "net/ssl/ssl_config_service_defaults.h" #include "net/tools/quic/quic_in_memory_cache.h" +#include "net/tools/quic/quic_server.h" #include "net/tools/quic/test_tools/quic_in_memory_cache_peer.h" #include "net/tools/quic/test_tools/server_thread.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" -extern int FLAGS_fake_packet_loss_percentage; - using base::StringPiece; using net::tools::QuicInMemoryCache; +using net::tools::QuicServer; using net::tools::test::QuicInMemoryCachePeer; using net::tools::test::ServerThread; @@ -48,14 +49,11 @@ class TestTransactionFactory : public HttpTransactionFactory { : session_(new HttpNetworkSession(params)) {} virtual ~TestTransactionFactory() { - FLAGS_fake_packet_loss_percentage = 0; } // HttpTransactionFactory methods virtual int CreateTransaction(RequestPriority priority, - scoped_ptr<HttpTransaction>* trans, - HttpTransactionDelegate* delegate) OVERRIDE { - EXPECT_TRUE(delegate == NULL); + scoped_ptr<HttpTransaction>* trans) OVERRIDE { trans->reset(new HttpNetworkTransaction(priority, session_)); return OK; } @@ -137,13 +135,20 @@ class QuicEndToEndTest : public PlatformTest { CHECK(net::ParseIPLiteralToNumber("127.0.0.1", &ip)); server_address_ = IPEndPoint(ip, 0); server_config_.SetDefaults(); - server_thread_.reset(new ServerThread(server_address_, server_config_, - QuicSupportedVersions(), - strike_register_no_startup_period_)); - server_thread_->Start(); - server_thread_->WaitForServerStartup(); + server_config_.SetInitialFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + server_config_.SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + server_config_.SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + server_thread_.reset(new ServerThread( + new QuicServer(server_config_, QuicSupportedVersions()), + server_address_, + strike_register_no_startup_period_)); + server_thread_->Initialize(); server_address_ = IPEndPoint(server_address_.address(), server_thread_->GetPort()); + server_thread_->Start(); server_started_ = true; } diff --git a/chromium/net/quic/quic_fec_group.cc b/chromium/net/quic/quic_fec_group.cc index 4be48100e76..344f21679ec 100644 --- a/chromium/net/quic/quic_fec_group.cc +++ b/chromium/net/quic/quic_fec_group.cc @@ -6,6 +6,7 @@ #include <limits> +#include "base/basictypes.h" #include "base/logging.h" using base::StringPiece; @@ -22,12 +23,13 @@ QuicFecGroup::QuicFecGroup() : min_protected_packet_(kNoSequenceNumber), max_protected_packet_(kNoSequenceNumber), payload_parity_len_(0), - entropy_parity_(false) { + effective_encryption_level_(NUM_ENCRYPTION_LEVELS) { } QuicFecGroup::~QuicFecGroup() {} -bool QuicFecGroup::Update(const QuicPacketHeader& header, +bool QuicFecGroup::Update(EncryptionLevel encryption_level, + const QuicPacketHeader& header, StringPiece decrypted_payload) { if (received_packets_.count(header.packet_sequence_number) != 0) { return false; @@ -40,34 +42,39 @@ bool QuicFecGroup::Update(const QuicPacketHeader& header, << header.packet_sequence_number; return false; } - if (!UpdateParity(decrypted_payload, header.entropy_flag)) { + if (!UpdateParity(decrypted_payload)) { return false; } received_packets_.insert(header.packet_sequence_number); + if (encryption_level < effective_encryption_level_) { + effective_encryption_level_ = encryption_level; + } return true; } bool QuicFecGroup::UpdateFec( + EncryptionLevel encryption_level, QuicPacketSequenceNumber fec_packet_sequence_number, - bool fec_packet_entropy, const QuicFecData& fec) { if (min_protected_packet_ != kNoSequenceNumber) { return false; } SequenceNumberSet::const_iterator it = received_packets_.begin(); while (it != received_packets_.end()) { - if ((*it < fec.fec_group) || - (*it >= fec_packet_sequence_number)) { + if ((*it < fec.fec_group) || (*it >= fec_packet_sequence_number)) { DLOG(ERROR) << "FEC group does not cover received packet: " << *it; return false; } ++it; } - if (!UpdateParity(fec.redundancy, fec_packet_entropy)) { + if (!UpdateParity(fec.redundancy)) { return false; } min_protected_packet_ = fec.fec_group; max_protected_packet_ = fec_packet_sequence_number - 1; + if (encryption_level < effective_encryption_level_) { + effective_encryption_level_ = encryption_level; + } return true; } @@ -109,7 +116,7 @@ size_t QuicFecGroup::Revive(QuicPacketHeader* header, } header->packet_sequence_number = missing; - header->entropy_flag = entropy_parity_; + header->entropy_flag = false; // Unknown entropy. received_packets_.insert(missing); return payload_parity_len_; @@ -119,12 +126,12 @@ bool QuicFecGroup::ProtectsPacketsBefore(QuicPacketSequenceNumber num) const { if (max_protected_packet_ != kNoSequenceNumber) { return max_protected_packet_ < num; } - // Since we might not yet have recevied the FEC packet, we must check + // Since we might not yet have received the FEC packet, we must check // the packets we have received. return *received_packets_.begin() < num; } -bool QuicFecGroup::UpdateParity(StringPiece payload, bool entropy) { +bool QuicFecGroup::UpdateParity(StringPiece payload) { DCHECK_LE(payload.size(), kMaxPacketSize); if (payload.size() > kMaxPacketSize) { DLOG(ERROR) << "Illegal payload size: " << payload.size(); @@ -143,7 +150,6 @@ bool QuicFecGroup::UpdateParity(StringPiece payload, bool entropy) { memset(payload_parity_ + payload.size(), 0, kMaxPacketSize - payload.size()); } - entropy_parity_ = entropy; return true; } // Update the parity by XORing in the data (padding with 0s if necessary). @@ -151,8 +157,6 @@ bool QuicFecGroup::UpdateParity(StringPiece payload, bool entropy) { uint8 byte = i < payload.size() ? payload[i] : 0x00; payload_parity_[i] ^= byte; } - // xor of boolean values. - entropy_parity_ = (entropy_parity_ != entropy); return true; } diff --git a/chromium/net/quic/quic_fec_group.h b/chromium/net/quic/quic_fec_group.h index d905d03236e..718d09f83ad 100644 --- a/chromium/net/quic/quic_fec_group.h +++ b/chromium/net/quic/quic_fec_group.h @@ -9,8 +9,6 @@ #ifndef NET_QUIC_QUIC_FEC_GROUP_H_ #define NET_QUIC_QUIC_FEC_GROUP_H_ -#include <set> - #include "base/strings/string_piece.h" #include "net/quic/quic_protocol.h" @@ -21,17 +19,19 @@ class NET_EXPORT_PRIVATE QuicFecGroup { QuicFecGroup(); ~QuicFecGroup(); - // 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, + // Updates the FEC group based on the delivery of a data packet decrypted at + // |encryption_level|. Returns false if this packet has already been seen, + // true otherwise. + bool Update(EncryptionLevel encryption_level, + const QuicPacketHeader& header, base::StringPiece decrypted_payload); - // Updates the FEC group based on the delivery of an FEC packet. - // Returns false if this packet has already been seen or if it does - // not claim to protect all the packets previously seen in this group. - // |fec_packet_entropy|: XOR of entropy of all packets in the fec group. - bool UpdateFec(QuicPacketSequenceNumber fec_packet_sequence_number, - bool fec_packet_entropy, + // Updates the FEC group based on the delivery of an FEC packet decrypted at + // |encryption_level|. Returns false if this packet has already been seen or + // if it does not claim to protect all the packets previously seen in this + // group. + bool UpdateFec(EncryptionLevel encryption_level, + QuicPacketSequenceNumber fec_packet_sequence_number, const QuicFecData& fec); // Returns true if a packet can be revived from this FEC group. @@ -57,10 +57,6 @@ class NET_EXPORT_PRIVATE QuicFecGroup { return base::StringPiece(payload_parity_, payload_parity_len_); } - bool entropy_parity() const { - return entropy_parity_; - } - QuicPacketSequenceNumber min_protected_packet() const { return min_protected_packet_; } @@ -69,8 +65,13 @@ class NET_EXPORT_PRIVATE QuicFecGroup { return received_packets_.size(); } + // Returns the effective encryption level of the FEC group. + EncryptionLevel effective_encryption_level() const { + return effective_encryption_level_; + } + private: - bool UpdateParity(base::StringPiece payload, bool entropy); + bool UpdateParity(base::StringPiece payload); // Returns the number of missing packets, or size_t max if the number // of missing packets is not known. size_t NumMissingPackets() const; @@ -88,7 +89,9 @@ class NET_EXPORT_PRIVATE QuicFecGroup { // The cumulative parity calculation of all received packets. char payload_parity_[kMaxPacketSize]; size_t payload_parity_len_; - bool entropy_parity_; + // The effective encryption level, which is the lowest encryption level of + // the data and FEC in the group. + EncryptionLevel effective_encryption_level_; DISALLOW_COPY_AND_ASSIGN(QuicFecGroup); }; diff --git a/chromium/net/quic/quic_fec_group_test.cc b/chromium/net/quic/quic_fec_group_test.cc index 3de40b1bc7c..b9420dd11d5 100644 --- a/chromium/net/quic/quic_fec_group_test.cc +++ b/chromium/net/quic/quic_fec_group_test.cc @@ -2,12 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "net/quic/quic_fec_group.h" + #include <algorithm> #include <vector> +#include "base/basictypes.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" -#include "net/quic/quic_fec_group.h" #include "testing/gmock/include/gmock/gmock.h" using ::testing::_; @@ -35,8 +37,6 @@ const bool kEntropyFlag[] = { true, }; -const bool kTestFecPacketEntropy = false; - } // namespace class QuicFecGroupTest : public ::testing::Test { @@ -44,7 +44,6 @@ class QuicFecGroupTest : public ::testing::Test { void RunTest(size_t num_packets, size_t lost_packet, bool out_of_order) { size_t max_len = strlen(kData[0]); scoped_ptr<char[]> redundancy(new char[max_len]); - bool entropy_redundancy = false; for (size_t packet = 0; packet < num_packets; ++packet) { for (size_t i = 0; i < max_len; i++) { if (packet == 0) { @@ -56,7 +55,6 @@ class QuicFecGroupTest : public ::testing::Test { uint8 byte = i > strlen(kData[packet]) ? 0x00 : kData[packet][i]; redundancy[i] = redundancy[i] ^ byte; } - entropy_redundancy = (entropy_redundancy != kEntropyFlag[packet]); } QuicFecGroup group; @@ -71,17 +69,19 @@ class QuicFecGroupTest : public ::testing::Test { QuicFecData fec; fec.fec_group = 0; fec.redundancy = StringPiece(redundancy.get(), strlen(kData[0])); - ASSERT_TRUE(group.UpdateFec(num_packets, entropy_redundancy, fec)); + ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, num_packets, + fec)); } else { QuicPacketHeader header; header.packet_sequence_number = packet; header.entropy_flag = kEntropyFlag[packet]; - ASSERT_TRUE(group.Update(header, kData[packet])); + ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, + kData[packet])); } ASSERT_TRUE(group.CanRevive() == (packet == num_packets - 1)); } } else { - // Update the FEC state for each non-lost packet. + // Update the FEC state for each non-lost packet. for (size_t packet = 0; packet < num_packets; packet++) { if (packet == lost_packet) { continue; @@ -90,7 +90,8 @@ class QuicFecGroupTest : public ::testing::Test { QuicPacketHeader header; header.packet_sequence_number = packet; header.entropy_flag = kEntropyFlag[packet]; - ASSERT_TRUE(group.Update(header, kData[packet])); + ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, + kData[packet])); ASSERT_FALSE(group.CanRevive()); } @@ -100,7 +101,8 @@ class QuicFecGroupTest : public ::testing::Test { fec.fec_group = 0; fec.redundancy = StringPiece(redundancy.get(), strlen(kData[0])); - ASSERT_TRUE(group.UpdateFec(num_packets, entropy_redundancy, fec)); + ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, num_packets, + fec)); } QuicPacketHeader header; char recovered[kMaxPacketSize]; @@ -112,7 +114,8 @@ class QuicFecGroupTest : public ::testing::Test { EXPECT_EQ(lost_packet, header.packet_sequence_number) << "Failed to revive packet " << lost_packet << " out of " << num_packets; - EXPECT_EQ(kEntropyFlag[lost_packet], header.entropy_flag); + // Revived packets have an unknown entropy. + EXPECT_FALSE(header.entropy_flag); ASSERT_GE(len, strlen(kData[lost_packet])) << "Incorrect length"; for (size_t i = 0; i < strlen(kData[lost_packet]); i++) { EXPECT_EQ(kData[lost_packet][i], recovered[i]); @@ -150,14 +153,14 @@ TEST_F(QuicFecGroupTest, UpdateFecIfReceivedPacketIsNotCovered) { QuicPacketHeader header; header.packet_sequence_number = 3; - group.Update(header, data1); + group.Update(ENCRYPTION_FORWARD_SECURE, header, data1); QuicFecData fec; fec.fec_group = 1; fec.redundancy = redundancy; header.packet_sequence_number = 2; - ASSERT_FALSE(group.UpdateFec(2, kTestFecPacketEntropy, fec)); + ASSERT_FALSE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, 2, fec)); } TEST_F(QuicFecGroupTest, ProtectsPacketsBefore) { @@ -165,7 +168,7 @@ TEST_F(QuicFecGroupTest, ProtectsPacketsBefore) { header.packet_sequence_number = 3; QuicFecGroup group; - ASSERT_TRUE(group.Update(header, kData[0])); + ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, kData[0])); EXPECT_FALSE(group.ProtectsPacketsBefore(1)); EXPECT_FALSE(group.ProtectsPacketsBefore(2)); @@ -180,13 +183,13 @@ TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithSeveralPackets) { header.packet_sequence_number = 3; QuicFecGroup group; - ASSERT_TRUE(group.Update(header, kData[0])); + ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, kData[0])); header.packet_sequence_number = 7; - ASSERT_TRUE(group.Update(header, kData[0])); + ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, kData[0])); header.packet_sequence_number = 5; - ASSERT_TRUE(group.Update(header, kData[0])); + ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, kData[0])); EXPECT_FALSE(group.ProtectsPacketsBefore(1)); EXPECT_FALSE(group.ProtectsPacketsBefore(2)); @@ -206,7 +209,7 @@ TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithFecData) { fec.redundancy = kData[0]; QuicFecGroup group; - ASSERT_TRUE(group.UpdateFec(3, kTestFecPacketEntropy, fec)); + ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, 3, fec)); EXPECT_FALSE(group.ProtectsPacketsBefore(1)); EXPECT_FALSE(group.ProtectsPacketsBefore(2)); @@ -216,4 +219,24 @@ TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithFecData) { EXPECT_TRUE(group.ProtectsPacketsBefore(50)); } +TEST_F(QuicFecGroupTest, EffectiveEncryptionLevel) { + QuicFecGroup group; + EXPECT_EQ(NUM_ENCRYPTION_LEVELS, group.effective_encryption_level()); + + QuicPacketHeader header; + header.packet_sequence_number = 5; + ASSERT_TRUE(group.Update(ENCRYPTION_INITIAL, header, kData[0])); + EXPECT_EQ(ENCRYPTION_INITIAL, group.effective_encryption_level()); + + QuicFecData fec; + fec.fec_group = 0; + fec.redundancy = kData[0]; + ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, 7, fec)); + EXPECT_EQ(ENCRYPTION_INITIAL, group.effective_encryption_level()); + + header.packet_sequence_number = 3; + ASSERT_TRUE(group.Update(ENCRYPTION_NONE, header, kData[0])); + EXPECT_EQ(ENCRYPTION_NONE, group.effective_encryption_level()); +} + } // namespace net diff --git a/chromium/net/quic/quic_flags.cc b/chromium/net/quic/quic_flags.cc new file mode 100644 index 00000000000..10eaeb36847 --- /dev/null +++ b/chromium/net/quic/quic_flags.cc @@ -0,0 +1,48 @@ +// 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/quic_flags.h" + +// TODO(rtenneti): Remove this. +// Do not flip this flag until the flakiness of the +// net/tools/quic/end_to_end_test is fixed. +// If true, then QUIC connections will track the retransmission history of a +// packet so that an ack of a previous transmission will ack the data of all +// other transmissions. +bool FLAGS_track_retransmission_history = false; + +// Do not remove this flag until the Finch-trials described in b/11706275 +// are complete. +// If true, QUIC connections will support the use of a pacing algorithm when +// sending packets, in an attempt to reduce packet loss. The client must also +// request pacing for the server to enable it. +bool FLAGS_enable_quic_pacing = true; + +// Do not remove this flag until b/11792453 is marked as Fixed. +// If true, turns on stream flow control in QUIC. +// If this is disabled, all in flight QUIC connections talking QUIC_VERSION_17 +// or higher will timeout. New connections will be fine. +// If disabling this flag, also disable enable_quic_connection_flow_control_2. +bool FLAGS_enable_quic_stream_flow_control_2 = true; + +// Do not remove this flag until b/11792453 is marked as Fixed. +// If true, turns on connection level flow control in QUIC. +// If this is disabled, all in flight QUIC connections talking QUIC_VERSION_19 +// or higher will timeout. New connections will be fine. +bool FLAGS_enable_quic_connection_flow_control_2 = true; + +bool FLAGS_quic_allow_oversized_packets_for_test = false; + +// When true, the use time based loss detection instead of nack. +bool FLAGS_quic_use_time_loss_detection = false; + +// If true, allow peer port migration of established QUIC connections. +bool FLAGS_quic_allow_port_migration = true; + +// If true, it will return as soon as an error is detected while validating +// CHLO. +bool FLAGS_use_early_return_when_verifying_chlo = true; + +// If true, QUIC crypto reject message will include the reasons for rejection. +bool FLAGS_send_quic_crypto_reject_reason = false; diff --git a/chromium/net/quic/quic_flags.h b/chromium/net/quic/quic_flags.h new file mode 100644 index 00000000000..c650738cfe7 --- /dev/null +++ b/chromium/net/quic/quic_flags.h @@ -0,0 +1,20 @@ +// 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_QUIC_FLAGS_H_ +#define NET_QUIC_QUIC_FLAGS_H_ + +#include "net/base/net_export.h" + +NET_EXPORT_PRIVATE extern bool FLAGS_track_retransmission_history; +NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_pacing; +NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_stream_flow_control_2; +NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_connection_flow_control_2; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_oversized_packets_for_test; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_time_loss_detection; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_port_migration; +NET_EXPORT_PRIVATE extern bool FLAGS_use_early_return_when_verifying_chlo; +NET_EXPORT_PRIVATE extern bool FLAGS_send_quic_crypto_reject_reason; + +#endif // NET_QUIC_QUIC_FLAGS_H_ diff --git a/chromium/net/quic/quic_flow_controller.cc b/chromium/net/quic/quic_flow_controller.cc new file mode 100644 index 00000000000..107b4d9510a --- /dev/null +++ b/chromium/net/quic/quic_flow_controller.cc @@ -0,0 +1,203 @@ +// 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/quic_flow_controller.h" + +#include "base/basictypes.h" +#include "net/quic/quic_connection.h" +#include "net/quic/quic_flags.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +#define ENDPOINT (is_server_ ? "Server: " : " Client: ") + +QuicFlowController::QuicFlowController(QuicConnection* connection, + QuicStreamId id, + bool is_server, + uint64 send_window_offset, + uint64 receive_window_offset, + uint64 max_receive_window) + : connection_(connection), + id_(id), + is_enabled_(true), + is_server_(is_server), + bytes_consumed_(0), + highest_received_byte_offset_(0), + bytes_sent_(0), + send_window_offset_(send_window_offset), + receive_window_offset_(receive_window_offset), + max_receive_window_(max_receive_window), + last_blocked_send_window_offset_(0) { + DVLOG(1) << ENDPOINT << "Created flow controller for stream " << id_ + << ", setting initial receive window offset to: " + << receive_window_offset_ + << ", max receive window to: " + << max_receive_window_ + << ", setting send window offset to: " << send_window_offset_; + if (connection_->version() < QUIC_VERSION_17) { + DVLOG(1) << ENDPOINT << "Disabling QuicFlowController for stream " << id_ + << ", QUIC version " << connection_->version(); + Disable(); + } +} + +void QuicFlowController::AddBytesConsumed(uint64 bytes_consumed) { + if (!IsEnabled()) { + return; + } + + bytes_consumed_ += bytes_consumed; + DVLOG(1) << ENDPOINT << "Stream " << id_ << " consumed: " << bytes_consumed_; + + MaybeSendWindowUpdate(); +} + +bool QuicFlowController::UpdateHighestReceivedOffset(uint64 new_offset) { + if (!IsEnabled()) { + return false; + } + + // Only update if offset has increased. + if (new_offset <= highest_received_byte_offset_) { + return false; + } + + DVLOG(1) << ENDPOINT << "Stream " << id_ + << " highest byte offset increased from: " + << highest_received_byte_offset_ << " to " << new_offset; + highest_received_byte_offset_ = new_offset; + return true; +} + +void QuicFlowController::AddBytesSent(uint64 bytes_sent) { + if (!IsEnabled()) { + return; + } + + if (bytes_sent_ + bytes_sent > send_window_offset_) { + LOG(DFATAL) << ENDPOINT << "Stream " << id_ << " Trying to send an extra " + << bytes_sent << " bytes, when bytes_sent = " << bytes_sent_ + << ", and send_window_offset_ = " << send_window_offset_; + bytes_sent_ = send_window_offset_; + + // This is an error on our side, close the connection as soon as possible. + connection_->SendConnectionClose(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA); + return; + } + + bytes_sent_ += bytes_sent; + DVLOG(1) << ENDPOINT << "Stream " << id_ << " sent: " << bytes_sent_; +} + +bool QuicFlowController::FlowControlViolation() { + if (!IsEnabled()) { + return false; + } + + if (highest_received_byte_offset_ > receive_window_offset_) { + LOG(ERROR) << ENDPOINT << "Flow control violation on stream " + << id_ << ", receive window offset: " + << receive_window_offset_ + << ", highest received byte offset: " + << highest_received_byte_offset_; + return true; + } + return false; +} + +void QuicFlowController::MaybeSendWindowUpdate() { + if (!IsEnabled()) { + return; + } + + // Send WindowUpdate to increase receive window if + // (receive window offset - consumed bytes) < (max window / 2). + // This is behaviour copied from SPDY. + DCHECK_LT(bytes_consumed_, receive_window_offset_); + size_t consumed_window = receive_window_offset_ - bytes_consumed_; + size_t threshold = (max_receive_window_ / 2); + + if (consumed_window < threshold) { + // Update our receive window. + receive_window_offset_ += (max_receive_window_ - consumed_window); + + DVLOG(1) << ENDPOINT << "Sending WindowUpdate frame for stream " << id_ + << ", consumed bytes: " << bytes_consumed_ + << ", consumed window: " << consumed_window + << ", and threshold: " << threshold + << ", and max recvw: " << max_receive_window_ + << ". New receive window offset is: " << receive_window_offset_; + + // Inform the peer of our new receive window. + connection_->SendWindowUpdate(id_, receive_window_offset_); + } +} + +void QuicFlowController::MaybeSendBlocked() { + if (!IsEnabled()) { + return; + } + + if (SendWindowSize() == 0 && + last_blocked_send_window_offset_ < send_window_offset_) { + DVLOG(1) << ENDPOINT << "Stream " << id_ << " is flow control blocked. " + << "Send window: " << SendWindowSize() + << ", bytes sent: " << bytes_sent_ + << ", send limit: " << send_window_offset_; + // The entire send_window has been consumed, we are now flow control + // blocked. + connection_->SendBlocked(id_); + + // Keep track of when we last sent a BLOCKED frame so that we only send one + // at a given send offset. + last_blocked_send_window_offset_ = send_window_offset_; + } +} + +bool QuicFlowController::UpdateSendWindowOffset(uint64 new_send_window_offset) { + if (!IsEnabled()) { + return false; + } + + // Only update if send window has increased. + if (new_send_window_offset <= send_window_offset_) { + return false; + } + + DVLOG(1) << ENDPOINT << "UpdateSendWindowOffset for stream " << id_ + << " with new offset " << new_send_window_offset + << " , current offset: " << send_window_offset_; + + send_window_offset_ = new_send_window_offset; + return true; +} + +void QuicFlowController::Disable() { + is_enabled_ = false; +} + +bool QuicFlowController::IsEnabled() const { + bool connection_flow_control_enabled = + (id_ == kConnectionLevelId && + FLAGS_enable_quic_connection_flow_control_2); + bool stream_flow_control_enabled = + (id_ != kConnectionLevelId && + FLAGS_enable_quic_stream_flow_control_2); + return (connection_flow_control_enabled || stream_flow_control_enabled) && + is_enabled_; +} + +bool QuicFlowController::IsBlocked() const { + return IsEnabled() && SendWindowSize() == 0; +} + +uint64 QuicFlowController::SendWindowSize() const { + if (bytes_sent_ > send_window_offset_) { + return 0; + } + return send_window_offset_ - bytes_sent_; +} + +} // namespace net diff --git a/chromium/net/quic/quic_flow_controller.h b/chromium/net/quic/quic_flow_controller.h new file mode 100644 index 00000000000..e5f5494b7f1 --- /dev/null +++ b/chromium/net/quic/quic_flow_controller.h @@ -0,0 +1,130 @@ +// 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_QUIC_FLOW_CONTROLLER_H_ +#define NET_QUIC_QUIC_FLOW_CONTROLLER_H_ + +#include "base/basictypes.h" +#include "net/base/net_export.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +namespace test { +class QuicFlowControllerPeer; +} // namespace test + +class QuicConnection; + +const QuicStreamId kConnectionLevelId = 0; + +// QuicFlowController allows a QUIC stream or connection to perform flow +// control. The stream/connection owns a QuicFlowController which keeps track of +// bytes sent/received, can tell the owner if it is flow control blocked, and +// can send WINDOW_UPDATE or BLOCKED frames when needed. +class NET_EXPORT_PRIVATE QuicFlowController { + public: + QuicFlowController(QuicConnection* connection, + QuicStreamId id, + bool is_server, + uint64 send_window_offset, + uint64 receive_window_offset, + uint64 max_receive_window); + ~QuicFlowController() {} + + // Called when we see a new highest received byte offset from the peer, either + // via a data frame or a RST. + // Returns true if this call changes highest_received_byte_offset_, and false + // in the case where |new_offset| is <= highest_received_byte_offset_. + bool UpdateHighestReceivedOffset(uint64 new_offset); + + // Called when bytes received from the peer are consumed locally. This may + // trigger the sending of a WINDOW_UPDATE frame using |connection|. + void AddBytesConsumed(uint64 bytes_consumed); + + // Called when bytes are sent to the peer. + void AddBytesSent(uint64 bytes_sent); + + // Set a new send window offset. + // Returns true if this changes send_window_offset_, and false in the case + // where |new_send_window| is <= send_window_offset_. + bool UpdateSendWindowOffset(uint64 new_send_window_offset); + + // Returns the current available send window. + uint64 SendWindowSize() const; + + // Send a BLOCKED frame if appropriate. + void MaybeSendBlocked(); + + // Disable flow control. + void Disable(); + + // Returns true if flow control is enabled. + bool IsEnabled() const; + + // Returns true if flow control send limits have been reached. + bool IsBlocked() const; + + // Returns true if flow control receive limits have been violated by the peer. + bool FlowControlViolation(); + + uint64 bytes_consumed() const { return bytes_consumed_; } + + uint64 highest_received_byte_offset() const { + return highest_received_byte_offset_; + } + + private: + friend class test::QuicFlowControllerPeer; + + // Send a WINDOW_UPDATE frame if appropriate. + void MaybeSendWindowUpdate(); + + // The parent connection, used to send connection close on flow control + // violation, and WINDOW_UPDATE and BLOCKED frames when appropriate. + // Not owned. + QuicConnection* connection_; + + // ID of stream this flow controller belongs to. This can be 0 if this is a + // connection level flow controller. + QuicStreamId id_; + + // True if flow control is enabled. + bool is_enabled_; + + // True if this is owned by a server. + bool is_server_; + + // Track number of bytes received from the peer, which have been consumed + // locally. + uint64 bytes_consumed_; + + // The highest byte offset we have seen from the peer. This could be the + // highest offset in a data frame, or a final value in a RST. + uint64 highest_received_byte_offset_; + + // Tracks number of bytes sent to the peer. + uint64 bytes_sent_; + + // The absolute offset in the outgoing byte stream. If this offset is reached + // then we become flow control blocked until we receive a WINDOW_UPDATE. + uint64 send_window_offset_; + + // The absolute offset in the incoming byte stream. The peer should never send + // us bytes which are beyond this offset. + uint64 receive_window_offset_; + + // Largest size the receive window can grow to. + uint64 max_receive_window_; + + // Keep track of the last time we sent a BLOCKED frame. We should only send + // another when the number of bytes we have sent has changed. + uint64 last_blocked_send_window_offset_; + + DISALLOW_COPY_AND_ASSIGN(QuicFlowController); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_FLOW_CONTROLLER_H_ diff --git a/chromium/net/quic/quic_flow_controller_test.cc b/chromium/net/quic/quic_flow_controller_test.cc new file mode 100644 index 00000000000..26781acb65f --- /dev/null +++ b/chromium/net/quic/quic_flow_controller_test.cc @@ -0,0 +1,224 @@ +// 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/quic_flow_controller.h" + +#include "base/strings/stringprintf.h" +#include "net/quic/quic_flags.h" +#include "net/quic/quic_utils.h" +#include "net/quic/test_tools/quic_connection_peer.h" +#include "net/quic/test_tools/quic_flow_controller_peer.h" +#include "net/quic/test_tools/quic_test_utils.h" +#include "net/test/gtest_util.h" +#include "testing/gmock/include/gmock/gmock.h" + +using base::StringPrintf; + +namespace net { +namespace test { + +using ::testing::_; + +class QuicFlowControllerTest : public ::testing::Test { + public: + QuicFlowControllerTest() + : stream_id_(1234), + send_window_(kInitialSessionFlowControlWindowForTest), + receive_window_(kInitialSessionFlowControlWindowForTest), + max_receive_window_(kInitialSessionFlowControlWindowForTest), + connection_(false), + old_flag_(&FLAGS_enable_quic_stream_flow_control_2, true) { + } + + void Initialize() { + flow_controller_.reset(new QuicFlowController( + &connection_, stream_id_, false, send_window_, + receive_window_, max_receive_window_)); + } + + protected: + QuicStreamId stream_id_; + uint64 send_window_; + uint64 receive_window_; + uint64 max_receive_window_; + scoped_ptr<QuicFlowController> flow_controller_; + MockConnection connection_; + ValueRestore<bool> old_flag_; +}; + +TEST_F(QuicFlowControllerTest, SendingBytes) { + Initialize(); + + EXPECT_TRUE(flow_controller_->IsEnabled()); + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(send_window_, flow_controller_->SendWindowSize()); + + // Send some bytes, but not enough to block. + flow_controller_->AddBytesSent(send_window_ / 2); + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_EQ(send_window_ / 2, flow_controller_->SendWindowSize()); + + // Send enough bytes to block. + flow_controller_->AddBytesSent(send_window_ / 2); + EXPECT_TRUE(flow_controller_->IsBlocked()); + EXPECT_EQ(0u, flow_controller_->SendWindowSize()); + + // BLOCKED frame should get sent. + EXPECT_CALL(connection_, SendBlocked(stream_id_)).Times(1); + flow_controller_->MaybeSendBlocked(); + + // Update the send window, and verify this has unblocked. + EXPECT_TRUE(flow_controller_->UpdateSendWindowOffset(2 * send_window_)); + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_EQ(send_window_, flow_controller_->SendWindowSize()); + + // Updating with a smaller offset doesn't change anything. + EXPECT_FALSE(flow_controller_->UpdateSendWindowOffset(send_window_ / 10)); + EXPECT_EQ(send_window_, flow_controller_->SendWindowSize()); + + // Try to send more bytes, violating flow control. + EXPECT_CALL(connection_, + SendConnectionClose(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA)); + EXPECT_DFATAL( + flow_controller_->AddBytesSent(send_window_ * 10), + StringPrintf("Trying to send an extra %d bytes", + static_cast<int>(send_window_ * 10))); + EXPECT_TRUE(flow_controller_->IsBlocked()); + EXPECT_EQ(0u, flow_controller_->SendWindowSize()); +} + +TEST_F(QuicFlowControllerTest, ReceivingBytes) { + Initialize(); + + EXPECT_TRUE(flow_controller_->IsEnabled()); + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + // Receive some bytes, updating highest received offset, but not enough to + // fill flow control receive window. + EXPECT_TRUE( + flow_controller_->UpdateHighestReceivedOffset(1 + receive_window_ / 2)); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ((receive_window_ / 2) - 1, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + // Consume enough bytes to send a WINDOW_UPDATE frame. + EXPECT_CALL(connection_, SendWindowUpdate(stream_id_, _)).Times(1); + + flow_controller_->AddBytesConsumed(1 + receive_window_ / 2); + + // Result is that once again we have a fully open receive window. + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); +} + +TEST_F(QuicFlowControllerTest, + DisabledWhenQuicVersionDoesNotSupportFlowControl) { + // Only support version 16: no flow control. + QuicConnectionPeer::SetSupportedVersions(&connection_, + SupportedVersions(QUIC_VERSION_16)); + + Initialize(); + + MockConnection connection(false); + + // Should not be enabled, and should not report as blocked. + EXPECT_FALSE(flow_controller_->IsEnabled()); + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + + // Any attempts to add/remove bytes should have no effect. + EXPECT_EQ(send_window_, flow_controller_->SendWindowSize()); + EXPECT_EQ(send_window_, + QuicFlowControllerPeer::SendWindowOffset(flow_controller_.get())); + EXPECT_EQ(receive_window_, QuicFlowControllerPeer::ReceiveWindowOffset( + flow_controller_.get())); + flow_controller_->AddBytesSent(123); + flow_controller_->AddBytesConsumed(456); + flow_controller_->UpdateHighestReceivedOffset(789); + EXPECT_EQ(send_window_, flow_controller_->SendWindowSize()); + EXPECT_EQ(send_window_, + QuicFlowControllerPeer::SendWindowOffset(flow_controller_.get())); + EXPECT_EQ(receive_window_, QuicFlowControllerPeer::ReceiveWindowOffset( + flow_controller_.get())); + + // Any attempt to change offset should have no effect. + EXPECT_EQ(send_window_, flow_controller_->SendWindowSize()); + EXPECT_EQ(send_window_, + QuicFlowControllerPeer::SendWindowOffset(flow_controller_.get())); + flow_controller_->UpdateSendWindowOffset(send_window_ + 12345); + EXPECT_EQ(send_window_, flow_controller_->SendWindowSize()); + EXPECT_EQ(send_window_, + QuicFlowControllerPeer::SendWindowOffset(flow_controller_.get())); + + // The connection should never send WINDOW_UPDATE or BLOCKED frames, even if + // the internal state implies that it should. + + // If the flow controller was enabled, then a send window size of 0 would + // trigger a BLOCKED frame to be sent. + EXPECT_EQ(send_window_, flow_controller_->SendWindowSize()); + EXPECT_CALL(connection_, SendBlocked(_)).Times(0); + flow_controller_->MaybeSendBlocked(); + + // If the flow controller was enabled, then a WINDOW_UPDATE would be sent if + // (receive window) < (max receive window / 2) + QuicFlowControllerPeer::SetReceiveWindowOffset(flow_controller_.get(), + max_receive_window_ / 10); + EXPECT_TRUE(QuicFlowControllerPeer::ReceiveWindowSize( + flow_controller_.get()) < (max_receive_window_ / 2)); + EXPECT_CALL(connection_, SendWindowUpdate(_, _)).Times(0); + flow_controller_->AddBytesConsumed(0); + + // Should not be enabled, and should not report as blocked. + EXPECT_FALSE(flow_controller_->IsEnabled()); + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); +} + +TEST_F(QuicFlowControllerTest, OnlySendBlockedFrameOncePerOffset) { + Initialize(); + + // Test that we don't send duplicate BLOCKED frames. We should only send one + // BLOCKED frame at a given send window offset. + EXPECT_TRUE(flow_controller_->IsEnabled()); + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(send_window_, flow_controller_->SendWindowSize()); + + // Send enough bytes to block. + flow_controller_->AddBytesSent(send_window_); + EXPECT_TRUE(flow_controller_->IsBlocked()); + EXPECT_EQ(0u, flow_controller_->SendWindowSize()); + + // Expect that 2 BLOCKED frames should get sent in total. + EXPECT_CALL(connection_, SendBlocked(stream_id_)).Times(2); + + // BLOCKED frame should get sent. + flow_controller_->MaybeSendBlocked(); + + // BLOCKED frame should not get sent again until our send offset changes. + flow_controller_->MaybeSendBlocked(); + flow_controller_->MaybeSendBlocked(); + flow_controller_->MaybeSendBlocked(); + flow_controller_->MaybeSendBlocked(); + flow_controller_->MaybeSendBlocked(); + + // Update the send window, then send enough bytes to block again. + EXPECT_TRUE(flow_controller_->UpdateSendWindowOffset(2 * send_window_)); + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_EQ(send_window_, flow_controller_->SendWindowSize()); + flow_controller_->AddBytesSent(send_window_); + EXPECT_TRUE(flow_controller_->IsBlocked()); + EXPECT_EQ(0u, flow_controller_->SendWindowSize()); + + // BLOCKED frame should get sent as send offset has changed. + flow_controller_->MaybeSendBlocked(); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_framer.cc b/chromium/net/quic/quic_framer.cc index cc02882ddc9..fd077bc21da 100644 --- a/chromium/net/quic/quic_framer.cc +++ b/chromium/net/quic/quic_framer.cc @@ -5,10 +5,16 @@ #include "net/quic/quic_framer.h" #include "base/containers/hash_tables.h" +#include "base/stl_util.h" +#include "net/quic/crypto/crypto_framer.h" +#include "net/quic/crypto/crypto_handshake_message.h" +#include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/quic_data_reader.h" #include "net/quic/quic_data_writer.h" +#include "net/quic/quic_flags.h" +#include "net/quic/quic_socket_address_coder.h" using base::StringPiece; using std::make_pair; @@ -32,8 +38,8 @@ const QuicPacketSequenceNumber k2ByteSequenceNumberMask = const QuicPacketSequenceNumber k1ByteSequenceNumberMask = GG_UINT64_C(0x00000000000000FF); -const QuicGuid k1ByteGuidMask = GG_UINT64_C(0x00000000000000FF); -const QuicGuid k4ByteGuidMask = GG_UINT64_C(0x00000000FFFFFFFF); +const QuicConnectionId k1ByteConnectionIdMask = GG_UINT64_C(0x00000000000000FF); +const QuicConnectionId k4ByteConnectionIdMask = GG_UINT64_C(0x00000000FFFFFFFF); // Number of bits the sequence number length bits are shifted from the right // edge of the public header. @@ -49,6 +55,8 @@ const uint8 kPublicHeaderSequenceNumberShift = 4; // ResetStream : 0b 00000001 (0x01) // ConnectionClose : 0b 00000010 (0x02) // GoAway : 0b 00000011 (0x03) +// WindowUpdate : 0b 00000100 (0x04) +// Blocked : 0b 00000101 (0x05) // // Special Frame Types encode both a Frame Type and corresponding flags // all in the Frame Type byte. Currently defined Special Frame Types are: @@ -108,8 +116,45 @@ QuicPacketSequenceNumber ClosestTo(QuicPacketSequenceNumber target, return (Delta(target, a) < Delta(target, b)) ? a : b; } +QuicSequenceNumberLength ReadSequenceNumberLength(uint8 flags) { + switch (flags & PACKET_FLAGS_6BYTE_SEQUENCE) { + case PACKET_FLAGS_6BYTE_SEQUENCE: + return PACKET_6BYTE_SEQUENCE_NUMBER; + case PACKET_FLAGS_4BYTE_SEQUENCE: + return PACKET_4BYTE_SEQUENCE_NUMBER; + case PACKET_FLAGS_2BYTE_SEQUENCE: + return PACKET_2BYTE_SEQUENCE_NUMBER; + case PACKET_FLAGS_1BYTE_SEQUENCE: + return PACKET_1BYTE_SEQUENCE_NUMBER; + default: + LOG(DFATAL) << "Unreachable case statement."; + return PACKET_6BYTE_SEQUENCE_NUMBER; + } +} + +bool CanTruncate( + QuicVersion version, const QuicFrame& frame, size_t free_bytes) { + if ((frame.type == ACK_FRAME || frame.type == CONNECTION_CLOSE_FRAME) && + free_bytes >= + QuicFramer::GetMinAckFrameSize(version, + PACKET_6BYTE_SEQUENCE_NUMBER, + PACKET_6BYTE_SEQUENCE_NUMBER)) { + return true; + } + return false; +} + } // namespace +bool QuicFramerVisitorInterface::OnWindowUpdateFrame( + const QuicWindowUpdateFrame& frame) { + return true; +} + +bool QuicFramerVisitorInterface::OnBlockedFrame(const QuicBlockedFrame& frame) { + return true; +} + QuicFramer::QuicFramer(const QuicVersionVector& supported_versions, QuicTime creation_time, bool is_server) @@ -118,10 +163,13 @@ QuicFramer::QuicFramer(const QuicVersionVector& supported_versions, entropy_calculator_(NULL), error_(QUIC_NO_ERROR), last_sequence_number_(0), - last_serialized_guid_(0), + last_serialized_connection_id_(0), supported_versions_(supported_versions), + decrypter_level_(ENCRYPTION_NONE), + alternative_decrypter_level_(ENCRYPTION_NONE), alternative_decrypter_latch_(false), is_server_(is_server), + validate_flags_(true), creation_time_(creation_time) { DCHECK(!supported_versions.empty()); quic_version_ = supported_versions_[0]; @@ -136,10 +184,13 @@ QuicFramer::~QuicFramer() {} size_t QuicFramer::GetMinStreamFrameSize(QuicVersion version, QuicStreamId stream_id, QuicStreamOffset offset, - bool last_frame_in_packet) { + bool last_frame_in_packet, + InFecGroup is_in_fec_group) { + bool no_stream_frame_length = last_frame_in_packet && + is_in_fec_group == NOT_IN_FEC_GROUP; return kQuicFrameTypeSize + GetStreamIdSize(stream_id) + GetStreamOffsetSize(offset) + - (last_frame_in_packet ? 0 : kQuicStreamPayloadLengthSize); + (no_stream_frame_length ? 0 : kQuicStreamPayloadLengthSize); } // static @@ -147,14 +198,25 @@ size_t QuicFramer::GetMinAckFrameSize( QuicVersion version, QuicSequenceNumberLength sequence_number_length, QuicSequenceNumberLength largest_observed_length) { - return kQuicFrameTypeSize + kQuicEntropyHashSize + - sequence_number_length + kQuicEntropyHashSize + + size_t len = kQuicFrameTypeSize + kQuicEntropyHashSize + largest_observed_length + kQuicDeltaTimeLargestObservedSize; + if (version <= QUIC_VERSION_15) { + len += sequence_number_length + kQuicEntropyHashSize; + } + return len; } // static -size_t QuicFramer::GetMinRstStreamFrameSize() { - return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicErrorCodeSize + +size_t QuicFramer::GetStopWaitingFrameSize( + QuicSequenceNumberLength sequence_number_length) { + return kQuicFrameTypeSize + kQuicEntropyHashSize + + sequence_number_length; +} + +// static +size_t QuicFramer::GetMinRstStreamFrameSize(QuicVersion quic_version) { + return kQuicFrameTypeSize + kQuicMaxStreamIdSize + + kQuicMaxStreamOffsetSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize; } @@ -170,6 +232,16 @@ size_t QuicFramer::GetMinGoAwayFrameSize() { } // static +size_t QuicFramer::GetWindowUpdateFrameSize() { + return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize; +} + +// static +size_t QuicFramer::GetBlockedFrameSize() { + return kQuicFrameTypeSize + kQuicMaxStreamIdSize; +} + +// static size_t QuicFramer::GetStreamIdSize(QuicStreamId stream_id) { // Sizes are 1 through 4 bytes. for (int i = 1; i <= 4; ++i) { @@ -202,22 +274,10 @@ size_t QuicFramer::GetStreamOffsetSize(QuicStreamOffset offset) { // static size_t QuicFramer::GetVersionNegotiationPacketSize(size_t number_versions) { - return kPublicFlagsSize + PACKET_8BYTE_GUID + + return kPublicFlagsSize + PACKET_8BYTE_CONNECTION_ID + number_versions * kQuicVersionSize; } -// static -bool QuicFramer::CanTruncate( - QuicVersion version, const QuicFrame& frame, size_t free_bytes) { - if ((frame.type == ACK_FRAME || frame.type == CONNECTION_CLOSE_FRAME) && - free_bytes >= GetMinAckFrameSize(version, - PACKET_6BYTE_SEQUENCE_NUMBER, - PACKET_6BYTE_SEQUENCE_NUMBER)) { - return true; - } - return false; -} - bool QuicFramer::IsSupportedVersion(const QuicVersion version) const { for (size_t i = 0; i < supported_versions_.size(); ++i) { if (version == supported_versions_[i]) { @@ -232,57 +292,46 @@ size_t QuicFramer::GetSerializedFrameLength( size_t free_bytes, bool first_frame, bool last_frame, + InFecGroup is_in_fec_group, QuicSequenceNumberLength sequence_number_length) { if (frame.type == PADDING_FRAME) { // PADDING implies end of packet. return free_bytes; } size_t frame_len = - ComputeFrameLength(frame, last_frame, sequence_number_length); - 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(quic_version_, frame, free_bytes)) { - // Truncate the frame so the packet will not exceed kMaxPacketSize. - // Note that we may not use every byte of the writer in this case. - DVLOG(1) << "Truncating large frame"; - return free_bytes; - } + ComputeFrameLength(frame, last_frame, is_in_fec_group, + sequence_number_length); + if (frame_len <= free_bytes) { + // Frame fits within packet. Note that acks may be truncated. + return frame_len; + } + // 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(quic_version_, frame, free_bytes)) { + // Truncate the frame so the packet will not exceed kMaxPacketSize. + // Note that we may not use every byte of the writer in this case. + DVLOG(1) << "Truncating large frame, free bytes: " << free_bytes; + return free_bytes; } + if (!FLAGS_quic_allow_oversized_packets_for_test) { + return 0; + } + LOG(DFATAL) << "Packet size too small to fit frame."; return frame_len; } -QuicFramer::AckFrameInfo::AckFrameInfo() : max_delta(0) { } +QuicFramer::AckFrameInfo::AckFrameInfo() : max_delta(0) {} -QuicFramer::AckFrameInfo::~AckFrameInfo() { } +QuicFramer::AckFrameInfo::~AckFrameInfo() {} QuicPacketEntropyHash QuicFramer::GetPacketEntropyHash( const QuicPacketHeader& header) const { return header.entropy_flag << (header.packet_sequence_number % 8); } -// Test only. -SerializedPacket QuicFramer::BuildUnsizedDataPacket( - const QuicPacketHeader& header, - const QuicFrames& frames) { - const size_t max_plaintext_size = GetMaxPlaintextSize(kMaxPacketSize); - size_t packet_size = GetPacketHeaderSize(header); - for (size_t i = 0; i < frames.size(); ++i) { - DCHECK_LE(packet_size, max_plaintext_size); - bool first_frame = i == 0; - bool last_frame = i == frames.size() - 1; - const size_t frame_size = GetSerializedFrameLength( - frames[i], max_plaintext_size - packet_size, first_frame, last_frame, - header.public_header.sequence_number_length); - DCHECK(frame_size); - packet_size += frame_size; - } - return BuildDataPacket(header, frames, packet_size); -} - SerializedPacket QuicFramer::BuildDataPacket( const QuicPacketHeader& header, const QuicFrames& frames, @@ -291,14 +340,19 @@ SerializedPacket QuicFramer::BuildDataPacket( const SerializedPacket kNoPacket( 0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL); if (!AppendPacketHeader(header, &writer)) { + LOG(DFATAL) << "AppendPacketHeader failed"; return kNoPacket; } for (size_t i = 0; i < frames.size(); ++i) { const QuicFrame& frame = frames[i]; - const bool last_frame_in_packet = i == (frames.size() - 1); - if (!AppendTypeByte(frame, last_frame_in_packet, &writer)) { + // Determine if we should write stream frame length in header. + const bool no_stream_frame_length = + (header.is_in_fec_group == NOT_IN_FEC_GROUP) && + (i == frames.size() - 1); + if (!AppendTypeByte(frame, no_stream_frame_length, &writer)) { + LOG(DFATAL) << "AppendTypeByte failed"; return kNoPacket; } @@ -307,41 +361,80 @@ SerializedPacket QuicFramer::BuildDataPacket( writer.WritePadding(); break; case STREAM_FRAME: - if (!AppendStreamFramePayload( - *frame.stream_frame, last_frame_in_packet, &writer)) { + if (!AppendStreamFrame( + *frame.stream_frame, no_stream_frame_length, &writer)) { + LOG(DFATAL) << "AppendStreamFrame failed"; return kNoPacket; } break; case ACK_FRAME: - if (!AppendAckFramePayloadAndTypeByte( + if (!AppendAckFrameAndTypeByte( header, *frame.ack_frame, &writer)) { + LOG(DFATAL) << "AppendAckFrameAndTypeByte failed"; return kNoPacket; } break; case CONGESTION_FEEDBACK_FRAME: - if (!AppendQuicCongestionFeedbackFramePayload( + if (!AppendCongestionFeedbackFrame( *frame.congestion_feedback_frame, &writer)) { + LOG(DFATAL) << "AppendCongestionFeedbackFrame failed"; + return kNoPacket; + } + break; + case STOP_WAITING_FRAME: + if (quic_version_ <= QUIC_VERSION_15) { + LOG(DFATAL) << "Attempt to add a StopWaitingFrame in " + << QuicVersionToString(quic_version_); + return kNoPacket; + } + if (!AppendStopWaitingFrame( + header, *frame.stop_waiting_frame, &writer)) { + LOG(DFATAL) << "AppendStopWaitingFrame failed"; return kNoPacket; } break; + case PING_FRAME: + if (quic_version_ <= QUIC_VERSION_17) { + LOG(DFATAL) << "Attempt to add a PingFrame in " + << QuicVersionToString(quic_version_); + return kNoPacket; + } + // Ping has no payload. + break; case RST_STREAM_FRAME: - if (!AppendRstStreamFramePayload(*frame.rst_stream_frame, &writer)) { + if (!AppendRstStreamFrame(*frame.rst_stream_frame, &writer)) { + LOG(DFATAL) << "AppendRstStreamFrame failed"; return kNoPacket; } break; case CONNECTION_CLOSE_FRAME: - if (!AppendConnectionCloseFramePayload( + if (!AppendConnectionCloseFrame( *frame.connection_close_frame, &writer)) { + LOG(DFATAL) << "AppendConnectionCloseFrame failed"; return kNoPacket; } break; case GOAWAY_FRAME: - if (!AppendGoAwayFramePayload(*frame.goaway_frame, &writer)) { + if (!AppendGoAwayFrame(*frame.goaway_frame, &writer)) { + LOG(DFATAL) << "AppendGoAwayFrame failed"; + return kNoPacket; + } + break; + case WINDOW_UPDATE_FRAME: + if (!AppendWindowUpdateFrame(*frame.window_update_frame, &writer)) { + LOG(DFATAL) << "AppendWindowUpdateFrame failed"; + return kNoPacket; + } + break; + case BLOCKED_FRAME: + if (!AppendBlockedFrame(*frame.blocked_frame, &writer)) { + LOG(DFATAL) << "AppendBlockedFrame failed"; return kNoPacket; } break; default: RaiseError(QUIC_INVALID_FRAME_DATA); + LOG(DFATAL) << "QUIC_INVALID_FRAME_DATA"; return kNoPacket; } } @@ -352,7 +445,7 @@ SerializedPacket QuicFramer::BuildDataPacket( // length, even though they're typically slightly shorter. DCHECK_LE(len, packet_size); QuicPacket* packet = QuicPacket::NewDataPacket( - writer.take(), len, true, header.public_header.guid_length, + writer.take(), len, true, header.public_header.connection_id_length, header.public_header.version_flag, header.public_header.sequence_number_length); @@ -377,10 +470,12 @@ SerializedPacket QuicFramer::BuildFecPacket(const QuicPacketHeader& header, const SerializedPacket kNoPacket( 0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL); if (!AppendPacketHeader(header, &writer)) { + LOG(DFATAL) << "AppendPacketHeader failed"; return kNoPacket; } if (!writer.WriteBytes(fec.redundancy.data(), fec.redundancy.length())) { + LOG(DFATAL) << "Failed to add FEC"; return kNoPacket; } @@ -388,7 +483,7 @@ SerializedPacket QuicFramer::BuildFecPacket(const QuicPacketHeader& header, header.packet_sequence_number, header.public_header.sequence_number_length, QuicPacket::NewFecPacket(writer.take(), len, true, - header.public_header.guid_length, + header.public_header.connection_id_length, header.public_header.version_flag, header.public_header.sequence_number_length), GetPacketEntropyHash(header), NULL); @@ -398,27 +493,37 @@ SerializedPacket QuicFramer::BuildFecPacket(const QuicPacketHeader& header, QuicEncryptedPacket* QuicFramer::BuildPublicResetPacket( const QuicPublicResetPacket& packet) { DCHECK(packet.public_header.reset_flag); - size_t len = GetPublicResetPacketSize(); + + CryptoHandshakeMessage reset; + reset.set_tag(kPRST); + reset.SetValue(kRNON, packet.nonce_proof); + reset.SetValue(kRSEQ, packet.rejected_sequence_number); + if (!packet.client_address.address().empty()) { + // packet.client_address is non-empty. + QuicSocketAddressCoder address_coder(packet.client_address); + string serialized_address = address_coder.Encode(); + if (serialized_address.empty()) { + return NULL; + } + reset.SetStringPiece(kCADR, serialized_address); + } + const QuicData& reset_serialized = reset.GetSerialized(); + + size_t len = + kPublicFlagsSize + PACKET_8BYTE_CONNECTION_ID + reset_serialized.length(); QuicDataWriter writer(len); uint8 flags = static_cast<uint8>(PACKET_PUBLIC_FLAGS_RST | - PACKET_PUBLIC_FLAGS_8BYTE_GUID | - PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE); + PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID); if (!writer.WriteUInt8(flags)) { return NULL; } - if (!writer.WriteUInt64(packet.public_header.guid)) { - return NULL; - } - - if (!writer.WriteUInt64(packet.nonce_proof)) { + if (!writer.WriteUInt64(packet.public_header.connection_id)) { return NULL; } - if (!AppendPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, - packet.rejected_sequence_number, - &writer)) { + if (!writer.WriteBytes(reset_serialized.data(), reset_serialized.length())) { return NULL; } @@ -433,13 +538,12 @@ QuicEncryptedPacket* QuicFramer::BuildVersionNegotiationPacket( QuicDataWriter writer(len); uint8 flags = static_cast<uint8>(PACKET_PUBLIC_FLAGS_VERSION | - PACKET_PUBLIC_FLAGS_8BYTE_GUID | - PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE); + PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID); if (!writer.WriteUInt8(flags)) { return NULL; } - if (!writer.WriteUInt64(header.guid)) { + if (!writer.WriteUInt64(header.connection_id)) { return NULL; } @@ -466,6 +570,12 @@ bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) { return RaiseError(QUIC_INVALID_PACKET_HEADER); } + if (!visitor_->OnUnauthenticatedPublicHeader(public_header)) { + // The visitor suppresses further processing of the packet. + reader_.reset(NULL); + return true; + } + if (is_server_ && public_header.version_flag && public_header.versions[0] != quic_version_) { if (!visitor_->OnProtocolVersionMismatch(public_header.versions[0])) { @@ -548,16 +658,39 @@ bool QuicFramer::ProcessDataPacket( bool QuicFramer::ProcessPublicResetPacket( const QuicPacketPublicHeader& public_header) { QuicPublicResetPacket packet(public_header); - if (!reader_->ReadUInt64(&packet.nonce_proof)) { + + scoped_ptr<CryptoHandshakeMessage> reset( + CryptoFramer::ParseMessage(reader_->ReadRemainingPayload())); + if (!reset.get()) { + set_detailed_error("Unable to read reset message."); + return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET); + } + if (reset->tag() != kPRST) { + set_detailed_error("Incorrect message tag."); + return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET); + } + + if (reset->GetUint64(kRNON, &packet.nonce_proof) != QUIC_NO_ERROR) { set_detailed_error("Unable to read nonce proof."); return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET); } // TODO(satyamshekhar): validate nonce to protect against DoS. - if (!reader_->ReadUInt48(&packet.rejected_sequence_number)) { + if (reset->GetUint64(kRSEQ, &packet.rejected_sequence_number) != + QUIC_NO_ERROR) { set_detailed_error("Unable to read rejected sequence number."); return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET); } + + StringPiece address; + if (reset->GetStringPiece(kCADR, &address)) { + QuicSocketAddressCoder address_coder; + if (address_coder.Decode(address.data(), address.length())) { + packet.client_address = IPEndPoint(address_coder.ip(), + address_coder.port()); + } + } + visitor_->OnPublicResetPacket(packet); return true; } @@ -593,6 +726,7 @@ bool QuicFramer::ProcessRevivedPacket(QuicPacketHeader* header, bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header, QuicDataWriter* writer) { + DVLOG(1) << "Appending header: " << header; DCHECK(header.fec_group > 0 || header.is_in_fec_group == NOT_IN_FEC_GROUP); uint8 public_flags = 0; if (header.public_header.reset_flag) { @@ -606,38 +740,44 @@ bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header, GetSequenceNumberFlags(header.public_header.sequence_number_length) << kPublicHeaderSequenceNumberShift; - switch (header.public_header.guid_length) { - case PACKET_0BYTE_GUID: - if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_0BYTE_GUID)) { + switch (header.public_header.connection_id_length) { + case PACKET_0BYTE_CONNECTION_ID: + if (!writer->WriteUInt8( + public_flags | PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID)) { return false; } break; - case PACKET_1BYTE_GUID: - if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_1BYTE_GUID)) { + case PACKET_1BYTE_CONNECTION_ID: + if (!writer->WriteUInt8( + public_flags | PACKET_PUBLIC_FLAGS_1BYTE_CONNECTION_ID)) { return false; } - if (!writer->WriteUInt8(header.public_header.guid & k1ByteGuidMask)) { + if (!writer->WriteUInt8( + header.public_header.connection_id & k1ByteConnectionIdMask)) { return false; } break; - case PACKET_4BYTE_GUID: - if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_4BYTE_GUID)) { + case PACKET_4BYTE_CONNECTION_ID: + if (!writer->WriteUInt8( + public_flags | PACKET_PUBLIC_FLAGS_4BYTE_CONNECTION_ID)) { return false; } - if (!writer->WriteUInt32(header.public_header.guid & k4ByteGuidMask)) { + if (!writer->WriteUInt32( + header.public_header.connection_id & k4ByteConnectionIdMask)) { return false; } break; - case PACKET_8BYTE_GUID: - if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_8BYTE_GUID)) { + case PACKET_8BYTE_CONNECTION_ID: + if (!writer->WriteUInt8( + public_flags | PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID)) { return false; } - if (!writer->WriteUInt64(header.public_header.guid)) { + if (!writer->WriteUInt64(header.public_header.connection_id)) { return false; } break; } - last_serialized_guid_ = header.public_header.guid; + last_serialized_connection_id_ = header.public_header.connection_id; if (header.public_header.version_flag) { DCHECK(!is_server_); @@ -717,7 +857,8 @@ bool QuicFramer::ProcessPublicHeader( public_header->version_flag = (public_flags & PACKET_PUBLIC_FLAGS_VERSION) != 0; - if (!public_header->version_flag && public_flags > PACKET_PUBLIC_FLAGS_MAX) { + if (validate_flags_ && + !public_header->version_flag && public_flags > PACKET_PUBLIC_FLAGS_MAX) { set_detailed_error("Illegal public flags value."); return false; } @@ -727,46 +868,49 @@ bool QuicFramer::ProcessPublicHeader( return false; } - switch (public_flags & PACKET_PUBLIC_FLAGS_8BYTE_GUID) { - case PACKET_PUBLIC_FLAGS_8BYTE_GUID: - if (!reader_->ReadUInt64(&public_header->guid)) { - set_detailed_error("Unable to read GUID."); + switch (public_flags & PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID) { + case PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID: + if (!reader_->ReadUInt64(&public_header->connection_id)) { + set_detailed_error("Unable to read ConnectionId."); return false; } - public_header->guid_length = PACKET_8BYTE_GUID; + public_header->connection_id_length = PACKET_8BYTE_CONNECTION_ID; break; - case PACKET_PUBLIC_FLAGS_4BYTE_GUID: - // If the guid is truncated, expect to read the last serialized guid. - if (!reader_->ReadBytes(&public_header->guid, PACKET_4BYTE_GUID)) { - set_detailed_error("Unable to read GUID."); + case PACKET_PUBLIC_FLAGS_4BYTE_CONNECTION_ID: + // If the connection_id is truncated, expect to read the last serialized + // connection_id. + if (!reader_->ReadBytes(&public_header->connection_id, + PACKET_4BYTE_CONNECTION_ID)) { + set_detailed_error("Unable to read ConnectionId."); return false; } - if ((public_header->guid & k4ByteGuidMask) != - (last_serialized_guid_ & k4ByteGuidMask)) { - set_detailed_error( - "Truncated 4 byte GUID does not match previous guid."); + if ((public_header->connection_id & k4ByteConnectionIdMask) != + (last_serialized_connection_id_ & k4ByteConnectionIdMask)) { + set_detailed_error("Truncated 4 byte ConnectionId does not match " + "previous connection_id."); return false; } - public_header->guid_length = PACKET_4BYTE_GUID; - public_header->guid = last_serialized_guid_; + public_header->connection_id_length = PACKET_4BYTE_CONNECTION_ID; + public_header->connection_id = last_serialized_connection_id_; break; - case PACKET_PUBLIC_FLAGS_1BYTE_GUID: - if (!reader_->ReadBytes(&public_header->guid, PACKET_1BYTE_GUID)) { - set_detailed_error("Unable to read GUID."); + case PACKET_PUBLIC_FLAGS_1BYTE_CONNECTION_ID: + if (!reader_->ReadBytes(&public_header->connection_id, + PACKET_1BYTE_CONNECTION_ID)) { + set_detailed_error("Unable to read ConnectionId."); return false; } - if ((public_header->guid & k1ByteGuidMask) != - (last_serialized_guid_ & k1ByteGuidMask)) { - set_detailed_error( - "Truncated 1 byte GUID does not match previous guid."); + if ((public_header->connection_id & k1ByteConnectionIdMask) != + (last_serialized_connection_id_ & k1ByteConnectionIdMask)) { + set_detailed_error("Truncated 1 byte ConnectionId does not match " + "previous connection_id."); return false; } - public_header->guid_length = PACKET_1BYTE_GUID; - public_header->guid = last_serialized_guid_; + public_header->connection_id_length = PACKET_1BYTE_CONNECTION_ID; + public_header->connection_id = last_serialized_connection_id_; break; - case PACKET_PUBLIC_FLAGS_0BYTE_GUID: - public_header->guid_length = PACKET_0BYTE_GUID; - public_header->guid = last_serialized_guid_; + case PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID: + public_header->connection_id_length = PACKET_0BYTE_CONNECTION_ID; + public_header->connection_id = last_serialized_connection_id_; break; } @@ -797,40 +941,6 @@ bool QuicFramer::ProcessPublicHeader( } // static -bool QuicFramer::ReadGuidFromPacket(const QuicEncryptedPacket& packet, - QuicGuid* guid) { - QuicDataReader reader(packet.data(), packet.length()); - uint8 public_flags; - if (!reader.ReadBytes(&public_flags, 1)) { - return false; - } - // Ensure it's an 8 byte guid. - if ((public_flags & PACKET_PUBLIC_FLAGS_8BYTE_GUID) != - PACKET_PUBLIC_FLAGS_8BYTE_GUID) { - return false; - } - - return reader.ReadUInt64(guid); -} - -// static -QuicSequenceNumberLength QuicFramer::ReadSequenceNumberLength(uint8 flags) { - switch (flags & PACKET_FLAGS_6BYTE_SEQUENCE) { - case PACKET_FLAGS_6BYTE_SEQUENCE: - return PACKET_6BYTE_SEQUENCE_NUMBER; - case PACKET_FLAGS_4BYTE_SEQUENCE: - return PACKET_4BYTE_SEQUENCE_NUMBER; - case PACKET_FLAGS_2BYTE_SEQUENCE: - return PACKET_2BYTE_SEQUENCE_NUMBER; - case PACKET_FLAGS_1BYTE_SEQUENCE: - return PACKET_1BYTE_SEQUENCE_NUMBER; - default: - LOG(DFATAL) << "Unreachable case statement."; - return PACKET_6BYTE_SEQUENCE_NUMBER; - } -} - -// static QuicSequenceNumberLength QuicFramer::GetMinSequenceNumberLength( QuicPacketSequenceNumber sequence_number) { if (sequence_number < 1 << (PACKET_1BYTE_SEQUENCE_NUMBER * 8)) { @@ -1082,6 +1192,65 @@ bool QuicFramer::ProcessFrameData(const QuicPacketHeader& header) { continue; } + case WINDOW_UPDATE_FRAME: { + QuicWindowUpdateFrame window_update_frame; + if (!ProcessWindowUpdateFrame(&window_update_frame)) { + return RaiseError(QUIC_INVALID_WINDOW_UPDATE_DATA); + } + if (!visitor_->OnWindowUpdateFrame(window_update_frame)) { + DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + + case BLOCKED_FRAME: { + QuicBlockedFrame blocked_frame; + if (!ProcessBlockedFrame(&blocked_frame)) { + return RaiseError(QUIC_INVALID_BLOCKED_DATA); + } + if (!visitor_->OnBlockedFrame(blocked_frame)) { + DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + + case STOP_WAITING_FRAME: { + if (quic_version_ <= QUIC_VERSION_15) { + LOG(DFATAL) << "Trying to read a StopWaiting in " + << QuicVersionToString(quic_version_); + return RaiseError(QUIC_INTERNAL_ERROR); + } + QuicStopWaitingFrame stop_waiting_frame; + if (!ProcessStopWaitingFrame(header, &stop_waiting_frame)) { + return RaiseError(QUIC_INVALID_STOP_WAITING_DATA); + } + if (!visitor_->OnStopWaitingFrame(stop_waiting_frame)) { + DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + case PING_FRAME: { + if (quic_version_ <= QUIC_VERSION_17) { + LOG(DFATAL) << "Trying to read a Ping in " + << QuicVersionToString(quic_version_); + return RaiseError(QUIC_INTERNAL_ERROR); + } + // Ping has no payload. + QuicPingFrame ping_frame; + if (!visitor_->OnPingFrame(ping_frame)) { + DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + default: set_detailed_error("Illegal frame type."); DLOG(WARNING) << "Illegal frame type: " @@ -1152,8 +1321,10 @@ bool QuicFramer::ProcessStreamFrame(uint8 frame_type, bool QuicFramer::ProcessAckFrame(const QuicPacketHeader& header, uint8 frame_type, QuicAckFrame* frame) { - if (!ProcessSentInfo(header, &frame->sent_info)) { - return false; + if (quic_version_ <= QUIC_VERSION_15) { + if (!ProcessStopWaitingFrame(header, &frame->sent_info)) { + return false; + } } if (!ProcessReceivedInfo(frame_type, &frame->received_info)) { return false; @@ -1232,12 +1403,30 @@ bool QuicFramer::ProcessReceivedInfo(uint8 frame_type, last_sequence_number -= (range_length + 1); } + // Parse the revived packets list. + uint8 num_revived_packets; + if (!reader_->ReadBytes(&num_revived_packets, 1)) { + set_detailed_error("Unable to read num revived packets."); + return false; + } + + for (size_t i = 0; i < num_revived_packets; ++i) { + QuicPacketSequenceNumber revived_packet = 0; + if (!reader_->ReadBytes(&revived_packet, + largest_observed_sequence_number_length)) { + set_detailed_error("Unable to read revived packet."); + return false; + } + + received_info->revived_packets.insert(revived_packet); + } + return true; } -bool QuicFramer::ProcessSentInfo(const QuicPacketHeader& header, - SentPacketInfo* sent_info) { - if (!reader_->ReadBytes(&sent_info->entropy_hash, 1)) { +bool QuicFramer::ProcessStopWaitingFrame(const QuicPacketHeader& header, + QuicStopWaitingFrame* stop_waiting) { + if (!reader_->ReadBytes(&stop_waiting->entropy_hash, 1)) { set_detailed_error("Unable to read entropy hash for sent packets."); return false; } @@ -1249,7 +1438,7 @@ bool QuicFramer::ProcessSentInfo(const QuicPacketHeader& header, return false; } DCHECK_GE(header.packet_sequence_number, least_unacked_delta); - sent_info->least_unacked = + stop_waiting->least_unacked = header.packet_sequence_number - least_unacked_delta; return true; @@ -1269,12 +1458,6 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame( case kInterArrival: { CongestionFeedbackMessageInterArrival* inter_arrival = &frame->inter_arrival; - if (!reader_->ReadUInt16( - &inter_arrival->accumulated_number_of_lost_packets)) { - set_detailed_error( - "Unable to read accumulated number of lost packets."); - return false; - } uint8 num_received_packets; if (!reader_->ReadBytes(&num_received_packets, 1)) { set_detailed_error("Unable to read num received packets."); @@ -1333,11 +1516,6 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame( } case kTCP: { CongestionFeedbackMessageTCP* tcp = &frame->tcp; - if (!reader_->ReadUInt16(&tcp->accumulated_number_of_lost_packets)) { - set_detailed_error( - "Unable to read accumulated number of lost packets."); - return false; - } // TODO(ianswett): Remove receive window, since it's constant. uint16 receive_window = 0; if (!reader_->ReadUInt16(&receive_window)) { @@ -1364,6 +1542,11 @@ bool QuicFramer::ProcessRstStreamFrame(QuicRstStreamFrame* frame) { return false; } + if (!reader_->ReadUInt64(&frame->byte_offset)) { + set_detailed_error("Unable to read rst stream sent byte offset."); + return false; + } + uint32 error_code; if (!reader_->ReadUInt32(&error_code)) { set_detailed_error("Unable to read rst stream error code."); @@ -1444,26 +1627,54 @@ bool QuicFramer::ProcessGoAwayFrame(QuicGoAwayFrame* frame) { return true; } +bool QuicFramer::ProcessWindowUpdateFrame(QuicWindowUpdateFrame* frame) { + if (!reader_->ReadUInt32(&frame->stream_id)) { + set_detailed_error("Unable to read stream_id."); + return false; + } + + if (!reader_->ReadUInt64(&frame->byte_offset)) { + set_detailed_error("Unable to read window byte_offset."); + return false; + } + + return true; +} + +bool QuicFramer::ProcessBlockedFrame(QuicBlockedFrame* frame) { + if (!reader_->ReadUInt32(&frame->stream_id)) { + set_detailed_error("Unable to read stream_id."); + return false; + } + + return true; +} + // static StringPiece QuicFramer::GetAssociatedDataFromEncryptedPacket( const QuicEncryptedPacket& encrypted, - QuicGuidLength guid_length, + QuicConnectionIdLength connection_id_length, bool includes_version, QuicSequenceNumberLength sequence_number_length) { - return StringPiece(encrypted.data() + kStartOfHashData, - GetStartOfEncryptedData( - guid_length, includes_version, sequence_number_length) - - kStartOfHashData); + return StringPiece( + encrypted.data() + kStartOfHashData, GetStartOfEncryptedData( + connection_id_length, includes_version, sequence_number_length) + - kStartOfHashData); } -void QuicFramer::SetDecrypter(QuicDecrypter* decrypter) { +void QuicFramer::SetDecrypter(QuicDecrypter* decrypter, + EncryptionLevel level) { DCHECK(alternative_decrypter_.get() == NULL); + DCHECK_GE(level, decrypter_level_); decrypter_.reset(decrypter); + decrypter_level_ = level; } void QuicFramer::SetAlternativeDecrypter(QuicDecrypter* decrypter, + EncryptionLevel level, bool latch_once_used) { alternative_decrypter_.reset(decrypter); + alternative_decrypter_level_ = level; alternative_decrypter_latch_ = latch_once_used; } @@ -1489,18 +1700,6 @@ const QuicEncrypter* QuicFramer::encrypter(EncryptionLevel level) const { return encrypter_[level].get(); } -void QuicFramer::SwapCryptersForTest(QuicFramer* other) { - for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; i++) { - encrypter_[i].swap(other->encrypter_[i]); - } - decrypter_.swap(other->decrypter_); - alternative_decrypter_.swap(other->alternative_decrypter_); - - const bool other_latch = other->alternative_decrypter_latch_; - other->alternative_decrypter_latch_ = alternative_decrypter_latch_; - alternative_decrypter_latch_ = other_latch; -} - QuicEncryptedPacket* QuicFramer::EncryptPacket( EncryptionLevel level, QuicPacketSequenceNumber packet_sequence_number, @@ -1550,32 +1749,42 @@ bool QuicFramer::DecryptPayload(const QuicPacketHeader& header, header.packet_sequence_number, GetAssociatedDataFromEncryptedPacket( packet, - header.public_header.guid_length, + header.public_header.connection_id_length, header.public_header.version_flag, header.public_header.sequence_number_length), encrypted)); - if (decrypted_.get() == NULL && alternative_decrypter_.get() != NULL) { + if (decrypted_.get() != NULL) { + visitor_->OnDecryptedPacket(decrypter_level_); + } else if (alternative_decrypter_.get() != NULL) { decrypted_.reset(alternative_decrypter_->DecryptPacket( header.packet_sequence_number, GetAssociatedDataFromEncryptedPacket( packet, - header.public_header.guid_length, + header.public_header.connection_id_length, header.public_header.version_flag, header.public_header.sequence_number_length), encrypted)); if (decrypted_.get() != NULL) { + visitor_->OnDecryptedPacket(alternative_decrypter_level_); if (alternative_decrypter_latch_) { // Switch to the alternative decrypter and latch so that we cannot // switch back. decrypter_.reset(alternative_decrypter_.release()); + decrypter_level_ = alternative_decrypter_level_; + alternative_decrypter_level_ = ENCRYPTION_NONE; } else { // Switch the alternative decrypter so that we use it first next time. decrypter_.swap(alternative_decrypter_); + EncryptionLevel level = alternative_decrypter_level_; + alternative_decrypter_level_ = decrypter_level_; + decrypter_level_ = level; } } } if (decrypted_.get() == NULL) { + DLOG(WARNING) << "DecryptPacket failed for sequence_number:" + << header.packet_sequence_number; return false; } @@ -1592,24 +1801,31 @@ size_t QuicFramer::GetAckFrameSize( QuicSequenceNumberLength missing_sequence_number_length = GetMinSequenceNumberLength(ack_info.max_delta); - return GetMinAckFrameSize(quic_version_, - sequence_number_length, - largest_observed_length) + - (ack_info.nack_ranges.empty() ? 0 : kNumberOfMissingPacketsSize) + - ack_info.nack_ranges.size() * - (missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER); + size_t ack_size = GetMinAckFrameSize(quic_version_, + sequence_number_length, + largest_observed_length); + if (!ack_info.nack_ranges.empty()) { + ack_size += kNumberOfNackRangesSize + kNumberOfRevivedPacketsSize; + ack_size += min(ack_info.nack_ranges.size(), kMaxNackRanges) * + (missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER); + ack_size += min(ack.received_info.revived_packets.size(), + kMaxRevivedPackets) * largest_observed_length; + } + return ack_size; } size_t QuicFramer::ComputeFrameLength( const QuicFrame& frame, bool last_frame_in_packet, + InFecGroup is_in_fec_group, QuicSequenceNumberLength sequence_number_length) { switch (frame.type) { case STREAM_FRAME: return GetMinStreamFrameSize(quic_version_, frame.stream_frame->stream_id, frame.stream_frame->offset, - last_frame_in_packet) + + last_frame_in_packet, + is_in_fec_group) + frame.stream_frame->data.TotalBufferSize(); case ACK_FRAME: { return GetAckFrameSize(*frame.ack_frame, sequence_number_length); @@ -1624,7 +1840,6 @@ size_t QuicFramer::ComputeFrameLength( case kInterArrival: { const CongestionFeedbackMessageInterArrival& inter_arrival = congestion_feedback.inter_arrival; - len += 2; len += 1; // Number received packets. if (inter_arrival.received_packet_times.size() > 0) { len += PACKET_6BYTE_SEQUENCE_NUMBER; // Smallest received. @@ -1636,10 +1851,10 @@ size_t QuicFramer::ComputeFrameLength( break; } case kFixRate: - len += 4; + len += 4; // Bitrate. break; case kTCP: - len += 4; + len += 2; // Receive window. break; default: set_detailed_error("Illegal feedback type."); @@ -1648,14 +1863,23 @@ size_t QuicFramer::ComputeFrameLength( } return len; } + case STOP_WAITING_FRAME: + return GetStopWaitingFrameSize(sequence_number_length); + case PING_FRAME: + // Ping has no payload. + return kQuicFrameTypeSize; case RST_STREAM_FRAME: - return GetMinRstStreamFrameSize() + + return GetMinRstStreamFrameSize(quic_version_) + frame.rst_stream_frame->error_details.size(); case CONNECTION_CLOSE_FRAME: return GetMinConnectionCloseFrameSize() + frame.connection_close_frame->error_details.size(); case GOAWAY_FRAME: return GetMinGoAwayFrameSize() + frame.goaway_frame->reason_phrase.size(); + case WINDOW_UPDATE_FRAME: + return GetWindowUpdateFrameSize(); + case BLOCKED_FRAME: + return GetBlockedFrameSize(); case PADDING_FRAME: DCHECK(false); return 0; @@ -1670,7 +1894,7 @@ size_t QuicFramer::ComputeFrameLength( } bool QuicFramer::AppendTypeByte(const QuicFrame& frame, - bool last_frame_in_packet, + bool no_stream_frame_length, QuicDataWriter* writer) { uint8 type_byte = 0; switch (frame.type) { @@ -1683,7 +1907,7 @@ bool QuicFramer::AppendTypeByte(const QuicFrame& frame, // Data Length bit. type_byte <<= kQuicStreamDataLengthShift; - type_byte |= last_frame_in_packet ? 0 : kQuicStreamDataLengthMask; + type_byte |= no_stream_frame_length ? 0: kQuicStreamDataLengthMask; // Offset 3 bits. type_byte <<= kQuicStreamOffsetShift; @@ -1741,63 +1965,44 @@ bool QuicFramer::AppendPacketSequenceNumber( packet_sequence_number & k6ByteSequenceNumberMask); break; default: - NOTREACHED() << "sequence_number_length: " << sequence_number_length; + DCHECK(false) << "sequence_number_length: " << sequence_number_length; return false; } } -bool QuicFramer::AppendStreamFramePayload( +bool QuicFramer::AppendStreamFrame( const QuicStreamFrame& frame, - bool last_frame_in_packet, + bool no_stream_frame_length, QuicDataWriter* writer) { if (!writer->WriteBytes(&frame.stream_id, GetStreamIdSize(frame.stream_id))) { + LOG(DFATAL) << "Writing stream id size failed."; return false; } if (!writer->WriteBytes(&frame.offset, GetStreamOffsetSize(frame.offset))) { + LOG(DFATAL) << "Writing offset size failed."; return false; } - if (!last_frame_in_packet) { + if (!no_stream_frame_length) { if (!writer->WriteUInt16(frame.data.TotalBufferSize())) { + LOG(DFATAL) << "Writing stream frame length failed"; return false; } } if (!writer->WriteIOVector(frame.data)) { + LOG(DFATAL) << "Writing frame data failed."; return false; } return true; } // static -bool QuicFramer::HasVersionFlag(const QuicEncryptedPacket& packet) { - return packet.length() > 0 && - (packet.data()[0] & PACKET_PUBLIC_FLAGS_VERSION) != 0; -} - -// static -QuicPacketSequenceNumber QuicFramer::CalculateLargestObserved( - const SequenceNumberSet& missing_packets, - SequenceNumberSet::const_iterator largest_written) { - SequenceNumberSet::const_iterator it = largest_written; - QuicPacketSequenceNumber previous_missing = *it; - ++it; - - // See if the next thing is a gap in the missing packets: if it's a - // non-missing packet we can return it. - if (it != missing_packets.end() && previous_missing + 1 != *it) { - return *it - 1; - } - - // Otherwise return the largest missing packet, as indirectly observed. - return *largest_written; -} - void QuicFramer::set_version(const QuicVersion version) { - DCHECK(IsSupportedVersion(version)); + DCHECK(IsSupportedVersion(version)) << QuicVersionToString(version); quic_version_ = version; } -bool QuicFramer::AppendAckFramePayloadAndTypeByte( +bool QuicFramer::AppendAckFrameAndTypeByte( const QuicPacketHeader& header, const QuicAckFrame& frame, QuicDataWriter* writer) { @@ -1810,18 +2015,17 @@ bool QuicFramer::AppendAckFramePayloadAndTypeByte( GetMinSequenceNumberLength(ack_info.max_delta); // Determine whether we need to truncate ranges. size_t available_range_bytes = writer->capacity() - writer->length() - + kNumberOfRevivedPacketsSize - kNumberOfNackRangesSize - GetMinAckFrameSize(quic_version_, header.public_header.sequence_number_length, largest_observed_length); size_t max_num_ranges = available_range_bytes / (missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER); - max_num_ranges = - min(static_cast<size_t>(numeric_limits<uint8>::max()), max_num_ranges); + max_num_ranges = min(kMaxNackRanges, max_num_ranges); bool truncated = ack_info.nack_ranges.size() > max_num_ranges; DVLOG_IF(1, truncated) << "Truncating ack from " << ack_info.nack_ranges.size() << " ranges to " << max_num_ranges; - // Write out the type byte by setting the low order bits and doing shifts // to make room for the next bit flags to be set. // Whether there are any nacks. @@ -1845,18 +2049,10 @@ bool QuicFramer::AppendAckFramePayloadAndTypeByte( return false; } - // TODO(satyamshekhar): Decide how often we really should send this - // entropy_hash update. - if (!writer->WriteUInt8(frame.sent_info.entropy_hash)) { - return false; - } - - DCHECK_GE(header.packet_sequence_number, frame.sent_info.least_unacked); - const QuicPacketSequenceNumber least_unacked_delta = - header.packet_sequence_number - frame.sent_info.least_unacked; - if (!AppendPacketSequenceNumber(header.public_header.sequence_number_length, - least_unacked_delta, writer)) { - return false; + if (quic_version_ <= QUIC_VERSION_15) { + if (!AppendStopWaitingFrame(header, frame.sent_info, writer)) { + return false; + } } const ReceivedPacketInfo& received_info = frame.received_info; @@ -1927,12 +2123,33 @@ bool QuicFramer::AppendAckFramePayloadAndTypeByte( last_sequence_written = ack_iter->first - 1; ++num_ranges_written; } - DCHECK_EQ(num_missing_ranges, num_ranges_written); + + // Append revived packets. + // If not all the revived packets fit, only mention the ones that do. + uint8 num_revived_packets = min(received_info.revived_packets.size(), + kMaxRevivedPackets); + num_revived_packets = min( + static_cast<size_t>(num_revived_packets), + (writer->capacity() - writer->length()) / largest_observed_length); + if (!writer->WriteBytes(&num_revived_packets, 1)) { + return false; + } + + SequenceNumberSet::const_iterator iter = + received_info.revived_packets.begin(); + for (int i = 0; i < num_revived_packets; ++i, ++iter) { + LOG_IF(DFATAL, !ContainsKey(received_info.missing_packets, *iter)); + if (!AppendPacketSequenceNumber(largest_observed_length, + *iter, writer)) { + return false; + } + } + return true; } -bool QuicFramer::AppendQuicCongestionFeedbackFramePayload( +bool QuicFramer::AppendCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& frame, QuicDataWriter* writer) { if (!writer->WriteBytes(&frame.type, 1)) { @@ -1943,10 +2160,6 @@ bool QuicFramer::AppendQuicCongestionFeedbackFramePayload( case kInterArrival: { const CongestionFeedbackMessageInterArrival& inter_arrival = frame.inter_arrival; - if (!writer->WriteUInt16( - inter_arrival.accumulated_number_of_lost_packets)) { - return false; - } DCHECK_GE(numeric_limits<uint8>::max(), inter_arrival.received_packet_times.size()); if (inter_arrival.received_packet_times.size() > @@ -2007,9 +2220,6 @@ bool QuicFramer::AppendQuicCongestionFeedbackFramePayload( 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(receive_window)) { return false; } @@ -2022,13 +2232,48 @@ bool QuicFramer::AppendQuicCongestionFeedbackFramePayload( return true; } -bool QuicFramer::AppendRstStreamFramePayload( +bool QuicFramer::AppendStopWaitingFrame( + const QuicPacketHeader& header, + const QuicStopWaitingFrame& frame, + QuicDataWriter* writer) { + DCHECK_GE(header.packet_sequence_number, frame.least_unacked); + const QuicPacketSequenceNumber least_unacked_delta = + header.packet_sequence_number - frame.least_unacked; + const QuicPacketSequenceNumber length_shift = + header.public_header.sequence_number_length * 8; + if (!writer->WriteUInt8(frame.entropy_hash)) { + LOG(DFATAL) << " hash failed"; + return false; + } + + if (least_unacked_delta >> length_shift > 0) { + LOG(DFATAL) << "sequence_number_length " + << header.public_header.sequence_number_length + << " is too small for least_unacked_delta: " + << least_unacked_delta; + return false; + } + if (!AppendPacketSequenceNumber(header.public_header.sequence_number_length, + least_unacked_delta, writer)) { + LOG(DFATAL) << " seq failed: " + << header.public_header.sequence_number_length; + return false; + } + + return true; +} + +bool QuicFramer::AppendRstStreamFrame( const QuicRstStreamFrame& frame, QuicDataWriter* writer) { if (!writer->WriteUInt32(frame.stream_id)) { return false; } + if (!writer->WriteUInt64(frame.byte_offset)) { + return false; + } + uint32 error_code = static_cast<uint32>(frame.error_code); if (!writer->WriteUInt32(error_code)) { return false; @@ -2040,7 +2285,7 @@ bool QuicFramer::AppendRstStreamFramePayload( return true; } -bool QuicFramer::AppendConnectionCloseFramePayload( +bool QuicFramer::AppendConnectionCloseFrame( const QuicConnectionCloseFrame& frame, QuicDataWriter* writer) { uint32 error_code = static_cast<uint32>(frame.error_code); @@ -2053,8 +2298,8 @@ bool QuicFramer::AppendConnectionCloseFramePayload( return true; } -bool QuicFramer::AppendGoAwayFramePayload(const QuicGoAwayFrame& frame, - QuicDataWriter* writer) { +bool QuicFramer::AppendGoAwayFrame(const QuicGoAwayFrame& frame, + QuicDataWriter* writer) { uint32 error_code = static_cast<uint32>(frame.error_code); if (!writer->WriteUInt32(error_code)) { return false; @@ -2069,8 +2314,29 @@ bool QuicFramer::AppendGoAwayFramePayload(const QuicGoAwayFrame& frame, return true; } +bool QuicFramer::AppendWindowUpdateFrame(const QuicWindowUpdateFrame& frame, + QuicDataWriter* writer) { + uint32 stream_id = static_cast<uint32>(frame.stream_id); + if (!writer->WriteUInt32(stream_id)) { + return false; + } + if (!writer->WriteUInt64(frame.byte_offset)) { + return false; + } + return true; +} + +bool QuicFramer::AppendBlockedFrame(const QuicBlockedFrame& frame, + QuicDataWriter* writer) { + uint32 stream_id = static_cast<uint32>(frame.stream_id); + if (!writer->WriteUInt32(stream_id)) { + return false; + } + return true; +} + bool QuicFramer::RaiseError(QuicErrorCode error) { - DVLOG(1) << detailed_error_; + DVLOG(1) << "Error detail: " << detailed_error_; set_error(error); visitor_->OnError(this); reader_.reset(NULL); diff --git a/chromium/net/quic/quic_framer.h b/chromium/net/quic/quic_framer.h index 132375200be..25518109a41 100644 --- a/chromium/net/quic/quic_framer.h +++ b/chromium/net/quic/quic_framer.h @@ -46,7 +46,15 @@ const size_t kQuicEntropyHashSize = 1; // sequence number in ack frames. const size_t kQuicDeltaTimeLargestObservedSize = 2; // Size in bytes reserved for the number of missing packets in ack frames. -const size_t kNumberOfMissingPacketsSize = 1; +const size_t kNumberOfNackRangesSize = 1; +// Maximum number of missing packet ranges that can fit within an ack frame. +const size_t kMaxNackRanges = + (1 << (kNumberOfNackRangesSize * 8)) - 1; +// Size in bytes reserved for the number of revived packets in ack frames. +const size_t kNumberOfRevivedPacketsSize = 1; +// Maximum number of revived packets that can fit within an ack frame. +const size_t kMaxRevivedPackets = + (1 << (kNumberOfRevivedPacketsSize * 8)) - 1; // This class receives callbacks from the framer when packets // are processed. @@ -82,11 +90,20 @@ class NET_EXPORT_PRIVATE QuicFramerVisitorInterface { // before it has been processed. virtual void OnRevivedPacket() = 0; + // Called when the public header has been parsed, but has not been + // authenticated. If it returns false, framing for this packet will cease. + virtual bool OnUnauthenticatedPublicHeader( + const QuicPacketPublicHeader& header) = 0; + // Called when the unauthenticated portion of the header has been parsed. // If OnUnauthenticatedHeader returns false, framing for this packet will // cease. virtual bool OnUnauthenticatedHeader(const QuicPacketHeader& header) = 0; + // Called when a packet has been decrypted. |level| is the encryption level + // of the packet. + virtual void OnDecryptedPacket(EncryptionLevel level) = 0; + // Called when the complete header of a packet had been parsed. // If OnPacketHeader returns false, framing for this packet will cease. virtual bool OnPacketHeader(const QuicPacketHeader& header) = 0; @@ -106,6 +123,12 @@ class NET_EXPORT_PRIVATE QuicFramerVisitorInterface { virtual bool OnCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& frame) = 0; + // Called when a StopWaitingFrame has been parsed. + virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) = 0; + + // Called when a PingFrame has been parsed. + virtual bool OnPingFrame(const QuicPingFrame& frame) = 0; + // Called when a RstStreamFrame has been parsed. virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) = 0; @@ -116,6 +139,12 @@ class NET_EXPORT_PRIVATE QuicFramerVisitorInterface { // Called when a GoAwayFrame has been parsed. virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) = 0; + // Called when a WindowUpdateFrame has been parsed. + virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) = 0; + + // Called when a BlockedFrame has been parsed. + virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) = 0; + // Called when FEC data has been parsed. virtual void OnFecData(const QuicFecData& fec) = 0; @@ -167,16 +196,6 @@ class NET_EXPORT_PRIVATE QuicFramer { // Returns true if |version| is a supported protocol version. bool IsSupportedVersion(const QuicVersion version) const; - // Returns true if the version flag is set in the public flags. - static bool HasVersionFlag(const QuicEncryptedPacket& packet); - - // Calculates the largest observed packet to advertise in the case an Ack - // Frame was truncated. last_written in this case is the iterator for the - // last missing packet which fit in the outgoing ack. - static QuicPacketSequenceNumber CalculateLargestObserved( - const SequenceNumberSet& missing_packets, - SequenceNumberSet::const_iterator last_written); - // Set callbacks to be called from the framer. A visitor must be set, or // else the framer will likely crash. It is acceptable for the visitor // to do nothing. If this is called multiple times, only the last visitor @@ -239,22 +258,27 @@ class NET_EXPORT_PRIVATE QuicFramer { static size_t GetMinStreamFrameSize(QuicVersion version, QuicStreamId stream_id, QuicStreamOffset offset, - bool last_frame_in_packet); + bool last_frame_in_packet, + InFecGroup is_in_fec_group); // Size in bytes of all ack frame fields without the missing packets. static size_t GetMinAckFrameSize( QuicVersion version, QuicSequenceNumberLength sequence_number_length, QuicSequenceNumberLength largest_observed_length); + // Size in bytes of a stop waiting frame. + static size_t GetStopWaitingFrameSize( + QuicSequenceNumberLength sequence_number_length); // Size in bytes of all reset stream frame without the error details. - static size_t GetMinRstStreamFrameSize(); + static size_t GetMinRstStreamFrameSize(QuicVersion quic_version); // Size in bytes of all connection close frame fields without the error // details and the missing packets from the enclosed ack frame. static size_t GetMinConnectionCloseFrameSize(); // Size in bytes of all GoAway frame fields without the reason phrase. static size_t GetMinGoAwayFrameSize(); - // The maximum number of nacks which can be transmitted in a single ack packet - // without exceeding kDefaultMaxPacketSize. - static size_t GetMaxUnackedPackets(QuicPacketHeader header); + // Size in bytes of all WindowUpdate frame fields. + static size_t GetWindowUpdateFrameSize(); + // Size in bytes of all Blocked frame fields. + static size_t GetBlockedFrameSize(); // Size in bytes required to serialize the stream id. static size_t GetStreamIdSize(QuicStreamId stream_id); // Size in bytes required to serialize the stream offset. @@ -262,36 +286,26 @@ class NET_EXPORT_PRIVATE QuicFramer { // Size in bytes required for a serialized version negotiation packet static size_t GetVersionNegotiationPacketSize(size_t number_versions); - - static bool CanTruncate( - QuicVersion version, const QuicFrame& frame, size_t free_bytes); - // 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, - bool last_frame, + bool first_frame_in_packet, + bool last_frame_in_packet, + InFecGroup is_in_fec_group, QuicSequenceNumberLength sequence_number_length); // Returns the associated data from the encrypted packet |encrypted| as a // stringpiece. static base::StringPiece GetAssociatedDataFromEncryptedPacket( const QuicEncryptedPacket& encrypted, - QuicGuidLength guid_length, + QuicConnectionIdLength connection_id_length, bool includes_version, QuicSequenceNumberLength sequence_number_length); // Returns a SerializedPacket whose |packet| member is owned by the caller, - // and is populated with the fields in |header| and |frames|, or is NULL if - // the packet could not be created. - // TODO(ianswett): Used for testing only. - SerializedPacket BuildUnsizedDataPacket(const QuicPacketHeader& header, - const QuicFrames& frames); - - // Returns a SerializedPacket whose |packet| member is owned by the caller, // is created from the first |num_frames| frames, or is NULL if the packet // could not be created. The packet must be of size |packet_size|. SerializedPacket BuildDataPacket(const QuicPacketHeader& header, @@ -315,16 +329,18 @@ class NET_EXPORT_PRIVATE QuicFramer { // SetDecrypter sets the primary decrypter, replacing any that already exists, // and takes ownership. If an alternative decrypter is in place then the // function DCHECKs. This is intended for cases where one knows that future - // packets will be using the new decrypter and the previous decrypter is not - // obsolete. - void SetDecrypter(QuicDecrypter* decrypter); + // packets will be using the new decrypter and the previous decrypter is now + // obsolete. |level| indicates the encryption level of the new decrypter. + void SetDecrypter(QuicDecrypter* decrypter, EncryptionLevel level); // SetAlternativeDecrypter sets a decrypter that may be used to decrypt - // future packets and takes ownership of it. If |latch_once_used| is true, - // then the first time that the decrypter is successful it will replace the - // primary decrypter. Otherwise both decrypters will remain active and the - // primary decrypter will be the one last used. + // future packets and takes ownership of it. |level| indicates the encryption + // level of the decrypter. If |latch_once_used| is true, then the first time + // that the decrypter is successful it will replace the primary decrypter. + // Otherwise both decrypters will remain active and the primary decrypter + // will be the one last used. void SetAlternativeDecrypter(QuicDecrypter* decrypter, + EncryptionLevel level, bool latch_once_used); const QuicDecrypter* decrypter() const; @@ -335,10 +351,6 @@ class NET_EXPORT_PRIVATE QuicFramer { void SetEncrypter(EncryptionLevel level, QuicEncrypter* encrypter); const QuicEncrypter* encrypter(EncryptionLevel level) const; - // SwapCryptersForTest exchanges the state of the crypters with |other|. To - // be used in tests only. - void SwapCryptersForTest(QuicFramer* other); - // Returns a new encrypted packet, owned by the caller. QuicEncryptedPacket* EncryptPacket(EncryptionLevel level, QuicPacketSequenceNumber sequence_number, @@ -350,17 +362,19 @@ class NET_EXPORT_PRIVATE QuicFramer { const std::string& detailed_error() { return detailed_error_; } - // Read the full 8 byte guid from a packet header. - // Return true on success, else false. - static bool ReadGuidFromPacket(const QuicEncryptedPacket& packet, - QuicGuid* guid); - - static QuicSequenceNumberLength ReadSequenceNumberLength(uint8 flags); - // The minimum sequence number length required to represent |sequence_number|. static QuicSequenceNumberLength GetMinSequenceNumberLength( QuicPacketSequenceNumber sequence_number); + void SetSupportedVersions(const QuicVersionVector& versions) { + supported_versions_ = versions; + quic_version_ = versions[0]; + } + + void set_validate_flags(bool value) { validate_flags_ = value; } + + bool is_server() const { return is_server_; } + private: friend class test::QuicFramerPeer; @@ -400,13 +414,15 @@ class NET_EXPORT_PRIVATE QuicFramer { uint8 frame_type, QuicAckFrame* frame); bool ProcessReceivedInfo(uint8 frame_type, ReceivedPacketInfo* received_info); - bool ProcessSentInfo(const QuicPacketHeader& public_header, - SentPacketInfo* sent_info); + bool ProcessStopWaitingFrame(const QuicPacketHeader& public_header, + QuicStopWaitingFrame* stop_waiting); bool ProcessQuicCongestionFeedbackFrame( QuicCongestionFeedbackFrame* congestion_feedback); bool ProcessRstStreamFrame(QuicRstStreamFrame* frame); bool ProcessConnectionCloseFrame(QuicConnectionCloseFrame* frame); bool ProcessGoAwayFrame(QuicGoAwayFrame* frame); + bool ProcessWindowUpdateFrame(QuicWindowUpdateFrame* frame); + bool ProcessBlockedFrame(QuicBlockedFrame* frame); bool DecryptPayload(const QuicPacketHeader& header, const QuicEncryptedPacket& packet); @@ -424,6 +440,7 @@ class NET_EXPORT_PRIVATE QuicFramer { // Computes the wire size in bytes of the payload of |frame|. size_t ComputeFrameLength(const QuicFrame& frame, bool last_frame_in_packet, + InFecGroup is_in_fec_group, QuicSequenceNumberLength sequence_number_length); static bool AppendPacketSequenceNumber( @@ -436,27 +453,34 @@ class NET_EXPORT_PRIVATE QuicFramer { static AckFrameInfo GetAckFrameInfo(const QuicAckFrame& frame); + // The Append* methods attempt to write the provided header or frame using the + // |writer|, and return true if successful. bool AppendPacketHeader(const QuicPacketHeader& header, QuicDataWriter* writer); bool AppendTypeByte(const QuicFrame& frame, bool last_frame_in_packet, QuicDataWriter* writer); - bool AppendStreamFramePayload(const QuicStreamFrame& frame, - bool last_frame_in_packet, - QuicDataWriter* builder); - bool AppendAckFramePayloadAndTypeByte(const QuicPacketHeader& header, - const QuicAckFrame& frame, - QuicDataWriter* builder); - bool AppendQuicCongestionFeedbackFramePayload( - const QuicCongestionFeedbackFrame& frame, - QuicDataWriter* builder); - bool AppendRstStreamFramePayload(const QuicRstStreamFrame& frame, - QuicDataWriter* builder); - bool AppendConnectionCloseFramePayload( - const QuicConnectionCloseFrame& frame, - QuicDataWriter* builder); - bool AppendGoAwayFramePayload(const QuicGoAwayFrame& frame, - QuicDataWriter* writer); + bool AppendStreamFrame(const QuicStreamFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* builder); + bool AppendAckFrameAndTypeByte(const QuicPacketHeader& header, + const QuicAckFrame& frame, + QuicDataWriter* builder); + bool AppendCongestionFeedbackFrame(const QuicCongestionFeedbackFrame& frame, + QuicDataWriter* builder); + bool AppendStopWaitingFrame(const QuicPacketHeader& header, + const QuicStopWaitingFrame& frame, + QuicDataWriter* builder); + bool AppendRstStreamFrame(const QuicRstStreamFrame& frame, + QuicDataWriter* builder); + bool AppendConnectionCloseFrame(const QuicConnectionCloseFrame& frame, + QuicDataWriter* builder); + bool AppendGoAwayFrame(const QuicGoAwayFrame& frame, QuicDataWriter* writer); + bool AppendWindowUpdateFrame(const QuicWindowUpdateFrame& frame, + QuicDataWriter* writer); + bool AppendBlockedFrame(const QuicBlockedFrame& frame, + QuicDataWriter* writer); + bool RaiseError(QuicErrorCode error); void set_error(QuicErrorCode error) { @@ -476,7 +500,7 @@ class NET_EXPORT_PRIVATE QuicFramer { // Updated by ProcessPacketHeader when it succeeds. QuicPacketSequenceNumber last_sequence_number_; // Updated by WritePacketHeader. - QuicGuid last_serialized_guid_; + QuicConnectionId last_serialized_connection_id_; // Buffer containing decrypted payload data during parsing. scoped_ptr<QuicData> decrypted_; // Version of the protocol being used. @@ -490,7 +514,11 @@ class NET_EXPORT_PRIVATE QuicFramer { scoped_ptr<QuicDecrypter> decrypter_; // Alternative decrypter that can also be used to decrypt packets. scoped_ptr<QuicDecrypter> alternative_decrypter_; - // alternative_decrypter_latch_is true if, when |alternative_decrypter_| + // The encryption level of |decrypter_|. + EncryptionLevel decrypter_level_; + // The encryption level of |alternative_decrypter_|. + EncryptionLevel alternative_decrypter_level_; + // |alternative_decrypter_latch_| is true if, when |alternative_decrypter_| // successfully decrypts a packet, we should install it as the only // decrypter. bool alternative_decrypter_latch_; @@ -499,6 +527,8 @@ class NET_EXPORT_PRIVATE QuicFramer { // Tracks if the framer is being used by the entity that received the // connection or the entity that initiated it. bool is_server_; + // If false, skip validation that the public flags are set to legal values. + bool validate_flags_; // The time this frames was created. Time written to the wire will be // written as a delta from this value. QuicTime creation_time_; diff --git a/chromium/net/quic/quic_framer_test.cc b/chromium/net/quic/quic_framer_test.cc index eef4a5894c1..fd206e18620 100644 --- a/chromium/net/quic/quic_framer_test.cc +++ b/chromium/net/quic/quic_framer_test.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "net/quic/quic_framer.h" + #include <algorithm> #include <map> #include <string> @@ -14,11 +16,11 @@ #include "base/stl_util.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" -#include "net/quic/quic_framer.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_utils.h" #include "net/quic/test_tools/quic_framer_peer.h" #include "net/quic/test_tools/quic_test_utils.h" +#include "net/test/gtest_util.h" using base::hash_set; using base::StringPiece; @@ -37,46 +39,54 @@ namespace test { const QuicPacketSequenceNumber kEpoch = GG_UINT64_C(1) << 48; const QuicPacketSequenceNumber kMask = kEpoch - 1; -// Index into the guid offset in the header. -const size_t kGuidOffset = kPublicFlagsSize; +// Index into the connection_id offset in the header. +const size_t kConnectionIdOffset = kPublicFlagsSize; // Index into the version string in the header. (if present). -const size_t kVersionOffset = kGuidOffset + PACKET_8BYTE_GUID; +const size_t kVersionOffset = kConnectionIdOffset + PACKET_8BYTE_CONNECTION_ID; + +// Size in bytes of the stream frame fields for an arbitrary StreamID and +// offset and the last frame in a packet. +size_t GetMinStreamFrameSize(QuicVersion version) { + return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize; +} // Index into the sequence number offset in the header. -size_t GetSequenceNumberOffset(QuicGuidLength guid_length, +size_t GetSequenceNumberOffset(QuicConnectionIdLength connection_id_length, bool include_version) { - return kGuidOffset + guid_length + + return kConnectionIdOffset + connection_id_length + (include_version ? kQuicVersionSize : 0); } size_t GetSequenceNumberOffset(bool include_version) { - return GetSequenceNumberOffset(PACKET_8BYTE_GUID, include_version); + return GetSequenceNumberOffset(PACKET_8BYTE_CONNECTION_ID, include_version); } // Index into the private flags offset in the data packet header. -size_t GetPrivateFlagsOffset(QuicGuidLength guid_length, bool include_version) { - return GetSequenceNumberOffset(guid_length, include_version) + +size_t GetPrivateFlagsOffset(QuicConnectionIdLength connection_id_length, + bool include_version) { + return GetSequenceNumberOffset(connection_id_length, include_version) + PACKET_6BYTE_SEQUENCE_NUMBER; } size_t GetPrivateFlagsOffset(bool include_version) { - return GetPrivateFlagsOffset(PACKET_8BYTE_GUID, include_version); + return GetPrivateFlagsOffset(PACKET_8BYTE_CONNECTION_ID, include_version); } size_t GetPrivateFlagsOffset(bool include_version, QuicSequenceNumberLength sequence_number_length) { - return GetSequenceNumberOffset(PACKET_8BYTE_GUID, include_version) + + return GetSequenceNumberOffset(PACKET_8BYTE_CONNECTION_ID, include_version) + sequence_number_length; } // Index into the fec group offset in the header. -size_t GetFecGroupOffset(QuicGuidLength guid_length, bool include_version) { - return GetPrivateFlagsOffset(guid_length, include_version) + +size_t GetFecGroupOffset(QuicConnectionIdLength connection_id_length, + bool include_version) { + return GetPrivateFlagsOffset(connection_id_length, include_version) + kPrivateFlagsSize; } size_t GetFecGroupOffset(bool include_version) { - return GetPrivateFlagsOffset(PACKET_8BYTE_GUID, include_version) + + return GetPrivateFlagsOffset(PACKET_8BYTE_CONNECTION_ID, include_version) + kPrivateFlagsSize; } @@ -86,14 +96,10 @@ size_t GetFecGroupOffset(bool include_version, kPrivateFlagsSize; } -// Index into the nonce proof of the public reset packet. -// Public resets always have full guids. -const size_t kPublicResetPacketNonceProofOffset = - kGuidOffset + PACKET_8BYTE_GUID; - -// Index into the rejected sequence number of the public reset packet. -const size_t kPublicResetPacketRejectedSequenceNumberOffset = - kPublicResetPacketNonceProofOffset + kPublicResetNonceSize; +// Index into the message tag of the public reset packet. +// Public resets always have full connection_ids. +const size_t kPublicResetPacketMessageTagOffset = + kConnectionIdOffset + PACKET_8BYTE_CONNECTION_ID; class TestEncrypter : public QuicEncrypter { public: @@ -188,20 +194,23 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface { fec_count_(0), complete_packets_(0), revived_packets_(0), - accept_packet_(true) { + accept_packet_(true), + accept_public_header_(true) { } virtual ~TestQuicVisitor() { STLDeleteElements(&stream_frames_); STLDeleteElements(&ack_frames_); STLDeleteElements(&congestion_feedback_frames_); + STLDeleteElements(&stop_waiting_frames_); + STLDeleteElements(&ping_frames_); STLDeleteElements(&fec_data_); } virtual void OnError(QuicFramer* f) OVERRIDE { - DLOG(INFO) << "QuicFramer Error: " << QuicUtils::ErrorToString(f->error()) - << " (" << f->error() << ")"; - error_count_++; + DVLOG(1) << "QuicFramer Error: " << QuicUtils::ErrorToString(f->error()) + << " (" << f->error() << ")"; + ++error_count_; } virtual void OnPacket() OVERRIDE {} @@ -217,19 +226,19 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface { } virtual void OnRevivedPacket() OVERRIDE { - revived_packets_++; + ++revived_packets_; } virtual bool OnProtocolVersionMismatch(QuicVersion version) OVERRIDE { - DLOG(INFO) << "QuicFramer Version Mismatch, version: " << version; - version_mismatch_++; + DVLOG(1) << "QuicFramer Version Mismatch, version: " << version; + ++version_mismatch_; return true; } - virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE { - packet_count_++; - header_.reset(new QuicPacketHeader(header)); - return accept_packet_; + virtual bool OnUnauthenticatedPublicHeader( + const QuicPacketPublicHeader& header) OVERRIDE { + public_header_.reset(new QuicPacketPublicHeader(header)); + return accept_public_header_; } virtual bool OnUnauthenticatedHeader( @@ -237,8 +246,16 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface { return true; } + virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE {} + + virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE { + ++packet_count_; + header_.reset(new QuicPacketHeader(header)); + return accept_packet_; + } + virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE { - frame_count_++; + ++frame_count_; stream_frames_.push_back(new QuicStreamFrame(frame)); return true; } @@ -248,26 +265,38 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface { } virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE { - frame_count_++; + ++frame_count_; ack_frames_.push_back(new QuicAckFrame(frame)); return true; } virtual bool OnCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& frame) OVERRIDE { - frame_count_++; + ++frame_count_; congestion_feedback_frames_.push_back( new QuicCongestionFeedbackFrame(frame)); return true; } + virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) OVERRIDE { + ++frame_count_; + stop_waiting_frames_.push_back(new QuicStopWaitingFrame(frame)); + return true; + } + + virtual bool OnPingFrame(const QuicPingFrame& frame) OVERRIDE { + ++frame_count_; + ping_frames_.push_back(new QuicPingFrame(frame)); + return true; + } + virtual void OnFecData(const QuicFecData& fec) OVERRIDE { - fec_count_++; + ++fec_count_; fec_data_.push_back(new QuicFecData(fec)); } virtual void OnPacketComplete() OVERRIDE { - complete_packets_++; + ++complete_packets_; } virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE { @@ -286,6 +315,17 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface { return true; } + virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) + OVERRIDE { + window_update_frame_ = frame; + return true; + } + + virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE { + blocked_frame_ = frame; + return true; + } + // Counters from the visitor_ callbacks. int error_count_; int version_mismatch_; @@ -295,18 +335,24 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface { int complete_packets_; int revived_packets_; bool accept_packet_; + bool accept_public_header_; scoped_ptr<QuicPacketHeader> header_; + scoped_ptr<QuicPacketPublicHeader> public_header_; scoped_ptr<QuicPublicResetPacket> public_reset_packet_; scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_; vector<QuicStreamFrame*> stream_frames_; vector<QuicAckFrame*> ack_frames_; vector<QuicCongestionFeedbackFrame*> congestion_feedback_frames_; + vector<QuicStopWaitingFrame*> stop_waiting_frames_; + vector<QuicPingFrame*> ping_frames_; vector<QuicFecData*> fec_data_; string fec_protected_payload_; QuicRstStreamFrame rst_stream_frame_; QuicConnectionCloseFrame connection_close_frame_; QuicGoAwayFrame goaway_frame_; + QuicWindowUpdateFrame window_update_frame_; + QuicBlockedFrame blocked_frame_; }; class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> { @@ -318,7 +364,7 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> { framer_(QuicSupportedVersions(), start_, true) { version_ = GetParam(); framer_.set_version(version_); - framer_.SetDecrypter(decrypter_); + framer_.SetDecrypter(decrypter_, ENCRYPTION_NONE); framer_.SetEncrypter(ENCRYPTION_NONE, encrypter_); framer_.set_visitor(&visitor_); framer_.set_received_entropy_calculator(&entropy_calculator_); @@ -369,18 +415,18 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> { return false; } if (QuicFramer::GetAssociatedDataFromEncryptedPacket( - encrypted, PACKET_8BYTE_GUID, + encrypted, PACKET_8BYTE_CONNECTION_ID, includes_version, PACKET_6BYTE_SEQUENCE_NUMBER) != decrypter_->associated_data_) { LOG(ERROR) << "Decrypted incorrect associated data. expected " << QuicFramer::GetAssociatedDataFromEncryptedPacket( - encrypted, PACKET_8BYTE_GUID, + encrypted, PACKET_8BYTE_CONNECTION_ID, includes_version, PACKET_6BYTE_SEQUENCE_NUMBER) << " actual: " << decrypter_->associated_data_; return false; } StringPiece ciphertext(encrypted.AsStringPiece().substr( - GetStartOfEncryptedData(PACKET_8BYTE_GUID, includes_version, + GetStartOfEncryptedData(PACKET_8BYTE_CONNECTION_ID, includes_version, PACKET_6BYTE_SEQUENCE_NUMBER))); if (ciphertext != decrypter_->ciphertext_) { LOG(ERROR) << "Decrypted incorrect ciphertext data. expected " @@ -428,7 +474,7 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> { } CheckProcessingFails( packet, - i + GetPacketHeaderSize(PACKET_8BYTE_GUID, include_version, + i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, include_version, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), expected_error, QUIC_INVALID_STREAM_DATA); @@ -448,6 +494,11 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> { << " wire_sequence_number: " << wire_sequence_number; } + QuicPacket* BuildDataPacket(const QuicPacketHeader& header, + const QuicFrames& frames) { + return BuildUnsizedDataPacket(&framer_, header, frames).packet; + } + test::TestEncrypter* encrypter_; test::TestDecrypter* decrypter_; QuicVersion version_; @@ -572,9 +623,9 @@ TEST_P(QuicFramerTest, EmptyPacket) { TEST_P(QuicFramerTest, LargePacket) { unsigned char packet[kMaxPacketSize + 1] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -585,10 +636,10 @@ TEST_P(QuicFramerTest, LargePacket) { }; memset(packet + GetPacketHeaderSize( - PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), 0, kMaxPacketSize - GetPacketHeaderSize( - PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP) + 1); QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); @@ -597,16 +648,16 @@ TEST_P(QuicFramerTest, LargePacket) { ASSERT_TRUE(visitor_.header_.get()); // Make sure we've parsed the packet header, so we can send an error. EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), - visitor_.header_->public_header.guid); + visitor_.header_->public_header.connection_id); // Make sure the correct error is propagated. EXPECT_EQ(QUIC_PACKET_TOO_LARGE, framer_.error()); } TEST_P(QuicFramerTest, PacketHeader) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -621,7 +672,7 @@ TEST_P(QuicFramerTest, PacketHeader) { EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), - visitor_.header_->public_header.guid); + visitor_.header_->public_header.connection_id); EXPECT_FALSE(visitor_.header_->public_header.reset_flag); EXPECT_FALSE(visitor_.header_->public_header.version_flag); EXPECT_FALSE(visitor_.header_->fec_flag); @@ -634,14 +685,14 @@ TEST_P(QuicFramerTest, PacketHeader) { // Now test framing boundaries for (size_t i = 0; - i < GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); ++i) { string expected_error; - if (i < kGuidOffset) { + if (i < kConnectionIdOffset) { expected_error = "Unable to read public flags."; } else if (i < GetSequenceNumberOffset(!kIncludeVersion)) { - expected_error = "Unable to read GUID."; + expected_error = "Unable to read ConnectionId."; } else if (i < GetPrivateFlagsOffset(!kIncludeVersion)) { expected_error = "Unable to read sequence number."; } else if (i < GetFecGroupOffset(!kIncludeVersion)) { @@ -653,14 +704,14 @@ TEST_P(QuicFramerTest, PacketHeader) { } } -TEST_P(QuicFramerTest, PacketHeaderWith4ByteGuid) { - QuicFramerPeer::SetLastSerializedGuid(&framer_, - GG_UINT64_C(0xFEDCBA9876543210)); +TEST_P(QuicFramerTest, PacketHeaderWith4ByteConnectionId) { + QuicFramerPeer::SetLastSerializedConnectionId( + &framer_, GG_UINT64_C(0xFEDCBA9876543210)); unsigned char packet[] = { - // public flags (4 byte guid) + // public flags (4 byte connection_id) 0x38, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, @@ -674,7 +725,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteGuid) { EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), - visitor_.header_->public_header.guid); + visitor_.header_->public_header.connection_id); EXPECT_FALSE(visitor_.header_->public_header.reset_flag); EXPECT_FALSE(visitor_.header_->public_header.version_flag); EXPECT_FALSE(visitor_.header_->fec_flag); @@ -687,19 +738,20 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteGuid) { // Now test framing boundaries for (size_t i = 0; - i < GetPacketHeaderSize(PACKET_4BYTE_GUID, !kIncludeVersion, + i < GetPacketHeaderSize(PACKET_4BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); ++i) { string expected_error; - if (i < kGuidOffset) { + if (i < kConnectionIdOffset) { expected_error = "Unable to read public flags."; - } else if (i < GetSequenceNumberOffset(PACKET_4BYTE_GUID, + } else if (i < GetSequenceNumberOffset(PACKET_4BYTE_CONNECTION_ID, !kIncludeVersion)) { - expected_error = "Unable to read GUID."; - } else if (i < GetPrivateFlagsOffset(PACKET_4BYTE_GUID, + expected_error = "Unable to read ConnectionId."; + } else if (i < GetPrivateFlagsOffset(PACKET_4BYTE_CONNECTION_ID, !kIncludeVersion)) { expected_error = "Unable to read sequence number."; - } else if (i < GetFecGroupOffset(PACKET_4BYTE_GUID, !kIncludeVersion)) { + } else if (i < GetFecGroupOffset(PACKET_4BYTE_CONNECTION_ID, + !kIncludeVersion)) { expected_error = "Unable to read private flags."; } else { expected_error = "Unable to read first fec protected packet offset."; @@ -708,14 +760,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteGuid) { } } -TEST_P(QuicFramerTest, PacketHeader1ByteGuid) { - QuicFramerPeer::SetLastSerializedGuid(&framer_, - GG_UINT64_C(0xFEDCBA9876543210)); +TEST_P(QuicFramerTest, PacketHeader1ByteConnectionId) { + QuicFramerPeer::SetLastSerializedConnectionId( + &framer_, GG_UINT64_C(0xFEDCBA9876543210)); unsigned char packet[] = { - // public flags (1 byte guid) + // public flags (1 byte connection_id) 0x34, - // guid + // connection_id 0x10, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, @@ -729,7 +781,7 @@ TEST_P(QuicFramerTest, PacketHeader1ByteGuid) { EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), - visitor_.header_->public_header.guid); + visitor_.header_->public_header.connection_id); EXPECT_FALSE(visitor_.header_->public_header.reset_flag); EXPECT_FALSE(visitor_.header_->public_header.version_flag); EXPECT_FALSE(visitor_.header_->fec_flag); @@ -742,18 +794,20 @@ TEST_P(QuicFramerTest, PacketHeader1ByteGuid) { // Now test framing boundaries for (size_t i = 0; - i < GetPacketHeaderSize(PACKET_1BYTE_GUID, !kIncludeVersion, + i < GetPacketHeaderSize(PACKET_1BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); ++i) { string expected_error; - if (i < kGuidOffset) { + if (i < kConnectionIdOffset) { expected_error = "Unable to read public flags."; - } else if (i < GetSequenceNumberOffset(PACKET_1BYTE_GUID, + } else if (i < GetSequenceNumberOffset(PACKET_1BYTE_CONNECTION_ID, !kIncludeVersion)) { - expected_error = "Unable to read GUID."; - } else if (i < GetPrivateFlagsOffset(PACKET_1BYTE_GUID, !kIncludeVersion)) { + expected_error = "Unable to read ConnectionId."; + } else if (i < GetPrivateFlagsOffset(PACKET_1BYTE_CONNECTION_ID, + !kIncludeVersion)) { expected_error = "Unable to read sequence number."; - } else if (i < GetFecGroupOffset(PACKET_1BYTE_GUID, !kIncludeVersion)) { + } else if (i < GetFecGroupOffset(PACKET_1BYTE_CONNECTION_ID, + !kIncludeVersion)) { expected_error = "Unable to read private flags."; } else { expected_error = "Unable to read first fec protected packet offset."; @@ -762,14 +816,14 @@ TEST_P(QuicFramerTest, PacketHeader1ByteGuid) { } } -TEST_P(QuicFramerTest, PacketHeaderWith0ByteGuid) { - QuicFramerPeer::SetLastSerializedGuid(&framer_, - GG_UINT64_C(0xFEDCBA9876543210)); +TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) { + QuicFramerPeer::SetLastSerializedConnectionId( + &framer_, GG_UINT64_C(0xFEDCBA9876543210)); unsigned char packet[] = { - // public flags (0 byte guid) + // public flags (0 byte connection_id) 0x30, - // guid + // connection_id // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -782,7 +836,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith0ByteGuid) { EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), - visitor_.header_->public_header.guid); + visitor_.header_->public_header.connection_id); EXPECT_FALSE(visitor_.header_->public_header.reset_flag); EXPECT_FALSE(visitor_.header_->public_header.version_flag); EXPECT_FALSE(visitor_.header_->fec_flag); @@ -795,18 +849,20 @@ TEST_P(QuicFramerTest, PacketHeaderWith0ByteGuid) { // Now test framing boundaries for (size_t i = 0; - i < GetPacketHeaderSize(PACKET_0BYTE_GUID, !kIncludeVersion, + i < GetPacketHeaderSize(PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); ++i) { string expected_error; - if (i < kGuidOffset) { + if (i < kConnectionIdOffset) { expected_error = "Unable to read public flags."; - } else if (i < GetSequenceNumberOffset(PACKET_0BYTE_GUID, + } else if (i < GetSequenceNumberOffset(PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion)) { - expected_error = "Unable to read GUID."; - } else if (i < GetPrivateFlagsOffset(PACKET_0BYTE_GUID, !kIncludeVersion)) { + expected_error = "Unable to read ConnectionId."; + } else if (i < GetPrivateFlagsOffset(PACKET_0BYTE_CONNECTION_ID, + !kIncludeVersion)) { expected_error = "Unable to read sequence number."; - } else if (i < GetFecGroupOffset(PACKET_0BYTE_GUID, !kIncludeVersion)) { + } else if (i < GetFecGroupOffset(PACKET_0BYTE_CONNECTION_ID, + !kIncludeVersion)) { expected_error = "Unable to read private flags."; } else { expected_error = "Unable to read first fec protected packet offset."; @@ -819,7 +875,7 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) { unsigned char packet[] = { // public flags (version) 0x3D, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag @@ -836,7 +892,7 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) { EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), - visitor_.header_->public_header.guid); + visitor_.header_->public_header.connection_id); EXPECT_FALSE(visitor_.header_->public_header.reset_flag); EXPECT_TRUE(visitor_.header_->public_header.version_flag); EXPECT_EQ(GetParam(), visitor_.header_->public_header.versions[0]); @@ -850,14 +906,14 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) { // Now test framing boundaries for (size_t i = 0; - i < GetPacketHeaderSize(PACKET_8BYTE_GUID, kIncludeVersion, + i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); ++i) { string expected_error; - if (i < kGuidOffset) { + if (i < kConnectionIdOffset) { expected_error = "Unable to read public flags."; } else if (i < kVersionOffset) { - expected_error = "Unable to read GUID."; + expected_error = "Unable to read ConnectionId."; } else if (i < GetSequenceNumberOffset(kIncludeVersion)) { expected_error = "Unable to read protocol version."; } else if (i < GetPrivateFlagsOffset(kIncludeVersion)) { @@ -876,9 +932,9 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteSequenceNumber) { GG_UINT64_C(0x123456789ABA)); unsigned char packet[] = { - // public flags (8 byte guid and 4 byte sequence number) + // public flags (8 byte connection_id and 4 byte sequence number) 0x2C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -892,7 +948,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteSequenceNumber) { EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), - visitor_.header_->public_header.guid); + visitor_.header_->public_header.connection_id); EXPECT_FALSE(visitor_.header_->public_header.reset_flag); EXPECT_FALSE(visitor_.header_->public_header.version_flag); EXPECT_FALSE(visitor_.header_->fec_flag); @@ -905,14 +961,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteSequenceNumber) { // Now test framing boundaries for (size_t i = 0; - i < GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_4BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); ++i) { string expected_error; - if (i < kGuidOffset) { + if (i < kConnectionIdOffset) { expected_error = "Unable to read public flags."; } else if (i < GetSequenceNumberOffset(!kIncludeVersion)) { - expected_error = "Unable to read GUID."; + expected_error = "Unable to read ConnectionId."; } else if (i < GetPrivateFlagsOffset(!kIncludeVersion, PACKET_4BYTE_SEQUENCE_NUMBER)) { expected_error = "Unable to read sequence number."; @@ -931,9 +987,9 @@ TEST_P(QuicFramerTest, PacketHeaderWith2ByteSequenceNumber) { GG_UINT64_C(0x123456789ABA)); unsigned char packet[] = { - // public flags (8 byte guid and 2 byte sequence number) + // public flags (8 byte connection_id and 2 byte sequence number) 0x1C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -947,7 +1003,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith2ByteSequenceNumber) { EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), - visitor_.header_->public_header.guid); + visitor_.header_->public_header.connection_id); EXPECT_FALSE(visitor_.header_->public_header.reset_flag); EXPECT_FALSE(visitor_.header_->public_header.version_flag); EXPECT_FALSE(visitor_.header_->fec_flag); @@ -960,14 +1016,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith2ByteSequenceNumber) { // Now test framing boundaries for (size_t i = 0; - i < GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_2BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); ++i) { string expected_error; - if (i < kGuidOffset) { + if (i < kConnectionIdOffset) { expected_error = "Unable to read public flags."; } else if (i < GetSequenceNumberOffset(!kIncludeVersion)) { - expected_error = "Unable to read GUID."; + expected_error = "Unable to read ConnectionId."; } else if (i < GetPrivateFlagsOffset(!kIncludeVersion, PACKET_2BYTE_SEQUENCE_NUMBER)) { expected_error = "Unable to read sequence number."; @@ -986,9 +1042,9 @@ TEST_P(QuicFramerTest, PacketHeaderWith1ByteSequenceNumber) { GG_UINT64_C(0x123456789ABA)); unsigned char packet[] = { - // public flags (8 byte guid and 1 byte sequence number) + // public flags (8 byte connection_id and 1 byte sequence number) 0x0C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -1002,7 +1058,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith1ByteSequenceNumber) { EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), - visitor_.header_->public_header.guid); + visitor_.header_->public_header.connection_id); EXPECT_FALSE(visitor_.header_->public_header.reset_flag); EXPECT_FALSE(visitor_.header_->public_header.version_flag); EXPECT_FALSE(visitor_.header_->fec_flag); @@ -1015,14 +1071,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith1ByteSequenceNumber) { // Now test framing boundaries for (size_t i = 0; - i < GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); ++i) { string expected_error; - if (i < kGuidOffset) { + if (i < kConnectionIdOffset) { expected_error = "Unable to read public flags."; } else if (i < GetSequenceNumberOffset(!kIncludeVersion)) { - expected_error = "Unable to read GUID."; + expected_error = "Unable to read ConnectionId."; } else if (i < GetPrivateFlagsOffset(!kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER)) { expected_error = "Unable to read sequence number."; @@ -1038,9 +1094,9 @@ TEST_P(QuicFramerTest, PacketHeaderWith1ByteSequenceNumber) { TEST_P(QuicFramerTest, InvalidPublicFlag) { unsigned char packet[] = { - // public flags, unknown flag at bit 6 - 0x40, - // guid + // public flags: all flags set but the public reset flag and version flag. + 0xFC, + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -1057,13 +1113,18 @@ TEST_P(QuicFramerTest, InvalidPublicFlag) { arraysize(packet), "Illegal public flags value.", QUIC_INVALID_PACKET_HEADER); + + // Now turn off validation. + framer_.set_validate_flags(false); + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); }; TEST_P(QuicFramerTest, InvalidPublicFlagWithMatchingVersions) { unsigned char packet[] = { - // public flags (8 byte guid and version flag and an unknown flag) + // public flags (8 byte connection_id and version flag and an unknown flag) 0x4D, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag @@ -1086,9 +1147,9 @@ TEST_P(QuicFramerTest, InvalidPublicFlagWithMatchingVersions) { TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) { unsigned char packet[] = { - // public flags (8 byte guid, version flag and an unknown flag) + // public flags (8 byte connection_id, version flag and an unknown flag) 0x7D, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag @@ -1113,9 +1174,9 @@ TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) { TEST_P(QuicFramerTest, InvalidPrivateFlag) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -1136,9 +1197,9 @@ TEST_P(QuicFramerTest, InvalidPrivateFlag) { TEST_P(QuicFramerTest, InvalidFECGroupOffset) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -1158,9 +1219,9 @@ TEST_P(QuicFramerTest, InvalidFECGroupOffset) { TEST_P(QuicFramerTest, PaddingFrame) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -1198,16 +1259,16 @@ TEST_P(QuicFramerTest, PaddingFrame) { // A packet with no frames is not acceptable. CheckProcessingFails( packet, - GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), "Packet has no frames.", QUIC_MISSING_PAYLOAD); } TEST_P(QuicFramerTest, StreamFrame) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -1253,9 +1314,9 @@ TEST_P(QuicFramerTest, StreamFrame) { TEST_P(QuicFramerTest, StreamFrame3ByteStreamId) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -1302,9 +1363,9 @@ TEST_P(QuicFramerTest, StreamFrame3ByteStreamId) { TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -1351,9 +1412,9 @@ TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) { TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -1400,9 +1461,9 @@ TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) { TEST_P(QuicFramerTest, StreamFrameWithVersion) { unsigned char packet[] = { - // public flags (version, 8 byte guid) + // public flags (version, 8 byte connection_id) 0x3D, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag @@ -1433,8 +1494,8 @@ TEST_P(QuicFramerTest, StreamFrameWithVersion) { EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); - EXPECT_TRUE(visitor_.header_.get()->public_header.version_flag); - EXPECT_EQ(GetParam(), visitor_.header_.get()->public_header.versions[0]); + EXPECT_TRUE(visitor_.header_->public_header.version_flag); + EXPECT_EQ(GetParam(), visitor_.header_->public_header.versions[0]); EXPECT_TRUE(CheckDecryption(encrypted, kIncludeVersion)); ASSERT_EQ(1u, visitor_.stream_frames_.size()); @@ -1454,9 +1515,9 @@ TEST_P(QuicFramerTest, RejectPacket) { visitor_.accept_packet_ = false; unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -1491,6 +1552,25 @@ TEST_P(QuicFramerTest, RejectPacket) { EXPECT_EQ(0u, visitor_.ack_frames_.size()); } +TEST_P(QuicFramerTest, RejectPublicHeader) { + visitor_.accept_public_header_ = false; + + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.public_header_.get()); + ASSERT_FALSE(visitor_.header_.get()); +} + TEST_P(QuicFramerTest, RevivedStreamFrame) { unsigned char payload[] = { // frame type (stream frame with fin) @@ -1509,7 +1589,7 @@ TEST_P(QuicFramerTest, RevivedStreamFrame) { }; QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = true; @@ -1526,7 +1606,7 @@ TEST_P(QuicFramerTest, RevivedStreamFrame) { ASSERT_EQ(1, visitor_.revived_packets_); ASSERT_TRUE(visitor_.header_.get()); EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), - visitor_.header_->public_header.guid); + visitor_.header_->public_header.connection_id); EXPECT_FALSE(visitor_.header_->public_header.reset_flag); EXPECT_FALSE(visitor_.header_->public_header.version_flag); EXPECT_TRUE(visitor_.header_->fec_flag); @@ -1549,9 +1629,9 @@ TEST_P(QuicFramerTest, RevivedStreamFrame) { TEST_P(QuicFramerTest, StreamFrameInFecGroup) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -1586,8 +1666,10 @@ TEST_P(QuicFramerTest, StreamFrameInFecGroup) { EXPECT_EQ(IN_FEC_GROUP, visitor_.header_->is_in_fec_group); EXPECT_EQ(GG_UINT64_C(0x341256789ABA), visitor_.header_->fec_group); - const size_t fec_offset = GetStartOfFecProtectedData( - PACKET_8BYTE_GUID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER); + const size_t fec_offset = + GetStartOfFecProtectedData(PACKET_8BYTE_CONNECTION_ID, + !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER); EXPECT_EQ( string(AsChars(packet) + fec_offset, arraysize(packet) - fec_offset), visitor_.fec_protected_payload_); @@ -1601,11 +1683,15 @@ TEST_P(QuicFramerTest, StreamFrameInFecGroup) { CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]); } -TEST_P(QuicFramerTest, AckFrame) { +TEST_P(QuicFramerTest, AckFrame15) { + if (framer_.version() != QUIC_VERSION_15) { + return; + } + unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -1635,6 +1721,8 @@ TEST_P(QuicFramerTest, AckFrame) { 0x01, // 0 more missing packets in range. 0x00, + // Number of revived packets. + 0x00, }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); @@ -1667,11 +1755,13 @@ TEST_P(QuicFramerTest, AckFrame) { const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset + kQuicDeltaTimeLargestObservedSize; const size_t kMissingPacketsOffset = kNumMissingPacketOffset + - kNumberOfMissingPacketsSize; + kNumberOfNackRangesSize; const size_t kMissingPacketsRange = kMissingPacketsOffset + PACKET_1BYTE_SEQUENCE_NUMBER; + const size_t kRevivedPacketsLength = kMissingPacketsRange + + PACKET_1BYTE_SEQUENCE_NUMBER; // Now test framing boundaries - const size_t ack_frame_size = kMissingPacketsRange + + const size_t ack_frame_size = kRevivedPacketsLength + PACKET_1BYTE_SEQUENCE_NUMBER; for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) { string expected_error; @@ -1689,22 +1779,396 @@ TEST_P(QuicFramerTest, AckFrame) { expected_error = "Unable to read num missing packet ranges."; } else if (i < kMissingPacketsRange) { expected_error = "Unable to read missing sequence number delta."; + } else if (i < kRevivedPacketsLength) { + expected_error = "Unable to read missing sequence number range."; + } else { + expected_error = "Unable to read num revived packets."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_ACK_DATA); + } +} + +TEST_P(QuicFramerTest, AckFrame) { + if (framer_.version() <= QUIC_VERSION_15) { + return; + } + + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (has nacks, not truncated, 6 byte largest observed, 1 byte delta) + 0x6C, + // entropy hash of all received packets. + 0xBA, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Zero delta time. + 0x0, 0x0, + // num missing packets + 0x01, + // missing packet delta + 0x01, + // 0 more missing packets in range. + 0x00, + // Number of revived packets. + 0x00, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(0xBA, frame.received_info.entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed); + ASSERT_EQ(1u, frame.received_info.missing_packets.size()); + SequenceNumberSet::const_iterator missing_iter = + frame.received_info.missing_packets.begin(); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter); + + const size_t kReceivedEntropyOffset = kQuicFrameTypeSize; + const size_t kLargestObservedOffset = kReceivedEntropyOffset + + kQuicEntropyHashSize; + const size_t kMissingDeltaTimeOffset = kLargestObservedOffset + + PACKET_6BYTE_SEQUENCE_NUMBER; + const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset + + kQuicDeltaTimeLargestObservedSize; + const size_t kMissingPacketsOffset = kNumMissingPacketOffset + + kNumberOfNackRangesSize; + const size_t kMissingPacketsRange = kMissingPacketsOffset + + PACKET_1BYTE_SEQUENCE_NUMBER; + const size_t kRevivedPacketsLength = kMissingPacketsRange + + PACKET_1BYTE_SEQUENCE_NUMBER; + // Now test framing boundaries + const size_t ack_frame_size = kRevivedPacketsLength + + PACKET_1BYTE_SEQUENCE_NUMBER; + for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) { + string expected_error; + if (i < kLargestObservedOffset) { + expected_error = "Unable to read entropy hash for received packets."; + } else if (i < kMissingDeltaTimeOffset) { + expected_error = "Unable to read largest observed."; + } else if (i < kNumMissingPacketOffset) { + expected_error = "Unable to read delta time largest observed."; + } else if (i < kMissingPacketsOffset) { + expected_error = "Unable to read num missing packet ranges."; + } else if (i < kMissingPacketsRange) { + expected_error = "Unable to read missing sequence number delta."; + } else if (i < kRevivedPacketsLength) { + expected_error = "Unable to read missing sequence number range."; + } else { + expected_error = "Unable to read num revived packets."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_ACK_DATA); + } +} + +TEST_P(QuicFramerTest, AckFrameRevivedPackets) { + if (framer_.version() <= QUIC_VERSION_15) { + return; + } + + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (has nacks, not truncated, 6 byte largest observed, 1 byte delta) + 0x6C, + // entropy hash of all received packets. + 0xBA, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Zero delta time. + 0x0, 0x0, + // num missing packets + 0x01, + // missing packet delta + 0x01, + // 0 more missing packets in range. + 0x00, + // Number of revived packets. + 0x01, + // Revived packet sequence number. + 0xBE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(0xBA, frame.received_info.entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed); + ASSERT_EQ(1u, frame.received_info.missing_packets.size()); + SequenceNumberSet::const_iterator missing_iter = + frame.received_info.missing_packets.begin(); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter); + + const size_t kReceivedEntropyOffset = kQuicFrameTypeSize; + const size_t kLargestObservedOffset = kReceivedEntropyOffset + + kQuicEntropyHashSize; + const size_t kMissingDeltaTimeOffset = kLargestObservedOffset + + PACKET_6BYTE_SEQUENCE_NUMBER; + const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset + + kQuicDeltaTimeLargestObservedSize; + const size_t kMissingPacketsOffset = kNumMissingPacketOffset + + kNumberOfNackRangesSize; + const size_t kMissingPacketsRange = kMissingPacketsOffset + + PACKET_1BYTE_SEQUENCE_NUMBER; + const size_t kRevivedPacketsLength = kMissingPacketsRange + + PACKET_1BYTE_SEQUENCE_NUMBER; + const size_t kRevivedPacketSequenceNumberLength = kRevivedPacketsLength + + PACKET_1BYTE_SEQUENCE_NUMBER; + // Now test framing boundaries + const size_t ack_frame_size = kRevivedPacketSequenceNumberLength + + PACKET_6BYTE_SEQUENCE_NUMBER; + for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) { + string expected_error; + if (i < kReceivedEntropyOffset) { + expected_error = "Unable to read least unacked delta."; + } else if (i < kLargestObservedOffset) { + expected_error = "Unable to read entropy hash for received packets."; + } else if (i < kMissingDeltaTimeOffset) { + expected_error = "Unable to read largest observed."; + } else if (i < kNumMissingPacketOffset) { + expected_error = "Unable to read delta time largest observed."; + } else if (i < kMissingPacketsOffset) { + expected_error = "Unable to read num missing packet ranges."; + } else if (i < kMissingPacketsRange) { + expected_error = "Unable to read missing sequence number delta."; + } else if (i < kRevivedPacketsLength) { + expected_error = "Unable to read missing sequence number range."; + } else if (i < kRevivedPacketSequenceNumberLength) { + expected_error = "Unable to read num revived packets."; } else { + expected_error = "Unable to read revived packet."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_ACK_DATA); + } +} + +TEST_P(QuicFramerTest, AckFrameRevivedPackets15) { + if (framer_.version() != QUIC_VERSION_15) { + return; + } + + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (has nacks, not truncated, 6 byte largest observed, 1 byte delta) + 0x6C, + // entropy hash of sent packets till least awaiting - 1. + 0xAB, + // least packet sequence number awaiting an ack, delta from sequence number. + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, + // entropy hash of all received packets. + 0xBA, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Zero delta time. + 0x0, 0x0, + // num missing packets + 0x01, + // missing packet delta + 0x01, + // 0 more missing packets in range. + 0x00, + // Number of revived packets. + 0x01, + // Revived packet sequence number. + 0xBE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(0xAB, frame.sent_info.entropy_hash); + EXPECT_EQ(0xBA, frame.received_info.entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed); + ASSERT_EQ(1u, frame.received_info.missing_packets.size()); + SequenceNumberSet::const_iterator missing_iter = + frame.received_info.missing_packets.begin(); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter); + EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.sent_info.least_unacked); + + const size_t kSentEntropyOffset = kQuicFrameTypeSize; + const size_t kLeastUnackedOffset = kSentEntropyOffset + kQuicEntropyHashSize; + const size_t kReceivedEntropyOffset = kLeastUnackedOffset + + PACKET_6BYTE_SEQUENCE_NUMBER; + const size_t kLargestObservedOffset = kReceivedEntropyOffset + + kQuicEntropyHashSize; + const size_t kMissingDeltaTimeOffset = kLargestObservedOffset + + PACKET_6BYTE_SEQUENCE_NUMBER; + const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset + + kQuicDeltaTimeLargestObservedSize; + const size_t kMissingPacketsOffset = kNumMissingPacketOffset + + kNumberOfNackRangesSize; + const size_t kMissingPacketsRange = kMissingPacketsOffset + + PACKET_1BYTE_SEQUENCE_NUMBER; + const size_t kRevivedPacketsLength = kMissingPacketsRange + + PACKET_1BYTE_SEQUENCE_NUMBER; + const size_t kRevivedPacketSequenceNumberLength = kRevivedPacketsLength + + PACKET_1BYTE_SEQUENCE_NUMBER; + // Now test framing boundaries + const size_t ack_frame_size = kRevivedPacketSequenceNumberLength + + PACKET_6BYTE_SEQUENCE_NUMBER; + for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) { + string expected_error; + if (i < kLeastUnackedOffset) { + expected_error = "Unable to read entropy hash for sent packets."; + } else if (i < kReceivedEntropyOffset) { + expected_error = "Unable to read least unacked delta."; + } else if (i < kLargestObservedOffset) { + expected_error = "Unable to read entropy hash for received packets."; + } else if (i < kMissingDeltaTimeOffset) { + expected_error = "Unable to read largest observed."; + } else if (i < kNumMissingPacketOffset) { + expected_error = "Unable to read delta time largest observed."; + } else if (i < kMissingPacketsOffset) { + expected_error = "Unable to read num missing packet ranges."; + } else if (i < kMissingPacketsRange) { + expected_error = "Unable to read missing sequence number delta."; + } else if (i < kRevivedPacketsLength) { expected_error = "Unable to read missing sequence number range."; + } else if (i < kRevivedPacketSequenceNumberLength) { + expected_error = "Unable to read num revived packets."; + } else { + expected_error = "Unable to read revived packet."; } CheckProcessingFails( packet, - i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), expected_error, QUIC_INVALID_ACK_DATA); } } TEST_P(QuicFramerTest, AckFrameNoNacks) { + if (framer_.version() <= QUIC_VERSION_15) { + return; + } unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (no nacks, not truncated, 6 byte largest observed, 1 byte delta) + 0x4C, + // entropy hash of all received packets. + 0xBA, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Zero delta time. + 0x0, 0x0, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + QuicAckFrame* frame = visitor_.ack_frames_[0]; + EXPECT_EQ(0xBA, frame->received_info.entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), + frame->received_info.largest_observed); + ASSERT_EQ(0u, frame->received_info.missing_packets.size()); + + // Verify that the packet re-serializes identically. + QuicFrames frames; + frames.push_back(QuicFrame(frame)); + scoped_ptr<QuicPacket> data(BuildDataPacket(*visitor_.header_, frames)); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, AckFrameNoNacks15) { + if (framer_.version() > QUIC_VERSION_15) { + return; + } + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -1750,8 +2214,7 @@ TEST_P(QuicFramerTest, AckFrameNoNacks) { // Verify that the packet re-serializes identically. QuicFrames frames; frames.push_back(QuicFrame(frame)); - scoped_ptr<QuicPacket> data( - framer_.BuildUnsizedDataPacket(*visitor_.header_, frames).packet); + scoped_ptr<QuicPacket> data(BuildDataPacket(*visitor_.header_, frames)); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -1760,10 +2223,87 @@ TEST_P(QuicFramerTest, AckFrameNoNacks) { } TEST_P(QuicFramerTest, AckFrame500Nacks) { + if (framer_.version() <= QUIC_VERSION_15) { + return; + } + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (has nacks, not truncated, 6 byte largest observed, 1 byte delta) + 0x6C, + // entropy hash of all received packets. + 0xBA, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Zero delta time. + 0x0, 0x0, + // num missing packet ranges + 0x02, + // missing packet delta + 0x01, + // 243 more missing packets in range. + // The ranges are listed in this order so the re-constructed packet matches. + 0xF3, + // No gap between ranges + 0x00, + // 255 more missing packets in range. + 0xFF, + // No revived packets. + 0x00, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + QuicAckFrame* frame = visitor_.ack_frames_[0]; + EXPECT_EQ(0xBA, frame->received_info.entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), + frame->received_info.largest_observed); + EXPECT_EQ(0u, frame->received_info.revived_packets.size()); + ASSERT_EQ(500u, frame->received_info.missing_packets.size()); + SequenceNumberSet::const_iterator first_missing_iter = + frame->received_info.missing_packets.begin(); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABE) - 499, *first_missing_iter); + SequenceNumberSet::const_reverse_iterator last_missing_iter = + frame->received_info.missing_packets.rbegin(); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *last_missing_iter); + + // Verify that the packet re-serializes identically. + QuicFrames frames; + frames.push_back(QuicFrame(frame)); + scoped_ptr<QuicPacket> data(BuildDataPacket(*visitor_.header_, frames)); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, AckFrame500Nacks15) { + if (framer_.version() != QUIC_VERSION_15) { + return; + } unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -1798,6 +2338,8 @@ TEST_P(QuicFramerTest, AckFrame500Nacks) { 0x00, // 255 more missing packets in range. 0xFF, + // No revived packets. + 0x00, }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); @@ -1814,6 +2356,7 @@ TEST_P(QuicFramerTest, AckFrame500Nacks) { EXPECT_EQ(0xBA, frame->received_info.entropy_hash); EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame->received_info.largest_observed); + EXPECT_EQ(0u, frame->received_info.revived_packets.size()); ASSERT_EQ(500u, frame->received_info.missing_packets.size()); SequenceNumberSet::const_iterator first_missing_iter = frame->received_info.missing_packets.begin(); @@ -1826,8 +2369,7 @@ TEST_P(QuicFramerTest, AckFrame500Nacks) { // Verify that the packet re-serializes identically. QuicFrames frames; frames.push_back(QuicFrame(frame)); - scoped_ptr<QuicPacket> data( - framer_.BuildUnsizedDataPacket(*visitor_.header_, frames).packet); + scoped_ptr<QuicPacket> data(BuildDataPacket(*visitor_.header_, frames)); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -1837,9 +2379,9 @@ TEST_P(QuicFramerTest, AckFrame500Nacks) { TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -1852,8 +2394,6 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) { 0x20, // congestion feedback type (tcp) 0x00, - // ack_frame.feedback.tcp.accumulated_number_of_lost_packets - 0x01, 0x02, // ack_frame.feedback.tcp.receive_window 0x03, 0x04, }; @@ -1870,23 +2410,19 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) { const QuicCongestionFeedbackFrame& frame = *visitor_.congestion_feedback_frames_[0]; ASSERT_EQ(kTCP, frame.type); - EXPECT_EQ(0x0201, - frame.tcp.accumulated_number_of_lost_packets); EXPECT_EQ(0x4030u, frame.tcp.receive_window); // Now test framing boundaries - for (size_t i = kQuicFrameTypeSize; i < 6; ++i) { + for (size_t i = kQuicFrameTypeSize; i < 4; ++i) { string expected_error; if (i < 2) { expected_error = "Unable to read congestion feedback type."; } else if (i < 4) { - expected_error = "Unable to read accumulated number of lost packets."; - } else if (i < 6) { expected_error = "Unable to read receive window."; } CheckProcessingFails( packet, - i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), expected_error, QUIC_INVALID_CONGESTION_FEEDBACK_DATA); } @@ -1894,9 +2430,9 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) { TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -1909,8 +2445,6 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) { 0x20, // congestion feedback type (inter arrival) 0x01, - // accumulated_number_of_lost_packets - 0x02, 0x03, // num received packets 0x03, // lowest sequence number @@ -1941,8 +2475,6 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) { const QuicCongestionFeedbackFrame& frame = *visitor_.congestion_feedback_frames_[0]; ASSERT_EQ(kInterArrival, frame.type); - EXPECT_EQ(0x0302, frame.inter_arrival. - accumulated_number_of_lost_packets); ASSERT_EQ(3u, frame.inter_arrival.received_packet_times.size()); TimeMap::const_iterator iter = frame.inter_arrival.received_packet_times.begin(); @@ -1959,30 +2491,28 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) { iter->second.Subtract(start_).ToMicroseconds()); // Now test framing boundaries - for (size_t i = kQuicFrameTypeSize; i < 31; ++i) { + for (size_t i = kQuicFrameTypeSize; i < 29; ++i) { string expected_error; if (i < 2) { expected_error = "Unable to read congestion feedback type."; - } else if (i < 4) { - expected_error = "Unable to read accumulated number of lost packets."; - } else if (i < 5) { + } else if (i < 3) { expected_error = "Unable to read num received packets."; - } else if (i < 11) { + } else if (i < 9) { expected_error = "Unable to read smallest received."; - } else if (i < 19) { + } else if (i < 17) { expected_error = "Unable to read time received."; - } else if (i < 21) { + } else if (i < 19) { expected_error = "Unable to read sequence delta in received packets."; - } else if (i < 25) { + } else if (i < 23) { expected_error = "Unable to read time delta in received packets."; - } else if (i < 27) { + } else if (i < 25) { expected_error = "Unable to read sequence delta in received packets."; - } else if (i < 31) { + } else if (i < 29) { expected_error = "Unable to read time delta in received packets."; } CheckProcessingFails( packet, - i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), expected_error, QUIC_INVALID_CONGESTION_FEEDBACK_DATA); } @@ -1990,9 +2520,9 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) { TEST_P(QuicFramerTest, CongestionFeedbackFrameFixRate) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -2034,7 +2564,7 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameFixRate) { } CheckProcessingFails( packet, - i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), expected_error, QUIC_INVALID_CONGESTION_FEEDBACK_DATA); } @@ -2042,9 +2572,9 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameFixRate) { TEST_P(QuicFramerTest, CongestionFeedbackFrameInvalidFeedback) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -2065,11 +2595,68 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameInvalidFeedback) { EXPECT_EQ(QUIC_INVALID_CONGESTION_FEEDBACK_DATA, framer_.error()); } -TEST_P(QuicFramerTest, RstStreamFrame) { +TEST_P(QuicFramerTest, StopWaitingFrame) { + if (framer_.version() <= QUIC_VERSION_15) { + return; + } unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (has nacks, not truncated, 6 byte largest observed, 1 byte delta) + 0x06, + // entropy hash of sent packets till least awaiting - 1. + 0xAB, + // least packet sequence number awaiting an ack, delta from sequence number. + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.stop_waiting_frames_.size()); + const QuicStopWaitingFrame& frame = *visitor_.stop_waiting_frames_[0]; + EXPECT_EQ(0xAB, frame.entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.least_unacked); + + const size_t kSentEntropyOffset = kQuicFrameTypeSize; + const size_t kLeastUnackedOffset = kSentEntropyOffset + kQuicEntropyHashSize; + const size_t frame_size = 7; + for (size_t i = kQuicFrameTypeSize; i < frame_size; ++i) { + string expected_error; + if (i < kLeastUnackedOffset) { + expected_error = "Unable to read entropy hash for sent packets."; + } else { + expected_error = "Unable to read least unacked delta."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_STOP_WAITING_DATA); + } +} + +TEST_P(QuicFramerTest, RstStreamFrameQuic) { + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -2082,6 +2669,11 @@ TEST_P(QuicFramerTest, RstStreamFrame) { 0x01, // stream id 0x04, 0x03, 0x02, 0x01, + + // sent byte offset + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + // error code 0x01, 0x00, 0x00, 0x00, @@ -2104,21 +2696,27 @@ TEST_P(QuicFramerTest, RstStreamFrame) { EXPECT_EQ(GG_UINT64_C(0x01020304), visitor_.rst_stream_frame_.stream_id); EXPECT_EQ(0x01, visitor_.rst_stream_frame_.error_code); EXPECT_EQ("because I can", visitor_.rst_stream_frame_.error_details); + EXPECT_EQ(GG_UINT64_C(0x0807060504030201), + visitor_.rst_stream_frame_.byte_offset); // Now test framing boundaries - for (size_t i = kQuicFrameTypeSize; i < 24; ++i) { + for (size_t i = kQuicFrameTypeSize; + i < QuicFramer::GetMinRstStreamFrameSize(version_); ++i) { string expected_error; if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) { expected_error = "Unable to read stream_id."; } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + - kQuicErrorCodeSize) { + + kQuicMaxStreamOffsetSize) { + expected_error = "Unable to read rst stream sent byte offset."; + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + + + kQuicMaxStreamOffsetSize + kQuicErrorCodeSize) { expected_error = "Unable to read rst stream error code."; } else { expected_error = "Unable to read rst stream error details."; } CheckProcessingFails( packet, - i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), expected_error, QUIC_INVALID_RST_STREAM_DATA); } @@ -2126,9 +2724,9 @@ TEST_P(QuicFramerTest, RstStreamFrame) { TEST_P(QuicFramerTest, ConnectionCloseFrame) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -2176,7 +2774,7 @@ TEST_P(QuicFramerTest, ConnectionCloseFrame) { } CheckProcessingFails( packet, - i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), expected_error, QUIC_INVALID_CONNECTION_CLOSE_DATA); } @@ -2184,9 +2782,9 @@ TEST_P(QuicFramerTest, ConnectionCloseFrame) { TEST_P(QuicFramerTest, GoAwayFrame) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -2237,25 +2835,164 @@ TEST_P(QuicFramerTest, GoAwayFrame) { } CheckProcessingFails( packet, - i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), expected_error, QUIC_INVALID_GOAWAY_DATA); } } +TEST_P(QuicFramerTest, WindowUpdateFrame) { + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + + // frame type (window update frame) + 0x04, + // stream id + 0x04, 0x03, 0x02, 0x01, + // byte offset + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(GG_UINT64_C(0x01020304), + visitor_.window_update_frame_.stream_id); + EXPECT_EQ(GG_UINT64_C(0x0c0b0a0908070605), + visitor_.window_update_frame_.byte_offset); + + // Now test framing boundaries + for (size_t i = kQuicFrameTypeSize; + i < QuicFramer::GetWindowUpdateFrameSize(); ++i) { + string expected_error; + if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) { + expected_error = "Unable to read stream_id."; + } else { + expected_error = "Unable to read window byte_offset."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_WINDOW_UPDATE_DATA); + } +} + +TEST_P(QuicFramerTest, BlockedFrame) { + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + + // frame type (blocked frame) + 0x05, + // stream id + 0x04, 0x03, 0x02, 0x01, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(GG_UINT64_C(0x01020304), + visitor_.blocked_frame_.stream_id); + + // Now test framing boundaries + for (size_t i = kQuicFrameTypeSize; i < QuicFramer::GetBlockedFrameSize(); + ++i) { + string expected_error = "Unable to read stream_id."; + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_BLOCKED_DATA); + } +} + +TEST_P(QuicFramerTest, PingFrame) { + if (version_ <= QUIC_VERSION_17) { + return; + } + + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + + // frame type (ping frame) + 0x07, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(1u, visitor_.ping_frames_.size()); + + // No need to check the PING frame boundaries because it has no payload. +} + TEST_P(QuicFramerTest, PublicResetPacket) { unsigned char packet[] = { - // public flags (public reset, 8 byte guid) - 0x3E, - // guid + // public flags (public reset, 8 byte connection_id) + 0x0E, + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, + // message tag (kPRST) + 'P', 'R', 'S', 'T', + // num_entries (2) + padding + 0x02, 0x00, 0x00, 0x00, + // tag kRNON + 'R', 'N', 'O', 'N', + // end offset 8 + 0x08, 0x00, 0x00, 0x00, + // tag kRSEQ + 'R', 'S', 'E', 'Q', + // end offset 16 + 0x10, 0x00, 0x00, 0x00, // nonce proof 0x89, 0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, // rejected sequence number 0xBC, 0x9A, 0x78, 0x56, - 0x34, 0x12, + 0x34, 0x12, 0x00, 0x00, }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); @@ -2263,32 +3000,136 @@ TEST_P(QuicFramerTest, PublicResetPacket) { ASSERT_EQ(QUIC_NO_ERROR, framer_.error()); ASSERT_TRUE(visitor_.public_reset_packet_.get()); EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), - visitor_.public_reset_packet_->public_header.guid); + visitor_.public_reset_packet_->public_header.connection_id); EXPECT_TRUE(visitor_.public_reset_packet_->public_header.reset_flag); EXPECT_FALSE(visitor_.public_reset_packet_->public_header.version_flag); EXPECT_EQ(GG_UINT64_C(0xABCDEF0123456789), visitor_.public_reset_packet_->nonce_proof); EXPECT_EQ(GG_UINT64_C(0x123456789ABC), visitor_.public_reset_packet_->rejected_sequence_number); + EXPECT_TRUE( + visitor_.public_reset_packet_->client_address.address().empty()); // Now test framing boundaries - for (size_t i = 0; i < GetPublicResetPacketSize(); ++i) { + for (size_t i = 0; i < arraysize(packet); ++i) { string expected_error; - DLOG(INFO) << "iteration: " << i; - if (i < kGuidOffset) { + DVLOG(1) << "iteration: " << i; + if (i < kConnectionIdOffset) { expected_error = "Unable to read public flags."; CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER); - } else if (i < kPublicResetPacketNonceProofOffset) { - expected_error = "Unable to read GUID."; + } else if (i < kPublicResetPacketMessageTagOffset) { + expected_error = "Unable to read ConnectionId."; CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER); - } else if (i < kPublicResetPacketRejectedSequenceNumberOffset) { - expected_error = "Unable to read nonce proof."; + } else { + expected_error = "Unable to read reset message."; CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PUBLIC_RST_PACKET); + } + } +} + +TEST_P(QuicFramerTest, PublicResetPacketWithTrailingJunk) { + unsigned char packet[] = { + // public flags (public reset, 8 byte connection_id) + 0x0E, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // message tag (kPRST) + 'P', 'R', 'S', 'T', + // num_entries (2) + padding + 0x02, 0x00, 0x00, 0x00, + // tag kRNON + 'R', 'N', 'O', 'N', + // end offset 8 + 0x08, 0x00, 0x00, 0x00, + // tag kRSEQ + 'R', 'S', 'E', 'Q', + // end offset 16 + 0x10, 0x00, 0x00, 0x00, + // nonce proof + 0x89, 0x67, 0x45, 0x23, + 0x01, 0xEF, 0xCD, 0xAB, + // rejected sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, 0x00, 0x00, + // trailing junk + 'j', 'u', 'n', 'k', + }; + + string expected_error = "Unable to read reset message."; + CheckProcessingFails(packet, arraysize(packet), expected_error, + QUIC_INVALID_PUBLIC_RST_PACKET); +} + +TEST_P(QuicFramerTest, PublicResetPacketWithClientAddress) { + unsigned char packet[] = { + // public flags (public reset, 8 byte connection_id) + 0x0E, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // message tag (kPRST) + 'P', 'R', 'S', 'T', + // num_entries (3) + padding + 0x03, 0x00, 0x00, 0x00, + // tag kRNON + 'R', 'N', 'O', 'N', + // end offset 8 + 0x08, 0x00, 0x00, 0x00, + // tag kRSEQ + 'R', 'S', 'E', 'Q', + // end offset 16 + 0x10, 0x00, 0x00, 0x00, + // tag kCADR + 'C', 'A', 'D', 'R', + // end offset 24 + 0x18, 0x00, 0x00, 0x00, + // nonce proof + 0x89, 0x67, 0x45, 0x23, + 0x01, 0xEF, 0xCD, 0xAB, + // rejected sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, 0x00, 0x00, + // client address: 4.31.198.44:443 + 0x02, 0x00, + 0x04, 0x1F, 0xC6, 0x2C, + 0xBB, 0x01, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + ASSERT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.public_reset_packet_.get()); + EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + visitor_.public_reset_packet_->public_header.connection_id); + EXPECT_TRUE(visitor_.public_reset_packet_->public_header.reset_flag); + EXPECT_FALSE(visitor_.public_reset_packet_->public_header.version_flag); + EXPECT_EQ(GG_UINT64_C(0xABCDEF0123456789), + visitor_.public_reset_packet_->nonce_proof); + EXPECT_EQ(GG_UINT64_C(0x123456789ABC), + visitor_.public_reset_packet_->rejected_sequence_number); + EXPECT_EQ("4.31.198.44", + IPAddressToString(visitor_.public_reset_packet_-> + client_address.address())); + EXPECT_EQ(443, visitor_.public_reset_packet_->client_address.port()); + + // Now test framing boundaries + for (size_t i = 0; i < arraysize(packet); ++i) { + string expected_error; + DVLOG(1) << "iteration: " << i; + if (i < kConnectionIdOffset) { + expected_error = "Unable to read public flags."; + CheckProcessingFails(packet, i, expected_error, + QUIC_INVALID_PACKET_HEADER); + } else if (i < kPublicResetPacketMessageTagOffset) { + expected_error = "Unable to read ConnectionId."; + CheckProcessingFails(packet, i, expected_error, + QUIC_INVALID_PACKET_HEADER); } else { - expected_error = "Unable to read rejected sequence number."; + expected_error = "Unable to read reset message."; CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PUBLIC_RST_PACKET); } @@ -2297,9 +3138,9 @@ TEST_P(QuicFramerTest, PublicResetPacket) { TEST_P(QuicFramerTest, VersionNegotiationPacket) { unsigned char packet[] = { - // public flags (version, 8 byte guid) + // public flags (version, 8 byte connection_id) 0x3D, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag @@ -2316,13 +3157,13 @@ TEST_P(QuicFramerTest, VersionNegotiationPacket) { EXPECT_EQ(2u, visitor_.version_negotiation_packet_->versions.size()); EXPECT_EQ(GetParam(), visitor_.version_negotiation_packet_->versions[0]); - for (size_t i = 0; i <= kPublicFlagsSize + PACKET_8BYTE_GUID; ++i) { + for (size_t i = 0; i <= kPublicFlagsSize + PACKET_8BYTE_CONNECTION_ID; ++i) { string expected_error; QuicErrorCode error_code = QUIC_INVALID_PACKET_HEADER; - if (i < kGuidOffset) { + if (i < kConnectionIdOffset) { expected_error = "Unable to read public flags."; } else if (i < kVersionOffset) { - expected_error = "Unable to read GUID."; + expected_error = "Unable to read ConnectionId."; } else { expected_error = "Unable to read supported version in negotiation."; error_code = QUIC_INVALID_VERSION_NEGOTIATION_PACKET; @@ -2333,9 +3174,9 @@ TEST_P(QuicFramerTest, VersionNegotiationPacket) { TEST_P(QuicFramerTest, FecPacket) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -2370,7 +3211,7 @@ TEST_P(QuicFramerTest, FecPacket) { TEST_P(QuicFramerTest, BuildPaddingFramePacket) { QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; @@ -2384,9 +3225,9 @@ TEST_P(QuicFramerTest, BuildPaddingFramePacket) { frames.push_back(QuicFrame(&padding_frame)); unsigned char packet[kMaxPacketSize] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -2401,12 +3242,11 @@ TEST_P(QuicFramerTest, BuildPaddingFramePacket) { }; uint64 header_size = - GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); - scoped_ptr<QuicPacket> data( - framer_.BuildUnsizedDataPacket(header, frames).packet); + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2417,7 +3257,7 @@ TEST_P(QuicFramerTest, BuildPaddingFramePacket) { TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) { QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; @@ -2432,9 +3272,9 @@ TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) { frames.push_back(QuicFrame(&padding_frame)); unsigned char packet[kMaxPacketSize] = { - // public flags (8 byte guid and 4 byte sequence number) + // public flags (8 byte connection_id and 4 byte sequence number) 0x2C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -2448,12 +3288,11 @@ TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) { }; uint64 header_size = - GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_4BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); - scoped_ptr<QuicPacket> data( - framer_.BuildUnsizedDataPacket(header, frames).packet); + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2464,7 +3303,7 @@ TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) { TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) { QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; @@ -2479,9 +3318,9 @@ TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) { frames.push_back(QuicFrame(&padding_frame)); unsigned char packet[kMaxPacketSize] = { - // public flags (8 byte guid and 2 byte sequence number) + // public flags (8 byte connection_id and 2 byte sequence number) 0x1C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -2495,12 +3334,11 @@ TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) { }; uint64 header_size = - GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_2BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); - scoped_ptr<QuicPacket> data( - framer_.BuildUnsizedDataPacket(header, frames).packet); + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2511,7 +3349,7 @@ TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) { TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) { QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; @@ -2526,9 +3364,9 @@ TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) { frames.push_back(QuicFrame(&padding_frame)); unsigned char packet[kMaxPacketSize] = { - // public flags (8 byte guid and 1 byte sequence number) + // public flags (8 byte connection_id and 1 byte sequence number) 0x0C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -2542,12 +3380,11 @@ TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) { }; uint64 header_size = - GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); - scoped_ptr<QuicPacket> data( - framer_.BuildUnsizedDataPacket(header, frames).packet); + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2558,7 +3395,7 @@ TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) { TEST_P(QuicFramerTest, BuildStreamFramePacket) { QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; @@ -2576,9 +3413,9 @@ TEST_P(QuicFramerTest, BuildStreamFramePacket) { frames.push_back(QuicFrame(&stream_frame)); unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -2600,8 +3437,62 @@ TEST_P(QuicFramerTest, BuildStreamFramePacket) { 'r', 'l', 'd', '!', }; - scoped_ptr<QuicPacket> data( - framer_.BuildUnsizedDataPacket(header, frames).packet); + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildStreamFramePacketInFecGroup) { + QuicPacketHeader header; + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.fec_flag = false; + header.entropy_flag = true; + header.packet_sequence_number = GG_UINT64_C(0x77123456789ABC); + header.is_in_fec_group = IN_FEC_GROUP; + header.fec_group = GG_UINT64_C(0x77123456789ABC); + + QuicStreamFrame stream_frame; + stream_frame.stream_id = 0x01020304; + stream_frame.fin = true; + stream_frame.offset = GG_UINT64_C(0xBA98FEDC32107654); + stream_frame.data = MakeIOVector("hello world!"); + + QuicFrames frames; + frames.push_back(QuicFrame(&stream_frame)); + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy, is_in_fec_group) + 0x03, + // FEC group + 0x00, + // frame type (stream frame with fin and data length field) + 0xFF, + // stream id + 0x04, 0x03, 0x02, 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data length (since packet is in an FEC group) + 0x0C, 0x00, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2611,7 +3502,7 @@ TEST_P(QuicFramerTest, BuildStreamFramePacket) { TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) { QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = true; header.fec_flag = false; @@ -2629,9 +3520,9 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) { frames.push_back(QuicFrame(&stream_frame)); unsigned char packet[] = { - // public flags (version, 8 byte guid) + // public flags (version, 8 byte connection_id) 0x3D, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag @@ -2656,8 +3547,7 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) { }; QuicFramerPeer::SetIsServer(&framer_, false); - scoped_ptr<QuicPacket> data( - framer_.BuildUnsizedDataPacket(header, frames).packet); + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2667,14 +3557,14 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) { TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) { QuicPacketPublicHeader header; - header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.reset_flag = false; header.version_flag = true; unsigned char packet[] = { - // public flags (version, 8 byte guid) - 0x3D, - // guid + // public flags (version, 8 byte connection_id) + 0x0D, + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag @@ -2692,8 +3582,74 @@ TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) { } TEST_P(QuicFramerTest, BuildAckFramePacket) { + if (version_ <= QUIC_VERSION_15) { + return; + } QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.fec_flag = false; + header.entropy_flag = true; + header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8); + header.fec_group = 0; + + QuicAckFrame ack_frame; + ack_frame.received_info.entropy_hash = 0x43; + ack_frame.received_info.largest_observed = GG_UINT64_C(0x770123456789ABF); + ack_frame.received_info.delta_time_largest_observed = QuicTime::Delta::Zero(); + ack_frame.received_info.missing_packets.insert( + GG_UINT64_C(0x770123456789ABE)); + + QuicFrames frames; + frames.push_back(QuicFrame(&ack_frame)); + + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (has nacks, not truncated, 6 byte largest observed, 1 byte delta) + 0x6C, + // entropy hash of all received packets. + 0x43, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Zero delta time. + 0x0, 0x0, + // num missing packet ranges + 0x01, + // missing packet delta + 0x01, + // 0 more missing packets in range. + 0x00, + // 0 revived packets. + 0x00, + }; + + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildAckFramePacket15) { + if (version_ != QUIC_VERSION_15) { + return; + } + QuicPacketHeader header; + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; @@ -2714,9 +3670,9 @@ TEST_P(QuicFramerTest, BuildAckFramePacket) { frames.push_back(QuicFrame(&ack_frame)); unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -2746,10 +3702,128 @@ TEST_P(QuicFramerTest, BuildAckFramePacket) { 0x01, // 0 more missing packets in range. 0x00, + // 0 revived packets. + 0x00, + }; + + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +// TODO(jri): Add test for tuncated packets in which the original ack frame had +// revived packets. (In both the large and small packet cases below). +TEST_P(QuicFramerTest, BuildTruncatedAckFrameLargePacket) { + if (version_ <= QUIC_VERSION_15) { + return; + } + QuicPacketHeader header; + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.fec_flag = false; + header.entropy_flag = true; + header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8); + header.fec_group = 0; + + QuicAckFrame ack_frame; + // This entropy hash is different from what shows up in the packet below, + // since entropy is recomputed by the framer on ack truncation (by + // TestEntropyCalculator for this test.) + ack_frame.received_info.entropy_hash = 0x43; + ack_frame.received_info.largest_observed = 2 * 300; + ack_frame.received_info.delta_time_largest_observed = QuicTime::Delta::Zero(); + for (size_t i = 1; i < 2 * 300; i += 2) { + ack_frame.received_info.missing_packets.insert(i); + } + + QuicFrames frames; + frames.push_back(QuicFrame(&ack_frame)); + + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (has nacks, is truncated, 2 byte largest observed, 1 byte delta) + 0x74, + // entropy hash of all received packets, set to 1 by TestEntropyCalculator + // since ack is truncated. + 0x01, + // 2-byte largest observed packet sequence number. + // Expected to be 510 (0x1FE), since only 255 nack ranges can fit. + 0xFE, 0x01, + // Zero delta time. + 0x0, 0x0, + // num missing packet ranges (limited to 255 by size of this field). + 0xFF, + // {missing packet delta, further missing packets in range} + // 6 nack ranges x 42 + 3 nack ranges + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + + // 0 revived packets. + 0x00, }; scoped_ptr<QuicPacket> data( - framer_.BuildUnsizedDataPacket(header, frames).packet); + framer_.BuildDataPacket(header, frames, kMaxPacketSize).packet); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2757,9 +3831,79 @@ TEST_P(QuicFramerTest, BuildAckFramePacket) { AsChars(packet), arraysize(packet)); } + +TEST_P(QuicFramerTest, BuildTruncatedAckFrameSmallPacket) { + if (version_ <= QUIC_VERSION_15) { + return; + } + QuicPacketHeader header; + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.fec_flag = false; + header.entropy_flag = true; + header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8); + header.fec_group = 0; + + QuicAckFrame ack_frame; + // This entropy hash is different from what shows up in the packet below, + // since entropy is recomputed by the framer on ack truncation (by + // TestEntropyCalculator for this test.) + ack_frame.received_info.entropy_hash = 0x43; + ack_frame.received_info.largest_observed = 2 * 300; + ack_frame.received_info.delta_time_largest_observed = QuicTime::Delta::Zero(); + for (size_t i = 1; i < 2 * 300; i += 2) { + ack_frame.received_info.missing_packets.insert(i); + } + + QuicFrames frames; + frames.push_back(QuicFrame(&ack_frame)); + + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (has nacks, is truncated, 2 byte largest observed, 1 byte delta) + 0x74, + // entropy hash of all received packets, set to 1 by TestEntropyCalculator + // since ack is truncated. + 0x01, + // 2-byte largest observed packet sequence number. + // Expected to be 12 (0x0C), since only 6 nack ranges can fit. + 0x0C, 0x00, + // Zero delta time. + 0x0, 0x0, + // num missing packet ranges (limited to 6 by packet size of 37). + 0x06, + // {missing packet delta, further missing packets in range} + // 6 nack ranges + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + // 0 revived packets. + 0x00, + }; + + scoped_ptr<QuicPacket> data( + framer_.BuildDataPacket(header, frames, 37u).packet); + ASSERT_TRUE(data != NULL); + // Expect 1 byte unused since at least 2 bytes are needed to fit more nacks. + EXPECT_EQ(36u, data->length()); + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) { QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; @@ -2769,16 +3913,15 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) { 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 = 0x4030; QuicFrames frames; frames.push_back(QuicFrame(&congestion_feedback_frame)); unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -2791,14 +3934,11 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) { 0x20, // congestion feedback type (TCP) 0x00, - // accumulated number of lost packets - 0x01, 0x02, // TCP receive window 0x03, 0x04, }; - scoped_ptr<QuicPacket> data( - framer_.BuildUnsizedDataPacket(header, frames).packet); + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2808,7 +3948,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) { TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) { QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; @@ -2818,7 +3958,6 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) { QuicCongestionFeedbackFrame frame; frame.type = kInterArrival; - frame.inter_arrival.accumulated_number_of_lost_packets = 0x0302; frame.inter_arrival.received_packet_times.insert( make_pair(GG_UINT64_C(0x0123456789ABA), start_.Add(QuicTime::Delta::FromMicroseconds( @@ -2835,9 +3974,9 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) { frames.push_back(QuicFrame(&frame)); unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -2850,8 +3989,6 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) { 0x20, // congestion feedback type (inter arrival) 0x01, - // accumulated_number_of_lost_packets - 0x02, 0x03, // num received packets 0x03, // lowest sequence number @@ -2870,8 +4007,56 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) { 0x02, 0x00, 0x00, 0x00, }; - scoped_ptr<QuicPacket> data( - framer_.BuildUnsizedDataPacket(header, frames).packet); + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildStopWaitingPacket) { + if (version_ <= QUIC_VERSION_15) { + return; + } + QuicPacketHeader header; + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.fec_flag = false; + header.entropy_flag = true; + header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8); + header.fec_group = 0; + + QuicStopWaitingFrame stop_waiting_frame; + stop_waiting_frame.entropy_hash = 0x14; + stop_waiting_frame.least_unacked = GG_UINT64_C(0x770123456789AA0); + + QuicFrames frames; + frames.push_back(QuicFrame(&stop_waiting_frame)); + + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (stop waiting frame) + 0x06, + // entropy hash of sent packets till least awaiting - 1. + 0x14, + // least packet sequence number awaiting an ack, delta from sequence number. + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, + }; + + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2881,7 +4066,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) { TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketFixRate) { QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; @@ -2898,9 +4083,9 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketFixRate) { frames.push_back(QuicFrame(&congestion_feedback_frame)); unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -2917,8 +4102,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketFixRate) { 0x01, 0x02, 0x03, 0x04, }; - scoped_ptr<QuicPacket> data( - framer_.BuildUnsizedDataPacket(header, frames).packet); + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -2928,7 +4112,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketFixRate) { TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInvalidFeedback) { QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; @@ -2943,14 +4127,16 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInvalidFeedback) { QuicFrames frames; frames.push_back(QuicFrame(&congestion_feedback_frame)); - scoped_ptr<QuicPacket> data( - framer_.BuildUnsizedDataPacket(header, frames).packet); + scoped_ptr<QuicPacket> data; + EXPECT_DFATAL( + data.reset(BuildDataPacket(header, frames)), + "AppendCongestionFeedbackFrame failed"); ASSERT_TRUE(data == NULL); } -TEST_P(QuicFramerTest, BuildRstFramePacket) { +TEST_P(QuicFramerTest, BuildRstFramePacketQuic) { QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; @@ -2962,11 +4148,12 @@ TEST_P(QuicFramerTest, BuildRstFramePacket) { rst_frame.stream_id = 0x01020304; rst_frame.error_code = static_cast<QuicRstStreamErrorCode>(0x05060708); rst_frame.error_details = "because I can"; + rst_frame.byte_offset = 0x0807060504030201; unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -2979,6 +4166,9 @@ TEST_P(QuicFramerTest, BuildRstFramePacket) { 0x01, // stream id 0x04, 0x03, 0x02, 0x01, + // sent byte offset + 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, // error code 0x08, 0x07, 0x06, 0x05, // error details length @@ -2993,8 +4183,7 @@ TEST_P(QuicFramerTest, BuildRstFramePacket) { QuicFrames frames; frames.push_back(QuicFrame(&rst_frame)); - scoped_ptr<QuicPacket> data( - framer_.BuildUnsizedDataPacket(header, frames).packet); + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -3004,7 +4193,7 @@ TEST_P(QuicFramerTest, BuildRstFramePacket) { TEST_P(QuicFramerTest, BuildCloseFramePacket) { QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; @@ -3020,9 +4209,9 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) { frames.push_back(QuicFrame(&close_frame)); unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -3044,8 +4233,7 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) { 'n', }; - scoped_ptr<QuicPacket> data( - framer_.BuildUnsizedDataPacket(header, frames).packet); + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -3055,7 +4243,7 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) { TEST_P(QuicFramerTest, BuildGoAwayPacket) { QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; @@ -3072,9 +4260,9 @@ TEST_P(QuicFramerTest, BuildGoAwayPacket) { frames.push_back(QuicFrame(&goaway_frame)); unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -3098,8 +4286,7 @@ TEST_P(QuicFramerTest, BuildGoAwayPacket) { 'n', }; - scoped_ptr<QuicPacket> data( - framer_.BuildUnsizedDataPacket(header, frames).packet); + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != NULL); test::CompareCharArraysWithHexError("constructed packet", @@ -3107,25 +4294,224 @@ TEST_P(QuicFramerTest, BuildGoAwayPacket) { AsChars(packet), arraysize(packet)); } +TEST_P(QuicFramerTest, BuildWindowUpdatePacket) { + QuicPacketHeader header; + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.fec_flag = false; + header.entropy_flag = true; + header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); + header.fec_group = 0; + + QuicWindowUpdateFrame window_update_frame; + window_update_frame.stream_id = 0x01020304; + window_update_frame.byte_offset = 0x1122334455667788; + + QuicFrames frames; + frames.push_back(QuicFrame(&window_update_frame)); + + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags(entropy) + 0x01, + + // frame type (window update frame) + 0x04, + // stream id + 0x04, 0x03, 0x02, 0x01, + // byte offset + 0x88, 0x77, 0x66, 0x55, + 0x44, 0x33, 0x22, 0x11, + }; + + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet), + arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildBlockedPacket) { + QuicPacketHeader header; + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.fec_flag = false; + header.entropy_flag = true; + header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); + header.fec_group = 0; + + QuicBlockedFrame blocked_frame; + blocked_frame.stream_id = 0x01020304; + + QuicFrames frames; + frames.push_back(QuicFrame(&blocked_frame)); + + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags(entropy) + 0x01, + + // frame type (blocked frame) + 0x05, + // stream id + 0x04, 0x03, 0x02, 0x01, + }; + + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet), + arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildPingPacket) { + QuicPacketHeader header; + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.fec_flag = false; + header.entropy_flag = true; + header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); + header.fec_group = 0; + + QuicPingFrame ping_frame; + + QuicFrames frames; + frames.push_back(QuicFrame(&ping_frame)); + + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags(entropy) + 0x01, + + // frame type (ping frame) + 0x07, + }; + + if (version_ > QUIC_VERSION_17) { + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet), + arraysize(packet)); + } else { + string expected_error = + "Attempt to add a PingFrame in " + QuicVersionToString(version_); + EXPECT_DFATAL(BuildDataPacket(header, frames), + expected_error); + return; + } +} + TEST_P(QuicFramerTest, BuildPublicResetPacket) { QuicPublicResetPacket reset_packet; - reset_packet.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + reset_packet.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); reset_packet.public_header.reset_flag = true; reset_packet.public_header.version_flag = false; reset_packet.rejected_sequence_number = GG_UINT64_C(0x123456789ABC); reset_packet.nonce_proof = GG_UINT64_C(0xABCDEF0123456789); unsigned char packet[] = { - // public flags (public reset, 8 byte GUID) - 0x3E, - // guid + // public flags (public reset, 8 byte ConnectionId) + 0x0E, + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, + // message tag (kPRST) + 'P', 'R', 'S', 'T', + // num_entries (2) + padding + 0x02, 0x00, 0x00, 0x00, + // tag kRNON + 'R', 'N', 'O', 'N', + // end offset 8 + 0x08, 0x00, 0x00, 0x00, + // tag kRSEQ + 'R', 'S', 'E', 'Q', + // end offset 16 + 0x10, 0x00, 0x00, 0x00, // nonce proof 0x89, 0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, // rejected sequence number 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, 0x00, 0x00, + }; + + scoped_ptr<QuicEncryptedPacket> data( + framer_.BuildPublicResetPacket(reset_packet)); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildPublicResetPacketWithClientAddress) { + QuicPublicResetPacket reset_packet; + reset_packet.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + reset_packet.public_header.reset_flag = true; + reset_packet.public_header.version_flag = false; + reset_packet.rejected_sequence_number = GG_UINT64_C(0x123456789ABC); + reset_packet.nonce_proof = GG_UINT64_C(0xABCDEF0123456789); + reset_packet.client_address = IPEndPoint(Loopback4(), 0x1234); + + unsigned char packet[] = { + // public flags (public reset, 8 byte ConnectionId) + 0x0E, + // connection_id + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // message tag (kPRST) + 'P', 'R', 'S', 'T', + // num_entries (3) + padding + 0x03, 0x00, 0x00, 0x00, + // tag kRNON + 'R', 'N', 'O', 'N', + // end offset 8 + 0x08, 0x00, 0x00, 0x00, + // tag kRSEQ + 'R', 'S', 'E', 'Q', + // end offset 16 + 0x10, 0x00, 0x00, 0x00, + // tag kCADR + 'C', 'A', 'D', 'R', + // end offset 24 + 0x18, 0x00, 0x00, 0x00, + // nonce proof + 0x89, 0x67, 0x45, 0x23, + 0x01, 0xEF, 0xCD, 0xAB, + // rejected sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, 0x00, 0x00, + // client address + 0x02, 0x00, + 0x7F, 0x00, 0x00, 0x01, 0x34, 0x12, }; @@ -3140,7 +4526,7 @@ TEST_P(QuicFramerTest, BuildPublicResetPacket) { TEST_P(QuicFramerTest, BuildFecPacket) { QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = true; @@ -3154,9 +4540,9 @@ TEST_P(QuicFramerTest, BuildFecPacket) { fec_data.redundancy = "abcdefghijklmnop"; unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -3186,9 +4572,9 @@ TEST_P(QuicFramerTest, BuildFecPacket) { TEST_P(QuicFramerTest, EncryptPacket) { QuicPacketSequenceNumber sequence_number = GG_UINT64_C(0x123456789ABC); unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -3208,7 +4594,7 @@ TEST_P(QuicFramerTest, EncryptPacket) { scoped_ptr<QuicPacket> raw( QuicPacket::NewDataPacket(AsChars(packet), arraysize(packet), false, - PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER)); scoped_ptr<QuicEncryptedPacket> encrypted( framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number, *raw)); @@ -3220,9 +4606,9 @@ TEST_P(QuicFramerTest, EncryptPacket) { TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) { QuicPacketSequenceNumber sequence_number = GG_UINT64_C(0x123456789ABC); unsigned char packet[] = { - // public flags (version, 8 byte guid) + // public flags (version, 8 byte connection_id) 0x3D, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag @@ -3244,7 +4630,7 @@ TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) { scoped_ptr<QuicPacket> raw( QuicPacket::NewDataPacket(AsChars(packet), arraysize(packet), false, - PACKET_8BYTE_GUID, kIncludeVersion, + PACKET_8BYTE_CONNECTION_ID, kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER)); scoped_ptr<QuicEncryptedPacket> encrypted( framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number, *raw)); @@ -3253,31 +4639,98 @@ TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) { EXPECT_TRUE(CheckEncryption(sequence_number, raw.get())); } -// TODO(rch): re-enable after https://codereview.chromium.org/11820005/ -// lands. Currently this is causing valgrind problems, but it should be -// fixed in the followup CL. -TEST_P(QuicFramerTest, DISABLED_CalculateLargestReceived) { - SequenceNumberSet missing; - missing.insert(1); - missing.insert(5); - missing.insert(7); +TEST_P(QuicFramerTest, AckTruncationLargePacket) { + if (framer_.version() <= QUIC_VERSION_15) { + return; + } + QuicPacketHeader header; + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.fec_flag = false; + header.entropy_flag = false; + header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); + header.fec_group = 0; + + // Create a packet with just the ack. + QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(300, 0u); + QuicFrame frame; + frame.type = ACK_FRAME; + frame.ack_frame = &ack_frame; + QuicFrames frames; + frames.push_back(frame); + + // Build an ack packet with truncation due to limit in number of nack ranges. + scoped_ptr<QuicPacket> raw_ack_packet( + framer_.BuildDataPacket(header, frames, kMaxPacketSize).packet); + ASSERT_TRUE(raw_ack_packet != NULL); + scoped_ptr<QuicEncryptedPacket> ack_packet( + framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, + *raw_ack_packet)); + // Now make sure we can turn our ack packet back into an ack frame. + ASSERT_TRUE(framer_.ProcessPacket(*ack_packet)); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0]; + EXPECT_TRUE(processed_ack_frame.received_info.is_truncated); + EXPECT_EQ(510u, processed_ack_frame.received_info.largest_observed); + ASSERT_EQ(255u, processed_ack_frame.received_info.missing_packets.size()); + SequenceNumberSet::const_iterator missing_iter = + processed_ack_frame.received_info.missing_packets.begin(); + EXPECT_EQ(1u, *missing_iter); + SequenceNumberSet::const_reverse_iterator last_missing_iter = + processed_ack_frame.received_info.missing_packets.rbegin(); + EXPECT_EQ(509u, *last_missing_iter); +} + +TEST_P(QuicFramerTest, AckTruncationSmallPacket) { + if (framer_.version() <= QUIC_VERSION_15) { + return; + } + QuicPacketHeader header; + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.fec_flag = false; + header.entropy_flag = false; + header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); + header.fec_group = 0; - // These two we just walk to the next gap, and return the largest seen. - EXPECT_EQ(4u, QuicFramer::CalculateLargestObserved(missing, missing.find(1))); - EXPECT_EQ(6u, QuicFramer::CalculateLargestObserved(missing, missing.find(5))); + // Create a packet with just the ack. + QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(300, 0u); + QuicFrame frame; + frame.type = ACK_FRAME; + frame.ack_frame = &ack_frame; + QuicFrames frames; + frames.push_back(frame); - missing.insert(2); - // For 1, we can't go forward as 2 would be implicitly acked so we return the - // largest missing packet. - EXPECT_EQ(1u, QuicFramer::CalculateLargestObserved(missing, missing.find(1))); - // For 2, we've seen 3 and 4, so can admit to a largest observed. - EXPECT_EQ(4u, QuicFramer::CalculateLargestObserved(missing, missing.find(2))); + // Build an ack packet with truncation due to limit in number of nack ranges. + scoped_ptr<QuicPacket> raw_ack_packet( + framer_.BuildDataPacket(header, frames, 500).packet); + ASSERT_TRUE(raw_ack_packet != NULL); + scoped_ptr<QuicEncryptedPacket> ack_packet( + framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, + *raw_ack_packet)); + // Now make sure we can turn our ack packet back into an ack frame. + ASSERT_TRUE(framer_.ProcessPacket(*ack_packet)); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0]; + EXPECT_TRUE(processed_ack_frame.received_info.is_truncated); + EXPECT_EQ(476u, processed_ack_frame.received_info.largest_observed); + ASSERT_EQ(238u, processed_ack_frame.received_info.missing_packets.size()); + SequenceNumberSet::const_iterator missing_iter = + processed_ack_frame.received_info.missing_packets.begin(); + EXPECT_EQ(1u, *missing_iter); + SequenceNumberSet::const_reverse_iterator last_missing_iter = + processed_ack_frame.received_info.missing_packets.rbegin(); + EXPECT_EQ(475u, *last_missing_iter); } -// TODO(rch) enable after landing the revised truncation CL. -TEST_P(QuicFramerTest, DISABLED_Truncation) { +TEST_P(QuicFramerTest, Truncation15) { + if (framer_.version() > QUIC_VERSION_15) { + return; + } QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; @@ -3287,31 +4740,31 @@ TEST_P(QuicFramerTest, DISABLED_Truncation) { QuicAckFrame ack_frame; ack_frame.received_info.largest_observed = 601; - ack_frame.sent_info.least_unacked = 0; + ack_frame.sent_info.least_unacked = header.packet_sequence_number - 1; for (uint64 i = 1; i < ack_frame.received_info.largest_observed; i += 2) { ack_frame.received_info.missing_packets.insert(i); } - // Create a packet with just the ack + // Create a packet with just the ack. QuicFrame frame; frame.type = ACK_FRAME; frame.ack_frame = &ack_frame; QuicFrames frames; frames.push_back(frame); - scoped_ptr<QuicPacket> raw_ack_packet( - framer_.BuildUnsizedDataPacket(header, frames).packet); + scoped_ptr<QuicPacket> raw_ack_packet(BuildDataPacket(header, frames)); ASSERT_TRUE(raw_ack_packet != NULL); scoped_ptr<QuicEncryptedPacket> ack_packet( framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, *raw_ack_packet)); - // Now make sure we can turn our ack packet back into an ack frame + // Now make sure we can turn our ack packet back into an ack frame. ASSERT_TRUE(framer_.ProcessPacket(*ack_packet)); ASSERT_EQ(1u, visitor_.ack_frames_.size()); const QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0]; - EXPECT_EQ(0u, processed_ack_frame.sent_info.least_unacked); + EXPECT_EQ(header.packet_sequence_number - 1, + processed_ack_frame.sent_info.least_unacked); EXPECT_TRUE(processed_ack_frame.received_info.is_truncated); EXPECT_EQ(510u, processed_ack_frame.received_info.largest_observed); ASSERT_EQ(255u, processed_ack_frame.received_info.missing_packets.size()); @@ -3325,7 +4778,7 @@ TEST_P(QuicFramerTest, DISABLED_Truncation) { TEST_P(QuicFramerTest, CleanTruncation) { QuicPacketHeader header; - header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; @@ -3335,27 +4788,26 @@ TEST_P(QuicFramerTest, CleanTruncation) { QuicAckFrame ack_frame; ack_frame.received_info.largest_observed = 201; - ack_frame.sent_info.least_unacked = 0; + ack_frame.sent_info.least_unacked = header.packet_sequence_number - 2; for (uint64 i = 1; i < ack_frame.received_info.largest_observed; ++i) { ack_frame.received_info.missing_packets.insert(i); } - // Create a packet with just the ack + // Create a packet with just the ack. QuicFrame frame; frame.type = ACK_FRAME; frame.ack_frame = &ack_frame; QuicFrames frames; frames.push_back(frame); - scoped_ptr<QuicPacket> raw_ack_packet( - framer_.BuildUnsizedDataPacket(header, frames).packet); + scoped_ptr<QuicPacket> raw_ack_packet(BuildDataPacket(header, frames)); ASSERT_TRUE(raw_ack_packet != NULL); scoped_ptr<QuicEncryptedPacket> ack_packet( framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, *raw_ack_packet)); - // Now make sure we can turn our ack packet back into an ack frame + // Now make sure we can turn our ack packet back into an ack frame. ASSERT_TRUE(framer_.ProcessPacket(*ack_packet)); // Test for clean truncation of the ack by comparing the length of the @@ -3366,8 +4818,7 @@ TEST_P(QuicFramerTest, CleanTruncation) { frames.push_back(frame); size_t original_raw_length = raw_ack_packet->length(); - raw_ack_packet.reset( - framer_.BuildUnsizedDataPacket(header, frames).packet); + raw_ack_packet.reset(BuildDataPacket(header, frames)); ASSERT_TRUE(raw_ack_packet != NULL); EXPECT_EQ(original_raw_length, raw_ack_packet->length()); ASSERT_TRUE(raw_ack_packet != NULL); @@ -3375,9 +4826,9 @@ TEST_P(QuicFramerTest, CleanTruncation) { TEST_P(QuicFramerTest, EntropyFlagTest) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -3410,9 +4861,9 @@ TEST_P(QuicFramerTest, EntropyFlagTest) { TEST_P(QuicFramerTest, FecEntropyTest) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -3447,9 +4898,9 @@ TEST_P(QuicFramerTest, FecEntropyTest) { TEST_P(QuicFramerTest, StopPacketProcessing) { unsigned char packet[] = { - // public flags (8 byte guid) + // public flags (8 byte connection_id) 0x3C, - // guid + // connection_id 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // packet sequence number @@ -3498,6 +4949,7 @@ TEST_P(QuicFramerTest, StopPacketProcessing) { EXPECT_CALL(visitor, OnStreamFrame(_)).WillOnce(Return(false)); EXPECT_CALL(visitor, OnAckFrame(_)).Times(0); EXPECT_CALL(visitor, OnPacketComplete()); + EXPECT_CALL(visitor, OnUnauthenticatedPublicHeader(_)).WillOnce(Return(true)); QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); diff --git a/chromium/net/quic/quic_headers_stream.cc b/chromium/net/quic/quic_headers_stream.cc new file mode 100644 index 00000000000..c158e085055 --- /dev/null +++ b/chromium/net/quic/quic_headers_stream.cc @@ -0,0 +1,266 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_headers_stream.h" + +#include "net/quic/quic_session.h" + +using base::StringPiece; + +namespace net { + +namespace { + +const QuicStreamId kInvalidStreamId = 0; + +} // namespace + +// A SpdyFramer visitor which passed SYN_STREAM and SYN_REPLY frames to +// the QuicDataStream, and closes the connection if any unexpected frames +// are received. +class QuicHeadersStream::SpdyFramerVisitor + : public SpdyFramerVisitorInterface, + public SpdyFramerDebugVisitorInterface { + public: + explicit SpdyFramerVisitor(QuicHeadersStream* stream) : stream_(stream) {} + + // SpdyFramerVisitorInterface implementation + virtual void OnSynStream(SpdyStreamId stream_id, + SpdyStreamId associated_stream_id, + SpdyPriority priority, + bool fin, + bool unidirectional) OVERRIDE { + if (!stream_->IsConnected()) { + return; + } + + if (associated_stream_id != 0) { + CloseConnection("associated_stream_id != 0"); + return; + } + + if (unidirectional != 0) { + CloseConnection("unidirectional != 0"); + return; + } + + stream_->OnSynStream(stream_id, priority, fin); + } + + virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE { + if (!stream_->IsConnected()) { + return; + } + + stream_->OnSynReply(stream_id, fin); + } + + virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id, + const char* header_data, + size_t len) OVERRIDE { + if (!stream_->IsConnected()) { + return false; + } + stream_->OnControlFrameHeaderData(stream_id, header_data, len); + return true; + } + + virtual void OnStreamFrameData(SpdyStreamId stream_id, + const char* data, + size_t len, + bool fin) OVERRIDE { + if (fin && len == 0) { + // The framer invokes OnStreamFrameData with zero-length data and + // fin = true after processing a SYN_STREAM or SYN_REPLY frame + // that had the fin bit set. + return; + } + CloseConnection("SPDY DATA frame received."); + } + + virtual void OnError(SpdyFramer* framer) OVERRIDE { + CloseConnection("SPDY framing error."); + } + + virtual void OnDataFrameHeader(SpdyStreamId stream_id, + size_t length, + bool fin) OVERRIDE { + CloseConnection("SPDY DATA frame received."); + } + + virtual void OnRstStream(SpdyStreamId stream_id, + SpdyRstStreamStatus status) OVERRIDE { + CloseConnection("SPDY RST_STREAM frame received."); + } + + virtual void OnSetting(SpdySettingsIds id, + uint8 flags, + uint32 value) OVERRIDE { + CloseConnection("SPDY SETTINGS frame received."); + } + + virtual void OnSettingsAck() OVERRIDE { + CloseConnection("SPDY SETTINGS frame received."); + } + + virtual void OnSettingsEnd() OVERRIDE { + CloseConnection("SPDY SETTINGS frame received."); + } + + virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE { + CloseConnection("SPDY PING frame received."); + } + + virtual void OnGoAway(SpdyStreamId last_accepted_stream_id, + SpdyGoAwayStatus status) OVERRIDE { + CloseConnection("SPDY GOAWAY frame received."); + } + + virtual void OnHeaders(SpdyStreamId stream_id, bool fin, bool end) OVERRIDE { + CloseConnection("SPDY HEADERS frame received."); + } + + virtual void OnWindowUpdate(SpdyStreamId stream_id, + uint32 delta_window_size) OVERRIDE { + CloseConnection("SPDY WINDOW_UPDATE frame received."); + } + + virtual void OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id, + bool end) OVERRIDE { + LOG(DFATAL) << "PUSH_PROMISE frame received from a SPDY/3 framer"; + CloseConnection("SPDY PUSH_PROMISE frame received."); + } + + virtual void OnContinuation(SpdyStreamId stream_id, bool end) OVERRIDE { + CloseConnection("SPDY CONTINUATION frame received."); + } + + // SpdyFramerDebugVisitorInterface implementation + virtual void OnSendCompressedFrame(SpdyStreamId stream_id, + SpdyFrameType type, + size_t payload_len, + size_t frame_len) OVERRIDE {} + + virtual void OnReceiveCompressedFrame(SpdyStreamId stream_id, + SpdyFrameType type, + size_t frame_len) OVERRIDE { + if (stream_->IsConnected()) { + stream_->OnCompressedFrameSize(frame_len); + } + } + + private: + void CloseConnection(const string& details) { + if (stream_->IsConnected()) { + stream_->CloseConnectionWithDetails( + QUIC_INVALID_HEADERS_STREAM_DATA, details); + } + } + + private: + QuicHeadersStream* stream_; + + DISALLOW_COPY_AND_ASSIGN(SpdyFramerVisitor); +}; + +QuicHeadersStream::QuicHeadersStream(QuicSession* session) + : ReliableQuicStream(kHeadersStreamId, session), + stream_id_(kInvalidStreamId), + fin_(false), + frame_len_(0), + spdy_framer_(SPDY3), + spdy_framer_visitor_(new SpdyFramerVisitor(this)) { + spdy_framer_.set_visitor(spdy_framer_visitor_.get()); + spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get()); + // TODO(jri): Set headers to be always FEC protected. + DisableFlowControl(); +} + +QuicHeadersStream::~QuicHeadersStream() {} + +size_t QuicHeadersStream::WriteHeaders( + QuicStreamId stream_id, + const SpdyHeaderBlock& headers, + bool fin, + QuicAckNotifier::DelegateInterface* ack_notifier_delegate) { + scoped_ptr<SpdySerializedFrame> frame; + if (session()->is_server()) { + SpdySynReplyIR syn_reply(stream_id); + syn_reply.set_name_value_block(headers); + syn_reply.set_fin(fin); + frame.reset(spdy_framer_.SerializeFrame(syn_reply)); + } else { + SpdySynStreamIR syn_stream(stream_id); + syn_stream.set_name_value_block(headers); + syn_stream.set_fin(fin); + frame.reset(spdy_framer_.SerializeFrame(syn_stream)); + } + WriteOrBufferData(StringPiece(frame->data(), frame->size()), false, + ack_notifier_delegate); + return frame->size(); +} + +uint32 QuicHeadersStream::ProcessRawData(const char* data, + uint32 data_len) { + return spdy_framer_.ProcessInput(data, data_len); +} + +QuicPriority QuicHeadersStream::EffectivePriority() const { return 0; } + +void QuicHeadersStream::OnSynStream(SpdyStreamId stream_id, + SpdyPriority priority, + bool fin) { + if (!session()->is_server()) { + CloseConnectionWithDetails( + QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY SYN_STREAM frame received at the client"); + return; + } + DCHECK_EQ(kInvalidStreamId, stream_id_); + stream_id_ = stream_id; + fin_ = fin; + session()->OnStreamHeadersPriority(stream_id, priority); +} + +void QuicHeadersStream::OnSynReply(SpdyStreamId stream_id, bool fin) { + if (session()->is_server()) { + CloseConnectionWithDetails( + QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY SYN_REPLY frame received at the server"); + return; + } + DCHECK_EQ(kInvalidStreamId, stream_id_); + stream_id_ = stream_id; + fin_ = fin; +} + +void QuicHeadersStream::OnControlFrameHeaderData(SpdyStreamId stream_id, + const char* header_data, + size_t len) { + DCHECK_EQ(stream_id_, stream_id); + if (len == 0) { + DCHECK_NE(0u, stream_id_); + DCHECK_NE(0u, frame_len_); + session()->OnStreamHeadersComplete(stream_id_, fin_, frame_len_); + // Reset state for the next frame. + stream_id_ = kInvalidStreamId; + fin_ = false; + frame_len_ = 0; + } else { + session()->OnStreamHeaders(stream_id_, StringPiece(header_data, len)); + } +} + +void QuicHeadersStream::OnCompressedFrameSize(size_t frame_len) { + DCHECK_EQ(kInvalidStreamId, stream_id_); + DCHECK_EQ(0u, frame_len_); + frame_len_ = frame_len; +} + +bool QuicHeadersStream::IsConnected() { + return session()->connection()->connected(); +} + +} // namespace net diff --git a/chromium/net/quic/quic_headers_stream.h b/chromium/net/quic/quic_headers_stream.h new file mode 100644 index 00000000000..c3ccbdada40 --- /dev/null +++ b/chromium/net/quic/quic_headers_stream.h @@ -0,0 +1,82 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_QUIC_HEADERS_STREAM_H_ +#define NET_QUIC_QUIC_HEADERS_STREAM_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/net_export.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/reliable_quic_stream.h" +#include "net/spdy/spdy_framer.h" + +namespace net { + +// Headers in QUIC are sent as SPDY SYN_STREAM or SYN_REPLY frames +// over a reserved reliable stream with the id 2. Each endpoint (client +// and server) will allocate an instance of QuicHeadersStream to send +// and receive headers. +class NET_EXPORT_PRIVATE QuicHeadersStream : public ReliableQuicStream { + public: + explicit QuicHeadersStream(QuicSession* session); + virtual ~QuicHeadersStream(); + + // Writes |headers| for |stream_id| in a SYN_STREAM or SYN_REPLY + // frame to the peer. If |fin| is true, the fin flag will be set on + // the SPDY frame. Returns the size, in bytes, of the resulting + // SPDY frame. + size_t WriteHeaders( + QuicStreamId stream_id, + const SpdyHeaderBlock& headers, + bool fin, + QuicAckNotifier::DelegateInterface* ack_notifier_delegate); + + // ReliableQuicStream implementation + virtual uint32 ProcessRawData(const char* data, uint32 data_len) OVERRIDE; + virtual QuicPriority EffectivePriority() const OVERRIDE; + + private: + class SpdyFramerVisitor; + + // The following methods are called by the SimpleVisitor. + + // Called when a SYN_STREAM frame has been received. + void OnSynStream(SpdyStreamId stream_id, + SpdyPriority priority, + bool fin); + + // Called when a SYN_REPLY frame been received. + void OnSynReply(SpdyStreamId stream_id, bool fin); + + // Called when a chunk of header data is available. This is called + // after OnSynStream, or OnSynReply. + // |stream_id| The stream receiving the header data. + // |header_data| A buffer containing the header data chunk received. + // |len| The length of the header data buffer. A length of zero indicates + // that the header data block has been completely sent. + void OnControlFrameHeaderData(SpdyStreamId stream_id, + const char* header_data, + size_t len); + + // Called when the size of the compressed frame payload is available. + void OnCompressedFrameSize(size_t frame_len); + + // Returns true if the session is still connected. + bool IsConnected(); + + // Data about the stream whose headers are being processed. + QuicStreamId stream_id_; + bool fin_; + size_t frame_len_; + + SpdyFramer spdy_framer_; + scoped_ptr<SpdyFramerVisitor> spdy_framer_visitor_; + + DISALLOW_COPY_AND_ASSIGN(QuicHeadersStream); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_HEADERS_STREAM_H_ diff --git a/chromium/net/quic/quic_headers_stream_test.cc b/chromium/net/quic/quic_headers_stream_test.cc new file mode 100644 index 00000000000..0aa71088102 --- /dev/null +++ b/chromium/net/quic/quic_headers_stream_test.cc @@ -0,0 +1,334 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_headers_stream.h" + +#include "net/quic/quic_flags.h" +#include "net/quic/quic_utils.h" +#include "net/quic/spdy_utils.h" +#include "net/quic/test_tools/quic_connection_peer.h" +#include "net/quic/test_tools/quic_session_peer.h" +#include "net/quic/test_tools/quic_test_utils.h" +#include "net/spdy/spdy_protocol.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::StringPiece; +using std::string; +using testing::Invoke; +using testing::StrictMock; +using testing::WithArgs; +using testing::_; + +namespace net { +namespace test { +namespace { + +class MockVisitor : public SpdyFramerVisitorInterface { + public: + MOCK_METHOD1(OnError, void(SpdyFramer* framer)); + MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId stream_id, + size_t length, + bool fin)); + MOCK_METHOD4(OnStreamFrameData, void(SpdyStreamId stream_id, + const char* data, + size_t len, + bool fin)); + MOCK_METHOD3(OnControlFrameHeaderData, bool(SpdyStreamId stream_id, + const char* header_data, + size_t len)); + MOCK_METHOD5(OnSynStream, void(SpdyStreamId stream_id, + SpdyStreamId associated_stream_id, + SpdyPriority priority, + bool fin, + bool unidirectional)); + MOCK_METHOD2(OnSynReply, void(SpdyStreamId stream_id, bool fin)); + MOCK_METHOD2(OnRstStream, void(SpdyStreamId stream_id, + SpdyRstStreamStatus status)); + MOCK_METHOD1(OnSettings, void(bool clear_persisted)); + MOCK_METHOD3(OnSetting, void(SpdySettingsIds id, uint8 flags, uint32 value)); + MOCK_METHOD0(OnSettingsAck, void()); + MOCK_METHOD0(OnSettingsEnd, void()); + MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack)); + MOCK_METHOD2(OnGoAway, void(SpdyStreamId last_accepted_stream_id, + SpdyGoAwayStatus status)); + MOCK_METHOD3(OnHeaders, void(SpdyStreamId stream_id, bool fin, bool end)); + MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId stream_id, + uint32 delta_window_size)); + MOCK_METHOD2(OnCredentialFrameData, bool(const char* credential_data, + size_t len)); + MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id)); + MOCK_METHOD3(OnPushPromise, void(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id, + bool end)); + MOCK_METHOD2(OnContinuation, void(SpdyStreamId stream_id, bool end)); + MOCK_METHOD6(OnAltSvc, void(SpdyStreamId stream_id, + uint32 max_age, + uint16 port, + StringPiece protocol_id, + StringPiece host, + StringPiece origin)); +}; + +class QuicHeadersStreamTest : public ::testing::TestWithParam<bool> { + public: + static QuicVersionVector GetVersions() { + QuicVersionVector versions; + versions.push_back(QuicVersionMax()); + return versions; + } + + QuicHeadersStreamTest() + : connection_(new StrictMock<MockConnection>(is_server(), GetVersions())), + session_(connection_), + headers_stream_(QuicSessionPeer::GetHeadersStream(&session_)), + body_("hello world"), + framer_(SPDY3) { + headers_[":version"] = "HTTP/1.1"; + headers_[":status"] = "200 Ok"; + headers_["content-length"] = "11"; + framer_.set_visitor(&visitor_); + EXPECT_EQ(QuicVersionMax(), session_.connection()->version()); + EXPECT_TRUE(headers_stream_ != NULL); + } + + QuicConsumedData SaveIov(const IOVector& data) { + const iovec* iov = data.iovec(); + int count = data.Capacity(); + for (int i = 0 ; i < count; ++i) { + saved_data_.append(static_cast<char*>(iov[i].iov_base), iov[i].iov_len); + } + return QuicConsumedData(saved_data_.length(), false); + } + + bool SaveHeaderData(const char* data, int len) { + saved_header_data_.append(data, len); + return true; + } + + void SaveHeaderDataStringPiece(StringPiece data) { + saved_header_data_.append(data.data(), data.length()); + } + + void WriteHeadersAndExpectSynStream(QuicStreamId stream_id, + bool fin, + QuicPriority priority) { + WriteHeadersAndCheckData(stream_id, fin, priority, SYN_STREAM); + } + + void WriteHeadersAndExpectSynReply(QuicStreamId stream_id, + bool fin) { + WriteHeadersAndCheckData(stream_id, fin, 0, SYN_REPLY); + } + + void WriteHeadersAndCheckData(QuicStreamId stream_id, + bool fin, + QuicPriority priority, + SpdyFrameType type) { + // Write the headers and capture the outgoing data + EXPECT_CALL(session_, WritevData(kHeadersStreamId, _, _, false, _, NULL)) + .WillOnce(WithArgs<1>(Invoke(this, &QuicHeadersStreamTest::SaveIov))); + headers_stream_->WriteHeaders(stream_id, headers_, fin, NULL); + + // Parse the outgoing data and check that it matches was was written. + if (type == SYN_STREAM) { + EXPECT_CALL(visitor_, OnSynStream(stream_id, kNoAssociatedStream, 0, + // priority, + fin, kNotUnidirectional)); + } else { + EXPECT_CALL(visitor_, OnSynReply(stream_id, fin)); + } + EXPECT_CALL(visitor_, OnControlFrameHeaderData(stream_id, _, _)) + .WillRepeatedly(WithArgs<1, 2>( + Invoke(this, &QuicHeadersStreamTest::SaveHeaderData))); + if (fin) { + EXPECT_CALL(visitor_, OnStreamFrameData(stream_id, NULL, 0, true)); + } + framer_.ProcessInput(saved_data_.data(), saved_data_.length()); + EXPECT_FALSE(framer_.HasError()) << framer_.error_code(); + + CheckHeaders(); + saved_data_.clear(); + } + + void CheckHeaders() { + SpdyHeaderBlock headers; + EXPECT_EQ(saved_header_data_.length(), + framer_.ParseHeaderBlockInBuffer(saved_header_data_.data(), + saved_header_data_.length(), + &headers)); + EXPECT_EQ(headers_, headers); + saved_header_data_.clear(); + } + + bool is_server() { + return GetParam(); + } + + void CloseConnection() { + QuicConnectionPeer::CloseConnection(connection_); + } + + static const bool kNotUnidirectional = false; + static const bool kNoAssociatedStream = false; + + StrictMock<MockConnection>* connection_; + StrictMock<MockSession> session_; + QuicHeadersStream* headers_stream_; + SpdyHeaderBlock headers_; + string body_; + string saved_data_; + string saved_header_data_; + SpdyFramer framer_; + StrictMock<MockVisitor> visitor_; +}; + +INSTANTIATE_TEST_CASE_P(Tests, QuicHeadersStreamTest, testing::Bool()); + +TEST_P(QuicHeadersStreamTest, StreamId) { + EXPECT_EQ(3u, headers_stream_->id()); +} + +TEST_P(QuicHeadersStreamTest, EffectivePriority) { + EXPECT_EQ(0u, headers_stream_->EffectivePriority()); +} + +TEST_P(QuicHeadersStreamTest, WriteHeaders) { + for (QuicStreamId stream_id = kClientDataStreamId1; + stream_id < kClientDataStreamId3; stream_id += 2) { + for (int count = 0; count < 2; ++count) { + bool fin = (count == 0); + if (is_server()) { + WriteHeadersAndExpectSynReply(stream_id, fin); + } else { + for (QuicPriority priority = 0; priority < 7; ++priority) { + WriteHeadersAndExpectSynStream(stream_id, fin, priority); + } + } + } + } +} + +TEST_P(QuicHeadersStreamTest, ProcessRawData) { + for (QuicStreamId stream_id = kClientDataStreamId1; + stream_id < kClientDataStreamId3; stream_id += 2) { + for (int count = 0; count < 2; ++count) { + bool fin = (count == 0); + for (QuicPriority priority = 0; priority < 7; ++priority) { + // Replace with "WriteHeadersAndSaveData" + scoped_ptr<SpdySerializedFrame> frame; + if (is_server()) { + SpdySynStreamIR syn_stream(stream_id); + syn_stream.set_name_value_block(headers_); + syn_stream.set_fin(fin); + frame.reset(framer_.SerializeSynStream(syn_stream)); + EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0)); + } else { + SpdySynReplyIR syn_reply(stream_id); + syn_reply.set_name_value_block(headers_); + syn_reply.set_fin(fin); + frame.reset(framer_.SerializeSynReply(syn_reply)); + } + EXPECT_CALL(session_, OnStreamHeaders(stream_id, _)) + .WillRepeatedly(WithArgs<1>( + Invoke(this, + &QuicHeadersStreamTest::SaveHeaderDataStringPiece))); + EXPECT_CALL(session_, + OnStreamHeadersComplete(stream_id, fin, frame->size())); + headers_stream_->ProcessRawData(frame->data(), frame->size()); + + CheckHeaders(); + } + } + } +} + +TEST_P(QuicHeadersStreamTest, ProcessSpdyDataFrame) { + SpdyDataIR data(2, ""); + scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data)); + EXPECT_CALL(*connection_, + SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY DATA frame received.")) + .WillOnce(InvokeWithoutArgs(this, + &QuicHeadersStreamTest::CloseConnection)); + headers_stream_->ProcessRawData(frame->data(), frame->size()); +} + +TEST_P(QuicHeadersStreamTest, ProcessSpdyRstStreamFrame) { + SpdyRstStreamIR data(2, RST_STREAM_PROTOCOL_ERROR, ""); + scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data)); + EXPECT_CALL(*connection_, + SendConnectionCloseWithDetails( + QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY RST_STREAM frame received.")) + .WillOnce(InvokeWithoutArgs(this, + &QuicHeadersStreamTest::CloseConnection)); + headers_stream_->ProcessRawData(frame->data(), frame->size()); +} + +TEST_P(QuicHeadersStreamTest, ProcessSpdySettingsFrame) { + SpdySettingsIR data; + data.AddSetting(SETTINGS_UPLOAD_BANDWIDTH, true, true, 0); + scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data)); + EXPECT_CALL(*connection_, + SendConnectionCloseWithDetails( + QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY SETTINGS frame received.")) + .WillOnce(InvokeWithoutArgs(this, + &QuicHeadersStreamTest::CloseConnection)); + headers_stream_->ProcessRawData(frame->data(), frame->size()); +} + +TEST_P(QuicHeadersStreamTest, ProcessSpdyPingFrame) { + SpdyPingIR data(1); + scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data)); + EXPECT_CALL(*connection_, + SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY PING frame received.")) + .WillOnce(InvokeWithoutArgs(this, + &QuicHeadersStreamTest::CloseConnection)); + headers_stream_->ProcessRawData(frame->data(), frame->size()); +} + +TEST_P(QuicHeadersStreamTest, ProcessSpdyGoAwayFrame) { + SpdyGoAwayIR data(1, GOAWAY_PROTOCOL_ERROR, "go away"); + scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data)); + EXPECT_CALL(*connection_, + SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY GOAWAY frame received.")) + .WillOnce(InvokeWithoutArgs(this, + &QuicHeadersStreamTest::CloseConnection)); + headers_stream_->ProcessRawData(frame->data(), frame->size()); +} + +TEST_P(QuicHeadersStreamTest, ProcessSpdyHeadersFrame) { + SpdyHeadersIR data(1); + scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data)); + EXPECT_CALL(*connection_, + SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY HEADERS frame received.")) + .WillOnce(InvokeWithoutArgs(this, + &QuicHeadersStreamTest::CloseConnection)); + headers_stream_->ProcessRawData(frame->data(), frame->size()); +} + +TEST_P(QuicHeadersStreamTest, ProcessSpdyWindowUpdateFrame) { + SpdyWindowUpdateIR data(1, 1); + scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data)); + EXPECT_CALL(*connection_, + SendConnectionCloseWithDetails( + QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY WINDOW_UPDATE frame received.")) + .WillOnce(InvokeWithoutArgs(this, + &QuicHeadersStreamTest::CloseConnection)); + headers_stream_->ProcessRawData(frame->data(), frame->size()); +} + +TEST_P(QuicHeadersStreamTest, NoFlowControl) { + ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true); + EXPECT_FALSE(headers_stream_->flow_controller()->IsEnabled()); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_http_stream.cc b/chromium/net/quic/quic_http_stream.cc index f8b2e6517dc..20b60deb064 100644 --- a/chromium/net/quic/quic_http_stream.cc +++ b/chromium/net/quic/quic_http_stream.cc @@ -5,6 +5,7 @@ #include "net/quic/quic_http_stream.h" #include "base/callback_helpers.h" +#include "base/metrics/histogram.h" #include "base/strings/stringprintf.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" @@ -37,6 +38,7 @@ QuicHttpStream::QuicHttpStream(const base::WeakPtr<QuicClientSession>& session) response_status_(OK), response_headers_received_(false), read_buf_(new GrowableIOBuffer()), + closed_stream_received_bytes_(0), user_buffer_len_(0), weak_factory_(this) { DCHECK(session_); @@ -60,13 +62,16 @@ int QuicHttpStream::InitializeStream(const HttpRequestInfo* request_info, if (request_info->url.SchemeIsSecure()) { SSLInfo ssl_info; - if (!session_->GetSSLInfo(&ssl_info) || !ssl_info.cert) { + bool secure_session = session_->GetSSLInfo(&ssl_info) && ssl_info.cert; + UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.SecureResourceSecureSession", + secure_session); + if (!secure_session) return ERR_REQUEST_FOR_SECURE_RESOURCE_OVER_INSECURE_QUIC; - } } stream_net_log_ = stream_net_log; request_info_ = request_info; + request_time_ = base::Time::Now(); priority_ = priority; int rv = stream_request_.StartRequest( @@ -117,12 +122,9 @@ int QuicHttpStream::SendRequest(const HttpRequestHeaders& request_headers, // was being called even if we didn't yet allocate raw_request_body_buf_. // && (request_body_stream_->size() || // request_body_stream_->is_chunked())) - // - // Use kMaxPacketSize as the buffer size, since the request - // body data is written with this size at a time. - // TODO(rch): use a smarter value since we can't write an entire - // packet due to overhead. - raw_request_body_buf_ = new IOBufferWithSize(kMaxPacketSize); + // Use 10 packets as the body buffer size to give enough space to + // help ensure we don't often send out partial packets. + raw_request_body_buf_ = new IOBufferWithSize(10 * kMaxPacketSize); // The request body buffer is empty at first. request_body_buf_ = new DrainableIOBuffer(raw_request_body_buf_.get(), 0); } @@ -162,10 +164,6 @@ int QuicHttpStream::ReadResponseHeaders(const CompletionCallback& callback) { return ERR_IO_PENDING; } -const HttpResponseInfo* QuicHttpStream::GetResponseInfo() const { - return response_info_; -} - int QuicHttpStream::ReadResponseBody( IOBuffer* buf, int buf_len, const CompletionCallback& callback) { CHECK(buf); @@ -213,6 +211,7 @@ int QuicHttpStream::ReadResponseBody( void QuicHttpStream::Close(bool not_reusable) { // Note: the not_reusable flag has no meaning for SPDY streams. if (stream_) { + closed_stream_received_bytes_ = stream_->stream_bytes_read(); stream_->SetDelegate(NULL); stream_->Reset(QUIC_STREAM_CANCELLED); stream_ = NULL; @@ -246,8 +245,11 @@ bool QuicHttpStream::IsConnectionReusable() const { } int64 QuicHttpStream::GetTotalReceivedBytes() const { - // TODO(eustas): Implement. - return 0; + if (stream_) { + return stream_->stream_bytes_read(); + } + + return closed_stream_received_bytes_; } bool QuicHttpStream::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const { @@ -327,6 +329,7 @@ void QuicHttpStream::OnClose(QuicErrorCode error) { response_status_ = ERR_ABORTED; } + closed_stream_received_bytes_ = stream_->stream_bytes_read(); stream_ = NULL; if (!callback_.is_null()) DoCallback(response_status_); @@ -413,32 +416,23 @@ int QuicHttpStream::DoSendHeaders() { if (!stream_) return ERR_UNEXPECTED; - if (request_.empty() && !stream_->CanWrite( - base::Bind(&QuicHttpStream::OnIOComplete, - weak_factory_.GetWeakPtr()))) { - // Do not compress headers unless it is likely that they can be sent. - next_state_ = STATE_SEND_HEADERS; - return ERR_IO_PENDING; - } - request_ = stream_->compressor()->CompressHeadersWithPriority( - ConvertRequestPriorityToQuicPriority(priority_), request_headers_); - // Log the actual request with the URL Request's net log. stream_net_log_.AddEvent( - NetLog::TYPE_HTTP_TRANSACTION_SPDY_SEND_REQUEST_HEADERS, - base::Bind(&SpdyHeaderBlockNetLogCallback, &request_headers_)); + NetLog::TYPE_HTTP_TRANSACTION_QUIC_SEND_REQUEST_HEADERS, + base::Bind(&QuicRequestNetLogCallback, stream_->id(), &request_headers_, + priority_)); // Also log to the QuicSession's net log. stream_->net_log().AddEvent( NetLog::TYPE_QUIC_HTTP_STREAM_SEND_REQUEST_HEADERS, - base::Bind(&SpdyHeaderBlockNetLogCallback, &request_headers_)); - request_headers_.clear(); + base::Bind(&QuicRequestNetLogCallback, stream_->id(), &request_headers_, + priority_)); bool has_upload_data = request_body_stream_ != NULL; next_state_ = STATE_SEND_HEADERS_COMPLETE; - return stream_->WriteStreamData( - request_, !has_upload_data, - base::Bind(&QuicHttpStream::OnIOComplete, weak_factory_.GetWeakPtr())); + int rv = stream_->WriteHeaders(request_headers_, !has_upload_data, NULL); + request_headers_.clear(); + return rv; } int QuicHttpStream::DoSendHeadersComplete(int rv) { @@ -546,6 +540,8 @@ int QuicHttpStream::ParseResponseHeaders() { .Init(*request_info_, *response_info_->headers.get()); response_info_->was_npn_negotiated = true; response_info_->npn_negotiated_protocol = "quic/1+spdy/3"; + response_info_->response_time = base::Time::Now(); + response_info_->request_time = request_time_; response_headers_received_ = true; return OK; diff --git a/chromium/net/quic/quic_http_stream.h b/chromium/net/quic/quic_http_stream.h index 7a96ea3d206..5c608226315 100644 --- a/chromium/net/quic/quic_http_stream.h +++ b/chromium/net/quic/quic_http_stream.h @@ -41,7 +41,6 @@ class NET_EXPORT_PRIVATE QuicHttpStream : const CompletionCallback& callback) OVERRIDE; virtual UploadProgress GetUploadProgress() const OVERRIDE; virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE; - virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE; virtual int ReadResponseBody(IOBuffer* buf, int buf_len, const CompletionCallback& callback) OVERRIDE; @@ -119,6 +118,8 @@ class NET_EXPORT_PRIVATE QuicHttpStream : const HttpRequestInfo* request_info_; // The request body to send, if any, owned by the caller. UploadDataStream* request_body_stream_; + // Time the request was issued. + base::Time request_time_; // The priority of the request. RequestPriority priority_; // |response_info_| is the HTTP response data object which is filled in @@ -145,6 +146,9 @@ class NET_EXPORT_PRIVATE QuicHttpStream : // TODO(rch): This is infinite buffering, which is bad. std::list<scoped_refptr<IOBufferWithSize> > response_body_; + // Number of bytes received when the stream was closed. + int64 closed_stream_received_bytes_; + // The caller's callback to be used for asynchronous operations. CompletionCallback callback_; @@ -160,6 +164,8 @@ class NET_EXPORT_PRIVATE QuicHttpStream : BoundNetLog stream_net_log_; base::WeakPtrFactory<QuicHttpStream> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(QuicHttpStream); }; } // namespace net diff --git a/chromium/net/quic/quic_http_stream_test.cc b/chromium/net/quic/quic_http_stream_test.cc index 97ae97261be..c0643d4458d 100644 --- a/chromium/net/quic/quic_http_stream_test.cc +++ b/chromium/net/quic/quic_http_stream_test.cc @@ -16,17 +16,20 @@ #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/crypto/quic_server_info.h" #include "net/quic/quic_client_session.h" #include "net/quic/quic_connection.h" #include "net/quic/quic_connection_helper.h" #include "net/quic/quic_default_packet_writer.h" #include "net/quic/quic_http_utils.h" #include "net/quic/quic_reliable_client_stream.h" +#include "net/quic/quic_write_blocked_list.h" #include "net/quic/spdy_utils.h" #include "net/quic/test_tools/mock_clock.h" #include "net/quic/test_tools/mock_crypto_client_stream_factory.h" #include "net/quic/test_tools/mock_random.h" #include "net/quic/test_tools/quic_connection_peer.h" +#include "net/quic/test_tools/quic_test_packet_maker.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/quic/test_tools/test_task_runner.h" #include "net/socket/socket_test_util.h" @@ -34,7 +37,6 @@ #include "net/spdy/spdy_framer.h" #include "net/spdy/spdy_http_utils.h" #include "net/spdy/spdy_protocol.h" -#include "net/spdy/write_blocked_list.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -47,15 +49,18 @@ namespace test { namespace { const char kUploadData[] = "hello world!"; +const char kServerHostname[] = "www.google.com"; +const uint16 kServerPort = 80; class TestQuicConnection : public QuicConnection { public: - TestQuicConnection(QuicGuid guid, + TestQuicConnection(const QuicVersionVector& versions, + QuicConnectionId connection_id, IPEndPoint address, QuicConnectionHelper* helper, QuicPacketWriter* writer) - : QuicConnection(guid, address, helper, writer, false, - QuicSupportedVersions()) { + : QuicConnection(connection_id, address, helper, writer, false, + versions) { } void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) { @@ -69,27 +74,13 @@ class TestQuicConnection : public QuicConnection { class TestReceiveAlgorithm : public ReceiveAlgorithmInterface { public: - explicit TestReceiveAlgorithm(QuicCongestionFeedbackFrame* feedback) - : feedback_(feedback) { + virtual bool GenerateCongestionFeedback( + QuicCongestionFeedbackFrame* /*congestion_feedback*/) { + return false; } - bool GenerateCongestionFeedback( - QuicCongestionFeedbackFrame* congestion_feedback) { - if (feedback_ == NULL) { - return false; - } - *congestion_feedback = *feedback_; - return true; - } - - MOCK_METHOD4(RecordIncomingPacket, - void(QuicByteCount, QuicPacketSequenceNumber, QuicTime, bool)); - - private: - MockClock clock_; - QuicCongestionFeedbackFrame* feedback_; - - DISALLOW_COPY_AND_ASSIGN(TestReceiveAlgorithm); + MOCK_METHOD3(RecordIncomingPacket, + void(QuicByteCount, QuicPacketSequenceNumber, QuicTime)); }; // Subclass of QuicHttpStream that closes itself when the first piece of data @@ -116,9 +107,12 @@ class QuicHttpStreamPeer { } }; -class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { +class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> { protected: - const static bool kFin = true; + static const bool kFin = true; + static const bool kIncludeVersion = true; + static const bool kIncludeCongestionFeedback = true; + // Holds a packet to be written to the wire, and the IO mode that should // be used by the mock socket when performing the write. struct PacketToWrite { @@ -134,10 +128,10 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { : net_log_(BoundNetLog()), use_closing_stream_(false), read_buffer_(new IOBufferWithSize(4096)), - guid_(2), - framer_(QuicSupportedVersions(), QuicTime::Zero(), false), - random_generator_(0), - creator_(guid_, &framer_, &random_generator_, false) { + connection_id_(2), + stream_id_(kClientDataStreamId1), + maker_(GetParam(), connection_id_), + random_generator_(0) { IPAddressNumber ip; CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); peer_addr_ = IPEndPoint(ip, 443); @@ -152,8 +146,8 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { } // Adds a packet to the list of expected writes. - void AddWrite(IoMode mode, QuicEncryptedPacket* packet) { - writes_.push_back(PacketToWrite(mode, packet)); + void AddWrite(scoped_ptr<QuicEncryptedPacket> packet) { + writes_.push_back(PacketToWrite(SYNCHRONOUS, packet.release())); } // Returns the packet to be written at position |pos|. @@ -165,8 +159,8 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { return socket_data_->at_read_eof() && socket_data_->at_write_eof(); } - void ProcessPacket(const QuicEncryptedPacket& packet) { - connection_->ProcessUdpPacket(self_addr_, peer_addr_, packet); + void ProcessPacket(scoped_ptr<QuicEncryptedPacket> packet) { + connection_->ProcessUdpPacket(self_addr_, peer_addr_, *packet); } // Configures the test fixture to use the list of expected writes. @@ -186,25 +180,26 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { socket->Connect(peer_addr_); runner_ = new TestTaskRunner(&clock_); send_algorithm_ = new MockSendAlgorithm(); - receive_algorithm_ = new TestReceiveAlgorithm(NULL); - EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _)). + receive_algorithm_ = new TestReceiveAlgorithm(); + EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _)). Times(AnyNumber()); EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, _, _, _)).Times(AnyNumber()); + OnPacketSent(_, _, _, _, _)).WillRepeatedly(Return(true)); EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly( Return(QuicTime::Delta::Zero())); - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _, _)). + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly( + Return(kMaxPacketSize)); + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)). WillRepeatedly(Return(QuicTime::Delta::Zero())); - EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillRepeatedly( - Return(QuicTime::Delta::Zero())); EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillRepeatedly( Return(QuicBandwidth::Zero())); EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)).Times(AnyNumber()); helper_.reset(new QuicConnectionHelper(runner_.get(), &clock_, &random_generator_)); writer_.reset(new QuicDefaultPacketWriter(socket)); - connection_ = new TestQuicConnection(guid_, peer_addr_, helper_.get(), - writer_.get()); + connection_ = new TestQuicConnection(SupportedVersions(GetParam()), + connection_id_, peer_addr_, + helper_.get(), writer_.get()); connection_->set_visitor(&visitor_); connection_->SetSendAlgorithm(send_algorithm_); connection_->SetReceiveAlgorithm(receive_algorithm_); @@ -214,89 +209,76 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { scoped_ptr<DatagramClientSocket>(socket), writer_.Pass(), NULL, &crypto_client_stream_factory_, - "www.google.com", DefaultQuicConfig(), - &crypto_config_, NULL)); + make_scoped_ptr((QuicServerInfo*)NULL), + QuicServerId(kServerHostname, kServerPort, + false, PRIVACY_MODE_DISABLED), + DefaultQuicConfig(), &crypto_config_, + base::MessageLoop::current()-> + message_loop_proxy().get(), + NULL)); session_->GetCryptoStream()->CryptoConnect(); EXPECT_TRUE(session_->IsCryptoHandshakeConfirmed()); stream_.reset(use_closing_stream_ ? new AutoClosingStream(session_->GetWeakPtr()) : new QuicHttpStream(session_->GetWeakPtr())); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20)); } - void SetRequestString(const std::string& method, - const std::string& path, - RequestPriority priority) { - SpdyHeaderBlock headers; - headers[":method"] = method; - headers[":host"] = "www.google.com"; - headers[":path"] = path; - headers[":scheme"] = "http"; - headers[":version"] = "HTTP/1.1"; - request_data_ = SerializeHeaderBlock(headers, true, priority); - } - - void SetResponseString(const std::string& status, const std::string& body) { - SpdyHeaderBlock headers; - headers[":status"] = status; - headers[":version"] = "HTTP/1.1"; - headers["content-type"] = "text/plain"; - response_data_ = SerializeHeaderBlock(headers, false, DEFAULT_PRIORITY) + - body; + void SetRequest(const std::string& method, + const std::string& path, + RequestPriority priority) { + request_headers_ = maker_.GetRequestHeaders(method, "http", path); } - std::string SerializeHeaderBlock(const SpdyHeaderBlock& headers, - bool write_priority, - RequestPriority priority) { - QuicSpdyCompressor compressor; - if (write_priority) { - return compressor.CompressHeadersWithPriority( - ConvertRequestPriorityToQuicPriority(priority), headers); - } - return compressor.CompressHeaders(headers); + void SetResponse(const std::string& status, const std::string& body) { + response_headers_ = maker_.GetResponseHeaders(status); + response_data_ = body; } - // Returns a newly created packet to send kData on stream 3. - QuicEncryptedPacket* ConstructDataPacket( + scoped_ptr<QuicEncryptedPacket> ConstructDataPacket( QuicPacketSequenceNumber sequence_number, bool should_include_version, bool fin, QuicStreamOffset offset, base::StringPiece data) { - InitializeHeader(sequence_number, should_include_version); - QuicStreamFrame frame(3, fin, offset, MakeIOVector(data)); - return ConstructPacket(header_, QuicFrame(&frame)); + return maker_.MakeDataPacket( + sequence_number, stream_id_, should_include_version, fin, offset, data); } - // Returns a newly created packet to RST_STREAM stream 3. - QuicEncryptedPacket* ConstructRstStreamPacket( - QuicPacketSequenceNumber sequence_number) { - InitializeHeader(sequence_number, false); - QuicRstStreamFrame frame(3, QUIC_STREAM_CANCELLED); - return ConstructPacket(header_, QuicFrame(&frame)); + scoped_ptr<QuicEncryptedPacket> ConstructRequestHeadersPacket( + QuicPacketSequenceNumber sequence_number, + bool fin) { + return maker_.MakeRequestHeadersPacket( + sequence_number, stream_id_, kIncludeVersion, fin, request_headers_); } - // Returns a newly created packet to send ack data. - QuicEncryptedPacket* ConstructAckPacket( + scoped_ptr<QuicEncryptedPacket> ConstructResponseHeadersPacket( QuicPacketSequenceNumber sequence_number, - QuicPacketSequenceNumber largest_received, - QuicPacketSequenceNumber least_unacked) { - InitializeHeader(sequence_number, false); + bool fin) { + return maker_.MakeResponseHeadersPacket( + sequence_number, stream_id_, !kIncludeVersion, fin, response_headers_); + } - QuicAckFrame ack(largest_received, QuicTime::Zero(), least_unacked); - ack.sent_info.entropy_hash = 0; - ack.received_info.entropy_hash = 0; + scoped_ptr<QuicEncryptedPacket> ConstructRstStreamPacket( + QuicPacketSequenceNumber sequence_number) { + return maker_.MakeRstPacket( + sequence_number, true, stream_id_, + AdjustErrorForVersion(QUIC_RST_FLOW_CONTROL_ACCOUNTING, GetParam())); + } - return ConstructPacket(header_, QuicFrame(&ack)); + scoped_ptr<QuicEncryptedPacket> ConstructAckAndRstStreamPacket( + QuicPacketSequenceNumber sequence_number) { + return maker_.MakeAckAndRstPacket( + sequence_number, !kIncludeVersion, stream_id_, QUIC_STREAM_CANCELLED, + 2, 1, !kIncludeCongestionFeedback); } - // Returns a newly created packet to send ack data. - QuicEncryptedPacket* ConstructRstPacket( + scoped_ptr<QuicEncryptedPacket> ConstructAckPacket( QuicPacketSequenceNumber sequence_number, - QuicStreamId stream_id) { - InitializeHeader(sequence_number, false); - - QuicRstStreamFrame rst(stream_id, QUIC_STREAM_NO_ERROR); - return ConstructPacket(header_, QuicFrame(&rst)); + QuicPacketSequenceNumber largest_received, + QuicPacketSequenceNumber least_unacked) { + return maker_.MakeAckPacket(sequence_number, largest_received, + least_unacked, !kIncludeCongestionFeedback); } BoundNetLog net_log_; @@ -318,63 +300,44 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { HttpRequestHeaders headers_; HttpResponseInfo response_; scoped_refptr<IOBufferWithSize> read_buffer_; + SpdyHeaderBlock request_headers_; + SpdyHeaderBlock response_headers_; std::string request_data_; std::string response_data_; private: - void InitializeHeader(QuicPacketSequenceNumber sequence_number, - bool should_include_version) { - header_.public_header.guid = guid_; - header_.public_header.reset_flag = false; - header_.public_header.version_flag = should_include_version; - header_.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; - header_.packet_sequence_number = sequence_number; - header_.fec_group = 0; - header_.entropy_flag = false; - header_.fec_flag = false; - } - - QuicEncryptedPacket* ConstructPacket(const QuicPacketHeader& header, - const QuicFrame& frame) { - QuicFrames frames; - frames.push_back(frame); - scoped_ptr<QuicPacket> packet( - framer_.BuildUnsizedDataPacket(header_, frames).packet); - return framer_.EncryptPacket( - ENCRYPTION_NONE, header.packet_sequence_number, *packet); - } - - const QuicGuid guid_; - QuicFramer framer_; + const QuicConnectionId connection_id_; + const QuicStreamId stream_id_; + QuicTestPacketMaker maker_; IPEndPoint self_addr_; IPEndPoint peer_addr_; MockRandom random_generator_; MockCryptoClientStreamFactory crypto_client_stream_factory_; - QuicPacketCreator creator_; - QuicPacketHeader header_; scoped_ptr<StaticSocketDataProvider> socket_data_; std::vector<PacketToWrite> writes_; }; -TEST_F(QuicHttpStreamTest, RenewStreamForAuth) { +INSTANTIATE_TEST_CASE_P(Version, QuicHttpStreamTest, + ::testing::ValuesIn(QuicSupportedVersions())); + +TEST_P(QuicHttpStreamTest, RenewStreamForAuth) { Initialize(); EXPECT_EQ(NULL, stream_->RenewStreamForAuth()); } -TEST_F(QuicHttpStreamTest, CanFindEndOfResponse) { +TEST_P(QuicHttpStreamTest, CanFindEndOfResponse) { Initialize(); EXPECT_TRUE(stream_->CanFindEndOfResponse()); } -TEST_F(QuicHttpStreamTest, IsConnectionReusable) { +TEST_P(QuicHttpStreamTest, IsConnectionReusable) { Initialize(); EXPECT_FALSE(stream_->IsConnectionReusable()); } -TEST_F(QuicHttpStreamTest, GetRequest) { - SetRequestString("GET", "/", DEFAULT_PRIORITY); - AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, - request_data_)); +TEST_P(QuicHttpStreamTest, GetRequest) { + SetRequest("GET", "/", DEFAULT_PRIORITY); + AddWrite(ConstructRequestHeadersPacket(1, kFin)); Initialize(); request_.method = "GET"; @@ -384,26 +347,23 @@ TEST_F(QuicHttpStreamTest, GetRequest) { net_log_, callback_.callback())); EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_, callback_.callback())); - EXPECT_EQ(&response_, stream_->GetResponseInfo()); // Ack the request. - scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0)); - ProcessPacket(*ack); + ProcessPacket(ConstructAckPacket(1, 0, 0)); EXPECT_EQ(ERR_IO_PENDING, stream_->ReadResponseHeaders(callback_.callback())); - // Send the response without a body. - SetResponseString("404 Not Found", std::string()); - scoped_ptr<QuicEncryptedPacket> resp( - ConstructDataPacket(2, false, kFin, 0, response_data_)); - ProcessPacket(*resp); + SetResponse("404 Not Found", std::string()); + ProcessPacket(ConstructResponseHeadersPacket(2, kFin)); // Now that the headers have been processed, the callback will return. EXPECT_EQ(OK, callback_.WaitForResult()); ASSERT_TRUE(response_.headers.get()); EXPECT_EQ(404, response_.headers->response_code()); EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain")); + EXPECT_FALSE(response_.response_time.is_null()); + EXPECT_FALSE(response_.request_time.is_null()); // There is no body, so this should return immediately. EXPECT_EQ(0, stream_->ReadResponseBody(read_buffer_.get(), @@ -414,10 +374,9 @@ TEST_F(QuicHttpStreamTest, GetRequest) { } // Regression test for http://crbug.com/288128 -TEST_F(QuicHttpStreamTest, GetRequestLargeResponse) { - SetRequestString("GET", "/", DEFAULT_PRIORITY); - AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, - request_data_)); +TEST_P(QuicHttpStreamTest, GetRequestLargeResponse) { + SetRequest("GET", "/", DEFAULT_PRIORITY); + AddWrite(ConstructRequestHeadersPacket(1, kFin)); Initialize(); request_.method = "GET"; @@ -427,11 +386,9 @@ TEST_F(QuicHttpStreamTest, GetRequestLargeResponse) { net_log_, callback_.callback())); EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_, callback_.callback())); - EXPECT_EQ(&response_, stream_->GetResponseInfo()); // Ack the request. - scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0)); - ProcessPacket(*ack); + ProcessPacket(ConstructAckPacket(1, 0, 0)); EXPECT_EQ(ERR_IO_PENDING, stream_->ReadResponseHeaders(callback_.callback())); @@ -461,55 +418,11 @@ TEST_F(QuicHttpStreamTest, GetRequestLargeResponse) { EXPECT_TRUE(AtEof()); } -TEST_F(QuicHttpStreamTest, GetRequestFullResponseInSinglePacket) { - SetRequestString("GET", "/", DEFAULT_PRIORITY); - AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_)); - Initialize(); - - request_.method = "GET"; - request_.url = GURL("http://www.google.com/"); - - EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, - net_log_, callback_.callback())); - EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_, - callback_.callback())); - EXPECT_EQ(&response_, stream_->GetResponseInfo()); - - // Ack the request. - scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0)); - ProcessPacket(*ack); - - EXPECT_EQ(ERR_IO_PENDING, - stream_->ReadResponseHeaders(callback_.callback())); - - // Send the response with a body. - SetResponseString("200 OK", "hello world!"); - scoped_ptr<QuicEncryptedPacket> resp( - ConstructDataPacket(2, false, kFin, 0, response_data_)); - ProcessPacket(*resp); - - // Now that the headers have been processed, the callback will return. - EXPECT_EQ(OK, callback_.WaitForResult()); - ASSERT_TRUE(response_.headers.get()); - EXPECT_EQ(200, response_.headers->response_code()); - EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain")); - - // There is no body, so this should return immediately. - // Since the body has already arrived, this should return immediately. - EXPECT_EQ(12, stream_->ReadResponseBody(read_buffer_.get(), - read_buffer_->size(), - callback_.callback())); - EXPECT_TRUE(stream_->IsResponseBodyComplete()); - EXPECT_TRUE(AtEof()); -} - -TEST_F(QuicHttpStreamTest, SendPostRequest) { - SetRequestString("POST", "/", DEFAULT_PRIORITY); - AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, !kFin, 0, request_data_)); - AddWrite(SYNCHRONOUS, ConstructDataPacket(2, true, kFin, - request_data_.length(), - kUploadData)); - AddWrite(SYNCHRONOUS, ConstructAckPacket(3, 3, 1)); +TEST_P(QuicHttpStreamTest, SendPostRequest) { + SetRequest("POST", "/", DEFAULT_PRIORITY); + AddWrite(ConstructRequestHeadersPacket(1, !kFin)); + AddWrite(ConstructDataPacket(2, kIncludeVersion, kFin, 0, kUploadData)); + AddWrite(ConstructAckPacket(3, 3, 1)); Initialize(); @@ -526,17 +439,13 @@ TEST_F(QuicHttpStreamTest, SendPostRequest) { net_log_, callback_.callback())); EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_, callback_.callback())); - EXPECT_EQ(&response_, stream_->GetResponseInfo()); // Ack both packets in the request. - scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0)); - ProcessPacket(*ack); + ProcessPacket(ConstructAckPacket(1, 0, 0)); // Send the response headers (but not the body). - SetResponseString("200 OK", std::string()); - scoped_ptr<QuicEncryptedPacket> resp( - ConstructDataPacket(2, false, !kFin, 0, response_data_)); - ProcessPacket(*resp); + SetResponse("200 OK", std::string()); + ProcessPacket(ConstructResponseHeadersPacket(2, !kFin)); // Since the headers have already arrived, this should return immediately. EXPECT_EQ(OK, stream_->ReadResponseHeaders(callback_.callback())); @@ -546,11 +455,7 @@ TEST_F(QuicHttpStreamTest, SendPostRequest) { // Send the response body. const char kResponseBody[] = "Hello world!"; - scoped_ptr<QuicEncryptedPacket> resp_body( - ConstructDataPacket(3, false, kFin, response_data_.length(), - kResponseBody)); - ProcessPacket(*resp_body); - + ProcessPacket(ConstructDataPacket(3, false, kFin, 0, kResponseBody)); // Since the body has already arrived, this should return immediately. EXPECT_EQ(static_cast<int>(strlen(kResponseBody)), stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(), @@ -560,18 +465,14 @@ TEST_F(QuicHttpStreamTest, SendPostRequest) { EXPECT_TRUE(AtEof()); } -TEST_F(QuicHttpStreamTest, SendChunkedPostRequest) { - SetRequestString("POST", "/", DEFAULT_PRIORITY); +TEST_P(QuicHttpStreamTest, SendChunkedPostRequest) { + SetRequest("POST", "/", DEFAULT_PRIORITY); size_t chunk_size = strlen(kUploadData); - AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, !kFin, 0, request_data_)); - AddWrite(SYNCHRONOUS, ConstructDataPacket(2, true, !kFin, - request_data_.length(), - kUploadData)); - AddWrite(SYNCHRONOUS, ConstructDataPacket(3, true, kFin, - request_data_.length() + chunk_size, - kUploadData)); - AddWrite(SYNCHRONOUS, ConstructAckPacket(4, 3, 1)); - + AddWrite(ConstructRequestHeadersPacket(1, !kFin)); + AddWrite(ConstructDataPacket(2, kIncludeVersion, !kFin, 0, kUploadData)); + AddWrite(ConstructDataPacket(3, kIncludeVersion, kFin, chunk_size, + kUploadData)); + AddWrite(ConstructAckPacket(4, 3, 1)); Initialize(); UploadDataStream upload_data_stream(UploadDataStream::CHUNKED, 0); @@ -586,19 +487,15 @@ TEST_F(QuicHttpStreamTest, SendChunkedPostRequest) { net_log_, callback_.callback())); ASSERT_EQ(ERR_IO_PENDING, stream_->SendRequest(headers_, &response_, callback_.callback())); - EXPECT_EQ(&response_, stream_->GetResponseInfo()); upload_data_stream.AppendChunk(kUploadData, chunk_size, true); // Ack both packets in the request. - scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0)); - ProcessPacket(*ack); + ProcessPacket(ConstructAckPacket(1, 0, 0)); // Send the response headers (but not the body). - SetResponseString("200 OK", std::string()); - scoped_ptr<QuicEncryptedPacket> resp( - ConstructDataPacket(2, false, !kFin, 0, response_data_)); - ProcessPacket(*resp); + SetResponse("200 OK", std::string()); + ProcessPacket(ConstructResponseHeadersPacket(2, !kFin)); // Since the headers have already arrived, this should return immediately. ASSERT_EQ(OK, stream_->ReadResponseHeaders(callback_.callback())); @@ -608,10 +505,8 @@ TEST_F(QuicHttpStreamTest, SendChunkedPostRequest) { // Send the response body. const char kResponseBody[] = "Hello world!"; - scoped_ptr<QuicEncryptedPacket> resp_body( - ConstructDataPacket(3, false, kFin, response_data_.length(), - kResponseBody)); - ProcessPacket(*resp_body); + ProcessPacket(ConstructDataPacket(3, false, kFin, response_data_.length(), + kResponseBody)); // Since the body has already arrived, this should return immediately. ASSERT_EQ(static_cast<int>(strlen(kResponseBody)), @@ -622,10 +517,10 @@ TEST_F(QuicHttpStreamTest, SendChunkedPostRequest) { EXPECT_TRUE(AtEof()); } -TEST_F(QuicHttpStreamTest, DestroyedEarly) { - SetRequestString("GET", "/", DEFAULT_PRIORITY); - AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_)); - AddWrite(SYNCHRONOUS, ConstructRstStreamPacket(2)); +TEST_P(QuicHttpStreamTest, DestroyedEarly) { + SetRequest("GET", "/", DEFAULT_PRIORITY); + AddWrite(ConstructRequestHeadersPacket(1, kFin)); + AddWrite(ConstructAckAndRstStreamPacket(2)); use_closing_stream_ = true; Initialize(); @@ -636,29 +531,24 @@ TEST_F(QuicHttpStreamTest, DestroyedEarly) { net_log_, callback_.callback())); EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_, callback_.callback())); - EXPECT_EQ(&response_, stream_->GetResponseInfo()); // Ack the request. - scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0)); - ProcessPacket(*ack); + ProcessPacket(ConstructAckPacket(1, 0, 0)); EXPECT_EQ(ERR_IO_PENDING, stream_->ReadResponseHeaders(callback_.callback())); // Send the response with a body. - SetResponseString("404 OK", "hello world!"); - scoped_ptr<QuicEncryptedPacket> resp( - ConstructDataPacket(2, false, kFin, 0, response_data_)); - + SetResponse("404 OK", "hello world!"); // In the course of processing this packet, the QuicHttpStream close itself. - ProcessPacket(*resp); + ProcessPacket(ConstructResponseHeadersPacket(2, kFin)); EXPECT_TRUE(AtEof()); } -TEST_F(QuicHttpStreamTest, Priority) { - SetRequestString("GET", "/", MEDIUM); - AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_)); - AddWrite(SYNCHRONOUS, ConstructRstStreamPacket(2)); +TEST_P(QuicHttpStreamTest, Priority) { + SetRequest("GET", "/", MEDIUM); + AddWrite(ConstructRequestHeadersPacket(1, kFin)); + AddWrite(ConstructAckAndRstStreamPacket(2)); use_closing_stream_ = true; Initialize(); @@ -672,38 +562,36 @@ TEST_F(QuicHttpStreamTest, Priority) { QuicReliableClientStream* reliable_stream = QuicHttpStreamPeer::GetQuicReliableClientStream(stream_.get()); DCHECK(reliable_stream); - DCHECK_EQ(static_cast<QuicPriority>(kHighestPriority), + DCHECK_EQ(QuicWriteBlockedList::kHighestPriority, reliable_stream->EffectivePriority()); EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_, callback_.callback())); - EXPECT_EQ(&response_, stream_->GetResponseInfo()); // Check that priority has now dropped back to MEDIUM. DCHECK_EQ(MEDIUM, ConvertQuicPriorityToRequestPriority( reliable_stream->EffectivePriority())); // Ack the request. - scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0)); - ProcessPacket(*ack); + ProcessPacket(ConstructAckPacket(1, 0, 0)); EXPECT_EQ(ERR_IO_PENDING, stream_->ReadResponseHeaders(callback_.callback())); // Send the response with a body. - SetResponseString("404 OK", "hello world!"); - scoped_ptr<QuicEncryptedPacket> resp( - ConstructDataPacket(2, false, kFin, 0, response_data_)); - + SetResponse("404 OK", "hello world!"); // In the course of processing this packet, the QuicHttpStream close itself. - ProcessPacket(*resp); + ProcessPacket(ConstructResponseHeadersPacket(2, kFin)); EXPECT_TRUE(AtEof()); } // Regression test for http://crbug.com/294870 -TEST_F(QuicHttpStreamTest, CheckPriorityWithNoDelegate) { - SetRequestString("GET", "/", MEDIUM); +TEST_P(QuicHttpStreamTest, CheckPriorityWithNoDelegate) { + SetRequest("GET", "/", MEDIUM); use_closing_stream_ = true; + + AddWrite(ConstructRstStreamPacket(1)); + Initialize(); request_.method = "GET"; @@ -718,48 +606,16 @@ TEST_F(QuicHttpStreamTest, CheckPriorityWithNoDelegate) { DCHECK(reliable_stream); QuicReliableClientStream::Delegate* delegate = reliable_stream->GetDelegate(); DCHECK(delegate); - DCHECK_EQ(static_cast<QuicPriority>(kHighestPriority), + DCHECK_EQ(QuicWriteBlockedList::kHighestPriority, reliable_stream->EffectivePriority()); // Set Delegate to NULL and make sure EffectivePriority returns highest // priority. reliable_stream->SetDelegate(NULL); - DCHECK_EQ(static_cast<QuicPriority>(kHighestPriority), + DCHECK_EQ(QuicWriteBlockedList::kHighestPriority, reliable_stream->EffectivePriority()); reliable_stream->SetDelegate(delegate); } -TEST_F(QuicHttpStreamTest, DontCompressHeadersWhenNotWritable) { - SetRequestString("GET", "/", MEDIUM); - AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_)); - - Initialize(); - request_.method = "GET"; - request_.url = GURL("http://www.google.com/"); - - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _, _)). - WillRepeatedly(Return(QuicTime::Delta::Infinite())); - EXPECT_EQ(OK, stream_->InitializeStream(&request_, MEDIUM, - net_log_, callback_.callback())); - EXPECT_EQ(ERR_IO_PENDING, stream_->SendRequest(headers_, &response_, - callback_.callback())); - - // Verify that the headers have not been compressed and buffered in - // the stream. - QuicReliableClientStream* reliable_stream = - QuicHttpStreamPeer::GetQuicReliableClientStream(stream_.get()); - EXPECT_FALSE(reliable_stream->HasBufferedData()); - EXPECT_FALSE(AtEof()); - - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _, _)). - WillRepeatedly(Return(QuicTime::Delta::Zero())); - - // Data should flush out now. - connection_->OnCanWrite(); - EXPECT_FALSE(reliable_stream->HasBufferedData()); - EXPECT_TRUE(AtEof()); -} - } // namespace test - } // namespace net diff --git a/chromium/net/quic/quic_http_utils.cc b/chromium/net/quic/quic_http_utils.cc index 173403d82dc..2bad8a6a71a 100644 --- a/chromium/net/quic/quic_http_utils.cc +++ b/chromium/net/quic/quic_http_utils.cc @@ -20,4 +20,16 @@ NET_EXPORT_PRIVATE RequestPriority ConvertQuicPriorityToRequestPriority( IDLE : static_cast<RequestPriority>(HIGHEST - priority); } +base::Value* QuicRequestNetLogCallback( + QuicStreamId stream_id, + const SpdyHeaderBlock* headers, + QuicPriority priority, + NetLog::LogLevel log_level) { + base::DictionaryValue* dict = static_cast<base::DictionaryValue*>( + SpdyHeaderBlockNetLogCallback(headers, log_level)); + dict->SetInteger("quic_priority", static_cast<int>(priority)); + dict->SetInteger("quic_stream_id", static_cast<int>(stream_id)); + return dict; +} + } // namespace net diff --git a/chromium/net/quic/quic_http_utils.h b/chromium/net/quic/quic_http_utils.h index c7e031ae605..862b7c61214 100644 --- a/chromium/net/quic/quic_http_utils.h +++ b/chromium/net/quic/quic_http_utils.h @@ -5,9 +5,11 @@ #ifndef NET_QUIC_QUIC_HTTP_UTILS_H_ #define NET_QUIC_QUIC_HTTP_UTILS_H_ +#include "base/values.h" #include "net/base/net_export.h" #include "net/base/request_priority.h" #include "net/quic/quic_protocol.h" +#include "net/spdy/spdy_header_block.h" namespace net { @@ -17,6 +19,14 @@ NET_EXPORT_PRIVATE QuicPriority ConvertRequestPriorityToQuicPriority( NET_EXPORT_PRIVATE RequestPriority ConvertQuicPriorityToRequestPriority( QuicPriority priority); +// Converts a SpdyHeaderBlock and priority into NetLog event parameters. Caller +// takes ownership of returned value. +NET_EXPORT base::Value* QuicRequestNetLogCallback( + QuicStreamId stream_id, + const SpdyHeaderBlock* headers, + QuicPriority priority, + NetLog::LogLevel log_level); + } // namespace net #endif // NET_QUIC_QUIC_HTTP_UTILS_H_ diff --git a/chromium/net/quic/quic_in_memory_cache.cc b/chromium/net/quic/quic_in_memory_cache.cc new file mode 100644 index 00000000000..45d25b24e40 --- /dev/null +++ b/chromium/net/quic/quic_in_memory_cache.cc @@ -0,0 +1,242 @@ +// 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/quic_in_memory_cache.h" + +#include "base/file_util.h" +#include "base/files/file_enumerator.h" +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "net/tools/balsa/balsa_headers.h" + +using base::FilePath; +using base::StringPiece; +using std::string; + +// Specifies the directory used during QuicInMemoryCache +// construction to seed the cache. Cache directory can be +// generated using `wget -p --save-headers <url> + +namespace net { + +namespace { + +const FilePath::CharType* g_quic_in_memory_cache_dir = FILE_PATH_LITERAL(""); + +// BalsaVisitor implementation (glue) which caches response bodies. +class CachingBalsaVisitor : public NoOpBalsaVisitor { + public: + CachingBalsaVisitor() : done_framing_(false) {} + virtual void ProcessBodyData(const char* input, size_t size) OVERRIDE { + AppendToBody(input, size); + } + virtual void MessageDone() OVERRIDE { + done_framing_ = true; + } + virtual void HandleHeaderError(BalsaFrame* framer) OVERRIDE { + UnhandledError(); + } + virtual void HandleHeaderWarning(BalsaFrame* framer) OVERRIDE { + UnhandledError(); + } + virtual void HandleChunkingError(BalsaFrame* framer) OVERRIDE { + UnhandledError(); + } + virtual void HandleBodyError(BalsaFrame* framer) OVERRIDE { + UnhandledError(); + } + void UnhandledError() { + LOG(DFATAL) << "Unhandled error framing HTTP."; + } + void AppendToBody(const char* input, size_t size) { + body_.append(input, size); + } + bool done_framing() const { return done_framing_; } + const string& body() const { return body_; } + + private: + bool done_framing_; + string body_; +}; + +} // namespace + +// static +QuicInMemoryCache* QuicInMemoryCache::GetInstance() { + return Singleton<QuicInMemoryCache>::get(); +} + +const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse( + const BalsaHeaders& request_headers) const { + ResponseMap::const_iterator it = responses_.find(GetKey(request_headers)); + if (it == responses_.end()) { + return NULL; + } + return it->second; +} + +void QuicInMemoryCache::AddSimpleResponse(StringPiece method, + StringPiece path, + StringPiece version, + StringPiece response_code, + StringPiece response_detail, + StringPiece body) { + BalsaHeaders request_headers, response_headers; + request_headers.SetRequestFirstlineFromStringPieces(method, + path, + version); + response_headers.SetRequestFirstlineFromStringPieces(version, + response_code, + response_detail); + response_headers.AppendHeader("content-length", + base::IntToString(body.length())); + + AddResponse(request_headers, response_headers, body); +} + +void QuicInMemoryCache::AddResponse(const BalsaHeaders& request_headers, + const BalsaHeaders& response_headers, + StringPiece response_body) { + VLOG(1) << "Adding response for: " << GetKey(request_headers); + if (ContainsKey(responses_, GetKey(request_headers))) { + LOG(DFATAL) << "Response for given request already exists!"; + return; + } + Response* new_response = new Response(); + new_response->set_headers(response_headers); + new_response->set_body(response_body); + responses_[GetKey(request_headers)] = new_response; +} + +void QuicInMemoryCache::AddSpecialResponse(StringPiece method, + StringPiece path, + StringPiece version, + SpecialResponseType response_type) { + BalsaHeaders request_headers, response_headers; + request_headers.SetRequestFirstlineFromStringPieces(method, + path, + version); + AddResponse(request_headers, response_headers, ""); + responses_[GetKey(request_headers)]->response_type_ = response_type; +} + +QuicInMemoryCache::QuicInMemoryCache() { + Initialize(); +} + +void QuicInMemoryCache::ResetForTests() { + STLDeleteValues(&responses_); + Initialize(); +} + +void QuicInMemoryCache::Initialize() { + // If there's no defined cache dir, we have no initialization to do. + if (g_quic_in_memory_cache_dir[0] == '\0') { + VLOG(1) << "No cache directory found. Skipping initialization."; + return; + } + VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: " + << g_quic_in_memory_cache_dir; + + FilePath directory(g_quic_in_memory_cache_dir); + base::FileEnumerator file_list(directory, + true, + base::FileEnumerator::FILES); + + FilePath file = file_list.Next(); + while (!file.empty()) { + // Need to skip files in .svn directories + if (file.value().find(FILE_PATH_LITERAL("/.svn/")) != std::string::npos) { + file = file_list.Next(); + continue; + } + + BalsaHeaders request_headers, response_headers; + + string file_contents; + base::ReadFileToString(file, &file_contents); + + // Frame HTTP. + CachingBalsaVisitor caching_visitor; + BalsaFrame framer; + framer.set_balsa_headers(&response_headers); + framer.set_balsa_visitor(&caching_visitor); + size_t processed = 0; + while (processed < file_contents.length() && + !caching_visitor.done_framing()) { + processed += framer.ProcessInput(file_contents.c_str() + processed, + file_contents.length() - processed); + } + + string response_headers_str; + response_headers.DumpToString(&response_headers_str); + if (!caching_visitor.done_framing()) { + LOG(DFATAL) << "Did not frame entire message from file: " << file.value() + << " (" << processed << " of " << file_contents.length() + << " bytes)."; + } + if (processed < file_contents.length()) { + // Didn't frame whole file. Assume remainder is body. + // This sometimes happens as a result of incompatibilities between + // BalsaFramer and wget's serialization of HTTP sans content-length. + caching_visitor.AppendToBody(file_contents.c_str() + processed, + file_contents.length() - processed); + processed += file_contents.length(); + } + + StringPiece base = file.AsUTF8Unsafe(); + if (response_headers.HasHeader("X-Original-Url")) { + base = response_headers.GetHeader("X-Original-Url"); + response_headers.RemoveAllOfHeader("X-Original-Url"); + // Remove the protocol so that the string is of the form host + path, + // which is parsed properly below. + if (StringPieceUtils::StartsWithIgnoreCase(base, "https://")) { + base.remove_prefix(8); + } else if (StringPieceUtils::StartsWithIgnoreCase(base, "http://")) { + base.remove_prefix(7); + } + } + int path_start = base.find_first_of('/'); + DCHECK_LT(0, path_start); + StringPiece host(base.substr(0, path_start)); + StringPiece path(base.substr(path_start)); + if (path[path.length() - 1] == ',') { + path.remove_suffix(1); + } + // Set up request headers. Assume method is GET and protocol is HTTP/1.1. + request_headers.SetRequestFirstlineFromStringPieces("GET", + path, + "HTTP/1.1"); + request_headers.ReplaceOrAppendHeader("host", host); + + VLOG(1) << "Inserting 'http://" << GetKey(request_headers) + << "' into QuicInMemoryCache."; + + AddResponse(request_headers, response_headers, caching_visitor.body()); + + file = file_list.Next(); + } +} + +QuicInMemoryCache::~QuicInMemoryCache() { + STLDeleteValues(&responses_); +} + +string QuicInMemoryCache::GetKey(const BalsaHeaders& request_headers) const { + StringPiece uri = request_headers.request_uri(); + if (uri.size() == 0) { + return ""; + } + StringPiece host; + if (uri[0] == '/') { + host = request_headers.GetHeader("host"); + } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "https://")) { + uri.remove_prefix(8); + } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "http://")) { + uri.remove_prefix(7); + } + return host.as_string() + uri.as_string(); +} + +} // namespace net diff --git a/chromium/net/quic/quic_in_memory_cache.h b/chromium/net/quic/quic_in_memory_cache.h new file mode 100644 index 00000000000..752620abf2d --- /dev/null +++ b/chromium/net/quic/quic_in_memory_cache.h @@ -0,0 +1,116 @@ +// 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_QUIC_IN_MEMORY_CACHE_H_ +#define NET_QUIC_QUIC_IN_MEMORY_CACHE_H_ + +#include <string> + +#include "base/containers/hash_tables.h" +#include "base/memory/singleton.h" +#include "base/strings/string_piece.h" +#include "net/base/net_export.h" +#include "net/tools/balsa/balsa_frame.h" +#include "net/tools/balsa/balsa_headers.h" +#include "net/tools/balsa/noop_balsa_visitor.h" + +template <typename T> struct DefaultSingletonTraits; + +namespace net { + +namespace test { +class QuicInMemoryCachePeer; +} // namespace + +class QuicServer; + +// In-memory cache for HTTP responses. +// Reads from disk cache generated by: +// `wget -p --save_headers <url>` +class QuicInMemoryCache { + public: + enum SpecialResponseType { + REGULAR_RESPONSE, // Send the headers and body like a server should. + CLOSE_CONNECTION, // Close the connection (sending the close packet). + IGNORE_REQUEST, // Do nothing, expect the client to time out. + }; + + // Container for response header/body pairs. + class Response { + public: + Response() : response_type_(REGULAR_RESPONSE) {} + ~Response() {} + + SpecialResponseType response_type() const { return response_type_; } + const BalsaHeaders& headers() const { return headers_; } + const base::StringPiece body() const { return base::StringPiece(body_); } + + private: + friend class QuicInMemoryCache; + + void set_headers(const BalsaHeaders& headers) { + headers_.CopyFrom(headers); + } + void set_body(base::StringPiece body) { + body.CopyToString(&body_); + } + + SpecialResponseType response_type_; + BalsaHeaders headers_; + std::string body_; + + DISALLOW_COPY_AND_ASSIGN(Response); + }; + + // Returns the singleton instance of the cache. + static QuicInMemoryCache* GetInstance(); + + // Retrieve a response from this cache for a given request. + // If no appropriate response exists, NULL is returned. + // Currently, responses are selected based on request URI only. + const Response* GetResponse(const BalsaHeaders& request_headers) const; + + // Adds a simple response to the cache. The response headers will + // only contain the "content-length" header with the lenght of |body|. + void AddSimpleResponse(base::StringPiece method, + base::StringPiece path, + base::StringPiece version, + base::StringPiece response_code, + base::StringPiece response_detail, + base::StringPiece body); + + // Add a response to the cache. + void AddResponse(const BalsaHeaders& request_headers, + const BalsaHeaders& response_headers, + base::StringPiece response_body); + + // Simulate a special behavior at a particular path. + void AddSpecialResponse(base::StringPiece method, + base::StringPiece path, + base::StringPiece version, + SpecialResponseType response_type); + + private: + typedef base::hash_map<std::string, Response*> ResponseMap; + friend struct DefaultSingletonTraits<QuicInMemoryCache>; + friend class test::QuicInMemoryCachePeer; + + QuicInMemoryCache(); + ~QuicInMemoryCache(); + + void ResetForTests(); + + void Initialize(); + + std::string GetKey(const BalsaHeaders& response_headers) const; + + // Cached responses. + ResponseMap responses_; + + DISALLOW_COPY_AND_ASSIGN(QuicInMemoryCache); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_IN_MEMORY_CACHE_H_ diff --git a/chromium/net/quic/quic_network_transaction_unittest.cc b/chromium/net/quic/quic_network_transaction_unittest.cc index 2e4610ce5e7..bd3e3cd94b2 100644 --- a/chromium/net/quic/quic_network_transaction_unittest.cc +++ b/chromium/net/quic/quic_network_transaction_unittest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <vector> + #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" @@ -17,7 +19,7 @@ #include "net/http/http_server_properties_impl.h" #include "net/http/http_stream.h" #include "net/http/http_stream_factory.h" -#include "net/http/http_transaction_unittest.h" +#include "net/http/http_transaction_test_util.h" #include "net/http/transport_security_state.h" #include "net/proxy/proxy_config_service_fixed.h" #include "net/proxy/proxy_resolver.h" @@ -30,6 +32,7 @@ #include "net/quic/test_tools/mock_clock.h" #include "net/quic/test_tools/mock_crypto_client_stream_factory.h" #include "net/quic/test_tools/mock_random.h" +#include "net/quic/test_tools/quic_test_packet_maker.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/socket/client_socket_factory.h" #include "net/socket/mock_client_socket_pool_manager.h" @@ -50,18 +53,62 @@ static const char kQuicAlternateProtocolHttpHeader[] = "Alternate-Protocol: 80:quic\r\n\r\n"; static const char kQuicAlternateProtocolHttpsHeader[] = "Alternate-Protocol: 443:quic\r\n\r\n"; + } // namespace namespace net { namespace test { -class QuicNetworkTransactionTest : public PlatformTest { +// Helper class to encapsulate MockReads and MockWrites for QUIC. +// Simplify ownership issues and the interaction with the MockSocketFactory. +class MockQuicData { + public: + ~MockQuicData() { + STLDeleteElements(&packets_); + } + + void AddRead(scoped_ptr<QuicEncryptedPacket> packet) { + reads_.push_back(MockRead(SYNCHRONOUS, packet->data(), packet->length(), + sequence_number_++)); + packets_.push_back(packet.release()); + } + + void AddRead(IoMode mode, int rv) { + reads_.push_back(MockRead(mode, rv)); + } + + void AddWrite(scoped_ptr<QuicEncryptedPacket> packet) { + writes_.push_back(MockWrite(SYNCHRONOUS, packet->data(), packet->length(), + sequence_number_++)); + packets_.push_back(packet.release()); + } + + void AddDelayedSocketDataToFactory(MockClientSocketFactory* factory, + size_t delay) { + MockRead* reads = reads_.empty() ? NULL : &reads_[0]; + MockWrite* writes = writes_.empty() ? NULL : &writes_[0]; + socket_data_.reset(new DelayedSocketData( + delay, reads, reads_.size(), writes, writes_.size())); + factory->AddSocketDataProvider(socket_data_.get()); + } + + private: + std::vector<QuicEncryptedPacket*> packets_; + std::vector<MockWrite> writes_; + std::vector<MockRead> reads_; + size_t sequence_number_; + scoped_ptr<SocketDataProvider> socket_data_; +}; + +class QuicNetworkTransactionTest + : public PlatformTest, + public ::testing::WithParamInterface<QuicVersion> { protected: QuicNetworkTransactionTest() - : clock_(new MockClock), + : maker_(GetParam(), 0), + clock_(new MockClock), ssl_config_service_(new SSLConfigServiceDefaults), proxy_service_(ProxyService::CreateDirect()), - compressor_(new QuicSpdyCompressor()), auth_handler_factory_( HttpAuthHandlerFactory::CreateDefault(&host_resolver_)), random_generator_(0), @@ -69,6 +116,7 @@ class QuicNetworkTransactionTest : public PlatformTest { request_.method = "GET"; request_.url = GURL("http://www.google.com/"); request_.load_flags = 0; + clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(20)); } virtual void SetUp() { @@ -83,146 +131,71 @@ class QuicNetworkTransactionTest : public PlatformTest { PlatformTest::TearDown(); NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); base::MessageLoop::current()->RunUntilIdle(); - HttpStreamFactory::set_use_alternate_protocols(false); - HttpStreamFactory::SetNextProtos(std::vector<NextProto>()); - } - - scoped_ptr<QuicEncryptedPacket> ConstructRstPacket( - QuicPacketSequenceNumber num, - QuicStreamId stream_id) { - QuicPacketHeader header; - header.public_header.guid = random_generator_.RandUint64(); - header.public_header.reset_flag = false; - header.public_header.version_flag = false; - header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; - header.packet_sequence_number = num; - header.entropy_flag = false; - header.fec_flag = false; - header.fec_group = 0; - - QuicRstStreamFrame rst(stream_id, QUIC_STREAM_NO_ERROR); - return scoped_ptr<QuicEncryptedPacket>( - ConstructPacket(header, QuicFrame(&rst))); } scoped_ptr<QuicEncryptedPacket> ConstructConnectionClosePacket( QuicPacketSequenceNumber num) { - QuicPacketHeader header; - header.public_header.guid = random_generator_.RandUint64(); - header.public_header.reset_flag = false; - header.public_header.version_flag = false; - header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; - header.packet_sequence_number = num; - header.entropy_flag = false; - header.fec_flag = false; - header.fec_group = 0; - - QuicConnectionCloseFrame close; - close.error_code = QUIC_CRYPTO_VERSION_NOT_SUPPORTED; - close.error_details = "Time to panic!"; - return scoped_ptr<QuicEncryptedPacket>( - ConstructPacket(header, QuicFrame(&close))); + return maker_.MakeConnectionClosePacket(num); } scoped_ptr<QuicEncryptedPacket> ConstructAckPacket( QuicPacketSequenceNumber largest_received, QuicPacketSequenceNumber least_unacked) { - QuicPacketHeader header; - header.public_header.guid = random_generator_.RandUint64(); - header.public_header.reset_flag = false; - header.public_header.version_flag = false; - header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; - header.packet_sequence_number = 2; - header.entropy_flag = false; - header.fec_flag = false; - header.fec_group = 0; - - QuicAckFrame ack(largest_received, QuicTime::Zero(), least_unacked); - - QuicCongestionFeedbackFrame feedback; - feedback.type = kTCP; - feedback.tcp.accumulated_number_of_lost_packets = 0; - feedback.tcp.receive_window = 256000; - - QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), false); - QuicFrames frames; - frames.push_back(QuicFrame(&ack)); - frames.push_back(QuicFrame(&feedback)); - scoped_ptr<QuicPacket> packet( - framer.BuildUnsizedDataPacket(header, frames).packet); - return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket( - ENCRYPTION_NONE, header.packet_sequence_number, *packet)); + return maker_.MakeAckPacket(2, largest_received, least_unacked, true); } - std::string GetRequestString(const std::string& method, - const std::string& scheme, - const std::string& path) { - SpdyHeaderBlock headers; - headers[":method"] = method; - headers[":host"] = "www.google.com"; - headers[":path"] = path; - headers[":scheme"] = scheme; - headers[":version"] = "HTTP/1.1"; - return SerializeHeaderBlock(headers); + SpdyHeaderBlock GetRequestHeaders(const std::string& method, + const std::string& scheme, + const std::string& path) { + return maker_.GetRequestHeaders(method, scheme, path); } - std::string GetResponseString(const std::string& status, - const std::string& body) { - SpdyHeaderBlock headers; - headers[":status"] = status; - headers[":version"] = "HTTP/1.1"; - headers["content-type"] = "text/plain"; - return compressor_->CompressHeaders(headers) + body; + SpdyHeaderBlock GetResponseHeaders(const std::string& status) { + return maker_.GetResponseHeaders(status); } - std::string SerializeHeaderBlock(const SpdyHeaderBlock& headers) { - QuicSpdyCompressor compressor; - return compressor.CompressHeadersWithPriority( - ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY), headers); - } - - // Returns a newly created packet to send kData on stream 1. - QuicEncryptedPacket* ConstructDataPacket( + scoped_ptr<QuicEncryptedPacket> ConstructDataPacket( QuicPacketSequenceNumber sequence_number, QuicStreamId stream_id, bool should_include_version, bool fin, QuicStreamOffset offset, base::StringPiece data) { - InitializeHeader(sequence_number, should_include_version); - QuicStreamFrame frame(stream_id, fin, offset, MakeIOVector(data)); - return ConstructPacket(header_, QuicFrame(&frame)).release(); + return maker_.MakeDataPacket( + sequence_number, stream_id, should_include_version, fin, offset, data); } - scoped_ptr<QuicEncryptedPacket> ConstructPacket( - const QuicPacketHeader& header, - const QuicFrame& frame) { - QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), false); - QuicFrames frames; - frames.push_back(frame); - scoped_ptr<QuicPacket> packet( - framer.BuildUnsizedDataPacket(header, frames).packet); - return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket( - ENCRYPTION_NONE, header.packet_sequence_number, *packet)); + scoped_ptr<QuicEncryptedPacket> ConstructRequestHeadersPacket( + QuicPacketSequenceNumber sequence_number, + QuicStreamId stream_id, + bool should_include_version, + bool fin, + const SpdyHeaderBlock& headers) { + return maker_.MakeRequestHeadersPacket( + sequence_number, stream_id, should_include_version, fin, headers); } - void InitializeHeader(QuicPacketSequenceNumber sequence_number, - bool should_include_version) { - header_.public_header.guid = random_generator_.RandUint64(); - header_.public_header.reset_flag = false; - header_.public_header.version_flag = should_include_version; - header_.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; - header_.packet_sequence_number = sequence_number; - header_.fec_group = 0; - header_.entropy_flag = false; - header_.fec_flag = false; + scoped_ptr<QuicEncryptedPacket> ConstructResponseHeadersPacket( + QuicPacketSequenceNumber sequence_number, + QuicStreamId stream_id, + bool should_include_version, + bool fin, + const SpdyHeaderBlock& headers) { + return maker_.MakeResponseHeadersPacket( + sequence_number, stream_id, should_include_version, fin, headers); } void CreateSession() { - CreateSessionWithFactory(&socket_factory_); + CreateSessionWithFactory(&socket_factory_, false); + } + + void CreateSessionWithNextProtos() { + CreateSessionWithFactory(&socket_factory_, true); } - void CreateSessionWithFactory(ClientSocketFactory* socket_factory) { + // If |use_next_protos| is true, enables SPDY and QUIC. + void CreateSessionWithFactory(ClientSocketFactory* socket_factory, + bool use_next_protos) { params_.enable_quic = true; params_.quic_clock = clock_; params_.quic_random = &random_generator_; @@ -235,6 +208,12 @@ class QuicNetworkTransactionTest : public PlatformTest { params_.ssl_config_service = ssl_config_service_.get(); params_.http_auth_handler_factory = auth_handler_factory_.get(); params_.http_server_properties = http_server_properties.GetWeakPtr(); + params_.quic_supported_versions = SupportedVersions(GetParam()); + + if (use_next_protos) { + params_.use_alternate_protocols = true; + params_.next_protos = NextProtosSpdy3(); + } session_ = new HttpNetworkSession(params_); session_->quic_stream_factory()->set_require_confirmation(false); @@ -308,13 +287,22 @@ class QuicNetworkTransactionTest : public PlatformTest { EXPECT_EQ(ALTERNATE_PROTOCOL_BROKEN, alternate.protocol); } + void ExpectQuicAlternateProtocolMapping() { + ASSERT_TRUE(session_->http_server_properties()->HasAlternateProtocol( + HostPortPair::FromURL(request_.url))); + const PortAlternateProtocolPair alternate = + session_->http_server_properties()->GetAlternateProtocol( + HostPortPair::FromURL(request_.url)); + EXPECT_EQ(QUIC, alternate.protocol); + } + void AddHangingNonAlternateProtocolSocketData() { MockConnect hanging_connect(SYNCHRONOUS, ERR_IO_PENDING); hanging_data_.set_connect_data(hanging_connect); socket_factory_.AddSocketDataProvider(&hanging_data_); } - QuicPacketHeader header_; + QuicTestPacketMaker maker_; scoped_refptr<HttpNetworkSession> session_; MockClientSocketFactory socket_factory_; MockCryptoClientStreamFactory crypto_client_stream_factory_; @@ -324,7 +312,6 @@ class QuicNetworkTransactionTest : public PlatformTest { TransportSecurityState transport_security_state_; scoped_refptr<SSLConfigServiceDefaults> ssl_config_service_; scoped_ptr<ProxyService> proxy_service_; - scoped_ptr<QuicSpdyCompressor> compressor_; scoped_ptr<HttpAuthHandlerFactory> auth_handler_factory_; MockRandom random_generator_; HttpServerPropertiesImpl http_server_properties; @@ -334,35 +321,26 @@ class QuicNetworkTransactionTest : public PlatformTest { StaticSocketDataProvider hanging_data_; }; -TEST_F(QuicNetworkTransactionTest, ForceQuic) { +INSTANTIATE_TEST_CASE_P(Version, QuicNetworkTransactionTest, + ::testing::ValuesIn(QuicSupportedVersions())); + +TEST_P(QuicNetworkTransactionTest, ForceQuic) { params_.origin_to_force_quic_on = HostPortPair::FromString("www.google.com:80"); - QuicStreamId stream_id = 3; - scoped_ptr<QuicEncryptedPacket> req( - ConstructDataPacket(1, stream_id, true, true, 0, - GetRequestString("GET", "http", "/"))); - scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0)); - - MockWrite quic_writes[] = { - MockWrite(SYNCHRONOUS, req->data(), req->length()), - MockWrite(SYNCHRONOUS, ack->data(), ack->length()), - }; - - scoped_ptr<QuicEncryptedPacket> resp( - ConstructDataPacket( - 1, stream_id, false, true, 0, GetResponseString("200 OK", "hello!"))); - MockRead quic_reads[] = { - MockRead(SYNCHRONOUS, resp->data(), resp->length()), - MockRead(ASYNC, OK), // EOF - }; - - DelayedSocketData quic_data( - 1, // wait for one write to finish before reading. - quic_reads, arraysize(quic_reads), - quic_writes, arraysize(quic_writes)); + MockQuicData mock_quic_data; + mock_quic_data.AddWrite( + ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true, + GetRequestHeaders("GET", "http", "/"))); + mock_quic_data.AddRead( + ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false, + GetResponseHeaders("200 OK"))); + mock_quic_data.AddRead( + ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!")); + mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); + mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF - socket_factory_.AddSocketDataProvider(&quic_data); + mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); // The non-alternate protocol job needs to hang in order to guarantee that // the alternate-protocol job will "win". @@ -405,19 +383,43 @@ TEST_F(QuicNetworkTransactionTest, ForceQuic) { int log_stream_id; ASSERT_TRUE(entries[pos].GetIntegerValue("stream_id", &log_stream_id)); - EXPECT_EQ(stream_id, static_cast<QuicStreamId>(log_stream_id)); + EXPECT_EQ(3, log_stream_id); } -TEST_F(QuicNetworkTransactionTest, ForceQuicWithErrorConnecting) { +TEST_P(QuicNetworkTransactionTest, QuicProxy) { + proxy_service_.reset( + ProxyService::CreateFixedFromPacResult("QUIC myproxy:70")); + + MockQuicData mock_quic_data; + mock_quic_data.AddWrite( + ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true, + GetRequestHeaders("GET", "http", "/"))); + mock_quic_data.AddRead( + ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false, + GetResponseHeaders("200 OK"))); + mock_quic_data.AddRead( + ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!")); + mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); + mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF + + mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); + + // There is no need to set up an alternate protocol job, because + // no attempt will be made to speak to the proxy over TCP. + + CreateSession(); + + SendRequestAndExpectQuicResponse("hello!"); +} + +TEST_P(QuicNetworkTransactionTest, ForceQuicWithErrorConnecting) { params_.origin_to_force_quic_on = HostPortPair::FromString("www.google.com:80"); - MockRead quic_reads[] = { - MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED), - }; - StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads), - NULL, 0); - socket_factory_.AddSocketDataProvider(&quic_data); + MockQuicData mock_quic_data; + mock_quic_data.AddRead(ASYNC, ERR_SOCKET_NOT_CONNECTED); + + mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 0); CreateSession(); @@ -429,7 +431,7 @@ TEST_F(QuicNetworkTransactionTest, ForceQuicWithErrorConnecting) { EXPECT_EQ(ERR_CONNECTION_CLOSED, callback.WaitForResult()); } -TEST_F(QuicNetworkTransactionTest, DoNotForceQuicForHttps) { +TEST_P(QuicNetworkTransactionTest, DoNotForceQuicForHttps) { // Attempt to "force" quic on 443, which will not be honored. params_.origin_to_force_quic_on = HostPortPair::FromString("www.google.com:443"); @@ -451,9 +453,7 @@ TEST_F(QuicNetworkTransactionTest, DoNotForceQuicForHttps) { SendRequestAndExpectHttpResponse("hello world"); } -TEST_F(QuicNetworkTransactionTest, UseAlternateProtocolForQuic) { - HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too. - +TEST_P(QuicNetworkTransactionTest, UseAlternateProtocolForQuic) { MockRead http_reads[] = { MockRead("HTTP/1.1 200 OK\r\n"), MockRead(kQuicAlternateProtocolHttpHeader), @@ -466,46 +466,34 @@ TEST_F(QuicNetworkTransactionTest, UseAlternateProtocolForQuic) { NULL, 0); socket_factory_.AddSocketDataProvider(&http_data); - scoped_ptr<QuicEncryptedPacket> req( - ConstructDataPacket(1, 3, true, true, 0, - GetRequestString("GET", "http", "/"))); - scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0)); - - MockWrite quic_writes[] = { - MockWrite(SYNCHRONOUS, req->data(), req->length()), - MockWrite(SYNCHRONOUS, ack->data(), ack->length()), - }; - - scoped_ptr<QuicEncryptedPacket> resp( - ConstructDataPacket( - 1, 3, false, true, 0, GetResponseString("200 OK", "hello!"))); - MockRead quic_reads[] = { - MockRead(SYNCHRONOUS, resp->data(), resp->length()), - MockRead(ASYNC, OK), // EOF - }; - - DelayedSocketData quic_data( - 1, // wait for one write to finish before reading. - quic_reads, arraysize(quic_reads), - quic_writes, arraysize(quic_writes)); + MockQuicData mock_quic_data; + mock_quic_data.AddWrite( + ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true, + GetRequestHeaders("GET", "http", "/"))); + mock_quic_data.AddRead( + ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false, + GetResponseHeaders("200 OK"))); + mock_quic_data.AddRead( + ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!")); + mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); + mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF - socket_factory_.AddSocketDataProvider(&quic_data); + mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); // The non-alternate protocol job needs to hang in order to guarantee that // the alternate-protocol job will "win". AddHangingNonAlternateProtocolSocketData(); - CreateSession(); + CreateSessionWithNextProtos(); SendRequestAndExpectHttpResponse("hello world"); SendRequestAndExpectQuicResponse("hello!"); } -TEST_F(QuicNetworkTransactionTest, UseAlternateProtocolForQuicForHttps) { +TEST_P(QuicNetworkTransactionTest, UseAlternateProtocolForQuicForHttps) { params_.origin_to_force_quic_on = HostPortPair::FromString("www.google.com:443"); params_.enable_quic_https = true; - HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too. MockRead http_reads[] = { MockRead("HTTP/1.1 200 OK\r\n"), @@ -519,43 +507,31 @@ TEST_F(QuicNetworkTransactionTest, UseAlternateProtocolForQuicForHttps) { NULL, 0); socket_factory_.AddSocketDataProvider(&http_data); - scoped_ptr<QuicEncryptedPacket> req( - ConstructDataPacket(1, 3, true, true, 0, - GetRequestString("GET", "https", "/"))); - scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0)); + MockQuicData mock_quic_data; + mock_quic_data.AddWrite( + ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true, + GetRequestHeaders("GET", "http", "/"))); + mock_quic_data.AddRead( + ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false, + GetResponseHeaders("200 OK"))); + mock_quic_data.AddRead( + ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!")); + mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); + mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF - MockWrite quic_writes[] = { - MockWrite(SYNCHRONOUS, req->data(), req->length()), - MockWrite(SYNCHRONOUS, ack->data(), ack->length()), - }; - - scoped_ptr<QuicEncryptedPacket> resp( - ConstructDataPacket( - 1, 3, false, true, 0, GetResponseString("200 OK", "hello!"))); - MockRead quic_reads[] = { - MockRead(SYNCHRONOUS, resp->data(), resp->length()), - MockRead(ASYNC, OK), // EOF - }; - - DelayedSocketData quic_data( - 1, // wait for one write to finish before reading. - quic_reads, arraysize(quic_reads), - quic_writes, arraysize(quic_writes)); - - socket_factory_.AddSocketDataProvider(&quic_data); + mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); // The non-alternate protocol job needs to hang in order to guarantee that // the alternate-protocol job will "win". AddHangingNonAlternateProtocolSocketData(); - CreateSession(); + CreateSessionWithNextProtos(); // TODO(rtenneti): Test QUIC over HTTPS, GetSSLInfo(). SendRequestAndExpectHttpResponse("hello world"); } -TEST_F(QuicNetworkTransactionTest, HungAlternateProtocol) { - HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too. +TEST_P(QuicNetworkTransactionTest, HungAlternateProtocol) { crypto_client_stream_factory_.set_handshake_mode( MockCryptoClientStream::COLD_START); @@ -594,7 +570,7 @@ TEST_F(QuicNetworkTransactionTest, HungAlternateProtocol) { http_writes, arraysize(http_writes)); socket_factory.AddSocketDataProvider(&http_data2); - CreateSessionWithFactory(&socket_factory); + CreateSessionWithFactory(&socket_factory, true); // Run the first request. http_data.StopAfter(arraysize(http_reads) + arraysize(http_writes)); @@ -613,70 +589,43 @@ TEST_F(QuicNetworkTransactionTest, HungAlternateProtocol) { ASSERT_TRUE(!quic_data.at_write_eof()); } -TEST_F(QuicNetworkTransactionTest, ZeroRTTWithHttpRace) { - HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too. - - scoped_ptr<QuicEncryptedPacket> req( - ConstructDataPacket(1, 3, true, true, 0, - GetRequestString("GET", "http", "/"))); - scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0)); - - MockWrite quic_writes[] = { - MockWrite(SYNCHRONOUS, req->data(), req->length()), - MockWrite(SYNCHRONOUS, ack->data(), ack->length()), - }; - - scoped_ptr<QuicEncryptedPacket> resp( - ConstructDataPacket( - 1, 3, false, true, 0, GetResponseString("200 OK", "hello!"))); - MockRead quic_reads[] = { - MockRead(SYNCHRONOUS, resp->data(), resp->length()), - MockRead(ASYNC, OK), // EOF - }; - - DelayedSocketData quic_data( - 1, // wait for one write to finish before reading. - quic_reads, arraysize(quic_reads), - quic_writes, arraysize(quic_writes)); - - socket_factory_.AddSocketDataProvider(&quic_data); +TEST_P(QuicNetworkTransactionTest, ZeroRTTWithHttpRace) { + MockQuicData mock_quic_data; + mock_quic_data.AddWrite( + ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true, + GetRequestHeaders("GET", "http", "/"))); + mock_quic_data.AddRead( + ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false, + GetResponseHeaders("200 OK"))); + mock_quic_data.AddRead( + ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!")); + mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); + mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF + + mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); // The non-alternate protocol job needs to hang in order to guarantee that // the alternate-protocol job will "win". AddHangingNonAlternateProtocolSocketData(); - CreateSession(); + CreateSessionWithNextProtos(); AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT); SendRequestAndExpectQuicResponse("hello!"); } -TEST_F(QuicNetworkTransactionTest, ZeroRTTWithNoHttpRace) { - HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too. - - scoped_ptr<QuicEncryptedPacket> req( - ConstructDataPacket(1, 3, true, true, 0, - GetRequestString("GET", "http", "/"))); - scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0)); - - MockWrite quic_writes[] = { - MockWrite(SYNCHRONOUS, req->data(), req->length()), - MockWrite(SYNCHRONOUS, ack->data(), ack->length()), - }; - - scoped_ptr<QuicEncryptedPacket> resp( - ConstructDataPacket( - 1, 3, false, true, 0, GetResponseString("200 OK", "hello!"))); - MockRead quic_reads[] = { - MockRead(SYNCHRONOUS, resp->data(), resp->length()), - MockRead(ASYNC, OK), // EOF - }; - - DelayedSocketData quic_data( - 1, // wait for one write to finish before reading. - quic_reads, arraysize(quic_reads), - quic_writes, arraysize(quic_writes)); - - socket_factory_.AddSocketDataProvider(&quic_data); +TEST_P(QuicNetworkTransactionTest, ZeroRTTWithNoHttpRace) { + MockQuicData mock_quic_data; + mock_quic_data.AddWrite( + ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true, + GetRequestHeaders("GET", "http", "/"))); + mock_quic_data.AddRead( + ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false, + GetResponseHeaders("200 OK"))); + mock_quic_data.AddRead( + ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!")); + mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); + mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF + mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); // In order for a new QUIC session to be established via alternate-protocol // without racing an HTTP connection, we need the host resolution to happen @@ -692,38 +641,65 @@ TEST_F(QuicNetworkTransactionTest, ZeroRTTWithNoHttpRace) { NULL, net_log_.bound()); - CreateSession(); + CreateSessionWithNextProtos(); AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT); SendRequestAndExpectQuicResponse("hello!"); } -TEST_F(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) { - HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too. - - scoped_ptr<QuicEncryptedPacket> req( - ConstructDataPacket(1, 3, true, true, 0, - GetRequestString("GET", "http", "/"))); - scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0)); +TEST_P(QuicNetworkTransactionTest, ZeroRTTWithProxy) { + proxy_service_.reset( + ProxyService::CreateFixedFromPacResult("PROXY myproxy:70")); - MockWrite quic_writes[] = { - MockWrite(SYNCHRONOUS, req->data(), req->length()), - MockWrite(SYNCHRONOUS, ack->data(), ack->length()), + // Since we are using a proxy, the QUIC job will not succeed. + MockWrite http_writes[] = { + MockWrite(SYNCHRONOUS, 0, "GET http://www.google.com/ HTTP/1.1\r\n"), + MockWrite(SYNCHRONOUS, 1, "Host: www.google.com\r\n"), + MockWrite(SYNCHRONOUS, 2, "Proxy-Connection: keep-alive\r\n\r\n") }; - scoped_ptr<QuicEncryptedPacket> resp( - ConstructDataPacket( - 1, 3, false, true, 0, GetResponseString("200 OK", "hello!"))); - MockRead quic_reads[] = { - MockRead(SYNCHRONOUS, resp->data(), resp->length()), - MockRead(ASYNC, OK), // EOF + MockRead http_reads[] = { + MockRead(SYNCHRONOUS, 3, "HTTP/1.1 200 OK\r\n"), + MockRead(SYNCHRONOUS, 4, kQuicAlternateProtocolHttpHeader), + MockRead(SYNCHRONOUS, 5, "hello world"), + MockRead(SYNCHRONOUS, OK, 6) }; - DelayedSocketData quic_data( - 1, // wait for one write to finish before reading. - quic_reads, arraysize(quic_reads), - quic_writes, arraysize(quic_writes)); + StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), + http_writes, arraysize(http_writes)); + socket_factory_.AddSocketDataProvider(&http_data); - socket_factory_.AddSocketDataProvider(&quic_data); + // In order for a new QUIC session to be established via alternate-protocol + // without racing an HTTP connection, we need the host resolution to happen + // synchronously. + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule("www.google.com", "192.168.0.1", ""); + HostResolver::RequestInfo info(HostPortPair("www.google.com", 80)); + AddressList address; + host_resolver_.Resolve(info, + DEFAULT_PRIORITY, + &address, + CompletionCallback(), + NULL, + net_log_.bound()); + + CreateSessionWithNextProtos(); + AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT); + SendRequestAndExpectHttpResponse("hello world"); +} + +TEST_P(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) { + MockQuicData mock_quic_data; + mock_quic_data.AddWrite( + ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true, + GetRequestHeaders("GET", "http", "/"))); + mock_quic_data.AddRead( + ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false, + GetResponseHeaders("200 OK"))); + mock_quic_data.AddRead( + ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!")); + mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); + mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF + mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1); // The non-alternate protocol job needs to hang in order to guarantee that // the alternate-protocol job will "win". @@ -741,7 +717,7 @@ TEST_F(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) { host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address, CompletionCallback(), NULL, net_log_.bound()); - CreateSession(); + CreateSessionWithNextProtos(); session_->quic_stream_factory()->set_require_confirmation(true); AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT); @@ -756,9 +732,7 @@ TEST_F(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) { EXPECT_EQ(OK, callback.WaitForResult()); } -TEST_F(QuicNetworkTransactionTest, BrokenAlternateProtocol) { - HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too. - +TEST_P(QuicNetworkTransactionTest, BrokenAlternateProtocol) { // Alternate-protocol job scoped_ptr<QuicEncryptedPacket> close(ConstructConnectionClosePacket(1)); MockRead quic_reads[] = { @@ -781,15 +755,13 @@ TEST_F(QuicNetworkTransactionTest, BrokenAlternateProtocol) { NULL, 0); socket_factory_.AddSocketDataProvider(&http_data); - CreateSession(); + CreateSessionWithNextProtos(); AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START); SendRequestAndExpectHttpResponse("hello from http"); ExpectBrokenAlternateProtocolMapping(); } -TEST_F(QuicNetworkTransactionTest, BrokenAlternateProtocolReadError) { - HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too. - +TEST_P(QuicNetworkTransactionTest, BrokenAlternateProtocolReadError) { // Alternate-protocol job MockRead quic_reads[] = { MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED), @@ -810,16 +782,45 @@ TEST_F(QuicNetworkTransactionTest, BrokenAlternateProtocolReadError) { NULL, 0); socket_factory_.AddSocketDataProvider(&http_data); - CreateSession(); + CreateSessionWithNextProtos(); AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START); SendRequestAndExpectHttpResponse("hello from http"); ExpectBrokenAlternateProtocolMapping(); } -TEST_F(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) { - HttpStreamFactory::EnableNpnSpdy3(); // Enables QUIC too. +TEST_P(QuicNetworkTransactionTest, NoBrokenAlternateProtocolIfTcpFails) { + // Alternate-protocol job will fail when the session attempts to read. + MockRead quic_reads[] = { + MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED), + }; + StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads), + NULL, 0); + socket_factory_.AddSocketDataProvider(&quic_data); + // Main job will also fail. + MockRead http_reads[] = { + MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED), + }; + + StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), + NULL, 0); + http_data.set_connect_data(MockConnect(ASYNC, ERR_SOCKET_NOT_CONNECTED)); + socket_factory_.AddSocketDataProvider(&http_data); + + CreateSessionWithNextProtos(); + + AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START); + scoped_ptr<HttpNetworkTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get())); + TestCompletionCallback callback; + int rv = trans->Start(&request_, callback.callback(), net_log_.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(ERR_SOCKET_NOT_CONNECTED, callback.WaitForResult()); + ExpectQuicAlternateProtocolMapping(); +} + +TEST_P(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) { // Alternate-protocol job MockRead quic_reads[] = { MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED), @@ -830,6 +831,11 @@ TEST_F(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) { AddHangingNonAlternateProtocolSocketData(); + // Second Alternate-protocol job which will race with the TCP job. + StaticSocketDataProvider quic_data2(quic_reads, arraysize(quic_reads), + NULL, 0); + socket_factory_.AddSocketDataProvider(&quic_data2); + // Final job that will proceed when the QUIC job fails. MockRead http_reads[] = { MockRead("HTTP/1.1 200 OK\r\n\r\n"), @@ -842,7 +848,7 @@ TEST_F(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) { NULL, 0); socket_factory_.AddSocketDataProvider(&http_data); - CreateSession(); + CreateSessionWithNextProtos(); AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT); @@ -854,5 +860,100 @@ TEST_F(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) { EXPECT_TRUE(quic_data.at_write_eof()); } +TEST_P(QuicNetworkTransactionTest, DISABLED_HangingZeroRttFallback) { + // Alternate-protocol job + MockRead quic_reads[] = { + MockRead(ASYNC, ERR_IO_PENDING), + }; + StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads), + NULL, 0); + socket_factory_.AddSocketDataProvider(&quic_data); + + // Main job that will proceed when the QUIC job fails. + MockRead http_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n\r\n"), + MockRead("hello from http"), + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), + MockRead(ASYNC, OK) + }; + + StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), + NULL, 0); + socket_factory_.AddSocketDataProvider(&http_data); + + CreateSessionWithNextProtos(); + + AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT); + + SendRequestAndExpectHttpResponse("hello from http"); +} + +TEST_P(QuicNetworkTransactionTest, BrokenAlternateProtocolOnConnectFailure) { + // Alternate-protocol job will fail before creating a QUIC session. + StaticSocketDataProvider quic_data(NULL, 0, NULL, 0); + quic_data.set_connect_data(MockConnect(SYNCHRONOUS, + ERR_INTERNET_DISCONNECTED)); + socket_factory_.AddSocketDataProvider(&quic_data); + + // Main job which will succeed even though the alternate job fails. + MockRead http_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n\r\n"), + MockRead("hello from http"), + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), + MockRead(ASYNC, OK) + }; + + StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), + NULL, 0); + socket_factory_.AddSocketDataProvider(&http_data); + + CreateSessionWithNextProtos(); + AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START); + SendRequestAndExpectHttpResponse("hello from http"); + + ExpectBrokenAlternateProtocolMapping(); +} + +TEST_P(QuicNetworkTransactionTest, ConnectionCloseDuringConnect) { + MockQuicData mock_quic_data; + mock_quic_data.AddRead(ConstructConnectionClosePacket(1)); + mock_quic_data.AddWrite( + ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true, + GetRequestHeaders("GET", "http", "/"))); + mock_quic_data.AddWrite(ConstructAckPacket(2, 1)); + mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 0); + + // When the QUIC connection fails, we will try the request again over HTTP. + MockRead http_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(kQuicAlternateProtocolHttpHeader), + MockRead("hello world"), + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), + MockRead(ASYNC, OK) + }; + + StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), + NULL, 0); + socket_factory_.AddSocketDataProvider(&http_data); + + // In order for a new QUIC session to be established via alternate-protocol + // without racing an HTTP connection, we need the host resolution to happen + // synchronously. + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule("www.google.com", "192.168.0.1", ""); + HostResolver::RequestInfo info(HostPortPair("www.google.com", 80)); + AddressList address; + host_resolver_.Resolve(info, + DEFAULT_PRIORITY, + &address, + CompletionCallback(), + NULL, + net_log_.bound()); + + CreateSessionWithNextProtos(); + AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT); + SendRequestAndExpectHttpResponse("hello world"); +} + } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_packet_creator.cc b/chromium/net/quic/quic_packet_creator.cc index cb37cc271de..4eb0ea99a83 100644 --- a/chromium/net/quic/quic_packet_creator.cc +++ b/chromium/net/quic/quic_packet_creator.cc @@ -4,6 +4,7 @@ #include "net/quic/quic_packet_creator.h" +#include "base/basictypes.h" #include "base/logging.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_ack_notifier.h" @@ -54,18 +55,22 @@ class QuicRandomBoolSource { DISALLOW_COPY_AND_ASSIGN(QuicRandomBoolSource); }; -QuicPacketCreator::QuicPacketCreator(QuicGuid guid, +QuicPacketCreator::QuicPacketCreator(QuicConnectionId connection_id, QuicFramer* framer, - QuicRandom* random_generator, - bool is_server) - : guid_(guid), + QuicRandom* random_generator) + : connection_id_(connection_id), + encryption_level_(ENCRYPTION_NONE), framer_(framer), random_bool_source_(new QuicRandomBoolSource(random_generator)), sequence_number_(0), + should_fec_protect_(false), fec_group_number_(0), - is_server_(is_server), - send_version_in_packet_(!is_server), - sequence_number_length_(options_.send_sequence_number_length), + send_version_in_packet_(!framer->is_server()), + max_packet_length_(kDefaultMaxPacketSize), + max_packets_per_fec_group_(0), + connection_id_length_(PACKET_8BYTE_CONNECTION_ID), + next_sequence_number_length_(PACKET_1BYTE_SEQUENCE_NUMBER), + sequence_number_length_(next_sequence_number_length_), packet_size_(0) { framer_->set_fec_builder(this); } @@ -77,23 +82,79 @@ void QuicPacketCreator::OnBuiltFecProtectedPayload( const QuicPacketHeader& header, StringPiece payload) { if (fec_group_.get()) { DCHECK_NE(0u, header.fec_group); - fec_group_->Update(header, payload); + fec_group_->Update(encryption_level_, header, payload); } } bool QuicPacketCreator::ShouldSendFec(bool force_close) const { + DCHECK(!HasPendingFrames()); return fec_group_.get() != NULL && fec_group_->NumReceivedPackets() > 0 && - (force_close || - fec_group_->NumReceivedPackets() >= options_.max_packets_per_fec_group); + (force_close || fec_group_->NumReceivedPackets() >= + max_packets_per_fec_group_); } -void QuicPacketCreator::MaybeStartFEC() { - if (options_.max_packets_per_fec_group > 0 && fec_group_.get() == NULL) { - DCHECK(queued_frames_.empty()); - // Set the fec group number to the sequence number of the next packet. - fec_group_number_ = sequence_number() + 1; - fec_group_.reset(new QuicFecGroup()); +bool QuicPacketCreator::IsFecGroupOpen() const { + return ShouldSendFec(true); +} + +void QuicPacketCreator::StartFecProtectingPackets() { + if (!IsFecEnabled()) { + LOG(DFATAL) << "Cannot start FEC protection when FEC is not enabled."; + return; + } + // TODO(jri): This currently requires that the generator flush out any + // pending frames when FEC protection is turned on. If current packet can be + // converted to an FEC protected packet, do it. This will require the + // generator to check if the resulting expansion still allows the incoming + // frame to be added to the packet. + if (HasPendingFrames()) { + LOG(DFATAL) << "Cannot start FEC protection with pending frames."; + return; + } + DCHECK(!should_fec_protect_); + should_fec_protect_ = true; +} + +void QuicPacketCreator::StopFecProtectingPackets() { + if (fec_group_.get() != NULL) { + LOG(DFATAL) << "Cannot stop FEC protection with open FEC group."; + return; } + DCHECK(should_fec_protect_); + should_fec_protect_ = false; + fec_group_number_ = 0; +} + +bool QuicPacketCreator::IsFecProtected() const { + return should_fec_protect_; +} + +bool QuicPacketCreator::IsFecEnabled() const { + return max_packets_per_fec_group_ > 0; +} + +InFecGroup QuicPacketCreator::MaybeUpdateLengthsAndStartFec() { + if (fec_group_.get() != NULL) { + // Don't update any lengths when an FEC group is open, to ensure same + // packet header size in all packets within a group. + return IN_FEC_GROUP; + } + if (!queued_frames_.empty()) { + // Don't change creator state if there are frames queued. + return fec_group_.get() == NULL ? NOT_IN_FEC_GROUP : IN_FEC_GROUP; + } + + // Update sequence number length only on packet and FEC group boundaries. + sequence_number_length_ = next_sequence_number_length_; + + if (!should_fec_protect_) { + return NOT_IN_FEC_GROUP; + } + // Start a new FEC group since protection is on. Set the fec group number to + // the sequence number of the next packet. + fec_group_number_ = sequence_number() + 1; + fec_group_.reset(new QuicFecGroup()); + return IN_FEC_GROUP; } // Stops serializing version of the protocol in packets sent after this call. @@ -110,38 +171,44 @@ void QuicPacketCreator::StopSendingVersion() { void QuicPacketCreator::UpdateSequenceNumberLength( QuicPacketSequenceNumber least_packet_awaited_by_peer, - QuicByteCount bytes_per_second) { + QuicByteCount congestion_window) { DCHECK_LE(least_packet_awaited_by_peer, sequence_number_ + 1); // Since the packet creator will not change sequence number length mid FEC // group, include the size of an FEC group to be safe. const QuicPacketSequenceNumber current_delta = - options_.max_packets_per_fec_group + sequence_number_ + 1 + max_packets_per_fec_group_ + sequence_number_ + 1 - least_packet_awaited_by_peer; - const uint64 congestion_window = - bytes_per_second / options_.max_packet_length; - const uint64 delta = max(current_delta, congestion_window); - - options_.send_sequence_number_length = + const uint64 congestion_window_packets = + congestion_window / max_packet_length_; + const uint64 delta = max(current_delta, congestion_window_packets); + next_sequence_number_length_ = QuicFramer::GetMinSequenceNumberLength(delta * 4); } bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id, QuicStreamOffset offset) const { + // TODO(jri): This is a simple safe decision for now, but make + // is_in_fec_group a parameter. Same as with all public methods in + // QuicPacketCreator. return BytesFree() > - QuicFramer::GetMinStreamFrameSize(framer_->version(), id, offset, true); + QuicFramer::GetMinStreamFrameSize(framer_->version(), id, offset, true, + should_fec_protect_ ? IN_FEC_GROUP : + NOT_IN_FEC_GROUP); } // static size_t QuicPacketCreator::StreamFramePacketOverhead( QuicVersion version, - QuicGuidLength guid_length, + QuicConnectionIdLength connection_id_length, bool include_version, QuicSequenceNumberLength sequence_number_length, + QuicStreamOffset offset, InFecGroup is_in_fec_group) { - return GetPacketHeaderSize(guid_length, include_version, + return GetPacketHeaderSize(connection_id_length, include_version, sequence_number_length, is_in_fec_group) + // Assumes this is a stream with a single lone packet. - QuicFramer::GetMinStreamFrameSize(version, 1u, 0u, true); + QuicFramer::GetMinStreamFrameSize(version, 1u, offset, true, + is_in_fec_group); } size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id, @@ -149,51 +216,31 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id, QuicStreamOffset offset, bool fin, QuicFrame* frame) { - DCHECK_GT(options_.max_packet_length, - StreamFramePacketOverhead( - framer_->version(), PACKET_8BYTE_GUID, kIncludeVersion, - PACKET_6BYTE_SEQUENCE_NUMBER, IN_FEC_GROUP)); - if (!HasRoomForStreamFrame(id, offset)) { - LOG(DFATAL) << "No room for Stream frame, BytesFree: " << BytesFree() - << " MinStreamFrameSize: " - << QuicFramer::GetMinStreamFrameSize( - framer_->version(), id, offset, true); - } + DCHECK_GT(max_packet_length_, StreamFramePacketOverhead( + framer_->version(), PACKET_8BYTE_CONNECTION_ID, kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, offset, IN_FEC_GROUP)); + + InFecGroup is_in_fec_group = MaybeUpdateLengthsAndStartFec(); + + LOG_IF(DFATAL, !HasRoomForStreamFrame(id, offset)) + << "No room for Stream frame, BytesFree: " << BytesFree() + << " MinStreamFrameSize: " + << QuicFramer::GetMinStreamFrameSize( + framer_->version(), id, offset, true, is_in_fec_group); if (data.Empty()) { - if (!fin) { - LOG(DFATAL) << "Creating a stream frame with no data or fin."; - } + LOG_IF(DFATAL, !fin) + << "Creating a stream frame with no data or fin."; // Create a new packet for the fin, if necessary. *frame = QuicFrame(new QuicStreamFrame(id, true, offset, data)); return 0; } - const size_t free_bytes = BytesFree(); - size_t bytes_consumed = 0; const size_t data_size = data.TotalBufferSize(); - - // When a STREAM frame is the last frame in a packet, it consumes two fewer - // bytes of framing overhead. - // Anytime more data is available than fits in with the extra two bytes, - // the frame will be the last, and up to two extra bytes are consumed. - // TODO(ianswett): If QUIC pads, the 1 byte PADDING frame does not fit when - // 1 byte is available, because then the STREAM frame isn't the last. - - // The minimum frame size(0 bytes of data) if it's not the last frame. size_t min_frame_size = QuicFramer::GetMinStreamFrameSize( - framer_->version(), id, offset, false); - // Check if it's the last frame in the packet. - if (data_size + min_frame_size > free_bytes) { - // The minimum frame size(0 bytes of data) if it is the last frame. - size_t min_last_frame_size = QuicFramer::GetMinStreamFrameSize( - framer_->version(), id, offset, true); - bytes_consumed = - min<size_t>(free_bytes - min_last_frame_size, data_size); - } else { - DCHECK_LT(data_size, BytesFree()); - bytes_consumed = data_size; - } + framer_->version(), id, offset, /*last_frame_in_packet=*/ true, + is_in_fec_group); + size_t bytes_consumed = min<size_t>(BytesFree() - min_frame_size, data_size); bool set_fin = fin && bytes_consumed == data_size; // Last frame. IOVector frame_data; @@ -224,25 +271,22 @@ size_t QuicPacketCreator::CreateStreamFrameWithNotifier( SerializedPacket QuicPacketCreator::ReserializeAllFrames( const QuicFrames& frames, QuicSequenceNumberLength original_length) { - const QuicSequenceNumberLength start_length = sequence_number_length_; - const QuicSequenceNumberLength start_options_length = - options_.send_sequence_number_length; - const QuicFecGroupNumber start_fec_group = fec_group_number_; - const size_t start_max_packets_per_fec_group = - options_.max_packets_per_fec_group; - - // Temporarily set the sequence number length and disable FEC. + DCHECK(fec_group_.get() == NULL); + const QuicSequenceNumberLength saved_length = sequence_number_length_; + const QuicSequenceNumberLength saved_next_length = + next_sequence_number_length_; + const bool saved_should_fec_protect = should_fec_protect_; + + // Temporarily set the sequence number length and stop FEC protection. sequence_number_length_ = original_length; - options_.send_sequence_number_length = original_length; - fec_group_number_ = 0; - options_.max_packets_per_fec_group = 0; + next_sequence_number_length_ = original_length; + should_fec_protect_ = false; - // Serialize the packet and restore the fec and sequence number length state. + // Serialize the packet and restore the FEC and sequence number length state. SerializedPacket serialized_packet = SerializeAllFrames(frames); - sequence_number_length_ = start_length; - options_.send_sequence_number_length = start_options_length; - fec_group_number_ = start_fec_group; - options_.max_packets_per_fec_group = start_max_packets_per_fec_group; + sequence_number_length_ = saved_length; + next_sequence_number_length_ = saved_next_length; + should_fec_protect_ = saved_should_fec_protect; return serialized_packet; } @@ -253,9 +297,8 @@ SerializedPacket QuicPacketCreator::SerializeAllFrames( // frames from SendStreamData()[send_stream_should_flush_ == false && // data.empty() == true] and retransmit due to RTO. DCHECK_EQ(0u, queued_frames_.size()); - if (frames.empty()) { - LOG(DFATAL) << "Attempt to serialize empty packet"; - } + LOG_IF(DFATAL, frames.empty()) + << "Attempt to serialize empty packet"; for (size_t i = 0; i < frames.size(); ++i) { bool success = AddFrame(frames[i], false); DCHECK(success); @@ -265,43 +308,46 @@ SerializedPacket QuicPacketCreator::SerializeAllFrames( return packet; } -bool QuicPacketCreator::HasPendingFrames() { +bool QuicPacketCreator::HasPendingFrames() const { return !queued_frames_.empty(); } -size_t QuicPacketCreator::BytesFree() const { - const size_t max_plaintext_size = - framer_->GetMaxPlaintextSize(options_.max_packet_length); - DCHECK_GE(max_plaintext_size, PacketSize()); +bool QuicPacketCreator::HasPendingRetransmittableFrames() const { + return queued_retransmittable_frames_.get() != NULL && + !queued_retransmittable_frames_->frames().empty(); +} - // If the last frame in the packet is a stream frame, then it can be - // two bytes smaller than if it were not the last. So this means that - // there are two fewer bytes available to the next frame in this case. +size_t QuicPacketCreator::ExpansionOnNewFrame() const { + // If packet is FEC protected, there's no expansion. + if (should_fec_protect_) { + return 0; + } + // If the last frame in the packet is a stream frame, then it will expand to + // include the stream_length field when a new frame is added. bool has_trailing_stream_frame = !queued_frames_.empty() && queued_frames_.back().type == STREAM_FRAME; - size_t expanded_packet_size = PacketSize() + - (has_trailing_stream_frame ? kQuicStreamPayloadLengthSize : 0); + return has_trailing_stream_frame ? kQuicStreamPayloadLengthSize : 0; +} - if (expanded_packet_size >= max_plaintext_size) { - return 0; - } - return max_plaintext_size - expanded_packet_size; +size_t QuicPacketCreator::BytesFree() const { + const size_t max_plaintext_size = + framer_->GetMaxPlaintextSize(max_packet_length_); + DCHECK_GE(max_plaintext_size, PacketSize()); + return max_plaintext_size - min(max_plaintext_size, PacketSize() + + ExpansionOnNewFrame()); } size_t QuicPacketCreator::PacketSize() const { - if (queued_frames_.empty()) { - // Only adjust the sequence number length when the FEC group is not open, - // to ensure no packets in a group are too large. - if (fec_group_.get() == NULL || - fec_group_->NumReceivedPackets() == 0) { - sequence_number_length_ = options_.send_sequence_number_length; - } - packet_size_ = GetPacketHeaderSize(options_.send_guid_length, - send_version_in_packet_, - sequence_number_length_, - options_.max_packets_per_fec_group == 0 ? - NOT_IN_FEC_GROUP : IN_FEC_GROUP); + if (!queued_frames_.empty()) { + return packet_size_; } + if (fec_group_.get() == NULL) { + // Update sequence number length on packet and FEC boundary. + sequence_number_length_ = next_sequence_number_length_; + } + packet_size_ = GetPacketHeaderSize( + connection_id_length_, send_version_in_packet_, sequence_number_length_, + should_fec_protect_ ? IN_FEC_GROUP : NOT_IN_FEC_GROUP); return packet_size_; } @@ -310,36 +356,32 @@ bool QuicPacketCreator::AddSavedFrame(const QuicFrame& frame) { } SerializedPacket QuicPacketCreator::SerializePacket() { - if (queued_frames_.empty()) { - LOG(DFATAL) << "Attempt to serialize empty packet"; - } + LOG_IF(DFATAL, queued_frames_.empty()) + << "Attempt to serialize empty packet"; + DCHECK_GE(sequence_number_ + 1, fec_group_number_); QuicPacketHeader header; - FillPacketHeader(fec_group_number_, false, false, &header); + FillPacketHeader(should_fec_protect_ ? fec_group_number_ : 0, false, &header); MaybeAddPadding(); size_t max_plaintext_size = - framer_->GetMaxPlaintextSize(options_.max_packet_length); + framer_->GetMaxPlaintextSize(max_packet_length_); DCHECK_GE(max_plaintext_size, packet_size_); - // ACK and CONNECTION_CLOSE Frames will be truncated only if they're - // the first frame in the packet. If truncation is to occur, then - // GetSerializedFrameLength will have returned all bytes free. - bool possibly_truncated = - packet_size_ != max_plaintext_size || - queued_frames_.size() != 1 || - (queued_frames_.back().type == ACK_FRAME || - queued_frames_.back().type == CONNECTION_CLOSE_FRAME); + // ACK Frames will be truncated only if they're the only frame in the packet, + // and if packet_size_ was set to max_plaintext_size. If truncation occurred, + // then GetSerializedFrameLength will have returned all bytes free. + bool possibly_truncated = packet_size_ == max_plaintext_size && + queued_frames_.size() == 1 && + queued_frames_.back().type == ACK_FRAME; SerializedPacket serialized = framer_->BuildDataPacket(header, queued_frames_, packet_size_); - if (!serialized.packet) { - LOG(DFATAL) << "Failed to serialize " << queued_frames_.size() - << " frames."; - } + LOG_IF(DFATAL, !serialized.packet) + << "Failed to serialize " << queued_frames_.size() << " frames."; // Because of possible truncation, we can't be confident that our // packet size calculation worked correctly. - if (!possibly_truncated) + if (!possibly_truncated) { DCHECK_EQ(packet_size_, serialized.packet->length()); - + } packet_size_ = 0; queued_frames_.clear(); serialized.retransmittable_frames = queued_retransmittable_frames_.release(); @@ -347,23 +389,24 @@ SerializedPacket QuicPacketCreator::SerializePacket() { } SerializedPacket QuicPacketCreator::SerializeFec() { - DCHECK_LT(0u, fec_group_->NumReceivedPackets()); + if (fec_group_.get() == NULL || fec_group_->NumReceivedPackets() <= 0) { + LOG(DFATAL) << "SerializeFEC called but no group or zero packets in group."; + // TODO(jri): Make this a public method of framer? + SerializedPacket kNoPacket(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL); + return kNoPacket; + } DCHECK_EQ(0u, queued_frames_.size()); QuicPacketHeader header; - FillPacketHeader(fec_group_number_, true, - fec_group_->entropy_parity(), &header); + FillPacketHeader(fec_group_number_, true, &header); QuicFecData fec_data; fec_data.fec_group = fec_group_->min_protected_packet(); fec_data.redundancy = fec_group_->payload_parity(); SerializedPacket serialized = framer_->BuildFecPacket(header, fec_data); fec_group_.reset(NULL); - fec_group_number_ = 0; packet_size_ = 0; - if (!serialized.packet) { - LOG(DFATAL) << "Failed to serialize fec packet for group:" - << fec_data.fec_group; - } - DCHECK_GE(options_.max_packet_length, serialized.packet->length()); + LOG_IF(DFATAL, !serialized.packet) + << "Failed to serialize fec packet for group:" << fec_data.fec_group; + DCHECK_GE(max_packet_length_, serialized.packet->length()); return serialized; } @@ -376,64 +419,59 @@ SerializedPacket QuicPacketCreator::SerializeConnectionClose( QuicEncryptedPacket* QuicPacketCreator::SerializeVersionNegotiationPacket( const QuicVersionVector& supported_versions) { - DCHECK(is_server_); + DCHECK(framer_->is_server()); QuicPacketPublicHeader header; - header.guid = guid_; + header.connection_id = connection_id_; header.reset_flag = false; header.version_flag = true; header.versions = supported_versions; QuicEncryptedPacket* encrypted = framer_->BuildVersionNegotiationPacket(header, supported_versions); DCHECK(encrypted); - DCHECK_GE(options_.max_packet_length, encrypted->length()); + DCHECK_GE(max_packet_length_, encrypted->length()); return encrypted; } void QuicPacketCreator::FillPacketHeader(QuicFecGroupNumber fec_group, bool fec_flag, - bool fec_entropy_flag, QuicPacketHeader* header) { - header->public_header.guid = guid_; + header->public_header.connection_id = connection_id_; header->public_header.reset_flag = false; header->public_header.version_flag = send_version_in_packet_; header->fec_flag = fec_flag; header->packet_sequence_number = ++sequence_number_; header->public_header.sequence_number_length = sequence_number_length_; - - bool entropy_flag; - if (fec_flag) { - // FEC packets don't have an entropy of their own. Entropy flag for FEC - // packets is the XOR of entropy of previous packets. - entropy_flag = fec_entropy_flag; - } else { - entropy_flag = random_bool_source_->RandBool(); - } - header->entropy_flag = entropy_flag; + header->entropy_flag = random_bool_source_->RandBool(); header->is_in_fec_group = fec_group == 0 ? NOT_IN_FEC_GROUP : IN_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; + switch (frame.type) { + case ACK_FRAME: + case CONGESTION_FEEDBACK_FRAME: + case PADDING_FRAME: + case STOP_WAITING_FRAME: + return false; + default: + return true; + } } bool QuicPacketCreator::AddFrame(const QuicFrame& frame, bool save_retransmittable_frames) { + DVLOG(1) << "Adding frame: " << frame; + InFecGroup is_in_fec_group = MaybeUpdateLengthsAndStartFec(); + size_t frame_len = framer_->GetSerializedFrameLength( - frame, BytesFree(), queued_frames_.empty(), true, - options()->send_sequence_number_length); + frame, BytesFree(), queued_frames_.empty(), true, is_in_fec_group, + sequence_number_length_); if (frame_len == 0) { return false; } DCHECK_LT(0u, packet_size_); - MaybeStartFEC(); - packet_size_ += frame_len; - // If the last frame in the packet was a stream frame, then once we add the - // new frame it's serialization will be two bytes larger. - if (!queued_frames_.empty() && queued_frames_.back().type == STREAM_FRAME) { - packet_size_ += kQuicStreamPayloadLengthSize; - } + packet_size_ += ExpansionOnNewFrame() + frame_len; + if (save_retransmittable_frames && ShouldRetransmit(frame)) { if (queued_retransmittable_frames_.get() == NULL) { queued_retransmittable_frames_.reset(new RetransmittableFrames()); diff --git a/chromium/net/quic/quic_packet_creator.h b/chromium/net/quic/quic_packet_creator.h index b6756af4059..10b8e8883b8 100644 --- a/chromium/net/quic/quic_packet_creator.h +++ b/chromium/net/quic/quic_packet_creator.h @@ -29,27 +29,10 @@ class QuicRandomBoolSource; class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { public: - // Options for controlling how packets are created. - struct Options { - Options() - : max_packet_length(kDefaultMaxPacketSize), - max_packets_per_fec_group(0), - send_guid_length(PACKET_8BYTE_GUID), - send_sequence_number_length(PACKET_1BYTE_SEQUENCE_NUMBER) {} - - size_t max_packet_length; - // 0 indicates fec is disabled. - size_t max_packets_per_fec_group; - // Length of guid to send over the wire. - QuicGuidLength send_guid_length; - QuicSequenceNumberLength send_sequence_number_length; - }; - // QuicRandom* required for packet entropy. - QuicPacketCreator(QuicGuid guid, + QuicPacketCreator(QuicConnectionId connection_id, QuicFramer* framer, - QuicRandom* random_generator, - bool is_server); + QuicRandom* random_generator); virtual ~QuicPacketCreator(); @@ -57,10 +40,24 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { virtual void OnBuiltFecProtectedPayload(const QuicPacketHeader& header, base::StringPiece payload) OVERRIDE; + // Turn on FEC protection for subsequently created packets. FEC should be + // enabled first (max_packets_per_fec_group should be non-zero) for FEC + // protection to start. + void StartFecProtectingPackets(); + + // Turn off FEC protection for subsequently created packets. If the creator + // has any open FEC group, call will fail. It is the caller's responsibility + // to flush out FEC packets in generation, and to verify with ShouldSendFec() + // that there is no open FEC group. + void StopFecProtectingPackets(); + // Checks if it's time to send an FEC packet. |force_close| forces this to - // return true if an fec group is open. + // return true if an FEC group is open. bool ShouldSendFec(bool force_close) const; + // Returns true if an FEC packet is under construction. + bool IsFecGroupOpen() const; + // Makes the framer not serialize the protocol version in sent packets. void StopSendingVersion(); @@ -68,14 +65,15 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { // can be safely changed. void UpdateSequenceNumberLength( QuicPacketSequenceNumber least_packet_awaited_by_peer, - QuicByteCount bytes_per_second); + QuicByteCount congestion_window); // The overhead the framing will add for a packet with one frame. static size_t StreamFramePacketOverhead( QuicVersion version, - QuicGuidLength guid_length, + QuicConnectionIdLength connection_id_length, bool include_version, QuicSequenceNumberLength sequence_number_length, + QuicStreamOffset offset, InFecGroup is_in_fec_group); bool HasRoomForStreamFrame(QuicStreamId id, QuicStreamOffset offset) const; @@ -109,11 +107,27 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { // Re-serializes frames with the original packet's sequence number length. // Used for retransmitting packets to ensure they aren't too long. + // Caller must ensure that any open FEC group is closed before calling this + // method. SerializedPacket ReserializeAllFrames( - const QuicFrames& frames, QuicSequenceNumberLength original_length); + const QuicFrames& frames, + QuicSequenceNumberLength original_length); // Returns true if there are frames pending to be serialized. - bool HasPendingFrames(); + bool HasPendingFrames() const; + + // Returns true if there are retransmittable frames pending to be serialized. + bool HasPendingRetransmittableFrames() const; + + // Returns whether FEC protection is currently enabled. Note: Enabled does not + // mean that an FEC group is currently active; i.e., IsFecProtected() may + // still return false. + bool IsFecEnabled() const; + + // Returns true if subsequent packets will be FEC protected. Note: True does + // not mean that an FEC packet is currently under construction; i.e., + // fec_group_.get() may still be NULL, until MaybeStartFec() is called. + bool IsFecProtected() const; // Returns the number of bytes which are available to be used by additional // frames in the packet. Since stream frames are slightly smaller when they @@ -121,6 +135,13 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { // value than max_packet_size - PacketSize(), in this case. size_t BytesFree() const; + // Returns the number of bytes that the packet will expand by if a new frame + // is added to the packet. If the last frame was a stream frame, it will + // expand slightly when a new frame is added, and this method returns the + // amount of expected expansion. If the packet is in an FEC group, no + // expansion happens and this method always returns zero. + size_t ExpansionOnNewFrame() const; + // Returns the number of bytes in the current packet, including the header, // if serialized with the current frames. Adding a frame to the packet // may change the serialized length of existing frames, as per the comment @@ -161,6 +182,11 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { QuicEncryptedPacket* SerializeVersionNegotiationPacket( const QuicVersionVector& supported_versions); + // Sets the encryption level that will be applied to new packets. + void set_encryption_level(EncryptionLevel level) { + encryption_level_ = level; + } + // Sequence number of the last created packet, or 0 if no packets have been // created. QuicPacketSequenceNumber sequence_number() const { @@ -171,8 +197,39 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { sequence_number_ = s; } - Options* options() { - return &options_; + QuicConnectionIdLength connection_id_length() const { + return connection_id_length_; + } + + QuicSequenceNumberLength next_sequence_number_length() const { + return next_sequence_number_length_; + } + + void set_next_sequence_number_length(QuicSequenceNumberLength length) { + next_sequence_number_length_ = length; + } + + size_t max_packet_length() const { + return max_packet_length_; + } + + void set_max_packet_length(size_t length) { + // |max_packet_length_| should not be changed mid-packet or mid-FEC group. + DCHECK(fec_group_.get() == NULL && queued_frames_.empty()); + max_packet_length_ = length; + } + + // Returns current max number of packets covered by an FEC group. + size_t max_packets_per_fec_group() const { + return max_packets_per_fec_group_; + } + + // Sets creator's max number of packets covered by an FEC group. + void set_max_packets_per_fec_group( + size_t max_packets_per_fec_group) { + // To turn off FEC protection, use StopFecProtectingPackets(). + DCHECK_NE(0u, max_packets_per_fec_group); + max_packets_per_fec_group_ = max_packets_per_fec_group; } private: @@ -180,13 +237,16 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { static bool ShouldRetransmit(const QuicFrame& frame); - // 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(); + // Updates sequence number and max packet lengths on a packet or FEC group + // boundary. + void MaybeUpdateLengths(); + + // Updates lengths and also starts an FEC group if FEC protection is on and + // there is not already an FEC group open. + InFecGroup MaybeUpdateLengthsAndStartFec(); void FillPacketHeader(QuicFecGroupNumber fec_group, bool fec_flag, - bool fec_entropy_flag, QuicPacketHeader* header); // Allows a frame to be added without creating retransmittable frames. @@ -198,21 +258,32 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { // padding frame. void MaybeAddPadding(); - Options options_; - QuicGuid guid_; + QuicConnectionId connection_id_; + EncryptionLevel encryption_level_; QuicFramer* framer_; scoped_ptr<QuicRandomBoolSource> random_bool_source_; QuicPacketSequenceNumber sequence_number_; + // If true, any created packets will be FEC protected. + bool should_fec_protect_; QuicFecGroupNumber fec_group_number_; scoped_ptr<QuicFecGroup> fec_group_; - // bool to keep track if this packet creator is being used the server. - bool is_server_; // Controls whether protocol version should be included while serializing the // packet. bool send_version_in_packet_; - // The sequence number length for the current packet and the current FEC group - // if FEC is enabled. - // Mutable so PacketSize() can adjust it when the packet is empty. + // Maximum length including headers and encryption (UDP payload length.) + size_t max_packet_length_; + // 0 indicates FEC is disabled. + size_t max_packets_per_fec_group_; + // Length of connection_id to send over the wire. + QuicConnectionIdLength connection_id_length_; + // Staging variable to hold next packet sequence number length. When sequence + // number length is to be changed, this variable holds the new length until + // a packet or FEC group boundary, when the creator's sequence_number_length_ + // can be changed to this new value. + QuicSequenceNumberLength next_sequence_number_length_; + // Sequence number length for the current packet and for the current FEC group + // when FEC is enabled. Mutable so PacketSize() can adjust it when the packet + // is empty. mutable QuicSequenceNumberLength sequence_number_length_; // packet_size_ is mutable because it's just a cache of the current size. // packet_size should never be read directly, use PacketSize() instead. diff --git a/chromium/net/quic/quic_packet_creator_test.cc b/chromium/net/quic/quic_packet_creator_test.cc index 33f2e5c3010..af9b53afecf 100644 --- a/chromium/net/quic/quic_packet_creator_test.cc +++ b/chromium/net/quic/quic_packet_creator_test.cc @@ -10,11 +10,14 @@ #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/quic_utils.h" #include "net/quic/test_tools/mock_random.h" +#include "net/quic/test_tools/quic_framer_peer.h" #include "net/quic/test_tools/quic_packet_creator_peer.h" #include "net/quic/test_tools/quic_test_utils.h" +#include "net/test/gtest_util.h" #include "testing/gmock/include/gmock/gmock.h" using base::StringPiece; +using std::ostream; using std::string; using std::vector; using testing::DoAll; @@ -27,19 +30,52 @@ namespace net { namespace test { namespace { -class QuicPacketCreatorTest : public ::testing::TestWithParam<bool> { +// Run tests with combinations of {QuicVersion, ToggleVersionSerialization}. +struct TestParams { + TestParams(QuicVersion version, + bool version_serialization) + : version(version), + version_serialization(version_serialization) { + } + + friend ostream& operator<<(ostream& os, const TestParams& p) { + os << "{ client_version: " << QuicVersionToString(p.version) + << " include version: " << p.version_serialization << " }"; + return os; + } + + QuicVersion version; + bool version_serialization; +}; + +// Constructs various test permutations. +vector<TestParams> GetTestParams() { + vector<TestParams> params; + QuicVersionVector all_supported_versions = QuicSupportedVersions(); + for (size_t i = 0; i < all_supported_versions.size(); ++i) { + params.push_back(TestParams(all_supported_versions[i], true)); + params.push_back(TestParams(all_supported_versions[i], false)); + } + return params; +} + +class QuicPacketCreatorTest : public ::testing::TestWithParam<TestParams> { protected: QuicPacketCreatorTest() - : server_framer_(QuicSupportedVersions(), QuicTime::Zero(), true), - client_framer_(QuicSupportedVersions(), QuicTime::Zero(), false), + : server_framer_(SupportedVersions(GetParam().version), QuicTime::Zero(), + true), + client_framer_(SupportedVersions(GetParam().version), QuicTime::Zero(), + false), sequence_number_(0), - guid_(2), + connection_id_(2), data_("foo"), - creator_(guid_, &client_framer_, &mock_random_, false) { + creator_(connection_id_, &client_framer_, &mock_random_) { client_framer_.set_visitor(&framer_visitor_); + client_framer_.set_received_entropy_calculator(&entropy_calculator_); server_framer_.set_visitor(&framer_visitor_); } - ~QuicPacketCreatorTest() { + + virtual ~QuicPacketCreatorTest() OVERRIDE { } void ProcessPacket(QuicPacket* packet) { @@ -64,30 +100,36 @@ class QuicPacketCreatorTest : public ::testing::TestWithParam<bool> { } // Returns the number of bytes consumed by the header of packet, including - // the version, that is not in an FEC group. - size_t GetPacketHeaderOverhead() { - return GetPacketHeaderSize(creator_.options()->send_guid_length, + // the version. + size_t GetPacketHeaderOverhead(InFecGroup is_in_fec_group) { + return GetPacketHeaderSize(creator_.connection_id_length(), kIncludeVersion, - creator_.options()->send_sequence_number_length, - NOT_IN_FEC_GROUP); + creator_.next_sequence_number_length(), + is_in_fec_group); } // Returns the number of bytes of overhead that will be added to a packet // of maximum length. size_t GetEncryptionOverhead() { - return creator_.options()->max_packet_length - - client_framer_.GetMaxPlaintextSize( - creator_.options()->max_packet_length); + return creator_.max_packet_length() - client_framer_.GetMaxPlaintextSize( + creator_.max_packet_length()); } // Returns the number of bytes consumed by the non-data fields of a stream // frame, assuming it is the last frame in the packet - size_t GetStreamFrameOverhead() { - return QuicFramer::GetMinStreamFrameSize( - client_framer_.version(), kStreamId, kOffset, true); + size_t GetStreamFrameOverhead(InFecGroup is_in_fec_group) { + return QuicFramer::GetMinStreamFrameSize(client_framer_.version(), + kClientDataStreamId1, kOffset, + true, is_in_fec_group); + } + + // Enables and turns on FEC protection. Returns true if FEC protection is on. + bool SwitchFecProtectionOn(size_t max_packets_per_fec_group) { + creator_.set_max_packets_per_fec_group(max_packets_per_fec_group); + creator_.StartFecProtectingPackets(); + return creator_.IsFecProtected(); } - static const QuicStreamId kStreamId = 1u; static const QuicStreamOffset kOffset = 1u; QuicFrames frames_; @@ -95,14 +137,21 @@ class QuicPacketCreatorTest : public ::testing::TestWithParam<bool> { QuicFramer client_framer_; testing::StrictMock<MockFramerVisitor> framer_visitor_; QuicPacketSequenceNumber sequence_number_; - QuicGuid guid_; + QuicConnectionId connection_id_; string data_; MockRandom mock_random_; QuicPacketCreator creator_; + MockEntropyCalculator entropy_calculator_; }; -TEST_F(QuicPacketCreatorTest, SerializeFrames) { - frames_.push_back(QuicFrame(new QuicAckFrame(0u, QuicTime::Zero(), 0u))); +// Run all packet creator tests with all supported versions of QUIC, and with +// and without version in the packet header. +INSTANTIATE_TEST_CASE_P(QuicPacketCreatorTests, + QuicPacketCreatorTest, + ::testing::ValuesIn(GetTestParams())); + +TEST_P(QuicPacketCreatorTest, SerializeFrames) { + frames_.push_back(QuicFrame(new QuicAckFrame(MakeAckFrame(0u, 0u)))); frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector()))); frames_.push_back(QuicFrame(new QuicStreamFrame(0u, true, 0u, IOVector()))); SerializedPacket serialized = creator_.SerializeAllFrames(frames_); @@ -113,7 +162,9 @@ TEST_F(QuicPacketCreatorTest, SerializeFrames) { { InSequence s; EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); EXPECT_CALL(framer_visitor_, OnAckFrame(_)); EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); @@ -124,9 +175,12 @@ TEST_F(QuicPacketCreatorTest, SerializeFrames) { delete serialized.packet; } -TEST_F(QuicPacketCreatorTest, SerializeWithFEC) { - creator_.options()->max_packets_per_fec_group = 6; - ASSERT_FALSE(creator_.ShouldSendFec(false)); +TEST_P(QuicPacketCreatorTest, SerializeWithFEC) { + // Enable FEC protection, and send FEC packet every 6 packets. + EXPECT_TRUE(SwitchFecProtectionOn(6)); + // Should return false since we do not have enough packets in the FEC group to + // trigger an FEC packet. + ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false)); frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector()))); SerializedPacket serialized = creator_.SerializeAllFrames(frames_); @@ -135,7 +189,9 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFEC) { { InSequence s; EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); EXPECT_CALL(framer_visitor_, OnFecProtectedPayload(_)); EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); @@ -144,16 +200,20 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFEC) { ProcessPacket(serialized.packet); delete serialized.packet; - ASSERT_FALSE(creator_.ShouldSendFec(false)); - ASSERT_TRUE(creator_.ShouldSendFec(true)); + // Should return false since we do not have enough packets in the FEC group to + // trigger an FEC packet. + ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false)); + // Should return true since there are packets in the FEC group. + ASSERT_TRUE(creator_.ShouldSendFec(/*force_close=*/true)); serialized = creator_.SerializeFec(); ASSERT_EQ(2u, serialized.sequence_number); - { InSequence s; EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); EXPECT_CALL(framer_visitor_, OnFecData(_)); EXPECT_CALL(framer_visitor_, OnPacketComplete()); @@ -162,11 +222,10 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFEC) { delete serialized.packet; } -TEST_F(QuicPacketCreatorTest, SerializeChangingSequenceNumberLength) { - frames_.push_back(QuicFrame(new QuicAckFrame(0u, QuicTime::Zero(), 0u))); +TEST_P(QuicPacketCreatorTest, SerializeChangingSequenceNumberLength) { + frames_.push_back(QuicFrame(new QuicAckFrame(MakeAckFrame(0u, 0u)))); creator_.AddSavedFrame(frames_[0]); - creator_.options()->send_sequence_number_length = - PACKET_4BYTE_SEQUENCE_NUMBER; + creator_.set_next_sequence_number_length(PACKET_4BYTE_SEQUENCE_NUMBER); SerializedPacket serialized = creator_.SerializePacket(); // The sequence number length will not change mid-packet. EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length); @@ -174,7 +233,9 @@ TEST_F(QuicPacketCreatorTest, SerializeChangingSequenceNumberLength) { { InSequence s; EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); EXPECT_CALL(framer_visitor_, OnAckFrame(_)); EXPECT_CALL(framer_visitor_, OnPacketComplete()); @@ -191,7 +252,9 @@ TEST_F(QuicPacketCreatorTest, SerializeChangingSequenceNumberLength) { { InSequence s; EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); EXPECT_CALL(framer_visitor_, OnAckFrame(_)); EXPECT_CALL(framer_visitor_, OnPacketComplete()); @@ -200,22 +263,130 @@ TEST_F(QuicPacketCreatorTest, SerializeChangingSequenceNumberLength) { delete serialized.packet; } -TEST_F(QuicPacketCreatorTest, SerializeWithFECChangingSequenceNumberLength) { - creator_.options()->max_packets_per_fec_group = 6; - ASSERT_FALSE(creator_.ShouldSendFec(false)); +TEST_P(QuicPacketCreatorTest, ChangeSequenceNumberLengthMidPacket) { + if (GetParam().version <= QUIC_VERSION_15) { + return; + } + // Changing the sequence number length with queued frames in the creator + // should hold the change until after any currently queued frames are + // serialized. + + // Packet 1. + // Queue a frame in the creator. + EXPECT_FALSE(creator_.HasPendingFrames()); + QuicFrame ack_frame = QuicFrame(new QuicAckFrame(MakeAckFrame(0u, 0u))); + creator_.AddSavedFrame(ack_frame); + + // Now change sequence number length. + creator_.set_next_sequence_number_length(PACKET_4BYTE_SEQUENCE_NUMBER); + + // Add a STOP_WAITING frame since it contains a packet sequence number, + // whose length should be 1. + QuicStopWaitingFrame stop_waiting_frame; + EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&stop_waiting_frame))); + EXPECT_TRUE(creator_.HasPendingFrames()); - frames_.push_back(QuicFrame(new QuicAckFrame(0u, QuicTime::Zero(), 0u))); + // Ensure the packet is successfully created. + SerializedPacket serialized = creator_.SerializePacket(); + ASSERT_TRUE(serialized.packet); + EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length); + + // Verify that header in transmitted packet has 1 byte sequence length. + QuicPacketHeader header; + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)).WillOnce( + DoAll(SaveArg<0>(&header), Return(true))); + EXPECT_CALL(framer_visitor_, OnAckFrame(_)); + EXPECT_CALL(framer_visitor_, OnStopWaitingFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + ProcessPacket(serialized.packet); + EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, + header.public_header.sequence_number_length); + delete serialized.packet; + + // Packet 2. + EXPECT_FALSE(creator_.HasPendingFrames()); + // Generate Packet 2 with one frame -- sequence number length should now + // change to 4 bytes. + EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&stop_waiting_frame))); + EXPECT_TRUE(creator_.HasPendingFrames()); + + // Ensure the packet is successfully created. + serialized = creator_.SerializePacket(); + ASSERT_TRUE(serialized.packet); + EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length); + + // Verify that header in transmitted packet has 4 byte sequence length. + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)).WillOnce( + DoAll(SaveArg<0>(&header), Return(true))); + EXPECT_CALL(framer_visitor_, OnStopWaitingFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + ProcessPacket(serialized.packet); + EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, + header.public_header.sequence_number_length); + + delete serialized.packet; + delete ack_frame.ack_frame; +} + +TEST_P(QuicPacketCreatorTest, SerializeWithFECChangingSequenceNumberLength) { + // Test goal is to test the following sequence (P1 => generate Packet 1): + // P1 <change seq num length> P2 FEC, + // and we expect that sequence number length should not change until the end + // of the open FEC group. + + // Enable FEC protection, and send FEC packet every 6 packets. + EXPECT_TRUE(SwitchFecProtectionOn(6)); + // Should return false since we do not have enough packets in the FEC group to + // trigger an FEC packet. + ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false)); + frames_.push_back(QuicFrame(new QuicAckFrame(MakeAckFrame(0u, 0u)))); + + // Generate Packet 1. creator_.AddSavedFrame(frames_[0]); // Change the sequence number length mid-FEC group and it should not change. - creator_.options()->send_sequence_number_length = - PACKET_4BYTE_SEQUENCE_NUMBER; + creator_.set_next_sequence_number_length(PACKET_4BYTE_SEQUENCE_NUMBER); SerializedPacket serialized = creator_.SerializePacket(); EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length); { InSequence s; EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnFecProtectedPayload(_)); + EXPECT_CALL(framer_visitor_, OnAckFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + ProcessPacket(serialized.packet); + delete serialized.packet; + + // Generate Packet 2. + creator_.AddSavedFrame(frames_[0]); + serialized = creator_.SerializePacket(); + EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length); + + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); EXPECT_CALL(framer_visitor_, OnFecProtectedPayload(_)); EXPECT_CALL(framer_visitor_, OnAckFrame(_)); @@ -224,17 +395,23 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFECChangingSequenceNumberLength) { ProcessPacket(serialized.packet); delete serialized.packet; - ASSERT_FALSE(creator_.ShouldSendFec(false)); - ASSERT_TRUE(creator_.ShouldSendFec(true)); + // Should return false since we do not have enough packets in the FEC group to + // trigger an FEC packet. + ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false)); + // Should return true since there are packets in the FEC group. + ASSERT_TRUE(creator_.ShouldSendFec(/*force_close=*/true)); + // Force generation of FEC packet. serialized = creator_.SerializeFec(); EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length); - ASSERT_EQ(2u, serialized.sequence_number); + ASSERT_EQ(3u, serialized.sequence_number); { InSequence s; EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); EXPECT_CALL(framer_visitor_, OnFecData(_)); EXPECT_CALL(framer_visitor_, OnPacketComplete()); @@ -249,19 +426,18 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFECChangingSequenceNumberLength) { delete serialized.packet; } -TEST_F(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) { +TEST_P(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) { // If the original packet sequence number length, the current sequence number // length, and the configured send sequence number length are different, the // retransmit must sent with the original length and the others do not change. - creator_.options()->send_sequence_number_length = - PACKET_4BYTE_SEQUENCE_NUMBER; + creator_.set_next_sequence_number_length(PACKET_4BYTE_SEQUENCE_NUMBER); QuicPacketCreatorPeer::SetSequenceNumberLength(&creator_, PACKET_2BYTE_SEQUENCE_NUMBER); frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector()))); SerializedPacket serialized = creator_.ReserializeAllFrames(frames_, PACKET_1BYTE_SEQUENCE_NUMBER); EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, - creator_.options()->send_sequence_number_length); + creator_.next_sequence_number_length()); EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER, QuicPacketCreatorPeer::GetSequenceNumberLength(&creator_)); EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length); @@ -270,7 +446,9 @@ TEST_F(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) { { InSequence s; EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); EXPECT_CALL(framer_visitor_, OnPacketComplete()); @@ -279,7 +457,7 @@ TEST_F(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) { delete serialized.packet; } -TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) { +TEST_P(QuicPacketCreatorTest, SerializeConnectionClose) { QuicConnectionCloseFrame frame; frame.error_code = QUIC_NO_ERROR; frame.error_details = "error"; @@ -290,7 +468,9 @@ TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) { InSequence s; EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); EXPECT_CALL(framer_visitor_, OnConnectionCloseFrame(_)); EXPECT_CALL(framer_visitor_, OnPacketComplete()); @@ -299,7 +479,84 @@ TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) { delete serialized.packet; } -TEST_F(QuicPacketCreatorTest, CreateStreamFrame) { +TEST_P(QuicPacketCreatorTest, SwitchFecOnOffWithNoGroup) { + // Enable FEC protection. + creator_.set_max_packets_per_fec_group(6); + EXPECT_TRUE(creator_.IsFecEnabled()); + EXPECT_FALSE(creator_.IsFecProtected()); + + // Turn on FEC protection. + creator_.StartFecProtectingPackets(); + EXPECT_TRUE(creator_.IsFecProtected()); + // We have no packets in the FEC group, so no FEC packet can be created. + EXPECT_FALSE(creator_.ShouldSendFec(/*force_close=*/true)); + // Since no packets are in FEC group yet, we should be able to turn FEC + // off with no trouble. + creator_.StopFecProtectingPackets(); + EXPECT_FALSE(creator_.IsFecProtected()); +} + +TEST_P(QuicPacketCreatorTest, SwitchFecOnOffWithGroupInProgress) { + // Enable FEC protection, and send FEC packet every 6 packets. + EXPECT_TRUE(SwitchFecProtectionOn(6)); + frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector()))); + SerializedPacket serialized = creator_.SerializeAllFrames(frames_); + delete frames_[0].stream_frame; + delete serialized.packet; + + EXPECT_TRUE(creator_.IsFecProtected()); + // We do not have enough packets in the FEC group to trigger an FEC packet. + EXPECT_FALSE(creator_.ShouldSendFec(/*force_close=*/false)); + // Should return true since there are packets in the FEC group. + EXPECT_TRUE(creator_.ShouldSendFec(/*force_close=*/true)); + + // Switching FEC off should not change creator state, since there is an + // FEC packet under construction. + EXPECT_DFATAL(creator_.StopFecProtectingPackets(), + "Cannot stop FEC protection with open FEC group."); + EXPECT_TRUE(creator_.IsFecProtected()); + // Confirm that FEC packet is still under construction. + EXPECT_TRUE(creator_.ShouldSendFec(/*force_close=*/true)); + + serialized = creator_.SerializeFec(); + delete serialized.packet; + + // Switching FEC on/off should work now. + creator_.StopFecProtectingPackets(); + EXPECT_FALSE(creator_.IsFecProtected()); + creator_.StartFecProtectingPackets(); + EXPECT_TRUE(creator_.IsFecProtected()); +} + +TEST_P(QuicPacketCreatorTest, SwitchFecOnWithStreamFrameQueued) { + // Add a stream frame to the creator. + QuicFrame frame; + size_t consumed = creator_.CreateStreamFrame( + 1u, MakeIOVector("test"), 0u, false, &frame); + EXPECT_EQ(4u, consumed); + ASSERT_TRUE(frame.stream_frame); + EXPECT_TRUE(creator_.AddSavedFrame(frame)); + EXPECT_TRUE(creator_.HasPendingFrames()); + + // Enable FEC protection, and send FEC packet every 6 packets. + creator_.set_max_packets_per_fec_group(6); + EXPECT_TRUE(creator_.IsFecEnabled()); + EXPECT_DFATAL(creator_.StartFecProtectingPackets(), + "Cannot start FEC protection with pending frames."); + EXPECT_FALSE(creator_.IsFecProtected()); + + // Serialize packet for transmission. + SerializedPacket serialized = creator_.SerializePacket(); + delete serialized.packet; + delete serialized.retransmittable_frames; + EXPECT_FALSE(creator_.HasPendingFrames()); + + // Since all pending frames have been serialized, turning FEC on should work. + creator_.StartFecProtectingPackets(); + EXPECT_TRUE(creator_.IsFecProtected()); +} + +TEST_P(QuicPacketCreatorTest, CreateStreamFrame) { QuicFrame frame; size_t consumed = creator_.CreateStreamFrame(1u, MakeIOVector("test"), 0u, false, &frame); @@ -308,7 +565,7 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrame) { delete frame.stream_frame; } -TEST_F(QuicPacketCreatorTest, CreateStreamFrameFin) { +TEST_P(QuicPacketCreatorTest, CreateStreamFrameFin) { QuicFrame frame; size_t consumed = creator_.CreateStreamFrame(1u, MakeIOVector("test"), 10u, true, &frame); @@ -317,7 +574,7 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrameFin) { delete frame.stream_frame; } -TEST_F(QuicPacketCreatorTest, CreateStreamFrameFinOnly) { +TEST_P(QuicPacketCreatorTest, CreateStreamFrameFinOnly) { QuicFrame frame; size_t consumed = creator_.CreateStreamFrame(1u, IOVector(), 0u, true, &frame); @@ -326,17 +583,20 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrameFinOnly) { delete frame.stream_frame; } -TEST_F(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) { - const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead(); +TEST_P(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) { + const size_t overhead = GetPacketHeaderOverhead(NOT_IN_FEC_GROUP) + + GetEncryptionOverhead(); for (size_t i = overhead; i < overhead + 100; ++i) { - creator_.options()->max_packet_length = i; - const bool should_have_room = i > overhead + GetStreamFrameOverhead(); - ASSERT_EQ(should_have_room, - creator_.HasRoomForStreamFrame(kStreamId, kOffset)); + creator_.set_max_packet_length(i); + const bool should_have_room = i > overhead + GetStreamFrameOverhead( + NOT_IN_FEC_GROUP); + ASSERT_EQ(should_have_room, creator_.HasRoomForStreamFrame( + kClientDataStreamId1, kOffset)); if (should_have_room) { QuicFrame frame; size_t bytes_consumed = creator_.CreateStreamFrame( - kStreamId, MakeIOVector("testdata"), kOffset, false, &frame); + kClientDataStreamId1, MakeIOVector("testdata"), kOffset, false, + &frame); EXPECT_LT(0u, bytes_consumed); ASSERT_TRUE(creator_.AddSavedFrame(frame)); SerializedPacket serialized_packet = creator_.SerializePacket(); @@ -347,10 +607,10 @@ TEST_F(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) { } } -TEST_F(QuicPacketCreatorTest, StreamFrameConsumption) { +TEST_P(QuicPacketCreatorTest, StreamFrameConsumption) { // Compute the total overhead for a single frame in packet. - const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead() - + GetStreamFrameOverhead(); + const size_t overhead = GetPacketHeaderOverhead(NOT_IN_FEC_GROUP) + + GetEncryptionOverhead() + GetStreamFrameOverhead(NOT_IN_FEC_GROUP); size_t capacity = kDefaultMaxPacketSize - overhead; // Now, test various sizes around this size. for (int delta = -5; delta <= 5; ++delta) { @@ -358,12 +618,13 @@ TEST_F(QuicPacketCreatorTest, StreamFrameConsumption) { size_t bytes_free = delta > 0 ? 0 : 0 - delta; QuicFrame frame; size_t bytes_consumed = creator_.CreateStreamFrame( - kStreamId, MakeIOVector(data), kOffset, false, &frame); + kClientDataStreamId1, MakeIOVector(data), kOffset, false, &frame); EXPECT_EQ(capacity - bytes_free, bytes_consumed); ASSERT_TRUE(creator_.AddSavedFrame(frame)); // BytesFree() returns bytes available for the next frame, which will // be two bytes smaller since the stream frame would need to be grown. + EXPECT_EQ(2u, creator_.ExpansionOnNewFrame()); size_t expected_bytes_free = bytes_free < 3 ? 0 : bytes_free - 2; EXPECT_EQ(expected_bytes_free, creator_.BytesFree()) << "delta: " << delta; SerializedPacket serialized_packet = creator_.SerializePacket(); @@ -373,10 +634,40 @@ TEST_F(QuicPacketCreatorTest, StreamFrameConsumption) { } } -TEST_F(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) { +TEST_P(QuicPacketCreatorTest, StreamFrameConsumptionWithFec) { + // Enable FEC protection, and send FEC packet every 6 packets. + EXPECT_TRUE(SwitchFecProtectionOn(6)); // Compute the total overhead for a single frame in packet. - const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead() - + GetStreamFrameOverhead(); + const size_t overhead = GetPacketHeaderOverhead(IN_FEC_GROUP) + + GetEncryptionOverhead() + GetStreamFrameOverhead(IN_FEC_GROUP); + size_t capacity = kDefaultMaxPacketSize - overhead; + // Now, test various sizes around this size. + for (int delta = -5; delta <= 5; ++delta) { + string data(capacity + delta, 'A'); + size_t bytes_free = delta > 0 ? 0 : 0 - delta; + QuicFrame frame; + size_t bytes_consumed = creator_.CreateStreamFrame( + kClientDataStreamId1, MakeIOVector(data), kOffset, false, &frame); + EXPECT_EQ(capacity - bytes_free, bytes_consumed); + + ASSERT_TRUE(creator_.AddSavedFrame(frame)); + // BytesFree() returns bytes available for the next frame. Since stream + // frame does not grow for FEC protected packets, this should be the same + // as bytes_free (bound by 0). + EXPECT_EQ(0u, creator_.ExpansionOnNewFrame()); + size_t expected_bytes_free = bytes_free > 0 ? bytes_free : 0; + EXPECT_EQ(expected_bytes_free, creator_.BytesFree()) << "delta: " << delta; + SerializedPacket serialized_packet = creator_.SerializePacket(); + ASSERT_TRUE(serialized_packet.packet); + delete serialized_packet.packet; + delete serialized_packet.retransmittable_frames; + } +} + +TEST_P(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) { + // Compute the total overhead for a single frame in packet. + const size_t overhead = GetPacketHeaderOverhead(NOT_IN_FEC_GROUP) + + GetEncryptionOverhead() + GetStreamFrameOverhead(NOT_IN_FEC_GROUP); ASSERT_GT(kMaxPacketSize, overhead); size_t capacity = kDefaultMaxPacketSize - overhead; // Now, test various sizes around this size. @@ -386,7 +677,7 @@ TEST_F(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) { QuicFrame frame; size_t bytes_consumed = creator_.CreateStreamFrame( - kStreamId, MakeIOVector(data), kOffset, false, &frame); + kCryptoStreamId, MakeIOVector(data), kOffset, false, &frame); EXPECT_LT(0u, bytes_consumed); ASSERT_TRUE(creator_.AddSavedFrame(frame)); SerializedPacket serialized_packet = creator_.SerializePacket(); @@ -406,10 +697,10 @@ TEST_F(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) { } } -TEST_F(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) { +TEST_P(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) { // Compute the total overhead for a single frame in packet. - const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead() - + GetStreamFrameOverhead(); + const size_t overhead = GetPacketHeaderOverhead(NOT_IN_FEC_GROUP) + + GetEncryptionOverhead() + GetStreamFrameOverhead(NOT_IN_FEC_GROUP); ASSERT_GT(kDefaultMaxPacketSize, overhead); size_t capacity = kDefaultMaxPacketSize - overhead; // Now, test various sizes around this size. @@ -419,7 +710,7 @@ TEST_F(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) { QuicFrame frame; size_t bytes_consumed = creator_.CreateStreamFrame( - kStreamId + 2, MakeIOVector(data), kOffset, false, &frame); + kClientDataStreamId1, MakeIOVector(data), kOffset, false, &frame); EXPECT_LT(0u, bytes_consumed); ASSERT_TRUE(creator_.AddSavedFrame(frame)); SerializedPacket serialized_packet = creator_.SerializePacket(); @@ -436,8 +727,8 @@ TEST_F(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) { } } -TEST_F(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) { - QuicPacketCreatorPeer::SetIsServer(&creator_, true); +TEST_P(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) { + QuicFramerPeer::SetIsServer(&client_framer_, true); QuicVersionVector versions; versions.push_back(test::QuicVersionMax()); scoped_ptr<QuicEncryptedPacket> encrypted( @@ -446,64 +737,66 @@ TEST_F(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) { { InSequence s; EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); EXPECT_CALL(framer_visitor_, OnVersionNegotiationPacket(_)); } - client_framer_.ProcessPacket(*encrypted.get()); + QuicFramerPeer::SetIsServer(&client_framer_, false); + client_framer_.ProcessPacket(*encrypted); } -TEST_F(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthLeastAwaiting) { +TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthLeastAwaiting) { EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, - creator_.options()->send_sequence_number_length); + creator_.next_sequence_number_length()); creator_.set_sequence_number(64); creator_.UpdateSequenceNumberLength(2, 10000); EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, - creator_.options()->send_sequence_number_length); + creator_.next_sequence_number_length()); creator_.set_sequence_number(64 * 256); creator_.UpdateSequenceNumberLength(2, 10000); EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER, - creator_.options()->send_sequence_number_length); + creator_.next_sequence_number_length()); creator_.set_sequence_number(64 * 256 * 256); creator_.UpdateSequenceNumberLength(2, 10000); EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, - creator_.options()->send_sequence_number_length); + creator_.next_sequence_number_length()); creator_.set_sequence_number(GG_UINT64_C(64) * 256 * 256 * 256 * 256); creator_.UpdateSequenceNumberLength(2, 10000); EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER, - creator_.options()->send_sequence_number_length); + creator_.next_sequence_number_length()); } -TEST_F(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthBandwidth) { +TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthBandwidth) { EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, - creator_.options()->send_sequence_number_length); + creator_.next_sequence_number_length()); creator_.UpdateSequenceNumberLength(1, 10000); EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, - creator_.options()->send_sequence_number_length); + creator_.next_sequence_number_length()); creator_.UpdateSequenceNumberLength(1, 10000 * 256); EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER, - creator_.options()->send_sequence_number_length); + creator_.next_sequence_number_length()); creator_.UpdateSequenceNumberLength(1, 10000 * 256 * 256); EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, - creator_.options()->send_sequence_number_length); + creator_.next_sequence_number_length()); creator_.UpdateSequenceNumberLength( 1, GG_UINT64_C(1000) * 256 * 256 * 256 * 256); EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER, - creator_.options()->send_sequence_number_length); + creator_.next_sequence_number_length()); } -TEST_F(QuicPacketCreatorTest, CreateStreamFrameWithNotifier) { +TEST_P(QuicPacketCreatorTest, CreateStreamFrameWithNotifier) { // Ensure that if CreateStreamFrame does not consume any data (e.g. a FIN only // frame) then any QuicAckNotifier that is passed in still gets attached to // the frame. - MockAckNotifierDelegate delegate; - QuicAckNotifier notifier(&delegate); + scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate); + QuicAckNotifier notifier(delegate.get()); QuicFrame frame; IOVector empty_iovector; bool fin = true; @@ -514,12 +807,8 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrameWithNotifier) { delete frame.stream_frame; } -INSTANTIATE_TEST_CASE_P(ToggleVersionSerialization, - QuicPacketCreatorTest, - ::testing::Values(false, true)); - TEST_P(QuicPacketCreatorTest, SerializeFrame) { - if (!GetParam()) { + if (!GetParam().version_serialization) { creator_.StopSendingVersion(); } frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector()))); @@ -530,27 +819,30 @@ TEST_P(QuicPacketCreatorTest, SerializeFrame) { { InSequence s; EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)).WillOnce( DoAll(SaveArg<0>(&header), Return(true))); EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); EXPECT_CALL(framer_visitor_, OnPacketComplete()); } ProcessPacket(serialized.packet); - EXPECT_EQ(GetParam(), header.public_header.version_flag); + EXPECT_EQ(GetParam().version_serialization, + header.public_header.version_flag); delete serialized.packet; } TEST_P(QuicPacketCreatorTest, CreateStreamFrameTooLarge) { - if (!GetParam()) { + if (!GetParam().version_serialization) { creator_.StopSendingVersion(); } // A string larger than fits into a frame. size_t payload_length; - creator_.options()->max_packet_length = GetPacketLengthForOneStream( + creator_.set_max_packet_length(GetPacketLengthForOneStream( client_framer_.version(), QuicPacketCreatorPeer::SendVersionInPacket(&creator_), - PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP, &payload_length); + PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP, &payload_length)); QuicFrame frame; const string too_long_payload(payload_length * 2, 'a'); size_t consumed = creator_.CreateStreamFrame( @@ -562,21 +854,21 @@ TEST_P(QuicPacketCreatorTest, CreateStreamFrameTooLarge) { } TEST_P(QuicPacketCreatorTest, AddFrameAndSerialize) { - if (!GetParam()) { + if (!GetParam().version_serialization) { creator_.StopSendingVersion(); } const size_t max_plaintext_size = - client_framer_.GetMaxPlaintextSize(creator_.options()->max_packet_length); + client_framer_.GetMaxPlaintextSize(creator_.max_packet_length()); EXPECT_FALSE(creator_.HasPendingFrames()); EXPECT_EQ(max_plaintext_size - GetPacketHeaderSize( - creator_.options()->send_guid_length, + creator_.connection_id_length(), QuicPacketCreatorPeer::SendVersionInPacket(&creator_), PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), creator_.BytesFree()); // Add a variety of frame types and then a padding frame. - QuicAckFrame ack_frame(0u, QuicTime::Zero(), 0u); + QuicAckFrame ack_frame(MakeAckFrame(0u, 0u)); EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame))); EXPECT_TRUE(creator_.HasPendingFrames()); @@ -614,14 +906,94 @@ TEST_P(QuicPacketCreatorTest, AddFrameAndSerialize) { EXPECT_FALSE(creator_.HasPendingFrames()); EXPECT_EQ(max_plaintext_size - GetPacketHeaderSize( - creator_.options()->send_guid_length, + creator_.connection_id_length(), QuicPacketCreatorPeer::SendVersionInPacket(&creator_), PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), creator_.BytesFree()); } -TEST_F(QuicPacketCreatorTest, EntropyFlag) { +TEST_P(QuicPacketCreatorTest, SerializeTruncatedAckFrameWithLargePacketSize) { + if (!GetParam().version_serialization) { + creator_.StopSendingVersion(); + } + creator_.set_max_packet_length(kMaxPacketSize); + const size_t max_plaintext_size = + client_framer_.GetMaxPlaintextSize(creator_.max_packet_length()); + + // Serialized length of ack frame with 2000 nack ranges should be limited by + // the number of nack ranges that can be fit in an ack frame. + QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(2000u, 0u); + size_t frame_len = client_framer_.GetSerializedFrameLength( + QuicFrame(&ack_frame), creator_.BytesFree(), true, true, + NOT_IN_FEC_GROUP, PACKET_1BYTE_SEQUENCE_NUMBER); + EXPECT_GT(creator_.BytesFree(), frame_len); + EXPECT_GT(max_plaintext_size, creator_.PacketSize()); + + // Add ack frame to creator. + EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame))); + EXPECT_TRUE(creator_.HasPendingFrames()); + EXPECT_GT(max_plaintext_size, creator_.PacketSize()); + EXPECT_LT(0u, creator_.BytesFree()); + + // Make sure that an additional stream frame can be added to the packet. + QuicFrame stream_frame; + size_t consumed = creator_.CreateStreamFrame( + 2u, MakeIOVector("test"), 0u, false, &stream_frame); + EXPECT_EQ(4u, consumed); + ASSERT_TRUE(stream_frame.stream_frame); + EXPECT_TRUE(creator_.AddSavedFrame(stream_frame)); + EXPECT_TRUE(creator_.HasPendingFrames()); + + // Ensure the packet is successfully created, and the packet size estimate + // matches the serialized packet length. + EXPECT_CALL(entropy_calculator_, + EntropyHash(_)).WillOnce(testing::Return(0)); + size_t est_packet_size = creator_.PacketSize(); + SerializedPacket serialized = creator_.SerializePacket(); + ASSERT_TRUE(serialized.packet); + EXPECT_EQ(est_packet_size, serialized.packet->length()); + delete serialized.retransmittable_frames; + delete serialized.packet; +} + +TEST_P(QuicPacketCreatorTest, SerializeTruncatedAckFrameWithSmallPacketSize) { + if (!GetParam().version_serialization) { + creator_.StopSendingVersion(); + } + creator_.set_max_packet_length(500u); + + const size_t max_plaintext_size = + client_framer_.GetMaxPlaintextSize(creator_.max_packet_length()); + EXPECT_EQ(max_plaintext_size - creator_.PacketSize(), creator_.BytesFree()); + + // Serialized length of ack frame with 2000 nack ranges should be limited by + // the packet size. + QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(2000u, 0u); + size_t frame_len = client_framer_.GetSerializedFrameLength( + QuicFrame(&ack_frame), creator_.BytesFree(), true, true, + NOT_IN_FEC_GROUP, PACKET_1BYTE_SEQUENCE_NUMBER); + EXPECT_EQ(creator_.BytesFree(), frame_len); + + // Add ack frame to creator. + EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame))); + EXPECT_TRUE(creator_.HasPendingFrames()); + EXPECT_EQ(max_plaintext_size, creator_.PacketSize()); + EXPECT_EQ(0u, creator_.BytesFree()); + + // Ensure the packet is successfully created, and the packet size estimate + // may not match the serialized packet length. + EXPECT_CALL(entropy_calculator_, + EntropyHash(_)).WillOnce(Return(0)); + size_t est_packet_size = creator_.PacketSize(); + SerializedPacket serialized = creator_.SerializePacket(); + ASSERT_TRUE(serialized.packet); + EXPECT_GE(est_packet_size, serialized.packet->length()); + delete serialized.packet; +} + + +TEST_P(QuicPacketCreatorTest, EntropyFlag) { frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector()))); for (int i = 0; i < 2; ++i) { diff --git a/chromium/net/quic/quic_packet_generator.cc b/chromium/net/quic/quic_packet_generator.cc index 3785f7d2f8d..9e7473ebbf0 100644 --- a/chromium/net/quic/quic_packet_generator.cc +++ b/chromium/net/quic/quic_packet_generator.cc @@ -4,6 +4,7 @@ #include "net/quic/quic_packet_generator.h" +#include "base/basictypes.h" #include "base/logging.h" #include "net/quic/quic_fec_group.h" #include "net/quic/quic_utils.h" @@ -14,15 +15,18 @@ namespace net { class QuicAckNotifier; -QuicPacketGenerator::QuicPacketGenerator(DelegateInterface* delegate, - DebugDelegateInterface* debug_delegate, - QuicPacketCreator* creator) +QuicPacketGenerator::QuicPacketGenerator(QuicConnectionId connection_id, + QuicFramer* framer, + QuicRandom* random_generator, + DelegateInterface* delegate) : delegate_(delegate), - debug_delegate_(debug_delegate), - packet_creator_(creator), + debug_delegate_(NULL), + packet_creator_(connection_id, framer, random_generator), batch_mode_(false), + should_fec_protect_(false), should_send_ack_(false), - should_send_feedback_(false) { + should_send_feedback_(false), + should_send_stop_waiting_(false) { } QuicPacketGenerator::~QuicPacketGenerator() { @@ -50,15 +54,34 @@ QuicPacketGenerator::~QuicPacketGenerator() { case GOAWAY_FRAME: delete it->goaway_frame; break; + case WINDOW_UPDATE_FRAME: + delete it->window_update_frame; + break; + case BLOCKED_FRAME: + delete it->blocked_frame; + break; + case STOP_WAITING_FRAME: + delete it->stop_waiting_frame; + break; + case PING_FRAME: + delete it->ping_frame; + break; case NUM_FRAME_TYPES: DCHECK(false) << "Cannot delete type: " << it->type; } } } -void QuicPacketGenerator::SetShouldSendAck(bool also_send_feedback) { +void QuicPacketGenerator::SetShouldSendAck(bool also_send_feedback, + bool also_send_stop_waiting) { should_send_ack_ = true; should_send_feedback_ = also_send_feedback; + should_send_stop_waiting_ = also_send_stop_waiting; + SendQueuedFrames(false); +} + +void QuicPacketGenerator::SetShouldSendStopWaiting() { + should_send_stop_waiting_ = true; SendQueuedFrames(false); } @@ -71,20 +94,26 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id, const IOVector& data_to_write, QuicStreamOffset offset, bool fin, + FecProtection fec_protection, QuicAckNotifier* notifier) { IsHandshake handshake = id == kCryptoStreamId ? IS_HANDSHAKE : NOT_HANDSHAKE; - // The caller should have flushed pending frames before sending handshake - // messages. - DCHECK(handshake == NOT_HANDSHAKE || !HasPendingFrames()); - SendQueuedFrames(false); + // To make reasoning about crypto frames easier, we don't combine them with + // other retransmittable frames in a single packet. + const bool flush = handshake == IS_HANDSHAKE && + packet_creator_.HasPendingRetransmittableFrames(); + SendQueuedFrames(flush); size_t total_bytes_consumed = 0; bool fin_consumed = false; - if (!packet_creator_->HasRoomForStreamFrame(id, offset)) { + if (!packet_creator_.HasRoomForStreamFrame(id, offset)) { SerializeAndSendPacket(); } + if (fec_protection == MUST_FEC_PROTECT) { + MaybeStartFecProtection(); + } + IOVector data = data_to_write; size_t data_size = data.TotalBufferSize(); while (delegate_->ShouldGeneratePacket(NOT_RETRANSMISSION, @@ -93,10 +122,10 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id, size_t bytes_consumed; if (notifier != NULL) { // We want to track which packet this stream frame ends up in. - bytes_consumed = packet_creator_->CreateStreamFrameWithNotifier( + bytes_consumed = packet_creator_.CreateStreamFrameWithNotifier( id, data, offset + total_bytes_consumed, fin, notifier, &frame); } else { - bytes_consumed = packet_creator_->CreateStreamFrame( + bytes_consumed = packet_creator_.CreateStreamFrame( id, data, offset + total_bytes_consumed, fin, &frame); } if (!AddFrame(frame)) { @@ -110,10 +139,10 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id, total_bytes_consumed += bytes_consumed; fin_consumed = fin && total_bytes_consumed == data_size; data.Consume(bytes_consumed); - DCHECK(data.Empty() || packet_creator_->BytesFree() == 0u); + DCHECK(data.Empty() || packet_creator_.BytesFree() == 0u); // TODO(ianswett): Restore packet reordering. - if (!InBatchMode() || !packet_creator_->HasRoomForStreamFrame(id, offset)) { + if (!InBatchMode() || !packet_creator_.HasRoomForStreamFrame(id, offset)) { SerializeAndSendPacket(); } @@ -121,27 +150,33 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id, // 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. + if (fec_protection == MUST_FEC_PROTECT) { + // Turn off FEC protection when we're done writing protected data. + DVLOG(1) << "Turning FEC protection OFF"; + should_fec_protect_ = false; + } break; } } - // Ensure the FEC group is closed at the end of this method if not in batch - // mode. - if (!InBatchMode() && packet_creator_->ShouldSendFec(true)) { - SerializedPacket serialized_fec = packet_creator_->SerializeFec(); - DCHECK(serialized_fec.packet); - delegate_->OnSerializedPacket(serialized_fec); + // Don't allow the handshake to be bundled with other retransmittable frames. + if (handshake == IS_HANDSHAKE) { + SendQueuedFrames(true); } - DCHECK(InBatchMode() || !packet_creator_->HasPendingFrames()); + // Try to close FEC group since we've either run out of data to send or we're + // blocked. If not in batch mode, force close the group. + MaybeSendFecPacketAndCloseGroup(!InBatchMode()); + + DCHECK(InBatchMode() || !packet_creator_.HasPendingFrames()); return QuicConsumedData(total_bytes_consumed, fin_consumed); } bool QuicPacketGenerator::CanSendWithNextPendingFrameAddition() const { DCHECK(HasPendingFrames()); HasRetransmittableData retransmittable = - (should_send_ack_ || should_send_feedback_) ? NO_RETRANSMITTABLE_DATA - : HAS_RETRANSMITTABLE_DATA; + (should_send_ack_ || should_send_feedback_ || should_send_stop_waiting_) + ? NO_RETRANSMITTABLE_DATA : HAS_RETRANSMITTABLE_DATA; if (retransmittable == HAS_RETRANSMITTABLE_DATA) { DCHECK(!queued_control_frames_.empty()); // These are retransmittable. } @@ -160,17 +195,58 @@ void QuicPacketGenerator::SendQueuedFrames(bool flush) { } if (!InBatchMode() || flush) { - if (packet_creator_->HasPendingFrames()) { + if (packet_creator_.HasPendingFrames()) { SerializeAndSendPacket(); } - // Ensure the FEC group is closed at the end of this method unless other // writes are pending. - if (packet_creator_->ShouldSendFec(true)) { - SerializedPacket serialized_fec = packet_creator_->SerializeFec(); - DCHECK(serialized_fec.packet); - delegate_->OnSerializedPacket(serialized_fec); - } + MaybeSendFecPacketAndCloseGroup(true); + } +} + +void QuicPacketGenerator::MaybeStartFecProtection() { + if (!packet_creator_.IsFecEnabled()) { + return; + } + DVLOG(1) << "Turning FEC protection ON"; + should_fec_protect_ = true; + if (packet_creator_.IsFecProtected()) { + // Only start creator's FEC protection if not already on. + return; + } + if (HasQueuedFrames()) { + // TODO(jri): This currently requires that the generator flush out any + // pending frames when FEC protection is turned on. If current packet can be + // converted to an FEC protected packet, do it. This will require the + // generator to check if the resulting expansion still allows the incoming + // frame to be added to the packet. + SendQueuedFrames(true); + } + packet_creator_.StartFecProtectingPackets(); + DCHECK(packet_creator_.IsFecProtected()); +} + +void QuicPacketGenerator::MaybeSendFecPacketAndCloseGroup(bool force) { + if (!packet_creator_.IsFecProtected() || + packet_creator_.HasPendingFrames()) { + return; + } + + if (packet_creator_.ShouldSendFec(force)) { + // TODO(jri): SerializeFec can return a NULL packet, and this should + // cause an early return, with a call to + // delegate_->OnPacketGenerationError. + SerializedPacket serialized_fec = packet_creator_.SerializeFec(); + DCHECK(serialized_fec.packet); + delegate_->OnSerializedPacket(serialized_fec); + } + + // Turn FEC protection off if the creator does not have an FEC group open. + // Note: We only wait until the frames queued in the creator are flushed; + // pending frames in the generator will not keep us from turning FEC off. + if (!should_fec_protect_ && !packet_creator_.IsFecGroupOpen()) { + packet_creator_.StopFecProtectingPackets(); + DCHECK(!packet_creator_.IsFecProtected()); } } @@ -192,12 +268,12 @@ void QuicPacketGenerator::FlushAllQueuedFrames() { } bool QuicPacketGenerator::HasQueuedFrames() const { - return packet_creator_->HasPendingFrames() || HasPendingFrames(); + return packet_creator_.HasPendingFrames() || HasPendingFrames(); } bool QuicPacketGenerator::HasPendingFrames() const { return should_send_ack_ || should_send_feedback_ || - !queued_control_frames_.empty(); + should_send_stop_waiting_ || !queued_control_frames_.empty(); } bool QuicPacketGenerator::AddNextPendingFrame() { @@ -219,9 +295,18 @@ bool QuicPacketGenerator::AddNextPendingFrame() { return !should_send_feedback_; } - if (queued_control_frames_.empty()) { - LOG(DFATAL) << "AddNextPendingFrame called with no queued control frames."; + if (should_send_stop_waiting_) { + pending_stop_waiting_frame_.reset(delegate_->CreateStopWaitingFrame()); + // If we can't this add the frame now, then we still need to do so later. + should_send_stop_waiting_ = + !AddFrame(QuicFrame(pending_stop_waiting_frame_.get())); + // Return success if we have cleared out this flag (i.e., added the frame). + // If we still need to send, then the frame is full, and we have failed. + return !should_send_stop_waiting_; } + + LOG_IF(DFATAL, queued_control_frames_.empty()) + << "AddNextPendingFrame called with no queued control frames."; if (!AddFrame(queued_control_frames_.back())) { // Packet was full. return false; @@ -231,7 +316,7 @@ bool QuicPacketGenerator::AddNextPendingFrame() { } bool QuicPacketGenerator::AddFrame(const QuicFrame& frame) { - bool success = packet_creator_->AddSavedFrame(frame); + bool success = packet_creator_.AddSavedFrame(frame); if (success && debug_delegate_) { debug_delegate_->OnFrameAddedToPacket(frame); } @@ -239,15 +324,48 @@ bool QuicPacketGenerator::AddFrame(const QuicFrame& frame) { } void QuicPacketGenerator::SerializeAndSendPacket() { - SerializedPacket serialized_packet = packet_creator_->SerializePacket(); + SerializedPacket serialized_packet = packet_creator_.SerializePacket(); DCHECK(serialized_packet.packet); delegate_->OnSerializedPacket(serialized_packet); + MaybeSendFecPacketAndCloseGroup(false); +} - if (packet_creator_->ShouldSendFec(false)) { - SerializedPacket serialized_fec = packet_creator_->SerializeFec(); - DCHECK(serialized_fec.packet); - delegate_->OnSerializedPacket(serialized_fec); - } +void QuicPacketGenerator::StopSendingVersion() { + packet_creator_.StopSendingVersion(); +} + +QuicPacketSequenceNumber QuicPacketGenerator::sequence_number() const { + return packet_creator_.sequence_number(); +} + +size_t QuicPacketGenerator::max_packet_length() const { + return packet_creator_.max_packet_length(); +} + +void QuicPacketGenerator::set_max_packet_length(size_t length) { + packet_creator_.set_max_packet_length(length); +} + +QuicEncryptedPacket* QuicPacketGenerator::SerializeVersionNegotiationPacket( + const QuicVersionVector& supported_versions) { + return packet_creator_.SerializeVersionNegotiationPacket(supported_versions); +} + +SerializedPacket QuicPacketGenerator::ReserializeAllFrames( + const QuicFrames& frames, + QuicSequenceNumberLength original_length) { + return packet_creator_.ReserializeAllFrames(frames, original_length); +} + +void QuicPacketGenerator::UpdateSequenceNumberLength( + QuicPacketSequenceNumber least_packet_awaited_by_peer, + QuicByteCount congestion_window) { + return packet_creator_.UpdateSequenceNumberLength( + least_packet_awaited_by_peer, congestion_window); +} + +void QuicPacketGenerator::set_encryption_level(EncryptionLevel level) { + packet_creator_.set_encryption_level(level); } } // namespace net diff --git a/chromium/net/quic/quic_packet_generator.h b/chromium/net/quic/quic_packet_generator.h index 1ff04882ae1..d408aeab31b 100644 --- a/chromium/net/quic/quic_packet_generator.h +++ b/chromium/net/quic/quic_packet_generator.h @@ -54,9 +54,14 @@ #define NET_QUIC_QUIC_PACKET_GENERATOR_H_ #include "net/quic/quic_packet_creator.h" +#include "net/quic/quic_types.h" namespace net { +namespace test { +class QuicPacketGeneratorPeer; +} // namespace test + class QuicAckNotifier; class NET_EXPORT_PRIVATE QuicPacketGenerator { @@ -69,6 +74,7 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator { IsHandshake handshake) = 0; virtual QuicAckFrame* CreateAckFrame() = 0; virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() = 0; + virtual QuicStopWaitingFrame* CreateStopWaitingFrame() = 0; // Takes ownership of |packet.packet| and |packet.retransmittable_frames|. virtual bool OnSerializedPacket(const SerializedPacket& packet) = 0; virtual void CloseConnection(QuicErrorCode error, bool from_peer) = 0; @@ -77,25 +83,33 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator { // Interface which gets callbacks from the QuicPacketGenerator at interesting // points. Implementations must not mutate the state of the generator // as a result of these callbacks. - class NET_EXPORT_PRIVATE DebugDelegateInterface { + class NET_EXPORT_PRIVATE DebugDelegate { public: - virtual ~DebugDelegateInterface() {} + virtual ~DebugDelegate() {} // Called when a frame has been added to the current packet. - virtual void OnFrameAddedToPacket(const QuicFrame& frame) = 0; + virtual void OnFrameAddedToPacket(const QuicFrame& frame) {} }; - QuicPacketGenerator(DelegateInterface* delegate, - DebugDelegateInterface* debug_delegate, - QuicPacketCreator* creator); + QuicPacketGenerator(QuicConnectionId connection_id, + QuicFramer* framer, + QuicRandom* random_generator, + DelegateInterface* delegate); virtual ~QuicPacketGenerator(); // Indicates that an ACK frame should be sent. If |also_send_feedback| is // true, then it also indicates a CONGESTION_FEEDBACK frame should be sent. + // If |also_send_stop_waiting| is true, then it also indicates that a + // STOP_WAITING frame should be sent as well. // The contents of the frame(s) will be generated via a call to the delegates // CreateAckFrame() and CreateFeedbackFrame() when the packet is serialized. - void SetShouldSendAck(bool also_send_feedback); + void SetShouldSendAck(bool also_send_feedback, + bool also_send_stop_waiting); + + // Indicates that a STOP_WAITING frame should be sent. + void SetShouldSendStopWaiting(); + void AddControlFrame(const QuicFrame& frame); // Given some data, may consume part or all of it and pass it to the @@ -109,6 +123,7 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator { const IOVector& data, QuicStreamOffset offset, bool fin, + FecProtection fec_protection, QuicAckNotifier* notifier); // Indicates whether batch mode is currently enabled. @@ -123,11 +138,63 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator { bool HasQueuedFrames() const; - void set_debug_delegate(DebugDelegateInterface* debug_delegate) { + // Makes the framer not serialize the protocol version in sent packets. + void StopSendingVersion(); + + // Creates a version negotiation packet which supports |supported_versions|. + // Caller owns the created packet. Also, sets the entropy hash of the + // serialized packet to a random bool and returns that value as a member of + // SerializedPacket. + QuicEncryptedPacket* SerializeVersionNegotiationPacket( + const QuicVersionVector& supported_versions); + + + // Re-serializes frames with the original packet's sequence number length. + // Used for retransmitting packets to ensure they aren't too long. + // Caller must ensure that any open FEC group is closed before calling this + // method. + SerializedPacket ReserializeAllFrames( + const QuicFrames& frames, + QuicSequenceNumberLength original_length); + + // Update the sequence number length to use in future packets as soon as it + // can be safely changed. + void UpdateSequenceNumberLength( + QuicPacketSequenceNumber least_packet_awaited_by_peer, + QuicByteCount congestion_window); + + // Sets the encryption level that will be applied to new packets. + void set_encryption_level(EncryptionLevel level); + + // Sequence number of the last created packet, or 0 if no packets have been + // created. + QuicPacketSequenceNumber sequence_number() const; + + size_t max_packet_length() const; + + void set_max_packet_length(size_t length); + + void set_debug_delegate(DebugDelegate* debug_delegate) { debug_delegate_ = debug_delegate; } private: + friend class test::QuicPacketGeneratorPeer; + + // Turn on FEC protection for subsequent packets in the generator. + // If no FEC group is currently open in the creator, this method flushes any + // queued frames in the generator and in the creator, and it then turns FEC on + // in the creator. This method may be called with an open FEC group in the + // creator, in which case, only the generator's state is altered. + void MaybeStartFecProtection(); + + // Serializes and calls the delegate on an FEC packet if one was under + // construction in the creator. When |force| is false, it relies on the + // creator being ready to send an FEC packet, otherwise FEC packet is sent + // as long as one is under construction in the creator. Also tries to turns + // off FEC protection in the creator if it's off in the generator. + void MaybeSendFecPacketAndCloseGroup(bool force); + void SendQueuedFrames(bool flush); // Test to see if we have pending ack, feedback, or control frames. @@ -144,23 +211,29 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator { void SerializeAndSendPacket(); DelegateInterface* delegate_; - DebugDelegateInterface* debug_delegate_; + DebugDelegate* debug_delegate_; - QuicPacketCreator* packet_creator_; + QuicPacketCreator packet_creator_; QuicFrames queued_control_frames_; // True if batch mode is currently enabled. bool batch_mode_; + // True if FEC protection is on. The creator may have an open FEC group even + // if this variable is false. + bool should_fec_protect_; + // Flags to indicate the need for just-in-time construction of a frame. bool should_send_ack_; bool should_send_feedback_; + bool should_send_stop_waiting_; // If we put a non-retransmittable frame (namley ack or feedback frame) in // this packet, then we have to hold a reference to it until we flush (and // serialize it). Retransmittable frames are referenced elsewhere so that they // can later be (optionally) retransmitted. scoped_ptr<QuicAckFrame> pending_ack_frame_; scoped_ptr<QuicCongestionFeedbackFrame> pending_feedback_frame_; + scoped_ptr<QuicStopWaitingFrame> pending_stop_waiting_frame_; DISALLOW_COPY_AND_ASSIGN(QuicPacketGenerator); }; diff --git a/chromium/net/quic/quic_packet_generator_test.cc b/chromium/net/quic/quic_packet_generator_test.cc index 8907db95a12..331b1834667 100644 --- a/chromium/net/quic/quic_packet_generator_test.cc +++ b/chromium/net/quic/quic_packet_generator_test.cc @@ -11,6 +11,8 @@ #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/quic_utils.h" +#include "net/quic/test_tools/quic_packet_creator_peer.h" +#include "net/quic/test_tools/quic_packet_generator_peer.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/quic/test_tools/simple_quic_framer.h" #include "testing/gmock/include/gmock/gmock.h" @@ -31,7 +33,7 @@ namespace { class MockDelegate : public QuicPacketGenerator::DelegateInterface { public: MockDelegate() {} - virtual ~MockDelegate() {} + virtual ~MockDelegate() OVERRIDE {} MOCK_METHOD3(ShouldGeneratePacket, bool(TransmissionType transmission_type, @@ -39,6 +41,7 @@ class MockDelegate : public QuicPacketGenerator::DelegateInterface { IsHandshake handshake)); MOCK_METHOD0(CreateAckFrame, QuicAckFrame*()); MOCK_METHOD0(CreateFeedbackFrame, QuicCongestionFeedbackFrame*()); + MOCK_METHOD0(CreateStopWaitingFrame, QuicStopWaitingFrame*()); MOCK_METHOD1(OnSerializedPacket, bool(const SerializedPacket& packet)); MOCK_METHOD2(CloseConnection, void(QuicErrorCode, bool)); @@ -81,6 +84,7 @@ struct PacketContents { num_feedback_frames(0), num_goaway_frames(0), num_rst_stream_frames(0), + num_stop_waiting_frames(0), num_stream_frames(0), fec_group(0) { } @@ -90,6 +94,7 @@ struct PacketContents { size_t num_feedback_frames; size_t num_goaway_frames; size_t num_rst_stream_frames; + size_t num_stop_waiting_frames; size_t num_stream_frames; QuicFecGroupNumber fec_group; @@ -101,16 +106,18 @@ class QuicPacketGeneratorTest : public ::testing::Test { protected: QuicPacketGeneratorTest() : framer_(QuicSupportedVersions(), QuicTime::Zero(), false), - creator_(42, &framer_, &random_, false), - generator_(&delegate_, NULL, &creator_), + generator_(42, &framer_, &random_, &delegate_), + creator_(QuicPacketGeneratorPeer::GetPacketCreator(&generator_)), packet_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL), packet2_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL), packet3_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL), packet4_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL), - packet5_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL) { + packet5_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL), + packet6_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL), + packet7_(0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL) { } - ~QuicPacketGeneratorTest() { + virtual ~QuicPacketGeneratorTest() OVERRIDE { delete packet_.packet; delete packet_.retransmittable_frames; delete packet2_.packet; @@ -121,11 +128,15 @@ class QuicPacketGeneratorTest : public ::testing::Test { delete packet4_.retransmittable_frames; delete packet5_.packet; delete packet5_.retransmittable_frames; + delete packet6_.packet; + delete packet6_.retransmittable_frames; + delete packet7_.packet; + delete packet7_.retransmittable_frames; } QuicAckFrame* CreateAckFrame() { // TODO(rch): Initialize this so it can be verified later. - return new QuicAckFrame(0, QuicTime::Zero(), 0); + return new QuicAckFrame(MakeAckFrame(0, 0)); } QuicCongestionFeedbackFrame* CreateFeedbackFrame() { @@ -135,8 +146,15 @@ class QuicPacketGeneratorTest : public ::testing::Test { return frame; } + QuicStopWaitingFrame* CreateStopWaitingFrame() { + QuicStopWaitingFrame* frame = new QuicStopWaitingFrame(); + frame->entropy_hash = 0; + frame->least_unacked = 0; + return frame; + } + QuicRstStreamFrame* CreateRstStreamFrame() { - return new QuicRstStreamFrame(1, QUIC_STREAM_NO_ERROR); + return new QuicRstStreamFrame(1, QUIC_STREAM_NO_ERROR, 0); } QuicGoAwayFrame* CreateGoAwayFrame() { @@ -149,7 +167,7 @@ class QuicPacketGeneratorTest : public ::testing::Test { contents.num_goaway_frames + contents.num_rst_stream_frames + contents.num_stream_frames; size_t num_frames = contents.num_feedback_frames + contents.num_ack_frames + - num_retransmittable_frames; + contents.num_stop_waiting_frames + num_retransmittable_frames; if (num_retransmittable_frames == 0) { ASSERT_TRUE(packet.retransmittable_frames == NULL); @@ -173,6 +191,8 @@ class QuicPacketGeneratorTest : public ::testing::Test { simple_framer_.rst_stream_frames().size()); EXPECT_EQ(contents.num_stream_frames, simple_framer_.stream_frames().size()); + EXPECT_EQ(contents.num_stop_waiting_frames, + simple_framer_.stop_waiting_frames().size()); EXPECT_EQ(contents.fec_group, simple_framer_.header().fec_group); } @@ -204,21 +224,23 @@ class QuicPacketGeneratorTest : public ::testing::Test { QuicFramer framer_; MockRandom random_; - QuicPacketCreator creator_; StrictMock<MockDelegate> delegate_; QuicPacketGenerator generator_; + QuicPacketCreator* creator_; SimpleQuicFramer simple_framer_; SerializedPacket packet_; SerializedPacket packet2_; SerializedPacket packet3_; SerializedPacket packet4_; SerializedPacket packet5_; + SerializedPacket packet6_; + SerializedPacket packet7_; private: scoped_ptr<char[]> data_array_; }; -class MockDebugDelegate : public QuicPacketGenerator::DebugDelegateInterface { +class MockDebugDelegate : public QuicPacketGenerator::DebugDelegate { public: MOCK_METHOD1(OnFrameAddedToPacket, void(const QuicFrame&)); @@ -227,7 +249,7 @@ class MockDebugDelegate : public QuicPacketGenerator::DebugDelegateInterface { TEST_F(QuicPacketGeneratorTest, ShouldSendAck_NotWritable) { delegate_.SetCanNotWrite(); - generator_.SetShouldSendAck(false); + generator_.SetShouldSendAck(false, false); EXPECT_TRUE(generator_.HasQueuedFrames()); } @@ -241,7 +263,7 @@ TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldNotFlush) { EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame())); EXPECT_CALL(debug_delegate, OnFrameAddedToPacket(_)).Times(1); - generator_.SetShouldSendAck(false); + generator_.SetShouldSendAck(false, false); EXPECT_TRUE(generator_.HasQueuedFrames()); } @@ -252,7 +274,7 @@ TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldFlush) { EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( DoAll(SaveArg<0>(&packet_), Return(true))); - generator_.SetShouldSendAck(false); + generator_.SetShouldSendAck(false, false); EXPECT_FALSE(generator_.HasQueuedFrames()); PacketContents contents; @@ -269,7 +291,7 @@ TEST_F(QuicPacketGeneratorTest, EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce( Return(CreateFeedbackFrame())); - generator_.SetShouldSendAck(true); + generator_.SetShouldSendAck(true, false); EXPECT_TRUE(generator_.HasQueuedFrames()); } @@ -280,16 +302,19 @@ TEST_F(QuicPacketGeneratorTest, EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame())); EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce( Return(CreateFeedbackFrame())); + EXPECT_CALL(delegate_, CreateStopWaitingFrame()).WillOnce( + Return(CreateStopWaitingFrame())); EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( DoAll(SaveArg<0>(&packet_), Return(true))); - generator_.SetShouldSendAck(true); + generator_.SetShouldSendAck(true, true); EXPECT_FALSE(generator_.HasQueuedFrames()); PacketContents contents; contents.num_ack_frames = 1; contents.num_feedback_frames = 1; + contents.num_stop_waiting_frames = 1; CheckPacketContains(contents, packet_); } @@ -351,8 +376,8 @@ TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldFlush) { TEST_F(QuicPacketGeneratorTest, ConsumeData_NotWritable) { delegate_.SetCanNotWrite(); - QuicConsumedData consumed = generator_.ConsumeData(1, MakeIOVector("foo"), 2, - true, NULL); + QuicConsumedData consumed = generator_.ConsumeData( + kHeadersStreamId, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, NULL); EXPECT_EQ(0u, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); EXPECT_FALSE(generator_.HasQueuedFrames()); @@ -362,8 +387,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldNotFlush) { delegate_.SetCanWriteAnything(); generator_.StartBatchOperations(); - QuicConsumedData consumed = generator_.ConsumeData(1, MakeIOVector("foo"), 2, - true, NULL); + QuicConsumedData consumed = generator_.ConsumeData( + kHeadersStreamId, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, NULL); EXPECT_EQ(3u, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); EXPECT_TRUE(generator_.HasQueuedFrames()); @@ -374,8 +399,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldFlush) { EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( DoAll(SaveArg<0>(&packet_), Return(true))); - QuicConsumedData consumed = generator_.ConsumeData(1, MakeIOVector("foo"), 2, - true, NULL); + QuicConsumedData consumed = generator_.ConsumeData( + kHeadersStreamId, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, NULL); EXPECT_EQ(3u, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); EXPECT_FALSE(generator_.HasQueuedFrames()); @@ -390,9 +415,10 @@ TEST_F(QuicPacketGeneratorTest, delegate_.SetCanWriteAnything(); generator_.StartBatchOperations(); - generator_.ConsumeData(1, MakeIOVector("foo"), 2, true, NULL); - QuicConsumedData consumed = generator_.ConsumeData(3, MakeIOVector("quux"), 7, - false, NULL); + generator_.ConsumeData(kHeadersStreamId, MakeIOVector("foo"), 2, true, + MAY_FEC_PROTECT, NULL); + QuicConsumedData consumed = generator_.ConsumeData( + 3, MakeIOVector("quux"), 7, false, MAY_FEC_PROTECT, NULL); EXPECT_EQ(4u, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); EXPECT_TRUE(generator_.HasQueuedFrames()); @@ -402,9 +428,10 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_BatchOperations) { delegate_.SetCanWriteAnything(); generator_.StartBatchOperations(); - generator_.ConsumeData(1, MakeIOVector("foo"), 2, true, NULL); - QuicConsumedData consumed = generator_.ConsumeData(3, MakeIOVector("quux"), 7, - false, NULL); + generator_.ConsumeData(kHeadersStreamId, MakeIOVector("foo"), 2, true, + MAY_FEC_PROTECT, NULL); + QuicConsumedData consumed = generator_.ConsumeData( + 3, MakeIOVector("quux"), 7, false, MAY_FEC_PROTECT, NULL); EXPECT_EQ(4u, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); EXPECT_TRUE(generator_.HasQueuedFrames()); @@ -424,7 +451,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFEC) { delegate_.SetCanWriteAnything(); // Send FEC every two packets. - creator_.options()->max_packets_per_fec_group = 2; + creator_->set_max_packets_per_fec_group(2); { InSequence dummy; @@ -440,10 +467,12 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFEC) { DoAll(SaveArg<0>(&packet5_), Return(true))); } - // Send enough data to create 3 packets: two full and one partial. + // Send enough data to create 3 packets: two full and one partial. Send + // with MUST_FEC_PROTECT flag. size_t data_len = 2 * kDefaultMaxPacketSize + 100; QuicConsumedData consumed = - generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL); + generator_.ConsumeData(3, CreateData(data_len), 0, true, + MUST_FEC_PROTECT, NULL); EXPECT_EQ(data_len, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); EXPECT_FALSE(generator_.HasQueuedFrames()); @@ -459,9 +488,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFEC) { TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) { delegate_.SetCanWriteAnything(); - // Send FEC every six packets. - creator_.options()->max_packets_per_fec_group = 6; - + // Enable FEC. + creator_->set_max_packets_per_fec_group(6); { InSequence dummy; EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( @@ -472,10 +500,12 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) { DoAll(SaveArg<0>(&packet3_), Return(true))); } - // Send enough data to create 2 packets: one full and one partial. + // Send enough data to create 2 packets: one full and one partial. Send + // with MUST_FEC_PROTECT flag. size_t data_len = 1 * kDefaultMaxPacketSize + 100; QuicConsumedData consumed = - generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL); + generator_.ConsumeData(3, CreateData(data_len), 0, true, + MUST_FEC_PROTECT, NULL); EXPECT_EQ(data_len, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); EXPECT_FALSE(generator_.HasQueuedFrames()); @@ -488,16 +518,19 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) { TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) { // Set the packet size be enough for two stream frames with 0 stream offset, // but not enough for a stream frame of 0 offset and one with non-zero offset. - creator_.options()->max_packet_length = + size_t length = NullEncrypter().GetCiphertextSize(0) + - GetPacketHeaderSize(creator_.options()->send_guid_length, + GetPacketHeaderSize(creator_->connection_id_length(), true, - creator_.options()->send_sequence_number_length, + creator_->next_sequence_number_length(), NOT_IN_FEC_GROUP) + // Add an extra 3 bytes for the payload and 1 byte so BytesFree is larger // than the GetMinStreamFrameSize. - QuicFramer::GetMinStreamFrameSize(framer_.version(), 1, 0, false) + 3 + - QuicFramer::GetMinStreamFrameSize(framer_.version(), 1, 0, true) + 1; + QuicFramer::GetMinStreamFrameSize(framer_.version(), 1, 0, false, + NOT_IN_FEC_GROUP) + 3 + + QuicFramer::GetMinStreamFrameSize(framer_.version(), 1, 0, true, + NOT_IN_FEC_GROUP) + 1; + creator_->set_max_packet_length(length); delegate_.SetCanWriteAnything(); { InSequence dummy; @@ -509,8 +542,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) { generator_.StartBatchOperations(); // Queue enough data to prevent a stream frame with a non-zero offset from // fitting. - QuicConsumedData consumed = generator_.ConsumeData(1, MakeIOVector("foo"), 0, - false, NULL); + QuicConsumedData consumed = generator_.ConsumeData( + kHeadersStreamId, MakeIOVector("foo"), 0, false, MAY_FEC_PROTECT, NULL); EXPECT_EQ(3u, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); EXPECT_TRUE(generator_.HasQueuedFrames()); @@ -518,7 +551,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) { // This frame will not fit with the existing frame, causing the queued frame // to be serialized, and it will not fit with another frame like it, so it is // serialized by itself. - consumed = generator_.ConsumeData(1, MakeIOVector("bar"), 3, true, NULL); + consumed = generator_.ConsumeData(kHeadersStreamId, MakeIOVector("bar"), 3, + true, MAY_FEC_PROTECT, NULL); EXPECT_EQ(3u, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); EXPECT_FALSE(generator_.HasQueuedFrames()); @@ -529,10 +563,259 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) { CheckPacketContains(contents, packet2_); } +TEST_F(QuicPacketGeneratorTest, SwitchFecOnOff) { + delegate_.SetCanWriteAnything(); + // Enable FEC. + creator_->set_max_packets_per_fec_group(2); + EXPECT_FALSE(creator_->IsFecProtected()); + + // Send one unprotected data packet. + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet_), Return(true))); + QuicConsumedData consumed = + generator_.ConsumeData(5, CreateData(1u), 0, true, MAY_FEC_PROTECT, + NULL); + EXPECT_EQ(1u, consumed.bytes_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(creator_->IsFecProtected()); + // Verify that one data packet was sent. + PacketContents contents; + contents.num_stream_frames = 1; + CheckPacketContains(contents, packet_); + + { + InSequence dummy; + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet2_), Return(true))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet3_), Return(true))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet4_), Return(true))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet5_), Return(true))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet6_), Return(true))); + } + // Send enough data to create 3 packets with MUST_FEC_PROTECT flag. + size_t data_len = 2 * kDefaultMaxPacketSize + 100; + consumed = generator_.ConsumeData(7, CreateData(data_len), 0, true, + MUST_FEC_PROTECT, NULL); + EXPECT_EQ(data_len, consumed.bytes_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + + // Verify that two FEC packets were sent. + CheckPacketHasSingleStreamFrame(packet2_); + CheckPacketHasSingleStreamFrame(packet3_); + CheckPacketIsFec(packet4_, /*fec_group=*/2u); + CheckPacketHasSingleStreamFrame(packet5_); + CheckPacketIsFec(packet6_, /*fec_group=*/5u); // Sent at the end of stream. + + // Send one unprotected data packet. + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet7_), Return(true))); + consumed = generator_.ConsumeData(7, CreateData(1u), 0, true, + MAY_FEC_PROTECT, NULL); + EXPECT_EQ(1u, consumed.bytes_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(creator_->IsFecProtected()); + // Verify that one unprotected data packet was sent. + CheckPacketContains(contents, packet7_); +} + +TEST_F(QuicPacketGeneratorTest, SwitchFecOnWithPendingFrameInCreator) { + delegate_.SetCanWriteAnything(); + // Enable FEC. + creator_->set_max_packets_per_fec_group(2); + + generator_.StartBatchOperations(); + // Queue enough data to prevent a stream frame with a non-zero offset from + // fitting. + QuicConsumedData consumed = generator_.ConsumeData( + 7, CreateData(1u), 0, true, MAY_FEC_PROTECT, NULL); + EXPECT_EQ(1u, consumed.bytes_consumed); + EXPECT_TRUE(creator_->HasPendingFrames()); + + // Queue protected data for sending. Should cause queued frames to be flushed. + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet_), Return(true))); + EXPECT_FALSE(creator_->IsFecProtected()); + consumed = generator_.ConsumeData(7, CreateData(1u), 0, true, + MUST_FEC_PROTECT, NULL); + EXPECT_EQ(1u, consumed.bytes_consumed); + PacketContents contents; + contents.num_stream_frames = 1; + // Transmitted packet was not FEC protected. + CheckPacketContains(contents, packet_); + EXPECT_TRUE(creator_->IsFecProtected()); + EXPECT_TRUE(creator_->HasPendingFrames()); +} + +TEST_F(QuicPacketGeneratorTest, SwitchFecOnWithPendingFramesInGenerator) { + // Enable FEC. + creator_->set_max_packets_per_fec_group(2); + + // Queue control frames in generator. + delegate_.SetCanNotWrite(); + generator_.SetShouldSendAck(true, true); + delegate_.SetCanWriteAnything(); + generator_.StartBatchOperations(); + + // Set up frames to write into the creator when control frames are written. + EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame())); + EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce( + Return(CreateFeedbackFrame())); + EXPECT_CALL(delegate_, CreateStopWaitingFrame()).WillOnce( + Return(CreateStopWaitingFrame())); + + // Generator should have queued control frames, and creator should be empty. + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_FALSE(creator_->HasPendingFrames()); + EXPECT_FALSE(creator_->IsFecProtected()); + + // Queue protected data for sending. Should cause queued frames to be flushed. + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet_), Return(true))); + QuicConsumedData consumed = generator_.ConsumeData(7, CreateData(1u), 0, true, + MUST_FEC_PROTECT, NULL); + EXPECT_EQ(1u, consumed.bytes_consumed); + PacketContents contents; + contents.num_ack_frames = 1; + contents.num_feedback_frames = 1; + contents.num_stop_waiting_frames = 1; + CheckPacketContains(contents, packet_); + + // FEC protection should be on in creator. + EXPECT_TRUE(creator_->IsFecProtected()); +} + +TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffWithSubsequentFramesProtected) { + delegate_.SetCanWriteAnything(); + + // Enable FEC. + creator_->set_max_packets_per_fec_group(2); + EXPECT_FALSE(creator_->IsFecProtected()); + + // Queue stream frame to be protected in creator. + generator_.StartBatchOperations(); + QuicConsumedData consumed = generator_.ConsumeData(5, CreateData(1u), 0, true, + MUST_FEC_PROTECT, NULL); + EXPECT_EQ(1u, consumed.bytes_consumed); + // Creator has a pending protected frame. + EXPECT_TRUE(creator_->HasPendingFrames()); + EXPECT_TRUE(creator_->IsFecProtected()); + + // Add enough unprotected data to exceed size of current packet, so that + // current packet is sent. Both frames will be sent out in a single packet. + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet_), Return(true))); + size_t data_len = kDefaultMaxPacketSize; + consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true, + MAY_FEC_PROTECT, NULL); + EXPECT_EQ(data_len, consumed.bytes_consumed); + PacketContents contents; + contents.num_stream_frames = 2u; + contents.fec_group = 1u; + CheckPacketContains(contents, packet_); + // FEC protection should still be on in creator. + EXPECT_TRUE(creator_->IsFecProtected()); +} + +TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffWithSubsequentPacketsProtected) { + delegate_.SetCanWriteAnything(); + + // Enable FEC. + creator_->set_max_packets_per_fec_group(2); + EXPECT_FALSE(creator_->IsFecProtected()); + + generator_.StartBatchOperations(); + // Send first packet, FEC protected. + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet_), Return(true))); + // Write enough data to cause a packet to be emitted. + size_t data_len = kDefaultMaxPacketSize; + QuicConsumedData consumed = generator_.ConsumeData( + 5, CreateData(data_len), 0, true, MUST_FEC_PROTECT, NULL); + EXPECT_EQ(data_len, consumed.bytes_consumed); + PacketContents contents; + contents.num_stream_frames = 1u; + contents.fec_group = 1u; + CheckPacketContains(contents, packet_); + + // FEC should still be on in creator. + EXPECT_TRUE(creator_->IsFecProtected()); + + // Send enough unprotected data to cause second packet to be sent, which gets + // protected because it happens to fall within an open FEC group. Data packet + // will be followed by FEC packet. + { + InSequence dummy; + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet2_), Return(true))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet3_), Return(true))); + } + consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true, + MAY_FEC_PROTECT, NULL); + EXPECT_EQ(data_len, consumed.bytes_consumed); + contents.num_stream_frames = 2u; + CheckPacketContains(contents, packet2_); + CheckPacketIsFec(packet3_, /*fec_group=*/1u); + + // FEC protection should be off in creator. + EXPECT_FALSE(creator_->IsFecProtected()); +} + +TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffThenOnWithCreatorProtectionOn) { + delegate_.SetCanWriteAnything(); + generator_.StartBatchOperations(); + + // Enable FEC. + creator_->set_max_packets_per_fec_group(2); + EXPECT_FALSE(creator_->IsFecProtected()); + + // Queue one byte of FEC protected data. + QuicConsumedData consumed = generator_.ConsumeData(5, CreateData(1u), 0, true, + MUST_FEC_PROTECT, NULL); + EXPECT_TRUE(creator_->HasPendingFrames()); + + // Add more unprotected data causing first packet to be sent, FEC protected. + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet_), Return(true))); + size_t data_len = kDefaultMaxPacketSize; + consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true, + MAY_FEC_PROTECT, NULL); + EXPECT_EQ(data_len, consumed.bytes_consumed); + PacketContents contents; + contents.num_stream_frames = 2u; + contents.fec_group = 1u; + CheckPacketContains(contents, packet_); + + // FEC group is still open in creator. + EXPECT_TRUE(creator_->IsFecProtected()); + + // Add data that should be protected, large enough to cause second packet to + // be sent. Data packet should be followed by FEC packet. + { + InSequence dummy; + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet2_), Return(true))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet3_), Return(true))); + } + consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true, + MUST_FEC_PROTECT, NULL); + EXPECT_EQ(data_len, consumed.bytes_consumed); + CheckPacketContains(contents, packet2_); + CheckPacketIsFec(packet3_, /*fec_group=*/1u); + + // FEC protection should remain on in creator. + EXPECT_TRUE(creator_->IsFecProtected()); +} + TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) { delegate_.SetCanNotWrite(); - generator_.SetShouldSendAck(true); + generator_.SetShouldSendAck(true, false); generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame())); EXPECT_TRUE(generator_.HasQueuedFrames()); @@ -547,7 +830,8 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) { Return(CreateFeedbackFrame())); // Send some data and a control frame - generator_.ConsumeData(3, MakeIOVector("quux"), 7, false, NULL); + generator_.ConsumeData(3, MakeIOVector("quux"), 7, false, + MAY_FEC_PROTECT, NULL); generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame())); // All five frames will be flushed out in a single packet. @@ -568,7 +852,7 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) { TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) { delegate_.SetCanNotWrite(); - generator_.SetShouldSendAck(true); + generator_.SetShouldSendAck(true, false); generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame())); EXPECT_TRUE(generator_.HasQueuedFrames()); @@ -594,7 +878,8 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) { // Send enough data to exceed one packet size_t data_len = kDefaultMaxPacketSize + 100; QuicConsumedData consumed = - generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL); + generator_.ConsumeData(3, CreateData(data_len), 0, true, + MAY_FEC_PROTECT, NULL); EXPECT_EQ(data_len, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame())); diff --git a/chromium/net/quic/quic_packet_writer.h b/chromium/net/quic/quic_packet_writer.h index 5df30c8fa14..16b7adeadc6 100644 --- a/chromium/net/quic/quic_packet_writer.h +++ b/chromium/net/quic/quic_packet_writer.h @@ -10,7 +10,6 @@ namespace net { -class QuicBlockedWriterInterface; struct WriteResult; // An interface between writers and the entity managing the @@ -26,14 +25,20 @@ class NET_EXPORT_PRIVATE QuicPacketWriter { // and error_code is populated. virtual WriteResult WritePacket( const char* buffer, size_t buf_len, - const net::IPAddressNumber& self_address, - const net::IPEndPoint& peer_address, - QuicBlockedWriterInterface* blocked_writer) = 0; + const IPAddressNumber& self_address, + const IPEndPoint& peer_address) = 0; // Returns true if the writer buffers and subsequently rewrites data // when an attempt to write results in the underlying socket becoming // write blocked. virtual bool IsWriteBlockedDataBuffered() const = 0; + + // Returns true if the network socket is not writable. + virtual bool IsWriteBlocked() const = 0; + + // Records that the socket has become writable, for example when an EPOLLOUT + // is received or an asynchronous write completes. + virtual void SetWritable() = 0; }; } // namespace net diff --git a/chromium/net/quic/quic_protocol.cc b/chromium/net/quic/quic_protocol.cc index cc37c111d7e..398c94d525a 100644 --- a/chromium/net/quic/quic_protocol.cc +++ b/chromium/net/quic/quic_protocol.cc @@ -15,48 +15,46 @@ using std::string; namespace net { -size_t GetPacketHeaderSize(QuicPacketHeader header) { - return GetPacketHeaderSize(header.public_header.guid_length, +size_t GetPacketHeaderSize(const QuicPacketHeader& header) { + return GetPacketHeaderSize(header.public_header.connection_id_length, header.public_header.version_flag, header.public_header.sequence_number_length, header.is_in_fec_group); } -size_t GetPacketHeaderSize(QuicGuidLength guid_length, +size_t GetPacketHeaderSize(QuicConnectionIdLength connection_id_length, bool include_version, QuicSequenceNumberLength sequence_number_length, InFecGroup is_in_fec_group) { - return kPublicFlagsSize + guid_length + + return kPublicFlagsSize + connection_id_length + (include_version ? kQuicVersionSize : 0) + sequence_number_length + kPrivateFlagsSize + (is_in_fec_group == IN_FEC_GROUP ? kFecGroupSize : 0); } -size_t GetPublicResetPacketSize() { - return kPublicFlagsSize + PACKET_8BYTE_GUID + kPublicResetNonceSize + - PACKET_6BYTE_SEQUENCE_NUMBER; -} - size_t GetStartOfFecProtectedData( - QuicGuidLength guid_length, + QuicConnectionIdLength connection_id_length, bool include_version, QuicSequenceNumberLength sequence_number_length) { - return GetPacketHeaderSize( - guid_length, include_version, sequence_number_length, IN_FEC_GROUP); + return GetPacketHeaderSize(connection_id_length, + include_version, + sequence_number_length, + IN_FEC_GROUP); } size_t GetStartOfEncryptedData( - QuicGuidLength guid_length, + QuicConnectionIdLength connection_id_length, bool include_version, QuicSequenceNumberLength sequence_number_length) { // Don't include the fec size, since encryption starts before private flags. - return GetPacketHeaderSize( - guid_length, include_version, sequence_number_length, NOT_IN_FEC_GROUP) - - kPrivateFlagsSize; + return GetPacketHeaderSize(connection_id_length, + include_version, + sequence_number_length, + NOT_IN_FEC_GROUP) - kPrivateFlagsSize; } QuicPacketPublicHeader::QuicPacketPublicHeader() - : guid(0), - guid_length(PACKET_8BYTE_GUID), + : connection_id(0), + connection_id_length(PACKET_8BYTE_CONNECTION_ID), reset_flag(false), version_flag(false), sequence_number_length(PACKET_6BYTE_SEQUENCE_NUMBER) { @@ -64,8 +62,8 @@ QuicPacketPublicHeader::QuicPacketPublicHeader() QuicPacketPublicHeader::QuicPacketPublicHeader( const QuicPacketPublicHeader& other) - : guid(other.guid), - guid_length(other.guid_length), + : connection_id(other.connection_id), + connection_id_length(other.connection_id_length), reset_flag(other.reset_flag), version_flag(other.version_flag), sequence_number_length(other.sequence_number_length), @@ -93,7 +91,21 @@ QuicPacketHeader::QuicPacketHeader(const QuicPacketPublicHeader& header) fec_group(0) { } -QuicStreamFrame::QuicStreamFrame() {} +QuicPublicResetPacket::QuicPublicResetPacket() + : nonce_proof(0), + rejected_sequence_number(0) {} + +QuicPublicResetPacket::QuicPublicResetPacket( + const QuicPacketPublicHeader& header) + : public_header(header), + nonce_proof(0), + rejected_sequence_number(0) {} + +QuicStreamFrame::QuicStreamFrame() + : stream_id(0), + fin(false), + offset(0), + notifier(NULL) {} QuicStreamFrame::QuicStreamFrame(const QuicStreamFrame& frame) : stream_id(frame.stream_id), @@ -132,6 +144,11 @@ uint32 MakeQuicTag(char a, char b, char c, char d) { static_cast<uint32>(d) << 24; } +bool ContainsQuicTag(const QuicTagVector& tag_vector, QuicTag tag) { + return std::find(tag_vector.begin(), tag_vector.end(), tag) + != tag_vector.end(); +} + QuicVersionVector QuicSupportedVersions() { QuicVersionVector supported_versions; for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) { @@ -142,8 +159,18 @@ QuicVersionVector QuicSupportedVersions() { QuicTag QuicVersionToQuicTag(const QuicVersion version) { switch (version) { - case QUIC_VERSION_12: - return MakeQuicTag('Q', '0', '1', '2'); + case QUIC_VERSION_15: + return MakeQuicTag('Q', '0', '1', '5'); + case QUIC_VERSION_16: + return MakeQuicTag('Q', '0', '1', '6'); + case QUIC_VERSION_17: + return MakeQuicTag('Q', '0', '1', '7'); + case QUIC_VERSION_18: + return MakeQuicTag('Q', '0', '1', '8'); + case QUIC_VERSION_19: + return MakeQuicTag('Q', '0', '1', '9'); + case QUIC_VERSION_20: + return MakeQuicTag('Q', '0', '2', '0'); default: // This shold be an ERROR because we should never attempt to convert an // invalid QuicVersion to be written to the wire. @@ -160,7 +187,7 @@ QuicVersion QuicTagToQuicVersion(const QuicTag version_tag) { } // Reading from the client so this should not be considered an ERROR. DVLOG(1) << "Unsupported QuicTag version: " - << QuicUtils::TagToString(version_tag); + << QuicUtils::TagToString(version_tag); return QUIC_VERSION_UNSUPPORTED; } @@ -170,7 +197,12 @@ return #x string QuicVersionToString(const QuicVersion version) { switch (version) { - RETURN_STRING_LITERAL(QUIC_VERSION_12); + RETURN_STRING_LITERAL(QUIC_VERSION_15); + RETURN_STRING_LITERAL(QUIC_VERSION_16); + RETURN_STRING_LITERAL(QUIC_VERSION_17); + RETURN_STRING_LITERAL(QUIC_VERSION_18); + RETURN_STRING_LITERAL(QUIC_VERSION_19); + RETURN_STRING_LITERAL(QUIC_VERSION_20); default: return "QUIC_VERSION_UNSUPPORTED"; } @@ -188,8 +220,8 @@ string QuicVersionVectorToString(const QuicVersionVector& versions) { } ostream& operator<<(ostream& os, const QuicPacketHeader& header) { - os << "{ guid: " << header.public_header.guid - << ", guid_length:" << header.public_header.guid_length + os << "{ connection_id: " << header.public_header.connection_id + << ", connection_id_length:" << header.public_header.connection_id_length << ", sequence_number_length:" << header.public_header.sequence_number_length << ", reset_flag: " << header.public_header.reset_flag @@ -210,10 +242,10 @@ ostream& operator<<(ostream& os, const QuicPacketHeader& header) { } ReceivedPacketInfo::ReceivedPacketInfo() - : largest_observed(0), + : entropy_hash(0), + largest_observed(0), delta_time_largest_observed(QuicTime::Delta::Infinite()), - is_truncated(false) { -} + is_truncated(false) {} ReceivedPacketInfo::~ReceivedPacketInfo() {} @@ -231,21 +263,122 @@ void InsertMissingPacketsBetween(ReceivedPacketInfo* received_info, } } -SentPacketInfo::SentPacketInfo() {} +QuicStopWaitingFrame::QuicStopWaitingFrame() + : entropy_hash(0), + least_unacked(0) { +} + +QuicStopWaitingFrame::~QuicStopWaitingFrame() {} + +QuicAckFrame::QuicAckFrame() {} + +CongestionFeedbackMessageTCP::CongestionFeedbackMessageTCP() + : receive_window(0) { +} + +CongestionFeedbackMessageInterArrival::CongestionFeedbackMessageInterArrival() { +} + +CongestionFeedbackMessageInterArrival:: + ~CongestionFeedbackMessageInterArrival() {} + +QuicCongestionFeedbackFrame::QuicCongestionFeedbackFrame() : type(kTCP) {} + +QuicCongestionFeedbackFrame::~QuicCongestionFeedbackFrame() {} -SentPacketInfo::~SentPacketInfo() {} +QuicRstStreamErrorCode AdjustErrorForVersion( + QuicRstStreamErrorCode error_code, + QuicVersion version) { + switch (error_code) { + case QUIC_RST_FLOW_CONTROL_ACCOUNTING: + if (version <= QUIC_VERSION_17) { + return QUIC_STREAM_NO_ERROR; + } + break; + default: + return error_code; + } + return error_code; +} + +QuicRstStreamFrame::QuicRstStreamFrame() + : stream_id(0), + error_code(QUIC_STREAM_NO_ERROR) { +} + +QuicRstStreamFrame::QuicRstStreamFrame(QuicStreamId stream_id, + QuicRstStreamErrorCode error_code, + QuicStreamOffset bytes_written) + : stream_id(stream_id), + error_code(error_code), + byte_offset(bytes_written) { + DCHECK_LE(error_code, numeric_limits<uint8>::max()); +} + +QuicConnectionCloseFrame::QuicConnectionCloseFrame() + : error_code(QUIC_NO_ERROR) { +} + +QuicFrame::QuicFrame() {} + +QuicFrame::QuicFrame(QuicPaddingFrame* padding_frame) + : type(PADDING_FRAME), + padding_frame(padding_frame) { +} + +QuicFrame::QuicFrame(QuicStreamFrame* stream_frame) + : type(STREAM_FRAME), + stream_frame(stream_frame) { +} -// Testing convenience method. -QuicAckFrame::QuicAckFrame(QuicPacketSequenceNumber largest_observed, - QuicTime largest_observed_receive_time, - QuicPacketSequenceNumber least_unacked) { - received_info.largest_observed = largest_observed; - received_info.entropy_hash = 0; - sent_info.least_unacked = least_unacked; - sent_info.entropy_hash = 0; +QuicFrame::QuicFrame(QuicAckFrame* frame) + : type(ACK_FRAME), + ack_frame(frame) { } -ostream& operator<<(ostream& os, const SentPacketInfo& sent_info) { +QuicFrame::QuicFrame(QuicCongestionFeedbackFrame* frame) + : type(CONGESTION_FEEDBACK_FRAME), + congestion_feedback_frame(frame) { +} + +QuicFrame::QuicFrame(QuicStopWaitingFrame* frame) + : type(STOP_WAITING_FRAME), + stop_waiting_frame(frame) { +} + +QuicFrame::QuicFrame(QuicPingFrame* frame) + : type(PING_FRAME), + ping_frame(frame) { +} + +QuicFrame::QuicFrame(QuicRstStreamFrame* frame) + : type(RST_STREAM_FRAME), + rst_stream_frame(frame) { +} + +QuicFrame::QuicFrame(QuicConnectionCloseFrame* frame) + : type(CONNECTION_CLOSE_FRAME), + connection_close_frame(frame) { +} + +QuicFrame::QuicFrame(QuicGoAwayFrame* frame) + : type(GOAWAY_FRAME), + goaway_frame(frame) { +} + +QuicFrame::QuicFrame(QuicWindowUpdateFrame* frame) + : type(WINDOW_UPDATE_FRAME), + window_update_frame(frame) { +} + +QuicFrame::QuicFrame(QuicBlockedFrame* frame) + : type(BLOCKED_FRAME), + blocked_frame(frame) { +} + +QuicFecData::QuicFecData() : fec_group(0) {} + +ostream& operator<<(ostream& os, const QuicStopWaitingFrame& sent_info) { os << "entropy_hash: " << static_cast<int>(sent_info.entropy_hash) << " least_unacked: " << sent_info.least_unacked; return os; @@ -253,21 +386,125 @@ ostream& operator<<(ostream& os, const SentPacketInfo& sent_info) { ostream& operator<<(ostream& os, const ReceivedPacketInfo& received_info) { os << "entropy_hash: " << static_cast<int>(received_info.entropy_hash) + << " is_truncated: " << received_info.is_truncated << " largest_observed: " << received_info.largest_observed + << " delta_time_largest_observed: " + << received_info.delta_time_largest_observed.ToMicroseconds() << " missing_packets: [ "; for (SequenceNumberSet::const_iterator it = received_info.missing_packets.begin(); it != received_info.missing_packets.end(); ++it) { os << *it << " "; } - os << " ] "; + os << " ] revived_packets: [ "; + for (SequenceNumberSet::const_iterator it = + received_info.revived_packets.begin(); + it != received_info.revived_packets.end(); ++it) { + os << *it << " "; + } + os << " ]"; + return os; +} + +ostream& operator<<(ostream& os, const QuicFrame& frame) { + switch (frame.type) { + case PADDING_FRAME: { + os << "type { PADDING_FRAME } "; + break; + } + case RST_STREAM_FRAME: { + os << "type { " << RST_STREAM_FRAME << " } " << *(frame.rst_stream_frame); + break; + } + case CONNECTION_CLOSE_FRAME: { + os << "type { CONNECTION_CLOSE_FRAME } " + << *(frame.connection_close_frame); + break; + } + case GOAWAY_FRAME: { + os << "type { GOAWAY_FRAME } " << *(frame.goaway_frame); + break; + } + case WINDOW_UPDATE_FRAME: { + os << "type { WINDOW_UPDATE_FRAME } " << *(frame.window_update_frame); + break; + } + case BLOCKED_FRAME: { + os << "type { BLOCKED_FRAME } " << *(frame.blocked_frame); + break; + } + case STREAM_FRAME: { + os << "type { STREAM_FRAME } " << *(frame.stream_frame); + break; + } + case ACK_FRAME: { + os << "type { ACK_FRAME } " << *(frame.ack_frame); + break; + } + case CONGESTION_FEEDBACK_FRAME: { + os << "type { CONGESTION_FEEDBACK_FRAME } " + << *(frame.congestion_feedback_frame); + break; + } + case STOP_WAITING_FRAME: { + os << "type { STOP_WAITING_FRAME } " << *(frame.stop_waiting_frame); + break; + } + default: { + LOG(ERROR) << "Unknown frame type: " << frame.type; + break; + } + } return os; } -QuicCongestionFeedbackFrame::QuicCongestionFeedbackFrame() { +ostream& operator<<(ostream& os, const QuicRstStreamFrame& rst_frame) { + os << "stream_id { " << rst_frame.stream_id << " } " + << "error_code { " << rst_frame.error_code << " } " + << "error_details { " << rst_frame.error_details << " }\n"; + return os; } -QuicCongestionFeedbackFrame::~QuicCongestionFeedbackFrame() { +ostream& operator<<(ostream& os, + const QuicConnectionCloseFrame& connection_close_frame) { + os << "error_code { " << connection_close_frame.error_code << " } " + << "error_details { " << connection_close_frame.error_details << " }\n"; + return os; +} + +ostream& operator<<(ostream& os, const QuicGoAwayFrame& goaway_frame) { + os << "error_code { " << goaway_frame.error_code << " } " + << "last_good_stream_id { " << goaway_frame.last_good_stream_id << " } " + << "reason_phrase { " << goaway_frame.reason_phrase << " }\n"; + return os; +} + +ostream& operator<<(ostream& os, + const QuicWindowUpdateFrame& window_update_frame) { + os << "stream_id { " << window_update_frame.stream_id << " } " + << "byte_offset { " << window_update_frame.byte_offset << " }\n"; + return os; +} + +ostream& operator<<(ostream& os, const QuicBlockedFrame& blocked_frame) { + os << "stream_id { " << blocked_frame.stream_id << " }\n"; + return os; +} + +ostream& operator<<(ostream& os, const QuicStreamFrame& stream_frame) { + os << "stream_id { " << stream_frame.stream_id << " } " + << "fin { " << stream_frame.fin << " } " + << "offset { " << stream_frame.offset << " } " + << "data { " + << QuicUtils::StringToHexASCIIDump(*(stream_frame.GetDataAsString())) + << " }\n"; + return os; +} + +ostream& operator<<(ostream& os, const QuicAckFrame& ack_frame) { + os << "sent info { " << ack_frame.sent_info << " } " + << "received info { " << ack_frame.received_info << " }\n"; + return os; } ostream& operator<<(ostream& os, @@ -277,8 +514,6 @@ ostream& operator<<(ostream& os, case kInterArrival: { const CongestionFeedbackMessageInterArrival& inter_arrival = congestion_frame.inter_arrival; - os << " accumulated_number_of_lost_packets: " - << inter_arrival.accumulated_number_of_lost_packets; os << " received packets: [ "; for (TimeMap::const_iterator it = inter_arrival.received_packet_times.begin(); @@ -295,30 +530,25 @@ ostream& operator<<(ostream& os, } case kTCP: { const CongestionFeedbackMessageTCP& tcp = congestion_frame.tcp; - os << " accumulated_number_of_lost_packets: " - << congestion_frame.tcp.accumulated_number_of_lost_packets; os << " receive_window: " << tcp.receive_window; break; } + case kTCPBBR: { + LOG(DFATAL) << "TCPBBR is not yet supported."; + break; + } } return os; } -ostream& operator<<(ostream& os, const QuicAckFrame& ack_frame) { - os << "sent info { " << ack_frame.sent_info << " } " - << "received info { " << ack_frame.received_info << " }\n"; - return os; -} - CongestionFeedbackMessageFixRate::CongestionFeedbackMessageFixRate() : bitrate(QuicBandwidth::Zero()) { } -CongestionFeedbackMessageInterArrival:: -CongestionFeedbackMessageInterArrival() {} - -CongestionFeedbackMessageInterArrival:: -~CongestionFeedbackMessageInterArrival() {} +QuicGoAwayFrame::QuicGoAwayFrame() + : error_code(QUIC_NO_ERROR), + last_good_stream_id(0) { +} QuicGoAwayFrame::QuicGoAwayFrame(QuicErrorCode error_code, QuicStreamId last_good_stream_id, @@ -329,7 +559,20 @@ QuicGoAwayFrame::QuicGoAwayFrame(QuicErrorCode error_code, DCHECK_LE(error_code, numeric_limits<uint8>::max()); } -QuicFecData::QuicFecData() {} +QuicData::QuicData(const char* buffer, + size_t length) + : buffer_(buffer), + length_(length), + owns_buffer_(false) { +} + +QuicData::QuicData(char* buffer, + size_t length, + bool owns_buffer) + : buffer_(buffer), + length_(length), + owns_buffer_(owns_buffer) { +} QuicData::~QuicData() { if (owns_buffer_) { @@ -337,9 +580,43 @@ QuicData::~QuicData() { } } +QuicWindowUpdateFrame::QuicWindowUpdateFrame(QuicStreamId stream_id, + QuicStreamOffset byte_offset) + : stream_id(stream_id), + byte_offset(byte_offset) {} + +QuicBlockedFrame::QuicBlockedFrame(QuicStreamId stream_id) + : stream_id(stream_id) {} + +QuicPacket::QuicPacket(char* buffer, + size_t length, + bool owns_buffer, + QuicConnectionIdLength connection_id_length, + bool includes_version, + QuicSequenceNumberLength sequence_number_length, + bool is_fec_packet) + : QuicData(buffer, length, owns_buffer), + buffer_(buffer), + is_fec_packet_(is_fec_packet), + connection_id_length_(connection_id_length), + includes_version_(includes_version), + sequence_number_length_(sequence_number_length) { +} + +QuicEncryptedPacket::QuicEncryptedPacket(const char* buffer, + size_t length) + : QuicData(buffer, length) { +} + +QuicEncryptedPacket::QuicEncryptedPacket(char* buffer, + size_t length, + bool owns_buffer) + : QuicData(buffer, length, owns_buffer) { +} + StringPiece QuicPacket::FecProtectedData() const { const size_t start_of_fec = GetStartOfFecProtectedData( - guid_length_, includes_version_, sequence_number_length_); + connection_id_length_, includes_version_, sequence_number_length_); return StringPiece(data() + start_of_fec, length() - start_of_fec); } @@ -347,12 +624,12 @@ StringPiece QuicPacket::AssociatedData() const { return StringPiece( data() + kStartOfHashData, GetStartOfEncryptedData( - guid_length_, includes_version_, sequence_number_length_) - + connection_id_length_, includes_version_, sequence_number_length_) - kStartOfHashData); } StringPiece QuicPacket::BeforePlaintext() const { - return StringPiece(data(), GetStartOfEncryptedData(guid_length_, + return StringPiece(data(), GetStartOfEncryptedData(connection_id_length_, includes_version_, sequence_number_length_)); } @@ -360,7 +637,7 @@ StringPiece QuicPacket::BeforePlaintext() const { StringPiece QuicPacket::Plaintext() const { const size_t start_of_encrypted_data = GetStartOfEncryptedData( - guid_length_, includes_version_, sequence_number_length_); + connection_id_length_, includes_version_, sequence_number_length_); return StringPiece(data() + start_of_encrypted_data, length() - start_of_encrypted_data); } @@ -384,6 +661,12 @@ RetransmittableFrames::~RetransmittableFrames() { case CONGESTION_FEEDBACK_FRAME: delete it->congestion_feedback_frame; break; + case STOP_WAITING_FRAME: + delete it->stop_waiting_frame; + break; + case PING_FRAME: + delete it->ping_frame; + break; case RST_STREAM_FRAME: delete it->rst_stream_frame; break; @@ -393,6 +676,12 @@ RetransmittableFrames::~RetransmittableFrames() { case GOAWAY_FRAME: delete it->goaway_frame; break; + case WINDOW_UPDATE_FRAME: + delete it->window_update_frame; + break; + case BLOCKED_FRAME: + delete it->blocked_frame; + break; case NUM_FRAME_TYPES: DCHECK(false) << "Cannot delete type: " << it->type; } @@ -419,6 +708,16 @@ const QuicFrame& RetransmittableFrames::AddNonStreamFrame( return frames_.back(); } +IsHandshake RetransmittableFrames::HasCryptoHandshake() const { + for (size_t i = 0; i < frames().size(); ++i) { + if (frames()[i].type == STREAM_FRAME && + frames()[i].stream_frame->stream_id == kCryptoStreamId) { + return IS_HANDSHAKE; + } + } + return NOT_HANDSHAKE; +} + void RetransmittableFrames::set_encryption_level(EncryptionLevel level) { encryption_level_ = level; } @@ -449,10 +748,46 @@ ostream& operator<<(ostream& os, const QuicEncryptedPacket& s) { return os; } -ostream& operator<<(ostream& os, const QuicConsumedData& s) { - os << "bytes_consumed: " << s.bytes_consumed - << " fin_consumed: " << s.fin_consumed; - return os; +TransmissionInfo::TransmissionInfo() + : retransmittable_frames(NULL), + sequence_number_length(PACKET_1BYTE_SEQUENCE_NUMBER), + sent_time(QuicTime::Zero()), + bytes_sent(0), + nack_count(0), + transmission_type(NOT_RETRANSMISSION), + all_transmissions(NULL), + in_flight(false) {} + +TransmissionInfo::TransmissionInfo( + RetransmittableFrames* retransmittable_frames, + QuicPacketSequenceNumber sequence_number, + QuicSequenceNumberLength sequence_number_length) + : retransmittable_frames(retransmittable_frames), + sequence_number_length(sequence_number_length), + sent_time(QuicTime::Zero()), + bytes_sent(0), + nack_count(0), + transmission_type(NOT_RETRANSMISSION), + all_transmissions(new SequenceNumberSet), + in_flight(false) { + all_transmissions->insert(sequence_number); +} + +TransmissionInfo::TransmissionInfo( + RetransmittableFrames* retransmittable_frames, + QuicPacketSequenceNumber sequence_number, + QuicSequenceNumberLength sequence_number_length, + TransmissionType transmission_type, + SequenceNumberSet* all_transmissions) + : retransmittable_frames(retransmittable_frames), + sequence_number_length(sequence_number_length), + sent_time(QuicTime::Zero()), + bytes_sent(0), + nack_count(0), + transmission_type(transmission_type), + all_transmissions(all_transmissions), + in_flight(false) { + all_transmissions->insert(sequence_number); } } // namespace net diff --git a/chromium/net/quic/quic_protocol.h b/chromium/net/quic/quic_protocol.h index e97e2fc77aa..e84d338f4ad 100644 --- a/chromium/net/quic/quic_protocol.h +++ b/chromium/net/quic/quic_protocol.h @@ -19,6 +19,7 @@ #include "base/logging.h" #include "base/strings/string_piece.h" #include "net/base/int128.h" +#include "net/base/ip_endpoint.h" #include "net/base/net_export.h" #include "net/quic/iovector.h" #include "net/quic/quic_bandwidth.h" @@ -32,7 +33,7 @@ class QuicAckNotifier; class QuicPacket; struct QuicPacketHeader; -typedef uint64 QuicGuid; +typedef uint64 QuicConnectionId; typedef uint32 QuicStreamId; typedef uint64 QuicStreamOffset; typedef uint64 QuicPacketSequenceNumber; @@ -43,6 +44,9 @@ typedef uint32 QuicHeaderId; // QuicTag is the type of a tag in the wire protocol. typedef uint32 QuicTag; typedef std::vector<QuicTag> QuicTagVector; +typedef std::map<QuicTag, std::string> QuicTagValueMap; +// TODO(rtenneti): Didn't use SpdyPriority because SpdyPriority is uint8 and +// QuicPriority is uint32. Use SpdyPriority when we change the QUIC_VERSION. typedef uint32 QuicPriority; // TODO(rch): Consider Quic specific names for these constants. @@ -53,19 +57,22 @@ const QuicByteCount kDefaultMaxPacketSize = 1200; // additional 8 bytes. This is a total overhead of 48 bytes. Ethernet's // max packet size is 1500 bytes, 1500 - 48 = 1452. const QuicByteCount kMaxPacketSize = 1452; +// Default maximum packet size used in Linux TCP implementations. +const QuicByteCount kDefaultTCPMSS = 1460; // Maximum size of the initial congestion window in packets. const size_t kDefaultInitialWindow = 10; -// TODO(ianswett): Temporarily changed to 10 due to a large number of clients -// mistakenly negotiating 100 initially and suffering the consequences. -const size_t kMaxInitialWindow = 10; +const uint32 kMaxInitialWindow = 100; + +// Default size of initial flow control window, for both stream and session. +const uint32 kDefaultFlowControlSendWindow = 16 * 1024; // 16 KB // Maximum size of the congestion window, in packets, for TCP congestion control // algorithms. const size_t kMaxTcpCongestionWindow = 200; // Don't allow a client to suggest an RTT longer than 15 seconds. -const size_t kMaxInitialRoundTripTimeUs = 15 * kNumMicrosPerSecond; +const uint32 kMaxInitialRoundTripTimeUs = 15 * kNumMicrosPerSecond; // Maximum number of open streams per connection. const size_t kDefaultMaxStreamsPerConnection = 100; @@ -78,8 +85,6 @@ const size_t kQuicVersionSize = 4; const size_t kPrivateFlagsSize = 1; // Number of bytes reserved for FEC group in the packet header. const size_t kFecGroupSize = 1; -// Number of bytes reserved for the nonce proof in public reset packet. -const size_t kPublicResetNonceSize = 8; // Signifies that the QuicPacket will contain version of the protocol. const bool kIncludeVersion = true; @@ -88,20 +93,25 @@ const bool kIncludeVersion = true; const size_t kStartOfHashData = 0; // Limit on the delta between stream IDs. -const QuicStreamId kMaxStreamIdDelta = 100; +const QuicStreamId kMaxStreamIdDelta = 200; // Limit on the delta between header IDs. -const QuicHeaderId kMaxHeaderIdDelta = 100; +const QuicHeaderId kMaxHeaderIdDelta = 200; // Reserved ID for the crypto stream. -// TODO(rch): ensure that this is not usable by any other streams. const QuicStreamId kCryptoStreamId = 1; +// Reserved ID for the headers stream. +const QuicStreamId kHeadersStreamId = 3; + // This is the default network timeout a for connection till the crypto // handshake succeeds and the negotiated timeout from the handshake is received. const int64 kDefaultInitialTimeoutSecs = 120; // 2 mins. const int64 kDefaultTimeoutSecs = 60 * 10; // 10 minutes. const int64 kDefaultMaxTimeForCryptoHandshakeSecs = 5; // 5 secs. +// Default ping timeout. +const int64 kPingTimeoutSecs = 15; // 15 secs. + // We define an unsigned 16-bit floating point value, inspired by IEEE floats // (http://en.wikipedia.org/wiki/Half_precision_floating-point_format), // with 5-bit exponent (bias 1), 11-bit mantissa (effective 12 with hidden @@ -121,8 +131,13 @@ const uint64 kUFloat16MaxValue = // 0x3FFC0000000 enum TransmissionType { NOT_RETRANSMISSION, - NACK_RETRANSMISSION, - RTO_RETRANSMISSION, + FIRST_TRANSMISSION_TYPE = NOT_RETRANSMISSION, + HANDSHAKE_RETRANSMISSION, // Retransmits due to handshake timeouts. + ALL_UNACKED_RETRANSMISSION, // Retransmits of all unacked packets. + LOSS_RETRANSMISSION, // Retransmits due to loss detection. + RTO_RETRANSMISSION, // Retransmits due to retransmit time out. + TLP_RETRANSMISSION, // Tail loss probes. + LAST_TRANSMISSION_TYPE = TLP_RETRANSMISSION, }; enum RetransmissionType { @@ -140,22 +155,43 @@ enum IsHandshake { IS_HANDSHAKE }; +// Indicates FEC protection level for data being written. +enum FecProtection { + MUST_FEC_PROTECT, // Callee must FEC protect this data. + MAY_FEC_PROTECT // Callee does not have to but may FEC protect this data. +}; + +// Indicates FEC policy. +enum FecPolicy { + FEC_PROTECT_ALWAYS, // All data in the stream should be FEC protected. + FEC_PROTECT_OPTIONAL // Data in the stream does not need FEC protection. +}; + enum QuicFrameType { + // Regular frame types. The values set here cannot change without the + // introduction of a new QUIC version. PADDING_FRAME = 0, - RST_STREAM_FRAME, - CONNECTION_CLOSE_FRAME, - GOAWAY_FRAME, + RST_STREAM_FRAME = 1, + CONNECTION_CLOSE_FRAME = 2, + GOAWAY_FRAME = 3, + WINDOW_UPDATE_FRAME = 4, + BLOCKED_FRAME = 5, + STOP_WAITING_FRAME = 6, + PING_FRAME = 7, + + // STREAM, ACK, and CONGESTION_FEEDBACK frames are special frames. They are + // encoded differently on the wire and their values do not need to be stable. STREAM_FRAME, ACK_FRAME, CONGESTION_FEEDBACK_FRAME, NUM_FRAME_TYPES }; -enum QuicGuidLength { - PACKET_0BYTE_GUID = 0, - PACKET_1BYTE_GUID = 1, - PACKET_4BYTE_GUID = 4, - PACKET_8BYTE_GUID = 8 +enum QuicConnectionIdLength { + PACKET_0BYTE_CONNECTION_ID = 0, + PACKET_1BYTE_CONNECTION_ID = 1, + PACKET_4BYTE_CONNECTION_ID = 4, + PACKET_8BYTE_CONNECTION_ID = 8 }; enum InFecGroup { @@ -188,15 +224,15 @@ enum QuicPacketPublicFlags { // Bit 1: Is this packet a public reset packet? PACKET_PUBLIC_FLAGS_RST = 1 << 1, - // Bits 2 and 3 specify the length of the GUID as follows: + // Bits 2 and 3 specify the length of the ConnectionId as follows: // ----00--: 0 bytes // ----01--: 1 byte // ----10--: 4 bytes // ----11--: 8 bytes - PACKET_PUBLIC_FLAGS_0BYTE_GUID = 0, - PACKET_PUBLIC_FLAGS_1BYTE_GUID = 1 << 2, - PACKET_PUBLIC_FLAGS_4BYTE_GUID = 1 << 3, - PACKET_PUBLIC_FLAGS_8BYTE_GUID = 1 << 3 | 1 << 2, + PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID = 0, + PACKET_PUBLIC_FLAGS_1BYTE_CONNECTION_ID = 1 << 2, + PACKET_PUBLIC_FLAGS_4BYTE_CONNECTION_ID = 1 << 3, + PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID = 1 << 3 | 1 << 2, // Bits 4 and 5 describe the packet sequence number length as follows: // --00----: 1 byte @@ -239,7 +275,12 @@ enum QuicVersion { // Special case to indicate unknown/unsupported QUIC version. QUIC_VERSION_UNSUPPORTED = 0, - QUIC_VERSION_12 = 12, // Current version. + QUIC_VERSION_15 = 15, + QUIC_VERSION_16 = 16, + QUIC_VERSION_17 = 17, + QUIC_VERSION_18 = 18, + QUIC_VERSION_19 = 19, + QUIC_VERSION_20 = 20, // Current version. }; // This vector contains QUIC versions which we currently support. @@ -249,7 +290,12 @@ enum QuicVersion { // // IMPORTANT: if you are addding to this list, follow the instructions at // http://sites/quic/adding-and-removing-versions -static const QuicVersion kSupportedQuicVersions[] = {QUIC_VERSION_12}; +static const QuicVersion kSupportedQuicVersions[] = {QUIC_VERSION_20, + QUIC_VERSION_19, + QUIC_VERSION_18, + QUIC_VERSION_17, + QUIC_VERSION_16, + QUIC_VERSION_15}; typedef std::vector<QuicVersion> QuicVersionVector; @@ -286,26 +332,26 @@ NET_EXPORT_PRIVATE std::string QuicVersionVectorToString( // MakeQuicTag('C', 'H', 'L', 'O'); NET_EXPORT_PRIVATE QuicTag MakeQuicTag(char a, char b, char c, char d); +// Returns true if the tag vector contains the specified tag. +bool ContainsQuicTag(const QuicTagVector& tag_vector, QuicTag tag); + // Size in bytes of the data or fec packet header. -NET_EXPORT_PRIVATE size_t GetPacketHeaderSize(QuicPacketHeader header); +NET_EXPORT_PRIVATE size_t GetPacketHeaderSize(const QuicPacketHeader& header); NET_EXPORT_PRIVATE size_t GetPacketHeaderSize( - QuicGuidLength guid_length, + QuicConnectionIdLength connection_id_length, bool include_version, QuicSequenceNumberLength sequence_number_length, InFecGroup is_in_fec_group); -// Size in bytes of the public reset packet. -NET_EXPORT_PRIVATE size_t GetPublicResetPacketSize(); - // Index of the first byte in a QUIC packet of FEC protected data. NET_EXPORT_PRIVATE size_t GetStartOfFecProtectedData( - QuicGuidLength guid_length, + QuicConnectionIdLength connection_id_length, bool include_version, QuicSequenceNumberLength sequence_number_length); // Index of the first byte in a QUIC packet of encrypted data. NET_EXPORT_PRIVATE size_t GetStartOfEncryptedData( - QuicGuidLength guid_length, + QuicConnectionIdLength connection_id_length, bool include_version, QuicSequenceNumberLength sequence_number_length); @@ -325,11 +371,20 @@ enum QuicRstStreamErrorCode { QUIC_STREAM_PEER_GOING_AWAY, // The stream has been cancelled. QUIC_STREAM_CANCELLED, + // Sending a RST to allow for proper flow control accounting. + QUIC_RST_FLOW_CONTROL_ACCOUNTING, // No error. Used as bound while iterating. QUIC_STREAM_LAST_ERROR, }; +// Because receiving an unknown QuicRstStreamErrorCode results in connection +// teardown, we use this to make sure any errors predating a given version are +// downgraded to the most appropriate existing error. +NET_EXPORT_PRIVATE QuicRstStreamErrorCode AdjustErrorForVersion( + QuicRstStreamErrorCode error_code, + QuicVersion version); + // These values must remain stable as they are uploaded to UMA histograms. // To add a new error code, use the current value of QUIC_LAST_ERROR and // increment QUIC_LAST_ERROR. @@ -350,12 +405,20 @@ enum QuicErrorCode { QUIC_INVALID_FEC_DATA = 5, // STREAM frame data is malformed. QUIC_INVALID_STREAM_DATA = 46, + // STREAM frame data is not encrypted. + QUIC_UNENCRYPTED_STREAM_DATA = 61, // RST_STREAM frame data is malformed. QUIC_INVALID_RST_STREAM_DATA = 6, // CONNECTION_CLOSE frame data is malformed. QUIC_INVALID_CONNECTION_CLOSE_DATA = 7, // GOAWAY frame data is malformed. QUIC_INVALID_GOAWAY_DATA = 8, + // WINDOW_UPDATE frame data is malformed. + QUIC_INVALID_WINDOW_UPDATE_DATA = 57, + // BLOCKED frame data is malformed. + QUIC_INVALID_BLOCKED_DATA = 58, + // STOP_WAITING frame data is malformed. + QUIC_INVALID_STOP_WAITING_DATA = 60, // ACK frame data is malformed. QUIC_INVALID_ACK_DATA = 9, // CONGESTION_FEEDBACK frame data is malformed. @@ -384,8 +447,9 @@ enum QuicErrorCode { QUIC_PUBLIC_RESET = 19, // Invalid protocol version. QUIC_INVALID_VERSION = 20, - // Stream reset before headers decompressed. - QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED = 21, + + // deprecated: QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED = 21 + // The Header ID for a stream was too far from the previous. QUIC_INVALID_HEADER_ID = 22, // Negotiable parameter received during handshake had invalid value. @@ -402,7 +466,16 @@ enum QuicErrorCode { QUIC_PACKET_READ_ERROR = 51, // We received a STREAM_FRAME with no data and no fin flag set. QUIC_INVALID_STREAM_FRAME = 50, - + // We received invalid data on the headers stream. + QUIC_INVALID_HEADERS_STREAM_DATA = 56, + // The peer received too much data, violating flow control. + QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA = 59, + // The peer sent too much data, violating flow control. + QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA = 63, + // The peer received an invalid flow control window. + QUIC_FLOW_CONTROL_INVALID_WINDOW = 64, + // The connection has been IP pooled into an existing connection. + QUIC_CONNECTION_IP_POOLED = 62, // Crypto errors. @@ -458,7 +531,7 @@ enum QuicErrorCode { QUIC_VERSION_NEGOTIATION_MISMATCH = 55, // No error. Used as bound while iterating. - QUIC_LAST_ERROR = 56, + QUIC_LAST_ERROR = 65, }; struct NET_EXPORT_PRIVATE QuicPacketPublicHeader { @@ -466,9 +539,10 @@ struct NET_EXPORT_PRIVATE QuicPacketPublicHeader { explicit QuicPacketPublicHeader(const QuicPacketPublicHeader& other); ~QuicPacketPublicHeader(); - // Universal header. All QuicPacket headers will have a guid and public flags. - QuicGuid guid; - QuicGuidLength guid_length; + // Universal header. All QuicPacket headers will have a connection_id and + // public flags. + QuicConnectionId connection_id; + QuicConnectionIdLength connection_id_length; bool reset_flag; bool version_flag; QuicSequenceNumberLength sequence_number_length; @@ -493,12 +567,13 @@ struct NET_EXPORT_PRIVATE QuicPacketHeader { }; struct NET_EXPORT_PRIVATE QuicPublicResetPacket { - QuicPublicResetPacket() {} - explicit QuicPublicResetPacket(const QuicPacketPublicHeader& header) - : public_header(header) {} + QuicPublicResetPacket(); + explicit QuicPublicResetPacket(const QuicPacketPublicHeader& header); + QuicPacketPublicHeader public_header; - QuicPacketSequenceNumber rejected_sequence_number; QuicPublicResetNonceProof nonce_proof; + QuicPacketSequenceNumber rejected_sequence_number; + IPEndPoint client_address; }; enum QuicVersionNegotiationState { @@ -521,6 +596,11 @@ typedef QuicPacketPublicHeader QuicVersionNegotiationPacket; struct NET_EXPORT_PRIVATE QuicPaddingFrame { }; +// A ping frame contains no payload, though it is retransmittable, +// and ACK'd just like other normal frames. +struct NET_EXPORT_PRIVATE QuicPingFrame { +}; + struct NET_EXPORT_PRIVATE QuicStreamFrame { QuicStreamFrame(); QuicStreamFrame(const QuicStreamFrame& frame); @@ -529,6 +609,9 @@ struct NET_EXPORT_PRIVATE QuicStreamFrame { QuicStreamOffset offset, IOVector data); + NET_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, const QuicStreamFrame& s); + // Returns a copy of the IOVector |data| as a heap-allocated string. // Caller must take ownership of the returned string. std::string* GetDataAsString() const; @@ -552,6 +635,7 @@ typedef std::map<QuicPacketSequenceNumber, QuicTime> TimeMap; struct NET_EXPORT_PRIVATE ReceivedPacketInfo { ReceivedPacketInfo(); ~ReceivedPacketInfo(); + NET_EXPORT_PRIVATE friend std::ostream& operator<<( std::ostream& os, const ReceivedPacketInfo& s); @@ -580,6 +664,10 @@ struct NET_EXPORT_PRIVATE ReceivedPacketInfo { // Whether the ack had to be truncated when sent. bool is_truncated; + + // Packets which have been revived via FEC. + // All of these must also be in missing_packets. + SequenceNumberSet revived_packets; }; // True if the sequence number is greater than largest_observed or is listed @@ -595,11 +683,12 @@ void NET_EXPORT_PRIVATE InsertMissingPacketsBetween( QuicPacketSequenceNumber lower, QuicPacketSequenceNumber higher); -struct NET_EXPORT_PRIVATE SentPacketInfo { - SentPacketInfo(); - ~SentPacketInfo(); +struct NET_EXPORT_PRIVATE QuicStopWaitingFrame { + QuicStopWaitingFrame(); + ~QuicStopWaitingFrame(); + NET_EXPORT_PRIVATE friend std::ostream& operator<<( - std::ostream& os, const SentPacketInfo& s); + std::ostream& os, const QuicStopWaitingFrame& s); // Entropy hash of all packets up to, but not including, the least unacked // packet. @@ -609,17 +698,12 @@ struct NET_EXPORT_PRIVATE SentPacketInfo { }; struct NET_EXPORT_PRIVATE QuicAckFrame { - QuicAckFrame() {} - // Testing convenience method to construct a QuicAckFrame with all packets - // from least_unacked to largest_observed acked. - QuicAckFrame(QuicPacketSequenceNumber largest_observed, - QuicTime largest_observed_receive_time, - QuicPacketSequenceNumber least_unacked); + QuicAckFrame(); NET_EXPORT_PRIVATE friend std::ostream& operator<<( std::ostream& os, const QuicAckFrame& s); - SentPacketInfo sent_info; + QuicStopWaitingFrame sent_info; ReceivedPacketInfo received_info; }; @@ -630,17 +714,24 @@ enum CongestionFeedbackType { kTCP, // Used to mimic TCP. kInterArrival, // Use additional inter arrival information. kFixRate, // Provided for testing. + kTCPBBR, // BBR implementation based on TCP congestion feedback. +}; + +enum LossDetectionType { + kNack, // Used to mimic TCP's loss detection. + kTime, // Time based loss detection. }; struct NET_EXPORT_PRIVATE CongestionFeedbackMessageTCP { - uint16 accumulated_number_of_lost_packets; + CongestionFeedbackMessageTCP(); + QuicByteCount receive_window; }; struct NET_EXPORT_PRIVATE CongestionFeedbackMessageInterArrival { CongestionFeedbackMessageInterArrival(); ~CongestionFeedbackMessageInterArrival(); - uint16 accumulated_number_of_lost_packets; + // The set of received packets since the last feedback was sent, along with // their arrival times. TimeMap received_packet_times; @@ -667,33 +758,86 @@ struct NET_EXPORT_PRIVATE QuicCongestionFeedbackFrame { }; struct NET_EXPORT_PRIVATE QuicRstStreamFrame { - QuicRstStreamFrame() {} - QuicRstStreamFrame(QuicStreamId stream_id, QuicRstStreamErrorCode error_code) - : stream_id(stream_id), error_code(error_code) { - DCHECK_LE(error_code, std::numeric_limits<uint8>::max()); - } + QuicRstStreamFrame(); + QuicRstStreamFrame(QuicStreamId stream_id, + QuicRstStreamErrorCode error_code, + QuicStreamOffset bytes_written); + + NET_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, const QuicRstStreamFrame& r); QuicStreamId stream_id; QuicRstStreamErrorCode error_code; std::string error_details; + + // Used to update flow control windows. On termination of a stream, both + // endpoints must inform the peer of the number of bytes they have sent on + // that stream. This can be done through normal termination (data packet with + // FIN) or through a RST. + QuicStreamOffset byte_offset; }; struct NET_EXPORT_PRIVATE QuicConnectionCloseFrame { + QuicConnectionCloseFrame(); + + NET_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, const QuicConnectionCloseFrame& c); + QuicErrorCode error_code; std::string error_details; }; struct NET_EXPORT_PRIVATE QuicGoAwayFrame { - QuicGoAwayFrame() {} + QuicGoAwayFrame(); QuicGoAwayFrame(QuicErrorCode error_code, QuicStreamId last_good_stream_id, const std::string& reason); + NET_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, const QuicGoAwayFrame& g); + QuicErrorCode error_code; QuicStreamId last_good_stream_id; std::string reason_phrase; }; +// Flow control updates per-stream and at the connection levoel. +// Based on SPDY's WINDOW_UPDATE frame, but uses an absolute byte offset rather +// than a window delta. +// TODO(rjshade): A possible future optimization is to make stream_id and +// byte_offset variable length, similar to stream frames. +struct NET_EXPORT_PRIVATE QuicWindowUpdateFrame { + QuicWindowUpdateFrame() {} + QuicWindowUpdateFrame(QuicStreamId stream_id, QuicStreamOffset byte_offset); + + NET_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, const QuicWindowUpdateFrame& w); + + // The stream this frame applies to. 0 is a special case meaning the overall + // connection rather than a specific stream. + QuicStreamId stream_id; + + // Byte offset in the stream or connection. The receiver of this frame must + // not send data which would result in this offset being exceeded. + QuicStreamOffset byte_offset; +}; + +// The BLOCKED frame is used to indicate to the remote endpoint that this +// endpoint believes itself to be flow-control blocked but otherwise ready to +// send data. The BLOCKED frame is purely advisory and optional. +// Based on SPDY's BLOCKED frame (undocumented as of 2014-01-28). +struct NET_EXPORT_PRIVATE QuicBlockedFrame { + QuicBlockedFrame() {} + explicit QuicBlockedFrame(QuicStreamId stream_id); + + NET_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, const QuicBlockedFrame& b); + + // The stream this frame applies to. 0 is a special case meaning the overall + // connection rather than a specific stream. + QuicStreamId stream_id; +}; + // EncryptionLevel enumerates the stages of encryption that a QUIC connection // progresses through. When retransmitting a packet, the encryption level needs // to be specified so that it is retransmitted at a level which the peer can @@ -707,35 +851,21 @@ enum EncryptionLevel { }; struct NET_EXPORT_PRIVATE QuicFrame { - QuicFrame() {} - explicit QuicFrame(QuicPaddingFrame* padding_frame) - : type(PADDING_FRAME), - padding_frame(padding_frame) { - } - explicit QuicFrame(QuicStreamFrame* stream_frame) - : type(STREAM_FRAME), - stream_frame(stream_frame) { - } - explicit QuicFrame(QuicAckFrame* frame) - : type(ACK_FRAME), - ack_frame(frame) { - } - explicit QuicFrame(QuicCongestionFeedbackFrame* frame) - : type(CONGESTION_FEEDBACK_FRAME), - congestion_feedback_frame(frame) { - } - explicit QuicFrame(QuicRstStreamFrame* frame) - : type(RST_STREAM_FRAME), - rst_stream_frame(frame) { - } - explicit QuicFrame(QuicConnectionCloseFrame* frame) - : type(CONNECTION_CLOSE_FRAME), - connection_close_frame(frame) { - } - explicit QuicFrame(QuicGoAwayFrame* frame) - : type(GOAWAY_FRAME), - goaway_frame(frame) { - } + QuicFrame(); + explicit QuicFrame(QuicPaddingFrame* padding_frame); + explicit QuicFrame(QuicStreamFrame* stream_frame); + explicit QuicFrame(QuicAckFrame* frame); + explicit QuicFrame(QuicCongestionFeedbackFrame* frame); + explicit QuicFrame(QuicRstStreamFrame* frame); + explicit QuicFrame(QuicConnectionCloseFrame* frame); + explicit QuicFrame(QuicStopWaitingFrame* frame); + explicit QuicFrame(QuicPingFrame* frame); + explicit QuicFrame(QuicGoAwayFrame* frame); + explicit QuicFrame(QuicWindowUpdateFrame* frame); + explicit QuicFrame(QuicBlockedFrame* frame); + + NET_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, const QuicFrame& frame); QuicFrameType type; union { @@ -743,9 +873,13 @@ struct NET_EXPORT_PRIVATE QuicFrame { QuicStreamFrame* stream_frame; QuicAckFrame* ack_frame; QuicCongestionFeedbackFrame* congestion_feedback_frame; + QuicStopWaitingFrame* stop_waiting_frame; + QuicPingFrame* ping_frame; QuicRstStreamFrame* rst_stream_frame; QuicConnectionCloseFrame* connection_close_frame; QuicGoAwayFrame* goaway_frame; + QuicWindowUpdateFrame* window_update_frame; + QuicBlockedFrame* blocked_frame; }; }; @@ -763,16 +897,8 @@ struct NET_EXPORT_PRIVATE QuicFecData { class NET_EXPORT_PRIVATE QuicData { public: - QuicData(const char* buffer, size_t length) - : buffer_(buffer), - length_(length), - owns_buffer_(false) {} - - QuicData(char* buffer, size_t length, bool owns_buffer) - : buffer_(buffer), - length_(length), - owns_buffer_(owns_buffer) {} - + QuicData(const char* buffer, size_t length); + QuicData(char* buffer, size_t length, bool owns_buffer); virtual ~QuicData(); base::StringPiece AsStringPiece() const { @@ -796,10 +922,10 @@ class NET_EXPORT_PRIVATE QuicPacket : public QuicData { char* buffer, size_t length, bool owns_buffer, - QuicGuidLength guid_length, + QuicConnectionIdLength connection_id_length, bool includes_version, QuicSequenceNumberLength sequence_number_length) { - return new QuicPacket(buffer, length, owns_buffer, guid_length, + return new QuicPacket(buffer, length, owns_buffer, connection_id_length, includes_version, sequence_number_length, false); } @@ -807,10 +933,10 @@ class NET_EXPORT_PRIVATE QuicPacket : public QuicData { char* buffer, size_t length, bool owns_buffer, - QuicGuidLength guid_length, + QuicConnectionIdLength connection_id_length, bool includes_version, QuicSequenceNumberLength sequence_number_length) { - return new QuicPacket(buffer, length, owns_buffer, guid_length, + return new QuicPacket(buffer, length, owns_buffer, connection_id_length, includes_version, sequence_number_length, true); } @@ -827,20 +953,14 @@ class NET_EXPORT_PRIVATE QuicPacket : public QuicData { QuicPacket(char* buffer, size_t length, bool owns_buffer, - QuicGuidLength guid_length, + QuicConnectionIdLength connection_id_length, bool includes_version, QuicSequenceNumberLength sequence_number_length, - bool is_fec_packet) - : QuicData(buffer, length, owns_buffer), - buffer_(buffer), - is_fec_packet_(is_fec_packet), - guid_length_(guid_length), - includes_version_(includes_version), - sequence_number_length_(sequence_number_length) {} + bool is_fec_packet); char* buffer_; const bool is_fec_packet_; - const QuicGuidLength guid_length_; + const QuicConnectionIdLength connection_id_length_; const bool includes_version_; const QuicSequenceNumberLength sequence_number_length_; @@ -849,11 +969,8 @@ class NET_EXPORT_PRIVATE QuicPacket : public QuicData { class NET_EXPORT_PRIVATE QuicEncryptedPacket : public QuicData { public: - QuicEncryptedPacket(const char* buffer, size_t length) - : QuicData(buffer, length) {} - - QuicEncryptedPacket(char* buffer, size_t length, bool owns_buffer) - : QuicData(buffer, length, owns_buffer) {} + QuicEncryptedPacket(const char* buffer, size_t length); + QuicEncryptedPacket(char* buffer, size_t length, bool owns_buffer); // Clones the packet into a new packet which owns the buffer. QuicEncryptedPacket* Clone() const; @@ -882,6 +999,8 @@ class NET_EXPORT_PRIVATE RetransmittableFrames { const QuicFrame& AddNonStreamFrame(const QuicFrame& frame); const QuicFrames& frames() const { return frames_; } + IsHandshake HasCryptoHandshake() const; + void set_encryption_level(EncryptionLevel level); EncryptionLevel encryption_level() const { return encryption_level_; @@ -914,43 +1033,38 @@ struct NET_EXPORT_PRIVATE SerializedPacket { std::set<QuicAckNotifier*> notifiers; }; -// A struct for functions which consume data payloads and fins. -struct QuicConsumedData { - QuicConsumedData(size_t bytes_consumed, bool fin_consumed) - : bytes_consumed(bytes_consumed), - fin_consumed(fin_consumed) {} - // By default, gtest prints the raw bytes of an object. The bool data - // member causes this object to have padding bytes, which causes the - // default gtest object printer to read uninitialize memory. So we need - // to teach gtest how to print this object. - NET_EXPORT_PRIVATE friend std::ostream& operator<<( - std::ostream& os, const QuicConsumedData& s); - - // How many bytes were consumed. - size_t bytes_consumed; +struct NET_EXPORT_PRIVATE TransmissionInfo { + // Used by STL when assigning into a map. + TransmissionInfo(); - // True if an incoming fin was consumed. - bool fin_consumed; -}; - -enum WriteStatus { - WRITE_STATUS_OK, - WRITE_STATUS_BLOCKED, - WRITE_STATUS_ERROR, -}; + // Constructs a Transmission with a new all_tranmissions set + // containing |sequence_number|. + TransmissionInfo(RetransmittableFrames* retransmittable_frames, + QuicPacketSequenceNumber sequence_number, + QuicSequenceNumberLength sequence_number_length); -// A struct used to return the result of write calls including either the number -// of bytes written or the error code, depending upon the status. -struct NET_EXPORT_PRIVATE WriteResult { - WriteResult(WriteStatus status, int bytes_written_or_error_code) : - status(status), bytes_written(bytes_written_or_error_code) { - } + // Constructs a Transmission with the specified |all_tranmissions| set + // and inserts |sequence_number| into it. + TransmissionInfo(RetransmittableFrames* retransmittable_frames, + QuicPacketSequenceNumber sequence_number, + QuicSequenceNumberLength sequence_number_length, + TransmissionType transmission_type, + SequenceNumberSet* all_transmissions); - WriteStatus status; - union { - int bytes_written; // only valid when status is OK - int error_code; // only valid when status is ERROR - }; + RetransmittableFrames* retransmittable_frames; + QuicSequenceNumberLength sequence_number_length; + // Zero when the packet is serialized, non-zero once it's sent. + QuicTime sent_time; + // Zero when the packet is serialized, non-zero once it's sent. + QuicByteCount bytes_sent; + size_t nack_count; + // Reason why this packet was transmitted. + TransmissionType transmission_type; + // Stores the sequence numbers of all transmissions of this packet. + // Can never be null. + SequenceNumberSet* all_transmissions; + // In flight packets have not been abandoned or lost. + bool in_flight; }; } // namespace net diff --git a/chromium/net/quic/quic_protocol_test.cc b/chromium/net/quic/quic_protocol_test.cc index bf1fece981c..b1eae45d251 100644 --- a/chromium/net/quic/quic_protocol_test.cc +++ b/chromium/net/quic/quic_protocol_test.cc @@ -11,6 +11,19 @@ namespace net { namespace test { namespace { +TEST(QuicProtocolTest, AdjustErrorForVersion) { + ASSERT_EQ(8, QUIC_STREAM_LAST_ERROR) + << "Any additions to QuicRstStreamErrorCode require an addition to " + << "AdjustErrorForVersion and this associated test."; + + EXPECT_EQ(QUIC_STREAM_NO_ERROR, + AdjustErrorForVersion(QUIC_RST_FLOW_CONTROL_ACCOUNTING, + QUIC_VERSION_17)); + EXPECT_EQ(QUIC_RST_FLOW_CONTROL_ACCOUNTING, AdjustErrorForVersion( + QUIC_RST_FLOW_CONTROL_ACCOUNTING, + static_cast<QuicVersion>(QUIC_VERSION_17 + 1))); +} + TEST(QuicProtocolTest, MakeQuicTag) { QuicTag tag = MakeQuicTag('A', 'B', 'C', 'D'); char bytes[4]; @@ -56,8 +69,8 @@ TEST(QuicProtocolTest, QuicVersionToQuicTag) { #endif // Explicitly test a specific version. - EXPECT_EQ(MakeQuicTag('Q', '0', '1', '2'), - QuicVersionToQuicTag(QUIC_VERSION_12)); + EXPECT_EQ(MakeQuicTag('Q', '0', '1', '6'), + QuicVersionToQuicTag(QUIC_VERSION_16)); // Loop over all supported versions and make sure that we never hit the // default case (i.e. all supported versions should be successfully converted @@ -95,8 +108,8 @@ TEST(QuicProtocolTest, QuicTagToQuicVersion) { #endif // Explicitly test specific versions. - EXPECT_EQ(QUIC_VERSION_12, - QuicTagToQuicVersion(MakeQuicTag('Q', '0', '1', '2'))); + EXPECT_EQ(QUIC_VERSION_16, + QuicTagToQuicVersion(MakeQuicTag('Q', '0', '1', '6'))); for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) { QuicVersion version = kSupportedQuicVersions[i]; @@ -127,23 +140,23 @@ TEST(QuicProtocolTest, QuicTagToQuicVersionUnsupported) { } TEST(QuicProtocolTest, QuicVersionToString) { - EXPECT_EQ("QUIC_VERSION_12", QuicVersionToString(QUIC_VERSION_12)); + EXPECT_EQ("QUIC_VERSION_16", QuicVersionToString(QUIC_VERSION_16)); EXPECT_EQ("QUIC_VERSION_UNSUPPORTED", QuicVersionToString(QUIC_VERSION_UNSUPPORTED)); - QuicVersion single_version[] = {QUIC_VERSION_12}; + QuicVersion single_version[] = {QUIC_VERSION_16}; QuicVersionVector versions_vector; for (size_t i = 0; i < arraysize(single_version); ++i) { versions_vector.push_back(single_version[i]); } - EXPECT_EQ("QUIC_VERSION_12", QuicVersionVectorToString(versions_vector)); + EXPECT_EQ("QUIC_VERSION_16", QuicVersionVectorToString(versions_vector)); - QuicVersion multiple_versions[] = {QUIC_VERSION_UNSUPPORTED, QUIC_VERSION_12}; + QuicVersion multiple_versions[] = {QUIC_VERSION_UNSUPPORTED, QUIC_VERSION_16}; versions_vector.clear(); for (size_t i = 0; i < arraysize(multiple_versions); ++i) { versions_vector.push_back(multiple_versions[i]); } - EXPECT_EQ("QUIC_VERSION_UNSUPPORTED,QUIC_VERSION_12", + EXPECT_EQ("QUIC_VERSION_UNSUPPORTED,QUIC_VERSION_16", QuicVersionVectorToString(versions_vector)); // Make sure that all supported versions are present in QuicVersionToString. diff --git a/chromium/net/quic/quic_received_packet_manager.cc b/chromium/net/quic/quic_received_packet_manager.cc index b24ac7c671e..3b47687beb6 100644 --- a/chromium/net/quic/quic_received_packet_manager.cc +++ b/chromium/net/quic/quic_received_packet_manager.cc @@ -7,6 +7,7 @@ #include "base/logging.h" #include "base/stl_util.h" #include "net/base/linked_hash_map.h" +#include "net/quic/quic_connection_stats.h" using std::make_pair; using std::max; @@ -25,15 +26,115 @@ const size_t kMaxPacketsAfterNewMissing = 4; } +QuicReceivedPacketManager::EntropyTracker::EntropyTracker() + : packets_entropy_hash_(0), + first_gap_(1), + largest_observed_(0) { +} + +QuicReceivedPacketManager::EntropyTracker::~EntropyTracker() {} + +QuicPacketEntropyHash QuicReceivedPacketManager::EntropyTracker::EntropyHash( + QuicPacketSequenceNumber sequence_number) const { + DCHECK_LE(sequence_number, largest_observed_); + if (sequence_number == largest_observed_) { + return packets_entropy_hash_; + } + + DCHECK_GE(sequence_number, first_gap_); + ReceivedEntropyMap::const_iterator it = + packets_entropy_.upper_bound(sequence_number); + // When this map is empty we should only query entropy for + // largest_observed_, since no other entropy can be correctly + // calculated, because we're not storing the entropy for any prior packets. + // TODO(rtenneti): add support for LOG_IF_EVERY_N_SEC to chromium. + // LOG_IF_EVERY_N_SEC(DFATAL, it == packets_entropy_.end(), 10) + LOG_IF(DFATAL, it == packets_entropy_.end()) + << "EntropyHash may be unknown. largest_received: " + << largest_observed_ + << " sequence_number: " << sequence_number; + + // TODO(satyamshekhar): Make this O(1). + QuicPacketEntropyHash hash = packets_entropy_hash_; + for (; it != packets_entropy_.end(); ++it) { + hash ^= it->second; + } + return hash; +} + +void QuicReceivedPacketManager::EntropyTracker::RecordPacketEntropyHash( + QuicPacketSequenceNumber sequence_number, + QuicPacketEntropyHash entropy_hash) { + if (sequence_number < first_gap_) { + DVLOG(1) << "Ignoring received packet entropy for sequence_number:" + << sequence_number << " less than largest_peer_sequence_number:" + << first_gap_; + return; + } + + if (sequence_number > largest_observed_) { + largest_observed_ = sequence_number; + } + + packets_entropy_hash_ ^= entropy_hash; + DVLOG(2) << "setting cumulative received entropy hash to: " + << static_cast<int>(packets_entropy_hash_) + << " updated with sequence number " << sequence_number + << " entropy hash: " << static_cast<int>(entropy_hash); + + packets_entropy_.insert(make_pair(sequence_number, entropy_hash)); + AdvanceFirstGapAndGarbageCollectEntropyMap(); +} + +void QuicReceivedPacketManager::EntropyTracker::SetCumulativeEntropyUpTo( + QuicPacketSequenceNumber sequence_number, + QuicPacketEntropyHash entropy_hash) { + DCHECK_LE(sequence_number, largest_observed_); + if (sequence_number < first_gap_) { + DVLOG(1) << "Ignoring set entropy at:" << sequence_number + << " less than first_gap_:" << first_gap_; + return; + } + // Compute the current entropy based on the hash. + packets_entropy_hash_ = entropy_hash; + ReceivedEntropyMap::iterator it = + packets_entropy_.lower_bound(sequence_number); + // TODO(satyamshekhar): Make this O(1). + for (; it != packets_entropy_.end(); ++it) { + packets_entropy_hash_ ^= it->second; + } + // Update first_gap_ and discard old entropies. + first_gap_ = sequence_number; + packets_entropy_.erase( + packets_entropy_.begin(), + packets_entropy_.lower_bound(sequence_number)); + + // Garbage collect entries from the beginning of the map. + AdvanceFirstGapAndGarbageCollectEntropyMap(); +} + +void QuicReceivedPacketManager::EntropyTracker:: +AdvanceFirstGapAndGarbageCollectEntropyMap() { + while (!packets_entropy_.empty()) { + ReceivedEntropyMap::iterator it = packets_entropy_.begin(); + if (it->first != first_gap_) { + DCHECK_GT(it->first, first_gap_); + break; + } + packets_entropy_.erase(it); + ++first_gap_; + } +} + QuicReceivedPacketManager::QuicReceivedPacketManager( - CongestionFeedbackType congestion_type) - : packets_entropy_hash_(0), - largest_sequence_number_(0), - peer_largest_observed_packet_(0), + CongestionFeedbackType congestion_type, + QuicConnectionStats* stats) + : peer_largest_observed_packet_(0), least_packet_awaited_by_peer_(1), peer_least_packet_awaiting_ack_(0), time_largest_observed_(QuicTime::Zero()), - receive_algorithm_(ReceiveAlgorithmInterface::Create(congestion_type)) { + receive_algorithm_(ReceiveAlgorithmInterface::Create(congestion_type)), + stats_(stats) { received_info_.largest_observed = 0; received_info_.entropy_hash = 0; } @@ -43,33 +144,46 @@ QuicReceivedPacketManager::~QuicReceivedPacketManager() {} void QuicReceivedPacketManager::RecordPacketReceived( QuicByteCount bytes, const QuicPacketHeader& header, - QuicTime receipt_time, - bool revived) { + QuicTime receipt_time) { QuicPacketSequenceNumber sequence_number = header.packet_sequence_number; DCHECK(IsAwaitingPacket(sequence_number)); InsertMissingPacketsBetween( &received_info_, max(received_info_.largest_observed + 1, peer_least_packet_awaiting_ack_), - header.packet_sequence_number); + sequence_number); - if (received_info_.largest_observed > header.packet_sequence_number) { + if (received_info_.largest_observed > sequence_number) { // We've gotten one of the out of order packets - remove it from our // "missing packets" list. DVLOG(1) << "Removing " << sequence_number << " from missing list"; received_info_.missing_packets.erase(sequence_number); + + // Record how out of order stats. + ++stats_->packets_reordered; + uint32 sequence_gap = received_info_.largest_observed - sequence_number; + stats_->max_sequence_reordering = + max(stats_->max_sequence_reordering, sequence_gap); + uint32 reordering_time_us = + receipt_time.Subtract(time_largest_observed_).ToMicroseconds(); + stats_->max_time_reordering_us = max(stats_->max_time_reordering_us, + reordering_time_us); } - if (header.packet_sequence_number > received_info_.largest_observed) { - received_info_.largest_observed = header.packet_sequence_number; + if (sequence_number > received_info_.largest_observed) { + received_info_.largest_observed = sequence_number; time_largest_observed_ = receipt_time; } - RecordPacketEntropyHash(sequence_number, header.entropy_hash); + entropy_tracker_.RecordPacketEntropyHash(sequence_number, + header.entropy_hash); - // Don't update the receive algorithm for revived packets. - if (!revived) { - receive_algorithm_->RecordIncomingPacket( - bytes, sequence_number, receipt_time, revived); - } + receive_algorithm_->RecordIncomingPacket( + bytes, sequence_number, receipt_time); +} + +void QuicReceivedPacketManager::RecordPacketRevived( + QuicPacketSequenceNumber sequence_number) { + LOG_IF(DFATAL, !IsAwaitingPacket(sequence_number)); + received_info_.revived_packets.insert(sequence_number); } bool QuicReceivedPacketManager::IsMissing( @@ -104,23 +218,6 @@ void QuicReceivedPacketManager::UpdateReceivedPacketInfo( approximate_now.Subtract(time_largest_observed_); } -void QuicReceivedPacketManager::RecordPacketEntropyHash( - QuicPacketSequenceNumber sequence_number, - QuicPacketEntropyHash entropy_hash) { - if (sequence_number < largest_sequence_number_) { - DVLOG(1) << "Ignoring received packet entropy for sequence_number:" - << sequence_number << " less than largest_peer_sequence_number:" - << largest_sequence_number_; - return; - } - packets_entropy_.insert(make_pair(sequence_number, entropy_hash)); - packets_entropy_hash_ ^= entropy_hash; - DVLOG(2) << "setting cumulative received entropy hash to: " - << static_cast<int>(packets_entropy_hash_) - << " updated with sequence number " << sequence_number - << " entropy hash: " << static_cast<int>(entropy_hash); -} - bool QuicReceivedPacketManager::GenerateCongestionFeedback( QuicCongestionFeedbackFrame* feedback) { return receive_algorithm_->GenerateCongestionFeedback(feedback); @@ -128,74 +225,27 @@ bool QuicReceivedPacketManager::GenerateCongestionFeedback( QuicPacketEntropyHash QuicReceivedPacketManager::EntropyHash( QuicPacketSequenceNumber sequence_number) const { - DCHECK_LE(sequence_number, received_info_.largest_observed); - DCHECK_GE(sequence_number, largest_sequence_number_); - if (sequence_number == received_info_.largest_observed) { - return packets_entropy_hash_; - } - - ReceivedEntropyMap::const_iterator it = - packets_entropy_.upper_bound(sequence_number); - // When this map is empty we should only query entropy for - // received_info_.largest_observed, since no other entropy can be correctly - // calculated, because we're not storing the entropy for any prior packets. - // TODO(rtenneti): add support for LOG_IF_EVERY_N_SEC to chromium. - // LOG_IF_EVERY_N_SEC(DFATAL, it == packets_entropy_.end(), 10) - LOG_IF(DFATAL, it == packets_entropy_.end()) - << "EntropyHash may be unknown. largest_received: " - << received_info_.largest_observed - << " sequence_number: " << sequence_number; - - // TODO(satyamshekhar): Make this O(1). - QuicPacketEntropyHash hash = packets_entropy_hash_; - for (; it != packets_entropy_.end(); ++it) { - hash ^= it->second; - } - return hash; -} - -void QuicReceivedPacketManager::RecalculateEntropyHash( - QuicPacketSequenceNumber peer_least_unacked, - QuicPacketEntropyHash entropy_hash) { - DCHECK_LE(peer_least_unacked, received_info_.largest_observed); - if (peer_least_unacked < largest_sequence_number_) { - DVLOG(1) << "Ignoring received peer_least_unacked:" << peer_least_unacked - << " less than largest_peer_sequence_number:" - << largest_sequence_number_; - return; - } - largest_sequence_number_ = peer_least_unacked; - packets_entropy_hash_ = entropy_hash; - ReceivedEntropyMap::iterator it = - packets_entropy_.lower_bound(peer_least_unacked); - // TODO(satyamshekhar): Make this O(1). - for (; it != packets_entropy_.end(); ++it) { - packets_entropy_hash_ ^= it->second; - } - // Discard entropies before least unacked. - packets_entropy_.erase( - packets_entropy_.begin(), - packets_entropy_.lower_bound( - min(peer_least_unacked, received_info_.largest_observed))); + return entropy_tracker_.EntropyHash(sequence_number); } void QuicReceivedPacketManager::UpdatePacketInformationReceivedByPeer( - const QuicAckFrame& incoming_ack) { + const ReceivedPacketInfo& received_info) { // ValidateAck should fail if largest_observed ever shrinks. - DCHECK_LE(peer_largest_observed_packet_, - incoming_ack.received_info.largest_observed); - peer_largest_observed_packet_ = incoming_ack.received_info.largest_observed; + DCHECK_LE(peer_largest_observed_packet_, received_info.largest_observed); + peer_largest_observed_packet_ = received_info.largest_observed; - if (incoming_ack.received_info.missing_packets.empty()) { + if (received_info.missing_packets.empty()) { least_packet_awaited_by_peer_ = peer_largest_observed_packet_ + 1; } else { - least_packet_awaited_by_peer_ = - *(incoming_ack.received_info.missing_packets.begin()); + least_packet_awaited_by_peer_ = *(received_info.missing_packets.begin()); } } bool QuicReceivedPacketManager::DontWaitForPacketsBefore( QuicPacketSequenceNumber least_unacked) { + received_info_.revived_packets.erase( + received_info_.revived_packets.begin(), + received_info_.revived_packets.lower_bound(least_unacked)); size_t missing_packets_count = received_info_.missing_packets.size(); received_info_.missing_packets.erase( received_info_.missing_packets.begin(), @@ -204,22 +254,19 @@ bool QuicReceivedPacketManager::DontWaitForPacketsBefore( } void QuicReceivedPacketManager::UpdatePacketInformationSentByPeer( - const QuicAckFrame& incoming_ack) { + const QuicStopWaitingFrame& stop_waiting) { // ValidateAck() should fail if peer_least_packet_awaiting_ack_ shrinks. - DCHECK_LE(peer_least_packet_awaiting_ack_, - incoming_ack.sent_info.least_unacked); - if (incoming_ack.sent_info.least_unacked > peer_least_packet_awaiting_ack_) { - bool missed_packets = - DontWaitForPacketsBefore(incoming_ack.sent_info.least_unacked); - if (missed_packets || incoming_ack.sent_info.least_unacked > - received_info_.largest_observed + 1) { + DCHECK_LE(peer_least_packet_awaiting_ack_, stop_waiting.least_unacked); + if (stop_waiting.least_unacked > peer_least_packet_awaiting_ack_) { + bool missed_packets = DontWaitForPacketsBefore(stop_waiting.least_unacked); + if (missed_packets) { DVLOG(1) << "Updating entropy hashed since we missed packets"; // There were some missing packets that we won't ever get now. Recalculate // the received entropy hash. - RecalculateEntropyHash(incoming_ack.sent_info.least_unacked, - incoming_ack.sent_info.entropy_hash); + entropy_tracker_.SetCumulativeEntropyUpTo(stop_waiting.least_unacked, + stop_waiting.entropy_hash); } - peer_least_packet_awaiting_ack_ = incoming_ack.sent_info.least_unacked; + peer_least_packet_awaiting_ack_ = stop_waiting.least_unacked; } DCHECK(received_info_.missing_packets.empty() || *received_info_.missing_packets.begin() >= diff --git a/chromium/net/quic/quic_received_packet_manager.h b/chromium/net/quic/quic_received_packet_manager.h index 9e2fb59c241..1b283316e33 100644 --- a/chromium/net/quic/quic_received_packet_manager.h +++ b/chromium/net/quic/quic_received_packet_manager.h @@ -15,29 +15,98 @@ namespace net { namespace test { +class EntropyTrackerPeer; class QuicConnectionPeer; class QuicReceivedPacketManagerPeer; } // namespace test +struct QuicConnectionStats; + // Records all received packets by a connection and tracks their entropy. // Also calculates the correct entropy for the framer when it truncates an ack // frame being serialized. class NET_EXPORT_PRIVATE QuicReceivedPacketManager : public QuicReceivedEntropyHashCalculatorInterface { public: - explicit QuicReceivedPacketManager(CongestionFeedbackType congestion_type); + class NET_EXPORT_PRIVATE EntropyTracker { + public: + EntropyTracker(); + ~EntropyTracker(); + + // Compute the XOR of the entropy of all received packets up to + // and including sequence_number. + // Requires that either: + // sequence_number == largest_observed_ + // or: + // sequence_number > first_gap_ && + // sequence_number < largest_observed_ && + // sequence_number in packets_entropy_ + QuicPacketEntropyHash EntropyHash( + QuicPacketSequenceNumber sequence_number) const; + + // Record the received entropy hash against |sequence_number|. + // Performs garbage collection to advance first_gap_ if + // sequence_number == first_gap_. + void RecordPacketEntropyHash(QuicPacketSequenceNumber sequence_number, + QuicPacketEntropyHash entropy_hash); + + // Sets the entropy hash up to but not including a sequence number based + // on the hash provided by a StopWaiting frame. Clears older packet + // entropy entries and performs garbage collection up to the first gap. + void SetCumulativeEntropyUpTo(QuicPacketSequenceNumber sequence_number, + QuicPacketEntropyHash entropy_hash); + + private: + friend class test::EntropyTrackerPeer; + + typedef std::map<QuicPacketSequenceNumber, + QuicPacketEntropyHash> ReceivedEntropyMap; + + // Recomputes first_gap_ and removes packets_entropy_ entries that are no + // longer needed to compute EntropyHash. + void AdvanceFirstGapAndGarbageCollectEntropyMap(); + + // TODO(satyamshekhar): Can be optimized using an interval set like data + // structure. + // Map of received sequence numbers to their corresponding entropy. + // Stores an entry for every received packet whose sequence_number is larger + // than first_gap_. Packets without the entropy bit set have an entropy + // value of 0. + // TODO(ianswett): When the entropy flag is off, the entropy + // should not be 0. + ReceivedEntropyMap packets_entropy_; + + // Cumulative hash of entropy of all received packets. + QuicPacketEntropyHash packets_entropy_hash_; + + // Sequence number of the first packet that we do not know the entropy of. + // If there are no gaps in the received packet sequence, + // packets_entropy_ will be empty and first_gap_ will be equal to + // 'largest_observed_ + 1' since that's the first packet for which + // entropy is unknown. If there are gaps, packets_entropy_ will + // contain entries for all received packets with sequence_number > + // first_gap_. + QuicPacketSequenceNumber first_gap_; + + // Sequence number of the largest observed packet. + QuicPacketSequenceNumber largest_observed_; + + DISALLOW_COPY_AND_ASSIGN(EntropyTracker); + }; + + explicit QuicReceivedPacketManager(CongestionFeedbackType congestion_type, + QuicConnectionStats* stats); virtual ~QuicReceivedPacketManager(); // Updates the internal state concerning which packets have been received. // bytes: the packet size in bytes including Quic Headers. // header: the 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. void RecordPacketReceived(QuicByteCount bytes, const QuicPacketHeader& header, - QuicTime receipt_time, - bool revived); + QuicTime receipt_time); + + void RecordPacketRevived(QuicPacketSequenceNumber sequence_number); // Checks whether |sequence_number| is missing and less than largest observed. bool IsMissing(QuicPacketSequenceNumber sequence_number); @@ -62,12 +131,12 @@ class NET_EXPORT_PRIVATE QuicReceivedPacketManager : virtual QuicPacketEntropyHash EntropyHash( QuicPacketSequenceNumber sequence_number) const OVERRIDE; - // These two are called by OnAckFrame. - // - // Updates internal state based on |incoming_ack.received_info|. - void UpdatePacketInformationReceivedByPeer(const QuicAckFrame& incoming_ack); - // Updates internal state based on |incoming_ack.sent_info|. - void UpdatePacketInformationSentByPeer(const QuicAckFrame& incoming_ack); + // Updates internal state based on |received_info|. + void UpdatePacketInformationReceivedByPeer( + const ReceivedPacketInfo& received_nfo); + // Updates internal state based on |stop_waiting|. + void UpdatePacketInformationSentByPeer( + const QuicStopWaitingFrame& stop_waiting); // Returns whether the peer is missing packets. bool HasMissingPackets(); @@ -92,40 +161,14 @@ class NET_EXPORT_PRIVATE QuicReceivedPacketManager : friend class test::QuicConnectionPeer; friend class test::QuicReceivedPacketManagerPeer; - typedef std::map<QuicPacketSequenceNumber, - QuicPacketEntropyHash> ReceivedEntropyMap; - - // Record the received entropy hash against |sequence_number|. - void RecordPacketEntropyHash(QuicPacketSequenceNumber sequence_number, - QuicPacketEntropyHash entropy_hash); - - // Recalculate the entropy hash and clears old packet entropies, - // now that the sender sent us the |entropy_hash| for packets up to, - // but not including, |peer_least_unacked|. - void RecalculateEntropyHash(QuicPacketSequenceNumber peer_least_unacked, - QuicPacketEntropyHash entropy_hash); - // Deletes all missing packets before least unacked. The connection won't // process any packets with sequence number before |least_unacked| that it // received after this call. Returns true if there were missing packets before // |least_unacked| unacked, false otherwise. bool DontWaitForPacketsBefore(QuicPacketSequenceNumber least_unacked); - // TODO(satyamshekhar): Can be optimized using an interval set like data - // structure. - // Map of received sequence numbers to their corresponding entropy. - // Every received packet has an entry, and packets without the entropy bit set - // have an entropy value of 0. - // TODO(ianswett): When the entropy flag is off, the entropy should not be 0. - ReceivedEntropyMap packets_entropy_; - - // Cumulative hash of entropy of all received packets. - QuicPacketEntropyHash packets_entropy_hash_; - - // The largest sequence number cleared by RecalculateEntropyHash. - // Received entropy cannot be calculated for numbers less than it. - QuicPacketSequenceNumber largest_sequence_number_; - + // Tracks entropy hashes of received packets. + EntropyTracker entropy_tracker_; // Track some peer state so we can do less bookkeeping. // Largest sequence number that the peer has observed. Mostly received, @@ -146,6 +189,10 @@ class NET_EXPORT_PRIVATE QuicReceivedPacketManager : QuicTime time_largest_observed_; scoped_ptr<ReceiveAlgorithmInterface> receive_algorithm_; + + QuicConnectionStats* stats_; + + DISALLOW_COPY_AND_ASSIGN(QuicReceivedPacketManager); }; } // namespace net diff --git a/chromium/net/quic/quic_received_packet_manager_test.cc b/chromium/net/quic/quic_received_packet_manager_test.cc index 9d11129f0ff..b5793092980 100644 --- a/chromium/net/quic/quic_received_packet_manager_test.cc +++ b/chromium/net/quic/quic_received_packet_manager_test.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <vector> +#include "net/quic/quic_connection_stats.h" #include "net/quic/test_tools/quic_received_packet_manager_peer.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -17,20 +18,191 @@ using std::vector; namespace net { namespace test { + +class EntropyTrackerPeer { + public: + static QuicPacketSequenceNumber first_gap( + const QuicReceivedPacketManager::EntropyTracker& tracker) { + return tracker.first_gap_; + } + static QuicPacketSequenceNumber largest_observed( + const QuicReceivedPacketManager::EntropyTracker& tracker) { + return tracker.largest_observed_; + } + static int packets_entropy_size( + const QuicReceivedPacketManager::EntropyTracker& tracker) { + return tracker.packets_entropy_.size(); + } + static bool IsTrackingPacket( + const QuicReceivedPacketManager::EntropyTracker& tracker, + QuicPacketSequenceNumber sequence_number) { + return tracker.packets_entropy_.find(sequence_number) != + tracker.packets_entropy_.end(); + } +}; + namespace { +// Entropy of individual packets is not tracked if there are no gaps. +TEST(EntropyTrackerTest, NoGaps) { + QuicReceivedPacketManager::EntropyTracker tracker; + + tracker.RecordPacketEntropyHash(1, 23); + tracker.RecordPacketEntropyHash(2, 42); + + EXPECT_EQ(23 ^ 42, tracker.EntropyHash(2)); + EXPECT_EQ(3u, EntropyTrackerPeer::first_gap(tracker)); + + EXPECT_EQ(2u, EntropyTrackerPeer::largest_observed(tracker)); + EXPECT_EQ(0, EntropyTrackerPeer::packets_entropy_size(tracker)); + EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 1)); + EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 2)); +} + +// Entropy of individual packets is tracked as long as there are gaps. +// Filling the first gap results in entropy getting garbage collected. +TEST(EntropyTrackerTest, FillGaps) { + QuicReceivedPacketManager::EntropyTracker tracker; + + tracker.RecordPacketEntropyHash(2, 5); + tracker.RecordPacketEntropyHash(5, 17); + tracker.RecordPacketEntropyHash(6, 23); + tracker.RecordPacketEntropyHash(9, 42); + + EXPECT_EQ(1u, EntropyTrackerPeer::first_gap(tracker)); + EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker)); + EXPECT_EQ(4, EntropyTrackerPeer::packets_entropy_size(tracker)); + + EXPECT_EQ(5, tracker.EntropyHash(2)); + EXPECT_EQ(5 ^ 17, tracker.EntropyHash(5)); + EXPECT_EQ(5 ^ 17 ^ 23, tracker.EntropyHash(6)); + EXPECT_EQ(5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9)); + + EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 1)); + EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 2)); + EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 5)); + EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 6)); + EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 9)); + + // Fill the gap at 1. + tracker.RecordPacketEntropyHash(1, 2); + + EXPECT_EQ(3u, EntropyTrackerPeer::first_gap(tracker)); + EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker)); + EXPECT_EQ(3, EntropyTrackerPeer::packets_entropy_size(tracker)); + + EXPECT_EQ(2 ^ 5 ^ 17, tracker.EntropyHash(5)); + EXPECT_EQ(2 ^ 5 ^ 17 ^ 23, tracker.EntropyHash(6)); + EXPECT_EQ(2 ^ 5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9)); + + EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 1)); + EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 2)); + EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 5)); + EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 6)); + EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 9)); + + // Fill the gap at 4. + tracker.RecordPacketEntropyHash(4, 2); + + EXPECT_EQ(3u, EntropyTrackerPeer::first_gap(tracker)); + EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker)); + EXPECT_EQ(4, EntropyTrackerPeer::packets_entropy_size(tracker)); + + EXPECT_EQ(5, tracker.EntropyHash(4)); + EXPECT_EQ(5 ^ 17, tracker.EntropyHash(5)); + EXPECT_EQ(5 ^ 17 ^ 23, tracker.EntropyHash(6)); + EXPECT_EQ(5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9)); + + EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 3)); + EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 4)); + EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 5)); + EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 6)); + EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 9)); + + // Fill the gap at 3. Entropy for packets 3 to 6 are forgotten. + tracker.RecordPacketEntropyHash(3, 2); + + EXPECT_EQ(7u, EntropyTrackerPeer::first_gap(tracker)); + EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker)); + EXPECT_EQ(1, EntropyTrackerPeer::packets_entropy_size(tracker)); + + EXPECT_EQ(2 ^ 5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9)); + + EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 3)); + EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 4)); + EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 5)); + EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 6)); + EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 9)); + + // Fill in the rest. + tracker.RecordPacketEntropyHash(7, 2); + tracker.RecordPacketEntropyHash(8, 2); + + EXPECT_EQ(10u, EntropyTrackerPeer::first_gap(tracker)); + EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker)); + EXPECT_EQ(0, EntropyTrackerPeer::packets_entropy_size(tracker)); + + EXPECT_EQ(2 ^ 5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9)); +} + +TEST(EntropyTrackerTest, SetCumulativeEntropyUpTo) { + QuicReceivedPacketManager::EntropyTracker tracker; + + tracker.RecordPacketEntropyHash(2, 5); + tracker.RecordPacketEntropyHash(5, 17); + tracker.RecordPacketEntropyHash(6, 23); + tracker.RecordPacketEntropyHash(9, 42); + + EXPECT_EQ(1u, EntropyTrackerPeer::first_gap(tracker)); + EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker)); + EXPECT_EQ(4, EntropyTrackerPeer::packets_entropy_size(tracker)); + + // Inform the tracker about value of the hash at a gap. + tracker.SetCumulativeEntropyUpTo(3, 7); + EXPECT_EQ(3u, EntropyTrackerPeer::first_gap(tracker)); + EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker)); + EXPECT_EQ(3, EntropyTrackerPeer::packets_entropy_size(tracker)); + + EXPECT_EQ(7 ^ 17, tracker.EntropyHash(5)); + EXPECT_EQ(7 ^ 17 ^ 23, tracker.EntropyHash(6)); + EXPECT_EQ(7 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9)); + + // Inform the tracker about value of the hash at a known location. + tracker.SetCumulativeEntropyUpTo(6, 1); + EXPECT_EQ(7u, EntropyTrackerPeer::first_gap(tracker)); + EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker)); + EXPECT_EQ(1, EntropyTrackerPeer::packets_entropy_size(tracker)); + + EXPECT_EQ(1 ^ 23 ^ 42, tracker.EntropyHash(9)); + + // Inform the tracker about value of the hash at the last location. + tracker.SetCumulativeEntropyUpTo(9, 21); + EXPECT_EQ(10u, EntropyTrackerPeer::first_gap(tracker)); + EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker)); + EXPECT_EQ(0, EntropyTrackerPeer::packets_entropy_size(tracker)); + + EXPECT_EQ(42 ^ 21, tracker.EntropyHash(9)); +} + class QuicReceivedPacketManagerTest : public ::testing::Test { protected: - QuicReceivedPacketManagerTest() : received_manager_(kTCP) { } + QuicReceivedPacketManagerTest() : received_manager_(kTCP, &stats_) {} - void RecordPacketEntropyHash(QuicPacketSequenceNumber sequence_number, - QuicPacketEntropyHash entropy_hash) { + void RecordPacketReceipt(QuicPacketSequenceNumber sequence_number, + QuicPacketEntropyHash entropy_hash) { + RecordPacketReceipt(sequence_number, entropy_hash, QuicTime::Zero()); + } + + void RecordPacketReceipt(QuicPacketSequenceNumber sequence_number, + QuicPacketEntropyHash entropy_hash, + QuicTime receipt_time) { QuicPacketHeader header; header.packet_sequence_number = sequence_number; header.entropy_hash = entropy_hash; - received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero(), false); + received_manager_.RecordPacketReceived(0u, header, receipt_time); } + QuicConnectionStats stats_; QuicReceivedPacketManager received_manager_; }; @@ -43,8 +215,7 @@ TEST_F(QuicReceivedPacketManagerTest, ReceivedPacketEntropyHash) { entropies.push_back(make_pair(8, 34)); for (size_t i = 0; i < entropies.size(); ++i) { - RecordPacketEntropyHash(entropies[i].first, - entropies[i].second); + RecordPacketReceipt(entropies[i].first, entropies[i].second); } sort(entropies.begin(), entropies.end()); @@ -56,58 +227,68 @@ TEST_F(QuicReceivedPacketManagerTest, ReceivedPacketEntropyHash) { hash ^= entropies[index].second; ++index; } + if (i < 3) continue; EXPECT_EQ(hash, received_manager_.EntropyHash(i)); } + // Reorder by 5 when 2 is received after 7. + EXPECT_EQ(5u, stats_.max_sequence_reordering); + EXPECT_EQ(0u, stats_.max_time_reordering_us); + EXPECT_EQ(2u, stats_.packets_reordered); } TEST_F(QuicReceivedPacketManagerTest, EntropyHashBelowLeastObserved) { EXPECT_EQ(0, received_manager_.EntropyHash(0)); - RecordPacketEntropyHash(4, 5); + RecordPacketReceipt(4, 5); EXPECT_EQ(0, received_manager_.EntropyHash(3)); } TEST_F(QuicReceivedPacketManagerTest, EntropyHashAboveLargestObserved) { EXPECT_EQ(0, received_manager_.EntropyHash(0)); - RecordPacketEntropyHash(4, 5); + RecordPacketReceipt(4, 5); EXPECT_EQ(0, received_manager_.EntropyHash(3)); } -TEST_F(QuicReceivedPacketManagerTest, RecalculateEntropyHash) { +TEST_F(QuicReceivedPacketManagerTest, SetCumulativeEntropyUpTo) { vector<pair<QuicPacketSequenceNumber, QuicPacketEntropyHash> > entropies; entropies.push_back(make_pair(1, 12)); entropies.push_back(make_pair(2, 1)); entropies.push_back(make_pair(3, 33)); entropies.push_back(make_pair(4, 3)); - entropies.push_back(make_pair(5, 34)); - entropies.push_back(make_pair(6, 29)); + entropies.push_back(make_pair(6, 34)); + entropies.push_back(make_pair(7, 29)); QuicPacketEntropyHash entropy_hash = 0; for (size_t i = 0; i < entropies.size(); ++i) { - RecordPacketEntropyHash(entropies[i].first, entropies[i].second); + RecordPacketReceipt(entropies[i].first, entropies[i].second); entropy_hash ^= entropies[i].second; } - EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(6)); + EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(7)); - // Now set the entropy hash up to 4 to be 100. + // Now set the entropy hash up to 5 to be 100. entropy_hash ^= 100; - for (size_t i = 0; i < 3; ++i) { + for (size_t i = 0; i < 4; ++i) { entropy_hash ^= entropies[i].second; } - QuicReceivedPacketManagerPeer::RecalculateEntropyHash( - &received_manager_, 4, 100); - EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(6)); + QuicReceivedPacketManagerPeer::SetCumulativeEntropyUpTo( + &received_manager_, 5, 100); + EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(7)); - QuicReceivedPacketManagerPeer::RecalculateEntropyHash( + QuicReceivedPacketManagerPeer::SetCumulativeEntropyUpTo( &received_manager_, 1, 50); - EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(6)); + EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(7)); + + // No reordering. + EXPECT_EQ(0u, stats_.max_sequence_reordering); + EXPECT_EQ(0u, stats_.max_time_reordering_us); + EXPECT_EQ(0u, stats_.packets_reordered); } TEST_F(QuicReceivedPacketManagerTest, DontWaitForPacketsBefore) { QuicPacketHeader header; header.packet_sequence_number = 2u; - received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero(), false); + received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero()); header.packet_sequence_number = 7u; - received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero(), false); + received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero()); EXPECT_TRUE(received_manager_.IsAwaitingPacket(3u)); EXPECT_TRUE(received_manager_.IsAwaitingPacket(6u)); EXPECT_TRUE(QuicReceivedPacketManagerPeer::DontWaitForPacketsBefore( @@ -120,7 +301,7 @@ TEST_F(QuicReceivedPacketManagerTest, UpdateReceivedPacketInfo) { QuicPacketHeader header; header.packet_sequence_number = 2u; QuicTime two_ms = QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(2)); - received_manager_.RecordPacketReceived(0u, header, two_ms, false); + received_manager_.RecordPacketReceived(0u, header, two_ms); ReceivedPacketInfo info; received_manager_.UpdateReceivedPacketInfo(&info, QuicTime::Zero()); @@ -136,6 +317,17 @@ TEST_F(QuicReceivedPacketManagerTest, UpdateReceivedPacketInfo) { info.delta_time_largest_observed); } +TEST_F(QuicReceivedPacketManagerTest, UpdateReceivedConnectionStats) { + RecordPacketReceipt(1, 0); + RecordPacketReceipt(6, 0); + RecordPacketReceipt( + 2, 0, QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(1))); + + EXPECT_EQ(4u, stats_.max_sequence_reordering); + EXPECT_EQ(1000u, stats_.max_time_reordering_us); + EXPECT_EQ(1u, stats_.packets_reordered); +} + } // namespace } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_reliable_client_stream.cc b/chromium/net/quic/quic_reliable_client_stream.cc index af12b8b222b..4eb74d6a31d 100644 --- a/chromium/net/quic/quic_reliable_client_stream.cc +++ b/chromium/net/quic/quic_reliable_client_stream.cc @@ -7,7 +7,7 @@ #include "base/callback_helpers.h" #include "net/base/net_errors.h" #include "net/quic/quic_session.h" -#include "net/spdy/write_blocked_list.h" +#include "net/quic/quic_write_blocked_list.h" namespace net { @@ -59,7 +59,7 @@ QuicPriority QuicReliableClientStream::EffectivePriority() const { if (delegate_ && delegate_->HasSendHeadersComplete()) { return QuicDataStream::EffectivePriority(); } - return kHighestPriority; + return QuicWriteBlockedList::kHighestPriority; } int QuicReliableClientStream::WriteStreamData( @@ -69,7 +69,7 @@ int QuicReliableClientStream::WriteStreamData( // We should not have data buffered. DCHECK(!HasBufferedData()); // Writes the data, or buffers it. - WriteOrBufferData(data, fin); + WriteOrBufferData(data, fin, NULL); if (!HasBufferedData()) { return OK; } @@ -93,9 +93,7 @@ void QuicReliableClientStream::OnError(int error) { } bool QuicReliableClientStream::CanWrite(const CompletionCallback& callback) { - bool can_write = session()->connection()->CanWrite( - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, - id() == kCryptoStreamId ? IS_HANDSHAKE : NOT_HANDSHAKE); + bool can_write = session()->connection()->CanWrite(HAS_RETRANSMITTABLE_DATA); if (!can_write) { session()->MarkWriteBlocked(id(), EffectivePriority()); DCHECK(callback_.is_null()); diff --git a/chromium/net/quic/quic_reliable_client_stream_test.cc b/chromium/net/quic/quic_reliable_client_stream_test.cc index 081186ec846..caa697aa484 100644 --- a/chromium/net/quic/quic_reliable_client_stream_test.cc +++ b/chromium/net/quic/quic_reliable_client_stream_test.cc @@ -12,6 +12,7 @@ #include "net/quic/test_tools/quic_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" +using testing::AnyNumber; using testing::Return; using testing::StrEq; using testing::_; @@ -20,7 +21,7 @@ namespace net { namespace test { namespace { -const QuicGuid kStreamId = 3; +const QuicConnectionId kStreamId = 3; class MockDelegate : public QuicReliableClientStream::Delegate { public: @@ -37,12 +38,14 @@ class MockDelegate : public QuicReliableClientStream::Delegate { DISALLOW_COPY_AND_ASSIGN(MockDelegate); }; -class QuicReliableClientStreamTest : public ::testing::Test { +class QuicReliableClientStreamTest + : public ::testing::TestWithParam<QuicVersion> { public: QuicReliableClientStreamTest() - : session_(new MockConnection(false)), - stream_(kStreamId, &session_, BoundNetLog()) { - stream_.SetDelegate(&delegate_); + : session_(new MockConnection(false, SupportedVersions(GetParam()))) { + stream_ = new QuicReliableClientStream(kStreamId, &session_, BoundNetLog()); + session_.ActivateStream(stream_); + stream_->SetDelegate(&delegate_); } void InitializeHeaders() { @@ -77,37 +80,39 @@ class QuicReliableClientStreamTest : public ::testing::Test { testing::StrictMock<MockDelegate> delegate_; MockSession session_; - QuicReliableClientStream stream_; + QuicReliableClientStream* stream_; QuicCryptoClientConfig crypto_config_; SpdyHeaderBlock headers_; }; -TEST_F(QuicReliableClientStreamTest, OnFinRead) { +INSTANTIATE_TEST_CASE_P(Version, QuicReliableClientStreamTest, + ::testing::ValuesIn(QuicSupportedVersions())); + +TEST_P(QuicReliableClientStreamTest, OnFinRead) { InitializeHeaders(); - QuicSpdyCompressor compressor; - string compressed_headers = compressor.CompressHeaders(headers_); - QuicStreamFrame frame1(kStreamId, false, 0, MakeIOVector(compressed_headers)); string uncompressed_headers = SpdyUtils::SerializeUncompressedHeaders(headers_); EXPECT_CALL(delegate_, OnDataReceived(StrEq(uncompressed_headers.data()), uncompressed_headers.size())); - stream_.OnStreamFrame(frame1); + QuicStreamOffset offset = 0; + stream_->OnStreamHeaders(uncompressed_headers); + stream_->OnStreamHeadersComplete(false, uncompressed_headers.length()); IOVector iov; - QuicStreamFrame frame2(kStreamId, true, compressed_headers.length(), iov); + QuicStreamFrame frame2(kStreamId, true, offset, iov); EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR)); - stream_.OnStreamFrame(frame2); + stream_->OnStreamFrame(frame2); } -TEST_F(QuicReliableClientStreamTest, ProcessData) { +TEST_P(QuicReliableClientStreamTest, ProcessData) { const char data[] = "hello world!"; EXPECT_CALL(delegate_, OnDataReceived(StrEq(data), arraysize(data))); EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR)); - EXPECT_EQ(arraysize(data), stream_.ProcessData(data, arraysize(data))); + EXPECT_EQ(arraysize(data), stream_->ProcessData(data, arraysize(data))); } -TEST_F(QuicReliableClientStreamTest, ProcessDataWithError) { +TEST_P(QuicReliableClientStreamTest, ProcessDataWithError) { const char data[] = "hello world!"; EXPECT_CALL(delegate_, OnDataReceived(StrEq(data), @@ -115,50 +120,50 @@ TEST_F(QuicReliableClientStreamTest, ProcessDataWithError) { EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR)); - EXPECT_EQ(0u, stream_.ProcessData(data, arraysize(data))); + EXPECT_EQ(0u, stream_->ProcessData(data, arraysize(data))); } -TEST_F(QuicReliableClientStreamTest, OnError) { +TEST_P(QuicReliableClientStreamTest, OnError) { EXPECT_CALL(delegate_, OnError(ERR_INTERNET_DISCONNECTED)); - stream_.OnError(ERR_INTERNET_DISCONNECTED); - EXPECT_FALSE(stream_.GetDelegate()); + stream_->OnError(ERR_INTERNET_DISCONNECTED); + EXPECT_FALSE(stream_->GetDelegate()); } -TEST_F(QuicReliableClientStreamTest, WriteStreamData) { +TEST_P(QuicReliableClientStreamTest, WriteStreamData) { EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR)); const char kData1[] = "hello world"; const size_t kDataLen = arraysize(kData1); // All data written. - EXPECT_CALL(session_, WritevData(stream_.id(), _, _, _, _, _)).WillOnce( + EXPECT_CALL(session_, WritevData(stream_->id(), _, _, _, _, _)).WillOnce( Return(QuicConsumedData(kDataLen, true))); TestCompletionCallback callback; - EXPECT_EQ(OK, stream_.WriteStreamData(base::StringPiece(kData1, kDataLen), - true, callback.callback())); + EXPECT_EQ(OK, stream_->WriteStreamData(base::StringPiece(kData1, kDataLen), + true, callback.callback())); } -TEST_F(QuicReliableClientStreamTest, WriteStreamDataAsync) { - EXPECT_CALL(delegate_, HasSendHeadersComplete()); +TEST_P(QuicReliableClientStreamTest, WriteStreamDataAsync) { + EXPECT_CALL(delegate_, HasSendHeadersComplete()).Times(AnyNumber()); EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR)); const char kData1[] = "hello world"; const size_t kDataLen = arraysize(kData1); // No data written. - EXPECT_CALL(session_, WritevData(stream_.id(), _, _, _, _, _)).WillOnce( + EXPECT_CALL(session_, WritevData(stream_->id(), _, _, _, _, _)).WillOnce( Return(QuicConsumedData(0, false))); TestCompletionCallback callback; EXPECT_EQ(ERR_IO_PENDING, - stream_.WriteStreamData(base::StringPiece(kData1, kDataLen), - true, callback.callback())); + stream_->WriteStreamData(base::StringPiece(kData1, kDataLen), + true, callback.callback())); ASSERT_FALSE(callback.have_result()); // All data written. - EXPECT_CALL(session_, WritevData(stream_.id(), _, _, _, _, _)).WillOnce( + EXPECT_CALL(session_, WritevData(stream_->id(), _, _, _, _, _)).WillOnce( Return(QuicConsumedData(kDataLen, true))); - stream_.OnCanWrite(); + stream_->OnCanWrite(); ASSERT_TRUE(callback.have_result()); EXPECT_EQ(OK, callback.WaitForResult()); } diff --git a/chromium/net/quic/quic_sent_entropy_manager.h b/chromium/net/quic/quic_sent_entropy_manager.h index a101e738d06..c89d97730de 100644 --- a/chromium/net/quic/quic_sent_entropy_manager.h +++ b/chromium/net/quic/quic_sent_entropy_manager.h @@ -51,6 +51,8 @@ class NET_EXPORT_PRIVATE QuicSentEntropyManager { // Cumulative hash of entropy of all sent packets. QuicPacketEntropyHash packets_entropy_hash_; + + DISALLOW_COPY_AND_ASSIGN(QuicSentEntropyManager); }; } // namespace net diff --git a/chromium/net/quic/quic_sent_packet_manager.cc b/chromium/net/quic/quic_sent_packet_manager.cc index bda30c72a1f..806297e3575 100644 --- a/chromium/net/quic/quic_sent_packet_manager.cc +++ b/chromium/net/quic/quic_sent_packet_manager.cc @@ -4,38 +4,23 @@ #include "net/quic/quic_sent_packet_manager.h" +#include <algorithm> + #include "base/logging.h" #include "base/stl_util.h" #include "net/quic/congestion_control/pacing_sender.h" +#include "net/quic/crypto/crypto_protocol.h" #include "net/quic/quic_ack_notifier_manager.h" +#include "net/quic/quic_connection_stats.h" +#include "net/quic/quic_flags.h" +#include "net/quic/quic_utils_chromium.h" using std::make_pair; +using std::max; using std::min; -// TODO(rtenneti): Remove this. -// Do not flip this flag until the flakiness of the -// net/tools/quic/end_to_end_test is fixed. -// If true, then QUIC connections will track the retransmission history of a -// packet so that an ack of a previous transmission will ack the data of all -// other transmissions. -bool FLAGS_track_retransmission_history = false; - -// A test-only flag to prevent the RTO from backing off when multiple sequential -// tail drops occur. -bool FLAGS_limit_rto_increase_for_tests = false; - -// Do not remove this flag until the Finch-trials described in b/11706275 -// are complete. -// If true, QUIC connections will support the use of a pacing algorithm when -// sending packets, in an attempt to reduce packet loss. The client must also -// request pacing for the server to enable it. -bool FLAGS_enable_quic_pacing = false; - namespace net { namespace { -static const int kBitrateSmoothingPeriodMs = 1000; -static const int kHistoryPeriodMs = 5000; - static const int kDefaultRetransmissionTimeMs = 500; // TCP RFC calls for 1 second RTO however Linux differs from this default and // define the minimum RTO to 200ms, we will use the same until we have data to @@ -44,272 +29,282 @@ static const int kMinRetransmissionTimeMs = 200; static const int kMaxRetransmissionTimeMs = 60000; static const size_t kMaxRetransmissions = 10; -// We only retransmit 2 packets per ack. -static const size_t kMaxRetransmissionsPerAck = 2; +// Only exponentially back off the handshake timer 5 times due to a timeout. +static const size_t kMaxHandshakeRetransmissionBackoffs = 5; +static const size_t kMinHandshakeTimeoutMs = 10; + +// Sends up to two tail loss probes before firing an RTO, +// per draft RFC draft-dukkipati-tcpm-tcp-loss-probe. +static const size_t kDefaultMaxTailLossProbes = 2; +static const int64 kMinTailLossProbeTimeoutMs = 10; -// TCP retransmits after 3 nacks. -static const size_t kNumberOfNacksBeforeRetransmission = 3; +// Number of samples before we force a new recent min rtt to be captured. +static const size_t kNumMinRttSamplesAfterQuiescence = 2; + +bool HasCryptoHandshake(const TransmissionInfo& transmission_info) { + if (transmission_info.retransmittable_frames == NULL) { + return false; + } + return transmission_info.retransmittable_frames->HasCryptoHandshake() == + IS_HANDSHAKE; +} -COMPILE_ASSERT(kHistoryPeriodMs >= kBitrateSmoothingPeriodMs, - history_must_be_longer_or_equal_to_the_smoothing_period); } // namespace #define ENDPOINT (is_server_ ? "Server: " : " Client: ") -QuicSentPacketManager::HelperInterface::~HelperInterface() { -} - QuicSentPacketManager::QuicSentPacketManager(bool is_server, - HelperInterface* helper, const QuicClock* clock, - CongestionFeedbackType type) - : is_server_(is_server), - helper_(helper), + QuicConnectionStats* stats, + CongestionFeedbackType type, + LossDetectionType loss_type) + : unacked_packets_(), + is_server_(is_server), clock_(clock), - send_algorithm_(SendAlgorithmInterface::Create(clock, type)), - rtt_sample_(QuicTime::Delta::Infinite()), + stats_(stats), + debug_delegate_(NULL), + send_algorithm_( + SendAlgorithmInterface::Create(clock, &rtt_stats_, type, stats)), + loss_algorithm_(LossDetectionInterface::Create(loss_type)), + largest_observed_(0), consecutive_rto_count_(0), + consecutive_tlp_count_(0), + consecutive_crypto_retransmission_count_(0), + pending_tlp_transmission_(false), + max_tail_loss_probes_(kDefaultMaxTailLossProbes), using_pacing_(false) { } QuicSentPacketManager::~QuicSentPacketManager() { - for (UnackedPacketMap::iterator it = unacked_packets_.begin(); - it != unacked_packets_.end(); ++it) { - delete it->second.retransmittable_frames; - // Only delete previous_transmissions once, for the newest packet. - if (it->second.previous_transmissions != NULL && - it->first == *it->second.previous_transmissions->rbegin()) { - delete it->second.previous_transmissions; - } - } - STLDeleteValues(&packet_history_map_); } void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) { - if (config.initial_round_trip_time_us() > 0 && - rtt_sample_.IsInfinite()) { - // The initial rtt should already be set on the client side. - DVLOG_IF(1, !is_server_) - << "Client did not set an initial RTT, but did negotiate one."; - rtt_sample_ = - QuicTime::Delta::FromMicroseconds(config.initial_round_trip_time_us()); - } - if (config.congestion_control() == kPACE) { + if (config.HasReceivedInitialRoundTripTimeUs() && + config.ReceivedInitialRoundTripTimeUs() > 0) { + rtt_stats_.set_initial_rtt_us(min(kMaxInitialRoundTripTimeUs, + config.ReceivedInitialRoundTripTimeUs())); + } + // TODO(ianswett): BBR is currently a server only feature. + if (config.HasReceivedCongestionOptions() && + ContainsQuicTag(config.ReceivedCongestionOptions(), kTBBR)) { + send_algorithm_.reset( + SendAlgorithmInterface::Create(clock_, &rtt_stats_, kTCPBBR, stats_)); + } + if (config.congestion_feedback() == kPACE) { MaybeEnablePacing(); } + if (config.HasReceivedLossDetection() && + config.ReceivedLossDetection() == kTIME) { + loss_algorithm_.reset(LossDetectionInterface::Create(kTime)); + } send_algorithm_->SetFromConfig(config, is_server_); } -void QuicSentPacketManager::SetMaxPacketSize(QuicByteCount max_packet_size) { - send_algorithm_->SetMaxPacketSize(max_packet_size); -} - +// TODO(ianswett): Combine this method with OnPacketSent once packets are always +// sent in order and the connection tracks RetransmittableFrames for longer. void QuicSentPacketManager::OnSerializedPacket( const SerializedPacket& serialized_packet) { - if (serialized_packet.retransmittable_frames == NULL && - !serialized_packet.packet->is_fec_packet()) { - // Don't track ack/congestion feedback packets. - return; + if (serialized_packet.retransmittable_frames) { + ack_notifier_manager_.OnSerializedPacket(serialized_packet); } - ack_notifier_manager_.OnSerializedPacket(serialized_packet); - - DCHECK(unacked_packets_.empty() || - unacked_packets_.rbegin()->first < serialized_packet.sequence_number); - unacked_packets_[serialized_packet.sequence_number] = - TransmissionInfo(serialized_packet.retransmittable_frames, - serialized_packet.sequence_number_length); + unacked_packets_.AddPacket(serialized_packet); } void QuicSentPacketManager::OnRetransmittedPacket( QuicPacketSequenceNumber old_sequence_number, QuicPacketSequenceNumber new_sequence_number) { - DCHECK(ContainsKey(unacked_packets_, old_sequence_number)); - DCHECK(ContainsKey(pending_retransmissions_, old_sequence_number)); - DCHECK(unacked_packets_.empty() || - unacked_packets_.rbegin()->first < new_sequence_number); - - pending_retransmissions_.erase(old_sequence_number); - - UnackedPacketMap::iterator unacked_it = - unacked_packets_.find(old_sequence_number); - RetransmittableFrames* frames = unacked_it->second.retransmittable_frames; - DCHECK(frames); + TransmissionType transmission_type; + PendingRetransmissionMap::iterator it = + pending_retransmissions_.find(old_sequence_number); + if (it != pending_retransmissions_.end()) { + transmission_type = it->second; + pending_retransmissions_.erase(it); + } else { + DLOG(DFATAL) << "Expected sequence number to be in " + "pending_retransmissions_. sequence_number: " << old_sequence_number; + transmission_type = NOT_RETRANSMISSION; + } // A notifier may be waiting to hear about ACKs for the original sequence // number. Inform them that the sequence number has changed. ack_notifier_manager_.UpdateSequenceNumber(old_sequence_number, new_sequence_number); - // We keep the old packet in the unacked packet list until it, or one of - // the retransmissions of it are acked. - unacked_it->second.retransmittable_frames = NULL; - unacked_packets_[new_sequence_number] = - TransmissionInfo(frames, GetSequenceNumberLength(old_sequence_number)); - - // Keep track of all sequence numbers that this packet - // has been transmitted as. - SequenceNumberSet* previous_transmissions = - unacked_it->second.previous_transmissions; - if (previous_transmissions == NULL) { - // This is the first retransmission of this packet, so create a new entry. - previous_transmissions = new SequenceNumberSet; - unacked_it->second.previous_transmissions = previous_transmissions; - previous_transmissions->insert(old_sequence_number); - } - previous_transmissions->insert(new_sequence_number); - unacked_packets_[new_sequence_number].previous_transmissions = - previous_transmissions; - - DCHECK(HasRetransmittableFrames(new_sequence_number)); -} - -bool QuicSentPacketManager::OnIncomingAck( - const ReceivedPacketInfo& received_info, QuicTime ack_receive_time) { - // Determine if the least unacked sequence number is being acked. - QuicPacketSequenceNumber least_unacked_sent_before = - GetLeastUnackedSentPacket(); - bool new_least_unacked = !IsAwaitingPacket(received_info, - least_unacked_sent_before); + unacked_packets_.OnRetransmittedPacket(old_sequence_number, + new_sequence_number, + transmission_type); +} - HandleAckForSentPackets(received_info); +void QuicSentPacketManager::OnIncomingAck( + const ReceivedPacketInfo& received_info, + QuicTime ack_receive_time) { + QuicByteCount bytes_in_flight = unacked_packets_.bytes_in_flight(); - SequenceNumberSet retransmission_packets = - OnIncomingAckFrame(received_info, ack_receive_time); + // We rely on delta_time_largest_observed to compute an RTT estimate, so + // we only update rtt when the largest observed gets acked. + bool largest_observed_acked = MaybeUpdateRTT(received_info, ack_receive_time); + if (largest_observed_ < received_info.largest_observed) { + largest_observed_ = received_info.largest_observed; + unacked_packets_.IncreaseLargestObserved(largest_observed_); + } + HandleAckForSentPackets(received_info); + InvokeLossDetection(ack_receive_time); + MaybeInvokeCongestionEvent(largest_observed_acked, bytes_in_flight); - for (SequenceNumberSet::const_iterator it = retransmission_packets.begin(); - it != retransmission_packets.end(); ++it) { - DCHECK(!ContainsKey(pending_packets_, *it)); - MarkForRetransmission(*it, NACK_RETRANSMISSION); + // If we have received a truncated ack, then we need to clear out some + // previous transmissions to allow the peer to actually ACK new packets. + if (received_info.is_truncated) { + unacked_packets_.ClearPreviousRetransmissions( + received_info.missing_packets.size() / 2); } - if (new_least_unacked) { + // Anytime we are making forward progress and have a new RTT estimate, reset + // the backoff counters. + if (largest_observed_acked) { + // Reset all retransmit counters any time a new packet is acked. consecutive_rto_count_ = 0; + consecutive_tlp_count_ = 0; + consecutive_crypto_retransmission_count_ = 0; } - - return new_least_unacked; } -void QuicSentPacketManager::DiscardUnackedPacket( - QuicPacketSequenceNumber sequence_number) { - MarkPacketReceivedByPeer(sequence_number); +void QuicSentPacketManager::MaybeInvokeCongestionEvent( + bool rtt_updated, QuicByteCount bytes_in_flight) { + if (rtt_updated || !packets_acked_.empty() || + !packets_lost_.empty()) { + send_algorithm_->OnCongestionEvent( + rtt_updated, bytes_in_flight, packets_acked_, packets_lost_); + packets_acked_.clear(); + packets_lost_.clear(); + } } void QuicSentPacketManager::HandleAckForSentPackets( const ReceivedPacketInfo& received_info) { // Go through the packets we have not received an ack for and see if this // incoming_ack shows they've been seen by the peer. - UnackedPacketMap::iterator it = unacked_packets_.begin(); + QuicTime::Delta delta_largest_observed = + received_info.delta_time_largest_observed; + QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); while (it != unacked_packets_.end()) { QuicPacketSequenceNumber sequence_number = it->first; if (sequence_number > received_info.largest_observed) { - // These are very new sequence_numbers. + // These packets are still in flight. break; } if (IsAwaitingPacket(received_info, sequence_number)) { + // Consider it multiple nacks when there is a gap between the missing + // packet and the largest observed, since the purpose of a nack + // threshold is to tolerate re-ordering. This handles both StretchAcks + // and Forward Acks. + // The nack count only increases when the largest observed increases. + size_t min_nacks = received_info.largest_observed - sequence_number; + // Truncated acks can nack the largest observed, so use a min of 1. + if (min_nacks == 0) { + min_nacks = 1; + } + unacked_packets_.NackPacket(sequence_number, min_nacks); ++it; continue; } - // Packet was acked, so remove it from our unacked packet list. - DVLOG(1) << ENDPOINT <<"Got an ack for packet " << sequence_number; + DVLOG(1) << ENDPOINT << "Got an ack for packet " << sequence_number; // If data is associated with the most recent transmission of this // packet, then inform the caller. - it = MarkPacketReceivedByPeer(sequence_number); - - // The AckNotifierManager is informed of every ACKed sequence number. - ack_notifier_manager_.OnPacketAcked(sequence_number); - } - - // If we have received a truncated ack, then we need to - // clear out some previous transmissions to allow the peer - // to actually ACK new packets. - if (received_info.is_truncated) { - ClearPreviousRetransmissions(received_info.missing_packets.size() / 2); - } -} - -void QuicSentPacketManager::ClearPreviousRetransmissions(size_t num_to_clear) { - UnackedPacketMap::iterator it = unacked_packets_.begin(); - while (it != unacked_packets_.end() && num_to_clear > 0) { - QuicPacketSequenceNumber sequence_number = it->first; - // If this is not a previous transmission then there is no point - // in clearing out any further packets, because it will not affect - // the high water mark. - SequenceNumberSet* previous_transmissions = - it->second.previous_transmissions; - if (previous_transmissions == NULL) { - break; - } - QuicPacketSequenceNumber newest_transmission = - *previous_transmissions->rbegin(); - if (sequence_number == newest_transmission) { - break; + if (it->second.in_flight) { + packets_acked_[sequence_number] = it->second; } + it = MarkPacketHandled(it, delta_largest_observed); + } - DCHECK(it->second.retransmittable_frames == NULL); - previous_transmissions->erase(sequence_number); - if (previous_transmissions->size() == 1) { - unacked_packets_[newest_transmission].previous_transmissions = NULL; - delete previous_transmissions; - } - unacked_packets_.erase(it++); - --num_to_clear; + // Discard any retransmittable frames associated with revived packets. + for (SequenceNumberSet::const_iterator revived_it = + received_info.revived_packets.begin(); + revived_it != received_info.revived_packets.end(); ++revived_it) { + MarkPacketRevived(*revived_it, delta_largest_observed); } } bool QuicSentPacketManager::HasRetransmittableFrames( QuicPacketSequenceNumber sequence_number) const { - if (!ContainsKey(unacked_packets_, sequence_number)) { - return false; - } - - return unacked_packets_.find( - sequence_number)->second.retransmittable_frames != NULL; + return unacked_packets_.HasRetransmittableFrames(sequence_number); } void QuicSentPacketManager::RetransmitUnackedPackets( RetransmissionType retransmission_type) { - if (unacked_packets_.empty()) { - return; + QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); + while (it != unacked_packets_.end()) { + const RetransmittableFrames* frames = it->second.retransmittable_frames; + // TODO(ianswett): Consider adding a new retransmission type which removes + // all these old packets from unacked and retransmits them as new sequence + // numbers with no connection to the previous ones. + if (frames != NULL && (retransmission_type == ALL_PACKETS || + frames->encryption_level() == ENCRYPTION_INITIAL)) { + MarkForRetransmission(it->first, ALL_UNACKED_RETRANSMISSION); + } + ++it; } +} - for (UnackedPacketMap::const_iterator unacked_it = unacked_packets_.begin(); - unacked_it != unacked_packets_.end(); ++unacked_it) { - const RetransmittableFrames* frames = - unacked_it->second.retransmittable_frames; - if (frames == NULL) { - continue; - } - if (retransmission_type == ALL_PACKETS || - frames->encryption_level() == ENCRYPTION_INITIAL) { - // TODO(satyamshekhar): Think about congestion control here. - // Specifically, about the retransmission count of packets being sent - // proactively to achieve 0 (minimal) RTT. - OnPacketAbandoned(unacked_it->first); - if (!MarkForRetransmission(unacked_it->first, NACK_RETRANSMISSION)) { - DiscardUnackedPacket(unacked_it->first); - } +void QuicSentPacketManager::NeuterUnencryptedPackets() { + QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); + while (it != unacked_packets_.end()) { + const RetransmittableFrames* frames = it->second.retransmittable_frames; + QuicPacketSequenceNumber sequence_number = it->first; + ++it; + if (frames != NULL && frames->encryption_level() == ENCRYPTION_NONE) { + // Once you're forward secure, no unencrypted packets will be sent, crypto + // or otherwise. Unencrypted packets are neutered and abandoned, to ensure + // they are not retransmitted or considered lost from a congestion control + // perspective. + pending_retransmissions_.erase(sequence_number); + unacked_packets_.RemoveFromInFlight(sequence_number); + // RemoveRetransmittibility is safe because only the newest sequence + // number can have frames. + unacked_packets_.RemoveRetransmittability(sequence_number); } } } -bool QuicSentPacketManager::MarkForRetransmission( +void QuicSentPacketManager::MarkForRetransmission( QuicPacketSequenceNumber sequence_number, TransmissionType transmission_type) { - DCHECK(ContainsKey(unacked_packets_, sequence_number)); - if (!HasRetransmittableFrames(sequence_number)) { - return false; - } - // If it's already in the retransmission map, don't add it again, just let - // the prior retransmission request win out. + const TransmissionInfo& transmission_info = + unacked_packets_.GetTransmissionInfo(sequence_number); + LOG_IF(DFATAL, transmission_info.retransmittable_frames == NULL); + if (transmission_type != TLP_RETRANSMISSION) { + unacked_packets_.RemoveFromInFlight(sequence_number); + } + // TODO(ianswett): Currently the RTO can fire while there are pending NACK + // retransmissions for the same data, which is not ideal. if (ContainsKey(pending_retransmissions_, sequence_number)) { - return true; + return; } pending_retransmissions_[sequence_number] = transmission_type; - return true; +} + +void QuicSentPacketManager::RecordSpuriousRetransmissions( + const SequenceNumberSet& all_transmissions, + QuicPacketSequenceNumber acked_sequence_number) { + for (SequenceNumberSet::const_iterator + it = all_transmissions.upper_bound(acked_sequence_number), + end = all_transmissions.end(); + it != end; + ++it) { + const TransmissionInfo& retransmit_info = + unacked_packets_.GetTransmissionInfo(*it); + + stats_->bytes_spuriously_retransmitted += retransmit_info.bytes_sent; + ++stats_->packets_spuriously_retransmitted; + if (debug_delegate_ != NULL) { + debug_delegate_->OnSpuriousPacketRetransmition( + retransmit_info.transmission_type, + retransmit_info.bytes_sent); + } + } } bool QuicSentPacketManager::HasPendingRetransmissions() const { @@ -321,353 +316,347 @@ QuicSentPacketManager::PendingRetransmission DCHECK(!pending_retransmissions_.empty()); QuicPacketSequenceNumber sequence_number = pending_retransmissions_.begin()->first; - DCHECK(ContainsKey(unacked_packets_, sequence_number)); - const RetransmittableFrames* retransmittable_frames = - unacked_packets_[sequence_number].retransmittable_frames; - DCHECK(retransmittable_frames); + TransmissionType transmission_type = pending_retransmissions_.begin()->second; + if (unacked_packets_.HasPendingCryptoPackets()) { + // Ensure crypto packets are retransmitted before other packets. + PendingRetransmissionMap::const_iterator it = + pending_retransmissions_.begin(); + do { + if (HasCryptoHandshake(unacked_packets_.GetTransmissionInfo(it->first))) { + sequence_number = it->first; + transmission_type = it->second; + break; + } + ++it; + } while (it != pending_retransmissions_.end()); + } + DCHECK(unacked_packets_.IsUnacked(sequence_number)) << sequence_number; + const TransmissionInfo& transmission_info = + unacked_packets_.GetTransmissionInfo(sequence_number); + DCHECK(transmission_info.retransmittable_frames); return PendingRetransmission(sequence_number, - pending_retransmissions_.begin()->second, - *retransmittable_frames, - GetSequenceNumberLength(sequence_number)); + transmission_type, + *transmission_info.retransmittable_frames, + transmission_info.sequence_number_length); } -bool QuicSentPacketManager::IsPreviousTransmission( - QuicPacketSequenceNumber sequence_number) const { - DCHECK(ContainsKey(unacked_packets_, sequence_number)); - - UnackedPacketMap::const_iterator it = unacked_packets_.find(sequence_number); - if (it->second.previous_transmissions == NULL) { - return false; +void QuicSentPacketManager::MarkPacketRevived( + QuicPacketSequenceNumber sequence_number, + QuicTime::Delta delta_largest_observed) { + if (!unacked_packets_.IsUnacked(sequence_number)) { + return; } - SequenceNumberSet* previous_transmissions = it->second.previous_transmissions; - DCHECK(!previous_transmissions->empty()); - return *previous_transmissions->rbegin() != sequence_number; -} - -QuicSentPacketManager::UnackedPacketMap::iterator -QuicSentPacketManager::MarkPacketReceivedByPeer( - QuicPacketSequenceNumber sequence_number) { - DCHECK(ContainsKey(unacked_packets_, sequence_number)); - - // If this packet has never been retransmitted, then simply drop it. - UnackedPacketMap::const_iterator previous_it = - unacked_packets_.find(sequence_number); - if (previous_it->second.previous_transmissions == NULL) { - UnackedPacketMap::iterator next_unacked = - unacked_packets_.find(sequence_number); - ++next_unacked; - DiscardPacket(sequence_number); - return next_unacked; - } - - SequenceNumberSet* previous_transmissions = - previous_it->second.previous_transmissions; - DCHECK(!previous_transmissions->empty()); - SequenceNumberSet::reverse_iterator previous_transmissions_it = - previous_transmissions->rbegin(); - QuicPacketSequenceNumber newest_transmission = *previous_transmissions_it; - if (newest_transmission == sequence_number) { - DiscardPacket(newest_transmission); - } else { - // If we have received an ack for a previous transmission of a packet, - // we want to keep the "new" transmission of the packet unacked, - // but prevent the data from being retransmitted. - delete unacked_packets_[newest_transmission].retransmittable_frames; - unacked_packets_[newest_transmission].retransmittable_frames = NULL; - unacked_packets_[newest_transmission].previous_transmissions = NULL; - pending_retransmissions_.erase(newest_transmission); + const TransmissionInfo& transmission_info = + unacked_packets_.GetTransmissionInfo(sequence_number); + QuicPacketSequenceNumber newest_transmission = + *transmission_info.all_transmissions->rbegin(); + // This packet has been revived at the receiver. If we were going to + // retransmit it, do not retransmit it anymore. + pending_retransmissions_.erase(newest_transmission); + + // The AckNotifierManager needs to be notified for revived packets, + // since it indicates the packet arrived from the appliction's perspective. + if (transmission_info.retransmittable_frames) { + ack_notifier_manager_.OnPacketAcked( + newest_transmission, delta_largest_observed); + } + + unacked_packets_.RemoveRetransmittability(sequence_number); +} + +QuicUnackedPacketMap::const_iterator QuicSentPacketManager::MarkPacketHandled( + QuicUnackedPacketMap::const_iterator it, + QuicTime::Delta delta_largest_observed) { + LOG_IF(DFATAL, it == unacked_packets_.end()) + << "MarkPacketHandled must be passed a valid iterator entry."; + const QuicPacketSequenceNumber sequence_number = it->first; + const TransmissionInfo& transmission_info = it->second; + + QuicPacketSequenceNumber newest_transmission = + *transmission_info.all_transmissions->rbegin(); + // Remove the most recent packet, if it is pending retransmission. + pending_retransmissions_.erase(newest_transmission); + + // Notify observers about the ACKed packet. + { + // The AckNotifierManager needs to be notified about the most recent + // transmission, since that's the one only one it tracks. + ack_notifier_manager_.OnPacketAcked(newest_transmission, + delta_largest_observed); + if (newest_transmission != sequence_number) { + RecordSpuriousRetransmissions(*transmission_info.all_transmissions, + sequence_number); + } } - // Clear out information all previous transmissions. - ++previous_transmissions_it; - while (previous_transmissions_it != previous_transmissions->rend()) { - QuicPacketSequenceNumber previous_transmission = *previous_transmissions_it; - ++previous_transmissions_it; - DiscardPacket(previous_transmission); - } + // Two cases for MarkPacketHandled: + // 1) Handle the most recent or a crypto packet, so remove all transmissions. + // 2) Handle old transmission, keep all other pending transmissions, + // but disassociate them from one another. - delete previous_transmissions; + // If it's a crypto handshake packet, discard it and all retransmissions, + // since they won't be acked now that one has been processed. + // TODO(ianswett): Instead of handling all crypto packets in a special way, + // only handle NULL encrypted packets in a special way. + if (HasCryptoHandshake( + unacked_packets_.GetTransmissionInfo(newest_transmission))) { + unacked_packets_.RemoveFromInFlight(newest_transmission); + } + unacked_packets_.RemoveFromInFlight(sequence_number); + unacked_packets_.RemoveRetransmittability(sequence_number); - UnackedPacketMap::iterator next_unacked = unacked_packets_.begin(); + QuicUnackedPacketMap::const_iterator next_unacked = unacked_packets_.begin(); while (next_unacked != unacked_packets_.end() && - next_unacked->first < sequence_number) { + next_unacked->first <= sequence_number) { ++next_unacked; } return next_unacked; } -void QuicSentPacketManager::DiscardPacket( - QuicPacketSequenceNumber sequence_number) { - UnackedPacketMap::iterator unacked_it = - unacked_packets_.find(sequence_number); - // Packet was not meant to be retransmitted. - if (unacked_it == unacked_packets_.end()) { - return; - } - - // Delete the retransmittable frames. - delete unacked_it->second.retransmittable_frames; - unacked_packets_.erase(unacked_it); - pending_retransmissions_.erase(sequence_number); - return; -} - bool QuicSentPacketManager::IsUnacked( QuicPacketSequenceNumber sequence_number) const { - return ContainsKey(unacked_packets_, sequence_number); -} - -QuicSequenceNumberLength QuicSentPacketManager::GetSequenceNumberLength( - QuicPacketSequenceNumber sequence_number) const { - DCHECK(ContainsKey(unacked_packets_, sequence_number)); - - return unacked_packets_.find(sequence_number)->second.sequence_number_length; + return unacked_packets_.IsUnacked(sequence_number); } bool QuicSentPacketManager::HasUnackedPackets() const { - return !unacked_packets_.empty(); -} - -size_t QuicSentPacketManager::GetNumRetransmittablePackets() const { - size_t num_unacked_packets = 0; - for (UnackedPacketMap::const_iterator it = unacked_packets_.begin(); - it != unacked_packets_.end(); ++it) { - QuicPacketSequenceNumber sequence_number = it->first; - if (HasRetransmittableFrames(sequence_number)) { - ++num_unacked_packets; - } - } - return num_unacked_packets; + return unacked_packets_.HasUnackedPackets(); } QuicPacketSequenceNumber QuicSentPacketManager::GetLeastUnackedSentPacket() const { - if (unacked_packets_.empty()) { - // If there are no unacked packets, set the least unacked packet to - // the sequence number of the next packet sent. - return helper_->GetNextPacketSequenceNumber(); - } - - return unacked_packets_.begin()->first; + return unacked_packets_.GetLeastUnackedSentPacket(); } -SequenceNumberSet QuicSentPacketManager::GetUnackedPackets() const { - SequenceNumberSet unacked_packets; - for (UnackedPacketMap::const_iterator it = unacked_packets_.begin(); - it != unacked_packets_.end(); ++it) { - unacked_packets.insert(it->first); - } - return unacked_packets; -} - -void QuicSentPacketManager::OnPacketSent( +bool QuicSentPacketManager::OnPacketSent( QuicPacketSequenceNumber sequence_number, QuicTime sent_time, QuicByteCount bytes, TransmissionType transmission_type, HasRetransmittableData has_retransmittable_data) { DCHECK_LT(0u, sequence_number); - DCHECK(!ContainsKey(pending_packets_, sequence_number)); - if (ContainsKey(unacked_packets_, sequence_number)) { - unacked_packets_[sequence_number].sent_time = sent_time; + LOG_IF(DFATAL, bytes == 0) << "Cannot send empty packets."; + pending_tlp_transmission_ = false; + // In rare circumstances, the packet could be serialized, sent, and then acked + // before OnPacketSent is called. + if (!unacked_packets_.IsUnacked(sequence_number)) { + return false; } - // Only track packets the send algorithm wants us to track. - if (!send_algorithm_->OnPacketSent(sent_time, sequence_number, bytes, - transmission_type, - has_retransmittable_data)) { - return; + if (unacked_packets_.bytes_in_flight() == 0) { + // TODO(ianswett): Consider being less aggressive to force a new + // recent_min_rtt, likely by not discarding a relatively new sample. + DVLOG(1) << "Sampling a new recent min rtt within 2 samples. currently:" + << rtt_stats_.recent_min_rtt().ToMilliseconds() << "ms"; + rtt_stats_.SampleNewRecentMinRtt(kNumMinRttSamplesAfterQuiescence); } - packet_history_map_[sequence_number] = new SendAlgorithmInterface::SentPacket( - bytes, sent_time, has_retransmittable_data); - pending_packets_.insert(sequence_number); - CleanupPacketHistory(); + + // Only track packets as in flight that the send algorithm wants us to track. + const bool in_flight = + send_algorithm_->OnPacketSent(sent_time, + unacked_packets_.bytes_in_flight(), + sequence_number, + bytes, + has_retransmittable_data); + unacked_packets_.SetSent(sequence_number, sent_time, bytes, in_flight); + + // Reset the retransmission timer anytime a pending packet is sent. + return in_flight; } void QuicSentPacketManager::OnRetransmissionTimeout() { - // Abandon all pending packets to ensure the congestion window - // opens up before we attempt to retransmit packets. - QuicTime::Delta retransmission_delay = GetRetransmissionDelay(); - QuicTime max_send_time = - clock_->ApproximateNow().Subtract(retransmission_delay); - for (SequenceNumberSet::iterator it = pending_packets_.begin(); - it != pending_packets_.end();) { - QuicPacketSequenceNumber sequence_number = *it; - DCHECK(ContainsKey(packet_history_map_, sequence_number)); - DCHECK(ContainsKey(unacked_packets_, sequence_number)); - const TransmissionInfo& transmission_info = - unacked_packets_.find(sequence_number)->second; - // Abandon retransmittable packet and old non-retransmittable packets. - if (transmission_info.retransmittable_frames || - transmission_info.sent_time <= max_send_time) { - pending_packets_.erase(it++); - send_algorithm_->OnPacketAbandoned( - sequence_number, packet_history_map_[sequence_number]->bytes_sent()); - } else { - ++it; + DCHECK(unacked_packets_.HasInFlightPackets()); + DCHECK(!pending_tlp_transmission_); + // Handshake retransmission, timer based loss detection, TLP, and RTO are + // implemented with a single alarm. The handshake alarm is set when the + // handshake has not completed, the loss alarm is set when the loss detection + // algorithm says to, and the TLP and RTO alarms are set after that. + // The TLP alarm is always set to run for under an RTO. + switch (GetRetransmissionMode()) { + case HANDSHAKE_MODE: + ++stats_->crypto_retransmit_count; + RetransmitCryptoPackets(); + return; + case LOSS_MODE: { + ++stats_->loss_timeout_count; + QuicByteCount bytes_in_flight = unacked_packets_.bytes_in_flight(); + InvokeLossDetection(clock_->Now()); + MaybeInvokeCongestionEvent(false, bytes_in_flight); + return; } + case TLP_MODE: + // If no tail loss probe can be sent, because there are no retransmittable + // packets, execute a conventional RTO to abandon old packets. + ++stats_->tlp_count; + ++consecutive_tlp_count_; + pending_tlp_transmission_ = true; + // TLPs prefer sending new data instead of retransmitting data, so + // give the connection a chance to write before completing the TLP. + return; + case RTO_MODE: + ++stats_->rto_count; + RetransmitAllPackets(); + return; } +} - // Attempt to send all the unacked packets when the RTO fires, let the - // congestion manager decide how many to send immediately and the remaining - // packets will be queued for future sending. - DVLOG(1) << "OnRetransmissionTimeout() fired with " - << unacked_packets_.size() << " unacked packets."; +void QuicSentPacketManager::RetransmitCryptoPackets() { + DCHECK_EQ(HANDSHAKE_MODE, GetRetransmissionMode()); + // TODO(ianswett): Typical TCP implementations only retransmit 5 times. + consecutive_crypto_retransmission_count_ = + min(kMaxHandshakeRetransmissionBackoffs, + consecutive_crypto_retransmission_count_ + 1); + bool packet_retransmitted = false; + for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); + it != unacked_packets_.end(); ++it) { + QuicPacketSequenceNumber sequence_number = it->first; + const RetransmittableFrames* frames = it->second.retransmittable_frames; + // Only retransmit frames which are in flight, and therefore have been sent. + if (!it->second.in_flight || frames == NULL || + frames->HasCryptoHandshake() != IS_HANDSHAKE) { + continue; + } + packet_retransmitted = true; + MarkForRetransmission(sequence_number, HANDSHAKE_RETRANSMISSION); + } + DCHECK(packet_retransmitted) << "No crypto packets found to retransmit."; +} - // Retransmit any packet with retransmittable frames. - bool packets_retransmitted = false; - for (UnackedPacketMap::const_iterator it = unacked_packets_.begin(); +bool QuicSentPacketManager::MaybeRetransmitTailLossProbe() { + if (!pending_tlp_transmission_) { + return false; + } + for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); it != unacked_packets_.end(); ++it) { - if (it->second.retransmittable_frames != NULL) { + QuicPacketSequenceNumber sequence_number = it->first; + const RetransmittableFrames* frames = it->second.retransmittable_frames; + // Only retransmit frames which are in flight, and therefore have been sent. + if (!it->second.in_flight || frames == NULL) { + continue; + } + DCHECK_NE(IS_HANDSHAKE, frames->HasCryptoHandshake()); + MarkForRetransmission(sequence_number, TLP_RETRANSMISSION); + return true; + } + DLOG(FATAL) + << "No retransmittable packets, so RetransmitOldestPacket failed."; + return false; +} + +void QuicSentPacketManager::RetransmitAllPackets() { + DVLOG(1) << "RetransmitAllPackets() called with " + << unacked_packets_.GetNumUnackedPackets() << " unacked packets."; + // Request retransmission of all retransmittable packets when the RTO + // fires, and let the congestion manager decide how many to send + // immediately and the remaining packets will be queued. + // Abandon any non-retransmittable packets that are sufficiently old. + bool packets_retransmitted = false; + QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); + while (it != unacked_packets_.end()) { + const RetransmittableFrames* frames = it->second.retransmittable_frames; + QuicPacketSequenceNumber sequence_number = it->first; + ++it; + if (frames != NULL) { packets_retransmitted = true; - MarkForRetransmission(it->first, RTO_RETRANSMISSION); + MarkForRetransmission(sequence_number, RTO_RETRANSMISSION); + } else { + unacked_packets_.RemoveFromInFlight(sequence_number); } } - // Only inform the sent packet manager of an RTO if data was retransmitted. + send_algorithm_->OnRetransmissionTimeout(packets_retransmitted); if (packets_retransmitted) { ++consecutive_rto_count_; - send_algorithm_->OnRetransmissionTimeout(); } } -void QuicSentPacketManager::OnPacketAbandoned( - QuicPacketSequenceNumber sequence_number) { - SequenceNumberSet::iterator it = pending_packets_.find(sequence_number); - if (it != pending_packets_.end()) { - DCHECK(ContainsKey(packet_history_map_, sequence_number)); - send_algorithm_->OnPacketAbandoned( - sequence_number, packet_history_map_[sequence_number]->bytes_sent()); - pending_packets_.erase(it); +QuicSentPacketManager::RetransmissionTimeoutMode + QuicSentPacketManager::GetRetransmissionMode() const { + DCHECK(unacked_packets_.HasInFlightPackets()); + if (unacked_packets_.HasPendingCryptoPackets()) { + return HANDSHAKE_MODE; + } + if (loss_algorithm_->GetLossTimeout() != QuicTime::Zero()) { + return LOSS_MODE; + } + if (consecutive_tlp_count_ < max_tail_loss_probes_) { + if (unacked_packets_.HasUnackedRetransmittableFrames()) { + return TLP_MODE; + } } + return RTO_MODE; } void QuicSentPacketManager::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& frame, const QuicTime& feedback_receive_time) { send_algorithm_->OnIncomingQuicCongestionFeedbackFrame( - frame, feedback_receive_time, packet_history_map_); + frame, feedback_receive_time); } -SequenceNumberSet QuicSentPacketManager::OnIncomingAckFrame( - const ReceivedPacketInfo& received_info, - const QuicTime& ack_receive_time) { - MaybeUpdateRTT(received_info, ack_receive_time); - - // We want to. - // * Get all packets lower(including) than largest_observed - // from pending_packets_. - // * Remove all packets no longer being waited for(ie: acked). - // * Send each ACK in the list to send_algorithm_. - SequenceNumberSet::iterator it = pending_packets_.begin(); - SequenceNumberSet::iterator it_upper = - pending_packets_.upper_bound(received_info.largest_observed); - - SequenceNumberSet retransmission_packets; - SequenceNumberSet lost_packets; - while (it != it_upper) { - QuicPacketSequenceNumber sequence_number = *it; - const SendAlgorithmInterface::SentPacket* sent_packet = - packet_history_map_[sequence_number]; - if (!IsAwaitingPacket(received_info, sequence_number)) { - // Not missing, hence implicitly acked. - size_t bytes_sent = sent_packet->bytes_sent(); - send_algorithm_->OnPacketAcked(sequence_number, bytes_sent, rtt_sample_); - pending_packets_.erase(it++); // Must be incremented post to work. - continue; - } - - // The peer got packets after this sequence number. This is an explicit - // nack. - DVLOG(1) << "still missing packet " << sequence_number; - DCHECK(ContainsKey(packet_history_map_, sequence_number)); - // Consider it multiple nacks when there is a gap between the missing packet - // and the largest observed, since the purpose of a nack threshold is to - // tolerate re-ordering. This handles both StretchAcks and Forward Acks. - // TODO(ianswett): This relies heavily on sequential reception of packets, - // and makes an assumption that the congestion control uses TCP style nacks. - size_t min_nacks = received_info.largest_observed - sequence_number; - packet_history_map_[sequence_number]->Nack(min_nacks); - - size_t num_nacks_needed = kNumberOfNacksBeforeRetransmission; - // Check for early retransmit(RFC5827) when the last packet gets acked and - // the there are fewer than 4 pending packets. - if (pending_packets_.size() <= kNumberOfNacksBeforeRetransmission && - sent_packet->has_retransmittable_data() == HAS_RETRANSMITTABLE_DATA && - *pending_packets_.rbegin() == received_info.largest_observed) { - num_nacks_needed = received_info.largest_observed - sequence_number; - } - - if (sent_packet->nack_count() < num_nacks_needed) { - ++it; - continue; - } - - // If the number of retransmissions has maxed out, don't lose or retransmit - // any more packets. - if (retransmission_packets.size() >= kMaxRetransmissionsPerAck) { - ++it; - continue; - } - - lost_packets.insert(sequence_number); - if (sent_packet->has_retransmittable_data() == HAS_RETRANSMITTABLE_DATA) { - retransmission_packets.insert(sequence_number); - } - - ++it; - } - // Abandon packets after the loop over pending packets, because otherwise it - // changes the early retransmit logic and iteration. +void QuicSentPacketManager::InvokeLossDetection(QuicTime time) { + SequenceNumberSet lost_packets = + loss_algorithm_->DetectLostPackets(unacked_packets_, + time, + largest_observed_, + rtt_stats_); for (SequenceNumberSet::const_iterator it = lost_packets.begin(); it != lost_packets.end(); ++it) { - // TODO(ianswett): OnPacketLost is also called from TCPCubicSender when - // an FEC packet is lost, but FEC loss information should be shared among - // congestion managers. Additionally, if it's expected the FEC packet may - // repair the loss, it should be recorded as a loss to the congestion - // manager, but not retransmitted until it's known whether the FEC packet - // arrived. - send_algorithm_->OnPacketLost(*it, ack_receive_time); - OnPacketAbandoned(*it); + QuicPacketSequenceNumber sequence_number = *it; + const TransmissionInfo& transmission_info = + unacked_packets_.GetTransmissionInfo(sequence_number); + // TODO(ianswett): If it's expected the FEC packet may repair the loss, it + // should be recorded as a loss to the send algorithm, but not retransmitted + // until it's known whether the FEC packet arrived. + ++stats_->packets_lost; + packets_lost_[sequence_number] = transmission_info; + DVLOG(1) << ENDPOINT << "Lost packet " << sequence_number; + + if (transmission_info.retransmittable_frames != NULL) { + MarkForRetransmission(sequence_number, LOSS_RETRANSMISSION); + } else { + // Since we will not retransmit this, we need to remove it from + // unacked_packets_. This is either the current transmission of + // a packet whose previous transmission has been acked, a packet that has + // been TLP retransmitted, or an FEC packet. + unacked_packets_.RemoveFromInFlight(sequence_number); + } } - - return retransmission_packets; } -void QuicSentPacketManager::MaybeUpdateRTT( +bool QuicSentPacketManager::MaybeUpdateRTT( const ReceivedPacketInfo& received_info, const QuicTime& ack_receive_time) { + if (!unacked_packets_.IsUnacked(received_info.largest_observed)) { + return false; + } // We calculate the RTT based on the highest ACKed sequence number, the lower // sequence numbers will include the ACK aggregation delay. - SendAlgorithmInterface::SentPacketsMap::iterator history_it = - packet_history_map_.find(received_info.largest_observed); - // TODO(satyamshekhar): largest_observed might be missing. - if (history_it == packet_history_map_.end()) { - return; + const TransmissionInfo& transmission_info = + unacked_packets_.GetTransmissionInfo(received_info.largest_observed); + // Don't update the RTT if it hasn't been sent. + if (transmission_info.sent_time == QuicTime::Zero()) { + return false; } - QuicTime::Delta send_delta = ack_receive_time.Subtract( - history_it->second->send_timestamp()); - if (send_delta > received_info.delta_time_largest_observed) { - rtt_sample_ = send_delta.Subtract( - received_info.delta_time_largest_observed); - } else if (rtt_sample_.IsInfinite()) { - // Even though we received information from the peer suggesting - // an invalid (negative) RTT, we can use the send delta as an - // approximation until we get a better estimate. - rtt_sample_ = send_delta; - } + QuicTime::Delta send_delta = + ack_receive_time.Subtract(transmission_info.sent_time); + rtt_stats_.UpdateRtt( + send_delta, received_info.delta_time_largest_observed, ack_receive_time); + return true; } QuicTime::Delta QuicSentPacketManager::TimeUntilSend( QuicTime now, - TransmissionType transmission_type, - HasRetransmittableData retransmittable, - IsHandshake handshake) { - return send_algorithm_->TimeUntilSend(now, transmission_type, retransmittable, - handshake); + HasRetransmittableData retransmittable) { + // The TLP logic is entirely contained within QuicSentPacketManager, so the + // send algorithm does not need to be consulted. + if (pending_tlp_transmission_) { + return QuicTime::Delta::Zero(); + } + return send_algorithm_->TimeUntilSend( + now, unacked_packets_.bytes_in_flight(), retransmittable); } // Ensures that the Delayed Ack timer is always set to a value lesser @@ -682,52 +671,91 @@ QuicTime::Delta QuicSentPacketManager::TimeUntilSend( // different MinRTO, we may get spurious retransmissions. May not have // any benefits, but if the delayed ack becomes a significant source // of (likely, tail) latency, then consider such a mechanism. - -const QuicTime::Delta QuicSentPacketManager::DelayedAckTime() { +const QuicTime::Delta QuicSentPacketManager::DelayedAckTime() const { return QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs/2); } -const QuicTime::Delta QuicSentPacketManager::GetRetransmissionDelay() const { - size_t number_retransmissions = consecutive_rto_count_; - if (FLAGS_limit_rto_increase_for_tests) { - const size_t kTailDropWindowSize = 5; - const size_t kTailDropMaxRetransmissions = 4; - if (pending_packets_.size() <= kTailDropWindowSize) { - // Avoid exponential backoff of RTO when there are only a few packets - // outstanding. This helps avoid the situation where fake packet loss - // causes a packet and it's retransmission to be dropped causing - // test timouts. - if (number_retransmissions <= kTailDropMaxRetransmissions) { - number_retransmissions = 0; - } else { - number_retransmissions -= kTailDropMaxRetransmissions; - } +const QuicTime QuicSentPacketManager::GetRetransmissionTime() const { + // Don't set the timer if there are no packets in flight or we've already + // queued a tlp transmission and it hasn't been sent yet. + if (!unacked_packets_.HasInFlightPackets() || pending_tlp_transmission_) { + return QuicTime::Zero(); + } + switch (GetRetransmissionMode()) { + case HANDSHAKE_MODE: + return clock_->ApproximateNow().Add(GetCryptoRetransmissionDelay()); + case LOSS_MODE: + return loss_algorithm_->GetLossTimeout(); + case TLP_MODE: { + // TODO(ianswett): When CWND is available, it would be preferable to + // set the timer based on the earliest retransmittable packet. + // Base the updated timer on the send time of the last packet. + const QuicTime sent_time = unacked_packets_.GetLastPacketSentTime(); + const QuicTime tlp_time = sent_time.Add(GetTailLossProbeDelay()); + // Ensure the TLP timer never gets set to a time in the past. + return QuicTime::Max(clock_->ApproximateNow(), tlp_time); } + case RTO_MODE: { + // The RTO is based on the first outstanding packet. + const QuicTime sent_time = + unacked_packets_.GetFirstInFlightPacketSentTime(); + QuicTime rto_timeout = sent_time.Add(GetRetransmissionDelay()); + // Always wait at least 1.5 * RTT from now. + QuicTime min_timeout = clock_->ApproximateNow().Add( + rtt_stats_.SmoothedRtt().Multiply(1.5)); + + return QuicTime::Max(min_timeout, rto_timeout); + } + } + DCHECK(false); + return QuicTime::Zero(); +} + +const QuicTime::Delta QuicSentPacketManager::GetCryptoRetransmissionDelay() + const { + // This is equivalent to the TailLossProbeDelay, but slightly more aggressive + // because crypto handshake messages don't incur a delayed ack time. + int64 delay_ms = max<int64>(kMinHandshakeTimeoutMs, + 1.5 * rtt_stats_.SmoothedRtt().ToMilliseconds()); + return QuicTime::Delta::FromMilliseconds( + delay_ms << consecutive_crypto_retransmission_count_); +} + +const QuicTime::Delta QuicSentPacketManager::GetTailLossProbeDelay() const { + QuicTime::Delta srtt = rtt_stats_.SmoothedRtt(); + if (!unacked_packets_.HasMultipleInFlightPackets()) { + return QuicTime::Delta::Max( + srtt.Multiply(1.5).Add(DelayedAckTime()), srtt.Multiply(2)); } + return QuicTime::Delta::FromMilliseconds( + max(kMinTailLossProbeTimeoutMs, + static_cast<int64>(2 * srtt.ToMilliseconds()))); +} +const QuicTime::Delta QuicSentPacketManager::GetRetransmissionDelay() const { QuicTime::Delta retransmission_delay = send_algorithm_->RetransmissionDelay(); + // TODO(rch): This code should move to |send_algorithm_|. if (retransmission_delay.IsZero()) { // We are in the initial state, use default timeout values. retransmission_delay = QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs); + } else if (retransmission_delay.ToMilliseconds() < kMinRetransmissionTimeMs) { + retransmission_delay = + QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs); } + // Calculate exponential back off. - retransmission_delay = QuicTime::Delta::FromMilliseconds( - retransmission_delay.ToMilliseconds() * static_cast<size_t>( - (1 << min<size_t>(number_retransmissions, kMaxRetransmissions)))); + retransmission_delay = retransmission_delay.Multiply( + 1 << min<size_t>(consecutive_rto_count_, kMaxRetransmissions)); - // TODO(rch): This code should move to |send_algorithm_|. - if (retransmission_delay.ToMilliseconds() < kMinRetransmissionTimeMs) { - return QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs); - } if (retransmission_delay.ToMilliseconds() > kMaxRetransmissionTimeMs) { return QuicTime::Delta::FromMilliseconds(kMaxRetransmissionTimeMs); } return retransmission_delay; } -const QuicTime::Delta QuicSentPacketManager::SmoothedRtt() const { - return send_algorithm_->SmoothedRtt(); +const RttStats* QuicSentPacketManager::GetRttStats() const { + return &rtt_stats_; } QuicBandwidth QuicSentPacketManager::BandwidthEstimate() const { @@ -738,27 +766,6 @@ QuicByteCount QuicSentPacketManager::GetCongestionWindow() const { return send_algorithm_->GetCongestionWindow(); } -void QuicSentPacketManager::CleanupPacketHistory() { - const QuicTime::Delta kHistoryPeriod = - QuicTime::Delta::FromMilliseconds(kHistoryPeriodMs); - QuicTime now = clock_->ApproximateNow(); - - SendAlgorithmInterface::SentPacketsMap::iterator history_it = - packet_history_map_.begin(); - for (; history_it != packet_history_map_.end(); ++history_it) { - if (now.Subtract(history_it->second->send_timestamp()) <= kHistoryPeriod) { - return; - } - // Don't remove packets which have not been acked. - if (ContainsKey(pending_packets_, history_it->first)) { - continue; - } - delete history_it->second; - packet_history_map_.erase(history_it); - history_it = packet_history_map_.begin(); - } -} - void QuicSentPacketManager::MaybeEnablePacing() { if (!FLAGS_enable_quic_pacing) { return; @@ -768,10 +775,11 @@ void QuicSentPacketManager::MaybeEnablePacing() { return; } + // Set up a pacing sender with a 5 millisecond alarm granularity. using_pacing_ = true; send_algorithm_.reset( new PacingSender(send_algorithm_.release(), - QuicTime::Delta::FromMicroseconds(1))); + QuicTime::Delta::FromMilliseconds(5))); } } // namespace net diff --git a/chromium/net/quic/quic_sent_packet_manager.h b/chromium/net/quic/quic_sent_packet_manager.h index 71c61e93c03..2e75786119a 100644 --- a/chromium/net/quic/quic_sent_packet_manager.h +++ b/chromium/net/quic/quic_sent_packet_manager.h @@ -14,14 +14,14 @@ #include <vector> #include "base/containers/hash_tables.h" +#include "base/memory/scoped_ptr.h" #include "net/base/linked_hash_map.h" +#include "net/quic/congestion_control/loss_detection_interface.h" +#include "net/quic/congestion_control/rtt_stats.h" #include "net/quic/congestion_control/send_algorithm_interface.h" #include "net/quic/quic_ack_notifier_manager.h" #include "net/quic/quic_protocol.h" - -NET_EXPORT_PRIVATE extern bool FLAGS_track_retransmission_history; -NET_EXPORT_PRIVATE extern bool FLAGS_limit_rto_increase_for_tests; -NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_pacing; +#include "net/quic/quic_unacked_packet_map.h" namespace net { @@ -32,6 +32,7 @@ class QuicSentPacketManagerPeer; class QuicClock; class QuicConfig; +struct QuicConnectionStats; // Class which tracks the set of packets sent on a QUIC connection and contains // a send algorithm to decide when to send new packets. It keeps track of any @@ -40,6 +41,19 @@ class QuicConfig; // previous transmission is acked, the data will not be retransmitted. class NET_EXPORT_PRIVATE QuicSentPacketManager { public: + // Interface which gets callbacks from the QuicSentPacketManager at + // interesting points. Implementations must not mutate the state of + // the packet manager or connection as a result of these callbacks. + class NET_EXPORT_PRIVATE DebugDelegate { + public: + virtual ~DebugDelegate() {} + + // Called when a spurious retransmission is detected. + virtual void OnSpuriousPacketRetransmition( + TransmissionType transmission_type, + QuicByteCount byte_size) {} + }; + // Struct to store the pending retransmission information. struct PendingRetransmission { PendingRetransmission(QuicPacketSequenceNumber sequence_number, @@ -58,25 +72,15 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { QuicSequenceNumberLength sequence_number_length; }; - // Interface which provides callbacks that the manager needs. - class NET_EXPORT_PRIVATE HelperInterface { - public: - virtual ~HelperInterface(); - - // Called to return the sequence number of the next packet to be sent. - virtual QuicPacketSequenceNumber GetNextPacketSequenceNumber() = 0; - }; - QuicSentPacketManager(bool is_server, - HelperInterface* helper, const QuicClock* clock, - CongestionFeedbackType congestion_type); + QuicConnectionStats* stats, + CongestionFeedbackType congestion_type, + LossDetectionType loss_type); virtual ~QuicSentPacketManager(); virtual void SetFromConfig(const QuicConfig& config); - virtual void SetMaxPacketSize(QuicByteCount max_packet_size); - // Called when a new packet is serialized. If the packet contains // retransmittable data, it will be added to the unacked packet map. void OnSerializedPacket(const SerializedPacket& serialized_packet); @@ -87,22 +91,24 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { void OnRetransmittedPacket(QuicPacketSequenceNumber old_sequence_number, QuicPacketSequenceNumber new_sequence_number); - // Processes the incoming ack and returns true if the retransmission or ack - // alarm should be reset. - bool OnIncomingAck(const ReceivedPacketInfo& received_info, + // Processes the incoming ack. + void OnIncomingAck(const ReceivedPacketInfo& received_info, QuicTime ack_receive_time); - // Discards any information for the packet corresponding to |sequence_number|. - // If this packet has been retransmitted, information on those packets - // will be discarded as well. - void DiscardUnackedPacket(QuicPacketSequenceNumber sequence_number); - // Returns true if the non-FEC packet |sequence_number| is unacked. bool IsUnacked(QuicPacketSequenceNumber sequence_number) const; // Requests retransmission of all unacked packets of |retransmission_type|. void RetransmitUnackedPackets(RetransmissionType retransmission_type); + // Retransmits the oldest pending packet there is still a tail loss probe + // pending. Invoked after OnRetransmissionTimeout. + bool MaybeRetransmitTailLossProbe(); + + // Removes the retransmittable frames from all unencrypted packets to ensure + // they don't get retransmitted. + void NeuterUnencryptedPackets(); + // Returns true if the unacked packet |sequence_number| has retransmittable // frames. This will only return false if the packet has been acked, if a // previous transmission of this packet was ACK'd, or if this packet has been @@ -117,39 +123,19 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { bool HasUnackedPackets() const; - // Returns the number of unacked packets which have retransmittable frames. - size_t GetNumRetransmittablePackets() const; - - // Returns the smallest sequence number of a sent packet which has not been - // acked by the peer. Excludes any packets which have been retransmitted - // with a new sequence number. If all packets have been acked, returns the - // sequence number of the next packet that will be sent. + // Returns the smallest sequence number of a serialized packet which has not + // been acked by the peer. If there are no unacked packets, returns 0. QuicPacketSequenceNumber GetLeastUnackedSentPacket() const; - // Returns the set of sequence numbers of all unacked packets. - // Test only. - SequenceNumberSet GetUnackedPackets() const; - - // Returns true if |sequence_number| is a previous transmission of packet. - bool IsPreviousTransmission(QuicPacketSequenceNumber sequence_number) const; - - // TODO(ianswett): Combine the congestion control related methods below with - // some of the methods above and cleanup the resulting code. - // Called when we have received an ack frame from peer. - // Returns a set containing all the sequence numbers to be nack retransmitted - // as a result of the ack. - virtual SequenceNumberSet OnIncomingAckFrame( - const ReceivedPacketInfo& received_info, - const QuicTime& ack_receive_time); - // Called when a congestion feedback frame is received from peer. virtual void OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& frame, const QuicTime& feedback_receive_time); // Called when we have sent bytes to the peer. This informs the manager both - // the number of bytes sent and if they were retransmitted. - virtual void OnPacketSent(QuicPacketSequenceNumber sequence_number, + // the number of bytes sent and if they were retransmitted. Returns true if + // the sender should reset the retransmission timer. + virtual bool OnPacketSent(QuicPacketSequenceNumber sequence_number, QuicTime sent_time, QuicByteCount bytes, TransmissionType transmission_type, @@ -158,28 +144,23 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { // Called when the retransmission timer expires. virtual void OnRetransmissionTimeout(); - // Called when a packet is timed out, such as an RTO. Removes the bytes from - // the congestion manager, but does not change the congestion window size. - virtual void OnPacketAbandoned(QuicPacketSequenceNumber sequence_number); - // 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(QuicTime now, - TransmissionType transmission_type, - HasRetransmittableData retransmittable, - IsHandshake handshake); + HasRetransmittableData retransmittable); // Returns amount of time for delayed ack timer. - const QuicTime::Delta DelayedAckTime(); + const QuicTime::Delta DelayedAckTime() const; - // Returns the current RTO delay. - const QuicTime::Delta GetRetransmissionDelay() const; + // Returns the current delay for the retransmission timer, which may send + // either a tail loss probe or do a full RTO. Returns QuicTime::Zero() if + // there are no retransmittable packets. + const QuicTime GetRetransmissionTime() const; - // Returns the estimated smoothed RTT calculated by the congestion algorithm. - const QuicTime::Delta SmoothedRtt() const; + const RttStats* GetRttStats() const; // Returns the estimated bandwidth calculated by the congestion algorithm. QuicBandwidth BandwidthEstimate() const; @@ -195,73 +176,91 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { bool using_pacing() const { return using_pacing_; } + void set_debug_delegate(DebugDelegate* debug_delegate) { + debug_delegate_ = debug_delegate; + } private: friend class test::QuicConnectionPeer; friend class test::QuicSentPacketManagerPeer; - struct TransmissionInfo { - TransmissionInfo() - : retransmittable_frames(NULL), - sequence_number_length(PACKET_1BYTE_SEQUENCE_NUMBER), - sent_time(QuicTime::Zero()), - previous_transmissions(NULL) { } - TransmissionInfo(RetransmittableFrames* retransmittable_frames, - QuicSequenceNumberLength sequence_number_length) - : retransmittable_frames(retransmittable_frames), - sequence_number_length(sequence_number_length), - sent_time(QuicTime::Zero()), - previous_transmissions(NULL) { - } - - RetransmittableFrames* retransmittable_frames; - QuicSequenceNumberLength sequence_number_length; - // Zero when the packet is serialized, non-zero once it's sent. - QuicTime sent_time; - // Stores all previous transmissions if the packet has been retransmitted, - // and is NULL otherwise. - SequenceNumberSet* previous_transmissions; + // The retransmission timer is a single timer which switches modes depending + // upon connection state. + enum RetransmissionTimeoutMode { + // A conventional TCP style RTO. + RTO_MODE, + // A tail loss probe. By default, QUIC sends up to two before RTOing. + TLP_MODE, + // Retransmission of handshake packets prior to handshake completion. + HANDSHAKE_MODE, + // Re-invoke the loss detection when a packet is not acked before the + // loss detection algorithm expects. + LOSS_MODE, }; typedef linked_hash_map<QuicPacketSequenceNumber, - TransmissionInfo> UnackedPacketMap; - typedef linked_hash_map<QuicPacketSequenceNumber, TransmissionType> PendingRetransmissionMap; - typedef base::hash_map<QuicPacketSequenceNumber, SequenceNumberSet*> - PreviousTransmissionMap; // Process the incoming ack looking for newly ack'd data packets. void HandleAckForSentPackets(const ReceivedPacketInfo& received_info); + // Returns the current retransmission mode. + RetransmissionTimeoutMode GetRetransmissionMode() const; + + // Retransmits all crypto stream packets. + void RetransmitCryptoPackets(); + + // Retransmits all the packets and abandons by invoking a full RTO. + void RetransmitAllPackets(); + + // Returns the timer for retransmitting crypto handshake packets. + const QuicTime::Delta GetCryptoRetransmissionDelay() const; + + // Returns the timer for a new tail loss probe. + const QuicTime::Delta GetTailLossProbeDelay() const; + + // Returns the retransmission timeout, after which a full RTO occurs. + const QuicTime::Delta GetRetransmissionDelay() const; + // Update the RTT if the ack is for the largest acked sequence number. - void MaybeUpdateRTT(const ReceivedPacketInfo& received_info, + // Returns true if the rtt was updated. + bool MaybeUpdateRTT(const ReceivedPacketInfo& received_info, const QuicTime& ack_receive_time); - // Marks |sequence_number| as having been seen by the peer. Returns an - // iterator to the next remaining unacked packet. - UnackedPacketMap::iterator MarkPacketReceivedByPeer( - QuicPacketSequenceNumber sequence_number); - - // Simply removes the entries, if any, from the unacked packet map - // and the retransmission map. - void DiscardPacket(QuicPacketSequenceNumber sequence_number); + // Invokes the loss detection algorithm and loses and retransmits packets if + // necessary. + void InvokeLossDetection(QuicTime time); + + // Invokes OnCongestionEvent if |rtt_updated| is true, there are pending acks, + // or pending losses. Clears pending acks and pending losses afterwards. + // |bytes_in_flight| is the number of bytes in flight before the losses or + // acks. + void MaybeInvokeCongestionEvent(bool rtt_updated, + QuicByteCount bytes_in_flight); + + // Marks |sequence_number| as having been revived by the peer, but not + // received, so the packet remains pending if it is and the congestion control + // does not consider the packet acked. + void MarkPacketRevived(QuicPacketSequenceNumber sequence_number, + QuicTime::Delta delta_largest_observed); + + // Removes the retransmittability and pending properties from the packet at + // |it| due to receipt by the peer. Returns an iterator to the next remaining + // unacked packet. + QuicUnackedPacketMap::const_iterator MarkPacketHandled( + QuicUnackedPacketMap::const_iterator it, + QuicTime::Delta delta_largest_observed); // Request that |sequence_number| be retransmitted after the other pending - // retransmissions. Returns false if there are no retransmittable frames for - // |sequence_number| and true if it will be retransmitted. - bool MarkForRetransmission(QuicPacketSequenceNumber sequence_number, + // retransmissions. Does not add it to the retransmissions if it's already + // a pending retransmission. + void MarkForRetransmission(QuicPacketSequenceNumber sequence_number, TransmissionType transmission_type); - // Returns the length of the serialized sequence number for - // the packet |sequence_number|. - QuicSequenceNumberLength GetSequenceNumberLength( - QuicPacketSequenceNumber sequence_number) const; - - // Clears up to |num_to_clear| previous transmissions in order to make room - // in the ack frame for new acks. - void ClearPreviousRetransmissions(size_t num_to_clear); - - void CleanupPacketHistory(); + // Notify observers about spurious retransmits. + void RecordSpuriousRetransmissions( + const SequenceNumberSet& all_transmissions, + QuicPacketSequenceNumber acked_sequence_number); // Newly serialized retransmittable and fec packets are added to this map, // which contains owning pointers to any contained frames. If a packet is @@ -271,7 +270,7 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { // If the old packet is acked before the new packet, then the old entry will // be removed from the map and the new entry's retransmittable frames will be // set to NULL. - UnackedPacketMap unacked_packets_; + QuicUnackedPacketMap unacked_packets_; // Pending retransmissions which have not been packetized and sent yet. PendingRetransmissionMap pending_retransmissions_; @@ -279,25 +278,35 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { // Tracks if the connection was created by the server. bool is_server_; - HelperInterface* helper_; - // An AckNotifier can register to be informed when ACKs have been received for // all packets that a given block of data was sent in. The AckNotifierManager // maintains the currently active notifiers. AckNotifierManager ack_notifier_manager_; const QuicClock* clock_; + QuicConnectionStats* stats_; + DebugDelegate* debug_delegate_; + RttStats rtt_stats_; scoped_ptr<SendAlgorithmInterface> send_algorithm_; - // Tracks the send time, size, and nack count of sent packets. Packets are - // removed after 5 seconds and they've been removed from pending_packets_. - SendAlgorithmInterface::SentPacketsMap packet_history_map_; - // Packets that are outstanding and have not been abandoned or lost. - SequenceNumberSet pending_packets_; - QuicTime::Delta rtt_sample_; // RTT estimate from the most recent ACK. + scoped_ptr<LossDetectionInterface> loss_algorithm_; + + QuicPacketSequenceNumber largest_observed_; // From the most recent ACK. // Number of times the RTO timer has fired in a row without receiving an ack. size_t consecutive_rto_count_; + // Number of times the tail loss probe has been sent. + size_t consecutive_tlp_count_; + // Number of times the crypto handshake has been retransmitted. + size_t consecutive_crypto_retransmission_count_; + // Whether a tlp packet can be sent even if the send algorithm says not to. + bool pending_tlp_transmission_; + // Maximum number of tail loss probes to send before firing an RTO. + size_t max_tail_loss_probes_; bool using_pacing_; + // Sets of packets acked and lost as a result of the last congestion event. + SendAlgorithmInterface::CongestionMap packets_acked_; + SendAlgorithmInterface::CongestionMap packets_lost_; + DISALLOW_COPY_AND_ASSIGN(QuicSentPacketManager); }; diff --git a/chromium/net/quic/quic_sent_packet_manager_test.cc b/chromium/net/quic/quic_sent_packet_manager_test.cc index 168cbf378cf..28f433216d9 100644 --- a/chromium/net/quic/quic_sent_packet_manager_test.cc +++ b/chromium/net/quic/quic_sent_packet_manager_test.cc @@ -5,42 +5,65 @@ #include "net/quic/quic_sent_packet_manager.h" #include "base/stl_util.h" +#include "net/quic/test_tools/quic_config_peer.h" #include "net/quic/test_tools/quic_sent_packet_manager_peer.h" #include "net/quic/test_tools/quic_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using std::vector; -using testing::_; +using testing::ElementsAre; +using testing::Pair; +using testing::Pointwise; using testing::Return; using testing::StrictMock; +using testing::_; namespace net { namespace test { namespace { -class MockHelper : public QuicSentPacketManager::HelperInterface { +// Default packet length. +const uint32 kDefaultLength = 1000; + +// Matcher to check the key of the key-value pair it receives as first argument +// equals its second argument. +MATCHER(KeyEq, "") { + return std::tr1::get<0>(arg).first == std::tr1::get<1>(arg); +} + +class MockDebugDelegate : public QuicSentPacketManager::DebugDelegate { public: - MOCK_METHOD0(GetNextPacketSequenceNumber, QuicPacketSequenceNumber()); + MOCK_METHOD2(OnSpuriousPacketRetransmition, + void(TransmissionType transmission_type, + QuicByteCount byte_size)); }; class QuicSentPacketManagerTest : public ::testing::TestWithParam<bool> { protected: QuicSentPacketManagerTest() - : manager_(true, &helper_, &clock_, kFixRate), + : manager_(true, &clock_, &stats_, kFixRate, kNack), send_algorithm_(new StrictMock<MockSendAlgorithm>) { QuicSentPacketManagerPeer::SetSendAlgorithm(&manager_, send_algorithm_); + // Disable tail loss probes for most tests. + QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 0); + // Advance the time 1s so the send times are never QuicTime::Zero. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000)); } - ~QuicSentPacketManagerTest() { + virtual ~QuicSentPacketManagerTest() OVERRIDE { STLDeleteElements(&packets_); } + QuicByteCount BytesInFlight() { + return QuicSentPacketManagerPeer::GetBytesInFlight(&manager_); + } void VerifyUnackedPackets(QuicPacketSequenceNumber* packets, size_t num_packets) { if (num_packets == 0) { EXPECT_FALSE(manager_.HasUnackedPackets()); - EXPECT_EQ(0u, manager_.GetNumRetransmittablePackets()); + EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetNumRetransmittablePackets( + &manager_)); return; } @@ -53,102 +76,188 @@ class QuicSentPacketManagerTest : public ::testing::TestWithParam<bool> { void VerifyRetransmittablePackets(QuicPacketSequenceNumber* packets, size_t num_packets) { - SequenceNumberSet unacked = manager_.GetUnackedPackets(); + EXPECT_EQ(num_packets, + QuicSentPacketManagerPeer::GetNumRetransmittablePackets( + &manager_)); for (size_t i = 0; i < num_packets; ++i) { - EXPECT_TRUE(ContainsKey(unacked, packets[i])) << packets[i]; - } - size_t num_retransmittable = 0; - for (SequenceNumberSet::const_iterator it = unacked.begin(); - it != unacked.end(); ++it) { - if (manager_.HasRetransmittableFrames(*it)) { - ++num_retransmittable; - } + EXPECT_TRUE(manager_.HasRetransmittableFrames(packets[i])) + << " packets[" << i << "]:" << packets[i]; } - EXPECT_EQ(num_packets, manager_.GetNumRetransmittablePackets()); - EXPECT_EQ(num_packets, num_retransmittable); } - void VerifyAckedPackets(QuicPacketSequenceNumber* expected, - size_t num_expected, - const SequenceNumberSet& actual) { - if (num_expected == 0) { - EXPECT_TRUE(actual.empty()); - return; - } + void ExpectAck(QuicPacketSequenceNumber largest_observed) { + EXPECT_CALL(*send_algorithm_, OnCongestionEvent( + true, _, ElementsAre(Pair(largest_observed, _)), _)); + } + + void ExpectUpdatedRtt(QuicPacketSequenceNumber largest_observed) { + EXPECT_CALL(*send_algorithm_, + OnCongestionEvent(true, _, _, _)); + } + + void ExpectAckAndLoss(bool rtt_updated, + QuicPacketSequenceNumber largest_observed, + QuicPacketSequenceNumber lost_packet) { + EXPECT_CALL(*send_algorithm_, OnCongestionEvent( + rtt_updated, _, ElementsAre(Pair(largest_observed, _)), + ElementsAre(Pair(lost_packet, _)))); + } - EXPECT_EQ(num_expected, actual.size()); - for (size_t i = 0; i < num_expected; ++i) { - EXPECT_TRUE(ContainsKey(actual, expected[i])) << expected[i]; + // |packets_acked| and |packets_lost| should be in sequence number order. + void ExpectAcksAndLosses(bool rtt_updated, + QuicPacketSequenceNumber* packets_acked, + size_t num_packets_acked, + QuicPacketSequenceNumber* packets_lost, + size_t num_packets_lost) { + vector<QuicPacketSequenceNumber> ack_vector; + for (size_t i = 0; i < num_packets_acked; ++i) { + ack_vector.push_back(packets_acked[i]); + } + vector<QuicPacketSequenceNumber> lost_vector; + for (size_t i = 0; i < num_packets_lost; ++i) { + lost_vector.push_back(packets_lost[i]); } + EXPECT_CALL(*send_algorithm_, + OnCongestionEvent(rtt_updated, _, + Pointwise(KeyEq(), ack_vector), + Pointwise(KeyEq(), lost_vector))); } + // Retransmits a packet as though it was a TLP retransmission, because TLP + // leaves the |old_sequence_number| pending. + // TODO(ianswett): Test with transmission types besides TLP. void RetransmitPacket(QuicPacketSequenceNumber old_sequence_number, QuicPacketSequenceNumber new_sequence_number) { QuicSentPacketManagerPeer::MarkForRetransmission( - &manager_, old_sequence_number, NACK_RETRANSMISSION); + &manager_, old_sequence_number, TLP_RETRANSMISSION); EXPECT_TRUE(manager_.HasPendingRetransmissions()); QuicSentPacketManager::PendingRetransmission next_retransmission = manager_.NextPendingRetransmission(); EXPECT_EQ(old_sequence_number, next_retransmission.sequence_number); - EXPECT_EQ(NACK_RETRANSMISSION, next_retransmission.transmission_type); - manager_.OnRetransmittedPacket(old_sequence_number, new_sequence_number); + EXPECT_EQ(TLP_RETRANSMISSION, + next_retransmission.transmission_type); + manager_.OnRetransmittedPacket(old_sequence_number, + new_sequence_number); EXPECT_TRUE(QuicSentPacketManagerPeer::IsRetransmission( &manager_, new_sequence_number)); } - SerializedPacket CreatePacket(QuicPacketSequenceNumber sequence_number) { + void RetransmitAndSendPacket(QuicPacketSequenceNumber old_sequence_number, + QuicPacketSequenceNumber new_sequence_number) { + RetransmitPacket(old_sequence_number, new_sequence_number); + + EXPECT_CALL(*send_algorithm_, + OnPacketSent(_, BytesInFlight(), new_sequence_number, + kDefaultLength, HAS_RETRANSMITTABLE_DATA)) + .WillOnce(Return(true)); + manager_.OnPacketSent(new_sequence_number, + clock_.Now(), + kDefaultLength, + LOSS_RETRANSMISSION, + HAS_RETRANSMITTABLE_DATA); + } + + SerializedPacket CreateDataPacket(QuicPacketSequenceNumber sequence_number) { + return CreatePacket(sequence_number, true); + } + + SerializedPacket CreatePacket(QuicPacketSequenceNumber sequence_number, + bool retransmittable) { packets_.push_back(QuicPacket::NewDataPacket( - NULL, 0, false, PACKET_8BYTE_GUID, false, + NULL, kDefaultLength, false, PACKET_8BYTE_CONNECTION_ID, false, PACKET_6BYTE_SEQUENCE_NUMBER)); - return SerializedPacket(sequence_number, PACKET_6BYTE_SEQUENCE_NUMBER, - packets_.back(), 0u, new RetransmittableFrames()); + return SerializedPacket( + sequence_number, PACKET_6BYTE_SEQUENCE_NUMBER, + packets_.back(), 0u, + retransmittable ? new RetransmittableFrames() : NULL); } SerializedPacket CreateFecPacket(QuicPacketSequenceNumber sequence_number) { packets_.push_back(QuicPacket::NewFecPacket( - NULL, 0, false, PACKET_8BYTE_GUID, false, + NULL, kDefaultLength, false, PACKET_8BYTE_CONNECTION_ID, false, PACKET_6BYTE_SEQUENCE_NUMBER)); return SerializedPacket(sequence_number, PACKET_6BYTE_SEQUENCE_NUMBER, packets_.back(), 0u, NULL); } void SendDataPacket(QuicPacketSequenceNumber sequence_number) { - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, sequence_number, _, _, _)) + EXPECT_CALL(*send_algorithm_, + OnPacketSent(_, BytesInFlight(), sequence_number, _, _)) + .Times(1).WillOnce(Return(true)); + SerializedPacket packet(CreateDataPacket(sequence_number)); + manager_.OnSerializedPacket(packet); + manager_.OnPacketSent(sequence_number, clock_.Now(), + packet.packet->length(), NOT_RETRANSMISSION, + HAS_RETRANSMITTABLE_DATA); + } + + void SendCryptoPacket(QuicPacketSequenceNumber sequence_number) { + EXPECT_CALL(*send_algorithm_, + OnPacketSent(_, BytesInFlight(), sequence_number, + kDefaultLength, HAS_RETRANSMITTABLE_DATA)) .Times(1).WillOnce(Return(true)); - SerializedPacket packet(CreatePacket(sequence_number)); + SerializedPacket packet(CreateDataPacket(sequence_number)); + packet.retransmittable_frames->AddStreamFrame( + new QuicStreamFrame(1, false, 0, IOVector())); + packet.retransmittable_frames->set_encryption_level(ENCRYPTION_NONE); manager_.OnSerializedPacket(packet); manager_.OnPacketSent(sequence_number, clock_.ApproximateNow(), packet.packet->length(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); } + void SendFecPacket(QuicPacketSequenceNumber sequence_number) { + EXPECT_CALL(*send_algorithm_, + OnPacketSent(_, BytesInFlight(), sequence_number, + kDefaultLength, NO_RETRANSMITTABLE_DATA)) + .Times(1).WillOnce(Return(true)); + SerializedPacket packet(CreateFecPacket(sequence_number)); + manager_.OnSerializedPacket(packet); + manager_.OnPacketSent(sequence_number, clock_.ApproximateNow(), + packet.packet->length(), NOT_RETRANSMISSION, + NO_RETRANSMITTABLE_DATA); + } + + void SendAckPacket(QuicPacketSequenceNumber sequence_number) { + EXPECT_CALL(*send_algorithm_, + OnPacketSent(_, BytesInFlight(), sequence_number, + kDefaultLength, NO_RETRANSMITTABLE_DATA)) + .Times(1).WillOnce(Return(false)); + SerializedPacket packet(CreatePacket(sequence_number, false)); + manager_.OnSerializedPacket(packet); + manager_.OnPacketSent(sequence_number, clock_.Now(), + packet.packet->length(), NOT_RETRANSMISSION, + NO_RETRANSMITTABLE_DATA); + } + // Based on QuicConnection's WritePendingRetransmissions. void RetransmitNextPacket( QuicPacketSequenceNumber retransmission_sequence_number) { EXPECT_TRUE(manager_.HasPendingRetransmissions()); EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, retransmission_sequence_number, _, _, _)) + OnPacketSent(_, _, retransmission_sequence_number, + kDefaultLength, HAS_RETRANSMITTABLE_DATA)) .Times(1).WillOnce(Return(true)); const QuicSentPacketManager::PendingRetransmission pending = manager_.NextPendingRetransmission(); - manager_.OnRetransmittedPacket( - pending.sequence_number, retransmission_sequence_number); - manager_.OnPacketSent(retransmission_sequence_number, - clock_.ApproximateNow(), 1000, - pending.transmission_type, HAS_RETRANSMITTABLE_DATA); + manager_.OnRetransmittedPacket(pending.sequence_number, + retransmission_sequence_number); + manager_.OnPacketSent(retransmission_sequence_number, clock_.Now(), + kDefaultLength, pending.transmission_type, + HAS_RETRANSMITTABLE_DATA); } - testing::StrictMock<MockHelper> helper_; QuicSentPacketManager manager_; vector<QuicPacket*> packets_; MockClock clock_; + QuicConnectionStats stats_; MockSendAlgorithm* send_algorithm_; }; TEST_F(QuicSentPacketManagerTest, IsUnacked) { VerifyUnackedPackets(NULL, 0); - SerializedPacket serialized_packet(CreatePacket(1)); + SerializedPacket serialized_packet(CreateDataPacket(1)); manager_.OnSerializedPacket(serialized_packet); @@ -159,9 +268,7 @@ TEST_F(QuicSentPacketManagerTest, IsUnacked) { } TEST_F(QuicSentPacketManagerTest, IsUnAckedRetransmit) { - SerializedPacket serialized_packet(CreatePacket(1)); - - manager_.OnSerializedPacket(serialized_packet); + SendDataPacket(1); RetransmitPacket(1, 2); EXPECT_TRUE(QuicSentPacketManagerPeer::IsRetransmission(&manager_, 2)); @@ -172,34 +279,34 @@ TEST_F(QuicSentPacketManagerTest, IsUnAckedRetransmit) { } TEST_F(QuicSentPacketManagerTest, RetransmitThenAck) { - SerializedPacket serialized_packet(CreatePacket(1)); - - manager_.OnSerializedPacket(serialized_packet); - RetransmitPacket(1, 2); + SendDataPacket(1); + RetransmitAndSendPacket(1, 2); // Ack 2 but not 1. ReceivedPacketInfo received_info; received_info.largest_observed = 2; received_info.missing_packets.insert(1); - manager_.OnIncomingAck(received_info, QuicTime::Zero()); + ExpectAck(2); + manager_.OnIncomingAck(received_info, clock_.Now()); - // No unacked packets remain. - VerifyUnackedPackets(NULL, 0); + // Packet 1 is unacked, pending, but not retransmittable. + QuicPacketSequenceNumber unacked[] = { 1 }; + VerifyUnackedPackets(unacked, arraysize(unacked)); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); VerifyRetransmittablePackets(NULL, 0); } TEST_F(QuicSentPacketManagerTest, RetransmitThenAckBeforeSend) { - SerializedPacket serialized_packet(CreatePacket(1)); - - manager_.OnSerializedPacket(serialized_packet); + SendDataPacket(1); QuicSentPacketManagerPeer::MarkForRetransmission( - &manager_, 1, NACK_RETRANSMISSION); + &manager_, 1, TLP_RETRANSMISSION); EXPECT_TRUE(manager_.HasPendingRetransmissions()); // Ack 1. ReceivedPacketInfo received_info; received_info.largest_observed = 1; - manager_.OnIncomingAck(received_info, QuicTime::Zero()); + ExpectAck(1); + manager_.OnIncomingAck(received_info, clock_.Now()); // There should no longer be a pending retransmission. EXPECT_FALSE(manager_.HasPendingRetransmissions()); @@ -207,211 +314,311 @@ TEST_F(QuicSentPacketManagerTest, RetransmitThenAckBeforeSend) { // No unacked packets remain. VerifyUnackedPackets(NULL, 0); VerifyRetransmittablePackets(NULL, 0); + EXPECT_EQ(0u, stats_.packets_spuriously_retransmitted); } TEST_F(QuicSentPacketManagerTest, RetransmitThenAckPrevious) { - SerializedPacket serialized_packet(CreatePacket(1)); - - manager_.OnSerializedPacket(serialized_packet); + SendDataPacket(1); RetransmitPacket(1, 2); + QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15); + clock_.AdvanceTime(rtt); + + // Ack 1 but not 2. + ExpectAck(1); + ReceivedPacketInfo received_info; + received_info.largest_observed = 1; + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); + + // 2 should be unacked, since it may provide an RTT measurement. + QuicPacketSequenceNumber unacked[] = { 2 }; + VerifyUnackedPackets(unacked, arraysize(unacked)); + EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + VerifyRetransmittablePackets(NULL, 0); + + // Verify that the retransmission alarm would not fire, + // since there is no retransmittable data outstanding. + EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime()); + EXPECT_EQ(1u, stats_.packets_spuriously_retransmitted); +} + +TEST_F(QuicSentPacketManagerTest, RetransmitAndSendThenAckPrevious) { + SendDataPacket(1); + RetransmitAndSendPacket(1, 2); + QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15); + clock_.AdvanceTime(rtt); // Ack 1 but not 2. + ExpectAck(1); ReceivedPacketInfo received_info; received_info.largest_observed = 1; - manager_.OnIncomingAck(received_info, QuicTime::Zero()); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); // 2 remains unacked, but no packets have retransmittable data. QuicPacketSequenceNumber unacked[] = { 2 }; VerifyUnackedPackets(unacked, arraysize(unacked)); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); VerifyRetransmittablePackets(NULL, 0); - // Verify that if the retransmission alarm does fire to abandon packet 2, - // the sent packet manager is not notified, since there is no retransmittable - // data outstanding. - EXPECT_CALL(*send_algorithm_, RetransmissionDelay()) - .WillOnce(Return(QuicTime::Delta::FromMilliseconds(1))); + EXPECT_EQ(1u, stats_.packets_spuriously_retransmitted); +} + +TEST_F(QuicSentPacketManagerTest, RetransmitThenAckPreviousThenNackRetransmit) { + SendDataPacket(1); + RetransmitPacket(1, 2); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2, _, _)) + .WillOnce(Return(true)); + manager_.OnPacketSent(2, clock_.ApproximateNow(), kDefaultLength, + LOSS_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); + QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15); + clock_.AdvanceTime(rtt); + + // First, ACK packet 1 which makes packet 2 non-retransmittable. + ExpectAck(1); + ReceivedPacketInfo received_info; + received_info.largest_observed = 1; + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); + + SendDataPacket(3); + SendDataPacket(4); + SendDataPacket(5); + clock_.AdvanceTime(rtt); + + // Next, NACK packet 2 three times. + received_info.largest_observed = 3; + received_info.missing_packets.insert(2); + ExpectAck(3); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); + + received_info.largest_observed = 4; + ExpectAck(4); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); + + received_info.largest_observed = 5; + ExpectAckAndLoss(true, 5, 2); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); + + // No packets remain unacked. + VerifyUnackedPackets(NULL, 0); + EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + VerifyRetransmittablePackets(NULL, 0); + + // Verify that the retransmission alarm would not fire, + // since there is no retransmittable data outstanding. + EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime()); +} + +TEST_F(QuicSentPacketManagerTest, RetransmitTwiceThenAckPreviousBeforeSend) { + SendDataPacket(1); + RetransmitAndSendPacket(1, 2); + + // Fire the RTO, which will mark 2 for retransmission (but will not send it). + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); manager_.OnRetransmissionTimeout(); + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + + // Ack 1 but not 2, before 2 is able to be sent. + // Since 1 has been retransmitted, it has already been lost, and so the + // send algorithm is not informed that it has been ACK'd. + ReceivedPacketInfo received_info; + received_info.largest_observed = 1; + ExpectUpdatedRtt(1); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); + + // Since 2 was marked for retransmit, when 1 is acked, 2 is kept for RTT. + QuicPacketSequenceNumber unacked[] = { 2 }; + VerifyUnackedPackets(unacked, arraysize(unacked)); + EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + VerifyRetransmittablePackets(NULL, 0); + + // Verify that the retransmission alarm would not fire, + // since there is no retransmittable data outstanding. + EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime()); } TEST_F(QuicSentPacketManagerTest, RetransmitTwiceThenAckFirst) { - SerializedPacket serialized_packet(CreatePacket(1)); + StrictMock<MockDebugDelegate> debug_delegate; + EXPECT_CALL(debug_delegate, OnSpuriousPacketRetransmition( + TLP_RETRANSMISSION, kDefaultLength)).Times(2); + manager_.set_debug_delegate(&debug_delegate); - manager_.OnSerializedPacket(serialized_packet); - RetransmitPacket(1, 2); - RetransmitPacket(2, 3); + SendDataPacket(1); + RetransmitAndSendPacket(1, 2); + RetransmitAndSendPacket(2, 3); + QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15); + clock_.AdvanceTime(rtt); // Ack 1 but not 2 or 3. + ExpectAck(1); ReceivedPacketInfo received_info; received_info.largest_observed = 1; - manager_.OnIncomingAck(received_info, QuicTime::Zero()); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); - // 3 remains unacked, but no packets have retransmittable data. - QuicPacketSequenceNumber unacked[] = { 3 }; + // 2 and 3 remain unacked, but no packets have retransmittable data. + QuicPacketSequenceNumber unacked[] = { 2, 3 }; VerifyUnackedPackets(unacked, arraysize(unacked)); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); VerifyRetransmittablePackets(NULL, 0); - // Verify that if the retransmission alarm does fire to abandon packet 3, - // the sent packet manager is not notified, since there is no retransmittable - // data outstanding. - EXPECT_CALL(*send_algorithm_, RetransmissionDelay()) - .WillOnce(Return(QuicTime::Delta::FromMilliseconds(1))); - manager_.OnRetransmissionTimeout(); -} + // Ensure packet 2 is lost when 4 is sent and 3 and 4 are acked. + SendDataPacket(4); + received_info.largest_observed = 4; + received_info.missing_packets.insert(2); + QuicPacketSequenceNumber acked[] = { 3, 4 }; + ExpectAcksAndLosses(true, acked, arraysize(acked), NULL, 0); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); -TEST_F(QuicSentPacketManagerTest, TruncatedAck) { - SerializedPacket serialized_packet(CreatePacket(1)); + QuicPacketSequenceNumber unacked2[] = { 2 }; + VerifyUnackedPackets(unacked2, arraysize(unacked2)); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); - manager_.OnSerializedPacket(serialized_packet); - RetransmitPacket(1, 2); - RetransmitPacket(2, 3); - RetransmitPacket(3, 4); + SendDataPacket(5); + received_info.largest_observed = 5; + ExpectAckAndLoss(true, 5, 2); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); + + VerifyUnackedPackets(NULL, 0); + EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + EXPECT_EQ(2u, stats_.packets_spuriously_retransmitted); +} + +TEST_F(QuicSentPacketManagerTest, LoseButDontRetransmitRevivedPacket) { + SendDataPacket(1); + SendDataPacket(2); + SendFecPacket(3); + SendDataPacket(4); - // Truncated ack with 2 NACKs + // Ack 2 and 3, and mark 1 as revived. ReceivedPacketInfo received_info; - received_info.largest_observed = 2; + received_info.largest_observed = 3; received_info.missing_packets.insert(1); - received_info.missing_packets.insert(2); - received_info.is_truncated = true; - manager_.OnIncomingAck(received_info, QuicTime::Zero()); + received_info.revived_packets.insert(1); + QuicPacketSequenceNumber acked[] = { 2, 3 }; + ExpectAcksAndLosses(true, acked, arraysize(acked), NULL, 0); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); - // High water mark will be raised. - QuicPacketSequenceNumber unacked[] = { 2, 3, 4 }; + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + QuicPacketSequenceNumber unacked[] = { 1, 4 }; VerifyUnackedPackets(unacked, arraysize(unacked)); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); QuicPacketSequenceNumber retransmittable[] = { 4 }; VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable)); -} -TEST_F(QuicSentPacketManagerTest, SendDropAckRetransmitManyPackets) { - manager_.OnSerializedPacket(CreatePacket(1)); - manager_.OnSerializedPacket(CreatePacket(2)); - manager_.OnSerializedPacket(CreatePacket(3)); + // Ack the 4th packet and expect the 1st to be considered lost. + received_info.largest_observed = 4; + ExpectAckAndLoss(true, 4, 1); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); - { - // Ack packets 1 and 3. - ReceivedPacketInfo received_info; - received_info.largest_observed = 3; - received_info.missing_packets.insert(2); - manager_.OnIncomingAck(received_info, QuicTime::Zero()); - - QuicPacketSequenceNumber unacked[] = { 2 }; - VerifyUnackedPackets(unacked, arraysize(unacked)); - QuicPacketSequenceNumber retransmittable[] = { 2 }; - VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable)); - } - - manager_.OnSerializedPacket(CreatePacket(4)); - manager_.OnSerializedPacket(CreatePacket(5)); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + VerifyRetransmittablePackets(NULL, 0); +} - { - // Ack packets 5. - ReceivedPacketInfo received_info; - received_info.largest_observed = 5; - received_info.missing_packets.insert(2); - received_info.missing_packets.insert(4); - manager_.OnIncomingAck(received_info, QuicTime::Zero()); +TEST_F(QuicSentPacketManagerTest, MarkLostThenReviveAndDontRetransmitPacket) { + SendDataPacket(1); + SendDataPacket(2); + SendDataPacket(3); + SendDataPacket(4); + SendFecPacket(5); - QuicPacketSequenceNumber unacked[] = { 2, 4 }; - VerifyUnackedPackets(unacked, arraysize(unacked)); - QuicPacketSequenceNumber retransmittable[] = { 2, 4 }; - VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable)); - } + // Ack 2, 3, and 4, and expect the 1st to be considered lost. + ReceivedPacketInfo received_info; + received_info.largest_observed = 4; + received_info.missing_packets.insert(1); + QuicPacketSequenceNumber acked[] = { 2, 3, 4 }; + QuicPacketSequenceNumber lost[] = { 1 }; + ExpectAcksAndLosses(true, acked, arraysize(acked), lost, arraysize(lost)); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); - manager_.OnSerializedPacket(CreatePacket(6)); - manager_.OnSerializedPacket(CreatePacket(7)); + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + QuicPacketSequenceNumber unacked[] = { 1, 5 }; + VerifyUnackedPackets(unacked, arraysize(unacked)); + QuicPacketSequenceNumber retransmittable[] = { 1 }; + VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable)); - { - // Ack packets 7. - ReceivedPacketInfo received_info; - received_info.largest_observed = 7; - received_info.missing_packets.insert(2); - received_info.missing_packets.insert(4); - received_info.missing_packets.insert(6); - manager_.OnIncomingAck(received_info, QuicTime::Zero()); + // Ack 5th packet (FEC) and revive 1st packet. 1st packet should now be + // removed from pending retransmissions map. + received_info.largest_observed = 5; + received_info.revived_packets.insert(1); + ExpectAck(5); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); - QuicPacketSequenceNumber unacked[] = { 2, 4, 6 }; - VerifyUnackedPackets(unacked, arraysize(unacked)); - QuicPacketSequenceNumber retransmittable[] = { 2, 4, 6 }; - VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable)); - } + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + VerifyRetransmittablePackets(NULL, 0); +} - RetransmitPacket(2, 8); - manager_.OnSerializedPacket(CreatePacket(9)); - manager_.OnSerializedPacket(CreatePacket(10)); +TEST_F(QuicSentPacketManagerTest, TruncatedAck) { + SendDataPacket(1); + RetransmitAndSendPacket(1, 2); + RetransmitAndSendPacket(2, 3); + RetransmitAndSendPacket(3, 4); + RetransmitAndSendPacket(4, 5); - { - // Ack packet 10. - ReceivedPacketInfo received_info; - received_info.largest_observed = 10; - received_info.missing_packets.insert(2); - received_info.missing_packets.insert(4); - received_info.missing_packets.insert(6); - received_info.missing_packets.insert(8); - received_info.missing_packets.insert(9); - manager_.OnIncomingAck(received_info, QuicTime::Zero()); - - QuicPacketSequenceNumber unacked[] = { 2, 4, 6, 8, 9 }; - VerifyUnackedPackets(unacked, arraysize(unacked)); - QuicPacketSequenceNumber retransmittable[] = { 4, 6, 8, 9 }; - VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable)); - } + // Truncated ack with 4 NACKs, so the first packet is lost. + ReceivedPacketInfo received_info; + received_info.largest_observed = 4; + received_info.missing_packets.insert(1); + received_info.missing_packets.insert(2); + received_info.missing_packets.insert(3); + received_info.missing_packets.insert(4); + received_info.is_truncated = true; + QuicPacketSequenceNumber lost[] = { 1 }; + ExpectAcksAndLosses(true, NULL, 0, lost, arraysize(lost)); + manager_.OnIncomingAck(received_info, clock_.Now()); - RetransmitPacket(4, 11); - manager_.OnSerializedPacket(CreatePacket(12)); - manager_.OnSerializedPacket(CreatePacket(13)); + // High water mark will be raised. + QuicPacketSequenceNumber unacked[] = { 2, 3, 4, 5 }; + VerifyUnackedPackets(unacked, arraysize(unacked)); + QuicPacketSequenceNumber retransmittable[] = { 5 }; + VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable)); +} +TEST_F(QuicSentPacketManagerTest, AckPreviousTransmissionThenTruncatedAck) { + SendDataPacket(1); + RetransmitAndSendPacket(1, 2); + RetransmitAndSendPacket(2, 3); + RetransmitAndSendPacket(3, 4); + manager_.OnSerializedPacket(CreateDataPacket(5)); + manager_.OnSerializedPacket(CreateDataPacket(6)); + manager_.OnSerializedPacket(CreateDataPacket(7)); + manager_.OnSerializedPacket(CreateDataPacket(8)); + manager_.OnSerializedPacket(CreateDataPacket(9)); + + // Ack previous transmission { - // Ack packet 13. ReceivedPacketInfo received_info; - received_info.largest_observed = 13; - received_info.missing_packets.insert(2); - received_info.missing_packets.insert(4); - received_info.missing_packets.insert(6); - received_info.missing_packets.insert(8); - received_info.missing_packets.insert(9); - received_info.missing_packets.insert(11); - received_info.missing_packets.insert(12); - manager_.OnIncomingAck(received_info, QuicTime::Zero()); - - QuicPacketSequenceNumber unacked[] = { 2, 4, 6, 8, 9, 11, 12 }; - VerifyUnackedPackets(unacked, arraysize(unacked)); - QuicPacketSequenceNumber retransmittable[] = { 6, 8, 9, 11, 12 }; - VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable)); + received_info.largest_observed = 2; + received_info.missing_packets.insert(1); + ExpectAck(2); + manager_.OnIncomingAck(received_info, clock_.Now()); + EXPECT_TRUE(manager_.IsUnacked(4)); } - RetransmitPacket(6, 14); - manager_.OnSerializedPacket(CreatePacket(15)); - manager_.OnSerializedPacket(CreatePacket(16)); - + // Truncated ack with 4 NACKs { - // Ack packet 16. ReceivedPacketInfo received_info; - received_info.largest_observed = 13; - received_info.missing_packets.insert(2); + received_info.largest_observed = 6; + received_info.missing_packets.insert(3); received_info.missing_packets.insert(4); + received_info.missing_packets.insert(5); received_info.missing_packets.insert(6); - received_info.missing_packets.insert(8); - received_info.missing_packets.insert(9); - received_info.missing_packets.insert(11); - received_info.missing_packets.insert(12); received_info.is_truncated = true; - manager_.OnIncomingAck(received_info, QuicTime::Zero()); - - // Truncated ack raises the high water mark by clearing out 2, 4, and 6. - QuicPacketSequenceNumber unacked[] = { 8, 9, 11, 12, 14, 15, 16 }; - VerifyUnackedPackets(unacked, arraysize(unacked)); - QuicPacketSequenceNumber retransmittable[] = { 8, 9, 11, 12, 14, 15, 16 }; - VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable)); + ExpectAckAndLoss(false, 1, 3); + manager_.OnIncomingAck(received_info, clock_.Now()); } + + // High water mark will be raised. + QuicPacketSequenceNumber unacked[] = { 4, 5, 6, 7, 8, 9 }; + VerifyUnackedPackets(unacked, arraysize(unacked)); + QuicPacketSequenceNumber retransmittable[] = { 5, 6, 7, 8, 9 }; + VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable)); } TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacket) { - EXPECT_CALL(helper_, GetNextPacketSequenceNumber()).WillOnce(Return(1u)); - EXPECT_EQ(1u, manager_.GetLeastUnackedSentPacket()); + EXPECT_EQ(0u, manager_.GetLeastUnackedSentPacket()); } TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacketUnacked) { - SerializedPacket serialized_packet(CreatePacket(1)); + SerializedPacket serialized_packet(CreateDataPacket(1)); manager_.OnSerializedPacket(serialized_packet); EXPECT_EQ(1u, manager_.GetLeastUnackedSentPacket()); @@ -424,15 +631,6 @@ TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacketUnackedFec) { EXPECT_EQ(1u, manager_.GetLeastUnackedSentPacket()); } -TEST_F(QuicSentPacketManagerTest, GetLeastUnackedSentPacketDiscardUnacked) { - SerializedPacket serialized_packet(CreatePacket(1)); - - manager_.OnSerializedPacket(serialized_packet); - manager_.DiscardUnackedPacket(1u); - EXPECT_CALL(helper_, GetNextPacketSequenceNumber()).WillOnce(Return(2u)); - EXPECT_EQ(2u, manager_.GetLeastUnackedSentPacket()); -} - TEST_F(QuicSentPacketManagerTest, GetLeastUnackedPacketAndDiscard) { VerifyUnackedPackets(NULL, 0); @@ -452,19 +650,12 @@ TEST_F(QuicSentPacketManagerTest, GetLeastUnackedPacketAndDiscard) { VerifyUnackedPackets(unacked, arraysize(unacked)); VerifyRetransmittablePackets(NULL, 0); - manager_.DiscardUnackedPacket(1); - EXPECT_EQ(2u, manager_.GetLeastUnackedSentPacket()); - - // Ack 2. + // Ack 2, which has never been sent, so there's no rtt update. ReceivedPacketInfo received_info; received_info.largest_observed = 2; - manager_.OnIncomingAck(received_info, QuicTime::Zero()); + manager_.OnIncomingAck(received_info, clock_.Now()); EXPECT_EQ(3u, manager_.GetLeastUnackedSentPacket()); - - // Discard the 3rd packet and ensure there are no FEC packets. - manager_.DiscardUnackedPacket(3); - EXPECT_FALSE(manager_.HasUnackedPackets()); } TEST_F(QuicSentPacketManagerTest, GetSentTime) { @@ -472,17 +663,18 @@ TEST_F(QuicSentPacketManagerTest, GetSentTime) { SerializedPacket serialized_packet(CreateFecPacket(1)); manager_.OnSerializedPacket(serialized_packet); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 1, _, _, _)) + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)) .Times(1).WillOnce(Return(true)); - manager_.OnPacketSent( - 1, QuicTime::Zero(), 0, NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); + manager_.OnPacketSent(1, QuicTime::Zero(), kDefaultLength, NOT_RETRANSMISSION, + NO_RETRANSMITTABLE_DATA); + SerializedPacket serialized_packet2(CreateFecPacket(2)); QuicTime sent_time = QuicTime::Zero().Add(QuicTime::Delta::FromSeconds(1)); manager_.OnSerializedPacket(serialized_packet2); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, 2, _, _, _)) + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2, _, _)) .Times(1).WillOnce(Return(true)); - manager_.OnPacketSent( - 2, sent_time, 0, NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); + manager_.OnPacketSent(2, sent_time, kDefaultLength, NOT_RETRANSMISSION, + NO_RETRANSMITTABLE_DATA); QuicPacketSequenceNumber unacked[] = { 1, 2 }; VerifyUnackedPackets(unacked, arraysize(unacked)); @@ -494,332 +686,431 @@ TEST_F(QuicSentPacketManagerTest, GetSentTime) { EXPECT_EQ(sent_time, QuicSentPacketManagerPeer::GetSentTime(&manager_, 2)); } -TEST_F(QuicSentPacketManagerTest, NackRetransmit1Packet) { - const size_t kNumSentPackets = 4; - // Transmit 4 packets. - for (size_t i = 1; i <= kNumSentPackets; ++i) { - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) - .Times(1).WillOnce(Return(true)); - manager_.OnPacketSent(i, clock_.Now(), 1000, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); - } +TEST_F(QuicSentPacketManagerTest, AckAckAndUpdateRtt) { + SendDataPacket(1); + SendAckPacket(2); - // Nack the first packet 3 times with increasing largest observed. + // Now ack the ack and expect an RTT update. ReceivedPacketInfo received_info; + received_info.largest_observed = 2; received_info.delta_time_largest_observed = QuicTime::Delta::FromMilliseconds(5); - received_info.missing_packets.insert(1); - for (QuicPacketSequenceNumber i = 1; i <= 3; ++i) { - received_info.largest_observed = i + 1; - EXPECT_CALL(*send_algorithm_, OnPacketAcked(i + 1, _, _)).Times(1); - if (i == 3) { - EXPECT_CALL(*send_algorithm_, OnPacketLost(1, _)).Times(1); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1); - } - SequenceNumberSet retransmissions = - manager_.OnIncomingAckFrame(received_info, clock_.Now()); - EXPECT_EQ(i == 3 ? 1u : 0u, retransmissions.size()); - EXPECT_EQ(i, QuicSentPacketManagerPeer::GetNackCount(&manager_, 1)); - } + + ExpectAck(1); + manager_.OnIncomingAck(received_info, clock_.Now()); + + SendAckPacket(3); + + // Now ack the ack and expect only an RTT update. + received_info.largest_observed = 3; + ExpectUpdatedRtt(3); + manager_.OnIncomingAck(received_info, clock_.Now()); } -// A stretch ack is an ack that covers more than 1 packet of previously -// unacknowledged data. -TEST_F(QuicSentPacketManagerTest, NackRetransmit1PacketWith1StretchAck) { - const size_t kNumSentPackets = 4; - // Transmit 4 packets. - for (size_t i = 1; i <= kNumSentPackets; ++i) { - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) - .Times(1).WillOnce(Return(true)); - manager_.OnPacketSent(i, clock_.Now(), 1000, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); - } +TEST_F(QuicSentPacketManagerTest, Rtt) { + QuicPacketSequenceNumber sequence_number = 1; + QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(15); + SendDataPacket(sequence_number); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20)); - // Nack the first packet 3 times in a single StretchAck. + ExpectAck(sequence_number); ReceivedPacketInfo received_info; + received_info.largest_observed = sequence_number; received_info.delta_time_largest_observed = - QuicTime::Delta::FromMilliseconds(5); - received_info.missing_packets.insert(1); - received_info.largest_observed = kNumSentPackets; - EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _, _)).Times(3); - EXPECT_CALL(*send_algorithm_, OnPacketLost(1, _)).Times(1); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1); - SequenceNumberSet retransmissions = - manager_.OnIncomingAckFrame(received_info, clock_.Now()); - EXPECT_EQ(1u, retransmissions.size()); - EXPECT_EQ(3u, QuicSentPacketManagerPeer::GetNackCount(&manager_, 1)); -} - -// Ack a packet 3 packets ahead, causing a retransmit. -TEST_F(QuicSentPacketManagerTest, NackRetransmit1PacketSingleAck) { - const size_t kNumSentPackets = 4; - // Transmit 4 packets. - for (size_t i = 1; i <= kNumSentPackets; ++i) { - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) - .Times(1).WillOnce(Return(true)); - manager_.OnPacketSent(i, clock_.Now(), 1000, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); - } + QuicTime::Delta::FromMilliseconds(5); + manager_.OnIncomingAck(received_info, clock_.Now()); + EXPECT_EQ(expected_rtt, + QuicSentPacketManagerPeer::GetRttStats(&manager_)->latest_rtt()); +} - // Nack the first packet 3 times in an AckFrame with three missing packets. +TEST_F(QuicSentPacketManagerTest, RttWithInvalidDelta) { + // Expect that the RTT is equal to the local time elapsed, since the + // delta_time_largest_observed is larger than the local time elapsed + // and is hence invalid. + QuicPacketSequenceNumber sequence_number = 1; + QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10); + SendDataPacket(sequence_number); + clock_.AdvanceTime(expected_rtt); + + ExpectAck(sequence_number); ReceivedPacketInfo received_info; + received_info.largest_observed = sequence_number; received_info.delta_time_largest_observed = - QuicTime::Delta::FromMilliseconds(5); - received_info.missing_packets.insert(1); - received_info.missing_packets.insert(2); - received_info.missing_packets.insert(3); - received_info.largest_observed = kNumSentPackets; - EXPECT_CALL(*send_algorithm_, OnPacketAcked(kNumSentPackets, _, _)).Times(1); - EXPECT_CALL(*send_algorithm_, OnPacketLost(1, _)).Times(1); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1); - SequenceNumberSet retransmissions = - manager_.OnIncomingAckFrame(received_info, clock_.Now()); - EXPECT_EQ(1u, retransmissions.size()); - EXPECT_EQ(3u, QuicSentPacketManagerPeer::GetNackCount(&manager_, 1)); -} - -TEST_F(QuicSentPacketManagerTest, EarlyRetransmit1Packet) { - const size_t kNumSentPackets = 2; - // Transmit 2 packets. - for (size_t i = 1; i <= kNumSentPackets; ++i) { - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) - .Times(1).WillOnce(Return(true)); - manager_.OnPacketSent(i, clock_.Now(), 1000, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); - } + QuicTime::Delta::FromMilliseconds(11); + manager_.OnIncomingAck(received_info, clock_.Now()); + EXPECT_EQ(expected_rtt, + QuicSentPacketManagerPeer::GetRttStats(&manager_)->latest_rtt()); +} + +TEST_F(QuicSentPacketManagerTest, RttWithInfiniteDelta) { + // Expect that the RTT is equal to the local time elapsed, since the + // delta_time_largest_observed is infinite, and is hence invalid. + QuicPacketSequenceNumber sequence_number = 1; + QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10); + SendDataPacket(sequence_number); + clock_.AdvanceTime(expected_rtt); - // Early retransmit when the final packet gets acked and the first is nacked. + ExpectAck(sequence_number); ReceivedPacketInfo received_info; - received_info.delta_time_largest_observed = - QuicTime::Delta::FromMilliseconds(5); - received_info.missing_packets.insert(1); - received_info.largest_observed = kNumSentPackets; - EXPECT_CALL(*send_algorithm_, OnPacketAcked(kNumSentPackets, _, _)).Times(1); - EXPECT_CALL(*send_algorithm_, OnPacketLost(1, _)).Times(1); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1); - SequenceNumberSet retransmissions = - manager_.OnIncomingAckFrame(received_info, clock_.Now()); - EXPECT_EQ(1u, retransmissions.size()); - EXPECT_EQ(1u, QuicSentPacketManagerPeer::GetNackCount(&manager_, 1)); -} - -TEST_F(QuicSentPacketManagerTest, DontEarlyRetransmitPacket) { - const size_t kNumSentPackets = 4; - for (size_t i = 1; i <= kNumSentPackets; ++i) { - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) - .Times(1).WillOnce(Return(true)); - manager_.OnPacketSent(i, clock_.Now(), 1000, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); - } + received_info.largest_observed = sequence_number; + received_info.delta_time_largest_observed = QuicTime::Delta::Infinite(); + manager_.OnIncomingAck(received_info, clock_.Now()); + EXPECT_EQ(expected_rtt, + QuicSentPacketManagerPeer::GetRttStats(&manager_)->latest_rtt()); +} + +TEST_F(QuicSentPacketManagerTest, RttZeroDelta) { + // Expect that the RTT is the time between send and receive since the + // delta_time_largest_observed is zero. + QuicPacketSequenceNumber sequence_number = 1; + QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10); + SendDataPacket(sequence_number); + clock_.AdvanceTime(expected_rtt); - // Fast retransmit when the final packet gets acked, but don't early - // retransmit as well, because there are 4 packets outstanding when the ack - // arrives. + ExpectAck(sequence_number); ReceivedPacketInfo received_info; - received_info.delta_time_largest_observed = - QuicTime::Delta::FromMilliseconds(5); + received_info.largest_observed = sequence_number; + received_info.delta_time_largest_observed = QuicTime::Delta::Zero(); + manager_.OnIncomingAck(received_info, clock_.Now()); + EXPECT_EQ(expected_rtt, + QuicSentPacketManagerPeer::GetRttStats(&manager_)->latest_rtt()); +} + +TEST_F(QuicSentPacketManagerTest, TailLossProbeTimeout) { + QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2); + + // Send 1 packet. + QuicPacketSequenceNumber sequence_number = 1; + SendDataPacket(sequence_number); + + // The first tail loss probe retransmits 1 packet. + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), + manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA)); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + manager_.MaybeRetransmitTailLossProbe(); + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + RetransmitNextPacket(2); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + + // The second tail loss probe retransmits 1 packet. + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), + manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA)); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + manager_.MaybeRetransmitTailLossProbe(); + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + RetransmitNextPacket(3); + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return( + QuicTime::Delta::Infinite())); + EXPECT_EQ(QuicTime::Delta::Infinite(), + manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA)); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + + // Ack the third and ensure the first two are still pending. + ExpectAck(3); + ReceivedPacketInfo received_info; + received_info.largest_observed = 3; received_info.missing_packets.insert(1); received_info.missing_packets.insert(2); - received_info.missing_packets.insert(3); - received_info.largest_observed = kNumSentPackets; - EXPECT_CALL(*send_algorithm_, OnPacketAcked(kNumSentPackets, _, _)).Times(1); - EXPECT_CALL(*send_algorithm_, OnPacketLost(1, _)).Times(1); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1); - SequenceNumberSet retransmissions = - manager_.OnIncomingAckFrame(received_info, clock_.Now()); - EXPECT_EQ(1u, retransmissions.size()); - EXPECT_EQ(3u, QuicSentPacketManagerPeer::GetNackCount(&manager_, 1)); -} - -TEST_F(QuicSentPacketManagerTest, NackRetransmit2Packets) { - const size_t kNumSentPackets = 20; - // Transmit 20 packets. - for (QuicPacketSequenceNumber i = 1; i <= kNumSentPackets; ++i) { - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) - .Times(1).WillOnce(Return(true)); - manager_.OnPacketSent(i, clock_.Now(), 1000, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); - } + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); - // Nack the first 19 packets 3 times. - ReceivedPacketInfo received_info; - received_info.largest_observed = kNumSentPackets; - received_info.delta_time_largest_observed = - QuicTime::Delta::FromMilliseconds(5); - for (size_t i = 1; i < kNumSentPackets; ++i) { - received_info.missing_packets.insert(i); - } - EXPECT_CALL(*send_algorithm_, - OnPacketAcked(kNumSentPackets, _, _)).Times(1); - EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2); - SequenceNumberSet retransmissions = - manager_.OnIncomingAckFrame(received_info, clock_.Now()); - EXPECT_EQ(2u, retransmissions.size()); - for (size_t i = 1; i < kNumSentPackets; ++i) { - EXPECT_EQ(kNumSentPackets - i, - QuicSentPacketManagerPeer::GetNackCount(&manager_, i)); - } + EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + + // Acking two more packets will lose both of them due to nacks. + received_info.largest_observed = 5; + QuicPacketSequenceNumber lost[] = { 1, 2 }; + ExpectAcksAndLosses(false, NULL, 0, lost, arraysize(lost)); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); + + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + EXPECT_EQ(2u, stats_.tlp_count); + EXPECT_EQ(0u, stats_.rto_count); } -TEST_F(QuicSentPacketManagerTest, NackRetransmit2PacketsAlternateAcks) { - const size_t kNumSentPackets = 30; - // Transmit 15 packets of data and 15 ack packets. The send algorithm will - // inform the congestion manager not to save the acks by returning false. - for (QuicPacketSequenceNumber i = 1; i <= kNumSentPackets; ++i) { - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) - .Times(1).WillOnce(Return(i % 2 == 0 ? false : true)); - manager_.OnPacketSent( - i, clock_.Now(), 1000, NOT_RETRANSMISSION, - i % 2 == 0 ? NO_RETRANSMITTABLE_DATA : HAS_RETRANSMITTABLE_DATA); - } +TEST_F(QuicSentPacketManagerTest, TailLossProbeThenRTO) { + QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2); - // Nack the first 29 packets 3 times. - ReceivedPacketInfo received_info; - received_info.largest_observed = kNumSentPackets; - received_info.delta_time_largest_observed = - QuicTime::Delta::FromMilliseconds(5); - for (size_t i = 1; i < kNumSentPackets; ++i) { - received_info.missing_packets.insert(i); - } - // We never actually get an ack call, since the kNumSentPackets packet was - // not saved. - EXPECT_CALL(*send_algorithm_, OnPacketLost(_, _)).Times(2); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(2); - SequenceNumberSet retransmissions = - manager_.OnIncomingAckFrame(received_info, clock_.Now()); - EXPECT_EQ(2u, retransmissions.size()); - // Only non-ack packets have a nack count. - for (size_t i = 1; i < kNumSentPackets; i += 2) { - EXPECT_EQ(kNumSentPackets - i, - QuicSentPacketManagerPeer::GetNackCount(&manager_, i)); + // Send 100 packets. + const size_t kNumSentPackets = 100; + for (size_t i = 1; i <= kNumSentPackets; ++i) { + SendDataPacket(i); } - // Ensure only the odd packets were retransmitted, since the others were not - // retransmittable(ie: acks). - for (SequenceNumberSet::const_iterator it = retransmissions.begin(); - it != retransmissions.end(); ++it) { - EXPECT_EQ(1u, *it % 2); - } + // The first tail loss probe retransmits 1 packet. + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), + manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA)); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + manager_.MaybeRetransmitTailLossProbe(); + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + RetransmitNextPacket(101); + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return( + QuicTime::Delta::Infinite())); + EXPECT_EQ(QuicTime::Delta::Infinite(), + manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA)); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + + // The second tail loss probe retransmits 1 packet. + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), + manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA)); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + EXPECT_TRUE(manager_.MaybeRetransmitTailLossProbe()); + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + RetransmitNextPacket(102); + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return( + QuicTime::Delta::Infinite())); + EXPECT_EQ(QuicTime::Delta::Infinite(), + manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA)); + + // Advance the time enough to ensure all packets are RTO'd. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000)); + + // The final RTO abandons all of them. + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + manager_.OnRetransmissionTimeout(); + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + EXPECT_EQ(2u, stats_.tlp_count); + EXPECT_EQ(1u, stats_.rto_count); } -TEST_F(QuicSentPacketManagerTest, NackTwiceThenAck) { - // Transmit 4 packets. - for (QuicPacketSequenceNumber i = 1; i <= 4; ++i) { - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) - .Times(1).WillOnce(Return(true)); - manager_.OnPacketSent(i, clock_.Now(), 1000, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); +TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeout) { + // Send 2 crypto packets and 3 data packets. + const size_t kNumSentCryptoPackets = 2; + for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) { + SendCryptoPacket(i); + } + const size_t kNumSentDataPackets = 3; + for (size_t i = 1; i <= kNumSentDataPackets; ++i) { + SendDataPacket(kNumSentCryptoPackets + i); } + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + + // The first retransmits 2 packets. + manager_.OnRetransmissionTimeout(); + RetransmitNextPacket(6); + RetransmitNextPacket(7); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); - // Nack the first packet 2 times, then ack it. + // The second retransmits 2 packets. + manager_.OnRetransmissionTimeout(); + RetransmitNextPacket(8); + RetransmitNextPacket(9); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + + // Now ack the two crypto packets and the speculatively encrypted request, + // and ensure the first four crypto packets get abandoned, but not lost. + QuicPacketSequenceNumber acked[] = { 3, 4, 5, 8, 9 }; + ExpectAcksAndLosses(true, acked, arraysize(acked), NULL, 0); ReceivedPacketInfo received_info; + received_info.largest_observed = 9; received_info.missing_packets.insert(1); - for (size_t i = 1; i <= 3; ++i) { - if (i == 3) { - received_info.missing_packets.clear(); - } - received_info.largest_observed = i + 1; - received_info.delta_time_largest_observed = - QuicTime::Delta::FromMilliseconds(5); - EXPECT_CALL(*send_algorithm_, - OnPacketAcked(_, _, _)).Times(i == 3 ? 2 : 1); - SequenceNumberSet retransmissions = - manager_.OnIncomingAckFrame(received_info, clock_.Now()); - EXPECT_EQ(0u, retransmissions.size()); - // The nack count remains at 2 when the packet is acked. - EXPECT_EQ(i == 3 ? 2u : i, - QuicSentPacketManagerPeer::GetNackCount(&manager_, 1)); + received_info.missing_packets.insert(2); + received_info.missing_packets.insert(6); + received_info.missing_packets.insert(7); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); + + EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); +} + +TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeoutVersionNegotiation) { + // Send 2 crypto packets and 3 data packets. + const size_t kNumSentCryptoPackets = 2; + for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) { + SendCryptoPacket(i); } + const size_t kNumSentDataPackets = 3; + for (size_t i = 1; i <= kNumSentDataPackets; ++i) { + SendDataPacket(kNumSentCryptoPackets + i); + } + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + + // The first retransmission timeout retransmits 2 crypto packets. + manager_.OnRetransmissionTimeout(); + RetransmitNextPacket(6); + RetransmitNextPacket(7); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + + // Now act like a version negotiation packet arrived, which would cause all + // unacked packets to be retransmitted. + manager_.RetransmitUnackedPackets(ALL_PACKETS); + + // Ensure the first two pending packets are the crypto retransmits. + ASSERT_TRUE(manager_.HasPendingRetransmissions()); + EXPECT_EQ(6u, manager_.NextPendingRetransmission().sequence_number); + RetransmitNextPacket(8); + EXPECT_EQ(7u, manager_.NextPendingRetransmission().sequence_number); + RetransmitNextPacket(9); + + EXPECT_TRUE(manager_.HasPendingRetransmissions()); } -TEST_F(QuicSentPacketManagerTest, Rtt) { - QuicPacketSequenceNumber sequence_number = 1; - QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(15); +TEST_F(QuicSentPacketManagerTest, CryptoHandshakeSpuriousRetransmission) { + // Send 1 crypto packet. + SendCryptoPacket(1); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) - .Times(1).WillOnce(Return(true)); - EXPECT_CALL(*send_algorithm_, - OnPacketAcked(sequence_number, _, expected_rtt)).Times(1); + // Retransmit the crypto packet as 2. + manager_.OnRetransmissionTimeout(); + RetransmitNextPacket(2); - manager_.OnPacketSent(sequence_number, clock_.Now(), 1000, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20)); + // Retransmit the crypto packet as 3. + manager_.OnRetransmissionTimeout(); + RetransmitNextPacket(3); + // Now ack the second crypto packet, and ensure the first gets removed, but + // the third does not. + ExpectUpdatedRtt(2); ReceivedPacketInfo received_info; - received_info.largest_observed = sequence_number; - received_info.delta_time_largest_observed = - QuicTime::Delta::FromMilliseconds(5); - manager_.OnIncomingAckFrame(received_info, clock_.Now()); - EXPECT_EQ(expected_rtt, QuicSentPacketManagerPeer::rtt(&manager_)); + received_info.largest_observed = 2; + received_info.missing_packets.insert(1); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); + + EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + QuicPacketSequenceNumber unacked[] = { 3 }; + VerifyUnackedPackets(unacked, arraysize(unacked)); } -TEST_F(QuicSentPacketManagerTest, RttWithInvalidDelta) { - // Expect that the RTT is equal to the local time elapsed, since the - // delta_time_largest_observed is larger than the local time elapsed - // and is hence invalid. - QuicPacketSequenceNumber sequence_number = 1; - QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10); +TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeoutUnsentDataPacket) { + // Send 2 crypto packets and serialize 1 data packet. + const size_t kNumSentCryptoPackets = 2; + for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) { + SendCryptoPacket(i); + } + SerializedPacket packet(CreateDataPacket(3)); + manager_.OnSerializedPacket(packet); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + + // Retransmit 2 crypto packets, but not the serialized packet. + manager_.OnRetransmissionTimeout(); + RetransmitNextPacket(6); + RetransmitNextPacket(7); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); +} - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) - .Times(1).WillOnce(Return(true)); - EXPECT_CALL(*send_algorithm_, - OnPacketAcked(sequence_number, _, expected_rtt)).Times(1); +TEST_F(QuicSentPacketManagerTest, + CryptoHandshakeRetransmissionThenRetransmitAll) { + // Send 1 crypto packet. + SendCryptoPacket(1); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); - manager_.OnPacketSent(sequence_number, clock_.Now(), 1000, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + // Retransmit the crypto packet as 2. + manager_.OnRetransmissionTimeout(); + RetransmitNextPacket(2); - ReceivedPacketInfo received_info; - received_info.largest_observed = sequence_number; - received_info.delta_time_largest_observed = - QuicTime::Delta::FromMilliseconds(11); - manager_.OnIncomingAckFrame(received_info, clock_.Now()); - EXPECT_EQ(expected_rtt, QuicSentPacketManagerPeer::rtt(&manager_)); + // Now retransmit all the unacked packets, which occurs when there is a + // version negotiation. + manager_.RetransmitUnackedPackets(ALL_PACKETS); + QuicPacketSequenceNumber unacked[] = { 1, 2 }; + VerifyUnackedPackets(unacked, arraysize(unacked)); + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); } -TEST_F(QuicSentPacketManagerTest, RttWithInfiniteDelta) { - // Expect that the RTT is equal to the local time elapsed, since the - // delta_time_largest_observed is infinite, and is hence invalid. - QuicPacketSequenceNumber sequence_number = 1; - QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10); +TEST_F(QuicSentPacketManagerTest, + CryptoHandshakeRetransmissionThenNeuterAndAck) { + // Send 1 crypto packet. + SendCryptoPacket(1); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) - .Times(1).WillOnce(Return(true)); - EXPECT_CALL(*send_algorithm_, - OnPacketAcked(sequence_number, _, expected_rtt)).Times(1); + // Retransmit the crypto packet as 2. + manager_.OnRetransmissionTimeout(); + RetransmitNextPacket(2); - manager_.OnPacketSent(sequence_number, clock_.Now(), 1000, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + // Retransmit the crypto packet as 3. + manager_.OnRetransmissionTimeout(); + RetransmitNextPacket(3); + // Now neuter all unacked unencrypted packets, which occurs when the + // connection goes forward secure. + manager_.NeuterUnencryptedPackets(); + QuicPacketSequenceNumber unacked[] = { 1, 2, 3}; + VerifyUnackedPackets(unacked, arraysize(unacked)); + VerifyRetransmittablePackets(NULL, 0); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); + + // Ensure both packets get discarded when packet 2 is acked. ReceivedPacketInfo received_info; - received_info.largest_observed = sequence_number; - received_info.delta_time_largest_observed = QuicTime::Delta::Infinite(); - manager_.OnIncomingAckFrame(received_info, clock_.Now()); - EXPECT_EQ(expected_rtt, QuicSentPacketManagerPeer::rtt(&manager_)); + received_info.largest_observed = 3; + received_info.missing_packets.insert(1); + received_info.missing_packets.insert(2); + ExpectUpdatedRtt(3); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); + VerifyUnackedPackets(NULL, 0); + VerifyRetransmittablePackets(NULL, 0); } -TEST_F(QuicSentPacketManagerTest, RttZeroDelta) { - // Expect that the RTT is the time between send and receive since the - // delta_time_largest_observed is zero. - QuicPacketSequenceNumber sequence_number = 1; - QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10); +TEST_F(QuicSentPacketManagerTest, TailLossProbeTimeoutUnsentDataPacket) { + QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2); + // Serialize two data packets and send the latter. + SerializedPacket packet(CreateDataPacket(1)); + manager_.OnSerializedPacket(packet); + SendDataPacket(2); + EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) - .Times(1).WillOnce(Return(true)); - EXPECT_CALL(*send_algorithm_, OnPacketAcked(sequence_number, _, expected_rtt)) - .Times(1); + // Retransmit 1 unacked packets, but not the first serialized packet. + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), + manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA)); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + manager_.MaybeRetransmitTailLossProbe(); + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + RetransmitNextPacket(3); + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return( + QuicTime::Delta::Infinite())); + EXPECT_EQ(QuicTime::Delta::Infinite(), + manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA)); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_)); + EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_)); +} - manager_.OnPacketSent(sequence_number, clock_.Now(), 1000, - NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); - clock_.AdvanceTime(expected_rtt); +TEST_F(QuicSentPacketManagerTest, ResetRecentMinRTTWithEmptyWindow) { + QuicTime::Delta min_rtt = QuicTime::Delta::FromMilliseconds(50); + QuicSentPacketManagerPeer::GetRttStats(&manager_)->UpdateRtt( + min_rtt, QuicTime::Delta::Zero(), QuicTime::Zero()); + EXPECT_EQ(min_rtt, + QuicSentPacketManagerPeer::GetRttStats(&manager_)->min_rtt()); + EXPECT_EQ(min_rtt, + QuicSentPacketManagerPeer::GetRttStats( + &manager_)->recent_min_rtt()); + + // Send two packets with no prior bytes in flight. + SendDataPacket(1); + SendDataPacket(2); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100)); + // Ack two packets with 100ms RTT observations. ReceivedPacketInfo received_info; - received_info.largest_observed = sequence_number; received_info.delta_time_largest_observed = QuicTime::Delta::Zero(); - manager_.OnIncomingAckFrame(received_info, clock_.Now()); - EXPECT_EQ(expected_rtt, QuicSentPacketManagerPeer::rtt(&manager_)); + received_info.largest_observed = 1; + ExpectAck(1); + manager_.OnIncomingAck(received_info, clock_.Now()); + + // First ack does not change recent min rtt. + EXPECT_EQ(min_rtt, + QuicSentPacketManagerPeer::GetRttStats( + &manager_)->recent_min_rtt()); + + received_info.largest_observed = 2; + ExpectAck(2); + manager_.OnIncomingAck(received_info, clock_.Now()); + + EXPECT_EQ(min_rtt, + QuicSentPacketManagerPeer::GetRttStats(&manager_)->min_rtt()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), + QuicSentPacketManagerPeer::GetRttStats( + &manager_)->recent_min_rtt()); } TEST_F(QuicSentPacketManagerTest, RetransmissionTimeout) { @@ -829,20 +1120,134 @@ TEST_F(QuicSentPacketManagerTest, RetransmissionTimeout) { SendDataPacket(i); } - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(_, _)).Times(kNumSentPackets); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + EXPECT_FALSE(manager_.MaybeRetransmitTailLossProbe()); + manager_.OnRetransmissionTimeout(); +} + +TEST_F(QuicSentPacketManagerTest, GetTransmissionTime) { + EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime()); +} + +TEST_F(QuicSentPacketManagerTest, GetTransmissionTimeCryptoHandshake) { + SendCryptoPacket(1); + + // Check the min. + QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt_us( + 1 * base::Time::kMicrosecondsPerMillisecond); + EXPECT_EQ(clock_.Now().Add(QuicTime::Delta::FromMilliseconds(10)), + manager_.GetRetransmissionTime()); + + // Test with a standard smoothed RTT. + QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt_us( + 100 * base::Time::kMicrosecondsPerMillisecond); + + QuicTime::Delta srtt = manager_.GetRttStats()->SmoothedRtt(); + QuicTime expected_time = clock_.Now().Add(srtt.Multiply(1.5)); + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); + + // Retransmit the packet by invoking the retransmission timeout. + clock_.AdvanceTime(srtt.Multiply(1.5)); + manager_.OnRetransmissionTimeout(); + RetransmitNextPacket(2); + + // The retransmission time should now be twice as far in the future. + expected_time = clock_.Now().Add(srtt.Multiply(2).Multiply(1.5)); + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); +} + +TEST_F(QuicSentPacketManagerTest, GetTransmissionTimeTailLossProbe) { + QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2); + SendDataPacket(1); + SendDataPacket(2); + + // Check the min. + QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt_us( + 1 * base::Time::kMicrosecondsPerMillisecond); + EXPECT_EQ(clock_.Now().Add(QuicTime::Delta::FromMilliseconds(10)), + manager_.GetRetransmissionTime()); + + // Test with a standard smoothed RTT. + QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt_us( + 100 * base::Time::kMicrosecondsPerMillisecond); + QuicTime::Delta srtt = manager_.GetRttStats()->SmoothedRtt(); + QuicTime::Delta expected_tlp_delay = srtt.Multiply(2); + QuicTime expected_time = clock_.Now().Add(expected_tlp_delay); + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); + + // Retransmit the packet by invoking the retransmission timeout. + clock_.AdvanceTime(expected_tlp_delay); + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), + manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA)); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + EXPECT_TRUE(manager_.MaybeRetransmitTailLossProbe()); + EXPECT_TRUE(manager_.HasPendingRetransmissions()); + RetransmitNextPacket(3); + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return( + QuicTime::Delta::Infinite())); + EXPECT_EQ(QuicTime::Delta::Infinite(), + manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA)); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + + expected_time = clock_.Now().Add(expected_tlp_delay); + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); +} +TEST_F(QuicSentPacketManagerTest, GetTransmissionTimeRTO) { + QuicSentPacketManagerPeer::GetRttStats(&manager_)->UpdateRtt( + QuicTime::Delta::FromMilliseconds(100), + QuicTime::Delta::Zero(), + QuicTime::Zero()); + + SendDataPacket(1); + SendDataPacket(2); + + QuicTime::Delta expected_rto_delay = QuicTime::Delta::FromMilliseconds(500); EXPECT_CALL(*send_algorithm_, RetransmissionDelay()) - .WillOnce(Return(QuicTime::Delta::FromMilliseconds(1))); - EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout()); + .WillRepeatedly(Return(expected_rto_delay)); + QuicTime expected_time = clock_.Now().Add(expected_rto_delay); + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); + + // Retransmit the packet by invoking the retransmission timeout. + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + clock_.AdvanceTime(expected_rto_delay); manager_.OnRetransmissionTimeout(); + RetransmitNextPacket(3); + RetransmitNextPacket(4); + EXPECT_FALSE(manager_.HasPendingRetransmissions()); + + // The delay should double the second time. + expected_time = clock_.Now().Add(expected_rto_delay).Add(expected_rto_delay); + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); + + // Ack a packet and ensure the RTO goes back to the original value. + ReceivedPacketInfo received_info; + received_info.largest_observed = 2; + received_info.missing_packets.insert(1); + ExpectUpdatedRtt(2); + manager_.OnIncomingAck(received_info, clock_.ApproximateNow()); + + expected_time = clock_.Now().Add(expected_rto_delay); + EXPECT_EQ(expected_time, manager_.GetRetransmissionTime()); } TEST_F(QuicSentPacketManagerTest, GetTransmissionDelayMin) { + SendDataPacket(1); EXPECT_CALL(*send_algorithm_, RetransmissionDelay()) - .WillOnce(Return(QuicTime::Delta::FromMilliseconds(1))); + .WillRepeatedly(Return(QuicTime::Delta::FromMilliseconds(1))); + QuicTime::Delta delay = QuicTime::Delta::FromMilliseconds(200); - EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), - manager_.GetRetransmissionDelay()); + // If the delay is smaller than the min, ensure it exponentially backs off + // from the min. + for (int i = 0; i < 5; ++i) { + EXPECT_EQ(delay, + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_)); + delay = delay.Add(delay); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); + manager_.OnRetransmissionTimeout(); + RetransmitNextPacket(i + 2); + } } TEST_F(QuicSentPacketManagerTest, GetTransmissionDelayMax) { @@ -850,7 +1255,7 @@ TEST_F(QuicSentPacketManagerTest, GetTransmissionDelayMax) { .WillOnce(Return(QuicTime::Delta::FromSeconds(500))); EXPECT_EQ(QuicTime::Delta::FromSeconds(60), - manager_.GetRetransmissionDelay()); + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_)); } TEST_F(QuicSentPacketManagerTest, GetTransmissionDelay) { @@ -861,36 +1266,62 @@ TEST_F(QuicSentPacketManagerTest, GetTransmissionDelay) { // Delay should back off exponentially. for (int i = 0; i < 5; ++i) { - EXPECT_EQ(delay, manager_.GetRetransmissionDelay()); + EXPECT_EQ(delay, + QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_)); delay = delay.Add(delay); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(i + 1, _)); - EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout()); + EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); manager_.OnRetransmissionTimeout(); RetransmitNextPacket(i + 2); } } -TEST_F(QuicSentPacketManagerTest, GetTestTransmissionDelayTailDrop) { - FLAGS_limit_rto_increase_for_tests = true; +TEST_F(QuicSentPacketManagerTest, GetLossDelay) { + MockLossAlgorithm* loss_algorithm = new MockLossAlgorithm(); + QuicSentPacketManagerPeer::SetLossAlgorithm(&manager_, loss_algorithm); + EXPECT_CALL(*loss_algorithm, GetLossTimeout()) + .WillRepeatedly(Return(QuicTime::Zero())); SendDataPacket(1); - QuicTime::Delta delay = QuicTime::Delta::FromMilliseconds(500); - EXPECT_CALL(*send_algorithm_, RetransmissionDelay()) - .WillRepeatedly(Return(delay)); + SendDataPacket(2); - // No backoff for the first 5 retransmissions. - for (int i = 0; i < 5; ++i) { - EXPECT_EQ(delay, manager_.GetRetransmissionDelay()); - EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(i + 1, _)); - EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout()); - manager_.OnRetransmissionTimeout(); - RetransmitNextPacket(i + 2); - } + // Handle an ack which causes the loss algorithm to be evaluated and + // set the loss timeout. + ExpectAck(2); + EXPECT_CALL(*loss_algorithm, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(SequenceNumberSet())); + ReceivedPacketInfo received_info; + received_info.largest_observed = 2; + received_info.missing_packets.insert(1); + manager_.OnIncomingAck(received_info, clock_.Now()); + + QuicTime timeout(clock_.Now().Add(QuicTime::Delta::FromMilliseconds(10))); + EXPECT_CALL(*loss_algorithm, GetLossTimeout()) + .WillRepeatedly(Return(timeout)); + EXPECT_EQ(timeout, manager_.GetRetransmissionTime()); - // Then backoff starts - EXPECT_EQ(delay.Add(delay), manager_.GetRetransmissionDelay()); + // Fire the retransmission timeout and ensure the loss detection algorithm + // is invoked. + EXPECT_CALL(*loss_algorithm, DetectLostPackets(_, _, _, _)) + .WillOnce(Return(SequenceNumberSet())); + manager_.OnRetransmissionTimeout(); +} + +TEST_F(QuicSentPacketManagerTest, NegotiateTimeLossDetection) { + EXPECT_EQ(kNack, + QuicSentPacketManagerPeer::GetLossAlgorithm( + &manager_)->GetLossDetectionType()); + + QuicConfig config; + QuicConfigPeer::SetReceivedLossDetection(&config, kTIME); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + manager_.SetFromConfig(config); + + EXPECT_EQ(kTime, + QuicSentPacketManagerPeer::GetLossAlgorithm( + &manager_)->GetLossDetectionType()); } + } // namespace } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_server_id.cc b/chromium/net/quic/quic_server_id.cc new file mode 100644 index 00000000000..ff870997df7 --- /dev/null +++ b/chromium/net/quic/quic_server_id.cc @@ -0,0 +1,58 @@ +// 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/quic_server_id.h" + +using std::string; + +namespace net { + +QuicServerId::QuicServerId() {} + +QuicServerId::QuicServerId(const HostPortPair& host_port_pair, + bool is_https, + PrivacyMode privacy_mode) + : host_port_pair_(host_port_pair), + is_https_(is_https), + privacy_mode_(privacy_mode) {} + +QuicServerId::QuicServerId(const string& host, + uint16 port, + bool is_https) + : host_port_pair_(host, port), + is_https_(is_https), + privacy_mode_(PRIVACY_MODE_DISABLED) {} + +QuicServerId::QuicServerId(const string& host, + uint16 port, + bool is_https, + PrivacyMode privacy_mode) + : host_port_pair_(host, port), + is_https_(is_https), + privacy_mode_(privacy_mode) {} + +QuicServerId::~QuicServerId() {} + +bool QuicServerId::operator<(const QuicServerId& other) const { + if (!host_port_pair_.Equals(other.host_port_pair_)) { + return host_port_pair_ < other.host_port_pair_; + } + if (is_https_ != other.is_https_) { + return is_https_ < other.is_https_; + } + return privacy_mode_ < other.privacy_mode_; +} + +bool QuicServerId::operator==(const QuicServerId& other) const { + return is_https_ == other.is_https_ && + privacy_mode_ == other.privacy_mode_ && + host_port_pair_.Equals(other.host_port_pair_); +} + +string QuicServerId::ToString() const { + return (is_https_ ? "https://" : "http://") + host_port_pair_.ToString() + + (privacy_mode_ == PRIVACY_MODE_ENABLED ? "/private" : ""); +} + +} // namespace net diff --git a/chromium/net/quic/quic_server_id.h b/chromium/net/quic/quic_server_id.h new file mode 100644 index 00000000000..6d016705cd8 --- /dev/null +++ b/chromium/net/quic/quic_server_id.h @@ -0,0 +1,60 @@ +// 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_QUIC_SERVER_ID_H_ +#define NET_QUIC_QUIC_SERVER_ID_H_ + +#include <string> + +#include "net/base/host_port_pair.h" +#include "net/base/net_export.h" +#include "net/base/privacy_mode.h" + +namespace net { + +// The id used to identify sessions. Includes the hostname, port, scheme and +// privacy_mode. +class NET_EXPORT_PRIVATE QuicServerId { + public: + QuicServerId(); + QuicServerId(const HostPortPair& host_port_pair, + bool is_https, + PrivacyMode privacy_mode); + QuicServerId(const std::string& host, + uint16 port, + bool is_https); + QuicServerId(const std::string& host, + uint16 port, + bool is_https, + PrivacyMode privacy_mode); + ~QuicServerId(); + + // Needed to be an element of std::set. + bool operator<(const QuicServerId& other) const; + bool operator==(const QuicServerId& other) const; + + // ToString() will convert the QuicServerId to "scheme:hostname:port" or + // "scheme:hostname:port/private". "scheme" would either be "http" or "https" + // based on |is_https|. + std::string ToString() const; + + const HostPortPair& host_port_pair() const { return host_port_pair_; } + + const std::string& host() const { return host_port_pair_.host(); } + + uint16 port() const { return host_port_pair_.port(); } + + bool is_https() const { return is_https_; } + + PrivacyMode privacy_mode() const { return privacy_mode_; } + + private: + HostPortPair host_port_pair_; + bool is_https_; + PrivacyMode privacy_mode_; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_SERVER_ID_H_ diff --git a/chromium/net/quic/quic_server_id_test.cc b/chromium/net/quic/quic_server_id_test.cc new file mode 100644 index 00000000000..4a9c0f3cd80 --- /dev/null +++ b/chromium/net/quic/quic_server_id_test.cc @@ -0,0 +1,319 @@ +// 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/quic_server_id.h" + +#include "testing/gtest/include/gtest/gtest.h" + +using std::string; + +namespace net { + +namespace { + +TEST(QuicServerIdTest, ToString) { + HostPortPair google_host_port_pair("google.com", 10); + + QuicServerId google_http_server_id(google_host_port_pair, false, + PRIVACY_MODE_DISABLED); + string google_http_server_id_str = google_http_server_id.ToString(); + EXPECT_EQ("http://google.com:10", google_http_server_id_str); + + QuicServerId google_https_server_id(google_host_port_pair, true, + PRIVACY_MODE_DISABLED); + string google_https_server_id_str = google_https_server_id.ToString(); + EXPECT_EQ("https://google.com:10", google_https_server_id_str); + + QuicServerId private_http_server_id(google_host_port_pair, false, + PRIVACY_MODE_ENABLED); + string private_http_server_id_str = private_http_server_id.ToString(); + EXPECT_EQ("http://google.com:10/private", private_http_server_id_str); + + QuicServerId private_https_server_id(google_host_port_pair, true, + PRIVACY_MODE_ENABLED); + string private_https_server_id_str = private_https_server_id.ToString(); + EXPECT_EQ("https://google.com:10/private", private_https_server_id_str); +} + +TEST(QuicServerIdTest, LessThan) { + QuicServerId a_10_http(HostPortPair("a.com", 10), false, + PRIVACY_MODE_DISABLED); + QuicServerId a_10_https(HostPortPair("a.com", 10), true, + PRIVACY_MODE_DISABLED); + QuicServerId a_11_http(HostPortPair("a.com", 11), false, + PRIVACY_MODE_DISABLED); + QuicServerId a_11_https(HostPortPair("a.com", 11), true, + PRIVACY_MODE_DISABLED); + QuicServerId b_10_http(HostPortPair("b.com", 10), false, + PRIVACY_MODE_DISABLED); + QuicServerId b_10_https(HostPortPair("b.com", 10), true, + PRIVACY_MODE_DISABLED); + QuicServerId b_11_http(HostPortPair("b.com", 11), false, + PRIVACY_MODE_DISABLED); + QuicServerId b_11_https(HostPortPair("b.com", 11), true, + PRIVACY_MODE_DISABLED); + + QuicServerId a_10_http_private(HostPortPair("a.com", 10), false, + PRIVACY_MODE_ENABLED); + QuicServerId a_10_https_private(HostPortPair("a.com", 10), true, + PRIVACY_MODE_ENABLED); + QuicServerId a_11_http_private(HostPortPair("a.com", 11), false, + PRIVACY_MODE_ENABLED); + QuicServerId a_11_https_private(HostPortPair("a.com", 11), true, + PRIVACY_MODE_ENABLED); + QuicServerId b_10_http_private(HostPortPair("b.com", 10), false, + PRIVACY_MODE_ENABLED); + QuicServerId b_10_https_private(HostPortPair("b.com", 10), true, + PRIVACY_MODE_ENABLED); + QuicServerId b_11_http_private(HostPortPair("b.com", 11), false, + PRIVACY_MODE_ENABLED); + QuicServerId b_11_https_private(HostPortPair("b.com", 11), true, + PRIVACY_MODE_ENABLED); + + // Test combinations of host, port, https and privacy being same on left and + // right side of less than. + EXPECT_FALSE(a_10_http < a_10_http); + EXPECT_TRUE(a_10_http < a_10_https); + EXPECT_FALSE(a_10_https < a_10_http); + EXPECT_FALSE(a_10_https < a_10_https); + + EXPECT_TRUE(a_10_http < a_10_http_private); + EXPECT_TRUE(a_10_http < a_10_https_private); + EXPECT_FALSE(a_10_https < a_10_http_private); + EXPECT_TRUE(a_10_https < a_10_https_private); + + EXPECT_FALSE(a_10_http_private < a_10_http); + EXPECT_TRUE(a_10_http_private < a_10_https); + EXPECT_FALSE(a_10_https_private < a_10_http); + EXPECT_FALSE(a_10_https_private < a_10_https); + + EXPECT_FALSE(a_10_http_private < a_10_http_private); + EXPECT_TRUE(a_10_http_private < a_10_https_private); + EXPECT_FALSE(a_10_https_private < a_10_http_private); + EXPECT_FALSE(a_10_https_private < a_10_https_private); + + // Test with either host, port or https being different on left and right side + // of less than. + PrivacyMode left_privacy; + PrivacyMode right_privacy; + for (int i = 0; i < 4; i++) { + switch (i) { + case 0: + left_privacy = PRIVACY_MODE_DISABLED; + right_privacy = PRIVACY_MODE_DISABLED; + break; + case 1: + left_privacy = PRIVACY_MODE_DISABLED; + right_privacy = PRIVACY_MODE_ENABLED; + break; + case 2: + left_privacy = PRIVACY_MODE_ENABLED; + right_privacy = PRIVACY_MODE_DISABLED; + break; + case 3: + left_privacy = PRIVACY_MODE_ENABLED; + right_privacy = PRIVACY_MODE_ENABLED; + break; + } + QuicServerId a_10_http_left_private(HostPortPair("a.com", 10), false, + left_privacy); + QuicServerId a_10_http_right_private(HostPortPair("a.com", 10), false, + right_privacy); + QuicServerId a_10_https_left_private(HostPortPair("a.com", 10), true, + left_privacy); + QuicServerId a_10_https_right_private(HostPortPair("a.com", 10), true, + right_privacy); + QuicServerId a_11_http_left_private(HostPortPair("a.com", 11), false, + left_privacy); + QuicServerId a_11_http_right_private(HostPortPair("a.com", 11), false, + right_privacy); + QuicServerId a_11_https_left_private(HostPortPair("a.com", 11), true, + left_privacy); + QuicServerId a_11_https_right_private(HostPortPair("a.com", 11), true, + right_privacy); + + QuicServerId b_10_http_left_private(HostPortPair("b.com", 10), false, + left_privacy); + QuicServerId b_10_http_right_private(HostPortPair("b.com", 10), false, + right_privacy); + QuicServerId b_10_https_left_private(HostPortPair("b.com", 10), true, + left_privacy); + QuicServerId b_10_https_right_private(HostPortPair("b.com", 10), true, + right_privacy); + QuicServerId b_11_http_left_private(HostPortPair("b.com", 11), false, + left_privacy); + QuicServerId b_11_http_right_private(HostPortPair("b.com", 11), false, + right_privacy); + QuicServerId b_11_https_left_private(HostPortPair("b.com", 11), true, + left_privacy); + QuicServerId b_11_https_right_private(HostPortPair("b.com", 11), true, + right_privacy); + + EXPECT_TRUE(a_10_http_left_private < a_11_http_right_private); + EXPECT_TRUE(a_10_http_left_private < a_11_https_right_private); + EXPECT_TRUE(a_10_https_left_private < a_11_http_right_private); + EXPECT_TRUE(a_10_https_left_private < a_11_https_right_private); + + EXPECT_TRUE(a_10_http_left_private < b_10_http_right_private); + EXPECT_TRUE(a_10_http_left_private < b_10_https_right_private); + EXPECT_TRUE(a_10_https_left_private < b_10_http_right_private); + EXPECT_TRUE(a_10_https_left_private < b_10_https_right_private); + + EXPECT_TRUE(a_10_http_left_private < b_11_http_right_private); + EXPECT_TRUE(a_10_http_left_private < b_11_https_right_private); + EXPECT_TRUE(a_10_https_left_private < b_11_http_right_private); + EXPECT_TRUE(a_10_https_left_private < b_11_https_right_private); + + EXPECT_FALSE(a_11_http_left_private < a_10_http_right_private); + EXPECT_FALSE(a_11_http_left_private < a_10_https_right_private); + EXPECT_FALSE(a_11_https_left_private < a_10_http_right_private); + EXPECT_FALSE(a_11_https_left_private < a_10_https_right_private); + + EXPECT_FALSE(a_11_http_left_private < b_10_http_right_private); + EXPECT_FALSE(a_11_http_left_private < b_10_https_right_private); + EXPECT_FALSE(a_11_https_left_private < b_10_http_right_private); + EXPECT_FALSE(a_11_https_left_private < b_10_https_right_private); + + EXPECT_TRUE(a_11_http_left_private < b_11_http_right_private); + EXPECT_TRUE(a_11_http_left_private < b_11_https_right_private); + EXPECT_TRUE(a_11_https_left_private < b_11_http_right_private); + EXPECT_TRUE(a_11_https_left_private < b_11_https_right_private); + + EXPECT_FALSE(b_10_http_left_private < a_10_http_right_private); + EXPECT_FALSE(b_10_http_left_private < a_10_https_right_private); + EXPECT_FALSE(b_10_https_left_private < a_10_http_right_private); + EXPECT_FALSE(b_10_https_left_private < a_10_https_right_private); + + EXPECT_TRUE(b_10_http_left_private < a_11_http_right_private); + EXPECT_TRUE(b_10_http_left_private < a_11_https_right_private); + EXPECT_TRUE(b_10_https_left_private < a_11_http_right_private); + EXPECT_TRUE(b_10_https_left_private < a_11_https_right_private); + + EXPECT_TRUE(b_10_http_left_private < b_11_http_right_private); + EXPECT_TRUE(b_10_http_left_private < b_11_https_right_private); + EXPECT_TRUE(b_10_https_left_private < b_11_http_right_private); + EXPECT_TRUE(b_10_https_left_private < b_11_https_right_private); + + EXPECT_FALSE(b_11_http_left_private < a_10_http_right_private); + EXPECT_FALSE(b_11_http_left_private < a_10_https_right_private); + EXPECT_FALSE(b_11_https_left_private < a_10_http_right_private); + EXPECT_FALSE(b_11_https_left_private < a_10_https_right_private); + + EXPECT_FALSE(b_11_http_left_private < a_11_http_right_private); + EXPECT_FALSE(b_11_http_left_private < a_11_https_right_private); + EXPECT_FALSE(b_11_https_left_private < a_11_http_right_private); + EXPECT_FALSE(b_11_https_left_private < a_11_https_right_private); + + EXPECT_FALSE(b_11_http_left_private < b_10_http_right_private); + EXPECT_FALSE(b_11_http_left_private < b_10_https_right_private); + EXPECT_FALSE(b_11_https_left_private < b_10_http_right_private); + EXPECT_FALSE(b_11_https_left_private < b_10_https_right_private); + } +} + +TEST(QuicServerIdTest, Equals) { + PrivacyMode left_privacy; + PrivacyMode right_privacy; + for (int i = 0; i < 2; i++) { + switch (i) { + case 0: + left_privacy = PRIVACY_MODE_DISABLED; + right_privacy = PRIVACY_MODE_DISABLED; + break; + case 1: + left_privacy = PRIVACY_MODE_ENABLED; + right_privacy = PRIVACY_MODE_ENABLED; + break; + } + QuicServerId a_10_http_right_private(HostPortPair("a.com", 10), false, + right_privacy); + QuicServerId a_10_https_right_private(HostPortPair("a.com", 10), true, + right_privacy); + QuicServerId a_11_http_right_private(HostPortPair("a.com", 11), false, + right_privacy); + QuicServerId a_11_https_right_private(HostPortPair("a.com", 11), true, + right_privacy); + QuicServerId b_10_http_right_private(HostPortPair("b.com", 10), false, + right_privacy); + QuicServerId b_10_https_right_private(HostPortPair("b.com", 10), true, + right_privacy); + QuicServerId b_11_http_right_private(HostPortPair("b.com", 11), false, + right_privacy); + QuicServerId b_11_https_right_private(HostPortPair("b.com", 11), true, + right_privacy); + + QuicServerId new_a_10_http_left_private(HostPortPair("a.com", 10), false, + left_privacy); + QuicServerId new_a_10_https_left_private(HostPortPair("a.com", 10), true, + left_privacy); + QuicServerId new_a_11_http_left_private(HostPortPair("a.com", 11), false, + left_privacy); + QuicServerId new_a_11_https_left_private(HostPortPair("a.com", 11), true, + left_privacy); + QuicServerId new_b_10_http_left_private(HostPortPair("b.com", 10), false, + left_privacy); + QuicServerId new_b_10_https_left_private(HostPortPair("b.com", 10), true, + left_privacy); + QuicServerId new_b_11_http_left_private(HostPortPair("b.com", 11), false, + left_privacy); + QuicServerId new_b_11_https_left_private(HostPortPair("b.com", 11), true, + left_privacy); + + EXPECT_EQ(new_a_10_http_left_private, a_10_http_right_private); + EXPECT_EQ(new_a_10_https_left_private, a_10_https_right_private); + EXPECT_EQ(new_a_11_http_left_private, a_11_http_right_private); + EXPECT_EQ(new_a_11_https_left_private, a_11_https_right_private); + EXPECT_EQ(new_b_10_http_left_private, b_10_http_right_private); + EXPECT_EQ(new_b_10_https_left_private, b_10_https_right_private); + EXPECT_EQ(new_b_11_http_left_private, b_11_http_right_private); + EXPECT_EQ(new_b_11_https_left_private, b_11_https_right_private); + } + + for (int i = 0; i < 2; i++) { + switch (i) { + case 0: + right_privacy = PRIVACY_MODE_DISABLED; + break; + case 1: + right_privacy = PRIVACY_MODE_ENABLED; + break; + } + QuicServerId a_10_http_right_private(HostPortPair("a.com", 10), false, + right_privacy); + QuicServerId a_10_https_right_private(HostPortPair("a.com", 10), true, + right_privacy); + QuicServerId a_11_http_right_private(HostPortPair("a.com", 11), false, + right_privacy); + QuicServerId a_11_https_right_private(HostPortPair("a.com", 11), true, + right_privacy); + QuicServerId b_10_http_right_private(HostPortPair("b.com", 10), false, + right_privacy); + QuicServerId b_10_https_right_private(HostPortPair("b.com", 10), true, + right_privacy); + QuicServerId b_11_http_right_private(HostPortPair("b.com", 11), false, + right_privacy); + QuicServerId b_11_https_right_private(HostPortPair("b.com", 11), true, + right_privacy); + + QuicServerId new_a_10_http_left_private(HostPortPair("a.com", 10), false, + PRIVACY_MODE_DISABLED); + + EXPECT_FALSE(new_a_10_http_left_private == a_10_https_right_private); + EXPECT_FALSE(new_a_10_http_left_private == a_11_http_right_private); + EXPECT_FALSE(new_a_10_http_left_private == b_10_http_right_private); + EXPECT_FALSE(new_a_10_http_left_private == a_11_https_right_private); + EXPECT_FALSE(new_a_10_http_left_private == b_10_https_right_private); + EXPECT_FALSE(new_a_10_http_left_private == b_11_http_right_private); + EXPECT_FALSE(new_a_10_http_left_private == b_11_https_right_private); + } + QuicServerId a_10_http_private(HostPortPair("a.com", 10), false, + PRIVACY_MODE_ENABLED); + QuicServerId new_a_10_http_no_private(HostPortPair("a.com", 10), false, + PRIVACY_MODE_DISABLED); + EXPECT_FALSE(new_a_10_http_no_private == a_10_http_private); +} + +} // namespace + +} // namespace net diff --git a/chromium/net/quic/quic_server_packet_writer.cc b/chromium/net/quic/quic_server_packet_writer.cc new file mode 100644 index 00000000000..c2635b699eb --- /dev/null +++ b/chromium/net/quic/quic_server_packet_writer.cc @@ -0,0 +1,74 @@ +// 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/quic_server_packet_writer.h" + +#include "base/location.h" +#include "base/logging.h" +#include "base/metrics/sparse_histogram.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" + +namespace net { + +QuicServerPacketWriter::QuicServerPacketWriter() : weak_factory_(this) { +} + +QuicServerPacketWriter::QuicServerPacketWriter(UDPServerSocket* socket) + : weak_factory_(this), + socket_(socket), + write_blocked_(false) { +} + +QuicServerPacketWriter::~QuicServerPacketWriter() { +} + +WriteResult QuicServerPacketWriter::WritePacket( + const char* buffer, size_t buf_len, + const net::IPAddressNumber& self_address, + const net::IPEndPoint& peer_address) { + scoped_refptr<StringIOBuffer> buf( + new StringIOBuffer(std::string(buffer, buf_len))); + DCHECK(!IsWriteBlocked()); + int rv = socket_->SendTo(buf.get(), + buf_len, + peer_address, + base::Bind(&QuicServerPacketWriter::OnWriteComplete, + weak_factory_.GetWeakPtr())); + WriteStatus status = WRITE_STATUS_OK; + if (rv < 0) { + if (rv != ERR_IO_PENDING) { + UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.WriteError", -rv); + status = WRITE_STATUS_ERROR; + } else { + status = WRITE_STATUS_BLOCKED; + write_blocked_ = true; + } + } + + return WriteResult(status, rv); +} + +bool QuicServerPacketWriter::IsWriteBlockedDataBuffered() const { + // UDPServerSocket::SendTo buffers the data until the Write is permitted. + return true; +} + +bool QuicServerPacketWriter::IsWriteBlocked() const { + return write_blocked_; +} + +void QuicServerPacketWriter::SetWritable() { + write_blocked_ = false; +} + +void QuicServerPacketWriter::OnWriteComplete(int rv) { + DCHECK_NE(rv, ERR_IO_PENDING); + write_blocked_ = false; + WriteResult result(rv < 0 ? WRITE_STATUS_ERROR : WRITE_STATUS_OK, rv); + connection_->OnPacketSent(result); + connection_->OnCanWrite(); +} + +} // namespace net diff --git a/chromium/net/quic/quic_server_packet_writer.h b/chromium/net/quic/quic_server_packet_writer.h new file mode 100644 index 00000000000..63829935521 --- /dev/null +++ b/chromium/net/quic/quic_server_packet_writer.h @@ -0,0 +1,53 @@ +// 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_QUIC_SERVER_PACKET_WRITER_H_ +#define NET_QUIC_QUIC_SERVER_PACKET_WRITER_H_ + +#include "base/basictypes.h" +#include "base/memory/weak_ptr.h" +#include "net/base/ip_endpoint.h" +#include "net/quic/quic_connection.h" +#include "net/quic/quic_packet_writer.h" +#include "net/quic/quic_protocol.h" +#include "net/udp/udp_server_socket.h" + +namespace net { + +struct WriteResult; + +// Chrome specific packet writer which uses a UDPServerSocket for writing +// data. +class QuicServerPacketWriter : public QuicPacketWriter { + public: + QuicServerPacketWriter(); + explicit QuicServerPacketWriter(UDPServerSocket* socket); + virtual ~QuicServerPacketWriter(); + + // QuicPacketWriter + virtual WriteResult WritePacket(const char* buffer, + size_t buf_len, + const net::IPAddressNumber& self_address, + const net::IPEndPoint& peer_address) OVERRIDE; + virtual bool IsWriteBlockedDataBuffered() const OVERRIDE; + virtual bool IsWriteBlocked() const OVERRIDE; + virtual void SetWritable() OVERRIDE; + + void OnWriteComplete(int rv); + void SetConnection(QuicConnection* connection) { + connection_ = connection; + } + + private: + base::WeakPtrFactory<QuicServerPacketWriter> weak_factory_; + UDPServerSocket* socket_; + QuicConnection* connection_; + bool write_blocked_; + + DISALLOW_COPY_AND_ASSIGN(QuicServerPacketWriter); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_SERVER_PACKET_WRITER_H_ diff --git a/chromium/net/quic/quic_server_session.cc b/chromium/net/quic/quic_server_session.cc new file mode 100644 index 00000000000..1de91a44aa1 --- /dev/null +++ b/chromium/net/quic/quic_server_session.cc @@ -0,0 +1,81 @@ +// 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/quic_server_session.h" + +#include "base/logging.h" +#include "net/quic/quic_connection.h" +#include "net/quic/quic_spdy_server_stream.h" +#include "net/quic/reliable_quic_stream.h" + +namespace net { + +QuicServerSession::QuicServerSession(const QuicConfig& config, + QuicConnection* connection, + QuicServerSessionVisitor* visitor) + : QuicSession(connection, config), + visitor_(visitor) {} + +QuicServerSession::~QuicServerSession() {} + +void QuicServerSession::InitializeSession( + const QuicCryptoServerConfig& crypto_config) { + crypto_stream_.reset(CreateQuicCryptoServerStream(crypto_config)); +} + +QuicCryptoServerStream* QuicServerSession::CreateQuicCryptoServerStream( + const QuicCryptoServerConfig& crypto_config) { + return new QuicCryptoServerStream(crypto_config, this); +} + +void QuicServerSession::OnConnectionClosed(QuicErrorCode error, + bool from_peer) { + QuicSession::OnConnectionClosed(error, from_peer); + // In the unlikely event we get a connection close while doing an asynchronous + // crypto event, make sure we cancel the callback. + if (crypto_stream_.get() != NULL) { + crypto_stream_->CancelOutstandingCallbacks(); + } + visitor_->OnConnectionClosed(connection()->connection_id(), error); +} + +void QuicServerSession::OnWriteBlocked() { + QuicSession::OnWriteBlocked(); + visitor_->OnWriteBlocked(connection()); +} + +bool QuicServerSession::ShouldCreateIncomingDataStream(QuicStreamId id) { + if (id % 2 == 0) { + DVLOG(1) << "Invalid incoming even stream_id:" << id; + connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID); + return false; + } + if (GetNumOpenStreams() >= get_max_open_streams()) { + DVLOG(1) << "Failed to create a new incoming stream with id:" << id + << " Already " << GetNumOpenStreams() << " open."; + connection()->SendConnectionClose(QUIC_TOO_MANY_OPEN_STREAMS); + return false; + } + return true; +} + +QuicDataStream* QuicServerSession::CreateIncomingDataStream( + QuicStreamId id) { + if (!ShouldCreateIncomingDataStream(id)) { + return NULL; + } + + return new QuicSpdyServerStream(id, this); +} + +QuicDataStream* QuicServerSession::CreateOutgoingDataStream() { + DLOG(ERROR) << "Server push not yet supported"; + return NULL; +} + +QuicCryptoServerStream* QuicServerSession::GetCryptoStream() { + return crypto_stream_.get(); +} + +} // namespace net diff --git a/chromium/net/quic/quic_server_session.h b/chromium/net/quic/quic_server_session.h new file mode 100644 index 00000000000..30ac08c80df --- /dev/null +++ b/chromium/net/quic/quic_server_session.h @@ -0,0 +1,87 @@ +// 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. +// +// A server specific QuicSession subclass. + +#ifndef NET_QUIC_QUIC_SERVER_SESSION_H_ +#define NET_QUIC_QUIC_SERVER_SESSION_H_ + +#include <set> +#include <vector> + +#include "base/basictypes.h" +#include "base/containers/hash_tables.h" +#include "base/memory/scoped_ptr.h" +#include "net/quic/quic_crypto_server_stream.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_session.h" + +namespace net { + +class QuicBlockedWriterInterface; +class QuicConfig; +class QuicConnection; +class QuicCryptoServerConfig; +class ReliableQuicStream; + +namespace test { +class QuicServerSessionPeer; +} // namespace test + +// An interface from the session to the entity owning the session. +// This lets the session notify its owner (the Dispatcher) when the connection +// is closed or blocked. +class QuicServerSessionVisitor { + public: + virtual ~QuicServerSessionVisitor() {} + + virtual void OnConnectionClosed(QuicConnectionId connection_id, + QuicErrorCode error) = 0; + virtual void OnWriteBlocked(QuicBlockedWriterInterface* writer) = 0; +}; + +class QuicServerSession : public QuicSession { + public: + QuicServerSession(const QuicConfig& config, + QuicConnection* connection, + QuicServerSessionVisitor* visitor); + + // Override the base class to notify the owner of the connection close. + virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE; + virtual void OnWriteBlocked() OVERRIDE; + + virtual ~QuicServerSession(); + + virtual void InitializeSession(const QuicCryptoServerConfig& crypto_config); + + const QuicCryptoServerStream* crypto_stream() const { + return crypto_stream_.get(); + } + + protected: + // QuicSession methods: + virtual QuicDataStream* CreateIncomingDataStream(QuicStreamId id) OVERRIDE; + virtual QuicDataStream* CreateOutgoingDataStream() OVERRIDE; + virtual QuicCryptoServerStream* GetCryptoStream() OVERRIDE; + + // If we should create an incoming stream, returns true. Otherwise + // does error handling, including communicating the error to the client and + // possibly closing the connection, and returns false. + virtual bool ShouldCreateIncomingDataStream(QuicStreamId id); + + virtual QuicCryptoServerStream* CreateQuicCryptoServerStream( + const QuicCryptoServerConfig& crypto_config); + + private: + friend class test::QuicServerSessionPeer; + + scoped_ptr<QuicCryptoServerStream> crypto_stream_; + QuicServerSessionVisitor* visitor_; + + DISALLOW_COPY_AND_ASSIGN(QuicServerSession); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_SERVER_SESSION_H_ diff --git a/chromium/net/quic/quic_session.cc b/chromium/net/quic/quic_session.cc index 67cdf54d1d1..4cd9a335718 100644 --- a/chromium/net/quic/quic_session.cc +++ b/chromium/net/quic/quic_session.cc @@ -7,6 +7,9 @@ #include "base/stl_util.h" #include "net/quic/crypto/proof_verifier.h" #include "net/quic/quic_connection.h" +#include "net/quic/quic_flags.h" +#include "net/quic/quic_flow_controller.h" +#include "net/quic/quic_headers_stream.h" #include "net/ssl/ssl_info.h" using base::StringPiece; @@ -17,9 +20,6 @@ using std::vector; namespace net { -const size_t kMaxPrematurelyClosedStreamsTracked = 20; -const size_t kMaxZombieStreams = 20; - #define ENDPOINT (is_server() ? "Server: " : " Client: ") // We want to make sure we delete any closed streams in a safe manner. @@ -33,10 +33,9 @@ class VisitorShim : public QuicConnectionVisitorInterface { public: explicit VisitorShim(QuicSession* session) : session_(session) {} - virtual bool OnStreamFrames(const vector<QuicStreamFrame>& frames) OVERRIDE { - bool accepted = session_->OnStreamFrames(frames); + virtual void OnStreamFrames(const vector<QuicStreamFrame>& frames) OVERRIDE { + session_->OnStreamFrames(frames); session_->PostProcessAfterData(); - return accepted; } virtual void OnRstStream(const QuicRstStreamFrame& frame) OVERRIDE { session_->OnRstStream(frame); @@ -48,10 +47,21 @@ class VisitorShim : public QuicConnectionVisitorInterface { session_->PostProcessAfterData(); } - virtual bool OnCanWrite() OVERRIDE { - bool rc = session_->OnCanWrite(); + virtual void OnWindowUpdateFrames(const vector<QuicWindowUpdateFrame>& frames) + OVERRIDE { + session_->OnWindowUpdateFrames(frames); + session_->PostProcessAfterData(); + } + + virtual void OnBlockedFrames(const vector<QuicBlockedFrame>& frames) + OVERRIDE { + session_->OnBlockedFrames(frames); + session_->PostProcessAfterData(); + } + + virtual void OnCanWrite() OVERRIDE { + session_->OnCanWrite(); session_->PostProcessAfterData(); - return rc; } virtual void OnSuccessfulVersionNegotiation( @@ -59,26 +69,33 @@ class VisitorShim : public QuicConnectionVisitorInterface { session_->OnSuccessfulVersionNegotiation(version); } - virtual void OnConfigNegotiated() OVERRIDE { - session_->OnConfigNegotiated(); - } - - virtual void OnConnectionClosed(QuicErrorCode error, - bool from_peer) OVERRIDE { + virtual void OnConnectionClosed( + QuicErrorCode error, bool from_peer) OVERRIDE { session_->OnConnectionClosed(error, from_peer); // The session will go away, so don't bother with cleanup. } + virtual void OnWriteBlocked() OVERRIDE { + session_->OnWriteBlocked(); + } + + virtual bool WillingAndAbleToWrite() const OVERRIDE { + return session_->WillingAndAbleToWrite(); + } + virtual bool HasPendingHandshake() const OVERRIDE { return session_->HasPendingHandshake(); } + virtual bool HasOpenDataStreams() const OVERRIDE { + return session_->HasOpenDataStreams(); + } + private: QuicSession* session_; }; -QuicSession::QuicSession(QuicConnection* connection, - const QuicConfig& config) +QuicSession::QuicSession(QuicConnection* connection, const QuicConfig& config) : connection_(connection), visitor_shim_(new VisitorShim(this)), config_(config), @@ -89,6 +106,17 @@ QuicSession::QuicSession(QuicConnection* connection, goaway_received_(false), goaway_sent_(false), has_pending_handshake_(false) { + if (connection_->version() <= QUIC_VERSION_19) { + flow_controller_.reset(new QuicFlowController( + connection_.get(), 0, is_server(), kDefaultFlowControlSendWindow, + config_.GetInitialFlowControlWindowToSend(), + config_.GetInitialFlowControlWindowToSend())); + } else { + flow_controller_.reset(new QuicFlowController( + connection_.get(), 0, is_server(), kDefaultFlowControlSendWindow, + config_.GetInitialSessionFlowControlWindowToSend(), + config_.GetInitialSessionFlowControlWindowToSend())); + } connection_->set_visitor(visitor_shim_.get()); connection_->SetFromConfig(config_); @@ -96,70 +124,77 @@ QuicSession::QuicSession(QuicConnection* connection, connection_->SetOverallConnectionTimeout( config_.max_time_before_crypto_handshake()); } + headers_stream_.reset(new QuicHeadersStream(this)); + if (!is_server()) { + // For version above QUIC v12, the headers stream is stream 3, so the + // next available local stream ID should be 5. + DCHECK_EQ(kHeadersStreamId, next_stream_id_); + next_stream_id_ += 2; + } } QuicSession::~QuicSession() { STLDeleteElements(&closed_streams_); STLDeleteValues(&stream_map_); -} - -bool QuicSession::OnStreamFrames(const vector<QuicStreamFrame>& frames) { - for (size_t i = 0; i < frames.size(); ++i) { - // TODO(rch) deal with the error case of stream id 0 - if (IsClosedStream(frames[i].stream_id)) { - // If we get additional frames for a stream where we didn't process - // headers, it's highly likely our compression context will end up - // permanently out of sync with the peer's, so we give up and close the - // connection. - if (ContainsKey(prematurely_closed_streams_, frames[i].stream_id)) { - connection()->SendConnectionClose( - QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED); - return false; - } - continue; - } - - ReliableQuicStream* stream = GetStream(frames[i].stream_id); - if (stream == NULL) return false; - if (!stream->WillAcceptStreamFrame(frames[i])) return false; - // TODO(alyssar) check against existing connection address: if changed, make - // sure we update the connection. - } + DLOG_IF(WARNING, + locally_closed_streams_highest_offset_.size() > max_open_streams_) + << "Surprisingly high number of locally closed streams still waiting for " + "final byte offset: " << locally_closed_streams_highest_offset_.size(); +} +void QuicSession::OnStreamFrames(const vector<QuicStreamFrame>& frames) { for (size_t i = 0; i < frames.size(); ++i) { - QuicStreamId stream_id = frames[i].stream_id; + // TODO(rch) deal with the error case of stream id 0. + const QuicStreamFrame& frame = frames[i]; + QuicStreamId stream_id = frame.stream_id; ReliableQuicStream* stream = GetStream(stream_id); if (!stream) { + // The stream no longer exists, but we may still be interested in the + // final stream byte offset sent by the peer. A frame with a FIN can give + // us this offset. + if (frame.fin) { + QuicStreamOffset final_byte_offset = + frame.offset + frame.data.TotalBufferSize(); + UpdateFlowControlOnFinalReceivedByteOffset(stream_id, + final_byte_offset); + } + continue; } stream->OnStreamFrame(frames[i]); + } +} - // If the stream is a data stream had been prematurely closed, and the - // headers are now decompressed, then we are finally finished - // with this stream. - if (ContainsKey(zombie_streams_, stream_id) && - static_cast<QuicDataStream*>(stream)->headers_decompressed()) { - CloseZombieStream(stream_id); - } +void QuicSession::OnStreamHeaders(QuicStreamId stream_id, + StringPiece headers_data) { + QuicDataStream* stream = GetDataStream(stream_id); + if (!stream) { + // It's quite possible to receive headers after a stream has been reset. + return; } + stream->OnStreamHeaders(headers_data); +} - while (!decompression_blocked_streams_.empty()) { - QuicHeaderId header_id = decompression_blocked_streams_.begin()->first; - if (header_id != decompressor_.current_header_id()) { - break; - } - QuicStreamId stream_id = decompression_blocked_streams_.begin()->second; - decompression_blocked_streams_.erase(header_id); - QuicDataStream* stream = GetDataStream(stream_id); - if (!stream) { - connection()->SendConnectionClose( - QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED); - return false; - } - stream->OnDecompressorAvailable(); +void QuicSession::OnStreamHeadersPriority(QuicStreamId stream_id, + QuicPriority priority) { + QuicDataStream* stream = GetDataStream(stream_id); + if (!stream) { + // It's quite possible to receive headers after a stream has been reset. + return; } - return true; + stream->OnStreamHeadersPriority(priority); +} + +void QuicSession::OnStreamHeadersComplete(QuicStreamId stream_id, + bool fin, + size_t frame_len) { + QuicDataStream* stream = GetDataStream(stream_id); + if (!stream) { + // It's quite possible to receive headers after a stream has been reset. + return; + } + stream->OnStreamHeadersComplete(fin, frame_len); } void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) { @@ -169,25 +204,23 @@ void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) { "Attempt to reset the crypto stream"); return; } + if (frame.stream_id == kHeadersStreamId) { + connection()->SendConnectionCloseWithDetails( + QUIC_INVALID_STREAM_ID, + "Attempt to reset the headers stream"); + return; + } + QuicDataStream* stream = GetDataStream(frame.stream_id); if (!stream) { + // The RST frame contains the final byte offset for the stream: we can now + // update the connection level flow controller if needed. + UpdateFlowControlOnFinalReceivedByteOffset(frame.stream_id, + frame.byte_offset); return; // Errors are handled by GetStream. } - if (ContainsKey(zombie_streams_, stream->id())) { - // If this was a zombie stream then we close it out now. - CloseZombieStream(stream->id()); - // However, since the headers still have not been decompressed, we want to - // mark it a prematurely closed so that if we ever receive frames - // for this stream we can close the connection. - DCHECK(!stream->headers_decompressed()); - AddPrematurelyClosedStream(frame.stream_id); - return; - } - if (stream->stream_bytes_read() > 0 && !stream->headers_decompressed()) { - connection()->SendConnectionClose( - QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED); - } - stream->OnStreamReset(frame.error_code); + + stream->OnStreamReset(frame); } void QuicSession::OnGoAway(const QuicGoAwayFrame& frame) { @@ -214,60 +247,148 @@ void QuicSession::OnConnectionClosed(QuicErrorCode error, bool from_peer) { } } -bool QuicSession::OnCanWrite() { - // We latch this here rather than doing a traditional loop, because streams - // may be modifying the list as we loop. - int remaining_writes = write_blocked_streams_.NumBlockedStreams(); +void QuicSession::OnWindowUpdateFrames( + const vector<QuicWindowUpdateFrame>& frames) { + bool connection_window_updated = false; + for (size_t i = 0; i < frames.size(); ++i) { + // Stream may be closed by the time we receive a WINDOW_UPDATE, so we can't + // assume that it still exists. + QuicStreamId stream_id = frames[i].stream_id; + if (stream_id == 0) { + // This is a window update that applies to the connection, rather than an + // individual stream. + DVLOG(1) << ENDPOINT + << "Received connection level flow control window update with " + "byte offset: " << frames[i].byte_offset; + if (FLAGS_enable_quic_connection_flow_control_2 && + flow_controller_->UpdateSendWindowOffset(frames[i].byte_offset)) { + connection_window_updated = true; + } + continue; + } + + QuicDataStream* stream = GetDataStream(stream_id); + if (stream) { + stream->OnWindowUpdateFrame(frames[i]); + } + } + + // Connection level flow control window has increased, so blocked streams can + // write again. + if (connection_window_updated) { + OnCanWrite(); + } +} + +void QuicSession::OnBlockedFrames(const vector<QuicBlockedFrame>& frames) { + for (size_t i = 0; i < frames.size(); ++i) { + // TODO(rjshade): Compare our flow control receive windows for specified + // streams: if we have a large window then maybe something + // had gone wrong with the flow control accounting. + DVLOG(1) << ENDPOINT << "Received BLOCKED frame with stream id: " + << frames[i].stream_id; + } +} + +void QuicSession::OnCanWrite() { + // We limit the number of writes to the number of pending streams. If more + // streams become pending, WillingAndAbleToWrite will be true, which will + // cause the connection to request resumption before yielding to other + // connections. + size_t num_writes = write_blocked_streams_.NumBlockedStreams(); + if (flow_controller_->IsBlocked()) { + // If we are connection level flow control blocked, then only allow the + // crypto and headers streams to try writing as all other streams will be + // blocked. + num_writes = 0; + if (write_blocked_streams_.crypto_stream_blocked()) { + num_writes += 1; + } + if (write_blocked_streams_.headers_stream_blocked()) { + num_writes += 1; + } + } + if (num_writes == 0) { + return; + } - while (!connection_->HasQueuedData() && - remaining_writes > 0) { - DCHECK(write_blocked_streams_.HasWriteBlockedStreams()); - if (!write_blocked_streams_.HasWriteBlockedStreams()) { + QuicConnection::ScopedPacketBundler ack_bundler( + connection_.get(), QuicConnection::NO_ACK); + for (size_t i = 0; i < num_writes; ++i) { + if (!(write_blocked_streams_.HasWriteBlockedCryptoOrHeadersStream() || + write_blocked_streams_.HasWriteBlockedDataStreams())) { + // Writing one stream removed another!? Something's broken. LOG(DFATAL) << "WriteBlockedStream is missing"; connection_->CloseConnection(QUIC_INTERNAL_ERROR, false); - return true; // We have no write blocked streams. + return; + } + if (!connection_->CanWriteStreamData()) { + return; } - int index = write_blocked_streams_.GetHighestPriorityWriteBlockedList(); - QuicStreamId stream_id = write_blocked_streams_.PopFront(index); + QuicStreamId stream_id = write_blocked_streams_.PopFront(); if (stream_id == kCryptoStreamId) { has_pending_handshake_ = false; // We just popped it. } ReliableQuicStream* stream = GetStream(stream_id); - if (stream != NULL) { + if (stream != NULL && !stream->flow_controller()->IsBlocked()) { // If the stream can't write all bytes, it'll re-add itself to the blocked // list. stream->OnCanWrite(); } - --remaining_writes; } +} - return !write_blocked_streams_.HasWriteBlockedStreams(); +bool QuicSession::WillingAndAbleToWrite() const { + // If the crypto or headers streams are blocked, we want to schedule a write - + // they don't get blocked by connection level flow control. Otherwise only + // schedule a write if we are not flow control blocked at the connection + // level. + return write_blocked_streams_.HasWriteBlockedCryptoOrHeadersStream() || + (!flow_controller_->IsBlocked() && + write_blocked_streams_.HasWriteBlockedDataStreams()); } bool QuicSession::HasPendingHandshake() const { return has_pending_handshake_; } +bool QuicSession::HasOpenDataStreams() const { + return GetNumOpenStreams() > 0; +} + QuicConsumedData QuicSession::WritevData( QuicStreamId id, - const struct iovec* iov, - int iov_count, + const IOVector& data, QuicStreamOffset offset, bool fin, + FecProtection fec_protection, QuicAckNotifier::DelegateInterface* ack_notifier_delegate) { - IOVector data; - data.AppendIovec(iov, iov_count); - return connection_->SendStreamData(id, data, offset, fin, + return connection_->SendStreamData(id, data, offset, fin, fec_protection, ack_notifier_delegate); } +size_t QuicSession::WriteHeaders( + QuicStreamId id, + const SpdyHeaderBlock& headers, + bool fin, + QuicAckNotifier::DelegateInterface* ack_notifier_delegate) { + return headers_stream_->WriteHeaders(id, headers, fin, ack_notifier_delegate); +} + void QuicSession::SendRstStream(QuicStreamId id, - QuicRstStreamErrorCode error) { - connection_->SendRstStream(id, error); + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written) { + if (connection()->connected()) { + // Only send a RST_STREAM frame if still connected. + connection_->SendRstStream(id, error, bytes_written); + } CloseStreamInner(id, true); } void QuicSession::SendGoAway(QuicErrorCode error_code, const string& reason) { + if (goaway_sent_) { + return; + } goaway_sent_ = true; connection_->SendGoAway(error_code, largest_peer_created_stream_id_, reason); } @@ -286,63 +407,55 @@ void QuicSession::CloseStreamInner(QuicStreamId stream_id, return; } QuicDataStream* stream = it->second; - if (connection_->connected() && !stream->headers_decompressed()) { - // If the stream is being closed locally (for example a client cancelling - // a request before receiving the response) then we need to make sure that - // we keep the stream alive long enough to process any response or - // RST_STREAM frames. - if (locally_reset && !is_server()) { - AddZombieStream(stream_id); - return; - } - // This stream has been closed before the headers were decompressed. - // This might cause problems with head of line blocking of headers. - // If the peer sent headers which were lost but we now close the stream - // we will never be able to decompress headers for other streams. - // To deal with this, we keep track of streams which have been closed - // prematurely. If we ever receive data frames for this steam, then we - // know there actually has been a problem and we close the connection. - AddPrematurelyClosedStream(stream->id()); + // Tell the stream that a RST has been sent. + if (locally_reset) { + stream->set_rst_sent(true); } + closed_streams_.push_back(it->second); - if (ContainsKey(zombie_streams_, stream->id())) { - zombie_streams_.erase(stream->id()); + + // If we haven't received a FIN or RST for this stream, we need to keep track + // of the how many bytes the stream's flow controller believes it has + // received, for accurate connection level flow control accounting. + if (!stream->HasFinalReceivedByteOffset() && + stream->flow_controller()->IsEnabled() && + FLAGS_enable_quic_connection_flow_control_2) { + locally_closed_streams_highest_offset_[stream_id] = + stream->flow_controller()->highest_received_byte_offset(); } + stream_map_.erase(it); stream->OnClose(); } -void QuicSession::AddZombieStream(QuicStreamId stream_id) { - if (zombie_streams_.size() == kMaxZombieStreams) { - QuicStreamId oldest_zombie_stream_id = zombie_streams_.begin()->first; - CloseZombieStream(oldest_zombie_stream_id); - // However, since the headers still have not been decompressed, we want to - // mark it a prematurely closed so that if we ever receive frames - // for this stream we can close the connection. - AddPrematurelyClosedStream(oldest_zombie_stream_id); +void QuicSession::UpdateFlowControlOnFinalReceivedByteOffset( + QuicStreamId stream_id, QuicStreamOffset final_byte_offset) { + if (!FLAGS_enable_quic_connection_flow_control_2) { + return; } - zombie_streams_.insert(make_pair(stream_id, true)); -} -void QuicSession::CloseZombieStream(QuicStreamId stream_id) { - DCHECK(ContainsKey(zombie_streams_, stream_id)); - zombie_streams_.erase(stream_id); - QuicDataStream* stream = GetDataStream(stream_id); - if (!stream) { + map<QuicStreamId, QuicStreamOffset>::iterator it = + locally_closed_streams_highest_offset_.find(stream_id); + if (it == locally_closed_streams_highest_offset_.end()) { return; } - stream_map_.erase(stream_id); - stream->OnClose(); - closed_streams_.push_back(stream); -} -void QuicSession::AddPrematurelyClosedStream(QuicStreamId stream_id) { - if (prematurely_closed_streams_.size() == - kMaxPrematurelyClosedStreamsTracked) { - prematurely_closed_streams_.erase(prematurely_closed_streams_.begin()); + DVLOG(1) << ENDPOINT << "Received final byte offset " << final_byte_offset + << " for stream " << stream_id; + uint64 offset_diff = final_byte_offset - it->second; + if (flow_controller_->UpdateHighestReceivedOffset( + flow_controller_->highest_received_byte_offset() + offset_diff)) { + // If the final offset violates flow control, close the connection now. + if (flow_controller_->FlowControlViolation()) { + connection_->SendConnectionClose( + QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA); + return; + } } - prematurely_closed_streams_.insert(make_pair(stream_id, true)); + + flow_controller_->AddBytesConsumed(offset_diff); + locally_closed_streams_highest_offset_.erase(it); } bool QuicSession::IsEncryptionEstablished() { @@ -355,6 +468,68 @@ bool QuicSession::IsCryptoHandshakeConfirmed() { void QuicSession::OnConfigNegotiated() { connection_->SetFromConfig(config_); + QuicVersion version = connection()->version(); + if (version < QUIC_VERSION_17) { + return; + } + + if (version <= QUIC_VERSION_19) { + // QUIC_VERSION_17,18,19 don't support independent stream/session flow + // control windows. + if (config_.HasReceivedInitialFlowControlWindowBytes()) { + // Streams which were created before the SHLO was received (0-RTT + // requests) are now informed of the peer's initial flow control window. + uint32 new_window = config_.ReceivedInitialFlowControlWindowBytes(); + OnNewStreamFlowControlWindow(new_window); + OnNewSessionFlowControlWindow(new_window); + } + + return; + } + + // QUIC_VERSION_20 and higher can have independent stream and session flow + // control windows. + if (config_.HasReceivedInitialStreamFlowControlWindowBytes()) { + // Streams which were created before the SHLO was received (0-RTT + // requests) are now informed of the peer's initial flow control window. + OnNewStreamFlowControlWindow( + config_.ReceivedInitialStreamFlowControlWindowBytes()); + } + if (config_.HasReceivedInitialSessionFlowControlWindowBytes()) { + OnNewSessionFlowControlWindow( + config_.ReceivedInitialSessionFlowControlWindowBytes()); + } +} + +void QuicSession::OnNewStreamFlowControlWindow(uint32 new_window) { + if (new_window < kDefaultFlowControlSendWindow) { + LOG(ERROR) + << "Peer sent us an invalid stream flow control send window: " + << new_window << ", below default: " << kDefaultFlowControlSendWindow; + if (connection_->connected()) { + connection_->SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW); + } + return; + } + + for (DataStreamMap::iterator it = stream_map_.begin(); + it != stream_map_.end(); ++it) { + it->second->flow_controller()->UpdateSendWindowOffset(new_window); + } +} + +void QuicSession::OnNewSessionFlowControlWindow(uint32 new_window) { + if (new_window < kDefaultFlowControlSendWindow) { + LOG(ERROR) + << "Peer sent us an invalid session flow control send window: " + << new_window << ", below default: " << kDefaultFlowControlSendWindow; + if (connection_->connected()) { + connection_->SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW); + } + return; + } + + flow_controller_->UpdateSendWindowOffset(new_window); } void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { @@ -373,6 +548,9 @@ void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { case HANDSHAKE_CONFIRMED: LOG_IF(DFATAL, !config_.negotiated()) << ENDPOINT << "Handshake confirmed without parameter negotiation."; + // Discard originally encrypted packets, since they can't be decrypted by + // the peer. + connection_->NeuterUnencryptedPackets(); connection_->SetOverallConnectionTimeout(QuicTime::Delta::Infinite()); max_open_streams_ = config_.max_streams_per_connection(); break; @@ -396,7 +574,7 @@ QuicConfig* QuicSession::config() { void QuicSession::ActivateStream(QuicDataStream* stream) { DVLOG(1) << ENDPOINT << "num_streams: " << stream_map_.size() - << ". activating " << stream->id(); + << ". activating " << stream->id(); DCHECK_EQ(stream_map_.count(stream->id()), 0u); stream_map_[stream->id()] = stream; } @@ -411,6 +589,9 @@ ReliableQuicStream* QuicSession::GetStream(const QuicStreamId stream_id) { if (stream_id == kCryptoStreamId) { return GetCryptoStream(); } + if (stream_id == kHeadersStreamId) { + return headers_stream_.get(); + } return GetDataStream(stream_id); } @@ -419,6 +600,10 @@ QuicDataStream* QuicSession::GetDataStream(const QuicStreamId stream_id) { DLOG(FATAL) << "Attempt to call GetDataStream with the crypto stream id"; return NULL; } + if (stream_id == kHeadersStreamId) { + DLOG(FATAL) << "Attempt to call GetDataStream with the headers stream id"; + return NULL; + } DataStreamMap::iterator it = stream_map_.find(stream_id); if (it != stream_map_.end()) { @@ -432,34 +617,40 @@ QuicDataStream* QuicSession::GetDataStream(const QuicStreamId stream_id) { if (stream_id % 2 == next_stream_id_ % 2) { // We've received a frame for a locally-created stream that is not // currently active. This is an error. - connection()->SendConnectionClose(QUIC_PACKET_FOR_NONEXISTENT_STREAM); + if (connection()->connected()) { + connection()->SendConnectionClose(QUIC_PACKET_FOR_NONEXISTENT_STREAM); + } return NULL; } - return GetIncomingReliableStream(stream_id); + return GetIncomingDataStream(stream_id); } -QuicDataStream* QuicSession::GetIncomingReliableStream( - QuicStreamId stream_id) { +QuicDataStream* QuicSession::GetIncomingDataStream(QuicStreamId stream_id) { if (IsClosedStream(stream_id)) { return NULL; } - if (goaway_sent_) { - // We've already sent a GoAway - SendRstStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY); - return NULL; - } - implicitly_created_streams_.erase(stream_id); if (stream_id > largest_peer_created_stream_id_) { - // TODO(rch) add unit test for this if (stream_id - largest_peer_created_stream_id_ > kMaxStreamIdDelta) { - connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID); + // We may already have sent a connection close due to multiple reset + // streams in the same packet. + if (connection()->connected()) { + LOG(ERROR) << "Trying to get stream: " << stream_id + << ", largest peer created stream: " + << largest_peer_created_stream_id_ + << ", max delta: " << kMaxStreamIdDelta; + connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID); + } return NULL; } if (largest_peer_created_stream_id_ == 0) { - largest_peer_created_stream_id_= 1; + if (is_server()) { + largest_peer_created_stream_id_= 3; + } else { + largest_peer_created_stream_id_= 1; + } } for (QuicStreamId id = largest_peer_created_stream_id_ + 2; id < stream_id; @@ -481,8 +672,8 @@ bool QuicSession::IsClosedStream(QuicStreamId id) { if (id == kCryptoStreamId) { return false; } - if (ContainsKey(zombie_streams_, id)) { - return true; + if (id == kHeadersStreamId) { + return false; } if (ContainsKey(stream_map_, id)) { // Stream is active @@ -500,11 +691,22 @@ bool QuicSession::IsClosedStream(QuicStreamId id) { } size_t QuicSession::GetNumOpenStreams() const { - return stream_map_.size() + implicitly_created_streams_.size() - - zombie_streams_.size(); + return stream_map_.size() + implicitly_created_streams_.size(); } void QuicSession::MarkWriteBlocked(QuicStreamId id, QuicPriority priority) { +#ifndef NDEBUG + ReliableQuicStream* stream = GetStream(id); + if (stream != NULL) { + LOG_IF(DFATAL, priority != stream->EffectivePriority()) + << ENDPOINT << "Stream " << id + << "Priorities do not match. Got: " << priority + << " Expected: " << stream->EffectivePriority(); + } else { + LOG(DFATAL) << "Marking unknown stream " << id << " blocked."; + } +#endif + if (id == kCryptoStreamId) { DCHECK(!has_pending_handshake_); has_pending_handshake_ = true; @@ -516,17 +718,13 @@ void QuicSession::MarkWriteBlocked(QuicStreamId id, QuicPriority priority) { write_blocked_streams_.PushBack(id, priority); } -bool QuicSession::HasQueuedData() const { - return write_blocked_streams_.NumBlockedStreams() || - connection_->HasQueuedData(); -} - -void QuicSession::MarkDecompressionBlocked(QuicHeaderId header_id, - QuicStreamId stream_id) { - decompression_blocked_streams_[header_id] = stream_id; +bool QuicSession::HasDataToWrite() const { + return write_blocked_streams_.HasWriteBlockedCryptoOrHeadersStream() || + write_blocked_streams_.HasWriteBlockedDataStreams() || + connection_->HasQueuedData(); } -bool QuicSession::GetSSLInfo(SSLInfo* ssl_info) { +bool QuicSession::GetSSLInfo(SSLInfo* ssl_info) const { NOTIMPLEMENTED(); return false; } @@ -536,4 +734,19 @@ void QuicSession::PostProcessAfterData() { closed_streams_.clear(); } +void QuicSession::OnSuccessfulVersionNegotiation(const QuicVersion& version) { + if (version < QUIC_VERSION_19) { + flow_controller_->Disable(); + } + + // Inform all streams about the negotiated version. They may have been created + // with a different version. + for (DataStreamMap::iterator it = stream_map_.begin(); + it != stream_map_.end(); ++it) { + if (version < QUIC_VERSION_17) { + it->second->flow_controller()->Disable(); + } + } +} + } // namespace net diff --git a/chromium/net/quic/quic_session.h b/chromium/net/quic/quic_session.h index 083990b55f6..1b31280020c 100644 --- a/chromium/net/quic/quic_session.h +++ b/chromium/net/quic/quic_session.h @@ -16,16 +16,16 @@ #include "net/quic/quic_connection.h" #include "net/quic/quic_crypto_stream.h" #include "net/quic/quic_data_stream.h" +#include "net/quic/quic_headers_stream.h" #include "net/quic/quic_packet_creator.h" #include "net/quic/quic_protocol.h" -#include "net/quic/quic_spdy_compressor.h" -#include "net/quic/quic_spdy_decompressor.h" +#include "net/quic/quic_write_blocked_list.h" #include "net/quic/reliable_quic_stream.h" -#include "net/spdy/write_blocked_list.h" namespace net { class QuicCryptoStream; +class QuicFlowController; class ReliableQuicStream; class SSLInfo; class VisitorShim; @@ -53,42 +53,75 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { HANDSHAKE_CONFIRMED, }; - QuicSession(QuicConnection* connection, - const QuicConfig& config); + QuicSession(QuicConnection* connection, const QuicConfig& config); virtual ~QuicSession(); // QuicConnectionVisitorInterface methods: - virtual bool OnStreamFrames( + virtual void OnStreamFrames( const std::vector<QuicStreamFrame>& frames) OVERRIDE; virtual void OnRstStream(const QuicRstStreamFrame& frame) OVERRIDE; virtual void OnGoAway(const QuicGoAwayFrame& frame) OVERRIDE; + virtual void OnWindowUpdateFrames( + const std::vector<QuicWindowUpdateFrame>& frames) OVERRIDE; + virtual void OnBlockedFrames( + const std::vector<QuicBlockedFrame>& frames) OVERRIDE; virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE; + virtual void OnWriteBlocked() OVERRIDE {} virtual void OnSuccessfulVersionNegotiation( - const QuicVersion& version) OVERRIDE {} - virtual void OnConfigNegotiated() OVERRIDE; - // Not needed for HTTP. - virtual bool OnCanWrite() OVERRIDE; + const QuicVersion& version) OVERRIDE; + virtual void OnCanWrite() OVERRIDE; + virtual bool WillingAndAbleToWrite() const OVERRIDE; virtual bool HasPendingHandshake() const OVERRIDE; + virtual bool HasOpenDataStreams() const OVERRIDE; + + // Called by the headers stream when headers have been received for a stream. + virtual void OnStreamHeaders(QuicStreamId stream_id, + base::StringPiece headers_data); + // Called by the headers stream when headers with a priority have been + // received for this stream. This method will only be called for server + // streams. + virtual void OnStreamHeadersPriority(QuicStreamId stream_id, + QuicPriority priority); + // Called by the headers stream when headers have been completely received + // for a stream. |fin| will be true if the fin flag was set in the headers + // frame. + virtual void OnStreamHeadersComplete(QuicStreamId stream_id, + bool fin, + size_t frame_len); // Called by streams when they want to write data to the peer. // Returns a pair with the number of bytes consumed from data, and a boolean // indicating if the fin bit was consumed. This does not indicate the data // has been sent on the wire: it may have been turned into a packet and queued - // if the socket was unexpectedly blocked. + // if the socket was unexpectedly blocked. |fec_protection| indicates if + // data is to be FEC protected. Note that data that is sent immediately + // following MUST_FEC_PROTECT data may get protected by falling within the + // same FEC group. // If provided, |ack_notifier_delegate| will be registered to be notified when - // we have seen ACKs for all packets resulting from this call. Not owned by - // this class. + // we have seen ACKs for all packets resulting from this call. virtual QuicConsumedData WritevData( QuicStreamId id, - const struct iovec* iov, - int iov_count, + const IOVector& data, QuicStreamOffset offset, bool fin, + FecProtection fec_protection, + QuicAckNotifier::DelegateInterface* ack_notifier_delegate); + + // Writes |headers| for the stream |id| to the dedicated headers stream. + // If |fin| is true, then no more data will be sent for the stream |id|. + // If provided, |ack_notifier_delegate| will be registered to be notified when + // we have seen ACKs for all packets resulting from this call. + size_t WriteHeaders( + QuicStreamId id, + const SpdyHeaderBlock& headers, + bool fin, QuicAckNotifier::DelegateInterface* ack_notifier_delegate); // Called by streams when they want to close the stream in both directions. - virtual void SendRstStream(QuicStreamId id, QuicRstStreamErrorCode error); + virtual void SendRstStream(QuicStreamId id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written); // Called when the session wants to go away and not accept any new streams. void SendGoAway(QuicErrorCode error_code, const std::string& reason); @@ -104,6 +137,9 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // a server, returns true if a full, valid client hello has been received. virtual bool IsCryptoHandshakeConfirmed(); + // Called by the QuicCryptoStream when a new QuicConfig has been negotiated. + virtual void OnConfigNegotiated(); + // Called by the QuicCryptoStream when the handshake enters a new state. // // Clients will call this function in the order: @@ -137,24 +173,20 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { const IPEndPoint& peer_address() const { return connection_->peer_address(); } - QuicGuid guid() const { return connection_->guid(); } - - QuicPacketCreator::Options* options() { return connection()->options(); } + QuicConnectionId connection_id() const { + return connection_->connection_id(); + } // Returns the number of currently open streams, including those which have - // been implicitly created. + // been implicitly created, but excluding the reserved headers and crypto + // streams. virtual size_t GetNumOpenStreams() const; void MarkWriteBlocked(QuicStreamId id, QuicPriority priority); // Returns true if the session has data to be sent, either queued in the // connection, or in a write-blocked stream. - bool HasQueuedData() const; - - // Marks that |stream_id| is blocked waiting to decompress the - // headers identified by |decompression_id|. - void MarkDecompressionBlocked(QuicHeaderId decompression_id, - QuicStreamId stream_id); + bool HasDataToWrite() const; bool goaway_received() const { return goaway_received_; @@ -164,16 +196,15 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { return goaway_sent_; } - QuicSpdyDecompressor* decompressor() { return &decompressor_; } - QuicSpdyCompressor* compressor() { return &compressor_; } - // Gets the SSL connection information. - virtual bool GetSSLInfo(SSLInfo* ssl_info); + virtual bool GetSSLInfo(SSLInfo* ssl_info) const; QuicErrorCode error() const { return error_; } bool is_server() const { return connection_->is_server(); } + QuicFlowController* flow_controller() { return flow_controller_.get(); } + protected: typedef base::hash_map<QuicStreamId, QuicDataStream*> DataStreamMap; @@ -195,7 +226,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // Returns the stream id for a new stream. QuicStreamId GetNextStreamId(); - QuicDataStream* GetIncomingReliableStream(QuicStreamId stream_id); + QuicDataStream* GetIncomingDataStream(QuicStreamId stream_id); QuicDataStream* GetDataStream(const QuicStreamId stream_id); @@ -226,35 +257,32 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { friend class VisitorShim; // Performs the work required to close |stream_id|. If |locally_reset| - // then the stream has been reset by this endpoint, not by the peer. This - // means the stream may become a zombie stream which needs to stay - // around until headers have been decompressed. + // then the stream has been reset by this endpoint, not by the peer. void CloseStreamInner(QuicStreamId stream_id, bool locally_reset); - // Adds |stream_id| to the zobmie stream map, closing the oldest - // zombie stream if the set is full. - void AddZombieStream(QuicStreamId stream_id); + // When a stream is closed locally, it may not yet know how many bytes the + // peer sent on that stream. + // When this data arrives (via stream frame w. FIN, or RST) this method + // is called, and correctly updates the connection level flow controller. + void UpdateFlowControlOnFinalReceivedByteOffset( + QuicStreamId id, QuicStreamOffset final_byte_offset); - // Closes the zombie stream |stream_id| and removes it from the zombie - // stream map. - void CloseZombieStream(QuicStreamId stream_id); + // Called in OnConfigNegotiated when we receive a new stream level flow + // control window in a negotiated config. Closes the connection if invalid. + void OnNewStreamFlowControlWindow(uint32 new_window); - // Adds |stream_id| to the prematurely closed stream map, removing the - // oldest prematurely closed stream if the set is full. - void AddPrematurelyClosedStream(QuicStreamId stream_id); + // Called in OnConfigNegotiated when we receive a new session level flow + // control window in a negotiated config. Closes the connection if invalid. + void OnNewSessionFlowControlWindow(uint32 new_window); - scoped_ptr<QuicConnection> connection_; + // Keep track of highest received byte offset of locally closed streams, while + // waiting for a definitive final highest offset from the peer. + std::map<QuicStreamId, QuicStreamOffset> + locally_closed_streams_highest_offset_; - // Tracks the last 20 streams which closed without decompressing headers. - // This is for best-effort detection of an unrecoverable compression context. - // Ideally this would be a linked_hash_set as the boolean is unused. - linked_hash_map<QuicStreamId, bool> prematurely_closed_streams_; + scoped_ptr<QuicConnection> connection_; - // Streams which have been locally reset before decompressing headers - // from the peer. These streams need to stay open long enough to - // process any headers from the peer. - // Ideally this would be a linked_hash_set as the boolean is unused. - linked_hash_map<QuicStreamId, bool> zombie_streams_; + scoped_ptr<QuicHeadersStream> headers_stream_; // A shim to stand between the connection and the session, to handle stream // deletions. @@ -262,9 +290,6 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { std::vector<QuicDataStream*> closed_streams_; - QuicSpdyDecompressor decompressor_; - QuicSpdyCompressor compressor_; - QuicConfig config_; // Returns the maximum number of streams this connection can open. @@ -279,11 +304,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { base::hash_set<QuicStreamId> implicitly_created_streams_; // A list of streams which need to write more data. - WriteBlockedList<QuicStreamId> write_blocked_streams_; - - // A map of headers waiting to be compressed, and the streams - // they are associated with. - map<uint32, QuicStreamId> decompression_blocked_streams_; + QuicWriteBlockedList write_blocked_streams_; QuicStreamId largest_peer_created_stream_id_; @@ -298,6 +319,9 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // Indicate if there is pending data for the crypto stream. bool has_pending_handshake_; + // Used for session level flow control. + scoped_ptr<QuicFlowController> flow_controller_; + DISALLOW_COPY_AND_ASSIGN(QuicSession); }; diff --git a/chromium/net/quic/quic_session_test.cc b/chromium/net/quic/quic_session_test.cc index ecbafb5c6a2..aeb41037bee 100644 --- a/chromium/net/quic/quic_session_test.cc +++ b/chromium/net/quic/quic_session_test.cc @@ -7,31 +7,43 @@ #include <set> #include <vector> +#include "base/basictypes.h" #include "base/containers/hash_tables.h" -#include "net/quic/crypto/crypto_handshake.h" -#include "net/quic/quic_connection.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_crypto_stream.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_protocol.h" +#include "net/quic/quic_utils.h" +#include "net/quic/reliable_quic_stream.h" +#include "net/quic/test_tools/quic_config_peer.h" #include "net/quic/test_tools/quic_connection_peer.h" #include "net/quic/test_tools/quic_data_stream_peer.h" +#include "net/quic/test_tools/quic_flow_controller_peer.h" +#include "net/quic/test_tools/quic_session_peer.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/quic/test_tools/reliable_quic_stream_peer.h" #include "net/spdy/spdy_framer.h" +#include "net/test/gtest_util.h" #include "testing/gmock/include/gmock/gmock.h" +#include "testing/gmock_mutant.h" #include "testing/gtest/include/gtest/gtest.h" using base::hash_map; using std::set; using std::vector; -using testing::_; +using testing::CreateFunctor; using testing::InSequence; -using testing::InvokeWithoutArgs; +using testing::Invoke; +using testing::Return; using testing::StrictMock; +using testing::_; namespace net { namespace test { namespace { -const QuicPriority kSomeMiddlePriority = 2; +const QuicPriority kHighestPriority = 0; +const QuicPriority kSomeMiddlePriority = 3; class TestCryptoStream : public QuicCryptoStream { public: @@ -45,9 +57,15 @@ class TestCryptoStream : public QuicCryptoStream { handshake_confirmed_ = true; CryptoHandshakeMessage msg; string error_details; + session()->config()->SetInitialFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + session()->config()->SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + session()->config()->SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); session()->config()->ToHandshakeMessage(&msg); - const QuicErrorCode error = session()->config()->ProcessClientHello( - msg, &error_details); + const QuicErrorCode error = session()->config()->ProcessPeerHello( + msg, CLIENT, &error_details); EXPECT_EQ(QUIC_NO_ERROR, error); session()->OnConfigNegotiated(); session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); @@ -56,6 +74,15 @@ class TestCryptoStream : public QuicCryptoStream { MOCK_METHOD0(OnCanWrite, void()); }; +class TestHeadersStream : public QuicHeadersStream { + public: + explicit TestHeadersStream(QuicSession* session) + : QuicHeadersStream(session) { + } + + MOCK_METHOD0(OnCanWrite, void()); +}; + class TestStream : public QuicDataStream { public: TestStream(QuicStreamId id, QuicSession* session) @@ -64,10 +91,14 @@ class TestStream : public QuicDataStream { using ReliableQuicStream::CloseWriteSide; - virtual uint32 ProcessData(const char* data, uint32 data_len) { + virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE { return data_len; } + void SendBody(const string& data, bool fin) { + WriteOrBufferData(data, fin, NULL); + } + MOCK_METHOD0(OnCanWrite, void()); }; @@ -91,9 +122,10 @@ class StreamBlocker { class TestSession : public QuicSession { public: explicit TestSession(QuicConnection* connection) - : QuicSession(connection, DefaultQuicConfig()), - crypto_stream_(this) { - } + : QuicSession(connection, + DefaultQuicConfig()), + crypto_stream_(this), + writev_consumes_all_data_(false) {} virtual TestCryptoStream* GetCryptoStream() OVERRIDE { return &crypto_stream_; @@ -113,18 +145,53 @@ class TestSession : public QuicSession { return QuicSession::IsClosedStream(id); } - QuicDataStream* GetIncomingReliableStream(QuicStreamId stream_id) { - return QuicSession::GetIncomingReliableStream(stream_id); + QuicDataStream* GetIncomingDataStream(QuicStreamId stream_id) { + return QuicSession::GetIncomingDataStream(stream_id); } - TestCryptoStream crypto_stream_; + virtual QuicConsumedData WritevData( + QuicStreamId id, + const IOVector& data, + QuicStreamOffset offset, + bool fin, + FecProtection fec_protection, + QuicAckNotifier::DelegateInterface* ack_notifier_delegate) OVERRIDE { + // Always consumes everything. + if (writev_consumes_all_data_) { + return QuicConsumedData(data.TotalBufferSize(), fin); + } else { + return QuicSession::WritevData(id, data, offset, fin, fec_protection, + ack_notifier_delegate); + } + } + + void set_writev_consumes_all_data(bool val) { + writev_consumes_all_data_ = val; + } + + QuicConsumedData SendStreamData(QuicStreamId id) { + return WritevData(id, IOVector(), 0, true, MAY_FEC_PROTECT, NULL); + } + + using QuicSession::PostProcessAfterData; + + private: + StrictMock<TestCryptoStream> crypto_stream_; + + bool writev_consumes_all_data_; }; -class QuicSessionTest : public ::testing::Test { +class QuicSessionTest : public ::testing::TestWithParam<QuicVersion> { protected: QuicSessionTest() - : connection_(new MockConnection(true)), + : connection_(new MockConnection(true, SupportedVersions(GetParam()))), session_(connection_) { + session_.config()->SetInitialFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + session_.config()->SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + session_.config()->SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); headers_[":host"] = "www.google.com"; headers_[":path"] = "/index.hml"; headers_[":scheme"] = "http"; @@ -169,46 +236,49 @@ class QuicSessionTest : public ::testing::Test { closed_streams_.insert(id); } + QuicVersion version() const { return connection_->version(); } + MockConnection* connection_; TestSession session_; set<QuicStreamId> closed_streams_; SpdyHeaderBlock headers_; }; -TEST_F(QuicSessionTest, PeerAddress) { +INSTANTIATE_TEST_CASE_P(Tests, QuicSessionTest, + ::testing::ValuesIn(QuicSupportedVersions())); + +TEST_P(QuicSessionTest, PeerAddress) { EXPECT_EQ(IPEndPoint(Loopback4(), kTestPort), session_.peer_address()); } -TEST_F(QuicSessionTest, IsCryptoHandshakeConfirmed) { +TEST_P(QuicSessionTest, IsCryptoHandshakeConfirmed) { EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed()); CryptoHandshakeMessage message; - session_.crypto_stream_.OnHandshakeMessage(message); + session_.GetCryptoStream()->OnHandshakeMessage(message); EXPECT_TRUE(session_.IsCryptoHandshakeConfirmed()); } -TEST_F(QuicSessionTest, IsClosedStreamDefault) { +TEST_P(QuicSessionTest, IsClosedStreamDefault) { // Ensure that no streams are initially closed. for (int i = kCryptoStreamId; i < 100; i++) { - EXPECT_FALSE(session_.IsClosedStream(i)); + EXPECT_FALSE(session_.IsClosedStream(i)) << "stream id: " << i; } } -TEST_F(QuicSessionTest, ImplicitlyCreatedStreams) { - ASSERT_TRUE(session_.GetIncomingReliableStream(7) != NULL); +TEST_P(QuicSessionTest, ImplicitlyCreatedStreams) { + ASSERT_TRUE(session_.GetIncomingDataStream(7) != NULL); // Both 3 and 5 should be implicitly created. EXPECT_FALSE(session_.IsClosedStream(3)); EXPECT_FALSE(session_.IsClosedStream(5)); - ASSERT_TRUE(session_.GetIncomingReliableStream(5) != NULL); - ASSERT_TRUE(session_.GetIncomingReliableStream(3) != NULL); + ASSERT_TRUE(session_.GetIncomingDataStream(5) != NULL); + ASSERT_TRUE(session_.GetIncomingDataStream(3) != NULL); } -TEST_F(QuicSessionTest, IsClosedStreamLocallyCreated) { +TEST_P(QuicSessionTest, IsClosedStreamLocallyCreated) { TestStream* stream2 = session_.CreateOutgoingDataStream(); EXPECT_EQ(2u, stream2->id()); - QuicDataStreamPeer::SetHeadersDecompressed(stream2, true); TestStream* stream4 = session_.CreateOutgoingDataStream(); EXPECT_EQ(4u, stream4->id()); - QuicDataStreamPeer::SetHeadersDecompressed(stream4, true); CheckClosedStreams(); CloseStream(4); @@ -217,43 +287,78 @@ TEST_F(QuicSessionTest, IsClosedStreamLocallyCreated) { CheckClosedStreams(); } -TEST_F(QuicSessionTest, IsClosedStreamPeerCreated) { - QuicDataStream* stream3 = session_.GetIncomingReliableStream(3); - QuicDataStreamPeer::SetHeadersDecompressed(stream3, true); - QuicDataStream* stream5 = session_.GetIncomingReliableStream(5); - QuicDataStreamPeer::SetHeadersDecompressed(stream5, true); +TEST_P(QuicSessionTest, IsClosedStreamPeerCreated) { + QuicStreamId stream_id1 = kClientDataStreamId1; + QuicStreamId stream_id2 = kClientDataStreamId2; + QuicDataStream* stream1 = session_.GetIncomingDataStream(stream_id1); + QuicDataStreamPeer::SetHeadersDecompressed(stream1, true); + QuicDataStream* stream2 = session_.GetIncomingDataStream(stream_id2); + QuicDataStreamPeer::SetHeadersDecompressed(stream2, true); CheckClosedStreams(); - CloseStream(3); + CloseStream(stream_id1); CheckClosedStreams(); - CloseStream(5); - // Create stream id 9, and implicitly 7 - QuicDataStream* stream9 = session_.GetIncomingReliableStream(9); - QuicDataStreamPeer::SetHeadersDecompressed(stream9, true); + CloseStream(stream_id2); + // Create a stream explicitly, and another implicitly. + QuicDataStream* stream3 = session_.GetIncomingDataStream(stream_id2 + 4); + QuicDataStreamPeer::SetHeadersDecompressed(stream3, true); CheckClosedStreams(); - // Close 9, but make sure 7 is still not closed - CloseStream(9); + // Close one, but make sure the other is still not closed + CloseStream(stream3->id()); CheckClosedStreams(); } -TEST_F(QuicSessionTest, StreamIdTooLarge) { - session_.GetIncomingReliableStream(3); +TEST_P(QuicSessionTest, StreamIdTooLarge) { + QuicStreamId stream_id = kClientDataStreamId1; + session_.GetIncomingDataStream(stream_id); EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_STREAM_ID)); - session_.GetIncomingReliableStream(105); + session_.GetIncomingDataStream(stream_id + kMaxStreamIdDelta + 2); +} + +TEST_P(QuicSessionTest, DecompressionError) { + QuicHeadersStream* stream = QuicSessionPeer::GetHeadersStream(&session_); + const unsigned char data[] = { + 0x80, 0x03, 0x00, 0x01, // SPDY/3 SYN_STREAM frame + 0x00, 0x00, 0x00, 0x25, // flags/length + 0x00, 0x00, 0x00, 0x05, // stream id + 0x00, 0x00, 0x00, 0x00, // associated stream id + 0x00, 0x00, + 'a', 'b', 'c', 'd' // invalid compressed data + }; + EXPECT_CALL(*connection_, + SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY framing error.")); + stream->ProcessRawData(reinterpret_cast<const char*>(data), + arraysize(data)); } -TEST_F(QuicSessionTest, DecompressionError) { - ReliableQuicStream* stream = session_.GetIncomingReliableStream(3); - EXPECT_CALL(*connection_, SendConnectionClose(QUIC_DECOMPRESSION_FAILURE)); - const char data[] = - "\0\0\0\0" // priority - "\1\0\0\0" // headers id - "\0\0\0\4" // length - "abcd"; // invalid compressed data - stream->ProcessRawData(data, arraysize(data)); +TEST_P(QuicSessionTest, DebugDFatalIfMarkingClosedStreamWriteBlocked) { + TestStream* stream2 = session_.CreateOutgoingDataStream(); + // Close the stream. + stream2->Reset(QUIC_BAD_APPLICATION_PAYLOAD); + // TODO(rtenneti): enable when chromium supports EXPECT_DEBUG_DFATAL. + /* + QuicStreamId kClosedStreamId = stream2->id(); + EXPECT_DEBUG_DFATAL( + session_.MarkWriteBlocked(kClosedStreamId, kSomeMiddlePriority), + "Marking unknown stream 2 blocked."); + */ } -TEST_F(QuicSessionTest, OnCanWrite) { +TEST_P(QuicSessionTest, DebugDFatalIfMarkWriteBlockedCalledWithWrongPriority) { + const QuicPriority kDifferentPriority = 0; + + TestStream* stream2 = session_.CreateOutgoingDataStream(); + EXPECT_NE(kDifferentPriority, stream2->EffectivePriority()); + // TODO(rtenneti): enable when chromium supports EXPECT_DEBUG_DFATAL. + /* + EXPECT_DEBUG_DFATAL( + session_.MarkWriteBlocked(stream2->id(), kDifferentPriority), + "Priorities do not match. Got: 0 Expected: 3"); + */ +} + +TEST_P(QuicSessionTest, OnCanWrite) { TestStream* stream2 = session_.CreateOutgoingDataStream(); TestStream* stream4 = session_.CreateOutgoingDataStream(); TestStream* stream6 = session_.CreateOutgoingDataStream(); @@ -264,16 +369,99 @@ TEST_F(QuicSessionTest, OnCanWrite) { InSequence s; StreamBlocker stream2_blocker(&session_, stream2->id()); - EXPECT_CALL(*stream2, OnCanWrite()).WillOnce( - // Reregister, to test the loop limit. - InvokeWithoutArgs(&stream2_blocker, &StreamBlocker::MarkWriteBlocked)); + // Reregister, to test the loop limit. + EXPECT_CALL(*stream2, OnCanWrite()) + .WillOnce(Invoke(&stream2_blocker, &StreamBlocker::MarkWriteBlocked)); EXPECT_CALL(*stream6, OnCanWrite()); EXPECT_CALL(*stream4, OnCanWrite()); + session_.OnCanWrite(); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); +} + +TEST_P(QuicSessionTest, OnCanWriteBundlesStreams) { + // Drive congestion control manually. + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); - EXPECT_FALSE(session_.OnCanWrite()); + TestStream* stream2 = session_.CreateOutgoingDataStream(); + TestStream* stream4 = session_.CreateOutgoingDataStream(); + TestStream* stream6 = session_.CreateOutgoingDataStream(); + + session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority); + session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority); + session_.MarkWriteBlocked(stream4->id(), kSomeMiddlePriority); + + EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillRepeatedly( + Return(QuicTime::Delta::Zero())); + EXPECT_CALL(*send_algorithm, GetCongestionWindow()) + .WillOnce(Return(kMaxPacketSize * 10)); + EXPECT_CALL(*stream2, OnCanWrite()) + .WillOnce(IgnoreResult(Invoke(CreateFunctor( + &session_, &TestSession::SendStreamData, stream2->id())))); + EXPECT_CALL(*stream4, OnCanWrite()) + .WillOnce(IgnoreResult(Invoke(CreateFunctor( + &session_, &TestSession::SendStreamData, stream4->id())))); + EXPECT_CALL(*stream6, OnCanWrite()) + .WillOnce(IgnoreResult(Invoke(CreateFunctor( + &session_, &TestSession::SendStreamData, stream6->id())))); + + // Expect that we only send one packet, the writes from different streams + // should be bundled together. + MockPacketWriter* writer = + static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _)).WillOnce( + Return(WriteResult(WRITE_STATUS_OK, 0))); + EXPECT_CALL(*send_algorithm, OnPacketSent(_, _, _, _, _)).Times(1); + session_.OnCanWrite(); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); } -TEST_F(QuicSessionTest, BufferedHandshake) { +TEST_P(QuicSessionTest, OnCanWriteCongestionControlBlocks) { + InSequence s; + + // Drive congestion control manually. + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); + + TestStream* stream2 = session_.CreateOutgoingDataStream(); + TestStream* stream4 = session_.CreateOutgoingDataStream(); + TestStream* stream6 = session_.CreateOutgoingDataStream(); + + session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority); + session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority); + session_.MarkWriteBlocked(stream4->id(), kSomeMiddlePriority); + + StreamBlocker stream2_blocker(&session_, stream2->id()); + EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return( + QuicTime::Delta::Zero())); + EXPECT_CALL(*stream2, OnCanWrite()); + EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return( + QuicTime::Delta::Zero())); + EXPECT_CALL(*stream6, OnCanWrite()); + EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return( + QuicTime::Delta::Infinite())); + // stream4->OnCanWrite is not called. + + session_.OnCanWrite(); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); + + // Still congestion-control blocked. + EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return( + QuicTime::Delta::Infinite())); + session_.OnCanWrite(); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); + + // stream4->OnCanWrite is called once the connection stops being + // congestion-control blocked. + EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return( + QuicTime::Delta::Zero())); + EXPECT_CALL(*stream4, OnCanWrite()); + session_.OnCanWrite(); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); +} + +TEST_P(QuicSessionTest, BufferedHandshake) { EXPECT_FALSE(session_.HasPendingHandshake()); // Default value. // Test that blocking other streams does not change our status. @@ -288,7 +476,7 @@ TEST_F(QuicSessionTest, BufferedHandshake) { EXPECT_FALSE(session_.HasPendingHandshake()); // Blocking (due to buffering of) the Crypto stream is detected. - session_.MarkWriteBlocked(kCryptoStreamId, kSomeMiddlePriority); + session_.MarkWriteBlocked(kCryptoStreamId, kHighestPriority); EXPECT_TRUE(session_.HasPendingHandshake()); TestStream* stream4 = session_.CreateOutgoingDataStream(); @@ -307,20 +495,19 @@ TEST_F(QuicSessionTest, BufferedHandshake) { EXPECT_CALL(*crypto_stream, OnCanWrite()); // Re-register all other streams, to show they weren't able to proceed. - EXPECT_CALL(*stream2, OnCanWrite()).WillOnce( - InvokeWithoutArgs(&stream2_blocker, &StreamBlocker::MarkWriteBlocked)); - - EXPECT_CALL(*stream3, OnCanWrite()).WillOnce( - InvokeWithoutArgs(&stream3_blocker, &StreamBlocker::MarkWriteBlocked)); - - EXPECT_CALL(*stream4, OnCanWrite()).WillOnce( - InvokeWithoutArgs(&stream4_blocker, &StreamBlocker::MarkWriteBlocked)); - - EXPECT_FALSE(session_.OnCanWrite()); + EXPECT_CALL(*stream2, OnCanWrite()) + .WillOnce(Invoke(&stream2_blocker, &StreamBlocker::MarkWriteBlocked)); + EXPECT_CALL(*stream3, OnCanWrite()) + .WillOnce(Invoke(&stream3_blocker, &StreamBlocker::MarkWriteBlocked)); + EXPECT_CALL(*stream4, OnCanWrite()) + .WillOnce(Invoke(&stream4_blocker, &StreamBlocker::MarkWriteBlocked)); + + session_.OnCanWrite(); + EXPECT_TRUE(session_.WillingAndAbleToWrite()); EXPECT_FALSE(session_.HasPendingHandshake()); // Crypto stream wrote. } -TEST_F(QuicSessionTest, OnCanWriteWithClosedStream) { +TEST_P(QuicSessionTest, OnCanWriteWithClosedStream) { TestStream* stream2 = session_.CreateOutgoingDataStream(); TestStream* stream4 = session_.CreateOutgoingDataStream(); TestStream* stream6 = session_.CreateOutgoingDataStream(); @@ -333,146 +520,410 @@ TEST_F(QuicSessionTest, OnCanWriteWithClosedStream) { InSequence s; EXPECT_CALL(*stream2, OnCanWrite()); EXPECT_CALL(*stream4, OnCanWrite()); - EXPECT_TRUE(session_.OnCanWrite()); + session_.OnCanWrite(); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); } -// Regression test for http://crbug.com/248737 -TEST_F(QuicSessionTest, OutOfOrderHeaders) { - QuicSpdyCompressor compressor; - vector<QuicStreamFrame> frames; - QuicPacketHeader header; - header.public_header.guid = session_.guid(); - - TestStream* stream2 = session_.CreateOutgoingDataStream(); - TestStream* stream4 = session_.CreateOutgoingDataStream(); - stream2->CloseWriteSide(); - stream4->CloseWriteSide(); - - // Create frame with headers for stream2. - string compressed_headers1 = compressor.CompressHeaders(headers_); - QuicStreamFrame frame1( - stream2->id(), false, 0, MakeIOVector(compressed_headers1)); - - // Create frame with headers for stream4. - string compressed_headers2 = compressor.CompressHeaders(headers_); - QuicStreamFrame frame2( - stream4->id(), true, 0, MakeIOVector(compressed_headers2)); - - // Process the second frame first. This will cause the headers to - // be queued up and processed after the first frame is processed. - frames.push_back(frame2); - session_.OnStreamFrames(frames); +TEST_P(QuicSessionTest, OnCanWriteLimitsNumWritesIfFlowControlBlocked) { + if (version() < QUIC_VERSION_19) { + return; + } - // Process the first frame, and un-cork the buffered headers. - frames[0] = frame1; - session_.OnStreamFrames(frames); + ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2, + true); + // Ensure connection level flow control blockage. + QuicFlowControllerPeer::SetSendWindowOffset(session_.flow_controller(), 0); + EXPECT_TRUE(session_.flow_controller()->IsBlocked()); + + // Mark the crypto and headers streams as write blocked, we expect them to be + // allowed to write later. + session_.MarkWriteBlocked(kCryptoStreamId, kHighestPriority); + session_.MarkWriteBlocked(kHeadersStreamId, kHighestPriority); + + // Create a data stream, and although it is write blocked we never expect it + // to be allowed to write as we are connection level flow control blocked. + TestStream* stream = session_.CreateOutgoingDataStream(); + session_.MarkWriteBlocked(stream->id(), kSomeMiddlePriority); + EXPECT_CALL(*stream, OnCanWrite()).Times(0); + + // The crypto and headers streams should be called even though we are + // connection flow control blocked. + TestCryptoStream* crypto_stream = session_.GetCryptoStream(); + EXPECT_CALL(*crypto_stream, OnCanWrite()).Times(1); + TestHeadersStream* headers_stream = new TestHeadersStream(&session_); + QuicSessionPeer::SetHeadersStream(&session_, headers_stream); + EXPECT_CALL(*headers_stream, OnCanWrite()).Times(1); - // Ensure that the streams actually close and we don't DCHECK. - connection_->CloseConnection(QUIC_CONNECTION_TIMED_OUT, true); + session_.OnCanWrite(); + EXPECT_FALSE(session_.WillingAndAbleToWrite()); } -TEST_F(QuicSessionTest, SendGoAway) { - // After sending a GoAway, ensure new incoming streams cannot be created and - // result in a RST being sent. +TEST_P(QuicSessionTest, SendGoAway) { EXPECT_CALL(*connection_, SendGoAway(QUIC_PEER_GOING_AWAY, 0u, "Going Away.")); session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away."); EXPECT_TRUE(session_.goaway_sent()); - EXPECT_CALL(*connection_, SendRstStream(3u, QUIC_STREAM_PEER_GOING_AWAY)); - EXPECT_FALSE(session_.GetIncomingReliableStream(3u)); + EXPECT_CALL(*connection_, + SendRstStream(3u, QUIC_STREAM_PEER_GOING_AWAY, 0)).Times(0); + EXPECT_TRUE(session_.GetIncomingDataStream(3u)); +} + +TEST_P(QuicSessionTest, DoNotSendGoAwayTwice) { + EXPECT_CALL(*connection_, + SendGoAway(QUIC_PEER_GOING_AWAY, 0u, "Going Away.")).Times(1); + session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away."); + EXPECT_TRUE(session_.goaway_sent()); + session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away."); } -TEST_F(QuicSessionTest, IncreasedTimeoutAfterCryptoHandshake) { +TEST_P(QuicSessionTest, IncreasedTimeoutAfterCryptoHandshake) { EXPECT_EQ(kDefaultInitialTimeoutSecs, QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); CryptoHandshakeMessage msg; - session_.crypto_stream_.OnHandshakeMessage(msg); + session_.GetCryptoStream()->OnHandshakeMessage(msg); EXPECT_EQ(kDefaultTimeoutSecs, QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); } -TEST_F(QuicSessionTest, ZombieStream) { - StrictMock<MockConnection>* connection = - new StrictMock<MockConnection>(false); - TestSession session(connection); - - TestStream* stream3 = session.CreateOutgoingDataStream(); - EXPECT_EQ(3u, stream3->id()); - TestStream* stream5 = session.CreateOutgoingDataStream(); - EXPECT_EQ(5u, stream5->id()); - EXPECT_EQ(2u, session.GetNumOpenStreams()); +TEST_P(QuicSessionTest, RstStreamBeforeHeadersDecompressed) { + // Send two bytes of payload. + QuicStreamFrame data1(kClientDataStreamId1, false, 0, MakeIOVector("HT")); + vector<QuicStreamFrame> frames; + frames.push_back(data1); + session_.OnStreamFrames(frames); + EXPECT_EQ(1u, session_.GetNumOpenStreams()); - // Reset the stream, but since the headers have not been decompressed - // it will become a zombie and will continue to process data - // until the headers are decompressed. - EXPECT_CALL(*connection, SendRstStream(3, QUIC_STREAM_CANCELLED)); - session.SendRstStream(3, QUIC_STREAM_CANCELLED); + QuicRstStreamFrame rst1(kClientDataStreamId1, QUIC_STREAM_NO_ERROR, 0); + session_.OnRstStream(rst1); + EXPECT_EQ(0u, session_.GetNumOpenStreams()); + // Connection should remain alive. + EXPECT_TRUE(connection_->connected()); +} - EXPECT_EQ(1u, session.GetNumOpenStreams()); +TEST_P(QuicSessionTest, MultipleRstStreamsCauseSingleConnectionClose) { + // If multiple invalid reset stream frames arrive in a single packet, this + // should trigger a connection close. However there is no need to send + // multiple connection close frames. + // Create valid stream. + QuicStreamFrame data1(kClientDataStreamId1, false, 0, MakeIOVector("HT")); vector<QuicStreamFrame> frames; - QuicPacketHeader header; - header.public_header.guid = session_.guid(); + frames.push_back(data1); + session_.OnStreamFrames(frames); + EXPECT_EQ(1u, session_.GetNumOpenStreams()); - // Create frame with headers for stream2. - QuicSpdyCompressor compressor; - string compressed_headers1 = compressor.CompressHeaders(headers_); - QuicStreamFrame frame1( - stream3->id(), false, 0, MakeIOVector(compressed_headers1)); + // Process first invalid stream reset, resulting in the connection being + // closed. + EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_STREAM_ID)) + .Times(1); + QuicStreamId kLargeInvalidStreamId = 99999999; + QuicRstStreamFrame rst1(kLargeInvalidStreamId, QUIC_STREAM_NO_ERROR, 0); + session_.OnRstStream(rst1); + QuicConnectionPeer::CloseConnection(connection_); - // Process the second frame first. This will cause the headers to - // be queued up and processed after the first frame is processed. - frames.push_back(frame1); - EXPECT_FALSE(stream3->headers_decompressed()); + // Processing of second invalid stream reset should not result in the + // connection being closed for a second time. + QuicRstStreamFrame rst2(kLargeInvalidStreamId, QUIC_STREAM_NO_ERROR, 0); + session_.OnRstStream(rst2); +} + +TEST_P(QuicSessionTest, HandshakeUnblocksFlowControlBlockedStream) { + // Test that if a stream is flow control blocked, then on receipt of the SHLO + // containing a suitable send window offset, the stream becomes unblocked. + if (version() < QUIC_VERSION_17) { + return; + } + ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true); - session.OnStreamFrames(frames); - EXPECT_EQ(1u, session.GetNumOpenStreams()); + // Ensure that Writev consumes all the data it is given (simulate no socket + // blocking). + session_.set_writev_consumes_all_data(true); + + // Create a stream, and send enough data to make it flow control blocked. + TestStream* stream2 = session_.CreateOutgoingDataStream(); + string body(kDefaultFlowControlSendWindow, '.'); + EXPECT_FALSE(stream2->flow_controller()->IsBlocked()); + stream2->SendBody(body, false); + EXPECT_TRUE(stream2->flow_controller()->IsBlocked()); - EXPECT_TRUE(connection->connected()); + // Now complete the crypto handshake, resulting in an increased flow control + // send window. + CryptoHandshakeMessage msg; + session_.GetCryptoStream()->OnHandshakeMessage(msg); + + // Stream is now unblocked. + EXPECT_FALSE(stream2->flow_controller()->IsBlocked()); } -TEST_F(QuicSessionTest, ZombieStreamConnectionClose) { - StrictMock<MockConnection>* connection = - new StrictMock<MockConnection>(false); - TestSession session(connection); +TEST_P(QuicSessionTest, InvalidFlowControlWindowInHandshake) { + // TODO(rjshade): Remove this test when removing QUIC_VERSION_19. + // Test that receipt of an invalid (< default) flow control window from + // the peer results in the connection being torn down. + if (version() <= QUIC_VERSION_16 || version() > QUIC_VERSION_19) { + return; + } + ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true); - TestStream* stream3 = session.CreateOutgoingDataStream(); - EXPECT_EQ(3u, stream3->id()); - TestStream* stream5 = session.CreateOutgoingDataStream(); - EXPECT_EQ(5u, stream5->id()); - EXPECT_EQ(2u, session.GetNumOpenStreams()); + uint32 kInvalidWindow = kDefaultFlowControlSendWindow - 1; + QuicConfigPeer::SetReceivedInitialFlowControlWindow(session_.config(), + kInvalidWindow); - stream3->CloseWriteSide(); - // Reset the stream, but since the headers have not been decompressed - // it will become a zombie and will continue to process data - // until the headers are decompressed. - EXPECT_CALL(*connection, SendRstStream(3, QUIC_STREAM_CANCELLED)); - session.SendRstStream(3, QUIC_STREAM_CANCELLED); + EXPECT_CALL(*connection_, + SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW)).Times(2); + session_.OnConfigNegotiated(); +} - EXPECT_EQ(1u, session.GetNumOpenStreams()); +TEST_P(QuicSessionTest, InvalidStreamFlowControlWindowInHandshake) { + // Test that receipt of an invalid (< default) stream flow control window from + // the peer results in the connection being torn down. + if (version() <= QUIC_VERSION_19) { + return; + } + ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true); - connection->CloseConnection(QUIC_CONNECTION_TIMED_OUT, false); + uint32 kInvalidWindow = kDefaultFlowControlSendWindow - 1; + QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(session_.config(), + kInvalidWindow); - EXPECT_EQ(0u, session.GetNumOpenStreams()); + EXPECT_CALL(*connection_, + SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW)); + session_.OnConfigNegotiated(); } -TEST_F(QuicSessionTest, RstStreamBeforeHeadersDecompressed) { - // Send two bytes of payload. - QuicStreamFrame data1(3, false, 0, MakeIOVector("HT")); +TEST_P(QuicSessionTest, InvalidSessionFlowControlWindowInHandshake) { + // Test that receipt of an invalid (< default) session flow control window + // from the peer results in the connection being torn down. + if (version() <= QUIC_VERSION_19) { + return; + } + ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true); + + uint32 kInvalidWindow = kDefaultFlowControlSendWindow - 1; + QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(session_.config(), + kInvalidWindow); + + EXPECT_CALL(*connection_, + SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW)); + session_.OnConfigNegotiated(); +} + +TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstOutOfOrder) { + if (version() < QUIC_VERSION_19) { + return; + } + + ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true); + ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2, + true); + // Test that when we receive an out of order stream RST we correctly adjust + // our connection level flow control receive window. + // On close, the stream should mark as consumed all bytes between the highest + // byte consumed so far and the final byte offset from the RST frame. + TestStream* stream = session_.CreateOutgoingDataStream(); + + const QuicStreamOffset kByteOffset = + 1 + kInitialSessionFlowControlWindowForTest / 2; + + // Expect no stream WINDOW_UPDATE frames, as stream read side closed. + EXPECT_CALL(*connection_, SendWindowUpdate(stream->id(), _)).Times(0); + // We do expect a connection level WINDOW_UPDATE when the stream is reset. + EXPECT_CALL(*connection_, + SendWindowUpdate(0, kInitialSessionFlowControlWindowForTest + + kByteOffset)).Times(1); + + QuicRstStreamFrame rst_frame(stream->id(), QUIC_STREAM_CANCELLED, + kByteOffset); + session_.OnRstStream(rst_frame); + session_.PostProcessAfterData(); + EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed()); +} + +TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAndLocalReset) { + if (version() < QUIC_VERSION_19) { + return; + } + + ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true); + ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2, + true); + // Test the situation where we receive a FIN on a stream, and before we fully + // consume all the data from the sequencer buffer we locally RST the stream. + // The bytes between highest consumed byte, and the final byte offset that we + // determined when the FIN arrived, should be marked as consumed at the + // connection level flow controller when the stream is reset. + TestStream* stream = session_.CreateOutgoingDataStream(); + + const QuicStreamOffset kByteOffset = + 1 + kInitialSessionFlowControlWindowForTest / 2; + QuicStreamFrame frame(stream->id(), true, kByteOffset, IOVector()); vector<QuicStreamFrame> frames; - frames.push_back(data1); - EXPECT_TRUE(session_.OnStreamFrames(frames)); - EXPECT_EQ(1u, session_.GetNumOpenStreams()); + frames.push_back(frame); + session_.OnStreamFrames(frames); + session_.PostProcessAfterData(); + + EXPECT_EQ(0u, stream->flow_controller()->bytes_consumed()); + EXPECT_EQ(kByteOffset, + stream->flow_controller()->highest_received_byte_offset()); + + // We only expect to see a connection WINDOW_UPDATE when talking + // QUIC_VERSION_19, as in this case both stream and session flow control + // windows are the same size. In later versions we will not see a connection + // level WINDOW_UPDATE when exhausting a stream, as the stream flow control + // limit is much lower than the connection flow control limit. + if (version() == QUIC_VERSION_19) { + // Expect no stream WINDOW_UPDATE frames, as stream read side closed. + EXPECT_CALL(*connection_, SendWindowUpdate(stream->id(), _)).Times(0); + // We do expect a connection level WINDOW_UPDATE when the stream is reset. + EXPECT_CALL(*connection_, + SendWindowUpdate(0, kInitialSessionFlowControlWindowForTest + + kByteOffset)).Times(1); + } - // Send a reset before the headers have been decompressed. This causes - // an unrecoverable compression context state. - EXPECT_CALL(*connection_, SendConnectionClose( - QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED)); + // Reset stream locally. + stream->Reset(QUIC_STREAM_CANCELLED); + EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed()); +} - QuicRstStreamFrame rst1(3, QUIC_STREAM_NO_ERROR); - session_.OnRstStream(rst1); - EXPECT_EQ(0u, session_.GetNumOpenStreams()); +TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAfterRst) { + // Test that when we RST the stream (and tear down stream state), and then + // receive a FIN from the peer, we correctly adjust our connection level flow + // control receive window. + if (version() < QUIC_VERSION_19) { + return; + } + + ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true); + ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2, + true); + // Connection starts with some non-zero highest received byte offset, + // due to other active streams. + const uint64 kInitialConnectionBytesConsumed = 567; + const uint64 kInitialConnectionHighestReceivedOffset = 1234; + EXPECT_LT(kInitialConnectionBytesConsumed, + kInitialConnectionHighestReceivedOffset); + session_.flow_controller()->UpdateHighestReceivedOffset( + kInitialConnectionHighestReceivedOffset); + session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed); + + // Reset our stream: this results in the stream being closed locally. + TestStream* stream = session_.CreateOutgoingDataStream(); + stream->Reset(QUIC_STREAM_CANCELLED); + + // Now receive a response from the peer with a FIN. We should handle this by + // adjusting the connection level flow control receive window to take into + // account the total number of bytes sent by the peer. + const QuicStreamOffset kByteOffset = 5678; + string body = "hello"; + IOVector data = MakeIOVector(body); + QuicStreamFrame frame(stream->id(), true, kByteOffset, data); + vector<QuicStreamFrame> frames; + frames.push_back(frame); + session_.OnStreamFrames(frames); + + QuicStreamOffset total_stream_bytes_sent_by_peer = + kByteOffset + body.length(); + EXPECT_EQ(kInitialConnectionBytesConsumed + total_stream_bytes_sent_by_peer, + session_.flow_controller()->bytes_consumed()); + EXPECT_EQ( + kInitialConnectionHighestReceivedOffset + total_stream_bytes_sent_by_peer, + session_.flow_controller()->highest_received_byte_offset()); +} + +TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstAfterRst) { + // Test that when we RST the stream (and tear down stream state), and then + // receive a RST from the peer, we correctly adjust our connection level flow + // control receive window. + if (version() < QUIC_VERSION_19) { + return; + } + + ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true); + ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2, + true); + // Connection starts with some non-zero highest received byte offset, + // due to other active streams. + const uint64 kInitialConnectionBytesConsumed = 567; + const uint64 kInitialConnectionHighestReceivedOffset = 1234; + EXPECT_LT(kInitialConnectionBytesConsumed, + kInitialConnectionHighestReceivedOffset); + session_.flow_controller()->UpdateHighestReceivedOffset( + kInitialConnectionHighestReceivedOffset); + session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed); + + // Reset our stream: this results in the stream being closed locally. + TestStream* stream = session_.CreateOutgoingDataStream(); + stream->Reset(QUIC_STREAM_CANCELLED); + + // Now receive a RST from the peer. We should handle this by adjusting the + // connection level flow control receive window to take into account the total + // number of bytes sent by the peer. + const QuicStreamOffset kByteOffset = 5678; + QuicRstStreamFrame rst_frame(stream->id(), QUIC_STREAM_CANCELLED, + kByteOffset); + session_.OnRstStream(rst_frame); + + EXPECT_EQ(kInitialConnectionBytesConsumed + kByteOffset, + session_.flow_controller()->bytes_consumed()); + EXPECT_EQ(kInitialConnectionHighestReceivedOffset + kByteOffset, + session_.flow_controller()->highest_received_byte_offset()); +} + +TEST_P(QuicSessionTest, FlowControlWithInvalidFinalOffset) { + // Test that if we receive a stream RST with a highest byte offset that + // violates flow control, that we close the connection. + if (version() < QUIC_VERSION_17) { + return; + } + ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true); + ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2, + true); + + const uint64 kLargeOffset = kInitialSessionFlowControlWindowForTest + 1; + EXPECT_CALL(*connection_, + SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA)) + .Times(2); + + // Check that stream frame + FIN results in connection close. + TestStream* stream = session_.CreateOutgoingDataStream(); + stream->Reset(QUIC_STREAM_CANCELLED); + QuicStreamFrame frame(stream->id(), true, kLargeOffset, IOVector()); + vector<QuicStreamFrame> frames; + frames.push_back(frame); + session_.OnStreamFrames(frames); + + // Check that RST results in connection close. + QuicRstStreamFrame rst_frame(stream->id(), QUIC_STREAM_CANCELLED, + kLargeOffset); + session_.OnRstStream(rst_frame); +} + +TEST_P(QuicSessionTest, VersionNegotiationDisablesFlowControl) { + if (version() < QUIC_VERSION_19) { + return; + } + + ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true); + ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2, + true); + // Test that after successful version negotiation, flow control is disabled + // appropriately at both the connection and stream level. + + // Initially both stream and connection flow control are enabled. + TestStream* stream = session_.CreateOutgoingDataStream(); + EXPECT_TRUE(stream->flow_controller()->IsEnabled()); + EXPECT_TRUE(session_.flow_controller()->IsEnabled()); + + // Version 17 implies that stream flow control is enabled, but connection + // level is disabled. + session_.OnSuccessfulVersionNegotiation(QUIC_VERSION_17); + EXPECT_FALSE(session_.flow_controller()->IsEnabled()); + EXPECT_TRUE(stream->flow_controller()->IsEnabled()); + + // Version 16 means all flow control is disabled. + session_.OnSuccessfulVersionNegotiation(QUIC_VERSION_16); + EXPECT_FALSE(session_.flow_controller()->IsEnabled()); + EXPECT_FALSE(stream->flow_controller()->IsEnabled()); } } // namespace diff --git a/chromium/net/quic/quic_socket_address_coder.cc b/chromium/net/quic/quic_socket_address_coder.cc new file mode 100644 index 00000000000..77e595f2d65 --- /dev/null +++ b/chromium/net/quic/quic_socket_address_coder.cc @@ -0,0 +1,89 @@ +// 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/quic_socket_address_coder.h" + +using std::string; + +namespace net { + +namespace { + +// For convenience, the values of these constants match the values of AF_INET +// and AF_INET6 on Linux. +const uint16 kIPv4 = 2; +const uint16 kIPv6 = 10; + +} // namespace + +QuicSocketAddressCoder::QuicSocketAddressCoder() { +} + +QuicSocketAddressCoder::QuicSocketAddressCoder(const IPEndPoint& address) + : address_(address) { +} + +QuicSocketAddressCoder::~QuicSocketAddressCoder() { +} + +string QuicSocketAddressCoder::Encode() const { + string serialized; + uint16 address_family; + switch (address_.GetSockAddrFamily()) { + case AF_INET: + address_family = kIPv4; + break; + case AF_INET6: + address_family = kIPv6; + break; + default: + return serialized; + } + serialized.append(reinterpret_cast<const char*>(&address_family), + sizeof(address_family)); + serialized.append(IPAddressToPackedString(address_.address())); + uint16 port = address_.port(); + serialized.append(reinterpret_cast<const char*>(&port), sizeof(port)); + return serialized; +} + +bool QuicSocketAddressCoder::Decode(const char* data, size_t length) { + uint16 address_family; + if (length < sizeof(address_family)) { + return false; + } + memcpy(&address_family, data, sizeof(address_family)); + data += sizeof(address_family); + length -= sizeof(address_family); + + size_t ip_length; + switch (address_family) { + case kIPv4: + ip_length = kIPv4AddressSize; + break; + case kIPv6: + ip_length = kIPv6AddressSize; + break; + default: + return false; + } + if (length < ip_length) { + return false; + } + IPAddressNumber ip(ip_length); + memcpy(&ip[0], data, ip_length); + data += ip_length; + length -= ip_length; + + uint16 port; + if (length != sizeof(port)) { + return false; + } + memcpy(&port, data, length); + + address_ = IPEndPoint(ip, port); + return true; +} + +} // namespace net diff --git a/chromium/net/quic/quic_socket_address_coder.h b/chromium/net/quic/quic_socket_address_coder.h new file mode 100644 index 00000000000..36ad1d03620 --- /dev/null +++ b/chromium/net/quic/quic_socket_address_coder.h @@ -0,0 +1,45 @@ +// 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_QUIC_SOCKET_ADDRESS_CODER_H_ +#define NET_QUIC_QUIC_SOCKET_ADDRESS_CODER_H_ + +#include <string> + +#include "base/basictypes.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_export.h" + +namespace net { + +// Serializes and parses a socket address (IP address and port), to be used in +// the kCADR tag in the ServerHello handshake message and the Public Reset +// packet. +class NET_EXPORT_PRIVATE QuicSocketAddressCoder { + public: + QuicSocketAddressCoder(); + explicit QuicSocketAddressCoder(const IPEndPoint& address); + ~QuicSocketAddressCoder(); + + std::string Encode() const; + + bool Decode(const char* data, size_t length); + + IPAddressNumber ip() const { + return address_.address(); + } + + uint16 port() const { + return address_.port(); + } + + private: + IPEndPoint address_; + + DISALLOW_COPY_AND_ASSIGN(QuicSocketAddressCoder); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_SOCKET_ADDRESS_CODER_H_ diff --git a/chromium/net/quic/quic_socket_address_coder_test.cc b/chromium/net/quic/quic_socket_address_coder_test.cc new file mode 100644 index 00000000000..56bed58b488 --- /dev/null +++ b/chromium/net/quic/quic_socket_address_coder_test.cc @@ -0,0 +1,117 @@ +// 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/quic_socket_address_coder.h" + +#include "testing/gtest/include/gtest/gtest.h" + +using std::string; + +namespace net { +namespace test { + +TEST(QuicSocketAddressCoderTest, EncodeIPv4) { + IPAddressNumber ip; + ASSERT_TRUE(ParseIPLiteralToNumber("4.31.198.44", &ip)); + QuicSocketAddressCoder coder(IPEndPoint(ip, 0x1234)); + string serialized = coder.Encode(); + string expected("\x02\x00\x04\x1f\xc6\x2c\x34\x12", 8); + EXPECT_EQ(expected, serialized); +} + +TEST(QuicSocketAddressCoderTest, EncodeIPv6) { + IPAddressNumber ip; + ASSERT_TRUE(ParseIPLiteralToNumber("2001:700:300:1800::f", &ip)); + QuicSocketAddressCoder coder(IPEndPoint(ip, 0x5678)); + string serialized = coder.Encode(); + string expected("\x0a\x00" + "\x20\x01\x07\x00\x03\x00\x18\x00" + "\x00\x00\x00\x00\x00\x00\x00\x0f" + "\x78\x56", 20); + EXPECT_EQ(expected, serialized); +} + +TEST(QuicSocketAddressCoderTest, DecodeIPv4) { + string serialized("\x02\x00\x04\x1f\xc6\x2c\x34\x12", 8); + QuicSocketAddressCoder coder; + ASSERT_TRUE(coder.Decode(serialized.data(), serialized.length())); + EXPECT_EQ(AF_INET, ConvertAddressFamily(GetAddressFamily(coder.ip()))); + string expected_addr("\x04\x1f\xc6\x2c", 4); + EXPECT_EQ(expected_addr, IPAddressToPackedString(coder.ip())); + EXPECT_EQ(0x1234, coder.port()); +} + +TEST(QuicSocketAddressCoderTest, DecodeIPv6) { + string serialized("\x0a\x00" + "\x20\x01\x07\x00\x03\x00\x18\x00" + "\x00\x00\x00\x00\x00\x00\x00\x0f" + "\x78\x56", 20); + QuicSocketAddressCoder coder; + ASSERT_TRUE(coder.Decode(serialized.data(), serialized.length())); + EXPECT_EQ(AF_INET6, ConvertAddressFamily(GetAddressFamily(coder.ip()))); + string expected_addr("\x20\x01\x07\x00\x03\x00\x18\x00" + "\x00\x00\x00\x00\x00\x00\x00\x0f", 16); + EXPECT_EQ(expected_addr, IPAddressToPackedString(coder.ip())); + EXPECT_EQ(0x5678, coder.port()); +} + +TEST(QuicSocketAddressCoderTest, DecodeBad) { + string serialized("\x0a\x00" + "\x20\x01\x07\x00\x03\x00\x18\x00" + "\x00\x00\x00\x00\x00\x00\x00\x0f" + "\x78\x56", 20); + QuicSocketAddressCoder coder; + EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length())); + // Append junk. + serialized.push_back('\0'); + EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length())); + // Undo. + serialized.resize(20); + EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length())); + + // Set an unknown address family. + serialized[0] = '\x03'; + EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length())); + // Undo. + serialized[0] = '\x0a'; + EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length())); + + // Truncate. + size_t len = serialized.length(); + for (size_t i = 0; i < len; i++) { + ASSERT_FALSE(serialized.empty()); + serialized.erase(serialized.length() - 1); + EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length())); + } + EXPECT_TRUE(serialized.empty()); +} + +TEST(QuicSocketAddressCoderTest, EncodeAndDecode) { + struct { + const char* ip_literal; + uint16 port; + } test_case[] = { + { "93.184.216.119", 0x1234 }, + { "199.204.44.194", 80 }, + { "149.20.4.69", 443 }, + { "127.0.0.1", 8080 }, + { "2001:700:300:1800::", 0x5678 }, + { "::1", 65534 }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_case); i++) { + IPAddressNumber ip; + ASSERT_TRUE(ParseIPLiteralToNumber(test_case[i].ip_literal, &ip)); + QuicSocketAddressCoder encoder(IPEndPoint(ip, test_case[i].port)); + string serialized = encoder.Encode(); + + QuicSocketAddressCoder decoder; + ASSERT_TRUE(decoder.Decode(serialized.data(), serialized.length())); + EXPECT_EQ(encoder.ip(), decoder.ip()); + EXPECT_EQ(encoder.port(), decoder.port()); + } +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_spdy_compressor.cc b/chromium/net/quic/quic_spdy_compressor.cc deleted file mode 100644 index 55015b0b42d..00000000000 --- a/chromium/net/quic/quic_spdy_compressor.cc +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/quic_spdy_compressor.h" - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" - -using std::string; - -namespace net { - -QuicSpdyCompressor::QuicSpdyCompressor() - : spdy_framer_(SPDY3), - header_sequence_id_(1) { - spdy_framer_.set_enable_compression(true); -} - -QuicSpdyCompressor::~QuicSpdyCompressor() { -} - -string QuicSpdyCompressor::CompressHeadersWithPriority( - QuicPriority priority, - const SpdyHeaderBlock& headers) { - return CompressHeadersInternal(priority, headers, true); -} - -string QuicSpdyCompressor::CompressHeaders( - const SpdyHeaderBlock& headers) { - // CompressHeadersInternal ignores priority when write_priority is false. - return CompressHeadersInternal(0 /* ignored */, headers, false); -} - -string QuicSpdyCompressor::CompressHeadersInternal( - QuicPriority priority, - const SpdyHeaderBlock& headers, - bool write_priority) { - // TODO(rch): Modify the SpdyFramer to expose a - // CreateCompressedHeaderBlock method, or some such. - SpdyStreamId stream_id = 3; // unused. - scoped_ptr<SpdyFrame> frame(spdy_framer_.CreateHeaders( - stream_id, CONTROL_FLAG_NONE, &headers)); - - // The size of the spdy HEADER frame's fixed prefix which - // needs to be stripped off from the resulting frame. - const size_t header_frame_prefix_len = 12; - string serialized = string(frame->data() + header_frame_prefix_len, - frame->size() - header_frame_prefix_len); - uint32 serialized_len = serialized.length(); - char priority_str[sizeof(priority)]; - memcpy(&priority_str, &priority, sizeof(priority)); - char id_str[sizeof(header_sequence_id_)]; - memcpy(&id_str, &header_sequence_id_, sizeof(header_sequence_id_)); - char len_str[sizeof(serialized_len)]; - memcpy(&len_str, &serialized_len, sizeof(serialized_len)); - string compressed; - int priority_len = write_priority ? arraysize(priority_str) : 0; - compressed.reserve( - priority_len + arraysize(id_str) + arraysize(len_str) + serialized_len); - if (write_priority) { - compressed.append(priority_str, arraysize(priority_str)); - } - compressed.append(id_str, arraysize(id_str)); - compressed.append(len_str, arraysize(len_str)); - compressed.append(serialized); - ++header_sequence_id_; - return compressed; -} - -} // namespace net diff --git a/chromium/net/quic/quic_spdy_compressor.h b/chromium/net/quic/quic_spdy_compressor.h deleted file mode 100644 index 6efb73c74bd..00000000000 --- a/chromium/net/quic/quic_spdy_compressor.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef NET_QUIC_QUIC_SPDY_COMPRESSOR_H_ -#define NET_QUIC_QUIC_SPDY_COMPRESSOR_H_ - -#include <string> - -#include "net/base/net_export.h" -#include "net/quic/quic_protocol.h" -#include "net/spdy/spdy_framer.h" - -namespace net { - -// Handles the compression of request/response headers blocks. The -// serialized format is: -// uint32 - Priority -// uint32 - Header ID -// uint32 - Compressed header length -// ... - Compressed data -// -class NET_EXPORT_PRIVATE QuicSpdyCompressor { - public: - QuicSpdyCompressor(); - ~QuicSpdyCompressor(); - - // Returns a string comprised of [header_sequence_id, compressed_headers]. - std::string CompressHeaders(const SpdyHeaderBlock& headers); - - // Returns a string comprised of - // [priority, header_sequence_id, compressed_headers] - std::string CompressHeadersWithPriority(QuicPriority priority, - const SpdyHeaderBlock& headers); - - private: - std::string CompressHeadersInternal(QuicPriority priority, - const SpdyHeaderBlock& headers, - bool write_priority); - - SpdyFramer spdy_framer_; - QuicHeaderId header_sequence_id_; - - DISALLOW_COPY_AND_ASSIGN(QuicSpdyCompressor); -}; - -} // namespace net - -#endif // NET_QUIC_QUIC_SPDY_COMPRESSOR_H_ diff --git a/chromium/net/quic/quic_spdy_compressor_test.cc b/chromium/net/quic/quic_spdy_compressor_test.cc deleted file mode 100644 index 93066f29091..00000000000 --- a/chromium/net/quic/quic_spdy_compressor_test.cc +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <string> - -#include "net/quic/quic_spdy_compressor.h" -#include "net/quic/quic_spdy_decompressor.h" -#include "net/quic/spdy_utils.h" -#include "net/quic/test_tools/quic_test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -using std::string; - -namespace net { -namespace test { -namespace { - -class QuicSpdyCompressorTest : public ::testing::Test { - protected: - TestDecompressorVisitor visitor_; -}; - -TEST_F(QuicSpdyCompressorTest, Compress) { - QuicSpdyCompressor compressor; - QuicSpdyDecompressor decompressor; - - SpdyHeaderBlock headers; - headers[":host"] = "www.google.com"; - headers[":path"] = "/index.hml"; - headers[":scheme"] = "https"; - - string compressed_headers = compressor.CompressHeaders(headers); - EXPECT_EQ('\1', compressed_headers[0]); - EXPECT_EQ('\0', compressed_headers[1]); - EXPECT_EQ('\0', compressed_headers[2]); - EXPECT_EQ('\0', compressed_headers[3]); - string compressed_data = compressed_headers.substr(4); - decompressor.DecompressData(compressed_data, &visitor_); - EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers), - visitor_.data()); -} - -} // namespace -} // namespace test -} // namespace net diff --git a/chromium/net/quic/quic_spdy_decompressor.cc b/chromium/net/quic/quic_spdy_decompressor.cc deleted file mode 100644 index f8bd4c142a8..00000000000 --- a/chromium/net/quic/quic_spdy_decompressor.cc +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/quic_spdy_decompressor.h" - -#include <algorithm> - -#include "base/logging.h" - -using base::StringPiece; -using std::min; - -namespace net { - -class SpdyFramerVisitor : public SpdyFramerVisitorInterface { - public: - explicit SpdyFramerVisitor(QuicSpdyDecompressor::Visitor* visitor) - : visitor_(visitor), - error_(false) { - } - - virtual void OnError(SpdyFramer* framer) OVERRIDE { - error_ = true; - } - virtual void OnDataFrameHeader(SpdyStreamId stream_id, - size_t length, - bool fin) OVERRIDE {} - virtual void OnStreamFrameData(SpdyStreamId stream_id, - const char* data, - size_t len, - bool fin) OVERRIDE {} - virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id, - const char* header_data, - size_t len) OVERRIDE; - virtual void OnSynStream(SpdyStreamId stream_id, - SpdyStreamId associated_stream_id, - SpdyPriority priority, - uint8 credential_slot, - bool fin, - bool unidirectional) OVERRIDE {} - virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE {} - virtual void OnRstStream(SpdyStreamId stream_id, - SpdyRstStreamStatus status) OVERRIDE {} - virtual void OnSetting(SpdySettingsIds id, - uint8 flags, - uint32 value) OVERRIDE {} - virtual void OnPing(uint32 unique_id) OVERRIDE {} - virtual void OnGoAway(SpdyStreamId last_accepted_stream_id, - SpdyGoAwayStatus status) OVERRIDE {} - virtual void OnHeaders(SpdyStreamId stream_id, bool fin) OVERRIDE {} - virtual void OnWindowUpdate(SpdyStreamId stream_id, - uint32 delta_window_size) OVERRIDE {} - virtual bool OnCredentialFrameData(const char* credential_data, - size_t len) OVERRIDE { - return false; - } - virtual void OnPushPromise(SpdyStreamId stream_id, - SpdyStreamId promised_stream_id) OVERRIDE {} - void set_visitor(QuicSpdyDecompressor::Visitor* visitor) { - DCHECK(visitor); - visitor_ = visitor; - } - - private: - QuicSpdyDecompressor::Visitor* visitor_; - bool error_; -}; - -bool SpdyFramerVisitor::OnControlFrameHeaderData(SpdyStreamId stream_id, - const char* header_data, - size_t len) { - DCHECK(visitor_); - return visitor_->OnDecompressedData(StringPiece(header_data, len)); -} - -QuicSpdyDecompressor::QuicSpdyDecompressor() - : spdy_framer_(SPDY3), - spdy_visitor_(new SpdyFramerVisitor(NULL)), - current_header_id_(1), - has_current_compressed_size_(false), - current_compressed_size_(0), - compressed_bytes_consumed_(0) { - spdy_framer_.set_visitor(spdy_visitor_.get()); -} - -QuicSpdyDecompressor::~QuicSpdyDecompressor() { -} - -size_t QuicSpdyDecompressor::DecompressData(StringPiece data, - Visitor* visitor) { - spdy_visitor_->set_visitor(visitor); - size_t bytes_consumed = 0; - - if (!has_current_compressed_size_) { - const size_t kCompressedBufferSizeSize = sizeof(uint32); - DCHECK_GT(kCompressedBufferSizeSize, compressed_size_buffer_.length()); - size_t missing_size = - kCompressedBufferSizeSize - compressed_size_buffer_.length(); - if (data.length() < missing_size) { - data.AppendToString(&compressed_size_buffer_); - return data.length(); - } - bytes_consumed += missing_size; - data.substr(0, missing_size).AppendToString(&compressed_size_buffer_); - DCHECK_EQ(kCompressedBufferSizeSize, compressed_size_buffer_.length()); - memcpy(¤t_compressed_size_, compressed_size_buffer_.data(), - kCompressedBufferSizeSize); - compressed_size_buffer_.clear(); - has_current_compressed_size_ = true; - data = data.substr(missing_size); - compressed_bytes_consumed_ = 0; - } - - size_t bytes_to_consume = - min(current_compressed_size_ - compressed_bytes_consumed_, - static_cast<uint32>(data.length())); - if (bytes_to_consume > 0) { - if (!spdy_framer_.IncrementallyDecompressControlFrameHeaderData( - current_header_id_, data.data(), bytes_to_consume)) { - visitor->OnDecompressionError(); - return bytes_consumed; - } - compressed_bytes_consumed_ += bytes_to_consume; - bytes_consumed += bytes_to_consume; - } - if (current_compressed_size_ - compressed_bytes_consumed_ == 0) { - ResetForNextHeaders(); - } - return bytes_consumed; -} - -void QuicSpdyDecompressor::ResetForNextHeaders() { - has_current_compressed_size_ = false; - current_compressed_size_ = 0; - compressed_bytes_consumed_ = 0; - ++current_header_id_; -} - -} // namespace net diff --git a/chromium/net/quic/quic_spdy_decompressor.h b/chromium/net/quic/quic_spdy_decompressor.h deleted file mode 100644 index a56c4799015..00000000000 --- a/chromium/net/quic/quic_spdy_decompressor.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef NET_QUIC_QUIC_SPDY_DECOMPRESSOR_H_ -#define NET_QUIC_QUIC_SPDY_DECOMPRESSOR_H_ - -#include <string> - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "base/strings/string_piece.h" -#include "net/base/net_export.h" -#include "net/quic/quic_protocol.h" -#include "net/spdy/spdy_framer.h" - -namespace net { - -class SpdyFramerVisitor; - -// Handles the compression of request/response headers blocks. -class NET_EXPORT_PRIVATE QuicSpdyDecompressor { - public: - // Interface that receives callbacks with decompressed data as it - // becomes available. - class NET_EXPORT_PRIVATE Visitor { - public: - virtual ~Visitor() {} - virtual bool OnDecompressedData(base::StringPiece data) = 0; - virtual void OnDecompressionError() = 0; - }; - - QuicSpdyDecompressor(); - ~QuicSpdyDecompressor(); - - // Decompresses the data in |data| and invokes |OnDecompressedData|, - // possibly multiple times, on |visitor|. Returns number of bytes - // consumed from |data|. - size_t DecompressData(base::StringPiece data, Visitor* visitor); - - QuicHeaderId current_header_id() { return current_header_id_; } - - private: - void ResetForNextHeaders(); - - SpdyFramer spdy_framer_; - scoped_ptr<SpdyFramerVisitor> spdy_visitor_; - // ID of the header currently being parsed. - QuicHeaderId current_header_id_; - // True when the size of the headers has been parsed. - bool has_current_compressed_size_; - // Size of the headers being parsed. - uint32 current_compressed_size_; - // Buffer into which the partial compressed size is written until - // it is fully parsed. - std::string compressed_size_buffer_; - // Number of compressed bytes consumed, out of the total in - // |current_compressed_size_|. - uint32 compressed_bytes_consumed_; - DISALLOW_COPY_AND_ASSIGN(QuicSpdyDecompressor); -}; - -} // namespace net - -#endif // NET_QUIC_QUIC_SPDY_DECOMPRESSOR_H_ diff --git a/chromium/net/quic/quic_spdy_decompressor_test.cc b/chromium/net/quic/quic_spdy_decompressor_test.cc deleted file mode 100644 index de9156bdfa4..00000000000 --- a/chromium/net/quic/quic_spdy_decompressor_test.cc +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <string> - -#include "net/quic/quic_spdy_compressor.h" -#include "net/quic/quic_spdy_decompressor.h" -#include "net/quic/spdy_utils.h" -#include "net/quic/test_tools/quic_test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -using std::string; - -namespace net { -namespace test { -namespace { - -class QuicSpdyDecompressorTest : public ::testing::Test { - protected: - QuicSpdyDecompressor decompressor_; - QuicSpdyCompressor compressor_; - TestDecompressorVisitor visitor_; -}; - -TEST_F(QuicSpdyDecompressorTest, Decompress) { - SpdyHeaderBlock headers; - headers[":host"] = "www.google.com"; - headers[":path"] = "/index.hml"; - headers[":scheme"] = "https"; - - EXPECT_EQ(1u, decompressor_.current_header_id()); - string compressed_headers = compressor_.CompressHeaders(headers).substr(4); - EXPECT_EQ(compressed_headers.length(), - decompressor_.DecompressData(compressed_headers, &visitor_)); - - EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers), visitor_.data()); - EXPECT_EQ(2u, decompressor_.current_header_id()); -} - -TEST_F(QuicSpdyDecompressorTest, DecompressPartial) { - SpdyHeaderBlock headers; - headers[":host"] = "www.google.com"; - headers[":path"] = "/index.hml"; - headers[":scheme"] = "https"; - string compressed_headers = compressor_.CompressHeaders(headers).substr(4); - - for (size_t i = 0; i < compressed_headers.length(); ++i) { - QuicSpdyDecompressor decompressor; - TestDecompressorVisitor visitor; - - EXPECT_EQ(1u, decompressor.current_header_id()); - - string partial_compressed_headers = compressed_headers.substr(0, i); - EXPECT_EQ(partial_compressed_headers.length(), - decompressor.DecompressData(partial_compressed_headers, - &visitor)); - EXPECT_EQ(1u, decompressor.current_header_id()) << "i: " << i; - - string remaining_compressed_headers = - compressed_headers.substr(partial_compressed_headers.length()); - EXPECT_EQ(remaining_compressed_headers.length(), - decompressor.DecompressData(remaining_compressed_headers, - &visitor)); - EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers), visitor.data()); - - EXPECT_EQ(2u, decompressor.current_header_id()); - } -} - -TEST_F(QuicSpdyDecompressorTest, DecompressAndIgnoreTrailingData) { - SpdyHeaderBlock headers; - headers[":host"] = "www.google.com"; - headers[":path"] = "/index.hml"; - headers[":scheme"] = "https"; - - string compressed_headers = compressor_.CompressHeaders(headers).substr(4); - EXPECT_EQ(compressed_headers.length(), - decompressor_.DecompressData(compressed_headers + "abc123", - &visitor_)); - - EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers), visitor_.data()); -} - -TEST_F(QuicSpdyDecompressorTest, DecompressError) { - SpdyHeaderBlock headers; - headers[":host"] = "www.google.com"; - headers[":path"] = "/index.hml"; - headers[":scheme"] = "https"; - - EXPECT_EQ(1u, decompressor_.current_header_id()); - string compressed_headers = compressor_.CompressHeaders(headers).substr(4); - compressed_headers[compressed_headers.length() - 1] ^= 0x01; - EXPECT_NE(compressed_headers.length(), - decompressor_.DecompressData(compressed_headers, &visitor_)); - - EXPECT_TRUE(visitor_.error()); - EXPECT_EQ("", visitor_.data()); -} - -} // namespace -} // namespace test -} // namespace net diff --git a/chromium/net/quic/quic_spdy_server_stream.cc b/chromium/net/quic/quic_spdy_server_stream.cc new file mode 100644 index 00000000000..81fc2e064e7 --- /dev/null +++ b/chromium/net/quic/quic_spdy_server_stream.cc @@ -0,0 +1,143 @@ +// 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/quic_spdy_server_stream.h" + +#include "base/memory/singleton.h" +#include "net/quic/quic_in_memory_cache.h" +#include "net/quic/quic_session.h" +#include "net/spdy/spdy_framer.h" +#include "net/tools/quic/spdy_utils.h" + +using base::StringPiece; +using std::string; +using net::tools::SpdyUtils; + +namespace net { + +static const size_t kHeaderBufInitialSize = 4096; + +QuicSpdyServerStream::QuicSpdyServerStream(QuicStreamId id, + QuicSession* session) + : QuicDataStream(id, session), + read_buf_(new GrowableIOBuffer()), + request_headers_received_(false) { +} + +QuicSpdyServerStream::~QuicSpdyServerStream() { +} + +uint32 QuicSpdyServerStream::ProcessData(const char* data, uint32 data_len) { + uint32 total_bytes_processed = 0; + + // Are we still reading the request headers. + if (!request_headers_received_) { + // Grow the read buffer if necessary. + if (read_buf_->RemainingCapacity() < (int)data_len) { + read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize); + } + memcpy(read_buf_->data(), data, data_len); + read_buf_->set_offset(read_buf_->offset() + data_len); + ParseRequestHeaders(); + } else { + body_.append(data + total_bytes_processed, + data_len - total_bytes_processed); + } + return data_len; +} + +void QuicSpdyServerStream::OnFinRead() { + ReliableQuicStream::OnFinRead(); + if (write_side_closed() || fin_buffered()) { + return; + } + + if (!request_headers_received_) { + SendErrorResponse(); // We're not done reading headers. + } else if ((headers_.content_length_status() == + BalsaHeadersEnums::VALID_CONTENT_LENGTH) && + body_.size() != headers_.content_length()) { + SendErrorResponse(); // Invalid content length + } else { + SendResponse(); + } +} + +int QuicSpdyServerStream::ParseRequestHeaders() { + size_t read_buf_len = static_cast<size_t>(read_buf_->offset()); + SpdyFramer framer(SPDY3); + SpdyHeaderBlock headers; + char* data = read_buf_->StartOfBuffer(); + size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_->offset(), + &headers); + if (len == 0) { + return -1; + } + + if (!SpdyUtils::FillBalsaRequestHeaders(headers, &headers_)) { + SendErrorResponse(); + return -1; + } + + size_t delta = read_buf_len - len; + if (delta > 0) { + body_.append(data + len, delta); + } + + request_headers_received_ = true; + return len; +} + +void QuicSpdyServerStream::SendResponse() { + // Find response in cache. If not found, send error response. + const QuicInMemoryCache::Response* response = + QuicInMemoryCache::GetInstance()->GetResponse(headers_); + if (response == NULL) { + SendErrorResponse(); + return; + } + + if (response->response_type() == QuicInMemoryCache::CLOSE_CONNECTION) { + DVLOG(1) << "Special response: closing connection."; + CloseConnection(QUIC_NO_ERROR); + return; + } + + if (response->response_type() == QuicInMemoryCache::IGNORE_REQUEST) { + DVLOG(1) << "Special response: ignoring request."; + return; + } + + DVLOG(1) << "Sending response for stream " << id(); + SendHeadersAndBody(response->headers(), response->body()); +} + +void QuicSpdyServerStream::SendErrorResponse() { + DVLOG(1) << "Sending error response for stream " << id(); + BalsaHeaders headers; + headers.SetResponseFirstlineFromStringPieces( + "HTTP/1.1", "500", "Server Error"); + headers.ReplaceOrAppendHeader("content-length", "3"); + SendHeadersAndBody(headers, "bad"); +} + +void QuicSpdyServerStream::SendHeadersAndBody( + const BalsaHeaders& response_headers, + StringPiece body) { + // We only support SPDY and HTTP, and neither handles bidirectional streaming. + if (!read_side_closed()) { + CloseReadSide(); + } + + SpdyHeaderBlock header_block = + SpdyUtils::ResponseHeadersToSpdyHeaders(response_headers); + + WriteHeaders(header_block, body.empty(), NULL); + + if (!body.empty()) { + WriteOrBufferData(body, true, NULL); + } +} + +} // namespace net diff --git a/chromium/net/quic/quic_spdy_server_stream.h b/chromium/net/quic/quic_spdy_server_stream.h new file mode 100644 index 00000000000..8a6808bd3bc --- /dev/null +++ b/chromium/net/quic/quic_spdy_server_stream.h @@ -0,0 +1,64 @@ +// 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_QUIC_SPDY_SERVER_STREAM_H_ +#define NET_QUIC_QUIC_SPDY_SERVER_STREAM_H_ + +#include <string> + +#include "base/basictypes.h" +#include "net/base/io_buffer.h" +#include "net/quic/quic_data_stream.h" +#include "net/quic/quic_protocol.h" +#include "net/tools/balsa/balsa_headers.h" + +namespace net { + +class QuicSession; + +namespace test { +class QuicSpdyServerStreamPeer; +} // namespace test + +// All this does right now is aggregate data, and on fin, send an HTTP +// response. +class QuicSpdyServerStream : public QuicDataStream { + public: + QuicSpdyServerStream(QuicStreamId id, QuicSession* session); + virtual ~QuicSpdyServerStream(); + + // ReliableQuicStream implementation called by the session when there's + // data for us. + virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE; + virtual void OnFinRead() OVERRIDE; + + int ParseRequestHeaders(); + + private: + friend class test::QuicSpdyServerStreamPeer; + + // Sends a basic 200 response using SendHeaders for the headers and WriteData + // for the body. + void SendResponse(); + + // Sends a basic 500 response using SendHeaders for the headers and WriteData + // for the body + void SendErrorResponse(); + + void SendHeadersAndBody(const BalsaHeaders& response_headers, + base::StringPiece body); + + BalsaHeaders headers_; + string body_; + + // Buffer into which response header data is read. + scoped_refptr<GrowableIOBuffer> read_buf_; + bool request_headers_received_; + + DISALLOW_COPY_AND_ASSIGN(QuicSpdyServerStream); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_SPDY_SERVER_STREAM_H_ diff --git a/chromium/net/quic/quic_stream_factory.cc b/chromium/net/quic/quic_stream_factory.cc index 036a91d38aa..2fc1b9e334c 100644 --- a/chromium/net/quic/quic_stream_factory.cc +++ b/chromium/net/quic/quic_stream_factory.cc @@ -6,7 +6,7 @@ #include <set> -#include "base/logging.h" +#include "base/cpu.h" #include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop_proxy.h" #include "base/metrics/histogram.h" @@ -22,6 +22,7 @@ #include "net/quic/congestion_control/tcp_receiver.h" #include "net/quic/crypto/proof_verifier_chromium.h" #include "net/quic/crypto/quic_random.h" +#include "net/quic/crypto/quic_server_info.h" #include "net/quic/port_suggester.h" #include "net/quic/quic_client_session.h" #include "net/quic/quic_clock.h" @@ -31,24 +32,117 @@ #include "net/quic/quic_default_packet_writer.h" #include "net/quic/quic_http_stream.h" #include "net/quic/quic_protocol.h" +#include "net/quic/quic_server_id.h" #include "net/socket/client_socket_factory.h" +#if defined(OS_WIN) +#include "base/win/windows_version.h" +#endif + using std::string; using std::vector; namespace net { +namespace { + +enum CreateSessionFailure { + CREATION_ERROR_CONNECTING_SOCKET, + CREATION_ERROR_SETTING_RECEIVE_BUFFER, + CREATION_ERROR_SETTING_SEND_BUFFER, + CREATION_ERROR_MAX +}; + +// When a connection is idle for 30 seconds it will be closed. +const int kIdleConnectionTimeoutSeconds = 30; + +// The initial receive window size for both streams and sessions. +const int32 kInitialReceiveWindowSize = 10 * 1024 * 1024; // 10MB + +// The suggested initial congestion windows for a server to use. +// TODO: This should be tested and optimized, and even better, suggest a window +// that corresponds to historical bandwidth and min-RTT. +// Larger initial congestion windows can, if we don't overshoot, reduce latency +// by avoiding the RTT needed for slow start to double (and re-double) from a +// default of 10. +// We match SPDY's use of 32 when secure (since we'd compete with SPDY). +const int32 kServerSecureInitialCongestionWindow = 32; +// Be conservative, and just use double a typical TCP ICWND for HTTP. +const int32 kServerInecureInitialCongestionWindow = 20; + +void HistogramCreateSessionFailure(enum CreateSessionFailure error) { + UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.CreationError", error, + CREATION_ERROR_MAX); +} + +bool IsEcdsaSupported() { +#if defined(OS_WIN) + if (base::win::GetVersion() < base::win::VERSION_VISTA) + return false; +#endif + + return true; +} + +QuicConfig InitializeQuicConfig(bool enable_pacing, + bool enable_time_based_loss_detection) { + QuicConfig config; + config.SetDefaults(); + config.EnablePacing(enable_pacing); + if (enable_time_based_loss_detection) + config.SetLossDetectionToSend(kTIME); + config.set_idle_connection_state_lifetime( + QuicTime::Delta::FromSeconds(kIdleConnectionTimeoutSeconds), + QuicTime::Delta::FromSeconds(kIdleConnectionTimeoutSeconds)); + return config; +} + +} // namespace + +QuicStreamFactory::IpAliasKey::IpAliasKey() {} + +QuicStreamFactory::IpAliasKey::IpAliasKey(IPEndPoint ip_endpoint, + bool is_https) + : ip_endpoint(ip_endpoint), + is_https(is_https) {} + +QuicStreamFactory::IpAliasKey::~IpAliasKey() {} + +bool QuicStreamFactory::IpAliasKey::operator<( + const QuicStreamFactory::IpAliasKey& other) const { + if (!(ip_endpoint == other.ip_endpoint)) { + return ip_endpoint < other.ip_endpoint; + } + return is_https < other.is_https; +} + +bool QuicStreamFactory::IpAliasKey::operator==( + const QuicStreamFactory::IpAliasKey& other) const { + return is_https == other.is_https && + ip_endpoint == other.ip_endpoint; +}; + // Responsible for creating a new QUIC session to the specified server, and // for notifying any associated requests when complete. class QuicStreamFactory::Job { public: Job(QuicStreamFactory* factory, HostResolver* host_resolver, - const HostPortProxyPair& host_port_proxy_pair, + const HostPortPair& host_port_pair, bool is_https, - CertVerifier* cert_verifier, + bool was_alternate_protocol_recently_broken, + PrivacyMode privacy_mode, + base::StringPiece method, + QuicServerInfo* server_info, const BoundNetLog& net_log); + // Creates a new job to handle the resumption of for connecting an + // existing session. + Job(QuicStreamFactory* factory, + HostResolver* host_resolver, + QuicClientSession* session, + QuicServerId server_id); + ~Job(); int Run(const CompletionCallback& callback); @@ -56,7 +150,10 @@ class QuicStreamFactory::Job { int DoLoop(int rv); int DoResolveHost(); int DoResolveHostComplete(int rv); + int DoLoadServerInfo(); + int DoLoadServerInfoComplete(int rv); int DoConnect(); + int DoResumeConnect(); int DoConnectComplete(int rv); void OnIOComplete(int rv); @@ -65,8 +162,8 @@ class QuicStreamFactory::Job { return callback_; } - const HostPortProxyPair& host_port_proxy_pair() const { - return host_port_proxy_pair_; + const QuicServerId server_id() const { + return server_id_; } private: @@ -74,44 +171,68 @@ class QuicStreamFactory::Job { STATE_NONE, STATE_RESOLVE_HOST, STATE_RESOLVE_HOST_COMPLETE, + STATE_LOAD_SERVER_INFO, + STATE_LOAD_SERVER_INFO_COMPLETE, STATE_CONNECT, + STATE_RESUME_CONNECT, STATE_CONNECT_COMPLETE, }; IoState io_state_; QuicStreamFactory* factory_; SingleRequestHostResolver host_resolver_; - const HostPortProxyPair host_port_proxy_pair_; - bool is_https_; - CertVerifier* cert_verifier_; + QuicServerId server_id_; + bool is_post_; + bool was_alternate_protocol_recently_broken_; + scoped_ptr<QuicServerInfo> server_info_; const BoundNetLog net_log_; QuicClientSession* session_; CompletionCallback callback_; AddressList address_list_; + base::TimeTicks disk_cache_load_start_time_; + base::WeakPtrFactory<Job> weak_factory_; DISALLOW_COPY_AND_ASSIGN(Job); }; -QuicStreamFactory::Job::Job( - QuicStreamFactory* factory, - HostResolver* host_resolver, - const HostPortProxyPair& host_port_proxy_pair, - bool is_https, - CertVerifier* cert_verifier, - const BoundNetLog& net_log) - : factory_(factory), +QuicStreamFactory::Job::Job(QuicStreamFactory* factory, + HostResolver* host_resolver, + const HostPortPair& host_port_pair, + bool is_https, + bool was_alternate_protocol_recently_broken, + PrivacyMode privacy_mode, + base::StringPiece method, + QuicServerInfo* server_info, + const BoundNetLog& net_log) + : io_state_(STATE_RESOLVE_HOST), + factory_(factory), host_resolver_(host_resolver), - host_port_proxy_pair_(host_port_proxy_pair), - is_https_(is_https), - cert_verifier_(cert_verifier), + server_id_(host_port_pair, is_https, privacy_mode), + is_post_(method == "POST"), + was_alternate_protocol_recently_broken_( + was_alternate_protocol_recently_broken), + server_info_(server_info), net_log_(net_log), - session_(NULL) { -} + session_(NULL), + weak_factory_(this) {} + +QuicStreamFactory::Job::Job(QuicStreamFactory* factory, + HostResolver* host_resolver, + QuicClientSession* session, + QuicServerId server_id) + : io_state_(STATE_RESUME_CONNECT), + factory_(factory), + host_resolver_(host_resolver), // unused + server_id_(server_id), + is_post_(false), // unused + was_alternate_protocol_recently_broken_(false), // unused + net_log_(session->net_log()), // unused + session_(session), + weak_factory_(this) {} QuicStreamFactory::Job::~Job() { } int QuicStreamFactory::Job::Run(const CompletionCallback& callback) { - io_state_ = STATE_RESOLVE_HOST; int rv = DoLoop(OK); if (rv == ERR_IO_PENDING) callback_ = callback; @@ -131,10 +252,21 @@ int QuicStreamFactory::Job::DoLoop(int rv) { case STATE_RESOLVE_HOST_COMPLETE: rv = DoResolveHostComplete(rv); break; + case STATE_LOAD_SERVER_INFO: + CHECK_EQ(OK, rv); + rv = DoLoadServerInfo(); + break; + case STATE_LOAD_SERVER_INFO_COMPLETE: + rv = DoLoadServerInfoComplete(rv); + break; case STATE_CONNECT: CHECK_EQ(OK, rv); rv = DoConnect(); break; + case STATE_RESUME_CONNECT: + CHECK_EQ(OK, rv); + rv = DoResumeConnect(); + break; case STATE_CONNECT_COMPLETE: rv = DoConnectComplete(rv); break; @@ -155,12 +287,19 @@ void QuicStreamFactory::Job::OnIOComplete(int rv) { } int QuicStreamFactory::Job::DoResolveHost() { + // Start loading the data now, and wait for it after we resolve the host. + if (server_info_) { + disk_cache_load_start_time_ = base::TimeTicks::Now(); + server_info_->Start(); + } + io_state_ = STATE_RESOLVE_HOST_COMPLETE; return host_resolver_.Resolve( - HostResolver::RequestInfo(host_port_proxy_pair_.first), + HostResolver::RequestInfo(server_id_.host_port_pair()), DEFAULT_PRIORITY, &address_list_, - base::Bind(&QuicStreamFactory::Job::OnIOComplete, base::Unretained(this)), + base::Bind(&QuicStreamFactory::Job::OnIOComplete, + weak_factory_.GetWeakPtr()), net_log_); } @@ -168,11 +307,97 @@ int QuicStreamFactory::Job::DoResolveHostComplete(int rv) { if (rv != OK) return rv; - DCHECK(!factory_->HasActiveSession(host_port_proxy_pair_)); + DCHECK(!factory_->HasActiveSession(server_id_)); + + // Inform the factory of this resolution, which will set up + // a session alias, if possible. + if (factory_->OnResolution(server_id_, address_list_)) { + return OK; + } + + io_state_ = STATE_LOAD_SERVER_INFO; + return OK; +} + +int QuicStreamFactory::Job::DoLoadServerInfo() { + io_state_ = STATE_LOAD_SERVER_INFO_COMPLETE; + + if (!server_info_) + return OK; + + return server_info_->WaitForDataReady( + base::Bind(&QuicStreamFactory::Job::OnIOComplete, + weak_factory_.GetWeakPtr())); +} + +int QuicStreamFactory::Job::DoLoadServerInfoComplete(int rv) { + if (server_info_) { + UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheReadTime", + base::TimeTicks::Now() - disk_cache_load_start_time_); + } + + if (rv != OK) { + server_info_.reset(); + } + io_state_ = STATE_CONNECT; return OK; } +int QuicStreamFactory::Job::DoConnect() { + io_state_ = STATE_CONNECT_COMPLETE; + + int rv = factory_->CreateSession(server_id_, server_info_.Pass(), + address_list_, net_log_, &session_); + if (rv != OK) { + DCHECK(rv != ERR_IO_PENDING); + DCHECK(!session_); + return rv; + } + + session_->StartReading(); + if (!session_->connection()->connected()) { + return ERR_QUIC_PROTOCOL_ERROR; + } + bool require_confirmation = + factory_->require_confirmation() || is_post_ || + was_alternate_protocol_recently_broken_; + rv = session_->CryptoConnect( + require_confirmation, + base::Bind(&QuicStreamFactory::Job::OnIOComplete, + base::Unretained(this))); + return rv; +} + +int QuicStreamFactory::Job::DoResumeConnect() { + io_state_ = STATE_CONNECT_COMPLETE; + + int rv = session_->ResumeCryptoConnect( + base::Bind(&QuicStreamFactory::Job::OnIOComplete, + base::Unretained(this))); + + return rv; +} + +int QuicStreamFactory::Job::DoConnectComplete(int rv) { + if (rv != OK) + return rv; + + DCHECK(!factory_->HasActiveSession(server_id_)); + // There may well now be an active session for this IP. If so, use the + // existing session instead. + AddressList address(session_->connection()->peer_address()); + if (factory_->OnResolution(server_id_, address)) { + session_->connection()->SendConnectionClose(QUIC_CONNECTION_IP_POOLED); + session_ = NULL; + return OK; + } + + factory_->ActivateSession(server_id_, session_); + + return OK; +} + QuicStreamRequest::QuicStreamRequest(QuicStreamFactory* factory) : factory_(factory) {} @@ -181,20 +406,20 @@ QuicStreamRequest::~QuicStreamRequest() { factory_->CancelRequest(this); } -int QuicStreamRequest::Request( - const HostPortProxyPair& host_port_proxy_pair, - bool is_https, - CertVerifier* cert_verifier, - const BoundNetLog& net_log, - const CompletionCallback& callback) { +int QuicStreamRequest::Request(const HostPortPair& host_port_pair, + bool is_https, + PrivacyMode privacy_mode, + base::StringPiece method, + const BoundNetLog& net_log, + const CompletionCallback& callback) { DCHECK(!stream_); DCHECK(callback_.is_null()); - int rv = factory_->Create(host_port_proxy_pair, is_https, cert_verifier, + DCHECK(factory_); + int rv = factory_->Create(host_port_pair, is_https, privacy_mode, method, net_log, this); if (rv == ERR_IO_PENDING) { - host_port_proxy_pair_ = host_port_proxy_pair; + host_port_pair_ = host_port_pair; is_https_ = is_https; - cert_verifier_ = cert_verifier; net_log_ = net_log; callback_ = callback; } else { @@ -220,103 +445,131 @@ scoped_ptr<QuicHttpStream> QuicStreamRequest::ReleaseStream() { return stream_.Pass(); } -int QuicStreamFactory::Job::DoConnect() { - io_state_ = STATE_CONNECT_COMPLETE; - - int rv = factory_->CreateSession(host_port_proxy_pair_, is_https_, - cert_verifier_, address_list_, net_log_, &session_); - if (rv != OK) { - DCHECK(rv != ERR_IO_PENDING); - DCHECK(!session_); - return rv; - } - - session_->StartReading(); - rv = session_->CryptoConnect( - factory_->require_confirmation() || is_https_, - base::Bind(&QuicStreamFactory::Job::OnIOComplete, - base::Unretained(this))); - return rv; -} - -int QuicStreamFactory::Job::DoConnectComplete(int rv) { - if (rv != OK) - return rv; - - DCHECK(!factory_->HasActiveSession(host_port_proxy_pair_)); - factory_->ActivateSession(host_port_proxy_pair_, session_); - - return OK; -} - QuicStreamFactory::QuicStreamFactory( HostResolver* host_resolver, ClientSocketFactory* client_socket_factory, base::WeakPtr<HttpServerProperties> http_server_properties, + CertVerifier* cert_verifier, QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory, QuicRandom* random_generator, QuicClock* clock, - size_t max_packet_length) + size_t max_packet_length, + const std::string& user_agent_id, + const QuicVersionVector& supported_versions, + bool enable_port_selection, + bool enable_pacing, + bool enable_time_based_loss_detection) : require_confirmation_(true), host_resolver_(host_resolver), client_socket_factory_(client_socket_factory), http_server_properties_(http_server_properties), + cert_verifier_(cert_verifier), + quic_server_info_factory_(NULL), quic_crypto_client_stream_factory_(quic_crypto_client_stream_factory), random_generator_(random_generator), clock_(clock), max_packet_length_(max_packet_length), - weak_factory_(this), - port_seed_(random_generator_->RandUint64()) { - config_.SetDefaults(); - config_.set_idle_connection_state_lifetime( - QuicTime::Delta::FromSeconds(30), - QuicTime::Delta::FromSeconds(30)); - - cannoncial_suffixes_.push_back(string(".c.youtube.com")); - cannoncial_suffixes_.push_back(string(".googlevideo.com")); + config_(InitializeQuicConfig(enable_pacing, + enable_time_based_loss_detection)), + supported_versions_(supported_versions), + enable_port_selection_(enable_port_selection), + port_seed_(random_generator_->RandUint64()), + weak_factory_(this) { + crypto_config_.SetDefaults(); + crypto_config_.set_user_agent_id(user_agent_id); + crypto_config_.AddCanonicalSuffix(".c.youtube.com"); + crypto_config_.AddCanonicalSuffix(".googlevideo.com"); + crypto_config_.SetProofVerifier(new ProofVerifierChromium(cert_verifier)); + base::CPU cpu; + if (cpu.has_aesni() && cpu.has_avx()) + crypto_config_.PreferAesGcm(); + if (!IsEcdsaSupported()) + crypto_config_.DisableEcdsa(); } QuicStreamFactory::~QuicStreamFactory() { CloseAllSessions(ERR_ABORTED); - STLDeleteElements(&all_sessions_); + while (!all_sessions_.empty()) { + delete all_sessions_.begin()->first; + all_sessions_.erase(all_sessions_.begin()); + } STLDeleteValues(&active_jobs_); - STLDeleteValues(&all_crypto_configs_); } -int QuicStreamFactory::Create(const HostPortProxyPair& host_port_proxy_pair, +int QuicStreamFactory::Create(const HostPortPair& host_port_pair, bool is_https, - CertVerifier* cert_verifier, + PrivacyMode privacy_mode, + base::StringPiece method, const BoundNetLog& net_log, QuicStreamRequest* request) { - if (HasActiveSession(host_port_proxy_pair)) { - request->set_stream(CreateIfSessionExists(host_port_proxy_pair, net_log)); + QuicServerId server_id(host_port_pair, is_https, privacy_mode); + if (HasActiveSession(server_id)) { + request->set_stream(CreateIfSessionExists(server_id, net_log)); return OK; } - if (HasActiveJob(host_port_proxy_pair)) { - Job* job = active_jobs_[host_port_proxy_pair]; + if (HasActiveJob(server_id)) { + Job* job = active_jobs_[server_id]; active_requests_[request] = job; job_requests_map_[job].insert(request); return ERR_IO_PENDING; } - scoped_ptr<Job> job(new Job(this, host_resolver_, host_port_proxy_pair, - is_https, cert_verifier, net_log)); + QuicServerInfo* quic_server_info = NULL; + if (quic_server_info_factory_) { + QuicCryptoClientConfig::CachedState* cached = + crypto_config_.LookupOrCreate(server_id); + DCHECK(cached); + if (cached->IsEmpty()) { + quic_server_info = quic_server_info_factory_->GetForServer(server_id); + } + } + bool was_alternate_protocol_recently_broken = + http_server_properties_ && + http_server_properties_->WasAlternateProtocolRecentlyBroken( + server_id.host_port_pair()); + scoped_ptr<Job> job(new Job(this, host_resolver_, host_port_pair, is_https, + was_alternate_protocol_recently_broken, + privacy_mode, method, quic_server_info, net_log)); int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete, base::Unretained(this), job.get())); if (rv == ERR_IO_PENDING) { active_requests_[request] = job.get(); job_requests_map_[job.get()].insert(request); - active_jobs_[host_port_proxy_pair] = job.release(); + active_jobs_[server_id] = job.release(); } if (rv == OK) { - DCHECK(HasActiveSession(host_port_proxy_pair)); - request->set_stream(CreateIfSessionExists(host_port_proxy_pair, net_log)); + DCHECK(HasActiveSession(server_id)); + request->set_stream(CreateIfSessionExists(server_id, net_log)); } return rv; } +bool QuicStreamFactory::OnResolution( + const QuicServerId& server_id, + const AddressList& address_list) { + DCHECK(!HasActiveSession(server_id)); + for (size_t i = 0; i < address_list.size(); ++i) { + const IPEndPoint& address = address_list[i]; + const IpAliasKey ip_alias_key(address, server_id.is_https()); + if (!ContainsKey(ip_aliases_, ip_alias_key)) + continue; + + const SessionSet& sessions = ip_aliases_[ip_alias_key]; + for (SessionSet::const_iterator i = sessions.begin(); + i != sessions.end(); ++i) { + QuicClientSession* session = *i; + if (!session->CanPool(server_id.host())) + continue; + active_sessions_[server_id] = session; + session_aliases_[session].insert(server_id); + return true; + } + } + return false; +} + void QuicStreamFactory::OnJobComplete(Job* job, int rv) { if (rv == OK) { require_confirmation_ = false; @@ -324,8 +577,8 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) { // Create all the streams, but do not notify them yet. for (RequestSet::iterator it = job_requests_map_[job].begin(); it != job_requests_map_[job].end() ; ++it) { - DCHECK(HasActiveSession(job->host_port_proxy_pair())); - (*it)->set_stream(CreateIfSessionExists(job->host_port_proxy_pair(), + DCHECK(HasActiveSession(job->server_id())); + (*it)->set_stream(CreateIfSessionExists(job->server_id(), (*it)->net_log())); } } @@ -339,7 +592,7 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) { // profile which can not be deleted via callbacks. request->OnRequestComplete(rv); } - active_jobs_.erase(job->host_port_proxy_pair()); + active_jobs_.erase(job->server_id()); job_requests_map_.erase(job); delete job; return; @@ -348,14 +601,14 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) { // Returns a newly created QuicHttpStream owned by the caller, if a // matching session already exists. Returns NULL otherwise. scoped_ptr<QuicHttpStream> QuicStreamFactory::CreateIfSessionExists( - const HostPortProxyPair& host_port_proxy_pair, + const QuicServerId& server_id, const BoundNetLog& net_log) { - if (!HasActiveSession(host_port_proxy_pair)) { + if (!HasActiveSession(server_id)) { DVLOG(1) << "No active session"; return scoped_ptr<QuicHttpStream>(); } - QuicClientSession* session = active_sessions_[host_port_proxy_pair]; + QuicClientSession* session = active_sessions_[server_id]; DCHECK(session); return scoped_ptr<QuicHttpStream>( new QuicHttpStream(session->GetWeakPtr())); @@ -370,13 +623,22 @@ void QuicStreamFactory::OnSessionGoingAway(QuicClientSession* session) { ++it) { DCHECK(active_sessions_.count(*it)); DCHECK_EQ(session, active_sessions_[*it]); + // Track sessions which have recently gone away so that we can disable + // port suggestions. + if (session->goaway_received()) { + gone_away_aliases_.insert(*it); + } + active_sessions_.erase(*it); - if (!session->IsCryptoHandshakeConfirmed() && http_server_properties_) { - // TODO(rch): In the special case where the session has received no - // packets from the peer, we should consider blacklisting this - // differently so that we still race TCP but we don't consider the - // session connected until the handshake has been confirmed. - http_server_properties_->SetBrokenAlternateProtocol(it->first); + ProcessGoingAwaySession(session, *it, true); + } + ProcessGoingAwaySession(session, all_sessions_[session], false); + if (!aliases.empty()) { + const IpAliasKey ip_alias_key(session->connection()->peer_address(), + aliases.begin()->is_https()); + ip_aliases_[ip_alias_key].erase(session); + if (ip_aliases_[ip_alias_key].empty()) { + ip_aliases_.erase(ip_alias_key); } } session_aliases_.erase(session); @@ -385,8 +647,37 @@ void QuicStreamFactory::OnSessionGoingAway(QuicClientSession* session) { void QuicStreamFactory::OnSessionClosed(QuicClientSession* session) { DCHECK_EQ(0u, session->GetNumOpenStreams()); OnSessionGoingAway(session); - all_sessions_.erase(session); delete session; + all_sessions_.erase(session); +} + +void QuicStreamFactory::OnSessionConnectTimeout( + QuicClientSession* session) { + const AliasSet& aliases = session_aliases_[session]; + for (AliasSet::const_iterator it = aliases.begin(); it != aliases.end(); + ++it) { + DCHECK(active_sessions_.count(*it)); + DCHECK_EQ(session, active_sessions_[*it]); + active_sessions_.erase(*it); + } + + if (aliases.empty()) { + return; + } + + const IpAliasKey ip_alias_key(session->connection()->peer_address(), + aliases.begin()->is_https()); + ip_aliases_[ip_alias_key].erase(session); + if (ip_aliases_[ip_alias_key].empty()) { + ip_aliases_.erase(ip_alias_key); + } + QuicServerId server_id = *aliases.begin(); + session_aliases_.erase(session); + Job* job = new Job(this, host_resolver_, session, server_id); + active_jobs_[server_id] = job; + int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete, + base::Unretained(this), job)); + DCHECK_EQ(ERR_IO_PENDING, rv); } void QuicStreamFactory::CancelRequest(QuicStreamRequest* request) { @@ -404,7 +695,7 @@ void QuicStreamFactory::CloseAllSessions(int error) { } while (!all_sessions_.empty()) { size_t initial_size = all_sessions_.size(); - (*all_sessions_.begin())->CloseSessionOnError(error); + all_sessions_.begin()->first->CloseSessionOnError(error); DCHECK_NE(initial_size, all_sessions_.size()); } DCHECK(all_sessions_.empty()); @@ -415,14 +706,26 @@ base::Value* QuicStreamFactory::QuicStreamFactoryInfoToValue() const { for (SessionMap::const_iterator it = active_sessions_.begin(); it != active_sessions_.end(); ++it) { - const HostPortProxyPair& pair = it->first; - const QuicClientSession* session = it->second; - - list->Append(session->GetInfoAsValue(pair.first)); + const QuicServerId& server_id = it->first; + QuicClientSession* session = it->second; + const AliasSet& aliases = session_aliases_.find(session)->second; + // Only add a session to the list once. + if (server_id == *aliases.begin()) { + std::set<HostPortPair> hosts; + for (AliasSet::const_iterator alias_it = aliases.begin(); + alias_it != aliases.end(); ++alias_it) { + hosts.insert(alias_it->host_port_pair()); + } + list->Append(session->GetInfoAsValue(hosts)); + } } return list; } +void QuicStreamFactory::ClearCachedStatesInCryptoConfig() { + crypto_config_.ClearCachedStates(); +} + void QuicStreamFactory::OnIPAddressChanged() { CloseAllSessions(ERR_NETWORK_CHANGED); require_confirmation_ = true; @@ -446,54 +749,69 @@ void QuicStreamFactory::OnCACertChanged(const X509Certificate* cert) { } bool QuicStreamFactory::HasActiveSession( - const HostPortProxyPair& host_port_proxy_pair) { - return ContainsKey(active_sessions_, host_port_proxy_pair); + const QuicServerId& server_id) const { + return ContainsKey(active_sessions_, server_id); } int QuicStreamFactory::CreateSession( - const HostPortProxyPair& host_port_proxy_pair, - bool is_https, - CertVerifier* cert_verifier, + const QuicServerId& server_id, + scoped_ptr<QuicServerInfo> server_info, const AddressList& address_list, const BoundNetLog& net_log, QuicClientSession** session) { - QuicGuid guid = random_generator_->RandUint64(); + bool enable_port_selection = enable_port_selection_; + if (enable_port_selection && + ContainsKey(gone_away_aliases_, server_id)) { + // Disable port selection when the server is going away. + // There is no point in trying to return to the same server, if + // that server is no longer handling requests. + enable_port_selection = false; + gone_away_aliases_.erase(server_id); + } + + QuicConnectionId connection_id = random_generator_->RandUint64(); IPEndPoint addr = *address_list.begin(); scoped_refptr<PortSuggester> port_suggester = - new PortSuggester(host_port_proxy_pair.first, port_seed_); - DatagramSocket::BindType bind_type = DatagramSocket::RANDOM_BIND; -#if defined(OS_WIN) - // TODO(jar)bug=329255 Provide better implementation to avoid pop-up warning. - bind_type = DatagramSocket::DEFAULT_BIND; -#endif + new PortSuggester(server_id.host_port_pair(), port_seed_); + DatagramSocket::BindType bind_type = enable_port_selection ? + DatagramSocket::RANDOM_BIND : // Use our callback. + DatagramSocket::DEFAULT_BIND; // Use OS to randomize. scoped_ptr<DatagramClientSocket> socket( client_socket_factory_->CreateDatagramClientSocket( bind_type, base::Bind(&PortSuggester::SuggestPort, port_suggester), net_log.net_log(), net_log.source())); int rv = socket->Connect(addr); - if (rv != OK) + if (rv != OK) { + HistogramCreateSessionFailure(CREATION_ERROR_CONNECTING_SOCKET); return rv; + } UMA_HISTOGRAM_COUNTS("Net.QuicEphemeralPortsSuggested", port_suggester->call_count()); - -#if defined(OS_WIN) - // TODO(jar)bug=329255 Provide better implementation to avoid pop-up warning. - DCHECK_EQ(0u, port_suggester->call_count()); -#else - DCHECK_LE(1u, port_suggester->call_count()); -#endif + if (enable_port_selection) { + DCHECK_LE(1u, port_suggester->call_count()); + } else { + DCHECK_EQ(0u, port_suggester->call_count()); + } // We should adaptively set this buffer size, but for now, we'll use a size // that is more than large enough for a full receive window, and yet // does not consume "too much" memory. If we see bursty packet loss, we may // revisit this setting and test for its impact. const int32 kSocketBufferSize(TcpReceiver::kReceiveWindowTCP); - socket->SetReceiveBufferSize(kSocketBufferSize); + rv = socket->SetReceiveBufferSize(kSocketBufferSize); + if (rv != OK) { + HistogramCreateSessionFailure(CREATION_ERROR_SETTING_RECEIVE_BUFFER); + return rv; + } // Set a buffer large enough to contain the initial CWND's worth of packet // to work around the problem with CHLO packets being sent out with the // wrong encryption level, when the send buffer is full. - socket->SetSendBufferSize(kMaxPacketSize * 20); // Support 20 packets. + rv = socket->SetSendBufferSize(kMaxPacketSize * 20); + if (rv != OK) { + HistogramCreateSessionFailure(CREATION_ERROR_SETTING_SEND_BUFFER); + return rv; + } scoped_ptr<QuicDefaultPacketWriter> writer( new QuicDefaultPacketWriter(socket.get())); @@ -504,96 +822,134 @@ int QuicStreamFactory::CreateSession( clock_.get(), random_generator_)); } - QuicConnection* connection = new QuicConnection(guid, addr, helper_.get(), - writer.get(), false, - QuicSupportedVersions()); + QuicConnection* connection = + new QuicConnection(connection_id, addr, helper_.get(), writer.get(), + false, supported_versions_); writer->SetConnection(connection); - connection->options()->max_packet_length = max_packet_length_; - - QuicCryptoClientConfig* crypto_config = - GetOrCreateCryptoConfig(host_port_proxy_pair); - DCHECK(crypto_config); + connection->set_max_packet_length(max_packet_length_); + + InitializeCachedStateInCryptoConfig(server_id, server_info); + + QuicConfig config = config_; + config.SetInitialCongestionWindowToSend( + server_id.is_https() ? kServerSecureInitialCongestionWindow + : kServerInecureInitialCongestionWindow); + config.SetInitialFlowControlWindowToSend(kInitialReceiveWindowSize); + config.SetInitialStreamFlowControlWindowToSend(kInitialReceiveWindowSize); + config.SetInitialSessionFlowControlWindowToSend(kInitialReceiveWindowSize); + if (http_server_properties_) { + const HttpServerProperties::NetworkStats* stats = + http_server_properties_->GetServerNetworkStats( + server_id.host_port_pair()); + if (stats != NULL) { + config.SetInitialRoundTripTimeUsToSend(stats->srtt.InMicroseconds()); + } + } *session = new QuicClientSession( connection, socket.Pass(), writer.Pass(), this, - quic_crypto_client_stream_factory_, host_port_proxy_pair.first.host(), - config_, crypto_config, net_log.net_log()); - all_sessions_.insert(*session); // owning pointer - if (is_https) { - crypto_config->SetProofVerifier( - new ProofVerifierChromium(cert_verifier, net_log)); - } + quic_crypto_client_stream_factory_, server_info.Pass(), server_id, + config, &crypto_config_, + base::MessageLoop::current()->message_loop_proxy().get(), + net_log.net_log()); + all_sessions_[*session] = server_id; // owning pointer return OK; } -bool QuicStreamFactory::HasActiveJob( - const HostPortProxyPair& host_port_proxy_pair) { - return ContainsKey(active_jobs_, host_port_proxy_pair); +bool QuicStreamFactory::HasActiveJob(const QuicServerId& key) const { + return ContainsKey(active_jobs_, key); } void QuicStreamFactory::ActivateSession( - const HostPortProxyPair& host_port_proxy_pair, + const QuicServerId& server_id, QuicClientSession* session) { - DCHECK(!HasActiveSession(host_port_proxy_pair)); - active_sessions_[host_port_proxy_pair] = session; - session_aliases_[session].insert(host_port_proxy_pair); -} + DCHECK(!HasActiveSession(server_id)); + UMA_HISTOGRAM_COUNTS("Net.QuicActiveSessions", active_sessions_.size()); + active_sessions_[server_id] = session; + session_aliases_[session].insert(server_id); + const IpAliasKey ip_alias_key(session->connection()->peer_address(), + server_id.is_https()); + DCHECK(!ContainsKey(ip_aliases_[ip_alias_key], session)); + ip_aliases_[ip_alias_key].insert(session); +} + +void QuicStreamFactory::InitializeCachedStateInCryptoConfig( + const QuicServerId& server_id, + const scoped_ptr<QuicServerInfo>& server_info) { + if (!server_info) + return; -QuicCryptoClientConfig* QuicStreamFactory::GetOrCreateCryptoConfig( - const HostPortProxyPair& host_port_proxy_pair) { - QuicCryptoClientConfig* crypto_config; + QuicCryptoClientConfig::CachedState* cached = + crypto_config_.LookupOrCreate(server_id); + if (!cached->IsEmpty()) + return; - if (ContainsKey(all_crypto_configs_, host_port_proxy_pair)) { - crypto_config = all_crypto_configs_[host_port_proxy_pair]; - DCHECK(crypto_config); - } else { - // TODO(rtenneti): if two quic_sessions for the same host_port_proxy_pair - // share the same crypto_config, will it cause issues? - crypto_config = new QuicCryptoClientConfig(); - crypto_config->SetDefaults(); - all_crypto_configs_[host_port_proxy_pair] = crypto_config; - PopulateFromCanonicalConfig(host_port_proxy_pair, crypto_config); + if (!cached->Initialize(server_info->state().server_config, + server_info->state().source_address_token, + server_info->state().certs, + server_info->state().server_config_sig, + clock_->WallNow())) + return; + + if (!server_id.is_https()) { + // Don't check the certificates for insecure QUIC. + cached->SetProofValid(); } - return crypto_config; } -void QuicStreamFactory::PopulateFromCanonicalConfig( - const HostPortProxyPair& host_port_proxy_pair, - QuicCryptoClientConfig* crypto_config) { - const string server_hostname = host_port_proxy_pair.first.host(); - - unsigned i = 0; - for (; i < cannoncial_suffixes_.size(); ++i) { - if (EndsWith(server_hostname, cannoncial_suffixes_[i], false)) { - break; - } - } - if (i == cannoncial_suffixes_.size()) +void QuicStreamFactory::ProcessGoingAwaySession( + QuicClientSession* session, + const QuicServerId& server_id, + bool session_was_active) { + if (!http_server_properties_) return; - HostPortPair canonical_host_port(cannoncial_suffixes_[i], - host_port_proxy_pair.first.port()); - if (!ContainsKey(canonical_hostname_to_origin_map_, canonical_host_port)) { - // This is the first host we've seen which matches the suffix, so make it - // canonical. - canonical_hostname_to_origin_map_[canonical_host_port] = - host_port_proxy_pair; + const QuicConnectionStats& stats = session->connection()->GetStats(); + if (session->IsCryptoHandshakeConfirmed()) { + HttpServerProperties::NetworkStats network_stats; + network_stats.srtt = base::TimeDelta::FromMicroseconds(stats.srtt_us); + network_stats.bandwidth_estimate = stats.estimated_bandwidth; + http_server_properties_->SetServerNetworkStats(server_id.host_port_pair(), + network_stats); return; } - const HostPortProxyPair& canonical_host_port_proxy_pair = - canonical_hostname_to_origin_map_[canonical_host_port]; - QuicCryptoClientConfig* canonical_crypto_config = - all_crypto_configs_[canonical_host_port_proxy_pair]; - DCHECK(canonical_crypto_config); + UMA_HISTOGRAM_COUNTS("Net.QuicHandshakeNotConfirmedNumPacketsReceived", + stats.packets_received); + + if (!session_was_active) + return; + + const HostPortPair& server = server_id.host_port_pair(); + // Don't try to change the alternate-protocol state, if the + // alternate-protocol state is unknown. + if (!http_server_properties_->HasAlternateProtocol(server)) + return; - // Copy the CachedState for the canonical server from canonical_crypto_config - // as the initial CachedState for the server_hostname in crypto_config. - crypto_config->InitializeFrom(server_hostname, - canonical_host_port_proxy_pair.first.host(), - canonical_crypto_config); - // Update canonical version to point at the "most recent" crypto_config. - canonical_hostname_to_origin_map_[canonical_host_port] = host_port_proxy_pair; + // TODO(rch): In the special case where the session has received no + // packets from the peer, we should consider blacklisting this + // differently so that we still race TCP but we don't consider the + // session connected until the handshake has been confirmed. + HistogramBrokenAlternateProtocolLocation( + BROKEN_ALTERNATE_PROTOCOL_LOCATION_QUIC_STREAM_FACTORY); + PortAlternateProtocolPair alternate = + http_server_properties_->GetAlternateProtocol(server); + DCHECK_EQ(QUIC, alternate.protocol); + + // Since the session was active, there's no longer an + // HttpStreamFactoryImpl::Job running which can mark it broken, unless the + // TCP job also fails. So to avoid not using QUIC when we otherwise could, + // we mark it as broken, and then immediately re-enable it. This leaves + // QUIC as "recently broken" which means that 0-RTT will be disabled but + // we'll still race. + http_server_properties_->SetBrokenAlternateProtocol(server); + http_server_properties_->ClearAlternateProtocol(server); + http_server_properties_->SetAlternateProtocol( + server, alternate.port, alternate.protocol); + DCHECK_EQ(QUIC, + http_server_properties_->GetAlternateProtocol(server).protocol); + DCHECK(http_server_properties_->WasAlternateProtocolRecentlyBroken( + server)); } } // namespace net diff --git a/chromium/net/quic/quic_stream_factory.h b/chromium/net/quic/quic_stream_factory.h index e55c83c3cbe..eb82659bbf8 100644 --- a/chromium/net/quic/quic_stream_factory.h +++ b/chromium/net/quic/quic_stream_factory.h @@ -5,10 +5,12 @@ #ifndef NET_QUIC_QUIC_STREAM_FACTORY_H_ #define NET_QUIC_QUIC_STREAM_FACTORY_H_ +#include <list> #include <map> #include <string> #include <vector> +#include "base/logging.h" #include "base/memory/weak_ptr.h" #include "net/base/address_list.h" #include "net/base/completion_callback.h" @@ -33,6 +35,8 @@ class QuicClientSession; class QuicConnectionHelper; class QuicCryptoClientStreamFactory; class QuicRandom; +class QuicServerInfoFactory; +class QuicServerId; class QuicStreamFactory; namespace test { @@ -48,9 +52,10 @@ class NET_EXPORT_PRIVATE QuicStreamRequest { ~QuicStreamRequest(); // For http, |is_https| is false and |cert_verifier| can be null. - int Request(const HostPortProxyPair& host_port_proxy_pair, + int Request(const HostPortPair& host_port_pair, bool is_https, - CertVerifier* cert_verifier, + PrivacyMode privacy_mode, + base::StringPiece method, const BoundNetLog& net_log, const CompletionCallback& callback); @@ -66,9 +71,8 @@ class NET_EXPORT_PRIVATE QuicStreamRequest { private: QuicStreamFactory* factory_; - HostPortProxyPair host_port_proxy_pair_; + HostPortPair host_port_pair_; bool is_https_; - CertVerifier* cert_verifier_; BoundNetLog net_log_; CompletionCallback callback_; scoped_ptr<QuicHttpStream> stream_; @@ -86,31 +90,32 @@ class NET_EXPORT_PRIVATE QuicStreamFactory HostResolver* host_resolver, ClientSocketFactory* client_socket_factory, base::WeakPtr<HttpServerProperties> http_server_properties, + CertVerifier* cert_verifier, QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory, QuicRandom* random_generator, QuicClock* clock, - size_t max_packet_length); + size_t max_packet_length, + const std::string& user_agent_id, + const QuicVersionVector& supported_versions, + bool enable_port_selection, + bool enable_pacing, + bool enable_time_based_loss_detection); virtual ~QuicStreamFactory(); - // Creates a new QuicHttpStream to |host_port_proxy_pair| which will be + // Creates a new QuicHttpStream to |host_port_pair| which will be // owned by |request|. |is_https| specifies if the protocol is https or not. // |cert_verifier| is used by ProofVerifier for verifying the certificate // chain and signature. For http, this can be null. If a matching session // already exists, this method will return OK. If no matching session exists, // this will return ERR_IO_PENDING and will invoke OnRequestComplete // asynchronously. - int Create(const HostPortProxyPair& host_port_proxy_pair, + int Create(const HostPortPair& host_port_pair, bool is_https, - CertVerifier* cert_verifier, + PrivacyMode privacy_mode, + base::StringPiece method, const BoundNetLog& net_log, QuicStreamRequest* request); - // Returns a newly created QuicHttpStream owned by the caller, if a - // matching session already exists. Returns NULL otherwise. - scoped_ptr<QuicHttpStream> CreateIfSessionExists( - const HostPortProxyPair& host_port_proxy_pair, - const BoundNetLog& net_log); - // Called by a session when it becomes idle. void OnIdleSession(QuicClientSession* session); @@ -121,6 +126,9 @@ class NET_EXPORT_PRIVATE QuicStreamFactory // Called by a session after it shuts down. void OnSessionClosed(QuicClientSession* session); + // Called by a session whose connection has timed out. + void OnSessionConnectTimeout(QuicClientSession* session); + // Cancels a pending request. void CancelRequest(QuicStreamRequest* request); @@ -129,6 +137,9 @@ class NET_EXPORT_PRIVATE QuicStreamFactory base::Value* QuicStreamFactoryInfoToValue() const; + // Delete all cached state objects in |crypto_config_|. + void ClearCachedStatesInCryptoConfig(); + // NetworkChangeNotifier::IPAddressObserver methods: // Until the servers support roaming, close all connections when the local @@ -149,48 +160,83 @@ class NET_EXPORT_PRIVATE QuicStreamFactory QuicConnectionHelper* helper() { return helper_.get(); } + bool enable_port_selection() const { return enable_port_selection_; } + + bool has_quic_server_info_factory() { + return quic_server_info_factory_ != NULL; + } + + void set_quic_server_info_factory( + QuicServerInfoFactory* quic_server_info_factory) { + DCHECK(!quic_server_info_factory_); + quic_server_info_factory_ = quic_server_info_factory; + } + private: class Job; friend class test::QuicStreamFactoryPeer; - typedef std::map<HostPortProxyPair, QuicClientSession*> SessionMap; - typedef std::set<HostPortProxyPair> AliasSet; + // The key used to find session by ip. Includes + // the ip address, port, and scheme. + struct NET_EXPORT_PRIVATE IpAliasKey { + IpAliasKey(); + IpAliasKey(IPEndPoint ip_endpoint, bool is_https); + ~IpAliasKey(); + + IPEndPoint ip_endpoint; + bool is_https; + + // Needed to be an element of std::set. + bool operator<(const IpAliasKey &other) const; + bool operator==(const IpAliasKey &other) const; + }; + + typedef std::map<QuicServerId, QuicClientSession*> SessionMap; + typedef std::map<QuicClientSession*, QuicServerId> SessionIdMap; + typedef std::set<QuicServerId> AliasSet; typedef std::map<QuicClientSession*, AliasSet> SessionAliasMap; typedef std::set<QuicClientSession*> SessionSet; - typedef std::map<HostPortProxyPair, QuicCryptoClientConfig*> CryptoConfigMap; - typedef std::map<HostPortPair, HostPortProxyPair> CanonicalHostMap; - typedef std::map<HostPortProxyPair, Job*> JobMap; + typedef std::map<IpAliasKey, SessionSet> IPAliasMap; + typedef std::map<QuicServerId, QuicCryptoClientConfig*> CryptoConfigMap; + typedef std::map<QuicServerId, Job*> JobMap; typedef std::map<QuicStreamRequest*, Job*> RequestMap; typedef std::set<QuicStreamRequest*> RequestSet; typedef std::map<Job*, RequestSet> JobRequestsMap; + // Returns a newly created QuicHttpStream owned by the caller, if a + // matching session already exists. Returns NULL otherwise. + scoped_ptr<QuicHttpStream> CreateIfSessionExists(const QuicServerId& key, + const BoundNetLog& net_log); + + bool OnResolution(const QuicServerId& server_id, + const AddressList& address_list); void OnJobComplete(Job* job, int rv); - bool HasActiveSession(const HostPortProxyPair& host_port_proxy_pair); - bool HasActiveJob(const HostPortProxyPair& host_port_proxy_pair); - int CreateSession(const HostPortProxyPair& host_port_proxy_pair, - bool is_https, - CertVerifier* cert_verifier, + bool HasActiveSession(const QuicServerId& server_id) const; + bool HasActiveJob(const QuicServerId& server_id) const; + int CreateSession(const QuicServerId& server_id, + scoped_ptr<QuicServerInfo> quic_server_info, const AddressList& address_list, const BoundNetLog& net_log, QuicClientSession** session); - void ActivateSession(const HostPortProxyPair& host_port_proxy_pair, + void ActivateSession(const QuicServerId& key, QuicClientSession* session); - QuicCryptoClientConfig* GetOrCreateCryptoConfig( - const HostPortProxyPair& host_port_proxy_pair); + // Initializes the cached state associated with |server_id| in + // |crypto_config_| with the information in |server_info|. + void InitializeCachedStateInCryptoConfig( + const QuicServerId& server_id, + const scoped_ptr<QuicServerInfo>& server_info); - // If |host_port_proxy_pair| suffix contains ".c.youtube.com" (in future we - // could support other suffixes), then populate |crypto_config| with a - // canonical server config data from |canonical_hostname_to_origin_map_| for - // that suffix. - void PopulateFromCanonicalConfig( - const HostPortProxyPair& host_port_proxy_pair, - QuicCryptoClientConfig* crypto_config); + void ProcessGoingAwaySession(QuicClientSession* session, + const QuicServerId& server_id, + bool was_session_active); bool require_confirmation_; HostResolver* host_resolver_; ClientSocketFactory* client_socket_factory_; base::WeakPtr<HttpServerProperties> http_server_properties_; + CertVerifier* cert_verifier_; + QuicServerInfoFactory* quic_server_info_factory_; QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory_; QuicRandom* random_generator_; scoped_ptr<QuicClock> clock_; @@ -200,35 +246,31 @@ class NET_EXPORT_PRIVATE QuicStreamFactory scoped_ptr<QuicConnectionHelper> helper_; // Contains owning pointers to all sessions that currently exist. - SessionSet all_sessions_; + SessionIdMap all_sessions_; // Contains non-owning pointers to currently active session // (not going away session, once they're implemented). SessionMap active_sessions_; + // Map from session to set of aliases that this session is known by. SessionAliasMap session_aliases_; + // Map from IP address to sessions which are connected to this address. + IPAliasMap ip_aliases_; - // Contains owning pointers to QuicCryptoClientConfig. QuicCryptoClientConfig - // contains configuration and cached state about servers. - // TODO(rtenneti): Persist all_crypto_configs_ to disk and decide when to - // clear the data in the map. - CryptoConfigMap all_crypto_configs_; + // Origins which have gone away recently. + AliasSet gone_away_aliases_; - // Contains a map of servers which could share the same server config. Map - // from a Canonical host/port (host is some postfix of host names) to an - // actual origin, which has a plausible set of initial certificates (or at - // least server public key). - CanonicalHostMap canonical_hostname_to_origin_map_; - - // Contains list of suffixes (for exmaple ".c.youtube.com", - // ".googlevideo.com") of cannoncial hostnames. - std::vector<std::string> cannoncial_suffixes_; - - QuicConfig config_; + const QuicConfig config_; + QuicCryptoClientConfig crypto_config_; JobMap active_jobs_; JobRequestsMap job_requests_map_; RequestMap active_requests_; - base::WeakPtrFactory<QuicStreamFactory> weak_factory_; + QuicVersionVector supported_versions_; + + // Determine if we should consistently select a client UDP port. If false, + // then we will just let the OS select a random client port for each new + // connection. + bool enable_port_selection_; // Each profile will (probably) have a unique port_seed_ value. This value is // used to help seed a pseudo-random number generator (PortSuggester) so that @@ -238,6 +280,8 @@ class NET_EXPORT_PRIVATE QuicStreamFactory // port requests. uint64 port_seed_; + base::WeakPtrFactory<QuicStreamFactory> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(QuicStreamFactory); }; diff --git a/chromium/net/quic/quic_stream_factory_test.cc b/chromium/net/quic/quic_stream_factory_test.cc index 03cc6f7e8bc..e88ea4b8052 100644 --- a/chromium/net/quic/quic_stream_factory_test.cc +++ b/chromium/net/quic/quic_stream_factory_test.cc @@ -6,20 +6,25 @@ #include "base/run_loop.h" #include "base/strings/string_util.h" +#include "net/base/test_data_directory.h" #include "net/cert/cert_verifier.h" #include "net/dns/mock_host_resolver.h" #include "net/http/http_response_headers.h" #include "net/http/http_response_info.h" #include "net/http/http_util.h" #include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/crypto/proof_verifier_chromium.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/quic_http_stream.h" +#include "net/quic/quic_server_id.h" #include "net/quic/test_tools/mock_clock.h" #include "net/quic/test_tools/mock_crypto_client_stream_factory.h" #include "net/quic/test_tools/mock_random.h" +#include "net/quic/test_tools/quic_test_packet_maker.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/socket/socket_test_util.h" +#include "net/test/cert_test_util.h" #include "testing/gtest/include/gtest/gtest.h" using base::StringPiece; @@ -29,152 +34,169 @@ using std::vector; namespace net { namespace test { +namespace { +const char kDefaultServerHostName[] = "www.google.com"; +const int kDefaultServerPort = 443; +} // namespace anonymous + class QuicStreamFactoryPeer { public: - static QuicCryptoClientConfig* GetOrCreateCryptoConfig( - QuicStreamFactory* factory, - const HostPortProxyPair& host_port_proxy_pair) { - return factory->GetOrCreateCryptoConfig(host_port_proxy_pair); + static QuicCryptoClientConfig* GetCryptoConfig(QuicStreamFactory* factory) { + return &factory->crypto_config_; } static bool HasActiveSession(QuicStreamFactory* factory, - const HostPortProxyPair& host_port_proxy_pair) { - return factory->HasActiveSession(host_port_proxy_pair); + const HostPortPair& host_port_pair, + bool is_https) { + QuicServerId server_id(host_port_pair, is_https, PRIVACY_MODE_DISABLED); + return factory->HasActiveSession(server_id); } static QuicClientSession* GetActiveSession( QuicStreamFactory* factory, - const HostPortProxyPair& host_port_proxy_pair) { - DCHECK(factory->HasActiveSession(host_port_proxy_pair)); - return factory->active_sessions_[host_port_proxy_pair]; + const HostPortPair& host_port_pair, + bool is_https) { + QuicServerId server_id(host_port_pair, is_https, PRIVACY_MODE_DISABLED); + DCHECK(factory->HasActiveSession(server_id)); + return factory->active_sessions_[server_id]; + } + + static scoped_ptr<QuicHttpStream> CreateIfSessionExists( + QuicStreamFactory* factory, + const HostPortPair& host_port_pair, + bool is_https, + const BoundNetLog& net_log) { + QuicServerId server_id(host_port_pair, is_https, PRIVACY_MODE_DISABLED); + return factory->CreateIfSessionExists(server_id, net_log); } static bool IsLiveSession(QuicStreamFactory* factory, QuicClientSession* session) { - for (QuicStreamFactory::SessionSet::iterator it = + for (QuicStreamFactory::SessionIdMap::iterator it = factory->all_sessions_.begin(); it != factory->all_sessions_.end(); ++it) { - if (*it == session) + if (it->first == session) return true; } return false; } }; -class QuicStreamFactoryTest : public ::testing::Test { +class QuicStreamFactoryTest : public ::testing::TestWithParam<QuicVersion> { protected: QuicStreamFactoryTest() : random_generator_(0), + maker_(GetParam(), 0), clock_(new MockClock()), + cert_verifier_(CertVerifier::CreateDefault()), factory_(&host_resolver_, &socket_factory_, - base::WeakPtr<HttpServerProperties>(), - &crypto_client_stream_factory_, - &random_generator_, clock_, kDefaultMaxPacketSize), - host_port_proxy_pair_(HostPortPair("www.google.com", 443), - ProxyServer::Direct()), + base::WeakPtr<HttpServerProperties>(), cert_verifier_.get(), + &crypto_client_stream_factory_, &random_generator_, clock_, + kDefaultMaxPacketSize, std::string(), + SupportedVersions(GetParam()), true, true, true), + host_port_pair_(kDefaultServerHostName, kDefaultServerPort), is_https_(false), - cert_verifier_(CertVerifier::CreateDefault()) { + privacy_mode_(PRIVACY_MODE_DISABLED) { factory_.set_require_confirmation(false); } - scoped_ptr<QuicEncryptedPacket> ConstructRstPacket( - QuicPacketSequenceNumber num, - QuicStreamId stream_id) { - QuicPacketHeader header; - header.public_header.guid = random_generator_.RandUint64(); - header.public_header.reset_flag = false; - header.public_header.version_flag = true; - header.packet_sequence_number = num; - header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; - header.entropy_flag = false; - header.fec_flag = false; - header.fec_group = 0; - - QuicRstStreamFrame rst(stream_id, QUIC_STREAM_CANCELLED); - return scoped_ptr<QuicEncryptedPacket>( - ConstructPacket(header, QuicFrame(&rst))); + scoped_ptr<QuicHttpStream> CreateIfSessionExists( + const HostPortPair& host_port_pair, + const BoundNetLog& net_log) { + return QuicStreamFactoryPeer::CreateIfSessionExists( + &factory_, host_port_pair, false, net_log_); } - scoped_ptr<QuicEncryptedPacket> ConstructAckPacket( - QuicPacketSequenceNumber largest_received, - QuicPacketSequenceNumber least_unacked) { - QuicPacketHeader header; - header.public_header.guid = random_generator_.RandUint64(); - header.public_header.reset_flag = false; - header.public_header.version_flag = false; - header.packet_sequence_number = 2; - header.entropy_flag = false; - header.fec_flag = false; - header.fec_group = 0; - - QuicAckFrame ack(largest_received, QuicTime::Zero(), least_unacked); - QuicCongestionFeedbackFrame feedback; - feedback.type = kTCP; - feedback.tcp.accumulated_number_of_lost_packets = 0; - feedback.tcp.receive_window = 16000; - - QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), false); - QuicFrames frames; - frames.push_back(QuicFrame(&ack)); - frames.push_back(QuicFrame(&feedback)); - scoped_ptr<QuicPacket> packet( - framer.BuildUnsizedDataPacket(header, frames).packet); - return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket( - ENCRYPTION_NONE, header.packet_sequence_number, *packet)); + int GetSourcePortForNewSession(const HostPortPair& destination) { + return GetSourcePortForNewSessionInner(destination, false); } - // Returns a newly created packet to send congestion feedback data. - scoped_ptr<QuicEncryptedPacket> ConstructFeedbackPacket( - QuicPacketSequenceNumber sequence_number) { - QuicPacketHeader header; - header.public_header.guid = random_generator_.RandUint64(); - header.public_header.reset_flag = false; - header.public_header.version_flag = false; - header.packet_sequence_number = sequence_number; - header.entropy_flag = false; - header.fec_flag = false; - header.fec_group = 0; - - QuicCongestionFeedbackFrame frame; - frame.type = kTCP; - frame.tcp.accumulated_number_of_lost_packets = 0; - frame.tcp.receive_window = 16000; - - return scoped_ptr<QuicEncryptedPacket>( - ConstructPacket(header, QuicFrame(&frame))); + int GetSourcePortForNewSessionAndGoAway( + const HostPortPair& destination) { + return GetSourcePortForNewSessionInner(destination, true); } - scoped_ptr<QuicEncryptedPacket> ConstructPacket( - const QuicPacketHeader& header, - const QuicFrame& frame) { - QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), false); - QuicFrames frames; - frames.push_back(frame); - scoped_ptr<QuicPacket> packet( - framer.BuildUnsizedDataPacket(header, frames).packet); - return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket( - ENCRYPTION_NONE, header.packet_sequence_number, *packet)); + int GetSourcePortForNewSessionInner(const HostPortPair& destination, + bool goaway_received) { + // Should only be called if there is no active session for this destination. + EXPECT_EQ(NULL, CreateIfSessionExists(destination, net_log_).get()); + size_t socket_count = socket_factory_.udp_client_sockets().size(); + + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0); + socket_data.StopAfter(1); + socket_factory_.AddSocketDataProvider(&socket_data); + + QuicStreamRequest request(&factory_); + EXPECT_EQ(ERR_IO_PENDING, + request.Request(destination, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); + + EXPECT_EQ(OK, callback_.WaitForResult()); + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); + EXPECT_TRUE(stream.get()); + stream.reset(); + + QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession( + &factory_, destination, is_https_); + + if (socket_count + 1 != socket_factory_.udp_client_sockets().size()) { + EXPECT_TRUE(false); + return 0; + } + + IPEndPoint endpoint; + socket_factory_. + udp_client_sockets()[socket_count]->GetLocalAddress(&endpoint); + int port = endpoint.port(); + if (goaway_received) { + QuicGoAwayFrame goaway(QUIC_NO_ERROR, 1, ""); + session->OnGoAway(goaway); + } + + factory_.OnSessionClosed(session); + EXPECT_EQ(NULL, CreateIfSessionExists(destination, net_log_).get()); + EXPECT_TRUE(socket_data.at_read_eof()); + EXPECT_TRUE(socket_data.at_write_eof()); + return port; + } + + scoped_ptr<QuicEncryptedPacket> ConstructRstPacket() { + QuicStreamId stream_id = kClientDataStreamId1; + return maker_.MakeRstPacket( + 1, true, stream_id, + AdjustErrorForVersion(QUIC_RST_FLOW_CONTROL_ACCOUNTING, GetParam())); } MockHostResolver host_resolver_; DeterministicMockClientSocketFactory socket_factory_; MockCryptoClientStreamFactory crypto_client_stream_factory_; MockRandom random_generator_; + QuicTestPacketMaker maker_; MockClock* clock_; // Owned by factory_. + scoped_ptr<CertVerifier> cert_verifier_; QuicStreamFactory factory_; - HostPortProxyPair host_port_proxy_pair_; + HostPortPair host_port_pair_; bool is_https_; - scoped_ptr<CertVerifier> cert_verifier_; + PrivacyMode privacy_mode_; BoundNetLog net_log_; TestCompletionCallback callback_; }; -TEST_F(QuicStreamFactoryTest, CreateIfSessionExists) { - EXPECT_EQ(NULL, factory_.CreateIfSessionExists(host_port_proxy_pair_, - net_log_).get()); +INSTANTIATE_TEST_CASE_P(Version, QuicStreamFactoryTest, + ::testing::ValuesIn(QuicSupportedVersions())); + +TEST_P(QuicStreamFactoryTest, CreateIfSessionExists) { + EXPECT_EQ(NULL, CreateIfSessionExists(host_port_pair_, net_log_).get()); } -TEST_F(QuicStreamFactoryTest, Create) { +TEST_P(QuicStreamFactoryTest, Create) { MockRead reads[] = { MockRead(ASYNC, OK, 0) // EOF }; @@ -183,24 +205,32 @@ TEST_F(QuicStreamFactoryTest, Create) { socket_data.StopAfter(1); QuicStreamRequest request(&factory_); - EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, - callback_.callback())); + EXPECT_EQ(ERR_IO_PENDING, + request.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); EXPECT_TRUE(stream.get()); // Will reset stream 3. - stream = factory_.CreateIfSessionExists(host_port_proxy_pair_, net_log_); + stream = CreateIfSessionExists(host_port_pair_, net_log_); EXPECT_TRUE(stream.get()); // TODO(rtenneti): We should probably have a tests that HTTP and HTTPS result // in streams on different sessions. QuicStreamRequest request2(&factory_); - EXPECT_EQ(OK, request2.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, - callback_.callback())); + EXPECT_EQ(OK, + request2.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); stream = request2.ReleaseStream(); // Will reset stream 5. stream.reset(); // Will reset stream 7. @@ -208,36 +238,382 @@ TEST_F(QuicStreamFactoryTest, Create) { EXPECT_TRUE(socket_data.at_write_eof()); } -TEST_F(QuicStreamFactoryTest, FailedCreate) { - MockConnect connect(SYNCHRONOUS, ERR_ADDRESS_IN_USE); - DeterministicSocketData socket_data(NULL, 0, NULL, 0); - socket_data.set_connect_data(connect); +TEST_P(QuicStreamFactoryTest, CreateZeroRtt) { + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0); socket_factory_.AddSocketDataProvider(&socket_data); socket_data.StopAfter(1); + crypto_client_stream_factory_.set_handshake_mode( + MockCryptoClientStream::ZERO_RTT); + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(), + "192.168.0.1", ""); + QuicStreamRequest request(&factory_); - EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, - callback_.callback())); + EXPECT_EQ(OK, + request.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); - EXPECT_EQ(ERR_ADDRESS_IN_USE, callback_.WaitForResult()); + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); + EXPECT_TRUE(stream.get()); + EXPECT_TRUE(socket_data.at_read_eof()); + EXPECT_TRUE(socket_data.at_write_eof()); } -TEST_F(QuicStreamFactoryTest, Goaway) { +TEST_P(QuicStreamFactoryTest, CreateZeroRttPost) { MockRead reads[] = { MockRead(ASYNC, OK, 0) // EOF }; DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0); + socket_factory_.AddSocketDataProvider(&socket_data); socket_data.StopAfter(1); + + crypto_client_stream_factory_.set_handshake_mode( + MockCryptoClientStream::ZERO_RTT); + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(), + "192.168.0.1", ""); + + QuicStreamRequest request(&factory_); + // Posts require handshake confirmation, so this will return asynchronously. + EXPECT_EQ(ERR_IO_PENDING, + request.Request(host_port_pair_, + is_https_, + privacy_mode_, + "POST", + net_log_, + callback_.callback())); + + // Confirm the handshake and verify that the stream is created. + crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent( + QuicSession::HANDSHAKE_CONFIRMED); + + EXPECT_EQ(OK, callback_.WaitForResult()); + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); + EXPECT_TRUE(stream.get()); + EXPECT_TRUE(socket_data.at_read_eof()); + EXPECT_TRUE(socket_data.at_write_eof()); +} + +TEST_P(QuicStreamFactoryTest, CreateHttpVsHttps) { + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data1(reads, arraysize(reads), NULL, 0); + DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0); + socket_factory_.AddSocketDataProvider(&socket_data1); + socket_factory_.AddSocketDataProvider(&socket_data2); + socket_data1.StopAfter(1); + socket_data2.StopAfter(1); + + QuicStreamRequest request(&factory_); + EXPECT_EQ(ERR_IO_PENDING, + request.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); + + EXPECT_EQ(OK, callback_.WaitForResult()); + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); + EXPECT_TRUE(stream.get()); + + QuicStreamRequest request2(&factory_); + EXPECT_EQ(ERR_IO_PENDING, + request2.Request(host_port_pair_, + !is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); + EXPECT_EQ(OK, callback_.WaitForResult()); + stream = request2.ReleaseStream(); + EXPECT_TRUE(stream.get()); + stream.reset(); + + EXPECT_NE(QuicStreamFactoryPeer::GetActiveSession( + &factory_, host_port_pair_, is_https_), + QuicStreamFactoryPeer::GetActiveSession( + &factory_, host_port_pair_, !is_https_)); + + EXPECT_TRUE(socket_data1.at_read_eof()); + EXPECT_TRUE(socket_data1.at_write_eof()); + EXPECT_TRUE(socket_data2.at_read_eof()); + EXPECT_TRUE(socket_data2.at_write_eof()); +} + +// TODO(rch): re-enable this. +TEST_P(QuicStreamFactoryTest, DISABLED_Pooling) { + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0); socket_factory_.AddSocketDataProvider(&socket_data); + socket_data.StopAfter(1); + + HostPortPair server2("mail.google.com", kDefaultServerPort); + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule( + kDefaultServerHostName, "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule( + "mail.google.com", "192.168.0.1", ""); + + QuicStreamRequest request(&factory_); + EXPECT_EQ(OK, + request.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); + EXPECT_TRUE(stream.get()); + + TestCompletionCallback callback; + QuicStreamRequest request2(&factory_); + EXPECT_EQ(OK, + request2.Request(server2, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback.callback())); + scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); + EXPECT_TRUE(stream2.get()); + + EXPECT_EQ( + QuicStreamFactoryPeer::GetActiveSession( + &factory_, host_port_pair_, is_https_), + QuicStreamFactoryPeer::GetActiveSession(&factory_, server2, is_https_)); + + EXPECT_TRUE(socket_data.at_read_eof()); + EXPECT_TRUE(socket_data.at_write_eof()); +} + +TEST_P(QuicStreamFactoryTest, NoPoolingAfterGoAway) { + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data1(reads, arraysize(reads), NULL, 0); DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0); + socket_factory_.AddSocketDataProvider(&socket_data1); + socket_factory_.AddSocketDataProvider(&socket_data2); + socket_data1.StopAfter(1); socket_data2.StopAfter(1); + + HostPortPair server2("mail.google.com", kDefaultServerPort); + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule( + kDefaultServerHostName, "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule( + "mail.google.com", "192.168.0.1", ""); + + QuicStreamRequest request(&factory_); + EXPECT_EQ(OK, + request.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); + EXPECT_TRUE(stream.get()); + + TestCompletionCallback callback; + QuicStreamRequest request2(&factory_); + EXPECT_EQ(OK, + request2.Request(server2, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback.callback())); + scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); + EXPECT_TRUE(stream2.get()); + + factory_.OnSessionGoingAway(QuicStreamFactoryPeer::GetActiveSession( + &factory_, host_port_pair_, is_https_)); + EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession( + &factory_, host_port_pair_, is_https_)); + EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession( + &factory_, server2, is_https_)); + + TestCompletionCallback callback3; + QuicStreamRequest request3(&factory_); + EXPECT_EQ(OK, + request3.Request(server2, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback3.callback())); + scoped_ptr<QuicHttpStream> stream3 = request3.ReleaseStream(); + EXPECT_TRUE(stream3.get()); + + EXPECT_TRUE(QuicStreamFactoryPeer::HasActiveSession( + &factory_, server2, is_https_)); + + EXPECT_TRUE(socket_data1.at_read_eof()); + EXPECT_TRUE(socket_data1.at_write_eof()); + EXPECT_TRUE(socket_data2.at_read_eof()); + EXPECT_TRUE(socket_data2.at_write_eof()); +} + +// TODO(rch): re-enable this. +TEST_P(QuicStreamFactoryTest, DISABLED_HttpsPooling) { + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0); + socket_factory_.AddSocketDataProvider(&socket_data); + socket_data.StopAfter(1); + + HostPortPair server1("www.example.org", 443); + HostPortPair server2("mail.example.org", 443); + + // Load a cert that is valid for: + // www.example.org (server1) + // mail.example.org (server2) + // www.example.com + base::FilePath certs_dir = GetTestCertsDirectory(); + scoped_refptr<X509Certificate> test_cert( + ImportCertFromFile(certs_dir, "spdy_pooling.pem")); + ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert); + ProofVerifyDetailsChromium verify_details; + verify_details.cert_verify_result.verified_cert = test_cert; + crypto_client_stream_factory_.set_proof_verify_details(&verify_details); + + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", ""); + + QuicStreamRequest request(&factory_); + is_https_ = true; + EXPECT_EQ(OK, + request.Request(server1, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); + EXPECT_TRUE(stream.get()); + + TestCompletionCallback callback; + QuicStreamRequest request2(&factory_); + EXPECT_EQ(OK, + request2.Request(server2, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); + scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); + EXPECT_TRUE(stream2.get()); + + EXPECT_EQ(QuicStreamFactoryPeer::GetActiveSession( + &factory_, server1, is_https_), + QuicStreamFactoryPeer::GetActiveSession( + &factory_, server2, is_https_)); + + EXPECT_TRUE(socket_data.at_read_eof()); + EXPECT_TRUE(socket_data.at_write_eof()); +} + +TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithCertMismatch) { + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data1(reads, arraysize(reads), NULL, 0); + DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0); + socket_factory_.AddSocketDataProvider(&socket_data1); socket_factory_.AddSocketDataProvider(&socket_data2); + socket_data1.StopAfter(1); + socket_data2.StopAfter(1); + + HostPortPair server1("www.example.org", 443); + HostPortPair server2("mail.google.com", 443); + + // Load a cert that is valid for: + // www.example.org (server1) + // mail.example.org + // www.example.com + // But is not valid for mail.google.com (server2). + base::FilePath certs_dir = GetTestCertsDirectory(); + scoped_refptr<X509Certificate> test_cert( + ImportCertFromFile(certs_dir, "spdy_pooling.pem")); + ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert); + ProofVerifyDetailsChromium verify_details; + verify_details.cert_verify_result.verified_cert = test_cert; + crypto_client_stream_factory_.set_proof_verify_details(&verify_details); + + + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", ""); QuicStreamRequest request(&factory_); - EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, - callback_.callback())); + is_https_ = true; + EXPECT_EQ(OK, + request.Request(server1, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); + EXPECT_TRUE(stream.get()); + + TestCompletionCallback callback; + QuicStreamRequest request2(&factory_); + EXPECT_EQ(OK, + request2.Request(server2, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); + scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); + EXPECT_TRUE(stream2.get()); + + EXPECT_NE(QuicStreamFactoryPeer::GetActiveSession( + &factory_, server1, is_https_), + QuicStreamFactoryPeer::GetActiveSession( + &factory_, server2, is_https_)); + + EXPECT_TRUE(socket_data1.at_read_eof()); + EXPECT_TRUE(socket_data1.at_write_eof()); + EXPECT_TRUE(socket_data2.at_read_eof()); + EXPECT_TRUE(socket_data2.at_write_eof()); +} + +TEST_P(QuicStreamFactoryTest, Goaway) { + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0); + socket_data.StopAfter(1); + socket_factory_.AddSocketDataProvider(&socket_data); + DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0); + socket_data2.StopAfter(1); + socket_factory_.AddSocketDataProvider(&socket_data2); + + QuicStreamRequest request(&factory_); + EXPECT_EQ(ERR_IO_PENDING, + request.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); @@ -246,29 +622,33 @@ TEST_F(QuicStreamFactoryTest, Goaway) { // Mark the session as going away. Ensure that while it is still alive // that it is no longer active. QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession( - &factory_, host_port_proxy_pair_); + &factory_, host_port_pair_, is_https_); factory_.OnSessionGoingAway(session); EXPECT_EQ(true, QuicStreamFactoryPeer::IsLiveSession(&factory_, session)); - EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(&factory_, - host_port_proxy_pair_)); - EXPECT_EQ(NULL, factory_.CreateIfSessionExists(host_port_proxy_pair_, - net_log_).get()); + EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession( + &factory_, host_port_pair_, is_https_)); + EXPECT_EQ(NULL, CreateIfSessionExists(host_port_pair_, net_log_).get()); // Create a new request for the same destination and verify that a // new session is created. QuicStreamRequest request2(&factory_); - EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, - callback_.callback())); + EXPECT_EQ(ERR_IO_PENDING, + request2.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); EXPECT_TRUE(stream2.get()); EXPECT_TRUE(QuicStreamFactoryPeer::HasActiveSession(&factory_, - host_port_proxy_pair_)); + host_port_pair_, + is_https_)); EXPECT_NE(session, QuicStreamFactoryPeer::GetActiveSession( - &factory_, host_port_proxy_pair_)); + &factory_, host_port_pair_, is_https_)); EXPECT_EQ(true, QuicStreamFactoryPeer::IsLiveSession(&factory_, session)); stream2.reset(); @@ -276,13 +656,17 @@ TEST_F(QuicStreamFactoryTest, Goaway) { EXPECT_TRUE(socket_data.at_read_eof()); EXPECT_TRUE(socket_data.at_write_eof()); + EXPECT_TRUE(socket_data2.at_read_eof()); + EXPECT_TRUE(socket_data2.at_write_eof()); } -TEST_F(QuicStreamFactoryTest, MaxOpenStream) { +TEST_P(QuicStreamFactoryTest, MaxOpenStream) { MockRead reads[] = { MockRead(ASYNC, OK, 0) // EOF }; - scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket(1, 3)); + QuicStreamId stream_id = kClientDataStreamId1; + scoped_ptr<QuicEncryptedPacket> rst( + maker_.MakeRstPacket(1, true, stream_id, QUIC_STREAM_CANCELLED)); MockWrite writes[] = { MockWrite(ASYNC, rst->data(), rst->length(), 1), }; @@ -297,8 +681,11 @@ TEST_F(QuicStreamFactoryTest, MaxOpenStream) { // 2 * kDefaultMaxStreamsPerConnection. for (size_t i = 0; i < 2 * kDefaultMaxStreamsPerConnection; i++) { QuicStreamRequest request(&factory_); - int rv = request.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, + int rv = request.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, callback_.callback()); if (i == 0) { EXPECT_EQ(ERR_IO_PENDING, rv); @@ -314,9 +701,13 @@ TEST_F(QuicStreamFactoryTest, MaxOpenStream) { } QuicStreamRequest request(&factory_); - EXPECT_EQ(OK, request.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, - CompletionCallback())); + EXPECT_EQ(OK, + request.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + CompletionCallback())); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); EXPECT_TRUE(stream); EXPECT_EQ(ERR_IO_PENDING, stream->InitializeStream( @@ -334,16 +725,20 @@ TEST_F(QuicStreamFactoryTest, MaxOpenStream) { STLDeleteElements(&streams); } -TEST_F(QuicStreamFactoryTest, CreateError) { +TEST_P(QuicStreamFactoryTest, ResolutionErrorInCreate) { DeterministicSocketData socket_data(NULL, 0, NULL, 0); socket_factory_.AddSocketDataProvider(&socket_data); - host_resolver_.rules()->AddSimulatedFailure("www.google.com"); + host_resolver_.rules()->AddSimulatedFailure(kDefaultServerHostName); QuicStreamRequest request(&factory_); - EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, - callback_.callback())); + EXPECT_EQ(ERR_IO_PENDING, + request.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); EXPECT_EQ(ERR_NAME_NOT_RESOLVED, callback_.WaitForResult()); @@ -351,7 +746,29 @@ TEST_F(QuicStreamFactoryTest, CreateError) { EXPECT_TRUE(socket_data.at_write_eof()); } -TEST_F(QuicStreamFactoryTest, CancelCreate) { +TEST_P(QuicStreamFactoryTest, ConnectErrorInCreate) { + MockConnect connect(SYNCHRONOUS, ERR_ADDRESS_IN_USE); + DeterministicSocketData socket_data(NULL, 0, NULL, 0); + socket_data.set_connect_data(connect); + socket_factory_.AddSocketDataProvider(&socket_data); + socket_data.StopAfter(1); + + QuicStreamRequest request(&factory_); + EXPECT_EQ(ERR_IO_PENDING, + request.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); + + EXPECT_EQ(ERR_ADDRESS_IN_USE, callback_.WaitForResult()); + + EXPECT_TRUE(socket_data.at_read_eof()); + EXPECT_TRUE(socket_data.at_write_eof()); +} + +TEST_P(QuicStreamFactoryTest, CancelCreate) { MockRead reads[] = { MockRead(ASYNC, OK, 0) // EOF }; @@ -359,9 +776,13 @@ TEST_F(QuicStreamFactoryTest, CancelCreate) { socket_factory_.AddSocketDataProvider(&socket_data); { QuicStreamRequest request(&factory_); - EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, - callback_.callback())); + EXPECT_EQ(ERR_IO_PENDING, + request.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); } socket_data.StopAfter(1); @@ -369,7 +790,7 @@ TEST_F(QuicStreamFactoryTest, CancelCreate) { run_loop.RunUntilIdle(); scoped_ptr<QuicHttpStream> stream( - factory_.CreateIfSessionExists(host_port_proxy_pair_, net_log_)); + CreateIfSessionExists(host_port_pair_, net_log_)); EXPECT_TRUE(stream.get()); stream.reset(); @@ -377,11 +798,40 @@ TEST_F(QuicStreamFactoryTest, CancelCreate) { EXPECT_TRUE(socket_data.at_write_eof()); } -TEST_F(QuicStreamFactoryTest, CloseAllSessions) { +TEST_P(QuicStreamFactoryTest, CreateConsistentEphemeralPort) { + // Sequentially connect to the default host, then another host, and then the + // default host. Verify that the default host gets a consistent ephemeral + // port, that is different from the other host's connection. + + std::string other_server_name = "other.google.com"; + EXPECT_NE(kDefaultServerHostName, other_server_name); + HostPortPair host_port_pair2(other_server_name, kDefaultServerPort); + + int original_port = GetSourcePortForNewSession(host_port_pair_); + EXPECT_NE(original_port, GetSourcePortForNewSession(host_port_pair2)); + EXPECT_EQ(original_port, GetSourcePortForNewSession(host_port_pair_)); +} + +TEST_P(QuicStreamFactoryTest, GoAwayDisablesConsistentEphemeralPort) { + // Get a session to the host using the port suggester. + int original_port = + GetSourcePortForNewSessionAndGoAway(host_port_pair_); + // Verify that the port is different after the goaway. + EXPECT_NE(original_port, GetSourcePortForNewSession(host_port_pair_)); + // Since the previous session did not goaway we should see the original port. + EXPECT_EQ(original_port, GetSourcePortForNewSession(host_port_pair_)); +} + +TEST_P(QuicStreamFactoryTest, CloseAllSessions) { MockRead reads[] = { MockRead(ASYNC, 0, 0) // EOF }; - DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0); + scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket()); + std::vector<MockWrite> writes; + writes.push_back(MockWrite(ASYNC, rst->data(), rst->length(), 1)); + DeterministicSocketData socket_data(reads, arraysize(reads), + writes.empty() ? NULL : &writes[0], + writes.size()); socket_factory_.AddSocketDataProvider(&socket_data); socket_data.StopAfter(1); @@ -393,9 +843,13 @@ TEST_F(QuicStreamFactoryTest, CloseAllSessions) { socket_data2.StopAfter(1); QuicStreamRequest request(&factory_); - EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, - callback_.callback())); + EXPECT_EQ(ERR_IO_PENDING, + request.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); @@ -413,9 +867,13 @@ TEST_F(QuicStreamFactoryTest, CloseAllSessions) { // a new session. QuicStreamRequest request2(&factory_); - EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, - callback_.callback())); + EXPECT_EQ(ERR_IO_PENDING, + request2.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); stream = request2.ReleaseStream(); @@ -427,11 +885,16 @@ TEST_F(QuicStreamFactoryTest, CloseAllSessions) { EXPECT_TRUE(socket_data2.at_write_eof()); } -TEST_F(QuicStreamFactoryTest, OnIPAddressChanged) { +TEST_P(QuicStreamFactoryTest, OnIPAddressChanged) { MockRead reads[] = { MockRead(ASYNC, 0, 0) // EOF }; - DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0); + scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket()); + std::vector<MockWrite> writes; + writes.push_back(MockWrite(ASYNC, rst->data(), rst->length(), 1)); + DeterministicSocketData socket_data(reads, arraysize(reads), + writes.empty() ? NULL : &writes[0], + writes.size()); socket_factory_.AddSocketDataProvider(&socket_data); socket_data.StopAfter(1); @@ -443,9 +906,13 @@ TEST_F(QuicStreamFactoryTest, OnIPAddressChanged) { socket_data2.StopAfter(1); QuicStreamRequest request(&factory_); - EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, - callback_.callback())); + EXPECT_EQ(ERR_IO_PENDING, + request.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); @@ -464,9 +931,13 @@ TEST_F(QuicStreamFactoryTest, OnIPAddressChanged) { // a new session. QuicStreamRequest request2(&factory_); - EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, - callback_.callback())); + EXPECT_EQ(ERR_IO_PENDING, + request2.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); stream = request2.ReleaseStream(); @@ -478,11 +949,16 @@ TEST_F(QuicStreamFactoryTest, OnIPAddressChanged) { EXPECT_TRUE(socket_data2.at_write_eof()); } -TEST_F(QuicStreamFactoryTest, OnCertAdded) { +TEST_P(QuicStreamFactoryTest, OnCertAdded) { MockRead reads[] = { MockRead(ASYNC, 0, 0) // EOF }; - DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0); + scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket()); + std::vector<MockWrite> writes; + writes.push_back(MockWrite(ASYNC, rst->data(), rst->length(), 1)); + DeterministicSocketData socket_data(reads, arraysize(reads), + writes.empty() ? NULL : &writes[0], + writes.size()); socket_factory_.AddSocketDataProvider(&socket_data); socket_data.StopAfter(1); @@ -494,9 +970,13 @@ TEST_F(QuicStreamFactoryTest, OnCertAdded) { socket_data2.StopAfter(1); QuicStreamRequest request(&factory_); - EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, - callback_.callback())); + EXPECT_EQ(ERR_IO_PENDING, + request.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); @@ -515,9 +995,13 @@ TEST_F(QuicStreamFactoryTest, OnCertAdded) { // a new session. QuicStreamRequest request2(&factory_); - EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, - callback_.callback())); + EXPECT_EQ(ERR_IO_PENDING, + request2.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); stream = request2.ReleaseStream(); @@ -529,11 +1013,16 @@ TEST_F(QuicStreamFactoryTest, OnCertAdded) { EXPECT_TRUE(socket_data2.at_write_eof()); } -TEST_F(QuicStreamFactoryTest, OnCACertChanged) { +TEST_P(QuicStreamFactoryTest, OnCACertChanged) { MockRead reads[] = { MockRead(ASYNC, 0, 0) // EOF }; - DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0); + scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket()); + std::vector<MockWrite> writes; + writes.push_back(MockWrite(ASYNC, rst->data(), rst->length(), 1)); + DeterministicSocketData socket_data(reads, arraysize(reads), + writes.empty() ? NULL : &writes[0], + writes.size()); socket_factory_.AddSocketDataProvider(&socket_data); socket_data.StopAfter(1); @@ -545,9 +1034,13 @@ TEST_F(QuicStreamFactoryTest, OnCACertChanged) { socket_data2.StopAfter(1); QuicStreamRequest request(&factory_); - EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, - callback_.callback())); + EXPECT_EQ(ERR_IO_PENDING, + request.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); @@ -566,9 +1059,13 @@ TEST_F(QuicStreamFactoryTest, OnCACertChanged) { // a new session. QuicStreamRequest request2(&factory_); - EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_, - cert_verifier_.get(), net_log_, - callback_.callback())); + EXPECT_EQ(ERR_IO_PENDING, + request2.Request(host_port_pair_, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); stream = request2.ReleaseStream(); @@ -580,7 +1077,7 @@ TEST_F(QuicStreamFactoryTest, OnCACertChanged) { EXPECT_TRUE(socket_data2.at_write_eof()); } -TEST_F(QuicStreamFactoryTest, SharedCryptoConfig) { +TEST_P(QuicStreamFactoryTest, SharedCryptoConfig) { vector<string> cannoncial_suffixes; cannoncial_suffixes.push_back(string(".c.youtube.com")); cannoncial_suffixes.push_back(string(".googlevideo.com")); @@ -591,15 +1088,12 @@ TEST_F(QuicStreamFactoryTest, SharedCryptoConfig) { r1_host_name.append(cannoncial_suffixes[i]); r2_host_name.append(cannoncial_suffixes[i]); - HostPortProxyPair host_port_proxy_pair1(HostPortPair(r1_host_name, 80), - ProxyServer::Direct()); - - QuicCryptoClientConfig* crypto_config1 = - QuicStreamFactoryPeer::GetOrCreateCryptoConfig(&factory_, - host_port_proxy_pair1); - DCHECK(crypto_config1); + HostPortPair host_port_pair1(r1_host_name, 80); + QuicCryptoClientConfig* crypto_config = + QuicStreamFactoryPeer::GetCryptoConfig(&factory_); + QuicServerId server_id1(host_port_pair1, is_https_, privacy_mode_); QuicCryptoClientConfig::CachedState* cached1 = - crypto_config1->LookupOrCreate(host_port_proxy_pair1.first.host()); + crypto_config->LookupOrCreate(server_id1); EXPECT_FALSE(cached1->proof_valid()); EXPECT_TRUE(cached1->source_address_token().empty()); @@ -608,20 +1102,16 @@ TEST_F(QuicStreamFactoryTest, SharedCryptoConfig) { cached1->set_source_address_token(r1_host_name); cached1->SetProofValid(); - HostPortProxyPair host_port_proxy_pair2(HostPortPair(r2_host_name, 80), - ProxyServer::Direct()); - QuicCryptoClientConfig* crypto_config2 = - QuicStreamFactoryPeer::GetOrCreateCryptoConfig(&factory_, - host_port_proxy_pair2); - DCHECK(crypto_config2); + HostPortPair host_port_pair2(r2_host_name, 80); + QuicServerId server_id2(host_port_pair2, is_https_, privacy_mode_); QuicCryptoClientConfig::CachedState* cached2 = - crypto_config2->LookupOrCreate(host_port_proxy_pair2.first.host()); + crypto_config->LookupOrCreate(server_id2); EXPECT_EQ(cached1->source_address_token(), cached2->source_address_token()); EXPECT_TRUE(cached2->proof_valid()); } } -TEST_F(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) { +TEST_P(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) { vector<string> cannoncial_suffixes; cannoncial_suffixes.push_back(string(".c.youtube.com")); cannoncial_suffixes.push_back(string(".googlevideo.com")); @@ -632,15 +1122,12 @@ TEST_F(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) { r3_host_name.append(cannoncial_suffixes[i]); r4_host_name.append(cannoncial_suffixes[i]); - HostPortProxyPair host_port_proxy_pair1(HostPortPair(r3_host_name, 80), - ProxyServer::Direct()); - - QuicCryptoClientConfig* crypto_config1 = - QuicStreamFactoryPeer::GetOrCreateCryptoConfig(&factory_, - host_port_proxy_pair1); - DCHECK(crypto_config1); + HostPortPair host_port_pair1(r3_host_name, 80); + QuicCryptoClientConfig* crypto_config = + QuicStreamFactoryPeer::GetCryptoConfig(&factory_); + QuicServerId server_id1(host_port_pair1, is_https_, privacy_mode_); QuicCryptoClientConfig::CachedState* cached1 = - crypto_config1->LookupOrCreate(host_port_proxy_pair1.first.host()); + crypto_config->LookupOrCreate(server_id1); EXPECT_FALSE(cached1->proof_valid()); EXPECT_TRUE(cached1->source_address_token().empty()); @@ -649,14 +1136,10 @@ TEST_F(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) { cached1->set_source_address_token(r3_host_name); cached1->SetProofInvalid(); - HostPortProxyPair host_port_proxy_pair2(HostPortPair(r4_host_name, 80), - ProxyServer::Direct()); - QuicCryptoClientConfig* crypto_config2 = - QuicStreamFactoryPeer::GetOrCreateCryptoConfig(&factory_, - host_port_proxy_pair2); - DCHECK(crypto_config2); + HostPortPair host_port_pair2(r4_host_name, 80); + QuicServerId server_id2(host_port_pair2, is_https_, privacy_mode_); QuicCryptoClientConfig::CachedState* cached2 = - crypto_config2->LookupOrCreate(host_port_proxy_pair2.first.host()); + crypto_config->LookupOrCreate(server_id2); EXPECT_NE(cached1->source_address_token(), cached2->source_address_token()); EXPECT_TRUE(cached2->source_address_token().empty()); EXPECT_FALSE(cached2->proof_valid()); diff --git a/chromium/net/quic/quic_stream_sequencer.cc b/chromium/net/quic/quic_stream_sequencer.cc index 02fec67f200..3a303957c2c 100644 --- a/chromium/net/quic/quic_stream_sequencer.cc +++ b/chromium/net/quic/quic_stream_sequencer.cc @@ -8,8 +8,10 @@ #include <limits> #include "base/logging.h" +#include "base/metrics/sparse_histogram.h" #include "net/quic/reliable_quic_stream.h" +using std::make_pair; using std::min; using std::numeric_limits; @@ -18,61 +20,29 @@ namespace net { QuicStreamSequencer::QuicStreamSequencer(ReliableQuicStream* quic_stream) : stream_(quic_stream), num_bytes_consumed_(0), - max_frame_memory_(numeric_limits<size_t>::max()), - close_offset_(numeric_limits<QuicStreamOffset>::max()) { -} - -QuicStreamSequencer::QuicStreamSequencer(size_t max_frame_memory, - ReliableQuicStream* quic_stream) - : stream_(quic_stream), - num_bytes_consumed_(0), - max_frame_memory_(max_frame_memory), - close_offset_(numeric_limits<QuicStreamOffset>::max()) { - if (max_frame_memory < kMaxPacketSize) { - LOG(DFATAL) << "Setting max frame memory to " << max_frame_memory - << ". Some frames will be impossible to handle."; - } + close_offset_(numeric_limits<QuicStreamOffset>::max()), + blocked_(false), + num_bytes_buffered_(0), + num_frames_received_(0), + num_duplicate_frames_received_(0) { } QuicStreamSequencer::~QuicStreamSequencer() { } -bool QuicStreamSequencer::WillAcceptStreamFrame( - const QuicStreamFrame& frame) const { - size_t data_len = frame.data.TotalBufferSize(); - DCHECK_LE(data_len, max_frame_memory_); - +bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { + ++num_frames_received_; if (IsDuplicate(frame)) { + ++num_duplicate_frames_received_; + // Silently ignore duplicates. return true; } - QuicStreamOffset byte_offset = frame.offset; - if (data_len > max_frame_memory_) { - // We're never going to buffer this frame and we can't pass it up. - // The stream might only consume part of it and we'd need a partial ack. - // - // Ideally this should never happen, as we check that - // max_frame_memory_ > kMaxPacketSize and lower levels should reject - // frames larger than that. - return false; - } - if (byte_offset + data_len - num_bytes_consumed_ > max_frame_memory_) { - // We can buffer this but not right now. Toss it. - // It might be worth trying an experiment where we try best-effort buffering - return false; - } - return true; -} -bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { - if (!WillAcceptStreamFrame(frame)) { - // This should not happen, as WillAcceptFrame should be called before - // OnStreamFrame. Error handling should be done by the caller. + if (FrameOverlapsBufferedData(frame)) { + stream_->CloseConnectionWithDetails( + QUIC_INVALID_STREAM_FRAME, "Stream frame overlaps with buffered data."); return false; } - if (IsDuplicate(frame)) { - // Silently ignore duplicates. - return true; - } QuicStreamOffset byte_offset = frame.offset; size_t data_len = frame.data.TotalBufferSize(); @@ -92,7 +62,10 @@ bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { IOVector data; data.AppendIovec(frame.data.iovec(), frame.data.Size()); - if (byte_offset == num_bytes_consumed_) { + + // If the frame has arrived in-order then we can process it immediately, only + // buffering if the stream is unable to process it. + if (!blocked_ && byte_offset == num_bytes_consumed_) { DVLOG(1) << "Processing byte offset " << byte_offset; size_t bytes_consumed = 0; for (size_t i = 0; i < data.Size(); ++i) { @@ -101,6 +74,8 @@ bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { data.iovec()[i].iov_len); } num_bytes_consumed_ += bytes_consumed; + stream_->AddBytesConsumed(bytes_consumed); + if (MaybeCloseStream()) { return true; } @@ -111,18 +86,21 @@ bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { FlushBufferedFrames(); return true; // it's safe to ack this frame. } else { - // Set ourselves up to buffer what's left + // Set ourselves up to buffer what's left. data_len -= bytes_consumed; data.Consume(bytes_consumed); byte_offset += bytes_consumed; } } + + // Buffer any remaining data to be consumed by the stream when ready. for (size_t i = 0; i < data.Size(); ++i) { DVLOG(1) << "Buffering stream data at offset " << byte_offset; - frames_.insert(make_pair(byte_offset, string( - static_cast<char*>(data.iovec()[i].iov_base), - data.iovec()[i].iov_len))); - byte_offset += data.iovec()[i].iov_len; + const iovec& iov = data.iovec()[i]; + buffered_frames_.insert(make_pair( + byte_offset, string(static_cast<char*>(iov.iov_base), iov.iov_len))); + byte_offset += iov.iov_len; + num_bytes_buffered_ += iov.iov_len; } return true; } @@ -143,23 +121,26 @@ void QuicStreamSequencer::CloseStreamAtOffset(QuicStreamOffset offset) { } bool QuicStreamSequencer::MaybeCloseStream() { - if (IsClosed()) { + if (!blocked_ && IsClosed()) { DVLOG(1) << "Passing up termination, as we've processed " << num_bytes_consumed_ << " of " << close_offset_ << " bytes."; // Technically it's an error if num_bytes_consumed isn't exactly // equal, but error handling seems silly at this point. stream_->OnFinRead(); + buffered_frames_.clear(); + num_bytes_buffered_ = 0; return true; } return false; } int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) { - FrameMap::iterator it = frames_.begin(); + DCHECK(!blocked_); + FrameMap::iterator it = buffered_frames_.begin(); size_t index = 0; QuicStreamOffset offset = num_bytes_consumed_; - while (it != frames_.end() && index < iov_len) { + while (it != buffered_frames_.end() && index < iov_len) { if (it->first != offset) return index; iov[index].iov_base = static_cast<void*>( @@ -174,14 +155,15 @@ int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) { } int QuicStreamSequencer::Readv(const struct iovec* iov, size_t iov_len) { - FrameMap::iterator it = frames_.begin(); + DCHECK(!blocked_); + FrameMap::iterator it = buffered_frames_.begin(); size_t iov_index = 0; size_t iov_offset = 0; size_t frame_offset = 0; size_t initial_bytes_consumed = num_bytes_consumed_; while (iov_index < iov_len && - it != frames_.end() && + it != buffered_frames_.end() && it->first == num_bytes_consumed_) { int bytes_to_read = min(iov[iov_index].iov_len - iov_offset, it->second.size() - frame_offset); @@ -199,79 +181,90 @@ int QuicStreamSequencer::Readv(const struct iovec* iov, size_t iov_len) { } if (it->second.size() == frame_offset) { // We've copied this whole frame - num_bytes_consumed_ += it->second.size(); - frames_.erase(it); - it = frames_.begin(); + RecordBytesConsumed(it->second.size()); + buffered_frames_.erase(it); + it = buffered_frames_.begin(); frame_offset = 0; } } // We've finished copying. If we have a partial frame, update it. if (frame_offset != 0) { - frames_.insert(make_pair(it->first + frame_offset, - it->second.substr(frame_offset))); - frames_.erase(frames_.begin()); - num_bytes_consumed_ += frame_offset; + buffered_frames_.insert( + make_pair(it->first + frame_offset, it->second.substr(frame_offset))); + buffered_frames_.erase(buffered_frames_.begin()); + RecordBytesConsumed(frame_offset); } return num_bytes_consumed_ - initial_bytes_consumed; } -void QuicStreamSequencer::MarkConsumed(size_t num_bytes_consumed) { - size_t end_offset = num_bytes_consumed_ + num_bytes_consumed; - while (!frames_.empty() && end_offset != num_bytes_consumed_) { - FrameMap::iterator it = frames_.begin(); - if (it->first != num_bytes_consumed_) { - LOG(DFATAL) << "Invalid argument to MarkConsumed. " - << " num_bytes_consumed_: " << num_bytes_consumed_ - << " end_offset: " << end_offset - << " offset: " << it->first - << " length: " << it->second.length(); - stream_->Reset(QUIC_ERROR_PROCESSING_STREAM); - return; - } - - if (it->first + it->second.length() <= end_offset) { - num_bytes_consumed_ += it->second.length(); - // This chunk is entirely consumed. - frames_.erase(it); - continue; - } - - // Partially consume this frame. - size_t delta = end_offset - it->first; - num_bytes_consumed_ += delta; - frames_.insert(make_pair(end_offset, it->second.substr(delta))); - frames_.erase(it); - break; - } -} - bool QuicStreamSequencer::HasBytesToRead() const { - FrameMap::const_iterator it = frames_.begin(); + FrameMap::const_iterator it = buffered_frames_.begin(); - return it != frames_.end() && it->first == num_bytes_consumed_; + return it != buffered_frames_.end() && it->first == num_bytes_consumed_; } bool QuicStreamSequencer::IsClosed() const { return num_bytes_consumed_ >= close_offset_; } +bool QuicStreamSequencer::FrameOverlapsBufferedData( + const QuicStreamFrame& frame) const { + if (buffered_frames_.empty()) { + return false; + } + + FrameMap::const_iterator next_frame = + buffered_frames_.lower_bound(frame.offset); + // Duplicate frames should have been dropped in IsDuplicate. + DCHECK(next_frame == buffered_frames_.end() || + next_frame->first != frame.offset); + + // If there is a buffered frame with a higher starting offset, then we check + // to see if the new frame runs into the higher frame. + if (next_frame != buffered_frames_.end() && + (frame.offset + frame.data.TotalBufferSize()) > next_frame->first) { + DVLOG(1) << "New frame overlaps next frame: " << frame.offset << " + " + << frame.data.TotalBufferSize() << " > " << next_frame->first; + return true; + } + + // If there is a buffered frame with a lower starting offset, then we check + // to see if the buffered frame runs into the new frame. + if (next_frame != buffered_frames_.begin()) { + FrameMap::const_iterator preceeding_frame = --next_frame; + QuicStreamOffset offset = preceeding_frame->first; + uint64 data_length = preceeding_frame->second.length(); + if ((offset + data_length) > frame.offset) { + DVLOG(1) << "Preceeding frame overlaps new frame: " << offset << " + " + << data_length << " > " << frame.offset; + return true; + } + } + return false; +} + bool QuicStreamSequencer::IsDuplicate(const QuicStreamFrame& frame) const { // A frame is duplicate if the frame offset is smaller than our bytes consumed // or we have stored the frame in our map. // TODO(pwestin): Is it possible that a new frame contain more data even if // the offset is the same? return frame.offset < num_bytes_consumed_ || - frames_.find(frame.offset) != frames_.end(); + buffered_frames_.find(frame.offset) != buffered_frames_.end(); +} + +void QuicStreamSequencer::SetBlockedUntilFlush() { + blocked_ = true; } void QuicStreamSequencer::FlushBufferedFrames() { - FrameMap::iterator it = frames_.find(num_bytes_consumed_); - while (it != frames_.end()) { + blocked_ = false; + FrameMap::iterator it = buffered_frames_.find(num_bytes_consumed_); + while (it != buffered_frames_.end()) { DVLOG(1) << "Flushing buffered packet at offset " << it->first; string* data = &it->second; size_t bytes_consumed = stream_->ProcessRawData(data->c_str(), data->size()); - num_bytes_consumed_ += bytes_consumed; + RecordBytesConsumed(bytes_consumed); if (MaybeCloseStream()) { return; } @@ -279,15 +272,23 @@ void QuicStreamSequencer::FlushBufferedFrames() { stream_->Reset(QUIC_ERROR_PROCESSING_STREAM); // Programming error return; } else if (bytes_consumed == data->size()) { - frames_.erase(it); - it = frames_.find(num_bytes_consumed_); + buffered_frames_.erase(it); + it = buffered_frames_.find(num_bytes_consumed_); } else { string new_data = it->second.substr(bytes_consumed); - frames_.erase(it); - frames_.insert(make_pair(num_bytes_consumed_, new_data)); + buffered_frames_.erase(it); + buffered_frames_.insert(make_pair(num_bytes_consumed_, new_data)); return; } } + MaybeCloseStream(); +} + +void QuicStreamSequencer::RecordBytesConsumed(size_t bytes_consumed) { + num_bytes_consumed_ += bytes_consumed; + num_bytes_buffered_ -= bytes_consumed; + + stream_->AddBytesConsumed(bytes_consumed); } } // namespace net diff --git a/chromium/net/quic/quic_stream_sequencer.h b/chromium/net/quic/quic_stream_sequencer.h index e2601246059..a4580a9ca3f 100644 --- a/chromium/net/quic/quic_stream_sequencer.h +++ b/chromium/net/quic/quic_stream_sequencer.h @@ -8,7 +8,6 @@ #include <map> #include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" #include "net/base/iovec.h" #include "net/quic/quic_protocol.h" @@ -30,21 +29,13 @@ class ReliableQuicStream; class NET_EXPORT_PRIVATE QuicStreamSequencer { public: explicit QuicStreamSequencer(ReliableQuicStream* quic_stream); - QuicStreamSequencer(size_t max_frame_memory, - ReliableQuicStream* quic_stream); - virtual ~QuicStreamSequencer(); - // Returns the expected value of OnStreamFrame for this frame. - bool WillAcceptStreamFrame(const QuicStreamFrame& frame) const; - // If the frame is the next one we need in order to process in-order data, // ProcessData will be immediately called on the stream until all buffered // data is processed or the stream fails to consume data. Any unconsumed - // data will be buffered. - // - // If the frame is not the next in line, it will either be buffered, and - // this will return true, or it will be rejected and this will return false. + // data will be buffered. If the frame is not the next in line, it will be + // buffered. bool OnStreamFrame(const QuicStreamFrame& frame); // Once data is buffered, it's up to the stream to read it when the stream @@ -58,10 +49,6 @@ class NET_EXPORT_PRIVATE QuicStreamSequencer { // bytes read. Any buffered data no longer in use will be released. int Readv(const struct iovec* iov, size_t iov_len); - // Consumes |num_bytes| data. Used in conjunction with |GetReadableRegions| - // to do zero-copy reads. - void MarkConsumed(size_t num_bytes); - // Returns true if the sequncer has bytes available for reading. bool HasBytesToRead() const; @@ -71,28 +58,74 @@ class NET_EXPORT_PRIVATE QuicStreamSequencer { // Returns true if the sequencer has received this frame before. bool IsDuplicate(const QuicStreamFrame& frame) const; + // Returns true if |frame| contains data which overlaps buffered data + // (indicating an invalid stream frame has been received). + bool FrameOverlapsBufferedData(const QuicStreamFrame& frame) const; + // Calls |ProcessRawData| on |stream_| for each buffered frame that may // be processed. void FlushBufferedFrames(); + // Blocks processing of frames until |FlushBufferedFrames| is called. + void SetBlockedUntilFlush(); + + size_t num_bytes_buffered() const { return num_bytes_buffered_; } + QuicStreamOffset num_bytes_consumed() const { return num_bytes_consumed_; } + + int num_frames_received() const { return num_frames_received_; } + + int num_duplicate_frames_received() const { + return num_duplicate_frames_received_; + } + private: friend class test::QuicStreamSequencerPeer; - // TODO(alyssar) use something better than strings. - typedef map<QuicStreamOffset, string> FrameMap; - // Wait until we've seen 'offset' bytes, and then terminate the stream. void CloseStreamAtOffset(QuicStreamOffset offset); + // If we've received a FIN and have processed all remaining data, then inform + // the stream of FIN, and clear buffers. bool MaybeCloseStream(); - ReliableQuicStream* stream_; // The stream which owns this sequencer. - QuicStreamOffset num_bytes_consumed_; // The last data consumed by the stream - FrameMap frames_; // sequence number -> frame - size_t max_frame_memory_; // the maximum memory the sequencer can buffer. + // Called whenever bytes are consumed by the stream. Updates + // num_bytes_consumed_ and num_bytes_buffered_. + void RecordBytesConsumed(size_t bytes_consumed); + + // The stream which owns this sequencer. + ReliableQuicStream* stream_; + + // The last data consumed by the stream. + QuicStreamOffset num_bytes_consumed_; + + // TODO(alyssar) use something better than strings. + // TODO(rjshade): In future we may support retransmission of partial stream + // frames, in which case we will have to allow receipt of overlapping frames. + // Maybe write new frames into a ring buffer, and keep track of consumed + // bytes, and gaps. + typedef map<QuicStreamOffset, string> FrameMap; + + // Stores buffered frames (maps from sequence number -> frame data as string). + FrameMap buffered_frames_; + // The offset, if any, we got a stream termination for. When this many bytes // have been processed, the sequencer will be closed. QuicStreamOffset close_offset_; + + // If true, the sequencer is blocked from passing data to the stream and will + // buffer all new incoming data until FlushBufferedFrames is called. + bool blocked_; + + // Tracks how many bytes the sequencer has buffered. + size_t num_bytes_buffered_; + + // Count of the number of frames received. + int num_frames_received_; + + // Count of the number of duplicate frames received. + int num_duplicate_frames_received_; + + DISALLOW_COPY_AND_ASSIGN(QuicStreamSequencer); }; } // namespace net diff --git a/chromium/net/quic/quic_stream_sequencer_test.cc b/chromium/net/quic/quic_stream_sequencer_test.cc index f898e6611f7..0d9e82bf443 100644 --- a/chromium/net/quic/quic_stream_sequencer_test.cc +++ b/chromium/net/quic/quic_stream_sequencer_test.cc @@ -7,14 +7,19 @@ #include <utility> #include <vector> +#include "base/logging.h" #include "base/rand_util.h" #include "net/base/ip_endpoint.h" +#include "net/quic/quic_utils.h" #include "net/quic/reliable_quic_stream.h" +#include "net/quic/test_tools/quic_stream_sequencer_peer.h" #include "net/quic/test_tools/quic_test_utils.h" +#include "net/test/gtest_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using base::StringPiece; +using std::map; using std::min; using std::pair; using std::vector; @@ -27,42 +32,6 @@ using testing::StrEq; namespace net { namespace test { -class QuicStreamSequencerPeer : public QuicStreamSequencer { - public: - explicit QuicStreamSequencerPeer(ReliableQuicStream* stream) - : QuicStreamSequencer(stream) { - } - - QuicStreamSequencerPeer(int32 max_mem, ReliableQuicStream* stream) - : QuicStreamSequencer(max_mem, stream) { - } - - virtual bool OnFinFrame(QuicStreamOffset byte_offset, const char* data) { - QuicStreamFrame frame; - frame.stream_id = 1; - frame.offset = byte_offset; - frame.data.Append(const_cast<char*>(data), strlen(data)); - frame.fin = true; - return OnStreamFrame(frame); - } - - virtual bool OnFrame(QuicStreamOffset byte_offset, const char* data) { - QuicStreamFrame frame; - frame.stream_id = 1; - frame.offset = byte_offset; - frame.data.Append(const_cast<char*>(data), strlen(data)); - frame.fin = false; - return OnStreamFrame(frame); - } - - void SetMemoryLimit(size_t limit) { - max_frame_memory_ = limit; - } - uint64 num_bytes_consumed() const { return num_bytes_consumed_; } - const FrameMap* frames() const { return &frames_; } - QuicStreamOffset close_offset() const { return close_offset_; } -}; - class MockStream : public ReliableQuicStream { public: MockStream(QuicSession* session, QuicStreamId id) @@ -75,9 +44,12 @@ class MockStream : public ReliableQuicStream { const string& details)); MOCK_METHOD1(Reset, void(QuicRstStreamErrorCode error)); MOCK_METHOD0(OnCanWrite, void()); - virtual QuicPriority EffectivePriority() const { + virtual QuicPriority EffectivePriority() const OVERRIDE { return QuicUtils::HighestPriority(); } + virtual bool IsFlowControlEnabled() const { + return true; + } }; namespace { @@ -91,7 +63,9 @@ class QuicStreamSequencerTest : public ::testing::Test { : connection_(new MockConnection(false)), session_(connection_), stream_(&session_, 1), - sequencer_(new QuicStreamSequencerPeer(&stream_)) { + sequencer_(new QuicStreamSequencer(&stream_)), + buffered_frames_( + QuicStreamSequencerPeer::GetBufferedFrames(sequencer_.get())) { } bool VerifyReadableRegions(const char** expected, size_t num_expected) { @@ -132,111 +106,145 @@ class QuicStreamSequencerTest : public ::testing::Test { return true; } + bool OnFinFrame(QuicStreamOffset byte_offset, const char* data) { + QuicStreamFrame frame; + frame.stream_id = 1; + frame.offset = byte_offset; + frame.data.Append(const_cast<char*>(data), strlen(data)); + frame.fin = true; + return sequencer_->OnStreamFrame(frame); + } + + bool OnFrame(QuicStreamOffset byte_offset, const char* data) { + QuicStreamFrame frame; + frame.stream_id = 1; + frame.offset = byte_offset; + frame.data.Append(const_cast<char*>(data), strlen(data)); + frame.fin = false; + return sequencer_->OnStreamFrame(frame); + } + MockConnection* connection_; MockSession session_; testing::StrictMock<MockStream> stream_; - scoped_ptr<QuicStreamSequencerPeer> sequencer_; + scoped_ptr<QuicStreamSequencer> sequencer_; + map<QuicStreamOffset, string>* buffered_frames_; }; TEST_F(QuicStreamSequencerTest, RejectOldFrame) { - EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)) - .WillOnce(Return(3)); + EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3)); - EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); - EXPECT_EQ(0u, sequencer_->frames()->size()); + EXPECT_TRUE(OnFrame(0, "abc")); + EXPECT_EQ(0u, buffered_frames_->size()); EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); // Ignore this - it matches a past sequence number and we should not see it // again. - EXPECT_TRUE(sequencer_->OnFrame(0, "def")); - EXPECT_EQ(0u, sequencer_->frames()->size()); -} - -TEST_F(QuicStreamSequencerTest, RejectOverlyLargeFrame) { - // TODO(rch): enable when chromium supports EXPECT_DFATAL. - /* - EXPECT_DFATAL(sequencer_.reset(new QuicStreamSequencerPeer(2, &stream_)), - "Setting max frame memory to 2. " - "Some frames will be impossible to handle."); - - EXPECT_DEBUG_DEATH(sequencer_->OnFrame(0, "abc"), ""); - */ -} - -TEST_F(QuicStreamSequencerTest, DropFramePastBuffering) { - sequencer_->SetMemoryLimit(3); - - EXPECT_FALSE(sequencer_->OnFrame(3, "abc")); + EXPECT_TRUE(OnFrame(0, "def")); + EXPECT_EQ(0u, buffered_frames_->size()); } TEST_F(QuicStreamSequencerTest, RejectBufferedFrame) { EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)); - EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); - EXPECT_EQ(1u, sequencer_->frames()->size()); + EXPECT_TRUE(OnFrame(0, "abc")); + EXPECT_EQ(1u, buffered_frames_->size()); EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); // Ignore this - it matches a buffered frame. // Right now there's no checking that the payload is consistent. - EXPECT_TRUE(sequencer_->OnFrame(0, "def")); - EXPECT_EQ(1u, sequencer_->frames()->size()); + EXPECT_TRUE(OnFrame(0, "def")); + EXPECT_EQ(1u, buffered_frames_->size()); } TEST_F(QuicStreamSequencerTest, FullFrameConsumed) { EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3)); - EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); - EXPECT_EQ(0u, sequencer_->frames()->size()); + EXPECT_TRUE(OnFrame(0, "abc")); + EXPECT_EQ(0u, buffered_frames_->size()); + EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); +} + +TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameConsumed) { + sequencer_->SetBlockedUntilFlush(); + + EXPECT_TRUE(OnFrame(0, "abc")); + EXPECT_EQ(1u, buffered_frames_->size()); + EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); + + EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3)); + sequencer_->FlushBufferedFrames(); + EXPECT_EQ(0u, buffered_frames_->size()); + EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); + + EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(3)); + EXPECT_CALL(stream_, OnFinRead()); + EXPECT_TRUE(OnFinFrame(3, "def")); +} + +TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameAndFinConsumed) { + sequencer_->SetBlockedUntilFlush(); + + EXPECT_TRUE(OnFinFrame(0, "abc")); + EXPECT_EQ(1u, buffered_frames_->size()); + EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); + + EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3)); + EXPECT_CALL(stream_, OnFinRead()); + sequencer_->FlushBufferedFrames(); + EXPECT_EQ(0u, buffered_frames_->size()); EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); } TEST_F(QuicStreamSequencerTest, EmptyFrame) { EXPECT_CALL(stream_, CloseConnectionWithDetails(QUIC_INVALID_STREAM_FRAME, _)); - EXPECT_FALSE(sequencer_->OnFrame(0, "")); - EXPECT_EQ(0u, sequencer_->frames()->size()); + EXPECT_FALSE(OnFrame(0, "")); + EXPECT_EQ(0u, buffered_frames_->size()); EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); } TEST_F(QuicStreamSequencerTest, EmptyFinFrame) { EXPECT_CALL(stream_, OnFinRead()); - EXPECT_TRUE(sequencer_->OnFinFrame(0, "")); - EXPECT_EQ(0u, sequencer_->frames()->size()); + EXPECT_TRUE(OnFinFrame(0, "")); + EXPECT_EQ(0u, buffered_frames_->size()); EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); } TEST_F(QuicStreamSequencerTest, PartialFrameConsumed) { EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(2)); - EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); - EXPECT_EQ(1u, sequencer_->frames()->size()); + EXPECT_TRUE(OnFrame(0, "abc")); + EXPECT_EQ(1u, buffered_frames_->size()); EXPECT_EQ(2u, sequencer_->num_bytes_consumed()); - EXPECT_EQ("c", sequencer_->frames()->find(2)->second); + EXPECT_EQ("c", buffered_frames_->find(2)->second); } TEST_F(QuicStreamSequencerTest, NextxFrameNotConsumed) { EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0)); - EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); - EXPECT_EQ(1u, sequencer_->frames()->size()); + EXPECT_TRUE(OnFrame(0, "abc")); + EXPECT_EQ(1u, buffered_frames_->size()); EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); - EXPECT_EQ("abc", sequencer_->frames()->find(0)->second); + EXPECT_EQ("abc", buffered_frames_->find(0)->second); } TEST_F(QuicStreamSequencerTest, FutureFrameNotProcessed) { - EXPECT_TRUE(sequencer_->OnFrame(3, "abc")); - EXPECT_EQ(1u, sequencer_->frames()->size()); + EXPECT_TRUE(OnFrame(3, "abc")); + EXPECT_EQ(1u, buffered_frames_->size()); EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); - EXPECT_EQ("abc", sequencer_->frames()->find(3)->second); + EXPECT_EQ("abc", buffered_frames_->find(3)->second); } TEST_F(QuicStreamSequencerTest, OutOfOrderFrameProcessed) { // Buffer the first - EXPECT_TRUE(sequencer_->OnFrame(6, "ghi")); - EXPECT_EQ(1u, sequencer_->frames()->size()); + EXPECT_TRUE(OnFrame(6, "ghi")); + EXPECT_EQ(1u, buffered_frames_->size()); EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); + EXPECT_EQ(3u, sequencer_->num_bytes_buffered()); // Buffer the second - EXPECT_TRUE(sequencer_->OnFrame(3, "def")); - EXPECT_EQ(2u, sequencer_->frames()->size()); + EXPECT_TRUE(OnFrame(3, "def")); + EXPECT_EQ(2u, buffered_frames_->size()); EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); + EXPECT_EQ(6u, sequencer_->num_bytes_buffered()); InSequence s; EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3)); @@ -244,190 +252,11 @@ TEST_F(QuicStreamSequencerTest, OutOfOrderFrameProcessed) { EXPECT_CALL(stream_, ProcessRawData(StrEq("ghi"), 3)).WillOnce(Return(3)); // Ack right away - EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + EXPECT_TRUE(OnFrame(0, "abc")); EXPECT_EQ(9u, sequencer_->num_bytes_consumed()); + EXPECT_EQ(0u, sequencer_->num_bytes_buffered()); - EXPECT_EQ(0u, sequencer_->frames()->size()); -} - -TEST_F(QuicStreamSequencerTest, OutOfOrderFramesProcessedWithBuffering) { - sequencer_->SetMemoryLimit(9); - - // Too far to buffer. - EXPECT_FALSE(sequencer_->OnFrame(9, "jkl")); - - // We can afford to buffer this. - EXPECT_TRUE(sequencer_->OnFrame(6, "ghi")); - EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); - - InSequence s; - EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3)); - - // Ack right away - EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); - EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); - - // We should be willing to buffer this now. - EXPECT_TRUE(sequencer_->OnFrame(9, "jkl")); - EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); - - EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(3)); - EXPECT_CALL(stream_, ProcessRawData(StrEq("ghi"), 3)).WillOnce(Return(3)); - EXPECT_CALL(stream_, ProcessRawData(StrEq("jkl"), 3)).WillOnce(Return(3)); - - EXPECT_TRUE(sequencer_->OnFrame(3, "def")); - EXPECT_EQ(12u, sequencer_->num_bytes_consumed()); - EXPECT_EQ(0u, sequencer_->frames()->size()); -} - -TEST_F(QuicStreamSequencerTest, OutOfOrderFramesBlockignWithReadv) { - sequencer_->SetMemoryLimit(9); - char buffer[20]; - iovec iov[2]; - iov[0].iov_base = &buffer[0]; - iov[0].iov_len = 1; - iov[1].iov_base = &buffer[1]; - iov[1].iov_len = 2; - - // Push abc - process. - // Push jkl - buffer (not next data) - // Push def - don't process. - // Push mno - drop (too far out) - // Push ghi - buffer (def not processed) - // Read 2. - // Push mno - buffer (not all read) - // Read all - // Push pqr - process - - InSequence s; - EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3)); - EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(0)); - EXPECT_CALL(stream_, ProcessRawData(StrEq("pqr"), 3)).WillOnce(Return(3)); - - EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); - EXPECT_TRUE(sequencer_->OnFrame(3, "def")); - EXPECT_TRUE(sequencer_->OnFrame(9, "jkl")); - EXPECT_FALSE(sequencer_->OnFrame(12, "mno")); - EXPECT_TRUE(sequencer_->OnFrame(6, "ghi")); - - // Read 3 bytes. - EXPECT_EQ(3, sequencer_->Readv(iov, 2)); - EXPECT_EQ(0, strncmp(buffer, "def", 3)); - - // Now we have space to bufer this. - EXPECT_TRUE(sequencer_->OnFrame(12, "mno")); - - // Read the remaining 9 bytes. - iov[1].iov_len = 19; - EXPECT_EQ(9, sequencer_->Readv(iov, 2)); - EXPECT_EQ(0, strncmp(buffer, "ghijklmno", 9)); - - EXPECT_TRUE(sequencer_->OnFrame(15, "pqr")); -} - -// Same as above, just using a different method for reading. -TEST_F(QuicStreamSequencerTest, OutOfOrderFramesBlockignWithGetReadableRegion) { - sequencer_->SetMemoryLimit(9); - - InSequence s; - EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3)); - EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(0)); - EXPECT_CALL(stream_, ProcessRawData(StrEq("pqr"), 3)).WillOnce(Return(3)); - - EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); - EXPECT_TRUE(sequencer_->OnFrame(3, "def")); - EXPECT_TRUE(sequencer_->OnFrame(9, "jkl")); - EXPECT_FALSE(sequencer_->OnFrame(12, "mno")); - EXPECT_TRUE(sequencer_->OnFrame(6, "ghi")); - - // Read 3 bytes. - const char* expected[] = {"def", "ghi", "jkl"}; - ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected))); - char buffer[9]; - iovec read_iov = { &buffer[0], 3 }; - ASSERT_EQ(3, sequencer_->Readv(&read_iov, 1)); - - // Now we have space to bufer this. - EXPECT_TRUE(sequencer_->OnFrame(12, "mno")); - - // Read the remaining 9 bytes. - const char* expected2[] = {"ghi", "jkl", "mno"}; - ASSERT_TRUE(VerifyReadableRegions(expected2, arraysize(expected2))); - read_iov.iov_len = 9; - ASSERT_EQ(9, sequencer_->Readv(&read_iov, 1)); - - EXPECT_TRUE(sequencer_->OnFrame(15, "pqr")); -} - -// Same as above, just using a different method for reading. -TEST_F(QuicStreamSequencerTest, MarkConsumed) { - sequencer_->SetMemoryLimit(9); - - InSequence s; - EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0)); - - EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); - EXPECT_TRUE(sequencer_->OnFrame(3, "def")); - EXPECT_TRUE(sequencer_->OnFrame(6, "ghi")); - - // Peek into the data. - const char* expected[] = {"abc", "def", "ghi"}; - ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected))); - - // Consume 1 byte. - sequencer_->MarkConsumed(1); - // Verify data. - const char* expected2[] = {"bc", "def", "ghi"}; - ASSERT_TRUE(VerifyReadableRegions(expected2, arraysize(expected2))); - - // Consume 2 bytes. - sequencer_->MarkConsumed(2); - // Verify data. - const char* expected3[] = {"def", "ghi"}; - ASSERT_TRUE(VerifyReadableRegions(expected3, arraysize(expected3))); - - // Consume 5 bytes. - sequencer_->MarkConsumed(5); - // Verify data. - const char* expected4[] = {"i"}; - ASSERT_TRUE(VerifyReadableRegions(expected4, arraysize(expected4))); -} - -TEST_F(QuicStreamSequencerTest, MarkConsumedError) { - // TODO(rch): enable when chromium supports EXPECT_DFATAL. - /* - EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0)); - - EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); - EXPECT_TRUE(sequencer_->OnFrame(9, "jklmnopqrstuvwxyz")); - - // Peek into the data. Only the first chunk should be readable - // because of the missing data. - const char* expected[] = {"abc"}; - ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected))); - - // Now, attempt to mark consumed more data than was readable - // and expect the stream to be closed. - EXPECT_CALL(stream_, Reset(QUIC_ERROR_PROCESSING_STREAM)); - EXPECT_DFATAL(sequencer_->MarkConsumed(4), - "Invalid argument to MarkConsumed. num_bytes_consumed_: 3 " - "end_offset: 4 offset: 9 length: 17"); - */ -} - -TEST_F(QuicStreamSequencerTest, MarkConsumedWithMissingPacket) { - InSequence s; - EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0)); - - EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); - EXPECT_TRUE(sequencer_->OnFrame(3, "def")); - // Missing packet: 6, ghi - EXPECT_TRUE(sequencer_->OnFrame(9, "jkl")); - - const char* expected[] = {"abc", "def"}; - ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected))); - - sequencer_->MarkConsumed(6); + EXPECT_EQ(0u, buffered_frames_->size()); } TEST_F(QuicStreamSequencerTest, BasicHalfCloseOrdered) { @@ -435,64 +264,64 @@ TEST_F(QuicStreamSequencerTest, BasicHalfCloseOrdered) { EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3)); EXPECT_CALL(stream_, OnFinRead()); - EXPECT_TRUE(sequencer_->OnFinFrame(0, "abc")); + EXPECT_TRUE(OnFinFrame(0, "abc")); - EXPECT_EQ(3u, sequencer_->close_offset()); + EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); } TEST_F(QuicStreamSequencerTest, BasicHalfCloseUnorderedWithFlush) { - sequencer_->OnFinFrame(6, ""); - EXPECT_EQ(6u, sequencer_->close_offset()); + OnFinFrame(6, ""); + EXPECT_EQ(6u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); InSequence s; EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3)); EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(3)); EXPECT_CALL(stream_, OnFinRead()); - EXPECT_TRUE(sequencer_->OnFrame(3, "def")); - EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + EXPECT_TRUE(OnFrame(3, "def")); + EXPECT_TRUE(OnFrame(0, "abc")); } TEST_F(QuicStreamSequencerTest, BasicHalfUnordered) { - sequencer_->OnFinFrame(3, ""); - EXPECT_EQ(3u, sequencer_->close_offset()); + OnFinFrame(3, ""); + EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); InSequence s; EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3)); EXPECT_CALL(stream_, OnFinRead()); - EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + EXPECT_TRUE(OnFrame(0, "abc")); } TEST_F(QuicStreamSequencerTest, TerminateWithReadv) { char buffer[3]; - sequencer_->OnFinFrame(3, ""); - EXPECT_EQ(3u, sequencer_->close_offset()); + OnFinFrame(3, ""); + EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); EXPECT_FALSE(sequencer_->IsClosed()); EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0)); - EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + EXPECT_TRUE(OnFrame(0, "abc")); - iovec iov = { &buffer[0], 3 }; + iovec iov = {&buffer[0], 3}; int bytes_read = sequencer_->Readv(&iov, 1); EXPECT_EQ(3, bytes_read); EXPECT_TRUE(sequencer_->IsClosed()); } TEST_F(QuicStreamSequencerTest, MutipleOffsets) { - sequencer_->OnFinFrame(3, ""); - EXPECT_EQ(3u, sequencer_->close_offset()); + OnFinFrame(3, ""); + EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); EXPECT_CALL(stream_, Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS)); - sequencer_->OnFinFrame(5, ""); - EXPECT_EQ(3u, sequencer_->close_offset()); + OnFinFrame(5, ""); + EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); EXPECT_CALL(stream_, Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS)); - sequencer_->OnFinFrame(1, ""); - EXPECT_EQ(3u, sequencer_->close_offset()); + OnFinFrame(1, ""); + EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); - sequencer_->OnFinFrame(3, ""); - EXPECT_EQ(3u, sequencer_->close_offset()); + OnFinFrame(3, ""); + EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get())); } class QuicSequencerRandomTest : public QuicStreamSequencerTest { @@ -544,38 +373,71 @@ TEST_F(QuicSequencerRandomTest, RandomFramesNoDroppingNoBackup) { while (!list_.empty()) { int index = OneToN(list_.size()) - 1; - LOG(ERROR) << "Sending index " << index << " " - << list_[index].second.data(); - EXPECT_TRUE(sequencer_->OnFrame(list_[index].first, - list_[index].second.data())); + LOG(ERROR) << "Sending index " << index << " " << list_[index].second; + EXPECT_TRUE(OnFrame(list_[index].first, list_[index].second.data())); list_.erase(list_.begin() + index); } } -// All frames are processed as soon as we have sequential data. -// Buffering, so some frames are rejected. -TEST_F(QuicSequencerRandomTest, RandomFramesDroppingNoBackup) { - sequencer_->SetMemoryLimit(26); +TEST_F(QuicStreamSequencerTest, FrameOverlapsBufferedData) { + // Ensure that FrameOverlapsBufferedData returns appropriate responses when + // there is existing data buffered. + + map<QuicStreamOffset, string>* buffered_frames = + QuicStreamSequencerPeer::GetBufferedFrames(sequencer_.get()); + + const int kBufferedOffset = 10; + const int kBufferedDataLength = 3; + const int kNewDataLength = 3; + IOVector data = MakeIOVector(string(kNewDataLength, '.')); + + // No overlap if no buffered frames. + EXPECT_TRUE(buffered_frames_->empty()); + EXPECT_FALSE(sequencer_->FrameOverlapsBufferedData( + QuicStreamFrame(1, false, kBufferedOffset - 1, data))); + + // Add a buffered frame. + buffered_frames->insert( + make_pair(kBufferedOffset, string(kBufferedDataLength, '.'))); + + // New byte range partially overlaps with buffered frame, start offset + // preceeding buffered frame. + EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData( + QuicStreamFrame(1, false, kBufferedOffset - 1, data))); + EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData( + QuicStreamFrame(1, false, kBufferedOffset - kNewDataLength + 1, data))); + + // New byte range partially overlaps with buffered frame, start offset + // inside existing buffered frame. + EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData( + QuicStreamFrame(1, false, kBufferedOffset + 1, data))); + EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(QuicStreamFrame( + 1, false, kBufferedOffset + kBufferedDataLength - 1, data))); + + // New byte range entirely outside of buffered frames, start offset preceeding + // buffered frame. + EXPECT_FALSE(sequencer_->FrameOverlapsBufferedData( + QuicStreamFrame(1, false, kBufferedOffset - kNewDataLength, data))); + + // New byte range entirely outside of buffered frames, start offset later than + // buffered frame. + EXPECT_FALSE(sequencer_->FrameOverlapsBufferedData(QuicStreamFrame( + 1, false, kBufferedOffset + kBufferedDataLength, data))); +} - InSequence s; - for (size_t i = 0; i < list_.size(); ++i) { - string* data = &list_[i].second; - EXPECT_CALL(stream_, ProcessRawData(StrEq(*data), data->size())) - .WillOnce(Return(data->size())); - } +TEST_F(QuicStreamSequencerTest, DontAcceptOverlappingFrames) { + // The peer should never send us non-identical stream frames which contain + // overlapping byte ranges - if they do, we close the connection. - while (!list_.empty()) { - int index = OneToN(list_.size()) - 1; - LOG(ERROR) << "Sending index " << index << " " - << list_[index].second.data(); - bool acked = sequencer_->OnFrame(list_[index].first, - list_[index].second.data()); + QuicStreamFrame frame1(kClientDataStreamId1, false, 1, MakeIOVector("hello")); + sequencer_->OnStreamFrame(frame1); - if (acked) { - list_.erase(list_.begin() + index); - } - } + QuicStreamFrame frame2(kClientDataStreamId1, false, 2, MakeIOVector("hello")); + EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(frame2)); + EXPECT_CALL(stream_, CloseConnectionWithDetails(QUIC_INVALID_STREAM_FRAME, _)) + .Times(1); + sequencer_->OnStreamFrame(frame2); } } // namespace diff --git a/chromium/net/quic/quic_time.cc b/chromium/net/quic/quic_time.cc index a56775663e4..d467980c43d 100644 --- a/chromium/net/quic/quic_time.cc +++ b/chromium/net/quic/quic_time.cc @@ -62,6 +62,20 @@ QuicTime::Delta QuicTime::Delta::Subtract(const Delta& delta) const { delta.ToMicroseconds()); } +QuicTime::Delta QuicTime::Delta::Multiply(int i) const { + return QuicTime::Delta::FromMicroseconds(ToMicroseconds() * i); +} + +QuicTime::Delta QuicTime::Delta::Multiply(double d) const { + return QuicTime::Delta::FromMicroseconds(ToMicroseconds() * d); +} + +// static +QuicTime::Delta QuicTime::Delta::Max(QuicTime::Delta delta1, + QuicTime::Delta delta2) { + return delta1 < delta2 ? delta2 : delta1; +} + bool QuicTime::Delta::IsZero() const { return delta_.InMicroseconds() == 0; } @@ -75,6 +89,11 @@ QuicTime QuicTime::Zero() { return QuicTime(base::TimeTicks()); } +// static +QuicTime QuicTime::Max(QuicTime time1, QuicTime time2) { + return time1 > time2 ? time1 : time2; +} + QuicTime::QuicTime(base::TimeTicks ticks) : ticks_(ticks) { } diff --git a/chromium/net/quic/quic_time.h b/chromium/net/quic/quic_time.h index cdb7f83e81e..20cc3088f72 100644 --- a/chromium/net/quic/quic_time.h +++ b/chromium/net/quic/quic_time.h @@ -58,6 +58,12 @@ class NET_EXPORT_PRIVATE QuicTime { Delta Subtract(const Delta& delta) const; + Delta Multiply(int i) const; + Delta Multiply(double d) const; + + // Returns the later delta of time1 and time2. + static Delta Max(Delta delta1, Delta delta2); + bool IsZero() const; bool IsInfinite() const; @@ -75,6 +81,9 @@ class NET_EXPORT_PRIVATE QuicTime { // will return false for these times. static QuicTime Zero(); + // Returns the later time of time1 and time2. + static QuicTime Max(QuicTime time1, QuicTime time2); + // Produce the internal value to be used when logging. This value // represents the number of microseconds since some epoch. It may // be the UNIX epoch on some platforms. On others, it may diff --git a/chromium/net/quic/quic_time_test.cc b/chromium/net/quic/quic_time_test.cc index 18b1de4f3d3..a2e355050c7 100644 --- a/chromium/net/quic/quic_time_test.cc +++ b/chromium/net/quic/quic_time_test.cc @@ -48,6 +48,21 @@ TEST(QuicTimeDeltaTest, Subtract) { QuicTime::Delta::FromMilliseconds(1))); } +TEST(QuicTimeDeltaTest, Multiply) { + int i = 2; + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(4000), + QuicTime::Delta::FromMilliseconds(2).Multiply(i)); + double d = 2; + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(4000), + QuicTime::Delta::FromMilliseconds(2).Multiply(d)); +} + +TEST(QuicTimeDeltaTest, Max) { + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2000), + QuicTime::Delta::Max(QuicTime::Delta::FromMicroseconds(1000), + QuicTime::Delta::FromMicroseconds(2000))); +} + TEST(QuicTimeDeltaTest, NotEqual) { EXPECT_TRUE(QuicTime::Delta::FromSeconds(0) != QuicTime::Delta::FromSeconds(1)); @@ -95,6 +110,15 @@ TEST_F(QuicTimeTest, SubtractDelta) { time.Subtract(QuicTime::Delta::FromMilliseconds(1))); } +TEST_F(QuicTimeTest, Max) { + QuicTime time_1 = QuicTime::Zero().Add( + QuicTime::Delta::FromMilliseconds(1)); + QuicTime time_2 = QuicTime::Zero().Add( + QuicTime::Delta::FromMilliseconds(2)); + + EXPECT_EQ(time_2, QuicTime::Max(time_1, time_2)); +} + TEST_F(QuicTimeTest, MockClock) { clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); diff --git a/chromium/net/quic/quic_time_wait_list_manager.cc b/chromium/net/quic/quic_time_wait_list_manager.cc new file mode 100644 index 00000000000..d1f3419e77d --- /dev/null +++ b/chromium/net/quic/quic_time_wait_list_manager.cc @@ -0,0 +1,283 @@ +// 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/quic_time_wait_list_manager.h" + +#include <errno.h> + +#include "base/containers/hash_tables.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" +#include "net/base/ip_endpoint.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/crypto/quic_decrypter.h" +#include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/quic_clock.h" +#include "net/quic/quic_connection_helper.h" +#include "net/quic/quic_framer.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_server_session.h" +#include "net/quic/quic_utils.h" + +using base::StringPiece; +using std::make_pair; + +namespace net { + +namespace { + +// Time period for which the connection_id should live in time wait state.. +const int kTimeWaitSeconds = 5; + +} // namespace + +// A very simple alarm that just informs the QuicTimeWaitListManager to clean +// up old connection_ids. This alarm should be unregistered and deleted before +// the QuicTimeWaitListManager is deleted. +class ConnectionIdCleanUpAlarm : public QuicAlarm::Delegate { + public: + explicit ConnectionIdCleanUpAlarm( + QuicTimeWaitListManager* time_wait_list_manager) + : time_wait_list_manager_(time_wait_list_manager) {} + + virtual QuicTime OnAlarm() OVERRIDE { + time_wait_list_manager_->CleanUpOldConnectionIds(); + // Let the time wait manager register the alarm at appropriate time. + return QuicTime::Zero(); + } + + private: + // Not owned. + QuicTimeWaitListManager* time_wait_list_manager_; +}; + +// This class stores pending public reset packets to be sent to clients. +// server_address - server address on which a packet what was received for +// a connection_id in time wait state. +// client_address - address of the client that sent that packet. Needed to send +// the public reset packet back to the client. +// packet - the pending public reset packet that is to be sent to the client. +// created instance takes the ownership of this packet. +class QuicTimeWaitListManager::QueuedPacket { + public: + QueuedPacket(const IPEndPoint& server_address, + const IPEndPoint& client_address, + QuicEncryptedPacket* packet) + : server_address_(server_address), + client_address_(client_address), + packet_(packet) {} + + const IPEndPoint& server_address() const { return server_address_; } + const IPEndPoint& client_address() const { return client_address_; } + QuicEncryptedPacket* packet() { return packet_.get(); } + + private: + const IPEndPoint server_address_; + const IPEndPoint client_address_; + scoped_ptr<QuicEncryptedPacket> packet_; + + DISALLOW_COPY_AND_ASSIGN(QueuedPacket); +}; + +QuicTimeWaitListManager::QuicTimeWaitListManager( + QuicPacketWriter* writer, + QuicServerSessionVisitor* visitor, + QuicConnectionHelperInterface* helper, + const QuicVersionVector& supported_versions) + : helper_(helper), + kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)), + connection_id_clean_up_alarm_( + helper_->CreateAlarm(new ConnectionIdCleanUpAlarm(this))), + writer_(writer), + visitor_(visitor) { + SetConnectionIdCleanUpAlarm(); +} + +QuicTimeWaitListManager::~QuicTimeWaitListManager() { + connection_id_clean_up_alarm_->Cancel(); + STLDeleteElements(&pending_packets_queue_); + for (ConnectionIdMap::iterator it = connection_id_map_.begin(); + it != connection_id_map_.end(); + ++it) { + delete it->second.close_packet; + } +} + +void QuicTimeWaitListManager::AddConnectionIdToTimeWait( + QuicConnectionId connection_id, + QuicVersion version, + QuicEncryptedPacket* close_packet) { + DVLOG(1) << "Adding " << connection_id << " to the time wait list."; + int num_packets = 0; + ConnectionIdMap::iterator it = connection_id_map_.find(connection_id); + if (it != connection_id_map_.end()) { // Replace record if it is reinserted. + num_packets = it->second.num_packets; + delete it->second.close_packet; + connection_id_map_.erase(it); + } + ConnectionIdData data(num_packets, + version, + helper_->GetClock()->ApproximateNow(), + close_packet); + connection_id_map_.insert(make_pair(connection_id, data)); +} + +bool QuicTimeWaitListManager::IsConnectionIdInTimeWait( + QuicConnectionId connection_id) const { + return ContainsKey(connection_id_map_, connection_id); +} + +QuicVersion QuicTimeWaitListManager::GetQuicVersionFromConnectionId( + QuicConnectionId connection_id) { + ConnectionIdMap::iterator it = connection_id_map_.find(connection_id); + DCHECK(it != connection_id_map_.end()); + return (it->second).version; +} + +void QuicTimeWaitListManager::OnCanWrite() { + while (!pending_packets_queue_.empty()) { + QueuedPacket* queued_packet = pending_packets_queue_.front(); + if (!WriteToWire(queued_packet)) { + return; + } + pending_packets_queue_.pop_front(); + delete queued_packet; + } +} + +void QuicTimeWaitListManager::ProcessPacket( + const IPEndPoint& server_address, + const IPEndPoint& client_address, + QuicConnectionId connection_id, + QuicPacketSequenceNumber sequence_number, + const QuicEncryptedPacket& /*packet*/) { + DCHECK(IsConnectionIdInTimeWait(connection_id)); + DVLOG(1) << "Processing " << connection_id << " in time wait state."; + // TODO(satyamshekhar): Think about handling packets from different client + // addresses. + ConnectionIdMap::iterator it = connection_id_map_.find(connection_id); + DCHECK(it != connection_id_map_.end()); + // Increment the received packet count. + ++((it->second).num_packets); + if (!ShouldSendResponse((it->second).num_packets)) { + return; + } + if (it->second.close_packet) { + QueuedPacket* queued_packet = + new QueuedPacket(server_address, + client_address, + it->second.close_packet->Clone()); + // Takes ownership of the packet. + SendOrQueuePacket(queued_packet); + } else { + SendPublicReset(server_address, + client_address, + connection_id, + sequence_number); + } +} + +// Returns true if the number of packets received for this connection_id is a +// power of 2 to throttle the number of public reset packets we send to a +// client. +bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) { + return (received_packet_count & (received_packet_count - 1)) == 0; +} + +void QuicTimeWaitListManager::SendPublicReset( + const IPEndPoint& server_address, + const IPEndPoint& client_address, + QuicConnectionId connection_id, + QuicPacketSequenceNumber rejected_sequence_number) { + QuicPublicResetPacket packet; + packet.public_header.connection_id = connection_id; + packet.public_header.reset_flag = true; + packet.public_header.version_flag = false; + packet.rejected_sequence_number = rejected_sequence_number; + // TODO(satyamshekhar): generate a valid nonce for this connection_id. + packet.nonce_proof = 1010101; + packet.client_address = client_address; + QueuedPacket* queued_packet = new QueuedPacket( + server_address, + client_address, + BuildPublicReset(packet)); + // Takes ownership of the packet. + SendOrQueuePacket(queued_packet); +} + +QuicEncryptedPacket* QuicTimeWaitListManager::BuildPublicReset( + const QuicPublicResetPacket& packet) { + return QuicFramer::BuildPublicResetPacket(packet); +} + +// Either sends the packet and deletes it or makes pending queue the +// owner of the packet. +void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) { + if (WriteToWire(packet)) { + delete packet; + } else { + // pending_packets_queue takes the ownership of the queued packet. + pending_packets_queue_.push_back(packet); + } +} + +bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) { + if (writer_->IsWriteBlocked()) { + visitor_->OnWriteBlocked(this); + return false; + } + WriteResult result = writer_->WritePacket( + queued_packet->packet()->data(), + queued_packet->packet()->length(), + queued_packet->server_address().address(), + queued_packet->client_address()); + if (result.status == WRITE_STATUS_BLOCKED) { + // If blocked and unbuffered, return false to retry sending. + DCHECK(writer_->IsWriteBlocked()); + visitor_->OnWriteBlocked(this); + return writer_->IsWriteBlockedDataBuffered(); + } else if (result.status == WRITE_STATUS_ERROR) { + LOG(WARNING) << "Received unknown error while sending reset packet to " + << queued_packet->client_address().ToString() << ": " + << strerror(result.error_code); + } + return true; +} + +void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() { + connection_id_clean_up_alarm_->Cancel(); + QuicTime now = helper_->GetClock()->ApproximateNow(); + QuicTime next_alarm_time = now; + if (!connection_id_map_.empty()) { + QuicTime oldest_connection_id = + connection_id_map_.begin()->second.time_added; + if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) { + next_alarm_time = oldest_connection_id.Add(kTimeWaitPeriod_); + } else { + LOG(ERROR) << "ConnectionId lingered for longer than kTimeWaitPeriod"; + } + } else { + // No connection_ids added so none will expire before kTimeWaitPeriod_. + next_alarm_time = now.Add(kTimeWaitPeriod_); + } + + connection_id_clean_up_alarm_->Set(next_alarm_time); +} + +void QuicTimeWaitListManager::CleanUpOldConnectionIds() { + QuicTime now = helper_->GetClock()->ApproximateNow(); + while (!connection_id_map_.empty()) { + ConnectionIdMap::iterator it = connection_id_map_.begin(); + QuicTime oldest_connection_id = it->second.time_added; + if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) { + break; + } + // This connection_id has lived its age, retire it now. + delete it->second.close_packet; + connection_id_map_.erase(it); + } + SetConnectionIdCleanUpAlarm(); +} + +} // namespace net diff --git a/chromium/net/quic/quic_time_wait_list_manager.h b/chromium/net/quic/quic_time_wait_list_manager.h new file mode 100644 index 00000000000..4a5fd42b0a3 --- /dev/null +++ b/chromium/net/quic/quic_time_wait_list_manager.h @@ -0,0 +1,174 @@ +// 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. +// +// Handles packets for connection_ids in time wait state by discarding the +// packet and sending the clients a public reset packet with exponential +// backoff. + +#ifndef NET_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_ +#define NET_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_ + +#include <deque> + +#include "base/basictypes.h" +#include "base/containers/hash_tables.h" +#include "base/strings/string_piece.h" +#include "net/base/linked_hash_map.h" +#include "net/quic/quic_blocked_writer_interface.h" +#include "net/quic/quic_connection_helper.h" +#include "net/quic/quic_framer.h" +#include "net/quic/quic_packet_writer.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +class ConnectionIdCleanUpAlarm; +class QuicServerSessionVisitor; + +namespace test { +class QuicTimeWaitListManagerPeer; +} // namespace test + +// Maintains a list of all connection_ids that have been recently closed. A +// connection_id lives in this state for kTimeWaitPeriod. All packets received +// for connection_ids in this state are handed over to the +// QuicTimeWaitListManager by the QuicDispatcher. Decides whether to send a +// public reset packet, a copy of the previously sent connection close packet, +// or nothing to the client which sent a packet with the connection_id in time +// wait state. After the connection_id expires its time wait period, a new +// connection/session will be created if a packet is received for this +// connection_id. +class QuicTimeWaitListManager : public QuicBlockedWriterInterface { + public: + // writer - the entity that writes to the socket. (Owned by the dispatcher) + // visitor - the entity that manages blocked writers. (The dispatcher) + // helper - used to run clean up alarms. (Owned by the owner of the server) + QuicTimeWaitListManager(QuicPacketWriter* writer, + QuicServerSessionVisitor* visitor, + QuicConnectionHelperInterface* helper, + const QuicVersionVector& supported_versions); + virtual ~QuicTimeWaitListManager(); + + // Adds the given connection_id to time wait state for kTimeWaitPeriod. + // Henceforth, any packet bearing this connection_id should not be processed + // while the connection_id remains in this list. If a non-NULL |close_packet| + // is provided, it is sent again when packets are received for added + // connection_ids. If NULL, a public reset packet is sent with the specified + // |version|. DCHECKs that connection_id is not already on the list. + void AddConnectionIdToTimeWait(QuicConnectionId connection_id, + QuicVersion version, + QuicEncryptedPacket* close_packet); // Owned. + + // Returns true if the connection_id is in time wait state, false otherwise. + // Packets received for this connection_id should not lead to creation of new + // QuicSessions. + bool IsConnectionIdInTimeWait(QuicConnectionId connection_id) const; + + // Called when a packet is received for a connection_id that is in time wait + // state. Sends a public reset packet to the client which sent this + // connection_id. Sending of the public reset packet is throttled by using + // exponential back off. DCHECKs for the connection_id to be in time wait + // state. virtual to override in tests. + virtual void ProcessPacket(const IPEndPoint& server_address, + const IPEndPoint& client_address, + QuicConnectionId connection_id, + QuicPacketSequenceNumber sequence_number, + const QuicEncryptedPacket& packet); + + // Called by the dispatcher when the underlying socket becomes writable again, + // since we might need to send pending public reset packets which we didn't + // send because the underlying socket was write blocked. + virtual void OnCanWrite() OVERRIDE; + + // Used to delete connection_id entries that have outlived their time wait + // period. + void CleanUpOldConnectionIds(); + + // Given a ConnectionId that exists in the time wait list, returns the + // QuicVersion associated with it. + QuicVersion GetQuicVersionFromConnectionId(QuicConnectionId connection_id); + + protected: + virtual QuicEncryptedPacket* BuildPublicReset( + const QuicPublicResetPacket& packet); + + private: + friend class test::QuicTimeWaitListManagerPeer; + + // Internal structure to store pending public reset packets. + class QueuedPacket; + + // Decides if a packet should be sent for this connection_id based on the + // number of received packets. + bool ShouldSendResponse(int received_packet_count); + + // Creates a public reset packet and sends it or queues it to be sent later. + void SendPublicReset(const IPEndPoint& server_address, + const IPEndPoint& client_address, + QuicConnectionId connection_id, + QuicPacketSequenceNumber rejected_sequence_number); + + // Either sends the packet and deletes it or makes pending_packets_queue_ the + // owner of the packet. + void SendOrQueuePacket(QueuedPacket* packet); + + // Sends the packet out. Returns true if the packet was successfully consumed. + // If the writer got blocked and did not buffer the packet, we'll need to keep + // the packet and retry sending. In case of all other errors we drop the + // packet. + bool WriteToWire(QueuedPacket* packet); + + // Register the alarm to wake up at appropriate time. + void SetConnectionIdCleanUpAlarm(); + + // A map from a recently closed connection_id to the number of packets + // received after the termination of the connection bound to the + // connection_id. + struct ConnectionIdData { + ConnectionIdData(int num_packets_, + QuicVersion version_, + QuicTime time_added_, + QuicEncryptedPacket* close_packet) + : num_packets(num_packets_), + version(version_), + time_added(time_added_), + close_packet(close_packet) {} + int num_packets; + QuicVersion version; + QuicTime time_added; + QuicEncryptedPacket* close_packet; + }; + + // linked_hash_map allows lookup by ConnectionId and traversal in add order. + typedef linked_hash_map<QuicConnectionId, ConnectionIdData> ConnectionIdMap; + ConnectionIdMap connection_id_map_; + + // Pending public reset packets that need to be sent out to the client + // when we are given a chance to write by the dispatcher. + std::deque<QueuedPacket*> pending_packets_queue_; + + // Used to schedule alarms to delete old connection_ids which have been in the + // list for too long. + QuicConnectionHelperInterface* helper_; + + // Time period for which connection_ids should remain in time wait state. + const QuicTime::Delta kTimeWaitPeriod_; + + // Alarm registered with the connection helper to clean up connection_ids that + // have + // out lived their duration in time wait state. + scoped_ptr<QuicAlarm> connection_id_clean_up_alarm_; + + // Interface that writes given buffer to the socket. + QuicPacketWriter* writer_; + + // Interface that manages blocked writers. + QuicServerSessionVisitor* visitor_; + + DISALLOW_COPY_AND_ASSIGN(QuicTimeWaitListManager); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_ diff --git a/chromium/net/quic/quic_types.cc b/chromium/net/quic/quic_types.cc new file mode 100644 index 00000000000..cdfb36dbccb --- /dev/null +++ b/chromium/net/quic/quic_types.cc @@ -0,0 +1,34 @@ +// 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/quic_types.h" + +using std::ostream; + +namespace net { + +QuicConsumedData::QuicConsumedData(size_t bytes_consumed, + bool fin_consumed) + : bytes_consumed(bytes_consumed), + fin_consumed(fin_consumed) { +} + +ostream& operator<<(ostream& os, const QuicConsumedData& s) { + os << "bytes_consumed: " << s.bytes_consumed + << " fin_consumed: " << s.fin_consumed; + return os; +} + +WriteResult::WriteResult() + : status(WRITE_STATUS_ERROR), + bytes_written(0) { +} + +WriteResult::WriteResult(WriteStatus status, + int bytes_written_or_error_code) + : status(status), + bytes_written(bytes_written_or_error_code) { +} + +} // namespace net diff --git a/chromium/net/quic/quic_types.h b/chromium/net/quic/quic_types.h new file mode 100644 index 00000000000..01415bc3607 --- /dev/null +++ b/chromium/net/quic/quic_types.h @@ -0,0 +1,69 @@ +// 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_QUIC_TYPES_H_ +#define NET_QUIC_QUIC_TYPES_H_ + +// This header defines some basic types that don't depend on quic_protocol.h, +// so that classes not directly related to the protocol wire format can avoid +// including quic_protocol.h. + +#include <stddef.h> +#include <ostream> + +#include "net/base/net_export.h" + +namespace net { + +// A struct for functions which consume data payloads and fins. +struct NET_EXPORT_PRIVATE QuicConsumedData { + QuicConsumedData(size_t bytes_consumed, bool fin_consumed); + + // By default, gtest prints the raw bytes of an object. The bool data + // member causes this object to have padding bytes, which causes the + // default gtest object printer to read uninitialize memory. So we need + // to teach gtest how to print this object. + NET_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, const QuicConsumedData& s); + + // How many bytes were consumed. + size_t bytes_consumed; + + // True if an incoming fin was consumed. + bool fin_consumed; +}; + +// QuicAsyncStatus enumerates the possible results of an asynchronous +// operation. +enum QuicAsyncStatus { + QUIC_SUCCESS = 0, + QUIC_FAILURE = 1, + // QUIC_PENDING results from an operation that will occur asynchonously. When + // the operation is complete, a callback's |Run| method will be called. + QUIC_PENDING = 2, +}; + +// TODO(wtc): see if WriteStatus can be replaced by QuicAsyncStatus. +enum WriteStatus { + WRITE_STATUS_OK, + WRITE_STATUS_BLOCKED, + WRITE_STATUS_ERROR, +}; + +// A struct used to return the result of write calls including either the number +// of bytes written or the error code, depending upon the status. +struct NET_EXPORT_PRIVATE WriteResult { + WriteResult(WriteStatus status, int bytes_written_or_error_code); + WriteResult(); + + WriteStatus status; + union { + int bytes_written; // only valid when status is WRITE_STATUS_OK + int error_code; // only valid when status is WRITE_STATUS_ERROR + }; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_TYPES_H_ diff --git a/chromium/net/quic/quic_unacked_packet_map.cc b/chromium/net/quic/quic_unacked_packet_map.cc new file mode 100644 index 00000000000..e9dd1368f6a --- /dev/null +++ b/chromium/net/quic/quic_unacked_packet_map.cc @@ -0,0 +1,323 @@ +// 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/quic_unacked_packet_map.h" + +#include "base/logging.h" +#include "base/stl_util.h" +#include "net/quic/quic_connection_stats.h" +#include "net/quic/quic_utils_chromium.h" + +using std::max; + +namespace net { + +QuicUnackedPacketMap::QuicUnackedPacketMap() + : largest_sent_packet_(0), + largest_observed_(0), + bytes_in_flight_(0), + pending_crypto_packet_count_(0) { +} + +QuicUnackedPacketMap::~QuicUnackedPacketMap() { + for (UnackedPacketMap::iterator it = unacked_packets_.begin(); + it != unacked_packets_.end(); ++it) { + delete it->second.retransmittable_frames; + // Only delete all_transmissions once, for the newest packet. + if (it->first == *it->second.all_transmissions->rbegin()) { + delete it->second.all_transmissions; + } + } +} + +// TODO(ianswett): Combine this method with OnPacketSent once packets are always +// sent in order and the connection tracks RetransmittableFrames for longer. +void QuicUnackedPacketMap::AddPacket( + const SerializedPacket& serialized_packet) { + if (!unacked_packets_.empty()) { + bool is_old_packet = unacked_packets_.rbegin()->first >= + serialized_packet.sequence_number; + LOG_IF(DFATAL, is_old_packet) << "Old packet serialized: " + << serialized_packet.sequence_number + << " vs: " + << unacked_packets_.rbegin()->first; + } + + unacked_packets_[serialized_packet.sequence_number] = + TransmissionInfo(serialized_packet.retransmittable_frames, + serialized_packet.sequence_number, + serialized_packet.sequence_number_length); + if (serialized_packet.retransmittable_frames != NULL && + serialized_packet.retransmittable_frames->HasCryptoHandshake() + == IS_HANDSHAKE) { + ++pending_crypto_packet_count_; + } +} + +void QuicUnackedPacketMap::OnRetransmittedPacket( + QuicPacketSequenceNumber old_sequence_number, + QuicPacketSequenceNumber new_sequence_number, + TransmissionType transmission_type) { + DCHECK(ContainsKey(unacked_packets_, old_sequence_number)); + DCHECK(unacked_packets_.empty() || + unacked_packets_.rbegin()->first < new_sequence_number); + + // TODO(ianswett): Discard and lose the packet lazily instead of immediately. + TransmissionInfo* transmission_info = + FindOrNull(unacked_packets_, old_sequence_number); + RetransmittableFrames* frames = transmission_info->retransmittable_frames; + LOG_IF(DFATAL, frames == NULL) << "Attempt to retransmit packet with no " + << "retransmittable frames: " + << old_sequence_number; + + // We keep the old packet in the unacked packet list until it, or one of + // the retransmissions of it are acked. + transmission_info->retransmittable_frames = NULL; + unacked_packets_[new_sequence_number] = + TransmissionInfo(frames, + new_sequence_number, + transmission_info->sequence_number_length, + transmission_type, + transmission_info->all_transmissions); +} + +void QuicUnackedPacketMap::ClearPreviousRetransmissions(size_t num_to_clear) { + UnackedPacketMap::iterator it = unacked_packets_.begin(); + while (it != unacked_packets_.end() && num_to_clear > 0) { + QuicPacketSequenceNumber sequence_number = it->first; + // If this packet is in flight, or has retransmittable data, then there is + // no point in clearing out any further packets, because they would not + // affect the high water mark. + if (it->second.in_flight || it->second.retransmittable_frames != NULL) { + break; + } + + it->second.all_transmissions->erase(sequence_number); + LOG_IF(DFATAL, it->second.all_transmissions->empty()) + << "Previous retransmissions must have a newer transmission."; + ++it; + unacked_packets_.erase(sequence_number); + --num_to_clear; + } +} + +bool QuicUnackedPacketMap::HasRetransmittableFrames( + QuicPacketSequenceNumber sequence_number) const { + const TransmissionInfo* transmission_info = + FindOrNull(unacked_packets_, sequence_number); + if (transmission_info == NULL) { + return false; + } + + return transmission_info->retransmittable_frames != NULL; +} + +void QuicUnackedPacketMap::NackPacket(QuicPacketSequenceNumber sequence_number, + size_t min_nacks) { + UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number); + if (it == unacked_packets_.end()) { + LOG(DFATAL) << "NackPacket called for packet that is not unacked: " + << sequence_number; + return; + } + + it->second.nack_count = max(min_nacks, it->second.nack_count); +} + +void QuicUnackedPacketMap::RemoveRetransmittability( + QuicPacketSequenceNumber sequence_number) { + UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number); + if (it == unacked_packets_.end()) { + DVLOG(1) << "packet is not in unacked_packets: " << sequence_number; + return; + } + SequenceNumberSet* all_transmissions = it->second.all_transmissions; + // TODO(ianswett): Consider optimizing this for lone packets. + // TODO(ianswett): Consider adding a check to ensure there are retransmittable + // frames associated with this packet. + for (SequenceNumberSet::reverse_iterator it = all_transmissions->rbegin(); + it != all_transmissions->rend(); ++it) { + TransmissionInfo* transmission_info = FindOrNull(unacked_packets_, *it); + if (transmission_info == NULL) { + LOG(DFATAL) << "All transmissions in all_transmissions must be present " + << "in the unacked packet map."; + continue; + } + MaybeRemoveRetransmittableFrames(transmission_info); + if (*it <= largest_observed_ && !transmission_info->in_flight) { + unacked_packets_.erase(*it); + } else { + transmission_info->all_transmissions = new SequenceNumberSet(); + transmission_info->all_transmissions->insert(*it); + } + } + + delete all_transmissions; +} + +void QuicUnackedPacketMap::MaybeRemoveRetransmittableFrames( + TransmissionInfo* transmission_info) { + if (transmission_info->retransmittable_frames != NULL) { + if (transmission_info->retransmittable_frames->HasCryptoHandshake() + == IS_HANDSHAKE) { + --pending_crypto_packet_count_; + } + delete transmission_info->retransmittable_frames; + transmission_info->retransmittable_frames = NULL; + } +} + +void QuicUnackedPacketMap::IncreaseLargestObserved( + QuicPacketSequenceNumber largest_observed) { + DCHECK_LT(largest_observed_, largest_observed); + largest_observed_ = largest_observed; + UnackedPacketMap::iterator it = unacked_packets_.begin(); + while (it != unacked_packets_.end() && it->first <= largest_observed_) { + if (!IsPacketUseless(it)) { + ++it; + continue; + } + delete it->second.all_transmissions; + QuicPacketSequenceNumber sequence_number = it->first; + ++it; + unacked_packets_.erase(sequence_number); + } +} + +bool QuicUnackedPacketMap::IsPacketUseless( + UnackedPacketMap::const_iterator it) const { + return it->first <= largest_observed_ && + !it->second.in_flight && + it->second.retransmittable_frames == NULL && + it->second.all_transmissions->size() == 1; +} + +bool QuicUnackedPacketMap::IsUnacked( + QuicPacketSequenceNumber sequence_number) const { + return ContainsKey(unacked_packets_, sequence_number); +} + +void QuicUnackedPacketMap::RemoveFromInFlight( + QuicPacketSequenceNumber sequence_number) { + UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number); + if (it == unacked_packets_.end()) { + LOG(DFATAL) << "RemoveFromFlight called for packet that is not unacked: " + << sequence_number; + return; + } + if (it->second.in_flight) { + LOG_IF(DFATAL, bytes_in_flight_ < it->second.bytes_sent); + bytes_in_flight_ -= it->second.bytes_sent; + it->second.in_flight = false; + } + if (IsPacketUseless(it)) { + delete it->second.all_transmissions; + unacked_packets_.erase(it); + } +} + +bool QuicUnackedPacketMap::HasUnackedPackets() const { + return !unacked_packets_.empty(); +} + +bool QuicUnackedPacketMap::HasInFlightPackets() const { + return bytes_in_flight_ > 0; +} + +const TransmissionInfo& QuicUnackedPacketMap::GetTransmissionInfo( + QuicPacketSequenceNumber sequence_number) const { + return unacked_packets_.find(sequence_number)->second; +} + +QuicTime QuicUnackedPacketMap::GetLastPacketSentTime() const { + UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin(); + while (it != unacked_packets_.rend()) { + if (it->second.in_flight) { + LOG_IF(DFATAL, it->second.sent_time == QuicTime::Zero()) + << "Sent time can never be zero for a packet in flight."; + return it->second.sent_time; + } + ++it; + } + LOG(DFATAL) << "GetLastPacketSentTime requires in flight packets."; + return QuicTime::Zero(); +} + +QuicTime QuicUnackedPacketMap::GetFirstInFlightPacketSentTime() const { + UnackedPacketMap::const_iterator it = unacked_packets_.begin(); + while (it != unacked_packets_.end() && !it->second.in_flight) { + ++it; + } + if (it == unacked_packets_.end()) { + LOG(DFATAL) << "GetFirstInFlightPacketSentTime requires in flight packets."; + return QuicTime::Zero(); + } + return it->second.sent_time; +} + +size_t QuicUnackedPacketMap::GetNumUnackedPackets() const { + return unacked_packets_.size(); +} + +bool QuicUnackedPacketMap::HasMultipleInFlightPackets() const { + size_t num_in_flight = 0; + for (UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin(); + it != unacked_packets_.rend(); ++it) { + if (it->second.in_flight) { + ++num_in_flight; + } + if (num_in_flight > 1) { + return true; + } + } + return false; +} + +bool QuicUnackedPacketMap::HasPendingCryptoPackets() const { + return pending_crypto_packet_count_ > 0; +} + +bool QuicUnackedPacketMap::HasUnackedRetransmittableFrames() const { + for (UnackedPacketMap::const_reverse_iterator it = + unacked_packets_.rbegin(); it != unacked_packets_.rend(); ++it) { + if (it->second.in_flight && it->second.retransmittable_frames) { + return true; + } + } + return false; +} + +QuicPacketSequenceNumber +QuicUnackedPacketMap::GetLeastUnackedSentPacket() const { + if (unacked_packets_.empty()) { + // If there are no unacked packets, return 0. + return 0; + } + + return unacked_packets_.begin()->first; +} + +void QuicUnackedPacketMap::SetSent(QuicPacketSequenceNumber sequence_number, + QuicTime sent_time, + QuicByteCount bytes_sent, + bool set_in_flight) { + DCHECK_LT(0u, sequence_number); + UnackedPacketMap::iterator it = unacked_packets_.find(sequence_number); + if (it == unacked_packets_.end()) { + LOG(DFATAL) << "OnPacketSent called for packet that is not unacked: " + << sequence_number; + return; + } + DCHECK(!it->second.in_flight); + + largest_sent_packet_ = max(sequence_number, largest_sent_packet_); + it->second.sent_time = sent_time; + if (set_in_flight) { + bytes_in_flight_ += bytes_sent; + it->second.bytes_sent = bytes_sent; + it->second.in_flight = true; + } +} + +} // namespace net diff --git a/chromium/net/quic/quic_unacked_packet_map.h b/chromium/net/quic/quic_unacked_packet_map.h new file mode 100644 index 00000000000..ae72548b9b4 --- /dev/null +++ b/chromium/net/quic/quic_unacked_packet_map.h @@ -0,0 +1,152 @@ +// 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_QUIC_UNACKED_PACKET_MAP_H_ +#define NET_QUIC_QUIC_UNACKED_PACKET_MAP_H_ + +#include "net/base/linked_hash_map.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +// Class which tracks unacked packets for three purposes: +// 1) Track retransmittable data, including multiple transmissions of frames. +// 2) Track packets and bytes in flight for congestion control. +// 3) Track sent time of packets to provide RTT measurements from acks. +class NET_EXPORT_PRIVATE QuicUnackedPacketMap { + public: + QuicUnackedPacketMap(); + ~QuicUnackedPacketMap(); + + // Adds |serialized_packet| to the map. Does not mark it in flight. + void AddPacket(const SerializedPacket& serialized_packet); + + // Called when a packet is retransmitted with a new sequence number. + // |old_sequence_number| will remain unacked, but will have no + // retransmittable data associated with it. |new_sequence_number| will + // be both unacked and associated with retransmittable data. + void OnRetransmittedPacket(QuicPacketSequenceNumber old_sequence_number, + QuicPacketSequenceNumber new_sequence_number, + TransmissionType transmission_type); + + // Returns true if the packet |sequence_number| is unacked. + bool IsUnacked(QuicPacketSequenceNumber sequence_number) const; + + // Sets the nack count to the max of the current nack count and |min_nacks|. + void NackPacket(QuicPacketSequenceNumber sequence_number, + size_t min_nacks); + + // Marks |sequence_number| as no longer in flight. + void RemoveFromInFlight(QuicPacketSequenceNumber sequence_number); + + // Returns true if the unacked packet |sequence_number| has retransmittable + // frames. This will return false if the packet has been acked, if a + // previous transmission of this packet was ACK'd, or if this packet has been + // retransmitted as with different sequence number, or if the packet never + // had any retransmittable packets in the first place. + bool HasRetransmittableFrames(QuicPacketSequenceNumber sequence_number) const; + + // Returns true if there are any unacked packets. + bool HasUnackedPackets() const; + + // Returns true if there are any unacked packets which have retransmittable + // frames. + bool HasUnackedRetransmittableFrames() const; + + // Returns the largest sequence number that has been sent. + QuicPacketSequenceNumber largest_sent_packet() const { + return largest_sent_packet_; + } + + // Returns the sum of bytes from all packets in flight. + QuicByteCount bytes_in_flight() const { + return bytes_in_flight_; + } + + // Returns the smallest sequence number of a serialized packet which has not + // been acked by the peer. If there are no unacked packets, returns 0. + QuicPacketSequenceNumber GetLeastUnackedSentPacket() const; + + // Sets a packet as sent with the sent time |sent_time|. Marks the packet + // as in flight if |set_in_flight| is true. + // Packets marked as in flight are expected to be marked as missing when they + // don't arrive, indicating the need for retransmission. + void SetSent(QuicPacketSequenceNumber sequence_number, + QuicTime sent_time, + QuicByteCount bytes_sent, + bool set_in_flight); + + // Clears up to |num_to_clear| previous transmissions in order to make room + // in the ack frame for new acks. + void ClearPreviousRetransmissions(size_t num_to_clear); + + typedef linked_hash_map<QuicPacketSequenceNumber, + TransmissionInfo> UnackedPacketMap; + + typedef UnackedPacketMap::const_iterator const_iterator; + + const_iterator begin() const { return unacked_packets_.begin(); } + const_iterator end() const { return unacked_packets_.end(); } + + // Returns true if there are unacked packets that are in flight. + bool HasInFlightPackets() const; + + // Returns the TransmissionInfo associated with |sequence_number|, which + // must be unacked. + const TransmissionInfo& GetTransmissionInfo( + QuicPacketSequenceNumber sequence_number) const; + + // Returns the time that the last unacked packet was sent. + QuicTime GetLastPacketSentTime() const; + + // Returns the time that the first in flight packet was sent. + QuicTime GetFirstInFlightPacketSentTime() const; + + // Returns the number of unacked packets. + size_t GetNumUnackedPackets() const; + + // Returns true if there are multiple packets in flight. + bool HasMultipleInFlightPackets() const; + + // Returns true if there are any pending crypto packets. + bool HasPendingCryptoPackets() const; + + // Removes any retransmittable frames from this transmission or an associated + // transmission. It removes now useless transmissions, and disconnects any + // other packets from other transmissions. + void RemoveRetransmittability(QuicPacketSequenceNumber sequence_number); + + // Increases the largest observed. Any packets less or equal to + // |largest_acked_packet| are discarded if they are only for the RTT purposes. + void IncreaseLargestObserved(QuicPacketSequenceNumber largest_observed); + + private: + void MaybeRemoveRetransmittableFrames(TransmissionInfo* transmission_info); + + // Returns true if the packet no longer has a purpose in the map. + bool IsPacketUseless(UnackedPacketMap::const_iterator it) const; + + QuicPacketSequenceNumber largest_sent_packet_; + QuicPacketSequenceNumber largest_observed_; + + // Newly serialized retransmittable and fec packets are added to this map, + // which contains owning pointers to any contained frames. If a packet is + // retransmitted, this map will contain entries for both the old and the new + // packet. The old packet's retransmittable frames entry will be NULL, while + // the new packet's entry will contain the frames to retransmit. + // If the old packet is acked before the new packet, then the old entry will + // be removed from the map and the new entry's retransmittable frames will be + // set to NULL. + UnackedPacketMap unacked_packets_; + + size_t bytes_in_flight_; + // Number of retransmittable crypto handshake packets. + size_t pending_crypto_packet_count_; + + DISALLOW_COPY_AND_ASSIGN(QuicUnackedPacketMap); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_UNACKED_PACKET_MAP_H_ diff --git a/chromium/net/quic/quic_unacked_packet_map_test.cc b/chromium/net/quic/quic_unacked_packet_map_test.cc new file mode 100644 index 00000000000..c5264fd16e9 --- /dev/null +++ b/chromium/net/quic/quic_unacked_packet_map_test.cc @@ -0,0 +1,166 @@ +// 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/quic_unacked_packet_map.h" + +#include "net/quic/test_tools/quic_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { +namespace { + +// Default packet length. +const uint32 kDefaultAckLength = 50; +const uint32 kDefaultLength = 1000; + +class QuicUnackedPacketMapTest : public ::testing::Test { + protected: + QuicUnackedPacketMapTest() + : now_(QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(1000))) { + } + + SerializedPacket CreateRetransmittablePacket( + QuicPacketSequenceNumber sequence_number) { + return SerializedPacket(sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, + 0, new RetransmittableFrames()); + } + + SerializedPacket CreateNonRetransmittablePacket( + QuicPacketSequenceNumber sequence_number) { + return SerializedPacket( + sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL); + } + + void VerifyPendingPackets(QuicPacketSequenceNumber* packets, + size_t num_packets) { + if (num_packets == 0) { + EXPECT_FALSE(unacked_packets_.HasInFlightPackets()); + EXPECT_FALSE(unacked_packets_.HasMultipleInFlightPackets()); + return; + } + if (num_packets == 1) { + EXPECT_TRUE(unacked_packets_.HasInFlightPackets()); + EXPECT_FALSE(unacked_packets_.HasMultipleInFlightPackets()); + } + for (size_t i = 0; i < num_packets; ++i) { + ASSERT_TRUE(unacked_packets_.IsUnacked(packets[i])); + EXPECT_TRUE(unacked_packets_.GetTransmissionInfo(packets[i]).in_flight); + } + } + + void VerifyUnackedPackets(QuicPacketSequenceNumber* packets, + size_t num_packets) { + if (num_packets == 0) { + EXPECT_FALSE(unacked_packets_.HasUnackedPackets()); + EXPECT_FALSE(unacked_packets_.HasUnackedRetransmittableFrames()); + return; + } + EXPECT_TRUE(unacked_packets_.HasUnackedPackets()); + for (size_t i = 0; i < num_packets; ++i) { + EXPECT_TRUE(unacked_packets_.IsUnacked(packets[i])) << packets[i]; + } + } + + void VerifyRetransmittablePackets(QuicPacketSequenceNumber* packets, + size_t num_packets) { + size_t num_retransmittable_packets = 0; + for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); + it != unacked_packets_.end(); ++it) { + if (it->second.retransmittable_frames != NULL) { + ++num_retransmittable_packets; + } + } + EXPECT_EQ(num_packets, num_retransmittable_packets); + for (size_t i = 0; i < num_packets; ++i) { + EXPECT_TRUE(unacked_packets_.HasRetransmittableFrames(packets[i])) + << " packets[" << i << "]:" << packets[i]; + } + } + + QuicUnackedPacketMap unacked_packets_; + QuicTime now_; +}; + +TEST_F(QuicUnackedPacketMapTest, RttOnly) { + // Acks are only tracked for RTT measurement purposes. + unacked_packets_.AddPacket(CreateNonRetransmittablePacket(1)); + unacked_packets_.SetSent(1, now_, kDefaultAckLength, false); + + QuicPacketSequenceNumber unacked[] = { 1 }; + VerifyUnackedPackets(unacked, arraysize(unacked)); + VerifyPendingPackets(NULL, 0); + VerifyRetransmittablePackets(NULL, 0); + + unacked_packets_.IncreaseLargestObserved(1); + VerifyUnackedPackets(NULL, 0); + VerifyPendingPackets(NULL, 0); + VerifyRetransmittablePackets(NULL, 0); +} + +TEST_F(QuicUnackedPacketMapTest, RetransmittableInflightAndRtt) { + // Simulate a retransmittable packet being sent and acked. + unacked_packets_.AddPacket(CreateRetransmittablePacket(1)); + unacked_packets_.SetSent(1, now_, kDefaultLength, true); + + QuicPacketSequenceNumber unacked[] = { 1 }; + VerifyUnackedPackets(unacked, arraysize(unacked)); + VerifyPendingPackets(unacked, arraysize(unacked)); + VerifyRetransmittablePackets(unacked, arraysize(unacked)); + + unacked_packets_.RemoveRetransmittability(1); + VerifyUnackedPackets(unacked, arraysize(unacked)); + VerifyPendingPackets(unacked, arraysize(unacked)); + VerifyRetransmittablePackets(NULL, 0); + + unacked_packets_.IncreaseLargestObserved(1); + VerifyUnackedPackets(unacked, arraysize(unacked)); + VerifyPendingPackets(unacked, arraysize(unacked)); + VerifyRetransmittablePackets(NULL, 0); + + unacked_packets_.RemoveFromInFlight(1); + VerifyUnackedPackets(NULL, 0); + VerifyPendingPackets(NULL, 0); + VerifyRetransmittablePackets(NULL, 0); +} + +TEST_F(QuicUnackedPacketMapTest, RetransmittedPacket) { + // Simulate a retransmittable packet being sent, retransmitted, and the first + // transmission being acked. + unacked_packets_.AddPacket(CreateRetransmittablePacket(1)); + unacked_packets_.SetSent(1, now_, kDefaultLength, true); + unacked_packets_.OnRetransmittedPacket(1, 2, LOSS_RETRANSMISSION); + unacked_packets_.SetSent(2, now_, kDefaultLength, true); + + QuicPacketSequenceNumber unacked[] = { 1, 2 }; + VerifyUnackedPackets(unacked, arraysize(unacked)); + VerifyPendingPackets(unacked, arraysize(unacked)); + QuicPacketSequenceNumber retransmittable[] = { 2 }; + VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable)); + + unacked_packets_.RemoveRetransmittability(1); + VerifyUnackedPackets(unacked, arraysize(unacked)); + VerifyPendingPackets(unacked, arraysize(unacked)); + VerifyRetransmittablePackets(NULL, 0); + + unacked_packets_.IncreaseLargestObserved(2); + VerifyUnackedPackets(unacked, arraysize(unacked)); + VerifyPendingPackets(unacked, arraysize(unacked)); + VerifyRetransmittablePackets(NULL, 0); + + unacked_packets_.RemoveFromInFlight(2); + QuicPacketSequenceNumber unacked2[] = { 1 }; + VerifyUnackedPackets(unacked, arraysize(unacked2)); + VerifyPendingPackets(unacked, arraysize(unacked2)); + VerifyRetransmittablePackets(NULL, 0); + + unacked_packets_.RemoveFromInFlight(1); + VerifyUnackedPackets(NULL, 0); + VerifyPendingPackets(NULL, 0); + VerifyRetransmittablePackets(NULL, 0); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_utils.cc b/chromium/net/quic/quic_utils.cc index 0aff3772c26..ae4bb2eedc9 100644 --- a/chromium/net/quic/quic_utils.cc +++ b/chromium/net/quic/quic_utils.cc @@ -8,11 +8,12 @@ #include <algorithm> +#include "base/basictypes.h" #include "base/logging.h" #include "base/port.h" #include "base/strings/stringprintf.h" #include "base/strings/string_number_conversions.h" -#include "net/spdy/write_blocked_list.h" +#include "net/quic/quic_write_blocked_list.h" using base::StringPiece; using std::string; @@ -137,6 +138,7 @@ const char* QuicUtils::StreamErrorToString(QuicRstStreamErrorCode error) { RETURN_STRING_LITERAL(QUIC_BAD_APPLICATION_PAYLOAD); RETURN_STRING_LITERAL(QUIC_STREAM_PEER_GOING_AWAY); RETURN_STRING_LITERAL(QUIC_STREAM_CANCELLED); + RETURN_STRING_LITERAL(QUIC_RST_FLOW_CONTROL_ACCOUNTING); RETURN_STRING_LITERAL(QUIC_STREAM_LAST_ERROR); } // Return a default value so that we return this when |error| doesn't match @@ -156,9 +158,13 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_MISSING_PAYLOAD); RETURN_STRING_LITERAL(QUIC_INVALID_FEC_DATA); RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_DATA); + RETURN_STRING_LITERAL(QUIC_UNENCRYPTED_STREAM_DATA); RETURN_STRING_LITERAL(QUIC_INVALID_RST_STREAM_DATA); RETURN_STRING_LITERAL(QUIC_INVALID_CONNECTION_CLOSE_DATA); RETURN_STRING_LITERAL(QUIC_INVALID_GOAWAY_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_WINDOW_UPDATE_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_BLOCKED_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_STOP_WAITING_DATA); RETURN_STRING_LITERAL(QUIC_INVALID_ACK_DATA); RETURN_STRING_LITERAL(QUIC_INVALID_CONGESTION_FEEDBACK_DATA); RETURN_STRING_LITERAL(QUIC_INVALID_VERSION_NEGOTIATION_PACKET); @@ -187,7 +193,6 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_TOO_MANY_OPEN_STREAMS); RETURN_STRING_LITERAL(QUIC_PUBLIC_RESET); RETURN_STRING_LITERAL(QUIC_INVALID_VERSION); - RETURN_STRING_LITERAL(QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED); RETURN_STRING_LITERAL(QUIC_INVALID_HEADER_ID); RETURN_STRING_LITERAL(QUIC_INVALID_NEGOTIATED_VALUE); RETURN_STRING_LITERAL(QUIC_DECOMPRESSION_FAILURE); @@ -196,6 +201,11 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_PACKET_WRITE_ERROR); RETURN_STRING_LITERAL(QUIC_PACKET_READ_ERROR); RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_FRAME); + RETURN_STRING_LITERAL(QUIC_INVALID_HEADERS_STREAM_DATA); + RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA); + RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA); + RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_INVALID_WINDOW); + RETURN_STRING_LITERAL(QUIC_CONNECTION_IP_POOLED); RETURN_STRING_LITERAL(QUIC_PROOF_INVALID); RETURN_STRING_LITERAL(QUIC_CRYPTO_DUPLICATE_TAG); RETURN_STRING_LITERAL(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT); @@ -226,6 +236,19 @@ const char* QuicUtils::EncryptionLevelToString(EncryptionLevel level) { } // static +const char* QuicUtils::TransmissionTypeToString(TransmissionType type) { + switch (type) { + RETURN_STRING_LITERAL(NOT_RETRANSMISSION); + RETURN_STRING_LITERAL(HANDSHAKE_RETRANSMISSION); + RETURN_STRING_LITERAL(LOSS_RETRANSMISSION); + RETURN_STRING_LITERAL(ALL_UNACKED_RETRANSMISSION); + RETURN_STRING_LITERAL(RTO_RETRANSMISSION); + RETURN_STRING_LITERAL(TLP_RETRANSMISSION); + } + return "INVALID_TRANSMISSION_TYPE"; +} + +// static string QuicUtils::TagToString(QuicTag tag) { char chars[4]; bool ascii = true; @@ -284,12 +307,12 @@ string QuicUtils::StringToHexASCIIDump(StringPiece in_buffer) { // static QuicPriority QuicUtils::LowestPriority() { - return static_cast<QuicPriority>(kLowestPriority); + return QuicWriteBlockedList::kLowestPriority; } // static QuicPriority QuicUtils::HighestPriority() { - return static_cast<QuicPriority>(kHighestPriority); + return QuicWriteBlockedList::kHighestPriority; } } // namespace net diff --git a/chromium/net/quic/quic_utils.h b/chromium/net/quic/quic_utils.h index 23f3b53a611..43f8d350ea6 100644 --- a/chromium/net/quic/quic_utils.h +++ b/chromium/net/quic/quic_utils.h @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -// Some helpers for quic +// Some helpers for quic. #ifndef NET_QUIC_QUIC_UTILS_H_ #define NET_QUIC_QUIC_UTILS_H_ @@ -20,7 +20,7 @@ class NET_EXPORT_PRIVATE QuicUtils { PEER_PRIORITY, }; - // returns the 64 bit FNV1a hash of the data. See + // Returns the 64 bit FNV1a hash of the data. See // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param static uint64 FNV1a_64_Hash(const char* data, int len); @@ -59,6 +59,9 @@ class NET_EXPORT_PRIVATE QuicUtils { // Returns the level of encryption as a char* static const char* EncryptionLevelToString(EncryptionLevel level); + // Returns TransmissionType as a char* + static const char* TransmissionTypeToString(TransmissionType type); + // TagToString is a utility function for pretty-printing handshake messages // that converts a tag to a string. It will try to maintain the human friendly // name if possible (i.e. kABCD -> "ABCD"), or will just treat it as a number @@ -79,6 +82,9 @@ class NET_EXPORT_PRIVATE QuicUtils { static QuicPriority LowestPriority(); static QuicPriority HighestPriority(); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicUtils); }; // Utility function that returns an IOVector object wrapped around |str|. diff --git a/chromium/net/quic/quic_utils_chromium.h b/chromium/net/quic/quic_utils_chromium.h new file mode 100644 index 00000000000..0229d1d0400 --- /dev/null +++ b/chromium/net/quic/quic_utils_chromium.h @@ -0,0 +1,80 @@ +// 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. +// +// Some helpers for quic that are for chromium codebase. + +#ifndef NET_QUIC_QUIC_UTILS_CHROMIUM_H_ +#define NET_QUIC_QUIC_UTILS_CHROMIUM_H_ + +#include "base/basictypes.h" +#include "base/logging.h" + +namespace net { + +// +// Find*() +// + +// Returns a const reference to the value associated with the given key if it +// exists. Crashes otherwise. +// +// This is intended as a replacement for operator[] as an rvalue (for reading) +// when the key is guaranteed to exist. +// +// operator[] for lookup is discouraged for several reasons: +// * It has a side-effect of inserting missing keys +// * It is not thread-safe (even when it is not inserting, it can still +// choose to resize the underlying storage) +// * It invalidates iterators (when it chooses to resize) +// * It default constructs a value object even if it doesn't need to +// +// This version assumes the key is printable, and includes it in the fatal log +// message. +template <class Collection> +const typename Collection::value_type::second_type& +FindOrDie(const Collection& collection, + const typename Collection::value_type::first_type& key) { + typename Collection::const_iterator it = collection.find(key); + CHECK(it != collection.end()) << "Map key not found: " << key; + return it->second; +} + +// Same as above, but returns a non-const reference. +template <class Collection> +typename Collection::value_type::second_type& +FindOrDie(Collection& collection, // NOLINT + const typename Collection::value_type::first_type& key) { + typename Collection::iterator it = collection.find(key); + CHECK(it != collection.end()) << "Map key not found: " << key; + return it->second; +} + +// Returns a pointer to the const value associated with the given key if it +// exists, or NULL otherwise. +template <class Collection> +const typename Collection::value_type::second_type* +FindOrNull(const Collection& collection, + const typename Collection::value_type::first_type& key) { + typename Collection::const_iterator it = collection.find(key); + if (it == collection.end()) { + return 0; + } + return &it->second; +} + +// Same as above but returns a pointer to the non-const value. +template <class Collection> +typename Collection::value_type::second_type* +FindOrNull(Collection& collection, // NOLINT + const typename Collection::value_type::first_type& key) { + typename Collection::iterator it = collection.find(key); + if (it == collection.end()) { + return 0; + } + return &it->second; +} + +} // namespace net + +#endif // NET_QUIC_QUIC_UTILS_CHROMIUM_H_ diff --git a/chromium/net/quic/quic_utils_chromium_test.cc b/chromium/net/quic/quic_utils_chromium_test.cc new file mode 100644 index 00000000000..247b8613c49 --- /dev/null +++ b/chromium/net/quic/quic_utils_chromium_test.cc @@ -0,0 +1,50 @@ +// 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/quic_utils_chromium.h" + +#include <map> + +#include "testing/gtest/include/gtest/gtest.h" + +using std::map; + +namespace net { +namespace test { +namespace { + +TEST(QuicUtilsChromiumTest, FindOrNullTest) { + map<int, int> m; + m[0] = 2; + + // Check FindOrNull + int* p1 = FindOrNull(m, 0); + CHECK_EQ(*p1, 2); + ++(*p1); + const map<int, int>& const_m = m; + const int* p2 = FindOrNull(const_m, 0); + CHECK_EQ(*p2, 3); + CHECK(FindOrNull(m, 1) == NULL); +} + +TEST(QuicUtilsChromiumTest, FindOrDieTest) { + std::map<int, int> m; + m[10] = 15; + EXPECT_EQ(15, FindOrDie(m, 10)); + // TODO(rtenneti): Use the latest DEATH macros after merging with latest rch's + // changes. + // ASSERT_DEATH(FindOrDie(m, 8), "Map key not found: 8"); + + // Make sure the non-const reference returning version works. + FindOrDie(m, 10) = 20; + EXPECT_EQ(20, FindOrDie(m, 10)); + + // Make sure we can lookup values in a const map. + const map<int, int>& const_m = m; + EXPECT_EQ(20, FindOrDie(const_m, 10)); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_write_blocked_list.cc b/chromium/net/quic/quic_write_blocked_list.cc new file mode 100644 index 00000000000..10f9c343bfa --- /dev/null +++ b/chromium/net/quic/quic_write_blocked_list.cc @@ -0,0 +1,20 @@ +// 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/quic_write_blocked_list.h" + +namespace net { + +const QuicPriority QuicWriteBlockedList::kHighestPriority = + static_cast<QuicPriority>(net::kHighestPriority); +const QuicPriority QuicWriteBlockedList::kLowestPriority = + static_cast<QuicPriority>(net::kLowestPriority); + +QuicWriteBlockedList::QuicWriteBlockedList() + : crypto_stream_blocked_(false), + headers_stream_blocked_(false) {} + +QuicWriteBlockedList::~QuicWriteBlockedList() {} + +} // namespace net diff --git a/chromium/net/quic/quic_write_blocked_list.h b/chromium/net/quic/quic_write_blocked_list.h new file mode 100644 index 00000000000..6727fb4e826 --- /dev/null +++ b/chromium/net/quic/quic_write_blocked_list.h @@ -0,0 +1,113 @@ +// 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_QUIC_WRITE_BLOCKED_LIST_H_ +#define NET_QUIC_QUIC_WRITE_BLOCKED_LIST_H_ + +#include <set> + +#include "net/base/net_export.h" +#include "net/quic/quic_protocol.h" +#include "net/spdy/write_blocked_list.h" + +namespace net { + +// Keeps tracks of the QUIC streams that have data to write, sorted by +// priority. QUIC stream priority order is: +// Crypto stream > Headers stream > Data streams by requested priority. +class NET_EXPORT_PRIVATE QuicWriteBlockedList { + private: + typedef WriteBlockedList<QuicStreamId> QuicWriteBlockedListBase; + + public: + static const QuicPriority kHighestPriority; + static const QuicPriority kLowestPriority; + + QuicWriteBlockedList(); + ~QuicWriteBlockedList(); + + bool HasWriteBlockedDataStreams() const { + return base_write_blocked_list_.HasWriteBlockedStreams(); + } + + bool HasWriteBlockedCryptoOrHeadersStream() const { + return crypto_stream_blocked_ || headers_stream_blocked_; + } + + size_t NumBlockedStreams() const { + size_t num_blocked = base_write_blocked_list_.NumBlockedStreams(); + if (crypto_stream_blocked_) { + ++num_blocked; + } + if (headers_stream_blocked_) { + ++num_blocked; + } + + return num_blocked; + } + + QuicStreamId PopFront() { + if (crypto_stream_blocked_) { + crypto_stream_blocked_ = false; + return kCryptoStreamId; + } + + if (headers_stream_blocked_) { + headers_stream_blocked_ = false; + return kHeadersStreamId; + } + + SpdyPriority priority = + base_write_blocked_list_.GetHighestPriorityWriteBlockedList(); + QuicStreamId id = base_write_blocked_list_.PopFront(priority); + blocked_streams_.erase(id); + return id; + } + + void PushBack(QuicStreamId stream_id, QuicPriority priority) { + if (stream_id == kCryptoStreamId) { + DCHECK_EQ(kHighestPriority, priority); + // TODO(avd) Add DCHECK(!crypto_stream_blocked_) + crypto_stream_blocked_ = true; + return; + } + + if (stream_id == kHeadersStreamId) { + DCHECK_EQ(kHighestPriority, priority); + // TODO(avd) Add DCHECK(!headers_stream_blocked_); + headers_stream_blocked_ = true; + return; + } + + if (blocked_streams_.find(stream_id) != blocked_streams_.end()) { + DVLOG(1) << "Stream " << stream_id << " already in write blocked list."; + return; + } + + base_write_blocked_list_.PushBack( + stream_id, static_cast<SpdyPriority>(priority)); + blocked_streams_.insert(stream_id); + return; + } + + bool crypto_stream_blocked() const { return crypto_stream_blocked_; } + bool headers_stream_blocked() const { return headers_stream_blocked_; } + + private: + QuicWriteBlockedListBase base_write_blocked_list_; + bool crypto_stream_blocked_; + bool headers_stream_blocked_; + + // Keep track of write blocked streams in a set for faster membership checking + // than iterating over the base_write_blocked_list_. The contents of this set + // should mirror the contents of base_write_blocked_list_. + std::set<QuicStreamId> blocked_streams_; + + DISALLOW_COPY_AND_ASSIGN(QuicWriteBlockedList); +}; + +} // namespace net + + +#endif // NET_QUIC_QUIC_WRITE_BLOCKED_LIST_H_ diff --git a/chromium/net/quic/quic_write_blocked_list_test.cc b/chromium/net/quic/quic_write_blocked_list_test.cc new file mode 100644 index 00000000000..0633f633636 --- /dev/null +++ b/chromium/net/quic/quic_write_blocked_list_test.cc @@ -0,0 +1,117 @@ +// 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/quic_write_blocked_list.h" + +#include "net/quic/test_tools/quic_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { +namespace { + +TEST(QuicWriteBlockedListTest, PriorityOrder) { + QuicWriteBlockedList write_blocked_list; + + // Mark streams blocked in roughly reverse priority order, and + // verify that streams are sorted. + write_blocked_list.PushBack(40, + QuicWriteBlockedList::kLowestPriority); + write_blocked_list.PushBack(23, + QuicWriteBlockedList::kHighestPriority); + write_blocked_list.PushBack(17, + QuicWriteBlockedList::kHighestPriority); + write_blocked_list.PushBack(kHeadersStreamId, + QuicWriteBlockedList::kHighestPriority); + write_blocked_list.PushBack(kCryptoStreamId, + QuicWriteBlockedList::kHighestPriority); + + EXPECT_EQ(5u, write_blocked_list.NumBlockedStreams()); + EXPECT_TRUE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream()); + EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams()); + // The Crypto stream is highest priority. + EXPECT_EQ(kCryptoStreamId, write_blocked_list.PopFront()); + // Followed by the Headers stream. + EXPECT_EQ(kHeadersStreamId, write_blocked_list.PopFront()); + // Streams with same priority are popped in the order they were inserted. + EXPECT_EQ(23u, write_blocked_list.PopFront()); + EXPECT_EQ(17u, write_blocked_list.PopFront()); + // Low priority stream appears last. + EXPECT_EQ(40u, write_blocked_list.PopFront()); + + EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams()); + EXPECT_FALSE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream()); + EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams()); +} + +TEST(QuicWriteBlockedListTest, CryptoStream) { + QuicWriteBlockedList write_blocked_list; + write_blocked_list.PushBack(kCryptoStreamId, + QuicWriteBlockedList::kHighestPriority); + + EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams()); + EXPECT_TRUE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream()); + EXPECT_EQ(kCryptoStreamId, write_blocked_list.PopFront()); + EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams()); + EXPECT_FALSE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream()); +} + +TEST(QuicWriteBlockedListTest, HeadersStream) { + QuicWriteBlockedList write_blocked_list; + write_blocked_list.PushBack(kHeadersStreamId, + QuicWriteBlockedList::kHighestPriority); + + EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams()); + EXPECT_TRUE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream()); + EXPECT_EQ(kHeadersStreamId, write_blocked_list.PopFront()); + EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams()); + EXPECT_FALSE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream()); +} + +TEST(QuicWriteBlockedListTest, VerifyHeadersStream) { + QuicWriteBlockedList write_blocked_list; + write_blocked_list.PushBack(5, + QuicWriteBlockedList::kHighestPriority); + write_blocked_list.PushBack(kHeadersStreamId, + QuicWriteBlockedList::kHighestPriority); + + EXPECT_EQ(2u, write_blocked_list.NumBlockedStreams()); + EXPECT_TRUE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream()); + EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams()); + // In newer QUIC versions, there is a headers stream which is + // higher priority than data streams. + EXPECT_EQ(kHeadersStreamId, write_blocked_list.PopFront()); + EXPECT_EQ(5u, write_blocked_list.PopFront()); + EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams()); + EXPECT_FALSE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream()); + EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams()); +} + +TEST(QuicWriteBlockedListTest, NoDuplicateEntries) { + // Test that QuicWriteBlockedList doesn't allow duplicate entries. + QuicWriteBlockedList write_blocked_list; + + // Try to add a stream to the write blocked list multiple times at the same + // priority. + const QuicStreamId kBlockedId = kClientDataStreamId1; + write_blocked_list.PushBack(kBlockedId, + QuicWriteBlockedList::kHighestPriority); + write_blocked_list.PushBack(kBlockedId, + QuicWriteBlockedList::kHighestPriority); + write_blocked_list.PushBack(kBlockedId, + QuicWriteBlockedList::kHighestPriority); + + // This should only result in one blocked stream being added. + EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams()); + EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams()); + + // There should only be one stream to pop off the front. + EXPECT_EQ(kBlockedId, write_blocked_list.PopFront()); + EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams()); + EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams()); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/reliable_quic_stream.cc b/chromium/net/quic/reliable_quic_stream.cc index 5d9c3cce49c..44d3a036fdd 100644 --- a/chromium/net/quic/reliable_quic_stream.cc +++ b/chromium/net/quic/reliable_quic_stream.cc @@ -4,9 +4,11 @@ #include "net/quic/reliable_quic_stream.h" +#include "base/logging.h" +#include "net/quic/iovector.h" +#include "net/quic/quic_flow_controller.h" #include "net/quic/quic_session.h" -#include "net/quic/quic_spdy_decompressor.h" -#include "net/spdy/write_blocked_list.h" +#include "net/quic/quic_write_blocked_list.h" using base::StringPiece; using std::min; @@ -23,10 +25,119 @@ struct iovec MakeIovec(StringPiece data) { return iov; } +size_t GetInitialStreamFlowControlWindowToSend(QuicSession* session) { + QuicVersion version = session->connection()->version(); + if (version <= QUIC_VERSION_19) { + return session->config()->GetInitialFlowControlWindowToSend(); + } + + return session->config()->GetInitialStreamFlowControlWindowToSend(); +} + +size_t GetReceivedFlowControlWindow(QuicSession* session) { + QuicVersion version = session->connection()->version(); + if (version <= QUIC_VERSION_19) { + if (session->config()->HasReceivedInitialFlowControlWindowBytes()) { + return session->config()->ReceivedInitialFlowControlWindowBytes(); + } + + return kDefaultFlowControlSendWindow; + } + + // Version must be >= QUIC_VERSION_20, so we check for stream specific flow + // control window. + if (session->config()->HasReceivedInitialStreamFlowControlWindowBytes()) { + return session->config()->ReceivedInitialStreamFlowControlWindowBytes(); + } + + return kDefaultFlowControlSendWindow; +} + } // namespace -ReliableQuicStream::ReliableQuicStream(QuicStreamId id, - QuicSession* session) +// Wrapper that aggregates OnAckNotifications for packets sent using +// WriteOrBufferData and delivers them to the original +// QuicAckNotifier::DelegateInterface after all bytes written using +// WriteOrBufferData are acked. This level of indirection is +// necessary because the delegate interface provides no mechanism that +// WriteOrBufferData can use to inform it that the write required +// multiple WritevData calls or that only part of the data has been +// sent out by the time ACKs start arriving. +class ReliableQuicStream::ProxyAckNotifierDelegate + : public QuicAckNotifier::DelegateInterface { + public: + explicit ProxyAckNotifierDelegate(DelegateInterface* delegate) + : delegate_(delegate), + pending_acks_(0), + wrote_last_data_(false), + num_original_packets_(0), + num_original_bytes_(0), + num_retransmitted_packets_(0), + num_retransmitted_bytes_(0) { + } + + virtual void OnAckNotification(int num_original_packets, + int num_original_bytes, + int num_retransmitted_packets, + int num_retransmitted_bytes, + QuicTime::Delta delta_largest_observed) + OVERRIDE { + DCHECK_LT(0, pending_acks_); + --pending_acks_; + num_original_packets_ += num_original_packets; + num_original_bytes_ += num_original_bytes; + num_retransmitted_packets_ += num_retransmitted_packets; + num_retransmitted_bytes_ += num_retransmitted_bytes; + + if (wrote_last_data_ && pending_acks_ == 0) { + delegate_->OnAckNotification(num_original_packets_, + num_original_bytes_, + num_retransmitted_packets_, + num_retransmitted_bytes_, + delta_largest_observed); + } + } + + void WroteData(bool last_data) { + DCHECK(!wrote_last_data_); + ++pending_acks_; + wrote_last_data_ = last_data; + } + + protected: + // Delegates are ref counted. + virtual ~ProxyAckNotifierDelegate() OVERRIDE { + } + + private: + // Original delegate. delegate_->OnAckNotification will be called when: + // wrote_last_data_ == true and pending_acks_ == 0 + scoped_refptr<DelegateInterface> delegate_; + + // Number of outstanding acks. + int pending_acks_; + + // True if no pending writes remain. + bool wrote_last_data_; + + // Accumulators. + int num_original_packets_; + int num_original_bytes_; + int num_retransmitted_packets_; + int num_retransmitted_bytes_; + + DISALLOW_COPY_AND_ASSIGN(ProxyAckNotifierDelegate); +}; + +ReliableQuicStream::PendingData::PendingData( + string data_in, scoped_refptr<ProxyAckNotifierDelegate> delegate_in) + : data(data_in), delegate(delegate_in) { +} + +ReliableQuicStream::PendingData::~PendingData() { +} + +ReliableQuicStream::ReliableQuicStream(QuicStreamId id, QuicSession* session) : sequencer_(this), id_(id), session_(session), @@ -38,41 +149,70 @@ ReliableQuicStream::ReliableQuicStream(QuicStreamId id, write_side_closed_(false), fin_buffered_(false), fin_sent_(false), - is_server_(session_->is_server()) { + fin_received_(false), + rst_sent_(false), + rst_received_(false), + fec_policy_(FEC_PROTECT_OPTIONAL), + is_server_(session_->is_server()), + flow_controller_( + session_->connection(), id_, is_server_, + GetReceivedFlowControlWindow(session), + GetInitialStreamFlowControlWindowToSend(session), + GetInitialStreamFlowControlWindowToSend(session)), + connection_flow_controller_(session_->flow_controller()) { } ReliableQuicStream::~ReliableQuicStream() { } -bool ReliableQuicStream::WillAcceptStreamFrame( - const QuicStreamFrame& frame) const { +bool ReliableQuicStream::OnStreamFrame(const QuicStreamFrame& frame) { if (read_side_closed_) { + DVLOG(1) << ENDPOINT << "Ignoring frame " << frame.stream_id; + // We don't want to be reading: blackhole the data. return true; } + if (frame.stream_id != id_) { LOG(ERROR) << "Error!"; return false; } - return sequencer_.WillAcceptStreamFrame(frame); -} -bool ReliableQuicStream::OnStreamFrame(const QuicStreamFrame& frame) { - DCHECK_EQ(frame.stream_id, id_); - if (read_side_closed_) { - DVLOG(1) << ENDPOINT << "Ignoring frame " << frame.stream_id; - // We don't want to be reading: blackhole the data. - return true; + if (frame.fin) { + fin_received_ = true; } - // Note: This count include duplicate data received. - stream_bytes_read_ += frame.data.TotalBufferSize(); - bool accepted = sequencer_.OnStreamFrame(frame); + // This count include duplicate data received. + size_t frame_payload_size = frame.data.TotalBufferSize(); + stream_bytes_read_ += frame_payload_size; + + // Flow control is interested in tracking highest received offset. + if (MaybeIncreaseHighestReceivedOffset(frame.offset + frame_payload_size)) { + // As the highest received offset has changed, we should check to see if + // this is a violation of flow control. + if (flow_controller_.FlowControlViolation() || + connection_flow_controller_->FlowControlViolation()) { + session_->connection()->SendConnectionClose( + QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA); + return false; + } + } - return accepted; + return sequencer_.OnStreamFrame(frame); } -void ReliableQuicStream::OnStreamReset(QuicRstStreamErrorCode error) { - stream_error_ = error; +int ReliableQuicStream::num_frames_received() const { + return sequencer_.num_frames_received(); +} + +int ReliableQuicStream::num_duplicate_frames_received() const { + return sequencer_.num_duplicate_frames_received(); +} + +void ReliableQuicStream::OnStreamReset(const QuicRstStreamFrame& frame) { + rst_received_ = true; + MaybeIncreaseHighestReceivedOffset(frame.byte_offset); + + stream_error_ = frame.error_code; CloseWriteSide(); CloseReadSide(); } @@ -100,7 +240,8 @@ void ReliableQuicStream::Reset(QuicRstStreamErrorCode error) { DCHECK_NE(QUIC_STREAM_NO_ERROR, error); stream_error_ = error; // Sending a RstStream results in calling CloseStream. - session()->SendRstStream(id(), error); + session()->SendRstStream(id(), error, stream_bytes_written_); + rst_sent_ = true; } void ReliableQuicStream::CloseConnection(QuicErrorCode error) { @@ -112,51 +253,95 @@ void ReliableQuicStream::CloseConnectionWithDetails(QuicErrorCode error, session()->connection()->SendConnectionCloseWithDetails(error, details); } -QuicVersion ReliableQuicStream::version() { +QuicVersion ReliableQuicStream::version() const { return session()->connection()->version(); } -void ReliableQuicStream::WriteOrBufferData(StringPiece data, bool fin) { - DCHECK(data.size() > 0 || fin); - DCHECK(!fin_buffered_); +void ReliableQuicStream::WriteOrBufferData( + StringPiece data, + bool fin, + QuicAckNotifier::DelegateInterface* ack_notifier_delegate) { + if (data.empty() && !fin) { + LOG(DFATAL) << "data.empty() && !fin"; + return; + } + + if (fin_buffered_) { + LOG(DFATAL) << "Fin already buffered"; + return; + } + + scoped_refptr<ProxyAckNotifierDelegate> proxy_delegate; + if (ack_notifier_delegate != NULL) { + proxy_delegate = new ProxyAckNotifierDelegate(ack_notifier_delegate); + } QuicConsumedData consumed_data(0, false); fin_buffered_ = fin; if (queued_data_.empty()) { struct iovec iov(MakeIovec(data)); - consumed_data = WritevData(&iov, 1, fin, NULL); + consumed_data = WritevData(&iov, 1, fin, proxy_delegate.get()); DCHECK_LE(consumed_data.bytes_consumed, data.length()); } + bool write_completed; // If there's unconsumed data or an unconsumed fin, queue it. if (consumed_data.bytes_consumed < data.length() || (fin && !consumed_data.fin_consumed)) { - queued_data_.push_back( - string(data.data() + consumed_data.bytes_consumed, - data.length() - consumed_data.bytes_consumed)); + StringPiece remainder(data.substr(consumed_data.bytes_consumed)); + queued_data_.push_back(PendingData(remainder.as_string(), proxy_delegate)); + write_completed = false; + } else { + write_completed = true; + } + + if ((proxy_delegate.get() != NULL) && + (consumed_data.bytes_consumed > 0 || consumed_data.fin_consumed)) { + proxy_delegate->WroteData(write_completed); } } void ReliableQuicStream::OnCanWrite() { bool fin = false; while (!queued_data_.empty()) { - const string& data = queued_data_.front(); + PendingData* pending_data = &queued_data_.front(); + ProxyAckNotifierDelegate* delegate = pending_data->delegate.get(); if (queued_data_.size() == 1 && fin_buffered_) { fin = true; } - struct iovec iov(MakeIovec(data)); - QuicConsumedData consumed_data = WritevData(&iov, 1, fin, NULL); - if (consumed_data.bytes_consumed == data.size() && + struct iovec iov(MakeIovec(pending_data->data)); + QuicConsumedData consumed_data = WritevData(&iov, 1, fin, delegate); + if (consumed_data.bytes_consumed == pending_data->data.size() && fin == consumed_data.fin_consumed) { queued_data_.pop_front(); + if (delegate != NULL) { + delegate->WroteData(true); + } } else { - queued_data_.front().erase(0, consumed_data.bytes_consumed); + if (consumed_data.bytes_consumed > 0) { + pending_data->data.erase(0, consumed_data.bytes_consumed); + if (delegate != NULL) { + delegate->WroteData(false); + } + } break; } } } +void ReliableQuicStream::MaybeSendBlocked() { + flow_controller_.MaybeSendBlocked(); + connection_flow_controller_->MaybeSendBlocked(); + // If we are connection level flow control blocked, then add the stream + // to the write blocked list. It will be given a chance to write when a + // connection level WINDOW_UPDATE arrives. + if (connection_flow_controller_->IsBlocked() && + !flow_controller_.IsBlocked()) { + session_->MarkWriteBlocked(id(), EffectivePriority()); + } +} + QuicConsumedData ReliableQuicStream::WritevData( const struct iovec* iov, int iov_count, @@ -167,14 +352,50 @@ QuicConsumedData ReliableQuicStream::WritevData( return QuicConsumedData(0, false); } - size_t write_length = 0u; - for (int i = 0; i < iov_count; ++i) { - write_length += iov[i].iov_len; + // How much data we want to write. + size_t write_length = TotalIovecLength(iov, iov_count); + + // A FIN with zero data payload should not be flow control blocked. + bool fin_with_zero_data = (fin && write_length == 0); + + if (flow_controller_.IsEnabled()) { + // How much data we are allowed to write from flow control. + uint64 send_window = flow_controller_.SendWindowSize(); + if (connection_flow_controller_->IsEnabled()) { + send_window = + min(send_window, connection_flow_controller_->SendWindowSize()); + } + + if (send_window == 0 && !fin_with_zero_data) { + // Quick return if we can't send anything. + MaybeSendBlocked(); + return QuicConsumedData(0, false); + } + + if (write_length > send_window) { + // Don't send the FIN if we aren't going to send all the data. + fin = false; + + // Writing more data would be a violation of flow control. + write_length = send_window; + } } + + // Fill an IOVector with bytes from the iovec. + IOVector data; + data.AppendIovecAtMostBytes(iov, iov_count, write_length); + QuicConsumedData consumed_data = session()->WritevData( - id(), iov, iov_count, stream_bytes_written_, fin, ack_notifier_delegate); + id(), data, stream_bytes_written_, fin, GetFecProtection(), + ack_notifier_delegate); stream_bytes_written_ += consumed_data.bytes_consumed; + + AddBytesSent(consumed_data.bytes_consumed); + if (consumed_data.bytes_consumed == write_length) { + if (!fin_with_zero_data) { + MaybeSendBlocked(); + } if (fin && consumed_data.fin_consumed) { fin_sent_ = true; CloseWriteSide(); @@ -187,6 +408,10 @@ QuicConsumedData ReliableQuicStream::WritevData( return consumed_data; } +FecProtection ReliableQuicStream::GetFecProtection() { + return fec_policy_ == FEC_PROTECT_ALWAYS ? MUST_FEC_PROTECT : MAY_FEC_PROTECT; +} + void ReliableQuicStream::CloseReadSide() { if (read_side_closed_) { return; @@ -213,13 +438,88 @@ void ReliableQuicStream::CloseWriteSide() { } } -bool ReliableQuicStream::HasBufferedData() { +bool ReliableQuicStream::HasBufferedData() const { return !queued_data_.empty(); } void ReliableQuicStream::OnClose() { CloseReadSide(); CloseWriteSide(); + + if (!fin_sent_ && !rst_sent_) { + // For flow control accounting, we must tell the peer how many bytes we have + // written on this stream before termination. Done here if needed, using a + // RST frame. + DVLOG(1) << ENDPOINT << "Sending RST in OnClose: " << id(); + session_->SendRstStream(id(), QUIC_RST_FLOW_CONTROL_ACCOUNTING, + stream_bytes_written_); + rst_sent_ = true; + } + + // We are closing the stream and will not process any further incoming bytes. + // As there may be more bytes in flight and we need to ensure that both + // endpoints have the same connection level flow control state, mark all + // unreceived or buffered bytes as consumed. + uint64 bytes_to_consume = flow_controller_.highest_received_byte_offset() - + flow_controller_.bytes_consumed(); + AddBytesConsumed(bytes_to_consume); +} + +void ReliableQuicStream::OnWindowUpdateFrame( + const QuicWindowUpdateFrame& frame) { + if (!flow_controller_.IsEnabled()) { + DLOG(DFATAL) << "Flow control not enabled! " << version(); + return; + } + + if (flow_controller_.UpdateSendWindowOffset(frame.byte_offset)) { + // We can write again! + // TODO(rjshade): This does not respect priorities (e.g. multiple + // outstanding POSTs are unblocked on arrival of + // SHLO with initial window). + // As long as the connection is not flow control blocked, we can write! + OnCanWrite(); + } +} + +bool ReliableQuicStream::MaybeIncreaseHighestReceivedOffset(uint64 new_offset) { + if (flow_controller_.IsEnabled()) { + uint64 increment = + new_offset - flow_controller_.highest_received_byte_offset(); + if (flow_controller_.UpdateHighestReceivedOffset(new_offset)) { + // If |new_offset| increased the stream flow controller's highest received + // offset, then we need to increase the connection flow controller's value + // by the incremental difference. + connection_flow_controller_->UpdateHighestReceivedOffset( + connection_flow_controller_->highest_received_byte_offset() + + increment); + return true; + } + } + return false; +} + +void ReliableQuicStream::AddBytesSent(uint64 bytes) { + if (flow_controller_.IsEnabled()) { + flow_controller_.AddBytesSent(bytes); + connection_flow_controller_->AddBytesSent(bytes); + } +} + +void ReliableQuicStream::AddBytesConsumed(uint64 bytes) { + if (flow_controller_.IsEnabled()) { + // Only adjust stream level flow controller if we are still reading. + if (!read_side_closed_) { + flow_controller_.AddBytesConsumed(bytes); + } + + connection_flow_controller_->AddBytesConsumed(bytes); + } +} + +bool ReliableQuicStream::IsFlowControlBlocked() { + return flow_controller_.IsBlocked() || + connection_flow_controller_->IsBlocked(); } } // namespace net diff --git a/chromium/net/quic/reliable_quic_stream.h b/chromium/net/quic/reliable_quic_stream.h index 7b0a79068d5..be89a284e4a 100644 --- a/chromium/net/quic/reliable_quic_stream.h +++ b/chromium/net/quic/reliable_quic_stream.h @@ -11,13 +11,16 @@ #include <list> +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" #include "base/strings/string_piece.h" #include "net/base/iovec.h" #include "net/base/net_export.h" #include "net/quic/quic_ack_notifier.h" +#include "net/quic/quic_flow_controller.h" #include "net/quic/quic_protocol.h" -#include "net/quic/quic_spdy_compressor.h" #include "net/quic/quic_stream_sequencer.h" +#include "net/quic/quic_types.h" namespace net { @@ -25,9 +28,7 @@ namespace test { class ReliableQuicStreamPeer; } // namespace test -class IPEndPoint; class QuicSession; -class SSLInfo; class NET_EXPORT_PRIVATE ReliableQuicStream { public: @@ -36,8 +37,6 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { virtual ~ReliableQuicStream(); - bool WillAcceptStreamFrame(const QuicStreamFrame& frame) const; - // Called when a (potentially duplicate) stream frame has been received // for this stream. Returns false if this frame can not be accepted // because there is too much data already buffered. @@ -51,7 +50,7 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { virtual void OnClose(); // Called when we get a stream reset from the peer. - virtual void OnStreamReset(QuicRstStreamErrorCode error); + virtual void OnStreamReset(const QuicRstStreamFrame& frame); // Called when we get or send a connection close, and should immediately // close the stream. This is not passed through the sequencer, @@ -83,15 +82,52 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { bool read_side_closed() const { return read_side_closed_; } bool write_side_closed() const { return write_side_closed_; } - uint64 stream_bytes_read() { return stream_bytes_read_; } - uint64 stream_bytes_written() { return stream_bytes_written_; } + uint64 stream_bytes_read() const { return stream_bytes_read_; } + uint64 stream_bytes_written() const { return stream_bytes_written_; } + + QuicVersion version() const; + + void set_fin_sent(bool fin_sent) { fin_sent_ = fin_sent; } + void set_rst_sent(bool rst_sent) { rst_sent_ = rst_sent; } + + // Adjust our flow control windows according to new offset in |frame|. + virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame); + + int num_frames_received() const; + + int num_duplicate_frames_received() const; - QuicVersion version(); + QuicFlowController* flow_controller() { return &flow_controller_; } + + // Called when we see a frame which could increase the highest offset. + // Returns true if the highest offset did increase. + bool MaybeIncreaseHighestReceivedOffset(uint64 new_offset); + // Called when bytese are sent to the peer. + void AddBytesSent(uint64 bytes); + // Called by the stream sequencer as bytes are consumed from the buffer. + // If our receive window has dropped below the threshold, then send a + // WINDOW_UPDATE frame. + void AddBytesConsumed(uint64 bytes); + + // Returns true if the stream is flow control blocked, by the stream flow + // control window or the connection flow control window. + bool IsFlowControlBlocked(); + + // Returns true if we have received either a RST or a FIN - either of which + // gives a definitive number of bytes which the peer has sent. If this is not + // true on stream termination the session must keep track of the stream's byte + // offset until a definitive final value arrives. + bool HasFinalReceivedByteOffset() const { + return fin_received_ || rst_received_; + } protected: // Sends as much of 'data' to the connection as the connection will consume, // and then buffers any remaining data in queued_data_. - void WriteOrBufferData(base::StringPiece data, bool fin); + void WriteOrBufferData( + base::StringPiece data, + bool fin, + QuicAckNotifier::DelegateInterface* ack_notifier_delegate); // Sends as many bytes in the first |count| buffers of |iov| to the connection // as the connection will consume. @@ -104,26 +140,53 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { bool fin, QuicAckNotifier::DelegateInterface* ack_notifier_delegate); + // Helper method that returns FecProtection to use for writes to the session. + FecProtection GetFecProtection(); + // Close the read side of the socket. Further frames will not be accepted. virtual void CloseReadSide(); // Close the write side of the socket. Further writes will fail. void CloseWriteSide(); - bool HasBufferedData(); + bool HasBufferedData() const; - bool fin_buffered() { return fin_buffered_; } + bool fin_buffered() const { return fin_buffered_; } + void set_fec_policy(FecPolicy fec_policy) { fec_policy_ = fec_policy; } + + const QuicSession* session() const { return session_; } QuicSession* session() { return session_; } const QuicStreamSequencer* sequencer() const { return &sequencer_; } QuicStreamSequencer* sequencer() { return &sequencer_; } + void DisableFlowControl() { + flow_controller_.Disable(); + } + private: friend class test::ReliableQuicStreamPeer; friend class QuicStreamUtils; + class ProxyAckNotifierDelegate; + + struct PendingData { + PendingData(string data_in, + scoped_refptr<ProxyAckNotifierDelegate> delegate_in); + ~PendingData(); - std::list<string> queued_data_; + string data; + // Delegate that should be notified when the pending data is acked. + // Can be nullptr. + scoped_refptr<ProxyAckNotifierDelegate> delegate; + }; + + // Calls MaybeSendBlocked on our flow controller, and connection level flow + // controller. If we are flow control blocked, marks this stream as write + // blocked. + void MaybeSendBlocked(); + + std::list<PendingData> queued_data_; QuicStreamSequencer sequencer_; QuicStreamId id_; @@ -149,9 +212,28 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { bool fin_buffered_; bool fin_sent_; + // True if this stream has received (and the sequencer has accepted) a + // StreamFrame with the FIN set. + bool fin_received_; + + // In combination with fin_sent_, used to ensure that a FIN and/or a RST is + // always sent before stream termination. + bool rst_sent_; + + // True if this stream has received a RST stream frame. + bool rst_received_; + + // FEC policy to be used for this stream. + FecPolicy fec_policy_; + // True if the session this stream is running under is a server session. bool is_server_; + QuicFlowController flow_controller_; + + // The connection level flow controller. Not owned. + QuicFlowController* connection_flow_controller_; + DISALLOW_COPY_AND_ASSIGN(ReliableQuicStream); }; diff --git a/chromium/net/quic/reliable_quic_stream_test.cc b/chromium/net/quic/reliable_quic_stream_test.cc index 7e82f533142..b673a4eef5d 100644 --- a/chromium/net/quic/reliable_quic_stream_test.cc +++ b/chromium/net/quic/reliable_quic_stream_test.cc @@ -6,22 +6,30 @@ #include "net/quic/quic_ack_notifier.h" #include "net/quic/quic_connection.h" -#include "net/quic/quic_spdy_compressor.h" -#include "net/quic/quic_spdy_decompressor.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_utils.h" +#include "net/quic/quic_write_blocked_list.h" #include "net/quic/spdy_utils.h" +#include "net/quic/test_tools/quic_config_peer.h" +#include "net/quic/test_tools/quic_connection_peer.h" +#include "net/quic/test_tools/quic_flow_controller_peer.h" #include "net/quic/test_tools/quic_session_peer.h" #include "net/quic/test_tools/quic_test_utils.h" +#include "net/quic/test_tools/reliable_quic_stream_peer.h" +#include "net/test/gtest_util.h" #include "testing/gmock/include/gmock/gmock.h" +#include "testing/gmock_mutant.h" using base::StringPiece; using std::min; -using testing::_; +using testing::CreateFunctor; using testing::InSequence; +using testing::Invoke; using testing::Return; using testing::SaveArg; -using testing::StrEq; using testing::StrictMock; +using testing::WithArgs; +using testing::_; namespace net { namespace test { @@ -30,7 +38,6 @@ namespace { const char kData1[] = "FooAndBar"; const char kData2[] = "EepAndBaz"; const size_t kDataLen = 9; -const QuicGuid kStreamId = 3; const bool kIsServer = true; const bool kShouldProcessData = true; @@ -56,8 +63,7 @@ class TestStream : public ReliableQuicStream { using ReliableQuicStream::WriteOrBufferData; using ReliableQuicStream::CloseReadSide; using ReliableQuicStream::CloseWriteSide; - - const string& data() const { return data_; } + using ReliableQuicStream::OnClose; private: bool should_process_data_; @@ -66,7 +72,10 @@ class TestStream : public ReliableQuicStream { class ReliableQuicStreamTest : public ::testing::TestWithParam<bool> { public: - ReliableQuicStreamTest() { + ReliableQuicStreamTest() + : initial_flow_control_window_bytes_(kMaxPacketSize), + zero_(QuicTime::Delta::Zero()), + supported_versions_(QuicSupportedVersions()) { headers_[":host"] = "www.google.com"; headers_[":path"] = "/index.hml"; headers_[":scheme"] = "https"; @@ -96,71 +105,86 @@ class ReliableQuicStreamTest : public ::testing::TestWithParam<bool> { "JBCScs_ejbKaqBDoB7ZGxTvqlrB__2ZmnHHjCr8RgMRtKNtIeuZAo "; } + void set_supported_versions(const QuicVersionVector& versions) { + supported_versions_ = versions; + } + void Initialize(bool stream_should_process_data) { - connection_ = new StrictMock<MockConnection>(kIsServer); + connection_ = + new StrictMock<MockConnection>(kIsServer, supported_versions_); session_.reset(new StrictMock<MockSession>(connection_)); - stream_.reset(new TestStream(kStreamId, session_.get(), - stream_should_process_data)); - stream2_.reset(new TestStream(kStreamId + 2, session_.get(), + + // New streams rely on having the peer's flow control receive window + // negotiated in the config. + QuicConfigPeer::SetReceivedInitialFlowControlWindow( + session_->config(), initial_flow_control_window_bytes_); + QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow( + session_->config(), initial_flow_control_window_bytes_); + + stream_.reset(new TestStream(kHeadersStreamId, session_.get(), stream_should_process_data)); - compressor_.reset(new QuicSpdyCompressor()); - decompressor_.reset(new QuicSpdyDecompressor); write_blocked_list_ = - QuicSessionPeer::GetWriteblockedStreams(session_.get()); + QuicSessionPeer::GetWriteBlockedStreams(session_.get()); + } + + bool fin_sent() { return ReliableQuicStreamPeer::FinSent(stream_.get()); } + bool rst_sent() { return ReliableQuicStreamPeer::RstSent(stream_.get()); } + + void set_initial_flow_control_window_bytes(uint32 val) { + initial_flow_control_window_bytes_ = val; + } + + bool HasWriteBlockedStreams() { + return write_blocked_list_->HasWriteBlockedCryptoOrHeadersStream() || + write_blocked_list_->HasWriteBlockedDataStreams(); } protected: MockConnection* connection_; scoped_ptr<MockSession> session_; scoped_ptr<TestStream> stream_; - scoped_ptr<TestStream> stream2_; - scoped_ptr<QuicSpdyCompressor> compressor_; - scoped_ptr<QuicSpdyDecompressor> decompressor_; SpdyHeaderBlock headers_; - WriteBlockedList<QuicStreamId>* write_blocked_list_; + QuicWriteBlockedList* write_blocked_list_; + uint32 initial_flow_control_window_bytes_; + QuicTime::Delta zero_; + QuicVersionVector supported_versions_; }; TEST_F(ReliableQuicStreamTest, WriteAllData) { Initialize(kShouldProcessData); - connection_->options()->max_packet_length = - 1 + QuicPacketCreator::StreamFramePacketOverhead( - connection_->version(), PACKET_8BYTE_GUID, !kIncludeVersion, - PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); - EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _, _)).WillOnce( + size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead( + connection_->version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, 0u, NOT_IN_FEC_GROUP); + QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length( + length); + + EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)).WillOnce( Return(QuicConsumedData(kDataLen, true))); - stream_->WriteOrBufferData(kData1, false); - EXPECT_FALSE(write_blocked_list_->HasWriteBlockedStreams()); + stream_->WriteOrBufferData(kData1, false, NULL); + EXPECT_FALSE(HasWriteBlockedStreams()); } -// TODO(rtenneti): Death tests crash on OS_ANDROID. -#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG) && !defined(OS_ANDROID) TEST_F(ReliableQuicStreamTest, NoBlockingIfNoDataOrFin) { Initialize(kShouldProcessData); // Write no data and no fin. If we consume nothing we should not be write // blocked. - EXPECT_DEBUG_DEATH({ - EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _, _)).WillOnce( - Return(QuicConsumedData(0, false))); - stream_->WriteOrBufferData(StringPiece(), false); - EXPECT_FALSE(write_blocked_list_->HasWriteBlockedStreams()); - }, ""); + EXPECT_DFATAL(stream_->WriteOrBufferData(StringPiece(), false, NULL), ""); + EXPECT_FALSE(HasWriteBlockedStreams()); } -#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) && !defined(OS_ANDROID) TEST_F(ReliableQuicStreamTest, BlockIfOnlySomeDataConsumed) { Initialize(kShouldProcessData); // Write some data and no fin. If we consume some but not all of the data, // we should be write blocked a not all the data was consumed. - EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _, _)).WillOnce( - Return(QuicConsumedData(1, false))); - stream_->WriteOrBufferData(StringPiece(kData1, 2), false); + EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(1, false))); + stream_->WriteOrBufferData(StringPiece(kData1, 2), false, NULL); ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams()); } - TEST_F(ReliableQuicStreamTest, BlockIfFinNotConsumedWithData) { Initialize(kShouldProcessData); @@ -168,9 +192,9 @@ TEST_F(ReliableQuicStreamTest, BlockIfFinNotConsumedWithData) { // we should be write blocked because the fin was not consumed. // (This should never actually happen as the fin should be sent out with the // last data) - EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _, _)).WillOnce( - Return(QuicConsumedData(2, false))); - stream_->WriteOrBufferData(StringPiece(kData1, 2), true); + EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(2, false))); + stream_->WriteOrBufferData(StringPiece(kData1, 2), true, NULL); ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams()); } @@ -179,38 +203,112 @@ TEST_F(ReliableQuicStreamTest, BlockIfSoloFinNotConsumed) { // Write no data and a fin. If we consume nothing we should be write blocked, // as the fin was not consumed. - EXPECT_CALL(*session_, WritevData(kStreamId, _, 1, _, _, _)).WillOnce( - Return(QuicConsumedData(0, false))); - stream_->WriteOrBufferData(StringPiece(), true); + EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(0, false))); + stream_->WriteOrBufferData(StringPiece(), true, NULL); ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams()); } TEST_F(ReliableQuicStreamTest, WriteOrBufferData) { Initialize(kShouldProcessData); - EXPECT_FALSE(write_blocked_list_->HasWriteBlockedStreams()); - connection_->options()->max_packet_length = - 1 + QuicPacketCreator::StreamFramePacketOverhead( - connection_->version(), PACKET_8BYTE_GUID, !kIncludeVersion, - PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); - EXPECT_CALL(*session_, WritevData(_, _, 1, _, _, _)).WillOnce( + EXPECT_FALSE(HasWriteBlockedStreams()); + size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead( + connection_->version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, 0u, NOT_IN_FEC_GROUP); + QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length( + length); + + EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).WillOnce( Return(QuicConsumedData(kDataLen - 1, false))); - stream_->WriteOrBufferData(kData1, false); - EXPECT_TRUE(write_blocked_list_->HasWriteBlockedStreams()); + stream_->WriteOrBufferData(kData1, false, NULL); + EXPECT_TRUE(HasWriteBlockedStreams()); // Queue a bytes_consumed write. - stream_->WriteOrBufferData(kData2, false); + stream_->WriteOrBufferData(kData2, false, NULL); // Make sure we get the tail of the first write followed by the bytes_consumed InSequence s; - EXPECT_CALL(*session_, WritevData(_, _, 1, _, _, _)). + EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)). WillOnce(Return(QuicConsumedData(1, false))); - EXPECT_CALL(*session_, WritevData(_, _, 1, _, _, _)). + EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)). WillOnce(Return(QuicConsumedData(kDataLen - 2, false))); stream_->OnCanWrite(); // And finally the end of the bytes_consumed. - EXPECT_CALL(*session_, WritevData(_, _, 1, _, _, _)). + EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)). + WillOnce(Return(QuicConsumedData(2, true))); + stream_->OnCanWrite(); +} + +TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithFecProtectAlways) { + Initialize(kShouldProcessData); + + // Set FEC policy on stream. + ReliableQuicStreamPeer::SetFecPolicy(stream_.get(), FEC_PROTECT_ALWAYS); + + EXPECT_FALSE(HasWriteBlockedStreams()); + size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead( + connection_->version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, 0u, IN_FEC_GROUP); + QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length( + length); + + // Write first data onto stream, which will cause one session write. + EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).WillOnce( + Return(QuicConsumedData(kDataLen - 1, false))); + stream_->WriteOrBufferData(kData1, false, NULL); + EXPECT_TRUE(HasWriteBlockedStreams()); + + // Queue a bytes_consumed write. + stream_->WriteOrBufferData(kData2, false, NULL); + + // Make sure we get the tail of the first write followed by the bytes_consumed + InSequence s; + EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)). + WillOnce(Return(QuicConsumedData(1, false))); + EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)). + WillOnce(Return(QuicConsumedData(kDataLen - 2, false))); + stream_->OnCanWrite(); + + // And finally the end of the bytes_consumed. + EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)). + WillOnce(Return(QuicConsumedData(2, true))); + stream_->OnCanWrite(); +} + +TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithFecProtectOptional) { + Initialize(kShouldProcessData); + + // Set FEC policy on stream. + ReliableQuicStreamPeer::SetFecPolicy(stream_.get(), FEC_PROTECT_OPTIONAL); + + EXPECT_FALSE(HasWriteBlockedStreams()); + size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead( + connection_->version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, 0u, NOT_IN_FEC_GROUP); + QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length( + length); + + // Write first data onto stream, which will cause one session write. + EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).WillOnce( + Return(QuicConsumedData(kDataLen - 1, false))); + stream_->WriteOrBufferData(kData1, false, NULL); + EXPECT_TRUE(HasWriteBlockedStreams()); + + // Queue a bytes_consumed write. + stream_->WriteOrBufferData(kData2, false, NULL); + + // Make sure we get the tail of the first write followed by the bytes_consumed + InSequence s; + EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)). + WillOnce(Return(QuicConsumedData(1, false))); + EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)). + WillOnce(Return(QuicConsumedData(kDataLen - 2, false))); + stream_->OnCanWrite(); + + // And finally the end of the bytes_consumed. + EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)). WillOnce(Return(QuicConsumedData(2, true))); stream_->OnCanWrite(); } @@ -227,6 +325,363 @@ TEST_F(ReliableQuicStreamTest, ConnectionCloseAfterStreamClose) { EXPECT_EQ(QUIC_NO_ERROR, stream_->connection_error()); } +TEST_F(ReliableQuicStreamTest, RstAlwaysSentIfNoFinSent) { + // For flow control accounting, a stream must send either a FIN or a RST frame + // before termination. + // Test that if no FIN has been sent, we send a RST. + + Initialize(kShouldProcessData); + EXPECT_FALSE(fin_sent()); + EXPECT_FALSE(rst_sent()); + + // Write some data, with no FIN. + EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(1, false))); + stream_->WriteOrBufferData(StringPiece(kData1, 1), false, NULL); + EXPECT_FALSE(fin_sent()); + EXPECT_FALSE(rst_sent()); + + // Now close the stream, and expect that we send a RST. + EXPECT_CALL(*session_, SendRstStream(_, _, _)); + stream_->OnClose(); + EXPECT_FALSE(fin_sent()); + EXPECT_TRUE(rst_sent()); +} + +TEST_F(ReliableQuicStreamTest, RstNotSentIfFinSent) { + // For flow control accounting, a stream must send either a FIN or a RST frame + // before termination. + // Test that if a FIN has been sent, we don't also send a RST. + + Initialize(kShouldProcessData); + EXPECT_FALSE(fin_sent()); + EXPECT_FALSE(rst_sent()); + + // Write some data, with FIN. + EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(1, true))); + stream_->WriteOrBufferData(StringPiece(kData1, 1), true, NULL); + EXPECT_TRUE(fin_sent()); + EXPECT_FALSE(rst_sent()); + + // Now close the stream, and expect that we do not send a RST. + stream_->OnClose(); + EXPECT_TRUE(fin_sent()); + EXPECT_FALSE(rst_sent()); +} + +TEST_F(ReliableQuicStreamTest, OnlySendOneRst) { + // For flow control accounting, a stream must send either a FIN or a RST frame + // before termination. + // Test that if a stream sends a RST, it doesn't send an additional RST during + // OnClose() (this shouldn't be harmful, but we shouldn't do it anyway...) + + Initialize(kShouldProcessData); + EXPECT_FALSE(fin_sent()); + EXPECT_FALSE(rst_sent()); + + // Reset the stream. + const int expected_resets = 1; + EXPECT_CALL(*session_, SendRstStream(_, _, _)).Times(expected_resets); + stream_->Reset(QUIC_STREAM_CANCELLED); + EXPECT_FALSE(fin_sent()); + EXPECT_TRUE(rst_sent()); + + // Now close the stream (any further resets being sent would break the + // expectation above). + stream_->OnClose(); + EXPECT_FALSE(fin_sent()); + EXPECT_TRUE(rst_sent()); +} + +TEST_F(ReliableQuicStreamTest, StreamFlowControlMultipleWindowUpdates) { + ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true); + set_initial_flow_control_window_bytes(1000); + + Initialize(kShouldProcessData); + + // If we receive multiple WINDOW_UPDATES (potentially out of order), then we + // want to make sure we latch the largest offset we see. + + // Initially should be default. + EXPECT_EQ( + initial_flow_control_window_bytes_, + QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller())); + + // Check a single WINDOW_UPDATE results in correct offset. + QuicWindowUpdateFrame window_update_1(stream_->id(), 1234); + stream_->OnWindowUpdateFrame(window_update_1); + EXPECT_EQ( + window_update_1.byte_offset, + QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller())); + + // Now send a few more WINDOW_UPDATES and make sure that only the largest is + // remembered. + QuicWindowUpdateFrame window_update_2(stream_->id(), 1); + QuicWindowUpdateFrame window_update_3(stream_->id(), 9999); + QuicWindowUpdateFrame window_update_4(stream_->id(), 5678); + stream_->OnWindowUpdateFrame(window_update_2); + stream_->OnWindowUpdateFrame(window_update_3); + stream_->OnWindowUpdateFrame(window_update_4); + EXPECT_EQ( + window_update_3.byte_offset, + QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller())); +} + +TEST_F(ReliableQuicStreamTest, StreamFlowControlShouldNotBlockInLessThanQ017) { + // TODO(rjshade): Remove this test when we no longer have any versions < + // QUIC_VERSION_17. + ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true); + + // Make sure we are using a version which does not support flow control. + QuicVersion kTestQuicVersions[] = {QUIC_VERSION_16}; + QuicVersionVector versions; + for (size_t i = 0; i < arraysize(kTestQuicVersions); ++i) { + versions.push_back(kTestQuicVersions[i]); + } + set_supported_versions(versions); + + // Peer is not talking QUIC_VERSION_17 so assumes that it can send a zero + // length flow control receive window with no consequences. + set_initial_flow_control_window_bytes(0); + + Initialize(kShouldProcessData); + + // The stream should _not_ be flow control blocked, because we are not talking + // a version which has flow control enabled. + EXPECT_FALSE(stream_->flow_controller()->IsBlocked()); +} + +void SaveProxyAckNotifierDelegate( + scoped_refptr<QuicAckNotifier::DelegateInterface>* delegate_out, + QuicAckNotifier::DelegateInterface* delegate) { + *delegate_out = delegate; +} + +TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithQuicAckNotifier) { + Initialize(kShouldProcessData); + + scoped_refptr<MockAckNotifierDelegate> delegate( + new StrictMock<MockAckNotifierDelegate>); + + const int kDataSize = 16 * 1024; + const string kData(kDataSize, 'a'); + + const int kFirstWriteSize = 100; + const int kSecondWriteSize = 50; + const int kLastWriteSize = kDataSize - kFirstWriteSize - kSecondWriteSize; + + // Set a large flow control send window so this doesn't interfere with test. + stream_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1); + if (FLAGS_enable_quic_connection_flow_control_2) { + session_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1); + } + + scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate; + + EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor( + &SaveProxyAckNotifierDelegate, &proxy_delegate))), + Return(QuicConsumedData(kFirstWriteSize, false)))); + stream_->WriteOrBufferData(kData, false, delegate.get()); + EXPECT_TRUE(HasWriteBlockedStreams()); + + EXPECT_CALL(*session_, + WritevData(kHeadersStreamId, _, _, _, _, proxy_delegate.get())) + .WillOnce(Return(QuicConsumedData(kSecondWriteSize, false))); + stream_->OnCanWrite(); + + // No ack expected for an empty write. + EXPECT_CALL(*session_, + WritevData(kHeadersStreamId, _, _, _, _, proxy_delegate.get())) + .WillOnce(Return(QuicConsumedData(0, false))); + stream_->OnCanWrite(); + + EXPECT_CALL(*session_, + WritevData(kHeadersStreamId, _, _, _, _, proxy_delegate.get())) + .WillOnce(Return(QuicConsumedData(kLastWriteSize, false))); + stream_->OnCanWrite(); + + // There were two writes, so OnAckNotification is not propagated + // until the third Ack arrives. + proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_); + proxy_delegate->OnAckNotification(10, 20, 30, 40, zero_); + + // The arguments to delegate->OnAckNotification are the sum of the + // arguments to proxy_delegate OnAckNotification calls. + EXPECT_CALL(*delegate, OnAckNotification(111, 222, 333, 444, zero_)); + proxy_delegate->OnAckNotification(100, 200, 300, 400, zero_); +} + +// Verify delegate behavior when packets are acked before the +// WritevData call that sends out the last byte. +TEST_F(ReliableQuicStreamTest, WriteOrBufferDataAckNotificationBeforeFlush) { + Initialize(kShouldProcessData); + + scoped_refptr<MockAckNotifierDelegate> delegate( + new StrictMock<MockAckNotifierDelegate>); + + const int kDataSize = 16 * 1024; + const string kData(kDataSize, 'a'); + + const int kInitialWriteSize = 100; + + // Set a large flow control send window so this doesn't interfere with test. + stream_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1); + if (FLAGS_enable_quic_connection_flow_control_2) { + session_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1); + } + + scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate; + + EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor( + &SaveProxyAckNotifierDelegate, &proxy_delegate))), + Return(QuicConsumedData(kInitialWriteSize, false)))); + stream_->WriteOrBufferData(kData, false, delegate.get()); + EXPECT_TRUE(HasWriteBlockedStreams()); + + // Handle the ack of the first write. + proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_); + proxy_delegate = NULL; + + EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)).WillOnce( + DoAll(WithArgs<5>(Invoke(CreateFunctor( + &SaveProxyAckNotifierDelegate, &proxy_delegate))), + Return(QuicConsumedData(kDataSize - kInitialWriteSize, false)))); + stream_->OnCanWrite(); + + // Handle the ack for the second write. + EXPECT_CALL(*delegate, OnAckNotification(101, 202, 303, 404, zero_)); + proxy_delegate->OnAckNotification(100, 200, 300, 400, zero_); +} + +// Verify delegate behavior when WriteOrBufferData does not buffer. +TEST_F(ReliableQuicStreamTest, WriteAndBufferDataWithAckNotiferNoBuffer) { + Initialize(kShouldProcessData); + + scoped_refptr<MockAckNotifierDelegate> delegate( + new StrictMock<MockAckNotifierDelegate>); + + scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate; + + EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor( + &SaveProxyAckNotifierDelegate, &proxy_delegate))), + Return(QuicConsumedData(kDataLen, true)))); + stream_->WriteOrBufferData(kData1, true, delegate.get()); + EXPECT_FALSE(HasWriteBlockedStreams()); + + // Handle the ack. + EXPECT_CALL(*delegate, OnAckNotification(1, 2, 3, 4, zero_)); + proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_); +} + +// Verify delegate behavior when WriteOrBufferData buffers all the data. +TEST_F(ReliableQuicStreamTest, BufferOnWriteAndBufferDataWithAckNotifer) { + Initialize(kShouldProcessData); + + scoped_refptr<MockAckNotifierDelegate> delegate( + new StrictMock<MockAckNotifierDelegate>); + + scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate; + + EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(0, false))); + stream_->WriteOrBufferData(kData1, true, delegate.get()); + EXPECT_TRUE(HasWriteBlockedStreams()); + + EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor( + &SaveProxyAckNotifierDelegate, &proxy_delegate))), + Return(QuicConsumedData(kDataLen, true)))); + stream_->OnCanWrite(); + + // Handle the ack. + EXPECT_CALL(*delegate, OnAckNotification(1, 2, 3, 4, zero_)); + proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_); +} + +// Verify delegate behavior when WriteOrBufferData when the FIN is +// sent out in a different packet. +TEST_F(ReliableQuicStreamTest, WriteAndBufferDataWithAckNotiferOnlyFinRemains) { + Initialize(kShouldProcessData); + + scoped_refptr<MockAckNotifierDelegate> delegate( + new StrictMock<MockAckNotifierDelegate>); + + scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate; + + EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor( + &SaveProxyAckNotifierDelegate, &proxy_delegate))), + Return(QuicConsumedData(kDataLen, false)))); + stream_->WriteOrBufferData(kData1, true, delegate.get()); + EXPECT_TRUE(HasWriteBlockedStreams()); + + EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor( + &SaveProxyAckNotifierDelegate, &proxy_delegate))), + Return(QuicConsumedData(0, true)))); + stream_->OnCanWrite(); + + // Handle the acks. + proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_); + EXPECT_CALL(*delegate, OnAckNotification(11, 22, 33, 44, zero_)); + proxy_delegate->OnAckNotification(10, 20, 30, 40, zero_); +} + +// Verify that when we receive a packet which violates flow control (i.e. sends +// too much data on the stream) that the stream sequencer never sees this frame, +// as we check for violation and close the connection early. +TEST_F(ReliableQuicStreamTest, + StreamSequencerNeverSeesPacketsViolatingFlowControl) { + ValueRestore<bool> old_stream_flag( + &FLAGS_enable_quic_stream_flow_control_2, true); + ValueRestore<bool> old_connection_flag( + &FLAGS_enable_quic_connection_flow_control_2, true); + + Initialize(kShouldProcessData); + + // Receive a stream frame that violates flow control: the byte offset is + // higher than the receive window offset. + QuicStreamFrame frame(stream_->id(), false, + kInitialSessionFlowControlWindowForTest + 1, + MakeIOVector(".")); + EXPECT_GT(frame.offset, QuicFlowControllerPeer::ReceiveWindowOffset( + stream_->flow_controller())); + + // Stream should not accept the frame, and the connection should be closed. + EXPECT_CALL(*connection_, + SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA)); + EXPECT_FALSE(stream_->OnStreamFrame(frame)); +} + +TEST_F(ReliableQuicStreamTest, FinalByteOffsetFromFin) { + Initialize(kShouldProcessData); + + EXPECT_FALSE(stream_->HasFinalReceivedByteOffset()); + + QuicStreamFrame stream_frame_no_fin(stream_->id(), false, 1234, + MakeIOVector(".")); + stream_->OnStreamFrame(stream_frame_no_fin); + EXPECT_FALSE(stream_->HasFinalReceivedByteOffset()); + + QuicStreamFrame stream_frame_with_fin(stream_->id(), true, 1234, + MakeIOVector(".")); + stream_->OnStreamFrame(stream_frame_with_fin); + EXPECT_TRUE(stream_->HasFinalReceivedByteOffset()); +} + +TEST_F(ReliableQuicStreamTest, FinalByteOffsetFromRst) { + Initialize(kShouldProcessData); + + EXPECT_FALSE(stream_->HasFinalReceivedByteOffset()); + QuicRstStreamFrame rst_frame(stream_->id(), QUIC_STREAM_CANCELLED, 1234); + stream_->OnStreamReset(rst_frame); + EXPECT_TRUE(stream_->HasFinalReceivedByteOffset()); +} + } // namespace } // namespace test } // namespace net diff --git a/chromium/net/quic/spdy_utils.cc b/chromium/net/quic/spdy_utils.cc index 350819e9143..c0894d54e6d 100644 --- a/chromium/net/quic/spdy_utils.cc +++ b/chromium/net/quic/spdy_utils.cc @@ -16,7 +16,7 @@ namespace net { // static string SpdyUtils::SerializeUncompressedHeaders(const SpdyHeaderBlock& headers) { int length = SpdyFramer::GetSerializedLength(SPDY3, &headers); - SpdyFrameBuilder builder(length); + SpdyFrameBuilder builder(length, SPDY3); SpdyFramer::WriteHeaderBlock(&builder, SPDY3, &headers); scoped_ptr<SpdyFrame> block(builder.take()); return string(block->data(), length); diff --git a/chromium/net/quic/spdy_utils.h b/chromium/net/quic/spdy_utils.h index ffc78f08da2..14d33002216 100644 --- a/chromium/net/quic/spdy_utils.h +++ b/chromium/net/quic/spdy_utils.h @@ -16,6 +16,9 @@ class NET_EXPORT_PRIVATE SpdyUtils { public: static std::string SerializeUncompressedHeaders( const SpdyHeaderBlock& headers); + + private: + DISALLOW_COPY_AND_ASSIGN(SpdyUtils); }; } // namespace net diff --git a/chromium/net/quic/test_tools/crypto_test_utils.cc b/chromium/net/quic/test_tools/crypto_test_utils.cc index a6b6cc1ed85..0dbf906c868 100644 --- a/chromium/net/quic/test_tools/crypto_test_utils.cc +++ b/chromium/net/quic/test_tools/crypto_test_utils.cc @@ -15,6 +15,7 @@ #include "net/quic/quic_crypto_client_stream.h" #include "net/quic/quic_crypto_server_stream.h" #include "net/quic/quic_crypto_stream.h" +#include "net/quic/quic_server_id.h" #include "net/quic/test_tools/quic_connection_peer.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/quic/test_tools/simple_quic_framer.h" @@ -30,6 +31,9 @@ namespace test { namespace { +const char kServerHostname[] = "test.example.com"; +const uint16 kServerPort = 80; + // CryptoFramerVisitor is a framer visitor that records handshake messages. class CryptoFramerVisitor : public CryptoFramerVisitorInterface { public: @@ -37,9 +41,7 @@ class CryptoFramerVisitor : public CryptoFramerVisitorInterface { : error_(false) { } - virtual void OnError(CryptoFramer* framer) OVERRIDE { - error_ = true; - } + virtual void OnError(CryptoFramer* framer) OVERRIDE { error_ = true; } virtual void OnHandshakeMessage( const CryptoHandshakeMessage& message) OVERRIDE { @@ -67,7 +69,7 @@ void MovePackets(PacketSavingConnection* source_conn, size_t *inout_packet_index, QuicCryptoStream* dest_stream, PacketSavingConnection* dest_conn) { - SimpleQuicFramer framer; + SimpleQuicFramer framer(source_conn->supported_versions()); CryptoFramer crypto_framer; CryptoFramerVisitor crypto_visitor; @@ -133,7 +135,8 @@ CryptoTestUtils::FakeClientOptions::FakeClientOptions() int CryptoTestUtils::HandshakeWithFakeServer( PacketSavingConnection* client_conn, QuicCryptoClientStream* client) { - PacketSavingConnection* server_conn = new PacketSavingConnection(true); + PacketSavingConnection* server_conn = + new PacketSavingConnection(true, client_conn->supported_versions()); TestSession server_session(server_conn, DefaultQuicConfig()); QuicCryptoServerConfig crypto_config(QuicCryptoServerConfig::TESTING, @@ -162,7 +165,7 @@ int CryptoTestUtils::HandshakeWithFakeClient( QuicCryptoServerStream* server, const FakeClientOptions& options) { PacketSavingConnection* client_conn = new PacketSavingConnection(false); - TestSession client_session(client_conn, DefaultQuicConfig()); + TestClientSession client_session(client_conn, DefaultQuicConfig()); QuicCryptoClientConfig crypto_config; client_session.config()->SetDefaults(); @@ -172,9 +175,11 @@ int CryptoTestUtils::HandshakeWithFakeClient( // crypto_config.SetProofVerifier(ProofVerifierForTesting()); // } if (options.channel_id_enabled) { - crypto_config.SetChannelIDSigner(ChannelIDSignerForTesting()); + crypto_config.SetChannelIDSource(ChannelIDSourceForTesting()); } - QuicCryptoClientStream client("test.example.com", &client_session, + QuicServerId server_id(kServerHostname, kServerPort, false, + PRIVACY_MODE_DISABLED); + QuicCryptoClientStream client(server_id, &client_session, NULL, &crypto_config); client_session.SetCryptoStream(&client); @@ -186,8 +191,13 @@ int CryptoTestUtils::HandshakeWithFakeClient( CompareClientAndServerKeys(&client, server); if (options.channel_id_enabled) { - EXPECT_EQ(crypto_config.channel_id_signer()->GetKeyForHostname( - "test.example.com"), + scoped_ptr<ChannelIDKey> channel_id_key; + QuicAsyncStatus status = + crypto_config.channel_id_source()->GetChannelIDKey(kServerHostname, + &channel_id_key, + NULL); + EXPECT_EQ(QUIC_SUCCESS, status); + EXPECT_EQ(channel_id_key->SerializeKey(), server->crypto_negotiated_params().channel_id); } @@ -230,6 +240,7 @@ void CryptoTestUtils::CommunicateHandshakeMessages( } } +// static pair<size_t, size_t> CryptoTestUtils::AdvanceHandshake( PacketSavingConnection* a_conn, QuicCryptoStream* a, @@ -486,7 +497,7 @@ CryptoHandshakeMessage CryptoTestUtils::BuildMessage(const char* message_tag, valuestr++; len--; - CHECK(len % 2 == 0); + CHECK_EQ(0u, len % 2); scoped_ptr<uint8[]> buf(new uint8[len/2]); for (size_t i = 0; i < len/2; i++) { diff --git a/chromium/net/quic/test_tools/crypto_test_utils.h b/chromium/net/quic/test_tools/crypto_test_utils.h index 60674a9e63d..a226c85ad9c 100644 --- a/chromium/net/quic/test_tools/crypto_test_utils.h +++ b/chromium/net/quic/test_tools/crypto_test_utils.h @@ -10,6 +10,7 @@ #include <utility> #include <vector> +#include "base/basictypes.h" #include "base/logging.h" #include "base/strings/string_piece.h" #include "net/quic/crypto/crypto_framer.h" @@ -18,10 +19,11 @@ namespace net { -class ChannelIDSigner; +class ChannelIDSource; class CommonCertSets; class ProofSource; class ProofVerifier; +class ProofVerifyContext; class QuicClock; class QuicConfig; class QuicCryptoClientStream; @@ -46,8 +48,7 @@ class CryptoTestUtils { bool dont_verify_certs; // If channel_id_enabled is true then the client will attempt to send a - // ChannelID. The key will be the same as is returned by - // ChannelIDSigner's |GetKeyForHostname|. + // ChannelID. bool channel_id_enabled; }; @@ -95,6 +96,10 @@ class CryptoTestUtils { // Returns a |ProofVerifier| that uses the QUIC testing root CA. static ProofVerifier* ProofVerifierForTesting(); + // Returns a |ProofVerifyContext| that must be used with the verifier + // returned by |ProofVerifierForTesting|. + static ProofVerifyContext* ProofVerifyContextForTesting(); + // MockCommonCertSets returns a CommonCertSets that contains a single set with // hash |hash|, consisting of the certificate |cert| at index |index|. static CommonCertSets* MockCommonCertSets(base::StringPiece cert, @@ -127,13 +132,17 @@ class CryptoTestUtils { static CryptoHandshakeMessage BuildMessage(const char* message_tag, va_list ap); - // ChannelIDSignerForTesting returns a ChannelIDSigner that generates keys - // deterministically based on the hostname given in the Sign call. - static ChannelIDSigner* ChannelIDSignerForTesting(); + // ChannelIDSourceForTesting returns a ChannelIDSource that generates keys + // deterministically based on the hostname given in the GetChannelIDKey call. + // This ChannelIDSource works in synchronous mode, i.e., its GetChannelIDKey + // method never returns QUIC_PENDING. + static ChannelIDSource* ChannelIDSourceForTesting(); private: static void CompareClientAndServerKeys(QuicCryptoClientStream* client, QuicCryptoServerStream* server); + + DISALLOW_COPY_AND_ASSIGN(CryptoTestUtils); }; } // namespace test diff --git a/chromium/net/quic/test_tools/crypto_test_utils_chromium.cc b/chromium/net/quic/test_tools/crypto_test_utils_chromium.cc index 8aaef425dac..9deee8388b7 100644 --- a/chromium/net/quic/test_tools/crypto_test_utils_chromium.cc +++ b/chromium/net/quic/test_tools/crypto_test_utils_chromium.cc @@ -22,14 +22,14 @@ class TestProofVerifierChromium : public ProofVerifierChromium { public: TestProofVerifierChromium(CertVerifier* cert_verifier, const std::string& cert_file) - : ProofVerifierChromium(cert_verifier, BoundNetLog()), + : ProofVerifierChromium(cert_verifier), cert_verifier_(cert_verifier) { // Load and install the root for the validated chain. scoped_refptr<X509Certificate> root_cert = ImportCertFromFile(GetTestCertsDirectory(), cert_file); scoped_root_.Reset(root_cert.get()); } - virtual ~TestProofVerifierChromium() { } + virtual ~TestProofVerifierChromium() {} private: ScopedTestRoot scoped_root_; @@ -48,6 +48,11 @@ ProofVerifier* CryptoTestUtils::ProofVerifierForTesting() { return proof_verifier; } +// static +ProofVerifyContext* CryptoTestUtils::ProofVerifyContextForTesting() { + return new ProofVerifyContextChromium(BoundNetLog()); +} + } // namespace test } // namespace net diff --git a/chromium/net/quic/test_tools/crypto_test_utils_nss.cc b/chromium/net/quic/test_tools/crypto_test_utils_nss.cc index 88c87679b93..c5aede89efd 100644 --- a/chromium/net/quic/test_tools/crypto_test_utils_nss.cc +++ b/chromium/net/quic/test_tools/crypto_test_utils_nss.cc @@ -20,31 +20,18 @@ namespace net { namespace test { -// TODO(rtenneti): Implement NSS support ChannelIDSigner. Convert Sign() to be -// asynchronous using completion callback. After porting TestChannelIDSigner, -// implement real ChannelIDSigner. -class TestChannelIDSigner : public ChannelIDSigner { +// TODO(rtenneti): Convert Sign() to be asynchronous using a completion +// callback. +class TestChannelIDKey : public ChannelIDKey { public: - virtual ~TestChannelIDSigner() { - STLDeleteValues(&hostname_to_key_); - } - - // ChannelIDSigner implementation. - - virtual bool Sign(const string& hostname, - StringPiece signed_data, - string* out_key, - string* out_signature) OVERRIDE { - crypto::ECPrivateKey* ecdsa_keypair = HostnameToKey(hostname); - if (!ecdsa_keypair) { - return false; - } + explicit TestChannelIDKey(crypto::ECPrivateKey* ecdsa_keypair) + : ecdsa_keypair_(ecdsa_keypair) {} + virtual ~TestChannelIDKey() {} - *out_key = SerializeKey(ecdsa_keypair->public_key()); - if (out_key->empty()) { - return false; - } + // ChannelIDKey implementation. + virtual bool Sign(StringPiece signed_data, + string* out_signature) const OVERRIDE { unsigned char hash_buf[SHA256_LENGTH]; SECItem hash_item = { siBuffer, hash_buf, sizeof(hash_buf) }; @@ -77,19 +64,49 @@ class TestChannelIDSigner : public ChannelIDSigner { kSignatureLength }; - if (PK11_Sign(ecdsa_keypair->key(), &sig_item, &hash_item) != SECSuccess) { + if (PK11_Sign(ecdsa_keypair_->key(), &sig_item, &hash_item) != SECSuccess) { return false; } *out_signature = signature; return true; } - virtual string GetKeyForHostname(const string& hostname) OVERRIDE { - crypto::ECPrivateKey* ecdsa_keypair = HostnameToKey(hostname); - if (!ecdsa_keypair) { + virtual string SerializeKey() const OVERRIDE { + const SECKEYPublicKey* public_key = ecdsa_keypair_->public_key(); + + // public_key->u.ec.publicValue is an ANSI X9.62 public key which, for + // a P-256 key, is 0x04 (meaning uncompressed) followed by the x and y field + // elements as 32-byte, big-endian numbers. + static const unsigned int kExpectedKeyLength = 65; + + const unsigned char* const data = public_key->u.ec.publicValue.data; + const unsigned int len = public_key->u.ec.publicValue.len; + if (len != kExpectedKeyLength || data[0] != 0x04) { return ""; } - return SerializeKey(ecdsa_keypair->public_key()); + + string key(reinterpret_cast<const char*>(data + 1), kExpectedKeyLength - 1); + return key; + } + + private: + crypto::ECPrivateKey* ecdsa_keypair_; +}; + +class TestChannelIDSource : public ChannelIDSource { + public: + virtual ~TestChannelIDSource() { + STLDeleteValues(&hostname_to_key_); + } + + // ChannelIDSource implementation. + + virtual QuicAsyncStatus GetChannelIDKey( + const string& hostname, + scoped_ptr<ChannelIDKey>* channel_id_key, + ChannelIDSourceCallback* /*callback*/) OVERRIDE { + channel_id_key->reset(new TestChannelIDKey(HostnameToKey(hostname))); + return QUIC_SUCCESS; } private: @@ -109,28 +126,12 @@ class TestChannelIDSigner : public ChannelIDSigner { return keypair; } - static string SerializeKey(const SECKEYPublicKey* public_key) { - // public_key->u.ec.publicValue is an ANSI X9.62 public key which, for - // a P-256 key, is 0x04 (meaning uncompressed) followed by the x and y field - // elements as 32-byte, big-endian numbers. - static const unsigned int kExpectedKeyLength = 65; - - const unsigned char* const data = public_key->u.ec.publicValue.data; - const unsigned int len = public_key->u.ec.publicValue.len; - if (len != kExpectedKeyLength || data[0] != 0x04) { - return ""; - } - - string key(reinterpret_cast<const char*>(data + 1), kExpectedKeyLength - 1); - return key; - } - HostnameToKeyMap hostname_to_key_; }; // static -ChannelIDSigner* CryptoTestUtils::ChannelIDSignerForTesting() { - return new TestChannelIDSigner(); +ChannelIDSource* CryptoTestUtils::ChannelIDSourceForTesting() { + return new TestChannelIDSource(); } } // namespace test diff --git a/chromium/net/quic/test_tools/crypto_test_utils_openssl.cc b/chromium/net/quic/test_tools/crypto_test_utils_openssl.cc index bb08a044aa4..388d257001f 100644 --- a/chromium/net/quic/test_tools/crypto_test_utils_openssl.cc +++ b/chromium/net/quic/test_tools/crypto_test_utils_openssl.cc @@ -30,31 +30,22 @@ namespace net { namespace test { -class TestChannelIDSigner : public ChannelIDSigner { +class TestChannelIDKey : public ChannelIDKey { public: - virtual ~TestChannelIDSigner() { } + explicit TestChannelIDKey(EVP_PKEY* ecdsa_key) : ecdsa_key_(ecdsa_key) {} + virtual ~TestChannelIDKey() OVERRIDE {} - // ChannelIDSigner implementation. - - virtual bool Sign(const string& hostname, - StringPiece signed_data, - string* out_key, - string* out_signature) OVERRIDE { - crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> ecdsa_key( - HostnameToKey(hostname)); - - *out_key = SerializeKey(ecdsa_key.get()); - if (out_key->empty()) { - return false; - } + // ChannelIDKey implementation. + virtual bool Sign(StringPiece signed_data, + string* out_signature) const OVERRIDE { EVP_MD_CTX md_ctx; EVP_MD_CTX_init(&md_ctx); crypto::ScopedOpenSSL<EVP_MD_CTX, EvpMdCtxCleanUp> md_ctx_cleanup(&md_ctx); if (EVP_DigestSignInit(&md_ctx, NULL, EVP_sha256(), NULL, - ecdsa_key.get()) != 1) { + ecdsa_key_.get()) != 1) { return false; } @@ -94,10 +85,40 @@ class TestChannelIDSigner : public ChannelIDSigner { return true; } - virtual string GetKeyForHostname(const string& hostname) OVERRIDE { - crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> ecdsa_key( - HostnameToKey(hostname)); - return SerializeKey(ecdsa_key.get()); + virtual string SerializeKey() const OVERRIDE { + // i2d_PublicKey will produce an ANSI X9.62 public key which, for a P-256 + // key, is 0x04 (meaning uncompressed) followed by the x and y field + // elements as 32-byte, big-endian numbers. + static const int kExpectedKeyLength = 65; + + int len = i2d_PublicKey(ecdsa_key_.get(), NULL); + if (len != kExpectedKeyLength) { + return ""; + } + + uint8 buf[kExpectedKeyLength]; + uint8* derp = buf; + i2d_PublicKey(ecdsa_key_.get(), &derp); + + return string(reinterpret_cast<char*>(buf + 1), kExpectedKeyLength - 1); + } + + private: + crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> ecdsa_key_; +}; + +class TestChannelIDSource : public ChannelIDSource { + public: + virtual ~TestChannelIDSource() {} + + // ChannelIDSource implementation. + + virtual QuicAsyncStatus GetChannelIDKey( + const string& hostname, + scoped_ptr<ChannelIDKey>* channel_id_key, + ChannelIDSourceCallback* /*callback*/) OVERRIDE { + channel_id_key->reset(new TestChannelIDKey(HostnameToKey(hostname))); + return QUIC_SUCCESS; } private: @@ -143,29 +164,11 @@ class TestChannelIDSigner : public ChannelIDSigner { return pkey.release(); } - - static string SerializeKey(EVP_PKEY* key) { - // i2d_PublicKey will produce an ANSI X9.62 public key which, for a P-256 - // key, is 0x04 (meaning uncompressed) followed by the x and y field - // elements as 32-byte, big-endian numbers. - static const int kExpectedKeyLength = 65; - - int len = i2d_PublicKey(key, NULL); - if (len != kExpectedKeyLength) { - return ""; - } - - uint8 buf[kExpectedKeyLength]; - uint8* derp = buf; - i2d_PublicKey(key, &derp); - - return string(reinterpret_cast<char*>(buf + 1), kExpectedKeyLength - 1); - } }; // static -ChannelIDSigner* CryptoTestUtils::ChannelIDSignerForTesting() { - return new TestChannelIDSigner(); +ChannelIDSource* CryptoTestUtils::ChannelIDSourceForTesting() { + return new TestChannelIDSource(); } } // namespace test diff --git a/chromium/net/quic/test_tools/delayed_verify_strike_register_client.cc b/chromium/net/quic/test_tools/delayed_verify_strike_register_client.cc index eec16b16e8d..b14a1180296 100644 --- a/chromium/net/quic/test_tools/delayed_verify_strike_register_client.cc +++ b/chromium/net/quic/test_tools/delayed_verify_strike_register_client.cc @@ -29,7 +29,7 @@ void DelayedVerifyStrikeRegisterClient::VerifyNonceIsValidAndUnique( QuicWallTime now, ResultCallback* cb) { if (delay_verifications_) { - pending_verifications_.push_back(VerifyArgs(nonce.as_string(), now, cb)); + pending_verifications_.push_back(VerifyArgs(nonce, now, cb)); } else { LocalStrikeRegisterClient::VerifyNonceIsValidAndUnique(nonce, now, cb); } diff --git a/chromium/net/quic/test_tools/delayed_verify_strike_register_client.h b/chromium/net/quic/test_tools/delayed_verify_strike_register_client.h index 69de1d077d3..6ae7ba1ee2a 100644 --- a/chromium/net/quic/test_tools/delayed_verify_strike_register_client.h +++ b/chromium/net/quic/test_tools/delayed_verify_strike_register_client.h @@ -43,7 +43,9 @@ class DelayedVerifyStrikeRegisterClient : public LocalStrikeRegisterClient { VerifyArgs(base::StringPiece in_nonce, QuicWallTime in_now, ResultCallback* in_cb) - : nonce(in_nonce.as_string()), now(in_now), cb(in_cb) { + : nonce(in_nonce.as_string()), + now(in_now), + cb(in_cb) { } std::string nonce; diff --git a/chromium/net/quic/test_tools/mock_clock.h b/chromium/net/quic/test_tools/mock_clock.h index 5c9631ae84c..d6e490f8961 100644 --- a/chromium/net/quic/test_tools/mock_clock.h +++ b/chromium/net/quic/test_tools/mock_clock.h @@ -16,7 +16,6 @@ namespace net { class MockClock : public QuicClock { public: MockClock(); - virtual ~MockClock(); void AdvanceTime(QuicTime::Delta delta); @@ -31,6 +30,8 @@ class MockClock : public QuicClock { private: QuicTime now_; + + DISALLOW_COPY_AND_ASSIGN(MockClock); }; } // namespace net diff --git a/chromium/net/quic/test_tools/mock_crypto_client_stream.cc b/chromium/net/quic/test_tools/mock_crypto_client_stream.cc index bfeb06f0629..a4b3ab0289d 100644 --- a/chromium/net/quic/test_tools/mock_crypto_client_stream.cc +++ b/chromium/net/quic/test_tools/mock_crypto_client_stream.cc @@ -3,17 +3,25 @@ // found in the LICENSE file. #include "net/quic/test_tools/mock_crypto_client_stream.h" + +#include "net/quic/crypto/quic_decrypter.h" +#include "net/quic/quic_client_session_base.h" +#include "net/quic/quic_server_id.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { MockCryptoClientStream::MockCryptoClientStream( - const string& server_hostname, - QuicSession* session, + const QuicServerId& server_id, + QuicClientSessionBase* session, + ProofVerifyContext* verify_context, QuicCryptoClientConfig* crypto_config, - HandshakeMode handshake_mode) - : QuicCryptoClientStream(server_hostname, session, crypto_config), - handshake_mode_(handshake_mode) { + HandshakeMode handshake_mode, + const ProofVerifyDetails* proof_verify_details) + : QuicCryptoClientStream(server_id, session, verify_context, + crypto_config), + handshake_mode_(handshake_mode), + proof_verify_details_(proof_verify_details) { } MockCryptoClientStream::~MockCryptoClientStream() { @@ -29,6 +37,8 @@ bool MockCryptoClientStream::CryptoConnect() { case ZERO_RTT: { encryption_established_ = true; handshake_confirmed_ = false; + session()->connection()->SetDecrypter(QuicDecrypter::Create(kNULL), + ENCRYPTION_INITIAL); session()->OnCryptoHandshakeEvent( QuicSession::ENCRYPTION_FIRST_ESTABLISHED); break; @@ -37,7 +47,14 @@ bool MockCryptoClientStream::CryptoConnect() { case CONFIRM_HANDSHAKE: { encryption_established_ = true; handshake_confirmed_ = true; + crypto_negotiated_params_.key_exchange = kC255; + crypto_negotiated_params_.aead = kAESG; + if (proof_verify_details_) { + client_session()->OnProofVerifyDetailsAvailable(*proof_verify_details_); + } SetConfigNegotiated(); + session()->connection()->SetDecrypter(QuicDecrypter::Create(kNULL), + ENCRYPTION_FORWARD_SECURE); session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); break; } @@ -66,7 +83,7 @@ void MockCryptoClientStream::SetConfigNegotiated() { QuicTagVector cgst; cgst.push_back(kINAR); cgst.push_back(kQBIC); - session()->config()->set_congestion_control(cgst, kQBIC); + session()->config()->set_congestion_feedback(cgst, kQBIC); session()->config()->set_idle_connection_state_lifetime( QuicTime::Delta::FromSeconds(2 * kDefaultTimeoutSecs), QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs)); @@ -77,9 +94,13 @@ void MockCryptoClientStream::SetConfigNegotiated() { session()->config()->ToHandshakeMessage(&msg); string error_details; const QuicErrorCode error = - session()->config()->ProcessClientHello(msg, &error_details); + session()->config()->ProcessPeerHello(msg, CLIENT, &error_details); ASSERT_EQ(QUIC_NO_ERROR, error); ASSERT_TRUE(session()->config()->negotiated()); } +QuicClientSessionBase* MockCryptoClientStream::client_session() { + return reinterpret_cast<QuicClientSessionBase*>(session()); +} + } // namespace net diff --git a/chromium/net/quic/test_tools/mock_crypto_client_stream.h b/chromium/net/quic/test_tools/mock_crypto_client_stream.h index ada1b6883ba..e940a1e940c 100644 --- a/chromium/net/quic/test_tools/mock_crypto_client_stream.h +++ b/chromium/net/quic/test_tools/mock_crypto_client_stream.h @@ -14,6 +14,8 @@ namespace net { +class QuicServerId; + class MockCryptoClientStream : public QuicCryptoClientStream { public: // HandshakeMode enumerates the handshake mode MockCryptoClientStream should @@ -34,10 +36,12 @@ class MockCryptoClientStream : public QuicCryptoClientStream { }; MockCryptoClientStream( - const string& server_hostname, - QuicSession* session, + const QuicServerId& server_id, + QuicClientSessionBase* session, + ProofVerifyContext* verify_context, QuicCryptoClientConfig* crypto_config, - HandshakeMode handshake_mode); + HandshakeMode handshake_mode, + const ProofVerifyDetails* proof_verify_details_); virtual ~MockCryptoClientStream(); // CryptoFramerVisitorInterface implementation. @@ -55,6 +59,11 @@ class MockCryptoClientStream : public QuicCryptoClientStream { private: void SetConfigNegotiated(); + QuicClientSessionBase* client_session(); + + const ProofVerifyDetails* proof_verify_details_; + + DISALLOW_COPY_AND_ASSIGN(MockCryptoClientStream); }; } // namespace net diff --git a/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc b/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc index e54fb41e2f5..302ed07e9aa 100644 --- a/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc +++ b/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc @@ -5,7 +5,9 @@ #include "net/quic/test_tools/mock_crypto_client_stream_factory.h" #include "base/lazy_instance.h" +#include "net/quic/quic_client_session.h" #include "net/quic/quic_crypto_client_stream.h" +#include "net/quic/quic_server_id.h" using std::string; @@ -13,16 +15,18 @@ namespace net { MockCryptoClientStreamFactory::MockCryptoClientStreamFactory() : handshake_mode_(MockCryptoClientStream::CONFIRM_HANDSHAKE), - last_stream_(NULL) { + last_stream_(NULL), + proof_verify_details_(NULL) { } QuicCryptoClientStream* MockCryptoClientStreamFactory::CreateQuicCryptoClientStream( - const string& server_hostname, - QuicSession* session, + const QuicServerId& server_id, + QuicClientSession* session, QuicCryptoClientConfig* crypto_config) { - last_stream_ = new MockCryptoClientStream(server_hostname, session, - crypto_config, handshake_mode_); + last_stream_ = new MockCryptoClientStream( + server_id, session, NULL, crypto_config, handshake_mode_, + proof_verify_details_); return last_stream_; } diff --git a/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h b/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h index 9d056cbc72d..721ec258cee 100644 --- a/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h +++ b/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h @@ -13,15 +13,16 @@ namespace net { +class QuicServerId; + class MockCryptoClientStreamFactory : public QuicCryptoClientStreamFactory { public: MockCryptoClientStreamFactory(); - virtual ~MockCryptoClientStreamFactory() {} virtual QuicCryptoClientStream* CreateQuicCryptoClientStream( - const string& server_hostname, - QuicSession* session, + const QuicServerId& server_id, + QuicClientSession* session, QuicCryptoClientConfig* crypto_config) OVERRIDE; void set_handshake_mode( @@ -29,6 +30,11 @@ class MockCryptoClientStreamFactory : public QuicCryptoClientStreamFactory { handshake_mode_ = handshake_mode; } + void set_proof_verify_details( + const ProofVerifyDetails* proof_verify_details) { + proof_verify_details_ = proof_verify_details; + } + MockCryptoClientStream* last_stream() const { return last_stream_; } @@ -36,6 +42,9 @@ class MockCryptoClientStreamFactory : public QuicCryptoClientStreamFactory { private: MockCryptoClientStream::HandshakeMode handshake_mode_; MockCryptoClientStream* last_stream_; + const ProofVerifyDetails* proof_verify_details_; + + DISALLOW_COPY_AND_ASSIGN(MockCryptoClientStreamFactory); }; } // namespace net diff --git a/chromium/net/quic/test_tools/mock_quic_dispatcher.cc b/chromium/net/quic/test_tools/mock_quic_dispatcher.cc new file mode 100644 index 00000000000..f3f41a5ae10 --- /dev/null +++ b/chromium/net/quic/test_tools/mock_quic_dispatcher.cc @@ -0,0 +1,23 @@ +// 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/test_tools/mock_quic_dispatcher.h" + +#include "net/quic/test_tools/quic_test_utils.h" + +namespace net { +namespace test { + +MockQuicDispatcher::MockQuicDispatcher( + const QuicConfig& config, + const QuicCryptoServerConfig& crypto_config, + QuicConnectionHelperInterface* helper) + : QuicDispatcher(config, crypto_config, QuicSupportedVersions(), helper) { +} + +MockQuicDispatcher::~MockQuicDispatcher() { +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/test_tools/mock_quic_dispatcher.h b/chromium/net/quic/test_tools/mock_quic_dispatcher.h new file mode 100644 index 00000000000..93ecda4ce6d --- /dev/null +++ b/chromium/net/quic/test_tools/mock_quic_dispatcher.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_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_ +#define NET_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_ + +#include "net/base/ip_endpoint.h" +#include "net/quic/crypto/quic_crypto_server_config.h" +#include "net/quic/quic_config.h" +#include "net/quic/quic_dispatcher.h" +#include "net/quic/quic_protocol.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace net { +namespace test { + +class MockQuicDispatcher : public QuicDispatcher { + public: + MockQuicDispatcher(const QuicConfig& config, + const QuicCryptoServerConfig& crypto_config, + QuicConnectionHelperInterface* helper); + + virtual ~MockQuicDispatcher(); + + MOCK_METHOD3(ProcessPacket, void(const IPEndPoint& server_address, + const IPEndPoint& client_address, + const QuicEncryptedPacket& packet)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockQuicDispatcher); +}; + +} // namespace test +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_ diff --git a/chromium/net/quic/test_tools/mock_random.h b/chromium/net/quic/test_tools/mock_random.h index 3a3c87f4497..53b24b74eeb 100644 --- a/chromium/net/quic/test_tools/mock_random.h +++ b/chromium/net/quic/test_tools/mock_random.h @@ -14,7 +14,7 @@ class MockRandom : public QuicRandom { public: // Initializes base_ to 0xDEADBEEF. MockRandom(); - MockRandom(uint32 base); + explicit MockRandom(uint32 base); // QuicRandom: // Fills the |data| buffer with a repeating byte, initially 'r'. @@ -32,6 +32,8 @@ class MockRandom : public QuicRandom { private: uint32 base_; uint8 increment_; + + DISALLOW_COPY_AND_ASSIGN(MockRandom); }; } // namespace net diff --git a/chromium/net/quic/test_tools/quic_config_peer.cc b/chromium/net/quic/test_tools/quic_config_peer.cc new file mode 100644 index 00000000000..798209c1855 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_config_peer.cc @@ -0,0 +1,45 @@ +// 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/test_tools/quic_config_peer.h" + +#include "net/quic/quic_config.h" + +namespace net { +namespace test { + +// static +void QuicConfigPeer::SetReceivedInitialWindow(QuicConfig* config, + size_t initial_window) { + config->initial_congestion_window_.SetReceivedValue(initial_window); +} + +// static +void QuicConfigPeer::SetReceivedLossDetection(QuicConfig* config, + QuicTag loss_detection) { + config->loss_detection_.SetReceivedValue(loss_detection); +} + +// static +void QuicConfigPeer::SetReceivedInitialFlowControlWindow(QuicConfig* config, + uint32 window_bytes) { + config->initial_flow_control_window_bytes_.SetReceivedValue(window_bytes); +} + +// static +void QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow( + QuicConfig* config, uint32 window_bytes) { + config->initial_stream_flow_control_window_bytes_.SetReceivedValue( + window_bytes); +} + +// static +void QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow( + QuicConfig* config, uint32 window_bytes) { + config->initial_session_flow_control_window_bytes_.SetReceivedValue( + window_bytes); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/test_tools/quic_config_peer.h b/chromium/net/quic/test_tools/quic_config_peer.h new file mode 100644 index 00000000000..53abb987635 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_config_peer.h @@ -0,0 +1,42 @@ +// 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_TEST_TOOLS_QUIC_CONFIG_PEER_H_ +#define NET_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_ + +#include "net/quic/quic_protocol.h" + +namespace net { + +class QuicConfig; + +namespace test { + +class QuicConfigPeer { + public: + static void SetReceivedInitialWindow(QuicConfig* config, + size_t initial_window); + + static void SetReceivedLossDetection(QuicConfig* config, + QuicTag loss_detection); + + // TODO(rjshade): Remove when removing QUIC_VERSION_19. + static void SetReceivedInitialFlowControlWindow(QuicConfig* config, + uint32 window_bytes); + + static void SetReceivedInitialStreamFlowControlWindow(QuicConfig* config, + uint32 window_bytes); + + static void SetReceivedInitialSessionFlowControlWindow(QuicConfig* config, + uint32 window_bytes); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicConfigPeer); +}; + +} // namespace test + +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_ diff --git a/chromium/net/quic/test_tools/quic_connection_peer.cc b/chromium/net/quic/test_tools/quic_connection_peer.cc index 54410b3bec1..e51fb52480a 100644 --- a/chromium/net/quic/test_tools/quic_connection_peer.cc +++ b/chromium/net/quic/test_tools/quic_connection_peer.cc @@ -11,8 +11,8 @@ #include "net/quic/quic_packet_writer.h" #include "net/quic/quic_received_packet_manager.h" #include "net/quic/test_tools/quic_framer_peer.h" +#include "net/quic/test_tools/quic_packet_generator_peer.h" #include "net/quic/test_tools/quic_sent_packet_manager_peer.h" -#include "net/quic/test_tools/quic_test_writer.h" namespace net { namespace test { @@ -51,7 +51,20 @@ QuicConnectionVisitorInterface* QuicConnectionPeer::GetVisitor( // static QuicPacketCreator* QuicConnectionPeer::GetPacketCreator( QuicConnection* connection) { - return &connection->packet_creator_; + return QuicPacketGeneratorPeer::GetPacketCreator( + &connection->packet_generator_); +} + +// static +QuicPacketGenerator* QuicConnectionPeer::GetPacketGenerator( + QuicConnection* connection) { + return &connection->packet_generator_; +} + +// static +QuicSentPacketManager* QuicConnectionPeer::GetSentPacketManager( + QuicConnection* connection) { + return &connection->sent_packet_manager_; } // static @@ -110,17 +123,6 @@ QuicPacketEntropyHash QuicConnectionPeer::ReceivedEntropyHash( } // static -bool QuicConnectionPeer::IsWriteBlocked(QuicConnection* connection) { - return connection->write_blocked_; -} - -// static -void QuicConnectionPeer::SetIsWriteBlocked(QuicConnection* connection, - bool write_blocked) { - connection->write_blocked_ = write_blocked; -} - -// static bool QuicConnectionPeer::IsServer(QuicConnection* connection) { return connection->is_server_; } @@ -147,7 +149,7 @@ void QuicConnectionPeer::SetPeerAddress(QuicConnection* connection, // static void QuicConnectionPeer::SwapCrypters(QuicConnection* connection, QuicFramer* framer) { - framer->SwapCryptersForTest(&connection->framer_); + QuicFramerPeer::SwapCrypters(framer, &connection->framer_); } // static @@ -173,6 +175,17 @@ QuicAlarm* QuicConnectionPeer::GetAckAlarm(QuicConnection* connection) { } // static +QuicAlarm* QuicConnectionPeer::GetPingAlarm(QuicConnection* connection) { + return connection->ping_alarm_.get(); +} + +// static +QuicAlarm* QuicConnectionPeer::GetResumeWritesAlarm( + QuicConnection* connection) { + return connection->resume_writes_alarm_.get(); +} + +// static QuicAlarm* QuicConnectionPeer::GetRetransmissionAlarm( QuicConnection* connection) { return connection->retransmission_alarm_.get(); @@ -184,12 +197,6 @@ QuicAlarm* QuicConnectionPeer::GetSendAlarm(QuicConnection* connection) { } // static -QuicAlarm* QuicConnectionPeer::GetResumeWritesAlarm( - QuicConnection* connection) { - return connection->resume_writes_alarm_.get(); -} - -// static QuicAlarm* QuicConnectionPeer::GetTimeoutAlarm(QuicConnection* connection) { return connection->timeout_alarm_.get(); } @@ -201,9 +208,26 @@ QuicPacketWriter* QuicConnectionPeer::GetWriter(QuicConnection* connection) { // static void QuicConnectionPeer::SetWriter(QuicConnection* connection, - QuicTestWriter* writer) { + QuicPacketWriter* writer) { connection->writer_ = writer; } +// static +void QuicConnectionPeer::CloseConnection(QuicConnection* connection) { + connection->connected_ = false; +} + +// static +QuicEncryptedPacket* QuicConnectionPeer::GetConnectionClosePacket( + QuicConnection* connection) { + return connection->connection_close_packet_.get(); +} + +// static +void QuicConnectionPeer::SetSupportedVersions(QuicConnection* connection, + QuicVersionVector versions) { + connection->framer_.SetSupportedVersions(versions); +} + } // namespace test } // namespace net diff --git a/chromium/net/quic/test_tools/quic_connection_peer.h b/chromium/net/quic/test_tools/quic_connection_peer.h index fd43661dae5..0cc6fbbdda2 100644 --- a/chromium/net/quic/test_tools/quic_connection_peer.h +++ b/chromium/net/quic/test_tools/quic_connection_peer.h @@ -18,18 +18,19 @@ class QuicAlarm; class QuicConnection; class QuicConnectionHelperInterface; class QuicConnectionVisitorInterface; +class QuicEncryptedPacket; class QuicFecGroup; class QuicFramer; class QuicPacketCreator; +class QuicPacketGenerator; class QuicPacketWriter; class QuicReceivedPacketManager; +class QuicSentPacketManager; class ReceiveAlgorithmInterface; class SendAlgorithmInterface; namespace test { -class QuicTestWriter; - // Peer to make public a number of otherwise private QuicConnection methods. class QuicConnectionPeer { public: @@ -48,6 +49,11 @@ class QuicConnectionPeer { static QuicPacketCreator* GetPacketCreator(QuicConnection* connection); + static QuicPacketGenerator* GetPacketGenerator(QuicConnection* connection); + + static QuicSentPacketManager* GetSentPacketManager( + QuicConnection* connection); + static QuicReceivedPacketManager* GetReceivedPacketManager( QuicConnection* connection); @@ -73,10 +79,6 @@ class QuicConnectionPeer { QuicConnection* connection, QuicPacketSequenceNumber sequence_number); - static bool IsWriteBlocked(QuicConnection* connection); - - static void SetIsWriteBlocked(QuicConnection* connection, bool write_blocked); - static bool IsServer(QuicConnection* connection); static void SetIsServer(QuicConnection* connection, bool is_server); @@ -97,19 +99,27 @@ class QuicConnectionPeer { static QuicFecGroup* GetFecGroup(QuicConnection* connection, int fec_group); static QuicAlarm* GetAckAlarm(QuicConnection* connection); + static QuicAlarm* GetPingAlarm(QuicConnection* connection); + static QuicAlarm* GetResumeWritesAlarm(QuicConnection* connection); static QuicAlarm* GetRetransmissionAlarm(QuicConnection* connection); static QuicAlarm* GetSendAlarm(QuicConnection* connection); - static QuicAlarm* GetResumeWritesAlarm(QuicConnection* connection); static QuicAlarm* GetTimeoutAlarm(QuicConnection* connection); static QuicPacketWriter* GetWriter(QuicConnection* connection); - static void SetWriter(QuicConnection* connection, QuicTestWriter* writer); + static void SetWriter(QuicConnection* connection, QuicPacketWriter* writer); + static void CloseConnection(QuicConnection* connection); + static QuicEncryptedPacket* GetConnectionClosePacket( + QuicConnection* connection); + + static void SetSupportedVersions(QuicConnection* connection, + QuicVersionVector versions); private: DISALLOW_COPY_AND_ASSIGN(QuicConnectionPeer); }; } // namespace test + } // namespace net -#endif // NET_QUIC_TEST_TOOLS_QUIC_TEST_PEER_H_ +#endif // NET_QUIC_TEST_TOOLS_QUIC_CONNECTION_PEER_H_ diff --git a/chromium/net/quic/test_tools/quic_data_stream_peer.h b/chromium/net/quic/test_tools/quic_data_stream_peer.h index bfaf826dcdc..914b87d8686 100644 --- a/chromium/net/quic/test_tools/quic_data_stream_peer.h +++ b/chromium/net/quic/test_tools/quic_data_stream_peer.h @@ -6,7 +6,6 @@ #define NET_QUIC_TEST_TOOLS_QUIC_DATA_STREAM_PEER_H_ #include "base/basictypes.h" -#include "net/quic/quic_protocol.h" namespace net { diff --git a/chromium/net/quic/test_tools/quic_flow_controller_peer.cc b/chromium/net/quic/test_tools/quic_flow_controller_peer.cc new file mode 100644 index 00000000000..35882ede4bf --- /dev/null +++ b/chromium/net/quic/test_tools/quic_flow_controller_peer.cc @@ -0,0 +1,61 @@ +// 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/test_tools/quic_flow_controller_peer.h" + +#include <list> + +#include "net/quic/quic_flow_controller.h" +#include "net/quic/quic_protocol.h" + +namespace net { +namespace test { + +// static +void QuicFlowControllerPeer::SetSendWindowOffset( + QuicFlowController* flow_controller, + uint64 offset) { + flow_controller->send_window_offset_ = offset; +} + +// static +void QuicFlowControllerPeer::SetReceiveWindowOffset( + QuicFlowController* flow_controller, + uint64 offset) { + flow_controller->receive_window_offset_ = offset; +} + +// static +void QuicFlowControllerPeer::SetMaxReceiveWindow( + QuicFlowController* flow_controller, uint64 window_size) { + flow_controller->max_receive_window_ = window_size; +} + +// static +uint64 QuicFlowControllerPeer::SendWindowOffset( + QuicFlowController* flow_controller) { + return flow_controller->send_window_offset_; +} + +// static +uint64 QuicFlowControllerPeer::SendWindowSize( + QuicFlowController* flow_controller) { + return flow_controller->SendWindowSize(); +} + +// static +uint64 QuicFlowControllerPeer::ReceiveWindowOffset( + QuicFlowController* flow_controller) { + return flow_controller->receive_window_offset_; +} + +// static +uint64 QuicFlowControllerPeer::ReceiveWindowSize( + QuicFlowController* flow_controller) { + return flow_controller->receive_window_offset_ - + flow_controller->highest_received_byte_offset_; +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/test_tools/quic_flow_controller_peer.h b/chromium/net/quic/test_tools/quic_flow_controller_peer.h new file mode 100644 index 00000000000..213d40d6fa6 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_flow_controller_peer.h @@ -0,0 +1,42 @@ +// 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_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_ +#define NET_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_ + +#include "net/quic/quic_protocol.h" + +namespace net { + +class QuicFlowController; + +namespace test { + +class QuicFlowControllerPeer { + public: + static void SetSendWindowOffset(QuicFlowController* flow_controller, + uint64 offset); + + static void SetReceiveWindowOffset(QuicFlowController* flow_controller, + uint64 offset); + + static void SetMaxReceiveWindow(QuicFlowController* flow_controller, + uint64 window_size); + + static uint64 SendWindowOffset(QuicFlowController* flow_controller); + + static uint64 SendWindowSize(QuicFlowController* flow_controller); + + static uint64 ReceiveWindowOffset(QuicFlowController* flow_controller); + + static uint64 ReceiveWindowSize(QuicFlowController* flow_controller); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicFlowControllerPeer); +}; + +} // namespace test +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_ diff --git a/chromium/net/quic/test_tools/quic_framer_peer.cc b/chromium/net/quic/test_tools/quic_framer_peer.cc index e8d43cdc10e..c6464cc70f7 100644 --- a/chromium/net/quic/test_tools/quic_framer_peer.cc +++ b/chromium/net/quic/test_tools/quic_framer_peer.cc @@ -20,8 +20,9 @@ QuicPacketSequenceNumber QuicFramerPeer::CalculatePacketSequenceNumberFromWire( } // static -void QuicFramerPeer::SetLastSerializedGuid(QuicFramer* framer, QuicGuid guid) { - framer->last_serialized_guid_ = guid; +void QuicFramerPeer::SetLastSerializedConnectionId( + QuicFramer* framer, QuicConnectionId connection_id) { + framer->last_serialized_connection_id_ = connection_id; } void QuicFramerPeer::SetLastSequenceNumber( @@ -34,5 +35,26 @@ void QuicFramerPeer::SetIsServer(QuicFramer* framer, bool is_server) { framer->is_server_ = is_server; } +void QuicFramerPeer::SwapCrypters(QuicFramer* framer1, QuicFramer* framer2) { + for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; i++) { + framer1->encrypter_[i].swap(framer2->encrypter_[i]); + } + framer1->decrypter_.swap(framer2->decrypter_); + framer1->alternative_decrypter_.swap(framer2->alternative_decrypter_); + + EncryptionLevel framer2_level = framer2->decrypter_level_; + framer2->decrypter_level_ = framer1->decrypter_level_; + framer1->decrypter_level_ = framer2_level; + framer2_level = framer2->alternative_decrypter_level_; + framer2->alternative_decrypter_level_ = + framer1->alternative_decrypter_level_; + framer1->alternative_decrypter_level_ = framer2_level; + + const bool framer2_latch = framer2->alternative_decrypter_latch_; + framer2->alternative_decrypter_latch_ = + framer1->alternative_decrypter_latch_; + framer1->alternative_decrypter_latch_ = framer2_latch; +} + } // namespace test } // namespace net diff --git a/chromium/net/quic/test_tools/quic_framer_peer.h b/chromium/net/quic/test_tools/quic_framer_peer.h index acb45ecb173..9b9c9fa140f 100644 --- a/chromium/net/quic/test_tools/quic_framer_peer.h +++ b/chromium/net/quic/test_tools/quic_framer_peer.h @@ -19,12 +19,17 @@ class QuicFramerPeer { QuicFramer* framer, QuicSequenceNumberLength sequence_number_length, QuicPacketSequenceNumber packet_sequence_number); - static void SetLastSerializedGuid(QuicFramer* framer, QuicGuid guid); + static void SetLastSerializedConnectionId(QuicFramer* framer, + QuicConnectionId connection_id); static void SetLastSequenceNumber( QuicFramer* framer, QuicPacketSequenceNumber packet_sequence_number); static void SetIsServer(QuicFramer* framer, bool is_server); + // SwapCrypters exchanges the state of the crypters of |framer1| with + // |framer2|. + static void SwapCrypters(QuicFramer* framer1, QuicFramer* framer2); + private: DISALLOW_COPY_AND_ASSIGN(QuicFramerPeer); }; diff --git a/chromium/net/quic/test_tools/quic_packet_creator_peer.cc b/chromium/net/quic/test_tools/quic_packet_creator_peer.cc index 1acdf244f2f..a8014479478 100644 --- a/chromium/net/quic/test_tools/quic_packet_creator_peer.cc +++ b/chromium/net/quic/test_tools/quic_packet_creator_peer.cc @@ -33,11 +33,5 @@ QuicSequenceNumberLength QuicPacketCreatorPeer::GetSequenceNumberLength( return creator->sequence_number_length_; } -// static -void QuicPacketCreatorPeer::SetIsServer(QuicPacketCreator* creator, - bool is_server) { - creator->is_server_ = is_server; -} - } // namespace test } // namespace net diff --git a/chromium/net/quic/test_tools/quic_packet_creator_peer.h b/chromium/net/quic/test_tools/quic_packet_creator_peer.h index 12ae676664b..b4bf8a73ba0 100644 --- a/chromium/net/quic/test_tools/quic_packet_creator_peer.h +++ b/chromium/net/quic/test_tools/quic_packet_creator_peer.h @@ -24,8 +24,6 @@ class QuicPacketCreatorPeer { static QuicSequenceNumberLength GetSequenceNumberLength( QuicPacketCreator* creator); - static void SetIsServer(QuicPacketCreator* creator, bool is_server); - private: DISALLOW_COPY_AND_ASSIGN(QuicPacketCreatorPeer); }; diff --git a/chromium/net/quic/test_tools/quic_packet_generator_peer.cc b/chromium/net/quic/test_tools/quic_packet_generator_peer.cc new file mode 100644 index 00000000000..ad0693a355f --- /dev/null +++ b/chromium/net/quic/test_tools/quic_packet_generator_peer.cc @@ -0,0 +1,20 @@ +// 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/test_tools/quic_packet_generator_peer.h" + +#include "net/quic/quic_packet_creator.h" +#include "net/quic/quic_packet_generator.h" + +namespace net { +namespace test { + +// static +QuicPacketCreator* QuicPacketGeneratorPeer::GetPacketCreator( + QuicPacketGenerator* generator) { + return &generator->packet_creator_; +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/test_tools/quic_packet_generator_peer.h b/chromium/net/quic/test_tools/quic_packet_generator_peer.h new file mode 100644 index 00000000000..0762d1f6c58 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_packet_generator_peer.h @@ -0,0 +1,29 @@ +// 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_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_ +#define NET_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_ + +#include "net/quic/quic_protocol.h" + +namespace net { + +class QuicPacketCreator; +class QuicPacketGenerator; + +namespace test { + +class QuicPacketGeneratorPeer { + public: + static QuicPacketCreator* GetPacketCreator(QuicPacketGenerator* generator); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicPacketGeneratorPeer); +}; + +} // namespace test + +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_ diff --git a/chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc b/chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc index d25a209b5f4..2faf84e644e 100644 --- a/chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc +++ b/chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc @@ -11,12 +11,12 @@ namespace net { namespace test { // static -void QuicReceivedPacketManagerPeer::RecalculateEntropyHash( +void QuicReceivedPacketManagerPeer::SetCumulativeEntropyUpTo( QuicReceivedPacketManager* received_packet_manager, QuicPacketSequenceNumber peer_least_unacked, QuicPacketEntropyHash entropy_hash) { - received_packet_manager->RecalculateEntropyHash(peer_least_unacked, - entropy_hash); + received_packet_manager->entropy_tracker_.SetCumulativeEntropyUpTo( + peer_least_unacked, entropy_hash); } // static diff --git a/chromium/net/quic/test_tools/quic_received_packet_manager_peer.h b/chromium/net/quic/test_tools/quic_received_packet_manager_peer.h index 4607a0c902d..c3cb2d3bc2b 100644 --- a/chromium/net/quic/test_tools/quic_received_packet_manager_peer.h +++ b/chromium/net/quic/test_tools/quic_received_packet_manager_peer.h @@ -15,7 +15,7 @@ namespace test { class QuicReceivedPacketManagerPeer { public: - static void RecalculateEntropyHash( + static void SetCumulativeEntropyUpTo( QuicReceivedPacketManager* received_packet_manager, QuicPacketSequenceNumber peer_least_unacked, QuicPacketEntropyHash entropy_hash); diff --git a/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.cc b/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.cc index e2e7c02c1a2..786e63c9b47 100644 --- a/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.cc +++ b/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.cc @@ -5,6 +5,7 @@ #include "net/quic/test_tools/quic_sent_packet_manager_peer.h" #include "base/stl_util.h" +#include "net/quic/congestion_control/loss_detection_interface.h" #include "net/quic/congestion_control/send_algorithm_interface.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_sent_packet_manager.h" @@ -13,6 +14,12 @@ namespace net { namespace test { // static +void QuicSentPacketManagerPeer::SetMaxTailLossProbes( + QuicSentPacketManager* sent_packet_manager, size_t max_tail_loss_probes) { + sent_packet_manager->max_tail_loss_probes_ = max_tail_loss_probes; +} + +// static void QuicSentPacketManagerPeer::SetSendAlgorithm( QuicSentPacketManager* sent_packet_manager, SendAlgorithmInterface* send_algorithm) { @@ -20,27 +27,52 @@ void QuicSentPacketManagerPeer::SetSendAlgorithm( } // static +const LossDetectionInterface* QuicSentPacketManagerPeer::GetLossAlgorithm( + QuicSentPacketManager* sent_packet_manager) { + return sent_packet_manager->loss_algorithm_.get(); +} + +// static +void QuicSentPacketManagerPeer::SetLossAlgorithm( + QuicSentPacketManager* sent_packet_manager, + LossDetectionInterface* loss_detector) { + sent_packet_manager->loss_algorithm_.reset(loss_detector); +} + +// static +RttStats* QuicSentPacketManagerPeer::GetRttStats( + QuicSentPacketManager* sent_packet_manager) { + return &sent_packet_manager->rtt_stats_; +} + +// static size_t QuicSentPacketManagerPeer::GetNackCount( const QuicSentPacketManager* sent_packet_manager, QuicPacketSequenceNumber sequence_number) { - return sent_packet_manager->packet_history_map_.find( - sequence_number)->second->nack_count(); + return sent_packet_manager->unacked_packets_. + GetTransmissionInfo(sequence_number).nack_count; } // static -QuicTime QuicSentPacketManagerPeer::GetSentTime( - const QuicSentPacketManager* sent_packet_manager, - QuicPacketSequenceNumber sequence_number) { - DCHECK(ContainsKey(sent_packet_manager->unacked_packets_, sequence_number)); +size_t QuicSentPacketManagerPeer::GetPendingRetransmissionCount( + const QuicSentPacketManager* sent_packet_manager) { + return sent_packet_manager->pending_retransmissions_.size(); +} - return sent_packet_manager->unacked_packets_ - .find(sequence_number)->second.sent_time; +// static +bool QuicSentPacketManagerPeer::HasPendingPackets( + const QuicSentPacketManager* sent_packet_manager) { + return sent_packet_manager->unacked_packets_.HasInFlightPackets(); } // static -QuicTime::Delta QuicSentPacketManagerPeer::rtt( - QuicSentPacketManager* sent_packet_manager) { - return sent_packet_manager->rtt_sample_; +QuicTime QuicSentPacketManagerPeer::GetSentTime( + const QuicSentPacketManager* sent_packet_manager, + QuicPacketSequenceNumber sequence_number) { + DCHECK(sent_packet_manager->unacked_packets_.IsUnacked(sequence_number)); + + return sent_packet_manager->unacked_packets_.GetTransmissionInfo( + sequence_number).sent_time; } // static @@ -49,8 +81,8 @@ bool QuicSentPacketManagerPeer::IsRetransmission( QuicPacketSequenceNumber sequence_number) { DCHECK(sent_packet_manager->HasRetransmittableFrames(sequence_number)); return sent_packet_manager->HasRetransmittableFrames(sequence_number) && - sent_packet_manager-> - unacked_packets_[sequence_number].previous_transmissions != NULL; + sent_packet_manager->unacked_packets_.GetTransmissionInfo( + sequence_number).all_transmissions->size() > 1; } // static @@ -62,5 +94,37 @@ void QuicSentPacketManagerPeer::MarkForRetransmission( transmission_type); } +// static +QuicTime::Delta QuicSentPacketManagerPeer::GetRetransmissionDelay( + const QuicSentPacketManager* sent_packet_manager) { + return sent_packet_manager->GetRetransmissionDelay(); +} + +// static +bool QuicSentPacketManagerPeer::HasUnackedCryptoPackets( + const QuicSentPacketManager* sent_packet_manager) { + return sent_packet_manager->unacked_packets_.HasPendingCryptoPackets(); +} + +// static +size_t QuicSentPacketManagerPeer::GetNumRetransmittablePackets( + const QuicSentPacketManager* sent_packet_manager) { + size_t num_unacked_packets = 0; + for (QuicUnackedPacketMap::const_iterator it = + sent_packet_manager->unacked_packets_.begin(); + it != sent_packet_manager->unacked_packets_.end(); ++it) { + if (it->second.retransmittable_frames != NULL) { + ++num_unacked_packets; + } + } + return num_unacked_packets; +} + +// static +QuicByteCount QuicSentPacketManagerPeer::GetBytesInFlight( + const QuicSentPacketManager* sent_packet_manager) { + return sent_packet_manager->unacked_packets_.bytes_in_flight(); +} + } // namespace test } // namespace net diff --git a/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.h b/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.h index 2ed9b218621..fed80153d77 100644 --- a/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.h +++ b/chromium/net/quic/test_tools/quic_sent_packet_manager_peer.h @@ -16,18 +16,33 @@ namespace test { class QuicSentPacketManagerPeer { public: + static void SetMaxTailLossProbes( + QuicSentPacketManager* sent_packet_manager, size_t max_tail_loss_probes); + static void SetSendAlgorithm(QuicSentPacketManager* sent_packet_manager, SendAlgorithmInterface* send_algorithm); + static const LossDetectionInterface* GetLossAlgorithm( + QuicSentPacketManager* sent_packet_manager); + + static void SetLossAlgorithm(QuicSentPacketManager* sent_packet_manager, + LossDetectionInterface* loss_detector); + + static RttStats* GetRttStats(QuicSentPacketManager* sent_packet_manager); + static size_t GetNackCount( const QuicSentPacketManager* sent_packet_manager, QuicPacketSequenceNumber sequence_number); + static size_t GetPendingRetransmissionCount( + const QuicSentPacketManager* sent_packet_manager); + + static bool HasPendingPackets( + const QuicSentPacketManager* sent_packet_manager); + static QuicTime GetSentTime(const QuicSentPacketManager* sent_packet_manager, QuicPacketSequenceNumber sequence_number); - static QuicTime::Delta rtt(QuicSentPacketManager* sent_packet_manager); - // Returns true if |sequence_number| is a retransmission of a packet. static bool IsRetransmission(QuicSentPacketManager* sent_packet_manager, QuicPacketSequenceNumber sequence_number); @@ -36,6 +51,18 @@ class QuicSentPacketManagerPeer { QuicPacketSequenceNumber sequence_number, TransmissionType transmission_type); + static QuicTime::Delta GetRetransmissionDelay( + const QuicSentPacketManager* sent_packet_manager); + + static bool HasUnackedCryptoPackets( + const QuicSentPacketManager* sent_packet_manager); + + static size_t GetNumRetransmittablePackets( + const QuicSentPacketManager* sent_packet_manager); + + static QuicByteCount GetBytesInFlight( + const QuicSentPacketManager* sent_packet_manager); + private: DISALLOW_COPY_AND_ASSIGN(QuicSentPacketManagerPeer); }; diff --git a/chromium/net/quic/test_tools/quic_session_peer.cc b/chromium/net/quic/test_tools/quic_session_peer.cc index c25b42fb216..39663fbb7eb 100644 --- a/chromium/net/quic/test_tools/quic_session_peer.cc +++ b/chromium/net/quic/test_tools/quic_session_peer.cc @@ -22,10 +22,28 @@ void QuicSessionPeer::SetMaxOpenStreams(QuicSession* session, } // static -WriteBlockedList<QuicStreamId>* QuicSessionPeer::GetWriteblockedStreams( +QuicHeadersStream* QuicSessionPeer::GetHeadersStream(QuicSession* session) { + return session->headers_stream_.get(); +} + +// static +void QuicSessionPeer::SetHeadersStream(QuicSession* session, + QuicHeadersStream* headers_stream) { + session->headers_stream_.reset(headers_stream); +} + +// static +QuicWriteBlockedList* QuicSessionPeer::GetWriteBlockedStreams( QuicSession* session) { return &session->write_blocked_streams_; } +// static +QuicDataStream* QuicSessionPeer::GetIncomingDataStream( + QuicSession* session, + QuicStreamId stream_id) { + return session->GetIncomingDataStream(stream_id); +} + } // namespace test } // namespace net diff --git a/chromium/net/quic/test_tools/quic_session_peer.h b/chromium/net/quic/test_tools/quic_session_peer.h index fb4529cc0ab..77b376c590b 100644 --- a/chromium/net/quic/test_tools/quic_session_peer.h +++ b/chromium/net/quic/test_tools/quic_session_peer.h @@ -6,12 +6,13 @@ #define NET_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_ #include "net/quic/quic_protocol.h" -#include "net/spdy/write_blocked_list.h" +#include "net/quic/quic_write_blocked_list.h" namespace net { +class QuicDataStream; +class QuicHeadersStream; class QuicSession; -class ReliableQuicStream; namespace test { @@ -19,8 +20,12 @@ class QuicSessionPeer { public: static void SetNextStreamId(QuicSession* session, QuicStreamId id); static void SetMaxOpenStreams(QuicSession* session, uint32 max_streams); - static WriteBlockedList<QuicStreamId>* GetWriteblockedStreams( - QuicSession* session); + static QuicHeadersStream* GetHeadersStream(QuicSession* session); + static void SetHeadersStream(QuicSession* session, + QuicHeadersStream* headers_stream); + static QuicWriteBlockedList* GetWriteBlockedStreams(QuicSession* session); + static QuicDataStream* GetIncomingDataStream(QuicSession* session, + QuicStreamId stream_id); private: DISALLOW_COPY_AND_ASSIGN(QuicSessionPeer); diff --git a/chromium/net/quic/test_tools/quic_stream_sequencer_peer.cc b/chromium/net/quic/test_tools/quic_stream_sequencer_peer.cc new file mode 100644 index 00000000000..08a5c5b0720 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_stream_sequencer_peer.cc @@ -0,0 +1,28 @@ +// 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/test_tools/quic_stream_sequencer_peer.h" + +#include "net/quic/quic_stream_sequencer.h" + +using std::map; +using std::string; + +namespace net { +namespace test { + +// static +map<QuicStreamOffset, string>* QuicStreamSequencerPeer::GetBufferedFrames( + QuicStreamSequencer* sequencer) { + return &(sequencer->buffered_frames_); +} + +// static +QuicStreamOffset QuicStreamSequencerPeer::GetCloseOffset( + QuicStreamSequencer* sequencer) { + return sequencer->close_offset_; +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/test_tools/quic_stream_sequencer_peer.h b/chromium/net/quic/test_tools/quic_stream_sequencer_peer.h new file mode 100644 index 00000000000..7b2b28aad46 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_stream_sequencer_peer.h @@ -0,0 +1,31 @@ +// 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_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_ +#define NET_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_ + +#include "base/basictypes.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +class QuicStreamSequencer; + +namespace test { + +class QuicStreamSequencerPeer { + public: + static std::map<QuicStreamOffset, std::string>* GetBufferedFrames( + QuicStreamSequencer* sequencer); + + static QuicStreamOffset GetCloseOffset(QuicStreamSequencer* sequencer); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicStreamSequencerPeer); +}; + +} // namespace test +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_ diff --git a/chromium/net/quic/test_tools/quic_test_packet_maker.cc b/chromium/net/quic/test_tools/quic_test_packet_maker.cc new file mode 100644 index 00000000000..7f0835f8bdf --- /dev/null +++ b/chromium/net/quic/test_tools/quic_test_packet_maker.cc @@ -0,0 +1,249 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/test_tools/quic_test_packet_maker.h" + +#include "net/quic/quic_framer.h" +#include "net/quic/quic_http_utils.h" +#include "net/quic/quic_utils.h" +#include "net/quic/test_tools/quic_test_utils.h" + +namespace net { +namespace test { + +QuicTestPacketMaker::QuicTestPacketMaker(QuicVersion version, + QuicConnectionId connection_id) + : version_(version), + connection_id_(connection_id), + spdy_request_framer_(SPDY3), + spdy_response_framer_(SPDY3) { +} + +QuicTestPacketMaker::~QuicTestPacketMaker() { +} + +scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeRstPacket( + QuicPacketSequenceNumber num, + bool include_version, + QuicStreamId stream_id, + QuicRstStreamErrorCode error_code) { + QuicPacketHeader header; + header.public_header.connection_id = connection_id_; + header.public_header.reset_flag = false; + header.public_header.version_flag = include_version; + header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; + header.packet_sequence_number = num; + header.entropy_flag = false; + header.fec_flag = false; + header.fec_group = 0; + + QuicRstStreamFrame rst(stream_id, error_code, 0); + return scoped_ptr<QuicEncryptedPacket>(MakePacket(header, QuicFrame(&rst))); +} + +scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeAckAndRstPacket( + QuicPacketSequenceNumber num, + bool include_version, + QuicStreamId stream_id, + QuicRstStreamErrorCode error_code, + QuicPacketSequenceNumber largest_received, + QuicPacketSequenceNumber least_unacked, + bool send_feedback) { + + QuicPacketHeader header; + header.public_header.connection_id = connection_id_; + header.public_header.reset_flag = false; + header.public_header.version_flag = include_version; + header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; + header.packet_sequence_number = num; + header.entropy_flag = false; + header.fec_flag = false; + header.fec_group = 0; + + QuicAckFrame ack(MakeAckFrame(largest_received, least_unacked)); + ack.received_info.delta_time_largest_observed = QuicTime::Delta::Zero(); + QuicFrames frames; + frames.push_back(QuicFrame(&ack)); + QuicCongestionFeedbackFrame feedback; + if (send_feedback) { + feedback.type = kTCP; + feedback.tcp.receive_window = 256000; + + frames.push_back(QuicFrame(&feedback)); + } + + QuicStopWaitingFrame stop_waiting; + if (version_ > QUIC_VERSION_15) { + stop_waiting.least_unacked = least_unacked; + frames.push_back(QuicFrame(&stop_waiting)); + } + + QuicRstStreamFrame rst(stream_id, error_code, 0); + frames.push_back(QuicFrame(&rst)); + + QuicFramer framer(SupportedVersions(version_), QuicTime::Zero(), false); + scoped_ptr<QuicPacket> packet( + BuildUnsizedDataPacket(&framer, header, frames).packet); + return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket( + ENCRYPTION_NONE, header.packet_sequence_number, *packet)); +} + +scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeConnectionClosePacket( + QuicPacketSequenceNumber num) { + QuicPacketHeader header; + header.public_header.connection_id = connection_id_; + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; + header.packet_sequence_number = num; + header.entropy_flag = false; + header.fec_flag = false; + header.fec_group = 0; + + QuicConnectionCloseFrame close; + close.error_code = QUIC_CRYPTO_VERSION_NOT_SUPPORTED; + close.error_details = "Time to panic!"; + return scoped_ptr<QuicEncryptedPacket>(MakePacket(header, QuicFrame(&close))); +} + +scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeAckPacket( + QuicPacketSequenceNumber sequence_number, + QuicPacketSequenceNumber largest_received, + QuicPacketSequenceNumber least_unacked, + bool send_feedback) { + QuicPacketHeader header; + header.public_header.connection_id = connection_id_; + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; + header.packet_sequence_number = sequence_number; + header.entropy_flag = false; + header.fec_flag = false; + header.fec_group = 0; + + QuicAckFrame ack(MakeAckFrame(largest_received, least_unacked)); + ack.received_info.delta_time_largest_observed = QuicTime::Delta::Zero(); + + QuicCongestionFeedbackFrame feedback; + feedback.type = kTCP; + feedback.tcp.receive_window = 256000; + + QuicFramer framer(SupportedVersions(version_), QuicTime::Zero(), false); + QuicFrames frames; + frames.push_back(QuicFrame(&ack)); + if (send_feedback) { + frames.push_back(QuicFrame(&feedback)); + } + + QuicStopWaitingFrame stop_waiting; + if (version_ > QUIC_VERSION_15) { + stop_waiting.least_unacked = least_unacked; + frames.push_back(QuicFrame(&stop_waiting)); + } + + scoped_ptr<QuicPacket> packet( + BuildUnsizedDataPacket(&framer, header, frames).packet); + return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket( + ENCRYPTION_NONE, header.packet_sequence_number, *packet)); +} + +// Returns a newly created packet to send kData on stream 1. +scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeDataPacket( + QuicPacketSequenceNumber sequence_number, + QuicStreamId stream_id, + bool should_include_version, + bool fin, + QuicStreamOffset offset, + base::StringPiece data) { + InitializeHeader(sequence_number, should_include_version); + QuicStreamFrame frame(stream_id, fin, offset, MakeIOVector(data)); + return MakePacket(header_, QuicFrame(&frame)); +} + +scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeRequestHeadersPacket( + QuicPacketSequenceNumber sequence_number, + QuicStreamId stream_id, + bool should_include_version, + bool fin, + const SpdyHeaderBlock& headers) { + InitializeHeader(sequence_number, should_include_version); + SpdySynStreamIR syn_stream(stream_id); + syn_stream.set_name_value_block(headers); + syn_stream.set_fin(fin); + syn_stream.set_priority(0); + scoped_ptr<SpdySerializedFrame> spdy_frame( + spdy_request_framer_.SerializeSynStream(syn_stream)); + QuicStreamFrame frame(kHeadersStreamId, false, 0, + MakeIOVector(base::StringPiece(spdy_frame->data(), + spdy_frame->size()))); + return MakePacket(header_, QuicFrame(&frame)); +} + +scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeResponseHeadersPacket( + QuicPacketSequenceNumber sequence_number, + QuicStreamId stream_id, + bool should_include_version, + bool fin, + const SpdyHeaderBlock& headers) { + InitializeHeader(sequence_number, should_include_version); + SpdySynReplyIR syn_reply(stream_id); + syn_reply.set_name_value_block(headers); + syn_reply.set_fin(fin); + scoped_ptr<SpdySerializedFrame> spdy_frame( + spdy_response_framer_.SerializeSynReply(syn_reply)); + QuicStreamFrame frame(kHeadersStreamId, false, 0, + MakeIOVector(base::StringPiece(spdy_frame->data(), + spdy_frame->size()))); + return MakePacket(header_, QuicFrame(&frame)); +} + +SpdyHeaderBlock QuicTestPacketMaker::GetRequestHeaders( + const std::string& method, + const std::string& scheme, + const std::string& path) { + SpdyHeaderBlock headers; + headers[":method"] = method; + headers[":host"] = "www.google.com"; + headers[":path"] = path; + headers[":scheme"] = scheme; + headers[":version"] = "HTTP/1.1"; + return headers; +} + +SpdyHeaderBlock QuicTestPacketMaker::GetResponseHeaders( + const std::string& status) { + SpdyHeaderBlock headers; + headers[":status"] = status; + headers[":version"] = "HTTP/1.1"; + headers["content-type"] = "text/plain"; + return headers; +} + +scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakePacket( + const QuicPacketHeader& header, + const QuicFrame& frame) { + QuicFramer framer(SupportedVersions(version_), QuicTime::Zero(), false); + QuicFrames frames; + frames.push_back(frame); + scoped_ptr<QuicPacket> packet( + BuildUnsizedDataPacket(&framer, header, frames).packet); + return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket( + ENCRYPTION_NONE, header.packet_sequence_number, *packet)); +} + +void QuicTestPacketMaker::InitializeHeader( + QuicPacketSequenceNumber sequence_number, + bool should_include_version) { + header_.public_header.connection_id = connection_id_; + header_.public_header.reset_flag = false; + header_.public_header.version_flag = should_include_version; + header_.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; + header_.packet_sequence_number = sequence_number; + header_.fec_group = 0; + header_.entropy_flag = false; + header_.fec_flag = false; +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/test_tools/quic_test_packet_maker.h b/chromium/net/quic/test_tools/quic_test_packet_maker.h new file mode 100644 index 00000000000..e4f3ec03300 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_test_packet_maker.h @@ -0,0 +1,91 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Provides a simple interface for QUIC tests to create a variety of packets. + +#ifndef NET_QUIC_TEST_TOOLS_QUIC_TEST_PACKET_MAKER_H_ +#define NET_QUIC_TEST_TOOLS_QUIC_TEST_PACKET_MAKER_H_ + +#include "base/memory/scoped_ptr.h" +#include "net/base/request_priority.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/test_tools/mock_random.h" +#include "net/spdy/spdy_framer.h" +#include "net/spdy/spdy_protocol.h" + +namespace net { +namespace test { + +class QuicTestPacketMaker { + public: + QuicTestPacketMaker(QuicVersion version, QuicConnectionId connection_id); + ~QuicTestPacketMaker(); + + scoped_ptr<QuicEncryptedPacket> MakeRstPacket( + QuicPacketSequenceNumber num, + bool include_version, + QuicStreamId stream_id, + QuicRstStreamErrorCode error_code); + scoped_ptr<QuicEncryptedPacket> MakeAckAndRstPacket( + QuicPacketSequenceNumber num, + bool include_version, + QuicStreamId stream_id, + QuicRstStreamErrorCode error_code, + QuicPacketSequenceNumber largest_received, + QuicPacketSequenceNumber least_unacked, + bool send_feedback); + scoped_ptr<QuicEncryptedPacket> MakeConnectionClosePacket( + QuicPacketSequenceNumber num); + scoped_ptr<QuicEncryptedPacket> MakeAckPacket( + QuicPacketSequenceNumber sequence_number, + QuicPacketSequenceNumber largest_received, + QuicPacketSequenceNumber least_unacked, + bool send_feedback); + scoped_ptr<QuicEncryptedPacket> MakeDataPacket( + QuicPacketSequenceNumber sequence_number, + QuicStreamId stream_id, + bool should_include_version, + bool fin, + QuicStreamOffset offset, + base::StringPiece data); + scoped_ptr<QuicEncryptedPacket> MakeRequestHeadersPacket( + QuicPacketSequenceNumber sequence_number, + QuicStreamId stream_id, + bool should_include_version, + bool fin, + const SpdyHeaderBlock& headers); + scoped_ptr<QuicEncryptedPacket> MakeResponseHeadersPacket( + QuicPacketSequenceNumber sequence_number, + QuicStreamId stream_id, + bool should_include_version, + bool fin, + const SpdyHeaderBlock& headers); + + SpdyHeaderBlock GetRequestHeaders(const std::string& method, + const std::string& scheme, + const std::string& path); + SpdyHeaderBlock GetResponseHeaders(const std::string& status); + + private: + scoped_ptr<QuicEncryptedPacket> MakePacket( + const QuicPacketHeader& header, + const QuicFrame& frame); + + void InitializeHeader(QuicPacketSequenceNumber sequence_number, + bool should_include_version); + + QuicVersion version_; + QuicConnectionId connection_id_; + SpdyFramer spdy_request_framer_; + SpdyFramer spdy_response_framer_; + MockRandom random_generator_; + QuicPacketHeader header_; + + DISALLOW_COPY_AND_ASSIGN(QuicTestPacketMaker); +}; + +} // namespace test +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_TEST_PACKET_MAKER_H_ diff --git a/chromium/net/quic/test_tools/quic_test_utils.cc b/chromium/net/quic/test_tools/quic_test_utils.cc index d85f92fa07e..b6a0744379a 100644 --- a/chromium/net/quic/test_tools/quic_test_utils.cc +++ b/chromium/net/quic/test_tools/quic_test_utils.cc @@ -4,6 +4,7 @@ #include "net/quic/test_tools/quic_test_utils.h" +#include "base/sha1.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "net/quic/crypto/crypto_framer.h" @@ -14,6 +15,7 @@ #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/quic_framer.h" #include "net/quic/quic_packet_creator.h" +#include "net/quic/quic_utils.h" #include "net/quic/test_tools/quic_connection_peer.h" #include "net/spdy/spdy_frame_builder.h" @@ -21,6 +23,7 @@ using base::StringPiece; using std::max; using std::min; using std::string; +using testing::AnyNumber; using testing::_; namespace net { @@ -40,6 +43,54 @@ class TestAlarm : public QuicAlarm { } // namespace +QuicAckFrame MakeAckFrame(QuicPacketSequenceNumber largest_observed, + QuicPacketSequenceNumber least_unacked) { + QuicAckFrame ack; + ack.received_info.largest_observed = largest_observed; + ack.received_info.entropy_hash = 0; + ack.sent_info.least_unacked = least_unacked; + ack.sent_info.entropy_hash = 0; + return ack; +} + +QuicAckFrame MakeAckFrameWithNackRanges( + size_t num_nack_ranges, QuicPacketSequenceNumber least_unacked) { + QuicAckFrame ack = MakeAckFrame(2 * num_nack_ranges + least_unacked, + least_unacked); + // Add enough missing packets to get num_nack_ranges nack ranges. + for (QuicPacketSequenceNumber i = 1; i < 2 * num_nack_ranges; i += 2) { + ack.received_info.missing_packets.insert(least_unacked + i); + } + return ack; +} + +SerializedPacket BuildUnsizedDataPacket(QuicFramer* framer, + const QuicPacketHeader& header, + const QuicFrames& frames) { + const size_t max_plaintext_size = framer->GetMaxPlaintextSize(kMaxPacketSize); + size_t packet_size = GetPacketHeaderSize(header); + for (size_t i = 0; i < frames.size(); ++i) { + DCHECK_LE(packet_size, max_plaintext_size); + bool first_frame = i == 0; + bool last_frame = i == frames.size() - 1; + const size_t frame_size = framer->GetSerializedFrameLength( + frames[i], max_plaintext_size - packet_size, first_frame, last_frame, + header.is_in_fec_group, + header.public_header.sequence_number_length); + DCHECK(frame_size); + packet_size += frame_size; + } + return framer->BuildDataPacket(header, frames, packet_size); +} + +uint64 SimpleRandom::RandUint64() { + unsigned char hash[base::kSHA1Length]; + base::SHA1HashBytes(reinterpret_cast<unsigned char*>(&seed_), sizeof(seed_), + hash); + memcpy(&seed_, hash, sizeof(seed_)); + return seed_; +} + MockFramerVisitor::MockFramerVisitor() { // By default, we want to accept packets. ON_CALL(*this, OnProtocolVersionMismatch(_)) @@ -49,6 +100,9 @@ MockFramerVisitor::MockFramerVisitor() { ON_CALL(*this, OnUnauthenticatedHeader(_)) .WillByDefault(testing::Return(true)); + ON_CALL(*this, OnUnauthenticatedPublicHeader(_)) + .WillByDefault(testing::Return(true)); + ON_CALL(*this, OnPacketHeader(_)) .WillByDefault(testing::Return(true)); @@ -61,6 +115,12 @@ MockFramerVisitor::MockFramerVisitor() { ON_CALL(*this, OnCongestionFeedbackFrame(_)) .WillByDefault(testing::Return(true)); + ON_CALL(*this, OnStopWaitingFrame(_)) + .WillByDefault(testing::Return(true)); + + ON_CALL(*this, OnPingFrame(_)) + .WillByDefault(testing::Return(true)); + ON_CALL(*this, OnRstStreamFrame(_)) .WillByDefault(testing::Return(true)); @@ -78,6 +138,11 @@ bool NoOpFramerVisitor::OnProtocolVersionMismatch(QuicVersion version) { return false; } +bool NoOpFramerVisitor::OnUnauthenticatedPublicHeader( + const QuicPacketPublicHeader& header) { + return true; +} + bool NoOpFramerVisitor::OnUnauthenticatedHeader( const QuicPacketHeader& header) { return true; @@ -100,107 +165,36 @@ bool NoOpFramerVisitor::OnCongestionFeedbackFrame( return true; } -bool NoOpFramerVisitor::OnRstStreamFrame( - const QuicRstStreamFrame& frame) { - return true; -} - -bool NoOpFramerVisitor::OnConnectionCloseFrame( - const QuicConnectionCloseFrame& frame) { +bool NoOpFramerVisitor::OnStopWaitingFrame( + const QuicStopWaitingFrame& frame) { return true; } -bool NoOpFramerVisitor::OnGoAwayFrame(const QuicGoAwayFrame& frame) { +bool NoOpFramerVisitor::OnPingFrame(const QuicPingFrame& frame) { return true; } -FramerVisitorCapturingFrames::FramerVisitorCapturingFrames() : frame_count_(0) { -} - -FramerVisitorCapturingFrames::~FramerVisitorCapturingFrames() { - Reset(); -} - -void FramerVisitorCapturingFrames::Reset() { - STLDeleteElements(&stream_data_); - stream_frames_.clear(); - frame_count_ = 0; - ack_.reset(); - feedback_.reset(); - rst_.reset(); - close_.reset(); - goaway_.reset(); - version_negotiation_packet_.reset(); -} - -bool FramerVisitorCapturingFrames::OnPacketHeader( - const QuicPacketHeader& header) { - header_ = header; - frame_count_ = 0; - return true; -} - -bool FramerVisitorCapturingFrames::OnStreamFrame(const QuicStreamFrame& frame) { - // Make a copy of the frame and store a copy of underlying string, since - // frame.data may not exist outside this callback. - stream_data_.push_back(frame.GetDataAsString()); - QuicStreamFrame frame_copy = frame; - frame_copy.data.Clear(); - frame_copy.data.Append(const_cast<char*>(stream_data_.back()->data()), - stream_data_.back()->size()); - stream_frames_.push_back(frame_copy); - ++frame_count_; - return true; -} - -bool FramerVisitorCapturingFrames::OnAckFrame(const QuicAckFrame& frame) { - ack_.reset(new QuicAckFrame(frame)); - ++frame_count_; - return true; -} - -bool FramerVisitorCapturingFrames::OnCongestionFeedbackFrame( - const QuicCongestionFeedbackFrame& frame) { - feedback_.reset(new QuicCongestionFeedbackFrame(frame)); - ++frame_count_; - return true; -} - -bool FramerVisitorCapturingFrames::OnRstStreamFrame( +bool NoOpFramerVisitor::OnRstStreamFrame( const QuicRstStreamFrame& frame) { - rst_.reset(new QuicRstStreamFrame(frame)); - ++frame_count_; return true; } -bool FramerVisitorCapturingFrames::OnConnectionCloseFrame( +bool NoOpFramerVisitor::OnConnectionCloseFrame( const QuicConnectionCloseFrame& frame) { - close_.reset(new QuicConnectionCloseFrame(frame)); - ++frame_count_; return true; } -bool FramerVisitorCapturingFrames::OnGoAwayFrame(const QuicGoAwayFrame& frame) { - goaway_.reset(new QuicGoAwayFrame(frame)); - ++frame_count_; +bool NoOpFramerVisitor::OnGoAwayFrame(const QuicGoAwayFrame& frame) { return true; } -void FramerVisitorCapturingFrames::OnVersionNegotiationPacket( - const QuicVersionNegotiationPacket& packet) { - version_negotiation_packet_.reset(new QuicVersionNegotiationPacket(packet)); - frame_count_ = 0; -} - -FramerVisitorCapturingPublicReset::FramerVisitorCapturingPublicReset() { -} - -FramerVisitorCapturingPublicReset::~FramerVisitorCapturingPublicReset() { +bool NoOpFramerVisitor::OnWindowUpdateFrame( + const QuicWindowUpdateFrame& frame) { + return true; } -void FramerVisitorCapturingPublicReset::OnPublicResetPacket( - const QuicPublicResetPacket& public_reset) { - public_reset_packet_ = public_reset; +bool NoOpFramerVisitor::OnBlockedFrame(const QuicBlockedFrame& frame) { + return true; } MockConnectionVisitor::MockConnectionVisitor() { @@ -232,8 +226,8 @@ void MockHelper::AdvanceTime(QuicTime::Delta delta) { } MockConnection::MockConnection(bool is_server) - : QuicConnection(kTestGuid, - IPEndPoint(Loopback4(), kTestPort), + : QuicConnection(kTestConnectionId, + IPEndPoint(TestPeerIPAddress(), kTestPort), new testing::NiceMock<MockHelper>(), new testing::NiceMock<MockPacketWriter>(), is_server, QuicSupportedVersions()), @@ -243,7 +237,7 @@ MockConnection::MockConnection(bool is_server) MockConnection::MockConnection(IPEndPoint address, bool is_server) - : QuicConnection(kTestGuid, address, + : QuicConnection(kTestConnectionId, address, new testing::NiceMock<MockHelper>(), new testing::NiceMock<MockPacketWriter>(), is_server, QuicSupportedVersions()), @@ -251,10 +245,10 @@ MockConnection::MockConnection(IPEndPoint address, helper_(helper()) { } -MockConnection::MockConnection(QuicGuid guid, +MockConnection::MockConnection(QuicConnectionId connection_id, bool is_server) - : QuicConnection(guid, - IPEndPoint(Loopback4(), kTestPort), + : QuicConnection(connection_id, + IPEndPoint(TestPeerIPAddress(), kTestPort), new testing::NiceMock<MockHelper>(), new testing::NiceMock<MockPacketWriter>(), is_server, QuicSupportedVersions()), @@ -262,6 +256,17 @@ MockConnection::MockConnection(QuicGuid guid, helper_(helper()) { } +MockConnection::MockConnection(bool is_server, + const QuicVersionVector& supported_versions) + : QuicConnection(kTestConnectionId, + IPEndPoint(TestPeerIPAddress(), kTestPort), + new testing::NiceMock<MockHelper>(), + new testing::NiceMock<MockPacketWriter>(), + is_server, supported_versions), + writer_(QuicConnectionPeer::GetWriter(this)), + helper_(helper()) { +} + MockConnection::~MockConnection() { } @@ -273,6 +278,12 @@ PacketSavingConnection::PacketSavingConnection(bool is_server) : MockConnection(is_server) { } +PacketSavingConnection::PacketSavingConnection( + bool is_server, + const QuicVersionVector& supported_versions) + : MockConnection(is_server, supported_versions) { +} + PacketSavingConnection::~PacketSavingConnection() { STLDeleteElements(&packets_); STLDeleteElements(&encrypted_packets_); @@ -283,8 +294,8 @@ bool PacketSavingConnection::SendOrQueuePacket( const SerializedPacket& packet, TransmissionType transmission_type) { packets_.push_back(packet.packet); - QuicEncryptedPacket* encrypted = - framer_.EncryptPacket(level, packet.sequence_number, *packet.packet); + QuicEncryptedPacket* encrypted = QuicConnectionPeer::GetFramer(this)-> + EncryptPacket(level, packet.sequence_number, *packet.packet); encrypted_packets_.push_back(encrypted); return true; } @@ -298,11 +309,9 @@ MockSession::MockSession(QuicConnection* connection) MockSession::~MockSession() { } -TestSession::TestSession(QuicConnection* connection, - const QuicConfig& config) +TestSession::TestSession(QuicConnection* connection, const QuicConfig& config) : QuicSession(connection, config), - crypto_stream_(NULL) { -} + crypto_stream_(NULL) {} TestSession::~TestSession() {} @@ -314,6 +323,24 @@ QuicCryptoStream* TestSession::GetCryptoStream() { return crypto_stream_; } +TestClientSession::TestClientSession(QuicConnection* connection, + const QuicConfig& config) + : QuicClientSessionBase(connection, + config), + crypto_stream_(NULL) { + EXPECT_CALL(*this, OnProofValid(_)).Times(AnyNumber()); +} + +TestClientSession::~TestClientSession() {} + +void TestClientSession::SetCryptoStream(QuicCryptoStream* stream) { + crypto_stream_ = stream; +} + +QuicCryptoStream* TestClientSession::GetCryptoStream() { + return crypto_stream_; +} + MockPacketWriter::MockPacketWriter() { } @@ -326,6 +353,12 @@ MockSendAlgorithm::MockSendAlgorithm() { MockSendAlgorithm::~MockSendAlgorithm() { } +MockLossAlgorithm::MockLossAlgorithm() { +} + +MockLossAlgorithm::~MockLossAlgorithm() { +} + MockAckNotifierDelegate::MockAckNotifierDelegate() { } @@ -373,16 +406,65 @@ string HexDumpWithMarks(const char* data, int length, } // namespace +IPAddressNumber TestPeerIPAddress() { return Loopback4(); } + QuicVersion QuicVersionMax() { return QuicSupportedVersions().front(); } QuicVersion QuicVersionMin() { return QuicSupportedVersions().back(); } IPAddressNumber Loopback4() { - net::IPAddressNumber addr; - CHECK(net::ParseIPLiteralToNumber("127.0.0.1", &addr)); + IPAddressNumber addr; + CHECK(ParseIPLiteralToNumber("127.0.0.1", &addr)); + return addr; +} + +IPAddressNumber Loopback6() { + IPAddressNumber addr; + CHECK(ParseIPLiteralToNumber("::1", &addr)); return addr; } +void GenerateBody(string* body, int length) { + body->clear(); + body->reserve(length); + for (int i = 0; i < length; ++i) { + body->append(1, static_cast<char>(32 + i % (126 - 32))); + } +} + +QuicEncryptedPacket* ConstructEncryptedPacket( + QuicConnectionId connection_id, + bool version_flag, + bool reset_flag, + QuicPacketSequenceNumber sequence_number, + const string& data) { + QuicPacketHeader header; + header.public_header.connection_id = connection_id; + header.public_header.connection_id_length = PACKET_8BYTE_CONNECTION_ID; + header.public_header.version_flag = version_flag; + header.public_header.reset_flag = reset_flag; + header.public_header.sequence_number_length = PACKET_6BYTE_SEQUENCE_NUMBER; + header.packet_sequence_number = sequence_number; + header.entropy_flag = false; + header.entropy_hash = 0; + header.fec_flag = false; + header.is_in_fec_group = NOT_IN_FEC_GROUP; + header.fec_group = 0; + QuicStreamFrame stream_frame(1, false, 0, MakeIOVector(data)); + QuicFrame frame(&stream_frame); + QuicFrames frames; + frames.push_back(frame); + QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), false); + scoped_ptr<QuicPacket> packet( + BuildUnsizedDataPacket(&framer, header, frames).packet); + EXPECT_TRUE(packet != NULL); + QuicEncryptedPacket* encrypted = framer.EncryptPacket(ENCRYPTION_NONE, + sequence_number, + *packet); + EXPECT_TRUE(encrypted != NULL); + return encrypted; +} + void CompareCharArraysWithHexError( const string& description, const char* actual, @@ -428,7 +510,7 @@ bool DecodeHexString(const base::StringPiece& hex, std::string* bytes) { } static QuicPacket* ConstructPacketFromHandshakeMessage( - QuicGuid guid, + QuicConnectionId connection_id, const CryptoHandshakeMessage& message, bool should_include_version) { CryptoFramer crypto_framer; @@ -436,7 +518,7 @@ static QuicPacket* ConstructPacketFromHandshakeMessage( QuicFramer quic_framer(QuicSupportedVersions(), QuicTime::Zero(), false); QuicPacketHeader header; - header.public_header.guid = guid; + header.public_header.connection_id = connection_id; header.public_header.reset_flag = false; header.public_header.version_flag = should_include_version; header.packet_sequence_number = 1; @@ -451,13 +533,14 @@ static QuicPacket* ConstructPacketFromHandshakeMessage( QuicFrame frame(&stream_frame); QuicFrames frames; frames.push_back(frame); - return quic_framer.BuildUnsizedDataPacket(header, frames).packet; + return BuildUnsizedDataPacket(&quic_framer, header, frames).packet; } -QuicPacket* ConstructHandshakePacket(QuicGuid guid, QuicTag tag) { +QuicPacket* ConstructHandshakePacket(QuicConnectionId connection_id, + QuicTag tag) { CryptoHandshakeMessage message; message.set_tag(tag); - return ConstructPacketFromHandshakeMessage(guid, message, false); + return ConstructPacketFromHandshakeMessage(connection_id, message, false); } size_t GetPacketLengthForOneStream( @@ -470,12 +553,12 @@ size_t GetPacketLengthForOneStream( const size_t stream_length = NullEncrypter().GetCiphertextSize(*payload_length) + QuicPacketCreator::StreamFramePacketOverhead( - version, PACKET_8BYTE_GUID, include_version, - sequence_number_length, is_in_fec_group); + version, PACKET_8BYTE_CONNECTION_ID, include_version, + sequence_number_length, 0u, is_in_fec_group); const size_t ack_length = NullEncrypter().GetCiphertextSize( QuicFramer::GetMinAckFrameSize( version, sequence_number_length, PACKET_1BYTE_SEQUENCE_NUMBER)) + - GetPacketHeaderSize(PACKET_8BYTE_GUID, include_version, + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, include_version, sequence_number_length, is_in_fec_group); if (stream_length < ack_length) { *payload_length = 1 + ack_length - stream_length; @@ -483,42 +566,39 @@ size_t GetPacketLengthForOneStream( return NullEncrypter().GetCiphertextSize(*payload_length) + QuicPacketCreator::StreamFramePacketOverhead( - version, PACKET_8BYTE_GUID, include_version, - sequence_number_length, is_in_fec_group); -} - -// Size in bytes of the stream frame fields for an arbitrary StreamID and -// offset and the last frame in a packet. -size_t GetMinStreamFrameSize(QuicVersion version) { - return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize; + version, PACKET_8BYTE_CONNECTION_ID, include_version, + sequence_number_length, 0u, is_in_fec_group); } -TestEntropyCalculator::TestEntropyCalculator() { } +TestEntropyCalculator::TestEntropyCalculator() {} -TestEntropyCalculator::~TestEntropyCalculator() { } +TestEntropyCalculator::~TestEntropyCalculator() {} QuicPacketEntropyHash TestEntropyCalculator::EntropyHash( QuicPacketSequenceNumber sequence_number) const { return 1u; } -MockEntropyCalculator::MockEntropyCalculator() { } +MockEntropyCalculator::MockEntropyCalculator() {} -MockEntropyCalculator::~MockEntropyCalculator() { } +MockEntropyCalculator::~MockEntropyCalculator() {} QuicConfig DefaultQuicConfig() { QuicConfig config; config.SetDefaults(); + config.SetInitialFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); + config.SetInitialStreamFlowControlWindowToSend( + kInitialStreamFlowControlWindowForTest); + config.SetInitialSessionFlowControlWindowToSend( + kInitialSessionFlowControlWindowForTest); return config; } -bool TestDecompressorVisitor::OnDecompressedData(StringPiece data) { - data.AppendToString(&data_); - return true; -} - -void TestDecompressorVisitor::OnDecompressionError() { - error_ = true; +QuicVersionVector SupportedVersions(QuicVersion version) { + QuicVersionVector versions; + versions.push_back(version); + return versions; } } // namespace test diff --git a/chromium/net/quic/test_tools/quic_test_utils.h b/chromium/net/quic/test_tools/quic_test_utils.h index 06c343189b5..574e7d15c67 100644 --- a/chromium/net/quic/test_tools/quic_test_utils.h +++ b/chromium/net/quic/test_tools/quic_test_utils.h @@ -11,12 +11,13 @@ #include <vector> #include "base/strings/string_piece.h" +#include "net/quic/congestion_control/loss_detection_interface.h" #include "net/quic/congestion_control/send_algorithm_interface.h" #include "net/quic/quic_ack_notifier.h" +#include "net/quic/quic_client_session_base.h" #include "net/quic/quic_connection.h" #include "net/quic/quic_framer.h" #include "net/quic/quic_session.h" -#include "net/quic/quic_spdy_decompressor.h" #include "net/quic/test_tools/mock_clock.h" #include "net/quic/test_tools/mock_random.h" #include "net/spdy/spdy_framer.h" @@ -26,8 +27,21 @@ namespace net { namespace test { -static const QuicGuid kTestGuid = 42; +static const QuicConnectionId kTestConnectionId = 42; static const int kTestPort = 123; +static const uint32 kInitialStreamFlowControlWindowForTest = + 32 * 1024; // 32 KB +static const uint32 kInitialSessionFlowControlWindowForTest = + 64 * 1024; // 64 KB + +// Data stream IDs start at 5: the crypto stream is 1, headers stream is 3. +static const QuicStreamId kClientDataStreamId1 = 5; +static const QuicStreamId kClientDataStreamId2 = 7; +static const QuicStreamId kClientDataStreamId3 = 9; +static const QuicStreamId kClientDataStreamId4 = 11; + +// Returns the test peer IP address. +IPAddressNumber TestPeerIPAddress(); // Upper limit on versions we support. QuicVersion QuicVersionMax(); @@ -38,6 +52,19 @@ QuicVersion QuicVersionMin(); // Returns an address for 127.0.0.1. IPAddressNumber Loopback4(); +// Returns an address for ::1. +IPAddressNumber Loopback6(); + +void GenerateBody(std::string* body, int length); + +// Create an encrypted packet for testing. +QuicEncryptedPacket* ConstructEncryptedPacket( + QuicConnectionId connection_id, + bool version_flag, + bool reset_flag, + QuicPacketSequenceNumber sequence_number, + const std::string& data); + void CompareCharArraysWithHexError(const std::string& description, const char* actual, const int actual_len, @@ -56,13 +83,29 @@ size_t GetPacketLengthForOneStream( InFecGroup is_in_fec_group, size_t* payload_length); -// Size in bytes of the stream frame fields for an arbitrary StreamID and -// offset and the last frame in a packet. -size_t GetMinStreamFrameSize(QuicVersion version); - // Returns QuicConfig set to default values. QuicConfig DefaultQuicConfig(); +// Returns a version vector consisting of |version|. +QuicVersionVector SupportedVersions(QuicVersion version); + +// Testing convenience method to construct a QuicAckFrame with all packets +// from least_unacked to largest_observed acked. +QuicAckFrame MakeAckFrame(QuicPacketSequenceNumber largest_observed, + QuicPacketSequenceNumber least_unacked); + +// Testing convenience method to construct a QuicAckFrame with |num_nack_ranges| +// nack ranges of width 1 packet, starting from |least_unacked|. +QuicAckFrame MakeAckFrameWithNackRanges(size_t num_nack_ranges, + QuicPacketSequenceNumber least_unacked); + +// Returns a SerializedPacket whose |packet| member is owned by the caller, and +// is populated with the fields in |header| and |frames|, or is NULL if the +// packet could not be created. +SerializedPacket BuildUnsizedDataPacket(QuicFramer* framer, + const QuicPacketHeader& header, + const QuicFrames& frames); + template<typename SaveType> class ValueRestore { public: @@ -82,10 +125,29 @@ class ValueRestore { DISALLOW_COPY_AND_ASSIGN(ValueRestore); }; +// Simple random number generator used to compute random numbers suitable +// for pseudo-randomly dropping packets in tests. It works by computing +// the sha1 hash of the current seed, and using the first 64 bits as +// the next random number, and the next seed. +class SimpleRandom { + public: + SimpleRandom() : seed_(0) {} + + // Returns a random number in the range [0, kuint64max]. + uint64 RandUint64(); + + void set_seed(uint64 seed) { seed_ = seed; } + + private: + uint64 seed_; + + DISALLOW_COPY_AND_ASSIGN(SimpleRandom); +}; + class MockFramerVisitor : public QuicFramerVisitorInterface { public: MockFramerVisitor(); - ~MockFramerVisitor(); + virtual ~MockFramerVisitor(); MOCK_METHOD1(OnError, void(QuicFramer* framer)); // The constructor sets this up to return false by default. @@ -97,17 +159,25 @@ class MockFramerVisitor : public QuicFramerVisitorInterface { MOCK_METHOD0(OnRevivedPacket, void()); // The constructor sets this up to return true by default. MOCK_METHOD1(OnUnauthenticatedHeader, bool(const QuicPacketHeader& header)); + // The constructor sets this up to return true by default. + MOCK_METHOD1(OnUnauthenticatedPublicHeader, bool( + const QuicPacketPublicHeader& header)); + MOCK_METHOD1(OnDecryptedPacket, void(EncryptionLevel level)); MOCK_METHOD1(OnPacketHeader, bool(const QuicPacketHeader& header)); MOCK_METHOD1(OnFecProtectedPayload, void(base::StringPiece payload)); MOCK_METHOD1(OnStreamFrame, bool(const QuicStreamFrame& frame)); MOCK_METHOD1(OnAckFrame, bool(const QuicAckFrame& frame)); MOCK_METHOD1(OnCongestionFeedbackFrame, bool(const QuicCongestionFeedbackFrame& frame)); + MOCK_METHOD1(OnStopWaitingFrame, bool(const QuicStopWaitingFrame& frame)); + MOCK_METHOD1(OnPingFrame, bool(const QuicPingFrame& frame)); MOCK_METHOD1(OnFecData, void(const QuicFecData& fec)); MOCK_METHOD1(OnRstStreamFrame, bool(const QuicRstStreamFrame& frame)); MOCK_METHOD1(OnConnectionCloseFrame, bool(const QuicConnectionCloseFrame& frame)); MOCK_METHOD1(OnGoAwayFrame, bool(const QuicGoAwayFrame& frame)); + MOCK_METHOD1(OnWindowUpdateFrame, bool(const QuicWindowUpdateFrame& frame)); + MOCK_METHOD1(OnBlockedFrame, bool(const QuicBlockedFrame& frame)); MOCK_METHOD0(OnPacketComplete, void()); private: @@ -127,103 +197,49 @@ class NoOpFramerVisitor : public QuicFramerVisitorInterface { virtual void OnRevivedPacket() OVERRIDE {} virtual bool OnProtocolVersionMismatch(QuicVersion version) OVERRIDE; virtual bool OnUnauthenticatedHeader(const QuicPacketHeader& header) OVERRIDE; + virtual bool OnUnauthenticatedPublicHeader( + const QuicPacketPublicHeader& header) OVERRIDE; + virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE {} virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE; virtual void OnFecProtectedPayload(base::StringPiece payload) OVERRIDE {} virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE; virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE; virtual bool OnCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& frame) OVERRIDE; + virtual bool OnStopWaitingFrame( + const QuicStopWaitingFrame& frame) OVERRIDE; + virtual bool OnPingFrame(const QuicPingFrame& frame) OVERRIDE; virtual void OnFecData(const QuicFecData& fec) OVERRIDE {} virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE; virtual bool OnConnectionCloseFrame( const QuicConnectionCloseFrame& frame) OVERRIDE; virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE; + virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) OVERRIDE; + virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE; virtual void OnPacketComplete() OVERRIDE {} private: DISALLOW_COPY_AND_ASSIGN(NoOpFramerVisitor); }; -class FramerVisitorCapturingPublicReset : public NoOpFramerVisitor { - public: - FramerVisitorCapturingPublicReset(); - virtual ~FramerVisitorCapturingPublicReset(); - - virtual void OnPublicResetPacket( - const QuicPublicResetPacket& packet) OVERRIDE; - - const QuicPublicResetPacket public_reset_packet() { - return public_reset_packet_; - } - - private: - QuicPublicResetPacket public_reset_packet_; -}; - -class FramerVisitorCapturingFrames : public NoOpFramerVisitor { - public: - FramerVisitorCapturingFrames(); - virtual ~FramerVisitorCapturingFrames(); - - // Reset the visitor to it's initial state. - void Reset(); - - // NoOpFramerVisitor - virtual void OnVersionNegotiationPacket( - const QuicVersionNegotiationPacket& packet) OVERRIDE; - virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE; - virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE; - virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE; - virtual bool OnCongestionFeedbackFrame( - const QuicCongestionFeedbackFrame& frame) OVERRIDE; - virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE; - virtual bool OnConnectionCloseFrame( - const QuicConnectionCloseFrame& frame) OVERRIDE; - virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE; - - size_t frame_count() const { return frame_count_; } - QuicPacketHeader* header() { return &header_; } - const std::vector<QuicStreamFrame>* stream_frames() const { - return &stream_frames_; - } - const std::vector<string*>& stream_data() const { - return stream_data_; - } - QuicAckFrame* ack() { return ack_.get(); } - QuicCongestionFeedbackFrame* feedback() { return feedback_.get(); } - QuicRstStreamFrame* rst() { return rst_.get(); } - QuicConnectionCloseFrame* close() { return close_.get(); } - QuicGoAwayFrame* goaway() { return goaway_.get(); } - QuicVersionNegotiationPacket* version_negotiation_packet() { - return version_negotiation_packet_.get(); - } - - private: - size_t frame_count_; - QuicPacketHeader header_; - std::vector<QuicStreamFrame> stream_frames_; - std::vector<std::string*> stream_data_; - scoped_ptr<QuicAckFrame> ack_; - scoped_ptr<QuicCongestionFeedbackFrame> feedback_; - scoped_ptr<QuicRstStreamFrame> rst_; - scoped_ptr<QuicConnectionCloseFrame> close_; - scoped_ptr<QuicGoAwayFrame> goaway_; - scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_; - - DISALLOW_COPY_AND_ASSIGN(FramerVisitorCapturingFrames); -}; - class MockConnectionVisitor : public QuicConnectionVisitorInterface { public: MockConnectionVisitor(); virtual ~MockConnectionVisitor(); - MOCK_METHOD1(OnStreamFrames, bool(const std::vector<QuicStreamFrame>& frame)); + MOCK_METHOD1(OnStreamFrames, void(const std::vector<QuicStreamFrame>& frame)); + MOCK_METHOD1(OnWindowUpdateFrames, + void(const std::vector<QuicWindowUpdateFrame>& frame)); + MOCK_METHOD1(OnBlockedFrames, + void(const std::vector<QuicBlockedFrame>& frame)); MOCK_METHOD1(OnRstStream, void(const QuicRstStreamFrame& frame)); MOCK_METHOD1(OnGoAway, void(const QuicGoAwayFrame& frame)); MOCK_METHOD2(OnConnectionClosed, void(QuicErrorCode error, bool from_peer)); - MOCK_METHOD0(OnCanWrite, bool()); + MOCK_METHOD0(OnWriteBlocked, void()); + MOCK_METHOD0(OnCanWrite, void()); + MOCK_CONST_METHOD0(WillingAndAbleToWrite, bool()); MOCK_CONST_METHOD0(HasPendingHandshake, bool()); + MOCK_CONST_METHOD0(HasOpenDataStreams, bool()); MOCK_METHOD1(OnSuccessfulVersionNegotiation, void(const QuicVersion& version)); MOCK_METHOD0(OnConfigNegotiated, void()); @@ -236,30 +252,31 @@ class MockHelper : public QuicConnectionHelperInterface { public: MockHelper(); virtual ~MockHelper(); - - MOCK_METHOD1(SetConnection, void(QuicConnection* connection)); - const QuicClock* GetClock() const; - QuicRandom* GetRandomGenerator(); + virtual const QuicClock* GetClock() const OVERRIDE; + virtual QuicRandom* GetRandomGenerator() OVERRIDE; + virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) OVERRIDE; void AdvanceTime(QuicTime::Delta delta); - virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate); private: MockClock clock_; MockRandom random_generator_; + + DISALLOW_COPY_AND_ASSIGN(MockHelper); }; class MockConnection : public QuicConnection { public: - // Uses a MockHelper, GUID of 42, and 127.0.0.1:123. + // Uses a MockHelper, ConnectionId of 42, and 127.0.0.1:123. explicit MockConnection(bool is_server); - // Uses a MockHelper, GUID of 42. - MockConnection(IPEndPoint address, - bool is_server); + // Uses a MockHelper, ConnectionId of 42. + MockConnection(IPEndPoint address, bool is_server); // Uses a MockHelper, and 127.0.0.1:123 - MockConnection(QuicGuid guid, - bool is_server); + MockConnection(QuicConnectionId connection_id, bool is_server); + + // Uses a Mock helper, ConnectionId of 42, and 127.0.0.1:123. + MockConnection(bool is_server, const QuicVersionVector& supported_versions); virtual ~MockConnection(); @@ -273,13 +290,18 @@ class MockConnection : public QuicConnection { MOCK_METHOD1(SendConnectionClose, void(QuicErrorCode error)); MOCK_METHOD2(SendConnectionCloseWithDetails, void(QuicErrorCode error, const string& details)); - MOCK_METHOD2(SendRstStream, void(QuicStreamId id, - QuicRstStreamErrorCode error)); + MOCK_METHOD2(SendConnectionClosePacket, void(QuicErrorCode error, + const string& details)); + MOCK_METHOD3(SendRstStream, void(QuicStreamId id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written)); MOCK_METHOD3(SendGoAway, void(QuicErrorCode error, QuicStreamId last_good_stream_id, const string& reason)); - MOCK_METHOD0(OnCanWrite, bool()); - MOCK_CONST_METHOD0(HasPendingHandshake, bool()); + MOCK_METHOD1(SendBlocked, void(QuicStreamId id)); + MOCK_METHOD2(SendWindowUpdate, void(QuicStreamId id, + QuicStreamOffset byte_offset)); + MOCK_METHOD0(OnCanWrite, void()); void ProcessUdpPacketInternal(const IPEndPoint& self_address, const IPEndPoint& peer_address, @@ -301,6 +323,10 @@ class MockConnection : public QuicConnection { class PacketSavingConnection : public MockConnection { public: explicit PacketSavingConnection(bool is_server); + + PacketSavingConnection(bool is_server, + const QuicVersionVector& supported_versions); + virtual ~PacketSavingConnection(); virtual bool SendOrQueuePacket(EncryptionLevel level, @@ -318,25 +344,31 @@ class MockSession : public QuicSession { public: explicit MockSession(QuicConnection* connection); virtual ~MockSession(); - - MOCK_METHOD4(OnPacket, bool(const IPEndPoint& self_address, - const IPEndPoint& peer_address, - const QuicPacketHeader& header, - const std::vector<QuicStreamFrame>& frame)); MOCK_METHOD2(OnConnectionClosed, void(QuicErrorCode error, bool from_peer)); MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id)); MOCK_METHOD0(GetCryptoStream, QuicCryptoStream*()); MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*()); MOCK_METHOD6(WritevData, QuicConsumedData(QuicStreamId id, - const struct iovec* iov, - int count, + const IOVector& data, QuicStreamOffset offset, bool fin, + FecProtection fec_protection, QuicAckNotifier::DelegateInterface*)); - MOCK_METHOD0(IsHandshakeComplete, bool()); + MOCK_METHOD2(OnStreamHeaders, void(QuicStreamId stream_id, + base::StringPiece headers_data)); + MOCK_METHOD2(OnStreamHeadersPriority, void(QuicStreamId stream_id, + QuicPriority priority)); + MOCK_METHOD3(OnStreamHeadersComplete, void(QuicStreamId stream_id, + bool fin, + size_t frame_len)); + MOCK_METHOD3(SendRstStream, void(QuicStreamId stream_id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written)); MOCK_METHOD0(IsCryptoHandshakeConfirmed, bool()); + using QuicSession::ActivateStream; + private: DISALLOW_COPY_AND_ASSIGN(MockSession); }; @@ -351,25 +383,55 @@ class TestSession : public QuicSession { void SetCryptoStream(QuicCryptoStream* stream); - virtual QuicCryptoStream* GetCryptoStream(); + virtual QuicCryptoStream* GetCryptoStream() OVERRIDE; private: QuicCryptoStream* crypto_stream_; + DISALLOW_COPY_AND_ASSIGN(TestSession); }; +class TestClientSession : public QuicClientSessionBase { + public: + TestClientSession(QuicConnection* connection, const QuicConfig& config); + virtual ~TestClientSession(); + + // QuicClientSessionBase + MOCK_METHOD1(OnProofValid, + void(const QuicCryptoClientConfig::CachedState& cached)); + MOCK_METHOD1(OnProofVerifyDetailsAvailable, + void(const ProofVerifyDetails& verify_details)); + + // TestClientSession + MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id)); + MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*()); + + void SetCryptoStream(QuicCryptoStream* stream); + + virtual QuicCryptoStream* GetCryptoStream() OVERRIDE; + + private: + QuicCryptoStream* crypto_stream_; + + DISALLOW_COPY_AND_ASSIGN(TestClientSession); +}; + class MockPacketWriter : public QuicPacketWriter { public: MockPacketWriter(); virtual ~MockPacketWriter(); - MOCK_METHOD5(WritePacket, + MOCK_METHOD4(WritePacket, WriteResult(const char* buffer, size_t buf_len, const IPAddressNumber& self_address, - const IPEndPoint& peer_address, - QuicBlockedWriterInterface* blocked_writer)); + const IPEndPoint& peer_address)); MOCK_CONST_METHOD0(IsWriteBlockedDataBuffered, bool()); + MOCK_CONST_METHOD0(IsWriteBlocked, bool()); + MOCK_METHOD0(SetWritable, void()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockPacketWriter); }; class MockSendAlgorithm : public SendAlgorithmInterface { @@ -379,24 +441,23 @@ class MockSendAlgorithm : public SendAlgorithmInterface { MOCK_METHOD2(SetFromConfig, void(const QuicConfig& config, bool is_server)); MOCK_METHOD1(SetMaxPacketSize, void(QuicByteCount max_packet_size)); - MOCK_METHOD3(OnIncomingQuicCongestionFeedbackFrame, + MOCK_METHOD2(OnIncomingQuicCongestionFeedbackFrame, void(const QuicCongestionFeedbackFrame&, - QuicTime feedback_receive_time, - const SentPacketsMap&)); - MOCK_METHOD3(OnPacketAcked, - void(QuicPacketSequenceNumber, QuicByteCount, QuicTime::Delta)); - MOCK_METHOD2(OnPacketLost, void(QuicPacketSequenceNumber, QuicTime)); + QuicTime feedback_receive_time)); + MOCK_METHOD4(OnCongestionEvent, void(bool rtt_updated, + QuicByteCount bytes_in_flight, + const CongestionMap& acked_packets, + const CongestionMap& lost_packets)); MOCK_METHOD5(OnPacketSent, - bool(QuicTime sent_time, QuicPacketSequenceNumber, QuicByteCount, - TransmissionType, HasRetransmittableData)); - MOCK_METHOD0(OnRetransmissionTimeout, void()); - MOCK_METHOD2(OnPacketAbandoned, void(QuicPacketSequenceNumber sequence_number, - QuicByteCount abandoned_bytes)); - MOCK_METHOD4(TimeUntilSend, QuicTime::Delta(QuicTime now, TransmissionType, - HasRetransmittableData, - IsHandshake)); + bool(QuicTime, QuicByteCount, QuicPacketSequenceNumber, + QuicByteCount, HasRetransmittableData)); + MOCK_METHOD1(OnRetransmissionTimeout, void(bool)); + MOCK_CONST_METHOD3(TimeUntilSend, + QuicTime::Delta(QuicTime now, + QuicByteCount bytes_in_flight, + HasRetransmittableData)); MOCK_CONST_METHOD0(BandwidthEstimate, QuicBandwidth(void)); - MOCK_CONST_METHOD0(SmoothedRtt, QuicTime::Delta(void)); + MOCK_METHOD1(OnRttUpdated, void(QuicPacketSequenceNumber)); MOCK_CONST_METHOD0(RetransmissionDelay, QuicTime::Delta(void)); MOCK_CONST_METHOD0(GetCongestionWindow, QuicByteCount()); @@ -404,6 +465,23 @@ class MockSendAlgorithm : public SendAlgorithmInterface { DISALLOW_COPY_AND_ASSIGN(MockSendAlgorithm); }; +class MockLossAlgorithm : public LossDetectionInterface { + public: + MockLossAlgorithm(); + virtual ~MockLossAlgorithm(); + + MOCK_CONST_METHOD0(GetLossDetectionType, LossDetectionType()); + MOCK_METHOD4(DetectLostPackets, + SequenceNumberSet(const QuicUnackedPacketMap& unacked_packets, + const QuicTime& time, + QuicPacketSequenceNumber largest_observed, + const RttStats& rtt_stats)); + MOCK_CONST_METHOD0(GetLossTimeout, QuicTime()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockLossAlgorithm); +}; + class TestEntropyCalculator : public QuicReceivedEntropyHashCalculatorInterface { public: @@ -412,6 +490,9 @@ class TestEntropyCalculator : virtual QuicPacketEntropyHash EntropyHash( QuicPacketSequenceNumber sequence_number) const OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(TestEntropyCalculator); }; class MockEntropyCalculator : public TestEntropyCalculator { @@ -422,28 +503,27 @@ class MockEntropyCalculator : public TestEntropyCalculator { MOCK_CONST_METHOD1( EntropyHash, QuicPacketEntropyHash(QuicPacketSequenceNumber sequence_number)); -}; - -class TestDecompressorVisitor : public QuicSpdyDecompressor::Visitor { - public: - virtual ~TestDecompressorVisitor() {} - virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE; - virtual void OnDecompressionError() OVERRIDE; - - string data() { return data_; } - bool error() { return error_; } private: - string data_; - bool error_; + DISALLOW_COPY_AND_ASSIGN(MockEntropyCalculator); }; class MockAckNotifierDelegate : public QuicAckNotifier::DelegateInterface { public: MockAckNotifierDelegate(); + + MOCK_METHOD5(OnAckNotification, void(int num_original_packets, + int num_original_bytes, + int num_retransmitted_packets, + int num_retransmitted_bytes, + QuicTime::Delta delta_largest_observed)); + + protected: + // Object is ref counted. virtual ~MockAckNotifierDelegate(); - MOCK_METHOD0(OnAckNotification, void()); + private: + DISALLOW_COPY_AND_ASSIGN(MockAckNotifierDelegate); }; } // namespace test diff --git a/chromium/net/quic/test_tools/quic_test_writer.cc b/chromium/net/quic/test_tools/quic_test_writer.cc deleted file mode 100644 index 6755b2cfa8c..00000000000 --- a/chromium/net/quic/test_tools/quic_test_writer.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/test_tools/quic_test_writer.h" - -namespace net { -namespace test { - -QuicTestWriter::QuicTestWriter() {} - -QuicTestWriter::~QuicTestWriter() {} - -QuicPacketWriter* QuicTestWriter::writer() { - return writer_.get(); -} - -void QuicTestWriter::set_writer(QuicPacketWriter* writer) { - writer_.reset(writer); -} - -} // namespace test -} // namespace net diff --git a/chromium/net/quic/test_tools/quic_test_writer.h b/chromium/net/quic/test_tools/quic_test_writer.h deleted file mode 100644 index ee33b7abe2a..00000000000 --- a/chromium/net/quic/test_tools/quic_test_writer.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef NET_QUIC_TEST_TOOLS_QUIC_TEST_WRITER_H_ -#define NET_QUIC_TEST_TOOLS_QUIC_TEST_WRITER_H_ - -#include "base/memory/scoped_ptr.h" -#include "net/quic/quic_packet_writer.h" - -namespace net { -namespace test { - -// Allows setting a writer for a QuicConnection, to allow -// fine-grained control of writes. -class QuicTestWriter : public QuicPacketWriter { - public: - QuicTestWriter(); - virtual ~QuicTestWriter(); - // Takes ownership of |writer|. - void set_writer(QuicPacketWriter* writer); - - protected: - QuicPacketWriter* writer(); - - private: - scoped_ptr<QuicPacketWriter> writer_; -}; - -} // namespace test -} // namespace net - -#endif // NET_QUIC_TEST_TOOLS_QUIC_TEST_WRITER_H_ diff --git a/chromium/net/quic/test_tools/reliable_quic_stream_peer.cc b/chromium/net/quic/test_tools/reliable_quic_stream_peer.cc index 5119d035faa..914609ee1d5 100644 --- a/chromium/net/quic/test_tools/reliable_quic_stream_peer.cc +++ b/chromium/net/quic/test_tools/reliable_quic_stream_peer.cc @@ -4,6 +4,8 @@ #include "net/quic/test_tools/reliable_quic_stream_peer.h" +#include <list> + #include "net/quic/reliable_quic_stream.h" namespace net { @@ -22,5 +24,38 @@ void ReliableQuicStreamPeer::SetStreamBytesWritten( stream->stream_bytes_written_ = stream_bytes_written; } +// static +void ReliableQuicStreamPeer::CloseReadSide(ReliableQuicStream* stream) { + stream->CloseReadSide(); +} + +// static +bool ReliableQuicStreamPeer::FinSent(ReliableQuicStream* stream) { + return stream->fin_sent_; +} + +// static +bool ReliableQuicStreamPeer::RstSent(ReliableQuicStream* stream) { + return stream->rst_sent_; +} + +// static +uint32 ReliableQuicStreamPeer::SizeOfQueuedData(ReliableQuicStream* stream) { + uint32 total = 0; + std::list<ReliableQuicStream::PendingData>::iterator it = + stream->queued_data_.begin(); + while (it != stream->queued_data_.end()) { + total += it->data.size(); + ++it; + } + return total; +} + +// static +void ReliableQuicStreamPeer::SetFecPolicy(ReliableQuicStream* stream, + FecPolicy fec_policy) { + stream->set_fec_policy(fec_policy); +} + } // namespace test } // namespace net diff --git a/chromium/net/quic/test_tools/reliable_quic_stream_peer.h b/chromium/net/quic/test_tools/reliable_quic_stream_peer.h index da229da44e0..1f5467d00ee 100644 --- a/chromium/net/quic/test_tools/reliable_quic_stream_peer.h +++ b/chromium/net/quic/test_tools/reliable_quic_stream_peer.h @@ -19,6 +19,14 @@ class ReliableQuicStreamPeer { static void SetWriteSideClosed(bool value, ReliableQuicStream* stream); static void SetStreamBytesWritten(QuicStreamOffset stream_bytes_written, ReliableQuicStream* stream); + static void CloseReadSide(ReliableQuicStream* stream); + + static bool FinSent(ReliableQuicStream* stream); + static bool RstSent(ReliableQuicStream* stream); + + static uint32 SizeOfQueuedData(ReliableQuicStream* stream); + + static void SetFecPolicy(ReliableQuicStream* stream, FecPolicy fec_policy); private: DISALLOW_COPY_AND_ASSIGN(ReliableQuicStreamPeer); diff --git a/chromium/net/quic/test_tools/simple_quic_framer.cc b/chromium/net/quic/test_tools/simple_quic_framer.cc index 7d6439d2dcb..a08d5555663 100644 --- a/chromium/net/quic/test_tools/simple_quic_framer.cc +++ b/chromium/net/quic/test_tools/simple_quic_framer.cc @@ -22,7 +22,7 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface { : error_(QUIC_NO_ERROR) { } - virtual ~SimpleFramerVisitor() { + virtual ~SimpleFramerVisitor() OVERRIDE { STLDeleteElements(&stream_data_); } @@ -36,21 +36,30 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface { virtual void OnPacket() OVERRIDE {} virtual void OnPublicResetPacket( - const QuicPublicResetPacket& packet) OVERRIDE {} + const QuicPublicResetPacket& packet) OVERRIDE { + public_reset_packet_.reset(new QuicPublicResetPacket(packet)); + } virtual void OnVersionNegotiationPacket( - const QuicVersionNegotiationPacket& packet) OVERRIDE {} + const QuicVersionNegotiationPacket& packet) OVERRIDE { + version_negotiation_packet_.reset( + new QuicVersionNegotiationPacket(packet)); + } virtual void OnRevivedPacket() OVERRIDE {} - virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE { - has_header_ = true; - header_ = header; + virtual bool OnUnauthenticatedPublicHeader( + const QuicPacketPublicHeader& header) OVERRIDE { return true; } - virtual bool OnUnauthenticatedHeader( const QuicPacketHeader& header) OVERRIDE { return true; } + virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE {} + virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE { + has_header_ = true; + header_ = header; + return true; + } virtual void OnFecProtectedPayload(StringPiece payload) OVERRIDE {} @@ -77,6 +86,16 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface { return true; } + virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) OVERRIDE { + stop_waiting_frames_.push_back(frame); + return true; + } + + virtual bool OnPingFrame(const QuicPingFrame& frame) OVERRIDE { + ping_frames_.push_back(frame); + return true; + } + virtual void OnFecData(const QuicFecData& fec) OVERRIDE { fec_data_ = fec; fec_redundancy_ = fec_data_.redundancy.as_string(); @@ -99,6 +118,17 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface { return true; } + virtual bool OnWindowUpdateFrame( + const QuicWindowUpdateFrame& frame) OVERRIDE { + window_update_frames_.push_back(frame); + return true; + } + + virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE { + blocked_frames_.push_back(frame); + return true; + } + virtual void OnPacketComplete() OVERRIDE {} const QuicPacketHeader& header() const { return header_; } @@ -118,22 +148,40 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface { const vector<QuicStreamFrame>& stream_frames() const { return stream_frames_; } + const vector<QuicStopWaitingFrame>& stop_waiting_frames() const { + return stop_waiting_frames_; + } + const vector<QuicPingFrame>& ping_frames() const { + return ping_frames_; + } const QuicFecData& fec_data() const { return fec_data_; } + const QuicVersionNegotiationPacket* version_negotiation_packet() const { + return version_negotiation_packet_.get(); + } + const QuicPublicResetPacket* public_reset_packet() const { + return public_reset_packet_.get(); + } private: QuicErrorCode error_; bool has_header_; QuicPacketHeader header_; QuicFecData fec_data_; + scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_; + scoped_ptr<QuicPublicResetPacket> public_reset_packet_; string fec_redundancy_; vector<QuicAckFrame> ack_frames_; vector<QuicCongestionFeedbackFrame> feedback_frames_; + vector<QuicStopWaitingFrame> stop_waiting_frames_; + vector<QuicPingFrame> ping_frames_; vector<QuicStreamFrame> stream_frames_; vector<QuicRstStreamFrame> rst_stream_frames_; vector<QuicGoAwayFrame> goaway_frames_; vector<QuicConnectionCloseFrame> connection_close_frames_; + vector<QuicWindowUpdateFrame> window_update_frames_; + vector<QuicBlockedFrame> blocked_frames_; vector<string*> stream_data_; DISALLOW_COPY_AND_ASSIGN(SimpleFramerVisitor); @@ -143,6 +191,10 @@ SimpleQuicFramer::SimpleQuicFramer() : framer_(QuicSupportedVersions(), QuicTime::Zero(), true) { } +SimpleQuicFramer::SimpleQuicFramer(const QuicVersionVector& supported_versions) + : framer_(supported_versions, QuicTime::Zero(), true) { +} + SimpleQuicFramer::~SimpleQuicFramer() { } @@ -158,6 +210,11 @@ bool SimpleQuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) { return framer_.ProcessPacket(packet); } +void SimpleQuicFramer::Reset() { + visitor_.reset(new SimpleFramerVisitor); +} + + const QuicPacketHeader& SimpleQuicFramer::header() const { return visitor_->header(); } @@ -166,16 +223,27 @@ const QuicFecData& SimpleQuicFramer::fec_data() const { return visitor_->fec_data(); } +const QuicVersionNegotiationPacket* +SimpleQuicFramer::version_negotiation_packet() const { + return visitor_->version_negotiation_packet(); +} + +const QuicPublicResetPacket* SimpleQuicFramer::public_reset_packet() const { + return visitor_->public_reset_packet(); +} + QuicFramer* SimpleQuicFramer::framer() { return &framer_; } size_t SimpleQuicFramer::num_frames() const { return ack_frames().size() + - stream_frames().size() + feedback_frames().size() + - rst_stream_frames().size() + goaway_frames().size() + + rst_stream_frames().size() + + stop_waiting_frames().size() + + stream_frames().size() + + ping_frames().size() + connection_close_frames().size(); } @@ -183,6 +251,15 @@ const vector<QuicAckFrame>& SimpleQuicFramer::ack_frames() const { return visitor_->ack_frames(); } +const vector<QuicStopWaitingFrame>& +SimpleQuicFramer::stop_waiting_frames() const { + return visitor_->stop_waiting_frames(); +} + +const vector<QuicPingFrame>& SimpleQuicFramer::ping_frames() const { + return visitor_->ping_frames(); +} + const vector<QuicStreamFrame>& SimpleQuicFramer::stream_frames() const { return visitor_->stream_frames(); } diff --git a/chromium/net/quic/test_tools/simple_quic_framer.h b/chromium/net/quic/test_tools/simple_quic_framer.h index 4416019dba3..7a31014d41e 100644 --- a/chromium/net/quic/test_tools/simple_quic_framer.h +++ b/chromium/net/quic/test_tools/simple_quic_framer.h @@ -7,6 +7,7 @@ #include <vector> +#include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "net/quic/quic_framer.h" #include "net/quic/quic_protocol.h" @@ -29,22 +30,33 @@ class SimpleFramerVisitor; class SimpleQuicFramer { public: SimpleQuicFramer(); + explicit SimpleQuicFramer(const QuicVersionVector& supported_versions); ~SimpleQuicFramer(); bool ProcessPacket(const QuicEncryptedPacket& packet); bool ProcessPacket(const QuicPacket& packet); + void Reset(); const QuicPacketHeader& header() const; size_t num_frames() const; const std::vector<QuicAckFrame>& ack_frames() const; const std::vector<QuicConnectionCloseFrame>& connection_close_frames() const; const std::vector<QuicCongestionFeedbackFrame>& feedback_frames() const; + const std::vector<QuicStopWaitingFrame>& stop_waiting_frames() const; + const std::vector<QuicPingFrame>& ping_frames() const; const std::vector<QuicGoAwayFrame>& goaway_frames() const; const std::vector<QuicRstStreamFrame>& rst_stream_frames() const; const std::vector<QuicStreamFrame>& stream_frames() const; const QuicFecData& fec_data() const; + const QuicVersionNegotiationPacket* version_negotiation_packet() const; + const QuicPublicResetPacket* public_reset_packet() const; + QuicFramer* framer(); + void SetSupportedVersions(const QuicVersionVector& versions) { + framer_.SetSupportedVersions(versions); + } + private: QuicFramer framer_; scoped_ptr<SimpleFramerVisitor> visitor_; |