diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-08-14 11:38:45 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-08-14 17:16:47 +0000 |
commit | 3a97ca8dd9b96b599ae2d33e40df0dd2f7ea5859 (patch) | |
tree | 43cc572ba067417c7341db81f71ae7cc6e0fcc3e /chromium/net/quic | |
parent | f61ab1ac7f855cd281809255c0aedbb1895e1823 (diff) | |
download | qtwebengine-chromium-3a97ca8dd9b96b599ae2d33e40df0dd2f7ea5859.tar.gz |
BASELINE: Update chromium to 45.0.2454.40
Change-Id: Id2121d9f11a8fc633677236c65a3e41feef589e4
Reviewed-by: Andras Becsi <andras.becsi@theqtcompany.com>
Diffstat (limited to 'chromium/net/quic')
145 files changed, 6816 insertions, 3050 deletions
diff --git a/chromium/net/quic/congestion_control/cubic.cc b/chromium/net/quic/congestion_control/cubic.cc index 499eafbf691..23ed2dd2f3b 100644 --- a/chromium/net/quic/congestion_control/cubic.cc +++ b/chromium/net/quic/congestion_control/cubic.cc @@ -4,6 +4,7 @@ #include "net/quic/congestion_control/cubic.h" +#include <stdint.h> #include <algorithm> #include <cmath> @@ -26,7 +27,7 @@ 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. const int kCubeCongestionWindowScale = 410; -const uint64 kCubeFactor = (GG_UINT64_C(1) << kCubeScale) / +const uint64 kCubeFactor = (UINT64_C(1) << kCubeScale) / kCubeCongestionWindowScale; const uint32 kDefaultNumConnections = 2; diff --git a/chromium/net/quic/congestion_control/cubic_bytes.cc b/chromium/net/quic/congestion_control/cubic_bytes.cc index b5af3d1219c..40259ceae61 100644 --- a/chromium/net/quic/congestion_control/cubic_bytes.cc +++ b/chromium/net/quic/congestion_control/cubic_bytes.cc @@ -4,6 +4,7 @@ #include "net/quic/congestion_control/cubic_bytes.h" +#include <stdint.h> #include <algorithm> #include <cmath> @@ -26,7 +27,7 @@ const int kCubeScale = 40; // 1024*1024^3 (first 1024 is from 0.100^3) // round trip time. const int kCubeCongestionWindowScale = 410; // The cube factor for packets in bytes. -const uint64 kCubeFactor = (GG_UINT64_C(1) << kCubeScale) / +const uint64 kCubeFactor = (UINT64_C(1) << kCubeScale) / kCubeCongestionWindowScale / kDefaultTCPMSS; const uint32 kDefaultNumConnections = 2; diff --git a/chromium/net/quic/congestion_control/hybrid_slow_start.cc b/chromium/net/quic/congestion_control/hybrid_slow_start.cc index f2ac90877ad..d6a47e4ed6d 100644 --- a/chromium/net/quic/congestion_control/hybrid_slow_start.cc +++ b/chromium/net/quic/congestion_control/hybrid_slow_start.cc @@ -22,9 +22,8 @@ const int kHybridStartDelayFactorExp = 3; // 2^3 = 8 const int64 kHybridStartDelayMinThresholdUs = 4000; const int64 kHybridStartDelayMaxThresholdUs = 16000; -HybridSlowStart::HybridSlowStart(const QuicClock* clock) - : clock_(clock), - started_(false), +HybridSlowStart::HybridSlowStart() + : started_(false), hystart_found_(NOT_FOUND), last_sent_sequence_number_(0), end_sequence_number_(0), diff --git a/chromium/net/quic/congestion_control/hybrid_slow_start.h b/chromium/net/quic/congestion_control/hybrid_slow_start.h index 2077211806c..9f0a9aef359 100644 --- a/chromium/net/quic/congestion_control/hybrid_slow_start.h +++ b/chromium/net/quic/congestion_control/hybrid_slow_start.h @@ -18,7 +18,6 @@ #include "base/basictypes.h" #include "net/base/net_export.h" -#include "net/quic/quic_clock.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_time.h" @@ -26,7 +25,7 @@ namespace net { class NET_EXPORT_PRIVATE HybridSlowStart { public: - explicit HybridSlowStart(const QuicClock* clock); + HybridSlowStart(); void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number, bool in_slow_start); @@ -67,7 +66,6 @@ class NET_EXPORT_PRIVATE HybridSlowStart { 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_; HystartState hystart_found_; 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 1b34ad5bed9..a839a3160d3 100644 --- a/chromium/net/quic/congestion_control/hybrid_slow_start_test.cc +++ b/chromium/net/quic/congestion_control/hybrid_slow_start_test.cc @@ -5,7 +5,6 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "net/quic/congestion_control/hybrid_slow_start.h" -#include "net/quic/test_tools/mock_clock.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -17,10 +16,9 @@ class HybridSlowStartTest : public ::testing::Test { : one_ms_(QuicTime::Delta::FromMilliseconds(1)), rtt_(QuicTime::Delta::FromMilliseconds(60)) { } - void SetUp() override { slow_start_.reset(new HybridSlowStart(&clock_)); } + void SetUp() override { slow_start_.reset(new HybridSlowStart); } const QuicTime::Delta one_ms_; const QuicTime::Delta rtt_; - MockClock clock_; scoped_ptr<HybridSlowStart> slow_start_; }; diff --git a/chromium/net/quic/congestion_control/tcp_cubic_bytes_sender.cc b/chromium/net/quic/congestion_control/tcp_cubic_bytes_sender.cc index 987cfc42ee5..b3ceaca57fb 100644 --- a/chromium/net/quic/congestion_control/tcp_cubic_bytes_sender.cc +++ b/chromium/net/quic/congestion_control/tcp_cubic_bytes_sender.cc @@ -34,8 +34,7 @@ TcpCubicBytesSender::TcpCubicBytesSender( QuicPacketCount initial_tcp_congestion_window, QuicPacketCount max_congestion_window, QuicConnectionStats* stats) - : hybrid_slow_start_(clock), - cubic_(clock), + : cubic_(clock), rtt_stats_(rtt_stats), stats_(stats), reno_(reno), @@ -99,7 +98,7 @@ bool TcpCubicBytesSender::ResumeConnectionState( // Make sure CWND is in appropriate range (in case of bad data). QuicByteCount new_congestion_window = bandwidth.ToBytesPerPeriod(rtt_ms); congestion_window_ = - max(min(new_congestion_window, kMaxTcpCongestionWindow * kMaxSegmentSize), + max(min(new_congestion_window, kMaxResumptionCwnd * kMaxSegmentSize), kMinCongestionWindowForBandwidthResumption * kMaxSegmentSize); // TODO(rjshade): Set appropriate CWND when previous connection was in slow diff --git a/chromium/net/quic/congestion_control/tcp_cubic_bytes_sender_test.cc b/chromium/net/quic/congestion_control/tcp_cubic_bytes_sender_test.cc index c00b76c1c3d..5bff6c0527a 100644 --- a/chromium/net/quic/congestion_control/tcp_cubic_bytes_sender_test.cc +++ b/chromium/net/quic/congestion_control/tcp_cubic_bytes_sender_test.cc @@ -35,7 +35,7 @@ class TcpCubicBytesSenderPeer : public TcpCubicBytesSender { &rtt_stats_, reno, kInitialCongestionWindowPackets, - kMaxTcpCongestionWindow, + kMaxResumptionCwnd, &stats_) {} const HybridSlowStart& hybrid_slow_start() const { @@ -573,9 +573,9 @@ TEST_F(TcpCubicBytesSenderTest, BandwidthResumption) { // Resumed CWND is limited to be in a sensible range. cached_network_params.set_bandwidth_estimate_bytes_per_second( - (kMaxTcpCongestionWindow + 1) * kDefaultTCPMSS); + (kMaxResumptionCwnd + 1) * kDefaultTCPMSS); EXPECT_TRUE(sender_->ResumeConnectionState(cached_network_params, false)); - EXPECT_EQ(kMaxTcpCongestionWindow * kDefaultTCPMSS, + EXPECT_EQ(kMaxResumptionCwnd * kDefaultTCPMSS, sender_->GetCongestionWindow()); cached_network_params.set_bandwidth_estimate_bytes_per_second( diff --git a/chromium/net/quic/congestion_control/tcp_cubic_sender.cc b/chromium/net/quic/congestion_control/tcp_cubic_sender.cc index 30c0be6bc5d..e6607b46891 100644 --- a/chromium/net/quic/congestion_control/tcp_cubic_sender.cc +++ b/chromium/net/quic/congestion_control/tcp_cubic_sender.cc @@ -6,7 +6,7 @@ #include <algorithm> -#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros.h" #include "net/quic/congestion_control/prr_sender.h" #include "net/quic/congestion_control/rtt_stats.h" #include "net/quic/crypto/crypto_protocol.h" @@ -34,8 +34,7 @@ TcpCubicSender::TcpCubicSender(const QuicClock* clock, QuicPacketCount initial_tcp_congestion_window, QuicPacketCount max_tcp_congestion_window, QuicConnectionStats* stats) - : hybrid_slow_start_(clock), - cubic_(clock), + : cubic_(clock), rtt_stats_(rtt_stats), stats_(stats), reno_(reno), @@ -61,11 +60,26 @@ void TcpCubicSender::SetFromConfig(const QuicConfig& config, Perspective perspective) { if (perspective == Perspective::IS_SERVER) { if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kIW03)) { + // Initial window experiment. + congestion_window_ = 3; + } + if (config.HasReceivedConnectionOptions() && ContainsQuicTag(config.ReceivedConnectionOptions(), kIW10)) { // Initial window experiment. congestion_window_ = 10; } if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kIW20)) { + // Initial window experiment. + congestion_window_ = 20; + } + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kIW50)) { + // Initial window experiment. + congestion_window_ = 50; + } + if (config.HasReceivedConnectionOptions() && ContainsQuicTag(config.ReceivedConnectionOptions(), kMIN1)) { // Min CWND experiment. min_congestion_window_ = 1; @@ -100,7 +114,7 @@ bool TcpCubicSender::ResumeConnectionState( // Make sure CWND is in appropriate range (in case of bad data). QuicPacketCount new_congestion_window = bandwidth.ToBytesPerPeriod(rtt_ms) / kMaxPacketSize; - congestion_window_ = max(min(new_congestion_window, kMaxTcpCongestionWindow), + congestion_window_ = max(min(new_congestion_window, kMaxResumptionCwnd), kMinCongestionWindowForBandwidthResumption); // TODO(rjshade): Set appropriate CWND when previous connection was in slow 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 cbcad0f8d52..36eb8b7aa6c 100644 --- a/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc +++ b/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc @@ -65,7 +65,7 @@ class TcpCubicSenderTest : public ::testing::Test { protected: TcpCubicSenderTest() : one_ms_(QuicTime::Delta::FromMilliseconds(1)), - sender_(new TcpCubicSenderPeer(&clock_, true, kMaxTcpCongestionWindow)), + sender_(new TcpCubicSenderPeer(&clock_, true, kMaxResumptionCwnd)), sequence_number_(1), acked_sequence_number_(0), bytes_in_flight_(0) { @@ -374,7 +374,7 @@ TEST_F(TcpCubicSenderTest, SlowStartBurstPacketLossPRR) { TEST_F(TcpCubicSenderTest, RTOCongestionWindow) { EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); - EXPECT_EQ(kMaxTcpCongestionWindow, sender_->slowstart_threshold()); + EXPECT_EQ(kMaxResumptionCwnd, sender_->slowstart_threshold()); // Expect the window to decrease to the minimum once the RTO fires // and slow start threshold to be set to 1/2 of the CWND. @@ -517,12 +517,29 @@ TEST_F(TcpCubicSenderTest, DontTrackAckPackets) { TEST_F(TcpCubicSenderTest, ConfigureInitialWindow) { QuicConfig config; - // Verify that kCOPT: kIW10 forces the congestion window to the default of 10. QuicTagVector options; + options.push_back(kIW03); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + sender_->SetFromConfig(config, Perspective::IS_SERVER); + EXPECT_EQ(3u, sender_->congestion_window()); + + options.clear(); options.push_back(kIW10); QuicConfigPeer::SetReceivedConnectionOptions(&config, options); sender_->SetFromConfig(config, Perspective::IS_SERVER); EXPECT_EQ(10u, sender_->congestion_window()); + + options.clear(); + options.push_back(kIW20); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + sender_->SetFromConfig(config, Perspective::IS_SERVER); + EXPECT_EQ(20u, sender_->congestion_window()); + + options.clear(); + options.push_back(kIW50); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + sender_->SetFromConfig(config, Perspective::IS_SERVER); + EXPECT_EQ(50u, sender_->congestion_window()); } TEST_F(TcpCubicSenderTest, ConfigureMinimumWindow) { @@ -667,9 +684,9 @@ TEST_F(TcpCubicSenderTest, BandwidthResumption) { // Resumed CWND is limited to be in a sensible range. cached_network_params.set_bandwidth_estimate_bytes_per_second( - (kMaxTcpCongestionWindow + 1) * kMaxPacketSize); + (kMaxResumptionCwnd + 1) * kMaxPacketSize); EXPECT_TRUE(sender_->ResumeConnectionState(cached_network_params, false)); - EXPECT_EQ(kMaxTcpCongestionWindow, sender_->congestion_window()); + EXPECT_EQ(kMaxResumptionCwnd, sender_->congestion_window()); cached_network_params.set_bandwidth_estimate_bytes_per_second( (kMinCongestionWindowForBandwidthResumption - 1) * kMaxPacketSize); diff --git a/chromium/net/quic/congestion_control/tcp_loss_algorithm_test.cc b/chromium/net/quic/congestion_control/tcp_loss_algorithm_test.cc index 69fb04bf931..a58ba7f87b0 100644 --- a/chromium/net/quic/congestion_control/tcp_loss_algorithm_test.cc +++ b/chromium/net/quic/congestion_control/tcp_loss_algorithm_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/congestion_control/tcp_loss_algorithm.h" + #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_ack_notifier_manager.h" #include "net/quic/quic_unacked_packet_map.h" #include "net/quic/test_tools/mock_clock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -23,8 +25,7 @@ const uint32 kDefaultLength = 1000; class TcpLossAlgorithmTest : public ::testing::Test { protected: - TcpLossAlgorithmTest() - : unacked_packets_() { + TcpLossAlgorithmTest() : unacked_packets_(&ack_notifier_manager_) { rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100), QuicTime::Delta::Zero(), clock_.Now()); @@ -56,6 +57,7 @@ class TcpLossAlgorithmTest : public ::testing::Test { } vector<QuicEncryptedPacket*> packets_; + AckNotifierManager ack_notifier_manager_; QuicUnackedPacketMap unacked_packets_; TCPLossAlgorithm loss_algorithm_; RttStats rtt_stats_; diff --git a/chromium/net/quic/congestion_control/time_loss_algorithm_test.cc b/chromium/net/quic/congestion_control/time_loss_algorithm_test.cc index 24f63509101..2505c6bd9bd 100644 --- a/chromium/net/quic/congestion_control/time_loss_algorithm_test.cc +++ b/chromium/net/quic/congestion_control/time_loss_algorithm_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/congestion_control/time_loss_algorithm.h" + #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_ack_notifier_manager.h" #include "net/quic/quic_unacked_packet_map.h" #include "net/quic/test_tools/mock_clock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -23,8 +25,7 @@ const uint32 kDefaultLength = 1000; class TimeLossAlgorithmTest : public ::testing::Test { protected: - TimeLossAlgorithmTest() - : unacked_packets_() { + TimeLossAlgorithmTest() : unacked_packets_(&ack_notifier_manager_) { rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100), QuicTime::Delta::Zero(), clock_.Now()); @@ -56,6 +57,7 @@ class TimeLossAlgorithmTest : public ::testing::Test { } vector<QuicEncryptedPacket*> packets_; + AckNotifierManager ack_notifier_manager_; QuicUnackedPacketMap unacked_packets_; TimeLossAlgorithm loss_algorithm_; RttStats rtt_stats_; diff --git a/chromium/net/quic/crypto/aead_base_decrypter.h b/chromium/net/quic/crypto/aead_base_decrypter.h index de9e9969b99..f8d6f1b861a 100644 --- a/chromium/net/quic/crypto/aead_base_decrypter.h +++ b/chromium/net/quic/crypto/aead_base_decrypter.h @@ -78,13 +78,6 @@ class NET_EXPORT_PRIVATE AeadBaseDecrypter : public QuicDecrypter { #endif // !defined(USE_OPENSSL) private: - bool Decrypt(base::StringPiece nonce, - const base::StringPiece& associated_data, - const base::StringPiece& ciphertext, - uint8* output, - size_t* output_length, - size_t max_output_length); - #if defined(USE_OPENSSL) const EVP_AEAD* const aead_alg_; #else diff --git a/chromium/net/quic/crypto/aead_base_decrypter_nss.cc b/chromium/net/quic/crypto/aead_base_decrypter_nss.cc index 1ed4f3ab6e2..255e88da3f8 100644 --- a/chromium/net/quic/crypto/aead_base_decrypter_nss.cc +++ b/chromium/net/quic/crypto/aead_base_decrypter_nss.cc @@ -47,16 +47,22 @@ bool AeadBaseDecrypter::SetNoncePrefix(StringPiece nonce_prefix) { return true; } -bool AeadBaseDecrypter::Decrypt(StringPiece nonce, - const StringPiece& associated_data, - const StringPiece& ciphertext, - uint8* output, - size_t* output_length, - size_t max_output_length) { - if (ciphertext.length() < auth_tag_size_ || - nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) { +bool AeadBaseDecrypter::DecryptPacket(QuicPacketSequenceNumber sequence_number, + const StringPiece& associated_data, + const StringPiece& ciphertext, + char* output, + size_t* output_length, + size_t max_output_length) { + if (ciphertext.length() < auth_tag_size_) { return false; } + + 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)); + // 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 @@ -93,7 +99,8 @@ bool AeadBaseDecrypter::Decrypt(StringPiece nonce, } AeadParams aead_params = {0}; - FillAeadParams(nonce, associated_data, auth_tag_size_, &aead_params); + FillAeadParams(StringPiece(reinterpret_cast<char*>(nonce), nonce_size), + associated_data, auth_tag_size_, &aead_params); SECItem param; param.type = siBuffer; @@ -101,8 +108,9 @@ bool AeadBaseDecrypter::Decrypt(StringPiece nonce, param.len = aead_params.len; unsigned int output_len; - if (pk11_decrypt_(aead_key.get(), aead_mechanism_, ¶m, output, - &output_len, max_output_length, + if (pk11_decrypt_(aead_key.get(), aead_mechanism_, ¶m, + reinterpret_cast<uint8*>(output), &output_len, + max_output_length, reinterpret_cast<const unsigned char*>(ciphertext.data()), ciphertext.length()) != SECSuccess) { return false; @@ -116,26 +124,6 @@ bool AeadBaseDecrypter::Decrypt(StringPiece nonce, return true; } -bool AeadBaseDecrypter::DecryptPacket(QuicPacketSequenceNumber sequence_number, - const StringPiece& associated_data, - const StringPiece& ciphertext, - char* output, - size_t* output_length, - size_t max_output_length) { - if (ciphertext.length() < auth_tag_size_) { - return false; - } - - 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)); - return Decrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size), - associated_data, ciphertext, reinterpret_cast<uint8*>(output), - output_length, max_output_length); -} - StringPiece AeadBaseDecrypter::GetKey() const { return StringPiece(reinterpret_cast<const char*>(key_), key_size_); } diff --git a/chromium/net/quic/crypto/aead_base_decrypter_openssl.cc b/chromium/net/quic/crypto/aead_base_decrypter_openssl.cc index 7929388114b..95991200a37 100644 --- a/chromium/net/quic/crypto/aead_base_decrypter_openssl.cc +++ b/chromium/net/quic/crypto/aead_base_decrypter_openssl.cc @@ -76,32 +76,6 @@ bool AeadBaseDecrypter::SetNoncePrefix(StringPiece nonce_prefix) { return true; } -bool AeadBaseDecrypter::Decrypt(StringPiece nonce, - const StringPiece& associated_data, - const StringPiece& ciphertext, - uint8* output, - size_t* output_length, - size_t max_output_length) { - if (ciphertext.length() < auth_tag_size_ || - nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) { - return false; - } - - if (!EVP_AEAD_CTX_open( - ctx_.get(), output, output_length, max_output_length, - 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())) { - // Because QuicFramer does trial decryption, decryption errors are expected - // when encryption level changes. So we don't log decryption errors. - ClearOpenSslErrors(); - return false; - } - return true; -} - bool AeadBaseDecrypter::DecryptPacket(QuicPacketSequenceNumber sequence_number, const StringPiece& associated_data, const StringPiece& ciphertext, @@ -114,12 +88,21 @@ bool AeadBaseDecrypter::DecryptPacket(QuicPacketSequenceNumber sequence_number, 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)); - return Decrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size), - associated_data, ciphertext, reinterpret_cast<uint8*>(output), - output_length, max_output_length); + if (!EVP_AEAD_CTX_open( + ctx_.get(), reinterpret_cast<uint8_t*>(output), output_length, + max_output_length, reinterpret_cast<const uint8_t*>(nonce), + nonce_size, reinterpret_cast<const uint8_t*>(ciphertext.data()), + ciphertext.size(), + reinterpret_cast<const uint8_t*>(associated_data.data()), + associated_data.size())) { + // Because QuicFramer does trial decryption, decryption errors are expected + // when encryption level changes. So we don't log decryption errors. + ClearOpenSslErrors(); + return false; + } + return true; } StringPiece AeadBaseDecrypter::GetKey() const { diff --git a/chromium/net/quic/crypto/aead_base_encrypter.h b/chromium/net/quic/crypto/aead_base_encrypter.h index 86db32c0253..dd8f775caeb 100644 --- a/chromium/net/quic/crypto/aead_base_encrypter.h +++ b/chromium/net/quic/crypto/aead_base_encrypter.h @@ -42,10 +42,6 @@ class NET_EXPORT_PRIVATE AeadBaseEncrypter : public QuicEncrypter { // QuicEncrypter implementation bool SetKey(base::StringPiece key) override; bool SetNoncePrefix(base::StringPiece nonce_prefix) override; - bool Encrypt(base::StringPiece nonce, - base::StringPiece associated_data, - base::StringPiece plaintext, - unsigned char* output) override; bool EncryptPacket(QuicPacketSequenceNumber sequence_number, base::StringPiece associated_data, base::StringPiece plaintext, @@ -59,6 +55,13 @@ class NET_EXPORT_PRIVATE AeadBaseEncrypter : public QuicEncrypter { base::StringPiece GetKey() const override; base::StringPiece GetNoncePrefix() const override; + // Necessary so unit tests can explicitly specify a nonce, instead of a + // nonce prefix and sequence number. + bool Encrypt(base::StringPiece nonce, + base::StringPiece associated_data, + base::StringPiece plaintext, + unsigned char* output); + 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 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 f6c3d6d47b9..aba387ae333 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 @@ -7,13 +7,7 @@ #include <pk11pub.h> #include <secerr.h> -#include "base/lazy_instance.h" -#include "crypto/ghash.h" -#include "crypto/scoped_nss_types.h" - -#if defined(USE_NSS_CERTS) -#include <dlfcn.h> -#endif +#include "crypto/aes_128_gcm_helpers_nss.h" using base::StringPiece; @@ -24,185 +18,16 @@ namespace { 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, -// lookup PK11_Decrypt with dlsym. - -// GcmSupportChecker is a singleton which caches the results of runtime symbol -// resolution of PK11_Decrypt. -class GcmSupportChecker { - public: - static PK11_DecryptFunction pk11_decrypt_func() { - return pk11_decrypt_func_; - } - - private: - friend struct base::DefaultLazyInstanceTraits<GcmSupportChecker>; - - GcmSupportChecker() { -#if !defined(USE_NSS_CERTS) - // Using a bundled version of NSS that is guaranteed to have this symbol. - pk11_decrypt_func_ = PK11_Decrypt; -#else - // Using system NSS libraries and PCKS #11 modules, which may not have the - // necessary function (PK11_Decrypt) or mechanism support (CKM_AES_GCM). - - // If PK11_Decrypt() was successfully resolved, then NSS will support - // AES-GCM directly. This was introduced in NSS 3.15. - pk11_decrypt_func_ = (PK11_DecryptFunction)dlsym(RTLD_DEFAULT, - "PK11_Decrypt"); -#endif - } - - // |pk11_decrypt_func_| stores the runtime symbol resolution of PK11_Decrypt. - static PK11_DecryptFunction pk11_decrypt_func_; -}; - -// static -PK11_DecryptFunction GcmSupportChecker::pk11_decrypt_func_ = nullptr; - -base::LazyInstance<GcmSupportChecker>::Leaky g_gcm_support_checker = - LAZY_INSTANCE_INITIALIZER; - -// 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, CK_MECHANISM_TYPE mechanism, SECItem* param, unsigned char* out, unsigned int* out_len, unsigned int max_len, - const unsigned char* enc, - unsigned int enc_len) { - // If PK11_Decrypt() was successfully resolved or if bundled version of NSS is - // being used, then NSS will support AES-GCM directly. - PK11_DecryptFunction pk11_decrypt_func = - GcmSupportChecker::pk11_decrypt_func(); - if (pk11_decrypt_func != nullptr) { - return pk11_decrypt_func(key, mechanism, param, out, out_len, max_len, enc, - enc_len); - } - - // Otherwise, the user has an older version of NSS. Regrettably, NSS 3.14.x - // has a bug in the AES GCM code - // (https://bugzilla.mozilla.org/show_bug.cgi?id=853285), as well as missing - // the PK11_Decrypt function - // (https://bugzilla.mozilla.org/show_bug.cgi?id=854063), both of which are - // resolved in NSS 3.15. - - DCHECK_EQ(mechanism, static_cast<CK_MECHANISM_TYPE>(CKM_AES_GCM)); - DCHECK_EQ(param->len, sizeof(CK_GCM_PARAMS)); - - const CK_GCM_PARAMS* gcm_params = - reinterpret_cast<CK_GCM_PARAMS*>(param->data); - - DCHECK_EQ(gcm_params->ulTagBits, - static_cast<CK_ULONG>(Aes128Gcm12Decrypter::kAuthTagSize * 8)); - if (gcm_params->ulIvLen != 12u) { - DVLOG(1) << "ulIvLen is not equal to 12"; - PORT_SetError(SEC_ERROR_INPUT_LEN); - return SECFailure; - } - - SECItem my_param = { siBuffer, nullptr, 0 }; - - // Step 2. Let H = CIPH_K(128 '0' bits). - unsigned char ghash_key[16] = {0}; - crypto::ScopedPK11Context ctx(PK11_CreateContextBySymKey( - CKM_AES_ECB, CKA_ENCRYPT, key, &my_param)); - if (!ctx) { - DVLOG(1) << "PK11_CreateContextBySymKey failed"; - return SECFailure; - } - int output_len; - if (PK11_CipherOp(ctx.get(), ghash_key, &output_len, sizeof(ghash_key), - ghash_key, sizeof(ghash_key)) != SECSuccess) { - DVLOG(1) << "PK11_CipherOp failed"; - return SECFailure; - } - - PK11_Finalize(ctx.get()); - - if (output_len != sizeof(ghash_key)) { - DVLOG(1) << "Wrong output length"; - PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); - return SECFailure; - } - - // Step 3. If len(IV)=96, then let J0 = IV || 31 '0' bits || 1. - CK_AES_CTR_PARAMS ctr_params = {0}; - ctr_params.ulCounterBits = 32; - memcpy(ctr_params.cb, gcm_params->pIv, gcm_params->ulIvLen); - ctr_params.cb[12] = 0; - ctr_params.cb[13] = 0; - ctr_params.cb[14] = 0; - ctr_params.cb[15] = 1; - - my_param.type = siBuffer; - my_param.data = reinterpret_cast<unsigned char*>(&ctr_params); - my_param.len = sizeof(ctr_params); - - ctx.reset(PK11_CreateContextBySymKey(CKM_AES_CTR, CKA_ENCRYPT, key, - &my_param)); - if (!ctx) { - DVLOG(1) << "PK11_CreateContextBySymKey failed"; - return SECFailure; - } - - // Step 6. Calculate the encryption mask of GCTR_K(J0, ...). - unsigned char tag_mask[16] = {0}; - if (PK11_CipherOp(ctx.get(), tag_mask, &output_len, sizeof(tag_mask), - tag_mask, sizeof(tag_mask)) != SECSuccess) { - DVLOG(1) << "PK11_CipherOp failed"; - return SECFailure; - } - if (output_len != sizeof(tag_mask)) { - DVLOG(1) << "Wrong output length"; - PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); - return SECFailure; - } - - if (enc_len < Aes128Gcm12Decrypter::kAuthTagSize) { - PORT_SetError(SEC_ERROR_INPUT_LEN); - return SECFailure; - } - - // The const_cast for |enc| can be removed if system NSS libraries are - // NSS 3.14.1 or later (NSS bug - // https://bugzilla.mozilla.org/show_bug.cgi?id=808218). - if (PK11_CipherOp(ctx.get(), out, &output_len, max_len, - const_cast<unsigned char*>(enc), - enc_len - Aes128Gcm12Decrypter::kAuthTagSize) != SECSuccess) { - DVLOG(1) << "PK11_CipherOp failed"; - return SECFailure; - } - - PK11_Finalize(ctx.get()); - - if (static_cast<unsigned int>(output_len) != - enc_len - Aes128Gcm12Decrypter::kAuthTagSize) { - DVLOG(1) << "Wrong output length"; - PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); - return SECFailure; - } - - crypto::GaloisHash ghash(ghash_key); - ghash.UpdateAdditional(gcm_params->pAAD, gcm_params->ulAADLen); - ghash.UpdateCiphertext(enc, output_len); - unsigned char auth_tag[Aes128Gcm12Decrypter::kAuthTagSize]; - ghash.Finish(auth_tag, Aes128Gcm12Decrypter::kAuthTagSize); - for (unsigned int i = 0; i < Aes128Gcm12Decrypter::kAuthTagSize; i++) { - auth_tag[i] ^= tag_mask[i]; - } - - if (NSS_SecureMemcmp(auth_tag, enc + output_len, - Aes128Gcm12Decrypter::kAuthTagSize) != 0) { - PORT_SetError(SEC_ERROR_BAD_DATA); - return SECFailure; - } - - *out_len = output_len; - return SECSuccess; + const unsigned char* data, + unsigned int data_len) { + return crypto::PK11DecryptHelper(key, mechanism, param, out, out_len, max_len, + data, data_len); } } // namespace @@ -213,7 +38,6 @@ Aes128Gcm12Decrypter::Aes128Gcm12Decrypter() static_assert(kKeySize <= kMaxKeySize, "key size too big"); static_assert(kNoncePrefixSize <= kMaxNoncePrefixSize, "nonce prefix size too big"); - ignore_result(g_gcm_support_checker.Get()); } Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {} 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 6ad96f4f34d..bc9b519eab8 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 @@ -7,13 +7,7 @@ #include <pk11pub.h> #include <secerr.h> -#include "base/lazy_instance.h" -#include "crypto/ghash.h" -#include "crypto/scoped_nss_types.h" - -#if defined(USE_NSS_CERTS) -#include <dlfcn.h> -#endif +#include "crypto/aes_128_gcm_helpers_nss.h" using base::StringPiece; @@ -24,48 +18,6 @@ namespace { 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, -// lookup PK11_Encrypt with dlsym. - -// GcmSupportChecker is a singleton which caches the results of runtime symbol -// resolution of PK11_Encrypt. -class GcmSupportChecker { - public: - static PK11_EncryptFunction pk11_encrypt_func() { - return pk11_encrypt_func_; - } - - private: - friend struct base::DefaultLazyInstanceTraits<GcmSupportChecker>; - - GcmSupportChecker() { -#if !defined(USE_NSS_CERTS) - // Using a bundled version of NSS that is guaranteed to have this symbol. - pk11_encrypt_func_ = PK11_Encrypt; -#else - // Using system NSS libraries and PCKS #11 modules, which may not have the - // necessary function (PK11_Encrypt) or mechanism support (CKM_AES_GCM). - - // If PK11_Encrypt() was successfully resolved, then NSS will support - // AES-GCM directly. This was introduced in NSS 3.15. - pk11_encrypt_func_ = (PK11_EncryptFunction)dlsym(RTLD_DEFAULT, - "PK11_Encrypt"); -#endif - } - - // |pk11_encrypt_func_| stores the runtime symbol resolution of PK11_Encrypt. - static PK11_EncryptFunction pk11_encrypt_func_; -}; - -// static -PK11_EncryptFunction GcmSupportChecker::pk11_encrypt_func_ = nullptr; - -base::LazyInstance<GcmSupportChecker>::Leaky g_gcm_support_checker = - LAZY_INSTANCE_INITIALIZER; - -// 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, CK_MECHANISM_TYPE mechanism, SECItem* param, @@ -74,134 +26,8 @@ SECStatus My_Encrypt(PK11SymKey* key, unsigned int max_len, const unsigned char* data, unsigned int data_len) { - // If PK11_Encrypt() was successfully resolved or if bundled version of NSS is - // being used, then NSS will support AES-GCM directly. - PK11_EncryptFunction pk11_encrypt_func = - GcmSupportChecker::pk11_encrypt_func(); - if (pk11_encrypt_func != nullptr) { - return pk11_encrypt_func(key, mechanism, param, out, out_len, max_len, data, - data_len); - } - - // Otherwise, the user has an older version of NSS. Regrettably, NSS 3.14.x - // has a bug in the AES GCM code - // (https://bugzilla.mozilla.org/show_bug.cgi?id=853285), as well as missing - // the PK11_Encrypt function - // (https://bugzilla.mozilla.org/show_bug.cgi?id=854063), both of which are - // resolved in NSS 3.15. - - DCHECK_EQ(mechanism, static_cast<CK_MECHANISM_TYPE>(CKM_AES_GCM)); - DCHECK_EQ(param->len, sizeof(CK_GCM_PARAMS)); - - if (max_len < static_cast<unsigned int>(Aes128Gcm12Encrypter::kAuthTagSize)) { - DVLOG(1) << "max_len is less than kAuthTagSize"; - PORT_SetError(SEC_ERROR_OUTPUT_LEN); - return SECFailure; - } - - const CK_GCM_PARAMS* gcm_params = - reinterpret_cast<CK_GCM_PARAMS*>(param->data); - - DCHECK_EQ(gcm_params->ulTagBits, - static_cast<CK_ULONG>(Aes128Gcm12Encrypter::kAuthTagSize * 8)); - if (gcm_params->ulIvLen != 12u) { - DVLOG(1) << "ulIvLen is not equal to 12"; - PORT_SetError(SEC_ERROR_INPUT_LEN); - return SECFailure; - } - - SECItem my_param = { siBuffer, nullptr, 0 }; - - // Step 1. Let H = CIPH_K(128 '0' bits). - unsigned char ghash_key[16] = {0}; - crypto::ScopedPK11Context ctx(PK11_CreateContextBySymKey( - CKM_AES_ECB, CKA_ENCRYPT, key, &my_param)); - if (!ctx) { - DVLOG(1) << "PK11_CreateContextBySymKey failed"; - return SECFailure; - } - int output_len; - if (PK11_CipherOp(ctx.get(), ghash_key, &output_len, sizeof(ghash_key), - ghash_key, sizeof(ghash_key)) != SECSuccess) { - DVLOG(1) << "PK11_CipherOp failed"; - return SECFailure; - } - - PK11_Finalize(ctx.get()); - - if (output_len != sizeof(ghash_key)) { - DVLOG(1) << "Wrong output length"; - PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); - return SECFailure; - } - - // Step 2. If len(IV)=96, then let J0 = IV || 31 '0' bits || 1. - CK_AES_CTR_PARAMS ctr_params = {0}; - ctr_params.ulCounterBits = 32; - memcpy(ctr_params.cb, gcm_params->pIv, gcm_params->ulIvLen); - ctr_params.cb[12] = 0; - ctr_params.cb[13] = 0; - ctr_params.cb[14] = 0; - ctr_params.cb[15] = 1; - - my_param.type = siBuffer; - my_param.data = reinterpret_cast<unsigned char*>(&ctr_params); - my_param.len = sizeof(ctr_params); - - ctx.reset(PK11_CreateContextBySymKey(CKM_AES_CTR, CKA_ENCRYPT, key, - &my_param)); - if (!ctx) { - DVLOG(1) << "PK11_CreateContextBySymKey failed"; - return SECFailure; - } - - // Step 6. Calculate the encryption mask of GCTR_K(J0, ...). - unsigned char tag_mask[16] = {0}; - if (PK11_CipherOp(ctx.get(), tag_mask, &output_len, sizeof(tag_mask), - tag_mask, sizeof(tag_mask)) != SECSuccess) { - DVLOG(1) << "PK11_CipherOp failed"; - return SECFailure; - } - if (output_len != sizeof(tag_mask)) { - DVLOG(1) << "Wrong output length"; - PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); - return SECFailure; - } - - // The const_cast for |data| can be removed if system NSS libraries are - // NSS 3.14.1 or later (NSS bug - // https://bugzilla.mozilla.org/show_bug.cgi?id=808218). - if (PK11_CipherOp(ctx.get(), out, &output_len, max_len, - const_cast<unsigned char*>(data), data_len) != SECSuccess) { - DVLOG(1) << "PK11_CipherOp failed"; - return SECFailure; - } - - PK11_Finalize(ctx.get()); - - if (static_cast<unsigned int>(output_len) != data_len) { - DVLOG(1) << "Wrong output length"; - PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); - return SECFailure; - } - - if ((max_len - Aes128Gcm12Encrypter::kAuthTagSize) < - static_cast<unsigned int>(output_len)) { - DVLOG(1) << "(max_len - kAuthTagSize) is less than output_len"; - PORT_SetError(SEC_ERROR_OUTPUT_LEN); - return SECFailure; - } - - crypto::GaloisHash ghash(ghash_key); - ghash.UpdateAdditional(gcm_params->pAAD, gcm_params->ulAADLen); - ghash.UpdateCiphertext(out, output_len); - ghash.Finish(out + output_len, Aes128Gcm12Encrypter::kAuthTagSize); - for (unsigned int i = 0; i < Aes128Gcm12Encrypter::kAuthTagSize; i++) { - out[output_len + i] ^= tag_mask[i]; - } - - *out_len = output_len + Aes128Gcm12Encrypter::kAuthTagSize; - return SECSuccess; + return crypto::PK11EncryptHelper(key, mechanism, param, out, out_len, max_len, + data, data_len); } } // namespace @@ -212,7 +38,6 @@ Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() static_assert(kKeySize <= kMaxKeySize, "key size too big"); static_assert(kNoncePrefixSize <= kMaxNoncePrefixSize, "nonce prefix size too big"); - ignore_result(g_gcm_support_checker.Get()); } Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {} diff --git a/chromium/net/quic/crypto/channel_id_chromium.cc b/chromium/net/quic/crypto/channel_id_chromium.cc index 53feed30630..a4d6d919c55 100644 --- a/chromium/net/quic/crypto/channel_id_chromium.cc +++ b/chromium/net/quic/crypto/channel_id_chromium.cc @@ -17,8 +17,9 @@ namespace net { ChannelIDKeyChromium::ChannelIDKeyChromium( - crypto::ECPrivateKey* ec_private_key) - : ec_private_key_(ec_private_key) {} + scoped_ptr<crypto::ECPrivateKey> ec_private_key) + : ec_private_key_(ec_private_key.Pass()) { +} ChannelIDKeyChromium::~ChannelIDKeyChromium() {} @@ -43,7 +44,7 @@ bool ChannelIDKeyChromium::Sign(base::StringPiece signed_data, if (!sig_creator->DecodeSignature(der_signature, &raw_signature)) { return false; } - memcpy(WriteInto(out_signature, raw_signature.size() + 1), + memcpy(base::WriteInto(out_signature, raw_signature.size() + 1), &raw_signature[0], raw_signature.size()); return true; } @@ -87,9 +88,8 @@ class ChannelIDSourceChromium::Job { ChannelIDService* const channel_id_service_; - std::string channel_id_private_key_; - std::string channel_id_cert_; - ChannelIDService::RequestHandle channel_id_request_handle_; + scoped_ptr<crypto::ECPrivateKey> channel_id_crypto_key_; + ChannelIDService::Request channel_id_request_; // |hostname| specifies the hostname for which we need a channel ID. std::string hostname_; @@ -178,12 +178,10 @@ int ChannelIDSourceChromium::Job::DoGetChannelIDKey(int result) { next_state_ = STATE_GET_CHANNEL_ID_KEY_COMPLETE; return channel_id_service_->GetOrCreateChannelID( - hostname_, - &channel_id_private_key_, - &channel_id_cert_, + hostname_, &channel_id_crypto_key_, base::Bind(&ChannelIDSourceChromium::Job::OnIOComplete, base::Unretained(this)), - &channel_id_request_handle_); + &channel_id_request_); } int ChannelIDSourceChromium::Job::DoGetChannelIDKeyComplete(int result) { @@ -193,28 +191,13 @@ int ChannelIDSourceChromium::Job::DoGetChannelIDKeyComplete(int result) { return result; } - std::vector<uint8> encrypted_private_key_info( - channel_id_private_key_.size()); - memcpy(&encrypted_private_key_info[0], channel_id_private_key_.data(), - channel_id_private_key_.size()); - - base::StringPiece spki_piece; - if (!asn1::ExtractSPKIFromDERCert(channel_id_cert_, &spki_piece)) { - return ERR_UNEXPECTED; - } - std::vector<uint8> subject_public_key_info(spki_piece.size()); - memcpy(&subject_public_key_info[0], spki_piece.data(), spki_piece.size()); - - crypto::ECPrivateKey* ec_private_key = - crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( - ChannelIDService::kEPKIPassword, encrypted_private_key_info, - subject_public_key_info); - if (!ec_private_key) { + if (!channel_id_crypto_key_) { // TODO(wtc): use the new error code ERR_CHANNEL_ID_IMPORT_FAILED to be // added in https://codereview.chromium.org/338093012/. return ERR_UNEXPECTED; } - channel_id_key_.reset(new ChannelIDKeyChromium(ec_private_key)); + channel_id_key_.reset( + new ChannelIDKeyChromium(channel_id_crypto_key_.Pass())); return result; } diff --git a/chromium/net/quic/crypto/channel_id_chromium.h b/chromium/net/quic/crypto/channel_id_chromium.h index 20a1d8d7aa8..43a1ed390b6 100644 --- a/chromium/net/quic/crypto/channel_id_chromium.h +++ b/chromium/net/quic/crypto/channel_id_chromium.h @@ -19,7 +19,8 @@ class ChannelIDService; class NET_EXPORT_PRIVATE ChannelIDKeyChromium: public ChannelIDKey { public: - explicit ChannelIDKeyChromium(crypto::ECPrivateKey* ec_private_key); + explicit ChannelIDKeyChromium( + scoped_ptr<crypto::ECPrivateKey> ec_private_key); ~ChannelIDKeyChromium() override; // ChannelIDKey interface diff --git a/chromium/net/quic/crypto/common_cert_set_1.c b/chromium/net/quic/crypto/common_cert_set_1.c index ba182332b5a..aa436e2e926 100644 --- a/chromium/net/quic/crypto/common_cert_set_1.c +++ b/chromium/net/quic/crypto/common_cert_set_1.c @@ -140,4 +140,4 @@ static const size_t kLens[] = { 1770, }; -static const uint64 kHash = GG_UINT64_C(0xff715ce4e7e9267b); +static const uint64 kHash = UINT64_C(0xff715ce4e7e9267b); diff --git a/chromium/net/quic/crypto/common_cert_set_test.cc b/chromium/net/quic/crypto/common_cert_set_test.cc index ad81ec4955e..09b06d18eb5 100644 --- a/chromium/net/quic/crypto/common_cert_set_test.cc +++ b/chromium/net/quic/crypto/common_cert_set_test.cc @@ -4,6 +4,8 @@ #include "net/quic/crypto/common_cert_set.h" +#include <stdint.h> + #include "testing/gtest/include/gtest/gtest.h" using base::StringPiece; @@ -104,7 +106,7 @@ TEST(CommonCertSets, FindGIA_1) { sizeof(kGIACertificate1)); const CommonCertSets* sets(CommonCertSets::GetInstanceQUIC()); - const uint64 in_hash = GG_UINT64_C(0xff715ce4e7e9267b); + const uint64 in_hash = UINT64_C(0xff715ce4e7e9267b); uint64 hash; uint32 index; ASSERT_TRUE(sets->MatchCert( @@ -122,7 +124,7 @@ TEST(CommonCertSets, FindGIA_1) { TEST(CommonCertSets, NonMatch) { const CommonCertSets* sets(CommonCertSets::GetInstanceQUIC()); StringPiece not_a_cert("hello"); - const uint64 in_hash = GG_UINT64_C(0xc9fef74053f99f39); + const uint64 in_hash = UINT64_C(0xc9fef74053f99f39); uint64 hash; uint32 index; EXPECT_FALSE(sets->MatchCert( diff --git a/chromium/net/quic/crypto/crypto_protocol.h b/chromium/net/quic/crypto/crypto_protocol.h index 1f881ea2ea6..2d16b9ce595 100644 --- a/chromium/net/quic/crypto/crypto_protocol.h +++ b/chromium/net/quic/crypto/crypto_protocol.h @@ -53,10 +53,15 @@ const QuicTag kSRBF = TAG('S', 'R', 'B', 'F'); // Socket receive buffer const QuicTag kQBIC = TAG('Q', 'B', 'I', 'C'); // TCP cubic // Connection options (COPT) values +const QuicTag kAFCW = TAG('A', 'F', 'C', 'W'); // Auto-tune flow control + // receive windows. const QuicTag kTBBR = TAG('T', 'B', 'B', 'R'); // Reduced Buffer Bloat TCP const QuicTag kRENO = TAG('R', 'E', 'N', 'O'); // Reno Congestion Control const QuicTag kBYTE = TAG('B', 'Y', 'T', 'E'); // TCP cubic or reno in bytes +const QuicTag kIW03 = TAG('I', 'W', '0', '3'); // Force ICWND to 3 const QuicTag kIW10 = TAG('I', 'W', '1', '0'); // Force ICWND to 10 +const QuicTag kIW20 = TAG('I', 'W', '2', '0'); // Force ICWND to 20 +const QuicTag kIW50 = TAG('I', 'W', '5', '0'); // Force ICWND to 50 const QuicTag k1CON = TAG('1', 'C', 'O', 'N'); // Emulate a single connection const QuicTag kNTLP = TAG('N', 'T', 'L', 'P'); // No tail loss probe const QuicTag kNCON = TAG('N', 'C', 'O', 'N'); // N Connection Congestion Ctrl @@ -73,11 +78,18 @@ const QuicTag kTCID = TAG('T', 'C', 'I', 'D'); // Connection ID truncation. // FEC options const QuicTag kFHDR = TAG('F', 'H', 'D', 'R'); // FEC protect headers +const QuicTag kFSTR = TAG('F', 'S', 'T', 'R'); // FEC protect all streams +// Set FecSendPolicy for sending FEC packet only when FEC alarm goes off. +const QuicTag kFSPA = TAG('F', 'S', 'P', 'A'); // Enable bandwidth resumption experiment. const QuicTag kBWRE = TAG('B', 'W', 'R', 'E'); // Bandwidth resumption. const QuicTag kBWMX = TAG('B', 'W', 'M', 'X'); // Max bandwidth resumption. +// Enable path MTU discovery experiment. +const QuicTag kMTUH = TAG('M', 'T', 'U', 'H'); // High-target MTU discovery. +const QuicTag kMTUL = TAG('M', 'T', 'U', 'L'); // Low-target MTU discovery. + // 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. diff --git a/chromium/net/quic/crypto/crypto_secret_boxer.cc b/chromium/net/quic/crypto/crypto_secret_boxer.cc index b139a6a50fb..a7898d995a4 100644 --- a/chromium/net/quic/crypto/crypto_secret_boxer.cc +++ b/chromium/net/quic/crypto/crypto_secret_boxer.cc @@ -6,6 +6,8 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "net/quic/crypto/aes_128_gcm_12_decrypter.h" +#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" @@ -41,7 +43,7 @@ void CryptoSecretBoxer::SetKey(StringPiece key) { } string CryptoSecretBoxer::Box(QuicRandom* rand, StringPiece plaintext) const { - scoped_ptr<QuicEncrypter> encrypter(QuicEncrypter::Create(kAESG)); + scoped_ptr<Aes128Gcm12Encrypter> encrypter(new Aes128Gcm12Encrypter()); if (!encrypter->SetKey(key_)) { DLOG(DFATAL) << "CryptoSecretBoxer's encrypter->SetKey failed."; return string(); @@ -82,7 +84,7 @@ bool CryptoSecretBoxer::Unbox(StringPiece ciphertext, memcpy(&sequence_number, nonce.data() + nonce_prefix.size(), sizeof(sequence_number)); - scoped_ptr<QuicDecrypter> decrypter(QuicDecrypter::Create(kAESG)); + scoped_ptr<Aes128Gcm12Decrypter> decrypter(new Aes128Gcm12Decrypter()); if (!decrypter->SetKey(key_)) { DLOG(DFATAL) << "CryptoSecretBoxer's decrypter->SetKey failed."; return false; diff --git a/chromium/net/quic/crypto/null_decrypter.cc b/chromium/net/quic/crypto/null_decrypter.cc index 8a18172a5c2..19f9ad10102 100644 --- a/chromium/net/quic/crypto/null_decrypter.cc +++ b/chromium/net/quic/crypto/null_decrypter.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 <stdint.h> + #include "net/quic/crypto/null_decrypter.h" #include "net/quic/quic_utils.h" #include "net/quic/quic_data_reader.h" @@ -67,7 +69,7 @@ uint128 NullDecrypter::ComputeHash(const StringPiece& data1, const StringPiece& data2) const { uint128 correct_hash = QuicUtils::FNV1a_128_Hash_Two( data1.data(), data1.length(), data2.data(), data2.length()); - uint128 mask(GG_UINT64_C(0x0), GG_UINT64_C(0xffffffff)); + uint128 mask(UINT64_C(0x0), UINT64_C(0xffffffff)); mask <<= 96; correct_hash &= ~mask; return correct_hash; diff --git a/chromium/net/quic/crypto/null_encrypter.cc b/chromium/net/quic/crypto/null_encrypter.cc index 286694ad7b2..c1e823e78cc 100644 --- a/chromium/net/quic/crypto/null_encrypter.cc +++ b/chromium/net/quic/crypto/null_encrypter.cc @@ -21,19 +21,6 @@ bool NullEncrypter::SetNoncePrefix(StringPiece nonce_prefix) { return nonce_prefix.empty(); } -bool NullEncrypter::Encrypt( - StringPiece /*nonce*/, - StringPiece associated_data, - StringPiece plaintext, - unsigned char* output) { - string buffer = associated_data.as_string(); - plaintext.AppendToString(&buffer); - uint128 hash = QuicUtils::FNV1a_128_Hash(buffer.data(), buffer.length()); - QuicUtils::SerializeUint128Short(hash, output); - memcpy(output + GetHashLength(), plaintext.data(), plaintext.size()); - return true; -} - bool NullEncrypter::EncryptPacket(QuicPacketSequenceNumber /*sequence_number*/, StringPiece associated_data, StringPiece plaintext, @@ -44,8 +31,12 @@ bool NullEncrypter::EncryptPacket(QuicPacketSequenceNumber /*sequence_number*/, if (max_output_length < len) { return false; } - Encrypt(StringPiece(), associated_data, plaintext, - reinterpret_cast<unsigned char*>(output)); + uint128 hash = QuicUtils::FNV1a_128_Hash_Two( + associated_data.data(), associated_data.size(), plaintext.data(), + plaintext.size()); + QuicUtils::SerializeUint128Short(hash, + reinterpret_cast<unsigned char*>(output)); + memcpy(output + GetHashLength(), plaintext.data(), plaintext.length()); *output_length = len; return true; } diff --git a/chromium/net/quic/crypto/null_encrypter.h b/chromium/net/quic/crypto/null_encrypter.h index 40bcacc703e..66618fb9530 100644 --- a/chromium/net/quic/crypto/null_encrypter.h +++ b/chromium/net/quic/crypto/null_encrypter.h @@ -22,10 +22,6 @@ class NET_EXPORT_PRIVATE NullEncrypter : public QuicEncrypter { // QuicEncrypter implementation bool SetKey(base::StringPiece key) override; bool SetNoncePrefix(base::StringPiece nonce_prefix) override; - bool Encrypt(base::StringPiece nonce, - base::StringPiece associated_data, - base::StringPiece plaintext, - unsigned char* output) override; bool EncryptPacket(QuicPacketSequenceNumber sequence_number, base::StringPiece associated_data, base::StringPiece plaintext, diff --git a/chromium/net/quic/crypto/proof_verifier_chromium.cc b/chromium/net/quic/crypto/proof_verifier_chromium.cc index 280ca5ade8d..90543cbbbcc 100644 --- a/chromium/net/quic/crypto/proof_verifier_chromium.cc +++ b/chromium/net/quic/crypto/proof_verifier_chromium.cc @@ -9,7 +9,7 @@ #include "base/callback_helpers.h" #include "base/compiler_specific.h" #include "base/logging.h" -#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros.h" #include "base/profiler/scoped_tracker.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" diff --git a/chromium/net/quic/crypto/proof_verifier_chromium.h b/chromium/net/quic/crypto/proof_verifier_chromium.h index 7c0b069f8e2..ca7358a9604 100644 --- a/chromium/net/quic/crypto/proof_verifier_chromium.h +++ b/chromium/net/quic/crypto/proof_verifier_chromium.h @@ -35,7 +35,7 @@ class NET_EXPORT_PRIVATE ProofVerifyDetailsChromium CertVerifyResult cert_verify_result; // pinning_failure_log contains a message produced by - // TransportSecurityState::DomainState::CheckPublicKeyPins in the event of a + // TransportSecurityState::PKPState::CheckPublicKeyPins in the event of a // pinning failure. It is a (somewhat) human-readable string. std::string pinning_failure_log; }; diff --git a/chromium/net/quic/crypto/quic_crypto_client_config.cc b/chromium/net/quic/crypto/quic_crypto_client_config.cc index a95c0f08e2b..a041f4c292f 100644 --- a/chromium/net/quic/crypto/quic_crypto_client_config.cc +++ b/chromium/net/quic/crypto/quic_crypto_client_config.cc @@ -4,7 +4,7 @@ #include "net/quic/crypto/quic_crypto_client_config.h" -#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros.h" #include "base/metrics/sparse_histogram.h" #include "base/stl_util.h" #include "base/strings/string_util.h" @@ -128,6 +128,15 @@ bool QuicCryptoClientConfig::CachedState::has_server_designated_connection_id() return !server_designated_connection_ids_.empty(); } +void QuicCryptoClientConfig::CachedState::add_server_nonce( + const string& server_nonce) { + server_nonces_.push(server_nonce); +} + +bool QuicCryptoClientConfig::CachedState::has_server_nonce() const { + return !server_nonces_.empty(); +} + QuicCryptoClientConfig::CachedState::ServerConfigState QuicCryptoClientConfig::CachedState::SetServerConfig( StringPiece server_config, QuicWallTime now, string* error_details) { @@ -325,6 +334,17 @@ QuicCryptoClientConfig::CachedState::GetNextServerDesignatedConnectionId() { return next_id; } +string QuicCryptoClientConfig::CachedState::GetNextServerNonce() { + if (server_nonces_.empty()) { + LOG(DFATAL) + << "Attempting to consume a server nonce that was never designated."; + return ""; + } + const string server_nonce = server_nonces_.front(); + server_nonces_.pop(); + return server_nonce; +} + void QuicCryptoClientConfig::SetDefaults() { // Key exchange methods. kexs.resize(2); @@ -727,6 +747,9 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection( return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; } cached->add_server_designated_connection_id(connection_id); + if (!nonce.empty()) { + cached->add_server_nonce(nonce.as_string()); + } return QUIC_NO_ERROR; } @@ -881,7 +904,7 @@ bool QuicCryptoClientConfig::PopulateFromCanonicalConfig( DCHECK(server_state->IsEmpty()); size_t i = 0; for (; i < canonical_suffixes_.size(); ++i) { - if (EndsWith(server_id.host(), canonical_suffixes_[i], false)) { + if (base::EndsWith(server_id.host(), canonical_suffixes_[i], false)) { break; } } diff --git a/chromium/net/quic/crypto/quic_crypto_client_config.h b/chromium/net/quic/crypto/quic_crypto_client_config.h index a65f007e9b1..4eb4caf0938 100644 --- a/chromium/net/quic/crypto/quic_crypto_client_config.h +++ b/chromium/net/quic/crypto/quic_crypto_client_config.h @@ -124,6 +124,18 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { // queue of ids. QuicConnectionId GetNextServerDesignatedConnectionId(); + // Adds the servernonce to the queue of server nonces. + void add_server_nonce(const std::string& server_nonce); + + // If true, the crypto config contains at least one server nonce, and the + // client should use one of these nonces. + bool has_server_nonce() const; + + // This function should only be called when has_server_nonce is true. + // Returns the next connection_id specified by the server and removes it + // from the queue of ids. + std::string GetNextServerNonce(); + // SetProofVerifyDetails takes ownership of |details|. void SetProofVerifyDetails(ProofVerifyDetails* details); @@ -161,8 +173,10 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { mutable scoped_ptr<CryptoHandshakeMessage> scfg_; // TODO(jokulik): Consider using a hash-set as extra book-keeping to ensure - // that no connection-id is added twice. + // that no connection-id is added twice. Also, consider keeping the server + // nonces and connection_ids together in one queue. std::queue<QuicConnectionId> server_designated_connection_ids_; + std::queue<std::string> server_nonces_; DISALLOW_COPY_AND_ASSIGN(CachedState); }; 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 348f8f90e4d..0e1ea792f86 100644 --- a/chromium/net/quic/crypto/quic_crypto_client_config_test.cc +++ b/chromium/net/quic/crypto/quic_crypto_client_config_test.cc @@ -94,6 +94,47 @@ TEST(QuicCryptoClientConfigTest, CachedState_ServerIdConsumedBeforeSet) { #endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) } +TEST(QuicCryptoClientConfigTest, CachedState_ServerNonce) { + QuicCryptoClientConfig::CachedState state; + EXPECT_FALSE(state.has_server_nonce()); + + string server_nonce = "nonce_1"; + state.add_server_nonce(server_nonce); + EXPECT_TRUE(state.has_server_nonce()); + EXPECT_EQ(server_nonce, state.GetNextServerNonce()); + EXPECT_FALSE(state.has_server_nonce()); + + // Allow the ID to be set multiple times. It's unusual that this would + // happen, but not impossible. + server_nonce = "nonce_2"; + state.add_server_nonce(server_nonce); + EXPECT_TRUE(state.has_server_nonce()); + EXPECT_EQ(server_nonce, state.GetNextServerNonce()); + server_nonce = "nonce_3"; + state.add_server_nonce(server_nonce); + EXPECT_EQ(server_nonce, state.GetNextServerNonce()); + EXPECT_FALSE(state.has_server_nonce()); + + // Test FIFO behavior. + const string first_nonce = "first_nonce"; + const string second_nonce = "second_nonce"; + state.add_server_nonce(first_nonce); + state.add_server_nonce(second_nonce); + EXPECT_TRUE(state.has_server_nonce()); + EXPECT_EQ(first_nonce, state.GetNextServerNonce()); + EXPECT_EQ(second_nonce, state.GetNextServerNonce()); +} + +TEST(QuicCryptoClientConfigTest, CachedState_ServerNonceConsumedBeforeSet) { + QuicCryptoClientConfig::CachedState state; + EXPECT_FALSE(state.has_server_nonce()); +#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG) + EXPECT_DEBUG_DEATH(state.GetNextServerNonce(), + "Attempting to consume a server nonce " + "that was never designated."); +#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) +} + TEST(QuicCryptoClientConfigTest, CachedState_InitializeFrom) { QuicCryptoClientConfig::CachedState state; QuicCryptoClientConfig::CachedState other; @@ -105,6 +146,7 @@ TEST(QuicCryptoClientConfigTest, CachedState_InitializeFrom) { EXPECT_EQ(state.certs(), other.certs()); EXPECT_EQ(1u, other.generation_counter()); EXPECT_FALSE(state.has_server_designated_connection_id()); + EXPECT_FALSE(state.has_server_nonce()); } TEST(QuicCryptoClientConfigTest, InchoateChlo) { @@ -356,6 +398,7 @@ TEST(QuicCryptoClientConfigTest, ProcessReject) { true, // is_https &out_params, &error)); EXPECT_FALSE(cached.has_server_designated_connection_id()); + EXPECT_FALSE(cached.has_server_nonce()); } TEST(QuicCryptoClientConfigTest, ProcessStatelessReject) { @@ -363,7 +406,9 @@ TEST(QuicCryptoClientConfigTest, ProcessStatelessReject) { CryptoHandshakeMessage rej; FillInDummyReject(&rej, /* stateless */ true); const QuicConnectionId kConnectionId = 0xdeadbeef; + const string server_nonce = "SERVER_NONCE"; rej.SetValue(kRCID, kConnectionId); + rej.SetStringPiece(kServerNonceTag, server_nonce); // Now process the rejection. QuicCryptoClientConfig::CachedState cached; @@ -376,6 +421,7 @@ TEST(QuicCryptoClientConfigTest, ProcessStatelessReject) { &out_params, &error)); EXPECT_TRUE(cached.has_server_designated_connection_id()); EXPECT_EQ(kConnectionId, cached.GetNextServerDesignatedConnectionId()); + EXPECT_EQ(server_nonce, cached.GetNextServerNonce()); } TEST(QuicCryptoClientConfigTest, BadlyFormattedStatelessReject) { diff --git a/chromium/net/quic/crypto/quic_crypto_server_config.h b/chromium/net/quic/crypto/quic_crypto_server_config.h index ab7442f0629..f7716e25f2b 100644 --- a/chromium/net/quic/crypto/quic_crypto_server_config.h +++ b/chromium/net/quic/crypto/quic_crypto_server_config.h @@ -215,8 +215,8 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // ProcessClientHello processes |client_hello| and decides whether to accept // or reject the connection. If the connection is to be accepted, |out| is // set to the contents of the ServerHello, |out_params| is completed and - // QUIC_NO_ERROR is returned. Otherwise |out| is set to be a REJ message and - // an error code is returned. + // QUIC_NO_ERROR is returned. Otherwise |out| is set to be a REJ or SREJ + // message and QUIC_NO_ERROR is returned. // // validate_chlo_result: Output from the asynchronous call to // ValidateClientHello. Contains the client hello message and diff --git a/chromium/net/quic/crypto/quic_encrypter.h b/chromium/net/quic/crypto/quic_encrypter.h index 2e565c543bc..a44ef207096 100644 --- a/chromium/net/quic/crypto/quic_encrypter.h +++ b/chromium/net/quic/crypto/quic_encrypter.h @@ -39,16 +39,6 @@ class NET_EXPORT_PRIVATE QuicEncrypter { // packet sequence number, even when retransmitting a lost packet. virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) = 0; - // Encrypt encrypts |plaintext| and writes the ciphertext, plus a MAC over - // both |associated_data| and |plaintext| to |output|, using |nonce| as the - // nonce. |nonce| must be |8+GetNoncePrefixSize()| bytes long and |output| - // must point to a buffer that is at least - // |GetCiphertextSize(plaintext.size()| bytes long. - virtual bool Encrypt(base::StringPiece nonce, - base::StringPiece associated_data, - base::StringPiece plaintext, - unsigned char* output) = 0; - // Returns a newly created QuicData object containing the encrypted // |plaintext| as well as a MAC over both |plaintext| and |associated_data|, // or nullptr if there is an error. |sequence_number| is appended to the diff --git a/chromium/net/quic/crypto/quic_server_info.cc b/chromium/net/quic/crypto/quic_server_info.cc index c86d1f63c10..40111519fc2 100644 --- a/chromium/net/quic/crypto/quic_server_info.cc +++ b/chromium/net/quic/crypto/quic_server_info.cc @@ -63,8 +63,8 @@ bool QuicServerInfo::ParseInner(const string& data) { return false; } - Pickle p(data.data(), data.size()); - PickleIterator iter(p); + base::Pickle p(data.data(), data.size()); + base::PickleIterator iter(p); int version = -1; if (!iter.ReadInt(&version)) { @@ -116,7 +116,7 @@ string QuicServerInfo::Serialize() { } string QuicServerInfo::SerializeInner() const { - Pickle p(sizeof(Pickle::Header)); + base::Pickle p(sizeof(base::Pickle::Header)); if (!p.WriteInt(kQuicCryptoConfigVersion) || !p.WriteString(state_.server_config) || diff --git a/chromium/net/quic/iovector.h b/chromium/net/quic/iovector.h index 22d2cc9a8cd..fcf50e87d57 100644 --- a/chromium/net/quic/iovector.h +++ b/chromium/net/quic/iovector.h @@ -89,7 +89,7 @@ class NET_EXPORT_PRIVATE IOVector { // and write, it always takes char*. Clients that writes will need to cast // away the constant of the pointer before appending a block. void Append(char* buffer, size_t length) { - if (buffer != NULL && length > 0) { + if (buffer != nullptr && length > 0) { if (iovec_.size() > 0) { struct iovec& last = iovec_.back(); // If the new block is contiguous with the last block, just extend. @@ -106,7 +106,7 @@ class NET_EXPORT_PRIVATE IOVector { // Same as Append, but doesn't do the tail merge optimization. // Intended for testing. void AppendNoCoalesce(char* buffer, size_t length) { - if (buffer != NULL && length > 0) { + if (buffer != nullptr && length > 0) { struct iovec tmp = {buffer, length}; iovec_.push_back(tmp); } @@ -127,7 +127,7 @@ class NET_EXPORT_PRIVATE IOVector { bytes_to_consume -= iter->iov_len; } iovec_.erase(iovec_.begin(), iter); - if (iovec_.size() > 0 && bytes_to_consume != 0) { + if (!iovec_.empty() && bytes_to_consume != 0) { iovec_[0].iov_base = static_cast<char*>(iovec_[0].iov_base) + bytes_to_consume; iovec_[0].iov_len -= bytes_to_consume; @@ -142,6 +142,41 @@ class NET_EXPORT_PRIVATE IOVector { return length - bytes_to_consume; } + // Identical to Consume, but also copies the portion of the buffer being + // consumed into |buffer|. |buffer| must be at least size |length|. If + // the IOVector is less than |length|, the method consumes the entire + // IOVector, logs an error and returns the length consumed. + size_t ConsumeAndCopy(size_t length, char* buffer) { + if (length == 0) + return 0; + + size_t bytes_to_consume = length; + // First consume all the iovecs which can be consumed completely. + std::vector<struct iovec>::iterator iter = iovec_.begin(); + std::vector<struct iovec>::iterator end = iovec_.end(); + for (; iter < end && bytes_to_consume >= iter->iov_len; ++iter) { + memcpy(buffer, iter->iov_base, iter->iov_len); + bytes_to_consume -= iter->iov_len; + buffer += iter->iov_len; + } + iovec_.erase(iovec_.begin(), iter); + if (bytes_to_consume == 0) { + return length; + } + if (iovec_.empty()) { + LOG_IF(DFATAL, bytes_to_consume > 0) << "Attempting to consume " + << bytes_to_consume + << " non-existent bytes."; + return length - bytes_to_consume; + } + // Partially consume the next iovec. + memcpy(buffer, iovec_[0].iov_base, bytes_to_consume); + iovec_[0].iov_base = + static_cast<char*>(iovec_[0].iov_base) + bytes_to_consume; + iovec_[0].iov_len -= bytes_to_consume; + return length; + } + // TODO(joechan): If capacity is large, swap out for a blank one. // Clears the IOVector object to contain no blocks. void Clear() { iovec_.clear(); } diff --git a/chromium/net/quic/iovector_test.cc b/chromium/net/quic/iovector_test.cc index 37a1523e5ce..f25ae1b5f51 100644 --- a/chromium/net/quic/iovector_test.cc +++ b/chromium/net/quic/iovector_test.cc @@ -7,6 +7,8 @@ #include <string.h> #include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "net/test/gtest_util.h" #include "testing/gtest/include/gtest/gtest.h" using std::string; @@ -63,7 +65,7 @@ TEST(IOVectorTest, Append) { for (size_t i = 0; i < arraysize(test_data); ++i) { const int str_len = strlen(test_data[i]); const int append_len = str_len / 2; - // This should append a new block + // This should append a new block. iov.Append(const_cast<char*>(test_data[i]), append_len); length += append_len; ASSERT_EQ(i + 1, static_cast<size_t>(iov.Size())); @@ -140,7 +142,7 @@ TEST(IOVectorTest, ConsumeHalfBlocks) { ASSERT_TRUE(iov2[0].iov_base == test_data[i] + tmp); ASSERT_EQ(iov2[0].iov_len, str_len - tmp); - // Consume the rest of the first block + // Consume the rest of the first block. consumed = iov.Consume(str_len - tmp); ASSERT_EQ(str_len - tmp, consumed); ASSERT_EQ(arraysize(test_data) - i - 1, static_cast<size_t>(iov.Size())); @@ -197,13 +199,107 @@ TEST(IOVectorTest, ConsumeTooMuch) { } int consumed = 0; - consumed = iov.Consume(length); - // TODO(rtenneti): enable when chromium supports EXPECT_DFATAL. - /* EXPECT_DFATAL( {consumed = iov.Consume(length + 1);}, "Attempting to consume 1 non-existent bytes."); - */ + ASSERT_EQ(length, consumed); + const struct iovec* iov2 = iov.iovec(); + ASSERT_EQ(0u, iov.Size()); + ASSERT_TRUE(iov2 == nullptr); + ASSERT_TRUE(iov.LastBlockEnd() == nullptr); +} + +TEST(IOVectorTest, ConsumeAndCopyHalfBlocks) { + IOVector iov; + int length = 0; + + for (size_t i = 0; i < arraysize(test_data); ++i) { + const int str_len = strlen(test_data[i]); + iov.Append(const_cast<char*>(test_data[i]), str_len); + length += str_len; + } + const char* endp = iov.LastBlockEnd(); + for (size_t i = 0; i < arraysize(test_data); ++i) { + const struct iovec* iov2 = iov.iovec(); + const size_t str_len = strlen(test_data[i]); + size_t tmp = str_len / 2; + + ASSERT_TRUE(iov2 != nullptr); + ASSERT_TRUE(iov2[0].iov_base == test_data[i]); + ASSERT_EQ(str_len, iov2[0].iov_len); + + // Consume half of the first block. + scoped_ptr<char[]> buffer(new char[str_len]); + size_t consumed = iov.ConsumeAndCopy(tmp, buffer.get()); + EXPECT_EQ(0, memcmp(test_data[i], buffer.get(), tmp)); + ASSERT_EQ(tmp, consumed); + ASSERT_EQ(arraysize(test_data) - i, static_cast<size_t>(iov.Size())); + iov2 = iov.iovec(); + ASSERT_TRUE(iov2 != nullptr); + ASSERT_TRUE(iov2[0].iov_base == test_data[i] + tmp); + ASSERT_EQ(iov2[0].iov_len, str_len - tmp); + + // Consume the rest of the first block. + consumed = iov.ConsumeAndCopy(str_len - tmp, buffer.get()); + ASSERT_EQ(str_len - tmp, consumed); + ASSERT_EQ(arraysize(test_data) - i - 1, static_cast<size_t>(iov.Size())); + iov2 = iov.iovec(); + if (iov.Size() > 0) { + ASSERT_TRUE(iov2 != nullptr); + ASSERT_TRUE(iov.LastBlockEnd() == endp); + } else { + ASSERT_TRUE(iov2 == nullptr); + ASSERT_TRUE(iov.LastBlockEnd() == nullptr); + } + } +} + +TEST(IOVectorTest, ConsumeAndCopyTwoAndHalfBlocks) { + IOVector iov; + size_t length = 0; + + for (size_t i = 0; i < arraysize(test_data); ++i) { + const int str_len = strlen(test_data[i]); + iov.Append(const_cast<char*>(test_data[i]), str_len); + length += str_len; + } + const size_t last_len = strlen(test_data[arraysize(test_data) - 1]); + const size_t half_len = last_len / 2; + + const char* endp = iov.LastBlockEnd(); + scoped_ptr<char[]> buffer(new char[length]); + size_t consumed = iov.ConsumeAndCopy(length - half_len, buffer.get()); + ASSERT_EQ(length - half_len, consumed); + const struct iovec* iov2 = iov.iovec(); + ASSERT_TRUE(iov2 != nullptr); + ASSERT_EQ(1u, iov.Size()); + ASSERT_TRUE(iov2[0].iov_base == + test_data[arraysize(test_data) - 1] + last_len - half_len); + ASSERT_EQ(half_len, iov2[0].iov_len); + ASSERT_TRUE(iov.LastBlockEnd() == endp); + + consumed = iov.Consume(half_len); + ASSERT_EQ(half_len, consumed); + iov2 = iov.iovec(); + ASSERT_EQ(0u, iov.Size()); + ASSERT_TRUE(iov2 == nullptr); + ASSERT_TRUE(iov.LastBlockEnd() == nullptr); +} + +TEST(IOVectorTest, ConsumeAndCopyTooMuch) { + IOVector iov; + int length = 0; + + for (size_t i = 0; i < arraysize(test_data); ++i) { + const int str_len = strlen(test_data[i]); + iov.Append(const_cast<char*>(test_data[i]), str_len); + length += str_len; + } + + int consumed = 0; + scoped_ptr<char[]> buffer(new char[length + 1]); + EXPECT_DFATAL({ consumed = iov.ConsumeAndCopy(length + 1, buffer.get()); }, + "Attempting to consume 1 non-existent bytes."); ASSERT_EQ(length, consumed); const struct iovec* iov2 = iov.iovec(); ASSERT_EQ(0u, iov.Size()); diff --git a/chromium/net/quic/quic_ack_notifier_manager.cc b/chromium/net/quic/quic_ack_notifier_manager.cc index cfdf8f90ffc..599e7d74c92 100644 --- a/chromium/net/quic/quic_ack_notifier_manager.cc +++ b/chromium/net/quic/quic_ack_notifier_manager.cc @@ -89,4 +89,25 @@ void AckNotifierManager::OnSerializedPacket( } } +void AckNotifierManager::OnPacketRemoved( + QuicPacketSequenceNumber sequence_number) { + // Determine if there are any notifiers interested in this packet. + auto map_it = ack_notifier_map_.find(sequence_number); + if (map_it == ack_notifier_map_.end()) { + return; + } + + // Notify all of the interested notifiers that the packet is abandoned. + for (QuicAckNotifier* ack_notifier : map_it->second) { + DCHECK(ack_notifier); + if (ack_notifier->OnPacketAbandoned()) { + // If this has resulted in an empty AckNotifer, erase it. + delete ack_notifier; + } + } + + // Remove the packet with given sequence number from the map. + ack_notifier_map_.erase(map_it); +} + } // namespace net diff --git a/chromium/net/quic/quic_ack_notifier_manager.h b/chromium/net/quic/quic_ack_notifier_manager.h index d4d97ad7cd7..aa9d9d1670c 100644 --- a/chromium/net/quic/quic_ack_notifier_manager.h +++ b/chromium/net/quic/quic_ack_notifier_manager.h @@ -15,6 +15,10 @@ namespace net { class QuicAckNotifier; +namespace test { +class AckNotifierManagerPeer; +} + // The AckNotifierManager is used by the QuicSentPacketManager to keep track of // all the AckNotifiers currently active. It owns the AckNotifiers which it gets // from the serialized packets passed into OnSerializedPacket. It maintains both @@ -46,7 +50,13 @@ class NET_EXPORT_PRIVATE AckNotifierManager { // inform the AckNotifier of the sequence number which it should track. void OnSerializedPacket(const SerializedPacket& serialized_packet); + // This method is invoked when a packet is removed from the list of unacked + // packets, and it is no longer necessary to keep track of the notifier. + void OnPacketRemoved(QuicPacketSequenceNumber sequence_number); + private: + friend class test::AckNotifierManagerPeer; + typedef std::list<QuicAckNotifier*> AckNotifierList; // TODO(ianswett): Further improvement may come from changing this to a deque. typedef base::hash_map<QuicPacketSequenceNumber, AckNotifierList> diff --git a/chromium/net/quic/quic_ack_notifier_manager_test.cc b/chromium/net/quic/quic_ack_notifier_manager_test.cc new file mode 100644 index 00000000000..e791b91b9e1 --- /dev/null +++ b/chromium/net/quic/quic_ack_notifier_manager_test.cc @@ -0,0 +1,172 @@ +// Copyright (c) 2015 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_ack_notifier_manager.h" + +#include "net/quic/quic_ack_notifier.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/test_tools/quic_ack_notifier_manager_peer.h" +#include "net/quic/test_tools/quic_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { +namespace { + +// Test fixture for testing AckNotifierManager. Instantiates a manager and +// provides shared code for adding notifiers and verifying the contents of the +// manager. +class QuicAckNotifierManagerTest : public ::testing::Test { + protected: + AckNotifierManager manager_; + scoped_refptr<MockAckNotifierDelegate> delegate_; + QuicTime::Delta zero_; + + QuicAckNotifierManagerTest() : zero_(QuicTime::Delta::Zero()) { + delegate_ = new MockAckNotifierDelegate; + } + + ~QuicAckNotifierManagerTest() override {} + + size_t CountPackets() const { + return AckNotifierManagerPeer::GetNumberOfRegisteredPackets(&manager_); + } + + // Add a mock packet with specified parameters. The packet with given + // sequence number must not exist in the map before. + void AddPacket(QuicPacketSequenceNumber sequence_number, + bool retransmittable) { + // Create a mock packet. + RetransmittableFrames frames(ENCRYPTION_NONE); + SerializedPacket packet(sequence_number, PACKET_4BYTE_SEQUENCE_NUMBER, + /*packet=*/nullptr, + /*entropy_hash=*/0, + retransmittable ? &frames : nullptr); + + // Create and register a notifier. Normally, this would be created by + // QuicPacketGenerator. + QuicAckNotifier* notifier = new QuicAckNotifier(delegate_.get()); + packet.notifiers.push_back(notifier); + + // Ensure that exactly one packet is added. + const size_t old_packet_count = CountPackets(); + + // Actually add the packet. + manager_.OnSerializedPacket(packet); + + // Ensure the change in the number of packets. + EXPECT_EQ(old_packet_count + 1, CountPackets()); + } +}; + +// This test verifies that QuicAckNotifierManager can handle the trivial case of +// received packet notification. +TEST_F(QuicAckNotifierManagerTest, SimpleAck) { + AddPacket(1, false); + AddPacket(2, true); + + EXPECT_CALL(*delegate_, OnAckNotification(0, 0, zero_)).Times(2); + manager_.OnPacketAcked(1, zero_); + EXPECT_EQ(1u, CountPackets()); + manager_.OnPacketAcked(2, zero_); + EXPECT_EQ(0u, CountPackets()); + + manager_.OnPacketRemoved(1); + manager_.OnPacketRemoved(2); + EXPECT_EQ(0u, CountPackets()); +} + +// This test verifies that QuicAckNotifierManager can correctly handle the case +// when some of the packets are lost, which causes retransmission and removal +// from the unacked packet map. +TEST_F(QuicAckNotifierManagerTest, AckWithLosses) { + const size_t retransmitted_packet_size = kDefaultMaxPacketSize; + + // Here, we simulate the following scenario: + // 1. We send packets 1 to 5, where only odd-numbered packets are + // retransmittable. + // 2. The peer acks 1, 2 and 5, but not 3 and 4. + // 3. We retransmit 3 as 6. + // 4. We remove 1 and 2, since we no longer care about them. + // 4. The peer acks 6. + // 5. We remove packets 3 to 6. + + // Step 1: send five packets. + AddPacket(1, true); + AddPacket(2, false); + AddPacket(3, true); + AddPacket(4, false); + AddPacket(5, true); + + // Step 2: handle acks from peer. + EXPECT_CALL(*delegate_, OnAckNotification(0, 0, zero_)).Times(3); + manager_.OnPacketAcked(1, zero_); + EXPECT_EQ(4u, CountPackets()); + manager_.OnPacketAcked(2, zero_); + EXPECT_EQ(3u, CountPackets()); + manager_.OnPacketAcked(5, zero_); + EXPECT_EQ(2u, CountPackets()); + + // Step 3: retransmit 3 as 6. + manager_.OnPacketRetransmitted(3, 6, retransmitted_packet_size); + EXPECT_EQ(2u, CountPackets()); + + // Step 4: remove 1 and 2. + manager_.OnPacketRemoved(1); + manager_.OnPacketRemoved(2); + EXPECT_EQ(2u, CountPackets()); + + // Step 4: ack packet 6. + EXPECT_CALL(*delegate_, + OnAckNotification(1, retransmitted_packet_size, zero_)).Times(1); + manager_.OnPacketAcked(6, zero_); + EXPECT_EQ(1u, CountPackets()); + + // Step 5: remove all packets. This causes packet 4 to be dropped from the + // map. + manager_.OnPacketRemoved(3); + manager_.OnPacketRemoved(4); + manager_.OnPacketRemoved(5); + manager_.OnPacketRemoved(6); + EXPECT_EQ(0u, CountPackets()); +} + +// This test verifies that the QuicAckNotifierManager behaves correctly when +// there are many retransmissions. +TEST_F(QuicAckNotifierManagerTest, RepeatedRetransmission) { + AddPacket(1, true); + + const size_t packet_size = kDefaultMaxPacketSize; + const size_t times_lost = 100; + const size_t total_size_lost = packet_size * times_lost; + const QuicPacketSequenceNumber last_packet = times_lost + 1; + + // Retransmit the packet many times. + for (size_t sequence_number = 1; sequence_number < last_packet; + sequence_number++) { + manager_.OnPacketRetransmitted(sequence_number, sequence_number + 1, + packet_size); + EXPECT_EQ(1u, CountPackets()); + } + + // Remove all lost packets. + for (QuicPacketSequenceNumber packet = 1; packet < last_packet; packet++) { + manager_.OnPacketRemoved(packet); + } + EXPECT_EQ(1u, CountPackets()); + + // Finally get the packet acknowledged. + EXPECT_CALL(*delegate_, OnAckNotification(times_lost, total_size_lost, zero_)) + .Times(1); + manager_.OnPacketAcked(last_packet, zero_); + EXPECT_EQ(0u, CountPackets()); + + // Remove the last packet. + manager_.OnPacketRemoved(last_packet); + EXPECT_EQ(0u, CountPackets()); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_bandwidth.cc b/chromium/net/quic/quic_bandwidth.cc index b42a1f37e2e..911eb6c83cf 100644 --- a/chromium/net/quic/quic_bandwidth.cc +++ b/chromium/net/quic/quic_bandwidth.cc @@ -4,6 +4,8 @@ #include "net/quic/quic_bandwidth.h" +#include <stdint.h> + #include "base/logging.h" #include "net/quic/quic_time.h" #include "net/quic/quic_types.h" @@ -11,7 +13,7 @@ namespace net { // Highest number that QuicBandwidth can hold. -const int64 kQuicInfiniteBandwidth = GG_INT64_C(0x7fffffffffffffff); +const int64 kQuicInfiniteBandwidth = INT64_C(0x7fffffffffffffff); // static QuicBandwidth QuicBandwidth::Zero() { diff --git a/chromium/net/quic/quic_client_session.cc b/chromium/net/quic/quic_client_session.cc index 9721e719961..e45a4b8d3a9 100644 --- a/chromium/net/quic/quic_client_session.cc +++ b/chromium/net/quic/quic_client_session.cc @@ -5,11 +5,13 @@ #include "net/quic/quic_client_session.h" #include "base/callback_helpers.h" -#include "base/message_loop/message_loop.h" -#include "base/metrics/histogram.h" +#include "base/location.h" +#include "base/metrics/histogram_macros.h" #include "base/metrics/sparse_histogram.h" +#include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" +#include "base/thread_task_runner_handle.h" #include "base/values.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" @@ -96,12 +98,12 @@ void RecordHandshakeState(HandshakeState state) { NUM_HANDSHAKE_STATES); } -base::Value* NetLogQuicClientSessionCallback( +scoped_ptr<base::Value> NetLogQuicClientSessionCallback( const QuicServerId* server_id, int cert_verify_flags, bool require_confirmation, NetLogCaptureMode /* capture_mode */) { - base::DictionaryValue* dict = new base::DictionaryValue(); + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); dict->SetString("host", server_id->host()); dict->SetInteger("port", server_id->port()); dict->SetBoolean("is_https", server_id->is_https()); @@ -109,7 +111,7 @@ base::Value* NetLogQuicClientSessionCallback( server_id->privacy_mode() == PRIVACY_MODE_ENABLED); dict->SetBoolean("require_confirmation", require_confirmation); dict->SetInteger("cert_verify_flags", cert_verify_flags); - return dict; + return dict.Pass(); } } // namespace @@ -157,21 +159,24 @@ QuicClientSession::QuicClientSession( QuicConnection* connection, scoped_ptr<DatagramClientSocket> socket, QuicStreamFactory* stream_factory, + QuicCryptoClientStreamFactory* crypto_client_stream_factory, TransportSecurityState* transport_security_state, scoped_ptr<QuicServerInfo> server_info, + const QuicServerId& server_id, int cert_verify_flags, const QuicConfig& config, + QuicCryptoClientConfig* crypto_config, const char* const connection_description, base::TimeTicks dns_resolution_end_time, base::TaskRunner* task_runner, NetLog* net_log) : QuicClientSessionBase(connection, config), + server_id_(server_id), require_confirmation_(false), stream_factory_(stream_factory), socket_(socket.Pass()), transport_security_state_(transport_security_state), server_info_(server_info.Pass()), - cert_verify_flags_(cert_verify_flags), num_total_streams_(0), task_runner_(task_runner), net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_QUIC_SESSION)), @@ -179,49 +184,40 @@ QuicClientSession::QuicClientSession( dns_resolution_end_time_(dns_resolution_end_time), logger_(new QuicConnectionLogger(this, connection_description, net_log_)), going_away_(false), + disabled_reason_(QUIC_DISABLED_NOT), weak_factory_(this) { - connection->set_debug_visitor(logger_.get()); - IPEndPoint address; - if (socket && socket->GetLocalAddress(&address) == OK && - address.GetFamily() == ADDRESS_FAMILY_IPV6) { - connection->set_max_packet_length( - connection->max_packet_length() - kAdditionalOverheadForIPv6); - } -} - -void QuicClientSession::InitializeSession( - const QuicServerId& server_id, - QuicCryptoClientConfig* crypto_config, - QuicCryptoClientStreamFactory* crypto_client_stream_factory) { - server_id_ = server_id; crypto_stream_.reset( crypto_client_stream_factory ? crypto_client_stream_factory->CreateQuicCryptoClientStream( server_id, this, crypto_config) : new QuicCryptoClientStream( server_id, this, - new ProofVerifyContextChromium(cert_verify_flags_, net_log_), + new ProofVerifyContextChromium(cert_verify_flags, net_log_), crypto_config)); - QuicClientSessionBase::InitializeSession(); - // TODO(rch): pass in full host port proxy pair + connection->set_debug_visitor(logger_.get()); net_log_.BeginEvent(NetLog::TYPE_QUIC_SESSION, base::Bind(NetLogQuicClientSessionCallback, &server_id, - cert_verify_flags_, require_confirmation_)); + cert_verify_flags, require_confirmation_)); + IPEndPoint address; + if (socket && socket->GetLocalAddress(&address) == OK && + address.GetFamily() == ADDRESS_FAMILY_IPV6) { + connection->set_max_packet_length(connection->max_packet_length() - + kAdditionalOverheadForIPv6); + } } QuicClientSession::~QuicClientSession() { - if (!streams()->empty()) + if (!dynamic_streams().empty()) RecordUnexpectedOpenStreams(DESTRUCTOR); if (!observers_.empty()) RecordUnexpectedObservers(DESTRUCTOR); if (!going_away_) RecordUnexpectedNotGoingAway(DESTRUCTOR); - while (!streams()->empty() || - !observers_.empty() || + while (!dynamic_streams().empty() || !observers_.empty() || !stream_requests_.empty()) { // The session must be closed before it is destroyed. - DCHECK(streams()->empty()); + DCHECK(dynamic_streams().empty()); CloseAllStreams(ERR_UNEXPECTED); DCHECK(observers_.empty()); CloseAllObservers(ERR_UNEXPECTED); @@ -311,6 +307,17 @@ QuicClientSession::~QuicClientSession() { } } + // The MTU used by QUIC is limited to a fairly small set of predefined values + // (initial values and MTU discovery values), but does not fare well when + // bucketed. Because of that, a sparse histogram is used here. + UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.ClientSideMtu", + connection()->max_packet_length()); + UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.ServerSideMtu", + stats.max_received_packet_size); + + UMA_HISTOGRAM_COUNTS("Net.QuicSession.MtuProbesSent", + connection()->mtu_probe_count()); + if (stats.max_sequence_reordering == 0) return; const base::HistogramBase::Sample kMaxReordering = 100; @@ -347,7 +354,7 @@ void QuicClientSession::OnStreamFrames( it->second); } - return QuicSession::OnStreamFrames(frames); + return QuicSpdySession::OnStreamFrames(frames); } void QuicClientSession::AddObserver(Observer* observer) { @@ -407,7 +414,7 @@ void QuicClientSession::CancelRequest(StreamRequest* request) { } } -QuicReliableClientStream* QuicClientSession::CreateOutgoingDataStream() { +QuicReliableClientStream* QuicClientSession::CreateOutgoingDynamicStream() { if (!crypto_stream_->encryption_established()) { DVLOG(1) << "Encryption not active so no outgoing stream created."; return nullptr; @@ -559,7 +566,7 @@ bool QuicClientSession::CanPool(const std::string& hostname, server_id_.host(), hostname); } -QuicDataStream* QuicClientSession::CreateIncomingDataStream( +QuicDataStream* QuicClientSession::CreateIncomingDynamicStream( QuicStreamId id) { DLOG(ERROR) << "Server push not supported"; return nullptr; @@ -572,14 +579,14 @@ void QuicClientSession::CloseStream(QuicStreamId stream_id) { stream_id, stream->num_frames_received(), stream->num_duplicate_frames_received()); } - QuicSession::CloseStream(stream_id); + QuicSpdySession::CloseStream(stream_id); OnClosedStream(); } void QuicClientSession::SendRstStream(QuicStreamId id, QuicRstStreamErrorCode error, QuicStreamOffset bytes_written) { - QuicSession::SendRstStream(id, error, bytes_written); + QuicSpdySession::SendRstStream(id, error, bytes_written); OnClosedStream(); } @@ -650,7 +657,7 @@ void QuicClientSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { if (server_info_) server_info_->OnExternalCacheHit(); } - QuicSession::OnCryptoHandshakeEvent(event); + QuicSpdySession::OnCryptoHandshakeEvent(event); } void QuicClientSession::OnCryptoHandshakeMessageSent( @@ -681,6 +688,7 @@ void QuicClientSession::OnConnectionClosed(QuicErrorCode error, GetNumOpenStreams()); if (IsCryptoHandshakeConfirmed()) { if (GetNumOpenStreams() > 0) { + disabled_reason_ = QUIC_DISABLED_TIMEOUT_WITH_OPEN_STREAMS; UMA_HISTOGRAM_BOOLEAN( "Net.QuicSession.TimedOutWithOpenStreams.HasUnackedPackets", connection()->sent_packet_manager().HasUnackedPackets()); @@ -727,6 +735,8 @@ void QuicClientSession::OnConnectionClosed(QuicErrorCode error, "Net.QuicSession.ConnectionClose.HandshakeFailureUnknown.QuicError", error); } + } else if (error == QUIC_PUBLIC_RESET) { + disabled_reason_ = QUIC_DISABLED_PUBLIC_RESET_POST_HANDSHAKE; } UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.QuicVersion", @@ -737,7 +747,7 @@ void QuicClientSession::OnConnectionClosed(QuicErrorCode error, } socket_->Close(); QuicSession::OnConnectionClosed(error, from_peer); - DCHECK(streams()->empty()); + DCHECK(dynamic_streams().empty()); CloseAllStreams(ERR_UNEXPECTED); CloseAllObservers(ERR_UNEXPECTED); NotifyFactoryOfSessionClosedLater(); @@ -746,7 +756,7 @@ void QuicClientSession::OnConnectionClosed(QuicErrorCode error, void QuicClientSession::OnSuccessfulVersionNegotiation( const QuicVersion& version) { logger_->OnSuccessfulVersionNegotiation(version); - QuicSession::OnSuccessfulVersionNegotiation(version); + QuicSpdySession::OnSuccessfulVersionNegotiation(version); } void QuicClientSession::OnProofValid( @@ -818,8 +828,8 @@ void QuicClientSession::CloseSessionOnErrorInner(int net_error, } void QuicClientSession::CloseAllStreams(int net_error) { - while (!streams()->empty()) { - ReliableQuicStream* stream = streams()->begin()->second; + while (!dynamic_streams().empty()) { + ReliableQuicStream* stream = dynamic_streams().begin()->second; QuicStreamId id = stream->id(); static_cast<QuicReliableClientStream*>(stream)->OnError(net_error); CloseStream(id); @@ -834,20 +844,19 @@ void QuicClientSession::CloseAllObservers(int net_error) { } } -base::Value* QuicClientSession::GetInfoAsValue( +scoped_ptr<base::Value> QuicClientSession::GetInfoAsValue( const std::set<HostPortPair>& aliases) { - base::DictionaryValue* dict = new base::DictionaryValue(); + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); 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()))); + scoped_ptr<base::ListValue> stream_list(new base::ListValue()); + for (base::hash_map<QuicStreamId, ReliableQuicStream*>::const_iterator it = + dynamic_streams().begin(); + it != dynamic_streams().end(); ++it) { + stream_list->Append( + new base::StringValue(base::UintToString(it->second->id()))); } - dict->Set("active_streams", stream_list); + dict->Set("active_streams", stream_list.Pass()); dict->SetInteger("total_streams", num_total_streams_); dict->SetString("peer_address", peer_address().ToString()); @@ -860,14 +869,14 @@ base::Value* QuicClientSession::GetInfoAsValue( SSLInfo ssl_info; dict->SetBoolean("secure", GetSSLInfo(&ssl_info) && ssl_info.cert.get()); - base::ListValue* alias_list = new base::ListValue(); + scoped_ptr<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); + dict->Set("aliases", alias_list.Pass()); - return dict; + return dict.Pass(); } base::WeakPtr<QuicClientSession> QuicClientSession::GetWeakPtr() { @@ -900,7 +909,7 @@ void QuicClientSession::NotifyFactoryOfSessionGoingAway() { } void QuicClientSession::NotifyFactoryOfSessionClosedLater() { - if (!streams()->empty()) + if (!dynamic_streams().empty()) RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER); if (!going_away_) @@ -909,14 +918,13 @@ void QuicClientSession::NotifyFactoryOfSessionClosedLater() { going_away_ = true; DCHECK_EQ(0u, GetNumOpenStreams()); DCHECK(!connection()->connected()); - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&QuicClientSession::NotifyFactoryOfSessionClosed, - weak_factory_.GetWeakPtr())); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&QuicClientSession::NotifyFactoryOfSessionClosed, + weak_factory_.GetWeakPtr())); } void QuicClientSession::NotifyFactoryOfSessionClosed() { - if (!streams()->empty()) + if (!dynamic_streams().empty()) RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED); if (!going_away_) diff --git a/chromium/net/quic/quic_client_session.h b/chromium/net/quic/quic_client_session.h index db13de99830..ae55a18ede4 100644 --- a/chromium/net/quic/quic_client_session.h +++ b/chromium/net/quic/quic_client_session.h @@ -44,6 +44,18 @@ class QuicClientSessionPeer; class NET_EXPORT_PRIVATE QuicClientSession : public QuicClientSessionBase, public QuicPacketReader::Visitor { public: + // Reasons to disable QUIC, that is under certain pathological + // connection errors. Note: these values must be kept in sync with + // the corresponding values of QuicDisabledReason in: + // tools/metrics/histograms/histograms.xml + enum QuicDisabledReason { + QUIC_DISABLED_NOT = 0, // default, not disabled + QUIC_DISABLED_PUBLIC_RESET_POST_HANDSHAKE = 1, + QUIC_DISABLED_TIMEOUT_WITH_OPEN_STREAMS = 2, + QUIC_DISABLED_BAD_PACKET_LOSS_RATE = 3, + QUIC_DISABLED_MAX = 4, + }; + // An interface for observing events on a session. class NET_EXPORT_PRIVATE Observer { public: @@ -95,22 +107,19 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicClientSessionBase, QuicClientSession(QuicConnection* connection, scoped_ptr<DatagramClientSocket> socket, QuicStreamFactory* stream_factory, + QuicCryptoClientStreamFactory* crypto_client_stream_factory, TransportSecurityState* transport_security_state, scoped_ptr<QuicServerInfo> server_info, + const QuicServerId& server_id, int cert_verify_flags, const QuicConfig& config, + QuicCryptoClientConfig* crypto_config, const char* const connection_description, base::TimeTicks dns_resolution_end_time, base::TaskRunner* task_runner, NetLog* net_log); ~QuicClientSession() override; - // Initialize session's connection to |server_id|. - void InitializeSession( - const QuicServerId& server_id, - QuicCryptoClientConfig* config, - QuicCryptoClientStreamFactory* crypto_client_stream_factory); - void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); @@ -129,7 +138,7 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicClientSessionBase, // QuicSession methods: void OnStreamFrames(const std::vector<QuicStreamFrame>& frames) override; - QuicReliableClientStream* CreateOutgoingDataStream() override; + QuicReliableClientStream* CreateOutgoingDynamicStream() override; QuicCryptoClientStream* GetCryptoStream() override; void CloseStream(QuicStreamId stream_id) override; void SendRstStream(QuicStreamId id, @@ -179,7 +188,7 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicClientSessionBase, void CloseSessionOnErrorAndNotifyFactoryLater(int error, QuicErrorCode quic_error); - base::Value* GetInfoAsValue(const std::set<HostPortPair>& aliases); + scoped_ptr<base::Value> GetInfoAsValue(const std::set<HostPortPair>& aliases); const BoundNetLog& net_log() const { return net_log_; } @@ -197,9 +206,11 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicClientSessionBase, const QuicServerId& server_id() const { return server_id_; } + QuicDisabledReason disabled_reason() const { return disabled_reason_; } + protected: // QuicSession methods: - QuicDataStream* CreateIncomingDataStream(QuicStreamId id) override; + QuicDataStream* CreateIncomingDynamicStream(QuicStreamId id) override; private: friend class test::QuicClientSessionPeer; @@ -250,7 +261,6 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicClientSessionBase, TransportSecurityState* transport_security_state_; scoped_ptr<QuicServerInfo> server_info_; scoped_ptr<CertVerifyResult> cert_verify_result_; - int cert_verify_flags_; std::string pinning_failure_log_; ObserverSet observers_; StreamRequestQueue stream_requests_; @@ -265,6 +275,7 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicClientSessionBase, // 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_; + QuicDisabledReason disabled_reason_; 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 index 40d4b86dc2c..12173e51019 100644 --- a/chromium/net/quic/quic_client_session_base.cc +++ b/chromium/net/quic/quic_client_session_base.cc @@ -8,10 +8,10 @@ namespace net { -QuicClientSessionBase::QuicClientSessionBase( - QuicConnection* connection, - const QuicConfig& config) - : QuicSession(connection, config) {} +QuicClientSessionBase::QuicClientSessionBase(QuicConnection* connection, + const QuicConfig& config) + : QuicSpdySession(connection, config) { +} QuicClientSessionBase::~QuicClientSessionBase() {} @@ -27,7 +27,7 @@ void QuicClientSessionBase::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { } // kFHDR config maps to FEC protection always for headers stream. // TODO(jri): Add crypto stream in addition to headers for kHDR. - headers_stream_->set_fec_policy(FEC_PROTECT_ALWAYS); + headers_stream()->set_fec_policy(FEC_PROTECT_ALWAYS); } } // namespace net diff --git a/chromium/net/quic/quic_client_session_base.h b/chromium/net/quic/quic_client_session_base.h index d72996c4118..0e3c0fa3ff3 100644 --- a/chromium/net/quic/quic_client_session_base.h +++ b/chromium/net/quic/quic_client_session_base.h @@ -6,12 +6,12 @@ #define NET_QUIC_QUIC_CLIENT_SESSION_BASE_H_ #include "net/quic/quic_crypto_client_stream.h" -#include "net/quic/quic_session.h" +#include "net/quic/quic_spdy_session.h" namespace net { // Base class for all client-specific QuicSession subclasses. -class NET_EXPORT_PRIVATE QuicClientSessionBase : public QuicSession { +class NET_EXPORT_PRIVATE QuicClientSessionBase : public QuicSpdySession { public: QuicClientSessionBase(QuicConnection* connection, const QuicConfig& config); diff --git a/chromium/net/quic/quic_client_session_test.cc b/chromium/net/quic/quic_client_session_test.cc index 1d9f0218a77..921e7a6dbcc 100644 --- a/chromium/net/quic/quic_client_session_test.cc +++ b/chromium/net/quic/quic_client_session_test.cc @@ -9,6 +9,7 @@ #include "base/base64.h" #include "base/files/file_path.h" #include "base/rand_util.h" +#include "base/thread_task_runner_handle.h" #include "net/base/test_completion_callback.h" #include "net/base/test_data_directory.h" #include "net/cert/cert_verify_result.h" @@ -22,6 +23,7 @@ #include "net/quic/crypto/quic_server_info.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_spdy_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" @@ -45,19 +47,22 @@ class QuicClientSessionTest : public ::testing::TestWithParam<QuicVersion> { SupportedVersions(GetParam()))), session_(connection_, GetSocket().Pass(), - nullptr, + /*stream_factory=*/nullptr, + /*crypto_client_stream_factory=*/nullptr, &transport_security_state_, make_scoped_ptr((QuicServerInfo*)nullptr), + QuicServerId(kServerHostname, + kServerPort, + /*is_secure=*/false, + PRIVACY_MODE_DISABLED), /*cert_verify_flags=*/0, DefaultQuicConfig(), + &crypto_config_, "CONNECTION_UNKNOWN", base::TimeTicks::Now(), - base::MessageLoop::current()->message_loop_proxy().get(), + base::ThreadTaskRunnerHandle::Get().get(), &net_log_) { - session_.InitializeSession(QuicServerId(kServerHostname, kServerPort, - /*is_secure=*/false, - PRIVACY_MODE_DISABLED), - &crypto_config_, nullptr); + session_.Initialize(); // Advance the time, because timers do not like uninitialized times. connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); } @@ -106,15 +111,15 @@ TEST_P(QuicClientSessionTest, MaxNumStreams) { std::vector<QuicReliableClientStream*> streams; for (size_t i = 0; i < kDefaultMaxStreamsPerConnection; i++) { - QuicReliableClientStream* stream = session_.CreateOutgoingDataStream(); + QuicReliableClientStream* stream = session_.CreateOutgoingDynamicStream(); EXPECT_TRUE(stream); streams.push_back(stream); } - EXPECT_FALSE(session_.CreateOutgoingDataStream()); + EXPECT_FALSE(session_.CreateOutgoingDynamicStream()); // Close a stream and ensure I can now open a new one. session_.CloseStream(streams[0]->id()); - EXPECT_TRUE(session_.CreateOutgoingDataStream()); + EXPECT_TRUE(session_.CreateOutgoingDynamicStream()); } TEST_P(QuicClientSessionTest, MaxNumStreamsViaRequest) { @@ -122,7 +127,7 @@ TEST_P(QuicClientSessionTest, MaxNumStreamsViaRequest) { std::vector<QuicReliableClientStream*> streams; for (size_t i = 0; i < kDefaultMaxStreamsPerConnection; i++) { - QuicReliableClientStream* stream = session_.CreateOutgoingDataStream(); + QuicReliableClientStream* stream = session_.CreateOutgoingDynamicStream(); EXPECT_TRUE(stream); streams.push_back(stream); } @@ -147,7 +152,7 @@ TEST_P(QuicClientSessionTest, GoAwayReceived) { // After receiving a GoAway, I should no longer be able to create outgoing // streams. session_.OnGoAway(QuicGoAwayFrame(QUIC_PEER_GOING_AWAY, 1u, "Going away.")); - EXPECT_EQ(nullptr, session_.CreateOutgoingDataStream()); + EXPECT_EQ(nullptr, session_.CreateOutgoingDynamicStream()); } TEST_P(QuicClientSessionTest, CanPool) { diff --git a/chromium/net/quic/quic_config.cc b/chromium/net/quic/quic_config.cc index 30db003ba4d..675716c2ca5 100644 --- a/chromium/net/quic/quic_config.cc +++ b/chromium/net/quic/quic_config.cc @@ -45,7 +45,6 @@ QuicErrorCode ReadUint32(const CryptoHandshakeMessage& msg, return error; } - QuicConfigValue::QuicConfigValue(QuicTag tag, QuicConfigPresence presence) : tag_(tag), @@ -388,6 +387,20 @@ QuicTagVector QuicConfig::SendConnectionOptions() const { return connection_options_.GetSendValues(); } +bool QuicConfig::HasClientSentConnectionOption(QuicTag tag, + Perspective perspective) const { + if (perspective == Perspective::IS_SERVER) { + if (HasReceivedConnectionOptions() && + ContainsQuicTag(ReceivedConnectionOptions(), tag)) { + return true; + } + } else if (HasSendConnectionOptions() && + ContainsQuicTag(SendConnectionOptions(), tag)) { + return true; + } + return false; +} + void QuicConfig::SetIdleConnectionStateLifetime( QuicTime::Delta max_idle_connection_state_lifetime, QuicTime::Delta default_idle_conection_state_lifetime) { diff --git a/chromium/net/quic/quic_config.h b/chromium/net/quic/quic_config.h index 34ea5930c8b..20d71c60a26 100644 --- a/chromium/net/quic/quic_config.h +++ b/chromium/net/quic/quic_config.h @@ -229,6 +229,11 @@ class NET_EXPORT_PRIVATE QuicConfig { QuicTagVector SendConnectionOptions() const; + // Returns true if the client is sending or the server has received a + // connection option. + bool HasClientSentConnectionOption(QuicTag tag, + Perspective perspective) const; + void SetIdleConnectionStateLifetime( QuicTime::Delta max_idle_connection_state_lifetime, QuicTime::Delta default_idle_conection_state_lifetime); diff --git a/chromium/net/quic/quic_config_test.cc b/chromium/net/quic/quic_config_test.cc index 4cd3820a7db..dcf19a58057 100644 --- a/chromium/net/quic/quic_config_test.cc +++ b/chromium/net/quic/quic_config_test.cc @@ -217,6 +217,34 @@ TEST_F(QuicConfigTest, InvalidFlowControlWindow) { config.GetInitialStreamFlowControlWindowToSend()); } +TEST_F(QuicConfigTest, HasClientSentConnectionOption) { + QuicConfig client_config; + QuicTagVector copt; + copt.push_back(kTBBR); + copt.push_back(kFHDR); + client_config.SetConnectionOptionsToSend(copt); + EXPECT_TRUE(client_config.HasClientSentConnectionOption( + kTBBR, Perspective::IS_CLIENT)); + EXPECT_TRUE(client_config.HasClientSentConnectionOption( + kFHDR, Perspective::IS_CLIENT)); + + CryptoHandshakeMessage msg; + client_config.ToHandshakeMessage(&msg); + + string error_details; + const QuicErrorCode error = + config_.ProcessPeerHello(msg, CLIENT, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_TRUE(config_.negotiated()); + + EXPECT_TRUE(config_.HasReceivedConnectionOptions()); + EXPECT_EQ(2u, config_.ReceivedConnectionOptions().size()); + EXPECT_TRUE( + config_.HasClientSentConnectionOption(kTBBR, Perspective::IS_SERVER)); + EXPECT_TRUE( + config_.HasClientSentConnectionOption(kFHDR, Perspective::IS_SERVER)); +} + } // namespace } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_connection.cc b/chromium/net/quic/quic_connection.cc index 73f43127873..3db39c3aef8 100644 --- a/chromium/net/quic/quic_connection.cc +++ b/chromium/net/quic/quic_connection.cc @@ -17,13 +17,14 @@ #include "base/debug/stack_trace.h" #include "base/format_macros.h" #include "base/logging.h" +#include "base/memory/ref_counted.h" #include "base/profiler/scoped_tracker.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "net/base/net_errors.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/iovector.h" #include "net/quic/proto/cached_network_parameters.pb.h" #include "net/quic/quic_bandwidth.h" #include "net/quic/quic_config.h" @@ -63,9 +64,6 @@ const size_t kMaxFecGroups = 2; // Maximum number of acks received before sending an ack in response. const QuicPacketCount kMaxPacketsReceivedBeforeAckSend = 20; -// Maximum number of tracked packets. -const QuicPacketCount kMaxTrackedPackets = 15 * kMaxTcpCongestionWindow; - bool Near(QuicPacketSequenceNumber a, QuicPacketSequenceNumber b) { QuicPacketSequenceNumber delta = (a > b) ? a - b : b - a; return delta <= kMaxPacketGap; @@ -164,6 +162,23 @@ class PingAlarm : public QuicAlarm::Delegate { DISALLOW_COPY_AND_ASSIGN(PingAlarm); }; +class MtuDiscoveryAlarm : public QuicAlarm::Delegate { + public: + explicit MtuDiscoveryAlarm(QuicConnection* connection) + : connection_(connection) {} + + QuicTime OnAlarm() override { + connection_->DiscoverMtu(); + // DiscoverMtu() handles rescheduling the alarm by itself. + return QuicTime::Zero(); + } + + private: + QuicConnection* connection_; + + DISALLOW_COPY_AND_ASSIGN(MtuDiscoveryAlarm); +}; + // This alarm may be scheduled when an FEC protected packet is sent out. class FecAlarm : public QuicAlarm::Delegate { public: @@ -181,6 +196,33 @@ class FecAlarm : public QuicAlarm::Delegate { DISALLOW_COPY_AND_ASSIGN(FecAlarm); }; +// Listens for acks of MTU discovery packets and raises the maximum packet size +// of the connection if the probe succeeds. +class MtuDiscoveryAckListener : public QuicAckNotifier::DelegateInterface { + public: + MtuDiscoveryAckListener(QuicConnection* connection, QuicByteCount probe_size) + : connection_(connection), probe_size_(probe_size) {} + + void OnAckNotification(int /*num_retransmittable_packets*/, + int /*num_retransmittable_bytes*/, + QuicTime::Delta /*delta_largest_observed*/) override { + // Since the probe was successful, increase the maximum packet size to that. + if (probe_size_ > connection_->max_packet_length()) { + connection_->set_max_packet_length(probe_size_); + } + } + + protected: + // MtuDiscoveryAckListener is ref counted. + ~MtuDiscoveryAckListener() override {} + + private: + QuicConnection* connection_; + QuicByteCount probe_size_; + + DISALLOW_COPY_AND_ASSIGN(MtuDiscoveryAckListener); +}; + } // namespace QuicConnection::QueuedPacket::QueuedPacket(SerializedPacket packet, @@ -240,12 +282,15 @@ QuicConnection::QuicConnection(QuicConnectionId connection_id, ack_queued_(false), num_packets_received_since_last_ack_sent_(0), stop_waiting_count_(0), + delay_setting_retransmission_alarm_(false), + pending_retransmission_alarm_(false), 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))), + mtu_discovery_alarm_(helper->CreateAlarm(new MtuDiscoveryAlarm(this))), visitor_(nullptr), debug_visitor_(nullptr), packet_generator_(connection_id_, &framer_, random_generator_, this), @@ -270,7 +315,12 @@ QuicConnection::QuicConnection(QuicConnectionId connection_id, self_ip_changed_(false), self_port_changed_(false), can_truncate_connection_ids_(true), - is_secure_(is_secure) { + is_secure_(is_secure), + mtu_discovery_target_(0), + mtu_probe_count_(0), + packets_between_mtu_probes_(kPacketsBetweenMtuProbesBase), + next_mtu_probe_at_(kPacketsBetweenMtuProbesBase), + largest_received_packet_size_(0) { DVLOG(1) << ENDPOINT << "Created connection with connection_id: " << connection_id; framer_.set_visitor(this); @@ -314,6 +364,18 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { config.ReceivedBytesForConnectionId()); } max_undecryptable_packets_ = config.max_undecryptable_packets(); + + if (FLAGS_quic_send_fec_packet_only_on_fec_alarm && + config.HasClientSentConnectionOption(kFSPA, perspective_)) { + packet_generator_.set_fec_send_policy(FecSendPolicy::FEC_ALARM_TRIGGER); + } + + if (config.HasClientSentConnectionOption(kMTUH, perspective_)) { + mtu_discovery_target_ = kMtuDiscoveryTargetPacketSizeHigh; + } + if (config.HasClientSentConnectionOption(kMTUL, perspective_)) { + mtu_discovery_target_ = kMtuDiscoveryTargetPacketSizeLow; + } } void QuicConnection::OnSendConnectionState( @@ -671,15 +733,10 @@ void QuicConnection::ProcessAckFrame(const QuicAckFrame& incoming_ack) { time_of_last_received_packet_); sent_entropy_manager_.ClearEntropyBefore( sent_packet_manager_.least_packet_awaited_by_peer() - 1); - if (sent_packet_manager_.HasPendingRetransmissions()) { - WriteIfNotBlocked(); - } // 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. - QuicTime retransmission_time = sent_packet_manager_.GetRetransmissionTime(); - retransmission_alarm_->Update(retransmission_time, - QuicTime::Delta::FromMilliseconds(1)); + SetRetransmissionAlarm(); } void QuicConnection::ProcessStopWaitingFrame( @@ -876,16 +933,16 @@ void QuicConnection::OnPacketComplete() { } DVLOG(1) << ENDPOINT << (last_packet_revived_ ? "Revived" : "Got") - << " packet " << last_header_.packet_sequence_number - << " with " << last_stream_frames_.size()<< " stream frames " - << last_ack_frames_.size() << " acks, " - << last_stop_waiting_frames_.size() << " stop_waiting, " - << last_rst_frames_.size() << " rsts, " - << last_goaway_frames_.size() << " goaways, " - << last_window_update_frames_.size() << " window updates, " - << last_blocked_frames_.size() << " blocked, " - << last_ping_frames_.size() << " pings, " - << last_close_frames_.size() << " closes, " + << " packet " << last_header_.packet_sequence_number << " with " // + << last_stream_frames_.size() << " stream frames, " // + << last_ack_frames_.size() << " acks, " // + << last_stop_waiting_frames_.size() << " stop_waiting, " // + << last_rst_frames_.size() << " rsts, " // + << last_goaway_frames_.size() << " goaways, " // + << last_window_update_frames_.size() << " window updates, " // + << last_blocked_frames_.size() << " blocked, " // + << last_ping_frames_.size() << " pings, " // + << last_close_frames_.size() << " closes " // << "for " << last_header_.public_header.connection_id; ++num_packets_received_since_last_ack_sent_; @@ -907,63 +964,61 @@ void QuicConnection::OnPacketComplete() { if (!last_stream_frames_.empty()) { visitor_->OnStreamFrames(last_stream_frames_); - if (!connected_ && !FLAGS_quic_stop_early) { + if (!connected_) { return; } } - for (size_t i = 0; i < last_stream_frames_.size(); ++i) { - stats_.stream_bytes_received += - last_stream_frames_[i].data.TotalBufferSize(); + for (const QuicStreamFrame& stream_frame : last_stream_frames_) { + stats_.stream_bytes_received += stream_frame.data.size(); } // Process window updates, blocked, stream resets, acks, then congestion // feedback. if (!last_window_update_frames_.empty()) { visitor_->OnWindowUpdateFrames(last_window_update_frames_); - if (!connected_ && !FLAGS_quic_stop_early) { + if (!connected_) { return; } } if (!last_blocked_frames_.empty()) { visitor_->OnBlockedFrames(last_blocked_frames_); - if (!connected_ && !FLAGS_quic_stop_early) { + if (!connected_) { return; } } for (size_t i = 0; i < last_goaway_frames_.size(); ++i) { visitor_->OnGoAway(last_goaway_frames_[i]); - if (!connected_ && !FLAGS_quic_stop_early) { + if (!connected_) { return; } } for (size_t i = 0; i < last_rst_frames_.size(); ++i) { visitor_->OnRstStream(last_rst_frames_[i]); - if (!connected_ && !FLAGS_quic_stop_early) { + if (!connected_) { return; } } for (size_t i = 0; i < last_ack_frames_.size(); ++i) { ProcessAckFrame(last_ack_frames_[i]); - if (!connected_ && !FLAGS_quic_stop_early) { + if (!connected_) { return; } } for (size_t i = 0; i < last_stop_waiting_frames_.size(); ++i) { ProcessStopWaitingFrame(last_stop_waiting_frames_[i]); - if (!connected_ && !FLAGS_quic_stop_early) { + if (!connected_) { return; } } if (!last_close_frames_.empty()) { CloseConnection(last_close_frames_[0].error_code, true); DCHECK(!connected_); - if (!FLAGS_quic_stop_early) { - return; - } + return; } // If there are new missing packets to report, send an ack immediately. - if (received_packet_manager_.HasNewMissingPackets()) { + if ((!FLAGS_quic_dont_ack_acks || ShouldLastPacketInstigateAck()) && + received_packet_manager_.HasNewMissingPackets()) { ack_queued_ = true; ack_alarm_->Cancel(); } @@ -974,11 +1029,15 @@ void QuicConnection::OnPacketComplete() { } void QuicConnection::MaybeQueueAck() { + // If the last packet is an ack, don't ack it. + if (!ShouldLastPacketInstigateAck()) { + return; + } // 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_queued_) { if (ack_alarm_->IsSet()) { ack_queued_ = true; } else { @@ -1084,9 +1143,7 @@ void QuicConnection::MaybeSendInResponseToPacket() { // 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(); - } + WriteIfNotBlocked(); } void QuicConnection::SendVersionNegotiationPacket() { @@ -1123,12 +1180,12 @@ void QuicConnection::SendVersionNegotiationPacket() { QuicConsumedData QuicConnection::SendStreamData( QuicStreamId id, - const IOVector& data, + const QuicIOVector& iov, QuicStreamOffset offset, bool fin, FecProtection fec_protection, QuicAckNotifier::DelegateInterface* delegate) { - if (!fin && data.Empty()) { + if (!fin && iov.total_length == 0) { LOG(DFATAL) << "Attempt to send empty stream frame"; return QuicConsumedData(0, false); } @@ -1146,8 +1203,9 @@ QuicConsumedData QuicConnection::SendStreamData( // 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. + ScopedRetransmissionScheduler alarm_delayer(this); ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK); - return packet_generator_.ConsumeData(id, data, offset, fin, fec_protection, + return packet_generator_.ConsumeData(id, iov, offset, fin, fec_protection, delegate); } @@ -1215,7 +1273,8 @@ const QuicConnectionStats& QuicConnection::GetStats() { stats_.srtt_us = srtt.ToMicroseconds(); stats_.estimated_bandwidth = sent_packet_manager_.BandwidthEstimate(); - stats_.max_packet_size = packet_generator_.max_packet_length(); + stats_.max_packet_size = packet_generator_.GetMaxPacketLength(); + stats_.max_received_packet_size = largest_received_packet_size_; return stats_; } @@ -1239,6 +1298,7 @@ void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address, stats_.bytes_received += packet.length(); ++stats_.packets_received; + ScopedRetransmissionScheduler alarm_delayer(this); if (!framer_.ProcessPacket(packet)) { // If we are unable to decrypt this packet, it might be // because the CHLO or SHLO packet was lost. @@ -1347,9 +1407,13 @@ bool QuicConnection::ProcessValidatedPacket() { DVLOG(1) << ENDPOINT << "time of last received packet: " << time_of_last_received_packet_.ToDebuggingValue(); + if (last_size_ > largest_received_packet_size_) { + largest_received_packet_size_ = last_size_; + } + if (perspective_ == Perspective::IS_SERVER && encryption_level_ == ENCRYPTION_NONE && - last_size_ > packet_generator_.max_packet_length()) { + last_size_ > packet_generator_.GetMaxPacketLength()) { set_max_packet_length(last_size_); } return true; @@ -1418,9 +1482,7 @@ void QuicConnection::RetransmitUnackedPackets( void QuicConnection::NeuterUnencryptedPackets() { sent_packet_manager_.NeuterUnencryptedPackets(); // This may have changed the retransmission timer, so re-arm it. - QuicTime retransmission_time = sent_packet_manager_.GetRetransmissionTime(); - retransmission_alarm_->Update(retransmission_time, - QuicTime::Delta::FromMilliseconds(1)); + SetRetransmissionAlarm(); } bool QuicConnection::ShouldGeneratePacket( @@ -1509,7 +1571,7 @@ bool QuicConnection::WritePacketInner(QueuedPacket* packet) { if (!FLAGS_quic_allow_oversized_packets_for_test) { DCHECK_LE(encrypted->length(), kMaxPacketSize); } - DCHECK_LE(encrypted->length(), packet_generator_.max_packet_length()); + DCHECK_LE(encrypted->length(), packet_generator_.GetMaxPacketLength()); DVLOG(1) << ENDPOINT << "Sending packet " << sequence_number << " : " << (packet->serialized_packet.is_fec_packet ? "FEC " @@ -1557,6 +1619,7 @@ bool QuicConnection::WritePacketInner(QueuedPacket* packet) { } SetPingAlarm(); MaybeSetFecAlarm(sequence_number); + MaybeSetMtuAlarm(); DVLOG(1) << ENDPOINT << "time we began writing last sent packet: " << packet_send_time.ToDebuggingValue(); @@ -1576,8 +1639,7 @@ bool QuicConnection::WritePacketInner(QueuedPacket* packet) { IsRetransmittable(*packet)); if (reset_retransmission_alarm || !retransmission_alarm_->IsSet()) { - retransmission_alarm_->Update(sent_packet_manager_.GetRetransmissionTime(), - QuicTime::Delta::FromMilliseconds(1)); + SetRetransmissionAlarm(); } stats_.bytes_sent += result.bytes_written; @@ -1646,9 +1708,7 @@ void QuicConnection::OnSerializedPacket( CloseConnection(QUIC_ENCRYPTION_FAILURE, false); return; } - if (serialized_packet.retransmittable_frames) { - sent_packet_manager_.OnSerializedPacket(serialized_packet); - } + sent_packet_manager_.OnSerializedPacket(serialized_packet); if (serialized_packet.is_fec_packet && fec_alarm_->IsSet()) { // If an FEC packet is serialized with the FEC alarm set, cancel the alarm. fec_alarm_->Cancel(); @@ -1656,6 +1716,14 @@ void QuicConnection::OnSerializedPacket( SendOrQueuePacket(QueuedPacket(serialized_packet, encryption_level_)); } +void QuicConnection::OnResetFecGroup() { + if (!fec_alarm_->IsSet()) { + return; + } + // If an FEC Group is closed with the FEC alarm set, cancel the alarm. + fec_alarm_->Cancel(); +} + void QuicConnection::OnCongestionWindowChange() { packet_generator_.OnCongestionWindowChange( sent_packet_manager_.EstimateMaxPacketsInFlight(max_packet_length())); @@ -1724,6 +1792,7 @@ void QuicConnection::SendPing() { void QuicConnection::SendAck() { ack_alarm_->Cancel(); + ack_queued_ = false; stop_waiting_count_ = 0; num_packets_received_since_last_ack_sent_ = 0; @@ -1753,11 +1822,10 @@ void QuicConnection::OnRetransmissionTimeout() { // Ensure the retransmission alarm is always set if there are unacked packets // and nothing waiting to be sent. + // This happens if the loss algorithm invokes a timer based loss, but the + // packet doesn't need to be retransmitted. if (!HasQueuedData() && !retransmission_alarm_->IsSet()) { - QuicTime rto_timeout = sent_packet_manager_.GetRetransmissionTime(); - if (rto_timeout.IsInitialized()) { - retransmission_alarm_->Set(rto_timeout); - } + SetRetransmissionAlarm(); } } @@ -1781,15 +1849,15 @@ void QuicConnection::SetDefaultEncryptionLevel(EncryptionLevel level) { packet_generator_.set_encryption_level(level); } -void QuicConnection::SetDecrypter(QuicDecrypter* decrypter, - EncryptionLevel level) { - framer_.SetDecrypter(decrypter, level); +void QuicConnection::SetDecrypter(EncryptionLevel level, + QuicDecrypter* decrypter) { + framer_.SetDecrypter(level, decrypter); } -void QuicConnection::SetAlternativeDecrypter(QuicDecrypter* decrypter, - EncryptionLevel level, +void QuicConnection::SetAlternativeDecrypter(EncryptionLevel level, + QuicDecrypter* decrypter, bool latch_once_used) { - framer_.SetAlternativeDecrypter(decrypter, level, latch_once_used); + framer_.SetAlternativeDecrypter(level, decrypter, latch_once_used); } const QuicDecrypter* QuicConnection::decrypter() const { @@ -1947,6 +2015,7 @@ void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) { retransmission_alarm_->Cancel(); send_alarm_->Cancel(); timeout_alarm_->Cancel(); + mtu_discovery_alarm_->Cancel(); } void QuicConnection::SendGoAway(QuicErrorCode error, @@ -1984,11 +2053,11 @@ void QuicConnection::CloseFecGroupsBefore( } QuicByteCount QuicConnection::max_packet_length() const { - return packet_generator_.max_packet_length(); + return packet_generator_.GetMaxPacketLength(); } void QuicConnection::set_max_packet_length(QuicByteCount length) { - return packet_generator_.set_max_packet_length(length); + return packet_generator_.SetMaxPacketLength(length, /*force=*/false); } bool QuicConnection::HasQueuedData() const { @@ -2088,7 +2157,7 @@ void QuicConnection::SetPingAlarm() { // Only clients send pings. return; } - if (!visitor_->HasOpenDataStreams()) { + if (!visitor_->HasOpenDynamicStreams()) { ping_alarm_->Cancel(); // Don't send a ping unless there are open streams. return; @@ -2098,6 +2167,43 @@ void QuicConnection::SetPingAlarm() { QuicTime::Delta::FromSeconds(1)); } +void QuicConnection::SetRetransmissionAlarm() { + if (delay_setting_retransmission_alarm_) { + pending_retransmission_alarm_ = true; + return; + } + QuicTime retransmission_time = sent_packet_manager_.GetRetransmissionTime(); + retransmission_alarm_->Update(retransmission_time, + QuicTime::Delta::FromMilliseconds(1)); +} + +void QuicConnection::MaybeSetMtuAlarm() { + if (!FLAGS_quic_do_path_mtu_discovery) { + return; + } + + // Do not set the alarm if the target size is less than the current size. + // This covers the case when |mtu_discovery_target_| is at its default value, + // zero. + if (mtu_discovery_target_ <= max_packet_length()) { + return; + } + + if (mtu_probe_count_ >= kMtuDiscoveryAttempts) { + return; + } + + if (mtu_discovery_alarm_->IsSet()) { + return; + } + + if (sequence_number_of_last_sent_packet_ >= next_mtu_probe_at_) { + // Use an alarm to send the MTU probe to ensure that no ScopedPacketBundlers + // are active. + mtu_discovery_alarm_->Set(clock_->ApproximateNow()); + } +} + QuicConnection::ScopedPacketBundler::ScopedPacketBundler( QuicConnection* connection, AckBundling send_ack) @@ -2136,6 +2242,28 @@ QuicConnection::ScopedPacketBundler::~ScopedPacketBundler() { connection_->packet_generator_.InBatchMode()); } +QuicConnection::ScopedRetransmissionScheduler::ScopedRetransmissionScheduler( + QuicConnection* connection) + : connection_(connection), + already_delayed_(connection_->delay_setting_retransmission_alarm_) { + if (FLAGS_quic_delay_retransmission_alarm) { + return; + } + connection_->delay_setting_retransmission_alarm_ = true; +} + +QuicConnection::ScopedRetransmissionScheduler:: + ~ScopedRetransmissionScheduler() { + if (FLAGS_quic_delay_retransmission_alarm || already_delayed_) { + return; + } + connection_->delay_setting_retransmission_alarm_ = false; + if (connection_->pending_retransmission_alarm_) { + connection_->SetRetransmissionAlarm(); + connection_->pending_retransmission_alarm_ = false; + } +} + HasRetransmittableData QuicConnection::IsRetransmittable( const QueuedPacket& packet) { // Retransmitted packets retransmittable frames are owned by the unacked @@ -2162,4 +2290,40 @@ bool QuicConnection::IsConnectionClose(const QueuedPacket& packet) { return false; } +void QuicConnection::SendMtuDiscoveryPacket(QuicByteCount target_mtu) { + // Create a listener for the new probe. The ownership of the listener is + // transferred to the AckNotifierManager. The notifier will get destroyed + // before the connection (because it's stored in one of the connection's + // subfields), hence |this| pointer is guaranteed to stay valid at all times. + scoped_refptr<MtuDiscoveryAckListener> last_mtu_discovery_ack_listener( + new MtuDiscoveryAckListener(this, target_mtu)); + + // Send the probe. + packet_generator_.GenerateMtuDiscoveryPacket( + target_mtu, last_mtu_discovery_ack_listener.get()); +} + +void QuicConnection::DiscoverMtu() { + DCHECK(!mtu_discovery_alarm_->IsSet()); + + // Chcek if the MTU has been already increased. + if (mtu_discovery_target_ <= max_packet_length()) { + return; + } + + // Schedule the next probe *before* sending the current one. This is + // important, otherwise, when SendMtuDiscoveryPacket() is called, + // MaybeSetMtuAlarm() will not realize that the probe has been just sent, and + // will reschedule this probe again. + packets_between_mtu_probes_ *= 2; + next_mtu_probe_at_ = + sequence_number_of_last_sent_packet_ + packets_between_mtu_probes_ + 1; + ++mtu_probe_count_; + + DVLOG(2) << "Sending a path MTU discovery packet #" << mtu_probe_count_; + SendMtuDiscoveryPacket(mtu_discovery_target_); + + DCHECK(!mtu_discovery_alarm_->IsSet()); +} + } // namespace net diff --git a/chromium/net/quic/quic_connection.h b/chromium/net/quic/quic_connection.h index d47b8dc9a72..b38504322d9 100644 --- a/chromium/net/quic/quic_connection.h +++ b/chromium/net/quic/quic_connection.h @@ -29,7 +29,6 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/string_piece.h" #include "net/base/ip_endpoint.h" -#include "net/quic/iovector.h" #include "net/quic/quic_ack_notifier.h" #include "net/quic/quic_ack_notifier_manager.h" #include "net/quic/quic_alarm.h" @@ -60,6 +59,35 @@ class PacketSavingConnection; class QuicConnectionPeer; } // namespace test +// The initial number of packets between MTU probes. After each attempt the +// number is doubled. +const QuicPacketCount kPacketsBetweenMtuProbesBase = 100; + +// The number of MTU probes that get sent before giving up. +const size_t kMtuDiscoveryAttempts = 3; + +// Ensure that exponential back-off does not result in an integer overflow. +// The number of packets can be potentially capped, but that is not useful at +// current kMtuDiscoveryAttempts value, and hence is not implemented at present. +static_assert(kMtuDiscoveryAttempts + 8 < 8 * sizeof(QuicPacketSequenceNumber), + "The number of MTU discovery attempts is too high"); +static_assert(kPacketsBetweenMtuProbesBase < (1 << 8), + "The initial number of packets between MTU probes is too high"); + +// The incresed packet size targeted when doing path MTU discovery. +const QuicByteCount kMtuDiscoveryTargetPacketSizeHigh = 1450; +const QuicByteCount kMtuDiscoveryTargetPacketSizeLow = 1430; + +static_assert(kMtuDiscoveryTargetPacketSizeLow <= kMaxPacketSize, + "MTU discovery target is too large"); +static_assert(kMtuDiscoveryTargetPacketSizeHigh <= kMaxPacketSize, + "MTU discovery target is too large"); + +static_assert(kMtuDiscoveryTargetPacketSizeLow > kDefaultMaxPacketSize, + "MTU discovery target does not exceed the default packet size"); +static_assert(kMtuDiscoveryTargetPacketSizeHigh > kDefaultMaxPacketSize, + "MTU discovery target does not exceed the default packet size"); + // Class that receives callbacks from the connection when frames are received // and when other interesting events happen. class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface { @@ -113,7 +141,7 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface { // Called to ask if any streams are open in this visitor, excluding the // reserved crypto and headers stream. - virtual bool HasOpenDataStreams() const = 0; + virtual bool HasOpenDynamicStreams() const = 0; }; // Interface which gets callbacks from the QuicConnection at interesting @@ -289,7 +317,7 @@ class NET_EXPORT_PRIVATE QuicConnection // received for all the packets written in this call. // The |delegate| is not owned by the QuicConnection and must outlive it. QuicConsumedData SendStreamData(QuicStreamId id, - const IOVector& data, + const QuicIOVector& iov, QuicStreamOffset offset, bool fin, FecProtection fec_protection, @@ -388,6 +416,7 @@ class NET_EXPORT_PRIVATE QuicConnection void PopulateAckFrame(QuicAckFrame* ack) override; void PopulateStopWaitingFrame(QuicStopWaitingFrame* stop_waiting) override; void OnSerializedPacket(const SerializedPacket& packet) override; + void OnResetFecGroup() override; // QuicSentPacketManager::NetworkChangeVisitor void OnCongestionWindowChange() override; @@ -414,6 +443,7 @@ class NET_EXPORT_PRIVATE QuicConnection QuicRandom* random_generator() const { return random_generator_; } QuicByteCount max_packet_length() const; void set_max_packet_length(QuicByteCount length); + size_t mtu_probe_count() const { return mtu_probe_count_; } bool connected() const { return connected_; } @@ -486,7 +516,7 @@ class NET_EXPORT_PRIVATE QuicConnection // 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. |level| indicates the encryption level of the new decrypter. - void SetDecrypter(QuicDecrypter* decrypter, EncryptionLevel level); + void SetDecrypter(EncryptionLevel level, QuicDecrypter* decrypter); // SetAlternativeDecrypter sets a decrypter that may be used to decrypt // future packets and takes ownership of it. |level| indicates the encryption @@ -494,8 +524,8 @@ class NET_EXPORT_PRIVATE QuicConnection // 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, + void SetAlternativeDecrypter(EncryptionLevel level, + QuicDecrypter* decrypter, bool latch_once_used); const QuicDecrypter* decrypter() const; @@ -535,13 +565,39 @@ class NET_EXPORT_PRIVATE QuicConnection bool already_in_batch_mode_; }; + // Delays setting the retransmission alarm until the scope is exited. + // When nested, only the outermost scheduler will set the alarm, and inner + // ones have no effect. + class NET_EXPORT_PRIVATE ScopedRetransmissionScheduler { + public: + explicit ScopedRetransmissionScheduler(QuicConnection* connection); + ~ScopedRetransmissionScheduler(); + + private: + QuicConnection* connection_; + // Set to the connection's delay_setting_retransmission_alarm_ value in the + // constructor and when true, causes this class to do nothing. + const bool already_delayed_; + }; + QuicPacketSequenceNumber sequence_number_of_last_sent_packet() const { return sequence_number_of_last_sent_packet_; } + + QuicPacketWriter* writer() { return writer_; } const QuicPacketWriter* writer() const { return writer_; } bool is_secure() const { return is_secure_; } + // Sends an MTU discovery packet of size |target_mtu|. If the packet is + // acknowledged by the peer, the maximum packet size will be increased to + // |target_mtu|. + void SendMtuDiscoveryPacket(QuicByteCount target_mtu); + + // Sends an MTU discovery packet of size |mtu_discovery_target_| and updates + // the MTU discovery alarm. + void DiscoverMtu(); + protected: // Packets which have not been written to the wire. // Owns the QuicPacket* packet. @@ -577,8 +633,6 @@ class NET_EXPORT_PRIVATE QuicConnection // such a version exists, false otherwise. bool SelectMutualVersion(const QuicVersionVector& available_versions); - QuicPacketWriter* writer() { return writer_; } - bool peer_port_changed() const { return peer_port_changed_; } private: @@ -674,6 +728,12 @@ class NET_EXPORT_PRIVATE QuicConnection // Sets the ping alarm to the appropriate value, if any. void SetPingAlarm(); + // Sets the retransmission alarm based on SentPacketManager. + void SetRetransmissionAlarm(); + + // Sets the MTU discovery alarm if necessary. + void MaybeSetMtuAlarm(); + // 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, @@ -766,6 +826,12 @@ class NET_EXPORT_PRIVATE QuicConnection // the peer needs to stop waiting for some packets. int stop_waiting_count_; + // Indicates the retransmit alarm is going to be set by the + // ScopedRetransmitAlarmDelayer + bool delay_setting_retransmission_alarm_; + // Indicates the retransmission alarm needs to be set. + bool pending_retransmission_alarm_; + // 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. @@ -780,6 +846,8 @@ class NET_EXPORT_PRIVATE QuicConnection scoped_ptr<QuicAlarm> timeout_alarm_; // An alarm that fires when a ping should be sent. scoped_ptr<QuicAlarm> ping_alarm_; + // An alarm that fires when an MTU probe should be sent. + scoped_ptr<QuicAlarm> mtu_discovery_alarm_; // Neither visitor is owned by this class. QuicConnectionVisitorInterface* visitor_; @@ -852,6 +920,22 @@ class NET_EXPORT_PRIVATE QuicConnection // True if this is a secure QUIC connection. bool is_secure_; + // The size of the packet we are targeting while doing path MTU discovery. + QuicByteCount mtu_discovery_target_; + + // The number of MTU probes already sent. + size_t mtu_probe_count_; + + // The number of packets between MTU probes. + QuicPacketCount packets_between_mtu_probes_; + + // The sequence number of the packet after which the next MTU probe will be + // sent. + QuicPacketSequenceNumber next_mtu_probe_at_; + + // The size of the largest packet received from peer. + QuicByteCount largest_received_packet_size_; + DISALLOW_COPY_AND_ASSIGN(QuicConnection); }; diff --git a/chromium/net/quic/quic_connection_logger.cc b/chromium/net/quic/quic_connection_logger.cc index fd60d8e8d52..8f55eae54cf 100644 --- a/chromium/net/quic/quic_connection_logger.cc +++ b/chromium/net/quic/quic_connection_logger.cc @@ -9,7 +9,7 @@ #include "base/bind.h" #include "base/callback.h" -#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros.h" #include "base/metrics/sparse_histogram.h" #include "base/profiler/scoped_tracker.h" #include "base/strings/string_number_conversions.h" @@ -35,51 +35,52 @@ namespace { // 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, - NetLogCaptureMode /* capture_mode */) { - base::DictionaryValue* dict = new base::DictionaryValue(); +scoped_ptr<base::Value> NetLogQuicPacketCallback( + const IPEndPoint* self_address, + const IPEndPoint* peer_address, + size_t packet_size, + NetLogCaptureMode /* capture_mode */) { + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); dict->SetString("self_address", self_address->ToString()); dict->SetString("peer_address", peer_address->ToString()); dict->SetInteger("size", packet_size); - return dict; + return dict.Pass(); } -base::Value* NetLogQuicPacketSentCallback( +scoped_ptr<base::Value> NetLogQuicPacketSentCallback( const SerializedPacket& serialized_packet, EncryptionLevel level, TransmissionType transmission_type, size_t packet_size, QuicTime sent_time, NetLogCaptureMode /* capture_mode */) { - base::DictionaryValue* dict = new base::DictionaryValue(); + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); dict->SetInteger("encryption_level", level); dict->SetInteger("transmission_type", transmission_type); dict->SetString("packet_sequence_number", base::Uint64ToString(serialized_packet.sequence_number)); dict->SetInteger("size", packet_size); - dict->SetInteger("sent_time_us", - static_cast<int>(sent_time.ToDebuggingValue())); - return dict; + dict->SetString("sent_time_us", + base::Int64ToString(sent_time.ToDebuggingValue())); + return dict.Pass(); } -base::Value* NetLogQuicPacketRetransmittedCallback( +scoped_ptr<base::Value> NetLogQuicPacketRetransmittedCallback( QuicPacketSequenceNumber old_sequence_number, QuicPacketSequenceNumber new_sequence_number, NetLogCaptureMode /* capture_mode */) { - base::DictionaryValue* dict = new base::DictionaryValue(); + scoped_ptr<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; + return dict.Pass(); } -base::Value* NetLogQuicPacketHeaderCallback( +scoped_ptr<base::Value> NetLogQuicPacketHeaderCallback( const QuicPacketHeader* header, NetLogCaptureMode /* capture_mode */) { - base::DictionaryValue* dict = new base::DictionaryValue(); + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); dict->SetString("connection_id", base::Uint64ToString(header->public_header.connection_id)); dict->SetInteger("reset_flag", header->public_header.reset_flag); @@ -89,28 +90,29 @@ base::Value* NetLogQuicPacketHeaderCallback( dict->SetInteger("entropy_flag", header->entropy_flag); dict->SetInteger("fec_flag", header->fec_flag); dict->SetInteger("fec_group", static_cast<int>(header->fec_group)); - return dict; + return dict.Pass(); } -base::Value* NetLogQuicStreamFrameCallback( +scoped_ptr<base::Value> NetLogQuicStreamFrameCallback( const QuicStreamFrame* frame, NetLogCaptureMode /* capture_mode */) { - base::DictionaryValue* dict = new base::DictionaryValue(); + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); dict->SetInteger("stream_id", frame->stream_id); dict->SetBoolean("fin", frame->fin); dict->SetString("offset", base::Uint64ToString(frame->offset)); - dict->SetInteger("length", frame->data.TotalBufferSize()); - return dict; + dict->SetInteger("length", frame->data.size()); + return dict.Pass(); } -base::Value* NetLogQuicAckFrameCallback(const QuicAckFrame* frame, - NetLogCaptureMode /* capture_mode */) { - base::DictionaryValue* dict = new base::DictionaryValue(); +scoped_ptr<base::Value> NetLogQuicAckFrameCallback( + const QuicAckFrame* frame, + NetLogCaptureMode /* capture_mode */) { + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); dict->SetString("largest_observed", base::Uint64ToString(frame->largest_observed)); - dict->SetInteger( + dict->SetString( "delta_time_largest_observed_us", - static_cast<int>(frame->delta_time_largest_observed.ToMicroseconds())); + base::Int64ToString(frame->delta_time_largest_observed.ToMicroseconds())); dict->SetInteger("entropy_hash", frame->entropy_hash); dict->SetBoolean("truncated", frame->is_truncated); @@ -138,117 +140,117 @@ base::Value* NetLogQuicAckFrameCallback(const QuicAckFrame* frame, it != received_times.end(); ++it) { base::DictionaryValue* info = new base::DictionaryValue(); info->SetInteger("sequence_number", static_cast<int>(it->first)); - info->SetInteger("received", - static_cast<int>(it->second.ToDebuggingValue())); + info->SetString("received", + base::Int64ToString(it->second.ToDebuggingValue())); received->Append(info); } - return dict; + return dict.Pass(); } -base::Value* NetLogQuicRstStreamFrameCallback( +scoped_ptr<base::Value> NetLogQuicRstStreamFrameCallback( const QuicRstStreamFrame* frame, NetLogCaptureMode /* capture_mode */) { - base::DictionaryValue* dict = new base::DictionaryValue(); + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); dict->SetInteger("stream_id", frame->stream_id); dict->SetInteger("quic_rst_stream_error", frame->error_code); dict->SetString("details", frame->error_details); - return dict; + return dict.Pass(); } -base::Value* NetLogQuicConnectionCloseFrameCallback( +scoped_ptr<base::Value> NetLogQuicConnectionCloseFrameCallback( const QuicConnectionCloseFrame* frame, NetLogCaptureMode /* capture_mode */) { - base::DictionaryValue* dict = new base::DictionaryValue(); + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); dict->SetInteger("quic_error", frame->error_code); dict->SetString("details", frame->error_details); - return dict; + return dict.Pass(); } -base::Value* NetLogQuicWindowUpdateFrameCallback( +scoped_ptr<base::Value> NetLogQuicWindowUpdateFrameCallback( const QuicWindowUpdateFrame* frame, NetLogCaptureMode /* capture_mode */) { - base::DictionaryValue* dict = new base::DictionaryValue(); + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); dict->SetInteger("stream_id", frame->stream_id); dict->SetString("byte_offset", base::Uint64ToString(frame->byte_offset)); - return dict; + return dict.Pass(); } -base::Value* NetLogQuicBlockedFrameCallback( +scoped_ptr<base::Value> NetLogQuicBlockedFrameCallback( const QuicBlockedFrame* frame, NetLogCaptureMode /* capture_mode */) { - base::DictionaryValue* dict = new base::DictionaryValue(); + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); dict->SetInteger("stream_id", frame->stream_id); - return dict; + return dict.Pass(); } -base::Value* NetLogQuicGoAwayFrameCallback( +scoped_ptr<base::Value> NetLogQuicGoAwayFrameCallback( const QuicGoAwayFrame* frame, NetLogCaptureMode /* capture_mode */) { - base::DictionaryValue* dict = new base::DictionaryValue(); + scoped_ptr<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; + return dict.Pass(); } -base::Value* NetLogQuicStopWaitingFrameCallback( +scoped_ptr<base::Value> NetLogQuicStopWaitingFrameCallback( const QuicStopWaitingFrame* frame, NetLogCaptureMode /* capture_mode */) { - base::DictionaryValue* dict = new base::DictionaryValue(); + scoped_ptr<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; + return dict.Pass(); } -base::Value* NetLogQuicVersionNegotiationPacketCallback( +scoped_ptr<base::Value> NetLogQuicVersionNegotiationPacketCallback( const QuicVersionNegotiationPacket* packet, NetLogCaptureMode /* capture_mode */) { - base::DictionaryValue* dict = new base::DictionaryValue(); + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); base::ListValue* versions = new base::ListValue(); dict->Set("versions", versions); for (QuicVersionVector::const_iterator it = packet->versions.begin(); it != packet->versions.end(); ++it) { versions->AppendString(QuicVersionToString(*it)); } - return dict; + return dict.Pass(); } -base::Value* NetLogQuicCryptoHandshakeMessageCallback( +scoped_ptr<base::Value> NetLogQuicCryptoHandshakeMessageCallback( const CryptoHandshakeMessage* message, NetLogCaptureMode /* capture_mode */) { - base::DictionaryValue* dict = new base::DictionaryValue(); + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); dict->SetString("quic_crypto_handshake_message", message->DebugString()); - return dict; + return dict.Pass(); } -base::Value* NetLogQuicOnConnectionClosedCallback( +scoped_ptr<base::Value> NetLogQuicOnConnectionClosedCallback( QuicErrorCode error, bool from_peer, NetLogCaptureMode /* capture_mode */) { - base::DictionaryValue* dict = new base::DictionaryValue(); + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); dict->SetInteger("quic_error", error); dict->SetBoolean("from_peer", from_peer); - return dict; + return dict.Pass(); } -base::Value* NetLogQuicCertificateVerifiedCallback( +scoped_ptr<base::Value> NetLogQuicCertificateVerifiedCallback( scoped_refptr<X509Certificate> cert, NetLogCaptureMode /* capture_mode */) { // Only the subjects are logged so that we can investigate connection pooling. // More fields could be logged in the future. std::vector<std::string> dns_names; cert->GetDNSNames(&dns_names); - base::DictionaryValue* dict = new base::DictionaryValue(); + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); base::ListValue* subjects = new base::ListValue(); for (std::vector<std::string>::const_iterator it = dns_names.begin(); it != dns_names.end(); it++) { subjects->Append(new base::StringValue(*it)); } dict->Set("subjects", subjects); - return dict; + return dict.Pass(); } void UpdatePacketGapSentHistogram(size_t num_consecutive_missing_packets) { @@ -279,7 +281,7 @@ AddressFamily GetRealAddressFamily(const IPAddressNumber& address) { } // namespace QuicConnectionLogger::QuicConnectionLogger( - QuicSession* session, + QuicSpdySession* session, const char* const connection_description, const BoundNetLog& net_log) : net_log_(net_log), @@ -426,6 +428,10 @@ void QuicConnectionLogger::OnFrameAddedToPacket(const QuicFrame& 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; + case MTU_DISCOVERY_FRAME: + // MtuDiscoveryFrame is PingFrame on wire, it does not have any payload. + net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_MTU_DISCOVERY_FRAME_SENT); + break; default: DCHECK(false) << "Illegal frame type: " << frame.type; } diff --git a/chromium/net/quic/quic_connection_logger.h b/chromium/net/quic/quic_connection_logger.h index 4812596be89..6e81e217c9e 100644 --- a/chromium/net/quic/quic_connection_logger.h +++ b/chromium/net/quic/quic_connection_logger.h @@ -12,7 +12,7 @@ #include "net/log/net_log.h" #include "net/quic/quic_connection.h" #include "net/quic/quic_protocol.h" -#include "net/quic/quic_session.h" +#include "net/quic/quic_spdy_session.h" namespace net { namespace test { @@ -27,7 +27,7 @@ class CertVerifyResult; class NET_EXPORT_PRIVATE QuicConnectionLogger : public QuicConnectionDebugVisitor { public: - QuicConnectionLogger(QuicSession* session, + QuicConnectionLogger(QuicSpdySession* session, const char* const connection_description, const BoundNetLog& net_log); @@ -118,7 +118,7 @@ class NET_EXPORT_PRIVATE QuicConnectionLogger void RecordLossHistograms() const; BoundNetLog net_log_; - QuicSession* session_; // Unowned. + QuicSpdySession* session_; // Unowned. // The last packet sequence number received. QuicPacketSequenceNumber last_received_packet_sequence_number_; // The size of the most recently received packet. diff --git a/chromium/net/quic/quic_connection_logger_unittest.cc b/chromium/net/quic/quic_connection_logger_unittest.cc index 9f14a09ef94..8bdb78ecdeb 100644 --- a/chromium/net/quic/quic_connection_logger_unittest.cc +++ b/chromium/net/quic/quic_connection_logger_unittest.cc @@ -36,7 +36,7 @@ class QuicConnectionLoggerTest : public ::testing::Test { logger_(&session_, "CONNECTION_UNKNOWN", net_log_) {} BoundNetLog net_log_; - MockSession session_; + MockQuicSpdySession session_; QuicConnectionLogger logger_; }; diff --git a/chromium/net/quic/quic_connection_stats.cc b/chromium/net/quic/quic_connection_stats.cc index 02938c2a108..e581c17c254 100644 --- a/chromium/net/quic/quic_connection_stats.cc +++ b/chromium/net/quic/quic_connection_stats.cc @@ -33,6 +33,7 @@ QuicConnectionStats::QuicConnectionStats() min_rtt_us(0), srtt_us(0), max_packet_size(0), + max_received_packet_size(0), estimated_bandwidth(QuicBandwidth::Zero()), packets_reordered(0), max_sequence_reordering(0), diff --git a/chromium/net/quic/quic_connection_stats.h b/chromium/net/quic/quic_connection_stats.h index cd321a95601..ca3fb91041c 100644 --- a/chromium/net/quic/quic_connection_stats.h +++ b/chromium/net/quic/quic_connection_stats.h @@ -63,6 +63,7 @@ struct NET_EXPORT_PRIVATE QuicConnectionStats { int64 min_rtt_us; // Minimum RTT in microseconds. int64 srtt_us; // Smoothed RTT in microseconds. QuicByteCount max_packet_size; + QuicByteCount max_received_packet_size; QuicBandwidth estimated_bandwidth; // Reordering stats for received packets. diff --git a/chromium/net/quic/quic_connection_test.cc b/chromium/net/quic/quic_connection_test.cc index fec998e1606..fcc9efece0a 100644 --- a/chromium/net/quic/quic_connection_test.cc +++ b/chromium/net/quic/quic_connection_test.cc @@ -4,6 +4,8 @@ #include "net/quic/quic_connection.h" +#include <ostream> + #include "base/basictypes.h" #include "base/bind.h" #include "base/memory/scoped_ptr.h" @@ -34,6 +36,7 @@ using base::StringPiece; using std::map; +using std::ostream; using std::string; using std::vector; using testing::AnyNumber; @@ -78,16 +81,6 @@ class TaggingEncrypter : public QuicEncrypter { bool SetNoncePrefix(StringPiece nonce_prefix) override { return true; } - bool Encrypt(StringPiece nonce, - StringPiece associated_data, - StringPiece plaintext, - unsigned char* output) override { - memcpy(output, plaintext.data(), plaintext.size()); - output += plaintext.size(); - memset(output, tag_, kTagSize); - return true; - } - bool EncryptPacket(QuicPacketSequenceNumber sequence_number, StringPiece associated_data, StringPiece plaintext, @@ -98,8 +91,9 @@ class TaggingEncrypter : public QuicEncrypter { if (max_output_length < len) { return false; } - Encrypt(StringPiece(), associated_data, plaintext, - reinterpret_cast<unsigned char*>(output)); + memcpy(output, plaintext.data(), plaintext.size()); + output += plaintext.size(); + memset(output, tag_, kTagSize); *output_length = len; return true; } @@ -262,7 +256,7 @@ class TestPacketWriter : public QuicPacketWriter { } if (use_tagging_decrypter_) { - framer_.framer()->SetDecrypter(new TaggingDecrypter, ENCRYPTION_NONE); + framer_.framer()->SetDecrypter(ENCRYPTION_NONE, new TaggingDecrypter); } EXPECT_TRUE(framer_.ProcessPacket(packet)); if (block_on_next_write_) { @@ -432,7 +426,7 @@ class TestConnection : public QuicConnection { : nullptr; char buffer[kMaxPacketSize]; QuicEncryptedPacket* encrypted = - QuicConnectionPeer::GetFramer(this)->EncryptPacket( + QuicConnectionPeer::GetFramer(this)->EncryptPayload( ENCRYPTION_NONE, sequence_number, *packet, buffer, kMaxPacketSize); delete packet; OnSerializedPacket(SerializedPacket(sequence_number, @@ -467,10 +461,8 @@ class TestConnection : public QuicConnection { bool fin, FecProtection fec_protection, QuicAckNotifier::DelegateInterface* delegate) { - IOVector data_iov; - if (!data.empty()) { - data_iov.Append(const_cast<char*>(data.data()), data.size()); - } + struct iovec iov; + QuicIOVector data_iov(MakeIOVector(data, &iov)); return QuicConnection::SendStreamData(id, data_iov, offset, fin, fec_protection, delegate); } @@ -523,6 +515,27 @@ class TestConnection : public QuicConnection { QuicConnectionPeer::SetPerspective(this, perspective); } + // Enable path MTU discovery. Assumes that the test is performed from the + // client perspective and the higher value of MTU target is used. + void EnablePathMtuDiscovery(MockSendAlgorithm* send_algorithm) { + ASSERT_EQ(Perspective::IS_CLIENT, perspective()); + + FLAGS_quic_do_path_mtu_discovery = true; + + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(kMTUH); + config.SetConnectionOptionsToSend(connection_options); + EXPECT_CALL(*send_algorithm, SetFromConfig(_, _)); + SetFromConfig(config); + + // Normally, the pacing would be disabled in the test, but calling + // SetFromConfig enables it. Set nearly-infinite bandwidth to make the + // pacing algorithm work. + EXPECT_CALL(*send_algorithm, PacingRate()) + .WillRepeatedly(Return(QuicBandwidth::FromKBytesPerSecond(10000))); + } + TestConnectionHelper::TestAlarm* GetAckAlarm() { return reinterpret_cast<TestConnectionHelper::TestAlarm*>( QuicConnectionPeer::GetAckAlarm(this)); @@ -558,6 +571,11 @@ class TestConnection : public QuicConnection { QuicConnectionPeer::GetTimeoutAlarm(this)); } + TestConnectionHelper::TestAlarm* GetMtuDiscoveryAlarm() { + return reinterpret_cast<TestConnectionHelper::TestAlarm*>( + QuicConnectionPeer::GetMtuDiscoveryAlarm(this)); + } + using QuicConnection::SelectMutualVersion; private: @@ -596,7 +614,33 @@ class MockPacketWriterFactory : public QuicConnection::PacketWriterFactory { MOCK_CONST_METHOD1(Create, QuicPacketWriter*(QuicConnection* connection)); }; -class QuicConnectionTest : public ::testing::TestWithParam<QuicVersion> { +// Run tests with combinations of {QuicVersion, fec_send_policy}. +struct TestParams { + TestParams(QuicVersion version, FecSendPolicy fec_send_policy) + : version(version), fec_send_policy(fec_send_policy) {} + + friend ostream& operator<<(ostream& os, const TestParams& p) { + os << "{ client_version: " << QuicVersionToString(p.version) + << " fec_send_policy: " << p.fec_send_policy << " }"; + return os; + } + + QuicVersion version; + FecSendPolicy fec_send_policy; +}; + +// 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], FEC_ANY_TRIGGER)); + params.push_back(TestParams(all_supported_versions[i], FEC_ALARM_TRIGGER)); + } + return params; +} + +class QuicConnectionTest : public ::testing::TestWithParam<TestParams> { protected: QuicConnectionTest() : connection_id_(42), @@ -618,14 +662,15 @@ class QuicConnectionTest : public ::testing::TestWithParam<QuicVersion> { creator_(QuicConnectionPeer::GetPacketCreator(&connection_)), generator_(QuicConnectionPeer::GetPacketGenerator(&connection_)), manager_(QuicConnectionPeer::GetSentPacketManager(&connection_)), - frame1_(1, false, 0, MakeIOVector(data1)), - frame2_(1, false, 3, MakeIOVector(data2)), + frame1_(1, false, 0, StringPiece(data1)), + frame2_(1, false, 3, StringPiece(data2)), 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_); + generator_->set_fec_send_policy(GetParam().fec_send_policy); EXPECT_CALL( *send_algorithm_, TimeUntilSend(_, _, _)).WillRepeatedly(Return( QuicTime::Delta::Zero())); @@ -647,7 +692,8 @@ class QuicConnectionTest : public ::testing::TestWithParam<QuicVersion> { EXPECT_CALL(visitor_, WillingAndAbleToWrite()).Times(AnyNumber()); EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber()); EXPECT_CALL(visitor_, OnCanWrite()).Times(AnyNumber()); - EXPECT_CALL(visitor_, HasOpenDataStreams()).WillRepeatedly(Return(false)); + EXPECT_CALL(visitor_, HasOpenDynamicStreams()) + .WillRepeatedly(Return(false)); EXPECT_CALL(visitor_, OnCongestionWindowChange(_)).Times(AnyNumber()); EXPECT_CALL(*loss_algorithm_, GetLossTimeout()) @@ -656,9 +702,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<QuicVersion> { .WillRepeatedly(Return(SequenceNumberSet())); } - QuicVersion version() { - return GetParam(); - } + QuicVersion version() { return GetParam().version; } QuicAckFrame* outgoing_ack() { QuicConnectionPeer::PopulateAckFrame(&connection_, &ack_); @@ -715,7 +759,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<QuicVersion> { entropy_flag)); char buffer[kMaxPacketSize]; scoped_ptr<QuicEncryptedPacket> encrypted( - framer_.EncryptPacket(level, number, *packet, buffer, kMaxPacketSize)); + framer_.EncryptPayload(level, number, *packet, buffer, kMaxPacketSize)); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); return encrypted->length(); } @@ -724,7 +768,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<QuicVersion> { QuicFecGroupNumber fec_group) { scoped_ptr<QuicPacket> packet(ConstructClosePacket(number, fec_group)); char buffer[kMaxPacketSize]; - scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket( + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPayload( ENCRYPTION_NONE, number, *packet, buffer, kMaxPacketSize)); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); } @@ -788,7 +832,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<QuicVersion> { scoped_ptr<QuicPacket> fec_packet(framer_.BuildFecPacket(header, fec_data)); char buffer[kMaxPacketSize]; - scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket( + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPayload( ENCRYPTION_NONE, number, *fec_packet, buffer, kMaxPacketSize)); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); @@ -819,6 +863,12 @@ class QuicConnectionTest : public ::testing::TestWithParam<QuicVersion> { .Times(AnyNumber()); } + void ProcessAckPacket(QuicPacketSequenceNumber packet_number, + QuicAckFrame* frame) { + QuicPacketCreatorPeer::SetSequenceNumber(&peer_creator_, packet_number - 1); + ProcessFramePacket(QuicFrame(frame)); + } + QuicPacketEntropyHash ProcessAckPacket(QuicAckFrame* frame) { return ProcessFramePacket(QuicFrame(frame)); } @@ -979,7 +1029,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<QuicVersion> { // Run all end to end tests with all supported versions. INSTANTIATE_TEST_CASE_P(SupportedVersion, QuicConnectionTest, - ::testing::ValuesIn(QuicSupportedVersions())); + ::testing::ValuesIn(GetTestParams())); TEST_P(QuicConnectionTest, MaxPacketSize) { EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); @@ -1011,7 +1061,7 @@ TEST_P(QuicConnectionTest, IncreaseServerMaxPacketSize) { frames.push_back(QuicFrame(&padding)); scoped_ptr<QuicPacket> packet(ConstructPacket(header, frames)); char buffer[kMaxPacketSize]; - scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket( + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPayload( ENCRYPTION_NONE, 12, *packet, buffer, kMaxPacketSize)); EXPECT_EQ(kMaxPacketSize, encrypted->length()); @@ -1094,10 +1144,9 @@ TEST_P(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. - QuicPacketCreatorPeer::SetSequenceNumber(&peer_creator_, 5); QuicAckFrame frame = InitAckFrame(1); EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _)); - ProcessAckPacket(&frame); + ProcessAckPacket(6, &frame); // Force an ack to be sent. SendAckPacketToPeer(); @@ -1183,9 +1232,8 @@ TEST_P(QuicConnectionTest, AckReceiptCausesAckSendBadEntropy) { TimeUntilSend(_, _, _)).WillRepeatedly( testing::Return(QuicTime::Delta::Zero())); // Skip a packet and then record an ack. - QuicPacketCreatorPeer::SetSequenceNumber(&peer_creator_, 2); QuicAckFrame frame = InitAckFrame(0); - ProcessAckPacket(&frame); + ProcessAckPacket(3, &frame); } TEST_P(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) { @@ -1208,6 +1256,46 @@ TEST_P(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) { EXPECT_EQ(3u, writer_->packets_write_attempts()); } +TEST_P(QuicConnectionTest, OutOfOrderAckReceiptCausesNoAck) { + ValueRestore<bool> old_flag(&FLAGS_quic_dont_ack_acks, true); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + SendStreamDataToPeer(1, "foo", 0, !kFin, nullptr); + SendStreamDataToPeer(1, "bar", 3, !kFin, nullptr); + EXPECT_EQ(2u, writer_->packets_write_attempts()); + + QuicAckFrame ack1 = InitAckFrame(1); + QuicAckFrame ack2 = InitAckFrame(2); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + ProcessAckPacket(2, &ack2); + // Should ack immediately since we have missing packets. + EXPECT_EQ(2u, writer_->packets_write_attempts()); + + ProcessAckPacket(1, &ack1); + // Should not ack an ack filling a missing packet. + EXPECT_EQ(2u, writer_->packets_write_attempts()); +} + +TEST_P(QuicConnectionTest, OutOfOrderAckReceiptCausesOneAck) { + ValueRestore<bool> old_flag(&FLAGS_quic_dont_ack_acks, false); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + + SendStreamDataToPeer(1, "foo", 0, !kFin, nullptr); + SendStreamDataToPeer(1, "bar", 3, !kFin, nullptr); + EXPECT_EQ(2u, writer_->packets_write_attempts()); + + QuicAckFrame ack1 = InitAckFrame(1); + QuicAckFrame ack2 = InitAckFrame(2); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + ProcessAckPacket(2, &ack2); + // Should ack immediately since we have missing packets. + EXPECT_EQ(3u, writer_->packets_write_attempts()); + + ProcessAckPacket(1, &ack1); + // Should not ack an ack filling a missing packet. + EXPECT_EQ(3u, writer_->packets_write_attempts()); +} + TEST_P(QuicConnectionTest, AckReceiptCausesAckSend) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); @@ -1319,7 +1407,8 @@ TEST_P(QuicConnectionTest, LeastUnackedLower) { TEST_P(QuicConnectionTest, TooManySentPackets) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - for (int i = 0; i < 3100; ++i) { + const int num_packets = kMaxTrackedPackets + 100; + for (int i = 0; i < num_packets; ++i) { SendStreamDataToPeer(1, "foo", 3 * i, !kFin, nullptr); } @@ -1330,27 +1419,28 @@ TEST_P(QuicConnectionTest, TooManySentPackets) { // We're receive buffer limited, so the connection won't try to write more. EXPECT_CALL(visitor_, OnCanWrite()).Times(0); - // Nack every packet except the last one, leaving a huge gap. - QuicAckFrame frame1 = InitAckFrame(3100); - for (QuicPacketSequenceNumber i = 1; i < 3100; ++i) { - NackPacket(i, &frame1); - } + // Nack the first packet and ack the rest, leaving a huge gap. + QuicAckFrame frame1 = InitAckFrame(num_packets); + NackPacket(1, &frame1); ProcessAckPacket(&frame1); } +// Flaky time out on Windows 7. http://crbug.com/501812 +#if !defined(OS_WIN) TEST_P(QuicConnectionTest, TooManyReceivedPackets) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); EXPECT_CALL(visitor_, OnConnectionClosed( QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS, false)); - // Miss every other packet for 3000 packets. - for (QuicPacketSequenceNumber i = 1; i < 3000; ++i) { - ProcessPacket(i * 2); + // Miss 99 of every 100 packets for 5500 packets. + for (QuicPacketSequenceNumber i = 1; i < kMaxTrackedPackets + 500; i += 100) { + ProcessPacket(i); if (!connection_.connected()) { break; } } } +#endif TEST_P(QuicConnectionTest, LargestObservedLower) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); @@ -1584,11 +1674,17 @@ TEST_P(QuicConnectionTest, FECSending) { connection_.version(), kIncludeVersion, PACKET_8BYTE_CONNECTION_ID, PACKET_1BYTE_SEQUENCE_NUMBER, IN_FEC_GROUP, &payload_length); - creator_->SetMaxPacketLength(length); + connection_.set_max_packet_length(length); - // Send 4 protected data packets, which should also trigger 1 FEC packet. - EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA)).Times(5); + if (generator_->fec_send_policy() == FEC_ALARM_TRIGGER) { + // Send 4 protected data packets. FEC packet is not sent. + EXPECT_CALL(*send_algorithm_, + OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA)).Times(4); + } else { + // Send 4 protected data packets, which should also trigger 1 FEC packet. + EXPECT_CALL(*send_algorithm_, + OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA)).Times(5); + } // 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, nullptr); @@ -1604,7 +1700,7 @@ TEST_P(QuicConnectionTest, FECQueueing) { connection_.version(), kIncludeVersion, PACKET_8BYTE_CONNECTION_ID, PACKET_1BYTE_SEQUENCE_NUMBER, IN_FEC_GROUP, &payload_length); - creator_->SetMaxPacketLength(length); + connection_.set_max_packet_length(length); EXPECT_TRUE(creator_->IsFecEnabled()); EXPECT_EQ(0u, connection_.NumQueuedPackets()); @@ -1613,8 +1709,13 @@ TEST_P(QuicConnectionTest, FECQueueing) { connection_.SendStreamDataWithStringWithFec(1, payload, 0, !kFin, nullptr); 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()); + if (generator_->fec_send_policy() == FEC_ALARM_TRIGGER) { + // Expect the first data packet to be queued and not the FEC packet. + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + } else { + // Expect the first data packet and the fec packet to be queued. + EXPECT_EQ(2u, connection_.NumQueuedPackets()); + } } TEST_P(QuicConnectionTest, FECAlarmStoppedWhenFECPacketSent) { @@ -1630,11 +1731,21 @@ TEST_P(QuicConnectionTest, FECAlarmStoppedWhenFECPacketSent) { connection_.SendStreamDataWithStringWithFec(3, "foo", 0, true, nullptr); EXPECT_TRUE(connection_.GetFecAlarm()->IsSet()); - // Second data packet triggers FEC packet out. FEC alarm should not be set. - EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA)).Times(2); + if (generator_->fec_send_policy() == FEC_ALARM_TRIGGER) { + // If FEC send policy is FEC_ALARM_TRIGGER, FEC packet is not sent. + // FEC alarm should not be set. + EXPECT_CALL(*send_algorithm_, + OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA)).Times(1); + } else { + // Second data packet triggers FEC packet out. FEC alarm should not be set. + EXPECT_CALL(*send_algorithm_, + OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA)).Times(2); + } connection_.SendStreamDataWithStringWithFec(5, "foo", 0, true, nullptr); - EXPECT_TRUE(writer_->header().fec_flag); + if (generator_->fec_send_policy() == FEC_ANY_TRIGGER) { + EXPECT_TRUE(writer_->header().fec_flag); + } + EXPECT_FALSE(creator_->IsFecGroupOpen()); EXPECT_FALSE(connection_.GetFecAlarm()->IsSet()); } @@ -2054,9 +2165,8 @@ TEST_P(QuicConnectionTest, FramePackingAckResponse) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); // Process an ack to cause the visitor's OnCanWrite to be invoked. - QuicPacketCreatorPeer::SetSequenceNumber(&peer_creator_, 2); QuicAckFrame ack_one = InitAckFrame(0); - ProcessAckPacket(&ack_one); + ProcessAckPacket(3, &ack_one); EXPECT_EQ(0u, connection_.NumQueuedPackets()); EXPECT_FALSE(connection_.HasQueuedData()); @@ -2077,10 +2187,13 @@ TEST_P(QuicConnectionTest, FramePackingSendv) { 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, MAY_FEC_PROTECT, nullptr); + struct iovec iov[2]; + iov[0].iov_base = data; + iov[0].iov_len = 2; + iov[1].iov_base = data + 2; + iov[1].iov_len = 2; + connection_.SendStreamData(1, QuicIOVector(iov, 2, 4), 0, !kFin, + MAY_FEC_PROTECT, nullptr); EXPECT_EQ(0u, connection_.NumQueuedPackets()); EXPECT_FALSE(connection_.HasQueuedData()); @@ -2091,9 +2204,7 @@ TEST_P(QuicConnectionTest, FramePackingSendv) { 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))); + EXPECT_EQ("ABCD", frame.data); } TEST_P(QuicConnectionTest, FramePackingSendvQueued) { @@ -2102,10 +2213,13 @@ TEST_P(QuicConnectionTest, FramePackingSendvQueued) { 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, MAY_FEC_PROTECT, nullptr); + struct iovec iov[2]; + iov[0].iov_base = data; + iov[0].iov_len = 2; + iov[1].iov_base = data + 2; + iov[1].iov_len = 2; + connection_.SendStreamData(1, QuicIOVector(iov, 2, 4), 0, !kFin, + MAY_FEC_PROTECT, nullptr); EXPECT_EQ(1u, connection_.NumQueuedPackets()); EXPECT_TRUE(connection_.HasQueuedData()); @@ -2124,7 +2238,7 @@ TEST_P(QuicConnectionTest, FramePackingSendvQueued) { TEST_P(QuicConnectionTest, SendingZeroBytes) { // Send a zero byte write with a fin using writev. EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); - IOVector empty_iov; + QuicIOVector empty_iov(nullptr, 0, 0); connection_.SendStreamData(1, empty_iov, 0, kFin, MAY_FEC_PROTECT, nullptr); EXPECT_EQ(0u, connection_.NumQueuedPackets()); @@ -2858,8 +2972,7 @@ TEST_P(QuicConnectionTest, BufferNonDecryptablePackets) { // 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), - ENCRYPTION_INITIAL); + connection_.SetDecrypter(ENCRYPTION_INITIAL, new StrictTaggingDecrypter(tag)); connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag)); EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(2); @@ -2891,7 +3004,7 @@ TEST_P(QuicConnectionTest, Buffer100NonDecryptablePackets) { // Transition to the new encryption state and process another encrypted packet // which should result in the original packets being processed. - connection_.SetDecrypter(new StrictTaggingDecrypter(tag), ENCRYPTION_INITIAL); + connection_.SetDecrypter(ENCRYPTION_INITIAL, new StrictTaggingDecrypter(tag)); connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag)); EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(101); @@ -3044,6 +3157,7 @@ TEST_P(QuicConnectionTest, InitialTimeout) { EXPECT_FALSE(connection_.GetResumeWritesAlarm()->IsSet()); EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); } TEST_P(QuicConnectionTest, OverallTimeout) { @@ -3091,7 +3205,7 @@ TEST_P(QuicConnectionTest, OverallTimeout) { TEST_P(QuicConnectionTest, PingAfterSend) { EXPECT_TRUE(connection_.connected()); - EXPECT_CALL(visitor_, HasOpenDataStreams()).WillRepeatedly(Return(true)); + EXPECT_CALL(visitor_, HasOpenDynamicStreams()).WillRepeatedly(Return(true)); EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); // Advance to 5ms, and send a packet to the peer, which will set @@ -3124,13 +3238,192 @@ TEST_P(QuicConnectionTest, PingAfterSend) { ASSERT_EQ(1u, writer_->ping_frames().size()); writer_->Reset(); - EXPECT_CALL(visitor_, HasOpenDataStreams()).WillRepeatedly(Return(false)); + EXPECT_CALL(visitor_, HasOpenDynamicStreams()).WillRepeatedly(Return(false)); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); SendAckPacketToPeer(); EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); } +// Tests whether sending an MTU discovery packet to peer successfully causes the +// maximum packet size to increase. +TEST_P(QuicConnectionTest, SendMtuDiscoveryPacket) { + EXPECT_TRUE(connection_.connected()); + + // Send an MTU probe. + const size_t new_mtu = kDefaultMaxPacketSize + 100; + QuicByteCount mtu_probe_size; + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(DoAll(SaveArg<3>(&mtu_probe_size), Return(true))); + connection_.SendMtuDiscoveryPacket(new_mtu); + EXPECT_EQ(new_mtu, mtu_probe_size); + EXPECT_EQ(1u, creator_->sequence_number()); + + // Send more than MTU worth of data. No acknowledgement was received so far, + // so the MTU should be at its old value. + const string data(kDefaultMaxPacketSize + 1, '.'); + QuicByteCount size_before_mtu_change; + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(DoAll(SaveArg<3>(&size_before_mtu_change), Return(true))) + .WillOnce(Return(true)); + connection_.SendStreamDataWithString(3, data, 0, kFin, nullptr); + EXPECT_EQ(3u, creator_->sequence_number()); + EXPECT_EQ(kDefaultMaxPacketSize, size_before_mtu_change); + + // Acknowledge all packets so far. + QuicAckFrame probe_ack = InitAckFrame(3); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + ProcessAckPacket(&probe_ack); + EXPECT_EQ(new_mtu, connection_.max_packet_length()); + + // Send the same data again. Check that it fits into a single packet now. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + connection_.SendStreamDataWithString(3, data, 0, kFin, nullptr); + EXPECT_EQ(4u, creator_->sequence_number()); +} + +// Tests whether MTU discovery does not happen when it is not explicitly enabled +// by the connection options. +TEST_P(QuicConnectionTest, MtuDiscoveryDisabled) { + EXPECT_TRUE(connection_.connected()); + + // Restore the current value FLAGS_quic_do_path_mtu_discovery after the test. + ValueRestore<bool> old_flag(&FLAGS_quic_do_path_mtu_discovery, true); + + const QuicPacketCount number_of_packets = kPacketsBetweenMtuProbesBase * 2; + for (QuicPacketCount i = 0; i < number_of_packets; i++) { + SendStreamDataToPeer(3, ".", i, /*fin=*/false, nullptr); + EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + } +} + +// Tests whether MTU discovery works when the probe gets acknowledged on the +// first try. +TEST_P(QuicConnectionTest, MtuDiscoveryEnabled) { + EXPECT_TRUE(connection_.connected()); + + // Restore the current value FLAGS_quic_do_path_mtu_discovery after the test. + ValueRestore<bool> old_flag(&FLAGS_quic_do_path_mtu_discovery, true); + connection_.EnablePathMtuDiscovery(send_algorithm_); + + // Send enough packets so that the next one triggers path MTU discovery. + for (QuicPacketCount i = 0; i < kPacketsBetweenMtuProbesBase - 1; i++) { + SendStreamDataToPeer(3, ".", i, /*fin=*/false, nullptr); + ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + } + + // Trigger the probe. + SendStreamDataToPeer(3, "!", kPacketsBetweenMtuProbesBase, + /*fin=*/false, nullptr); + ASSERT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + QuicByteCount probe_size; + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(DoAll(SaveArg<3>(&probe_size), Return(true))); + connection_.GetMtuDiscoveryAlarm()->Fire(); + EXPECT_EQ(kMtuDiscoveryTargetPacketSizeHigh, probe_size); + + const QuicPacketCount probe_sequence_number = + kPacketsBetweenMtuProbesBase + 1; + ASSERT_EQ(probe_sequence_number, creator_->sequence_number()); + + // Acknowledge all packets sent so far. + QuicAckFrame probe_ack = InitAckFrame(probe_sequence_number); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); + ProcessAckPacket(&probe_ack); + EXPECT_EQ(kMtuDiscoveryTargetPacketSizeHigh, connection_.max_packet_length()); + + // Send more packets, and ensure that none of them sets the alarm. + for (QuicPacketCount i = 0; i < 4 * kPacketsBetweenMtuProbesBase; i++) { + SendStreamDataToPeer(3, ".", i, /*fin=*/false, nullptr); + ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + } +} + +// Tests whether MTU discovery works correctly when the probes never get +// acknowledged. +TEST_P(QuicConnectionTest, MtuDiscoveryFailed) { + EXPECT_TRUE(connection_.connected()); + + // Restore the current value FLAGS_quic_do_path_mtu_discovery after the test. + ValueRestore<bool> old_flag(&FLAGS_quic_do_path_mtu_discovery, true); + connection_.EnablePathMtuDiscovery(send_algorithm_); + + const QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100); + + // This tests sends more packets than strictly necessary to make sure that if + // the connection was to send more discovery packets than needed, those would + // get caught as well. + const QuicPacketCount number_of_packets = + kPacketsBetweenMtuProbesBase * (1 << (kMtuDiscoveryAttempts + 1)); + vector<QuicPacketSequenceNumber> mtu_discovery_packets; + // Called by the first ack. + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + // Called on many acks. + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)) + .Times(AnyNumber()); + for (QuicPacketCount i = 0; i < number_of_packets; i++) { + SendStreamDataToPeer(3, "!", i, /*fin=*/false, nullptr); + clock_.AdvanceTime(rtt); + + // Receive an ACK, which marks all data packets as received, and all MTU + // discovery packets as missing. + QuicAckFrame ack = InitAckFrame(creator_->sequence_number()); + ack.missing_packets = SequenceNumberSet(mtu_discovery_packets.begin(), + mtu_discovery_packets.end()); + ProcessAckPacket(&ack); + + // Trigger MTU probe if it would be scheduled now. + if (!connection_.GetMtuDiscoveryAlarm()->IsSet()) { + continue; + } + + // Fire the alarm. The alarm should cause a packet to be sent. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .WillOnce(Return(true)); + connection_.GetMtuDiscoveryAlarm()->Fire(); + // Record the sequence number of the MTU discovery packet in order to + // mark it as NACK'd. + mtu_discovery_packets.push_back(creator_->sequence_number()); + } + + // Ensure the number of packets between probes grows exponentially by checking + // it against the closed-form expression for the sequence number. + ASSERT_EQ(kMtuDiscoveryAttempts, mtu_discovery_packets.size()); + for (QuicPacketSequenceNumber i = 0; i < kMtuDiscoveryAttempts; i++) { + // 2^0 + 2^1 + 2^2 + ... + 2^n = 2^(n + 1) - 1 + const QuicPacketCount packets_between_probes = + kPacketsBetweenMtuProbesBase * ((1 << (i + 1)) - 1); + EXPECT_EQ(packets_between_probes + (i + 1), mtu_discovery_packets[i]); + } + + EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + EXPECT_EQ(kDefaultMaxPacketSize, connection_.max_packet_length()); +} + +TEST_P(QuicConnectionTest, NoMtuDiscoveryAfterConnectionClosed) { + EXPECT_TRUE(connection_.connected()); + + // Restore the current value FLAGS_quic_do_path_mtu_discovery after the test. + ValueRestore<bool> old_flag(&FLAGS_quic_do_path_mtu_discovery, true); + connection_.EnablePathMtuDiscovery(send_algorithm_); + + // Send enough packets so that the next one triggers path MTU discovery. + for (QuicPacketCount i = 0; i < kPacketsBetweenMtuProbesBase - 1; i++) { + SendStreamDataToPeer(3, ".", i, /*fin=*/false, nullptr); + ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + } + + SendStreamDataToPeer(3, "!", kPacketsBetweenMtuProbesBase, + /*fin=*/false, nullptr); + EXPECT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet()); + + EXPECT_CALL(visitor_, OnConnectionClosed(_, _)); + connection_.CloseConnection(QUIC_INTERNAL_ERROR, /*from_peer=*/false); + EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet()); +} + TEST_P(QuicConnectionTest, TimeoutAfterSend) { EXPECT_TRUE(connection_.connected()); EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); @@ -3254,7 +3547,7 @@ TEST_P(QuicConnectionTest, TestQueueLimitsOnSendStreamData) { connection_.version(), kIncludeVersion, PACKET_8BYTE_CONNECTION_ID, PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP, &payload_length); - creator_->SetMaxPacketLength(length); + connection_.set_max_packet_length(length); // Queue the first packet. EXPECT_CALL(*send_algorithm_, @@ -3278,7 +3571,7 @@ TEST_P(QuicConnectionTest, LoopThroughSendingPackets) { connection_.version(), kIncludeVersion, PACKET_8BYTE_CONNECTION_ID, PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP, &payload_length); - creator_->SetMaxPacketLength(length); + connection_.set_max_packet_length(length); // Queue the first packet. EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(7); @@ -3342,8 +3635,7 @@ TEST_P(QuicConnectionTest, SendDelayedAck) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); const uint8 tag = 0x07; - connection_.SetDecrypter(new StrictTaggingDecrypter(tag), - ENCRYPTION_INITIAL); + connection_.SetDecrypter(ENCRYPTION_INITIAL, new StrictTaggingDecrypter(tag)); framer_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag)); // Process a packet from the non-crypto stream. frame1_.stream_id = 3; @@ -3712,7 +4004,7 @@ TEST_P(QuicConnectionTest, ServerSendsVersionNegotiationPacket) { frames.push_back(QuicFrame(&frame1_)); scoped_ptr<QuicPacket> packet(ConstructPacket(header, frames)); char buffer[kMaxPacketSize]; - scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket( + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPayload( ENCRYPTION_NONE, 12, *packet, buffer, kMaxPacketSize)); framer_.set_version(version()); @@ -3745,7 +4037,7 @@ TEST_P(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlocked) { frames.push_back(QuicFrame(&frame1_)); scoped_ptr<QuicPacket> packet(ConstructPacket(header, frames)); char buffer[kMaxPacketSize]; - scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket( + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPayload( ENCRYPTION_NONE, 12, *packet, buffer, kMaxPacketSize)); framer_.set_version(version()); @@ -3785,7 +4077,7 @@ TEST_P(QuicConnectionTest, frames.push_back(QuicFrame(&frame1_)); scoped_ptr<QuicPacket> packet(ConstructPacket(header, frames)); char buffer[kMaxPacketSize]; - scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket( + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPayload( ENCRYPTION_NONE, 12, *packet, buffer, kMaxPacketSize)); framer_.set_version(version()); @@ -3825,8 +4117,8 @@ TEST_P(QuicConnectionTest, ClientHandlesVersionNegotiation) { frames.push_back(QuicFrame(&frame1_)); scoped_ptr<QuicPacket> packet(ConstructPacket(header, frames)); char buffer[kMaxPacketSize]; - encrypted.reset(framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet, buffer, - kMaxPacketSize)); + encrypted.reset(framer_.EncryptPayload(ENCRYPTION_NONE, 12, *packet, buffer, + kMaxPacketSize)); EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); @@ -3883,7 +4175,7 @@ TEST_P(QuicConnectionTest, CheckSendStats) { EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _)) .WillOnce(Return(lost_packets)); EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); - EXPECT_CALL(visitor_, OnCanWrite()).Times(2); + EXPECT_CALL(visitor_, OnCanWrite()); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessAckPacket(&nack_three); @@ -3960,7 +4252,7 @@ TEST_P(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) { scoped_ptr<QuicPacket> packet(ConstructPacket(header, frames)); EXPECT_TRUE(nullptr != packet.get()); char buffer[kMaxPacketSize]; - scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket( + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPayload( ENCRYPTION_NONE, 1, *packet, buffer, kMaxPacketSize)); EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, true)); @@ -4353,6 +4645,27 @@ TEST_P(QuicConnectionTest, NoDataNoFin) { "Attempt to send empty stream frame"); } +TEST_P(QuicConnectionTest, FecSendPolicyReceivedConnectionOption) { + // Test sending SetReceivedConnectionOptions when FEC send policy is + // FEC_ANY_TRIGGER. + if (GetParam().fec_send_policy == FEC_ALARM_TRIGGER) { + return; + } + ValueRestore<bool> old_flag(&FLAGS_quic_send_fec_packet_only_on_fec_alarm, + true); + connection_.set_perspective(Perspective::IS_SERVER); + + // Test ReceivedConnectionOptions. + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + QuicTagVector copt; + copt.push_back(kFSPA); + QuicConfigPeer::SetReceivedConnectionOptions(&config, copt); + EXPECT_EQ(FEC_ANY_TRIGGER, generator_->fec_send_policy()); + connection_.SetFromConfig(config); + EXPECT_EQ(FEC_ALARM_TRIGGER, generator_->fec_send_policy()); +} + } // 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 0895700912f..84c994c18fa 100644 --- a/chromium/net/quic/quic_crypto_client_stream.cc +++ b/chromium/net/quic/quic_crypto_client_stream.cc @@ -4,12 +4,13 @@ #include "net/quic/quic_crypto_client_stream.h" -#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros.h" #include "base/profiler/scoped_tracker.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/quic_client_session_base.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_session.h" @@ -301,6 +302,15 @@ void QuicCryptoClientStream::DoSendCHLO( return; } + // If the server nonce is empty, copy over the server nonce from a previous + // SREJ, if there is one. + if (FLAGS_enable_quic_stateless_reject_support && + crypto_negotiated_params_.server_nonce.empty() && + cached->has_server_nonce()) { + crypto_negotiated_params_.server_nonce = cached->GetNextServerNonce(); + DCHECK(!crypto_negotiated_params_.server_nonce.empty()); + } + string error_details; QuicErrorCode error = crypto_config_->FillClientHello( server_id_, @@ -330,8 +340,8 @@ void QuicCryptoClientStream::DoSendCHLO( SendHandshakeMessage(out); // Be prepared to decrypt with the new server write key. session()->connection()->SetAlternativeDecrypter( - crypto_negotiated_params_.initial_crypters.decrypter.release(), ENCRYPTION_INITIAL, + crypto_negotiated_params_.initial_crypters.decrypter.release(), true /* latch once used */); // Send subsequent packets under encryption on the assumption that the // server will accept the handshake. @@ -563,7 +573,7 @@ void QuicCryptoClientStream::DoReceiveSHLO( // with the FORWARD_SECURE key until it receives a FORWARD_SECURE // packet from the client. session()->connection()->SetAlternativeDecrypter( - crypters->decrypter.release(), ENCRYPTION_FORWARD_SECURE, + ENCRYPTION_FORWARD_SECURE, crypters->decrypter.release(), false /* don't latch */); session()->connection()->SetEncrypter( ENCRYPTION_FORWARD_SECURE, crypters->encrypter.release()); diff --git a/chromium/net/quic/quic_crypto_client_stream_test.cc b/chromium/net/quic/quic_crypto_client_stream_test.cc index de3d68e251d..72bf40f4a2f 100644 --- a/chromium/net/quic/quic_crypto_client_stream_test.cc +++ b/chromium/net/quic/quic_crypto_client_stream_test.cc @@ -30,21 +30,22 @@ const uint16 kServerPort = 80; class QuicCryptoClientStreamTest : public ::testing::Test { public: QuicCryptoClientStreamTest() - : connection_(new PacketSavingConnection(Perspective::IS_CLIENT)), - session_(new TestClientSession(connection_, DefaultQuicConfig())), - server_id_(kServerHostname, kServerPort, false, PRIVACY_MODE_DISABLED), - stream_(new QuicCryptoClientStream(server_id_, - session_.get(), - nullptr, - &crypto_config_)) { - session_->SetCryptoStream(stream_.get()); + : server_id_(kServerHostname, kServerPort, false, PRIVACY_MODE_DISABLED) { + CreateConnection(); + } + + void CreateConnection() { + connection_ = new PacketSavingConnection(Perspective::IS_CLIENT); // Advance the time, because timers do not like uninitialized times. connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); + + session_.reset(new TestQuicSpdyClientSession( + connection_, DefaultQuicConfig(), server_id_, &crypto_config_)); } void CompleteCryptoHandshake() { - stream_->CryptoConnect(); - CryptoTestUtils::HandshakeWithFakeServer(connection_, stream_.get()); + stream()->CryptoConnect(); + CryptoTestUtils::HandshakeWithFakeServer(connection_, stream()); } void ConstructHandshakeMessage() { @@ -52,24 +53,25 @@ class QuicCryptoClientStreamTest : public ::testing::Test { message_data_.reset(framer.ConstructHandshakeMessage(message_)); } + QuicCryptoClientStream* stream() { return session_->GetCryptoStream(); } + PacketSavingConnection* connection_; - scoped_ptr<TestClientSession> session_; + scoped_ptr<TestQuicSpdyClientSession> session_; QuicServerId server_id_; - scoped_ptr<QuicCryptoClientStream> stream_; CryptoHandshakeMessage message_; scoped_ptr<QuicData> message_data_; QuicCryptoClientConfig crypto_config_; }; TEST_F(QuicCryptoClientStreamTest, NotInitiallyConected) { - EXPECT_FALSE(stream_->encryption_established()); - EXPECT_FALSE(stream_->handshake_confirmed()); + EXPECT_FALSE(stream()->encryption_established()); + EXPECT_FALSE(stream()->handshake_confirmed()); } TEST_F(QuicCryptoClientStreamTest, ConnectedAfterSHLO) { CompleteCryptoHandshake(); - EXPECT_TRUE(stream_->encryption_established()); - EXPECT_TRUE(stream_->handshake_confirmed()); + EXPECT_TRUE(stream()->encryption_established()); + EXPECT_TRUE(stream()->handshake_confirmed()); } TEST_F(QuicCryptoClientStreamTest, MessageAfterHandshake) { @@ -79,18 +81,18 @@ TEST_F(QuicCryptoClientStreamTest, MessageAfterHandshake) { QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE)); message_.set_tag(kCHLO); ConstructHandshakeMessage(); - stream_->ProcessRawData(message_data_->data(), message_data_->length()); + stream()->ProcessRawData(message_data_->data(), message_data_->length()); } TEST_F(QuicCryptoClientStreamTest, BadMessageType) { - stream_->CryptoConnect(); + stream()->CryptoConnect(); message_.set_tag(kCHLO); ConstructHandshakeMessage(); EXPECT_CALL(*connection_, SendConnectionCloseWithDetails( QUIC_INVALID_CRYPTO_MESSAGE_TYPE, "Expected REJ")); - stream_->ProcessRawData(message_data_->data(), message_data_->length()); + stream()->ProcessRawData(message_data_->data(), message_data_->length()); } TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) { @@ -103,39 +105,35 @@ TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) { config->MaxStreamsPerConnection()); const QuicCryptoNegotiatedParameters& crypto_params( - stream_->crypto_negotiated_params()); + stream()->crypto_negotiated_params()); EXPECT_EQ(crypto_config_.aead[0], crypto_params.aead); EXPECT_EQ(crypto_config_.kexs[0], crypto_params.key_exchange); } TEST_F(QuicCryptoClientStreamTest, InvalidHostname) { - QuicServerId server_id("invalid", 80, false, PRIVACY_MODE_DISABLED); - stream_.reset(new QuicCryptoClientStream(server_id, session_.get(), nullptr, - &crypto_config_)); - session_->SetCryptoStream(stream_.get()); + server_id_ = + QuicServerId("invalid", kServerPort, false, PRIVACY_MODE_DISABLED); + + CreateConnection(); CompleteCryptoHandshake(); - EXPECT_TRUE(stream_->encryption_established()); - EXPECT_TRUE(stream_->handshake_confirmed()); + EXPECT_TRUE(stream()->encryption_established()); + EXPECT_TRUE(stream()->handshake_confirmed()); } TEST_F(QuicCryptoClientStreamTest, ExpiredServerConfig) { // Seed the config with a cached server config. CompleteCryptoHandshake(); - connection_ = new PacketSavingConnection(Perspective::IS_CLIENT); - session_.reset(new TestClientSession(connection_, DefaultQuicConfig())); - stream_.reset(new QuicCryptoClientStream(server_id_, session_.get(), nullptr, - &crypto_config_)); - - session_->SetCryptoStream(stream_.get()); + // Recreate connection with the new config. + CreateConnection(); // Advance time 5 years to ensure that we pass the expiry time of the cached // server config. connection_->AdvanceTime( QuicTime::Delta::FromSeconds(60 * 60 * 24 * 365 * 5)); - stream_->CryptoConnect(); + stream()->CryptoConnect(); // Check that a client hello was sent. ASSERT_EQ(1u, connection_->encrypted_packets_.size()); } @@ -179,7 +177,7 @@ TEST_F(QuicCryptoClientStreamTest, ServerConfigUpdate) { scoped_ptr<QuicData> data( CryptoFramer::ConstructHandshakeMessage(server_config_update)); - stream_->ProcessRawData(data->data(), data->length()); + stream()->ProcessRawData(data->data(), data->length()); // Make sure that the STK and SCFG are cached correctly. EXPECT_EQ("xstk", state->source_address_token()); @@ -197,7 +195,7 @@ TEST_F(QuicCryptoClientStreamTest, ServerConfigUpdateBeforeHandshake) { server_config_update.set_tag(kSCUP); scoped_ptr<QuicData> data( CryptoFramer::ConstructHandshakeMessage(server_config_update)); - stream_->ProcessRawData(data->data(), data->length()); + stream()->ProcessRawData(data->data(), data->length()); } class QuicCryptoClientStreamStatelessTest : public ::testing::Test { @@ -206,55 +204,50 @@ class QuicCryptoClientStreamStatelessTest : public ::testing::Test { : server_crypto_config_(QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance()), server_id_(kServerHostname, kServerPort, false, PRIVACY_MODE_DISABLED) { - TestClientSession* client_session = nullptr; - QuicCryptoClientStream* client_stream = nullptr; - SetupCryptoClientStreamForTest(server_id_, - /* supports_stateless_rejects= */ true, - QuicTime::Delta::FromSeconds(100000), - &client_crypto_config_, &client_connection_, - &client_session, &client_stream); + TestQuicSpdyClientSession* client_session = nullptr; + CreateClientSessionForTest(server_id_, + /* supports_stateless_rejects= */ true, + QuicTime::Delta::FromSeconds(100000), + &client_crypto_config_, &client_connection_, + &client_session); CHECK(client_session); - CHECK(client_stream); client_session_.reset(client_session); - client_stream_.reset(client_stream); + } + + QuicCryptoServerStream* server_stream() { + return server_session_->GetCryptoStream(); } void AdvanceHandshakeWithFakeServer() { - client_stream_->CryptoConnect(); - CryptoTestUtils::AdvanceHandshake(client_connection_, client_stream_.get(), - 0, server_connection_, - server_stream_.get(), 0); + client_session_->GetCryptoStream()->CryptoConnect(); + CryptoTestUtils::AdvanceHandshake(client_connection_, + client_session_->GetCryptoStream(), 0, + server_connection_, server_stream(), 0); } // Initializes the server_stream_ for stateless rejects. void InitializeFakeStatelessRejectServer() { - TestServerSession* server_session = nullptr; - QuicCryptoServerStream* server_stream = nullptr; - SetupCryptoServerStreamForTest(server_id_, - QuicTime::Delta::FromSeconds(100000), - &server_crypto_config_, &server_connection_, - &server_session, &server_stream); + TestQuicSpdyServerSession* server_session = nullptr; + CreateServerSessionForTest(server_id_, QuicTime::Delta::FromSeconds(100000), + &server_crypto_config_, &server_connection_, + &server_session); CHECK(server_session); - CHECK(server_stream); server_session_.reset(server_session); - server_stream_.reset(server_stream); CryptoTestUtils::SetupCryptoServerConfigForTest( server_connection_->clock(), server_connection_->random_generator(), server_session_->config(), &server_crypto_config_); - server_stream_->set_use_stateless_rejects_if_peer_supported(true); + server_stream()->set_use_stateless_rejects_if_peer_supported(true); } // Client crypto stream state PacketSavingConnection* client_connection_; - scoped_ptr<TestClientSession> client_session_; - scoped_ptr<QuicCryptoClientStream> client_stream_; + scoped_ptr<TestQuicSpdyClientSession> client_session_; QuicCryptoClientConfig client_crypto_config_; // Server crypto stream state PacketSavingConnection* server_connection_; - scoped_ptr<TestServerSession> server_session_; + scoped_ptr<TestQuicSpdyServerSession> server_session_; QuicCryptoServerConfig server_crypto_config_; - scoped_ptr<QuicCryptoServerStream> server_stream_; QuicServerId server_id_; }; @@ -271,12 +264,17 @@ TEST_F(QuicCryptoClientStreamStatelessTest, StatelessReject) { InitializeFakeStatelessRejectServer(); AdvanceHandshakeWithFakeServer(); - EXPECT_FALSE(client_stream_->encryption_established()); - EXPECT_FALSE(client_stream_->handshake_confirmed()); + EXPECT_EQ(1, server_stream()->num_handshake_messages()); + EXPECT_EQ(0, server_stream()->num_handshake_messages_with_server_nonces()); + + EXPECT_FALSE(client_session_->GetCryptoStream()->encryption_established()); + EXPECT_FALSE(client_session_->GetCryptoStream()->handshake_confirmed()); // Even though the handshake was not complete, the cached client_state is // complete, and can be used for a subsequent successful handshake. EXPECT_TRUE(client_state->IsComplete(QuicWallTime::FromUNIXSeconds(0))); + ASSERT_TRUE(client_state->has_server_nonce()); + ASSERT_FALSE(client_state->GetNextServerNonce().empty()); ASSERT_TRUE(client_state->has_server_designated_connection_id()); QuicConnectionId server_designated_id = client_state->GetNextServerDesignatedConnectionId(); diff --git a/chromium/net/quic/quic_crypto_server_stream.cc b/chromium/net/quic/quic_crypto_server_stream.cc index ec243c6f19f..dd22a8e450d 100644 --- a/chromium/net/quic/quic_crypto_server_stream.cc +++ b/chromium/net/quic/quic_crypto_server_stream.cc @@ -34,6 +34,7 @@ QuicCryptoServerStream::QuicCryptoServerStream( crypto_config_(crypto_config), validate_client_hello_cb_(nullptr), num_handshake_messages_(0), + num_handshake_messages_with_server_nonces_(0), num_server_config_update_messages_sent_(0), use_stateless_rejects_if_peer_supported_(false), peer_supports_stateless_rejects_(false) { @@ -134,8 +135,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(), - ENCRYPTION_INITIAL); + ENCRYPTION_INITIAL, + crypto_negotiated_params_.initial_crypters.decrypter.release()); // We want to be notified when the SHLO is ACKed so that we can disable // HANDSHAKE_MODE in the sent packet manager. @@ -147,8 +148,9 @@ void QuicCryptoServerStream::FinishProcessingHandshakeMessage( ENCRYPTION_FORWARD_SECURE, crypto_negotiated_params_.forward_secure_crypters.encrypter.release()); session()->connection()->SetAlternativeDecrypter( + ENCRYPTION_FORWARD_SECURE, crypto_negotiated_params_.forward_secure_crypters.decrypter.release(), - ENCRYPTION_FORWARD_SECURE, false /* don't latch */); + false /* don't latch */); encryption_established_ = true; handshake_confirmed_ = true; @@ -227,6 +229,9 @@ QuicErrorCode QuicCryptoServerStream::ProcessClientHello( const ValidateClientHelloResultCallback::Result& result, CryptoHandshakeMessage* reply, string* error_details) { + if (!result.info.server_nonce.empty()) { + ++num_handshake_messages_with_server_nonces_; + } // Store the bandwidth estimate from the client. if (result.cached_network_params.bandwidth_estimate_bytes_per_second() > 0) { previous_cached_network_params_.reset( diff --git a/chromium/net/quic/quic_crypto_server_stream.h b/chromium/net/quic/quic_crypto_server_stream.h index 27f6f958482..0a1a61a4f7a 100644 --- a/chromium/net/quic/quic_crypto_server_stream.h +++ b/chromium/net/quic/quic_crypto_server_stream.h @@ -68,6 +68,10 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { uint8 num_handshake_messages() const { return num_handshake_messages_; } + uint8 num_handshake_messages_with_server_nonces() const { + return num_handshake_messages_with_server_nonces_; + } + int num_server_config_update_messages_sent() const { return num_server_config_update_messages_sent_; } @@ -167,6 +171,11 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { // Number of handshake messages received by this stream. uint8 num_handshake_messages_; + // Number of handshake messages received by this stream that contain + // server nonces (indicating that this is a non-zero-RTT handshake + // attempt). + uint8 num_handshake_messages_with_server_nonces_; + // Number of server config update (SCUP) messages sent by this stream. int num_server_config_update_messages_sent_; diff --git a/chromium/net/quic/quic_crypto_server_stream_test.cc b/chromium/net/quic/quic_crypto_server_stream_test.cc index 6874612dd1b..aa3828bbf67 100644 --- a/chromium/net/quic/quic_crypto_server_stream_test.cc +++ b/chromium/net/quic/quic_crypto_server_stream_test.cc @@ -95,34 +95,35 @@ class QuicCryptoServerStreamTest : public ::testing::TestWithParam<bool> { // Initializes the crypto server stream state for testing. May be // called multiple times. void InitializeServer() { - TestServerSession* server_session = nullptr; - QuicCryptoServerStream* server_stream = nullptr; - SetupCryptoServerStreamForTest(server_id_, - QuicTime::Delta::FromSeconds(100000), - &server_crypto_config_, &server_connection_, - &server_session, &server_stream); + TestQuicSpdyServerSession* server_session = nullptr; + CreateServerSessionForTest(server_id_, QuicTime::Delta::FromSeconds(100000), + &server_crypto_config_, &server_connection_, + &server_session); CHECK(server_session); - CHECK(server_stream); server_session_.reset(server_session); - server_stream_.reset(server_stream); CryptoTestUtils::SetupCryptoServerConfigForTest( server_connection_->clock(), server_connection_->random_generator(), server_session_->config(), &server_crypto_config_); } + QuicCryptoServerStream* server_stream() { + return server_session_->GetCryptoStream(); + } + + QuicCryptoClientStream* client_stream() { + return client_session_->GetCryptoStream(); + } + // Initializes a fake client, and all its associated state, for // testing. May be called multiple times. void InitializeFakeClient(bool supports_stateless_rejects) { - TestClientSession* client_session = nullptr; - QuicCryptoClientStream* client_stream = nullptr; - SetupCryptoClientStreamForTest(server_id_, supports_stateless_rejects, - QuicTime::Delta::FromSeconds(100000), - &client_crypto_config_, &client_connection_, - &client_session, &client_stream); + TestQuicSpdyClientSession* client_session = nullptr; + CreateClientSessionForTest(server_id_, supports_stateless_rejects, + QuicTime::Delta::FromSeconds(100000), + &client_crypto_config_, &client_connection_, + &client_session); CHECK(client_session); - CHECK(client_stream); client_session_.reset(client_session); - client_stream_.reset(client_stream); } bool AsyncStrikeRegisterVerification() { @@ -136,39 +137,34 @@ class QuicCryptoServerStreamTest : public ::testing::TestWithParam<bool> { int CompleteCryptoHandshake() { CHECK(server_connection_); - CHECK(server_stream_ != nullptr); + CHECK(server_session_ != nullptr); return CryptoTestUtils::HandshakeWithFakeClient( - server_connection_, server_stream_.get(), client_options_); + server_connection_, server_stream(), client_options_); } // Performs a single round of handshake message-exchange between the // client and server. void AdvanceHandshakeWithFakeClient() { CHECK(server_connection_); - CHECK(server_stream_ != nullptr); CHECK(client_session_ != nullptr); - CHECK(client_stream_ != nullptr); EXPECT_CALL(*client_session_, OnProofValid(_)).Times(testing::AnyNumber()); - client_stream_->CryptoConnect(); - CryptoTestUtils::AdvanceHandshake(client_connection_, client_stream_.get(), - 0, server_connection_, - server_stream_.get(), 0); + client_stream()->CryptoConnect(); + CryptoTestUtils::AdvanceHandshake(client_connection_, client_stream(), 0, + server_connection_, server_stream(), 0); } protected: // Server state PacketSavingConnection* server_connection_; - scoped_ptr<TestServerSession> server_session_; + scoped_ptr<TestQuicSpdyServerSession> server_session_; QuicCryptoServerConfig server_crypto_config_; - scoped_ptr<QuicCryptoServerStream> server_stream_; QuicServerId server_id_; // Client state PacketSavingConnection* client_connection_; QuicCryptoClientConfig client_crypto_config_; - scoped_ptr<TestClientSession> client_session_; - scoped_ptr<QuicCryptoClientStream> client_stream_; + scoped_ptr<TestQuicSpdyClientSession> client_session_; CryptoHandshakeMessage message_; scoped_ptr<QuicData> message_data_; @@ -179,13 +175,13 @@ class QuicCryptoServerStreamTest : public ::testing::TestWithParam<bool> { INSTANTIATE_TEST_CASE_P(Tests, QuicCryptoServerStreamTest, testing::Bool()); TEST_P(QuicCryptoServerStreamTest, NotInitiallyConected) { - EXPECT_FALSE(server_stream_->encryption_established()); - EXPECT_FALSE(server_stream_->handshake_confirmed()); + EXPECT_FALSE(server_stream()->encryption_established()); + EXPECT_FALSE(server_stream()->handshake_confirmed()); } TEST_P(QuicCryptoServerStreamTest, NotInitiallySendingStatelessRejects) { - EXPECT_FALSE(server_stream_->use_stateless_rejects_if_peer_supported()); - EXPECT_FALSE(server_stream_->peer_supports_stateless_rejects()); + EXPECT_FALSE(server_stream()->use_stateless_rejects_if_peer_supported()); + EXPECT_FALSE(server_stream()->peer_supports_stateless_rejects()); } TEST_P(QuicCryptoServerStreamTest, ConnectedAfterCHLO) { @@ -194,27 +190,31 @@ TEST_P(QuicCryptoServerStreamTest, ConnectedAfterCHLO) { // * One to get a source-address token and certificates. // * One to complete the handshake. EXPECT_EQ(2, CompleteCryptoHandshake()); - EXPECT_TRUE(server_stream_->encryption_established()); - EXPECT_TRUE(server_stream_->handshake_confirmed()); + EXPECT_TRUE(server_stream()->encryption_established()); + EXPECT_TRUE(server_stream()->handshake_confirmed()); } TEST_P(QuicCryptoServerStreamTest, StatelessRejectAfterCHLO) { ValueRestore<bool> old_flag(&FLAGS_enable_quic_stateless_reject_support, true); - server_stream_->set_use_stateless_rejects_if_peer_supported(true); + server_stream()->set_use_stateless_rejects_if_peer_supported(true); InitializeFakeClient(/* supports_stateless_rejects= */ true); AdvanceHandshakeWithFakeClient(); // Check the server to make the sure the handshake did not succeed. - EXPECT_FALSE(server_stream_->encryption_established()); - EXPECT_FALSE(server_stream_->handshake_confirmed()); + EXPECT_FALSE(server_stream()->encryption_established()); + EXPECT_FALSE(server_stream()->handshake_confirmed()); - // Check the client state to make sure that it received a - // server-designated connection id. + // Check the client state to make sure that it received a server-designated + // connection id. QuicCryptoClientConfig::CachedState* client_state = client_crypto_config_.LookupOrCreate(server_id_); + ASSERT_TRUE(client_state->has_server_nonce()); + ASSERT_FALSE(client_state->GetNextServerNonce().empty()); + ASSERT_FALSE(client_state->has_server_nonce()); + ASSERT_TRUE(client_state->has_server_designated_connection_id()); const QuicConnectionId server_designated_connection_id = client_state->GetNextServerDesignatedConnectionId(); @@ -229,14 +229,16 @@ TEST_P(QuicCryptoServerStreamTest, StatelessRejectAfterCHLO) { TEST_P(QuicCryptoServerStreamTest, ConnectedAfterStatelessHandshake) { ValueRestore<bool> old_flag(&FLAGS_enable_quic_stateless_reject_support, true); - server_stream_->set_use_stateless_rejects_if_peer_supported(true); + server_stream()->set_use_stateless_rejects_if_peer_supported(true); InitializeFakeClient(/* supports_stateless_rejects= */ true); AdvanceHandshakeWithFakeClient(); // On the first round, encryption will not be established. - EXPECT_FALSE(server_stream_->encryption_established()); - EXPECT_FALSE(server_stream_->handshake_confirmed()); + EXPECT_FALSE(server_stream()->encryption_established()); + EXPECT_FALSE(server_stream()->handshake_confirmed()); + EXPECT_EQ(1, server_stream()->num_handshake_messages()); + EXPECT_EQ(0, server_stream()->num_handshake_messages_with_server_nonces()); // Now check the client state. QuicCryptoClientConfig::CachedState* client_state = @@ -255,58 +257,37 @@ TEST_P(QuicCryptoServerStreamTest, ConnectedAfterStatelessHandshake) { // Now create new client and server streams with the existing config // and try the handshake again (0-RTT handshake). InitializeServer(); - server_stream_->set_use_stateless_rejects_if_peer_supported(true); + server_stream()->set_use_stateless_rejects_if_peer_supported(true); InitializeFakeClient(/* supports_stateless_rejects= */ true); - client_stream_->CryptoConnect(); - if (AsyncStrikeRegisterVerification()) { - EXPECT_FALSE(client_stream_->handshake_confirmed()); - EXPECT_FALSE(server_stream_->handshake_confirmed()); + client_stream()->CryptoConnect(); - // Advance the handshake. Expect that the server will be stuck - // waiting for client nonce verification to complete. - pair<size_t, size_t> messages_moved = CryptoTestUtils::AdvanceHandshake( - client_connection_, client_stream_.get(), 0, server_connection_, - server_stream_.get(), 0); - EXPECT_EQ(1u, messages_moved.first); - EXPECT_EQ(0u, messages_moved.second); - EXPECT_EQ(1, strike_register_client_->PendingVerifications()); - EXPECT_FALSE(client_stream_->handshake_confirmed()); - EXPECT_FALSE(server_stream_->handshake_confirmed()); - - // The server handshake completes once the nonce verification completes. - strike_register_client_->RunPendingVerifications(); - EXPECT_FALSE(client_stream_->handshake_confirmed()); - EXPECT_TRUE(server_stream_->handshake_confirmed()); - - messages_moved = CryptoTestUtils::AdvanceHandshake( - client_connection_, client_stream_.get(), messages_moved.first, - server_connection_, server_stream_.get(), messages_moved.second); - EXPECT_EQ(1u, messages_moved.first); - EXPECT_EQ(1u, messages_moved.second); - } else { - AdvanceHandshakeWithFakeClient(); - } + // In the stateless case, the second handshake contains a server-nonce, so the + // AsyncStrikeRegisterVerification() case will still succeed (unlike a 0-RTT + // handshake). + AdvanceHandshakeWithFakeClient(); // On the second round, encryption will be established. - EXPECT_TRUE(server_stream_->encryption_established()); - EXPECT_TRUE(server_stream_->handshake_confirmed()); + EXPECT_TRUE(server_stream()->encryption_established()); + EXPECT_TRUE(server_stream()->handshake_confirmed()); + EXPECT_EQ(2, server_stream()->num_handshake_messages()); + EXPECT_EQ(1, server_stream()->num_handshake_messages_with_server_nonces()); } TEST_P(QuicCryptoServerStreamTest, NoStatelessRejectIfNoClientSupport) { ValueRestore<bool> old_flag(&FLAGS_enable_quic_stateless_reject_support, true); - server_stream_->set_use_stateless_rejects_if_peer_supported(true); + server_stream()->set_use_stateless_rejects_if_peer_supported(true); - // The server is configured to use stateless rejects, but the client - // does not support it. + // The server is configured to use stateless rejects, but the client does not + // support it. InitializeFakeClient(/* supports_stateless_rejects= */ false); AdvanceHandshakeWithFakeClient(); // Check the server to make the sure the handshake did not succeed. - EXPECT_FALSE(server_stream_->encryption_established()); - EXPECT_FALSE(server_stream_->handshake_confirmed()); + EXPECT_FALSE(server_stream()->encryption_established()); + EXPECT_FALSE(server_stream()->handshake_confirmed()); // Check the client state to make sure that it did not receive a // server-designated connection id. @@ -329,42 +310,42 @@ TEST_P(QuicCryptoServerStreamTest, ZeroRTT) { InitializeFakeClient(/* supports_stateless_rejects= */ false); InitializeServer(); - client_stream_->CryptoConnect(); + client_stream()->CryptoConnect(); if (AsyncStrikeRegisterVerification()) { - EXPECT_FALSE(client_stream_->handshake_confirmed()); - EXPECT_FALSE(server_stream_->handshake_confirmed()); + EXPECT_FALSE(client_stream()->handshake_confirmed()); + EXPECT_FALSE(server_stream()->handshake_confirmed()); - // Advance the handshake. Expect that the server will be stuck - // waiting for client nonce verification to complete. + // Advance the handshake. Expect that the server will be stuck waiting for + // client nonce verification to complete. pair<size_t, size_t> messages_moved = CryptoTestUtils::AdvanceHandshake( - client_connection_, client_stream_.get(), 0, server_connection_, - server_stream_.get(), 0); + client_connection_, client_stream(), 0, server_connection_, + server_stream(), 0); EXPECT_EQ(1u, messages_moved.first); EXPECT_EQ(0u, messages_moved.second); EXPECT_EQ(1, strike_register_client_->PendingVerifications()); - EXPECT_FALSE(client_stream_->handshake_confirmed()); - EXPECT_FALSE(server_stream_->handshake_confirmed()); + EXPECT_FALSE(client_stream()->handshake_confirmed()); + EXPECT_FALSE(server_stream()->handshake_confirmed()); // The server handshake completes once the nonce verification completes. strike_register_client_->RunPendingVerifications(); - EXPECT_FALSE(client_stream_->handshake_confirmed()); - EXPECT_TRUE(server_stream_->handshake_confirmed()); + EXPECT_FALSE(client_stream()->handshake_confirmed()); + EXPECT_TRUE(server_stream()->handshake_confirmed()); messages_moved = CryptoTestUtils::AdvanceHandshake( - client_connection_, client_stream_.get(), messages_moved.first, - server_connection_, server_stream_.get(), messages_moved.second); + client_connection_, client_stream(), messages_moved.first, + server_connection_, server_stream(), messages_moved.second); EXPECT_EQ(1u, messages_moved.first); EXPECT_EQ(1u, messages_moved.second); - EXPECT_TRUE(client_stream_->handshake_confirmed()); - EXPECT_TRUE(server_stream_->handshake_confirmed()); + EXPECT_TRUE(client_stream()->handshake_confirmed()); + EXPECT_TRUE(server_stream()->handshake_confirmed()); } else { CryptoTestUtils::CommunicateHandshakeMessages( - client_connection_, client_stream_.get(), server_connection_, - server_stream_.get()); + client_connection_, client_stream(), server_connection_, + server_stream()); } - EXPECT_EQ(1, client_stream_->num_sent_client_hellos()); + EXPECT_EQ(1, client_stream()->num_sent_client_hellos()); } TEST_P(QuicCryptoServerStreamTest, MessageAfterHandshake) { @@ -374,8 +355,8 @@ TEST_P(QuicCryptoServerStreamTest, MessageAfterHandshake) { SendConnectionClose(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE)); message_.set_tag(kCHLO); ConstructHandshakeMessage(); - server_stream_->ProcessRawData(message_data_->data(), - message_data_->length()); + server_stream()->ProcessRawData(message_data_->data(), + message_data_->length()); } TEST_P(QuicCryptoServerStreamTest, BadMessageType) { @@ -383,8 +364,8 @@ TEST_P(QuicCryptoServerStreamTest, BadMessageType) { ConstructHandshakeMessage(); EXPECT_CALL(*server_connection_, SendConnectionClose(QUIC_INVALID_CRYPTO_MESSAGE_TYPE)); - server_stream_->ProcessRawData(message_data_->data(), - message_data_->length()); + server_stream()->ProcessRawData(message_data_->data(), + message_data_->length()); } TEST_P(QuicCryptoServerStreamTest, WithoutCertificates) { @@ -394,34 +375,34 @@ TEST_P(QuicCryptoServerStreamTest, WithoutCertificates) { // Only 2 client hellos need to be sent in the no-certs case: one to get the // source-address token and the second to finish. EXPECT_EQ(2, CompleteCryptoHandshake()); - EXPECT_TRUE(server_stream_->encryption_established()); - EXPECT_TRUE(server_stream_->handshake_confirmed()); + EXPECT_TRUE(server_stream()->encryption_established()); + EXPECT_TRUE(server_stream()->handshake_confirmed()); } TEST_P(QuicCryptoServerStreamTest, ChannelID) { client_options_.channel_id_enabled = true; client_options_.channel_id_source_async = false; // CompleteCryptoHandshake verifies - // server_stream_->crypto_negotiated_params().channel_id is correct. + // server_stream()->crypto_negotiated_params().channel_id is correct. EXPECT_EQ(2, CompleteCryptoHandshake()); - EXPECT_TRUE(server_stream_->encryption_established()); - EXPECT_TRUE(server_stream_->handshake_confirmed()); + EXPECT_TRUE(server_stream()->encryption_established()); + EXPECT_TRUE(server_stream()->handshake_confirmed()); } TEST_P(QuicCryptoServerStreamTest, ChannelIDAsync) { client_options_.channel_id_enabled = true; client_options_.channel_id_source_async = true; // CompleteCryptoHandshake verifies - // server_stream_->crypto_negotiated_params().channel_id is correct. + // server_stream()->crypto_negotiated_params().channel_id is correct. EXPECT_EQ(2, CompleteCryptoHandshake()); - EXPECT_TRUE(server_stream_->encryption_established()); - EXPECT_TRUE(server_stream_->handshake_confirmed()); + EXPECT_TRUE(server_stream()->encryption_established()); + EXPECT_TRUE(server_stream()->handshake_confirmed()); } TEST_P(QuicCryptoServerStreamTest, OnlySendSCUPAfterHandshakeComplete) { // An attempt to send a SCUP before completing handshake should fail. - server_stream_->SendServerConfigUpdate(nullptr); - EXPECT_EQ(0, server_stream_->num_server_config_update_messages_sent()); + server_stream()->SendServerConfigUpdate(nullptr); + EXPECT_EQ(0, server_stream()->num_server_config_update_messages_sent()); } TEST_P(QuicCryptoServerStreamTest, DoesPeerSupportStatelessRejects) { diff --git a/chromium/net/quic/quic_crypto_stream_test.cc b/chromium/net/quic/quic_crypto_stream_test.cc index 8c83e923d17..5d7001923d0 100644 --- a/chromium/net/quic/quic_crypto_stream_test.cc +++ b/chromium/net/quic/quic_crypto_stream_test.cc @@ -62,7 +62,7 @@ class QuicCryptoStreamTest : public ::testing::Test { protected: MockConnection* connection_; - MockSession session_; + MockQuicSpdySession session_; MockQuicCryptoStream stream_; CryptoHandshakeMessage message_; scoped_ptr<QuicData> message_data_; diff --git a/chromium/net/quic/quic_data_stream.cc b/chromium/net/quic/quic_data_stream.cc index a817b5b3571..4b24c990006 100644 --- a/chromium/net/quic/quic_data_stream.cc +++ b/chromium/net/quic/quic_data_stream.cc @@ -5,7 +5,7 @@ #include "net/quic/quic_data_stream.h" #include "base/logging.h" -#include "net/quic/quic_session.h" +#include "net/quic/quic_spdy_session.h" #include "net/quic/quic_utils.h" #include "net/quic/quic_write_blocked_list.h" @@ -28,8 +28,9 @@ QuicPriority kDefaultPriority = 3; } // namespace -QuicDataStream::QuicDataStream(QuicStreamId id, QuicSession* session) - : ReliableQuicStream(id, session), +QuicDataStream::QuicDataStream(QuicStreamId id, QuicSpdySession* spdy_session) + : ReliableQuicStream(id, spdy_session), + spdy_session_(spdy_session), visitor_(nullptr), headers_decompressed_(false), priority_(kDefaultPriority) { @@ -46,7 +47,7 @@ size_t QuicDataStream::WriteHeaders( const SpdyHeaderBlock& header_block, bool fin, QuicAckNotifier::DelegateInterface* ack_notifier_delegate) { - size_t bytes_written = session()->WriteHeaders( + size_t bytes_written = spdy_session_->WriteHeaders( id(), header_block, fin, priority_, ack_notifier_delegate); if (fin) { // TODO(rch): Add test to ensure fin_sent_ is set whenever a fin is sent. @@ -57,42 +58,13 @@ size_t QuicDataStream::WriteHeaders( } 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 - // Readv method. - return sequencer()->Readv(iov, iov_len); - } - // Otherwise, copy decompressed header data into |iov|. - size_t bytes_consumed = 0; - size_t iov_index = 0; - while (iov_index < iov_len && - decompressed_headers_.length() > bytes_consumed) { - size_t bytes_to_read = min(iov[iov_index].iov_len, - decompressed_headers_.length() - bytes_consumed); - char* iov_ptr = static_cast<char*>(iov[iov_index].iov_base); - memcpy(iov_ptr, - decompressed_headers_.data() + bytes_consumed, bytes_to_read); - bytes_consumed += bytes_to_read; - ++iov_index; - } - decompressed_headers_.erase(0, bytes_consumed); - if (FinishedReadingHeaders()) { - sequencer()->FlushBufferedFrames(); - } - return bytes_consumed; + DCHECK(FinishedReadingHeaders()); + return sequencer()->Readv(iov, iov_len); } -int QuicDataStream::GetReadableRegions(iovec* iov, size_t iov_len) { - if (FinishedReadingHeaders()) { - return sequencer()->GetReadableRegions(iov, iov_len); - } - if (iov_len == 0) { - return 0; - } - iov[0].iov_base = static_cast<void*>( - const_cast<char*>(decompressed_headers_.data())); - iov[0].iov_len = decompressed_headers_.length(); - return 1; +int QuicDataStream::GetReadableRegions(iovec* iov, size_t iov_len) const { + DCHECK(FinishedReadingHeaders()); + return sequencer()->GetReadableRegions(iov, iov_len); } bool QuicDataStream::IsDoneReading() const { @@ -106,6 +78,13 @@ bool QuicDataStream::HasBytesToRead() const { return !decompressed_headers_.empty() || sequencer()->HasBytesToRead(); } +void QuicDataStream::MarkHeadersConsumed(size_t bytes_consumed) { + decompressed_headers_.erase(0, bytes_consumed); + if (FinishedReadingHeaders()) { + sequencer()->FlushBufferedFrames(); + } +} + void QuicDataStream::set_priority(QuicPriority priority) { DCHECK_EQ(0u, stream_bytes_written()); priority_ = priority; @@ -123,24 +102,8 @@ uint32 QuicDataStream::ProcessRawData(const char* data, uint32 data_len) { return ProcessData(data, data_len); } -uint32 QuicDataStream::ProcessHeaderData() { - if (decompressed_headers_.empty()) { - return 0; - } - - size_t bytes_processed = ProcessData(decompressed_headers_.data(), - decompressed_headers_.length()); - if (bytes_processed == decompressed_headers_.length()) { - decompressed_headers_.clear(); - } else { - decompressed_headers_ = decompressed_headers_.erase(0, bytes_processed); - } - return bytes_processed; -} - void QuicDataStream::OnStreamHeaders(StringPiece headers_data) { headers_data.AppendToString(&decompressed_headers_); - ProcessHeaderData(); } void QuicDataStream::OnStreamHeadersPriority(QuicPriority priority) { @@ -151,9 +114,8 @@ void QuicDataStream::OnStreamHeadersPriority(QuicPriority priority) { void QuicDataStream::OnStreamHeadersComplete(bool fin, size_t frame_len) { headers_decompressed_ = true; if (fin) { - sequencer()->OnStreamFrame(QuicStreamFrame(id(), fin, 0, IOVector())); + sequencer()->OnStreamFrame(QuicStreamFrame(id(), fin, 0, StringPiece())); } - ProcessHeaderData(); if (FinishedReadingHeaders()) { sequencer()->FlushBufferedFrames(); } @@ -171,7 +133,7 @@ void QuicDataStream::OnClose() { } } -bool QuicDataStream::FinishedReadingHeaders() { +bool QuicDataStream::FinishedReadingHeaders() const { 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 aa4a7c3dc44..60356a142ba 100644 --- a/chromium/net/quic/quic_data_stream.h +++ b/chromium/net/quic/quic_data_stream.h @@ -32,9 +32,9 @@ class QuicDataStreamPeer; class ReliableQuicStreamPeer; } // namespace test -class QuicSession; +class QuicSpdySession; -// All this does right now is send data to subclasses via the sequencer. +// A QUIC data stream that can also send and receive headers. class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream { public: // Visitor receives callbacks from the stream. @@ -52,13 +52,13 @@ class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream { DISALLOW_COPY_AND_ASSIGN(Visitor); }; - QuicDataStream(QuicStreamId id, QuicSession* session); - + QuicDataStream(QuicStreamId id, QuicSpdySession* spdy_session); ~QuicDataStream() override; // ReliableQuicStream implementation void OnClose() override; 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 @@ -91,19 +91,26 @@ class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream { bool fin, QuicAckNotifier::DelegateInterface* ack_notifier_delegate); + // Marks |bytes_consumed| of the headers data as consumed. + void MarkHeadersConsumed(size_t bytes_consumed); + // 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. virtual size_t Readv(const struct iovec* iov, size_t iov_len); - virtual int GetReadableRegions(iovec* iov, size_t iov_len); + virtual int GetReadableRegions(iovec* iov, size_t iov_len) const; // Returns true when all data has been read from the peer, including the fin. - virtual bool IsDoneReading() const; - virtual bool HasBytesToRead() const; + bool IsDoneReading() const; + bool HasBytesToRead() const; void set_visitor(Visitor* visitor) { visitor_ = visitor; } bool headers_decompressed() const { return headers_decompressed_; } + const std::string& decompressed_headers() const { + return decompressed_headers_; + } + protected: // Sets priority_ to priority. This should only be called before bytes are // written to the server. @@ -112,17 +119,17 @@ class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream { // instead. QuicPriority priority() const { return priority_; } + bool FinishedReadingHeaders() const; + private: friend class test::QuicDataStreamPeer; friend class test::ReliableQuicStreamPeer; friend class QuicStreamUtils; - uint32 ProcessHeaderData(); - - bool FinishedReadingHeaders(); + QuicSpdySession* spdy_session_; Visitor* visitor_; - // True if the headers have been completely decompresssed. + // True if the headers have been completely decompressed. bool headers_decompressed_; // The priority of the stream, once parsed. QuicPriority priority_; diff --git a/chromium/net/quic/quic_data_stream_test.cc b/chromium/net/quic/quic_data_stream_test.cc index 55fab6be427..e063367bc81 100644 --- a/chromium/net/quic/quic_data_stream_test.cc +++ b/chromium/net/quic/quic_data_stream_test.cc @@ -35,7 +35,7 @@ const bool kShouldProcessData = true; class TestStream : public QuicDataStream { public: TestStream(QuicStreamId id, - QuicSession* session, + QuicSpdySession* session, bool should_process_data) : QuicDataStream(id, session), should_process_data_(should_process_data) {} @@ -93,7 +93,7 @@ class QuicDataStreamTest : public ::testing::TestWithParam<QuicVersion> { void Initialize(bool stream_should_process_data) { connection_ = new testing::StrictMock<MockConnection>( Perspective::IS_SERVER, SupportedVersions(GetParam())); - session_.reset(new testing::StrictMock<MockSession>(connection_)); + session_.reset(new testing::StrictMock<MockQuicSpdySession>(connection_)); stream_.reset(new TestStream(kClientDataStreamId1, session_.get(), stream_should_process_data)); stream2_.reset(new TestStream(kClientDataStreamId2, session_.get(), @@ -104,7 +104,7 @@ class QuicDataStreamTest : public ::testing::TestWithParam<QuicVersion> { protected: MockConnection* connection_; - scoped_ptr<MockSession> session_; + scoped_ptr<MockQuicSpdySession> session_; scoped_ptr<TestStream> stream_; scoped_ptr<TestStream> stream2_; SpdyHeaderBlock headers_; @@ -121,13 +121,34 @@ TEST_P(QuicDataStreamTest, ProcessHeaders) { SpdyUtils::SerializeUncompressedHeaders(headers_, GetParam()); stream_->OnStreamHeadersPriority(QuicUtils::HighestPriority()); stream_->OnStreamHeaders(headers); - EXPECT_EQ(headers, stream_->data()); + EXPECT_EQ("", stream_->data()); + EXPECT_EQ(headers, stream_->decompressed_headers()); stream_->OnStreamHeadersComplete(false, headers.size()); EXPECT_EQ(QuicUtils::HighestPriority(), stream_->EffectivePriority()); - EXPECT_EQ(headers, stream_->data()); + EXPECT_EQ("", stream_->data()); + EXPECT_EQ(headers, stream_->decompressed_headers()); EXPECT_FALSE(stream_->IsDoneReading()); } +TEST_P(QuicDataStreamTest, MarkHeadersConsumed) { + Initialize(kShouldProcessData); + + string headers = + SpdyUtils::SerializeUncompressedHeaders(headers_, GetParam()); + string body = "this is the body"; + + stream_->OnStreamHeaders(headers); + stream_->OnStreamHeadersComplete(false, headers.size()); + EXPECT_EQ(headers, stream_->decompressed_headers()); + + headers.erase(0, 10); + stream_->MarkHeadersConsumed(10); + EXPECT_EQ(headers, stream_->decompressed_headers()); + + stream_->MarkHeadersConsumed(headers.length()); + EXPECT_EQ("", stream_->decompressed_headers()); +} + TEST_P(QuicDataStreamTest, ProcessHeadersAndBody) { Initialize(kShouldProcessData); @@ -136,12 +157,15 @@ TEST_P(QuicDataStreamTest, ProcessHeadersAndBody) { string body = "this is the body"; stream_->OnStreamHeaders(headers); - EXPECT_EQ(headers, stream_->data()); + EXPECT_EQ("", stream_->data()); + EXPECT_EQ(headers, stream_->decompressed_headers()); stream_->OnStreamHeadersComplete(false, headers.size()); - QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body)); + EXPECT_EQ(headers, stream_->decompressed_headers()); + stream_->MarkHeadersConsumed(headers.length()); + QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); stream_->OnStreamFrame(frame); - - EXPECT_EQ(headers + body, stream_->data()); + EXPECT_EQ("", stream_->decompressed_headers()); + EXPECT_EQ(body, stream_->data()); } TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyFragments) { @@ -160,16 +184,18 @@ TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyFragments) { stream_->OnStreamHeaders(fragment); } stream_->OnStreamHeadersComplete(false, headers.size()); + ASSERT_EQ(headers, stream_->decompressed_headers()) + << "fragment_size: " << fragment_size; + stream_->MarkHeadersConsumed(headers.length()); 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)); + StringPiece(fragment)); stream_->OnStreamFrame(frame); } - ASSERT_EQ(headers + body, - stream_->data()) << "fragment_size: " << fragment_size; + ASSERT_EQ(body, stream_->data()) << "fragment_size: " << fragment_size; } } @@ -187,20 +213,22 @@ TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyFragmentsSplit) { headers.size() - split_point); stream_->OnStreamHeaders(headers2); stream_->OnStreamHeadersComplete(false, headers.size()); + ASSERT_EQ(headers, stream_->decompressed_headers()) + << "split_point: " << split_point; + stream_->MarkHeadersConsumed(headers.length()); StringPiece fragment1(body.data(), split_point); QuicStreamFrame frame1(kClientDataStreamId1, false, 0, - MakeIOVector(fragment1)); + StringPiece(fragment1)); stream_->OnStreamFrame(frame1); StringPiece fragment2(body.data() + split_point, body.size() - split_point); QuicStreamFrame frame2(kClientDataStreamId1, false, split_point, - MakeIOVector(fragment2)); + StringPiece(fragment2)); stream_->OnStreamFrame(frame2); - ASSERT_EQ(headers + body, - stream_->data()) << "split_point: " << split_point; + ASSERT_EQ(body, stream_->data()) << "split_point: " << split_point; } } @@ -212,22 +240,18 @@ TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyReadv) { string body = "this is the body"; stream_->OnStreamHeaders(headers); - EXPECT_EQ(headers, stream_->data()); stream_->OnStreamHeadersComplete(false, headers.size()); - QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body)); + QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); stream_->OnStreamFrame(frame); + stream_->MarkHeadersConsumed(headers.length()); char buffer[2048]; - ASSERT_LT(headers.length() + body.length(), arraysize(buffer)); + ASSERT_LT(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(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)); } @@ -239,21 +263,20 @@ TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyIncrementalReadv) { SpdyUtils::SerializeUncompressedHeaders(headers_, GetParam()); string body = "this is the body"; stream_->OnStreamHeaders(headers); - EXPECT_EQ(headers, stream_->data()); stream_->OnStreamHeadersComplete(false, headers.size()); - QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body)); + QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); stream_->OnStreamFrame(frame); + stream_->MarkHeadersConsumed(headers.length()); char buffer[1]; struct iovec vec; vec.iov_base = buffer; vec.iov_len = arraysize(buffer); - string data = headers + body; - for (size_t i = 0; i < data.length(); ++i) { + for (size_t i = 0; i < body.length(); ++i) { size_t bytes_read = stream_->Readv(&vec, 1); ASSERT_EQ(1u, bytes_read); - EXPECT_EQ(data.data()[i], buffer[0]); + EXPECT_EQ(body.data()[i], buffer[0]); } } @@ -264,10 +287,10 @@ TEST_P(QuicDataStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) { SpdyUtils::SerializeUncompressedHeaders(headers_, GetParam()); string body = "this is the body"; stream_->OnStreamHeaders(headers); - EXPECT_EQ(headers, stream_->data()); stream_->OnStreamHeadersComplete(false, headers.size()); - QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body)); + QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); stream_->OnStreamFrame(frame); + stream_->MarkHeadersConsumed(headers.length()); char buffer1[1]; char buffer2[1]; @@ -276,12 +299,12 @@ TEST_P(QuicDataStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) { vec[0].iov_len = arraysize(buffer1); vec[1].iov_base = buffer2; vec[1].iov_len = arraysize(buffer2); - string data = headers + body; - for (size_t i = 0; i < data.length(); i += 2) { + + for (size_t i = 0; i < body.length(); i += 2) { size_t bytes_read = stream_->Readv(vec, 2); ASSERT_EQ(2u, bytes_read) << i; - ASSERT_EQ(data.data()[i], buffer1[0]) << i; - ASSERT_EQ(data.data()[i + 1], buffer2[0]) << i; + ASSERT_EQ(body.data()[i], buffer1[0]) << i; + ASSERT_EQ(body.data()[i + 1], buffer2[0]) << i; } } @@ -345,10 +368,9 @@ TEST_P(QuicDataStreamTest, StreamFlowControlNoWindowUpdateIfNotConsumed) { 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)); + QuicStreamFrame frame1(kClientDataStreamId1, false, 0, StringPiece(body)); stream_->OnStreamFrame(frame1); EXPECT_EQ(kWindow - (kWindow / 3), QuicFlowControllerPeer::ReceiveWindowSize( stream_->flow_controller())); @@ -357,7 +379,7 @@ TEST_P(QuicDataStreamTest, StreamFlowControlNoWindowUpdateIfNotConsumed) { // half full. This should all be buffered, decreasing the receive window but // not sending WINDOW_UPDATE. QuicStreamFrame frame2(kClientDataStreamId1, false, kWindow / 3, - MakeIOVector(body)); + StringPiece(body)); stream_->OnStreamFrame(frame2); EXPECT_EQ( kWindow - (2 * kWindow / 3), @@ -385,10 +407,10 @@ TEST_P(QuicDataStreamTest, StreamFlowControlWindowUpdate) { string body; GenerateBody(&body, kWindow / 3); stream_->OnStreamHeaders(headers); - EXPECT_EQ(headers, stream_->data()); stream_->OnStreamHeadersComplete(false, headers.size()); + stream_->MarkHeadersConsumed(headers.length()); - QuicStreamFrame frame1(kClientDataStreamId1, false, 0, MakeIOVector(body)); + QuicStreamFrame frame1(kClientDataStreamId1, false, 0, StringPiece(body)); stream_->OnStreamFrame(frame1); EXPECT_EQ(kWindow - (kWindow / 3), QuicFlowControllerPeer::ReceiveWindowSize( stream_->flow_controller())); @@ -398,7 +420,7 @@ TEST_P(QuicDataStreamTest, StreamFlowControlWindowUpdate) { // 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)); + StringPiece(body)); EXPECT_CALL(*connection_, SendWindowUpdate(kClientDataStreamId1, QuicFlowControllerPeer::ReceiveWindowOffset( @@ -435,16 +457,18 @@ TEST_P(QuicDataStreamTest, ConnectionFlowControlWindowUpdate) { SpdyUtils::SerializeUncompressedHeaders(headers_, GetParam()); stream_->OnStreamHeaders(headers); stream_->OnStreamHeadersComplete(false, headers.size()); + stream_->MarkHeadersConsumed(headers.length()); stream2_->OnStreamHeaders(headers); stream2_->OnStreamHeadersComplete(false, headers.size()); + stream2_->MarkHeadersConsumed(headers.length()); // 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)); + QuicStreamFrame frame1(kClientDataStreamId1, false, 0, StringPiece(body)); stream_->OnStreamFrame(frame1); - QuicStreamFrame frame2(kClientDataStreamId2, false, 0, MakeIOVector(body)); + QuicStreamFrame frame2(kClientDataStreamId2, false, 0, StringPiece(body)); stream2_->OnStreamFrame(frame2); // Now receive a further single byte on one stream - again this does not @@ -457,7 +481,7 @@ TEST_P(QuicDataStreamTest, ConnectionFlowControlWindowUpdate) { session_->flow_controller()) + 1 + kWindow / 2)); QuicStreamFrame frame3(kClientDataStreamId1, false, (kWindow / 4), - MakeIOVector("a")); + StringPiece("a")); stream_->OnStreamFrame(frame3); } @@ -477,13 +501,12 @@ TEST_P(QuicDataStreamTest, StreamFlowControlViolation) { string headers = SpdyUtils::SerializeUncompressedHeaders(headers_, GetParam()); 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)); + QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); EXPECT_CALL(*connection_, SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA)); stream_->OnStreamFrame(frame); @@ -509,14 +532,13 @@ TEST_P(QuicDataStreamTest, ConnectionFlowControlViolation) { string headers = SpdyUtils::SerializeUncompressedHeaders(headers_, GetParam()); 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)); + QuicStreamFrame frame(kClientDataStreamId1, false, 0, StringPiece(body)); EXPECT_CALL(*connection_, SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA)); diff --git a/chromium/net/quic/quic_data_writer.cc b/chromium/net/quic/quic_data_writer.cc index 4ebfab07da5..49257829389 100644 --- a/chromium/net/quic/quic_data_writer.cc +++ b/chromium/net/quic/quic_data_writer.cc @@ -4,6 +4,7 @@ #include "net/quic/quic_data_writer.h" +#include <stdint.h> #include <algorithm> #include <limits> @@ -47,7 +48,7 @@ bool QuicDataWriter::WriteUInt64(uint64 value) { bool QuicDataWriter::WriteUFloat16(uint64 value) { uint16 result; - if (value < (GG_UINT64_C(1) << kUFloat16MantissaEffectiveBits)) { + if (value < (UINT64_C(1) << kUFloat16MantissaEffectiveBits)) { // Fast path: either the value is denormalized, or has exponent zero. // Both cases are represented by the value itself. result = static_cast<uint16>(value); @@ -64,7 +65,7 @@ bool QuicDataWriter::WriteUFloat16(uint64 value) { // Right-shift the value until the highest bit is in position 11. // For offset of 16, 8, 4, 2 and 1 (binary search over 1-30), // shift if the bit is at or above 11 + offset. - if (value >= (GG_UINT64_C(1) << (kUFloat16MantissaBits + offset))) { + if (value >= (UINT64_C(1) << (kUFloat16MantissaBits + offset))) { exponent += offset; value >>= offset; } @@ -72,8 +73,8 @@ bool QuicDataWriter::WriteUFloat16(uint64 value) { DCHECK_GE(exponent, 1); DCHECK_LE(exponent, kUFloat16MaxExponent); - DCHECK_GE(value, GG_UINT64_C(1) << kUFloat16MantissaBits); - DCHECK_LT(value, GG_UINT64_C(1) << kUFloat16MantissaEffectiveBits); + DCHECK_GE(value, UINT64_C(1) << kUFloat16MantissaBits); + DCHECK_LT(value, UINT64_C(1) << kUFloat16MantissaEffectiveBits); // Hidden bit (position 11) is set. We should remove it and increment the // exponent. Equivalently, we just add it to the exponent. @@ -94,18 +95,6 @@ bool QuicDataWriter::WriteStringPiece16(StringPiece val) { return WriteBytes(val.data(), val.size()); } -bool QuicDataWriter::WriteIOVector(const IOVector& data) { - char *dest = BeginWrite(data.TotalBufferSize()); - if (!dest) { - return false; - } - for (size_t i = 0; i < data.Size(); ++i) { - WriteBytes(data.iovec()[i].iov_base, data.iovec()[i].iov_len); - } - - return true; -} - char* QuicDataWriter::BeginWrite(size_t length) { if (length_ > capacity_) { return nullptr; diff --git a/chromium/net/quic/quic_data_writer.h b/chromium/net/quic/quic_data_writer.h index 762c3aaee49..0f1df204654 100644 --- a/chromium/net/quic/quic_data_writer.h +++ b/chromium/net/quic/quic_data_writer.h @@ -10,7 +10,6 @@ #include "base/basictypes.h" #include "base/logging.h" -#include "base/port.h" #include "base/strings/string_piece.h" #include "net/base/int128.h" #include "net/base/net_export.h" @@ -49,7 +48,6 @@ class NET_EXPORT_PRIVATE QuicDataWriter { // not be represented directly are rounded down. bool WriteUFloat16(uint64 value); bool WriteStringPiece16(base::StringPiece val); - bool WriteIOVector(const IOVector& data); bool WriteBytes(const void* data, size_t data_len); bool WriteRepeatedByte(uint8 byte, size_t count); // Fills the remaining buffer with null characters. diff --git a/chromium/net/quic/quic_data_writer_test.cc b/chromium/net/quic/quic_data_writer_test.cc index a7ac2a8ea41..d34e5cef570 100644 --- a/chromium/net/quic/quic_data_writer_test.cc +++ b/chromium/net/quic/quic_data_writer_test.cc @@ -4,6 +4,8 @@ #include "net/quic/quic_data_writer.h" +#include <stdint.h> + #include "base/memory/scoped_ptr.h" #include "net/quic/quic_data_reader.h" #include "net/test/gtest_util.h" @@ -19,7 +21,7 @@ TEST(QuicDataWriterTest, SanityCheckUFloat16Consts) { EXPECT_EQ(30, kUFloat16MaxExponent); EXPECT_EQ(11, kUFloat16MantissaBits); EXPECT_EQ(12, kUFloat16MantissaEffectiveBits); - EXPECT_EQ(GG_UINT64_C(0x3FFC0000000), kUFloat16MaxValue); + EXPECT_EQ(UINT64_C(0x3FFC0000000), kUFloat16MaxValue); } TEST(QuicDataWriterTest, WriteUFloat16) { @@ -118,7 +120,7 @@ TEST(QuicDataWriterTest, RoundTripUFloat16) { if (i > 2000) EXPECT_GT(previous_value * 1005, value * 1000); // Check we're always within the promised range. - EXPECT_LT(value, GG_UINT64_C(0x3FFC0000000)); + EXPECT_LT(value, UINT64_C(0x3FFC0000000)); previous_value = value; char buffer[6]; QuicDataWriter writer(6, buffer); diff --git a/chromium/net/quic/quic_default_packet_writer.cc b/chromium/net/quic/quic_default_packet_writer.cc index aea9e9bf912..2fdb27ac1d9 100644 --- a/chromium/net/quic/quic_default_packet_writer.cc +++ b/chromium/net/quic/quic_default_packet_writer.cc @@ -6,7 +6,7 @@ #include "base/location.h" #include "base/logging.h" -#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros.h" #include "base/metrics/sparse_histogram.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" diff --git a/chromium/net/quic/quic_flags.cc b/chromium/net/quic/quic_flags.cc index cf3b2629363..6c4a082a9ae 100644 --- a/chromium/net/quic/quic_flags.cc +++ b/chromium/net/quic/quic_flags.cc @@ -40,8 +40,24 @@ int64 FLAGS_quic_time_wait_list_seconds = 5; int64 FLAGS_quic_time_wait_list_max_connections = 50000; // Enables server-side support for QUIC stateless rejects. -bool FLAGS_enable_quic_stateless_reject_support = false; +bool FLAGS_enable_quic_stateless_reject_support = true; -// If true, stop processing quic data as soon as the connection is closed rather -// than processing a full packet. -bool FLAGS_quic_stop_early = true; +// If true, flow controller may grow the receive window size if necessary. +bool FLAGS_quic_auto_tune_receive_window = true; + +// Don't ack acks in QUIC, even when there is a recent missing packet. +bool FLAGS_quic_dont_ack_acks = true; + +// Enables sending of FEC packet only when FEC alarm goes off. +bool FLAGS_quic_send_fec_packet_only_on_fec_alarm = true; + +// Change from using IsPacketRemovable to IsPacketUseless in +// QuicUnackedPacketMap. +bool FLAGS_quic_use_is_useless_packet = true; + +// Delay setting QUIC's retransmission alarm until an ack is fully +// processed or a write is complete. +bool FLAGS_quic_delay_retransmission_alarm = true; + +// Enables server-side path MTU discovery in QUIC. +bool FLAGS_quic_do_path_mtu_discovery = true; diff --git a/chromium/net/quic/quic_flags.h b/chromium/net/quic/quic_flags.h index 30bf0ae865e..f54d079ed31 100644 --- a/chromium/net/quic/quic_flags.h +++ b/chromium/net/quic/quic_flags.h @@ -18,6 +18,11 @@ NET_EXPORT_PRIVATE extern bool FLAGS_quic_too_many_outstanding_packets; NET_EXPORT_PRIVATE extern int64 FLAGS_quic_time_wait_list_seconds; NET_EXPORT_PRIVATE extern int64 FLAGS_quic_time_wait_list_max_connections; NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_stateless_reject_support; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_stop_early; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_auto_tune_receive_window; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_dont_ack_acks; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_send_fec_packet_only_on_fec_alarm; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_is_useless_packet; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_delay_retransmission_alarm; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_do_path_mtu_discovery; #endif // NET_QUIC_QUIC_FLAGS_H_ diff --git a/chromium/net/quic/quic_flow_controller.cc b/chromium/net/quic/quic_flow_controller.cc index af79b956434..699f836d20d 100644 --- a/chromium/net/quic/quic_flow_controller.cc +++ b/chromium/net/quic/quic_flow_controller.cc @@ -11,6 +11,11 @@ namespace net { +namespace { +const QuicByteCount kStreamReceiveWindowLimit = 16 * 1024 * 1024; // 16 MB +const QuicByteCount kSessionReceiveWindowLimit = 24 * 1024 * 1024; // 24 MB +} + #define ENDPOINT \ (perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ") @@ -19,22 +24,28 @@ QuicFlowController::QuicFlowController(QuicConnection* connection, Perspective perspective, QuicStreamOffset send_window_offset, QuicStreamOffset receive_window_offset, - QuicByteCount max_receive_window) + bool should_auto_tune_receive_window) : connection_(connection), id_(id), perspective_(perspective), - bytes_consumed_(0), - highest_received_byte_offset_(0), bytes_sent_(0), send_window_offset_(send_window_offset), + bytes_consumed_(0), + highest_received_byte_offset_(0), receive_window_offset_(receive_window_offset), - max_receive_window_(max_receive_window), - last_blocked_send_window_offset_(0) { + receive_window_size_(receive_window_offset), + auto_tune_receive_window_(should_auto_tune_receive_window), + last_blocked_send_window_offset_(0), + prev_window_update_time_(QuicTime::Zero()) { + receive_window_size_limit_ = (id_ == kConnectionLevelId) + ? kSessionReceiveWindowLimit + : kStreamReceiveWindowLimit; + 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_ + << ", max receive window to: " << receive_window_size_ + << ", max receive window limit to: " << receive_window_size_limit_ << ", setting send window offset to: " << send_window_offset_; } @@ -87,28 +98,102 @@ bool QuicFlowController::FlowControlViolation() { return false; } +void QuicFlowController::MaybeIncreaseMaxWindowSize() { + // Core of receive window auto tuning. This method should be called before a + // WINDOW_UPDATE frame is sent. Ideally, window updates should occur close to + // once per RTT. If a window update happens much faster than RTT, it implies + // that the flow control window is imposing a bottleneck. To prevent this, + // this method will increase the receive window size (subject to a reasonable + // upper bound). For simplicity this algorithm is deliberately asymmetric, in + // that it may increase window size but never decreases. + + if (!FLAGS_quic_auto_tune_receive_window) { + return; + } + + // Keep track of timing between successive window updates. + QuicTime now = connection_->clock()->ApproximateNow(); + QuicTime prev = prev_window_update_time_; + prev_window_update_time_ = now; + if (!prev.IsInitialized()) { + DVLOG(1) << ENDPOINT << "first window update for stream " << id_; + return; + } + + if (!auto_tune_receive_window_) { + return; + } + + // Get outbound RTT. + QuicTime::Delta rtt = + connection_->sent_packet_manager().GetRttStats()->smoothed_rtt(); + if (rtt.IsZero()) { + DVLOG(1) << ENDPOINT << "rtt zero for stream " << id_; + return; + } + + // Now we can compare timing of window updates with RTT. + QuicTime::Delta since_last = now.Subtract(prev); + QuicTime::Delta two_rtt = rtt.Multiply(2); + + if (since_last >= two_rtt) { + // If interval between window updates is sufficiently large, there + // is no need to increase receive_window_size_. + return; + } + + QuicByteCount old_window = receive_window_size_; + receive_window_size_ *= 2; + receive_window_size_ = + std::min(receive_window_size_, receive_window_size_limit_); + + if (receive_window_size_ > old_window) { + DVLOG(1) << ENDPOINT << "New max window increase for stream " << id_ + << " after " << since_last.ToMicroseconds() << " us, and RTT is " + << rtt.ToMicroseconds() + << "us. max wndw: " << receive_window_size_; + } else { + // TODO(ckrasic) - add a varz to track this (?). + DVLOG(1) << ENDPOINT << "Max window at limit for stream " << id_ + << " after " << since_last.ToMicroseconds() << " us, and RTT is " + << rtt.ToMicroseconds() + << "us. Limit size: " << receive_window_size_; + } +} + +QuicByteCount QuicFlowController::WindowUpdateThreshold() { + return receive_window_size_ / 2; +} + void QuicFlowController::MaybeSendWindowUpdate() { // 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_); + DCHECK_LE(bytes_consumed_, receive_window_offset_); QuicStreamOffset available_window = receive_window_offset_ - bytes_consumed_; - QuicByteCount threshold = (max_receive_window_ / 2); - - if (available_window < threshold) { - // Update our receive window. - receive_window_offset_ += (max_receive_window_ - available_window); + QuicByteCount threshold = WindowUpdateThreshold(); - DVLOG(1) << ENDPOINT << "Sending WindowUpdate frame for stream " << id_ - << ", consumed bytes: " << bytes_consumed_ + if (available_window >= threshold) { + DVLOG(1) << ENDPOINT << "Not sending WindowUpdate for stream " << id_ << ", available window: " << available_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_); + << ">= threshold: " << threshold; + return; } + + MaybeIncreaseMaxWindowSize(); + + // Update our receive window. + receive_window_offset_ += (receive_window_size_ - available_window); + + DVLOG(1) << ENDPOINT << "Sending WindowUpdate frame for stream " << id_ + << ", consumed bytes: " << bytes_consumed_ + << ", available window: " << available_window + << ", and threshold: " << threshold + << ", and receive window size: " << receive_window_size_ + << ". 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() { diff --git a/chromium/net/quic/quic_flow_controller.h b/chromium/net/quic/quic_flow_controller.h index afa022b1cf2..f1973f2cb56 100644 --- a/chromium/net/quic/quic_flow_controller.h +++ b/chromium/net/quic/quic_flow_controller.h @@ -30,7 +30,8 @@ class NET_EXPORT_PRIVATE QuicFlowController { Perspective perspective, QuicStreamOffset send_window_offset, QuicStreamOffset receive_window_offset, - QuicByteCount max_receive_window); + bool should_auto_tune_receive_window); + ~QuicFlowController() {} // Called when we see a new highest received byte offset from the peer, either @@ -68,12 +69,26 @@ class NET_EXPORT_PRIVATE QuicFlowController { return highest_received_byte_offset_; } + void set_receive_window_size_limit(QuicByteCount receive_window_size_limit) { + DCHECK_GE(receive_window_size_limit, receive_window_size_limit_); + receive_window_size_limit_ = receive_window_size_limit; + } + + void set_auto_tune_receive_window(bool enable) { + auto_tune_receive_window_ = enable; + } + + bool auto_tune_receive_window() { return auto_tune_receive_window_; } + private: friend class test::QuicFlowControllerPeer; // Send a WINDOW_UPDATE frame if appropriate. void MaybeSendWindowUpdate(); + // Auto-tune the max receive window size. + void MaybeIncreaseMaxWindowSize(); + // The parent connection, used to send connection close on flow control // violation, and WINDOW_UPDATE and BLOCKED frames when appropriate. // Not owned. @@ -86,6 +101,33 @@ class NET_EXPORT_PRIVATE QuicFlowController { // Tracks if this is owned by a server or a client. Perspective perspective_; + // Tracks number of bytes sent to the peer. + QuicByteCount 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. + QuicStreamOffset send_window_offset_; + + // Overview of receive flow controller. + // + // 0=...===1=======2-------3 ...... FIN + // |<--- <= 4 --->| + // + + // 1) bytes_consumed_ - moves forward when data is read out of the + // stream. + // + // 2) highest_received_byte_offset_ - moves when data is received + // from the peer. + // + // 3) receive_window_offset_ - moves when WINDOW_UPDATE is sent. + // + // 4) receive_window_size_ - maximum allowed unread data (3 - 1). + // This value may be increased by auto-tuning. + // + // 5) receive_window_size_limit_ - limit on receive_window_size_; + // auto-tuning will not increase window size beyond this limit. + // Track number of bytes received from the peer, which have been consumed // locally. QuicByteCount bytes_consumed_; @@ -94,24 +136,31 @@ class NET_EXPORT_PRIVATE QuicFlowController { // highest offset in a data frame, or a final value in a RST. QuicStreamOffset highest_received_byte_offset_; - // Tracks number of bytes sent to the peer. - QuicByteCount 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. - QuicStreamOffset send_window_offset_; // The absolute offset in the incoming byte stream. The peer should never send // us bytes which are beyond this offset. QuicStreamOffset receive_window_offset_; // Largest size the receive window can grow to. - QuicByteCount max_receive_window_; + QuicByteCount receive_window_size_; + + // Upper limit on receive_window_size_; + QuicByteCount receive_window_size_limit_; + + // Used to dynamically enable receive window auto-tuning. + bool auto_tune_receive_window_; + + // Send window update when receive window size drops below this. + QuicByteCount WindowUpdateThreshold(); // 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. QuicStreamOffset last_blocked_send_window_offset_; + // Keep time of the last time a window update was sent. We use this + // as part of the receive window auto tuning. + QuicTime prev_window_update_time_; + DISALLOW_COPY_AND_ASSIGN(QuicFlowController); }; diff --git a/chromium/net/quic/quic_flow_controller_test.cc b/chromium/net/quic/quic_flow_controller_test.cc index 4562a142d3b..dffbd56d7f6 100644 --- a/chromium/net/quic/quic_flow_controller_test.cc +++ b/chromium/net/quic/quic_flow_controller_test.cc @@ -6,9 +6,11 @@ #include "base/format_macros.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_sent_packet_manager_peer.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/test/gtest_util.h" #include "testing/gmock/include/gmock/gmock.h" @@ -16,26 +18,27 @@ namespace net { namespace test { +// Receive window auto-tuning uses RTT in its logic. +const int64 kRtt = 100; + class QuicFlowControllerTest : public ::testing::Test { public: QuicFlowControllerTest() : stream_id_(1234), send_window_(kInitialSessionFlowControlWindowForTest), receive_window_(kInitialSessionFlowControlWindowForTest), - max_receive_window_(kInitialSessionFlowControlWindowForTest), connection_(Perspective::IS_CLIENT) {} void Initialize() { - flow_controller_.reset(new QuicFlowController( - &connection_, stream_id_, Perspective::IS_CLIENT, send_window_, - receive_window_, max_receive_window_)); + flow_controller_.reset( + new QuicFlowController(&connection_, stream_id_, Perspective::IS_CLIENT, + send_window_, receive_window_, false)); } protected: QuicStreamId stream_id_; QuicByteCount send_window_; QuicByteCount receive_window_; - QuicByteCount max_receive_window_; scoped_ptr<QuicFlowController> flow_controller_; MockConnection connection_; }; @@ -147,5 +150,225 @@ TEST_F(QuicFlowControllerTest, OnlySendBlockedFrameOncePerOffset) { flow_controller_->MaybeSendBlocked(); } +TEST_F(QuicFlowControllerTest, ReceivingBytesFastIncreasesFlowWindow) { + ValueRestore<bool> old_flag(&FLAGS_quic_auto_tune_receive_window, true); + // This test will generate two WINDOW_UPDATE frames. + EXPECT_CALL(connection_, SendWindowUpdate(stream_id_, ::testing::_)).Times(2); + + Initialize(); + flow_controller_->set_auto_tune_receive_window(true); + + // Make sure clock is inititialized. + connection_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + + QuicSentPacketManager* manager = + QuicConnectionPeer::GetSentPacketManager(&connection_); + + RttStats* rtt_stats = QuicSentPacketManagerPeer::GetRttStats(manager); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kRtt), + QuicTime::Delta::Zero(), QuicTime::Zero()); + + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + QuicByteCount threshold = + QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get()); + + QuicStreamOffset receive_offset = threshold + 1; + // Receive some bytes, updating highest received offset, but not enough to + // fill flow control receive window. + EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset)); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest - receive_offset, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + // Consume enough bytes to send a WINDOW_UPDATE frame. + flow_controller_->AddBytesConsumed(threshold + 1); + // 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())); + + // Move time forward, but by less than two RTTs. Then receive and consume + // some more, forcing a second WINDOW_UPDATE with an increased max window + // size. + connection_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2 * kRtt - 1)); + receive_offset += threshold + 1; + EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset)); + flow_controller_->AddBytesConsumed(threshold + 1); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + QuicByteCount new_threshold = + QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get()); + EXPECT_GT(new_threshold, threshold); +} + +TEST_F(QuicFlowControllerTest, ReceivingBytesFastStatusQuo) { + ValueRestore<bool> old_flag(&FLAGS_quic_auto_tune_receive_window, false); + // This test will generate two WINDOW_UPDATE frames. + EXPECT_CALL(connection_, SendWindowUpdate(stream_id_, ::testing::_)).Times(2); + + Initialize(); + flow_controller_->set_auto_tune_receive_window(true); + + // Make sure clock is inititialized. + connection_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + + QuicSentPacketManager* manager = + QuicConnectionPeer::GetSentPacketManager(&connection_); + + RttStats* rtt_stats = QuicSentPacketManagerPeer::GetRttStats(manager); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kRtt), + QuicTime::Delta::Zero(), QuicTime::Zero()); + + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + QuicByteCount threshold = + QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get()); + + QuicStreamOffset receive_offset = threshold + 1; + // Receive some bytes, updating highest received offset, but not enough to + // fill flow control receive window. + EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset)); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest - receive_offset, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + // Consume enough bytes to send a WINDOW_UPDATE frame. + flow_controller_->AddBytesConsumed(threshold + 1); + // 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())); + + // Move time forward, but by less than two RTTs. Then receive and consume + // some more, forcing a second WINDOW_UPDATE with an increased max window + // size. + connection_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2 * kRtt - 1)); + receive_offset += threshold + 1; + EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset)); + flow_controller_->AddBytesConsumed(threshold + 1); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + QuicByteCount new_threshold = + QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get()); + EXPECT_EQ(new_threshold, threshold); +} + +TEST_F(QuicFlowControllerTest, ReceivingBytesNormalStableFlowWindow) { + ValueRestore<bool> old_flag(&FLAGS_quic_auto_tune_receive_window, true); + // This test will generate two WINDOW_UPDATE frames. + EXPECT_CALL(connection_, SendWindowUpdate(stream_id_, ::testing::_)).Times(2); + + Initialize(); + flow_controller_->set_auto_tune_receive_window(true); + + // Make sure clock is inititialized. + connection_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + + QuicSentPacketManager* manager = + QuicConnectionPeer::GetSentPacketManager(&connection_); + RttStats* rtt_stats = QuicSentPacketManagerPeer::GetRttStats(manager); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kRtt), + QuicTime::Delta::Zero(), QuicTime::Zero()); + + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + QuicByteCount threshold = + QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get()); + + QuicStreamOffset receive_offset = threshold + 1; + // Receive some bytes, updating highest received offset, but not enough to + // fill flow control receive window. + EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset)); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest - receive_offset, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + flow_controller_->AddBytesConsumed(threshold + 1); + + // 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())); + + // Move time forward, but by more than two RTTs. Then receive and consume + // some more, forcing a second WINDOW_UPDATE with unchanged max window size. + connection_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2 * kRtt + 1)); + + receive_offset += threshold + 1; + EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset)); + + flow_controller_->AddBytesConsumed(threshold + 1); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + + QuicByteCount new_threshold = + QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get()); + + EXPECT_EQ(new_threshold, threshold); +} + +TEST_F(QuicFlowControllerTest, ReceivingBytesNormalStatusQuo) { + ValueRestore<bool> old_flag(&FLAGS_quic_auto_tune_receive_window, false); + // This test will generate two WINDOW_UPDATE frames. + EXPECT_CALL(connection_, SendWindowUpdate(stream_id_, ::testing::_)).Times(2); + + Initialize(); + flow_controller_->set_auto_tune_receive_window(true); + + // Make sure clock is inititialized. + connection_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + + QuicSentPacketManager* manager = + QuicConnectionPeer::GetSentPacketManager(&connection_); + RttStats* rtt_stats = QuicSentPacketManagerPeer::GetRttStats(manager); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kRtt), + QuicTime::Delta::Zero(), QuicTime::Zero()); + + EXPECT_FALSE(flow_controller_->IsBlocked()); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + QuicByteCount threshold = + QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get()); + + QuicStreamOffset receive_offset = threshold + 1; + // Receive some bytes, updating highest received offset, but not enough to + // fill flow control receive window. + EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset)); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + EXPECT_EQ(kInitialSessionFlowControlWindowForTest - receive_offset, + QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get())); + + flow_controller_->AddBytesConsumed(threshold + 1); + + // 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())); + + // Move time forward, but by more than two RTTs. Then receive and consume + // some more, forcing a second WINDOW_UPDATE with unchanged max window size. + connection_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2 * kRtt + 1)); + + receive_offset += threshold + 1; + EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset)); + + flow_controller_->AddBytesConsumed(threshold + 1); + EXPECT_FALSE(flow_controller_->FlowControlViolation()); + + QuicByteCount new_threshold = + QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get()); + + EXPECT_EQ(new_threshold, threshold); +} + } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_framer.cc b/chromium/net/quic/quic_framer.cc index db0d39dd880..ed5c011ff6c 100644 --- a/chromium/net/quic/quic_framer.cc +++ b/chromium/net/quic/quic_framer.cc @@ -4,6 +4,8 @@ #include "net/quic/quic_framer.h" +#include <stdint.h> + #include "base/basictypes.h" #include "base/logging.h" #include "base/stl_util.h" @@ -31,16 +33,16 @@ namespace { // Mask to select the lowest 48 bits of a sequence number. const QuicPacketSequenceNumber k6ByteSequenceNumberMask = - GG_UINT64_C(0x0000FFFFFFFFFFFF); + UINT64_C(0x0000FFFFFFFFFFFF); const QuicPacketSequenceNumber k4ByteSequenceNumberMask = - GG_UINT64_C(0x00000000FFFFFFFF); + UINT64_C(0x00000000FFFFFFFF); const QuicPacketSequenceNumber k2ByteSequenceNumberMask = - GG_UINT64_C(0x000000000000FFFF); + UINT64_C(0x000000000000FFFF); const QuicPacketSequenceNumber k1ByteSequenceNumberMask = - GG_UINT64_C(0x00000000000000FF); + UINT64_C(0x00000000000000FF); -const QuicConnectionId k1ByteConnectionIdMask = GG_UINT64_C(0x00000000000000FF); -const QuicConnectionId k4ByteConnectionIdMask = GG_UINT64_C(0x00000000FFFFFFFF); +const QuicConnectionId k1ByteConnectionIdMask = UINT64_C(0x00000000000000FF); +const QuicConnectionId k4ByteConnectionIdMask = UINT64_C(0x00000000FFFFFFFF); // Number of bits the sequence number length bits are shifted from the right // edge of the public header. @@ -133,15 +135,6 @@ QuicSequenceNumberLength ReadSequenceNumberLength(uint8 flags) { } // 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, Perspective perspective) @@ -180,7 +173,6 @@ size_t QuicFramer::GetMinStreamFrameSize(QuicStreamId stream_id, // static size_t QuicFramer::GetMinAckFrameSize( - QuicSequenceNumberLength sequence_number_length, QuicSequenceNumberLength largest_observed_length) { return kQuicFrameTypeSize + kQuicEntropyHashSize + largest_observed_length + kQuicDeltaTimeLargestObservedSize; @@ -308,9 +300,9 @@ size_t QuicFramer::GetSerializedFrameLength( if (!first_frame) { return 0; } - bool can_truncate = frame.type == ACK_FRAME && - free_bytes >= GetMinAckFrameSize(PACKET_6BYTE_SEQUENCE_NUMBER, - PACKET_6BYTE_SEQUENCE_NUMBER); + bool can_truncate = + frame.type == ACK_FRAME && + free_bytes >= GetMinAckFrameSize(PACKET_6BYTE_SEQUENCE_NUMBER); if (can_truncate) { // Truncate the frame so the packet will not exceed kMaxPacketSize. // Note that we may not use every byte of the writer in this case. @@ -380,6 +372,8 @@ QuicPacket* QuicFramer::BuildDataPacket(const QuicPacketHeader& header, return nullptr; } break; + case MTU_DISCOVERY_FRAME: + // MTU discovery frames are serialized as ping frames. case PING_FRAME: // Ping has no payload. break; @@ -810,7 +804,7 @@ const QuicTime::Delta QuicFramer::CalculateTimestampFromWire( // // epoch_delta is the delta between epochs. A delta is 4 bytes of // microseconds. - const uint64 epoch_delta = GG_UINT64_C(1) << 32; + const uint64 epoch_delta = UINT64_C(1) << 32; uint64 epoch = last_timestamp_.ToMicroseconds() & ~(epoch_delta - 1); // Wrapping is safe here because a wrapped value will not be ClosestTo below. uint64 prev_epoch = epoch - epoch_delta; @@ -837,7 +831,7 @@ QuicPacketSequenceNumber QuicFramer::CalculatePacketSequenceNumberFromWire( // with, so the correct value is likely the same epoch as the last sequence // number or an adjacent epoch. const QuicPacketSequenceNumber epoch_delta = - GG_UINT64_C(1) << (8 * sequence_number_length); + UINT64_C(1) << (8 * sequence_number_length); QuicPacketSequenceNumber next_sequence_number = last_sequence_number_ + 1; QuicPacketSequenceNumber epoch = last_sequence_number_ & ~(epoch_delta - 1); QuicPacketSequenceNumber prev_epoch = epoch - epoch_delta; @@ -955,7 +949,7 @@ QuicSequenceNumberLength QuicFramer::GetMinSequenceNumberLength( } else if (sequence_number < 1 << (PACKET_2BYTE_SEQUENCE_NUMBER * 8)) { return PACKET_2BYTE_SEQUENCE_NUMBER; } else if (sequence_number < - GG_UINT64_C(1) << (PACKET_4BYTE_SEQUENCE_NUMBER * 8)) { + UINT64_C(1) << (PACKET_4BYTE_SEQUENCE_NUMBER * 8)) { return PACKET_4BYTE_SEQUENCE_NUMBER; } else { return PACKET_6BYTE_SEQUENCE_NUMBER; @@ -1279,23 +1273,17 @@ bool QuicFramer::ProcessStreamFrame(uint8 frame_type, return false; } - StringPiece frame_data; if (has_data_length) { - if (!reader_->ReadStringPiece16(&frame_data)) { + if (!reader_->ReadStringPiece16(&frame->data)) { set_detailed_error("Unable to read frame data."); return false; } } else { - if (!reader_->ReadStringPiece(&frame_data, reader_->BytesRemaining())) { + if (!reader_->ReadStringPiece(&frame->data, reader_->BytesRemaining())) { set_detailed_error("Unable to read frame data."); return false; } } - // Point frame to the right data. - frame->data.Clear(); - if (!frame_data.empty()) { - frame->data.Append(const_cast<char*>(frame_data.data()), frame_data.size()); - } return true; } @@ -1596,16 +1584,15 @@ StringPiece QuicFramer::GetAssociatedDataFromEncryptedPacket( - kStartOfHashData); } -void QuicFramer::SetDecrypter(QuicDecrypter* decrypter, - EncryptionLevel level) { +void QuicFramer::SetDecrypter(EncryptionLevel level, QuicDecrypter* decrypter) { DCHECK(alternative_decrypter_.get() == nullptr); DCHECK_GE(level, decrypter_level_); decrypter_.reset(decrypter); decrypter_level_ = level; } -void QuicFramer::SetAlternativeDecrypter(QuicDecrypter* decrypter, - EncryptionLevel level, +void QuicFramer::SetAlternativeDecrypter(EncryptionLevel level, + QuicDecrypter* decrypter, bool latch_once_used) { alternative_decrypter_.reset(decrypter); alternative_decrypter_level_ = level; @@ -1627,7 +1614,7 @@ void QuicFramer::SetEncrypter(EncryptionLevel level, encrypter_[level].reset(encrypter); } -QuicEncryptedPacket* QuicFramer::EncryptPacket( +QuicEncryptedPacket* QuicFramer::EncryptPayload( EncryptionLevel level, QuicPacketSequenceNumber packet_sequence_number, const QuicPacket& packet, @@ -1742,8 +1729,7 @@ size_t QuicFramer::GetAckFrameSize( QuicSequenceNumberLength missing_sequence_number_length = GetMinSequenceNumberLength(ack_info.max_delta); - size_t ack_size = GetMinAckFrameSize(sequence_number_length, - largest_observed_length); + size_t ack_size = GetMinAckFrameSize(largest_observed_length); if (!ack_info.nack_ranges.empty()) { ack_size += kNumberOfNackRangesSize + kNumberOfRevivedPacketsSize; ack_size += min(ack_info.nack_ranges.size(), kMaxNackRanges) * @@ -1780,14 +1766,15 @@ size_t QuicFramer::ComputeFrameLength( case STREAM_FRAME: return GetMinStreamFrameSize(frame.stream_frame->stream_id, frame.stream_frame->offset, - last_frame_in_packet, - is_in_fec_group) + - frame.stream_frame->data.TotalBufferSize(); + last_frame_in_packet, is_in_fec_group) + + frame.stream_frame->data.length(); case ACK_FRAME: { return GetAckFrameSize(*frame.ack_frame, sequence_number_length); } case STOP_WAITING_FRAME: return GetStopWaitingFrameSize(sequence_number_length); + case MTU_DISCOVERY_FRAME: + // MTU discovery frames are serialized as ping frames. case PING_FRAME: // Ping has no payload. return kQuicFrameTypeSize; @@ -1850,6 +1837,9 @@ bool QuicFramer::AppendTypeByte(const QuicFrame& frame, } case ACK_FRAME: return true; + case MTU_DISCOVERY_FRAME: + type_byte = static_cast<uint8>(PING_FRAME); + break; default: type_byte = static_cast<uint8>(frame.type); break; @@ -1904,15 +1894,14 @@ bool QuicFramer::AppendStreamFrame( return false; } if (!no_stream_frame_length) { - if ((frame.data.TotalBufferSize() > numeric_limits<uint16>::max()) || - !writer->WriteUInt16( - static_cast<uint16>(frame.data.TotalBufferSize()))) { + if ((frame.data.size() > numeric_limits<uint16>::max()) || + !writer->WriteUInt16(static_cast<uint16>(frame.data.size()))) { LOG(DFATAL) << "Writing stream frame length failed"; return false; } } - if (!writer->WriteIOVector(frame.data)) { + if (!writer->WriteBytes(frame.data.data(), frame.data.size())) { LOG(DFATAL) << "Writing frame data failed."; return false; } @@ -1935,10 +1924,9 @@ bool QuicFramer::AppendAckFrameAndTypeByte( QuicSequenceNumberLength missing_sequence_number_length = GetMinSequenceNumberLength(ack_info.max_delta); // Determine whether we need to truncate ranges. - size_t available_range_bytes = writer->capacity() - writer->length() - - kNumberOfRevivedPacketsSize - kNumberOfNackRangesSize - - GetMinAckFrameSize(header.public_header.sequence_number_length, - largest_observed_length); + size_t available_range_bytes = + writer->capacity() - writer->length() - kNumberOfRevivedPacketsSize - + kNumberOfNackRangesSize - GetMinAckFrameSize(largest_observed_length); size_t max_num_ranges = available_range_bytes / (missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER); max_num_ranges = min(kMaxNackRanges, max_num_ranges); @@ -2100,7 +2088,7 @@ bool QuicFramer::AppendTimestampToAckFrame(const QuicAckFrame& frame, } // Use the lowest 4 bytes of the time delta from the creation_time_. - const uint64 time_epoch_delta_us = GG_UINT64_C(1) << 32; + const uint64 time_epoch_delta_us = UINT64_C(1) << 32; uint32 time_delta_us = static_cast<uint32>(it->second.Subtract(creation_time_).ToMicroseconds() & (time_epoch_delta_us - 1)); diff --git a/chromium/net/quic/quic_framer.h b/chromium/net/quic/quic_framer.h index c0c2c662fb5..c994c2ef3f7 100644 --- a/chromium/net/quic/quic_framer.h +++ b/chromium/net/quic/quic_framer.h @@ -243,7 +243,6 @@ class NET_EXPORT_PRIVATE QuicFramer { InFecGroup is_in_fec_group); // Size in bytes of all ack frame fields without the missing packets. static size_t GetMinAckFrameSize( - QuicSequenceNumberLength sequence_number_length, QuicSequenceNumberLength largest_observed_length); // Size in bytes of a stop waiting frame. static size_t GetStopWaitingFrameSize( @@ -315,7 +314,7 @@ class NET_EXPORT_PRIVATE QuicFramer { // 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. |level| indicates the encryption level of the new decrypter. - void SetDecrypter(QuicDecrypter* decrypter, EncryptionLevel level); + void SetDecrypter(EncryptionLevel level, QuicDecrypter* decrypter); // SetAlternativeDecrypter sets a decrypter that may be used to decrypt // future packets and takes ownership of it. |level| indicates the encryption @@ -323,8 +322,8 @@ class NET_EXPORT_PRIVATE QuicFramer { // 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, + void SetAlternativeDecrypter(EncryptionLevel level, + QuicDecrypter* decrypter, bool latch_once_used); const QuicDecrypter* decrypter() const; @@ -337,11 +336,11 @@ class NET_EXPORT_PRIVATE QuicFramer { // Returns a new encrypted packet, owned by the caller. // Encrypts into |buffer| if |buffer_len| is long enough, and otherwise // constructs a new buffer owned by the EncryptedPacket. - QuicEncryptedPacket* EncryptPacket(EncryptionLevel level, - QuicPacketSequenceNumber sequence_number, - const QuicPacket& packet, - char* buffer, - size_t buffer_len); + QuicEncryptedPacket* EncryptPayload(EncryptionLevel level, + QuicPacketSequenceNumber sequence_number, + const QuicPacket& packet, + char* buffer, + size_t buffer_len); // Returns the maximum length of plaintext that can be encrypted // to ciphertext no larger than |ciphertext_size|. @@ -518,7 +517,7 @@ class NET_EXPORT_PRIVATE QuicFramer { // successfully decrypts a packet, we should install it as the only // decrypter. bool alternative_decrypter_latch_; - // Encrypters used to encrypt packets via EncryptPacket(). + // Encrypters used to encrypt packets via EncryptPayload(). scoped_ptr<QuicEncrypter> encrypter_[NUM_ENCRYPTION_LEVELS]; // Tracks if the framer is being used by the entity that received the // connection or the entity that initiated it. diff --git a/chromium/net/quic/quic_framer_test.cc b/chromium/net/quic/quic_framer_test.cc index 16383620b5f..7f564f33a4d 100644 --- a/chromium/net/quic/quic_framer_test.cc +++ b/chromium/net/quic/quic_framer_test.cc @@ -4,6 +4,7 @@ #include "net/quic/quic_framer.h" +#include <stdint.h> #include <algorithm> #include <map> #include <string> @@ -12,7 +13,6 @@ #include "base/containers/hash_tables.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" -#include "base/port.h" #include "base/stl_util.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" @@ -31,12 +31,13 @@ using std::pair; using std::string; using std::vector; using testing::Return; +using testing::Truly; using testing::_; namespace net { namespace test { -const QuicPacketSequenceNumber kEpoch = GG_UINT64_C(1) << 48; +const QuicPacketSequenceNumber kEpoch = UINT64_C(1) << 48; const QuicPacketSequenceNumber kMask = kEpoch - 1; // Index into the connection_id offset in the header. @@ -106,13 +107,6 @@ class TestEncrypter : public QuicEncrypter { ~TestEncrypter() override {} bool SetKey(StringPiece key) override { return true; } bool SetNoncePrefix(StringPiece nonce_prefix) override { return true; } - bool Encrypt(StringPiece nonce, - StringPiece associated_data, - StringPiece plaintext, - unsigned char* output) override { - CHECK(false) << "Not implemented"; - return false; - } bool EncryptPacket(QuicPacketSequenceNumber sequence_number, StringPiece associated_data, StringPiece plaintext, @@ -236,12 +230,12 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { bool OnStreamFrame(const QuicStreamFrame& frame) override { ++frame_count_; // Save a copy of the data so it is valid after the packet is processed. - stream_data_.push_back(frame.GetDataAsString()); + string* string_data = new string(); + frame.data.AppendToString(string_data); + stream_data_.push_back(string_data); QuicStreamFrame* stream_frame = new QuicStreamFrame(frame); // Make sure that the stream frame points to this data. - stream_frame->data.Clear(); - stream_frame->data.Append(const_cast<char*>(stream_data_.back()->data()), - stream_data_.back()->size()); + stream_frame->data = StringPiece(*string_data); stream_frames_.push_back(stream_frame); return true; } @@ -345,7 +339,7 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> { framer_(QuicSupportedVersions(), start_, Perspective::IS_SERVER) { version_ = GetParam(); framer_.set_version(version_); - framer_.SetDecrypter(decrypter_, ENCRYPTION_NONE); + framer_.SetDecrypter(ENCRYPTION_NONE, decrypter_); framer_.SetEncrypter(ENCRYPTION_NONE, encrypter_); framer_.set_visitor(&visitor_); framer_.set_received_entropy_calculator(&entropy_calculator_); @@ -434,8 +428,7 @@ class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> { // Checks if the supplied string matches data in the supplied StreamFrame. void CheckStreamFrameData(string str, QuicStreamFrame* frame) { - scoped_ptr<string> frame_data(frame->GetDataAsString()); - EXPECT_EQ(str, *frame_data); + EXPECT_EQ(str, frame->data); } void CheckStreamFrameBoundaries(unsigned char* packet, @@ -501,7 +494,7 @@ INSTANTIATE_TEST_CASE_P(QuicFramerTests, TEST_P(QuicFramerTest, CalculatePacketSequenceNumberFromWireNearEpochStart) { // A few quick manual sanity checks - CheckCalculatePacketSequenceNumber(GG_UINT64_C(1), GG_UINT64_C(0)); + CheckCalculatePacketSequenceNumber(UINT64_C(1), UINT64_C(0)); CheckCalculatePacketSequenceNumber(kEpoch + 1, kMask); CheckCalculatePacketSequenceNumber(kEpoch, kMask); @@ -643,7 +636,7 @@ 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), + EXPECT_EQ(UINT64_C(0xFEDCBA9876543210), visitor_.header_->public_header.connection_id); // Make sure the correct error is propagated. EXPECT_EQ(QUIC_PACKET_TOO_LARGE, framer_.error()); @@ -667,14 +660,14 @@ TEST_P(QuicFramerTest, PacketHeader) { EXPECT_FALSE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); - EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + EXPECT_EQ(UINT64_C(0xFEDCBA9876543210), 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); EXPECT_FALSE(visitor_.header_->entropy_flag); EXPECT_EQ(0, visitor_.header_->entropy_hash); - EXPECT_EQ(GG_UINT64_C(0x123456789ABC), + EXPECT_EQ(UINT64_C(0x123456789ABC), visitor_.header_->packet_sequence_number); EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group); EXPECT_EQ(0x00u, visitor_.header_->fec_group); @@ -702,7 +695,7 @@ TEST_P(QuicFramerTest, PacketHeader) { TEST_P(QuicFramerTest, PacketHeaderWith4ByteConnectionId) { QuicFramerPeer::SetLastSerializedConnectionId( - &framer_, GG_UINT64_C(0xFEDCBA9876543210)); + &framer_, UINT64_C(0xFEDCBA9876543210)); unsigned char packet[] = { // public flags (4 byte connection_id) @@ -720,14 +713,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteConnectionId) { EXPECT_FALSE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); - EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + EXPECT_EQ(UINT64_C(0xFEDCBA9876543210), 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); EXPECT_FALSE(visitor_.header_->entropy_flag); EXPECT_EQ(0, visitor_.header_->entropy_hash); - EXPECT_EQ(GG_UINT64_C(0x123456789ABC), + EXPECT_EQ(UINT64_C(0x123456789ABC), visitor_.header_->packet_sequence_number); EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group); EXPECT_EQ(0x00u, visitor_.header_->fec_group); @@ -758,7 +751,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteConnectionId) { TEST_P(QuicFramerTest, PacketHeader1ByteConnectionId) { QuicFramerPeer::SetLastSerializedConnectionId( - &framer_, GG_UINT64_C(0xFEDCBA9876543210)); + &framer_, UINT64_C(0xFEDCBA9876543210)); unsigned char packet[] = { // public flags (1 byte connection_id) @@ -776,14 +769,14 @@ TEST_P(QuicFramerTest, PacketHeader1ByteConnectionId) { EXPECT_FALSE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); - EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + EXPECT_EQ(UINT64_C(0xFEDCBA9876543210), 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); EXPECT_FALSE(visitor_.header_->entropy_flag); EXPECT_EQ(0, visitor_.header_->entropy_hash); - EXPECT_EQ(GG_UINT64_C(0x123456789ABC), + EXPECT_EQ(UINT64_C(0x123456789ABC), visitor_.header_->packet_sequence_number); EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group); EXPECT_EQ(0x00u, visitor_.header_->fec_group); @@ -814,7 +807,7 @@ TEST_P(QuicFramerTest, PacketHeader1ByteConnectionId) { TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) { QuicFramerPeer::SetLastSerializedConnectionId( - &framer_, GG_UINT64_C(0xFEDCBA9876543210)); + &framer_, UINT64_C(0xFEDCBA9876543210)); unsigned char packet[] = { // public flags (0 byte connection_id) @@ -831,14 +824,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) { EXPECT_FALSE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); - EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + EXPECT_EQ(UINT64_C(0xFEDCBA9876543210), 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); EXPECT_FALSE(visitor_.header_->entropy_flag); EXPECT_EQ(0, visitor_.header_->entropy_hash); - EXPECT_EQ(GG_UINT64_C(0x123456789ABC), + EXPECT_EQ(UINT64_C(0x123456789ABC), visitor_.header_->packet_sequence_number); EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group); EXPECT_EQ(0x00u, visitor_.header_->fec_group); @@ -887,7 +880,7 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) { EXPECT_FALSE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); - EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + EXPECT_EQ(UINT64_C(0xFEDCBA9876543210), visitor_.header_->public_header.connection_id); EXPECT_FALSE(visitor_.header_->public_header.reset_flag); EXPECT_TRUE(visitor_.header_->public_header.version_flag); @@ -895,7 +888,7 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) { EXPECT_FALSE(visitor_.header_->fec_flag); EXPECT_FALSE(visitor_.header_->entropy_flag); EXPECT_EQ(0, visitor_.header_->entropy_hash); - EXPECT_EQ(GG_UINT64_C(0x123456789ABC), + EXPECT_EQ(UINT64_C(0x123456789ABC), visitor_.header_->packet_sequence_number); EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group); EXPECT_EQ(0x00u, visitor_.header_->fec_group); @@ -924,8 +917,7 @@ TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) { } TEST_P(QuicFramerTest, PacketHeaderWith4ByteSequenceNumber) { - QuicFramerPeer::SetLastSequenceNumber(&framer_, - GG_UINT64_C(0x123456789ABA)); + QuicFramerPeer::SetLastSequenceNumber(&framer_, UINT64_C(0x123456789ABA)); unsigned char packet[] = { // public flags (8 byte connection_id and 4 byte sequence number) @@ -943,15 +935,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteSequenceNumber) { EXPECT_FALSE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); - EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + EXPECT_EQ(UINT64_C(0xFEDCBA9876543210), 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); EXPECT_FALSE(visitor_.header_->entropy_flag); EXPECT_EQ(0, visitor_.header_->entropy_hash); - EXPECT_EQ(GG_UINT64_C(0x123456789ABC), - visitor_.header_->packet_sequence_number); + EXPECT_EQ(UINT64_C(0x123456789ABC), visitor_.header_->packet_sequence_number); EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group); EXPECT_EQ(0x00u, visitor_.header_->fec_group); @@ -979,8 +970,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith4ByteSequenceNumber) { } TEST_P(QuicFramerTest, PacketHeaderWith2ByteSequenceNumber) { - QuicFramerPeer::SetLastSequenceNumber(&framer_, - GG_UINT64_C(0x123456789ABA)); + QuicFramerPeer::SetLastSequenceNumber(&framer_, UINT64_C(0x123456789ABA)); unsigned char packet[] = { // public flags (8 byte connection_id and 2 byte sequence number) @@ -998,15 +988,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith2ByteSequenceNumber) { EXPECT_FALSE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); - EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + EXPECT_EQ(UINT64_C(0xFEDCBA9876543210), 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); EXPECT_FALSE(visitor_.header_->entropy_flag); EXPECT_EQ(0, visitor_.header_->entropy_hash); - EXPECT_EQ(GG_UINT64_C(0x123456789ABC), - visitor_.header_->packet_sequence_number); + EXPECT_EQ(UINT64_C(0x123456789ABC), visitor_.header_->packet_sequence_number); EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group); EXPECT_EQ(0x00u, visitor_.header_->fec_group); @@ -1034,8 +1023,7 @@ TEST_P(QuicFramerTest, PacketHeaderWith2ByteSequenceNumber) { } TEST_P(QuicFramerTest, PacketHeaderWith1ByteSequenceNumber) { - QuicFramerPeer::SetLastSequenceNumber(&framer_, - GG_UINT64_C(0x123456789ABA)); + QuicFramerPeer::SetLastSequenceNumber(&framer_, UINT64_C(0x123456789ABA)); unsigned char packet[] = { // public flags (8 byte connection_id and 1 byte sequence number) @@ -1053,15 +1041,14 @@ TEST_P(QuicFramerTest, PacketHeaderWith1ByteSequenceNumber) { EXPECT_FALSE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); - EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + EXPECT_EQ(UINT64_C(0xFEDCBA9876543210), 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); EXPECT_FALSE(visitor_.header_->entropy_flag); EXPECT_EQ(0, visitor_.header_->entropy_hash); - EXPECT_EQ(GG_UINT64_C(0x123456789ABC), - visitor_.header_->packet_sequence_number); + EXPECT_EQ(UINT64_C(0x123456789ABC), visitor_.header_->packet_sequence_number); EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group); EXPECT_EQ(0x00u, visitor_.header_->fec_group); @@ -1300,8 +1287,7 @@ TEST_P(QuicFramerTest, StreamFrame) { EXPECT_EQ(static_cast<uint64>(0x01020304), visitor_.stream_frames_[0]->stream_id); EXPECT_TRUE(visitor_.stream_frames_[0]->fin); - EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654), - visitor_.stream_frames_[0]->offset); + EXPECT_EQ(UINT64_C(0xBA98FEDC32107654), visitor_.stream_frames_[0]->offset); CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]); // Now test framing boundaries. @@ -1345,10 +1331,9 @@ TEST_P(QuicFramerTest, StreamFrame3ByteStreamId) { ASSERT_EQ(1u, visitor_.stream_frames_.size()); EXPECT_EQ(0u, visitor_.ack_frames_.size()); - EXPECT_EQ(GG_UINT64_C(0x00020304), visitor_.stream_frames_[0]->stream_id); + EXPECT_EQ(UINT64_C(0x00020304), visitor_.stream_frames_[0]->stream_id); EXPECT_TRUE(visitor_.stream_frames_[0]->fin); - EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654), - visitor_.stream_frames_[0]->offset); + EXPECT_EQ(UINT64_C(0xBA98FEDC32107654), visitor_.stream_frames_[0]->offset); CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]); // Now test framing boundaries. @@ -1396,8 +1381,7 @@ TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) { EXPECT_EQ(static_cast<uint64>(0x00000304), visitor_.stream_frames_[0]->stream_id); EXPECT_TRUE(visitor_.stream_frames_[0]->fin); - EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654), - visitor_.stream_frames_[0]->offset); + EXPECT_EQ(UINT64_C(0xBA98FEDC32107654), visitor_.stream_frames_[0]->offset); CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]); // Now test framing boundaries. @@ -1445,8 +1429,7 @@ TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) { EXPECT_EQ(static_cast<uint64>(0x00000004), visitor_.stream_frames_[0]->stream_id); EXPECT_TRUE(visitor_.stream_frames_[0]->fin); - EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654), - visitor_.stream_frames_[0]->offset); + EXPECT_EQ(UINT64_C(0xBA98FEDC32107654), visitor_.stream_frames_[0]->offset); CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]); // Now test framing boundaries. @@ -1498,8 +1481,7 @@ TEST_P(QuicFramerTest, StreamFrameWithVersion) { EXPECT_EQ(static_cast<uint64>(0x01020304), visitor_.stream_frames_[0]->stream_id); EXPECT_TRUE(visitor_.stream_frames_[0]->fin); - EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654), - visitor_.stream_frames_[0]->offset); + EXPECT_EQ(UINT64_C(0xBA98FEDC32107654), visitor_.stream_frames_[0]->offset); CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]); // Now test framing boundaries. @@ -1584,12 +1566,12 @@ TEST_P(QuicFramerTest, RevivedStreamFrame) { }; QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = true; header.entropy_flag = true; - header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); + header.packet_sequence_number = UINT64_C(0x123456789ABC); header.fec_group = 0; // Do not encrypt the payload because the revived payload is post-encryption. @@ -1600,7 +1582,7 @@ TEST_P(QuicFramerTest, RevivedStreamFrame) { EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); ASSERT_EQ(1, visitor_.revived_packets_); ASSERT_TRUE(visitor_.header_.get()); - EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + EXPECT_EQ(UINT64_C(0xFEDCBA9876543210), visitor_.header_->public_header.connection_id); EXPECT_FALSE(visitor_.header_->public_header.reset_flag); EXPECT_FALSE(visitor_.header_->public_header.version_flag); @@ -1608,17 +1590,15 @@ TEST_P(QuicFramerTest, RevivedStreamFrame) { EXPECT_TRUE(visitor_.header_->entropy_flag); EXPECT_EQ(1 << (header.packet_sequence_number % 8), visitor_.header_->entropy_hash); - EXPECT_EQ(GG_UINT64_C(0x123456789ABC), - visitor_.header_->packet_sequence_number); + EXPECT_EQ(UINT64_C(0x123456789ABC), visitor_.header_->packet_sequence_number); EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group); EXPECT_EQ(0x00u, visitor_.header_->fec_group); ASSERT_EQ(1u, visitor_.stream_frames_.size()); EXPECT_EQ(0u, visitor_.ack_frames_.size()); - EXPECT_EQ(GG_UINT64_C(0x01020304), visitor_.stream_frames_[0]->stream_id); + EXPECT_EQ(UINT64_C(0x01020304), visitor_.stream_frames_[0]->stream_id); EXPECT_TRUE(visitor_.stream_frames_[0]->fin); - EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654), - visitor_.stream_frames_[0]->offset); + EXPECT_EQ(UINT64_C(0xBA98FEDC32107654), visitor_.stream_frames_[0]->offset); CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]); } @@ -1659,8 +1639,7 @@ TEST_P(QuicFramerTest, StreamFrameInFecGroup) { ASSERT_TRUE(visitor_.header_.get()); EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); EXPECT_EQ(IN_FEC_GROUP, visitor_.header_->is_in_fec_group); - EXPECT_EQ(GG_UINT64_C(0x341256789ABA), - visitor_.header_->fec_group); + EXPECT_EQ(UINT64_C(0x341256789ABA), visitor_.header_->fec_group); const size_t fec_offset = GetStartOfFecProtectedData(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, @@ -1671,10 +1650,9 @@ TEST_P(QuicFramerTest, StreamFrameInFecGroup) { ASSERT_EQ(1u, visitor_.stream_frames_.size()); EXPECT_EQ(0u, visitor_.ack_frames_.size()); - EXPECT_EQ(GG_UINT64_C(0x01020304), visitor_.stream_frames_[0]->stream_id); + EXPECT_EQ(UINT64_C(0x01020304), visitor_.stream_frames_[0]->stream_id); EXPECT_TRUE(visitor_.stream_frames_[0]->fin); - EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654), - visitor_.stream_frames_[0]->offset); + EXPECT_EQ(UINT64_C(0xBA98FEDC32107654), visitor_.stream_frames_[0]->offset); CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]); } @@ -1729,12 +1707,12 @@ TEST_P(QuicFramerTest, AckFrameTwoTimestamp) { ASSERT_EQ(1u, visitor_.ack_frames_.size()); const QuicAckFrame& frame = *visitor_.ack_frames_[0]; EXPECT_EQ(0xBA, frame.entropy_hash); - EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.largest_observed); + EXPECT_EQ(UINT64_C(0x0123456789ABF), frame.largest_observed); ASSERT_EQ(1u, frame.missing_packets.size()); ASSERT_EQ(2u, frame.received_packet_times.size()); SequenceNumberSet::const_iterator missing_iter = frame.missing_packets.begin(); - EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter); + EXPECT_EQ(UINT64_C(0x0123456789ABE), *missing_iter); const size_t kReceivedEntropyOffset = kQuicFrameTypeSize; const size_t kLargestObservedOffset = kReceivedEntropyOffset + @@ -1846,12 +1824,12 @@ TEST_P(QuicFramerTest, AckFrameOneTimestamp) { ASSERT_EQ(1u, visitor_.ack_frames_.size()); const QuicAckFrame& frame = *visitor_.ack_frames_[0]; EXPECT_EQ(0xBA, frame.entropy_hash); - EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.largest_observed); + EXPECT_EQ(UINT64_C(0x0123456789ABF), frame.largest_observed); ASSERT_EQ(1u, frame.missing_packets.size()); ASSERT_EQ(1u, frame.received_packet_times.size()); SequenceNumberSet::const_iterator missing_iter = frame.missing_packets.begin(); - EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter); + EXPECT_EQ(UINT64_C(0x0123456789ABE), *missing_iter); const size_t kReceivedEntropyOffset = kQuicFrameTypeSize; const size_t kLargestObservedOffset = kReceivedEntropyOffset + @@ -1949,11 +1927,11 @@ TEST_P(QuicFramerTest, AckFrame) { ASSERT_EQ(1u, visitor_.ack_frames_.size()); const QuicAckFrame& frame = *visitor_.ack_frames_[0]; EXPECT_EQ(0xBA, frame.entropy_hash); - EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.largest_observed); + EXPECT_EQ(UINT64_C(0x0123456789ABF), frame.largest_observed); ASSERT_EQ(1u, frame.missing_packets.size()); SequenceNumberSet::const_iterator missing_iter = frame.missing_packets.begin(); - EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter); + EXPECT_EQ(UINT64_C(0x0123456789ABE), *missing_iter); const size_t kReceivedEntropyOffset = kQuicFrameTypeSize; const size_t kLargestObservedOffset = kReceivedEntropyOffset + @@ -2047,11 +2025,11 @@ TEST_P(QuicFramerTest, AckFrameRevivedPackets) { ASSERT_EQ(1u, visitor_.ack_frames_.size()); const QuicAckFrame& frame = *visitor_.ack_frames_[0]; EXPECT_EQ(0xBA, frame.entropy_hash); - EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.largest_observed); + EXPECT_EQ(UINT64_C(0x0123456789ABF), frame.largest_observed); ASSERT_EQ(1u, frame.missing_packets.size()); SequenceNumberSet::const_iterator missing_iter = frame.missing_packets.begin(); - EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter); + EXPECT_EQ(UINT64_C(0x0123456789ABE), *missing_iter); const size_t kReceivedEntropyOffset = kQuicFrameTypeSize; const size_t kLargestObservedOffset = kReceivedEntropyOffset + @@ -2139,7 +2117,7 @@ TEST_P(QuicFramerTest, AckFrameNoNacks) { ASSERT_EQ(1u, visitor_.ack_frames_.size()); QuicAckFrame* frame = visitor_.ack_frames_[0]; EXPECT_EQ(0xBA, frame->entropy_hash); - EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame->largest_observed); + EXPECT_EQ(UINT64_C(0x0123456789ABF), frame->largest_observed); ASSERT_EQ(0u, frame->missing_packets.size()); // Verify that the packet re-serializes identically. @@ -2202,15 +2180,15 @@ TEST_P(QuicFramerTest, AckFrame500Nacks) { ASSERT_EQ(1u, visitor_.ack_frames_.size()); QuicAckFrame* frame = visitor_.ack_frames_[0]; EXPECT_EQ(0xBA, frame->entropy_hash); - EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame->largest_observed); + EXPECT_EQ(UINT64_C(0x0123456789ABF), frame->largest_observed); EXPECT_EQ(0u, frame->revived_packets.size()); ASSERT_EQ(500u, frame->missing_packets.size()); SequenceNumberSet::const_iterator first_missing_iter = frame->missing_packets.begin(); - EXPECT_EQ(GG_UINT64_C(0x0123456789ABE) - 499, *first_missing_iter); + EXPECT_EQ(UINT64_C(0x0123456789ABE) - 499, *first_missing_iter); SequenceNumberSet::const_reverse_iterator last_missing_iter = frame->missing_packets.rbegin(); - EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *last_missing_iter); + EXPECT_EQ(UINT64_C(0x0123456789ABE), *last_missing_iter); // Verify that the packet re-serializes identically. QuicFrames frames; @@ -2257,7 +2235,7 @@ TEST_P(QuicFramerTest, StopWaitingFrame) { 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); + EXPECT_EQ(UINT64_C(0x0123456789AA0), frame.least_unacked); const size_t kSentEntropyOffset = kQuicFrameTypeSize; const size_t kLeastUnackedOffset = kSentEntropyOffset + kQuicEntropyHashSize; @@ -2323,10 +2301,10 @@ TEST_P(QuicFramerTest, RstStreamFrameQuicVersion24) { ASSERT_TRUE(visitor_.header_.get()); EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); - EXPECT_EQ(GG_UINT64_C(0x01020304), visitor_.rst_stream_frame_.stream_id); + EXPECT_EQ(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), + EXPECT_EQ(UINT64_C(0x0807060504030201), visitor_.rst_stream_frame_.byte_offset); // Now test framing boundaries. @@ -2392,9 +2370,9 @@ TEST_P(QuicFramerTest, RstStreamFrameQuic) { ASSERT_TRUE(visitor_.header_.get()); EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); - EXPECT_EQ(GG_UINT64_C(0x01020304), visitor_.rst_stream_frame_.stream_id); + EXPECT_EQ(UINT64_C(0x01020304), visitor_.rst_stream_frame_.stream_id); EXPECT_EQ(0x01, visitor_.rst_stream_frame_.error_code); - EXPECT_EQ(GG_UINT64_C(0x0807060504030201), + EXPECT_EQ(UINT64_C(0x0807060504030201), visitor_.rst_stream_frame_.byte_offset); // Now test framing boundaries. @@ -2511,8 +2489,7 @@ TEST_P(QuicFramerTest, GoAwayFrame) { ASSERT_TRUE(visitor_.header_.get()); EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); - EXPECT_EQ(GG_UINT64_C(0x01020304), - visitor_.goaway_frame_.last_good_stream_id); + EXPECT_EQ(UINT64_C(0x01020304), visitor_.goaway_frame_.last_good_stream_id); EXPECT_EQ(0x9, visitor_.goaway_frame_.error_code); EXPECT_EQ("because I can", visitor_.goaway_frame_.reason_phrase); @@ -2567,9 +2544,8 @@ TEST_P(QuicFramerTest, WindowUpdateFrame) { 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), + EXPECT_EQ(UINT64_C(0x01020304), visitor_.window_update_frame_.stream_id); + EXPECT_EQ(UINT64_C(0x0c0b0a0908070605), visitor_.window_update_frame_.byte_offset); // Now test framing boundaries. @@ -2616,8 +2592,7 @@ TEST_P(QuicFramerTest, BlockedFrame) { ASSERT_TRUE(visitor_.header_.get()); EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); - EXPECT_EQ(GG_UINT64_C(0x01020304), - visitor_.blocked_frame_.stream_id); + EXPECT_EQ(UINT64_C(0x01020304), visitor_.blocked_frame_.stream_id); // Now test framing boundaries. for (size_t i = kQuicFrameTypeSize; i < QuicFramer::GetBlockedFrameSize(); @@ -2691,13 +2666,13 @@ TEST_P(QuicFramerTest, PublicResetPacket) { 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), + EXPECT_EQ(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), + EXPECT_EQ(UINT64_C(0xABCDEF0123456789), visitor_.public_reset_packet_->nonce_proof); - EXPECT_EQ(GG_UINT64_C(0x123456789ABC), + EXPECT_EQ(UINT64_C(0x123456789ABC), visitor_.public_reset_packet_->rejected_sequence_number); EXPECT_TRUE( visitor_.public_reset_packet_->client_address.address().empty()); @@ -2795,13 +2770,13 @@ TEST_P(QuicFramerTest, PublicResetPacketWithClientAddress) { 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), + EXPECT_EQ(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), + EXPECT_EQ(UINT64_C(0xABCDEF0123456789), visitor_.public_reset_packet_->nonce_proof); - EXPECT_EQ(GG_UINT64_C(0x123456789ABC), + EXPECT_EQ(UINT64_C(0x123456789ABC), visitor_.public_reset_packet_->rejected_sequence_number); EXPECT_EQ("4.31.198.44", IPAddressToString(visitor_.public_reset_packet_-> @@ -2897,18 +2872,18 @@ TEST_P(QuicFramerTest, FecPacket) { EXPECT_EQ(0u, visitor_.ack_frames_.size()); ASSERT_EQ(1, visitor_.fec_count_); const QuicFecData& fec_data = *visitor_.fec_data_[0]; - EXPECT_EQ(GG_UINT64_C(0x0123456789ABB), fec_data.fec_group); + EXPECT_EQ(UINT64_C(0x0123456789ABB), fec_data.fec_group); EXPECT_EQ("abcdefghijklmnop", fec_data.redundancy); } TEST_P(QuicFramerTest, BuildPaddingFramePacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = UINT64_C(0x123456789ABC); header.fec_group = 0; QuicPaddingFrame padding_frame; @@ -2949,13 +2924,13 @@ TEST_P(QuicFramerTest, BuildPaddingFramePacket) { TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; header.entropy_flag = false; header.public_header.sequence_number_length = PACKET_4BYTE_SEQUENCE_NUMBER; - header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); + header.packet_sequence_number = UINT64_C(0x123456789ABC); header.fec_group = 0; QuicPaddingFrame padding_frame; @@ -2995,13 +2970,13 @@ TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) { TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; header.entropy_flag = false; header.public_header.sequence_number_length = PACKET_2BYTE_SEQUENCE_NUMBER; - header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); + header.packet_sequence_number = UINT64_C(0x123456789ABC); header.fec_group = 0; QuicPaddingFrame padding_frame; @@ -3041,13 +3016,13 @@ TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) { TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = false; header.entropy_flag = false; header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; - header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); + header.packet_sequence_number = UINT64_C(0x123456789ABC); header.fec_group = 0; QuicPaddingFrame padding_frame; @@ -3087,19 +3062,16 @@ TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) { TEST_P(QuicFramerTest, BuildStreamFramePacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = UINT64_C(0x77123456789ABC); header.fec_group = 0; - 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!"); + QuicStreamFrame stream_frame(0x01020304, true, UINT64_C(0xBA98FEDC32107654), + StringPiece("hello world!")); QuicFrames frames; frames.push_back(QuicFrame(&stream_frame)); @@ -3139,20 +3111,17 @@ TEST_P(QuicFramerTest, BuildStreamFramePacket) { TEST_P(QuicFramerTest, BuildStreamFramePacketInFecGroup) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = UINT64_C(0x77123456789ABC); header.is_in_fec_group = IN_FEC_GROUP; - header.fec_group = GG_UINT64_C(0x77123456789ABC); + header.fec_group = 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!"); + QuicStreamFrame stream_frame(0x01020304, true, UINT64_C(0xBA98FEDC32107654), + StringPiece("hello world!")); QuicFrames frames; frames.push_back(QuicFrame(&stream_frame)); @@ -3189,19 +3158,16 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketInFecGroup) { TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = true; header.fec_flag = false; header.entropy_flag = true; - header.packet_sequence_number = GG_UINT64_C(0x77123456789ABC); + header.packet_sequence_number = UINT64_C(0x77123456789ABC); header.fec_group = 0; - 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!"); + QuicStreamFrame stream_frame(0x01020304, true, UINT64_C(0xBA98FEDC32107654), + StringPiece("hello world!")); QuicFrames frames; frames.push_back(QuicFrame(&stream_frame)); @@ -3275,7 +3241,7 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) { TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) { QuicPacketPublicHeader header; - header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.connection_id = UINT64_C(0xFEDCBA9876543210); header.reset_flag = false; header.version_flag = true; @@ -3310,19 +3276,19 @@ TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) { TEST_P(QuicFramerTest, BuildAckFramePacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = UINT64_C(0x770123456789AA8); header.fec_group = 0; QuicAckFrame ack_frame; ack_frame.entropy_hash = 0x43; - ack_frame.largest_observed = GG_UINT64_C(0x770123456789ABF); + ack_frame.largest_observed = UINT64_C(0x770123456789ABF); ack_frame.delta_time_largest_observed = QuicTime::Delta::Zero(); - ack_frame.missing_packets.insert(GG_UINT64_C(0x770123456789ABE)); + ack_frame.missing_packets.insert(UINT64_C(0x770123456789ABE)); QuicFrames frames; frames.push_back(QuicFrame(&ack_frame)); @@ -3371,12 +3337,12 @@ TEST_P(QuicFramerTest, BuildAckFramePacket) { TEST_P(QuicFramerTest, BuildTruncatedAckFrameLargePacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = UINT64_C(0x770123456789AA8); header.fec_group = 0; QuicAckFrame ack_frame; @@ -3480,12 +3446,12 @@ TEST_P(QuicFramerTest, BuildTruncatedAckFrameLargePacket) { TEST_P(QuicFramerTest, BuildTruncatedAckFrameSmallPacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = UINT64_C(0x770123456789AA8); header.fec_group = 0; QuicAckFrame ack_frame; @@ -3543,17 +3509,17 @@ TEST_P(QuicFramerTest, BuildTruncatedAckFrameSmallPacket) { TEST_P(QuicFramerTest, BuildStopWaitingPacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = 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); + stop_waiting_frame.least_unacked = UINT64_C(0x770123456789AA0); QuicFrames frames; frames.push_back(QuicFrame(&stop_waiting_frame)); @@ -3594,12 +3560,12 @@ TEST_P(QuicFramerTest, BuildRstFramePacketQuicVersion24) { } QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = UINT64_C(0x123456789ABC); header.fec_group = 0; QuicRstStreamFrame rst_frame; @@ -3656,12 +3622,12 @@ TEST_P(QuicFramerTest, BuildRstFramePacketQuic) { } QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = UINT64_C(0x123456789ABC); header.fec_group = 0; QuicRstStreamFrame rst_frame; @@ -3707,12 +3673,12 @@ TEST_P(QuicFramerTest, BuildRstFramePacketQuic) { TEST_P(QuicFramerTest, BuildCloseFramePacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = UINT64_C(0x123456789ABC); header.fec_group = 0; QuicConnectionCloseFrame close_frame; @@ -3757,12 +3723,12 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) { TEST_P(QuicFramerTest, BuildGoAwayPacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = UINT64_C(0x123456789ABC); header.fec_group = 0; QuicGoAwayFrame goaway_frame; @@ -3810,12 +3776,12 @@ TEST_P(QuicFramerTest, BuildGoAwayPacket) { TEST_P(QuicFramerTest, BuildWindowUpdatePacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = UINT64_C(0x123456789ABC); header.fec_group = 0; QuicWindowUpdateFrame window_update_frame; @@ -3856,12 +3822,12 @@ TEST_P(QuicFramerTest, BuildWindowUpdatePacket) { TEST_P(QuicFramerTest, BuildBlockedPacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = UINT64_C(0x123456789ABC); header.fec_group = 0; QuicBlockedFrame blocked_frame; @@ -3898,12 +3864,12 @@ TEST_P(QuicFramerTest, BuildBlockedPacket) { TEST_P(QuicFramerTest, BuildPingPacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = UINT64_C(0x123456789ABC); header.fec_group = 0; QuicPingFrame ping_frame; @@ -3935,13 +3901,55 @@ TEST_P(QuicFramerTest, BuildPingPacket) { arraysize(packet)); } +// Test that the MTU discovery packet is serialized correctly as a PING packet. +TEST_P(QuicFramerTest, BuildMtuDiscoveryPacket) { + QuicPacketHeader header; + header.public_header.connection_id = 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 = UINT64_C(0x123456789ABC); + header.fec_group = 0; + + QuicMtuDiscoveryFrame mtu_discovery_frame; + + QuicFrames frames; + frames.push_back(QuicFrame(&mtu_discovery_frame)); + + // clang-format off + 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, + }; + // clang-format on + + scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(packet), + arraysize(packet)); +} + TEST_P(QuicFramerTest, BuildPublicResetPacket) { QuicPublicResetPacket reset_packet; - reset_packet.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + reset_packet.public_header.connection_id = 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.rejected_sequence_number = UINT64_C(0x123456789ABC); + reset_packet.nonce_proof = UINT64_C(0xABCDEF0123456789); unsigned char packet[] = { // public flags (public reset, 8 byte ConnectionId) @@ -3980,11 +3988,11 @@ TEST_P(QuicFramerTest, BuildPublicResetPacket) { TEST_P(QuicFramerTest, BuildPublicResetPacketWithClientAddress) { QuicPublicResetPacket reset_packet; - reset_packet.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + reset_packet.public_header.connection_id = 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.rejected_sequence_number = UINT64_C(0x123456789ABC); + reset_packet.nonce_proof = UINT64_C(0xABCDEF0123456789); reset_packet.client_address = IPEndPoint(Loopback4(), 0x1234); unsigned char packet[] = { @@ -4032,14 +4040,14 @@ TEST_P(QuicFramerTest, BuildPublicResetPacketWithClientAddress) { TEST_P(QuicFramerTest, BuildFecPacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; header.public_header.version_flag = false; header.fec_flag = true; header.entropy_flag = true; - header.packet_sequence_number = (GG_UINT64_C(0x123456789ABC)); + header.packet_sequence_number = (UINT64_C(0x123456789ABC)); header.is_in_fec_group = IN_FEC_GROUP; - header.fec_group = GG_UINT64_C(0x123456789ABB);; + header.fec_group = UINT64_C(0x123456789ABB); QuicFecData fec_data; fec_data.fec_group = 1; @@ -4075,7 +4083,7 @@ TEST_P(QuicFramerTest, BuildFecPacket) { } TEST_P(QuicFramerTest, EncryptPacket) { - QuicPacketSequenceNumber sequence_number = GG_UINT64_C(0x123456789ABC); + QuicPacketSequenceNumber sequence_number = UINT64_C(0x123456789ABC); unsigned char packet[] = { // public flags (8 byte connection_id) 0x3C, @@ -4101,7 +4109,7 @@ TEST_P(QuicFramerTest, EncryptPacket) { AsChars(packet), arraysize(packet), false, PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER)); char buffer[kMaxPacketSize]; - scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket( + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPayload( ENCRYPTION_NONE, sequence_number, *raw, buffer, kMaxPacketSize)); ASSERT_TRUE(encrypted.get() != nullptr); @@ -4109,7 +4117,7 @@ TEST_P(QuicFramerTest, EncryptPacket) { } TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) { - QuicPacketSequenceNumber sequence_number = GG_UINT64_C(0x123456789ABC); + QuicPacketSequenceNumber sequence_number = UINT64_C(0x123456789ABC); unsigned char packet[] = { // public flags (version, 8 byte connection_id) 0x3D, @@ -4137,7 +4145,7 @@ TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) { AsChars(packet), arraysize(packet), false, PACKET_8BYTE_CONNECTION_ID, kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER)); char buffer[kMaxPacketSize]; - scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket( + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPayload( ENCRYPTION_NONE, sequence_number, *raw, buffer, kMaxPacketSize)); ASSERT_TRUE(encrypted.get() != nullptr); @@ -4146,12 +4154,12 @@ TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) { TEST_P(QuicFramerTest, AckTruncationLargePacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = UINT64_C(0x123456789ABC); header.fec_group = 0; // Create a packet with just the ack. @@ -4167,8 +4175,8 @@ TEST_P(QuicFramerTest, AckTruncationLargePacket) { ASSERT_TRUE(raw_ack_packet != nullptr); char buffer[kMaxPacketSize]; scoped_ptr<QuicEncryptedPacket> ack_packet( - framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, - *raw_ack_packet, buffer, kMaxPacketSize)); + framer_.EncryptPayload(ENCRYPTION_NONE, header.packet_sequence_number, + *raw_ack_packet, buffer, kMaxPacketSize)); // 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()); @@ -4186,12 +4194,12 @@ TEST_P(QuicFramerTest, AckTruncationLargePacket) { TEST_P(QuicFramerTest, AckTruncationSmallPacket) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = UINT64_C(0x123456789ABC); header.fec_group = 0; // Create a packet with just the ack. @@ -4207,8 +4215,8 @@ TEST_P(QuicFramerTest, AckTruncationSmallPacket) { ASSERT_TRUE(raw_ack_packet != nullptr); char buffer[kMaxPacketSize]; scoped_ptr<QuicEncryptedPacket> ack_packet( - framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, - *raw_ack_packet, buffer, kMaxPacketSize)); + framer_.EncryptPayload(ENCRYPTION_NONE, header.packet_sequence_number, + *raw_ack_packet, buffer, kMaxPacketSize)); // 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()); @@ -4226,12 +4234,12 @@ TEST_P(QuicFramerTest, AckTruncationSmallPacket) { TEST_P(QuicFramerTest, CleanTruncation) { QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.connection_id = 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.packet_sequence_number = UINT64_C(0x123456789ABC); header.fec_group = 0; QuicAckFrame ack_frame; @@ -4252,8 +4260,8 @@ TEST_P(QuicFramerTest, CleanTruncation) { char buffer[kMaxPacketSize]; scoped_ptr<QuicEncryptedPacket> ack_packet( - framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, - *raw_ack_packet, buffer, kMaxPacketSize)); + framer_.EncryptPayload(ENCRYPTION_NONE, header.packet_sequence_number, + *raw_ack_packet, buffer, kMaxPacketSize)); // Now make sure we can turn our ack packet back into an ack frame. ASSERT_TRUE(framer_.ProcessPacket(*ack_packet)); @@ -4406,5 +4414,79 @@ TEST_P(QuicFramerTest, StopPacketProcessing) { EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); } +static char kTestString[] = "At least 20 characters."; +static QuicStreamId kTestQuicStreamId = 1; +static bool ExpectedStreamFrame(const QuicStreamFrame& frame) { + return frame.stream_id == kTestQuicStreamId && !frame.fin && + frame.offset == 0 && frame.data == kTestString; + // FIN is hard-coded false in ConstructEncryptedPacket. + // Offset 0 is hard-coded in ConstructEncryptedPacket. +} + +// Verify that the packet returned by ConstructEncryptedPacket() can be properly +// parsed by the framer. +TEST_P(QuicFramerTest, ConstructEncryptedPacket) { + // Since we are using ConstructEncryptedPacket, we have to set the framer's + // crypto to be Null. + framer_.SetDecrypter(ENCRYPTION_NONE, QuicDecrypter::Create(kNULL)); + framer_.SetEncrypter(ENCRYPTION_NONE, QuicEncrypter::Create(kNULL)); + + scoped_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket( + 42, false, false, kTestQuicStreamId, kTestString, + PACKET_8BYTE_CONNECTION_ID, PACKET_6BYTE_SEQUENCE_NUMBER)); + + MockFramerVisitor visitor; + framer_.set_visitor(&visitor); + EXPECT_CALL(visitor, OnPacket()).Times(1); + EXPECT_CALL(visitor, OnUnauthenticatedPublicHeader(_)) + .Times(1) + .WillOnce(Return(true)); + EXPECT_CALL(visitor, OnUnauthenticatedHeader(_)) + .Times(1) + .WillOnce(Return(true)); + EXPECT_CALL(visitor, OnPacketHeader(_)).Times(1).WillOnce(Return(true)); + EXPECT_CALL(visitor, OnDecryptedPacket(_)).Times(1); + EXPECT_CALL(visitor, OnError(_)).Times(0); + EXPECT_CALL(visitor, OnStreamFrame(_)).Times(0); + EXPECT_CALL(visitor, OnStreamFrame(Truly(ExpectedStreamFrame))).Times(1); + EXPECT_CALL(visitor, OnAckFrame(_)).Times(0); + EXPECT_CALL(visitor, OnPacketComplete()).Times(1); + + EXPECT_TRUE(framer_.ProcessPacket(*packet)); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); +} + +// Verify that the packet returned by ConstructMisFramedEncryptedPacket() +// does cause the framer to return an error. +TEST_P(QuicFramerTest, ConstructMisFramedEncryptedPacket) { + // Since we are using ConstructEncryptedPacket, we have to set the framer's + // crypto to be Null. + framer_.SetDecrypter(ENCRYPTION_NONE, QuicDecrypter::Create(kNULL)); + framer_.SetEncrypter(ENCRYPTION_NONE, QuicEncrypter::Create(kNULL)); + + scoped_ptr<QuicEncryptedPacket> packet(ConstructMisFramedEncryptedPacket( + 42, false, false, kTestQuicStreamId, kTestString, + PACKET_8BYTE_CONNECTION_ID, PACKET_6BYTE_SEQUENCE_NUMBER, nullptr)); + + MockFramerVisitor visitor; + framer_.set_visitor(&visitor); + EXPECT_CALL(visitor, OnPacket()).Times(1); + EXPECT_CALL(visitor, OnUnauthenticatedPublicHeader(_)) + .Times(1) + .WillOnce(Return(true)); + EXPECT_CALL(visitor, OnUnauthenticatedHeader(_)) + .Times(1) + .WillOnce(Return(true)); + EXPECT_CALL(visitor, OnPacketHeader(_)).Times(0); + EXPECT_CALL(visitor, OnDecryptedPacket(_)).Times(1); + EXPECT_CALL(visitor, OnError(_)).Times(1); + EXPECT_CALL(visitor, OnStreamFrame(_)).Times(0); + EXPECT_CALL(visitor, OnAckFrame(_)).Times(0); + EXPECT_CALL(visitor, OnPacketComplete()).Times(0); + + EXPECT_FALSE(framer_.ProcessPacket(*packet)); + EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error()); +} + } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_headers_stream.cc b/chromium/net/quic/quic_headers_stream.cc index f1d972c5cfd..0b57780d544 100644 --- a/chromium/net/quic/quic_headers_stream.cc +++ b/chromium/net/quic/quic_headers_stream.cc @@ -5,7 +5,7 @@ #include "net/quic/quic_headers_stream.h" #include "base/strings/stringprintf.h" -#include "net/quic/quic_session.h" +#include "net/quic/quic_spdy_session.h" using base::StringPiece; using std::string; @@ -25,8 +25,7 @@ class QuicHeadersStream::SpdyFramerVisitor : public SpdyFramerVisitorInterface, public SpdyFramerDebugVisitorInterface { public: - SpdyFramerVisitor(SpdyMajorVersion spdy_version, QuicHeadersStream* stream) - : spdy_version_(spdy_version), stream_(stream) {} + SpdyFramerVisitor(QuicHeadersStream* stream) : stream_(stream) {} // SpdyFramerVisitorInterface implementation void OnSynStream(SpdyStreamId stream_id, @@ -109,6 +108,8 @@ class QuicHeadersStream::SpdyFramerVisitor void OnHeaders(SpdyStreamId stream_id, bool has_priority, SpdyPriority priority, + SpdyStreamId parent_stream_id, + bool exclusive, bool fin, bool end) override { if (!stream_->IsConnected()) { @@ -121,8 +122,7 @@ class QuicHeadersStream::SpdyFramerVisitor } } - void OnWindowUpdate(SpdyStreamId stream_id, - uint32 delta_window_size) override { + void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override { CloseConnection("SPDY WINDOW_UPDATE frame received."); } @@ -163,19 +163,19 @@ class QuicHeadersStream::SpdyFramerVisitor } private: - SpdyMajorVersion spdy_version_; QuicHeadersStream* stream_; DISALLOW_COPY_AND_ASSIGN(SpdyFramerVisitor); }; -QuicHeadersStream::QuicHeadersStream(QuicSession* session) +QuicHeadersStream::QuicHeadersStream(QuicSpdySession* session) : ReliableQuicStream(kHeadersStreamId, session), + spdy_session_(session), stream_id_(kInvalidStreamId), fin_(false), frame_len_(0), - spdy_framer_(SPDY4), - spdy_framer_visitor_(new SpdyFramerVisitor(SPDY4, this)) { + spdy_framer_(HTTP2), + spdy_framer_visitor_(new SpdyFramerVisitor(this)) { spdy_framer_.set_visitor(spdy_framer_visitor_.get()); spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get()); // The headers stream is exempt from connection level flow control. @@ -223,7 +223,7 @@ void QuicHeadersStream::OnSynStream(SpdyStreamId stream_id, DCHECK_EQ(kInvalidStreamId, stream_id_); stream_id_ = stream_id; fin_ = fin; - session()->OnStreamHeadersPriority(stream_id, priority); + spdy_session_->OnStreamHeadersPriority(stream_id, priority); } void QuicHeadersStream::OnSynReply(SpdyStreamId stream_id, bool fin) { @@ -245,13 +245,13 @@ void QuicHeadersStream::OnControlFrameHeaderData(SpdyStreamId stream_id, if (len == 0) { DCHECK_NE(0u, stream_id_); DCHECK_NE(0u, frame_len_); - session()->OnStreamHeadersComplete(stream_id_, fin_, frame_len_); + spdy_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)); + spdy_session_->OnStreamHeaders(stream_id_, StringPiece(header_data, len)); } } diff --git a/chromium/net/quic/quic_headers_stream.h b/chromium/net/quic/quic_headers_stream.h index a9184731ae2..f0838de8485 100644 --- a/chromium/net/quic/quic_headers_stream.h +++ b/chromium/net/quic/quic_headers_stream.h @@ -14,13 +14,15 @@ namespace net { +class QuicSpdySession; + // 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 +// over a reserved reliable stream with the id 3. 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); + explicit QuicHeadersStream(QuicSpdySession* session); ~QuicHeadersStream() override; // Writes |headers| for |stream_id| in a SYN_STREAM or SYN_REPLY @@ -67,6 +69,8 @@ class NET_EXPORT_PRIVATE QuicHeadersStream : public ReliableQuicStream { // Returns true if the session is still connected. bool IsConnected(); + QuicSpdySession* spdy_session_; + // Data about the stream whose headers are being processed. QuicStreamId stream_id_; bool fin_; diff --git a/chromium/net/quic/quic_headers_stream_test.cc b/chromium/net/quic/quic_headers_stream_test.cc index 9f3b59f2d22..4e417b2238e 100644 --- a/chromium/net/quic/quic_headers_stream_test.cc +++ b/chromium/net/quic/quic_headers_stream_test.cc @@ -7,7 +7,7 @@ #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_spdy_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_protocol.h" @@ -55,10 +55,16 @@ class MockVisitor : public SpdyFramerVisitorInterface { MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack)); MOCK_METHOD2(OnGoAway, void(SpdyStreamId last_accepted_stream_id, SpdyGoAwayStatus status)); - MOCK_METHOD5(OnHeaders, void(SpdyStreamId stream_id, bool has_priority, - SpdyPriority priority, bool fin, bool end)); - MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId stream_id, - uint32 delta_window_size)); + MOCK_METHOD7(OnHeaders, + void(SpdyStreamId stream_id, + bool has_priority, + SpdyPriority priority, + SpdyStreamId parent_stream_id, + bool exclusive, + bool fin, + bool end)); + MOCK_METHOD2(OnWindowUpdate, + void(SpdyStreamId stream_id, int delta_window_size)); MOCK_METHOD2(OnCredentialFrameData, bool(const char* credential_data, size_t len)); MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id)); @@ -66,12 +72,11 @@ class MockVisitor : public SpdyFramerVisitorInterface { 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)); + MOCK_METHOD3(OnAltSvc, + void(SpdyStreamId stream_id, + StringPiece origin, + const SpdyAltSvcWireFormat::AlternativeServiceVector& + altsvc_vector)); MOCK_METHOD2(OnUnknownFrame, bool(SpdyStreamId stream_id, int frame_type)); }; @@ -107,9 +112,9 @@ class QuicHeadersStreamTest : public ::testing::TestWithParam<TestParams> { : connection_( new StrictMock<MockConnection>(perspective(), GetVersion())), session_(connection_), - headers_stream_(QuicSessionPeer::GetHeadersStream(&session_)), + headers_stream_(QuicSpdySessionPeer::GetHeadersStream(&session_)), body_("hello world"), - framer_(SPDY4) { + framer_(HTTP2) { headers_[":version"] = "HTTP/1.1"; headers_[":status"] = "200 Ok"; headers_["content-length"] = "11"; @@ -119,9 +124,9 @@ class QuicHeadersStreamTest : public ::testing::TestWithParam<TestParams> { VLOG(1) << GetParam(); } - QuicConsumedData SaveIov(const IOVector& data) { - const iovec* iov = data.iovec(); - int count = data.Capacity(); + QuicConsumedData SaveIov(const QuicIOVector& data) { + const iovec* iov = data.iov; + int count = data.iov_count; for (int i = 0 ; i < count; ++i) { saved_data_.append(static_cast<char*>(iov[i].iov_base), iov[i].iov_len); } @@ -159,11 +164,17 @@ class QuicHeadersStreamTest : public ::testing::TestWithParam<TestParams> { // Parse the outgoing data and check that it matches was was written. if (type == SYN_STREAM) { - EXPECT_CALL(visitor_, OnHeaders(stream_id, kHasPriority, priority, fin, - kFrameComplete)); + EXPECT_CALL(visitor_, OnHeaders(stream_id, kHasPriority, priority, + /*parent_stream_id=*/0, + /*exclusive=*/false, + + fin, kFrameComplete)); } else { - EXPECT_CALL(visitor_, OnHeaders(stream_id, !kHasPriority, - /*priority=*/0, fin, kFrameComplete)); + EXPECT_CALL(visitor_, + OnHeaders(stream_id, !kHasPriority, + /*priority=*/0, + /*parent_stream_id=*/0, + /*exclusive=*/false, fin, kFrameComplete)); } EXPECT_CALL(visitor_, OnControlFrameHeaderData(stream_id, _, _)) .WillRepeatedly(WithArgs<1, 2>( @@ -207,7 +218,7 @@ class QuicHeadersStreamTest : public ::testing::TestWithParam<TestParams> { static const bool kHasPriority = true; StrictMock<MockConnection>* connection_; - StrictMock<MockSession> session_; + StrictMock<MockQuicSpdySession> session_; QuicHeadersStream* headers_stream_; SpdyHeaderBlock headers_; string body_; diff --git a/chromium/net/quic/quic_http_stream.cc b/chromium/net/quic/quic_http_stream.cc index cce1512c788..b516aba1387 100644 --- a/chromium/net/quic/quic_http_stream.cc +++ b/chromium/net/quic/quic_http_stream.cc @@ -5,7 +5,7 @@ #include "net/quic/quic_http_stream.h" #include "base/callback_helpers.h" -#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros.h" #include "base/strings/stringprintf.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" @@ -24,8 +24,6 @@ namespace net { -static const size_t kHeaderBufInitialSize = 4096; - QuicHttpStream::QuicHttpStream(const base::WeakPtr<QuicClientSession>& session) : next_state_(STATE_NONE), session_(session), @@ -38,7 +36,6 @@ QuicHttpStream::QuicHttpStream(const base::WeakPtr<QuicClientSession>& session) response_info_(nullptr), response_status_(OK), response_headers_received_(false), - read_buf_(new GrowableIOBuffer()), closed_stream_received_bytes_(0), user_buffer_len_(0), weak_factory_(this) { @@ -306,25 +303,15 @@ void QuicHttpStream::SetPriority(RequestPriority priority) { priority_ = priority; } +void QuicHttpStream::OnHeadersAvailable(StringPiece headers) { + int rv = ParseResponseHeaders(headers); + if (rv != ERR_IO_PENDING && !callback_.is_null()) { + DoCallback(rv); + } +} + int QuicHttpStream::OnDataReceived(const char* data, int length) { DCHECK_NE(0, length); - // Are we still reading the response headers. - if (!response_headers_received_) { - // Grow the read buffer if necessary. - if (read_buf_->RemainingCapacity() < length) { - size_t additional_capacity = length - read_buf_->RemainingCapacity(); - if (additional_capacity < kHeaderBufInitialSize) - additional_capacity = kHeaderBufInitialSize; - read_buf_->SetCapacity(read_buf_->capacity() + additional_capacity); - } - memcpy(read_buf_->data(), data, length); - read_buf_->set_offset(read_buf_->offset() + length); - int rv = ParseResponseHeaders(); - if (rv != ERR_IO_PENDING && !callback_.is_null()) { - DoCallback(rv); - } - return OK; - } if (callback_.is_null()) { BufferResponseBody(data, length); @@ -529,22 +516,14 @@ int QuicHttpStream::DoSendBodyComplete(int rv) { return OK; } -int QuicHttpStream::ParseResponseHeaders() { - size_t read_buf_len = static_cast<size_t>(read_buf_->offset()); +int QuicHttpStream::ParseResponseHeaders(StringPiece headers_data) { SpdyFramer framer(GetSpdyVersion()); SpdyHeaderBlock headers; - char* data = read_buf_->StartOfBuffer(); - size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_->offset(), - &headers); - - if (len == 0) { - return ERR_IO_PENDING; - } - - // Save the remaining received data. - size_t delta = read_buf_len - len; - if (delta > 0) { - BufferResponseBody(data + len, delta); + size_t len = framer.ParseHeaderBlockInBuffer(headers_data.data(), + headers_data.length(), &headers); + if (len == 0 || len != headers_data.length()) { + DLOG(WARNING) << "Invalid headers"; + return ERR_QUIC_PROTOCOL_ERROR; } // The URLRequest logs these headers, so only log to the QuicSession's diff --git a/chromium/net/quic/quic_http_stream.h b/chromium/net/quic/quic_http_stream.h index 12ca814f4a3..e78e57962f6 100644 --- a/chromium/net/quic/quic_http_stream.h +++ b/chromium/net/quic/quic_http_stream.h @@ -60,6 +60,7 @@ class NET_EXPORT_PRIVATE QuicHttpStream : void SetPriority(RequestPriority priority) override; // QuicReliableClientStream::Delegate implementation + void OnHeadersAvailable(StringPiece headers) override; int OnDataReceived(const char* data, int length) override; void OnClose(QuicErrorCode error) override; void OnError(int error) override; @@ -97,7 +98,7 @@ class NET_EXPORT_PRIVATE QuicHttpStream : int DoReadResponseHeaders(); int DoReadResponseHeadersComplete(int rv); - int ParseResponseHeaders(); + int ParseResponseHeaders(StringPiece headers); void BufferResponseBody(const char* data, int length); @@ -139,9 +140,6 @@ class NET_EXPORT_PRIVATE QuicHttpStream : // Serialized HTTP request. std::string request_; - // Buffer into which response header data is read. - scoped_refptr<GrowableIOBuffer> read_buf_; - // We buffer the response body as it arrives asynchronously from the stream. // TODO(rch): This is infinite buffering, which is bad. std::list<scoped_refptr<IOBufferWithSize> > response_body_; diff --git a/chromium/net/quic/quic_http_stream_test.cc b/chromium/net/quic/quic_http_stream_test.cc index fb4d6ae95e2..412bd23ef2a 100644 --- a/chromium/net/quic/quic_http_stream_test.cc +++ b/chromium/net/quic/quic_http_stream_test.cc @@ -6,6 +6,7 @@ #include <vector> +#include "base/thread_task_runner_handle.h" #include "net/base/chunked_upload_data_stream.h" #include "net/base/elements_upload_data_stream.h" #include "net/base/net_errors.h" @@ -82,6 +83,8 @@ class AutoClosingStream : public QuicHttpStream { : QuicHttpStream(session) { } + void OnHeadersAvailable(StringPiece headers) override { Close(false); } + int OnDataReceived(const char* data, int length) override { Close(false); return OK; @@ -162,7 +165,8 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> { } bool AtEof() { - return socket_data_->at_read_eof() && socket_data_->at_write_eof(); + return socket_data_->AllReadDataConsumed() && + socket_data_->AllWriteDataConsumed(); } void ProcessPacket(scoped_ptr<QuicEncryptedPacket> packet) { @@ -186,6 +190,8 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> { socket->Connect(peer_addr_); runner_ = new TestTaskRunner(&clock_); send_algorithm_ = new MockSendAlgorithm(); + EXPECT_CALL(*send_algorithm_, InRecovery()).WillRepeatedly(Return(false)); + EXPECT_CALL(*send_algorithm_, InSlowStart()).WillRepeatedly(Return(false)); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).WillRepeatedly(Return(true)); EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly( @@ -206,15 +212,15 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> { connection_->set_visitor(&visitor_); connection_->SetSendAlgorithm(send_algorithm_); session_.reset(new QuicClientSession( - connection_, scoped_ptr<DatagramClientSocket>(socket), nullptr, + connection_, scoped_ptr<DatagramClientSocket>(socket), + /*stream_factory=*/nullptr, &crypto_client_stream_factory_, &transport_security_state_, make_scoped_ptr((QuicServerInfo*)nullptr), - /*cert_verify_flags=*/0, DefaultQuicConfig(), "CONNECTION_UNKNOWN", - base::TimeTicks::Now(), - base::MessageLoop::current()->message_loop_proxy().get(), nullptr)); - session_->InitializeSession( QuicServerId(kDefaultServerHostName, kDefaultServerPort, /*is_secure=*/false, PRIVACY_MODE_DISABLED), - &crypto_config_, &crypto_client_stream_factory_); + /*cert_verify_flags=*/0, DefaultQuicConfig(), &crypto_config_, + "CONNECTION_UNKNOWN", base::TimeTicks::Now(), + base::ThreadTaskRunnerHandle::Get().get(), nullptr)); + session_->Initialize(); session_->GetCryptoStream()->CryptoConnect(); EXPECT_TRUE(session_->IsCryptoHandshakeConfirmed()); stream_.reset(use_closing_stream_ ? @@ -398,13 +404,10 @@ TEST_P(QuicHttpStreamTest, GetRequestLargeResponse) { headers[":status"] = "200 OK"; headers[":version"] = "HTTP/1.1"; headers["content-type"] = "text/plain"; - headers["big6"] = std::string(10000, 'x'); // Lots of x's. + headers["big6"] = std::string(1000, 'x'); // Lots of x's. - std::string response = - SpdyUtils::SerializeUncompressedHeaders(headers, GetParam()); - EXPECT_LT(4096u, response.length()); - stream_->OnDataReceived(response.data(), response.length()); - stream_->OnClose(QUIC_NO_ERROR); + response_headers_ = headers; + ProcessPacket(ConstructResponseHeadersPacket(2, kFin)); // Now that the headers have been processed, the callback will return. EXPECT_EQ(OK, callback_.WaitForResult()); diff --git a/chromium/net/quic/quic_http_utils.cc b/chromium/net/quic/quic_http_utils.cc index 9a1c6bbb896..60fbd9a4a7f 100644 --- a/chromium/net/quic/quic_http_utils.cc +++ b/chromium/net/quic/quic_http_utils.cc @@ -20,15 +20,16 @@ NET_EXPORT_PRIVATE RequestPriority ConvertQuicPriorityToRequestPriority( IDLE : static_cast<RequestPriority>(HIGHEST - priority); } -base::Value* QuicRequestNetLogCallback(QuicStreamId stream_id, - const SpdyHeaderBlock* headers, - QuicPriority priority, - NetLogCaptureMode capture_mode) { - base::DictionaryValue* dict = static_cast<base::DictionaryValue*>( - SpdyHeaderBlockNetLogCallback(headers, capture_mode)); +scoped_ptr<base::Value> QuicRequestNetLogCallback( + QuicStreamId stream_id, + const SpdyHeaderBlock* headers, + QuicPriority priority, + NetLogCaptureMode capture_mode) { + scoped_ptr<base::DictionaryValue> dict(static_cast<base::DictionaryValue*>( + SpdyHeaderBlockNetLogCallback(headers, capture_mode).release())); dict->SetInteger("quic_priority", static_cast<int>(priority)); dict->SetInteger("quic_stream_id", static_cast<int>(stream_id)); - return dict; + return dict.Pass(); } } // namespace net diff --git a/chromium/net/quic/quic_http_utils.h b/chromium/net/quic/quic_http_utils.h index 6eafbc0f092..82306d63ce2 100644 --- a/chromium/net/quic/quic_http_utils.h +++ b/chromium/net/quic/quic_http_utils.h @@ -19,9 +19,8 @@ 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( +// Converts a SpdyHeaderBlock and priority into NetLog event parameters. +NET_EXPORT scoped_ptr<base::Value> QuicRequestNetLogCallback( QuicStreamId stream_id, const SpdyHeaderBlock* headers, QuicPriority priority, diff --git a/chromium/net/quic/quic_network_transaction_unittest.cc b/chromium/net/quic/quic_network_transaction_unittest.cc index b78bde6d58c..697e73a188f 100644 --- a/chromium/net/quic/quic_network_transaction_unittest.cc +++ b/chromium/net/quic/quic_network_transaction_unittest.cc @@ -252,6 +252,8 @@ class QuicNetworkTransactionTest session_ = new HttpNetworkSession(params_); session_->quic_stream_factory()->set_require_confirmation(false); + ASSERT_EQ(params_.quic_socket_receive_buffer_size, + session_->quic_stream_factory()->socket_receive_buffer_size()); } void CheckWasQuicResponse(const scoped_ptr<HttpNetworkTransaction>& trans) { @@ -331,18 +333,19 @@ class QuicNetworkTransactionTest void ExpectBrokenAlternateProtocolMapping() { const HostPortPair origin = HostPortPair::FromURL(request_.url); - const AlternativeService alternative_service = - http_server_properties_.GetAlternativeService(origin); - EXPECT_NE(UNINITIALIZED_ALTERNATE_PROTOCOL, alternative_service.protocol); + const AlternativeServiceVector alternative_service_vector = + http_server_properties_.GetAlternativeServices(origin); + EXPECT_EQ(1u, alternative_service_vector.size()); EXPECT_TRUE(http_server_properties_.IsAlternativeServiceBroken( - alternative_service)); + alternative_service_vector[0])); } void ExpectQuicAlternateProtocolMapping() { - const AlternativeService alternative_service = - http_server_properties_.GetAlternativeService( - HostPortPair::FromURL(request_.url)); - EXPECT_EQ(QUIC, alternative_service.protocol); + const HostPortPair origin = HostPortPair::FromURL(request_.url); + const AlternativeServiceVector alternative_service_vector = + http_server_properties_.GetAlternativeServices(origin); + EXPECT_EQ(1u, alternative_service_vector.size()); + EXPECT_EQ(QUIC, alternative_service_vector[0].protocol); } void AddHangingNonAlternateProtocolSocketData() { @@ -477,6 +480,48 @@ TEST_P(QuicNetworkTransactionTest, QuicProxy) { SendRequestAndExpectQuicResponseFromProxyOnPort("hello!", 70); } +// Regression test for https://crbug.com/492458. Test that for an HTTP +// connection through a QUIC proxy, the certificate exhibited by the proxy is +// checked against the proxy hostname, not the origin hostname. +TEST_P(QuicNetworkTransactionTest, QuicProxyWithCert) { + const std::string origin_host = "news.example.com"; + const std::string proxy_host = "www.example.org"; + + params_.enable_quic_for_proxies = true; + proxy_service_.reset( + ProxyService::CreateFixedFromPacResult("QUIC " + proxy_host + ":70")); + + maker_.set_hostname(origin_host); + 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); + mock_quic_data.AddSocketDataToFactory(&socket_factory_); + + scoped_refptr<X509Certificate> cert( + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem")); + ASSERT_TRUE(cert.get()); + // This certificate is valid for the proxy, but not for the origin. + bool common_name_fallback_used; + EXPECT_TRUE(cert->VerifyNameMatch(proxy_host, &common_name_fallback_used)); + EXPECT_FALSE(cert->VerifyNameMatch(origin_host, &common_name_fallback_used)); + ProofVerifyDetailsChromium verify_details; + verify_details.cert_verify_result.verified_cert = cert; + crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); + + request_.url = GURL("http://" + origin_host); + AddHangingNonAlternateProtocolSocketData(); + CreateSessionWithNextProtos(); + AddQuicAlternateProtocolMapping(MockCryptoClientStream::CONFIRM_HANDSHAKE); + SendRequestAndExpectQuicResponseFromProxyOnPort("hello!", 70); +} + TEST_P(QuicNetworkTransactionTest, ForceQuicWithErrorConnecting) { params_.origin_to_force_quic_on = HostPortPair::FromString("www.google.com:80"); @@ -779,6 +824,88 @@ TEST_P(QuicNetworkTransactionTest, UseAlternateProtocolForQuicForHttps) { SendRequestAndExpectHttpResponse("hello world"); } +class QuicAltSvcCertificateVerificationTest + : public QuicNetworkTransactionTest { + public: + void Run(bool valid) { + HostPortPair origin(valid ? "mail.example.org" : "invalid.example.org", + 443); + HostPortPair alternative("www.example.org", 443); + std::string url("https://"); + url.append(origin.host()); + url.append(":443"); + request_.url = GURL(url); + + maker_.set_hostname(origin.host()); + MockQuicData mock_quic_data; + mock_quic_data.AddWrite( + ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true, + GetRequestHeaders("GET", "https", "/"))); + 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); + mock_quic_data.AddSocketDataToFactory(&socket_factory_); + + scoped_refptr<X509Certificate> cert( + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem")); + ASSERT_TRUE(cert.get()); + bool common_name_fallback_used; + EXPECT_EQ(valid, + cert->VerifyNameMatch(origin.host(), &common_name_fallback_used)); + EXPECT_TRUE( + cert->VerifyNameMatch(alternative.host(), &common_name_fallback_used)); + ProofVerifyDetailsChromium verify_details; + verify_details.cert_verify_result.verified_cert = cert; + verify_details.cert_verify_result.is_issued_by_known_root = true; + crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); + crypto_client_stream_factory_.set_handshake_mode( + MockCryptoClientStream::CONFIRM_HANDSHAKE); + + // Connection to |origin| fails, so that success of |request| depends on + // connection to |alternate| only. + MockConnect refused_connect(ASYNC, ERR_CONNECTION_REFUSED); + StaticSocketDataProvider refused_data; + refused_data.set_connect_data(refused_connect); + socket_factory_.AddSocketDataProvider(&refused_data); + + CreateSessionWithNextProtos(); + AlternativeService alternative_service(QUIC, alternative); + session_->http_server_properties()->SetAlternativeService( + origin, alternative_service, 1.0); + 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); + rv = callback.WaitForResult(); + if (valid) { + EXPECT_EQ(OK, rv); + CheckWasQuicResponse(trans); + CheckResponsePort(trans, 443); + CheckResponseData(trans, "hello!"); + } else { + EXPECT_EQ(ERR_CONNECTION_REFUSED, rv); + } + } +}; + +INSTANTIATE_TEST_CASE_P(Version, + QuicAltSvcCertificateVerificationTest, + ::testing::ValuesIn(QuicSupportedVersions())); + +TEST_P(QuicAltSvcCertificateVerificationTest, + RequestSucceedsWithValidCertificate) { + Run(true); +} + +TEST_P(QuicAltSvcCertificateVerificationTest, + RequestFailsWithInvalidCertificate) { + Run(false); +} + TEST_P(QuicNetworkTransactionTest, HungAlternateProtocol) { crypto_client_stream_factory_.set_handshake_mode( MockCryptoClientStream::COLD_START); @@ -823,18 +950,18 @@ TEST_P(QuicNetworkTransactionTest, HungAlternateProtocol) { // Run the first request. http_data.StopAfter(arraysize(http_reads) + arraysize(http_writes)); SendRequestAndExpectHttpResponse("hello world"); - ASSERT_TRUE(http_data.at_read_eof()); - ASSERT_TRUE(http_data.at_write_eof()); + ASSERT_TRUE(http_data.AllReadDataConsumed()); + ASSERT_TRUE(http_data.AllWriteDataConsumed()); // Now run the second request in which the QUIC socket hangs, // and verify the the transaction continues over HTTP. http_data2.StopAfter(arraysize(http_reads) + arraysize(http_writes)); SendRequestAndExpectHttpResponse("hello world"); - ASSERT_TRUE(http_data2.at_read_eof()); - ASSERT_TRUE(http_data2.at_write_eof()); - ASSERT_TRUE(!quic_data.at_read_eof()); - ASSERT_TRUE(!quic_data.at_write_eof()); + ASSERT_TRUE(http_data2.AllReadDataConsumed()); + ASSERT_TRUE(http_data2.AllWriteDataConsumed()); + ASSERT_TRUE(!quic_data.AllReadDataConsumed()); + ASSERT_TRUE(!quic_data.AllWriteDataConsumed()); } TEST_P(QuicNetworkTransactionTest, ZeroRTTWithHttpRace) { @@ -1104,8 +1231,8 @@ TEST_P(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) { ExpectBrokenAlternateProtocolMapping(); - EXPECT_TRUE(quic_data.at_read_eof()); - EXPECT_TRUE(quic_data.at_write_eof()); + EXPECT_TRUE(quic_data.AllReadDataConsumed()); + EXPECT_TRUE(quic_data.AllWriteDataConsumed()); } TEST_P(QuicNetworkTransactionTest, DISABLED_HangingZeroRttFallback) { diff --git a/chromium/net/quic/quic_packet_creator.cc b/chromium/net/quic/quic_packet_creator.cc index 9deb614f130..7474b0b17b7 100644 --- a/chromium/net/quic/quic_packet_creator.cc +++ b/chromium/net/quic/quic_packet_creator.cc @@ -78,11 +78,13 @@ QuicPacketCreator::QuicPacketCreator(QuicConnectionId connection_id, should_fec_protect_(false), fec_group_number_(0), send_version_in_packet_(framer->perspective() == Perspective::IS_CLIENT), + max_packet_length_(0), max_packets_per_fec_group_(kDefaultMaxPacketsPerFecGroup), 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) { + packet_size_(0), + needs_padding_(false) { SetMaxPacketLength(kDefaultMaxPacketSize); } @@ -103,9 +105,20 @@ void QuicPacketCreator::SetEncrypter(EncryptionLevel level, max_plaintext_size_ = framer_->GetMaxPlaintextSize(max_packet_length_); } -void QuicPacketCreator::SetMaxPacketLength(QuicByteCount length) { +bool QuicPacketCreator::CanSetMaxPacketLength() const { // |max_packet_length_| should not be changed mid-packet or mid-FEC group. - DCHECK(fec_group_.get() == nullptr && queued_frames_.empty()); + return fec_group_.get() == nullptr && queued_frames_.empty(); +} + +void QuicPacketCreator::SetMaxPacketLength(QuicByteCount length) { + DCHECK(CanSetMaxPacketLength()); + + // Avoid recomputing |max_plaintext_size_| if the length does not actually + // change. + if (length == max_packet_length_) { + return; + } + max_packet_length_ = length; max_plaintext_size_ = framer_->GetMaxPlaintextSize(max_packet_length_); } @@ -124,6 +137,15 @@ bool QuicPacketCreator::ShouldSendFec(bool force_close) const { fec_group_->NumReceivedPackets() >= max_packets_per_fec_group_); } +void QuicPacketCreator::ResetFecGroup() { + if (HasPendingFrames()) { + LOG_IF(DFATAL, packet_size_ != 0) + << "Cannot reset FEC group with pending frames."; + return; + } + fec_group_.reset(nullptr); +} + bool QuicPacketCreator::IsFecGroupOpen() const { return fec_group_.get() != nullptr; } @@ -172,7 +194,7 @@ InFecGroup QuicPacketCreator::MaybeUpdateLengthsAndStartFec() { } if (!queued_frames_.empty()) { // Don't change creator state if there are frames queued. - return fec_group_.get() == nullptr ? NOT_IN_FEC_GROUP : IN_FEC_GROUP; + return NOT_IN_FEC_GROUP; } // Update sequence number length only on packet and FEC group boundaries. @@ -239,13 +261,16 @@ size_t QuicPacketCreator::StreamFramePacketOverhead( } size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id, - const IOVector& data, + const QuicIOVector& iov, + size_t iov_offset, QuicStreamOffset offset, bool fin, - QuicFrame* frame) { + QuicFrame* frame, + scoped_ptr<char[]>* buffer) { DCHECK_GT(max_packet_length_, StreamFramePacketOverhead( connection_id_length_, kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, offset, IN_FEC_GROUP)); + DCHECK(buffer); InFecGroup is_in_fec_group = MaybeUpdateLengthsAndStartFec(); @@ -254,28 +279,49 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id, << " MinStreamFrameSize: " << QuicFramer::GetMinStreamFrameSize(id, offset, true, is_in_fec_group); - if (data.Empty()) { + if (iov_offset == iov.total_length) { 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)); + *frame = QuicFrame(new QuicStreamFrame(id, true, offset, StringPiece())); return 0; } - const size_t data_size = data.TotalBufferSize(); + const size_t data_size = iov.total_length - iov_offset; size_t min_frame_size = QuicFramer::GetMinStreamFrameSize( 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; - frame_data.AppendIovecAtMostBytes(data.iovec(), data.Size(), - bytes_consumed); - DCHECK_EQ(frame_data.TotalBufferSize(), bytes_consumed); - *frame = QuicFrame(new QuicStreamFrame(id, set_fin, offset, frame_data)); + buffer->reset(new char[bytes_consumed]); + CopyToBuffer(iov, iov_offset, bytes_consumed, buffer->get()); + *frame = QuicFrame(new QuicStreamFrame( + id, set_fin, offset, StringPiece(buffer->get(), bytes_consumed))); return bytes_consumed; } +// static +void QuicPacketCreator::CopyToBuffer(const QuicIOVector& iov, + size_t iov_offset, + size_t length, + char* buffer) { + int iovnum = 0; + while (iovnum < iov.iov_count && iov_offset >= iov.iov[iovnum].iov_len) { + iov_offset -= iov.iov[iovnum].iov_len; + ++iovnum; + } + while (iovnum < iov.iov_count && length > 0) { + const size_t copy_len = min(length, iov.iov[iovnum].iov_len - iov_offset); + memcpy(buffer, static_cast<char*>(iov.iov[iovnum].iov_base) + iov_offset, + copy_len); + iov_offset = 0; + length -= copy_len; + buffer += copy_len; + ++iovnum; + } + LOG_IF(DFATAL, length > 0) << "Failed to copy entire length to buffer."; +} + SerializedPacket QuicPacketCreator::ReserializeAllFrames( const RetransmittableFrames& frames, QuicSequenceNumberLength original_length, @@ -286,6 +332,7 @@ SerializedPacket QuicPacketCreator::ReserializeAllFrames( const QuicSequenceNumberLength saved_next_length = next_sequence_number_length_; const bool saved_should_fec_protect = should_fec_protect_; + const bool needs_padding = needs_padding_; const EncryptionLevel default_encryption_level = encryption_level_; // Temporarily set the sequence number length, stop FEC protection, @@ -294,6 +341,7 @@ SerializedPacket QuicPacketCreator::ReserializeAllFrames( next_sequence_number_length_ = original_length; should_fec_protect_ = false; encryption_level_ = frames.encryption_level(); + needs_padding_ = frames.needs_padding(); // Serialize the packet and restore the FEC and sequence number length state. SerializedPacket serialized_packet = @@ -301,6 +349,7 @@ SerializedPacket QuicPacketCreator::ReserializeAllFrames( sequence_number_length_ = saved_length; next_sequence_number_length_ = saved_next_length; should_fec_protect_ = saved_should_fec_protect; + needs_padding_ = needs_padding; encryption_level_ = default_encryption_level; return serialized_packet; @@ -313,7 +362,7 @@ SerializedPacket QuicPacketCreator::SerializeAllFrames(const QuicFrames& frames, LOG_IF(DFATAL, frames.empty()) << "Attempt to serialize empty packet"; for (const QuicFrame& frame : frames) { - bool success = AddFrame(frame, false); + bool success = AddFrame(frame, false, false, nullptr); DCHECK(success); } SerializedPacket packet = SerializePacket(buffer, buffer_len); @@ -363,7 +412,22 @@ size_t QuicPacketCreator::PacketSize() const { } bool QuicPacketCreator::AddSavedFrame(const QuicFrame& frame) { - return AddFrame(frame, true); + return AddFrame(frame, + /*save_retransmittable_frames=*/true, + /*needs_padding=*/false, nullptr); +} + +bool QuicPacketCreator::AddSavedFrame(const QuicFrame& frame, char* buffer) { + return AddFrame(frame, + /*save_retransmittable_frames=*/true, + /*needs_padding=*/false, buffer); +} + +bool QuicPacketCreator::AddPaddedSavedFrame(const QuicFrame& frame, + char* buffer) { + return AddFrame(frame, + /*save_retransmittable_frames=*/true, + /*needs_padding=*/true, buffer); } SerializedPacket QuicPacketCreator::SerializePacket( @@ -399,10 +463,14 @@ SerializedPacket QuicPacketCreator::SerializePacket( packet.reset(framer_->BuildDataPacket(header, queued_frames_, large_buffer.get(), packet_size_)); } + if (packet == nullptr) { + LOG(DFATAL) << "Failed to serialize " << queued_frames_.size() + << " frames."; + return NoPacket(); + } + OnBuiltFecProtectedPayload(header, packet->FecProtectedData()); - LOG_IF(DFATAL, packet == nullptr) << "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_by_length) { @@ -411,15 +479,24 @@ SerializedPacket QuicPacketCreator::SerializePacket( // Immediately encrypt the packet, to ensure we don't encrypt the same packet // sequence number multiple times. QuicEncryptedPacket* encrypted = - framer_->EncryptPacket(encryption_level_, sequence_number_, *packet, - encrypted_buffer, encrypted_buffer_len); + framer_->EncryptPayload(encryption_level_, sequence_number_, *packet, + encrypted_buffer, encrypted_buffer_len); if (encrypted == nullptr) { LOG(DFATAL) << "Failed to encrypt packet number " << sequence_number_; return NoPacket(); } + // Update |needs_padding_| flag of |queued_retransmittable_frames_| here, and + // not in AddFrame, because when the first padded frame is added to the queue, + // it might not be retransmittable, and hence the flag would end up being not + // set. + if (queued_retransmittable_frames_.get() != nullptr) { + queued_retransmittable_frames_->set_needs_padding(needs_padding_); + } + packet_size_ = 0; queued_frames_.clear(); + needs_padding_ = false; return SerializedPacket(header.packet_sequence_number, header.public_header.sequence_number_length, encrypted, QuicFramer::GetPacketEntropyHash(header), @@ -448,7 +525,7 @@ SerializedPacket QuicPacketCreator::SerializeFec(char* buffer, DCHECK_GE(max_packet_length_, packet->length()); // Immediately encrypt the packet, to ensure we don't encrypt the same packet // sequence number multiple times. - QuicEncryptedPacket* encrypted = framer_->EncryptPacket( + QuicEncryptedPacket* encrypted = framer_->EncryptPayload( encryption_level_, sequence_number_, *packet, buffer, buffer_len); if (encrypted == nullptr) { LOG(DFATAL) << "Failed to encrypt packet number " << sequence_number_; @@ -501,6 +578,7 @@ bool QuicPacketCreator::ShouldRetransmit(const QuicFrame& frame) { case ACK_FRAME: case PADDING_FRAME: case STOP_WAITING_FRAME: + case MTU_DISCOVERY_FRAME: return false; default: return true; @@ -508,7 +586,9 @@ bool QuicPacketCreator::ShouldRetransmit(const QuicFrame& frame) { } bool QuicPacketCreator::AddFrame(const QuicFrame& frame, - bool save_retransmittable_frames) { + bool save_retransmittable_frames, + bool needs_padding, + char* buffer) { DVLOG(1) << "Adding frame: " << frame; InFecGroup is_in_fec_group = MaybeUpdateLengthsAndStartFec(); @@ -526,47 +606,31 @@ bool QuicPacketCreator::AddFrame(const QuicFrame& frame, queued_retransmittable_frames_.reset( new RetransmittableFrames(encryption_level_)); } - if (frame.type == STREAM_FRAME) { - queued_frames_.push_back( - queued_retransmittable_frames_->AddStreamFrame(frame.stream_frame)); - } else { - queued_frames_.push_back( - queued_retransmittable_frames_->AddNonStreamFrame(frame)); - } + queued_frames_.push_back( + queued_retransmittable_frames_->AddFrame(frame, buffer)); } else { queued_frames_.push_back(frame); } + + if (needs_padding) { + needs_padding_ = true; + } + return true; } void QuicPacketCreator::MaybeAddPadding() { - if (BytesFree() == 0) { - // Don't pad full packets. + if (!needs_padding_) { return; } - // Since ReserializeAllFrames does not populate queued_retransmittable_frames_ - // it's not sufficient to simply call - // queued_retransmittable_frames_->HasCryptoHandshake(). - // TODO(rch): we should really make ReserializeAllFrames not be a special - // case! - - // If any of the frames in the current packet are on the crypto stream - // then they contain handshake messagses, and we should pad them. - bool is_handshake = false; - for (const QuicFrame& frame : queued_frames_) { - if (frame.type == STREAM_FRAME && - frame.stream_frame->stream_id == kCryptoStreamId) { - is_handshake = true; - break; - } - } - if (!is_handshake) { + if (BytesFree() == 0) { + // Don't pad full packets. return; } QuicPaddingFrame padding; - bool success = AddFrame(QuicFrame(&padding), false); + bool success = AddFrame(QuicFrame(&padding), false, false, nullptr); DCHECK(success); } diff --git a/chromium/net/quic/quic_packet_creator.h b/chromium/net/quic/quic_packet_creator.h index 190e5300dfe..493a47b9766 100644 --- a/chromium/net/quic/quic_packet_creator.h +++ b/chromium/net/quic/quic_packet_creator.h @@ -52,6 +52,10 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { // return true if an FEC group is open. bool ShouldSendFec(bool force_close) const; + // Resets (closes) the FEC group. This method should only be called on a + // packet boundary. + void ResetFecGroup(); + // Returns true if an FEC packet is under construction. bool IsFecGroupOpen() const; @@ -75,14 +79,18 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { bool HasRoomForStreamFrame(QuicStreamId id, QuicStreamOffset offset) const; // Converts a raw payload to a frame which fits into the currently open - // packet if there is one. Returns the number of bytes consumed from data. + // packet. The payload begins at |iov_offset| into the |iov|. + // Returns the number of bytes consumed from data. // If data is empty and fin is true, the expected behavior is to consume the - // fin but return 0. + // fin but return 0. If any data is consumed, it will be copied into a + // new buffer that |frame| will point to and will be stored in |buffer|. size_t CreateStreamFrame(QuicStreamId id, - const IOVector& data, + const QuicIOVector& iov, + size_t iov_offset, QuicStreamOffset offset, bool fin, - QuicFrame* frame); + QuicFrame* frame, + scoped_ptr<char[]>* buffer); // Serializes all frames into a single packet. All frames must fit into a // single packet. Also, sets the entropy hash of the serialized packet to a @@ -145,6 +153,14 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { // Returns false if the frame doesn't fit into the current packet. bool AddSavedFrame(const QuicFrame& frame); + // Identical to AddSavedFrame, but takes ownership of the buffer if it returns + // true. + bool AddSavedFrame(const QuicFrame& frame, char* buffer); + + // Identical to AddSavedFrame, but takes ownership of the buffer if it returns + // true, and allows to cause the packet to be padded. + bool AddPaddedSavedFrame(const QuicFrame& frame, char* buffer); + // Serializes all frames which have been added and adds any which should be // retransmitted to |retransmittable_frames| if it's not nullptr. All frames // must fit into a single packet. Sets the entropy hash of the serialized @@ -198,6 +214,10 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { // plaintext size. void SetEncrypter(EncryptionLevel level, QuicEncrypter* encrypter); + // Indicates whether the packet creator is in a state where it can change + // current maximum packet length. + bool CanSetMaxPacketLength() const; + // Sets the maximum packet length. void SetMaxPacketLength(QuicByteCount length); @@ -222,6 +242,14 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { static bool ShouldRetransmit(const QuicFrame& frame); + // Copies |length| bytes from iov starting at offset |iov_offset| into buffer. + // |iov| must be at least iov_offset+length total length and buffer must be + // at least |length| long. + static void CopyToBuffer(const QuicIOVector& iov, + size_t iov_offset, + size_t length, + char* buffer); + // Updates lengths and also starts an FEC group if FEC protection is on and // there is not already an FEC group open. InFecGroup MaybeUpdateLengthsAndStartFec(); @@ -237,7 +265,10 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { // Allows a frame to be added without creating retransmittable frames. // Particularly useful for retransmits using SerializeAllFrames(). - bool AddFrame(const QuicFrame& frame, bool save_retransmittable_frames); + bool AddFrame(const QuicFrame& frame, + bool save_retransmittable_frames, + bool needs_padding, + char* buffer); // Adds a padding frame to the current packet only if the current packet // contains a handshake message, and there is sufficient room to fit a @@ -277,6 +308,8 @@ class NET_EXPORT_PRIVATE QuicPacketCreator { mutable size_t max_plaintext_size_; QuicFrames queued_frames_; scoped_ptr<RetransmittableFrames> queued_retransmittable_frames_; + // If true, the packet will be padded up to |max_packet_length_|. + bool needs_padding_; DISALLOW_COPY_AND_ASSIGN(QuicPacketCreator); }; diff --git a/chromium/net/quic/quic_packet_creator_test.cc b/chromium/net/quic/quic_packet_creator_test.cc index 8aecc8b24a3..46cf52818d8 100644 --- a/chromium/net/quic/quic_packet_creator_test.cc +++ b/chromium/net/quic/quic_packet_creator_test.cc @@ -4,6 +4,8 @@ #include "net/quic/quic_packet_creator.h" +#include <stdint.h> + #include "base/stl_util.h" #include "net/quic/crypto/null_encrypter.h" #include "net/quic/crypto/quic_decrypter.h" @@ -102,8 +104,7 @@ class QuicPacketCreatorTest : public ::testing::TestWithParam<TestParams> { EXPECT_EQ(STREAM_FRAME, frame.type); ASSERT_TRUE(frame.stream_frame); EXPECT_EQ(stream_id, frame.stream_frame->stream_id); - scoped_ptr<string> frame_data(frame.stream_frame->GetDataAsString()); - EXPECT_EQ(data, *frame_data); + EXPECT_EQ(data, frame.stream_frame->data); EXPECT_EQ(offset, frame.stream_frame->offset); EXPECT_EQ(fin, frame.stream_frame->fin); } @@ -138,6 +139,10 @@ class QuicPacketCreatorTest : public ::testing::TestWithParam<TestParams> { return creator_.IsFecProtected(); } + QuicIOVector MakeIOVector(StringPiece s) { + return ::net::MakeIOVector(s, &iov_); + } + static const QuicStreamOffset kOffset = 1u; QuicFrames frames_; @@ -146,6 +151,7 @@ class QuicPacketCreatorTest : public ::testing::TestWithParam<TestParams> { testing::StrictMock<MockFramerVisitor> framer_visitor_; QuicConnectionId connection_id_; string data_; + struct iovec iov_; MockRandom mock_random_; QuicPacketCreator creator_; MockEntropyCalculator entropy_calculator_; @@ -160,8 +166,10 @@ INSTANTIATE_TEST_CASE_P(QuicPacketCreatorTests, TEST_P(QuicPacketCreatorTest, SerializeFrames) { frames_.push_back(QuicFrame(new QuicAckFrame(MakeAckFrame(0u)))); - frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector()))); - frames_.push_back(QuicFrame(new QuicStreamFrame(0u, true, 0u, IOVector()))); + frames_.push_back( + QuicFrame(new QuicStreamFrame(0u, false, 0u, StringPiece()))); + frames_.push_back( + QuicFrame(new QuicStreamFrame(0u, true, 0u, StringPiece()))); char buffer[kMaxPacketSize]; SerializedPacket serialized = creator_.SerializeAllFrames(frames_, buffer, kMaxPacketSize); @@ -192,7 +200,8 @@ TEST_P(QuicPacketCreatorTest, SerializeWithFEC) { // trigger an FEC packet. ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false)); - frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector()))); + frames_.push_back( + QuicFrame(new QuicStreamFrame(0u, false, 0u, StringPiece()))); char buffer[kMaxPacketSize]; SerializedPacket serialized = creator_.SerializeAllFrames(frames_, buffer, kMaxPacketSize); @@ -453,9 +462,9 @@ TEST_P(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) { QuicPacketCreatorPeer::SetSequenceNumberLength(&creator_, PACKET_2BYTE_SEQUENCE_NUMBER); QuicStreamFrame* stream_frame = - new QuicStreamFrame(kCryptoStreamId, /*fin=*/false, 0u, IOVector()); + new QuicStreamFrame(kCryptoStreamId, /*fin=*/false, 0u, StringPiece()); RetransmittableFrames frames(ENCRYPTION_NONE); - frames.AddStreamFrame(stream_frame); + frames.AddFrame(QuicFrame(stream_frame)); char buffer[kMaxPacketSize]; SerializedPacket serialized = creator_.ReserializeAllFrames( frames, PACKET_1BYTE_SEQUENCE_NUMBER, buffer, kMaxPacketSize); @@ -480,11 +489,14 @@ TEST_P(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) { } TEST_P(QuicPacketCreatorTest, ReserializeFramesWithPadding) { - QuicStreamFrame* stream_frame = - new QuicStreamFrame(kCryptoStreamId, /*fin=*/false, /*offset=*/0, - MakeIOVector("fake handshake message data")); + QuicFrame frame; + QuicIOVector io_vector(MakeIOVector("fake handshake message data")); + scoped_ptr<char[]> stream_buffer; + creator_.CreateStreamFrame(kCryptoStreamId, io_vector, 0u, 0u, false, &frame, + &stream_buffer); RetransmittableFrames frames(ENCRYPTION_NONE); - frames.AddStreamFrame(stream_frame); + frames.AddFrame(frame); + frames.set_needs_padding(true); char buffer[kMaxPacketSize]; SerializedPacket serialized = creator_.ReserializeAllFrames( frames, QuicPacketCreatorPeer::NextSequenceNumberLength(&creator_), @@ -501,10 +513,14 @@ TEST_P(QuicPacketCreatorTest, ReserializeFramesWithFullPacketAndPadding) { string data(capacity + delta, 'A'); size_t bytes_free = 0 - delta; - QuicStreamFrame* stream_frame = new QuicStreamFrame( - kCryptoStreamId, /*fin=*/false, kOffset, MakeIOVector(data)); + QuicFrame frame; + QuicIOVector io_vector(MakeIOVector(data)); + scoped_ptr<char[]> stream_buffer; + creator_.CreateStreamFrame(kCryptoStreamId, io_vector, 0, kOffset, false, + &frame, &stream_buffer); RetransmittableFrames frames(ENCRYPTION_NONE); - frames.AddStreamFrame(stream_frame); + frames.AddFrame(frame); + frames.set_needs_padding(true); char buffer[kMaxPacketSize]; SerializedPacket serialized = creator_.ReserializeAllFrames( frames, QuicPacketCreatorPeer::NextSequenceNumberLength(&creator_), @@ -571,7 +587,8 @@ TEST_P(QuicPacketCreatorTest, SwitchFecOnOffWithNoGroup) { 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()))); + frames_.push_back( + QuicFrame(new QuicStreamFrame(0u, false, 0u, StringPiece()))); char buffer[kMaxPacketSize]; SerializedPacket serialized = creator_.SerializeAllFrames(frames_, buffer, kMaxPacketSize); @@ -605,8 +622,10 @@ TEST_P(QuicPacketCreatorTest, SwitchFecOnOffWithGroupInProgress) { TEST_P(QuicPacketCreatorTest, SwitchFecOnWithStreamFrameQueued) { // Add a stream frame to the creator. QuicFrame frame; - size_t consumed = creator_.CreateStreamFrame( - 1u, MakeIOVector("test"), 0u, false, &frame); + QuicIOVector io_vector(MakeIOVector("test")); + scoped_ptr<char[]> stream_buffer; + size_t consumed = creator_.CreateStreamFrame(1u, io_vector, 0u, 0u, false, + &frame, &stream_buffer); EXPECT_EQ(4u, consumed); ASSERT_TRUE(frame.stream_frame); EXPECT_TRUE(creator_.AddSavedFrame(frame)); @@ -634,26 +653,34 @@ TEST_P(QuicPacketCreatorTest, SwitchFecOnWithStreamFrameQueued) { TEST_P(QuicPacketCreatorTest, CreateStreamFrame) { QuicFrame frame; - size_t consumed = creator_.CreateStreamFrame(1u, MakeIOVector("test"), 0u, - false, &frame); + QuicIOVector io_vector(MakeIOVector("test")); + scoped_ptr<char[]> stream_buffer; + size_t consumed = creator_.CreateStreamFrame(1u, io_vector, 0u, 0u, false, + &frame, &stream_buffer); EXPECT_EQ(4u, consumed); CheckStreamFrame(frame, 1u, "test", 0u, false); - delete frame.stream_frame; + RetransmittableFrames cleanup_frames(ENCRYPTION_NONE); + cleanup_frames.AddFrame(frame); } TEST_P(QuicPacketCreatorTest, CreateStreamFrameFin) { QuicFrame frame; - size_t consumed = creator_.CreateStreamFrame(1u, MakeIOVector("test"), 10u, - true, &frame); + QuicIOVector io_vector(MakeIOVector("test")); + scoped_ptr<char[]> stream_buffer; + size_t consumed = creator_.CreateStreamFrame(1u, io_vector, 0u, 10u, true, + &frame, &stream_buffer); EXPECT_EQ(4u, consumed); CheckStreamFrame(frame, 1u, "test", 10u, true); - delete frame.stream_frame; + RetransmittableFrames cleanup_frames(ENCRYPTION_NONE); + cleanup_frames.AddFrame(frame); } TEST_P(QuicPacketCreatorTest, CreateStreamFrameFinOnly) { QuicFrame frame; - size_t consumed = creator_.CreateStreamFrame(1u, IOVector(), 0u, true, - &frame); + QuicIOVector io_vector(nullptr, 0, 0); + scoped_ptr<char[]> stream_buffer; + size_t consumed = creator_.CreateStreamFrame(1u, io_vector, 0u, 0u, true, + &frame, &stream_buffer); EXPECT_EQ(0u, consumed); CheckStreamFrame(frame, 1u, string(), 0u, true); delete frame.stream_frame; @@ -670,9 +697,11 @@ TEST_P(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) { kClientDataStreamId1, kOffset)); if (should_have_room) { QuicFrame frame; - size_t bytes_consumed = creator_.CreateStreamFrame( - kClientDataStreamId1, MakeIOVector("testdata"), kOffset, false, - &frame); + QuicIOVector io_vector(MakeIOVector("testdata")); + scoped_ptr<char[]> stream_buffer; + size_t bytes_consumed = + creator_.CreateStreamFrame(kClientDataStreamId1, io_vector, 0u, + kOffset, false, &frame, &stream_buffer); EXPECT_LT(0u, bytes_consumed); ASSERT_TRUE(creator_.AddSavedFrame(frame)); char buffer[kMaxPacketSize]; @@ -695,8 +724,11 @@ TEST_P(QuicPacketCreatorTest, StreamFrameConsumption) { 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); + QuicIOVector io_vector(MakeIOVector(data)); + scoped_ptr<char[]> stream_buffer; + size_t bytes_consumed = + creator_.CreateStreamFrame(kClientDataStreamId1, io_vector, 0u, kOffset, + false, &frame, &stream_buffer); EXPECT_EQ(capacity - bytes_free, bytes_consumed); ASSERT_TRUE(creator_.AddSavedFrame(frame)); @@ -726,8 +758,11 @@ TEST_P(QuicPacketCreatorTest, StreamFrameConsumptionWithFec) { 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); + QuicIOVector io_vector(MakeIOVector(data)); + scoped_ptr<char[]> stream_buffer; + size_t bytes_consumed = + creator_.CreateStreamFrame(kClientDataStreamId1, io_vector, 0u, kOffset, + false, &frame, &stream_buffer); EXPECT_EQ(capacity - bytes_free, bytes_consumed); ASSERT_TRUE(creator_.AddSavedFrame(frame)); @@ -758,10 +793,12 @@ TEST_P(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) { size_t bytes_free = delta > 0 ? 0 : 0 - delta; QuicFrame frame; + QuicIOVector io_vector(MakeIOVector(data)); + scoped_ptr<char[]> stream_buffer; size_t bytes_consumed = creator_.CreateStreamFrame( - kCryptoStreamId, MakeIOVector(data), kOffset, false, &frame); + kCryptoStreamId, io_vector, 0u, kOffset, false, &frame, &stream_buffer); EXPECT_LT(0u, bytes_consumed); - ASSERT_TRUE(creator_.AddSavedFrame(frame)); + ASSERT_TRUE(creator_.AddPaddedSavedFrame(frame, nullptr)); char buffer[kMaxPacketSize]; SerializedPacket serialized_packet = creator_.SerializePacket(buffer, kMaxPacketSize); @@ -792,8 +829,11 @@ TEST_P(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) { size_t bytes_free = delta > 0 ? 0 : 0 - delta; QuicFrame frame; - size_t bytes_consumed = creator_.CreateStreamFrame( - kClientDataStreamId1, MakeIOVector(data), kOffset, false, &frame); + QuicIOVector io_vector(MakeIOVector(data)); + scoped_ptr<char[]> stream_buffer; + size_t bytes_consumed = + creator_.CreateStreamFrame(kClientDataStreamId1, io_vector, 0u, kOffset, + false, &frame, &stream_buffer); EXPECT_LT(0u, bytes_consumed); ASSERT_TRUE(creator_.AddSavedFrame(frame)); char buffer[kMaxPacketSize]; @@ -854,7 +894,7 @@ TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthLeastAwaiting) { QuicPacketCreatorPeer::SetSequenceNumber( &creator_, - GG_UINT64_C(64) * 256 * 256 * 256 * 256 - max_packets_per_fec_group); + UINT64_C(64) * 256 * 256 * 256 * 256 - max_packets_per_fec_group); creator_.UpdateSequenceNumberLength(2, 10000 / kDefaultMaxPacketSize); EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER, QuicPacketCreatorPeer::NextSequenceNumberLength(&creator_)); @@ -878,7 +918,7 @@ TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthBandwidth) { QuicPacketCreatorPeer::NextSequenceNumberLength(&creator_)); creator_.UpdateSequenceNumberLength( - 1, GG_UINT64_C(1000) * 256 * 256 * 256 * 256 / kDefaultMaxPacketSize); + 1, UINT64_C(1000) * 256 * 256 * 256 * 256 / kDefaultMaxPacketSize); EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER, QuicPacketCreatorPeer::NextSequenceNumberLength(&creator_)); } @@ -887,7 +927,8 @@ TEST_P(QuicPacketCreatorTest, SerializeFrame) { if (!GetParam().version_serialization) { creator_.StopSendingVersion(); } - frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector()))); + frames_.push_back( + QuicFrame(new QuicStreamFrame(0u, false, 0u, StringPiece()))); char buffer[kMaxPacketSize]; SerializedPacket serialized = creator_.SerializeAllFrames(frames_, buffer, kMaxPacketSize); @@ -924,12 +965,15 @@ TEST_P(QuicPacketCreatorTest, CreateStreamFrameTooLarge) { 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( - 1u, MakeIOVector(too_long_payload), 0u, true, &frame); + QuicIOVector io_vector(MakeIOVector(too_long_payload)); + scoped_ptr<char[]> stream_buffer; + size_t consumed = creator_.CreateStreamFrame(1u, io_vector, 0u, 0u, true, + &frame, &stream_buffer); EXPECT_EQ(payload_length, consumed); const string payload(payload_length, 'a'); CheckStreamFrame(frame, 1u, payload, 0u, false); - delete frame.stream_frame; + RetransmittableFrames cleanup_frames(ENCRYPTION_NONE); + cleanup_frames.AddFrame(frame); } TEST_P(QuicPacketCreatorTest, AddFrameAndSerialize) { @@ -952,8 +996,10 @@ TEST_P(QuicPacketCreatorTest, AddFrameAndSerialize) { EXPECT_TRUE(creator_.HasPendingFrames()); QuicFrame frame; - size_t consumed = creator_.CreateStreamFrame( - 1u, MakeIOVector("test"), 0u, false, &frame); + QuicIOVector io_vector(MakeIOVector("test")); + scoped_ptr<char[]> stream_buffer; + size_t consumed = creator_.CreateStreamFrame(1u, io_vector, 0u, 0u, false, + &frame, &stream_buffer); EXPECT_EQ(4u, consumed); ASSERT_TRUE(frame.stream_frame); EXPECT_TRUE(creator_.AddSavedFrame(frame)); @@ -1012,8 +1058,10 @@ TEST_P(QuicPacketCreatorTest, SerializeTruncatedAckFrameWithLargePacketSize) { // 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); + QuicIOVector io_vector(MakeIOVector("test")); + scoped_ptr<char[]> stream_buffer; + size_t consumed = creator_.CreateStreamFrame(2u, io_vector, 0u, 0u, false, + &stream_frame, &stream_buffer); EXPECT_EQ(4u, consumed); ASSERT_TRUE(stream_frame.stream_frame); EXPECT_TRUE(creator_.AddSavedFrame(stream_frame)); @@ -1075,7 +1123,8 @@ TEST_P(QuicPacketCreatorTest, SerializeTruncatedAckFrameWithSmallPacketSize) { TEST_P(QuicPacketCreatorTest, EntropyFlag) { - frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector()))); + frames_.push_back( + QuicFrame(new QuicStreamFrame(0u, false, 0u, StringPiece()))); char buffer[kMaxPacketSize]; for (int i = 0; i < 2; ++i) { @@ -1084,7 +1133,7 @@ TEST_P(QuicPacketCreatorTest, EntropyFlag) { creator_.SerializeAllFrames(frames_, buffer, kMaxPacketSize); // Verify both BoolSource and hash algorithm. bool expected_rand_bool = - (mock_random_.RandUint64() & (GG_UINT64_C(1) << j)) != 0; + (mock_random_.RandUint64() & (UINT64_C(1) << j)) != 0; bool observed_rand_bool = (serialized.entropy_hash & (1 << ((j+1) % 8))) != 0; uint8 rest_of_hash = serialized.entropy_hash & ~(1 << ((j+1) % 8)); @@ -1099,6 +1148,86 @@ TEST_P(QuicPacketCreatorTest, EntropyFlag) { delete frames_[0].stream_frame; } +TEST_P(QuicPacketCreatorTest, ResetFecGroup) { + // Enable FEC protection, and send FEC packet every 6 packets. + EXPECT_TRUE(SwitchFecProtectionOn(6)); + frames_.push_back( + QuicFrame(new QuicStreamFrame(0u, false, 0u, StringPiece()))); + char buffer[kMaxPacketSize]; + SerializedPacket serialized = + creator_.SerializeAllFrames(frames_, buffer, kMaxPacketSize); + delete serialized.packet; + + EXPECT_TRUE(creator_.IsFecProtected()); + EXPECT_TRUE(creator_.IsFecGroupOpen()); + // 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)); + + // Close the FEC Group. + creator_.ResetFecGroup(); + EXPECT_TRUE(creator_.IsFecProtected()); + EXPECT_FALSE(creator_.IsFecGroupOpen()); + // We do not have enough packets in the FEC group to trigger an FEC packet. + EXPECT_FALSE(creator_.ShouldSendFec(/*force_close=*/false)); + // Confirm that there is no FEC packet under construction. + EXPECT_FALSE(creator_.ShouldSendFec(/*force_close=*/true)); + + EXPECT_DFATAL(serialized = creator_.SerializeFec(buffer, kMaxPacketSize), + "SerializeFEC called but no group or zero packets in group."); + delete serialized.packet; + + // Start a new FEC packet. + serialized = creator_.SerializeAllFrames(frames_, buffer, kMaxPacketSize); + delete frames_[0].stream_frame; + delete serialized.packet; + + EXPECT_TRUE(creator_.IsFecProtected()); + EXPECT_TRUE(creator_.IsFecGroupOpen()); + // 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)); + + // 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(buffer, kMaxPacketSize); + ASSERT_EQ(3u, serialized.sequence_number); + delete serialized.packet; +} + +TEST_P(QuicPacketCreatorTest, ResetFecGroupWithQueuedFrames) { + // Add a stream frame to the creator. + QuicFrame frame; + QuicIOVector io_vector(MakeIOVector("test")); + scoped_ptr<char[]> stream_buffer; + size_t consumed = creator_.CreateStreamFrame(1u, io_vector, 0u, 0u, false, + &frame, &stream_buffer); + EXPECT_EQ(4u, consumed); + ASSERT_TRUE(frame.stream_frame); + EXPECT_TRUE(creator_.AddSavedFrame(frame)); + EXPECT_TRUE(creator_.HasPendingFrames()); + EXPECT_DFATAL(creator_.ResetFecGroup(), + "Cannot reset FEC group with pending frames."); + + // Serialize packet for transmission. + char buffer[kMaxPacketSize]; + SerializedPacket serialized = + creator_.SerializePacket(buffer, kMaxPacketSize); + delete serialized.packet; + delete serialized.retransmittable_frames; + EXPECT_FALSE(creator_.HasPendingFrames()); + + // Close the FEC Group. + creator_.ResetFecGroup(); + EXPECT_FALSE(creator_.IsFecGroupOpen()); +} + } // namespace } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_packet_generator.cc b/chromium/net/quic/quic_packet_generator.cc index 77f57759857..cc3a79331a1 100644 --- a/chromium/net/quic/quic_packet_generator.cc +++ b/chromium/net/quic/quic_packet_generator.cc @@ -45,10 +45,12 @@ QuicPacketGenerator::QuicPacketGenerator(QuicConnectionId connection_id, batch_mode_(false), fec_timeout_(QuicTime::Delta::Zero()), should_fec_protect_(false), + fec_send_policy_(FEC_ANY_TRIGGER), should_send_ack_(false), should_send_stop_waiting_(false), ack_queued_(false), - stop_waiting_queued_(false) { + stop_waiting_queued_(false), + max_packet_length_(kDefaultMaxPacketSize) { } QuicPacketGenerator::~QuicPacketGenerator() { @@ -63,6 +65,9 @@ QuicPacketGenerator::~QuicPacketGenerator() { case ACK_FRAME: delete frame.ack_frame; break; + case MTU_DISCOVERY_FRAME: + delete frame.mtu_discovery_frame; + break; case RST_STREAM_FRAME: delete frame.rst_stream_frame; break; @@ -114,17 +119,17 @@ void QuicPacketGenerator::SetShouldSendAck(bool also_send_stop_waiting) { should_send_ack_ = true; should_send_stop_waiting_ = also_send_stop_waiting; - SendQueuedFrames(false); + SendQueuedFrames(/*flush=*/false, /*is_fec_timeout=*/false); } void QuicPacketGenerator::AddControlFrame(const QuicFrame& frame) { queued_control_frames_.push_back(frame); - SendQueuedFrames(false); + SendQueuedFrames(/*flush=*/false, /*is_fec_timeout=*/false); } QuicConsumedData QuicPacketGenerator::ConsumeData( QuicStreamId id, - const IOVector& data_to_write, + const QuicIOVector& iov, QuicStreamOffset offset, bool fin, FecProtection fec_protection, @@ -134,7 +139,7 @@ QuicConsumedData QuicPacketGenerator::ConsumeData( // other retransmittable frames in a single packet. const bool flush = has_handshake && packet_creator_.HasPendingRetransmittableFrames(); - SendQueuedFrames(flush); + SendQueuedFrames(flush, /*is_fec_timeout=*/false); size_t total_bytes_consumed = 0; bool fin_consumed = false; @@ -154,9 +159,7 @@ QuicConsumedData QuicPacketGenerator::ConsumeData( notifier = new QuicAckNotifier(delegate); } - IOVector data = data_to_write; - size_t data_size = data.TotalBufferSize(); - if (!fin && (data_size == 0)) { + if (!fin && (iov.total_length == 0)) { LOG(DFATAL) << "Attempt to consume empty data without FIN."; return QuicConsumedData(0, false); } @@ -165,8 +168,10 @@ QuicConsumedData QuicPacketGenerator::ConsumeData( while (delegate_->ShouldGeneratePacket( HAS_RETRANSMITTABLE_DATA, has_handshake ? IS_HANDSHAKE : NOT_HANDSHAKE)) { QuicFrame frame; + scoped_ptr<char[]> buffer; size_t bytes_consumed = packet_creator_.CreateStreamFrame( - id, data, offset + total_bytes_consumed, fin, &frame); + id, iov, total_bytes_consumed, offset + total_bytes_consumed, fin, + &frame, &buffer); ++frames_created; // We want to track which packet this stream frame ends up in. @@ -174,7 +179,7 @@ QuicConsumedData QuicPacketGenerator::ConsumeData( ack_notifiers_.push_back(notifier); } - if (!AddFrame(frame)) { + if (!AddFrame(frame, buffer.get(), has_handshake)) { LOG(DFATAL) << "Failed to add stream frame."; // Inability to add a STREAM frame creates an unrecoverable hole in a // the stream, so it's best to close the connection. @@ -182,18 +187,22 @@ QuicConsumedData QuicPacketGenerator::ConsumeData( delete notifier; return QuicConsumedData(0, false); } + // When AddFrame succeeds, it takes ownership of the buffer. + ignore_result(buffer.release()); total_bytes_consumed += bytes_consumed; - fin_consumed = fin && total_bytes_consumed == data_size; - data.Consume(bytes_consumed); - DCHECK(data.Empty() || packet_creator_.BytesFree() == 0u); + fin_consumed = fin && total_bytes_consumed == iov.total_length; + DCHECK(total_bytes_consumed == iov.total_length || + packet_creator_.BytesFree() == 0u); - // TODO(ianswett): Restore packet reordering. if (!InBatchMode() || !packet_creator_.HasRoomForStreamFrame(id, offset)) { + // TODO(rtenneti): remove MaybeSendFecPacketAndCloseGroup() from inside + // SerializeAndSendPacket() and make it an explicit call here (and + // elsewhere where we call SerializeAndSendPacket?). SerializeAndSendPacket(); } - if (data.Empty()) { + if (total_bytes_consumed == iov.total_length) { // 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. @@ -213,17 +222,50 @@ QuicConsumedData QuicPacketGenerator::ConsumeData( // Don't allow the handshake to be bundled with other retransmittable frames. if (has_handshake) { - SendQueuedFrames(true); + SendQueuedFrames(/*flush=*/true, /*is_fec_timeout=*/false); } // 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(/*force=*/false); + MaybeSendFecPacketAndCloseGroup(/*force=*/false, /*is_fec_timeout=*/false); DCHECK(InBatchMode() || !packet_creator_.HasPendingFrames()); return QuicConsumedData(total_bytes_consumed, fin_consumed); } +void QuicPacketGenerator::GenerateMtuDiscoveryPacket( + QuicByteCount target_mtu, + QuicAckNotifier::DelegateInterface* delegate) { + // MTU discovery frames must be sent by themselves. + DCHECK(!InBatchMode() && !packet_creator_.HasPendingFrames()); + + // If an ack notifier delegate is provided, register it. + if (delegate) { + QuicAckNotifier* ack_notifier = new QuicAckNotifier(delegate); + // The notifier manager will take the ownership of the notifier after the + // packet is sent. + ack_notifiers_.push_back(ack_notifier); + } + + const QuicByteCount current_mtu = GetMaxPacketLength(); + + // The MTU discovery frame is allocated on the stack, since it is going to be + // serialized within this function. + QuicMtuDiscoveryFrame mtu_discovery_frame; + QuicFrame frame(&mtu_discovery_frame); + + // Send the probe packet with the new length. + SetMaxPacketLength(target_mtu, /*force=*/true); + const bool success = AddFrame(frame, nullptr, /*needs_padding=*/true); + SerializeAndSendPacket(); + // The only reason AddFrame can fail is that the packet is too full to fit in + // a ping. This is not possible for any sane MTU. + DCHECK(success); + + // Reset the packet length back. + SetMaxPacketLength(current_mtu, /*force=*/true); +} + bool QuicPacketGenerator::CanSendWithNextPendingFrameAddition() const { DCHECK(HasPendingFrames()); HasRetransmittableData retransmittable = @@ -236,7 +278,7 @@ bool QuicPacketGenerator::CanSendWithNextPendingFrameAddition() const { return delegate_->ShouldGeneratePacket(retransmittable, NOT_HANDSHAKE); } -void QuicPacketGenerator::SendQueuedFrames(bool flush) { +void QuicPacketGenerator::SendQueuedFrames(bool flush, bool is_fec_timeout) { // Only add pending frames if we are SURE we can then send the whole packet. while (HasPendingFrames() && (flush || CanSendWithNextPendingFrameAddition())) { @@ -248,7 +290,7 @@ void QuicPacketGenerator::SendQueuedFrames(bool flush) { if (packet_creator_.HasPendingFrames() && (flush || !InBatchMode())) { SerializeAndSendPacket(); } - MaybeSendFecPacketAndCloseGroup(flush); + MaybeSendFecPacketAndCloseGroup(flush, is_fec_timeout); } void QuicPacketGenerator::MaybeStartFecProtection() { @@ -267,23 +309,31 @@ void QuicPacketGenerator::MaybeStartFecProtection() { // 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); + SendQueuedFrames(/*flush=*/true, /*is_fec_timeout=*/false); } packet_creator_.StartFecProtectingPackets(); DCHECK(packet_creator_.IsFecProtected()); } -void QuicPacketGenerator::MaybeSendFecPacketAndCloseGroup(bool force) { +void QuicPacketGenerator::MaybeSendFecPacketAndCloseGroup(bool force, + bool is_fec_timeout) { if (!ShouldSendFecPacket(force)) { return; } - // TODO(jri): SerializeFec can return a NULL packet, and this should - // cause an early return, with a call to delegate_->OnPacketGenerationError. - char buffer[kMaxPacketSize]; - SerializedPacket serialized_fec = - packet_creator_.SerializeFec(buffer, kMaxPacketSize); - DCHECK(serialized_fec.packet); - delegate_->OnSerializedPacket(serialized_fec); + + // If we want to send FEC packet only when FEC alaram goes off and if it is + // not a FEC timeout then close the group and dont send FEC packet. + if (fec_send_policy_ == FEC_ALARM_TRIGGER && !is_fec_timeout) { + ResetFecGroup(); + } else { + // TODO(jri): SerializeFec can return a NULL packet, and this should + // cause an early return, with a call to delegate_->OnPacketGenerationError. + char buffer[kMaxPacketSize]; + SerializedPacket serialized_fec = + packet_creator_.SerializeFec(buffer, kMaxPacketSize); + DCHECK(serialized_fec.packet); + delegate_->OnSerializedPacket(serialized_fec); + } // Turn FEC protection off if creator's protection is on and the creator // does not have an open FEC group. // Note: We only wait until the frames queued in the creator are flushed; @@ -300,6 +350,12 @@ bool QuicPacketGenerator::ShouldSendFecPacket(bool force) { packet_creator_.ShouldSendFec(force); } +void QuicPacketGenerator::ResetFecGroup() { + DCHECK(packet_creator_.IsFecGroupOpen()); + packet_creator_.ResetFecGroup(); + delegate_->OnResetFecGroup(); +} + void QuicPacketGenerator::OnFecTimeout() { DCHECK(!InBatchMode()); if (!ShouldSendFecPacket(true)) { @@ -308,8 +364,8 @@ void QuicPacketGenerator::OnFecTimeout() { } // Flush out any pending frames in the generator and the creator, and then // send out FEC packet. - SendQueuedFrames(true); - MaybeSendFecPacketAndCloseGroup(/*force=*/true); + SendQueuedFrames(/*flush=*/true, /*is_fec_timeout=*/true); + MaybeSendFecPacketAndCloseGroup(/*force=*/true, /*is_fec_timeout=*/true); } QuicTime::Delta QuicPacketGenerator::GetFecTimeout( @@ -334,11 +390,11 @@ void QuicPacketGenerator::StartBatchOperations() { void QuicPacketGenerator::FinishBatchOperations() { batch_mode_ = false; - SendQueuedFrames(false); + SendQueuedFrames(/*flush=*/false, /*is_fec_timeout=*/false); } void QuicPacketGenerator::FlushAllQueuedFrames() { - SendQueuedFrames(true); + SendQueuedFrames(/*flush=*/true, /*is_fec_timeout=*/false); } bool QuicPacketGenerator::HasQueuedFrames() const { @@ -355,7 +411,8 @@ bool QuicPacketGenerator::AddNextPendingFrame() { delegate_->PopulateAckFrame(&pending_ack_frame_); ack_queued_ = true; // If we can't this add the frame now, then we still need to do so later. - should_send_ack_ = !AddFrame(QuicFrame(&pending_ack_frame_)); + should_send_ack_ = !AddFrame(QuicFrame(&pending_ack_frame_), nullptr, + /*needs_padding=*/false); // 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_ack_; @@ -366,7 +423,8 @@ bool QuicPacketGenerator::AddNextPendingFrame() { stop_waiting_queued_ = true; // 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_)); + !AddFrame(QuicFrame(&pending_stop_waiting_frame_), nullptr, + /*needs_padding=*/false); // 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_; @@ -374,7 +432,8 @@ bool QuicPacketGenerator::AddNextPendingFrame() { LOG_IF(DFATAL, queued_control_frames_.empty()) << "AddNextPendingFrame called with no queued control frames."; - if (!AddFrame(queued_control_frames_.back())) { + if (!AddFrame(queued_control_frames_.back(), nullptr, + /*needs_padding=*/false)) { // Packet was full. return false; } @@ -382,8 +441,12 @@ bool QuicPacketGenerator::AddNextPendingFrame() { return true; } -bool QuicPacketGenerator::AddFrame(const QuicFrame& frame) { - bool success = packet_creator_.AddSavedFrame(frame); +bool QuicPacketGenerator::AddFrame(const QuicFrame& frame, + char* buffer, + bool needs_padding) { + bool success = needs_padding + ? packet_creator_.AddPaddedSavedFrame(frame, buffer) + : packet_creator_.AddSavedFrame(frame, buffer); if (success && debug_delegate_) { debug_delegate_->OnFrameAddedToPacket(frame); } @@ -394,14 +457,25 @@ void QuicPacketGenerator::SerializeAndSendPacket() { char buffer[kMaxPacketSize]; SerializedPacket serialized_packet = packet_creator_.SerializePacket(buffer, kMaxPacketSize); - DCHECK(serialized_packet.packet); + if (serialized_packet.packet == nullptr) { + LOG(DFATAL) << "Failed to SerializePacket. fec_policy:" << fec_send_policy_ + << " should_fec_protect_:" << should_fec_protect_; + delegate_->CloseConnection(QUIC_FAILED_TO_SERIALIZE_PACKET, false); + return; + } // There may be AckNotifiers interested in this packet. serialized_packet.notifiers.swap(ack_notifiers_); ack_notifiers_.clear(); delegate_->OnSerializedPacket(serialized_packet); - MaybeSendFecPacketAndCloseGroup(/*force=*/false); + MaybeSendFecPacketAndCloseGroup(/*force=*/false, /*is_fec_timeout=*/false); + + // Maximum packet size may be only enacted while no packet is currently being + // constructed, so here we have a good opportunity to actually change it. + if (packet_creator_.CanSetMaxPacketLength()) { + packet_creator_.SetMaxPacketLength(max_packet_length_); + } // The packet has now been serialized, so the frames are no longer queued. ack_queued_ = false; @@ -416,12 +490,28 @@ QuicPacketSequenceNumber QuicPacketGenerator::sequence_number() const { return packet_creator_.sequence_number(); } -QuicByteCount QuicPacketGenerator::max_packet_length() const { +QuicByteCount QuicPacketGenerator::GetMaxPacketLength() const { + return max_packet_length_; +} + +QuicByteCount QuicPacketGenerator::GetCurrentMaxPacketLength() const { return packet_creator_.max_packet_length(); } -void QuicPacketGenerator::set_max_packet_length(QuicByteCount length) { - packet_creator_.SetMaxPacketLength(length); +void QuicPacketGenerator::SetMaxPacketLength(QuicByteCount length, bool force) { + // If we cannot immediately set new maximum packet length, and the |force| + // flag is set, we have to flush the contents of the queue and close existing + // FEC group. + if (!packet_creator_.CanSetMaxPacketLength() && force) { + SendQueuedFrames(/*flush=*/true, /*is_fec_timeout=*/false); + MaybeSendFecPacketAndCloseGroup(/*force=*/true, /*is_fec_timeout=*/false); + DCHECK(packet_creator_.CanSetMaxPacketLength()); + } + + max_packet_length_ = length; + if (packet_creator_.CanSetMaxPacketLength()) { + packet_creator_.SetMaxPacketLength(length); + } } QuicEncryptedPacket* QuicPacketGenerator::SerializeVersionNegotiationPacket( diff --git a/chromium/net/quic/quic_packet_generator.h b/chromium/net/quic/quic_packet_generator.h index 5db862d07e3..9002b1a0988 100644 --- a/chromium/net/quic/quic_packet_generator.h +++ b/chromium/net/quic/quic_packet_generator.h @@ -79,6 +79,8 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator { // Takes ownership of |packet.packet| and |packet.retransmittable_frames|. virtual void OnSerializedPacket(const SerializedPacket& packet) = 0; virtual void CloseConnection(QuicErrorCode error, bool from_peer) = 0; + // Called when a FEC Group is reset (closed). + virtual void OnResetFecGroup() = 0; }; // Interface which gets callbacks from the QuicPacketGenerator at interesting @@ -120,12 +122,16 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator { // |delegate| (if not nullptr) will be informed once all packets sent as a // result of this call are ACKed by the peer. QuicConsumedData ConsumeData(QuicStreamId id, - const IOVector& data, + const QuicIOVector& iov, QuicStreamOffset offset, bool fin, FecProtection fec_protection, QuicAckNotifier::DelegateInterface* delegate); + // Generates an MTU discovery packet of specified size. + void GenerateMtuDiscoveryPacket(QuicByteCount target_mtu, + QuicAckNotifier::DelegateInterface* delegate); + // Indicates whether batch mode is currently enabled. bool InBatchMode(); // Disables flushing. @@ -188,14 +194,28 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator { // created. QuicPacketSequenceNumber sequence_number() const; - QuicByteCount max_packet_length() const; + // Returns the maximum packet length. Note that this is the long-term maximum + // packet length, and it may not be the maximum length of the current packet, + // if the generator is in the middle of the packet (in batch mode) or FEC + // group. + QuicByteCount GetMaxPacketLength() const; + // Returns the maximum length current packet can actually have. + QuicByteCount GetCurrentMaxPacketLength() const; - void set_max_packet_length(QuicByteCount length); + // Set maximum packet length sent. If |force| is set to true, all pending + // unfinished packets and FEC groups are closed, and the change is enacted + // immediately. Otherwise, it is enacted at the next opportunity. + void SetMaxPacketLength(QuicByteCount length, bool force); void set_debug_delegate(DebugDelegate* debug_delegate) { debug_delegate_ = debug_delegate; } + FecSendPolicy fec_send_policy() { return fec_send_policy_; } + void set_fec_send_policy(FecSendPolicy fec_send_policy) { + fec_send_policy_ = fec_send_policy; + } + private: friend class test::QuicPacketGeneratorPeer; @@ -211,13 +231,19 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator { // 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 turn // off FEC protection in the creator if it's off in the generator. - void MaybeSendFecPacketAndCloseGroup(bool force); + // When |fec_send_policy_| is FEC_SEND_QUIESCENCE, then send FEC + // packet if |is_fec_timeout| is true otherwise close the FEC group. + void MaybeSendFecPacketAndCloseGroup(bool force, bool is_fec_timeout); // Returns true if an FEC packet should be generated based on |force| and // current state of the generator and the creator. bool ShouldSendFecPacket(bool force); - void SendQueuedFrames(bool flush); + // Resets (closes) the FEC group and calls the Delegate's OnResetFecGroup. + // Asserts that FEC group is open. + void ResetFecGroup(); + + void SendQueuedFrames(bool flush, bool is_fec_timeout); // Test to see if we have pending ack, or control frames. bool HasPendingFrames() const; @@ -226,8 +252,9 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator { bool CanSendWithNextPendingFrameAddition() const; // Add exactly one pending frame, preferring ack frames over control frames. bool AddNextPendingFrame(); - - bool AddFrame(const QuicFrame& frame); + // Adds a frame and takes ownership of the underlying buffer if the addition + // was successful. + bool AddFrame(const QuicFrame& frame, char* buffer, bool needs_padding); void SerializeAndSendPacket(); @@ -248,13 +275,16 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator { // if this variable is false. bool should_fec_protect_; + // FEC policy that specifies when to send FEC packet. + FecSendPolicy fec_send_policy_; + // Flags to indicate the need for just-in-time construction of a frame. bool should_send_ack_; bool should_send_stop_waiting_; - // If we put a non-retransmittable frame (ack 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. + // If we put a non-retransmittable 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. QuicAckFrame pending_ack_frame_; QuicStopWaitingFrame pending_stop_waiting_frame_; // True if an ack or stop waiting frame is already queued, and should not be @@ -265,6 +295,11 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator { // Stores notifiers that should be attached to the next serialized packet. std::list<QuicAckNotifier*> ack_notifiers_; + // Stores the maximum packet size we are allowed to send. This might not be + // the maximum size we are actually using now, if we are in the middle of the + // packet. + QuicByteCount max_packet_length_; + 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 21e186def67..bd6a207715e 100644 --- a/chromium/net/quic/quic_packet_generator_test.cc +++ b/chromium/net/quic/quic_packet_generator_test.cc @@ -35,6 +35,11 @@ namespace { const int64 kMinFecTimeoutMs = 5u; +static const FecSendPolicy kFecSendPolicyList[] = { + FEC_ANY_TRIGGER, + FEC_ALARM_TRIGGER, +}; + class MockDelegate : public QuicPacketGenerator::DelegateInterface { public: MockDelegate() {} @@ -47,6 +52,7 @@ class MockDelegate : public QuicPacketGenerator::DelegateInterface { MOCK_METHOD1(PopulateStopWaitingFrame, void(QuicStopWaitingFrame*)); MOCK_METHOD1(OnSerializedPacket, void(const SerializedPacket& packet)); MOCK_METHOD2(CloseConnection, void(QuicErrorCode, bool)); + MOCK_METHOD0(OnResetFecGroup, void()); void SetCanWriteAnything() { EXPECT_CALL(*this, ShouldGeneratePacket(_, _)).WillRepeatedly(Return(true)); @@ -84,8 +90,9 @@ struct PacketContents { num_rst_stream_frames(0), num_stop_waiting_frames(0), num_stream_frames(0), - fec_group(0) { - } + num_ping_frames(0), + num_mtu_discovery_frames(0), + fec_group(0) {} size_t num_ack_frames; size_t num_connection_close_frames; @@ -93,20 +100,24 @@ struct PacketContents { size_t num_rst_stream_frames; size_t num_stop_waiting_frames; size_t num_stream_frames; + size_t num_ping_frames; + size_t num_mtu_discovery_frames; QuicFecGroupNumber fec_group; }; } // namespace -class QuicPacketGeneratorTest : public ::testing::Test { +class QuicPacketGeneratorTest : public ::testing::TestWithParam<FecSendPolicy> { public: QuicPacketGeneratorTest() : framer_(QuicSupportedVersions(), QuicTime::Zero(), Perspective::IS_CLIENT), generator_(42, &framer_, &random_, &delegate_), - creator_(QuicPacketGeneratorPeer::GetPacketCreator(&generator_)) {} + creator_(QuicPacketGeneratorPeer::GetPacketCreator(&generator_)) { + generator_.set_fec_send_policy(GetParam()); + } ~QuicPacketGeneratorTest() override { for (SerializedPacket& packet : packets_) { @@ -135,12 +146,13 @@ class QuicPacketGeneratorTest : public ::testing::Test { size_t packet_index) { ASSERT_GT(packets_.size(), packet_index); const SerializedPacket& packet = packets_[packet_index]; - size_t num_retransmittable_frames = contents.num_connection_close_frames + - contents.num_goaway_frames + contents.num_rst_stream_frames + - contents.num_stream_frames; - size_t num_frames = contents.num_ack_frames + - contents.num_stop_waiting_frames + - num_retransmittable_frames; + size_t num_retransmittable_frames = + contents.num_connection_close_frames + contents.num_goaway_frames + + contents.num_rst_stream_frames + contents.num_stream_frames + + contents.num_ping_frames; + size_t num_frames = + contents.num_ack_frames + contents.num_stop_waiting_frames + + contents.num_mtu_discovery_frames + num_retransmittable_frames; if (num_retransmittable_frames == 0) { ASSERT_TRUE(packet.retransmittable_frames == nullptr); @@ -165,6 +177,10 @@ class QuicPacketGeneratorTest : public ::testing::Test { EXPECT_EQ(contents.num_stop_waiting_frames, simple_framer_.stop_waiting_frames().size()); EXPECT_EQ(contents.fec_group, simple_framer_.header().fec_group); + + // From the receiver's perspective, MTU discovery frames are ping frames. + EXPECT_EQ(contents.num_ping_frames + contents.num_mtu_discovery_frames, + simple_framer_.ping_frames().size()); } void CheckPacketHasSingleStreamFrame(size_t packet_index) { @@ -178,6 +194,12 @@ class QuicPacketGeneratorTest : public ::testing::Test { EXPECT_EQ(1u, simple_framer_.stream_frames().size()); } + void CheckAllPacketsHaveSingleStreamFrame() { + for (size_t i = 0; i < packets_.size(); i++) { + CheckPacketHasSingleStreamFrame(i); + } + } + void CheckPacketIsFec(size_t packet_index, QuicPacketSequenceNumber fec_group) { ASSERT_GT(packets_.size(), packet_index); @@ -189,12 +211,16 @@ class QuicPacketGeneratorTest : public ::testing::Test { EXPECT_EQ(fec_group, simple_framer_.fec_data().fec_group); } - IOVector CreateData(size_t len) { + QuicIOVector CreateData(size_t len) { data_array_.reset(new char[len]); memset(data_array_.get(), '?', len); - IOVector data; - data.Append(data_array_.get(), len); - return data; + iov_.iov_base = data_array_.get(); + iov_.iov_len = len; + return QuicIOVector(&iov_, 1, len); + } + + QuicIOVector MakeIOVector(StringPiece s) { + return ::net::MakeIOVector(s, &iov_); } QuicFramer framer_; @@ -207,6 +233,7 @@ class QuicPacketGeneratorTest : public ::testing::Test { private: scoped_ptr<char[]> data_array_; + struct iovec iov_; }; class MockDebugDelegate : public QuicPacketGenerator::DebugDelegate { @@ -215,14 +242,19 @@ class MockDebugDelegate : public QuicPacketGenerator::DebugDelegate { void(const QuicFrame&)); }; -TEST_F(QuicPacketGeneratorTest, ShouldSendAck_NotWritable) { +// Run all end to end tests with all supported FEC send polocies. +INSTANTIATE_TEST_CASE_P(FecSendPolicy, + QuicPacketGeneratorTest, + ::testing::ValuesIn(kFecSendPolicyList)); + +TEST_P(QuicPacketGeneratorTest, ShouldSendAck_NotWritable) { delegate_.SetCanNotWrite(); generator_.SetShouldSendAck(false); EXPECT_TRUE(generator_.HasQueuedFrames()); } -TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldNotFlush) { +TEST_P(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldNotFlush) { StrictMock<MockDebugDelegate> debug_delegate; generator_.set_debug_delegate(&debug_delegate); @@ -236,7 +268,7 @@ TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldNotFlush) { EXPECT_TRUE(generator_.HasQueuedFrames()); } -TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldFlush) { +TEST_P(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldFlush) { delegate_.SetCanWriteOnlyNonRetransmittable(); EXPECT_CALL(delegate_, PopulateAckFrame(_)); @@ -251,7 +283,7 @@ TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldFlush) { CheckPacketContains(contents, 0); } -TEST_F(QuicPacketGeneratorTest, ShouldSendAck_MultipleCalls) { +TEST_P(QuicPacketGeneratorTest, ShouldSendAck_MultipleCalls) { // Make sure that calling SetShouldSendAck multiple times does not result in a // crash. Previously this would result in multiple QuicFrames queued in the // packet generator, with all but the last with internal pointers to freed @@ -270,21 +302,21 @@ TEST_F(QuicPacketGeneratorTest, ShouldSendAck_MultipleCalls) { generator_.FinishBatchOperations(); } -TEST_F(QuicPacketGeneratorTest, AddControlFrame_NotWritable) { +TEST_P(QuicPacketGeneratorTest, AddControlFrame_NotWritable) { delegate_.SetCanNotWrite(); generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame())); EXPECT_TRUE(generator_.HasQueuedFrames()); } -TEST_F(QuicPacketGeneratorTest, AddControlFrame_OnlyAckWritable) { +TEST_P(QuicPacketGeneratorTest, AddControlFrame_OnlyAckWritable) { delegate_.SetCanWriteOnlyNonRetransmittable(); generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame())); EXPECT_TRUE(generator_.HasQueuedFrames()); } -TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldNotFlush) { +TEST_P(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldNotFlush) { delegate_.SetCanWriteAnything(); generator_.StartBatchOperations(); @@ -292,7 +324,7 @@ TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldNotFlush) { EXPECT_TRUE(generator_.HasQueuedFrames()); } -TEST_F(QuicPacketGeneratorTest, AddControlFrame_NotWritableBatchThenFlush) { +TEST_P(QuicPacketGeneratorTest, AddControlFrame_NotWritableBatchThenFlush) { delegate_.SetCanNotWrite(); generator_.StartBatchOperations(); @@ -311,7 +343,7 @@ TEST_F(QuicPacketGeneratorTest, AddControlFrame_NotWritableBatchThenFlush) { CheckPacketContains(contents, 0); } -TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldFlush) { +TEST_P(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldFlush) { delegate_.SetCanWriteAnything(); EXPECT_CALL(delegate_, OnSerializedPacket(_)) @@ -325,7 +357,7 @@ TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldFlush) { CheckPacketContains(contents, 0); } -TEST_F(QuicPacketGeneratorTest, ConsumeData_NotWritable) { +TEST_P(QuicPacketGeneratorTest, ConsumeData_NotWritable) { delegate_.SetCanNotWrite(); QuicConsumedData consumed = generator_.ConsumeData( @@ -335,7 +367,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_NotWritable) { EXPECT_FALSE(generator_.HasQueuedFrames()); } -TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldNotFlush) { +TEST_P(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldNotFlush) { delegate_.SetCanWriteAnything(); generator_.StartBatchOperations(); @@ -346,7 +378,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldNotFlush) { EXPECT_TRUE(generator_.HasQueuedFrames()); } -TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldFlush) { +TEST_P(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldFlush) { delegate_.SetCanWriteAnything(); EXPECT_CALL(delegate_, OnSerializedPacket(_)) @@ -362,13 +394,36 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldFlush) { CheckPacketContains(contents, 0); } -TEST_F(QuicPacketGeneratorTest, ConsumeData_EmptyData) { +// Test the behavior of ConsumeData when the data consumed is for the crypto +// handshake stream. Ensure that the packet is always sent and padded even if +// the generator operates in batch mode. +TEST_P(QuicPacketGeneratorTest, ConsumeData_Handshake) { + delegate_.SetCanWriteAnything(); + generator_.StartBatchOperations(); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + QuicConsumedData consumed = generator_.ConsumeData( + kCryptoStreamId, MakeIOVector("foo"), 0, false, MAY_FEC_PROTECT, nullptr); + EXPECT_EQ(3u, consumed.bytes_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + + PacketContents contents; + contents.num_stream_frames = 1; + CheckPacketContains(contents, 0); + + ASSERT_EQ(1u, packets_.size()); + ASSERT_EQ(kDefaultMaxPacketSize, generator_.GetMaxPacketLength()); + EXPECT_EQ(kDefaultMaxPacketSize, packets_[0].packet->length()); +} + +TEST_P(QuicPacketGeneratorTest, ConsumeData_EmptyData) { EXPECT_DFATAL(generator_.ConsumeData(kHeadersStreamId, MakeIOVector(""), 0, false, MAY_FEC_PROTECT, nullptr), "Attempt to consume empty data without FIN."); } -TEST_F(QuicPacketGeneratorTest, +TEST_P(QuicPacketGeneratorTest, ConsumeDataMultipleTimes_WritableAndShouldNotFlush) { delegate_.SetCanWriteAnything(); generator_.StartBatchOperations(); @@ -382,7 +437,7 @@ TEST_F(QuicPacketGeneratorTest, EXPECT_TRUE(generator_.HasQueuedFrames()); } -TEST_F(QuicPacketGeneratorTest, ConsumeData_BatchOperations) { +TEST_P(QuicPacketGeneratorTest, ConsumeData_BatchOperations) { delegate_.SetCanWriteAnything(); generator_.StartBatchOperations(); @@ -405,7 +460,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_BatchOperations) { CheckPacketContains(contents, 0); } -TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecOnMaxGroupSize) { +TEST_P(QuicPacketGeneratorTest, ConsumeDataFecOnMaxGroupSize) { delegate_.SetCanWriteAnything(); // Send FEC every two packets. @@ -417,8 +472,14 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecOnMaxGroupSize) { .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); - EXPECT_CALL(delegate_, OnSerializedPacket(_)) - .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) { + // FEC packet is not sent when send policy is FEC_ALARM_TRIGGER, but FEC + // group is closed. + EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1); + } else { + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + } EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); } @@ -434,30 +495,47 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecOnMaxGroupSize) { CheckPacketHasSingleStreamFrame(0); CheckPacketHasSingleStreamFrame(1); - CheckPacketIsFec(2, 1); - CheckPacketHasSingleStreamFrame(3); + if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) { + // FEC packet is not sent when send policy is FEC_ALARM_TRIGGER. + CheckPacketHasSingleStreamFrame(2); + } else { + CheckPacketIsFec(2, 1); + CheckPacketHasSingleStreamFrame(3); + } EXPECT_TRUE(creator_->IsFecProtected()); - // The FEC packet under construction will be sent when one more packet is sent - // (since FEC group size is 2), or when OnFecTimeout is called. Send more data - // with MAY_FEC_PROTECT. This packet should also be protected, and FEC packet - // is sent since FEC group size is reached. + // If FEC send policy is FEC_ANY_TRIGGER, then the FEC packet under + // construction will be sent when one more packet is sent (since FEC group + // size is 2), or when OnFecTimeout is called. Send more data with + // MAY_FEC_PROTECT. This packet should also be protected, and FEC packet is + // sent since FEC group size is reached. + // + // If FEC send policy is FEC_ALARM_TRIGGER, FEC group is closed when the group + // size is reached. FEC packet is not sent. { InSequence dummy; EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); - EXPECT_CALL(delegate_, OnSerializedPacket(_)) - .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) { + EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1); + } else { + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + } } consumed = generator_.ConsumeData(5, CreateData(1u), 0, true, MAY_FEC_PROTECT, nullptr); EXPECT_EQ(1u, consumed.bytes_consumed); - CheckPacketHasSingleStreamFrame(4); - CheckPacketIsFec(5, 4); + if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) { + CheckPacketHasSingleStreamFrame(3); + } else { + CheckPacketHasSingleStreamFrame(4); + CheckPacketIsFec(5, 4); + } EXPECT_FALSE(creator_->IsFecProtected()); } -TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecOnTimeout) { +TEST_P(QuicPacketGeneratorTest, ConsumeDataSendsFecOnTimeout) { delegate_.SetCanWriteAnything(); creator_->set_max_packets_per_fec_group(1000); @@ -515,7 +593,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecOnTimeout) { EXPECT_FALSE(creator_->IsFecProtected()); } -TEST_F(QuicPacketGeneratorTest, GetFecTimeoutFiniteOnlyOnFirstPacketInGroup) { +TEST_P(QuicPacketGeneratorTest, GetFecTimeoutFiniteOnlyOnFirstPacketInGroup) { delegate_.SetCanWriteAnything(); creator_->set_max_packets_per_fec_group(6); @@ -608,7 +686,7 @@ TEST_F(QuicPacketGeneratorTest, GetFecTimeoutFiniteOnlyOnFirstPacketInGroup) { generator_.GetFecTimeout(/*sequence_number=*/8u)); } -TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) { +TEST_P(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. size_t length = @@ -621,7 +699,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) { // than the GetMinStreamFrameSize. QuicFramer::GetMinStreamFrameSize(1, 0, false, NOT_IN_FEC_GROUP) + 3 + QuicFramer::GetMinStreamFrameSize(1, 0, true, NOT_IN_FEC_GROUP) + 1; - creator_->SetMaxPacketLength(length); + generator_.SetMaxPacketLength(length, /*force=*/false); delegate_.SetCanWriteAnything(); { InSequence dummy; @@ -655,7 +733,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) { CheckPacketContains(contents, 1); } -TEST_F(QuicPacketGeneratorTest, NoFecPacketSentWhenBatchEnds) { +TEST_P(QuicPacketGeneratorTest, NoFecPacketSentWhenBatchEnds) { delegate_.SetCanWriteAnything(); creator_->set_max_packets_per_fec_group(6); @@ -687,7 +765,7 @@ TEST_F(QuicPacketGeneratorTest, NoFecPacketSentWhenBatchEnds) { CheckPacketIsFec(1, /*fec_group=*/1u); } -TEST_F(QuicPacketGeneratorTest, FecTimeoutOnRttChange) { +TEST_P(QuicPacketGeneratorTest, FecTimeoutOnRttChange) { EXPECT_EQ(QuicTime::Delta::Zero(), QuicPacketGeneratorPeer::GetFecTimeout(&generator_)); generator_.OnRttChange(QuicTime::Delta::FromMilliseconds(300)); @@ -695,7 +773,7 @@ TEST_F(QuicPacketGeneratorTest, FecTimeoutOnRttChange) { QuicPacketGeneratorPeer::GetFecTimeout(&generator_)); } -TEST_F(QuicPacketGeneratorTest, FecGroupSizeOnCongestionWindowChange) { +TEST_P(QuicPacketGeneratorTest, FecGroupSizeOnCongestionWindowChange) { delegate_.SetCanWriteAnything(); creator_->set_max_packets_per_fec_group(50); EXPECT_EQ(50u, creator_->max_packets_per_fec_group()); @@ -714,7 +792,7 @@ TEST_F(QuicPacketGeneratorTest, FecGroupSizeOnCongestionWindowChange) { EXPECT_EQ(2u, creator_->max_packets_per_fec_group()); } -TEST_F(QuicPacketGeneratorTest, FecGroupSizeChangeWithOpenGroup) { +TEST_P(QuicPacketGeneratorTest, FecGroupSizeChangeWithOpenGroup) { delegate_.SetCanWriteAnything(); generator_.StartBatchOperations(); creator_->set_max_packets_per_fec_group(50); @@ -742,25 +820,34 @@ TEST_F(QuicPacketGeneratorTest, FecGroupSizeChangeWithOpenGroup) { generator_.OnCongestionWindowChange(2); EXPECT_EQ(2u, creator_->max_packets_per_fec_group()); - // Send enough data to trigger one unprotected data packet, causing the FEC - // packet to also be sent. + // If FEC send policy is FEC_ANY_TRIGGER, then send enough data to trigger one + // unprotected data packet, causing the FEC packet to also be sent. + // + // If FEC send policy is FEC_ALARM_TRIGGER, FEC group is closed and FEC packet + // is not sent. { InSequence dummy; EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); - EXPECT_CALL(delegate_, OnSerializedPacket(_)) - .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) { + EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1); + } else { + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + } } consumed = generator_.ConsumeData(7, CreateData(kDefaultMaxPacketSize), 0, true, MAY_FEC_PROTECT, nullptr); EXPECT_EQ(kDefaultMaxPacketSize, consumed.bytes_consumed); - // Verify that one FEC packet was sent. - CheckPacketIsFec(4, /*fec_group=*/1u); + if (generator_.fec_send_policy() == FEC_ANY_TRIGGER) { + // Verify that one FEC packet was sent. + CheckPacketIsFec(4, /*fec_group=*/1u); + } EXPECT_FALSE(creator_->IsFecGroupOpen()); EXPECT_FALSE(creator_->IsFecProtected()); } -TEST_F(QuicPacketGeneratorTest, SwitchFecOnOff) { +TEST_P(QuicPacketGeneratorTest, SwitchFecOnOff) { delegate_.SetCanWriteAnything(); creator_->set_max_packets_per_fec_group(2); EXPECT_FALSE(creator_->IsFecProtected()); @@ -784,8 +871,13 @@ TEST_F(QuicPacketGeneratorTest, SwitchFecOnOff) { .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); - EXPECT_CALL(delegate_, OnSerializedPacket(_)) - .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) { + // If FEC send policy is FEC_ALARM_TRIGGER, FEC group is closed. + EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1); + } else { + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + } EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); } @@ -796,17 +888,29 @@ TEST_F(QuicPacketGeneratorTest, SwitchFecOnOff) { EXPECT_EQ(data_len, consumed.bytes_consumed); EXPECT_FALSE(generator_.HasQueuedFrames()); - // Verify that packets sent were 3 data and 1 FEC. + // If FEC send policy is FEC_ANY_TRIGGER, verify that packets sent were 3 data + // and 1 FEC. + // + // If FEC send policy is FEC_ALARM_TRIGGER, verify that packets sent were 3 + // data and FEC group is closed. CheckPacketHasSingleStreamFrame(1); CheckPacketHasSingleStreamFrame(2); - CheckPacketIsFec(3, /*fec_group=*/2u); - CheckPacketHasSingleStreamFrame(4); + if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) { + CheckPacketHasSingleStreamFrame(3); + } else { + CheckPacketIsFec(3, /*fec_group=*/2u); + CheckPacketHasSingleStreamFrame(4); + } // Calling OnFecTimeout should emit the pending FEC packet. EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); generator_.OnFecTimeout(); - CheckPacketIsFec(5, /*fec_group=*/5u); + if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) { + CheckPacketIsFec(4, /*fec_group=*/4u); + } else { + CheckPacketIsFec(5, /*fec_group=*/5u); + } // Send one unprotected data packet. EXPECT_CALL(delegate_, OnSerializedPacket(_)) @@ -817,10 +921,14 @@ TEST_F(QuicPacketGeneratorTest, SwitchFecOnOff) { EXPECT_FALSE(generator_.HasQueuedFrames()); EXPECT_FALSE(creator_->IsFecProtected()); // Verify that one unprotected data packet was sent. - CheckPacketContains(contents, 6); + if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) { + CheckPacketContains(contents, 5); + } else { + CheckPacketContains(contents, 6); + } } -TEST_F(QuicPacketGeneratorTest, SwitchFecOnWithPendingFrameInCreator) { +TEST_P(QuicPacketGeneratorTest, SwitchFecOnWithPendingFrameInCreator) { delegate_.SetCanWriteAnything(); // Enable FEC. creator_->set_max_packets_per_fec_group(2); @@ -848,7 +956,7 @@ TEST_F(QuicPacketGeneratorTest, SwitchFecOnWithPendingFrameInCreator) { EXPECT_TRUE(creator_->HasPendingFrames()); } -TEST_F(QuicPacketGeneratorTest, SwitchFecOnWithPendingFramesInGenerator) { +TEST_P(QuicPacketGeneratorTest, SwitchFecOnWithPendingFramesInGenerator) { // Enable FEC. creator_->set_max_packets_per_fec_group(2); @@ -882,7 +990,7 @@ TEST_F(QuicPacketGeneratorTest, SwitchFecOnWithPendingFramesInGenerator) { EXPECT_TRUE(creator_->IsFecProtected()); } -TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffWithSubsequentFramesProtected) { +TEST_P(QuicPacketGeneratorTest, SwitchFecOnOffWithSubsequentFramesProtected) { delegate_.SetCanWriteAnything(); // Enable FEC. @@ -914,7 +1022,7 @@ TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffWithSubsequentFramesProtected) { EXPECT_TRUE(creator_->IsFecProtected()); } -TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffWithSubsequentPacketsProtected) { +TEST_P(QuicPacketGeneratorTest, SwitchFecOnOffWithSubsequentPacketsProtected) { delegate_.SetCanWriteAnything(); // Enable FEC. @@ -942,21 +1050,28 @@ TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffWithSubsequentPacketsProtected) { InSequence dummy; EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); - EXPECT_CALL(delegate_, OnSerializedPacket(_)) - .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) { + EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1); + } else { + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + } } consumed = generator_.ConsumeData(5, CreateData(1u), 0, true, MAY_FEC_PROTECT, nullptr); EXPECT_EQ(1u, consumed.bytes_consumed); contents.num_stream_frames = 1u; CheckPacketContains(contents, 1); - CheckPacketIsFec(2, /*fec_group=*/1u); + if (generator_.fec_send_policy() == FEC_ANY_TRIGGER) { + // FEC packet is sent when send policy is FEC_ANY_TRIGGER. + CheckPacketIsFec(2, /*fec_group=*/1u); + } // FEC protection should be off in creator. EXPECT_FALSE(creator_->IsFecProtected()); } -TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffThenOnWithCreatorProtectionOn) { +TEST_P(QuicPacketGeneratorTest, SwitchFecOnOffThenOnWithCreatorProtectionOn) { delegate_.SetCanWriteAnything(); generator_.StartBatchOperations(); @@ -990,20 +1105,154 @@ TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffThenOnWithCreatorProtectionOn) { InSequence dummy; EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); - EXPECT_CALL(delegate_, OnSerializedPacket(_)) - .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) { + EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1); + } else { + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + } } consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true, MUST_FEC_PROTECT, nullptr); EXPECT_EQ(data_len, consumed.bytes_consumed); CheckPacketContains(contents, 1); - CheckPacketIsFec(2, /*fec_group=*/1u); + if (generator_.fec_send_policy() == FEC_ANY_TRIGGER) { + // FEC packet is sent when send policy is FEC_ANY_TRIGGER. + CheckPacketIsFec(2, /*fec_group=*/1u); + } // FEC protection should remain on in creator. EXPECT_TRUE(creator_->IsFecProtected()); } -TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) { +TEST_P(QuicPacketGeneratorTest, ResetFecGroupNoTimeout) { + delegate_.SetCanWriteAnything(); + // Send FEC packet after 2 packets. + creator_->set_max_packets_per_fec_group(2); + EXPECT_FALSE(creator_->IsFecProtected()); + + // Send two packets so that when this data is consumed, two packets are sent + // out. In FEC_TRIGGER_ANY, this will cause an FEC packet to be sent out and + // with FEC_TRIGGER_ALARM, this will cause a Reset to be called. In both + // cases, the creator's fec protection will be turned off afterwards. + { + InSequence dummy; + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) { + // FEC packet is not sent when send policy is FEC_ALARM_TRIGGER, but FEC + // group is closed. + EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1); + } else { + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + } + // Fin Packet. + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + } + size_t data_len = 2 * kDefaultMaxPacketSize; + QuicConsumedData consumed = generator_.ConsumeData( + 5, CreateData(data_len), 0, true, MUST_FEC_PROTECT, nullptr); + EXPECT_EQ(data_len, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + CheckPacketHasSingleStreamFrame(0); + CheckPacketHasSingleStreamFrame(1); + if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) { + // FEC packet is not sent when send policy is FEC_ALARM_TRIGGER. + CheckPacketHasSingleStreamFrame(2); + } else { + // FEC packet is sent after 2 packets and when send policy is + // FEC_ANY_TRIGGER. + CheckPacketIsFec(2, 1); + CheckPacketHasSingleStreamFrame(3); + } + EXPECT_TRUE(creator_->IsFecProtected()); + + // Do the same send (with MUST_FEC_PROTECT) on a different stream id. + { + InSequence dummy; + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + // FEC packet is sent after 2 packets and when send policy is + // FEC_ANY_TRIGGER. When policy is FEC_ALARM_TRIGGER, FEC group is closed + // and FEC packet is not sent. + if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) { + EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1); + } else { + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + } + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + // FEC packet is sent after 2 packets and when send policy is + // FEC_ANY_TRIGGER. When policy is FEC_ALARM_TRIGGER, FEC group is closed + // and FEC packet is not sent. + if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) { + EXPECT_CALL(delegate_, OnResetFecGroup()).Times(1); + } else { + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + } + } + consumed = generator_.ConsumeData(7, CreateData(data_len), 0, true, + MUST_FEC_PROTECT, nullptr); + EXPECT_EQ(data_len, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + if (generator_.fec_send_policy() == FEC_ALARM_TRIGGER) { + CheckPacketHasSingleStreamFrame(3); + CheckPacketHasSingleStreamFrame(4); + CheckPacketHasSingleStreamFrame(5); + } else { + CheckPacketHasSingleStreamFrame(4); + // FEC packet is sent after 2 packets and when send policy is + // FEC_ANY_TRIGGER. + CheckPacketIsFec(5, 4); + CheckPacketHasSingleStreamFrame(6); + CheckPacketHasSingleStreamFrame(7); + // FEC packet is sent after 2 packets and when send policy is + // FEC_ANY_TRIGGER. + CheckPacketIsFec(8, 7); + } + EXPECT_TRUE(creator_->IsFecProtected()); +} + +// 1. Create and send one packet with MUST_FEC_PROTECT. +// 2. Call FecTimeout, expect FEC packet is sent. +// 3. Do the same thing over again, with a different stream id. +TEST_P(QuicPacketGeneratorTest, FecPacketSentOnFecTimeout) { + delegate_.SetCanWriteAnything(); + creator_->set_max_packets_per_fec_group(1000); + EXPECT_FALSE(creator_->IsFecProtected()); + + for (int i = 1; i < 4; i = i + 2) { + // Send data with MUST_FEC_PROTECT flag. No FEC packet is emitted, but the + // creator FEC protects all data. + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + QuicConsumedData consumed = generator_.ConsumeData( + i + 2, CreateData(1u), 0, true, MUST_FEC_PROTECT, nullptr); + EXPECT_EQ(1u, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + CheckPacketHasSingleStreamFrame(0); + EXPECT_TRUE(creator_->IsFecProtected()); + + // Calling OnFecTimeout should cause the FEC packet to be emitted. + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + generator_.OnFecTimeout(); + CheckPacketIsFec(i, i); + EXPECT_FALSE(creator_->IsFecProtected()); + } +} + +TEST_P(QuicPacketGeneratorTest, NotWritableThenBatchOperations) { delegate_.SetCanNotWrite(); generator_.SetShouldSendAck(false); @@ -1036,7 +1285,7 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) { CheckPacketContains(contents, 0); } -TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) { +TEST_P(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) { delegate_.SetCanNotWrite(); generator_.SetShouldSendAck(false); @@ -1084,7 +1333,7 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) { CheckPacketContains(contents2, 1); } -TEST_F(QuicPacketGeneratorTest, TestConnectionIdLength) { +TEST_P(QuicPacketGeneratorTest, TestConnectionIdLength) { generator_.SetConnectionIdLength(0); EXPECT_EQ(PACKET_0BYTE_CONNECTION_ID, creator_->connection_id_length()); generator_.SetConnectionIdLength(1); @@ -1107,5 +1356,302 @@ TEST_F(QuicPacketGeneratorTest, TestConnectionIdLength) { EXPECT_EQ(PACKET_8BYTE_CONNECTION_ID, creator_->connection_id_length()); } +// Test whether SetMaxPacketLength() works in the situation when the queue is +// empty, and we send three packets worth of data. +TEST_P(QuicPacketGeneratorTest, SetMaxPacketLength_Initial) { + delegate_.SetCanWriteAnything(); + + // Send enough data for three packets. + size_t data_len = 3 * kDefaultMaxPacketSize + 1; + size_t packet_len = kDefaultMaxPacketSize + 100; + ASSERT_LE(packet_len, kMaxPacketSize); + generator_.SetMaxPacketLength(packet_len, /*force=*/false); + EXPECT_EQ(packet_len, generator_.GetCurrentMaxPacketLength()); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .Times(3) + .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + QuicConsumedData consumed = + generator_.ConsumeData(kHeadersStreamId, CreateData(data_len), + /*offset=*/2, + /*fin=*/true, MAY_FEC_PROTECT, nullptr); + EXPECT_EQ(data_len, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + + // We expect three packets, and first two of them have to be of packet_len + // size. We check multiple packets (instead of just one) because we want to + // ensure that |max_packet_length_| does not get changed incorrectly by the + // generator after first packet is serialized. + ASSERT_EQ(3u, packets_.size()); + EXPECT_EQ(packet_len, packets_[0].packet->length()); + EXPECT_EQ(packet_len, packets_[1].packet->length()); + CheckAllPacketsHaveSingleStreamFrame(); +} + +// Test whether SetMaxPacketLength() works in the situation when we first write +// data, then change packet size, then write data again. +TEST_P(QuicPacketGeneratorTest, SetMaxPacketLength_Middle) { + delegate_.SetCanWriteAnything(); + + // We send enough data to overflow default packet length, but not the altered + // one. + size_t data_len = kDefaultMaxPacketSize; + size_t packet_len = kDefaultMaxPacketSize + 100; + ASSERT_LE(packet_len, kMaxPacketSize); + + // We expect to see three packets in total. + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .Times(3) + .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + // Send two packets before packet size change. + QuicConsumedData consumed = + generator_.ConsumeData(kHeadersStreamId, CreateData(data_len), + /*offset=*/2, + /*fin=*/false, MAY_FEC_PROTECT, nullptr); + EXPECT_EQ(data_len, consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + + // Make sure we already have two packets. + ASSERT_EQ(2u, packets_.size()); + + // Increase packet size. + generator_.SetMaxPacketLength(packet_len, /*force=*/false); + EXPECT_EQ(packet_len, generator_.GetCurrentMaxPacketLength()); + + // Send a packet after packet size change. + consumed = generator_.ConsumeData(kHeadersStreamId, CreateData(data_len), + 2 + data_len, + /*fin=*/true, MAY_FEC_PROTECT, nullptr); + EXPECT_EQ(data_len, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + + // We expect first data chunk to get fragmented, but the second one to fit + // into a single packet. + ASSERT_EQ(3u, packets_.size()); + EXPECT_EQ(kDefaultMaxPacketSize, packets_[0].packet->length()); + EXPECT_LE(kDefaultMaxPacketSize, packets_[2].packet->length()); + CheckAllPacketsHaveSingleStreamFrame(); +} + +// Test whether SetMaxPacketLength() works correctly when we change the packet +// size in the middle of the batched packet. +TEST_P(QuicPacketGeneratorTest, SetMaxPacketLength_Midpacket) { + delegate_.SetCanWriteAnything(); + generator_.StartBatchOperations(); + + size_t first_write_len = kDefaultMaxPacketSize / 2; + size_t second_write_len = kDefaultMaxPacketSize; + size_t packet_len = kDefaultMaxPacketSize + 100; + ASSERT_LE(packet_len, kMaxPacketSize); + + // First send half of the packet worth of data. We are in the batch mode, so + // should not cause packet serialization. + QuicConsumedData consumed = + generator_.ConsumeData(kHeadersStreamId, CreateData(first_write_len), + /*offset=*/2, + /*fin=*/false, MAY_FEC_PROTECT, nullptr); + EXPECT_EQ(first_write_len, consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_TRUE(generator_.HasQueuedFrames()); + + // Make sure we have no packets so far. + ASSERT_EQ(0u, packets_.size()); + + // Increase packet size. Ensure it's not immediately enacted. + generator_.SetMaxPacketLength(packet_len, /*force=*/false); + EXPECT_EQ(packet_len, generator_.GetMaxPacketLength()); + EXPECT_EQ(kDefaultMaxPacketSize, generator_.GetCurrentMaxPacketLength()); + + // We expect to see exactly one packet serialized after that, since we are in + // batch mode and we have sent approximately 3/2 of our MTU. + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + // Send a packet worth of data to the same stream. This should trigger + // serialization of other packet. + consumed = + generator_.ConsumeData(kHeadersStreamId, CreateData(second_write_len), + /*offset=*/2 + first_write_len, + /*fin=*/true, MAY_FEC_PROTECT, nullptr); + EXPECT_EQ(second_write_len, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_TRUE(generator_.HasQueuedFrames()); + + // We expect the first packet to contain two frames, and to not reflect the + // packet size change. + ASSERT_EQ(1u, packets_.size()); + EXPECT_EQ(kDefaultMaxPacketSize, packets_[0].packet->length()); + + PacketContents contents; + contents.num_stream_frames = 2; + CheckPacketContains(contents, 0); +} + +// Test whether SetMaxPacketLength() works correctly when we force the change of +// the packet size in the middle of the batched packet. +TEST_P(QuicPacketGeneratorTest, SetMaxPacketLength_MidpacketFlush) { + delegate_.SetCanWriteAnything(); + generator_.StartBatchOperations(); + + size_t first_write_len = kDefaultMaxPacketSize / 2; + size_t packet_len = kDefaultMaxPacketSize + 100; + size_t second_write_len = packet_len + 1; + ASSERT_LE(packet_len, kMaxPacketSize); + + // First send half of the packet worth of data. We are in the batch mode, so + // should not cause packet serialization. + QuicConsumedData consumed = + generator_.ConsumeData(kHeadersStreamId, CreateData(first_write_len), + /*offset=*/2, + /*fin=*/false, MAY_FEC_PROTECT, nullptr); + EXPECT_EQ(first_write_len, consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_TRUE(generator_.HasQueuedFrames()); + + // Make sure we have no packets so far. + ASSERT_EQ(0u, packets_.size()); + + // Expect a packet to be flushed. + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + // Increase packet size. Ensure it's immediately enacted. + generator_.SetMaxPacketLength(packet_len, /*force=*/true); + EXPECT_EQ(packet_len, generator_.GetMaxPacketLength()); + EXPECT_EQ(packet_len, generator_.GetCurrentMaxPacketLength()); + EXPECT_FALSE(generator_.HasQueuedFrames()); + + // We expect to see exactly one packet serialized after that, because we send + // a value somewhat exceeding new max packet size, and the tail data does not + // get serialized because we are still in the batch mode. + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + // Send a more than a packet worth of data to the same stream. This should + // trigger serialization of one packet, and queue another one. + consumed = + generator_.ConsumeData(kHeadersStreamId, CreateData(second_write_len), + /*offset=*/2 + first_write_len, + /*fin=*/true, MAY_FEC_PROTECT, nullptr); + EXPECT_EQ(second_write_len, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_TRUE(generator_.HasQueuedFrames()); + + // We expect the first packet to be underfilled, and the second packet be up + // to the new max packet size. + ASSERT_EQ(2u, packets_.size()); + EXPECT_GT(kDefaultMaxPacketSize, packets_[0].packet->length()); + EXPECT_EQ(packet_len, packets_[1].packet->length()); + + CheckAllPacketsHaveSingleStreamFrame(); +} + +// Test sending an MTU probe, without any surrounding data. +TEST_P(QuicPacketGeneratorTest, GenerateMtuDiscoveryPacket_Simple) { + delegate_.SetCanWriteAnything(); + + const size_t target_mtu = kDefaultMaxPacketSize + 100; + static_assert(target_mtu < kMaxPacketSize, + "The MTU probe used by the test exceeds maximum packet size"); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + generator_.GenerateMtuDiscoveryPacket(target_mtu, nullptr); + + EXPECT_FALSE(generator_.HasQueuedFrames()); + ASSERT_EQ(1u, packets_.size()); + EXPECT_EQ(target_mtu, packets_[0].packet->length()); + + PacketContents contents; + contents.num_mtu_discovery_frames = 1; + CheckPacketContains(contents, 0); +} + +// Test sending an MTU probe. Surround it with data, to ensure that it resets +// the MTU to the value before the probe was sent. +TEST_P(QuicPacketGeneratorTest, GenerateMtuDiscoveryPacket_SurroundedByData) { + delegate_.SetCanWriteAnything(); + + const size_t target_mtu = kDefaultMaxPacketSize + 100; + static_assert(target_mtu < kMaxPacketSize, + "The MTU probe used by the test exceeds maximum packet size"); + + // Send enough data so it would always cause two packets to be sent. + const size_t data_len = target_mtu + 1; + + // Send a total of five packets: two packets before the probe, the probe + // itself, and two packets after the probe. + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .Times(5) + .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + + // Send data before the MTU probe. + QuicConsumedData consumed = + generator_.ConsumeData(kHeadersStreamId, CreateData(data_len), + /*offset=*/2, + /*fin=*/false, MAY_FEC_PROTECT, nullptr); + EXPECT_EQ(data_len, consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + + // Send the MTU probe. + generator_.GenerateMtuDiscoveryPacket(target_mtu, nullptr); + EXPECT_FALSE(generator_.HasQueuedFrames()); + + // Send data after the MTU probe. + consumed = generator_.ConsumeData(kHeadersStreamId, CreateData(data_len), + /*offset=*/2 + data_len, + /*fin=*/true, MAY_FEC_PROTECT, nullptr); + EXPECT_EQ(data_len, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + + ASSERT_EQ(5u, packets_.size()); + EXPECT_EQ(kDefaultMaxPacketSize, packets_[0].packet->length()); + EXPECT_EQ(target_mtu, packets_[2].packet->length()); + EXPECT_EQ(kDefaultMaxPacketSize, packets_[3].packet->length()); + + PacketContents probe_contents; + probe_contents.num_mtu_discovery_frames = 1; + + CheckPacketHasSingleStreamFrame(0); + CheckPacketHasSingleStreamFrame(1); + CheckPacketContains(probe_contents, 2); + CheckPacketHasSingleStreamFrame(3); + CheckPacketHasSingleStreamFrame(4); +} + +TEST_P(QuicPacketGeneratorTest, DontCrashOnInvalidStopWaiting) { + // Test added to ensure the generator does not crash when an invalid frame is + // added. Because this is an indication of internal programming errors, + // DFATALs are expected. + // A 1 byte sequence number length can't encode a gap of 1000. + QuicPacketCreatorPeer::SetSequenceNumber(creator_, 1000); + + delegate_.SetCanNotWrite(); + generator_.SetShouldSendAck(true); + delegate_.SetCanWriteAnything(); + generator_.StartBatchOperations(); + + // Set up frames to write into the creator when control frames are written. + EXPECT_CALL(delegate_, PopulateAckFrame(_)); + EXPECT_CALL(delegate_, PopulateStopWaitingFrame(_)); + // Generator should have queued control frames, and creator should be empty. + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_FALSE(creator_->HasPendingFrames()); + + // This will not serialize any packets, because of the invalid frame. + EXPECT_CALL(delegate_, + CloseConnection(QUIC_FAILED_TO_SERIALIZE_PACKET, false)); + EXPECT_DFATAL(generator_.FinishBatchOperations(), + "sequence_number_length 1 is too small " + "for least_unacked_delta: 1001"); +} + } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_packet_reader.cc b/chromium/net/quic/quic_packet_reader.cc index 93451daaaba..c58a51fa7f8 100644 --- a/chromium/net/quic/quic_packet_reader.cc +++ b/chromium/net/quic/quic_packet_reader.cc @@ -4,7 +4,10 @@ #include "net/quic/quic_packet_reader.h" -#include "base/metrics/histogram.h" +#include "base/location.h" +#include "base/metrics/histogram_macros.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" #include "net/base/net_errors.h" namespace net { @@ -44,7 +47,7 @@ void QuicPacketReader::StartReading() { // Data was read, process it. // 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( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&QuicPacketReader::OnReadComplete, weak_factory_.GetWeakPtr(), rv)); } else { diff --git a/chromium/net/quic/quic_protocol.cc b/chromium/net/quic/quic_protocol.cc index 7fa153d9c51..c110522bb20 100644 --- a/chromium/net/quic/quic_protocol.cc +++ b/chromium/net/quic/quic_protocol.cc @@ -73,20 +73,20 @@ QuicPacketPublicHeader::QuicPacketPublicHeader( QuicPacketPublicHeader::~QuicPacketPublicHeader() {} QuicPacketHeader::QuicPacketHeader() - : fec_flag(false), + : packet_sequence_number(0), + fec_flag(false), entropy_flag(false), entropy_hash(0), - packet_sequence_number(0), is_in_fec_group(NOT_IN_FEC_GROUP), fec_group(0) { } QuicPacketHeader::QuicPacketHeader(const QuicPacketPublicHeader& header) : public_header(header), + packet_sequence_number(0), fec_flag(false), entropy_flag(false), entropy_hash(0), - packet_sequence_number(0), is_in_fec_group(NOT_IN_FEC_GROUP), fec_group(0) { } @@ -114,21 +114,10 @@ QuicStreamFrame::QuicStreamFrame(const QuicStreamFrame& frame) QuicStreamFrame::QuicStreamFrame(QuicStreamId stream_id, bool fin, QuicStreamOffset offset, - IOVector data) + StringPiece data) : stream_id(stream_id), fin(fin), offset(offset), data(data) { } -string* QuicStreamFrame::GetDataAsString() const { - string* data_string = new string(); - data_string->reserve(data.TotalBufferSize()); - for (size_t i = 0; i < data.Size(); ++i) { - data_string->append(static_cast<char*>(data.iovec()[i].iov_base), - data.iovec()[i].iov_len); - } - DCHECK_EQ(data_string->size(), data.TotalBufferSize()); - return data_string; -} - uint32 MakeQuicTag(char a, char b, char c, char d) { return static_cast<uint32>(a) | static_cast<uint32>(b) << 8 | @@ -300,6 +289,10 @@ QuicFrame::QuicFrame(QuicAckFrame* frame) ack_frame(frame) { } +QuicFrame::QuicFrame(QuicMtuDiscoveryFrame* frame) + : type(MTU_DISCOVERY_FRAME), mtu_discovery_frame(frame) { +} + QuicFrame::QuicFrame(QuicStopWaitingFrame* frame) : type(STOP_WAITING_FRAME), stop_waiting_frame(frame) { @@ -412,6 +405,10 @@ ostream& operator<<(ostream& os, const QuicFrame& frame) { os << "type { PING_FRAME } "; break; } + case MTU_DISCOVERY_FRAME: { + os << "type { MTU_DISCOVERY_FRAME } "; + break; + } default: { LOG(ERROR) << "Unknown frame type: " << frame.type; break; @@ -457,8 +454,7 @@ 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())) + << "data { " << QuicUtils::StringToHexASCIIDump(stream_frame.data) << " }\n"; return os; } @@ -559,7 +555,9 @@ StringPiece QuicPacket::Plaintext() const { } RetransmittableFrames::RetransmittableFrames(EncryptionLevel level) - : encryption_level_(level), has_crypto_handshake_(NOT_HANDSHAKE) { + : encryption_level_(level), + has_crypto_handshake_(NOT_HANDSHAKE), + needs_padding_(false) { } RetransmittableFrames::~RetransmittableFrames() { @@ -574,6 +572,9 @@ RetransmittableFrames::~RetransmittableFrames() { case ACK_FRAME: delete it->ack_frame; break; + case MTU_DISCOVERY_FRAME: + delete it->mtu_discovery_frame; + break; case STOP_WAITING_FRAME: delete it->stop_waiting_frame; break; @@ -599,27 +600,24 @@ RetransmittableFrames::~RetransmittableFrames() { DCHECK(false) << "Cannot delete type: " << it->type; } } - STLDeleteElements(&stream_data_); -} - -const QuicFrame& RetransmittableFrames::AddStreamFrame( - QuicStreamFrame* stream_frame) { - // Make an owned copy of the stream frame's data. - stream_data_.push_back(stream_frame->GetDataAsString()); - // Ensure the stream frame's IOVector points to the owned copy of the data. - stream_frame->data.Clear(); - stream_frame->data.Append(const_cast<char*>(stream_data_.back()->data()), - stream_data_.back()->size()); - frames_.push_back(QuicFrame(stream_frame)); - if (stream_frame->stream_id == kCryptoStreamId) { - has_crypto_handshake_ = IS_HANDSHAKE; + for (const char* buffer : stream_data_) { + delete[] buffer; } - return frames_.back(); } -const QuicFrame& RetransmittableFrames::AddNonStreamFrame( - const QuicFrame& frame) { - DCHECK_NE(frame.type, STREAM_FRAME); +const QuicFrame& RetransmittableFrames::AddFrame(const QuicFrame& frame) { + return AddFrame(frame, nullptr); +} + +const QuicFrame& RetransmittableFrames::AddFrame(const QuicFrame& frame, + char* buffer) { + if (frame.type == STREAM_FRAME && + frame.stream_frame->stream_id == kCryptoStreamId) { + has_crypto_handshake_ = IS_HANDSHAKE; + } + if (buffer != nullptr) { + stream_data_.push_back(buffer); + } frames_.push_back(frame); return frames_.back(); } @@ -642,11 +640,11 @@ SerializedPacket::SerializedPacket( QuicEncryptedPacket* packet, QuicPacketEntropyHash entropy_hash, RetransmittableFrames* retransmittable_frames) - : sequence_number(sequence_number), + : packet(packet), + retransmittable_frames(retransmittable_frames), + sequence_number(sequence_number), sequence_number_length(sequence_number_length), - packet(packet), entropy_hash(entropy_hash), - retransmittable_frames(retransmittable_frames), is_fec_packet(false) { } diff --git a/chromium/net/quic/quic_protocol.h b/chromium/net/quic/quic_protocol.h index 8c2bca76bba..d68f300c505 100644 --- a/chromium/net/quic/quic_protocol.h +++ b/chromium/net/quic/quic_protocol.h @@ -6,6 +6,7 @@ #define NET_QUIC_QUIC_PROTOCOL_H_ #include <stddef.h> +#include <stdint.h> #include <limits> #include <list> #include <map> @@ -20,11 +21,12 @@ #include "base/logging.h" #include "base/strings/string_piece.h" #include "net/base/int128.h" +#include "net/base/iovec.h" #include "net/base/ip_endpoint.h" #include "net/base/net_export.h" -#include "net/quic/iovector.h" #include "net/quic/quic_bandwidth.h" #include "net/quic/quic_time.h" +#include "net/quic/quic_types.h" namespace net { @@ -72,8 +74,11 @@ const uint32 kMinimumFlowControlSendWindow = 16 * 1024; // 16 KB // Minimum size of the CWND, in packets, when doing bandwidth resumption. const QuicPacketCount kMinCongestionWindowForBandwidthResumption = 10; -// Maximum size of the CWND, in packets, for TCP congestion control algorithms. -const QuicPacketCount kMaxTcpCongestionWindow = 200; +// Maximum size of the CWND, in packets, for bandwidth resumption. +const QuicPacketCount kMaxResumptionCwnd = 200; + +// Maximum number of tracked packets. +const QuicPacketCount kMaxTrackedPackets = 5000; // Default size of the socket receive buffer in bytes. const QuicByteCount kDefaultSocketReceiveBuffer = 256 * 1024; @@ -169,7 +174,7 @@ const int kUFloat16MaxExponent = (1 << kUFloat16ExponentBits) - 2; // 30 const int kUFloat16MantissaBits = 16 - kUFloat16ExponentBits; // 11 const int kUFloat16MantissaEffectiveBits = kUFloat16MantissaBits + 1; // 12 const uint64 kUFloat16MaxValue = // 0x3FFC0000000 - ((GG_UINT64_C(1) << kUFloat16MantissaEffectiveBits) - 1) << + ((UINT64_C(1) << kUFloat16MantissaEffectiveBits) - 1) << kUFloat16MaxExponent; enum TransmissionType { @@ -211,6 +216,14 @@ enum FecPolicy { FEC_PROTECT_OPTIONAL // Data in the stream does not need FEC protection. }; +// Indicates FEC policy about when to send FEC packet. +enum FecSendPolicy { + // Send FEC packet when FEC group is full or when FEC alarm goes off. + FEC_ANY_TRIGGER, + // Send FEC packet only when FEC alarm goes off. + FEC_ALARM_TRIGGER +}; + enum QuicFrameType { // Regular frame types. The values set here cannot change without the // introduction of a new QUIC version. @@ -227,6 +240,8 @@ enum QuicFrameType { // the wire and their values do not need to be stable. STREAM_FRAME, ACK_FRAME, + // The path MTU discovery frame is encoded as a PING frame on the wire. + MTU_DISCOVERY_FRAME, NUM_FRAME_TYPES }; @@ -527,6 +542,12 @@ enum QuicErrorCode { QUIC_CONNECTION_CANCELLED = 70, // Disabled QUIC because of high packet loss rate. QUIC_BAD_PACKET_LOSS_RATE = 71, + // Disabled QUIC because of too many PUBLIC_RESETs post handshake. + QUIC_PUBLIC_RESETS_POST_HANDSHAKE = 73, + // Disabled QUIC because of too many timeouts with streams open. + QUIC_TIMEOUTS_WITH_OPEN_STREAMS = 74, + // Closed because we failed to serialize a packet. + QUIC_FAILED_TO_SERIALIZE_PACKET = 75, // Crypto errors. @@ -586,7 +607,7 @@ enum QuicErrorCode { QUIC_VERSION_NEGOTIATION_MISMATCH = 55, // No error. Used as bound while iterating. - QUIC_LAST_ERROR = 73, + QUIC_LAST_ERROR = 76, }; struct NET_EXPORT_PRIVATE QuicPacketPublicHeader { @@ -616,10 +637,10 @@ struct NET_EXPORT_PRIVATE QuicPacketHeader { std::ostream& os, const QuicPacketHeader& s); QuicPacketPublicHeader public_header; + QuicPacketSequenceNumber packet_sequence_number; bool fec_flag; bool entropy_flag; QuicPacketEntropyHash entropy_hash; - QuicPacketSequenceNumber packet_sequence_number; InFecGroup is_in_fec_group; QuicFecGroupNumber fec_group; }; @@ -659,25 +680,25 @@ struct NET_EXPORT_PRIVATE QuicPaddingFrame { struct NET_EXPORT_PRIVATE QuicPingFrame { }; +// A path MTU discovery frame contains no payload and is serialized as a ping +// frame. +struct NET_EXPORT_PRIVATE QuicMtuDiscoveryFrame {}; + struct NET_EXPORT_PRIVATE QuicStreamFrame { QuicStreamFrame(); QuicStreamFrame(const QuicStreamFrame& frame); QuicStreamFrame(QuicStreamId stream_id, bool fin, QuicStreamOffset offset, - IOVector data); + base::StringPiece 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; - QuicStreamId stream_id; bool fin; QuicStreamOffset offset; // Location of this data in the stream. - IOVector data; + base::StringPiece data; }; // TODO(ianswett): Re-evaluate the trade-offs of hash_set vs set when framing @@ -871,6 +892,7 @@ struct NET_EXPORT_PRIVATE QuicFrame { explicit QuicFrame(QuicPaddingFrame* padding_frame); explicit QuicFrame(QuicStreamFrame* stream_frame); explicit QuicFrame(QuicAckFrame* frame); + explicit QuicFrame(QuicMtuDiscoveryFrame* frame); explicit QuicFrame(QuicRstStreamFrame* frame); explicit QuicFrame(QuicConnectionCloseFrame* frame); @@ -888,6 +910,7 @@ struct NET_EXPORT_PRIVATE QuicFrame { QuicPaddingFrame* padding_frame; QuicStreamFrame* stream_frame; QuicAckFrame* ack_frame; + QuicMtuDiscoveryFrame* mtu_discovery_frame; QuicStopWaitingFrame* stop_waiting_frame; @@ -983,12 +1006,10 @@ class NET_EXPORT_PRIVATE RetransmittableFrames { explicit RetransmittableFrames(EncryptionLevel level); ~RetransmittableFrames(); - // Allocates a local copy of the referenced StringPiece has QuicStreamFrame - // use it. - // Takes ownership of |stream_frame|. - const QuicFrame& AddStreamFrame(QuicStreamFrame* stream_frame); // Takes ownership of the frame inside |frame|. - const QuicFrame& AddNonStreamFrame(const QuicFrame& frame); + const QuicFrame& AddFrame(const QuicFrame& frame); + // Takes ownership of the frame inside |frame| and |buffer|. + const QuicFrame& AddFrame(const QuicFrame& frame, char* buffer); // Removes all stream frames associated with |stream_id|. void RemoveFramesForStream(QuicStreamId stream_id); @@ -1002,12 +1023,17 @@ class NET_EXPORT_PRIVATE RetransmittableFrames { return encryption_level_; } + bool needs_padding() const { return needs_padding_; } + + void set_needs_padding(bool needs_padding) { needs_padding_ = needs_padding; } + private: QuicFrames frames_; const EncryptionLevel encryption_level_; IsHandshake has_crypto_handshake_; + bool needs_padding_; // Data referenced by the StringPiece of a QuicStreamFrame. - std::vector<std::string*> stream_data_; + std::vector<const char*> stream_data_; DISALLOW_COPY_AND_ASSIGN(RetransmittableFrames); }; @@ -1020,11 +1046,11 @@ struct NET_EXPORT_PRIVATE SerializedPacket { RetransmittableFrames* retransmittable_frames); ~SerializedPacket(); + QuicEncryptedPacket* packet; + RetransmittableFrames* retransmittable_frames; QuicPacketSequenceNumber sequence_number; QuicSequenceNumberLength sequence_number_length; - QuicEncryptedPacket* packet; QuicPacketEntropyHash entropy_hash; - RetransmittableFrames* retransmittable_frames; bool is_fec_packet; // Optional notifiers which will be informed when this packet has been ACKed. @@ -1062,6 +1088,17 @@ struct NET_EXPORT_PRIVATE TransmissionInfo { bool is_fec_packet; }; +// Convenience wrapper to wrap an iovec array and the total length, which must +// be less than or equal to the actual total length of the iovecs. +struct NET_EXPORT_PRIVATE QuicIOVector { + QuicIOVector(const struct iovec* iov, int iov_count, size_t total_length) + : iov(iov), iov_count(iov_count), total_length(total_length) {} + + const struct iovec* iov; + const int iov_count; + const size_t total_length; +}; + } // namespace net #endif // NET_QUIC_QUIC_PROTOCOL_H_ diff --git a/chromium/net/quic/quic_reliable_client_stream.cc b/chromium/net/quic/quic_reliable_client_stream.cc index 68651cb049c..b65acf95c37 100644 --- a/chromium/net/quic/quic_reliable_client_stream.cc +++ b/chromium/net/quic/quic_reliable_client_stream.cc @@ -6,17 +6,15 @@ #include "base/callback_helpers.h" #include "net/base/net_errors.h" -#include "net/quic/quic_session.h" +#include "net/quic/quic_spdy_session.h" #include "net/quic/quic_write_blocked_list.h" namespace net { QuicReliableClientStream::QuicReliableClientStream(QuicStreamId id, - QuicSession* session, + QuicSpdySession* session, const BoundNetLog& net_log) - : QuicDataStream(id, session), - net_log_(net_log), - delegate_(nullptr) { + : QuicDataStream(id, session), net_log_(net_log), delegate_(nullptr) { } QuicReliableClientStream::~QuicReliableClientStream() { @@ -24,6 +22,15 @@ QuicReliableClientStream::~QuicReliableClientStream() { delegate_->OnClose(connection_error()); } +void QuicReliableClientStream::OnStreamHeadersComplete(bool fin, + size_t frame_len) { + QuicDataStream::OnStreamHeadersComplete(fin, frame_len); + if (delegate_) { + delegate_->OnHeadersAvailable(decompressed_headers()); + MarkHeadersConsumed(decompressed_headers().length()); + } +} + uint32 QuicReliableClientStream::ProcessData(const char* data, uint32 data_len) { // TODO(rch): buffer data if we don't have a delegate. @@ -33,6 +40,11 @@ uint32 QuicReliableClientStream::ProcessData(const char* data, return 0; } + if (!FinishedReadingHeaders()) { + // Buffer the data in the sequencer until the headers have been read. + return 0; + } + int rv = delegate_->OnDataReceived(data, data_len); if (rv != OK) { DLOG(ERROR) << "Delegate refused data, rv: " << rv; diff --git a/chromium/net/quic/quic_reliable_client_stream.h b/chromium/net/quic/quic_reliable_client_stream.h index 48f5cc91ef7..fb2c51d1e58 100644 --- a/chromium/net/quic/quic_reliable_client_stream.h +++ b/chromium/net/quic/quic_reliable_client_stream.h @@ -16,7 +16,7 @@ namespace net { -class QuicClientSession; +class QuicSpdySession; // A client-initiated ReliableQuicStream. Instances of this class // are owned by the QuicClientSession which created them. @@ -27,6 +27,9 @@ class NET_EXPORT_PRIVATE QuicReliableClientStream : public QuicDataStream { public: Delegate() {} + // Called when headers are available. + virtual void OnHeadersAvailable(StringPiece headers) = 0; + // Called when data is received. // Returns network error code. OK when it successfully receives data. virtual int OnDataReceived(const char* data, int length) = 0; @@ -48,12 +51,13 @@ class NET_EXPORT_PRIVATE QuicReliableClientStream : public QuicDataStream { }; QuicReliableClientStream(QuicStreamId id, - QuicSession* session, + QuicSpdySession* session, const BoundNetLog& net_log); ~QuicReliableClientStream() override; // QuicDataStream + void OnStreamHeadersComplete(bool fin, size_t frame_len) override; uint32 ProcessData(const char* data, uint32 data_len) override; void OnClose() override; void OnCanWrite() override; diff --git a/chromium/net/quic/quic_reliable_client_stream_test.cc b/chromium/net/quic/quic_reliable_client_stream_test.cc index b36e61046fb..c53e414018b 100644 --- a/chromium/net/quic/quic_reliable_client_stream_test.cc +++ b/chromium/net/quic/quic_reliable_client_stream_test.cc @@ -21,7 +21,7 @@ namespace net { namespace test { namespace { -const QuicConnectionId kStreamId = 3; +const QuicStreamId kTestStreamId = 5u; class MockDelegate : public QuicReliableClientStream::Delegate { public: @@ -29,6 +29,7 @@ class MockDelegate : public QuicReliableClientStream::Delegate { MOCK_METHOD0(OnSendData, int()); MOCK_METHOD2(OnSendDataComplete, int(int, bool*)); + MOCK_METHOD1(OnHeadersAvailable, void(StringPiece)); MOCK_METHOD2(OnDataReceived, int(const char*, int)); MOCK_METHOD1(OnClose, void(QuicErrorCode)); MOCK_METHOD1(OnError, void(int)); @@ -44,7 +45,8 @@ class QuicReliableClientStreamTest QuicReliableClientStreamTest() : session_(new MockConnection(Perspective::IS_CLIENT, SupportedVersions(GetParam()))) { - stream_ = new QuicReliableClientStream(kStreamId, &session_, BoundNetLog()); + stream_ = + new QuicReliableClientStream(kTestStreamId, &session_, BoundNetLog()); session_.ActivateStream(stream_); stream_->SetDelegate(&delegate_); } @@ -80,7 +82,7 @@ class QuicReliableClientStreamTest } testing::StrictMock<MockDelegate> delegate_; - MockSession session_; + MockQuicSpdySession session_; QuicReliableClientStream* stream_; QuicCryptoClientConfig crypto_config_; SpdyHeaderBlock headers_; @@ -93,19 +95,32 @@ TEST_P(QuicReliableClientStreamTest, OnFinRead) { InitializeHeaders(); std::string uncompressed_headers = SpdyUtils::SerializeUncompressedHeaders(headers_, GetParam()); - EXPECT_CALL(delegate_, OnDataReceived(StrEq(uncompressed_headers.data()), - uncompressed_headers.size())); + EXPECT_CALL(delegate_, OnHeadersAvailable(StringPiece(uncompressed_headers))); QuicStreamOffset offset = 0; stream_->OnStreamHeaders(uncompressed_headers); stream_->OnStreamHeadersComplete(false, uncompressed_headers.length()); + EXPECT_TRUE(stream_->decompressed_headers().empty()); - IOVector iov; - QuicStreamFrame frame2(kStreamId, true, offset, iov); + QuicStreamFrame frame2(kTestStreamId, true, offset, StringPiece()); EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR)); stream_->OnStreamFrame(frame2); } +TEST_P(QuicReliableClientStreamTest, ProcessDataBeforeHeaders) { + const char data[] = "hello world!"; + EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR)); + + EXPECT_EQ(0u, stream_->ProcessData(data, arraysize(data))); +} + TEST_P(QuicReliableClientStreamTest, ProcessData) { + InitializeHeaders(); + std::string uncompressed_headers = + SpdyUtils::SerializeUncompressedHeaders(headers_, GetParam()); + EXPECT_CALL(delegate_, OnHeadersAvailable(StringPiece(uncompressed_headers))); + stream_->OnStreamHeaders(uncompressed_headers); + stream_->OnStreamHeadersComplete(false, uncompressed_headers.length()); + const char data[] = "hello world!"; EXPECT_CALL(delegate_, OnDataReceived(StrEq(data), arraysize(data))); EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR)); @@ -114,6 +129,9 @@ TEST_P(QuicReliableClientStreamTest, ProcessData) { } TEST_P(QuicReliableClientStreamTest, ProcessDataWithError) { + EXPECT_CALL(delegate_, OnHeadersAvailable(StringPiece(""))); + stream_->OnStreamHeadersComplete(false, 0); // Send empty headers. + const char data[] = "hello world!"; EXPECT_CALL(delegate_, OnDataReceived(StrEq(data), diff --git a/chromium/net/quic/quic_sent_packet_manager.cc b/chromium/net/quic/quic_sent_packet_manager.cc index c81c93df8e3..039a43d990e 100644 --- a/chromium/net/quic/quic_sent_packet_manager.cc +++ b/chromium/net/quic/quic_sent_packet_manager.cc @@ -69,7 +69,7 @@ QuicSentPacketManager::QuicSentPacketManager( CongestionControlType congestion_control_type, LossDetectionType loss_type, bool is_secure) - : unacked_packets_(), + : unacked_packets_(&ack_notifier_manager_), perspective_(perspective), clock_(clock), stats_(stats), @@ -146,16 +146,16 @@ void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) { } EnablePacing(); - if (HasClientSentConnectionOption(config, k1CON)) { + if (config.HasClientSentConnectionOption(k1CON, perspective_)) { send_algorithm_->SetNumEmulatedConnections(1); } - if (HasClientSentConnectionOption(config, kNCON)) { + if (config.HasClientSentConnectionOption(kNCON, perspective_)) { n_connection_simulation_ = true; } - if (HasClientSentConnectionOption(config, kNTLP)) { + if (config.HasClientSentConnectionOption(kNTLP, perspective_)) { max_tail_loss_probes_ = 0; } - if (HasClientSentConnectionOption(config, kNRTO)) { + if (config.HasClientSentConnectionOption(kNRTO, perspective_)) { use_new_rto_ = true; } if (config.HasReceivedConnectionOptions() && @@ -198,20 +198,6 @@ void QuicSentPacketManager::SetNumOpenStreams(size_t num_streams) { } } -bool QuicSentPacketManager::HasClientSentConnectionOption( - const QuicConfig& config, QuicTag tag) const { - if (perspective_ == Perspective::IS_SERVER) { - if (config.HasReceivedConnectionOptions() && - ContainsQuicTag(config.ReceivedConnectionOptions(), tag)) { - return true; - } - } else if (config.HasSendConnectionOptions() && - ContainsQuicTag(config.SendConnectionOptions(), tag)) { - return true; - } - return false; -} - void QuicSentPacketManager::OnIncomingAck(const QuicAckFrame& ack_frame, QuicTime ack_receive_time) { QuicByteCount bytes_in_flight = unacked_packets_.bytes_in_flight(); diff --git a/chromium/net/quic/quic_sent_packet_manager.h b/chromium/net/quic/quic_sent_packet_manager.h index bb65e5eb1b5..e0310c05743 100644 --- a/chromium/net/quic/quic_sent_packet_manager.h +++ b/chromium/net/quic/quic_sent_packet_manager.h @@ -337,11 +337,6 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { const SequenceNumberList& all_transmissions, QuicPacketSequenceNumber acked_sequence_number); - // Returns true if the client is sending or the server has received a - // connection option. - bool HasClientSentConnectionOption(const QuicConfig& config, - QuicTag tag) const; - // 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 diff --git a/chromium/net/quic/quic_sent_packet_manager_test.cc b/chromium/net/quic/quic_sent_packet_manager_test.cc index 2b55382fdd9..1c78779c547 100644 --- a/chromium/net/quic/quic_sent_packet_manager_test.cc +++ b/chromium/net/quic/quic_sent_packet_manager_test.cc @@ -183,8 +183,8 @@ class QuicSentPacketManagerTest : public ::testing::TestWithParam<bool> { RetransmittableFrames* frames = nullptr; if (retransmittable) { frames = new RetransmittableFrames(ENCRYPTION_NONE); - frames->AddStreamFrame( - new QuicStreamFrame(kStreamId, false, 0, IOVector())); + frames->AddFrame( + QuicFrame(new QuicStreamFrame(kStreamId, false, 0, StringPiece()))); } return SerializedPacket(sequence_number, PACKET_6BYTE_SEQUENCE_NUMBER, packets_.back(), 0u, frames); @@ -214,8 +214,8 @@ class QuicSentPacketManagerTest : public ::testing::TestWithParam<bool> { kDefaultLength, HAS_RETRANSMITTABLE_DATA)) .Times(1).WillOnce(Return(true)); SerializedPacket packet(CreateDataPacket(sequence_number)); - packet.retransmittable_frames->AddStreamFrame( - new QuicStreamFrame(1, false, 0, IOVector())); + packet.retransmittable_frames->AddFrame( + QuicFrame(new QuicStreamFrame(1, false, 0, StringPiece()))); manager_.OnPacketSent(&packet, 0, clock_.Now(), packet.packet->length(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); diff --git a/chromium/net/quic/quic_session.cc b/chromium/net/quic/quic_session.cc index 570eab7eb17..00318f75757 100644 --- a/chromium/net/quic/quic_session.cc +++ b/chromium/net/quic/quic_session.cc @@ -8,7 +8,6 @@ #include "net/quic/crypto/proof_verifier.h" #include "net/quic/quic_connection.h" #include "net/quic/quic_flow_controller.h" -#include "net/quic/quic_headers_stream.h" #include "net/ssl/ssl_info.h" using base::StringPiece; @@ -89,8 +88,8 @@ class VisitorShim : public QuicConnectionVisitorInterface { return session_->HasPendingHandshake(); } - bool HasOpenDataStreams() const override { - return session_->HasOpenDataStreams(); + bool HasOpenDynamicStreams() const override { + return session_->HasOpenDynamicStreams(); } private: @@ -102,29 +101,32 @@ QuicSession::QuicSession(QuicConnection* connection, const QuicConfig& config) visitor_shim_(new VisitorShim(this)), config_(config), max_open_streams_(config_.MaxStreamsPerConnection()), - next_stream_id_(perspective() == Perspective::IS_SERVER ? 2 : 5), - largest_peer_created_stream_id_(0), + next_stream_id_(perspective() == Perspective::IS_SERVER ? 2 : 3), + largest_peer_created_stream_id_( + perspective() == Perspective::IS_SERVER ? 1 : 0), error_(QUIC_NO_ERROR), flow_controller_(connection_.get(), 0, perspective(), kMinimumFlowControlSendWindow, config_.GetInitialSessionFlowControlWindowToSend(), - config_.GetInitialSessionFlowControlWindowToSend()), + false), goaway_received_(false), goaway_sent_(false), has_pending_handshake_(false) { } -void QuicSession::InitializeSession() { +void QuicSession::Initialize() { connection_->set_visitor(visitor_shim_.get()); connection_->SetFromConfig(config_); - headers_stream_.reset(new QuicHeadersStream(this)); + + DCHECK_EQ(kCryptoStreamId, GetCryptoStream()->id()); + static_stream_map_[kCryptoStreamId] = GetCryptoStream(); } QuicSession::~QuicSession() { STLDeleteElements(&closed_streams_); - STLDeleteValues(&stream_map_); + STLDeleteValues(&dynamic_stream_map_); DLOG_IF(WARNING, locally_closed_streams_highest_offset_.size() > max_open_streams_) @@ -143,8 +145,7 @@ void QuicSession::OnStreamFrames(const vector<QuicStreamFrame>& frames) { // 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(); + QuicStreamOffset final_byte_offset = frame.offset + frame.data.size(); UpdateFlowControlOnFinalReceivedByteOffset(stream_id, final_byte_offset); } @@ -152,58 +153,17 @@ void QuicSession::OnStreamFrames(const vector<QuicStreamFrame>& frames) { continue; } stream->OnStreamFrame(frames[i]); - if (!connection_->connected()) { - return; - } - } -} - -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); -} - -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; } - 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) { - if (frame.stream_id == kCryptoStreamId) { + if (ContainsKey(static_stream_map_, frame.stream_id)) { connection()->SendConnectionCloseWithDetails( - QUIC_INVALID_STREAM_ID, - "Attempt to reset the crypto stream"); - return; - } - if (frame.stream_id == kHeadersStreamId) { - connection()->SendConnectionCloseWithDetails( - QUIC_INVALID_STREAM_ID, - "Attempt to reset the headers stream"); + QUIC_INVALID_STREAM_ID, "Attempt to reset a static stream"); return; } - QuicDataStream* stream = GetDataStream(frame.stream_id); + ReliableQuicStream* stream = GetDynamicStream(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. @@ -226,12 +186,12 @@ void QuicSession::OnConnectionClosed(QuicErrorCode error, bool from_peer) { error_ = error; } - while (!stream_map_.empty()) { - DataStreamMap::iterator it = stream_map_.begin(); + while (!dynamic_stream_map_.empty()) { + StreamMap::iterator it = dynamic_stream_map_.begin(); QuicStreamId id = it->first; it->second->OnConnectionClosed(error, from_peer); // The stream should call CloseStream as part of OnConnectionClosed. - if (stream_map_.find(id) != stream_map_.end()) { + if (dynamic_stream_map_.find(id) != dynamic_stream_map_.end()) { LOG(DFATAL) << ENDPOINT << "Stream failed to close under OnConnectionClosed"; CloseStream(id); @@ -346,34 +306,29 @@ bool QuicSession::HasPendingHandshake() const { return has_pending_handshake_; } -bool QuicSession::HasOpenDataStreams() const { +bool QuicSession::HasOpenDynamicStreams() const { return GetNumOpenStreams() > 0; } QuicConsumedData QuicSession::WritevData( QuicStreamId id, - const IOVector& data, + const QuicIOVector& iov, QuicStreamOffset offset, bool fin, FecProtection fec_protection, QuicAckNotifier::DelegateInterface* ack_notifier_delegate) { - return connection_->SendStreamData(id, data, offset, fin, fec_protection, + return connection_->SendStreamData(id, iov, offset, fin, fec_protection, ack_notifier_delegate); } -size_t QuicSession::WriteHeaders( - QuicStreamId id, - const SpdyHeaderBlock& headers, - bool fin, - QuicPriority priority, - QuicAckNotifier::DelegateInterface* ack_notifier_delegate) { - return headers_stream_->WriteHeaders(id, headers, fin, priority, - ack_notifier_delegate); -} - void QuicSession::SendRstStream(QuicStreamId id, QuicRstStreamErrorCode error, QuicStreamOffset bytes_written) { + if (ContainsKey(static_stream_map_, id)) { + LOG(DFATAL) << "Cannot send RST for a static stream with ID " << id; + return; + } + if (connection()->connected()) { // Only send a RST_STREAM frame if still connected. connection_->SendRstStream(id, error, bytes_written); @@ -397,12 +352,15 @@ void QuicSession::CloseStreamInner(QuicStreamId stream_id, bool locally_reset) { DVLOG(1) << ENDPOINT << "Closing stream " << stream_id; - DataStreamMap::iterator it = stream_map_.find(stream_id); - if (it == stream_map_.end()) { + StreamMap::iterator it = dynamic_stream_map_.find(stream_id); + if (it == dynamic_stream_map_.end()) { + // When CloseStreamInner has been called recursively (via + // ReliableQuicStream::OnClose), the stream will already have been deleted + // from stream_map_, so return immediately. DVLOG(1) << ENDPOINT << "Stream is already closed: " << stream_id; return; } - QuicDataStream* stream = it->second; + ReliableQuicStream* stream = it->second; // Tell the stream that a RST has been sent. if (locally_reset) { @@ -419,10 +377,11 @@ void QuicSession::CloseStreamInner(QuicStreamId stream_id, stream->flow_controller()->highest_received_byte_offset(); } - stream_map_.erase(it); + dynamic_stream_map_.erase(it); + draining_streams_.erase(stream_id); stream->OnClose(); // Decrease the number of streams being emulated when a new one is opened. - connection_->SetNumOpenStreams(stream_map_.size()); + connection_->SetNumOpenStreams(dynamic_stream_map_.size()); } void QuicSession::UpdateFlowControlOnFinalReceivedByteOffset( @@ -471,6 +430,11 @@ void QuicSession::OnConfigNegotiated() { max_streams = max(max_streams + kMaxStreamsMinimumIncrement, static_cast<uint32>(max_streams * kMaxStreamsMultiplier)); + + if (config_.HasReceivedConnectionOptions() && + ContainsQuicTag(config_.ReceivedConnectionOptions(), kAFCW)) { + EnableAutoTuneReceiveWindow(); + } } set_max_open_streams(max_streams); @@ -486,6 +450,17 @@ void QuicSession::OnConfigNegotiated() { } } +void QuicSession::EnableAutoTuneReceiveWindow() { + flow_controller_.set_auto_tune_receive_window(true); + // Inform all existing streams about the new window. + for (auto const& kv : static_stream_map_) { + kv.second->flow_controller()->set_auto_tune_receive_window(true); + } + for (auto const& kv : dynamic_stream_map_) { + kv.second->flow_controller()->set_auto_tune_receive_window(true); + } +} + void QuicSession::OnNewStreamFlowControlWindow(QuicStreamOffset new_window) { if (new_window < kMinimumFlowControlSendWindow) { LOG(ERROR) << "Peer sent us an invalid stream flow control send window: " @@ -498,11 +473,11 @@ void QuicSession::OnNewStreamFlowControlWindow(QuicStreamOffset new_window) { } // Inform all existing streams about the new window. - GetCryptoStream()->UpdateSendWindowOffset(new_window); - headers_stream_->UpdateSendWindowOffset(new_window); - for (DataStreamMap::iterator it = stream_map_.begin(); - it != stream_map_.end(); ++it) { - it->second->UpdateSendWindowOffset(new_window); + for (auto const& kv : static_stream_map_) { + kv.second->UpdateSendWindowOffset(new_window); + } + for (auto const& kv : dynamic_stream_map_) { + kv.second->UpdateSendWindowOffset(new_window); } } @@ -558,13 +533,14 @@ QuicConfig* QuicSession::config() { return &config_; } -void QuicSession::ActivateStream(QuicDataStream* stream) { - DVLOG(1) << ENDPOINT << "num_streams: " << stream_map_.size() +void QuicSession::ActivateStream(ReliableQuicStream* stream) { + DVLOG(1) << ENDPOINT << "num_streams: " << dynamic_stream_map_.size() << ". activating " << stream->id(); - DCHECK_EQ(stream_map_.count(stream->id()), 0u); - stream_map_[stream->id()] = stream; + DCHECK(!ContainsKey(dynamic_stream_map_, stream->id())); + DCHECK(!ContainsKey(static_stream_map_, stream->id())); + dynamic_stream_map_[stream->id()] = stream; // Increase the number of streams being emulated when a new one is opened. - connection_->SetNumOpenStreams(stream_map_.size()); + connection_->SetNumOpenStreams(dynamic_stream_map_.size()); } QuicStreamId QuicSession::GetNextStreamId() { @@ -574,27 +550,29 @@ QuicStreamId QuicSession::GetNextStreamId() { } ReliableQuicStream* QuicSession::GetStream(const QuicStreamId stream_id) { - if (stream_id == kCryptoStreamId) { - return GetCryptoStream(); - } - if (stream_id == kHeadersStreamId) { - return headers_stream_.get(); + StreamMap::iterator it = static_stream_map_.find(stream_id); + if (it != static_stream_map_.end()) { + return it->second; } - return GetDataStream(stream_id); + return GetDynamicStream(stream_id); } -QuicDataStream* QuicSession::GetDataStream(const QuicStreamId stream_id) { - if (stream_id == kCryptoStreamId) { - DLOG(FATAL) << "Attempt to call GetDataStream with the crypto stream id"; - return nullptr; +void QuicSession::StreamDraining(QuicStreamId stream_id) { + DCHECK(ContainsKey(dynamic_stream_map_, stream_id)); + if (!ContainsKey(draining_streams_, stream_id)) { + draining_streams_.insert(stream_id); } - if (stream_id == kHeadersStreamId) { - DLOG(FATAL) << "Attempt to call GetDataStream with the headers stream id"; +} + +ReliableQuicStream* QuicSession::GetDynamicStream( + const QuicStreamId stream_id) { + if (static_stream_map_.find(stream_id) != static_stream_map_.end()) { + DLOG(FATAL) << "Attempt to call GetDynamicStream for a static stream"; return nullptr; } - DataStreamMap::iterator it = stream_map_.find(stream_id); - if (it != stream_map_.end()) { + StreamMap::iterator it = dynamic_stream_map_.find(stream_id); + if (it != dynamic_stream_map_.end()) { return it->second; } @@ -611,10 +589,11 @@ QuicDataStream* QuicSession::GetDataStream(const QuicStreamId stream_id) { return nullptr; } - return GetIncomingDataStream(stream_id); + return GetIncomingDynamicStream(stream_id); } -QuicDataStream* QuicSession::GetIncomingDataStream(QuicStreamId stream_id) { +ReliableQuicStream* QuicSession::GetIncomingDynamicStream( + QuicStreamId stream_id) { if (IsClosedStream(stream_id)) { return nullptr; } @@ -633,13 +612,6 @@ QuicDataStream* QuicSession::GetIncomingDataStream(QuicStreamId stream_id) { } return nullptr; } - if (largest_peer_created_stream_id_ == 0) { - if (perspective() == Perspective::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; id += 2) { @@ -647,7 +619,7 @@ QuicDataStream* QuicSession::GetIncomingDataStream(QuicStreamId stream_id) { } largest_peer_created_stream_id_ = stream_id; } - QuicDataStream* stream = CreateIncomingDataStream(stream_id); + ReliableQuicStream* stream = CreateIncomingDynamicStream(stream_id); if (stream == nullptr) { return nullptr; } @@ -662,13 +634,8 @@ void QuicSession::set_max_open_streams(size_t max_open_streams) { bool QuicSession::IsClosedStream(QuicStreamId id) { DCHECK_NE(0u, id); - if (id == kCryptoStreamId) { - return false; - } - if (id == kHeadersStreamId) { - return false; - } - if (ContainsKey(stream_map_, id)) { + if (ContainsKey(static_stream_map_, id) || + ContainsKey(dynamic_stream_map_, id)) { // Stream is active return false; } @@ -684,7 +651,8 @@ bool QuicSession::IsClosedStream(QuicStreamId id) { } size_t QuicSession::GetNumOpenStreams() const { - return stream_map_.size() + implicitly_created_streams_.size(); + return dynamic_stream_map_.size() + implicitly_created_streams_.size() - + draining_streams_.size(); } void QuicSession::MarkWriteBlocked(QuicStreamId id, QuicPriority priority) { @@ -732,13 +700,13 @@ bool QuicSession::IsConnectionFlowControlBlocked() const { } bool QuicSession::IsStreamFlowControlBlocked() { - if (headers_stream_->flow_controller()->IsBlocked() || - GetCryptoStream()->flow_controller()->IsBlocked()) { - return true; + for (auto const& kv : static_stream_map_) { + if (kv.second->flow_controller()->IsBlocked()) { + return true; + } } - for (DataStreamMap::iterator it = stream_map_.begin(); - it != stream_map_.end(); ++it) { - if (it->second->flow_controller()->IsBlocked()) { + for (auto const& kv : dynamic_stream_map_) { + if (kv.second->flow_controller()->IsBlocked()) { return true; } } diff --git a/chromium/net/quic/quic_session.h b/chromium/net/quic/quic_session.h index 4a401bf2b48..275a00e0628 100644 --- a/chromium/net/quic/quic_session.h +++ b/chromium/net/quic/quic_session.h @@ -18,8 +18,6 @@ #include "net/base/ip_endpoint.h" #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_write_blocked_list.h" @@ -56,10 +54,11 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { }; QuicSession(QuicConnection* connection, const QuicConfig& config); - void InitializeSession(); ~QuicSession() override; + virtual void Initialize(); + // QuicConnectionVisitorInterface methods: void OnStreamFrames(const std::vector<QuicStreamFrame>& frames) override; void OnRstStream(const QuicRstStreamFrame& frame) override; @@ -74,22 +73,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { void OnCongestionWindowChange(QuicTime now) override {} bool WillingAndAbleToWrite() const override; bool HasPendingHandshake() const override; - 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); + bool HasOpenDynamicStreams() const override; // Called by streams when they want to write data to the peer. // Returns a pair with the number of bytes consumed from data, and a boolean @@ -103,23 +87,12 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // we have seen ACKs for all packets resulting from this call. virtual QuicConsumedData WritevData( QuicStreamId id, - const IOVector& data, + const QuicIOVector& iov, 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, - QuicPriority priority, - 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, @@ -171,7 +144,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { QuicConnection* connection() { return connection_.get(); } const QuicConnection* connection() const { return connection_.get(); } - size_t num_active_requests() const { return stream_map_.size(); } + size_t num_active_requests() const { return dynamic_stream_map_.size(); } const IPEndPoint& peer_address() const { return connection_->peer_address(); } @@ -211,35 +184,35 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { size_t get_max_open_streams() const { return max_open_streams_; } - // Used in Chrome. - const QuicHeadersStream* headers_stream() { return headers_stream_.get(); } + ReliableQuicStream* GetStream(const QuicStreamId stream_id); + + // Mark a stream as draining. + void StreamDraining(QuicStreamId id); protected: - typedef base::hash_map<QuicStreamId, QuicDataStream*> DataStreamMap; + typedef base::hash_map<QuicStreamId, ReliableQuicStream*> StreamMap; // Creates a new stream, owned by the caller, to handle a peer-initiated // stream. Returns nullptr and does error handling if the stream can not be // created. - virtual QuicDataStream* CreateIncomingDataStream(QuicStreamId id) = 0; + virtual ReliableQuicStream* CreateIncomingDynamicStream(QuicStreamId id) = 0; // Create a new stream, owned by the caller, to handle a locally-initiated // stream. Returns nullptr if max streams have already been opened. - virtual QuicDataStream* CreateOutgoingDataStream() = 0; + virtual ReliableQuicStream* CreateOutgoingDynamicStream() = 0; // Return the reserved crypto stream. virtual QuicCryptoStream* GetCryptoStream() = 0; // Adds 'stream' to the active stream map. - virtual void ActivateStream(QuicDataStream* stream); + virtual void ActivateStream(ReliableQuicStream* stream); // Returns the stream id for a new stream. QuicStreamId GetNextStreamId(); - QuicDataStream* GetIncomingDataStream(QuicStreamId stream_id); - - QuicDataStream* GetDataStream(const QuicStreamId stream_id); + ReliableQuicStream* GetIncomingDynamicStream(QuicStreamId stream_id); - ReliableQuicStream* GetStream(const QuicStreamId stream_id); + ReliableQuicStream* GetDynamicStream(const QuicStreamId stream_id); // This is called after every call other than OnConnectionClose from the // QuicConnectionVisitor to allow post-processing once the work has been done. @@ -247,19 +220,22 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // operations are being done on the streams at this time) virtual void PostProcessAfterData(); - base::hash_map<QuicStreamId, QuicDataStream*>* streams() { - return &stream_map_; - } + StreamMap& static_streams() { return static_stream_map_; } + const StreamMap& static_streams() const { return static_stream_map_; } - const base::hash_map<QuicStreamId, QuicDataStream*>* streams() const { - return &stream_map_; - } + StreamMap& dynamic_streams() { return dynamic_stream_map_; } + const StreamMap& dynamic_streams() const { return dynamic_stream_map_; } - std::vector<QuicDataStream*>* closed_streams() { return &closed_streams_; } + std::vector<ReliableQuicStream*>* closed_streams() { + return &closed_streams_; + } void set_max_open_streams(size_t max_open_streams); - scoped_ptr<QuicHeadersStream> headers_stream_; + void set_largest_peer_created_stream_id( + QuicStreamId largest_peer_created_stream_id) { + largest_peer_created_stream_id_ = largest_peer_created_stream_id; + } private: friend class test::QuicSessionPeer; @@ -280,10 +256,14 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // control window in a negotiated config. Closes the connection if invalid. void OnNewStreamFlowControlWindow(QuicStreamOffset new_window); - // Called in OnConfigNegotiated when we receive a new session level flow + // Called in OnConfigNegotiated when we receive a new connection level flow // control window in a negotiated config. Closes the connection if invalid. void OnNewSessionFlowControlWindow(QuicStreamOffset new_window); + // Called in OnConfigNegotiated when auto-tuning is enabled for flow + // control receive windows. + void EnableAutoTuneReceiveWindow(); + // 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> @@ -295,21 +275,30 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // deletions. scoped_ptr<VisitorShim> visitor_shim_; - std::vector<QuicDataStream*> closed_streams_; + std::vector<ReliableQuicStream*> closed_streams_; QuicConfig config_; // Returns the maximum number of streams this connection can open. size_t max_open_streams_; + // Static streams, such as crypto and header streams. Owned by child classes + // that create these streams. + StreamMap static_stream_map_; + // Map from StreamId to pointers to streams that are owned by the caller. - DataStreamMap stream_map_; + StreamMap dynamic_stream_map_; QuicStreamId next_stream_id_; // Set of stream ids that have been "implicitly created" by receipt // of a stream id larger than the next expected stream id. base::hash_set<QuicStreamId> implicitly_created_streams_; + // Set of stream ids that are "draining" -- a FIN has been sent and received, + // but the stream object still exists because not all the received data has + // been consumed. + base::hash_set<QuicStreamId> draining_streams_; + // A list of streams which need to write more data. QuicWriteBlockedList write_blocked_streams_; @@ -318,7 +307,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // The latched error with which the connection was closed. QuicErrorCode error_; - // Used for session level flow control. + // Used for connection-level flow control. QuicFlowController flow_controller_; // Whether a GoAway has been received. diff --git a/chromium/net/quic/quic_session_test.cc b/chromium/net/quic/quic_session_test.cc index c3f49070201..672ecf800f4 100644 --- a/chromium/net/quic/quic_session_test.cc +++ b/chromium/net/quic/quic_session_test.cc @@ -22,6 +22,7 @@ #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_spdy_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" @@ -76,18 +77,16 @@ class TestCryptoStream : public QuicCryptoStream { class TestHeadersStream : public QuicHeadersStream { public: - explicit TestHeadersStream(QuicSession* session) - : QuicHeadersStream(session) { - } + explicit TestHeadersStream(QuicSpdySession* session) + : QuicHeadersStream(session) {} MOCK_METHOD0(OnCanWrite, void()); }; class TestStream : public QuicDataStream { public: - TestStream(QuicStreamId id, QuicSession* session) - : QuicDataStream(id, session) { - } + TestStream(QuicStreamId id, QuicSpdySession* session) + : QuicDataStream(id, session) {} using ReliableQuicStream::CloseWriteSide; @@ -119,24 +118,24 @@ class StreamBlocker { const QuicStreamId stream_id_; }; -class TestSession : public QuicSession { +class TestSession : public QuicSpdySession { public: explicit TestSession(QuicConnection* connection) - : QuicSession(connection, DefaultQuicConfig()), + : QuicSpdySession(connection, DefaultQuicConfig()), crypto_stream_(this), writev_consumes_all_data_(false) { - InitializeSession(); + Initialize(); } TestCryptoStream* GetCryptoStream() override { return &crypto_stream_; } - TestStream* CreateOutgoingDataStream() override { + TestStream* CreateOutgoingDynamicStream() override { TestStream* stream = new TestStream(GetNextStreamId(), this); ActivateStream(stream); return stream; } - TestStream* CreateIncomingDataStream(QuicStreamId id) override { + TestStream* CreateIncomingDynamicStream(QuicStreamId id) override { return new TestStream(id, this); } @@ -144,20 +143,20 @@ class TestSession : public QuicSession { return QuicSession::IsClosedStream(id); } - QuicDataStream* GetIncomingDataStream(QuicStreamId stream_id) { - return QuicSession::GetIncomingDataStream(stream_id); + ReliableQuicStream* GetIncomingDynamicStream(QuicStreamId stream_id) { + return QuicSpdySession::GetIncomingDynamicStream(stream_id); } QuicConsumedData WritevData( QuicStreamId id, - const IOVector& data, + const QuicIOVector& 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); + return QuicConsumedData(data.total_length, fin); } else { return QuicSession::WritevData(id, data, offset, fin, fec_protection, ack_notifier_delegate); @@ -169,8 +168,9 @@ class TestSession : public QuicSession { } QuicConsumedData SendStreamData(QuicStreamId id) { - return WritevData(id, MakeIOVector("not empty"), 0, true, MAY_FEC_PROTECT, - nullptr); + struct iovec iov; + return WritevData(id, MakeIOVector("not empty", &iov), 0, true, + MAY_FEC_PROTECT, nullptr); } using QuicSession::PostProcessAfterData; @@ -181,11 +181,11 @@ class TestSession : public QuicSession { bool writev_consumes_all_data_; }; -class QuicSessionTest : public ::testing::TestWithParam<QuicVersion> { +class QuicSessionTestBase : public ::testing::TestWithParam<QuicVersion> { protected: - QuicSessionTest() + explicit QuicSessionTestBase(Perspective perspective) : connection_( - new StrictMock<MockConnection>(Perspective::IS_SERVER, + new StrictMock<MockConnection>(perspective, SupportedVersions(GetParam()))), session_(connection_) { session_.config()->SetInitialStreamFlowControlWindowToSend( @@ -223,7 +223,7 @@ class QuicSessionTest : public ::testing::TestWithParam<QuicVersion> { } void CheckClosedStreams() { - for (int i = kCryptoStreamId; i < 100; i++) { + for (QuicStreamId i = kCryptoStreamId; i < 100; i++) { if (!ContainsKey(closed_streams_, i)) { EXPECT_FALSE(session_.IsClosedStream(i)) << " stream id: " << i; } else { @@ -246,40 +246,46 @@ class QuicSessionTest : public ::testing::TestWithParam<QuicVersion> { SpdyHeaderBlock headers_; }; -INSTANTIATE_TEST_CASE_P(Tests, QuicSessionTest, +class QuicSessionTestServer : public QuicSessionTestBase { + protected: + QuicSessionTestServer() : QuicSessionTestBase(Perspective::IS_SERVER) {} +}; + +INSTANTIATE_TEST_CASE_P(Tests, + QuicSessionTestServer, ::testing::ValuesIn(QuicSupportedVersions())); -TEST_P(QuicSessionTest, PeerAddress) { +TEST_P(QuicSessionTestServer, PeerAddress) { EXPECT_EQ(IPEndPoint(Loopback4(), kTestPort), session_.peer_address()); } -TEST_P(QuicSessionTest, IsCryptoHandshakeConfirmed) { +TEST_P(QuicSessionTestServer, IsCryptoHandshakeConfirmed) { EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed()); CryptoHandshakeMessage message; session_.GetCryptoStream()->OnHandshakeMessage(message); EXPECT_TRUE(session_.IsCryptoHandshakeConfirmed()); } -TEST_P(QuicSessionTest, IsClosedStreamDefault) { +TEST_P(QuicSessionTestServer, IsClosedStreamDefault) { // Ensure that no streams are initially closed. - for (int i = kCryptoStreamId; i < 100; i++) { + for (QuicStreamId i = kCryptoStreamId; i < 100; i++) { EXPECT_FALSE(session_.IsClosedStream(i)) << "stream id: " << i; } } -TEST_P(QuicSessionTest, ImplicitlyCreatedStreams) { - ASSERT_TRUE(session_.GetIncomingDataStream(7) != nullptr); - // Both 3 and 5 should be implicitly created. - EXPECT_FALSE(session_.IsClosedStream(3)); - EXPECT_FALSE(session_.IsClosedStream(5)); - ASSERT_TRUE(session_.GetIncomingDataStream(5) != nullptr); - ASSERT_TRUE(session_.GetIncomingDataStream(3) != nullptr); +TEST_P(QuicSessionTestServer, ImplicitlyCreatedStreams) { + ASSERT_TRUE(session_.GetIncomingDynamicStream(9) != nullptr); + // Both 5 and 7 should be implicitly created. + EXPECT_TRUE(QuicSessionPeer::IsStreamImplicitlyCreated(&session_, 5)); + EXPECT_TRUE(QuicSessionPeer::IsStreamImplicitlyCreated(&session_, 7)); + ASSERT_TRUE(session_.GetIncomingDynamicStream(7) != nullptr); + ASSERT_TRUE(session_.GetIncomingDynamicStream(5) != nullptr); } -TEST_P(QuicSessionTest, IsClosedStreamLocallyCreated) { - TestStream* stream2 = session_.CreateOutgoingDataStream(); +TEST_P(QuicSessionTestServer, IsClosedStreamLocallyCreated) { + TestStream* stream2 = session_.CreateOutgoingDynamicStream(); EXPECT_EQ(2u, stream2->id()); - TestStream* stream4 = session_.CreateOutgoingDataStream(); + TestStream* stream4 = session_.CreateOutgoingDynamicStream(); EXPECT_EQ(4u, stream4->id()); CheckClosedStreams(); @@ -289,36 +295,34 @@ TEST_P(QuicSessionTest, IsClosedStreamLocallyCreated) { CheckClosedStreams(); } -TEST_P(QuicSessionTest, IsClosedStreamPeerCreated) { +TEST_P(QuicSessionTestServer, 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); + session_.GetIncomingDynamicStream(stream_id1); + session_.GetIncomingDynamicStream(stream_id2); CheckClosedStreams(); CloseStream(stream_id1); CheckClosedStreams(); CloseStream(stream_id2); // Create a stream explicitly, and another implicitly. - QuicDataStream* stream3 = session_.GetIncomingDataStream(stream_id2 + 4); - QuicDataStreamPeer::SetHeadersDecompressed(stream3, true); + ReliableQuicStream* stream3 = + session_.GetIncomingDynamicStream(stream_id2 + 4); CheckClosedStreams(); // Close one, but make sure the other is still not closed CloseStream(stream3->id()); CheckClosedStreams(); } -TEST_P(QuicSessionTest, StreamIdTooLarge) { +TEST_P(QuicSessionTestServer, StreamIdTooLarge) { QuicStreamId stream_id = kClientDataStreamId1; - session_.GetIncomingDataStream(stream_id); + session_.GetIncomingDynamicStream(stream_id); EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_STREAM_ID)); - session_.GetIncomingDataStream(stream_id + kMaxStreamIdDelta + 2); + session_.GetIncomingDynamicStream(stream_id + kMaxStreamIdDelta + 2); } -TEST_P(QuicSessionTest, DebugDFatalIfMarkingClosedStreamWriteBlocked) { - TestStream* stream2 = session_.CreateOutgoingDataStream(); +TEST_P(QuicSessionTestServer, DebugDFatalIfMarkingClosedStreamWriteBlocked) { + TestStream* stream2 = session_.CreateOutgoingDynamicStream(); QuicStreamId kClosedStreamId = stream2->id(); // Close the stream. EXPECT_CALL(*connection_, SendRstStream(kClosedStreamId, _, _)); @@ -328,20 +332,21 @@ TEST_P(QuicSessionTest, DebugDFatalIfMarkingClosedStreamWriteBlocked) { "Marking unknown stream 2 blocked."); } -TEST_P(QuicSessionTest, DebugDFatalIfMarkWriteBlockedCalledWithWrongPriority) { +TEST_P(QuicSessionTestServer, + DebugDFatalIfMarkWriteBlockedCalledWithWrongPriority) { const QuicPriority kDifferentPriority = 0; - TestStream* stream2 = session_.CreateOutgoingDataStream(); + TestStream* stream2 = session_.CreateOutgoingDynamicStream(); EXPECT_NE(kDifferentPriority, stream2->EffectivePriority()); 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(); +TEST_P(QuicSessionTestServer, OnCanWrite) { + TestStream* stream2 = session_.CreateOutgoingDynamicStream(); + TestStream* stream4 = session_.CreateOutgoingDynamicStream(); + TestStream* stream6 = session_.CreateOutgoingDynamicStream(); session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority); session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority); @@ -358,14 +363,14 @@ TEST_P(QuicSessionTest, OnCanWrite) { EXPECT_TRUE(session_.WillingAndAbleToWrite()); } -TEST_P(QuicSessionTest, OnCanWriteBundlesStreams) { +TEST_P(QuicSessionTestServer, OnCanWriteBundlesStreams) { // 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(); + TestStream* stream2 = session_.CreateOutgoingDynamicStream(); + TestStream* stream4 = session_.CreateOutgoingDynamicStream(); + TestStream* stream6 = session_.CreateOutgoingDynamicStream(); session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority); session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority); @@ -376,13 +381,13 @@ TEST_P(QuicSessionTest, OnCanWriteBundlesStreams) { EXPECT_CALL(*send_algorithm, GetCongestionWindow()) .WillRepeatedly(Return(kMaxPacketSize * 10)); EXPECT_CALL(*stream2, OnCanWrite()) - .WillOnce(IgnoreResult(Invoke(CreateFunctor( + .WillOnce(testing::IgnoreResult(Invoke(CreateFunctor( &session_, &TestSession::SendStreamData, stream2->id())))); EXPECT_CALL(*stream4, OnCanWrite()) - .WillOnce(IgnoreResult(Invoke(CreateFunctor( + .WillOnce(testing::IgnoreResult(Invoke(CreateFunctor( &session_, &TestSession::SendStreamData, stream4->id())))); EXPECT_CALL(*stream6, OnCanWrite()) - .WillOnce(IgnoreResult(Invoke(CreateFunctor( + .WillOnce(testing::IgnoreResult(Invoke(CreateFunctor( &session_, &TestSession::SendStreamData, stream6->id())))); // Expect that we only send one packet, the writes from different streams @@ -397,16 +402,16 @@ TEST_P(QuicSessionTest, OnCanWriteBundlesStreams) { EXPECT_FALSE(session_.WillingAndAbleToWrite()); } -TEST_P(QuicSessionTest, OnCanWriteCongestionControlBlocks) { +TEST_P(QuicSessionTestServer, 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(); + TestStream* stream2 = session_.CreateOutgoingDynamicStream(); + TestStream* stream4 = session_.CreateOutgoingDynamicStream(); + TestStream* stream6 = session_.CreateOutgoingDynamicStream(); session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority); session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority); @@ -441,16 +446,16 @@ TEST_P(QuicSessionTest, OnCanWriteCongestionControlBlocks) { EXPECT_FALSE(session_.WillingAndAbleToWrite()); } -TEST_P(QuicSessionTest, BufferedHandshake) { +TEST_P(QuicSessionTestServer, BufferedHandshake) { EXPECT_FALSE(session_.HasPendingHandshake()); // Default value. // Test that blocking other streams does not change our status. - TestStream* stream2 = session_.CreateOutgoingDataStream(); + TestStream* stream2 = session_.CreateOutgoingDynamicStream(); StreamBlocker stream2_blocker(&session_, stream2->id()); stream2_blocker.MarkWriteBlocked(); EXPECT_FALSE(session_.HasPendingHandshake()); - TestStream* stream3 = session_.CreateOutgoingDataStream(); + TestStream* stream3 = session_.CreateOutgoingDynamicStream(); StreamBlocker stream3_blocker(&session_, stream3->id()); stream3_blocker.MarkWriteBlocked(); EXPECT_FALSE(session_.HasPendingHandshake()); @@ -459,7 +464,7 @@ TEST_P(QuicSessionTest, BufferedHandshake) { session_.MarkWriteBlocked(kCryptoStreamId, kHighestPriority); EXPECT_TRUE(session_.HasPendingHandshake()); - TestStream* stream4 = session_.CreateOutgoingDataStream(); + TestStream* stream4 = session_.CreateOutgoingDynamicStream(); StreamBlocker stream4_blocker(&session_, stream4->id()); stream4_blocker.MarkWriteBlocked(); EXPECT_TRUE(session_.HasPendingHandshake()); @@ -487,10 +492,10 @@ TEST_P(QuicSessionTest, BufferedHandshake) { EXPECT_FALSE(session_.HasPendingHandshake()); // Crypto stream wrote. } -TEST_P(QuicSessionTest, OnCanWriteWithClosedStream) { - TestStream* stream2 = session_.CreateOutgoingDataStream(); - TestStream* stream4 = session_.CreateOutgoingDataStream(); - TestStream* stream6 = session_.CreateOutgoingDataStream(); +TEST_P(QuicSessionTestServer, OnCanWriteWithClosedStream) { + TestStream* stream2 = session_.CreateOutgoingDynamicStream(); + TestStream* stream4 = session_.CreateOutgoingDynamicStream(); + TestStream* stream6 = session_.CreateOutgoingDynamicStream(); session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority); session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority); @@ -504,7 +509,7 @@ TEST_P(QuicSessionTest, OnCanWriteWithClosedStream) { EXPECT_FALSE(session_.WillingAndAbleToWrite()); } -TEST_P(QuicSessionTest, OnCanWriteLimitsNumWritesIfFlowControlBlocked) { +TEST_P(QuicSessionTestServer, OnCanWriteLimitsNumWritesIfFlowControlBlocked) { // Ensure connection level flow control blockage. QuicFlowControllerPeer::SetSendWindowOffset(session_.flow_controller(), 0); EXPECT_TRUE(session_.flow_controller()->IsBlocked()); @@ -518,7 +523,7 @@ TEST_P(QuicSessionTest, OnCanWriteLimitsNumWritesIfFlowControlBlocked) { // 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(); + TestStream* stream = session_.CreateOutgoingDynamicStream(); session_.MarkWriteBlocked(stream->id(), kSomeMiddlePriority); EXPECT_CALL(*stream, OnCanWrite()).Times(0); @@ -527,33 +532,35 @@ TEST_P(QuicSessionTest, OnCanWriteLimitsNumWritesIfFlowControlBlocked) { TestCryptoStream* crypto_stream = session_.GetCryptoStream(); EXPECT_CALL(*crypto_stream, OnCanWrite()).Times(1); TestHeadersStream* headers_stream = new TestHeadersStream(&session_); - QuicSessionPeer::SetHeadersStream(&session_, headers_stream); + QuicSpdySessionPeer::SetHeadersStream(&session_, headers_stream); EXPECT_CALL(*headers_stream, OnCanWrite()).Times(1); session_.OnCanWrite(); EXPECT_FALSE(session_.WillingAndAbleToWrite()); } -TEST_P(QuicSessionTest, SendGoAway) { - EXPECT_CALL(*connection_, - SendGoAway(QUIC_PEER_GOING_AWAY, 0u, "Going Away.")); +TEST_P(QuicSessionTestServer, SendGoAway) { + EXPECT_CALL(*connection_, SendGoAway(QUIC_PEER_GOING_AWAY, kHeadersStreamId, + "Going Away.")); session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away."); EXPECT_TRUE(session_.goaway_sent()); + const QuicStreamId kTestStreamId = 5u; EXPECT_CALL(*connection_, - SendRstStream(3u, QUIC_STREAM_PEER_GOING_AWAY, 0)).Times(0); - EXPECT_TRUE(session_.GetIncomingDataStream(3u)); + SendRstStream(kTestStreamId, QUIC_STREAM_PEER_GOING_AWAY, 0)) + .Times(0); + EXPECT_TRUE(session_.GetIncomingDynamicStream(kTestStreamId)); } -TEST_P(QuicSessionTest, DoNotSendGoAwayTwice) { - EXPECT_CALL(*connection_, - SendGoAway(QUIC_PEER_GOING_AWAY, 0u, "Going Away.")).Times(1); +TEST_P(QuicSessionTestServer, DoNotSendGoAwayTwice) { + EXPECT_CALL(*connection_, SendGoAway(QUIC_PEER_GOING_AWAY, kHeadersStreamId, + "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_P(QuicSessionTest, IncreasedTimeoutAfterCryptoHandshake) { +TEST_P(QuicSessionTestServer, IncreasedTimeoutAfterCryptoHandshake) { EXPECT_EQ(kInitialIdleTimeoutSecs + 3, QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); CryptoHandshakeMessage msg; @@ -562,9 +569,9 @@ TEST_P(QuicSessionTest, IncreasedTimeoutAfterCryptoHandshake) { QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); } -TEST_P(QuicSessionTest, RstStreamBeforeHeadersDecompressed) { +TEST_P(QuicSessionTestServer, RstStreamBeforeHeadersDecompressed) { // Send two bytes of payload. - QuicStreamFrame data1(kClientDataStreamId1, false, 0, MakeIOVector("HT")); + QuicStreamFrame data1(kClientDataStreamId1, false, 0, StringPiece("HT")); vector<QuicStreamFrame> frames; frames.push_back(data1); session_.OnStreamFrames(frames); @@ -578,13 +585,13 @@ TEST_P(QuicSessionTest, RstStreamBeforeHeadersDecompressed) { EXPECT_TRUE(connection_->connected()); } -TEST_P(QuicSessionTest, MultipleRstStreamsCauseSingleConnectionClose) { +TEST_P(QuicSessionTestServer, 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")); + QuicStreamFrame data1(kClientDataStreamId1, false, 0, StringPiece("HT")); vector<QuicStreamFrame> frames; frames.push_back(data1); session_.OnStreamFrames(frames); @@ -594,7 +601,7 @@ TEST_P(QuicSessionTest, MultipleRstStreamsCauseSingleConnectionClose) { // closed. EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_STREAM_ID)) .Times(1); - QuicStreamId kLargeInvalidStreamId = 99999999; + const QuicStreamId kLargeInvalidStreamId = 99999999; QuicRstStreamFrame rst1(kLargeInvalidStreamId, QUIC_STREAM_NO_ERROR, 0); session_.OnRstStream(rst1); QuicConnectionPeer::CloseConnection(connection_); @@ -605,7 +612,7 @@ TEST_P(QuicSessionTest, MultipleRstStreamsCauseSingleConnectionClose) { session_.OnRstStream(rst2); } -TEST_P(QuicSessionTest, HandshakeUnblocksFlowControlBlockedStream) { +TEST_P(QuicSessionTestServer, 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. @@ -614,7 +621,7 @@ TEST_P(QuicSessionTest, HandshakeUnblocksFlowControlBlockedStream) { session_.set_writev_consumes_all_data(true); // Create a stream, and send enough data to make it flow control blocked. - TestStream* stream2 = session_.CreateOutgoingDataStream(); + TestStream* stream2 = session_.CreateOutgoingDynamicStream(); string body(kMinimumFlowControlSendWindow, '.'); EXPECT_FALSE(stream2->flow_controller()->IsBlocked()); EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); @@ -640,7 +647,7 @@ TEST_P(QuicSessionTest, HandshakeUnblocksFlowControlBlockedStream) { EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); } -TEST_P(QuicSessionTest, HandshakeUnblocksFlowControlBlockedCryptoStream) { +TEST_P(QuicSessionTestServer, HandshakeUnblocksFlowControlBlockedCryptoStream) { // Test that if the crypto stream is flow control blocked, then if the SHLO // contains a larger send window offset, the stream becomes unblocked. session_.set_writev_consumes_all_data(true); @@ -649,21 +656,20 @@ TEST_P(QuicSessionTest, HandshakeUnblocksFlowControlBlockedCryptoStream) { EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); QuicHeadersStream* headers_stream = - QuicSessionPeer::GetHeadersStream(&session_); + QuicSpdySessionPeer::GetHeadersStream(&session_); EXPECT_FALSE(headers_stream->flow_controller()->IsBlocked()); EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); // Write until the crypto stream is flow control blocked. EXPECT_CALL(*connection_, SendBlocked(kCryptoStreamId)); - int i = 0; - while (!crypto_stream->flow_controller()->IsBlocked() && i < 1000) { + for (QuicStreamId i = 0; + !crypto_stream->flow_controller()->IsBlocked() && i < 1000u; i++) { EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); QuicConfig config; CryptoHandshakeMessage crypto_message; config.ToHandshakeMessage(&crypto_message); crypto_stream->SendHandshakeMessage(crypto_message); - ++i; } EXPECT_TRUE(crypto_stream->flow_controller()->IsBlocked()); EXPECT_FALSE(headers_stream->flow_controller()->IsBlocked()); @@ -686,7 +692,8 @@ TEST_P(QuicSessionTest, HandshakeUnblocksFlowControlBlockedCryptoStream) { EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); } -TEST_P(QuicSessionTest, HandshakeUnblocksFlowControlBlockedHeadersStream) { +TEST_P(QuicSessionTestServer, + HandshakeUnblocksFlowControlBlockedHeadersStream) { // Test that if the header stream is flow control blocked, then if the SHLO // contains a larger send window offset, the stream becomes unblocked. session_.set_writev_consumes_all_data(true); @@ -695,7 +702,7 @@ TEST_P(QuicSessionTest, HandshakeUnblocksFlowControlBlockedHeadersStream) { EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); QuicHeadersStream* headers_stream = - QuicSessionPeer::GetHeadersStream(&session_); + QuicSpdySessionPeer::GetHeadersStream(&session_); EXPECT_FALSE(headers_stream->flow_controller()->IsBlocked()); EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); @@ -735,12 +742,12 @@ TEST_P(QuicSessionTest, HandshakeUnblocksFlowControlBlockedHeadersStream) { EXPECT_FALSE(headers_stream->HasBufferedData()); } -TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstOutOfOrder) { +TEST_P(QuicSessionTestServer, ConnectionFlowControlAccountingRstOutOfOrder) { // 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(); + TestStream* stream = session_.CreateOutgoingDynamicStream(); const QuicStreamOffset kByteOffset = 1 + kInitialSessionFlowControlWindowForTest / 2; @@ -760,17 +767,17 @@ TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstOutOfOrder) { EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed()); } -TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAndLocalReset) { +TEST_P(QuicSessionTestServer, ConnectionFlowControlAccountingFinAndLocalReset) { // 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(); + TestStream* stream = session_.CreateOutgoingDynamicStream(); const QuicStreamOffset kByteOffset = kInitialSessionFlowControlWindowForTest / 2; - QuicStreamFrame frame(stream->id(), true, kByteOffset, IOVector()); + QuicStreamFrame frame(stream->id(), true, kByteOffset, StringPiece()); vector<QuicStreamFrame> frames; frames.push_back(frame); session_.OnStreamFrames(frames); @@ -787,7 +794,7 @@ TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAndLocalReset) { EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed()); } -TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAfterRst) { +TEST_P(QuicSessionTestServer, 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. @@ -803,7 +810,7 @@ TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAfterRst) { session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed); // Reset our stream: this results in the stream being closed locally. - TestStream* stream = session_.CreateOutgoingDataStream(); + TestStream* stream = session_.CreateOutgoingDynamicStream(); EXPECT_CALL(*connection_, SendRstStream(stream->id(), _, _)); stream->Reset(QUIC_STREAM_CANCELLED); @@ -812,8 +819,7 @@ TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAfterRst) { // 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); + QuicStreamFrame frame(stream->id(), true, kByteOffset, StringPiece(body)); vector<QuicStreamFrame> frames; frames.push_back(frame); session_.OnStreamFrames(frames); @@ -827,7 +833,7 @@ TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAfterRst) { session_.flow_controller()->highest_received_byte_offset()); } -TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstAfterRst) { +TEST_P(QuicSessionTestServer, 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. @@ -843,7 +849,7 @@ TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstAfterRst) { session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed); // Reset our stream: this results in the stream being closed locally. - TestStream* stream = session_.CreateOutgoingDataStream(); + TestStream* stream = session_.CreateOutgoingDynamicStream(); EXPECT_CALL(*connection_, SendRstStream(stream->id(), _, _)); stream->Reset(QUIC_STREAM_CANCELLED); @@ -861,10 +867,10 @@ TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstAfterRst) { session_.flow_controller()->highest_received_byte_offset()); } -TEST_P(QuicSessionTest, InvalidStreamFlowControlWindowInHandshake) { +TEST_P(QuicSessionTestServer, InvalidStreamFlowControlWindowInHandshake) { // Test that receipt of an invalid (< default) stream flow control window from // the peer results in the connection being torn down. - uint32 kInvalidWindow = kMinimumFlowControlSendWindow - 1; + const uint32 kInvalidWindow = kMinimumFlowControlSendWindow - 1; QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(session_.config(), kInvalidWindow); @@ -873,10 +879,10 @@ TEST_P(QuicSessionTest, InvalidStreamFlowControlWindowInHandshake) { session_.OnConfigNegotiated(); } -TEST_P(QuicSessionTest, InvalidSessionFlowControlWindowInHandshake) { +TEST_P(QuicSessionTestServer, InvalidSessionFlowControlWindowInHandshake) { // Test that receipt of an invalid (< default) session flow control window // from the peer results in the connection being torn down. - uint32 kInvalidWindow = kMinimumFlowControlSendWindow - 1; + const uint32 kInvalidWindow = kMinimumFlowControlSendWindow - 1; QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(session_.config(), kInvalidWindow); @@ -885,7 +891,7 @@ TEST_P(QuicSessionTest, InvalidSessionFlowControlWindowInHandshake) { session_.OnConfigNegotiated(); } -TEST_P(QuicSessionTest, FlowControlWithInvalidFinalOffset) { +TEST_P(QuicSessionTestServer, FlowControlWithInvalidFinalOffset) { // Test that if we receive a stream RST with a highest byte offset that // violates flow control, that we close the connection. const uint64 kLargeOffset = kInitialSessionFlowControlWindowForTest + 1; @@ -894,10 +900,10 @@ TEST_P(QuicSessionTest, FlowControlWithInvalidFinalOffset) { .Times(2); // Check that stream frame + FIN results in connection close. - TestStream* stream = session_.CreateOutgoingDataStream(); + TestStream* stream = session_.CreateOutgoingDynamicStream(); EXPECT_CALL(*connection_, SendRstStream(stream->id(), _, _)); stream->Reset(QUIC_STREAM_CANCELLED); - QuicStreamFrame frame(stream->id(), true, kLargeOffset, IOVector()); + QuicStreamFrame frame(stream->id(), true, kLargeOffset, StringPiece()); vector<QuicStreamFrame> frames; frames.push_back(frame); session_.OnStreamFrames(frames); @@ -908,13 +914,13 @@ TEST_P(QuicSessionTest, FlowControlWithInvalidFinalOffset) { session_.OnRstStream(rst_frame); } -TEST_P(QuicSessionTest, WindowUpdateUnblocksHeadersStream) { +TEST_P(QuicSessionTestServer, WindowUpdateUnblocksHeadersStream) { // Test that a flow control blocked headers stream gets unblocked on recipt of - // a WINDOW_UPDATE frame. Regression test for b/17413860. + // a WINDOW_UPDATE frame. // Set the headers stream to be flow control blocked. QuicHeadersStream* headers_stream = - QuicSessionPeer::GetHeadersStream(&session_); + QuicSpdySessionPeer::GetHeadersStream(&session_); QuicFlowControllerPeer::SetSendWindowOffset(headers_stream->flow_controller(), 0); EXPECT_TRUE(headers_stream->flow_controller()->IsBlocked()); @@ -932,21 +938,22 @@ TEST_P(QuicSessionTest, WindowUpdateUnblocksHeadersStream) { EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); } -TEST_P(QuicSessionTest, TooManyUnfinishedStreamsCauseConnectionClose) { +TEST_P(QuicSessionTestServer, TooManyUnfinishedStreamsCauseConnectionClose) { // If a buggy/malicious peer creates too many streams that are not ended with // a FIN or RST then we send a connection close. EXPECT_CALL(*connection_, SendConnectionClose(QUIC_TOO_MANY_UNFINISHED_STREAMS)).Times(1); - const int kMaxStreams = 5; + const QuicStreamId kMaxStreams = 5; QuicSessionPeer::SetMaxOpenStreams(&session_, kMaxStreams); // Create kMaxStreams + 1 data streams, and close them all without receiving a - // FIN or a RST from the client. - const int kFirstStreamId = kClientDataStreamId1; - const int kFinalStreamId = kClientDataStreamId1 + 2 * kMaxStreams + 1; - for (int i = kFirstStreamId; i < kFinalStreamId; i += 2) { - QuicStreamFrame data1(i, false, 0, MakeIOVector("HT")); + // FIN or a RST_STREAM from the client. + const QuicStreamId kFirstStreamId = kClientDataStreamId1; + const QuicStreamId kFinalStreamId = + kClientDataStreamId1 + 2 * kMaxStreams + 1; + for (QuicStreamId i = kFirstStreamId; i < kFinalStreamId; i += 2) { + QuicStreamFrame data1(i, false, 0, StringPiece("HT")); vector<QuicStreamFrame> frames; frames.push_back(data1); session_.OnStreamFrames(frames); @@ -960,6 +967,55 @@ TEST_P(QuicSessionTest, TooManyUnfinishedStreamsCauseConnectionClose) { session_.PostProcessAfterData(); } +TEST_P(QuicSessionTestServer, DrainingStreamsDoNotCountAsOpened) { + // Verify that a draining stream (which has received a FIN but not consumed + // it) does not count against the open quota (because it is closed from the + // protocol point of view). + EXPECT_CALL(*connection_, + SendConnectionClose(QUIC_TOO_MANY_UNFINISHED_STREAMS)).Times(0); + + const QuicStreamId kMaxStreams = 5; + QuicSessionPeer::SetMaxOpenStreams(&session_, kMaxStreams); + + // Create kMaxStreams + 1 data streams, and mark them draining. + const QuicStreamId kFirstStreamId = kClientDataStreamId1; + const QuicStreamId kFinalStreamId = + kClientDataStreamId1 + 2 * kMaxStreams + 1; + for (QuicStreamId i = kFirstStreamId; i < kFinalStreamId; i += 2) { + QuicStreamFrame data1(i, true, 0, StringPiece("HT")); + vector<QuicStreamFrame> frames; + frames.push_back(data1); + session_.OnStreamFrames(frames); + EXPECT_EQ(1u, session_.GetNumOpenStreams()); + session_.StreamDraining(i); + EXPECT_EQ(0u, session_.GetNumOpenStreams()); + } + + // Called after any new data is received by the session, and triggers the call + // to close the connection. + session_.PostProcessAfterData(); +} + +class QuicSessionTestClient : public QuicSessionTestBase { + protected: + QuicSessionTestClient() : QuicSessionTestBase(Perspective::IS_CLIENT) {} +}; + +INSTANTIATE_TEST_CASE_P(Tests, + QuicSessionTestClient, + ::testing::ValuesIn(QuicSupportedVersions())); + +TEST_P(QuicSessionTestClient, ImplicitlyCreatedStreamsClient) { + ASSERT_TRUE(session_.GetIncomingDynamicStream(6) != nullptr); + // Both 2 and 4 should be implicitly created. + EXPECT_TRUE(QuicSessionPeer::IsStreamImplicitlyCreated(&session_, 2)); + EXPECT_TRUE(QuicSessionPeer::IsStreamImplicitlyCreated(&session_, 4)); + ASSERT_TRUE(session_.GetIncomingDynamicStream(2) != nullptr); + ASSERT_TRUE(session_.GetIncomingDynamicStream(4) != nullptr); + // And 5 should be not implicitly created. + EXPECT_FALSE(QuicSessionPeer::IsStreamImplicitlyCreated(&session_, 5)); +} + } // namespace } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_socket_address_coder.cc b/chromium/net/quic/quic_socket_address_coder.cc index 77e595f2d65..5dba82107f5 100644 --- a/chromium/net/quic/quic_socket_address_coder.cc +++ b/chromium/net/quic/quic_socket_address_coder.cc @@ -4,6 +4,8 @@ #include "net/quic/quic_socket_address_coder.h" +#include "net/base/sys_addrinfo.h" + using std::string; namespace net { diff --git a/chromium/net/quic/quic_socket_address_coder_test.cc b/chromium/net/quic/quic_socket_address_coder_test.cc index cbfac2b12d8..6a94550d8e0 100644 --- a/chromium/net/quic/quic_socket_address_coder_test.cc +++ b/chromium/net/quic/quic_socket_address_coder_test.cc @@ -4,6 +4,8 @@ #include "net/quic/quic_socket_address_coder.h" +#include "net/base/net_util.h" +#include "net/base/sys_addrinfo.h" #include "testing/gtest/include/gtest/gtest.h" using std::string; diff --git a/chromium/net/quic/quic_spdy_session.cc b/chromium/net/quic/quic_spdy_session.cc new file mode 100644 index 00000000000..0a8dda6929e --- /dev/null +++ b/chromium/net/quic/quic_spdy_session.cc @@ -0,0 +1,80 @@ +// Copyright (c) 2015 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_session.h" + +#include "net/quic/quic_headers_stream.h" + +namespace net { + +QuicSpdySession::QuicSpdySession(QuicConnection* connection, + const QuicConfig& config) + : QuicSession(connection, config) { +} + +QuicSpdySession::~QuicSpdySession() { +} + +void QuicSpdySession::Initialize() { + QuicSession::Initialize(); + + if (perspective() == Perspective::IS_SERVER) { + set_largest_peer_created_stream_id(kHeadersStreamId); + } else { + QuicStreamId headers_stream_id = GetNextStreamId(); + DCHECK_EQ(headers_stream_id, kHeadersStreamId); + } + + headers_stream_.reset(new QuicHeadersStream(this)); + DCHECK_EQ(kHeadersStreamId, headers_stream_->id()); + static_streams()[kHeadersStreamId] = headers_stream_.get(); +} + +void QuicSpdySession::OnStreamHeaders(QuicStreamId stream_id, + StringPiece headers_data) { + QuicDataStream* stream = GetSpdyDataStream(stream_id); + if (!stream) { + // It's quite possible to receive headers after a stream has been reset. + return; + } + stream->OnStreamHeaders(headers_data); +} + +void QuicSpdySession::OnStreamHeadersPriority(QuicStreamId stream_id, + QuicPriority priority) { + QuicDataStream* stream = GetSpdyDataStream(stream_id); + if (!stream) { + // It's quite possible to receive headers after a stream has been reset. + return; + } + stream->OnStreamHeadersPriority(priority); +} + +void QuicSpdySession::OnStreamHeadersComplete(QuicStreamId stream_id, + bool fin, + size_t frame_len) { + QuicDataStream* stream = GetSpdyDataStream(stream_id); + if (!stream) { + // It's quite possible to receive headers after a stream has been reset. + return; + } + stream->OnStreamHeadersComplete(fin, frame_len); +} + +size_t QuicSpdySession::WriteHeaders( + QuicStreamId id, + const SpdyHeaderBlock& headers, + bool fin, + QuicPriority priority, + QuicAckNotifier::DelegateInterface* ack_notifier_delegate) { + return headers_stream_->WriteHeaders(id, headers, fin, priority, + ack_notifier_delegate); +} + +QuicDataStream* QuicSpdySession::GetSpdyDataStream( + const QuicStreamId stream_id) { + return static_cast<QuicDataStream*>(GetDynamicStream(stream_id)); +} + +} // namespace net diff --git a/chromium/net/quic/quic_spdy_session.h b/chromium/net/quic/quic_spdy_session.h new file mode 100644 index 00000000000..b59eb63aa0e --- /dev/null +++ b/chromium/net/quic/quic_spdy_session.h @@ -0,0 +1,76 @@ +// Copyright (c) 2015 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_SESSION_H_ +#define NET_QUIC_QUIC_SPDY_SESSION_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "net/quic/quic_data_stream.h" +#include "net/quic/quic_headers_stream.h" +#include "net/quic/quic_session.h" + +namespace net { + +namespace test { +class QuicSpdySessionPeer; +} // namespace test + +// A QUIC session with a headers stream. +class NET_EXPORT_PRIVATE QuicSpdySession : public QuicSession { + public: + QuicSpdySession(QuicConnection* connection, const QuicConfig& config); + + ~QuicSpdySession() override; + + void Initialize() override; + + // Called by |headers_stream_| when headers have been received for a stream. + virtual void OnStreamHeaders(QuicStreamId stream_id, + StringPiece headers_data); + // Called by |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 |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); + + // 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, + QuicPriority priority, + QuicAckNotifier::DelegateInterface* ack_notifier_delegate); + + QuicHeadersStream* headers_stream() { return headers_stream_.get(); } + + protected: + // Override CreateIncomingDynamicStream() and CreateOutgoingDynamicStream() + // with QuicDataStream return type to make sure that all data streams are + // QuicDataStreams. + QuicDataStream* CreateIncomingDynamicStream(QuicStreamId id) override = 0; + QuicDataStream* CreateOutgoingDynamicStream() override = 0; + + QuicDataStream* GetSpdyDataStream(const QuicStreamId stream_id); + + private: + friend class test::QuicSpdySessionPeer; + + scoped_ptr<QuicHeadersStream> headers_stream_; + + DISALLOW_COPY_AND_ASSIGN(QuicSpdySession); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_SPDY_SESSION_H_ diff --git a/chromium/net/quic/quic_stream_factory.cc b/chromium/net/quic/quic_stream_factory.cc index 76ab6563c68..6ad16be7d2b 100644 --- a/chromium/net/quic/quic_stream_factory.cc +++ b/chromium/net/quic/quic_stream_factory.cc @@ -4,17 +4,19 @@ #include "net/quic/quic_stream_factory.h" +#include <algorithm> #include <set> -#include "base/cpu.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/location.h" #include "base/metrics/field_trial.h" -#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros.h" #include "base/metrics/sparse_histogram.h" #include "base/rand_util.h" +#include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/thread_task_runner_handle.h" #include "base/values.h" #include "net/base/net_errors.h" #include "net/cert/cert_verifier.h" @@ -43,6 +45,13 @@ #include "base/win/windows_version.h" #endif +#if defined(USE_OPENSSL) +#include <openssl/aead.h> +#include "crypto/openssl_util.h" +#else +#include "base/cpu.h" +#endif + namespace net { namespace { @@ -57,8 +66,9 @@ enum CreateSessionFailure { // 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 maximum receive window sizes for QUIC sessions and streams. +const int32 kQuicSessionMaxRecvWindowSize = 15 * 1024 * 1024; // 15 MB +const int32 kQuicStreamMaxRecvWindowSize = 6 * 1024 * 1024; // 6 MB // Set the maximum number of undecryptable packets the connection will store. const int32 kMaxUndecryptablePackets = 100; @@ -138,8 +148,9 @@ class QuicStreamFactory::Job { Job(QuicStreamFactory* factory, HostResolver* host_resolver, const HostPortPair& host_port_pair, + bool server_and_origin_have_same_host, bool is_https, - bool was_alternate_protocol_recently_broken, + bool was_alternative_service_recently_broken, PrivacyMode privacy_mode, int cert_verify_flags, bool is_post, @@ -195,8 +206,10 @@ class QuicStreamFactory::Job { SingleRequestHostResolver host_resolver_; QuicServerId server_id_; int cert_verify_flags_; + // True if and only if server and origin have the same hostname. + bool server_and_origin_have_same_host_; bool is_post_; - bool was_alternate_protocol_recently_broken_; + bool was_alternative_service_recently_broken_; scoped_ptr<QuicServerInfo> server_info_; bool started_another_job_; const BoundNetLog net_log_; @@ -212,8 +225,9 @@ class QuicStreamFactory::Job { QuicStreamFactory::Job::Job(QuicStreamFactory* factory, HostResolver* host_resolver, const HostPortPair& host_port_pair, + bool server_and_origin_have_same_host, bool is_https, - bool was_alternate_protocol_recently_broken, + bool was_alternative_service_recently_broken, PrivacyMode privacy_mode, int cert_verify_flags, bool is_post, @@ -224,9 +238,10 @@ QuicStreamFactory::Job::Job(QuicStreamFactory* factory, host_resolver_(host_resolver), server_id_(host_port_pair, is_https, privacy_mode), cert_verify_flags_(cert_verify_flags), + server_and_origin_have_same_host_(server_and_origin_have_same_host), is_post_(is_post), - was_alternate_protocol_recently_broken_( - was_alternate_protocol_recently_broken), + was_alternative_service_recently_broken_( + was_alternative_service_recently_broken), server_info_(server_info), started_another_job_(false), net_log_(net_log), @@ -242,11 +257,12 @@ QuicStreamFactory::Job::Job(QuicStreamFactory* factory, factory_(factory), host_resolver_(host_resolver), // unused server_id_(server_id), - cert_verify_flags_(0), // unused - is_post_(false), // unused - was_alternate_protocol_recently_broken_(false), // unused - started_another_job_(false), // unused - net_log_(session->net_log()), // unused + cert_verify_flags_(0), // unused + server_and_origin_have_same_host_(false), // unused + is_post_(false), // unused + was_alternative_service_recently_broken_(false), // unused + started_another_job_(false), // unused + net_log_(session->net_log()), // unused session_(session), weak_factory_(this) { } @@ -395,7 +411,8 @@ int QuicStreamFactory::Job::DoLoadServerInfo() { // If we are waiting to load server config from the disk cache, then start // another job. started_another_job_ = true; - factory_->CreateAuxilaryJob(server_id_, cert_verify_flags_, is_post_, + factory_->CreateAuxilaryJob(server_id_, cert_verify_flags_, + server_and_origin_have_same_host_, is_post_, net_log_); } return rv; @@ -442,9 +459,9 @@ int QuicStreamFactory::Job::DoConnect() { if (!session_->connection()->connected()) { return ERR_QUIC_PROTOCOL_ERROR; } - bool require_confirmation = - factory_->require_confirmation() || is_post_ || - was_alternate_protocol_recently_broken_; + bool require_confirmation = factory_->require_confirmation() || + !server_and_origin_have_same_host_ || is_post_ || + was_alternative_service_recently_broken_; rv = session_->CryptoConnect( require_confirmation, @@ -492,14 +509,18 @@ int QuicStreamRequest::Request(const HostPortPair& host_port_pair, bool is_https, PrivacyMode privacy_mode, int cert_verify_flags, + base::StringPiece origin_host, base::StringPiece method, const BoundNetLog& net_log, const CompletionCallback& callback) { DCHECK(!stream_); DCHECK(callback_.is_null()); DCHECK(factory_); - int rv = factory_->Create(host_port_pair, is_https, privacy_mode, - cert_verify_flags, method, net_log, this); + origin_host_ = origin_host.as_string(); + privacy_mode_ = privacy_mode; + int rv = + factory_->Create(host_port_pair, is_https, privacy_mode, + cert_verify_flags, origin_host, method, net_log, this); if (rv == ERR_IO_PENDING) { host_port_pair_ = host_port_pair; net_log_ = net_log; @@ -547,8 +568,12 @@ QuicStreamFactory::QuicStreamFactory( bool enable_connection_racing, bool enable_non_blocking_io, bool disable_disk_cache, + bool prefer_aes, int max_number_of_lossy_connections, float packet_loss_threshold, + int max_disabled_reasons, + int threshold_public_resets_post_handshake, + int threshold_timeouts_with_open_streams, int socket_receive_buffer_size, const QuicTagVector& connection_options) : require_confirmation_(true), @@ -572,8 +597,18 @@ QuicStreamFactory::QuicStreamFactory( enable_connection_racing_(enable_connection_racing), enable_non_blocking_io_(enable_non_blocking_io), disable_disk_cache_(disable_disk_cache), + prefer_aes_(prefer_aes), max_number_of_lossy_connections_(max_number_of_lossy_connections), packet_loss_threshold_(packet_loss_threshold), + max_disabled_reasons_(max_disabled_reasons), + num_public_resets_post_handshake_(0), + num_timeouts_with_open_streams_(0), + max_public_resets_post_handshake_(0), + max_timeouts_with_open_streams_(0), + threshold_timeouts_with_open_streams_( + threshold_timeouts_with_open_streams), + threshold_public_resets_post_handshake_( + threshold_public_resets_post_handshake), socket_receive_buffer_size_(socket_receive_buffer_size), port_seed_(random_generator_->RandUint64()), check_persisted_supports_quic_(true), @@ -583,6 +618,7 @@ QuicStreamFactory::QuicStreamFactory( crypto_config_.set_user_agent_id(user_agent_id); crypto_config_.AddCanonicalSuffix(".c.youtube.com"); crypto_config_.AddCanonicalSuffix(".googlevideo.com"); + crypto_config_.AddCanonicalSuffix(".googleusercontent.com"); crypto_config_.SetProofVerifier( new ProofVerifierChromium(cert_verifier, transport_security_state)); // TODO(rtenneti): http://crbug.com/487355. Temporary fix for b/20760730 until @@ -591,8 +627,16 @@ QuicStreamFactory::QuicStreamFactory( crypto_config_.SetChannelIDSource( new ChannelIDSourceChromium(channel_id_service)); } +#if defined(USE_OPENSSL) + crypto::EnsureOpenSSLInit(); + bool has_aes_hardware_support = !!EVP_has_aes_hardware(); +#else base::CPU cpu; - if (cpu.has_aesni() && cpu.has_avx()) + bool has_aes_hardware_support = cpu.has_aesni() && cpu.has_avx(); +#endif + UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.PreferAesGcm", + has_aes_hardware_support); + if (has_aes_hardware_support || prefer_aes_) crypto_config_.PreferAesGcm(); if (!IsEcdsaSupported()) crypto_config_.DisableEcdsa(); @@ -623,12 +667,17 @@ int QuicStreamFactory::Create(const HostPortPair& host_port_pair, bool is_https, PrivacyMode privacy_mode, int cert_verify_flags, + base::StringPiece origin_host, base::StringPiece method, const BoundNetLog& net_log, QuicStreamRequest* request) { QuicServerId server_id(host_port_pair, is_https, privacy_mode); - if (HasActiveSession(server_id)) { - request->set_stream(CreateIfSessionExists(server_id, net_log)); + SessionMap::iterator it = active_sessions_.find(server_id); + if (it != active_sessions_.end()) { + QuicClientSession* session = it->second; + if (!session->CanPool(origin_host.as_string(), privacy_mode)) + return ERR_ALTERNATIVE_CERT_NOT_VALID_FOR_ORIGIN; + request->set_stream(CreateFromSession(session)); return OK; } @@ -641,7 +690,7 @@ int QuicStreamFactory::Create(const HostPortPair& host_port_pair, // TODO(rtenneti): |task_runner_| is used by the Job. Initialize task_runner_ // in the constructor after WebRequestActionWithThreadsTest.* tests are fixed. if (!task_runner_) - task_runner_ = base::MessageLoop::current()->message_loop_proxy().get(); + task_runner_ = base::ThreadTaskRunnerHandle::Get().get(); QuicServerInfo* quic_server_info = nullptr; if (quic_server_info_factory_) { @@ -649,13 +698,21 @@ int QuicStreamFactory::Create(const HostPortPair& host_port_pair, if (http_server_properties_) { const AlternativeServiceMap& alternative_service_map = http_server_properties_->alternative_service_map(); - AlternativeServiceMap::const_iterator it = + AlternativeServiceMap::const_iterator map_it = alternative_service_map.Peek(server_id.host_port_pair()); - if (it == alternative_service_map.end() || - it->second.alternative_service.protocol != QUIC) { + if (map_it != alternative_service_map.end()) { + const AlternativeServiceInfoVector& alternative_service_info_vector = + map_it->second; + AlternativeServiceInfoVector::const_iterator it; + for (it = alternative_service_info_vector.begin(); + it != alternative_service_info_vector.end(); ++it) { + if (it->alternative_service.protocol == QUIC) + break; + } // If there is no entry for QUIC, consider that as a new server and // don't wait for Cache thread to load the data for that server. - load_from_disk_cache = false; + if (it == alternative_service_info_vector.end()) + load_from_disk_cache = false; } } if (load_from_disk_cache && CryptoConfigCacheIsEmpty(server_id)) { @@ -663,7 +720,9 @@ int QuicStreamFactory::Create(const HostPortPair& host_port_pair, } } - scoped_ptr<Job> job(new Job(this, host_resolver_, host_port_pair, is_https, + bool server_and_origin_have_same_host = host_port_pair.host() == origin_host; + scoped_ptr<Job> job(new Job(this, host_resolver_, host_port_pair, + server_and_origin_have_same_host, is_https, WasQuicRecentlyBroken(server_id), privacy_mode, cert_verify_flags, method == "POST" /* is_post */, quic_server_info, net_log)); @@ -676,20 +735,26 @@ int QuicStreamFactory::Create(const HostPortPair& host_port_pair, return rv; } if (rv == OK) { - DCHECK(HasActiveSession(server_id)); - request->set_stream(CreateIfSessionExists(server_id, net_log)); + it = active_sessions_.find(server_id); + DCHECK(it != active_sessions_.end()); + QuicClientSession* session = it->second; + if (!session->CanPool(origin_host.as_string(), privacy_mode)) + return ERR_ALTERNATIVE_CERT_NOT_VALID_FOR_ORIGIN; + request->set_stream(CreateFromSession(session)); } return rv; } void QuicStreamFactory::CreateAuxilaryJob(const QuicServerId server_id, int cert_verify_flags, + bool server_and_origin_have_same_host, bool is_post, const BoundNetLog& net_log) { - Job* aux_job = new Job(this, host_resolver_, server_id.host_port_pair(), - server_id.is_https(), WasQuicRecentlyBroken(server_id), - server_id.privacy_mode(), cert_verify_flags, is_post, - nullptr, net_log); + Job* aux_job = + new Job(this, host_resolver_, server_id.host_port_pair(), + server_and_origin_have_same_host, server_id.is_https(), + WasQuicRecentlyBroken(server_id), server_id.privacy_mode(), + cert_verify_flags, is_post, nullptr, net_log); active_jobs_[server_id].insert(aux_job); task_runner_->PostTask(FROM_HERE, base::Bind(&QuicStreamFactory::Job::RunAuxilaryJob, @@ -739,9 +804,25 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) { set_require_confirmation(false); // Create all the streams, but do not notify them yet. - for (QuicStreamRequest* request : job_requests_map_[server_id]) { - DCHECK(HasActiveSession(server_id)); - request->set_stream(CreateIfSessionExists(server_id, request->net_log())); + SessionMap::iterator session_it = active_sessions_.find(server_id); + for (RequestSet::iterator request_it = job_requests_map_[server_id].begin(); + request_it != job_requests_map_[server_id].end();) { + DCHECK(session_it != active_sessions_.end()); + QuicClientSession* session = session_it->second; + QuicStreamRequest* request = *request_it; + if (!session->CanPool(request->origin_host(), request->privacy_mode())) { + RequestSet::iterator old_request_it = request_it; + ++request_it; + // Remove request from containers so that OnRequestComplete() is not + // called later again on the same request. + job_requests_map_[server_id].erase(old_request_it); + active_requests_.erase(request); + // Notify request of certificate error. + request->OnRequestComplete(ERR_ALTERNATIVE_CERT_NOT_VALID_FOR_ORIGIN); + continue; + } + request->set_stream(CreateFromSession(session)); + ++request_it; } } @@ -766,25 +847,50 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) { job_requests_map_.erase(server_id); } -// Returns a newly created QuicHttpStream owned by the caller, if a -// matching session already exists. Returns nullptr otherwise. -scoped_ptr<QuicHttpStream> QuicStreamFactory::CreateIfSessionExists( - const QuicServerId& server_id, - const BoundNetLog& net_log) { - if (!HasActiveSession(server_id)) { - DVLOG(1) << "No active session"; - return scoped_ptr<QuicHttpStream>(); +scoped_ptr<QuicHttpStream> QuicStreamFactory::CreateFromSession( + QuicClientSession* session) { + return scoped_ptr<QuicHttpStream>(new QuicHttpStream(session->GetWeakPtr())); +} + +QuicClientSession::QuicDisabledReason QuicStreamFactory::QuicDisabledReason( + uint16 port) const { + if (max_number_of_lossy_connections_ > 0 && + number_of_lossy_connections_.find(port) != + number_of_lossy_connections_.end() && + number_of_lossy_connections_.at(port) >= + max_number_of_lossy_connections_) { + return QuicClientSession::QUIC_DISABLED_BAD_PACKET_LOSS_RATE; + } + if (threshold_public_resets_post_handshake_ > 0 && + num_public_resets_post_handshake_ >= + threshold_public_resets_post_handshake_) { + return QuicClientSession::QUIC_DISABLED_PUBLIC_RESET_POST_HANDSHAKE; } + if (threshold_timeouts_with_open_streams_ > 0 && + num_timeouts_with_open_streams_ >= + threshold_timeouts_with_open_streams_) { + return QuicClientSession::QUIC_DISABLED_TIMEOUT_WITH_OPEN_STREAMS; + } + return QuicClientSession::QUIC_DISABLED_NOT; +} - QuicClientSession* session = active_sessions_[server_id]; - DCHECK(session); - return scoped_ptr<QuicHttpStream>( - new QuicHttpStream(session->GetWeakPtr())); +const char* QuicStreamFactory::QuicDisabledReasonString() const { + // TODO(ckrasic) - better solution for port/lossy connections? + const uint16 port = 443; + switch (QuicDisabledReason(port)) { + case QuicClientSession::QUIC_DISABLED_BAD_PACKET_LOSS_RATE: + return "Bad packet loss rate."; + case QuicClientSession::QUIC_DISABLED_PUBLIC_RESET_POST_HANDSHAKE: + return "Public resets after successful handshakes."; + case QuicClientSession::QUIC_DISABLED_TIMEOUT_WITH_OPEN_STREAMS: + return "Connection timeouts with streams open."; + default: + return ""; + } } bool QuicStreamFactory::IsQuicDisabled(uint16 port) { - return max_number_of_lossy_connections_ > 0 && - number_of_lossy_connections_[port] >= max_number_of_lossy_connections_; + return QuicDisabledReason(port) != QuicClientSession::QUIC_DISABLED_NOT; } bool QuicStreamFactory::OnHandshakeConfirmed(QuicClientSession* session, @@ -803,19 +909,29 @@ bool QuicStreamFactory::OnHandshakeConfirmed(QuicClientSession* session, AlternativeService(QUIC, session->server_id().host(), port)); } - // We abandon the connection if packet loss rate is too bad. - session->CloseSessionOnErrorAndNotifyFactoryLater(ERR_ABORTED, - QUIC_BAD_PACKET_LOSS_RATE); - - if (IsQuicDisabled(port)) - return true; // Exit if Quic is already disabled for this port. + bool was_quic_disabled = IsQuicDisabled(port); + ++number_of_lossy_connections_[port]; - if (++number_of_lossy_connections_[port] >= - max_number_of_lossy_connections_) { - UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicStreamFactory.QuicIsDisabled", port); + // Collect data for port 443 for packet loss events. + if (port == 443 && max_number_of_lossy_connections_ > 0) { + UMA_HISTOGRAM_SPARSE_SLOWLY( + base::StringPrintf("Net.QuicStreamFactory.BadPacketLossEvents%d", + max_number_of_lossy_connections_), + std::min(number_of_lossy_connections_[port], + max_number_of_lossy_connections_)); } - return true; + bool is_quic_disabled = IsQuicDisabled(port); + if (is_quic_disabled) { + // Close QUIC connection if Quic is disabled for this port. + session->CloseSessionOnErrorAndNotifyFactoryLater( + ERR_ABORTED, QUIC_BAD_PACKET_LOSS_RATE); + + // If this bad packet loss rate disabled the QUIC, then record it. + if (!was_quic_disabled) + UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicStreamFactory.QuicIsDisabled", port); + } + return is_quic_disabled; } void QuicStreamFactory::OnIdleSession(QuicClientSession* session) { @@ -848,8 +964,67 @@ void QuicStreamFactory::OnSessionGoingAway(QuicClientSession* session) { session_aliases_.erase(session); } +void QuicStreamFactory::MaybeDisableQuic(QuicClientSession* session) { + DCHECK(session); + uint16 port = session->server_id().port(); + if (IsQuicDisabled(port)) + return; + + // Expire the oldest disabled_reason if appropriate. This enforces that we + // only consider the max_disabled_reasons_ most recent sessions. + QuicClientSession::QuicDisabledReason disabled_reason; + if (static_cast<int>(disabled_reasons_.size()) == max_disabled_reasons_) { + disabled_reason = disabled_reasons_.front(); + disabled_reasons_.pop_front(); + if (disabled_reason == + QuicClientSession::QUIC_DISABLED_PUBLIC_RESET_POST_HANDSHAKE) { + --num_public_resets_post_handshake_; + } else if (disabled_reason == + QuicClientSession::QUIC_DISABLED_TIMEOUT_WITH_OPEN_STREAMS) { + --num_timeouts_with_open_streams_; + } + } + disabled_reason = session->disabled_reason(); + disabled_reasons_.push_back(disabled_reason); + if (disabled_reason == + QuicClientSession::QUIC_DISABLED_PUBLIC_RESET_POST_HANDSHAKE) { + ++num_public_resets_post_handshake_; + } else if (disabled_reason == + QuicClientSession::QUIC_DISABLED_TIMEOUT_WITH_OPEN_STREAMS) { + ++num_timeouts_with_open_streams_; + } + if (num_timeouts_with_open_streams_ > max_timeouts_with_open_streams_) { + max_timeouts_with_open_streams_ = num_timeouts_with_open_streams_; + UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicStreamFactory.TimeoutsWithOpenStreams", + num_timeouts_with_open_streams_, 0, 20, 10); + } + + if (num_public_resets_post_handshake_ > max_public_resets_post_handshake_) { + max_public_resets_post_handshake_ = num_public_resets_post_handshake_; + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Net.QuicStreamFactory.PublicResetsPostHandshake", + num_public_resets_post_handshake_, 0, 20, 10); + } + + if (IsQuicDisabled(port)) { + if (disabled_reason == + QuicClientSession::QUIC_DISABLED_PUBLIC_RESET_POST_HANDSHAKE) { + session->CloseSessionOnErrorAndNotifyFactoryLater( + ERR_ABORTED, QUIC_PUBLIC_RESETS_POST_HANDSHAKE); + } else if (disabled_reason == + QuicClientSession::QUIC_DISABLED_TIMEOUT_WITH_OPEN_STREAMS) { + session->CloseSessionOnErrorAndNotifyFactoryLater( + ERR_ABORTED, QUIC_TIMEOUTS_WITH_OPEN_STREAMS); + } + UMA_HISTOGRAM_ENUMERATION("Net.QuicStreamFactory.DisabledReasons", + disabled_reason, + QuicClientSession::QUIC_DISABLED_MAX); + } +} + void QuicStreamFactory::OnSessionClosed(QuicClientSession* session) { DCHECK_EQ(0u, session->GetNumOpenStreams()); + MaybeDisableQuic(session); OnSessionGoingAway(session); delete session; all_sessions_.erase(session); @@ -907,8 +1082,9 @@ void QuicStreamFactory::CloseAllSessions(int error) { DCHECK(all_sessions_.empty()); } -base::Value* QuicStreamFactory::QuicStreamFactoryInfoToValue() const { - base::ListValue* list = new base::ListValue(); +scoped_ptr<base::Value> QuicStreamFactory::QuicStreamFactoryInfoToValue() + const { + scoped_ptr<base::ListValue> list(new base::ListValue()); for (SessionMap::const_iterator it = active_sessions_.begin(); it != active_sessions_.end(); ++it) { @@ -925,7 +1101,7 @@ base::Value* QuicStreamFactory::QuicStreamFactoryInfoToValue() const { list->Append(session->GetInfoAsValue(hosts)); } } - return list; + return list.Pass(); } void QuicStreamFactory::ClearCachedStatesInCryptoConfig() { @@ -1041,9 +1217,9 @@ int QuicStreamFactory::CreateSession(const QuicServerId& server_id, DefaultPacketWriterFactory packet_writer_factory(socket.get()); if (!helper_.get()) { - helper_.reset(new QuicConnectionHelper( - base::MessageLoop::current()->message_loop_proxy().get(), - clock_.get(), random_generator_)); + helper_.reset( + new QuicConnectionHelper(base::ThreadTaskRunnerHandle::Get().get(), + clock_.get(), random_generator_)); } QuicConnection* connection = new QuicConnection( @@ -1057,8 +1233,9 @@ int QuicStreamFactory::CreateSession(const QuicServerId& server_id, QuicConfig config = config_; config.SetSocketReceiveBufferToSend(socket_receive_buffer_size_); config.set_max_undecryptable_packets(kMaxUndecryptablePackets); - config.SetInitialStreamFlowControlWindowToSend(kInitialReceiveWindowSize); - config.SetInitialSessionFlowControlWindowToSend(kInitialReceiveWindowSize); + config.SetInitialSessionFlowControlWindowToSend( + kQuicSessionMaxRecvWindowSize); + config.SetInitialStreamFlowControlWindowToSend(kQuicStreamMaxRecvWindowSize); int64 srtt = GetServerNetworkStatsSmoothedRttInMicroseconds(server_id); if (srtt > 0) config.SetInitialRoundTripTimeUsToSend(static_cast<uint32>(srtt)); @@ -1073,16 +1250,15 @@ int QuicStreamFactory::CreateSession(const QuicServerId& server_id, } *session = new QuicClientSession( - connection, socket.Pass(), this, transport_security_state_, - server_info.Pass(), cert_verify_flags, config, + connection, socket.Pass(), this, quic_crypto_client_stream_factory_, + transport_security_state_, server_info.Pass(), server_id, + cert_verify_flags, config, &crypto_config_, network_connection_.GetDescription(), dns_resolution_end_time, - base::MessageLoop::current()->message_loop_proxy().get(), - net_log.net_log()); + base::ThreadTaskRunnerHandle::Get().get(), net_log.net_log()); all_sessions_[*session] = server_id; // owning pointer - (*session)->InitializeSession(server_id, &crypto_config_, - quic_crypto_client_stream_factory_); + (*session)->Initialize(); bool closed_during_initialize = !ContainsKey(all_sessions_, *session) || !(*session)->connection()->connected(); @@ -1153,10 +1329,14 @@ void QuicStreamFactory::InitializeCachedStateInCryptoConfig( if (http_server_properties_) { if (quic_supported_servers_at_startup_.empty()) { - for (const std::pair<const HostPortPair, AlternativeServiceInfo>& + for (const std::pair<const HostPortPair, AlternativeServiceInfoVector>& key_value : http_server_properties_->alternative_service_map()) { - if (key_value.second.alternative_service.protocol == QUIC) { - quic_supported_servers_at_startup_.insert(key_value.first); + for (const AlternativeServiceInfo& alternative_service_info : + key_value.second) { + if (alternative_service_info.alternative_service.protocol == QUIC) { + quic_supported_servers_at_startup_.insert(key_value.first); + break; + } } } } diff --git a/chromium/net/quic/quic_stream_factory.h b/chromium/net/quic/quic_stream_factory.h index f7400a3355b..2e39387df42 100644 --- a/chromium/net/quic/quic_stream_factory.h +++ b/chromium/net/quic/quic_stream_factory.h @@ -20,6 +20,7 @@ #include "net/log/net_log.h" #include "net/proxy/proxy_server.h" #include "net/quic/network_connection.h" +#include "net/quic/quic_client_session.h" #include "net/quic/quic_config.h" #include "net/quic/quic_crypto_stream.h" #include "net/quic/quic_http_stream.h" @@ -61,6 +62,7 @@ class NET_EXPORT_PRIVATE QuicStreamRequest { bool is_https, PrivacyMode privacy_mode, int cert_verify_flags, + base::StringPiece origin_host, base::StringPiece method, const BoundNetLog& net_log, const CompletionCallback& callback); @@ -71,6 +73,10 @@ class NET_EXPORT_PRIVATE QuicStreamRequest { void set_stream(scoped_ptr<QuicHttpStream> stream); + const std::string origin_host() const { return origin_host_; } + + PrivacyMode privacy_mode() const { return privacy_mode_; } + const BoundNetLog& net_log() const{ return net_log_; } @@ -78,6 +84,8 @@ class NET_EXPORT_PRIVATE QuicStreamRequest { private: QuicStreamFactory* factory_; HostPortPair host_port_pair_; + std::string origin_host_; + PrivacyMode privacy_mode_; BoundNetLog net_log_; CompletionCallback callback_; scoped_ptr<QuicHttpStream> stream_; @@ -111,8 +119,12 @@ class NET_EXPORT_PRIVATE QuicStreamFactory bool enable_connection_racing, bool enable_non_blocking_io, bool disable_disk_cache, + bool prefer_aes, int max_number_of_lossy_connections, float packet_loss_threshold, + int max_recent_disabled_reasons, + int threshold_timeouts_with_streams_open, + int threshold_public_resets_post_handshake, int socket_receive_buffer_size, const QuicTagVector& connection_options); ~QuicStreamFactory() override; @@ -126,19 +138,30 @@ class NET_EXPORT_PRIVATE QuicStreamFactory bool is_https, PrivacyMode privacy_mode, int cert_verify_flags, + base::StringPiece origin_host, base::StringPiece method, const BoundNetLog& net_log, QuicStreamRequest* request); - // Returns false if |packet_loss_rate| is less than |packet_loss_threshold_| - // otherwise it returns true and closes the session and marks QUIC as recently - // broken for the port of the session. Increments - // |number_of_lossy_connections_| by port. + // If |packet_loss_rate| is greater than or equal to |packet_loss_threshold_| + // it marks QUIC as recently broken for the port of the session. Increments + // |number_of_lossy_connections_| by port. If |number_of_lossy_connections_| + // is greater than or equal to |max_number_of_lossy_connections_| then it + // disables QUIC. If QUIC is disabled then it closes the connection. + // + // Returns true if QUIC is disabled for the port of the session. bool OnHandshakeConfirmed(QuicClientSession* session, float packet_loss_rate); // Returns true if QUIC is disabled for this port. bool IsQuicDisabled(uint16 port); + // Returns reason QUIC is disabled for this port, or QUIC_DISABLED_NOT if not. + QuicClientSession::QuicDisabledReason QuicDisabledReason(uint16 port) const; + + // Returns reason QUIC is disabled as string for net-internals, or + // returns empty string if QUIC is not disabled. + const char* QuicDisabledReasonString() const; + // Called by a session when it becomes idle. void OnIdleSession(QuicClientSession* session); @@ -158,7 +181,7 @@ class NET_EXPORT_PRIVATE QuicStreamFactory // Closes all current sessions. void CloseAllSessions(int error); - base::Value* QuicStreamFactoryInfoToValue() const; + scoped_ptr<base::Value> QuicStreamFactoryInfoToValue() const; // Delete all cached state objects in |crypto_config_|. void ClearCachedStatesInCryptoConfig(); @@ -200,9 +223,12 @@ class NET_EXPORT_PRIVATE QuicStreamFactory enable_connection_racing_ = enable_connection_racing; } + int socket_receive_buffer_size() const { return socket_receive_buffer_size_; } + private: class Job; friend class test::QuicStreamFactoryPeer; + FRIEND_TEST_ALL_PREFIXES(HttpStreamFactoryTest, QuicLossyProxyMarkedAsBad); // The key used to find session by ip. Includes // the ip address, port, and scheme. @@ -231,18 +257,19 @@ class NET_EXPORT_PRIVATE QuicStreamFactory typedef std::map<QuicStreamRequest*, QuicServerId> RequestMap; typedef std::set<QuicStreamRequest*> RequestSet; typedef std::map<QuicServerId, RequestSet> ServerIDRequestsMap; + typedef std::deque<enum QuicClientSession::QuicDisabledReason> + DisabledReasonsQueue; // Creates a job which doesn't wait for server config to be loaded from the // disk cache. This job is started via a PostTask. void CreateAuxilaryJob(const QuicServerId server_id, int cert_verify_flags, + bool server_and_origin_have_same_host, bool is_post, const BoundNetLog& net_log); - // 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); + // Returns a newly created QuicHttpStream owned by the caller. + scoped_ptr<QuicHttpStream> CreateFromSession(QuicClientSession*); bool OnResolution(const QuicServerId& server_id, const AddressList& address_list); @@ -279,6 +306,9 @@ class NET_EXPORT_PRIVATE QuicStreamFactory const QuicServerId& server_id, bool was_session_active); + // Collect stats from recent connections, possibly disabling Quic. + void MaybeDisableQuic(QuicClientSession* session); + bool require_confirmation_; HostResolver* host_resolver_; ClientSocketFactory* client_socket_factory_; @@ -344,6 +374,9 @@ class NET_EXPORT_PRIVATE QuicStreamFactory // Set if we do not want to load server config from the disk cache. bool disable_disk_cache_; + // Set if AES-GCM should be preferred, even if there is no hardware support. + bool prefer_aes_; + // Set if we want to disable QUIC when there is high packet loss rate. // Specifies the maximum number of connections with high packet loss in a row // after which QUIC will be disabled. @@ -354,6 +387,21 @@ class NET_EXPORT_PRIVATE QuicStreamFactory // Count number of lossy connections by port. std::map<uint16, int> number_of_lossy_connections_; + // Keep track of stats for recently closed connections, using a + // bounded queue. + int max_disabled_reasons_; + DisabledReasonsQueue disabled_reasons_; + // Events that can trigger disabling QUIC + int num_public_resets_post_handshake_; + int num_timeouts_with_open_streams_; + // Keep track the largest values for UMA histograms, that will help + // determine good threshold values. + int max_public_resets_post_handshake_; + int max_timeouts_with_open_streams_; + // Thresholds if greater than zero, determine when to + int threshold_timeouts_with_open_streams_; + int threshold_public_resets_post_handshake_; + // Size of the UDP receive buffer. int socket_receive_buffer_size_; diff --git a/chromium/net/quic/quic_stream_factory_test.cc b/chromium/net/quic/quic_stream_factory_test.cc index 456a8da56d2..b4044558b36 100644 --- a/chromium/net/quic/quic_stream_factory_test.cc +++ b/chromium/net/quic/quic_stream_factory_test.cc @@ -6,6 +6,7 @@ #include "base/run_loop.h" #include "base/strings/string_util.h" +#include "base/thread_task_runner_handle.h" #include "net/base/test_data_directory.h" #include "net/cert/cert_verifier.h" #include "net/dns/mock_host_resolver.h" @@ -96,13 +97,10 @@ class QuicStreamFactoryPeer { return factory->active_sessions_[server_id]; } - static scoped_ptr<QuicHttpStream> CreateIfSessionExists( + static scoped_ptr<QuicHttpStream> CreateFromSession( 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); + QuicClientSession* session) { + return factory->CreateFromSession(session); } static bool IsLiveSession(QuicStreamFactory* factory, @@ -154,6 +152,28 @@ class QuicStreamFactoryPeer { const QuicServerId& server_id) { return (factory->active_jobs_[server_id]).size(); } + + static void SetThresholdTimeoutsWithOpenStreams( + QuicStreamFactory* factory, + int threshold_timeouts_with_open_streams) { + factory->threshold_timeouts_with_open_streams_ = + threshold_timeouts_with_open_streams; + } + + static int GetNumTimeoutsWithOpenStreams(QuicStreamFactory* factory) { + return factory->num_timeouts_with_open_streams_; + } + + static void SetThresholdPublicResetsPostHandshake( + QuicStreamFactory* factory, + int threshold_public_resets_post_handshake) { + factory->threshold_public_resets_post_handshake_ = + threshold_public_resets_post_handshake; + } + + static int GetNumPublicResetsPostHandshake(QuicStreamFactory* factory) { + return factory->num_public_resets_post_handshake_; + } }; class MockQuicServerInfo : public QuicServerInfo { @@ -201,7 +221,7 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<TestParams> { cert_verifier_(CertVerifier::CreateDefault()), channel_id_service_( new ChannelIDService(new DefaultChannelIDStore(nullptr), - base::MessageLoopProxy::current())), + base::ThreadTaskRunnerHandle::Get())), factory_(&host_resolver_, &socket_factory_, base::WeakPtr<HttpServerProperties>(), @@ -221,8 +241,12 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<TestParams> { /*enable_connection_racing=*/false, /*enable_non_blocking_io=*/true, /*disable_disk_cache=*/false, + /*prefer_aes=*/false, /*max_number_of_lossy_connections=*/0, /*packet_loss_threshold=*/1.0f, + /*max_disabled_reasons=*/3, + /*threshold_timeouts_with_open_streams=*/2, + /*threshold_pulic_resets_post_handshake=*/2, /*receive_buffer_size=*/0, QuicTagVector()), host_port_pair_(kDefaultServerHostName, kDefaultServerPort), @@ -234,11 +258,16 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<TestParams> { &factory_, GetParam().enable_connection_racing); } - scoped_ptr<QuicHttpStream> CreateIfSessionExists( - const HostPortPair& host_port_pair, - const BoundNetLog& net_log) { - return QuicStreamFactoryPeer::CreateIfSessionExists( - &factory_, host_port_pair, false, net_log_); + bool HasActiveSession(const HostPortPair& host_port_pair) { + return QuicStreamFactoryPeer::HasActiveSession(&factory_, host_port_pair, + /*is_https_=*/false); + } + + scoped_ptr<QuicHttpStream> CreateFromSession( + const HostPortPair& host_port_pair) { + QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession( + &factory_, host_port_pair, /*is_https=*/false); + return QuicStreamFactoryPeer::CreateFromSession(&factory_, session); } int GetSourcePortForNewSession(const HostPortPair& destination) { @@ -253,7 +282,7 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<TestParams> { int GetSourcePortForNewSessionInner(const HostPortPair& destination, bool goaway_received) { // Should only be called if there is no active session for this destination. - EXPECT_EQ(nullptr, CreateIfSessionExists(destination, net_log_).get()); + EXPECT_FALSE(HasActiveSession(destination)); size_t socket_count = socket_factory_.udp_client_sockets().size(); MockRead reads[] = { @@ -266,8 +295,8 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<TestParams> { QuicStreamRequest request(&factory_); EXPECT_EQ(ERR_IO_PENDING, request.Request(destination, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, destination.host(), + "GET", net_log_, callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); @@ -292,9 +321,9 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<TestParams> { } factory_.OnSessionClosed(session); - EXPECT_EQ(nullptr, CreateIfSessionExists(destination, net_log_).get()); - EXPECT_TRUE(socket_data.at_read_eof()); - EXPECT_TRUE(socket_data.at_write_eof()); + EXPECT_FALSE(HasActiveSession(destination)); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); return port; } @@ -305,6 +334,18 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<TestParams> { AdjustErrorForVersion(QUIC_RST_ACKNOWLEDGEMENT, GetParam().version)); } + static ProofVerifyDetailsChromium DefaultProofVerifyDetails() { + // Load a certificate that is valid for www.example.org, mail.example.org, + // and mail.example.com. + scoped_refptr<X509Certificate> test_cert( + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem")); + EXPECT_TRUE(test_cert.get()); + ProofVerifyDetailsChromium verify_details; + verify_details.cert_verify_result.verified_cert = test_cert; + verify_details.cert_verify_result.is_issued_by_known_root = true; + return verify_details; + } + MockQuicServerInfoFactory quic_server_info_factory_; MockHostResolver host_resolver_; DeterministicMockClientSocketFactory socket_factory_; @@ -328,10 +369,6 @@ INSTANTIATE_TEST_CASE_P(Version, QuicStreamFactoryTest, ::testing::ValuesIn(GetTestParams())); -TEST_P(QuicStreamFactoryTest, CreateIfSessionExists) { - EXPECT_EQ(nullptr, CreateIfSessionExists(host_port_pair_, net_log_).get()); -} - TEST_P(QuicStreamFactoryTest, Create) { MockRead reads[] = { MockRead(ASYNC, OK, 0) // EOF @@ -343,28 +380,29 @@ TEST_P(QuicStreamFactoryTest, Create) { QuicStreamRequest request(&factory_); EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "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 = CreateIfSessionExists(host_port_pair_, net_log_); + stream = CreateFromSession(host_port_pair_); 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_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + EXPECT_EQ(OK, + request2.Request(host_port_pair_, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); stream = request2.ReleaseStream(); // Will reset stream 5. stream.reset(); // Will reset stream 7. - EXPECT_TRUE(socket_data.at_read_eof()); - EXPECT_TRUE(socket_data.at_write_eof()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, CreateZeroRtt) { @@ -383,13 +421,13 @@ TEST_P(QuicStreamFactoryTest, CreateZeroRtt) { QuicStreamRequest request(&factory_); EXPECT_EQ(OK, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); EXPECT_TRUE(stream.get()); - EXPECT_TRUE(socket_data.at_read_eof()); - EXPECT_TRUE(socket_data.at_write_eof()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, CreateZeroRttPost) { @@ -410,8 +448,8 @@ TEST_P(QuicStreamFactoryTest, CreateZeroRttPost) { // Posts require handshake confirmation, so this will return asynchronously. EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "POST", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "POST", net_log_, callback_.callback())); // Confirm the handshake and verify that the stream is created. crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent( @@ -420,8 +458,40 @@ TEST_P(QuicStreamFactoryTest, CreateZeroRttPost) { 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()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); +} + +TEST_P(QuicStreamFactoryTest, NoZeroRttForDifferentHost) { + MockRead reads[] = { + MockRead(ASYNC, OK, 0), + }; + DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 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_); + int rv = request.Request( + host_port_pair_, is_https_, privacy_mode_, /*cert_verify_flags=*/0, + "different.host.example.com", "GET", net_log_, callback_.callback()); + // If server and origin have different hostnames, then handshake confirmation + // should be required, so Request will return asynchronously. + EXPECT_EQ(ERR_IO_PENDING, rv); + // Confirm handshake. + 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.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, CreateHttpVsHttps) { @@ -438,8 +508,8 @@ TEST_P(QuicStreamFactoryTest, CreateHttpVsHttps) { QuicStreamRequest request(&factory_); EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); @@ -448,8 +518,8 @@ TEST_P(QuicStreamFactoryTest, CreateHttpVsHttps) { QuicStreamRequest request2(&factory_); EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_pair_, !is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); stream = request2.ReleaseStream(); EXPECT_TRUE(stream.get()); @@ -460,10 +530,10 @@ TEST_P(QuicStreamFactoryTest, CreateHttpVsHttps) { 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()); + EXPECT_TRUE(socket_data1.AllReadDataConsumed()); + EXPECT_TRUE(socket_data1.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, Pooling) { @@ -483,16 +553,16 @@ TEST_P(QuicStreamFactoryTest, Pooling) { QuicStreamRequest request(&factory_); EXPECT_EQ(OK, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "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_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback.callback())); + /*cert_verify_flags=*/0, server2.host(), "GET", + net_log_, callback.callback())); scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); EXPECT_TRUE(stream2.get()); @@ -501,8 +571,8 @@ TEST_P(QuicStreamFactoryTest, Pooling) { &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()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, NoPoolingIfDisabled) { @@ -528,16 +598,16 @@ TEST_P(QuicStreamFactoryTest, NoPoolingIfDisabled) { QuicStreamRequest request(&factory_); EXPECT_EQ(OK, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "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_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback.callback())); + /*cert_verify_flags=*/0, server2.host(), "GET", + net_log_, callback.callback())); scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); EXPECT_TRUE(stream2.get()); @@ -546,10 +616,10 @@ TEST_P(QuicStreamFactoryTest, NoPoolingIfDisabled) { &factory_, host_port_pair_, 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()); + EXPECT_TRUE(socket_data1.AllReadDataConsumed()); + EXPECT_TRUE(socket_data1.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, NoPoolingAfterGoAway) { @@ -572,16 +642,16 @@ TEST_P(QuicStreamFactoryTest, NoPoolingAfterGoAway) { QuicStreamRequest request(&factory_); EXPECT_EQ(OK, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "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_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback.callback())); + /*cert_verify_flags=*/0, server2.host(), "GET", + net_log_, callback.callback())); scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); EXPECT_TRUE(stream2.get()); @@ -595,18 +665,18 @@ TEST_P(QuicStreamFactoryTest, NoPoolingAfterGoAway) { TestCompletionCallback callback3; QuicStreamRequest request3(&factory_); EXPECT_EQ(OK, request3.Request(server2, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback3.callback())); + /*cert_verify_flags=*/0, server2.host(), "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()); + EXPECT_TRUE(socket_data1.AllReadDataConsumed()); + EXPECT_TRUE(socket_data1.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, HttpsPooling) { @@ -620,17 +690,7 @@ TEST_P(QuicStreamFactoryTest, HttpsPooling) { 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*>(nullptr), test_cert.get()); - ProofVerifyDetailsChromium verify_details; - verify_details.cert_verify_result.verified_cert = test_cert; - verify_details.cert_verify_result.is_issued_by_known_root = true; + ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); host_resolver_.set_synchronous_mode(true); @@ -640,16 +700,16 @@ TEST_P(QuicStreamFactoryTest, HttpsPooling) { QuicStreamRequest request(&factory_); is_https_ = true; EXPECT_EQ(OK, request.Request(server1, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, server1.host(), "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_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, server2.host(), "GET", + net_log_, callback_.callback())); scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); EXPECT_TRUE(stream2.get()); @@ -658,8 +718,8 @@ TEST_P(QuicStreamFactoryTest, HttpsPooling) { QuicStreamFactoryPeer::GetActiveSession( &factory_, server2, is_https_)); - EXPECT_TRUE(socket_data.at_read_eof()); - EXPECT_TRUE(socket_data.at_write_eof()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, NoHttpsPoolingIfDisabled) { @@ -676,17 +736,7 @@ TEST_P(QuicStreamFactoryTest, NoHttpsPoolingIfDisabled) { 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*>(nullptr), test_cert.get()); - ProofVerifyDetailsChromium verify_details; - verify_details.cert_verify_result.verified_cert = test_cert; - verify_details.cert_verify_result.is_issued_by_known_root = true; + ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); host_resolver_.set_synchronous_mode(true); @@ -699,16 +749,16 @@ TEST_P(QuicStreamFactoryTest, NoHttpsPoolingIfDisabled) { QuicStreamRequest request(&factory_); is_https_ = true; EXPECT_EQ(OK, request.Request(server1, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, server1.host(), "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_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, server2.host(), "GET", + net_log_, callback_.callback())); scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); EXPECT_TRUE(stream2.get()); @@ -717,68 +767,83 @@ TEST_P(QuicStreamFactoryTest, NoHttpsPoolingIfDisabled) { 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()); + EXPECT_TRUE(socket_data1.AllReadDataConsumed()); + EXPECT_TRUE(socket_data1.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); } -TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithCertMismatch) { - MockRead reads[] = { - MockRead(ASYNC, OK, 0) // EOF - }; - DeterministicSocketData socket_data1(reads, arraysize(reads), nullptr, 0); - DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 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*>(nullptr), test_cert.get()); - ProofVerifyDetailsChromium verify_details; - verify_details.cert_verify_result.verified_cert = test_cert; - crypto_client_stream_factory_.AddProofVerifyDetails(&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", ""); +class QuicAlternativeServiceCertificateValidationPooling + : public QuicStreamFactoryTest { + public: + void Run(bool valid) { + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data1(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data1); + socket_data1.StopAfter(1); + + HostPortPair server1("www.example.org", 443); + HostPortPair server2("mail.example.org", 443); + + std::string origin_host(valid ? "mail.example.org" : "invalid.example.org"); + HostPortPair alternative("www.example.org", 443); + + ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); + bool common_name_fallback_used; + EXPECT_EQ(valid, + verify_details.cert_verify_result.verified_cert->VerifyNameMatch( + origin_host, &common_name_fallback_used)); + EXPECT_TRUE( + verify_details.cert_verify_result.verified_cert->VerifyNameMatch( + alternative.host(), &common_name_fallback_used)); + crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); + + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule(alternative.host(), "192.168.0.1", + ""); + + // Open first stream to alternative. + QuicStreamRequest request1(&factory_); + is_https_ = true; + EXPECT_EQ(OK, request1.Request(alternative, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, alternative.host(), + "GET", net_log_, callback_.callback())); + scoped_ptr<QuicHttpStream> stream1 = request1.ReleaseStream(); + EXPECT_TRUE(stream1.get()); + + QuicStreamRequest request2(&factory_); + int rv = request2.Request(alternative, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, origin_host, "GET", + net_log_, callback_.callback()); + if (valid) { + // Alternative service of origin to |alternative| should pool to session + // of |stream1| even if origin is different. Since only one + // SocketDataProvider is set up, the second request succeeding means that + // it pooled to the session opened by the first one. + EXPECT_EQ(OK, rv); + scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); + EXPECT_TRUE(stream2.get()); + } else { + EXPECT_EQ(ERR_ALTERNATIVE_CERT_NOT_VALID_FOR_ORIGIN, rv); + } - QuicStreamRequest request(&factory_); - is_https_ = true; - EXPECT_EQ(OK, request.Request(server1, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); - scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); - EXPECT_TRUE(stream.get()); + EXPECT_TRUE(socket_data1.AllReadDataConsumed()); + EXPECT_TRUE(socket_data1.AllWriteDataConsumed()); + } +}; - TestCompletionCallback callback; - QuicStreamRequest request2(&factory_); - EXPECT_EQ(OK, request2.Request(server2, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); - scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); - EXPECT_TRUE(stream2.get()); +INSTANTIATE_TEST_CASE_P(Version, + QuicAlternativeServiceCertificateValidationPooling, + ::testing::ValuesIn(GetTestParams())); - EXPECT_NE(QuicStreamFactoryPeer::GetActiveSession( - &factory_, server1, is_https_), - QuicStreamFactoryPeer::GetActiveSession( - &factory_, server2, is_https_)); +TEST_P(QuicAlternativeServiceCertificateValidationPooling, Valid) { + Run(true); +} - 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(QuicAlternativeServiceCertificateValidationPooling, Invalid) { + Run(false); } TEST_P(QuicStreamFactoryTest, HttpsPoolingWithMatchingPins) { @@ -796,16 +861,7 @@ TEST_P(QuicStreamFactoryTest, HttpsPoolingWithMatchingPins) { test::AddPin(&transport_security_state_, "mail.example.org", primary_pin, backup_pin); - // Load a cert that is valid for: - // www.example.org (server1) - // mail.example.org (server2) - base::FilePath certs_dir = GetTestCertsDirectory(); - scoped_refptr<X509Certificate> test_cert( - ImportCertFromFile(certs_dir, "spdy_pooling.pem")); - ASSERT_NE(static_cast<X509Certificate*>(nullptr), test_cert.get()); - ProofVerifyDetailsChromium verify_details; - verify_details.cert_verify_result.verified_cert = test_cert; - verify_details.cert_verify_result.is_issued_by_known_root = true; + ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); verify_details.cert_verify_result.public_key_hashes.push_back( test::GetTestHashValue(primary_pin)); crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); @@ -817,16 +873,16 @@ TEST_P(QuicStreamFactoryTest, HttpsPoolingWithMatchingPins) { QuicStreamRequest request(&factory_); is_https_ = true; EXPECT_EQ(OK, request.Request(server1, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, server1.host(), "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_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, server2.host(), "GET", + net_log_, callback_.callback())); scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); EXPECT_TRUE(stream2.get()); @@ -835,8 +891,8 @@ TEST_P(QuicStreamFactoryTest, HttpsPoolingWithMatchingPins) { QuicStreamFactoryPeer::GetActiveSession( &factory_, server2, is_https_)); - EXPECT_TRUE(socket_data.at_read_eof()); - EXPECT_TRUE(socket_data.at_write_eof()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithMatchingPinsIfDisabled) { @@ -857,16 +913,7 @@ TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithMatchingPinsIfDisabled) { test::AddPin(&transport_security_state_, "mail.example.org", primary_pin, backup_pin); - // Load a cert that is valid for: - // www.example.org (server1) - // mail.example.org (server2) - base::FilePath certs_dir = GetTestCertsDirectory(); - scoped_refptr<X509Certificate> test_cert( - ImportCertFromFile(certs_dir, "spdy_pooling.pem")); - ASSERT_NE(static_cast<X509Certificate*>(nullptr), test_cert.get()); - ProofVerifyDetailsChromium verify_details; - verify_details.cert_verify_result.verified_cert = test_cert; - verify_details.cert_verify_result.is_issued_by_known_root = true; + ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); verify_details.cert_verify_result.public_key_hashes.push_back( test::GetTestHashValue(primary_pin)); crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); @@ -881,16 +928,16 @@ TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithMatchingPinsIfDisabled) { QuicStreamRequest request(&factory_); is_https_ = true; EXPECT_EQ(OK, request.Request(server1, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, server1.host(), "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_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, server2.host(), "GET", + net_log_, callback_.callback())); scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); EXPECT_TRUE(stream2.get()); @@ -899,10 +946,10 @@ TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithMatchingPinsIfDisabled) { 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()); + EXPECT_TRUE(socket_data1.AllReadDataConsumed()); + EXPECT_TRUE(socket_data1.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithDifferentPins) { @@ -924,24 +971,12 @@ TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithDifferentPins) { test::AddPin(&transport_security_state_, "mail.example.org", primary_pin, backup_pin); - // Load a cert that is valid for: - // www.example.org (server1) - // mail.example.org (server2) - base::FilePath certs_dir = GetTestCertsDirectory(); - scoped_refptr<X509Certificate> test_cert( - ImportCertFromFile(certs_dir, "spdy_pooling.pem")); - ASSERT_NE(static_cast<X509Certificate*>(nullptr), test_cert.get()); - - ProofVerifyDetailsChromium verify_details1; - verify_details1.cert_verify_result.verified_cert = test_cert; - verify_details1.cert_verify_result.is_issued_by_known_root = true; + ProofVerifyDetailsChromium verify_details1 = DefaultProofVerifyDetails(); verify_details1.cert_verify_result.public_key_hashes.push_back( test::GetTestHashValue(bad_pin)); crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details1); - ProofVerifyDetailsChromium verify_details2; - verify_details2.cert_verify_result.verified_cert = test_cert; - verify_details2.cert_verify_result.is_issued_by_known_root = true; + ProofVerifyDetailsChromium verify_details2 = DefaultProofVerifyDetails(); verify_details2.cert_verify_result.public_key_hashes.push_back( test::GetTestHashValue(primary_pin)); crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details2); @@ -953,16 +988,16 @@ TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithDifferentPins) { QuicStreamRequest request(&factory_); is_https_ = true; EXPECT_EQ(OK, request.Request(server1, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, server1.host(), "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_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, server2.host(), "GET", + net_log_, callback_.callback())); scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); EXPECT_TRUE(stream2.get()); @@ -971,10 +1006,10 @@ TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithDifferentPins) { 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()); + EXPECT_TRUE(socket_data1.AllReadDataConsumed()); + EXPECT_TRUE(socket_data1.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, Goaway) { @@ -991,8 +1026,8 @@ TEST_P(QuicStreamFactoryTest, Goaway) { QuicStreamRequest request(&factory_); EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); @@ -1006,15 +1041,15 @@ TEST_P(QuicStreamFactoryTest, Goaway) { EXPECT_EQ(true, QuicStreamFactoryPeer::IsLiveSession(&factory_, session)); EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession( &factory_, host_port_pair_, is_https_)); - EXPECT_EQ(nullptr, CreateIfSessionExists(host_port_pair_, net_log_).get()); + EXPECT_FALSE(HasActiveSession(host_port_pair_)); // 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_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); EXPECT_TRUE(stream2.get()); @@ -1030,10 +1065,10 @@ TEST_P(QuicStreamFactoryTest, Goaway) { stream2.reset(); stream.reset(); - 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()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, MaxOpenStream) { @@ -1058,8 +1093,8 @@ TEST_P(QuicStreamFactoryTest, MaxOpenStream) { for (size_t i = 0; i < kDefaultMaxStreamsPerConnection / 2; i++) { QuicStreamRequest request(&factory_); int rv = request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback()); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback()); if (i == 0) { EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_EQ(OK, callback_.WaitForResult()); @@ -1075,8 +1110,8 @@ TEST_P(QuicStreamFactoryTest, MaxOpenStream) { QuicStreamRequest request(&factory_); EXPECT_EQ(OK, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - CompletionCallback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, CompletionCallback())); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); EXPECT_TRUE(stream); EXPECT_EQ(ERR_IO_PENDING, stream->InitializeStream( @@ -1089,8 +1124,8 @@ TEST_P(QuicStreamFactoryTest, MaxOpenStream) { EXPECT_EQ(OK, callback_.WaitForResult()); - EXPECT_TRUE(socket_data.at_read_eof()); - EXPECT_TRUE(socket_data.at_write_eof()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); STLDeleteElements(&streams); } @@ -1103,13 +1138,13 @@ TEST_P(QuicStreamFactoryTest, ResolutionErrorInCreate) { QuicStreamRequest request(&factory_); EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); EXPECT_EQ(ERR_NAME_NOT_RESOLVED, callback_.WaitForResult()); - EXPECT_TRUE(socket_data.at_read_eof()); - EXPECT_TRUE(socket_data.at_write_eof()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, ConnectErrorInCreate) { @@ -1122,13 +1157,13 @@ TEST_P(QuicStreamFactoryTest, ConnectErrorInCreate) { QuicStreamRequest request(&factory_); EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "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()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, CancelCreate) { @@ -1141,21 +1176,20 @@ TEST_P(QuicStreamFactoryTest, CancelCreate) { QuicStreamRequest request(&factory_); EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); } socket_data.StopAfter(1); base::RunLoop run_loop; run_loop.RunUntilIdle(); - scoped_ptr<QuicHttpStream> stream( - CreateIfSessionExists(host_port_pair_, net_log_)); + scoped_ptr<QuicHttpStream> stream(CreateFromSession(host_port_pair_)); EXPECT_TRUE(stream.get()); stream.reset(); - EXPECT_TRUE(socket_data.at_read_eof()); - EXPECT_TRUE(socket_data.at_write_eof()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, CreateConsistentEphemeralPort) { @@ -1205,8 +1239,8 @@ TEST_P(QuicStreamFactoryTest, CloseAllSessions) { QuicStreamRequest request(&factory_); EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); @@ -1226,17 +1260,17 @@ TEST_P(QuicStreamFactoryTest, CloseAllSessions) { QuicStreamRequest request2(&factory_); EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); stream = request2.ReleaseStream(); stream.reset(); // Will reset stream 3. - 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()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, OnIPAddressChanged) { @@ -1262,8 +1296,8 @@ TEST_P(QuicStreamFactoryTest, OnIPAddressChanged) { QuicStreamRequest request(&factory_); EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); @@ -1284,17 +1318,17 @@ TEST_P(QuicStreamFactoryTest, OnIPAddressChanged) { QuicStreamRequest request2(&factory_); EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); stream = request2.ReleaseStream(); stream.reset(); // Will reset stream 3. - 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()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, OnCertAdded) { @@ -1320,8 +1354,8 @@ TEST_P(QuicStreamFactoryTest, OnCertAdded) { QuicStreamRequest request(&factory_); EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); @@ -1342,17 +1376,17 @@ TEST_P(QuicStreamFactoryTest, OnCertAdded) { QuicStreamRequest request2(&factory_); EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); stream = request2.ReleaseStream(); stream.reset(); // Will reset stream 3. - 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()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, OnCACertChanged) { @@ -1378,8 +1412,8 @@ TEST_P(QuicStreamFactoryTest, OnCACertChanged) { QuicStreamRequest request(&factory_); EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); @@ -1400,17 +1434,17 @@ TEST_P(QuicStreamFactoryTest, OnCACertChanged) { QuicStreamRequest request2(&factory_); EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); EXPECT_EQ(OK, callback_.WaitForResult()); stream = request2.ReleaseStream(); stream.reset(); // Will reset stream 3. - 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()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, SharedCryptoConfig) { @@ -1511,8 +1545,8 @@ TEST_P(QuicStreamFactoryTest, RacingConnections) { QuicServerId server_id(host_port_pair_, is_https_, privacy_mode_); EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); EXPECT_EQ(2u, QuicStreamFactoryPeer::GetNumberOfActiveJobs(&factory_, server_id)); @@ -1520,8 +1554,8 @@ TEST_P(QuicStreamFactoryTest, RacingConnections) { scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); EXPECT_TRUE(stream.get()); - EXPECT_TRUE(socket_data.at_read_eof()); - EXPECT_TRUE(socket_data.at_write_eof()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumberOfActiveJobs(&factory_, server_id)); } @@ -1546,8 +1580,8 @@ TEST_P(QuicStreamFactoryTest, EnableNotLoadFromDiskCache) { QuicStreamRequest request(&factory_); EXPECT_EQ(OK, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); // If we are waiting for disk cache, we would have posted a task. Verify that // the CancelWaitForDataReady task hasn't been posted. @@ -1555,8 +1589,8 @@ TEST_P(QuicStreamFactoryTest, EnableNotLoadFromDiskCache) { scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); EXPECT_TRUE(stream.get()); - EXPECT_TRUE(socket_data.at_read_eof()); - EXPECT_TRUE(socket_data.at_write_eof()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, BadPacketLoss) { @@ -1576,16 +1610,21 @@ TEST_P(QuicStreamFactoryTest, BadPacketLoss) { socket_factory_.AddSocketDataProvider(&socket_data); socket_data.StopAfter(1); - DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 0); + DeterministicSocketData socket_data2(nullptr, 0, nullptr, 0); socket_factory_.AddSocketDataProvider(&socket_data2); socket_data2.StopAfter(1); - DeterministicSocketData socket_data3(reads, arraysize(reads), nullptr, 0); + DeterministicSocketData socket_data3(nullptr, 0, nullptr, 0); socket_factory_.AddSocketDataProvider(&socket_data3); socket_data3.StopAfter(1); + DeterministicSocketData socket_data4(nullptr, 0, nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data4); + socket_data4.StopAfter(1); + HostPortPair server2("mail.example.org", kDefaultServerPort); HostPortPair server3("docs.example.org", kDefaultServerPort); + HostPortPair server4("images.example.org", kDefaultServerPort); crypto_client_stream_factory_.set_handshake_mode( MockCryptoClientStream::ZERO_RTT); @@ -1594,11 +1633,12 @@ TEST_P(QuicStreamFactoryTest, BadPacketLoss) { "192.168.0.1", ""); host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", ""); host_resolver_.rules()->AddIPLiteralRule(server3.host(), "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server4.host(), "192.168.0.1", ""); QuicStreamRequest request(&factory_); EXPECT_EQ(OK, request.Request(host_port_pair_, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback_.callback())); + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession( &factory_, host_port_pair_, is_https_); @@ -1617,16 +1657,16 @@ TEST_P(QuicStreamFactoryTest, BadPacketLoss) { &factory_, host_port_pair_.port())); // Set packet_loss_rate to a higher value than packet_loss_threshold only once - // and that should close the session, but shouldn't disable QUIC. - EXPECT_TRUE( + // and that shouldn't close the session and it shouldn't disable QUIC. + EXPECT_FALSE( factory_.OnHandshakeConfirmed(session, /*packet_loss_rate=*/1.0f)); EXPECT_EQ(1, QuicStreamFactoryPeer::GetNumberOfLossyConnections( &factory_, host_port_pair_.port())); + EXPECT_TRUE(session->connection()->connected()); EXPECT_FALSE( QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); - EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession( + EXPECT_TRUE(QuicStreamFactoryPeer::HasActiveSession( &factory_, host_port_pair_, is_https_)); - EXPECT_EQ(nullptr, CreateIfSessionExists(host_port_pair_, net_log_).get()); // Test N-in-a-row high packet loss connections. @@ -1635,8 +1675,8 @@ TEST_P(QuicStreamFactoryTest, BadPacketLoss) { TestCompletionCallback callback2; QuicStreamRequest request2(&factory_); EXPECT_EQ(OK, request2.Request(server2, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback2.callback())); + /*cert_verify_flags=*/0, server2.host(), "GET", + net_log_, callback2.callback())); QuicClientSession* session2 = QuicStreamFactoryPeer::GetActiveSession(&factory_, server2, is_https_); @@ -1652,52 +1692,760 @@ TEST_P(QuicStreamFactoryTest, BadPacketLoss) { QuicStreamFactoryPeer::IsQuicDisabled(&factory_, server2.port())); // Set packet_loss_rate to a higher value than packet_loss_threshold only once - // and that should close the session, but shouldn't disable QUIC. - EXPECT_TRUE( + // and that shouldn't close the session and it shouldn't disable QUIC. + EXPECT_FALSE( factory_.OnHandshakeConfirmed(session2, /*packet_loss_rate=*/1.0f)); EXPECT_EQ(1, QuicStreamFactoryPeer::GetNumberOfLossyConnections( &factory_, server2.port())); - EXPECT_FALSE(session2->connection()->connected()); + EXPECT_TRUE(session2->connection()->connected()); EXPECT_FALSE( QuicStreamFactoryPeer::IsQuicDisabled(&factory_, server2.port())); - EXPECT_FALSE( + EXPECT_TRUE( QuicStreamFactoryPeer::HasActiveSession(&factory_, server2, is_https_)); - EXPECT_EQ(nullptr, CreateIfSessionExists(server2, net_log_).get()); DVLOG(1) << "Create 3rd session which also has packet loss"; TestCompletionCallback callback3; QuicStreamRequest request3(&factory_); EXPECT_EQ(OK, request3.Request(server3, is_https_, privacy_mode_, - /*cert_verify_flags=*/0, "GET", net_log_, - callback3.callback())); + /*cert_verify_flags=*/0, server3.host(), "GET", + net_log_, callback3.callback())); QuicClientSession* session3 = QuicStreamFactoryPeer::GetActiveSession(&factory_, server3, is_https_); + DVLOG(1) << "Create 4th session with packet loss and test IsQuicDisabled()"; + TestCompletionCallback callback4; + QuicStreamRequest request4(&factory_); + EXPECT_EQ(OK, request4.Request(server4, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, server4.host(), "GET", + net_log_, callback4.callback())); + QuicClientSession* session4 = + QuicStreamFactoryPeer::GetActiveSession(&factory_, server4, is_https_); + // Set packet_loss_rate to higher value than packet_loss_threshold 2nd time in // a row and that should close the session and disable QUIC. EXPECT_TRUE( factory_.OnHandshakeConfirmed(session3, /*packet_loss_rate=*/1.0f)); EXPECT_EQ(2, QuicStreamFactoryPeer::GetNumberOfLossyConnections( &factory_, server3.port())); - EXPECT_FALSE(session2->connection()->connected()); + EXPECT_FALSE(session3->connection()->connected()); EXPECT_TRUE(QuicStreamFactoryPeer::IsQuicDisabled(&factory_, server3.port())); EXPECT_FALSE( QuicStreamFactoryPeer::HasActiveSession(&factory_, server3, is_https_)); - EXPECT_EQ(nullptr, CreateIfSessionExists(server3, net_log_).get()); + EXPECT_FALSE(HasActiveSession(server3)); + + // Set packet_loss_rate to higher value than packet_loss_threshold 3rd time in + // a row and IsQuicDisabled() should close the session. + EXPECT_TRUE( + factory_.OnHandshakeConfirmed(session4, /*packet_loss_rate=*/1.0f)); + EXPECT_EQ(3, QuicStreamFactoryPeer::GetNumberOfLossyConnections( + &factory_, server4.port())); + EXPECT_FALSE(session4->connection()->connected()); + EXPECT_TRUE(QuicStreamFactoryPeer::IsQuicDisabled(&factory_, server4.port())); + EXPECT_FALSE( + QuicStreamFactoryPeer::HasActiveSession(&factory_, server4, is_https_)); + EXPECT_FALSE(HasActiveSession(server4)); + + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); + EXPECT_TRUE(stream.get()); + scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); + EXPECT_TRUE(stream2.get()); + scoped_ptr<QuicHttpStream> stream3 = request3.ReleaseStream(); + EXPECT_TRUE(stream3.get()); + scoped_ptr<QuicHttpStream> stream4 = request4.ReleaseStream(); + EXPECT_TRUE(stream4.get()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data3.AllReadDataConsumed()); + EXPECT_TRUE(socket_data3.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data4.AllReadDataConsumed()); + EXPECT_TRUE(socket_data4.AllWriteDataConsumed()); +} + +TEST_P(QuicStreamFactoryTest, PublicResetPostHandshakeTwoOfTwo) { + factory_.set_quic_server_info_factory(&quic_server_info_factory_); + QuicStreamFactoryPeer::SetTaskRunner(&factory_, runner_.get()); + QuicStreamFactoryPeer::SetDisableDiskCache(&factory_, true); + QuicStreamFactoryPeer::SetThresholdPublicResetsPostHandshake(&factory_, 2); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + EXPECT_EQ(0, QuicStreamFactoryPeer::GetNumberOfLossyConnections( + &factory_, host_port_pair_.port())); + + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data); + socket_data.StopAfter(1); + + DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data2); + socket_data2.StopAfter(1); + + HostPortPair server2("mail.example.org", kDefaultServerPort); + + crypto_client_stream_factory_.set_handshake_mode( + MockCryptoClientStream::CONFIRM_HANDSHAKE); + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(), + "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", ""); + + QuicStreamRequest request(&factory_); + EXPECT_EQ(OK, request.Request(host_port_pair_, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); + + QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession( + &factory_, host_port_pair_, is_https_); + + DVLOG(1) << "Created 1st session. Now trigger public reset post handshake"; + session->connection()->CloseConnection(QUIC_PUBLIC_RESET, true); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop; + run_loop.RunUntilIdle(); + + EXPECT_EQ(1, + QuicStreamFactoryPeer::GetNumPublicResetsPostHandshake(&factory_)); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + + // Test two-in-a-row public reset post handshakes.. + DVLOG(1) << "Create 2nd session and trigger public reset post handshake"; + TestCompletionCallback callback2; + QuicStreamRequest request2(&factory_); + EXPECT_EQ(OK, request2.Request(server2, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, server2.host(), "GET", + net_log_, callback2.callback())); + QuicClientSession* session2 = + QuicStreamFactoryPeer::GetActiveSession(&factory_, server2, is_https_); + + session2->connection()->CloseConnection(QUIC_PUBLIC_RESET, true); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop2; + run_loop2.RunUntilIdle(); + EXPECT_EQ(2, + QuicStreamFactoryPeer::GetNumPublicResetsPostHandshake(&factory_)); + EXPECT_TRUE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + EXPECT_EQ(QuicClientSession::QUIC_DISABLED_PUBLIC_RESET_POST_HANDSHAKE, + factory_.QuicDisabledReason(host_port_pair_.port())); + + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); + EXPECT_TRUE(stream.get()); + scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); + EXPECT_TRUE(stream2.get()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); +} + +TEST_P(QuicStreamFactoryTest, TimeoutsWithOpenStreamsTwoOfTwo) { + factory_.set_quic_server_info_factory(&quic_server_info_factory_); + QuicStreamFactoryPeer::SetTaskRunner(&factory_, runner_.get()); + QuicStreamFactoryPeer::SetDisableDiskCache(&factory_, true); + QuicStreamFactoryPeer::SetThresholdTimeoutsWithOpenStreams(&factory_, 2); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + EXPECT_EQ(0, QuicStreamFactoryPeer::GetNumberOfLossyConnections( + &factory_, host_port_pair_.port())); + + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data); + socket_data.StopAfter(1); + + DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data2); + socket_data2.StopAfter(1); + + HostPortPair server2("mail.example.org", kDefaultServerPort); + + crypto_client_stream_factory_.set_handshake_mode( + MockCryptoClientStream::CONFIRM_HANDSHAKE); + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(), + "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", ""); + + QuicStreamRequest request(&factory_); + EXPECT_EQ(OK, request.Request(host_port_pair_, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); + + QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession( + &factory_, host_port_pair_, is_https_); + + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); + EXPECT_TRUE(stream.get()); + HttpRequestInfo request_info; + EXPECT_EQ(OK, stream->InitializeStream(&request_info, DEFAULT_PRIORITY, + net_log_, CompletionCallback())); + + DVLOG(1) + << "Created 1st session and initialized a stream. Now trigger timeout"; + session->connection()->CloseConnection(QUIC_CONNECTION_TIMED_OUT, false); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop; + run_loop.RunUntilIdle(); + + EXPECT_EQ(1, QuicStreamFactoryPeer::GetNumTimeoutsWithOpenStreams(&factory_)); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + + // Test two-in-a-row timeouts with open streams. + DVLOG(1) << "Create 2nd session and timeout with open stream"; + TestCompletionCallback callback2; + QuicStreamRequest request2(&factory_); + EXPECT_EQ(OK, request2.Request(server2, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, server2.host(), "GET", + net_log_, callback2.callback())); + QuicClientSession* session2 = + QuicStreamFactoryPeer::GetActiveSession(&factory_, server2, is_https_); + + scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); + EXPECT_TRUE(stream2.get()); + EXPECT_EQ(OK, stream2->InitializeStream(&request_info, DEFAULT_PRIORITY, + net_log_, CompletionCallback())); + + session2->connection()->CloseConnection(QUIC_CONNECTION_TIMED_OUT, false); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop2; + run_loop2.RunUntilIdle(); + EXPECT_EQ(2, QuicStreamFactoryPeer::GetNumTimeoutsWithOpenStreams(&factory_)); + EXPECT_TRUE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + EXPECT_EQ(QuicClientSession::QUIC_DISABLED_TIMEOUT_WITH_OPEN_STREAMS, + factory_.QuicDisabledReason(host_port_pair_.port())); + + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); +} + +TEST_P(QuicStreamFactoryTest, PublicResetPostHandshakeTwoOfThree) { + factory_.set_quic_server_info_factory(&quic_server_info_factory_); + QuicStreamFactoryPeer::SetTaskRunner(&factory_, runner_.get()); + QuicStreamFactoryPeer::SetDisableDiskCache(&factory_, true); + QuicStreamFactoryPeer::SetThresholdPublicResetsPostHandshake(&factory_, 2); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + EXPECT_EQ(0, QuicStreamFactoryPeer::GetNumberOfLossyConnections( + &factory_, host_port_pair_.port())); + + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data); + socket_data.StopAfter(1); + + DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data2); + socket_data2.StopAfter(1); + + DeterministicSocketData socket_data3(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data3); + socket_data3.StopAfter(1); + + HostPortPair server2("mail.example.org", kDefaultServerPort); + HostPortPair server3("docs.example.org", kDefaultServerPort); + + crypto_client_stream_factory_.set_handshake_mode( + MockCryptoClientStream::CONFIRM_HANDSHAKE); + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(), + "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server3.host(), "192.168.0.1", ""); + + // Test first and third out of three public reset post handshakes. + QuicStreamRequest request(&factory_); + EXPECT_EQ(OK, request.Request(host_port_pair_, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); + + QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession( + &factory_, host_port_pair_, is_https_); + + DVLOG(1) << "Created 1st session. Now trigger public reset post handshake"; + session->connection()->CloseConnection(QUIC_PUBLIC_RESET, true); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop; + run_loop.RunUntilIdle(); + + EXPECT_EQ(1, + QuicStreamFactoryPeer::GetNumPublicResetsPostHandshake(&factory_)); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + + DVLOG(1) << "Create 2nd session without disable trigger"; + TestCompletionCallback callback2; + QuicStreamRequest request2(&factory_); + EXPECT_EQ(OK, request2.Request(server2, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, server2.host(), "GET", + net_log_, callback2.callback())); + QuicClientSession* session2 = + QuicStreamFactoryPeer::GetActiveSession(&factory_, server2, is_https_); + + session2->connection()->CloseConnection(QUIC_NO_ERROR, false); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop2; + run_loop2.RunUntilIdle(); + EXPECT_EQ(1, + QuicStreamFactoryPeer::GetNumPublicResetsPostHandshake(&factory_)); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + + DVLOG(1) << "Create 3rd session with public reset post handshake," + << " will disable QUIC"; + TestCompletionCallback callback3; + QuicStreamRequest request3(&factory_); + EXPECT_EQ(OK, request3.Request(server3, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, server3.host(), "GET", + net_log_, callback3.callback())); + QuicClientSession* session3 = + QuicStreamFactoryPeer::GetActiveSession(&factory_, server3, is_https_); + + session3->connection()->CloseConnection(QUIC_PUBLIC_RESET, true); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop3; + run_loop3.RunUntilIdle(); + EXPECT_EQ(2, + QuicStreamFactoryPeer::GetNumPublicResetsPostHandshake(&factory_)); + EXPECT_TRUE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + EXPECT_EQ(QuicClientSession::QUIC_DISABLED_PUBLIC_RESET_POST_HANDSHAKE, + factory_.QuicDisabledReason(host_port_pair_.port())); + + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); + EXPECT_TRUE(stream.get()); + scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); + EXPECT_TRUE(stream2.get()); + scoped_ptr<QuicHttpStream> stream3 = request3.ReleaseStream(); + EXPECT_TRUE(stream3.get()); + + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data3.AllReadDataConsumed()); + EXPECT_TRUE(socket_data3.AllWriteDataConsumed()); +} + +TEST_P(QuicStreamFactoryTest, TimeoutsWithOpenStreamsTwoOfThree) { + factory_.set_quic_server_info_factory(&quic_server_info_factory_); + QuicStreamFactoryPeer::SetTaskRunner(&factory_, runner_.get()); + QuicStreamFactoryPeer::SetDisableDiskCache(&factory_, true); + QuicStreamFactoryPeer::SetThresholdTimeoutsWithOpenStreams(&factory_, 2); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + EXPECT_EQ(0, QuicStreamFactoryPeer::GetNumberOfLossyConnections( + &factory_, host_port_pair_.port())); + + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data); + socket_data.StopAfter(1); + + // DeterministicSocketData socket_data2(nullptr, 0, nullptr, 0); + DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data2); + socket_data2.StopAfter(1); + + DeterministicSocketData socket_data3(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data3); + socket_data3.StopAfter(1); + + HostPortPair server2("mail.example.org", kDefaultServerPort); + HostPortPair server3("docs.example.org", kDefaultServerPort); + + crypto_client_stream_factory_.set_handshake_mode( + MockCryptoClientStream::CONFIRM_HANDSHAKE); + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(), + "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server3.host(), "192.168.0.1", ""); + + // Test first and third out of three timeouts with open streams. + QuicStreamRequest request(&factory_); + EXPECT_EQ(OK, request.Request(host_port_pair_, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); + + QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession( + &factory_, host_port_pair_, is_https_); scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); EXPECT_TRUE(stream.get()); + HttpRequestInfo request_info; + EXPECT_EQ(OK, stream->InitializeStream(&request_info, DEFAULT_PRIORITY, + net_log_, CompletionCallback())); + + DVLOG(1) + << "Created 1st session and initialized a stream. Now trigger timeout"; + session->connection()->CloseConnection(QUIC_CONNECTION_TIMED_OUT, false); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop; + run_loop.RunUntilIdle(); + + EXPECT_EQ(1, QuicStreamFactoryPeer::GetNumTimeoutsWithOpenStreams(&factory_)); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + + // Test two-in-a-row timeouts with open streams. + DVLOG(1) << "Create 2nd session without timeout"; + TestCompletionCallback callback2; + QuicStreamRequest request2(&factory_); + EXPECT_EQ(OK, request2.Request(server2, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, server2.host(), "GET", + net_log_, callback2.callback())); + QuicClientSession* session2 = + QuicStreamFactoryPeer::GetActiveSession(&factory_, server2, is_https_); + + session2->connection()->CloseConnection(QUIC_NO_ERROR, true); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop2; + run_loop2.RunUntilIdle(); + EXPECT_EQ(1, QuicStreamFactoryPeer::GetNumTimeoutsWithOpenStreams(&factory_)); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + + DVLOG(1) << "Create 3rd session with timeout with open streams," + << " will disable QUIC"; + + TestCompletionCallback callback3; + QuicStreamRequest request3(&factory_); + EXPECT_EQ(OK, request3.Request(server3, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, server3.host(), "GET", + net_log_, callback3.callback())); + QuicClientSession* session3 = + QuicStreamFactoryPeer::GetActiveSession(&factory_, server3, is_https_); + + scoped_ptr<QuicHttpStream> stream3 = request3.ReleaseStream(); + EXPECT_TRUE(stream3.get()); + EXPECT_EQ(OK, stream3->InitializeStream(&request_info, DEFAULT_PRIORITY, + net_log_, CompletionCallback())); + session3->connection()->CloseConnection(QUIC_CONNECTION_TIMED_OUT, false); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop3; + run_loop3.RunUntilIdle(); + EXPECT_EQ(2, QuicStreamFactoryPeer::GetNumTimeoutsWithOpenStreams(&factory_)); + EXPECT_TRUE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + EXPECT_EQ(QuicClientSession::QUIC_DISABLED_TIMEOUT_WITH_OPEN_STREAMS, + factory_.QuicDisabledReason(host_port_pair_.port())); + + scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); + EXPECT_TRUE(stream2.get()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data3.AllReadDataConsumed()); + EXPECT_TRUE(socket_data3.AllWriteDataConsumed()); +} + +TEST_P(QuicStreamFactoryTest, PublicResetPostHandshakeTwoOfFour) { + factory_.set_quic_server_info_factory(&quic_server_info_factory_); + QuicStreamFactoryPeer::SetTaskRunner(&factory_, runner_.get()); + QuicStreamFactoryPeer::SetDisableDiskCache(&factory_, true); + QuicStreamFactoryPeer::SetThresholdPublicResetsPostHandshake(&factory_, 2); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + EXPECT_EQ(0, QuicStreamFactoryPeer::GetNumberOfLossyConnections( + &factory_, host_port_pair_.port())); + + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data); + socket_data.StopAfter(1); + + DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data2); + socket_data2.StopAfter(1); + + DeterministicSocketData socket_data3(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data3); + socket_data3.StopAfter(1); + + DeterministicSocketData socket_data4(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data4); + socket_data4.StopAfter(1); + + HostPortPair server2("mail.example.org", kDefaultServerPort); + HostPortPair server3("docs.example.org", kDefaultServerPort); + HostPortPair server4("images.example.org", kDefaultServerPort); + + crypto_client_stream_factory_.set_handshake_mode( + MockCryptoClientStream::CONFIRM_HANDSHAKE); + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(), + "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server3.host(), "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server4.host(), "192.168.0.1", ""); + + // Test first and fourth out of four public reset post handshakes. + QuicStreamRequest request(&factory_); + EXPECT_EQ(OK, request.Request(host_port_pair_, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); + + QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession( + &factory_, host_port_pair_, is_https_); + + DVLOG(1) << "Created 1st session. Now trigger public reset post handshake"; + session->connection()->CloseConnection(QUIC_PUBLIC_RESET, true); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop; + run_loop.RunUntilIdle(); + + EXPECT_EQ(1, + QuicStreamFactoryPeer::GetNumPublicResetsPostHandshake(&factory_)); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + + DVLOG(1) << "Create 2nd and 3rd sessions without disable trigger"; + TestCompletionCallback callback2; + QuicStreamRequest request2(&factory_); + EXPECT_EQ(OK, request2.Request(server2, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, server2.host(), "GET", + net_log_, callback2.callback())); + QuicClientSession* session2 = + QuicStreamFactoryPeer::GetActiveSession(&factory_, server2, is_https_); + + session2->connection()->CloseConnection(QUIC_NO_ERROR, false); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop2; + run_loop2.RunUntilIdle(); + EXPECT_EQ(1, + QuicStreamFactoryPeer::GetNumPublicResetsPostHandshake(&factory_)); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + + TestCompletionCallback callback3; + QuicStreamRequest request3(&factory_); + EXPECT_EQ(OK, request3.Request(server3, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, server3.host(), "GET", + net_log_, callback3.callback())); + QuicClientSession* session3 = + QuicStreamFactoryPeer::GetActiveSession(&factory_, server3, is_https_); + + session3->connection()->CloseConnection(QUIC_NO_ERROR, false); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop3; + run_loop3.RunUntilIdle(); + EXPECT_EQ(1, + QuicStreamFactoryPeer::GetNumPublicResetsPostHandshake(&factory_)); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + + DVLOG(1) << "Create 4rd session with public reset post handshake," + << " will not disable QUIC"; + TestCompletionCallback callback4; + QuicStreamRequest request4(&factory_); + EXPECT_EQ(OK, request4.Request(server4, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, server4.host(), "GET", + net_log_, callback4.callback())); + QuicClientSession* session4 = + QuicStreamFactoryPeer::GetActiveSession(&factory_, server4, is_https_); + + session4->connection()->CloseConnection(QUIC_PUBLIC_RESET, true); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop4; + run_loop4.RunUntilIdle(); + EXPECT_EQ(1, + QuicStreamFactoryPeer::GetNumPublicResetsPostHandshake(&factory_)); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); + EXPECT_TRUE(stream.get()); + scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); + EXPECT_TRUE(stream2.get()); + scoped_ptr<QuicHttpStream> stream3 = request3.ReleaseStream(); + EXPECT_TRUE(stream3.get()); + scoped_ptr<QuicHttpStream> stream4 = request4.ReleaseStream(); + EXPECT_TRUE(stream4.get()); + + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data3.AllReadDataConsumed()); + EXPECT_TRUE(socket_data3.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data4.AllReadDataConsumed()); + EXPECT_TRUE(socket_data4.AllWriteDataConsumed()); +} + +TEST_P(QuicStreamFactoryTest, TimeoutsWithOpenStreamsTwoOfFour) { + factory_.set_quic_server_info_factory(&quic_server_info_factory_); + QuicStreamFactoryPeer::SetTaskRunner(&factory_, runner_.get()); + QuicStreamFactoryPeer::SetDisableDiskCache(&factory_, true); + QuicStreamFactoryPeer::SetThresholdTimeoutsWithOpenStreams(&factory_, 2); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + EXPECT_EQ(0, QuicStreamFactoryPeer::GetNumberOfLossyConnections( + &factory_, host_port_pair_.port())); + + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data); + socket_data.StopAfter(1); + + // DeterministicSocketData socket_data2(nullptr, 0, nullptr, 0); + DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data2); + socket_data2.StopAfter(1); + + DeterministicSocketData socket_data3(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data3); + socket_data3.StopAfter(1); + + DeterministicSocketData socket_data4(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data4); + socket_data4.StopAfter(1); + + HostPortPair server2("mail.example.org", kDefaultServerPort); + HostPortPair server3("docs.example.org", kDefaultServerPort); + HostPortPair server4("images.example.org", kDefaultServerPort); + + crypto_client_stream_factory_.set_handshake_mode( + MockCryptoClientStream::CONFIRM_HANDSHAKE); + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(), + "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server3.host(), "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server4.host(), "192.168.0.1", ""); + + // Test first and fourth out of three timeouts with open streams. + QuicStreamRequest request(&factory_); + EXPECT_EQ(OK, request.Request(host_port_pair_, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, host_port_pair_.host(), + "GET", net_log_, callback_.callback())); + + QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession( + &factory_, host_port_pair_, is_https_); + + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); + EXPECT_TRUE(stream.get()); + HttpRequestInfo request_info; + EXPECT_EQ(OK, stream->InitializeStream(&request_info, DEFAULT_PRIORITY, + net_log_, CompletionCallback())); + + DVLOG(1) + << "Created 1st session and initialized a stream. Now trigger timeout"; + session->connection()->CloseConnection(QUIC_CONNECTION_TIMED_OUT, false); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop; + run_loop.RunUntilIdle(); + + EXPECT_EQ(1, QuicStreamFactoryPeer::GetNumTimeoutsWithOpenStreams(&factory_)); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + + DVLOG(1) << "Create 2nd and 3rd sessions without timeout"; + TestCompletionCallback callback2; + QuicStreamRequest request2(&factory_); + EXPECT_EQ(OK, request2.Request(server2, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, server2.host(), "GET", + net_log_, callback2.callback())); + QuicClientSession* session2 = + QuicStreamFactoryPeer::GetActiveSession(&factory_, server2, is_https_); + + session2->connection()->CloseConnection(QUIC_NO_ERROR, true); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop2; + run_loop2.RunUntilIdle(); + EXPECT_EQ(1, QuicStreamFactoryPeer::GetNumTimeoutsWithOpenStreams(&factory_)); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + + TestCompletionCallback callback3; + QuicStreamRequest request3(&factory_); + EXPECT_EQ(OK, request3.Request(server3, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, server3.host(), "GET", + net_log_, callback3.callback())); + QuicClientSession* session3 = + QuicStreamFactoryPeer::GetActiveSession(&factory_, server3, is_https_); + + session3->connection()->CloseConnection(QUIC_NO_ERROR, true); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop3; + run_loop3.RunUntilIdle(); + EXPECT_EQ(1, QuicStreamFactoryPeer::GetNumTimeoutsWithOpenStreams(&factory_)); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + + DVLOG(1) << "Create 4th session with timeout with open streams," + << " will not disable QUIC"; + + TestCompletionCallback callback4; + QuicStreamRequest request4(&factory_); + EXPECT_EQ(OK, request4.Request(server4, is_https_, privacy_mode_, + /*cert_verify_flags=*/0, server4.host(), "GET", + net_log_, callback4.callback())); + QuicClientSession* session4 = + QuicStreamFactoryPeer::GetActiveSession(&factory_, server4, is_https_); + + scoped_ptr<QuicHttpStream> stream4 = request4.ReleaseStream(); + EXPECT_TRUE(stream4.get()); + EXPECT_EQ(OK, stream4->InitializeStream(&request_info, DEFAULT_PRIORITY, + net_log_, CompletionCallback())); + session4->connection()->CloseConnection(QUIC_CONNECTION_TIMED_OUT, false); + // Need to spin the loop now to ensure that + // QuicStreamFactory::OnSessionClosed() runs. + base::RunLoop run_loop4; + run_loop4.RunUntilIdle(); + EXPECT_EQ(1, QuicStreamFactoryPeer::GetNumTimeoutsWithOpenStreams(&factory_)); + EXPECT_FALSE( + QuicStreamFactoryPeer::IsQuicDisabled(&factory_, host_port_pair_.port())); + scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); EXPECT_TRUE(stream2.get()); scoped_ptr<QuicHttpStream> stream3 = request3.ReleaseStream(); EXPECT_TRUE(stream3.get()); - 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()); - EXPECT_TRUE(socket_data3.at_read_eof()); - EXPECT_TRUE(socket_data3.at_write_eof()); + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data3.AllReadDataConsumed()); + EXPECT_TRUE(socket_data3.AllWriteDataConsumed()); + EXPECT_TRUE(socket_data4.AllReadDataConsumed()); + EXPECT_TRUE(socket_data4.AllWriteDataConsumed()); } } // namespace test diff --git a/chromium/net/quic/quic_stream_sequencer.cc b/chromium/net/quic/quic_stream_sequencer.cc index 6ca6352f4d5..b9b7ad32700 100644 --- a/chromium/net/quic/quic_stream_sequencer.cc +++ b/chromium/net/quic/quic_stream_sequencer.cc @@ -45,8 +45,8 @@ void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { return; } - QuicStreamOffset byte_offset = frame.offset; - size_t data_len = frame.data.TotalBufferSize(); + const QuicStreamOffset byte_offset = frame.offset; + const size_t data_len = frame.data.length(); if (data_len == 0 && !frame.fin) { // Stream frames must have data or a fin flag. stream_->CloseConnectionWithDetails(QUIC_INVALID_STREAM_FRAME, @@ -61,23 +61,17 @@ void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { } } - IOVector data; - data.AppendIovec(frame.data.iovec(), frame.data.Size()); - if (byte_offset > num_bytes_consumed_) { ++num_early_frames_received_; } // If the frame has arrived in-order then we can process it immediately, only // buffering if the stream is unable to process it. + size_t bytes_consumed = 0; 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) { - bytes_consumed += stream_->ProcessRawData( - static_cast<char*>(data.iovec()[i].iov_base), - data.iovec()[i].iov_len); - } + bytes_consumed = + stream_->ProcessRawData(frame.data.data(), frame.data.length()); num_bytes_consumed_ += bytes_consumed; stream_->AddBytesConsumed(bytes_consumed); @@ -90,24 +84,18 @@ void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { } else if (bytes_consumed == data_len) { FlushBufferedFrames(); return; // it's safe to ack this frame. - } else { - // 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) { + if (bytes_consumed < data_len) { DVLOG(1) << "Buffering stream data at offset " << byte_offset; - const iovec& iov = data.iovec()[i]; + const size_t remaining_length = data_len - bytes_consumed; buffered_frames_.insert(std::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; + byte_offset + bytes_consumed, + string(frame.data.data() + bytes_consumed, remaining_length))); + num_bytes_buffered_ += remaining_length; } - return; } void QuicStreamSequencer::CloseStreamAtOffset(QuicStreamOffset offset) { @@ -140,9 +128,9 @@ bool QuicStreamSequencer::MaybeCloseStream() { return false; } -int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) { +int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) const { DCHECK(!blocked_); - FrameMap::iterator it = buffered_frames_.begin(); + FrameMap::const_iterator it = buffered_frames_.begin(); size_t index = 0; QuicStreamOffset offset = num_bytes_consumed_; while (it != buffered_frames_.end() && index < iov_len) { @@ -227,9 +215,9 @@ bool QuicStreamSequencer::FrameOverlapsBufferedData( // 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) { + (frame.offset + frame.data.size()) > next_frame->first) { DVLOG(1) << "New frame overlaps next frame: " << frame.offset << " + " - << frame.data.TotalBufferSize() << " > " << next_frame->first; + << frame.data.size() << " > " << next_frame->first; return true; } @@ -248,6 +236,37 @@ bool QuicStreamSequencer::FrameOverlapsBufferedData( return false; } +void QuicStreamSequencer::MarkConsumed(size_t num_bytes_consumed) { + DCHECK(!blocked_); + size_t end_offset = num_bytes_consumed_ + num_bytes_consumed; + while (!buffered_frames_.empty() && end_offset != num_bytes_consumed_) { + FrameMap::iterator it = buffered_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(); + num_bytes_buffered_ -= it->second.length(); + // This chunk is entirely consumed. + buffered_frames_.erase(it); + continue; + } + + // Partially consume this frame. + size_t delta = end_offset - it->first; + RecordBytesConsumed(delta); + buffered_frames_.insert(make_pair(end_offset, it->second.substr(delta))); + buffered_frames_.erase(it); + break; + } +} + 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. diff --git a/chromium/net/quic/quic_stream_sequencer.h b/chromium/net/quic/quic_stream_sequencer.h index 3570d3b86cc..87eff0d8aa9 100644 --- a/chromium/net/quic/quic_stream_sequencer.h +++ b/chromium/net/quic/quic_stream_sequencer.h @@ -40,25 +40,22 @@ class NET_EXPORT_PRIVATE QuicStreamSequencer { // Fills in up to iov_len iovecs with the next readable regions. Returns the // number of iovs used. Non-destructive of the underlying data. - int GetReadableRegions(iovec* iov, size_t iov_len); + int GetReadableRegions(iovec* iov, size_t iov_len) const; // Copies the data into the iov_len buffers provided. Returns the number of // 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; // Returns true if the sequencer has delivered the fin. bool IsClosed() const; - // 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(); @@ -80,6 +77,13 @@ class NET_EXPORT_PRIVATE QuicStreamSequencer { private: friend class test::QuicStreamSequencerPeer; + // Returns true if |frame| contains data which overlaps buffered data + // (indicating an invalid stream frame has been received). + bool FrameOverlapsBufferedData(const QuicStreamFrame& frame) const; + + // Returns true if the sequencer has received this frame before. + bool IsDuplicate(const QuicStreamFrame& frame) const; + // Wait until we've seen 'offset' bytes, and then terminate the stream. void CloseStreamAtOffset(QuicStreamOffset offset); @@ -104,7 +108,7 @@ class NET_EXPORT_PRIVATE QuicStreamSequencer { // bytes, and gaps. typedef std::map<QuicStreamOffset, std::string> FrameMap; - // Stores buffered frames (maps from sequence number -> frame data as string). + // Stores buffered frames (maps from byte offset -> frame data as string). FrameMap buffered_frames_; // The offset, if any, we got a stream termination for. When this many bytes diff --git a/chromium/net/quic/quic_stream_sequencer_test.cc b/chromium/net/quic/quic_stream_sequencer_test.cc index e15f3d3e67d..ed0e780b5cd 100644 --- a/chromium/net/quic/quic_stream_sequencer_test.cc +++ b/chromium/net/quic/quic_stream_sequencer_test.cc @@ -64,9 +64,31 @@ class QuicStreamSequencerTest : public ::testing::Test { : connection_(new MockConnection(Perspective::IS_CLIENT)), session_(connection_), stream_(&session_, 1), - sequencer_(new QuicStreamSequencer(&stream_)), - buffered_frames_( - QuicStreamSequencerPeer::GetBufferedFrames(sequencer_.get())) {} + sequencer_(new QuicStreamSequencer(&stream_)) {} + + bool VerifyReadableRegions(const char** expected, size_t num_expected) { + iovec iovecs[5]; + size_t num_iovecs = + sequencer_->GetReadableRegions(iovecs, arraysize(iovecs)); + return VerifyIovecs(iovecs, num_iovecs, expected, num_expected); + } + + bool VerifyIovecs(iovec* iovecs, + size_t num_iovecs, + const char** expected, + size_t num_expected) { + if (num_expected != num_iovecs) { + LOG(ERROR) << "Incorrect number of iovecs. Expected: " << num_expected + << " Actual: " << num_iovecs; + return false; + } + for (size_t i = 0; i < num_expected; ++i) { + if (!VerifyIovec(iovecs[i], expected[i])) { + return false; + } + } + return true; + } bool VerifyIovec(const iovec& iovec, StringPiece expected) { if (iovec.iov_len != expected.length()) { @@ -86,7 +108,7 @@ class QuicStreamSequencerTest : public ::testing::Test { QuicStreamFrame frame; frame.stream_id = 1; frame.offset = byte_offset; - frame.data.Append(const_cast<char*>(data), strlen(data)); + frame.data = StringPiece(data); frame.fin = true; sequencer_->OnStreamFrame(frame); } @@ -95,47 +117,55 @@ class QuicStreamSequencerTest : public ::testing::Test { QuicStreamFrame frame; frame.stream_id = 1; frame.offset = byte_offset; - frame.data.Append(const_cast<char*>(data), strlen(data)); + frame.data = StringPiece(data); frame.fin = false; sequencer_->OnStreamFrame(frame); } + size_t NumBufferedFrames() { + return QuicStreamSequencerPeer::GetNumBufferedFrames(sequencer_.get()); + } + + bool FrameOverlapsBufferedData(const QuicStreamFrame& frame) { + return QuicStreamSequencerPeer::FrameOverlapsBufferedData(sequencer_.get(), + frame); + } + MockConnection* connection_; - MockSession session_; + MockQuicSpdySession session_; testing::StrictMock<MockStream> stream_; scoped_ptr<QuicStreamSequencer> sequencer_; - map<QuicStreamOffset, string>* buffered_frames_; }; TEST_F(QuicStreamSequencerTest, RejectOldFrame) { EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3)); OnFrame(0, "abc"); - EXPECT_EQ(0u, buffered_frames_->size()); + EXPECT_EQ(0u, NumBufferedFrames()); EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); // Ignore this - it matches a past sequence number and we should not see it // again. OnFrame(0, "def"); - EXPECT_EQ(0u, buffered_frames_->size()); + EXPECT_EQ(0u, NumBufferedFrames()); } TEST_F(QuicStreamSequencerTest, RejectBufferedFrame) { EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)); OnFrame(0, "abc"); - EXPECT_EQ(1u, buffered_frames_->size()); + EXPECT_EQ(1u, NumBufferedFrames()); 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. OnFrame(0, "def"); - EXPECT_EQ(1u, buffered_frames_->size()); + EXPECT_EQ(1u, NumBufferedFrames()); } TEST_F(QuicStreamSequencerTest, FullFrameConsumed) { EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3)); OnFrame(0, "abc"); - EXPECT_EQ(0u, buffered_frames_->size()); + EXPECT_EQ(0u, NumBufferedFrames()); EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); } @@ -143,12 +173,12 @@ TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameConsumed) { sequencer_->SetBlockedUntilFlush(); OnFrame(0, "abc"); - EXPECT_EQ(1u, buffered_frames_->size()); + EXPECT_EQ(1u, NumBufferedFrames()); 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(0u, NumBufferedFrames()); EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(3)); @@ -160,13 +190,13 @@ TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameAndFinConsumed) { sequencer_->SetBlockedUntilFlush(); OnFinFrame(0, "abc"); - EXPECT_EQ(1u, buffered_frames_->size()); + EXPECT_EQ(1u, NumBufferedFrames()); 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(0u, NumBufferedFrames()); EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); } @@ -174,14 +204,14 @@ TEST_F(QuicStreamSequencerTest, EmptyFrame) { EXPECT_CALL(stream_, CloseConnectionWithDetails(QUIC_INVALID_STREAM_FRAME, _)); OnFrame(0, ""); - EXPECT_EQ(0u, buffered_frames_->size()); + EXPECT_EQ(0u, NumBufferedFrames()); EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); } TEST_F(QuicStreamSequencerTest, EmptyFinFrame) { EXPECT_CALL(stream_, OnFinRead()); OnFinFrame(0, ""); - EXPECT_EQ(0u, buffered_frames_->size()); + EXPECT_EQ(0u, NumBufferedFrames()); EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); } @@ -189,38 +219,35 @@ TEST_F(QuicStreamSequencerTest, PartialFrameConsumed) { EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(2)); OnFrame(0, "abc"); - EXPECT_EQ(1u, buffered_frames_->size()); + EXPECT_EQ(1u, NumBufferedFrames()); EXPECT_EQ(2u, sequencer_->num_bytes_consumed()); - EXPECT_EQ("c", buffered_frames_->find(2)->second); } TEST_F(QuicStreamSequencerTest, NextxFrameNotConsumed) { EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0)); OnFrame(0, "abc"); - EXPECT_EQ(1u, buffered_frames_->size()); + EXPECT_EQ(1u, NumBufferedFrames()); EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); - EXPECT_EQ("abc", buffered_frames_->find(0)->second); EXPECT_EQ(0, sequencer_->num_early_frames_received()); } TEST_F(QuicStreamSequencerTest, FutureFrameNotProcessed) { OnFrame(3, "abc"); - EXPECT_EQ(1u, buffered_frames_->size()); + EXPECT_EQ(1u, NumBufferedFrames()); EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); - EXPECT_EQ("abc", buffered_frames_->find(3)->second); EXPECT_EQ(1, sequencer_->num_early_frames_received()); } TEST_F(QuicStreamSequencerTest, OutOfOrderFrameProcessed) { // Buffer the first OnFrame(6, "ghi"); - EXPECT_EQ(1u, buffered_frames_->size()); + EXPECT_EQ(1u, NumBufferedFrames()); EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); EXPECT_EQ(3u, sequencer_->num_bytes_buffered()); // Buffer the second OnFrame(3, "def"); - EXPECT_EQ(2u, buffered_frames_->size()); + EXPECT_EQ(2u, NumBufferedFrames()); EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); EXPECT_EQ(6u, sequencer_->num_bytes_buffered()); @@ -234,7 +261,7 @@ TEST_F(QuicStreamSequencerTest, OutOfOrderFrameProcessed) { EXPECT_EQ(9u, sequencer_->num_bytes_consumed()); EXPECT_EQ(0u, sequencer_->num_bytes_buffered()); - EXPECT_EQ(0u, buffered_frames_->size()); + EXPECT_EQ(0u, NumBufferedFrames()); } TEST_F(QuicStreamSequencerTest, BasicHalfCloseOrdered) { @@ -358,61 +385,126 @@ TEST_F(QuicSequencerRandomTest, RandomFramesNoDroppingNoBackup) { } } +// Same as above, just using a different method for reading. +TEST_F(QuicStreamSequencerTest, MarkConsumed) { + InSequence s; + EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0)); + + OnFrame(0, "abc"); + OnFrame(3, "def"); + OnFrame(6, "ghi"); + + // abcdefghi buffered. + EXPECT_EQ(9u, sequencer_->num_bytes_buffered()); + + // 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))); + EXPECT_EQ(8u, sequencer_->num_bytes_buffered()); + + // Consume 2 bytes. + sequencer_->MarkConsumed(2); + // Verify data. + const char* expected3[] = {"def", "ghi"}; + ASSERT_TRUE(VerifyReadableRegions(expected3, arraysize(expected3))); + EXPECT_EQ(6u, sequencer_->num_bytes_buffered()); + + // Consume 5 bytes. + sequencer_->MarkConsumed(5); + // Verify data. + const char* expected4[] = {"i"}; + ASSERT_TRUE(VerifyReadableRegions(expected4, arraysize(expected4))); + EXPECT_EQ(1u, sequencer_->num_bytes_buffered()); +} + +TEST_F(QuicStreamSequencerTest, MarkConsumedError) { + EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0)); + + OnFrame(0, "abc"); + 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)); + + OnFrame(0, "abc"); + OnFrame(3, "def"); + // Missing packet: 6, ghi. + OnFrame(9, "jkl"); + + const char* expected[] = {"abc", "def"}; + ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected))); + + sequencer_->MarkConsumed(6); +} + 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, '.')); + string data(kNewDataLength, '.'); // No overlap if no buffered frames. - EXPECT_TRUE(buffered_frames_->empty()); - EXPECT_FALSE(sequencer_->FrameOverlapsBufferedData( - QuicStreamFrame(1, false, kBufferedOffset - 1, data))); - + EXPECT_EQ(0u, NumBufferedFrames()); // Add a buffered frame. - buffered_frames->insert( - std::make_pair(kBufferedOffset, string(kBufferedDataLength, '.'))); + sequencer_->OnStreamFrame(QuicStreamFrame(1, false, kBufferedOffset, + string(kBufferedDataLength, '.'))); // New byte range partially overlaps with buffered frame, start offset - // preceeding buffered frame. - EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData( + // preceding buffered frame. + EXPECT_TRUE(FrameOverlapsBufferedData( QuicStreamFrame(1, false, kBufferedOffset - 1, data))); - EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData( + EXPECT_TRUE(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( + // New byte range partially overlaps with buffered frame, start offset inside + // existing buffered frame. + EXPECT_TRUE(FrameOverlapsBufferedData( QuicStreamFrame(1, false, kBufferedOffset + 1, data))); - EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(QuicStreamFrame( + EXPECT_TRUE(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( + EXPECT_FALSE(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))); + EXPECT_FALSE(FrameOverlapsBufferedData( + QuicStreamFrame(1, false, kBufferedOffset + kBufferedDataLength, data))); } 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. - QuicStreamFrame frame1(kClientDataStreamId1, false, 1, MakeIOVector("hello")); + QuicStreamFrame frame1(kClientDataStreamId1, false, 1, StringPiece("hello")); sequencer_->OnStreamFrame(frame1); - QuicStreamFrame frame2(kClientDataStreamId1, false, 2, MakeIOVector("hello")); - EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(frame2)); + QuicStreamFrame frame2(kClientDataStreamId1, false, 2, StringPiece("hello")); + EXPECT_TRUE(FrameOverlapsBufferedData(frame2)); EXPECT_CALL(stream_, CloseConnectionWithDetails(QUIC_INVALID_STREAM_FRAME, _)) .Times(1); sequencer_->OnStreamFrame(frame2); diff --git a/chromium/net/quic/quic_time.cc b/chromium/net/quic/quic_time.cc index 6d3155746fe..7da849ebc01 100644 --- a/chromium/net/quic/quic_time.cc +++ b/chromium/net/quic/quic_time.cc @@ -4,12 +4,14 @@ #include "net/quic/quic_time.h" +#include <stdint.h> + #include "base/logging.h" namespace net { // Highest number of microseconds that DateTimeOffset can hold. -const int64 kQuicInfiniteTimeUs = GG_INT64_C(0x7fffffffffffffff) / 10; +const int64 kQuicInfiniteTimeUs = INT64_C(0x7fffffffffffffff) / 10; QuicTime::Delta::Delta(base::TimeDelta delta) : delta_(delta) { diff --git a/chromium/net/quic/quic_unacked_packet_map.cc b/chromium/net/quic/quic_unacked_packet_map.cc index 87a733bbd3a..fb5a892af06 100644 --- a/chromium/net/quic/quic_unacked_packet_map.cc +++ b/chromium/net/quic/quic_unacked_packet_map.cc @@ -6,19 +6,23 @@ #include "base/logging.h" #include "base/stl_util.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::max; namespace net { -QuicUnackedPacketMap::QuicUnackedPacketMap() +QuicUnackedPacketMap::QuicUnackedPacketMap( + AckNotifierManager* ack_notifier_manager) : largest_sent_packet_(0), largest_observed_(0), least_unacked_(1), bytes_in_flight_(0), - pending_crypto_packet_count_(0) { + pending_crypto_packet_count_(0), + ack_notifier_manager_(ack_notifier_manager) { } QuicUnackedPacketMap::~QuicUnackedPacketMap() { @@ -72,11 +76,15 @@ void QuicUnackedPacketMap::AddSentPacket( void QuicUnackedPacketMap::RemoveObsoletePackets() { while (!unacked_packets_.empty()) { - if (!IsPacketRemovable(least_unacked_, unacked_packets_.front())) { + if (FLAGS_quic_use_is_useless_packet && + !IsPacketUseless(least_unacked_, unacked_packets_.front())) { break; } - unacked_packets_.pop_front(); - ++least_unacked_; + if (!FLAGS_quic_use_is_useless_packet && + !IsPacketRemovable(least_unacked_, unacked_packets_.front())) { + break; + } + PopLeastUnacked(); } } @@ -162,8 +170,7 @@ void QuicUnackedPacketMap::ClearAllPreviousRetransmissions() { } } } - unacked_packets_.pop_front(); - ++least_unacked_; + PopLeastUnacked(); } } @@ -273,7 +280,7 @@ bool QuicUnackedPacketMap::IsPacketRemovable( QuicPacketSequenceNumber sequence_number, const TransmissionInfo& info) const { return (!IsPacketUsefulForMeasuringRtt(sequence_number, info) || - unacked_packets_.size() > kMaxTcpCongestionWindow) && + unacked_packets_.size() > 200) && !IsPacketUsefulForCongestionControl(info) && !IsPacketUsefulForRetransmittableData(info); } @@ -302,10 +309,6 @@ void QuicUnackedPacketMap::RemoveFromInFlight( void QuicUnackedPacketMap::CancelRetransmissionsForStream( QuicStreamId stream_id) { - if (stream_id == kCryptoStreamId || stream_id == kHeadersStreamId) { - LOG(DFATAL) << "Special streams must always retransmit data: " << stream_id; - return; - } QuicPacketSequenceNumber sequence_number = least_unacked_; for (UnackedPacketMap::const_iterator it = unacked_packets_.begin(); it != unacked_packets_.end(); ++it, ++sequence_number) { @@ -391,4 +394,11 @@ QuicPacketSequenceNumber QuicUnackedPacketMap::GetLeastUnacked() const { return least_unacked_; } +void QuicUnackedPacketMap::PopLeastUnacked() { + ack_notifier_manager_->OnPacketRemoved(least_unacked_); + + unacked_packets_.pop_front(); + ++least_unacked_; +} + } // namespace net diff --git a/chromium/net/quic/quic_unacked_packet_map.h b/chromium/net/quic/quic_unacked_packet_map.h index 24abfeff11d..106c4cbcf8b 100644 --- a/chromium/net/quic/quic_unacked_packet_map.h +++ b/chromium/net/quic/quic_unacked_packet_map.h @@ -11,13 +11,18 @@ namespace net { +class AckNotifierManager; + // 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(); + // Initialize an instance of UnackedPacketMap. The AckNotifierManager + // provided to the constructor will be notified whenever a packet is removed + // from the map. + explicit QuicUnackedPacketMap(AckNotifierManager* ack_notifier_manager); ~QuicUnackedPacketMap(); // Adds |serialized_packet| to the map and marks it as sent at |sent_time|. @@ -158,6 +163,9 @@ class NET_EXPORT_PRIVATE QuicUnackedPacketMap { bool IsPacketRemovable(QuicPacketSequenceNumber sequence_number, const TransmissionInfo& info) const; + // Removes the packet with lowest sequence number from the map. + void PopLeastUnacked(); + QuicPacketSequenceNumber largest_sent_packet_; QuicPacketSequenceNumber largest_observed_; @@ -177,6 +185,10 @@ class NET_EXPORT_PRIVATE QuicUnackedPacketMap { // Number of retransmittable crypto handshake packets. size_t pending_crypto_packet_count_; + // Notifier manager for ACK packets. We notify it every time we abandon a + // packet. + AckNotifierManager* ack_notifier_manager_; + DISALLOW_COPY_AND_ASSIGN(QuicUnackedPacketMap); }; diff --git a/chromium/net/quic/quic_unacked_packet_map_test.cc b/chromium/net/quic/quic_unacked_packet_map_test.cc index 150bebca4c2..f99e8b1ffba 100644 --- a/chromium/net/quic/quic_unacked_packet_map_test.cc +++ b/chromium/net/quic/quic_unacked_packet_map_test.cc @@ -4,6 +4,9 @@ #include "net/quic/quic_unacked_packet_map.h" +#include "net/quic/quic_ack_notifier_manager.h" +#include "net/quic/quic_flags.h" +#include "net/quic/quic_utils.h" #include "net/quic/test_tools/quic_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" @@ -21,8 +24,8 @@ const uint32 kDefaultLength = 1000; class QuicUnackedPacketMapTest : public ::testing::Test { protected: QuicUnackedPacketMapTest() - : now_(QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(1000))) { - } + : unacked_packets_(&ack_notifier_manager_), + now_(QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(1000))) {} ~QuicUnackedPacketMapTest() override { STLDeleteElements(&packets_); @@ -43,7 +46,7 @@ class QuicUnackedPacketMapTest : public ::testing::Test { RetransmittableFrames* frames = new RetransmittableFrames(ENCRYPTION_NONE); QuicStreamFrame* frame = new QuicStreamFrame(); frame->stream_id = stream_id; - frames->AddStreamFrame(frame); + frames->AddFrame(QuicFrame(frame)); return SerializedPacket(sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER, packets_.back(), 0, frames); } @@ -115,6 +118,7 @@ class QuicUnackedPacketMapTest : public ::testing::Test { } } vector<QuicEncryptedPacket*> packets_; + AckNotifierManager ack_notifier_manager_; QuicUnackedPacketMap unacked_packets_; QuicTime now_; }; @@ -137,6 +141,7 @@ TEST_F(QuicUnackedPacketMapTest, RttOnly) { } TEST_F(QuicUnackedPacketMapTest, DiscardOldRttOnly) { + ValueRestore<bool> old_flag(&FLAGS_quic_use_is_useless_packet, false); // Acks are only tracked for RTT measurement purposes, and are discarded // when more than 200 accumulate. const size_t kNumUnackedPackets = 200; @@ -440,7 +445,6 @@ TEST_F(QuicUnackedPacketMapTest, SendWithGap) { EXPECT_EQ(5u, unacked_packets_.largest_sent_packet()); } - } // namespace } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_utils.cc b/chromium/net/quic/quic_utils.cc index 9f8b99371d6..32f8c09ce4c 100644 --- a/chromium/net/quic/quic_utils.cc +++ b/chromium/net/quic/quic_utils.cc @@ -5,6 +5,7 @@ #include "net/quic/quic_utils.h" #include <ctype.h> +#include <stdint.h> #include <algorithm> #include <vector> @@ -12,7 +13,6 @@ #include "base/basictypes.h" #include "base/containers/adapters.h" #include "base/logging.h" -#include "base/port.h" #include "base/strings/stringprintf.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" @@ -25,8 +25,8 @@ namespace net { // static uint64 QuicUtils::FNV1a_64_Hash(const char* data, int len) { - static const uint64 kOffset = GG_UINT64_C(14695981039346656037); - static const uint64 kPrime = GG_UINT64_C(1099511628211); + static const uint64 kOffset = UINT64_C(14695981039346656037); + static const uint64 kPrime = UINT64_C(1099511628211); const uint8* octets = reinterpret_cast<const uint8*>(data); @@ -53,8 +53,8 @@ uint128 QuicUtils::FNV1a_128_Hash_Two(const char* data1, // The two constants are defined as part of the hash algorithm. // see http://www.isthe.com/chongo/tech/comp/fnv/ // 144066263297769815596495629667062367629 - const uint128 kOffset(GG_UINT64_C(7809847782465536322), - GG_UINT64_C(7113472399480571277)); + const uint128 kOffset(UINT64_C(7809847782465536322), + UINT64_C(7113472399480571277)); uint128 hash = IncrementalHash(kOffset, data1, len1); if (data2 == nullptr) { @@ -228,6 +228,9 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS); RETURN_STRING_LITERAL(QUIC_CONNECTION_CANCELLED); RETURN_STRING_LITERAL(QUIC_BAD_PACKET_LOSS_RATE); + RETURN_STRING_LITERAL(QUIC_PUBLIC_RESETS_POST_HANDSHAKE); + RETURN_STRING_LITERAL(QUIC_TIMEOUTS_WITH_OPEN_STREAMS); + RETURN_STRING_LITERAL(QUIC_FAILED_TO_SERIALIZE_PACKET); RETURN_STRING_LITERAL(QUIC_LAST_ERROR); // Intentionally have no default case, so we'll break the build // if we add errors and don't put them here. @@ -292,11 +295,11 @@ string QuicUtils::TagToString(QuicTag tag) { QuicTagVector QuicUtils::ParseQuicConnectionOptions( const std::string& connection_options) { QuicTagVector options; - std::vector<std::string> tokens; - base::SplitString(connection_options, ',', &tokens); // Tokens are expected to be no more than 4 characters long, but we // handle overflow gracefully. - for (const std::string& token : tokens) { + for (const base::StringPiece& token : + base::SplitStringPiece(connection_options, ",", base::TRIM_WHITESPACE, + base::SPLIT_WANT_ALL)) { uint32 option = 0; for (char token_char : base::Reversed(token)) { option <<= 8; diff --git a/chromium/net/quic/quic_utils.h b/chromium/net/quic/quic_utils.h index 46652f20103..fb379e66e71 100644 --- a/chromium/net/quic/quic_utils.h +++ b/chromium/net/quic/quic_utils.h @@ -9,6 +9,7 @@ #include <string> +#include "base/strings/string_piece.h" #include "net/base/int128.h" #include "net/base/net_export.h" #include "net/quic/quic_protocol.h" @@ -100,11 +101,13 @@ class NET_EXPORT_PRIVATE QuicUtils { DISALLOW_COPY_AND_ASSIGN(QuicUtils); }; -// Utility function that returns an IOVector object wrapped around |str|. -inline IOVector MakeIOVector(base::StringPiece str) { - IOVector iov; - iov.Append(const_cast<char*>(str.data()), str.size()); - return iov; +// Utility function that returns an QuicIOVector object wrapped around |str|. +// |str|'s data is stored in |iov|. +inline QuicIOVector MakeIOVector(base::StringPiece str, struct iovec* iov) { + iov->iov_base = const_cast<char*>(str.data()); + iov->iov_len = static_cast<size_t>(str.size()); + QuicIOVector quic_iov(iov, 1, str.size()); + return quic_iov; } } // namespace net diff --git a/chromium/net/quic/reliable_quic_stream.cc b/chromium/net/quic/reliable_quic_stream.cc index 33f14d4a236..924211f8a46 100644 --- a/chromium/net/quic/reliable_quic_stream.cc +++ b/chromium/net/quic/reliable_quic_stream.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "net/quic/iovector.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_flow_controller.h" #include "net/quic/quic_session.h" #include "net/quic/quic_write_blocked_list.h" @@ -133,18 +134,26 @@ ReliableQuicStream::ReliableQuicStream(QuicStreamId id, QuicSession* session) perspective_, GetReceivedFlowControlWindow(session), GetInitialStreamFlowControlWindowToSend(session), - GetInitialStreamFlowControlWindowToSend(session)), + session_->flow_controller()->auto_tune_receive_window()), connection_flow_controller_(session_->flow_controller()), stream_contributes_to_connection_flow_control_(true) { + SetFromConfig(); } ReliableQuicStream::~ReliableQuicStream() { } +void ReliableQuicStream::SetFromConfig() { + if (FLAGS_quic_send_fec_packet_only_on_fec_alarm && + session_->config()->HasClientSentConnectionOption(kFSTR, perspective_)) { + fec_policy_ = FEC_PROTECT_ALWAYS; + } +} + void 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. + // The subclass does not want read data: blackhole the data. return; } @@ -155,16 +164,19 @@ void ReliableQuicStream::OnStreamFrame(const QuicStreamFrame& frame) { if (frame.fin) { fin_received_ = true; + if (fin_sent_) { + session_->StreamDraining(id_); + } } - // This count include duplicate data received. - size_t frame_payload_size = frame.data.TotalBufferSize(); + // This count includes duplicate data received. + size_t frame_payload_size = frame.data.size(); 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. + // As the highest received offset has changed, check to see if this is a + // violation of flow control. if (flow_controller_.FlowControlViolation() || connection_flow_controller_->FlowControlViolation()) { session_->connection()->SendConnectionClose( @@ -213,7 +225,12 @@ void ReliableQuicStream::OnConnectionClosed(QuicErrorCode error, void ReliableQuicStream::OnFinRead() { DCHECK(sequencer_.IsClosed()); + // OnFinRead can be called due to a FIN flag in a headers block, so there may + // have been no OnStreamFrame call with a FIN in the frame. fin_received_ = true; + // If fin_sent_ is true, then CloseWriteSide has already been called, and the + // stream will be destroyed by CloseReadSide, so don't need to call + // StreamDraining. CloseReadSide(); } @@ -247,6 +264,10 @@ void ReliableQuicStream::WriteOrBufferData( LOG(DFATAL) << "Fin already buffered"; return; } + if (write_side_closed_) { + DLOG(ERROR) << ENDPOINT << "Attempt to write when the write side is closed"; + return; + } scoped_refptr<ProxyAckNotifierDelegate> proxy_delegate; if (ack_notifier_delegate != nullptr) { @@ -325,9 +346,10 @@ void ReliableQuicStream::MaybeSendBlocked() { return; } 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 the stream is blocked by connection-level flow control but not by + // stream-level flow control, add the stream to the write blocked list so that + // the stream 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()); @@ -344,13 +366,13 @@ QuicConsumedData ReliableQuicStream::WritevData( return QuicConsumedData(0, false); } - // How much data we want to write. + // How much data was provided. 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); - // How much data we are allowed to write from flow control. + // How much data flow control permits to be written. QuicByteCount send_window = flow_controller_.SendWindowSize(); if (stream_contributes_to_connection_flow_control_) { send_window = @@ -358,26 +380,22 @@ QuicConsumedData ReliableQuicStream::WritevData( } if (send_window == 0 && !fin_with_zero_data) { - // Quick return if we can't send anything. + // Quick return if nothing can be sent. 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. + // Don't send the FIN unless all the data will be sent. fin = false; // Writing more data would be a violation of flow control. write_length = static_cast<size_t>(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(), data, stream_bytes_written_, fin, GetFecProtection(), - ack_notifier_delegate); + id(), QuicIOVector(iov, iov_count, write_length), stream_bytes_written_, + fin, GetFecProtection(), ack_notifier_delegate); stream_bytes_written_ += consumed_data.bytes_consumed; AddBytesSent(consumed_data.bytes_consumed); @@ -388,6 +406,9 @@ QuicConsumedData ReliableQuicStream::WritevData( } if (fin && consumed_data.fin_consumed) { fin_sent_ = true; + if (fin_received_) { + session_->StreamDraining(id_); + } CloseWriteSide(); } else if (fin && !consumed_data.fin_consumed) { session_->MarkWriteBlocked(id(), EffectivePriority()); @@ -441,19 +462,19 @@ void ReliableQuicStream::OnClose() { CloseWriteSide(); if (!fin_sent_ && !rst_sent_) { - // For flow control accounting, we must tell the peer how many bytes we have + // For flow control accounting, tell the peer how many bytes have been // written on this stream before termination. Done here if needed, using a - // RST frame. - DVLOG(1) << ENDPOINT << "Sending RST in OnClose: " << id(); + // RST_STREAM frame. + DVLOG(1) << ENDPOINT << "Sending RST_STREAM in OnClose: " << id(); session_->SendRstStream(id(), QUIC_RST_ACKNOWLEDGEMENT, 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. + // The stream is being closed and will not process any further incoming bytes. + // As there may be more bytes in flight, to ensure that both endpoints have + // the same connection level flow control state, mark all unreceived or + // buffered bytes as consumed. QuicByteCount bytes_to_consume = flow_controller_.highest_received_byte_offset() - flow_controller_.bytes_consumed(); @@ -463,11 +484,11 @@ void ReliableQuicStream::OnClose() { void ReliableQuicStream::OnWindowUpdateFrame( const QuicWindowUpdateFrame& frame) { if (flow_controller_.UpdateSendWindowOffset(frame.byte_offset)) { - // We can write again! + // Writing can be done 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! + // As long as the connection is not flow control blocked, write on! OnCanWrite(); } } @@ -481,8 +502,8 @@ bool ReliableQuicStream::MaybeIncreaseHighestReceivedOffset( } // 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. + // offset, increase the connection flow controller's value by the incremental + // difference. if (stream_contributes_to_connection_flow_control_) { connection_flow_controller_->UpdateHighestReceivedOffset( connection_flow_controller_->highest_received_byte_offset() + @@ -499,7 +520,7 @@ void ReliableQuicStream::AddBytesSent(QuicByteCount bytes) { } void ReliableQuicStream::AddBytesConsumed(QuicByteCount bytes) { - // Only adjust stream level flow controller if we are still reading. + // Only adjust stream level flow controller if still reading. if (!read_side_closed_) { flow_controller_.AddBytesConsumed(bytes); } diff --git a/chromium/net/quic/reliable_quic_stream.h b/chromium/net/quic/reliable_quic_stream.h index 7a954673685..265e4795a78 100644 --- a/chromium/net/quic/reliable_quic_stream.h +++ b/chromium/net/quic/reliable_quic_stream.h @@ -4,6 +4,16 @@ // // The base class for client/server reliable streams. +// It does not contain the entire interface needed by an application to interact +// with a QUIC stream. Some parts of the interface must be obtained by +// accessing the owning session object. A subclass of ReliableQuicStream +// connects the object and the application that generates and consumes the data +// of the stream. + +// The ReliableQuicStream object has a dependent QuicStreamSequencer object, +// which is given the stream frames as they arrive, and provides stream data in +// order by invoking ProcessRawData(). + #ifndef NET_QUIC_RELIABLE_QUIC_STREAM_H_ #define NET_QUIC_RELIABLE_QUIC_STREAM_H_ @@ -38,34 +48,53 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { virtual ~ReliableQuicStream(); - // Called when a (potentially duplicate) stream frame has been received - // for this stream. + // Sets |fec_policy_| parameter from |session_|'s config. + void SetFromConfig(); + + // Called by the session when a (potentially duplicate) stream frame has been + // received for this stream. virtual void OnStreamFrame(const QuicStreamFrame& frame); - // Called when the connection becomes writeable to allow the stream - // to write any pending data. + // Called by the session when the connection becomes writeable to allow the + // stream to write any pending data. virtual void OnCanWrite(); - // Called by the session just before the stream is deleted. + // Called by the session just before the object is destroyed. + // The object should not be accessed after OnClose is called. + // Sends a RST_STREAM with code QUIC_RST_ACKNOWLEDGEMENT if neither a FIN nor + // a RST_STREAM has been sent. virtual void OnClose(); - // Called when we get a stream reset from the peer. + // Called by the session when the endpoint receives a RST_STREAM from the + // peer. 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, - // but is handled immediately. + // Called by the session when the endpoint receives or sends a connection + // close, and should immediately close the stream. virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer); - // Called when the final data has been read. + // Called by the sequencer after ProcessRawData has accepted the final + // incoming data. virtual void OnFinRead(); + // Called by the sequencer to deliver to the subclass blocks of the stream's + // data in sequential order. + // ProcessRawData is called when the next sequential block of data becomes + // available, unless the subclass has left the next block of data in the + // sequencer. In that case, the subclass must actively retrieve the data + // using the sequencer's Readv() or FlushBufferedFrames(). + // Return value is the number of data bytes accepted by the callee, i.e., + // the first (return value) bytes of data are removed from the sequencer, + // and the final data_len-(return value) bytes of data are retained by the + // sequencer. virtual uint32 ProcessRawData(const char* data, uint32 data_len) = 0; - // Called to reset the stream from this end. + // Called by the subclass or the sequencer to reset the stream from this + // end. virtual void Reset(QuicRstStreamErrorCode error); - // Called to close the entire connection from this end. + // Called by the subclass or the sequencer to close the entire connection from + // this end. virtual void CloseConnection(QuicErrorCode error); virtual void CloseConnectionWithDetails(QuicErrorCode error, const std::string& details); @@ -91,7 +120,7 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { void set_fec_policy(FecPolicy fec_policy) { fec_policy_ = fec_policy; } FecPolicy fec_policy() const { return fec_policy_; } - // Adjust our flow control windows according to new offset in |frame|. + // Adjust the flow control window according to new offset in |frame|. virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame); // Used in Chrome. @@ -101,13 +130,14 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { QuicFlowController* flow_controller() { return &flow_controller_; } - // Called when we see a frame which could increase the highest offset. + // Called when endpoint receives a frame which could increase the highest + // offset. // Returns true if the highest offset did increase. bool MaybeIncreaseHighestReceivedOffset(QuicStreamOffset new_offset); - // Called when bytese are sent to the peer. + // Called when bytes are sent to the peer. void AddBytesSent(QuicByteCount 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 + // If the receive window has dropped below the threshold, then send a // WINDOW_UPDATE frame. void AddBytesConsumed(QuicByteCount bytes); @@ -115,10 +145,11 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { // it was blocked before. void UpdateSendWindowOffset(QuicStreamOffset new_offset); - // 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. + // Returns true if the stream have received either a RST_STREAM or a FIN - + // either of which gives a definitive number of bytes which the peer has + // sent. If this is not true on deletion of the stream object, 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_; } @@ -129,9 +160,13 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { // Returns the version of QUIC being used for this stream. QuicVersion version() const; + bool fin_received() const { return fin_received_; } + protected: // Sends as much of 'data' to the connection as the connection will consume, // and then buffers any remaining data in queued_data_. + // If fin is true: if it is immediately passed on to the session, + // write_side_closed() becomes true, otherwise fin_buffered_ becomes true. void WriteOrBufferData( base::StringPiece data, bool fin, @@ -148,15 +183,19 @@ 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. + // Close the read side of the stream. Further incoming stream frames will be + // discarded. Can be called by the subclass or internally. + // May cause the stream to be closed. virtual void CloseReadSide(); // Close the write side of the socket. Further writes will fail. + // Can be called by the subclass or internally. + // Does not send a FIN. May cause the stream to be closed. void CloseWriteSide(); + // Helper method that returns FecProtection to use when writing. + FecProtection GetFecProtection(); + bool fin_buffered() const { return fin_buffered_; } const QuicSession* session() const { return session_; } @@ -188,15 +227,17 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { 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. + // Calls MaybeSendBlocked on the stream's flow controller and the connection + // level flow controller. If the stream is flow control blocked by the + // connection-level flow controller but not by the stream-level flow + // controller, marks this stream as connection-level write blocked. void MaybeSendBlocked(); std::list<PendingData> queued_data_; QuicStreamSequencer sequencer_; QuicStreamId id_; + // Pointer to the owning QuicSession object. QuicSession* session_; // Bytes read and written refer to payload bytes only: they do not include // framing, encryption overhead etc. @@ -216,18 +257,22 @@ class NET_EXPORT_PRIVATE ReliableQuicStream { // True if the write side is closed, and further writes should fail. bool write_side_closed_; + // True if the subclass has written a FIN with WriteOrBufferData, but it was + // buffered in queued_data_ rather than being sent to the session. bool fin_buffered_; + // True if a FIN has been sent to the session. 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. + // True if an RST_STREAM has been sent to the session. + // In combination with fin_sent_, used to ensure that a FIN and/or a + // RST_STREAM is always sent to terminate the stream. bool rst_sent_; - // True if this stream has received a RST stream frame. + // True if this stream has received a RST_STREAM frame. bool rst_received_; // FEC policy to be used for this stream. diff --git a/chromium/net/quic/reliable_quic_stream_test.cc b/chromium/net/quic/reliable_quic_stream_test.cc index b17def0d1b4..dbdb7b5d176 100644 --- a/chromium/net/quic/reliable_quic_stream_test.cc +++ b/chromium/net/quic/reliable_quic_stream_test.cc @@ -6,6 +6,7 @@ #include "net/quic/quic_ack_notifier.h" #include "net/quic/quic_connection.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" @@ -22,6 +23,7 @@ using base::StringPiece; using std::min; using std::string; +using testing::AnyNumber; using testing::CreateFunctor; using testing::InSequence; using testing::Invoke; @@ -39,6 +41,7 @@ const char kData1[] = "FooAndBar"; const char kData2[] = "EepAndBaz"; const size_t kDataLen = 9; const bool kShouldProcessData = true; +const bool kShouldNotProcessData = false; class TestStream : public ReliableQuicStream { public: @@ -107,21 +110,26 @@ class ReliableQuicStreamTest : public ::testing::TestWithParam<bool> { void Initialize(bool stream_should_process_data) { connection_ = new StrictMock<MockConnection>(Perspective::IS_SERVER, supported_versions_); - session_.reset(new StrictMock<MockSession>(connection_)); + session_.reset(new StrictMock<MockQuicSpdySession>(connection_)); // New streams rely on having the peer's flow control receive window // negotiated in the config. QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow( session_->config(), initial_flow_control_window_bytes_); - stream_.reset(new TestStream(kHeadersStreamId, session_.get(), - stream_should_process_data)); + stream_ = new TestStream(kTestStreamId, session_.get(), + stream_should_process_data); + // session_ now owns stream_. + session_->ActivateStream(stream_); + // Ignore resetting when session_ is terminated. + EXPECT_CALL(*session_, SendRstStream(kTestStreamId, _, _)) + .Times(AnyNumber()); write_blocked_list_ = QuicSessionPeer::GetWriteBlockedStreams(session_.get()); } - bool fin_sent() { return ReliableQuicStreamPeer::FinSent(stream_.get()); } - bool rst_sent() { return ReliableQuicStreamPeer::RstSent(stream_.get()); } + bool fin_sent() { return ReliableQuicStreamPeer::FinSent(stream_); } + bool rst_sent() { return ReliableQuicStreamPeer::RstSent(stream_); } void set_initial_flow_control_window_bytes(uint32 val) { initial_flow_control_window_bytes_ = val; @@ -134,13 +142,14 @@ class ReliableQuicStreamTest : public ::testing::TestWithParam<bool> { protected: MockConnection* connection_; - scoped_ptr<MockSession> session_; - scoped_ptr<TestStream> stream_; + scoped_ptr<MockQuicSpdySession> session_; + TestStream* stream_; SpdyHeaderBlock headers_; QuicWriteBlockedList* write_blocked_list_; uint32 initial_flow_control_window_bytes_; QuicTime::Delta zero_; QuicVersionVector supported_versions_; + const QuicStreamId kTestStreamId = 5u; }; TEST_F(ReliableQuicStreamTest, WriteAllData) { @@ -149,10 +158,10 @@ TEST_F(ReliableQuicStreamTest, WriteAllData) { size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead( PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, 0u, NOT_IN_FEC_GROUP); - QuicConnectionPeer::GetPacketCreator(connection_)->SetMaxPacketLength(length); + connection_->set_max_packet_length(length); - EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)).WillOnce( - Return(QuicConsumedData(kDataLen, true))); + EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(kDataLen, true))); stream_->WriteOrBufferData(kData1, false, nullptr); EXPECT_FALSE(HasWriteBlockedStreams()); } @@ -171,7 +180,7 @@ TEST_F(ReliableQuicStreamTest, BlockIfOnlySomeDataConsumed) { // 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(kHeadersStreamId, _, _, _, _, _)) + EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(Return(QuicConsumedData(1, false))); stream_->WriteOrBufferData(StringPiece(kData1, 2), false, nullptr); ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams()); @@ -184,7 +193,7 @@ 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(kHeadersStreamId, _, _, _, _, _)) + EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(Return(QuicConsumedData(2, false))); stream_->WriteOrBufferData(StringPiece(kData1, 2), true, nullptr); ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams()); @@ -195,7 +204,7 @@ 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(kHeadersStreamId, _, _, _, _, _)) + EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(Return(QuicConsumedData(0, false))); stream_->WriteOrBufferData(StringPiece(), true, nullptr); ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams()); @@ -208,7 +217,7 @@ TEST_F(ReliableQuicStreamTest, WriteOrBufferData) { size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead( PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, 0u, NOT_IN_FEC_GROUP); - QuicConnectionPeer::GetPacketCreator(connection_)->SetMaxPacketLength(length); + connection_->set_max_packet_length(length); EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).WillOnce( Return(QuicConsumedData(kDataLen - 1, false))); @@ -236,13 +245,13 @@ TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithFecProtectAlways) { Initialize(kShouldProcessData); // Set FEC policy on stream. - ReliableQuicStreamPeer::SetFecPolicy(stream_.get(), FEC_PROTECT_ALWAYS); + ReliableQuicStreamPeer::SetFecPolicy(stream_, FEC_PROTECT_ALWAYS); EXPECT_FALSE(HasWriteBlockedStreams()); size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead( PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, 0u, IN_FEC_GROUP); - QuicConnectionPeer::GetPacketCreator(connection_)->SetMaxPacketLength(length); + 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( @@ -271,14 +280,13 @@ TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithFecProtectOptional) { Initialize(kShouldProcessData); // Set FEC policy on stream. - ReliableQuicStreamPeer::SetFecPolicy(stream_.get(), FEC_PROTECT_OPTIONAL); + ReliableQuicStreamPeer::SetFecPolicy(stream_, FEC_PROTECT_OPTIONAL); EXPECT_FALSE(HasWriteBlockedStreams()); size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead( PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, PACKET_6BYTE_SEQUENCE_NUMBER, 0u, NOT_IN_FEC_GROUP); - QuicConnectionPeer::GetPacketCreator(connection_)->SetMaxPacketLength( - length); + 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( @@ -325,7 +333,7 @@ TEST_F(ReliableQuicStreamTest, RstAlwaysSentIfNoFinSent) { EXPECT_FALSE(rst_sent()); // Write some data, with no FIN. - EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(Return(QuicConsumedData(1, false))); stream_->WriteOrBufferData(StringPiece(kData1, 1), false, nullptr); EXPECT_FALSE(fin_sent()); @@ -348,7 +356,7 @@ TEST_F(ReliableQuicStreamTest, RstNotSentIfFinSent) { EXPECT_FALSE(rst_sent()); // Write some data, with FIN. - EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(Return(QuicConsumedData(1, true))); stream_->WriteOrBufferData(StringPiece(kData1, 1), true, nullptr); EXPECT_TRUE(fin_sent()); @@ -442,7 +450,7 @@ TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithQuicAckNotifier) { scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate; - EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor( &SaveProxyAckNotifierDelegate, &proxy_delegate))), Return(QuicConsumedData(kFirstWriteSize, false)))); @@ -450,23 +458,23 @@ TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithQuicAckNotifier) { EXPECT_TRUE(HasWriteBlockedStreams()); EXPECT_CALL(*session_, - WritevData(kHeadersStreamId, _, _, _, _, proxy_delegate.get())) + WritevData(kTestStreamId, _, _, _, _, 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())) + WritevData(kTestStreamId, _, _, _, _, proxy_delegate.get())) .WillOnce(Return(QuicConsumedData(0, false))); stream_->OnCanWrite(); EXPECT_CALL(*session_, - WritevData(kHeadersStreamId, _, _, _, _, proxy_delegate.get())) + WritevData(kTestStreamId, _, _, _, _, proxy_delegate.get())) .WillOnce(Return(QuicConsumedData(kLastWriteSize, false))); stream_->OnCanWrite(); - // There were two writes, so OnAckNotification is not propagated - // until the third Ack arrives. + // There were two writes, so OnAckNotification is not propagated until the + // third Ack arrives. proxy_delegate->OnAckNotification(3, 4, zero_); proxy_delegate->OnAckNotification(30, 40, zero_); @@ -476,8 +484,8 @@ TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithQuicAckNotifier) { proxy_delegate->OnAckNotification(300, 400, zero_); } -// Verify delegate behavior when packets are acked before the -// WritevData call that sends out the last byte. +// Verify delegate behavior when packets are acked before the WritevData call +// that sends out the last byte. TEST_F(ReliableQuicStreamTest, WriteOrBufferDataAckNotificationBeforeFlush) { Initialize(kShouldProcessData); @@ -495,7 +503,7 @@ TEST_F(ReliableQuicStreamTest, WriteOrBufferDataAckNotificationBeforeFlush) { scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate; - EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor( &SaveProxyAckNotifierDelegate, &proxy_delegate))), Return(QuicConsumedData(kInitialWriteSize, false)))); @@ -506,10 +514,11 @@ TEST_F(ReliableQuicStreamTest, WriteOrBufferDataAckNotificationBeforeFlush) { proxy_delegate->OnAckNotification(3, 4, zero_); proxy_delegate = nullptr; - EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)).WillOnce( - DoAll(WithArgs<5>(Invoke(CreateFunctor( - &SaveProxyAckNotifierDelegate, &proxy_delegate))), - Return(QuicConsumedData(kDataSize - kInitialWriteSize, false)))); + EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) + .WillOnce(DoAll( + WithArgs<5>(Invoke( + CreateFunctor(&SaveProxyAckNotifierDelegate, &proxy_delegate))), + Return(QuicConsumedData(kDataSize - kInitialWriteSize, false)))); stream_->OnCanWrite(); // Handle the ack for the second write. @@ -526,7 +535,7 @@ TEST_F(ReliableQuicStreamTest, WriteAndBufferDataWithAckNotiferNoBuffer) { scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate; - EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor( &SaveProxyAckNotifierDelegate, &proxy_delegate))), Return(QuicConsumedData(kDataLen, true)))); @@ -547,12 +556,12 @@ TEST_F(ReliableQuicStreamTest, BufferOnWriteAndBufferDataWithAckNotifer) { scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate; - EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(Return(QuicConsumedData(0, false))); stream_->WriteOrBufferData(kData1, true, delegate.get()); EXPECT_TRUE(HasWriteBlockedStreams()); - EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor( &SaveProxyAckNotifierDelegate, &proxy_delegate))), Return(QuicConsumedData(kDataLen, true)))); @@ -573,14 +582,14 @@ TEST_F(ReliableQuicStreamTest, WriteAndBufferDataWithAckNotiferOnlyFinRemains) { scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate; - EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)) + EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .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, _, _, _, _, _)) + EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor( &SaveProxyAckNotifierDelegate, &proxy_delegate))), Return(QuicConsumedData(0, true)))); @@ -603,7 +612,7 @@ TEST_F(ReliableQuicStreamTest, // higher than the receive window offset. QuicStreamFrame frame(stream_->id(), false, kInitialSessionFlowControlWindowForTest + 1, - MakeIOVector(".")); + StringPiece(".")); EXPECT_GT(frame.offset, QuicFlowControllerPeer::ReceiveWindowOffset( stream_->flow_controller())); @@ -619,12 +628,12 @@ TEST_F(ReliableQuicStreamTest, FinalByteOffsetFromFin) { EXPECT_FALSE(stream_->HasFinalReceivedByteOffset()); QuicStreamFrame stream_frame_no_fin(stream_->id(), false, 1234, - MakeIOVector(".")); + StringPiece(".")); stream_->OnStreamFrame(stream_frame_no_fin); EXPECT_FALSE(stream_->HasFinalReceivedByteOffset()); QuicStreamFrame stream_frame_with_fin(stream_->id(), true, 1234, - MakeIOVector(".")); + StringPiece(".")); stream_->OnStreamFrame(stream_frame_with_fin); EXPECT_TRUE(stream_->HasFinalReceivedByteOffset()); } @@ -638,6 +647,71 @@ TEST_F(ReliableQuicStreamTest, FinalByteOffsetFromRst) { EXPECT_TRUE(stream_->HasFinalReceivedByteOffset()); } +TEST_F(ReliableQuicStreamTest, SetDrainingIncomingOutgoing) { + // Don't have incoming data consumed. + Initialize(kShouldNotProcessData); + + // Incoming data with FIN. + QuicStreamFrame stream_frame_with_fin(stream_->id(), true, 1234, + StringPiece(".")); + stream_->OnStreamFrame(stream_frame_with_fin); + // The FIN has been received but not consumed. + EXPECT_TRUE(stream_->HasFinalReceivedByteOffset()); + EXPECT_FALSE(stream_->read_side_closed()); + + EXPECT_EQ(1u, session_->GetNumOpenStreams()); + + // Outgoing data with FIN. + EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(2, true))); + stream_->WriteOrBufferData(StringPiece(kData1, 2), true, nullptr); + EXPECT_TRUE(stream_->write_side_closed()); + + EXPECT_EQ(1u, QuicSessionPeer::GetDrainingStreams(session_.get()) + ->count(kTestStreamId)); + EXPECT_EQ(0u, session_->GetNumOpenStreams()); +} + +TEST_F(ReliableQuicStreamTest, SetDrainingOutgoingIncoming) { + // Don't have incoming data consumed. + Initialize(kShouldNotProcessData); + + // Outgoing data with FIN. + EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(2, true))); + stream_->WriteOrBufferData(StringPiece(kData1, 2), true, nullptr); + EXPECT_TRUE(stream_->write_side_closed()); + + EXPECT_EQ(1u, session_->GetNumOpenStreams()); + + // Incoming data with FIN. + QuicStreamFrame stream_frame_with_fin(stream_->id(), true, 1234, + StringPiece(".")); + stream_->OnStreamFrame(stream_frame_with_fin); + // The FIN has been received but not consumed. + EXPECT_TRUE(stream_->HasFinalReceivedByteOffset()); + EXPECT_FALSE(stream_->read_side_closed()); + + EXPECT_EQ(1u, QuicSessionPeer::GetDrainingStreams(session_.get()) + ->count(kTestStreamId)); + EXPECT_EQ(0u, session_->GetNumOpenStreams()); +} + +TEST_F(ReliableQuicStreamTest, FecSendPolicyReceivedConnectionOption) { + ValueRestore<bool> old_flag(&FLAGS_quic_send_fec_packet_only_on_fec_alarm, + true); + Initialize(kShouldProcessData); + + // Test ReceivedConnectionOptions. + QuicConfig* config = session_->config(); + QuicTagVector copt; + copt.push_back(kFSTR); + QuicConfigPeer::SetReceivedConnectionOptions(config, copt); + EXPECT_EQ(FEC_PROTECT_OPTIONAL, stream_->fec_policy()); + stream_->SetFromConfig(); + EXPECT_EQ(FEC_PROTECT_ALWAYS, stream_->fec_policy()); +} + } // namespace } // namespace test } // namespace net diff --git a/chromium/net/quic/spdy_utils.cc b/chromium/net/quic/spdy_utils.cc index 4ebae318f55..c03d5887cfe 100644 --- a/chromium/net/quic/spdy_utils.cc +++ b/chromium/net/quic/spdy_utils.cc @@ -17,7 +17,7 @@ namespace net { SpdyMajorVersion SpdyUtils::GetSpdyVersionForQuicVersion( QuicVersion quic_version) { if (quic_version > QUIC_VERSION_24) { - return SPDY4; + return HTTP2; } return SPDY3; } @@ -39,7 +39,7 @@ string SpdyUtils::SerializeUncompressedHeaders(const SpdyHeaderBlock& headers, QuicVersion quic_version) { SpdyMajorVersion spdy_version = GetSpdyVersionForQuicVersion(quic_version); - int length = SpdyFramer::GetSerializedLength(spdy_version, &headers); + size_t length = SpdyFramer::GetSerializedLength(spdy_version, &headers); SpdyFrameBuilder builder(length, spdy_version); SpdyFramer::WriteHeaderBlock(&builder, spdy_version, &headers); scoped_ptr<SpdyFrame> block(builder.take()); diff --git a/chromium/net/quic/test_tools/crypto_test_utils.cc b/chromium/net/quic/test_tools/crypto_test_utils.cc index f402256e112..811da803f1a 100644 --- a/chromium/net/quic/test_tools/crypto_test_utils.cc +++ b/chromium/net/quic/test_tools/crypto_test_utils.cc @@ -90,11 +90,8 @@ void MovePackets(PacketSavingConnection* source_conn, break; } - for (vector<QuicStreamFrame>::const_iterator - i = framer.stream_frames().begin(); - i != framer.stream_frames().end(); ++i) { - scoped_ptr<string> frame_data(i->GetDataAsString()); - ASSERT_TRUE(crypto_framer.ProcessInput(*frame_data)); + for (const QuicStreamFrame& stream_frame : framer.stream_frames()) { + ASSERT_TRUE(crypto_framer.ProcessInput(stream_frame.data)); ASSERT_FALSE(crypto_visitor.error()); } } @@ -104,10 +101,8 @@ void MovePackets(PacketSavingConnection* source_conn, ASSERT_EQ(0u, crypto_framer.InputBytesRemaining()); - for (vector<CryptoHandshakeMessage>::const_iterator - i = crypto_visitor.messages().begin(); - i != crypto_visitor.messages().end(); ++i) { - dest_stream->OnHandshakeMessage(*i); + for (const CryptoHandshakeMessage& message : crypto_visitor.messages()) { + dest_stream->OnHandshakeMessage(message); } } @@ -186,25 +181,23 @@ int CryptoTestUtils::HandshakeWithFakeServer( QuicCryptoClientStream* client) { PacketSavingConnection* server_conn = new PacketSavingConnection( Perspective::IS_SERVER, client_conn->supported_versions()); - TestSession server_session(server_conn, DefaultQuicConfig()); - server_session.InitializeSession(); + + QuicConfig config = DefaultQuicConfig(); QuicCryptoServerConfig crypto_config(QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance()); + SetupCryptoServerConfigForTest(server_conn->clock(), + server_conn->random_generator(), &config, + &crypto_config); - SetupCryptoServerConfigForTest( - server_session.connection()->clock(), - server_session.connection()->random_generator(), - server_session.config(), &crypto_config); - - QuicCryptoServerStream server(&crypto_config, &server_session); - server_session.SetCryptoStream(&server); + TestQuicSpdyServerSession server_session(server_conn, config, &crypto_config); // The client's handshake must have been started already. CHECK_NE(0u, client_conn->encrypted_packets_.size()); - CommunicateHandshakeMessages(client_conn, client, server_conn, &server); + CommunicateHandshakeMessages(client_conn, client, server_conn, + server_session.GetCryptoStream()); - CompareClientAndServerKeys(client, &server); + CompareClientAndServerKeys(client, server_session.GetCryptoStream()); return client->num_sent_client_hellos(); } @@ -218,14 +211,8 @@ int CryptoTestUtils::HandshakeWithFakeClient( new PacketSavingConnection(Perspective::IS_CLIENT); // Advance the time, because timers do not like uninitialized times. client_conn->AdvanceTime(QuicTime::Delta::FromSeconds(1)); - TestClientSession client_session(client_conn, DefaultQuicConfig()); - QuicCryptoClientConfig crypto_config; - if (!options.dont_verify_certs) { - // TODO(wtc): replace this with ProofVerifierForTesting() when we have - // a working ProofSourceForTesting(). - crypto_config.SetProofVerifier(FakeProofVerifierForTesting()); - } + QuicCryptoClientConfig crypto_config; bool is_https = false; AsyncTestChannelIDSource* async_channel_id_source = nullptr; if (options.channel_id_enabled) { @@ -240,18 +227,22 @@ int CryptoTestUtils::HandshakeWithFakeClient( } QuicServerId server_id(kServerHostname, kServerPort, is_https, PRIVACY_MODE_DISABLED); - QuicCryptoClientStream client(server_id, &client_session, - ProofVerifyContextForTesting(), - &crypto_config); - client_session.SetCryptoStream(&client); + if (!options.dont_verify_certs) { + // TODO(wtc): replace this with ProofVerifierForTesting() when we have + // a working ProofSourceForTesting(). + crypto_config.SetProofVerifier(FakeProofVerifierForTesting()); + } + TestQuicSpdyClientSession client_session(client_conn, DefaultQuicConfig(), + server_id, &crypto_config); - client.CryptoConnect(); + client_session.GetCryptoStream()->CryptoConnect(); CHECK_EQ(1u, client_conn->encrypted_packets_.size()); CommunicateHandshakeMessagesAndRunCallbacks( - client_conn, &client, server_conn, server, async_channel_id_source); + client_conn, client_session.GetCryptoStream(), server_conn, server, + async_channel_id_source); - CompareClientAndServerKeys(&client, server); + CompareClientAndServerKeys(client_session.GetCryptoStream(), server); if (options.channel_id_enabled) { scoped_ptr<ChannelIDKey> channel_id_key; @@ -260,11 +251,12 @@ int CryptoTestUtils::HandshakeWithFakeClient( EXPECT_EQ(QUIC_SUCCESS, status); EXPECT_EQ(channel_id_key->SerializeKey(), server->crypto_negotiated_params().channel_id); - EXPECT_EQ(options.channel_id_source_async, - client.WasChannelIDSourceCallbackRun()); + EXPECT_EQ( + options.channel_id_source_async, + client_session.GetCryptoStream()->WasChannelIDSourceCallbackRun()); } - return client.num_sent_client_hellos(); + return client_session.GetCryptoStream()->num_sent_client_hellos(); } // static 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 f42775003fa..946b0531824 100644 --- a/chromium/net/quic/test_tools/crypto_test_utils_nss.cc +++ b/chromium/net/quic/test_tools/crypto_test_utils_nss.cc @@ -35,10 +35,10 @@ class TestChannelIDSource : public ChannelIDSource { private: typedef std::map<string, crypto::ECPrivateKey*> HostnameToKeyMap; - crypto::ECPrivateKey* HostnameToKey(const string& hostname) { + scoped_ptr<crypto::ECPrivateKey> HostnameToKey(const string& hostname) { HostnameToKeyMap::const_iterator it = hostname_to_key_.find(hostname); if (it != hostname_to_key_.end()) { - return it->second->Copy(); + return make_scoped_ptr(it->second->Copy()); } crypto::ECPrivateKey* keypair = crypto::ECPrivateKey::Create(); @@ -46,7 +46,7 @@ class TestChannelIDSource : public ChannelIDSource { return nullptr; } hostname_to_key_[hostname] = keypair; - return keypair->Copy(); + return make_scoped_ptr(keypair->Copy()); } HostnameToKeyMap hostname_to_key_; 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 0018e91eb9c..290693933fd 100644 --- a/chromium/net/quic/test_tools/mock_crypto_client_stream.cc +++ b/chromium/net/quic/test_tools/mock_crypto_client_stream.cc @@ -38,8 +38,8 @@ void MockCryptoClientStream::CryptoConnect() { case ZERO_RTT: { encryption_established_ = true; handshake_confirmed_ = false; - session()->connection()->SetDecrypter(QuicDecrypter::Create(kNULL), - ENCRYPTION_INITIAL); + session()->connection()->SetDecrypter(ENCRYPTION_INITIAL, + QuicDecrypter::Create(kNULL)); session()->OnCryptoHandshakeEvent( QuicSession::ENCRYPTION_FIRST_ESTABLISHED); break; @@ -54,8 +54,8 @@ void MockCryptoClientStream::CryptoConnect() { client_session()->OnProofVerifyDetailsAvailable(*proof_verify_details_); } SetConfigNegotiated(); - session()->connection()->SetDecrypter(QuicDecrypter::Create(kNULL), - ENCRYPTION_FORWARD_SECURE); + session()->connection()->SetDecrypter(ENCRYPTION_FORWARD_SECURE, + QuicDecrypter::Create(kNULL)); session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); break; } diff --git a/chromium/net/quic/test_tools/quic_ack_notifier_manager_peer.cc b/chromium/net/quic/test_tools/quic_ack_notifier_manager_peer.cc new file mode 100644 index 00000000000..3e7f90cb652 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_ack_notifier_manager_peer.cc @@ -0,0 +1,18 @@ +// Copyright (c) 2015 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_ack_notifier_manager_peer.h" + +#include "net/quic/quic_ack_notifier_manager.h" + +namespace net { +namespace test { + +size_t AckNotifierManagerPeer::GetNumberOfRegisteredPackets( + const AckNotifierManager* manager) { + return manager->ack_notifier_map_.size(); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/test_tools/quic_ack_notifier_manager_peer.h b/chromium/net/quic/test_tools/quic_ack_notifier_manager_peer.h new file mode 100644 index 00000000000..ea128d3af24 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_ack_notifier_manager_peer.h @@ -0,0 +1,29 @@ +// Copyright (c) 2015 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_ACK_NOTIFIER_MANAGER_PEER_H_ +#define NET_QUIC_TEST_TOOLS_QUIC_ACK_NOTIFIER_MANAGER_PEER_H_ + +#include "net/quic/quic_protocol.h" + +namespace net { + +class AckNotifierManager; + +namespace test { + +// Exposes the internal fields of AckNotifierManager for tests. +class AckNotifierManagerPeer { + public: + // Returns total number of packets known to the map. + static size_t GetNumberOfRegisteredPackets(const AckNotifierManager* manager); + + private: + DISALLOW_COPY_AND_ASSIGN(AckNotifierManagerPeer); +}; + +} // namespace test +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_ACK_NOTIFIER_MANAGER_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 3525a3247ef..df7d9e78e27 100644 --- a/chromium/net/quic/test_tools/quic_connection_peer.cc +++ b/chromium/net/quic/test_tools/quic_connection_peer.cc @@ -185,6 +185,12 @@ QuicAlarm* QuicConnectionPeer::GetTimeoutAlarm(QuicConnection* connection) { } // static +QuicAlarm* QuicConnectionPeer::GetMtuDiscoveryAlarm( + QuicConnection* connection) { + return connection->mtu_discovery_alarm_.get(); +} + +// static QuicPacketWriter* QuicConnectionPeer::GetWriter(QuicConnection* connection) { return connection->writer_; } diff --git a/chromium/net/quic/test_tools/quic_connection_peer.h b/chromium/net/quic/test_tools/quic_connection_peer.h index 88b05f034e7..56762757234 100644 --- a/chromium/net/quic/test_tools/quic_connection_peer.h +++ b/chromium/net/quic/test_tools/quic_connection_peer.h @@ -94,6 +94,7 @@ class QuicConnectionPeer { static QuicAlarm* GetRetransmissionAlarm(QuicConnection* connection); static QuicAlarm* GetSendAlarm(QuicConnection* connection); static QuicAlarm* GetTimeoutAlarm(QuicConnection* connection); + static QuicAlarm* GetMtuDiscoveryAlarm(QuicConnection* connection); static QuicPacketWriter* GetWriter(QuicConnection* connection); // If |owns_writer| is true, takes ownership of |writer|. diff --git a/chromium/net/quic/test_tools/quic_flow_controller_peer.cc b/chromium/net/quic/test_tools/quic_flow_controller_peer.cc index d6a10987773..602aaf83808 100644 --- a/chromium/net/quic/test_tools/quic_flow_controller_peer.cc +++ b/chromium/net/quic/test_tools/quic_flow_controller_peer.cc @@ -30,7 +30,7 @@ void QuicFlowControllerPeer::SetReceiveWindowOffset( void QuicFlowControllerPeer::SetMaxReceiveWindow( QuicFlowController* flow_controller, QuicByteCount window_size) { - flow_controller->max_receive_window_ = window_size; + flow_controller->receive_window_size_ = window_size; } // static @@ -58,5 +58,11 @@ QuicByteCount QuicFlowControllerPeer::ReceiveWindowSize( flow_controller->highest_received_byte_offset_; } +// static +QuicByteCount QuicFlowControllerPeer::WindowUpdateThreshold( + QuicFlowController* flow_controller) { + return flow_controller->WindowUpdateThreshold(); +} + } // 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 index 4270d96f1e3..3d7c9d00d30 100644 --- a/chromium/net/quic/test_tools/quic_flow_controller_peer.h +++ b/chromium/net/quic/test_tools/quic_flow_controller_peer.h @@ -33,6 +33,9 @@ class QuicFlowControllerPeer { static QuicByteCount ReceiveWindowSize(QuicFlowController* flow_controller); + static QuicByteCount WindowUpdateThreshold( + QuicFlowController* flow_controller); + private: DISALLOW_COPY_AND_ASSIGN(QuicFlowControllerPeer); }; diff --git a/chromium/net/quic/test_tools/quic_framer_peer.cc b/chromium/net/quic/test_tools/quic_framer_peer.cc index d477a808e6f..577a71ce475 100644 --- a/chromium/net/quic/test_tools/quic_framer_peer.cc +++ b/chromium/net/quic/test_tools/quic_framer_peer.cc @@ -25,17 +25,20 @@ void QuicFramerPeer::SetLastSerializedConnectionId( framer->last_serialized_connection_id_ = connection_id; } +// static void QuicFramerPeer::SetLastSequenceNumber( QuicFramer* framer, QuicPacketSequenceNumber packet_sequence_number) { framer->last_sequence_number_ = packet_sequence_number; } +// static void QuicFramerPeer::SetPerspective(QuicFramer* framer, Perspective perspective) { framer->perspective_ = perspective; } +// static void QuicFramerPeer::SwapCrypters(QuicFramer* framer1, QuicFramer* framer2) { for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; i++) { framer1->encrypter_[i].swap(framer2->encrypter_[i]); @@ -57,7 +60,7 @@ void QuicFramerPeer::SwapCrypters(QuicFramer* framer1, QuicFramer* framer2) { framer1->alternative_decrypter_latch_ = framer2_latch; } -// static. +// static QuicEncrypter* QuicFramerPeer::GetEncrypter(QuicFramer* framer, EncryptionLevel level) { return framer->encrypter_[level].get(); diff --git a/chromium/net/quic/test_tools/quic_framer_peer.h b/chromium/net/quic/test_tools/quic_framer_peer.h index 57818701cf9..2896e770563 100644 --- a/chromium/net/quic/test_tools/quic_framer_peer.h +++ b/chromium/net/quic/test_tools/quic_framer_peer.h @@ -6,12 +6,11 @@ #define NET_QUIC_TEST_TOOLS_QUIC_FRAMER_PEER_H_ #include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/quic_framer.h" #include "net/quic/quic_protocol.h" namespace net { -class QuicFramer; - namespace test { class QuicFramerPeer { diff --git a/chromium/net/quic/test_tools/quic_session_peer.cc b/chromium/net/quic/test_tools/quic_session_peer.cc index 7dd7b66df9d..c1f86198c02 100644 --- a/chromium/net/quic/test_tools/quic_session_peer.cc +++ b/chromium/net/quic/test_tools/quic_session_peer.cc @@ -4,6 +4,7 @@ #include "net/quic/test_tools/quic_session_peer.h" +#include "base/stl_util.h" #include "net/quic/quic_session.h" #include "net/quic/reliable_quic_stream.h" @@ -29,27 +30,16 @@ QuicCryptoStream* QuicSessionPeer::GetCryptoStream(QuicSession* session) { } // static -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( +ReliableQuicStream* QuicSessionPeer::GetIncomingDynamicStream( QuicSession* session, QuicStreamId stream_id) { - return session->GetIncomingDataStream(stream_id); + return session->GetIncomingDynamicStream(stream_id); } // static @@ -58,5 +48,42 @@ QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(QuicSession* session) { return session->locally_closed_streams_highest_offset_; } +// static +base::hash_set<QuicStreamId>* QuicSessionPeer::GetDrainingStreams( + QuicSession* session) { + return &session->draining_streams_; +} + +// static +bool QuicSessionPeer::IsStreamClosed(QuicSession* session, QuicStreamId id) { + DCHECK_NE(0u, id); + return session->IsClosedStream(id); +} + +// static +bool QuicSessionPeer::IsStreamCreated(QuicSession* session, QuicStreamId id) { + DCHECK_NE(0u, id); + return ContainsKey(session->dynamic_streams(), id); +} + +// static +bool QuicSessionPeer::IsStreamImplicitlyCreated(QuicSession* session, + QuicStreamId id) { + DCHECK_NE(0u, id); + return ContainsKey(session->implicitly_created_streams_, id); +} + +// static +bool QuicSessionPeer::IsStreamUncreated(QuicSession* session, QuicStreamId id) { + DCHECK_NE(0u, id); + if (id % 2 == session->next_stream_id_ % 2) { + // locally-created stream. + return id >= session->next_stream_id_; + } else { + // peer-created stream. + return id > session->largest_peer_created_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 4a88c88fba0..9f4f0110901 100644 --- a/chromium/net/quic/test_tools/quic_session_peer.h +++ b/chromium/net/quic/test_tools/quic_session_peer.h @@ -7,6 +7,7 @@ #include <map> +#include "base/containers/hash_tables.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_write_blocked_list.h" @@ -16,6 +17,7 @@ class QuicCryptoStream; class QuicDataStream; class QuicHeadersStream; class QuicSession; +class ReliableQuicStream; namespace test { @@ -24,20 +26,26 @@ class QuicSessionPeer { static void SetNextStreamId(QuicSession* session, QuicStreamId id); static void SetMaxOpenStreams(QuicSession* session, uint32 max_streams); static QuicCryptoStream* GetCryptoStream(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); + static ReliableQuicStream* GetIncomingDynamicStream(QuicSession* session, + QuicStreamId stream_id); static std::map<QuicStreamId, QuicStreamOffset>& GetLocallyClosedStreamsHighestOffset(QuicSession* session); + static base::hash_set<QuicStreamId>* GetDrainingStreams(QuicSession* session); + + // Discern the state of a stream. Exactly one of these should be true at a + // time for any stream id > 0 (other than the special streams 1 and 3). + static bool IsStreamClosed(QuicSession* session, QuicStreamId id); + static bool IsStreamCreated(QuicSession* session, QuicStreamId id); + static bool IsStreamImplicitlyCreated(QuicSession* session, QuicStreamId id); + static bool IsStreamUncreated(QuicSession* session, QuicStreamId id); private: DISALLOW_COPY_AND_ASSIGN(QuicSessionPeer); }; } // namespace test + } // namespace net #endif // NET_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_ diff --git a/chromium/net/quic/test_tools/quic_spdy_session_peer.cc b/chromium/net/quic/test_tools/quic_spdy_session_peer.cc new file mode 100644 index 00000000000..e13e477b05c --- /dev/null +++ b/chromium/net/quic/test_tools/quic_spdy_session_peer.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2015 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_spdy_session_peer.h" + +#include "net/quic/quic_spdy_session.h" + +namespace net { +namespace test { + +// static +QuicHeadersStream* QuicSpdySessionPeer::GetHeadersStream( + QuicSpdySession* session) { + return session->headers_stream_.get(); +} + +// static +void QuicSpdySessionPeer::SetHeadersStream(QuicSpdySession* session, + QuicHeadersStream* headers_stream) { + session->headers_stream_.reset(headers_stream); + session->static_streams()[headers_stream->id()] = headers_stream; +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/test_tools/quic_spdy_session_peer.h b/chromium/net/quic/test_tools/quic_spdy_session_peer.h new file mode 100644 index 00000000000..840f3dcb2c2 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_spdy_session_peer.h @@ -0,0 +1,32 @@ +// Copyright (c) 2015 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_SPDY_SESSION_PEER_H_ +#define NET_QUIC_TEST_TOOLS_QUIC_SPDY_SESSION_PEER_H_ + +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_write_blocked_list.h" + +namespace net { + +class QuicHeadersStream; +class QuicSpdySession; + +namespace test { + +class QuicSpdySessionPeer { + public: + static QuicHeadersStream* GetHeadersStream(QuicSpdySession* session); + static void SetHeadersStream(QuicSpdySession* session, + QuicHeadersStream* headers_stream); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicSpdySessionPeer); +}; + +} // namespace test + +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_SPDY_SESSION_PEER_H_ diff --git a/chromium/net/quic/test_tools/quic_stream_sequencer_peer.cc b/chromium/net/quic/test_tools/quic_stream_sequencer_peer.cc index 08a5c5b0720..43db645e6de 100644 --- a/chromium/net/quic/test_tools/quic_stream_sequencer_peer.cc +++ b/chromium/net/quic/test_tools/quic_stream_sequencer_peer.cc @@ -13,9 +13,16 @@ namespace net { namespace test { // static -map<QuicStreamOffset, string>* QuicStreamSequencerPeer::GetBufferedFrames( +size_t QuicStreamSequencerPeer::GetNumBufferedFrames( QuicStreamSequencer* sequencer) { - return &(sequencer->buffered_frames_); + return sequencer->buffered_frames_.size(); +} + +// static +bool QuicStreamSequencerPeer::FrameOverlapsBufferedData( + QuicStreamSequencer* sequencer, + const QuicStreamFrame& frame) { + return sequencer->FrameOverlapsBufferedData(frame); } // static diff --git a/chromium/net/quic/test_tools/quic_stream_sequencer_peer.h b/chromium/net/quic/test_tools/quic_stream_sequencer_peer.h index 7b2b28aad46..22e3a9908b7 100644 --- a/chromium/net/quic/test_tools/quic_stream_sequencer_peer.h +++ b/chromium/net/quic/test_tools/quic_stream_sequencer_peer.h @@ -16,8 +16,10 @@ namespace test { class QuicStreamSequencerPeer { public: - static std::map<QuicStreamOffset, std::string>* GetBufferedFrames( - QuicStreamSequencer* sequencer); + static size_t GetNumBufferedFrames(QuicStreamSequencer* sequencer); + + static bool FrameOverlapsBufferedData(QuicStreamSequencer* sequencer, + const QuicStreamFrame& frame); static QuicStreamOffset GetCloseOffset(QuicStreamSequencer* sequencer); diff --git a/chromium/net/quic/test_tools/quic_test_packet_maker.cc b/chromium/net/quic/test_tools/quic_test_packet_maker.cc index 2ac6b405d27..63c9ac6761f 100644 --- a/chromium/net/quic/test_tools/quic_test_packet_maker.cc +++ b/chromium/net/quic/test_tools/quic_test_packet_maker.cc @@ -24,8 +24,8 @@ QuicTestPacketMaker::QuicTestPacketMaker(QuicVersion version, connection_id_(connection_id), clock_(clock), host_(host), - spdy_request_framer_(SPDY4), - spdy_response_framer_(SPDY4) { + spdy_request_framer_(HTTP2), + spdy_response_framer_(HTTP2) { } QuicTestPacketMaker::~QuicTestPacketMaker() { @@ -94,8 +94,8 @@ scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeAckAndRstPacket( BuildUnsizedDataPacket(&framer, header, frames)); char buffer[kMaxPacketSize]; scoped_ptr<QuicEncryptedPacket> encrypted( - framer.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, - *packet, buffer, kMaxPacketSize)); + framer.EncryptPayload(ENCRYPTION_NONE, header.packet_sequence_number, + *packet, buffer, kMaxPacketSize)); EXPECT_TRUE(encrypted != nullptr); return scoped_ptr<QuicEncryptedPacket>(encrypted->Clone()); } @@ -152,8 +152,8 @@ scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeAckPacket( BuildUnsizedDataPacket(&framer, header, frames)); char buffer[kMaxPacketSize]; scoped_ptr<QuicEncryptedPacket> encrypted( - framer.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, - *packet, buffer, kMaxPacketSize)); + framer.EncryptPayload(ENCRYPTION_NONE, header.packet_sequence_number, + *packet, buffer, kMaxPacketSize)); EXPECT_TRUE(encrypted != nullptr); return scoped_ptr<QuicEncryptedPacket>(encrypted->Clone()); } @@ -167,7 +167,7 @@ scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeDataPacket( QuicStreamOffset offset, base::StringPiece data) { InitializeHeader(sequence_number, should_include_version); - QuicStreamFrame frame(stream_id, fin, offset, MakeIOVector(data)); + QuicStreamFrame frame(stream_id, fin, offset, data); return MakePacket(header_, QuicFrame(&frame)); } @@ -194,9 +194,9 @@ scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeRequestHeadersPacket( headers_frame.set_has_priority(true); spdy_frame.reset(spdy_request_framer_.SerializeFrame(headers_frame)); } - QuicStreamFrame frame(kHeadersStreamId, false, 0, - MakeIOVector(base::StringPiece(spdy_frame->data(), - spdy_frame->size()))); + QuicStreamFrame frame( + kHeadersStreamId, false, 0, + base::StringPiece(spdy_frame->data(), spdy_frame->size())); return MakePacket(header_, QuicFrame(&frame)); } @@ -219,9 +219,9 @@ scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeResponseHeadersPacket( headers_frame.set_fin(fin); spdy_frame.reset(spdy_request_framer_.SerializeFrame(headers_frame)); } - QuicStreamFrame frame(kHeadersStreamId, false, 0, - MakeIOVector(base::StringPiece(spdy_frame->data(), - spdy_frame->size()))); + QuicStreamFrame frame( + kHeadersStreamId, false, 0, + base::StringPiece(spdy_frame->data(), spdy_frame->size())); return MakePacket(header_, QuicFrame(&frame)); } @@ -266,8 +266,8 @@ scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakePacket( BuildUnsizedDataPacket(&framer, header, frames)); char buffer[kMaxPacketSize]; scoped_ptr<QuicEncryptedPacket> encrypted( - framer.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, - *packet, buffer, kMaxPacketSize)); + framer.EncryptPayload(ENCRYPTION_NONE, header.packet_sequence_number, + *packet, buffer, kMaxPacketSize)); EXPECT_TRUE(encrypted != nullptr); return scoped_ptr<QuicEncryptedPacket>(encrypted->Clone()); } diff --git a/chromium/net/quic/test_tools/quic_test_utils.cc b/chromium/net/quic/test_tools/quic_test_utils.cc index 676a37dcac8..db45d15ab1d 100644 --- a/chromium/net/quic/test_tools/quic_test_utils.cc +++ b/chromium/net/quic/test_tools/quic_test_utils.cc @@ -17,6 +17,7 @@ #include "net/quic/quic_framer.h" #include "net/quic/quic_packet_creator.h" #include "net/quic/quic_utils.h" +#include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/quic_connection_peer.h" #include "net/spdy/spdy_frame_builder.h" #include "net/tools/quic/quic_per_connection_packet_writer.h" @@ -337,56 +338,50 @@ void PacketSavingConnection::SendOrQueuePacket(QueuedPacket packet) { NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); } -MockSession::MockSession(QuicConnection* connection) - : QuicSession(connection, DefaultQuicConfig()) { - InitializeSession(); +MockQuicSpdySession::MockQuicSpdySession(QuicConnection* connection) + : QuicSpdySession(connection, DefaultQuicConfig()) { + crypto_stream_.reset(new QuicCryptoStream(this)); + Initialize(); ON_CALL(*this, WritevData(_, _, _, _, _, _)) .WillByDefault(testing::Return(QuicConsumedData(0, false))); } -MockSession::~MockSession() { +MockQuicSpdySession::~MockQuicSpdySession() { } -TestSession::TestSession(QuicConnection* connection, const QuicConfig& config) - : QuicSession(connection, config), - crypto_stream_(nullptr) { - InitializeSession(); +TestQuicSpdyServerSession::TestQuicSpdyServerSession( + QuicConnection* connection, + const QuicConfig& config, + const QuicCryptoServerConfig* crypto_config) + : QuicSpdySession(connection, config) { + crypto_stream_.reset(new QuicCryptoServerStream(crypto_config, this)); + Initialize(); } -TestSession::~TestSession() {} - -void TestSession::SetCryptoStream(QuicCryptoStream* stream) { - crypto_stream_ = stream; -} - -QuicCryptoStream* TestSession::GetCryptoStream() { - return crypto_stream_; +TestQuicSpdyServerSession::~TestQuicSpdyServerSession() { } -TestClientSession::TestClientSession(QuicConnection* connection, - const QuicConfig& config) - : QuicClientSessionBase(connection, config), - crypto_stream_(nullptr) { - EXPECT_CALL(*this, OnProofValid(_)).Times(AnyNumber()); - InitializeSession(); -} - -TestClientSession::~TestClientSession() {} - -void TestClientSession::SetCryptoStream(QuicCryptoStream* stream) { - crypto_stream_ = stream; +QuicCryptoServerStream* TestQuicSpdyServerSession::GetCryptoStream() { + return crypto_stream_.get(); } -QuicCryptoStream* TestClientSession::GetCryptoStream() { - return crypto_stream_; +TestQuicSpdyClientSession::TestQuicSpdyClientSession( + QuicConnection* connection, + const QuicConfig& config, + const QuicServerId& server_id, + QuicCryptoClientConfig* crypto_config) + : QuicClientSessionBase(connection, config) { + crypto_stream_.reset(new QuicCryptoClientStream( + server_id, this, CryptoTestUtils::ProofVerifyContextForTesting(), + crypto_config)); + Initialize(); } -TestServerSession::TestServerSession(const QuicConfig& config, - QuicConnection* connection) - : QuicServerSession(config, connection, nullptr) { +TestQuicSpdyClientSession::~TestQuicSpdyClientSession() { } -TestServerSession::~TestServerSession() { +QuicCryptoClientStream* TestQuicSpdyClientSession::GetCryptoStream() { + return crypto_stream_.get(); } MockPacketWriter::MockPacketWriter() { @@ -538,7 +533,7 @@ QuicEncryptedPacket* ConstructEncryptedPacket( 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)); + QuicStreamFrame stream_frame(1, false, 0, StringPiece(data)); QuicFrame frame(&stream_frame); QuicFrames frames; frames.push_back(frame); @@ -549,7 +544,7 @@ QuicEncryptedPacket* ConstructEncryptedPacket( BuildUnsizedDataPacket(&framer, header, frames)); EXPECT_TRUE(packet != nullptr); char buffer[kMaxPacketSize]; - scoped_ptr<QuicEncryptedPacket> encrypted(framer.EncryptPacket( + scoped_ptr<QuicEncryptedPacket> encrypted(framer.EncryptPayload( ENCRYPTION_NONE, sequence_number, *packet, buffer, kMaxPacketSize)); EXPECT_TRUE(encrypted != nullptr); return encrypted->Clone(); @@ -576,15 +571,24 @@ QuicEncryptedPacket* ConstructMisFramedEncryptedPacket( header.fec_flag = false; header.is_in_fec_group = NOT_IN_FEC_GROUP; header.fec_group = 0; + QuicStreamFrame stream_frame(1, false, 0, StringPiece(data)); + QuicFrame frame(&stream_frame); QuicFrames frames; - QuicFramer framer(versions ? *versions : QuicSupportedVersions(), + frames.push_back(frame); + QuicFramer framer(versions != nullptr ? *versions : QuicSupportedVersions(), QuicTime::Zero(), Perspective::IS_CLIENT); - // Build a packet with zero frames, which is an error. + scoped_ptr<QuicPacket> packet( BuildUnsizedDataPacket(&framer, header, frames)); EXPECT_TRUE(packet != nullptr); + + // Now set the packet's private flags byte to 0xFF, which is an invalid value. + reinterpret_cast<unsigned char*>( + packet->mutable_data())[GetStartOfEncryptedData( + connection_id_length, version_flag, sequence_number_length)] = 0xFF; + char buffer[kMaxPacketSize]; - scoped_ptr<QuicEncryptedPacket> encrypted(framer.EncryptPacket( + scoped_ptr<QuicEncryptedPacket> encrypted(framer.EncryptPayload( ENCRYPTION_NONE, sequence_number, *packet, buffer, kMaxPacketSize)); EXPECT_TRUE(encrypted != nullptr); return encrypted->Clone(); @@ -654,7 +658,7 @@ static QuicPacket* ConstructPacketFromHandshakeMessage( header.fec_group = 0; QuicStreamFrame stream_frame(kCryptoStreamId, false, 0, - MakeIOVector(data->AsStringPiece())); + data->AsStringPiece()); QuicFrame frame(&stream_frame); QuicFrames frames; @@ -682,9 +686,9 @@ size_t GetPacketLengthForOneStream( QuicPacketCreator::StreamFramePacketOverhead( PACKET_8BYTE_CONNECTION_ID, include_version, sequence_number_length, 0u, is_in_fec_group); - const size_t ack_length = NullEncrypter().GetCiphertextSize( - QuicFramer::GetMinAckFrameSize( - sequence_number_length, PACKET_1BYTE_SEQUENCE_NUMBER)) + + const size_t ack_length = + NullEncrypter().GetCiphertextSize( + QuicFramer::GetMinAckFrameSize(PACKET_1BYTE_SEQUENCE_NUMBER)) + GetPacketHeaderSize(connection_id_length, include_version, sequence_number_length, is_in_fec_group); if (stream_length < ack_length) { @@ -786,18 +790,15 @@ MockQuicConnectionDebugVisitor::MockQuicConnectionDebugVisitor() { MockQuicConnectionDebugVisitor::~MockQuicConnectionDebugVisitor() { } -void SetupCryptoClientStreamForTest( - QuicServerId server_id, - bool supports_stateless_rejects, - QuicTime::Delta connection_start_time, - QuicCryptoClientConfig* crypto_client_config, - PacketSavingConnection** client_connection, - TestClientSession** client_session, - QuicCryptoClientStream** client_stream) { +void CreateClientSessionForTest(QuicServerId server_id, + bool supports_stateless_rejects, + QuicTime::Delta connection_start_time, + QuicCryptoClientConfig* crypto_client_config, + PacketSavingConnection** client_connection, + TestQuicSpdyClientSession** client_session) { CHECK(crypto_client_config); CHECK(client_connection); CHECK(client_session); - CHECK(client_stream); CHECK(!connection_start_time.IsZero()) << "Connections must start at non-zero times, otherwise the " << "strike-register will be unhappy."; @@ -806,35 +807,26 @@ void SetupCryptoClientStreamForTest( ? DefaultQuicConfigStatelessRejects() : DefaultQuicConfig(); *client_connection = new PacketSavingConnection(Perspective::IS_CLIENT); - *client_session = new TestClientSession(*client_connection, config); - *client_stream = new QuicCryptoClientStream(server_id, *client_session, - nullptr, crypto_client_config); - (*client_session)->SetCryptoStream(*client_stream); + *client_session = new TestQuicSpdyClientSession( + *client_connection, config, server_id, crypto_client_config); (*client_connection)->AdvanceTime(connection_start_time); } -// Setup or create? -void SetupCryptoServerStreamForTest( - QuicServerId server_id, - QuicTime::Delta connection_start_time, - QuicCryptoServerConfig* server_crypto_config, - PacketSavingConnection** server_connection, - TestServerSession** server_session, - QuicCryptoServerStream** server_stream) { +void CreateServerSessionForTest(QuicServerId server_id, + QuicTime::Delta connection_start_time, + QuicCryptoServerConfig* server_crypto_config, + PacketSavingConnection** server_connection, + TestQuicSpdyServerSession** server_session) { CHECK(server_crypto_config); CHECK(server_connection); CHECK(server_session); - CHECK(server_stream); CHECK(!connection_start_time.IsZero()) << "Connections must start at non-zero times, otherwise the " << "strike-register will be unhappy."; *server_connection = new PacketSavingConnection(Perspective::IS_SERVER); - *server_session = - new TestServerSession(DefaultQuicConfig(), *server_connection); - *server_stream = - new QuicCryptoServerStream(server_crypto_config, *server_session); - (*server_session)->InitializeSession(server_crypto_config); + *server_session = new TestQuicSpdyServerSession( + *server_connection, DefaultQuicConfig(), server_crypto_config); // We advance the clock initially because the default time is zero and the // strike register worries that we've just overflowed a uint32 time. diff --git a/chromium/net/quic/test_tools/quic_test_utils.h b/chromium/net/quic/test_tools/quic_test_utils.h index facdcbf6118..874bce5e124 100644 --- a/chromium/net/quic/test_tools/quic_test_utils.h +++ b/chromium/net/quic/test_tools/quic_test_utils.h @@ -66,6 +66,8 @@ void GenerateBody(std::string* body, int length); // Create an encrypted packet for testing. // If versions == nullptr, uses &QuicSupportedVersions(). +// Note that the packet is encrypted with NullEncrypter, so to decrypt the +// constructed packet, the framer must be set to use NullDecrypter. QuicEncryptedPacket* ConstructEncryptedPacket( QuicConnectionId connection_id, bool version_flag, @@ -96,8 +98,11 @@ QuicEncryptedPacket* ConstructEncryptedPacket( QuicPacketSequenceNumber sequence_number, const std::string& data); -// Create an encrypted packet for testing whose data portion contains -// a framing error. +// Create an encrypted packet for testing whose data portion erroneous. +// The specific way the data portion is erroneous is not specified, but +// it is an error that QuicFramer detects. +// Note that the packet is encrypted with NullEncrypter, so to decrypt the +// constructed packet, the framer must be set to use NullDecrypter. QuicEncryptedPacket* ConstructMisFramedEncryptedPacket( QuicConnectionId connection_id, bool version_flag, @@ -284,7 +289,7 @@ class MockConnectionVisitor : public QuicConnectionVisitorInterface { MOCK_METHOD1(OnCongestionWindowChange, void(QuicTime now)); MOCK_CONST_METHOD0(WillingAndAbleToWrite, bool()); MOCK_CONST_METHOD0(HasPendingHandshake, bool()); - MOCK_CONST_METHOD0(HasOpenDataStreams, bool()); + MOCK_CONST_METHOD0(HasOpenDynamicStreams, bool()); MOCK_METHOD1(OnSuccessfulVersionNegotiation, void(const QuicVersion& version)); MOCK_METHOD0(OnConfigNegotiated, void()); @@ -417,17 +422,19 @@ class PacketSavingConnection : public MockConnection { DISALLOW_COPY_AND_ASSIGN(PacketSavingConnection); }; -class MockSession : public QuicSession { +class MockQuicSpdySession : public QuicSpdySession { public: - explicit MockSession(QuicConnection* connection); - ~MockSession() override; + explicit MockQuicSpdySession(QuicConnection* connection); + ~MockQuicSpdySession() override; + + QuicCryptoStream* GetCryptoStream() override { return crypto_stream_.get(); } + MOCK_METHOD2(OnConnectionClosed, void(QuicErrorCode error, bool from_peer)); - MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id)); - MOCK_METHOD0(GetCryptoStream, QuicCryptoStream*()); - MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*()); + MOCK_METHOD1(CreateIncomingDynamicStream, QuicDataStream*(QuicStreamId id)); + MOCK_METHOD0(CreateOutgoingDynamicStream, QuicDataStream*()); MOCK_METHOD6(WritevData, QuicConsumedData(QuicStreamId id, - const IOVector& data, + const QuicIOVector& data, QuicStreamOffset offset, bool fin, FecProtection fec_protection, @@ -447,31 +454,36 @@ class MockSession : public QuicSession { using QuicSession::ActivateStream; private: - DISALLOW_COPY_AND_ASSIGN(MockSession); + scoped_ptr<QuicCryptoStream> crypto_stream_; + + DISALLOW_COPY_AND_ASSIGN(MockQuicSpdySession); }; -class TestSession : public QuicSession { +class TestQuicSpdyServerSession : public QuicSpdySession { public: - TestSession(QuicConnection* connection, const QuicConfig& config); - ~TestSession() override; + TestQuicSpdyServerSession(QuicConnection* connection, + const QuicConfig& config, + const QuicCryptoServerConfig* crypto_config); + ~TestQuicSpdyServerSession() override; - MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id)); - MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*()); + MOCK_METHOD1(CreateIncomingDynamicStream, QuicDataStream*(QuicStreamId id)); + MOCK_METHOD0(CreateOutgoingDynamicStream, QuicDataStream*()); - void SetCryptoStream(QuicCryptoStream* stream); - - QuicCryptoStream* GetCryptoStream() override; + QuicCryptoServerStream* GetCryptoStream() override; private: - QuicCryptoStream* crypto_stream_; + scoped_ptr<QuicCryptoServerStream> crypto_stream_; - DISALLOW_COPY_AND_ASSIGN(TestSession); + DISALLOW_COPY_AND_ASSIGN(TestQuicSpdyServerSession); }; -class TestClientSession : public QuicClientSessionBase { +class TestQuicSpdyClientSession : public QuicClientSessionBase { public: - TestClientSession(QuicConnection* connection, const QuicConfig& config); - ~TestClientSession() override; + TestQuicSpdyClientSession(QuicConnection* connection, + const QuicConfig& config, + const QuicServerId& server_id, + QuicCryptoClientConfig* crypto_config); + ~TestQuicSpdyClientSession() override; // QuicClientSessionBase MOCK_METHOD1(OnProofValid, @@ -479,18 +491,16 @@ class TestClientSession : public QuicClientSessionBase { MOCK_METHOD1(OnProofVerifyDetailsAvailable, void(const ProofVerifyDetails& verify_details)); - // TestClientSession - MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id)); - MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*()); - - void SetCryptoStream(QuicCryptoStream* stream); + // TestQuicSpdyClientSession + MOCK_METHOD1(CreateIncomingDynamicStream, QuicDataStream*(QuicStreamId id)); + MOCK_METHOD0(CreateOutgoingDynamicStream, QuicDataStream*()); - QuicCryptoStream* GetCryptoStream() override; + QuicCryptoClientStream* GetCryptoStream() override; private: - QuicCryptoStream* crypto_stream_; + scoped_ptr<QuicCryptoClientStream> crypto_stream_; - DISALLOW_COPY_AND_ASSIGN(TestClientSession); + DISALLOW_COPY_AND_ASSIGN(TestQuicSpdyClientSession); }; class MockPacketWriter : public QuicPacketWriter { @@ -710,17 +720,7 @@ class MockQuicConnectionDebugVisitor : public QuicConnectionDebugVisitor { void(const QuicPacketHeader&, StringPiece payload)); }; -class TestServerSession : public tools::QuicServerSession { - public: - TestServerSession(const QuicConfig& config, QuicConnection* connection); - ~TestServerSession() override; - - private: - DISALLOW_COPY_AND_ASSIGN(TestServerSession); -}; - -// Creates and sets up a QuicCryptoClientStream for testing, and all -// its associated state. +// Creates a client session for testing. // // server_id: The server id associated with this stream. // supports_stateless_rejects: Does this client support stateless rejects. @@ -734,19 +734,14 @@ class TestServerSession : public tools::QuicServerSession { // client_session. // client_session: Pointer reference for the newly created client // session. The new object will be owned by the caller. -// client_stream: Pointer reference for the newly created crypto -// client stream. The new object will be owned by the caller. -void SetupCryptoClientStreamForTest( - QuicServerId server_id, - bool supports_stateless_rejects, - QuicTime::Delta connection_start_time, - QuicCryptoClientConfig* crypto_client_config, - PacketSavingConnection** client_connection, - TestClientSession** client_session, - QuicCryptoClientStream** client_stream); - -// Creates and sets up a QuicCryptoServerStream for testing, and all -// its associated state. +void CreateClientSessionForTest(QuicServerId server_id, + bool supports_stateless_rejects, + QuicTime::Delta connection_start_time, + QuicCryptoClientConfig* crypto_client_config, + PacketSavingConnection** client_connection, + TestQuicSpdyClientSession** client_session); + +// Creates a server session for testing. // // server_id: The server id associated with this stream. // connection_start_time: The time to set for the connection clock. @@ -759,15 +754,11 @@ void SetupCryptoClientStreamForTest( // server_session. // server_session: Pointer reference for the newly created server // session. The new object will be owned by the caller. -// server_stream: Pointer reference for the newly created crypto -// server stream. The new object will be owned by the caller. -void SetupCryptoServerStreamForTest( - QuicServerId server_id, - QuicTime::Delta connection_start_time, - QuicCryptoServerConfig* crypto_server_config, - PacketSavingConnection** server_connection, - TestServerSession** server_session, - QuicCryptoServerStream** server_stream); +void CreateServerSessionForTest(QuicServerId server_id, + QuicTime::Delta connection_start_time, + QuicCryptoServerConfig* crypto_server_config, + PacketSavingConnection** server_connection, + TestQuicSpdyServerSession** server_session); } // namespace test } // namespace net diff --git a/chromium/net/quic/test_tools/simple_quic_framer.cc b/chromium/net/quic/test_tools/simple_quic_framer.cc index 639a2f6c40d..7784695d035 100644 --- a/chromium/net/quic/test_tools/simple_quic_framer.cc +++ b/chromium/net/quic/test_tools/simple_quic_framer.cc @@ -56,12 +56,12 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface { bool OnStreamFrame(const QuicStreamFrame& frame) override { // Save a copy of the data so it is valid after the packet is processed. - stream_data_.push_back(frame.GetDataAsString()); + string* string_data = new string(); + frame.data.AppendToString(string_data); + stream_data_.push_back(string_data); QuicStreamFrame stream_frame(frame); // Make sure that the stream frame points to this data. - stream_frame.data.Clear(); - stream_frame.data.Append(const_cast<char*>(stream_data_.back()->data()), - stream_data_.back()->size()); + stream_frame.data = StringPiece(*string_data); stream_frames_.push_back(stream_frame); return true; } |