diff options
author | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-14 16:25:33 +0000 |
---|---|---|
committer | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-14 16:25:33 +0000 |
commit | 14e8106ca5036b98170eb83c68fbc873773596a4 (patch) | |
tree | 9d41bc22f19a2da72bca94b37b2e40532770e62a /net | |
parent | ce585bf732e8bd85d6aa623c6445f73c5bb89ca4 (diff) | |
download | chromium_src-14e8106ca5036b98170eb83c68fbc873773596a4.zip chromium_src-14e8106ca5036b98170eb83c68fbc873773596a4.tar.gz chromium_src-14e8106ca5036b98170eb83c68fbc873773596a4.tar.bz2 |
Land Recent QUIC Changes
Handle versioning by closing the connection on version mismatch for now.
Merge internal change: 43606997
Number of cleanups from landing recent crypto changes.
Merge internal change: 43606111
Added delta_time_largest_observed to ReceivedPacketInfo to calculate accurate RTT.
Merge internal change: 43582099
Implement server-side QUIC key expansion. The derived keys are still not being used yet.
TODO: Code is in crypto_test_utils needs to be enabled.
Merge internal change: 43570937
Added AbandoningPacket to congestion control to avoid issue with FEC.
Merge internal change: 43570099
Wait infinite (aka wait for next ack) is not handled correctly.
Merge internal change: 43558636
Enable faster stats for QUIC.
Merge internal change: 43557310
Implement QUIC key expansion on the client side. The keys are not being used yet.
Merge internal change: 43515237
Add missing quic_stats files.
Track some connection stats.
Merge internal change: 43506869
Fix bug in WriteQueuedPackets
Merge internal change: 43499600
Small comment change in crypto_handshake's ProcessServerHello method.
Merge internal change: 43448804
R=rch@chromium.org
BUG=
Review URL: https://chromiumcodereview.appspot.com/12806002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@188096 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
77 files changed, 2009 insertions, 652 deletions
diff --git a/net/net.gyp b/net/net.gyp index 6980f78..49e0b0e 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -700,7 +700,6 @@ 'quic/crypto/crypto_framer.h', 'quic/crypto/crypto_handshake.cc', 'quic/crypto/crypto_handshake.h', - 'quic/crypto/crypto_protocol.cc', 'quic/crypto/crypto_protocol.h', 'quic/crypto/crypto_utils.cc', 'quic/crypto/crypto_utils.h', @@ -761,6 +760,8 @@ 'quic/quic_reliable_client_stream.h', 'quic/quic_session.cc', 'quic/quic_session.h', + 'quic/quic_stats.cc', + 'quic/quic_stats.h', 'quic/quic_stream_factory.cc', 'quic/quic_stream_factory.h', 'quic/quic_stream_sequencer.cc', @@ -1552,6 +1553,10 @@ 'quic/test_tools/mock_random.h', 'quic/test_tools/quic_connection_peer.cc', 'quic/test_tools/quic_connection_peer.h', + 'quic/test_tools/quic_framer_peer.cc', + 'quic/test_tools/quic_framer_peer.h', + 'quic/test_tools/quic_packet_creator_peer.cc', + 'quic/test_tools/quic_packet_creator_peer.h', 'quic/test_tools/quic_session_peer.cc', 'quic/test_tools/quic_session_peer.h', 'quic/test_tools/quic_test_utils.cc', diff --git a/net/quic/congestion_control/fix_rate_sender.cc b/net/quic/congestion_control/fix_rate_sender.cc index 924a7b4..19549a47 100644 --- a/net/quic/congestion_control/fix_rate_sender.cc +++ b/net/quic/congestion_control/fix_rate_sender.cc @@ -20,10 +20,14 @@ FixRateSender::FixRateSender(const QuicClock* clock) : bitrate_(QuicBandwidth::FromBytesPerSecond(kInitialBitrate)), fix_rate_leaky_bucket_(bitrate_), paced_sender_(bitrate_), - data_in_flight_(0) { + data_in_flight_(0), + latest_rtt_(QuicTime::Delta::Zero()) { DLOG(INFO) << "FixRateSender"; } +FixRateSender::~FixRateSender() { +} + void FixRateSender::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, QuicTime feedback_receive_time, @@ -42,7 +46,8 @@ void FixRateSender::OnIncomingQuicCongestionFeedbackFrame( void FixRateSender::OnIncomingAck( QuicPacketSequenceNumber /*acked_sequence_number*/, QuicByteCount bytes_acked, - QuicTime::Delta /*rtt*/) { + QuicTime::Delta rtt) { + latest_rtt_ = rtt; data_in_flight_ -= bytes_acked; } @@ -53,8 +58,7 @@ void FixRateSender::OnIncomingLoss(QuicTime /*ack_receive_time*/) { void FixRateSender::SentPacket(QuicTime sent_time, QuicPacketSequenceNumber /*sequence_number*/, QuicByteCount bytes, - bool is_retransmission, - bool /*has_retransmittable_data*/) { + bool is_retransmission) { fix_rate_leaky_bucket_.Add(sent_time, bytes); paced_sender_.SentPacket(sent_time, bytes); if (!is_retransmission) { @@ -62,8 +66,15 @@ void FixRateSender::SentPacket(QuicTime sent_time, } } -QuicTime::Delta FixRateSender::TimeUntilSend(QuicTime now, - bool /*is_retransmission*/) { +void FixRateSender::AbandoningPacket( + QuicPacketSequenceNumber /*sequence_number*/, + QuicByteCount /*abandoned_bytes*/) { +} + +QuicTime::Delta FixRateSender::TimeUntilSend( + QuicTime now, + bool /*is_retransmission*/, + bool /*has_retransmittable_data*/) { if (CongestionWindow() > fix_rate_leaky_bucket_.BytesPending(now)) { if (CongestionWindow() <= data_in_flight_) { // We need an ack before we send more. @@ -90,4 +101,9 @@ QuicBandwidth FixRateSender::BandwidthEstimate() { return bitrate_; } +QuicTime::Delta FixRateSender::SmoothedRtt() { + // TODO(satyamshekhar): Calculate and return smoothed rtt. + return latest_rtt_; +} + } // namespace net diff --git a/net/quic/congestion_control/fix_rate_sender.h b/net/quic/congestion_control/fix_rate_sender.h index d47462a..e81981a 100644 --- a/net/quic/congestion_control/fix_rate_sender.h +++ b/net/quic/congestion_control/fix_rate_sender.h @@ -19,9 +19,9 @@ namespace net { class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface { - public: explicit FixRateSender(const QuicClock* clock); + virtual ~FixRateSender(); // Start implementation of SendAlgorithmInterface. virtual void OnIncomingQuicCongestionFeedbackFrame( @@ -36,11 +36,14 @@ class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface { virtual void SentPacket(QuicTime sent_time, QuicPacketSequenceNumber equence_number, QuicByteCount bytes, - bool is_retransmission, - bool has_retransmittable_data) OVERRIDE; + bool is_retransmission) OVERRIDE; + virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number, + QuicByteCount abandoned_bytes) OVERRIDE; virtual QuicTime::Delta TimeUntilSend(QuicTime now, - bool is_retransmission) OVERRIDE; + bool is_retransmission, + bool has_retransmittable_data) OVERRIDE; virtual QuicBandwidth BandwidthEstimate() OVERRIDE; + virtual QuicTime::Delta SmoothedRtt() OVERRIDE; // End implementation of SendAlgorithmInterface. private: @@ -50,6 +53,7 @@ class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface { LeakyBucket fix_rate_leaky_bucket_; PacedSender paced_sender_; QuicByteCount data_in_flight_; + QuicTime::Delta latest_rtt_; DISALLOW_COPY_AND_ASSIGN(FixRateSender); }; diff --git a/net/quic/congestion_control/fix_rate_test.cc b/net/quic/congestion_control/fix_rate_test.cc index d32e336..bbbb3cd 100644 --- a/net/quic/congestion_control/fix_rate_test.cc +++ b/net/quic/congestion_control/fix_rate_test.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// Test for FixRate sender and receiver. + #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "net/quic/congestion_control/fix_rate_receiver.h" @@ -24,7 +26,6 @@ class FixRateReceiverPeer : public FixRateReceiver { } }; -const bool kHasRetransmittableData = true; class FixRateTest : public ::testing::Test { protected: FixRateTest() @@ -60,24 +61,21 @@ TEST_F(FixRateTest, SenderAPI) { sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), unused_bandwidth_, unused_packet_map_); EXPECT_EQ(300000, sender_->BandwidthEstimate().ToBytesPerSecond()); - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero()); - sender_->SentPacket(clock_.Now(), 1, kMaxPacketSize, false, - kHasRetransmittableData); - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero()); - sender_->SentPacket(clock_.Now(), 2, kMaxPacketSize, false, - kHasRetransmittableData); - sender_->SentPacket(clock_.Now(), 3, 600, false, - kHasRetransmittableData); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); + sender_->SentPacket(clock_.Now(), 1, kMaxPacketSize, false); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); + sender_->SentPacket(clock_.Now(), 2, kMaxPacketSize, false); + sender_->SentPacket(clock_.Now(), 3, 600, false); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), - sender_->TimeUntilSend(clock_.Now(), false)); + sender_->TimeUntilSend(clock_.Now(), false, true)); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2)); EXPECT_EQ(QuicTime::Delta::Infinite(), - sender_->TimeUntilSend(clock_.Now(), false)); + sender_->TimeUntilSend(clock_.Now(), false, true)); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8)); sender_->OnIncomingAck(1, kMaxPacketSize, rtt_); sender_->OnIncomingAck(2, kMaxPacketSize, rtt_); sender_->OnIncomingAck(3, 600, rtt_); - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); } TEST_F(FixRateTest, FixRatePacing) { @@ -92,13 +90,12 @@ TEST_F(FixRateTest, FixRatePacing) { QuicTime acc_advance_time(QuicTime::Zero()); QuicPacketSequenceNumber sequence_number = 0; for (int i = 0; i < num_packets; i += 2) { - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero()); - sender_->SentPacket(clock_.Now(), sequence_number++, packet_size, false, - kHasRetransmittableData); - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero()); - sender_->SentPacket(clock_.Now(), sequence_number++, packet_size, false, - kHasRetransmittableData); - QuicTime::Delta advance_time = sender_->TimeUntilSend(clock_.Now(), false); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); + sender_->SentPacket(clock_.Now(), sequence_number++, packet_size, false); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); + sender_->SentPacket(clock_.Now(), sequence_number++, packet_size, false); + QuicTime::Delta advance_time = sender_->TimeUntilSend(clock_.Now(), false, + true); clock_.AdvanceTime(advance_time); sender_->OnIncomingAck(sequence_number - 1, packet_size, rtt_); sender_->OnIncomingAck(sequence_number - 2, packet_size, rtt_); diff --git a/net/quic/congestion_control/hybrid_slow_start.cc b/net/quic/congestion_control/hybrid_slow_start.cc index 8a42524..62b28f8 100644 --- a/net/quic/congestion_control/hybrid_slow_start.cc +++ b/net/quic/congestion_control/hybrid_slow_start.cc @@ -105,4 +105,9 @@ bool HybridSlowStart::Exit() { return false; } +QuicTime::Delta HybridSlowStart::SmoothedRtt() { + // TODO(satyamshekhar): Calculate and return smooth average of rtt over time. + return current_rtt_; +} + } // namespace net diff --git a/net/quic/congestion_control/hybrid_slow_start.h b/net/quic/congestion_control/hybrid_slow_start.h index cee9c73..b0e4248 100644 --- a/net/quic/congestion_control/hybrid_slow_start.h +++ b/net/quic/congestion_control/hybrid_slow_start.h @@ -46,6 +46,8 @@ class NET_EXPORT_PRIVATE HybridSlowStart { bool started() { return started_; } + QuicTime::Delta SmoothedRtt(); + private: const QuicClock* clock_; bool started_; diff --git a/net/quic/congestion_control/quic_congestion_manager.cc b/net/quic/congestion_control/quic_congestion_manager.cc index e5c6973..79b6c4d 100644 --- a/net/quic/congestion_control/quic_congestion_manager.cc +++ b/net/quic/congestion_control/quic_congestion_manager.cc @@ -35,7 +35,8 @@ QuicCongestionManager::QuicCongestionManager( : clock_(clock), receive_algorithm_(ReceiveAlgorithmInterface::Create(clock, type)), send_algorithm_(SendAlgorithmInterface::Create(clock, type)), - largest_missing_(0) { + largest_missing_(0), + current_rtt_(QuicTime::Delta::Infinite()) { } QuicCongestionManager::~QuicCongestionManager() { @@ -45,11 +46,10 @@ QuicCongestionManager::~QuicCongestionManager() { void QuicCongestionManager::SentPacket(QuicPacketSequenceNumber sequence_number, QuicTime sent_time, QuicByteCount bytes, - bool is_retransmission, - bool has_retransmittable_data) { + bool is_retransmission) { DCHECK(!ContainsKey(pending_packets_, sequence_number)); send_algorithm_->SentPacket(sent_time, sequence_number, bytes, - is_retransmission, has_retransmittable_data); + is_retransmission); packet_history_map_[sequence_number] = new class SendAlgorithmInterface::SentPacket(bytes, sent_time); @@ -57,6 +57,16 @@ void QuicCongestionManager::SentPacket(QuicPacketSequenceNumber sequence_number, CleanupPacketHistory(); } +// Called when a packet is timed out. +void QuicCongestionManager::AbandoningPacket( + QuicPacketSequenceNumber sequence_number) { + PendingPacketsMap::iterator it = pending_packets_.find(sequence_number); + if (it != pending_packets_.end()) { + send_algorithm_->AbandoningPacket(sequence_number, it->second); + pending_packets_.erase(it); + } +} + void QuicCongestionManager::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& frame, QuicTime feedback_receive_time) { QuicBandwidth sent_bandwidth = SentBandwidth(feedback_receive_time); @@ -68,12 +78,17 @@ void QuicCongestionManager::OnIncomingAckFrame(const QuicAckFrame& frame, QuicTime ack_receive_time) { // We calculate the RTT based on the highest ACKed sequence number, the lower // sequence numbers will include the ACK aggregation delay. - QuicTime::Delta rtt = QuicTime::Delta::Zero(); SendAlgorithmInterface::SentPacketsMap::iterator history_it = packet_history_map_.find(frame.received_info.largest_observed); - if (history_it != packet_history_map_.end()) { - // TODO(pwestin): we need to add the delta to the feedback message. - rtt = ack_receive_time.Subtract(history_it->second->SendTimestamp()); + // TODO(satyamshekhar): largest_observed might be missing. + if (history_it != packet_history_map_.end() && + !frame.received_info.delta_time_largest_observed.IsInfinite()) { + QuicTime::Delta send_delta = ack_receive_time.Subtract( + history_it->second->SendTimestamp()); + if (send_delta > frame.received_info.delta_time_largest_observed) { + current_rtt_ = send_delta.Subtract( + frame.received_info.delta_time_largest_observed); + } } // We want to. // * Get all packets lower(including) than largest_observed @@ -89,9 +104,7 @@ void QuicCongestionManager::OnIncomingAckFrame(const QuicAckFrame& frame, QuicPacketSequenceNumber sequence_number = it->first; if (!IsAwaitingPacket(frame.received_info, sequence_number)) { // Not missing, hence implicitly acked. - send_algorithm_->OnIncomingAck(sequence_number, - it->second, - rtt); + send_algorithm_->OnIncomingAck(sequence_number, it->second, current_rtt_); pending_packets_.erase(it++); // Must be incremented post to work. } else { if (sequence_number > largest_missing_) { @@ -107,9 +120,12 @@ void QuicCongestionManager::OnIncomingAckFrame(const QuicAckFrame& frame, } } -QuicTime::Delta QuicCongestionManager::TimeUntilSend(QuicTime now, - bool is_retransmission) { - return send_algorithm_->TimeUntilSend(now, is_retransmission); +QuicTime::Delta QuicCongestionManager::TimeUntilSend( + QuicTime now, + bool is_retransmission, + bool has_retransmittable_data) { + return send_algorithm_->TimeUntilSend(now, is_retransmission, + has_retransmittable_data); } bool QuicCongestionManager::GenerateCongestionFeedback( @@ -126,15 +142,19 @@ void QuicCongestionManager::RecordIncomingPacket( revived); } -// static +const QuicTime::Delta QuicCongestionManager::rtt() { + return current_rtt_; +} + const QuicTime::Delta QuicCongestionManager::DefaultRetransmissionTime() { return QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs); } -// static const QuicTime::Delta QuicCongestionManager::GetRetransmissionDelay( size_t unacked_packets_count, size_t number_retransmissions) { + // TODO(pwestin): This should take the RTT into account instead of a hard + // coded kDefaultRetransmissionTimeMs. Ideally the variance of the RTT too. if (unacked_packets_count <= kTailDropWindowSize) { return QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs); } @@ -144,6 +164,10 @@ const QuicTime::Delta QuicCongestionManager::GetRetransmissionDelay( (1 << min<size_t>(number_retransmissions, kMaxRetransmissions))); } +const QuicTime::Delta QuicCongestionManager::SmoothedRtt() { + return send_algorithm_->SmoothedRtt(); +} + QuicBandwidth QuicCongestionManager::SentBandwidth( QuicTime feedback_receive_time) const { const QuicTime::Delta kBitrateSmoothingPeriod = diff --git a/net/quic/congestion_control/quic_congestion_manager.h b/net/quic/congestion_control/quic_congestion_manager.h index 981c14f..4560957 100644 --- a/net/quic/congestion_control/quic_congestion_manager.h +++ b/net/quic/congestion_control/quic_congestion_manager.h @@ -46,8 +46,10 @@ class QuicCongestionManager { virtual void SentPacket(QuicPacketSequenceNumber sequence_number, QuicTime sent_time, QuicByteCount bytes, - bool is_retransmission, - bool has_retransmittable_data); + bool is_retransmission); + + // Called when a packet is timed out. + virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number); // Calculate the time until we can send the next packet to the wire. // Note 1: When kUnknownWaitTime is returned, there is no need to poll @@ -55,7 +57,8 @@ class QuicCongestionManager { // Note 2: Send algorithms may or may not use |retransmit| in their // calculations. virtual QuicTime::Delta TimeUntilSend(QuicTime now, - bool is_retransmission); + bool is_retransmission, + bool has_retransmittable_data); // Should be called before sending an ACK packet, to decide if we need // to attach a QuicCongestionFeedbackFrame block. @@ -65,7 +68,7 @@ class QuicCongestionManager { QuicCongestionFeedbackFrame* feedback); // Should be called for each incoming packet. - // bytes: the packet size in bytes including IP headers. + // bytes: the packet size in bytes including Quic Headers. // sequence_number: the unique sequence number from the QUIC packet header. // timestamp: the arrival time of the packet. // revived: true if the packet was lost and then recovered with help of a @@ -81,14 +84,21 @@ class QuicCongestionManager { size_t unacked_packets_count, size_t number_retransmissions); + // Returns the estimated smoothed RTT calculated by the congestion algorithm. + const QuicTime::Delta SmoothedRtt(); + + // Returns the estimated bandwidth calculated by the congestion algorithm. + QuicBandwidth BandwidthEstimate(); + private: friend class test::QuicConnectionPeer; friend class test::QuicCongestionManagerPeer; typedef std::map<QuicPacketSequenceNumber, size_t> PendingPacketsMap; + // Get the current(last) rtt. Infinite is returned if invalid. + const QuicTime::Delta rtt(); + QuicBandwidth SentBandwidth(QuicTime feedback_receive_time) const; - // TODO(pwestin): Currently only used for testing. How do we surface this? - QuicBandwidth BandwidthEstimate(); void CleanupPacketHistory(); const QuicClock* clock_; @@ -97,6 +107,7 @@ class QuicCongestionManager { SendAlgorithmInterface::SentPacketsMap packet_history_map_; PendingPacketsMap pending_packets_; QuicPacketSequenceNumber largest_missing_; + QuicTime::Delta current_rtt_; DISALLOW_COPY_AND_ASSIGN(QuicCongestionManager); }; diff --git a/net/quic/congestion_control/send_algorithm_interface.h b/net/quic/congestion_control/send_algorithm_interface.h index 95843d1..effa671 100644 --- a/net/quic/congestion_control/send_algorithm_interface.h +++ b/net/quic/congestion_control/send_algorithm_interface.h @@ -58,16 +58,23 @@ class NET_EXPORT_PRIVATE SendAlgorithmInterface { virtual void SentPacket(QuicTime sent_time, QuicPacketSequenceNumber sequence_number, QuicByteCount bytes, - bool is_retransmission, - bool has_retransmittable_data) = 0; + bool is_retransmission) = 0; + + // Called when a packet is timed out. + virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number, + QuicByteCount abandoned_bytes) = 0; // Calculate the time until we can send the next packet. virtual QuicTime::Delta TimeUntilSend(QuicTime now, - bool is_retransmission) = 0; + bool is_retransmission, + bool has_retransmittable_data) = 0; // What's the current estimated bandwidth in bytes per second. // Returns 0 when it does not have an estimate. virtual QuicBandwidth BandwidthEstimate() = 0; + + // TODO(satyamshekhar): Monitor MinRtt. + virtual QuicTime::Delta SmoothedRtt() = 0; }; } // namespace net diff --git a/net/quic/congestion_control/tcp_cubic_sender.cc b/net/quic/congestion_control/tcp_cubic_sender.cc index 4c82a35..70367ff 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.cc +++ b/net/quic/congestion_control/tcp_cubic_sender.cc @@ -14,6 +14,7 @@ const QuicByteCount kDefaultReceiveWindow = 64000; const int64 kInitialCongestionWindow = 10; const int64 kMaxCongestionWindow = 10000; const int kMaxBurstLength = 3; +const int kInitialRttMs = 60; // At a typical RTT 60 ms. }; TcpCubicSender::TcpCubicSender(const QuicClock* clock, bool reno) @@ -84,11 +85,8 @@ void TcpCubicSender::OnIncomingLoss(QuicTime /*ack_receive_time*/) { void TcpCubicSender::SentPacket(QuicTime /*sent_time*/, QuicPacketSequenceNumber sequence_number, QuicByteCount bytes, - bool is_retransmission, - bool has_retransmittable_data) { - if (!is_retransmission && has_retransmittable_data) { - bytes_in_flight_ += bytes; - } + bool is_retransmission) { + bytes_in_flight_ += bytes; if (!is_retransmission && update_end_sequence_number_) { end_sequence_number_ = sequence_number; if (AvailableCongestionWindow() == 0) { @@ -98,10 +96,16 @@ void TcpCubicSender::SentPacket(QuicTime /*sent_time*/, } } +void TcpCubicSender::AbandoningPacket(QuicPacketSequenceNumber sequence_number, + QuicByteCount abandoned_bytes) { + bytes_in_flight_ -= abandoned_bytes; +} + QuicTime::Delta TcpCubicSender::TimeUntilSend(QuicTime now, - bool is_retransmission) { - if (is_retransmission) { - // For TCP we can always send a retransmission immediately. + bool is_retransmission, + bool has_retransmittable_data) { + if (is_retransmission || !has_retransmittable_data) { + // For TCP we can always send a retransmission and/or an ACK immediately. return QuicTime::Delta::Zero(); } if (AvailableCongestionWindow() == 0) { @@ -130,6 +134,14 @@ QuicBandwidth TcpCubicSender::BandwidthEstimate() { return QuicBandwidth::Zero(); } +QuicTime::Delta TcpCubicSender::SmoothedRtt() { + // TODO(satyamshekhar): Return the smoothed averaged RTT. + if (delay_min_.IsZero()) { + return QuicTime::Delta::FromMilliseconds(kInitialRttMs); + } + return delay_min_; +} + void TcpCubicSender::Reset() { delay_min_ = QuicTime::Delta::Zero(); hybrid_slow_start_.Restart(); @@ -192,10 +204,12 @@ void TcpCubicSender::OnTimeOut() { } void TcpCubicSender::AckAccounting(QuicTime::Delta rtt) { - if (rtt.ToMicroseconds() <= 0) { - // RTT can't be 0 or negative. + if (rtt.IsInfinite() || rtt.IsZero()) { return; } + // RTT can't be negative. + DCHECK_LT(0, rtt.ToMicroseconds()); + // TODO(pwestin): Discard delay samples right after fast recovery, // during 1 second?. diff --git a/net/quic/congestion_control/tcp_cubic_sender.h b/net/quic/congestion_control/tcp_cubic_sender.h index 85f0988..6943044 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.h +++ b/net/quic/congestion_control/tcp_cubic_sender.h @@ -42,11 +42,14 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface { virtual void SentPacket(QuicTime sent_time, QuicPacketSequenceNumber sequence_number, QuicByteCount bytes, - bool is_retransmission, - bool has_retransmittable_data) OVERRIDE; + bool is_retransmission) OVERRIDE; + virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number, + QuicByteCount abandoned_bytes) OVERRIDE; virtual QuicTime::Delta TimeUntilSend(QuicTime now, - bool is_retransmission) OVERRIDE; + bool is_retransmission, + bool has_retransmittable_data) OVERRIDE; virtual QuicBandwidth BandwidthEstimate() OVERRIDE; + virtual QuicTime::Delta SmoothedRtt() OVERRIDE; // End implementation of SendAlgorithmInterface. private: diff --git a/net/quic/congestion_control/tcp_cubic_sender_test.cc b/net/quic/congestion_control/tcp_cubic_sender_test.cc index 395088c..2aab55a 100644 --- a/net/quic/congestion_control/tcp_cubic_sender_test.cc +++ b/net/quic/congestion_control/tcp_cubic_sender_test.cc @@ -40,10 +40,10 @@ class TcpCubicSenderTest : public ::testing::Test { while (bytes_to_send > 0) { QuicByteCount bytes_in_packet = std::min(kMaxPacketSize, bytes_to_send); sender_->SentPacket(clock_.Now(), sequence_number_++, bytes_in_packet, - false, true); + false); bytes_to_send -= bytes_in_packet; if (bytes_to_send > 0) { - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); } } } @@ -73,31 +73,31 @@ TEST_F(TcpCubicSenderTest, SimpleSender) { EXPECT_EQ(kDefaultWindowTCP, sender_->AvailableCongestionWindow()); // At startup make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), fake_bandwidth_, not_used_); // Make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); // And that window is un-affected. EXPECT_EQ(kDefaultWindowTCP, sender_->AvailableCongestionWindow()); // A retransmitt should always retun 0. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), true).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), true, true).IsZero()); } TEST_F(TcpCubicSenderTest, ExponentialSlowStart) { const int kNumberOfAck = 20; QuicCongestionFeedbackFrame feedback; // At startup make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), fake_bandwidth_, not_used_); // Make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); for (int n = 0; n < kNumberOfAck; ++n) { // Send our full congestion window. @@ -118,13 +118,13 @@ TEST_F(TcpCubicSenderTest, SlowStartAckTrain) { const int kNumberOfAck = 65; QuicCongestionFeedbackFrame feedback; // At startup make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), fake_bandwidth_, not_used_); // Make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); for (int n = 0; n < kNumberOfAck; ++n) { // Send our full congestion window. @@ -159,13 +159,13 @@ TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) { const int kNumberOfAck = 10; QuicCongestionFeedbackFrame feedback; // At startup make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), fake_bandwidth_, not_used_); // Make sure we can send. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsZero()); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsZero()); for (int i = 0; i < kNumberOfAck; ++i) { // Send our full congestion window. @@ -180,7 +180,7 @@ TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) { sender_->OnIncomingLoss(clock_.Now()); // Make sure that we should not send right now. - EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false).IsInfinite()); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), false, true).IsInfinite()); // We should now have fallen out of slow start. // We expect window to be cut in half. diff --git a/net/quic/crypto/crypto_framer.cc b/net/quic/crypto/crypto_framer.cc index 86b2df7..e2909d4 100644 --- a/net/quic/crypto/crypto_framer.cc +++ b/net/quic/crypto/crypto_framer.cc @@ -4,9 +4,9 @@ #include "net/quic/crypto/crypto_framer.h" +#include "net/quic/crypto/crypto_handshake.h" #include "net/quic/quic_data_reader.h" #include "net/quic/quic_data_writer.h" -#include "net/quic/quic_protocol.h" using base::StringPiece; diff --git a/net/quic/crypto/crypto_framer.h b/net/quic/crypto/crypto_framer.h index efa5259..1d289a6 100644 --- a/net/quic/crypto/crypto_framer.h +++ b/net/quic/crypto/crypto_framer.h @@ -6,7 +6,6 @@ #define NET_QUIC_CRYPTO_CRYPTO_FRAMER_H_ #include <map> -#include <vector> #include "base/basictypes.h" #include "base/logging.h" @@ -21,6 +20,7 @@ namespace net { class CryptoFramer; class QuicDataReader; class QuicData; +struct CryptoHandshakeMessage; class NET_EXPORT_PRIVATE CryptoFramerVisitorInterface { public: diff --git a/net/quic/crypto/crypto_framer_test.cc b/net/quic/crypto/crypto_framer_test.cc index 93d7d58..58c4f45 100644 --- a/net/quic/crypto/crypto_framer_test.cc +++ b/net/quic/crypto/crypto_framer_test.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "net/quic/crypto/crypto_framer.h" +#include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/test_tools/quic_test_utils.h" diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc index 7faa21d..9d8c48f 100644 --- a/net/quic/crypto/crypto_handshake.cc +++ b/net/quic/crypto/crypto_handshake.cc @@ -6,12 +6,15 @@ #include "base/memory/scoped_ptr.h" #include "base/stl_util.h" +#include "crypto/hkdf.h" #include "crypto/secure_hash.h" #include "net/base/net_util.h" #include "net/quic/crypto/crypto_framer.h" #include "net/quic/crypto/crypto_utils.h" #include "net/quic/crypto/curve25519_key_exchange.h" #include "net/quic/crypto/key_exchange.h" +#include "net/quic/crypto/quic_decrypter.h" +#include "net/quic/crypto/quic_encrypter.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_protocol.h" @@ -25,6 +28,8 @@ namespace net { // implement. static const uint16 kVersion = 0; +static const char kLabel[] = "QUIC key expansion"; + using crypto::SecureHash; QuicServerConfigProtobuf::QuicServerConfigProtobuf() { @@ -34,6 +39,169 @@ QuicServerConfigProtobuf::~QuicServerConfigProtobuf() { STLDeleteElements(&keys_); } +CryptoHandshakeMessage::CryptoHandshakeMessage() {} + +CryptoHandshakeMessage::CryptoHandshakeMessage( + const CryptoHandshakeMessage& other) + : tag(other.tag), + tag_value_map(other.tag_value_map) { + // Don't copy serialized_. scoped_ptr doesn't have a copy constructor. + // The new object can reconstruct serialized_ lazily. +} + +CryptoHandshakeMessage::~CryptoHandshakeMessage() {} + +CryptoHandshakeMessage& CryptoHandshakeMessage::operator=( + const CryptoHandshakeMessage& other) { + tag = other.tag; + tag_value_map = other.tag_value_map; + // Don't copy serialized_. scoped_ptr doesn't have an assignment operator. + // However, invalidate serialized_. + serialized_.reset(); + return *this; +} + +const QuicData& CryptoHandshakeMessage::GetSerialized() const { + if (!serialized_.get()) { + serialized_.reset(CryptoFramer::ConstructHandshakeMessage(*this)); + } + return *serialized_.get(); +} + +void CryptoHandshakeMessage::SetTaglist(CryptoTag tag, ...) { + // Warning, if sizeof(CryptoTag) > sizeof(int) then this function will break + // because the terminating 0 will only be promoted to int. + COMPILE_ASSERT(sizeof(CryptoTag) <= sizeof(int), + crypto_tag_not_be_larger_than_int_or_varargs_will_break); + + vector<CryptoTag> tags; + va_list ap; + + va_start(ap, tag); + for (;;) { + CryptoTag tag = va_arg(ap, CryptoTag); + if (tag == 0) { + break; + } + tags.push_back(tag); + } + + // Because of the way that we keep tags in memory, we can copy the contents + // of the vector and get the correct bytes in wire format. See + // crypto_protocol.h. This assumes that the system is little-endian. + SetVector(tag, tags); + + va_end(ap); +} + +QuicErrorCode CryptoHandshakeMessage::GetTaglist(CryptoTag tag, + const CryptoTag** out_tags, + size_t* out_len) const { + CryptoTagValueMap::const_iterator it = tag_value_map.find(tag); + QuicErrorCode ret = QUIC_NO_ERROR; + + if (it == tag_value_map.end()) { + ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } else if (it->second.size() % sizeof(CryptoTag) != 0) { + ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (ret != QUIC_NO_ERROR) { + *out_tags = NULL; + *out_len = 0; + return ret; + } + + *out_tags = reinterpret_cast<const CryptoTag*>(it->second.data()); + *out_len = it->second.size() / sizeof(CryptoTag); + return ret; +} + +bool CryptoHandshakeMessage::GetStringPiece(CryptoTag tag, + StringPiece* out) const { + CryptoTagValueMap::const_iterator it = tag_value_map.find(tag); + if (it == tag_value_map.end()) { + return false; + } + *out = it->second; + return true; +} + +QuicErrorCode CryptoHandshakeMessage::GetNthValue16( + CryptoTag tag, + unsigned index, + StringPiece* out) const { + StringPiece value; + if (!GetStringPiece(tag, &value)) { + return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } + + for (unsigned i = 0;; i++) { + if (value.empty()) { + return QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND; + } + if (value.size() < 2) { + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + const unsigned char* data = + reinterpret_cast<const unsigned char*>(value.data()); + size_t size = static_cast<size_t>(data[0]) | + (static_cast<size_t>(data[1]) << 8); + value.remove_prefix(2); + + if (value.size() < size) { + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (i == index) { + *out = StringPiece(value.data(), size); + return QUIC_NO_ERROR; + } + + value.remove_prefix(size); + } +} + +bool CryptoHandshakeMessage::GetString(CryptoTag tag, string* out) const { + CryptoTagValueMap::const_iterator it = tag_value_map.find(tag); + if (it == tag_value_map.end()) { + return false; + } + *out = it->second; + return true; +} + +QuicErrorCode CryptoHandshakeMessage::GetUint16(CryptoTag tag, + uint16* out) const { + return GetPOD(tag, out, sizeof(uint16)); +} + +QuicErrorCode CryptoHandshakeMessage::GetUint32(CryptoTag tag, + uint32* out) const { + return GetPOD(tag, out, sizeof(uint32)); +} + +QuicErrorCode CryptoHandshakeMessage::GetPOD( + CryptoTag tag, void* out, size_t len) const { + CryptoTagValueMap::const_iterator it = tag_value_map.find(tag); + QuicErrorCode ret = QUIC_NO_ERROR; + + if (it == tag_value_map.end()) { + ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } else if (it->second.size() != len) { + ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (ret != QUIC_NO_ERROR) { + memset(out, 0, len); + return ret; + } + + memcpy(out, it->second.data(), len); + return ret; +} + QuicCryptoNegotiatedParams::QuicCryptoNegotiatedParams() : version(0), key_exchange(0), @@ -43,7 +211,6 @@ QuicCryptoNegotiatedParams::QuicCryptoNegotiatedParams() QuicCryptoNegotiatedParams::~QuicCryptoNegotiatedParams() { } - QuicCryptoConfig::QuicCryptoConfig() : version(0) { } @@ -56,7 +223,7 @@ bool QuicCryptoConfig::ProcessPeerHandshake( const CryptoHandshakeMessage& peer_msg, CryptoUtils::Priority priority, QuicCryptoNegotiatedParams* out_params, - string *error_details) const { + string* error_details) const { if (peer_msg.GetUint16(kVERS, &out_params->version) != QUIC_NO_ERROR || out_params->version != kVersion) { if (error_details) { @@ -125,6 +292,10 @@ bool QuicCryptoConfig::ProcessPeerHandshake( return true; } +QuicCryptoClientConfig::QuicCryptoClientConfig() + : hkdf_info(kLabel, arraysize(kLabel)) { +} + void QuicCryptoClientConfig::SetDefaults(QuicRandom* rand) { // Version must be 0. version = kVersion; @@ -183,6 +354,7 @@ void QuicCryptoClientConfig::FillClientHello(const string& nonce, QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( const CryptoHandshakeMessage& server_hello, + const string& nonce, QuicCryptoNegotiatedParams* out_params, string* error_details) { if (server_hello.tag != kSHLO) { @@ -198,8 +370,7 @@ QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( scoped_ptr<CryptoHandshakeMessage> scfg( CryptoFramer::ParseMessage(scfg_bytes)); - if (!scfg.get() || - scfg->tag != kSCFG) { + if (!scfg.get() || scfg->tag != kSCFG) { *error_details = "Invalid SCFG"; return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } @@ -209,11 +380,29 @@ QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } + hkdf_info.append(scfg_bytes.data(), scfg_bytes.size()); + + out_params->encrypter.reset(QuicEncrypter::Create(out_params->aead)); + out_params->decrypter.reset(QuicDecrypter::Create(out_params->aead)); + size_t key_bytes = out_params->encrypter->GetKeySize(); + size_t nonce_prefix_bytes = out_params->encrypter->GetNoncePrefixSize(); + uint32 key_length_in_bits = 8 * 2 * (key_bytes + nonce_prefix_bytes); + hkdf_info.append(reinterpret_cast<char*>(&key_length_in_bits), + sizeof(key_length_in_bits)); + + crypto::HKDF hkdf(out_params->premaster_secret, nonce, + hkdf_info, key_bytes, nonce_prefix_bytes); + out_params->encrypter->SetKey(hkdf.client_write_key()); + out_params->encrypter->SetNoncePrefix(hkdf.client_write_iv()); + out_params->decrypter->SetKey(hkdf.server_write_key()); + out_params->decrypter->SetNoncePrefix(hkdf.server_write_iv()); + return QUIC_NO_ERROR; } -QuicCryptoServerConfig::QuicCryptoServerConfig() { +QuicCryptoServerConfig::QuicCryptoServerConfig() + : hkdf_info(kLabel, arraysize(kLabel)) { } QuicCryptoServerConfig::~QuicCryptoServerConfig() { @@ -400,6 +589,31 @@ bool QuicCryptoServerConfig::ProcessClientHello( return false; } + StringPiece client_nonce; + if (!client_hello.GetStringPiece(kNONC, &client_nonce)) { + return false; + } + + const QuicData& client_hello_serialized = client_hello.GetSerialized(); + hkdf_info.append(client_hello_serialized.data(), + client_hello_serialized.length()); + hkdf_info.append(config->serialized); + + out_params->encrypter.reset(QuicEncrypter::Create(out_params->aead)); + out_params->decrypter.reset(QuicDecrypter::Create(out_params->aead)); + size_t key_bytes = out_params->encrypter->GetKeySize(); + size_t nonce_prefix_bytes = out_params->encrypter->GetNoncePrefixSize(); + uint32 key_length_in_bits = 8 * 2 * (key_bytes + nonce_prefix_bytes); + hkdf_info.append(reinterpret_cast<char*>(&key_length_in_bits), + sizeof(key_length_in_bits)); + + crypto::HKDF hkdf(out_params->premaster_secret, client_nonce, + hkdf_info, key_bytes, nonce_prefix_bytes); + out_params->encrypter->SetKey(hkdf.server_write_key()); + out_params->encrypter->SetNoncePrefix(hkdf.server_write_iv()); + out_params->decrypter->SetKey(hkdf.client_write_key()); + out_params->decrypter->SetNoncePrefix(hkdf.client_write_iv()); + // TODO(agl): This is obviously missing most of the handshake. out->tag = kSHLO; out->tag_value_map[kNONC] = nonce; diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h index aca4aa0..92f8f9b 100644 --- a/net/quic/crypto/crypto_handshake.h +++ b/net/quic/crypto/crypto_handshake.h @@ -7,7 +7,10 @@ #include <map> #include <string> +#include <vector> +#include "base/memory/scoped_ptr.h" +#include "base/string_piece.h" #include "net/base/net_export.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/crypto_utils.h" @@ -15,9 +18,84 @@ namespace net { class KeyExchange; +class QuicDecrypter; +class QuicEncrypter; class QuicRandom; class QuicClock; +// An intermediate format of a handshake message that's convenient for a +// CryptoFramer to serialize from or parse into. +struct NET_EXPORT_PRIVATE CryptoHandshakeMessage { + CryptoHandshakeMessage(); + CryptoHandshakeMessage(const CryptoHandshakeMessage& other); + ~CryptoHandshakeMessage(); + + CryptoHandshakeMessage& operator=(const CryptoHandshakeMessage& other); + + // GetSerialized returns the serialized form of this message and caches the + // result. Subsequently altering the message does not invalidate the cache. + const QuicData& GetSerialized() const; + + // SetValue sets an element with the given tag to the raw, memory contents of + // |v|. + template<class T> void SetValue(CryptoTag tag, const T& v) { + tag_value_map[tag] = std::string(reinterpret_cast<const char*>(&v), + sizeof(v)); + } + + // SetVector sets an element with the given tag to the raw contents of an + // array of elements in |v|. + template<class T> void SetVector(CryptoTag tag, const std::vector<T>& v) { + if (v.empty()) { + tag_value_map[tag] = std::string(); + } else { + tag_value_map[tag] = std::string(reinterpret_cast<const char*>(&v[0]), + v.size() * sizeof(T)); + } + } + + // SetTaglist sets an element with the given tag to contain a list of tags, + // passed as varargs. The argument list must be terminated with a 0 element. + void SetTaglist(CryptoTag tag, ...); + + // GetTaglist finds an element with the given tag containing zero or more + // tags. If such a tag doesn't exist, it returns false. Otherwise it sets + // |out_tags| and |out_len| to point to the array of tags and returns true. + // The array points into the CryptoHandshakeMessage and is valid only for as + // long as the CryptoHandshakeMessage exists and is not modified. + QuicErrorCode GetTaglist(CryptoTag tag, const CryptoTag** out_tags, + size_t* out_len) const; + + bool GetStringPiece(CryptoTag tag, base::StringPiece* out) const; + + // GetNthValue16 interprets the value with the given tag to be a series of + // 16-bit length prefixed values and it returns the subvalue with the given + // index. + QuicErrorCode GetNthValue16(CryptoTag tag, + unsigned index, + base::StringPiece* out) const; + bool GetString(CryptoTag tag, std::string* out) const; + QuicErrorCode GetUint16(CryptoTag tag, uint16* out) const; + QuicErrorCode GetUint32(CryptoTag tag, uint32* out) const; + + CryptoTag tag; + CryptoTagValueMap tag_value_map; + + private: + // GetPOD is a utility function for extracting a plain-old-data value. If + // |tag| exists in the message, and has a value of exactly |len| bytes then + // it copies |len| bytes of data into |out|. Otherwise |len| bytes at |out| + // are zeroed out. + // + // If used to copy integers then this assumes that the machine is + // little-endian. + QuicErrorCode GetPOD(CryptoTag tag, void* out, size_t len) const; + + // The serialized form of the handshake message. This member is constructed + // lasily. + mutable scoped_ptr<QuicData> serialized_; +}; + // TODO(rch): sync with server more rationally class NET_EXPORT_PRIVATE QuicServerConfigProtobuf { public: @@ -81,6 +159,8 @@ struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParams { CryptoTag key_exchange; CryptoTag aead; std::string premaster_secret; + scoped_ptr<QuicEncrypter> encrypter; + scoped_ptr<QuicDecrypter> decrypter; }; // QuicCryptoConfig contains common configuration between clients and servers. @@ -102,7 +182,7 @@ class NET_EXPORT_PRIVATE QuicCryptoConfig { // index. CryptoTagVector kexs; std::vector<KeyExchange*> key_exchanges; - // Authenticated encryption with associated data (AEAD) algorithms + // Authenticated encryption with associated data (AEAD) algorithms. CryptoTagVector aead; private: @@ -113,6 +193,8 @@ class NET_EXPORT_PRIVATE QuicCryptoConfig { // client. class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { public: + QuicCryptoClientConfig(); + // Sets the members to reasonable, default values. |rand| is used in order to // generate ephemeral ECDH keys. void SetDefaults(QuicRandom* rand); @@ -123,13 +205,17 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { const std::string& server_hostname, CryptoHandshakeMessage* out); - // ProcessServerHello processes the tags in |server_hello|, writes the + // ProcessServerHello processes the message in |server_hello|, writes the // negotiated parameters to |out_params| and returns QUIC_NO_ERROR. If // |server_hello| is unacceptable then it puts an error message in // |error_details| and returns an error code. QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, + const std::string& nonce, QuicCryptoNegotiatedParams* out_params, std::string* error_details); + + // The |info| string for the HKDF function. + std::string hkdf_info; }; // QuicCryptoServerConfig contains the crypto configuration of a QUIC server. @@ -171,6 +257,9 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { QuicCryptoNegotiatedParams* out_params, std::string* error_details); + // The |info| string for the HKDF function. + std::string hkdf_info; + private: // Config represents a server config: a collection of preferences and // Diffie-Hellman public values. diff --git a/net/quic/crypto/crypto_protocol.cc b/net/quic/crypto/crypto_protocol.cc deleted file mode 100644 index 322e7cc..0000000 --- a/net/quic/crypto/crypto_protocol.cc +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/crypto/crypto_protocol.h" - -#include <stdarg.h> -#include <string.h> - -#include "base/memory/scoped_ptr.h" - -using base::StringPiece; -using std::string; - -namespace net { - -CryptoHandshakeMessage::CryptoHandshakeMessage() {} -CryptoHandshakeMessage::~CryptoHandshakeMessage() {} - -void CryptoHandshakeMessage::SetTaglist(CryptoTag tag, ...) { - // Warning, if sizeof(CryptoTag) > sizeof(int) then this function will break - // because the terminating 0 will only be promoted to int. - COMPILE_ASSERT(sizeof(CryptoTag) <= sizeof(int), - crypto_tag_not_be_larger_than_int_or_varargs_will_break); - - std::vector<CryptoTag> tags; - va_list ap; - - va_start(ap, tag); - for (;;) { - CryptoTag tag = va_arg(ap, CryptoTag); - if (tag == 0) { - break; - } - tags.push_back(tag); - } - - // Because of the way that we keep tags in memory, we can copy the contents - // of the vector and get the correct bytes in wire format. See - // crypto_protocol.h. This assumes that the system is little-endian. - SetVector(tag, tags); - - va_end(ap); -} - -QuicErrorCode CryptoHandshakeMessage::GetTaglist(CryptoTag tag, - const CryptoTag** out_tags, - size_t* out_len) const { - CryptoTagValueMap::const_iterator it = tag_value_map.find(tag); - QuicErrorCode ret = QUIC_NO_ERROR; - - if (it == tag_value_map.end()) { - ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; - } else if (it->second.size() % sizeof(CryptoTag) != 0) { - ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - if (ret != QUIC_NO_ERROR) { - *out_tags = NULL; - *out_len = 0; - return ret; - } - - *out_tags = reinterpret_cast<const CryptoTag*>(it->second.data()); - *out_len = it->second.size() / sizeof(CryptoTag); - return ret; -} - -bool CryptoHandshakeMessage::GetStringPiece(CryptoTag tag, - StringPiece* out) const { - CryptoTagValueMap::const_iterator it = tag_value_map.find(tag); - if (it == tag_value_map.end()) { - return false; - } - *out = it->second; - return true; -} - -QuicErrorCode CryptoHandshakeMessage::GetNthValue16( - CryptoTag tag, - unsigned index, - StringPiece* out) const { - StringPiece value; - if (!GetStringPiece(tag, &value)) { - return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; - } - - for (unsigned i = 0;; i++) { - if (value.empty()) { - return QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND; - } - if (value.size() < 2) { - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - const unsigned char* data = - reinterpret_cast<const unsigned char*>(value.data()); - size_t size = static_cast<size_t>(data[0]) | - (static_cast<size_t>(data[1]) << 8); - value.remove_prefix(2); - - if (value.size() < size) { - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - if (i == index) { - *out = StringPiece(value.data(), size); - return QUIC_NO_ERROR; - } - - value.remove_prefix(size); - } -} - -bool CryptoHandshakeMessage::GetString(CryptoTag tag, string* out) const { - CryptoTagValueMap::const_iterator it = tag_value_map.find(tag); - if (it == tag_value_map.end()) { - return false; - } - *out = it->second; - return true; -} - -QuicErrorCode CryptoHandshakeMessage::GetUint16(CryptoTag tag, - uint16* out) const { - return GetPOD(tag, out, sizeof(uint16)); -} - -QuicErrorCode CryptoHandshakeMessage::GetUint32(CryptoTag tag, - uint32* out) const { - return GetPOD(tag, out, sizeof(uint32)); -} - -QuicErrorCode CryptoHandshakeMessage::GetPOD( - CryptoTag tag, void* out, size_t len) const { - CryptoTagValueMap::const_iterator it = tag_value_map.find(tag); - QuicErrorCode ret = QUIC_NO_ERROR; - - if (it == tag_value_map.end()) { - ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; - } else if (it->second.size() != len) { - ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - if (ret != QUIC_NO_ERROR) { - memset(out, 0, len); - return ret; - } - - memcpy(out, it->second.data(), len); - return ret; -} - -} // namespace net diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h index 4a3212b..010b1d0 100644 --- a/net/quic/crypto/crypto_protocol.h +++ b/net/quic/crypto/crypto_protocol.h @@ -22,67 +22,6 @@ typedef uint32 CryptoTag; typedef std::string ServerConfigID; typedef std::map<CryptoTag, std::string> CryptoTagValueMap; typedef std::vector<CryptoTag> CryptoTagVector; -// An intermediate format of a handshake message that's convenient for a -// CryptoFramer to serialize from or parse into. -struct NET_EXPORT_PRIVATE CryptoHandshakeMessage { - CryptoHandshakeMessage(); - ~CryptoHandshakeMessage(); - - // SetValue sets an element with the given tag to the raw, memory contents of - // |v|. - template<class T> void SetValue(CryptoTag tag, const T& v) { - tag_value_map[tag] = std::string(reinterpret_cast<const char*>(&v), - sizeof(v)); - } - - // SetVector sets an element with the given tag to the raw contents of an - // array of elements in |v|. - template<class T> void SetVector(CryptoTag tag, const std::vector<T>& v) { - if (v.empty()) { - tag_value_map[tag] = std::string(); - } else { - tag_value_map[tag] = std::string(reinterpret_cast<const char*>(&v[0]), - v.size() * sizeof(T)); - } - } - - // SetTaglist sets an element with the given tag to contain a list of tags, - // passed as varargs. The argument list must be terminated with a 0 element. - void SetTaglist(CryptoTag tag, ...); - - // GetTaglist finds an element with the given tag containing zero or more - // tags. If such a tag doesn't exist, it returns false. Otherwise it sets - // |out_tags| and |out_len| to point to the array of tags and returns true. - // The array points into the CryptoHandshakeMessage and is valid only for as - // long as the CryptoHandshakeMessage exists and is not modified. - QuicErrorCode GetTaglist(CryptoTag tag, const CryptoTag** out_tags, - size_t* out_len) const; - - bool GetStringPiece(CryptoTag tag, base::StringPiece* out) const; - - // GetNthValue16 interprets the value with the given tag to be a series of - // 16-bit length prefixed values and it returns the subvalue with the given - // index. - QuicErrorCode GetNthValue16(CryptoTag tag, - unsigned index, - base::StringPiece* out) const; - bool GetString(CryptoTag tag, std::string* out) const; - QuicErrorCode GetUint16(CryptoTag tag, uint16* out) const; - QuicErrorCode GetUint32(CryptoTag tag, uint32* out) const; - - CryptoTag tag; - CryptoTagValueMap tag_value_map; - - private: - // GetPOD is a utility function for extracting a plain-old-data value. If - // |tag| exists in the message, and has a value of exactly |len| bytes then - // it copies |len| bytes of data into |out|. Otherwise |len| bytes at |out| - // are zeroed out. - // - // If used to copy integers then this assumes that the machine is - // little-endian. - QuicErrorCode GetPOD(CryptoTag tag, void* out, size_t len) const; -}; const CryptoTag kCHLO = MAKE_TAG('C', 'H', 'L', 'O'); // Client hello const CryptoTag kSHLO = MAKE_TAG('S', 'H', 'L', 'O'); // Server hello diff --git a/net/quic/crypto/null_decrypter.cc b/net/quic/crypto/null_decrypter.cc index 92b9204..05d6bda 100644 --- a/net/quic/crypto/null_decrypter.cc +++ b/net/quic/crypto/null_decrypter.cc @@ -41,4 +41,12 @@ QuicData* NullDecrypter::Decrypt(QuicPacketSequenceNumber /*sequence_number*/, return new QuicData(plaintext.data(), plaintext.length()); } +StringPiece NullDecrypter::GetKey() const { + return StringPiece(); +} + +StringPiece NullDecrypter::GetNoncePrefix() const { + return StringPiece(); +} + } // namespace net diff --git a/net/quic/crypto/null_decrypter.h b/net/quic/crypto/null_decrypter.h index 93315a8..bd4a7ea 100644 --- a/net/quic/crypto/null_decrypter.h +++ b/net/quic/crypto/null_decrypter.h @@ -24,6 +24,8 @@ class NET_EXPORT_PRIVATE NullDecrypter : public QuicDecrypter { virtual QuicData* Decrypt(QuicPacketSequenceNumber sequence_number, base::StringPiece associated_data, base::StringPiece ciphertext) OVERRIDE; + virtual base::StringPiece GetKey() const OVERRIDE; + virtual base::StringPiece GetNoncePrefix() const OVERRIDE; }; } // namespace net diff --git a/net/quic/crypto/null_encrypter.cc b/net/quic/crypto/null_encrypter.cc index 49fc927..b563107 100644 --- a/net/quic/crypto/null_encrypter.cc +++ b/net/quic/crypto/null_encrypter.cc @@ -51,4 +51,12 @@ size_t NullEncrypter::GetCiphertextSize(size_t plaintext_size) const { return plaintext_size + kHashSize; } +StringPiece NullEncrypter::GetKey() const { + return StringPiece(); +} + +StringPiece NullEncrypter::GetNoncePrefix() const { + return StringPiece(); +} + } // namespace net diff --git a/net/quic/crypto/null_encrypter.h b/net/quic/crypto/null_encrypter.h index 62f4ef6..c452348 100644 --- a/net/quic/crypto/null_encrypter.h +++ b/net/quic/crypto/null_encrypter.h @@ -28,6 +28,8 @@ class NET_EXPORT_PRIVATE NullEncrypter : public QuicEncrypter { virtual size_t GetNoncePrefixSize() const OVERRIDE; virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const OVERRIDE; virtual size_t GetCiphertextSize(size_t plaintext_size) const OVERRIDE; + virtual base::StringPiece GetKey() const OVERRIDE; + virtual base::StringPiece GetNoncePrefix() const OVERRIDE; }; } // namespace net diff --git a/net/quic/crypto/quic_decrypter.cc b/net/quic/crypto/quic_decrypter.cc index 997a311..aa036df 100644 --- a/net/quic/crypto/quic_decrypter.cc +++ b/net/quic/crypto/quic_decrypter.cc @@ -10,6 +10,10 @@ namespace net { // static QuicDecrypter* QuicDecrypter::Create(CryptoTag algorithm) { switch (algorithm) { + case kAESG: + // TODO(wtc): add support for Aes128GcmDecrypter. + // return new Aes128GcmDecrypter(); + return new NullDecrypter(); case kNULL: return new NullDecrypter(); default: diff --git a/net/quic/crypto/quic_decrypter.h b/net/quic/crypto/quic_decrypter.h index c8b691d..c648668 100644 --- a/net/quic/crypto/quic_decrypter.h +++ b/net/quic/crypto/quic_decrypter.h @@ -49,6 +49,10 @@ class NET_EXPORT_PRIVATE QuicDecrypter { virtual QuicData* Decrypt(QuicPacketSequenceNumber sequence_number, base::StringPiece associated_data, base::StringPiece ciphertext) = 0; + + // For use by unit tests only. + virtual base::StringPiece GetKey() const = 0; + virtual base::StringPiece GetNoncePrefix() const = 0; }; } // namespace net diff --git a/net/quic/crypto/quic_encrypter.cc b/net/quic/crypto/quic_encrypter.cc index cc13013..ec521ec 100644 --- a/net/quic/crypto/quic_encrypter.cc +++ b/net/quic/crypto/quic_encrypter.cc @@ -10,6 +10,10 @@ namespace net { // static QuicEncrypter* QuicEncrypter::Create(CryptoTag algorithm) { switch (algorithm) { + case kAESG: + // TODO(wtc): add support for Aes128GcmEncrypter. + // return new Aes128GcmEncrypter(); + return new NullEncrypter(); case kNULL: return new NullEncrypter(); default: diff --git a/net/quic/crypto/quic_encrypter.h b/net/quic/crypto/quic_encrypter.h index 214ce2d..013aff1 100644 --- a/net/quic/crypto/quic_encrypter.h +++ b/net/quic/crypto/quic_encrypter.h @@ -66,6 +66,10 @@ class NET_EXPORT_PRIVATE QuicEncrypter { // Returns the length of the ciphertext that would be generated by encrypting // to plaintext of size |plaintext_size|. virtual size_t GetCiphertextSize(size_t plaintext_size) const = 0; + + // For use by unit tests only. + virtual base::StringPiece GetKey() const = 0; + virtual base::StringPiece GetNoncePrefix() const = 0; }; } // namespace net diff --git a/net/quic/quic_client_session_test.cc b/net/quic/quic_client_session_test.cc index f2f819a..6d2c368 100644 --- a/net/quic/quic_client_session_test.cc +++ b/net/quic/quic_client_session_test.cc @@ -28,7 +28,7 @@ class QuicClientSessionTest : public ::testing::Test { protected: QuicClientSessionTest() : guid_(1), - connection_(new PacketSavingConnection(guid_, IPEndPoint())), + connection_(new PacketSavingConnection(guid_, IPEndPoint(), false)), session_(connection_, NULL, NULL, kServerHostname, &net_log_) { } @@ -92,7 +92,8 @@ TEST_F(QuicClientSessionTest, Logging) { // Receive a packet, and verify that it was logged. QuicFramer framer(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)); + QuicEncrypter::Create(kNULL), + false); QuicRstStreamFrame frame; frame.stream_id = 2; frame.error_code = QUIC_CONNECTION_TIMED_OUT; diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index c81f50b..955d82c 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -69,7 +69,7 @@ const int kMaxPacketsPerRetransmissionAlarm = 10; const bool kForce = true; // Named constant for CanWrite(). const bool kIsRetransmission = true; -// Named constatn for WritePacket. +// Named constant for WritePacket. const bool kHasRetransmittableData = true; bool Near(QuicPacketSequenceNumber a, QuicPacketSequenceNumber b) { @@ -81,11 +81,13 @@ bool Near(QuicPacketSequenceNumber a, QuicPacketSequenceNumber b) { QuicConnection::QuicConnection(QuicGuid guid, IPEndPoint address, - QuicConnectionHelperInterface* helper) + QuicConnectionHelperInterface* helper, + bool is_server) : helper_(helper), framer_(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)), + QuicEncrypter::Create(kNULL), + is_server), clock_(helper->GetClock()), random_generator_(helper->GetRandomGenerator()), guid_(guid), @@ -97,16 +99,21 @@ QuicConnection::QuicConnection(QuicGuid guid, handling_retransmission_timeout_(false), write_blocked_(false), debug_visitor_(NULL), - packet_creator_(guid_, &framer_, random_generator_), + packet_creator_(guid_, &framer_, random_generator_, is_server), packet_generator_(this, &packet_creator_), timeout_(QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)), time_of_last_received_packet_(clock_->ApproximateNow()), time_of_last_sent_packet_(clock_->ApproximateNow()), congestion_manager_(clock_, kTCP), + version_negotiation_state_(START_NEGOTIATION), + quic_version_(kQuicVersion1), + is_server_(is_server), connected_(true), received_truncated_ack_(false), send_ack_in_response_to_packet_(false) { helper_->SetConnection(this); + // TODO(satyamshekhar): Have a smaller timeout till version is negotiated and + // connection is established (CHLO fully processed). helper_->SetTimeoutAlarm(timeout_); framer_.set_visitor(this); framer_.set_entropy_calculator(&entropy_manager_); @@ -134,11 +141,27 @@ QuicConnection::~QuicConnection() { } } +bool QuicConnection::SelectMutualVersion( + const QuicVersionTagList& available_versions) { + // TODO(satyamshekhar): Make this generic. + if (std::find(available_versions.begin(), available_versions.end(), + kQuicVersion1) == available_versions.end()) { + return false; + } + + // Right now we only support kQuicVersion1 so it's okay not to + // update the framer and quic_version_. When start supporting more + // versions please update both. + return true; +} + void QuicConnection::OnError(QuicFramer* framer) { SendConnectionClose(framer->error()); } void QuicConnection::OnPacket() { + // TODO(satyamshekhar): Validate packet before updating the time + // since it affects the timeout of the connection. time_of_last_received_packet_ = clock_->Now(); DVLOG(1) << "time of last received packet: " << time_of_last_received_packet_.ToMicroseconds(); @@ -156,6 +179,91 @@ void QuicConnection::OnPublicResetPacket( CloseConnection(QUIC_PUBLIC_RESET, true); } +bool QuicConnection::OnProtocolVersionMismatch( + QuicVersionTag received_version) { + // TODO(satyamshekhar): Implement no server state in this mode. + if (!is_server_) { + LOG(DFATAL) << "Framer called OnProtocolVersionMismatch for server. " + << "Closing connection."; + CloseConnection(QUIC_INTERNAL_ERROR, false); + } + DCHECK_NE(quic_version_, received_version); + + if (debug_visitor_) { + debug_visitor_->OnProtocolVersionMismatch(received_version); + } + + switch (version_negotiation_state_) { + case START_NEGOTIATION: + if (!framer_.IsSupportedVersion(received_version)) { + SendVersionNegotiationPacket(); + version_negotiation_state_ = SENT_NEGOTIATION_PACKET; + return false; + } + break; + + case SENT_NEGOTIATION_PACKET: + if (!framer_.IsSupportedVersion(received_version)) { + // Drop packets which can't be parsed due to version mismatch. + return false; + } + break; + + case NEGOTIATED_VERSION: + // Might be old packets that were sent by the client before the version + // was negotiated. Drop these. + return false; + + default: + DCHECK(false); + } + + // Right now we only support kQuicVersion1 so it's okay not to + // update the framer and quic_version_. When start supporting more + // versions please update both. + version_negotiation_state_ = NEGOTIATED_VERSION; + // TODO(satyamshekhar): Store the sequence number of this packet and close the + // connection if we ever received a packet with incorrect version and whose + // sequence number is greater. + return true; +} + +// Handles version negotiation for client connection. +void QuicConnection::OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) { + if (is_server_) { + LOG(DFATAL) << "Framer parsed VersionNegotiationPacket for server." + << "Closing connection."; + CloseConnection(QUIC_INTERNAL_ERROR, false); + } + if (debug_visitor_) { + debug_visitor_->OnVersionNegotiationPacket(packet); + } + + if (version_negotiation_state_ == NEGOTIATED_VERSION) { + // Possibly a duplicate version negotiation packet. + return; + } + + if (std::find(packet.versions.begin(), + packet.versions.end(), quic_version_) != + packet.versions.end()) { + DLOG(WARNING) << "The server already supports our version. It should have " + << "accepted our connection."; + // Just drop the connection. + CloseConnection(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, false); + return; + } + + if (!SelectMutualVersion(packet.versions)) { + SendConnectionCloseWithDetails(QUIC_INVALID_VERSION, + "no common version found"); + } + + version_negotiation_state_ = NEGOTIATED_VERSION; + RetransmitAllUnackedPackets(); +} + void QuicConnection::OnRevivedPacket() { } @@ -163,6 +271,10 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { if (debug_visitor_) { debug_visitor_->OnPacketHeader(header); } + + // Will be decrement below if we fall through to return true; + ++stats_.packets_dropped; + if (header.public_header.guid != guid_) { DLOG(INFO) << "Ignoring packet from unexpected GUID: " << header.public_header.guid << " instead of " << guid_; @@ -184,6 +296,32 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { return false; } + if (version_negotiation_state_ != NEGOTIATED_VERSION) { + if (is_server_) { + if (!header.public_header.version_flag) { + DLOG(WARNING) << "Got packet without version flag before version " + << "negotiated."; + // Packets should have the version flag till version negotiation is + // done. + CloseConnection(QUIC_INVALID_VERSION, false); + return false; + } else { + DCHECK_EQ(1u, header.public_header.versions.size()); + DCHECK_EQ(header.public_header.versions[0], quic_version_); + version_negotiation_state_ = NEGOTIATED_VERSION; + } + } else { + DCHECK(!header.public_header.version_flag); + // If the client gets a packet without the version flag from the server + // it should stop sending version since the version negotiation is done. + packet_creator_.StopSendingVersion(); + version_negotiation_state_ = NEGOTIATED_VERSION; + } + } + + DCHECK_EQ(NEGOTIATED_VERSION, version_negotiation_state_); + + --stats_.packets_dropped; DVLOG(1) << "Received packet header: " << header; last_header_ = header; return true; @@ -239,15 +377,16 @@ void QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) { if (queued_packets_.empty()) { return; } + bool has_retransmittable_data = true; QuicTime::Delta delay = congestion_manager_.TimeUntilSend( - time_of_last_received_packet_, false); + time_of_last_received_packet_, false, has_retransmittable_data); if (delay.IsZero()) { helper_->UnregisterSendAlarmIfRegistered(); if (!write_blocked_) { OnCanWrite(); } - } else { + } else if (!delay.IsInfinite()) { helper_->SetSendAlarm(delay); } } @@ -402,11 +541,11 @@ bool QuicConnection::DontWaitForPacketsBefore( QuicPacketSequenceNumber least_unacked) { size_t missing_packets_count = outgoing_ack_.received_info.missing_packets.size(); - outgoing_ack_.received_info.missing_packets.erase( + outgoing_ack_.received_info.missing_packets.erase( outgoing_ack_.received_info.missing_packets.begin(), outgoing_ack_.received_info.missing_packets.lower_bound(least_unacked)); - return missing_packets_count != - outgoing_ack_.received_info.missing_packets.size(); + return missing_packets_count != + outgoing_ack_.received_info.missing_packets.size(); } void QuicConnection::UpdatePacketInformationSentByPeer( @@ -516,6 +655,17 @@ void QuicConnection::MaybeSendAckInResponseToPacket() { send_ack_in_response_to_packet_ = !send_ack_in_response_to_packet_; } +void QuicConnection::SendVersionNegotiationPacket() { + QuicVersionTagList supported_versions; + supported_versions.push_back(kQuicVersion1); + QuicEncryptedPacket* encrypted = + packet_creator_.SerializeVersionNegotiationPacket(supported_versions); + // TODO(satyamshekhar): implement zero server state negotiation. + int error; + helper_->WritePacketToWire(*encrypted, &error); + delete encrypted; +} + QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id, base::StringPiece data, QuicStreamOffset offset, @@ -529,6 +679,14 @@ void QuicConnection::SendRstStream(QuicStreamId id, QuicFrame(new QuicRstStreamFrame(id, error))); } +const QuicConnectionStats& QuicConnection::GetStats() { + // Update rtt and estimated bandwidth. + stats_.rtt = congestion_manager_.SmoothedRtt().ToMicroseconds(); + stats_.estimated_bandwidth = + congestion_manager_.BandwidthEstimate().ToBytesPerSecond(); + return stats_; +} + void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address, const IPEndPoint& peer_address, const QuicEncryptedPacket& packet) { @@ -539,12 +697,15 @@ void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address, last_size_ = packet.length(); last_self_address_ = self_address; last_peer_address_ = peer_address; + + stats_.bytes_received += packet.length(); + ++stats_.packets_received; + framer_.ProcessPacket(packet); MaybeProcessRevivedPacket(); } bool QuicConnection::OnCanWrite() { - LOG(INFO) << "here!!!"; write_blocked_ = false; WriteQueuedPackets(); @@ -553,15 +714,14 @@ bool QuicConnection::OnCanWrite() { // or the congestion manager to prohibit sending. If we've sent everything // we had queued and we're still not blocked, let the visitor know it can // write more. - // TODO(rch): shouldn't this be "if (CanWrite(false))" - if (!write_blocked_) { + if (CanWrite(false, true)) { packet_generator_.StartBatchOperations(); bool all_bytes_written = visitor_->OnCanWrite(); packet_generator_.FinishBatchOperations(); // After the visitor writes, it may have caused the socket to become write // blocked or the congestion manager to prohibit sending, so check again. - if (!write_blocked_ && !all_bytes_written && !helper_->IsSendAlarmSet()) { + if (!write_blocked_ && !all_bytes_written && CanWrite(false, true)) { // We're not write blocked, but some stream didn't write out all of its // bytes. Register for 'immediate' resumption so we'll keep writing after // other quic connections have had a chance to use the socket. @@ -577,19 +737,20 @@ bool QuicConnection::WriteQueuedPackets() { size_t num_queued_packets = queued_packets_.size() + 1; QueuedPacketList::iterator packet_iterator = queued_packets_.begin(); - while (!write_blocked_ && !helper_->IsSendAlarmSet() && - packet_iterator != queued_packets_.end()) { + while (!write_blocked_ && packet_iterator != queued_packets_.end()) { // Ensure that from one iteration of this loop to the next we // succeeded in sending a packet so we don't infinitely loop. // TODO(rch): clean up and close the connection if we really hit this. DCHECK_LT(queued_packets_.size(), num_queued_packets); num_queued_packets = queued_packets_.size(); if (WritePacket(packet_iterator->sequence_number, - packet_iterator->packet, kHasRetransmittableData, + packet_iterator->packet, + packet_iterator->has_retransmittable_data, !kForce)) { packet_iterator = queued_packets_.erase(packet_iterator); } else { // Continue, because some queued packets may still be writable. + // This can happen if a retransmit send fail. ++packet_iterator; } } @@ -618,6 +779,7 @@ void QuicConnection::RecordPacketReceived(const QuicPacketHeader& header) { outgoing_ack_.received_info.largest_observed = max( outgoing_ack_.received_info.largest_observed, header.packet_sequence_number); + // TODO(pwestin): update received_info with time_of_last_received_packet_. entropy_manager_.RecordReceivedPacketEntropyHash(sequence_number, header.entropy_hash); } @@ -643,11 +805,19 @@ bool QuicConnection::MaybeRetransmitPacketForRTO( sequence_number > peer_largest_observed_packet_) { return false; } else { + ++stats_.rto_count; RetransmitPacket(sequence_number); return true; } } +void QuicConnection::RetransmitAllUnackedPackets() { + for (UnackedPacketMap::iterator it = unacked_packets_.begin(); + it != unacked_packets_.end(); ++it) { + RetransmitPacket(it->first); + } +} + void QuicConnection::RetransmitPacket( QuicPacketSequenceNumber sequence_number) { UnackedPacketMap::iterator unacked_it = @@ -661,6 +831,9 @@ void QuicConnection::RetransmitPacket( DCHECK(unacked_it != unacked_packets_.end()); DCHECK(retransmission_it != retransmission_map_.end()); RetransmittableFrames* unacked = unacked_it->second; + // TODO(pwestin): Need to fix potential issue with FEC and a 1 packet + // congestion window see b/8331807 for details. + congestion_manager_.AbandoningPacket(sequence_number); // TODO(ianswett): Never change the sequence number of the connect packet. // Re-packetize the frames with a new sequence number for retransmission. // Retransmitted data packets do not use FEC, even when it's enabled. @@ -686,17 +859,22 @@ void QuicConnection::RetransmitPacket( true); } -bool QuicConnection::CanWrite(bool is_retransmission) { +bool QuicConnection::CanWrite(bool is_retransmission, + bool has_retransmittable_data) { // TODO(ianswett): If the packet is a retransmit, the current send alarm may // be too long. if (write_blocked_ || helper_->IsSendAlarmSet()) { return false; } - QuicTime::Delta delay = congestion_manager_.TimeUntilSend(clock_->Now(), - is_retransmission); + + QuicTime::Delta delay = congestion_manager_.TimeUntilSend( + clock_->Now(), is_retransmission, has_retransmittable_data); + if (delay.IsInfinite()) { + return false; + } + // If the scheduler requires a delay, then we can not send this packet now. - if (!delay.IsZero() && !delay.IsInfinite()) { - // TODO(pwestin): we need to handle delay.IsInfinite() separately. + if (!delay.IsZero()) { helper_->SetSendAlarm(delay); return false; } @@ -752,7 +930,7 @@ bool QuicConnection::WritePacket(QuicPacketSequenceNumber sequence_number, bool is_retransmission = IsRetransmission(sequence_number); // If we are not forced and we can't write, then simply return false; - if (!forced && !CanWrite(is_retransmission)) { + if (!forced && !CanWrite(is_retransmission, has_retransmittable_data)) { return false; } @@ -760,13 +938,13 @@ bool QuicConnection::WritePacket(QuicPacketSequenceNumber sequence_number, framer_.EncryptPacket(sequence_number, *packet)); DLOG(INFO) << "Sending packet number " << sequence_number << " : " << (packet->is_fec_packet() ? "FEC " : - (ContainsKey(retransmission_map_, sequence_number) ? - "data bearing " : " ack only ")); + (has_retransmittable_data ? "data bearing " : " ack only ")) + << " Packet length:" << packet->length(); DCHECK(encrypted->length() <= kMaxPacketSize) << "Packet " << sequence_number << " will not be read; too large: " << packet->length() << " " << encrypted->length() << " " - << outgoing_ack_; + << outgoing_ack_ << " forced: " << (forced ? "yes" : "no"); int error; QuicTime now = clock_->Now(); @@ -788,7 +966,16 @@ bool QuicConnection::WritePacket(QuicPacketSequenceNumber sequence_number, MaybeSetupRetransmission(sequence_number); congestion_manager_.SentPacket(sequence_number, now, packet->length(), - is_retransmission, has_retransmittable_data); + is_retransmission); + + stats_.bytes_sent += encrypted->length(); + ++stats_.packets_sent; + + if (is_retransmission) { + stats_.bytes_retransmitted += encrypted->length(); + ++stats_.packets_retransmitted; + } + delete packet; return true; } @@ -821,7 +1008,8 @@ bool QuicConnection::SendOrQueuePacket(QuicPacketSequenceNumber sequence_number, entropy_hash); if (!WritePacket(sequence_number, packet, has_retransmittable_data, !kForce)) { - queued_packets_.push_back(QueuedPacket(sequence_number, packet)); + queued_packets_.push_back(QueuedPacket(sequence_number, packet, + has_retransmittable_data)); return false; } return true; @@ -931,6 +1119,8 @@ void QuicConnection::MaybeProcessRevivedPacket() { debug_visitor_->OnRevivedPacket(revived_header, StringPiece(revived_payload, len)); } + + ++stats_.packets_revived; framer_.ProcessRevivedPacket(&revived_header, StringPiece(revived_payload, len)); } @@ -976,8 +1166,6 @@ void QuicConnection::SendConnectionCloseWithDetails(QuicErrorCode error, } void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) { - // TODO(satyamshekhar): Ask the dispatcher to delete visitor and hence self - // if the visitor will always be deleted by closing the connection. connected_ = false; visitor_->ConnectionClose(error, from_peer); } diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h index a1411e2..17f0a11 100644 --- a/net/quic/quic_connection.h +++ b/net/quic/quic_connection.h @@ -32,6 +32,7 @@ #include "net/quic/quic_packet_entropy_manager.h" #include "net/quic/quic_packet_generator.h" #include "net/quic/quic_protocol.h" +#include "net/quic/quic_stats.h" namespace net { @@ -89,7 +90,12 @@ class NET_EXPORT_PRIVATE QuicConnectionDebugVisitorInterface { const IPEndPoint& peer_address, const QuicEncryptedPacket& packet) = 0; - // Called when the header of a packet has been parsed. + // Called when the protocol version on the received packet doensn't match + // current protocol version of the connection. + virtual void OnProtocolVersionMismatch( + QuicVersionTag version) = 0; + + // Called when the complete header of a packet has been parsed. virtual void OnPacketHeader(const QuicPacketHeader& header) = 0; // Called when a StreamFrame has been parsed. @@ -112,6 +118,10 @@ class NET_EXPORT_PRIVATE QuicConnectionDebugVisitorInterface { // Called when a public reset packet has been received. virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) = 0; + // Called when a version negotiation packet has been received. + virtual void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) = 0; + // Called after a packet has been successfully parsed which results // in the revival of a packet via FEC. virtual void OnRevivedPacket(const QuicPacketHeader& revived_header, @@ -182,7 +192,8 @@ class NET_EXPORT_PRIVATE QuicConnection // |helper| will be owned by this connection. QuicConnection(QuicGuid guid, IPEndPoint address, - QuicConnectionHelperInterface* helper); + QuicConnectionHelperInterface* helper, + bool is_server); virtual ~QuicConnection(); static void DeleteEnclosedFrame(QuicFrame* frame); @@ -217,6 +228,9 @@ class NET_EXPORT_PRIVATE QuicConnection QuicStreamId last_good_stream_id, const std::string& reason); + // Returns statistics tracked for this connection. + const QuicConnectionStats& GetStats(); + // Processes an incoming UDP packet (consisting of a QuicEncryptedPacket) from // the peer. If processing this packet permits a packet to be revived from // its FEC group that packet will be revived and processed. @@ -229,11 +243,19 @@ class NET_EXPORT_PRIVATE QuicConnection // queued writes to happen. Returns false if the socket has become blocked. virtual bool OnCanWrite() OVERRIDE; + QuicVersionTag version() const { + return quic_version_; + } + // From QuicFramerVisitorInterface virtual void OnError(QuicFramer* framer) OVERRIDE; + virtual bool OnProtocolVersionMismatch( + QuicVersionTag received_version) OVERRIDE; virtual void OnPacket() OVERRIDE; virtual void OnPublicResetPacket( const QuicPublicResetPacket& packet) OVERRIDE; + virtual void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) OVERRIDE; virtual void OnRevivedPacket() OVERRIDE; virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE; virtual void OnFecProtectedPayload(base::StringPiece payload) OVERRIDE; @@ -358,13 +380,16 @@ class NET_EXPORT_PRIVATE QuicConnection // Owns the QuicPacket* packet. struct QueuedPacket { QueuedPacket(QuicPacketSequenceNumber sequence_number, - QuicPacket* packet) + QuicPacket* packet, + bool has_retransmittable_data) : sequence_number(sequence_number), - packet(packet) { + packet(packet), + has_retransmittable_data(has_retransmittable_data) { } QuicPacketSequenceNumber sequence_number; QuicPacket* packet; + bool has_retransmittable_data; }; struct RetransmissionInfo { @@ -402,11 +427,21 @@ class NET_EXPORT_PRIVATE QuicConnection RetransmissionInfoComparator> RetransmissionTimeouts; + // Selects and updates the version of the protocol being used by selecting a + // version from |available_versions| which is also supported. Returns true if + // such a version exists, false otherwise. + bool SelectMutualVersion( + const QuicVersionTagList& available_versions); + // Sends a version negotiation packet to the peer. + void SendVersionNegotiationPacket(); + // Checks if a packet can be written now, and sets the timer if necessary. - virtual bool CanWrite(bool is_retransmission) OVERRIDE; + virtual bool CanWrite(bool is_retransmission, + bool has_retransmittable_data) OVERRIDE; void MaybeSetupRetransmission(QuicPacketSequenceNumber sequence_number); bool IsRetransmission(QuicPacketSequenceNumber sequence_number); + void RetransmitAllUnackedPackets(); // Writes as many queued packets as possible. The connection must not be // blocked when this is called. @@ -500,6 +535,9 @@ class NET_EXPORT_PRIVATE QuicConnection // Network idle time before we kill of this connection. const QuicTime::Delta timeout_; + // Statistics for this session. + QuicConnectionStats stats_; + // The time that we got a packet for this connection. QuicTime time_of_last_received_packet_; @@ -510,6 +548,15 @@ class NET_EXPORT_PRIVATE QuicConnection // as well as collecting and generating congestion feedback. QuicCongestionManager congestion_manager_; + // The state of connection in version negotiation finite state machine. + QuicVersionNegotiationState version_negotiation_state_; + + // The version of the protocol this connection is using. + QuicVersionTag quic_version_; + + // Tracks if the connection was created by the server. + bool is_server_; + // True by default. False if we've received or sent an explicit connection // close. bool connected_; diff --git a/net/quic/quic_connection_helper_test.cc b/net/quic/quic_connection_helper_test.cc index 544dec4..59ba5f75 100644 --- a/net/quic/quic_connection_helper_test.cc +++ b/net/quic/quic_connection_helper_test.cc @@ -30,7 +30,7 @@ class TestConnection : public QuicConnection { TestConnection(QuicGuid guid, IPEndPoint address, QuicConnectionHelper* helper) - : QuicConnection(guid, address, helper) { + : QuicConnection(guid, address, helper, false) { } void SendAck() { @@ -61,8 +61,8 @@ class QuicConnectionHelperTest : public ::testing::Test { : guid_(2), framer_(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)), - creator_(guid_, &framer_, QuicRandom::GetInstance()), + QuicEncrypter::Create(kNULL), + false), net_log_(BoundNetLog()), frame_(1, false, 0, kData) { Initialize(); @@ -107,7 +107,7 @@ class QuicConnectionHelperTest : public ::testing::Test { helper_.reset(new QuicConnectionHelper(runner_.get(), &clock_, &random_generator_, socket)); send_algorithm_ = new testing::StrictMock<MockSendAlgorithm>(); - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _)). + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)). WillRepeatedly(testing::Return(QuicTime::Delta::Zero())); connection_.reset(new TestConnection(guid_, IPEndPoint(), helper_.get())); connection_->set_visitor(&visitor_); @@ -137,7 +137,7 @@ class QuicConnectionHelperTest : public ::testing::Test { QuicPacketSequenceNumber sequence_number) { InitializeHeader(sequence_number); - QuicAckFrame ack(0, sequence_number); + QuicAckFrame ack(0, QuicTime::Zero(), sequence_number); ack.sent_info.entropy_hash = 0; ack.received_info.entropy_hash = 0; @@ -161,7 +161,7 @@ class QuicConnectionHelperTest : public ::testing::Test { InitializeHeader(sequence_number); QuicFrames frames; - QuicAckFrame ack(0, least_waiting + 1); + QuicAckFrame ack(0, QuicTime::Zero(), least_waiting + 1); ack.sent_info.entropy_hash = 0; ack.received_info.entropy_hash = 0; QuicConnectionCloseFrame close; @@ -184,7 +184,7 @@ class QuicConnectionHelperTest : public ::testing::Test { void InitializeHeader(QuicPacketSequenceNumber sequence_number) { header_.public_header.guid = guid_; header_.public_header.reset_flag = false; - header_.public_header.version_flag = false; + header_.public_header.version_flag = true; header_.packet_sequence_number = sequence_number; header_.entropy_flag = false; header_.fec_entropy_flag = false; @@ -203,7 +203,6 @@ class QuicConnectionHelperTest : public ::testing::Test { QuicGuid guid_; QuicFramer framer_; - QuicPacketCreator creator_; QuicPacketHeader header_; BoundNetLog net_log_; QuicStreamFrame frame_; @@ -249,7 +248,7 @@ TEST_F(QuicConnectionHelperTest, SetAckAlarm) { EXPECT_EQ(base::TimeDelta::FromMicroseconds(delta.ToMicroseconds()), runner_->GetPostedTasks()[1].delay); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false, !kHasData)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false)); runner_->RunNextTask(); EXPECT_EQ(QuicTime::Zero().Add(delta), clock_.ApproximateNow()); } @@ -293,7 +292,7 @@ TEST_F(QuicConnectionHelperTest, ResetAckAlarm) { // Verify that the ack alarm task has been re-posted. ASSERT_EQ(2u, runner_->GetPostedTasks().size()); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false, !kHasData)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false)); runner_->RunNextTask(); EXPECT_EQ(QuicTime::Zero().Add(delta2), clock_.ApproximateNow()); } @@ -307,10 +306,11 @@ TEST_F(QuicConnectionHelperTest, TestRetransmission) { QuicTime::Delta::FromMilliseconds(500); QuicTime start = clock_.ApproximateNow(); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false, kHasData)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false)); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)); // Send a packet. connection_->SendStreamData(1, kData, 0, false); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, true, kHasData)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, true)); // Since no ack was received, the retransmission alarm will fire and // retransmit it. runner_->RunNextTask(); @@ -329,7 +329,7 @@ TEST_F(QuicConnectionHelperTest, InitialTimeout) { EXPECT_EQ(base::TimeDelta::FromMicroseconds(kDefaultTimeoutUs), runner_->GetPostedTasks().front().delay); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false, !kHasData)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false)); // After we run the next task, we should close the connection. EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); @@ -374,7 +374,7 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) { // When we send a packet, the timeout will change to 5000 + kDefaultTimeout. clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(5000)); EXPECT_EQ(5000u, clock_.ApproximateNow().ToMicroseconds()); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false, !kHasData)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false)); // Send an ack so we don't set the retransmission alarm. connection_->SendAck(); @@ -389,7 +389,7 @@ TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) { // This time, we should time out. EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, false, !kHasData)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, false)); runner_->RunNextTask(); EXPECT_EQ(kDefaultTimeoutUs + 5000, clock_.ApproximateNow().ToMicroseconds()); EXPECT_FALSE(connection_->connected()); @@ -401,17 +401,17 @@ TEST_F(QuicConnectionHelperTest, SendSchedulerDelayThenSend) { Initialize(); // Test that if we send a packet with a delay, it ends up queued. - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, false)).WillOnce( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, false, _)).WillOnce( testing::Return(QuicTime::Delta::FromMicroseconds(1))); QuicPacket* packet = ConstructRawDataPacket(1); connection_->SendOrQueuePacket(1, packet, 0, kHasData); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false, kHasData)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, false)); EXPECT_EQ(1u, connection_->NumQueuedPackets()); // Advance the clock to fire the alarm, and configure the scheduler // to permit the packet to be sent. - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, false)).WillOnce( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, false, _)).WillRepeatedly( testing::Return(QuicTime::Delta::Zero())); EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(testing::Return(true)); runner_->RunNextTask(); diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc index 6545f2d..3679d1ce3 100644 --- a/net/quic/quic_connection_logger.cc +++ b/net/quic/quic_connection_logger.cc @@ -147,6 +147,11 @@ void QuicConnectionLogger::OnPacketReceived(const IPEndPoint& self_address, packet.length())); } +void QuicConnectionLogger::OnProtocolVersionMismatch( + QuicVersionTag received_version) { + // TODO(rtenneti): Add logging. +} + void QuicConnectionLogger::OnPacketHeader(const QuicPacketHeader& header) { net_log_.AddEvent( NetLog::TYPE_QUIC_SESSION_PACKET_HEADER_RECEIVED, @@ -189,6 +194,10 @@ void QuicConnectionLogger::OnPublicResetPacket( const QuicPublicResetPacket& packet) { } +void QuicConnectionLogger::OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) { +} + void QuicConnectionLogger::OnRevivedPacket( const QuicPacketHeader& revived_header, base::StringPiece payload) { diff --git a/net/quic/quic_connection_logger.h b/net/quic/quic_connection_logger.h index 36d322d..4fe390b 100644 --- a/net/quic/quic_connection_logger.h +++ b/net/quic/quic_connection_logger.h @@ -24,6 +24,8 @@ class NET_EXPORT_PRIVATE QuicConnectionLogger virtual void OnPacketReceived(const IPEndPoint& self_address, const IPEndPoint& peer_address, const QuicEncryptedPacket& packet) OVERRIDE; + virtual void OnProtocolVersionMismatch( + QuicVersionTag version) OVERRIDE; virtual void OnPacketHeader(const QuicPacketHeader& header) OVERRIDE; virtual void OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE; virtual void OnAckFrame(const QuicAckFrame& frame) OVERRIDE; @@ -34,6 +36,8 @@ class NET_EXPORT_PRIVATE QuicConnectionLogger const QuicConnectionCloseFrame& frame) OVERRIDE; virtual void OnPublicResetPacket( const QuicPublicResetPacket& packet) OVERRIDE; + virtual void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) OVERRIDE; virtual void OnRevivedPacket(const QuicPacketHeader& revived_header, base::StringPiece payload) OVERRIDE; diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc index d55850b..6cb5dea 100644 --- a/net/quic/quic_connection_test.cc +++ b/net/quic/quic_connection_test.cc @@ -15,6 +15,8 @@ #include "net/quic/test_tools/mock_clock.h" #include "net/quic/test_tools/mock_random.h" #include "net/quic/test_tools/quic_connection_peer.h" +#include "net/quic/test_tools/quic_framer_peer.h" +#include "net/quic/test_tools/quic_packet_creator_peer.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/quic/quic_utils.h" #include "testing/gmock/include/gmock/gmock.h" @@ -81,7 +83,8 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { retransmission_alarm_(QuicTime::Zero()), send_alarm_(QuicTime::FromMilliseconds(-1)), timeout_alarm_(QuicTime::Zero()), - blocked_(false) { + blocked_(false), + is_server_(true) { } // QuicConnectionHelperInterface @@ -99,7 +102,8 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { int* error) OVERRIDE { QuicFramer framer(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)); + QuicEncrypter::Create(kNULL), + is_server_); FramerVisitorCapturingFrames visitor; framer.set_visitor(&visitor); EXPECT_TRUE(framer.ProcessPacket(packet)); @@ -114,12 +118,17 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { if (visitor.stream_frames() != NULL && !visitor.stream_frames()->empty()) { stream_frames_ = *visitor.stream_frames(); } + if (visitor.version_negotiation_packet() != NULL) { + version_negotiation_packet_.reset(new QuicVersionNegotiationPacket( + *visitor.version_negotiation_packet())); + } if (blocked_) { *error = ERR_IO_PENDING; return -1; } *error = 0; - return packet.length(); + last_packet_size_ = packet.length(); + return last_packet_size_; } virtual void SetRetransmissionAlarm(QuicTime::Delta delay) OVERRIDE { @@ -163,8 +172,18 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { return &stream_frames_; } + size_t last_packet_size() { + return last_packet_size_; + } + + QuicVersionNegotiationPacket* version_negotiation_packet() { + return version_negotiation_packet_.get(); + } + void set_blocked(bool blocked) { blocked_ = blocked; } + void set_is_server(bool is_server) { is_server_ = is_server; } + private: MockClock* clock_; MockRandom* random_generator_; @@ -176,7 +195,10 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { scoped_ptr<QuicAckFrame> ack_; scoped_ptr<QuicCongestionFeedbackFrame> feedback_; vector<QuicStreamFrame> stream_frames_; + scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_; + size_t last_packet_size_; bool blocked_; + bool is_server_; DISALLOW_COPY_AND_ASSIGN(TestConnectionHelper); }; @@ -185,8 +207,11 @@ class TestConnection : public QuicConnection { public: TestConnection(QuicGuid guid, IPEndPoint address, - TestConnectionHelper* helper) - : QuicConnection(guid, address, helper) { + TestConnectionHelper* helper, + bool is_server) + : QuicConnection(guid, address, helper, is_server), + helper_(helper) { + helper_->set_is_server(!is_server); } void SendAck() { @@ -209,10 +234,21 @@ class TestConnection : public QuicConnection { return SendStreamData(2u, "food2", 0, !kFin); } + bool is_server() { + return QuicConnectionPeer::IsServer(this); + } + + void set_is_server(bool is_server) { + helper_->set_is_server(!is_server); + QuicConnectionPeer::SetIsServer(this, is_server); + } + using QuicConnection::SendOrQueuePacket; using QuicConnection::DontWaitForPacketsBefore; private: + TestConnectionHelper* helper_; + DISALLOW_COPY_AND_ASSIGN(TestConnection); }; @@ -222,11 +258,12 @@ class QuicConnectionTest : public ::testing::Test { : guid_(42), framer_(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)), - creator_(guid_, &framer_, QuicRandom::GetInstance()), + QuicEncrypter::Create(kNULL), + false), + creator_(guid_, &framer_, QuicRandom::GetInstance(), false), send_algorithm_(new StrictMock<MockSendAlgorithm>), helper_(new TestConnectionHelper(&clock_, &random_generator_)), - connection_(guid_, IPEndPoint(), helper_.get()), + connection_(guid_, IPEndPoint(), helper_.get(), false), frame1_(1, false, 0, data1), frame2_(1, false, 3, data2), accept_packet_(true) { @@ -234,11 +271,11 @@ class QuicConnectionTest : public ::testing::Test { connection_.SetSendAlgorithm(send_algorithm_); // Simplify tests by not sending feedback unless specifically configured. SetFeedback(NULL); - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _)).WillRepeatedly(Return( + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillRepeatedly(Return( QuicTime::Delta::Zero())); EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _)).Times(AnyNumber()); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(AnyNumber()); } QuicAckFrame* outgoing_ack() { @@ -257,6 +294,10 @@ class QuicConnectionTest : public ::testing::Test { return helper_->header(); } + size_t last_sent_packet_size() { + return helper_->last_packet_size(); + } + void ProcessPacket(QuicPacketSequenceNumber number) { EXPECT_CALL(visitor_, OnPacket(_, _, _, _)) .WillOnce(Return(accept_packet_)); @@ -266,6 +307,8 @@ class QuicConnectionTest : public ::testing::Test { QuicPacketEntropyHash ProcessFramePacket(QuicFrame frame) { QuicFrames frames; frames.push_back(QuicFrame(frame)); + QuicPacketCreatorPeer::SetSendVersionInPacket(&creator_, + connection_.is_server()); SerializedPacket serialized_packet = creator_.SerializeAllFrames(frames); scoped_ptr<QuicPacket> packet(serialized_packet.packet); scoped_ptr<QuicEncryptedPacket> encrypted( @@ -274,7 +317,7 @@ class QuicConnectionTest : public ::testing::Test { return serialized_packet.entropy_hash; } - void ProcessFecProtectedPacket(QuicPacketSequenceNumber number, + size_t ProcessFecProtectedPacket(QuicPacketSequenceNumber number, bool expect_revival) { if (expect_revival) { EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).Times(2).WillRepeatedly( @@ -283,10 +326,10 @@ class QuicConnectionTest : public ::testing::Test { EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce( Return(accept_packet_)); } - ProcessDataPacket(number, 1, !kEntropyFlag); + return ProcessDataPacket(number, 1, !kEntropyFlag); } - void ProcessDataPacket(QuicPacketSequenceNumber number, + size_t ProcessDataPacket(QuicPacketSequenceNumber number, QuicFecGroupNumber fec_group, bool entropy_flag) { scoped_ptr<QuicPacket> packet(ConstructDataPacket(number, fec_group, @@ -294,6 +337,7 @@ class QuicConnectionTest : public ::testing::Test { scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(number, *packet)); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); + return encrypted->length(); } void ProcessClosePacket(QuicPacketSequenceNumber number, @@ -304,7 +348,7 @@ class QuicConnectionTest : public ::testing::Test { connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); } - void ProcessFecProtectedPacket(QuicPacketSequenceNumber number, + size_t ProcessFecProtectedPacket(QuicPacketSequenceNumber number, bool expect_revival, bool entropy_flag) { if (expect_revival) { EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(DoAll( @@ -312,11 +356,11 @@ class QuicConnectionTest : public ::testing::Test { } EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(Return(accept_packet_)) .RetiresOnSaturation(); - ProcessDataPacket(number, 1, entropy_flag); + return ProcessDataPacket(number, 1, entropy_flag); } // Sends an FEC packet that covers the packets that would have been sent. - void ProcessFecPacket(QuicPacketSequenceNumber number, + size_t ProcessFecPacket(QuicPacketSequenceNumber number, QuicPacketSequenceNumber min_protected_packet, bool expect_revival, bool fec_entropy_flag) { @@ -357,24 +401,28 @@ class QuicConnectionTest : public ::testing::Test { framer_.EncryptPacket(number, *fec_packet)); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); + return encrypted->length(); } - void SendStreamDataToPeer(QuicStreamId id, StringPiece data, - QuicStreamOffset offset, bool fin, - QuicPacketSequenceNumber* last_packet) { - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, kHasData)); + QuicByteCount SendStreamDataToPeer(QuicStreamId id, StringPiece data, + QuicStreamOffset offset, bool fin, + QuicPacketSequenceNumber* last_packet) { + QuicByteCount packet_size; + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).WillOnce( + SaveArg<2>(&packet_size)); connection_.SendStreamData(id, data, offset, fin); if (last_packet != NULL) { *last_packet = QuicConnectionPeer::GetPacketCreator(&connection_)->sequence_number(); } - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(AnyNumber()); + return packet_size; } void SendAckPacketToPeer() { - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, !kHasData)).Times(1); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1); connection_.SendAck(); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, _)).Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(AnyNumber()); } QuicPacketEntropyHash ProcessAckPacket(QuicAckFrame* frame) { @@ -423,7 +471,7 @@ class QuicConnectionTest : public ::testing::Test { QuicConnectionCloseFrame qccf; qccf.error_code = QUIC_PEER_GOING_AWAY; - qccf.ack_frame = QuicAckFrame(0, 1); + qccf.ack_frame = QuicAckFrame(0, QuicTime::Zero(), 1); QuicFrames frames; QuicFrame frame(&qccf); @@ -538,7 +586,7 @@ TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) { // awaiting' is 4. The connection should then realize 1 will not be // retransmitted, and will remove it from the missing list. creator_.set_sequence_number(5); - QuicAckFrame frame(0, 4); + QuicAckFrame frame(0, QuicTime::Zero(), 4); ProcessAckPacket(&frame); // Force an ack to be sent. @@ -563,7 +611,7 @@ TEST_F(QuicConnectionTest, TruncatedAck) { SendStreamDataToPeer(1, "foo", i * 3, !kFin, NULL); } - QuicAckFrame frame(0, 1); + QuicAckFrame frame(0, QuicTime::Zero(), 1); frame.received_info.largest_observed = 192; InsertMissingPacketsBetween(&frame.received_info, 1, 192); frame.received_info.entropy_hash = @@ -590,20 +638,20 @@ TEST_F(QuicConnectionTest, LeastUnackedLower) { // Start out saying the least unacked is 2 creator_.set_sequence_number(5); - QuicAckFrame frame(0, 2); + QuicAckFrame frame(0, QuicTime::Zero(), 2); ProcessAckPacket(&frame); // Change it to 1, but lower the sequence number to fake out-of-order packets. // This should be fine. creator_.set_sequence_number(1); - QuicAckFrame frame2(0, 1); + QuicAckFrame frame2(0, QuicTime::Zero(), 1); // The scheduler will not process out of order acks. ProcessAckPacket(&frame2); // Now claim it's one, but set the ordering so it was sent "after" the first // one. This should cause a connection error. EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, !kHasData)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); creator_.set_sequence_number(7); ProcessAckPacket(&frame2); } @@ -615,24 +663,24 @@ TEST_F(QuicConnectionTest, LargestObservedLower) { EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2); // Start out saying the largest observed is 2. - QuicAckFrame frame(2, 0); + QuicAckFrame frame(2, QuicTime::Zero(), 0); frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash( &connection_, 2); EXPECT_CALL(visitor_, OnAck(_)); ProcessAckPacket(&frame); // Now change it to 1, and it should cause a connection error. - QuicAckFrame frame2(1, 0); + QuicAckFrame frame2(1, QuicTime::Zero(), 0); EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); ProcessAckPacket(&frame2); } TEST_F(QuicConnectionTest, LeastUnackedGreaterThanPacketSequenceNumber) { EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, !kHasData)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); // Create an ack with least_unacked is 2 in packet number 1. creator_.set_sequence_number(0); - QuicAckFrame frame(0, 2); + QuicAckFrame frame(0, QuicTime::Zero(), 2); ProcessAckPacket(&frame); } @@ -643,8 +691,8 @@ TEST_F(QuicConnectionTest, SendStreamDataToPeer(1, "eep", 6, !kFin, NULL); EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, !kHasData)); - QuicAckFrame frame(0, 1); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + QuicAckFrame frame(0, QuicTime::Zero(), 1); frame.received_info.missing_packets.insert(3); ProcessAckPacket(&frame); } @@ -652,8 +700,8 @@ TEST_F(QuicConnectionTest, TEST_F(QuicConnectionTest, AckUnsentData) { // Ack a packet which has not been sent. EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, !kHasData)); - QuicAckFrame frame(1, 0); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + QuicAckFrame frame(1, QuicTime::Zero(), 0); ProcessAckPacket(&frame); } @@ -661,7 +709,7 @@ TEST_F(QuicConnectionTest, AckAll) { ProcessPacket(1); creator_.set_sequence_number(1); - QuicAckFrame frame1(0, 1); + QuicAckFrame frame1(0, QuicTime::Zero(), 1); ProcessAckPacket(&frame1); } @@ -694,7 +742,7 @@ TEST_F(QuicConnectionTest, BasicSending) { // Client acks up to packet 3 EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); - QuicAckFrame frame(3, 0); + QuicAckFrame frame(3, QuicTime::Zero(), 0); frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(&connection_, 3); ProcessAckPacket(&frame); @@ -709,7 +757,7 @@ TEST_F(QuicConnectionTest, BasicSending) { // Client acks up to packet 4, the last packet EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); - QuicAckFrame frame2(6, 0); + QuicAckFrame frame2(6, QuicTime::Zero(), 0); frame2.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(&connection_, 6); ProcessAckPacket(&frame2); // Even parity triggers ack packet 7 @@ -730,14 +778,14 @@ TEST_F(QuicConnectionTest, BasicSending) { TEST_F(QuicConnectionTest, FECSending) { // Limit to one byte per packet. + // All packets carry version info till version is negotiated. connection_.options()->max_packet_length = - GetPacketLengthForOneStream(!kIncludeVersion, 1); + GetPacketLengthForOneStream(kIncludeVersion, 1); // And send FEC every two packets. connection_.options()->max_packets_per_fec_group = 2; // Send 4 data packets and 2 FEC packets. - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, kHasData)).Times(4); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, !kHasData)).Times(2); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(6); connection_.SendStreamData(1, "food", 0, !kFin); // Expect the FEC group to be closed after SendStreamData. EXPECT_FALSE(creator_.ShouldSendFec(true)); @@ -745,8 +793,9 @@ TEST_F(QuicConnectionTest, FECSending) { TEST_F(QuicConnectionTest, FECQueueing) { // Limit to one byte per packet. + // All packets carry version info till version is negotiated. connection_.options()->max_packet_length = - GetPacketLengthForOneStream(!kIncludeVersion, 1); + GetPacketLengthForOneStream(kIncludeVersion, 1); // And send FEC every two packets. connection_.options()->max_packets_per_fec_group = 2; @@ -774,7 +823,7 @@ TEST_F(QuicConnectionTest, FramePacking) { // Unblock the connection. helper_->UnregisterSendAlarmIfRegistered(); EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, !kIsRetransmission, kHasData)) + SentPacket(_, _, _, !kIsRetransmission)) .Times(1); connection_.OnCanWrite(); EXPECT_EQ(0u, connection_.NumQueuedPackets()); @@ -807,9 +856,7 @@ TEST_F(QuicConnectionTest, FramePackingFEC) { // Unblock the connection. helper_->UnregisterSendAlarmIfRegistered(); EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, !kIsRetransmission, kHasData)); - EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, !kIsRetransmission, !kHasData)); + SentPacket(_, _, _, !kIsRetransmission)).Times(2); connection_.OnCanWrite(); EXPECT_EQ(0u, connection_.NumQueuedPackets()); EXPECT_FALSE(connection_.HasQueuedData()); @@ -829,7 +876,7 @@ TEST_F(QuicConnectionTest, OnCanWrite) { Return(false))); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, !kIsRetransmission)).WillRepeatedly( + TimeUntilSend(_, !kIsRetransmission, _)).WillRepeatedly( testing::Return(QuicTime::Delta::Zero())); // Unblock the connection. @@ -845,9 +892,12 @@ TEST_F(QuicConnectionTest, OnCanWrite) { TEST_F(QuicConnectionTest, RetransmitOnNack) { EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2); EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(2, _)).Times(1); QuicPacketSequenceNumber last_packet; + QuicByteCount second_packet_size; SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1 - SendStreamDataToPeer(1, "foos", 3, !kFin, &last_packet); // Packet 2 + second_packet_size = + SendStreamDataToPeer(1, "foos", 3, !kFin, &last_packet); // Packet 2 SendStreamDataToPeer(1, "fooos", 7, !kFin, &last_packet); // Packet 3 SequenceNumberSet expected_acks; @@ -856,7 +906,7 @@ TEST_F(QuicConnectionTest, RetransmitOnNack) { // Client acks one but not two or three. Right now we only retransmit on // explicit nack, so it should not trigger a retransimission. - QuicAckFrame ack_one(1, 0); + QuicAckFrame ack_one(1, QuicTime::Zero(), 0); ack_one.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); ProcessAckPacket(&ack_one); @@ -869,7 +919,7 @@ TEST_F(QuicConnectionTest, RetransmitOnNack) { // Client acks up to 3 with two explicitly missing. Two nacks should cause no // change. - QuicAckFrame nack_two(3, 0); + QuicAckFrame nack_two(3, QuicTime::Zero(), 0); nack_two.received_info.missing_packets.insert(2); nack_two.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^ @@ -880,8 +930,8 @@ TEST_F(QuicConnectionTest, RetransmitOnNack) { // The third nack should trigger a retransimission. EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, 37, kIsRetransmission, kHasData)) - .Times(1); + SentPacket(_, _, second_packet_size - kQuicVersionSize, + kIsRetransmission)).Times(1); ProcessAckPacket(&nack_two); } @@ -889,29 +939,29 @@ TEST_F(QuicConnectionTest, RetransmitNackedLargestObserved) { EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); QuicPacketSequenceNumber largest_observed; QuicByteCount packet_size; - EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, !kIsRetransmission, kHasData)) + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, !kIsRetransmission)) .WillOnce(DoAll(SaveArg<1>(&largest_observed), SaveArg<2>(&packet_size))); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)).Times(1); connection_.SendStreamData(1, "foo", 0, !kFin); - QuicAckFrame frame(1, largest_observed); + QuicAckFrame frame(1, QuicTime::Zero(), largest_observed); frame.received_info.missing_packets.insert(largest_observed); frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash( &connection_, largest_observed - 1); ProcessAckPacket(&frame); // Second udp packet will force an ack frame. EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, !kIsRetransmission, !kHasData)); + SentPacket(_, _, _, !kIsRetransmission)); ProcessAckPacket(&frame); // Third nack should retransmit the largest observed packet. - EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, packet_size, kIsRetransmission, kHasData)); - + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, packet_size - kQuicVersionSize, + kIsRetransmission)); ProcessAckPacket(&frame); } TEST_F(QuicConnectionTest, LimitPacketsPerNack) { EXPECT_CALL(*send_algorithm_, OnIncomingAck(12, _, _)).Times(1); EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(11); int offset = 0; // Send packets 1 to 12 for (int i = 0; i < 12; ++i) { @@ -920,7 +970,7 @@ TEST_F(QuicConnectionTest, LimitPacketsPerNack) { } // Ack 12, nack 1-11 - QuicAckFrame nack(12, 0); + QuicAckFrame nack(12, QuicTime::Zero(), 0); for (int i = 1; i < 12; ++i) { nack.received_info.missing_packets.insert(i); } @@ -935,15 +985,14 @@ TEST_F(QuicConnectionTest, LimitPacketsPerNack) { // Nack three times. ProcessAckPacket(&nack); // The second call will trigger an ack. - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, !kHasData)).Times(1); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1); ProcessAckPacket(&nack); // The third call should trigger retransmitting 10 packets. - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, kHasData)).Times(10); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(10); ProcessAckPacket(&nack); // The fourth call should trigger retransmitting the 11th packet and an ack. - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, kHasData)).Times(1); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, !kHasData)).Times(1); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(2); ProcessAckPacket(&nack); } @@ -965,7 +1014,7 @@ TEST_F(QuicConnectionTest, MultipleAcks) { EXPECT_EQ(6u, last_packet); // Client will ack packets 1, [!2], 3, 4, 5 - QuicAckFrame frame1(5, 0); + QuicAckFrame frame1(5, QuicTime::Zero(), 0); frame1.received_info.missing_packets.insert(2); frame1.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(&connection_, 5) ^ @@ -983,7 +1032,7 @@ TEST_F(QuicConnectionTest, MultipleAcks) { ProcessAckPacket(&frame1); // Now the client implicitly acks 2, and explicitly acks 6 - QuicAckFrame frame2(6, 0); + QuicAckFrame frame2(6, QuicTime::Zero(), 0); frame2.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash(&connection_, 6); expected_acks.clear(); @@ -1006,7 +1055,7 @@ TEST_F(QuicConnectionTest, DontLatchUnackedPacket) { expected_acks.insert(1); // Client acks packet 1 EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); - QuicAckFrame frame(1, 0); + QuicAckFrame frame(1, QuicTime::Zero(), 0); frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash( &connection_, 1); ProcessAckPacket(&frame); @@ -1081,7 +1130,8 @@ TEST_F(QuicConnectionTest, TestRetransmit) { EXPECT_EQ(default_retransmission_time, helper_->retransmission_alarm()); // Simulate the retransimission alarm firing clock_.AdvanceTime(kDefaultRetransmissionTime); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, kHasData)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)).Times(1); connection_.RetransmitPacket(1); EXPECT_EQ(2u, last_header()->packet_sequence_number); EXPECT_EQ(2u, outgoing_ack()->sent_info.least_unacked); @@ -1089,11 +1139,13 @@ TEST_F(QuicConnectionTest, TestRetransmit) { TEST_F(QuicConnectionTest, TestRetransmitOrder) { QuicByteCount first_packet_size; - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, kHasData)).WillOnce( + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).WillOnce( SaveArg<2>(&first_packet_size)); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(2); + connection_.SendStreamData(1, "first_packet", 0, !kFin); QuicByteCount second_packet_size; - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, kHasData)).WillOnce( + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).WillOnce( SaveArg<2>(&second_packet_size)); connection_.SendStreamData(1, "second_packet", 12, !kFin); EXPECT_NE(first_packet_size, second_packet_size); @@ -1102,18 +1154,19 @@ TEST_F(QuicConnectionTest, TestRetransmitOrder) { { InSequence s; EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, first_packet_size, _, kHasData)); + SentPacket(_, _, first_packet_size, _)); EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, second_packet_size, _, kHasData)); + SentPacket(_, _, second_packet_size, _)); } connection_.OnRetransmissionTimeout(); } TEST_F(QuicConnectionTest, TestRetransmissionCountCalculation) { EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(2); QuicPacketSequenceNumber original_sequence_number; EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, !kIsRetransmission, kHasData)) + SentPacket(_, _, _, !kIsRetransmission)) .WillOnce(SaveArg<1>(&original_sequence_number)); connection_.SendStreamData(1, "foo", 0, !kFin); EXPECT_TRUE(QuicConnectionPeer::IsSavedForRetransmission( @@ -1124,7 +1177,7 @@ TEST_F(QuicConnectionTest, TestRetransmissionCountCalculation) { clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10)); QuicPacketSequenceNumber rto_sequence_number; EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, kIsRetransmission, kHasData)) + SentPacket(_, _, _, kIsRetransmission)) .WillOnce(SaveArg<1>(&rto_sequence_number)); connection_.OnRetransmissionTimeout(); EXPECT_FALSE(QuicConnectionPeer::IsSavedForRetransmission( @@ -1137,13 +1190,12 @@ TEST_F(QuicConnectionTest, TestRetransmissionCountCalculation) { QuicPacketSequenceNumber nack_sequence_number; // Ack packets might generate some other packets, which are not // retransmissions. (More ack packets). - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, !kIsRetransmission, _)) + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, !kIsRetransmission)) .Times(AnyNumber()); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, kIsRetransmission, _)) + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, kIsRetransmission)) .WillOnce(SaveArg<1>(&nack_sequence_number)); - QuicAckFrame ack(rto_sequence_number, 0); + QuicAckFrame ack(rto_sequence_number, QuicTime::Zero(), 0); // Ack the retransmitted packet. - EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1); ack.received_info.missing_packets.insert(rto_sequence_number); ack.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash( &connection_, rto_sequence_number - 1); @@ -1197,7 +1249,7 @@ TEST_F(QuicConnectionTest, CloseFecGroup) { ASSERT_EQ(1u, connection_.NumFecGroups()); // Now send non-fec protected ack packet and close the group - QuicAckFrame frame(0, 5); + QuicAckFrame frame(0, QuicTime::Zero(), 5); creator_.set_sequence_number(4); ProcessAckPacket(&frame); ASSERT_EQ(0u, connection_.NumFecGroups()); @@ -1236,7 +1288,7 @@ TEST_F(QuicConnectionTest, DontUpdateQuicCongestionFeedbackFrameForRevived) { TEST_F(QuicConnectionTest, InitialTimeout) { EXPECT_TRUE(connection_.connected()); EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, !kHasData)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); QuicTime default_timeout = clock_.ApproximateNow().Add( QuicTime::Delta::FromMicroseconds(kDefaultTimeoutUs)); @@ -1273,7 +1325,7 @@ TEST_F(QuicConnectionTest, TimeoutAfterSend) { // This time, we should time out. EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, !kHasData)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); EXPECT_EQ(default_timeout.Add(QuicTime::Delta::FromMilliseconds(5)), clock_.ApproximateNow()); @@ -1285,9 +1337,10 @@ TEST_F(QuicConnectionTest, TimeoutAfterSend) { TEST_F(QuicConnectionTest, SendScheduler) { // Test that if we send a packet without delay, it is not queued. QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce( - testing::Return(QuicTime::Delta::Zero())); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, kHasData)); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + testing::Return(QuicTime::Delta::Zero())); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, true); EXPECT_EQ(0u, connection_.NumQueuedPackets()); } @@ -1295,9 +1348,10 @@ TEST_F(QuicConnectionTest, SendScheduler) { TEST_F(QuicConnectionTest, SendSchedulerDelay) { // Test that if we send a packet with a delay, it ends up queued. QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce( - testing::Return(QuicTime::Delta::FromMicroseconds(1))); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _, kHasData)).Times(0); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(1))); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0); connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, true); EXPECT_EQ(1u, connection_.NumQueuedPackets()); } @@ -1305,8 +1359,9 @@ TEST_F(QuicConnectionTest, SendSchedulerDelay) { TEST_F(QuicConnectionTest, SendSchedulerForce) { // Test that if we force send a packet, it is not queued. QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, kIsRetransmission)).Times(0); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, kHasData)); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, kIsRetransmission, _)).Times(0); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, true); // XXX: fixme. was: connection_.SendOrQueuePacket(1, packet, kForce); EXPECT_EQ(0u, connection_.NumQueuedPackets()); @@ -1315,9 +1370,10 @@ TEST_F(QuicConnectionTest, SendSchedulerForce) { TEST_F(QuicConnectionTest, SendSchedulerEAGAIN) { QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); helper_->set_blocked(true); - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce( - testing::Return(QuicTime::Delta::Zero())); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _, kHasData)).Times(0); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + testing::Return(QuicTime::Delta::Zero())); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0); connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, true); EXPECT_EQ(1u, connection_.NumQueuedPackets()); } @@ -1325,46 +1381,51 @@ TEST_F(QuicConnectionTest, SendSchedulerEAGAIN) { TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) { // Test that if we send a packet with a delay, it ends up queued. QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce( - testing::Return(QuicTime::Delta::FromMicroseconds(1))); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(1))); connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, true); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // Advance the clock to fire the alarm, and configure the scheduler // to permit the packet to be sent. - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce( - testing::Return(QuicTime::Delta::Zero())); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, !kIsRetransmission, _)).WillRepeatedly( + testing::Return(QuicTime::Delta::Zero())); clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1)); helper_->UnregisterSendAlarmIfRegistered(); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, kHasData)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); EXPECT_CALL(visitor_, OnCanWrite()); connection_.OnCanWrite(); EXPECT_EQ(0u, connection_.NumQueuedPackets()); } TEST_F(QuicConnectionTest, SendSchedulerDelayThenRetransmit) { - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)) + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission, _)) .WillRepeatedly(testing::Return(QuicTime::Delta::Zero())); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)).Times(1); EXPECT_CALL(*send_algorithm_, - SentPacket(_, 1, _, !kIsRetransmission, kHasData)); + SentPacket(_, 1, _, !kIsRetransmission)); connection_.SendStreamData(1, "foo", 0, !kFin); EXPECT_EQ(0u, connection_.NumQueuedPackets()); // Advance the time for retransmission of lost packet. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(501)); // Test that if we send a retransmit with a delay, it ends up queued. - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, kIsRetransmission)).WillOnce( - testing::Return(QuicTime::Delta::FromMicroseconds(1))); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, kIsRetransmission, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(1))); connection_.OnRetransmissionTimeout(); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // Advance the clock to fire the alarm, and configure the scheduler // to permit the packet to be sent. - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, kIsRetransmission)).WillOnce( - testing::Return(QuicTime::Delta::Zero())); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, kIsRetransmission, _)).WillOnce( + testing::Return(QuicTime::Delta::Zero())); // Ensure the scheduler is notified this is a retransmit. EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, kIsRetransmission, kHasData)); + SentPacket(_, _, _, kIsRetransmission)); clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1)); helper_->UnregisterSendAlarmIfRegistered(); EXPECT_CALL(visitor_, OnCanWrite()); @@ -1374,8 +1435,9 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenRetransmit) { TEST_F(QuicConnectionTest, SendSchedulerDelayAndQueue) { QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce( - testing::Return(QuicTime::Delta::FromMicroseconds(1))); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(1))); connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, true); EXPECT_EQ(1u, connection_.NumQueuedPackets()); @@ -1387,19 +1449,20 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayAndQueue) { TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) { QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce( - testing::Return(QuicTime::Delta::FromMicroseconds(10))); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(10))); connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, true); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // Now send non-retransmitting information, that we're not going to // retransmit 3. The far end should stop waiting for it. - QuicAckFrame frame(0, 1); + QuicAckFrame frame(0, QuicTime::Zero(), 1); EXPECT_CALL(*send_algorithm_, - TimeUntilSend(_, !kIsRetransmission)).WillRepeatedly( + TimeUntilSend(_, !kIsRetransmission, _)).WillRepeatedly( testing::Return(QuicTime::Delta::Zero())); EXPECT_CALL(*send_algorithm_, - SentPacket(_, _, _, _, kHasData)); + SentPacket(_, _, _, _)); EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(Return(true)); ProcessAckPacket(&frame); @@ -1410,16 +1473,18 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) { TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) { QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce( - testing::Return(QuicTime::Delta::FromMicroseconds(10))); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(10))); connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, kHasData); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // Now send non-retransmitting information, that we're not going to // retransmit 3. The far end should stop waiting for it. - QuicAckFrame frame(0, 1); - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce( - testing::Return(QuicTime::Delta::FromMicroseconds(1))); + QuicAckFrame frame(0, QuicTime::Zero(), 1); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(1))); ProcessAckPacket(&frame); EXPECT_EQ(1u, connection_.NumQueuedPackets()); @@ -1427,26 +1492,28 @@ TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) { TEST_F(QuicConnectionTest, SendSchedulerDelayThenOnCanWrite) { QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce( - testing::Return(QuicTime::Delta::FromMicroseconds(10))); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(10))); connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, kHasData); EXPECT_EQ(1u, connection_.NumQueuedPackets()); // OnCanWrite should not send the packet (because of the delay) // but should still return true. - EXPECT_CALL(visitor_, OnCanWrite()); EXPECT_TRUE(connection_.OnCanWrite()); EXPECT_EQ(1u, connection_.NumQueuedPackets()); } TEST_F(QuicConnectionTest, TestQueueLimitsOnSendStreamData) { // Limit to one byte per packet. + // All packets carry version info till version is negotiated. connection_.options()->max_packet_length = - GetPacketLengthForOneStream(!kIncludeVersion, 1); + GetPacketLengthForOneStream(kIncludeVersion, 1); // Queue the first packet. - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, !kIsRetransmission)).WillOnce( - testing::Return(QuicTime::Delta::FromMicroseconds(10))); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, !kIsRetransmission, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(10))); EXPECT_EQ(0u, connection_.SendStreamData( 1, "EnoughDataToQueue", 0, !kFin).bytes_consumed); EXPECT_EQ(0u, connection_.NumQueuedPackets()); @@ -1454,11 +1521,12 @@ TEST_F(QuicConnectionTest, TestQueueLimitsOnSendStreamData) { TEST_F(QuicConnectionTest, LoopThroughSendingPackets) { // Limit to one byte per packet. + // All packets carry version info till version is negotiated. connection_.options()->max_packet_length = - GetPacketLengthForOneStream(!kIncludeVersion, 1); + GetPacketLengthForOneStream(kIncludeVersion, 1); // Queue the first packet. - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, kHasData)).Times(17); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(17); EXPECT_EQ(17u, connection_.SendStreamData( 1, "EnoughDataToQueue", 0, !kFin).bytes_consumed); } @@ -1467,7 +1535,7 @@ TEST_F(QuicConnectionTest, NoAckForClose) { ProcessPacket(1); EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(0); EXPECT_CALL(visitor_, ConnectionClose(QUIC_PEER_GOING_AWAY, true)); - EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _, kHasData)).Times(0); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(0); ProcessClosePacket(2, 0); } @@ -1477,7 +1545,7 @@ TEST_F(QuicConnectionTest, SendWhenDisconnected) { connection_.CloseConnection(QUIC_PEER_GOING_AWAY, false); EXPECT_FALSE(connection_.connected()); QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); - EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _, kHasData)).Times(0); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0); connection_.SendOrQueuePacket(1, packet, kTestEntropyHash, kHasData); } @@ -1503,7 +1571,7 @@ TEST_F(QuicConnectionTest, GoAway) { } TEST_F(QuicConnectionTest, MissingPacketsBeforeLeastUnacked) { - QuicAckFrame ack(0, 4); + QuicAckFrame ack(0, QuicTime::Zero(), 4); // Set the sequence number of the ack packet to be least unacked (4) creator_.set_sequence_number(3); ProcessAckPacket(&ack); @@ -1526,7 +1594,7 @@ TEST_F(QuicConnectionTest, UpdateEntropyForReceivedPackets) { ProcessDataPacket(4, 1, !kEntropyFlag); EXPECT_EQ(34u, outgoing_ack()->received_info.entropy_hash); // Make 4th packet my least unacked, and update entropy for 2, 3 packets. - QuicAckFrame ack(0, 4); + QuicAckFrame ack(0, QuicTime::Zero(), 4); QuicPacketEntropyHash kRandomEntropyHash = 129u; ack.sent_info.entropy_hash = kRandomEntropyHash; creator_.set_sequence_number(5); @@ -1548,7 +1616,7 @@ TEST_F(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) { creator_.set_sequence_number(22); QuicPacketEntropyHash kRandomEntropyHash = 85u; // Current packet is the least unacked packet. - QuicAckFrame ack(0, 23); + QuicAckFrame ack(0, QuicTime::Zero(), 23); ack.sent_info.entropy_hash = kRandomEntropyHash; QuicPacketEntropyHash ack_entropy_hash = ProcessAckPacket(&ack); EXPECT_EQ((kRandomEntropyHash + ack_entropy_hash), @@ -1610,6 +1678,111 @@ TEST_F(QuicConnectionTest, CheckSentEntropyHash) { << ""; } +// TODO(satyamsehkhar): Add more test when we start supporting more versions. +TEST_F(QuicConnectionTest, SendVersionNegotiationPacket) { + QuicVersionTag kRandomVersion = 143; + QuicFramerPeer::SetVersion(&framer_, kRandomVersion); + + QuicPacketHeader header; + header.public_header.guid = guid_; + header.public_header.reset_flag = false; + header.public_header.version_flag = true; + header.entropy_flag = false; + header.fec_flag = false; + header.fec_entropy_flag = false; + header.packet_sequence_number = 12; + header.fec_group = 0; + + QuicFrames frames; + QuicFrame frame(&frame1_); + frames.push_back(frame); + scoped_ptr<QuicPacket> packet( + framer_.ConstructFrameDataPacket(header, frames).packet); + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(12, *packet)); + + QuicFramerPeer::SetVersion(&framer_, kQuicVersion1); + connection_.set_is_server(true); + connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); + EXPECT_TRUE(helper_->version_negotiation_packet() != NULL); + EXPECT_EQ(1u, + helper_->version_negotiation_packet()->versions.size()); + EXPECT_EQ(kQuicVersion1, + helper_->version_negotiation_packet()->versions[0]); +} + +TEST_F(QuicConnectionTest, CheckSendStats) { + EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(3); + EXPECT_CALL(*send_algorithm_, + SentPacket(_, _, _, !kIsRetransmission)); + connection_.SendStreamData(1u, "first", 0, !kFin); + size_t first_packet_size = last_sent_packet_size(); + + EXPECT_CALL(*send_algorithm_, + SentPacket(_, _, _, !kIsRetransmission)).Times(2); + connection_.SendStreamData(1u, "second", 0, !kFin); + size_t second_packet_size = last_sent_packet_size(); + + // 2 retransmissions due to rto, 1 due to explicit nack. + EXPECT_CALL(*send_algorithm_, + SentPacket(_, _, _, kIsRetransmission)).Times(3); + + // Retransmit due to RTO. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10)); + connection_.OnRetransmissionTimeout(); + + // Retransmit due to explicit nacks + QuicAckFrame nack_three(4, QuicTime::Zero(), 0); + nack_three.received_info.missing_packets.insert(3); + nack_three.received_info.entropy_hash = + QuicConnectionPeer::GetSentEntropyHash(&connection_, 4) ^ + QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^ + QuicConnectionPeer::GetSentEntropyHash(&connection_, 2); + QuicFrame frame(&nack_three); + EXPECT_CALL(visitor_, OnAck(_)); + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1); + EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); + + ProcessFramePacket(frame); + ProcessFramePacket(frame); + size_t ack_packet_size = last_sent_packet_size(); + ProcessFramePacket(frame); + + EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillOnce( + Return(QuicTime::Delta::Zero())); + EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce( + Return(QuicBandwidth::Zero())); + + const QuicConnectionStats& stats = connection_.GetStats(); + EXPECT_EQ(3 * first_packet_size + 2 * second_packet_size + ack_packet_size - + kQuicVersionSize, stats.bytes_sent); + EXPECT_EQ(6u, stats.packets_sent); + EXPECT_EQ(2 * first_packet_size + second_packet_size - kQuicVersionSize, + stats.bytes_retransmitted); + EXPECT_EQ(3u, stats.packets_retransmitted); + EXPECT_EQ(2u, stats.rto_count); +} + +TEST_F(QuicConnectionTest, CheckReceiveStats) { + size_t received_bytes = 0; + received_bytes += ProcessFecProtectedPacket(1, false, !kEntropyFlag); + received_bytes += ProcessFecProtectedPacket(3, false, !kEntropyFlag); + // Should be counted against dropped packets. + received_bytes += ProcessDataPacket(3, 1, !kEntropyFlag); + received_bytes += ProcessFecPacket(4, 1, true, !kEntropyFlag); // Fec packet + + EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillOnce( + Return(QuicTime::Delta::Zero())); + EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce( + Return(QuicBandwidth::Zero())); + + const QuicConnectionStats& stats = connection_.GetStats(); + EXPECT_EQ(received_bytes, stats.bytes_received); + EXPECT_EQ(4u, stats.packets_received); + + EXPECT_EQ(1u, stats.packets_revived); + EXPECT_EQ(1u, stats.packets_dropped); +} + } // namespace } // namespace test } // namespace net diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc index cccbc0a..a84a756 100644 --- a/net/quic/quic_crypto_client_stream.cc +++ b/net/quic/quic_crypto_client_stream.cc @@ -16,6 +16,10 @@ QuicCryptoClientStream::QuicCryptoClientStream(QuicSession* session, : QuicCryptoStream(session), server_hostname_(server_hostname) { config_.SetDefaults(); + + QuicGuid guid = session->connection()->guid(); + crypto_config_.hkdf_info.append(reinterpret_cast<char*>(&guid), + sizeof(guid)); } QuicCryptoClientStream::~QuicCryptoClientStream() { @@ -46,7 +50,7 @@ void QuicCryptoClientStream::OnHandshakeMessage( } QuicErrorCode err = crypto_config_.ProcessServerHello( - message, &crypto_negotiated_params_, &error_details); + message, nonce_, &crypto_negotiated_params_, &error_details); if (err != QUIC_NO_ERROR) { CloseConnectionWithDetails(err, error_details); return; @@ -64,6 +68,8 @@ bool QuicCryptoClientStream::CryptoConnect() { CryptoHandshakeMessage message; crypto_config_.FillClientHello(nonce_, server_hostname_, &message); config_.ToHandshakeMessage(&message); + const QuicData& data = message.GetSerialized(); + crypto_config_.hkdf_info.append(data.data(), data.length()); SendHandshakeMessage(message); return true; } diff --git a/net/quic/quic_crypto_client_stream.h b/net/quic/quic_crypto_client_stream.h index 1f76c110..33cf6c4 100644 --- a/net/quic/quic_crypto_client_stream.h +++ b/net/quic/quic_crypto_client_stream.h @@ -15,6 +15,10 @@ namespace net { class QuicSession; struct CryptoHandshakeMessage; +namespace test { +class CryptoTestUtils; +} // namespace test + class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { public: QuicCryptoClientStream(QuicSession* session, const string& server_hostname); @@ -29,6 +33,8 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { bool CryptoConnect(); private: + friend class test::CryptoTestUtils; + QuicConfig config_; QuicCryptoClientConfig crypto_config_; diff --git a/net/quic/quic_crypto_client_stream_test.cc b/net/quic/quic_crypto_client_stream_test.cc index 29ae7cb..10dcb5b 100644 --- a/net/quic/quic_crypto_client_stream_test.cc +++ b/net/quic/quic_crypto_client_stream_test.cc @@ -61,7 +61,7 @@ class QuicCryptoClientStreamTest : public ::testing::Test { public: QuicCryptoClientStreamTest() : addr_(), - connection_(new PacketSavingConnection(1, addr_)), + connection_(new PacketSavingConnection(1, addr_, true)), session_(connection_, true), stream_(&session_, kServerHostname) { } diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc index 0f4d4f4..8fdd9da 100644 --- a/net/quic/quic_crypto_server_stream.cc +++ b/net/quic/quic_crypto_server_stream.cc @@ -17,6 +17,10 @@ QuicCryptoServerStream::QuicCryptoServerStream(QuicSession* session) // Use hardcoded crypto parameters for now. CryptoHandshakeMessage extra_tags; config_.ToHandshakeMessage(&extra_tags); + + QuicGuid guid = session->connection()->guid(); + crypto_config_.hkdf_info.append(reinterpret_cast<char*>(&guid), + sizeof(guid)); // TODO(agl): AddTestingConfig generates a new, random config. In the future // this will be replaced with a real source of configs. scoped_ptr<CryptoTagValueMap> config_tags( @@ -56,9 +60,8 @@ void QuicCryptoServerStream::OnHandshakeMessage( CryptoUtils::GenerateNonce(session()->connection()->clock(), session()->connection()->random_generator(), &server_nonce_); - QuicCryptoNegotiatedParams params; - crypto_config_.ProcessClientHello(message, server_nonce_, &shlo, ¶ms, - &error_details); + crypto_config_.ProcessClientHello(message, server_nonce_, &shlo, + &crypto_negotiated_params_, &error_details); if (!error_details.empty()) { DLOG(INFO) << "Rejecting CHLO: " << error_details; } diff --git a/net/quic/quic_crypto_server_stream.h b/net/quic/quic_crypto_server_stream.h index 5f9ddd1..ac63495 100644 --- a/net/quic/quic_crypto_server_stream.h +++ b/net/quic/quic_crypto_server_stream.h @@ -15,6 +15,10 @@ namespace net { class QuicSession; struct CryptoHandshakeMessage; +namespace test { +class CryptoTestUtils; +} // namespace test + class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { public: explicit QuicCryptoServerStream(QuicSession* session); @@ -25,6 +29,8 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { const CryptoHandshakeMessage& message) OVERRIDE; private: + friend class test::CryptoTestUtils; + // config_ contains non-crypto parameters that are negotiated in the crypto // handshake. QuicConfig config_; diff --git a/net/quic/quic_crypto_server_stream_test.cc b/net/quic/quic_crypto_server_stream_test.cc index 995cb6b..db8841d 100644 --- a/net/quic/quic_crypto_server_stream_test.cc +++ b/net/quic/quic_crypto_server_stream_test.cc @@ -67,7 +67,7 @@ class QuicCryptoServerStreamTest : public ::testing::Test { QuicCryptoServerStreamTest() : guid_(1), addr_(), - connection_(new PacketSavingConnection(guid_, addr_)), + connection_(new PacketSavingConnection(guid_, addr_, true)), session_(connection_, true), stream_(&session_) { } diff --git a/net/quic/quic_crypto_stream.cc b/net/quic/quic_crypto_stream.cc index 712c8fd..d34e6ec 100644 --- a/net/quic/quic_crypto_stream.cc +++ b/net/quic/quic_crypto_stream.cc @@ -3,8 +3,15 @@ // found in the LICENSE file. #include "net/quic/quic_crypto_stream.h" + +#include <string> + +#include "base/string_piece.h" +#include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/quic_connection.h" #include "net/quic/quic_session.h" +using std::string; using base::StringPiece; namespace net { @@ -49,9 +56,9 @@ void QuicCryptoStream::SetHandshakeComplete(QuicErrorCode error) { void QuicCryptoStream::SendHandshakeMessage( const CryptoHandshakeMessage& message) { - scoped_ptr<QuicData> data(crypto_framer_.ConstructHandshakeMessage(message)); + const QuicData& data = message.GetSerialized(); // TODO(wtc): check the return value. - WriteData(string(data->data(), data->length()), false); + WriteData(string(data.data(), data.length()), false); } QuicNegotiatedParameters::QuicNegotiatedParameters() diff --git a/net/quic/quic_crypto_stream.h b/net/quic/quic_crypto_stream.h index fa12ce3..fb2f6c0 100644 --- a/net/quic/quic_crypto_stream.h +++ b/net/quic/quic_crypto_stream.h @@ -28,7 +28,6 @@ struct CryptoHandshakeMessage; class NET_EXPORT_PRIVATE QuicCryptoStream : public ReliableQuicStream, public CryptoFramerVisitorInterface { - public: explicit QuicCryptoStream(QuicSession* session); diff --git a/net/quic/quic_crypto_stream_test.cc b/net/quic/quic_crypto_stream_test.cc index 10f1657..98d3fb2 100644 --- a/net/quic/quic_crypto_stream_test.cc +++ b/net/quic/quic_crypto_stream_test.cc @@ -8,6 +8,7 @@ #include <vector> #include "base/memory/scoped_ptr.h" +#include "net/quic/crypto/crypto_handshake.h" #include "net/quic/test_tools/quic_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -44,7 +45,7 @@ class QuicCryptoStreamTest : public ::testing::Test { public: QuicCryptoStreamTest() : addr_(IPAddressNumber(), 1), - connection_(new MockConnection(1, addr_)), + connection_(new MockConnection(1, addr_, false)), session_(connection_, true), stream_(&session_) { message_.tag = kSHLO; diff --git a/net/quic/quic_data_reader.h b/net/quic/quic_data_reader.h index 46f37bf..5477bd1 100644 --- a/net/quic/quic_data_reader.h +++ b/net/quic/quic_data_reader.h @@ -58,6 +58,7 @@ class NET_EXPORT_PRIVATE QuicDataReader { // Forwards the internal iterator on success. // Returns true on success, false otherwise. bool ReadUInt128(uint128* result); + // Reads a string prefixed with 16-bit length into the given output parameter. // // NOTE: Does not copy but rather references strings in the underlying buffer. diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index 2329605..462172c 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -45,15 +45,17 @@ QuicPacketSequenceNumber ClosestTo(QuicPacketSequenceNumber target, QuicFramer::QuicFramer(QuicVersionTag version, QuicDecrypter* decrypter, - QuicEncrypter* encrypter) + QuicEncrypter* encrypter, + bool is_server) : visitor_(NULL), fec_builder_(NULL), error_(QUIC_NO_ERROR), last_sequence_number_(0), quic_version_(version), decrypter_(decrypter), - encrypter_(encrypter) { - DCHECK_EQ(kQuicVersion1, version); + encrypter_(encrypter), + is_server_(is_server) { + DCHECK(IsSupportedVersion(version)); } QuicFramer::~QuicFramer() {} @@ -96,6 +98,15 @@ size_t QuicFramer::GetMinGoAwayFrameSize() { kQuicStreamIdSize; } +bool QuicFramer::IsSupportedVersion(QuicVersionTag version) { + return version == kQuicVersion1; +} + +size_t QuicFramer::GetVersionNegotiationPacketSize(size_t number_versions) { + return kQuicGuidSize + kPublicFlagsSize + + number_versions * kQuicVersionSize; +} + size_t QuicFramer::GetSerializedFrameLength( const QuicFrame& frame, size_t free_bytes, bool first_frame) { if (frame.type == PADDING_FRAME) { @@ -245,7 +256,6 @@ SerializedPacket QuicFramer::ConstructFecPacket(const QuicPacketHeader& header, QuicEncryptedPacket* QuicFramer::ConstructPublicResetPacket( const QuicPublicResetPacket& packet) { DCHECK(packet.public_header.reset_flag); - DCHECK(!packet.public_header.version_flag); size_t len = GetPublicResetPacketSize(); QuicDataWriter writer(len); @@ -270,23 +280,56 @@ QuicEncryptedPacket* QuicFramer::ConstructPublicResetPacket( return new QuicEncryptedPacket(writer.take(), len, true); } +QuicEncryptedPacket* QuicFramer::ConstructVersionNegotiationPacket( + const QuicPacketPublicHeader& header, + const QuicVersionTagList& supported_versions) { + DCHECK(header.version_flag); + size_t len = GetVersionNegotiationPacketSize(supported_versions.size()); + QuicDataWriter writer(len); + + if (!writer.WriteUInt64(header.guid)) { + return NULL; + } + + uint8 flags = static_cast<uint8>(PACKET_PUBLIC_FLAGS_VERSION); + if (!writer.WriteUInt8(flags)) { + return NULL; + } + + for (size_t i = 0; i < supported_versions.size(); ++i) { + if (!writer.WriteUInt32(supported_versions[i])) { + return NULL; + } + } + + return new QuicEncryptedPacket(writer.take(), len, true); +} + bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) { DCHECK(!reader_.get()); reader_.reset(new QuicDataReader(packet.data(), packet.length())); + visitor_->OnPacket(); + // First parse the public header. QuicPacketPublicHeader public_header; if (!ProcessPublicHeader(&public_header)) { DLOG(WARNING) << "Unable to process public header."; return RaiseError(QUIC_INVALID_PACKET_HEADER); } - // TODO(satyamshekhar): Handle version negotiation. - if (public_header.version_flag && public_header.version != quic_version_) { - return false; + + if (is_server_ && public_header.version_flag && + public_header.versions[0] != quic_version_) { + if (!visitor_->OnProtocolVersionMismatch(public_header.versions[0])) { + reader_.reset(NULL); + return true; + } } bool rv; - if (public_header.reset_flag) { + if (!is_server_ && public_header.version_flag) { + rv = ProcessVersionNegotiationPacket(&public_header); + } else if (public_header.reset_flag) { rv = ProcessPublicResetPacket(public_header); } else { rv = ProcessDataPacket(public_header, packet); @@ -296,11 +339,26 @@ bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) { return rv; } +bool QuicFramer::ProcessVersionNegotiationPacket( + QuicPacketPublicHeader* public_header) { + DCHECK(!is_server_); + // Try reading at least once to raise error if the packet is invalid. + do { + QuicVersionTag version; + if (!reader_->ReadBytes(&version, kQuicVersionSize)) { + set_detailed_error("Unable to read supported version in negotiation."); + return RaiseError(QUIC_INVALID_VERSION_NEGOTIATION_PACKET); + } + public_header->versions.push_back(version); + } while (!reader_->IsDoneReading()); + + visitor_->OnVersionNegotiationPacket(*public_header); + return true; +} + bool QuicFramer::ProcessDataPacket( const QuicPacketPublicHeader& public_header, const QuicEncryptedPacket& packet) { - visitor_->OnPacket(); - QuicPacketHeader header(public_header); if (!ProcessPacketHeader(&header, packet)) { DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessPacketHeader sets the error. @@ -344,6 +402,7 @@ bool QuicFramer::ProcessPublicResetPacket( const QuicPacketPublicHeader& public_header) { QuicPublicResetPacket packet(public_header); if (!reader_->ReadUInt64(&packet.nonce_proof)) { + // TODO(satyamshekhar): Raise error. set_detailed_error("Unable to read nonce proof."); return false; } @@ -403,6 +462,7 @@ bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header, } if (header.public_header.version_flag) { + DCHECK(!is_server_); writer->WriteUInt32(quic_version_); } @@ -481,12 +541,21 @@ bool QuicFramer::ProcessPublicHeader(QuicPacketPublicHeader* public_header) { public_header->version_flag = (public_flags & PACKET_PUBLIC_FLAGS_VERSION) != 0; - if (public_header->version_flag && - !reader_->ReadUInt32(&public_header->version)) { - set_detailed_error("Unable to read protocol version."); + if (public_header->reset_flag && public_header->version_flag) { + set_detailed_error("Got version flag in reset packet"); return false; } + if (public_header->version_flag && is_server_) { + QuicVersionTag version; + if (!reader_->ReadUInt32(&version)) { + // Read the version only if the packet is from the client. + // version flag from the server means version negotiation packet. + set_detailed_error("Unable to read protocol version."); + return false; + } + public_header->versions.push_back(version); + } return true; } @@ -673,6 +742,7 @@ bool QuicFramer::ProcessReceivedInfo(ReceivedPacketInfo* received_info) { set_detailed_error("Unable to read largest observed."); return false; } + // TODO(pwestin): read and update delta_time_largest_observed. uint8 num_missing_packets; if (!reader_->ReadBytes(&num_missing_packets, 1)) { @@ -889,8 +959,8 @@ bool QuicFramer::ProcessGoAwayFrame() { StringPiece QuicFramer::GetAssociatedDataFromEncryptedPacket( const QuicEncryptedPacket& encrypted, bool includes_version) { return StringPiece(encrypted.data() + kStartOfHashData, - GetStartOfEncryptedData(includes_version) - - kStartOfHashData); + GetStartOfEncryptedData(includes_version) - + kStartOfHashData); } QuicEncryptedPacket* QuicFramer::EncryptPacket( @@ -1076,6 +1146,8 @@ bool QuicFramer::AppendAckFramePayload( writer)) { return false; } + // TODO(pwestin): calculate and add delta_time_largest_observed to the + // message. // We don't check for overflowing uint8 here, because we only can fit 192 acks // per packet, so if we overflow we will be truncated. diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h index 86b255d..7a00aaa 100644 --- a/net/quic/quic_framer.h +++ b/net/quic/quic_framer.h @@ -56,6 +56,13 @@ class NET_EXPORT_PRIVATE QuicFramerVisitorInterface { // Called if an error is detected in the QUIC protocol. virtual void OnError(QuicFramer* framer) = 0; + // Called only when |is_server_| is true and the the framer gets a packet with + // version flag true and the version on the packet doesn't match + // |quic_version_|. The visitor should return true after it updates the + // version of the |framer_| to |received_version| or false to stop processing + // this packet. + virtual bool OnProtocolVersionMismatch(QuicVersionTag received_version) = 0; + // Called when a new packet has been received, before it // has been validated or processed. virtual void OnPacket() = 0; @@ -65,12 +72,17 @@ class NET_EXPORT_PRIVATE QuicFramerVisitorInterface { virtual void OnPublicResetPacket( const QuicPublicResetPacket& packet) = 0; + // Called only when |is_server_| is false and a version negotiation packet has + // been parsed. + virtual void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) = 0; + // Called when a lost packet has been recovered via FEC, // before it has been processed. virtual void OnRevivedPacket() = 0; - // Called when the header of a packet had been parsed. - // If OnPacketHeader returns false, parsing for this packet will cease. + // Called when the complete header of a packet had been parsed. + // If OnPacketHeader returns false, framing for this packet will cease. virtual bool OnPacketHeader(const QuicPacketHeader& header) = 0; // Called when a data packet is parsed that is part of an FEC group. @@ -138,10 +150,14 @@ class NET_EXPORT_PRIVATE QuicFramer { // Constructs a new framer that will own |decrypter| and |encrypter|. QuicFramer(QuicVersionTag quic_version, QuicDecrypter* decrypter, - QuicEncrypter* encrypter); + QuicEncrypter* encrypter, + bool is_server); virtual ~QuicFramer(); + // Returns true if |version| is a supported protocol version. + bool IsSupportedVersion(QuicVersionTag version); + // Calculates the largest observed packet to advertise in the case an Ack // Frame was truncated. last_written in this case is the iterator for the // last missing packet which fit in the outgoing ack. @@ -168,6 +184,11 @@ class NET_EXPORT_PRIVATE QuicFramer { return quic_version_; } + void set_version(QuicVersionTag version) { + DCHECK(IsSupportedVersion(version)); + quic_version_ = version; + } + // Set entropy calculator to be called from the framer when it needs the // entropy of a truncated ack frame. An entropy calculator must be set or else // the framer will likely crash. If this is called multiple times, only the @@ -206,6 +227,8 @@ class NET_EXPORT_PRIVATE QuicFramer { static size_t GetMinConnectionCloseFrameSize(); // Size in bytes of all GoAway frame fields without the reason phrase. static size_t GetMinGoAwayFrameSize(); + // Size in bytes required for a serialized version negotiation packet + size_t GetVersionNegotiationPacketSize(size_t number_versions); // Returns the number of bytes added to the packet for the specified frame, // and 0 if the frame doesn't fit. Includes the header size for the first @@ -242,6 +265,10 @@ class NET_EXPORT_PRIVATE QuicFramer { static QuicEncryptedPacket* ConstructPublicResetPacket( const QuicPublicResetPacket& packet); + QuicEncryptedPacket* ConstructVersionNegotiationPacket( + const QuicPacketPublicHeader& header, + const QuicVersionTagList& supported_versions); + // Returns a new encrypted packet, owned by the caller. QuicEncryptedPacket* EncryptPacket(QuicPacketSequenceNumber sequence_number, const QuicPacket& packet); @@ -268,6 +295,8 @@ class NET_EXPORT_PRIVATE QuicFramer { bool ProcessPublicResetPacket(const QuicPacketPublicHeader& public_header); + bool ProcessVersionNegotiationPacket(QuicPacketPublicHeader* public_header); + bool WritePacketHeader(const QuicPacketHeader& header, QuicDataWriter* writer); @@ -344,6 +373,9 @@ class NET_EXPORT_PRIVATE QuicFramer { scoped_ptr<QuicDecrypter> decrypter_; // Encrypter used to encrypt packets via EncryptPacket(). scoped_ptr<QuicEncrypter> encrypter_; + // Tracks if the framer is being used by the entity that received the + // connection or the entity that initiated it. + bool is_server_; DISALLOW_COPY_AND_ASSIGN(QuicFramer); }; diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc index eeecd08..8af9baf 100644 --- a/net/quic/quic_framer_test.cc +++ b/net/quic/quic_framer_test.cc @@ -17,6 +17,7 @@ #include "net/quic/quic_framer.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_utils.h" +#include "net/quic/test_tools/quic_framer_peer.h" #include "net/quic/test_tools/quic_test_utils.h" using base::hash_set; @@ -64,21 +65,6 @@ const size_t kPublicResetPacketNonceProofOffset = const size_t kPublicResetPacketRejectedSequenceNumberOffset = kPublicResetPacketNonceProofOffset + kPublicResetNonceSize; -class QuicFramerPeer { - public: - static QuicPacketSequenceNumber CalculatePacketSequenceNumberFromWire( - QuicFramer* framer, - QuicPacketSequenceNumber packet_sequence_number) { - return framer->CalculatePacketSequenceNumberFromWire( - packet_sequence_number); - } - static void SetLastSequenceNumber( - QuicFramer* framer, - QuicPacketSequenceNumber packet_sequence_number) { - framer->last_sequence_number_ = packet_sequence_number; - } -}; - class TestEncrypter : public QuicEncrypter { public: virtual ~TestEncrypter() {} @@ -108,6 +94,12 @@ class TestEncrypter : public QuicEncrypter { virtual size_t GetCiphertextSize(size_t plaintext_size) const OVERRIDE { return plaintext_size; } + virtual StringPiece GetKey() const { + return StringPiece(); + } + virtual StringPiece GetNoncePrefix() const { + return StringPiece(); + } QuicPacketSequenceNumber sequence_number_; string associated_data_; string plaintext_; @@ -130,6 +122,12 @@ class TestDecrypter : public QuicDecrypter { ciphertext_ = ciphertext.as_string(); return new QuicData(ciphertext.data(), ciphertext.length()); } + virtual StringPiece GetKey() const { + return StringPiece(); + } + virtual StringPiece GetNoncePrefix() const { + return StringPiece(); + } QuicPacketSequenceNumber sequence_number_; string associated_data_; string ciphertext_; @@ -167,10 +165,21 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface { public_reset_packet_.reset(new QuicPublicResetPacket(packet)); } + virtual void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) OVERRIDE { + version_negotiation_packet_.reset(new QuicVersionNegotiationPacket(packet)); + } + virtual void OnRevivedPacket() OVERRIDE { revived_packets_++; } + virtual bool OnProtocolVersionMismatch( + QuicVersionTag version) OVERRIDE { + DCHECK(false); + return true; + } + virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE { packet_count_++; header_.reset(new QuicPacketHeader(header)); @@ -231,6 +240,7 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface { scoped_ptr<QuicPacketHeader> header_; scoped_ptr<QuicPublicResetPacket> public_reset_packet_; + scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_; vector<QuicStreamFrame*> stream_frames_; vector<QuicAckFrame*> ack_frames_; vector<QuicCongestionFeedbackFrame*> congestion_feedback_frames_; @@ -246,7 +256,7 @@ class QuicFramerTest : public ::testing::Test { QuicFramerTest() : encrypter_(new test::TestEncrypter()), decrypter_(new test::TestDecrypter()), - framer_(kQuicVersion1, decrypter_, encrypter_) { + framer_(kQuicVersion1, decrypter_, encrypter_, true) { framer_.set_visitor(&visitor_); framer_.set_entropy_calculator(&entropy_calculator_); } @@ -555,7 +565,7 @@ TEST_F(QuicFramerTest, PacketHeaderWithVersionFlag) { visitor_.header_->public_header.guid); EXPECT_FALSE(visitor_.header_->public_header.reset_flag); EXPECT_TRUE(visitor_.header_->public_header.version_flag); - EXPECT_EQ(kQuicVersion1, visitor_.header_->public_header.version); + EXPECT_EQ(kQuicVersion1, visitor_.header_->public_header.versions[0]); EXPECT_FALSE(visitor_.header_->fec_flag); EXPECT_FALSE(visitor_.header_->entropy_flag); EXPECT_FALSE(visitor_.header_->fec_entropy_flag); @@ -814,7 +824,7 @@ TEST_F(QuicFramerTest, StreamFrameWithVersion) { EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); EXPECT_TRUE(visitor_.header_.get()->public_header.version_flag); - EXPECT_EQ(kQuicVersion1, visitor_.header_.get()->public_header.version); + EXPECT_EQ(kQuicVersion1, visitor_.header_.get()->public_header.versions[0]); EXPECT_TRUE(CheckDecryption(encrypted, kIncludeVersion)); ASSERT_EQ(1u, visitor_.stream_frames_.size()); @@ -1562,6 +1572,7 @@ TEST_F(QuicFramerTest, PublicResetPacket) { // Now test framing boundaries for (size_t i = 0; i < GetPublicResetPacketSize(); ++i) { string expected_error; + DLOG(INFO) << "iteration: " << i; if (i < kPublicFlagsOffset) { expected_error = "Unable to read GUID."; } else if (i < kPublicResetPacketNonceProofOffset) { @@ -1575,6 +1586,42 @@ TEST_F(QuicFramerTest, PublicResetPacket) { } } +TEST_F(QuicFramerTest, VersionNegotiationPacket) { + unsigned char packet[] = { + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // public flags (version) + 0x01, + // version tag + 'Q', '1', '.', '0', + 'Q', '2', '.', '0', + }; + + QuicFramerPeer::SetIsServer(&framer_, false); + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + ASSERT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.version_negotiation_packet_.get()); + EXPECT_EQ(2u, visitor_.version_negotiation_packet_->versions.size()); + EXPECT_EQ(kQuicVersion1, + visitor_.version_negotiation_packet_->versions[0]); + + for (size_t i = 0; i <= kQuicGuidSize + kPublicFlagsSize; ++i) { + string expected_error; + QuicErrorCode error_code = QUIC_INVALID_PACKET_HEADER; + if (i < kPublicFlagsOffset) { + expected_error = "Unable to read GUID."; + } else if (i < kVersionOffset) { + expected_error = "Unable to read public flags."; + } else { + expected_error = "Unable to read supported version in negotiation."; + error_code = QUIC_INVALID_VERSION_NEGOTIATION_PACKET; + } + CheckProcessingFails(packet, i, expected_error, error_code); + } +} + TEST_F(QuicFramerTest, FecPacket) { unsigned char packet[] = { // guid @@ -1771,6 +1818,7 @@ TEST_F(QuicFramerTest, ConstructStreamFramePacketWithVersionFlag) { 'r', 'l', 'd', '!', }; + QuicFramerPeer::SetIsServer(&framer_, false); scoped_ptr<QuicPacket> data( framer_.ConstructFrameDataPacket(header, frames).packet); ASSERT_TRUE(data != NULL); @@ -1780,6 +1828,33 @@ TEST_F(QuicFramerTest, ConstructStreamFramePacketWithVersionFlag) { AsChars(packet), arraysize(packet)); } +TEST_F(QuicFramerTest, ConstructVersionNegotiationPacket) { + QuicPacketPublicHeader header; + header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.reset_flag = false; + header.version_flag = true; + + unsigned char packet[] = { + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // public flags (version) + 0x01, + // version tag + 'Q', '1', '.', '0', + 'Q', '2', '.', '0', + }; + + const int kQuicVersion2 = MAKE_TAG('Q', '2', '.', '0'); + QuicVersionTagList versions; + versions.push_back(kQuicVersion1); + versions.push_back(kQuicVersion2); + scoped_ptr<QuicEncryptedPacket> data( + framer_.ConstructVersionNegotiationPacket(header, versions)); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} TEST_F(QuicFramerTest, ConstructAckFramePacket) { QuicPacketHeader header; diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc index e0bfee7..4f5aa1c 100644 --- a/net/quic/quic_http_stream_test.cc +++ b/net/quic/quic_http_stream_test.cc @@ -44,7 +44,7 @@ class TestQuicConnection : public QuicConnection { TestQuicConnection(QuicGuid guid, IPEndPoint address, QuicConnectionHelper* helper) - : QuicConnection(guid, address, helper) { + : QuicConnection(guid, address, helper, false) { } void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) { @@ -118,8 +118,9 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { guid_(2), framer_(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)), - creator_(guid_, &framer_, &random_) { + QuicEncrypter::Create(kNULL), + false), + creator_(guid_, &framer_, &random_, false) { IPAddressNumber ip; CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); peer_addr_ = IPEndPoint(ip, 443); @@ -168,7 +169,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { runner_ = new TestTaskRunner(&clock_); send_algorithm_ = new MockSendAlgorithm(); receive_algorithm_ = new TestReceiveAlgorithm(NULL); - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _)). + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)). WillRepeatedly(testing::Return(QuicTime::Delta::Zero())); helper_ = new QuicConnectionHelper(runner_.get(), &clock_, &random_generator_, socket); @@ -178,10 +179,12 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { connection_->SetReceiveAlgorithm(receive_algorithm_); session_.reset(new QuicClientSession(connection_, helper_, NULL, "www.google.com", NULL)); - CryptoHandshakeMessage message = - CreateShloMessage(&clock_, &random_generator_, "www.google.com"); + scoped_ptr<QuicPacket> shlo(ConstructServerHelloPacket( + guid_, &clock_, &random_generator_, "www.google.com")); + scoped_ptr<QuicEncryptedPacket> shlo_packet( + framer_.EncryptPacket(1, *shlo)); session_->GetCryptoStream()->CryptoConnect(); - session_->GetCryptoStream()->OnHandshakeMessage(message); + ProcessPacket(*shlo_packet); EXPECT_TRUE(session_->IsCryptoHandshakeComplete()); QuicReliableClientStream* stream = session_->CreateOutgoingReliableStream(); @@ -219,7 +222,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { QuicEncryptedPacket* ConstructChloPacket() { scoped_ptr<QuicPacket> chlo( ConstructClientHelloPacket(guid_, &clock_, &random_generator_, - "www.google.com")); + "www.google.com", true)); return framer_.EncryptPacket(1, *chlo); } @@ -241,7 +244,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { QuicPacketSequenceNumber least_unacked) { InitializeHeader(sequence_number); - QuicAckFrame ack(largest_received, least_unacked); + QuicAckFrame ack(largest_received, QuicTime::Zero(), least_unacked); ack.sent_info.entropy_hash = 0; ack.received_info.entropy_hash = 0; @@ -340,7 +343,7 @@ TEST_F(QuicHttpStreamTest, GetRequest) { AddWrite(SYNCHRONOUS, ConstructChloPacket()); AddWrite(SYNCHRONOUS, ConstructDataPacket(2, kFin, 0, request_data_)); - AddWrite(SYNCHRONOUS, ConstructAckPacket(3, 2, 2)); + AddWrite(SYNCHRONOUS, ConstructAckPacket(3, 2, 1)); Initialize(); request_.method = "GET"; @@ -383,7 +386,7 @@ TEST_F(QuicHttpStreamTest, GetRequestFullResponseInSinglePacket) { SetRequestString("GET", "/"); AddWrite(SYNCHRONOUS, ConstructChloPacket()); AddWrite(SYNCHRONOUS, ConstructDataPacket(2, kFin, 0, request_data_)); - AddWrite(SYNCHRONOUS, ConstructAckPacket(3, 2, 2)); + AddWrite(SYNCHRONOUS, ConstructAckPacket(3, 2, 1)); Initialize(); request_.method = "GET"; @@ -429,7 +432,7 @@ TEST_F(QuicHttpStreamTest, SendPostRequest) { AddWrite(SYNCHRONOUS, ConstructDataPacket(2, !kFin, 0, request_data_)); AddWrite(SYNCHRONOUS, ConstructDataPacket(3, kFin, request_data_.length(), kUploadData)); - AddWrite(SYNCHRONOUS, ConstructAckPacket(4, 2, 3)); + AddWrite(SYNCHRONOUS, ConstructAckPacket(4, 2, 1)); Initialize(); @@ -489,7 +492,7 @@ TEST_F(QuicHttpStreamTest, SendChunkedPostRequest) { AddWrite(SYNCHRONOUS, ConstructDataPacket(4, kFin, request_data_.length() + chunk_size, kUploadData)); - AddWrite(SYNCHRONOUS, ConstructAckPacket(5, 2, 3)); + AddWrite(SYNCHRONOUS, ConstructAckPacket(5, 2, 1)); Initialize(); @@ -545,7 +548,7 @@ TEST_F(QuicHttpStreamTest, DestroyedEarly) { AddWrite(SYNCHRONOUS, ConstructChloPacket()); AddWrite(SYNCHRONOUS, ConstructDataPacket(2, kFin, 0, request_data_)); AddWrite(SYNCHRONOUS, ConstructRstPacket(3, 3)); - AddWrite(SYNCHRONOUS, ConstructAckPacket(4, 2, 2)); + AddWrite(SYNCHRONOUS, ConstructAckPacket(4, 2, 1)); use_closing_stream_ = true; Initialize(); diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc index d184f29..10c1ad9 100644 --- a/net/quic/quic_network_transaction_unittest.cc +++ b/net/quic/quic_network_transaction_unittest.cc @@ -88,10 +88,12 @@ class QuicNetworkTransactionTest : public PlatformTest { scoped_ptr<QuicPacket> chlo(ConstructClientHelloPacket(0xDEADBEEF, clock_, &random_generator_, - host)); + host, + true)); QuicFramer framer(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)); + QuicEncrypter::Create(kNULL), + false); return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(1, *chlo)); } @@ -103,7 +105,8 @@ class QuicNetworkTransactionTest : public PlatformTest { host)); QuicFramer framer(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)); + QuicEncrypter::Create(kNULL), + false); return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(1, *shlo)); } @@ -138,7 +141,7 @@ class QuicNetworkTransactionTest : public PlatformTest { header.fec_entropy_flag = false; header.fec_group = 0; - QuicAckFrame ack(largest_received, least_unacked); + QuicAckFrame ack(largest_received, QuicTime::Zero(), least_unacked); QuicCongestionFeedbackFrame feedback; feedback.type = kTCP; @@ -147,7 +150,8 @@ class QuicNetworkTransactionTest : public PlatformTest { QuicFramer framer(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)); + QuicEncrypter::Create(kNULL), + false); QuicFrames frames; frames.push_back(QuicFrame(&ack)); frames.push_back(QuicFrame(&feedback)); @@ -201,7 +205,8 @@ class QuicNetworkTransactionTest : public PlatformTest { const QuicFrame& frame) { QuicFramer framer(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)); + QuicEncrypter::Create(kNULL), + false); QuicFrames frames; frames.push_back(frame); scoped_ptr<QuicPacket> packet( diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc index ea1e90a..05a6893 100644 --- a/net/quic/quic_packet_creator.cc +++ b/net/quic/quic_packet_creator.cc @@ -9,7 +9,6 @@ #include "net/quic/quic_fec_group.h" #include "net/quic/quic_utils.h" - using base::StringPiece; using std::make_pair; using std::min; @@ -20,14 +19,16 @@ namespace net { QuicPacketCreator::QuicPacketCreator(QuicGuid guid, QuicFramer* framer, - QuicRandom* random_generator) + QuicRandom* random_generator, + bool is_server) : guid_(guid), framer_(framer), random_generator_(random_generator), sequence_number_(0), fec_group_number_(0), - // TODO(satyashekhar): Fix this when versioning is implemented. - packet_size_(GetPacketHeaderSize(!kIncludeVersion)) { + is_server_(is_server), + send_version_in_packet_(!is_server), + packet_size_(GetPacketHeaderSize(send_version_in_packet_)) { framer_->set_fec_builder(this); } @@ -55,6 +56,18 @@ void QuicPacketCreator::MaybeStartFEC() { } } +// Stops serializing version of the protocol in packets sent after this call. +// A packet that is already open might send kQuicVersionSize bytes less than the +// maximum packet size if we stop sending version before it is serialized. +void QuicPacketCreator::StopSendingVersion() { + DCHECK(send_version_in_packet_); + send_version_in_packet_ = false; + if (packet_size_ > 0) { + DCHECK_LT(kQuicVersionSize, packet_size_); + packet_size_ -= kQuicVersionSize; + } +} + bool QuicPacketCreator::HasRoomForStreamFrame() const { return BytesFree() > QuicFramer::GetMinStreamFrameSize(); } @@ -134,8 +147,7 @@ SerializedPacket QuicPacketCreator::SerializePacket() { SerializedPacket serialized = framer_->ConstructFrameDataPacket( header, queued_frames_, packet_size_); queued_frames_.clear(); - // TODO(satyamshekhar) Fix this versioning is implemented. - packet_size_ = GetPacketHeaderSize(false); + packet_size_ = GetPacketHeaderSize(send_version_in_packet_); serialized.retransmittable_frames = queued_retransmittable_frames_.release(); return serialized; } @@ -164,13 +176,28 @@ SerializedPacket QuicPacketCreator::SerializeConnectionClose( return SerializeAllFrames(frames); } +QuicEncryptedPacket* QuicPacketCreator::SerializeVersionNegotiationPacket( + const QuicVersionTagList& supported_versions) { + DCHECK(!is_server_); + QuicPacketPublicHeader header; + header.guid = guid_; + header.reset_flag = false; + header.version_flag = true; + header.versions = supported_versions; + QuicEncryptedPacket* encrypted = + framer_->ConstructVersionNegotiationPacket(header, supported_versions); + DCHECK(encrypted); + DCHECK_GE(options_.max_packet_length, encrypted->length()); + return encrypted; +} + void QuicPacketCreator::FillPacketHeader(QuicFecGroupNumber fec_group, bool fec_flag, bool fec_entropy_flag, QuicPacketHeader* header) { header->public_header.guid = guid_; header->public_header.reset_flag = false; - header->public_header.version_flag = false; + header->public_header.version_flag = send_version_in_packet_; header->fec_flag = fec_flag; header->fec_entropy_flag = fec_entropy_flag; header->packet_sequence_number = ++sequence_number_; diff --git a/net/quic/quic_packet_creator.h b/net/quic/quic_packet_creator.h index e3fdc2b..60f9fb4 100644 --- a/net/quic/quic_packet_creator.h +++ b/net/quic/quic_packet_creator.h @@ -19,6 +19,9 @@ #include "net/quic/quic_protocol.h" namespace net { +namespace test { +class QuicPacketCreatorPeer; +} class QuicRandom; @@ -41,7 +44,8 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { // QuicRandom* required for packet entropy. QuicPacketCreator(QuicGuid guid, QuicFramer* framer, - QuicRandom* random_generator); + QuicRandom* random_generator, + bool is_server); virtual ~QuicPacketCreator(); @@ -57,6 +61,9 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { // and there is not already an FEC group open. void MaybeStartFEC(); + // Makes the framer not serialize the protocol version in sent packets. + void StopSendingVersion(); + // The overhead the framing will add for a packet with num_frames frames. static size_t StreamFramePacketOverhead(int num_frames, bool include_version); @@ -108,6 +115,13 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { SerializedPacket SerializeConnectionClose( QuicConnectionCloseFrame* close_frame); + // Creates a version negotiation packet which supports |supported_versions|. + // Caller owns the created packet. Also, sets the entropy hash of the + // serialized packet to a random bool and returns that value as a member of + // SerializedPacket. + QuicEncryptedPacket* SerializeVersionNegotiationPacket( + const QuicVersionTagList& supported_versions); + QuicPacketSequenceNumber sequence_number() const { return sequence_number_; } @@ -121,6 +135,8 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { } private: + friend class test::QuicPacketCreatorPeer; + static bool ShouldRetransmit(const QuicFrame& frame); void FillPacketHeader(QuicFecGroupNumber fec_group, @@ -139,6 +155,11 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { QuicPacketSequenceNumber sequence_number_; QuicFecGroupNumber fec_group_number_; scoped_ptr<QuicFecGroup> fec_group_; + // bool to keep track if this packet creator is being used the server. + bool is_server_; + // Controls whether protocol version should be included while serializing the + // packet. + bool send_version_in_packet_; size_t packet_size_; QuicFrames queued_frames_; scoped_ptr<RetransmittableFrames> queued_retransmittable_frames_; diff --git a/net/quic/quic_packet_creator_test.cc b/net/quic/quic_packet_creator_test.cc index 752da91..423028a 100644 --- a/net/quic/quic_packet_creator_test.cc +++ b/net/quic/quic_packet_creator_test.cc @@ -10,39 +10,49 @@ #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_utils.h" +#include "net/quic/test_tools/quic_packet_creator_peer.h" #include "net/quic/test_tools/quic_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" using base::StringPiece; using std::string; using std::vector; +using testing::DoAll; using testing::InSequence; +using testing::Return; +using testing::SaveArg; using testing::_; namespace net { namespace test { namespace { -class QuicPacketCreatorTest : public ::testing::Test { +class QuicPacketCreatorTest : public ::testing::TestWithParam<bool> { protected: QuicPacketCreatorTest() - : framer_(kQuicVersion1, - QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)), + : server_framer_(kQuicVersion1, + QuicDecrypter::Create(kNULL), + QuicEncrypter::Create(kNULL), + true), + client_framer_(kQuicVersion1, + QuicDecrypter::Create(kNULL), + QuicEncrypter::Create(kNULL), + false), id_(1), sequence_number_(0), guid_(2), data_("foo"), - creator_(guid_, &framer_, QuicRandom::GetInstance()) { - framer_.set_visitor(&framer_visitor_); + creator_(guid_, &client_framer_, QuicRandom::GetInstance(), false) { + client_framer_.set_visitor(&framer_visitor_); + server_framer_.set_visitor(&framer_visitor_); } ~QuicPacketCreatorTest() { } void ProcessPacket(QuicPacket* packet) { scoped_ptr<QuicEncryptedPacket> encrypted( - framer_.EncryptPacket(sequence_number_, *packet)); - framer_.ProcessPacket(*encrypted); + server_framer_.EncryptPacket(sequence_number_, *packet)); + server_framer_.ProcessPacket(*encrypted); } void CheckStreamFrame(const QuicFrame& frame, QuicStreamId stream_id, @@ -56,7 +66,8 @@ class QuicPacketCreatorTest : public ::testing::Test { } QuicFrames frames_; - QuicFramer framer_; + QuicFramer server_framer_; + QuicFramer client_framer_; testing::StrictMock<MockFramerVisitor> framer_visitor_; QuicStreamId id_; QuicPacketSequenceNumber sequence_number_; @@ -65,25 +76,8 @@ class QuicPacketCreatorTest : public ::testing::Test { QuicPacketCreator creator_; }; -TEST_F(QuicPacketCreatorTest, SerializeFrame) { - frames_.push_back(QuicFrame(new QuicStreamFrame( - 0u, false, 0u, StringPiece("")))); - SerializedPacket serialized = creator_.SerializeAllFrames(frames_); - delete frames_[0].stream_frame; - - { - InSequence s; - EXPECT_CALL(framer_visitor_, OnPacket()); - EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); - EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); - EXPECT_CALL(framer_visitor_, OnPacketComplete()); - } - ProcessPacket(serialized.packet); - delete serialized.packet; -} - TEST_F(QuicPacketCreatorTest, SerializeFrames) { - frames_.push_back(QuicFrame(new QuicAckFrame(0u, 0u))); + frames_.push_back(QuicFrame(new QuicAckFrame(0u, QuicTime::Zero(), 0u))); frames_.push_back(QuicFrame(new QuicStreamFrame( 0u, false, 0u, StringPiece("")))); frames_.push_back(QuicFrame(new QuicStreamFrame( @@ -147,7 +141,7 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFEC) { TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) { QuicConnectionCloseFrame frame; frame.error_code = QUIC_NO_ERROR; - frame.ack_frame = QuicAckFrame(0u, 0u); + frame.ack_frame = QuicAckFrame(0u, QuicTime::Zero(), 0u); SerializedPacket serialized = creator_.SerializeConnectionClose(&frame); ASSERT_EQ(1u, serialized.sequence_number); @@ -188,11 +182,54 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrameFinOnly) { delete frame.stream_frame; } -TEST_F(QuicPacketCreatorTest, CreateStreamFrameTooLarge) { +TEST_F(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) { + QuicVersionTagList versions; + versions.push_back(kQuicVersion1); + scoped_ptr<QuicEncryptedPacket> encrypted( + creator_.SerializeVersionNegotiationPacket(versions)); + + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnVersionNegotiationPacket(_)); + } + client_framer_.ProcessPacket(*encrypted.get()); +} + +INSTANTIATE_TEST_CASE_P(ToggleVersionSerialization, + QuicPacketCreatorTest, + ::testing::Values(false, true)); + +TEST_P(QuicPacketCreatorTest, SerializeFrame) { + if (!GetParam()) { + creator_.StopSendingVersion(); + } + frames_.push_back(QuicFrame(new QuicStreamFrame( + 0u, false, 0u, StringPiece("")))); + SerializedPacket serialized = creator_.SerializeAllFrames(frames_); + delete frames_[0].stream_frame; + + QuicPacketHeader header; + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)).WillOnce( + DoAll(SaveArg<0>(&header), Return(true))); + EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + ProcessPacket(serialized.packet); + EXPECT_EQ(GetParam(), header.public_header.version_flag); + delete serialized.packet; +} + +TEST_P(QuicPacketCreatorTest, CreateStreamFrameTooLarge) { + if (!GetParam()) { + creator_.StopSendingVersion(); + } // A string larger than fits into a frame. - size_t ciphertext_size = NullEncrypter().GetCiphertextSize(1); - creator_.options()->max_packet_length = ciphertext_size + - QuicPacketCreator::StreamFramePacketOverhead(1, !kIncludeVersion); + creator_.options()->max_packet_length = GetPacketLengthForOneStream( + QuicPacketCreatorPeer::SendVersionInPacket(&creator_), 1); QuicFrame frame; size_t consumed = creator_.CreateStreamFrame(1u, "test", 0u, true, &frame); EXPECT_EQ(1u, consumed); @@ -200,11 +237,16 @@ TEST_F(QuicPacketCreatorTest, CreateStreamFrameTooLarge) { delete frame.stream_frame; } -TEST_F(QuicPacketCreatorTest, AddFrameAndSerialize) { +TEST_P(QuicPacketCreatorTest, AddFrameAndSerialize) { + if (!GetParam()) { + creator_.StopSendingVersion(); + } const size_t max_plaintext_size = - framer_.GetMaxPlaintextSize(creator_.options()->max_packet_length); + client_framer_.GetMaxPlaintextSize(creator_.options()->max_packet_length); EXPECT_FALSE(creator_.HasPendingFrames()); - EXPECT_EQ(max_plaintext_size - GetPacketHeaderSize(!kIncludeVersion), + EXPECT_EQ(max_plaintext_size - + GetPacketHeaderSize( + QuicPacketCreatorPeer::SendVersionInPacket(&creator_)), creator_.BytesFree()); // Add a variety of frame types and then a padding frame. @@ -243,7 +285,9 @@ TEST_F(QuicPacketCreatorTest, AddFrameAndSerialize) { delete serialized.retransmittable_frames; EXPECT_FALSE(creator_.HasPendingFrames()); - EXPECT_EQ(max_plaintext_size - GetPacketHeaderSize(!kIncludeVersion), + EXPECT_EQ(max_plaintext_size - + GetPacketHeaderSize( + QuicPacketCreatorPeer::SendVersionInPacket(&creator_)), creator_.BytesFree()); } diff --git a/net/quic/quic_packet_generator.cc b/net/quic/quic_packet_generator.cc index d3de9b55..87464f7 100644 --- a/net/quic/quic_packet_generator.cc +++ b/net/quic/quic_packet_generator.cc @@ -72,8 +72,9 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id, size_t total_bytes_consumed = 0; bool fin_consumed = false; + bool has_retransmittable_data = true; - while (delegate_->CanWrite(false)) { + while (delegate_->CanWrite(false, has_retransmittable_data)) { // TODO(rch) figure out FEC. // packet_creator_.MaybeStartFEC(); QuicFrame frame; @@ -113,7 +114,8 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id, } void QuicPacketGenerator::SendQueuedData() { - while (HasPendingData() && delegate_->CanWrite(false)) { + while (HasPendingData() && + delegate_->CanWrite(false, packet_creator_->HasPendingFrames())) { if (!AddNextPendingFrame()) { // Packet was full, so serialize and send it. SerializeAndSendPacket(); diff --git a/net/quic/quic_packet_generator.h b/net/quic/quic_packet_generator.h index 254a4fe..99f6098 100644 --- a/net/quic/quic_packet_generator.h +++ b/net/quic/quic_packet_generator.h @@ -62,7 +62,8 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator { class NET_EXPORT_PRIVATE DelegateInterface { public: virtual ~DelegateInterface() {} - virtual bool CanWrite(bool is_retransmission) = 0; + virtual bool CanWrite(bool is_retransmission, + bool has_retransmittable_data) = 0; virtual QuicAckFrame* CreateAckFrame() = 0; virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() = 0; // Takes ownership of |packet.packet| and |packet.retransmittable_frames|. diff --git a/net/quic/quic_packet_generator_test.cc b/net/quic/quic_packet_generator_test.cc index 3b97472..5ed1df5 100644 --- a/net/quic/quic_packet_generator_test.cc +++ b/net/quic/quic_packet_generator_test.cc @@ -31,14 +31,15 @@ class MockDelegate : public QuicPacketGenerator::DelegateInterface { MockDelegate() {} virtual ~MockDelegate() {} - MOCK_METHOD1(CanWrite, bool(bool is_retransmission)); + MOCK_METHOD2(CanWrite, bool(bool is_retransmission, + bool has_retransmittable_data)); MOCK_METHOD0(CreateAckFrame, QuicAckFrame*()); MOCK_METHOD0(CreateFeedbackFrame, QuicCongestionFeedbackFrame*()); MOCK_METHOD1(OnSerializedPacket, bool(const SerializedPacket& packet)); void SetCanWrite(bool can_write) { - EXPECT_CALL(*this, CanWrite(false)).WillRepeatedly(Return(can_write)); + EXPECT_CALL(*this, CanWrite(false, _)).WillRepeatedly(Return(can_write)); } private: @@ -76,8 +77,9 @@ class QuicPacketGeneratorTest : public ::testing::Test { QuicPacketGeneratorTest() : framer_(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)), - creator_(42, &framer_, &random_), + QuicEncrypter::Create(kNULL), + false), + creator_(42, &framer_, &random_, false), generator_(&delegate_, &creator_), packet_(0, NULL, 0, NULL), packet2_(0, NULL, 0, NULL), @@ -101,7 +103,7 @@ class QuicPacketGeneratorTest : public ::testing::Test { QuicAckFrame* CreateAckFrame() { // TODO(rch): Initialize this so it can be verified later. - return new QuicAckFrame(0, 0); + return new QuicAckFrame(0, QuicTime::Zero(), 0); } QuicCongestionFeedbackFrame* CreateFeedbackFrame() { diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc index 0a7239a..6fca452 100644 --- a/net/quic/quic_protocol.cc +++ b/net/quic/quic_protocol.cc @@ -33,6 +33,29 @@ size_t GetStartOfEncryptedData(bool include_version) { kFecGroupSize; } +QuicPacketPublicHeader::QuicPacketPublicHeader() {} + +QuicPacketPublicHeader::QuicPacketPublicHeader( + const QuicPacketPublicHeader& other) + : guid(other.guid), + reset_flag(other.reset_flag), + version_flag(other.version_flag), + versions(other.versions) { +} + +QuicPacketPublicHeader::~QuicPacketPublicHeader() {} + +QuicPacketPublicHeader& QuicPacketPublicHeader::operator=( + const QuicPacketPublicHeader& other) { + guid = other.guid; + reset_flag = other.reset_flag; + version_flag = other.version_flag; + // Window's STL crashes when empty std::vectors are copied. + if (other.versions.size() > 0) + versions = other.versions; + return *this; +} + QuicStreamFrame::QuicStreamFrame() {} QuicStreamFrame::QuicStreamFrame(QuicStreamId stream_id, @@ -48,8 +71,14 @@ QuicStreamFrame::QuicStreamFrame(QuicStreamId stream_id, ostream& operator<<(ostream& os, const QuicPacketHeader& header) { os << "{ guid: " << header.public_header.guid << ", reset_flag: " << header.public_header.reset_flag - << ", version_flag: " << header.public_header.version_flag - << ", fec_flag: " << header.fec_flag + << ", version_flag: " << header.public_header.version_flag; + if (header.public_header.version_flag) { + os << " version: "; + for (size_t i = 0; i < header.public_header.versions.size(); ++i) { + os << header.public_header.versions[0] << " "; + } + } + os << ", fec_flag: " << header.fec_flag << ", entropy_flag: " << header.entropy_flag << ", entropy hash: " << static_cast<int>(header.entropy_hash) << ", sequence_number: " << header.packet_sequence_number @@ -59,7 +88,8 @@ ostream& operator<<(ostream& os, const QuicPacketHeader& header) { // TODO(ianswett): Initializing largest_observed to 0 should not be necessary. ReceivedPacketInfo::ReceivedPacketInfo() - : largest_observed(0) { + : largest_observed(0), + delta_time_largest_observed(QuicTime::Delta::Infinite()) { } ReceivedPacketInfo::~ReceivedPacketInfo() {} @@ -84,6 +114,7 @@ SentPacketInfo::~SentPacketInfo() {} // Testing convenience method. QuicAckFrame::QuicAckFrame(QuicPacketSequenceNumber largest_observed, + QuicTime largest_observed_receive_time, QuicPacketSequenceNumber least_unacked) { received_info.largest_observed = largest_observed; received_info.entropy_hash = 0; diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index 9a1e128..2a297ba 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -37,6 +37,7 @@ typedef QuicPacketSequenceNumber QuicFecGroupNumber; typedef uint64 QuicPublicResetNonceProof; typedef uint8 QuicPacketEntropyHash; typedef uint32 QuicVersionTag; +typedef std::vector<QuicVersionTag> QuicVersionTagList; // TODO(rch): Consider Quic specific names for these constants. // Maximum size in bytes of a QUIC packet. @@ -72,6 +73,8 @@ NET_EXPORT_PRIVATE size_t GetPublicResetPacketSize(); NET_EXPORT_PRIVATE size_t GetStartOfFecProtectedData(bool include_version); // Index of the first byte in a QUIC packet of encrypted data. NET_EXPORT_PRIVATE size_t GetStartOfEncryptedData(bool include_version); +// Returns true if |version| is a supported protocol version. +NET_EXPORT_PRIVATE bool IsSupportedVersion(QuicVersionTag version); // Index of the first byte in a QUIC packet which is used in hash calculation. const size_t kStartOfHashData = 0; @@ -118,6 +121,9 @@ enum QuicErrorCode { // Stream errors. QUIC_NO_ERROR = 0, + // Connection has reached an invalid state. + QUIC_INTERNAL_ERROR, + // There were data frames after the a fin or reset. QUIC_STREAM_DATA_AFTER_TERMINATION, // There was some server error which halted stream processing. @@ -143,6 +149,8 @@ enum QuicErrorCode { QUIC_INVALID_GOAWAY_DATA, // Ack data is malformed. QUIC_INVALID_ACK_DATA, + // Version negotiation packet is malformed. + QUIC_INVALID_VERSION_NEGOTIATION_PACKET, // There was an error decrypting. QUIC_DECRYPTION_FAILURE, // There was an error encrypting. @@ -159,6 +167,8 @@ enum QuicErrorCode { QUIC_TOO_MANY_OPEN_STREAMS, // Received public reset for this connection. QUIC_PUBLIC_RESET, + // Invalid protocol version + QUIC_INVALID_VERSION, // We hit our prenegotiated (or default) timeout QUIC_CONNECTION_TIMED_OUT, @@ -195,14 +205,21 @@ enum QuicErrorCode { // to reverse the order of the bytes. #define MAKE_TAG(a, b, c, d) ((d << 24) + (c << 16) + (b << 8) + a) +const QuicVersionTag kUnsupportedVersion = -1; const QuicVersionTag kQuicVersion1 = MAKE_TAG('Q', '1', '.', '0'); struct NET_EXPORT_PRIVATE QuicPacketPublicHeader { + QuicPacketPublicHeader(); + explicit QuicPacketPublicHeader(const QuicPacketPublicHeader& other); + ~QuicPacketPublicHeader(); + + QuicPacketPublicHeader& operator=(const QuicPacketPublicHeader& other); + // Universal header. All QuicPacket headers will have a guid and public flags. QuicGuid guid; bool reset_flag; bool version_flag; - QuicVersionTag version; + QuicVersionTagList versions; }; // Header for Data or FEC packets. @@ -232,6 +249,14 @@ struct QuicPublicResetPacket { QuicPublicResetNonceProof nonce_proof; }; +enum QuicVersionNegotiationState { + START_NEGOTIATION = 0, + SENT_NEGOTIATION_PACKET, + NEGOTIATED_VERSION +}; + +typedef QuicPacketPublicHeader QuicVersionNegotiationPacket; + // A padding frame contains no payload. struct NET_EXPORT_PRIVATE QuicPaddingFrame { }; @@ -275,6 +300,10 @@ struct NET_EXPORT_PRIVATE ReceivedPacketInfo { // list. QuicPacketSequenceNumber largest_observed; + // Time elapsed since largest_observed was received until this Ack frame was + // sent. + QuicTime::Delta delta_time_largest_observed; + // TODO(satyamshekhar): Can be optimized using an interval set like data // structure. // The set of packets which we're expecting and have not received. @@ -312,6 +341,7 @@ struct NET_EXPORT_PRIVATE QuicAckFrame { // Testing convenience method to construct a QuicAckFrame with all packets // from least_unacked to largest_observed acked. QuicAckFrame(QuicPacketSequenceNumber largest_observed, + QuicTime largest_observed_receive_time, QuicPacketSequenceNumber least_unacked); NET_EXPORT_PRIVATE friend std::ostream& operator<<( diff --git a/net/quic/quic_reliable_client_stream_test.cc b/net/quic/quic_reliable_client_stream_test.cc index 7990f16..3da022b 100644 --- a/net/quic/quic_reliable_client_stream_test.cc +++ b/net/quic/quic_reliable_client_stream_test.cc @@ -34,7 +34,7 @@ class MockDelegate : public QuicReliableClientStream::Delegate { class QuicReliableClientStreamTest : public ::testing::Test { public: QuicReliableClientStreamTest() - : session_(new MockConnection(1, IPEndPoint()), false), + : session_(new MockConnection(1, IPEndPoint(), false), false), stream_(1, &session_, BoundNetLog()) { stream_.SetDelegate(&delegate_); } diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc index b97a021..a629f20 100644 --- a/net/quic/quic_session_test.cc +++ b/net/quic/quic_session_test.cc @@ -8,6 +8,7 @@ #include <set> #include "base/hash_tables.h" +#include "net/quic/crypto/crypto_handshake.h" #include "net/quic/test_tools/quic_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -87,7 +88,7 @@ class QuicSessionTest : public ::testing::Test { protected: QuicSessionTest() : guid_(1), - connection_(new MockConnection(guid_, IPEndPoint())), + connection_(new MockConnection(guid_, IPEndPoint(), false)), session_(connection_, true) { } diff --git a/net/quic/quic_stats.cc b/net/quic/quic_stats.cc new file mode 100644 index 0000000..162e093 --- /dev/null +++ b/net/quic/quic_stats.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_stats.h" + +namespace net { + +QuicConnectionStats::QuicConnectionStats() { + bytes_sent = 0; + packets_sent = 0; + + bytes_received = 0; + packets_received = 0; + + bytes_retransmitted = 0; + packets_retransmitted = 0; + + packets_revived = 0; + packets_dropped = 0; + rto_count = 0; + + rtt = 0; + estimated_bandwidth = 0; +} + +QuicConnectionStats::~QuicConnectionStats() {} + +} // namespace net diff --git a/net/quic/quic_stats.h b/net/quic/quic_stats.h new file mode 100644 index 0000000..252791e --- /dev/null +++ b/net/quic/quic_stats.h @@ -0,0 +1,50 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_QUIC_STATS_H_ +#define NET_QUIC_QUIC_STATS_H_ + +#include "base/basictypes.h" +#include "net/base/net_export.h" + +namespace net { +// TODO(satyamshekhar): Add more interesting stats: +// 1. (C/S)HLO retransmission count. +// 2. SHLO received to first stream packet processed time. +// 3. CHLO sent to SHLO received time. +// 4. Number of migrations. +// 5. Number of out of order packets. +// 6. Avg packet size. +// 7. Number of connections that require more that 1-RTT. +// 8. Avg number of streams / session. +// 9. Number of duplicates received. +// 10. Fraction of traffic sent/received that was not data (protocol overhead). +// 11. Fraction of data transferred that was padding. + +// Structure to hold stats for a QuicConnection. +struct NET_EXPORT_PRIVATE QuicConnectionStats { + QuicConnectionStats(); + ~QuicConnectionStats(); + + uint64 bytes_sent; // includes retransmissions, fec. + uint32 packets_sent; + + uint64 bytes_received; // includes duplicate data for a stream, fec. + uint32 packets_received; // includes dropped packets + + uint64 bytes_retransmitted; + uint32 packets_retransmitted; + + uint32 packets_revived; + uint32 packets_dropped; // duplicate or less than least unacked. + uint32 rto_count; + + uint32 rtt; + uint64 estimated_bandwidth; + // TODO(satyamshekhar): Add window_size, mss and mtu. +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_STATS_H_ diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc index 9f9d96a..c5a5026 100644 --- a/net/quic/quic_stream_factory.cc +++ b/net/quic/quic_stream_factory.cc @@ -373,7 +373,7 @@ QuicClientSession* QuicStreamFactory::CreateSession( MessageLoop::current()->message_loop_proxy(), clock_.get(), random_generator_, socket); - QuicConnection* connection = new QuicConnection(guid, addr, helper); + QuicConnection* connection = new QuicConnection(guid, addr, helper, false); QuicClientSession* session = new QuicClientSession(connection, helper, this, host, net_log.net_log()); all_sessions_.insert(session); // owning pointer diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc index 5ddae7c..34b06b8 100644 --- a/net/quic/quic_stream_factory_test.cc +++ b/net/quic/quic_stream_factory_test.cc @@ -37,10 +37,12 @@ class QuicStreamFactoryTest : public ::testing::Test { scoped_ptr<QuicPacket> chlo(ConstructClientHelloPacket(0xDEADBEEF, clock_, &random_generator_, - host)); + host, + true)); QuicFramer framer(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)); + QuicEncrypter::Create(kNULL), + false); return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(1, *chlo)); } @@ -52,7 +54,8 @@ class QuicStreamFactoryTest : public ::testing::Test { host)); QuicFramer framer(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)); + QuicEncrypter::Create(kNULL), + false); return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(1, *shlo)); } @@ -87,7 +90,7 @@ class QuicStreamFactoryTest : public ::testing::Test { header.fec_flag = false; header.fec_group = 0; - QuicAckFrame ack(largest_received, least_unacked); + QuicAckFrame ack(largest_received, QuicTime::Zero(), least_unacked); QuicCongestionFeedbackFrame feedback; feedback.type = kTCP; feedback.tcp.accumulated_number_of_lost_packets = 0; @@ -95,7 +98,8 @@ class QuicStreamFactoryTest : public ::testing::Test { QuicFramer framer(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)); + QuicEncrypter::Create(kNULL), + false); QuicFrames frames; frames.push_back(QuicFrame(&ack)); frames.push_back(QuicFrame(&feedback)); @@ -132,7 +136,8 @@ class QuicStreamFactoryTest : public ::testing::Test { const QuicFrame& frame) { QuicFramer framer(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)); + QuicEncrypter::Create(kNULL), + false); QuicFrames frames; frames.push_back(frame); scoped_ptr<QuicPacket> packet( diff --git a/net/quic/quic_time.cc b/net/quic/quic_time.cc index a2c6901..dac6231 100644 --- a/net/quic/quic_time.cc +++ b/net/quic/quic_time.cc @@ -121,4 +121,4 @@ QuicTime::Delta QuicTime::Subtract(const QuicTime& other) const { return QuicTime::Delta(ticks_ - other.ticks_); } -} // namespace gfe_quic +} // namespace net diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc index bf6f61b..0f6750b 100644 --- a/net/quic/quic_utils.cc +++ b/net/quic/quic_utils.cc @@ -39,6 +39,7 @@ return #x; const char* QuicUtils::ErrorToString(QuicErrorCode error) { switch (error) { RETURN_STRING_LITERAL(QUIC_NO_ERROR); + RETURN_STRING_LITERAL(QUIC_INTERNAL_ERROR); RETURN_STRING_LITERAL(QUIC_STREAM_DATA_AFTER_TERMINATION); RETURN_STRING_LITERAL(QUIC_SERVER_ERROR_PROCESSING_STREAM); RETURN_STRING_LITERAL(QUIC_MULTIPLE_TERMINATION_OFFSETS); @@ -50,6 +51,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_INVALID_CONNECTION_CLOSE_DATA); RETURN_STRING_LITERAL(QUIC_INVALID_GOAWAY_DATA); RETURN_STRING_LITERAL(QUIC_INVALID_ACK_DATA); + RETURN_STRING_LITERAL(QUIC_INVALID_VERSION_NEGOTIATION_PACKET); RETURN_STRING_LITERAL(QUIC_DECRYPTION_FAILURE); RETURN_STRING_LITERAL(QUIC_ENCRYPTION_FAILURE); RETURN_STRING_LITERAL(QUIC_PACKET_TOO_LARGE); @@ -67,6 +69,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_ID); RETURN_STRING_LITERAL(QUIC_TOO_MANY_OPEN_STREAMS); RETURN_STRING_LITERAL(QUIC_PUBLIC_RESET); + RETURN_STRING_LITERAL(QUIC_INVALID_VERSION); RETURN_STRING_LITERAL(QUIC_CONNECTION_TIMED_OUT); // Intentionally have no default case, so we'll break the build // if we add errors and don't put them here. diff --git a/net/quic/reliable_quic_stream_test.cc b/net/quic/reliable_quic_stream_test.cc index 0800dc8..c941e44 100644 --- a/net/quic/reliable_quic_stream_test.cc +++ b/net/quic/reliable_quic_stream_test.cc @@ -37,7 +37,7 @@ class QuicReliableTestStream : public ReliableQuicStream { class ReliableQuicStreamTest : public ::testing::TestWithParam<bool> { public: ReliableQuicStreamTest() - : connection_(new MockConnection(1, IPEndPoint())), + : connection_(new MockConnection(1, IPEndPoint(), false)), session_(connection_, true), stream_(1, &session_) { } diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc index a9c5e0d..71f3234 100644 --- a/net/quic/test_tools/crypto_test_utils.cc +++ b/net/quic/test_tools/crypto_test_utils.cc @@ -4,12 +4,17 @@ #include "net/quic/test_tools/crypto_test_utils.h" +#include "base/string_piece.h" +#include "net/quic/crypto/quic_decrypter.h" +#include "net/quic/crypto/quic_encrypter.h" #include "net/quic/quic_crypto_client_stream.h" #include "net/quic/quic_crypto_server_stream.h" #include "net/quic/quic_crypto_stream.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/quic/test_tools/simple_quic_framer.h" +using base::StringPiece; + namespace net { namespace test { @@ -61,13 +66,13 @@ void CommunicateHandshakeMessages( // static void CryptoTestUtils::HandshakeWithFakeServer( PacketSavingConnection* client_conn, - QuicCryptoStream* client) { + QuicCryptoClientStream* client) { QuicGuid guid(1); IPAddressNumber ip; CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); IPEndPoint addr = IPEndPoint(ip, 1); PacketSavingConnection* server_conn = - new PacketSavingConnection(guid, addr); + new PacketSavingConnection(guid, addr, true); TestSession server_session(server_conn, true); QuicCryptoServerStream server(&server_session); @@ -75,18 +80,20 @@ void CryptoTestUtils::HandshakeWithFakeServer( CHECK_NE(0u, client_conn->packets_.size()); CommunicateHandshakeMessages(client_conn, client, server_conn, &server); + + CompareClientAndServerKeys(client, &server); } // static void CryptoTestUtils::HandshakeWithFakeClient( PacketSavingConnection* server_conn, - QuicCryptoStream* server) { + QuicCryptoServerStream* server) { QuicGuid guid(1); IPAddressNumber ip; CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); IPEndPoint addr = IPEndPoint(ip, 1); PacketSavingConnection* client_conn = - new PacketSavingConnection(guid, addr); + new PacketSavingConnection(guid, addr, false); TestSession client_session(client_conn, true); QuicCryptoClientStream client(&client_session, "test.example.com"); @@ -94,6 +101,50 @@ void CryptoTestUtils::HandshakeWithFakeClient( CHECK_EQ(1u, client_conn->packets_.size()); CommunicateHandshakeMessages(client_conn, &client, server_conn, server); + + CompareClientAndServerKeys(&client, server); +} + +// static +void CryptoTestUtils::CompareClientAndServerKeys( + QuicCryptoClientStream* client, + QuicCryptoServerStream* server) { + StringPiece client_encrypter_key = + client->crypto_negotiated_params_.encrypter->GetKey(); + StringPiece client_encrypter_iv = + client->crypto_negotiated_params_.encrypter->GetNoncePrefix(); + StringPiece client_decrypter_key = + client->crypto_negotiated_params_.decrypter->GetKey(); + StringPiece client_decrypter_iv = + client->crypto_negotiated_params_.decrypter->GetNoncePrefix(); + StringPiece server_encrypter_key = + server->crypto_negotiated_params_.encrypter->GetKey(); + StringPiece server_encrypter_iv = + server->crypto_negotiated_params_.encrypter->GetNoncePrefix(); + StringPiece server_decrypter_key = + server->crypto_negotiated_params_.decrypter->GetKey(); + StringPiece server_decrypter_iv = + server->crypto_negotiated_params_.decrypter->GetNoncePrefix(); + CompareCharArraysWithHexError("client write key", + client_encrypter_key.data(), + client_encrypter_key.length(), + server_decrypter_key.data(), + server_decrypter_key.length()); + CompareCharArraysWithHexError("client write IV", + client_encrypter_iv.data(), + client_encrypter_iv.length(), + server_decrypter_iv.data(), + server_decrypter_iv.length()); + CompareCharArraysWithHexError("server write key", + server_encrypter_key.data(), + server_encrypter_key.length(), + client_decrypter_key.data(), + client_decrypter_key.length()); + CompareCharArraysWithHexError("server write IV", + server_encrypter_iv.data(), + server_encrypter_iv.length(), + client_decrypter_iv.data(), + client_decrypter_iv.length()); } } // namespace test } // namespace net diff --git a/net/quic/test_tools/crypto_test_utils.h b/net/quic/test_tools/crypto_test_utils.h index 6251396..a205074 100644 --- a/net/quic/test_tools/crypto_test_utils.h +++ b/net/quic/test_tools/crypto_test_utils.h @@ -14,7 +14,8 @@ namespace net { -class QuicCryptoStream; +class QuicCryptoClientStream; +class QuicCryptoServerStream; namespace test { @@ -23,10 +24,14 @@ class PacketSavingConnection; class CryptoTestUtils { public: static void HandshakeWithFakeServer(PacketSavingConnection* client_conn, - QuicCryptoStream* client); + QuicCryptoClientStream* client); static void HandshakeWithFakeClient(PacketSavingConnection* server_conn, - QuicCryptoStream* server); + QuicCryptoServerStream* server); + + private: + static void CompareClientAndServerKeys(QuicCryptoClientStream* client, + QuicCryptoServerStream* server); }; } // namespace test diff --git a/net/quic/test_tools/quic_connection_peer.cc b/net/quic/test_tools/quic_connection_peer.cc index d834d5e..102b2d7 100644 --- a/net/quic/test_tools/quic_connection_peer.cc +++ b/net/quic/test_tools/quic_connection_peer.cc @@ -9,6 +9,7 @@ #include "net/quic/congestion_control/receive_algorithm_interface.h" #include "net/quic/congestion_control/send_algorithm_interface.h" #include "net/quic/quic_connection.h" +#include "net/quic/test_tools/quic_framer_peer.h" namespace net { namespace test { @@ -102,5 +103,17 @@ QuicPacketEntropyHash QuicConnectionPeer::ReceivedEntropyHash( return connection->entropy_manager_.ReceivedEntropyHash(sequence_number); } +// static +bool QuicConnectionPeer::IsServer(QuicConnection* connection) { + return connection->is_server_; +} + +// static +void QuicConnectionPeer::SetIsServer(QuicConnection* connection, + bool is_server) { + connection->is_server_ = is_server; + QuicFramerPeer::SetIsServer(&connection->framer_, is_server); +} + } // namespace test } // namespace net diff --git a/net/quic/test_tools/quic_connection_peer.h b/net/quic/test_tools/quic_connection_peer.h index 8c3ff67..97f7afa 100644 --- a/net/quic/test_tools/quic_connection_peer.h +++ b/net/quic/test_tools/quic_connection_peer.h @@ -7,6 +7,7 @@ #include "base/basictypes.h" #include "net/quic/quic_protocol.h" +#include "net/quic/quic_stats.h" namespace net { @@ -62,6 +63,10 @@ class QuicConnectionPeer { QuicConnection* connection, QuicPacketSequenceNumber sequence_number); + static bool IsServer(QuicConnection* connection); + + static void SetIsServer(QuicConnection* connection, bool is_server); + private: DISALLOW_COPY_AND_ASSIGN(QuicConnectionPeer); }; diff --git a/net/quic/test_tools/quic_framer_peer.cc b/net/quic/test_tools/quic_framer_peer.cc new file mode 100644 index 0000000..15a8067 --- /dev/null +++ b/net/quic/test_tools/quic_framer_peer.cc @@ -0,0 +1,36 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/test_tools/quic_framer_peer.h" + +#include "net/quic/quic_framer.h" +#include "net/quic/quic_protocol.h" + +namespace net { +namespace test { + +// static +QuicPacketSequenceNumber QuicFramerPeer::CalculatePacketSequenceNumberFromWire( + QuicFramer* framer, + QuicPacketSequenceNumber packet_sequence_number) { + return framer->CalculatePacketSequenceNumberFromWire(packet_sequence_number); +} + +// static +void QuicFramerPeer::SetLastSequenceNumber( + QuicFramer* framer, + QuicPacketSequenceNumber packet_sequence_number) { + framer->last_sequence_number_ = packet_sequence_number; +} + +void QuicFramerPeer::SetIsServer(QuicFramer* framer, bool is_server) { + framer->is_server_ = is_server; +} + +void QuicFramerPeer::SetVersion(QuicFramer* framer, QuicVersionTag version) { + framer->quic_version_ = version; +} + +} // namespace test +} // namespace net diff --git a/net/quic/test_tools/quic_framer_peer.h b/net/quic/test_tools/quic_framer_peer.h new file mode 100644 index 0000000..3880412 --- /dev/null +++ b/net/quic/test_tools/quic_framer_peer.h @@ -0,0 +1,35 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_TEST_TOOLS_QUIC_FRAMER_PEER_H_ +#define NET_QUIC_TEST_TOOLS_QUIC_FRAMER_PEER_H_ + +#include "net/quic/quic_protocol.h" + +namespace net { + +class QuicFramer; + +namespace test { + +class QuicFramerPeer { + public: + static QuicPacketSequenceNumber CalculatePacketSequenceNumberFromWire( + QuicFramer* framer, + QuicPacketSequenceNumber packet_sequence_number); + static void SetLastSequenceNumber( + QuicFramer* framer, + QuicPacketSequenceNumber packet_sequence_number); + static void SetIsServer(QuicFramer* framer, bool is_server); + static void SetVersion(QuicFramer* framer, QuicVersionTag version); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicFramerPeer); +}; + +} // namespace test + +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_FRAMER_PEER_H_ diff --git a/net/quic/test_tools/quic_packet_creator_peer.cc b/net/quic/test_tools/quic_packet_creator_peer.cc new file mode 100644 index 0000000..4451f02 --- /dev/null +++ b/net/quic/test_tools/quic_packet_creator_peer.cc @@ -0,0 +1,30 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/test_tools/quic_packet_creator_peer.h" + +#include "net/quic/quic_packet_creator.h" + +namespace net { +namespace test { + +// static +bool QuicPacketCreatorPeer::SendVersionInPacket(QuicPacketCreator* creator) { + return creator->send_version_in_packet_; +} + +// static +void QuicPacketCreatorPeer::SetSendVersionInPacket( + QuicPacketCreator* creator, bool send_version_in_packet) { + creator->send_version_in_packet_ = send_version_in_packet; +} + +// static +void QuicPacketCreatorPeer::SetIsServer(QuicPacketCreator* creator, + bool is_server) { + creator->is_server_ = is_server; +} + +} // namespace test +} // namespace net diff --git a/net/quic/test_tools/quic_packet_creator_peer.h b/net/quic/test_tools/quic_packet_creator_peer.h new file mode 100644 index 0000000..816afa9 --- /dev/null +++ b/net/quic/test_tools/quic_packet_creator_peer.h @@ -0,0 +1,32 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_TEST_TOOLS_QUIC_PACKET_CREATOR_PEER_H_ +#define NET_QUIC_TEST_TOOLS_QUIC_PACKET_CREATOR_PEER_H_ + +#include "net/quic/quic_protocol.h" + +namespace net { +class QuicPacketCreator; + +namespace test { + +class QuicPacketCreatorPeer { + public: + static bool SendVersionInPacket(QuicPacketCreator* creator); + + static void SetSendVersionInPacket(QuicPacketCreator* creator, + bool send_version_in_packet); + + static void SetIsServer(QuicPacketCreator* creator, bool is_server); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicPacketCreatorPeer); +}; + +} // namespace test + +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_PACKET_CREATOR_PEER_H_ diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc index b1c2657..6eef75c 100644 --- a/net/quic/test_tools/quic_test_utils.cc +++ b/net/quic/test_tools/quic_test_utils.cc @@ -23,6 +23,10 @@ namespace test { MockFramerVisitor::MockFramerVisitor() { // By default, we want to accept packets. + ON_CALL(*this, OnProtocolVersionMismatch(_)) + .WillByDefault(testing::Return(false)); + + // By default, we want to accept packets. ON_CALL(*this, OnPacketHeader(_)) .WillByDefault(testing::Return(true)); } @@ -30,6 +34,10 @@ MockFramerVisitor::MockFramerVisitor() { MockFramerVisitor::~MockFramerVisitor() { } +bool NoOpFramerVisitor::OnProtocolVersionMismatch(QuicVersionTag version) { + return false; +} + bool NoOpFramerVisitor::OnPacketHeader(const QuicPacketHeader& header) { return true; } @@ -82,6 +90,12 @@ void FramerVisitorCapturingFrames::OnGoAwayFrame(const QuicGoAwayFrame& frame) { ++frame_count_; } +void FramerVisitorCapturingFrames::OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) { + version_negotiation_packet_.reset(new QuicVersionNegotiationPacket(packet)); + frame_count_ = 0; +} + FramerVisitorCapturingPublicReset::FramerVisitorCapturingPublicReset() { } @@ -113,15 +127,18 @@ QuicRandom* MockHelper::GetRandomGenerator() { return &random_generator_; } -MockConnection::MockConnection(QuicGuid guid, IPEndPoint address) - : QuicConnection(guid, address, new MockHelper()), +MockConnection::MockConnection(QuicGuid guid, + IPEndPoint address, + bool is_server) + : QuicConnection(guid, address, new MockHelper(), is_server), helper_(helper()) { } MockConnection::MockConnection(QuicGuid guid, IPEndPoint address, - QuicConnectionHelperInterface* helper) - : QuicConnection(guid, address, helper), + QuicConnectionHelperInterface* helper, + bool is_server) + : QuicConnection(guid, address, helper, is_server), helper_(helper) { } @@ -129,8 +146,9 @@ MockConnection::~MockConnection() { } PacketSavingConnection::PacketSavingConnection(QuicGuid guid, - IPEndPoint address) - : MockConnection(guid, address) { + IPEndPoint address, + bool is_server) + : MockConnection(guid, address, is_server) { } PacketSavingConnection::~PacketSavingConnection() { @@ -245,17 +263,19 @@ void CompareQuicDataWithHexError( static QuicPacket* ConstructPacketFromHandshakeMessage( QuicGuid guid, - const CryptoHandshakeMessage& message) { + const CryptoHandshakeMessage& message, + bool should_include_version) { CryptoFramer crypto_framer; scoped_ptr<QuicData> data(crypto_framer.ConstructHandshakeMessage(message)); QuicFramer quic_framer(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)); + QuicEncrypter::Create(kNULL), + false); QuicPacketHeader header; header.public_header.guid = guid; header.public_header.reset_flag = false; - header.public_header.version_flag = false; + header.public_header.version_flag = should_include_version; header.packet_sequence_number = 1; header.entropy_flag = false; header.entropy_hash = 0; @@ -275,7 +295,7 @@ static QuicPacket* ConstructPacketFromHandshakeMessage( QuicPacket* ConstructHandshakePacket(QuicGuid guid, CryptoTag tag) { CryptoHandshakeMessage message; message.tag = tag; - return ConstructPacketFromHandshakeMessage(guid, message); + return ConstructPacketFromHandshakeMessage(guid, message, false); } CryptoHandshakeMessage CreateChloMessage(const QuicClock* clock, @@ -298,10 +318,12 @@ CryptoHandshakeMessage CreateChloMessage(const QuicClock* clock, QuicPacket* ConstructClientHelloPacket(QuicGuid guid, const QuicClock* clock, QuicRandom* random_generator, - const string& server_hostname) { + const string& server_hostname, + bool should_include_version) { CryptoHandshakeMessage chlo = CreateChloMessage(clock, random_generator, server_hostname); - return ConstructPacketFromHandshakeMessage(guid, chlo); + return ConstructPacketFromHandshakeMessage( + guid, chlo, should_include_version); } CryptoHandshakeMessage CreateShloMessage(const QuicClock* clock, @@ -344,10 +366,11 @@ QuicPacket* ConstructServerHelloPacket(QuicGuid guid, const string& server_hostname) { CryptoHandshakeMessage shlo = CreateShloMessage(clock, random_generator, server_hostname); - return ConstructPacketFromHandshakeMessage(guid, shlo); + return ConstructPacketFromHandshakeMessage(guid, shlo, false); } size_t GetPacketLengthForOneStream(bool include_version, size_t payload) { + // TODO(wtc): the hardcoded use of NullEncrypter here seems wrong. return NullEncrypter().GetCiphertextSize(payload) + QuicPacketCreator::StreamFramePacketOverhead(1, include_version); } diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h index 33a5c56..f1d8cbf 100644 --- a/net/quic/test_tools/quic_test_utils.h +++ b/net/quic/test_tools/quic_test_utils.h @@ -39,7 +39,8 @@ CryptoHandshakeMessage CreateChloMessage(const QuicClock* clock, QuicPacket* ConstructClientHelloPacket(QuicGuid guid, const QuicClock* clock, QuicRandom* random_generator, - const std::string& server_hostname); + const std::string& server_hostname, + bool should_include_version); CryptoHandshakeMessage CreateShloMessage(const QuicClock* clock, QuicRandom* random_generator, @@ -61,10 +62,14 @@ class MockFramerVisitor : public QuicFramerVisitorInterface { ~MockFramerVisitor(); MOCK_METHOD1(OnError, void(QuicFramer* framer)); + // The constructor sets this up to return false by default. + MOCK_METHOD1(OnProtocolVersionMismatch, bool(QuicVersionTag version)); MOCK_METHOD0(OnPacket, void()); MOCK_METHOD1(OnPublicResetPacket, void(const QuicPublicResetPacket& header)); + MOCK_METHOD1(OnVersionNegotiationPacket, + void(const QuicVersionNegotiationPacket& packet)); MOCK_METHOD0(OnRevivedPacket, void()); - // The constructor set this up to return true by default. + // The constructor sets this up to return true by default. MOCK_METHOD1(OnPacketHeader, bool(const QuicPacketHeader& header)); MOCK_METHOD1(OnFecProtectedPayload, void(base::StringPiece payload)); MOCK_METHOD1(OnStreamFrame, void(const QuicStreamFrame& frame)); @@ -90,7 +95,10 @@ class NoOpFramerVisitor : public QuicFramerVisitorInterface { virtual void OnPacket() OVERRIDE {} virtual void OnPublicResetPacket( const QuicPublicResetPacket& packet) OVERRIDE {} + virtual void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) OVERRIDE {} virtual void OnRevivedPacket() OVERRIDE {} + virtual bool OnProtocolVersionMismatch(QuicVersionTag version) OVERRIDE; virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE; virtual void OnFecProtectedPayload(base::StringPiece payload) OVERRIDE {} virtual void OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE {} @@ -130,7 +138,8 @@ class FramerVisitorCapturingFrames : public NoOpFramerVisitor { virtual ~FramerVisitorCapturingFrames(); // NoOpFramerVisitor - + virtual void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) OVERRIDE; virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE; virtual void OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE; virtual void OnAckFrame(const QuicAckFrame& frame) OVERRIDE; @@ -151,6 +160,9 @@ class FramerVisitorCapturingFrames : public NoOpFramerVisitor { QuicRstStreamFrame* rst() { return rst_.get(); } QuicConnectionCloseFrame* close() { return close_.get(); } QuicGoAwayFrame* goaway() { return goaway_.get(); } + QuicVersionNegotiationPacket* version_negotiation_packet() { + return version_negotiation_packet_.get(); + } private: size_t frame_count_; @@ -161,6 +173,7 @@ class FramerVisitorCapturingFrames : public NoOpFramerVisitor { scoped_ptr<QuicRstStreamFrame> rst_; scoped_ptr<QuicConnectionCloseFrame> close_; scoped_ptr<QuicGoAwayFrame> goaway_; + scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_; DISALLOW_COPY_AND_ASSIGN(FramerVisitorCapturingFrames); }; @@ -209,10 +222,11 @@ class MockHelper : public QuicConnectionHelperInterface { class MockConnection : public QuicConnection { public: // Uses a MockHelper. - MockConnection(QuicGuid guid, IPEndPoint address); + MockConnection(QuicGuid guid, IPEndPoint address, bool is_server); MockConnection(QuicGuid guid, IPEndPoint address, - QuicConnectionHelperInterface* helper); + QuicConnectionHelperInterface* helper, + bool is_server); virtual ~MockConnection(); MOCK_METHOD3(ProcessUdpPacket, void(const IPEndPoint& self_address, @@ -233,6 +247,10 @@ class MockConnection : public QuicConnection { QuicConnection::ProcessUdpPacket(self_address, peer_address, packet); } + virtual bool OnProtocolVersionMismatch(QuicVersionTag version) OVERRIDE { + return false; + } + private: scoped_ptr<QuicConnectionHelperInterface> helper_; DISALLOW_COPY_AND_ASSIGN(MockConnection); @@ -240,7 +258,7 @@ class MockConnection : public QuicConnection { class PacketSavingConnection : public MockConnection { public: - PacketSavingConnection(QuicGuid guid, IPEndPoint address); + PacketSavingConnection(QuicGuid guid, IPEndPoint address, bool is_server); virtual ~PacketSavingConnection(); virtual bool SendOrQueuePacket(QuicPacketSequenceNumber sequence_number, @@ -291,10 +309,13 @@ class MockSendAlgorithm : public SendAlgorithmInterface { MOCK_METHOD3(OnIncomingAck, void(QuicPacketSequenceNumber, QuicByteCount, QuicTime::Delta)); MOCK_METHOD1(OnIncomingLoss, void(QuicTime)); - MOCK_METHOD5(SentPacket, void(QuicTime sent_time, QuicPacketSequenceNumber, - QuicByteCount, bool, bool)); - MOCK_METHOD2(TimeUntilSend, QuicTime::Delta(QuicTime now, bool)); + MOCK_METHOD4(SentPacket, void(QuicTime sent_time, QuicPacketSequenceNumber, + QuicByteCount, bool)); + MOCK_METHOD2(AbandoningPacket, void(QuicPacketSequenceNumber sequence_number, + QuicByteCount abandoned_bytes)); + MOCK_METHOD3(TimeUntilSend, QuicTime::Delta(QuicTime now, bool, bool)); MOCK_METHOD0(BandwidthEstimate, QuicBandwidth(void)); + MOCK_METHOD0(SmoothedRtt, QuicTime::Delta(void)); private: DISALLOW_COPY_AND_ASSIGN(MockSendAlgorithm); diff --git a/net/quic/test_tools/simple_quic_framer.cc b/net/quic/test_tools/simple_quic_framer.cc index 5dec9d0..d529ac7 100644 --- a/net/quic/test_tools/simple_quic_framer.cc +++ b/net/quic/test_tools/simple_quic_framer.cc @@ -25,9 +25,16 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface { error_ = framer->error(); } + virtual bool OnProtocolVersionMismatch(QuicVersionTag version) { + return false; + } + virtual void OnPacket() {} virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) {} + virtual void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) {} virtual void OnRevivedPacket() {} + virtual bool OnPacketHeader(const QuicPacketHeader& header) { has_header_ = true; header_ = header; @@ -116,7 +123,9 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface { SimpleQuicFramer::SimpleQuicFramer() : framer_(kQuicVersion1, QuicDecrypter::Create(kNULL), - QuicEncrypter::Create(kNULL)) { + QuicEncrypter::Create(kNULL), + true), + visitor_(NULL) { } SimpleQuicFramer::~SimpleQuicFramer() { |