diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-11-20 10:33:36 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-11-22 11:45:12 +0000 |
commit | be59a35641616a4cf23c4a13fa0632624b021c1b (patch) | |
tree | 9da183258bdf9cc413f7562079d25ace6955467f /chromium/net/quic/core | |
parent | d702e4b6a64574e97fc7df8fe3238cde70242080 (diff) | |
download | qtwebengine-chromium-be59a35641616a4cf23c4a13fa0632624b021c1b.tar.gz |
BASELINE: Update Chromium to 62.0.3202.101
Change-Id: I2d5eca8117600df6d331f6166ab24d943d9814ac
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/net/quic/core')
104 files changed, 5804 insertions, 3492 deletions
diff --git a/chromium/net/quic/core/congestion_control/bandwidth_sampler.cc b/chromium/net/quic/core/congestion_control/bandwidth_sampler.cc index f95fdd71816..47b64ef8c21 100644 --- a/chromium/net/quic/core/congestion_control/bandwidth_sampler.cc +++ b/chromium/net/quic/core/congestion_control/bandwidth_sampler.cc @@ -20,10 +20,7 @@ BandwidthSampler::BandwidthSampler() last_sent_packet_(0), is_app_limited_(false), end_of_app_limited_phase_(0), - connection_state_map_(), - connection_state_map_new_(), - use_new_connection_state_map_( - FLAGS_quic_reloadable_flag_quic_faster_bandwidth_sampler) {} + connection_state_map_() {} BandwidthSampler::~BandwidthSampler() {} @@ -56,61 +53,33 @@ void BandwidthSampler::OnPacketSent( last_acked_packet_sent_time_ = sent_time; } - if (use_new_connection_state_map_) { - if (!connection_state_map_new_.IsEmpty() && - packet_number > - connection_state_map_new_.last_packet() + kMaxTrackedPackets) { - QUIC_BUG << "BandwidthSampler in-flight packet map has exceeded maximum " - "number " - "of tracked packets."; - } - - bool success = connection_state_map_new_.Emplace(packet_number, sent_time, - bytes, *this); - QUIC_BUG_IF(!success) << "BandwidthSampler failed to insert the packet " - "into the map, most likely because it's already " - "in it."; - return; + if (!connection_state_map_.IsEmpty() && + packet_number > + connection_state_map_.last_packet() + kMaxTrackedPackets) { + QUIC_BUG << "BandwidthSampler in-flight packet map has exceeded maximum " + "number " + "of tracked packets."; } - DCHECK(connection_state_map_.find(packet_number) == - connection_state_map_.end()); - connection_state_map_.emplace( - packet_number, ConnectionStateOnSentPacket(sent_time, bytes, *this)); - - QUIC_BUG_IF(connection_state_map_.size() > kMaxTrackedPackets) - << "BandwidthSampler in-flight packet map has exceeded maximum number " - "of tracked packets."; + bool success = + connection_state_map_.Emplace(packet_number, sent_time, bytes, *this); + QUIC_BUG_IF(!success) << "BandwidthSampler failed to insert the packet " + "into the map, most likely because it's already " + "in it."; } BandwidthSample BandwidthSampler::OnPacketAcknowledged( QuicTime ack_time, QuicPacketNumber packet_number) { - if (use_new_connection_state_map_) { - ConnectionStateOnSentPacket* sent_packet_pointer = - connection_state_map_new_.GetEntry(packet_number); - if (sent_packet_pointer == nullptr) { - // See the TODO below. - return BandwidthSample(); - } - BandwidthSample sample = OnPacketAcknowledgedInner(ack_time, packet_number, - *sent_packet_pointer); - connection_state_map_new_.Remove(packet_number); - QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_faster_bandwidth_sampler, 1, 2); - return sample; - } - - auto it = connection_state_map_.find(packet_number); - if (it == connection_state_map_.end()) { - // TODO(vasilvv): currently, this can happen because the congestion - // controller can be created while some of the handshake packets are still - // in flight. Once the sampler is fully integrated with unacked packet map, - // this should be a QUIC_BUG equivalent. + ConnectionStateOnSentPacket* sent_packet_pointer = + connection_state_map_.GetEntry(packet_number); + if (sent_packet_pointer == nullptr) { + // See the TODO below. return BandwidthSample(); } BandwidthSample sample = - OnPacketAcknowledgedInner(ack_time, packet_number, it->second); - connection_state_map_.erase(it); + OnPacketAcknowledgedInner(ack_time, packet_number, *sent_packet_pointer); + connection_state_map_.Remove(packet_number); return sample; } @@ -172,23 +141,10 @@ BandwidthSample BandwidthSampler::OnPacketAcknowledgedInner( } void BandwidthSampler::OnPacketLost(QuicPacketNumber packet_number) { - if (use_new_connection_state_map_) { - // TODO(vasilvv): see the comment for the case of missing packets in - // BandwidthSampler::OnPacketAcknowledged on why this does not raise a - // QUIC_BUG when removal fails. - connection_state_map_new_.Remove(packet_number); - QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_faster_bandwidth_sampler, 2, 2); - return; - } - - auto it = connection_state_map_.find(packet_number); - if (it == connection_state_map_.end()) { - // TODO(vasilvv): see the comment for the same case in - // BandwidthSampler::OnPacketAcknowledged. - return; - } - - connection_state_map_.erase(it); + // TODO(vasilvv): see the comment for the case of missing packets in + // BandwidthSampler::OnPacketAcknowledged on why this does not raise a + // QUIC_BUG when removal fails. + connection_state_map_.Remove(packet_number); } void BandwidthSampler::OnAppLimited() { @@ -197,18 +153,22 @@ void BandwidthSampler::OnAppLimited() { } void BandwidthSampler::RemoveObsoletePackets(QuicPacketNumber least_unacked) { - if (use_new_connection_state_map_) { - while (!connection_state_map_new_.IsEmpty() && - connection_state_map_new_.first_packet() < least_unacked) { - connection_state_map_new_.Remove( - connection_state_map_new_.first_packet()); - } - return; - } - while (!connection_state_map_.empty() && - connection_state_map_.begin()->first < least_unacked) { - connection_state_map_.pop_front(); + while (!connection_state_map_.IsEmpty() && + connection_state_map_.first_packet() < least_unacked) { + connection_state_map_.Remove(connection_state_map_.first_packet()); } } +QuicByteCount BandwidthSampler::total_bytes_acked() const { + return total_bytes_acked_; +} + +bool BandwidthSampler::is_app_limited() const { + return is_app_limited_; +} + +QuicPacketNumber BandwidthSampler::end_of_app_limited_phase() const { + return end_of_app_limited_phase_; +} + } // namespace net diff --git a/chromium/net/quic/core/congestion_control/bandwidth_sampler.h b/chromium/net/quic/core/congestion_control/bandwidth_sampler.h index 75845404186..b42e8b91569 100644 --- a/chromium/net/quic/core/congestion_control/bandwidth_sampler.h +++ b/chromium/net/quic/core/congestion_control/bandwidth_sampler.h @@ -37,6 +37,50 @@ struct QUIC_EXPORT_PRIVATE BandwidthSample { is_app_limited(false) {} }; +// An interface common to any class that can provide bandwidth samples from the +// information per individual acknowledged packet. +class QUIC_EXPORT_PRIVATE BandwidthSamplerInterface { + public: + virtual ~BandwidthSamplerInterface() {} + + // Inputs the sent packet information into the sampler. Assumes that all + // packets are sent in order. The information about the packet will not be + // released from the sampler until it the packet is either acknowledged or + // declared lost. + virtual void OnPacketSent( + QuicTime sent_time, + QuicPacketNumber packet_number, + QuicByteCount bytes, + QuicByteCount bytes_in_flight, + HasRetransmittableData has_retransmittable_data) = 0; + + // Notifies the sampler that the |packet_number| is acknowledged. Returns a + // bandwidth sample. If no bandwidth sample is available, + // QuicBandwidth::Zero() is returned. + virtual BandwidthSample OnPacketAcknowledged( + QuicTime ack_time, + QuicPacketNumber packet_number) = 0; + + // Informs the sampler that a packet is considered lost and it should no + // longer keep track of it. + virtual void OnPacketLost(QuicPacketNumber packet_number) = 0; + + // Informs the sampler that the connection is currently app-limited, causing + // the sampler to enter the app-limited phase. The phase will expire by + // itself. + virtual void OnAppLimited() = 0; + + // Remove all the packets lower than the specified packet number. + virtual void RemoveObsoletePackets(QuicPacketNumber least_unacked) = 0; + + // Total number of bytes currently acknowledged by the receiver. + virtual QuicByteCount total_bytes_acked() const = 0; + + // Application-limited information exported for debugging. + virtual bool is_app_limited() const = 0; + virtual QuicPacketNumber end_of_app_limited_phase() const = 0; +}; + // BandwidthSampler keeps track of sent and acknowledged packets and outputs a // bandwidth sample for every packet acknowledged. The samples are taken for // individual packets, and are not filtered; the consumer has to filter the @@ -117,42 +161,27 @@ struct QUIC_EXPORT_PRIVATE BandwidthSample { // up until an ack for a packet that was sent after OnAppLimited() was called. // Note that while the scenario above is not the only scenario when the // connection is app-limited, the approach works in other cases too. -class QUIC_EXPORT_PRIVATE BandwidthSampler { +class QUIC_EXPORT_PRIVATE BandwidthSampler : public BandwidthSamplerInterface { public: BandwidthSampler(); - ~BandwidthSampler(); + ~BandwidthSampler() override; - // Inputs the sent packet information into the sampler. Assumes that all - // packets are sent in order. The information about the packet will not be - // released from the sampler until it the packet is either acknowledged or - // declared lost. void OnPacketSent(QuicTime sent_time, QuicPacketNumber packet_number, QuicByteCount bytes, QuicByteCount bytes_in_flight, - HasRetransmittableData has_retransmittable_data); - // Notifies the sampler that the |packet_number| is acknowledged. Returns a - // bandwidth sample. If no bandwidth sample is available, - // QuicBandwidth::Zero() is returned. + HasRetransmittableData has_retransmittable_data) override; BandwidthSample OnPacketAcknowledged(QuicTime ack_time, - QuicPacketNumber packet_number); - // Informs the sampler that a packet is considered lost and it should no - // longer keep track of it. - void OnPacketLost(QuicPacketNumber packet_number); + QuicPacketNumber packet_number) override; + void OnPacketLost(QuicPacketNumber packet_number) override; - // Informs the sampler that the connection is currently app-limited, causing - // the sampler to enter the app-limited phase. The phase will expire by itself - // (see |is_app_limited_| documentation for details). - void OnAppLimited(); + void OnAppLimited() override; - // Remove all the packets lower than the specified packet number. - void RemoveObsoletePackets(QuicPacketNumber least_unacked); + void RemoveObsoletePackets(QuicPacketNumber least_unacked) override; - QuicByteCount total_bytes_acked() const { return total_bytes_acked_; } - bool is_app_limited() const { return is_app_limited_; } - QuicPacketNumber end_of_app_limited_phase() const { - return end_of_app_limited_phase_; - } + QuicByteCount total_bytes_acked() const override; + bool is_app_limited() const override; + QuicPacketNumber end_of_app_limited_phase() const override; private: friend class test::BandwidthSamplerPeer; @@ -212,8 +241,13 @@ class QUIC_EXPORT_PRIVATE BandwidthSampler { // PacketNumberIndexedQueue. ConnectionStateOnSentPacket() : sent_time(QuicTime::Zero()), + size(0), + total_bytes_sent(0), + total_bytes_sent_at_last_acked_packet(0), last_acked_packet_sent_time(QuicTime::Zero()), - last_acked_packet_ack_time(QuicTime::Zero()) {} + last_acked_packet_ack_time(QuicTime::Zero()), + total_bytes_acked_at_the_last_acked_packet(0), + is_app_limited(false) {} }; typedef QuicLinkedHashMap<QuicPacketNumber, ConnectionStateOnSentPacket> @@ -249,10 +283,7 @@ class QUIC_EXPORT_PRIVATE BandwidthSampler { // Record of the connection state at the point where each packet in flight was // sent, indexed by the packet number. - ConnectionStateMap connection_state_map_; - PacketNumberIndexedQueue<ConnectionStateOnSentPacket> - connection_state_map_new_; - const bool use_new_connection_state_map_; + PacketNumberIndexedQueue<ConnectionStateOnSentPacket> connection_state_map_; // Handles the actual bandwidth calculations, whereas the outer method handles // retrieving and removing |sent_packet|. diff --git a/chromium/net/quic/core/congestion_control/bandwidth_sampler_test.cc b/chromium/net/quic/core/congestion_control/bandwidth_sampler_test.cc index 517ab552cb8..503e39ff3d4 100644 --- a/chromium/net/quic/core/congestion_control/bandwidth_sampler_test.cc +++ b/chromium/net/quic/core/congestion_control/bandwidth_sampler_test.cc @@ -14,19 +14,12 @@ namespace test { class BandwidthSamplerPeer { public: static size_t GetNumberOfTrackedPackets(const BandwidthSampler& sampler) { - if (FLAGS_quic_reloadable_flag_quic_faster_bandwidth_sampler) { - return sampler.connection_state_map_new_.number_of_present_entries(); - } - return sampler.connection_state_map_.size(); + return sampler.connection_state_map_.number_of_present_entries(); } static QuicByteCount GetPacketSize(const BandwidthSampler& sampler, QuicPacketNumber packet_number) { - if (FLAGS_quic_reloadable_flag_quic_faster_bandwidth_sampler) { - return sampler.connection_state_map_new_.GetEntry(packet_number)->size; - } - auto iterator = sampler.connection_state_map_.find(packet_number); - return iterator->second.size; + return sampler.connection_state_map_.GetEntry(packet_number)->size; } }; diff --git a/chromium/net/quic/core/congestion_control/bbr_sender.cc b/chromium/net/quic/core/congestion_control/bbr_sender.cc index e8dadc9b337..710c1600a1c 100644 --- a/chromium/net/quic/core/congestion_control/bbr_sender.cc +++ b/chromium/net/quic/core/congestion_control/bbr_sender.cc @@ -63,7 +63,7 @@ BbrSender::DebugState::DebugState(const BbrSender& sender) recovery_state(sender.recovery_state_), recovery_window(sender.recovery_window_), last_sample_is_app_limited(sender.last_sample_is_app_limited_), - end_of_app_limited_phase(sender.sampler_.end_of_app_limited_phase()) {} + end_of_app_limited_phase(sender.sampler_->end_of_app_limited_phase()) {} BbrSender::DebugState::DebugState(const DebugState& state) = default; @@ -76,7 +76,7 @@ BbrSender::BbrSender(const RttStats* rtt_stats, unacked_packets_(unacked_packets), random_(random), mode_(STARTUP), - sampler_(), + sampler_(new BandwidthSampler()), round_trip_count_(0), last_sent_packet_(0), current_round_trip_end_(0), @@ -85,6 +85,7 @@ BbrSender::BbrSender(const RttStats* rtt_stats, aggregation_epoch_start_time_(QuicTime::Zero()), aggregation_epoch_bytes_(0), bytes_acked_since_queue_drained_(0), + max_aggregation_bytes_multiplier_(0), min_rtt_(QuicTime::Delta::Zero()), min_rtt_timestamp_(QuicTime::Zero()), congestion_window_(initial_tcp_congestion_window * kDefaultTCPMSS), @@ -99,6 +100,7 @@ BbrSender::BbrSender(const RttStats* rtt_stats, rtt_variance_weight_( static_cast<float>(FLAGS_quic_bbr_rtt_variation_weight)), num_startup_rtts_(kRoundTripsWithoutGrowthBeforeExitingStartup), + exit_startup_on_loss_(false), cycle_current_offset_(0), last_cycle_start_(QuicTime::Zero()), is_at_full_bandwidth_(false), @@ -128,12 +130,16 @@ bool BbrSender::OnPacketSent(QuicTime sent_time, HasRetransmittableData is_retransmittable) { last_sent_packet_ = packet_number; - if (bytes_in_flight == 0 && sampler_.is_app_limited()) { + if (bytes_in_flight == 0 && sampler_->is_app_limited()) { exiting_quiescence_ = true; } - sampler_.OnPacketSent(sent_time, packet_number, bytes, bytes_in_flight, - is_retransmittable); + if (!aggregation_epoch_start_time_.IsInitialized()) { + aggregation_epoch_start_time_ = sent_time; + } + + sampler_->OnPacketSent(sent_time, packet_number, bytes, bytes_in_flight, + is_retransmittable); return is_retransmittable == HAS_RETRANSMITTABLE_DATA; } @@ -177,8 +183,17 @@ bool BbrSender::InRecovery() const { return recovery_state_ != NOT_IN_RECOVERY; } +bool BbrSender::IsProbingForMoreBandwidth() const { + return mode_ == PROBE_BW && pacing_gain_ > 1; +} + void BbrSender::SetFromConfig(const QuicConfig& config, Perspective perspective) { + if (FLAGS_quic_reloadable_flag_quic_bbr_exit_startup_on_loss && + config.HasClientRequestedIndependentOption(kLRTT, perspective)) { + QUIC_FLAG_COUNT(quic_reloadable_flag_quic_bbr_exit_startup_on_loss); + exit_startup_on_loss_ = true; + } if (config.HasClientRequestedIndependentOption(k1RTT, perspective)) { num_startup_rtts_ = 1; } @@ -189,25 +204,31 @@ void BbrSender::SetFromConfig(const QuicConfig& config, config.HasClientRequestedIndependentOption(kBBRR, perspective)) { rate_based_recovery_ = true; } + if (FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes4 && + config.HasClientRequestedIndependentOption(kBBR1, perspective)) { + QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_bbr_ack_aggregation_bytes4, 1, + 2); + max_aggregation_bytes_multiplier_ = 1.5; + } + if (FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes4 && + config.HasClientRequestedIndependentOption(kBBR2, perspective)) { + QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_bbr_ack_aggregation_bytes4, 2, + 2); + max_aggregation_bytes_multiplier_ = 2; + } } -void BbrSender::ResumeConnectionState( - const CachedNetworkParameters& cached_network_params, - bool max_bandwidth_resumption) { +void BbrSender::AdjustNetworkParameters(QuicBandwidth bandwidth, + QuicTime::Delta rtt) { if (!FLAGS_quic_reloadable_flag_quic_bbr_bandwidth_resumption) { return; } QUIC_FLAG_COUNT(quic_reloadable_flag_quic_bbr_bandwidth_resumption); - QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond( - max_bandwidth_resumption - ? cached_network_params.max_bandwidth_estimate_bytes_per_second() - : cached_network_params.bandwidth_estimate_bytes_per_second()); - QuicTime::Delta rtt = - QuicTime::Delta::FromMilliseconds(cached_network_params.min_rtt_ms()); - - max_bandwidth_.Update(bandwidth, round_trip_count_); + if (!bandwidth.IsZero()) { + max_bandwidth_.Update(bandwidth, round_trip_count_); + } if (!rtt.IsZero() && (min_rtt_ > rtt || min_rtt_.IsZero())) { min_rtt_ = rtt; } @@ -216,9 +237,9 @@ void BbrSender::ResumeConnectionState( void BbrSender::OnCongestionEvent(bool /*rtt_updated*/, QuicByteCount prior_in_flight, QuicTime event_time, - const CongestionVector& acked_packets, + const AckedPacketVector& acked_packets, const CongestionVector& lost_packets) { - const QuicByteCount total_bytes_acked_before = sampler_.total_bytes_acked(); + const QuicByteCount total_bytes_acked_before = sampler_->total_bytes_acked(); bool is_round_start = false; bool min_rtt_expired = false; @@ -227,26 +248,17 @@ void BbrSender::OnCongestionEvent(bool /*rtt_updated*/, // Input the new data into the BBR model of the connection. if (!acked_packets.empty()) { - QuicPacketNumber last_acked_packet = acked_packets.rbegin()->first; + QuicPacketNumber last_acked_packet = acked_packets.rbegin()->packet_number; is_round_start = UpdateRoundTripCounter(last_acked_packet); min_rtt_expired = UpdateBandwidthAndMinRtt(event_time, acked_packets); UpdateRecoveryState(last_acked_packet, !lost_packets.empty(), is_round_start); const QuicByteCount bytes_acked = - sampler_.total_bytes_acked() - total_bytes_acked_before; + sampler_->total_bytes_acked() - total_bytes_acked_before; UpdateAckAggregationBytes(event_time, bytes_acked); - if (FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes2 || - FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes3) { - if (FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes2) { - QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_bbr_ack_aggregation_bytes2, - 1, 2); - } - if (FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes3) { - QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_bbr_ack_aggregation_bytes3, - 1, 2); - } + if (max_aggregation_bytes_multiplier_ > 0) { if (unacked_packets_->bytes_in_flight() <= 1.25 * GetTargetCongestionWindow(pacing_gain_)) { bytes_acked_since_queue_drained_ = 0; @@ -272,7 +284,7 @@ void BbrSender::OnCongestionEvent(bool /*rtt_updated*/, // Calculate number of packets acked and lost. QuicByteCount bytes_acked = - sampler_.total_bytes_acked() - total_bytes_acked_before; + sampler_->total_bytes_acked() - total_bytes_acked_before; QuicByteCount bytes_lost = 0; for (const auto& packet : lost_packets) { bytes_lost += packet.second; @@ -285,7 +297,7 @@ void BbrSender::OnCongestionEvent(bool /*rtt_updated*/, CalculateRecoveryWindow(bytes_acked, bytes_lost); // Cleanup internal state. - sampler_.RemoveObsoletePackets(unacked_packets_->GetLeastUnacked()); + sampler_->RemoveObsoletePackets(unacked_packets_->GetLeastUnacked()); } CongestionControlType BbrSender::GetCongestionControlType() const { @@ -334,7 +346,7 @@ void BbrSender::EnterProbeBandwidthMode(QuicTime now) { void BbrSender::DiscardLostPackets(const CongestionVector& lost_packets) { for (const auto& packet : lost_packets) { - sampler_.OnPacketLost(packet.first); + sampler_->OnPacketLost(packet.first); } } @@ -350,11 +362,11 @@ bool BbrSender::UpdateRoundTripCounter(QuicPacketNumber last_acked_packet) { bool BbrSender::UpdateBandwidthAndMinRtt( QuicTime now, - const CongestionVector& acked_packets) { + const AckedPacketVector& acked_packets) { QuicTime::Delta sample_min_rtt = QuicTime::Delta::Infinite(); for (const auto& packet : acked_packets) { BandwidthSample bandwidth_sample = - sampler_.OnPacketAcknowledged(now, packet.first); + sampler_->OnPacketAcknowledged(now, packet.packet_number); last_sample_is_app_limited_ = bandwidth_sample.is_app_limited; if (!bandwidth_sample.rtt.IsZero()) { sample_min_rtt = std::min(sample_min_rtt, bandwidth_sample.rtt); @@ -431,7 +443,8 @@ void BbrSender::CheckIfFullBandwidthReached() { } rounds_without_bandwidth_gain_++; - if (rounds_without_bandwidth_gain_ >= num_startup_rtts_) { + if ((rounds_without_bandwidth_gain_ >= num_startup_rtts_) || + (exit_startup_on_loss_ && InRecovery())) { is_at_full_bandwidth_ = true; } } @@ -460,7 +473,7 @@ void BbrSender::MaybeEnterOrExitProbeRtt(QuicTime now, } if (mode_ == PROBE_RTT) { - sampler_.OnAppLimited(); + sampler_->OnAppLimited(); if (exit_probe_rtt_at_ == QuicTime::Zero()) { // If the window has reached the appropriate size, schedule exiting @@ -588,25 +601,15 @@ void BbrSender::CalculateCongestionWindow(QuicByteCount bytes_acked) { if (rtt_variance_weight_ > 0.f && !BandwidthEstimate().IsZero()) { target_window += rtt_variance_weight_ * rtt_stats_->mean_deviation() * BandwidthEstimate(); - } else if (FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes2 && - is_at_full_bandwidth_) { - QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_bbr_ack_aggregation_bytes2, 2, - 2); - if (2 * max_ack_height_.GetBest() > bytes_acked_since_queue_drained_) { - target_window += - 2 * max_ack_height_.GetBest() - bytes_acked_since_queue_drained_; - } - } else if (FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes3 && - is_at_full_bandwidth_) { - QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_bbr_ack_aggregation_bytes3, 2, - 2); + } else if (max_aggregation_bytes_multiplier_ > 0 && is_at_full_bandwidth_) { // Subtracting only half the bytes_acked_since_queue_drained ensures sending // doesn't completely stop for a long period of time if the queue hasn't // been drained recently. - if (1.5 * max_ack_height_.GetBest() > + if (max_aggregation_bytes_multiplier_ * max_ack_height_.GetBest() > bytes_acked_since_queue_drained_ / 2) { - target_window += 1.5 * max_ack_height_.GetBest() - - bytes_acked_since_queue_drained_ / 2; + target_window += + max_aggregation_bytes_multiplier_ * max_ack_height_.GetBest() - + bytes_acked_since_queue_drained_ / 2; } } else if (is_at_full_bandwidth_) { target_window += max_ack_height_.GetBest(); @@ -635,7 +638,7 @@ void BbrSender::CalculateCongestionWindow(QuicByteCount bytes_acked) { congestion_window_ = std::min(target_window, congestion_window_ + bytes_acked); } else if (congestion_window_ < target_window || - sampler_.total_bytes_acked() < initial_congestion_window_) { + sampler_->total_bytes_acked() < initial_congestion_window_) { // If the connection is not yet out of startup phase, do not decrease the // window. congestion_window_ = congestion_window_ + bytes_acked; @@ -693,7 +696,7 @@ void BbrSender::OnApplicationLimited(QuicByteCount bytes_in_flight) { return; } - sampler_.OnAppLimited(); + sampler_->OnAppLimited(); QUIC_DVLOG(2) << "Becoming application limited. Last sent packet: " << last_sent_packet_ << ", CWND: " << GetCongestionWindow(); } diff --git a/chromium/net/quic/core/congestion_control/bbr_sender.h b/chromium/net/quic/core/congestion_control/bbr_sender.h index 71405ddeeef..c170db6c7a9 100644 --- a/chromium/net/quic/core/congestion_control/bbr_sender.h +++ b/chromium/net/quic/core/congestion_control/bbr_sender.h @@ -98,18 +98,18 @@ class QUIC_EXPORT_PRIVATE BbrSender : public SendAlgorithmInterface { // Start implementation of SendAlgorithmInterface. bool InSlowStart() const override; bool InRecovery() const override; + bool IsProbingForMoreBandwidth() const override; void SetFromConfig(const QuicConfig& config, Perspective perspective) override; - void ResumeConnectionState( - const CachedNetworkParameters& cached_network_params, - bool max_bandwidth_resumption) override; + void AdjustNetworkParameters(QuicBandwidth bandwidth, + QuicTime::Delta rtt) override; void SetNumEmulatedConnections(int num_connections) override {} void OnCongestionEvent(bool rtt_updated, QuicByteCount prior_in_flight, QuicTime event_time, - const CongestionVector& acked_packets, + const AckedPacketVector& acked_packets, const CongestionVector& lost_packets) override; bool OnPacketSent(QuicTime sent_time, QuicByteCount bytes_in_flight, @@ -175,7 +175,7 @@ class QUIC_EXPORT_PRIVATE BbrSender : public SendAlgorithmInterface { // Updates the current bandwidth and min_rtt estimate based on the samples for // the received acknowledgements. Returns true if min_rtt has expired. bool UpdateBandwidthAndMinRtt(QuicTime now, - const CongestionVector& acked_packets); + const AckedPacketVector& acked_packets); // Updates the current gain used in PROBE_BW mode. void UpdateGainCyclePhase(QuicTime now, QuicByteCount prior_in_flight, @@ -217,7 +217,7 @@ class QUIC_EXPORT_PRIVATE BbrSender : public SendAlgorithmInterface { // Bandwidth sampler provides BBR with the bandwidth measurements at // individual points. - BandwidthSampler sampler_; + std::unique_ptr<BandwidthSamplerInterface> sampler_; // The number of the round trips that have occurred during the connection. QuicRoundTripCount round_trip_count_; @@ -243,6 +243,10 @@ class QUIC_EXPORT_PRIVATE BbrSender : public SendAlgorithmInterface { // dropped below the target window. QuicByteCount bytes_acked_since_queue_drained_; + // The muliplier for calculating the max amount of extra CWND to add to + // compensate for ack aggregation. + float max_aggregation_bytes_multiplier_; + // Minimum RTT estimate. Automatically expires within 10 seconds (and // triggers PROBE_RTT mode) if no new value is sampled during that period. QuicTime::Delta min_rtt_; @@ -274,6 +278,9 @@ class QUIC_EXPORT_PRIVATE BbrSender : public SendAlgorithmInterface { const float rtt_variance_weight_; // The number of RTTs to stay in STARTUP mode. Defaults to 3. QuicRoundTripCount num_startup_rtts_; + // If true, exit startup if 1RTT has passed with no bandwidth increase and + // the connection is in recovery. + bool exit_startup_on_loss_; // Number of round-trips in PROBE_BW mode, used for determining the current // pacing gain cycle. diff --git a/chromium/net/quic/core/congestion_control/bbr_sender_test.cc b/chromium/net/quic/core/congestion_control/bbr_sender_test.cc index a3a4d2314db..69e151bfb12 100644 --- a/chromium/net/quic/core/congestion_control/bbr_sender_test.cc +++ b/chromium/net/quic/core/congestion_control/bbr_sender_test.cc @@ -92,6 +92,7 @@ class BbrSenderTest : public QuicTest { {&receiver_, &competing_receiver_}) { // These will be changed by the appropriate tests as necessary. FLAGS_quic_reloadable_flag_quic_bbr_add_tso_cwnd = false; + FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes4 = true; rtt_stats_ = bbr_sender_.connection()->sent_packet_manager().GetRttStats(); sender_ = SetupBbrSender(&bbr_sender_); @@ -239,6 +240,14 @@ class BbrSenderTest : public QuicTest { simulator_.RunFor(wait_time + kTestRtt); ASSERT_EQ(0u, bbr_sender_.bytes_to_transfer()); } + + void SetConnectionOption(QuicTag option) { + QuicConfig config; + QuicTagVector options; + options.push_back(option); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + sender_->SetFromConfig(config, Perspective::IS_SERVER); + } }; // Test a simple long data transfer in the default setup. @@ -292,7 +301,6 @@ TEST_F(BbrSenderTest, SimpleTransferSmallBuffer) { // Test a simple long data transfer with 2 rtts of aggregation. TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes) { - FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes2 = false; FLAGS_quic_reloadable_flag_quic_bbr_add_tso_cwnd = false; CreateDefaultSetup(); // 2 RTTs of aggregation, with a max of 10kb. @@ -318,7 +326,6 @@ TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes) { // Test a simple long data transfer with 2 rtts of aggregation. TEST_F(BbrSenderTest, SimpleTransferAckDecimation) { - FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes2 = false; // Decrease the CWND gain so extra CWND is required with stretch acks. FLAGS_quic_bbr_cwnd_gain = 1.0; sender_ = new BbrSender( @@ -353,73 +360,13 @@ TEST_F(BbrSenderTest, SimpleTransferAckDecimation) { } // Test a simple long data transfer with 2 rtts of aggregation. -TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes2) { - FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes2 = true; +TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes4) { FLAGS_quic_reloadable_flag_quic_bbr_add_tso_cwnd = false; - CreateDefaultSetup(); - // 2 RTTs of aggregation, with a max of 10kb. - EnableAggregation(10 * 1024, 2 * kTestRtt); - - // Transfer 12MB. - DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); - EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); - // It's possible to read a bandwidth as much as 50% too high with aggregation. - EXPECT_LE(kTestLinkBandwidth * 0.99f, - sender_->ExportDebugState().max_bandwidth); - // TODO(ianswett): Tighten this bound once we understand why BBR is - // overestimating bandwidth with aggregation. b/36022633 - EXPECT_GE(kTestLinkBandwidth * 1.5f, - sender_->ExportDebugState().max_bandwidth); - // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures - // bandwidth higher than the link rate. - // The margin here is high, because the aggregation greatly increases - // smoothed rtt. - EXPECT_GE(kTestRtt * 4, rtt_stats_->smoothed_rtt()); - ExpectApproxEq(kTestRtt, rtt_stats_->min_rtt(), 0.33f); -} -// Test a simple long data transfer with 2 rtts of aggregation. -TEST_F(BbrSenderTest, SimpleTransferAckDecimation2) { - FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes2 = true; - // Decrease the CWND gain so extra CWND is required with stretch acks. - FLAGS_quic_bbr_cwnd_gain = 1.0; - sender_ = new BbrSender( - rtt_stats_, - QuicSentPacketManagerPeer::GetUnackedPacketMap( - QuicConnectionPeer::GetSentPacketManager(bbr_sender_.connection())), - kInitialCongestionWindowPackets, kDefaultMaxCongestionWindowPackets, - &random_); - QuicConnectionPeer::SetSendAlgorithm(bbr_sender_.connection(), sender_); - // Enable Ack Decimation on the receiver. - QuicConnectionPeer::SetAckMode(receiver_.connection(), - QuicConnection::AckMode::ACK_DECIMATION); CreateDefaultSetup(); + // Enable ack aggregation that forces the queue to be drained. + SetConnectionOption(kBBR1); - // Transfer 12MB. - DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); - EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); - // It's possible to read a bandwidth as much as 50% too high with aggregation. - EXPECT_LE(kTestLinkBandwidth * 0.99f, - sender_->ExportDebugState().max_bandwidth); - // TODO(ianswett): Tighten this bound once we understand why BBR is - // overestimating bandwidth with aggregation. b/36022633 - EXPECT_GE(kTestLinkBandwidth * 1.5f, - sender_->ExportDebugState().max_bandwidth); - // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures - // bandwidth higher than the link rate. - EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); - // The margin here is high, because the aggregation greatly increases - // smoothed rtt. - EXPECT_GE(kTestRtt * 2, rtt_stats_->smoothed_rtt()); - ExpectApproxEq(kTestRtt, rtt_stats_->min_rtt(), 0.1f); -} - -// Test a simple long data transfer with 2 rtts of aggregation. -TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes3) { - FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes2 = false; - FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes3 = true; - FLAGS_quic_reloadable_flag_quic_bbr_add_tso_cwnd = false; - CreateDefaultSetup(); // 2 RTTs of aggregation, with a max of 10kb. EnableAggregation(10 * 1024, 2 * kTestRtt); @@ -442,9 +389,7 @@ TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes3) { } // Test a simple long data transfer with 2 rtts of aggregation. -TEST_F(BbrSenderTest, SimpleTransferAckDecimation3) { - FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes2 = false; - FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes3 = true; +TEST_F(BbrSenderTest, SimpleTransferAckDecimation4) { // Decrease the CWND gain so extra CWND is required with stretch acks. FLAGS_quic_bbr_cwnd_gain = 1.0; sender_ = new BbrSender( @@ -458,6 +403,8 @@ TEST_F(BbrSenderTest, SimpleTransferAckDecimation3) { QuicConnectionPeer::SetAckMode(receiver_.connection(), QuicConnection::AckMode::ACK_DECIMATION); CreateDefaultSetup(); + // Enable ack aggregation that forces the queue to be drained. + SetConnectionOption(kBBR1); // Transfer 12MB. DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); @@ -727,11 +674,7 @@ TEST_F(BbrSenderTest, NoBandwidthDropOnStartup) { TEST_F(BbrSenderTest, SimpleTransfer1RTTStartup) { CreateDefaultSetup(); - QuicConfig config; - QuicTagVector options; - options.push_back(k1RTT); - QuicConfigPeer::SetReceivedConnectionOptions(&config, options); - sender_->SetFromConfig(config, Perspective::IS_SERVER); + SetConnectionOption(k1RTT); EXPECT_EQ(1u, sender_->num_startup_rtts()); // Run until the full bandwidth is reached and check how many rounds it was. @@ -761,11 +704,7 @@ TEST_F(BbrSenderTest, SimpleTransfer2RTTStartup) { FLAGS_quic_reloadable_flag_quic_bbr_add_tso_cwnd = false; CreateDefaultSetup(); - QuicConfig config; - QuicTagVector options; - options.push_back(k2RTT); - QuicConfigPeer::SetReceivedConnectionOptions(&config, options); - sender_->SetFromConfig(config, Perspective::IS_SERVER); + SetConnectionOption(k2RTT); EXPECT_EQ(2u, sender_->num_startup_rtts()); // Run until the full bandwidth is reached and check how many rounds it was. @@ -789,6 +728,64 @@ TEST_F(BbrSenderTest, SimpleTransfer2RTTStartup) { EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); } +// Test exiting STARTUP earlier upon loss due to the LRTT connection option. +TEST_F(BbrSenderTest, SimpleTransferLRTTStartup) { + FLAGS_quic_reloadable_flag_quic_bbr_exit_startup_on_loss = true; + CreateDefaultSetup(); + + SetConnectionOption(kLRTT); + EXPECT_EQ(3u, sender_->num_startup_rtts()); + + // Run until the full bandwidth is reached and check how many rounds it was. + bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024); + QuicRoundTripCount max_bw_round = 0; + QuicBandwidth max_bw(QuicBandwidth::Zero()); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this, &max_bw, &max_bw_round]() { + if (max_bw < sender_->ExportDebugState().max_bandwidth) { + max_bw = sender_->ExportDebugState().max_bandwidth; + max_bw_round = sender_->ExportDebugState().round_trip_count; + } + return sender_->ExportDebugState().is_at_full_bandwidth; + }, + QuicTime::Delta::FromSeconds(5)); + ASSERT_TRUE(simulator_result); + EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode); + EXPECT_EQ(3u, sender_->ExportDebugState().round_trip_count - max_bw_round); + EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain); + EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); +} + +// Test exiting STARTUP earlier upon loss due to the LRTT connection option. +TEST_F(BbrSenderTest, SimpleTransferLRTTStartupSmallBuffer) { + FLAGS_quic_reloadable_flag_quic_bbr_exit_startup_on_loss = true; + CreateSmallBufferSetup(); + + SetConnectionOption(kLRTT); + EXPECT_EQ(3u, sender_->num_startup_rtts()); + + // Run until the full bandwidth is reached and check how many rounds it was. + bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024); + QuicRoundTripCount max_bw_round = 0; + QuicBandwidth max_bw(QuicBandwidth::Zero()); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this, &max_bw, &max_bw_round]() { + if (max_bw < sender_->ExportDebugState().max_bandwidth) { + max_bw = sender_->ExportDebugState().max_bandwidth; + max_bw_round = sender_->ExportDebugState().round_trip_count; + } + return sender_->ExportDebugState().is_at_full_bandwidth; + }, + QuicTime::Delta::FromSeconds(5)); + ASSERT_TRUE(simulator_result); + EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode); + EXPECT_EQ(2u, sender_->ExportDebugState().round_trip_count - max_bw_round); + EXPECT_EQ(1u, sender_->ExportDebugState().rounds_without_bandwidth_gain); + EXPECT_NE(0u, bbr_sender_.connection()->GetStats().packets_lost); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); +} + // Test that two BBR flows started slightly apart from each other terminate. TEST_F(BbrSenderTest, SimpleCompetition) { const QuicByteCount transfer_size = 10 * 1024 * 1024; @@ -821,11 +818,7 @@ TEST_F(BbrSenderTest, ResumeConnectionState) { FLAGS_quic_reloadable_flag_quic_bbr_bandwidth_resumption = true; CreateDefaultSetup(); - CachedNetworkParameters params; - params.set_bandwidth_estimate_bytes_per_second( - kTestLinkBandwidth.ToBytesPerSecond()); - params.set_min_rtt_ms(kTestRtt.ToMilliseconds()); - sender_->ResumeConnectionState(params, false); + sender_->AdjustNetworkParameters(kTestLinkBandwidth, kTestRtt); EXPECT_EQ(kTestLinkBandwidth, sender_->ExportDebugState().max_bandwidth); EXPECT_EQ(kTestLinkBandwidth, sender_->BandwidthEstimate()); ExpectApproxEq(kTestRtt, sender_->ExportDebugState().min_rtt, 0.01f); diff --git a/chromium/net/quic/core/congestion_control/pacing_sender.cc b/chromium/net/quic/core/congestion_control/pacing_sender.cc index c645b8a8b41..d7b7b55dfda 100644 --- a/chromium/net/quic/core/congestion_control/pacing_sender.cc +++ b/chromium/net/quic/core/congestion_control/pacing_sender.cc @@ -38,7 +38,7 @@ void PacingSender::OnCongestionEvent( bool rtt_updated, QuicByteCount bytes_in_flight, QuicTime event_time, - const SendAlgorithmInterface::CongestionVector& acked_packets, + const SendAlgorithmInterface::AckedPacketVector& acked_packets, const SendAlgorithmInterface::CongestionVector& lost_packets) { DCHECK(sender_ != nullptr); if (!lost_packets.empty()) { diff --git a/chromium/net/quic/core/congestion_control/pacing_sender.h b/chromium/net/quic/core/congestion_control/pacing_sender.h index 65047210b91..c749d3f0962 100644 --- a/chromium/net/quic/core/congestion_control/pacing_sender.h +++ b/chromium/net/quic/core/congestion_control/pacing_sender.h @@ -43,7 +43,7 @@ class QUIC_EXPORT_PRIVATE PacingSender { bool rtt_updated, QuicByteCount bytes_in_flight, QuicTime event_time, - const SendAlgorithmInterface::CongestionVector& acked_packets, + const SendAlgorithmInterface::AckedPacketVector& acked_packets, const SendAlgorithmInterface::CongestionVector& lost_packets); bool OnPacketSent(QuicTime sent_time, diff --git a/chromium/net/quic/core/congestion_control/pacing_sender_test.cc b/chromium/net/quic/core/congestion_control/pacing_sender_test.cc index 174d93da223..a339355260d 100644 --- a/chromium/net/quic/core/congestion_control/pacing_sender_test.cc +++ b/chromium/net/quic/core/congestion_control/pacing_sender_test.cc @@ -13,6 +13,7 @@ #include "net/quic/test_tools/quic_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" +using testing::IsEmpty; using testing::Return; using testing::StrictMock; using testing::_; @@ -47,7 +48,7 @@ class PacingSenderTest : public QuicTest { EXPECT_CALL(*mock_sender_, OnCongestionEvent(_, _, _, _, _)); SendAlgorithmInterface::CongestionVector lost_packets; lost_packets.push_back(std::make_pair(1, kMaxPacketSize)); - SendAlgorithmInterface::CongestionVector empty; + SendAlgorithmInterface::AckedPacketVector empty; pacing_sender_->OnCongestionEvent(true, 1234, clock_.Now(), empty, lost_packets); } else if (burst_size != kInitialBurstPackets) { @@ -103,9 +104,10 @@ class PacingSenderTest : public QuicTest { void UpdateRtt() { EXPECT_CALL(*mock_sender_, OnCongestionEvent(true, kBytesInFlight, _, _, _)); - SendAlgorithmInterface::CongestionVector empty_map; + SendAlgorithmInterface::AckedPacketVector empty_acked; + SendAlgorithmInterface::CongestionVector empty_lost; pacing_sender_->OnCongestionEvent(true, kBytesInFlight, clock_.Now(), - empty_map, empty_map); + empty_acked, empty_lost); } const QuicTime::Delta zero_time_; @@ -320,11 +322,11 @@ TEST_F(PacingSenderTest, NoBurstEnteringRecovery) { // Losing a packet will set clear burst tokens. SendAlgorithmInterface::CongestionVector lost_packets; lost_packets.push_back(std::make_pair(1, kMaxPacketSize)); - SendAlgorithmInterface::CongestionVector empty; - EXPECT_CALL(*mock_sender_, - OnCongestionEvent(true, kMaxPacketSize, _, empty, lost_packets)); - pacing_sender_->OnCongestionEvent(true, kMaxPacketSize, clock_.Now(), empty, - lost_packets); + SendAlgorithmInterface::AckedPacketVector empty_acked; + EXPECT_CALL(*mock_sender_, OnCongestionEvent(true, kMaxPacketSize, _, + IsEmpty(), lost_packets)); + pacing_sender_->OnCongestionEvent(true, kMaxPacketSize, clock_.Now(), + empty_acked, lost_packets); // One packet is sent immediately, because of 1ms pacing granularity. CheckPacketIsSentImmediately(); // Ensure packets are immediately paced. diff --git a/chromium/net/quic/core/congestion_control/send_algorithm_interface.h b/chromium/net/quic/core/congestion_control/send_algorithm_interface.h index 5abb3e94761..f1557817665 100644 --- a/chromium/net/quic/core/congestion_control/send_algorithm_interface.h +++ b/chromium/net/quic/core/congestion_control/send_algorithm_interface.h @@ -29,6 +29,26 @@ const QuicPacketCount kDefaultMaxCongestionWindowPackets = 2000; class QUIC_EXPORT_PRIVATE SendAlgorithmInterface { public: + struct AckedPacket { + AckedPacket(QuicPacketNumber packet_number, + QuicPacketLength bytes_acked, + QuicTime receive_timestamp) + : packet_number(packet_number), + bytes_acked(bytes_acked), + receive_timestamp(receive_timestamp) {} + + QuicPacketNumber packet_number; + // Number of bytes sent in the packet that was acknowledged. + QuicPacketLength bytes_acked; + // The time |packet_number| was received by the peer, according to the + // optional timestamp the peer included in the ACK frame which acknowledged + // |packet_number|. Zero if no timestamp was available for this packet. + QuicTime receive_timestamp; + }; + + // A vector of acked packets. + typedef std::vector<AckedPacket> AckedPacketVector; + // A sorted vector of packets. typedef std::vector<std::pair<QuicPacketNumber, QuicPacketLength>> CongestionVector; @@ -59,7 +79,7 @@ class QUIC_EXPORT_PRIVATE SendAlgorithmInterface { virtual void OnCongestionEvent(bool rtt_updated, QuicByteCount prior_in_flight, QuicTime event_time, - const CongestionVector& acked_packets, + const AckedPacketVector& acked_packets, const CongestionVector& lost_packets) = 0; // Inform that we sent |bytes| to the wire, and if the packet is @@ -104,18 +124,21 @@ class QUIC_EXPORT_PRIVATE SendAlgorithmInterface { // Whether the send algorithm is currently in recovery. virtual bool InRecovery() const = 0; + // True when the congestion control is probing for more bandwidth and needs + // enough data to not be app-limited to do so. + virtual bool IsProbingForMoreBandwidth() const = 0; + // Returns the size of the slow start congestion window in bytes, - // aka ssthresh. Some send algorithms do not define a slow start - // threshold and will return 0. + // aka ssthresh. Only defined for Cubic and Reno, other algorithms return 0. virtual QuicByteCount GetSlowStartThreshold() const = 0; virtual CongestionControlType GetCongestionControlType() const = 0; - // Called by the Session when we get a bandwidth estimate from the client. - // Uses the max bandwidth in the params if |max_bandwidth_resumption| is true. - virtual void ResumeConnectionState( - const CachedNetworkParameters& cached_network_params, - bool max_bandwidth_resumption) = 0; + // Notifies the congestion control algorithm of an external network + // measurement or prediction. Either |bandwidth| or |rtt| may be zero if no + // sample is available. + virtual void AdjustNetworkParameters(QuicBandwidth bandwidth, + QuicTime::Delta rtt) = 0; // Retrieves debugging information about the current state of the // send algorithm. diff --git a/chromium/net/quic/core/congestion_control/tcp_cubic_sender_base.cc b/chromium/net/quic/core/congestion_control/tcp_cubic_sender_base.cc index d56fbf8394c..de05e72d54c 100644 --- a/chromium/net/quic/core/congestion_control/tcp_cubic_sender_base.cc +++ b/chromium/net/quic/core/congestion_control/tcp_cubic_sender_base.cc @@ -21,7 +21,6 @@ namespace { const QuicByteCount kMaxBurstBytes = 3 * kDefaultTCPMSS; const float kRenoBeta = 0.7f; // Reno backoff factor. const uint32_t kDefaultNumConnections = 2; // N-connection emulation. -const float kRateBasedExtraCwnd = 1.5f; // CWND for rate based sending. } // namespace TcpCubicSenderBase::TcpCubicSenderBase(const QuicClock* clock, @@ -38,7 +37,6 @@ TcpCubicSenderBase::TcpCubicSenderBase(const QuicClock* clock, min4_mode_(false), last_cutback_exited_slowstart_(false), slow_start_large_reduction_(false), - rate_based_sending_(false), no_prr_(false) {} TcpCubicSenderBase::~TcpCubicSenderBase() {} @@ -87,23 +85,14 @@ void TcpCubicSenderBase::SetFromConfig(const QuicConfig& config, // Use unity pacing instead of PRR. no_prr_ = true; } - if (config.HasReceivedConnectionOptions() && - ContainsQuicTag(config.ReceivedConnectionOptions(), kRATE)) { - // Rate based sending experiment - rate_based_sending_ = true; - } } } -void TcpCubicSenderBase::ResumeConnectionState( - const CachedNetworkParameters& cached_network_params, - bool max_bandwidth_resumption) { - QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond( - max_bandwidth_resumption - ? cached_network_params.max_bandwidth_estimate_bytes_per_second() - : cached_network_params.bandwidth_estimate_bytes_per_second()); - QuicTime::Delta rtt = - QuicTime::Delta::FromMilliseconds(cached_network_params.min_rtt_ms()); +void TcpCubicSenderBase::AdjustNetworkParameters(QuicBandwidth bandwidth, + QuicTime::Delta rtt) { + if (bandwidth.IsZero() || rtt.IsZero()) { + return; + } SetCongestionWindowFromBandwidthAndRtt(bandwidth, rtt); } @@ -124,7 +113,7 @@ void TcpCubicSenderBase::OnCongestionEvent( bool rtt_updated, QuicByteCount prior_in_flight, QuicTime event_time, - const CongestionVector& acked_packets, + const AckedPacketVector& acked_packets, const CongestionVector& lost_packets) { if (rtt_updated && InSlowStart() && hybrid_slow_start_.ShouldExitSlowStart( @@ -136,9 +125,9 @@ void TcpCubicSenderBase::OnCongestionEvent( it != lost_packets.end(); ++it) { OnPacketLost(it->first, it->second, prior_in_flight); } - for (CongestionVector::const_iterator it = acked_packets.begin(); - it != acked_packets.end(); ++it) { - OnPacketAcked(it->first, it->second, prior_in_flight, event_time); + for (const SendAlgorithmInterface::AckedPacket acked_packet : acked_packets) { + OnPacketAcked(acked_packet.packet_number, acked_packet.bytes_acked, + prior_in_flight, event_time); } } @@ -200,15 +189,11 @@ QuicTime::Delta TcpCubicSenderBase::TimeUntilSend( if (min4_mode_ && bytes_in_flight < 4 * kDefaultTCPMSS) { return QuicTime::Delta::Zero(); } - if (rate_based_sending_ && - GetCongestionWindow() * kRateBasedExtraCwnd > bytes_in_flight) { - return QuicTime::Delta::Zero(); - } return QuicTime::Delta::Infinite(); } QuicBandwidth TcpCubicSenderBase::PacingRate( - QuicByteCount bytes_in_flight) const { + QuicByteCount /* bytes_in_flight */) const { // We pace at twice the rate of the underlying sender's bandwidth estimate // during slow start and 1.25x during congestion avoidance to ensure pacing // doesn't prevent us from filling the window. @@ -218,11 +203,6 @@ QuicBandwidth TcpCubicSenderBase::PacingRate( } const QuicBandwidth bandwidth = QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(), srtt); - if (rate_based_sending_ && bytes_in_flight > GetCongestionWindow()) { - // Rate based sending allows sending more than CWND, but reduces the pacing - // rate when the bytes in flight is more than the CWND to 75% of bandwidth. - return 0.75 * bandwidth; - } return bandwidth * (InSlowStart() ? 2 : (no_prr_ && InRecovery() ? 1 : 1.25)); } @@ -255,6 +235,10 @@ bool TcpCubicSenderBase::InRecovery() const { largest_acked_packet_number_ != 0; } +bool TcpCubicSenderBase::IsProbingForMoreBandwidth() const { + return false; +} + void TcpCubicSenderBase::OnRetransmissionTimeout(bool packets_retransmitted) { largest_sent_at_last_cutback_ = 0; if (!packets_retransmitted) { diff --git a/chromium/net/quic/core/congestion_control/tcp_cubic_sender_base.h b/chromium/net/quic/core/congestion_control/tcp_cubic_sender_base.h index 2488bf2536c..e60f782ab64 100644 --- a/chromium/net/quic/core/congestion_control/tcp_cubic_sender_base.h +++ b/chromium/net/quic/core/congestion_control/tcp_cubic_sender_base.h @@ -45,14 +45,13 @@ class QUIC_EXPORT_PRIVATE TcpCubicSenderBase : public SendAlgorithmInterface { // Start implementation of SendAlgorithmInterface. void SetFromConfig(const QuicConfig& config, Perspective perspective) override; - void ResumeConnectionState( - const CachedNetworkParameters& cached_network_params, - bool max_bandwidth_resumption) override; + void AdjustNetworkParameters(QuicBandwidth bandwidth, + QuicTime::Delta rtt) override; void SetNumEmulatedConnections(int num_connections) override; void OnCongestionEvent(bool rtt_updated, QuicByteCount prior_in_flight, QuicTime event_time, - const CongestionVector& acked_packets, + const AckedPacketVector& acked_packets, const CongestionVector& lost_packets) override; bool OnPacketSent(QuicTime sent_time, QuicByteCount bytes_in_flight, @@ -67,6 +66,7 @@ class QUIC_EXPORT_PRIVATE TcpCubicSenderBase : public SendAlgorithmInterface { QuicBandwidth BandwidthEstimate() const override; bool InSlowStart() const override; bool InRecovery() const override; + bool IsProbingForMoreBandwidth() const override; std::string GetDebugState() const override; void OnApplicationLimited(QuicByteCount bytes_in_flight) override; @@ -148,9 +148,6 @@ class QUIC_EXPORT_PRIVATE TcpCubicSenderBase : public SendAlgorithmInterface { // When true, exit slow start with large cutback of congestion window. bool slow_start_large_reduction_; - // When true, use rate based sending instead of only sending if there's CWND. - bool rate_based_sending_; - // When true, use unity pacing instead of PRR. bool no_prr_; diff --git a/chromium/net/quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc b/chromium/net/quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc index b35cb2b7dbb..9798de95e6c 100644 --- a/chromium/net/quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc +++ b/chromium/net/quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc @@ -84,12 +84,12 @@ class TcpCubicSenderBytesTest : public QuicTest { void AckNPackets(int n) { sender_->rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(60), QuicTime::Delta::Zero(), clock_.Now()); - SendAlgorithmInterface::CongestionVector acked_packets; + SendAlgorithmInterface::AckedPacketVector acked_packets; SendAlgorithmInterface::CongestionVector lost_packets; for (int i = 0; i < n; ++i) { ++acked_packet_number_; - acked_packets.push_back( - std::make_pair(acked_packet_number_, kDefaultTCPMSS)); + acked_packets.push_back(SendAlgorithmInterface::AckedPacket( + acked_packet_number_, kDefaultTCPMSS, QuicTime::Zero())); } sender_->OnCongestionEvent(true, bytes_in_flight_, clock_.Now(), acked_packets, lost_packets); @@ -100,7 +100,7 @@ class TcpCubicSenderBytesTest : public QuicTest { void LoseNPackets(int n) { LoseNPackets(n, kDefaultTCPMSS); } void LoseNPackets(int n, QuicPacketLength packet_length) { - SendAlgorithmInterface::CongestionVector acked_packets; + SendAlgorithmInterface::AckedPacketVector acked_packets; SendAlgorithmInterface::CongestionVector lost_packets; for (int i = 0; i < n; ++i) { ++acked_packet_number_; @@ -114,7 +114,7 @@ class TcpCubicSenderBytesTest : public QuicTest { // Does not increment acked_packet_number_. void LosePacket(QuicPacketNumber packet_number) { - SendAlgorithmInterface::CongestionVector acked_packets; + SendAlgorithmInterface::AckedPacketVector acked_packets; SendAlgorithmInterface::CongestionVector lost_packets; lost_packets.push_back(std::make_pair(packet_number, kDefaultTCPMSS)); sender_->OnCongestionEvent(false, bytes_in_flight_, clock_.Now(), @@ -675,40 +675,27 @@ TEST_F(TcpCubicSenderBytesTest, 1ConnectionCongestionAvoidanceAtEndOfRecovery) { TEST_F(TcpCubicSenderBytesTest, BandwidthResumption) { // Test that when provided with CachedNetworkParameters and opted in to the - // bandwidth resumption experiment, that the TcpCubicSender sets initial CWND - // appropriately. + // bandwidth resumption experiment, that the TcpCubicSenderPackets sets + // initial CWND appropriately. // Set some common values. - CachedNetworkParameters cached_network_params; const QuicPacketCount kNumberOfPackets = 123; - const int kBandwidthEstimateBytesPerSecond = - kNumberOfPackets * kDefaultTCPMSS; - cached_network_params.set_bandwidth_estimate_bytes_per_second( - kBandwidthEstimateBytesPerSecond); - cached_network_params.set_min_rtt_ms(1000); - - // Make sure that a bandwidth estimate results in a changed CWND. - cached_network_params.set_timestamp(clock_.WallNow().ToUNIXSeconds() - - (kNumSecondsPerHour - 1)); - sender_->ResumeConnectionState(cached_network_params, false); + const QuicBandwidth kBandwidthEstimate = + QuicBandwidth::FromBytesPerSecond(kNumberOfPackets * kDefaultTCPMSS); + const QuicTime::Delta kRttEstimate = QuicTime::Delta::FromSeconds(1); + sender_->AdjustNetworkParameters(kBandwidthEstimate, kRttEstimate); EXPECT_EQ(kNumberOfPackets * kDefaultTCPMSS, sender_->GetCongestionWindow()); - // Resumed CWND is limited to be in a sensible range. - cached_network_params.set_bandwidth_estimate_bytes_per_second( - (kMaxCongestionWindowPackets + 1) * kDefaultTCPMSS); - sender_->ResumeConnectionState(cached_network_params, false); - EXPECT_EQ(kMaxCongestionWindowPackets * kDefaultTCPMSS, - sender_->GetCongestionWindow()); - - // Resume with an illegal value of 0 and verify the server uses 1 instead. - cached_network_params.set_bandwidth_estimate_bytes_per_second(0); - sender_->ResumeConnectionState(cached_network_params, false); - EXPECT_EQ(sender_->min_congestion_window(), sender_->GetCongestionWindow()); + // Resume with an illegal value of 0 and verify the server ignores it. + sender_->AdjustNetworkParameters(QuicBandwidth::Zero(), kRttEstimate); + EXPECT_EQ(kNumberOfPackets * kDefaultTCPMSS, sender_->GetCongestionWindow()); - // Resume to the max value. - cached_network_params.set_max_bandwidth_estimate_bytes_per_second( - kMaxCongestionWindowPackets * kDefaultTCPMSS); - sender_->ResumeConnectionState(cached_network_params, true); + // Resumed CWND is limited to be in a sensible range. + const QuicBandwidth kUnreasonableBandwidth = + QuicBandwidth::FromBytesPerSecond((kMaxCongestionWindowPackets + 1) * + kDefaultTCPMSS); + sender_->AdjustNetworkParameters(kUnreasonableBandwidth, + QuicTime::Delta::FromSeconds(1)); EXPECT_EQ(kMaxCongestionWindowPackets * kDefaultTCPMSS, sender_->GetCongestionWindow()); } @@ -764,38 +751,6 @@ TEST_F(TcpCubicSenderBytesTest, NoPRR) { sender_->PacingRate(kRenoBeta * kDefaultWindowTCP)); } -TEST_F(TcpCubicSenderBytesTest, PaceSlowerAboveCwnd) { - QuicTime::Delta rtt(QuicTime::Delta::FromMilliseconds(60)); - sender_->rtt_stats_.UpdateRtt(rtt, QuicTime::Delta::Zero(), clock_.Now()); - - QuicConfig config; - QuicTagVector options; - options.push_back(kRATE); - QuicConfigPeer::SetReceivedConnectionOptions(&config, options); - sender_->SetFromConfig(config, Perspective::IS_SERVER); - EXPECT_EQ(10 * kDefaultTCPMSS, sender_->GetCongestionWindow()); - sender_->SetNumEmulatedConnections(1); - // Lose a packet to exit slow start. - LoseNPackets(1); - const QuicPacketCount cwnd = 7; - EXPECT_EQ(cwnd * kDefaultTCPMSS, sender_->GetCongestionWindow()); - - EXPECT_TRUE( - sender_->TimeUntilSend(QuicTime::Zero(), kDefaultTCPMSS).IsZero()); - EXPECT_EQ( - sender_->PacingRate(kDefaultTCPMSS), - QuicBandwidth::FromBytesAndTimeDelta(7 * kDefaultTCPMSS, rtt) * 1.25); - for (QuicPacketCount i = cwnd + 1; i < 1.5 * cwnd; ++i) { - EXPECT_TRUE( - sender_->TimeUntilSend(QuicTime::Zero(), i * kDefaultTCPMSS).IsZero()); - EXPECT_EQ(sender_->PacingRate(i * kDefaultTCPMSS), - QuicBandwidth::FromBytesAndTimeDelta(cwnd * kDefaultTCPMSS, rtt) * - 0.75); - } - EXPECT_FALSE( - sender_->TimeUntilSend(QuicTime::Zero(), 11 * kDefaultTCPMSS).IsZero()); -} - TEST_F(TcpCubicSenderBytesTest, ResetAfterConnectionMigration) { // Starts from slow start. sender_->SetNumEmulatedConnections(1); @@ -834,11 +789,12 @@ TEST_F(TcpCubicSenderBytesTest, DefaultMaxCwnd) { &clock_, &rtt_stats, /*unacked_packets=*/nullptr, kCubicBytes, QuicRandom::GetInstance(), &stats, kInitialCongestionWindow)); - SendAlgorithmInterface::CongestionVector acked_packets; + SendAlgorithmInterface::AckedPacketVector acked_packets; SendAlgorithmInterface::CongestionVector missing_packets; for (uint64_t i = 1; i < kDefaultMaxCongestionWindowPackets; ++i) { acked_packets.clear(); - acked_packets.push_back(std::make_pair(i, 1350)); + acked_packets.push_back( + SendAlgorithmInterface::AckedPacket(i, 1350, QuicTime::Zero())); sender->OnCongestionEvent(true, sender->GetCongestionWindow(), clock_.Now(), acked_packets, missing_packets); } diff --git a/chromium/net/quic/core/congestion_control/tcp_cubic_sender_packets_test.cc b/chromium/net/quic/core/congestion_control/tcp_cubic_sender_packets_test.cc index 66afc3fd4ed..d7807c9ba07 100644 --- a/chromium/net/quic/core/congestion_control/tcp_cubic_sender_packets_test.cc +++ b/chromium/net/quic/core/congestion_control/tcp_cubic_sender_packets_test.cc @@ -93,12 +93,12 @@ class TcpCubicSenderPacketsTest : public QuicTest { void AckNPackets(int n) { sender_->rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(60), QuicTime::Delta::Zero(), clock_.Now()); - SendAlgorithmInterface::CongestionVector acked_packets; + SendAlgorithmInterface::AckedPacketVector acked_packets; SendAlgorithmInterface::CongestionVector lost_packets; for (int i = 0; i < n; ++i) { ++acked_packet_number_; - acked_packets.push_back( - std::make_pair(acked_packet_number_, kDefaultTCPMSS)); + acked_packets.push_back(SendAlgorithmInterface::AckedPacket( + acked_packet_number_, kDefaultTCPMSS, QuicTime::Zero())); } sender_->OnCongestionEvent(true, bytes_in_flight_, clock_.Now(), acked_packets, lost_packets); @@ -109,7 +109,7 @@ class TcpCubicSenderPacketsTest : public QuicTest { void LoseNPackets(int n) { LoseNPackets(n, kDefaultTCPMSS); } void LoseNPackets(int n, QuicPacketLength packet_length) { - SendAlgorithmInterface::CongestionVector acked_packets; + SendAlgorithmInterface::AckedPacketVector acked_packets; SendAlgorithmInterface::CongestionVector lost_packets; for (int i = 0; i < n; ++i) { ++acked_packet_number_; @@ -123,7 +123,7 @@ class TcpCubicSenderPacketsTest : public QuicTest { // Does not increment acked_packet_number_. void LosePacket(QuicPacketNumber packet_number) { - SendAlgorithmInterface::CongestionVector acked_packets; + SendAlgorithmInterface::AckedPacketVector acked_packets; SendAlgorithmInterface::CongestionVector lost_packets; lost_packets.push_back(std::make_pair(packet_number, kDefaultTCPMSS)); sender_->OnCongestionEvent(false, bytes_in_flight_, clock_.Now(), @@ -783,37 +783,24 @@ TEST_F(TcpCubicSenderPacketsTest, BandwidthResumption) { // initial CWND appropriately. // Set some common values. - CachedNetworkParameters cached_network_params; const QuicPacketCount kNumberOfPackets = 123; - const int kBandwidthEstimateBytesPerSecond = - kNumberOfPackets * kDefaultTCPMSS; - cached_network_params.set_bandwidth_estimate_bytes_per_second( - kBandwidthEstimateBytesPerSecond); - cached_network_params.set_min_rtt_ms(1000); - - // Make sure that a bandwidth estimate results in a changed CWND. - cached_network_params.set_timestamp(clock_.WallNow().ToUNIXSeconds() - - (kNumSecondsPerHour - 1)); - sender_->ResumeConnectionState(cached_network_params, false); + const QuicBandwidth kBandwidthEstimate = + QuicBandwidth::FromBytesPerSecond(kNumberOfPackets * kDefaultTCPMSS); + const QuicTime::Delta kRttEstimate = QuicTime::Delta::FromSeconds(1); + sender_->AdjustNetworkParameters(kBandwidthEstimate, kRttEstimate); + EXPECT_EQ(kNumberOfPackets, sender_->congestion_window()); + + // Resume with an illegal value of 0 and verify the server ignores it. + sender_->AdjustNetworkParameters(QuicBandwidth::Zero(), kRttEstimate); EXPECT_EQ(kNumberOfPackets, sender_->congestion_window()); // Resumed CWND is limited to be in a sensible range. - cached_network_params.set_bandwidth_estimate_bytes_per_second( - (kMaxCongestionWindowPackets + 1) * kDefaultTCPMSS); - sender_->ResumeConnectionState(cached_network_params, false); + const QuicBandwidth kUnreasonableBandwidth = + QuicBandwidth::FromBytesPerSecond((kMaxCongestionWindowPackets + 1) * + kDefaultTCPMSS); + sender_->AdjustNetworkParameters(kUnreasonableBandwidth, + QuicTime::Delta::FromSeconds(1)); EXPECT_EQ(kMaxCongestionWindowPackets, sender_->congestion_window()); - - // Resume with an illegal value of 0 and verify the server uses 1 instead. - cached_network_params.set_bandwidth_estimate_bytes_per_second(0); - sender_->ResumeConnectionState(cached_network_params, false); - EXPECT_EQ(sender_->min_congestion_window(), sender_->congestion_window()); - - // Resume to the max value. - cached_network_params.set_max_bandwidth_estimate_bytes_per_second( - kMaxCongestionWindowPackets * kDefaultTCPMSS); - sender_->ResumeConnectionState(cached_network_params, true); - EXPECT_EQ(kMaxCongestionWindowPackets * kDefaultTCPMSS, - sender_->GetCongestionWindow()); } TEST_F(TcpCubicSenderPacketsTest, PaceBelowCWND) { @@ -867,38 +854,6 @@ TEST_F(TcpCubicSenderPacketsTest, NoPRR) { sender_->PacingRate(kRenoBeta * kDefaultWindowTCP)); } -TEST_F(TcpCubicSenderPacketsTest, PaceSlowerAboveCwnd) { - QuicTime::Delta rtt(QuicTime::Delta::FromMilliseconds(60)); - sender_->rtt_stats_.UpdateRtt(rtt, QuicTime::Delta::Zero(), clock_.Now()); - - QuicConfig config; - QuicTagVector options; - options.push_back(kRATE); - QuicConfigPeer::SetReceivedConnectionOptions(&config, options); - sender_->SetFromConfig(config, Perspective::IS_SERVER); - EXPECT_EQ(10u, sender_->congestion_window()); - sender_->SetNumEmulatedConnections(1); - // Lose a packet to exit slow start. - LoseNPackets(1); - const QuicPacketCount cwnd = 7; - EXPECT_EQ(cwnd * kDefaultTCPMSS, sender_->GetCongestionWindow()); - - EXPECT_TRUE( - sender_->TimeUntilSend(QuicTime::Zero(), kDefaultTCPMSS).IsZero()); - EXPECT_EQ( - sender_->PacingRate(kDefaultTCPMSS), - QuicBandwidth::FromBytesAndTimeDelta(7 * kDefaultTCPMSS, rtt) * 1.25); - for (QuicPacketCount i = cwnd + 1; i < 1.5 * cwnd; ++i) { - EXPECT_TRUE( - sender_->TimeUntilSend(QuicTime::Zero(), i * kDefaultTCPMSS).IsZero()); - EXPECT_EQ(sender_->PacingRate(i * kDefaultTCPMSS), - QuicBandwidth::FromBytesAndTimeDelta(cwnd * kDefaultTCPMSS, rtt) * - 0.75); - } - EXPECT_FALSE( - sender_->TimeUntilSend(QuicTime::Zero(), 11 * kDefaultTCPMSS).IsZero()); -} - TEST_F(TcpCubicSenderPacketsTest, ResetAfterConnectionMigration) { EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); EXPECT_EQ(kMaxCongestionWindowPackets, sender_->slowstart_threshold()); @@ -940,11 +895,12 @@ TEST_F(TcpCubicSenderPacketsTest, DefaultMaxCwnd) { &clock_, &rtt_stats, /*unacked_packets=*/nullptr, kCubic, QuicRandom::GetInstance(), &stats, kInitialCongestionWindow)); - SendAlgorithmInterface::CongestionVector acked_packets; + SendAlgorithmInterface::AckedPacketVector acked_packets; SendAlgorithmInterface::CongestionVector missing_packets; for (uint64_t i = 1; i < kDefaultMaxCongestionWindowPackets; ++i) { acked_packets.clear(); - acked_packets.push_back(std::make_pair(i, 1350)); + acked_packets.push_back( + SendAlgorithmInterface::AckedPacket(i, 1350, QuicTime::Zero())); sender->OnCongestionEvent(true, sender->GetCongestionWindow(), clock_.Now(), acked_packets, missing_packets); } diff --git a/chromium/net/quic/core/crypto/crypto_framer.h b/chromium/net/quic/core/crypto/crypto_framer.h index 3daf862ef13..9ff70361959 100644 --- a/chromium/net/quic/core/crypto/crypto_framer.h +++ b/chromium/net/quic/core/crypto/crypto_framer.h @@ -12,6 +12,7 @@ #include <vector> #include "net/quic/core/crypto/crypto_handshake_message.h" +#include "net/quic/core/crypto/crypto_message_parser.h" #include "net/quic/core/quic_packets.h" #include "net/quic/platform/api/quic_export.h" @@ -32,22 +33,6 @@ class QUIC_EXPORT_PRIVATE CryptoFramerVisitorInterface { virtual void OnHandshakeMessage(const CryptoHandshakeMessage& message) = 0; }; -class QUIC_EXPORT_PRIVATE CryptoMessageParser { - public: - virtual ~CryptoMessageParser() {} - - virtual QuicErrorCode error() const = 0; - virtual const std::string& error_detail() const = 0; - - // Processes input data, which must be delivered in order. Returns - // false if there was an error, and true otherwise. - virtual bool ProcessInput(QuicStringPiece input, Perspective perspective) = 0; - - // Returns the number of bytes of buffered input data remaining to be - // parsed. - virtual size_t InputBytesRemaining() const = 0; -}; - // A class for framing the crypto messages that are exchanged in a QUIC // session. class QUIC_EXPORT_PRIVATE CryptoFramer : public CryptoMessageParser { diff --git a/chromium/net/quic/core/crypto/crypto_framer_test.cc b/chromium/net/quic/core/crypto/crypto_framer_test.cc index 846f91f4be1..9d61aa4ecf1 100644 --- a/chromium/net/quic/core/crypto/crypto_framer_test.cc +++ b/chromium/net/quic/core/crypto/crypto_framer_test.cc @@ -47,6 +47,11 @@ class TestCryptoVisitor : public CryptoFramerVisitorInterface { std::vector<CryptoHandshakeMessage> messages_; }; +INSTANTIATE_TEST_CASE_P(Tests, + CryptoFramerTest, + ::testing::ValuesIn({Perspective::IS_CLIENT, + Perspective::IS_SERVER})); + TEST_P(CryptoFramerTest, ConstructHandshakeMessage) { CryptoHandshakeMessage message; message.set_tag(0xFFAA7733); diff --git a/chromium/net/quic/core/crypto/crypto_handshake.cc b/chromium/net/quic/core/crypto/crypto_handshake.cc index f1d8998ca06..a8313c27eec 100644 --- a/chromium/net/quic/core/crypto/crypto_handshake.cc +++ b/chromium/net/quic/core/crypto/crypto_handshake.cc @@ -15,8 +15,6 @@ QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters() : key_exchange(0), aead(0), token_binding_key_param(0), - x509_ecdsa_supported(false), - x509_supported(false), sct_supported_by_client(false) {} QuicCryptoNegotiatedParameters::~QuicCryptoNegotiatedParameters() {} diff --git a/chromium/net/quic/core/crypto/crypto_handshake.h b/chromium/net/quic/core/crypto/crypto_handshake.h index b646c58e693..cef859be08b 100644 --- a/chromium/net/quic/core/crypto/crypto_handshake.h +++ b/chromium/net/quic/core/crypto/crypto_handshake.h @@ -137,8 +137,6 @@ struct QUIC_EXPORT_PRIVATE QuicCryptoNegotiatedParameters QuicTag token_binding_key_param; // Used when generating proof signature when sending server config updates. - bool x509_ecdsa_supported; - bool x509_supported; // Used to generate cert chain when sending server config updates. std::string client_common_set_hashes; diff --git a/chromium/net/quic/core/crypto/crypto_handshake_message_test.cc b/chromium/net/quic/core/crypto/crypto_handshake_message_test.cc index a44e64af5c0..1f5660b939a 100644 --- a/chromium/net/quic/core/crypto/crypto_handshake_message_test.cc +++ b/chromium/net/quic/core/crypto/crypto_handshake_message_test.cc @@ -6,6 +6,7 @@ #include "net/quic/core/crypto/crypto_handshake.h" #include "net/quic/core/crypto/crypto_protocol.h" +#include "net/quic/platform/api/quic_endian.h" #include "net/quic/platform/api/quic_test.h" namespace net { @@ -14,6 +15,11 @@ namespace { class CryptoHandshakeMessageTest : public QuicTestWithParam<Perspective> {}; +INSTANTIATE_TEST_CASE_P(Perspective, + CryptoHandshakeMessageTest, + ::testing::ValuesIn({Perspective::IS_CLIENT, + Perspective::IS_SERVER})); + TEST_P(CryptoHandshakeMessageTest, DebugString) { const char* str = "SHLO<\n>"; @@ -99,7 +105,8 @@ TEST_P(CryptoHandshakeMessageTest, ServerDesignatedConnectionId) { CryptoHandshakeMessage message; message.set_tag(kSREJ); - message.SetValue(kRCID, UINT64_C(18364758544493064720)); + message.SetValue(kRCID, + QuicEndian::NetToHost64(UINT64_C(18364758544493064720))); EXPECT_EQ(str, message.DebugString(GetParam())); // Test copy diff --git a/chromium/net/quic/core/crypto/crypto_message_parser.h b/chromium/net/quic/core/crypto/crypto_message_parser.h new file mode 100644 index 00000000000..3f08b351aca --- /dev/null +++ b/chromium/net/quic/core/crypto/crypto_message_parser.h @@ -0,0 +1,32 @@ +// 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_CORE_CRYPTO_CRYPTO_MESSAGE_PARSER_H_ +#define NET_QUIC_CORE_CRYPTO_CRYPTO_MESSAGE_PARSER_H_ + +#include "net/quic/core/quic_error_codes.h" +#include "net/quic/core/quic_types.h" +#include "net/quic/platform/api/quic_string_piece.h" + +namespace net { + +class QUIC_EXPORT_PRIVATE CryptoMessageParser { + public: + virtual ~CryptoMessageParser() {} + + virtual QuicErrorCode error() const = 0; + virtual const std::string& error_detail() const = 0; + + // Processes input data, which must be delivered in order. Returns + // false if there was an error, and true otherwise. + virtual bool ProcessInput(QuicStringPiece input, Perspective perspective) = 0; + + // Returns the number of bytes of buffered input data remaining to be + // parsed. + virtual size_t InputBytesRemaining() const = 0; +}; + +} // namespace net + +#endif // NET_QUIC_CORE_CRYPTO_CRYPTO_MESSAGE_PARSER_H_ diff --git a/chromium/net/quic/core/crypto/crypto_protocol.h b/chromium/net/quic/core/crypto/crypto_protocol.h index 56d8e63844c..1a54cd8e76a 100644 --- a/chromium/net/quic/core/crypto/crypto_protocol.h +++ b/chromium/net/quic/core/crypto/crypto_protocol.h @@ -82,12 +82,14 @@ const QuicTag kIFWA = TAG('I', 'F', 'W', 'a'); // Set initial size const QuicTag kTBBR = TAG('T', 'B', 'B', 'R'); // Reduced Buffer Bloat TCP const QuicTag k1RTT = TAG('1', 'R', 'T', 'T'); // STARTUP in BBR for 1 RTT const QuicTag k2RTT = TAG('2', 'R', 'T', 'T'); // STARTUP in BBR for 2 RTTs +const QuicTag kLRTT = TAG('L', 'R', 'T', 'T'); // Exit STARTUP in BBR on loss const QuicTag kBBRR = TAG('B', 'B', 'R', 'R'); // Rate-based recovery in BBR +const QuicTag kBBR1 = TAG('B', 'B', 'R', '1'); // Ack aggregatation v1 +const QuicTag kBBR2 = TAG('B', 'B', 'R', '2'); // Ack aggregatation v2 const QuicTag kRENO = TAG('R', 'E', 'N', 'O'); // Reno Congestion Control const QuicTag kTPCC = TAG('P', 'C', 'C', '\0'); // Performance-Oriented // Congestion Control const QuicTag kBYTE = TAG('B', 'Y', 'T', 'E'); // TCP cubic or reno in bytes -const QuicTag kRATE = TAG('R', 'A', 'T', 'E'); // TCP cubic rate based sending const QuicTag kIW03 = TAG('I', 'W', '0', '3'); // Force ICWND to 3 const QuicTag kIW10 = TAG('I', 'W', '1', '0'); // Force ICWND to 10 const QuicTag kIW20 = TAG('I', 'W', '2', '0'); // Force ICWND to 20 @@ -96,8 +98,6 @@ const QuicTag k1CON = TAG('1', 'C', 'O', 'N'); // Emulate a single connection const QuicTag kNTLP = TAG('N', 'T', 'L', 'P'); // No tail loss probe const QuicTag kNCON = TAG('N', 'C', 'O', 'N'); // N Connection Congestion Ctrl const QuicTag kNRTO = TAG('N', 'R', 'T', 'O'); // CWND reduction on loss -const QuicTag kUNDO = TAG('U', 'N', 'D', 'O'); // Undo any pending retransmits - // if they're likely spurious. const QuicTag kTIME = TAG('T', 'I', 'M', 'E'); // Time based loss detection const QuicTag kATIM = TAG('A', 'T', 'I', 'M'); // Adaptive time loss detection const QuicTag kMIN1 = TAG('M', 'I', 'N', '1'); // Min CWND of 1 packet @@ -112,6 +112,8 @@ const QuicTag kAKD3 = TAG('A', 'K', 'D', '3'); // Ack decimation style acking // with 1/8 RTT acks. const QuicTag kAKD4 = TAG('A', 'K', 'D', '4'); // Ack decimation with 1/8 RTT // tolerating out of order. +const QuicTag kAKDU = TAG('A', 'K', 'D', 'U'); // Unlimited number of packets + // receieved before acking const QuicTag kSSLR = TAG('S', 'S', 'L', 'R'); // Slow Start Large Reduction. const QuicTag kNPRR = TAG('N', 'P', 'R', 'R'); // Pace at unity instead of PRR const QuicTag k5RTO = TAG('5', 'R', 'T', 'O'); // Close connection on 5 RTOs diff --git a/chromium/net/quic/core/crypto/crypto_server_config_protobuf.h b/chromium/net/quic/core/crypto/crypto_server_config_protobuf.h index fe0cc4eaa4f..0077f3b2102 100644 --- a/chromium/net/quic/core/crypto/crypto_server_config_protobuf.h +++ b/chromium/net/quic/core/crypto/crypto_server_config_protobuf.h @@ -56,7 +56,7 @@ class QUIC_EXPORT_PRIVATE QuicServerConfigProtobuf { void set_config(QuicStringPiece config) { config.CopyToString(&config_); } QuicServerConfigProtobuf::PrivateKey* add_key() { - keys_.push_back(base::MakeUnique<PrivateKey>()); + keys_.push_back(std::make_unique<PrivateKey>()); return keys_.back().get(); } diff --git a/chromium/net/quic/core/crypto/proof_source.h b/chromium/net/quic/core/crypto/proof_source.h index eb5d70bb920..d44591c03c6 100644 --- a/chromium/net/quic/core/crypto/proof_source.h +++ b/chromium/net/quic/core/crypto/proof_source.h @@ -74,6 +74,25 @@ class QUIC_EXPORT_PRIVATE ProofSource { Callback& operator=(const Callback&) = delete; }; + // Base class for signalling the completion of a call to ComputeTlsSignature. + class SignatureCallback { + public: + SignatureCallback() {} + virtual ~SignatureCallback() = default; + + // Invoked upon completion of ComputeTlsSignature. + // + // |ok| indicates whether the operation completed successfully. + // + // |signature| contains the signature of the data provided to + // ComputeTlsSignature. Its value is undefined if |ok| is false. + virtual void Run(bool ok, std::string signature) = 0; + + private: + SignatureCallback(const SignatureCallback&) = delete; + SignatureCallback& operator=(const SignatureCallback&) = delete; + }; + virtual ~ProofSource() {} // GetProof finds a certificate chain for |hostname| (in leaf-first order), @@ -101,6 +120,24 @@ class QUIC_EXPORT_PRIVATE ProofSource { QuicStringPiece chlo_hash, const QuicTagVector& connection_options, std::unique_ptr<Callback> callback) = 0; + + // Returns the certificate chain for |hostname| in leaf-first order. + virtual QuicReferenceCountedPointer<Chain> GetCertChain( + const QuicSocketAddress& server_address, + const std::string& hostname) = 0; + + // Computes a signature using the private key of the certificate for + // |hostname|. The value in |in| is signed using the algorithm specified by + // |signature_algorithm|, which is an |SSL_SIGN_*| value (as defined in TLS + // 1.3). + // + // Callers should expect that |callback| might be invoked synchronously. + virtual void ComputeTlsSignature( + const QuicSocketAddress& server_address, + const std::string& hostname, + uint16_t signature_algorithm, + QuicStringPiece in, + std::unique_ptr<SignatureCallback> callback) = 0; }; } // namespace net diff --git a/chromium/net/quic/core/frames/quic_ack_frame.cc b/chromium/net/quic/core/frames/quic_ack_frame.cc index 77016b9d386..c9c4d2eead5 100644 --- a/chromium/net/quic/core/frames/quic_ack_frame.cc +++ b/chromium/net/quic/core/frames/quic_ack_frame.cc @@ -4,10 +4,15 @@ #include "net/quic/core/frames/quic_ack_frame.h" +#include <algorithm> + #include "net/quic/core/quic_constants.h" #include "net/quic/platform/api/quic_bug_tracker.h" #include "net/quic/platform/api/quic_flag_utils.h" +using std::max; +using std::min; + namespace net { PacketNumberQueue::const_iterator::const_iterator(const const_iterator& other) = @@ -67,9 +72,9 @@ std::ostream& operator<<(std::ostream& os, const QuicAckFrame& ack_frame) { return os; } PacketNumberQueue::PacketNumberQueue() - : use_deque_(FLAGS_quic_reloadable_flag_quic_frames_deque) { + : use_deque_(FLAGS_quic_reloadable_flag_quic_frames_deque2) { if (use_deque_) { - QUIC_FLAG_COUNT(quic_reloadable_flag_quic_frames_deque); + QUIC_FLAG_COUNT(quic_reloadable_flag_quic_frames_deque2); } } @@ -90,27 +95,30 @@ void PacketNumberQueue::Add(QuicPacketNumber packet_number) { Interval<QuicPacketNumber>(packet_number, packet_number + 1)); return; } + Interval<QuicPacketNumber> back = packet_number_deque_.back(); // Check for the typical case, // when the next packet in order is acked - if ((packet_number_deque_.back()).max() == packet_number) { - (packet_number_deque_.back()).SetMax(packet_number + 1); + if (back.max() == packet_number) { + packet_number_deque_.back().SetMax(packet_number + 1); return; } // Check if the next packet in order is skipped - if ((packet_number_deque_.back()).max() < packet_number) { + if (back.max() < packet_number) { packet_number_deque_.push_back( Interval<QuicPacketNumber>(packet_number, packet_number + 1)); return; } + + Interval<QuicPacketNumber> front = packet_number_deque_.front(); // Check if the packet can be popped on the front - if ((packet_number_deque_.front()).min() > packet_number + 1) { + if (front.min() > packet_number + 1) { packet_number_deque_.push_front( Interval<QuicPacketNumber>(packet_number, packet_number + 1)); return; } - if ((packet_number_deque_.front()).min() == packet_number + 1) { - (packet_number_deque_.front()).SetMin(packet_number); + if (front.min() == packet_number + 1) { + packet_number_deque_.front().SetMin(packet_number); return; } @@ -118,36 +126,33 @@ void PacketNumberQueue::Add(QuicPacketNumber packet_number) { // Iterating through the queue backwards // to find a proper place for the packet while (i >= 0) { + Interval<QuicPacketNumber> packet_interval = packet_number_deque_[i]; + DCHECK(packet_interval.min() < packet_interval.max()); // Check if the packet is contained in an interval already - if (packet_number_deque_[i].max() > packet_number && - packet_number_deque_[i].min() <= packet_number) { + if (packet_interval.Contains(packet_number)) { return; } - // Check if the packet can extend an interval - // and merges two intervals if needed - if (packet_number_deque_[i].max() == packet_number) { + // Check if the packet can extend an interval. + if (packet_interval.max() == packet_number) { packet_number_deque_[i].SetMax(packet_number + 1); - if (static_cast<size_t>(i) < packet_number_deque_.size() - 1 && - packet_number_deque_[i].max() == - packet_number_deque_[i + 1].min()) { - packet_number_deque_[i].SetMax(packet_number_deque_[i + 1].max()); - packet_number_deque_.erase(packet_number_deque_.begin() + i + 1); - } return; } - if (packet_number_deque_[i].min() == packet_number + 1) { + // Check if the packet can extend an interval + // and merge two intervals if needed. + // There is no need to merge an interval in the previous + // if statement, as all merges will happen here. + if (packet_interval.min() == packet_number + 1) { packet_number_deque_[i].SetMin(packet_number); - if (i > 0 && packet_number_deque_[i].min() == - packet_number_deque_[i - 1].max()) { - packet_number_deque_[i - 1].SetMax(packet_number_deque_[i].max()); + if (i > 0 && packet_number == packet_number_deque_[i - 1].max()) { + packet_number_deque_[i - 1].SetMax(packet_interval.max()); packet_number_deque_.erase(packet_number_deque_.begin() + i); } return; } // Check if we need to make a new interval for the packet - if (packet_number_deque_[i].max() < packet_number + 1) { + if (packet_interval.max() < packet_number + 1) { packet_number_deque_.insert( packet_number_deque_.begin() + i + 1, Interval<QuicPacketNumber>(packet_number, packet_number + 1)); @@ -160,7 +165,8 @@ void PacketNumberQueue::Add(QuicPacketNumber packet_number) { } } -void PacketNumberQueue::Add(QuicPacketNumber lower, QuicPacketNumber higher) { +void PacketNumberQueue::AddRange(QuicPacketNumber lower, + QuicPacketNumber higher) { if (lower >= higher) { return; } @@ -168,26 +174,47 @@ void PacketNumberQueue::Add(QuicPacketNumber lower, QuicPacketNumber higher) { if (packet_number_deque_.empty()) { packet_number_deque_.push_front( Interval<QuicPacketNumber>(lower, higher)); + return; + } + Interval<QuicPacketNumber> back = packet_number_deque_.back(); - } else if ((packet_number_deque_.back()).max() == lower) { + if (back.max() == lower) { // Check for the typical case, // when the next packet in order is acked - (packet_number_deque_.back()).SetMax(higher); - - } else if ((packet_number_deque_.back()).max() < lower) { + packet_number_deque_.back().SetMax(higher); + return; + } + if (back.max() < lower) { // Check if the next packet in order is skipped packet_number_deque_.push_back(Interval<QuicPacketNumber>(lower, higher)); - - // Check if the packets are being added in reverse order - } else if ((packet_number_deque_.front()).min() == higher) { - (packet_number_deque_.front()).SetMax(lower); - } else if ((packet_number_deque_.front()).min() > higher) { + return; + } + Interval<QuicPacketNumber> front = packet_number_deque_.front(); + // Check if the packets are being added in reverse order + if (front.min() == higher) { + packet_number_deque_.front().SetMin(lower); + } else if (front.min() > higher) { packet_number_deque_.push_front( Interval<QuicPacketNumber>(lower, higher)); } else { // Iterating through the interval and adding packets one by one - for (size_t i = lower; i != higher; i++) { + QUIC_BUG << "In the slowpath of AddRange. Adding [" << lower << ", " + << higher << "), in a deque of size " + << packet_number_deque_.size() << ", whose largest element is " + << back.max() << " and smallest " << front.min() << ".\n"; + // Check if the first and/or the last interval of the deque can be + // extended, which would reduce the compexity of the following for loop. + if (higher >= back.max()) { + packet_number_deque_.back().SetMax(higher); + higher = max(lower, back.min()); + } + if (lower < front.min()) { + packet_number_deque_.front().SetMin(lower); + lower = min(higher, front.max()); + } + + for (size_t i = lower; i < higher; i++) { PacketNumberQueue::Add(i); } } @@ -203,12 +230,12 @@ bool PacketNumberQueue::RemoveUpTo(QuicPacketNumber higher) { const QuicPacketNumber old_min = Min(); if (use_deque_) { while (!packet_number_deque_.empty()) { - if (packet_number_deque_[0].max() < higher) { + Interval<QuicPacketNumber> front = packet_number_deque_.front(); + if (front.max() < higher) { packet_number_deque_.pop_front(); - } else if (packet_number_deque_[0].min() < higher && - packet_number_deque_[0].max() >= higher) { - packet_number_deque_[0].SetMin(higher); - if (packet_number_deque_[0].max() == packet_number_deque_[0].min()) { + } else if (front.min() < higher && front.max() >= higher) { + packet_number_deque_.front().SetMin(higher); + if (front.max() == higher) { packet_number_deque_.pop_front(); } break; @@ -239,16 +266,15 @@ void PacketNumberQueue::RemoveSmallestInterval() { bool PacketNumberQueue::Contains(QuicPacketNumber packet_number) const { if (use_deque_) { - // TODO(lilika): Consider using std::binary_search based on profiles - // http://www.cplusplus.com/reference/algorithm/binary_search/ if (packet_number_deque_.empty()) { return false; } + if (packet_number_deque_.front().min() > packet_number || + packet_number_deque_.back().max() <= packet_number) { + return false; + } for (Interval<QuicPacketNumber> interval : packet_number_deque_) { - if (packet_number < interval.min()) { - return false; - } - if (interval.min() <= packet_number && interval.max() > packet_number) { + if (interval.Contains(packet_number)) { return true; } } @@ -269,7 +295,7 @@ bool PacketNumberQueue::Empty() const { QuicPacketNumber PacketNumberQueue::Min() const { DCHECK(!Empty()); if (use_deque_) { - return packet_number_deque_[0].min(); + return packet_number_deque_.front().min(); } else { return packet_number_intervals_.begin()->min(); } @@ -278,7 +304,7 @@ QuicPacketNumber PacketNumberQueue::Min() const { QuicPacketNumber PacketNumberQueue::Max() const { DCHECK(!Empty()); if (use_deque_) { - return packet_number_deque_[packet_number_deque_.size() - 1].max() - 1; + return packet_number_deque_.back().max() - 1; } else { return packet_number_intervals_.rbegin()->max() - 1; } @@ -286,9 +312,9 @@ QuicPacketNumber PacketNumberQueue::Max() const { size_t PacketNumberQueue::NumPacketsSlow() const { if (use_deque_) { - size_t n_packets = 0; - for (size_t i = 0; i < packet_number_deque_.size(); i++) { - n_packets += packet_number_deque_[i].Length(); + int n_packets = 0; + for (Interval<QuicPacketNumber> interval : packet_number_deque_) { + n_packets += interval.Length(); } return n_packets; } else { @@ -343,7 +369,7 @@ PacketNumberQueue::const_reverse_iterator PacketNumberQueue::rend() const { QuicPacketNumber PacketNumberQueue::LastIntervalLength() const { DCHECK(!Empty()); if (use_deque_) { - return packet_number_deque_[packet_number_deque_.size() - 1].Length(); + return packet_number_deque_.back().Length(); } else { return packet_number_intervals_.rbegin()->Length(); } diff --git a/chromium/net/quic/core/frames/quic_ack_frame.h b/chromium/net/quic/core/frames/quic_ack_frame.h index c7584c1e27b..54f95a3ffb0 100644 --- a/chromium/net/quic/core/frames/quic_ack_frame.h +++ b/chromium/net/quic/core/frames/quic_ack_frame.h @@ -198,7 +198,7 @@ class QUIC_EXPORT_PRIVATE PacketNumberQueue { // Adds packets between [lower, higher) to the set of packets in the queue. It // is undefined behavior to call this with |higher| < |lower|. - void Add(QuicPacketNumber lower, QuicPacketNumber higher); + void AddRange(QuicPacketNumber lower, QuicPacketNumber higher); // Removes packets with values less than |higher| from the set of packets in // the queue. Returns true if packets were removed. @@ -244,7 +244,7 @@ class QUIC_EXPORT_PRIVATE PacketNumberQueue { private: // TODO(lilika): Remove QuicIntervalSet<QuicPacketNumber> - // once FLAGS_quic_reloadable_flag_quic_frames_deque is removed + // once FLAGS_quic_reloadable_flag_quic_frames_deque2 is removed QuicIntervalSet<QuicPacketNumber> packet_number_intervals_; std::deque<Interval<QuicPacketNumber>> packet_number_deque_; bool use_deque_; diff --git a/chromium/net/quic/core/frames/quic_frames_test.cc b/chromium/net/quic/core/frames/quic_frames_test.cc index 70cbfcb3259..323e8cdd7fd 100644 --- a/chromium/net/quic/core/frames/quic_frames_test.cc +++ b/chromium/net/quic/core/frames/quic_frames_test.cc @@ -15,6 +15,7 @@ #include "net/quic/core/frames/quic_stream_frame.h" #include "net/quic/core/frames/quic_window_update_frame.h" #include "net/quic/platform/api/quic_test.h" +#include "net/quic/test_tools/quic_test_utils.h" namespace net { namespace test { @@ -117,7 +118,7 @@ TEST_F(QuicFramesTest, StopWaitingFrameToString) { TEST_F(QuicFramesTest, IsAwaitingPacket) { QuicAckFrame ack_frame1; ack_frame1.largest_observed = 10u; - ack_frame1.packets.Add(1, 11); + ack_frame1.packets.AddRange(1, 11); EXPECT_TRUE(IsAwaitingPacket(ack_frame1, 11u, 0u)); EXPECT_FALSE(IsAwaitingPacket(ack_frame1, 1u, 0u)); @@ -126,12 +127,12 @@ TEST_F(QuicFramesTest, IsAwaitingPacket) { QuicAckFrame ack_frame2; ack_frame2.largest_observed = 100u; - ack_frame2.packets.Add(21, 100); + ack_frame2.packets.AddRange(21, 100); EXPECT_FALSE(IsAwaitingPacket(ack_frame2, 11u, 20u)); EXPECT_FALSE(IsAwaitingPacket(ack_frame2, 80u, 20u)); EXPECT_TRUE(IsAwaitingPacket(ack_frame2, 101u, 20u)); - ack_frame2.packets.Add(102, 200); + ack_frame2.packets.AddRange(102, 200); EXPECT_TRUE(IsAwaitingPacket(ack_frame2, 101u, 20u)); } @@ -207,8 +208,8 @@ TEST_F(QuicFramesTest, AddPacket) { TEST_F(QuicFramesTest, AddInterval) { QuicAckFrame ack_frame1; - ack_frame1.packets.Add(1, 10); - ack_frame1.packets.Add(50, 100); + ack_frame1.packets.AddRange(1, 10); + ack_frame1.packets.AddRange(50, 100); EXPECT_EQ(2u, ack_frame1.packets.NumIntervals()); EXPECT_EQ(1u, ack_frame1.packets.Min()); @@ -223,7 +224,12 @@ TEST_F(QuicFramesTest, AddInterval) { EXPECT_EQ(expected_intervals, actual_intervals); - ack_frame1.packets.Add(20, 30); + if (FLAGS_quic_reloadable_flag_quic_frames_deque2) { + EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(20, 30), ""); + } else { + ack_frame1.packets.AddRange(20, 30); + } + const std::vector<Interval<QuicPacketNumber>> actual_intervals2( ack_frame1.packets.begin(), ack_frame1.packets.end()); @@ -235,8 +241,13 @@ TEST_F(QuicFramesTest, AddInterval) { EXPECT_EQ(3u, ack_frame1.packets.NumIntervals()); EXPECT_EQ(expected_intervals2, actual_intervals2); - ack_frame1.packets.Add(15, 20); - ack_frame1.packets.Add(30, 35); + if (FLAGS_quic_reloadable_flag_quic_frames_deque2) { + EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(15, 20), ""); + EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(30, 35), ""); + } else { + ack_frame1.packets.AddRange(15, 20); + ack_frame1.packets.AddRange(30, 35); + } const std::vector<Interval<QuicPacketNumber>> actual_intervals3( ack_frame1.packets.begin(), ack_frame1.packets.end()); @@ -248,15 +259,23 @@ TEST_F(QuicFramesTest, AddInterval) { EXPECT_EQ(expected_intervals3, actual_intervals3); - ack_frame1.packets.Add(20, 35); + if (FLAGS_quic_reloadable_flag_quic_frames_deque2) { + EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(20, 35), ""); + } else { + ack_frame1.packets.AddRange(20, 35); + } const std::vector<Interval<QuicPacketNumber>> actual_intervals4( ack_frame1.packets.begin(), ack_frame1.packets.end()); EXPECT_EQ(expected_intervals3, actual_intervals4); - - ack_frame1.packets.Add(12, 20); - ack_frame1.packets.Add(30, 38); + if (FLAGS_quic_reloadable_flag_quic_frames_deque2) { + EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(12, 20), ""); + EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(30, 38), ""); + } else { + ack_frame1.packets.AddRange(12, 20); + ack_frame1.packets.AddRange(30, 38); + } const std::vector<Interval<QuicPacketNumber>> actual_intervals5( ack_frame1.packets.begin(), ack_frame1.packets.end()); @@ -267,8 +286,11 @@ TEST_F(QuicFramesTest, AddInterval) { expected_intervals5.push_back(Interval<QuicPacketNumber>(50, 100)); EXPECT_EQ(expected_intervals5, actual_intervals5); - - ack_frame1.packets.Add(8, 55); + if (FLAGS_quic_reloadable_flag_quic_frames_deque2) { + EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(8, 55), ""); + } else { + ack_frame1.packets.AddRange(8, 55); + } const std::vector<Interval<QuicPacketNumber>> actual_intervals6( ack_frame1.packets.begin(), ack_frame1.packets.end()); @@ -278,7 +300,11 @@ TEST_F(QuicFramesTest, AddInterval) { EXPECT_EQ(expected_intervals6, actual_intervals6); - ack_frame1.packets.Add(0, 200); + if (FLAGS_quic_reloadable_flag_quic_frames_deque2) { + EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(0, 200), ""); + } else { + ack_frame1.packets.AddRange(0, 200); + } const std::vector<Interval<QuicPacketNumber>> actual_intervals7( ack_frame1.packets.begin(), ack_frame1.packets.end()); @@ -289,11 +315,11 @@ TEST_F(QuicFramesTest, AddInterval) { EXPECT_EQ(expected_intervals7, actual_intervals7); QuicAckFrame ack_frame2; - ack_frame2.packets.Add(20, 25); - ack_frame2.packets.Add(40, 45); - ack_frame2.packets.Add(60, 65); - ack_frame2.packets.Add(10, 15); - ack_frame2.packets.Add(80, 85); + ack_frame2.packets.AddRange(20, 25); + ack_frame2.packets.AddRange(40, 45); + ack_frame2.packets.AddRange(60, 65); + ack_frame2.packets.AddRange(10, 15); + ack_frame2.packets.AddRange(80, 85); const std::vector<Interval<QuicPacketNumber>> actual_intervals8( ack_frame2.packets.begin(), ack_frame2.packets.end()); @@ -308,12 +334,131 @@ TEST_F(QuicFramesTest, AddInterval) { EXPECT_EQ(expected_intervals8, actual_intervals8); } +TEST_F(QuicFramesTest, AddAdjacentReverse) { + QuicAckFrame ack_frame1; + ack_frame1.packets.AddRange(70, 100); + ack_frame1.packets.AddRange(60, 70); + ack_frame1.packets.AddRange(50, 60); + ack_frame1.packets.Add(49); + + std::vector<Interval<QuicPacketNumber>> expected_intervals; + expected_intervals.push_back(Interval<QuicPacketNumber>(49, 100)); + + const std::vector<Interval<QuicPacketNumber>> actual_intervals( + ack_frame1.packets.begin(), ack_frame1.packets.end()); + + EXPECT_EQ(expected_intervals, actual_intervals); +} + +TEST_F(QuicFramesTest, AddMerges) { + QuicAckFrame ack_frame1; + ack_frame1.packets.AddRange(110, 112); + ack_frame1.packets.AddRange(106, 108); + ack_frame1.packets.AddRange(102, 104); + ack_frame1.packets.AddRange(1, 2); + ack_frame1.packets.AddRange(4, 7); + ack_frame1.packets.AddRange(10, 20); + ack_frame1.packets.AddRange(21, 30); + ack_frame1.packets.Add(20); + ack_frame1.packets.AddRange(40, 50); + ack_frame1.packets.AddRange(30, 35); + ack_frame1.packets.AddRange(35, 40); + ack_frame1.packets.AddRange(108, 110); + if (FLAGS_quic_reloadable_flag_quic_frames_deque2) { + EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(50, 106), ""); + } else { + ack_frame1.packets.AddRange(50, 106); + } + ack_frame1.packets.AddRange(2, 4); + ack_frame1.packets.AddRange(7, 11); + std::vector<Interval<QuicPacketNumber>> expected_intervals; + expected_intervals.push_back(Interval<QuicPacketNumber>(1, 112)); + + const std::vector<Interval<QuicPacketNumber>> actual_intervals( + ack_frame1.packets.begin(), ack_frame1.packets.end()); + + EXPECT_EQ(expected_intervals, actual_intervals); +} + +TEST_F(QuicFramesTest, AddIntervalBig) { + QuicAckFrame ack_frame1; + ack_frame1.packets.AddRange(20, 30); + ack_frame1.packets.AddRange(70, 100); + if (FLAGS_quic_reloadable_flag_quic_frames_deque2) { + EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(56, 58), ""); + EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(65, 69), ""); + EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(59, 64), ""); + EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(50, 55), ""); + } else { + ack_frame1.packets.AddRange(56, 58); + ack_frame1.packets.AddRange(65, 69); + ack_frame1.packets.AddRange(59, 64); + ack_frame1.packets.AddRange(50, 55); + } + + std::vector<Interval<QuicPacketNumber>> expected_intervals; + expected_intervals.push_back(Interval<QuicPacketNumber>(20, 30)); + expected_intervals.push_back(Interval<QuicPacketNumber>(50, 55)); + expected_intervals.push_back(Interval<QuicPacketNumber>(56, 58)); + expected_intervals.push_back(Interval<QuicPacketNumber>(59, 64)); + expected_intervals.push_back(Interval<QuicPacketNumber>(65, 69)); + expected_intervals.push_back(Interval<QuicPacketNumber>(70, 100)); + + const std::vector<Interval<QuicPacketNumber>> actual_intervals( + ack_frame1.packets.begin(), ack_frame1.packets.end()); + + EXPECT_EQ(expected_intervals, actual_intervals); + if (FLAGS_quic_reloadable_flag_quic_frames_deque2) { + EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(10, 60), ""); + } else { + ack_frame1.packets.AddRange(10, 60); + } + + std::vector<Interval<QuicPacketNumber>> expected_intervals2; + expected_intervals2.push_back(Interval<QuicPacketNumber>(10, 64)); + expected_intervals2.push_back(Interval<QuicPacketNumber>(65, 69)); + expected_intervals2.push_back(Interval<QuicPacketNumber>(70, 100)); + + const std::vector<Interval<QuicPacketNumber>> actual_intervals2( + ack_frame1.packets.begin(), ack_frame1.packets.end()); + + EXPECT_EQ(expected_intervals2, actual_intervals2); + + if (FLAGS_quic_reloadable_flag_quic_frames_deque2) { + EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(68, 1000), ""); + } else { + ack_frame1.packets.AddRange(68, 1000); + } + + std::vector<Interval<QuicPacketNumber>> expected_intervals3; + expected_intervals3.push_back(Interval<QuicPacketNumber>(10, 64)); + expected_intervals3.push_back(Interval<QuicPacketNumber>(65, 1000)); + + const std::vector<Interval<QuicPacketNumber>> actual_intervals3( + ack_frame1.packets.begin(), ack_frame1.packets.end()); + + EXPECT_EQ(expected_intervals3, actual_intervals3); + if (FLAGS_quic_reloadable_flag_quic_frames_deque2) { + EXPECT_QUIC_BUG(ack_frame1.packets.AddRange(0, 10000), ""); + } else { + ack_frame1.packets.AddRange(0, 10000); + } + + std::vector<Interval<QuicPacketNumber>> expected_intervals4; + expected_intervals4.push_back(Interval<QuicPacketNumber>(0, 10000)); + + const std::vector<Interval<QuicPacketNumber>> actual_intervals4( + ack_frame1.packets.begin(), ack_frame1.packets.end()); + + EXPECT_EQ(expected_intervals4, actual_intervals4); +} + TEST_F(QuicFramesTest, RemoveSmallestInterval) { QuicAckFrame ack_frame1; ack_frame1.largest_observed = 100u; - ack_frame1.packets.Add(51, 60); - ack_frame1.packets.Add(71, 80); - ack_frame1.packets.Add(91, 100); + ack_frame1.packets.AddRange(51, 60); + ack_frame1.packets.AddRange(71, 80); + ack_frame1.packets.AddRange(91, 100); ack_frame1.packets.RemoveSmallestInterval(); EXPECT_EQ(2u, ack_frame1.packets.NumIntervals()); EXPECT_EQ(71u, ack_frame1.packets.Min()); @@ -330,7 +475,7 @@ class PacketNumberQueueTest : public QuicTest {}; // Tests that a queue contains the expected data after calls to Add(). TEST_F(PacketNumberQueueTest, AddRange) { PacketNumberQueue queue; - queue.Add(1, 51); + queue.AddRange(1, 51); queue.Add(53); EXPECT_FALSE(queue.Contains(0)); @@ -353,7 +498,7 @@ TEST_F(PacketNumberQueueTest, AddRange) { TEST_F(PacketNumberQueueTest, Contains) { PacketNumberQueue queue; EXPECT_FALSE(queue.Contains(0)); - queue.Add(5, 10); + queue.AddRange(5, 10); queue.Add(20); for (int i = 1; i < 5; ++i) { @@ -389,7 +534,7 @@ TEST_F(PacketNumberQueueTest, Contains) { TEST_F(PacketNumberQueueTest, Removal) { PacketNumberQueue queue; EXPECT_FALSE(queue.Contains(51)); - queue.Add(0, 100); + queue.AddRange(0, 100); EXPECT_TRUE(queue.RemoveUpTo(51)); EXPECT_FALSE(queue.RemoveUpTo(51)); @@ -406,7 +551,7 @@ TEST_F(PacketNumberQueueTest, Removal) { EXPECT_EQ(99u, queue.Max()); PacketNumberQueue queue2; - queue2.Add(0, 5); + queue2.AddRange(0, 5); EXPECT_TRUE(queue2.RemoveUpTo(3)); EXPECT_TRUE(queue2.RemoveUpTo(50)); EXPECT_TRUE(queue2.Empty()); @@ -418,7 +563,7 @@ TEST_F(PacketNumberQueueTest, Empty) { EXPECT_TRUE(queue.Empty()); EXPECT_EQ(0u, queue.NumPacketsSlow()); - queue.Add(1, 100); + queue.AddRange(1, 100); EXPECT_TRUE(queue.RemoveUpTo(100)); EXPECT_TRUE(queue.Empty()); EXPECT_EQ(0u, queue.NumPacketsSlow()); @@ -431,21 +576,21 @@ TEST_F(PacketNumberQueueTest, LogDoesNotCrash) { oss << queue; queue.Add(1); - queue.Add(50, 100); + queue.AddRange(50, 100); oss << queue; } // Tests that the iterators returned from a packet queue iterate over the queue. TEST_F(PacketNumberQueueTest, Iterators) { PacketNumberQueue queue; - queue.Add(1, 100); + queue.AddRange(1, 100); const std::vector<Interval<QuicPacketNumber>> actual_intervals(queue.begin(), queue.end()); PacketNumberQueue queue2; for (int i = 1; i < 100; i++) { - queue2.Add(i, i + 1); + queue2.AddRange(i, i + 1); } const std::vector<Interval<QuicPacketNumber>> actual_intervals2( @@ -460,10 +605,10 @@ TEST_F(PacketNumberQueueTest, Iterators) { TEST_F(PacketNumberQueueTest, ReversedIterators) { PacketNumberQueue queue; - queue.Add(1, 100); + queue.AddRange(1, 100); PacketNumberQueue queue2; for (int i = 1; i < 100; i++) { - queue2.Add(i, i + 1); + queue2.AddRange(i, i + 1); } const std::vector<Interval<QuicPacketNumber>> actual_intervals(queue.rbegin(), queue.rend()); @@ -495,9 +640,9 @@ TEST_F(PacketNumberQueueTest, ReversedIterators) { TEST_F(PacketNumberQueueTest, IntervalLengthAndRemoveInterval) { PacketNumberQueue queue; - queue.Add(1, 10); - queue.Add(20, 30); - queue.Add(40, 50); + queue.AddRange(1, 10); + queue.AddRange(20, 30); + queue.AddRange(40, 50); EXPECT_EQ(3u, queue.NumIntervals()); EXPECT_EQ(10u, queue.LastIntervalLength()); diff --git a/chromium/net/quic/core/frames/quic_stream_frame.h b/chromium/net/quic/core/frames/quic_stream_frame.h index 90cfbfe8d2c..4d280fcf0ec 100644 --- a/chromium/net/quic/core/frames/quic_stream_frame.h +++ b/chromium/net/quic/core/frames/quic_stream_frame.h @@ -65,9 +65,9 @@ struct QUIC_EXPORT_PRIVATE QuicStreamFrame { const char* data_buffer; QuicStreamOffset offset; // Location of this data in the stream. // TODO(fayang): When deprecating - // FLAGS_quic_reloadable_flag_quic_stream_owns_data: (1) Remove buffer from - // QuicStreamFrame; (2) remove the constructor uses UniqueStreamBuffer and (3) - // Move definition of UniqueStreamBuffer to QuicStreamSendBuffer. + // quic_reloadable_flag_quic_save_data_before_consumption2: (1) Remove buffer + // from QuicStreamFrame; (2) remove the constructor uses UniqueStreamBuffer + // and (3) Move definition of UniqueStreamBuffer to QuicStreamSendBuffer. // nullptr when the QuicStreamFrame is received, and non-null when sent. UniqueStreamBuffer buffer; diff --git a/chromium/net/quic/core/quic_buffered_packet_store.cc b/chromium/net/quic/core/quic_buffered_packet_store.cc index 0ed808761a8..72186af8c79 100644 --- a/chromium/net/quic/core/quic_buffered_packet_store.cc +++ b/chromium/net/quic/core/quic_buffered_packet_store.cc @@ -197,7 +197,6 @@ bool QuicBufferedPacketStore::ShouldBufferPacket(bool is_chlo) { size_t num_connections_without_chlo = undecryptable_packets_.size() - connections_with_chlo_.size(); bool reach_non_chlo_limit = - FLAGS_quic_reloadable_flag_quic_limit_num_new_sessions_per_epoll_loop && num_connections_without_chlo >= kMaxConnectionsWithoutCHLO; return is_store_full || reach_non_chlo_limit; diff --git a/chromium/net/quic/core/quic_buffered_packet_store_test.cc b/chromium/net/quic/core/quic_buffered_packet_store_test.cc index 935d7ac2786..743535cda7c 100644 --- a/chromium/net/quic/core/quic_buffered_packet_store_test.cc +++ b/chromium/net/quic/core/quic_buffered_packet_store_test.cc @@ -129,13 +129,11 @@ TEST_F(QuicBufferedPacketStoreTest, // buffered. size_t num_packets = kDefaultMaxUndecryptablePackets + 1; QuicConnectionId connection_id = 1; - if (FLAGS_quic_reloadable_flag_quic_limit_num_new_sessions_per_epoll_loop) { - // Arrived CHLO packet shouldn't affect how many non-CHLO pacekts store can - // keep. - EXPECT_EQ(QuicBufferedPacketStore::SUCCESS, - store_.EnqueuePacket(connection_id, packet_, server_address_, - client_address_, true, "")); - } + // Arrived CHLO packet shouldn't affect how many non-CHLO pacekts store can + // keep. + EXPECT_EQ(QuicBufferedPacketStore::SUCCESS, + store_.EnqueuePacket(connection_id, packet_, server_address_, + client_address_, true, "")); for (size_t i = 1; i <= num_packets; ++i) { // Only first |kDefaultMaxUndecryptablePackets packets| will be buffered. EnqueuePacketResult result = store_.EnqueuePacket( @@ -149,30 +147,19 @@ TEST_F(QuicBufferedPacketStoreTest, // Only first |kDefaultMaxUndecryptablePackets| non-CHLO packets and CHLO are // buffered. - EXPECT_EQ( - kDefaultMaxUndecryptablePackets + - (FLAGS_quic_reloadable_flag_quic_limit_num_new_sessions_per_epoll_loop - ? 1 - : 0), - store_.DeliverPackets(connection_id).buffered_packets.size()); + EXPECT_EQ(kDefaultMaxUndecryptablePackets + 1, + store_.DeliverPackets(connection_id).buffered_packets.size()); } TEST_F(QuicBufferedPacketStoreTest, ReachNonChloConnectionUpperLimit) { // Tests that store can only keep early arrived packets for limited number of // connections. - const size_t kNumConnections = - (FLAGS_quic_reloadable_flag_quic_limit_num_new_sessions_per_epoll_loop - ? kMaxConnectionsWithoutCHLO - : kDefaultMaxConnectionsInStore) + - 1; + const size_t kNumConnections = kMaxConnectionsWithoutCHLO + 1; for (size_t connection_id = 1; connection_id <= kNumConnections; ++connection_id) { EnqueuePacketResult result = store_.EnqueuePacket( connection_id, packet_, server_address_, client_address_, false, ""); - if (connection_id <= - (FLAGS_quic_reloadable_flag_quic_limit_num_new_sessions_per_epoll_loop - ? kMaxConnectionsWithoutCHLO - : kDefaultMaxConnectionsInStore)) { + if (connection_id <= kMaxConnectionsWithoutCHLO) { EXPECT_EQ(EnqueuePacketResult::SUCCESS, result); } else { EXPECT_EQ(EnqueuePacketResult::TOO_MANY_CONNECTIONS, result); @@ -183,10 +170,7 @@ TEST_F(QuicBufferedPacketStoreTest, ReachNonChloConnectionUpperLimit) { ++connection_id) { std::list<BufferedPacket> queue = store_.DeliverPackets(connection_id).buffered_packets; - if (connection_id <= - (FLAGS_quic_reloadable_flag_quic_limit_num_new_sessions_per_epoll_loop - ? kMaxConnectionsWithoutCHLO - : kDefaultMaxConnectionsInStore)) { + if (connection_id <= kMaxConnectionsWithoutCHLO) { EXPECT_EQ(1u, queue.size()); } else { EXPECT_EQ(0u, queue.size()); @@ -196,7 +180,6 @@ TEST_F(QuicBufferedPacketStoreTest, ReachNonChloConnectionUpperLimit) { TEST_F(QuicBufferedPacketStoreTest, FullStoreFailToBufferDataPacketOnNewConnection) { - FLAGS_quic_reloadable_flag_quic_limit_num_new_sessions_per_epoll_loop = true; // Send enough CHLOs so that store gets full before number of connections // without CHLO reaches its upper limit. size_t num_chlos = @@ -222,7 +205,6 @@ TEST_F(QuicBufferedPacketStoreTest, } TEST_F(QuicBufferedPacketStoreTest, EnqueueChloOnTooManyDifferentConnections) { - FLAGS_quic_reloadable_flag_quic_limit_num_new_sessions_per_epoll_loop = true; // Buffer data packets on different connections upto limit. for (QuicConnectionId conn_id = 1; conn_id <= kMaxConnectionsWithoutCHLO; ++conn_id) { @@ -273,69 +255,15 @@ TEST_F(QuicBufferedPacketStoreTest, EnqueueChloOnTooManyDifferentConnections) { EXPECT_FALSE(store_.HasChlosBuffered()); } -TEST_F(QuicBufferedPacketStoreTest, PacketQueueExpiredBeforeDelivery1) { - FLAGS_quic_reloadable_flag_quic_limit_num_new_sessions_per_epoll_loop = false; - QuicConnectionId connection_id = 1; - store_.EnqueuePacket(connection_id, packet_, server_address_, client_address_, - false, ""); - // Packet for another connection arrive 1ms later. - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); - QuicConnectionId connection_id2 = 2; - // Use different client address to differetiate packets from different - // connections. - QuicSocketAddress another_client_address(QuicIpAddress::Any4(), 255); - store_.EnqueuePacket(connection_id2, packet_, server_address_, - another_client_address, false, ""); - // Advance clock to the time when connection 1 expires. - clock_.AdvanceTime( - QuicBufferedPacketStorePeer::expiration_alarm(&store_)->deadline() - - clock_.ApproximateNow()); - ASSERT_GE(clock_.ApproximateNow(), - QuicBufferedPacketStorePeer::expiration_alarm(&store_)->deadline()); - // Fire alarm to remove long-staying connection 1 packets. - alarm_factory_.FireAlarm( - QuicBufferedPacketStorePeer::expiration_alarm(&store_)); - EXPECT_EQ(1u, visitor_.last_expired_packet_queue_.buffered_packets.size()); - // Try to deliver packets, but packet queue has been removed so no - // packets can be returned. - ASSERT_EQ(0u, store_.DeliverPackets(connection_id).buffered_packets.size()); - - // Deliver packets on connection 2. And the queue for connection 2 should be - // returned. - std::list<BufferedPacket> queue = - store_.DeliverPackets(connection_id2).buffered_packets; - ASSERT_EQ(1u, queue.size()); - // Packets in connection 2 should use another client address. - EXPECT_EQ(another_client_address, queue.front().client_address); - - // Test the alarm is reset by enqueueing 2 packets for 3rd connection and wait - // for them to expire. - QuicConnectionId connection_id3 = 3; - store_.EnqueuePacket(connection_id3, packet_, server_address_, - client_address_, false, ""); - store_.EnqueuePacket(connection_id3, packet_, server_address_, - client_address_, false, ""); - clock_.AdvanceTime( - QuicBufferedPacketStorePeer::expiration_alarm(&store_)->deadline() - - clock_.ApproximateNow()); - alarm_factory_.FireAlarm( - QuicBufferedPacketStorePeer::expiration_alarm(&store_)); - // |last_expired_packet_queue_| should be updated. - EXPECT_EQ(2u, visitor_.last_expired_packet_queue_.buffered_packets.size()); -} - // Tests that store expires long-staying connections appropriately for // connections both with and without CHLOs. -TEST_F(QuicBufferedPacketStoreTest, PacketQueueExpiredBeforeDelivery2) { - FLAGS_quic_reloadable_flag_quic_limit_num_new_sessions_per_epoll_loop = true; +TEST_F(QuicBufferedPacketStoreTest, PacketQueueExpiredBeforeDelivery) { QuicConnectionId connection_id = 1; store_.EnqueuePacket(connection_id, packet_, server_address_, client_address_, false, ""); - if (FLAGS_quic_reloadable_flag_quic_limit_num_new_sessions_per_epoll_loop) { - EXPECT_EQ(EnqueuePacketResult::SUCCESS, - store_.EnqueuePacket(connection_id, packet_, server_address_, - client_address_, true, "")); - } + EXPECT_EQ(EnqueuePacketResult::SUCCESS, + store_.EnqueuePacket(connection_id, packet_, server_address_, + client_address_, true, "")); QuicConnectionId connection_id2 = 2; EXPECT_EQ(EnqueuePacketResult::SUCCESS, store_.EnqueuePacket(connection_id2, packet_, server_address_, diff --git a/chromium/net/quic/core/quic_client_promised_info.cc b/chromium/net/quic/core/quic_client_promised_info.cc index ca8334be778..f6f5ef3af0c 100644 --- a/chromium/net/quic/core/quic_client_promised_info.cc +++ b/chromium/net/quic/core/quic_client_promised_info.cc @@ -4,16 +4,20 @@ #include "net/quic/core/quic_client_promised_info.h" +#include <utility> + #include "net/quic/core/spdy_utils.h" #include "net/quic/platform/api/quic_logging.h" +#include "net/spdy/core/spdy_protocol.h" using std::string; namespace net { -QuicClientPromisedInfo::QuicClientPromisedInfo(QuicClientSessionBase* session, - QuicStreamId id, - string url) +QuicClientPromisedInfo::QuicClientPromisedInfo( + QuicSpdyClientSessionBase* session, + QuicStreamId id, + string url) : session_(session), id_(id), url_(std::move(url)), @@ -38,7 +42,7 @@ void QuicClientPromisedInfo::Init() { void QuicClientPromisedInfo::OnPromiseHeaders(const SpdyHeaderBlock& headers) { // RFC7540, Section 8.2, requests MUST be safe [RFC7231], Section // 4.2.1. GET and HEAD are the methods that are safe and required. - SpdyHeaderBlock::const_iterator it = headers.find(":method"); + SpdyHeaderBlock::const_iterator it = headers.find(kHttp2MethodHeader); if (it == headers.end()) { QUIC_DVLOG(1) << "Promise for stream " << id_ << " has no method"; Reset(QUIC_INVALID_PROMISE_METHOD); diff --git a/chromium/net/quic/core/quic_client_promised_info.h b/chromium/net/quic/core/quic_client_promised_info.h index 1f755fe45c1..1946956e812 100644 --- a/chromium/net/quic/core/quic_client_promised_info.h +++ b/chromium/net/quic/core/quic_client_promised_info.h @@ -10,16 +10,14 @@ #include "net/quic/core/quic_alarm.h" #include "net/quic/core/quic_client_push_promise_index.h" -#include "net/quic/core/quic_client_session_base.h" #include "net/quic/core/quic_packets.h" +#include "net/quic/core/quic_spdy_client_session_base.h" #include "net/quic/core/quic_spdy_stream.h" #include "net/quic/platform/api/quic_export.h" #include "net/spdy/core/spdy_framer.h" namespace net { -class QuicClientSessionBase; - namespace test { class QuicClientPromisedInfoPeer; } // namespace test @@ -32,7 +30,7 @@ class QUIC_EXPORT_PRIVATE QuicClientPromisedInfo : public QuicClientPushPromiseIndex::TryHandle { public: // Interface to QuicSpdyClientStream - QuicClientPromisedInfo(QuicClientSessionBase* session, + QuicClientPromisedInfo(QuicSpdyClientSessionBase* session, QuicStreamId id, std::string url); virtual ~QuicClientPromisedInfo(); @@ -60,7 +58,7 @@ class QUIC_EXPORT_PRIVATE QuicClientPromisedInfo // uing the |promised_by_url| map. The push can be cross-origin, so // the client should validate that the session is authoritative for // the promised URL. If not, it should call |RejectUnauthorized|. - QuicClientSessionBase* session() { return session_; } + QuicSpdyClientSessionBase* session() { return session_; } // If the promised response contains Vary header, then the fields // specified by Vary must match between the client request header @@ -94,7 +92,7 @@ class QUIC_EXPORT_PRIVATE QuicClientPromisedInfo QuicAsyncStatus FinalValidation(); - QuicClientSessionBase* session_; + QuicSpdyClientSessionBase* session_; QuicStreamId id_; std::string url_; std::unique_ptr<SpdyHeaderBlock> request_headers_; diff --git a/chromium/net/quic/core/quic_client_promised_info_test.cc b/chromium/net/quic/core/quic_client_promised_info_test.cc index 59e87c9882d..9d5d758cdaa 100644 --- a/chromium/net/quic/core/quic_client_promised_info_test.cc +++ b/chromium/net/quic/core/quic_client_promised_info_test.cc @@ -15,7 +15,7 @@ #include "net/quic/test_tools/quic_client_promised_info_peer.h" #include "net/quic/test_tools/quic_spdy_session_peer.h" #include "net/test/gtest_util.h" -#include "net/tools/quic/quic_client_session.h" +#include "net/tools/quic/quic_spdy_client_session.h" using std::string; using testing::StrictMock; @@ -24,11 +24,12 @@ namespace net { namespace test { namespace { -class MockQuicClientSession : public QuicClientSession { +class MockQuicSpdyClientSession : public QuicSpdyClientSession { public: - explicit MockQuicClientSession(QuicConnection* connection, - QuicClientPushPromiseIndex* push_promise_index) - : QuicClientSession( + explicit MockQuicSpdyClientSession( + QuicConnection* connection, + QuicClientPushPromiseIndex* push_promise_index) + : QuicSpdyClientSession( DefaultQuicConfig(), connection, QuicServerId("example.com", 443, PRIVACY_MODE_DISABLED), @@ -36,7 +37,7 @@ class MockQuicClientSession : public QuicClientSession { push_promise_index), crypto_config_(crypto_test_utils::ProofVerifierForTesting()), authorized_(true) {} - ~MockQuicClientSession() override {} + ~MockQuicSpdyClientSession() override {} bool IsAuthorized(const string& authority) override { return authorized_; } @@ -49,7 +50,7 @@ class MockQuicClientSession : public QuicClientSession { bool authorized_; - DISALLOW_COPY_AND_ASSIGN(MockQuicClientSession); + DISALLOW_COPY_AND_ASSIGN(MockQuicSpdyClientSession); }; class QuicClientPromisedInfoTest : public QuicTest { @@ -104,7 +105,7 @@ class QuicClientPromisedInfoTest : public QuicTest { StrictMock<MockQuicConnection>* connection_; QuicClientPushPromiseIndex push_promise_index_; - MockQuicClientSession session_; + MockQuicSpdyClientSession session_; std::unique_ptr<QuicSpdyClientStream> stream_; std::unique_ptr<StreamVisitor> stream_visitor_; std::unique_ptr<QuicSpdyClientStream> promised_stream_; diff --git a/chromium/net/quic/core/quic_client_push_promise_index.h b/chromium/net/quic/core/quic_client_push_promise_index.h index 38dc1fc8218..2609ae143de 100644 --- a/chromium/net/quic/core/quic_client_push_promise_index.h +++ b/chromium/net/quic/core/quic_client_push_promise_index.h @@ -7,7 +7,7 @@ #include <string> -#include "net/quic/core/quic_client_session_base.h" +#include "net/quic/core/quic_spdy_client_session_base.h" #include "net/quic/core/quic_types.h" #include "net/quic/platform/api/quic_export.h" diff --git a/chromium/net/quic/core/quic_client_push_promise_index_test.cc b/chromium/net/quic/core/quic_client_push_promise_index_test.cc index 0a93cf177e2..9f980f14da5 100644 --- a/chromium/net/quic/core/quic_client_push_promise_index_test.cc +++ b/chromium/net/quic/core/quic_client_push_promise_index_test.cc @@ -12,36 +12,37 @@ #include "net/quic/test_tools/mock_quic_client_promised_info.h" #include "net/quic/test_tools/quic_spdy_session_peer.h" #include "net/quic/test_tools/quic_test_utils.h" -#include "net/tools/quic/quic_client_session.h" +#include "net/tools/quic/quic_spdy_client_session.h" -using testing::_; using testing::Return; using testing::StrictMock; +using testing::_; using std::string; namespace net { namespace test { namespace { -class MockQuicClientSession : public QuicClientSession { +class MockQuicSpdyClientSession : public QuicSpdyClientSession { public: - explicit MockQuicClientSession(QuicConnection* connection, - QuicClientPushPromiseIndex* push_promise_index) - : QuicClientSession( + explicit MockQuicSpdyClientSession( + QuicConnection* connection, + QuicClientPushPromiseIndex* push_promise_index) + : QuicSpdyClientSession( DefaultQuicConfig(), connection, QuicServerId("example.com", 443, PRIVACY_MODE_DISABLED), &crypto_config_, push_promise_index), crypto_config_(crypto_test_utils::ProofVerifierForTesting()) {} - ~MockQuicClientSession() override {} + ~MockQuicSpdyClientSession() override {} MOCK_METHOD1(CloseStream, void(QuicStreamId stream_id)); private: QuicCryptoClientConfig crypto_config_; - DISALLOW_COPY_AND_ASSIGN(MockQuicClientSession); + DISALLOW_COPY_AND_ASSIGN(MockQuicSpdyClientSession); }; class QuicClientPushPromiseIndexTest : public QuicTest { @@ -66,7 +67,7 @@ class QuicClientPushPromiseIndexTest : public QuicTest { MockQuicConnectionHelper helper_; MockAlarmFactory alarm_factory_; StrictMock<MockQuicConnection>* connection_; - MockQuicClientSession session_; + MockQuicSpdyClientSession session_; QuicClientPushPromiseIndex index_; SpdyHeaderBlock request_; string url_; diff --git a/chromium/net/quic/core/quic_connection.cc b/chromium/net/quic/core/quic_connection.cc index 99a12803858..d566cd58403 100644 --- a/chromium/net/quic/core/quic_connection.cc +++ b/chromium/net/quic/core/quic_connection.cc @@ -216,6 +216,7 @@ QuicConnection::QuicConnection(QuicConnectionId connection_id, stop_waiting_count_(0), ack_mode_(TCP_ACKING), ack_decimation_delay_(kAckDecimationDelay), + unlimited_ack_decimation_(false), delay_setting_retransmission_alarm_(false), pending_retransmission_alarm_(false), defer_send_in_response_to_packets_(false), @@ -246,7 +247,7 @@ QuicConnection::QuicConnection(QuicConnectionId connection_id, packet_generator_(connection_id_, &framer_, random_generator_, - helper->GetBufferAllocator(), + helper->GetStreamFrameBufferAllocator(), this), idle_network_timeout_(QuicTime::Delta::Infinite()), handshake_timeout_(QuicTime::Delta::Infinite()), @@ -270,7 +271,7 @@ QuicConnection::QuicConnection(QuicConnectionId connection_id, largest_received_packet_size_(0), goaway_sent_(false), goaway_received_(false), - write_error_occured_(false), + write_error_occurred_(false), no_stop_waiting_frames_(false), consecutive_num_packets_with_no_retransmittable_frames_(0) { QUIC_DLOG(INFO) << ENDPOINT @@ -354,6 +355,12 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { ack_mode_ = ACK_DECIMATION_WITH_REORDERING; ack_decimation_delay_ = kShortAckDecimationDelay; } + if (FLAGS_quic_reloadable_flag_quic_ack_decimation) { + QUIC_FLAG_COUNT(quic_reloadable_flag_quic_ack_decimation); + if (config.HasClientSentConnectionOption(kAKDU, perspective_)) { + unlimited_ack_decimation_ = true; + } + } if (config.HasClientSentConnectionOption(k5RTO, perspective_)) { close_connection_after_five_rtos_ = true; } @@ -536,6 +543,8 @@ void QuicConnection::OnVersionNegotiationPacket( return; } + server_supported_versions_ = packet.versions; + if (!SelectMutualVersion(packet.versions)) { CloseConnection( QUIC_INVALID_VERSION, @@ -549,7 +558,6 @@ void QuicConnection::OnVersionNegotiationPacket( QUIC_DLOG(INFO) << ENDPOINT << "Negotiated version: " << QuicVersionToString(version()); - server_supported_versions_ = packet.versions; version_negotiation_state_ = NEGOTIATION_IN_PROGRESS; RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION); } @@ -634,16 +642,26 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { return false; } - // Only migrate connection to a new peer address if a change is not underway. PeerAddressChangeType peer_migration_type = QuicUtils::DetermineAddressChangeType(peer_address_, last_packet_source_address_); - // Do not migrate connection if the changed address packet is a reordered - // packet. - if (active_peer_migration_type_ == NO_CHANGE && - peer_migration_type != NO_CHANGE && - header.packet_number > received_packet_manager_.GetLargestObserved()) { - StartPeerMigration(peer_migration_type); + // Initiate connection migration if a non-reordered packet is received from a + // new address. + if (header.packet_number > received_packet_manager_.GetLargestObserved() && + peer_migration_type != NO_CHANGE) { + if (FLAGS_quic_reloadable_flag_quic_disable_peer_migration_on_client && + perspective_ == Perspective::IS_CLIENT) { + QUIC_FLAG_COUNT_N( + quic_reloadable_flag_quic_disable_peer_migration_on_client, 1, 2); + QUIC_DLOG(INFO) << ENDPOINT << "Peer's ip:port changed from " + << peer_address_.ToString() << " to " + << last_packet_source_address_.ToString(); + peer_address_ = last_packet_source_address_; + } else if (active_peer_migration_type_ == NO_CHANGE) { + // Only migrate connection to a new peer address if there is no + // pending change underway. + StartPeerMigration(peer_migration_type); + } } --stats_.packets_dropped; @@ -897,7 +915,7 @@ bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) { bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { DCHECK(connected_); if (debug_visitor_ != nullptr) { - debug_visitor_->OnWindowUpdateFrame(frame); + debug_visitor_->OnWindowUpdateFrame(frame, time_of_last_received_packet_); } QUIC_DLOG(INFO) << ENDPOINT << "WINDOW_UPDATE_FRAME received for stream: " << frame.stream_id @@ -968,9 +986,10 @@ void QuicConnection::MaybeQueueAck(bool was_missing) { ++num_retransmittable_packets_received_since_last_ack_sent_; if (ack_mode_ != TCP_ACKING && last_header_.packet_number > kMinReceivedBeforeAckDecimation) { - // Ack up to 10 packets at once. - if (num_retransmittable_packets_received_since_last_ack_sent_ >= - kMaxRetransmittablePacketsBeforeAck) { + // Ack up to 10 packets at once unless ack decimation is unlimited. + if (!unlimited_ack_decimation_ && + num_retransmittable_packets_received_since_last_ack_sent_ >= + kMaxRetransmittablePacketsBeforeAck) { ack_queued_ = true; } else if (!ack_alarm_->IsSet()) { // Wait the minimum of a quarter min_rtt and the delayed ack time. @@ -1091,14 +1110,17 @@ QuicConsumedData QuicConnection::SendStreamData( ScopedPacketBundler ack_bundler(this, SEND_ACK_IF_PENDING); // The optimized path may be used for data only packets which fit into a // standard buffer and don't need padding. - if (id != kCryptoStreamId && !packet_generator_.HasQueuedFrames() && + const bool flag_run_fast_path = + FLAGS_quic_reloadable_flag_quic_consuming_data_faster; + if (!flag_run_fast_path && id != kCryptoStreamId && + !packet_generator_.HasQueuedFrames() && iov.total_length > kMaxPacketSize && state != FIN_AND_PADDING) { // Use the fast path to send full data packets. return packet_generator_.ConsumeDataFastPath( - id, iov, offset, state != NO_FIN, std::move(ack_listener)); + id, iov, offset, state != NO_FIN, 0, ack_listener); } - return packet_generator_.ConsumeData(id, iov, offset, state, - std::move(ack_listener)); + return packet_generator_.ConsumeData( + id, iov, offset, state, std::move(ack_listener), flag_run_fast_path); } void QuicConnection::SendRstStream(QuicStreamId id, @@ -1236,7 +1258,15 @@ void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address, if (active_peer_migration_type_ != NO_CHANGE && sent_packet_manager_.GetLargestObserved() > highest_packet_sent_before_peer_migration_) { - OnPeerMigrationValidated(); + if (FLAGS_quic_reloadable_flag_quic_disable_peer_migration_on_client) { + QUIC_FLAG_COUNT_N( + quic_reloadable_flag_quic_disable_peer_migration_on_client, 2, 2); + if (perspective_ == Perspective::IS_SERVER) { + OnPeerMigrationValidated(); + } + } else { + OnPeerMigrationValidated(); + } } MaybeProcessUndecryptablePackets(); MaybeSendInResponseToPacket(); @@ -1531,7 +1561,7 @@ bool QuicConnection::WritePacket(SerializedPacket* packet) { if (result.status == WRITE_STATUS_BLOCKED) { visitor_->OnWriteBlocked(); - // If the socket buffers the the data, then the packet should not + // If the socket buffers the data, then the packet should not // be queued and sent again, which would result in an unnecessary // duplicate packet being sent. The helper must call OnCanWrite // when the write completes, and OnWriteError if an error occurs. @@ -1633,11 +1663,11 @@ bool QuicConnection::AllowSelfAddressChange() const { } void QuicConnection::OnWriteError(int error_code) { - if (write_error_occured_) { + if (write_error_occurred_) { // A write error already occurred. The connection is being closed. return; } - write_error_occured_ = true; + write_error_occurred_ = true; const string error_details = QuicStrCat( "Write failed with error: ", error_code, " (", strerror(error_code), ")"); diff --git a/chromium/net/quic/core/quic_connection.h b/chromium/net/quic/core/quic_connection.h index 6ed662289a5..fc180415b17 100644 --- a/chromium/net/quic/core/quic_connection.h +++ b/chromium/net/quic/core/quic_connection.h @@ -230,7 +230,8 @@ class QUIC_EXPORT_PRIVATE QuicConnectionDebugVisitor virtual void OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) {} // Called when a WindowUpdate has been parsed. - virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {} + virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame, + const QuicTime& receive_time) {} // Called when a BlockedFrame has been parsed. virtual void OnBlockedFrame(const QuicBlockedFrame& frame) {} @@ -282,7 +283,10 @@ class QUIC_EXPORT_PRIVATE QuicConnectionHelperInterface { virtual QuicRandom* GetRandomGenerator() = 0; // Returns a QuicBufferAllocator to be used for all stream frame buffers. - virtual QuicBufferAllocator* GetBufferAllocator() = 0; + virtual QuicBufferAllocator* GetStreamFrameBufferAllocator() = 0; + + // Returns a QuicBufferAllocator to be used for stream send buffers. + virtual QuicBufferAllocator* GetStreamSendBufferAllocator() = 0; }; class QUIC_EXPORT_PRIVATE QuicConnection @@ -957,6 +961,9 @@ class QUIC_EXPORT_PRIVATE QuicConnection AckMode ack_mode_; // The max delay in fraction of min_rtt to use when sending decimated acks. float ack_decimation_delay_; + // When true, removes ack decimation's max number of packets(10) before + // sending an ack. + bool unlimited_ack_decimation_; // Indicates the retransmit alarm is going to be set by the // ScopedRetransmitAlarmDelayer @@ -1092,7 +1099,7 @@ class QUIC_EXPORT_PRIVATE QuicConnection // Indicates whether a write error is encountered currently. This is used to // avoid infinite write errors. - bool write_error_occured_; + bool write_error_occurred_; // Indicates not to send or process stop waiting frames. bool no_stop_waiting_frames_; diff --git a/chromium/net/quic/core/quic_connection_test.cc b/chromium/net/quic/core/quic_connection_test.cc index ec9ebb406a8..771491c8af0 100644 --- a/chromium/net/quic/core/quic_connection_test.cc +++ b/chromium/net/quic/core/quic_connection_test.cc @@ -230,7 +230,11 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { QuicRandom* GetRandomGenerator() override { return random_generator_; } - QuicBufferAllocator* GetBufferAllocator() override { + QuicBufferAllocator* GetStreamFrameBufferAllocator() override { + return &buffer_allocator_; + } + + QuicBufferAllocator* GetStreamSendBufferAllocator() override { return &buffer_allocator_; } @@ -995,7 +999,7 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { const QuicAckFrame InitAckFrame(QuicPacketNumber largest_observed) { QuicAckFrame frame(MakeAckFrame(largest_observed)); if (largest_observed > 0) { - frame.packets.Add(1, largest_observed + 1); + frame.packets.AddRange(1, largest_observed + 1); } return frame; } @@ -1013,12 +1017,12 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { QuicPacketNumber missing) { QuicAckFrame ack_frame; if (largest_acked > missing) { - ack_frame.packets.Add(1, missing); - ack_frame.packets.Add(missing + 1, largest_acked + 1); + ack_frame.packets.AddRange(1, missing); + ack_frame.packets.AddRange(missing + 1, largest_acked + 1); ack_frame.largest_observed = largest_acked; } if (largest_acked == missing) { - ack_frame.packets.Add(1, missing); + ack_frame.packets.AddRange(1, missing); ack_frame.largest_observed = largest_acked; } return ack_frame; @@ -1202,6 +1206,58 @@ TEST_P(QuicConnectionTest, ClientAddressChangeAndPacketReordered) { kPeerAddress); } +TEST_P(QuicConnectionTest, PeerAddressChangeAtServer) { + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + set_perspective(Perspective::IS_SERVER); + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); + + // Clear peer address. + QuicConnectionPeer::SetPeerAddress(&connection_, QuicSocketAddress()); + EXPECT_FALSE(connection_.peer_address().IsInitialized()); + + QuicStreamFrame stream_frame(1u, false, 0u, QuicStringPiece()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + ProcessFramePacketWithAddresses(QuicFrame(&stream_frame), kSelfAddress, + kPeerAddress); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + + // Process another packet with a different peer address on server side will + // start connection migration. + const QuicSocketAddress kNewPeerAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); + EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); + ProcessFramePacketWithAddresses(QuicFrame(&stream_frame), kSelfAddress, + kNewPeerAddress); + EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); +} + +TEST_P(QuicConnectionTest, PeerAddressChangeAtClient) { + FLAGS_quic_reloadable_flag_quic_disable_peer_migration_on_client = true; + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + set_perspective(Perspective::IS_CLIENT); + EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); + + // Clear peer address. + QuicConnectionPeer::SetPeerAddress(&connection_, QuicSocketAddress()); + EXPECT_FALSE(connection_.peer_address().IsInitialized()); + + QuicStreamFrame stream_frame(1u, false, 0u, QuicStringPiece()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + ProcessFramePacketWithAddresses(QuicFrame(&stream_frame), kSelfAddress, + kPeerAddress); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + + // Process another packet with a different peer address on client side will + // only update peer address. + const QuicSocketAddress kNewPeerAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); + EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); + ProcessFramePacketWithAddresses(QuicFrame(&stream_frame), kSelfAddress, + kNewPeerAddress); + EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); +} + TEST_P(QuicConnectionTest, MaxPacketSize) { EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); EXPECT_EQ(1350u, connection_.max_packet_length()); @@ -3147,12 +3203,12 @@ TEST_P(QuicConnectionTest, MtuDiscoveryFailed) { mtu_discovery_packets.end()); QuicPacketNumber max_packet = *max_element(mtu_discovery_packets.begin(), mtu_discovery_packets.end()); - ack.packets.Add(1, min_packet); - ack.packets.Add(max_packet + 1, creator_->packet_number() + 1); + ack.packets.AddRange(1, min_packet); + ack.packets.AddRange(max_packet + 1, creator_->packet_number() + 1); ack.largest_observed = creator_->packet_number(); } else { - ack.packets.Add(1, creator_->packet_number() + 1); + ack.packets.AddRange(1, creator_->packet_number() + 1); ack.largest_observed = creator_->packet_number(); } @@ -3321,7 +3377,7 @@ TEST_P(QuicConnectionTest, TimeoutAfterSend) { SendStreamDataToPeer(kClientDataStreamId1, "foo", 0, FIN, nullptr); EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline()); - // Now send more data. This will not move the timeout becase + // Now send more data. This will not move the timeout because // no data has been recieved since the previous write. clock_.AdvanceTime(five_ms); SendStreamDataToPeer(kClientDataStreamId1, "foo", 3, FIN, nullptr); @@ -3458,7 +3514,7 @@ TEST_P(QuicConnectionTest, NewTimeoutAfterSendSilentClose) { SendStreamDataToPeer(kClientDataStreamId1, "foo", 0, FIN, nullptr); EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline()); - // Now send more data. This will not move the timeout becase + // Now send more data. This will not move the timeout because // no data has been recieved since the previous write. clock_.AdvanceTime(five_ms); SendStreamDataToPeer(kClientDataStreamId1, "foo", 3, FIN, nullptr); @@ -3842,6 +3898,64 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimation) { EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); } +TEST_P(QuicConnectionTest, SendDelayedAckDecimationUnlimitedAggregation) { + FLAGS_quic_reloadable_flag_quic_ack_decimation = true; + EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(kACKD); + // No limit on the number of packets received before sending an ack. + connection_options.push_back(kAKDU); + config.SetConnectionOptionsToSend(connection_options); + connection_.SetFromConfig(config); + + const size_t kMinRttMs = 40; + RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs), + QuicTime::Delta::Zero(), QuicTime::Zero()); + // The ack time should be based on min_rtt/4, since it's less than the + // default delayed ack time. + QuicTime ack_time = clock_.ApproximateNow() + + QuicTime::Delta::FromMilliseconds(kMinRttMs / 4); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + const uint8_t tag = 0x07; + connection_.SetDecrypter(ENCRYPTION_INITIAL, new StrictTaggingDecrypter(tag)); + peer_framer_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag)); + // Process a packet from the non-crypto stream. + frame1_.stream_id = 3; + + // Process all the initial packets in order so there aren't missing packets. + QuicPacketNumber kFirstDecimatedPacket = 101; + for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) { + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_INITIAL); + } + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + // The same as ProcessPacket(1) except that ENCRYPTION_INITIAL is used + // instead of ENCRYPTION_NONE. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting, + ENCRYPTION_INITIAL); + + // Check if delayed ack timer is running for the expected interval. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); + + // 18 packets will not cause an ack to be sent. 19 will because when + // stop waiting frames are in use, we ack every 20 packets no matter what. + for (int i = 0; i < 18; ++i) { + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting, + ENCRYPTION_INITIAL); + } + // The delayed ack timer should still be set to the expected deadline. + EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); +} + TEST_P(QuicConnectionTest, SendDelayedAckDecimationEighthRtt) { EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber()); QuicConnectionPeer::SetAckMode(&connection_, QuicConnection::ACK_DECIMATION); @@ -5255,6 +5369,16 @@ TEST_P(QuicConnectionTest, CloseConnectionForStatelessReject) { ConnectionCloseBehavior::SILENT_CLOSE); } +// Regression test for b/63620844. +TEST_P(QuicConnectionTest, FailedToWriteHandshakePacket) { + FLAGS_quic_reloadable_flag_quic_clear_packet_before_handed_over = true; + SimulateNextPacketTooLarge(); + EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PACKET_WRITE_ERROR, _, + ConnectionCloseSource::FROM_SELF)) + .Times(1); + connection_.SendCryptoStreamData(); +} + } // namespace } // namespace test } // namespace net diff --git a/chromium/net/quic/core/quic_constants.h b/chromium/net/quic/core/quic_constants.h index 8fcf115de00..9dd96c78baf 100644 --- a/chromium/net/quic/core/quic_constants.h +++ b/chromium/net/quic/core/quic_constants.h @@ -183,6 +183,11 @@ const QuicPacketNumber kMaxPacketGap = 5000; // The maximum number of random padding bytes to add. const QuicByteCount kMaxNumRandomPaddingBytes = 256; +// The size of stream send buffer data slice size in bytes. A data slice is +// piece of stream data stored in contiguous memory, and a stream frame can +// contain data from multiple data slices. +const QuicByteCount kQuicStreamSendBufferSliceSize = 4 * 1024; + } // namespace net #endif // NET_QUIC_CORE_QUIC_CONSTANTS_H_ diff --git a/chromium/net/quic/core/quic_crypto_client_handshaker.cc b/chromium/net/quic/core/quic_crypto_client_handshaker.cc new file mode 100644 index 00000000000..2fe6c6f6828 --- /dev/null +++ b/chromium/net/quic/core/quic_crypto_client_handshaker.cc @@ -0,0 +1,698 @@ +// 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/core/quic_crypto_client_handshaker.h" + +#include <memory> + +#include "base/metrics/histogram_macros.h" +#include "base/metrics/sparse_histogram.h" +#include "net/quic/core/crypto/crypto_protocol.h" +#include "net/quic/core/crypto/crypto_utils.h" +#include "net/quic/core/quic_session.h" +#include "net/quic/platform/api/quic_flags.h" +#include "net/quic/platform/api/quic_logging.h" +#include "net/quic/platform/api/quic_str_cat.h" + +using std::string; + +namespace net { + +QuicCryptoClientHandshaker::ChannelIDSourceCallbackImpl:: + ChannelIDSourceCallbackImpl(QuicCryptoClientHandshaker* parent) + : parent_(parent) {} + +QuicCryptoClientHandshaker::ChannelIDSourceCallbackImpl:: + ~ChannelIDSourceCallbackImpl() {} + +void QuicCryptoClientHandshaker::ChannelIDSourceCallbackImpl::Run( + std::unique_ptr<ChannelIDKey>* channel_id_key) { + if (parent_ == nullptr) { + return; + } + + parent_->channel_id_key_ = std::move(*channel_id_key); + parent_->channel_id_source_callback_run_ = true; + parent_->channel_id_source_callback_ = nullptr; + parent_->DoHandshakeLoop(nullptr); + + // The ChannelIDSource owns this object and will delete it when this method + // returns. +} + +void QuicCryptoClientHandshaker::ChannelIDSourceCallbackImpl::Cancel() { + parent_ = nullptr; +} + +QuicCryptoClientHandshaker::ProofVerifierCallbackImpl:: + ProofVerifierCallbackImpl(QuicCryptoClientHandshaker* parent) + : parent_(parent) {} + +QuicCryptoClientHandshaker::ProofVerifierCallbackImpl:: + ~ProofVerifierCallbackImpl() {} + +void QuicCryptoClientHandshaker::ProofVerifierCallbackImpl::Run( + bool ok, + const string& error_details, + std::unique_ptr<ProofVerifyDetails>* details) { + if (parent_ == nullptr) { + return; + } + + parent_->verify_ok_ = ok; + parent_->verify_error_details_ = error_details; + parent_->verify_details_ = std::move(*details); + parent_->proof_verify_callback_ = nullptr; + parent_->DoHandshakeLoop(nullptr); + + // The ProofVerifier owns this object and will delete it when this method + // returns. +} + +void QuicCryptoClientHandshaker::ProofVerifierCallbackImpl::Cancel() { + parent_ = nullptr; +} + +QuicCryptoClientHandshaker::QuicCryptoClientHandshaker( + const QuicServerId& server_id, + QuicCryptoClientStream* stream, + QuicSession* session, + ProofVerifyContext* verify_context, + QuicCryptoClientConfig* crypto_config, + QuicCryptoClientStream::ProofHandler* proof_handler) + : QuicCryptoHandshaker(stream, session), + stream_(stream), + session_(session), + next_state_(STATE_IDLE), + num_client_hellos_(0), + crypto_config_(crypto_config), + server_id_(server_id), + generation_counter_(0), + channel_id_sent_(false), + channel_id_source_callback_run_(false), + channel_id_source_callback_(nullptr), + verify_context_(verify_context), + proof_verify_callback_(nullptr), + proof_handler_(proof_handler), + verify_ok_(false), + stateless_reject_received_(false), + num_scup_messages_received_(0), + encryption_established_(false), + handshake_confirmed_(false), + crypto_negotiated_params_(new QuicCryptoNegotiatedParameters) {} + +QuicCryptoClientHandshaker::~QuicCryptoClientHandshaker() { + if (channel_id_source_callback_) { + channel_id_source_callback_->Cancel(); + } + if (proof_verify_callback_) { + proof_verify_callback_->Cancel(); + } +} + +void QuicCryptoClientHandshaker::OnHandshakeMessage( + const CryptoHandshakeMessage& message) { + QuicCryptoHandshaker::OnHandshakeMessage(message); + if (message.tag() == kSCUP) { + if (!handshake_confirmed()) { + stream_->CloseConnectionWithDetails( + QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE, + "Early SCUP disallowed"); + return; + } + + // |message| is an update from the server, so we treat it differently from a + // handshake message. + HandleServerConfigUpdateMessage(message); + num_scup_messages_received_++; + return; + } + + // Do not process handshake messages after the handshake is confirmed. + if (handshake_confirmed()) { + stream_->CloseConnectionWithDetails( + QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE, + "Unexpected handshake message"); + return; + } + + DoHandshakeLoop(&message); +} + +bool QuicCryptoClientHandshaker::CryptoConnect() { + next_state_ = STATE_INITIALIZE; + DoHandshakeLoop(nullptr); + return session()->connection()->connected(); +} + +int QuicCryptoClientHandshaker::num_sent_client_hellos() const { + return num_client_hellos_; +} + +int QuicCryptoClientHandshaker::num_scup_messages_received() const { + return num_scup_messages_received_; +} + +bool QuicCryptoClientHandshaker::WasChannelIDSent() const { + return channel_id_sent_; +} + +bool QuicCryptoClientHandshaker::WasChannelIDSourceCallbackRun() const { + return channel_id_source_callback_run_; +} + +string QuicCryptoClientHandshaker::chlo_hash() const { + return chlo_hash_; +} + +bool QuicCryptoClientHandshaker::encryption_established() const { + return encryption_established_; +} + +bool QuicCryptoClientHandshaker::handshake_confirmed() const { + return handshake_confirmed_; +} + +const QuicCryptoNegotiatedParameters& +QuicCryptoClientHandshaker::crypto_negotiated_params() const { + return *crypto_negotiated_params_; +} + +CryptoMessageParser* QuicCryptoClientHandshaker::crypto_message_parser() { + return QuicCryptoHandshaker::crypto_message_parser(); +} + +void QuicCryptoClientHandshaker::HandleServerConfigUpdateMessage( + const CryptoHandshakeMessage& server_config_update) { + DCHECK(server_config_update.tag() == kSCUP); + string error_details; + QuicCryptoClientConfig::CachedState* cached = + crypto_config_->LookupOrCreate(server_id_); + QuicErrorCode error = crypto_config_->ProcessServerConfigUpdate( + server_config_update, session()->connection()->clock()->WallNow(), + session()->connection()->version(), chlo_hash_, cached, + crypto_negotiated_params_, &error_details); + + if (error != QUIC_NO_ERROR) { + stream_->CloseConnectionWithDetails( + error, "Server config update invalid: " + error_details); + return; + } + + DCHECK(handshake_confirmed()); + if (proof_verify_callback_) { + proof_verify_callback_->Cancel(); + } + next_state_ = STATE_INITIALIZE_SCUP; + DoHandshakeLoop(nullptr); +} + +void QuicCryptoClientHandshaker::DoHandshakeLoop( + const CryptoHandshakeMessage* in) { + QuicCryptoClientConfig::CachedState* cached = + crypto_config_->LookupOrCreate(server_id_); + + QuicAsyncStatus rv = QUIC_SUCCESS; + do { + CHECK_NE(STATE_NONE, next_state_); + const State state = next_state_; + next_state_ = STATE_IDLE; + rv = QUIC_SUCCESS; + switch (state) { + case STATE_INITIALIZE: + DoInitialize(cached); + break; + case STATE_SEND_CHLO: + DoSendCHLO(cached); + return; // return waiting to hear from server. + case STATE_RECV_REJ: + DoReceiveREJ(in, cached); + break; + case STATE_VERIFY_PROOF: + rv = DoVerifyProof(cached); + break; + case STATE_VERIFY_PROOF_COMPLETE: + DoVerifyProofComplete(cached); + break; + case STATE_GET_CHANNEL_ID: + rv = DoGetChannelID(cached); + break; + case STATE_GET_CHANNEL_ID_COMPLETE: + DoGetChannelIDComplete(); + break; + case STATE_RECV_SHLO: + DoReceiveSHLO(in, cached); + break; + case STATE_IDLE: + // This means that the peer sent us a message that we weren't expecting. + stream_->CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, + "Handshake in idle state"); + return; + case STATE_INITIALIZE_SCUP: + DoInitializeServerConfigUpdate(cached); + break; + case STATE_NONE: + QUIC_NOTREACHED(); + return; // We are done. + } + } while (rv != QUIC_PENDING && next_state_ != STATE_NONE); +} + +void QuicCryptoClientHandshaker::DoInitialize( + QuicCryptoClientConfig::CachedState* cached) { + if (!cached->IsEmpty() && !cached->signature().empty()) { + // Note that we verify the proof even if the cached proof is valid. + // This allows us to respond to CA trust changes or certificate + // expiration because it may have been a while since we last verified + // the proof. + DCHECK(crypto_config_->proof_verifier()); + // Track proof verification time when cached server config is used. + proof_verify_start_time_ = base::TimeTicks::Now(); + chlo_hash_ = cached->chlo_hash(); + // If the cached state needs to be verified, do it now. + next_state_ = STATE_VERIFY_PROOF; + } else { + next_state_ = STATE_GET_CHANNEL_ID; + } +} + +void QuicCryptoClientHandshaker::DoSendCHLO( + QuicCryptoClientConfig::CachedState* cached) { + if (stateless_reject_received_) { + // If we've gotten to this point, we've sent at least one hello + // and received a stateless reject in response. We cannot + // continue to send hellos because the server has abandoned state + // for this connection. Abandon further handshakes. + next_state_ = STATE_NONE; + if (session()->connection()->connected()) { + session()->connection()->CloseConnection( + QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, "stateless reject received", + ConnectionCloseBehavior::SILENT_CLOSE); + } + return; + } + + // Send the client hello in plaintext. + session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_NONE); + encryption_established_ = false; + if (num_client_hellos_ > QuicCryptoClientStream::kMaxClientHellos) { + stream_->CloseConnectionWithDetails( + QUIC_CRYPTO_TOO_MANY_REJECTS, + QuicStrCat("More than ", QuicCryptoClientStream::kMaxClientHellos, + " rejects")); + return; + } + num_client_hellos_++; + + CryptoHandshakeMessage out; + DCHECK(session() != nullptr); + DCHECK(session()->config() != nullptr); + // Send all the options, regardless of whether we're sending an + // inchoate or subsequent hello. + session()->config()->ToHandshakeMessage(&out); + + // Send a local timestamp to the server. + out.SetValue(kCTIM, + session()->connection()->clock()->WallNow().ToUNIXSeconds()); + + if (!cached->IsComplete(session()->connection()->clock()->WallNow())) { + crypto_config_->FillInchoateClientHello( + server_id_, session()->connection()->supported_versions().front(), + cached, session()->connection()->random_generator(), + /* demand_x509_proof= */ true, crypto_negotiated_params_, &out); + // Pad the inchoate client hello to fill up a packet. + const QuicByteCount kFramingOverhead = 50; // A rough estimate. + const QuicByteCount max_packet_size = + session()->connection()->max_packet_length(); + if (max_packet_size <= kFramingOverhead) { + QUIC_DLOG(DFATAL) << "max_packet_length (" << max_packet_size + << ") has no room for framing overhead."; + stream_->CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, + "max_packet_size too smalll"); + return; + } + if (kClientHelloMinimumSize > max_packet_size - kFramingOverhead) { + QUIC_DLOG(DFATAL) << "Client hello won't fit in a single packet."; + stream_->CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, + "CHLO too large"); + return; + } + // TODO(rch): Remove this when we remove: + // FLAGS_quic_reloadable_flag_quic_use_chlo_packet_size + out.set_minimum_size( + static_cast<size_t>(max_packet_size - kFramingOverhead)); + next_state_ = STATE_RECV_REJ; + CryptoUtils::HashHandshakeMessage(out, &chlo_hash_, Perspective::IS_CLIENT); + SendHandshakeMessage(out); + return; + } + + // If the server nonce is empty, copy over the server nonce from a previous + // SREJ, if there is one. + if (FLAGS_quic_reloadable_flag_enable_quic_stateless_reject_support && + crypto_negotiated_params_->server_nonce.empty() && + cached->has_server_nonce()) { + crypto_negotiated_params_->server_nonce = cached->GetNextServerNonce(); + DCHECK(!crypto_negotiated_params_->server_nonce.empty()); + } + + string error_details; + QuicErrorCode error = crypto_config_->FillClientHello( + server_id_, session()->connection()->connection_id(), + session()->connection()->supported_versions().front(), cached, + session()->connection()->clock()->WallNow(), + session()->connection()->random_generator(), channel_id_key_.get(), + crypto_negotiated_params_, &out, &error_details); + if (error != QUIC_NO_ERROR) { + // Flush the cached config so that, if it's bad, the server has a + // chance to send us another in the future. + cached->InvalidateServerConfig(); + stream_->CloseConnectionWithDetails(error, error_details); + return; + } + CryptoUtils::HashHandshakeMessage(out, &chlo_hash_, Perspective::IS_CLIENT); + channel_id_sent_ = (channel_id_key_.get() != nullptr); + if (cached->proof_verify_details()) { + proof_handler_->OnProofVerifyDetailsAvailable( + *cached->proof_verify_details()); + } + next_state_ = STATE_RECV_SHLO; + SendHandshakeMessage(out); + // Be prepared to decrypt with the new server write key. + session()->connection()->SetAlternativeDecrypter( + ENCRYPTION_INITIAL, + crypto_negotiated_params_->initial_crypters.decrypter.release(), + true /* latch once used */); + // Send subsequent packets under encryption on the assumption that the + // server will accept the handshake. + session()->connection()->SetEncrypter( + ENCRYPTION_INITIAL, + crypto_negotiated_params_->initial_crypters.encrypter.release()); + session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); + + // TODO(ianswett): Merge ENCRYPTION_REESTABLISHED and + // ENCRYPTION_FIRST_ESTABLSIHED + encryption_established_ = true; + session()->OnCryptoHandshakeEvent(QuicSession::ENCRYPTION_REESTABLISHED); +} + +void QuicCryptoClientHandshaker::DoReceiveREJ( + const CryptoHandshakeMessage* in, + QuicCryptoClientConfig::CachedState* cached) { + // We sent a dummy CHLO because we didn't have enough information to + // perform a handshake, or we sent a full hello that the server + // rejected. Here we hope to have a REJ that contains the information + // that we need. + if ((in->tag() != kREJ) && (in->tag() != kSREJ)) { + next_state_ = STATE_NONE; + stream_->CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, + "Expected REJ"); + return; + } + + QuicTagVector reject_reasons; + static_assert(sizeof(QuicTag) == sizeof(uint32_t), "header out of sync"); + if (in->GetTaglist(kRREJ, &reject_reasons) == QUIC_NO_ERROR) { + uint32_t packed_error = 0; + for (size_t i = 0; i < reject_reasons.size(); ++i) { + // HANDSHAKE_OK is 0 and don't report that as error. + if (reject_reasons[i] == HANDSHAKE_OK || reject_reasons[i] >= 32) { + continue; + } + HandshakeFailureReason reason = + static_cast<HandshakeFailureReason>(reject_reasons[i]); + packed_error |= 1 << (reason - 1); + } + DVLOG(1) << "Reasons for rejection: " << packed_error; + if (num_client_hellos_ == QuicCryptoClientStream::kMaxClientHellos) { + UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicClientHelloRejectReasons.TooMany", + packed_error); + } + UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicClientHelloRejectReasons.Secure", + packed_error); + } + + // Receipt of a REJ message means that the server received the CHLO + // so we can cancel and retransmissions. + session()->connection()->NeuterUnencryptedPackets(); + + stateless_reject_received_ = in->tag() == kSREJ; + string error_details; + QuicErrorCode error = crypto_config_->ProcessRejection( + *in, session()->connection()->clock()->WallNow(), + session()->connection()->version(), chlo_hash_, cached, + crypto_negotiated_params_, &error_details); + + if (error != QUIC_NO_ERROR) { + next_state_ = STATE_NONE; + stream_->CloseConnectionWithDetails(error, error_details); + return; + } + if (!cached->proof_valid()) { + if (!cached->signature().empty()) { + // Note that we only verify the proof if the cached proof is not + // valid. If the cached proof is valid here, someone else must have + // just added the server config to the cache and verified the proof, + // so we can assume no CA trust changes or certificate expiration + // has happened since then. + next_state_ = STATE_VERIFY_PROOF; + return; + } + } + next_state_ = STATE_GET_CHANNEL_ID; +} + +QuicAsyncStatus QuicCryptoClientHandshaker::DoVerifyProof( + QuicCryptoClientConfig::CachedState* cached) { + ProofVerifier* verifier = crypto_config_->proof_verifier(); + DCHECK(verifier); + next_state_ = STATE_VERIFY_PROOF_COMPLETE; + generation_counter_ = cached->generation_counter(); + + ProofVerifierCallbackImpl* proof_verify_callback = + new ProofVerifierCallbackImpl(this); + + verify_ok_ = false; + + QuicAsyncStatus status = verifier->VerifyProof( + server_id_.host(), server_id_.port(), cached->server_config(), + session()->connection()->version(), chlo_hash_, cached->certs(), + cached->cert_sct(), cached->signature(), verify_context_.get(), + &verify_error_details_, &verify_details_, + std::unique_ptr<ProofVerifierCallback>(proof_verify_callback)); + + switch (status) { + case QUIC_PENDING: + proof_verify_callback_ = proof_verify_callback; + QUIC_DVLOG(1) << "Doing VerifyProof"; + break; + case QUIC_FAILURE: + break; + case QUIC_SUCCESS: + verify_ok_ = true; + break; + } + return status; +} + +void QuicCryptoClientHandshaker::DoVerifyProofComplete( + QuicCryptoClientConfig::CachedState* cached) { + if (!proof_verify_start_time_.is_null()) { + UMA_HISTOGRAM_TIMES("Net.QuicSession.VerifyProofTime.CachedServerConfig", + base::TimeTicks::Now() - proof_verify_start_time_); + } + if (!verify_ok_) { + if (verify_details_.get()) { + proof_handler_->OnProofVerifyDetailsAvailable(*verify_details_); + } + if (num_client_hellos_ == 0) { + cached->Clear(); + next_state_ = STATE_INITIALIZE; + return; + } + next_state_ = STATE_NONE; + UMA_HISTOGRAM_BOOLEAN("Net.QuicVerifyProofFailed.HandshakeConfirmed", + handshake_confirmed()); + stream_->CloseConnectionWithDetails( + QUIC_PROOF_INVALID, "Proof invalid: " + verify_error_details_); + return; + } + + // Check if generation_counter has changed between STATE_VERIFY_PROOF and + // STATE_VERIFY_PROOF_COMPLETE state changes. + if (generation_counter_ != cached->generation_counter()) { + next_state_ = STATE_VERIFY_PROOF; + } else { + SetCachedProofValid(cached); + cached->SetProofVerifyDetails(verify_details_.release()); + if (!handshake_confirmed()) { + next_state_ = STATE_GET_CHANNEL_ID; + } else { + // TODO: Enable Expect-Staple. https://crbug.com/631101 + next_state_ = STATE_NONE; + } + } +} + +QuicAsyncStatus QuicCryptoClientHandshaker::DoGetChannelID( + QuicCryptoClientConfig::CachedState* cached) { + next_state_ = STATE_GET_CHANNEL_ID_COMPLETE; + channel_id_key_.reset(); + if (!RequiresChannelID(cached)) { + next_state_ = STATE_SEND_CHLO; + return QUIC_SUCCESS; + } + + ChannelIDSourceCallbackImpl* channel_id_source_callback = + new ChannelIDSourceCallbackImpl(this); + QuicAsyncStatus status = crypto_config_->channel_id_source()->GetChannelIDKey( + server_id_.host(), &channel_id_key_, channel_id_source_callback); + + switch (status) { + case QUIC_PENDING: + channel_id_source_callback_ = channel_id_source_callback; + QUIC_DVLOG(1) << "Looking up channel ID"; + break; + case QUIC_FAILURE: + next_state_ = STATE_NONE; + delete channel_id_source_callback; + stream_->CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE, + "Channel ID lookup failed"); + break; + case QUIC_SUCCESS: + delete channel_id_source_callback; + break; + } + return status; +} + +void QuicCryptoClientHandshaker::DoGetChannelIDComplete() { + if (!channel_id_key_.get()) { + next_state_ = STATE_NONE; + stream_->CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE, + "Channel ID lookup failed"); + return; + } + next_state_ = STATE_SEND_CHLO; +} + +void QuicCryptoClientHandshaker::DoReceiveSHLO( + const CryptoHandshakeMessage* in, + QuicCryptoClientConfig::CachedState* cached) { + next_state_ = STATE_NONE; + // We sent a CHLO that we expected to be accepted and now we're + // hoping for a SHLO from the server to confirm that. First check + // to see whether the response was a reject, and if so, move on to + // the reject-processing state. + if ((in->tag() == kREJ) || (in->tag() == kSREJ)) { + // alternative_decrypter will be nullptr if the original alternative + // decrypter latched and became the primary decrypter. That happens + // if we received a message encrypted with the INITIAL key. + if (session()->connection()->alternative_decrypter() == nullptr) { + // The rejection was sent encrypted! + stream_->CloseConnectionWithDetails( + QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT, "encrypted REJ message"); + return; + } + next_state_ = STATE_RECV_REJ; + return; + } + + if (in->tag() != kSHLO) { + stream_->CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, + "Expected SHLO or REJ"); + return; + } + + // alternative_decrypter will be nullptr if the original alternative + // decrypter latched and became the primary decrypter. That happens + // if we received a message encrypted with the INITIAL key. + if (session()->connection()->alternative_decrypter() != nullptr) { + // The server hello was sent without encryption. + stream_->CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT, + "unencrypted SHLO message"); + return; + } + + string error_details; + QuicErrorCode error = crypto_config_->ProcessServerHello( + *in, session()->connection()->connection_id(), + session()->connection()->version(), + session()->connection()->server_supported_versions(), cached, + crypto_negotiated_params_, &error_details); + + if (error != QUIC_NO_ERROR) { + stream_->CloseConnectionWithDetails( + error, "Server hello invalid: " + error_details); + return; + } + error = session()->config()->ProcessPeerHello(*in, SERVER, &error_details); + if (error != QUIC_NO_ERROR) { + stream_->CloseConnectionWithDetails( + error, "Server hello invalid: " + error_details); + return; + } + session()->OnConfigNegotiated(); + + CrypterPair* crypters = &crypto_negotiated_params_->forward_secure_crypters; + // TODO(agl): we don't currently latch this decrypter because the idea + // has been floated that the server shouldn't send packets encrypted + // with the FORWARD_SECURE key until it receives a FORWARD_SECURE + // packet from the client. + session()->connection()->SetAlternativeDecrypter( + ENCRYPTION_FORWARD_SECURE, crypters->decrypter.release(), + false /* don't latch */); + session()->connection()->SetEncrypter(ENCRYPTION_FORWARD_SECURE, + crypters->encrypter.release()); + session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + + handshake_confirmed_ = true; + session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); + session()->connection()->OnHandshakeComplete(); +} + +void QuicCryptoClientHandshaker::DoInitializeServerConfigUpdate( + QuicCryptoClientConfig::CachedState* cached) { + bool update_ignored = false; + if (!cached->IsEmpty() && !cached->signature().empty()) { + // Note that we verify the proof even if the cached proof is valid. + DCHECK(crypto_config_->proof_verifier()); + next_state_ = STATE_VERIFY_PROOF; + } else { + update_ignored = true; + next_state_ = STATE_NONE; + } + UMA_HISTOGRAM_COUNTS_1M("Net.QuicNumServerConfig.UpdateMessagesIgnored", + update_ignored); +} + +void QuicCryptoClientHandshaker::SetCachedProofValid( + QuicCryptoClientConfig::CachedState* cached) { + cached->SetProofValid(); + proof_handler_->OnProofValid(*cached); +} + +bool QuicCryptoClientHandshaker::RequiresChannelID( + QuicCryptoClientConfig::CachedState* cached) { + if (server_id_.privacy_mode() == PRIVACY_MODE_ENABLED || + !crypto_config_->channel_id_source()) { + return false; + } + const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); + if (!scfg) { // scfg may be null then we send an inchoate CHLO. + return false; + } + QuicTagVector their_proof_demands; + if (scfg->GetTaglist(kPDMD, &their_proof_demands) != QUIC_NO_ERROR) { + return false; + } + for (const QuicTag tag : their_proof_demands) { + if (tag == kCHID) { + return true; + } + } + return false; +} + +} // namespace net diff --git a/chromium/net/quic/core/quic_crypto_client_handshaker.h b/chromium/net/quic/core/quic_crypto_client_handshaker.h new file mode 100644 index 00000000000..ad5e79b4fc5 --- /dev/null +++ b/chromium/net/quic/core/quic_crypto_client_handshaker.h @@ -0,0 +1,240 @@ +// 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_CORE_QUIC_CRYPTO_CLIENT_HANDSHAKER_H_ +#define NET_QUIC_CORE_QUIC_CRYPTO_CLIENT_HANDSHAKER_H_ + +#include "net/quic/core/crypto/channel_id.h" +#include "net/quic/core/crypto/proof_verifier.h" +#include "net/quic/core/crypto/quic_crypto_client_config.h" +#include "net/quic/core/quic_crypto_client_stream.h" +#include "net/quic/core/quic_server_id.h" +#include "net/quic/platform/api/quic_export.h" + +namespace net { + +namespace test { +class QuicChromiumClientSessionPeer; +} // namespace test + +// An implementation of QuicCryptoClientStream::HandshakerDelegate which uses +// QUIC crypto as the crypto handshake protocol. +class QUIC_EXPORT_PRIVATE QuicCryptoClientHandshaker + : public QuicCryptoClientStream::HandshakerDelegate, + public QuicCryptoHandshaker { + public: + QuicCryptoClientHandshaker( + const QuicServerId& server_id, + QuicCryptoClientStream* stream, + QuicSession* session, + ProofVerifyContext* verify_context, + QuicCryptoClientConfig* crypto_config, + QuicCryptoClientStream::ProofHandler* proof_handler); + + ~QuicCryptoClientHandshaker() override; + + // From QuicCryptoClientStream::HandshakerDelegate + bool CryptoConnect() override; + int num_sent_client_hellos() const override; + int num_scup_messages_received() const override; + bool WasChannelIDSent() const override; + bool WasChannelIDSourceCallbackRun() const override; + std::string chlo_hash() const override; + bool encryption_established() const override; + bool handshake_confirmed() const override; + const QuicCryptoNegotiatedParameters& crypto_negotiated_params() + const override; + CryptoMessageParser* crypto_message_parser() override; + + // From QuicCryptoHandshaker + void OnHandshakeMessage(const CryptoHandshakeMessage& message) override; + + private: + // ChannelIDSourceCallbackImpl is passed as the callback method to + // GetChannelIDKey. The ChannelIDSource calls this class with the result of + // channel ID lookup when lookup is performed asynchronously. + class ChannelIDSourceCallbackImpl : public ChannelIDSourceCallback { + public: + explicit ChannelIDSourceCallbackImpl(QuicCryptoClientHandshaker* parent); + ~ChannelIDSourceCallbackImpl() override; + + // ChannelIDSourceCallback interface. + void Run(std::unique_ptr<ChannelIDKey>* channel_id_key) override; + + // Cancel causes any future callbacks to be ignored. It must be called on + // the same thread as the callback will be made on. + void Cancel(); + + private: + QuicCryptoClientHandshaker* parent_; + }; + + // ProofVerifierCallbackImpl is passed as the callback method to VerifyProof. + // The ProofVerifier calls this class with the result of proof verification + // when verification is performed asynchronously. + class ProofVerifierCallbackImpl : public ProofVerifierCallback { + public: + explicit ProofVerifierCallbackImpl(QuicCryptoClientHandshaker* parent); + ~ProofVerifierCallbackImpl() override; + + // ProofVerifierCallback interface. + void Run(bool ok, + const std::string& error_details, + std::unique_ptr<ProofVerifyDetails>* details) override; + + // Cancel causes any future callbacks to be ignored. It must be called on + // the same thread as the callback will be made on. + void Cancel(); + + private: + QuicCryptoClientHandshaker* parent_; + }; + + friend class test::QuicChromiumClientSessionPeer; + + enum State { + STATE_IDLE, + STATE_INITIALIZE, + STATE_SEND_CHLO, + STATE_RECV_REJ, + STATE_VERIFY_PROOF, + STATE_VERIFY_PROOF_COMPLETE, + STATE_GET_CHANNEL_ID, + STATE_GET_CHANNEL_ID_COMPLETE, + STATE_RECV_SHLO, + STATE_INITIALIZE_SCUP, + STATE_NONE, + }; + + // Handles new server config and optional source-address token provided by the + // server during a connection. + void HandleServerConfigUpdateMessage( + const CryptoHandshakeMessage& server_config_update); + + // DoHandshakeLoop performs a step of the handshake state machine. Note that + // |in| may be nullptr if the call did not result from a received message. + void DoHandshakeLoop(const CryptoHandshakeMessage* in); + + // Start the handshake process. + void DoInitialize(QuicCryptoClientConfig::CachedState* cached); + + // Send either InchoateClientHello or ClientHello message to the server. + void DoSendCHLO(QuicCryptoClientConfig::CachedState* cached); + + // Process REJ message from the server. + void DoReceiveREJ(const CryptoHandshakeMessage* in, + QuicCryptoClientConfig::CachedState* cached); + + // Start the proof verification process. Returns the QuicAsyncStatus returned + // by the ProofVerifier's VerifyProof. + QuicAsyncStatus DoVerifyProof(QuicCryptoClientConfig::CachedState* cached); + + // If proof is valid then it sets the proof as valid (which persists the + // server config). If not, it closes the connection. + void DoVerifyProofComplete(QuicCryptoClientConfig::CachedState* cached); + + // Start the look up of Channel ID process. Returns either QUIC_SUCCESS if + // RequiresChannelID returns false or QuicAsyncStatus returned by + // GetChannelIDKey. + QuicAsyncStatus DoGetChannelID(QuicCryptoClientConfig::CachedState* cached); + + // If there is no channel ID, then close the connection otherwise transtion to + // STATE_SEND_CHLO state. + void DoGetChannelIDComplete(); + + // Process SHLO message from the server. + void DoReceiveSHLO(const CryptoHandshakeMessage* in, + QuicCryptoClientConfig::CachedState* cached); + + // Start the proof verification if |server_id_| is https and |cached| has + // signature. + void DoInitializeServerConfigUpdate( + QuicCryptoClientConfig::CachedState* cached); + + // Called to set the proof of |cached| valid. Also invokes the session's + // OnProofValid() method. + void SetCachedProofValid(QuicCryptoClientConfig::CachedState* cached); + + // Returns true if the server crypto config in |cached| requires a ChannelID + // and the client config settings also allow sending a ChannelID. + bool RequiresChannelID(QuicCryptoClientConfig::CachedState* cached); + + // Returns the QuicSession that this stream belongs to. + QuicSession* session() const { return session_; } + + QuicCryptoClientStream* stream_; + + QuicSession* session_; + + State next_state_; + // num_client_hellos_ contains the number of client hello messages that this + // connection has sent. + int num_client_hellos_; + + QuicCryptoClientConfig* const crypto_config_; + + // SHA-256 hash of the most recently sent CHLO. + std::string chlo_hash_; + + // Server's (hostname, port, is_https, privacy_mode) tuple. + const QuicServerId server_id_; + + // Generation counter from QuicCryptoClientConfig's CachedState. + uint64_t generation_counter_; + + // True if a channel ID was sent. + bool channel_id_sent_; + + // True if channel_id_source_callback_ was run. + bool channel_id_source_callback_run_; + + // channel_id_source_callback_ contains the callback object that we passed + // to an asynchronous channel ID lookup. The ChannelIDSource owns this + // object. + ChannelIDSourceCallbackImpl* channel_id_source_callback_; + + // These members are used to store the result of an asynchronous channel ID + // lookup. These members must not be used after + // STATE_GET_CHANNEL_ID_COMPLETE. + std::unique_ptr<ChannelIDKey> channel_id_key_; + + // verify_context_ contains the context object that we pass to asynchronous + // proof verifications. + std::unique_ptr<ProofVerifyContext> verify_context_; + + // proof_verify_callback_ contains the callback object that we passed to an + // asynchronous proof verification. The ProofVerifier owns this object. + ProofVerifierCallbackImpl* proof_verify_callback_; + // proof_handler_ contains the callback object used by a quic client + // for proof verification. It is not owned by this class. + QuicCryptoClientStream::ProofHandler* proof_handler_; + + // These members are used to store the result of an asynchronous proof + // verification. These members must not be used after + // STATE_VERIFY_PROOF_COMPLETE. + bool verify_ok_; + std::string verify_error_details_; + std::unique_ptr<ProofVerifyDetails> verify_details_; + + // True if the server responded to a previous CHLO with a stateless + // reject. Used for book-keeping between the STATE_RECV_REJ, + // STATE_VERIFY_PROOF*, and subsequent STATE_SEND_CHLO state. + bool stateless_reject_received_; + + // Only used in chromium, not internally. + base::TimeTicks proof_verify_start_time_; + + int num_scup_messages_received_; + + bool encryption_established_; + bool handshake_confirmed_; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> + crypto_negotiated_params_; + + DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientHandshaker); +}; + +} // namespace net + +#endif // NET_QUIC_CORE_QUIC_CRYPTO_CLIENT_HANDSHAKER_H_ diff --git a/chromium/net/quic/core/quic_crypto_client_stream.cc b/chromium/net/quic/core/quic_crypto_client_stream.cc index 251d13019cd..1599716cac8 100644 --- a/chromium/net/quic/core/quic_crypto_client_stream.cc +++ b/chromium/net/quic/core/quic_crypto_client_stream.cc @@ -11,6 +11,7 @@ #include "net/quic/core/crypto/crypto_protocol.h" #include "net/quic/core/crypto/crypto_utils.h" #include "net/quic/core/crypto/null_encrypter.h" +#include "net/quic/core/quic_crypto_client_handshaker.h" #include "net/quic/core/quic_packets.h" #include "net/quic/core/quic_session.h" #include "net/quic/core/quic_utils.h" @@ -27,61 +28,6 @@ const int QuicCryptoClientStream::kMaxClientHellos; QuicCryptoClientStreamBase::QuicCryptoClientStreamBase(QuicSession* session) : QuicCryptoStream(session) {} -QuicCryptoClientHandshaker::ChannelIDSourceCallbackImpl:: - ChannelIDSourceCallbackImpl(QuicCryptoClientHandshaker* parent) - : parent_(parent) {} - -QuicCryptoClientHandshaker::ChannelIDSourceCallbackImpl:: - ~ChannelIDSourceCallbackImpl() {} - -void QuicCryptoClientHandshaker::ChannelIDSourceCallbackImpl::Run( - std::unique_ptr<ChannelIDKey>* channel_id_key) { - if (parent_ == nullptr) { - return; - } - - parent_->channel_id_key_ = std::move(*channel_id_key); - parent_->channel_id_source_callback_run_ = true; - parent_->channel_id_source_callback_ = nullptr; - parent_->DoHandshakeLoop(nullptr); - - // The ChannelIDSource owns this object and will delete it when this method - // returns. -} - -void QuicCryptoClientHandshaker::ChannelIDSourceCallbackImpl::Cancel() { - parent_ = nullptr; -} - -QuicCryptoClientHandshaker::ProofVerifierCallbackImpl:: - ProofVerifierCallbackImpl(QuicCryptoClientHandshaker* parent) - : parent_(parent) {} - -QuicCryptoClientHandshaker::ProofVerifierCallbackImpl:: - ~ProofVerifierCallbackImpl() {} - -void QuicCryptoClientHandshaker::ProofVerifierCallbackImpl::Run( - bool ok, - const string& error_details, - std::unique_ptr<ProofVerifyDetails>* details) { - if (parent_ == nullptr) { - return; - } - - parent_->verify_ok_ = ok; - parent_->verify_error_details_ = error_details; - parent_->verify_details_ = std::move(*details); - parent_->proof_verify_callback_ = nullptr; - parent_->DoHandshakeLoop(nullptr); - - // The ProofVerifier owns this object and will delete it when this method - // returns. -} - -void QuicCryptoClientHandshaker::ProofVerifierCallbackImpl::Cancel() { - parent_ = nullptr; -} - QuicCryptoClientStream::QuicCryptoClientStream( const QuicServerId& server_id, QuicSession* session, @@ -137,625 +83,4 @@ string QuicCryptoClientStream::chlo_hash() const { return handshaker_->chlo_hash(); } -QuicCryptoClientHandshaker::QuicCryptoClientHandshaker( - const QuicServerId& server_id, - QuicCryptoClientStream* stream, - QuicSession* session, - ProofVerifyContext* verify_context, - QuicCryptoClientConfig* crypto_config, - QuicCryptoClientStream::ProofHandler* proof_handler) - : QuicCryptoHandshaker(stream, session), - stream_(stream), - session_(session), - next_state_(STATE_IDLE), - num_client_hellos_(0), - crypto_config_(crypto_config), - server_id_(server_id), - generation_counter_(0), - channel_id_sent_(false), - channel_id_source_callback_run_(false), - channel_id_source_callback_(nullptr), - verify_context_(verify_context), - proof_verify_callback_(nullptr), - proof_handler_(proof_handler), - verify_ok_(false), - stateless_reject_received_(false), - num_scup_messages_received_(0), - encryption_established_(false), - handshake_confirmed_(false), - crypto_negotiated_params_(new QuicCryptoNegotiatedParameters) {} - -QuicCryptoClientHandshaker::~QuicCryptoClientHandshaker() { - if (channel_id_source_callback_) { - channel_id_source_callback_->Cancel(); - } - if (proof_verify_callback_) { - proof_verify_callback_->Cancel(); - } -} - -void QuicCryptoClientHandshaker::OnHandshakeMessage( - const CryptoHandshakeMessage& message) { - QuicCryptoHandshaker::OnHandshakeMessage(message); - if (message.tag() == kSCUP) { - if (!handshake_confirmed()) { - stream_->CloseConnectionWithDetails( - QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE, - "Early SCUP disallowed"); - return; - } - - // |message| is an update from the server, so we treat it differently from a - // handshake message. - HandleServerConfigUpdateMessage(message); - num_scup_messages_received_++; - return; - } - - // Do not process handshake messages after the handshake is confirmed. - if (handshake_confirmed()) { - stream_->CloseConnectionWithDetails( - QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE, - "Unexpected handshake message"); - return; - } - - DoHandshakeLoop(&message); -} - -bool QuicCryptoClientHandshaker::CryptoConnect() { - next_state_ = STATE_INITIALIZE; - DoHandshakeLoop(nullptr); - return session()->connection()->connected(); -} - -int QuicCryptoClientHandshaker::num_sent_client_hellos() const { - return num_client_hellos_; -} - -int QuicCryptoClientHandshaker::num_scup_messages_received() const { - return num_scup_messages_received_; -} - -bool QuicCryptoClientHandshaker::WasChannelIDSent() const { - return channel_id_sent_; -} - -bool QuicCryptoClientHandshaker::WasChannelIDSourceCallbackRun() const { - return channel_id_source_callback_run_; -} - -string QuicCryptoClientHandshaker::chlo_hash() const { - return chlo_hash_; -} - -bool QuicCryptoClientHandshaker::encryption_established() const { - return encryption_established_; -} - -bool QuicCryptoClientHandshaker::handshake_confirmed() const { - return handshake_confirmed_; -} - -const QuicCryptoNegotiatedParameters& -QuicCryptoClientHandshaker::crypto_negotiated_params() const { - return *crypto_negotiated_params_; -} - -CryptoMessageParser* QuicCryptoClientHandshaker::crypto_message_parser() { - return QuicCryptoHandshaker::crypto_message_parser(); -} - -void QuicCryptoClientHandshaker::HandleServerConfigUpdateMessage( - const CryptoHandshakeMessage& server_config_update) { - DCHECK(server_config_update.tag() == kSCUP); - string error_details; - QuicCryptoClientConfig::CachedState* cached = - crypto_config_->LookupOrCreate(server_id_); - QuicErrorCode error = crypto_config_->ProcessServerConfigUpdate( - server_config_update, session()->connection()->clock()->WallNow(), - session()->connection()->version(), chlo_hash_, cached, - crypto_negotiated_params_, &error_details); - - if (error != QUIC_NO_ERROR) { - stream_->CloseConnectionWithDetails( - error, "Server config update invalid: " + error_details); - return; - } - - DCHECK(handshake_confirmed()); - if (proof_verify_callback_) { - proof_verify_callback_->Cancel(); - } - next_state_ = STATE_INITIALIZE_SCUP; - DoHandshakeLoop(nullptr); -} - -void QuicCryptoClientHandshaker::DoHandshakeLoop( - const CryptoHandshakeMessage* in) { - QuicCryptoClientConfig::CachedState* cached = - crypto_config_->LookupOrCreate(server_id_); - - QuicAsyncStatus rv = QUIC_SUCCESS; - do { - CHECK_NE(STATE_NONE, next_state_); - const State state = next_state_; - next_state_ = STATE_IDLE; - rv = QUIC_SUCCESS; - switch (state) { - case STATE_INITIALIZE: - DoInitialize(cached); - break; - case STATE_SEND_CHLO: - DoSendCHLO(cached); - return; // return waiting to hear from server. - case STATE_RECV_REJ: - DoReceiveREJ(in, cached); - break; - case STATE_VERIFY_PROOF: - rv = DoVerifyProof(cached); - break; - case STATE_VERIFY_PROOF_COMPLETE: - DoVerifyProofComplete(cached); - break; - case STATE_GET_CHANNEL_ID: - rv = DoGetChannelID(cached); - break; - case STATE_GET_CHANNEL_ID_COMPLETE: - DoGetChannelIDComplete(); - break; - case STATE_RECV_SHLO: - DoReceiveSHLO(in, cached); - break; - case STATE_IDLE: - // This means that the peer sent us a message that we weren't expecting. - stream_->CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, - "Handshake in idle state"); - return; - case STATE_INITIALIZE_SCUP: - DoInitializeServerConfigUpdate(cached); - break; - case STATE_NONE: - QUIC_NOTREACHED(); - return; // We are done. - } - } while (rv != QUIC_PENDING && next_state_ != STATE_NONE); -} - -void QuicCryptoClientHandshaker::DoInitialize( - QuicCryptoClientConfig::CachedState* cached) { - if (!cached->IsEmpty() && !cached->signature().empty()) { - // Note that we verify the proof even if the cached proof is valid. - // This allows us to respond to CA trust changes or certificate - // expiration because it may have been a while since we last verified - // the proof. - DCHECK(crypto_config_->proof_verifier()); - // Track proof verification time when cached server config is used. - proof_verify_start_time_ = base::TimeTicks::Now(); - chlo_hash_ = cached->chlo_hash(); - // If the cached state needs to be verified, do it now. - next_state_ = STATE_VERIFY_PROOF; - } else { - next_state_ = STATE_GET_CHANNEL_ID; - } -} - -void QuicCryptoClientHandshaker::DoSendCHLO( - QuicCryptoClientConfig::CachedState* cached) { - if (stateless_reject_received_) { - // If we've gotten to this point, we've sent at least one hello - // and received a stateless reject in response. We cannot - // continue to send hellos because the server has abandoned state - // for this connection. Abandon further handshakes. - next_state_ = STATE_NONE; - if (session()->connection()->connected()) { - session()->connection()->CloseConnection( - QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, "stateless reject received", - ConnectionCloseBehavior::SILENT_CLOSE); - } - return; - } - - // Send the client hello in plaintext. - session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_NONE); - encryption_established_ = false; - if (num_client_hellos_ > QuicCryptoClientStream::kMaxClientHellos) { - stream_->CloseConnectionWithDetails( - QUIC_CRYPTO_TOO_MANY_REJECTS, - QuicStrCat("More than ", QuicCryptoClientStream::kMaxClientHellos, - " rejects")); - return; - } - num_client_hellos_++; - - CryptoHandshakeMessage out; - DCHECK(session() != nullptr); - DCHECK(session()->config() != nullptr); - // Send all the options, regardless of whether we're sending an - // inchoate or subsequent hello. - session()->config()->ToHandshakeMessage(&out); - - // Send a local timestamp to the server. - out.SetValue(kCTIM, - session()->connection()->clock()->WallNow().ToUNIXSeconds()); - - if (!cached->IsComplete(session()->connection()->clock()->WallNow())) { - crypto_config_->FillInchoateClientHello( - server_id_, session()->connection()->supported_versions().front(), - cached, session()->connection()->random_generator(), - /* demand_x509_proof= */ true, crypto_negotiated_params_, &out); - // Pad the inchoate client hello to fill up a packet. - const QuicByteCount kFramingOverhead = 50; // A rough estimate. - const QuicByteCount max_packet_size = - session()->connection()->max_packet_length(); - if (max_packet_size <= kFramingOverhead) { - QUIC_DLOG(DFATAL) << "max_packet_length (" << max_packet_size - << ") has no room for framing overhead."; - stream_->CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, - "max_packet_size too smalll"); - return; - } - if (kClientHelloMinimumSize > max_packet_size - kFramingOverhead) { - QUIC_DLOG(DFATAL) << "Client hello won't fit in a single packet."; - stream_->CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, - "CHLO too large"); - return; - } - // TODO(rch): Remove this when we remove: - // FLAGS_quic_reloadable_flag_quic_use_chlo_packet_size - out.set_minimum_size( - static_cast<size_t>(max_packet_size - kFramingOverhead)); - next_state_ = STATE_RECV_REJ; - CryptoUtils::HashHandshakeMessage(out, &chlo_hash_, Perspective::IS_CLIENT); - SendHandshakeMessage(out); - return; - } - - // If the server nonce is empty, copy over the server nonce from a previous - // SREJ, if there is one. - if (FLAGS_quic_reloadable_flag_enable_quic_stateless_reject_support && - crypto_negotiated_params_->server_nonce.empty() && - cached->has_server_nonce()) { - crypto_negotiated_params_->server_nonce = cached->GetNextServerNonce(); - DCHECK(!crypto_negotiated_params_->server_nonce.empty()); - } - - string error_details; - QuicErrorCode error = crypto_config_->FillClientHello( - server_id_, session()->connection()->connection_id(), - session()->connection()->supported_versions().front(), cached, - session()->connection()->clock()->WallNow(), - session()->connection()->random_generator(), channel_id_key_.get(), - crypto_negotiated_params_, &out, &error_details); - if (error != QUIC_NO_ERROR) { - // Flush the cached config so that, if it's bad, the server has a - // chance to send us another in the future. - cached->InvalidateServerConfig(); - stream_->CloseConnectionWithDetails(error, error_details); - return; - } - CryptoUtils::HashHandshakeMessage(out, &chlo_hash_, Perspective::IS_CLIENT); - channel_id_sent_ = (channel_id_key_.get() != nullptr); - if (cached->proof_verify_details()) { - proof_handler_->OnProofVerifyDetailsAvailable( - *cached->proof_verify_details()); - } - next_state_ = STATE_RECV_SHLO; - SendHandshakeMessage(out); - // Be prepared to decrypt with the new server write key. - session()->connection()->SetAlternativeDecrypter( - ENCRYPTION_INITIAL, - crypto_negotiated_params_->initial_crypters.decrypter.release(), - true /* latch once used */); - // Send subsequent packets under encryption on the assumption that the - // server will accept the handshake. - session()->connection()->SetEncrypter( - ENCRYPTION_INITIAL, - crypto_negotiated_params_->initial_crypters.encrypter.release()); - session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); - - // TODO(ianswett): Merge ENCRYPTION_REESTABLISHED and - // ENCRYPTION_FIRST_ESTABLSIHED - encryption_established_ = true; - session()->OnCryptoHandshakeEvent(QuicSession::ENCRYPTION_REESTABLISHED); -} - -void QuicCryptoClientHandshaker::DoReceiveREJ( - const CryptoHandshakeMessage* in, - QuicCryptoClientConfig::CachedState* cached) { - // We sent a dummy CHLO because we didn't have enough information to - // perform a handshake, or we sent a full hello that the server - // rejected. Here we hope to have a REJ that contains the information - // that we need. - if ((in->tag() != kREJ) && (in->tag() != kSREJ)) { - next_state_ = STATE_NONE; - stream_->CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, - "Expected REJ"); - return; - } - - QuicTagVector reject_reasons; - static_assert(sizeof(QuicTag) == sizeof(uint32_t), "header out of sync"); - if (in->GetTaglist(kRREJ, &reject_reasons) == QUIC_NO_ERROR) { - uint32_t packed_error = 0; - for (size_t i = 0; i < reject_reasons.size(); ++i) { - // HANDSHAKE_OK is 0 and don't report that as error. - if (reject_reasons[i] == HANDSHAKE_OK || reject_reasons[i] >= 32) { - continue; - } - HandshakeFailureReason reason = - static_cast<HandshakeFailureReason>(reject_reasons[i]); - packed_error |= 1 << (reason - 1); - } - DVLOG(1) << "Reasons for rejection: " << packed_error; - if (num_client_hellos_ == QuicCryptoClientStream::kMaxClientHellos) { - UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicClientHelloRejectReasons.TooMany", - packed_error); - } - UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicClientHelloRejectReasons.Secure", - packed_error); - } - - // Receipt of a REJ message means that the server received the CHLO - // so we can cancel and retransmissions. - session()->connection()->NeuterUnencryptedPackets(); - - stateless_reject_received_ = in->tag() == kSREJ; - string error_details; - QuicErrorCode error = crypto_config_->ProcessRejection( - *in, session()->connection()->clock()->WallNow(), - session()->connection()->version(), chlo_hash_, cached, - crypto_negotiated_params_, &error_details); - - if (error != QUIC_NO_ERROR) { - next_state_ = STATE_NONE; - stream_->CloseConnectionWithDetails(error, error_details); - return; - } - if (!cached->proof_valid()) { - if (!cached->signature().empty()) { - // Note that we only verify the proof if the cached proof is not - // valid. If the cached proof is valid here, someone else must have - // just added the server config to the cache and verified the proof, - // so we can assume no CA trust changes or certificate expiration - // has happened since then. - next_state_ = STATE_VERIFY_PROOF; - return; - } - } - next_state_ = STATE_GET_CHANNEL_ID; -} - -QuicAsyncStatus QuicCryptoClientHandshaker::DoVerifyProof( - QuicCryptoClientConfig::CachedState* cached) { - ProofVerifier* verifier = crypto_config_->proof_verifier(); - DCHECK(verifier); - next_state_ = STATE_VERIFY_PROOF_COMPLETE; - generation_counter_ = cached->generation_counter(); - - ProofVerifierCallbackImpl* proof_verify_callback = - new ProofVerifierCallbackImpl(this); - - verify_ok_ = false; - - QuicAsyncStatus status = verifier->VerifyProof( - server_id_.host(), server_id_.port(), cached->server_config(), - session()->connection()->version(), chlo_hash_, cached->certs(), - cached->cert_sct(), cached->signature(), verify_context_.get(), - &verify_error_details_, &verify_details_, - std::unique_ptr<ProofVerifierCallback>(proof_verify_callback)); - - switch (status) { - case QUIC_PENDING: - proof_verify_callback_ = proof_verify_callback; - QUIC_DVLOG(1) << "Doing VerifyProof"; - break; - case QUIC_FAILURE: - break; - case QUIC_SUCCESS: - verify_ok_ = true; - break; - } - return status; -} - -void QuicCryptoClientHandshaker::DoVerifyProofComplete( - QuicCryptoClientConfig::CachedState* cached) { - if (!proof_verify_start_time_.is_null()) { - UMA_HISTOGRAM_TIMES("Net.QuicSession.VerifyProofTime.CachedServerConfig", - base::TimeTicks::Now() - proof_verify_start_time_); - } - if (!verify_ok_) { - if (verify_details_.get()) { - proof_handler_->OnProofVerifyDetailsAvailable(*verify_details_); - } - if (num_client_hellos_ == 0) { - cached->Clear(); - next_state_ = STATE_INITIALIZE; - return; - } - next_state_ = STATE_NONE; - UMA_HISTOGRAM_BOOLEAN("Net.QuicVerifyProofFailed.HandshakeConfirmed", - handshake_confirmed()); - stream_->CloseConnectionWithDetails( - QUIC_PROOF_INVALID, "Proof invalid: " + verify_error_details_); - return; - } - - // Check if generation_counter has changed between STATE_VERIFY_PROOF and - // STATE_VERIFY_PROOF_COMPLETE state changes. - if (generation_counter_ != cached->generation_counter()) { - next_state_ = STATE_VERIFY_PROOF; - } else { - SetCachedProofValid(cached); - cached->SetProofVerifyDetails(verify_details_.release()); - if (!handshake_confirmed()) { - next_state_ = STATE_GET_CHANNEL_ID; - } else { - // TODO: Enable Expect-Staple. https://crbug.com/631101 - next_state_ = STATE_NONE; - } - } -} - -QuicAsyncStatus QuicCryptoClientHandshaker::DoGetChannelID( - QuicCryptoClientConfig::CachedState* cached) { - next_state_ = STATE_GET_CHANNEL_ID_COMPLETE; - channel_id_key_.reset(); - if (!RequiresChannelID(cached)) { - next_state_ = STATE_SEND_CHLO; - return QUIC_SUCCESS; - } - - ChannelIDSourceCallbackImpl* channel_id_source_callback = - new ChannelIDSourceCallbackImpl(this); - QuicAsyncStatus status = crypto_config_->channel_id_source()->GetChannelIDKey( - server_id_.host(), &channel_id_key_, channel_id_source_callback); - - switch (status) { - case QUIC_PENDING: - channel_id_source_callback_ = channel_id_source_callback; - QUIC_DVLOG(1) << "Looking up channel ID"; - break; - case QUIC_FAILURE: - next_state_ = STATE_NONE; - delete channel_id_source_callback; - stream_->CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE, - "Channel ID lookup failed"); - break; - case QUIC_SUCCESS: - delete channel_id_source_callback; - break; - } - return status; -} - -void QuicCryptoClientHandshaker::DoGetChannelIDComplete() { - if (!channel_id_key_.get()) { - next_state_ = STATE_NONE; - stream_->CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE, - "Channel ID lookup failed"); - return; - } - next_state_ = STATE_SEND_CHLO; -} - -void QuicCryptoClientHandshaker::DoReceiveSHLO( - const CryptoHandshakeMessage* in, - QuicCryptoClientConfig::CachedState* cached) { - next_state_ = STATE_NONE; - // We sent a CHLO that we expected to be accepted and now we're - // hoping for a SHLO from the server to confirm that. First check - // to see whether the response was a reject, and if so, move on to - // the reject-processing state. - if ((in->tag() == kREJ) || (in->tag() == kSREJ)) { - // alternative_decrypter will be nullptr if the original alternative - // decrypter latched and became the primary decrypter. That happens - // if we received a message encrypted with the INITIAL key. - if (session()->connection()->alternative_decrypter() == nullptr) { - // The rejection was sent encrypted! - stream_->CloseConnectionWithDetails( - QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT, "encrypted REJ message"); - return; - } - next_state_ = STATE_RECV_REJ; - return; - } - - if (in->tag() != kSHLO) { - stream_->CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, - "Expected SHLO or REJ"); - return; - } - - // alternative_decrypter will be nullptr if the original alternative - // decrypter latched and became the primary decrypter. That happens - // if we received a message encrypted with the INITIAL key. - if (session()->connection()->alternative_decrypter() != nullptr) { - // The server hello was sent without encryption. - stream_->CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT, - "unencrypted SHLO message"); - return; - } - - string error_details; - QuicErrorCode error = crypto_config_->ProcessServerHello( - *in, session()->connection()->connection_id(), - session()->connection()->version(), - session()->connection()->server_supported_versions(), cached, - crypto_negotiated_params_, &error_details); - - if (error != QUIC_NO_ERROR) { - stream_->CloseConnectionWithDetails( - error, "Server hello invalid: " + error_details); - return; - } - error = session()->config()->ProcessPeerHello(*in, SERVER, &error_details); - if (error != QUIC_NO_ERROR) { - stream_->CloseConnectionWithDetails( - error, "Server hello invalid: " + error_details); - return; - } - session()->OnConfigNegotiated(); - - CrypterPair* crypters = &crypto_negotiated_params_->forward_secure_crypters; - // TODO(agl): we don't currently latch this decrypter because the idea - // has been floated that the server shouldn't send packets encrypted - // with the FORWARD_SECURE key until it receives a FORWARD_SECURE - // packet from the client. - session()->connection()->SetAlternativeDecrypter( - ENCRYPTION_FORWARD_SECURE, crypters->decrypter.release(), - false /* don't latch */); - session()->connection()->SetEncrypter(ENCRYPTION_FORWARD_SECURE, - crypters->encrypter.release()); - session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); - - handshake_confirmed_ = true; - session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); - session()->connection()->OnHandshakeComplete(); -} - -void QuicCryptoClientHandshaker::DoInitializeServerConfigUpdate( - QuicCryptoClientConfig::CachedState* cached) { - bool update_ignored = false; - if (!cached->IsEmpty() && !cached->signature().empty()) { - // Note that we verify the proof even if the cached proof is valid. - DCHECK(crypto_config_->proof_verifier()); - next_state_ = STATE_VERIFY_PROOF; - } else { - update_ignored = true; - next_state_ = STATE_NONE; - } - UMA_HISTOGRAM_COUNTS_1M("Net.QuicNumServerConfig.UpdateMessagesIgnored", - update_ignored); -} - -void QuicCryptoClientHandshaker::SetCachedProofValid( - QuicCryptoClientConfig::CachedState* cached) { - cached->SetProofValid(); - proof_handler_->OnProofValid(*cached); -} - -bool QuicCryptoClientHandshaker::RequiresChannelID( - QuicCryptoClientConfig::CachedState* cached) { - if (server_id_.privacy_mode() == PRIVACY_MODE_ENABLED || - !crypto_config_->channel_id_source()) { - return false; - } - const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); - if (!scfg) { // scfg may be null then we send an inchoate CHLO. - return false; - } - QuicTagVector their_proof_demands; - if (scfg->GetTaglist(kPDMD, &their_proof_demands) != QUIC_NO_ERROR) { - return false; - } - for (const QuicTag tag : their_proof_demands) { - if (tag == kCHID) { - return true; - } - } - return false; -} - } // namespace net diff --git a/chromium/net/quic/core/quic_crypto_client_stream.h b/chromium/net/quic/core/quic_crypto_client_stream.h index ecb06e05c51..af47c9b3f76 100644 --- a/chromium/net/quic/core/quic_crypto_client_stream.h +++ b/chromium/net/quic/core/quic_crypto_client_stream.h @@ -14,16 +14,13 @@ #include "net/quic/core/crypto/proof_verifier.h" #include "net/quic/core/crypto/quic_crypto_client_config.h" #include "net/quic/core/quic_config.h" +#include "net/quic/core/quic_crypto_handshaker.h" #include "net/quic/core/quic_crypto_stream.h" #include "net/quic/core/quic_server_id.h" #include "net/quic/platform/api/quic_export.h" namespace net { -namespace test { -class QuicChromiumClientSessionPeer; -} // namespace test - class QUIC_EXPORT_PRIVATE QuicCryptoClientStreamBase : public QuicCryptoStream { public: explicit QuicCryptoClientStreamBase(QuicSession* session); @@ -163,223 +160,6 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientStream DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientStream); }; -// An implementation of QuicCryptoClientStream::HandshakerDelegate which uses -// QUIC crypto as the crypto handshake protocol. -class QUIC_EXPORT_PRIVATE QuicCryptoClientHandshaker - : public QuicCryptoClientStream::HandshakerDelegate, - public QuicCryptoHandshaker { - public: - QuicCryptoClientHandshaker( - const QuicServerId& server_id, - QuicCryptoClientStream* stream, - QuicSession* session, - ProofVerifyContext* verify_context, - QuicCryptoClientConfig* crypto_config, - QuicCryptoClientStream::ProofHandler* proof_handler); - - ~QuicCryptoClientHandshaker() override; - - // From QuicCryptoClientStream::HandshakerDelegate - bool CryptoConnect() override; - int num_sent_client_hellos() const override; - int num_scup_messages_received() const override; - bool WasChannelIDSent() const override; - bool WasChannelIDSourceCallbackRun() const override; - std::string chlo_hash() const override; - bool encryption_established() const override; - bool handshake_confirmed() const override; - const QuicCryptoNegotiatedParameters& crypto_negotiated_params() - const override; - CryptoMessageParser* crypto_message_parser() override; - - // From QuicCryptoHandshaker - void OnHandshakeMessage(const CryptoHandshakeMessage& message) override; - - private: - // ChannelIDSourceCallbackImpl is passed as the callback method to - // GetChannelIDKey. The ChannelIDSource calls this class with the result of - // channel ID lookup when lookup is performed asynchronously. - class ChannelIDSourceCallbackImpl : public ChannelIDSourceCallback { - public: - explicit ChannelIDSourceCallbackImpl(QuicCryptoClientHandshaker* parent); - ~ChannelIDSourceCallbackImpl() override; - - // ChannelIDSourceCallback interface. - void Run(std::unique_ptr<ChannelIDKey>* channel_id_key) override; - - // Cancel causes any future callbacks to be ignored. It must be called on - // the same thread as the callback will be made on. - void Cancel(); - - private: - QuicCryptoClientHandshaker* parent_; - }; - - // ProofVerifierCallbackImpl is passed as the callback method to VerifyProof. - // The ProofVerifier calls this class with the result of proof verification - // when verification is performed asynchronously. - class ProofVerifierCallbackImpl : public ProofVerifierCallback { - public: - explicit ProofVerifierCallbackImpl(QuicCryptoClientHandshaker* parent); - ~ProofVerifierCallbackImpl() override; - - // ProofVerifierCallback interface. - void Run(bool ok, - const std::string& error_details, - std::unique_ptr<ProofVerifyDetails>* details) override; - - // Cancel causes any future callbacks to be ignored. It must be called on - // the same thread as the callback will be made on. - void Cancel(); - - private: - QuicCryptoClientHandshaker* parent_; - }; - - friend class test::QuicChromiumClientSessionPeer; - - enum State { - STATE_IDLE, - STATE_INITIALIZE, - STATE_SEND_CHLO, - STATE_RECV_REJ, - STATE_VERIFY_PROOF, - STATE_VERIFY_PROOF_COMPLETE, - STATE_GET_CHANNEL_ID, - STATE_GET_CHANNEL_ID_COMPLETE, - STATE_RECV_SHLO, - STATE_INITIALIZE_SCUP, - STATE_NONE, - }; - - // Handles new server config and optional source-address token provided by the - // server during a connection. - void HandleServerConfigUpdateMessage( - const CryptoHandshakeMessage& server_config_update); - - // DoHandshakeLoop performs a step of the handshake state machine. Note that - // |in| may be nullptr if the call did not result from a received message. - void DoHandshakeLoop(const CryptoHandshakeMessage* in); - - // Start the handshake process. - void DoInitialize(QuicCryptoClientConfig::CachedState* cached); - - // Send either InchoateClientHello or ClientHello message to the server. - void DoSendCHLO(QuicCryptoClientConfig::CachedState* cached); - - // Process REJ message from the server. - void DoReceiveREJ(const CryptoHandshakeMessage* in, - QuicCryptoClientConfig::CachedState* cached); - - // Start the proof verification process. Returns the QuicAsyncStatus returned - // by the ProofVerifier's VerifyProof. - QuicAsyncStatus DoVerifyProof(QuicCryptoClientConfig::CachedState* cached); - - // If proof is valid then it sets the proof as valid (which persists the - // server config). If not, it closes the connection. - void DoVerifyProofComplete(QuicCryptoClientConfig::CachedState* cached); - - // Start the look up of Channel ID process. Returns either QUIC_SUCCESS if - // RequiresChannelID returns false or QuicAsyncStatus returned by - // GetChannelIDKey. - QuicAsyncStatus DoGetChannelID(QuicCryptoClientConfig::CachedState* cached); - - // If there is no channel ID, then close the connection otherwise transtion to - // STATE_SEND_CHLO state. - void DoGetChannelIDComplete(); - - // Process SHLO message from the server. - void DoReceiveSHLO(const CryptoHandshakeMessage* in, - QuicCryptoClientConfig::CachedState* cached); - - // Start the proof verification if |server_id_| is https and |cached| has - // signature. - void DoInitializeServerConfigUpdate( - QuicCryptoClientConfig::CachedState* cached); - - // Called to set the proof of |cached| valid. Also invokes the session's - // OnProofValid() method. - void SetCachedProofValid(QuicCryptoClientConfig::CachedState* cached); - - // Returns true if the server crypto config in |cached| requires a ChannelID - // and the client config settings also allow sending a ChannelID. - bool RequiresChannelID(QuicCryptoClientConfig::CachedState* cached); - - // Returns the QuicSession that this stream belongs to. - QuicSession* session() const { return session_; } - - QuicCryptoClientStream* stream_; - - QuicSession* session_; - - State next_state_; - // num_client_hellos_ contains the number of client hello messages that this - // connection has sent. - int num_client_hellos_; - - QuicCryptoClientConfig* const crypto_config_; - - // SHA-256 hash of the most recently sent CHLO. - std::string chlo_hash_; - - // Server's (hostname, port, is_https, privacy_mode) tuple. - const QuicServerId server_id_; - - // Generation counter from QuicCryptoClientConfig's CachedState. - uint64_t generation_counter_; - - // True if a channel ID was sent. - bool channel_id_sent_; - - // True if channel_id_source_callback_ was run. - bool channel_id_source_callback_run_; - - // channel_id_source_callback_ contains the callback object that we passed - // to an asynchronous channel ID lookup. The ChannelIDSource owns this - // object. - ChannelIDSourceCallbackImpl* channel_id_source_callback_; - - // These members are used to store the result of an asynchronous channel ID - // lookup. These members must not be used after - // STATE_GET_CHANNEL_ID_COMPLETE. - std::unique_ptr<ChannelIDKey> channel_id_key_; - - // verify_context_ contains the context object that we pass to asynchronous - // proof verifications. - std::unique_ptr<ProofVerifyContext> verify_context_; - - // proof_verify_callback_ contains the callback object that we passed to an - // asynchronous proof verification. The ProofVerifier owns this object. - ProofVerifierCallbackImpl* proof_verify_callback_; - // proof_handler_ contains the callback object used by a quic client - // for proof verification. It is not owned by this class. - QuicCryptoClientStream::ProofHandler* proof_handler_; - - // These members are used to store the result of an asynchronous proof - // verification. These members must not be used after - // STATE_VERIFY_PROOF_COMPLETE. - bool verify_ok_; - std::string verify_error_details_; - std::unique_ptr<ProofVerifyDetails> verify_details_; - - // True if the server responded to a previous CHLO with a stateless - // reject. Used for book-keeping between the STATE_RECV_REJ, - // STATE_VERIFY_PROOF*, and subsequent STATE_SEND_CHLO state. - bool stateless_reject_received_; - - // Only used in chromium, not internally. - base::TimeTicks proof_verify_start_time_; - - int num_scup_messages_received_; - - bool encryption_established_; - bool handshake_confirmed_; - QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> - crypto_negotiated_params_; - - DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientHandshaker); -}; - } // namespace net #endif // NET_QUIC_CORE_QUIC_CRYPTO_CLIENT_STREAM_H_ diff --git a/chromium/net/quic/core/quic_crypto_client_stream_test.cc b/chromium/net/quic/core/quic_crypto_client_stream_test.cc index 2453761d4b7..0c04daa2283 100644 --- a/chromium/net/quic/core/quic_crypto_client_stream_test.cc +++ b/chromium/net/quic/core/quic_crypto_client_stream_test.cc @@ -225,8 +225,7 @@ TEST_F(QuicCryptoClientStreamTest, ServerConfigUpdate) { reinterpret_cast<char*>(scfg), arraysize(scfg)); QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(stream()); - EXPECT_NE(FLAGS_quic_reloadable_flag_quic_release_crypto_stream_buffer, - QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); + EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); } TEST_F(QuicCryptoClientStreamTest, ServerConfigUpdateWithCert) { diff --git a/chromium/net/quic/core/quic_crypto_handshaker.cc b/chromium/net/quic/core/quic_crypto_handshaker.cc new file mode 100644 index 00000000000..39a9a4e9c9b --- /dev/null +++ b/chromium/net/quic/core/quic_crypto_handshaker.cc @@ -0,0 +1,51 @@ +// 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/core/quic_crypto_handshaker.h" + +#include "net/quic/core/quic_session.h" + +namespace net { + +#define ENDPOINT \ + (session()->perspective() == Perspective::IS_SERVER ? "Server: " \ + : "Client:" \ + " ") + +QuicCryptoHandshaker::QuicCryptoHandshaker(QuicCryptoStream* stream, + QuicSession* session) + : stream_(stream), session_(session) { + crypto_framer_.set_visitor(this); +} + +QuicCryptoHandshaker::~QuicCryptoHandshaker() {} + +void QuicCryptoHandshaker::SendHandshakeMessage( + const CryptoHandshakeMessage& message) { + QUIC_DVLOG(1) << ENDPOINT << "Sending " + << message.DebugString(session()->perspective()); + session()->connection()->NeuterUnencryptedPackets(); + session()->OnCryptoHandshakeMessageSent(message); + const QuicData& data = message.GetSerialized(session()->perspective()); + stream_->WriteOrBufferData(QuicStringPiece(data.data(), data.length()), false, + nullptr); +} + +void QuicCryptoHandshaker::OnError(CryptoFramer* framer) { + QUIC_DLOG(WARNING) << "Error processing crypto data: " + << QuicErrorCodeToString(framer->error()); +} + +void QuicCryptoHandshaker::OnHandshakeMessage( + const CryptoHandshakeMessage& message) { + QUIC_DVLOG(1) << ENDPOINT << "Received " + << message.DebugString(session()->perspective()); + session()->OnCryptoHandshakeMessageReceived(message); +} + +CryptoMessageParser* QuicCryptoHandshaker::crypto_message_parser() { + return &crypto_framer_; +} + +} // namespace net diff --git a/chromium/net/quic/core/quic_crypto_handshaker.h b/chromium/net/quic/core/quic_crypto_handshaker.h new file mode 100644 index 00000000000..76deb37f7c7 --- /dev/null +++ b/chromium/net/quic/core/quic_crypto_handshaker.h @@ -0,0 +1,42 @@ +// 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_CORE_QUIC_CRYPTO_HANDSHAKER_H_ +#define NET_QUIC_CORE_QUIC_CRYPTO_HANDSHAKER_H_ + +#include "net/quic/core/quic_crypto_stream.h" +#include "net/quic/platform/api/quic_export.h" + +namespace net { + +class QUIC_EXPORT_PRIVATE QuicCryptoHandshaker + : public CryptoFramerVisitorInterface { + public: + QuicCryptoHandshaker(QuicCryptoStream* stream, QuicSession* session); + + ~QuicCryptoHandshaker() override; + + // Sends |message| to the peer. + // TODO(wtc): return a success/failure status. + void SendHandshakeMessage(const CryptoHandshakeMessage& message); + + void OnError(CryptoFramer* framer) override; + void OnHandshakeMessage(const CryptoHandshakeMessage& message) override; + + CryptoMessageParser* crypto_message_parser(); + + private: + QuicSession* session() { return session_; } + + QuicCryptoStream* stream_; + QuicSession* session_; + + CryptoFramer crypto_framer_; + + DISALLOW_COPY_AND_ASSIGN(QuicCryptoHandshaker); +}; + +} // namespace net + +#endif // NET_QUIC_CORE_QUIC_CRYPTO_HANDSHAKER_H_ diff --git a/chromium/net/quic/core/quic_crypto_server_handshaker.cc b/chromium/net/quic/core/quic_crypto_server_handshaker.cc new file mode 100644 index 00000000000..d5102ce9a73 --- /dev/null +++ b/chromium/net/quic/core/quic_crypto_server_handshaker.cc @@ -0,0 +1,482 @@ +// 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/core/quic_crypto_server_handshaker.h" + +#include <memory> + +#include "net/quic/platform/api/quic_text_utils.h" +#include "third_party/boringssl/src/include/openssl/sha.h" + +using std::string; + +namespace net { + +class QuicCryptoServerHandshaker::ProcessClientHelloCallback + : public ProcessClientHelloResultCallback { + public: + ProcessClientHelloCallback( + QuicCryptoServerHandshaker* parent, + const QuicReferenceCountedPointer< + ValidateClientHelloResultCallback::Result>& result) + : parent_(parent), result_(result) {} + + void Run(QuicErrorCode error, + const string& error_details, + std::unique_ptr<CryptoHandshakeMessage> message, + std::unique_ptr<DiversificationNonce> diversification_nonce, + std::unique_ptr<net::ProofSource::Details> proof_source_details) + override { + if (parent_ == nullptr) { + return; + } + + parent_->FinishProcessingHandshakeMessageAfterProcessClientHello( + *result_, error, error_details, std::move(message), + std::move(diversification_nonce), std::move(proof_source_details)); + } + + void Cancel() { parent_ = nullptr; } + + private: + QuicCryptoServerHandshaker* parent_; + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + result_; +}; + +QuicCryptoServerHandshaker::QuicCryptoServerHandshaker( + const QuicCryptoServerConfig* crypto_config, + QuicCryptoServerStream* stream, + QuicCompressedCertsCache* compressed_certs_cache, + bool use_stateless_rejects_if_peer_supported, + QuicSession* session, + QuicCryptoServerStream::Helper* helper) + : QuicCryptoHandshaker(stream, session), + stream_(stream), + session_(session), + crypto_config_(crypto_config), + compressed_certs_cache_(compressed_certs_cache), + signed_config_(new QuicSignedServerConfig), + helper_(helper), + num_handshake_messages_(0), + num_handshake_messages_with_server_nonces_(0), + send_server_config_update_cb_(nullptr), + num_server_config_update_messages_sent_(0), + use_stateless_rejects_if_peer_supported_( + use_stateless_rejects_if_peer_supported), + peer_supports_stateless_rejects_(false), + zero_rtt_attempted_(false), + chlo_packet_size_(0), + validate_client_hello_cb_(nullptr), + process_client_hello_cb_(nullptr), + encryption_established_(false), + handshake_confirmed_(false), + crypto_negotiated_params_(new QuicCryptoNegotiatedParameters) {} + +QuicCryptoServerHandshaker::~QuicCryptoServerHandshaker() { + CancelOutstandingCallbacks(); +} + +void QuicCryptoServerHandshaker::CancelOutstandingCallbacks() { + // Detach from the validation callback. Calling this multiple times is safe. + if (validate_client_hello_cb_ != nullptr) { + validate_client_hello_cb_->Cancel(); + validate_client_hello_cb_ = nullptr; + } + if (send_server_config_update_cb_ != nullptr) { + send_server_config_update_cb_->Cancel(); + send_server_config_update_cb_ = nullptr; + } + if (process_client_hello_cb_ != nullptr) { + process_client_hello_cb_->Cancel(); + process_client_hello_cb_ = nullptr; + } +} + +void QuicCryptoServerHandshaker::OnHandshakeMessage( + const CryptoHandshakeMessage& message) { + QuicCryptoHandshaker::OnHandshakeMessage(message); + ++num_handshake_messages_; + chlo_packet_size_ = session()->connection()->GetCurrentPacket().length(); + + // Do not process handshake messages after the handshake is confirmed. + if (handshake_confirmed_) { + stream_->CloseConnectionWithDetails( + QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE, + "Unexpected handshake message from client"); + return; + } + + if (message.tag() != kCHLO) { + stream_->CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, + "Handshake packet not CHLO"); + return; + } + + if (validate_client_hello_cb_ != nullptr || + process_client_hello_cb_ != nullptr) { + // Already processing some other handshake message. The protocol + // does not allow for clients to send multiple handshake messages + // before the server has a chance to respond. + stream_->CloseConnectionWithDetails( + QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO, + "Unexpected handshake message while processing CHLO"); + return; + } + + CryptoUtils::HashHandshakeMessage(message, &chlo_hash_, + Perspective::IS_SERVER); + + std::unique_ptr<ValidateCallback> cb(new ValidateCallback(this)); + DCHECK(validate_client_hello_cb_ == nullptr); + DCHECK(process_client_hello_cb_ == nullptr); + validate_client_hello_cb_ = cb.get(); + crypto_config_->ValidateClientHello( + message, GetClientAddress().host(), + session()->connection()->self_address(), version(), + session()->connection()->clock(), signed_config_, std::move(cb)); +} + +void QuicCryptoServerHandshaker::FinishProcessingHandshakeMessage( + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + result, + std::unique_ptr<ProofSource::Details> details) { + const CryptoHandshakeMessage& message = result->client_hello; + + // Clear the callback that got us here. + DCHECK(validate_client_hello_cb_ != nullptr); + DCHECK(process_client_hello_cb_ == nullptr); + validate_client_hello_cb_ = nullptr; + + if (use_stateless_rejects_if_peer_supported_) { + peer_supports_stateless_rejects_ = + QuicCryptoServerStreamBase::DoesPeerSupportStatelessRejects(message); + } + + std::unique_ptr<ProcessClientHelloCallback> cb( + new ProcessClientHelloCallback(this, result)); + process_client_hello_cb_ = cb.get(); + ProcessClientHello(result, std::move(details), std::move(cb)); +} + +void QuicCryptoServerHandshaker:: + FinishProcessingHandshakeMessageAfterProcessClientHello( + const ValidateClientHelloResultCallback::Result& result, + QuicErrorCode error, + const string& error_details, + std::unique_ptr<CryptoHandshakeMessage> reply, + std::unique_ptr<DiversificationNonce> diversification_nonce, + std::unique_ptr<ProofSource::Details> proof_source_details) { + // Clear the callback that got us here. + DCHECK(process_client_hello_cb_ != nullptr); + DCHECK(validate_client_hello_cb_ == nullptr); + process_client_hello_cb_ = nullptr; + + const CryptoHandshakeMessage& message = result.client_hello; + if (error != QUIC_NO_ERROR) { + stream_->CloseConnectionWithDetails(error, error_details); + return; + } + + if (reply->tag() != kSHLO) { + if (reply->tag() == kSREJ) { + DCHECK(use_stateless_rejects_if_peer_supported_); + DCHECK(peer_supports_stateless_rejects_); + // Before sending the SREJ, cause the connection to save crypto packets + // so that they can be added to the time wait list manager and + // retransmitted. + session()->connection()->EnableSavingCryptoPackets(); + } + SendHandshakeMessage(*reply); + + if (reply->tag() == kSREJ) { + DCHECK(use_stateless_rejects_if_peer_supported_); + DCHECK(peer_supports_stateless_rejects_); + DCHECK(!handshake_confirmed()); + QUIC_DLOG(INFO) << "Closing connection " + << session()->connection()->connection_id() + << " because of a stateless reject."; + session()->connection()->CloseConnection( + QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, "stateless reject", + ConnectionCloseBehavior::SILENT_CLOSE); + } + return; + } + + // If we are returning a SHLO then we accepted the handshake. Now + // process the negotiated configuration options as part of the + // session config. + QuicConfig* config = session()->config(); + OverrideQuicConfigDefaults(config); + string process_error_details; + const QuicErrorCode process_error = + config->ProcessPeerHello(message, CLIENT, &process_error_details); + if (process_error != QUIC_NO_ERROR) { + stream_->CloseConnectionWithDetails(process_error, process_error_details); + return; + } + + session()->OnConfigNegotiated(); + + config->ToHandshakeMessage(reply.get()); + + // Receiving a full CHLO implies the client is prepared to decrypt with + // the new server write key. We can start to encrypt with the new server + // write key. + // + // NOTE: the SHLO will be encrypted with the new server write key. + session()->connection()->SetEncrypter( + ENCRYPTION_INITIAL, + crypto_negotiated_params_->initial_crypters.encrypter.release()); + session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); + // Set the decrypter immediately so that we no longer accept unencrypted + // packets. + session()->connection()->SetDecrypter( + ENCRYPTION_INITIAL, + crypto_negotiated_params_->initial_crypters.decrypter.release()); + session()->connection()->SetDiversificationNonce(*diversification_nonce); + + SendHandshakeMessage(*reply); + + session()->connection()->SetEncrypter( + ENCRYPTION_FORWARD_SECURE, + crypto_negotiated_params_->forward_secure_crypters.encrypter.release()); + session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + + session()->connection()->SetAlternativeDecrypter( + ENCRYPTION_FORWARD_SECURE, + crypto_negotiated_params_->forward_secure_crypters.decrypter.release(), + false /* don't latch */); + + encryption_established_ = true; + handshake_confirmed_ = true; + session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); +} + +void QuicCryptoServerHandshaker::SendServerConfigUpdate( + const CachedNetworkParameters* cached_network_params) { + if (!handshake_confirmed_) { + return; + } + + if (send_server_config_update_cb_ != nullptr) { + QUIC_DVLOG(1) + << "Skipped server config update since one is already in progress"; + return; + } + + std::unique_ptr<SendServerConfigUpdateCallback> cb( + new SendServerConfigUpdateCallback(this)); + send_server_config_update_cb_ = cb.get(); + + crypto_config_->BuildServerConfigUpdateMessage( + session()->connection()->version(), chlo_hash_, + previous_source_address_tokens_, session()->connection()->self_address(), + GetClientAddress().host(), session()->connection()->clock(), + session()->connection()->random_generator(), compressed_certs_cache_, + *crypto_negotiated_params_, cached_network_params, + (session()->config()->HasReceivedConnectionOptions() + ? session()->config()->ReceivedConnectionOptions() + : QuicTagVector()), + std::move(cb)); +} + +QuicCryptoServerHandshaker::SendServerConfigUpdateCallback:: + SendServerConfigUpdateCallback(QuicCryptoServerHandshaker* parent) + : parent_(parent) {} + +void QuicCryptoServerHandshaker::SendServerConfigUpdateCallback::Cancel() { + parent_ = nullptr; +} + +// From BuildServerConfigUpdateMessageResultCallback +void QuicCryptoServerHandshaker::SendServerConfigUpdateCallback::Run( + bool ok, + const CryptoHandshakeMessage& message) { + if (parent_ == nullptr) { + return; + } + parent_->FinishSendServerConfigUpdate(ok, message); +} + +void QuicCryptoServerHandshaker::FinishSendServerConfigUpdate( + bool ok, + const CryptoHandshakeMessage& message) { + // Clear the callback that got us here. + DCHECK(send_server_config_update_cb_ != nullptr); + send_server_config_update_cb_ = nullptr; + + if (!ok) { + QUIC_DVLOG(1) << "Server: Failed to build server config update (SCUP)!"; + return; + } + + QUIC_DVLOG(1) << "Server: Sending server config update: " + << message.DebugString(Perspective::IS_SERVER); + const QuicData& data = message.GetSerialized(Perspective::IS_SERVER); + stream_->WriteOrBufferData(QuicStringPiece(data.data(), data.length()), false, + nullptr); + + ++num_server_config_update_messages_sent_; +} + +uint8_t QuicCryptoServerHandshaker::NumHandshakeMessages() const { + return num_handshake_messages_; +} + +uint8_t QuicCryptoServerHandshaker::NumHandshakeMessagesWithServerNonces() + const { + return num_handshake_messages_with_server_nonces_; +} + +int QuicCryptoServerHandshaker::NumServerConfigUpdateMessagesSent() const { + return num_server_config_update_messages_sent_; +} + +const CachedNetworkParameters* +QuicCryptoServerHandshaker::PreviousCachedNetworkParams() const { + return previous_cached_network_params_.get(); +} + +bool QuicCryptoServerHandshaker::UseStatelessRejectsIfPeerSupported() const { + return use_stateless_rejects_if_peer_supported_; +} + +bool QuicCryptoServerHandshaker::PeerSupportsStatelessRejects() const { + return peer_supports_stateless_rejects_; +} + +bool QuicCryptoServerHandshaker::ZeroRttAttempted() const { + return zero_rtt_attempted_; +} + +void QuicCryptoServerHandshaker::SetPeerSupportsStatelessRejects( + bool peer_supports_stateless_rejects) { + peer_supports_stateless_rejects_ = peer_supports_stateless_rejects; +} + +void QuicCryptoServerHandshaker::SetPreviousCachedNetworkParams( + CachedNetworkParameters cached_network_params) { + previous_cached_network_params_.reset( + new CachedNetworkParameters(cached_network_params)); +} + +bool QuicCryptoServerHandshaker::ShouldSendExpectCTHeader() const { + return signed_config_->proof.send_expect_ct_header; +} + +bool QuicCryptoServerHandshaker::GetBase64SHA256ClientChannelID( + string* output) const { + if (!encryption_established() || + crypto_negotiated_params_->channel_id.empty()) { + return false; + } + + const string& channel_id(crypto_negotiated_params_->channel_id); + uint8_t digest[SHA256_DIGEST_LENGTH]; + SHA256(reinterpret_cast<const uint8_t*>(channel_id.data()), channel_id.size(), + digest); + + QuicTextUtils::Base64Encode(digest, arraysize(digest), output); + return true; +} + +bool QuicCryptoServerHandshaker::encryption_established() const { + return encryption_established_; +} + +bool QuicCryptoServerHandshaker::handshake_confirmed() const { + return handshake_confirmed_; +} + +const QuicCryptoNegotiatedParameters& +QuicCryptoServerHandshaker::crypto_negotiated_params() const { + return *crypto_negotiated_params_; +} + +CryptoMessageParser* QuicCryptoServerHandshaker::crypto_message_parser() { + return QuicCryptoHandshaker::crypto_message_parser(); +} + +void QuicCryptoServerHandshaker::ProcessClientHello( + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + result, + std::unique_ptr<ProofSource::Details> proof_source_details, + std::unique_ptr<ProcessClientHelloResultCallback> done_cb) { + const CryptoHandshakeMessage& message = result->client_hello; + string error_details; + if (!helper_->CanAcceptClientHello( + message, session()->connection()->self_address(), &error_details)) { + done_cb->Run(QUIC_HANDSHAKE_FAILED, error_details, nullptr, nullptr, + nullptr); + return; + } + if (!result->info.server_nonce.empty()) { + ++num_handshake_messages_with_server_nonces_; + } + + if (num_handshake_messages_ == 1) { + // Client attempts zero RTT handshake by sending a non-inchoate CHLO. + QuicStringPiece public_value; + zero_rtt_attempted_ = message.GetStringPiece(kPUBS, &public_value); + } + + // Store the bandwidth estimate from the client. + if (result->cached_network_params.bandwidth_estimate_bytes_per_second() > 0) { + previous_cached_network_params_.reset( + new CachedNetworkParameters(result->cached_network_params)); + } + previous_source_address_tokens_ = result->info.source_address_tokens; + + const bool use_stateless_rejects_in_crypto_config = + use_stateless_rejects_if_peer_supported_ && + peer_supports_stateless_rejects_; + QuicConnection* connection = session()->connection(); + const QuicConnectionId server_designated_connection_id = + GenerateConnectionIdForReject(use_stateless_rejects_in_crypto_config); + crypto_config_->ProcessClientHello( + result, /*reject_only=*/false, connection->connection_id(), + connection->self_address(), GetClientAddress(), version(), + connection->supported_versions(), use_stateless_rejects_in_crypto_config, + server_designated_connection_id, connection->clock(), + connection->random_generator(), compressed_certs_cache_, + crypto_negotiated_params_, signed_config_, + QuicCryptoStream::CryptoMessageFramingOverhead(version()), + chlo_packet_size_, std::move(done_cb)); +} + +void QuicCryptoServerHandshaker::OverrideQuicConfigDefaults( + QuicConfig* config) {} + +QuicCryptoServerHandshaker::ValidateCallback::ValidateCallback( + QuicCryptoServerHandshaker* parent) + : parent_(parent) {} + +void QuicCryptoServerHandshaker::ValidateCallback::Cancel() { + parent_ = nullptr; +} + +void QuicCryptoServerHandshaker::ValidateCallback::Run( + QuicReferenceCountedPointer<Result> result, + std::unique_ptr<ProofSource::Details> details) { + if (parent_ != nullptr) { + parent_->FinishProcessingHandshakeMessage(std::move(result), + std::move(details)); + } +} + +QuicConnectionId QuicCryptoServerHandshaker::GenerateConnectionIdForReject( + bool use_stateless_rejects) { + if (!use_stateless_rejects) { + return 0; + } + return helper_->GenerateConnectionIdForReject( + session()->connection()->connection_id()); +} + +const QuicSocketAddress QuicCryptoServerHandshaker::GetClientAddress() { + return session()->connection()->peer_address(); +} + +} // namespace net diff --git a/chromium/net/quic/core/quic_crypto_server_handshaker.h b/chromium/net/quic/core/quic_crypto_server_handshaker.h new file mode 100644 index 00000000000..6fffb98573e --- /dev/null +++ b/chromium/net/quic/core/quic_crypto_server_handshaker.h @@ -0,0 +1,240 @@ +// 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_CORE_QUIC_CRYPTO_SERVER_HANDSHAKER_H_ +#define NET_QUIC_CORE_QUIC_CRYPTO_SERVER_HANDSHAKER_H_ + +#include "net/quic/core/quic_crypto_handshaker.h" +#include "net/quic/core/quic_crypto_server_stream.h" +#include "net/quic/core/quic_session.h" +#include "net/quic/platform/api/quic_export.h" + +namespace net { + +namespace test { +class QuicCryptoServerStreamPeer; +} // namespace test + +class QUIC_EXPORT_PRIVATE QuicCryptoServerHandshaker + : public QuicCryptoServerStream::HandshakerDelegate, + public QuicCryptoHandshaker { + public: + // |crypto_config| must outlive the stream. + // |session| must outlive the stream. + // |helper| must outlive the stream. + QuicCryptoServerHandshaker(const QuicCryptoServerConfig* crypto_config, + QuicCryptoServerStream* stream, + QuicCompressedCertsCache* compressed_certs_cache, + bool use_stateless_rejects_if_peer_supported, + QuicSession* session, + QuicCryptoServerStream::Helper* helper); + + ~QuicCryptoServerHandshaker() override; + + // From HandshakerDelegate + void CancelOutstandingCallbacks() override; + bool GetBase64SHA256ClientChannelID(std::string* output) const override; + void SendServerConfigUpdate( + const CachedNetworkParameters* cached_network_params) override; + uint8_t NumHandshakeMessages() const override; + uint8_t NumHandshakeMessagesWithServerNonces() const override; + int NumServerConfigUpdateMessagesSent() const override; + const CachedNetworkParameters* PreviousCachedNetworkParams() const override; + bool UseStatelessRejectsIfPeerSupported() const override; + bool PeerSupportsStatelessRejects() const override; + bool ZeroRttAttempted() const override; + void SetPeerSupportsStatelessRejects( + bool peer_supports_stateless_rejects) override; + void SetPreviousCachedNetworkParams( + CachedNetworkParameters cached_network_params) override; + bool ShouldSendExpectCTHeader() const override; + + // From QuicCryptoStream + bool encryption_established() const override; + bool handshake_confirmed() const override; + const QuicCryptoNegotiatedParameters& crypto_negotiated_params() + const override; + CryptoMessageParser* crypto_message_parser() override; + + // From QuicCryptoHandshaker + void OnHandshakeMessage(const CryptoHandshakeMessage& message) override; + + protected: + virtual void ProcessClientHello( + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + result, + std::unique_ptr<ProofSource::Details> proof_source_details, + std::unique_ptr<ProcessClientHelloResultCallback> done_cb); + + // Hook that allows the server to set QuicConfig defaults just + // before going through the parameter negotiation step. + virtual void OverrideQuicConfigDefaults(QuicConfig* config); + + // Returns client address used to generate and validate source address token. + virtual const QuicSocketAddress GetClientAddress(); + + private: + friend class test::QuicCryptoServerStreamPeer; + + class ValidateCallback : public ValidateClientHelloResultCallback { + public: + explicit ValidateCallback(QuicCryptoServerHandshaker* parent); + // To allow the parent to detach itself from the callback before deletion. + void Cancel(); + + // From ValidateClientHelloResultCallback + void Run(QuicReferenceCountedPointer<Result> result, + std::unique_ptr<ProofSource::Details> details) override; + + private: + QuicCryptoServerHandshaker* parent_; + + DISALLOW_COPY_AND_ASSIGN(ValidateCallback); + }; + + class SendServerConfigUpdateCallback + : public BuildServerConfigUpdateMessageResultCallback { + public: + explicit SendServerConfigUpdateCallback(QuicCryptoServerHandshaker* parent); + SendServerConfigUpdateCallback(const SendServerConfigUpdateCallback&) = + delete; + void operator=(const SendServerConfigUpdateCallback&) = delete; + + // To allow the parent to detach itself from the callback before deletion. + void Cancel(); + + // From BuildServerConfigUpdateMessageResultCallback + void Run(bool ok, const CryptoHandshakeMessage& message) override; + + private: + QuicCryptoServerHandshaker* parent_; + }; + + // Invoked by ValidateCallback::RunImpl once initial validation of + // the client hello is complete. Finishes processing of the client + // hello message and handles handshake success/failure. + void FinishProcessingHandshakeMessage( + QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> + result, + std::unique_ptr<ProofSource::Details> details); + + class ProcessClientHelloCallback; + friend class ProcessClientHelloCallback; + + // Portion of FinishProcessingHandshakeMessage which executes after + // ProcessClientHello has been called. + void FinishProcessingHandshakeMessageAfterProcessClientHello( + const ValidateClientHelloResultCallback::Result& result, + QuicErrorCode error, + const std::string& error_details, + std::unique_ptr<CryptoHandshakeMessage> reply, + std::unique_ptr<DiversificationNonce> diversification_nonce, + std::unique_ptr<ProofSource::Details> proof_source_details); + + // Invoked by SendServerConfigUpdateCallback::RunImpl once the proof has been + // received. |ok| indicates whether or not the proof was successfully + // acquired, and |message| holds the partially-constructed message from + // SendServerConfigUpdate. + void FinishSendServerConfigUpdate(bool ok, + const CryptoHandshakeMessage& message); + + // Returns a new ConnectionId to be used for statelessly rejected connections + // if |use_stateless_rejects| is true. Returns 0 otherwise. + QuicConnectionId GenerateConnectionIdForReject(bool use_stateless_rejects); + + // Returns the QuicSession that this stream belongs to. + QuicSession* session() const { return session_; } + + // Returns the QuicVersion of the connection. + QuicVersion version() const { return session_->connection()->version(); } + + QuicCryptoServerStream* stream_; + + QuicSession* session_; + + // crypto_config_ contains crypto parameters for the handshake. + const QuicCryptoServerConfig* crypto_config_; + + // compressed_certs_cache_ contains a set of most recently compressed certs. + // Owned by QuicDispatcher. + QuicCompressedCertsCache* compressed_certs_cache_; + + // Server's certificate chain and signature of the server config, as provided + // by ProofSource::GetProof. + QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_; + + // Hash of the last received CHLO message which can be used for generating + // server config update messages. + std::string chlo_hash_; + + // Pointer to the helper for this crypto stream. Must outlive this stream. + QuicCryptoServerStream::Helper* helper_; + + // Number of handshake messages received by this stream. + uint8_t num_handshake_messages_; + + // Number of handshake messages received by this stream that contain + // server nonces (indicating that this is a non-zero-RTT handshake + // attempt). + uint8_t num_handshake_messages_with_server_nonces_; + + // Pointer to the active callback that will receive the result of + // BuildServerConfigUpdateMessage and forward it to + // FinishSendServerConfigUpdate. nullptr if no update message is currently + // being built. + SendServerConfigUpdateCallback* send_server_config_update_cb_; + + // Number of server config update (SCUP) messages sent by this stream. + int num_server_config_update_messages_sent_; + + // If the client provides CachedNetworkParameters in the STK in the CHLO, then + // store here, and send back in future STKs if we have no better bandwidth + // estimate to send. + std::unique_ptr<CachedNetworkParameters> previous_cached_network_params_; + + // Contains any source address tokens which were present in the CHLO. + SourceAddressTokens previous_source_address_tokens_; + + // If true, the server should use stateless rejects, so long as the + // client supports them, as indicated by + // peer_supports_stateless_rejects_. + bool use_stateless_rejects_if_peer_supported_; + + // Set to true, once the server has received information from the + // client that it supports stateless reject. + // TODO(jokulik): Remove once client stateless reject support + // becomes the default. + bool peer_supports_stateless_rejects_; + + // True if client attempts 0-rtt handshake (which can succeed or fail). If + // stateless rejects are used, this variable will be false for the stateless + // rejected connection and true for subsequent connections. + bool zero_rtt_attempted_; + + // Size of the packet containing the most recently received CHLO. + QuicByteCount chlo_packet_size_; + + // Pointer to the active callback that will receive the result of the client + // hello validation request and forward it to FinishProcessingHandshakeMessage + // for processing. nullptr if no handshake message is being validated. Note + // that this field is mutually exclusive with process_client_hello_cb_. + ValidateCallback* validate_client_hello_cb_; + + // Pointer to the active callback which will receive the results of + // ProcessClientHello and forward it to + // FinishProcessingHandshakeMessageAfterProcessClientHello. Note that this + // field is mutually exclusive with validate_client_hello_cb_. + ProcessClientHelloCallback* process_client_hello_cb_; + + bool encryption_established_; + bool handshake_confirmed_; + QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> + crypto_negotiated_params_; + + DISALLOW_COPY_AND_ASSIGN(QuicCryptoServerHandshaker); +}; + +} // namespace net + +#endif // NET_QUIC_CORE_QUIC_CRYPTO_SERVER_HANDSHAKER_H_ diff --git a/chromium/net/quic/core/quic_crypto_server_stream.cc b/chromium/net/quic/core/quic_crypto_server_stream.cc index 7119a8e67c7..d9d2c83cb33 100644 --- a/chromium/net/quic/core/quic_crypto_server_stream.cc +++ b/chromium/net/quic/core/quic_crypto_server_stream.cc @@ -12,50 +12,17 @@ #include "net/quic/core/crypto/quic_random.h" #include "net/quic/core/proto/cached_network_parameters.pb.h" #include "net/quic/core/quic_config.h" +#include "net/quic/core/quic_crypto_server_handshaker.h" #include "net/quic/core/quic_packets.h" #include "net/quic/core/quic_session.h" #include "net/quic/platform/api/quic_flags.h" #include "net/quic/platform/api/quic_logging.h" #include "net/quic/platform/api/quic_string_piece.h" -#include "net/quic/platform/api/quic_text_utils.h" -#include "third_party/boringssl/src/include/openssl/sha.h" using std::string; namespace net { -class QuicCryptoServerHandshaker::ProcessClientHelloCallback - : public ProcessClientHelloResultCallback { - public: - ProcessClientHelloCallback( - QuicCryptoServerHandshaker* parent, - const QuicReferenceCountedPointer< - ValidateClientHelloResultCallback::Result>& result) - : parent_(parent), result_(result) {} - - void Run(QuicErrorCode error, - const string& error_details, - std::unique_ptr<CryptoHandshakeMessage> message, - std::unique_ptr<DiversificationNonce> diversification_nonce, - std::unique_ptr<net::ProofSource::Details> proof_source_details) - override { - if (parent_ == nullptr) { - return; - } - - parent_->FinishProcessingHandshakeMessageAfterProcessClientHello( - *result_, error, error_details, std::move(message), - std::move(diversification_nonce), std::move(proof_source_details)); - } - - void Cancel() { parent_ = nullptr; } - - private: - QuicCryptoServerHandshaker* parent_; - QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> - result_; -}; - QuicCryptoServerStreamBase::QuicCryptoServerStreamBase(QuicSession* session) : QuicCryptoStream(session) {} @@ -172,438 +139,4 @@ QuicCryptoServerStream::HandshakerDelegate* QuicCryptoServerStream::handshaker() return handshaker_.get(); } -QuicCryptoServerHandshaker::QuicCryptoServerHandshaker( - const QuicCryptoServerConfig* crypto_config, - QuicCryptoServerStream* stream, - QuicCompressedCertsCache* compressed_certs_cache, - bool use_stateless_rejects_if_peer_supported, - QuicSession* session, - QuicCryptoServerStream::Helper* helper) - : QuicCryptoHandshaker(stream, session), - stream_(stream), - session_(session), - crypto_config_(crypto_config), - compressed_certs_cache_(compressed_certs_cache), - signed_config_(new QuicSignedServerConfig), - helper_(helper), - num_handshake_messages_(0), - num_handshake_messages_with_server_nonces_(0), - send_server_config_update_cb_(nullptr), - num_server_config_update_messages_sent_(0), - use_stateless_rejects_if_peer_supported_( - use_stateless_rejects_if_peer_supported), - peer_supports_stateless_rejects_(false), - zero_rtt_attempted_(false), - chlo_packet_size_(0), - validate_client_hello_cb_(nullptr), - process_client_hello_cb_(nullptr), - encryption_established_(false), - handshake_confirmed_(false), - crypto_negotiated_params_(new QuicCryptoNegotiatedParameters) {} - -QuicCryptoServerHandshaker::~QuicCryptoServerHandshaker() { - CancelOutstandingCallbacks(); -} - -void QuicCryptoServerHandshaker::CancelOutstandingCallbacks() { - // Detach from the validation callback. Calling this multiple times is safe. - if (validate_client_hello_cb_ != nullptr) { - validate_client_hello_cb_->Cancel(); - validate_client_hello_cb_ = nullptr; - } - if (send_server_config_update_cb_ != nullptr) { - send_server_config_update_cb_->Cancel(); - send_server_config_update_cb_ = nullptr; - } - if (process_client_hello_cb_ != nullptr) { - process_client_hello_cb_->Cancel(); - process_client_hello_cb_ = nullptr; - } -} - -void QuicCryptoServerHandshaker::OnHandshakeMessage( - const CryptoHandshakeMessage& message) { - QuicCryptoHandshaker::OnHandshakeMessage(message); - ++num_handshake_messages_; - chlo_packet_size_ = session()->connection()->GetCurrentPacket().length(); - - // Do not process handshake messages after the handshake is confirmed. - if (handshake_confirmed_) { - stream_->CloseConnectionWithDetails( - QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE, - "Unexpected handshake message from client"); - return; - } - - if (message.tag() != kCHLO) { - stream_->CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, - "Handshake packet not CHLO"); - return; - } - - if (validate_client_hello_cb_ != nullptr || - process_client_hello_cb_ != nullptr) { - // Already processing some other handshake message. The protocol - // does not allow for clients to send multiple handshake messages - // before the server has a chance to respond. - stream_->CloseConnectionWithDetails( - QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO, - "Unexpected handshake message while processing CHLO"); - return; - } - - CryptoUtils::HashHandshakeMessage(message, &chlo_hash_, - Perspective::IS_SERVER); - - std::unique_ptr<ValidateCallback> cb(new ValidateCallback(this)); - DCHECK(validate_client_hello_cb_ == nullptr); - DCHECK(process_client_hello_cb_ == nullptr); - validate_client_hello_cb_ = cb.get(); - crypto_config_->ValidateClientHello( - message, GetClientAddress().host(), - session()->connection()->self_address(), version(), - session()->connection()->clock(), signed_config_, std::move(cb)); -} - -void QuicCryptoServerHandshaker::FinishProcessingHandshakeMessage( - QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> - result, - std::unique_ptr<ProofSource::Details> details) { - const CryptoHandshakeMessage& message = result->client_hello; - - // Clear the callback that got us here. - DCHECK(validate_client_hello_cb_ != nullptr); - DCHECK(process_client_hello_cb_ == nullptr); - validate_client_hello_cb_ = nullptr; - - if (use_stateless_rejects_if_peer_supported_) { - peer_supports_stateless_rejects_ = - QuicCryptoServerStreamBase::DoesPeerSupportStatelessRejects(message); - } - - std::unique_ptr<ProcessClientHelloCallback> cb( - new ProcessClientHelloCallback(this, result)); - process_client_hello_cb_ = cb.get(); - ProcessClientHello(result, std::move(details), std::move(cb)); -} - -void QuicCryptoServerHandshaker:: - FinishProcessingHandshakeMessageAfterProcessClientHello( - const ValidateClientHelloResultCallback::Result& result, - QuicErrorCode error, - const string& error_details, - std::unique_ptr<CryptoHandshakeMessage> reply, - std::unique_ptr<DiversificationNonce> diversification_nonce, - std::unique_ptr<ProofSource::Details> proof_source_details) { - // Clear the callback that got us here. - DCHECK(process_client_hello_cb_ != nullptr); - DCHECK(validate_client_hello_cb_ == nullptr); - process_client_hello_cb_ = nullptr; - - const CryptoHandshakeMessage& message = result.client_hello; - if (error != QUIC_NO_ERROR) { - stream_->CloseConnectionWithDetails(error, error_details); - return; - } - - if (reply->tag() != kSHLO) { - if (reply->tag() == kSREJ) { - DCHECK(use_stateless_rejects_if_peer_supported_); - DCHECK(peer_supports_stateless_rejects_); - // Before sending the SREJ, cause the connection to save crypto packets - // so that they can be added to the time wait list manager and - // retransmitted. - session()->connection()->EnableSavingCryptoPackets(); - } - SendHandshakeMessage(*reply); - - if (reply->tag() == kSREJ) { - DCHECK(use_stateless_rejects_if_peer_supported_); - DCHECK(peer_supports_stateless_rejects_); - DCHECK(!handshake_confirmed()); - QUIC_DLOG(INFO) << "Closing connection " - << session()->connection()->connection_id() - << " because of a stateless reject."; - session()->connection()->CloseConnection( - QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, "stateless reject", - ConnectionCloseBehavior::SILENT_CLOSE); - } - return; - } - - // If we are returning a SHLO then we accepted the handshake. Now - // process the negotiated configuration options as part of the - // session config. - QuicConfig* config = session()->config(); - OverrideQuicConfigDefaults(config); - string process_error_details; - const QuicErrorCode process_error = - config->ProcessPeerHello(message, CLIENT, &process_error_details); - if (process_error != QUIC_NO_ERROR) { - stream_->CloseConnectionWithDetails(process_error, process_error_details); - return; - } - - session()->OnConfigNegotiated(); - - config->ToHandshakeMessage(reply.get()); - - // Receiving a full CHLO implies the client is prepared to decrypt with - // the new server write key. We can start to encrypt with the new server - // write key. - // - // NOTE: the SHLO will be encrypted with the new server write key. - session()->connection()->SetEncrypter( - ENCRYPTION_INITIAL, - crypto_negotiated_params_->initial_crypters.encrypter.release()); - session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); - // Set the decrypter immediately so that we no longer accept unencrypted - // packets. - session()->connection()->SetDecrypter( - ENCRYPTION_INITIAL, - crypto_negotiated_params_->initial_crypters.decrypter.release()); - session()->connection()->SetDiversificationNonce(*diversification_nonce); - - SendHandshakeMessage(*reply); - - session()->connection()->SetEncrypter( - ENCRYPTION_FORWARD_SECURE, - crypto_negotiated_params_->forward_secure_crypters.encrypter.release()); - session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); - - session()->connection()->SetAlternativeDecrypter( - ENCRYPTION_FORWARD_SECURE, - crypto_negotiated_params_->forward_secure_crypters.decrypter.release(), - false /* don't latch */); - - encryption_established_ = true; - handshake_confirmed_ = true; - session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); -} - -void QuicCryptoServerHandshaker::SendServerConfigUpdate( - const CachedNetworkParameters* cached_network_params) { - if (!handshake_confirmed_) { - return; - } - - if (send_server_config_update_cb_ != nullptr) { - QUIC_DVLOG(1) - << "Skipped server config update since one is already in progress"; - return; - } - - std::unique_ptr<SendServerConfigUpdateCallback> cb( - new SendServerConfigUpdateCallback(this)); - send_server_config_update_cb_ = cb.get(); - - crypto_config_->BuildServerConfigUpdateMessage( - session()->connection()->version(), chlo_hash_, - previous_source_address_tokens_, session()->connection()->self_address(), - GetClientAddress().host(), session()->connection()->clock(), - session()->connection()->random_generator(), compressed_certs_cache_, - *crypto_negotiated_params_, cached_network_params, - (session()->config()->HasReceivedConnectionOptions() - ? session()->config()->ReceivedConnectionOptions() - : QuicTagVector()), - std::move(cb)); -} - -QuicCryptoServerHandshaker::SendServerConfigUpdateCallback:: - SendServerConfigUpdateCallback(QuicCryptoServerHandshaker* parent) - : parent_(parent) {} - -void QuicCryptoServerHandshaker::SendServerConfigUpdateCallback::Cancel() { - parent_ = nullptr; -} - -// From BuildServerConfigUpdateMessageResultCallback -void QuicCryptoServerHandshaker::SendServerConfigUpdateCallback::Run( - bool ok, - const CryptoHandshakeMessage& message) { - if (parent_ == nullptr) { - return; - } - parent_->FinishSendServerConfigUpdate(ok, message); -} - -void QuicCryptoServerHandshaker::FinishSendServerConfigUpdate( - bool ok, - const CryptoHandshakeMessage& message) { - // Clear the callback that got us here. - DCHECK(send_server_config_update_cb_ != nullptr); - send_server_config_update_cb_ = nullptr; - - if (!ok) { - QUIC_DVLOG(1) << "Server: Failed to build server config update (SCUP)!"; - return; - } - - QUIC_DVLOG(1) << "Server: Sending server config update: " - << message.DebugString(Perspective::IS_SERVER); - const QuicData& data = message.GetSerialized(Perspective::IS_SERVER); - stream_->WriteOrBufferData(QuicStringPiece(data.data(), data.length()), false, - nullptr); - - ++num_server_config_update_messages_sent_; -} - -uint8_t QuicCryptoServerHandshaker::NumHandshakeMessages() const { - return num_handshake_messages_; -} - -uint8_t QuicCryptoServerHandshaker::NumHandshakeMessagesWithServerNonces() - const { - return num_handshake_messages_with_server_nonces_; -} - -int QuicCryptoServerHandshaker::NumServerConfigUpdateMessagesSent() const { - return num_server_config_update_messages_sent_; -} - -const CachedNetworkParameters* -QuicCryptoServerHandshaker::PreviousCachedNetworkParams() const { - return previous_cached_network_params_.get(); -} - -bool QuicCryptoServerHandshaker::UseStatelessRejectsIfPeerSupported() const { - return use_stateless_rejects_if_peer_supported_; -} - -bool QuicCryptoServerHandshaker::PeerSupportsStatelessRejects() const { - return peer_supports_stateless_rejects_; -} - -bool QuicCryptoServerHandshaker::ZeroRttAttempted() const { - return zero_rtt_attempted_; -} - -void QuicCryptoServerHandshaker::SetPeerSupportsStatelessRejects( - bool peer_supports_stateless_rejects) { - peer_supports_stateless_rejects_ = peer_supports_stateless_rejects; -} - -void QuicCryptoServerHandshaker::SetPreviousCachedNetworkParams( - CachedNetworkParameters cached_network_params) { - previous_cached_network_params_.reset( - new CachedNetworkParameters(cached_network_params)); -} - -bool QuicCryptoServerHandshaker::ShouldSendExpectCTHeader() const { - return signed_config_->proof.send_expect_ct_header; -} - -bool QuicCryptoServerHandshaker::GetBase64SHA256ClientChannelID( - string* output) const { - if (!encryption_established() || - crypto_negotiated_params_->channel_id.empty()) { - return false; - } - - const string& channel_id(crypto_negotiated_params_->channel_id); - uint8_t digest[SHA256_DIGEST_LENGTH]; - SHA256(reinterpret_cast<const uint8_t*>(channel_id.data()), channel_id.size(), - digest); - - QuicTextUtils::Base64Encode(digest, arraysize(digest), output); - return true; -} - -bool QuicCryptoServerHandshaker::encryption_established() const { - return encryption_established_; -} - -bool QuicCryptoServerHandshaker::handshake_confirmed() const { - return handshake_confirmed_; -} - -const QuicCryptoNegotiatedParameters& -QuicCryptoServerHandshaker::crypto_negotiated_params() const { - return *crypto_negotiated_params_; -} - -CryptoMessageParser* QuicCryptoServerHandshaker::crypto_message_parser() { - return QuicCryptoHandshaker::crypto_message_parser(); -} - -void QuicCryptoServerHandshaker::ProcessClientHello( - QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> - result, - std::unique_ptr<ProofSource::Details> proof_source_details, - std::unique_ptr<ProcessClientHelloResultCallback> done_cb) { - const CryptoHandshakeMessage& message = result->client_hello; - string error_details; - if (!helper_->CanAcceptClientHello( - message, session()->connection()->self_address(), &error_details)) { - done_cb->Run(QUIC_HANDSHAKE_FAILED, error_details, nullptr, nullptr, - nullptr); - return; - } - if (!result->info.server_nonce.empty()) { - ++num_handshake_messages_with_server_nonces_; - } - - if (num_handshake_messages_ == 1) { - // Client attempts zero RTT handshake by sending a non-inchoate CHLO. - QuicStringPiece public_value; - zero_rtt_attempted_ = message.GetStringPiece(kPUBS, &public_value); - } - - // Store the bandwidth estimate from the client. - if (result->cached_network_params.bandwidth_estimate_bytes_per_second() > 0) { - previous_cached_network_params_.reset( - new CachedNetworkParameters(result->cached_network_params)); - } - previous_source_address_tokens_ = result->info.source_address_tokens; - - const bool use_stateless_rejects_in_crypto_config = - use_stateless_rejects_if_peer_supported_ && - peer_supports_stateless_rejects_; - QuicConnection* connection = session()->connection(); - const QuicConnectionId server_designated_connection_id = - GenerateConnectionIdForReject(use_stateless_rejects_in_crypto_config); - crypto_config_->ProcessClientHello( - result, /*reject_only=*/false, connection->connection_id(), - connection->self_address(), GetClientAddress(), version(), - connection->supported_versions(), use_stateless_rejects_in_crypto_config, - server_designated_connection_id, connection->clock(), - connection->random_generator(), compressed_certs_cache_, - crypto_negotiated_params_, signed_config_, - QuicCryptoStream::CryptoMessageFramingOverhead(version()), - chlo_packet_size_, std::move(done_cb)); -} - -void QuicCryptoServerHandshaker::OverrideQuicConfigDefaults( - QuicConfig* config) {} - -QuicCryptoServerHandshaker::ValidateCallback::ValidateCallback( - QuicCryptoServerHandshaker* parent) - : parent_(parent) {} - -void QuicCryptoServerHandshaker::ValidateCallback::Cancel() { - parent_ = nullptr; -} - -void QuicCryptoServerHandshaker::ValidateCallback::Run( - QuicReferenceCountedPointer<Result> result, - std::unique_ptr<ProofSource::Details> details) { - if (parent_ != nullptr) { - parent_->FinishProcessingHandshakeMessage(std::move(result), - std::move(details)); - } -} - -QuicConnectionId QuicCryptoServerHandshaker::GenerateConnectionIdForReject( - bool use_stateless_rejects) { - if (!use_stateless_rejects) { - return 0; - } - return helper_->GenerateConnectionIdForReject( - session()->connection()->connection_id()); -} - -const QuicSocketAddress QuicCryptoServerHandshaker::GetClientAddress() { - return session()->connection()->peer_address(); -} - } // namespace net diff --git a/chromium/net/quic/core/quic_crypto_server_stream.h b/chromium/net/quic/core/quic_crypto_server_stream.h index 29271e48993..d7772930638 100644 --- a/chromium/net/quic/core/quic_crypto_server_stream.h +++ b/chromium/net/quic/core/quic_crypto_server_stream.h @@ -15,6 +15,7 @@ #include "net/quic/core/crypto/quic_crypto_server_config.h" #include "net/quic/core/proto/source_address_token.pb.h" #include "net/quic/core/quic_config.h" +#include "net/quic/core/quic_crypto_handshaker.h" #include "net/quic/core/quic_crypto_stream.h" #include "net/quic/core/quic_session.h" #include "net/quic/platform/api/quic_export.h" @@ -26,10 +27,6 @@ class CryptoHandshakeMessage; class QuicCryptoServerConfig; class QuicCryptoServerStreamBase; -namespace test { -class QuicCryptoServerStreamPeer; -} // namespace test - // TODO(alyssar) see what can be moved out of QuicCryptoServerStream with // various code and test refactoring. class QUIC_EXPORT_PRIVATE QuicCryptoServerStreamBase : public QuicCryptoStream { @@ -205,225 +202,6 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerStream DISALLOW_COPY_AND_ASSIGN(QuicCryptoServerStream); }; -class QUIC_EXPORT_PRIVATE QuicCryptoServerHandshaker - : public QuicCryptoServerStream::HandshakerDelegate, - public QuicCryptoHandshaker { - public: - // |crypto_config| must outlive the stream. - // |session| must outlive the stream. - // |helper| must outlive the stream. - QuicCryptoServerHandshaker(const QuicCryptoServerConfig* crypto_config, - QuicCryptoServerStream* stream, - QuicCompressedCertsCache* compressed_certs_cache, - bool use_stateless_rejects_if_peer_supported, - QuicSession* session, - QuicCryptoServerStream::Helper* helper); - - ~QuicCryptoServerHandshaker() override; - - // From HandshakerDelegate - void CancelOutstandingCallbacks() override; - bool GetBase64SHA256ClientChannelID(std::string* output) const override; - void SendServerConfigUpdate( - const CachedNetworkParameters* cached_network_params) override; - uint8_t NumHandshakeMessages() const override; - uint8_t NumHandshakeMessagesWithServerNonces() const override; - int NumServerConfigUpdateMessagesSent() const override; - const CachedNetworkParameters* PreviousCachedNetworkParams() const override; - bool UseStatelessRejectsIfPeerSupported() const override; - bool PeerSupportsStatelessRejects() const override; - bool ZeroRttAttempted() const override; - void SetPeerSupportsStatelessRejects( - bool peer_supports_stateless_rejects) override; - void SetPreviousCachedNetworkParams( - CachedNetworkParameters cached_network_params) override; - bool ShouldSendExpectCTHeader() const override; - - // From QuicCryptoStream - bool encryption_established() const override; - bool handshake_confirmed() const override; - const QuicCryptoNegotiatedParameters& crypto_negotiated_params() - const override; - CryptoMessageParser* crypto_message_parser() override; - - // From QuicCryptoHandshaker - void OnHandshakeMessage(const CryptoHandshakeMessage& message) override; - - protected: - virtual void ProcessClientHello( - QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> - result, - std::unique_ptr<ProofSource::Details> proof_source_details, - std::unique_ptr<ProcessClientHelloResultCallback> done_cb); - - // Hook that allows the server to set QuicConfig defaults just - // before going through the parameter negotiation step. - virtual void OverrideQuicConfigDefaults(QuicConfig* config); - - // Returns client address used to generate and validate source address token. - virtual const QuicSocketAddress GetClientAddress(); - - private: - friend class test::QuicCryptoServerStreamPeer; - - class ValidateCallback : public ValidateClientHelloResultCallback { - public: - explicit ValidateCallback(QuicCryptoServerHandshaker* parent); - // To allow the parent to detach itself from the callback before deletion. - void Cancel(); - - // From ValidateClientHelloResultCallback - void Run(QuicReferenceCountedPointer<Result> result, - std::unique_ptr<ProofSource::Details> details) override; - - private: - QuicCryptoServerHandshaker* parent_; - - DISALLOW_COPY_AND_ASSIGN(ValidateCallback); - }; - - class SendServerConfigUpdateCallback - : public BuildServerConfigUpdateMessageResultCallback { - public: - explicit SendServerConfigUpdateCallback(QuicCryptoServerHandshaker* parent); - SendServerConfigUpdateCallback(const SendServerConfigUpdateCallback&) = - delete; - void operator=(const SendServerConfigUpdateCallback&) = delete; - - // To allow the parent to detach itself from the callback before deletion. - void Cancel(); - - // From BuildServerConfigUpdateMessageResultCallback - void Run(bool ok, const CryptoHandshakeMessage& message) override; - - private: - QuicCryptoServerHandshaker* parent_; - }; - - // Invoked by ValidateCallback::RunImpl once initial validation of - // the client hello is complete. Finishes processing of the client - // hello message and handles handshake success/failure. - void FinishProcessingHandshakeMessage( - QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> - result, - std::unique_ptr<ProofSource::Details> details); - - class ProcessClientHelloCallback; - friend class ProcessClientHelloCallback; - - // Portion of FinishProcessingHandshakeMessage which executes after - // ProcessClientHello has been called. - void FinishProcessingHandshakeMessageAfterProcessClientHello( - const ValidateClientHelloResultCallback::Result& result, - QuicErrorCode error, - const std::string& error_details, - std::unique_ptr<CryptoHandshakeMessage> reply, - std::unique_ptr<DiversificationNonce> diversification_nonce, - std::unique_ptr<ProofSource::Details> proof_source_details); - - // Invoked by SendServerConfigUpdateCallback::RunImpl once the proof has been - // received. |ok| indicates whether or not the proof was successfully - // acquired, and |message| holds the partially-constructed message from - // SendServerConfigUpdate. - void FinishSendServerConfigUpdate(bool ok, - const CryptoHandshakeMessage& message); - - // Returns a new ConnectionId to be used for statelessly rejected connections - // if |use_stateless_rejects| is true. Returns 0 otherwise. - QuicConnectionId GenerateConnectionIdForReject(bool use_stateless_rejects); - - // Returns the QuicSession that this stream belongs to. - QuicSession* session() const { return session_; } - - // Returns the QuicVersion of the connection. - QuicVersion version() const { return session_->connection()->version(); } - - QuicCryptoServerStream* stream_; - - QuicSession* session_; - - // crypto_config_ contains crypto parameters for the handshake. - const QuicCryptoServerConfig* crypto_config_; - - // compressed_certs_cache_ contains a set of most recently compressed certs. - // Owned by QuicDispatcher. - QuicCompressedCertsCache* compressed_certs_cache_; - - // Server's certificate chain and signature of the server config, as provided - // by ProofSource::GetProof. - QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_; - - // Hash of the last received CHLO message which can be used for generating - // server config update messages. - std::string chlo_hash_; - - // Pointer to the helper for this crypto stream. Must outlive this stream. - QuicCryptoServerStream::Helper* helper_; - - // Number of handshake messages received by this stream. - uint8_t num_handshake_messages_; - - // Number of handshake messages received by this stream that contain - // server nonces (indicating that this is a non-zero-RTT handshake - // attempt). - uint8_t num_handshake_messages_with_server_nonces_; - - // Pointer to the active callback that will receive the result of - // BuildServerConfigUpdateMessage and forward it to - // FinishSendServerConfigUpdate. nullptr if no update message is currently - // being built. - SendServerConfigUpdateCallback* send_server_config_update_cb_; - - // Number of server config update (SCUP) messages sent by this stream. - int num_server_config_update_messages_sent_; - - // If the client provides CachedNetworkParameters in the STK in the CHLO, then - // store here, and send back in future STKs if we have no better bandwidth - // estimate to send. - std::unique_ptr<CachedNetworkParameters> previous_cached_network_params_; - - // Contains any source address tokens which were present in the CHLO. - SourceAddressTokens previous_source_address_tokens_; - - // If true, the server should use stateless rejects, so long as the - // client supports them, as indicated by - // peer_supports_stateless_rejects_. - bool use_stateless_rejects_if_peer_supported_; - - // Set to true, once the server has received information from the - // client that it supports stateless reject. - // TODO(jokulik): Remove once client stateless reject support - // becomes the default. - bool peer_supports_stateless_rejects_; - - // True if client attempts 0-rtt handshake (which can succeed or fail). If - // stateless rejects are used, this variable will be false for the stateless - // rejected connection and true for subsequent connections. - bool zero_rtt_attempted_; - - // Size of the packet containing the most recently received CHLO. - QuicByteCount chlo_packet_size_; - - // Pointer to the active callback that will receive the result of the client - // hello validation request and forward it to FinishProcessingHandshakeMessage - // for processing. nullptr if no handshake message is being validated. Note - // that this field is mutually exclusive with process_client_hello_cb_. - ValidateCallback* validate_client_hello_cb_; - - // Pointer to the active callback which will receive the results of - // ProcessClientHello and forward it to - // FinishProcessingHandshakeMessageAfterProcessClientHello. Note that this - // field is mutually exclusive with validate_client_hello_cb_. - ProcessClientHelloCallback* process_client_hello_cb_; - - bool encryption_established_; - bool handshake_confirmed_; - QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> - crypto_negotiated_params_; - - DISALLOW_COPY_AND_ASSIGN(QuicCryptoServerHandshaker); -}; - } // namespace net #endif // NET_QUIC_CORE_QUIC_CRYPTO_SERVER_STREAM_H_ diff --git a/chromium/net/quic/core/quic_crypto_stream.cc b/chromium/net/quic/core/quic_crypto_stream.cc index 3af89889881..197a170bd49 100644 --- a/chromium/net/quic/core/quic_crypto_stream.cc +++ b/chromium/net/quic/core/quic_crypto_stream.cc @@ -57,9 +57,7 @@ void QuicCryptoStream::OnDataAvailable() { } sequencer()->MarkConsumed(iov.iov_len); if (handshake_confirmed() && - crypto_message_parser()->InputBytesRemaining() == 0 && - FLAGS_quic_reloadable_flag_quic_release_crypto_stream_buffer) { - QUIC_FLAG_COUNT(quic_reloadable_flag_quic_release_crypto_stream_buffer); + crypto_message_parser()->InputBytesRemaining() == 0) { // If the handshake is complete and the current message has been fully // processed then no more handshake messages are likely to arrive soon // so release the memory in the stream sequencer. @@ -94,39 +92,4 @@ bool QuicCryptoStream::ExportTokenBindingKeyingMaterial(string* result) const { /* context= */ "", 32, result); } -QuicCryptoHandshaker::QuicCryptoHandshaker(QuicCryptoStream* stream, - QuicSession* session) - : stream_(stream), session_(session) { - crypto_framer_.set_visitor(this); -} - -QuicCryptoHandshaker::~QuicCryptoHandshaker() {} - -void QuicCryptoHandshaker::SendHandshakeMessage( - const CryptoHandshakeMessage& message) { - QUIC_DVLOG(1) << ENDPOINT << "Sending " - << message.DebugString(session()->perspective()); - session()->connection()->NeuterUnencryptedPackets(); - session()->OnCryptoHandshakeMessageSent(message); - const QuicData& data = message.GetSerialized(session()->perspective()); - stream_->WriteOrBufferData(QuicStringPiece(data.data(), data.length()), false, - nullptr); -} - -void QuicCryptoHandshaker::OnError(CryptoFramer* framer) { - QUIC_DLOG(WARNING) << "Error processing crypto data: " - << QuicErrorCodeToString(framer->error()); -} - -void QuicCryptoHandshaker::OnHandshakeMessage( - const CryptoHandshakeMessage& message) { - QUIC_DVLOG(1) << ENDPOINT << "Received " - << message.DebugString(session()->perspective()); - session()->OnCryptoHandshakeMessageReceived(message); -} - -CryptoMessageParser* QuicCryptoHandshaker::crypto_message_parser() { - return &crypto_framer_; -} - } // namespace net diff --git a/chromium/net/quic/core/quic_crypto_stream.h b/chromium/net/quic/core/quic_crypto_stream.h index fdbf4dc091e..8c3a548da3e 100644 --- a/chromium/net/quic/core/quic_crypto_stream.h +++ b/chromium/net/quic/core/quic_crypto_stream.h @@ -18,7 +18,6 @@ namespace net { -class CryptoHandshakeMessage; class QuicSession; // Crypto handshake messages in QUIC take place over a reserved stream with the @@ -80,33 +79,6 @@ class QUIC_EXPORT_PRIVATE QuicCryptoStream : public QuicStream { DISALLOW_COPY_AND_ASSIGN(QuicCryptoStream); }; -class QUIC_EXPORT_PRIVATE QuicCryptoHandshaker - : public CryptoFramerVisitorInterface { - public: - QuicCryptoHandshaker(QuicCryptoStream* stream, QuicSession* session); - - ~QuicCryptoHandshaker() override; - - // Sends |message| to the peer. - // TODO(wtc): return a success/failure status. - void SendHandshakeMessage(const CryptoHandshakeMessage& message); - - void OnError(CryptoFramer* framer) override; - void OnHandshakeMessage(const CryptoHandshakeMessage& message) override; - - CryptoMessageParser* crypto_message_parser(); - - private: - QuicSession* session() { return session_; } - - QuicCryptoStream* stream_; - QuicSession* session_; - - CryptoFramer crypto_framer_; - - DISALLOW_COPY_AND_ASSIGN(QuicCryptoHandshaker); -}; - } // namespace net #endif // NET_QUIC_CORE_QUIC_CRYPTO_STREAM_H_ diff --git a/chromium/net/quic/core/quic_data_writer.cc b/chromium/net/quic/core/quic_data_writer.cc index e4716b30138..7e831d8b181 100644 --- a/chromium/net/quic/core/quic_data_writer.cc +++ b/chromium/net/quic/core/quic_data_writer.cc @@ -59,6 +59,17 @@ bool QuicDataWriter::WriteUInt64(uint64_t value) { return WriteBytes(&value, sizeof(value)); } +bool QuicDataWriter::WriteUInt8AtOffset(uint8_t value, size_t offset) { + if (offset > length_) { + return false; + } + size_t old_length = length_; + length_ = offset; + bool result = WriteBytes(&value, sizeof(value)); + length_ = old_length; + return result; +} + bool QuicDataWriter::WriteBytesToUInt64(size_t num_bytes, uint64_t value) { if (num_bytes > sizeof(value)) { return false; diff --git a/chromium/net/quic/core/quic_data_writer.h b/chromium/net/quic/core/quic_data_writer.h index 46df6862cbb..b2de22d6823 100644 --- a/chromium/net/quic/core/quic_data_writer.h +++ b/chromium/net/quic/core/quic_data_writer.h @@ -48,6 +48,10 @@ class QUIC_EXPORT_PRIVATE QuicDataWriter { bool WriteUInt32(uint32_t value); bool WriteUInt64(uint64_t value); + // Writes |value| to the position |offset| from the start of the data. + // |offset| must be less than the current length of the writer. + bool WriteUInt8AtOffset(uint8_t value, size_t offset); + // Writes least significant |num_bytes| of a 64-bit unsigned integer in the // correct byte order. bool WriteBytesToUInt64(size_t num_bytes, uint64_t value); diff --git a/chromium/net/quic/core/quic_data_writer_test.cc b/chromium/net/quic/core/quic_data_writer_test.cc index c6c2d7c31d4..0baba766126 100644 --- a/chromium/net/quic/core/quic_data_writer_test.cc +++ b/chromium/net/quic/core/quic_data_writer_test.cc @@ -587,6 +587,35 @@ TEST_P(QuicDataWriterTest, WriteIntegers) { } } +TEST_P(QuicDataWriterTest, WriteBytes) { + char bytes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; + char buf[arraysize(bytes)]; + QuicDataWriter writer(arraysize(buf), buf, GetParam().perspective, + GetParam().endianness); + EXPECT_TRUE(writer.WriteBytes(bytes, arraysize(bytes))); + for (unsigned int i = 0; i < arraysize(bytes); ++i) { + EXPECT_EQ(bytes[i], buf[i]); + } +} + +TEST_P(QuicDataWriterTest, WriteUInt8AtOffset) { + char bytes[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'}; + char buf[arraysize(bytes)]; + for (unsigned int i = 0; i < arraysize(bytes); ++i) { + QuicDataWriter writer(arraysize(buf), buf, GetParam().perspective, + GetParam().endianness); + EXPECT_TRUE(writer.WriteBytes(bytes, arraysize(bytes))); + EXPECT_TRUE(writer.WriteUInt8AtOffset('I', i)); + for (unsigned int j = 0; j < arraysize(bytes); ++j) { + if (j == i) { + EXPECT_EQ('I', buf[j]); + } else { + EXPECT_EQ(bytes[j], buf[j]); + } + } + } +} + } // namespace } // namespace test } // namespace net diff --git a/chromium/net/quic/core/quic_flags_list.h b/chromium/net/quic/core/quic_flags_list.h index a5617f399e8..e2ad9ced578 100644 --- a/chromium/net/quic/core/quic_flags_list.h +++ b/chromium/net/quic/core/quic_flags_list.h @@ -55,30 +55,10 @@ QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_respect_http2_settings_frame, true) -// If true, only open limited number of quic sessions per epoll event. Leave the -// rest to next event. -QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_limit_num_new_sessions_per_epoll_loop, - true) - -// If true, QUIC server push will enabled by default. -QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_enable_server_push_by_default, - true) - -// If true, release QuicCryptoStream\'s read buffer when stream are less -// frequently used. -QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_release_crypto_stream_buffer, - true) - // If true, v33 QUIC client uses 1 bit to specify 8-byte connection id in // public flag. QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_remove_v33_hacks2, false) -// Enable QUIC force HOL blocking experiment. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_force_hol_blocking, true) - // If true, allows packets to be buffered in anticipation of a future CHLO, and // allow CHLO packets to be buffered until next iteration of the event loop. QUIC_FLAG(bool, FLAGS_quic_allow_chlo_buffering, true) @@ -96,7 +76,7 @@ QUIC_FLAG(double, FLAGS_quic_bbr_cwnd_gain, 2.0f) // If true, do not send or process stop waiting frames in QUIC if the NSTP // connection option is provided. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_no_stop_waiting_frames, false) +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_no_stop_waiting_frames, true) // Allows one self address change. QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_allow_one_address_change, false) @@ -122,21 +102,6 @@ QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_adaptive_time_loss, false) // compressed for QUIC version >= 38. QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_random_padding, true) -// If true, update state if trailing headers with a :final-offset key are -// received for a previously closed QUIC stream. -QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_final_offset_from_trailers, - true) - -// If enabled, use refactored stream creation methods. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_refactor_stream_creation, false) - -// If true, GFEs generate and validate source address token using the actual -// client IP for proxied session. -QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_use_client_address_for_stk_in_proxy, - true) - // If true, export a varz mapping QUIC non 0-rtt handshake with corresponding // frontend service. QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_account_handshake, false) @@ -151,9 +116,6 @@ QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_pcc, false) // If true, enable QUIC v40. QUIC_FLAG(bool, FLAGS_quic_enable_version_40, false) -// If true, use the more CPU efficient bandwidth sampler datastructure. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_faster_bandwidth_sampler, true) - // In QUIC, QuicSession gets notified when stream frames are acked, discarded or // retransmitted. QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_use_stream_notifier2, false) @@ -161,9 +123,6 @@ QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_use_stream_notifier2, false) // When true, defaults to BBR congestion control instead of Cubic. QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_default_to_bbr, false) -// If true, stream sent data is saved in streams rather than stream frames. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_stream_owns_data, false) - // Allow a new rate based recovery in QUIC BBR to be enabled via connection // option. QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr_rate_recovery, false) @@ -174,25 +133,79 @@ QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_handle_duplicate_trailers, false) -// Allows QUIC BBR up to twice the previously measured ack aggregation to be -// added to the CWND as long as bytes_in_flight goes below the target recently. -QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes2, - false) - // If true, disables support for QUIC version 36. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_36, false) +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_36, true) // If true, disables support for the packets-based QUIC congestion control // algorithms. QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_packets_based_cc, false) -// When enabled, adds up to 1.5x the previously measured ack aggregation in -// bytes to the CWND, but reduces that amount by 1/2 the bytes acked since the -// queue was drained. +// When enabled, ack frame uses a deque internally instead of a set. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_frames_deque2, false) + +// If true, QUIC packet creator passes a stack allocated SerializedPacket to the +// connection. +QUIC_FLAG(bool, + FLAGS_quic_reloadable_flag_quic_clear_packet_before_handed_over, + true) + +// If true, enable QUIC v41. +QUIC_FLAG(bool, FLAGS_quic_enable_version_41, false) + +// Small optimization for QuicSentPacketManager::HandleAckForSentPackets. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_handle_acks, true) + +// When true, respect configured limits on header list size. +QUIC_FLAG(bool, FLAGS_quic_restart_flag_quic_header_list_size, false) + +// When true, allows the LRTT connection option to cause QUIC BBR to exit +// STARTUP when in recovery and there has been no bandwidth increase for 1RTT. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr_exit_startup_on_loss, false) + +// If true, application data is saved before consumption in QUIC. QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes3, + FLAGS_quic_reloadable_flag_quic_save_data_before_consumption2, false) -// When enabled, ack frame uses a deque internally instead of a set. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_frames_deque, false) +// If buffered data in QUIC stream is less than this threshold, buffers all +// provided data or asks upper layer for more data. +QUIC_FLAG(uint32_t, FLAGS_quic_buffered_data_threshold, 8192u) + +// Max size of data slice in bytes for QUIC stream send buffer. +QUIC_FLAG(uint32_t, FLAGS_quic_send_buffer_max_data_slice_size, 4096u) + +// Enables the BBR1 and BBR2 QUIC connection options, which enable two forms of +// ack aggregation that prevent persistent standing queues. +QUIC_FLAG(bool, + FLAGS_quic_reloadable_flag_quic_bbr_ack_aggregation_bytes4, + false) + +// Add 4 new ack decimation modes to QUIC that are entirely time based at 1/4 +// or 1/8 RTT. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_ack_decimation, false) + +// Enables using the ConsumeDataFastPath more often for large transfers. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_consuming_data_faster, false) + +// If true, in QUIC, set dispatcher framer\'s version to client packet's +// version in QuicDispatcher::OnStatelessRejectorProcessDone. +QUIC_FLAG( + bool, + FLAGS_quic_reloadable_flag_quic_set_version_on_async_get_proof_returns, + false) + +// If true, check for packet number underflow when reading ack blocks. +QUIC_FLAG(bool, + FLAGS_quic_reloadable_flag_sanitize_framer_addrange_input, + false) + +// If true, change QUIC connection to start peer migration only when peer +// address is changed on server side. For peer address change on client side, +// update the peer ip address only. +QUIC_FLAG(bool, + FLAGS_quic_reloadable_flag_quic_disable_peer_migration_on_client, + true) + +// If true, QUIC v40 is enabled which includes changes to RST_STREAM, ACK +// and STREAM frames match IETF format. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_version_40, false) diff --git a/chromium/net/quic/core/quic_framer.cc b/chromium/net/quic/core/quic_framer.cc index b2f29128df3..a1b05fa0871 100644 --- a/chromium/net/quic/core/quic_framer.cc +++ b/chromium/net/quic/core/quic_framer.cc @@ -27,6 +27,7 @@ #include "net/quic/platform/api/quic_logging.h" #include "net/quic/platform/api/quic_map_util.h" #include "net/quic/platform/api/quic_ptr_util.h" +#include "net/quic/platform/api/quic_str_cat.h" using std::string; @@ -56,40 +57,51 @@ const uint8_t kPublicHeaderSequenceNumberShift = 4; // // Special Frame Types encode both a Frame Type and corresponding flags // all in the Frame Type byte. Currently defined Special Frame Types are: -// Stream : 0b 1xxxxxxx -// Ack : 0b 01xxxxxx +// Stream : 0b 11xxxxxx +// Ack : 0b 101xxxxx // // Semantics of the flag bits above (the x bits) depends on the frame type. // Masks to determine if the frame type is a special use // and for specific special frame types. const uint8_t kQuicFrameTypeSpecialMask = 0xE0; // 0b 11100000 -const uint8_t kQuicFrameTypeStreamMask = 0x80; -const uint8_t kQuicFrameTypeAckMask = 0x40; +const uint8_t kQuicFrameTypeStreamMask_Pre40 = 0x80; +const uint8_t kQuicFrameTypeStreamMask = 0xC0; +const uint8_t kQuicFrameTypeAckMask_Pre40 = 0x40; +const uint8_t kQuicFrameTypeAckMask = 0xA0; +// Stream type format is 11FSSOOD. // Stream frame relative shifts and masks for interpreting the stream flags. // StreamID may be 1, 2, 3, or 4 bytes. -const uint8_t kQuicStreamIdShift = 2; -const uint8_t kQuicStreamIDLengthMask = 0x03; +const uint8_t kQuicStreamIdShift_Pre40 = 2; +const uint8_t kQuicStreamIDLengthMask_Pre40 = 0x03; +const uint8_t kQuicStreamIDLengthShift = 3; +const uint8_t kQuicStreamIDLengthNumBits = 2; -// Offset may be 0, 2, 3, 4, 5, 6, 7, 8 bytes. -const uint8_t kQuicStreamOffsetShift = 3; -const uint8_t kQuicStreamOffsetMask = 0x07; +// Offset may be 0, 2, 4, or 8 bytes. +const uint8_t kQuicStreamShift_Pre40 = 3; +const uint8_t kQuicStreamOffsetMask_Pre40 = 0x07; +const uint8_t kQuicStreamOffsetNumBits = 2; +const uint8_t kQuicStreamOffsetShift = 1; // Data length may be 0 or 2 bytes. -const uint8_t kQuicStreamDataLengthShift = 1; -const uint8_t kQuicStreamDataLengthMask = 0x01; +const uint8_t kQuicStreamDataLengthShift_Pre40 = 1; +const uint8_t kQuicStreamDataLengthMask_Pre40 = 0x01; +const uint8_t kQuicStreamDataLengthShift = 0; // Fin bit may be set or not. -const uint8_t kQuicStreamFinShift = 1; -const uint8_t kQuicStreamFinMask = 0x01; +const uint8_t kQuicStreamFinShift_Pre40 = 1; +const uint8_t kQuicStreamFinMask_Pre40 = 0x01; +const uint8_t kQuicStreamFinShift = 5; // packet number size shift used in AckFrames. -const uint8_t kQuicSequenceNumberLengthShift = 2; +const uint8_t kQuicSequenceNumberLengthNumBits = 2; +const uint8_t kActBlockLengthOffset = 0; +const uint8_t kLargestAckedOffset = 2; // Acks may have only one ack block. -const uint8_t kQuicHasMultipleAckBlocksMask = 0x01; -const uint8_t kQuicHasMultipleAckBlocksShift = 1; +const uint8_t kQuicHasMultipleAckBlocksOffset_Pre40 = 5; +const uint8_t kQuicHasMultipleAckBlocksOffset = 4; // Returns the absolute value of the difference between |a| and |b|. QuicPacketNumber Delta(QuicPacketNumber a, QuicPacketNumber b) { @@ -107,8 +119,8 @@ QuicPacketNumber ClosestTo(QuicPacketNumber target, } QuicPacketNumberLength ReadSequenceNumberLength(uint8_t flags) { - switch (flags & PACKET_FLAGS_6BYTE_PACKET) { - case PACKET_FLAGS_6BYTE_PACKET: + switch (flags & PACKET_FLAGS_8BYTE_PACKET) { + case PACKET_FLAGS_8BYTE_PACKET: return PACKET_6BYTE_PACKET_NUMBER; case PACKET_FLAGS_4BYTE_PACKET: return PACKET_4BYTE_PACKET_NUMBER; @@ -122,6 +134,24 @@ QuicPacketNumberLength ReadSequenceNumberLength(uint8_t flags) { } } +QuicPacketNumberLength ReadAckPacketNumberLength(QuicVersion version, + uint8_t flags) { + switch (flags & PACKET_FLAGS_8BYTE_PACKET) { + case PACKET_FLAGS_8BYTE_PACKET: + return version <= QUIC_VERSION_39 ? PACKET_6BYTE_PACKET_NUMBER + : PACKET_8BYTE_PACKET_NUMBER; + case PACKET_FLAGS_4BYTE_PACKET: + return PACKET_4BYTE_PACKET_NUMBER; + case PACKET_FLAGS_2BYTE_PACKET: + return PACKET_2BYTE_PACKET_NUMBER; + case PACKET_FLAGS_1BYTE_PACKET: + return PACKET_1BYTE_PACKET_NUMBER; + default: + QUIC_BUG << "Unreachable case statement."; + return PACKET_6BYTE_PACKET_NUMBER; + } +} + } // namespace QuicFramer::QuicFramer(const QuicVersionVector& supported_versions, @@ -151,11 +181,12 @@ QuicFramer::QuicFramer(const QuicVersionVector& supported_versions, QuicFramer::~QuicFramer() {} // static -size_t QuicFramer::GetMinStreamFrameSize(QuicStreamId stream_id, +size_t QuicFramer::GetMinStreamFrameSize(QuicVersion version, + QuicStreamId stream_id, QuicStreamOffset offset, bool last_frame_in_packet) { return kQuicFrameTypeSize + GetStreamIdSize(stream_id) + - GetStreamOffsetSize(offset) + + GetStreamOffsetSize(version, offset) + (last_frame_in_packet ? 0 : kQuicStreamPayloadLengthSize); } @@ -217,20 +248,32 @@ size_t QuicFramer::GetStreamIdSize(QuicStreamId stream_id) { } // static -size_t QuicFramer::GetStreamOffsetSize(QuicStreamOffset offset) { - // 0 is a special case. - if (offset == 0) { - return 0; - } - // 2 through 8 are the remaining sizes. - offset >>= 8; - for (int i = 2; i <= 8; ++i) { - offset >>= 8; +size_t QuicFramer::GetStreamOffsetSize(QuicVersion version, + QuicStreamOffset offset) { + if (version < QUIC_VERSION_40) { + // 0 is a special case. if (offset == 0) { + return 0; + } + // 2 through 8 are the remaining sizes. + offset >>= 8; + for (int i = 2; i <= 8; ++i) { + offset >>= 8; + if (offset == 0) { + return i; + } + } + QUIC_BUG << "Failed to determine StreamOffsetSize."; + return 8; + } + // try 0, 2 and 4. + for (int i = 0; i <= 4; i += 2) { + if ((offset >> (8 * i)) == 0) { return i; } } - QUIC_BUG << "Failed to determine StreamOffsetSize."; + + // 8 is the only remaining valid value and will contain any 64bit offset. return 8; } @@ -849,6 +892,7 @@ bool QuicFramer::ProcessPublicHeader(QuicDataReader* reader, // static QuicPacketNumberLength QuicFramer::GetMinPacketNumberLength( + QuicVersion version, QuicPacketNumber packet_number) { if (packet_number < 1 << (PACKET_1BYTE_PACKET_NUMBER * 8)) { return PACKET_1BYTE_PACKET_NUMBER; @@ -857,7 +901,8 @@ QuicPacketNumberLength QuicFramer::GetMinPacketNumberLength( } else if (packet_number < UINT64_C(1) << (PACKET_4BYTE_PACKET_NUMBER * 8)) { return PACKET_4BYTE_PACKET_NUMBER; } else { - return PACKET_6BYTE_PACKET_NUMBER; + return version <= QUIC_VERSION_39 ? PACKET_6BYTE_PACKET_NUMBER + : PACKET_8BYTE_PACKET_NUMBER; } } @@ -872,10 +917,11 @@ uint8_t QuicFramer::GetPacketNumberFlags( case PACKET_4BYTE_PACKET_NUMBER: return PACKET_FLAGS_4BYTE_PACKET; case PACKET_6BYTE_PACKET_NUMBER: - return PACKET_FLAGS_6BYTE_PACKET; + case PACKET_8BYTE_PACKET_NUMBER: + return PACKET_FLAGS_8BYTE_PACKET; default: QUIC_BUG << "Unreachable case statement."; - return PACKET_FLAGS_6BYTE_PACKET; + return PACKET_FLAGS_8BYTE_PACKET; } } @@ -966,7 +1012,11 @@ bool QuicFramer::ProcessFrameData(QuicDataReader* reader, if (frame_type & kQuicFrameTypeSpecialMask) { // Stream Frame - if (frame_type & kQuicFrameTypeStreamMask) { + if ((quic_version_ < QUIC_VERSION_40 && + (frame_type & kQuicFrameTypeStreamMask_Pre40)) || + (quic_version_ >= QUIC_VERSION_40 && + ((frame_type & kQuicFrameTypeStreamMask) == + kQuicFrameTypeStreamMask))) { QuicStreamFrame frame; if (!ProcessStreamFrame(reader, frame_type, &frame)) { return RaiseError(QUIC_INVALID_STREAM_DATA); @@ -981,7 +1031,11 @@ bool QuicFramer::ProcessFrameData(QuicDataReader* reader, } // Ack Frame - if (frame_type & kQuicFrameTypeAckMask) { + if ((quic_version_ < QUIC_VERSION_40 && + (frame_type & kQuicFrameTypeAckMask_Pre40)) || + (quic_version_ >= QUIC_VERSION_40 && + ((frame_type & kQuicFrameTypeSpecialMask) == + kQuicFrameTypeAckMask))) { QuicAckFrame frame; if (!ProcessAckFrame(reader, frame_type, &frame)) { return RaiseError(QUIC_INVALID_ACK_DATA); @@ -1121,30 +1175,88 @@ bool QuicFramer::ProcessFrameData(QuicDataReader* reader, return true; } +namespace { +// Create a mask that sets the last |num_bits| to 1 and the rest to 0. +inline uint8_t GetMaskFromNumBits(uint8_t num_bits) { + return (1u << num_bits) - 1; +} + +// Extract |num_bits| from |flags| offset by |offset|. +uint8_t ExtractBits(uint8_t flags, uint8_t num_bits, uint8_t offset) { + return (flags >> offset) & GetMaskFromNumBits(num_bits); +} + +// Extract the bit at position |offset| from |flags| as a bool. +bool ExtractBit(uint8_t flags, uint8_t offset) { + return ((flags >> offset) & GetMaskFromNumBits(1)) != 0; +} + +// Set |num_bits|, offset by |offset| to |val| in |flags|. +void SetBits(uint8_t* flags, uint8_t val, uint8_t num_bits, uint8_t offset) { + DCHECK_LE(val, GetMaskFromNumBits(num_bits)); + *flags |= val << offset; +} + +// Set the bit at position |offset| to |val| in |flags|. +void SetBit(uint8_t* flags, bool val, uint8_t offset) { + SetBits(flags, val ? 1 : 0, 1, offset); +} +} // namespace + bool QuicFramer::ProcessStreamFrame(QuicDataReader* reader, uint8_t frame_type, QuicStreamFrame* frame) { uint8_t stream_flags = frame_type; - stream_flags &= ~kQuicFrameTypeStreamMask; + uint8_t stream_id_length = 0; + uint8_t offset_length = 4; + bool has_data_length = true; + if (quic_version_ < QUIC_VERSION_40) { + stream_flags &= ~kQuicFrameTypeStreamMask_Pre40; - // Read from right to left: StreamID, Offset, Data Length, Fin. - const uint8_t stream_id_length = (stream_flags & kQuicStreamIDLengthMask) + 1; - stream_flags >>= kQuicStreamIdShift; + // Read from right to left: StreamID, Offset, Data Length, Fin. + stream_id_length = (stream_flags & kQuicStreamIDLengthMask_Pre40) + 1; + stream_flags >>= kQuicStreamIdShift_Pre40; - uint8_t offset_length = (stream_flags & kQuicStreamOffsetMask); - // There is no encoding for 1 byte, only 0 and 2 through 8. - if (offset_length > 0) { - offset_length += 1; - } - stream_flags >>= kQuicStreamOffsetShift; + offset_length = (stream_flags & kQuicStreamOffsetMask_Pre40); + // There is no encoding for 1 byte, only 0 and 2 through 8. + if (offset_length > 0) { + offset_length += 1; + } + stream_flags >>= kQuicStreamShift_Pre40; + + has_data_length = (stream_flags & kQuicStreamDataLengthMask_Pre40) == + kQuicStreamDataLengthMask_Pre40; + stream_flags >>= kQuicStreamDataLengthShift_Pre40; - bool has_data_length = - (stream_flags & kQuicStreamDataLengthMask) == kQuicStreamDataLengthMask; - stream_flags >>= kQuicStreamDataLengthShift; + frame->fin = + (stream_flags & kQuicStreamFinMask_Pre40) == kQuicStreamFinShift_Pre40; + + } else { + stream_flags &= ~kQuicFrameTypeStreamMask; - frame->fin = (stream_flags & kQuicStreamFinMask) == kQuicStreamFinShift; + stream_id_length = 1 + ExtractBits(stream_flags, kQuicStreamIDLengthNumBits, + kQuicStreamIDLengthShift); + offset_length = 1 << ExtractBits(stream_flags, kQuicStreamOffsetNumBits, + kQuicStreamOffsetShift); + + if (offset_length == 1) { + offset_length = 0; + } + + has_data_length = ExtractBit(stream_flags, kQuicStreamDataLengthShift); + + frame->fin = ExtractBit(stream_flags, kQuicStreamFinShift); + } + + uint16_t data_len = 0; + if (has_data_length && quic_version_ > QUIC_VERSION_39) { + if (!reader->ReadUInt16(&data_len)) { + set_detailed_error("Unable to read data length."); + return false; + } + } uint64_t stream_id = 0; if (!reader->ReadBytesToUInt64(stream_id_length, &stream_id)) { set_detailed_error("Unable to read stream_id."); @@ -1161,9 +1273,16 @@ bool QuicFramer::ProcessStreamFrame(QuicDataReader* reader, // TODO(ianswett): Don't use QuicStringPiece as an intermediary. QuicStringPiece data; if (has_data_length) { - if (!reader->ReadStringPiece16(&data)) { - set_detailed_error("Unable to read frame data."); - return false; + if (quic_version_ > QUIC_VERSION_39) { + if (!reader->ReadStringPiece(&data, data_len)) { + set_detailed_error("Unable to read frame data."); + return false; + } + } else { + if (!reader->ReadStringPiece16(&data)) { + set_detailed_error("Unable to read frame data."); + return false; + } } } else { if (!reader->ReadStringPiece(&data, reader->BytesRemaining())) { @@ -1180,16 +1299,31 @@ bool QuicFramer::ProcessStreamFrame(QuicDataReader* reader, bool QuicFramer::ProcessAckFrame(QuicDataReader* reader, uint8_t frame_type, QuicAckFrame* ack_frame) { + bool has_ack_blocks = + ExtractBit(frame_type, quic_version_ < QUIC_VERSION_40 + ? kQuicHasMultipleAckBlocksOffset_Pre40 + : kQuicHasMultipleAckBlocksOffset); + uint8_t num_ack_blocks = 0; + uint8_t num_received_packets = 0; + if (quic_version_ > QUIC_VERSION_39) { + if (has_ack_blocks && !reader->ReadUInt8(&num_ack_blocks)) { + set_detailed_error("Unable to read num of ack blocks."); + return false; + } + if (!reader->ReadUInt8(&num_received_packets)) { + set_detailed_error("Unable to read num received packets."); + return false; + } + } + // Determine the two lengths from the frame type: largest acked length, // ack block length. - const QuicPacketNumberLength ack_block_length = - ReadSequenceNumberLength(frame_type); - frame_type >>= kQuicSequenceNumberLengthShift; - const QuicPacketNumberLength largest_acked_length = - ReadSequenceNumberLength(frame_type); - frame_type >>= kQuicSequenceNumberLengthShift; - frame_type >>= kQuicHasMultipleAckBlocksShift; - bool has_ack_blocks = frame_type & kQuicHasMultipleAckBlocksMask; + const QuicPacketNumberLength ack_block_length = ReadAckPacketNumberLength( + quic_version_, ExtractBits(frame_type, kQuicSequenceNumberLengthNumBits, + kActBlockLengthOffset)); + const QuicPacketNumberLength largest_acked_length = ReadAckPacketNumberLength( + quic_version_, ExtractBits(frame_type, kQuicSequenceNumberLengthNumBits, + kLargestAckedOffset)); if (!reader->ReadBytesToUInt64(largest_acked_length, &ack_frame->largest_observed)) { @@ -1210,9 +1344,9 @@ bool QuicFramer::ProcessAckFrame(QuicDataReader* reader, QuicTime::Delta::FromMicroseconds(ack_delay_time_us); } - uint8_t num_ack_blocks = 0; if (has_ack_blocks) { - if (!reader->ReadUInt8(&num_ack_blocks)) { + if (quic_version_ <= QUIC_VERSION_39 && + !reader->ReadUInt8(&num_ack_blocks)) { set_detailed_error("Unable to read num of ack blocks."); return false; } @@ -1223,9 +1357,20 @@ bool QuicFramer::ProcessAckFrame(QuicDataReader* reader, set_detailed_error("Unable to read first ack block length."); return false; } + + if (FLAGS_quic_reloadable_flag_sanitize_framer_addrange_input && + first_block_length > ack_frame->largest_observed + 1) { + QUIC_FLAG_COUNT_N(quic_reloadable_flag_sanitize_framer_addrange_input, 1, + 2); + set_detailed_error(QuicStrCat("Underflow with first ack block length ", + first_block_length, " largest acked is ", + ack_frame->largest_observed + 1, ".") + .c_str()); + return false; + } QuicPacketNumber first_received = ack_frame->largest_observed + 1 - first_block_length; - ack_frame->packets.Add(first_received, ack_frame->largest_observed + 1); + ack_frame->packets.AddRange(first_received, ack_frame->largest_observed + 1); if (num_ack_blocks > 0) { for (size_t i = 0; i < num_ack_blocks; ++i) { @@ -1239,29 +1384,41 @@ bool QuicFramer::ProcessAckFrame(QuicDataReader* reader, set_detailed_error("Unable to ack block length."); return false; } + if (FLAGS_quic_reloadable_flag_sanitize_framer_addrange_input && + first_received < gap + current_block_length) { + QUIC_FLAG_COUNT_N(quic_reloadable_flag_sanitize_framer_addrange_input, + 2, 2); + set_detailed_error( + QuicStrCat("Underflow with ack block length ", current_block_length, + ", end of block is ", first_received - gap, ".") + .c_str()); + return false; + } + first_received -= (gap + current_block_length); if (current_block_length > 0) { - ack_frame->packets.Add(first_received, - first_received + current_block_length); + ack_frame->packets.AddRange(first_received, + first_received + current_block_length); } } } - if (!ProcessTimestampsInAckFrame(reader, ack_frame)) { + if (quic_version_ <= QUIC_VERSION_39 && + !reader->ReadUInt8(&num_received_packets)) { + set_detailed_error("Unable to read num received packets."); + return false; + } + + if (!ProcessTimestampsInAckFrame(num_received_packets, reader, ack_frame)) { return false; } return true; } -bool QuicFramer::ProcessTimestampsInAckFrame(QuicDataReader* reader, +bool QuicFramer::ProcessTimestampsInAckFrame(uint8_t num_received_packets, + QuicDataReader* reader, QuicAckFrame* ack_frame) { - uint8_t num_received_packets; - if (!reader->ReadUInt8(&num_received_packets)) { - set_detailed_error("Unable to read num received packets."); - return false; - } - if (num_received_packets > 0) { uint8_t delta_from_largest_observed; if (!reader->ReadUInt8(&delta_from_largest_observed)) { @@ -1334,9 +1491,11 @@ bool QuicFramer::ProcessRstStreamFrame(QuicDataReader* reader, return false; } - if (!reader->ReadUInt64(&frame->byte_offset)) { - set_detailed_error("Unable to read rst stream sent byte offset."); - return false; + if (quic_version_ <= QUIC_VERSION_39) { + if (!reader->ReadUInt64(&frame->byte_offset)) { + set_detailed_error("Unable to read rst stream sent byte offset."); + return false; + } } uint32_t error_code; @@ -1351,6 +1510,14 @@ bool QuicFramer::ProcessRstStreamFrame(QuicDataReader* reader, } frame->error_code = static_cast<QuicRstStreamErrorCode>(error_code); + + if (quic_version_ > QUIC_VERSION_39) { + if (!reader->ReadUInt64(&frame->byte_offset)) { + set_detailed_error("Unable to read rst stream sent byte offset."); + return false; + } + } + return true; } @@ -1642,9 +1809,9 @@ size_t QuicFramer::GetAckFrameSize( AckFrameInfo ack_info = GetAckFrameInfo(ack); QuicPacketNumberLength largest_acked_length = - GetMinPacketNumberLength(ack.largest_observed); + GetMinPacketNumberLength(quic_version_, ack.largest_observed); QuicPacketNumberLength ack_block_length = - GetMinPacketNumberLength(ack_info.max_block_length); + GetMinPacketNumberLength(quic_version_, ack_info.max_block_length); ack_size = GetMinAckFrameSize(quic_version_, largest_acked_length); // First ack block length. @@ -1667,7 +1834,7 @@ size_t QuicFramer::ComputeFrameLength( QuicPacketNumberLength packet_number_length) { switch (frame.type) { case STREAM_FRAME: - return GetMinStreamFrameSize(frame.stream_frame->stream_id, + return GetMinStreamFrameSize(quic_version_, frame.stream_frame->stream_id, frame.stream_frame->offset, last_frame_in_packet) + frame.stream_frame->data_length; @@ -1714,24 +1881,62 @@ bool QuicFramer::AppendTypeByte(const QuicFrame& frame, if (frame.stream_frame == nullptr) { QUIC_BUG << "Failed to append STREAM frame with no stream_frame."; } - // Fin bit. - type_byte |= frame.stream_frame->fin ? kQuicStreamFinMask : 0; - - // Data Length bit. - type_byte <<= kQuicStreamDataLengthShift; - type_byte |= no_stream_frame_length ? 0 : kQuicStreamDataLengthMask; - - // Offset 3 bits. - type_byte <<= kQuicStreamOffsetShift; - const size_t offset_len = GetStreamOffsetSize(frame.stream_frame->offset); - if (offset_len > 0) { - type_byte |= offset_len - 1; - } + if (quic_version_ < QUIC_VERSION_40) { + // Fin bit. + type_byte |= frame.stream_frame->fin ? kQuicStreamFinMask_Pre40 : 0; + + // Data Length bit. + type_byte <<= kQuicStreamDataLengthShift_Pre40; + type_byte |= + no_stream_frame_length ? 0 : kQuicStreamDataLengthMask_Pre40; + + // Offset 3 bits. + type_byte <<= kQuicStreamShift_Pre40; + const size_t offset_len = + GetStreamOffsetSize(quic_version_, frame.stream_frame->offset); + if (offset_len > 0) { + type_byte |= offset_len - 1; + } + + // stream id 2 bits. + type_byte <<= kQuicStreamIdShift_Pre40; + type_byte |= GetStreamIdSize(frame.stream_frame->stream_id) - 1; + type_byte |= + kQuicFrameTypeStreamMask_Pre40; // Set Stream Frame Type to 1. + } else { + // Fin bit. + SetBit(&type_byte, frame.stream_frame->fin, kQuicStreamFinShift); + + // Data Length bit. + SetBit(&type_byte, !no_stream_frame_length, kQuicStreamDataLengthShift); + + // Offset 2 bits. + uint8_t offset_len_encode = 3; + switch ( + GetStreamOffsetSize(quic_version_, frame.stream_frame->offset)) { + case 0: + offset_len_encode = 0; + break; + case 2: + offset_len_encode = 1; + break; + case 4: + offset_len_encode = 2; + break; + case 8: + offset_len_encode = 3; + break; + default: + QUIC_BUG << "Invalid offset_length."; + } + SetBits(&type_byte, offset_len_encode, kQuicStreamOffsetNumBits, + kQuicStreamOffsetShift); - // stream id 2 bits. - type_byte <<= kQuicStreamIdShift; - type_byte |= GetStreamIdSize(frame.stream_frame->stream_id) - 1; - type_byte |= kQuicFrameTypeStreamMask; // Set Stream Frame Type to 1. + // stream id 2 bits. + SetBits(&type_byte, GetStreamIdSize(frame.stream_frame->stream_id) - 1, + kQuicStreamIDLengthNumBits, kQuicStreamIDLengthShift); + type_byte |= kQuicFrameTypeStreamMask; // Set Stream Frame Type to 1. + } break; } case ACK_FRAME: @@ -1752,7 +1957,7 @@ bool QuicFramer::AppendPacketNumber(QuicPacketNumberLength packet_number_length, QuicPacketNumber packet_number, QuicDataWriter* writer) { size_t length = packet_number_length; - if (length != 1 && length != 2 && length != 4 && length != 6) { + if (length != 1 && length != 2 && length != 4 && length != 6 && length != 8) { QUIC_BUG << "Invalid packet_number_length: " << length; return false; } @@ -1794,17 +1999,24 @@ bool QuicFramer::AppendAckBlock(uint8_t gap, bool QuicFramer::AppendStreamFrame(const QuicStreamFrame& frame, bool no_stream_frame_length, QuicDataWriter* writer) { + if (!no_stream_frame_length && quic_version_ > QUIC_VERSION_39) { + if ((frame.data_length > std::numeric_limits<uint16_t>::max()) || + !writer->WriteUInt16(static_cast<uint16_t>(frame.data_length))) { + QUIC_BUG << "Writing stream frame length failed"; + return false; + } + } if (!AppendStreamId(GetStreamIdSize(frame.stream_id), frame.stream_id, writer)) { QUIC_BUG << "Writing stream id size failed."; return false; } - if (!AppendStreamOffset(GetStreamOffsetSize(frame.offset), frame.offset, - writer)) { + if (!AppendStreamOffset(GetStreamOffsetSize(quic_version_, frame.offset), + frame.offset, writer)) { QUIC_BUG << "Writing offset size failed."; return false; } - if (!no_stream_frame_length) { + if (!no_stream_frame_length && quic_version_ <= QUIC_VERSION_39) { if ((frame.data_length > std::numeric_limits<uint16_t>::max()) || !writer->WriteUInt16(static_cast<uint16_t>(frame.data_length))) { QUIC_BUG << "Writing stream frame length failed"; @@ -1842,9 +2054,9 @@ bool QuicFramer::AppendAckFrameAndTypeByte(const QuicAckFrame& frame, const AckFrameInfo new_ack_info = GetAckFrameInfo(frame); QuicPacketNumber largest_acked = frame.largest_observed; QuicPacketNumberLength largest_acked_length = - GetMinPacketNumberLength(largest_acked); + GetMinPacketNumberLength(quic_version_, largest_acked); QuicPacketNumberLength ack_block_length = - GetMinPacketNumberLength(new_ack_info.max_block_length); + GetMinPacketNumberLength(quic_version_, new_ack_info.max_block_length); // Calculate available bytes for timestamps and ack blocks. int32_t available_timestamp_and_ack_block_bytes = writer->capacity() - writer->length() - ack_block_length - @@ -1855,24 +2067,52 @@ bool QuicFramer::AppendAckFrameAndTypeByte(const QuicAckFrame& frame, // Write out the type byte by setting the low order bits and doing shifts // to make room for the next bit flags to be set. // Whether there are multiple ack blocks. - uint8_t type_byte = - new_ack_info.num_ack_blocks == 0 ? 0 : kQuicHasMultipleAckBlocksMask; - type_byte <<= kQuicHasMultipleAckBlocksShift; + uint8_t type_byte = 0; + SetBit(&type_byte, new_ack_info.num_ack_blocks != 0, + quic_version_ < QUIC_VERSION_40 ? kQuicHasMultipleAckBlocksOffset_Pre40 + : kQuicHasMultipleAckBlocksOffset); - // Largest acked length. - type_byte <<= kQuicSequenceNumberLengthShift; - type_byte |= GetPacketNumberFlags(largest_acked_length); + SetBits(&type_byte, GetPacketNumberFlags(largest_acked_length), + kQuicSequenceNumberLengthNumBits, kLargestAckedOffset); - // Ack block length. - type_byte <<= kQuicSequenceNumberLengthShift; - type_byte |= GetPacketNumberFlags(ack_block_length); + SetBits(&type_byte, GetPacketNumberFlags(ack_block_length), + kQuicSequenceNumberLengthNumBits, kActBlockLengthOffset); - type_byte |= kQuicFrameTypeAckMask; + if (quic_version_ < QUIC_VERSION_40) { + type_byte |= kQuicFrameTypeAckMask_Pre40; + } else { + type_byte |= kQuicFrameTypeAckMask; + } if (!writer->WriteUInt8(type_byte)) { return false; } + size_t num_timestamps_offset = 0; + size_t max_num_ack_blocks = available_timestamp_and_ack_block_bytes / + (ack_block_length + PACKET_1BYTE_PACKET_NUMBER); + + // Number of ack blocks. + size_t num_ack_blocks = + std::min(new_ack_info.num_ack_blocks, max_num_ack_blocks); + if (num_ack_blocks > std::numeric_limits<uint8_t>::max()) { + num_ack_blocks = std::numeric_limits<uint8_t>::max(); + } + + if (quic_version_ > QUIC_VERSION_39) { + if (num_ack_blocks > 0 && !writer->WriteBytes(&num_ack_blocks, 1)) { + return false; + } + + // Write a placeholder for the number of timestamps which will be + // overwritten after the ack blocks have been written. + num_timestamps_offset = writer->length(); + uint8_t num_timestamps = 0; + if (!writer->WriteUInt8(num_timestamps)) { + return false; + } + } + // Largest acked. if (!AppendPacketNumber(largest_acked_length, largest_acked, writer)) { return false; @@ -1888,19 +2128,11 @@ bool QuicFramer::AppendAckFrameAndTypeByte(const QuicAckFrame& frame, return false; } - size_t max_num_ack_blocks = available_timestamp_and_ack_block_bytes / - (ack_block_length + PACKET_1BYTE_PACKET_NUMBER); - - // Number of ack blocks. - size_t num_ack_blocks = - std::min(new_ack_info.num_ack_blocks, max_num_ack_blocks); - if (num_ack_blocks > std::numeric_limits<uint8_t>::max()) { - num_ack_blocks = std::numeric_limits<uint8_t>::max(); - } - - if (num_ack_blocks > 0) { - if (!writer->WriteBytes(&num_ack_blocks, 1)) { - return false; + if (quic_version_ <= QUIC_VERSION_39) { + if (num_ack_blocks > 0) { + if (!writer->WriteBytes(&num_ack_blocks, 1)) { + return false; + } } } @@ -1971,7 +2203,7 @@ bool QuicFramer::AppendAckFrameAndTypeByte(const QuicAckFrame& frame, // append any of them. if (writer->capacity() - writer->length() >= GetAckFrameTimeStampSize(frame)) { - if (!AppendTimestampToAckFrame(frame, writer)) { + if (!AppendTimestampsToAckFrame(frame, num_timestamps_offset, writer)) { return false; } } else { @@ -1984,8 +2216,9 @@ bool QuicFramer::AppendAckFrameAndTypeByte(const QuicAckFrame& frame, return true; } -bool QuicFramer::AppendTimestampToAckFrame(const QuicAckFrame& frame, - QuicDataWriter* writer) { +bool QuicFramer::AppendTimestampsToAckFrame(const QuicAckFrame& frame, + size_t num_timestamps_offset, + QuicDataWriter* writer) { DCHECK_GE(std::numeric_limits<uint8_t>::max(), frame.received_packet_times.size()); // num_received_packets is only 1 byte. @@ -1995,8 +2228,15 @@ bool QuicFramer::AppendTimestampToAckFrame(const QuicAckFrame& frame, } uint8_t num_received_packets = frame.received_packet_times.size(); - if (!writer->WriteBytes(&num_received_packets, 1)) { - return false; + if (quic_version_ <= QUIC_VERSION_39) { + if (!writer->WriteBytes(&num_received_packets, 1)) { + return false; + } + } else { + if (!writer->WriteUInt8AtOffset(num_received_packets, + num_timestamps_offset)) { + return false; + } } if (num_received_packets == 0) { return true; @@ -2081,8 +2321,10 @@ bool QuicFramer::AppendRstStreamFrame(const QuicRstStreamFrame& frame, return false; } - if (!writer->WriteUInt64(frame.byte_offset)) { - return false; + if (quic_version_ <= QUIC_VERSION_39) { + if (!writer->WriteUInt64(frame.byte_offset)) { + return false; + } } uint32_t error_code = static_cast<uint32_t>(frame.error_code); @@ -2090,6 +2332,12 @@ bool QuicFramer::AppendRstStreamFrame(const QuicRstStreamFrame& frame, return false; } + if (quic_version_ > QUIC_VERSION_39) { + if (!writer->WriteUInt64(frame.byte_offset)) { + return false; + } + } + return true; } @@ -2174,15 +2422,22 @@ Endianness QuicFramer::endianness() const { return quic_version_ > QUIC_VERSION_38 ? NETWORK_BYTE_ORDER : HOST_BYTE_ORDER; } -void QuicFramer::SaveStreamData(QuicStreamId id, - QuicIOVector iov, - size_t iov_offset, - QuicStreamOffset offset, - QuicByteCount data_length) { - DCHECK_NE(nullptr, data_producer_); - if (data_producer_ != nullptr) { - data_producer_->SaveStreamData(id, iov, iov_offset, offset, data_length); +bool QuicFramer::StartsWithChlo(QuicStreamId id, + QuicStreamOffset offset) const { + if (data_producer_ == nullptr) { + QUIC_BUG << "Does not have data producer."; + return false; + } + char buf[sizeof(kCHLO)]; + QuicDataWriter writer(sizeof(kCHLO), buf, perspective_, endianness()); + if (!data_producer_->WriteStreamData(id, offset, sizeof(kCHLO), &writer)) { + QUIC_BUG << "Failed to write data for stream " << id << " with offset " + << offset << " data_length = " << sizeof(kCHLO); + return false; } + + return strncmp(buf, reinterpret_cast<const char*>(&kCHLO), sizeof(kCHLO)) == + 0; } } // namespace net diff --git a/chromium/net/quic/core/quic_framer.h b/chromium/net/quic/core/quic_framer.h index 652f4128125..513505e1142 100644 --- a/chromium/net/quic/core/quic_framer.h +++ b/chromium/net/quic/core/quic_framer.h @@ -188,7 +188,8 @@ class QUIC_EXPORT_PRIVATE QuicFramer { bool ProcessPacket(const QuicEncryptedPacket& packet); // Largest size in bytes of all stream frame fields without the payload. - static size_t GetMinStreamFrameSize(QuicStreamId stream_id, + static size_t GetMinStreamFrameSize(QuicVersion version, + QuicStreamId stream_id, QuicStreamOffset offset, bool last_frame_in_packet); // Size in bytes of all ack frame fields without the missing packets or ack @@ -214,7 +215,8 @@ class QUIC_EXPORT_PRIVATE QuicFramer { // Size in bytes required to serialize the stream id. static size_t GetStreamIdSize(QuicStreamId stream_id); // Size in bytes required to serialize the stream offset. - static size_t GetStreamOffsetSize(QuicStreamOffset offset); + static size_t GetStreamOffsetSize(QuicVersion version, + QuicStreamOffset offset); // Size in bytes required for a serialized version negotiation packet static size_t GetVersionNegotiationPacketSize(size_t number_versions); @@ -324,6 +326,7 @@ class QUIC_EXPORT_PRIVATE QuicFramer { // The minimum packet number length required to represent |packet_number|. static QuicPacketNumberLength GetMinPacketNumberLength( + QuicVersion version, QuicPacketNumber packet_number); void SetSupportedVersions(const QuicVersionVector& versions) { @@ -334,6 +337,9 @@ class QUIC_EXPORT_PRIVATE QuicFramer { // Returns true if data_producer_ is not null. bool HasDataProducer() const { return data_producer_ != nullptr; } + // Returns true if data with |offset| of stream |id| starts with 'CHLO'. + bool StartsWithChlo(QuicStreamId id, QuicStreamOffset offset) const; + // Returns byte order to read/write integers and floating numbers. Endianness endianness() const; @@ -400,7 +406,9 @@ class QUIC_EXPORT_PRIVATE QuicFramer { bool ProcessAckFrame(QuicDataReader* reader, uint8_t frame_type, QuicAckFrame* frame); - bool ProcessTimestampsInAckFrame(QuicDataReader* reader, QuicAckFrame* frame); + bool ProcessTimestampsInAckFrame(uint8_t num_received_packets, + QuicDataReader* reader, + QuicAckFrame* ack_frame); bool ProcessStopWaitingFrame(QuicDataReader* reader, const QuicPacketHeader& public_header, QuicStopWaitingFrame* stop_waiting); @@ -477,8 +485,9 @@ class QUIC_EXPORT_PRIVATE QuicFramer { bool AppendAckFrameAndTypeByte(const QuicAckFrame& frame, QuicDataWriter* builder); - bool AppendTimestampToAckFrame(const QuicAckFrame& frame, - QuicDataWriter* builder); + bool AppendTimestampsToAckFrame(const QuicAckFrame& frame, + size_t num_timestamps_offset, + QuicDataWriter* writer); bool AppendStopWaitingFrame(const QuicPacketHeader& header, const QuicStopWaitingFrame& frame, QuicDataWriter* builder); @@ -546,8 +555,8 @@ class QUIC_EXPORT_PRIVATE QuicFramer { // The diversification nonce from the last received packet. DiversificationNonce last_nonce_; - // If not null, framer asks data_producer_ to save and write stream frame - // data. Not owned. + // If not null, framer asks data_producer_ to write stream frame data. Not + // owned. QuicStreamFrameDataProducer* data_producer_; DISALLOW_COPY_AND_ASSIGN(QuicFramer); diff --git a/chromium/net/quic/core/quic_framer_test.cc b/chromium/net/quic/core/quic_framer_test.cc index 34337faac80..1ba37a9fe7d 100644 --- a/chromium/net/quic/core/quic_framer_test.cc +++ b/chromium/net/quic/core/quic_framer_test.cc @@ -23,6 +23,7 @@ #include "net/quic/platform/api/quic_test.h" #include "net/quic/test_tools/quic_framer_peer.h" #include "net/quic/test_tools/quic_test_utils.h" +#include "net/quic/test_tools/simple_data_producer.h" using std::string; using testing::Return; @@ -397,13 +398,27 @@ class QuicFramerTest : public QuicTestWithParam<QuicVersion> { // Now test framing boundaries. for (size_t i = kQuicFrameTypeSize; i < GetMinStreamFrameSize(); ++i) { string expected_error; - if (i < kQuicFrameTypeSize + stream_id_size) { - expected_error = "Unable to read stream_id."; - } else if (i < kQuicFrameTypeSize + stream_id_size + - kQuicMaxStreamOffsetSize) { - expected_error = "Unable to read offset."; + if (framer_.version() <= QUIC_VERSION_39) { + if (i < kQuicFrameTypeSize + stream_id_size) { + expected_error = "Unable to read stream_id."; + } else if (i < kQuicFrameTypeSize + stream_id_size + + kQuicMaxStreamOffsetSize) { + expected_error = "Unable to read offset."; + } else { + expected_error = "Unable to read frame data."; + } } else { - expected_error = "Unable to read frame data."; + const size_t kQuicDataLenSize = sizeof(uint16_t); + if (i < kQuicFrameTypeSize + kQuicDataLenSize) { + expected_error = "Unable to read data length."; + } else if (i < kQuicFrameTypeSize + kQuicDataLenSize + stream_id_size) { + expected_error = "Unable to read stream_id."; + } else if (i < kQuicFrameTypeSize + kQuicDataLenSize + stream_id_size + + kQuicMaxStreamOffsetSize) { + expected_error = "Unable to read offset."; + } else { + expected_error = "Unable to read frame data."; + } } CheckProcessingFails( packet, i + GetPacketHeaderSize( @@ -1176,15 +1191,47 @@ TEST_P(QuicFramerTest, NewPaddingFrame) { // paddings 0x00, 0x00, }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, + + // paddings + 0x00, 0x00, + // frame type (stream frame with fin) + 0xFF, + // data length + 0x00, 0x0c, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0xBA, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + // paddings + 0x00, 0x00, + }; // clang-format on if (framer_.version() <= QUIC_VERSION_37) { return; } + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } - QuicEncryptedPacket encrypted( - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet), false); + QuicEncryptedPacket encrypted(AsChars(p), arraysize(packet), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); @@ -1251,11 +1298,39 @@ TEST_P(QuicFramerTest, StreamFrame) { 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, + + // frame type (stream frame with fin) + 0xFF, + // data length + 0x00, 0x0c, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0xBA, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; // clang-format on - QuicEncryptedPacket encrypted( - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet), false); + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + QuicEncryptedPacket encrypted(AsChars(p), arraysize(packet), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -1331,11 +1406,39 @@ TEST_P(QuicFramerTest, MissingDiversificationNonce) { 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, + + // frame type (stream frame with fin) + 0xFF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0xBA, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; // clang-format on - QuicEncryptedPacket encrypted( - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet), false); + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + QuicEncryptedPacket encrypted(AsChars(p), arraysize(packet), false); EXPECT_FALSE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_DECRYPTION_FAILURE, framer_.error()); } @@ -1389,11 +1492,40 @@ TEST_P(QuicFramerTest, StreamFrame3ByteStreamId) { 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', }; + + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, + + // frame type (stream frame with fin) + 0xF7, + // data length + 0x00, 0x0c, + // stream id + 0x02, 0x03, 0x04, + // offset + 0xBA, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; // clang-format on - QuicEncryptedPacket encrypted( - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet), false); + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + QuicEncryptedPacket encrypted(AsChars(p), arraysize(packet), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -1411,9 +1543,7 @@ TEST_P(QuicFramerTest, StreamFrame3ByteStreamId) { // Now test framing boundaries. const size_t stream_id_size = 3; - CheckStreamFrameBoundaries( - framer_.version() <= QUIC_VERSION_38 ? packet : packet39, stream_id_size, - !kIncludeVersion); + CheckStreamFrameBoundaries(p, stream_id_size, !kIncludeVersion); } TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) { @@ -1442,34 +1572,62 @@ TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) { 'r', 'l', 'd', '!', }; - unsigned char packet39[] = { - // public flags (8 byte connection_id) - 0x38, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - 0x9A, 0xBC, + unsigned char packet39[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, - // frame type (stream frame with fin) - 0xFD, - // stream id - 0x03, 0x04, - // offset - 0xBA, 0x98, 0xFE, 0xDC, - 0x32, 0x10, 0x76, 0x54, - // data length - 0x00, 0x0c, - // data - 'h', 'e', 'l', 'l', - 'o', ' ', 'w', 'o', - 'r', 'l', 'd', '!', - }; + // frame type (stream frame with fin) + 0xFD, + // stream id + 0x03, 0x04, + // offset + 0xBA, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, + + // frame type (stream frame with fin) + 0xEF, + // data length + 0x00, 0x0c, + // stream id + 0x03, 0x04, + // offset + 0xBA, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; // clang-format on - QuicEncryptedPacket encrypted( - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet), false); + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + QuicEncryptedPacket encrypted(AsChars(p), arraysize(packet), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -1487,9 +1645,7 @@ TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) { // Now test framing boundaries. const size_t stream_id_size = 2; - CheckStreamFrameBoundaries( - framer_.version() <= QUIC_VERSION_38 ? packet : packet39, stream_id_size, - !kIncludeVersion); + CheckStreamFrameBoundaries(p, stream_id_size, !kIncludeVersion); } TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) { @@ -1518,34 +1674,62 @@ TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) { 'r', 'l', 'd', '!', }; - unsigned char packet39[] = { - // public flags (8 byte connection_id) - 0x38, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // packet number - 0x12, 0x34, 0x56, 0x78, - 0x9A, 0xBC, + unsigned char packet39[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, - // frame type (stream frame with fin) - 0xFC, - // stream id - 0x04, - // offset - 0xBA, 0x98, 0xFE, 0xDC, - 0x32, 0x10, 0x76, 0x54, - // data length - 0x00, 0x0c, - // data - 'h', 'e', 'l', 'l', - 'o', ' ', 'w', 'o', - 'r', 'l', 'd', '!', - }; + // frame type (stream frame with fin) + 0xFC, + // stream id + 0x04, + // offset + 0xBA, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, + + // frame type (stream frame with fin) + 0xE7, + // data length + 0x00, 0x0c, + // stream id + 0x04, + // offset + 0xBA, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; // clang-format on - QuicEncryptedPacket encrypted( - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet), false); + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + QuicEncryptedPacket encrypted(AsChars(p), arraysize(packet), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -1563,9 +1747,7 @@ TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) { // Now test framing boundaries. const size_t stream_id_size = 1; - CheckStreamFrameBoundaries( - framer_.version() <= QUIC_VERSION_38 ? packet : packet39, stream_id_size, - !kIncludeVersion); + CheckStreamFrameBoundaries(p, stream_id_size, !kIncludeVersion); } TEST_P(QuicFramerTest, StreamFrameWithVersion) { @@ -1596,36 +1778,66 @@ TEST_P(QuicFramerTest, StreamFrameWithVersion) { 'r', 'l', 'd', '!', }; - unsigned char packet39[] = { - // public flags (version, 8 byte connection_id) - 0x39, - // connection_id - 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - // version tag - 'Q', '0', GetQuicVersionDigitTens(), GetQuicVersionDigitOnes(), - // packet number - 0x12, 0x34, 0x56, 0x78, - 0x9A, 0xBC, + unsigned char packet39[] = { + // public flags (version, 8 byte connection_id) + 0x39, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // version tag + 'Q', '0', GetQuicVersionDigitTens(), GetQuicVersionDigitOnes(), + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, - // frame type (stream frame with fin) - 0xFF, - // stream id - 0x01, 0x02, 0x03, 0x04, - // offset - 0xBA, 0x98, 0xFE, 0xDC, - 0x32, 0x10, 0x76, 0x54, - // data length - 0x00, 0x0c, - // data - 'h', 'e', 'l', 'l', - 'o', ' ', 'w', 'o', - 'r', 'l', 'd', '!', - }; + // frame type (stream frame with fin) + 0xFF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0xBA, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + + unsigned char packet40[] = { + // public flags (version, 8 byte connection_id) + 0x39, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // version tag + 'Q', '0', GetQuicVersionDigitTens(), GetQuicVersionDigitOnes(), + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, + + // frame type (stream frame with fin) + 0xFF, + // data length + 0x00, 0x0c, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0xBA, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; // clang-format on - QuicEncryptedPacket encrypted( - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet), false); + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + QuicEncryptedPacket encrypted(AsChars(p), arraysize(packet), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -1699,11 +1911,39 @@ TEST_P(QuicFramerTest, RejectPacket) { 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, + + // frame type (stream frame with fin) + 0xFF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0xBA, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; // clang-format on - QuicEncryptedPacket encrypted( - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet), false); + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + QuicEncryptedPacket encrypted(AsChars(p), arraysize(packet), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -1778,10 +2018,37 @@ TEST_P(QuicFramerTest, AckFrameOneAckBlock) { // num timestamps. 0x00, }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + + // frame type (ack frame) + // (one ack block, 2 byte largest observed, 2 byte block length) + 0xA5, + // num timestamps. + 0x00, + // largest acked + 0x12, 0x34, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x12, 0x34, + }; // clang-format on - QuicEncryptedPacket encrypted( - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet), false); + + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + + QuicEncryptedPacket encrypted(AsChars(p), arraysize(packet), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -1807,17 +2074,267 @@ TEST_P(QuicFramerTest, AckFrameOneAckBlock) { kFirstAckBlockLengthOffset + PACKET_2BYTE_PACKET_NUMBER; for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) { string expected_error; - if (i < kLargestAckedDeltaTimeOffset) { - expected_error = "Unable to read largest acked."; - } else if (i < kFirstAckBlockLengthOffset) { - expected_error = "Unable to read ack delay time."; - } else if (i < kNumTimestampsOffset) { - expected_error = "Unable to read first ack block length."; + if (framer_.version() > QUIC_VERSION_39) { + if (i < 2) { + expected_error = "Unable to read num received packets."; + } else if (i < 2 + PACKET_2BYTE_PACKET_NUMBER) { + expected_error = "Unable to read largest acked."; + } else if (i < 2 + PACKET_2BYTE_PACKET_NUMBER + + kQuicDeltaTimeLargestObservedSize) { + expected_error = "Unable to read ack delay time."; + } else { + expected_error = "Unable to read first ack block length."; + } + } else { + if (i < kLargestAckedDeltaTimeOffset) { + expected_error = "Unable to read largest acked."; + } else if (i < kFirstAckBlockLengthOffset) { + expected_error = "Unable to read ack delay time."; + } else if (i < kNumTimestampsOffset) { + expected_error = "Unable to read first ack block length."; + } else { + expected_error = "Unable to read num received packets."; + } + } + CheckProcessingFails( + p, + i + GetPacketHeaderSize(framer_.version(), PACKET_8BYTE_CONNECTION_ID, + !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_6BYTE_PACKET_NUMBER), + expected_error, QUIC_INVALID_ACK_DATA); + } +} +TEST_P(QuicFramerTest, FirstAckFrameUnderflow) { + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, + + // frame type (ack frame) + // (one ack block, 2 byte largest observed, 2 byte block length) + 0x45, + // largest acked + 0x34, 0x12, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x88, 0x88, + // num timestamps. + 0x00, + }; + + unsigned char packet39[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + + // frame type (ack frame) + // (one ack block, 2 byte largest observed, 2 byte block length) + 0x45, + // largest acked + 0x12, 0x34, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x88, 0x88, + // num timestamps. + 0x00, + }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + + // frame type (ack frame) + // (one ack block, 2 byte largest observed, 2 byte block length) + 0xA5, + // num timestamps. + 0x00, + // largest acked + 0x12, 0x34, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x88, 0x88, + }; + // clang-format on + + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + + QuicEncryptedPacket encrypted(AsChars(p), arraysize(packet), false); + framer_.ProcessPacket(encrypted); + + visitor_.header_.get(); + + const size_t kLargestAckedOffset = kQuicFrameTypeSize; + const size_t kLargestAckedDeltaTimeOffset = + kLargestAckedOffset + PACKET_2BYTE_PACKET_NUMBER; + const size_t kFirstAckBlockLengthOffset = + kLargestAckedDeltaTimeOffset + kQuicDeltaTimeLargestObservedSize; + // Now test framing boundaries. + const size_t ack_frame_size = + kFirstAckBlockLengthOffset + PACKET_2BYTE_PACKET_NUMBER; + string expected_error; + if (framer_.version() <= QUIC_VERSION_39) { + if (FLAGS_quic_reloadable_flag_sanitize_framer_addrange_input) { + expected_error = + "Underflow with first ack block length 34952 largest acked is " + "4661."; } else { expected_error = "Unable to read num received packets."; } CheckProcessingFails( - framer_.version() <= QUIC_VERSION_38 ? packet : packet39, + p, + ack_frame_size + + GetPacketHeaderSize(framer_.version(), PACKET_8BYTE_CONNECTION_ID, + !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_6BYTE_PACKET_NUMBER), + expected_error, QUIC_INVALID_ACK_DATA); + } +} + +TEST_P(QuicFramerTest, AckFrameOneAckBlockMaxLength) { + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, + + // frame type (ack frame) + // (one ack block, 6 byte largest observed, 2 byte block length) + 0x4D, + // largest acked + 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x34, 0x12, + // num timestamps. + 0x00, + }; + + unsigned char packet39[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + + // frame type (ack frame) + // (one ack block, 6 byte largest observed, 2 byte block length) + 0x4D, + // largest acked + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x12, 0x34, + // num timestamps. + 0x00, + }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + + // frame type (ack frame) + // (one ack block, 8 byte largest observed, 2 byte block length) + 0xAD, + // num timestamps. + 0x00, + // largest acked + 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x12, 0x34 + }; + // clang-format on + + unsigned char* p = packet; + size_t packet_size = arraysize(packet); + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + packet_size = arraysize(packet40); + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + packet_size = arraysize(packet39); + } + + QuicEncryptedPacket encrypted(AsChars(p), packet_size, false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion, + !kIncludeDiversificationNonce)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(kPacketNumber, frame.largest_observed); + ASSERT_EQ(4660u, frame.packets.NumPacketsSlow()); + + const size_t kLargestAckedOffset = kQuicFrameTypeSize; + const size_t kLargestAckedDeltaTimeOffset = + kLargestAckedOffset + PACKET_6BYTE_PACKET_NUMBER; + const size_t kFirstAckBlockLengthOffset = + kLargestAckedDeltaTimeOffset + kQuicDeltaTimeLargestObservedSize; + const size_t kNumTimestampsOffset = + kFirstAckBlockLengthOffset + PACKET_2BYTE_PACKET_NUMBER; + // Now test framing boundaries. + const size_t ack_frame_size = + kFirstAckBlockLengthOffset + PACKET_2BYTE_PACKET_NUMBER; + for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) { + string expected_error; + if (framer_.version() > QUIC_VERSION_39) { + if (i < 2) { + expected_error = "Unable to read num received packets."; + } else if (i < 2 + PACKET_8BYTE_PACKET_NUMBER) { + expected_error = "Unable to read largest acked."; + } else if (i < 2 + PACKET_8BYTE_PACKET_NUMBER + + kQuicDeltaTimeLargestObservedSize) { + expected_error = "Unable to read ack delay time."; + } else { + expected_error = "Unable to read first ack block length."; + } + } else { + if (i < kLargestAckedDeltaTimeOffset) { + expected_error = "Unable to read largest acked."; + } else if (i < kFirstAckBlockLengthOffset) { + expected_error = "Unable to read ack delay time."; + } else if (i < kNumTimestampsOffset) { + expected_error = "Unable to read first ack block length."; + } else { + expected_error = "Unable to read num received packets."; + } + } + CheckProcessingFails( + p, i + GetPacketHeaderSize(framer_.version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, !kIncludeDiversificationNonce, PACKET_6BYTE_PACKET_NUMBER), @@ -1920,10 +2437,61 @@ TEST_P(QuicFramerTest, AckFrameTwoTimeStampsMultipleAckBlocks) { // Delta time. 0x32, 0x10, }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + + // frame type (ack frame) + // (more than one ack block, 2 byte largest observed, 2 byte block length) + 0xB5, + // num ack blocks ranges. + 0x04, + // Number of timestamps. + 0x02, + // largest acked + 0x12, 0x34, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x00, 0x01, + // gap to next block. + 0x01, + // ack block length. + 0x0e, 0xaf, + // gap to next block. + 0xff, + // ack block length. + 0x00, 0x00, + // gap to next block. + 0x91, + // ack block length. + 0x01, 0xea, + // gap to next block. + 0x05, + // ack block length. + 0x00, 0x04, + // Delta from largest observed. + 0x01, + // Delta time. + 0x76, 0x54, 0x32, 0x10, + // Delta from largest observed. + 0x02, + // Delta time. + 0x32, 0x10, + }; // clang-format on - QuicEncryptedPacket encrypted( - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet), false); + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + QuicEncryptedPacket encrypted(AsChars(p), arraysize(packet), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -1936,6 +2504,7 @@ TEST_P(QuicFramerTest, AckFrameTwoTimeStampsMultipleAckBlocks) { const QuicAckFrame& frame = *visitor_.ack_frames_[0]; EXPECT_EQ(kSmallLargestObserved, frame.largest_observed); ASSERT_EQ(4254u, frame.packets.NumPacketsSlow()); + EXPECT_EQ(4u, frame.packets.NumIntervals()); const size_t kLargestAckedOffset = kQuicFrameTypeSize; const size_t kLargestAckedDeltaTimeOffset = @@ -1972,45 +2541,113 @@ TEST_P(QuicFramerTest, AckFrameTwoTimeStampsMultipleAckBlocks) { kAckBlockLengthOffset4 + PACKET_2BYTE_PACKET_NUMBER; for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) { string expected_error; - if (i < kLargestAckedDeltaTimeOffset) { - expected_error = "Unable to read largest acked."; - } else if (i < kNumberOfAckBlocksOffset) { - expected_error = "Unable to read ack delay time."; - } else if (i < kFirstAckBlockLengthOffset) { - expected_error = "Unable to read num of ack blocks."; - } else if (i < kGapToNextBlockOffset1) { - expected_error = "Unable to read first ack block length."; - } else if (i < kAckBlockLengthOffset1) { - expected_error = "Unable to read gap to next ack block."; - } else if (i < kGapToNextBlockOffset2) { - expected_error = "Unable to ack block length."; - } else if (i < kAckBlockLengthOffset2) { - expected_error = "Unable to read gap to next ack block."; - } else if (i < kGapToNextBlockOffset3) { - expected_error = "Unable to ack block length."; - } else if (i < kAckBlockLengthOffset3) { - expected_error = "Unable to read gap to next ack block."; - } else if (i < kGapToNextBlockOffset4) { - expected_error = "Unable to ack block length."; - } else if (i < kAckBlockLengthOffset4) { - expected_error = "Unable to read gap to next ack block."; - } else if (i < kNumTimestampsOffset) { - expected_error = "Unable to ack block length."; - } else if (i < kTimestampDeltaLargestObserved1) { - expected_error = "Unable to read num received packets."; - } else if (i < kTimestampTimeDeltaLargestObserved1) { - expected_error = "Unable to read sequence delta in received packets."; - } else if (i < kTimestampDeltaLargestObserved2) { - expected_error = "Unable to read time delta in received packets."; - } else if (i < kTimestampTimeDeltaLargestObserved2) { - expected_error = "Unable to read sequence delta in received packets."; + if (framer_.version() <= QUIC_VERSION_39) { + if (i < kLargestAckedDeltaTimeOffset) { + expected_error = "Unable to read largest acked."; + } else if (i < kNumberOfAckBlocksOffset) { + expected_error = "Unable to read ack delay time."; + } else if (i < kFirstAckBlockLengthOffset) { + expected_error = "Unable to read num of ack blocks."; + } else if (i < kGapToNextBlockOffset1) { + expected_error = "Unable to read first ack block length."; + } else if (i < kAckBlockLengthOffset1) { + expected_error = "Unable to read gap to next ack block."; + } else if (i < kGapToNextBlockOffset2) { + expected_error = "Unable to ack block length."; + } else if (i < kAckBlockLengthOffset2) { + expected_error = "Unable to read gap to next ack block."; + } else if (i < kGapToNextBlockOffset3) { + expected_error = "Unable to ack block length."; + } else if (i < kAckBlockLengthOffset3) { + expected_error = "Unable to read gap to next ack block."; + } else if (i < kGapToNextBlockOffset4) { + expected_error = "Unable to ack block length."; + } else if (i < kAckBlockLengthOffset4) { + expected_error = "Unable to read gap to next ack block."; + } else if (i < kNumTimestampsOffset) { + expected_error = "Unable to ack block length."; + } else if (i < kTimestampDeltaLargestObserved1) { + expected_error = "Unable to read num received packets."; + } else if (i < kTimestampTimeDeltaLargestObserved1) { + expected_error = "Unable to read sequence delta in received packets."; + } else if (i < kTimestampDeltaLargestObserved2) { + expected_error = "Unable to read time delta in received packets."; + } else if (i < kTimestampTimeDeltaLargestObserved2) { + expected_error = "Unable to read sequence delta in received packets."; + } else { + expected_error = + "Unable to read incremental time delta in received packets."; + } } else { - expected_error = - "Unable to read incremental time delta in received packets."; + const size_t kNumberOfAckBlocksOffset = kQuicFrameTypeSize; + const size_t kNumTimestampsOffset = kNumberOfAckBlocksOffset + 1; + const size_t kLargestAckedOffset = kNumTimestampsOffset + 1; + const size_t kLargestAckedDeltaTimeOffset = + kLargestAckedOffset + PACKET_2BYTE_PACKET_NUMBER; + const size_t kFirstAckBlockLengthOffset = + kLargestAckedDeltaTimeOffset + kQuicDeltaTimeLargestObservedSize; + const size_t kGapToNextBlockOffset1 = + kFirstAckBlockLengthOffset + PACKET_2BYTE_PACKET_NUMBER; + const size_t kAckBlockLengthOffset1 = kGapToNextBlockOffset1 + 1; + const size_t kGapToNextBlockOffset2 = + kAckBlockLengthOffset1 + PACKET_2BYTE_PACKET_NUMBER; + const size_t kAckBlockLengthOffset2 = kGapToNextBlockOffset2 + 1; + const size_t kGapToNextBlockOffset3 = + kAckBlockLengthOffset2 + PACKET_2BYTE_PACKET_NUMBER; + const size_t kAckBlockLengthOffset3 = kGapToNextBlockOffset3 + 1; + const size_t kGapToNextBlockOffset4 = + kAckBlockLengthOffset3 + PACKET_2BYTE_PACKET_NUMBER; + const size_t kAckBlockLengthOffset4 = kGapToNextBlockOffset3 + 1; + const size_t kTimestampDeltaLargestObserved1 = + kNumTimestampsOffset + kQuicNumTimestampsSize; + const size_t kTimestampTimeDeltaLargestObserved1 = + kTimestampDeltaLargestObserved1 + 1; + const size_t kTimestampDeltaLargestObserved2 = + kTimestampTimeDeltaLargestObserved1 + 4; + const size_t kTimestampTimeDeltaLargestObserved2 = + kTimestampDeltaLargestObserved2 + 1; + if (i < kNumTimestampsOffset) { + expected_error = "Unable to read num of ack blocks."; + } else if (i < kLargestAckedOffset) { + expected_error = "Unable to read num received packets."; + } else if (i < kLargestAckedDeltaTimeOffset) { + expected_error = "Unable to read largest acked."; + } else if (i < kFirstAckBlockLengthOffset) { + expected_error = "Unable to read ack delay time."; + } else if (i < kGapToNextBlockOffset1) { + expected_error = "Unable to read first ack block length."; + } else if (i < kAckBlockLengthOffset1) { + expected_error = "Unable to read gap to next ack block."; + } else if (i < kGapToNextBlockOffset2) { + expected_error = "Unable to ack block length."; + } else if (i < kAckBlockLengthOffset2) { + expected_error = "Unable to read gap to next ack block."; + } else if (i < kGapToNextBlockOffset3) { + expected_error = "Unable to ack block length."; + } else if (i < kAckBlockLengthOffset3) { + expected_error = "Unable to read gap to next ack block."; + } else if (i < kGapToNextBlockOffset4) { + expected_error = "Unable to ack block length."; + } else if (i < kAckBlockLengthOffset4) { + expected_error = "Unable to read gap to next ack block."; + } else if (i < kNumTimestampsOffset) { + expected_error = "Unable to ack block length."; + } else if (i < kTimestampDeltaLargestObserved1) { + expected_error = "Unable to read num received packets."; + } else if (i < kTimestampTimeDeltaLargestObserved1) { + expected_error = "Unable to read sequence delta in received packets."; + } else if (i < kTimestampDeltaLargestObserved2) { + expected_error = "Unable to read time delta in received packets."; + } else if (i < kTimestampTimeDeltaLargestObserved2) { + expected_error = "Unable to read sequence delta in received packets."; + } else { + expected_error = + "Unable to read incremental time delta in received packets."; + } } CheckProcessingFails( - framer_.version() <= QUIC_VERSION_38 ? packet : packet39, + p, i + GetPacketHeaderSize(framer_.version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, !kIncludeDiversificationNonce, PACKET_6BYTE_PACKET_NUMBER), @@ -2165,11 +2802,37 @@ TEST_P(QuicFramerTest, RstStreamFrameQuic) { // error code 0x00, 0x00, 0x00, 0x01, }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, + + // frame type (rst stream frame) + 0x01, + // stream id + 0x01, 0x02, 0x03, 0x04, + + // error code + 0x00, 0x00, 0x00, 0x01, + + // sent byte offset + 0xBA, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + }; // clang-format on - QuicEncryptedPacket encrypted( - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet), false); + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + QuicEncryptedPacket encrypted(AsChars(p), arraysize(packet), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); @@ -2185,17 +2848,29 @@ TEST_P(QuicFramerTest, RstStreamFrameQuic) { for (size_t i = kQuicFrameTypeSize; i < QuicFramer::GetRstStreamFrameSize(); ++i) { string expected_error; - if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) { - expected_error = "Unable to read stream_id."; - } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + - kQuicMaxStreamOffsetSize) { - expected_error = "Unable to read rst stream sent byte offset."; - } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + - kQuicMaxStreamOffsetSize + kQuicErrorCodeSize) { - expected_error = "Unable to read rst stream error code."; + if (framer_.version() <= QUIC_VERSION_39) { + if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) { + expected_error = "Unable to read stream_id."; + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + + kQuicMaxStreamOffsetSize) { + expected_error = "Unable to read rst stream sent byte offset."; + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + + kQuicMaxStreamOffsetSize + kQuicErrorCodeSize) { + expected_error = "Unable to read rst stream error code."; + } + } else { + if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) { + expected_error = "Unable to read stream_id."; + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + + kQuicErrorCodeSize) { + expected_error = "Unable to read rst stream error code."; + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + + kQuicMaxStreamOffsetSize + kQuicMaxStreamOffsetSize) { + expected_error = "Unable to read rst stream sent byte offset."; + } } CheckProcessingFails( - framer_.version() <= QUIC_VERSION_38 ? packet : packet39, + p, i + GetPacketHeaderSize(framer_.version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, !kIncludeDiversificationNonce, PACKET_6BYTE_PACKET_NUMBER), @@ -2975,15 +3650,50 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithNewPaddingFrame) { // paddings 0x00, 0x00, }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, + + // paddings + 0x00, 0x00, + // frame type (stream frame with fin) + 0xFF, + // data length + 0x00, 0x0c, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0xBA, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + // paddings + 0x00, 0x00, + }; // clang-format on std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != nullptr); - test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet)); + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + QuicEncryptedPacket encrypted(AsChars(p), arraysize(packet), false); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), + arraysize(packet)); } TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) { @@ -3188,15 +3898,42 @@ TEST_P(QuicFramerTest, BuildStreamFramePacket) { 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, + + // frame type (stream frame with fin and no length) + 0xFE, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0xBA, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; // clang-format on std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != nullptr); - test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet)); + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), + arraysize(packet)); } TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) { @@ -3252,16 +3989,42 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) { // data 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', }; + + unsigned char packet40[] = { + // public flags (version, 8 byte connection_id) + static_cast<unsigned char>( + FLAGS_quic_reloadable_flag_quic_remove_v33_hacks2 ? 0x39 : 0x3D), + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // version tag + 'Q', '0', GetQuicVersionDigitTens(), GetQuicVersionDigitOnes(), + // packet number + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + + // frame type (stream frame with fin and no length) + 0xFE, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0xBA, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', + }; // clang-format on QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != nullptr); - test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet)); + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), + arraysize(packet)); } TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) { @@ -3296,7 +4059,7 @@ TEST_P(QuicFramerTest, BuildAckFramePacketOneAckBlock) { QuicAckFrame ack_frame; ack_frame.largest_observed = kSmallLargestObserved; ack_frame.ack_delay_time = QuicTime::Delta::Zero(); - ack_frame.packets.Add(1, kSmallLargestObserved + 1); + ack_frame.packets.AddRange(1, kSmallLargestObserved + 1); QuicFrames frames = {QuicFrame(&ack_frame)}; @@ -3342,15 +4105,135 @@ TEST_P(QuicFramerTest, BuildAckFramePacketOneAckBlock) { // num timestamps. 0x00, }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + + // frame type (ack frame) + // (no ack blocks, 2 byte largest observed, 2 byte block length) + 0xA5, + // num timestamps. + 0x00, + // largest acked + 0x12, 0x34, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x12, 0x34, + }; // clang-format on + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != nullptr); + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), + arraysize(packet)); +} - test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet)); +TEST_P(QuicFramerTest, BuildAckFramePacketOneAckBlockMaxLength) { + QuicPacketHeader header; + header.public_header.connection_id = kConnectionId; + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicAckFrame ack_frame; + FLAGS_quic_reloadable_flag_quic_frames_deque2 = true; + ack_frame.largest_observed = kPacketNumber; + ack_frame.ack_delay_time = QuicTime::Delta::Zero(); + ack_frame.packets.AddRange(1, kPacketNumber + 1); + + QuicFrames frames = {QuicFrame(&ack_frame)}; + + // clang-format off + unsigned char packet[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, + + // frame type (ack frame) + // (no ack blocks, 6 byte largest observed, 6 byte block length) + 0x4F, + // largest acked + 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, + // num timestamps. + 0x00, + }; + + unsigned char packet39[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + + // frame type (ack frame) + // (no ack blocks, 6 byte largest observed, 6 byte block length) + 0x4F, + // largest acked + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + // num timestamps. + 0x00, + }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + + // frame type (ack frame) + // (no ack blocks, 8 byte largest observed, 8 byte block length) + 0xAF, + // num timestamps. + 0x00, + // largest acked + 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + }; + // clang-format on + unsigned char* p = packet; + size_t packet_size = arraysize(packet); + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + packet_size = arraysize(packet40); + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + packet_size = arraysize(packet39); + } + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), packet_size); } TEST_P(QuicFramerTest, BuildAckFramePacketMultipleAckBlocks) { @@ -3364,10 +4247,11 @@ TEST_P(QuicFramerTest, BuildAckFramePacketMultipleAckBlocks) { QuicAckFrame ack_frame; ack_frame.largest_observed = kSmallLargestObserved; ack_frame.ack_delay_time = QuicTime::Delta::Zero(); - ack_frame.packets.Add(1, 5); - ack_frame.packets.Add(10, 500); - ack_frame.packets.Add(900, kSmallMissingPacket); - ack_frame.packets.Add(kSmallMissingPacket + 1, kSmallLargestObserved + 1); + ack_frame.packets.AddRange(1, 5); + ack_frame.packets.AddRange(10, 500); + ack_frame.packets.AddRange(900, kSmallMissingPacket); + ack_frame.packets.AddRange(kSmallMissingPacket + 1, + kSmallLargestObserved + 1); QuicFrames frames = {QuicFrame(&ack_frame)}; @@ -3449,15 +4333,59 @@ TEST_P(QuicFramerTest, BuildAckFramePacketMultipleAckBlocks) { // num timestamps. 0x00, }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + + // frame type (ack frame) + // (has ack blocks, 2 byte largest observed, 2 byte block length) + 0xB5, + // num ack blocks ranges. + 0x04, + // num timestamps. + 0x00, + // largest acked + 0x12, 0x34, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x00, 0x01, + // gap to next block. + 0x01, + // ack block length. + 0x0e, 0xaf, + // gap to next block. + 0xff, + // ack block length. + 0x00, 0x00, + // gap to next block. + 0x91, + // ack block length. + 0x01, 0xea, + // gap to next block. + 0x05, + // ack block length. + 0x00, 0x04, + }; // clang-format on + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != nullptr); - test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet)); + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), + arraysize(packet)); } TEST_P(QuicFramerTest, BuildAckFramePacketMaxAckBlocks) { @@ -3475,7 +4403,7 @@ TEST_P(QuicFramerTest, BuildAckFramePacketMaxAckBlocks) { for (size_t i = 2; i < 2 * 300; i += 2) { ack_frame.packets.Add(i); } - ack_frame.packets.Add(600, kSmallLargestObserved + 1); + ack_frame.packets.AddRange(600, kSmallLargestObserved + 1); QuicFrames frames = {QuicFrame(&ack_frame)}; @@ -3665,15 +4593,113 @@ TEST_P(QuicFramerTest, BuildAckFramePacketMaxAckBlocks) { // num timestamps. 0x00, }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, + // frame type (ack frame) + // (has ack blocks, 2 byte largest observed, 2 byte block length) + 0xB5, + // num ack blocks ranges. + 0xff, + // num timestamps. + 0x00, + // largest acked + 0x12, 0x34, + // Zero delta time. + 0x00, 0x00, + // first ack block length. + 0x0f, 0xdd, + // 255 = 4 * 63 + 3 + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, + }; // clang-format on + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != nullptr); - test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet)); + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), + arraysize(packet)); } TEST_P(QuicFramerTest, BuildNewStopWaitingPacket) { @@ -3781,6 +4807,26 @@ TEST_P(QuicFramerTest, BuildRstFramePacketQuic) { // error code 0x05, 0x06, 0x07, 0x08, }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, + + // frame type (rst stream frame) + 0x01, + // stream id + 0x01, 0x02, 0x03, 0x04, + // error code + 0x05, 0x06, 0x07, 0x08, + // sent byte offset + 0x08, 0x07, 0x06, 0x05, + 0x04, 0x03, 0x02, 0x01, + }; // clang-format on QuicFrames frames = {QuicFrame(&rst_frame)}; @@ -3788,10 +4834,17 @@ TEST_P(QuicFramerTest, BuildRstFramePacketQuic) { std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); ASSERT_TRUE(data != nullptr); - test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet)); + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + QuicEncryptedPacket encrypted(AsChars(p), arraysize(packet), false); + + test::CompareCharArraysWithHexError("constructed packet", data->data(), + data->length(), AsChars(p), + arraysize(packet)); } TEST_P(QuicFramerTest, BuildCloseFramePacket) { @@ -4435,7 +5488,7 @@ TEST_P(QuicFramerTest, CleanTruncation) { QuicAckFrame ack_frame; ack_frame.largest_observed = 201; - ack_frame.packets.Add(1, ack_frame.largest_observed); + ack_frame.packets.AddRange(1, ack_frame.largest_observed); // Create a packet with just the ack. QuicFrames frames = {QuicFrame(&ack_frame)}; @@ -4542,6 +5595,45 @@ TEST_P(QuicFramerTest, StopPacketProcessing) { 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBE, }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x38, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, + + // frame type (stream frame with fin) + 0xFF, + // data length + 0x00, 0x0c, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0xBA, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + + // frame type (ack frame) + 0xA0, + // least packet number awaiting an ack + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xA0, + // largest observed packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBF, + // num missing packets + 0x01, + // missing packet + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBE, + }; + // clang-format on MockFramerVisitor visitor; @@ -4555,9 +5647,13 @@ TEST_P(QuicFramerTest, StopPacketProcessing) { EXPECT_CALL(visitor, OnUnauthenticatedHeader(_)).WillOnce(Return(true)); EXPECT_CALL(visitor, OnDecryptedPacket(_)); - QuicEncryptedPacket encrypted( - AsChars(framer_.version() <= QUIC_VERSION_38 ? packet : packet39), - arraysize(packet), false); + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + QuicEncryptedPacket encrypted(AsChars(p), arraysize(packet), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); } @@ -4725,10 +5821,61 @@ TEST_P(QuicFramerTest, FramerFuzzTest) { 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', }; + + unsigned char packet40[] = { + // public flags (8 byte connection_id) + 0x3C, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + 0x9A, 0xBC, + // private flags + 0x00, + + // frame type (stream frame with fin) + 0xFF, + // stream id + 0x01, 0x02, 0x03, 0x04, + // offset + 0xBA, 0x98, 0xFE, 0xDC, + 0x32, 0x10, 0x76, 0x54, + // data length + 0x00, 0x0c, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; // clang-format on - QuicFramerFuzzFunc(framer_.version() <= QUIC_VERSION_38 ? packet : packet39, - arraysize(packet)); + unsigned char* p = packet; + if (framer_.version() > QUIC_VERSION_39) { + p = packet40; + } else if (framer_.version() > QUIC_VERSION_38) { + p = packet39; + } + QuicFramerFuzzFunc(p, arraysize(packet)); +} + +TEST_P(QuicFramerTest, StartsWithChlo) { + EXPECT_FALSE(framer_.HasDataProducer()); + SimpleDataProducer producer; + framer_.set_data_producer(&producer); + EXPECT_TRUE(framer_.HasDataProducer()); + QuicStringPiece data("CHLOCHLO"); + struct iovec iovec; + iovec.iov_base = const_cast<char*>(data.data()); + iovec.iov_len = data.length(); + QuicIOVector iov(&iovec, 1, iovec.iov_len); + producer.SaveStreamData(kCryptoStreamId, iov, 0, 0, data.length()); + for (size_t offset = 0; offset < 5; ++offset) { + if (offset == 0 || offset == 4) { + EXPECT_TRUE(framer_.StartsWithChlo(kCryptoStreamId, offset)); + } else { + EXPECT_FALSE(framer_.StartsWithChlo(kCryptoStreamId, offset)); + } + } } } // namespace diff --git a/chromium/net/quic/core/quic_header_list.cc b/chromium/net/quic/core/quic_header_list.cc index 69f783adee6..3a7fe39ccbc 100644 --- a/chromium/net/quic/core/quic_header_list.cc +++ b/chromium/net/quic/core/quic_header_list.cc @@ -6,14 +6,17 @@ #include "net/quic/core/quic_packets.h" #include "net/quic/platform/api/quic_flags.h" +#include "net/spdy/core/spdy_protocol.h" using std::string; namespace net { QuicHeaderList::QuicHeaderList() - : max_uncompressed_header_bytes_(kDefaultMaxUncompressedHeaderSize), - uncompressed_header_bytes_(0) {} + : max_header_list_size_(kDefaultMaxUncompressedHeaderSize), + current_header_list_size_(0), + uncompressed_header_bytes_(0), + compressed_header_bytes_(0) {} QuicHeaderList::QuicHeaderList(QuicHeaderList&& other) = default; @@ -27,15 +30,29 @@ QuicHeaderList& QuicHeaderList::operator=(QuicHeaderList&& other) = default; QuicHeaderList::~QuicHeaderList() {} void QuicHeaderList::OnHeaderBlockStart() { - QUIC_BUG_IF(uncompressed_header_bytes_ != 0) - << "OnHeaderBlockStart called more than once!"; + if (FLAGS_quic_restart_flag_quic_header_list_size) { + QUIC_BUG_IF(current_header_list_size_ != 0) + << "OnHeaderBlockStart called more than once!"; + } else { + QUIC_BUG_IF(uncompressed_header_bytes_ != 0) + << "OnHeaderBlockStart called more than once!"; + } } void QuicHeaderList::OnHeader(QuicStringPiece name, QuicStringPiece value) { - // Avoid infinte buffering of headers. No longer store headers + // Avoid infinite buffering of headers. No longer store headers // once the current headers are over the limit. - if (uncompressed_header_bytes_ == 0 || !header_list_.empty()) { - header_list_.emplace_back(name.as_string(), value.as_string()); + if (FLAGS_quic_restart_flag_quic_header_list_size) { + if (current_header_list_size_ < max_header_list_size_) { + current_header_list_size_ += name.size(); + current_header_list_size_ += value.size(); + current_header_list_size_ += kPerHeaderOverhead; + header_list_.emplace_back(string(name), string(value)); + } + } else { + if (uncompressed_header_bytes_ == 0 || !header_list_.empty()) { + header_list_.emplace_back(string(name), string(value)); + } } } @@ -43,14 +60,22 @@ void QuicHeaderList::OnHeaderBlockEnd(size_t uncompressed_header_bytes, size_t compressed_header_bytes) { uncompressed_header_bytes_ = uncompressed_header_bytes; compressed_header_bytes_ = compressed_header_bytes; - if (uncompressed_header_bytes_ > max_uncompressed_header_bytes_) { - Clear(); + if (FLAGS_quic_restart_flag_quic_header_list_size) { + if (current_header_list_size_ > max_header_list_size_) { + Clear(); + } + } else { + if (uncompressed_header_bytes_ > max_header_list_size_) { + Clear(); + } } } void QuicHeaderList::Clear() { header_list_.clear(); + current_header_list_size_ = 0; uncompressed_header_bytes_ = 0; + compressed_header_bytes_ = 0; } string QuicHeaderList::DebugString() const { diff --git a/chromium/net/quic/core/quic_header_list.h b/chromium/net/quic/core/quic_header_list.h index 1de6ad2bf3e..983cd8ec1bb 100644 --- a/chromium/net/quic/core/quic_header_list.h +++ b/chromium/net/quic/core/quic_header_list.h @@ -49,15 +49,27 @@ class QUIC_EXPORT_PRIVATE QuicHeaderList : public SpdyHeadersHandlerInterface { } size_t compressed_header_bytes() const { return compressed_header_bytes_; } - void set_max_uncompressed_header_bytes(size_t max_uncompressed_header_bytes) { - max_uncompressed_header_bytes_ = max_uncompressed_header_bytes; + void set_max_header_list_size(size_t max_header_list_size) { + max_header_list_size_ = max_header_list_size; } + size_t max_header_list_size() const { return max_header_list_size_; } + std::string DebugString() const; private: std::deque<std::pair<std::string, std::string>> header_list_; - size_t max_uncompressed_header_bytes_; + + // The limit on the size of the header list (defined by spec as name + value + + // overhead for each header field). Headers over this limit will not be + // buffered, and the list will be cleared upon OnHeaderBlockEnd. + size_t max_header_list_size_; + + // Defined per the spec as the size of all header fields with an additional + // overhead for each field. + size_t current_header_list_size_; + + // TODO(dahollings) Are these fields necessary? size_t uncompressed_header_bytes_; size_t compressed_header_bytes_; }; diff --git a/chromium/net/quic/core/quic_header_list_test.cc b/chromium/net/quic/core/quic_header_list_test.cc index 39666a2cd41..8e45463078e 100644 --- a/chromium/net/quic/core/quic_header_list_test.cc +++ b/chromium/net/quic/core/quic_header_list_test.cc @@ -28,8 +28,18 @@ TEST_F(QuicHeaderListTest, TooLarge) { QuicHeaderList headers; string key = "key"; string value(1 << 18, '1'); + // Send a header that exceeds max_header_list_size. headers.OnHeader(key, value); - size_t total_bytes = key.size() + value.size(); + // Send a second header exceeding max_header_list_size. + headers.OnHeader(key + "2", value); + if (FLAGS_quic_restart_flag_quic_header_list_size) { + // We should not allocate more memory after exceeding max_header_list_size. + EXPECT_LT(headers.DebugString().size(), 2 * value.size()); + } else { + // Demonstrates previous behavior. + EXPECT_GE(headers.DebugString().size(), 2 * value.size()); + } + size_t total_bytes = 2 * (key.size() + value.size()) + 1; headers.OnHeaderBlockEnd(total_bytes, total_bytes); EXPECT_TRUE(headers.empty()); @@ -38,7 +48,7 @@ TEST_F(QuicHeaderListTest, TooLarge) { TEST_F(QuicHeaderListTest, NotTooLarge) { QuicHeaderList headers; - headers.set_max_uncompressed_header_bytes(1 << 20); + headers.set_max_header_list_size(1 << 20); string key = "key"; string value(1 << 18, '1'); headers.OnHeader(key, value); diff --git a/chromium/net/quic/core/quic_headers_stream.cc b/chromium/net/quic/core/quic_headers_stream.cc index b6dfec1c246..97d72c90ca2 100644 --- a/chromium/net/quic/core/quic_headers_stream.cc +++ b/chromium/net/quic/core/quic_headers_stream.cc @@ -62,7 +62,9 @@ QuicConsumedData QuicHeadersStream::WritevDataInner( QuicStreamOffset offset, bool fin, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { - if (!session()->use_stream_notifier()) { + if (!session()->use_stream_notifier() || + session()->save_data_before_consumption()) { + // If data is saved before consumption, unacked_headers has been populated. return QuicStream::WritevDataInner(iov, offset, fin, std::move(ack_listener)); } @@ -91,31 +93,37 @@ QuicConsumedData QuicHeadersStream::WritevDataInner( void QuicHeadersStream::OnStreamFrameAcked(const QuicStreamFrame& frame, QuicTime::Delta ack_delay_time) { + QuicStreamOffset offset = frame.offset; + QuicByteCount length = frame.data_length; for (CompressedHeaderInfo& header : unacked_headers_) { - if (frame.offset < header.headers_stream_offset) { + if (offset < header.headers_stream_offset) { // This header frame offset belongs to headers with smaller offset, stop // processing. break; } - if (frame.offset >= header.headers_stream_offset + header.full_length) { + if (offset >= header.headers_stream_offset + header.full_length) { // This header frame belongs to headers with larger offset. continue; } - if (header.unacked_length < frame.data_length) { - // This header frame is out of range. + QuicByteCount header_offset = offset - header.headers_stream_offset; + QuicByteCount acked_length = + std::min(length, header.full_length - header_offset); + + if (header.unacked_length < acked_length) { + QUIC_BUG << "Unsent stream data is acked. unacked_length: " + << header.unacked_length << " acked_length: " << acked_length; CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, "Unsent stream data is acked"); return; } - - header.unacked_length -= frame.data_length; - - if (header.ack_listener != nullptr) { - header.ack_listener->OnPacketAcked(frame.data_length, ack_delay_time); + if (header.ack_listener != nullptr && acked_length > 0) { + header.ack_listener->OnPacketAcked(acked_length, ack_delay_time); } - break; + header.unacked_length -= acked_length; + offset += acked_length; + length -= acked_length; } // Remove headers which are fully acked. Please note, header frames can be @@ -129,22 +137,47 @@ void QuicHeadersStream::OnStreamFrameAcked(const QuicStreamFrame& frame, void QuicHeadersStream::OnStreamFrameRetransmitted( const QuicStreamFrame& frame) { + QuicStreamOffset offset = frame.offset; + QuicByteCount length = frame.data_length; for (CompressedHeaderInfo& header : unacked_headers_) { - if (frame.offset < header.headers_stream_offset) { + if (offset < header.headers_stream_offset) { // This header frame offset belongs to headers with smaller offset, stop // processing. break; } - if (frame.offset >= header.headers_stream_offset + header.full_length) { + if (offset >= header.headers_stream_offset + header.full_length) { // This header frame belongs to headers with larger offset. continue; } - if (header.ack_listener != nullptr) { - header.ack_listener->OnPacketRetransmitted(frame.data_length); + QuicByteCount header_offset = offset - header.headers_stream_offset; + QuicByteCount retransmitted_length = + std::min(length, header.full_length - header_offset); + if (header.ack_listener != nullptr && retransmitted_length > 0) { + header.ack_listener->OnPacketRetransmitted(retransmitted_length); } - break; + offset += retransmitted_length; + length -= retransmitted_length; + } +} + +void QuicHeadersStream::OnDataBuffered( + QuicStreamOffset offset, + QuicByteCount data_length, + const QuicReferenceCountedPointer<QuicAckListenerInterface>& ack_listener) { + // Populate unacked_headers_. + if (!unacked_headers_.empty() && + (offset == unacked_headers_.back().headers_stream_offset + + unacked_headers_.back().full_length) && + ack_listener == unacked_headers_.back().ack_listener) { + // Try to combine with latest inserted entry if they belong to the same + // header (i.e., having contiguous offset and the same ack listener). + unacked_headers_.back().full_length += data_length; + unacked_headers_.back().unacked_length += data_length; + } else { + unacked_headers_.push_back( + CompressedHeaderInfo(offset, data_length, ack_listener)); } } diff --git a/chromium/net/quic/core/quic_headers_stream.h b/chromium/net/quic/core/quic_headers_stream.h index f74b869693c..1e17b1875e2 100644 --- a/chromium/net/quic/core/quic_headers_stream.h +++ b/chromium/net/quic/core/quic_headers_stream.h @@ -80,6 +80,15 @@ class QUIC_EXPORT_PRIVATE QuicHeadersStream : public QuicStream { // Returns true if the session is still connected. bool IsConnected(); + // Override to store mapping from offset, length to ack_listener. This + // ack_listener is notified once data within [offset, offset + length] is + // acked or retransmitted. + void OnDataBuffered( + QuicStreamOffset offset, + QuicByteCount data_length, + const QuicReferenceCountedPointer<QuicAckListenerInterface>& ack_listener) + override; + QuicSpdySession* spdy_session_; // Headers that have not been fully acked. diff --git a/chromium/net/quic/core/quic_headers_stream_test.cc b/chromium/net/quic/core/quic_headers_stream_test.cc index 7aec2d8eafe..2f47bcc32da 100644 --- a/chromium/net/quic/core/quic_headers_stream_test.cc +++ b/chromium/net/quic/core/quic_headers_stream_test.cc @@ -10,6 +10,7 @@ #include <tuple> #include <utility> +#include "net/quic/core/quic_data_writer.h" #include "net/quic/core/quic_utils.h" #include "net/quic/core/spdy_utils.h" #include "net/quic/platform/api/quic_bug_tracker.h" @@ -23,7 +24,7 @@ #include "net/quic/test_tools/quic_spdy_session_peer.h" #include "net/quic/test_tools/quic_stream_peer.h" #include "net/quic/test_tools/quic_test_utils.h" -#include "net/spdy/chromium/spdy_flags.h" +#include "net/spdy/core/http2_frame_decoder_adapter.h" #include "net/spdy/core/spdy_alt_svc_wire_format.h" #include "net/spdy/core/spdy_protocol.h" #include "net/spdy/core/spdy_test_utils.h" @@ -59,7 +60,7 @@ const bool kFins[] = {false, true}; class MockVisitor : public SpdyFramerVisitorInterface { public: - MOCK_METHOD1(OnError, void(SpdyFramer* framer)); + MOCK_METHOD1(OnError, void(Http2DecoderAdapter::SpdyFramerError error)); MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId stream_id, size_t length, bool fin)); MOCK_METHOD3(OnStreamFrameData, @@ -168,7 +169,8 @@ class QuicHeadersStreamTest : public QuicTestWithParam<TestParamsTuple> { headers_["content-length"] = "11"; framer_ = std::unique_ptr<SpdyFramer>( new SpdyFramer(SpdyFramer::ENABLE_COMPRESSION)); - framer_->set_visitor(&visitor_); + deframer_ = std::unique_ptr<Http2DecoderAdapter>(new Http2DecoderAdapter()); + deframer_->set_visitor(&visitor_); EXPECT_EQ(version(), session_.connection()->version()); EXPECT_TRUE(headers_stream_ != nullptr); connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); @@ -189,9 +191,20 @@ class QuicHeadersStreamTest : public QuicTestWithParam<TestParamsTuple> { const iovec* iov = data.iov; int count = data.iov_count; int consumed = 0; - for (int i = 0; i < count; ++i) { - saved_data_.append(static_cast<char*>(iov[i].iov_base), iov[i].iov_len); - consumed += iov[i].iov_len; + if (iov != nullptr) { + for (int i = 0; i < count; ++i) { + saved_data_.append(static_cast<char*>(iov[i].iov_base), iov[i].iov_len); + consumed += iov[i].iov_len; + } + } else { + consumed = data.total_length; + char* buf = new char[consumed]; + QuicDataWriter writer(consumed, buf, Perspective::IS_CLIENT, + NETWORK_BYTE_ORDER); + headers_stream_->WriteStreamData(headers_stream_->stream_bytes_written(), + consumed, &writer); + saved_data_.append(buf, consumed); + delete[] buf; } return QuicConsumedData(consumed, false); } @@ -199,7 +212,17 @@ class QuicHeadersStreamTest : public QuicTestWithParam<TestParamsTuple> { QuicConsumedData SaveIovShort(const QuicIOVector& data) { const iovec* iov = data.iov; int consumed = 1; - saved_data_.append(static_cast<char*>(iov[0].iov_base), consumed); + if (iov != nullptr) { + saved_data_.append(static_cast<char*>(iov[0].iov_base), consumed); + } else { + char* buf = new char[consumed]; + QuicDataWriter writer(consumed, buf, Perspective::IS_CLIENT, + NETWORK_BYTE_ORDER); + headers_stream_->WriteStreamData(headers_stream_->stream_bytes_written(), + consumed, &writer); + saved_data_.append(buf, consumed); + delete[] buf; + } return QuicConsumedData(consumed, false); } @@ -293,9 +316,10 @@ class QuicHeadersStreamTest : public QuicTestWithParam<TestParamsTuple> { if (fin) { EXPECT_CALL(visitor_, OnStreamEnd(stream_id)); } - framer_->ProcessInput(saved_data_.data(), saved_data_.length()); - EXPECT_FALSE(framer_->HasError()) - << SpdyFramer::SpdyFramerErrorToString(framer_->spdy_framer_error()); + deframer_->ProcessInput(saved_data_.data(), saved_data_.length()); + EXPECT_FALSE(deframer_->HasError()) + << Http2DecoderAdapter::SpdyFramerErrorToString( + deframer_->spdy_framer_error()); CheckHeaders(); saved_data_.clear(); @@ -340,6 +364,7 @@ class QuicHeadersStreamTest : public QuicTestWithParam<TestParamsTuple> { string saved_header_data_; string saved_payloads_; std::unique_ptr<SpdyFramer> framer_; + std::unique_ptr<Http2DecoderAdapter> deframer_; StrictMock<MockVisitor> visitor_; QuicStreamFrame stream_frame_; QuicStreamId next_promised_stream_id_; @@ -396,9 +421,10 @@ TEST_P(QuicHeadersStreamTest, WritePushPromises) { EXPECT_CALL(visitor_, OnHeaderFrameStart(stream_id)) .WillOnce(Return(headers_handler_.get())); EXPECT_CALL(visitor_, OnHeaderFrameEnd(stream_id)).Times(1); - framer_->ProcessInput(saved_data_.data(), saved_data_.length()); - EXPECT_FALSE(framer_->HasError()) - << SpdyFramer::SpdyFramerErrorToString(framer_->spdy_framer_error()); + deframer_->ProcessInput(saved_data_.data(), saved_data_.length()); + EXPECT_FALSE(deframer_->HasError()) + << Http2DecoderAdapter::SpdyFramerErrorToString( + deframer_->spdy_framer_error()); CheckHeaders(); saved_data_.clear(); } else { @@ -474,7 +500,6 @@ TEST_P(QuicHeadersStreamTest, ProcessPushPromise) { TEST_P(QuicHeadersStreamTest, ProcessPushPromiseDisabledSetting) { FLAGS_quic_reloadable_flag_quic_respect_http2_settings_frame = true; - FLAGS_quic_reloadable_flag_quic_enable_server_push_by_default = true; session_.OnConfigNegotiated(); SpdySettingsIR data; // Respect supported settings frames SETTINGS_ENABLE_PUSH. @@ -724,8 +749,7 @@ TEST_P(QuicHeadersStreamTest, RespectHttp2SettingsFrameUnsupportedFields) { QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", SETTINGS_INITIAL_WINDOW_SIZE), _)); - if (!FLAGS_quic_reloadable_flag_quic_enable_server_push_by_default || - session_.perspective() == Perspective::IS_CLIENT) { + if (session_.perspective() == Perspective::IS_CLIENT) { EXPECT_CALL(*connection_, CloseConnection( QUIC_INVALID_HEADERS_STREAM_DATA, @@ -913,7 +937,7 @@ TEST_P(QuicHeadersStreamTest, WritevStreamData) { if (fin) { EXPECT_CALL(visitor_, OnStreamEnd(id)); } - framer_->ProcessInput(saved_data_.data(), saved_data_.length()); + deframer_->ProcessInput(saved_data_.data(), saved_data_.length()); EXPECT_EQ(saved_payloads_, data); if (use_ack_listener && !session_.use_stream_notifier()) { @@ -991,8 +1015,7 @@ TEST_P(QuicHeadersStreamTest, AckSentData) { } EXPECT_CALL(session_, WritevData(headers_stream_, kHeadersStreamId, _, _, NO_FIN, _)) - .WillRepeatedly( - Invoke(&session_, &MockQuicSpdySession::ConsumeAndSaveAllData)); + .WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData)); InSequence s; QuicReferenceCountedPointer<MockAckListener> ack_listener1( new MockAckListener()); @@ -1040,8 +1063,59 @@ TEST_P(QuicHeadersStreamTest, AckSentData) { headers_stream_->OnStreamFrameAcked(frame1, QuicTime::Delta::Zero()); headers_stream_->OnStreamFrameAcked(frame2, QuicTime::Delta::Zero()); // Unsent data is acked. - EXPECT_CALL(*connection_, CloseConnection(QUIC_INTERNAL_ERROR, _, _)); + if (!session_.save_data_before_consumption()) { + EXPECT_CALL(*connection_, CloseConnection(QUIC_INTERNAL_ERROR, _, _)); + EXPECT_QUIC_BUG( + headers_stream_->OnStreamFrameAcked(frame3, QuicTime::Delta::Zero()), + "Unsent stream data is acked."); + } +} + +TEST_P(QuicHeadersStreamTest, FrameContainsMultipleHeaders) { + // In this test, a stream frame can contain multiple headers. + if (!session_.save_data_before_consumption()) { + return; + } + EXPECT_CALL(session_, + WritevData(headers_stream_, kHeadersStreamId, _, _, NO_FIN, _)) + .WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData)); + InSequence s; + QuicReferenceCountedPointer<MockAckListener> ack_listener1( + new MockAckListener()); + QuicReferenceCountedPointer<MockAckListener> ack_listener2( + new MockAckListener()); + QuicReferenceCountedPointer<MockAckListener> ack_listener3( + new MockAckListener()); + + headers_stream_->WriteOrBufferData("Header5", false, ack_listener1); + headers_stream_->WriteOrBufferData("Header5", false, ack_listener1); + headers_stream_->WriteOrBufferData("Header7", false, ack_listener2); + headers_stream_->WriteOrBufferData("Header9", false, ack_listener3); + headers_stream_->WriteOrBufferData("Header7", false, ack_listener2); + headers_stream_->WriteOrBufferData("Header9", false, ack_listener3); + + QuicStreamFrame frame1(kHeadersStreamId, false, 0, "Header5Header5Hea"); + QuicStreamFrame frame2(kHeadersStreamId, false, 17, "der7Header9He"); + QuicStreamFrame frame3(kHeadersStreamId, false, 30, "ader7Header9"); + + // Frame 1 is retransmitted. + EXPECT_CALL(*ack_listener1, OnPacketRetransmitted(14)); + EXPECT_CALL(*ack_listener2, OnPacketRetransmitted(3)); + headers_stream_->OnStreamFrameRetransmitted(frame1); + + // Frames are acked in order: 2, 3, 1. + EXPECT_CALL(*ack_listener2, OnPacketAcked(4, _)); + EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _)); + EXPECT_CALL(*ack_listener2, OnPacketAcked(2, _)); + headers_stream_->OnStreamFrameAcked(frame2, QuicTime::Delta::Zero()); + + EXPECT_CALL(*ack_listener2, OnPacketAcked(5, _)); + EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _)); headers_stream_->OnStreamFrameAcked(frame3, QuicTime::Delta::Zero()); + + EXPECT_CALL(*ack_listener1, OnPacketAcked(14, _)); + EXPECT_CALL(*ack_listener2, OnPacketAcked(3, _)); + headers_stream_->OnStreamFrameAcked(frame1, QuicTime::Delta::Zero()); } } // namespace diff --git a/chromium/net/quic/core/quic_packet_creator.cc b/chromium/net/quic/core/quic_packet_creator.cc index b21e85eae60..a3ed7ee198d 100644 --- a/chromium/net/quic/core/quic_packet_creator.cc +++ b/chromium/net/quic/core/quic_packet_creator.cc @@ -116,7 +116,7 @@ void QuicPacketCreator::UpdatePacketNumberLength( packet_.packet_number + 1 - least_packet_awaited_by_peer; const uint64_t delta = std::max(current_delta, max_packets_in_flight); packet_.packet_number_length = - QuicFramer::GetMinPacketNumberLength(delta * 4); + QuicFramer::GetMinPacketNumberLength(framer_->version(), delta * 4); } bool QuicPacketCreator::ConsumeData(QuicStreamId id, @@ -131,13 +131,13 @@ bool QuicPacketCreator::ConsumeData(QuicStreamId id, } CreateStreamFrame(id, iov, iov_offset, offset, fin, frame); // Explicitly disallow multi-packet CHLOs. - if (StreamFrameStartsWithChlo(iov, iov_offset, *frame->stream_frame) && - FLAGS_quic_enforce_single_packet_chlo && - frame->stream_frame->data_length < iov.iov->iov_len) { + if (FLAGS_quic_enforce_single_packet_chlo && + StreamFrameStartsWithChlo(iov, iov_offset, *frame->stream_frame) && + frame->stream_frame->data_length < iov.total_length) { const string error_details = "Client hello won't fit in a single packet."; QUIC_BUG << error_details << " Constructed stream frame length: " << frame->stream_frame->data_length - << " CHLO length: " << iov.iov->iov_len; + << " CHLO length: " << iov.total_length; delegate_->OnUnrecoverableError(QUIC_CRYPTO_CHLO_TOO_LARGE, error_details, ConnectionCloseSource::FROM_SELF); delete frame->stream_frame; @@ -157,7 +157,8 @@ bool QuicPacketCreator::ConsumeData(QuicStreamId id, bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id, QuicStreamOffset offset) { - return BytesFree() > QuicFramer::GetMinStreamFrameSize(id, offset, true); + return BytesFree() > QuicFramer::GetMinStreamFrameSize(framer_->version(), id, + offset, true); } // static @@ -172,7 +173,7 @@ size_t QuicPacketCreator::StreamFramePacketOverhead( include_diversification_nonce, packet_number_length) + // Assumes this is a stream with a single lone packet. - QuicFramer::GetMinStreamFrameSize(1u, offset, true); + QuicFramer::GetMinStreamFrameSize(version, 1u, offset, true); } void QuicPacketCreator::CreateStreamFrame(QuicStreamId id, @@ -190,7 +191,8 @@ void QuicPacketCreator::CreateStreamFrame(QuicStreamId id, QUIC_BUG_IF(!HasRoomForStreamFrame(id, offset)) << "No room for Stream frame, BytesFree: " << BytesFree() << " MinStreamFrameSize: " - << QuicFramer::GetMinStreamFrameSize(id, offset, true); + << QuicFramer::GetMinStreamFrameSize(framer_->version(), id, offset, + true); if (iov_offset == iov.total_length) { QUIC_BUG_IF(!fin) << "Creating a stream frame with no data or fin."; @@ -202,7 +204,7 @@ void QuicPacketCreator::CreateStreamFrame(QuicStreamId id, const size_t data_size = iov.total_length - iov_offset; size_t min_frame_size = QuicFramer::GetMinStreamFrameSize( - id, offset, /* last_frame_in_packet= */ true); + framer_->version(), id, offset, /* last_frame_in_packet= */ true); size_t bytes_consumed = std::min<size_t>(BytesFree() - min_frame_size, data_size); @@ -210,9 +212,6 @@ void QuicPacketCreator::CreateStreamFrame(QuicStreamId id, if (framer_->HasDataProducer()) { *frame = QuicFrame(new QuicStreamFrame(id, set_fin, offset, bytes_consumed)); - if (bytes_consumed > 0) { - framer_->SaveStreamData(id, iov, iov_offset, offset, bytes_consumed); - } return; } UniqueStreamBuffer buffer = @@ -270,8 +269,8 @@ void QuicPacketCreator::Flush() { return; } - QUIC_CACHELINE_ALIGNED char seralized_packet_buffer[kMaxPacketSize]; - SerializePacket(seralized_packet_buffer, kMaxPacketSize); + QUIC_CACHELINE_ALIGNED char serialized_packet_buffer[kMaxPacketSize]; + SerializePacket(serialized_packet_buffer, kMaxPacketSize); OnSerializedPacket(); } @@ -285,8 +284,15 @@ void QuicPacketCreator::OnSerializedPacket() { return; } - delegate_->OnSerializedPacket(&packet_); + if (!FLAGS_quic_reloadable_flag_quic_clear_packet_before_handed_over) { + delegate_->OnSerializedPacket(&packet_); + ClearPacket(); + return; + } + QUIC_FLAG_COUNT(quic_reloadable_flag_quic_clear_packet_before_handed_over); + SerializedPacket packet(std::move(packet_)); ClearPacket(); + delegate_->OnSerializedPacket(&packet); } void QuicPacketCreator::ClearPacket() { @@ -329,7 +335,7 @@ void QuicPacketCreator::CreateAndSerializeStreamFrame( << "Creating a stream frame with no data or fin."; const size_t remaining_data_size = iov.total_length - iov_offset; const size_t min_frame_size = QuicFramer::GetMinStreamFrameSize( - id, stream_offset, /* last_frame_in_packet= */ true); + framer_->version(), id, stream_offset, /* last_frame_in_packet= */ true); const size_t available_size = max_plaintext_size_ - writer.length() - min_frame_size; const size_t bytes_consumed = @@ -340,10 +346,6 @@ void QuicPacketCreator::CreateAndSerializeStreamFrame( if (framer_->HasDataProducer()) { frame = QuicMakeUnique<QuicStreamFrame>(id, set_fin, stream_offset, bytes_consumed); - if (bytes_consumed > 0) { - framer_->SaveStreamData(id, iov, iov_offset, stream_offset, - bytes_consumed); - } } else { UniqueStreamBuffer stream_buffer = NewStreamBuffer(buffer_allocator_, bytes_consumed); @@ -636,18 +638,10 @@ bool QuicPacketCreator::StreamFrameStartsWithChlo( } if (framer_->perspective() == Perspective::IS_SERVER || - frame.stream_id != kCryptoStreamId || iov_offset != 0 || - frame.data_length < sizeof(kCHLO)) { - return false; - } - - if (iov.iov[0].iov_len < sizeof(kCHLO)) { - QUIC_BUG << "iov length " << iov.iov[0].iov_len << " is less than " - << sizeof(kCHLO); + frame.stream_id != kCryptoStreamId || frame.data_length < sizeof(kCHLO)) { return false; } - return strncmp(reinterpret_cast<const char*>(iov.iov[0].iov_base), - reinterpret_cast<const char*>(&kCHLO), sizeof(kCHLO)) == 0; + return framer_->StartsWithChlo(frame.stream_id, frame.offset); } } // namespace net diff --git a/chromium/net/quic/core/quic_packet_creator.h b/chromium/net/quic/core/quic_packet_creator.h index 3911bea5385..0862c48baaa 100644 --- a/chromium/net/quic/core/quic_packet_creator.h +++ b/chromium/net/quic/core/quic_packet_creator.h @@ -20,7 +20,6 @@ #include "net/quic/core/quic_iovector.h" #include "net/quic/core/quic_packets.h" #include "net/quic/core/quic_pending_retransmission.h" -#include "net/quic/core/quic_stream_frame_data_producer.h" #include "net/quic/platform/api/quic_export.h" namespace net { diff --git a/chromium/net/quic/core/quic_packet_creator_test.cc b/chromium/net/quic/core/quic_packet_creator_test.cc index a71e3ef2678..c5a3c6339b3 100644 --- a/chromium/net/quic/core/quic_packet_creator_test.cc +++ b/chromium/net/quic/core/quic_packet_creator_test.cc @@ -23,6 +23,7 @@ #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/test_tools/simple_data_producer.h" using std::string; using testing::DoAll; @@ -50,7 +51,7 @@ struct TestParams { framer_has_data_producer(framer_has_data_producer) {} friend std::ostream& operator<<(std::ostream& os, const TestParams& p) { - os << "{ client_version: " << QuicVersionToString(p.version) + os << "{ version: " << QuicVersionToString(p.version) << " connection id length: " << p.connection_id_length << " include version: " << p.version_serialization << " framer_has_data_producer: " << p.framer_has_data_producer << " }"; @@ -84,6 +85,37 @@ std::vector<TestParams> GetTestParams() { return params; } +class TestPacketCreator : public QuicPacketCreator { + public: + TestPacketCreator(QuicConnectionId connection_id, + QuicFramer* framer, + QuicBufferAllocator* buffer_allocator, + DelegateInterface* delegate, + SimpleDataProducer* producer) + : QuicPacketCreator(connection_id, framer, buffer_allocator, delegate), + producer_(producer) {} + + bool ConsumeData(QuicStreamId id, + QuicIOVector iov, + size_t iov_offset, + QuicStreamOffset offset, + bool fin, + bool needs_full_padding, + QuicFrame* frame) { + if (QuicPacketCreatorPeer::framer(this)->HasDataProducer()) { + // Save data before data is consumed. + QuicByteCount data_length = iov.total_length - iov_offset; + if (data_length > 0) { + producer_->SaveStreamData(id, iov, iov_offset, offset, data_length); + } + } + return QuicPacketCreator::ConsumeData(id, iov, iov_offset, offset, fin, + needs_full_padding, frame); + } + + SimpleDataProducer* producer_; +}; + class QuicPacketCreatorTest : public QuicTestWithParam<TestParams> { public: void ClearSerializedPacketForTests(SerializedPacket* serialized_packet) { @@ -122,7 +154,8 @@ class QuicPacketCreatorTest : public QuicTestWithParam<TestParams> { creator_(connection_id_, &client_framer_, &buffer_allocator_, - &delegate_), + &delegate_, + &producer_), serialized_packet_(creator_.NoPacket()) { creator_.set_connection_id_length(GetParam().connection_id_length); @@ -199,9 +232,9 @@ class QuicPacketCreatorTest : public QuicTestWithParam<TestParams> { // Returns the number of bytes consumed by the non-data fields of a stream // frame, assuming it is the last frame in the packet - size_t GetStreamFrameOverhead() { - return QuicFramer::GetMinStreamFrameSize(kGetNthClientInitiatedStreamId1, - kOffset, true); + size_t GetStreamFrameOverhead(QuicVersion version) { + return QuicFramer::GetMinStreamFrameSize( + version, kGetNthClientInitiatedStreamId1, kOffset, true); } QuicIOVector MakeIOVectorFromStringPiece(QuicStringPiece s) { @@ -219,7 +252,7 @@ class QuicPacketCreatorTest : public QuicTestWithParam<TestParams> { num_padding_bytes, encryption_level, packet_number_length); } - static const QuicStreamOffset kOffset = 1u; + static const QuicStreamOffset kOffset = 0u; char buffer_[kMaxPacketSize]; QuicFrames frames_; @@ -231,7 +264,7 @@ class QuicPacketCreatorTest : public QuicTestWithParam<TestParams> { string data_; struct iovec iov_; SimpleBufferAllocator buffer_allocator_; - QuicPacketCreator creator_; + TestPacketCreator creator_; SerializedPacket serialized_packet_; SimpleDataProducer producer_; }; @@ -354,6 +387,10 @@ TEST_P(QuicPacketCreatorTest, ReserializeFramesWithFullPadding) { QuicFrame frame; QuicIOVector io_vector( MakeIOVectorFromStringPiece("fake handshake message data")); + if (client_framer_.HasDataProducer()) { + producer_.SaveStreamData(kCryptoStreamId, io_vector, 0u, 0u, + io_vector.total_length); + } QuicPacketCreatorPeer::CreateStreamFrame(&creator_, kCryptoStreamId, io_vector, 0u, 0u, false, &frame); QuicFrames frames; @@ -373,6 +410,10 @@ TEST_P(QuicPacketCreatorTest, ReserializeFramesWithFullPadding) { TEST_P(QuicPacketCreatorTest, DoNotRetransmitPendingPadding) { QuicFrame frame; QuicIOVector io_vector(MakeIOVectorFromStringPiece("fake message data")); + if (client_framer_.HasDataProducer()) { + producer_.SaveStreamData(kCryptoStreamId, io_vector, 0u, 0u, + io_vector.total_length); + } QuicPacketCreatorPeer::CreateStreamFrame(&creator_, kCryptoStreamId, io_vector, 0u, 0u, false, &frame); @@ -424,7 +465,8 @@ TEST_P(QuicPacketCreatorTest, DoNotRetransmitPendingPadding) { TEST_P(QuicPacketCreatorTest, ReserializeFramesWithFullPacketAndPadding) { const size_t overhead = GetPacketHeaderOverhead(client_framer_.version()) + - GetEncryptionOverhead() + GetStreamFrameOverhead(); + GetEncryptionOverhead() + + GetStreamFrameOverhead(client_framer_.version()); size_t capacity = kDefaultMaxPacketSize - overhead; for (int delta = -5; delta <= 0; ++delta) { string data(capacity + delta, 'A'); @@ -432,6 +474,12 @@ TEST_P(QuicPacketCreatorTest, ReserializeFramesWithFullPacketAndPadding) { QuicFrame frame; QuicIOVector io_vector(MakeIOVectorFromStringPiece(data)); + SimpleDataProducer producer; + if (client_framer_.HasDataProducer()) { + producer.SaveStreamData(kCryptoStreamId, io_vector, 0u, 0u, + io_vector.total_length); + QuicPacketCreatorPeer::framer(&creator_)->set_data_producer(&producer); + } QuicPacketCreatorPeer::CreateStreamFrame( &creator_, kCryptoStreamId, io_vector, 0, kOffset, false, &frame); QuicFrames frames; @@ -499,12 +547,12 @@ TEST_P(QuicPacketCreatorTest, ConsumeData) { TEST_P(QuicPacketCreatorTest, ConsumeDataFin) { QuicFrame frame; QuicIOVector io_vector(MakeIOVectorFromStringPiece("test")); - ASSERT_TRUE(creator_.ConsumeData(kCryptoStreamId, io_vector, 0u, 10u, true, + ASSERT_TRUE(creator_.ConsumeData(kCryptoStreamId, io_vector, 0u, 0u, true, false, &frame)); ASSERT_TRUE(frame.stream_frame); size_t consumed = frame.stream_frame->data_length; EXPECT_EQ(4u, consumed); - CheckStreamFrame(frame, 1u, "test", 10u, true); + CheckStreamFrame(frame, 1u, "test", 0u, true); EXPECT_TRUE(creator_.HasPendingFrames()); } @@ -526,7 +574,8 @@ TEST_P(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) { GetEncryptionOverhead(); for (size_t i = overhead; i < overhead + 100; ++i) { creator_.SetMaxPacketLength(i); - const bool should_have_room = i > overhead + GetStreamFrameOverhead(); + const bool should_have_room = + i > overhead + GetStreamFrameOverhead(client_framer_.version()); ASSERT_EQ(should_have_room, creator_.HasRoomForStreamFrame( kGetNthClientInitiatedStreamId1, kOffset)); if (should_have_room) { @@ -550,7 +599,8 @@ TEST_P(QuicPacketCreatorTest, StreamFrameConsumption) { creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); // Compute the total overhead for a single frame in packet. const size_t overhead = GetPacketHeaderOverhead(client_framer_.version()) + - GetEncryptionOverhead() + GetStreamFrameOverhead(); + GetEncryptionOverhead() + + GetStreamFrameOverhead(client_framer_.version()); size_t capacity = kDefaultMaxPacketSize - overhead; // Now, test various sizes around this size. for (int delta = -5; delta <= 5; ++delta) { @@ -578,7 +628,8 @@ TEST_P(QuicPacketCreatorTest, StreamFrameConsumption) { TEST_P(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) { // Compute the total overhead for a single frame in packet. const size_t overhead = GetPacketHeaderOverhead(client_framer_.version()) + - GetEncryptionOverhead() + GetStreamFrameOverhead(); + GetEncryptionOverhead() + + GetStreamFrameOverhead(client_framer_.version()); ASSERT_GT(kMaxPacketSize, overhead); size_t capacity = kDefaultMaxPacketSize - overhead; // Now, test various sizes around this size. @@ -615,7 +666,8 @@ TEST_P(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) { creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); // Compute the total overhead for a single frame in packet. const size_t overhead = GetPacketHeaderOverhead(client_framer_.version()) + - GetEncryptionOverhead() + GetStreamFrameOverhead(); + GetEncryptionOverhead() + + GetStreamFrameOverhead(client_framer_.version()); ASSERT_GT(kDefaultMaxPacketSize, overhead); size_t capacity = kDefaultMaxPacketSize - overhead; // Now, test various sizes around this size. @@ -683,8 +735,13 @@ TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthLeastAwaiting) { QuicPacketCreatorPeer::SetPacketNumber(&creator_, UINT64_C(64) * 256 * 256 * 256 * 256); creator_.UpdatePacketNumberLength(2, 10000 / kDefaultMaxPacketSize); - EXPECT_EQ(PACKET_6BYTE_PACKET_NUMBER, - QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + if (GetParam().version <= QUIC_VERSION_39) { + EXPECT_EQ(PACKET_6BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + } else { + EXPECT_EQ(PACKET_8BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + } } TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthCwnd) { @@ -706,8 +763,13 @@ TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthCwnd) { creator_.UpdatePacketNumberLength( 1, UINT64_C(1000) * 256 * 256 * 256 * 256 / kDefaultMaxPacketSize); - EXPECT_EQ(PACKET_6BYTE_PACKET_NUMBER, - QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + if (GetParam().version <= QUIC_VERSION_39) { + EXPECT_EQ(PACKET_6BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + } else { + EXPECT_EQ(PACKET_8BYTE_PACKET_NUMBER, + QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); + } } TEST_P(QuicPacketCreatorTest, SerializeFrame) { @@ -830,6 +892,9 @@ TEST_P(QuicPacketCreatorTest, SerializeAndSendStreamFrame) { EXPECT_FALSE(creator_.HasPendingFrames()); QuicIOVector iov(MakeIOVectorFromStringPiece("test")); + if (client_framer_.HasDataProducer()) { + producer_.SaveStreamData(kHeadersStreamId, iov, 0u, 0u, iov.total_length); + } EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); size_t num_bytes_consumed; @@ -953,6 +1018,10 @@ TEST_P(QuicPacketCreatorTest, SendPacketAfterFullPaddingRetransmission) { QuicFrame frame; QuicIOVector io_vector( MakeIOVectorFromStringPiece("fake handshake message data")); + if (client_framer_.HasDataProducer()) { + producer_.SaveStreamData(kCryptoStreamId, io_vector, 0u, 0u, + io_vector.total_length); + } QuicPacketCreatorPeer::CreateStreamFrame(&creator_, kCryptoStreamId, io_vector, 0u, 0u, false, &frame); QuicFrames frames; @@ -1006,7 +1075,8 @@ TEST_P(QuicPacketCreatorTest, ConsumeDataAndRandomPadding) { size_t length = GetPacketHeaderOverhead(client_framer_.version()) + GetEncryptionOverhead() + QuicFramer::GetMinStreamFrameSize( - kCryptoStreamId, 0, /*last_frame_in_packet=*/false) + + client_framer_.version(), kCryptoStreamId, 0, + /*last_frame_in_packet=*/false) + kStreamFramePayloadSize + 1; creator_.SetMaxPacketLength(length); creator_.AddPendingPadding(kMaxNumRandomPaddingBytes); @@ -1029,7 +1099,7 @@ TEST_P(QuicPacketCreatorTest, ConsumeDataAndRandomPadding) { creator_.ConsumeData(kCryptoStreamId, MakeIOVectorFromStringPiece( QuicStringPiece(buf, kStreamFramePayloadSize + 1)), - 0u, 0u, false, false, &frame); + 0u, kStreamFramePayloadSize, false, false, &frame); // No padding is sent. creator_.Flush(); delete frame.stream_frame; diff --git a/chromium/net/quic/core/quic_packet_generator.cc b/chromium/net/quic/core/quic_packet_generator.cc index 7487cde8fa4..8f1e372621e 100644 --- a/chromium/net/quic/core/quic_packet_generator.cc +++ b/chromium/net/quic/core/quic_packet_generator.cc @@ -9,6 +9,8 @@ #include "net/quic/core/crypto/quic_random.h" #include "net/quic/core/quic_utils.h" #include "net/quic/platform/api/quic_bug_tracker.h" +#include "net/quic/platform/api/quic_flag_utils.h" +#include "net/quic/platform/api/quic_flags.h" #include "net/quic/platform/api/quic_logging.h" namespace net { @@ -55,7 +57,8 @@ QuicConsumedData QuicPacketGenerator::ConsumeData( QuicIOVector iov, QuicStreamOffset offset, StreamSendingState state, - QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener, + bool flag_run_fast_path) { bool has_handshake = (id == kCryptoStreamId); bool fin = state != NO_FIN; QUIC_BUG_IF(has_handshake && fin) @@ -77,9 +80,16 @@ QuicConsumedData QuicPacketGenerator::ConsumeData( QUIC_BUG << "Attempt to consume empty data without FIN."; return QuicConsumedData(0, false); } - - while (delegate_->ShouldGeneratePacket( - HAS_RETRANSMITTABLE_DATA, has_handshake ? IS_HANDSHAKE : NOT_HANDSHAKE)) { + // We determine if we can enter the fast path before executing + // the slow path loop. + bool run_fast_path = + flag_run_fast_path && + (!has_handshake && state != FIN_AND_PADDING && !HasQueuedFrames() && + iov.total_length - total_bytes_consumed > kMaxPacketSize); + + while (!run_fast_path && delegate_->ShouldGeneratePacket( + HAS_RETRANSMITTABLE_DATA, + has_handshake ? IS_HANDSHAKE : NOT_HANDSHAKE)) { QuicFrame frame; if (!packet_creator_.ConsumeData(id, iov, total_bytes_consumed, offset + total_bytes_consumed, fin, @@ -115,6 +125,17 @@ QuicConsumedData QuicPacketGenerator::ConsumeData( } // TODO(ianswett): Move to having the creator flush itself when it's full. packet_creator_.Flush(); + + run_fast_path = + flag_run_fast_path && + (!has_handshake && state != FIN_AND_PADDING && !HasQueuedFrames() && + iov.total_length - total_bytes_consumed > kMaxPacketSize); + } + + if (run_fast_path) { + QUIC_FLAG_COUNT(quic_reloadable_flag_quic_consuming_data_faster); + return ConsumeDataFastPath(id, iov, offset, state != NO_FIN, + total_bytes_consumed, std::move(ack_listener)); } // Don't allow the handshake to be bundled with other retransmittable frames. @@ -131,9 +152,10 @@ QuicConsumedData QuicPacketGenerator::ConsumeDataFastPath( const QuicIOVector& iov, QuicStreamOffset offset, bool fin, - QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { + size_t total_bytes_consumed, + const QuicReferenceCountedPointer<QuicAckListenerInterface>& ack_listener) { DCHECK_NE(id, kCryptoStreamId); - size_t total_bytes_consumed = 0; + while (total_bytes_consumed < iov.total_length && delegate_->ShouldGeneratePacket(HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)) { diff --git a/chromium/net/quic/core/quic_packet_generator.h b/chromium/net/quic/core/quic_packet_generator.h index 43bffc1f4a4..637653c747b 100644 --- a/chromium/net/quic/core/quic_packet_generator.h +++ b/chromium/net/quic/core/quic_packet_generator.h @@ -101,17 +101,22 @@ class QUIC_EXPORT_PRIVATE QuicPacketGenerator { QuicIOVector iov, QuicStreamOffset offset, StreamSendingState state, - QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener, + bool flag_run_fast_path); // Sends as many data only packets as allowed by the send algorithm and the // available iov. - // This path does not support FEC, padding, or bundling pending frames. + // This path does not support padding, or bundling pending frames. + // In case we access this method from ConsumeData, total_bytes_consumed + // keeps track of how many bytes have already been consumed. QuicConsumedData ConsumeDataFastPath( QuicStreamId id, const QuicIOVector& iov, QuicStreamOffset offset, bool fin, - QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); + size_t total_bytes_consumed, + const QuicReferenceCountedPointer<QuicAckListenerInterface>& + ack_listener); // Generates an MTU discovery packet of specified size. void GenerateMtuDiscoveryPacket( diff --git a/chromium/net/quic/core/quic_packet_generator_test.cc b/chromium/net/quic/core/quic_packet_generator_test.cc index 74a9801ad89..2e5c8fba0a0 100644 --- a/chromium/net/quic/core/quic_packet_generator_test.cc +++ b/chromium/net/quic/core/quic_packet_generator_test.cc @@ -102,6 +102,61 @@ struct PacketContents { } // namespace +class TestPacketGenerator : public QuicPacketGenerator { + public: + TestPacketGenerator(QuicConnectionId connection_id, + QuicFramer* framer, + QuicRandom* random_generator, + QuicBufferAllocator* buffer_allocator, + DelegateInterface* delegate, + SimpleDataProducer* producer) + : QuicPacketGenerator(connection_id, + framer, + random_generator, + buffer_allocator, + delegate), + producer_(producer) {} + + QuicConsumedData ConsumeDataFastPath( + QuicStreamId id, + const QuicIOVector& iov, + QuicStreamOffset offset, + bool fin, + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { + if (QuicPacketCreatorPeer::framer( + QuicPacketGeneratorPeer::GetPacketCreator(this)) + ->HasDataProducer()) { + // Save data before data is consumed. + if (iov.total_length > 0) { + producer_->SaveStreamData(id, iov, 0, offset, iov.total_length); + } + } + return QuicPacketGenerator::ConsumeDataFastPath(id, iov, offset, fin, 0, + std::move(ack_listener)); + } + + QuicConsumedData ConsumeData( + QuicStreamId id, + QuicIOVector iov, + QuicStreamOffset offset, + StreamSendingState state, + QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { + if (QuicPacketCreatorPeer::framer( + QuicPacketGeneratorPeer::GetPacketCreator(this)) + ->HasDataProducer()) { + // Save data before data is consumed. + if (iov.total_length > 0) { + producer_->SaveStreamData(id, iov, 0, offset, iov.total_length); + } + } + return QuicPacketGenerator::ConsumeData( + id, iov, offset, state, std::move(ack_listener), + FLAGS_quic_reloadable_flag_quic_consuming_data_faster); + } + + SimpleDataProducer* producer_; +}; + class QuicPacketGeneratorTest : public QuicTest { public: QuicPacketGeneratorTest() @@ -112,12 +167,13 @@ class QuicPacketGeneratorTest : public QuicTest { &framer_, &random_generator_, &buffer_allocator_, - &delegate_), + &delegate_, + &producer_), creator_(QuicPacketGeneratorPeer::GetPacketCreator(&generator_)) { creator_->SetEncrypter(ENCRYPTION_FORWARD_SECURE, new NullEncrypter(Perspective::IS_CLIENT)); creator_->set_encryption_level(ENCRYPTION_FORWARD_SECURE); - if (FLAGS_quic_reloadable_flag_quic_stream_owns_data) { + if (FLAGS_quic_reloadable_flag_quic_save_data_before_consumption2) { framer_.set_data_producer(&producer_); } } @@ -223,7 +279,7 @@ class QuicPacketGeneratorTest : public QuicTest { MockRandom random_generator_; SimpleBufferAllocator buffer_allocator_; StrictMock<MockDelegate> delegate_; - QuicPacketGenerator generator_; + TestPacketGenerator generator_; QuicPacketCreator* creator_; SimpleQuicFramer simple_framer_; std::vector<SerializedPacket> packets_; @@ -367,7 +423,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_NotWritable) { delegate_.SetCanNotWrite(); QuicConsumedData consumed = generator_.ConsumeData( - kHeadersStreamId, MakeIOVectorFromStringPiece("foo"), 2, FIN, nullptr); + kHeadersStreamId, MakeIOVectorFromStringPiece("foo"), 0, FIN, nullptr); EXPECT_EQ(0u, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); EXPECT_FALSE(generator_.HasQueuedFrames()); @@ -379,7 +435,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldNotFlush) { generator_.StartBatchOperations(); QuicConsumedData consumed = generator_.ConsumeData( - kHeadersStreamId, MakeIOVectorFromStringPiece("foo"), 2, FIN, nullptr); + kHeadersStreamId, MakeIOVectorFromStringPiece("foo"), 0, FIN, nullptr); EXPECT_EQ(3u, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); EXPECT_TRUE(generator_.HasQueuedFrames()); @@ -392,7 +448,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldFlush) { EXPECT_CALL(delegate_, OnSerializedPacket(_)) .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); QuicConsumedData consumed = generator_.ConsumeData( - kHeadersStreamId, MakeIOVectorFromStringPiece("foo"), 2, FIN, nullptr); + kHeadersStreamId, MakeIOVectorFromStringPiece("foo"), 0, FIN, nullptr); EXPECT_EQ(3u, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); EXPECT_FALSE(generator_.HasQueuedFrames()); @@ -441,9 +497,9 @@ TEST_F(QuicPacketGeneratorTest, generator_.StartBatchOperations(); generator_.ConsumeData(kHeadersStreamId, MakeIOVectorFromStringPiece("foo"), - 2, FIN, nullptr); + 0, FIN, nullptr); QuicConsumedData consumed = generator_.ConsumeData( - 3, MakeIOVectorFromStringPiece("quux"), 7, NO_FIN, nullptr); + 3, MakeIOVectorFromStringPiece("quux"), 3, NO_FIN, nullptr); EXPECT_EQ(4u, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); EXPECT_TRUE(generator_.HasQueuedFrames()); @@ -455,9 +511,9 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_BatchOperations) { generator_.StartBatchOperations(); generator_.ConsumeData(kHeadersStreamId, MakeIOVectorFromStringPiece("foo"), - 2, FIN, nullptr); + 0, FIN, nullptr); QuicConsumedData consumed = generator_.ConsumeData( - 3, MakeIOVectorFromStringPiece("quux"), 7, NO_FIN, nullptr); + 3, MakeIOVectorFromStringPiece("quux"), 3, NO_FIN, nullptr); EXPECT_EQ(4u, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); EXPECT_TRUE(generator_.HasQueuedFrames()); @@ -486,8 +542,8 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) { QuicPacketCreatorPeer::GetPacketNumberLength(creator_)) + // Add an extra 3 bytes for the payload and 1 byte so BytesFree is larger // than the GetMinStreamFrameSize. - QuicFramer::GetMinStreamFrameSize(1, 0, false) + 3 + - QuicFramer::GetMinStreamFrameSize(1, 0, true) + 1; + QuicFramer::GetMinStreamFrameSize(framer_.version(), 1, 0, false) + 3 + + QuicFramer::GetMinStreamFrameSize(framer_.version(), 1, 0, true) + 1; generator_.SetMaxPacketLength(length); delegate_.SetCanWriteAnything(); { @@ -543,6 +599,113 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFastPath) { PacketContents contents; contents.num_stream_frames = 1; CheckPacketContains(contents, 0); + EXPECT_FALSE(packets_.empty()); + SerializedPacket packet = packets_.back(); + EXPECT_TRUE(!packet.retransmittable_frames.empty()); + EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type); + QuicStreamFrame* stream_frame = + packet.retransmittable_frames.front().stream_frame; + EXPECT_EQ(10000u, stream_frame->data_length + stream_frame->offset); +} + +TEST_F(QuicPacketGeneratorTest, ConsumeDataLarge) { + delegate_.SetCanWriteAnything(); + + // Create a 10000 byte IOVector. + QuicIOVector iov(CreateData(10000)); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + QuicConsumedData consumed = + generator_.ConsumeData(kHeadersStreamId, iov, 0, FIN, nullptr); + EXPECT_EQ(10000u, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + PacketContents contents; + contents.num_stream_frames = 1; + CheckPacketContains(contents, 0); + EXPECT_FALSE(packets_.empty()); + SerializedPacket packet = packets_.back(); + EXPECT_TRUE(!packet.retransmittable_frames.empty()); + EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type); + QuicStreamFrame* stream_frame = + packet.retransmittable_frames.front().stream_frame; + EXPECT_EQ(10000u, stream_frame->data_length + stream_frame->offset); +} + +TEST_F(QuicPacketGeneratorTest, ConsumeDataLargeSendAckFalse) { + delegate_.SetCanNotWrite(); + + generator_.SetShouldSendAck(false); + generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame())); + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_TRUE(generator_.HasRetransmittableFrames()); + + delegate_.SetCanWriteAnything(); + + generator_.StartBatchOperations(); + + EXPECT_CALL(delegate_, GetUpdatedAckFrame()) + .WillOnce(Return(QuicFrame(&ack_frame_))); + + // Create a 10000 byte IOVector. + QuicIOVector iov(CreateData(10000)); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + QuicConsumedData consumed = + generator_.ConsumeData(kHeadersStreamId, iov, 0, FIN, nullptr); + generator_.FinishBatchOperations(); + + EXPECT_EQ(10000u, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + EXPECT_FALSE(packets_.empty()); + SerializedPacket packet = packets_.back(); + EXPECT_TRUE(!packet.retransmittable_frames.empty()); + EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type); + QuicStreamFrame* stream_frame = + packet.retransmittable_frames.front().stream_frame; + EXPECT_EQ(10000u, stream_frame->data_length + stream_frame->offset); +} + +TEST_F(QuicPacketGeneratorTest, ConsumeDataLargeSendAckTrue) { + delegate_.SetCanNotWrite(); + generator_.SetShouldSendAck(true); + delegate_.SetCanWriteAnything(); + generator_.StartBatchOperations(); + + // Set up frames to write into the creator when control frames are written. + EXPECT_CALL(delegate_, GetUpdatedAckFrame()) + .WillOnce(Return(QuicFrame(&ack_frame_))); + EXPECT_CALL(delegate_, PopulateStopWaitingFrame(_)); + // Generator should have queued control frames, and creator should be empty. + EXPECT_TRUE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + EXPECT_FALSE(creator_->HasPendingFrames()); + + // Create a 10000 byte IOVector. + QuicIOVector iov(CreateData(10000)); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); + QuicConsumedData consumed = + generator_.ConsumeData(kHeadersStreamId, iov, 0, FIN, nullptr); + generator_.FinishBatchOperations(); + + EXPECT_EQ(10000u, consumed.bytes_consumed); + EXPECT_TRUE(consumed.fin_consumed); + EXPECT_FALSE(generator_.HasQueuedFrames()); + EXPECT_FALSE(generator_.HasRetransmittableFrames()); + + EXPECT_FALSE(packets_.empty()); + SerializedPacket packet = packets_.back(); + EXPECT_TRUE(!packet.retransmittable_frames.empty()); + EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type); + QuicStreamFrame* stream_frame = + packet.retransmittable_frames.front().stream_frame; + EXPECT_EQ(10000u, stream_frame->data_length + stream_frame->offset); } TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) { @@ -562,7 +725,7 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) { .WillOnce(Return(QuicFrame(&ack_frame_))); // Send some data and a control frame - generator_.ConsumeData(3, MakeIOVectorFromStringPiece("quux"), 7, NO_FIN, + generator_.ConsumeData(3, MakeIOVectorFromStringPiece("quux"), 0, NO_FIN, nullptr); generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame())); @@ -659,7 +822,7 @@ TEST_F(QuicPacketGeneratorTest, SetMaxPacketLength_Initial) { .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket)); QuicConsumedData consumed = generator_.ConsumeData(kHeadersStreamId, CreateData(data_len), - /*offset=*/2, FIN, nullptr); + /*offset=*/0, FIN, nullptr); EXPECT_EQ(data_len, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); EXPECT_FALSE(generator_.HasQueuedFrames()); @@ -694,7 +857,7 @@ TEST_F(QuicPacketGeneratorTest, SetMaxPacketLength_Middle) { // Send two packets before packet size change. QuicConsumedData consumed = generator_.ConsumeData(kHeadersStreamId, CreateData(data_len), - /*offset=*/2, NO_FIN, nullptr); + /*offset=*/0, NO_FIN, nullptr); EXPECT_EQ(data_len, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); EXPECT_FALSE(generator_.HasQueuedFrames()); @@ -709,7 +872,7 @@ TEST_F(QuicPacketGeneratorTest, SetMaxPacketLength_Middle) { // Send a packet after packet size change. consumed = generator_.ConsumeData(kHeadersStreamId, CreateData(data_len), - 2 + data_len, FIN, nullptr); + data_len, FIN, nullptr); EXPECT_EQ(data_len, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); EXPECT_FALSE(generator_.HasQueuedFrames()); @@ -738,7 +901,7 @@ TEST_F(QuicPacketGeneratorTest, SetMaxPacketLength_MidpacketFlush) { // should not cause packet serialization. QuicConsumedData consumed = generator_.ConsumeData(kHeadersStreamId, CreateData(first_write_len), - /*offset=*/2, NO_FIN, nullptr); + /*offset=*/0, NO_FIN, nullptr); EXPECT_EQ(first_write_len, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); EXPECT_TRUE(generator_.HasQueuedFrames()); @@ -769,7 +932,7 @@ TEST_F(QuicPacketGeneratorTest, SetMaxPacketLength_MidpacketFlush) { // trigger serialization of one packet, and queue another one. consumed = generator_.ConsumeData(kHeadersStreamId, CreateData(second_write_len), - /*offset=*/2 + first_write_len, FIN, nullptr); + /*offset=*/first_write_len, FIN, nullptr); EXPECT_EQ(second_write_len, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); EXPECT_TRUE(generator_.HasQueuedFrames()); @@ -829,7 +992,7 @@ TEST_F(QuicPacketGeneratorTest, GenerateMtuDiscoveryPacket_SurroundedByData) { // Send data before the MTU probe. QuicConsumedData consumed = generator_.ConsumeData(kHeadersStreamId, CreateData(data_len), - /*offset=*/2, NO_FIN, nullptr); + /*offset=*/0, NO_FIN, nullptr); EXPECT_EQ(data_len, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); EXPECT_FALSE(generator_.HasQueuedFrames()); @@ -842,7 +1005,7 @@ TEST_F(QuicPacketGeneratorTest, GenerateMtuDiscoveryPacket_SurroundedByData) { // Send data after the MTU probe. consumed = generator_.ConsumeData(kHeadersStreamId, CreateData(data_len), - /*offset=*/2 + data_len, FIN, nullptr); + /*offset=*/data_len, FIN, nullptr); EXPECT_EQ(data_len, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); EXPECT_FALSE(generator_.HasQueuedFrames()); @@ -916,14 +1079,15 @@ TEST_F(QuicPacketGeneratorTest, RandomPaddingAfterFinSingleStreamSinglePacket) { const QuicStreamId kDataStreamId = 5; // Set the packet size be enough for one stream frame with 0 stream offset and // max size of random padding. - size_t length = NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) + - GetPacketHeaderSize( - framer_.version(), creator_->connection_id_length(), - kIncludeVersion, !kIncludeDiversificationNonce, - QuicPacketCreatorPeer::GetPacketNumberLength(creator_)) + - QuicFramer::GetMinStreamFrameSize( - kDataStreamId, 0, /*last_frame_in_packet=*/false) + - kStreamFramePayloadSize + kMaxNumRandomPaddingBytes; + size_t length = + NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) + + GetPacketHeaderSize( + framer_.version(), creator_->connection_id_length(), kIncludeVersion, + !kIncludeDiversificationNonce, + QuicPacketCreatorPeer::GetPacketNumberLength(creator_)) + + QuicFramer::GetMinStreamFrameSize(framer_.version(), kDataStreamId, 0, + /*last_frame_in_packet=*/false) + + kStreamFramePayloadSize + kMaxNumRandomPaddingBytes; generator_.SetMaxPacketLength(length); delegate_.SetCanWriteAnything(); generator_.StartBatchOperations(); @@ -954,14 +1118,15 @@ TEST_F(QuicPacketGeneratorTest, const QuicStreamId kDataStreamId = 5; // Set the packet size be enough for one stream frame with 0 stream offset + // 1. One or more packets will accommodate. - size_t length = NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) + - GetPacketHeaderSize( - framer_.version(), creator_->connection_id_length(), - kIncludeVersion, !kIncludeDiversificationNonce, - QuicPacketCreatorPeer::GetPacketNumberLength(creator_)) + - QuicFramer::GetMinStreamFrameSize( - kDataStreamId, 0, /*last_frame_in_packet=*/false) + - kStreamFramePayloadSize + 1; + size_t length = + NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) + + GetPacketHeaderSize( + framer_.version(), creator_->connection_id_length(), kIncludeVersion, + !kIncludeDiversificationNonce, + QuicPacketCreatorPeer::GetPacketNumberLength(creator_)) + + QuicFramer::GetMinStreamFrameSize(framer_.version(), kDataStreamId, 0, + /*last_frame_in_packet=*/false) + + kStreamFramePayloadSize + 1; generator_.SetMaxPacketLength(length); delegate_.SetCanWriteAnything(); generator_.StartBatchOperations(); @@ -1000,17 +1165,18 @@ TEST_F(QuicPacketGeneratorTest, const QuicStreamId kDataStreamId2 = 6; // Set the packet size be enough for first frame with 0 stream offset + second // frame + 1 byte payload. two or more packets will accommodate. - size_t length = NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) + - GetPacketHeaderSize( - framer_.version(), creator_->connection_id_length(), - kIncludeVersion, !kIncludeDiversificationNonce, - QuicPacketCreatorPeer::GetPacketNumberLength(creator_)) + - QuicFramer::GetMinStreamFrameSize( - kDataStreamId1, 0, /*last_frame_in_packet=*/false) + - kStreamFramePayloadSize + - QuicFramer::GetMinStreamFrameSize( - kDataStreamId1, 0, /*last_frame_in_packet=*/false) + - 1; + size_t length = + NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) + + GetPacketHeaderSize( + framer_.version(), creator_->connection_id_length(), kIncludeVersion, + !kIncludeDiversificationNonce, + QuicPacketCreatorPeer::GetPacketNumberLength(creator_)) + + QuicFramer::GetMinStreamFrameSize(framer_.version(), kDataStreamId1, 0, + /*last_frame_in_packet=*/false) + + kStreamFramePayloadSize + + QuicFramer::GetMinStreamFrameSize(framer_.version(), kDataStreamId1, 0, + /*last_frame_in_packet=*/false) + + 1; generator_.SetMaxPacketLength(length); delegate_.SetCanWriteAnything(); generator_.StartBatchOperations(); diff --git a/chromium/net/quic/core/quic_packets.cc b/chromium/net/quic/core/quic_packets.cc index 579aa8cad5f..16720b7b984 100644 --- a/chromium/net/quic/core/quic_packets.cc +++ b/chromium/net/quic/core/quic_packets.cc @@ -216,6 +216,26 @@ SerializedPacket::SerializedPacket(QuicPacketNumber packet_number, SerializedPacket::SerializedPacket(const SerializedPacket& other) = default; +SerializedPacket& SerializedPacket::operator=(const SerializedPacket& other) = + default; + +SerializedPacket::SerializedPacket(SerializedPacket&& other) + : encrypted_buffer(other.encrypted_buffer), + encrypted_length(other.encrypted_length), + has_crypto_handshake(other.has_crypto_handshake), + num_padding_bytes(other.num_padding_bytes), + packet_number(other.packet_number), + packet_number_length(other.packet_number_length), + encryption_level(other.encryption_level), + has_ack(other.has_ack), + has_stop_waiting(other.has_stop_waiting), + transmission_type(other.transmission_type), + original_packet_number(other.original_packet_number), + largest_acked(other.largest_acked) { + retransmittable_frames.swap(other.retransmittable_frames); + listeners.swap(other.listeners); +} + SerializedPacket::~SerializedPacket() {} void ClearSerializedPacket(SerializedPacket* serialized_packet) { diff --git a/chromium/net/quic/core/quic_packets.h b/chromium/net/quic/core/quic_packets.h index d097a0cbbaa..f552f0d666a 100644 --- a/chromium/net/quic/core/quic_packets.h +++ b/chromium/net/quic/core/quic_packets.h @@ -214,6 +214,8 @@ struct QUIC_EXPORT_PRIVATE SerializedPacket { bool has_ack, bool has_stop_waiting); SerializedPacket(const SerializedPacket& other); + SerializedPacket& operator=(const SerializedPacket& other); + SerializedPacket(SerializedPacket&& other); ~SerializedPacket(); // Not owned. diff --git a/chromium/net/quic/core/quic_sent_packet_manager.cc b/chromium/net/quic/core/quic_sent_packet_manager.cc index 90eaad05966..1491f3add53 100644 --- a/chromium/net/quic/core/quic_sent_packet_manager.cc +++ b/chromium/net/quic/core/quic_sent_packet_manager.cc @@ -15,6 +15,7 @@ #include "net/quic/core/quic_connection_stats.h" #include "net/quic/core/quic_pending_retransmission.h" #include "net/quic/platform/api/quic_bug_tracker.h" +#include "net/quic/platform/api/quic_flag_utils.h" #include "net/quic/platform/api/quic_flags.h" #include "net/quic/platform/api/quic_logging.h" #include "net/quic/platform/api/quic_map_util.h" @@ -79,7 +80,6 @@ QuicSentPacketManager::QuicSentPacketManager( enable_half_rtt_tail_loss_probe_(false), using_pacing_(false), use_new_rto_(false), - undo_pending_retransmits_(false), conservative_handshake_retransmits_(false), largest_newly_acked_(0), largest_mtu_acked_(0), @@ -151,9 +151,6 @@ void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) { if (config.HasClientRequestedIndependentOption(kLFAK, perspective_)) { general_loss_algorithm_.SetLossDetectionType(kLazyFack); } - if (config.HasClientSentConnectionOption(kUNDO, perspective_)) { - undo_pending_retransmits_ = true; - } if (config.HasClientSentConnectionOption(kCONH, perspective_)) { conservative_handshake_retransmits_ = true; } @@ -174,8 +171,14 @@ void QuicSentPacketManager::ResumeConnectionState( std::max(kMinInitialRoundTripTimeUs, std::min(kMaxInitialRoundTripTimeUs, initial_rtt_us))); } - send_algorithm_->ResumeConnectionState(cached_network_params, - max_bandwidth_resumption); + + QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond( + max_bandwidth_resumption + ? cached_network_params.max_bandwidth_estimate_bytes_per_second() + : cached_network_params.bandwidth_estimate_bytes_per_second()); + QuicTime::Delta rtt = + QuicTime::Delta::FromMilliseconds(cached_network_params.min_rtt_ms()); + send_algorithm_->AdjustNetworkParameters(bandwidth, rtt); } void QuicSentPacketManager::SetNumOpenStreams(size_t num_streams) { @@ -238,17 +241,6 @@ void QuicSentPacketManager::OnIncomingAck(const QuicAckFrame& ack_frame, consecutive_tlp_count_ = 0; consecutive_crypto_retransmission_count_ = 0; } - // TODO(ianswett): Consider replacing the pending_retransmissions_ with a - // fast way to retrieve the next pending retransmission, if there are any. - // A single packet number indicating all packets below that are lost should - // be all the state that is necessary. - while (undo_pending_retransmits_ && !pending_retransmissions_.empty() && - pending_retransmissions_.front().first > largest_newly_acked_ && - pending_retransmissions_.front().second == LOSS_RETRANSMISSION) { - // Cancel any pending retransmissions larger than largest_newly_acked_. - unacked_packets_.RestoreToInFlight(pending_retransmissions_.front().first); - pending_retransmissions_.pop_front(); - } if (debug_delegate_ != nullptr) { debug_delegate_->OnIncomingAck(ack_frame, ack_receive_time, @@ -289,6 +281,8 @@ void QuicSentPacketManager::MaybeInvokeCongestionEvent( void QuicSentPacketManager::HandleAckForSentPackets( const QuicAckFrame& ack_frame) { + const bool skip_unackable_packets_early = + FLAGS_quic_reloadable_flag_quic_handle_acks; // Go through the packets we have not received an ack for and see if this // incoming_ack shows they've been seen by the peer. QuicTime::Delta ack_delay_time = ack_frame.ack_delay_time; @@ -299,7 +293,10 @@ void QuicSentPacketManager::HandleAckForSentPackets( // These packets are still in flight. break; } - + if (skip_unackable_packets_early && it->is_unackable) { + QUIC_FLAG_COUNT(quic_reloadable_flag_quic_handle_acks); + continue; + } if (!ack_frame.packets.Contains(packet_number)) { // Packet is still missing. continue; @@ -313,8 +310,9 @@ void QuicSentPacketManager::HandleAckForSentPackets( // If data is associated with the most recent transmission of this // packet, then inform the caller. if (it->in_flight) { - packets_acked_.push_back(std::make_pair(packet_number, it->bytes_sent)); - } else if (!it->is_unackable) { + packets_acked_.push_back(SendAlgorithmInterface::AckedPacket( + packet_number, it->bytes_sent, QuicTime::Zero())); + } else if (skip_unackable_packets_early || !it->is_unackable) { // Packets are marked unackable after they've been acked once. largest_newly_acked_ = packet_number; } @@ -670,8 +668,9 @@ QuicSentPacketManager::GetRetransmissionMode() const { void QuicSentPacketManager::InvokeLossDetection(QuicTime time) { if (!packets_acked_.empty()) { - DCHECK_LE(packets_acked_.front().first, packets_acked_.back().first); - largest_newly_acked_ = packets_acked_.back().first; + DCHECK_LE(packets_acked_.front().packet_number, + packets_acked_.back().packet_number); + largest_newly_acked_ = packets_acked_.back().packet_number; } loss_algorithm_->DetectLosses(unacked_packets_, time, rtt_stats_, largest_newly_acked_, &packets_lost_); @@ -929,11 +928,6 @@ QuicPacketNumber QuicSentPacketManager::GetLargestSentPacket() const { return unacked_packets_.largest_sent_packet(); } -// Remove this method when deprecating QUIC_VERSION_33. -QuicPacketNumber QuicSentPacketManager::GetLeastPacketAwaitedByPeer() const { - return least_packet_awaited_by_peer_; -} - void QuicSentPacketManager::SetNetworkChangeVisitor( NetworkChangeVisitor* visitor) { DCHECK(!network_change_visitor_); diff --git a/chromium/net/quic/core/quic_sent_packet_manager.h b/chromium/net/quic/core/quic_sent_packet_manager.h index 268a54c43c9..14d264707c2 100644 --- a/chromium/net/quic/core/quic_sent_packet_manager.h +++ b/chromium/net/quic/core/quic_sent_packet_manager.h @@ -210,8 +210,6 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { QuicPacketNumber GetLargestSentPacket() const; - QuicPacketNumber GetLeastPacketAwaitedByPeer() const; - void SetNetworkChangeVisitor(NetworkChangeVisitor* visitor); bool InSlowStart() const; @@ -382,14 +380,11 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { // If true, use the new RTO with loss based CWND reduction instead of the send // algorithms's OnRetransmissionTimeout to reduce the congestion window. bool use_new_rto_; - // If true, cancel pending retransmissions if they're larger than - // largest_newly_acked. - bool undo_pending_retransmits_; // If true, use a more conservative handshake retransmission policy. bool conservative_handshake_retransmits_; // Vectors packets acked and lost as a result of the last congestion event. - SendAlgorithmInterface::CongestionVector packets_acked_; + SendAlgorithmInterface::AckedPacketVector packets_acked_; SendAlgorithmInterface::CongestionVector packets_lost_; // Largest newly acknowledged packet. QuicPacketNumber largest_newly_acked_; diff --git a/chromium/net/quic/core/quic_sent_packet_manager_test.cc b/chromium/net/quic/core/quic_sent_packet_manager_test.cc index 4a2beb31f44..eb25463bb6e 100644 --- a/chromium/net/quic/core/quic_sent_packet_manager_test.cc +++ b/chromium/net/quic/core/quic_sent_packet_manager_test.cc @@ -43,6 +43,10 @@ const size_t kMinTimeoutsBeforePathDegrading = 2; MATCHER(KeyEq, "") { return std::tr1::get<0>(arg).first == std::tr1::get<1>(arg); } +// Matcher to check that the packet number matches the second argument. +MATCHER(PacketNumberEq, "") { + return std::tr1::get<0>(arg).packet_number == std::tr1::get<1>(arg); +} class MockDebugDelegate : public QuicSentPacketManager::DebugDelegate { public: @@ -115,7 +119,9 @@ class QuicSentPacketManagerTest : public QuicTest { void ExpectAck(QuicPacketNumber largest_observed) { EXPECT_CALL( *send_algorithm_, - OnCongestionEvent(true, _, _, ElementsAre(Pair(largest_observed, _)), + // Ensure the AckedPacketVector argument contains largest_observed. + OnCongestionEvent(true, _, _, + Pointwise(PacketNumberEq(), {largest_observed}), IsEmpty())); EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); } @@ -129,10 +135,11 @@ class QuicSentPacketManagerTest : public QuicTest { void ExpectAckAndLoss(bool rtt_updated, QuicPacketNumber largest_observed, QuicPacketNumber lost_packet) { - EXPECT_CALL(*send_algorithm_, - OnCongestionEvent(rtt_updated, _, _, - ElementsAre(Pair(largest_observed, _)), - ElementsAre(Pair(lost_packet, _)))); + EXPECT_CALL( + *send_algorithm_, + OnCongestionEvent(rtt_updated, _, _, + Pointwise(PacketNumberEq(), {largest_observed}), + ElementsAre(Pair(lost_packet, _)))); EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); } @@ -150,10 +157,10 @@ class QuicSentPacketManagerTest : public QuicTest { for (size_t i = 0; i < num_packets_lost; ++i) { lost_vector.push_back(packets_lost[i]); } - EXPECT_CALL( - *send_algorithm_, - OnCongestionEvent(rtt_updated, _, _, Pointwise(KeyEq(), ack_vector), - Pointwise(KeyEq(), lost_vector))); + EXPECT_CALL(*send_algorithm_, + OnCongestionEvent(rtt_updated, _, _, + Pointwise(PacketNumberEq(), ack_vector), + Pointwise(KeyEq(), lost_vector))); EXPECT_CALL(*network_change_visitor_, OnCongestionChange()) .Times(AnyNumber()); } @@ -257,7 +264,7 @@ class QuicSentPacketManagerTest : public QuicTest { const QuicAckFrame InitAckFrame(QuicPacketNumber largest_observed) { QuicAckFrame frame(MakeAckFrame(largest_observed)); if (largest_observed > 0) { - frame.packets.Add(1, largest_observed + 1); + frame.packets.AddRange(1, largest_observed + 1); } return frame; } @@ -269,10 +276,10 @@ class QuicSentPacketManagerTest : public QuicTest { QuicPacketNumber range2_end) { QuicAckFrame ack_frame; if (range1_start < range1_end) { - ack_frame.packets.Add(range1_start, range1_end); + ack_frame.packets.AddRange(range1_start, range1_end); } if (range2_start <= range2_end) { - ack_frame.packets.Add(range2_start, range2_end + 1); + ack_frame.packets.AddRange(range2_start, range2_end + 1); } ack_frame.largest_observed = range2_end; return ack_frame; @@ -724,9 +731,11 @@ TEST_F(QuicSentPacketManagerTest, TailLossProbeThenRTO) { RetransmitNextPacket(103); QuicAckFrame ack_frame = ConstructAckFrame(1, 0, 103, 103); + QuicPacketNumber largest_acked = 103; EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); EXPECT_CALL(*send_algorithm_, - OnCongestionEvent(true, _, _, ElementsAre(Pair(103, _)), _)); + OnCongestionEvent( + true, _, _, Pointwise(PacketNumberEq(), {largest_acked}), _)); EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow()); // All packets before 103 should be lost. @@ -766,8 +775,8 @@ TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeout) { QuicPacketNumber acked[] = {3, 4, 5, 8, 9}; ExpectAcksAndLosses(true, acked, arraysize(acked), nullptr, 0); QuicAckFrame ack_frame; - ack_frame.packets.Add(3, 6); - ack_frame.packets.Add(8, 10); + ack_frame.packets.AddRange(3, 6); + ack_frame.packets.AddRange(8, 10); ack_frame.largest_observed = 9; manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow()); @@ -940,8 +949,10 @@ TEST_F(QuicSentPacketManagerTest, RetransmissionTimeout) { QuicAckFrame ack_frame = ConstructAckFrame(1, 0, 102, 102); ack_frame.ack_delay_time = QuicTime::Delta::Zero(); // Ensure no packets are lost. + QuicPacketNumber largest_acked = 102; EXPECT_CALL(*send_algorithm_, - OnCongestionEvent(true, _, _, ElementsAre(Pair(102, _)), + OnCongestionEvent(true, _, _, + Pointwise(PacketNumberEq(), {largest_acked}), /*lost_packets=*/IsEmpty())); EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true)); @@ -987,8 +998,10 @@ TEST_F(QuicSentPacketManagerTest, NewRetransmissionTimeout) { QuicAckFrame ack_frame = ConstructAckFrame(1, 0, 102, 102); ack_frame.ack_delay_time = QuicTime::Delta::Zero(); // This will include packets in the lost packet map. + QuicPacketNumber largest_acked = 102; EXPECT_CALL(*send_algorithm_, - OnCongestionEvent(true, _, _, ElementsAre(Pair(102, _)), + OnCongestionEvent(true, _, _, + Pointwise(PacketNumberEq(), {largest_acked}), /*lost_packets=*/Not(IsEmpty()))); EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); manager_.OnIncomingAck(ack_frame, clock_.Now()); @@ -1574,70 +1587,6 @@ TEST_F(QuicSentPacketManagerTest, NegotiateNewRTOFromOptionsAtClient) { EXPECT_TRUE(QuicSentPacketManagerPeer::GetUseNewRto(&manager_)); } -TEST_F(QuicSentPacketManagerTest, NegotiateUndoFromOptionsAtServer) { - EXPECT_FALSE(QuicSentPacketManagerPeer::GetUndoRetransmits(&manager_)); - QuicConfig config; - QuicTagVector options; - - options.push_back(kUNDO); - QuicConfigPeer::SetReceivedConnectionOptions(&config, options); - EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); - EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); - manager_.SetFromConfig(config); - EXPECT_TRUE(QuicSentPacketManagerPeer::GetUndoRetransmits(&manager_)); - - // Ensure undo works as intended. - // Send 5 packets, mark the first 4 for retransmission, and then cancel - // them when 1 is acked. - EXPECT_CALL(*send_algorithm_, PacingRate(_)) - .WillRepeatedly(Return(QuicBandwidth::Zero())); - EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) - .WillOnce(Return(10 * kDefaultTCPMSS)); - const size_t kNumSentPackets = 5; - for (size_t i = 1; i <= kNumSentPackets; ++i) { - SendDataPacket(i); - } - auto loss_algorithm = QuicMakeUnique<MockLossAlgorithm>(); - QuicSentPacketManagerPeer::SetLossAlgorithm(&manager_, loss_algorithm.get()); - EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); - EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); - SendAlgorithmInterface::CongestionVector lost_packets; - for (size_t i = 1; i < kNumSentPackets; ++i) { - lost_packets.push_back(std::make_pair(i, kMaxPacketSize)); - } - EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _)) - .WillOnce(SetArgPointee<4>(lost_packets)); - QuicAckFrame ack_frame = - ConstructAckFrame(1, 1, kNumSentPackets, kNumSentPackets); - // Congestion block the sending right before losing the packets. - EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _)) - .WillRepeatedly(Return(QuicTime::Delta::Infinite())); - manager_.OnIncomingAck(ack_frame, clock_.Now()); - EXPECT_TRUE(manager_.HasPendingRetransmissions()); - EXPECT_EQ(0u, BytesInFlight()); - - // Ack 1 and ensure the retransmissions are cancelled and put back in flight. - EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _)); - ack_frame = ConstructAckFrame(1, 2, kNumSentPackets, 5); - manager_.OnIncomingAck(ack_frame, clock_.Now()); - EXPECT_FALSE(manager_.HasPendingRetransmissions()); - EXPECT_EQ(3u * kDefaultLength, BytesInFlight()); -} - -TEST_F(QuicSentPacketManagerTest, NegotiateUndoFromOptionsAtClient) { - EXPECT_FALSE(QuicSentPacketManagerPeer::GetUndoRetransmits(&manager_)); - QuicConfig client_config; - QuicTagVector options; - - options.push_back(kUNDO); - QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); - client_config.SetConnectionOptionsToSend(options); - EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); - EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); - manager_.SetFromConfig(client_config); - EXPECT_TRUE(QuicSentPacketManagerPeer::GetUndoRetransmits(&manager_)); -} - TEST_F(QuicSentPacketManagerTest, UseInitialRoundTripTimeToSend) { uint32_t initial_rtt_us = 325000; EXPECT_NE(initial_rtt_us, @@ -1660,7 +1609,9 @@ TEST_F(QuicSentPacketManagerTest, ResumeConnectionState) { CachedNetworkParameters cached_network_params; cached_network_params.set_min_rtt_ms(kRttMs); - EXPECT_CALL(*send_algorithm_, ResumeConnectionState(_, false)); + EXPECT_CALL(*send_algorithm_, AdjustNetworkParameters( + QuicBandwidth::Zero(), + QuicTime::Delta::FromMilliseconds(kRttMs))); manager_.ResumeConnectionState(cached_network_params, false); EXPECT_EQ(kRttMs * kNumMicrosPerMilli, static_cast<uint64_t>(manager_.GetRttStats()->initial_rtt_us())); diff --git a/chromium/net/quic/core/quic_server_session_base.cc b/chromium/net/quic/core/quic_server_session_base.cc index 2524e5c1963..2a352ca844e 100644 --- a/chromium/net/quic/core/quic_server_session_base.cc +++ b/chromium/net/quic/core/quic_server_session_base.cc @@ -55,8 +55,7 @@ void QuicServerSessionBase::OnConfigNegotiated() { bandwidth_resumption_enabled_ = last_bandwidth_resumption || max_bandwidth_resumption; - if (!FLAGS_quic_reloadable_flag_quic_enable_server_push_by_default || - connection()->version() < QUIC_VERSION_35) { + if (connection()->version() < QUIC_VERSION_35) { set_server_push_enabled( ContainsQuicTag(config()->ReceivedConnectionOptions(), kSPSH)); } @@ -195,7 +194,6 @@ void QuicServerSessionBase::OnCongestionWindowChange(QuicTime now) { } bool QuicServerSessionBase::ShouldCreateIncomingDynamicStream(QuicStreamId id) { - DCHECK(!FLAGS_quic_reloadable_flag_quic_refactor_stream_creation); if (!connection()->connected()) { QUIC_BUG << "ShouldCreateIncomingDynamicStream called when disconnected"; return false; @@ -212,7 +210,6 @@ bool QuicServerSessionBase::ShouldCreateIncomingDynamicStream(QuicStreamId id) { } bool QuicServerSessionBase::ShouldCreateOutgoingDynamicStream() { - DCHECK(!FLAGS_quic_reloadable_flag_quic_refactor_stream_creation); if (!connection()->connected()) { QUIC_BUG << "ShouldCreateOutgoingDynamicStream called when disconnected"; return false; diff --git a/chromium/net/quic/core/quic_server_session_base_test.cc b/chromium/net/quic/core/quic_server_session_base_test.cc index 8c1eb8794f2..8182bd6f0b7 100644 --- a/chromium/net/quic/core/quic_server_session_base_test.cc +++ b/chromium/net/quic/core/quic_server_session_base_test.cc @@ -80,8 +80,6 @@ class TestServerSession : public QuicServerSessionBase { ~TestServerSession() override { delete connection(); }; protected: - // TODO(ckrasic) - for two below, remove when - // quic_reloadable_flag_quic_refactor_stream_creation is deprecated. QuicSpdyStream* CreateIncomingDynamicStream(QuicStreamId id) override { if (!ShouldCreateIncomingDynamicStream(id)) { return nullptr; @@ -104,10 +102,6 @@ class TestServerSession : public QuicServerSessionBase { return stream; } - std::unique_ptr<QuicStream> CreateStream(QuicStreamId id) override { - return QuicMakeUnique<QuicSimpleServerStream>(id, this, response_cache_); - } - QuicCryptoServerStreamBase* CreateQuicCryptoServerStream( const QuicCryptoServerConfig* crypto_config, QuicCompressedCertsCache* compressed_certs_cache) override { @@ -198,17 +192,6 @@ MATCHER_P(EqualsProto, network_params, "") { INSTANTIATE_TEST_CASE_P(Tests, QuicServerSessionBaseTest, ::testing::ValuesIn(AllSupportedVersions())); -TEST_P(QuicServerSessionBaseTest, ServerPushDisabledByDefault) { - FLAGS_quic_reloadable_flag_quic_enable_server_push_by_default = true; - // Without the client explicitly sending kSPSH, server push will be disabled - // at the server, until version 35 when it is enabled by default. - EXPECT_FALSE( - session_->config()->HasReceivedConnectionOptions() && - ContainsQuicTag(session_->config()->ReceivedConnectionOptions(), kSPSH)); - session_->OnConfigNegotiated(); - EXPECT_TRUE(session_->server_push_enabled()); -} - TEST_P(QuicServerSessionBaseTest, CloseStreamDueToReset) { // Open a stream, then reset it. // Send two bytes of payload to open it. @@ -358,19 +341,6 @@ TEST_P(QuicServerSessionBaseTest, MaxAvailableStreams) { session_.get(), kLimitingStreamId + 2 * next_id)); } -// TODO(ckrasic): remove this when -// FLAGS_quic_reloadable_flag_quic_enable_server_push_by_default is -// deprecated. -TEST_P(QuicServerSessionBaseTest, EnableServerPushThroughConnectionOption) { - FLAGS_quic_reloadable_flag_quic_enable_server_push_by_default = false; - // Assume server received server push connection option. - QuicTagVector copt; - copt.push_back(kSPSH); - QuicConfigPeer::SetReceivedConnectionOptions(session_->config(), copt); - session_->OnConfigNegotiated(); - EXPECT_TRUE(session_->server_push_enabled()); -} - TEST_P(QuicServerSessionBaseTest, GetEvenIncomingError) { // Incoming streams on the server session must be odd. EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _)); @@ -381,15 +351,9 @@ TEST_P(QuicServerSessionBaseTest, GetEvenIncomingError) { TEST_P(QuicServerSessionBaseTest, GetStreamDisconnected) { // Don't create new streams if the connection is disconnected. QuicConnectionPeer::TearDownLocalConnectionState(connection_); - if (FLAGS_quic_reloadable_flag_quic_refactor_stream_creation) { - EXPECT_EQ(nullptr, QuicServerSessionBasePeer::GetOrCreateDynamicStream( - session_.get(), GetNthClientInitiatedId(0))); - } else { - EXPECT_QUIC_BUG( - QuicServerSessionBasePeer::GetOrCreateDynamicStream( - session_.get(), GetNthClientInitiatedId(0)), - "ShouldCreateIncomingDynamicStream called when disconnected"); - } + EXPECT_QUIC_BUG(QuicServerSessionBasePeer::GetOrCreateDynamicStream( + session_.get(), GetNthClientInitiatedId(0)), + "ShouldCreateIncomingDynamicStream called when disconnected"); } class MockQuicCryptoServerStream : public QuicCryptoServerStream { diff --git a/chromium/net/quic/core/quic_session.cc b/chromium/net/quic/core/quic_session.cc index 4f7a0ab45b6..f2da8007c26 100644 --- a/chromium/net/quic/core/quic_session.cc +++ b/chromium/net/quic/core/quic_session.cc @@ -10,6 +10,7 @@ #include "net/quic/core/quic_connection.h" #include "net/quic/core/quic_flow_controller.h" #include "net/quic/platform/api/quic_bug_tracker.h" +#include "net/quic/platform/api/quic_flag_utils.h" #include "net/quic/platform/api/quic_flags.h" #include "net/quic/platform/api/quic_logging.h" #include "net/quic/platform/api/quic_map_util.h" @@ -45,18 +46,18 @@ QuicSession::QuicSession(QuicConnection* connection, perspective() == Perspective::IS_SERVER, nullptr), currently_writing_stream_id_(0), - respect_goaway_(true), use_stream_notifier_( FLAGS_quic_reloadable_flag_quic_use_stream_notifier2), - streams_own_data_(use_stream_notifier_ && - FLAGS_quic_reloadable_flag_quic_stream_owns_data) {} + save_data_before_consumption_( + use_stream_notifier_ && + FLAGS_quic_reloadable_flag_quic_save_data_before_consumption2) {} void QuicSession::Initialize() { connection_->set_visitor(this); if (use_stream_notifier_) { connection_->SetStreamNotifier(this); } - if (streams_own_data_) { + if (save_data_before_consumption_) { connection_->SetDataProducer(this); } connection_->SetFromConfig(config_); @@ -743,11 +744,6 @@ QuicStream* QuicSession::GetOrCreateDynamicStream( if (!MaybeIncreaseLargestPeerStreamId(stream_id)) { return nullptr; } - - if (FLAGS_quic_reloadable_flag_quic_refactor_stream_creation) { - return MaybeCreateIncomingDynamicStream(stream_id); - } - // Check if the new number of open streams would cause the number of // open streams to exceed the limit. if (GetNumOpenIncomingStreams() >= max_open_incoming_streams()) { @@ -894,78 +890,6 @@ bool QuicSession::IsIncomingStream(QuicStreamId id) const { return id % 2 != next_outgoing_stream_id_ % 2; } -bool QuicSession::ShouldCreateIncomingDynamicStream2(QuicStreamId id) { - DCHECK(FLAGS_quic_reloadable_flag_quic_refactor_stream_creation); - if (goaway_received() && respect_goaway_) { - QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. " - << "Already received goaway."; - return false; - } - if (!IsIncomingStream(id)) { - QUIC_DLOG(INFO) << "invalid incoming stream id: " << id; - return false; - } - if (!connection()->connected()) { - QUIC_DLOG(INFO) - << "ShouldCreateIncomingDynamicStream called when disconnected"; - return false; - } - if (GetNumOpenIncomingStreams() >= max_open_incoming_streams()) { - DVLOG(1) << "Reset stream (refused) " << id; - SendRstStream(id, QUIC_REFUSED_STREAM, 0); - return false; - } - - return true; -} - -bool QuicSession::ShouldCreateOutgoingDynamicStream2() { - DCHECK(FLAGS_quic_reloadable_flag_quic_refactor_stream_creation); - if (!connection()->connected()) { - QUIC_DLOG(INFO) - << "ShouldCreateOutgoingDynamicStream called when disconnected"; - return false; - } - if (!IsEncryptionEstablished()) { - QUIC_DLOG(INFO) << "Encryption not established so no outgoing stream " - << "created."; - return false; - } - if (goaway_received() && respect_goaway_) { - QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. " - << "Already received goaway."; - return false; - } - if (GetNumOpenOutgoingStreams() >= max_open_outgoing_streams()) { - QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. " - << "Already " << GetNumOpenOutgoingStreams() << " open."; - return false; - } - return true; -} - -QuicStream* QuicSession::MaybeCreateIncomingDynamicStream(QuicStreamId id) { - if (!ShouldCreateIncomingDynamicStream2(id)) { - return nullptr; - } - return CreateAndActivateStream(id); -} - -QuicStream* QuicSession::MaybeCreateOutgoingDynamicStream( - SpdyPriority priority) { - if (!ShouldCreateOutgoingDynamicStream2()) { - return nullptr; - } - return CreateAndActivateStream(GetNextOutgoingStreamId()); -} - -QuicStream* QuicSession::CreateAndActivateStream(QuicStreamId id) { - std::unique_ptr<QuicStream> stream = CreateStream(id); - QuicStream* stream_ptr = stream.get(); - ActivateStream(std::move(stream)); - return stream_ptr; -} - void QuicSession::OnStreamDoneWaitingForAcks(QuicStreamId id) { auto it = zombie_streams_.find(id); if (it == zombie_streams_.end()) { @@ -1024,25 +948,10 @@ void QuicSession::OnStreamFrameDiscarded(const QuicStreamFrame& frame) { ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return; } + QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_use_stream_notifier2, 3, 3); stream->OnStreamFrameDiscarded(frame); } -void QuicSession::SaveStreamData(QuicStreamId id, - QuicIOVector iov, - size_t iov_offset, - QuicStreamOffset offset, - QuicByteCount data_length) { - QuicStream* stream = GetStream(id); - if (stream == nullptr) { - QUIC_BUG << "Stream " << id << " does not exist when trying to save data."; - connection()->CloseConnection( - QUIC_INTERNAL_ERROR, "Attempt to save data of a closed stream", - ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); - return; - } - stream->SaveStreamData(iov, iov_offset, offset, data_length); -} - bool QuicSession::WriteStreamData(QuicStreamId id, QuicStreamOffset offset, QuicByteCount data_length, diff --git a/chromium/net/quic/core/quic_session.h b/chromium/net/quic/core/quic_session.h index e52b21275c2..b95ea3635c8 100644 --- a/chromium/net/quic/core/quic_session.h +++ b/chromium/net/quic/core/quic_session.h @@ -110,12 +110,7 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, bool HasOpenDynamicStreams() const override; void OnPathDegrading() override; - // QuicStreamFrameDataProducer methods: - void SaveStreamData(QuicStreamId id, - QuicIOVector iov, - size_t iov_offset, - QuicStreamOffset offset, - QuicByteCount data_length) override; + // QuicStreamFrameDataProducer bool WriteStreamData(QuicStreamId id, QuicStreamOffset offset, QuicByteCount data_length, @@ -202,6 +197,9 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, const QuicSocketAddress& peer_address() const { return connection_->peer_address(); } + const QuicSocketAddress& self_address() const { + return connection_->self_address(); + } QuicConnectionId connection_id() const { return connection_->connection_id(); } @@ -275,13 +273,11 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, // Returns true if this stream should yield writes to another blocked stream. bool ShouldYield(QuicStreamId stream_id); - void set_respect_goaway(bool respect_goaway) { - respect_goaway_ = respect_goaway; - } - bool use_stream_notifier() const { return use_stream_notifier_; } - bool streams_own_data() const { return streams_own_data_; } + bool save_data_before_consumption() const { + return save_data_before_consumption_; + } protected: using StaticStreamMap = QuicSmallMap<QuicStreamId, QuicStream*, 2>; @@ -294,28 +290,6 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, using ZombieStreamMap = QuicSmallMap<QuicStreamId, std::unique_ptr<QuicStream>, 10>; - // TODO(ckrasic) - For all *DynamicStream2 below, rename after - // quic_reloadable_flag_quic_refactor_stream_creation is deprecated. - - // Returns true if an incoming stream can be created. - virtual bool ShouldCreateIncomingDynamicStream2(QuicStreamId id); - - // Returns true if an outgoing stream can be created. - virtual bool ShouldCreateOutgoingDynamicStream2(); - - // Creates a new stream to handle a peer-initiated stream. - // Caller does not own the returned stream. - // Returns nullptr and does error handling if the stream can not be created. - virtual QuicStream* MaybeCreateIncomingDynamicStream(QuicStreamId id); - - // Create a new stream to handle a locally-initiated stream. - // Caller does not own the returned stream. - // Returns nullptr if max streams have already been opened. - virtual QuicStream* MaybeCreateOutgoingDynamicStream(SpdyPriority priority); - - // TODO(ckrasic) - For all Create*DynamicStream below, remove when - // quic_reloadable_flag_quic_refactor_stream_creation is deprecated. - // Creates a new stream to handle a peer-initiated stream. // Caller does not own the returned stream. // Returns nullptr and does error handling if the stream can not be created. @@ -361,14 +335,6 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, // Return true if given stream is peer initiated. bool IsIncomingStream(QuicStreamId id) const; - // Unconditionally creates a stream. Subclasses should use this to - // provide streams appropriately subclassed from |QuicStream|, - // e.g. |QuicSpdySession::CreateStream()| creates a |QuicSpdyStream|. - virtual std::unique_ptr<QuicStream> CreateStream(QuicStreamId id) = 0; - - // Creates a stream and activates it, owned by the session. - QuicStream* CreateAndActivateStream(QuicStreamId id); - StaticStreamMap& static_streams() { return static_stream_map_; } const StaticStreamMap& static_streams() const { return static_stream_map_; } @@ -427,8 +393,6 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, virtual void HandleRstOnValidNonexistentStream( const QuicRstStreamFrame& frame); - bool respect_goaway() const { return respect_goaway_; } - private: friend class test::QuicSessionPeer; @@ -522,17 +486,11 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, // call stack of OnCanWrite. QuicStreamId currently_writing_stream_id_; - // If this is set to false, the session will ignore peer GOAWAYs and - // allow the creation of outgoing streams regardless of the high - // chance they will fail. - bool respect_goaway_; - // This session is notified on every ack or loss. const bool use_stream_notifier_; - // Streams of this session own their outstanding data. Outstanding data here - // means sent data waiting to be acked. - const bool streams_own_data_; + // Application data is saved before it is actually consumed. + const bool save_data_before_consumption_; DISALLOW_COPY_AND_ASSIGN(QuicSession); }; diff --git a/chromium/net/quic/core/quic_session_test.cc b/chromium/net/quic/core/quic_session_test.cc index fd1c15ee44a..82b7b9629ee 100644 --- a/chromium/net/quic/core/quic_session_test.cc +++ b/chromium/net/quic/core/quic_session_test.cc @@ -77,10 +77,6 @@ class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker { session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); } - void set_encryption_established(bool value) { - encryption_established_ = value; - } - // QuicCryptoStream implementation bool encryption_established() const override { return encryption_established_; @@ -188,15 +184,6 @@ class TestSession : public QuicSpdySession { bool ShouldCreateOutgoingDynamicStream() override { return true; } - TestStream* MaybeCreateOutgoingDynamicStream(SpdyPriority priority) override { - return static_cast<TestStream*>( - QuicSpdySession::MaybeCreateOutgoingDynamicStream(priority)); - } - - std::unique_ptr<QuicStream> CreateStream(QuicStreamId id) override { - return QuicMakeUnique<TestStream>(id, this); - } - bool IsClosedStream(QuicStreamId id) { return QuicSession::IsClosedStream(id); } @@ -219,8 +206,6 @@ class TestSession : public QuicSpdySession { consumed = QuicSession::WritevData(stream, id, data, offset, state, std::move(ack_listener)); } - stream->set_stream_bytes_written(stream->stream_bytes_written() + - consumed.bytes_consumed); if (fin && consumed.fin_consumed) { stream->set_fin_sent(true); } @@ -238,6 +223,10 @@ class TestSession : public QuicSpdySession { if (stream->id() != kCryptoStreamId) { this->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); } + if (save_data_before_consumption()) { + QuicStreamPeer::SendBuffer(stream).SaveStreamData( + MakeIOVector("not empty", &iov), 0, 9); + } QuicConsumedData consumed = WritevData( stream, stream->id(), MakeIOVector("not empty", &iov), 0, FIN, nullptr); return consumed; @@ -303,9 +292,6 @@ class QuicSessionTestBase : public QuicTestWithParam<QuicVersion> { "EFFlEYHsBQ98rXImL8ySDycdLEFvBPdtctPmWCfTxwmoSMLHU2SCVDhbqMWU5b0yr" "JBCScs_ejbKaqBDoB7ZGxTvqlrB__2ZmnHHjCr8RgMRtKNtIeuZAo "; connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); - if (FLAGS_quic_reloadable_flag_quic_refactor_stream_creation) { - session_.GetMutableCryptoStream()->set_encryption_established(true); - } } void CheckClosedStreams() { @@ -358,6 +344,10 @@ TEST_P(QuicSessionTestServer, PeerAddress) { session_.peer_address()); } +TEST_P(QuicSessionTestServer, SelfAddress) { + EXPECT_EQ(QuicSocketAddress(), session_.self_address()); +} + TEST_P(QuicSessionTestServer, IsCryptoHandshakeConfirmed) { EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed()); CryptoHandshakeMessage message; diff --git a/chromium/net/quic/core/quic_client_session_base.cc b/chromium/net/quic/core/quic_spdy_client_session_base.cc index b13496d73d7..a1fd538cec4 100644 --- a/chromium/net/quic/core/quic_client_session_base.cc +++ b/chromium/net/quic/core/quic_spdy_client_session_base.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "net/quic/core/quic_client_session_base.h" +#include "net/quic/core/quic_spdy_client_session_base.h" #include "net/quic/core/quic_client_promised_info.h" #include "net/quic/core/spdy_utils.h" @@ -13,7 +13,7 @@ using std::string; namespace net { -QuicClientSessionBase::QuicClientSessionBase( +QuicSpdyClientSessionBase::QuicSpdyClientSessionBase( QuicConnection* connection, QuicClientPushPromiseIndex* push_promise_index, const QuicConfig& config) @@ -21,7 +21,7 @@ QuicClientSessionBase::QuicClientSessionBase( push_promise_index_(push_promise_index), largest_promised_stream_id_(kInvalidStreamId) {} -QuicClientSessionBase::~QuicClientSessionBase() { +QuicSpdyClientSessionBase::~QuicSpdyClientSessionBase() { // all promised streams for this session for (auto& it : promised_by_id_) { QUIC_DVLOG(1) << "erase stream " << it.first << " url " << it.second->url(); @@ -30,15 +30,16 @@ QuicClientSessionBase::~QuicClientSessionBase() { delete connection(); } -void QuicClientSessionBase::OnConfigNegotiated() { +void QuicSpdyClientSessionBase::OnConfigNegotiated() { QuicSpdySession::OnConfigNegotiated(); } -void QuicClientSessionBase::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { +void QuicSpdyClientSessionBase::OnCryptoHandshakeEvent( + CryptoHandshakeEvent event) { QuicSpdySession::OnCryptoHandshakeEvent(event); } -void QuicClientSessionBase::OnInitialHeadersComplete( +void QuicSpdyClientSessionBase::OnInitialHeadersComplete( QuicStreamId stream_id, const SpdyHeaderBlock& response_headers) { // Note that the strong ordering of the headers stream means that @@ -53,7 +54,7 @@ void QuicClientSessionBase::OnInitialHeadersComplete( promised->OnResponseHeaders(response_headers); } -void QuicClientSessionBase::OnPromiseHeaderList( +void QuicSpdyClientSessionBase::OnPromiseHeaderList( QuicStreamId stream_id, QuicStreamId promised_stream_id, size_t frame_len, @@ -77,9 +78,9 @@ void QuicClientSessionBase::OnPromiseHeaderList( stream->OnPromiseHeaderList(promised_stream_id, frame_len, header_list); } -bool QuicClientSessionBase::HandlePromised(QuicStreamId /* associated_id */, - QuicStreamId promised_id, - const SpdyHeaderBlock& headers) { +bool QuicSpdyClientSessionBase::HandlePromised(QuicStreamId /* associated_id */, + QuicStreamId promised_id, + const SpdyHeaderBlock& headers) { // Due to pathalogical packet re-ordering, it is possible that // frames for the promised stream have already arrived, and the // promised stream could be active or closed. @@ -126,7 +127,7 @@ bool QuicClientSessionBase::HandlePromised(QuicStreamId /* associated_id */, return true; } -QuicClientPromisedInfo* QuicClientSessionBase::GetPromisedByUrl( +QuicClientPromisedInfo* QuicSpdyClientSessionBase::GetPromisedByUrl( const string& url) { QuicPromisedByUrlMap::iterator it = push_promise_index_->promised_by_url()->find(url); @@ -136,7 +137,7 @@ QuicClientPromisedInfo* QuicClientSessionBase::GetPromisedByUrl( return nullptr; } -QuicClientPromisedInfo* QuicClientSessionBase::GetPromisedById( +QuicClientPromisedInfo* QuicSpdyClientSessionBase::GetPromisedById( const QuicStreamId id) { QuicPromisedByIdMap::iterator it = promised_by_id_.find(id); if (it != promised_by_id_.end()) { @@ -145,7 +146,7 @@ QuicClientPromisedInfo* QuicClientSessionBase::GetPromisedById( return nullptr; } -QuicSpdyStream* QuicClientSessionBase::GetPromisedStream( +QuicSpdyStream* QuicSpdyClientSessionBase::GetPromisedStream( const QuicStreamId id) { DynamicStreamMap::iterator it = dynamic_streams().find(id); if (it != dynamic_streams().end()) { @@ -154,7 +155,8 @@ QuicSpdyStream* QuicClientSessionBase::GetPromisedStream( return nullptr; } -void QuicClientSessionBase::DeletePromised(QuicClientPromisedInfo* promised) { +void QuicSpdyClientSessionBase::DeletePromised( + QuicClientPromisedInfo* promised) { push_promise_index_->promised_by_url()->erase(promised->url()); // Since promised_by_id_ contains the unique_ptr, this will destroy // promised. @@ -162,23 +164,24 @@ void QuicClientSessionBase::DeletePromised(QuicClientPromisedInfo* promised) { headers_stream()->MaybeReleaseSequencerBuffer(); } -void QuicClientSessionBase::OnPushStreamTimedOut(QuicStreamId stream_id) {} +void QuicSpdyClientSessionBase::OnPushStreamTimedOut(QuicStreamId stream_id) {} -void QuicClientSessionBase::ResetPromised(QuicStreamId id, - QuicRstStreamErrorCode error_code) { +void QuicSpdyClientSessionBase::ResetPromised( + QuicStreamId id, + QuicRstStreamErrorCode error_code) { SendRstStream(id, error_code, 0); if (!IsOpenStream(id)) { MaybeIncreaseLargestPeerStreamId(id); } } -void QuicClientSessionBase::CloseStreamInner(QuicStreamId stream_id, - bool locally_reset) { +void QuicSpdyClientSessionBase::CloseStreamInner(QuicStreamId stream_id, + bool locally_reset) { QuicSpdySession::CloseStreamInner(stream_id, locally_reset); headers_stream()->MaybeReleaseSequencerBuffer(); } -bool QuicClientSessionBase::ShouldReleaseHeadersStreamSequencerBuffer() { +bool QuicSpdyClientSessionBase::ShouldReleaseHeadersStreamSequencerBuffer() { return num_active_requests() == 0 && promised_by_id_.empty(); } diff --git a/chromium/net/quic/core/quic_client_session_base.h b/chromium/net/quic/core/quic_spdy_client_session_base.h index 8e6c1a6d670..696be5edf68 100644 --- a/chromium/net/quic/core/quic_client_session_base.h +++ b/chromium/net/quic/core/quic_spdy_client_session_base.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef NET_QUIC_CORE_QUIC_CLIENT_SESSION_BASE_H_ -#define NET_QUIC_CORE_QUIC_CLIENT_SESSION_BASE_H_ +#ifndef NET_QUIC_CORE_QUIC_SPDY_CLIENT_SESSION_BASE_H_ +#define NET_QUIC_CORE_QUIC_SPDY_CLIENT_SESSION_BASE_H_ #include <string> @@ -33,17 +33,17 @@ using QuicPromisedByUrlMap = const int64_t kPushPromiseTimeoutSecs = 60; // Base class for all client-specific QuicSession subclasses. -class QUIC_EXPORT_PRIVATE QuicClientSessionBase +class QUIC_EXPORT_PRIVATE QuicSpdyClientSessionBase : public QuicSpdySession, public QuicCryptoClientStream::ProofHandler { public: // Takes ownership of |connection|. Caller retains ownership of // |promised_by_url|. - QuicClientSessionBase(QuicConnection* connection, - QuicClientPushPromiseIndex* push_promise_index, - const QuicConfig& config); + QuicSpdyClientSessionBase(QuicConnection* connection, + QuicClientPushPromiseIndex* push_promise_index, + const QuicConfig& config); - ~QuicClientSessionBase() override; + ~QuicSpdyClientSessionBase() override; void OnConfigNegotiated() override; @@ -131,9 +131,9 @@ class QUIC_EXPORT_PRIVATE QuicClientSessionBase QuicPromisedByIdMap promised_by_id_; QuicStreamId largest_promised_stream_id_; - DISALLOW_COPY_AND_ASSIGN(QuicClientSessionBase); + DISALLOW_COPY_AND_ASSIGN(QuicSpdyClientSessionBase); }; } // namespace net -#endif // NET_QUIC_CORE_QUIC_CLIENT_SESSION_BASE_H_ +#endif // NET_QUIC_CORE_QUIC_SPDY_CLIENT_SESSION_BASE_H_ diff --git a/chromium/net/quic/core/quic_spdy_session.cc b/chromium/net/quic/core/quic_spdy_session.cc index 95369471f29..57e5d0de38f 100644 --- a/chromium/net/quic/core/quic_spdy_session.cc +++ b/chromium/net/quic/core/quic_spdy_session.cc @@ -16,6 +16,7 @@ #include "net/quic/platform/api/quic_logging.h" #include "net/quic/platform/api/quic_str_cat.h" #include "net/quic/platform/api/quic_text_utils.h" +#include "net/spdy/core/http2_frame_decoder_adapter.h" using std::string; @@ -137,19 +138,19 @@ class QuicSpdySession::SpdyFramerVisitor QUIC_INVALID_HEADERS_STREAM_DATA); } - void OnError(SpdyFramer* framer) override { + void OnError(Http2DecoderAdapter::SpdyFramerError error) override { QuicErrorCode code = QUIC_INVALID_HEADERS_STREAM_DATA; - SpdyFramer::SpdyFramerError error = framer->spdy_framer_error(); switch (error) { - case SpdyFramer::SpdyFramerError::SPDY_DECOMPRESS_FAILURE: + case Http2DecoderAdapter::SpdyFramerError::SPDY_DECOMPRESS_FAILURE: code = QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE; break; default: break; } - CloseConnection(QuicStrCat("SPDY framing error: ", - SpdyFramer::SpdyFramerErrorToString(error)), - code); + CloseConnection( + QuicStrCat("SPDY framing error: ", + Http2DecoderAdapter::SpdyFramerErrorToString(error)), + code); } void OnDataFrameHeader(SpdyStreamId stream_id, @@ -178,8 +179,7 @@ class QuicSpdySession::SpdyFramerVisitor session_->UpdateHeaderEncoderTableSize(value); break; case SETTINGS_ENABLE_PUSH: - if (FLAGS_quic_reloadable_flag_quic_enable_server_push_by_default && - session_->perspective() == Perspective::IS_SERVER) { + if (session_->perspective() == Perspective::IS_SERVER) { // See rfc7540, Section 6.5.2. if (value > 1) { CloseConnection( @@ -309,8 +309,7 @@ class QuicSpdySession::SpdyFramerVisitor void set_max_uncompressed_header_bytes( size_t set_max_uncompressed_header_bytes) { - header_list_.set_max_uncompressed_header_bytes( - set_max_uncompressed_header_bytes); + header_list_.set_max_header_list_size(set_max_uncompressed_header_bytes); } private: @@ -335,8 +334,9 @@ QuicSpdySession::QuicSpdySession(QuicConnection* connection, QuicSession::Visitor* visitor, const QuicConfig& config) : QuicSession(connection, visitor, config), + max_inbound_header_list_size_(kDefaultMaxUncompressedHeaderSize), force_hol_blocking_(false), - server_push_enabled_(false), + server_push_enabled_(true), stream_id_(kInvalidStreamId), promised_stream_id_(kInvalidStreamId), fin_(false), @@ -347,8 +347,8 @@ QuicSpdySession::QuicSpdySession(QuicConnection* connection, prev_max_timestamp_(QuicTime::Zero()), spdy_framer_(SpdyFramer::ENABLE_COMPRESSION), spdy_framer_visitor_(new SpdyFramerVisitor(this)) { - spdy_framer_.set_visitor(spdy_framer_visitor_.get()); - spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get()); + h2_deframer_.set_visitor(spdy_framer_visitor_.get()); + h2_deframer_.set_debug_visitor(spdy_framer_visitor_.get()); } QuicSpdySession::~QuicSpdySession() { @@ -378,6 +378,14 @@ void QuicSpdySession::Initialize() { headers_stream_.reset(new QuicHeadersStream(this)); DCHECK_EQ(kHeadersStreamId, headers_stream_->id()); static_streams()[kHeadersStreamId] = headers_stream_.get(); + + if (FLAGS_quic_restart_flag_quic_header_list_size) { + QUIC_FLAG_COUNT_N(quic_restart_flag_quic_header_list_size, 1, 2); + set_max_uncompressed_header_bytes(max_inbound_header_list_size_); + + // Limit HPACK buffering to 2x header list size limit. + set_max_decode_buffer_size_bytes(2 * max_inbound_header_list_size_); + } } void QuicSpdySession::OnStreamHeadersPriority(QuicStreamId stream_id, @@ -396,31 +404,23 @@ void QuicSpdySession::OnStreamHeaderList(QuicStreamId stream_id, const QuicHeaderList& header_list) { QuicSpdyStream* stream = GetSpdyDataStream(stream_id); if (stream == nullptr) { - QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_final_offset_from_trailers, 1, - 3); - if (FLAGS_quic_reloadable_flag_quic_final_offset_from_trailers) { - QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_final_offset_from_trailers, 2, - 3); - // The stream no longer exists, but trailing headers may contain the final - // byte offset necessary for flow control and open stream accounting. - size_t final_byte_offset = 0; - for (const auto& header : header_list) { - const string& header_key = header.first; - const string& header_value = header.second; - if (header_key == kFinalOffsetHeaderKey) { - if (!QuicTextUtils::StringToSizeT(header_value, &final_byte_offset)) { - connection()->CloseConnection( - QUIC_INVALID_HEADERS_STREAM_DATA, - "Trailers are malformed (no final offset)", - ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); - return; - } - DVLOG(1) << "Received final byte offset in trailers for stream " - << stream_id << ", which no longer exists."; - OnFinalByteOffsetReceived(stream_id, final_byte_offset); - QUIC_FLAG_COUNT_N( - quic_reloadable_flag_quic_final_offset_from_trailers, 3, 3); + // The stream no longer exists, but trailing headers may contain the final + // byte offset necessary for flow control and open stream accounting. + size_t final_byte_offset = 0; + for (const auto& header : header_list) { + const string& header_key = header.first; + const string& header_value = header.second; + if (header_key == kFinalOffsetHeaderKey) { + if (!QuicTextUtils::StringToSizeT(header_value, &final_byte_offset)) { + connection()->CloseConnection( + QUIC_INVALID_HEADERS_STREAM_DATA, + "Trailers are malformed (no final offset)", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; } + DVLOG(1) << "Received final byte offset in trailers for stream " + << stream_id << ", which no longer exists."; + OnFinalByteOffsetReceived(stream_id, final_byte_offset); } } @@ -434,7 +434,7 @@ size_t QuicSpdySession::ProcessHeaderData(const struct iovec& iov, QuicTime timestamp) { DCHECK(timestamp.IsInitialized()); UpdateCurMaxTimeStamp(timestamp); - return spdy_framer_.ProcessInput(static_cast<char*>(iov.iov_base), + return h2_deframer_.ProcessInput(static_cast<char*>(iov.iov_base), iov.iov_len); } @@ -605,7 +605,11 @@ void QuicSpdySession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { QuicSession::OnCryptoHandshakeEvent(event); if (FLAGS_quic_reloadable_flag_quic_send_max_header_list_size && event == HANDSHAKE_CONFIRMED && config()->SupportMaxHeaderListSize()) { - SendMaxHeaderListSize(kDefaultMaxUncompressedHeaderSize); + if (FLAGS_quic_restart_flag_quic_header_list_size) { + SendMaxHeaderListSize(max_inbound_header_list_size_); + } else { + SendMaxHeaderListSize(kDefaultMaxUncompressedHeaderSize); + } } } @@ -613,7 +617,7 @@ void QuicSpdySession::OnPromiseHeaderList(QuicStreamId stream_id, QuicStreamId promised_stream_id, size_t frame_len, const QuicHeaderList& header_list) { - string error = "OnPromiseHeaderList should be overriden in client code."; + string error = "OnPromiseHeaderList should be overridden in client code."; QUIC_BUG << error; connection()->CloseConnection(QUIC_INTERNAL_ERROR, error, ConnectionCloseBehavior::SILENT_CLOSE); @@ -626,7 +630,6 @@ void QuicSpdySession::OnConfigNegotiated() { } const QuicVersion version = connection()->version(); if (!use_stream_notifier() && - FLAGS_quic_reloadable_flag_quic_enable_force_hol_blocking && version == QUIC_VERSION_36 && config()->ForceHolBlocking(perspective())) { force_hol_blocking_ = true; // Since all streams are tunneled through the headers stream, it @@ -639,9 +642,6 @@ void QuicSpdySession::OnConfigNegotiated() { headers_stream_->flow_controller()->UpdateSendWindowOffset( kStreamReceiveWindowLimit); } - - server_push_enabled_ = - FLAGS_quic_reloadable_flag_quic_enable_server_push_by_default; } void QuicSpdySession::OnStreamFrameData(QuicStreamId stream_id, @@ -745,7 +745,7 @@ void QuicSpdySession::SetHpackEncoderDebugVisitor( void QuicSpdySession::SetHpackDecoderDebugVisitor( std::unique_ptr<QuicHpackDebugVisitor> visitor) { - spdy_framer_.SetDecoderHeaderTableDebugVisitor( + h2_deframer_.SetDecoderHeaderTableDebugVisitor( std::unique_ptr<HeaderTableDebugVisitor>(new HeaderTableDebugVisitor( connection()->helper()->GetClock(), std::move(visitor)))); } @@ -804,20 +804,4 @@ void QuicSpdySession::CloseConnectionWithDetails(QuicErrorCode error, error, details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); } -QuicSpdyStream* QuicSpdySession::MaybeCreateIncomingDynamicStream( - QuicStreamId id) { - return static_cast<QuicSpdyStream*>( - QuicSession::MaybeCreateIncomingDynamicStream(id)); -} - -QuicSpdyStream* QuicSpdySession::MaybeCreateOutgoingDynamicStream( - SpdyPriority priority) { - auto* stream = static_cast<QuicSpdyStream*>( - QuicSession::MaybeCreateOutgoingDynamicStream(priority)); - if (stream) { - stream->SetPriority(priority); - } - return stream; -} - } // namespace net diff --git a/chromium/net/quic/core/quic_spdy_session.h b/chromium/net/quic/core/quic_spdy_session.h index 17115a76139..1855d4a99c9 100644 --- a/chromium/net/quic/core/quic_spdy_session.h +++ b/chromium/net/quic/core/quic_spdy_session.h @@ -15,6 +15,7 @@ #include "net/quic/core/quic_spdy_stream.h" #include "net/quic/platform/api/quic_export.h" #include "net/quic/platform/api/quic_string_piece.h" +#include "net/spdy/core/http2_frame_decoder_adapter.h" namespace net { @@ -76,7 +77,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession : public QuicSession { size_t frame_len, const QuicHeaderList& header_list); - // Sends contents of |iov| to spdy_framer_, returns number of bytes processd. + // Sends contents of |iov| to h2_deframer_, returns number of bytes processed. size_t ProcessHeaderData(const struct iovec& iov, QuicTime timestamp); // Writes |headers| for the stream |id| to the dedicated headers stream. @@ -148,24 +149,26 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession : public QuicSession { // aggressively. virtual bool ShouldReleaseHeadersStreamSequencerBuffer(); - SpdyFramer* spdy_framer() { return &spdy_framer_; } - void CloseConnectionWithDetails(QuicErrorCode error, const std::string& details); // Sets how much encoded data the hpack decoder of spdy_framer_ is willing to // buffer. void set_max_decode_buffer_size_bytes(size_t max_decode_buffer_size_bytes) { - spdy_framer_.set_max_decode_buffer_size_bytes(max_decode_buffer_size_bytes); + h2_deframer_.GetHpackDecoder()->set_max_decode_buffer_size_bytes( + max_decode_buffer_size_bytes); } + // TODO(dahollings): Move to private upon deprecation of + // --quic_restart_flag_quic_header_list_size. void set_max_uncompressed_header_bytes( size_t set_max_uncompressed_header_bytes); + void set_max_inbound_header_list_size(size_t max_inbound_header_list_size) { + max_inbound_header_list_size_ = max_inbound_header_list_size; + } + protected: - // TODO(ckrasic) - remove these two when - // FLAGS_quic_reloadable_flag_quic_refactor_stream_creation is - // deprecated. // Override CreateIncomingDynamicStream() and CreateOutgoingDynamicStream() // with QuicSpdyStream return type to make sure that all data streams are // QuicSpdyStreams. @@ -173,16 +176,8 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession : public QuicSession { QuicSpdyStream* CreateOutgoingDynamicStream(SpdyPriority priority) override = 0; - QuicSpdyStream* MaybeCreateIncomingDynamicStream(QuicStreamId id) override; - QuicSpdyStream* MaybeCreateOutgoingDynamicStream( - SpdyPriority priority) override; - QuicSpdyStream* GetSpdyDataStream(const QuicStreamId stream_id); - // TODO(ckrasic) - remove these two when - // FLAGS_quic_reloadable_flag_quic_refactor_stream_creation is - // depreacted. - // If an incoming stream can be created, return true. virtual bool ShouldCreateIncomingDynamicStream(QuicStreamId id) = 0; @@ -205,7 +200,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession : public QuicSession { std::unique_ptr<QuicHpackDebugVisitor> visitor); // Sets the maximum size of the header compression table spdy_framer_ is - // willing to use to decode header blocks. + // willing to use to encode header blocks. void UpdateHeaderEncoderTableSize(uint32_t value); // Called when SETTINGS_ENABLE_PUSH is received, only supported on @@ -263,6 +258,10 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession : public QuicSession { std::unique_ptr<QuicHeadersStream> headers_stream_; + // The maximum size of a header block that will be accepted from the peer, + // defined per spec as key + value + overhead per field (uncompressed). + size_t max_inbound_header_list_size_; + // If set, redirect all data through the headers stream in order to // simulate forced HOL blocking between streams as happens in // HTTP/2 over TCP. @@ -291,6 +290,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession : public QuicSession { QuicTime prev_max_timestamp_; SpdyFramer spdy_framer_; + Http2DecoderAdapter h2_deframer_; std::unique_ptr<SpdyFramerVisitor> spdy_framer_visitor_; DISALLOW_COPY_AND_ASSIGN(QuicSpdySession); diff --git a/chromium/net/quic/core/quic_spdy_stream.cc b/chromium/net/quic/core/quic_spdy_stream.cc index a1034f0664c..1c44653eccf 100644 --- a/chromium/net/quic/core/quic_spdy_stream.cc +++ b/chromium/net/quic/core/quic_spdy_stream.cc @@ -14,6 +14,7 @@ #include "net/quic/platform/api/quic_logging.h" #include "net/quic/platform/api/quic_string_piece.h" #include "net/quic/platform/api/quic_text_utils.h" +#include "net/spdy/core/spdy_protocol.h" using base::IntToString; using std::string; @@ -271,7 +272,7 @@ bool QuicSpdyStream::FinishedReadingHeaders() const { bool QuicSpdyStream::ParseHeaderStatusCode(const SpdyHeaderBlock& header, int* status_code) const { - SpdyHeaderBlock::const_iterator it = header.find(":status"); + SpdyHeaderBlock::const_iterator it = header.find(kHttp2StatusHeader); if (it == header.end()) { return false; } diff --git a/chromium/net/quic/core/quic_spdy_stream_test.cc b/chromium/net/quic/core/quic_spdy_stream_test.cc index eb295251a9d..6eb3423be55 100644 --- a/chromium/net/quic/core/quic_spdy_stream_test.cc +++ b/chromium/net/quic/core/quic_spdy_stream_test.cc @@ -924,7 +924,7 @@ TEST_P(QuicSpdyStreamTest, WritingTrailersWithQueuedBytes) { stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr); // Write non-zero body data, but only consume partially, ensuring queueing. - const int kBodySize = 1 * 1024; // 1 MB + const int kBodySize = 1 * 1024; // 1 KB EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillOnce(Return(QuicConsumedData(kBodySize - 1, false))); stream_->WriteOrBufferData(string(kBodySize, 'x'), false, nullptr); @@ -973,8 +973,7 @@ TEST_P(QuicSpdyStreamTest, HeaderStreamNotiferCorrespondingSpdyStream) { } EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .Times(AnyNumber()) - .WillRepeatedly( - Invoke(session_.get(), &MockQuicSpdySession::ConsumeAndSaveAllData)); + .WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData)); testing::InSequence s; QuicReferenceCountedPointer<MockAckListener> ack_listener1( new MockAckListener()); diff --git a/chromium/net/quic/core/quic_stream.cc b/chromium/net/quic/core/quic_stream.cc index e8583b62c04..4afb5c24531 100644 --- a/chromium/net/quic/core/quic_stream.cc +++ b/chromium/net/quic/core/quic_stream.cc @@ -7,6 +7,8 @@ #include "net/quic/core/quic_flow_controller.h" #include "net/quic/core/quic_session.h" #include "net/quic/platform/api/quic_bug_tracker.h" +#include "net/quic/platform/api/quic_flag_utils.h" +#include "net/quic/platform/api/quic_flags.h" #include "net/quic/platform/api/quic_logging.h" using std::string; @@ -78,7 +80,10 @@ QuicStream::QuicStream(QuicStreamId id, QuicSession* session) busy_counter_(0), add_random_padding_after_fin_(false), ack_listener_(nullptr), - send_buffer_(session->connection()->helper()->GetBufferAllocator()) { + send_buffer_( + session->connection()->helper()->GetStreamSendBufferAllocator()), + buffered_data_threshold_( + GetQuicFlag(FLAGS_quic_buffered_data_threshold)) { SetFromConfig(); } @@ -214,6 +219,26 @@ void QuicStream::WriteOrBufferData( QuicConsumedData consumed_data(0, false); fin_buffered_ = fin; + if (session_->save_data_before_consumption()) { + bool had_buffered_data = HasBufferedData(); + // Do not respect buffered data upper limit as WriteOrBufferData guarantees + // all data to be consumed. + if (data.length() > 0) { + struct iovec iov(MakeIovec(data)); + QuicIOVector quic_iov(&iov, 1, data.length()); + QuicStreamOffset offset = send_buffer_.stream_offset(); + send_buffer_.SaveStreamData(quic_iov, 0, data.length()); + OnDataBuffered(offset, data.length(), ack_listener); + } + if (!had_buffered_data && (HasBufferedData() || fin_buffered_)) { + // Write data if there is no buffered data before. + QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_save_data_before_consumption2, + 2, 4); + WriteBufferedData(); + } + return; + } + if (queued_data_.empty()) { struct iovec iov(MakeIovec(data)); consumed_data = WritevData(&iov, 1, fin, ack_listener); @@ -230,6 +255,26 @@ void QuicStream::WriteOrBufferData( } void QuicStream::OnCanWrite() { + if (session_->save_data_before_consumption()) { + DCHECK(queued_data_.empty()); + if (write_side_closed_) { + QUIC_DLOG(ERROR) << ENDPOINT << "Stream " << id() + << "attempting to write when the write side is closed"; + return; + } + if (HasBufferedData() || (fin_buffered_ && !fin_sent_)) { + QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_save_data_before_consumption2, + 3, 4); + WriteBufferedData(); + } + if (!fin_buffered_ && !fin_sent_ && CanWriteNewData()) { + // Notify upper layer to write new data when buffered data size is below + // low water mark. + OnCanWriteNewData(); + } + return; + } + bool fin = false; while (!queued_data_.empty()) { PendingData* pending_data = &queued_data_.front(); @@ -299,6 +344,38 @@ QuicConsumedData QuicStream::WritevData( } } + if (session_->save_data_before_consumption()) { + QuicConsumedData consumed_data(0, false); + if (fin_buffered_) { + QUIC_BUG << "Fin already buffered"; + return consumed_data; + } + + bool had_buffered_data = HasBufferedData(); + if (CanWriteNewData()) { + // Save all data if buffered data size is below low water mark. + QuicIOVector quic_iovec(iov, iov_count, write_length); + consumed_data.bytes_consumed = write_length; + if (consumed_data.bytes_consumed > 0) { + QuicStreamOffset offset = send_buffer_.stream_offset(); + send_buffer_.SaveStreamData(quic_iovec, 0, write_length); + OnDataBuffered(offset, write_length, ack_listener); + } + } + consumed_data.fin_consumed = + consumed_data.bytes_consumed == write_length && fin; + fin_buffered_ = consumed_data.fin_consumed; + + if (!had_buffered_data && (HasBufferedData() || fin_buffered_)) { + // Write data if there is no buffered data before. + QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_save_data_before_consumption2, + 1, 4); + WriteBufferedData(); + } + + return consumed_data; + } + // A FIN with zero data payload should not be flow control blocked. bool fin_with_zero_data = (fin && write_length == 0); @@ -409,6 +486,11 @@ void QuicStream::CloseWriteSide() { } bool QuicStream::HasBufferedData() const { + if (session_->save_data_before_consumption()) { + DCHECK(queued_data_.empty()); + DCHECK_GE(send_buffer_.stream_offset(), stream_bytes_written_); + return send_buffer_.stream_offset() > stream_bytes_written_; + } return !queued_data_.empty(); } @@ -511,12 +593,14 @@ void QuicStream::OnStreamFrameAcked(const QuicStreamFrame& frame, QuicTime::Delta ack_delay_time) { OnStreamFrameDiscarded(frame); if (ack_listener_ != nullptr) { + QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_use_stream_notifier2, 1, 3); ack_listener_->OnPacketAcked(frame.data_length, ack_delay_time); } } void QuicStream::OnStreamFrameRetransmitted(const QuicStreamFrame& frame) { if (ack_listener_ != nullptr) { + QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_use_stream_notifier2, 2, 3); ack_listener_->OnPacketRetransmitted(frame.data_length); } } @@ -533,7 +617,7 @@ void QuicStream::OnStreamFrameDiscarded(const QuicStreamFrame& frame) { if (frame.fin) { fin_outstanding_ = false; } - if (session_->streams_own_data() && frame.data_length > 0) { + if (session_->save_data_before_consumption() && frame.data_length > 0) { send_buffer_.RemoveStreamFrame(frame.offset, frame.data_length); } if (!IsWaitingForAcks()) { @@ -545,14 +629,6 @@ bool QuicStream::IsWaitingForAcks() const { return stream_bytes_outstanding_ || fin_outstanding_; } -void QuicStream::SaveStreamData(QuicIOVector iov, - size_t iov_offset, - QuicStreamOffset offset, - QuicByteCount data_length) { - DCHECK_LT(0u, data_length); - send_buffer_.SaveStreamData(iov, iov_offset, offset, data_length); -} - bool QuicStream::WriteStreamData(QuicStreamOffset offset, QuicByteCount data_length, QuicDataWriter* writer) { @@ -560,4 +636,99 @@ bool QuicStream::WriteStreamData(QuicStreamOffset offset, return send_buffer_.WriteStreamData(offset, data_length, writer); } +void QuicStream::WriteBufferedData() { + DCHECK(!write_side_closed_ && queued_data_.empty() && + (HasBufferedData() || fin_buffered_)); + + if (session_->ShouldYield(id())) { + session_->MarkConnectionLevelWriteBlocked(id()); + return; + } + + // Size of buffered data. + size_t write_length = queued_data_bytes(); + + // A FIN with zero data payload should not be flow control blocked. + bool fin_with_zero_data = (fin_buffered_ && write_length == 0); + + bool fin = fin_buffered_; + + // How much data flow control permits to be written. + QuicByteCount send_window = flow_controller_.SendWindowSize(); + if (stream_contributes_to_connection_flow_control_) { + send_window = + std::min(send_window, connection_flow_controller_->SendWindowSize()); + } + + if (send_window == 0 && !fin_with_zero_data) { + // Quick return if nothing can be sent. + MaybeSendBlocked(); + return; + } + + if (write_length > send_window) { + // Don't send the FIN unless all the data will be sent. + fin = false; + + // Writing more data would be a violation of flow control. + write_length = static_cast<size_t>(send_window); + QUIC_DVLOG(1) << "stream " << id() << " shortens write length to " + << write_length << " due to flow control"; + } + + QuicConsumedData consumed_data = WritevDataInner( + QuicIOVector(/*iov=*/nullptr, /*iov_count=*/0, write_length), + stream_bytes_written_, fin, nullptr); + + stream_bytes_written_ += consumed_data.bytes_consumed; + stream_bytes_outstanding_ += consumed_data.bytes_consumed; + + AddBytesSent(consumed_data.bytes_consumed); + QUIC_DVLOG(1) << ENDPOINT << "stream " << id_ << " sends " + << stream_bytes_written_ << " bytes " + << " and has buffered data " << queued_data_bytes() << " bytes." + << " fin is sent: " << consumed_data.fin_consumed + << " fin is buffered: " << fin_buffered_; + + // The write may have generated a write error causing this stream to be + // closed. If so, simply return without marking the stream write blocked. + if (write_side_closed_) { + return; + } + + if (consumed_data.bytes_consumed == write_length) { + if (!fin_with_zero_data) { + MaybeSendBlocked(); + } + if (fin && consumed_data.fin_consumed) { + fin_sent_ = true; + fin_outstanding_ = true; + if (fin_received_) { + session_->StreamDraining(id_); + } + CloseWriteSide(); + } else if (fin && !consumed_data.fin_consumed) { + session_->MarkConnectionLevelWriteBlocked(id()); + } + } else { + session_->MarkConnectionLevelWriteBlocked(id()); + } + if (consumed_data.bytes_consumed > 0 || consumed_data.fin_consumed) { + busy_counter_ = 0; + } +} + +uint64_t QuicStream::queued_data_bytes() const { + if (session_->save_data_before_consumption()) { + DCHECK_EQ(0u, queued_data_bytes_); + DCHECK_GE(send_buffer_.stream_offset(), stream_bytes_written_); + return send_buffer_.stream_offset() - stream_bytes_written_; + } + return queued_data_bytes_; +} + +bool QuicStream::CanWriteNewData() const { + return queued_data_bytes() < buffered_data_threshold_; +} + } // namespace net diff --git a/chromium/net/quic/core/quic_stream.h b/chromium/net/quic/core/quic_stream.h index a8bff46d130..be60503def0 100644 --- a/chromium/net/quic/core/quic_stream.h +++ b/chromium/net/quic/core/quic_stream.h @@ -114,14 +114,12 @@ class QUIC_EXPORT_PRIVATE QuicStream : public StreamNotifierInterface { bool fin_received() { return fin_received_; } bool fin_sent() { return fin_sent_; } - uint64_t queued_data_bytes() const { return queued_data_bytes_; } + // TODO(fayang): Rename this function to BufferedDataBytes() when + // deprecating quic_reloadable_flag_quic_save_data_before_consumption2. + uint64_t queued_data_bytes() const; uint64_t stream_bytes_read() const { return stream_bytes_read_; } uint64_t stream_bytes_written() const { return stream_bytes_written_; } - // For tests that override WritevData. - void set_stream_bytes_written(uint64_t bytes_written) { - stream_bytes_written_ = bytes_written; - } size_t busy_counter() const { return busy_counter_; } void set_busy_counter(size_t busy_counter) { busy_counter_ = busy_counter; } @@ -197,12 +195,6 @@ class QUIC_EXPORT_PRIVATE QuicStream : public StreamNotifierInterface { // Adds random padding after the fin is consumed for this stream. void AddRandomPaddingAfterFin(); - // Save |data_length| of data starts at |iov_offset| in |iov| to send buffer. - void SaveStreamData(QuicIOVector iov, - size_t iov_offset, - QuicStreamOffset offset, - QuicByteCount data_length); - // Write |data_length| of data starts at |offset| from send buffer. bool WriteStreamData(QuicStreamOffset offset, QuicByteCount data_length, @@ -216,10 +208,17 @@ class QUIC_EXPORT_PRIVATE QuicStream : public StreamNotifierInterface { protected: // Sends as many bytes in the first |count| buffers of |iov| to the connection - // as the connection will consume. + // as the connection will consume. If FIN is consumed, the write side is + // immediately closed. // If |ack_listener| is provided, then it will be notified once all // the ACKs for this write have been received. // Returns the number of bytes consumed by the connection. + // Please note: when quic_reloadable_flag_quic_save_data_before_consumption2 + // is true, returned consumed data is the amount of data saved in send buffer. + // The data is not necessarily consumed by the connection. So write side is + // closed when FIN is sent. + // TODO(fayang): Let WritevData return boolean when deprecating + // quic_reloadable_flag_quic_save_data_before_consumption2. QuicConsumedData WritevData( const struct iovec* iov, int iov_count, @@ -239,6 +238,26 @@ class QUIC_EXPORT_PRIVATE QuicStream : public StreamNotifierInterface { // Does not send a FIN. May cause the stream to be closed. virtual void CloseWriteSide(); + // Close the read side of the socket. May cause the stream to be closed. + // Subclasses and consumers should use StopReading to terminate reading early + // if expecting a FIN. Can be used directly by subclasses if not expecting a + // FIN. + void CloseReadSide(); + + // Called when data of [offset, offset + data_length] is buffered in send + // buffer. + virtual void OnDataBuffered( + QuicStreamOffset offset, + QuicByteCount data_length, + const QuicReferenceCountedPointer<QuicAckListenerInterface>& + ack_listener) {} + + // True if buffered data in send buffer is below buffered_data_threshold_. + bool CanWriteNewData() const; + + // Called when upper layer can write new data. + virtual void OnCanWriteNewData() {} + bool fin_buffered() const { return fin_buffered_; } const QuicSession* session() const { return session_; } @@ -260,10 +279,6 @@ class QUIC_EXPORT_PRIVATE QuicStream : public StreamNotifierInterface { friend class test::QuicStreamPeer; friend class QuicStreamUtils; - // Close the read side of the socket. May cause the stream to be closed. - // Subclasses and consumers should use StopReading to terminate reading early. - void CloseReadSide(); - // Subclasses and consumers should use reading_stopped. bool read_side_closed() const { return read_side_closed_; } @@ -288,8 +303,15 @@ class QUIC_EXPORT_PRIVATE QuicStream : public StreamNotifierInterface { // controller, marks this stream as connection-level write blocked. void MaybeSendBlocked(); + // Write buffered data in send buffer. TODO(fayang): Consider combine + // WriteOrBufferData, Writev and WriteBufferedData when deprecating + // quic_reloadable_flag_quic_save_data_before_consumption2. + void WriteBufferedData(); + std::list<PendingData> queued_data_; // How many bytes are queued? + // TODO(fayang): Remove this variable when deprecating + // quic_reloadable_flag_quic_save_data_before_consumption2. uint64_t queued_data_bytes_; QuicStreamSequencer sequencer_; @@ -366,6 +388,9 @@ class QUIC_EXPORT_PRIVATE QuicStream : public StreamNotifierInterface { // or discarded. QuicStreamSendBuffer send_buffer_; + // Latched value of FLAGS_quic_buffered_data_threshold. + const QuicByteCount buffered_data_threshold_; + DISALLOW_COPY_AND_ASSIGN(QuicStream); }; diff --git a/chromium/net/quic/core/quic_stream_frame_data_producer.h b/chromium/net/quic/core/quic_stream_frame_data_producer.h index abe8af1fb67..9786da6efce 100644 --- a/chromium/net/quic/core/quic_stream_frame_data_producer.h +++ b/chromium/net/quic/core/quic_stream_frame_data_producer.h @@ -12,18 +12,11 @@ namespace net { class QuicDataWriter; -// Pure virtual class to save and retrieve stream data. +// Pure virtual class to retrieve stream data. class QUIC_EXPORT_PRIVATE QuicStreamFrameDataProducer { public: virtual ~QuicStreamFrameDataProducer() {} - // Save |data_length| data starts at |iov_offset| in |iov|. - virtual void SaveStreamData(QuicStreamId id, - QuicIOVector iov, - size_t iov_offset, - QuicStreamOffset offset, - QuicByteCount data_length) = 0; - // Let |writer| write |data_length| data with |offset| of stream |id|. Returns // false when the writing fails either because stream is closed or // corresponding data is failed to be retrieved. This method allows writing a diff --git a/chromium/net/quic/core/quic_stream_send_buffer.cc b/chromium/net/quic/core/quic_stream_send_buffer.cc index e38d1008dd7..c091ca6574a 100644 --- a/chromium/net/quic/core/quic_stream_send_buffer.cc +++ b/chromium/net/quic/core/quic_stream_send_buffer.cc @@ -9,6 +9,8 @@ #include "net/quic/core/quic_stream_send_buffer.h" #include "net/quic/core/quic_utils.h" #include "net/quic/platform/api/quic_bug_tracker.h" +#include "net/quic/platform/api/quic_flags.h" +#include "net/quic/platform/api/quic_logging.h" namespace net { @@ -18,23 +20,28 @@ QuicStreamDataSlice::QuicStreamDataSlice(UniqueStreamBuffer data, : data(std::move(data)), offset(offset), data_length(data_length), - data_length_waiting_for_acks(data_length) {} + outstanding_data_length(data_length) {} QuicStreamDataSlice::~QuicStreamDataSlice() {} QuicStreamSendBuffer::QuicStreamSendBuffer(QuicBufferAllocator* allocator) - : allocator_(allocator) {} + : stream_offset_(0), allocator_(allocator) {} QuicStreamSendBuffer::~QuicStreamSendBuffer() {} void QuicStreamSendBuffer::SaveStreamData(QuicIOVector iov, size_t iov_offset, - QuicStreamOffset offset, QuicByteCount data_length) { - DCHECK_LE(iov_offset + data_length, iov.total_length); - UniqueStreamBuffer buffer = NewStreamBuffer(allocator_, data_length); - QuicUtils::CopyToBuffer(iov, iov_offset, data_length, buffer.get()); - send_buffer_.emplace_back(std::move(buffer), offset, data_length); + DCHECK_LT(0u, data_length); + // Latch the maximum data slice size. + const QuicByteCount max_data_slice_size = + GetQuicFlag(FLAGS_quic_send_buffer_max_data_slice_size); + while (data_length > 0) { + size_t slice_len = std::min(data_length, max_data_slice_size); + SaveStreamDataOneSlice(iov, iov_offset, slice_len); + data_length -= slice_len; + iov_offset += slice_len; + } } bool QuicStreamSendBuffer::WriteStreamData(QuicStreamOffset offset, @@ -62,7 +69,6 @@ bool QuicStreamSendBuffer::WriteStreamData(QuicStreamOffset offset, void QuicStreamSendBuffer::RemoveStreamFrame(QuicStreamOffset offset, QuicByteCount data_length) { - DCHECK_LT(0u, data_length); for (QuicStreamDataSlice& slice : send_buffer_) { if (offset < slice.offset) { break; @@ -73,7 +79,7 @@ void QuicStreamSendBuffer::RemoveStreamFrame(QuicStreamOffset offset, QuicByteCount slice_offset = offset - slice.offset; QuicByteCount removing_length = std::min(data_length, slice.data_length - slice_offset); - slice.data_length_waiting_for_acks -= removing_length; + slice.outstanding_data_length -= removing_length; offset += removing_length; data_length -= removing_length; } @@ -82,11 +88,21 @@ void QuicStreamSendBuffer::RemoveStreamFrame(QuicStreamOffset offset, // Remove data which stops waiting for acks. Please note, data can be // acked out of order, but send buffer is cleaned up in order. while (!send_buffer_.empty() && - send_buffer_.front().data_length_waiting_for_acks == 0) { + send_buffer_.front().outstanding_data_length == 0) { send_buffer_.pop_front(); } } +void QuicStreamSendBuffer::SaveStreamDataOneSlice(QuicIOVector iov, + size_t iov_offset, + QuicByteCount data_length) { + DCHECK_LE(iov_offset + data_length, iov.total_length); + UniqueStreamBuffer buffer = NewStreamBuffer(allocator_, data_length); + QuicUtils::CopyToBuffer(iov, iov_offset, data_length, buffer.get()); + send_buffer_.emplace_back(std::move(buffer), stream_offset_, data_length); + stream_offset_ += data_length; +} + size_t QuicStreamSendBuffer::size() const { return send_buffer_.size(); } diff --git a/chromium/net/quic/core/quic_stream_send_buffer.h b/chromium/net/quic/core/quic_stream_send_buffer.h index 15345213b31..14e50f76af3 100644 --- a/chromium/net/quic/core/quic_stream_send_buffer.h +++ b/chromium/net/quic/core/quic_stream_send_buffer.h @@ -12,6 +12,10 @@ namespace net { +namespace test { +class QuicStreamSendBufferPeer; +} // namespace test + class QuicDataWriter; // QuicStreamDataSlice comprises information of a piece of stream data. @@ -29,8 +33,8 @@ struct QuicStreamDataSlice { QuicStreamOffset offset; // Length of this data slice in bytes. QuicByteCount data_length; - // Length of payload which is waiting for acks. - QuicByteCount data_length_waiting_for_acks; + // Length of payload which is outstanding and waiting for acks. + QuicByteCount outstanding_data_length; }; // QuicStreamSendBuffer contains a list of QuicStreamDataSlices. New data slices @@ -47,7 +51,6 @@ class QUIC_EXPORT_PRIVATE QuicStreamSendBuffer { // Save |data_length| of data starts at |iov_offset| in |iov| to send buffer. void SaveStreamData(QuicIOVector iov, size_t iov_offset, - QuicStreamOffset offset, QuicByteCount data_length); // Write |data_length| of data starts at |offset|. @@ -62,9 +65,21 @@ class QUIC_EXPORT_PRIVATE QuicStreamSendBuffer { // Number of data slices in send buffer. size_t size() const; + QuicStreamOffset stream_offset() const { return stream_offset_; } + private: + friend class test::QuicStreamSendBufferPeer; + // Save |data_length| of data starts at |iov_offset| in |iov| to one data + // slice which contains data in a contiguous memory space. + void SaveStreamDataOneSlice(QuicIOVector iov, + size_t iov_offset, + QuicByteCount data_length); + std::deque<QuicStreamDataSlice> send_buffer_; + // Offset of next inserted byte. + QuicStreamOffset stream_offset_; + QuicBufferAllocator* allocator_; }; diff --git a/chromium/net/quic/core/quic_stream_send_buffer_test.cc b/chromium/net/quic/core/quic_stream_send_buffer_test.cc index d3520a8f09a..95d2bc0f9a1 100644 --- a/chromium/net/quic/core/quic_stream_send_buffer_test.cc +++ b/chromium/net/quic/core/quic_stream_send_buffer_test.cc @@ -7,6 +7,7 @@ #include "net/quic/core/quic_data_writer.h" #include "net/quic/core/quic_simple_buffer_allocator.h" #include "net/quic/core/quic_utils.h" +#include "net/quic/platform/api/quic_flags.h" #include "net/quic/platform/api/quic_test.h" #include "net/quic/test_tools/quic_test_utils.h" @@ -35,14 +36,9 @@ class QuicStreamSendBufferTest : public QuicTest { iov[2] = MakeIovec(QuicStringPiece(data3)); QuicIOVector quic_iov(iov, 3, 3840); - // Add all data. - send_buffer_.SaveStreamData(quic_iov, /*iov_offset=*/0, /*offset=*/0, 1024); - send_buffer_.SaveStreamData(quic_iov, /*iov_offset=*/1024, - /*offset=*/1024, 1024); - send_buffer_.SaveStreamData(quic_iov, /*iov_offset=*/2048, - /*offset=*/2048, 1024); - send_buffer_.SaveStreamData(quic_iov, /*iov_offset=*/3072, - /*offset=*/3072, 768); + // Save all data. + SetQuicFlag(&FLAGS_quic_send_buffer_max_data_slice_size, 1024); + send_buffer_.SaveStreamData(quic_iov, 0, 3840); EXPECT_EQ(4u, send_buffer_.size()); } diff --git a/chromium/net/quic/core/quic_stream_sequencer_test.cc b/chromium/net/quic/core/quic_stream_sequencer_test.cc index f65394dc730..8ca75ff1656 100644 --- a/chromium/net/quic/core/quic_stream_sequencer_test.cc +++ b/chromium/net/quic/core/quic_stream_sequencer_test.cc @@ -27,8 +27,6 @@ using testing::_; using testing::AnyNumber; using testing::CreateFunctor; using testing::InSequence; -using testing::Return; -using testing::StrEq; namespace net { namespace test { @@ -43,7 +41,6 @@ class MockStream : public QuicStream { void(QuicErrorCode error, const string& details)); MOCK_METHOD1(Reset, void(QuicRstStreamErrorCode error)); MOCK_METHOD0(OnCanWrite, void()); - virtual bool IsFlowControlEnabled() const { return true; } const QuicSocketAddress& PeerAddressOfLatestPacket() const override { return peer_address_; diff --git a/chromium/net/quic/core/quic_stream_test.cc b/chromium/net/quic/core/quic_stream_test.cc index e9e04a92261..77164b0bb29 100644 --- a/chromium/net/quic/core/quic_stream_test.cc +++ b/chromium/net/quic/core/quic_stream_test.cc @@ -48,23 +48,19 @@ const bool kShouldNotProcessData = false; class TestStream : public QuicStream { public: TestStream(QuicStreamId id, QuicSession* session, bool should_process_data) - : QuicStream(id, session), should_process_data_(should_process_data) {} + : QuicStream(id, session) {} void OnDataAvailable() override {} - uint32_t ProcessRawData(const char* data, uint32_t data_len) { - EXPECT_NE(0u, data_len); - QUIC_DVLOG(1) << "ProcessData data_len: " << data_len; - data_ += string(data, data_len); - return should_process_data_ ? data_len : 0; - } + MOCK_METHOD0(OnCanWriteNewData, void()); + using QuicStream::CanWriteNewData; using QuicStream::WriteOrBufferData; + using QuicStream::WritevData; using QuicStream::CloseWriteSide; using QuicStream::OnClose; private: - bool should_process_data_; string data_; }; @@ -249,17 +245,23 @@ TEST_F(QuicStreamTest, WriteOrBufferData) { EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillOnce(Return(QuicConsumedData(kDataLen - 1, false))); stream_->WriteOrBufferData(kData1, false, nullptr); + EXPECT_EQ(1u, stream_->queued_data_bytes()); EXPECT_TRUE(HasWriteBlockedStreams()); // Queue a bytes_consumed write. stream_->WriteOrBufferData(kData2, false, nullptr); - + EXPECT_EQ(10u, stream_->queued_data_bytes()); // Make sure we get the tail of the first write followed by the bytes_consumed InSequence s; - EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) - .WillOnce(Return(QuicConsumedData(1, false))); - EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) - .WillOnce(Return(QuicConsumedData(kDataLen - 2, false))); + if (session_->save_data_before_consumption()) { + EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(kDataLen - 1, false))); + } else { + EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(1, false))); + EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(kDataLen - 2, false))); + } stream_->OnCanWrite(); // And finally the end of the bytes_consumed. @@ -717,15 +719,14 @@ TEST_F(QuicStreamTest, EarlyResponseFinHandling) { TEST_F(QuicStreamTest, StreamWaitsForAcks) { Initialize(kShouldProcessData); EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) - .WillRepeatedly( - Invoke(session_.get(), &MockQuicSession::ConsumeAndSaveAllData)); + .WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData)); // Stream is not waiting for acks initially. EXPECT_FALSE(stream_->IsWaitingForAcks()); EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); // Send kData1. stream_->WriteOrBufferData(kData1, false, nullptr); - if (session_->streams_own_data()) { + if (session_->save_data_before_consumption()) { EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); } else { EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); @@ -740,14 +741,14 @@ TEST_F(QuicStreamTest, StreamWaitsForAcks) { // Send kData2. stream_->WriteOrBufferData(kData2, false, nullptr); EXPECT_TRUE(stream_->IsWaitingForAcks()); - if (session_->streams_own_data()) { + if (session_->save_data_before_consumption()) { EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); } else { EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); } // Send FIN. stream_->WriteOrBufferData("", true, nullptr); - if (session_->streams_own_data()) { + if (session_->save_data_before_consumption()) { // Fin only frame is not stored in send buffer. EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); } else { @@ -771,14 +772,13 @@ TEST_F(QuicStreamTest, StreamWaitsForAcks) { TEST_F(QuicStreamTest, StreamDataGetAckedOutOfOrder) { Initialize(kShouldProcessData); EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) - .WillRepeatedly( - Invoke(session_.get(), &MockQuicSession::ConsumeAndSaveAllData)); + .WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData)); // Send data. stream_->WriteOrBufferData(kData1, false, nullptr); stream_->WriteOrBufferData(kData1, false, nullptr); stream_->WriteOrBufferData(kData1, false, nullptr); stream_->WriteOrBufferData("", true, nullptr); - if (session_->streams_own_data()) { + if (session_->save_data_before_consumption()) { EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size()); } else { EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); @@ -790,11 +790,11 @@ TEST_F(QuicStreamTest, StreamDataGetAckedOutOfOrder) { QuicStreamFrame frame3(stream_->id(), false, 18, kData1); QuicStreamFrame frame4(stream_->id(), true, 27, ""); stream_->OnStreamFrameAcked(frame2, QuicTime::Delta::Zero()); - if (session_->streams_own_data()) { + if (session_->save_data_before_consumption()) { EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size()); } stream_->OnStreamFrameAcked(frame3, QuicTime::Delta::Zero()); - if (session_->streams_own_data()) { + if (session_->save_data_before_consumption()) { EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size()); } stream_->OnStreamFrameDiscarded(frame1); @@ -808,15 +808,14 @@ TEST_F(QuicStreamTest, StreamDataGetAckedOutOfOrder) { TEST_F(QuicStreamTest, CancelStream) { Initialize(kShouldProcessData); EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) - .WillRepeatedly( - Invoke(session_.get(), &MockQuicSession::ConsumeAndSaveAllData)); + .WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData)); EXPECT_FALSE(stream_->IsWaitingForAcks()); EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); stream_->WriteOrBufferData(kData1, false, nullptr); QuicStreamFrame frame(stream_->id(), 0, false, kData1); EXPECT_TRUE(stream_->IsWaitingForAcks()); - if (session_->streams_own_data()) { + if (session_->save_data_before_consumption()) { EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); } else { EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); @@ -840,15 +839,14 @@ TEST_F(QuicStreamTest, CancelStream) { TEST_F(QuicStreamTest, RstFrameReceivedStreamNotFinishSending) { Initialize(kShouldProcessData); EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) - .WillRepeatedly( - Invoke(session_.get(), &MockQuicSession::ConsumeAndSaveAllData)); + .WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData)); EXPECT_FALSE(stream_->IsWaitingForAcks()); EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); stream_->WriteOrBufferData(kData1, false, nullptr); QuicStreamFrame frame(stream_->id(), 0, false, kData1); EXPECT_TRUE(stream_->IsWaitingForAcks()); - if (session_->streams_own_data()) { + if (session_->save_data_before_consumption()) { EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); } else { EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); @@ -871,8 +869,7 @@ TEST_F(QuicStreamTest, RstFrameReceivedStreamNotFinishSending) { TEST_F(QuicStreamTest, RstFrameReceivedStreamFinishSending) { Initialize(kShouldProcessData); EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) - .WillRepeatedly( - Invoke(session_.get(), &MockQuicSession::ConsumeAndSaveAllData)); + .WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData)); EXPECT_FALSE(stream_->IsWaitingForAcks()); EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); @@ -885,7 +882,7 @@ TEST_F(QuicStreamTest, RstFrameReceivedStreamFinishSending) { stream_->OnStreamReset(rst_frame); // Stream stops waiting for acks as it has unacked data. EXPECT_TRUE(stream_->IsWaitingForAcks()); - if (session_->streams_own_data()) { + if (session_->save_data_before_consumption()) { EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); } else { EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); @@ -895,8 +892,7 @@ TEST_F(QuicStreamTest, RstFrameReceivedStreamFinishSending) { TEST_F(QuicStreamTest, ConnectionClosed) { Initialize(kShouldProcessData); EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) - .WillRepeatedly( - Invoke(session_.get(), &MockQuicSession::ConsumeAndSaveAllData)); + .WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData)); EXPECT_FALSE(stream_->IsWaitingForAcks()); EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); @@ -916,6 +912,96 @@ TEST_F(QuicStreamTest, ConnectionClosed) { } } +TEST_F(QuicStreamTest, WriteBufferedData) { + // Set buffered data low water mark to be 100. + SetQuicFlag(&FLAGS_quic_buffered_data_threshold, 100); + // Do not stream level flow control block this stream. + set_initial_flow_control_window_bytes(500000); + + Initialize(kShouldProcessData); + if (!session_->save_data_before_consumption()) { + return; + } + string data(1024, 'a'); + EXPECT_TRUE(stream_->CanWriteNewData()); + + // Testing WriteOrBufferData. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(100, false))); + stream_->WriteOrBufferData(data, false, nullptr); + stream_->WriteOrBufferData(data, false, nullptr); + stream_->WriteOrBufferData(data, false, nullptr); + // Verify all data is saved. + EXPECT_EQ(3 * data.length() - 100, stream_->queued_data_bytes()); + + EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(100, false))); + // Buffered data size > threshold, do not ask upper layer for more data. + EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(0); + stream_->OnCanWrite(); + EXPECT_EQ(3 * data.length() - 200, stream_->queued_data_bytes()); + EXPECT_FALSE(stream_->CanWriteNewData()); + + // Send buffered data to make buffered data size < threshold. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData( + 3 * data.length() - 200 - + GetQuicFlag(FLAGS_quic_buffered_data_threshold) + 1, + false))); + // Buffered data size < threshold, ask upper layer for more data. + EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(1); + stream_->OnCanWrite(); + EXPECT_EQ(GetQuicFlag(FLAGS_quic_buffered_data_threshold) - 1u, + stream_->queued_data_bytes()); + EXPECT_TRUE(stream_->CanWriteNewData()); + + // Flush all buffered data. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) + .WillOnce(Invoke(MockQuicSession::ConsumeAllData)); + EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(1); + stream_->OnCanWrite(); + EXPECT_EQ(0u, stream_->queued_data_bytes()); + EXPECT_FALSE(stream_->HasBufferedData()); + EXPECT_TRUE(stream_->CanWriteNewData()); + + // Testing Writev. + EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData(0, false))); + struct iovec iov = {const_cast<char*>(data.data()), data.length()}; + QuicConsumedData consumed = stream_->WritevData(&iov, 1, false, nullptr); + // There is no buffered data before, all data should be consumed without + // respecting buffered data upper limit. + EXPECT_EQ(data.length(), consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_EQ(data.length(), stream_->queued_data_bytes()); + EXPECT_FALSE(stream_->CanWriteNewData()); + + EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).Times(0); + consumed = stream_->WritevData(&iov, 1, false, nullptr); + // No Data can be consumed as buffered data is beyond upper limit. + EXPECT_EQ(0u, consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_EQ(data.length(), stream_->queued_data_bytes()); + + EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) + .WillOnce(Return(QuicConsumedData( + data.length() - FLAGS_quic_buffered_data_threshold + 1, false))); + EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(1); + stream_->OnCanWrite(); + EXPECT_EQ(GetQuicFlag(FLAGS_quic_buffered_data_threshold) - 1, + stream_->queued_data_bytes()); + EXPECT_TRUE(stream_->CanWriteNewData()); + + EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).Times(0); + // All data can be consumed as buffered data is below upper limit. + consumed = stream_->WritevData(&iov, 1, false, nullptr); + EXPECT_EQ(data.length(), consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); + EXPECT_EQ(data.length() + GetQuicFlag(FLAGS_quic_buffered_data_threshold) - 1, + stream_->queued_data_bytes()); + EXPECT_FALSE(stream_->CanWriteNewData()); +} + } // namespace } // namespace test } // namespace net diff --git a/chromium/net/quic/core/quic_time.h b/chromium/net/quic/core/quic_time.h index 18ef46f1170..2bbdc18003f 100644 --- a/chromium/net/quic/core/quic_time.h +++ b/chromium/net/quic/core/quic_time.h @@ -206,9 +206,6 @@ inline bool operator<=(QuicTime::Delta lhs, QuicTime::Delta rhs) { inline bool operator>=(QuicTime::Delta lhs, QuicTime::Delta rhs) { return !(lhs < rhs); } -inline QuicTime::Delta operator<<(QuicTime::Delta lhs, size_t rhs) { - return QuicTime::Delta(lhs.time_offset_ << rhs); -} inline QuicTime::Delta operator>>(QuicTime::Delta lhs, size_t rhs) { return QuicTime::Delta(lhs.time_offset_ >> rhs); } diff --git a/chromium/net/quic/core/quic_types.h b/chromium/net/quic/core/quic_types.h index eab0bc8398a..f7e9f9a5180 100644 --- a/chromium/net/quic/core/quic_types.h +++ b/chromium/net/quic/core/quic_types.h @@ -140,7 +140,9 @@ enum QuicPacketNumberLength : int8_t { PACKET_1BYTE_PACKET_NUMBER = 1, PACKET_2BYTE_PACKET_NUMBER = 2, PACKET_4BYTE_PACKET_NUMBER = 4, - PACKET_6BYTE_PACKET_NUMBER = 6 + // TODO(rch): Remove this when we remove QUIC_VERSION_39. + PACKET_6BYTE_PACKET_NUMBER = 6, + PACKET_8BYTE_PACKET_NUMBER = 8 }; // Used to indicate a QuicSequenceNumberLength using two flag bits. @@ -148,7 +150,7 @@ enum QuicPacketNumberLengthFlags { PACKET_FLAGS_1BYTE_PACKET = 0, // 00 PACKET_FLAGS_2BYTE_PACKET = 1, // 01 PACKET_FLAGS_4BYTE_PACKET = 1 << 1, // 10 - PACKET_FLAGS_6BYTE_PACKET = 1 << 1 | 1, // 11 + PACKET_FLAGS_8BYTE_PACKET = 1 << 1 | 1, // 11 }; // The public flags are specified in one byte. @@ -180,7 +182,7 @@ enum QuicPacketPublicFlags { PACKET_PUBLIC_FLAGS_1BYTE_PACKET = PACKET_FLAGS_1BYTE_PACKET << 4, PACKET_PUBLIC_FLAGS_2BYTE_PACKET = PACKET_FLAGS_2BYTE_PACKET << 4, PACKET_PUBLIC_FLAGS_4BYTE_PACKET = PACKET_FLAGS_4BYTE_PACKET << 4, - PACKET_PUBLIC_FLAGS_6BYTE_PACKET = PACKET_FLAGS_6BYTE_PACKET << 4, + PACKET_PUBLIC_FLAGS_6BYTE_PACKET = PACKET_FLAGS_8BYTE_PACKET << 4, // Reserved, unimplemented flags: diff --git a/chromium/net/quic/core/quic_utils.cc b/chromium/net/quic/core/quic_utils.cc index 078a26ca1bd..62bd144f11d 100644 --- a/chromium/net/quic/core/quic_utils.cc +++ b/chromium/net/quic/core/quic_utils.cc @@ -244,7 +244,7 @@ void QuicUtils::CopyToBuffer(QuicIOVector iov, // prefetch(next_base, PREFETCH_HINT_T0); if (iov.iov[iovnum + 1].iov_len >= 64) { // TODO(ckrasic) - investigate what to do about prefetch directives. - // prefetch(next_base + CACHELINE_SIZE, PREFETCH_HINT_T0); + // prefetch(next_base + ABSL_CACHELINE_SIZE, PREFETCH_HINT_T0); } } diff --git a/chromium/net/quic/core/quic_version_manager.cc b/chromium/net/quic/core/quic_version_manager.cc index 201d8ae6ba6..2136890fed0 100644 --- a/chromium/net/quic/core/quic_version_manager.cc +++ b/chromium/net/quic/core/quic_version_manager.cc @@ -10,7 +10,8 @@ namespace net { QuicVersionManager::QuicVersionManager(QuicVersionVector supported_versions) - : enable_version_40_(GetQuicFlag(FLAGS_quic_enable_version_40)), + : enable_version_41_(GetQuicFlag(FLAGS_quic_enable_version_41)), + enable_version_40_(FLAGS_quic_reloadable_flag_quic_enable_version_40), enable_version_39_(FLAGS_quic_reloadable_flag_quic_enable_version_39), enable_version_38_(FLAGS_quic_reloadable_flag_quic_enable_version_38), disable_version_36_(FLAGS_quic_reloadable_flag_quic_disable_version_36), @@ -26,12 +27,14 @@ const QuicVersionVector& QuicVersionManager::GetSupportedVersions() { } void QuicVersionManager::MaybeRefilterSupportedVersions() { - if (enable_version_40_ != GetQuicFlag(FLAGS_quic_enable_version_40) || + if (enable_version_41_ != GetQuicFlag(FLAGS_quic_enable_version_41) || + enable_version_40_ != FLAGS_quic_reloadable_flag_quic_enable_version_40 || enable_version_39_ != FLAGS_quic_reloadable_flag_quic_enable_version_39 || enable_version_38_ != FLAGS_quic_reloadable_flag_quic_enable_version_38 || disable_version_36_ != FLAGS_quic_reloadable_flag_quic_disable_version_36) { - enable_version_40_ = GetQuicFlag(FLAGS_quic_enable_version_40); + enable_version_41_ = GetQuicFlag(FLAGS_quic_enable_version_41); + enable_version_40_ = FLAGS_quic_reloadable_flag_quic_enable_version_40; enable_version_39_ = FLAGS_quic_reloadable_flag_quic_enable_version_39; enable_version_38_ = FLAGS_quic_reloadable_flag_quic_enable_version_38; disable_version_36_ = FLAGS_quic_reloadable_flag_quic_disable_version_36; diff --git a/chromium/net/quic/core/quic_version_manager.h b/chromium/net/quic/core/quic_version_manager.h index a9201dadace..c6fa10d7f93 100644 --- a/chromium/net/quic/core/quic_version_manager.h +++ b/chromium/net/quic/core/quic_version_manager.h @@ -31,7 +31,9 @@ class QUIC_EXPORT_PRIVATE QuicVersionManager { } private: - // FLAGS_quic_enable_version_40 + // FLAGS_quic_enable_version_41 + bool enable_version_41_; + // FLAGS_quic_reloadable_flag_quic_enable_version_40 bool enable_version_40_; // FLAGS_quic_reloadable_flag_quic_enable_version_39 bool enable_version_39_; diff --git a/chromium/net/quic/core/quic_version_manager_test.cc b/chromium/net/quic/core/quic_version_manager_test.cc index 232e5cc82b7..0fdd0982ce2 100644 --- a/chromium/net/quic/core/quic_version_manager_test.cc +++ b/chromium/net/quic/core/quic_version_manager_test.cc @@ -16,7 +16,8 @@ namespace { class QuicVersionManagerTest : public QuicTest {}; TEST_F(QuicVersionManagerTest, QuicVersionManager) { - SetQuicFlag(&FLAGS_quic_enable_version_40, false); + SetQuicFlag(&FLAGS_quic_enable_version_41, false); + FLAGS_quic_reloadable_flag_quic_enable_version_40 = false; FLAGS_quic_reloadable_flag_quic_enable_version_39 = false; FLAGS_quic_reloadable_flag_quic_enable_version_38 = false; FLAGS_quic_reloadable_flag_quic_disable_version_36 = false; @@ -48,7 +49,7 @@ TEST_F(QuicVersionManagerTest, QuicVersionManager) { EXPECT_EQ(QUIC_VERSION_37, manager.GetSupportedVersions()[2]); EXPECT_EQ(QUIC_VERSION_35, manager.GetSupportedVersions()[3]); - SetQuicFlag(&FLAGS_quic_enable_version_40, true); + FLAGS_quic_reloadable_flag_quic_enable_version_40 = true; ASSERT_EQ(5u, manager.GetSupportedVersions().size()); EXPECT_EQ(QUIC_VERSION_40, manager.GetSupportedVersions()[0]); EXPECT_EQ(QUIC_VERSION_39, manager.GetSupportedVersions()[1]); @@ -56,14 +57,24 @@ TEST_F(QuicVersionManagerTest, QuicVersionManager) { EXPECT_EQ(QUIC_VERSION_37, manager.GetSupportedVersions()[3]); EXPECT_EQ(QUIC_VERSION_35, manager.GetSupportedVersions()[4]); + SetQuicFlag(&FLAGS_quic_enable_version_41, true); + ASSERT_EQ(6u, manager.GetSupportedVersions().size()); + EXPECT_EQ(QUIC_VERSION_41, manager.GetSupportedVersions()[0]); + EXPECT_EQ(QUIC_VERSION_40, manager.GetSupportedVersions()[1]); + EXPECT_EQ(QUIC_VERSION_39, manager.GetSupportedVersions()[2]); + EXPECT_EQ(QUIC_VERSION_38, manager.GetSupportedVersions()[3]); + EXPECT_EQ(QUIC_VERSION_37, manager.GetSupportedVersions()[4]); + EXPECT_EQ(QUIC_VERSION_35, manager.GetSupportedVersions()[5]); + EXPECT_EQ(FilterSupportedVersions(AllSupportedVersions()), manager.GetSupportedVersions()); - ASSERT_EQ(5u, manager.GetSupportedVersions().size()); - EXPECT_EQ(QUIC_VERSION_40, manager.GetSupportedVersions()[0]); - EXPECT_EQ(QUIC_VERSION_39, manager.GetSupportedVersions()[1]); - EXPECT_EQ(QUIC_VERSION_38, manager.GetSupportedVersions()[2]); - EXPECT_EQ(QUIC_VERSION_37, manager.GetSupportedVersions()[3]); - EXPECT_EQ(QUIC_VERSION_35, manager.GetSupportedVersions()[4]); + ASSERT_EQ(6u, manager.GetSupportedVersions().size()); + EXPECT_EQ(QUIC_VERSION_41, manager.GetSupportedVersions()[0]); + EXPECT_EQ(QUIC_VERSION_40, manager.GetSupportedVersions()[1]); + EXPECT_EQ(QUIC_VERSION_39, manager.GetSupportedVersions()[2]); + EXPECT_EQ(QUIC_VERSION_38, manager.GetSupportedVersions()[3]); + EXPECT_EQ(QUIC_VERSION_37, manager.GetSupportedVersions()[4]); + EXPECT_EQ(QUIC_VERSION_35, manager.GetSupportedVersions()[5]); } } // namespace diff --git a/chromium/net/quic/core/quic_versions.cc b/chromium/net/quic/core/quic_versions.cc index ff4d7fe3bb5..9936bb4de60 100644 --- a/chromium/net/quic/core/quic_versions.cc +++ b/chromium/net/quic/core/quic_versions.cc @@ -30,8 +30,15 @@ QuicVersionVector FilterSupportedVersions(QuicVersionVector versions) { QuicVersionVector filtered_versions(versions.size()); filtered_versions.clear(); // Guaranteed by spec not to change capacity. for (QuicVersion version : versions) { - if (version == QUIC_VERSION_40) { - if (GetQuicFlag(FLAGS_quic_enable_version_40) && + if (version == QUIC_VERSION_41) { + if (GetQuicFlag(FLAGS_quic_enable_version_41) && + FLAGS_quic_reloadable_flag_quic_enable_version_40 && + FLAGS_quic_reloadable_flag_quic_enable_version_39 && + FLAGS_quic_reloadable_flag_quic_enable_version_38) { + filtered_versions.push_back(version); + } + } else if (version == QUIC_VERSION_40) { + if (FLAGS_quic_reloadable_flag_quic_enable_version_40 && FLAGS_quic_reloadable_flag_quic_enable_version_39 && FLAGS_quic_reloadable_flag_quic_enable_version_38) { filtered_versions.push_back(version); @@ -81,6 +88,8 @@ QuicTag QuicVersionToQuicTag(const QuicVersion version) { return MakeQuicTag('Q', '0', '3', '9'); case QUIC_VERSION_40: return MakeQuicTag('Q', '0', '4', '0'); + case QUIC_VERSION_41: + return MakeQuicTag('Q', '0', '4', '1'); default: // This shold be an ERROR because we should never attempt to convert an // invalid QuicVersion to be written to the wire. @@ -113,6 +122,7 @@ string QuicVersionToString(const QuicVersion version) { RETURN_STRING_LITERAL(QUIC_VERSION_38); RETURN_STRING_LITERAL(QUIC_VERSION_39); RETURN_STRING_LITERAL(QUIC_VERSION_40); + RETURN_STRING_LITERAL(QUIC_VERSION_41); default: return "QUIC_VERSION_UNSUPPORTED"; } diff --git a/chromium/net/quic/core/quic_versions.h b/chromium/net/quic/core/quic_versions.h index 1ddffe1ce55..3f9e1129ae3 100644 --- a/chromium/net/quic/core/quic_versions.h +++ b/chromium/net/quic/core/quic_versions.h @@ -33,11 +33,8 @@ enum QuicVersion { // endian. Dot not ack acks. Send a connection level // WINDOW_UPDATE every 20 sent packets which do not // contain retransmittable frames. - QUIC_VERSION_40 = 40, // Initial packet number is randomly chosen from - // [0:2^31], WINDOW_UPDATE for connection flow control - // advertises value in 1024-byte units, WINDOW_UPDATE - // splits into MAX_DATA and MAX_STREAM_DATA, BLOCKED - // frame split into BLOCKED and STREAM_BLOCKED frames + QUIC_VERSION_40 = 40, // RST_STREAM, ACK and STREAM frames match IETF format. + QUIC_VERSION_41 = 41, // Use IETF packet header format. // IMPORTANT: if you are adding to this list, follow the instructions at // http://sites/quic/adding-and-removing-versions @@ -51,7 +48,7 @@ enum QuicVersion { // IMPORTANT: if you are adding to this list, follow the instructions at // http://sites/quic/adding-and-removing-versions static const QuicVersion kSupportedQuicVersions[] = { - QUIC_VERSION_40, QUIC_VERSION_39, QUIC_VERSION_38, + QUIC_VERSION_41, QUIC_VERSION_40, QUIC_VERSION_39, QUIC_VERSION_38, QUIC_VERSION_37, QUIC_VERSION_36, QUIC_VERSION_35}; typedef std::vector<QuicVersion> QuicVersionVector; diff --git a/chromium/net/quic/core/spdy_utils.cc b/chromium/net/quic/core/spdy_utils.cc index 24f226442f1..e6285134a92 100644 --- a/chromium/net/quic/core/spdy_utils.cc +++ b/chromium/net/quic/core/spdy_utils.cc @@ -14,7 +14,6 @@ #include "net/quic/platform/api/quic_string_piece.h" #include "net/quic/platform/api/quic_text_utils.h" #include "net/quic/platform/api/quic_url_utils.h" -#include "net/spdy/chromium/spdy_flags.h" #include "net/spdy/core/spdy_frame_builder.h" #include "net/spdy/core/spdy_framer.h" #include "net/spdy/core/spdy_protocol.h" diff --git a/chromium/net/quic/core/spdy_utils.h b/chromium/net/quic/core/spdy_utils.h index cb11c93a629..724136af20e 100644 --- a/chromium/net/quic/core/spdy_utils.h +++ b/chromium/net/quic/core/spdy_utils.h @@ -54,8 +54,6 @@ class QUIC_EXPORT_PRIVATE SpdyUtils { static bool PopulateHeaderBlockFromUrl(const std::string url, SpdyHeaderBlock* headers); - static bool IsServerPushStream(QuicStreamId id) { return id % 2 == 0; } - private: DISALLOW_COPY_AND_ASSIGN(SpdyUtils); }; |