diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-09-03 13:32:17 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-10-01 14:31:55 +0200 |
commit | 21ba0c5d4bf8fba15dddd97cd693bad2358b77fd (patch) | |
tree | 91be119f694044dfc1ff9fdc054459e925de9df0 /chromium/net/third_party/quiche/src/quic/core | |
parent | 03c549e0392f92c02536d3f86d5e1d8dfa3435ac (diff) | |
download | qtwebengine-chromium-21ba0c5d4bf8fba15dddd97cd693bad2358b77fd.tar.gz |
BASELINE: Update Chromium to 92.0.4515.166
Change-Id: I42a050486714e9e54fc271f2a8939223a02ae364
Diffstat (limited to 'chromium/net/third_party/quiche/src/quic/core')
147 files changed, 5548 insertions, 4278 deletions
diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.cc b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.cc index 8b4a27f0866..173c8d30488 100644 --- a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.cc +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.cc @@ -23,8 +23,20 @@ WriteResult QuicBatchWriterBase::WritePacket( PerPacketOptions* options) { const WriteResult result = InternalWritePacket(buffer, buf_len, self_address, peer_address, options); - if (result.status == WRITE_STATUS_BLOCKED) { - write_blocked_ = true; + + if (GetQuicReloadableFlag(quic_batch_writer_fix_write_blocked)) { + if (IsWriteBlockedStatus(result.status)) { + if (result.status == WRITE_STATUS_BLOCKED_DATA_BUFFERED) { + QUIC_CODE_COUNT(quic_batch_writer_fix_write_blocked_data_buffered); + } else { + QUIC_CODE_COUNT(quic_batch_writer_fix_write_blocked_data_not_buffered); + } + write_blocked_ = true; + } + } else { + if (result.status == WRITE_STATUS_BLOCKED) { + write_blocked_ = true; + } } return result; } diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.h b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.h index ff924d0481c..e7d1f129684 100644 --- a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.h +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.h @@ -60,7 +60,7 @@ class QUIC_EXPORT_PRIVATE QuicBatchWriterBase : public QuicPacketWriter { const QuicBatchWriterBuffer& batch_buffer() const { return *batch_buffer_; } QuicBatchWriterBuffer& batch_buffer() { return *batch_buffer_; } - const QuicCircularDeque<BufferedWrite>& buffered_writes() const { + const quiche::QuicheCircularDeque<BufferedWrite>& buffered_writes() const { return batch_buffer_->buffered_writes(); } diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.h b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.h index 007c2834cc5..4faa3cba4e7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.h +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.h @@ -6,11 +6,11 @@ #define QUICHE_QUIC_PLATFORM_IMPL_BATCH_WRITER_QUIC_BATCH_WRITER_BUFFER_H_ #include "absl/base/optimization.h" -#include "quic/core/quic_circular_deque.h" #include "quic/core/quic_linux_socket_utils.h" #include "quic/core/quic_packet_writer.h" #include "quic/platform/api/quic_ip_address.h" #include "quic/platform/api/quic_socket_address.h" +#include "common/quiche_circular_deque.h" namespace quic { @@ -61,7 +61,7 @@ class QUIC_EXPORT_PRIVATE QuicBatchWriterBuffer { }; PopResult PopBufferedWrite(int32_t num_buffered_writes); - const QuicCircularDeque<BufferedWrite>& buffered_writes() const { + const quiche::QuicheCircularDeque<BufferedWrite>& buffered_writes() const { return buffered_writes_; } @@ -87,7 +87,7 @@ class QUIC_EXPORT_PRIVATE QuicBatchWriterBuffer { bool Invariants() const; const char* buffer_end() const { return buffer_ + sizeof(buffer_); } ABSL_CACHELINE_ALIGNED char buffer_[kBufferSize]; - QuicCircularDeque<BufferedWrite> buffered_writes_; + quiche::QuicheCircularDeque<BufferedWrite> buffered_writes_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer_test.cc b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer_test.cc index ec08164e529..8a1b93fc723 100644 --- a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer_test.cc @@ -322,6 +322,11 @@ TEST_F(QuicGsoBatchWriterTest, WriteBlockDataBuffered) { })); ASSERT_EQ(WriteResult(WRITE_STATUS_BLOCKED_DATA_BUFFERED, EWOULDBLOCK), WritePacket(&writer, 50)); + if (GetQuicReloadableFlag(quic_batch_writer_fix_write_blocked)) { + EXPECT_TRUE(writer.IsWriteBlocked()); + } else { + EXPECT_FALSE(writer.IsWriteBlocked()); + } ASSERT_EQ(250u, writer.batch_buffer().SizeInUse()); ASSERT_EQ(3u, writer.buffered_writes().size()); } diff --git a/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.cc b/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.cc index 48fe9a82c05..29a43a605e0 100644 --- a/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.cc +++ b/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.cc @@ -17,7 +17,6 @@ #include "quic/core/quic_framer.h" #include "quic/core/quic_types.h" #include "quic/core/quic_utils.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h index 7a34097fd54..6738f49ab05 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h @@ -9,13 +9,13 @@ #include "quic/core/congestion_control/windowed_filter.h" #include "quic/core/packet_number_indexed_queue.h" #include "quic/core/quic_bandwidth.h" -#include "quic/core/quic_circular_deque.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_time.h" #include "quic/core/quic_types.h" #include "quic/core/quic_unacked_packet_map.h" #include "quic/platform/api/quic_export.h" #include "quic/platform/api/quic_flags.h" +#include "common/quiche_circular_deque.h" namespace quic { @@ -540,7 +540,7 @@ class QUIC_EXPORT_PRIVATE BandwidthSampler : public BandwidthSamplerInterface { PacketNumberIndexedQueue<ConnectionStateOnSentPacket> connection_state_map_; RecentAckPoints recent_ack_points_; - QuicCircularDeque<AckPoint> a0_candidates_; + quiche::QuicheCircularDeque<AckPoint> a0_candidates_; // Maximum number of tracked packets. const QuicPacketCount max_tracked_packets_; diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.cc index d24e50efdbc..8d4a49350b5 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.cc @@ -116,9 +116,9 @@ void Bbr2NetworkModel::OnCongestionEventStart( << total_bytes_acked() - prior_bytes_acked << " bytes from " << acked_packets.size() << " packets have been acked, but sample_max_bandwidth is zero."; + congestion_event->sample_max_bandwidth = sample.sample_max_bandwidth; if (!sample.sample_is_app_limited || sample.sample_max_bandwidth > MaxBandwidth()) { - congestion_event->sample_max_bandwidth = sample.sample_max_bandwidth; max_bandwidth_filter_.Update(congestion_event->sample_max_bandwidth); } } @@ -188,99 +188,112 @@ void Bbr2NetworkModel::OnCongestionEventStart( void Bbr2NetworkModel::AdaptLowerBounds( const Bbr2CongestionEvent& congestion_event) { - if (Params().bw_lo_mode_ != Bbr2Params::DEFAULT) { - if (congestion_event.bytes_lost == 0) { + if (Params().bw_lo_mode_ == Bbr2Params::DEFAULT) { + if (!congestion_event.end_of_round_trip || + congestion_event.is_probing_for_bandwidth) { return; } - // Ignore losses from packets sent when probing for more bandwidth in - // STARTUP or PROBE_UP when they're lost in DRAIN or PROBE_DOWN. - if (pacing_gain_ < 1) { - return; - } - // Decrease bandwidth_lo whenever there is loss. - // Set bandwidth_lo_ if it is not yet set. - if (bandwidth_lo_.IsInfinite()) { - bandwidth_lo_ = MaxBandwidth(); - } - // Save bandwidth_lo_ if it hasn't already been saved. - if (prior_bandwidth_lo_.IsZero()) { - prior_bandwidth_lo_ = bandwidth_lo_; - } - switch (Params().bw_lo_mode_) { - case Bbr2Params::MIN_RTT_REDUCTION: - bandwidth_lo_ = - bandwidth_lo_ - QuicBandwidth::FromBytesAndTimeDelta( - congestion_event.bytes_lost, MinRtt()); - break; - case Bbr2Params::INFLIGHT_REDUCTION: { - // Use a max of BDP and inflight to avoid starving app-limited flows. - const QuicByteCount effective_inflight = - std::max(BDP(), congestion_event.prior_bytes_in_flight); - // This could use bytes_lost_in_round if the bandwidth_lo_ was saved - // when entering 'recovery', but this BBRv2 implementation doesn't have - // recovery defined. - bandwidth_lo_ = bandwidth_lo_ * - ((effective_inflight - congestion_event.bytes_lost) / - static_cast<double>(effective_inflight)); - break; + + if (bytes_lost_in_round_ > 0) { + if (bandwidth_lo_.IsInfinite()) { + bandwidth_lo_ = MaxBandwidth(); } - case Bbr2Params::CWND_REDUCTION: - bandwidth_lo_ = - bandwidth_lo_ * - ((congestion_event.prior_cwnd - congestion_event.bytes_lost) / - static_cast<double>(congestion_event.prior_cwnd)); - break; - case Bbr2Params::DEFAULT: - QUIC_BUG(quic_bug_10466_1) << "Unreachable case DEFAULT."; - } - if (pacing_gain_ > Params().startup_full_bw_threshold) { - // In STARTUP, pacing_gain_ is applied to bandwidth_lo_ in - // UpdatePacingRate, so this backs that multiplication out to allow the - // pacing rate to decrease, but not below - // bandwidth_latest_ * startup_full_bw_threshold. - bandwidth_lo_ = - std::max(bandwidth_lo_, - bandwidth_latest_ * - (Params().startup_full_bw_threshold / pacing_gain_)); - } else { - // Ensure bandwidth_lo isn't lower than bandwidth_latest_. - bandwidth_lo_ = std::max(bandwidth_lo_, bandwidth_latest_); - } - // If it's the end of the round, ensure bandwidth_lo doesn't decrease more - // than beta. - if (GetQuicReloadableFlag(quic_bbr2_fix_bw_lo_mode) && - congestion_event.end_of_round_trip) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_fix_bw_lo_mode, 2, 2); bandwidth_lo_ = - std::max(bandwidth_lo_, prior_bandwidth_lo_ * (1.0 - Params().beta)); - prior_bandwidth_lo_ = QuicBandwidth::Zero(); + std::max(bandwidth_latest_, bandwidth_lo_ * (1.0 - Params().beta)); + QUIC_DVLOG(3) << "bandwidth_lo_ updated to " << bandwidth_lo_ + << ", bandwidth_latest_ is " << bandwidth_latest_; + + if (Params().ignore_inflight_lo) { + return; + } + if (inflight_lo_ == inflight_lo_default()) { + inflight_lo_ = congestion_event.prior_cwnd; + } + inflight_lo_ = std::max<QuicByteCount>( + inflight_latest_, inflight_lo_ * (1.0 - Params().beta)); } - // This early return ignores inflight_lo as well. return; } - if (!congestion_event.end_of_round_trip || - congestion_event.is_probing_for_bandwidth) { + + // Params().bw_lo_mode_ != Bbr2Params::DEFAULT + if (congestion_event.bytes_lost == 0) { return; } - - if (bytes_lost_in_round_ > 0) { - if (bandwidth_lo_.IsInfinite()) { - bandwidth_lo_ = MaxBandwidth(); + // Ignore losses from packets sent when probing for more bandwidth in + // STARTUP or PROBE_UP when they're lost in DRAIN or PROBE_DOWN. + if (pacing_gain_ < 1) { + return; + } + // Decrease bandwidth_lo whenever there is loss. + // Set bandwidth_lo_ if it is not yet set. + if (bandwidth_lo_.IsInfinite()) { + bandwidth_lo_ = MaxBandwidth(); + } + // Save bandwidth_lo_ if it hasn't already been saved. + if (prior_bandwidth_lo_.IsZero()) { + prior_bandwidth_lo_ = bandwidth_lo_; + } + switch (Params().bw_lo_mode_) { + case Bbr2Params::MIN_RTT_REDUCTION: + bandwidth_lo_ = + bandwidth_lo_ - QuicBandwidth::FromBytesAndTimeDelta( + congestion_event.bytes_lost, MinRtt()); + break; + case Bbr2Params::INFLIGHT_REDUCTION: { + // Use a max of BDP and inflight to avoid starving app-limited flows. + const QuicByteCount effective_inflight = + std::max(BDP(), congestion_event.prior_bytes_in_flight); + // This could use bytes_lost_in_round if the bandwidth_lo_ was saved + // when entering 'recovery', but this BBRv2 implementation doesn't have + // recovery defined. + bandwidth_lo_ = + bandwidth_lo_ * ((effective_inflight - congestion_event.bytes_lost) / + static_cast<double>(effective_inflight)); + break; } + case Bbr2Params::CWND_REDUCTION: + bandwidth_lo_ = + bandwidth_lo_ * + ((congestion_event.prior_cwnd - congestion_event.bytes_lost) / + static_cast<double>(congestion_event.prior_cwnd)); + break; + case Bbr2Params::DEFAULT: + QUIC_BUG(quic_bug_10466_1) << "Unreachable case DEFAULT."; + } + QuicBandwidth last_bandwidth = bandwidth_latest_; + // sample_max_bandwidth will be Zero() if the loss is triggered by a timer + // expiring. Ideally we'd use the most recent bandwidth sample, + // but bandwidth_latest is safer than Zero(). + if (GetQuicReloadableFlag(quic_bbr2_fix_bw_lo_mode2) && + !congestion_event.sample_max_bandwidth.IsZero()) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_fix_bw_lo_mode2, 1, 2); + // bandwidth_latest_ is the max bandwidth for the round, but to allow + // fast, conservation style response to loss, use the last sample. + last_bandwidth = congestion_event.sample_max_bandwidth; + } + if (pacing_gain_ > Params().startup_full_bw_threshold) { + // In STARTUP, pacing_gain_ is applied to bandwidth_lo_ in + // UpdatePacingRate, so this backs that multiplication out to allow the + // pacing rate to decrease, but not below + // last_bandwidth * startup_full_bw_threshold. + // TODO(ianswett): Consider altering pacing_gain_ when in STARTUP instead. + bandwidth_lo_ = std::max( + bandwidth_lo_, + last_bandwidth * (Params().startup_full_bw_threshold / pacing_gain_)); + } else { + // Ensure bandwidth_lo isn't lower than last_bandwidth. + bandwidth_lo_ = std::max(bandwidth_lo_, last_bandwidth); + } + // If it's the end of the round, ensure bandwidth_lo doesn't decrease more + // than beta. + if (GetQuicReloadableFlag(quic_bbr2_fix_bw_lo_mode) && + congestion_event.end_of_round_trip) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_fix_bw_lo_mode, 2, 2); bandwidth_lo_ = - std::max(bandwidth_latest_, bandwidth_lo_ * (1.0 - Params().beta)); - QUIC_DVLOG(3) << "bandwidth_lo_ updated to " << bandwidth_lo_ - << ", bandwidth_latest_ is " << bandwidth_latest_; - - if (Params().ignore_inflight_lo) { - return; - } - if (inflight_lo_ == inflight_lo_default()) { - inflight_lo_ = congestion_event.prior_cwnd; - } - inflight_lo_ = std::max<QuicByteCount>( - inflight_latest_, inflight_lo_ * (1.0 - Params().beta)); + std::max(bandwidth_lo_, prior_bandwidth_lo_ * (1.0 - Params().beta)); + prior_bandwidth_lo_ = QuicBandwidth::Zero(); } + // These modes ignore inflight_lo as well. } void Bbr2NetworkModel::OnCongestionEventFinish( diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.h index 8f5d4185dfc..b7007f55705 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.h +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_misc.h @@ -92,7 +92,7 @@ struct QUIC_EXPORT_PRIVATE Bbr2Params { // If true, always exit STARTUP on loss, even if bandwidth exceeds threshold. // If false, exit STARTUP on loss only if bandwidth is below threshold. - bool always_exit_startup_on_excess_loss = true; + bool always_exit_startup_on_excess_loss = false; /* * DRAIN parameters. @@ -313,6 +313,8 @@ struct QUIC_EXPORT_PRIVATE Bbr2CongestionEvent { QuicTime::Delta sample_min_rtt = QuicTime::Delta::Infinite(); // Maximum bandwidth of all bandwidth samples from acked_packets. + // This sample may be app-limited, and will be Zero() if there are no newly + // acknowledged inflight packets. QuicBandwidth sample_max_bandwidth = QuicBandwidth::Zero(); // The send state of the largest packet in acked_packets, unless it is empty. diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_probe_bw.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_probe_bw.cc index 9045d7e034b..af0f00fee80 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_probe_bw.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_probe_bw.cc @@ -500,6 +500,15 @@ void Bbr2ProbeBwMode::EnterProbeDown(bool probed_too_high, cycle_.rounds_in_phase = 0; cycle_.phase_start_time = now; ++sender_->connection_stats_->bbr_num_cycles; + if (GetQuicReloadableFlag(quic_bbr2_fix_bw_lo_mode2) && + Params().bw_lo_mode_ != Bbr2Params::QuicBandwidthLoMode::DEFAULT) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_fix_bw_lo_mode2, 2, 2); + // Clear bandwidth lo if it was set in PROBE_UP, because losses in PROBE_UP + // should not permanently change bandwidth_lo. + // It's possible for bandwidth_lo to be set during REFILL, but if that was + // a valid value, it'll quickly be rediscovered. + model_->clear_bandwidth_lo(); + } // Pick probe wait time. cycle_.rounds_since_probe = diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.cc index d0f61277283..b69a7952afa 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_sender.cc @@ -132,7 +132,7 @@ void Bbr2Sender::ApplyConnectionOptions( params_.ignore_inflight_lo = true; } if (ContainsQuicTag(connection_options, kB2NE)) { - params_.always_exit_startup_on_excess_loss = false; + params_.always_exit_startup_on_excess_loss = true; } if (ContainsQuicTag(connection_options, kB2SL)) { params_.startup_loss_exit_use_max_delivered_for_inflight_hi = false; diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_simulator_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_simulator_test.cc index e26760e0bf9..99acf2c3e21 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_simulator_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_simulator_test.cc @@ -417,25 +417,6 @@ TEST_F(Bbr2DefaultTopologyTest, SimpleTransfer) { EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->smoothed_rtt(), 1.0f); } -TEST_F(Bbr2DefaultTopologyTest, SimpleTransferB2NE) { - SetConnectionOption(kB2NE); - DefaultTopologyParams params; - CreateNetwork(params); - - // Transfer 12MB. - DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); - EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); - - EXPECT_APPROX_EQ(params.BottleneckBandwidth(), - sender_->ExportDebugState().bandwidth_hi, 0.01f); - - EXPECT_LE(sender_loss_rate_in_packets(), 0.05); - // The margin here is high, because the aggregation greatly increases - // smoothed rtt. - EXPECT_GE(params.RTT() * 4, rtt_stats()->smoothed_rtt()); - EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f); -} - TEST_F(Bbr2DefaultTopologyTest, SimpleTransferB2RC) { SetConnectionOption(kB2RC); DefaultTopologyParams params; @@ -859,6 +840,42 @@ TEST_F(Bbr2DefaultTopologyTest, ExitStartupDueToLossB2SL) { EXPECT_APPROX_EQ(sender_->ExportDebugState().inflight_hi, params.BDP(), 0.1f); } +// Verifies that in STARTUP, if we exceed loss threshold in a round, we exit +// STARTUP at the end of the round even if there's enough bandwidth growth. +TEST_F(Bbr2DefaultTopologyTest, ExitStartupDueToLossB2NE) { + // Set up flags such that any loss will be considered "too high". + SetQuicFlag(FLAGS_quic_bbr2_default_startup_full_loss_count, 0); + SetQuicFlag(FLAGS_quic_bbr2_default_loss_threshold, 0.0); + + sender_ = SetupBbr2Sender(&sender_endpoint_, /*old_sender=*/nullptr); + + SetConnectionOption(kB2NE); + DefaultTopologyParams params; + params.switch_queue_capacity_in_bdp = 0.5; + CreateNetwork(params); + + // Run until the full bandwidth is reached and check how many rounds it was. + sender_endpoint_.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().bandwidth_hi) { + max_bw = sender_->ExportDebugState().bandwidth_hi; + max_bw_round = sender_->ExportDebugState().round_trip_count; + } + return sender_->ExportDebugState().startup.full_bandwidth_reached; + }, + QuicTime::Delta::FromSeconds(5)); + ASSERT_TRUE(simulator_result); + EXPECT_EQ(Bbr2Mode::DRAIN, sender_->ExportDebugState().mode); + EXPECT_EQ(sender_->ExportDebugState().round_trip_count, max_bw_round); + EXPECT_EQ( + 0u, + sender_->ExportDebugState().startup.round_trips_without_bandwidth_growth); + EXPECT_NE(0u, sender_connection_stats().packets_lost); +} + TEST_F(Bbr2DefaultTopologyTest, SenderPoliced) { DefaultTopologyParams params; params.sender_policer_params = TrafficPolicerParams(); diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_startup.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_startup.cc index 5a9a532e688..1bd89c6024f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_startup.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_startup.cc @@ -112,8 +112,9 @@ void Bbr2StartupMode::CheckExcessiveLosses( new_inflight_hi = model_->max_bytes_delivered_in_round(); } } - QUIC_DVLOG(3) << sender_ << " Exiting STARTUP due to loss. inflight_hi:" - << new_inflight_hi; + QUIC_DVLOG(3) << sender_ << " Exiting STARTUP due to loss at round " + << model_->RoundTripCount() + << ". inflight_hi:" << new_inflight_hi; // TODO(ianswett): Add a shared method to set inflight_hi in the model. model_->set_inflight_hi(new_inflight_hi); model_->set_full_bandwidth_reached(); diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc index c16668b2fdd..dccd7dfa48d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc @@ -13,7 +13,6 @@ #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/test_tools/quiche_test_utils.h" namespace { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc index 5c093ad4ee0..67e35ed8934 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc @@ -13,7 +13,6 @@ #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/test_tools/quiche_test_utils.h" namespace { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter_test.cc index 6877d9de48f..25abd716576 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter_test.cc @@ -12,7 +12,6 @@ #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/test_tools/quiche_test_utils.h" namespace { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter_test.cc index d51c5deb0ea..7e5fb25a937 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter_test.cc @@ -12,7 +12,6 @@ #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/test_tools/quiche_test_utils.h" namespace { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter_test.cc index bfafd2cb513..c63a589a3d8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter_test.cc @@ -13,7 +13,6 @@ #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/test_tools/quiche_test_utils.h" namespace { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter_test.cc index 8d4c1de8e16..a2f8066717c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter_test.cc @@ -13,7 +13,6 @@ #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/test_tools/quiche_test_utils.h" namespace { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/cert_compressor_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/cert_compressor_test.cc index b76ee7bf8d4..a024d131c2a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/cert_compressor_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/cert_compressor_test.cc @@ -12,7 +12,6 @@ #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/crypto_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { namespace test { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/certificate_view.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/certificate_view.cc index c21dd202b2d..4024a78e952 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/certificate_view.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/certificate_view.cc @@ -30,9 +30,9 @@ #include "quic/platform/api/quic_bug_tracker.h" #include "quic/platform/api/quic_ip_address.h" #include "quic/platform/api/quic_logging.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/platform/api/quiche_time_utils.h" #include "common/quiche_data_reader.h" +#include "common/quiche_text_utils.h" namespace quic { namespace { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter_test.cc index 42d8899a781..93e48d257d0 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter_test.cc @@ -12,7 +12,6 @@ #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/test_tools/quiche_test_utils.h" namespace { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter_test.cc index 8c403fede35..7fc32822f7e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter_test.cc @@ -8,12 +8,12 @@ #include <string> #include "absl/base/macros.h" +#include "absl/strings/escaping.h" #include "absl/strings/string_view.h" #include "quic/core/crypto/chacha20_poly1305_decrypter.h" #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/test_tools/quiche_test_utils.h" namespace { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc index 4b0f10bcd0d..b71bcf127f8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc @@ -12,7 +12,6 @@ #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/test_tools/quiche_test_utils.h" namespace { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc index 7bca425a708..61aaac73d17 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc @@ -14,7 +14,6 @@ #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/test_tools/quiche_test_utils.h" namespace { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_message_printer_bin.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_message_printer_bin.cc index 3109924d59e..68e3d9cbe19 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_message_printer_bin.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_message_printer_bin.cc @@ -13,7 +13,6 @@ #include "absl/strings/escaping.h" #include "quic/core/crypto/crypto_framer.h" #include "quic/core/quic_utils.h" -#include "common/platform/api/quiche_text_utils.h" using std::cerr; using std::cout; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h index e08d90b1c08..af62865b653 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h @@ -28,7 +28,7 @@ using ServerConfigID = std::string; // The following tags have been deprecated and should not be reused: // "1CON", "BBQ4", "NCON", "RCID", "SREJ", "TBKP", "TB10", "SCLS", "SMHL", -// "QNZR", "B2HI", "H2PR", "FIFO", "LIFO", "RRWS" +// "QNZR", "B2HI", "H2PR", "FIFO", "LIFO", "RRWS", "QNSP" // clang-format off const QuicTag kCHLO = TAG('C', 'H', 'L', 'O'); // Client hello @@ -127,9 +127,10 @@ const QuicTag kIW50 = TAG('I', 'W', '5', '0'); // Force ICWND to 50 const QuicTag kB2ON = TAG('B', '2', 'O', 'N'); // Enable BBRv2 const QuicTag kB2NA = TAG('B', '2', 'N', 'A'); // For BBRv2, do not add ack // height to queueing threshold -const QuicTag kB2NE = TAG('B', '2', 'N', 'E'); // For BBRv2, do not exit - // STARTUP if there's enough - // bandwidth growth +const QuicTag kB2NE = TAG('B', '2', 'N', 'E'); // For BBRv2, always exit + // STARTUP on loss, even if + // bandwidth growth exceeds + // threshold. const QuicTag kB2RP = TAG('B', '2', 'R', 'P'); // For BBRv2, run PROBE_RTT on // the regular schedule const QuicTag kB2CL = TAG('B', '2', 'C', 'L'); // For BBRv2, allow PROBE_BW @@ -342,6 +343,7 @@ const QuicTag kMTUL = TAG('M', 'T', 'U', 'L'); // Low-target MTU discovery. const QuicTag kNSLC = TAG('N', 'S', 'L', 'C'); // Always send connection close // for idle timeout. +const QuicTag kCHSP = TAG('C', 'H', 'S', 'P'); // Chaos protection. // Proof types (i.e. certificate types) // NOTE: although it would be silly to do so, specifying both kX509 and kX59R @@ -401,9 +403,6 @@ const QuicTag kPDP5 = TAG('P', 'D', 'P', '5'); // Path degrading triggered const QuicTag kQNZ2 = TAG('Q', 'N', 'Z', '2'); // Turn off QUIC crypto 0-RTT. -const QuicTag kQNSP = TAG('Q', 'N', 'S', 'P'); // Turn off server push in - // gQUIC. - const QuicTag kMAD = TAG('M', 'A', 'D', 0); // Max Ack Delay (IETF QUIC) const QuicTag kIGNP = TAG('I', 'G', 'N', 'P'); // Do not use PING only packet diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc index 7414eda8692..7fadbbdcef7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc @@ -33,7 +33,6 @@ #include "quic/test_tools/mock_random.h" #include "quic/test_tools/quic_crypto_server_config_peer.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/quiche_endian.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.cc index 2c8435035c9..67527d45826 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.cc @@ -762,4 +762,21 @@ std::string CryptoUtils::HashHandshakeMessage( } #undef RETURN_STRING_LITERAL // undef for jumbo builds + +// static +bool CryptoUtils::GetSSLCapabilities(const SSL* ssl, + bssl::UniquePtr<uint8_t>* capabilities, + size_t* capabilities_len) { + uint8_t* buffer; + CBB cbb; + + if (!CBB_init(&cbb, 128) || !SSL_serialize_capabilities(ssl, &cbb) || + !CBB_finish(&cbb, &buffer, capabilities_len)) { + return false; + } + + *capabilities = bssl::UniquePtr<uint8_t>(buffer); + return true; +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.h b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.h index ec68c6580f4..7aa32e1445a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.h @@ -237,6 +237,11 @@ class QUIC_EXPORT_PRIVATE CryptoUtils { // Returns a hash of the serialized |message|. static std::string HashHandshakeMessage(const CryptoHandshakeMessage& message, Perspective perspective); + + // Wraps SSL_serialize_capabilities. Return nullptr if failed. + static bool GetSSLCapabilities(const SSL* ssl, + bssl::UniquePtr<uint8_t>* capabilities, + size_t* capabilities_len); }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils_test.cc index 4586b9bbb4a..0f2df3a7f90 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils_test.cc @@ -11,7 +11,6 @@ #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/test_tools/quiche_test_utils.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.h b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.h index cee94f2e4af..31cbfd66114 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.h @@ -229,12 +229,15 @@ class QUIC_EXPORT_PRIVATE ProofSourceHandleCallback { // whether it is completed before ProofSourceHandle::SelectCertificate // returned. // |chain| the certificate chain in leaf-first order. + // |handshake_hints| (optional) handshake hints that can be used by + // SSL_set_handshake_hints. // // When called asynchronously(is_sync=false), this method will be responsible // to continue the handshake from where it left off. virtual void OnSelectCertificateDone(bool ok, bool is_sync, - const ProofSource::Chain* chain) = 0; + const ProofSource::Chain* chain, + absl::string_view handshake_hints) = 0; // Called when a ProofSourceHandle::ComputeSignature operation completes. virtual void OnComputeSignatureDone( @@ -280,9 +283,11 @@ class QUIC_EXPORT_PRIVATE ProofSourceHandle { virtual QuicAsyncStatus SelectCertificate( const QuicSocketAddress& server_address, const QuicSocketAddress& client_address, + absl::string_view ssl_capabilities, const std::string& hostname, absl::string_view client_hello, const std::string& alpn, + absl::optional<std::string> alps, const std::vector<uint8_t>& quic_transport_params, const absl::optional<std::vector<uint8_t>>& early_data_context) = 0; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.cc index c886cbe989e..3f94e7cda60 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.cc @@ -34,7 +34,6 @@ #include "quic/platform/api/quic_hostname_utils.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_map_util.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.cc index 4f19533b008..f2a88095aac 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.cc @@ -51,7 +51,6 @@ #include "quic/platform/api/quic_reference_counted.h" #include "quic/platform/api/quic_socket_address.h" #include "quic/platform/api/quic_testvalue.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { @@ -745,9 +744,7 @@ void QuicCryptoServerConfig::ProcessClientHelloAfterGetProof( << context->connection_id() << " which is invalid with version " << context->version(); - if (context->validate_chlo_result()->postpone_cert_validate_for_server && - context->info().reject_reasons.empty()) { - QUIC_RELOADABLE_FLAG_COUNT(quic_crypto_postpone_cert_validate_for_server); + if (context->info().reject_reasons.empty()) { if (!context->signed_config() || !context->signed_config()->chain) { // No chain. context->validate_chlo_result()->info.reject_reasons.push_back( @@ -1224,8 +1221,8 @@ void QuicCryptoServerConfig::SelectNewPrimaryConfig( } void QuicCryptoServerConfig::EvaluateClientHello( - const QuicSocketAddress& server_address, - const QuicSocketAddress& client_address, + const QuicSocketAddress& /*server_address*/, + const QuicSocketAddress& /*client_address*/, QuicTransportVersion /*version*/, const Configs& configs, QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> @@ -1294,17 +1291,6 @@ void QuicCryptoServerConfig::EvaluateClientHello( // No valid source address token. } - if (!client_hello_state->postpone_cert_validate_for_server) { - QuicReferenceCountedPointer<ProofSource::Chain> chain = - proof_source_->GetCertChain(server_address, client_address, - std::string(info->sni)); - if (!chain) { - info->reject_reasons.push_back(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE); - } else if (!ValidateExpectedLeafCertificate(client_hello, chain->certs)) { - info->reject_reasons.push_back(INVALID_EXPECTED_LEAF_CERTIFICATE); - } - } - if (info->client_nonce.size() != kNonceSize) { info->reject_reasons.push_back(CLIENT_NONCE_INVALID_FAILURE); // Invalid client nonce. diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h index 464f2bbfc36..4f3ad0819e4 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h @@ -98,9 +98,6 @@ class QUIC_EXPORT_PRIVATE ValidateClientHelloResultCallback { // Populated if the CHLO STK contained a CachedNetworkParameters proto. CachedNetworkParameters cached_network_params; - const bool postpone_cert_validate_for_server = - GetQuicReloadableFlag(quic_crypto_postpone_cert_validate_for_server); - protected: ~Result() override; }; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config_test.cc index 9f70c355237..484e46eee6a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config_test.cc @@ -25,7 +25,6 @@ #include "quic/test_tools/mock_clock.h" #include "quic/test_tools/quic_crypto_server_config_peer.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { namespace test { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_hkdf_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_hkdf_test.cc index 73f850ed68a..e23d3f2a1e9 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_hkdf_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_hkdf_test.cc @@ -9,7 +9,6 @@ #include "absl/base/macros.h" #include "absl/strings/escaping.h" #include "quic/platform/api/quic_test.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { namespace test { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.cc index 8926909cd78..6e9901b995e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.cc @@ -30,16 +30,12 @@ bssl::UniquePtr<SSL_CTX> TlsServerConnection::CreateSslCtx( QUIC_CODE_COUNT(quic_session_tickets_enabled); SSL_CTX_set_ticket_aead_method(ssl_ctx.get(), &TlsServerConnection::kSessionTicketMethod); - } else if (!GetQuicRestartFlag(quic_session_tickets_always_enabled)) { - QUIC_CODE_COUNT(quic_session_tickets_disabled_by_flag); - SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_NO_TICKET); } else { QUIC_CODE_COUNT(quic_session_tickets_disabled); } - if (proof_source->GetTicketCrypter() || - GetQuicRestartFlag(quic_session_tickets_always_enabled)) { - SSL_CTX_set_early_data_enabled(ssl_ctx.get(), 1); - } + + SSL_CTX_set_early_data_enabled(ssl_ctx.get(), 1); + SSL_CTX_set_select_certificate_cb( ssl_ctx.get(), &TlsServerConnection::EarlySelectCertCallback); SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE); diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_token_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_token_frame.cc index 8ad442fc694..8642fd93265 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_token_frame.cc +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_token_frame.cc @@ -6,7 +6,6 @@ #include "absl/strings/escaping.h" #include "quic/platform/api/quic_logging.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.cc index 857ca19291f..0f88bb83160 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.cc +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.cc @@ -6,7 +6,6 @@ #include "absl/strings/escaping.h" #include "quic/platform/api/quic_bug_tracker.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.cc index ade4156b8ab..f802b131b30 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.cc +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.cc @@ -6,7 +6,6 @@ #include "absl/strings/escaping.h" #include "quic/platform/api/quic_bug_tracker.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/handshaker_delegate_interface.h b/chromium/net/third_party/quiche/src/quic/core/handshaker_delegate_interface.h index ae291b7826f..1e47d16a5f1 100644 --- a/chromium/net/third_party/quiche/src/quic/core/handshaker_delegate_interface.h +++ b/chromium/net/third_party/quiche/src/quic/core/handshaker_delegate_interface.h @@ -74,6 +74,9 @@ class QUIC_EXPORT_PRIVATE HandshakerDelegateInterface { // Called at the end of an handshake operation callback. virtual void OnHandshakeCallbackDone() = 0; + + // Whether a packet flusher is currently attached. + virtual bool PacketFlusherAttached() const = 0; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/end_to_end_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/end_to_end_test.cc index aceebe1b33b..8efba8869bf 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/end_to_end_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/end_to_end_test.cc @@ -17,6 +17,7 @@ #include "quic/core/http/http_constants.h" #include "quic/core/http/quic_spdy_client_stream.h" #include "quic/core/http/web_transport_http3.h" +#include "quic/core/quic_connection.h" #include "quic/core/quic_data_writer.h" #include "quic/core/quic_epoll_connection_helper.h" #include "quic/core/quic_error_codes.h" @@ -43,7 +44,6 @@ #include "quic/test_tools/packet_reordering_writer.h" #include "quic/test_tools/qpack/qpack_encoder_peer.h" #include "quic/test_tools/qpack/qpack_encoder_test_utils.h" -#include "quic/test_tools/qpack/qpack_header_table_peer.h" #include "quic/test_tools/qpack/qpack_test_utils.h" #include "quic/test_tools/quic_client_peer.h" #include "quic/test_tools/quic_config_peer.h" @@ -80,6 +80,7 @@ using ::testing::_; using ::testing::Assign; using ::testing::Invoke; using ::testing::NiceMock; +using testing::NotNull; namespace quic { namespace test { @@ -750,6 +751,18 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { } } + void WaitForNewConnectionIds() { + // Wait until a new server CID is available for another migration. + const auto* client_connection = GetClientConnection(); + while (!QuicConnectionPeer::HasUnusedPeerIssuedConnectionId( + client_connection) || + (!client_connection->client_connection_id().IsEmpty() && + !QuicConnectionPeer::HasSelfIssuedConnectionIdToConsume( + client_connection))) { + client_->client()->WaitForEvents(); + } + } + ScopedEnvironmentForThreads environment_; bool initialized_; // If true, the Initialize() function will create |client_| and starts to @@ -1552,17 +1565,11 @@ TEST_P(EndToEndTest, AddressToken) { server_thread_->Pause(); QuicConnection* server_connection = GetServerConnection(); if (server_connection != nullptr) { - if (GetQuicReloadableFlag(quic_enable_token_based_address_validation)) { - // Verify address is validated via validating token received in INITIAL - // packet. - EXPECT_FALSE(server_connection->GetStats() - .address_validated_via_decrypting_packet); - EXPECT_TRUE(server_connection->GetStats().address_validated_via_token); - } else { - EXPECT_TRUE(server_connection->GetStats() - .address_validated_via_decrypting_packet); - EXPECT_FALSE(server_connection->GetStats().address_validated_via_token); - } + // Verify address is validated via validating token received in INITIAL + // packet. + EXPECT_FALSE( + server_connection->GetStats().address_validated_via_decrypting_packet); + EXPECT_TRUE(server_connection->GetStats().address_validated_via_token); } else { ADD_FAILURE() << "Missing server connection"; } @@ -2477,10 +2484,7 @@ TEST_P( HalfRttResponseBlocksShloRetransmissionWithoutTokenBasedAddressValidation) { // Turn off token based address validation to make the server get constrained // by amplification factor during handshake. - // TODO(fayang): Keep this test while deprecating - // quic_enable_token_based_address_validation. For example, consider always - // rejecting the received address token. - SetQuicReloadableFlag(quic_enable_token_based_address_validation, false); + SetQuicFlag(FLAGS_quic_reject_retry_token_in_initial_packet, true); ASSERT_TRUE(Initialize()); if (!version_.SupportsAntiAmplificationLimit()) { return; @@ -2502,17 +2506,8 @@ TEST_P( // Large response (100KB) for 0-RTT request. std::string large_body(102400, 'a'); AddToCache("/large_response", 200, large_body); - if (GetQuicReloadableFlag(quic_preempt_stream_data_with_handshake_packet)) { - SendSynchronousRequestAndCheckResponse(client_.get(), "/large_response", - large_body); - } else { - // Server consistently gets constrained by amplification factor, hence PTO - // never gets armed. The CHLO retransmission would trigger the - // retransmission of SHLO, however, the ENCRYPTION_HANDSHAKE packet NEVER - // gets retransmitted since half RTT data consumes the remaining space in - // the coalescer. - EXPECT_EQ("", client_->SendSynchronousRequest("/large_response")); - } + SendSynchronousRequestAndCheckResponse(client_.get(), "/large_response", + large_body); } TEST_P(EndToEndTest, MaxStreamsUberTest) { @@ -2620,6 +2615,253 @@ TEST_P(EndToEndTest, ConnectionMigrationClientIPChanged) { server_thread_->Resume(); } +TEST_P(EndToEndTest, IetfConnectionMigrationClientIPChangedMultipleTimes) { + ASSERT_TRUE(Initialize()); + if (!GetClientConnection()->connection_migration_use_new_cid()) { + return; + } + SendSynchronousFooRequestAndCheckResponse(); + + // Store the client IP address which was used to send the first request. + QuicIpAddress host0 = + client_->client()->network_helper()->GetLatestClientAddress().host(); + QuicConnection* client_connection = GetClientConnection(); + ASSERT_TRUE(client_connection != nullptr); + + // Migrate socket to a new IP address. + QuicIpAddress host1 = TestLoopback(2); + EXPECT_NE(host0, host1); + ASSERT_TRUE( + QuicConnectionPeer::HasUnusedPeerIssuedConnectionId(client_connection)); + QuicConnectionId server_cid0 = client_connection->connection_id(); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + EXPECT_TRUE(client_->client()->MigrateSocket(host1)); + QuicConnectionId server_cid1 = client_connection->connection_id(); + EXPECT_FALSE(server_cid1.IsEmpty()); + EXPECT_NE(server_cid0, server_cid1); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + + // Send a request using the new socket. + SendSynchronousBarRequestAndCheckResponse(); + EXPECT_EQ(1u, + client_connection->GetStats().num_connectivity_probing_received); + + // Send another request and wait for response making sure path response is + // received at server. + SendSynchronousBarRequestAndCheckResponse(); + + // Migrate socket to a new IP address. + WaitForNewConnectionIds(); + EXPECT_EQ(1u, client_connection->GetStats().num_retire_connection_id_sent); + QuicIpAddress host2 = TestLoopback(3); + EXPECT_NE(host0, host2); + EXPECT_NE(host1, host2); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + EXPECT_TRUE(client_->client()->MigrateSocket(host2)); + QuicConnectionId server_cid2 = client_connection->connection_id(); + EXPECT_FALSE(server_cid2.IsEmpty()); + EXPECT_NE(server_cid0, server_cid2); + EXPECT_NE(server_cid1, server_cid2); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + + // Send another request using the new socket and wait for response making sure + // path response is received at server. + SendSynchronousBarRequestAndCheckResponse(); + EXPECT_EQ(2u, + client_connection->GetStats().num_connectivity_probing_received); + + // Migrate socket back to an old IP address. + WaitForNewConnectionIds(); + EXPECT_EQ(2u, client_connection->GetStats().num_retire_connection_id_sent); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + EXPECT_TRUE(client_->client()->MigrateSocket(host1)); + QuicConnectionId server_cid3 = client_connection->connection_id(); + EXPECT_FALSE(server_cid3.IsEmpty()); + EXPECT_NE(server_cid0, server_cid3); + EXPECT_NE(server_cid1, server_cid3); + EXPECT_NE(server_cid2, server_cid3); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + const auto* client_packet_creator = + QuicConnectionPeer::GetPacketCreator(client_connection); + EXPECT_TRUE(client_packet_creator->GetClientConnectionId().IsEmpty()); + EXPECT_EQ(server_cid3, client_packet_creator->GetServerConnectionId()); + + // Send another request using the new socket and wait for response making sure + // path response is received at server. + SendSynchronousBarRequestAndCheckResponse(); + // Even this is an old path, server has forgotten about it and thus needs to + // validate the path again. + EXPECT_EQ(3u, + client_connection->GetStats().num_connectivity_probing_received); + + WaitForNewConnectionIds(); + EXPECT_EQ(3u, client_connection->GetStats().num_retire_connection_id_sent); + + server_thread_->Pause(); + QuicConnection* server_connection = GetServerConnection(); + // By the time the 2nd request is completed, the PATH_RESPONSE must have been + // received by the server. + EXPECT_FALSE(server_connection->HasPendingPathValidation()); + EXPECT_EQ(3u, server_connection->GetStats().num_validated_peer_migration); + EXPECT_EQ(server_cid3, server_connection->connection_id()); + const auto* server_packet_creator = + QuicConnectionPeer::GetPacketCreator(server_connection); + EXPECT_EQ(server_cid3, server_packet_creator->GetServerConnectionId()); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + server_connection) + .IsEmpty()); + EXPECT_EQ(4u, server_connection->GetStats().num_new_connection_id_sent); + server_thread_->Resume(); +} + +TEST_P(EndToEndTest, + ConnectionMigrationWithNonZeroConnectionIDClientIPChangedMultipleTimes) { + if (!version_.SupportsClientConnectionIds()) { + ASSERT_TRUE(Initialize()); + return; + } + override_client_connection_id_length_ = kQuicDefaultConnectionIdLength; + ASSERT_TRUE(Initialize()); + if (!GetClientConnection()->connection_migration_use_new_cid()) { + return; + } + SendSynchronousFooRequestAndCheckResponse(); + + // Store the client IP address which was used to send the first request. + QuicIpAddress host0 = + client_->client()->network_helper()->GetLatestClientAddress().host(); + QuicConnection* client_connection = GetClientConnection(); + ASSERT_TRUE(client_connection != nullptr); + + // Migrate socket to a new IP address. + QuicIpAddress host1 = TestLoopback(2); + EXPECT_NE(host0, host1); + ASSERT_TRUE( + QuicConnectionPeer::HasUnusedPeerIssuedConnectionId(client_connection)); + QuicConnectionId server_cid0 = client_connection->connection_id(); + QuicConnectionId client_cid0 = client_connection->client_connection_id(); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + EXPECT_TRUE(QuicConnectionPeer::GetClientConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + EXPECT_TRUE(client_->client()->MigrateSocket(host1)); + QuicConnectionId server_cid1 = client_connection->connection_id(); + QuicConnectionId client_cid1 = client_connection->client_connection_id(); + EXPECT_FALSE(server_cid1.IsEmpty()); + EXPECT_FALSE(client_cid1.IsEmpty()); + EXPECT_NE(server_cid0, server_cid1); + EXPECT_NE(client_cid0, client_cid1); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + EXPECT_TRUE(QuicConnectionPeer::GetClientConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + + // Send another request to ensure that the server will have time to finish the + // reverse path validation and send address token. + SendSynchronousBarRequestAndCheckResponse(); + EXPECT_EQ(1u, + client_connection->GetStats().num_connectivity_probing_received); + + // Migrate socket to a new IP address. + WaitForNewConnectionIds(); + EXPECT_EQ(1u, client_connection->GetStats().num_retire_connection_id_sent); + EXPECT_EQ(2u, client_connection->GetStats().num_new_connection_id_sent); + QuicIpAddress host2 = TestLoopback(3); + EXPECT_NE(host0, host2); + EXPECT_NE(host1, host2); + EXPECT_TRUE(client_->client()->MigrateSocket(host2)); + QuicConnectionId server_cid2 = client_connection->connection_id(); + QuicConnectionId client_cid2 = client_connection->client_connection_id(); + EXPECT_FALSE(server_cid2.IsEmpty()); + EXPECT_NE(server_cid0, server_cid2); + EXPECT_NE(server_cid1, server_cid2); + EXPECT_FALSE(client_cid2.IsEmpty()); + EXPECT_NE(client_cid0, client_cid2); + EXPECT_NE(client_cid1, client_cid2); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + EXPECT_TRUE(QuicConnectionPeer::GetClientConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + + // Send another request to ensure that the server will have time to finish the + // reverse path validation and send address token. + SendSynchronousBarRequestAndCheckResponse(); + EXPECT_EQ(2u, + client_connection->GetStats().num_connectivity_probing_received); + + // Migrate socket back to an old IP address. + WaitForNewConnectionIds(); + EXPECT_EQ(2u, client_connection->GetStats().num_retire_connection_id_sent); + EXPECT_EQ(3u, client_connection->GetStats().num_new_connection_id_sent); + EXPECT_TRUE(client_->client()->MigrateSocket(host1)); + QuicConnectionId server_cid3 = client_connection->connection_id(); + QuicConnectionId client_cid3 = client_connection->client_connection_id(); + EXPECT_FALSE(server_cid3.IsEmpty()); + EXPECT_NE(server_cid0, server_cid3); + EXPECT_NE(server_cid1, server_cid3); + EXPECT_NE(server_cid2, server_cid3); + EXPECT_FALSE(client_cid3.IsEmpty()); + EXPECT_NE(client_cid0, client_cid3); + EXPECT_NE(client_cid1, client_cid3); + EXPECT_NE(client_cid2, client_cid3); + const auto* client_packet_creator = + QuicConnectionPeer::GetPacketCreator(client_connection); + EXPECT_EQ(client_cid3, client_packet_creator->GetClientConnectionId()); + EXPECT_EQ(server_cid3, client_packet_creator->GetServerConnectionId()); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + + // Send another request to ensure that the server will have time to finish the + // reverse path validation and send address token. + SendSynchronousBarRequestAndCheckResponse(); + // Even this is an old path, server has forgotten about it and thus needs to + // validate the path again. + EXPECT_EQ(3u, + client_connection->GetStats().num_connectivity_probing_received); + + WaitForNewConnectionIds(); + EXPECT_EQ(3u, client_connection->GetStats().num_retire_connection_id_sent); + EXPECT_EQ(4u, client_connection->GetStats().num_new_connection_id_sent); + + server_thread_->Pause(); + // By the time the 2nd request is completed, the PATH_RESPONSE must have been + // received by the server. + QuicConnection* server_connection = GetServerConnection(); + EXPECT_FALSE(server_connection->HasPendingPathValidation()); + EXPECT_EQ(3u, server_connection->GetStats().num_validated_peer_migration); + EXPECT_EQ(server_cid3, server_connection->connection_id()); + EXPECT_EQ(client_cid3, server_connection->client_connection_id()); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + server_connection) + .IsEmpty()); + const auto* server_packet_creator = + QuicConnectionPeer::GetPacketCreator(server_connection); + EXPECT_EQ(client_cid3, server_packet_creator->GetClientConnectionId()); + EXPECT_EQ(server_cid3, server_packet_creator->GetServerConnectionId()); + EXPECT_EQ(3u, server_connection->GetStats().num_retire_connection_id_sent); + EXPECT_EQ(4u, server_connection->GetStats().num_new_connection_id_sent); + server_thread_->Resume(); +} + TEST_P(EndToEndTest, ConnectionMigrationNewTokenForNewIp) { ASSERT_TRUE(Initialize()); if (!version_.HasIetfQuicFrames() || @@ -2644,7 +2886,7 @@ TEST_P(EndToEndTest, ConnectionMigrationNewTokenForNewIp) { EXPECT_EQ(1u, client_connection->GetStats().num_connectivity_probing_received); - // Send another request to ensure that the server will time to finish the + // Send another request to ensure that the server will have time to finish the // reverse path validation and send address token. SendSynchronousBarRequestAndCheckResponse(); @@ -2661,17 +2903,11 @@ TEST_P(EndToEndTest, ConnectionMigrationNewTokenForNewIp) { server_thread_->Pause(); QuicConnection* server_connection = GetServerConnection(); if (server_connection != nullptr) { - if (GetQuicReloadableFlag(quic_enable_token_based_address_validation)) { - // Verify address is validated via validating token received in INITIAL - // packet. - EXPECT_FALSE(server_connection->GetStats() - .address_validated_via_decrypting_packet); - EXPECT_TRUE(server_connection->GetStats().address_validated_via_token); - } else { - EXPECT_TRUE(server_connection->GetStats() - .address_validated_via_decrypting_packet); - EXPECT_FALSE(server_connection->GetStats().address_validated_via_token); - } + // Verify address is validated via validating token received in INITIAL + // packet. + EXPECT_FALSE( + server_connection->GetStats().address_validated_via_decrypting_packet); + EXPECT_TRUE(server_connection->GetStats().address_validated_via_token); } else { ADD_FAILURE() << "Missing server connection"; } @@ -2709,14 +2945,19 @@ class DuplicatePacketWithSpoofedSelfAddressWriter TEST_P(EndToEndTest, ClientAddressSpoofedForSomePeriod) { ASSERT_TRUE(Initialize()); - if (!version_.HasIetfQuicFrames() || - !client_->client()->session()->connection()->validate_client_address()) { + if (!GetClientConnection()->connection_migration_use_new_cid()) { return; } auto writer = new DuplicatePacketWithSpoofedSelfAddressWriter(); client_.reset(CreateQuicClient(writer)); + + // Make sure client has unused peer connection ID before migration. + SendSynchronousFooRequestAndCheckResponse(); + ASSERT_TRUE(QuicConnectionPeer::HasUnusedPeerIssuedConnectionId( + GetClientConnection())); + QuicIpAddress real_host = TestLoopback(1); - client_->MigrateSocket(real_host); + ASSERT_TRUE(client_->MigrateSocket(real_host)); SendSynchronousFooRequestAndCheckResponse(); EXPECT_EQ( 0u, GetClientConnection()->GetStats().num_connectivity_probing_received); @@ -2755,10 +2996,107 @@ TEST_P(EndToEndTest, ClientAddressSpoofedForSomePeriod) { EXPECT_EQ(large_body, client_->response_body()); } -TEST_P(EndToEndTest, AsynchronousConnectionMigrationClientIPChanged) { +TEST_P(EndToEndTest, + AsynchronousConnectionMigrationClientIPChangedMultipleTimes) { ASSERT_TRUE(Initialize()); - if (!version_.HasIetfQuicFrames() || - !client_->client()->session()->connection()->use_path_validator()) { + if (!GetClientConnection()->connection_migration_use_new_cid()) { + return; + } + client_.reset(CreateQuicClient(nullptr)); + + SendSynchronousFooRequestAndCheckResponse(); + + // Store the client IP address which was used to send the first request. + QuicIpAddress host0 = + client_->client()->network_helper()->GetLatestClientAddress().host(); + QuicConnection* client_connection = GetClientConnection(); + QuicConnectionId server_cid0 = client_connection->connection_id(); + // Server should have one new connection ID upon handshake completion. + ASSERT_TRUE( + QuicConnectionPeer::HasUnusedPeerIssuedConnectionId(client_connection)); + + // Migrate socket to new IP address #1. + QuicIpAddress host1 = TestLoopback(2); + EXPECT_NE(host0, host1); + ASSERT_TRUE(client_->client()->ValidateAndMigrateSocket(host1)); + while (client_->client()->HasPendingPathValidation()) { + client_->client()->WaitForEvents(); + } + EXPECT_EQ(host1, client_->client()->session()->self_address().host()); + EXPECT_EQ(1u, + client_connection->GetStats().num_connectivity_probing_received); + QuicConnectionId server_cid1 = client_connection->connection_id(); + EXPECT_NE(server_cid0, server_cid1); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + + // Send a request using the new socket. + SendSynchronousBarRequestAndCheckResponse(); + + // Migrate socket to new IP address #2. + WaitForNewConnectionIds(); + QuicIpAddress host2 = TestLoopback(3); + EXPECT_NE(host0, host1); + ASSERT_TRUE(client_->client()->ValidateAndMigrateSocket(host2)); + + while (client_->client()->HasPendingPathValidation()) { + client_->client()->WaitForEvents(); + } + EXPECT_EQ(host2, client_->client()->session()->self_address().host()); + EXPECT_EQ(2u, + client_connection->GetStats().num_connectivity_probing_received); + QuicConnectionId server_cid2 = client_connection->connection_id(); + EXPECT_NE(server_cid0, server_cid2); + EXPECT_NE(server_cid1, server_cid2); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + + // Send a request using the new socket. + SendSynchronousBarRequestAndCheckResponse(); + + // Migrate socket back to IP address #1. + WaitForNewConnectionIds(); + ASSERT_TRUE(client_->client()->ValidateAndMigrateSocket(host1)); + + while (client_->client()->HasPendingPathValidation()) { + client_->client()->WaitForEvents(); + } + EXPECT_EQ(host1, client_->client()->session()->self_address().host()); + EXPECT_EQ(3u, + client_connection->GetStats().num_connectivity_probing_received); + QuicConnectionId server_cid3 = client_connection->connection_id(); + EXPECT_NE(server_cid0, server_cid3); + EXPECT_NE(server_cid1, server_cid3); + EXPECT_NE(server_cid2, server_cid3); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + + // Send a request using the new socket. + SendSynchronousBarRequestAndCheckResponse(); + server_thread_->Pause(); + const QuicConnection* server_connection = GetServerConnection(); + EXPECT_EQ(server_connection->connection_id(), server_cid3); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + server_connection) + .IsEmpty()); + server_thread_->Resume(); + + // There should be 1 new connection ID issued by the server. + WaitForNewConnectionIds(); +} + +TEST_P(EndToEndTest, + AsynchronousConnectionMigrationClientIPChangedWithNonEmptyClientCID) { + if (!version_.SupportsClientConnectionIds()) { + ASSERT_TRUE(Initialize()); + return; + } + override_client_connection_id_length_ = kQuicDefaultConnectionIdLength; + ASSERT_TRUE(Initialize()); + if (!GetClientConnection()->connection_migration_use_new_cid()) { return; } client_.reset(CreateQuicClient(nullptr)); @@ -2768,6 +3106,9 @@ TEST_P(EndToEndTest, AsynchronousConnectionMigrationClientIPChanged) { // Store the client IP address which was used to send the first request. QuicIpAddress old_host = client_->client()->network_helper()->GetLatestClientAddress().host(); + auto* client_connection = GetClientConnection(); + QuicConnectionId client_cid0 = client_connection->client_connection_id(); + QuicConnectionId server_cid0 = client_connection->connection_id(); // Migrate socket to the new IP address. QuicIpAddress new_host = TestLoopback(2); @@ -2778,12 +3119,26 @@ TEST_P(EndToEndTest, AsynchronousConnectionMigrationClientIPChanged) { client_->client()->WaitForEvents(); } EXPECT_EQ(new_host, client_->client()->session()->self_address().host()); - QuicConnection* client_connection = GetClientConnection(); - ASSERT_TRUE(client_connection); - EXPECT_EQ(client_connection->validate_client_address() ? 1u : 0, + EXPECT_EQ(1u, client_connection->GetStats().num_connectivity_probing_received); + QuicConnectionId client_cid1 = client_connection->client_connection_id(); + QuicConnectionId server_cid1 = client_connection->connection_id(); + const auto* client_packet_creator = + QuicConnectionPeer::GetPacketCreator(client_connection); + EXPECT_EQ(client_cid1, client_packet_creator->GetClientConnectionId()); + EXPECT_EQ(server_cid1, client_packet_creator->GetServerConnectionId()); // Send a request using the new socket. SendSynchronousBarRequestAndCheckResponse(); + + server_thread_->Pause(); + QuicConnection* server_connection = GetServerConnection(); + EXPECT_EQ(client_cid1, server_connection->client_connection_id()); + EXPECT_EQ(server_cid1, server_connection->connection_id()); + const auto* server_packet_creator = + QuicConnectionPeer::GetPacketCreator(server_connection); + EXPECT_EQ(client_cid1, server_packet_creator->GetClientConnectionId()); + EXPECT_EQ(server_cid1, server_packet_creator->GetServerConnectionId()); + server_thread_->Resume(); } TEST_P(EndToEndTest, ConnectionMigrationClientPortChanged) { @@ -3257,13 +3612,13 @@ TEST_P(EndToEndTest, AckNotifierWithPacketLossAndBlockedSocket) { ADD_FAILURE() << "Missing QPACK encoder"; return; } - QpackHeaderTable* header_table = + QpackEncoderHeaderTable* header_table = QpackEncoderPeer::header_table(qpack_encoder); if (header_table == nullptr) { ADD_FAILURE() << "Missing header table"; return; } - if (QpackHeaderTablePeer::dynamic_table_capacity(header_table) > 0) { + if (header_table->dynamic_table_capacity() > 0) { break; } } @@ -3880,312 +4235,6 @@ TEST_P(EndToEndTest, Trailers) { EXPECT_EQ(trailers, client_->response_trailers()); } -class EndToEndTestServerPush : public EndToEndTest { - protected: - const size_t kNumMaxStreams = 10; - - EndToEndTestServerPush() : EndToEndTest() { - client_config_.SetMaxBidirectionalStreamsToSend(kNumMaxStreams); - server_config_.SetMaxBidirectionalStreamsToSend(kNumMaxStreams); - client_config_.SetMaxUnidirectionalStreamsToSend(kNumMaxStreams); - server_config_.SetMaxUnidirectionalStreamsToSend(kNumMaxStreams); - } - - // Add a request with its response and |num_resources| push resources into - // cache. - // If |resource_size| == 0, response body of push resources use default string - // concatenating with resource url. Otherwise, generate a string of - // |resource_size| as body. - void AddRequestAndResponseWithServerPush(std::string host, - std::string path, - std::string response_body, - std::string* push_urls, - const size_t num_resources, - const size_t resource_size) { - bool use_large_response = resource_size != 0; - std::string large_resource; - if (use_large_response) { - // Generate a response common body larger than flow control window for - // push response. - large_resource = std::string(resource_size, 'a'); - } - std::list<QuicBackendResponse::ServerPushInfo> push_resources; - for (size_t i = 0; i < num_resources; ++i) { - std::string url = push_urls[i]; - QuicUrl resource_url(url); - std::string body = - use_large_response - ? large_resource - : absl::StrCat("This is server push response body for ", url); - SpdyHeaderBlock response_headers; - response_headers[":status"] = "200"; - response_headers["content-length"] = absl::StrCat(body.size()); - push_resources.push_back(QuicBackendResponse::ServerPushInfo( - resource_url, std::move(response_headers), kV3LowestPriority, body)); - } - - memory_cache_backend_.AddSimpleResponseWithServerPushResources( - host, path, 200, response_body, push_resources); - } -}; - -// Run all server push end to end tests with all supported versions. -INSTANTIATE_TEST_SUITE_P(EndToEndTestsServerPush, - EndToEndTestServerPush, - ::testing::ValuesIn(GetTestParams()), - ::testing::PrintToStringParamName()); - -TEST_P(EndToEndTestServerPush, ServerPush) { - ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); - - // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. - SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); - SetReorderPercentage(30); - - // Add a response with headers, body, and push resources. - const std::string kBody = "body content"; - size_t kNumResources = 4; - std::string push_urls[] = {"https://example.com/font.woff", - "https://example.com/script.js", - "https://fonts.example.com/font.woff", - "https://example.com/logo-hires.jpg"}; - AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, - push_urls, kNumResources, 0); - - client_->client()->set_response_listener( - std::unique_ptr<QuicSpdyClientBase::ResponseListener>( - new TestResponseListener)); - - QUIC_DVLOG(1) << "send request for /push_example"; - EXPECT_EQ(kBody, client_->SendSynchronousRequest( - "https://example.com/push_example")); - QuicStreamSequencer* sequencer = nullptr; - if (!version_.UsesHttp3()) { - QuicSpdyClientSession* client_session = GetClientSession(); - ASSERT_TRUE(client_session); - QuicHeadersStream* headers_stream = - QuicSpdySessionPeer::GetHeadersStream(client_session); - ASSERT_TRUE(headers_stream); - sequencer = QuicStreamPeer::sequencer(headers_stream); - ASSERT_TRUE(sequencer); - // Headers stream's sequencer buffer shouldn't be released because server - // push hasn't finished yet. - EXPECT_TRUE( - QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); - } - - for (const std::string& url : push_urls) { - QUIC_DVLOG(1) << "send request for pushed stream on url " << url; - std::string expected_body = - absl::StrCat("This is server push response body for ", url); - std::string response_body = client_->SendSynchronousRequest(url); - QUIC_DVLOG(1) << "response body " << response_body; - EXPECT_EQ(expected_body, response_body); - } - if (!version_.UsesHttp3()) { - ASSERT_TRUE(sequencer); - EXPECT_FALSE( - QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); - } -} - -TEST_P(EndToEndTestServerPush, ServerPushUnderLimit) { - // Tests that sending a request which has 4 push resources will trigger server - // to push those 4 resources and client can handle pushed resources and match - // them with requests later. - ASSERT_TRUE(Initialize()); - - if (version_.UsesHttp3()) { - return; - } - - EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); - // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. - SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); - SetReorderPercentage(30); - - // Add a response with headers, body, and push resources. - const std::string kBody = "body content"; - size_t const kNumResources = 4; - std::string push_urls[] = { - "https://example.com/font.woff", - "https://example.com/script.js", - "https://fonts.example.com/font.woff", - "https://example.com/logo-hires.jpg", - }; - AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, - push_urls, kNumResources, 0); - client_->client()->set_response_listener( - std::unique_ptr<QuicSpdyClientBase::ResponseListener>( - new TestResponseListener)); - - // Send the first request: this will trigger the server to send all the push - // resources associated with this request, and these will be cached by the - // client. - EXPECT_EQ(kBody, client_->SendSynchronousRequest( - "https://example.com/push_example")); - - for (const std::string& url : push_urls) { - // Sending subsequent requesets will not actually send anything on the wire, - // as the responses are already in the client's cache. - QUIC_DVLOG(1) << "send request for pushed stream on url " << url; - std::string expected_body = - absl::StrCat("This is server push response body for ", url); - std::string response_body = client_->SendSynchronousRequest(url); - QUIC_DVLOG(1) << "response body " << response_body; - EXPECT_EQ(expected_body, response_body); - } - // Expect only original request has been sent and push responses have been - // received as normal response. - EXPECT_EQ(1u, client_->num_requests()); - EXPECT_EQ(1u + kNumResources, client_->num_responses()); -} - -TEST_P(EndToEndTestServerPush, ServerPushOverLimitNonBlocking) { - if (version_.UsesHttp3()) { - ASSERT_TRUE(Initialize()); - return; - } - // Tests that when streams are not blocked by flow control or congestion - // control, pushing even more resources than max number of open outgoing - // streams should still work because all response streams get closed - // immediately after pushing resources. - ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); - - // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. - SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); - SetReorderPercentage(30); - - // Add a response with headers, body, and push resources. - const std::string kBody = "body content"; - - // One more resource than max number of outgoing stream of this session. - const size_t kNumResources = 1 + kNumMaxStreams; // 11. - std::string push_urls[11]; - for (size_t i = 0; i < kNumResources; ++i) { - push_urls[i] = absl::StrCat("https://example.com/push_resources", i); - } - AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, - push_urls, kNumResources, 0); - client_->client()->set_response_listener( - std::unique_ptr<QuicSpdyClientBase::ResponseListener>( - new TestResponseListener)); - - // Send the first request: this will trigger the server to send all the push - // resources associated with this request, and these will be cached by the - // client. - EXPECT_EQ(kBody, client_->SendSynchronousRequest( - "https://example.com/push_example")); - - for (const std::string& url : push_urls) { - // Sending subsequent requesets will not actually send anything on the wire, - // as the responses are already in the client's cache. - EXPECT_EQ(absl::StrCat("This is server push response body for ", url), - client_->SendSynchronousRequest(url)); - } - - // Only 1 request should have been sent. - EXPECT_EQ(1u, client_->num_requests()); - // The responses to the original request and all the promised resources - // should have been received. - EXPECT_EQ(12u, client_->num_responses()); -} - -TEST_P(EndToEndTestServerPush, ServerPushOverLimitWithBlocking) { - if (version_.UsesHttp3()) { - ASSERT_TRUE(Initialize()); - return; - } - - // Tests that when server tries to send more large resources(large enough to - // be blocked by flow control window or congestion control window) than max - // open outgoing streams , server can open upto max number of outgoing - // streams for them, and the rest will be queued up. - - // Reset flow control windows. - size_t kFlowControlWnd = 20 * 1024; // 20KB. - // Response body is larger than 1 flow controlblock window. - size_t kBodySize = kFlowControlWnd * 2; - set_client_initial_stream_flow_control_receive_window(kFlowControlWnd); - // Make sure conntection level flow control window is large enough not to - // block data being sent out though they will be blocked by stream level one. - set_client_initial_session_flow_control_receive_window( - kBodySize * kNumMaxStreams + 1024); - - ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); - - // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. - SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); - SetReorderPercentage(30); - - // Add a response with headers, body, and push resources. - const std::string kBody = "body content"; - - const size_t kNumResources = kNumMaxStreams + 1; - std::string push_urls[11]; - for (size_t i = 0; i < kNumResources; ++i) { - push_urls[i] = absl::StrCat("http://example.com/push_resources", i); - } - AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody, - push_urls, kNumResources, kBodySize); - - client_->client()->set_response_listener( - std::unique_ptr<QuicSpdyClientBase::ResponseListener>( - new TestResponseListener)); - - client_->SendRequest("https://example.com/push_example"); - - // Pause after the first response arrives. - while (!client_->response_complete()) { - // Because of priority, the first response arrived should be to original - // request. - client_->WaitForResponse(); - ASSERT_TRUE(client_->connected()); - } - - // Check server session to see if it has max number of outgoing streams opened - // though more resources need to be pushed. - if (!version_.HasIetfQuicFrames()) { - server_thread_->Pause(); - QuicSession* server_session = GetServerSession(); - if (server_session != nullptr) { - EXPECT_EQ(kNumMaxStreams, - QuicSessionPeer::GetStreamIdManager(server_session) - ->num_open_outgoing_streams()); - } else { - ADD_FAILURE() << "Missing server session"; - } - server_thread_->Resume(); - } - - EXPECT_EQ(1u, client_->num_requests()); - EXPECT_EQ(1u, client_->num_responses()); - EXPECT_EQ(kBody, client_->response_body()); - - // "Send" request for a promised resources will not really send out it because - // its response is being pushed(but blocked). And the following ack and - // flow control behavior of SendSynchronousRequests() - // will unblock the stream to finish receiving response. - client_->SendSynchronousRequest(push_urls[0]); - EXPECT_EQ(1u, client_->num_requests()); - EXPECT_EQ(2u, client_->num_responses()); - - // Do same thing for the rest 10 resources. - for (size_t i = 1; i < kNumResources; ++i) { - client_->SendSynchronousRequest(push_urls[i]); - } - - // Because of server push, client gets all pushed resources without actually - // sending requests for them. - EXPECT_EQ(1u, client_->num_requests()); - // Including response to original request, 12 responses in total were - // received. - EXPECT_EQ(12u, client_->num_responses()); -} - // TODO(fayang): this test seems to cause net_unittests timeouts :| TEST_P(EndToEndTest, DISABLED_TestHugePostWithPacketLoss) { // This test tests a huge post with introduced packet loss from client to @@ -4440,11 +4489,7 @@ TEST_P(EndToEndTest, client_.reset(CreateQuicClient(client_writer_)); EXPECT_EQ("", client_->SendSynchronousRequest("/foo")); - if (GetQuicReloadableFlag(quic_fix_dispatcher_sent_error_code)) { - EXPECT_THAT(client_->connection_error(), IsError(QUIC_PACKET_WRITE_ERROR)); - } else { - EXPECT_THAT(client_->connection_error(), IsError(QUIC_HANDSHAKE_FAILED)); - } + EXPECT_THAT(client_->connection_error(), IsError(QUIC_HANDSHAKE_FAILED)); } // Regression test for b/116200989. @@ -5007,6 +5052,207 @@ TEST_P(EndToEndPacketReorderingTest, PathValidationFailure) { server_thread_->Resume(); } +TEST_P(EndToEndPacketReorderingTest, MigrateAgainAfterPathValidationFailure) { + ASSERT_TRUE(Initialize()); + if (!GetClientConnection()->connection_migration_use_new_cid()) { + return; + } + + client_.reset(CreateQuicClient(nullptr)); + // Finish one request to make sure handshake established. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + + // Wait for the connection to become idle, to make sure the packet gets + // delayed is the connectivity probing packet. + client_->WaitForDelayedAcks(); + + QuicSocketAddress addr1 = client_->client()->session()->self_address(); + QuicConnection* client_connection = GetClientConnection(); + QuicConnectionId server_cid1 = client_connection->connection_id(); + + // Migrate socket to the new IP address. + QuicIpAddress host2 = TestLoopback(2); + EXPECT_NE(addr1.host(), host2); + + // Drop PATH_RESPONSE packets to timeout the path validation. + server_writer_->set_fake_packet_loss_percentage(100); + ASSERT_TRUE( + QuicConnectionPeer::HasUnusedPeerIssuedConnectionId(client_connection)); + + ASSERT_TRUE(client_->client()->ValidateAndMigrateSocket(host2)); + + QuicConnectionId server_cid2 = + QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection); + EXPECT_FALSE(server_cid2.IsEmpty()); + EXPECT_NE(server_cid2, server_cid1); + // Wait until path validation fails at the client. + while (client_->client()->HasPendingPathValidation()) { + EXPECT_EQ(server_cid2, + QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection)); + client_->client()->WaitForEvents(); + } + EXPECT_EQ(addr1, client_->client()->session()->self_address()); + EXPECT_EQ(server_cid1, GetClientConnection()->connection_id()); + + server_writer_->set_fake_packet_loss_percentage(0); + EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar")); + + WaitForNewConnectionIds(); + EXPECT_EQ(1u, client_connection->GetStats().num_retire_connection_id_sent); + EXPECT_EQ(0u, client_connection->GetStats().num_new_connection_id_sent); + + server_thread_->Pause(); + QuicConnection* server_connection = GetServerConnection(); + // Server has received 3 path challenges. + EXPECT_EQ(3u, + server_connection->GetStats().num_connectivity_probing_received); + EXPECT_EQ(server_cid1, server_connection->connection_id()); + EXPECT_EQ(0u, server_connection->GetStats().num_retire_connection_id_sent); + EXPECT_EQ(2u, server_connection->GetStats().num_new_connection_id_sent); + server_thread_->Resume(); + + // Migrate socket to a new IP address again. + QuicIpAddress host3 = TestLoopback(3); + EXPECT_NE(addr1.host(), host3); + EXPECT_NE(host2, host3); + + WaitForNewConnectionIds(); + EXPECT_EQ(1u, client_connection->GetStats().num_retire_connection_id_sent); + EXPECT_EQ(0u, client_connection->GetStats().num_new_connection_id_sent); + + ASSERT_TRUE(client_->client()->ValidateAndMigrateSocket(host3)); + QuicConnectionId server_cid3 = + QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection); + EXPECT_FALSE(server_cid3.IsEmpty()); + EXPECT_NE(server_cid1, server_cid3); + EXPECT_NE(server_cid2, server_cid3); + while (client_->client()->HasPendingPathValidation()) { + client_->client()->WaitForEvents(); + } + EXPECT_EQ(host3, client_->client()->session()->self_address().host()); + EXPECT_EQ(server_cid3, GetClientConnection()->connection_id()); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar")); + + // Server should send a new connection ID to client. + WaitForNewConnectionIds(); + EXPECT_EQ(2u, client_connection->GetStats().num_retire_connection_id_sent); + EXPECT_EQ(0u, client_connection->GetStats().num_new_connection_id_sent); +} + +TEST_P(EndToEndPacketReorderingTest, + MigrateAgainAfterPathValidationFailureWithNonZeroClientConnectionId) { + if (!version_.SupportsClientConnectionIds()) { + ASSERT_TRUE(Initialize()); + return; + } + override_client_connection_id_length_ = kQuicDefaultConnectionIdLength; + ASSERT_TRUE(Initialize()); + if (!GetClientConnection()->connection_migration_use_new_cid()) { + return; + } + + client_.reset(CreateQuicClient(nullptr)); + // Finish one request to make sure handshake established. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + + // Wait for the connection to become idle, to make sure the packet gets + // delayed is the connectivity probing packet. + client_->WaitForDelayedAcks(); + + QuicSocketAddress addr1 = client_->client()->session()->self_address(); + QuicConnection* client_connection = GetClientConnection(); + QuicConnectionId server_cid1 = client_connection->connection_id(); + QuicConnectionId client_cid1 = client_connection->client_connection_id(); + + // Migrate socket to the new IP address. + QuicIpAddress host2 = TestLoopback(2); + EXPECT_NE(addr1.host(), host2); + + // Drop PATH_RESPONSE packets to timeout the path validation. + server_writer_->set_fake_packet_loss_percentage(100); + ASSERT_TRUE( + QuicConnectionPeer::HasUnusedPeerIssuedConnectionId(client_connection)); + ASSERT_TRUE(client_->client()->ValidateAndMigrateSocket(host2)); + QuicConnectionId server_cid2 = + QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection); + EXPECT_FALSE(server_cid2.IsEmpty()); + EXPECT_NE(server_cid2, server_cid1); + QuicConnectionId client_cid2 = + QuicConnectionPeer::GetClientConnectionIdOnAlternativePath( + client_connection); + EXPECT_FALSE(client_cid2.IsEmpty()); + EXPECT_NE(client_cid2, client_cid1); + while (client_->client()->HasPendingPathValidation()) { + EXPECT_EQ(server_cid2, + QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection)); + client_->client()->WaitForEvents(); + } + EXPECT_EQ(addr1, client_->client()->session()->self_address()); + EXPECT_EQ(server_cid1, GetClientConnection()->connection_id()); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + server_writer_->set_fake_packet_loss_percentage(0); + EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar")); + WaitForNewConnectionIds(); + EXPECT_EQ(1u, client_connection->GetStats().num_retire_connection_id_sent); + EXPECT_EQ(2u, client_connection->GetStats().num_new_connection_id_sent); + + server_thread_->Pause(); + QuicConnection* server_connection = GetServerConnection(); + if (server_connection != nullptr) { + EXPECT_EQ(3u, + server_connection->GetStats().num_connectivity_probing_received); + EXPECT_EQ(server_cid1, server_connection->connection_id()); + } else { + ADD_FAILURE() << "Missing server connection"; + } + EXPECT_EQ(1u, server_connection->GetStats().num_retire_connection_id_sent); + EXPECT_EQ(2u, server_connection->GetStats().num_new_connection_id_sent); + server_thread_->Resume(); + + // Migrate socket to a new IP address again. + QuicIpAddress host3 = TestLoopback(3); + EXPECT_NE(addr1.host(), host3); + EXPECT_NE(host2, host3); + ASSERT_TRUE(client_->client()->ValidateAndMigrateSocket(host3)); + + QuicConnectionId server_cid3 = + QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection); + EXPECT_FALSE(server_cid3.IsEmpty()); + EXPECT_NE(server_cid1, server_cid3); + EXPECT_NE(server_cid2, server_cid3); + QuicConnectionId client_cid3 = + QuicConnectionPeer::GetClientConnectionIdOnAlternativePath( + client_connection); + EXPECT_NE(client_cid1, client_cid3); + EXPECT_NE(client_cid2, client_cid3); + while (client_->client()->HasPendingPathValidation()) { + client_->client()->WaitForEvents(); + } + EXPECT_EQ(host3, client_->client()->session()->self_address().host()); + EXPECT_EQ(server_cid3, GetClientConnection()->connection_id()); + EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath( + client_connection) + .IsEmpty()); + EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar")); + + // Server should send new server connection ID to client and retires old + // client connection ID. + WaitForNewConnectionIds(); + EXPECT_EQ(2u, client_connection->GetStats().num_retire_connection_id_sent); + EXPECT_EQ(3u, client_connection->GetStats().num_new_connection_id_sent); +} + TEST_P(EndToEndPacketReorderingTest, Buffer0RttRequest) { ASSERT_TRUE(Initialize()); // Finish one request to make sure handshake established. @@ -5384,6 +5630,74 @@ TEST_P(EndToEndTest, LegacyVersionEncapsulationWithLoss) { 0u); } +// Testing packet writer that makes a copy of the first sent packets before +// sending them. Useful for tests that need access to sent packets. +class CopyingPacketWriter : public PacketDroppingTestWriter { + public: + explicit CopyingPacketWriter(int num_packets_to_copy) + : num_packets_to_copy_(num_packets_to_copy) {} + WriteResult WritePacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options) override { + if (num_packets_to_copy_ > 0) { + num_packets_to_copy_--; + packets_.push_back( + QuicEncryptedPacket(buffer, buf_len, /*owns_buffer=*/false).Clone()); + } + return PacketDroppingTestWriter::WritePacket(buffer, buf_len, self_address, + peer_address, options); + } + + std::vector<std::unique_ptr<QuicEncryptedPacket>>& packets() { + return packets_; + } + + private: + int num_packets_to_copy_; + std::vector<std::unique_ptr<QuicEncryptedPacket>> packets_; +}; + +TEST_P(EndToEndTest, ChaosProtection) { + if (!version_.UsesCryptoFrames()) { + ASSERT_TRUE(Initialize()); + return; + } + // Replace the client's writer with one that'll save the first packet. + auto copying_writer = new CopyingPacketWriter(1); + delete client_writer_; + client_writer_ = copying_writer; + // Enable chaos protection and perform an HTTP request. + client_config_.SetClientConnectionOptions(QuicTagVector{kCHSP}); + ASSERT_TRUE(Initialize()); + SendSynchronousFooRequestAndCheckResponse(); + // Parse the saved packet to make sure it's valid. + SimpleQuicFramer validation_framer({version_}); + validation_framer.framer()->SetInitialObfuscators( + GetClientConnection()->connection_id()); + ASSERT_GT(copying_writer->packets().size(), 0u); + EXPECT_TRUE(validation_framer.ProcessPacket(*copying_writer->packets()[0])); + // TODO(dschinazi) figure out a way to use a MockRandom in this test so we + // can inspect the contents of this packet. +} + +TEST_P(EndToEndTest, ChaosProtectionWithMultiPacketChlo) { + if (!version_.UsesCryptoFrames()) { + ASSERT_TRUE(Initialize()); + return; + } + // Enable chaos protection. + client_config_.SetClientConnectionOptions(QuicTagVector{kCHSP}); + // Add a transport parameter to make the client hello span multiple packets. + constexpr auto kCustomParameter = + static_cast<TransportParameters::TransportParameterId>(0xff34); + client_config_.custom_transport_parameters_to_send()[kCustomParameter] = + std::string(2000, '?'); + ASSERT_TRUE(Initialize()); + SendSynchronousFooRequestAndCheckResponse(); +} + TEST_P(EndToEndTest, KeyUpdateInitiatedByClient) { if (!version_.UsesTls()) { // Key Update is only supported in TLS handshake. diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc index 3544cfddf9a..6d9290a12c1 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc @@ -36,8 +36,17 @@ HttpDecoder::HttpDecoder(Visitor* visitor, Options options) current_push_id_length_(0), remaining_push_id_length_(0), error_(QUIC_NO_ERROR), - error_detail_("") { + error_detail_(""), + ignore_old_priority_update_( + GetQuicReloadableFlag(quic_ignore_old_priority_update_frame)), + error_on_http3_push_(GetQuicReloadableFlag(quic_error_on_http3_push)) { QUICHE_DCHECK(visitor_); + if (ignore_old_priority_update_) { + QUIC_RELOADABLE_FLAG_COUNT(quic_ignore_old_priority_update_frame); + } + if (error_on_http3_push_) { + QUIC_RELOADABLE_FLAG_COUNT(quic_error_on_http3_push); + } } HttpDecoder::~HttpDecoder() {} @@ -172,6 +181,20 @@ bool HttpDecoder::ReadFrameType(QuicDataReader* reader) { current_frame_type_)); return false; } + + if (error_on_http3_push_) { + if (current_frame_type_ == + static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH)) { + RaiseError(QUIC_HTTP_FRAME_ERROR, "CANCEL_PUSH frame received."); + return false; + } + if (current_frame_type_ == + static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE)) { + RaiseError(QUIC_HTTP_FRAME_ERROR, "PUSH_PROMISE frame received."); + return false; + } + } + state_ = STATE_READING_FRAME_LENGTH; return true; } @@ -238,11 +261,19 @@ bool HttpDecoder::ReadFrameLength(QuicDataReader* reader) { visitor_->OnHeadersFrameStart(header_length, current_frame_length_); break; case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): + if (error_on_http3_push_) { + QUICHE_NOTREACHED(); + break; + } break; case static_cast<uint64_t>(HttpFrameType::SETTINGS): continue_processing = visitor_->OnSettingsFrameStart(header_length); break; case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE): + if (error_on_http3_push_) { + QUICHE_NOTREACHED(); + break; + } // This edge case needs to be handled here, because ReadFramePayload() // does not get called if |current_frame_length_| is zero. if (current_frame_length_ == 0) { @@ -257,7 +288,13 @@ bool HttpDecoder::ReadFrameLength(QuicDataReader* reader) { case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID): break; case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE): - continue_processing = visitor_->OnPriorityUpdateFrameStart(header_length); + if (ignore_old_priority_update_) { + continue_processing = visitor_->OnUnknownFrameStart( + current_frame_type_, header_length, current_frame_length_); + } else { + continue_processing = + visitor_->OnPriorityUpdateFrameStart(header_length); + } break; case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM): continue_processing = visitor_->OnPriorityUpdateFrameStart(header_length); @@ -307,7 +344,11 @@ bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) { break; } case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): { - continue_processing = BufferOrParsePayload(reader); + if (error_on_http3_push_) { + QUICHE_NOTREACHED(); + } else { + continue_processing = BufferOrParsePayload(reader); + } break; } case static_cast<uint64_t>(HttpFrameType::SETTINGS): { @@ -315,6 +356,10 @@ bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) { break; } case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE): { + if (error_on_http3_push_) { + QUICHE_NOTREACHED(); + break; + } PushId push_id; if (current_frame_length_ == remaining_frame_length_) { // A new Push Promise frame just arrived. @@ -388,7 +433,11 @@ bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) { break; } case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE): { - continue_processing = BufferOrParsePayload(reader); + if (ignore_old_priority_update_) { + continue_processing = HandleUnknownFramePayload(reader); + } else { + continue_processing = BufferOrParsePayload(reader); + } break; } case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM): { @@ -428,9 +477,13 @@ bool HttpDecoder::FinishParsing(QuicDataReader* reader) { break; } case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): { - // If frame payload is not empty, FinishParsing() is skipped. - QUICHE_DCHECK_EQ(0u, current_frame_length_); - continue_processing = BufferOrParsePayload(reader); + if (error_on_http3_push_) { + QUICHE_NOTREACHED(); + } else { + // If frame payload is not empty, FinishParsing() is skipped. + QUICHE_DCHECK_EQ(0u, current_frame_length_); + continue_processing = BufferOrParsePayload(reader); + } break; } case static_cast<uint64_t>(HttpFrameType::SETTINGS): { @@ -440,7 +493,11 @@ bool HttpDecoder::FinishParsing(QuicDataReader* reader) { break; } case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE): { - continue_processing = visitor_->OnPushPromiseFrameEnd(); + if (error_on_http3_push_) { + QUICHE_NOTREACHED(); + } else { + continue_processing = visitor_->OnPushPromiseFrameEnd(); + } break; } case static_cast<uint64_t>(HttpFrameType::GOAWAY): { @@ -456,9 +513,13 @@ bool HttpDecoder::FinishParsing(QuicDataReader* reader) { break; } case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE): { - // If frame payload is not empty, FinishParsing() is skipped. - QUICHE_DCHECK_EQ(0u, current_frame_length_); - continue_processing = BufferOrParsePayload(reader); + if (ignore_old_priority_update_) { + continue_processing = visitor_->OnUnknownFrameEnd(); + } else { + // If frame payload is not empty, FinishParsing() is skipped. + QUICHE_DCHECK_EQ(0u, current_frame_length_); + continue_processing = BufferOrParsePayload(reader); + } break; } case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM): { @@ -559,6 +620,10 @@ bool HttpDecoder::ParseEntirePayload(QuicDataReader* reader) { switch (current_frame_type_) { case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): { + if (error_on_http3_push_) { + QUICHE_NOTREACHED(); + return false; + } CancelPushFrame frame; if (!reader->ReadVarInt62(&frame.push_id)) { RaiseError(QUIC_HTTP_FRAME_ERROR, @@ -606,11 +671,16 @@ bool HttpDecoder::ParseEntirePayload(QuicDataReader* reader) { return visitor_->OnMaxPushIdFrame(frame); } case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE): { - PriorityUpdateFrame frame; - if (!ParsePriorityUpdateFrame(reader, &frame)) { + if (ignore_old_priority_update_) { + QUICHE_NOTREACHED(); return false; + } else { + PriorityUpdateFrame frame; + if (!ParsePriorityUpdateFrame(reader, &frame)) { + return false; + } + return visitor_->OnPriorityUpdateFrame(frame); } - return visitor_->OnPriorityUpdateFrame(frame); } case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM): { PriorityUpdateFrame frame; @@ -767,6 +837,7 @@ bool HttpDecoder::ParseAcceptChFrame(QuicDataReader* reader, QuicByteCount HttpDecoder::MaxFrameLength(uint64_t frame_type) { switch (frame_type) { case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): + // TODO(b/171463363): Remove. return sizeof(PushId); case static_cast<uint64_t>(HttpFrameType::SETTINGS): // This limit is arbitrary. @@ -774,6 +845,7 @@ QuicByteCount HttpDecoder::MaxFrameLength(uint64_t frame_type) { case static_cast<uint64_t>(HttpFrameType::GOAWAY): return VARIABLE_LENGTH_INTEGER_LENGTH_8; case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID): + // TODO(b/171463363): Remove. return sizeof(PushId); case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE): // This limit is arbitrary. diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h index a351415e964..3a970fb63b1 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h @@ -45,6 +45,7 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // processed. At that point it is safe to consume |header_length| bytes. // Called when a CANCEL_PUSH frame has been successfully parsed. + // TODO(b/171463363): Remove. virtual bool OnCancelPushFrame(const CancelPushFrame& frame) = 0; // Called when a MAX_PUSH_ID frame has been successfully parsed. @@ -83,6 +84,7 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // Called when a HEADERS frame has been completely processed. virtual bool OnHeadersFrameEnd() = 0; + // TODO(b/171463363): Remove all. // Called when a PUSH_PROMISE frame has been received. virtual bool OnPushPromiseFrameStart(QuicByteCount header_length) = 0; // Called when the Push ID field of a PUSH_PROMISE frame has been parsed. @@ -230,7 +232,7 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { void BufferFrameType(QuicDataReader* reader); // Buffers at most |remaining_push_id_length_| from |reader| to - // |push_id_buffer_|. + // |push_id_buffer_|. TODO(b/171463363): Remove. void BufferPushId(QuicDataReader* reader); // Sets |error_| and |error_detail_| accordingly. @@ -241,11 +243,13 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // Parses the payload of a PRIORITY_UPDATE frame (draft-01, type 0x0f) // from |reader| into |frame|. + // TODO(b/147306124): Remove. bool ParsePriorityUpdateFrame(QuicDataReader* reader, PriorityUpdateFrame* frame); // Parses the payload of a PRIORITY_UPDATE frame (draft-02, type 0xf0700) // from |reader| into |frame|. + // TODO(b/147306124): Rename to ParsePriorityUpdateFrame(). bool ParseNewPriorityUpdateFrame(QuicDataReader* reader, PriorityUpdateFrame* frame); @@ -276,8 +280,10 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // Remaining length that's needed for the frame's type field. QuicByteCount remaining_type_field_length_; // Length of PUSH_PROMISE frame's push id. + // TODO(b/171463363): Remove. QuicByteCount current_push_id_length_; // Remaining length that's needed for PUSH_PROMISE frame's push id field. + // TODO(b/171463363): Remove. QuicByteCount remaining_push_id_length_; // Last error. QuicErrorCode error_; @@ -290,7 +296,16 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // Remaining unparsed type field data. std::array<char, sizeof(uint64_t)> type_buffer_; // Remaining unparsed push id data. + // TODO(b/171463363): Remove. std::array<char, sizeof(uint64_t)> push_id_buffer_; + + // Latched value of + // gfe2_reloadable_flag_quic_ignore_old_priority_update_frame. + const bool ignore_old_priority_update_; + + // Latched value of + // gfe2_reloadable_flag_quic_error_on_http3_push. + const bool error_on_http3_push_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc index aac020a97a7..05d580e20e1 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc @@ -19,7 +19,6 @@ #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" using ::testing::_; using ::testing::AnyNumber; @@ -249,6 +248,14 @@ TEST_F(HttpDecoderTest, CancelPush) { "01" // length "01"); // Push Id + if (GetQuicReloadableFlag(quic_error_on_http3_push)) { + EXPECT_CALL(visitor_, OnError(&decoder_)); + EXPECT_EQ(1u, ProcessInput(input)); + EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_ERROR)); + EXPECT_EQ("CANCEL_PUSH frame received.", decoder_.error_detail()); + return; + } + // Visitor pauses processing. EXPECT_CALL(visitor_, OnCancelPushFrame(CancelPushFrame({1}))) .WillOnce(Return(false)); @@ -277,6 +284,14 @@ TEST_F(HttpDecoderTest, PushPromiseFrame) { "C000000000000101"), // push id 257 "Headers"); // headers + if (GetQuicReloadableFlag(quic_error_on_http3_push)) { + EXPECT_CALL(visitor_, OnError(&decoder_)); + EXPECT_EQ(1u, ProcessInput(input)); + EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_ERROR)); + EXPECT_EQ("PUSH_PROMISE frame received.", decoder_.error_detail()); + return; + } + // Visitor pauses processing. EXPECT_CALL(visitor_, OnPushPromiseFrameStart(2)).WillOnce(Return(false)); EXPECT_CALL(visitor_, OnPushPromiseFramePushId(257, 8, 7)) @@ -338,6 +353,10 @@ TEST_F(HttpDecoderTest, PushPromiseFrame) { } TEST_F(HttpDecoderTest, CorruptPushPromiseFrame) { + if (GetQuicReloadableFlag(quic_error_on_http3_push)) { + return; + } + InSequence s; std::string input = absl::HexStringToBytes( @@ -733,6 +752,10 @@ TEST_F(HttpDecoderTest, EmptyHeadersFrame) { } TEST_F(HttpDecoderTest, PushPromiseFrameNoHeaders) { + if (GetQuicReloadableFlag(quic_error_on_http3_push)) { + return; + } + InSequence s; std::string input = absl::HexStringToBytes( "05" // type (PUSH_PROMISE) @@ -768,6 +791,10 @@ TEST_F(HttpDecoderTest, PushPromiseFrameNoHeaders) { } TEST_F(HttpDecoderTest, MalformedFrameWithOverlyLargePayload) { + if (GetQuicReloadableFlag(quic_error_on_http3_push)) { + return; + } + std::string input = absl::HexStringToBytes( "03" // type (CANCEL_PUSH) "10" // length @@ -841,97 +868,183 @@ TEST_F(HttpDecoderTest, HeadersPausedThenData) { } TEST_F(HttpDecoderTest, CorruptFrame) { - InSequence s; - - struct { - const char* const input; - const char* const error_message; - } kTestData[] = {{"\x03" // type (CANCEL_PUSH) - "\x01" // length - "\x40", // first byte of two-byte varint push id - "Unable to read CANCEL_PUSH push_id."}, - {"\x03" // type (CANCEL_PUSH) - "\x04" // length - "\x05" // valid push id - "foo", // superfluous data - "Superfluous data in CANCEL_PUSH frame."}, - {"\x0D" // type (MAX_PUSH_ID) - "\x01" // length - "\x40", // first byte of two-byte varint push id - "Unable to read MAX_PUSH_ID push_id."}, - {"\x0D" // type (MAX_PUSH_ID) - "\x04" // length - "\x05" // valid push id - "foo", // superfluous data - "Superfluous data in MAX_PUSH_ID frame."}, - {"\x07" // type (GOAWAY) - "\x01" // length - "\x40", // first byte of two-byte varint stream id - "Unable to read GOAWAY ID."}, - {"\x07" // type (GOAWAY) - "\x04" // length - "\x05" // valid stream id - "foo", // superfluous data - "Superfluous data in GOAWAY frame."}, - {"\x40\x89" // type (ACCEPT_CH) - "\x01" // length - "\x40", // first byte of two-byte varint origin length - "Unable to read ACCEPT_CH origin."}, - {"\x40\x89" // type (ACCEPT_CH) - "\x01" // length - "\x05", // valid origin length but no origin string - "Unable to read ACCEPT_CH origin."}, - {"\x40\x89" // type (ACCEPT_CH) - "\x04" // length - "\x05" // valid origin length - "foo", // payload ends before origin ends - "Unable to read ACCEPT_CH origin."}, - {"\x40\x89" // type (ACCEPT_CH) - "\x04" // length - "\x03" // valid origin length - "foo", // payload ends at end of origin: no value - "Unable to read ACCEPT_CH value."}, - {"\x40\x89" // type (ACCEPT_CH) - "\x05" // length - "\x03" // valid origin length - "foo" // payload ends at end of origin: no value - "\x40", // first byte of two-byte varint value length - "Unable to read ACCEPT_CH value."}, - {"\x40\x89" // type (ACCEPT_CH) - "\x08" // length - "\x03" // valid origin length - "foo" // origin - "\x05" // valid value length - "bar", // payload ends before value ends - "Unable to read ACCEPT_CH value."}}; - - for (const auto& test_data : kTestData) { - { - HttpDecoder decoder(&visitor_); - EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber()); - EXPECT_CALL(visitor_, OnError(&decoder)); - - absl::string_view input(test_data.input); - decoder.ProcessInput(input.data(), input.size()); - EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR)); - EXPECT_EQ(test_data.error_message, decoder.error_detail()); + if (GetQuicReloadableFlag(quic_error_on_http3_push)) { + InSequence s; + + struct { + const char* const input; + const char* const error_message; + } kTestData[] = {{"\x0D" // type (MAX_PUSH_ID) + "\x01" // length + "\x40", // first byte of two-byte varint push id + "Unable to read MAX_PUSH_ID push_id."}, + {"\x0D" // type (MAX_PUSH_ID) + "\x04" // length + "\x05" // valid push id + "foo", // superfluous data + "Superfluous data in MAX_PUSH_ID frame."}, + {"\x07" // type (GOAWAY) + "\x01" // length + "\x40", // first byte of two-byte varint stream id + "Unable to read GOAWAY ID."}, + {"\x07" // type (GOAWAY) + "\x04" // length + "\x05" // valid stream id + "foo", // superfluous data + "Superfluous data in GOAWAY frame."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x01" // length + "\x40", // first byte of two-byte varint origin length + "Unable to read ACCEPT_CH origin."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x01" // length + "\x05", // valid origin length but no origin string + "Unable to read ACCEPT_CH origin."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x04" // length + "\x05" // valid origin length + "foo", // payload ends before origin ends + "Unable to read ACCEPT_CH origin."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x04" // length + "\x03" // valid origin length + "foo", // payload ends at end of origin: no value + "Unable to read ACCEPT_CH value."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x05" // length + "\x03" // valid origin length + "foo" // payload ends at end of origin: no value + "\x40", // first byte of two-byte varint value length + "Unable to read ACCEPT_CH value."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x08" // length + "\x03" // valid origin length + "foo" // origin + "\x05" // valid value length + "bar", // payload ends before value ends + "Unable to read ACCEPT_CH value."}}; + + for (const auto& test_data : kTestData) { + { + HttpDecoder decoder(&visitor_); + EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber()); + EXPECT_CALL(visitor_, OnError(&decoder)); + + absl::string_view input(test_data.input); + decoder.ProcessInput(input.data(), input.size()); + EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR)); + EXPECT_EQ(test_data.error_message, decoder.error_detail()); + } + { + HttpDecoder decoder(&visitor_); + EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber()); + EXPECT_CALL(visitor_, OnError(&decoder)); + + absl::string_view input(test_data.input); + for (auto c : input) { + decoder.ProcessInput(&c, 1); + } + EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR)); + EXPECT_EQ(test_data.error_message, decoder.error_detail()); + } } - { - HttpDecoder decoder(&visitor_); - EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber()); - EXPECT_CALL(visitor_, OnError(&decoder)); - - absl::string_view input(test_data.input); - for (auto c : input) { - decoder.ProcessInput(&c, 1); + } else { + InSequence s; + + struct { + const char* const input; + const char* const error_message; + } kTestData[] = {{"\x03" // type (CANCEL_PUSH) + "\x01" // length + "\x40", // first byte of two-byte varint push id + "Unable to read CANCEL_PUSH push_id."}, + {"\x03" // type (CANCEL_PUSH) + "\x04" // length + "\x05" // valid push id + "foo", // superfluous data + "Superfluous data in CANCEL_PUSH frame."}, + {"\x0D" // type (MAX_PUSH_ID) + "\x01" // length + "\x40", // first byte of two-byte varint push id + "Unable to read MAX_PUSH_ID push_id."}, + {"\x0D" // type (MAX_PUSH_ID) + "\x04" // length + "\x05" // valid push id + "foo", // superfluous data + "Superfluous data in MAX_PUSH_ID frame."}, + {"\x07" // type (GOAWAY) + "\x01" // length + "\x40", // first byte of two-byte varint stream id + "Unable to read GOAWAY ID."}, + {"\x07" // type (GOAWAY) + "\x04" // length + "\x05" // valid stream id + "foo", // superfluous data + "Superfluous data in GOAWAY frame."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x01" // length + "\x40", // first byte of two-byte varint origin length + "Unable to read ACCEPT_CH origin."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x01" // length + "\x05", // valid origin length but no origin string + "Unable to read ACCEPT_CH origin."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x04" // length + "\x05" // valid origin length + "foo", // payload ends before origin ends + "Unable to read ACCEPT_CH origin."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x04" // length + "\x03" // valid origin length + "foo", // payload ends at end of origin: no value + "Unable to read ACCEPT_CH value."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x05" // length + "\x03" // valid origin length + "foo" // payload ends at end of origin: no value + "\x40", // first byte of two-byte varint value length + "Unable to read ACCEPT_CH value."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x08" // length + "\x03" // valid origin length + "foo" // origin + "\x05" // valid value length + "bar", // payload ends before value ends + "Unable to read ACCEPT_CH value."}}; + + for (const auto& test_data : kTestData) { + { + HttpDecoder decoder(&visitor_); + EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber()); + EXPECT_CALL(visitor_, OnError(&decoder)); + + absl::string_view input(test_data.input); + decoder.ProcessInput(input.data(), input.size()); + EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR)); + EXPECT_EQ(test_data.error_message, decoder.error_detail()); + } + { + HttpDecoder decoder(&visitor_); + EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber()); + EXPECT_CALL(visitor_, OnError(&decoder)); + + absl::string_view input(test_data.input); + for (auto c : input) { + decoder.ProcessInput(&c, 1); + } + EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR)); + EXPECT_EQ(test_data.error_message, decoder.error_detail()); } - EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR)); - EXPECT_EQ(test_data.error_message, decoder.error_detail()); } } } TEST_F(HttpDecoderTest, EmptyCancelPushFrame) { + if (GetQuicReloadableFlag(quic_error_on_http3_push)) { + return; + } + std::string input = absl::HexStringToBytes( "03" // type (CANCEL_PUSH) "00"); // frame length @@ -959,6 +1072,10 @@ TEST_F(HttpDecoderTest, EmptySettingsFrame) { // Regression test for https://crbug.com/1001823. TEST_F(HttpDecoderTest, EmptyPushPromiseFrame) { + if (GetQuicReloadableFlag(quic_error_on_http3_push)) { + return; + } + std::string input = absl::HexStringToBytes( "05" // type (PUSH_PROMISE) "00"); // frame length @@ -1003,7 +1120,11 @@ TEST_F(HttpDecoderTest, LargeStreamIdInGoAway) { EXPECT_EQ("", decoder_.error_detail()); } -TEST_F(HttpDecoderTest, PriorityUpdateFrame) { +TEST_F(HttpDecoderTest, OldPriorityUpdateFrame) { + if (GetQuicReloadableFlag(quic_ignore_old_priority_update_frame)) { + return; + } + InSequence s; std::string input1 = absl::HexStringToBytes( "0f" // type (PRIORITY_UPDATE) @@ -1085,7 +1206,44 @@ TEST_F(HttpDecoderTest, PriorityUpdateFrame) { EXPECT_EQ("", decoder_.error_detail()); } -TEST_F(HttpDecoderTest, NewPriorityUpdateFrame) { +TEST_F(HttpDecoderTest, ObsoletePriorityUpdateFrame) { + if (!GetQuicReloadableFlag(quic_ignore_old_priority_update_frame)) { + return; + } + + const QuicByteCount header_length = 2; + const QuicByteCount payload_length = 3; + InSequence s; + std::string input = absl::HexStringToBytes( + "0f" // type (obsolete PRIORITY_UPDATE) + "03" // length + "666f6f"); // payload "foo" + + // Process frame as a whole. + EXPECT_CALL(visitor_, + OnUnknownFrameStart(0x0f, header_length, payload_length)); + EXPECT_CALL(visitor_, OnUnknownFramePayload(Eq("foo"))); + EXPECT_CALL(visitor_, OnUnknownFrameEnd()).WillOnce(Return(false)); + + EXPECT_EQ(header_length + payload_length, + ProcessInputWithGarbageAppended(input)); + EXPECT_THAT(decoder_.error(), IsQuicNoError()); + EXPECT_EQ("", decoder_.error_detail()); + + // Process frame byte by byte. + EXPECT_CALL(visitor_, + OnUnknownFrameStart(0x0f, header_length, payload_length)); + EXPECT_CALL(visitor_, OnUnknownFramePayload(Eq("f"))); + EXPECT_CALL(visitor_, OnUnknownFramePayload(Eq("o"))); + EXPECT_CALL(visitor_, OnUnknownFramePayload(Eq("o"))); + EXPECT_CALL(visitor_, OnUnknownFrameEnd()); + + ProcessInputCharByChar(input); + EXPECT_THAT(decoder_.error(), IsQuicNoError()); + EXPECT_EQ("", decoder_.error_detail()); +} + +TEST_F(HttpDecoderTest, PriorityUpdateFrame) { InSequence s; std::string input1 = absl::HexStringToBytes( "800f0700" // type (PRIORITY_UPDATE) @@ -1166,6 +1324,10 @@ TEST_F(HttpDecoderTest, NewPriorityUpdateFrame) { } TEST_F(HttpDecoderTest, CorruptPriorityUpdateFrame) { + if (GetQuicReloadableFlag(quic_ignore_old_priority_update_frame)) { + return; + } + std::string payload1 = absl::HexStringToBytes( "80" // prioritized element type: PUSH_STREAM "4005"); // prioritized element id diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_frames.h b/chromium/net/third_party/quiche/src/quic/core/http/http_frames.h index 56e53487b24..b14ebe5a91b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_frames.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_frames.h @@ -29,6 +29,7 @@ enum class HttpFrameType { GOAWAY = 0x7, MAX_PUSH_ID = 0xD, // https://tools.ietf.org/html/draft-ietf-httpbis-priority-01 + // TODO(b/147306124): Remove. PRIORITY_UPDATE = 0XF, // https://tools.ietf.org/html/draft-davidben-http-client-hint-reliability-02 ACCEPT_CH = 0x89, diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list.h index 9ddf6de8a84..01c861d5905 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list.h @@ -11,9 +11,9 @@ #include <utility> #include "absl/strings/string_view.h" -#include "quic/core/quic_circular_deque.h" #include "quic/platform/api/quic_bug_tracker.h" #include "quic/platform/api/quic_export.h" +#include "common/quiche_circular_deque.h" #include "spdy/core/spdy_header_block.h" #include "spdy/core/spdy_headers_handler_interface.h" @@ -23,7 +23,8 @@ namespace quic { class QUIC_EXPORT_PRIVATE QuicHeaderList : public spdy::SpdyHeadersHandlerInterface { public: - using ListType = QuicCircularDeque<std::pair<std::string, std::string>>; + using ListType = + quiche::QuicheCircularDeque<std::pair<std::string, std::string>>; using value_type = ListType::value_type; using const_iterator = ListType::const_iterator; @@ -59,7 +60,7 @@ class QUIC_EXPORT_PRIVATE QuicHeaderList std::string DebugString() const; private: - QuicCircularDeque<std::pair<std::string, std::string>> header_list_; + quiche::QuicheCircularDeque<std::pair<std::string, std::string>> header_list_; // 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 diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream.h index ae9e2afe0d1..153575be1de 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream.h @@ -91,7 +91,7 @@ class QUIC_EXPORT_PRIVATE QuicHeadersStream : public QuicStream { QuicSpdySession* spdy_session_; // Headers that have not been fully acked. - QuicCircularDeque<CompressedHeaderInfo> unacked_headers_; + quiche::QuicheCircularDeque<CompressedHeaderInfo> unacked_headers_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc index 55dafac3756..42fef63c978 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc @@ -563,6 +563,10 @@ TEST_P(QuicHeadersStreamTest, ProcessPriorityFrame) { } TEST_P(QuicHeadersStreamTest, ProcessPushPromiseDisabledSetting) { + if (perspective() != Perspective::IS_CLIENT) { + return; + } + session_.OnConfigNegotiated(); SpdySettingsIR data; // Respect supported settings frames SETTINGS_ENABLE_PUSH. @@ -570,15 +574,11 @@ TEST_P(QuicHeadersStreamTest, ProcessPushPromiseDisabledSetting) { SpdySerializedFrame frame(framer_->SerializeFrame(data)); stream_frame_.data_buffer = frame.data(); stream_frame_.data_length = frame.size(); - if (perspective() == Perspective::IS_CLIENT) { - EXPECT_CALL( - *connection_, - CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, - "Unsupported field of HTTP/2 SETTINGS frame: 2", _)); - } + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, + "Unsupported field of HTTP/2 SETTINGS frame: 2", _)); headers_stream_->OnStreamFrame(stream_frame_); - EXPECT_EQ(session_.server_push_enabled(), - perspective() == Perspective::IS_CLIENT); } TEST_P(QuicHeadersStreamTest, ProcessLargeRawData) { diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.cc index fbd7b2eb18e..39d8b42a5a2 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.cc @@ -15,7 +15,7 @@ #include "quic/core/quic_types.h" #include "quic/platform/api/quic_flag_utils.h" #include "quic/platform/api/quic_flags.h" -#include "common/platform/api/quiche_text_utils.h" +#include "common/quiche_text_utils.h" namespace quic { @@ -70,7 +70,6 @@ bool QuicReceiveControlStream::OnCancelPushFrame(const CancelPushFrame& frame) { spdy_session()->debug_visitor()->OnCancelPushFrameReceived(frame); } - // TODO(b/151841240): Handle CANCEL_PUSH frames instead of ignoring them. return ValidateFrameType(HttpFrameType::CANCEL_PUSH); } diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc index c9133436e9f..03f21d8a70f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc @@ -15,7 +15,6 @@ #include "quic/test_tools/quic_spdy_session_peer.h" #include "quic/test_tools/quic_stream_peer.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { @@ -165,7 +164,7 @@ TEST_P(QuicReceiveControlStreamTest, ReceiveSettings) { QuicStreamFrame frame(receive_control_stream_->id(), false, 1, data); QpackEncoder* qpack_encoder = session_.qpack_encoder(); - QpackHeaderTable* header_table = + QpackEncoderHeaderTable* header_table = QpackEncoderPeer::header_table(qpack_encoder); EXPECT_EQ(std::numeric_limits<size_t>::max(), session_.max_outbound_header_list_size()); @@ -311,7 +310,10 @@ TEST_P(QuicReceiveControlStreamTest, PushPromiseOnControlStreamShouldClose) { push_promise_frame); EXPECT_CALL( *connection_, - CloseConnection(QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM, _, _)) + CloseConnection(GetQuicReloadableFlag(quic_error_on_http3_push) + ? QUIC_HTTP_FRAME_ERROR + : QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM, + _, _)) .WillOnce( Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _)); @@ -382,13 +384,21 @@ TEST_P(QuicReceiveControlStreamTest, CancelPushFrameBeforeSettings) { "01" // payload length "01"); // push ID - EXPECT_CALL(*connection_, - CloseConnection(QUIC_HTTP_MISSING_SETTINGS_FRAME, - "First frame received on control stream is type " - "3, but it must be SETTINGS.", - _)) - .WillOnce( - Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); + if (GetQuicReloadableFlag(quic_error_on_http3_push)) { + EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_FRAME_ERROR, + "CANCEL_PUSH frame received.", _)) + .WillOnce( + Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); + } else { + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_HTTP_MISSING_SETTINGS_FRAME, + "First frame received on control stream is type " + "3, but it must be SETTINGS.", + _)) + .WillOnce( + Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); + } EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _)); EXPECT_CALL(session_, OnConnectionClosed(_, _)); diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc index d1be0df27ca..c5af1dc4a90 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc @@ -13,7 +13,6 @@ #include "quic/test_tools/quic_config_peer.h" #include "quic/test_tools/quic_spdy_session_peer.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/test_tools/quiche_test_utils.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.cc index b955835e2b8..c78df58e18a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.cc @@ -50,12 +50,6 @@ void QuicServerSessionBase::OnConfigNegotiated() { return; } - // Disable server push if peer sends the corresponding connection option. - if (!version().UsesHttp3() && - ContainsQuicTag(config()->ReceivedConnectionOptions(), kQNSP)) { - OnSetting(spdy::SETTINGS_ENABLE_PUSH, 0); - } - // Enable bandwidth resumption if peer sent correct connection options. const bool last_bandwidth_resumption = ContainsQuicTag(config()->ReceivedConnectionOptions(), kBWRE); diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base_test.cc index 29750f2e3cf..ac7fc1e59c8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base_test.cc @@ -10,7 +10,6 @@ #include <utility> #include "absl/memory/memory.h" -#include "absl/strings/string_view.h" #include "quic/core/crypto/null_encrypter.h" #include "quic/core/crypto/quic_crypto_server_config.h" #include "quic/core/crypto/quic_random.h" @@ -51,6 +50,12 @@ namespace quic { namespace test { namespace { +// Data to be sent on a request stream. In Google QUIC, this is interpreted as +// DATA payload (there is no framing on request streams). In IETF QUIC, this is +// interpreted as HEADERS frame (type 0x1) with payload length 122 ('z'). Since +// no payload is included, QPACK decoder will not be invoked. +const char* const kStreamData = "\1z"; + class TestServerSession : public QuicServerSessionBase { public: TestServerSession(const QuicConfig& config, @@ -148,7 +153,7 @@ class QuicServerSessionBaseTest : public QuicTestWithParam<ParsedQuicVersion> { config_.SetInitialSessionFlowControlWindowToSend( kInitialSessionFlowControlWindowForTest); - ParsedQuicVersionVector supported_versions = SupportedVersions(GetParam()); + ParsedQuicVersionVector supported_versions = SupportedVersions(version()); connection_ = new StrictMock<MockQuicConnection>( &helper_, &alarm_factory_, Perspective::IS_SERVER, supported_versions); connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); @@ -166,23 +171,24 @@ class QuicServerSessionBaseTest : public QuicTestWithParam<ParsedQuicVersion> { QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow( session_->config(), kMinimumFlowControlSendWindow); session_->OnConfigNegotiated(); - if (connection_->version().SupportsAntiAmplificationLimit()) { + if (version().SupportsAntiAmplificationLimit()) { QuicConnectionPeer::SetAddressValidated(connection_); } } QuicStreamId GetNthClientInitiatedBidirectionalId(int n) { - return GetNthClientInitiatedBidirectionalStreamId( - connection_->transport_version(), n); + return GetNthClientInitiatedBidirectionalStreamId(transport_version(), n); } QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) { return quic::test::GetNthServerInitiatedUnidirectionalStreamId( - connection_->transport_version(), n); + transport_version(), n); } + ParsedQuicVersion version() const { return GetParam(); } + QuicTransportVersion transport_version() const { - return connection_->transport_version(); + return version().transport_version; } // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a @@ -242,10 +248,9 @@ INSTANTIATE_TEST_SUITE_P(Tests, ::testing::PrintToStringParamName()); TEST_P(QuicServerSessionBaseTest, CloseStreamDueToReset) { - // Open a stream, then reset it. - // Send two bytes of payload to open it. + // Send some data open a stream, then reset it. QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0, - absl::string_view("HT")); + kStreamData); session_->OnStreamFrame(data1); EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); @@ -298,9 +303,9 @@ TEST_P(QuicServerSessionBaseTest, NeverOpenStreamDueToReset) { InjectStopSendingFrame(GetNthClientInitiatedBidirectionalId(0)); EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); - // Send two bytes of payload. + QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0, - absl::string_view("HT")); + kStreamData); session_->OnStreamFrame(data1); // The stream should never be opened, now that the reset is received. @@ -309,11 +314,11 @@ TEST_P(QuicServerSessionBaseTest, NeverOpenStreamDueToReset) { } TEST_P(QuicServerSessionBaseTest, AcceptClosedStream) { - // Send (empty) compressed headers followed by two bytes of data. + // Send some data to open two streams. QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0, - absl::string_view("\1\0\0\0\0\0\0\0HT")); + kStreamData); QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(1), false, 0, - absl::string_view("\3\0\0\0\0\0\0\0HT")); + kStreamData); session_->OnStreamFrame(frame1); session_->OnStreamFrame(frame2); EXPECT_EQ(2u, QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); @@ -341,9 +346,9 @@ TEST_P(QuicServerSessionBaseTest, AcceptClosedStream) { // past the reset point of stream 3. As it's a closed stream we just drop the // data on the floor, but accept the packet because it has data for stream 5. QuicStreamFrame frame3(GetNthClientInitiatedBidirectionalId(0), false, 2, - absl::string_view("TP")); + kStreamData); QuicStreamFrame frame4(GetNthClientInitiatedBidirectionalId(1), false, 2, - absl::string_view("TP")); + kStreamData); session_->OnStreamFrame(frame3); session_->OnStreamFrame(frame4); // The stream should never be opened, now that the reset is received. @@ -373,7 +378,7 @@ TEST_P(QuicServerSessionBaseTest, MaxOpenStreams) { for (size_t i = 0; i < kMaxStreamsForTest; ++i) { EXPECT_TRUE(QuicServerSessionBasePeer::GetOrCreateStream(session_.get(), stream_id)); - stream_id += QuicUtils::StreamIdDelta(connection_->transport_version()); + stream_id += QuicUtils::StreamIdDelta(transport_version()); } if (!VersionHasIetfQuicFrames(transport_version())) { @@ -382,11 +387,11 @@ TEST_P(QuicServerSessionBaseTest, MaxOpenStreams) { for (size_t i = 0; i < kMaxStreamsMinimumIncrement; ++i) { EXPECT_TRUE(QuicServerSessionBasePeer::GetOrCreateStream(session_.get(), stream_id)); - stream_id += QuicUtils::StreamIdDelta(connection_->transport_version()); + stream_id += QuicUtils::StreamIdDelta(transport_version()); } } // Now violate the server's internal stream limit. - stream_id += QuicUtils::StreamIdDelta(connection_->transport_version()); + stream_id += QuicUtils::StreamIdDelta(transport_version()); if (!VersionHasIetfQuicFrames(transport_version())) { // For non-version 99, QUIC responds to an attempt to exceed the stream @@ -418,8 +423,7 @@ TEST_P(QuicServerSessionBaseTest, MaxAvailableBidirectionalStreams) { session_.get(), GetNthClientInitiatedBidirectionalId(0))); // Establish available streams up to the server's limit. - QuicStreamId next_id = - QuicUtils::StreamIdDelta(connection_->transport_version()); + QuicStreamId next_id = QuicUtils::StreamIdDelta(transport_version()); const int kLimitingStreamId = GetNthClientInitiatedBidirectionalId(kAvailableStreamLimit + 1); if (!VersionHasIetfQuicFrames(transport_version())) { @@ -456,7 +460,7 @@ TEST_P(QuicServerSessionBaseTest, GetEvenIncomingError) { TEST_P(QuicServerSessionBaseTest, GetStreamDisconnected) { // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. - if (GetParam() != AllSupportedVersions()[0]) { + if (version() != AllSupportedVersions()[0]) { return; } @@ -527,14 +531,13 @@ TEST_P(QuicServerSessionBaseTest, BandwidthEstimates) { if (!VersionUsesHttp3(transport_version())) { session_->UnregisterStreamPriority( - QuicUtils::GetHeadersStreamId(connection_->transport_version()), + QuicUtils::GetHeadersStreamId(transport_version()), /*is_static=*/true); } QuicServerSessionBasePeer::SetCryptoStream(session_.get(), nullptr); MockQuicCryptoServerStream* quic_crypto_stream = nullptr; MockTlsServerHandshaker* tls_server_stream = nullptr; - if (session_->connection()->version().handshake_protocol == - PROTOCOL_QUIC_CRYPTO) { + if (version().handshake_protocol == PROTOCOL_QUIC_CRYPTO) { quic_crypto_stream = new MockQuicCryptoServerStream( &crypto_config_, &compressed_certs_cache_, session_.get(), &stream_helper_); @@ -548,7 +551,7 @@ TEST_P(QuicServerSessionBaseTest, BandwidthEstimates) { } if (!VersionUsesHttp3(transport_version())) { session_->RegisterStreamPriority( - QuicUtils::GetHeadersStreamId(connection_->transport_version()), + QuicUtils::GetHeadersStreamId(transport_version()), /*is_static=*/true, spdy::SpdyStreamPrecedence(QuicStream::kDefaultPriority)); } @@ -571,11 +574,11 @@ TEST_P(QuicServerSessionBaseTest, BandwidthEstimates) { // Queue up some pending data. if (!VersionUsesHttp3(transport_version())) { session_->MarkConnectionLevelWriteBlocked( - QuicUtils::GetHeadersStreamId(connection_->transport_version())); + QuicUtils::GetHeadersStreamId(transport_version())); } else { session_->MarkConnectionLevelWriteBlocked( - QuicUtils::GetFirstUnidirectionalStreamId( - connection_->transport_version(), Perspective::IS_SERVER)); + QuicUtils::GetFirstUnidirectionalStreamId(transport_version(), + Perspective::IS_SERVER)); } EXPECT_TRUE(session_->HasDataToWrite()); @@ -643,7 +646,7 @@ TEST_P(QuicServerSessionBaseTest, BandwidthEstimates) { } TEST_P(QuicServerSessionBaseTest, BandwidthResumptionExperiment) { - if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) { + if (version().handshake_protocol == PROTOCOL_TLS1_3) { // This test relies on resumption, which is not currently supported by the // TLS handshake. // TODO(nharper): Add support for resumption to the TLS handshake. @@ -720,20 +723,6 @@ TEST_P(QuicServerSessionBaseTest, NoBandwidthResumptionByDefault) { QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get())); } -TEST_P(QuicServerSessionBaseTest, TurnOffServerPush) { - if (session_->version().UsesHttp3()) { - return; - } - - EXPECT_TRUE(session_->server_push_enabled()); - QuicTagVector copt; - copt.push_back(kQNSP); - QuicConfigPeer::SetReceivedConnectionOptions(session_->config(), copt); - connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); - session_->OnConfigNegotiated(); - EXPECT_FALSE(session_->server_push_enabled()); -} - // Tests which check the lifetime management of data members of // QuicCryptoServerStream objects when async GetProof is in use. class StreamMemberLifetimeTest : public QuicServerSessionBaseTest { @@ -762,7 +751,7 @@ INSTANTIATE_TEST_SUITE_P(StreamMemberLifetimeTests, // ProofSource::GetProof. Delay the completion of the operation until after the // stream has been destroyed, and verify that there are no memory bugs. TEST_P(StreamMemberLifetimeTest, Basic) { - if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) { + if (version().handshake_protocol == PROTOCOL_TLS1_3) { // This test depends on the QUIC crypto protocol, so it is disabled for the // TLS handshake. // TODO(nharper): Fix this test so it doesn't rely on QUIC crypto. @@ -771,9 +760,9 @@ TEST_P(StreamMemberLifetimeTest, Basic) { const QuicClock* clock = helper_.GetClock(); CryptoHandshakeMessage chlo = crypto_test_utils::GenerateDefaultInchoateCHLO( - clock, GetParam().transport_version, &crypto_config_); + clock, transport_version(), &crypto_config_); chlo.SetVector(kCOPT, QuicTagVector{kREJ}); - std::vector<ParsedQuicVersion> packet_version_list = {GetParam()}; + std::vector<ParsedQuicVersion> packet_version_list = {version()}; std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket( TestConnectionId(1), EmptyQuicConnectionId(), true, false, 1, std::string(chlo.GetSerialized().AsStringPiece()), CONNECTION_ID_PRESENT, diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc index 7197c8ab7ed..af17b54ecff 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc @@ -278,10 +278,11 @@ TEST_P(QuicSpdyClientSessionTest, NoEncryptionAfterInitialEncryption) { EXPECT_TRUE(session_->CreateOutgoingBidirectionalStream() == nullptr); // Verify that no data may be send on existing streams. char data[] = "hello world"; - EXPECT_QUIC_BUG( + QuicConsumedData consumed = session_->WritevData(stream->id(), ABSL_ARRAYSIZE(data), 0, NO_FIN, - NOT_RETRANSMISSION, ENCRYPTION_INITIAL), - "Client: Try to send data of stream"); + NOT_RETRANSMISSION, ENCRYPTION_INITIAL); + EXPECT_EQ(0u, consumed.bytes_consumed); + EXPECT_FALSE(consumed.fin_consumed); } TEST_P(QuicSpdyClientSessionTest, MaxNumStreamsWithNoFinOrRst) { diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.cc index 16948101c6c..276a7aff6b6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.cc @@ -30,7 +30,6 @@ #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_stack_trace.h" -#include "common/platform/api/quiche_text_utils.h" #include "spdy/core/http2_frame_decoder_adapter.h" using http2::Http2DecoderAdapter; @@ -497,7 +496,6 @@ QuicSpdySession::QuicSpdySession( spdy_framer_visitor_(new SpdyFramerVisitor(this)), debug_visitor_(nullptr), destruction_indicator_(123456789), - server_push_enabled_(true), next_available_datagram_flow_id_(perspective() == Perspective::IS_SERVER ? kFirstDatagramFlowIdServer : kFirstDatagramFlowIdClient) { @@ -798,15 +796,12 @@ void QuicSpdySession::SendHttp3GoAway(QuicErrorCode error_code, const std::string& reason) { QUICHE_DCHECK_EQ(perspective(), Perspective::IS_SERVER); QUICHE_DCHECK(VersionUsesHttp3(transport_version())); - if (GetQuicReloadableFlag(quic_encrypted_goaway)) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_encrypted_goaway, 2, 2); - if (!IsEncryptionEstablished()) { - QUIC_CODE_COUNT(quic_h3_goaway_before_encryption_established); - connection()->CloseConnection( - error_code, reason, - ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); - return; - } + if (!IsEncryptionEstablished()) { + QUIC_CODE_COUNT(quic_h3_goaway_before_encryption_established); + connection()->CloseConnection( + error_code, reason, + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; } QuicStreamId stream_id; @@ -858,10 +853,6 @@ void QuicSpdySession::WritePushPromise(QuicStreamId original_stream_id, absl::string_view(frame.data(), frame.size()), false, nullptr); } -bool QuicSpdySession::server_push_enabled() const { - return VersionUsesHttp3(transport_version()) ? false : server_push_enabled_; -} - void QuicSpdySession::SendInitialData() { if (!VersionUsesHttp3(transport_version())) { return; @@ -1239,8 +1230,7 @@ bool QuicSpdySession::OnSetting(uint64_t id, uint64_t value) { return true; } QUIC_DVLOG(1) << ENDPOINT << "SETTINGS_ENABLE_PUSH received with value " - << value; - server_push_enabled_ = value; + << value << ", ignoring."; break; } else { QUIC_DLOG(ERROR) @@ -1458,14 +1448,7 @@ QuicStream* QuicSpdySession::ProcessPendingStream(PendingStream* pending) { default: break; } - if (GetQuicReloadableFlag(quic_unify_stop_sending)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_unify_stop_sending); - MaybeSendStopSendingFrame(pending->id(), QUIC_STREAM_STREAM_CREATION_ERROR); - } else { - // TODO(renjietang): deprecate SendStopSending() when the flag is - // deprecated. - SendStopSending(QUIC_STREAM_STREAM_CREATION_ERROR, pending->id()); - } + MaybeSendStopSendingFrame(pending->id(), QUIC_STREAM_STREAM_CREATION_ERROR); pending->StopReading(); return nullptr; } diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.h index b2004845306..214d1da13e2 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.h @@ -26,13 +26,13 @@ #include "quic/core/qpack/qpack_encoder_stream_sender.h" #include "quic/core/qpack/qpack_receive_stream.h" #include "quic/core/qpack/qpack_send_stream.h" -#include "quic/core/quic_circular_deque.h" #include "quic/core/quic_session.h" #include "quic/core/quic_time.h" #include "quic/core/quic_types.h" #include "quic/core/quic_versions.h" #include "quic/platform/api/quic_containers.h" #include "quic/platform/api/quic_export.h" +#include "common/quiche_circular_deque.h" #include "spdy/core/http2_frame_decoder_adapter.h" namespace quic { @@ -251,12 +251,6 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession const QuicHeadersStream* headers_stream() const { return headers_stream_; } - // Returns whether server push is enabled. - // For a Google QUIC client this always returns false. - // For a Google QUIC server this is set by incoming SETTINGS_ENABLE_PUSH. - // For an IETF QUIC client or server this returns false. - bool server_push_enabled() const; - // Called when the control stream receives HTTP/3 SETTINGS. // Returns false in case of 0-RTT if received settings are incompatible with // cached values, true otherwise. @@ -684,10 +678,6 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // an use-after-free. int32_t destruction_indicator_; - // Used in Google QUIC only. Set every time SETTINGS_ENABLE_PUSH is received. - // Defaults to true. - bool server_push_enabled_; - // The identifier in the most recently received GOAWAY frame. Unset if no // GOAWAY frame has been received yet. absl::optional<uint64_t> last_received_http3_goaway_id_; diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session_test.cc index d8bfa782aa7..29e1f6b7bcd 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session_test.cc @@ -38,7 +38,6 @@ #include "quic/platform/api/quic_map_util.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/qpack/qpack_encoder_peer.h" -#include "quic/test_tools/qpack/qpack_header_table_peer.h" #include "quic/test_tools/qpack/qpack_test_utils.h" #include "quic/test_tools/quic_config_peer.h" #include "quic/test_tools/quic_connection_peer.h" @@ -48,7 +47,6 @@ #include "quic/test_tools/quic_stream_peer.h" #include "quic/test_tools/quic_stream_send_buffer_peer.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/quiche_endian.h" #include "common/test_tools/quiche_test_utils.h" #include "spdy/core/spdy_framer.h" @@ -559,7 +557,6 @@ class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { } void ReceiveWebTransportSession(WebTransportSessionId session_id) { - SetQuicReloadableFlag(quic_accept_empty_stream_frame_with_no_fin, true); QuicStreamFrame frame(session_id, /*fin=*/false, /*offset=*/0, absl::string_view()); session_.OnStreamFrame(frame); @@ -1178,7 +1175,6 @@ TEST_P(QuicSpdySessionTestServer, SendGoAway) { } TEST_P(QuicSpdySessionTestServer, SendGoAwayWithoutEncryption) { - SetQuicReloadableFlag(quic_encrypted_goaway, true); if (VersionHasIetfQuicFrames(transport_version())) { // HTTP/3 GOAWAY has different semantic and thus has its own test. return; @@ -1220,7 +1216,6 @@ TEST_P(QuicSpdySessionTestServer, SendHttp3GoAway) { } TEST_P(QuicSpdySessionTestServer, SendHttp3GoAwayWithoutEncryption) { - SetQuicReloadableFlag(quic_encrypted_goaway, true); if (!VersionUsesHttp3(transport_version())) { return; } @@ -1918,12 +1913,6 @@ TEST_P(QuicSpdySessionTestClient, BadStreamFramePendingStream) { GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); // A bad stream frame with no data and no fin. QuicStreamFrame data1(stream_id1, false, 0, 0); - if (!GetQuicReloadableFlag(quic_accept_empty_stream_frame_with_no_fin)) { - EXPECT_CALL(*connection_, CloseConnection(_, _, _)) - .WillOnce( - Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); - EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _)); - } session_.OnStreamFrame(data1); } @@ -2519,19 +2508,17 @@ TEST_P(QuicSpdySessionTestServer, ReceiveControlStream) { QuicStreamFrame frame(stream_id, false, 1, absl::string_view(data)); QpackEncoder* qpack_encoder = session_.qpack_encoder(); - QpackHeaderTable* header_table = + QpackEncoderHeaderTable* header_table = QpackEncoderPeer::header_table(qpack_encoder); - EXPECT_NE(512u, - QpackHeaderTablePeer::maximum_dynamic_table_capacity(header_table)); + EXPECT_NE(512u, header_table->maximum_dynamic_table_capacity()); EXPECT_NE(5u, session_.max_outbound_header_list_size()); EXPECT_NE(42u, QpackEncoderPeer::maximum_blocked_streams(qpack_encoder)); EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(settings)); session_.OnStreamFrame(frame); - EXPECT_EQ(512u, - QpackHeaderTablePeer::maximum_dynamic_table_capacity(header_table)); + EXPECT_EQ(512u, header_table->maximum_dynamic_table_capacity()); EXPECT_EQ(5u, session_.max_outbound_header_list_size()); EXPECT_EQ(42u, QpackEncoderPeer::maximum_blocked_streams(qpack_encoder)); } @@ -2618,10 +2605,10 @@ TEST_P(QuicSpdySessionTestServer, SessionDestroyedWhileHeaderDecodingBlocked) { EXPECT_FALSE(stream->headers_decompressed()); // |session_| gets destoyed. That destroys QpackDecoder, a member of - // QuicSpdySession (derived class), which destroys QpackHeaderTable. + // QuicSpdySession (derived class), which destroys QpackDecoderHeaderTable. // Then |*stream|, owned by QuicSession (base class) get destroyed, which - // destroys QpackProgessiveDecoder, a registered Observer of QpackHeaderTable. - // This must not cause a crash. + // destroys QpackProgessiveDecoder, a registered Observer of + // QpackDecoderHeaderTable. This must not cause a crash. } TEST_P(QuicSpdySessionTestClient, ResetAfterInvalidIncomingStreamType) { @@ -2897,10 +2884,7 @@ TEST_P(QuicSpdySessionTestClient, Http3GoAwayLargerIdThanBefore) { session_.OnHttp3GoAway(stream_id2); } -// Test that receipt of CANCEL_PUSH frame does not result in closing the -// connection. -// TODO(b/151841240): Handle CANCEL_PUSH frames instead of ignoring them. -TEST_P(QuicSpdySessionTestClient, IgnoreCancelPush) { +TEST_P(QuicSpdySessionTestClient, CloseConnectionOnCancelPush) { if (!VersionUsesHttp3(transport_version())) { return; } @@ -2937,16 +2921,19 @@ TEST_P(QuicSpdySessionTestClient, IgnoreCancelPush) { "00"); // push ID QuicStreamFrame data3(receive_control_stream_id, /* fin = */ false, offset, cancel_push_frame); - EXPECT_CALL(debug_visitor, OnCancelPushFrameReceived(_)); - session_.OnStreamFrame(data3); -} - -TEST_P(QuicSpdySessionTestServer, ServerPushEnabledDefaultValue) { - if (VersionUsesHttp3(transport_version())) { - EXPECT_FALSE(session_.server_push_enabled()); + if (GetQuicReloadableFlag(quic_error_on_http3_push)) { + EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_FRAME_ERROR, + "CANCEL_PUSH frame received.", _)) + .WillOnce( + Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); + EXPECT_CALL(*connection_, + SendConnectionClosePacket(QUIC_HTTP_FRAME_ERROR, _, + "CANCEL_PUSH frame received.")); } else { - EXPECT_TRUE(session_.server_push_enabled()); + // CANCEL_PUSH is ignored. + EXPECT_CALL(debug_visitor, OnCancelPushFrameReceived(_)); } + session_.OnStreamFrame(data3); } TEST_P(QuicSpdySessionTestServer, OnSetting) { @@ -2964,7 +2951,7 @@ TEST_P(QuicSpdySessionTestServer, OnSetting) { session_.OnSetting(SETTINGS_QPACK_BLOCKED_STREAMS, 12); EXPECT_EQ(12u, QpackEncoderPeer::maximum_blocked_streams(qpack_encoder)); - QpackHeaderTable* header_table = + QpackEncoderHeaderTable* header_table = QpackEncoderPeer::header_table(qpack_encoder); EXPECT_EQ(0u, header_table->maximum_dynamic_table_capacity()); session_.OnSetting(SETTINGS_QPACK_MAX_TABLE_CAPACITY, 37); @@ -2978,10 +2965,6 @@ TEST_P(QuicSpdySessionTestServer, OnSetting) { session_.OnSetting(SETTINGS_MAX_FIELD_SECTION_SIZE, 5); EXPECT_EQ(5u, session_.max_outbound_header_list_size()); - EXPECT_TRUE(session_.server_push_enabled()); - session_.OnSetting(spdy::SETTINGS_ENABLE_PUSH, 0); - EXPECT_FALSE(session_.server_push_enabled()); - spdy::HpackEncoder* hpack_encoder = QuicSpdySessionPeer::GetSpdyFramer(&session_)->GetHpackEncoder(); EXPECT_EQ(4096u, hpack_encoder->CurrentHeaderTableSizeSetting()); @@ -3108,10 +3091,7 @@ TEST_P(QuicSpdySessionTestServer, PeerClosesCriticalSendStream) { session_.OnStopSendingFrame(stop_sending_encoder_stream); } -// Test that receipt of CANCEL_PUSH frame does not result in closing the -// connection. -// TODO(b/151841240): Handle CANCEL_PUSH frames instead of ignoring them. -TEST_P(QuicSpdySessionTestServer, IgnoreCancelPush) { +TEST_P(QuicSpdySessionTestServer, CloseConnectionOnCancelPush) { if (!VersionUsesHttp3(transport_version())) { return; } @@ -3148,7 +3128,18 @@ TEST_P(QuicSpdySessionTestServer, IgnoreCancelPush) { "00"); // push ID QuicStreamFrame data3(receive_control_stream_id, /* fin = */ false, offset, cancel_push_frame); - EXPECT_CALL(debug_visitor, OnCancelPushFrameReceived(_)); + if (GetQuicReloadableFlag(quic_error_on_http3_push)) { + EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_FRAME_ERROR, + "CANCEL_PUSH frame received.", _)) + .WillOnce( + Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); + EXPECT_CALL(*connection_, + SendConnectionClosePacket(QUIC_HTTP_FRAME_ERROR, _, + "CANCEL_PUSH frame received.")); + } else { + // CANCEL_PUSH is ignored. + EXPECT_CALL(debug_visitor, OnCancelPushFrameReceived(_)); + } session_.OnStreamFrame(data3); } diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.cc index 4d4cc95f94d..6ec48ca5896 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.cc @@ -30,7 +30,7 @@ #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_mem_slice_storage.h" -#include "common/platform/api/quiche_text_utils.h" +#include "common/quiche_text_utils.h" #include "spdy/core/spdy_protocol.h" using spdy::SpdyHeaderBlock; @@ -434,7 +434,12 @@ QuicConsumedData QuicSpdyStream::WriteBodySlices(QuicMemSliceSpan slices, QuicConnection::ScopedPacketFlusher flusher(spdy_session_->connection()); // Write frame header. +#if !defined(__ANDROID__) struct iovec header_iov = {static_cast<void*>(buffer.get()), header_length}; +#else + struct iovec header_iov = {static_cast<void*>(buffer.get()), + static_cast<__kernel_size_t>(header_length)}; +#endif QuicMemSliceStorage storage( &header_iov, 1, spdy_session_->connection()->helper()->GetStreamSendBufferAllocator(), diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.h index 7b2d9f19186..f34801a383c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.h @@ -7,11 +7,11 @@ #include "absl/base/attributes.h" #include "absl/strings/string_view.h" -#include "quic/core/quic_circular_deque.h" #include "quic/core/quic_constants.h" #include "quic/platform/api/quic_bug_tracker.h" #include "quic/platform/api/quic_export.h" #include "quic/platform/api/quic_iovec.h" +#include "common/quiche_circular_deque.h" namespace quic { @@ -84,7 +84,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStreamBodyManager { QuicByteCount trailing_non_body_byte_count; }; // Queue of body fragments and trailing non-body byte counts. - QuicCircularDeque<Fragment> fragments_; + quiche::QuicheCircularDeque<Fragment> fragments_; // Total body bytes received. QuicByteCount total_body_bytes_received_; }; diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_test.cc index bb464bdb4d5..8b814b8400b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_test.cc @@ -2772,6 +2772,9 @@ TEST_P(QuicSpdyStreamIncrementalConsumptionTest, UnknownFramesInterleaved) { // TODO(b/171463363): Remove. TEST_P(QuicSpdyStreamTest, PushPromiseOnDataStream) { Initialize(kShouldProcessData); + if (GetQuicReloadableFlag(quic_error_on_http3_push)) { + return; + } if (!UsesHttp3()) { return; } @@ -2802,6 +2805,9 @@ TEST_P(QuicSpdyStreamTest, PushPromiseOnDataStream) { TEST_P(QuicSpdyStreamTest, OnStreamHeaderBlockArgumentDoesNotIncludePushedHeaderBlock) { Initialize(kShouldProcessData); + if (GetQuicReloadableFlag(quic_error_on_http3_push)) { + return; + } if (!UsesHttp3()) { return; } diff --git a/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.cc b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.cc index d3a3c92d209..28b463afebe 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.cc @@ -17,7 +17,7 @@ #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_map_util.h" -#include "common/platform/api/quiche_text_utils.h" +#include "common/quiche_text_utils.h" #include "spdy/core/spdy_protocol.h" using spdy::SpdyHeaderBlock; diff --git a/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils_test.cc index c7207f383b3..7307665da3d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils_test.cc @@ -9,7 +9,6 @@ #include "absl/strings/string_view.h" #include "quic/core/http/spdy_utils.h" #include "quic/platform/api/quic_test.h" -#include "common/platform/api/quiche_text_utils.h" using spdy::SpdyHeaderBlock; using testing::Pair; diff --git a/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.h b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.h index 0c91c2b32a4..667256ea515 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.h @@ -75,8 +75,8 @@ class QUIC_EXPORT_PRIVATE WebTransportHttp3 bool ready_ = false; std::unique_ptr<WebTransportVisitor> visitor_; absl::flat_hash_set<QuicStreamId> streams_; - QuicCircularDeque<QuicStreamId> incoming_bidirectional_streams_; - QuicCircularDeque<QuicStreamId> incoming_unidirectional_streams_; + quiche::QuicheCircularDeque<QuicStreamId> incoming_bidirectional_streams_; + quiche::QuicheCircularDeque<QuicStreamId> incoming_unidirectional_streams_; }; class QUIC_EXPORT_PRIVATE WebTransportHttp3UnidirectionalStream diff --git a/chromium/net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h b/chromium/net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h index b83916060e9..8e80c7de4b7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h +++ b/chromium/net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h @@ -5,11 +5,11 @@ #ifndef QUICHE_QUIC_CORE_PACKET_NUMBER_INDEXED_QUEUE_H_ #define QUICHE_QUIC_CORE_PACKET_NUMBER_INDEXED_QUEUE_H_ -#include "quic/core/quic_circular_deque.h" #include "quic/core/quic_constants.h" #include "quic/core/quic_packet_number.h" #include "quic/core/quic_types.h" #include "quic/platform/api/quic_bug_tracker.h" +#include "common/quiche_circular_deque.h" namespace quic { @@ -115,7 +115,7 @@ class QUIC_NO_EXPORT PacketNumberIndexedQueue { return const_cast<EntryWrapper*>(const_this->GetEntryWrapper(offset)); } - QuicCircularDeque<EntryWrapper> entries_; + quiche::QuicheCircularDeque<EntryWrapper> entries_; // NOTE(wub): When --quic_bw_sampler_remove_packets_once_per_congestion_event // is enabled, |number_of_present_entries_| only represents number of holes, // which does not include number of acked or lost packets. diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager.h index 70870a60314..4188350e463 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_blocking_manager.h @@ -75,8 +75,8 @@ class QUIC_EXPORT_PRIVATE QpackBlockingManager { // A stream typically has only one header block, except for the rare cases of // 1xx responses, trailers, or push promises. Even if there are multiple // header blocks sent on a single stream, they might not be blocked at the - // same time. Use std::list instead of QuicCircularDeque because it has lower - // memory footprint when holding few elements. + // same time. Use std::list instead of quiche::QuicheCircularDeque because it + // has lower memory footprint when holding few elements. using HeaderBlocksForStream = std::list<IndexSet>; using HeaderBlocks = absl::flat_hash_map<QuicStreamId, HeaderBlocksForStream>; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc index 9ba7f1c511f..217f866dce1 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc @@ -12,7 +12,6 @@ #include "quic/platform/api/quic_test.h" #include "quic/test_tools/qpack/qpack_decoder_test_utils.h" #include "quic/test_tools/qpack/qpack_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" using ::testing::_; using ::testing::ElementsAre; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h index 32178bd9d7c..b947ce3cf0e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h @@ -111,7 +111,7 @@ class QUIC_EXPORT_PRIVATE QpackDecoder EncoderStreamErrorDelegate* const encoder_stream_error_delegate_; QpackEncoderStreamReceiver encoder_stream_receiver_; QpackDecoderStreamSender decoder_stream_sender_; - QpackHeaderTable header_table_; + QpackDecoderHeaderTable header_table_; std::set<QuicStreamId> blocked_streams_; const uint64_t maximum_blocked_streams_; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver_test.cc index 272597ac191..0a76a17450f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver_test.cc @@ -7,7 +7,6 @@ #include "absl/strings/escaping.h" #include "absl/strings/string_view.h" #include "quic/platform/api/quic_test.h" -#include "common/platform/api/quiche_text_utils.h" using testing::Eq; using testing::StrictMock; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender_test.cc index 275e76f3ca1..708e7a3d3f1 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender_test.cc @@ -7,7 +7,6 @@ #include "absl/strings/escaping.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/qpack/qpack_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" using ::testing::Eq; using ::testing::StrictMock; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc index 3b57ca6f1c3..772b1d25480 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc @@ -13,7 +13,6 @@ #include "quic/platform/api/quic_test.h" #include "quic/test_tools/qpack/qpack_decoder_test_utils.h" #include "quic/test_tools/qpack/qpack_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" #include "spdy/core/spdy_header_block.h" using ::testing::_; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc index 5e61ee7490b..3ae944152c0 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc @@ -122,7 +122,7 @@ QpackEncoder::Representations QpackEncoder::FirstPassEncode( header_table_.FindHeaderField(name, value, &is_static, &index); switch (match_type) { - case QpackHeaderTable::MatchType::kNameAndValue: + case QpackEncoderHeaderTable::MatchType::kNameAndValue: if (is_static) { // Refer to entry directly. representations.push_back( @@ -175,7 +175,7 @@ QpackEncoder::Representations QpackEncoder::FirstPassEncode( break; - case QpackHeaderTable::MatchType::kName: + case QpackEncoderHeaderTable::MatchType::kName: if (is_static) { if (blocking_allowed && QpackEntry::Size(name, value) <= @@ -242,7 +242,7 @@ QpackEncoder::Representations QpackEncoder::FirstPassEncode( break; - case QpackHeaderTable::MatchType::kNoMatch: + case QpackEncoderHeaderTable::MatchType::kNoMatch: // If allowed, insert entry and refer to it. if (!blocking_allowed) { blocked_stream_limit_exhausted = true; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h index 9d024417c93..c3d77fea061 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h @@ -153,7 +153,7 @@ class QUIC_EXPORT_PRIVATE QpackEncoder DecoderStreamErrorDelegate* const decoder_stream_error_delegate_; QpackDecoderStreamReceiver decoder_stream_receiver_; QpackEncoderStreamSender encoder_stream_sender_; - QpackHeaderTable header_table_; + QpackEncoderHeaderTable header_table_; uint64_t maximum_blocked_streams_; QpackBlockingManager blocking_manager_; int header_list_count_; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver_test.cc index efe5eb90b98..2fa440b51b4 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver_test.cc @@ -7,7 +7,6 @@ #include "absl/strings/escaping.h" #include "absl/strings/string_view.h" #include "quic/platform/api/quic_test.h" -#include "common/platform/api/quiche_text_utils.h" using testing::Eq; using testing::StrictMock; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender_test.cc index 5078c8f0d05..98e3e23030f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender_test.cc @@ -7,7 +7,6 @@ #include "absl/strings/escaping.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/qpack/qpack_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" using ::testing::Eq; using ::testing::StrictMock; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test.cc index 5045c8fb350..21397ed8571 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test.cc @@ -14,9 +14,7 @@ #include "quic/platform/api/quic_test.h" #include "quic/test_tools/qpack/qpack_encoder_peer.h" #include "quic/test_tools/qpack/qpack_encoder_test_utils.h" -#include "quic/test_tools/qpack/qpack_header_table_peer.h" #include "quic/test_tools/qpack/qpack_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" using ::testing::_; using ::testing::Eq; @@ -186,7 +184,8 @@ TEST_F(QpackEncoderTest, TooLargeInsertCountIncrement) { // Regression test for https://crbug.com/1014372. TEST_F(QpackEncoderTest, InsertCountIncrementOverflow) { - QpackHeaderTable* header_table = QpackEncoderPeer::header_table(&encoder_); + QpackEncoderHeaderTable* header_table = + QpackEncoderPeer::header_table(&encoder_); // Set dynamic table capacity large enough to hold one entry. header_table->SetMaximumDynamicTableCapacity(4096); @@ -461,11 +460,11 @@ TEST_F(QpackEncoderTest, DynamicTableCapacityLessThanMaximum) { encoder_.SetMaximumDynamicTableCapacity(1024); encoder_.SetDynamicTableCapacity(30); - QpackHeaderTable* header_table = QpackEncoderPeer::header_table(&encoder_); + QpackEncoderHeaderTable* header_table = + QpackEncoderPeer::header_table(&encoder_); - EXPECT_EQ(1024u, - QpackHeaderTablePeer::maximum_dynamic_table_capacity(header_table)); - EXPECT_EQ(30u, QpackHeaderTablePeer::dynamic_table_capacity(header_table)); + EXPECT_EQ(1024u, header_table->maximum_dynamic_table_capacity()); + EXPECT_EQ(30u, header_table->dynamic_table_capacity()); } } // namespace diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc index da3636db4e4..660727231d6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc @@ -10,47 +10,47 @@ namespace quic { -QpackHeaderTable::QpackHeaderTable() - : static_entries_(ObtainQpackStaticTable().GetStaticEntries()), - static_index_(ObtainQpackStaticTable().GetStaticIndex()), - static_name_index_(ObtainQpackStaticTable().GetStaticNameIndex()), - dynamic_table_size_(0), - dynamic_table_capacity_(0), - maximum_dynamic_table_capacity_(0), - max_entries_(0), - dropped_entry_count_(0), - dynamic_table_entry_referenced_(false) {} - -QpackHeaderTable::~QpackHeaderTable() { - for (auto& entry : observers_) { - entry.second->Cancel(); - } -} +QpackEncoderHeaderTable::QpackEncoderHeaderTable() + : static_index_(ObtainQpackStaticTable().GetStaticIndex()), + static_name_index_(ObtainQpackStaticTable().GetStaticNameIndex()) {} -const QpackEntry* QpackHeaderTable::LookupEntry(bool is_static, - uint64_t index) const { - if (is_static) { - if (index >= static_entries_.size()) { - return nullptr; - } +uint64_t QpackEncoderHeaderTable::InsertEntry(absl::string_view name, + absl::string_view value) { + const uint64_t index = + QpackHeaderTableBase<QpackEncoderDynamicTable>::InsertEntry(name, value); - return &static_entries_[index]; - } + // Make name and value point to the new entry. + name = dynamic_entries().back().name(); + value = dynamic_entries().back().value(); - if (index < dropped_entry_count_) { - return nullptr; + auto index_result = dynamic_index_.insert( + std::make_pair(QpackLookupEntry{name, value}, index)); + if (!index_result.second) { + // An entry with the same name and value already exists. It needs to be + // replaced, because |dynamic_index_| tracks the most recent entry for a + // given name and value. + QUICHE_DCHECK_GT(index, index_result.first->second); + dynamic_index_.erase(index_result.first); + auto result = dynamic_index_.insert( + std::make_pair(QpackLookupEntry{name, value}, index)); + QUICHE_CHECK(result.second); } - index -= dropped_entry_count_; - - if (index >= dynamic_entries_.size()) { - return nullptr; + auto name_result = dynamic_name_index_.insert({name, index}); + if (!name_result.second) { + // An entry with the same name already exists. It needs to be replaced, + // because |dynamic_name_index_| tracks the most recent entry for a given + // name. + QUICHE_DCHECK_GT(index, name_result.first->second); + dynamic_name_index_.erase(name_result.first); + auto result = dynamic_name_index_.insert({name, index}); + QUICHE_CHECK(result.second); } - return &dynamic_entries_[index]; + return index; } -QpackHeaderTable::MatchType QpackHeaderTable::FindHeaderField( +QpackEncoderHeaderTable::MatchType QpackEncoderHeaderTable::FindHeaderField( absl::string_view name, absl::string_view value, bool* is_static, @@ -92,56 +92,92 @@ QpackHeaderTable::MatchType QpackHeaderTable::FindHeaderField( return MatchType::kNoMatch; } -bool QpackHeaderTable::EntryFitsDynamicTableCapacity( - absl::string_view name, - absl::string_view value) const { - return QpackEntry::Size(name, value) <= dynamic_table_capacity_; +uint64_t QpackEncoderHeaderTable::MaxInsertSizeWithoutEvictingGivenEntry( + uint64_t index) const { + QUICHE_DCHECK_LE(dropped_entry_count(), index); + + if (index > inserted_entry_count()) { + // All entries are allowed to be evicted. + return dynamic_table_capacity(); + } + + // Initialize to current available capacity. + uint64_t max_insert_size = dynamic_table_capacity() - dynamic_table_size(); + + uint64_t entry_index = dropped_entry_count(); + for (const auto& entry : dynamic_entries()) { + if (entry_index >= index) { + break; + } + ++entry_index; + max_insert_size += entry.Size(); + } + + return max_insert_size; } -uint64_t QpackHeaderTable::InsertEntry(absl::string_view name, - absl::string_view value) { - QUICHE_DCHECK(EntryFitsDynamicTableCapacity(name, value)); +uint64_t QpackEncoderHeaderTable::draining_index( + float draining_fraction) const { + QUICHE_DCHECK_LE(0.0, draining_fraction); + QUICHE_DCHECK_LE(draining_fraction, 1.0); - const uint64_t index = dropped_entry_count_ + dynamic_entries_.size(); + const uint64_t required_space = draining_fraction * dynamic_table_capacity(); + uint64_t space_above_draining_index = + dynamic_table_capacity() - dynamic_table_size(); - // Copy name and value before modifying the container, because evicting - // entries or even inserting a new one might invalidate |name| or |value| if - // they point to an entry. - QpackEntry new_entry((std::string(name)), (std::string(value))); - const size_t entry_size = new_entry.Size(); + if (dynamic_entries().empty() || + space_above_draining_index >= required_space) { + return dropped_entry_count(); + } - EvictDownToCapacity(dynamic_table_capacity_ - entry_size); + auto it = dynamic_entries().begin(); + uint64_t entry_index = dropped_entry_count(); + while (space_above_draining_index < required_space) { + space_above_draining_index += it->Size(); + ++it; + ++entry_index; + if (it == dynamic_entries().end()) { + return inserted_entry_count(); + } + } - dynamic_table_size_ += entry_size; - dynamic_entries_.push_back(std::move(new_entry)); + return entry_index; +} - // Make name and value point to the new entry. - name = dynamic_entries_.back().name(); - value = dynamic_entries_.back().value(); +void QpackEncoderHeaderTable::RemoveEntryFromEnd() { + const QpackEntry* const entry = &dynamic_entries().front(); + const uint64_t index = dropped_entry_count(); - auto index_result = dynamic_index_.insert( - std::make_pair(QpackLookupEntry{name, value}, index)); - if (!index_result.second) { - // An entry with the same name and value already exists. It needs to be - // replaced, because |dynamic_index_| tracks the most recent entry for a - // given name and value. - QUICHE_DCHECK_GT(index, index_result.first->second); - dynamic_index_.erase(index_result.first); - auto result = dynamic_index_.insert( - std::make_pair(QpackLookupEntry{name, value}, index)); - QUICHE_CHECK(result.second); + auto index_it = dynamic_index_.find({entry->name(), entry->value()}); + // Remove |dynamic_index_| entry only if it points to the same + // QpackEntry in dynamic_entries(). + if (index_it != dynamic_index_.end() && index_it->second == index) { + dynamic_index_.erase(index_it); } - auto name_result = dynamic_name_index_.insert({name, index}); - if (!name_result.second) { - // An entry with the same name already exists. It needs to be replaced, - // because |dynamic_name_index_| tracks the most recent entry for a given - // name. - QUICHE_DCHECK_GT(index, name_result.first->second); - dynamic_name_index_.erase(name_result.first); - auto result = dynamic_name_index_.insert({name, index}); - QUICHE_CHECK(result.second); + auto name_it = dynamic_name_index_.find(entry->name()); + // Remove |dynamic_name_index_| entry only if it points to the same + // QpackEntry in dynamic_entries(). + if (name_it != dynamic_name_index_.end() && name_it->second == index) { + dynamic_name_index_.erase(name_it); + } + + QpackHeaderTableBase<QpackEncoderDynamicTable>::RemoveEntryFromEnd(); +} + +QpackDecoderHeaderTable::QpackDecoderHeaderTable() + : static_entries_(ObtainQpackStaticTable().GetStaticEntries()) {} + +QpackDecoderHeaderTable::~QpackDecoderHeaderTable() { + for (auto& entry : observers_) { + entry.second->Cancel(); } +} + +uint64_t QpackDecoderHeaderTable::InsertEntry(absl::string_view name, + absl::string_view value) { + const uint64_t index = + QpackHeaderTableBase<QpackDecoderDynamicTable>::InsertEntry(name, value); // Notify and deregister observers whose threshold is met, if any. while (!observers_.empty()) { @@ -157,62 +193,37 @@ uint64_t QpackHeaderTable::InsertEntry(absl::string_view name, return index; } -uint64_t QpackHeaderTable::MaxInsertSizeWithoutEvictingGivenEntry( - uint64_t index) const { - QUICHE_DCHECK_LE(dropped_entry_count_, index); +const QpackEntry* QpackDecoderHeaderTable::LookupEntry(bool is_static, + uint64_t index) const { + if (is_static) { + if (index >= static_entries_.size()) { + return nullptr; + } - if (index > inserted_entry_count()) { - // All entries are allowed to be evicted. - return dynamic_table_capacity_; + return &static_entries_[index]; } - // Initialize to current available capacity. - uint64_t max_insert_size = dynamic_table_capacity_ - dynamic_table_size_; - - uint64_t entry_index = dropped_entry_count_; - for (const auto& entry : dynamic_entries_) { - if (entry_index >= index) { - break; - } - ++entry_index; - max_insert_size += entry.Size(); + if (index < dropped_entry_count()) { + return nullptr; } - return max_insert_size; -} + index -= dropped_entry_count(); -bool QpackHeaderTable::SetDynamicTableCapacity(uint64_t capacity) { - if (capacity > maximum_dynamic_table_capacity_) { - return false; + if (index >= dynamic_entries().size()) { + return nullptr; } - dynamic_table_capacity_ = capacity; - EvictDownToCapacity(capacity); - - QUICHE_DCHECK_LE(dynamic_table_size_, dynamic_table_capacity_); - - return true; -} - -bool QpackHeaderTable::SetMaximumDynamicTableCapacity( - uint64_t maximum_dynamic_table_capacity) { - if (maximum_dynamic_table_capacity_ == 0) { - maximum_dynamic_table_capacity_ = maximum_dynamic_table_capacity; - max_entries_ = maximum_dynamic_table_capacity / 32; - return true; - } - // If the value is already set, it should not be changed. - return maximum_dynamic_table_capacity == maximum_dynamic_table_capacity_; + return &dynamic_entries()[index]; } -void QpackHeaderTable::RegisterObserver(uint64_t required_insert_count, - Observer* observer) { +void QpackDecoderHeaderTable::RegisterObserver(uint64_t required_insert_count, + Observer* observer) { QUICHE_DCHECK_GT(required_insert_count, 0u); observers_.insert({required_insert_count, observer}); } -void QpackHeaderTable::UnregisterObserver(uint64_t required_insert_count, - Observer* observer) { +void QpackDecoderHeaderTable::UnregisterObserver(uint64_t required_insert_count, + Observer* observer) { auto it = observers_.lower_bound(required_insert_count); while (it != observers_.end() && it->first == required_insert_count) { if (it->second == observer) { @@ -226,62 +237,4 @@ void QpackHeaderTable::UnregisterObserver(uint64_t required_insert_count, QUIC_NOTREACHED(); } -uint64_t QpackHeaderTable::draining_index(float draining_fraction) const { - QUICHE_DCHECK_LE(0.0, draining_fraction); - QUICHE_DCHECK_LE(draining_fraction, 1.0); - - const uint64_t required_space = draining_fraction * dynamic_table_capacity_; - uint64_t space_above_draining_index = - dynamic_table_capacity_ - dynamic_table_size_; - - if (dynamic_entries_.empty() || - space_above_draining_index >= required_space) { - return dropped_entry_count_; - } - - auto it = dynamic_entries_.begin(); - uint64_t entry_index = dropped_entry_count_; - while (space_above_draining_index < required_space) { - space_above_draining_index += it->Size(); - ++it; - ++entry_index; - if (it == dynamic_entries_.end()) { - return inserted_entry_count(); - } - } - - return entry_index; -} - -void QpackHeaderTable::EvictDownToCapacity(uint64_t capacity) { - while (dynamic_table_size_ > capacity) { - QUICHE_DCHECK(!dynamic_entries_.empty()); - - QpackEntry* const entry = &dynamic_entries_.front(); - - const uint64_t entry_size = entry->Size(); - QUICHE_DCHECK_GE(dynamic_table_size_, entry_size); - dynamic_table_size_ -= entry_size; - - const uint64_t index = dropped_entry_count_; - - auto index_it = dynamic_index_.find({entry->name(), entry->value()}); - // Remove |dynamic_index_| entry only if it points to the same - // QpackEntry in |dynamic_entries_|. - if (index_it != dynamic_index_.end() && index_it->second == index) { - dynamic_index_.erase(index_it); - } - - auto name_it = dynamic_name_index_.find(entry->name()); - // Remove |dynamic_name_index_| entry only if it points to the same - // QpackEntry in |dynamic_entries_|. - if (name_it != dynamic_name_index_.end() && name_it->second == index) { - dynamic_name_index_.erase(name_it); - } - - dynamic_entries_.pop_front(); - ++dropped_entry_count_; - } -} - } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h index a5d6c5d74b8..7193d102294 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h @@ -6,75 +6,41 @@ #define QUICHE_QUIC_CORE_QPACK_QPACK_HEADER_TABLE_H_ #include <cstdint> -#include <functional> -#include <queue> -#include <vector> +#include <deque> #include "absl/strings/string_view.h" #include "quic/platform/api/quic_export.h" +#include "common/quiche_circular_deque.h" #include "spdy/core/hpack/hpack_entry.h" #include "spdy/core/hpack/hpack_header_table.h" namespace quic { -namespace test { - -class QpackHeaderTablePeer; - -} // namespace test - using QpackEntry = spdy::HpackEntry; using QpackLookupEntry = spdy::HpackLookupEntry; constexpr size_t kQpackEntrySizeOverhead = spdy::kHpackEntrySizeOverhead; -// This class manages the QPACK static and dynamic tables. For dynamic entries, -// it only has a concept of absolute indices. The caller needs to perform the -// necessary transformations to and from relative indices and post-base indices. -class QUIC_EXPORT_PRIVATE QpackHeaderTable { +// Encoder needs pointer stability for |dynamic_index_| and +// |dynamic_name_index_|. However, it does not need random access. +// TODO(b/182349990): Change to a more memory efficient container. +using QpackEncoderDynamicTable = std::deque<QpackEntry>; + +// Decoder needs random access for LookupEntry(). +// However, it does not need pointer stability. +using QpackDecoderDynamicTable = quiche::QuicheCircularDeque<QpackEntry>; + +// This is a base class for encoder and decoder classes that manage the QPACK +// static and dynamic tables. For dynamic entries, it only has a concept of +// absolute indices. The caller needs to perform the necessary transformations +// to and from relative indices and post-base indices. +template <typename DynamicEntryTable> +class QUIC_EXPORT_PRIVATE QpackHeaderTableBase { public: - using StaticEntryTable = spdy::HpackHeaderTable::StaticEntryTable; - using DynamicEntryTable = spdy::HpackHeaderTable::DynamicEntryTable; - using NameValueToEntryMap = spdy::HpackHeaderTable::NameValueToEntryMap; - using NameToEntryMap = spdy::HpackHeaderTable::NameToEntryMap; + QpackHeaderTableBase(); + QpackHeaderTableBase(const QpackHeaderTableBase&) = delete; + QpackHeaderTableBase& operator=(const QpackHeaderTableBase&) = delete; - // Result of header table lookup. - enum class MatchType { kNameAndValue, kName, kNoMatch }; - - // Observer interface for dynamic table insertion. - class QUIC_EXPORT_PRIVATE Observer { - public: - virtual ~Observer() = default; - - // Called when inserted_entry_count() reaches the threshold the Observer was - // registered with. After this call the Observer automatically gets - // deregistered. - virtual void OnInsertCountReachedThreshold() = 0; - - // Called when QpackHeaderTable is destroyed to let the Observer know that - // it must not call UnregisterObserver(). - virtual void Cancel() = 0; - }; - - QpackHeaderTable(); - QpackHeaderTable(const QpackHeaderTable&) = delete; - QpackHeaderTable& operator=(const QpackHeaderTable&) = delete; - - ~QpackHeaderTable(); - - // Returns the entry at absolute index |index| from the static or dynamic - // table according to |is_static|. |index| is zero based for both the static - // and the dynamic table. The returned pointer is valid until the entry is - // evicted, even if other entries are inserted into the dynamic table. - // Returns nullptr if entry does not exist. - const QpackEntry* LookupEntry(bool is_static, uint64_t index) const; - - // Returns the absolute index of an entry with matching name and value if such - // exists, otherwise one with matching name is such exists. |index| is zero - // based for both the static and the dynamic table. - MatchType FindHeaderField(absl::string_view name, - absl::string_view value, - bool* is_static, - uint64_t* index) const; + virtual ~QpackHeaderTableBase() = default; // Returns whether an entry with |name| and |value| has a size (including // overhead) that is smaller than or equal to the capacity of the dynamic @@ -89,13 +55,7 @@ class QUIC_EXPORT_PRIVATE QpackHeaderTable { // the underlying container might move entries around when resizing for // insertion. // Returns the absolute index of the inserted dynamic table entry. - uint64_t InsertEntry(absl::string_view name, absl::string_view value); - - // Returns the size of the largest entry that could be inserted into the - // dynamic table without evicting entry |index|. |index| might be larger than - // inserted_entry_count(), in which case the capacity of the table is - // returned. |index| must not be smaller than dropped_entry_count(). - uint64_t MaxInsertSizeWithoutEvictingGivenEntry(uint64_t index) const; + virtual uint64_t InsertEntry(absl::string_view name, absl::string_view value); // Change dynamic table capacity to |capacity|. Returns true on success. // Returns false is |capacity| exceeds maximum dynamic table capacity. @@ -112,24 +72,11 @@ class QUIC_EXPORT_PRIVATE QpackHeaderTable { // returning false. bool SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity); - // Get |maximum_dynamic_table_capacity_|. + uint64_t dynamic_table_size() const { return dynamic_table_size_; } + uint64_t dynamic_table_capacity() const { return dynamic_table_capacity_; } uint64_t maximum_dynamic_table_capacity() const { return maximum_dynamic_table_capacity_; } - - // Register an observer to be notified when inserted_entry_count() reaches - // |required_insert_count|. After the notification, |observer| automatically - // gets unregistered. Each observer must only be registered at most once. - void RegisterObserver(uint64_t required_insert_count, Observer* observer); - - // Unregister previously registered observer. Must be called with the same - // |required_insert_count| value that |observer| was registered with. Must be - // called before an observer still waiting for notification is destroyed, - // unless QpackHeaderTable already called Observer::Cancel(), in which case - // this method must not be called. - void UnregisterObserver(uint64_t required_insert_count, Observer* observer); - - // Used on request streams to encode and decode Required Insert Count. uint64_t max_entries() const { return max_entries_; } // The number of entries inserted to the dynamic table (including ones that @@ -141,14 +88,6 @@ class QUIC_EXPORT_PRIVATE QpackHeaderTable { // The number of entries dropped from the dynamic table. uint64_t dropped_entry_count() const { return dropped_entry_count_; } - // Returns the draining index described at - // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#avoiding-blocked-insertions. - // Entries with an index larger than or equal to the draining index take up - // approximately |1.0 - draining_fraction| of dynamic table capacity. The - // remaining capacity is taken up by draining entries and unused space. - // The returned index might not be the index of a valid entry. - uint64_t draining_index(float draining_fraction) const; - void set_dynamic_table_entry_referenced() { dynamic_table_entry_referenced_ = true; } @@ -156,20 +95,178 @@ class QUIC_EXPORT_PRIVATE QpackHeaderTable { return dynamic_table_entry_referenced_; } - private: - friend class test::QpackHeaderTablePeer; + protected: + // Removes a single entry from the end of the dynamic table, updates + // |dynamic_table_size_| and |dropped_entry_count_|. + virtual void RemoveEntryFromEnd(); + const DynamicEntryTable& dynamic_entries() const { return dynamic_entries_; } + + private: // Evict entries from the dynamic table until table size is less than or equal // to |capacity|. void EvictDownToCapacity(uint64_t capacity); - // Static Table + // Dynamic Table entries. + DynamicEntryTable dynamic_entries_; - // |static_entries_|, |static_index_|, |static_name_index_| are owned by - // QpackStaticTable singleton. + // Size of the dynamic table. This is the sum of the size of its entries. + uint64_t dynamic_table_size_; - // Tracks QpackEntries by index. - const StaticEntryTable& static_entries_; + // Dynamic Table Capacity is the maximum allowed value of + // |dynamic_table_size_|. Entries are evicted if necessary before inserting a + // new entry to ensure that dynamic table size never exceeds capacity. + // Initial value is |maximum_dynamic_table_capacity_|. Capacity can be + // changed by the encoder, as long as it does not exceed + // |maximum_dynamic_table_capacity_|. + uint64_t dynamic_table_capacity_; + + // Maximum allowed value of |dynamic_table_capacity|. The initial value is + // zero. Can be changed by SetMaximumDynamicTableCapacity(). + uint64_t maximum_dynamic_table_capacity_; + + // MaxEntries, see Section 3.2.2. Calculated based on + // |maximum_dynamic_table_capacity_|. Used on request streams to encode and + // decode Required Insert Count. + uint64_t max_entries_; + + // The number of entries dropped from the dynamic table. + uint64_t dropped_entry_count_; + + // True if any dynamic table entries have been referenced from a header block. + // Set directly by the encoder or decoder. Used for stats. + bool dynamic_table_entry_referenced_; +}; + +template <typename DynamicEntryTable> +QpackHeaderTableBase<DynamicEntryTable>::QpackHeaderTableBase() + : dynamic_table_size_(0), + dynamic_table_capacity_(0), + maximum_dynamic_table_capacity_(0), + max_entries_(0), + dropped_entry_count_(0), + dynamic_table_entry_referenced_(false) {} + +template <typename DynamicEntryTable> +bool QpackHeaderTableBase<DynamicEntryTable>::EntryFitsDynamicTableCapacity( + absl::string_view name, + absl::string_view value) const { + return QpackEntry::Size(name, value) <= dynamic_table_capacity_; +} + +template <typename DynamicEntryTable> +uint64_t QpackHeaderTableBase<DynamicEntryTable>::InsertEntry( + absl::string_view name, + absl::string_view value) { + QUICHE_DCHECK(EntryFitsDynamicTableCapacity(name, value)); + + const uint64_t index = dropped_entry_count_ + dynamic_entries_.size(); + + // Copy name and value before modifying the container, because evicting + // entries or even inserting a new one might invalidate |name| or |value| if + // they point to an entry. + QpackEntry new_entry((std::string(name)), (std::string(value))); + const size_t entry_size = new_entry.Size(); + + EvictDownToCapacity(dynamic_table_capacity_ - entry_size); + + dynamic_table_size_ += entry_size; + dynamic_entries_.push_back(std::move(new_entry)); + + return index; +} + +template <typename DynamicEntryTable> +bool QpackHeaderTableBase<DynamicEntryTable>::SetDynamicTableCapacity( + uint64_t capacity) { + if (capacity > maximum_dynamic_table_capacity_) { + return false; + } + + dynamic_table_capacity_ = capacity; + EvictDownToCapacity(capacity); + + QUICHE_DCHECK_LE(dynamic_table_size_, dynamic_table_capacity_); + + return true; +} + +template <typename DynamicEntryTable> +bool QpackHeaderTableBase<DynamicEntryTable>::SetMaximumDynamicTableCapacity( + uint64_t maximum_dynamic_table_capacity) { + if (maximum_dynamic_table_capacity_ == 0) { + maximum_dynamic_table_capacity_ = maximum_dynamic_table_capacity; + max_entries_ = maximum_dynamic_table_capacity / 32; + return true; + } + // If the value is already set, it should not be changed. + return maximum_dynamic_table_capacity == maximum_dynamic_table_capacity_; +} + +template <typename DynamicEntryTable> +void QpackHeaderTableBase<DynamicEntryTable>::RemoveEntryFromEnd() { + const uint64_t entry_size = dynamic_entries_.front().Size(); + QUICHE_DCHECK_GE(dynamic_table_size_, entry_size); + dynamic_table_size_ -= entry_size; + + dynamic_entries_.pop_front(); + ++dropped_entry_count_; +} + +template <typename DynamicEntryTable> +void QpackHeaderTableBase<DynamicEntryTable>::EvictDownToCapacity( + uint64_t capacity) { + while (dynamic_table_size_ > capacity) { + QUICHE_DCHECK(!dynamic_entries_.empty()); + RemoveEntryFromEnd(); + } +} + +class QUIC_EXPORT_PRIVATE QpackEncoderHeaderTable + : public QpackHeaderTableBase<QpackEncoderDynamicTable> { + public: + // Result of header table lookup. + enum class MatchType { kNameAndValue, kName, kNoMatch }; + + QpackEncoderHeaderTable(); + ~QpackEncoderHeaderTable() override = default; + + uint64_t InsertEntry(absl::string_view name, + absl::string_view value) override; + + // Returns the absolute index of an entry with matching name and value if such + // exists, otherwise one with matching name is such exists. |index| is zero + // based for both the static and the dynamic table. + MatchType FindHeaderField(absl::string_view name, + absl::string_view value, + bool* is_static, + uint64_t* index) const; + + // Returns the size of the largest entry that could be inserted into the + // dynamic table without evicting entry |index|. |index| might be larger than + // inserted_entry_count(), in which case the capacity of the table is + // returned. |index| must not be smaller than dropped_entry_count(). + uint64_t MaxInsertSizeWithoutEvictingGivenEntry(uint64_t index) const; + + // Returns the draining index described at + // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#avoiding-blocked-insertions. + // Entries with an index larger than or equal to the draining index take up + // approximately |1.0 - draining_fraction| of dynamic table capacity. The + // remaining capacity is taken up by draining entries and unused space. + // The returned index might not be the index of a valid entry. + uint64_t draining_index(float draining_fraction) const; + + protected: + void RemoveEntryFromEnd() override; + + private: + using NameValueToEntryMap = spdy::HpackHeaderTable::NameValueToEntryMap; + using NameToEntryMap = spdy::HpackHeaderTable::NameToEntryMap; + + // Static Table + + // |static_index_| and |static_name_index_| are owned by QpackStaticTable + // singleton. // Tracks the unique static entry for a given header name and value. const NameValueToEntryMap& static_index_; @@ -179,49 +276,69 @@ class QUIC_EXPORT_PRIVATE QpackHeaderTable { // Dynamic Table - // Queue of dynamic table entries, for lookup by index. - // |dynamic_entries_| owns the entries in the dynamic table. - DynamicEntryTable dynamic_entries_; - // An unordered set of QpackEntry pointers with a comparison operator that // only cares about name and value. This allows fast lookup of the most // recently inserted dynamic entry for a given header name and value pair. - // Entries point to entries owned by |dynamic_entries_|. + // Entries point to entries owned by |QpackHeaderTableBase::dynamic_entries_|. NameValueToEntryMap dynamic_index_; // An unordered map of QpackEntry pointers keyed off header name. This allows // fast lookup of the most recently inserted dynamic entry for a given header - // name. Entries point to entries owned by |dynamic_entries_|. + // name. Entries point to entries owned by + // |QpackHeaderTableBase::dynamic_entries_|. NameToEntryMap dynamic_name_index_; +}; - // Size of the dynamic table. This is the sum of the size of its entries. - uint64_t dynamic_table_size_; +class QUIC_EXPORT_PRIVATE QpackDecoderHeaderTable + : public QpackHeaderTableBase<QpackDecoderDynamicTable> { + public: + // Observer interface for dynamic table insertion. + class QUIC_EXPORT_PRIVATE Observer { + public: + virtual ~Observer() = default; - // Dynamic Table Capacity is the maximum allowed value of - // |dynamic_table_size_|. Entries are evicted if necessary before inserting a - // new entry to ensure that dynamic table size never exceeds capacity. - // Initial value is |maximum_dynamic_table_capacity_|. Capacity can be - // changed by the encoder, as long as it does not exceed - // |maximum_dynamic_table_capacity_|. - uint64_t dynamic_table_capacity_; + // Called when inserted_entry_count() reaches the threshold the Observer was + // registered with. After this call the Observer automatically gets + // deregistered. + virtual void OnInsertCountReachedThreshold() = 0; - // Maximum allowed value of |dynamic_table_capacity|. The initial value is - // zero. Can be changed by SetMaximumDynamicTableCapacity(). - uint64_t maximum_dynamic_table_capacity_; + // Called when QpackDecoderHeaderTable is destroyed to let the Observer know + // that it must not call UnregisterObserver(). + virtual void Cancel() = 0; + }; - // MaxEntries, see Section 3.2.2. Calculated based on - // |maximum_dynamic_table_capacity_|. - uint64_t max_entries_; + QpackDecoderHeaderTable(); + ~QpackDecoderHeaderTable() override; - // The number of entries dropped from the dynamic table. - uint64_t dropped_entry_count_; + uint64_t InsertEntry(absl::string_view name, + absl::string_view value) override; + + // Returns the entry at absolute index |index| from the static or dynamic + // table according to |is_static|. |index| is zero based for both the static + // and the dynamic table. The returned pointer is valid until the entry is + // evicted, even if other entries are inserted into the dynamic table. + // Returns nullptr if entry does not exist. + const QpackEntry* LookupEntry(bool is_static, uint64_t index) const; + + // Register an observer to be notified when inserted_entry_count() reaches + // |required_insert_count|. After the notification, |observer| automatically + // gets unregistered. Each observer must only be registered at most once. + void RegisterObserver(uint64_t required_insert_count, Observer* observer); + + // Unregister previously registered observer. Must be called with the same + // |required_insert_count| value that |observer| was registered with. Must be + // called before an observer still waiting for notification is destroyed, + // unless QpackDecoderHeaderTable already called Observer::Cancel(), in which + // case this method must not be called. + void UnregisterObserver(uint64_t required_insert_count, Observer* observer); + + private: + // Static Table entries. Owned by QpackStaticTable singleton. + using StaticEntryTable = spdy::HpackHeaderTable::StaticEntryTable; + const StaticEntryTable& static_entries_; // Observers waiting to be notified, sorted by required insert count. std::multimap<uint64_t, Observer*> observers_; - - // True if any dynamic table entries have been referenced from a header block. - // Set directly by the encoder or decoder. Used for stats. - bool dynamic_table_entry_referenced_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc index 0af250efd32..4a1e39c2325 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc @@ -21,64 +21,16 @@ namespace { const uint64_t kMaximumDynamicTableCapacityForTesting = 1024 * 1024; -class MockObserver : public QpackHeaderTable::Observer { - public: - ~MockObserver() override = default; - - MOCK_METHOD(void, OnInsertCountReachedThreshold, (), (override)); - MOCK_METHOD(void, Cancel, (), (override)); -}; - +template <typename T> class QpackHeaderTableTest : public QuicTest { protected: - QpackHeaderTableTest() { - table_.SetMaximumDynamicTableCapacity( - kMaximumDynamicTableCapacityForTesting); - table_.SetDynamicTableCapacity(kMaximumDynamicTableCapacityForTesting); - } ~QpackHeaderTableTest() override = default; - void ExpectEntryAtIndex(bool is_static, - uint64_t index, - absl::string_view expected_name, - absl::string_view expected_value) const { - const auto* entry = table_.LookupEntry(is_static, index); - ASSERT_TRUE(entry); - EXPECT_EQ(expected_name, entry->name()); - EXPECT_EQ(expected_value, entry->value()); - } - - void ExpectNoEntryAtIndex(bool is_static, uint64_t index) const { - EXPECT_FALSE(table_.LookupEntry(is_static, index)); - } - - void ExpectMatch(absl::string_view name, - absl::string_view value, - QpackHeaderTable::MatchType expected_match_type, - bool expected_is_static, - uint64_t expected_index) const { - // Initialize outparams to a value different from the expected to ensure - // that FindHeaderField() sets them. - bool is_static = !expected_is_static; - uint64_t index = expected_index + 1; - - QpackHeaderTable::MatchType matchtype = - table_.FindHeaderField(name, value, &is_static, &index); - - EXPECT_EQ(expected_match_type, matchtype) << name << ": " << value; - EXPECT_EQ(expected_is_static, is_static) << name << ": " << value; - EXPECT_EQ(expected_index, index) << name << ": " << value; - } - - void ExpectNoMatch(absl::string_view name, absl::string_view value) const { - bool is_static = false; - uint64_t index = 0; - - QpackHeaderTable::MatchType matchtype = - table_.FindHeaderField(name, value, &is_static, &index); - - EXPECT_EQ(QpackHeaderTable::MatchType::kNoMatch, matchtype) - << name << ": " << value; + void SetUp() override { + ASSERT_TRUE(table_.SetMaximumDynamicTableCapacity( + kMaximumDynamicTableCapacityForTesting)); + ASSERT_TRUE( + table_.SetDynamicTableCapacity(kMaximumDynamicTableCapacityForTesting)); } bool EntryFitsDynamicTableCapacity(absl::string_view name, @@ -94,110 +46,129 @@ class QpackHeaderTableTest : public QuicTest { return table_.SetDynamicTableCapacity(capacity); } - void RegisterObserver(uint64_t required_insert_count, - QpackHeaderTable::Observer* observer) { - table_.RegisterObserver(required_insert_count, observer); - } - - void UnregisterObserver(uint64_t required_insert_count, - QpackHeaderTable::Observer* observer) { - table_.UnregisterObserver(required_insert_count, observer); - } - uint64_t max_entries() const { return table_.max_entries(); } uint64_t inserted_entry_count() const { return table_.inserted_entry_count(); } uint64_t dropped_entry_count() const { return table_.dropped_entry_count(); } - private: - QpackHeaderTable table_; + T table_; }; -TEST_F(QpackHeaderTableTest, LookupStaticEntry) { - ExpectEntryAtIndex(/* is_static = */ true, 0, ":authority", ""); +using MyTypes = + ::testing::Types<QpackEncoderHeaderTable, QpackDecoderHeaderTable>; +TYPED_TEST_SUITE(QpackHeaderTableTest, MyTypes); - ExpectEntryAtIndex(/* is_static = */ true, 1, ":path", "/"); - - // 98 is the last entry. - ExpectEntryAtIndex(/* is_static = */ true, 98, "x-frame-options", - "sameorigin"); +// MaxEntries is determined by maximum dynamic table capacity, +// which is set at construction time. +TYPED_TEST(QpackHeaderTableTest, MaxEntries) { + TypeParam table1; + table1.SetMaximumDynamicTableCapacity(1024); + EXPECT_EQ(32u, table1.max_entries()); - ASSERT_EQ(99u, QpackStaticTableVector().size()); - ExpectNoEntryAtIndex(/* is_static = */ true, 99); + TypeParam table2; + table2.SetMaximumDynamicTableCapacity(500); + EXPECT_EQ(15u, table2.max_entries()); } -TEST_F(QpackHeaderTableTest, InsertAndLookupDynamicEntry) { - // Dynamic table is initially entry. - ExpectNoEntryAtIndex(/* is_static = */ false, 0); - ExpectNoEntryAtIndex(/* is_static = */ false, 1); - ExpectNoEntryAtIndex(/* is_static = */ false, 2); - ExpectNoEntryAtIndex(/* is_static = */ false, 3); +TYPED_TEST(QpackHeaderTableTest, SetDynamicTableCapacity) { + // Dynamic table capacity does not affect MaxEntries. + EXPECT_TRUE(this->SetDynamicTableCapacity(1024)); + EXPECT_EQ(32u * 1024, this->max_entries()); - // Insert one entry. - InsertEntry("foo", "bar"); + EXPECT_TRUE(this->SetDynamicTableCapacity(500)); + EXPECT_EQ(32u * 1024, this->max_entries()); - ExpectEntryAtIndex(/* is_static = */ false, 0, "foo", "bar"); + // Dynamic table capacity cannot exceed maximum dynamic table capacity. + EXPECT_FALSE(this->SetDynamicTableCapacity( + 2 * kMaximumDynamicTableCapacityForTesting)); +} - ExpectNoEntryAtIndex(/* is_static = */ false, 1); - ExpectNoEntryAtIndex(/* is_static = */ false, 2); - ExpectNoEntryAtIndex(/* is_static = */ false, 3); +TYPED_TEST(QpackHeaderTableTest, EntryFitsDynamicTableCapacity) { + EXPECT_TRUE(this->SetDynamicTableCapacity(39)); - // Insert a different entry. - InsertEntry("baz", "bing"); + EXPECT_TRUE(this->EntryFitsDynamicTableCapacity("foo", "bar")); + EXPECT_TRUE(this->EntryFitsDynamicTableCapacity("foo", "bar2")); + EXPECT_FALSE(this->EntryFitsDynamicTableCapacity("foo", "bar12")); +} - ExpectEntryAtIndex(/* is_static = */ false, 0, "foo", "bar"); +class QpackEncoderHeaderTableTest + : public QpackHeaderTableTest<QpackEncoderHeaderTable> { + protected: + ~QpackEncoderHeaderTableTest() override = default; - ExpectEntryAtIndex(/* is_static = */ false, 1, "baz", "bing"); + void ExpectMatch(absl::string_view name, + absl::string_view value, + QpackEncoderHeaderTable::MatchType expected_match_type, + bool expected_is_static, + uint64_t expected_index) const { + // Initialize outparams to a value different from the expected to ensure + // that FindHeaderField() sets them. + bool is_static = !expected_is_static; + uint64_t index = expected_index + 1; - ExpectNoEntryAtIndex(/* is_static = */ false, 2); - ExpectNoEntryAtIndex(/* is_static = */ false, 3); + QpackEncoderHeaderTable::MatchType matchtype = + table_.FindHeaderField(name, value, &is_static, &index); - // Insert an entry identical to the most recently inserted one. - InsertEntry("baz", "bing"); + EXPECT_EQ(expected_match_type, matchtype) << name << ": " << value; + EXPECT_EQ(expected_is_static, is_static) << name << ": " << value; + EXPECT_EQ(expected_index, index) << name << ": " << value; + } - ExpectEntryAtIndex(/* is_static = */ false, 0, "foo", "bar"); + void ExpectNoMatch(absl::string_view name, absl::string_view value) const { + bool is_static = false; + uint64_t index = 0; - ExpectEntryAtIndex(/* is_static = */ false, 1, "baz", "bing"); + QpackEncoderHeaderTable::MatchType matchtype = + table_.FindHeaderField(name, value, &is_static, &index); - ExpectEntryAtIndex(/* is_static = */ false, 2, "baz", "bing"); + EXPECT_EQ(QpackEncoderHeaderTable::MatchType::kNoMatch, matchtype) + << name << ": " << value; + } - ExpectNoEntryAtIndex(/* is_static = */ false, 3); -} + uint64_t MaxInsertSizeWithoutEvictingGivenEntry(uint64_t index) const { + return table_.MaxInsertSizeWithoutEvictingGivenEntry(index); + } -TEST_F(QpackHeaderTableTest, FindStaticHeaderField) { + uint64_t draining_index(float draining_fraction) const { + return table_.draining_index(draining_fraction); + } +}; + +TEST_F(QpackEncoderHeaderTableTest, FindStaticHeaderField) { // A header name that has multiple entries with different values. - ExpectMatch(":method", "GET", QpackHeaderTable::MatchType::kNameAndValue, - true, 17u); + ExpectMatch(":method", "GET", + QpackEncoderHeaderTable::MatchType::kNameAndValue, true, 17u); - ExpectMatch(":method", "POST", QpackHeaderTable::MatchType::kNameAndValue, - true, 20u); + ExpectMatch(":method", "POST", + QpackEncoderHeaderTable::MatchType::kNameAndValue, true, 20u); - ExpectMatch(":method", "TRACE", QpackHeaderTable::MatchType::kName, true, - 15u); + ExpectMatch(":method", "TRACE", QpackEncoderHeaderTable::MatchType::kName, + true, 15u); // A header name that has a single entry with non-empty value. ExpectMatch("accept-encoding", "gzip, deflate, br", - QpackHeaderTable::MatchType::kNameAndValue, true, 31u); + QpackEncoderHeaderTable::MatchType::kNameAndValue, true, 31u); - ExpectMatch("accept-encoding", "compress", QpackHeaderTable::MatchType::kName, - true, 31u); + ExpectMatch("accept-encoding", "compress", + QpackEncoderHeaderTable::MatchType::kName, true, 31u); - ExpectMatch("accept-encoding", "", QpackHeaderTable::MatchType::kName, true, - 31u); + ExpectMatch("accept-encoding", "", QpackEncoderHeaderTable::MatchType::kName, + true, 31u); // A header name that has a single entry with empty value. - ExpectMatch("location", "", QpackHeaderTable::MatchType::kNameAndValue, true, - 12u); + ExpectMatch("location", "", QpackEncoderHeaderTable::MatchType::kNameAndValue, + true, 12u); - ExpectMatch("location", "foo", QpackHeaderTable::MatchType::kName, true, 12u); + ExpectMatch("location", "foo", QpackEncoderHeaderTable::MatchType::kName, + true, 12u); // No matching header name. ExpectNoMatch("foo", ""); ExpectNoMatch("foo", "bar"); } -TEST_F(QpackHeaderTableTest, FindDynamicHeaderField) { +TEST_F(QpackEncoderHeaderTableTest, FindDynamicHeaderField) { // Dynamic table is initially entry. ExpectNoMatch("foo", "bar"); ExpectNoMatch("foo", "baz"); @@ -206,75 +177,49 @@ TEST_F(QpackHeaderTableTest, FindDynamicHeaderField) { InsertEntry("foo", "bar"); // Match name and value. - ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue, false, - 0u); + ExpectMatch("foo", "bar", QpackEncoderHeaderTable::MatchType::kNameAndValue, + false, 0u); // Match name only. - ExpectMatch("foo", "baz", QpackHeaderTable::MatchType::kName, false, 0u); + ExpectMatch("foo", "baz", QpackEncoderHeaderTable::MatchType::kName, false, + 0u); // Insert an identical entry. FindHeaderField() should return the index of // the most recently inserted matching entry. InsertEntry("foo", "bar"); // Match name and value. - ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue, false, - 1u); + ExpectMatch("foo", "bar", QpackEncoderHeaderTable::MatchType::kNameAndValue, + false, 1u); // Match name only. - ExpectMatch("foo", "baz", QpackHeaderTable::MatchType::kName, false, 1u); + ExpectMatch("foo", "baz", QpackEncoderHeaderTable::MatchType::kName, false, + 1u); } -TEST_F(QpackHeaderTableTest, FindHeaderFieldPrefersStaticTable) { +TEST_F(QpackEncoderHeaderTableTest, FindHeaderFieldPrefersStaticTable) { // Insert an entry to the dynamic table that exists in the static table. InsertEntry(":method", "GET"); - // Insertion works. - ExpectEntryAtIndex(/* is_static = */ false, 0, ":method", "GET"); - // FindHeaderField() prefers static table if both have name-and-value match. - ExpectMatch(":method", "GET", QpackHeaderTable::MatchType::kNameAndValue, - true, 17u); + ExpectMatch(":method", "GET", + QpackEncoderHeaderTable::MatchType::kNameAndValue, true, 17u); // FindHeaderField() prefers static table if both have name match but no value // match, and prefers the first entry with matching name. - ExpectMatch(":method", "TRACE", QpackHeaderTable::MatchType::kName, true, - 15u); + ExpectMatch(":method", "TRACE", QpackEncoderHeaderTable::MatchType::kName, + true, 15u); // Add new entry to the dynamic table. InsertEntry(":method", "TRACE"); // FindHeaderField prefers name-and-value match in dynamic table over name // only match in static table. - ExpectMatch(":method", "TRACE", QpackHeaderTable::MatchType::kNameAndValue, - false, 1u); + ExpectMatch(":method", "TRACE", + QpackEncoderHeaderTable::MatchType::kNameAndValue, false, 1u); } -// MaxEntries is determined by maximum dynamic table capacity, -// which is set at construction time. -TEST_F(QpackHeaderTableTest, MaxEntries) { - QpackHeaderTable table1; - table1.SetMaximumDynamicTableCapacity(1024); - EXPECT_EQ(32u, table1.max_entries()); - - QpackHeaderTable table2; - table2.SetMaximumDynamicTableCapacity(500); - EXPECT_EQ(15u, table2.max_entries()); -} - -TEST_F(QpackHeaderTableTest, SetDynamicTableCapacity) { - // Dynamic table capacity does not affect MaxEntries. - EXPECT_TRUE(SetDynamicTableCapacity(1024)); - EXPECT_EQ(32u * 1024, max_entries()); - - EXPECT_TRUE(SetDynamicTableCapacity(500)); - EXPECT_EQ(32u * 1024, max_entries()); - - // Dynamic table capacity cannot exceed maximum dynamic table capacity. - EXPECT_FALSE( - SetDynamicTableCapacity(2 * kMaximumDynamicTableCapacityForTesting)); -} - -TEST_F(QpackHeaderTableTest, EvictByInsertion) { +TEST_F(QpackEncoderHeaderTableTest, EvictByInsertion) { EXPECT_TRUE(SetDynamicTableCapacity(40)); // Entry size is 3 + 3 + 32 = 38. @@ -282,7 +227,7 @@ TEST_F(QpackHeaderTableTest, EvictByInsertion) { EXPECT_EQ(1u, inserted_entry_count()); EXPECT_EQ(0u, dropped_entry_count()); - ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue, + ExpectMatch("foo", "bar", QpackEncoderHeaderTable::MatchType::kNameAndValue, /* expected_is_static = */ false, 0u); // Inserting second entry evicts the first one. @@ -291,20 +236,20 @@ TEST_F(QpackHeaderTableTest, EvictByInsertion) { EXPECT_EQ(1u, dropped_entry_count()); ExpectNoMatch("foo", "bar"); - ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue, + ExpectMatch("baz", "qux", QpackEncoderHeaderTable::MatchType::kNameAndValue, /* expected_is_static = */ false, 1u); } -TEST_F(QpackHeaderTableTest, EvictByUpdateTableSize) { +TEST_F(QpackEncoderHeaderTableTest, EvictByUpdateTableSize) { // Entry size is 3 + 3 + 32 = 38. InsertEntry("foo", "bar"); InsertEntry("baz", "qux"); EXPECT_EQ(2u, inserted_entry_count()); EXPECT_EQ(0u, dropped_entry_count()); - ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue, + ExpectMatch("foo", "bar", QpackEncoderHeaderTable::MatchType::kNameAndValue, /* expected_is_static = */ false, 0u); - ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue, + ExpectMatch("baz", "qux", QpackEncoderHeaderTable::MatchType::kNameAndValue, /* expected_is_static = */ false, 1u); EXPECT_TRUE(SetDynamicTableCapacity(40)); @@ -312,7 +257,7 @@ TEST_F(QpackHeaderTableTest, EvictByUpdateTableSize) { EXPECT_EQ(1u, dropped_entry_count()); ExpectNoMatch("foo", "bar"); - ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue, + ExpectMatch("baz", "qux", QpackEncoderHeaderTable::MatchType::kNameAndValue, /* expected_is_static = */ false, 1u); EXPECT_TRUE(SetDynamicTableCapacity(20)); @@ -323,7 +268,7 @@ TEST_F(QpackHeaderTableTest, EvictByUpdateTableSize) { ExpectNoMatch("baz", "qux"); } -TEST_F(QpackHeaderTableTest, EvictOldestOfIdentical) { +TEST_F(QpackEncoderHeaderTableTest, EvictOldestOfIdentical) { EXPECT_TRUE(SetDynamicTableCapacity(80)); // Entry size is 3 + 3 + 32 = 38. @@ -334,7 +279,7 @@ TEST_F(QpackHeaderTableTest, EvictOldestOfIdentical) { EXPECT_EQ(0u, dropped_entry_count()); // Find most recently inserted entry. - ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue, + ExpectMatch("foo", "bar", QpackEncoderHeaderTable::MatchType::kNameAndValue, /* expected_is_static = */ false, 1u); // Inserting third entry evicts the first one, not the second. @@ -342,13 +287,13 @@ TEST_F(QpackHeaderTableTest, EvictOldestOfIdentical) { EXPECT_EQ(3u, inserted_entry_count()); EXPECT_EQ(1u, dropped_entry_count()); - ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue, + ExpectMatch("foo", "bar", QpackEncoderHeaderTable::MatchType::kNameAndValue, /* expected_is_static = */ false, 1u); - ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue, + ExpectMatch("baz", "qux", QpackEncoderHeaderTable::MatchType::kNameAndValue, /* expected_is_static = */ false, 2u); } -TEST_F(QpackHeaderTableTest, EvictOldestOfSameName) { +TEST_F(QpackEncoderHeaderTableTest, EvictOldestOfSameName) { EXPECT_TRUE(SetDynamicTableCapacity(80)); // Entry size is 3 + 3 + 32 = 38. @@ -359,7 +304,7 @@ TEST_F(QpackHeaderTableTest, EvictOldestOfSameName) { EXPECT_EQ(0u, dropped_entry_count()); // Find most recently inserted entry with matching name. - ExpectMatch("foo", "foo", QpackHeaderTable::MatchType::kName, + ExpectMatch("foo", "foo", QpackEncoderHeaderTable::MatchType::kName, /* expected_is_static = */ false, 1u); // Inserting third entry evicts the first one, not the second. @@ -367,63 +312,276 @@ TEST_F(QpackHeaderTableTest, EvictOldestOfSameName) { EXPECT_EQ(3u, inserted_entry_count()); EXPECT_EQ(1u, dropped_entry_count()); - ExpectMatch("foo", "foo", QpackHeaderTable::MatchType::kName, + ExpectMatch("foo", "foo", QpackEncoderHeaderTable::MatchType::kName, /* expected_is_static = */ false, 1u); - ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue, + ExpectMatch("baz", "qux", QpackEncoderHeaderTable::MatchType::kNameAndValue, /* expected_is_static = */ false, 2u); } // Returns the size of the largest entry that could be inserted into the // dynamic table without evicting entry |index|. -TEST_F(QpackHeaderTableTest, MaxInsertSizeWithoutEvictingGivenEntry) { +TEST_F(QpackEncoderHeaderTableTest, MaxInsertSizeWithoutEvictingGivenEntry) { const uint64_t dynamic_table_capacity = 100; - QpackHeaderTable table; - table.SetMaximumDynamicTableCapacity(dynamic_table_capacity); - EXPECT_TRUE(table.SetDynamicTableCapacity(dynamic_table_capacity)); + EXPECT_TRUE(SetDynamicTableCapacity(dynamic_table_capacity)); // Empty table can take an entry up to its capacity. - EXPECT_EQ(dynamic_table_capacity, - table.MaxInsertSizeWithoutEvictingGivenEntry(0)); + EXPECT_EQ(dynamic_table_capacity, MaxInsertSizeWithoutEvictingGivenEntry(0)); const uint64_t entry_size1 = QpackEntry::Size("foo", "bar"); - table.InsertEntry("foo", "bar"); + InsertEntry("foo", "bar"); EXPECT_EQ(dynamic_table_capacity - entry_size1, - table.MaxInsertSizeWithoutEvictingGivenEntry(0)); + MaxInsertSizeWithoutEvictingGivenEntry(0)); // Table can take an entry up to its capacity if all entries are allowed to be // evicted. - EXPECT_EQ(dynamic_table_capacity, - table.MaxInsertSizeWithoutEvictingGivenEntry(1)); + EXPECT_EQ(dynamic_table_capacity, MaxInsertSizeWithoutEvictingGivenEntry(1)); const uint64_t entry_size2 = QpackEntry::Size("baz", "foobar"); - table.InsertEntry("baz", "foobar"); + InsertEntry("baz", "foobar"); // Table can take an entry up to its capacity if all entries are allowed to be // evicted. - EXPECT_EQ(dynamic_table_capacity, - table.MaxInsertSizeWithoutEvictingGivenEntry(2)); + EXPECT_EQ(dynamic_table_capacity, MaxInsertSizeWithoutEvictingGivenEntry(2)); // Second entry must stay. EXPECT_EQ(dynamic_table_capacity - entry_size2, - table.MaxInsertSizeWithoutEvictingGivenEntry(1)); + MaxInsertSizeWithoutEvictingGivenEntry(1)); // First and second entry must stay. EXPECT_EQ(dynamic_table_capacity - entry_size2 - entry_size1, - table.MaxInsertSizeWithoutEvictingGivenEntry(0)); + MaxInsertSizeWithoutEvictingGivenEntry(0)); // Third entry evicts first one. const uint64_t entry_size3 = QpackEntry::Size("last", "entry"); - table.InsertEntry("last", "entry"); - EXPECT_EQ(1u, table.dropped_entry_count()); + InsertEntry("last", "entry"); + EXPECT_EQ(1u, dropped_entry_count()); // Table can take an entry up to its capacity if all entries are allowed to be // evicted. - EXPECT_EQ(dynamic_table_capacity, - table.MaxInsertSizeWithoutEvictingGivenEntry(3)); + EXPECT_EQ(dynamic_table_capacity, MaxInsertSizeWithoutEvictingGivenEntry(3)); // Third entry must stay. EXPECT_EQ(dynamic_table_capacity - entry_size3, - table.MaxInsertSizeWithoutEvictingGivenEntry(2)); + MaxInsertSizeWithoutEvictingGivenEntry(2)); // Second and third entry must stay. EXPECT_EQ(dynamic_table_capacity - entry_size3 - entry_size2, - table.MaxInsertSizeWithoutEvictingGivenEntry(1)); + MaxInsertSizeWithoutEvictingGivenEntry(1)); +} + +TEST_F(QpackEncoderHeaderTableTest, DrainingIndex) { + EXPECT_TRUE(SetDynamicTableCapacity(4 * QpackEntry::Size("foo", "bar"))); + + // Empty table: no draining entry. + EXPECT_EQ(0u, draining_index(0.0)); + EXPECT_EQ(0u, draining_index(1.0)); + + // Table with one entry. + InsertEntry("foo", "bar"); + // Any entry can be referenced if none of the table is draining. + EXPECT_EQ(0u, draining_index(0.0)); + // No entry can be referenced if all of the table is draining. + EXPECT_EQ(1u, draining_index(1.0)); + + // Table with two entries is at half capacity. + InsertEntry("foo", "bar"); + // Any entry can be referenced if at most half of the table is draining, + // because current entries only take up half of total capacity. + EXPECT_EQ(0u, draining_index(0.0)); + EXPECT_EQ(0u, draining_index(0.5)); + // No entry can be referenced if all of the table is draining. + EXPECT_EQ(2u, draining_index(1.0)); + + // Table with four entries is full. + InsertEntry("foo", "bar"); + InsertEntry("foo", "bar"); + // Any entry can be referenced if none of the table is draining. + EXPECT_EQ(0u, draining_index(0.0)); + // In a full table with identically sized entries, |draining_fraction| of all + // entries are draining. + EXPECT_EQ(2u, draining_index(0.5)); + // No entry can be referenced if all of the table is draining. + EXPECT_EQ(4u, draining_index(1.0)); } -TEST_F(QpackHeaderTableTest, RegisterObserver) { +class MockObserver : public QpackDecoderHeaderTable::Observer { + public: + ~MockObserver() override = default; + + MOCK_METHOD(void, OnInsertCountReachedThreshold, (), (override)); + MOCK_METHOD(void, Cancel, (), (override)); +}; + +class QpackDecoderHeaderTableTest + : public QpackHeaderTableTest<QpackDecoderHeaderTable> { + protected: + ~QpackDecoderHeaderTableTest() override = default; + + void ExpectEntryAtIndex(bool is_static, + uint64_t index, + absl::string_view expected_name, + absl::string_view expected_value) const { + const auto* entry = table_.LookupEntry(is_static, index); + ASSERT_TRUE(entry); + EXPECT_EQ(expected_name, entry->name()); + EXPECT_EQ(expected_value, entry->value()); + } + + void ExpectNoEntryAtIndex(bool is_static, uint64_t index) const { + EXPECT_FALSE(table_.LookupEntry(is_static, index)); + } + + void RegisterObserver(uint64_t required_insert_count, + QpackDecoderHeaderTable::Observer* observer) { + table_.RegisterObserver(required_insert_count, observer); + } + + void UnregisterObserver(uint64_t required_insert_count, + QpackDecoderHeaderTable::Observer* observer) { + table_.UnregisterObserver(required_insert_count, observer); + } +}; + +TEST_F(QpackDecoderHeaderTableTest, LookupStaticEntry) { + ExpectEntryAtIndex(/* is_static = */ true, 0, ":authority", ""); + + ExpectEntryAtIndex(/* is_static = */ true, 1, ":path", "/"); + + // 98 is the last entry. + ExpectEntryAtIndex(/* is_static = */ true, 98, "x-frame-options", + "sameorigin"); + + ASSERT_EQ(99u, QpackStaticTableVector().size()); + ExpectNoEntryAtIndex(/* is_static = */ true, 99); +} + +TEST_F(QpackDecoderHeaderTableTest, InsertAndLookupDynamicEntry) { + // Dynamic table is initially entry. + ExpectNoEntryAtIndex(/* is_static = */ false, 0); + ExpectNoEntryAtIndex(/* is_static = */ false, 1); + ExpectNoEntryAtIndex(/* is_static = */ false, 2); + ExpectNoEntryAtIndex(/* is_static = */ false, 3); + + // Insert one entry. + InsertEntry("foo", "bar"); + + ExpectEntryAtIndex(/* is_static = */ false, 0, "foo", "bar"); + + ExpectNoEntryAtIndex(/* is_static = */ false, 1); + ExpectNoEntryAtIndex(/* is_static = */ false, 2); + ExpectNoEntryAtIndex(/* is_static = */ false, 3); + + // Insert a different entry. + InsertEntry("baz", "bing"); + + ExpectEntryAtIndex(/* is_static = */ false, 0, "foo", "bar"); + + ExpectEntryAtIndex(/* is_static = */ false, 1, "baz", "bing"); + + ExpectNoEntryAtIndex(/* is_static = */ false, 2); + ExpectNoEntryAtIndex(/* is_static = */ false, 3); + + // Insert an entry identical to the most recently inserted one. + InsertEntry("baz", "bing"); + + ExpectEntryAtIndex(/* is_static = */ false, 0, "foo", "bar"); + + ExpectEntryAtIndex(/* is_static = */ false, 1, "baz", "bing"); + + ExpectEntryAtIndex(/* is_static = */ false, 2, "baz", "bing"); + + ExpectNoEntryAtIndex(/* is_static = */ false, 3); +} + +TEST_F(QpackDecoderHeaderTableTest, EvictByInsertion) { + EXPECT_TRUE(SetDynamicTableCapacity(40)); + + // Entry size is 3 + 3 + 32 = 38. + InsertEntry("foo", "bar"); + EXPECT_EQ(1u, inserted_entry_count()); + EXPECT_EQ(0u, dropped_entry_count()); + + ExpectEntryAtIndex(/* is_static = */ false, 0u, "foo", "bar"); + + // Inserting second entry evicts the first one. + InsertEntry("baz", "qux"); + EXPECT_EQ(2u, inserted_entry_count()); + EXPECT_EQ(1u, dropped_entry_count()); + + ExpectNoEntryAtIndex(/* is_static = */ false, 0u); + ExpectEntryAtIndex(/* is_static = */ false, 1u, "baz", "qux"); +} + +TEST_F(QpackDecoderHeaderTableTest, EvictByUpdateTableSize) { + ExpectNoEntryAtIndex(/* is_static = */ false, 0u); + ExpectNoEntryAtIndex(/* is_static = */ false, 1u); + + // Entry size is 3 + 3 + 32 = 38. + InsertEntry("foo", "bar"); + InsertEntry("baz", "qux"); + EXPECT_EQ(2u, inserted_entry_count()); + EXPECT_EQ(0u, dropped_entry_count()); + + ExpectEntryAtIndex(/* is_static = */ false, 0u, "foo", "bar"); + ExpectEntryAtIndex(/* is_static = */ false, 1u, "baz", "qux"); + + EXPECT_TRUE(SetDynamicTableCapacity(40)); + EXPECT_EQ(2u, inserted_entry_count()); + EXPECT_EQ(1u, dropped_entry_count()); + + ExpectNoEntryAtIndex(/* is_static = */ false, 0u); + ExpectEntryAtIndex(/* is_static = */ false, 1u, "baz", "qux"); + + EXPECT_TRUE(SetDynamicTableCapacity(20)); + EXPECT_EQ(2u, inserted_entry_count()); + EXPECT_EQ(2u, dropped_entry_count()); + + ExpectNoEntryAtIndex(/* is_static = */ false, 0u); + ExpectNoEntryAtIndex(/* is_static = */ false, 1u); +} + +TEST_F(QpackDecoderHeaderTableTest, EvictOldestOfIdentical) { + EXPECT_TRUE(SetDynamicTableCapacity(80)); + + // Entry size is 3 + 3 + 32 = 38. + // Insert same entry twice. + InsertEntry("foo", "bar"); + InsertEntry("foo", "bar"); + EXPECT_EQ(2u, inserted_entry_count()); + EXPECT_EQ(0u, dropped_entry_count()); + + ExpectEntryAtIndex(/* is_static = */ false, 0u, "foo", "bar"); + ExpectEntryAtIndex(/* is_static = */ false, 1u, "foo", "bar"); + ExpectNoEntryAtIndex(/* is_static = */ false, 2u); + + // Inserting third entry evicts the first one, not the second. + InsertEntry("baz", "qux"); + EXPECT_EQ(3u, inserted_entry_count()); + EXPECT_EQ(1u, dropped_entry_count()); + + ExpectNoEntryAtIndex(/* is_static = */ false, 0u); + ExpectEntryAtIndex(/* is_static = */ false, 1u, "foo", "bar"); + ExpectEntryAtIndex(/* is_static = */ false, 2u, "baz", "qux"); +} + +TEST_F(QpackDecoderHeaderTableTest, EvictOldestOfSameName) { + EXPECT_TRUE(SetDynamicTableCapacity(80)); + + // Entry size is 3 + 3 + 32 = 38. + // Insert two entries with same name but different values. + InsertEntry("foo", "bar"); + InsertEntry("foo", "baz"); + EXPECT_EQ(2u, inserted_entry_count()); + EXPECT_EQ(0u, dropped_entry_count()); + + ExpectEntryAtIndex(/* is_static = */ false, 0u, "foo", "bar"); + ExpectEntryAtIndex(/* is_static = */ false, 1u, "foo", "baz"); + ExpectNoEntryAtIndex(/* is_static = */ false, 2u); + + // Inserting third entry evicts the first one, not the second. + InsertEntry("baz", "qux"); + EXPECT_EQ(3u, inserted_entry_count()); + EXPECT_EQ(1u, dropped_entry_count()); + + ExpectNoEntryAtIndex(/* is_static = */ false, 0u); + ExpectEntryAtIndex(/* is_static = */ false, 1u, "foo", "baz"); + ExpectEntryAtIndex(/* is_static = */ false, 2u, "baz", "qux"); +} + +TEST_F(QpackDecoderHeaderTableTest, RegisterObserver) { StrictMock<MockObserver> observer1; RegisterObserver(1, &observer1); EXPECT_CALL(observer1, OnInsertCountReachedThreshold); @@ -462,7 +620,7 @@ TEST_F(QpackHeaderTableTest, RegisterObserver) { Mock::VerifyAndClearExpectations(&observer5); } -TEST_F(QpackHeaderTableTest, UnregisterObserver) { +TEST_F(QpackDecoderHeaderTableTest, UnregisterObserver) { StrictMock<MockObserver> observer1; StrictMock<MockObserver> observer2; StrictMock<MockObserver> observer3; @@ -483,61 +641,15 @@ TEST_F(QpackHeaderTableTest, UnregisterObserver) { EXPECT_EQ(3u, inserted_entry_count()); } -TEST_F(QpackHeaderTableTest, DrainingIndex) { - QpackHeaderTable table; - table.SetMaximumDynamicTableCapacity(kMaximumDynamicTableCapacityForTesting); - EXPECT_TRUE( - table.SetDynamicTableCapacity(4 * QpackEntry::Size("foo", "bar"))); - - // Empty table: no draining entry. - EXPECT_EQ(0u, table.draining_index(0.0)); - EXPECT_EQ(0u, table.draining_index(1.0)); - - // Table with one entry. - table.InsertEntry("foo", "bar"); - // Any entry can be referenced if none of the table is draining. - EXPECT_EQ(0u, table.draining_index(0.0)); - // No entry can be referenced if all of the table is draining. - EXPECT_EQ(1u, table.draining_index(1.0)); - - // Table with two entries is at half capacity. - table.InsertEntry("foo", "bar"); - // Any entry can be referenced if at most half of the table is draining, - // because current entries only take up half of total capacity. - EXPECT_EQ(0u, table.draining_index(0.0)); - EXPECT_EQ(0u, table.draining_index(0.5)); - // No entry can be referenced if all of the table is draining. - EXPECT_EQ(2u, table.draining_index(1.0)); - - // Table with four entries is full. - table.InsertEntry("foo", "bar"); - table.InsertEntry("foo", "bar"); - // Any entry can be referenced if none of the table is draining. - EXPECT_EQ(0u, table.draining_index(0.0)); - // In a full table with identically sized entries, |draining_fraction| of all - // entries are draining. - EXPECT_EQ(2u, table.draining_index(0.5)); - // No entry can be referenced if all of the table is draining. - EXPECT_EQ(4u, table.draining_index(1.0)); -} - -TEST_F(QpackHeaderTableTest, Cancel) { +TEST_F(QpackDecoderHeaderTableTest, Cancel) { StrictMock<MockObserver> observer; - auto table = std::make_unique<QpackHeaderTable>(); + auto table = std::make_unique<QpackDecoderHeaderTable>(); table->RegisterObserver(1, &observer); EXPECT_CALL(observer, Cancel); table.reset(); } -TEST_F(QpackHeaderTableTest, EntryFitsDynamicTableCapacity) { - EXPECT_TRUE(SetDynamicTableCapacity(39)); - - EXPECT_TRUE(EntryFitsDynamicTableCapacity("foo", "bar")); - EXPECT_TRUE(EntryFitsDynamicTableCapacity("foo", "bar2")); - EXPECT_FALSE(EntryFitsDynamicTableCapacity("foo", "bar12")); -} - } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder_test.cc index 3f68d749f36..b10c1b42d86 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder_test.cc @@ -12,7 +12,6 @@ #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/qpack/qpack_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" using ::testing::_; using ::testing::Eq; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder_test.cc index 36b89bd01ec..c84634b1321 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder_test.cc @@ -8,7 +8,6 @@ #include "absl/strings/string_view.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_test.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { namespace test { diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc index ac07fedff61..3e7d0ad5127 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc @@ -20,7 +20,7 @@ QpackProgressiveDecoder::QpackProgressiveDecoder( QuicStreamId stream_id, BlockedStreamLimitEnforcer* enforcer, DecodingCompletedVisitor* visitor, - QpackHeaderTable* header_table, + QpackDecoderHeaderTable* header_table, HeadersHandlerInterface* handler) : stream_id_(stream_id), prefix_decoder_( diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h index e806ea0fd66..ced6bf0c2dd 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h @@ -18,12 +18,12 @@ namespace quic { -class QpackHeaderTable; +class QpackDecoderHeaderTable; // Class to decode a single header block. class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder : public QpackInstructionDecoder::Delegate, - public QpackHeaderTable::Observer { + public QpackDecoderHeaderTable::Observer { public: // Interface for receiving decoded header block from the decoder. class QUIC_EXPORT_PRIVATE HeadersHandlerInterface { @@ -82,7 +82,7 @@ class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder QpackProgressiveDecoder(QuicStreamId stream_id, BlockedStreamLimitEnforcer* enforcer, DecodingCompletedVisitor* visitor, - QpackHeaderTable* header_table, + QpackDecoderHeaderTable* header_table, HeadersHandlerInterface* handler); QpackProgressiveDecoder(const QpackProgressiveDecoder&) = delete; QpackProgressiveDecoder& operator=(const QpackProgressiveDecoder&) = delete; @@ -103,7 +103,7 @@ class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder void OnInstructionDecodingError(QpackInstructionDecoder::ErrorCode error_code, absl::string_view error_message) override; - // QpackHeaderTable::Observer implementation. + // QpackDecoderHeaderTable::Observer implementation. void OnInsertCountReachedThreshold() override; void Cancel() override; @@ -134,7 +134,7 @@ class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder BlockedStreamLimitEnforcer* const enforcer_; DecodingCompletedVisitor* const visitor_; - QpackHeaderTable* const header_table_; + QpackDecoderHeaderTable* const header_table_; HeadersHandlerInterface* const handler_; // Required Insert Count and Base are decoded from the Header Data Prefix. @@ -163,7 +163,7 @@ class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder // True if a decoding error has been detected. bool error_detected_; - // True if QpackHeaderTable has been destroyed + // True if QpackDecoderHeaderTable has been destroyed // while decoding is still blocked. bool cancelled_; }; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_chaos_protector.cc b/chromium/net/third_party/quiche/src/quic/core/quic_chaos_protector.cc new file mode 100644 index 00000000000..4eb353b183d --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/quic_chaos_protector.cc @@ -0,0 +1,229 @@ +// Copyright (c) 2021 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 "quic/core/quic_chaos_protector.h" + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <limits> +#include <memory> + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "quic/core/crypto/quic_random.h" +#include "quic/core/frames/quic_crypto_frame.h" +#include "quic/core/frames/quic_frame.h" +#include "quic/core/frames/quic_padding_frame.h" +#include "quic/core/frames/quic_ping_frame.h" +#include "quic/core/quic_data_reader.h" +#include "quic/core/quic_data_writer.h" +#include "quic/core/quic_framer.h" +#include "quic/core/quic_packets.h" +#include "quic/core/quic_stream_frame_data_producer.h" +#include "quic/platform/api/quic_bug_tracker.h" +#include "common/platform/api/quiche_logging.h" + +namespace quic { + +QuicChaosProtector::QuicChaosProtector(const QuicCryptoFrame& crypto_frame, + int num_padding_bytes, + size_t packet_size, + QuicFramer* framer, + QuicRandom* random) + : packet_size_(packet_size), + crypto_data_length_(crypto_frame.data_length), + crypto_buffer_offset_(crypto_frame.offset), + level_(crypto_frame.level), + remaining_padding_bytes_(num_padding_bytes), + framer_(framer), + random_(random) { + QUICHE_DCHECK_NE(framer_, nullptr); + QUICHE_DCHECK_NE(framer_->data_producer(), nullptr); + QUICHE_DCHECK_NE(random_, nullptr); +} + +QuicChaosProtector::~QuicChaosProtector() { + DeleteFrames(&frames_); +} + +absl::optional<size_t> QuicChaosProtector::BuildDataPacket( + const QuicPacketHeader& header, + char* buffer) { + if (!CopyCryptoDataToLocalBuffer()) { + return absl::nullopt; + } + SplitCryptoFrame(); + AddPingFrames(); + SpreadPadding(); + ReorderFrames(); + return BuildPacket(header, buffer); +} + +WriteStreamDataResult QuicChaosProtector::WriteStreamData( + QuicStreamId id, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* /*writer*/) { + QUIC_BUG(chaos stream) << "This should never be called; id " << id + << " offset " << offset << " data_length " + << data_length; + return STREAM_MISSING; +} + +bool QuicChaosProtector::WriteCryptoData(EncryptionLevel level, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer) { + if (level != level_) { + QUIC_BUG(chaos bad level) << "Unexpected " << level << " != " << level_; + return false; + } + // This is `offset + data_length > buffer_offset_ + buffer_length_` + // but with integer overflow protection. + if (offset < crypto_buffer_offset_ || data_length > crypto_data_length_ || + offset - crypto_buffer_offset_ > crypto_data_length_ - data_length) { + QUIC_BUG(chaos bad lengths) + << "Unexpected buffer_offset_ " << crypto_buffer_offset_ << " offset " + << offset << " buffer_length_ " << crypto_data_length_ + << " data_length " << data_length; + return false; + } + writer->WriteBytes(&crypto_data_buffer_[offset - crypto_buffer_offset_], + data_length); + return true; +} + +bool QuicChaosProtector::CopyCryptoDataToLocalBuffer() { + crypto_frame_buffer_ = std::make_unique<char[]>(packet_size_); + frames_.push_back(QuicFrame( + new QuicCryptoFrame(level_, crypto_buffer_offset_, crypto_data_length_))); + // We use |framer_| to serialize the CRYPTO frame in order to extract its + // data from the crypto data producer. This ensures that we reuse the + // usual serialization code path, but has the downside that we then need to + // parse the offset and length in order to skip over those fields. + QuicDataWriter writer(packet_size_, crypto_frame_buffer_.get()); + if (!framer_->AppendCryptoFrame(*frames_.front().crypto_frame, &writer)) { + QUIC_BUG(chaos write crypto data); + return false; + } + QuicDataReader reader(crypto_frame_buffer_.get(), writer.length()); + uint64_t parsed_offset, parsed_length; + if (!reader.ReadVarInt62(&parsed_offset) || + !reader.ReadVarInt62(&parsed_length)) { + QUIC_BUG(chaos parse crypto frame); + return false; + } + + absl::string_view crypto_data = reader.ReadRemainingPayload(); + crypto_data_buffer_ = crypto_data.data(); + + QUICHE_DCHECK_EQ(parsed_offset, crypto_buffer_offset_); + QUICHE_DCHECK_EQ(parsed_length, crypto_data_length_); + QUICHE_DCHECK_EQ(parsed_length, crypto_data.length()); + + return true; +} + +void QuicChaosProtector::SplitCryptoFrame() { + const int max_overhead_of_adding_a_crypto_frame = + static_cast<int>(QuicFramer::GetMinCryptoFrameSize( + crypto_buffer_offset_ + crypto_data_length_, crypto_data_length_)); + // Pick a random number of CRYPTO frames to add. + constexpr uint64_t kMaxAddedCryptoFrames = 10; + const uint64_t num_added_crypto_frames = + random_->InsecureRandUint64() % (kMaxAddedCryptoFrames + 1); + for (uint64_t i = 0; i < num_added_crypto_frames; i++) { + if (remaining_padding_bytes_ < max_overhead_of_adding_a_crypto_frame) { + break; + } + // Pick a random frame and split it by shrinking the picked frame and + // moving the second half of its data to a new frame that is then appended + // to |frames|. + size_t frame_to_split_index = + random_->InsecureRandUint64() % frames_.size(); + QuicCryptoFrame* frame_to_split = + frames_[frame_to_split_index].crypto_frame; + if (frame_to_split->data_length <= 1) { + continue; + } + const int frame_to_split_old_overhead = + static_cast<int>(QuicFramer::GetMinCryptoFrameSize( + frame_to_split->offset, frame_to_split->data_length)); + const QuicPacketLength frame_to_split_new_data_length = + 1 + (random_->InsecureRandUint64() % (frame_to_split->data_length - 1)); + const QuicPacketLength new_frame_data_length = + frame_to_split->data_length - frame_to_split_new_data_length; + const QuicStreamOffset new_frame_offset = + frame_to_split->offset + frame_to_split_new_data_length; + frame_to_split->data_length -= new_frame_data_length; + frames_.push_back(QuicFrame( + new QuicCryptoFrame(level_, new_frame_offset, new_frame_data_length))); + const int frame_to_split_new_overhead = + static_cast<int>(QuicFramer::GetMinCryptoFrameSize( + frame_to_split->offset, frame_to_split->data_length)); + const int new_frame_overhead = + static_cast<int>(QuicFramer::GetMinCryptoFrameSize( + new_frame_offset, new_frame_data_length)); + QUICHE_DCHECK_LE(frame_to_split_new_overhead, frame_to_split_old_overhead); + // Readjust padding based on increased overhead. + remaining_padding_bytes_ -= new_frame_overhead; + remaining_padding_bytes_ -= frame_to_split_new_overhead; + remaining_padding_bytes_ += frame_to_split_old_overhead; + } +} + +void QuicChaosProtector::AddPingFrames() { + constexpr uint64_t kMaxAddedPingFrames = 10; + const uint64_t num_ping_frames = + random_->InsecureRandUint64() % + std::min<uint64_t>(kMaxAddedPingFrames, remaining_padding_bytes_); + for (uint64_t i = 0; i < num_ping_frames; i++) { + frames_.push_back(QuicFrame(QuicPingFrame())); + } + remaining_padding_bytes_ -= static_cast<int>(num_ping_frames); +} + +void QuicChaosProtector::ReorderFrames() { + // Walk the array backwards and swap each frame with a random earlier one. + for (size_t i = frames_.size() - 1; i > 0; i--) { + std::swap(frames_[i], frames_[random_->InsecureRandUint64() % (i + 1)]); + } +} + +void QuicChaosProtector::SpreadPadding() { + for (auto it = frames_.begin(); it != frames_.end(); ++it) { + const int padding_bytes_in_this_frame = + random_->InsecureRandUint64() % (remaining_padding_bytes_ + 1); + if (padding_bytes_in_this_frame <= 0) { + continue; + } + it = frames_.insert( + it, QuicFrame(QuicPaddingFrame(padding_bytes_in_this_frame))); + ++it; // Skip over the padding frame we just added. + remaining_padding_bytes_ -= padding_bytes_in_this_frame; + } + if (remaining_padding_bytes_ > 0) { + frames_.push_back(QuicFrame(QuicPaddingFrame(remaining_padding_bytes_))); + } +} + +absl::optional<size_t> QuicChaosProtector::BuildPacket( + const QuicPacketHeader& header, + char* buffer) { + QuicStreamFrameDataProducer* original_data_producer = + framer_->data_producer(); + framer_->set_data_producer(this); + + size_t length = + framer_->BuildDataPacket(header, frames_, buffer, packet_size_, level_); + + framer_->set_data_producer(original_data_producer); + if (length == 0) { + return absl::nullopt; + } + return length; +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_chaos_protector.h b/chromium/net/third_party/quiche/src/quic/core/quic_chaos_protector.h new file mode 100644 index 00000000000..6bcd42087e7 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/quic_chaos_protector.h @@ -0,0 +1,99 @@ +// Copyright (c) 2021 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 QUICHE_QUIC_CORE_QUIC_CHAOS_PROTECTOR_H_ +#define QUICHE_QUIC_CORE_QUIC_CHAOS_PROTECTOR_H_ + +#include <cstddef> +#include <memory> + +#include "absl/types/optional.h" +#include "quic/core/crypto/quic_random.h" +#include "quic/core/frames/quic_crypto_frame.h" +#include "quic/core/frames/quic_frame.h" +#include "quic/core/quic_data_writer.h" +#include "quic/core/quic_framer.h" +#include "quic/core/quic_packets.h" +#include "quic/core/quic_stream_frame_data_producer.h" +#include "quic/core/quic_types.h" + +namespace quic { + +namespace test { +class QuicChaosProtectorTest; +} + +// QuicChaosProtector will take a crypto frame and an amount of padding and +// build a data packet that will parse to something equivalent. +class QUIC_EXPORT_PRIVATE QuicChaosProtector + : public QuicStreamFrameDataProducer { + public: + // |framer| and |random| must be valid for the lifetime of QuicChaosProtector. + explicit QuicChaosProtector(const QuicCryptoFrame& crypto_frame, + int num_padding_bytes, + size_t packet_size, + QuicFramer* framer, + QuicRandom* random); + + ~QuicChaosProtector() override; + + QuicChaosProtector(const QuicChaosProtector&) = delete; + QuicChaosProtector(QuicChaosProtector&&) = delete; + QuicChaosProtector& operator=(const QuicChaosProtector&) = delete; + QuicChaosProtector& operator=(QuicChaosProtector&&) = delete; + + // Attempts to build a data packet with chaos protection. If an error occurs, + // then absl::nullopt is returned. Otherwise returns the serialized length. + absl::optional<size_t> BuildDataPacket(const QuicPacketHeader& header, + char* buffer); + + // From QuicStreamFrameDataProducer. + WriteStreamDataResult WriteStreamData(QuicStreamId id, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* /*writer*/) override; + bool WriteCryptoData(EncryptionLevel level, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer) override; + + private: + friend class test::QuicChaosProtectorTest; + + // Allocate the crypto data buffer, create the CRYPTO frame and write the + // crypto data to our buffer. + bool CopyCryptoDataToLocalBuffer(); + + // Split the CRYPTO frame in |frames_| into one or more CRYPTO frames that + // collectively represent the same data. Adjusts padding to compensate. + void SplitCryptoFrame(); + + // Add a random number of PING frames to |frames_| and adjust padding. + void AddPingFrames(); + + // Randomly reorder |frames_|. + void ReorderFrames(); + + // Add PADDING frames randomly between all other frames. + void SpreadPadding(); + + // Serialize |frames_| using |framer_|. + absl::optional<size_t> BuildPacket(const QuicPacketHeader& header, + char* buffer); + + size_t packet_size_; + std::unique_ptr<char[]> crypto_frame_buffer_; + const char* crypto_data_buffer_ = nullptr; + QuicByteCount crypto_data_length_; + QuicStreamOffset crypto_buffer_offset_; + EncryptionLevel level_; + int remaining_padding_bytes_; + QuicFrames frames_; // Inner frames owned, will be deleted by destructor. + QuicFramer* framer_; // Unowned. + QuicRandom* random_; // Unowned. +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_CHAOS_PROTECTOR_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_chaos_protector_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_chaos_protector_test.cc new file mode 100644 index 00000000000..1ec985beea1 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/quic_chaos_protector_test.cc @@ -0,0 +1,207 @@ +// Copyright (c) 2021 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 "quic/core/quic_chaos_protector.h" + +#include <cstddef> +#include <memory> + +#include "absl/strings/string_view.h" +#include "quic/core/frames/quic_crypto_frame.h" +#include "quic/core/quic_connection_id.h" +#include "quic/core/quic_framer.h" +#include "quic/core/quic_packet_number.h" +#include "quic/core/quic_packets.h" +#include "quic/core/quic_stream_frame_data_producer.h" +#include "quic/core/quic_time.h" +#include "quic/core/quic_types.h" +#include "quic/core/quic_versions.h" +#include "quic/platform/api/quic_test.h" +#include "quic/test_tools/mock_random.h" +#include "quic/test_tools/quic_test_utils.h" +#include "quic/test_tools/simple_quic_framer.h" + +namespace quic { +namespace test { + +class QuicChaosProtectorTest : public QuicTestWithParam<ParsedQuicVersion>, + public QuicStreamFrameDataProducer { + public: + QuicChaosProtectorTest() + : version_(GetParam()), + framer_({version_}, + QuicTime::Zero(), + Perspective::IS_CLIENT, + kQuicDefaultConnectionIdLength), + validation_framer_({version_}), + random_(/*base=*/3), + level_(ENCRYPTION_INITIAL), + crypto_offset_(0), + crypto_data_length_(100), + crypto_frame_(level_, crypto_offset_, crypto_data_length_), + num_padding_bytes_(50), + packet_size_(1000), + packet_buffer_(std::make_unique<char[]>(packet_size_)), + chaos_protector_(crypto_frame_, + num_padding_bytes_, + packet_size_, + SetupHeaderAndFramers(), + &random_) {} + + // From QuicStreamFrameDataProducer. + WriteStreamDataResult WriteStreamData(QuicStreamId /*id*/, + QuicStreamOffset /*offset*/, + QuicByteCount /*data_length*/, + QuicDataWriter* /*writer*/) override { + ADD_FAILURE() << "This should never be called"; + return STREAM_MISSING; + } + + // From QuicStreamFrameDataProducer. + bool WriteCryptoData(EncryptionLevel level, + QuicStreamOffset offset, + QuicByteCount data_length, + QuicDataWriter* writer) override { + EXPECT_EQ(level, level); + EXPECT_EQ(offset, crypto_offset_); + EXPECT_EQ(data_length, crypto_data_length_); + for (QuicByteCount i = 0; i < data_length; i++) { + EXPECT_TRUE(writer->WriteUInt8(static_cast<uint8_t>(i & 0xFF))); + } + return true; + } + + protected: + QuicFramer* SetupHeaderAndFramers() { + // Setup header. + header_.destination_connection_id = TestConnectionId(); + header_.destination_connection_id_included = CONNECTION_ID_PRESENT; + header_.source_connection_id = EmptyQuicConnectionId(); + header_.source_connection_id_included = CONNECTION_ID_PRESENT; + header_.reset_flag = false; + header_.version_flag = true; + header_.has_possible_stateless_reset_token = false; + header_.packet_number_length = PACKET_4BYTE_PACKET_NUMBER; + header_.version = version_; + header_.packet_number = QuicPacketNumber(1); + header_.form = IETF_QUIC_LONG_HEADER_PACKET; + header_.long_packet_type = INITIAL; + header_.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1; + header_.length_length = kQuicDefaultLongHeaderLengthLength; + // Setup validation framer. + validation_framer_.framer()->SetInitialObfuscators( + header_.destination_connection_id); + // Setup framer. + framer_.SetInitialObfuscators(header_.destination_connection_id); + framer_.set_data_producer(this); + return &framer_; + } + + void BuildEncryptAndParse() { + absl::optional<size_t> length = + chaos_protector_.BuildDataPacket(header_, packet_buffer_.get()); + ASSERT_TRUE(length.has_value()); + ASSERT_GT(length.value(), 0u); + size_t encrypted_length = framer_.EncryptInPlace( + level_, header_.packet_number, + GetStartOfEncryptedData(framer_.transport_version(), header_), + length.value(), packet_size_, packet_buffer_.get()); + ASSERT_GT(encrypted_length, 0u); + ASSERT_TRUE(validation_framer_.ProcessPacket(QuicEncryptedPacket( + absl::string_view(packet_buffer_.get(), encrypted_length)))); + } + + void ResetOffset(QuicStreamOffset offset) { + crypto_offset_ = offset; + crypto_frame_.offset = offset; + chaos_protector_.crypto_buffer_offset_ = offset; + } + + ParsedQuicVersion version_; + QuicPacketHeader header_; + QuicFramer framer_; + SimpleQuicFramer validation_framer_; + MockRandom random_; + EncryptionLevel level_; + QuicStreamOffset crypto_offset_; + QuicByteCount crypto_data_length_; + QuicCryptoFrame crypto_frame_; + int num_padding_bytes_; + size_t packet_size_; + std::unique_ptr<char[]> packet_buffer_; + QuicChaosProtector chaos_protector_; +}; + +namespace { + +ParsedQuicVersionVector TestVersions() { + ParsedQuicVersionVector versions; + for (const ParsedQuicVersion& version : AllSupportedVersions()) { + if (version.UsesCryptoFrames()) { + versions.push_back(version); + } + } + return versions; +} + +INSTANTIATE_TEST_SUITE_P(QuicChaosProtectorTests, + QuicChaosProtectorTest, + ::testing::ValuesIn(TestVersions()), + ::testing::PrintToStringParamName()); + +TEST_P(QuicChaosProtectorTest, Main) { + BuildEncryptAndParse(); + ASSERT_EQ(validation_framer_.crypto_frames().size(), 4u); + EXPECT_EQ(validation_framer_.crypto_frames()[0]->offset, 0u); + EXPECT_EQ(validation_framer_.crypto_frames()[0]->data_length, 1u); + ASSERT_EQ(validation_framer_.ping_frames().size(), 3u); + ASSERT_EQ(validation_framer_.padding_frames().size(), 7u); + EXPECT_EQ(validation_framer_.padding_frames()[0].num_padding_bytes, 3); +} + +TEST_P(QuicChaosProtectorTest, DifferentRandom) { + random_.ResetBase(4); + BuildEncryptAndParse(); + ASSERT_EQ(validation_framer_.crypto_frames().size(), 4u); + ASSERT_EQ(validation_framer_.ping_frames().size(), 4u); + ASSERT_EQ(validation_framer_.padding_frames().size(), 8u); +} + +TEST_P(QuicChaosProtectorTest, RandomnessZero) { + random_.ResetBase(0); + BuildEncryptAndParse(); + ASSERT_EQ(validation_framer_.crypto_frames().size(), 1u); + EXPECT_EQ(validation_framer_.crypto_frames()[0]->offset, crypto_offset_); + EXPECT_EQ(validation_framer_.crypto_frames()[0]->data_length, + crypto_data_length_); + ASSERT_EQ(validation_framer_.ping_frames().size(), 0u); + ASSERT_EQ(validation_framer_.padding_frames().size(), 1u); +} + +TEST_P(QuicChaosProtectorTest, Offset) { + ResetOffset(123); + BuildEncryptAndParse(); + ASSERT_EQ(validation_framer_.crypto_frames().size(), 4u); + EXPECT_EQ(validation_framer_.crypto_frames()[0]->offset, crypto_offset_); + EXPECT_EQ(validation_framer_.crypto_frames()[0]->data_length, 1u); + ASSERT_EQ(validation_framer_.ping_frames().size(), 3u); + ASSERT_EQ(validation_framer_.padding_frames().size(), 7u); + EXPECT_EQ(validation_framer_.padding_frames()[0].num_padding_bytes, 3); +} + +TEST_P(QuicChaosProtectorTest, OffsetAndRandomnessZero) { + ResetOffset(123); + random_.ResetBase(0); + BuildEncryptAndParse(); + ASSERT_EQ(validation_framer_.crypto_frames().size(), 1u); + EXPECT_EQ(validation_framer_.crypto_frames()[0]->offset, crypto_offset_); + EXPECT_EQ(validation_framer_.crypto_frames()[0]->data_length, + crypto_data_length_); + ASSERT_EQ(validation_framer_.ping_frames().size(), 0u); + ASSERT_EQ(validation_framer_.padding_frames().size(), 1u); +} + +} // namespace +} // namespace test +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_circular_deque.h b/chromium/net/third_party/quiche/src/quic/core/quic_circular_deque.h deleted file mode 100644 index 44637abcbd6..00000000000 --- a/chromium/net/third_party/quiche/src/quic/core/quic_circular_deque.h +++ /dev/null @@ -1,757 +0,0 @@ -// Copyright (c) 2019 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 QUICHE_QUIC_CORE_QUIC_CIRCULAR_DEQUE_H_ -#define QUICHE_QUIC_CORE_QUIC_CIRCULAR_DEQUE_H_ - -#include <algorithm> -#include <cstddef> -#include <cstring> -#include <iterator> -#include <memory> -#include <ostream> -#include <type_traits> - -#include "quic/platform/api/quic_export.h" -#include "quic/platform/api/quic_logging.h" - -namespace quic { - -// QuicCircularDeque is a STL-style container that is similar to std deque in -// API and std::vector in capacity management. The goal is to optimize a common -// QUIC use case where we keep adding new elements to the end and removing old -// elements from the beginning, under such scenarios, if the container's size() -// remain relatively stable, QuicCircularDeque requires little to no memory -// allocations or deallocations. -// -// The implementation, as the name suggests, uses a flat circular buffer to hold -// all elements. At any point in time, either -// a) All elements are placed in a contiguous portion of this buffer, like a -// c-array, or -// b) Elements are phycially divided into two parts: the first part occupies the -// end of the buffer and the second part occupies the beginning of the -// buffer. -// -// Currently, elements can only be pushed or poped from either ends, it can't be -// inserted or erased in the middle. -// -// TODO(wub): Make memory grow/shrink strategies customizable. -template <typename T, - size_t MinCapacityIncrement = 3, - typename Allocator = std::allocator<T>> -class QUIC_NO_EXPORT QuicCircularDeque { - using AllocatorTraits = std::allocator_traits<Allocator>; - - // Pointee is either T or const T. - template <typename Pointee> - class QUIC_NO_EXPORT basic_iterator { - using size_type = typename AllocatorTraits::size_type; - - public: - using iterator_category = std::random_access_iterator_tag; - using value_type = typename AllocatorTraits::value_type; - using difference_type = typename AllocatorTraits::difference_type; - using pointer = Pointee*; - using reference = Pointee&; - - basic_iterator() = default; - - // A copy constructor if Pointee is T. - // A conversion from iterator to const_iterator if Pointee is const T. - basic_iterator( - const basic_iterator<value_type>& it) // NOLINT(runtime/explicit) - : deque_(it.deque_), index_(it.index_) {} - - // A copy assignment if Pointee is T. - // A assignment from iterator to const_iterator if Pointee is const T. - basic_iterator& operator=(const basic_iterator<value_type>& it) { - if (this != &it) { - deque_ = it.deque_; - index_ = it.index_; - } - return *this; - } - - reference operator*() const { return *deque_->index_to_address(index_); } - pointer operator->() const { return deque_->index_to_address(index_); } - reference operator[](difference_type i) { return *(*this + i); } - - basic_iterator& operator++() { - Increment(); - return *this; - } - - basic_iterator operator++(int) { - basic_iterator result = *this; - Increment(); - return result; - } - - basic_iterator operator--() { - Decrement(); - return *this; - } - - basic_iterator operator--(int) { - basic_iterator result = *this; - Decrement(); - return result; - } - - friend basic_iterator operator+(const basic_iterator& it, - difference_type delta) { - basic_iterator result = it; - result.IncrementBy(delta); - return result; - } - - basic_iterator& operator+=(difference_type delta) { - IncrementBy(delta); - return *this; - } - - friend basic_iterator operator-(const basic_iterator& it, - difference_type delta) { - basic_iterator result = it; - result.IncrementBy(-delta); - return result; - } - - basic_iterator& operator-=(difference_type delta) { - IncrementBy(-delta); - return *this; - } - - friend difference_type operator-(const basic_iterator& lhs, - const basic_iterator& rhs) { - return lhs.ExternalPosition() - rhs.ExternalPosition(); - } - - friend bool operator==(const basic_iterator& lhs, - const basic_iterator& rhs) { - return lhs.index_ == rhs.index_; - } - - friend bool operator!=(const basic_iterator& lhs, - const basic_iterator& rhs) { - return !(lhs == rhs); - } - - friend bool operator<(const basic_iterator& lhs, - const basic_iterator& rhs) { - return lhs.ExternalPosition() < rhs.ExternalPosition(); - } - - friend bool operator<=(const basic_iterator& lhs, - const basic_iterator& rhs) { - return !(lhs > rhs); - } - - friend bool operator>(const basic_iterator& lhs, - const basic_iterator& rhs) { - return lhs.ExternalPosition() > rhs.ExternalPosition(); - } - - friend bool operator>=(const basic_iterator& lhs, - const basic_iterator& rhs) { - return !(lhs < rhs); - } - - private: - basic_iterator(const QuicCircularDeque* deque, size_type index) - : deque_(deque), index_(index) {} - - void Increment() { - QUICHE_DCHECK_LE(ExternalPosition() + 1, deque_->size()); - index_ = deque_->index_next(index_); - } - - void Decrement() { - QUICHE_DCHECK_GE(ExternalPosition(), 1u); - index_ = deque_->index_prev(index_); - } - - void IncrementBy(difference_type delta) { - if (delta >= 0) { - // After increment we are before or at end(). - QUICHE_DCHECK_LE(static_cast<size_type>(ExternalPosition() + delta), - deque_->size()); - } else { - // After decrement we are after or at begin(). - QUICHE_DCHECK_GE(ExternalPosition(), static_cast<size_type>(-delta)); - } - index_ = deque_->index_increment_by(index_, delta); - } - - size_type ExternalPosition() const { - if (index_ >= deque_->begin_) { - return index_ - deque_->begin_; - } - return index_ + deque_->data_capacity() - deque_->begin_; - } - - friend class QuicCircularDeque; - const QuicCircularDeque* deque_ = nullptr; - size_type index_ = 0; - }; - - public: - using allocator_type = typename AllocatorTraits::allocator_type; - using value_type = typename AllocatorTraits::value_type; - using size_type = typename AllocatorTraits::size_type; - using difference_type = typename AllocatorTraits::difference_type; - using reference = value_type&; - using const_reference = const value_type&; - using pointer = typename AllocatorTraits::pointer; - using const_pointer = typename AllocatorTraits::const_pointer; - using iterator = basic_iterator<T>; - using const_iterator = basic_iterator<const T>; - using reverse_iterator = std::reverse_iterator<iterator>; - using const_reverse_iterator = std::reverse_iterator<const_iterator>; - - QuicCircularDeque() : QuicCircularDeque(allocator_type()) {} - explicit QuicCircularDeque(const allocator_type& alloc) - : allocator_and_data_(alloc) {} - - QuicCircularDeque(size_type count, - const T& value, - const Allocator& alloc = allocator_type()) - : allocator_and_data_(alloc) { - resize(count, value); - } - - explicit QuicCircularDeque(size_type count, - const Allocator& alloc = allocator_type()) - : allocator_and_data_(alloc) { - resize(count); - } - - template < - class InputIt, - typename = std::enable_if_t<std::is_base_of< - std::input_iterator_tag, - typename std::iterator_traits<InputIt>::iterator_category>::value>> - QuicCircularDeque(InputIt first, - InputIt last, - const Allocator& alloc = allocator_type()) - : allocator_and_data_(alloc) { - AssignRange(first, last); - } - - QuicCircularDeque(const QuicCircularDeque& other) - : QuicCircularDeque( - other, - AllocatorTraits::select_on_container_copy_construction( - other.allocator_and_data_.allocator())) {} - - QuicCircularDeque(const QuicCircularDeque& other, const allocator_type& alloc) - : allocator_and_data_(alloc) { - assign(other.begin(), other.end()); - } - - QuicCircularDeque(QuicCircularDeque&& other) - : begin_(other.begin_), - end_(other.end_), - allocator_and_data_(std::move(other.allocator_and_data_)) { - other.begin_ = other.end_ = 0; - other.allocator_and_data_.data = nullptr; - other.allocator_and_data_.data_capacity = 0; - } - - QuicCircularDeque(QuicCircularDeque&& other, const allocator_type& alloc) - : allocator_and_data_(alloc) { - MoveRetainAllocator(std::move(other)); - } - - QuicCircularDeque(std::initializer_list<T> init, - const allocator_type& alloc = allocator_type()) - : QuicCircularDeque(init.begin(), init.end(), alloc) {} - - QuicCircularDeque& operator=(const QuicCircularDeque& other) { - if (this == &other) { - return *this; - } - if (AllocatorTraits::propagate_on_container_copy_assignment::value && - (allocator_and_data_.allocator() != - other.allocator_and_data_.allocator())) { - // Destroy all current elements and blocks with the current allocator, - // before switching this to use the allocator propagated from "other". - DestroyAndDeallocateAll(); - begin_ = end_ = 0; - allocator_and_data_ = - AllocatorAndData(other.allocator_and_data_.allocator()); - } - assign(other.begin(), other.end()); - return *this; - } - - QuicCircularDeque& operator=(QuicCircularDeque&& other) { - if (this == &other) { - return *this; - } - if (AllocatorTraits::propagate_on_container_move_assignment::value) { - // Take over the storage of "other", along with its allocator. - this->~QuicCircularDeque(); - new (this) QuicCircularDeque(std::move(other)); - } else { - MoveRetainAllocator(std::move(other)); - } - return *this; - } - - ~QuicCircularDeque() { DestroyAndDeallocateAll(); } - - void assign(size_type count, const T& value) { - ClearRetainCapacity(); - reserve(count); - for (size_t i = 0; i < count; ++i) { - emplace_back(value); - } - } - - template < - class InputIt, - typename = std::enable_if_t<std::is_base_of< - std::input_iterator_tag, - typename std::iterator_traits<InputIt>::iterator_category>::value>> - void assign(InputIt first, InputIt last) { - AssignRange(first, last); - } - - void assign(std::initializer_list<T> ilist) { - assign(ilist.begin(), ilist.end()); - } - - reference at(size_type pos) { - QUICHE_DCHECK(pos < size()) << "pos:" << pos << ", size():" << size(); - size_type index = begin_ + pos; - if (index < data_capacity()) { - return *index_to_address(index); - } - return *index_to_address(index - data_capacity()); - } - - const_reference at(size_type pos) const { - return const_cast<QuicCircularDeque*>(this)->at(pos); - } - - reference operator[](size_type pos) { return at(pos); } - - const_reference operator[](size_type pos) const { return at(pos); } - - reference front() { - QUICHE_DCHECK(!empty()); - return *index_to_address(begin_); - } - - const_reference front() const { - return const_cast<QuicCircularDeque*>(this)->front(); - } - - reference back() { - QUICHE_DCHECK(!empty()); - return *(index_to_address(end_ == 0 ? data_capacity() - 1 : end_ - 1)); - } - - const_reference back() const { - return const_cast<QuicCircularDeque*>(this)->back(); - } - - iterator begin() { return iterator(this, begin_); } - const_iterator begin() const { return const_iterator(this, begin_); } - const_iterator cbegin() const { return const_iterator(this, begin_); } - - iterator end() { return iterator(this, end_); } - const_iterator end() const { return const_iterator(this, end_); } - const_iterator cend() const { return const_iterator(this, end_); } - - reverse_iterator rbegin() { return reverse_iterator(end()); } - const_reverse_iterator rbegin() const { - return const_reverse_iterator(end()); - } - const_reverse_iterator crbegin() const { return rbegin(); } - - reverse_iterator rend() { return reverse_iterator(begin()); } - const_reverse_iterator rend() const { - return const_reverse_iterator(begin()); - } - const_reverse_iterator crend() const { return rend(); } - - size_type capacity() const { - return data_capacity() == 0 ? 0 : data_capacity() - 1; - } - - void reserve(size_type new_cap) { - if (new_cap > capacity()) { - Relocate(new_cap); - } - } - - // Remove all elements. Leave capacity unchanged. - void clear() { ClearRetainCapacity(); } - - bool empty() const { return begin_ == end_; } - - size_type size() const { - if (begin_ <= end_) { - return end_ - begin_; - } - return data_capacity() + end_ - begin_; - } - - void resize(size_type count) { ResizeInternal(count); } - - void resize(size_type count, const value_type& value) { - ResizeInternal(count, value); - } - - void push_front(const T& value) { emplace_front(value); } - void push_front(T&& value) { emplace_front(std::move(value)); } - - template <class... Args> - reference emplace_front(Args&&... args) { - MaybeExpandCapacity(1); - begin_ = index_prev(begin_); - new (index_to_address(begin_)) T(std::forward<Args>(args)...); - return front(); - } - - void push_back(const T& value) { emplace_back(value); } - void push_back(T&& value) { emplace_back(std::move(value)); } - - template <class... Args> - reference emplace_back(Args&&... args) { - MaybeExpandCapacity(1); - new (index_to_address(end_)) T(std::forward<Args>(args)...); - end_ = index_next(end_); - return back(); - } - - void pop_front() { - QUICHE_DCHECK(!empty()); - DestroyByIndex(begin_); - begin_ = index_next(begin_); - MaybeShrinkCapacity(); - } - - size_type pop_front_n(size_type count) { - size_type num_elements_to_pop = std::min(count, size()); - size_type new_begin = index_increment_by(begin_, num_elements_to_pop); - DestroyRange(begin_, new_begin); - begin_ = new_begin; - MaybeShrinkCapacity(); - return num_elements_to_pop; - } - - void pop_back() { - QUICHE_DCHECK(!empty()); - end_ = index_prev(end_); - DestroyByIndex(end_); - MaybeShrinkCapacity(); - } - - size_type pop_back_n(size_type count) { - size_type num_elements_to_pop = std::min(count, size()); - size_type new_end = index_increment_by(end_, -num_elements_to_pop); - DestroyRange(new_end, end_); - end_ = new_end; - MaybeShrinkCapacity(); - return num_elements_to_pop; - } - - void swap(QuicCircularDeque& other) { - using std::swap; - swap(begin_, other.begin_); - swap(end_, other.end_); - - if (AllocatorTraits::propagate_on_container_swap::value) { - swap(allocator_and_data_, other.allocator_and_data_); - } else { - // When propagate_on_container_swap is false, it is undefined behavior, by - // c++ standard, to swap between two AllocatorAwareContainer(s) with - // unequal allocators. - QUICHE_DCHECK(get_allocator() == other.get_allocator()) - << "Undefined swap behavior"; - swap(allocator_and_data_.data, other.allocator_and_data_.data); - swap(allocator_and_data_.data_capacity, - other.allocator_and_data_.data_capacity); - } - } - - friend void swap(QuicCircularDeque& lhs, QuicCircularDeque& rhs) { - lhs.swap(rhs); - } - - allocator_type get_allocator() const { - return allocator_and_data_.allocator(); - } - - friend bool operator==(const QuicCircularDeque& lhs, - const QuicCircularDeque& rhs) { - return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); - } - - friend bool operator!=(const QuicCircularDeque& lhs, - const QuicCircularDeque& rhs) { - return !(lhs == rhs); - } - - friend QUIC_NO_EXPORT std::ostream& operator<<(std::ostream& os, - const QuicCircularDeque& dq) { - os << "{"; - for (size_type pos = 0; pos != dq.size(); ++pos) { - if (pos != 0) { - os << ","; - } - os << " " << dq[pos]; - } - os << " }"; - return os; - } - - private: - void MoveRetainAllocator(QuicCircularDeque&& other) { - if (get_allocator() == other.get_allocator()) { - // Take over the storage of "other", with which we share an allocator. - DestroyAndDeallocateAll(); - - begin_ = other.begin_; - end_ = other.end_; - allocator_and_data_.data = other.allocator_and_data_.data; - allocator_and_data_.data_capacity = - other.allocator_and_data_.data_capacity; - - other.begin_ = other.end_ = 0; - other.allocator_and_data_.data = nullptr; - other.allocator_and_data_.data_capacity = 0; - } else { - // We cannot take over of the storage from "other", since it has a - // different allocator; we're stuck move-assigning elements individually. - ClearRetainCapacity(); - for (auto& elem : other) { - push_back(std::move(elem)); - } - other.clear(); - } - } - - template < - typename InputIt, - typename = std::enable_if_t<std::is_base_of< - std::input_iterator_tag, - typename std::iterator_traits<InputIt>::iterator_category>::value>> - void AssignRange(InputIt first, InputIt last) { - ClearRetainCapacity(); - if (std::is_base_of< - std::random_access_iterator_tag, - typename std::iterator_traits<InputIt>::iterator_category>::value) { - reserve(std::distance(first, last)); - } - for (; first != last; ++first) { - emplace_back(*first); - } - } - - // WARNING: begin_, end_ and allocator_and_data_ are not modified. - void DestroyAndDeallocateAll() { - DestroyRange(begin_, end_); - - if (data_capacity() > 0) { - QUICHE_DCHECK_NE(nullptr, allocator_and_data_.data); - AllocatorTraits::deallocate(allocator_and_data_.allocator(), - allocator_and_data_.data, data_capacity()); - } - } - - void ClearRetainCapacity() { - DestroyRange(begin_, end_); - begin_ = end_ = 0; - } - - void MaybeShrinkCapacity() { - // TODO(wub): Implement a storage policy that actually shrinks. - } - - void MaybeExpandCapacity(size_t num_additional_elements) { - size_t new_size = size() + num_additional_elements; - if (capacity() >= new_size) { - return; - } - - // The minimum amount of additional capacity to grow. - size_t min_additional_capacity = - std::max(MinCapacityIncrement, capacity() / 4); - size_t new_capacity = - std::max(new_size, capacity() + min_additional_capacity); - - Relocate(new_capacity); - } - - void Relocate(size_t new_capacity) { - const size_t num_elements = size(); - QUICHE_DCHECK_GT(new_capacity, num_elements) - << "new_capacity:" << new_capacity << ", num_elements:" << num_elements; - - size_t new_data_capacity = new_capacity + 1; - pointer new_data = AllocatorTraits::allocate( - allocator_and_data_.allocator(), new_data_capacity); - - if (begin_ < end_) { - // Not wrapped. - RelocateUnwrappedRange(begin_, end_, new_data); - } else if (begin_ > end_) { - // Wrapped. - const size_t num_elements_before_wrap = data_capacity() - begin_; - RelocateUnwrappedRange(begin_, data_capacity(), new_data); - RelocateUnwrappedRange(0, end_, new_data + num_elements_before_wrap); - } - - if (data_capacity()) { - AllocatorTraits::deallocate(allocator_and_data_.allocator(), - allocator_and_data_.data, data_capacity()); - } - - allocator_and_data_.data = new_data; - allocator_and_data_.data_capacity = new_data_capacity; - begin_ = 0; - end_ = num_elements; - } - - template <typename T_ = T> - typename std::enable_if<std::is_trivially_copyable<T_>::value, void>::type - RelocateUnwrappedRange(size_type begin, size_type end, pointer dest) const { - QUICHE_DCHECK_LE(begin, end) << "begin:" << begin << ", end:" << end; - pointer src = index_to_address(begin); - QUICHE_DCHECK_NE(src, nullptr); - memcpy(dest, src, sizeof(T) * (end - begin)); - DestroyRange(begin, end); - } - - template <typename T_ = T> - typename std::enable_if<!std::is_trivially_copyable<T_>::value && - std::is_move_constructible<T_>::value, - void>::type - RelocateUnwrappedRange(size_type begin, size_type end, pointer dest) const { - QUICHE_DCHECK_LE(begin, end) << "begin:" << begin << ", end:" << end; - pointer src = index_to_address(begin); - pointer src_end = index_to_address(end); - while (src != src_end) { - new (dest) T(std::move(*src)); - DestroyByAddress(src); - ++dest; - ++src; - } - } - - template <typename T_ = T> - typename std::enable_if<!std::is_trivially_copyable<T_>::value && - !std::is_move_constructible<T_>::value, - void>::type - RelocateUnwrappedRange(size_type begin, size_type end, pointer dest) const { - QUICHE_DCHECK_LE(begin, end) << "begin:" << begin << ", end:" << end; - pointer src = index_to_address(begin); - pointer src_end = index_to_address(end); - while (src != src_end) { - new (dest) T(*src); - DestroyByAddress(src); - ++dest; - ++src; - } - } - - template <class... U> - void ResizeInternal(size_type count, U&&... u) { - if (count > size()) { - // Expanding. - MaybeExpandCapacity(count - size()); - while (size() < count) { - emplace_back(std::forward<U>(u)...); - } - } else { - // Most likely shrinking. No-op if count == size(). - size_type new_end = (begin_ + count) % data_capacity(); - DestroyRange(new_end, end_); - end_ = new_end; - - MaybeShrinkCapacity(); - } - } - - void DestroyRange(size_type begin, size_type end) const { - if (std::is_trivially_destructible<T>::value) { - return; - } - if (end >= begin) { - DestroyUnwrappedRange(begin, end); - } else { - DestroyUnwrappedRange(begin, data_capacity()); - DestroyUnwrappedRange(0, end); - } - } - - // Should only be called from DestroyRange. - void DestroyUnwrappedRange(size_type begin, size_type end) const { - QUICHE_DCHECK_LE(begin, end) << "begin:" << begin << ", end:" << end; - for (; begin != end; ++begin) { - DestroyByIndex(begin); - } - } - - void DestroyByIndex(size_type index) const { - DestroyByAddress(index_to_address(index)); - } - - void DestroyByAddress(pointer address) const { - if (std::is_trivially_destructible<T>::value) { - return; - } - address->~T(); - } - - size_type data_capacity() const { return allocator_and_data_.data_capacity; } - - pointer index_to_address(size_type index) const { - return allocator_and_data_.data + index; - } - - size_type index_prev(size_type index) const { - return index == 0 ? data_capacity() - 1 : index - 1; - } - - size_type index_next(size_type index) const { - return index == data_capacity() - 1 ? 0 : index + 1; - } - - size_type index_increment_by(size_type index, difference_type delta) const { - if (delta == 0) { - return index; - } - - QUICHE_DCHECK_LT(static_cast<size_type>(std::abs(delta)), data_capacity()); - return (index + data_capacity() + delta) % data_capacity(); - } - - // Empty base-class optimization: bundle storage for our allocator together - // with the fields we had to store anyway, via inheriting from the allocator, - // so this allocator instance doesn't consume any storage when its type has no - // data members. - struct AllocatorAndData : private allocator_type { - explicit AllocatorAndData(const allocator_type& alloc) - : allocator_type(alloc) {} - - const allocator_type& allocator() const { return *this; } - allocator_type& allocator() { return *this; } - - pointer data = nullptr; - size_type data_capacity = 0; - }; - - size_type begin_ = 0; - size_type end_ = 0; - AllocatorAndData allocator_and_data_; -}; - -} // namespace quic - -#endif // QUICHE_QUIC_CORE_QUIC_CIRCULAR_DEQUE_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_circular_deque_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_circular_deque_test.cc deleted file mode 100644 index 25aa78e709a..00000000000 --- a/chromium/net/third_party/quiche/src/quic/core/quic_circular_deque_test.cc +++ /dev/null @@ -1,796 +0,0 @@ -// Copyright (c) 2019 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 "quic/core/quic_circular_deque.h" - -#include <cstddef> -#include <cstdint> -#include <memory> -#include <type_traits> - -#include "quic/platform/api/quic_logging.h" -#include "quic/platform/api/quic_test.h" - -using testing::ElementsAre; - -namespace quic { -namespace test { -namespace { - -template <typename T, template <typename> class BaseAllocator = std::allocator> -class CountingAllocator : public BaseAllocator<T> { - using BaseType = BaseAllocator<T>; - - public: - using propagate_on_container_copy_assignment = std::true_type; - using propagate_on_container_move_assignment = std::true_type; - using propagate_on_container_swap = std::true_type; - - T* allocate(std::size_t n) { - ++shared_counts_->allocate_count; - return BaseType::allocate(n); - } - - void deallocate(T* ptr, std::size_t n) { - ++shared_counts_->deallocate_count; - return BaseType::deallocate(ptr, n); - } - - size_t allocate_count() const { return shared_counts_->allocate_count; } - - size_t deallocate_count() const { return shared_counts_->deallocate_count; } - - friend bool operator==(const CountingAllocator& lhs, - const CountingAllocator& rhs) { - return lhs.shared_counts_ == rhs.shared_counts_; - } - - friend bool operator!=(const CountingAllocator& lhs, - const CountingAllocator& rhs) { - return !(lhs == rhs); - } - - private: - struct Counts { - size_t allocate_count = 0; - size_t deallocate_count = 0; - }; - - std::shared_ptr<Counts> shared_counts_ = std::make_shared<Counts>(); -}; - -template <typename T, - typename propagate_on_copy_assignment, - typename propagate_on_move_assignment, - typename propagate_on_swap, - bool equality_result, - template <typename> class BaseAllocator = std::allocator> -struct ConfigurableAllocator : public BaseAllocator<T> { - using propagate_on_container_copy_assignment = propagate_on_copy_assignment; - using propagate_on_container_move_assignment = propagate_on_move_assignment; - using propagate_on_container_swap = propagate_on_swap; - - friend bool operator==(const ConfigurableAllocator& /*lhs*/, - const ConfigurableAllocator& /*rhs*/) { - return equality_result; - } - - friend bool operator!=(const ConfigurableAllocator& lhs, - const ConfigurableAllocator& rhs) { - return !(lhs == rhs); - } -}; - -// [1, 2, 3, 4] ==> [4, 1, 2, 3] -template <typename Deque> -void ShiftRight(Deque* dq, bool emplace) { - auto back = *(&dq->back()); - dq->pop_back(); - if (emplace) { - dq->emplace_front(back); - } else { - dq->push_front(back); - } -} - -// [1, 2, 3, 4] ==> [2, 3, 4, 1] -template <typename Deque> -void ShiftLeft(Deque* dq, bool emplace) { - auto front = *(&dq->front()); - dq->pop_front(); - if (emplace) { - dq->emplace_back(front); - } else { - dq->push_back(front); - } -} - -class QuicCircularDequeTest : public QuicTest {}; - -TEST_F(QuicCircularDequeTest, Empty) { - QuicCircularDeque<int> dq; - EXPECT_TRUE(dq.empty()); - EXPECT_EQ(0u, dq.size()); - dq.clear(); - dq.push_back(10); - EXPECT_FALSE(dq.empty()); - EXPECT_EQ(1u, dq.size()); - EXPECT_EQ(10, dq.front()); - EXPECT_EQ(10, dq.back()); - dq.pop_front(); - EXPECT_TRUE(dq.empty()); - EXPECT_EQ(0u, dq.size()); - - EXPECT_QUIC_DEBUG_DEATH(dq.front(), ""); - EXPECT_QUIC_DEBUG_DEATH(dq.back(), ""); - EXPECT_QUIC_DEBUG_DEATH(dq.at(0), ""); - EXPECT_QUIC_DEBUG_DEATH(dq[0], ""); -} - -TEST_F(QuicCircularDequeTest, Constructor) { - QuicCircularDeque<int> dq; - EXPECT_TRUE(dq.empty()); - - std::allocator<int> alloc; - QuicCircularDeque<int> dq1(alloc); - EXPECT_TRUE(dq1.empty()); - - QuicCircularDeque<int> dq2(8, 100, alloc); - EXPECT_THAT(dq2, ElementsAre(100, 100, 100, 100, 100, 100, 100, 100)); - - QuicCircularDeque<int> dq3(5, alloc); - EXPECT_THAT(dq3, ElementsAre(0, 0, 0, 0, 0)); - - QuicCircularDeque<int> dq4_rand_iter(dq3.begin(), dq3.end(), alloc); - EXPECT_THAT(dq4_rand_iter, ElementsAre(0, 0, 0, 0, 0)); - EXPECT_EQ(dq4_rand_iter, dq3); - - std::list<int> dq4_src = {4, 4, 4, 4}; - QuicCircularDeque<int> dq4_bidi_iter(dq4_src.begin(), dq4_src.end()); - EXPECT_THAT(dq4_bidi_iter, ElementsAre(4, 4, 4, 4)); - - QuicCircularDeque<int> dq5(dq4_bidi_iter); - EXPECT_THAT(dq5, ElementsAre(4, 4, 4, 4)); - EXPECT_EQ(dq5, dq4_bidi_iter); - - QuicCircularDeque<int> dq6(dq5, alloc); - EXPECT_THAT(dq6, ElementsAre(4, 4, 4, 4)); - EXPECT_EQ(dq6, dq5); - - QuicCircularDeque<int> dq7(std::move(*&dq6)); - EXPECT_THAT(dq7, ElementsAre(4, 4, 4, 4)); - EXPECT_TRUE(dq6.empty()); - - QuicCircularDeque<int> dq8_equal_allocator(std::move(*&dq7), alloc); - EXPECT_THAT(dq8_equal_allocator, ElementsAre(4, 4, 4, 4)); - EXPECT_TRUE(dq7.empty()); - - QuicCircularDeque<int, 3, CountingAllocator<int>> dq8_temp = {5, 6, 7, 8, 9}; - QuicCircularDeque<int, 3, CountingAllocator<int>> dq8_unequal_allocator( - std::move(*&dq8_temp), CountingAllocator<int>()); - EXPECT_THAT(dq8_unequal_allocator, ElementsAre(5, 6, 7, 8, 9)); - EXPECT_TRUE(dq8_temp.empty()); - - QuicCircularDeque<int> dq9({3, 4, 5, 6, 7}, alloc); - EXPECT_THAT(dq9, ElementsAre(3, 4, 5, 6, 7)); -} - -TEST_F(QuicCircularDequeTest, Assign) { - // assign() - QuicCircularDeque<int, 3, CountingAllocator<int>> dq; - dq.assign(7, 1); - EXPECT_THAT(dq, ElementsAre(1, 1, 1, 1, 1, 1, 1)); - EXPECT_EQ(1u, dq.get_allocator().allocate_count()); - - QuicCircularDeque<int, 3, CountingAllocator<int>> dq2; - dq2.assign(dq.begin(), dq.end()); - EXPECT_THAT(dq2, ElementsAre(1, 1, 1, 1, 1, 1, 1)); - EXPECT_EQ(1u, dq2.get_allocator().allocate_count()); - EXPECT_TRUE(std::equal(dq.begin(), dq.end(), dq2.begin(), dq2.end())); - - dq2.assign({2, 2, 2, 2, 2, 2}); - EXPECT_THAT(dq2, ElementsAre(2, 2, 2, 2, 2, 2)); - - // Assign from a non random access iterator. - std::list<int> dq3_src = {3, 3, 3, 3, 3}; - QuicCircularDeque<int, 3, CountingAllocator<int>> dq3; - dq3.assign(dq3_src.begin(), dq3_src.end()); - EXPECT_THAT(dq3, ElementsAre(3, 3, 3, 3, 3)); - EXPECT_LT(1u, dq3.get_allocator().allocate_count()); - - // Copy assignment - dq3 = *&dq3; - EXPECT_THAT(dq3, ElementsAre(3, 3, 3, 3, 3)); - - QuicCircularDeque< - int, 3, - ConfigurableAllocator<int, - /*propagate_on_copy_assignment=*/std::true_type, - /*propagate_on_move_assignment=*/std::true_type, - /*propagate_on_swap=*/std::true_type, - /*equality_result=*/false>> - dq4, dq5; - dq4.assign(dq3.begin(), dq3.end()); - dq5 = dq4; - EXPECT_THAT(dq5, ElementsAre(3, 3, 3, 3, 3)); - - QuicCircularDeque< - int, 3, - ConfigurableAllocator<int, - /*propagate_on_copy_assignment=*/std::false_type, - /*propagate_on_move_assignment=*/std::true_type, - /*propagate_on_swap=*/std::true_type, - /*equality_result=*/true>> - dq6, dq7; - dq6.assign(dq3.begin(), dq3.end()); - dq7 = dq6; - EXPECT_THAT(dq7, ElementsAre(3, 3, 3, 3, 3)); - - // Move assignment - dq3 = std::move(*&dq3); - EXPECT_THAT(dq3, ElementsAre(3, 3, 3, 3, 3)); - - ASSERT_TRUE(decltype( - dq3.get_allocator())::propagate_on_container_move_assignment::value); - decltype(dq3) dq8; - dq8 = std::move(*&dq3); - EXPECT_THAT(dq8, ElementsAre(3, 3, 3, 3, 3)); - EXPECT_TRUE(dq3.empty()); - - QuicCircularDeque< - int, 3, - ConfigurableAllocator<int, - /*propagate_on_copy_assignment=*/std::true_type, - /*propagate_on_move_assignment=*/std::false_type, - /*propagate_on_swap=*/std::true_type, - /*equality_result=*/true>> - dq9, dq10; - dq9.assign(dq8.begin(), dq8.end()); - dq10.assign(dq2.begin(), dq2.end()); - dq9 = std::move(*&dq10); - EXPECT_THAT(dq9, ElementsAre(2, 2, 2, 2, 2, 2)); - EXPECT_TRUE(dq10.empty()); - - QuicCircularDeque< - int, 3, - ConfigurableAllocator<int, - /*propagate_on_copy_assignment=*/std::true_type, - /*propagate_on_move_assignment=*/std::false_type, - /*propagate_on_swap=*/std::true_type, - /*equality_result=*/false>> - dq11, dq12; - dq11.assign(dq8.begin(), dq8.end()); - dq12.assign(dq2.begin(), dq2.end()); - dq11 = std::move(*&dq12); - EXPECT_THAT(dq11, ElementsAre(2, 2, 2, 2, 2, 2)); - EXPECT_TRUE(dq12.empty()); -} - -TEST_F(QuicCircularDequeTest, Access) { - // at() - // operator[] - // front() - // back() - - QuicCircularDeque<int, 3, CountingAllocator<int>> dq; - dq.push_back(10); - EXPECT_EQ(dq.front(), 10); - EXPECT_EQ(dq.back(), 10); - EXPECT_EQ(dq.at(0), 10); - EXPECT_EQ(dq[0], 10); - dq.front() = 12; - EXPECT_EQ(dq.front(), 12); - EXPECT_EQ(dq.back(), 12); - EXPECT_EQ(dq.at(0), 12); - EXPECT_EQ(dq[0], 12); - - const auto& dqref = dq; - EXPECT_EQ(dqref.front(), 12); - EXPECT_EQ(dqref.back(), 12); - EXPECT_EQ(dqref.at(0), 12); - EXPECT_EQ(dqref[0], 12); - - dq.pop_front(); - EXPECT_TRUE(dqref.empty()); - - // Push to capacity. - dq.push_back(15); - dq.push_front(5); - dq.push_back(25); - EXPECT_EQ(dq.size(), dq.capacity()); - EXPECT_THAT(dq, ElementsAre(5, 15, 25)); - EXPECT_LT(&dq.front(), &dq.back()); - EXPECT_EQ(dq.front(), 5); - EXPECT_EQ(dq.back(), 25); - EXPECT_EQ(dq.at(0), 5); - EXPECT_EQ(dq.at(1), 15); - EXPECT_EQ(dq.at(2), 25); - EXPECT_EQ(dq[0], 5); - EXPECT_EQ(dq[1], 15); - EXPECT_EQ(dq[2], 25); - - // Shift right such that begin=1 and end=0. Data is still not wrapped. - dq.pop_front(); - dq.push_back(35); - EXPECT_THAT(dq, ElementsAre(15, 25, 35)); - EXPECT_LT(&dq.front(), &dq.back()); - EXPECT_EQ(dq.front(), 15); - EXPECT_EQ(dq.back(), 35); - EXPECT_EQ(dq.at(0), 15); - EXPECT_EQ(dq.at(1), 25); - EXPECT_EQ(dq.at(2), 35); - EXPECT_EQ(dq[0], 15); - EXPECT_EQ(dq[1], 25); - EXPECT_EQ(dq[2], 35); - - // Shift right such that data is wrapped. - dq.pop_front(); - dq.push_back(45); - EXPECT_THAT(dq, ElementsAre(25, 35, 45)); - EXPECT_GT(&dq.front(), &dq.back()); - EXPECT_EQ(dq.front(), 25); - EXPECT_EQ(dq.back(), 45); - EXPECT_EQ(dq.at(0), 25); - EXPECT_EQ(dq.at(1), 35); - EXPECT_EQ(dq.at(2), 45); - EXPECT_EQ(dq[0], 25); - EXPECT_EQ(dq[1], 35); - EXPECT_EQ(dq[2], 45); - - // Shift right again, data is still wrapped. - dq.pop_front(); - dq.push_back(55); - EXPECT_THAT(dq, ElementsAre(35, 45, 55)); - EXPECT_GT(&dq.front(), &dq.back()); - EXPECT_EQ(dq.front(), 35); - EXPECT_EQ(dq.back(), 55); - EXPECT_EQ(dq.at(0), 35); - EXPECT_EQ(dq.at(1), 45); - EXPECT_EQ(dq.at(2), 55); - EXPECT_EQ(dq[0], 35); - EXPECT_EQ(dq[1], 45); - EXPECT_EQ(dq[2], 55); - - // Shift right one last time. begin returns to 0. Data is no longer wrapped. - dq.pop_front(); - dq.push_back(65); - EXPECT_THAT(dq, ElementsAre(45, 55, 65)); - EXPECT_LT(&dq.front(), &dq.back()); - EXPECT_EQ(dq.front(), 45); - EXPECT_EQ(dq.back(), 65); - EXPECT_EQ(dq.at(0), 45); - EXPECT_EQ(dq.at(1), 55); - EXPECT_EQ(dq.at(2), 65); - EXPECT_EQ(dq[0], 45); - EXPECT_EQ(dq[1], 55); - EXPECT_EQ(dq[2], 65); - - EXPECT_EQ(1u, dq.get_allocator().allocate_count()); -} - -TEST_F(QuicCircularDequeTest, Iterate) { - QuicCircularDeque<int> dq; - EXPECT_EQ(dq.begin(), dq.end()); - EXPECT_EQ(dq.cbegin(), dq.cend()); - EXPECT_EQ(dq.rbegin(), dq.rend()); - EXPECT_EQ(dq.crbegin(), dq.crend()); - - dq.emplace_back(2); - QuicCircularDeque<int>::const_iterator citer = dq.begin(); - EXPECT_NE(citer, dq.end()); - EXPECT_EQ(*citer, 2); - ++citer; - EXPECT_EQ(citer, dq.end()); - - EXPECT_EQ(*dq.begin(), 2); - EXPECT_EQ(*dq.cbegin(), 2); - EXPECT_EQ(*dq.rbegin(), 2); - EXPECT_EQ(*dq.crbegin(), 2); - - dq.emplace_front(1); - QuicCircularDeque<int>::const_reverse_iterator criter = dq.rbegin(); - EXPECT_NE(criter, dq.rend()); - EXPECT_EQ(*criter, 2); - ++criter; - EXPECT_NE(criter, dq.rend()); - EXPECT_EQ(*criter, 1); - ++criter; - EXPECT_EQ(criter, dq.rend()); - - EXPECT_EQ(*dq.begin(), 1); - EXPECT_EQ(*dq.cbegin(), 1); - EXPECT_EQ(*dq.rbegin(), 2); - EXPECT_EQ(*dq.crbegin(), 2); - - dq.push_back(3); - - // Forward iterate. - int expected_value = 1; - for (QuicCircularDeque<int>::iterator it = dq.begin(); it != dq.end(); ++it) { - EXPECT_EQ(expected_value++, *it); - } - - expected_value = 1; - for (QuicCircularDeque<int>::const_iterator it = dq.cbegin(); it != dq.cend(); - ++it) { - EXPECT_EQ(expected_value++, *it); - } - - // Reverse iterate. - expected_value = 3; - for (QuicCircularDeque<int>::reverse_iterator it = dq.rbegin(); - it != dq.rend(); ++it) { - EXPECT_EQ(expected_value--, *it); - } - - expected_value = 3; - for (QuicCircularDeque<int>::const_reverse_iterator it = dq.crbegin(); - it != dq.crend(); ++it) { - EXPECT_EQ(expected_value--, *it); - } -} - -TEST_F(QuicCircularDequeTest, Iterator) { - // Default constructed iterators of the same type compare equal. - EXPECT_EQ(QuicCircularDeque<int>::iterator(), - QuicCircularDeque<int>::iterator()); - EXPECT_EQ(QuicCircularDeque<int>::const_iterator(), - QuicCircularDeque<int>::const_iterator()); - EXPECT_EQ(QuicCircularDeque<int>::reverse_iterator(), - QuicCircularDeque<int>::reverse_iterator()); - EXPECT_EQ(QuicCircularDeque<int>::const_reverse_iterator(), - QuicCircularDeque<int>::const_reverse_iterator()); - - QuicCircularDeque<QuicCircularDeque<int>, 3> dqdq = { - {1, 2}, {10, 20, 30}, {100, 200, 300, 400}}; - - // iter points to {1, 2} - decltype(dqdq)::iterator iter = dqdq.begin(); - EXPECT_EQ(iter->size(), 2u); - EXPECT_THAT(*iter, ElementsAre(1, 2)); - - // citer points to {10, 20, 30} - decltype(dqdq)::const_iterator citer = dqdq.cbegin() + 1; - EXPECT_NE(*iter, *citer); - EXPECT_EQ(citer->size(), 3u); - int x = 10; - for (auto it = citer->begin(); it != citer->end(); ++it) { - EXPECT_EQ(*it, x); - x += 10; - } - - EXPECT_LT(iter, citer); - EXPECT_LE(iter, iter); - EXPECT_GT(citer, iter); - EXPECT_GE(citer, citer); - - // iter points to {100, 200, 300, 400} - iter += 2; - EXPECT_NE(*iter, *citer); - EXPECT_EQ(iter->size(), 4u); - for (int i = 1; i <= 4; ++i) { - EXPECT_EQ(iter->begin()[i - 1], i * 100); - } - - EXPECT_LT(citer, iter); - EXPECT_LE(iter, iter); - EXPECT_GT(iter, citer); - EXPECT_GE(citer, citer); - - // iter points to {10, 20, 30}. (same as citer) - iter -= 1; - EXPECT_EQ(*iter, *citer); - EXPECT_EQ(iter->size(), 3u); - x = 10; - for (auto it = iter->begin(); it != iter->end();) { - EXPECT_EQ(*(it++), x); - x += 10; - } - x = 30; - for (auto it = iter->begin() + 2; it != iter->begin();) { - EXPECT_EQ(*(it--), x); - x -= 10; - } -} - -TEST_F(QuicCircularDequeTest, Resize) { - QuicCircularDeque<int, 3, CountingAllocator<int>> dq; - dq.resize(8); - EXPECT_THAT(dq, ElementsAre(0, 0, 0, 0, 0, 0, 0, 0)); - EXPECT_EQ(1u, dq.get_allocator().allocate_count()); - - dq.resize(10, 5); - EXPECT_THAT(dq, ElementsAre(0, 0, 0, 0, 0, 0, 0, 0, 5, 5)); - - QuicCircularDeque<int, 3, CountingAllocator<int>> dq2 = dq; - - for (size_t new_size = dq.size(); new_size != 0; --new_size) { - dq.resize(new_size); - EXPECT_TRUE( - std::equal(dq.begin(), dq.end(), dq2.begin(), dq2.begin() + new_size)); - } - - dq.resize(0); - EXPECT_TRUE(dq.empty()); - - // Resize when data is wrapped. - ASSERT_EQ(dq2.size(), dq2.capacity()); - while (dq2.size() < dq2.capacity()) { - dq2.push_back(5); - } - - // Shift left once such that data is wrapped. - ASSERT_LT(&dq2.front(), &dq2.back()); - dq2.pop_back(); - dq2.push_front(-5); - ASSERT_GT(&dq2.front(), &dq2.back()); - - EXPECT_EQ(-5, dq2.front()); - EXPECT_EQ(5, dq2.back()); - dq2.resize(dq2.size() + 1, 10); - - // Data should be unwrapped after the resize. - ASSERT_LT(&dq2.front(), &dq2.back()); - EXPECT_EQ(-5, dq2.front()); - EXPECT_EQ(10, dq2.back()); - EXPECT_EQ(5, *(dq2.rbegin() + 1)); -} - -namespace { -class Foo { - public: - Foo() : Foo(0xF00) {} - - explicit Foo(int i) : i_(new int(i)) {} - - ~Foo() { - if (i_ != nullptr) { - delete i_; - // Do not set i_ to nullptr such that if the container calls destructor - // multiple times, asan can detect it. - } - } - - Foo(const Foo& other) : i_(new int(*other.i_)) {} - - Foo(Foo&& other) = delete; - - void Set(int i) { *i_ = i; } - - int i() const { return *i_; } - - friend bool operator==(const Foo& lhs, const Foo& rhs) { - return lhs.i() == rhs.i(); - } - - friend std::ostream& operator<<(std::ostream& os, const Foo& foo) { - return os << "Foo(" << foo.i() << ")"; - } - - private: - // By pointing i_ to a dynamically allocated integer, a memory leak will be - // reported if the container forget to properly destruct this object. - int* i_ = nullptr; -}; -} // namespace - -TEST_F(QuicCircularDequeTest, RelocateNonTriviallyCopyable) { - // When relocating non-trivially-copyable objects: - // - Move constructor is preferred, if available. - // - Copy constructor is used otherwise. - - { - // Move construct in Relocate. - using MoveConstructible = std::unique_ptr<Foo>; - ASSERT_FALSE(std::is_trivially_copyable<MoveConstructible>::value); - ASSERT_TRUE(std::is_move_constructible<MoveConstructible>::value); - QuicCircularDeque<MoveConstructible, 3, - CountingAllocator<MoveConstructible>> - dq1; - dq1.resize(3); - EXPECT_EQ(dq1.size(), dq1.capacity()); - EXPECT_EQ(1u, dq1.get_allocator().allocate_count()); - - dq1.emplace_back(new Foo(0xF1)); // Cause existing elements to relocate. - EXPECT_EQ(4u, dq1.size()); - EXPECT_EQ(2u, dq1.get_allocator().allocate_count()); - EXPECT_EQ(dq1[0], nullptr); - EXPECT_EQ(dq1[1], nullptr); - EXPECT_EQ(dq1[2], nullptr); - EXPECT_EQ(dq1[3]->i(), 0xF1); - } - - { - // Copy construct in Relocate. - using NonMoveConstructible = Foo; - ASSERT_FALSE(std::is_trivially_copyable<NonMoveConstructible>::value); - ASSERT_FALSE(std::is_move_constructible<NonMoveConstructible>::value); - QuicCircularDeque<NonMoveConstructible, 3, - CountingAllocator<NonMoveConstructible>> - dq2; - dq2.resize(3); - EXPECT_EQ(dq2.size(), dq2.capacity()); - EXPECT_EQ(1u, dq2.get_allocator().allocate_count()); - - dq2.emplace_back(0xF1); // Cause existing elements to relocate. - EXPECT_EQ(4u, dq2.size()); - EXPECT_EQ(2u, dq2.get_allocator().allocate_count()); - EXPECT_EQ(dq2[0].i(), 0xF00); - EXPECT_EQ(dq2[1].i(), 0xF00); - EXPECT_EQ(dq2[2].i(), 0xF00); - EXPECT_EQ(dq2[3].i(), 0xF1); - } -} - -TEST_F(QuicCircularDequeTest, PushPop) { - // (push|pop|emplace)_(back|front) - - { - QuicCircularDeque<Foo, 4, CountingAllocator<Foo>> dq(4); - for (size_t i = 0; i < dq.size(); ++i) { - dq[i].Set(i + 1); - } - QUIC_LOG(INFO) << "dq initialized to " << dq; - EXPECT_THAT(dq, ElementsAre(Foo(1), Foo(2), Foo(3), Foo(4))); - - ShiftLeft(&dq, false); - QUIC_LOG(INFO) << "shift left once : " << dq; - EXPECT_THAT(dq, ElementsAre(Foo(2), Foo(3), Foo(4), Foo(1))); - - ShiftLeft(&dq, true); - QUIC_LOG(INFO) << "shift left twice: " << dq; - EXPECT_THAT(dq, ElementsAre(Foo(3), Foo(4), Foo(1), Foo(2))); - ASSERT_GT(&dq.front(), &dq.back()); - // dq destructs with wrapped data. - } - - { - QuicCircularDeque<Foo, 4, CountingAllocator<Foo>> dq1(4); - for (size_t i = 0; i < dq1.size(); ++i) { - dq1[i].Set(i + 1); - } - QUIC_LOG(INFO) << "dq1 initialized to " << dq1; - EXPECT_THAT(dq1, ElementsAre(Foo(1), Foo(2), Foo(3), Foo(4))); - - ShiftRight(&dq1, false); - QUIC_LOG(INFO) << "shift right once : " << dq1; - EXPECT_THAT(dq1, ElementsAre(Foo(4), Foo(1), Foo(2), Foo(3))); - - ShiftRight(&dq1, true); - QUIC_LOG(INFO) << "shift right twice: " << dq1; - EXPECT_THAT(dq1, ElementsAre(Foo(3), Foo(4), Foo(1), Foo(2))); - ASSERT_GT(&dq1.front(), &dq1.back()); - // dq1 destructs with wrapped data. - } - - { // Pop n elements from front. - QuicCircularDeque<Foo, 4, CountingAllocator<Foo>> dq2(5); - for (size_t i = 0; i < dq2.size(); ++i) { - dq2[i].Set(i + 1); - } - EXPECT_THAT(dq2, ElementsAre(Foo(1), Foo(2), Foo(3), Foo(4), Foo(5))); - - EXPECT_EQ(2u, dq2.pop_front_n(2)); - EXPECT_THAT(dq2, ElementsAre(Foo(3), Foo(4), Foo(5))); - - EXPECT_EQ(3u, dq2.pop_front_n(100)); - EXPECT_TRUE(dq2.empty()); - } - - { // Pop n elements from back. - QuicCircularDeque<Foo, 4, CountingAllocator<Foo>> dq3(6); - for (size_t i = 0; i < dq3.size(); ++i) { - dq3[i].Set(i + 1); - } - EXPECT_THAT(dq3, - ElementsAre(Foo(1), Foo(2), Foo(3), Foo(4), Foo(5), Foo(6))); - - ShiftRight(&dq3, true); - ShiftRight(&dq3, true); - ShiftRight(&dq3, true); - EXPECT_THAT(dq3, - ElementsAre(Foo(4), Foo(5), Foo(6), Foo(1), Foo(2), Foo(3))); - - EXPECT_EQ(2u, dq3.pop_back_n(2)); - EXPECT_THAT(dq3, ElementsAre(Foo(4), Foo(5), Foo(6), Foo(1))); - - EXPECT_EQ(2u, dq3.pop_back_n(2)); - EXPECT_THAT(dq3, ElementsAre(Foo(4), Foo(5))); - } -} - -TEST_F(QuicCircularDequeTest, Allocation) { - CountingAllocator<int> alloc; - - { - QuicCircularDeque<int, 3, CountingAllocator<int>> dq(alloc); - EXPECT_EQ(alloc, dq.get_allocator()); - EXPECT_EQ(0u, dq.size()); - EXPECT_EQ(0u, dq.capacity()); - EXPECT_EQ(0u, alloc.allocate_count()); - EXPECT_EQ(0u, alloc.deallocate_count()); - - for (int i = 1; i <= 18; ++i) { - SCOPED_TRACE(testing::Message() - << "i=" << i << ", capacity_b4_push=" << dq.capacity()); - dq.push_back(i); - EXPECT_EQ(i, static_cast<int>(dq.size())); - - const size_t capacity = 3 + (i - 1) / 3 * 3; - EXPECT_EQ(capacity, dq.capacity()); - EXPECT_EQ(capacity / 3, alloc.allocate_count()); - EXPECT_EQ(capacity / 3 - 1, alloc.deallocate_count()); - } - - dq.push_back(19); - EXPECT_EQ(22u, dq.capacity()); // 18 + 18 / 4 - EXPECT_EQ(7u, alloc.allocate_count()); - EXPECT_EQ(6u, alloc.deallocate_count()); - } - - EXPECT_EQ(7u, alloc.deallocate_count()); -} - -} // namespace -} // namespace test -} // namespace quic - -// Use a non-quic namespace to make sure swap can be used via ADL. -namespace { - -template <typename T> -using SwappableAllocator = quic::test::ConfigurableAllocator< - T, - /*propagate_on_copy_assignment=*/std::true_type, - /*propagate_on_move_assignment=*/std::true_type, - /*propagate_on_swap=*/std::true_type, - /*equality_result=*/true>; - -template <typename T> -using UnswappableEqualAllocator = quic::test::ConfigurableAllocator< - T, - /*propagate_on_copy_assignment=*/std::true_type, - /*propagate_on_move_assignment=*/std::true_type, - /*propagate_on_swap=*/std::false_type, - /*equality_result=*/true>; - -template <typename T> -using UnswappableUnequalAllocator = quic::test::ConfigurableAllocator< - T, - /*propagate_on_copy_assignment=*/std::true_type, - /*propagate_on_move_assignment=*/std::true_type, - /*propagate_on_swap=*/std::false_type, - /*equality_result=*/false>; - -using quic::test::QuicCircularDequeTest; - -TEST_F(QuicCircularDequeTest, Swap) { - using std::swap; - - quic::QuicCircularDeque<int64_t, 3, SwappableAllocator<int64_t>> dq1, dq2; - dq1.push_back(10); - dq1.push_back(11); - dq2.push_back(20); - swap(dq1, dq2); - EXPECT_THAT(dq1, ElementsAre(20)); - EXPECT_THAT(dq2, ElementsAre(10, 11)); - - quic::QuicCircularDeque<char, 3, UnswappableEqualAllocator<char>> dq3, dq4; - dq3 = {1, 2, 3, 4, 5}; - dq4 = {6, 7, 8, 9, 0}; - swap(dq3, dq4); - EXPECT_THAT(dq3, ElementsAre(6, 7, 8, 9, 0)); - EXPECT_THAT(dq4, ElementsAre(1, 2, 3, 4, 5)); - - quic::QuicCircularDeque<int, 3, UnswappableUnequalAllocator<int>> dq5, dq6; - dq6.push_front(4); - - // Using UnswappableUnequalAllocator is ok as long as swap is not called. - dq5.assign(dq6.begin(), dq6.end()); - EXPECT_THAT(dq5, ElementsAre(4)); - - // Undefined behavior to swap between two containers with unequal allocators. - EXPECT_QUIC_DEBUG_DEATH(swap(dq5, dq6), "Undefined swap behavior"); -} -} // namespace diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection.cc index 9bd21403263..09806f5a8a8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection.cc @@ -48,7 +48,7 @@ #include "quic/platform/api/quic_map_util.h" #include "quic/platform/api/quic_server_stats.h" #include "quic/platform/api/quic_socket_address.h" -#include "common/platform/api/quiche_text_utils.h" +#include "common/quiche_text_utils.h" namespace quic { @@ -225,8 +225,8 @@ class ScopedCoalescedPacketClearer { }; // Whether this incoming packet is allowed to replace our connection ID. -bool PacketCanReplaceConnectionId(const QuicPacketHeader& header, - Perspective perspective) { +bool PacketCanReplaceServerConnectionId(const QuicPacketHeader& header, + Perspective perspective) { return perspective == Perspective::IS_CLIENT && header.form == IETF_QUIC_LONG_HEADER_PACKET && header.version.IsKnown() && @@ -282,7 +282,12 @@ QuicConnection::QuicConnection( client_connection_id_(EmptyQuicConnectionId()), client_connection_id_is_set_(false), direct_peer_address_(initial_peer_address), - default_path_(initial_self_address, QuicSocketAddress()), + default_path_(initial_self_address, + QuicSocketAddress(), + /*client_connection_id=*/EmptyQuicConnectionId(), + server_connection_id, + /*stateless_reset_token_received=*/false, + /*stateless_reset_token=*/{}), active_effective_peer_migration_type_(NO_CHANGE), support_key_update_for_connection_(false), last_packet_decrypted_(false), @@ -329,7 +334,7 @@ QuicConnection::QuicConnection( &arena_)), visitor_(nullptr), debug_visitor_(nullptr), - packet_creator_(server_connection_id_, &framer_, random_generator_, this), + packet_creator_(server_connection_id, &framer_, random_generator_, this), time_of_last_received_packet_(clock_->ApproximateNow()), sent_packet_manager_(perspective, clock_, @@ -370,11 +375,7 @@ QuicConnection::QuicConnection( encrypted_control_frames_ && GetQuicReloadableFlag(quic_use_encryption_level_context)), path_validator_(alarm_factory_, &arena_, this, random_generator_), - alternative_path_(QuicSocketAddress(), QuicSocketAddress()), most_recent_frame_type_(NUM_FRAME_TYPES) { - QUIC_BUG_IF(quic_bug_12714_1, - !start_peer_migration_earlier_ && send_path_response_); - QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT || default_path_.self_address.IsInitialized()); @@ -384,12 +385,10 @@ QuicConnection::QuicConnection( support_multiple_connection_ids_ = version().HasIetfQuicFrames() && - framer_.do_not_synthesize_source_cid_for_short_header() && - GetQuicRestartFlag(quic_use_reference_counted_sesssion_map) && GetQuicRestartFlag(quic_time_wait_list_support_multiple_cid_v2) && GetQuicRestartFlag( quic_dispatcher_support_multiple_cid_per_connection_v2) && - GetQuicReloadableFlag(quic_connection_support_multiple_cids_v2); + GetQuicReloadableFlag(quic_connection_support_multiple_cids_v4); QUIC_DLOG(INFO) << ENDPOINT << "Created connection with server connection ID " << server_connection_id @@ -418,7 +417,7 @@ QuicConnection::QuicConnection( MaybeEnableMultiplePacketNumberSpacesSupport(); QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT || supported_versions.size() == 1); - InstallInitialCrypters(server_connection_id_); + InstallInitialCrypters(ServerConnectionId()); // On the server side, version negotiation has been done by the dispatcher, // and the server connection is created with the right version. @@ -449,6 +448,7 @@ void QuicConnection::InstallInitialCrypters(QuicConnectionId connection_id) { } QuicConnection::~QuicConnection() { + QUICHE_DCHECK_GE(stats_.max_egress_mtu, long_term_mtu_); if (owns_writer_) { delete writer_; } @@ -482,9 +482,9 @@ bool QuicConnection::ValidateConfigConnectionIds(const QuicConfig& config) { // Validate initial_source_connection_id. QuicConnectionId expected_initial_source_connection_id; if (perspective_ == Perspective::IS_CLIENT) { - expected_initial_source_connection_id = server_connection_id_; + expected_initial_source_connection_id = ServerConnectionId(); } else { - expected_initial_source_connection_id = client_connection_id_; + expected_initial_source_connection_id = ClientConnectionId(); } if (!config.HasReceivedInitialSourceConnectionId() || config.ReceivedInitialSourceConnectionId() != @@ -585,6 +585,9 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { } else { SetNetworkTimeouts(config.max_time_before_crypto_handshake(), config.max_idle_time_before_crypto_handshake()); + if (config.HasClientRequestedIndependentOption(kCHSP, perspective_)) { + packet_creator_.set_chaos_protection_enabled(true); + } } if (support_multiple_connection_ids_ && @@ -679,8 +682,14 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { no_stop_waiting_frames_ = true; } if (config.HasReceivedStatelessResetToken()) { - stateless_reset_token_received_ = true; - received_stateless_reset_token_ = config.ReceivedStatelessResetToken(); + if (use_connection_id_on_default_path_) { + default_path_.stateless_reset_token_received = true; + default_path_.stateless_reset_token = + config.ReceivedStatelessResetToken(); + } else { + stateless_reset_token_received_ = true; + received_stateless_reset_token_ = config.ReceivedStatelessResetToken(); + } } if (config.HasReceivedAckDelayExponent()) { framer_.set_peer_ack_delay_exponent(config.ReceivedAckDelayExponent()); @@ -704,6 +713,17 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path3, 6, 6); validate_client_addresses_ = true; } + // Having connection_migration_use_new_cid_ depends on the same set of flags + // and connection option on both client and server sides has the advantage of: + // 1) Less chance of skew in using new connection ID or not between client + // and server in unit tests with random flag combinations. + // 2) Client side's rollout can be protected by the same connection option. + connection_migration_use_new_cid_ = + support_multiple_connection_ids_ && validate_client_addresses_ && + use_connection_id_on_default_path_ && + group_path_response_and_challenge_sending_closer_ && + GetQuicReloadableFlag(quic_drop_unsent_path_response) && + GetQuicReloadableFlag(quic_connection_migration_use_new_cid_v2); if (config.HasReceivedMaxPacketSize()) { peer_max_packet_size_ = config.ReceivedMaxPacketSize(); MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); @@ -865,7 +885,7 @@ void QuicConnection::OnPublicResetPacket(const QuicPublicResetPacket& packet) { // Check that any public reset packet with a different connection ID that was // routed to this QuicConnection has been redirected before control reaches // here. (Check for a bug regression.) - QUICHE_DCHECK_EQ(server_connection_id_, packet.connection_id); + QUICHE_DCHECK_EQ(ServerConnectionId(), packet.connection_id); QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT); QUICHE_DCHECK(!version().HasIetfInvariantHeader()); if (debug_visitor_ != nullptr) { @@ -903,7 +923,7 @@ void QuicConnection::OnVersionNegotiationPacket( // Check that any public reset packet with a different connection ID that was // routed to this QuicConnection has been redirected before control reaches // here. (Check for a bug regression.) - QUICHE_DCHECK_EQ(server_connection_id_, packet.connection_id); + QUICHE_DCHECK_EQ(ServerConnectionId(), packet.connection_id); if (perspective_ == Perspective::IS_SERVER) { const std::string error_details = "Server received version negotiation packet."; @@ -954,17 +974,17 @@ void QuicConnection::OnRetryPacket(QuicConnectionId original_connection_id, absl::string_view retry_without_tag) { QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective_); if (version().UsesTls()) { - if (!CryptoUtils::ValidateRetryIntegrityTag( - version(), server_connection_id_, retry_without_tag, - retry_integrity_tag)) { + if (!CryptoUtils::ValidateRetryIntegrityTag(version(), ServerConnectionId(), + retry_without_tag, + retry_integrity_tag)) { QUIC_DLOG(ERROR) << "Ignoring RETRY with invalid integrity tag"; return; } } else { - if (original_connection_id != server_connection_id_) { + if (original_connection_id != ServerConnectionId()) { QUIC_DLOG(ERROR) << "Ignoring RETRY with original connection ID " << original_connection_id << " not matching expected " - << server_connection_id_ << " token " + << ServerConnectionId() << " token " << absl::BytesToHexString(retry_token); return; } @@ -972,10 +992,10 @@ void QuicConnection::OnRetryPacket(QuicConnectionId original_connection_id, framer_.set_drop_incoming_retry_packets(true); stats_.retry_packet_processed = true; QUIC_DLOG(INFO) << "Received RETRY, replacing connection ID " - << server_connection_id_ << " with " << new_connection_id + << ServerConnectionId() << " with " << new_connection_id << ", received token " << absl::BytesToHexString(retry_token); if (!original_destination_connection_id_.has_value()) { - original_destination_connection_id_ = server_connection_id_; + original_destination_connection_id_ = ServerConnectionId(); } QUICHE_DCHECK(!retry_source_connection_id_.has_value()) << retry_source_connection_id_.value(); @@ -984,12 +1004,18 @@ void QuicConnection::OnRetryPacket(QuicConnectionId original_connection_id, packet_creator_.SetRetryToken(retry_token); // Reinstall initial crypters because the connection ID changed. - InstallInitialCrypters(server_connection_id_); + InstallInitialCrypters(ServerConnectionId()); sent_packet_manager_.MarkInitialPacketsForRetransmission(); } -bool QuicConnection::HasIncomingConnectionId(QuicConnectionId connection_id) { +bool QuicConnection::HasIncomingConnectionId( + QuicConnectionId connection_id) const { + if (quic_deprecate_incoming_connection_ids_) { + QUIC_RELOADABLE_FLAG_COUNT(quic_deprecate_incoming_connection_ids); + // TODO(haoyuewang) Inline this after the flag is deprecated. + return connection_id == original_destination_connection_id_; + } for (QuicConnectionId const& incoming_connection_id : incoming_connection_ids_) { if (incoming_connection_id == connection_id) { @@ -1004,54 +1030,89 @@ void QuicConnection::SetOriginalDestinationConnectionId( QUIC_DLOG(INFO) << "Setting original_destination_connection_id to " << original_destination_connection_id << " on connection with server_connection_id " - << server_connection_id_; - QUICHE_DCHECK_NE(original_destination_connection_id, server_connection_id_); - if (!HasIncomingConnectionId(original_destination_connection_id)) { - incoming_connection_ids_.push_back(original_destination_connection_id); + << ServerConnectionId(); + QUICHE_DCHECK_NE(original_destination_connection_id, ServerConnectionId()); + if (!quic_deprecate_incoming_connection_ids_) { + if (!HasIncomingConnectionId(original_destination_connection_id)) { + incoming_connection_ids_.push_back(original_destination_connection_id); + } } InstallInitialCrypters(original_destination_connection_id); QUICHE_DCHECK(!original_destination_connection_id_.has_value()) << original_destination_connection_id_.value(); original_destination_connection_id_ = original_destination_connection_id; + original_destination_connection_id_replacement_ = ServerConnectionId(); } QuicConnectionId QuicConnection::GetOriginalDestinationConnectionId() { if (original_destination_connection_id_.has_value()) { return original_destination_connection_id_.value(); } - return server_connection_id_; + return ServerConnectionId(); +} + +bool QuicConnection::ValidateServerConnectionId( + const QuicPacketHeader& header) const { + if (perspective_ == Perspective::IS_CLIENT && + header.form == IETF_QUIC_SHORT_HEADER_PACKET) { + return true; + } + + QuicConnectionId server_connection_id = + GetServerConnectionIdAsRecipient(header, perspective_); + + if (server_connection_id == ServerConnectionId() || + HasIncomingConnectionId(server_connection_id)) { + return true; + } + + if (PacketCanReplaceServerConnectionId(header, perspective_)) { + QUIC_DLOG(INFO) << ENDPOINT << "Accepting packet with new connection ID " + << server_connection_id << " instead of " + << ServerConnectionId(); + return true; + } + + if (connection_migration_use_new_cid_ && + perspective_ == Perspective::IS_SERVER && + self_issued_cid_manager_ != nullptr && + self_issued_cid_manager_->IsConnectionIdInUse(server_connection_id)) { + return true; + } + + return false; } bool QuicConnection::OnUnauthenticatedPublicHeader( const QuicPacketHeader& header) { + last_packet_destination_connection_id_ = header.destination_connection_id; + // If last packet destination connection ID is the original server + // connection ID chosen by client, replaces it with the connection ID chosen + // by server. + if (use_connection_id_on_default_path_ && + perspective_ == Perspective::IS_SERVER && + original_destination_connection_id_.has_value() && + last_packet_destination_connection_id_ == + *original_destination_connection_id_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_connection_id_on_default_path_v2, 3, + 3); + last_packet_destination_connection_id_ = + original_destination_connection_id_replacement_; + } + // As soon as we receive an initial we start ignoring subsequent retries. if (header.version_flag && header.long_packet_type == INITIAL) { framer_.set_drop_incoming_retry_packets(true); } - bool skip_server_connection_id_validation = - framer_.do_not_synthesize_source_cid_for_short_header() && - perspective_ == Perspective::IS_CLIENT && - header.form == IETF_QUIC_SHORT_HEADER_PACKET; - - QuicConnectionId server_connection_id = - GetServerConnectionIdAsRecipient(header, perspective_); - - if (!skip_server_connection_id_validation && - server_connection_id != server_connection_id_ && - !HasIncomingConnectionId(server_connection_id)) { - if (PacketCanReplaceConnectionId(header, perspective_)) { - QUIC_DLOG(INFO) << ENDPOINT << "Accepting packet with new connection ID " - << server_connection_id << " instead of " - << server_connection_id_; - return true; - } - + if (!ValidateServerConnectionId(header)) { ++stats_.packets_dropped; + QuicConnectionId server_connection_id = + GetServerConnectionIdAsRecipient(header, perspective_); QUIC_DLOG(INFO) << ENDPOINT << "Ignoring packet from unexpected server connection ID " << server_connection_id << " instead of " - << server_connection_id_; + << ServerConnectionId(); if (debug_visitor_ != nullptr) { debug_visitor_->OnIncorrectConnectionId(server_connection_id); } @@ -1066,18 +1127,15 @@ bool QuicConnection::OnUnauthenticatedPublicHeader( return true; } - if (framer_.do_not_synthesize_source_cid_for_short_header() && - perspective_ == Perspective::IS_SERVER && + if (perspective_ == Perspective::IS_SERVER && header.form == IETF_QUIC_SHORT_HEADER_PACKET) { - QUIC_RELOADABLE_FLAG_COUNT_N( - quic_do_not_synthesize_source_cid_for_short_header, 3, 3); return true; } QuicConnectionId client_connection_id = GetClientConnectionIdAsRecipient(header, perspective_); - if (client_connection_id == client_connection_id_) { + if (client_connection_id == ClientConnectionId()) { return true; } @@ -1089,11 +1147,18 @@ bool QuicConnection::OnUnauthenticatedPublicHeader( return true; } + if (connection_migration_use_new_cid_ && + perspective_ == Perspective::IS_CLIENT && + self_issued_cid_manager_ != nullptr && + self_issued_cid_manager_->IsConnectionIdInUse(client_connection_id)) { + return true; + } + ++stats_.packets_dropped; QUIC_DLOG(INFO) << ENDPOINT << "Ignoring packet from unexpected client connection ID " << client_connection_id << " instead of " - << client_connection_id_; + << ClientConnectionId(); return false; } @@ -1102,17 +1167,8 @@ bool QuicConnection::OnUnauthenticatedHeader(const QuicPacketHeader& header) { debug_visitor_->OnUnauthenticatedHeader(header); } - // Check that any public reset packet with a different connection ID that was - // routed to this QuicConnection has been redirected before control reaches - // here. - QUICHE_DCHECK((framer_.do_not_synthesize_source_cid_for_short_header() && - perspective_ == Perspective::IS_CLIENT && - header.form == IETF_QUIC_SHORT_HEADER_PACKET) || - GetServerConnectionIdAsRecipient(header, perspective_) == - server_connection_id_ || - HasIncomingConnectionId( - GetServerConnectionIdAsRecipient(header, perspective_)) || - PacketCanReplaceConnectionId(header, perspective_)); + // Sanity check on the server connection ID in header. + QUICHE_DCHECK(ValidateServerConnectionId(header)); if (packet_creator_.HasPendingFrames()) { // Incoming packets may change a queued ACK frame. @@ -1264,6 +1320,43 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { default_path_.peer_address, GetEffectivePeerAddressFromCurrentPacket()); + if (connection_migration_use_new_cid_) { + auto effective_peer_address = GetEffectivePeerAddressFromCurrentPacket(); + // Since server does not send new connection ID to client before handshake + // completion and source connection ID is omitted in short header packet, + // the server_connection_id on PathState on the server side does not + // affect the packets server writes after handshake completion. On the + // other hand, it is still desirable to have the "correct" server + // connection ID set on path. + // 1) If client uses 1 unique server connection ID per path and the packet + // is received from an existing path, then + // last_packet_destination_connection_id_ will always be the same as the + // server connection ID on path. Server side will maintain the 1-to-1 + // mapping from server connection ID to path. + // 2) If client uses multiple server connection IDs on the same path, + // compared to the server_connection_id on path, + // last_packet_destination_connection_id_ has the advantage that it is + // still present in the session map since the packet can be routed here + // regardless of packet reordering. + if (IsDefaultPath(last_packet_destination_address_, + effective_peer_address)) { + default_path_.server_connection_id = + last_packet_destination_connection_id_; + } else if (IsAlternativePath(last_packet_destination_address_, + effective_peer_address)) { + alternative_path_.server_connection_id = + last_packet_destination_connection_id_; + } + } + + if (use_connection_id_on_default_path_ && + last_packet_destination_connection_id_ != ServerConnectionId() && + (!original_destination_connection_id_.has_value() || + last_packet_destination_connection_id_ != + *original_destination_connection_id_)) { + QUIC_CODE_COUNT(quic_connection_id_change); + } + QUIC_DLOG_IF(INFO, current_effective_peer_migration_type_ != NO_CHANGE) << ENDPOINT << "Effective peer's ip:port changed from " << default_path_.peer_address.ToString() << " to " @@ -1284,17 +1377,13 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { uber_received_packet_manager_.RecordPacketReceived( last_decrypted_packet_level_, last_header_, idle_network_detector_.time_of_last_received_packet()); - if (GetQuicReloadableFlag(quic_enable_token_based_address_validation)) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_token_based_address_validation, 2, - 2); - if (EnforceAntiAmplificationLimit() && !IsHandshakeConfirmed() && - !header.retry_token.empty() && - visitor_->ValidateToken(header.retry_token)) { - QUIC_DLOG(INFO) << ENDPOINT << "Address validated via token."; - QUIC_CODE_COUNT(quic_address_validated_via_token); - default_path_.validated = true; - stats_.address_validated_via_token = true; - } + if (EnforceAntiAmplificationLimit() && !IsHandshakeConfirmed() && + !header.retry_token.empty() && + visitor_->ValidateToken(header.retry_token)) { + QUIC_DLOG(INFO) << ENDPOINT << "Address validated via token."; + QUIC_CODE_COUNT(quic_address_validated_via_token); + default_path_.validated = true; + stats_.address_validated_via_token = true; } QUICHE_DCHECK(connected_); return true; @@ -1684,8 +1773,12 @@ bool QuicConnection::OnPathChallengeFrame(const QuicPathChallengeFrame& frame) { // PATH_RESPONSE. This context needs to be out of scope before returning. // TODO(danzh) inline OnPathChallengeFrameInternal() once // support_reverse_path_validation_ is deprecated. - QuicPacketCreator::ScopedPeerAddressContext context( - &packet_creator_, last_packet_source_address_); + auto context = + group_path_response_and_challenge_sending_closer_ + ? nullptr + : std::make_unique<QuicPacketCreator::ScopedPeerAddressContext>( + &packet_creator_, last_packet_source_address_, + /*update_connection_id=*/false); if (!OnPathChallengeFrameInternal(frame)) { return false; } @@ -1695,6 +1788,7 @@ bool QuicConnection::OnPathChallengeFrame(const QuicPathChallengeFrame& frame) { bool QuicConnection::OnPathChallengeFrameInternal( const QuicPathChallengeFrame& frame) { + should_proactively_validate_peer_address_on_path_challenge_ = false; // UpdatePacketContent() may start reverse path validation. if (!UpdatePacketContent(PATH_CHALLENGE_FRAME)) { return false; @@ -1703,6 +1797,35 @@ bool QuicConnection::OnPathChallengeFrameInternal( debug_visitor_->OnPathChallengeFrame(frame); } + std::unique_ptr<QuicPacketCreator::ScopedPeerAddressContext> context; + const QuicSocketAddress current_effective_peer_address = + GetEffectivePeerAddressFromCurrentPacket(); + if (group_path_response_and_challenge_sending_closer_) { + QuicConnectionId client_cid, server_cid; + FindOnPathConnectionIds(last_packet_destination_address_, + current_effective_peer_address, &client_cid, + &server_cid); + context = std::make_unique<QuicPacketCreator::ScopedPeerAddressContext>( + &packet_creator_, last_packet_source_address_, client_cid, server_cid, + connection_migration_use_new_cid_); + } + if (should_proactively_validate_peer_address_on_path_challenge_) { + QUIC_RELOADABLE_FLAG_COUNT( + quic_group_path_response_and_challenge_sending_closer); + // Conditions to proactively validate peer address: + // The perspective is server + // The PATH_CHALLENGE is received on an unvalidated alternative path. + // The connection isn't validating migrated peer address, which is of + // higher prority. + QUIC_DVLOG(1) << "Proactively validate the effective peer address " + << current_effective_peer_address; + QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 2, 6); + ValidatePath(std::make_unique<ReversePathValidationContext>( + default_path_.self_address, last_packet_source_address_, + current_effective_peer_address, this), + std::make_unique<ReversePathValidationResultDelegate>( + this, peer_address())); + } if (!send_path_response_) { // Save the path challenge's payload, for later use in generating the // response. @@ -1717,10 +1840,19 @@ bool QuicConnection::OnPathChallengeFrameInternal( // Queue or send PATH_RESPONSE. Send PATH_RESPONSE to the source address of // the current incoming packet, even if it's not the default path or the // alternative path. - if (!SendPathResponse(frame.data_buffer, last_packet_source_address_)) { - // Queue the payloads to re-try later. - pending_path_challenge_payloads_.push_back( - {frame.data_buffer, last_packet_source_address_}); + const bool success = + SendPathResponse(frame.data_buffer, last_packet_source_address_, + current_effective_peer_address); + if (GetQuicReloadableFlag(quic_drop_unsent_path_response)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_drop_unsent_path_response); + } + if (!success) { + QUIC_CODE_COUNT(quic_failed_to_send_path_response); + if (!GetQuicReloadableFlag(quic_drop_unsent_path_response)) { + // Queue the payloads to re-try later. + pending_path_challenge_payloads_.push_back( + {frame.data_buffer, last_packet_source_address_}); + } } // TODO(b/150095588): change the stats to // num_valid_path_challenge_received. @@ -1885,6 +2017,39 @@ bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { return connected_; } +void QuicConnection::OnClientConnectionIdAvailable() { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 3, 5); + QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER); + if (!peer_issued_cid_manager_->HasUnusedConnectionId()) { + return; + } + if (default_path_.client_connection_id.IsEmpty()) { + const QuicConnectionIdData* unused_cid_data = + peer_issued_cid_manager_->ConsumeOneUnusedConnectionId(); + QUIC_DVLOG(1) << ENDPOINT << "Patch connection ID " + << unused_cid_data->connection_id << " to default path"; + default_path_.client_connection_id = unused_cid_data->connection_id; + default_path_.stateless_reset_token_received = true; + default_path_.stateless_reset_token = + unused_cid_data->stateless_reset_token; + QUICHE_DCHECK(!packet_creator_.HasPendingFrames()); + QUICHE_DCHECK(packet_creator_.GetDestinationConnectionId().IsEmpty()); + packet_creator_.SetClientConnectionId(default_path_.client_connection_id); + return; + } + if (alternative_path_.peer_address.IsInitialized() && + alternative_path_.client_connection_id.IsEmpty()) { + const QuicConnectionIdData* unused_cid_data = + peer_issued_cid_manager_->ConsumeOneUnusedConnectionId(); + QUIC_DVLOG(1) << ENDPOINT << "Patch connection ID " + << unused_cid_data->connection_id << " to alternative path"; + alternative_path_.client_connection_id = unused_cid_data->connection_id; + alternative_path_.stateless_reset_token_received = true; + alternative_path_.stateless_reset_token = + unused_cid_data->stateless_reset_token; + } +} + bool QuicConnection::OnNewConnectionIdFrameInner( const QuicNewConnectionIdFrame& frame) { QUICHE_DCHECK(support_multiple_connection_ids_); @@ -1903,7 +2068,11 @@ bool QuicConnection::OnNewConnectionIdFrameInner( ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return false; } - QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_support_multiple_cids_v2, 1, 2); + if (use_connection_id_on_default_path_ && + perspective_ == Perspective::IS_SERVER) { + OnClientConnectionIdAvailable(); + } + QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_support_multiple_cids_v4, 1, 2); return true; } @@ -1939,6 +2108,11 @@ bool QuicConnection::OnRetireConnectionIdFrame( if (debug_visitor_ != nullptr) { debug_visitor_->OnRetireConnectionIdFrame(frame); } + if (use_connection_id_on_default_path_ && + !connection_migration_use_new_cid_) { + // Do not respond to RetireConnectionId frame. + return true; + } if (!support_multiple_connection_ids_) { return true; } @@ -1957,7 +2131,7 @@ bool QuicConnection::OnRetireConnectionIdFrame( ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return false; } - QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_support_multiple_cids_v2, 2, 2); + QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_support_multiple_cids_v4, 2, 2); return true; } @@ -1972,17 +2146,14 @@ bool QuicConnection::OnNewTokenFrame(const QuicNewTokenFrame& frame) { if (debug_visitor_ != nullptr) { debug_visitor_->OnNewTokenFrame(frame); } - if (GetQuicReloadableFlag(quic_enable_token_based_address_validation)) { - if (perspective_ == Perspective::IS_SERVER) { - CloseConnection(QUIC_INVALID_NEW_TOKEN, - "Server received new token frame.", - ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); - return false; - } - // NEW_TOKEN frame should insitgate ACKs. - MaybeUpdateAckTimeout(); - visitor_->OnNewTokenReceived(frame.token); + if (perspective_ == Perspective::IS_SERVER) { + CloseConnection(QUIC_INVALID_NEW_TOKEN, "Server received new token frame.", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; } + // NEW_TOKEN frame should insitgate ACKs. + MaybeUpdateAckTimeout(); + visitor_->OnNewTokenReceived(frame.token); return true; } @@ -2181,21 +2352,14 @@ void QuicConnection::MaybeRespondToConnectivityProbingOrMigration() { return; } } - // Server starts to migrate connection upon receiving of non-probing packet - // from a new peer address. - if (!start_peer_migration_earlier_ && - last_header_.packet_number == GetLargestReceivedPacket()) { - direct_peer_address_ = last_packet_source_address_; - if (current_effective_peer_migration_type_ != NO_CHANGE) { - // TODO(fayang): When multiple packet number spaces is supported, only - // start peer migration for the application data. - StartEffectivePeerMigration(current_effective_peer_migration_type_); - } - } } bool QuicConnection::IsValidStatelessResetToken( const StatelessResetToken& token) const { + if (use_connection_id_on_default_path_) { + return default_path_.stateless_reset_token_received && + token == default_path_.stateless_reset_token; + } return stateless_reset_token_received_ && token == received_stateless_reset_token_; } @@ -2280,23 +2444,13 @@ void QuicConnection::ClearLastFrames() { } void QuicConnection::CloseIfTooManyOutstandingSentPackets() { - bool should_close; - if (GetQuicReloadableFlag( - quic_close_connection_with_too_many_outstanding_packets)) { - QUIC_RELOADABLE_FLAG_COUNT( - quic_close_connection_with_too_many_outstanding_packets); - should_close = - sent_packet_manager_.GetLargestSentPacket().IsInitialized() && - sent_packet_manager_.GetLargestSentPacket() > - sent_packet_manager_.GetLeastUnacked() + max_tracked_packets_; - } else { - should_close = - sent_packet_manager_.GetLargestObserved().IsInitialized() && - sent_packet_manager_.GetLargestObserved() > - sent_packet_manager_.GetLeastUnacked() + max_tracked_packets_; - } // This occurs if we don't discard old packets we've seen fast enough. It's // possible largest observed is less than leaset unacked. + const bool should_close = + sent_packet_manager_.GetLargestSentPacket().IsInitialized() && + sent_packet_manager_.GetLargestSentPacket() > + sent_packet_manager_.GetLeastUnacked() + max_tracked_packets_; + if (should_close) { CloseConnection( QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS, @@ -2414,18 +2568,23 @@ QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id, QuicUtils::IsCryptoStreamId(transport_version(), id)) { MaybeActivateLegacyVersionEncapsulation(); } - if (GetQuicReloadableFlag(quic_preempt_stream_data_with_handshake_packet) && - perspective_ == Perspective::IS_SERVER && + if (perspective_ == Perspective::IS_SERVER && version().CanSendCoalescedPackets() && !IsHandshakeConfirmed()) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_preempt_stream_data_with_handshake_packet, - 1, 2); + if (GetQuicReloadableFlag(quic_donot_pto_half_rtt_data)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_donot_pto_half_rtt_data); + if (in_on_retransmission_time_out_ && + coalesced_packet_.NumberOfPackets() == 0u) { + // PTO fires while handshake is not confirmed. Do not preempt handshake + // data with stream data. + QUIC_CODE_COUNT(quic_try_to_send_half_rtt_data_when_pto_fires); + return QuicConsumedData(0, false); + } + } if (coalesced_packet_.ContainsPacketOfEncryptionLevel(ENCRYPTION_INITIAL) && coalesced_packet_.NumberOfPackets() == 1u) { // Handshake is not confirmed yet, if there is only an initial packet in // the coalescer, try to bundle an ENCRYPTION_HANDSHAKE packet before // sending stream data. - QUIC_RELOADABLE_FLAG_COUNT_N( - quic_preempt_stream_data_with_handshake_packet, 2, 2); sent_packet_manager_.RetransmitDataOfSpaceIfAny(HANDSHAKE_DATA); } } @@ -2510,8 +2669,8 @@ const QuicConnectionStats& QuicConnection::GetStats() { stats_.estimated_bandwidth = sent_packet_manager_.BandwidthEstimate(); sent_packet_manager_.GetSendAlgorithm()->PopulateConnectionStats(&stats_); - stats_.max_packet_size = packet_creator_.max_packet_length(); - stats_.max_received_packet_size = largest_received_packet_size_; + stats_.egress_mtu = long_term_mtu_; + stats_.ingress_mtu = largest_received_packet_size_; return stats_; } @@ -2745,6 +2904,7 @@ void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address, MaybeSendInResponseToPacket(); } SetPingAlarm(); + RetirePeerIssuedConnectionIdsNoLongerOnPath(); current_packet_data_ = nullptr; is_current_packet_connectivity_probing_ = false; } @@ -2789,8 +2949,12 @@ void QuicConnection::OnCanWrite() { QUIC_RELOADABLE_FLAG_COUNT_N(quic_send_path_response2, 4, 5); const PendingPathChallenge& pending_path_challenge = pending_path_challenge_payloads_.front(); + // Note connection_migration_use_cid_ will depends on + // quic_drop_unsent_path_response flag eventually, and hence the empty + // effective_peer_address here will not be used. if (!SendPathResponse(pending_path_challenge.received_path_challenge, - pending_path_challenge.peer_address)) { + pending_path_challenge.peer_address, + /*effective_peer_address=*/QuicSocketAddress())) { break; } pending_path_challenge_payloads_.pop_front(); @@ -2826,6 +2990,17 @@ void QuicConnection::WriteIfNotBlocked() { } } +void QuicConnection::SetServerConnectionId( + const QuicConnectionId& server_connection_id) { + if (use_connection_id_on_default_path_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_connection_id_on_default_path_v2, 2, + 3); + default_path_.server_connection_id = server_connection_id; + } else { + server_connection_id_ = server_connection_id; + } +} + void QuicConnection::ReplaceInitialServerConnectionId( const QuicConnectionId& new_server_connection_id) { QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT); @@ -2836,10 +3011,10 @@ void QuicConnection::ReplaceInitialServerConnectionId( if (peer_issued_cid_manager_ != nullptr) { QUIC_BUG_IF(quic_bug_12714_22, !peer_issued_cid_manager_->IsConnectionIdActive( - server_connection_id_)) + ServerConnectionId())) << "Connection ID replaced header is no longer active. old id: " - << server_connection_id_ << " new_id: " << new_server_connection_id; - peer_issued_cid_manager_->ReplaceConnectionId(server_connection_id_, + << ServerConnectionId() << " new_id: " << new_server_connection_id; + peer_issued_cid_manager_->ReplaceConnectionId(ServerConnectionId(), new_server_connection_id); } else { peer_issued_cid_manager_ = @@ -2849,8 +3024,74 @@ void QuicConnection::ReplaceInitialServerConnectionId( } } } - server_connection_id_ = new_server_connection_id; - packet_creator_.SetServerConnectionId(server_connection_id_); + SetServerConnectionId(new_server_connection_id); + packet_creator_.SetServerConnectionId(ServerConnectionId()); +} + +void QuicConnection::FindMatchingOrNewClientConnectionIdOrToken( + const PathState& default_path, + const PathState& alternative_path, + const QuicConnectionId& server_connection_id, + QuicConnectionId* client_connection_id, + bool* stateless_reset_token_received, + StatelessResetToken* stateless_reset_token) { + if (!use_connection_id_on_default_path_) { + return; + } + QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER); + if (peer_issued_cid_manager_ == nullptr || + server_connection_id == default_path.server_connection_id) { + *client_connection_id = default_path.client_connection_id; + *stateless_reset_token_received = + default_path.stateless_reset_token_received; + *stateless_reset_token = default_path.stateless_reset_token; + return; + } + if (server_connection_id == alternative_path_.server_connection_id) { + *client_connection_id = alternative_path.client_connection_id; + *stateless_reset_token_received = + alternative_path.stateless_reset_token_received; + *stateless_reset_token = alternative_path.stateless_reset_token; + return; + } + if (!connection_migration_use_new_cid_) { + QUIC_BUG(quic_bug_46004) << "Cannot find matching connection ID."; + return; + } + auto* connection_id_data = + peer_issued_cid_manager_->ConsumeOneUnusedConnectionId(); + if (connection_id_data == nullptr) { + return; + } + *client_connection_id = connection_id_data->connection_id; + *stateless_reset_token = connection_id_data->stateless_reset_token; + *stateless_reset_token_received = true; +} + +bool QuicConnection::FindOnPathConnectionIds( + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + QuicConnectionId* client_connection_id, + QuicConnectionId* server_connection_id) const { + if (IsDefaultPath(self_address, peer_address)) { + *client_connection_id = default_path_.client_connection_id, + *server_connection_id = default_path_.server_connection_id; + return true; + } + if (IsAlternativePath(self_address, peer_address)) { + *client_connection_id = alternative_path_.client_connection_id, + *server_connection_id = alternative_path_.server_connection_id; + return true; + } + return false; +} + +void QuicConnection::SetDefaultPathState(PathState new_path_state) { + default_path_ = std::move(new_path_state); + if (connection_migration_use_new_cid_) { + packet_creator_.SetClientConnectionId(default_path_.client_connection_id); + packet_creator_.SetServerConnectionId(default_path_.server_connection_id); + } } bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) { @@ -2885,21 +3126,21 @@ bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) { default_path_.self_address = last_packet_destination_address_; } - if (PacketCanReplaceConnectionId(header, perspective_) && - server_connection_id_ != header.source_connection_id) { + if (PacketCanReplaceServerConnectionId(header, perspective_) && + ServerConnectionId() != header.source_connection_id) { QUICHE_DCHECK_EQ(header.long_packet_type, INITIAL); if (server_connection_id_replaced_by_initial_) { QUIC_DLOG(ERROR) << ENDPOINT << "Refusing to replace connection ID " - << server_connection_id_ << " with " + << ServerConnectionId() << " with " << header.source_connection_id; return false; } server_connection_id_replaced_by_initial_ = true; QUIC_DLOG(INFO) << ENDPOINT << "Replacing connection ID " - << server_connection_id_ << " with " + << ServerConnectionId() << " with " << header.source_connection_id; if (!original_destination_connection_id_.has_value()) { - original_destination_connection_id_ = server_connection_id_; + original_destination_connection_id_ = ServerConnectionId(); } ReplaceInitialServerConnectionId(header.source_connection_id); } @@ -3039,6 +3280,15 @@ bool QuicConnection::ShouldGeneratePacket( QuicVersionUsesCryptoFrames(transport_version())) << ENDPOINT << "Handshake in STREAM frames should not check ShouldGeneratePacket"; + if (support_multiple_connection_ids_ && peer_issued_cid_manager_ != nullptr && + packet_creator_.GetDestinationConnectionId().IsEmpty()) { + QUIC_CODE_COUNT(quic_generate_packet_blocked_by_no_connection_id); + QUIC_BUG_IF(quic_bug_90265_1, perspective_ == Perspective::IS_CLIENT); + QUIC_DLOG(INFO) << ENDPOINT + << "There is no destination connection ID available to " + "generate packet."; + return false; + } if (!count_bytes_on_alternative_path_separately_) { return CanWrite(retransmittable); } @@ -3319,7 +3569,7 @@ bool QuicConnection::WritePacket(SerializedPacket* packet) { legacy_version_encapsulation_sni_, absl::string_view(packet->encrypted_buffer, packet->encrypted_length), - server_connection_id_, framer_.creation_time(), + ServerConnectionId(), framer_.creation_time(), GetLimitedMaxPacketSize(long_term_mtu_), const_cast<char*>(packet->encrypted_buffer)); if (encapsulated_length != 0) { @@ -3514,6 +3764,7 @@ bool QuicConnection::WritePacket(SerializedPacket* packet) { SetRetransmissionAlarm(); } SetPingAlarm(); + RetirePeerIssuedConnectionIdsNoLongerOnPath(); // The packet number length must be updated after OnPacketSent, because it // may change the packet number length in packet. @@ -3827,13 +4078,13 @@ void QuicConnection::OnPathMtuIncreased(QuicPacketLength packet_size) { std::unique_ptr<QuicSelfIssuedConnectionIdManager> QuicConnection::MakeSelfIssuedConnectionIdManager() { QUICHE_DCHECK((perspective_ == Perspective::IS_CLIENT && - !client_connection_id_.IsEmpty()) || + !ClientConnectionId().IsEmpty()) || (perspective_ == Perspective::IS_SERVER && - !server_connection_id_.IsEmpty())); + !ServerConnectionId().IsEmpty())); return std::make_unique<QuicSelfIssuedConnectionIdManager>( kMinNumOfActiveConnectionIds, - perspective_ == Perspective::IS_CLIENT ? client_connection_id_ - : server_connection_id_, + perspective_ == Perspective::IS_CLIENT ? ClientConnectionId() + : ServerConnectionId(), clock_, alarm_factory_, this); } @@ -3847,6 +4098,11 @@ void QuicConnection::MaybeSendConnectionIdToClient() { void QuicConnection::OnHandshakeComplete() { sent_packet_manager_.SetHandshakeConfirmed(); + if (connection_migration_use_new_cid_ && + perspective_ == Perspective::IS_SERVER && + self_issued_cid_manager_ != nullptr) { + self_issued_cid_manager_->MaybeSendNewConnectionIds(); + } if (send_ack_frequency_on_handshake_completion_ && sent_packet_manager_.CanSendAckFrequency()) { QUIC_RELOADABLE_FLAG_COUNT_N(quic_can_send_ack_frequency, 2, 3); @@ -3926,6 +4182,7 @@ void QuicConnection::SendAck() { } void QuicConnection::OnRetransmissionTimeout() { + ScopedRetransmissionTimeoutIndicator indicator(this); #ifndef NDEBUG if (sent_packet_manager_.unacked_packets().empty()) { QUICHE_DCHECK(sent_packet_manager_.handshake_mode_disabled()); @@ -4309,8 +4566,9 @@ void QuicConnection::SendConnectionClosePacket( QuicIetfTransportErrorCodes ietf_error, const std::string& details) { // Always use the current path to send CONNECTION_CLOSE. - QuicPacketCreator::ScopedPeerAddressContext context(&packet_creator_, - peer_address()); + QuicPacketCreator::ScopedPeerAddressContext context( + &packet_creator_, peer_address(), default_path_.client_connection_id, + default_path_.server_connection_id, connection_migration_use_new_cid_); if (!SupportsMultiplePacketNumberSpaces()) { QUIC_DLOG(INFO) << ENDPOINT << "Sending connection close packet."; if (!use_encryption_level_context_) { @@ -4472,6 +4730,7 @@ QuicByteCount QuicConnection::max_packet_length() const { void QuicConnection::SetMaxPacketLength(QuicByteCount length) { long_term_mtu_ = length; + stats_.max_egress_mtu = std::max(stats_.max_egress_mtu, long_term_mtu_); MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); } @@ -4838,7 +5097,7 @@ bool QuicConnection::SendGenericPathProbePacket( QUIC_DLOG(INFO) << ENDPOINT << "Sending path probe packet for connection_id = " - << server_connection_id_; + << ServerConnectionId(); std::unique_ptr<SerializedPacket> probing_packet; if (!version().HasIetfQuicFrames()) { @@ -4883,7 +5142,7 @@ bool QuicConnection::WritePacketUsingWriter( const QuicTime packet_send_time = clock_->Now(); QUIC_DVLOG(2) << ENDPOINT << "Sending path probe packet for server connection ID " - << server_connection_id_ << std::endl + << ServerConnectionId() << std::endl << quiche::QuicheTextUtils::HexDump(absl::string_view( packet->encrypted_buffer, packet->encrypted_length)); WriteResult result = writer->WritePacket( @@ -5083,10 +5342,19 @@ void QuicConnection::StartEffectivePeerMigration(AddressChangeType type) { // Update the default path. if (IsAlternativePath(last_packet_destination_address_, current_effective_peer_address)) { - default_path_ = std::move(alternative_path_); + SetDefaultPathState(std::move(alternative_path_)); } else { - default_path_ = PathState(last_packet_destination_address_, - current_effective_peer_address); + QuicConnectionId client_connection_id; + bool stateless_reset_token_received = false; + StatelessResetToken stateless_reset_token; + FindMatchingOrNewClientConnectionIdOrToken( + previous_default_path, alternative_path_, + last_packet_destination_connection_id_, &client_connection_id, + &stateless_reset_token_received, &stateless_reset_token); + SetDefaultPathState(PathState( + last_packet_destination_address_, current_effective_peer_address, + client_connection_id, last_packet_destination_connection_id_, + stateless_reset_token_received, stateless_reset_token)); // The path is considered validated if its peer IP address matches any // validated path's peer IP address. default_path_.validated = @@ -5293,8 +5561,17 @@ bool QuicConnection::UpdatePacketContent(QuicFrameType type) { << current_effective_peer_address << ", self address " << last_packet_destination_address_; if (!validate_client_addresses_) { - alternative_path_ = PathState(last_packet_destination_address_, - current_effective_peer_address); + QuicConnectionId client_cid; + bool stateless_reset_token_received = false; + StatelessResetToken stateless_reset_token; + FindMatchingOrNewClientConnectionIdOrToken( + default_path_, alternative_path_, + last_packet_destination_connection_id_, &client_cid, + &stateless_reset_token_received, &stateless_reset_token); + alternative_path_ = PathState( + last_packet_destination_address_, current_effective_peer_address, + client_cid, last_packet_destination_connection_id_, + stateless_reset_token_received, stateless_reset_token); } else if (!default_path_.validated) { QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path3, 4, 6); // Skip reverse path validation because either handshake hasn't @@ -5311,24 +5588,38 @@ bool QuicConnection::UpdatePacketContent(QuicFrameType type) { << "No validated peer address to send after handshake comfirmed."; } else if (!IsReceivedPeerAddressValidated()) { QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path3, 5, 6); + QuicConnectionId client_connection_id; + bool stateless_reset_token_received; + StatelessResetToken stateless_reset_token; + FindMatchingOrNewClientConnectionIdOrToken( + default_path_, alternative_path_, + last_packet_destination_connection_id_, &client_connection_id, + &stateless_reset_token_received, &stateless_reset_token); // Only override alternative path state upon receiving a PATH_CHALLENGE // from an unvalidated peer address, and the connection isn't validating // a recent peer migration. - alternative_path_ = PathState(last_packet_destination_address_, - current_effective_peer_address); - // Conditions to proactively validate peer address: - // The perspective is server - // The PATH_CHALLENGE is received on an unvalidated alternative path. - // The connection isn't validating migrated peer address, which is of - // higher prority. - QUIC_DVLOG(1) << "Proactively validate the effective peer address " - << current_effective_peer_address; - ValidatePath( - std::make_unique<ReversePathValidationContext>( - default_path_.self_address, current_effective_peer_address, - current_effective_peer_address, this), - std::make_unique<ReversePathValidationResultDelegate>( - this, peer_address())); + alternative_path_ = PathState( + last_packet_destination_address_, current_effective_peer_address, + client_connection_id, last_packet_destination_connection_id_, + stateless_reset_token_received, stateless_reset_token); + if (group_path_response_and_challenge_sending_closer_) { + should_proactively_validate_peer_address_on_path_challenge_ = true; + } else { + // Conditions to proactively validate peer address: + // The perspective is server + // The PATH_CHALLENGE is received on an unvalidated alternative path. + // The connection isn't validating migrated peer address, which is of + // higher prority. + QUIC_DVLOG(1) << "Proactively validate the effective peer address " + << current_effective_peer_address; + QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 1, 6); + ValidatePath( + std::make_unique<ReversePathValidationContext>( + default_path_.self_address, last_packet_source_address_, + current_effective_peer_address, this), + std::make_unique<ReversePathValidationResultDelegate>( + this, peer_address())); + } } } MaybeUpdateBytesReceivedFromAlternativeAddress(last_size_); @@ -5398,10 +5689,6 @@ bool QuicConnection::UpdatePacketContent(QuicFrameType type) { void QuicConnection::MaybeStartIetfPeerMigration() { QUICHE_DCHECK(version().HasIetfQuicFrames()); - if (!start_peer_migration_earlier_) { - return; - } - QUIC_CODE_COUNT(quic_start_peer_migration_earlier); if (current_effective_peer_migration_type_ != NO_CHANGE && !IsHandshakeConfirmed()) { QUIC_LOG_EVERY_N_SEC(INFO, 60) @@ -5970,14 +6257,21 @@ void QuicConnection::set_client_connection_id( << client_connection_id << " with unsupported version " << version(); return; } - client_connection_id_ = client_connection_id; + if (use_connection_id_on_default_path_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_connection_id_on_default_path_v2, 1, + 3); + default_path_.client_connection_id = client_connection_id; + } else { + client_connection_id_ = client_connection_id; + } + client_connection_id_is_set_ = true; - if (support_multiple_connection_ids_ && !client_connection_id_.IsEmpty()) { + if (support_multiple_connection_ids_ && !client_connection_id.IsEmpty()) { if (perspective_ == Perspective::IS_SERVER) { QUICHE_DCHECK(peer_issued_cid_manager_ == nullptr); peer_issued_cid_manager_ = std::make_unique<QuicPeerIssuedConnectionIdManager>( - kMinNumOfActiveConnectionIds, client_connection_id_, clock_, + kMinNumOfActiveConnectionIds, client_connection_id, clock_, alarm_factory_, this); } else { // Note in Chromium client, set_client_connection_id is not called and @@ -5986,11 +6280,11 @@ void QuicConnection::set_client_connection_id( } } QUIC_DLOG(INFO) << ENDPOINT << "setting client connection ID to " - << client_connection_id_ + << ClientConnectionId() << " for connection with server connection ID " - << server_connection_id_; - packet_creator_.SetClientConnectionId(client_connection_id_); - framer_.SetExpectedClientConnectionIdLength(client_connection_id_.length()); + << ServerConnectionId(); + packet_creator_.SetClientConnectionId(ClientConnectionId()); + framer_.SetExpectedClientConnectionIdLength(ClientConnectionId().length()); } void QuicConnection::OnPathDegradingDetected() { @@ -6069,12 +6363,21 @@ void QuicConnection::OnIdleNetworkDetected() { void QuicConnection::OnPeerIssuedConnectionIdRetired() { QUICHE_DCHECK(peer_issued_cid_manager_ != nullptr); QuicConnectionId* default_path_cid = perspective_ == Perspective::IS_CLIENT - ? &server_connection_id_ - : &client_connection_id_; + ? &ServerConnectionId() + : &ClientConnectionId(); + QuicConnectionId* alternative_path_cid = + perspective_ == Perspective::IS_CLIENT + ? &alternative_path_.server_connection_id + : &alternative_path_.client_connection_id; + bool default_path_and_alternative_path_use_the_same_peer_connection_id = + *default_path_cid == *alternative_path_cid; if (!default_path_cid->IsEmpty() && !peer_issued_cid_manager_->IsConnectionIdActive(*default_path_cid)) { *default_path_cid = QuicConnectionId(); } + // TODO(haoyuewang) Handle the change for default_path_ & alternatvie_path_ + // via the same helper function after use_connection_id_on_default_path_ is + // default true. if (default_path_cid->IsEmpty()) { // Try setting a new connection ID now such that subsequent // RetireConnectionId frames can be sent on the default path. @@ -6082,9 +6385,15 @@ void QuicConnection::OnPeerIssuedConnectionIdRetired() { peer_issued_cid_manager_->ConsumeOneUnusedConnectionId(); if (unused_connection_id_data != nullptr) { *default_path_cid = unused_connection_id_data->connection_id; - received_stateless_reset_token_ = - unused_connection_id_data->stateless_reset_token; - stateless_reset_token_received_ = true; + if (use_connection_id_on_default_path_) { + default_path_.stateless_reset_token = + unused_connection_id_data->stateless_reset_token; + default_path_.stateless_reset_token_received = true; + } else { + received_stateless_reset_token_ = + unused_connection_id_data->stateless_reset_token; + stateless_reset_token_received_ = true; + } if (perspective_ == Perspective::IS_CLIENT) { packet_creator_.SetServerConnectionId( unused_connection_id_data->connection_id); @@ -6094,32 +6403,56 @@ void QuicConnection::OnPeerIssuedConnectionIdRetired() { } } } + if (use_connection_id_on_default_path_) { + if (default_path_and_alternative_path_use_the_same_peer_connection_id) { + *alternative_path_cid = *default_path_cid; + alternative_path_.stateless_reset_token_received = + default_path_.stateless_reset_token_received; + alternative_path_.stateless_reset_token = + default_path_.stateless_reset_token; + } else if (!alternative_path_cid->IsEmpty() && + !peer_issued_cid_manager_->IsConnectionIdActive( + *alternative_path_cid)) { + *alternative_path_cid = EmptyQuicConnectionId(); + const QuicConnectionIdData* unused_connection_id_data = + peer_issued_cid_manager_->ConsumeOneUnusedConnectionId(); + if (unused_connection_id_data != nullptr) { + *alternative_path_cid = unused_connection_id_data->connection_id; + alternative_path_.stateless_reset_token = + unused_connection_id_data->stateless_reset_token; + alternative_path_.stateless_reset_token_received = true; + } + } + } std::vector<uint64_t> retired_cid_sequence_numbers = peer_issued_cid_manager_->ConsumeToBeRetiredConnectionIdSequenceNumbers(); QUICHE_DCHECK(!retired_cid_sequence_numbers.empty()); for (const auto& sequence_number : retired_cid_sequence_numbers) { + ++stats_.num_retire_connection_id_sent; visitor_->SendRetireConnectionId(sequence_number); } } bool QuicConnection::SendNewConnectionId( const QuicNewConnectionIdFrame& frame) { - QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER); visitor_->SendNewConnectionId(frame); + ++stats_.num_new_connection_id_sent; return connected_; } void QuicConnection::OnNewConnectionIdIssued( const QuicConnectionId& connection_id) { - QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER); - visitor_->OnServerConnectionIdIssued(connection_id); + if (perspective_ == Perspective::IS_SERVER) { + visitor_->OnServerConnectionIdIssued(connection_id); + } } void QuicConnection::OnSelfIssuedConnectionIdRetired( const QuicConnectionId& connection_id) { - QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER); - visitor_->OnServerConnectionIdRetired(connection_id); + if (perspective_ == Perspective::IS_SERVER) { + visitor_->OnServerConnectionIdRetired(connection_id); + } } void QuicConnection::MaybeUpdateAckTimeout() { @@ -6197,15 +6530,41 @@ bool QuicConnection::SendPathChallenge( const QuicPathFrameBuffer& data_buffer, const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, - const QuicSocketAddress& /*effective_peer_address*/, + const QuicSocketAddress& effective_peer_address, QuicPacketWriter* writer) { + if (connection_migration_use_new_cid_) { + { + QuicConnectionId client_cid, server_cid; + FindOnPathConnectionIds(self_address, effective_peer_address, &client_cid, + &server_cid); + QuicPacketCreator::ScopedPeerAddressContext context( + &packet_creator_, peer_address, client_cid, server_cid, + connection_migration_use_new_cid_); + if (writer == writer_) { + ScopedPacketFlusher flusher(this); + // It's on current path, add the PATH_CHALLENGE the same way as other + // frames. This may cause connection to be closed. + packet_creator_.AddPathChallengeFrame(data_buffer); + } else { + std::unique_ptr<SerializedPacket> probing_packet = + packet_creator_.SerializePathChallengeConnectivityProbingPacket( + data_buffer); + QUICHE_DCHECK_EQ(IsRetransmittable(*probing_packet), + NO_RETRANSMITTABLE_DATA); + QUICHE_DCHECK_EQ(self_address, alternative_path_.self_address); + WritePacketUsingWriter(std::move(probing_packet), writer, self_address, + peer_address, /*measure_rtt=*/false); + } + } + return connected_; + } if (writer == writer_) { ScopedPacketFlusher flusher(this); { // It's on current path, add the PATH_CHALLENGE the same way as other // frames. - QuicPacketCreator::ScopedPeerAddressContext context(&packet_creator_, - peer_address); + QuicPacketCreator::ScopedPeerAddressContext context( + &packet_creator_, peer_address, /*update_connection_id=*/false); // This may cause connection to be closed. packet_creator_.AddPathChallengeFrame(data_buffer); } @@ -6236,26 +6595,75 @@ void QuicConnection::ValidatePath( std::unique_ptr<QuicPathValidationContext> context, std::unique_ptr<QuicPathValidator::ResultDelegate> result_delegate) { QUICHE_DCHECK(use_path_validator_); - if (perspective_ == Perspective::IS_CLIENT && + if (!connection_migration_use_new_cid_ && + perspective_ == Perspective::IS_CLIENT && !IsDefaultPath(context->self_address(), context->peer_address())) { - alternative_path_ = - PathState(context->self_address(), context->peer_address()); + alternative_path_ = PathState( + context->self_address(), context->peer_address(), + default_path_.client_connection_id, default_path_.server_connection_id, + default_path_.stateless_reset_token_received, + default_path_.stateless_reset_token); } if (path_validator_.HasPendingPathValidation()) { // Cancel and fail any earlier validation. path_validator_.CancelPathValidation(); } + if (connection_migration_use_new_cid_ && + perspective_ == Perspective::IS_CLIENT && + !IsDefaultPath(context->self_address(), context->peer_address())) { + if (self_issued_cid_manager_ != nullptr) { + self_issued_cid_manager_->MaybeSendNewConnectionIds(); + if (!connected_) { + return; + } + } + if ((self_issued_cid_manager_ != nullptr && + !self_issued_cid_manager_->HasConnectionIdToConsume()) || + (peer_issued_cid_manager_ != nullptr && + !peer_issued_cid_manager_->HasUnusedConnectionId())) { + QUIC_DVLOG(1) << "Client cannot start new path validation as there is no " + "requried connection ID is available."; + result_delegate->OnPathValidationFailure(std::move(context)); + return; + } + QuicConnectionId client_connection_id, server_connection_id; + StatelessResetToken stateless_reset_token; + bool stateless_reset_token_received = false; + if (self_issued_cid_manager_ != nullptr) { + client_connection_id = + *self_issued_cid_manager_->ConsumeOneConnectionId(); + } + if (peer_issued_cid_manager_ != nullptr) { + const auto* connection_id_data = + peer_issued_cid_manager_->ConsumeOneUnusedConnectionId(); + server_connection_id = connection_id_data->connection_id; + stateless_reset_token_received = true; + stateless_reset_token = connection_id_data->stateless_reset_token; + } + alternative_path_ = + PathState(context->self_address(), context->peer_address(), + client_connection_id, server_connection_id, + stateless_reset_token_received, stateless_reset_token); + } path_validator_.StartPathValidation(std::move(context), std::move(result_delegate)); } -bool QuicConnection::SendPathResponse(const QuicPathFrameBuffer& data_buffer, - QuicSocketAddress peer_address_to_send) { +bool QuicConnection::SendPathResponse( + const QuicPathFrameBuffer& data_buffer, + const QuicSocketAddress& peer_address_to_send, + const QuicSocketAddress& effective_peer_address) { + QuicConnectionId client_cid, server_cid; + if (connection_migration_use_new_cid_) { + FindOnPathConnectionIds(last_packet_destination_address_, + effective_peer_address, &client_cid, &server_cid); + } // Send PATH_RESPONSE using the provided peer address. If the creator has been // using a different peer address, it will flush before and after serializing // the current PATH_RESPONSE. - QuicPacketCreator::ScopedPeerAddressContext context(&packet_creator_, - peer_address_to_send); + QuicPacketCreator::ScopedPeerAddressContext context( + &packet_creator_, peer_address_to_send, client_cid, server_cid, + connection_migration_use_new_cid_); QUIC_DVLOG(1) << ENDPOINT << "Send PATH_RESPONSE to " << peer_address_to_send; if (default_path_.self_address == last_packet_destination_address_) { // The PATH_CHALLENGE is received on the default socket. Respond on the same @@ -6315,12 +6723,84 @@ void QuicConnection::CancelPathValidation() { path_validator_.CancelPathValidation(); } -void QuicConnection::MigratePath(const QuicSocketAddress& self_address, +bool QuicConnection::UpdateConnectionIdsOnClientMigration( + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address) { + QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT); + if (IsAlternativePath(self_address, peer_address)) { + // Client migration is after path validation. + default_path_.client_connection_id = alternative_path_.client_connection_id; + default_path_.server_connection_id = alternative_path_.server_connection_id; + default_path_.stateless_reset_token = + alternative_path_.stateless_reset_token; + default_path_.stateless_reset_token_received = + alternative_path_.stateless_reset_token_received; + return true; + } + // Client migration is without path validation. + if (self_issued_cid_manager_ != nullptr) { + self_issued_cid_manager_->MaybeSendNewConnectionIds(); + if (!connected_) { + return false; + } + } + if ((self_issued_cid_manager_ != nullptr && + !self_issued_cid_manager_->HasConnectionIdToConsume()) || + (peer_issued_cid_manager_ != nullptr && + !peer_issued_cid_manager_->HasUnusedConnectionId())) { + return false; + } + if (self_issued_cid_manager_ != nullptr) { + default_path_.client_connection_id = + *self_issued_cid_manager_->ConsumeOneConnectionId(); + } + if (peer_issued_cid_manager_ != nullptr) { + const auto* connection_id_data = + peer_issued_cid_manager_->ConsumeOneUnusedConnectionId(); + default_path_.server_connection_id = connection_id_data->connection_id; + default_path_.stateless_reset_token_received = true; + default_path_.stateless_reset_token = + connection_id_data->stateless_reset_token; + } + return true; +} + +void QuicConnection::RetirePeerIssuedConnectionIdsNoLongerOnPath() { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 4, 5); + if (!connection_migration_use_new_cid_ || + peer_issued_cid_manager_ == nullptr) { + return; + } + if (perspective_ == Perspective::IS_CLIENT) { + peer_issued_cid_manager_->MaybeRetireUnusedConnectionIds( + {default_path_.server_connection_id, + alternative_path_.server_connection_id}); + } else { + peer_issued_cid_manager_->MaybeRetireUnusedConnectionIds( + {default_path_.client_connection_id, + alternative_path_.client_connection_id}); + } +} + +bool QuicConnection::MigratePath(const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, QuicPacketWriter* writer, bool owns_writer) { if (!connected_) { - return; + return false; + } + QUICHE_DCHECK(!version().UsesHttp3() || IsHandshakeConfirmed()); + + if (connection_migration_use_new_cid_) { + if (!UpdateConnectionIdsOnClientMigration(self_address, peer_address)) { + return false; + } + if (packet_creator_.GetServerConnectionId().length() != + default_path_.server_connection_id.length()) { + packet_creator_.FlushCurrentPacket(); + } + packet_creator_.SetClientConnectionId(default_path_.client_connection_id); + packet_creator_.SetServerConnectionId(default_path_.server_connection_id); } const auto self_address_change_type = QuicUtils::DetermineAddressChangeType( @@ -6337,13 +6817,21 @@ void QuicConnection::MigratePath(const QuicSocketAddress& self_address, UpdatePeerAddress(peer_address); SetQuicPacketWriter(writer, owns_writer); OnSuccessfulMigration(is_port_change); + return true; +} + +void QuicConnection::OnPathValidationFailureAtClient() { + if (connection_migration_use_new_cid_) { + QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT); + alternative_path_.Clear(); + } } std::vector<QuicConnectionId> QuicConnection::GetActiveServerConnectionIds() const { if (!support_multiple_connection_ids_ || self_issued_cid_manager_ == nullptr) { - return {server_connection_id_}; + return {ServerConnectionId()}; } return self_issued_cid_manager_->GetUnretiredConnectionIds(); } @@ -6354,14 +6842,14 @@ void QuicConnection::CreateConnectionIdManager() { } if (perspective_ == Perspective::IS_CLIENT) { - if (!server_connection_id_.IsEmpty()) { + if (!ServerConnectionId().IsEmpty()) { peer_issued_cid_manager_ = std::make_unique<QuicPeerIssuedConnectionIdManager>( - kMinNumOfActiveConnectionIds, server_connection_id_, clock_, + kMinNumOfActiveConnectionIds, ServerConnectionId(), clock_, alarm_factory_, this); } } else { - if (!server_connection_id_.IsEmpty()) { + if (!ServerConnectionId().IsEmpty()) { self_issued_cid_manager_ = MakeSelfIssuedConnectionIdManager(); } } @@ -6450,6 +6938,9 @@ bool QuicConnection::IsAlternativePath( void QuicConnection::PathState::Clear() { self_address = QuicSocketAddress(); peer_address = QuicSocketAddress(); + client_connection_id = {}; + server_connection_id = {}; + stateless_reset_token_received = false; validated = false; bytes_received_before_address_validation = 0; bytes_sent_before_address_validation = 0; @@ -6466,6 +6957,10 @@ QuicConnection::PathState& QuicConnection::PathState::operator=( if (this != &other) { self_address = other.self_address; peer_address = other.peer_address; + client_connection_id = other.client_connection_id; + server_connection_id = other.server_connection_id; + stateless_reset_token_received = other.stateless_reset_token_received; + stateless_reset_token = other.stateless_reset_token; validated = other.validated; bytes_received_before_address_validation = other.bytes_received_before_address_validation; @@ -6508,10 +7003,12 @@ void QuicConnection::ReversePathValidationResultDelegate:: QUIC_DLOG(INFO) << "Successfully validated new path " << *context; if (connection_->IsDefaultPath(context->self_address(), context->peer_address())) { + QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 3, 6); connection_->OnEffectivePeerMigrationValidated(); } else { QUICHE_DCHECK(connection_->IsAlternativePath( context->self_address(), context->effective_peer_address())); + QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 4, 6); QUIC_DVLOG(1) << "Mark alternative peer address " << context->effective_peer_address() << " validated."; connection_->alternative_path_.validated = true; @@ -6528,13 +7025,29 @@ void QuicConnection::ReversePathValidationResultDelegate:: if (connection_->IsDefaultPath(context->self_address(), context->peer_address())) { // Only act upon validation failure on the default path. + QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 5, 6); connection_->RestoreToLastValidatedPath(original_direct_peer_address_); } else if (connection_->IsAlternativePath( context->self_address(), context->effective_peer_address())) { + QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 6, 6); connection_->alternative_path_.Clear(); } } +QuicConnection::ScopedRetransmissionTimeoutIndicator:: + ScopedRetransmissionTimeoutIndicator(QuicConnection* connection) + : connection_(connection) { + QUICHE_DCHECK(!connection_->in_on_retransmission_time_out_) + << "ScopedRetransmissionTimeoutIndicator is not supposed to be nested"; + connection_->in_on_retransmission_time_out_ = true; +} + +QuicConnection::ScopedRetransmissionTimeoutIndicator:: + ~ScopedRetransmissionTimeoutIndicator() { + QUICHE_DCHECK(connection_->in_on_retransmission_time_out_); + connection_->in_on_retransmission_time_out_ = false; +} + void QuicConnection::RestoreToLastValidatedPath( QuicSocketAddress original_direct_peer_address) { QUIC_DLOG(INFO) << "Switch back to use the old peer address " @@ -6562,7 +7075,7 @@ void QuicConnection::RestoreToLastValidatedPath( } UpdatePeerAddress(original_direct_peer_address); - default_path_ = std::move(alternative_path_); + SetDefaultPathState(std::move(alternative_path_)); active_effective_peer_migration_type_ = NO_CHANGE; ++stats_.num_invalid_peer_migration; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection.h b/chromium/net/third_party/quiche/src/quic/core/quic_connection.h index e2ba17bd9d8..b9567250637 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection.h @@ -36,7 +36,6 @@ #include "quic/core/quic_alarm.h" #include "quic/core/quic_alarm_factory.h" #include "quic/core/quic_blocked_writer_interface.h" -#include "quic/core/quic_circular_deque.h" #include "quic/core/quic_connection_id.h" #include "quic/core/quic_connection_id_manager.h" #include "quic/core/quic_connection_stats.h" @@ -58,6 +57,7 @@ #include "quic/platform/api/quic_export.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_socket_address.h" +#include "common/quiche_circular_deque.h" namespace quic { @@ -777,9 +777,9 @@ class QUIC_EXPORT_PRIVATE QuicConnection const QuicSocketAddress& effective_peer_address() const { return default_path_.peer_address; } - QuicConnectionId connection_id() const { return server_connection_id_; } - QuicConnectionId client_connection_id() const { - return client_connection_id_; + const QuicConnectionId& connection_id() const { return ServerConnectionId(); } + const QuicConnectionId& client_connection_id() const { + return ClientConnectionId(); } void set_client_connection_id(QuicConnectionId client_connection_id); const QuicClock* clock() const { return clock_; } @@ -1211,11 +1211,17 @@ class QUIC_EXPORT_PRIVATE QuicConnection void CancelPathValidation(); - void MigratePath(const QuicSocketAddress& self_address, + // Returns true if the migration succeeds, otherwise returns false (e.g., no + // available CIDs, connection disconnected, etc). + bool MigratePath(const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, QuicPacketWriter* writer, bool owns_writer); + // Called to clear the alternative_path_ when path validation failed on the + // client side. + void OnPathValidationFailureAtClient(); + void SetSourceAddressTokenToSend(absl::string_view token); void SendPing() { @@ -1228,6 +1234,22 @@ class QUIC_EXPORT_PRIVATE QuicConnection bool validate_client_address() const { return validate_client_addresses_; } + bool support_multiple_connection_ids() const { + return support_multiple_connection_ids_; + } + + bool use_connection_id_on_default_path() const { + return use_connection_id_on_default_path_; + } + + bool connection_migration_use_new_cid() const { + return connection_migration_use_new_cid_; + } + + bool count_bytes_on_alternative_path_separately() const { + return count_bytes_on_alternative_path_separately_; + } + // Instantiates connection ID manager. void CreateConnectionIdManager(); @@ -1331,10 +1353,20 @@ class QUIC_EXPORT_PRIVATE QuicConnection }; struct QUIC_EXPORT_PRIVATE PathState { + PathState() = default; + PathState(const QuicSocketAddress& alternative_self_address, - const QuicSocketAddress& alternative_peer_address) + const QuicSocketAddress& alternative_peer_address, + const QuicConnectionId& client_connection_id, + const QuicConnectionId& server_connection_id, + bool stateless_reset_token_received, + StatelessResetToken stateless_reset_token) : self_address(alternative_self_address), - peer_address(alternative_peer_address) {} + peer_address(alternative_peer_address), + client_connection_id(client_connection_id), + server_connection_id(server_connection_id), + stateless_reset_token(stateless_reset_token), + stateless_reset_token_received(stateless_reset_token_received) {} PathState(PathState&& other); @@ -1346,6 +1378,10 @@ class QUIC_EXPORT_PRIVATE QuicConnection QuicSocketAddress self_address; // The actual peer address behind the proxy if there is any. QuicSocketAddress peer_address; + QuicConnectionId client_connection_id; + QuicConnectionId server_connection_id; + StatelessResetToken stateless_reset_token; + bool stateless_reset_token_received = false; // True if the peer address has been validated. Address is considered // validated when 1) an address token of the peer address is received and // validated, or 2) a HANDSHAKE packet has been successfully processed on @@ -1419,6 +1455,41 @@ class QUIC_EXPORT_PRIVATE QuicConnection QuicSocketAddress original_direct_peer_address_; }; + // A class which sets and clears in_on_retransmission_time_out_ when entering + // and exiting OnRetransmissionTimeout, respectively. + class QUIC_EXPORT_PRIVATE ScopedRetransmissionTimeoutIndicator { + public: + // |connection| must outlive this indicator. + explicit ScopedRetransmissionTimeoutIndicator(QuicConnection* connection); + + ~ScopedRetransmissionTimeoutIndicator(); + + private: + QuicConnection* connection_; // Not owned. + }; + + QuicConnectionId& ClientConnectionId() { + return use_connection_id_on_default_path_ + ? default_path_.client_connection_id + : client_connection_id_; + } + const QuicConnectionId& ClientConnectionId() const { + return use_connection_id_on_default_path_ + ? default_path_.client_connection_id + : client_connection_id_; + } + QuicConnectionId& ServerConnectionId() { + return use_connection_id_on_default_path_ + ? default_path_.server_connection_id + : server_connection_id_; + } + const QuicConnectionId& ServerConnectionId() const { + return use_connection_id_on_default_path_ + ? default_path_.server_connection_id + : server_connection_id_; + } + void SetServerConnectionId(const QuicConnectionId& server_connection_id); + // Notifies the visitor of the close and marks the connection as disconnected. // Does not send a connection close frame to the peer. It should only be // called by CloseConnection or OnConnectionCloseFrame, OnPublicResetPacket, @@ -1438,6 +1509,42 @@ class QUIC_EXPORT_PRIVATE QuicConnection void ReplaceInitialServerConnectionId( const QuicConnectionId& new_server_connection_id); + // Given the server_connection_id find if there is already a corresponding + // client connection ID used on default/alternative path. If not, find if + // there is an unused connection ID. + void FindMatchingOrNewClientConnectionIdOrToken( + const PathState& default_path, + const PathState& alternative_path, + const QuicConnectionId& server_connection_id, + QuicConnectionId* client_connection_id, + bool* stateless_reset_token_received, + StatelessResetToken* stateless_reset_token); + + // Returns true and sets connection IDs if (self_address, peer_address) + // corresponds to either the default path or alternative path. Returns false + // otherwise. + bool FindOnPathConnectionIds(const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address, + QuicConnectionId* client_connection_id, + QuicConnectionId* server_connection_id) const; + + // Set default_path_ to the new_path_state and update the connection IDs in + // packet creator accordingly. + void SetDefaultPathState(PathState new_path_state); + + // Returns true if header contains valid server connection ID. + bool ValidateServerConnectionId(const QuicPacketHeader& header) const; + + // Update the connection IDs when client migrates with/without validation. + // Returns false if required connection ID is not available. + bool UpdateConnectionIdsOnClientMigration( + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address); + + // Retire active peer issued connection IDs after they are no longer used on + // any path. + void RetirePeerIssuedConnectionIdsNoLongerOnPath(); + // Writes the given packet to socket, encrypted with packet's // encryption_level. Returns true on successful write, and false if the writer // was blocked and the write needs to be tried again. Notifies the @@ -1598,7 +1705,7 @@ class QUIC_EXPORT_PRIVATE QuicConnection QuicPacketNumber GetLargestAckedPacket() const; // Whether incoming_connection_ids_ contains connection_id. - bool HasIncomingConnectionId(QuicConnectionId connection_id); + bool HasIncomingConnectionId(QuicConnectionId connection_id) const; // Whether connection is limited by amplification factor. bool LimitedByAmplificationFactor() const; @@ -1671,7 +1778,8 @@ class QUIC_EXPORT_PRIVATE QuicConnection // Send PATH_RESPONSE to the given peer address. bool SendPathResponse(const QuicPathFrameBuffer& data_buffer, - QuicSocketAddress peer_address_to_send); + const QuicSocketAddress& peer_address_to_send, + const QuicSocketAddress& effective_peer_address); // Update both connection's and packet creator's peer address. void UpdatePeerAddress(QuicSocketAddress peer_address); @@ -1734,6 +1842,10 @@ class QUIC_EXPORT_PRIVATE QuicConnection // preferred_address transport parameter. bool OnNewConnectionIdFrameInner(const QuicNewConnectionIdFrame& frame); + // Called to patch missing client connection ID on default/alternative paths + // when a new client connection ID is received. + void OnClientConnectionIdAvailable(); + QuicFramer framer_; // Contents received in the current packet, especially used to identify @@ -1829,7 +1941,7 @@ class QUIC_EXPORT_PRIVATE QuicConnection // Collection of coalesced packets which were received while processing // the current packet. - QuicCircularDeque<std::unique_ptr<QuicEncryptedPacket>> + quiche::QuicheCircularDeque<std::unique_ptr<QuicEncryptedPacket>> received_coalesced_packets_; // Maximum number of undecryptable packets the connection will store. @@ -1937,6 +2049,12 @@ class QUIC_EXPORT_PRIVATE QuicConnection // Source address of the last received packet. QuicSocketAddress last_packet_source_address_; + // Destination connection ID of the last received packet. If this ID is the + // original server connection ID chosen by client and server replaces it with + // a different ID, last_packet_destination_connection_id_ is set to the + // replacement connection ID on the server side. + QuicConnectionId last_packet_destination_connection_id_; + // Set to false if the connection should not send truncated connection IDs to // the peer, even if the peer supports it. bool can_truncate_connection_ids_; @@ -2036,12 +2154,15 @@ class QUIC_EXPORT_PRIVATE QuicConnection // saved and responded to. // TODO(danzh) deprecate this field when deprecating // --quic_send_path_response. - QuicCircularDeque<QuicPathFrameBuffer> received_path_challenge_payloads_; + quiche::QuicheCircularDeque<QuicPathFrameBuffer> + received_path_challenge_payloads_; // Buffer outstanding PATH_CHALLENGEs if socket write is blocked, future // OnCanWrite will attempt to respond with PATH_RESPONSEs using the retained // payload and peer addresses. - QuicCircularDeque<PendingPathChallenge> pending_path_challenge_payloads_; + // TODO(fayang): remove this when deprecating quic_drop_unsent_path_response. + quiche::QuicheCircularDeque<PendingPathChallenge> + pending_path_challenge_payloads_; // Set of connection IDs that should be accepted as destination on // received packets. This is conceptually a set but is implemented as a @@ -2054,6 +2175,9 @@ class QUIC_EXPORT_PRIVATE QuicConnection // |original_destination_connection_id_| for validation. absl::optional<QuicConnectionId> original_destination_connection_id_; + // The connection ID that replaces original_destination_connection_id_. + QuicConnectionId original_destination_connection_id_replacement_; + // After we receive a RETRY packet, |retry_source_connection_id_| contains // the source connection ID from that packet. absl::optional<QuicConnectionId> retry_source_connection_id_; @@ -2094,13 +2218,8 @@ class QUIC_EXPORT_PRIVATE QuicConnection size_t anti_amplification_factor_ = GetQuicFlag(FLAGS_quic_anti_amplification_factor); - bool start_peer_migration_earlier_ = - GetQuicReloadableFlag(quic_start_peer_migration_earlier); - - // latch --gfe2_reloadable_flag_quic_send_path_response and - // --gfe2_reloadable_flag_quic_start_peer_migration_earlier. - bool send_path_response_ = start_peer_migration_earlier_ && - GetQuicReloadableFlag(quic_send_path_response2); + // latch --gfe2_reloadable_flag_quic_send_path_response. + bool send_path_response_ = GetQuicReloadableFlag(quic_send_path_response2); bool use_path_validator_ = send_path_response_ && @@ -2130,6 +2249,9 @@ class QUIC_EXPORT_PRIVATE QuicConnection // True after the first 1-RTT packet has successfully decrypted. bool have_decrypted_first_one_rtt_packet_ = false; + // True if we are currently processing OnRetransmissionTimeout. + bool in_on_retransmission_time_out_ = false; + const bool encrypted_control_frames_; const bool use_encryption_level_context_; @@ -2163,6 +2285,23 @@ class QUIC_EXPORT_PRIVATE QuicConnection const bool donot_write_mid_packet_processing_ = GetQuicReloadableFlag(quic_donot_write_mid_packet_processing); + + bool use_connection_id_on_default_path_ = + GetQuicReloadableFlag(quic_use_connection_id_on_default_path_v2); + + // Indicates whether we should proactively validate peer address on a + // PATH_CHALLENGE received. + bool should_proactively_validate_peer_address_on_path_challenge_ = false; + + // Enable this via reloadable flag once this feature is complete. + bool connection_migration_use_new_cid_ = false; + + const bool group_path_response_and_challenge_sending_closer_ = + GetQuicReloadableFlag( + quic_group_path_response_and_challenge_sending_closer); + + const bool quic_deprecate_incoming_connection_ids_ = + GetQuicReloadableFlag(quic_deprecate_incoming_connection_ids); }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.cc index 303db8f2069..a8cb4c0f377 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.cc @@ -18,7 +18,6 @@ #include "quic/platform/api/quic_flag_utils.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/quiche_endian.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.cc index a8e8c542f5f..f7089b7ebfd 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.cc @@ -199,6 +199,22 @@ void QuicPeerIssuedConnectionIdManager::PrepareToRetireActiveConnectionId( } } +void QuicPeerIssuedConnectionIdManager::MaybeRetireUnusedConnectionIds( + const std::vector<QuicConnectionId>& active_connection_ids_on_path) { + std::vector<QuicConnectionId> cids_to_retire; + for (const auto& cid_data : active_connection_id_data_) { + if (std::find(active_connection_ids_on_path.begin(), + active_connection_ids_on_path.end(), + cid_data.connection_id) == + active_connection_ids_on_path.end()) { + cids_to_retire.push_back(cid_data.connection_id); + } + } + for (const auto& cid : cids_to_retire) { + PrepareToRetireActiveConnectionId(cid); + } +} + bool QuicPeerIssuedConnectionIdManager::IsConnectionIdActive( const QuicConnectionId& cid) const { return FindConnectionIdData(active_connection_id_data_, cid) != @@ -263,7 +279,8 @@ QuicSelfIssuedConnectionIdManager::QuicSelfIssuedConnectionIdManager( retire_connection_id_alarm_(alarm_factory->CreateAlarm( new RetireSelfIssuedConnectionIdAlarmDelegate(this))), last_connection_id_(initial_connection_id), - next_connection_id_sequence_number_(1u) { + next_connection_id_sequence_number_(1u), + last_connection_id_consumed_by_self_sequence_number_(0u) { active_connection_ids_.emplace_back(initial_connection_id, 0u); } @@ -386,4 +403,45 @@ void QuicSelfIssuedConnectionIdManager::MaybeSendNewConnectionIds() { } } +bool QuicSelfIssuedConnectionIdManager::HasConnectionIdToConsume() const { + for (const auto& active_cid_data : active_connection_ids_) { + if (active_cid_data.second > + last_connection_id_consumed_by_self_sequence_number_) { + return true; + } + } + return false; +} + +absl::optional<QuicConnectionId> +QuicSelfIssuedConnectionIdManager::ConsumeOneConnectionId() { + for (const auto& active_cid_data : active_connection_ids_) { + if (active_cid_data.second > + last_connection_id_consumed_by_self_sequence_number_) { + // Since connection IDs in active_connection_ids_ has monotonically + // increasing sequence numbers, the returned connection ID has the + // smallest sequence number among all unconsumed active connection IDs. + last_connection_id_consumed_by_self_sequence_number_ = + active_cid_data.second; + return active_cid_data.first; + } + } + return absl::nullopt; +} + +bool QuicSelfIssuedConnectionIdManager::IsConnectionIdInUse( + const QuicConnectionId& cid) const { + for (const auto& active_cid_data : active_connection_ids_) { + if (active_cid_data.first == cid) { + return true; + } + } + for (const auto& to_be_retired_cid_data : to_be_retired_connection_ids_) { + if (to_be_retired_cid_data.first == cid) { + return true; + } + } + return false; +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.h index b933a69f158..5e7fc8ccf72 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.h @@ -13,6 +13,7 @@ #include <cstddef> #include <memory> +#include "absl/types/optional.h" #include "quic/core/frames/quic_new_connection_id_frame.h" #include "quic/core/frames/quic_retire_connection_id_frame.h" #include "quic/core/quic_alarm.h" @@ -68,13 +69,19 @@ class QUIC_EXPORT_PRIVATE QuicPeerIssuedConnectionIdManager { QuicErrorCode OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame, std::string* error_detail); + bool HasUnusedConnectionId() const { + return !unused_connection_id_data_.empty(); + } + // Returns the data associated with an unused connection Id. After the call, // the Id is marked as used. Returns nullptr if there is no unused connection // Id. const QuicConnectionIdData* ConsumeOneUnusedConnectionId(); - // Add the connection Id to the pending retirement connection Id list. - void PrepareToRetireActiveConnectionId(const QuicConnectionId& cid); + // Add each active connection Id that is no longer on path to the pending + // retirement connection Id list. + void MaybeRetireUnusedConnectionIds( + const std::vector<QuicConnectionId>& active_connection_ids_on_path); bool IsConnectionIdActive(const QuicConnectionId& cid) const; @@ -90,6 +97,10 @@ class QUIC_EXPORT_PRIVATE QuicPeerIssuedConnectionIdManager { private: friend class test::QuicConnectionIdManagerPeer; + // Add the connection Id to the pending retirement connection Id list and + // schedule an alarm if needed. + void PrepareToRetireActiveConnectionId(const QuicConnectionId& cid); + bool IsConnectionIdNew(const QuicNewConnectionIdFrame& frame); void PrepareToRetireConnectionIdPriorTo( @@ -135,6 +146,17 @@ class QUIC_EXPORT_PRIVATE QuicSelfIssuedConnectionIdManager { // Sends new connection IDs if more can be sent. void MaybeSendNewConnectionIds(); + // The two functions are called on the client side to associate a client + // connection ID with a new probing/migration path when client uses + // non-empty connection ID. + bool HasConnectionIdToConsume() const; + absl::optional<QuicConnectionId> ConsumeOneConnectionId(); + + // Returns true if the given connection ID is issued by the + // QuicSelfIssuedConnectionIdManager and not retired locally yet. Called to + // tell if a received packet has a valid connection ID. + bool IsConnectionIdInUse(const QuicConnectionId& cid) const; + virtual QuicConnectionId GenerateNewConnectionId( const QuicConnectionId& old_connection_id) const; @@ -162,6 +184,8 @@ class QUIC_EXPORT_PRIVATE QuicSelfIssuedConnectionIdManager { // State of the last issued connection Id. QuicConnectionId last_connection_id_; uint64_t next_connection_id_sequence_number_; + // The sequence number of last connection ID consumed. + uint64_t last_connection_id_consumed_by_self_sequence_number_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager_test.cc index 9c7daf0db5e..7baeb2b247e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager_test.cc @@ -126,8 +126,8 @@ TEST_F(QuicPeerIssuedConnectionIdManagerTest, frame.stateless_reset_token); // Connection migration succeed. Prepares to retire CID #0. - peer_issued_cid_manager_.PrepareToRetireActiveConnectionId( - TestConnectionId(0)); + peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds( + {TestConnectionId(1)}); cid_manager_visitor_.SetCurrentPeerConnectionId(TestConnectionId(1)); ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet()); alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_); @@ -150,8 +150,8 @@ TEST_F(QuicPeerIssuedConnectionIdManagerTest, // Start to use CID #2 for alternative path. peer_issued_cid_manager_.ConsumeOneUnusedConnectionId(); // Connection migration succeed. Prepares to retire CID #1. - peer_issued_cid_manager_.PrepareToRetireActiveConnectionId( - TestConnectionId(1)); + peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds( + {TestConnectionId(2)}); cid_manager_visitor_.SetCurrentPeerConnectionId(TestConnectionId(2)); ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet()); alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_); @@ -174,8 +174,8 @@ TEST_F(QuicPeerIssuedConnectionIdManagerTest, // Start to use CID #3 for alternative path. peer_issued_cid_manager_.ConsumeOneUnusedConnectionId(); // Connection migration succeed. Prepares to retire CID #2. - peer_issued_cid_manager_.PrepareToRetireActiveConnectionId( - TestConnectionId(2)); + peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds( + {TestConnectionId(3)}); cid_manager_visitor_.SetCurrentPeerConnectionId(TestConnectionId(3)); ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet()); alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_); @@ -214,8 +214,8 @@ TEST_F(QuicPeerIssuedConnectionIdManagerTest, // Start to use CID #1 for alternative path. peer_issued_cid_manager_.ConsumeOneUnusedConnectionId(); // Connection migration fails. Prepares to retire CID #1. - peer_issued_cid_manager_.PrepareToRetireActiveConnectionId( - TestConnectionId(1)); + peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds( + {initial_connection_id_}); // Actually retires CID #1. ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet()); alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_); @@ -238,8 +238,8 @@ TEST_F(QuicPeerIssuedConnectionIdManagerTest, // Start to use CID #2 for alternative path. peer_issued_cid_manager_.ConsumeOneUnusedConnectionId(); // Connection migration fails again. Prepares to retire CID #2. - peer_issued_cid_manager_.PrepareToRetireActiveConnectionId( - TestConnectionId(2)); + peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds( + {initial_connection_id_}); // Actually retires CID #2. ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet()); alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_); @@ -262,8 +262,8 @@ TEST_F(QuicPeerIssuedConnectionIdManagerTest, // Start to use CID #3 for alternative path. peer_issued_cid_manager_.ConsumeOneUnusedConnectionId(); // Connection migration succeed. Prepares to retire CID #0. - peer_issued_cid_manager_.PrepareToRetireActiveConnectionId( - TestConnectionId(0)); + peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds( + {TestConnectionId(3)}); // After CID #3 is default (i.e., when there is no pending frame to write // associated with CID #0), #0 can actually be retired. cid_manager_visitor_.SetCurrentPeerConnectionId(TestConnectionId(3)); @@ -374,8 +374,8 @@ TEST_F(QuicPeerIssuedConnectionIdManagerTest, // Outcome: (active: #0 #1 unused: None) peer_issued_cid_manager_.ConsumeOneUnusedConnectionId(); // Prepare to retire CID #1 as path validation fails. - peer_issued_cid_manager_.PrepareToRetireActiveConnectionId( - TestConnectionId(1)); + peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds( + {initial_connection_id_}); // Actually retires CID #1. ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet()); alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_); @@ -845,20 +845,36 @@ TEST_F(QuicSelfIssuedConnectionIdManagerTest, QuicConnectionId cid1 = cid_manager_.GenerateNewConnectionId(cid0); QuicConnectionId cid2 = cid_manager_.GenerateNewConnectionId(cid1); QuicConnectionId cid3 = cid_manager_.GenerateNewConnectionId(cid2); + QuicConnectionId cid; EXPECT_CALL(cid_manager_visitor_, OnNewConnectionIdIssued(_)).Times(3); EXPECT_CALL(cid_manager_visitor_, SendNewConnectionId(_)) .Times(3) .WillRepeatedly(Return(true)); QuicTime::Delta connection_id_expire_timeout = 3 * pto_delay_; QuicRetireConnectionIdFrame retire_cid_frame; + EXPECT_TRUE(cid_manager_.IsConnectionIdInUse(cid0)); + EXPECT_FALSE(cid_manager_.HasConnectionIdToConsume()); + EXPECT_FALSE(cid_manager_.ConsumeOneConnectionId().has_value()); // CID #1 is sent to peer. cid_manager_.MaybeSendNewConnectionIds(); + EXPECT_TRUE(cid_manager_.IsConnectionIdInUse(cid1)); + EXPECT_TRUE(cid_manager_.HasConnectionIdToConsume()); + cid = *cid_manager_.ConsumeOneConnectionId(); + EXPECT_EQ(cid1, cid); + EXPECT_FALSE(cid_manager_.HasConnectionIdToConsume()); // CID #0's retirement is scheduled and CID #2 is sent to peer. retire_cid_frame.sequence_number = 0u; cid_manager_.OnRetireConnectionIdFrame(retire_cid_frame, pto_delay_, &error_details_); + EXPECT_TRUE(cid_manager_.IsConnectionIdInUse(cid0)); + EXPECT_TRUE(cid_manager_.IsConnectionIdInUse(cid1)); + EXPECT_TRUE(cid_manager_.IsConnectionIdInUse(cid2)); + EXPECT_TRUE(cid_manager_.HasConnectionIdToConsume()); + cid = *cid_manager_.ConsumeOneConnectionId(); + EXPECT_EQ(cid2, cid); + EXPECT_FALSE(cid_manager_.HasConnectionIdToConsume()); clock_.AdvanceTime(connection_id_expire_timeout * 0.1); @@ -874,6 +890,9 @@ TEST_F(QuicSelfIssuedConnectionIdManagerTest, EXPECT_CALL(cid_manager_visitor_, OnSelfIssuedConnectionIdRetired(cid0)); EXPECT_CALL(cid_manager_visitor_, OnSelfIssuedConnectionIdRetired(cid1)); alarm_factory_.FireAlarm(retire_self_issued_cid_alarm_); + EXPECT_FALSE(cid_manager_.IsConnectionIdInUse(cid0)); + EXPECT_FALSE(cid_manager_.IsConnectionIdInUse(cid1)); + EXPECT_TRUE(cid_manager_.IsConnectionIdInUse(cid2)); EXPECT_THAT(cid_manager_.GetUnretiredConnectionIds(), ElementsAre(cid2, cid3)); EXPECT_FALSE(retire_self_issued_cid_alarm_->IsSet()); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.cc index 3d1b3c422a8..fbaa20abd94 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.cc @@ -34,8 +34,9 @@ std::ostream& operator<<(std::ostream& os, const QuicConnectionStats& s) { os << " pto_count: " << s.pto_count; os << " min_rtt_us: " << s.min_rtt_us; os << " srtt_us: " << s.srtt_us; - os << " max_packet_size: " << s.max_packet_size; - os << " max_received_packet_size: " << s.max_received_packet_size; + os << " egress_mtu: " << s.egress_mtu; + os << " max_egress_mtu: " << s.max_egress_mtu; + os << " ingress_mtu: " << s.ingress_mtu; os << " estimated_bandwidth: " << s.estimated_bandwidth; os << " packets_reordered: " << s.packets_reordered; os << " max_sequence_reordering: " << s.max_sequence_reordering; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.h b/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.h index 06b7f5868bd..39fd5b087a6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.h @@ -103,8 +103,14 @@ struct QUIC_EXPORT_PRIVATE QuicConnectionStats { int64_t min_rtt_us = 0; // Minimum RTT in microseconds. int64_t srtt_us = 0; // Smoothed RTT in microseconds. int64_t cwnd_bootstrapping_rtt_us = 0; // RTT used in cwnd_bootstrapping. - QuicByteCount max_packet_size = 0; - QuicByteCount max_received_packet_size = 0; + // The connection's |long_term_mtu_| used for sending packets, populated by + // QuicConnection::GetStats(). + QuicByteCount egress_mtu = 0; + // The maximum |long_term_mtu_| the connection ever used. + QuicByteCount max_egress_mtu = 0; + // Size of the largest packet received from the peer, populated by + // QuicConnection::GetStats(). + QuicByteCount ingress_mtu = 0; QuicBandwidth estimated_bandwidth = QuicBandwidth::Zero(); // Reordering stats for received packets. @@ -209,6 +215,10 @@ struct QUIC_EXPORT_PRIVATE QuicConnectionStats { // which was canceled because the peer migrated again. Such migration is also // counted as invalid peer migration. size_t num_peer_migration_while_validating_default_path = 0; + // Number of NEW_CONNECTION_ID frames sent. + size_t num_new_connection_id_sent = 0; + // Number of RETIRE_CONNECTION_ID frames sent. + size_t num_retire_connection_id_sent = 0; struct QUIC_NO_EXPORT TlsServerOperationStats { bool success = false; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc index e2fba5b8325..fe2ff6cab6e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc @@ -21,10 +21,12 @@ #include "quic/core/crypto/quic_decrypter.h" #include "quic/core/crypto/quic_encrypter.h" #include "quic/core/frames/quic_connection_close_frame.h" +#include "quic/core/frames/quic_new_connection_id_frame.h" #include "quic/core/frames/quic_path_response_frame.h" #include "quic/core/quic_connection_id.h" #include "quic/core/quic_constants.h" #include "quic/core/quic_error_codes.h" +#include "quic/core/quic_packet_creator.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_path_validator.h" #include "quic/core/quic_simple_buffer_allocator.h" @@ -33,6 +35,7 @@ #include "quic/core/quic_versions.h" #include "quic/platform/api/quic_expect_bug.h" #include "quic/platform/api/quic_flags.h" +#include "quic/platform/api/quic_ip_address.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_reference_counted.h" #include "quic/platform/api/quic_socket_address.h" @@ -764,6 +767,12 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { void use_tagging_decrypter() { writer_->use_tagging_decrypter(); } + void SetClientConnectionId(const QuicConnectionId& client_connection_id) { + connection_.set_client_connection_id(client_connection_id); + writer_->framer()->framer()->SetExpectedClientConnectionIdLength( + client_connection_id.length()); + } + void SetDecrypter(EncryptionLevel level, std::unique_ptr<QuicDecrypter> decrypter) { if (connection_.version().KnowsWhichDecrypterToUse()) { @@ -1190,8 +1199,7 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { EncryptionLevel level) { QuicPacketHeader header = ConstructPacketHeader(number, level); QuicFrames frames; - if (GetQuicReloadableFlag(quic_reject_unexpected_ietf_frame_types) && - VersionHasIetfQuicFrames(version().transport_version) && + if (VersionHasIetfQuicFrames(version().transport_version) && (level == ENCRYPTION_INITIAL || level == ENCRYPTION_HANDSHAKE)) { frames.push_back(QuicFrame(QuicPingFrame())); frames.push_back(QuicFrame(QuicPaddingFrame(100))); @@ -1420,8 +1428,10 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { EXPECT_TRUE(connection_.connected()); } - void PathProbeTestInit(Perspective perspective) { + void PathProbeTestInit(Perspective perspective, + bool receive_new_server_connection_id = true) { set_perspective(perspective); + connection_.CreateConnectionIdManager(); EXPECT_EQ(connection_.perspective(), perspective); if (perspective == Perspective::IS_SERVER) { QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); @@ -1453,6 +1463,17 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { kPeerAddress, ENCRYPTION_FORWARD_SECURE); EXPECT_EQ(kPeerAddress, connection_.peer_address()); EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + if (perspective == Perspective::IS_CLIENT && + receive_new_server_connection_id) { + QuicNewConnectionIdFrame frame; + frame.connection_id = TestConnectionId(1234); + ASSERT_NE(frame.connection_id, connection_.connection_id()); + frame.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame.connection_id); + frame.retire_prior_to = 0u; + frame.sequence_number = 1u; + connection_.OnNewConnectionIdFrame(frame); + } } void TestClientRetryHandling(bool invalid_retry_tag, @@ -1691,13 +1712,8 @@ TEST_P(QuicConnectionTest, PeerPortChangeAtServer) { EXPECT_CALL(visitor_, OnStreamFrame(_)) .WillOnce(Invoke( [=]() { EXPECT_EQ(kPeerAddress, connection_.peer_address()); })) - .WillOnce(Invoke([=]() { - EXPECT_EQ((GetQuicReloadableFlag(quic_start_peer_migration_earlier) || - !GetParam().version.HasIetfQuicFrames() - ? kNewPeerAddress - : kPeerAddress), - connection_.peer_address()); - })); + .WillOnce(Invoke( + [=]() { EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); })); QuicFrames frames; frames.push_back(QuicFrame(frame1_)); ProcessFramesPacketWithAddresses(frames, kSelfAddress, kPeerAddress, @@ -1765,13 +1781,8 @@ TEST_P(QuicConnectionTest, PeerIpAddressChangeAtServer) { EXPECT_CALL(visitor_, OnStreamFrame(_)) .WillOnce(Invoke( [=]() { EXPECT_EQ(kPeerAddress, connection_.peer_address()); })) - .WillOnce(Invoke([=]() { - EXPECT_EQ((GetQuicReloadableFlag(quic_start_peer_migration_earlier) || - !GetParam().version.HasIetfQuicFrames() - ? kNewPeerAddress - : kPeerAddress), - connection_.peer_address()); - })); + .WillOnce(Invoke( + [=]() { EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); })); QuicFrames frames; frames.push_back(QuicFrame(frame1_)); ProcessFramesPacketWithAddresses(frames, kSelfAddress, kPeerAddress, @@ -1866,6 +1877,83 @@ TEST_P(QuicConnectionTest, PeerIpAddressChangeAtServer) { EXPECT_EQ(1u, connection_.GetStats().num_validated_peer_migration); } +TEST_P(QuicConnectionTest, PeerIpAddressChangeAtServerWithMissingConnectionId) { + set_perspective(Perspective::IS_SERVER); + if (!connection_.connection_migration_use_new_cid()) { + return; + } + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); + + QuicConnectionId client_cid0 = TestConnectionId(1); + QuicConnectionId client_cid1 = TestConnectionId(3); + QuicConnectionId server_cid1; + SetClientConnectionId(client_cid0); + connection_.CreateConnectionIdManager(); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + // Prevent packets from being coalesced. + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); + QuicConnectionPeer::SetAddressValidated(&connection_); + + // Sends new server CID to client. + EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_)) + .WillOnce( + Invoke([&](const QuicConnectionId& cid) { server_cid1 = cid; })); + EXPECT_CALL(visitor_, SendNewConnectionId(_)); + connection_.OnHandshakeComplete(); + + // Clear direct_peer_address. + QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); + // Clear effective_peer_address, it is the same as direct_peer_address for + // this test. + QuicConnectionPeer::SetEffectivePeerAddress(&connection_, + QuicSocketAddress()); + EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); + + const QuicSocketAddress kNewPeerAddress = + QuicSocketAddress(QuicIpAddress::Loopback4(), /*port=*/23456); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(2); + QuicFrames frames; + frames.push_back(QuicFrame(frame1_)); + ProcessFramesPacketWithAddresses(frames, kSelfAddress, kPeerAddress, + ENCRYPTION_FORWARD_SECURE); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + + // Send some data to make connection has packets in flight. + connection_.SendStreamData3(); + EXPECT_EQ(1u, writer_->packets_write_attempts()); + + // Process another packet with a different peer address on server side will + // start connection migration. + peer_creator_.SetServerConnectionId(server_cid1); + EXPECT_CALL(visitor_, OnConnectionMigration(IPV6_TO_IPV4_CHANGE)).Times(1); + // Do not propagate OnCanWrite() to session notifier. + EXPECT_CALL(visitor_, OnCanWrite()).Times(AtLeast(1u)); + + QuicFrames frames2; + frames2.push_back(QuicFrame(frame2_)); + ProcessFramesPacketWithAddresses(frames2, kSelfAddress, kNewPeerAddress, + ENCRYPTION_FORWARD_SECURE); + EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); + EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); + + // Writing path response & reverse path challenge is blocked due to missing + // client connection ID, i.e., packets_write_attempts is unchanged. + EXPECT_EQ(1u, writer_->packets_write_attempts()); + + // Receives new client CID from client would unblock write. + QuicNewConnectionIdFrame new_cid_frame; + new_cid_frame.connection_id = client_cid1; + new_cid_frame.sequence_number = 1u; + new_cid_frame.retire_prior_to = 0u; + connection_.OnNewConnectionIdFrame(new_cid_frame); + connection_.SendStreamData3(); + + EXPECT_EQ(2u, writer_->packets_write_attempts()); +} + TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) { set_perspective(Perspective::IS_SERVER); QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); @@ -1983,17 +2071,38 @@ TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) { TEST_P(QuicConnectionTest, ReversePathValidationFailureAtServer) { set_perspective(Perspective::IS_SERVER); - if (!connection_.validate_client_address()) { + if (!connection_.connection_migration_use_new_cid()) { return; } QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); + SetClientConnectionId(TestConnectionId(1)); + connection_.CreateConnectionIdManager(); connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); // Prevent packets from being coalesced. EXPECT_CALL(visitor_, GetHandshakeState()) .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); QuicConnectionPeer::SetAddressValidated(&connection_); + + QuicConnectionId client_cid0 = connection_.client_connection_id(); + QuicConnectionId client_cid1 = TestConnectionId(2); + QuicConnectionId server_cid0 = connection_.connection_id(); + QuicConnectionId server_cid1; + // Sends new server CID to client. + EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_)) + .WillOnce( + Invoke([&](const QuicConnectionId& cid) { server_cid1 = cid; })); + EXPECT_CALL(visitor_, SendNewConnectionId(_)); connection_.OnHandshakeComplete(); + // Receives new client CID from client. + QuicNewConnectionIdFrame new_cid_frame; + new_cid_frame.connection_id = client_cid1; + new_cid_frame.sequence_number = 1u; + new_cid_frame.retire_prior_to = 0u; + connection_.OnNewConnectionIdFrame(new_cid_frame); + auto* packet_creator = QuicConnectionPeer::GetPacketCreator(&connection_); + ASSERT_EQ(packet_creator->GetDestinationConnectionId(), client_cid0); + ASSERT_EQ(packet_creator->GetSourceConnectionId(), server_cid0); // Clear direct_peer_address. QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); @@ -2008,13 +2117,8 @@ TEST_P(QuicConnectionTest, ReversePathValidationFailureAtServer) { EXPECT_CALL(visitor_, OnStreamFrame(_)) .WillOnce(Invoke( [=]() { EXPECT_EQ(kPeerAddress, connection_.peer_address()); })) - .WillOnce(Invoke([=]() { - EXPECT_EQ((GetQuicReloadableFlag(quic_start_peer_migration_earlier) || - !GetParam().version.HasIetfQuicFrames() - ? kNewPeerAddress - : kPeerAddress), - connection_.peer_address()); - })); + .WillOnce(Invoke( + [=]() { EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); })); QuicFrames frames; frames.push_back(QuicFrame(frame1_)); ProcessFramesPacketWithAddresses(frames, kSelfAddress, kPeerAddress, @@ -2033,6 +2137,7 @@ TEST_P(QuicConnectionTest, ReversePathValidationFailureAtServer) { frames2.push_back(QuicFrame(frame2_)); QuicPaddingFrame padding; frames2.push_back(QuicFrame(padding)); + peer_creator_.SetServerConnectionId(server_cid1); ProcessFramesPacketWithAddresses(frames2, kSelfAddress, kNewPeerAddress, ENCRYPTION_FORWARD_SECURE); EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); @@ -2046,6 +2151,15 @@ TEST_P(QuicConnectionTest, ReversePathValidationFailureAtServer) { EXPECT_EQ(kNewPeerAddress, writer_->last_write_peer_address()); EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); + const auto* default_path = QuicConnectionPeer::GetDefaultPath(&connection_); + const auto* alternative_path = + QuicConnectionPeer::GetAlternativePath(&connection_); + EXPECT_EQ(default_path->client_connection_id, client_cid1); + EXPECT_EQ(default_path->server_connection_id, server_cid1); + EXPECT_EQ(alternative_path->client_connection_id, client_cid0); + EXPECT_EQ(alternative_path->server_connection_id, server_cid0); + EXPECT_EQ(packet_creator->GetDestinationConnectionId(), client_cid1); + EXPECT_EQ(packet_creator->GetSourceConnectionId(), server_cid1); for (size_t i = 0; i < QuicPathValidator::kMaxRetryTimes; ++i) { clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs)); @@ -2072,6 +2186,19 @@ TEST_P(QuicConnectionTest, ReversePathValidationFailureAtServer) { EXPECT_EQ(connection_.sent_packet_manager().GetSendAlgorithm(), send_algorithm_); EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + + // Verify that default_path_ is reverted and alternative_path_ is cleared. + EXPECT_EQ(default_path->client_connection_id, client_cid0); + EXPECT_EQ(default_path->server_connection_id, server_cid0); + EXPECT_TRUE(alternative_path->server_connection_id.IsEmpty()); + EXPECT_FALSE(alternative_path->stateless_reset_token_received); + auto* retire_peer_issued_cid_alarm = + connection_.GetRetirePeerIssuedConnectionIdAlarm(); + ASSERT_TRUE(retire_peer_issued_cid_alarm->IsSet()); + EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/1u)); + retire_peer_issued_cid_alarm->Fire(); + EXPECT_EQ(packet_creator->GetDestinationConnectionId(), client_cid0); + EXPECT_EQ(packet_creator->GetSourceConnectionId(), server_cid0); } TEST_P(QuicConnectionTest, ReceivePathProbeWithNoAddressChangeAtServer) { @@ -2184,10 +2311,12 @@ class TestQuicPathValidationContext : public QuicPathValidationContext { class TestValidationResultDelegate : public QuicPathValidator::ResultDelegate { public: - TestValidationResultDelegate(const QuicSocketAddress& expected_self_address, + TestValidationResultDelegate(QuicConnection* connection, + const QuicSocketAddress& expected_self_address, const QuicSocketAddress& expected_peer_address, bool* success) : QuicPathValidator::ResultDelegate(), + connection_(connection), expected_self_address_(expected_self_address), expected_peer_address_(expected_peer_address), success_(success) {} @@ -2202,10 +2331,14 @@ class TestValidationResultDelegate : public QuicPathValidator::ResultDelegate { std::unique_ptr<QuicPathValidationContext> context) override { EXPECT_EQ(expected_self_address_, context->self_address()); EXPECT_EQ(expected_peer_address_, context->peer_address()); + if (connection_->perspective() == Perspective::IS_CLIENT) { + connection_->OnPathValidationFailureAtClient(); + } *success_ = false; } private: + QuicConnection* connection_; QuicSocketAddress expected_self_address_; QuicSocketAddress expected_peer_address_; bool* success_; @@ -2291,7 +2424,8 @@ TEST_P(QuicConnectionTest, ReceivePathProbingAtServer) { std::make_unique<TestQuicPathValidationContext>( connection_.self_address(), kNewPeerAddress, writer_.get()), std::make_unique<TestValidationResultDelegate>( - connection_.self_address(), kNewPeerAddress, &success)); + &connection_, connection_.self_address(), kNewPeerAddress, + &success)); } EXPECT_EQ((connection_.validate_client_address() ? 2 : 3) * bytes_sent, QuicConnectionPeer::BytesSentOnAlternativePath(&connection_)); @@ -2836,8 +2970,7 @@ TEST_P(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) { TEST_P(QuicConnectionTest, RejectUnencryptedStreamData) { // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. if (!IsDefaultTestConfiguration() || - (GetQuicReloadableFlag(quic_reject_unexpected_ietf_frame_types) && - VersionHasIetfQuicFrames(version().transport_version))) { + VersionHasIetfQuicFrames(version().transport_version)) { return; } @@ -3169,20 +3302,6 @@ TEST_P(QuicConnectionTest, TooManySentPackets) { ProcessFramePacket(QuicFrame(QuicPingFrame())); - if (!GetQuicReloadableFlag( - quic_close_connection_with_too_many_outstanding_packets)) { - // When the flag is false, the ping packet processed above shouldn't cause - // the connection to close. But the ack packet below will. - EXPECT_TRUE(connection_.connected()); - - // Ack packet 1, which leaves more than the limit outstanding. - EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); - - // Nack the first packet and ack the rest, leaving a huge gap. - QuicAckFrame frame1 = ConstructAckFrame(num_packets, 1); - ProcessAckPacket(&frame1); - } - TestConnectionCloseQuicErrorCode(QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS); } @@ -3390,15 +3509,15 @@ TEST_P(QuicConnectionTest, FramePackingNonCryptoThenCrypto) { EXPECT_EQ(0u, connection_.NumQueuedPackets()); EXPECT_FALSE(connection_.HasQueuedData()); - // Parse the last packet and ensure it's the crypto stream frame. - EXPECT_EQ(2u, writer_->frame_count()); - ASSERT_EQ(1u, writer_->padding_frames().size()); + // Parse the last packet and ensure it contains a crypto stream frame. + EXPECT_LE(2u, writer_->frame_count()); + ASSERT_LE(1u, writer_->padding_frames().size()); if (!QuicVersionUsesCryptoFrames(connection_.transport_version())) { ASSERT_EQ(1u, writer_->stream_frames().size()); EXPECT_EQ(QuicUtils::GetCryptoStreamId(connection_.transport_version()), writer_->stream_frames()[0]->stream_id); } else { - EXPECT_EQ(1u, writer_->crypto_frames().size()); + EXPECT_LE(1u, writer_->crypto_frames().size()); } } @@ -7081,7 +7200,7 @@ TEST_P(QuicConnectionTest, CheckSendStats) { stats.bytes_retransmitted); EXPECT_EQ(3u, stats.packets_retransmitted); EXPECT_EQ(1u, stats.rto_count); - EXPECT_EQ(kDefaultMaxPacketSize, stats.max_packet_size); + EXPECT_EQ(kDefaultMaxPacketSize, stats.egress_mtu); } TEST_P(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) { @@ -7787,8 +7906,7 @@ TEST_P(QuicConnectionTest, ServerReceivesChloOnNonCryptoStream) { EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)); ForceProcessFramePacket(QuicFrame(frame1_)); - if (GetQuicReloadableFlag(quic_reject_unexpected_ietf_frame_types) && - VersionHasIetfQuicFrames(version().transport_version)) { + if (VersionHasIetfQuicFrames(version().transport_version)) { // INITIAL packet should not contain STREAM frame. TestConnectionCloseQuicErrorCode(IETF_QUIC_PROTOCOL_VIOLATION); } else { @@ -7810,8 +7928,7 @@ TEST_P(QuicConnectionTest, ClientReceivesRejOnNonCryptoStream) { EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)); ForceProcessFramePacket(QuicFrame(frame1_)); - if (GetQuicReloadableFlag(quic_reject_unexpected_ietf_frame_types) && - VersionHasIetfQuicFrames(version().transport_version)) { + if (VersionHasIetfQuicFrames(version().transport_version)) { // INITIAL packet should not contain STREAM frame. TestConnectionCloseQuicErrorCode(IETF_QUIC_PROTOCOL_VIOLATION); } else { @@ -8899,7 +9016,7 @@ TEST_P(QuicConnectionTest, ClientResponseToPathChallengeOnAlternativeSocket) { std::make_unique<TestQuicPathValidationContext>( kNewSelfAddress, connection_.peer_address(), &new_writer), std::make_unique<TestValidationResultDelegate>( - kNewSelfAddress, connection_.peer_address(), &success)); + &connection_, kNewSelfAddress, connection_.peer_address(), &success)); // Receiving a PATH_CHALLENGE on the alternative path. Response to this // PATH_CHALLENGE should be sent via the alternative writer. @@ -9300,7 +9417,7 @@ TEST_P(QuicConnectionTest, ValidClientConnectionId) { return; } EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - connection_.set_client_connection_id(TestConnectionId(0x33)); + SetClientConnectionId(TestConnectionId(0x33)); QuicPacketHeader header = ConstructPacketHeader(1, ENCRYPTION_FORWARD_SECURE); header.destination_connection_id = TestConnectionId(0x33); header.destination_connection_id_included = CONNECTION_ID_PRESENT; @@ -9328,7 +9445,7 @@ TEST_P(QuicConnectionTest, InvalidClientConnectionId) { if (!framer_.version().SupportsClientConnectionIds()) { return; } - connection_.set_client_connection_id(TestConnectionId(0x33)); + SetClientConnectionId(TestConnectionId(0x33)); QuicPacketHeader header = ConstructPacketHeader(1, ENCRYPTION_FORWARD_SECURE); header.destination_connection_id = TestConnectionId(0xbad); header.destination_connection_id_included = CONNECTION_ID_PRESENT; @@ -11162,8 +11279,7 @@ TEST_P(QuicConnectionTest, ProcessUndecryptablePacketsBasedOnEncryptionLevel) { std::make_unique<TaggingEncrypter>(0x01)); connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE); // Verify all ENCRYPTION_HANDSHAKE packets get processed. - if (!GetQuicReloadableFlag(quic_reject_unexpected_ietf_frame_types) || - !VersionHasIetfQuicFrames(version().transport_version)) { + if (!VersionHasIetfQuicFrames(version().transport_version)) { EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(6); } connection_.GetProcessUndecryptablePacketsAlarm()->Fire(); @@ -11750,7 +11866,7 @@ TEST_P(QuicConnectionTest, PathValidationOnNewSocketSuccess) { std::make_unique<TestQuicPathValidationContext>( kNewSelfAddress, connection_.peer_address(), &new_writer), std::make_unique<TestValidationResultDelegate>( - kNewSelfAddress, connection_.peer_address(), &success)); + &connection_, kNewSelfAddress, connection_.peer_address(), &success)); EXPECT_EQ(0u, writer_->packets_write_attempts()); QuicFrames frames; @@ -11784,30 +11900,39 @@ TEST_P(QuicConnectionTest, NewPathValidationCancelsPreviousOne) { std::make_unique<TestQuicPathValidationContext>( kNewSelfAddress, connection_.peer_address(), &new_writer), std::make_unique<TestValidationResultDelegate>( - kNewSelfAddress, connection_.peer_address(), &success)); + &connection_, kNewSelfAddress, connection_.peer_address(), &success)); EXPECT_EQ(0u, writer_->packets_write_attempts()); // Start another path validation request. const QuicSocketAddress kNewSelfAddress2(QuicIpAddress::Any4(), 12346); EXPECT_NE(kNewSelfAddress2, connection_.self_address()); TestPacketWriter new_writer2(version(), &clock_, Perspective::IS_CLIENT); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) - .Times(AtLeast(1u)) - .WillOnce(Invoke([&]() { - EXPECT_EQ(1u, new_writer2.packets_write_attempts()); - EXPECT_EQ(1u, new_writer2.path_challenge_frames().size()); - EXPECT_EQ(1u, new_writer2.padding_frames().size()); - EXPECT_EQ(kNewSelfAddress2.host(), - new_writer2.last_write_source_address()); - })); + if (!connection_.connection_migration_use_new_cid()) { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .Times(AtLeast(1u)) + .WillOnce(Invoke([&]() { + EXPECT_EQ(1u, new_writer2.packets_write_attempts()); + EXPECT_EQ(1u, new_writer2.path_challenge_frames().size()); + EXPECT_EQ(1u, new_writer2.padding_frames().size()); + EXPECT_EQ(kNewSelfAddress2.host(), + new_writer2.last_write_source_address()); + })); + } bool success2 = false; connection_.ValidatePath( std::make_unique<TestQuicPathValidationContext>( kNewSelfAddress2, connection_.peer_address(), &new_writer2), std::make_unique<TestValidationResultDelegate>( - kNewSelfAddress2, connection_.peer_address(), &success2)); + &connection_, kNewSelfAddress2, connection_.peer_address(), + &success2)); EXPECT_FALSE(success); - EXPECT_TRUE(connection_.HasPendingPathValidation()); + if (connection_.connection_migration_use_new_cid()) { + // There is no pening path validation as there is no available connection + // ID. + EXPECT_FALSE(connection_.HasPendingPathValidation()); + } else { + EXPECT_TRUE(connection_.HasPendingPathValidation()); + } } // Regression test for b/182571515. @@ -11825,12 +11950,12 @@ TEST_P(QuicConnectionTest, PathValidationRetry) { EXPECT_EQ(1u, writer_->padding_frames().size()); })); bool success = true; - connection_.ValidatePath( - std::make_unique<TestQuicPathValidationContext>( - connection_.self_address(), connection_.peer_address(), - writer_.get()), - std::make_unique<TestValidationResultDelegate>( - connection_.self_address(), connection_.peer_address(), &success)); + connection_.ValidatePath(std::make_unique<TestQuicPathValidationContext>( + connection_.self_address(), + connection_.peer_address(), writer_.get()), + std::make_unique<TestValidationResultDelegate>( + &connection_, connection_.self_address(), + connection_.peer_address(), &success)); EXPECT_EQ(1u, writer_->packets_write_attempts()); EXPECT_TRUE(connection_.HasPendingPathValidation()); @@ -11872,7 +11997,7 @@ TEST_P(QuicConnectionTest, PathValidationReceivesStatelessReset) { std::make_unique<TestQuicPathValidationContext>( kNewSelfAddress, connection_.peer_address(), &new_writer), std::make_unique<TestValidationResultDelegate>( - kNewSelfAddress, connection_.peer_address(), &success)); + &connection_, kNewSelfAddress, connection_.peer_address(), &success)); EXPECT_EQ(0u, writer_->packets_write_attempts()); EXPECT_TRUE(connection_.HasPendingPathValidation()); @@ -11916,7 +12041,7 @@ TEST_P(QuicConnectionTest, SendPathChallengeUsingBlockedNewSocket) { std::make_unique<TestQuicPathValidationContext>( kNewSelfAddress, connection_.peer_address(), &new_writer), std::make_unique<TestValidationResultDelegate>( - kNewSelfAddress, connection_.peer_address(), &success)); + &connection_, kNewSelfAddress, connection_.peer_address(), &success)); EXPECT_EQ(0u, writer_->packets_write_attempts()); new_writer.SetWritable(); @@ -11976,7 +12101,8 @@ TEST_P(QuicConnectionTest, SendPathChallengeUsingBlockedDefaultSocket) { std::make_unique<TestQuicPathValidationContext>( connection_.self_address(), kNewPeerAddress, writer_.get()), std::make_unique<TestValidationResultDelegate>( - connection_.self_address(), kNewPeerAddress, &success)); + &connection_, connection_.self_address(), kNewPeerAddress, + &success)); } EXPECT_EQ(1u, writer_->packets_write_attempts()); @@ -12020,7 +12146,7 @@ TEST_P(QuicConnectionTest, SendPathChallengeFailOnNewSocket) { std::make_unique<TestQuicPathValidationContext>( kNewSelfAddress, connection_.peer_address(), &new_writer), std::make_unique<TestValidationResultDelegate>( - kNewSelfAddress, connection_.peer_address(), &success)); + &connection_, kNewSelfAddress, connection_.peer_address(), &success)); EXPECT_EQ(1u, new_writer.packets_write_attempts()); EXPECT_EQ(1u, new_writer.path_challenge_frames().size()); EXPECT_EQ(1u, new_writer.padding_frames().size()); @@ -12052,12 +12178,12 @@ TEST_P(QuicConnectionTest, SendPathChallengeFailOnDefaultPath) { // packet creator. bool success = false; QuicConnection::ScopedPacketFlusher flusher(&connection_); - connection_.ValidatePath( - std::make_unique<TestQuicPathValidationContext>( - connection_.self_address(), connection_.peer_address(), - writer_.get()), - std::make_unique<TestValidationResultDelegate>( - connection_.self_address(), connection_.peer_address(), &success)); + connection_.ValidatePath(std::make_unique<TestQuicPathValidationContext>( + connection_.self_address(), + connection_.peer_address(), writer_.get()), + std::make_unique<TestValidationResultDelegate>( + &connection_, connection_.self_address(), + connection_.peer_address(), &success)); } EXPECT_EQ(1u, writer_->packets_write_attempts()); EXPECT_EQ(1u, writer_->path_challenge_frames().size()); @@ -12089,7 +12215,7 @@ TEST_P(QuicConnectionTest, SendPathChallengeFailOnAlternativePeerAddress) { std::make_unique<TestQuicPathValidationContext>( connection_.self_address(), kNewPeerAddress, writer_.get()), std::make_unique<TestValidationResultDelegate>( - connection_.self_address(), kNewPeerAddress, &success)); + &connection_, connection_.self_address(), kNewPeerAddress, &success)); EXPECT_EQ(1u, writer_->packets_write_attempts()); EXPECT_FALSE(connection_.HasPendingPathValidation()); @@ -12120,7 +12246,7 @@ TEST_P(QuicConnectionTest, std::make_unique<TestQuicPathValidationContext>( connection_.self_address(), kNewPeerAddress, writer_.get()), std::make_unique<TestValidationResultDelegate>( - connection_.self_address(), kNewPeerAddress, &success)); + &connection_, connection_.self_address(), kNewPeerAddress, &success)); EXPECT_TRUE(connection_.HasPendingPathValidation()); // Connection shouldn't be closed. EXPECT_TRUE(connection_.connected()); @@ -12391,7 +12517,12 @@ TEST_P(QuicConnectionTest, FailToWritePathResponse) { ProcessFramesPacketWithAddresses(frames, kSelfAddress, kNewPeerAddress, ENCRYPTION_FORWARD_SECURE); - EXPECT_EQ( + if (GetQuicReloadableFlag(quic_drop_unsent_path_response)) { + EXPECT_EQ(0u, QuicConnectionPeer::NumPendingPathChallengesToResponse( + &connection_)); + return; + } + ASSERT_EQ( 1u, QuicConnectionPeer::NumPendingPathChallengesToResponse(&connection_)); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AtLeast(1)); @@ -13444,6 +13575,9 @@ TEST_P(QuicConnectionTest, ServerHelloGetsReordered) { } TEST_P(QuicConnectionTest, MigratePath) { + EXPECT_CALL(visitor_, GetHandshakeState()) + .Times(testing::AtMost(1)) + .WillOnce(Return(HANDSHAKE_CONFIRMED)); EXPECT_CALL(visitor_, OnPathDegrading()); connection_.OnPathDegradingDetected(); const QuicSocketAddress kNewSelfAddress(QuicIpAddress::Any4(), 12345); @@ -13472,7 +13606,7 @@ TEST_P(QuicConnectionTest, MigrateToNewPathDuringProbing) { std::make_unique<TestQuicPathValidationContext>( kNewSelfAddress, connection_.peer_address(), &new_writer), std::make_unique<TestValidationResultDelegate>( - kNewSelfAddress, connection_.peer_address(), &success)); + &connection_, kNewSelfAddress, connection_.peer_address(), &success)); EXPECT_TRUE(connection_.HasPendingPathValidation()); EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath( &connection_, kNewSelfAddress, connection_.peer_address())); @@ -13568,7 +13702,6 @@ TEST_P(QuicConnectionTest, NewTokenFrameInstigateAcks) { if (!version().HasIetfQuicFrames()) { return; } - SetQuicReloadableFlag(quic_enable_token_based_address_validation, true); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); QuicNewTokenFrame* new_token = new QuicNewTokenFrame(); @@ -13583,7 +13716,6 @@ TEST_P(QuicConnectionTest, ServerClosesConnectionOnNewTokenFrame) { if (!version().HasIetfQuicFrames()) { return; } - SetQuicReloadableFlag(quic_enable_token_based_address_validation, true); set_perspective(Perspective::IS_SERVER); QuicNewTokenFrame* new_token = new QuicNewTokenFrame(); EXPECT_CALL(visitor_, OnNewTokenReceived(_)).Times(0); @@ -13686,8 +13818,7 @@ TEST_P(QuicConnectionTest, // Regression test for b/177312785 TEST_P(QuicConnectionTest, PeerMigrateBeforeHandshakeConfirm) { - if (!VersionHasIetfQuicFrames(version().transport_version) || - !GetQuicReloadableFlag(quic_start_peer_migration_earlier)) { + if (!VersionHasIetfQuicFrames(version().transport_version)) { return; } set_perspective(Perspective::IS_SERVER); @@ -13755,11 +13886,34 @@ TEST_P(QuicConnectionTest, TryToFlushAckWithAckQueued) { TEST_P(QuicConnectionTest, PathChallengeBeforePeerIpAddressChangeAtServer) { set_perspective(Perspective::IS_SERVER); - if (!connection_.validate_client_address()) { + if (!connection_.connection_migration_use_new_cid()) { return; } PathProbeTestInit(Perspective::IS_SERVER); + SetClientConnectionId(TestConnectionId(1)); + connection_.CreateConnectionIdManager(); + QuicConnectionId server_cid0 = connection_.connection_id(); + QuicConnectionId client_cid0 = connection_.client_connection_id(); + QuicConnectionId client_cid1 = TestConnectionId(2); + QuicConnectionId server_cid1; + // Sends new server CID to client. + EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_)) + .WillOnce( + Invoke([&](const QuicConnectionId& cid) { server_cid1 = cid; })); + EXPECT_CALL(visitor_, SendNewConnectionId(_)); + connection_.MaybeSendConnectionIdToClient(); + // Receives new client CID from client. + QuicNewConnectionIdFrame new_cid_frame; + new_cid_frame.connection_id = client_cid1; + new_cid_frame.sequence_number = 1u; + new_cid_frame.retire_prior_to = 0u; + connection_.OnNewConnectionIdFrame(new_cid_frame); + auto* packet_creator = QuicConnectionPeer::GetPacketCreator(&connection_); + ASSERT_EQ(packet_creator->GetDestinationConnectionId(), client_cid0); + ASSERT_EQ(packet_creator->GetSourceConnectionId(), server_cid0); + + peer_creator_.SetServerConnectionId(server_cid1); const QuicSocketAddress kNewPeerAddress = QuicSocketAddress(QuicIpAddress::Loopback4(), /*port=*/23456); QuicPathFrameBuffer path_challenge_payload{0, 1, 2, 3, 4, 5, 6, 7}; @@ -13783,6 +13937,15 @@ TEST_P(QuicConnectionTest, PathChallengeBeforePeerIpAddressChangeAtServer) { EXPECT_EQ(kPeerAddress, connection_.peer_address()); EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); EXPECT_TRUE(connection_.HasPendingPathValidation()); + const auto* default_path = QuicConnectionPeer::GetDefaultPath(&connection_); + const auto* alternative_path = + QuicConnectionPeer::GetAlternativePath(&connection_); + EXPECT_EQ(default_path->client_connection_id, client_cid0); + EXPECT_EQ(default_path->server_connection_id, server_cid0); + EXPECT_EQ(alternative_path->client_connection_id, client_cid1); + EXPECT_EQ(alternative_path->server_connection_id, server_cid1); + EXPECT_EQ(packet_creator->GetDestinationConnectionId(), client_cid0); + EXPECT_EQ(packet_creator->GetSourceConnectionId(), server_cid0); // Process another packet with a different peer address on server side will // start connection migration. @@ -13819,6 +13982,14 @@ TEST_P(QuicConnectionTest, PathChallengeBeforePeerIpAddressChangeAtServer) { EXPECT_CALL(*send_algorithm_, InRecovery()).Times(AnyNumber()); EXPECT_CALL(*send_algorithm_, PopulateConnectionStats(_)).Times(AnyNumber()); connection_.SetSendAlgorithm(send_algorithm_); + EXPECT_EQ(default_path->client_connection_id, client_cid1); + EXPECT_EQ(default_path->server_connection_id, server_cid1); + // The previous default path is kept as alternative path before reverse path + // validation finishes. + EXPECT_EQ(alternative_path->client_connection_id, client_cid0); + EXPECT_EQ(alternative_path->server_connection_id, server_cid0); + EXPECT_EQ(packet_creator->GetDestinationConnectionId(), client_cid1); + EXPECT_EQ(packet_creator->GetSourceConnectionId(), server_cid1); EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); @@ -13842,6 +14013,15 @@ TEST_P(QuicConnectionTest, PathChallengeBeforePeerIpAddressChangeAtServer) { ProcessFramesPacketWithAddresses(frames3, kSelfAddress, kNewPeerAddress, ENCRYPTION_FORWARD_SECURE); EXPECT_EQ(NO_CHANGE, connection_.active_effective_peer_migration_type()); + // Verify that alternative_path_ is cleared and the peer CID is retired. + EXPECT_TRUE(alternative_path->client_connection_id.IsEmpty()); + EXPECT_TRUE(alternative_path->server_connection_id.IsEmpty()); + EXPECT_FALSE(alternative_path->stateless_reset_token_received); + auto* retire_peer_issued_cid_alarm = + connection_.GetRetirePeerIssuedConnectionIdAlarm(); + ASSERT_TRUE(retire_peer_issued_cid_alarm->IsSet()); + EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/0u)); + retire_peer_issued_cid_alarm->Fire(); // Verify the anti-amplification limit is lifted by sending a packet larger // than the anti-amplification limit. @@ -13853,12 +14033,25 @@ TEST_P(QuicConnectionTest, PathChallengeBeforePeerIpAddressChangeAtServer) { TEST_P(QuicConnectionTest, PathValidationSucceedsBeforePeerIpAddressChangeAtServer) { set_perspective(Perspective::IS_SERVER); - if (!connection_.validate_client_address()) { + if (!connection_.connection_migration_use_new_cid()) { return; } PathProbeTestInit(Perspective::IS_SERVER); + connection_.CreateConnectionIdManager(); + + QuicConnectionId server_cid0 = connection_.connection_id(); + QuicConnectionId server_cid1; + // Sends new server CID to client. + EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_)) + .WillOnce( + Invoke([&](const QuicConnectionId& cid) { server_cid1 = cid; })); + EXPECT_CALL(visitor_, SendNewConnectionId(_)); + connection_.MaybeSendConnectionIdToClient(); + auto* packet_creator = QuicConnectionPeer::GetPacketCreator(&connection_); + ASSERT_EQ(packet_creator->GetSourceConnectionId(), server_cid0); // Receive probing packet with new peer address. + peer_creator_.SetServerConnectionId(server_cid1); const QuicSocketAddress kNewPeerAddress(QuicIpAddress::Loopback4(), /*port=*/23456); QuicPathFrameBuffer payload; @@ -13883,6 +14076,12 @@ TEST_P(QuicConnectionTest, ProcessFramesPacketWithAddresses(frames1, kSelfAddress, kNewPeerAddress, ENCRYPTION_FORWARD_SECURE); EXPECT_TRUE(connection_.HasPendingPathValidation()); + const auto* default_path = QuicConnectionPeer::GetDefaultPath(&connection_); + const auto* alternative_path = + QuicConnectionPeer::GetAlternativePath(&connection_); + EXPECT_EQ(default_path->server_connection_id, server_cid0); + EXPECT_EQ(alternative_path->server_connection_id, server_cid1); + EXPECT_EQ(packet_creator->GetSourceConnectionId(), server_cid0); // Receive PATH_RESPONSE should mark the new peer address validated. QuicFrames frames3; @@ -13920,6 +14119,12 @@ TEST_P(QuicConnectionTest, EXPECT_NE(connection_.sent_packet_manager().GetSendAlgorithm(), send_algorithm_); + EXPECT_EQ(default_path->server_connection_id, server_cid1); + EXPECT_EQ(packet_creator->GetSourceConnectionId(), server_cid1); + // Verify that alternative_path_ is cleared. + EXPECT_TRUE(alternative_path->server_connection_id.IsEmpty()); + EXPECT_FALSE(alternative_path->stateless_reset_token_received); + // Switch to use the mock send algorithm. send_algorithm_ = new StrictMock<MockSendAlgorithm>(); EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true)); @@ -14005,6 +14210,255 @@ TEST_P(QuicConnectionTest, } TEST_P(QuicConnectionTest, + PathValidationFailedOnClientDueToLackOfServerConnectionId) { + QuicConfig config; + config.SetConnectionOptionsToSend({kRVCM}); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + if (!connection_.connection_migration_use_new_cid()) { + return; + } + PathProbeTestInit(Perspective::IS_CLIENT, + /*receive_new_server_connection_id=*/false); + + const QuicSocketAddress kNewSelfAddress(QuicIpAddress::Loopback4(), + /*port=*/34567); + + bool success; + connection_.ValidatePath( + std::make_unique<TestQuicPathValidationContext>( + kNewSelfAddress, connection_.peer_address(), writer_.get()), + std::make_unique<TestValidationResultDelegate>( + &connection_, kNewSelfAddress, connection_.peer_address(), &success)); + + EXPECT_FALSE(success); +} + +TEST_P(QuicConnectionTest, + PathValidationFailedOnClientDueToLackOfClientConnectionIdTheSecondTime) { + QuicConfig config; + config.SetConnectionOptionsToSend({kRVCM}); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + if (!connection_.connection_migration_use_new_cid()) { + return; + } + PathProbeTestInit(Perspective::IS_CLIENT, + /*receive_new_server_connection_id=*/false); + SetClientConnectionId(TestConnectionId(1)); + + // Make sure server connection ID is available for the 1st validation. + QuicConnectionId server_cid0 = connection_.connection_id(); + QuicConnectionId server_cid1 = TestConnectionId(2); + QuicConnectionId server_cid2 = TestConnectionId(4); + QuicConnectionId client_cid1; + QuicNewConnectionIdFrame frame1; + frame1.connection_id = server_cid1; + frame1.sequence_number = 1u; + frame1.retire_prior_to = 0u; + frame1.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame1.connection_id); + connection_.OnNewConnectionIdFrame(frame1); + const auto* packet_creator = + QuicConnectionPeer::GetPacketCreator(&connection_); + ASSERT_EQ(packet_creator->GetDestinationConnectionId(), server_cid0); + + // Client will issue a new client connection ID to server. + EXPECT_CALL(visitor_, SendNewConnectionId(_)) + .WillOnce(Invoke([&](const QuicNewConnectionIdFrame& frame) { + client_cid1 = frame.connection_id; + })); + + const QuicSocketAddress kSelfAddress1(QuicIpAddress::Any4(), 12345); + ASSERT_NE(kSelfAddress1, connection_.self_address()); + bool success1; + connection_.ValidatePath( + std::make_unique<TestQuicPathValidationContext>( + kSelfAddress1, connection_.peer_address(), writer_.get()), + std::make_unique<TestValidationResultDelegate>( + &connection_, kSelfAddress1, connection_.peer_address(), &success1)); + + // Migrate upon 1st validation success. + TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT); + ASSERT_TRUE(connection_.MigratePath(kSelfAddress1, connection_.peer_address(), + &new_writer, /*owns_writer=*/false)); + QuicConnectionPeer::RetirePeerIssuedConnectionIdsNoLongerOnPath(&connection_); + const auto* default_path = QuicConnectionPeer::GetDefaultPath(&connection_); + EXPECT_EQ(default_path->client_connection_id, client_cid1); + EXPECT_EQ(default_path->server_connection_id, server_cid1); + EXPECT_EQ(default_path->stateless_reset_token, frame1.stateless_reset_token); + EXPECT_TRUE(default_path->stateless_reset_token_received); + const auto* alternative_path = + QuicConnectionPeer::GetAlternativePath(&connection_); + EXPECT_TRUE(alternative_path->client_connection_id.IsEmpty()); + EXPECT_TRUE(alternative_path->server_connection_id.IsEmpty()); + EXPECT_FALSE(alternative_path->stateless_reset_token_received); + ASSERT_EQ(packet_creator->GetDestinationConnectionId(), server_cid1); + + // Client will retire server connection ID on old default_path. + auto* retire_peer_issued_cid_alarm = + connection_.GetRetirePeerIssuedConnectionIdAlarm(); + ASSERT_TRUE(retire_peer_issued_cid_alarm->IsSet()); + EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/0u)); + retire_peer_issued_cid_alarm->Fire(); + + // Another server connection ID is available to client. + QuicNewConnectionIdFrame frame2; + frame2.connection_id = server_cid2; + frame2.sequence_number = 2u; + frame2.retire_prior_to = 1u; + frame2.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame2.connection_id); + connection_.OnNewConnectionIdFrame(frame2); + + const QuicSocketAddress kSelfAddress2(QuicIpAddress::Loopback4(), + /*port=*/45678); + bool success2; + connection_.ValidatePath( + std::make_unique<TestQuicPathValidationContext>( + kSelfAddress2, connection_.peer_address(), writer_.get()), + std::make_unique<TestValidationResultDelegate>( + &connection_, kSelfAddress2, connection_.peer_address(), &success2)); + // Since server does not retire any client connection ID yet, 2nd validation + // would fail due to lack of client connection ID. + EXPECT_FALSE(success2); +} + +TEST_P(QuicConnectionTest, ServerConnectionIdRetiredUponPathValidationFailure) { + QuicConfig config; + config.SetConnectionOptionsToSend({kRVCM}); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + if (!connection_.connection_migration_use_new_cid()) { + return; + } + PathProbeTestInit(Perspective::IS_CLIENT); + + // Make sure server connection ID is available for validation. + QuicNewConnectionIdFrame frame; + frame.connection_id = TestConnectionId(2); + frame.sequence_number = 1u; + frame.retire_prior_to = 0u; + frame.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame.connection_id); + connection_.OnNewConnectionIdFrame(frame); + + const QuicSocketAddress kNewSelfAddress(QuicIpAddress::Loopback4(), + /*port=*/34567); + bool success; + connection_.ValidatePath( + std::make_unique<TestQuicPathValidationContext>( + kNewSelfAddress, connection_.peer_address(), writer_.get()), + std::make_unique<TestValidationResultDelegate>( + &connection_, kNewSelfAddress, connection_.peer_address(), &success)); + + auto* path_validator = QuicConnectionPeer::path_validator(&connection_); + path_validator->CancelPathValidation(); + QuicConnectionPeer::RetirePeerIssuedConnectionIdsNoLongerOnPath(&connection_); + EXPECT_FALSE(success); + const auto* alternative_path = + QuicConnectionPeer::GetAlternativePath(&connection_); + EXPECT_TRUE(alternative_path->client_connection_id.IsEmpty()); + EXPECT_TRUE(alternative_path->server_connection_id.IsEmpty()); + EXPECT_FALSE(alternative_path->stateless_reset_token_received); + + // Client will retire server connection ID on alternative_path. + auto* retire_peer_issued_cid_alarm = + connection_.GetRetirePeerIssuedConnectionIdAlarm(); + ASSERT_TRUE(retire_peer_issued_cid_alarm->IsSet()); + EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/1u)); + retire_peer_issued_cid_alarm->Fire(); +} + +TEST_P(QuicConnectionTest, + MigratePathDirectlyFailedDueToLackOfServerConnectionId) { + QuicConfig config; + config.SetConnectionOptionsToSend({kRVCM}); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + if (!connection_.connection_migration_use_new_cid()) { + return; + } + PathProbeTestInit(Perspective::IS_CLIENT, + /*receive_new_server_connection_id=*/false); + const QuicSocketAddress kSelfAddress1(QuicIpAddress::Any4(), 12345); + ASSERT_NE(kSelfAddress1, connection_.self_address()); + + TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT); + ASSERT_FALSE(connection_.MigratePath(kSelfAddress1, + connection_.peer_address(), &new_writer, + /*owns_writer=*/false)); +} + +TEST_P(QuicConnectionTest, + MigratePathDirectlyFailedDueToLackOfClientConnectionIdTheSecondTime) { + QuicConfig config; + config.SetConnectionOptionsToSend({kRVCM}); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + if (!connection_.connection_migration_use_new_cid()) { + return; + } + PathProbeTestInit(Perspective::IS_CLIENT, + /*receive_new_server_connection_id=*/false); + SetClientConnectionId(TestConnectionId(1)); + + // Make sure server connection ID is available for the 1st migration. + QuicNewConnectionIdFrame frame1; + frame1.connection_id = TestConnectionId(2); + frame1.sequence_number = 1u; + frame1.retire_prior_to = 0u; + frame1.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame1.connection_id); + connection_.OnNewConnectionIdFrame(frame1); + + // Client will issue a new client connection ID to server. + QuicConnectionId new_client_connection_id; + EXPECT_CALL(visitor_, SendNewConnectionId(_)) + .WillOnce(Invoke([&](const QuicNewConnectionIdFrame& frame) { + new_client_connection_id = frame.connection_id; + })); + + // 1st migration is successful. + const QuicSocketAddress kSelfAddress1(QuicIpAddress::Any4(), 12345); + ASSERT_NE(kSelfAddress1, connection_.self_address()); + TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT); + ASSERT_TRUE(connection_.MigratePath(kSelfAddress1, connection_.peer_address(), + &new_writer, + /*owns_writer=*/false)); + QuicConnectionPeer::RetirePeerIssuedConnectionIdsNoLongerOnPath(&connection_); + const auto* default_path = QuicConnectionPeer::GetDefaultPath(&connection_); + EXPECT_EQ(default_path->client_connection_id, new_client_connection_id); + EXPECT_EQ(default_path->server_connection_id, frame1.connection_id); + EXPECT_EQ(default_path->stateless_reset_token, frame1.stateless_reset_token); + EXPECT_TRUE(default_path->stateless_reset_token_received); + + // Client will retire server connection ID on old default_path. + auto* retire_peer_issued_cid_alarm = + connection_.GetRetirePeerIssuedConnectionIdAlarm(); + ASSERT_TRUE(retire_peer_issued_cid_alarm->IsSet()); + EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/0u)); + retire_peer_issued_cid_alarm->Fire(); + + // Another server connection ID is available to client. + QuicNewConnectionIdFrame frame2; + frame2.connection_id = TestConnectionId(4); + frame2.sequence_number = 2u; + frame2.retire_prior_to = 1u; + frame2.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame2.connection_id); + connection_.OnNewConnectionIdFrame(frame2); + + // Since server does not retire any client connection ID yet, 2nd migration + // would fail due to lack of client connection ID. + const QuicSocketAddress kSelfAddress2(QuicIpAddress::Loopback4(), + /*port=*/45678); + ASSERT_FALSE(connection_.MigratePath(kSelfAddress2, + connection_.peer_address(), &new_writer, + /*owns_writer=*/false)); +} + +TEST_P(QuicConnectionTest, CloseConnectionAfterReceiveNewConnectionIdFromPeerUsingEmptyCID) { if (!version().HasIetfQuicFrames()) { return; @@ -14098,7 +14552,7 @@ TEST_P(QuicConnectionTest, } QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_); set_perspective(Perspective::IS_SERVER); - connection_.set_client_connection_id(TestConnectionId(0)); + SetClientConnectionId(TestConnectionId(0)); QuicNewConnectionIdFrame frame; frame.sequence_number = 1u; @@ -14129,9 +14583,71 @@ TEST_P(QuicConnectionTest, TestConnectionId(2)); } +TEST_P( + QuicConnectionTest, + ReplacePeerIssuedConnectionIdOnBothPathsTriggeredByNewConnectionIdFrame) { + if (!version().HasIetfQuicFrames() || !connection_.use_path_validator() || + !connection_.use_connection_id_on_default_path() || + !connection_.count_bytes_on_alternative_path_separately()) { + return; + } + QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_); + PathProbeTestInit(Perspective::IS_SERVER); + SetClientConnectionId(TestConnectionId(0)); + + // Populate alternative_path_ with probing packet. + std::unique_ptr<SerializedPacket> probing_packet = ConstructProbingPacket(); + + std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( + QuicEncryptedPacket(probing_packet->encrypted_buffer, + probing_packet->encrypted_length), + clock_.Now())); + QuicIpAddress new_host; + new_host.FromString("1.1.1.1"); + ProcessReceivedPacket(kSelfAddress, + QuicSocketAddress(new_host, /*port=*/23456), *received); + + EXPECT_EQ( + TestConnectionId(0), + QuicConnectionPeer::GetClientConnectionIdOnAlternativePath(&connection_)); + + QuicNewConnectionIdFrame frame; + frame.sequence_number = 1u; + frame.connection_id = TestConnectionId(1); + frame.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame.connection_id); + frame.retire_prior_to = 0u; + + EXPECT_TRUE(connection_.OnNewConnectionIdFrame(frame)); + auto* retire_peer_issued_cid_alarm = + connection_.GetRetirePeerIssuedConnectionIdAlarm(); + ASSERT_FALSE(retire_peer_issued_cid_alarm->IsSet()); + + frame.sequence_number = 2u; + frame.connection_id = TestConnectionId(2); + frame.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame.connection_id); + frame.retire_prior_to = 1u; // CID associated with #1 will be retired. + + EXPECT_TRUE(connection_.OnNewConnectionIdFrame(frame)); + ASSERT_TRUE(retire_peer_issued_cid_alarm->IsSet()); + EXPECT_EQ(connection_.client_connection_id(), TestConnectionId(0)); + + EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/0u)); + retire_peer_issued_cid_alarm->Fire(); + EXPECT_EQ(connection_.client_connection_id(), TestConnectionId(2)); + EXPECT_EQ(connection_.packet_creator().GetDestinationConnectionId(), + TestConnectionId(2)); + // Clean up alternative path connection ID. + EXPECT_EQ( + TestConnectionId(2), + QuicConnectionPeer::GetClientConnectionIdOnAlternativePath(&connection_)); +} + TEST_P(QuicConnectionTest, CloseConnectionAfterReceiveRetireConnectionIdWhenNoCIDIssued) { - if (!version().HasIetfQuicFrames()) { + if (!version().HasIetfQuicFrames() || + GetQuicReloadableFlag(quic_use_connection_id_on_default_path_v2)) { return; } QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_); @@ -14151,7 +14667,8 @@ TEST_P(QuicConnectionTest, } TEST_P(QuicConnectionTest, RetireConnectionIdFrameResultsInError) { - if (!version().HasIetfQuicFrames()) { + if (!version().HasIetfQuicFrames() || + GetQuicReloadableFlag(quic_use_connection_id_on_default_path_v2)) { return; } QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_); @@ -14175,8 +14692,40 @@ TEST_P(QuicConnectionTest, RetireConnectionIdFrameResultsInError) { IsError(IETF_QUIC_PROTOCOL_VIOLATION)); } +TEST_P(QuicConnectionTest, + ServerRetireSelfIssuedConnectionIdWithoutSendingNewConnectionIdBefore) { + if (!connection_.support_multiple_connection_ids()) { + return; + } + QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_); + set_perspective(Perspective::IS_SERVER); + connection_.CreateConnectionIdManager(); + + auto* retire_self_issued_cid_alarm = + connection_.GetRetireSelfIssuedConnectionIdAlarm(); + ASSERT_FALSE(retire_self_issued_cid_alarm->IsSet()); + + QuicConnectionId cid0 = connection_id_; + QuicRetireConnectionIdFrame frame; + frame.sequence_number = 0u; + if (!GetQuicReloadableFlag(quic_use_connection_id_on_default_path_v2) || + connection_.connection_migration_use_new_cid()) { + EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_)).Times(2); + EXPECT_CALL(visitor_, SendNewConnectionId(_)).Times(2); + } + EXPECT_TRUE(connection_.OnRetireConnectionIdFrame(frame)); + if (!GetQuicReloadableFlag(quic_use_connection_id_on_default_path_v2)) { + ASSERT_TRUE(retire_self_issued_cid_alarm->IsSet()); + // cid0 is retired when the retire CID alarm fires. + if (!GetQuicReloadableFlag(quic_use_connection_id_on_default_path_v2)) + EXPECT_CALL(visitor_, OnServerConnectionIdRetired(cid0)); + retire_self_issued_cid_alarm->Fire(); + } +} + TEST_P(QuicConnectionTest, ServerRetireSelfIssuedConnectionId) { - if (!version().HasIetfQuicFrames()) { + if (!version().HasIetfQuicFrames() || + GetQuicReloadableFlag(quic_use_connection_id_on_default_path_v2)) { return; } QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_); @@ -14219,6 +14768,121 @@ TEST_P(QuicConnectionTest, ServerRetireSelfIssuedConnectionId) { ElementsAre(cid1, cid2)); } +TEST_P(QuicConnectionTest, PatchMissingClientConnectionIdOntoAlternativePath) { + if (!version().HasIetfQuicFrames() || + !connection_.support_multiple_connection_ids() || + !connection_.use_connection_id_on_default_path()) { + return; + } + set_perspective(Perspective::IS_SERVER); + connection_.CreateConnectionIdManager(); + connection_.set_client_connection_id(TestConnectionId(1)); + + // Set up the state after path probing. + const auto* default_path = QuicConnectionPeer::GetDefaultPath(&connection_); + auto* alternative_path = QuicConnectionPeer::GetAlternativePath(&connection_); + QuicIpAddress new_host; + new_host.FromString("12.12.12.12"); + alternative_path->self_address = default_path->self_address; + alternative_path->peer_address = QuicSocketAddress(new_host, 12345); + alternative_path->server_connection_id = TestConnectionId(3); + ASSERT_TRUE(alternative_path->client_connection_id.IsEmpty()); + ASSERT_FALSE(alternative_path->stateless_reset_token_received); + + QuicNewConnectionIdFrame frame; + frame.sequence_number = 1u; + frame.connection_id = TestConnectionId(5); + frame.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame.connection_id); + frame.retire_prior_to = 0u; + // New ID is patched onto the alternative path when the needed + // NEW_CONNECTION_ID frame is received after PATH_CHALLENGE frame. + connection_.OnNewConnectionIdFrame(frame); + + ASSERT_EQ(alternative_path->client_connection_id, frame.connection_id); + ASSERT_EQ(alternative_path->stateless_reset_token, + frame.stateless_reset_token); + ASSERT_TRUE(alternative_path->stateless_reset_token_received); +} + +TEST_P(QuicConnectionTest, PatchMissingClientConnectionIdOntoDefaultPath) { + if (!version().HasIetfQuicFrames() || + !connection_.support_multiple_connection_ids() || + !connection_.use_connection_id_on_default_path()) { + return; + } + set_perspective(Perspective::IS_SERVER); + connection_.CreateConnectionIdManager(); + connection_.set_client_connection_id(TestConnectionId(1)); + + // Set up the state after peer migration without probing. + auto* default_path = QuicConnectionPeer::GetDefaultPath(&connection_); + auto* alternative_path = QuicConnectionPeer::GetAlternativePath(&connection_); + auto* packet_creator = QuicConnectionPeer::GetPacketCreator(&connection_); + *alternative_path = std::move(*default_path); + QuicIpAddress new_host; + new_host.FromString("12.12.12.12"); + default_path->self_address = default_path->self_address; + default_path->peer_address = QuicSocketAddress(new_host, 12345); + default_path->server_connection_id = TestConnectionId(3); + packet_creator->SetDefaultPeerAddress(default_path->peer_address); + packet_creator->SetServerConnectionId(default_path->server_connection_id); + packet_creator->SetClientConnectionId(default_path->client_connection_id); + + ASSERT_FALSE(default_path->validated); + ASSERT_TRUE(default_path->client_connection_id.IsEmpty()); + ASSERT_FALSE(default_path->stateless_reset_token_received); + + QuicNewConnectionIdFrame frame; + frame.sequence_number = 1u; + frame.connection_id = TestConnectionId(5); + frame.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame.connection_id); + frame.retire_prior_to = 0u; + // New ID is patched onto the default path when the needed + // NEW_CONNECTION_ID frame is received after PATH_CHALLENGE frame. + connection_.OnNewConnectionIdFrame(frame); + + ASSERT_EQ(default_path->client_connection_id, frame.connection_id); + ASSERT_EQ(default_path->stateless_reset_token, frame.stateless_reset_token); + ASSERT_TRUE(default_path->stateless_reset_token_received); + ASSERT_EQ(packet_creator->GetDestinationConnectionId(), frame.connection_id); +} + +TEST_P(QuicConnectionTest, ShouldGeneratePacketBlockedByMissingConnectionId) { + if (!version().HasIetfQuicFrames() || + !connection_.support_multiple_connection_ids()) { + return; + } + set_perspective(Perspective::IS_SERVER); + connection_.set_client_connection_id(TestConnectionId(1)); + connection_.CreateConnectionIdManager(); + if (version().SupportsAntiAmplificationLimit()) { + QuicConnectionPeer::SetAddressValidated(&connection_); + } + + ASSERT_TRUE( + connection_.ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + + QuicPacketCreator* packet_creator = + QuicConnectionPeer::GetPacketCreator(&connection_); + QuicIpAddress peer_host1; + peer_host1.FromString("12.12.12.12"); + QuicSocketAddress peer_address1(peer_host1, 1235); + + { + // No connection ID is available as context is created without any. + QuicPacketCreator::ScopedPeerAddressContext context( + packet_creator, peer_address1, EmptyQuicConnectionId(), + EmptyQuicConnectionId(), + /*update_connection_id=*/true); + ASSERT_FALSE(connection_.ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, + NOT_HANDSHAKE)); + } + ASSERT_TRUE( + connection_.ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); +} + // Regression test for b/182571515 TEST_P(QuicConnectionTest, LostDataThenGetAcknowledged) { set_perspective(Perspective::IS_SERVER); @@ -14278,6 +14942,53 @@ TEST_P(QuicConnectionTest, LostDataThenGetAcknowledged) { } } +TEST_P(QuicConnectionTest, PtoSendStreamData) { + if (!connection_.SupportsMultiplePacketNumberSpaces()) { + return; + } + set_perspective(Perspective::IS_SERVER); + if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { + EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); + } + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + use_tagging_decrypter(); + ProcessCryptoPacketAtLevel(1, ENCRYPTION_INITIAL); + EXPECT_TRUE(connection_.HasPendingAcks()); + + connection_.SetEncrypter(ENCRYPTION_INITIAL, + std::make_unique<TaggingEncrypter>(0x01)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); + // Send INITIAL 1. + connection_.SendCryptoDataWithString("foo", 0, ENCRYPTION_INITIAL); + + connection_.SetEncrypter(ENCRYPTION_HANDSHAKE, + std::make_unique<TaggingEncrypter>(0x02)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE); + SetDecrypter(ENCRYPTION_HANDSHAKE, + std::make_unique<StrictTaggingDecrypter>(0x02)); + // Send HANDSHAKE packets. + EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(1); + connection_.SendCryptoDataWithString("foo", 0, ENCRYPTION_HANDSHAKE); + + connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<TaggingEncrypter>(0x03)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + + // Send half RTT packet with congestion control blocked. + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(false)); + connection_.SendStreamDataWithString(2, std::string(1500, 'a'), 0, NO_FIN); + + ASSERT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + connection_.GetRetransmissionAlarm()->Fire(); + if (GetQuicReloadableFlag(quic_donot_pto_half_rtt_data)) { + // Verify INITIAL and HANDSHAKE get retransmitted. + EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet()); + } else { + // Application data preempts handshake data when PTO fires. + EXPECT_EQ(0x03030303u, writer_->final_bytes_of_last_packet()); + } +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h index 1bb37035912..48d228bff31 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h @@ -8,10 +8,11 @@ #include <cstdint> #include <string> +#include "absl/container/flat_hash_map.h" #include "quic/core/frames/quic_frame.h" -#include "quic/core/quic_circular_deque.h" #include "quic/core/quic_connection_id.h" #include "quic/core/quic_types.h" +#include "common/quiche_circular_deque.h" namespace quic { @@ -163,7 +164,7 @@ class QUIC_EXPORT_PRIVATE QuicControlFrameManager { // frame. void WriteOrBufferQuicFrame(QuicFrame frame); - QuicCircularDeque<QuicFrame> control_frames_; + quiche::QuicheCircularDeque<QuicFrame> control_frames_; // Id of latest saved control frame. 0 if no control frame has been saved. QuicControlFrameId last_control_frame_id_; @@ -182,7 +183,7 @@ class QUIC_EXPORT_PRIVATE QuicControlFrameManager { DelegateInterface* delegate_; // Last sent window update frame for each stream. - QuicSmallMap<QuicStreamId, QuicControlFrameId, 10> window_update_frames_; + absl::flat_hash_map<QuicStreamId, QuicControlFrameId> window_update_frames_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc index fc9be1764a2..1dd68995e2f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc @@ -11,7 +11,7 @@ #include "absl/strings/string_view.h" #include "third_party/boringssl/src/include/openssl/sha.h" #include "quic/platform/api/quic_flag_utils.h" -#include "common/platform/api/quiche_text_utils.h" +#include "common/quiche_text_utils.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_test.cc index 88fdda9d6c2..4e07f34b616 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_test.cc @@ -354,8 +354,7 @@ class QuicCryptoServerStreamTestWithFakeProofSource }; // Regression test for b/35422225, in which multiple CHLOs arriving on the same -// connection in close succession could cause a crash, especially when the use -// of Mentat signing meant that it took a while for each CHLO to be processed. +// connection in close succession could cause a crash. TEST_F(QuicCryptoServerStreamTestWithFakeProofSource, MultipleChlo) { Initialize(); GetFakeProofSource()->Activate(); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue.h b/chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue.h index 286aeae6bf8..4d3aa2f17f3 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue.h @@ -8,10 +8,10 @@ #include <memory> #include "absl/types/optional.h" -#include "quic/core/quic_circular_deque.h" #include "quic/core/quic_time.h" #include "quic/core/quic_types.h" #include "quic/platform/api/quic_mem_slice.h" +#include "common/quiche_circular_deque.h" namespace quic { @@ -80,7 +80,7 @@ class QUIC_EXPORT_PRIVATE QuicDatagramQueue { const QuicClock* clock_; QuicTime::Delta max_time_in_queue_ = QuicTime::Delta::Zero(); - QuicCircularDeque<Datagram> queue_; + quiche::QuicheCircularDeque<Datagram> queue_; std::unique_ptr<Observer> observer_; }; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.cc b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.cc index 0ed002a6850..e1a999c706a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.cc @@ -26,7 +26,7 @@ #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_stack_trace.h" -#include "common/platform/api/quiche_text_utils.h" +#include "common/quiche_text_utils.h" namespace quic { @@ -331,9 +331,6 @@ QuicDispatcher::QuicDispatcher( expected_server_connection_id_length_( expected_server_connection_id_length), should_update_expected_server_connection_id_length_(false) { - if (use_reference_counted_session_map_) { - QUIC_RESTART_FLAG_COUNT(quic_use_reference_counted_sesssion_map); - } QUIC_BUG_IF(quic_bug_12724_1, GetSupportedVersions().empty()) << "Trying to create dispatcher without any supported versions"; QUIC_DLOG(INFO) << "Created QuicDispatcher with versions: " @@ -341,15 +338,10 @@ QuicDispatcher::QuicDispatcher( } QuicDispatcher::~QuicDispatcher() { - if (use_reference_counted_session_map_) { - reference_counted_session_map_.clear(); - closed_ref_counted_session_list_.clear(); - if (support_multiple_cid_per_connection_) { - num_sessions_in_session_map_ = 0; - } - } else { - session_map_.clear(); - closed_session_list_.clear(); + reference_counted_session_map_.clear(); + closed_ref_counted_session_list_.clear(); + if (support_multiple_cid_per_connection_) { + num_sessions_in_session_map_ = 0; } } @@ -510,60 +502,45 @@ bool QuicDispatcher::MaybeDispatchPacket( return true; } + if (GetQuicReloadableFlag(quic_discard_packets_with_invalid_cid)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_discard_packets_with_invalid_cid); + if (packet_info.version_flag && packet_info.version.IsKnown() && + !QuicUtils::IsConnectionIdLengthValidForVersion( + server_connection_id.length(), + packet_info.version.transport_version)) { + QUIC_DLOG(INFO) << "Packet with destination connection ID " + << server_connection_id << " is invalid with version " + << packet_info.version; + // Drop the packet silently. + QUIC_CODE_COUNT(quic_dropped_invalid_initial_connection_id); + return true; + } + } + // Packets with connection IDs for active connections are processed // immediately. - if (use_reference_counted_session_map_) { - auto it = reference_counted_session_map_.find(server_connection_id); - if (it != reference_counted_session_map_.end()) { - QUICHE_DCHECK( - !buffered_packets_.HasBufferedPackets(server_connection_id)); - if (packet_info.version_flag && - packet_info.version != it->second->version() && - packet_info.version == LegacyVersionForEncapsulation()) { - // This packet is using the Legacy Version Encapsulation version but the - // corresponding session isn't, attempt extraction of inner packet. - ChloAlpnSniExtractor alpn_extractor; - if (ChloExtractor::Extract(packet_info.packet, packet_info.version, - config_->create_session_tag_indicators(), - &alpn_extractor, - server_connection_id.length())) { - if (MaybeHandleLegacyVersionEncapsulation(this, &alpn_extractor, - packet_info)) { - return true; - } + auto it = reference_counted_session_map_.find(server_connection_id); + if (it != reference_counted_session_map_.end()) { + QUICHE_DCHECK(!buffered_packets_.HasBufferedPackets(server_connection_id)); + if (packet_info.version_flag && + packet_info.version != it->second->version() && + packet_info.version == LegacyVersionForEncapsulation()) { + // This packet is using the Legacy Version Encapsulation version but the + // corresponding session isn't, attempt extraction of inner packet. + ChloAlpnSniExtractor alpn_extractor; + if (ChloExtractor::Extract(packet_info.packet, packet_info.version, + config_->create_session_tag_indicators(), + &alpn_extractor, + server_connection_id.length())) { + if (MaybeHandleLegacyVersionEncapsulation(this, &alpn_extractor, + packet_info)) { + return true; } } - it->second->ProcessUdpPacket(packet_info.self_address, - packet_info.peer_address, - packet_info.packet); - return true; - } - } else { - auto it = session_map_.find(server_connection_id); - if (it != session_map_.end()) { - QUICHE_DCHECK( - !buffered_packets_.HasBufferedPackets(server_connection_id)); - if (packet_info.version_flag && - packet_info.version != it->second->version() && - packet_info.version == LegacyVersionForEncapsulation()) { - // This packet is using the Legacy Version Encapsulation version but the - // corresponding session isn't, attempt extraction of inner packet. - ChloAlpnSniExtractor alpn_extractor; - if (ChloExtractor::Extract(packet_info.packet, packet_info.version, - config_->create_session_tag_indicators(), - &alpn_extractor, - server_connection_id.length())) { - if (MaybeHandleLegacyVersionEncapsulation(this, &alpn_extractor, - packet_info)) { - return true; - } - } - } - it->second->ProcessUdpPacket(packet_info.self_address, - packet_info.peer_address, - packet_info.packet); - return true; } + it->second->ProcessUdpPacket(packet_info.self_address, + packet_info.peer_address, packet_info.packet); + return true; } if (packet_info.version.IsKnown()) { // We did not find the connection ID, check if we've replaced it. @@ -575,28 +552,15 @@ bool QuicDispatcher::MaybeDispatchPacket( QuicConnectionId replaced_connection_id = MaybeReplaceServerConnectionId( server_connection_id, packet_info.version); if (replaced_connection_id != server_connection_id) { - if (use_reference_counted_session_map_) { - // Search for the replacement. - auto it2 = reference_counted_session_map_.find(replaced_connection_id); - if (it2 != reference_counted_session_map_.end()) { - QUICHE_DCHECK( - !buffered_packets_.HasBufferedPackets(replaced_connection_id)); - it2->second->ProcessUdpPacket(packet_info.self_address, - packet_info.peer_address, - packet_info.packet); - return true; - } - } else { - // Search for the replacement. - auto it2 = session_map_.find(replaced_connection_id); - if (it2 != session_map_.end()) { - QUICHE_DCHECK( - !buffered_packets_.HasBufferedPackets(replaced_connection_id)); - it2->second->ProcessUdpPacket(packet_info.self_address, - packet_info.peer_address, - packet_info.packet); - return true; - } + // Search for the replacement. + auto it2 = reference_counted_session_map_.find(replaced_connection_id); + if (it2 != reference_counted_session_map_.end()) { + QUICHE_DCHECK( + !buffered_packets_.HasBufferedPackets(replaced_connection_id)); + it2->second->ProcessUdpPacket(packet_info.self_address, + packet_info.peer_address, + packet_info.packet); + return true; } } } @@ -812,9 +776,9 @@ QuicDispatcher::QuicPacketFate QuicDispatcher::ValidityChecks( void QuicDispatcher::CleanUpSession(QuicConnectionId server_connection_id, QuicConnection* connection, - QuicErrorCode error, - const std::string& error_details, - ConnectionCloseSource source) { + QuicErrorCode /*error*/, + const std::string& /*error_details*/, + ConnectionCloseSource /*source*/) { write_blocked_list_.erase(connection); QuicTimeWaitListManager::TimeWaitAction action = QuicTimeWaitListManager::SEND_STATELESS_RESET; @@ -823,14 +787,8 @@ void QuicDispatcher::CleanUpSession(QuicConnectionId server_connection_id, action = QuicTimeWaitListManager::SEND_CONNECTION_CLOSE_PACKETS; } else { if (!connection->IsHandshakeComplete()) { - const bool fix_dispatcher_sent_error_code = - GetQuicReloadableFlag(quic_fix_dispatcher_sent_error_code) && - source == ConnectionCloseSource::FROM_SELF; // TODO(fayang): Do not serialize connection close packet if the // connection is closed by the client. - if (fix_dispatcher_sent_error_code) { - QUIC_RELOADABLE_FLAG_COUNT(quic_fix_dispatcher_sent_error_code); - } if (!connection->version().HasIetfInvariantHeader()) { QUIC_CODE_COUNT(gquic_add_to_time_wait_list_with_handshake_failed); } else { @@ -845,10 +803,8 @@ void QuicDispatcher::CleanUpSession(QuicConnectionId server_connection_id, server_connection_id, connection->version(), helper_.get(), time_wait_list_manager_.get()); terminator.CloseConnection( - fix_dispatcher_sent_error_code ? error : QUIC_HANDSHAKE_FAILED, - fix_dispatcher_sent_error_code - ? error_details - : "Connection is closed by server before handshake confirmed", + QUIC_HANDSHAKE_FAILED, + "Connection is closed by server before handshake confirmed", connection->version().HasIetfInvariantHeader(), connection->GetActiveServerConnectionIds()); } else { @@ -862,11 +818,8 @@ void QuicDispatcher::CleanUpSession(QuicConnectionId server_connection_id, : GOOGLE_QUIC_PACKET, /*version_flag=*/true, connection->version().HasLengthPrefixedConnectionIds(), - connection->version(), - fix_dispatcher_sent_error_code ? error : QUIC_HANDSHAKE_FAILED, - fix_dispatcher_sent_error_code - ? error_details - : "Connection is closed by server before handshake confirmed", + connection->version(), QUIC_HANDSHAKE_FAILED, + "Connection is closed by server before handshake confirmed", // Although it is our intention to send termination packets, the // |action| argument is not used by this call to // StatelesslyTerminateConnection(). @@ -898,18 +851,12 @@ void QuicDispatcher::StopAcceptingNewConnections() { void QuicDispatcher::PerformActionOnActiveSessions( std::function<void(QuicSession*)> operation) const { - if (use_reference_counted_session_map_) { - absl::flat_hash_set<QuicSession*> visited_session; - visited_session.reserve(reference_counted_session_map_.size()); - for (auto const& kv : reference_counted_session_map_) { - QuicSession* session = kv.second.get(); - if (visited_session.insert(session).second) { - operation(session); - } - } - } else { - for (auto const& kv : session_map_) { - operation(kv.second.get()); + absl::flat_hash_set<QuicSession*> visited_session; + visited_session.reserve(reference_counted_session_map_.size()); + for (auto const& kv : reference_counted_session_map_) { + QuicSession* session = kv.second.get(); + if (visited_session.insert(session).second) { + operation(session); } } } @@ -917,7 +864,6 @@ void QuicDispatcher::PerformActionOnActiveSessions( // Get a snapshot of all sessions. std::vector<std::shared_ptr<QuicSession>> QuicDispatcher::GetSessionsSnapshot() const { - QUICHE_DCHECK(use_reference_counted_session_map_); std::vector<std::shared_ptr<QuicSession>> snapshot; snapshot.reserve(reference_counted_session_map_.size()); absl::flat_hash_set<QuicSession*> visited_session; @@ -937,29 +883,16 @@ std::unique_ptr<QuicPerPacketContext> QuicDispatcher::GetPerPacketContext() } void QuicDispatcher::DeleteSessions() { - if (use_reference_counted_session_map_) { - if (!write_blocked_list_.empty()) { - for (const auto& session : closed_ref_counted_session_list_) { - if (write_blocked_list_.erase(session->connection()) != 0) { - QUIC_BUG(quic_bug_12724_2) - << "QuicConnection was in WriteBlockedList before destruction " - << session->connection()->connection_id(); - } + if (!write_blocked_list_.empty()) { + for (const auto& session : closed_ref_counted_session_list_) { + if (write_blocked_list_.erase(session->connection()) != 0) { + QUIC_BUG(quic_bug_12724_2) + << "QuicConnection was in WriteBlockedList before destruction " + << session->connection()->connection_id(); } } - closed_ref_counted_session_list_.clear(); - } else { - if (!write_blocked_list_.empty()) { - for (const std::unique_ptr<QuicSession>& session : closed_session_list_) { - if (write_blocked_list_.erase(session->connection()) != 0) { - QUIC_BUG(quic_bug_12724_3) - << "QuicConnection was in WriteBlockedList before destruction " - << session->connection()->connection_id(); - } - } - } - closed_session_list_.clear(); } + closed_ref_counted_session_list_.clear(); } void QuicDispatcher::OnCanWrite() { @@ -995,28 +928,15 @@ bool QuicDispatcher::HasPendingWrites() const { } void QuicDispatcher::Shutdown() { - if (use_reference_counted_session_map_) { - while (!reference_counted_session_map_.empty()) { - QuicSession* session = - reference_counted_session_map_.begin()->second.get(); - session->connection()->CloseConnection( - QUIC_PEER_GOING_AWAY, "Server shutdown imminent", - ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); - // Validate that the session removes itself from the session map on close. - QUICHE_DCHECK(reference_counted_session_map_.empty() || - reference_counted_session_map_.begin()->second.get() != - session); - } - } else { - while (!session_map_.empty()) { - QuicSession* session = session_map_.begin()->second.get(); - session->connection()->CloseConnection( - QUIC_PEER_GOING_AWAY, "Server shutdown imminent", - ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); - // Validate that the session removes itself from the session map on close. - QUICHE_DCHECK(session_map_.empty() || - session_map_.begin()->second.get() != session); - } + while (!reference_counted_session_map_.empty()) { + QuicSession* session = reference_counted_session_map_.begin()->second.get(); + session->connection()->CloseConnection( + QUIC_PEER_GOING_AWAY, "Server shutdown imminent", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + // Validate that the session removes itself from the session map on close. + QUICHE_DCHECK(reference_counted_session_map_.empty() || + reference_counted_session_map_.begin()->second.get() != + session); } DeleteSessions(); } @@ -1025,72 +945,41 @@ void QuicDispatcher::OnConnectionClosed(QuicConnectionId server_connection_id, QuicErrorCode error, const std::string& error_details, ConnectionCloseSource source) { - if (use_reference_counted_session_map_) { - auto it = reference_counted_session_map_.find(server_connection_id); - if (it == reference_counted_session_map_.end()) { - QUIC_BUG(quic_bug_10287_3) - << "ConnectionId " << server_connection_id - << " does not exist in the session map. Error: " - << QuicErrorCodeToString(error); - QUIC_BUG(quic_bug_10287_4) << QuicStackTrace(); - return; - } + auto it = reference_counted_session_map_.find(server_connection_id); + if (it == reference_counted_session_map_.end()) { + QUIC_BUG(quic_bug_10287_3) << "ConnectionId " << server_connection_id + << " does not exist in the session map. Error: " + << QuicErrorCodeToString(error); + QUIC_BUG(quic_bug_10287_4) << QuicStackTrace(); + return; + } - QUIC_DLOG_IF(INFO, error != QUIC_NO_ERROR) - << "Closing connection (" << server_connection_id - << ") due to error: " << QuicErrorCodeToString(error) - << ", with details: " << error_details; - - QuicConnection* connection = it->second->connection(); - if (ShouldDestroySessionAsynchronously()) { - // Set up alarm to fire immediately to bring destruction of this session - // out of current call stack. - if (closed_ref_counted_session_list_.empty()) { - delete_sessions_alarm_->Update(helper()->GetClock()->ApproximateNow(), - QuicTime::Delta::Zero()); - } - closed_ref_counted_session_list_.push_back(std::move(it->second)); + QUIC_DLOG_IF(INFO, error != QUIC_NO_ERROR) + << "Closing connection (" << server_connection_id + << ") due to error: " << QuicErrorCodeToString(error) + << ", with details: " << error_details; + + QuicConnection* connection = it->second->connection(); + if (ShouldDestroySessionAsynchronously()) { + // Set up alarm to fire immediately to bring destruction of this session + // out of current call stack. + if (closed_ref_counted_session_list_.empty()) { + delete_sessions_alarm_->Update(helper()->GetClock()->ApproximateNow(), + QuicTime::Delta::Zero()); } - CleanUpSession(it->first, connection, error, error_details, source); - if (support_multiple_cid_per_connection_) { - QUIC_RESTART_FLAG_COUNT_N( - quic_dispatcher_support_multiple_cid_per_connection_v2, 1, 2); - for (const QuicConnectionId& cid : - connection->GetActiveServerConnectionIds()) { - reference_counted_session_map_.erase(cid); - } - --num_sessions_in_session_map_; - } else { - reference_counted_session_map_.erase(it); + closed_ref_counted_session_list_.push_back(std::move(it->second)); + } + CleanUpSession(it->first, connection, error, error_details, source); + if (support_multiple_cid_per_connection_) { + QUIC_RESTART_FLAG_COUNT_N( + quic_dispatcher_support_multiple_cid_per_connection_v2, 1, 2); + for (const QuicConnectionId& cid : + connection->GetActiveServerConnectionIds()) { + reference_counted_session_map_.erase(cid); } + --num_sessions_in_session_map_; } else { - auto it = session_map_.find(server_connection_id); - if (it == session_map_.end()) { - QUIC_BUG(quic_bug_10287_5) - << "ConnectionId " << server_connection_id - << " does not exist in the session map. Error: " - << QuicErrorCodeToString(error); - QUIC_BUG(quic_bug_10287_6) << QuicStackTrace(); - return; - } - - QUIC_DLOG_IF(INFO, error != QUIC_NO_ERROR) - << "Closing connection (" << server_connection_id - << ") due to error: " << QuicErrorCodeToString(error) - << ", with details: " << error_details; - - QuicConnection* connection = it->second->connection(); - if (ShouldDestroySessionAsynchronously()) { - // Set up alarm to fire immediately to bring destruction of this session - // out of current call stack. - if (closed_session_list_.empty()) { - delete_sessions_alarm_->Update(helper()->GetClock()->ApproximateNow(), - QuicTime::Delta::Zero()); - } - closed_session_list_.push_back(std::move(it->second)); - } - CleanUpSession(it->first, connection, error, error_details, source); - session_map_.erase(it); + reference_counted_session_map_.erase(it); } } @@ -1126,6 +1015,7 @@ void QuicDispatcher::OnNewConnectionIdSent( << server_connection_id << " new_connection_id: " << new_connection_id; return; } + QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 5, 5); auto insertion_result = reference_counted_session_map_.insert( std::make_pair(new_connection_id, it->second)); QUICHE_DCHECK(insertion_result.second); @@ -1250,28 +1140,18 @@ void QuicDispatcher::ProcessBufferedChlos(size_t max_connections_to_create) { } QUIC_DLOG(INFO) << "Created new session for " << server_connection_id; - if (use_reference_counted_session_map_) { - auto insertion_result = reference_counted_session_map_.insert( - std::make_pair(server_connection_id, - std::shared_ptr<QuicSession>(std::move(session)))); - if (!insertion_result.second) { - QUIC_BUG(quic_bug_12724_5) - << "Tried to add a session to session_map with existing connection " - "id: " - << server_connection_id; - } else if (support_multiple_cid_per_connection_) { - ++num_sessions_in_session_map_; - } - DeliverPacketsToSession(packets, insertion_result.first->second.get()); - } else { - auto insertion_result = session_map_.insert( - std::make_pair(server_connection_id, std::move(session))); - QUIC_BUG_IF(quic_bug_12724_6, !insertion_result.second) + auto insertion_result = reference_counted_session_map_.insert( + std::make_pair(server_connection_id, + std::shared_ptr<QuicSession>(std::move(session)))); + if (!insertion_result.second) { + QUIC_BUG(quic_bug_12724_5) << "Tried to add a session to session_map with existing connection " "id: " << server_connection_id; - DeliverPacketsToSession(packets, insertion_result.first->second.get()); + } else if (support_multiple_cid_per_connection_) { + ++num_sessions_in_session_map_; } + DeliverPacketsToSession(packets, insertion_result.first->second.get()); } } @@ -1370,28 +1250,18 @@ void QuicDispatcher::ProcessChlo(const std::vector<std::string>& alpns, << packet_info->destination_connection_id; QuicSession* session_ptr; - if (use_reference_counted_session_map_) { - auto insertion_result = - reference_counted_session_map_.insert(std::make_pair( - packet_info->destination_connection_id, - std::shared_ptr<QuicSession>(std::move(session.release())))); - if (!insertion_result.second) { - QUIC_BUG(quic_bug_10287_9) - << "Tried to add a session to session_map with existing " - "connection id: " - << packet_info->destination_connection_id; - } else if (support_multiple_cid_per_connection_) { - ++num_sessions_in_session_map_; - } - session_ptr = insertion_result.first->second.get(); - } else { - auto insertion_result = session_map_.insert(std::make_pair( - packet_info->destination_connection_id, std::move(session))); - QUIC_BUG_IF(quic_bug_12724_8, !insertion_result.second) - << "Tried to add a session to session_map with existing connection id: " + auto insertion_result = reference_counted_session_map_.insert(std::make_pair( + packet_info->destination_connection_id, + std::shared_ptr<QuicSession>(std::move(session.release())))); + if (!insertion_result.second) { + QUIC_BUG(quic_bug_10287_9) + << "Tried to add a session to session_map with existing " + "connection id: " << packet_info->destination_connection_id; - session_ptr = insertion_result.first->second.get(); + } else if (support_multiple_cid_per_connection_) { + ++num_sessions_in_session_map_; } + session_ptr = insertion_result.first->second.get(); std::list<BufferedPacket> packets = buffered_packets_.DeliverPackets(packet_info->destination_connection_id) .buffered_packets; @@ -1449,7 +1319,7 @@ bool QuicDispatcher::IsSupportedVersion(const ParsedQuicVersion version) { void QuicDispatcher::MaybeResetPacketsWithNoVersion( const ReceivedPacketInfo& packet_info) { QUICHE_DCHECK(!packet_info.version_flag); - if (GetQuicReloadableFlag(quic_fix_stateless_reset) && + if (GetQuicRestartFlag(quic_fix_stateless_reset2) && packet_info.form != GOOGLE_QUIC_PACKET) { // Drop IETF packets smaller than the minimal stateless reset length. if (packet_info.packet.length() <= @@ -1481,9 +1351,7 @@ size_t QuicDispatcher::NumSessions() const { if (support_multiple_cid_per_connection_) { return num_sessions_in_session_map_; } - return use_reference_counted_session_map_ - ? reference_counted_session_map_.size() - : session_map_.size(); + return reference_counted_session_map_.size(); } } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.h b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.h index 81749543d4d..053f856da98 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.h @@ -166,10 +166,6 @@ class QUIC_NO_EXPORT QuicDispatcher bool accept_new_connections() const { return accept_new_connections_; } - bool use_reference_counted_session_map() const { - return use_reference_counted_session_map_; - } - bool support_multiple_cid_per_connection() const { return support_multiple_cid_per_connection_; } @@ -457,10 +453,7 @@ class QUIC_NO_EXPORT QuicDispatcher // destination connection ID length of all IETF long headers. bool should_update_expected_server_connection_id_length_; - const bool use_reference_counted_session_map_ = - GetQuicRestartFlag(quic_use_reference_counted_sesssion_map); const bool support_multiple_cid_per_connection_ = - use_reference_counted_session_map_ && GetQuicRestartFlag(quic_time_wait_list_support_multiple_cid_v2) && GetQuicRestartFlag( quic_dispatcher_support_multiple_cid_per_connection_v2); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc index 3df8a76965d..5a7a709f91b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc @@ -167,6 +167,7 @@ class TestDispatcher : public QuicDispatcher { std::string custom_packet_context_; + using QuicDispatcher::MaybeDispatchPacket; using QuicDispatcher::SetAllowShortInitialServerConnectionIds; using QuicDispatcher::writer; @@ -1101,6 +1102,31 @@ TEST_P(QuicDispatcherTestAllVersions, ProcessFirstFlight(client_address, EmptyQuicConnectionId()); } +TEST_P(QuicDispatcherTestAllVersions, + DropPacketWithKnownVersionAndInvalidInitialConnectionId) { + SetQuicReloadableFlag(quic_discard_packets_with_invalid_cid, true); + CreateTimeWaitListManager(); + + QuicSocketAddress server_address; + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + + // dispatcher_ should drop this packet with invalid connection ID. + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _)) + .Times(0); + EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) + .Times(0); + absl::string_view cid_str = "123456789abcdefg123456789abcdefg"; + QuicConnectionId invalid_connection_id(cid_str.data(), cid_str.length()); + QuicReceivedPacket packet("packet", 6, QuicTime::Zero()); + ReceivedPacketInfo packet_info(server_address, client_address, packet); + packet_info.version_flag = true; + packet_info.version = version_; + packet_info.destination_connection_id = invalid_connection_id; + + ASSERT_TRUE(dispatcher_->MaybeDispatchPacket(packet_info)); +} + void QuicDispatcherTestBase:: TestVersionNegotiationForUnknownVersionInvalidShortInitialConnectionId( const QuicConnectionId& server_connection_id, @@ -1976,7 +2002,6 @@ class QuicDispatcherSupportMultipleConnectionIdPerConnectionTest public: QuicDispatcherSupportMultipleConnectionIdPerConnectionTest() : QuicDispatcherTestBase(crypto_test_utils::ProofSourceForTesting()) { - SetQuicRestartFlag(quic_use_reference_counted_sesssion_map, true); SetQuicRestartFlag(quic_time_wait_list_support_multiple_cid_v2, true); SetQuicRestartFlag(quic_dispatcher_support_multiple_cid_per_connection_v2, true); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h index 32816b869cf..9fa422f578c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h @@ -459,8 +459,12 @@ enum QuicErrorCode { // Received multiple close offset. QUIC_STREAM_MULTIPLE_OFFSET = 130, - // Internal error codes for HTTP/3 errors. + // HTTP/3 errors. + + // Frame payload larger than what HttpDecoder is willing to buffer. QUIC_HTTP_FRAME_TOO_LARGE = 131, + // Malformed HTTP/3 frame, or PUSH_PROMISE or CANCEL_PUSH received (which is + // an error because MAX_PUSH_ID is never sent). QUIC_HTTP_FRAME_ERROR = 132, // A frame that is never allowed on a request stream is received. QUIC_HTTP_FRAME_UNEXPECTED_ON_SPDY_STREAM = 133, diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_flags_list.h b/chromium/net/third_party/quiche/src/quic/core/quic_flags_list.h index 910db544eff..df0df50f3fc 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_flags_list.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_flags_list.h @@ -4,77 +4,135 @@ // This file is autogenerated by the QUICHE Copybara export script. -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_abort_qpack_on_stream_reset, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_accept_empty_stream_frame_with_no_fin, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_ack_delay_alarm_granularity, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_add_stream_info_to_idle_close_detail, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_allocate_stream_sequencer_buffer_blocks_on_demand, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_allow_client_enabled_bbr_v2, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_alps_include_scheme_in_origin, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_and_tls_allow_sni_without_dots, false) +#ifdef QUIC_FLAG + +QUIC_FLAG(FLAGS_quic_restart_flag_dont_fetch_quic_private_keys_from_leto, false) + +QUIC_FLAG(FLAGS_quic_restart_flag_quic_offload_pacing_to_usps2, false) +// A testonly reloadable flag that will always default to false. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_testonly_default_false, false) +// A testonly reloadable flag that will always default to true. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_testonly_default_true, true) +// A testonly restart flag that will always default to false. +QUIC_FLAG(FLAGS_quic_restart_flag_quic_testonly_default_false, false) +// A testonly restart flag that will always default to true. +QUIC_FLAG(FLAGS_quic_restart_flag_quic_testonly_default_true, true) +// Fix QUIC BBRv2\'s bandwidth_lo modes to better approximate packet conservation. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_fix_bw_lo_mode2, false) +// If true, GFE will consider SNI values which do not contain dots (instead of throwing them away - see b/176998377). +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_and_tls_allow_sni_without_dots, true) +// If true, QUIC BBRv2\'s PROBE_BW mode will not reduce cwnd below BDP+ack_height. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_avoid_too_low_probe_bw_cwnd, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_fix_bw_lo_mode, false) +// If true, QUIC will default enable MTU discovery at server, with a target of 1450 bytes. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_mtu_discovery_at_server, false) +// If true, QuicBatchWriterBase will mark the writer as blocked when the write status is WRITE_STATUS_BLOCKED_DATA_BUFFERED. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_batch_writer_fix_write_blocked, true) +// If true, QuicGsoBatchWriter will support release time if it is available and the process has the permission to do so. +QUIC_FLAG(FLAGS_quic_restart_flag_quic_support_release_time_for_gso, false) +// If true, TlsHandshaker::AdvanceHandshake will retry SSL_do_handshake when entered early data. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_retry_handshake_on_early_data, true) +// If true, TlsServerHandshaker will install a packet flusher when async operation completes. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_add_packet_flusher_on_async_op_done, true) +// If true, TlsServerHandshaker will use handshake hints(if present) to speed up handshakes. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_server_use_handshake_hints, true) +// If true, abort async QPACK header decompression in QuicSpdyStream::Reset() and in QuicSpdyStream::OnStreamReset(). +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_abort_qpack_on_stream_reset, true) +// If true, ack frequency frame can be sent from server to client. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_can_send_ack_frequency, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_close_connection_with_too_many_outstanding_packets, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_connection_support_multiple_cids_v2, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_conservative_bursts, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_conservative_cwnd_and_pacing_gains, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_count_bytes_on_alternative_path_seperately, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_crypto_postpone_cert_validate_for_server, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_enable_5rto_blackhole_detection2, true) +// If true, allow client to enable BBRv2 on server via connection option \'B2ON\'. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_allow_client_enabled_bbr_v2, false) +// If true, close read side but not write side in QuicSpdyStream::OnStreamReset(). +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_on_stream_reset, true) +// If true, default on PTO which unifies TLP + RTO loss recovery. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_on_pto, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_to_bbr, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_to_bbr_v2, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_delay_initial_ack, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_server_blackhole_detection, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_draft_29, false) +// If true, default-enable 5RTO blachole detection. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_enable_5rto_blackhole_detection2, true) +// If true, determine stateless reset packet length based on the received packet length. +QUIC_FLAG(FLAGS_quic_restart_flag_quic_fix_stateless_reset2, false) +// If true, disable QUIC version Q043. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_q043, false) +// If true, disable QUIC version Q046. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_q046, false) +// If true, disable QUIC version Q050. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_q050, false) +// If true, disable QUIC version h3-29. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_draft_29, false) +// If true, disable QUIC version h3-T051. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_t051, false) +// If true, disable blackhole detection on server side. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_server_blackhole_detection, false) +// If true, discard INITIAL packet if the key has been dropped. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_discard_initial_packet_with_key_dropped, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_do_not_synthesize_source_cid_for_short_header, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_donot_reset_ideal_next_packet_send_time, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_donot_write_mid_packet_processing, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_alps_client, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_alps_server, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_mtu_discovery_at_server, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_server_on_wire_ping, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_token_based_address_validation, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_version_rfcv1, false) +// If true, do not bundle 2nd ACK with connection close if there is an ACK queued. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_single_ack_in_packet2, false) +// If true, do not count bytes sent/received on the alternative path into the bytes sent/received on the default path. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_count_bytes_on_alternative_path_seperately, true) +// If true, do not send control frames before encryption is established. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_encrypted_control_frames, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_encrypted_goaway, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_dispatcher_sent_error_code, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_key_update_on_first_packet, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_on_stream_reset, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_stateless_reset, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_willing_and_able_to_write2, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_h3_datagram, false) +// If true, do not send stream data when PTO fires. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_donot_pto_half_rtt_data, true) +// If true, do not write stream data and control frames in the middle of packet processing. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_donot_write_mid_packet_processing, true) +// If true, drop unsent PATH_RESPONSEs and rely on peer\'s retry. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_drop_unsent_path_response, true) +// If true, enable QUIC version h3 (RFCv1). +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_version_rfcv1, false) +// If true, enable server retransmittable on wire PING. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_server_on_wire_ping, true) +// If true, include stream information in idle timeout connection close detail. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_add_stream_info_to_idle_close_detail, true) +// If true, increase the size of stream sequencer buffer block container on demand. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_allocate_stream_sequencer_buffer_blocks_on_demand, false) +// If true, pass the received PATH_RESPONSE payload to path validator to move forward the path validation. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_preempt_stream_data_with_handshake_packet, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_reject_unexpected_ietf_frame_types, true) +// If true, quic connection sends/recieives NewConnectionId & RetireConnectionId frames. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_connection_support_multiple_cids_v4, true) +// If true, quic dispatcher discards packets with invalid server connection ID. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_discard_packets_with_invalid_cid, false) +// If true, quic dispatcher supports one connection to use multiple connection IDs. +QUIC_FLAG(FLAGS_quic_restart_flag_quic_dispatcher_support_multiple_cid_per_connection_v2, true) +// If true, require handshake confirmation for QUIC connections, functionally disabling 0-rtt handshakes. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_require_handshake_confirmation, false) +// If true, send PATH_RESPONSE upon receiving PATH_CHALLENGE regardless of perspective. --gfe2_reloadable_flag_quic_start_peer_migration_earlier has to be true before turn on this flag. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_path_response2, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_timestamps, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_tls_crypto_error_code, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_server_reverse_validate_new_path3, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_single_ack_in_packet2, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_start_peer_migration_earlier, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_testonly_default_false, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_testonly_default_true, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_use_normalized_sni_for_cert_selectioon, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_use_per_handshaker_proof_source, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_retry_handshake_on_early_data, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_unified_iw_options, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_unify_stop_sending, true) +// If true, set burst token to 2 in cwnd bootstrapping experiment. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_conservative_bursts, false) +// If true, signal error in HttpDecoder upon receiving a PUSH_PROMISE or CANCEL_PUSH frame. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_error_on_http3_push, true) +// If true, stop resetting ideal_next_packet_send_time_ in pacing sender. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_donot_reset_ideal_next_packet_send_time, false) +// If true, time_wait_list can support multiple connection IDs. +QUIC_FLAG(FLAGS_quic_restart_flag_quic_time_wait_list_support_multiple_cid_v2, true) +// If true, treat old (pre-draft02) PRIORITY_UPDATE frame as unknown frame. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_ignore_old_priority_update_frame, true) +// If true, upon receiving path challenge, send path response and reverse path challenge in the same function. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_group_path_response_and_challenge_sending_closer, false) +// If true, use BBRv2 as the default congestion controller. Takes precedence over --quic_default_to_bbr. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_to_bbr_v2, false) +// If true, use ScopedEncryptionLevelContext when sending data. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_use_encryption_level_context, true) +// If true, use WriteOrBufferDataAtLevel to send crypto data. Existing WriteOrBufferData is used to send application data. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_use_write_or_buffer_data_at_level, false) -QUIC_FLAG(FLAGS_quic_restart_flag_dont_fetch_quic_private_keys_from_leto, false) -QUIC_FLAG(FLAGS_quic_restart_flag_quic_dispatcher_support_multiple_cid_per_connection_v2, true) -QUIC_FLAG(FLAGS_quic_restart_flag_quic_offload_pacing_to_usps2, false) -QUIC_FLAG(FLAGS_quic_restart_flag_quic_session_tickets_always_enabled, true) -QUIC_FLAG(FLAGS_quic_restart_flag_quic_support_release_time_for_gso, false) -QUIC_FLAG(FLAGS_quic_restart_flag_quic_testonly_default_false, false) -QUIC_FLAG(FLAGS_quic_restart_flag_quic_testonly_default_true, true) -QUIC_FLAG(FLAGS_quic_restart_flag_quic_time_wait_list_support_multiple_cid_v2, true) -QUIC_FLAG(FLAGS_quic_restart_flag_quic_use_reference_counted_sesssion_map, true) +// If true, use new connection ID in connection migration. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_connection_migration_use_new_cid_v2, false) +// If true, use the connection ID and stateless reset token on default PathState. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_use_connection_id_on_default_path_v2, true) +// If true, uses conservative cwnd gain and pacing gain when cwnd gets bootstrapped. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_conservative_cwnd_and_pacing_gains, false) +// If true, validate that peer owns the new address once the server detects peer migration or is probed from that address, and also apply anti-amplification limit while sending to that address. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_server_reverse_validate_new_path3, false) +// If ture, replace the incoming_connection_ids check with original_destination_connection_id check. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_deprecate_incoming_connection_ids, true) +// When the STMP connection option is sent by the client, timestamps in the QUIC ACK frame are sent and processed. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_timestamps, false) +// When true, QuicSpdySession supports draft-ietf-masque-h3-datagram. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_h3_datagram, false) +// When true, defaults to BBR congestion control instead of Cubic. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_to_bbr, false) +// When true, makes the QUIC BBRv2 bw_lo modes more similar to standard BBRv2. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_fix_bw_lo_mode, true) +// When true, set the initial congestion control window from connection options in QuicSentPacketManager rather than TcpCubicSenderBytes. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_unified_iw_options, false) + +#endif + diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_framer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_framer.cc index c5de97be541..e186ce33ecb 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_framer.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_framer.cc @@ -49,7 +49,7 @@ #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_map_util.h" #include "quic/platform/api/quic_stack_trace.h" -#include "common/platform/api/quiche_text_utils.h" +#include "common/quiche_text_utils.h" namespace quic { @@ -435,10 +435,6 @@ QuicFramer::QuicFramer(const ParsedQuicVersionVector& supported_versions, version_ = supported_versions_[0]; QUICHE_DCHECK(version_.IsKnown()) << ParsedQuicVersionVectorToString(supported_versions_); - if (do_not_synthesize_source_cid_for_short_header_) { - QUIC_RELOADABLE_FLAG_COUNT_N( - quic_do_not_synthesize_source_cid_for_short_header, 1, 3); - } } QuicFramer::~QuicFramer() {} @@ -1309,9 +1305,9 @@ std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildIetfStatelessResetPacket( size_t received_packet_length, StatelessResetToken stateless_reset_token) { QUIC_DVLOG(1) << "Building IETF stateless reset packet."; - if (GetQuicReloadableFlag(quic_fix_stateless_reset)) { + if (GetQuicRestartFlag(quic_fix_stateless_reset2)) { if (received_packet_length <= GetMinStatelessResetPacketLength()) { - QUIC_BUG(362045737_1) + QUICHE_DLOG(ERROR) << "Tried to build stateless reset packet with received packet " "length " << received_packet_length; @@ -1344,7 +1340,7 @@ std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildIetfStatelessResetPacket( QUIC_BUG(362045737_3) << "Failed to write stateless reset token"; return nullptr; } - QUIC_RELOADABLE_FLAG_COUNT(quic_fix_stateless_reset); + QUIC_RESTART_FLAG_COUNT(quic_fix_stateless_reset2); return std::make_unique<QuicEncryptedPacket>(buffer.release(), len, /*owns_buffer=*/true); } @@ -2774,7 +2770,6 @@ bool QuicFramer::ProcessAndValidateIetfConnectionIdLength( bool QuicFramer::ValidateReceivedConnectionIds(const QuicPacketHeader& header) { bool skip_server_connection_id_validation = - do_not_synthesize_source_cid_for_short_header_ && perspective_ == Perspective::IS_CLIENT && header.form == IETF_QUIC_SHORT_HEADER_PACKET; if (!skip_server_connection_id_validation && @@ -2786,13 +2781,8 @@ bool QuicFramer::ValidateReceivedConnectionIds(const QuicPacketHeader& header) { } bool skip_client_connection_id_validation = - do_not_synthesize_source_cid_for_short_header_ && perspective_ == Perspective::IS_SERVER && header.form == IETF_QUIC_SHORT_HEADER_PACKET; - if (skip_client_connection_id_validation) { - QUIC_RELOADABLE_FLAG_COUNT_N( - quic_do_not_synthesize_source_cid_for_short_header, 2, 3); - } if (!skip_client_connection_id_validation && version_.SupportsClientConnectionIds() && !QuicUtils::IsConnectionIdValidForVersion( @@ -2829,15 +2819,6 @@ bool QuicFramer::ProcessIetfPacketHeader(QuicDataReader* reader, header->destination_connection_id_included = CONNECTION_ID_PRESENT; header->source_connection_id_included = header->version_flag ? CONNECTION_ID_PRESENT : CONNECTION_ID_ABSENT; - if (!do_not_synthesize_source_cid_for_short_header_ && - header->source_connection_id_included == CONNECTION_ID_ABSENT) { - QUICHE_DCHECK(header->source_connection_id.IsEmpty()); - if (perspective_ == Perspective::IS_CLIENT) { - header->source_connection_id = last_serialized_server_connection_id_; - } else { - header->source_connection_id = last_serialized_client_connection_id_; - } - } if (!ValidateReceivedConnectionIds(*header)) { return false; @@ -2925,13 +2906,6 @@ bool QuicFramer::ProcessIetfPacketHeader(QuicDataReader* reader, set_detailed_error("Client connection ID not supported in this version."); return false; } - if (!do_not_synthesize_source_cid_for_short_header_) { - if (perspective_ == Perspective::IS_CLIENT) { - header->source_connection_id = last_serialized_server_connection_id_; - } else { - header->source_connection_id = last_serialized_client_connection_id_; - } - } } return ValidateReceivedConnectionIds(*header); @@ -3232,20 +3206,14 @@ bool QuicFramer::ProcessIetfFrameData(QuicDataReader* reader, set_detailed_error("Unable to read frame type."); return RaiseError(QUIC_INVALID_FRAME_DATA); } - if (reject_unexpected_ietf_frame_types_) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_reject_unexpected_ietf_frame_types, 1, - 2); - if (!IsIetfFrameTypeExpectedForEncryptionLevel(frame_type, - decrypted_level)) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_reject_unexpected_ietf_frame_types, 2, - 2); - set_detailed_error(absl::StrCat( - "IETF frame type ", - QuicIetfFrameTypeString(static_cast<QuicIetfFrameType>(frame_type)), - " is unexpected at encryption level ", - EncryptionLevelToString(decrypted_level))); - return RaiseError(IETF_QUIC_PROTOCOL_VIOLATION); - } + if (!IsIetfFrameTypeExpectedForEncryptionLevel(frame_type, + decrypted_level)) { + set_detailed_error(absl::StrCat( + "IETF frame type ", + QuicIetfFrameTypeString(static_cast<QuicIetfFrameType>(frame_type)), + " is unexpected at encryption level ", + EncryptionLevelToString(decrypted_level))); + return RaiseError(IETF_QUIC_PROTOCOL_VIOLATION); } current_received_frame_type_ = frame_type; @@ -4872,10 +4840,8 @@ bool QuicFramer::DecryptPayload(size_t udp_packet_length, if ((current_key_phase_first_received_packet_number_.IsInitialized() && header.packet_number > current_key_phase_first_received_packet_number_) || - (GetQuicReloadableFlag(quic_fix_key_update_on_first_packet) && - !current_key_phase_first_received_packet_number_.IsInitialized() && + (!current_key_phase_first_received_packet_number_.IsInitialized() && !key_update_performed_)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_fix_key_update_on_first_packet); if (!next_decrypter_) { next_decrypter_ = visitor_->AdvanceKeysAndCreateCurrentOneRttDecrypter(); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_framer.h b/chromium/net/third_party/quiche/src/quic/core/quic_framer.h index e27c7f0727e..2920db351c4 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_framer.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_framer.h @@ -634,6 +634,8 @@ class QUIC_EXPORT_PRIVATE QuicFramer { Perspective perspective() const { return perspective_; } + QuicStreamFrameDataProducer* data_producer() const { return data_producer_; } + void set_data_producer(QuicStreamFrameDataProducer* data_producer) { data_producer_ = data_producer; } @@ -714,10 +716,6 @@ class QUIC_EXPORT_PRIVATE QuicFramer { drop_incoming_retry_packets_ = drop_incoming_retry_packets; } - bool do_not_synthesize_source_cid_for_short_header() const { - return do_not_synthesize_source_cid_for_short_header_; - } - private: friend class test::QuicFramerPeer; @@ -1174,14 +1172,6 @@ class QUIC_EXPORT_PRIVATE QuicFramer { // Indicates whether received RETRY packets should be dropped. bool drop_incoming_retry_packets_ = false; - bool reject_unexpected_ietf_frame_types_ = - GetQuicReloadableFlag(quic_reject_unexpected_ietf_frame_types); - - // Indicates whether source connection ID should be synthesized when read - // short header packet. - const bool do_not_synthesize_source_cid_for_short_header_ = - GetQuicReloadableFlag(quic_do_not_synthesize_source_cid_for_short_header); - // The length in bytes of the last packet number written to an IETF-framed // packet. size_t last_written_packet_number_length_; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc index 048a4b72ffb..63a1ad5ed4f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc @@ -34,7 +34,6 @@ #include "quic/test_tools/quic_framer_peer.h" #include "quic/test_tools/quic_test_utils.h" #include "quic/test_tools/simple_data_producer.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/test_tools/quiche_test_utils.h" using testing::_; @@ -1479,9 +1478,6 @@ TEST_P(QuicFramerTest, ClientConnectionIdFromShortHeaderToClient) { ASSERT_TRUE(visitor_.header_.get()); EXPECT_EQ(FramerTestConnectionId(), visitor_.header_->destination_connection_id); - if (!framer_.do_not_synthesize_source_cid_for_short_header()) { - EXPECT_EQ(TestConnectionId(0x33), visitor_.header_->source_connection_id); - } } // In short header packets from client to server, the client connection ID @@ -1515,9 +1511,6 @@ TEST_P(QuicFramerTest, ClientConnectionIdFromShortHeaderToServer) { ASSERT_TRUE(visitor_.header_.get()); EXPECT_EQ(FramerTestConnectionId(), visitor_.header_->destination_connection_id); - if (!framer_.do_not_synthesize_source_cid_for_short_header()) { - EXPECT_EQ(TestConnectionId(0x33), visitor_.header_->source_connection_id); - } } TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) { @@ -1567,9 +1560,6 @@ TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) { EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsError(QUIC_MISSING_PAYLOAD)); ASSERT_TRUE(visitor_.header_.get()); - if (!framer_.do_not_synthesize_source_cid_for_short_header()) { - EXPECT_EQ(FramerTestConnectionId(), visitor_.header_->source_connection_id); - } EXPECT_FALSE(visitor_.header_->reset_flag); EXPECT_FALSE(visitor_.header_->version_flag); EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number); @@ -9248,7 +9238,7 @@ TEST_P(QuicFramerTest, BuildPublicResetPacketWithEndpointId) { } TEST_P(QuicFramerTest, BuildIetfStatelessResetPacket) { - if (GetQuicReloadableFlag(quic_fix_stateless_reset)) { + if (GetQuicRestartFlag(quic_fix_stateless_reset2)) { // clang-format off unsigned char packet[] = { // 1st byte 01XX XXXX @@ -9282,13 +9272,12 @@ TEST_P(QuicFramerTest, BuildIetfStatelessResetPacket) { // Packets with length <= minimal stateless reset does not trigger stateless // reset. - EXPECT_QUIC_BUG( - std::unique_ptr<QuicEncryptedPacket> data2( - framer_.BuildIetfStatelessResetPacket( - FramerTestConnectionId(), - QuicFramer::GetMinStatelessResetPacketLength(), - kTestStatelessResetToken)), - "Tried to build stateless reset packet with received packet length"); + std::unique_ptr<QuicEncryptedPacket> data2( + framer_.BuildIetfStatelessResetPacket( + FramerTestConnectionId(), + QuicFramer::GetMinStatelessResetPacketLength(), + kTestStatelessResetToken)); + ASSERT_FALSE(data2); // Do not send stateless reset >= minimal stateless reset + 1 + max // connection ID length. @@ -15230,7 +15219,6 @@ TEST_P(QuicFramerTest, KeyUpdateOnFirstReceivedPacket) { // Key update is only used in QUIC+TLS. return; } - SetQuicReloadableFlag(quic_fix_key_update_on_first_packet, true); ASSERT_TRUE(framer_.version().KnowsWhichDecrypterToUse()); // Doesn't use SetDecrypterLevel since we want to use StrictTaggingDecrypter // instead of TestDecrypter. @@ -15262,8 +15250,7 @@ TEST_P(QuicFramerTest, KeyUpdateOnFirstReceivedPacket) { } TEST_P(QuicFramerTest, ErrorWhenUnexpectedFrameTypeEncountered) { - if (!GetQuicReloadableFlag(quic_reject_unexpected_ietf_frame_types) || - !VersionHasIetfQuicFrames(framer_.transport_version()) || + if (!VersionHasIetfQuicFrames(framer_.transport_version()) || !QuicVersionHasLongHeaderLengths(framer_.transport_version()) || !framer_.version().HasLongHeaderLengths()) { return; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_interval_deque.h b/chromium/net/third_party/quiche/src/quic/core/quic_interval_deque.h index ba27aa9d8f5..274c2e384df 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_interval_deque.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_interval_deque.h @@ -8,12 +8,12 @@ #include <algorithm> #include "absl/types/optional.h" -#include "quic/core/quic_circular_deque.h" #include "quic/core/quic_interval.h" #include "quic/core/quic_types.h" #include "quic/platform/api/quic_bug_tracker.h" #include "quic/platform/api/quic_export.h" #include "quic/platform/api/quic_logging.h" +#include "common/quiche_circular_deque.h" namespace quic { @@ -137,7 +137,7 @@ class QuicIntervalDequePeer; // // cached_index -> 1 // // container -> {{2, [25, 30)}, {3, [35, 50)}} -template <class T, class C = QUIC_NO_EXPORT QuicCircularDeque<T>> +template <class T, class C = QUIC_NO_EXPORT quiche::QuicheCircularDeque<T>> class QUIC_NO_EXPORT QuicIntervalDeque { public: class QUIC_NO_EXPORT Iterator { @@ -363,7 +363,7 @@ void QuicIntervalDeque<T, C>::PushBackUniversal(U&& item) { // Adding an empty interval is a bug. if (interval.Empty()) { QUIC_BUG(quic_bug_10862_3) - << "Trying to save empty interval to QuicCircularDeque."; + << "Trying to save empty interval to quiche::QuicheCircularDeque."; return; } container_.push_back(std::forward<U>(item)); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator.cc b/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator.cc index 27dbb9b48a8..11d2293bee5 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator.cc @@ -7,7 +7,6 @@ #include "quic/core/crypto/crypto_protocol.h" #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_bug_tracker.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator_test.cc index 1d855f2f55d..2f74d3ac7e7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator_test.cc @@ -9,7 +9,6 @@ #include "quic/platform/api/quic_expect_bug.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { namespace test { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils.h b/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils.h index 14c26dc8d73..5bbd875180b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils.h @@ -148,7 +148,7 @@ struct QUIC_EXPORT_PRIVATE BufferedWrite { // multiple packets at once via ::sendmmsg. // // Example: -// QuicCircularDeque<BufferedWrite> buffered_writes; +// quiche::QuicheCircularDeque<BufferedWrite> buffered_writes; // ... (Populate buffered_writes) ... // // QuicMMsgHdr mhdr( diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils_test.cc index 84259583063..588c2be8480 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils_test.cc @@ -12,9 +12,9 @@ #include <string> -#include "quic/core/quic_circular_deque.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_mock_syscall_wrapper.h" +#include "common/quiche_circular_deque.h" using testing::_; using testing::InSequence; @@ -28,8 +28,8 @@ class QuicLinuxSocketUtilsTest : public QuicTest { protected: WriteResult TestWriteMultiplePackets( int fd, - const QuicCircularDeque<BufferedWrite>::const_iterator& first, - const QuicCircularDeque<BufferedWrite>::const_iterator& last, + const quiche::QuicheCircularDeque<BufferedWrite>::const_iterator& first, + const quiche::QuicheCircularDeque<BufferedWrite>::const_iterator& last, int* num_packets_sent) { QuicMMsgHdr mhdr( first, last, kCmsgSpaceForIp, @@ -171,7 +171,7 @@ TEST_F(QuicLinuxSocketUtilsTest, QuicMsgHdr) { } TEST_F(QuicLinuxSocketUtilsTest, QuicMMsgHdr) { - QuicCircularDeque<BufferedWrite> buffered_writes; + quiche::QuicheCircularDeque<BufferedWrite> buffered_writes; char packet_buf1[1024]; char packet_buf2[512]; buffered_writes.emplace_back( @@ -205,7 +205,7 @@ TEST_F(QuicLinuxSocketUtilsTest, QuicMMsgHdr) { TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_NoPacketsToSend) { int num_packets_sent; - QuicCircularDeque<BufferedWrite> buffered_writes; + quiche::QuicheCircularDeque<BufferedWrite> buffered_writes; EXPECT_CALL(mock_syscalls_, Sendmmsg(_, _, _, _)).Times(0); @@ -216,7 +216,7 @@ TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_NoPacketsToSend) { TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_WriteBlocked) { int num_packets_sent; - QuicCircularDeque<BufferedWrite> buffered_writes; + quiche::QuicheCircularDeque<BufferedWrite> buffered_writes; buffered_writes.emplace_back(nullptr, 0, QuicIpAddress(), QuicSocketAddress(QuicIpAddress::Any4(), 0)); @@ -235,7 +235,7 @@ TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_WriteBlocked) { TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_WriteError) { int num_packets_sent; - QuicCircularDeque<BufferedWrite> buffered_writes; + quiche::QuicheCircularDeque<BufferedWrite> buffered_writes; buffered_writes.emplace_back(nullptr, 0, QuicIpAddress(), QuicSocketAddress(QuicIpAddress::Any4(), 0)); @@ -254,7 +254,7 @@ TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_WriteError) { TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_WriteSuccess) { int num_packets_sent; - QuicCircularDeque<BufferedWrite> buffered_writes; + quiche::QuicheCircularDeque<BufferedWrite> buffered_writes; const int kNumBufferedWrites = 10; static_assert(kNumBufferedWrites < 256, "Must be less than 256"); std::vector<std::string> buffer_holder; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.cc index 42aebe5ce4b..cbca89dae55 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.cc @@ -15,10 +15,12 @@ #include "absl/base/optimization.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "quic/core/crypto/crypto_protocol.h" #include "quic/core/frames/quic_frame.h" #include "quic/core/frames/quic_path_challenge_frame.h" #include "quic/core/frames/quic_stream_frame.h" +#include "quic/core/quic_chaos_protector.h" #include "quic/core/quic_connection_id.h" #include "quic/core/quic_constants.h" #include "quic/core/quic_data_writer.h" @@ -32,7 +34,6 @@ #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_server_stats.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { namespace { @@ -133,7 +134,8 @@ QuicPacketCreator::QuicPacketCreator(QuicConnectionId server_connection_id, flusher_attached_(false), fully_pad_crypto_handshake_packets_(true), latched_hard_max_packet_length_(0), - max_datagram_frame_size_(0) { + max_datagram_frame_size_(0), + chaos_protection_enabled_(false) { SetMaxPacketLength(kDefaultMaxPacketSize); if (!framer_->version().UsesTls()) { // QUIC+TLS negotiates the maximum datagram frame size via the @@ -159,21 +161,21 @@ bool QuicPacketCreator::CanSetMaxPacketLength() const { } void QuicPacketCreator::SetMaxPacketLength(QuicByteCount length) { - QUICHE_DCHECK(CanSetMaxPacketLength()); + QUICHE_DCHECK(CanSetMaxPacketLength()) << ENDPOINT; // Avoid recomputing |max_plaintext_size_| if the length does not actually // change. if (length == max_packet_length_) { return; } - QUIC_DVLOG(1) << "Updating packet creator max packet length from " + QUIC_DVLOG(1) << ENDPOINT << "Updating packet creator max packet length from " << max_packet_length_ << " to " << length; max_packet_length_ = length; max_plaintext_size_ = framer_->GetMaxPlaintextSize(max_packet_length_); QUIC_BUG_IF(quic_bug_12398_2, max_plaintext_size_ - PacketHeaderSize() < MinPlaintextPacketSize(framer_->version())) - << "Attempted to set max packet length too small"; + << ENDPOINT << "Attempted to set max packet length too small"; } void QuicPacketCreator::SetMaxDatagramFrameSize( @@ -192,7 +194,7 @@ void QuicPacketCreator::SetMaxDatagramFrameSize( } void QuicPacketCreator::SetSoftMaxPacketLength(QuicByteCount length) { - QUICHE_DCHECK(CanSetMaxPacketLength()); + QUICHE_DCHECK(CanSetMaxPacketLength()) << ENDPOINT; if (length > max_packet_length_) { QUIC_BUG(quic_bug_10752_2) << ENDPOINT @@ -204,11 +206,12 @@ void QuicPacketCreator::SetSoftMaxPacketLength(QuicByteCount length) { PacketHeaderSize() + MinPlaintextPacketSize(framer_->version())) { // Please note: this would not guarantee to fit next packet if the size of // packet header increases (e.g., encryption level changes). - QUIC_DLOG(INFO) << length << " is too small to fit packet header"; + QUIC_DLOG(INFO) << ENDPOINT << length + << " is too small to fit packet header"; RemoveSoftMaxPacketLength(); return; } - QUIC_DVLOG(1) << "Setting soft max packet length to: " << length; + QUIC_DVLOG(1) << ENDPOINT << "Setting soft max packet length to: " << length; latched_hard_max_packet_length_ = max_packet_length_; max_packet_length_ = length; max_plaintext_size_ = framer_->GetMaxPlaintextSize(length); @@ -218,18 +221,18 @@ void QuicPacketCreator::SetSoftMaxPacketLength(QuicByteCount length) { // A packet that is already open might send kQuicVersionSize bytes less than the // maximum packet size if we stop sending version before it is serialized. void QuicPacketCreator::StopSendingVersion() { - QUICHE_DCHECK(send_version_in_packet_); - QUICHE_DCHECK(!version().HasIetfInvariantHeader()); + QUICHE_DCHECK(send_version_in_packet_) << ENDPOINT; + QUICHE_DCHECK(!version().HasIetfInvariantHeader()) << ENDPOINT; send_version_in_packet_ = false; if (packet_size_ > 0) { - QUICHE_DCHECK_LT(kQuicVersionSize, packet_size_); + QUICHE_DCHECK_LT(kQuicVersionSize, packet_size_) << ENDPOINT; packet_size_ -= kQuicVersionSize; } } void QuicPacketCreator::SetDiversificationNonce( const DiversificationNonce& nonce) { - QUICHE_DCHECK(!have_diversification_nonce_); + QUICHE_DCHECK(!have_diversification_nonce_) << ENDPOINT; have_diversification_nonce_ = true; diversification_nonce_ = nonce; } @@ -240,14 +243,16 @@ void QuicPacketCreator::UpdatePacketNumberLength( if (!queued_frames_.empty()) { // Don't change creator state if there are frames queued. QUIC_BUG(quic_bug_10752_3) - << "Called UpdatePacketNumberLength with " << queued_frames_.size() + << ENDPOINT << "Called UpdatePacketNumberLength with " + << queued_frames_.size() << " queued_frames. First frame type:" << queued_frames_.front().type << " last frame type:" << queued_frames_.back().type; return; } const QuicPacketNumber next_packet_number = NextSendingPacketNumber(); - QUICHE_DCHECK_LE(least_packet_awaited_by_peer, next_packet_number); + QUICHE_DCHECK_LE(least_packet_awaited_by_peer, next_packet_number) + << ENDPOINT; const uint64_t current_delta = next_packet_number - least_packet_awaited_by_peer; const uint64_t delta = std::max(current_delta, max_packets_in_flight); @@ -273,7 +278,8 @@ void QuicPacketCreator::SkipNPacketNumbers( if (!queued_frames_.empty()) { // Don't change creator state if there are frames queued. QUIC_BUG(quic_bug_10752_4) - << "Called SkipNPacketNumbers with " << queued_frames_.size() + << ENDPOINT << "Called SkipNPacketNumbers with " + << queued_frames_.size() << " queued_frames. First frame type:" << queued_frames_.front().type << " last frame type:" << queued_frames_.back().type; return; @@ -281,7 +287,7 @@ void QuicPacketCreator::SkipNPacketNumbers( if (packet_.packet_number > packet_.packet_number + count) { // Skipping count packet numbers causes packet number wrapping around, // reject it. - QUIC_LOG(WARNING) << "Skipping " << count + QUIC_LOG(WARNING) << ENDPOINT << "Skipping " << count << " packet numbers causes packet number wrapping " "around, least_packet_awaited_by_peer: " << least_packet_awaited_by_peer @@ -300,7 +306,7 @@ bool QuicPacketCreator::ConsumeCryptoDataToFillCurrentPacket( bool needs_full_padding, TransmissionType transmission_type, QuicFrame* frame) { - QUIC_DVLOG(2) << "ConsumeCryptoDataToFillCurrentPacket " << level + QUIC_DVLOG(2) << ENDPOINT << "ConsumeCryptoDataToFillCurrentPacket " << level << " write_length " << write_length << " offset " << offset << (needs_full_padding ? " needs_full_padding" : "") << " " << transmission_type; @@ -338,7 +344,7 @@ bool QuicPacketCreator::ConsumeDataToFillCurrentPacket( const std::string error_details = "Client hello won't fit in a single packet."; QUIC_BUG(quic_bug_10752_5) - << error_details << " Constructed stream frame length: " + << ENDPOINT << error_details << " Constructed stream frame length: " << frame->stream_frame.data_length << " CHLO length: " << data_size; delegate_->OnUnrecoverableError(QUIC_CRYPTO_CHLO_TOO_LARGE, error_details); return false; @@ -422,16 +428,17 @@ void QuicPacketCreator::CreateStreamFrame(QuicStreamId id, GetSourceConnectionIdLength(), kIncludeVersion, IncludeNonceInPublicHeader(), PACKET_6BYTE_PACKET_NUMBER, GetRetryTokenLengthLength(), GetLengthLength(), offset) || - latched_hard_max_packet_length_ > 0); + latched_hard_max_packet_length_ > 0) + << ENDPOINT; QUIC_BUG_IF(quic_bug_12398_3, !HasRoomForStreamFrame(id, offset, data_size)) - << "No room for Stream frame, BytesFree: " << BytesFree() + << ENDPOINT << "No room for Stream frame, BytesFree: " << BytesFree() << " MinStreamFrameSize: " << QuicFramer::GetMinStreamFrameSize(framer_->transport_version(), id, offset, true, data_size); QUIC_BUG_IF(quic_bug_12398_4, data_size == 0 && !fin) - << "Creating a stream frame for stream ID:" << id + << ENDPOINT << "Creating a stream frame for stream ID:" << id << " with no data or fin."; size_t min_frame_size = QuicFramer::GetMinStreamFrameSize( framer_->transport_version(), id, offset, @@ -472,7 +479,7 @@ void QuicPacketCreator::FlushCurrentPacket() { external_buffer.release_buffer = nullptr; } - QUICHE_DCHECK_EQ(nullptr, packet_.encrypted_buffer); + QUICHE_DCHECK_EQ(nullptr, packet_.encrypted_buffer) << ENDPOINT; if (!SerializePacket(std::move(external_buffer), kMaxOutgoingPacketSize)) { return; } @@ -480,7 +487,8 @@ void QuicPacketCreator::FlushCurrentPacket() { } void QuicPacketCreator::OnSerializedPacket() { - QUIC_BUG_IF(quic_bug_12398_5, packet_.encrypted_buffer == nullptr); + QUIC_BUG_IF(quic_bug_12398_5, packet_.encrypted_buffer == nullptr) + << ENDPOINT; SerializedPacket packet(std::move(packet_)); ClearPacket(); @@ -499,10 +507,10 @@ void QuicPacketCreator::ClearPacket() { packet_.has_message = false; packet_.fate = SEND_TO_WRITER; QUIC_BUG_IF(quic_bug_12398_6, packet_.release_encrypted_buffer != nullptr) - << "packet_.release_encrypted_buffer should be empty"; + << ENDPOINT << "packet_.release_encrypted_buffer should be empty"; packet_.release_encrypted_buffer = nullptr; - QUICHE_DCHECK(packet_.retransmittable_frames.empty()); - QUICHE_DCHECK(packet_.nonretransmittable_frames.empty()); + QUICHE_DCHECK(packet_.retransmittable_frames.empty()) << ENDPOINT; + QUICHE_DCHECK(packet_.nonretransmittable_frames.empty()) << ENDPOINT; packet_.largest_acked.Clear(); needs_full_padding_ = false; } @@ -515,6 +523,7 @@ size_t QuicPacketCreator::ReserializeInitialPacketInCoalescedPacket( QUIC_BUG_IF(quic_bug_12398_7, packet.encryption_level != ENCRYPTION_INITIAL); QUIC_BUG_IF(quic_bug_12398_8, packet.nonretransmittable_frames.empty() && packet.retransmittable_frames.empty()) + << ENDPOINT << "Attempt to serialize empty ENCRYPTION_INITIAL packet in coalesced " "packet"; ScopedPacketContextSwitcher switcher( @@ -523,13 +532,15 @@ size_t QuicPacketCreator::ReserializeInitialPacketInCoalescedPacket( packet.packet_number_length, packet.encryption_level, &packet_); for (const QuicFrame& frame : packet.nonretransmittable_frames) { if (!AddFrame(frame, packet.transmission_type)) { - QUIC_BUG(quic_bug_10752_6) << "Failed to serialize frame: " << frame; + QUIC_BUG(quic_bug_10752_6) + << ENDPOINT << "Failed to serialize frame: " << frame; return 0; } } for (const QuicFrame& frame : packet.retransmittable_frames) { if (!AddFrame(frame, packet.transmission_type)) { - QUIC_BUG(quic_bug_10752_7) << "Failed to serialize frame: " << frame; + QUIC_BUG(quic_bug_10752_7) + << ENDPOINT << "Failed to serialize frame: " << frame; return 0; } } @@ -539,7 +550,7 @@ size_t QuicPacketCreator::ReserializeInitialPacketInCoalescedPacket( if (!AddFrame(QuicFrame(QuicPaddingFrame(padding_size)), packet.transmission_type)) { QUIC_BUG(quic_bug_10752_8) - << "Failed to add padding of size " << padding_size + << ENDPOINT << "Failed to add padding of size " << padding_size << " when serializing ENCRYPTION_INITIAL " "packet in coalesced packet"; return 0; @@ -566,8 +577,9 @@ void QuicPacketCreator::CreateAndSerializeStreamFrame( TransmissionType transmission_type, size_t* num_bytes_consumed) { // TODO(b/167222597): consider using ScopedSerializationFailureHandler. - QUICHE_DCHECK(queued_frames_.empty()); - QUICHE_DCHECK(!QuicUtils::IsCryptoStreamId(transport_version(), id)); + QUICHE_DCHECK(queued_frames_.empty()) << ENDPOINT; + QUICHE_DCHECK(!QuicUtils::IsCryptoStreamId(transport_version(), id)) + << ENDPOINT; // Write out the packet header QuicPacketHeader header; FillPacketHeader(&header); @@ -590,13 +602,13 @@ void QuicPacketCreator::CreateAndSerializeStreamFrame( QuicDataWriter writer(kMaxOutgoingPacketSize, encrypted_buffer); size_t length_field_offset = 0; if (!framer_->AppendPacketHeader(header, &writer, &length_field_offset)) { - QUIC_BUG(quic_bug_10752_9) << "AppendPacketHeader failed"; + QUIC_BUG(quic_bug_10752_9) << ENDPOINT << "AppendPacketHeader failed"; return; } // Create a Stream frame with the remaining space. QUIC_BUG_IF(quic_bug_12398_9, iov_offset == write_length && !fin) - << "Creating a stream frame with no data or fin."; + << ENDPOINT << "Creating a stream frame with no data or fin."; const size_t remaining_data_size = write_length - iov_offset; size_t min_frame_size = QuicFramer::GetMinStreamFrameSize( framer_->transport_version(), id, stream_offset, @@ -631,18 +643,18 @@ void QuicPacketCreator::CreateAndSerializeStreamFrame( // into one method that takes a QuicStreamFrame, if warranted. bool omit_frame_length = !needs_padding; if (!framer_->AppendTypeByte(QuicFrame(frame), omit_frame_length, &writer)) { - QUIC_BUG(quic_bug_10752_10) << "AppendTypeByte failed"; + QUIC_BUG(quic_bug_10752_10) << ENDPOINT << "AppendTypeByte failed"; return; } if (!framer_->AppendStreamFrame(frame, omit_frame_length, &writer)) { - QUIC_BUG(quic_bug_10752_11) << "AppendStreamFrame failed"; + QUIC_BUG(quic_bug_10752_11) << ENDPOINT << "AppendStreamFrame failed"; return; } if (needs_padding && plaintext_bytes_written < MinPlaintextPacketSize(framer_->version()) && !writer.WritePaddingBytes(MinPlaintextPacketSize(framer_->version()) - plaintext_bytes_written)) { - QUIC_BUG(quic_bug_10752_12) << "Unable to add padding bytes"; + QUIC_BUG(quic_bug_10752_12) << ENDPOINT << "Unable to add padding bytes"; return; } @@ -655,14 +667,15 @@ void QuicPacketCreator::CreateAndSerializeStreamFrame( QUICHE_DCHECK(packet_.encryption_level == ENCRYPTION_FORWARD_SECURE || packet_.encryption_level == ENCRYPTION_ZERO_RTT) - << packet_.encryption_level; + << ENDPOINT << packet_.encryption_level; size_t encrypted_length = framer_->EncryptInPlace( packet_.encryption_level, packet_.packet_number, GetStartOfEncryptedData(framer_->transport_version(), header), writer.length(), kMaxOutgoingPacketSize, encrypted_buffer); if (encrypted_length == 0) { QUIC_BUG(quic_bug_10752_13) - << "Failed to encrypt packet number " << header.packet_number; + << ENDPOINT << "Failed to encrypt packet number " + << header.packet_number; return; } // TODO(ianswett): Optimize the storage so RetransmitableFrames can be @@ -724,7 +737,6 @@ size_t QuicPacketCreator::ExpansionOnNewFrameWithLastFrame( } size_t QuicPacketCreator::BytesFree() const { - QUICHE_DCHECK_GE(max_plaintext_size_, PacketSize()); return max_plaintext_size_ - std::min(max_plaintext_size_, PacketSize() + ExpansionOnNewFrame()); } @@ -743,22 +755,51 @@ bool QuicPacketCreator::AddPaddedSavedFrame( return false; } +absl::optional<size_t> +QuicPacketCreator::MaybeBuildDataPacketWithChaosProtection( + const QuicPacketHeader& header, + char* buffer) { + if (!chaos_protection_enabled_ || + packet_.encryption_level != ENCRYPTION_INITIAL || + !framer_->version().UsesCryptoFrames() || queued_frames_.size() != 2u || + queued_frames_[0].type != CRYPTO_FRAME || + queued_frames_[1].type != PADDING_FRAME || + // Do not perform chaos protection if we do not have a known number of + // padding bytes to work with. + queued_frames_[1].padding_frame.num_padding_bytes <= 0 || + // Chaos protection relies on the framer using a crypto data producer, + // which is always the case in practice. + framer_->data_producer() == nullptr) { + return absl::nullopt; + } + const QuicCryptoFrame& crypto_frame = *queued_frames_[0].crypto_frame; + if (packet_.encryption_level != crypto_frame.level) { + QUIC_BUG(chaos frame level) + << ENDPOINT << packet_.encryption_level << " != " << crypto_frame.level; + return absl::nullopt; + } + QuicChaosProtector chaos_protector( + crypto_frame, queued_frames_[1].padding_frame.num_padding_bytes, + packet_size_, framer_, random_); + return chaos_protector.BuildDataPacket(header, buffer); +} + bool QuicPacketCreator::SerializePacket(QuicOwnedPacketBuffer encrypted_buffer, size_t encrypted_buffer_len) { if (packet_.encrypted_buffer != nullptr) { const std::string error_details = "Packet's encrypted buffer is not empty before serialization"; - QUIC_BUG(quic_bug_10752_14) << error_details; + QUIC_BUG(quic_bug_10752_14) << ENDPOINT << error_details; delegate_->OnUnrecoverableError(QUIC_FAILED_TO_SERIALIZE_PACKET, error_details); return false; } ScopedSerializationFailureHandler handler(this); - QUICHE_DCHECK_LT(0u, encrypted_buffer_len); + QUICHE_DCHECK_LT(0u, encrypted_buffer_len) << ENDPOINT; QUIC_BUG_IF(quic_bug_12398_10, queued_frames_.empty() && pending_padding_bytes_ == 0) - << "Attempt to serialize empty packet"; + << ENDPOINT << "Attempt to serialize empty packet"; QuicPacketHeader header; // FillPacketHeader increments packet_number_. FillPacketHeader(&header); @@ -789,15 +830,25 @@ bool QuicPacketCreator::SerializePacket(QuicOwnedPacketBuffer encrypted_buffer, return false; } - QUICHE_DCHECK_GE(max_plaintext_size_, packet_size_); + QUICHE_DCHECK_GE(max_plaintext_size_, packet_size_) << ENDPOINT; // Use the packet_size_ instead of the buffer size to ensure smaller // packet sizes are properly used. - size_t length = - framer_->BuildDataPacket(header, queued_frames_, encrypted_buffer.buffer, - packet_size_, packet_.encryption_level); + + size_t length; + absl::optional<size_t> length_with_chaos_protection = + MaybeBuildDataPacketWithChaosProtection(header, encrypted_buffer.buffer); + if (length_with_chaos_protection.has_value()) { + length = length_with_chaos_protection.value(); + } else { + length = framer_->BuildDataPacket(header, queued_frames_, + encrypted_buffer.buffer, packet_size_, + packet_.encryption_level); + } + if (length == 0) { QUIC_BUG(quic_bug_10752_16) - << "Failed to serialize " << QuicFramesToString(queued_frames_) + << ENDPOINT << "Failed to serialize " + << QuicFramesToString(queued_frames_) << " at encryption_level: " << packet_.encryption_level << ", needs_full_padding_: " << needs_full_padding_ << ", pending_padding_bytes_: " << pending_padding_bytes_ @@ -818,7 +869,7 @@ bool QuicPacketCreator::SerializePacket(QuicOwnedPacketBuffer encrypted_buffer, // Because of possible truncation, we can't be confident that our // packet size calculation worked correctly. if (!possibly_truncated_by_length) { - QUICHE_DCHECK_EQ(packet_size_, length); + QUICHE_DCHECK_EQ(packet_size_, length) << ENDPOINT; } const size_t encrypted_length = framer_->EncryptInPlace( packet_.encryption_level, packet_.packet_number, @@ -826,7 +877,8 @@ bool QuicPacketCreator::SerializePacket(QuicOwnedPacketBuffer encrypted_buffer, encrypted_buffer_len, encrypted_buffer.buffer); if (encrypted_length == 0) { QUIC_BUG(quic_bug_10752_17) - << "Failed to encrypt packet number " << packet_.packet_number; + << ENDPOINT << "Failed to encrypt packet number " + << packet_.packet_number; return false; } @@ -843,6 +895,7 @@ std::unique_ptr<SerializedPacket> QuicPacketCreator::SerializeConnectivityProbingPacket() { QUIC_BUG_IF(quic_bug_12398_11, VersionHasIetfQuicFrames(framer_->transport_version())) + << ENDPOINT << "Must not be version 99 to serialize padded ping connectivity probe"; RemoveSoftMaxPacketLength(); QuicPacketHeader header; @@ -855,14 +908,15 @@ QuicPacketCreator::SerializeConnectivityProbingPacket() { std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); size_t length = BuildConnectivityProbingPacket( header, buffer.get(), max_plaintext_size_, packet_.encryption_level); - QUICHE_DCHECK(length); + QUICHE_DCHECK(length) << ENDPOINT; - QUICHE_DCHECK_EQ(packet_.encryption_level, ENCRYPTION_FORWARD_SECURE); + QUICHE_DCHECK_EQ(packet_.encryption_level, ENCRYPTION_FORWARD_SECURE) + << ENDPOINT; const size_t encrypted_length = framer_->EncryptInPlace( packet_.encryption_level, packet_.packet_number, GetStartOfEncryptedData(framer_->transport_version(), header), length, kMaxOutgoingPacketSize, buffer.get()); - QUICHE_DCHECK(encrypted_length); + QUICHE_DCHECK(encrypted_length) << ENDPOINT; std::unique_ptr<SerializedPacket> serialize_packet(new SerializedPacket( header.packet_number, header.packet_number_length, buffer.release(), @@ -882,6 +936,7 @@ QuicPacketCreator::SerializePathChallengeConnectivityProbingPacket( const QuicPathFrameBuffer& payload) { QUIC_BUG_IF(quic_bug_12398_12, !VersionHasIetfQuicFrames(framer_->transport_version())) + << ENDPOINT << "Must be version 99 to serialize path challenge connectivity probe, " "is version " << framer_->transport_version(); @@ -896,14 +951,15 @@ QuicPacketCreator::SerializePathChallengeConnectivityProbingPacket( size_t length = BuildPaddedPathChallengePacket(header, buffer.get(), max_plaintext_size_, payload, packet_.encryption_level); - QUICHE_DCHECK(length); + QUICHE_DCHECK(length) << ENDPOINT; - QUICHE_DCHECK_EQ(packet_.encryption_level, ENCRYPTION_FORWARD_SECURE); + QUICHE_DCHECK_EQ(packet_.encryption_level, ENCRYPTION_FORWARD_SECURE) + << ENDPOINT; const size_t encrypted_length = framer_->EncryptInPlace( packet_.encryption_level, packet_.packet_number, GetStartOfEncryptedData(framer_->transport_version(), header), length, kMaxOutgoingPacketSize, buffer.get()); - QUICHE_DCHECK(encrypted_length); + QUICHE_DCHECK(encrypted_length) << ENDPOINT; std::unique_ptr<SerializedPacket> serialize_packet( new SerializedPacket(header.packet_number, header.packet_number_length, @@ -921,10 +977,11 @@ QuicPacketCreator::SerializePathChallengeConnectivityProbingPacket( std::unique_ptr<SerializedPacket> QuicPacketCreator::SerializePathResponseConnectivityProbingPacket( - const QuicCircularDeque<QuicPathFrameBuffer>& payloads, + const quiche::QuicheCircularDeque<QuicPathFrameBuffer>& payloads, const bool is_padded) { QUIC_BUG_IF(quic_bug_12398_13, !VersionHasIetfQuicFrames(framer_->transport_version())) + << ENDPOINT << "Must be version 99 to serialize path response connectivity probe, is " "version " << framer_->transport_version(); @@ -939,14 +996,15 @@ QuicPacketCreator::SerializePathResponseConnectivityProbingPacket( size_t length = BuildPathResponsePacket(header, buffer.get(), max_plaintext_size_, payloads, is_padded, packet_.encryption_level); - QUICHE_DCHECK(length); + QUICHE_DCHECK(length) << ENDPOINT; - QUICHE_DCHECK_EQ(packet_.encryption_level, ENCRYPTION_FORWARD_SECURE); + QUICHE_DCHECK_EQ(packet_.encryption_level, ENCRYPTION_FORWARD_SECURE) + << ENDPOINT; const size_t encrypted_length = framer_->EncryptInPlace( packet_.encryption_level, packet_.packet_number, GetStartOfEncryptedData(framer_->transport_version(), header), length, kMaxOutgoingPacketSize, buffer.get()); - QUICHE_DCHECK(encrypted_length); + QUICHE_DCHECK(encrypted_length) << ENDPOINT; std::unique_ptr<SerializedPacket> serialize_packet( new SerializedPacket(header.packet_number, header.packet_number_length, @@ -968,7 +1026,8 @@ size_t QuicPacketCreator::BuildPaddedPathChallengePacket( size_t packet_length, const QuicPathFrameBuffer& payload, EncryptionLevel level) { - QUICHE_DCHECK(VersionHasIetfQuicFrames(framer_->transport_version())); + QUICHE_DCHECK(VersionHasIetfQuicFrames(framer_->transport_version())) + << ENDPOINT; QuicFrames frames; // Write a PATH_CHALLENGE frame, which has a random 8-byte payload @@ -991,15 +1050,17 @@ size_t QuicPacketCreator::BuildPathResponsePacket( const QuicPacketHeader& header, char* buffer, size_t packet_length, - const QuicCircularDeque<QuicPathFrameBuffer>& payloads, + const quiche::QuicheCircularDeque<QuicPathFrameBuffer>& payloads, const bool is_padded, EncryptionLevel level) { if (payloads.empty()) { QUIC_BUG(quic_bug_12398_14) + << ENDPOINT << "Attempt to generate connectivity response with no request payloads"; return 0; } - QUICHE_DCHECK(VersionHasIetfQuicFrames(framer_->transport_version())); + QUICHE_DCHECK(VersionHasIetfQuicFrames(framer_->transport_version())) + << ENDPOINT; std::vector<std::unique_ptr<QuicPathResponseFrame>> path_response_frames; for (const QuicPathFrameBuffer& payload : payloads) { @@ -1052,12 +1113,12 @@ size_t QuicPacketCreator::SerializeCoalescedPacket( size_t buffer_len) { if (HasPendingFrames()) { QUIC_BUG(quic_bug_10752_18) - << "Try to serialize coalesced packet with pending frames"; + << ENDPOINT << "Try to serialize coalesced packet with pending frames"; return 0; } RemoveSoftMaxPacketLength(); QUIC_BUG_IF(quic_bug_12398_15, coalesced.length() == 0) - << "Attempt to serialize empty coalesced packet"; + << ENDPOINT << "Attempt to serialize empty coalesced packet"; size_t packet_length = 0; if (coalesced.initial_packet() != nullptr) { // Padding coalesced packet containing initial packet to full. @@ -1073,6 +1134,7 @@ size_t QuicPacketCreator::SerializeCoalescedPacket( *coalesced.initial_packet(), padding_size, buffer, buffer_len); if (initial_length == 0) { QUIC_BUG(quic_bug_10752_19) + << ENDPOINT << "Failed to reserialize ENCRYPTION_INITIAL packet in " "coalesced packet"; return 0; @@ -1141,7 +1203,8 @@ QuicConnectionIdIncluded QuicPacketCreator::GetSourceConnectionIdIncluded() QuicConnectionIdLength QuicPacketCreator::GetDestinationConnectionIdLength() const { QUICHE_DCHECK(QuicUtils::IsConnectionIdValidForVersion(server_connection_id_, - transport_version())); + transport_version())) + << ENDPOINT; return GetDestinationConnectionIdIncluded() == CONNECTION_ID_PRESENT ? static_cast<QuicConnectionIdLength>( GetDestinationConnectionId().length()) @@ -1150,7 +1213,8 @@ QuicConnectionIdLength QuicPacketCreator::GetDestinationConnectionIdLength() QuicConnectionIdLength QuicPacketCreator::GetSourceConnectionIdLength() const { QUICHE_DCHECK(QuicUtils::IsConnectionIdValidForVersion(server_connection_id_, - transport_version())); + transport_version())) + << ENDPOINT; return GetSourceConnectionIdIncluded() == CONNECTION_ID_PRESENT ? static_cast<QuicConnectionIdLength>( GetSourceConnectionId().length()) @@ -1201,8 +1265,10 @@ bool QuicPacketCreator::ConsumeRetransmittableControlFrame( QUIC_BUG_IF(quic_bug_12398_16, IsControlFrame(frame.type) && !GetControlFrameId(frame) && frame.type != PING_FRAME) + << ENDPOINT << "Adding a control frame with no control frame id: " << frame; - QUICHE_DCHECK(QuicUtils::IsRetransmittableFrame(frame.type)) << frame; + QUICHE_DCHECK(QuicUtils::IsRetransmittableFrame(frame.type)) + << ENDPOINT << frame; MaybeBundleAckOpportunistically(); if (HasPendingFrames()) { if (AddFrame(frame, next_transmission_type_)) { @@ -1210,7 +1276,7 @@ bool QuicPacketCreator::ConsumeRetransmittableControlFrame( return true; } } - QUICHE_DCHECK(!HasPendingFrames()); + QUICHE_DCHECK(!HasPendingFrames()) << ENDPOINT; if (frame.type != PING_FRAME && frame.type != CONNECTION_CLOSE_FRAME && !delegate_->ShouldGeneratePacket(HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)) { @@ -1219,7 +1285,7 @@ bool QuicPacketCreator::ConsumeRetransmittableControlFrame( } const bool success = AddFrame(frame, next_transmission_type_); QUIC_BUG_IF(quic_bug_10752_20, !success) - << "Failed to add frame:" << frame + << ENDPOINT << "Failed to add frame:" << frame << " transmission_type:" << next_transmission_type_; return success; } @@ -1229,13 +1295,14 @@ QuicConsumedData QuicPacketCreator::ConsumeData(QuicStreamId id, QuicStreamOffset offset, StreamSendingState state) { QUIC_BUG_IF(quic_bug_10752_21, !flusher_attached_) + << ENDPOINT << "Packet flusher is not attached when " "generator tries to write stream data."; bool has_handshake = QuicUtils::IsCryptoStreamId(transport_version(), id); MaybeBundleAckOpportunistically(); bool fin = state != NO_FIN; QUIC_BUG_IF(quic_bug_12398_17, has_handshake && fin) - << "Handshake packets should never send a fin"; + << ENDPOINT << "Handshake packets should never send a fin"; // To make reasoning about crypto frames easier, we don't combine them with // other retransmittable frames in a single packet. if (has_handshake && HasPendingRetransmittableFrames()) { @@ -1250,7 +1317,8 @@ QuicConsumedData QuicPacketCreator::ConsumeData(QuicStreamId id, } if (!fin && (write_length == 0)) { - QUIC_BUG(quic_bug_10752_22) << "Attempt to consume empty data without FIN."; + QUIC_BUG(quic_bug_10752_22) + << ENDPOINT << "Attempt to consume empty data without FIN."; return QuicConsumedData(0, false); } // We determine if we can enter the fast path before executing @@ -1273,7 +1341,8 @@ QuicConsumedData QuicPacketCreator::ConsumeData(QuicStreamId id, next_transmission_type_, &frame)) { // The creator is always flushed if there's not enough room for a new // stream frame before ConsumeData, so ConsumeData should always succeed. - QUIC_BUG(quic_bug_10752_23) << "Failed to ConsumeData, stream:" << id; + QUIC_BUG(quic_bug_10752_23) + << ENDPOINT << "Failed to ConsumeData, stream:" << id; return QuicConsumedData(0, false); } @@ -1285,7 +1354,8 @@ QuicConsumedData QuicPacketCreator::ConsumeData(QuicStreamId id, AddRandomPadding(); } QUICHE_DCHECK(total_bytes_consumed == write_length || - (bytes_consumed > 0 && HasPendingFrames())); + (bytes_consumed > 0 && HasPendingFrames())) + << ENDPOINT; if (total_bytes_consumed == write_length) { // We're done writing the data. Exit the loop. @@ -1320,7 +1390,8 @@ QuicConsumedData QuicPacketCreator::ConsumeDataFastPath( QuicStreamOffset offset, bool fin, size_t total_bytes_consumed) { - QUICHE_DCHECK(!QuicUtils::IsCryptoStreamId(transport_version(), id)); + QUICHE_DCHECK(!QuicUtils::IsCryptoStreamId(transport_version(), id)) + << ENDPOINT; if (AttemptingToSendUnencryptedStreamData()) { return QuicConsumedData(total_bytes_consumed, fin && (total_bytes_consumed == write_length)); @@ -1337,7 +1408,7 @@ QuicConsumedData QuicPacketCreator::ConsumeDataFastPath( if (bytes_consumed == 0) { const std::string error_details = "Failed in CreateAndSerializeStreamFrame."; - QUIC_BUG(quic_bug_10752_24) << error_details; + QUIC_BUG(quic_bug_10752_24) << ENDPOINT << error_details; delegate_->OnUnrecoverableError(QUIC_FAILED_TO_SERIALIZE_PACKET, error_details); break; @@ -1352,9 +1423,10 @@ QuicConsumedData QuicPacketCreator::ConsumeDataFastPath( size_t QuicPacketCreator::ConsumeCryptoData(EncryptionLevel level, size_t write_length, QuicStreamOffset offset) { - QUIC_DVLOG(2) << "ConsumeCryptoData " << level << " write_length " + QUIC_DVLOG(2) << ENDPOINT << "ConsumeCryptoData " << level << " write_length " << write_length << " offset " << offset; QUIC_BUG_IF(quic_bug_10752_25, !flusher_attached_) + << ENDPOINT << "Packet flusher is not attached when " "generator tries to write crypto data."; MaybeBundleAckOpportunistically(); @@ -1381,7 +1453,7 @@ size_t QuicPacketCreator::ConsumeCryptoData(EncryptionLevel level, // assuming here that they won't occupy so much of the packet that a // CRYPTO frame won't fit. QUIC_BUG(quic_bug_10752_26) - << "Failed to ConsumeCryptoData at level " << level; + << ENDPOINT << "Failed to ConsumeCryptoData at level " << level; return 0; } total_bytes_consumed += frame.crypto_frame->data_length; @@ -1398,6 +1470,7 @@ void QuicPacketCreator::GenerateMtuDiscoveryPacket(QuicByteCount target_mtu) { // MTU discovery frames must be sent by themselves. if (!CanSetMaxPacketLength()) { QUIC_BUG(quic_bug_10752_27) + << ENDPOINT << "MTU discovery packets should only be sent when no other " << "frames needs to be sent."; return; @@ -1416,7 +1489,7 @@ void QuicPacketCreator::GenerateMtuDiscoveryPacket(QuicByteCount target_mtu) { // The only reason AddFrame can fail is that the packet is too full to fit in // a ping. This is not possible for any sane MTU. QUIC_BUG_IF(quic_bug_10752_28, !success) - << "Failed to send path MTU target_mtu:" << target_mtu + << ENDPOINT << "Failed to send path MTU target_mtu:" << target_mtu << " transmission_type:" << next_transmission_type_; // Reset the packet length back. @@ -1435,12 +1508,13 @@ void QuicPacketCreator::MaybeBundleAckOpportunistically() { const bool flushed = FlushAckFrame(delegate_->MaybeBundleAckOpportunistically()); QUIC_BUG_IF(quic_bug_10752_29, !flushed) - << "Failed to flush ACK frame. encryption_level:" + << ENDPOINT << "Failed to flush ACK frame. encryption_level:" << packet_.encryption_level; } bool QuicPacketCreator::FlushAckFrame(const QuicFrames& frames) { QUIC_BUG_IF(quic_bug_10752_30, !flusher_attached_) + << ENDPOINT << "Packet flusher is not attached when " "generator tries to send ACK frame."; // MaybeBundleAckOpportunistically could be called nestedly when sending a @@ -1448,16 +1522,18 @@ bool QuicPacketCreator::FlushAckFrame(const QuicFrames& frames) { QUIC_BUG_IF(quic_bug_12398_18, GetQuicReloadableFlag(quic_single_ack_in_packet2) && !frames.empty() && has_ack()) - << "Trying to flush " << frames << " when there is ACK queued"; + << ENDPOINT << "Trying to flush " << frames + << " when there is ACK queued"; for (const auto& frame : frames) { - QUICHE_DCHECK(frame.type == ACK_FRAME || frame.type == STOP_WAITING_FRAME); + QUICHE_DCHECK(frame.type == ACK_FRAME || frame.type == STOP_WAITING_FRAME) + << ENDPOINT; if (HasPendingFrames()) { if (AddFrame(frame, next_transmission_type_)) { // There is pending frames and current frame fits. continue; } } - QUICHE_DCHECK(!HasPendingFrames()); + QUICHE_DCHECK(!HasPendingFrames()) << ENDPOINT; // There is no pending frames, consult the delegate whether a packet can be // generated. if (!delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, @@ -1465,7 +1541,8 @@ bool QuicPacketCreator::FlushAckFrame(const QuicFrames& frames) { return false; } const bool success = AddFrame(frame, next_transmission_type_); - QUIC_BUG_IF(quic_bug_10752_31, !success) << "Failed to flush " << frame; + QUIC_BUG_IF(quic_bug_10752_31, !success) + << ENDPOINT << "Failed to flush " << frame; } return true; } @@ -1488,7 +1565,7 @@ void QuicPacketCreator::Flush() { if (GetQuicFlag(FLAGS_quic_export_write_path_stats_at_server)) { if (!write_start_packet_number_.IsInitialized()) { QUIC_BUG(quic_bug_10752_32) - << "write_start_packet_number is not initialized"; + << ENDPOINT << "write_start_packet_number is not initialized"; return; } QUIC_SERVER_HISTOGRAM_COUNTS( @@ -1522,6 +1599,7 @@ void QuicPacketCreator::SetTransmissionType(TransmissionType type) { MessageStatus QuicPacketCreator::AddMessageFrame(QuicMessageId message_id, QuicMemSliceSpan message) { QUIC_BUG_IF(quic_bug_10752_33, !flusher_attached_) + << ENDPOINT << "Packet flusher is not attached when " "generator tries to add message frame."; MaybeBundleAckOpportunistically(); @@ -1535,7 +1613,8 @@ MessageStatus QuicPacketCreator::AddMessageFrame(QuicMessageId message_id, QuicMessageFrame* frame = new QuicMessageFrame(message_id, message); const bool success = AddFrame(QuicFrame(frame), next_transmission_type_); if (!success) { - QUIC_BUG(quic_bug_10752_34) << "Failed to send message " << message_id; + QUIC_BUG(quic_bug_10752_34) + << ENDPOINT << "Failed to send message " << message_id; delete frame; return MESSAGE_STATUS_INTERNAL_ERROR; } @@ -1564,7 +1643,8 @@ void QuicPacketCreator::FillPacketHeader(QuicPacketHeader* header) { header->reset_flag = false; header->version_flag = IncludeVersionInHeader(); if (IncludeNonceInPublicHeader()) { - QUICHE_DCHECK_EQ(Perspective::IS_SERVER, framer_->perspective()); + QUICHE_DCHECK_EQ(Perspective::IS_SERVER, framer_->perspective()) + << ENDPOINT; header->nonce = &diversification_nonce_; } else { header->nonce = nullptr; @@ -1640,7 +1720,8 @@ bool QuicPacketCreator::AddFrame(const QuicFrame& frame, frame.type != MESSAGE_FRAME && frame.type != NEW_TOKEN_FRAME && frame.type != RETIRE_CONNECTION_ID_FRAME && frame.type != ACK_FREQUENCY_FRAME)) - << frame.type << " not allowed at " << packet_.encryption_level; + << ENDPOINT << frame.type << " not allowed at " + << packet_.encryption_level; if (frame.type == STREAM_FRAME) { if (MaybeCoalesceStreamFrame(frame.stream_frame)) { @@ -1656,7 +1737,7 @@ bool QuicPacketCreator::AddFrame(const QuicFrame& frame, QUICHE_DCHECK(frame.type != ACK_FRAME || (!frame.ack_frame->packets.Empty() && frame.ack_frame->packets.Max() == frame.ack_frame->largest_acked)) - << "Invalid ACK frame: " << frame; + << ENDPOINT << "Invalid ACK frame: " << frame; size_t frame_len = GetSerializedFrameLength(frame); if (frame_len == 0 && RemoveSoftMaxPacketLength()) { @@ -1664,7 +1745,8 @@ bool QuicPacketCreator::AddFrame(const QuicFrame& frame, frame_len = GetSerializedFrameLength(frame); } if (frame_len == 0) { - QUIC_DVLOG(1) << "Flushing because current open packet is full when adding " + QUIC_DVLOG(1) << ENDPOINT + << "Flushing because current open packet is full when adding " << frame; FlushCurrentPacket(); return false; @@ -1672,7 +1754,7 @@ bool QuicPacketCreator::AddFrame(const QuicFrame& frame, if (queued_frames_.empty()) { packet_size_ = PacketHeaderSize(); } - QUICHE_DCHECK_LT(0u, packet_size_); + QUICHE_DCHECK_LT(0u, packet_size_) << ENDPOINT; packet_size_ += ExpansionOnNewFrame() + frame_len; @@ -1749,12 +1831,14 @@ bool QuicPacketCreator::MaybeCoalesceStreamFrame(const QuicStreamFrame& frame) { // The back of retransmittable frames must be the same as the original // queued frames' back. - QUICHE_DCHECK_EQ(packet_.retransmittable_frames.back().type, STREAM_FRAME); + QUICHE_DCHECK_EQ(packet_.retransmittable_frames.back().type, STREAM_FRAME) + << ENDPOINT; QuicStreamFrame* retransmittable = &packet_.retransmittable_frames.back().stream_frame; - QUICHE_DCHECK_EQ(retransmittable->stream_id, frame.stream_id); + QUICHE_DCHECK_EQ(retransmittable->stream_id, frame.stream_id) << ENDPOINT; QUICHE_DCHECK_EQ(retransmittable->offset + retransmittable->data_length, - frame.offset); + frame.offset) + << ENDPOINT; retransmittable->data_length = candidate->data_length; retransmittable->fin = candidate->fin; packet_size_ += frame.data_length; @@ -1771,7 +1855,7 @@ bool QuicPacketCreator::RemoveSoftMaxPacketLength() { if (!CanSetMaxPacketLength()) { return false; } - QUIC_DVLOG(1) << "Restoring max packet length to: " + QUIC_DVLOG(1) << ENDPOINT << "Restoring max packet length to: " << latched_hard_max_packet_length_; SetMaxPacketLength(latched_hard_max_packet_length_); // Reset latched_max_packet_length_. @@ -1814,7 +1898,7 @@ void QuicPacketCreator::MaybeAddPadding() { bool success = AddFrame(QuicFrame(QuicPaddingFrame(padding_bytes)), packet_.transmission_type); QUIC_BUG_IF(quic_bug_10752_36, !success) - << "Failed to add padding_bytes: " << padding_bytes + << ENDPOINT << "Failed to add padding_bytes: " << padding_bytes << " transmission_type: " << packet_.transmission_type; } @@ -1848,9 +1932,11 @@ bool QuicPacketCreator::StreamFrameIsClientHello( void QuicPacketCreator::SetServerConnectionIdIncluded( QuicConnectionIdIncluded server_connection_id_included) { QUICHE_DCHECK(server_connection_id_included == CONNECTION_ID_PRESENT || - server_connection_id_included == CONNECTION_ID_ABSENT); + server_connection_id_included == CONNECTION_ID_ABSENT) + << ENDPOINT; QUICHE_DCHECK(framer_->perspective() == Perspective::IS_SERVER || - server_connection_id_included != CONNECTION_ID_ABSENT); + server_connection_id_included != CONNECTION_ID_ABSENT) + << ENDPOINT; server_connection_id_included_ = server_connection_id_included; } @@ -1862,7 +1948,8 @@ void QuicPacketCreator::SetServerConnectionId( void QuicPacketCreator::SetClientConnectionId( QuicConnectionId client_connection_id) { QUICHE_DCHECK(client_connection_id.IsEmpty() || - framer_->version().SupportsClientConnectionIds()); + framer_->version().SupportsClientConnectionIds()) + << ENDPOINT; client_connection_id_ = client_connection_id; } @@ -1928,7 +2015,8 @@ QuicPacketLength QuicPacketCreator::GetGuaranteedLargestMessagePayload() const { const QuicPacketLength largest_payload = largest_frame - std::min(largest_frame, kQuicFrameTypeSize); // This must always be less than or equal to GetCurrentLargestMessagePayload. - QUICHE_DCHECK_LE(largest_payload, GetCurrentLargestMessagePayload()); + QUICHE_DCHECK_LE(largest_payload, GetCurrentLargestMessagePayload()) + << ENDPOINT; return largest_payload; } @@ -1940,7 +2028,7 @@ bool QuicPacketCreator::AttemptingToSendUnencryptedStreamData() { const std::string error_details = absl::StrCat("Cannot send stream data with level: ", EncryptionLevelToString(packet_.encryption_level)); - QUIC_BUG(quic_bug_10752_37) << error_details; + QUIC_BUG(quic_bug_10752_37) << ENDPOINT << error_details; delegate_->OnUnrecoverableError(QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA, error_details); return true; @@ -2005,19 +2093,56 @@ void QuicPacketCreator::SetDefaultPeerAddress(QuicSocketAddress address) { } } +#define ENDPOINT2 \ + (creator_->framer_->perspective() == Perspective::IS_SERVER ? "Server: " \ + : "Client: ") + +QuicPacketCreator::ScopedPeerAddressContext::ScopedPeerAddressContext( + QuicPacketCreator* creator, + QuicSocketAddress address, + bool update_connection_id) + : ScopedPeerAddressContext(creator, + address, + EmptyQuicConnectionId(), + EmptyQuicConnectionId(), + update_connection_id) {} + QuicPacketCreator::ScopedPeerAddressContext::ScopedPeerAddressContext( QuicPacketCreator* creator, - QuicSocketAddress address) - : creator_(creator), old_peer_address_(creator_->packet_.peer_address) { - QUIC_BUG_IF(quic_bug_12398_19, - !creator_->packet_.peer_address.IsInitialized()) - << "Context is used before seralized packet's peer address is " + QuicSocketAddress address, + const QuicConnectionId& client_connection_id, + const QuicConnectionId& server_connection_id, + bool update_connection_id) + : creator_(creator), + old_peer_address_(creator_->packet_.peer_address), + old_client_connection_id_(creator_->GetClientConnectionId()), + old_server_connection_id_(creator_->GetServerConnectionId()), + update_connection_id_(update_connection_id) { + QUIC_BUG_IF(quic_bug_12398_19, !old_peer_address_.IsInitialized()) + << ENDPOINT2 + << "Context is used before serialized packet's peer address is " "initialized."; creator_->SetDefaultPeerAddress(address); + if (update_connection_id_) { + // Flush current packet if connection ID length changes. + if (address == old_peer_address_ && + ((client_connection_id.length() != + old_client_connection_id_.length()) || + (server_connection_id.length() != + old_server_connection_id_.length()))) { + creator_->FlushCurrentPacket(); + } + creator_->SetClientConnectionId(client_connection_id); + creator_->SetServerConnectionId(server_connection_id); + } } QuicPacketCreator::ScopedPeerAddressContext::~ScopedPeerAddressContext() { creator_->SetDefaultPeerAddress(old_peer_address_); + if (update_connection_id_) { + creator_->SetClientConnectionId(old_client_connection_id_); + creator_->SetServerConnectionId(old_server_connection_id_); + } } QuicPacketCreator::ScopedSerializationFailureHandler:: @@ -2034,16 +2159,19 @@ QuicPacketCreator::ScopedSerializationFailureHandler:: if (creator_->packet_.encrypted_buffer == nullptr) { const std::string error_details = "Failed to SerializePacket."; - QUIC_BUG(quic_bug_10752_38) << error_details; + QUIC_BUG(quic_bug_10752_38) << ENDPOINT2 << error_details; creator_->delegate_->OnUnrecoverableError(QUIC_FAILED_TO_SERIALIZE_PACKET, error_details); } } +#undef ENDPOINT2 + void QuicPacketCreator::set_encryption_level(EncryptionLevel level) { QUICHE_DCHECK(level == packet_.encryption_level || !HasPendingFrames()) - << "Cannot update encryption level from " << packet_.encryption_level - << " to " << level << " when we already have pending frames: " + << ENDPOINT << "Cannot update encryption level from " + << packet_.encryption_level << " to " << level + << " when we already have pending frames: " << QuicFramesToString(queued_frames_); packet_.encryption_level = level; } @@ -2054,6 +2182,7 @@ void QuicPacketCreator::AddPathChallengeFrame( // AddFrame(). Sort out test helper functions and peer class that don't // enforce this check. QUIC_BUG_IF(quic_bug_10752_39, !flusher_attached_) + << ENDPOINT << "Packet flusher is not attached when " "generator tries to write stream data."; // Write a PATH_CHALLENGE frame, which has a random 8-byte payload. @@ -2094,13 +2223,13 @@ bool QuicPacketCreator::AddPaddedFrameWithRetry(const QuicFrame& frame) { } } // Frame was not queued but queued frames were flushed. - QUICHE_DCHECK(!HasPendingFrames()); + QUICHE_DCHECK(!HasPendingFrames()) << ENDPOINT; if (!delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)) { return false; } bool success = AddPaddedSavedFrame(frame, NOT_RETRANSMISSION); - QUIC_BUG_IF(quic_bug_12398_20, !success); + QUIC_BUG_IF(quic_bug_12398_20, !success) << ENDPOINT; return true; } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.h b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.h index 5e0c65e35fc..d4a58055282 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.h @@ -22,13 +22,15 @@ #include "absl/base/attributes.h" #include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "quic/core/frames/quic_stream_frame.h" -#include "quic/core/quic_circular_deque.h" #include "quic/core/quic_coalesced_packet.h" +#include "quic/core/quic_connection_id.h" #include "quic/core/quic_framer.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_types.h" #include "quic/platform/api/quic_export.h" +#include "common/quiche_circular_deque.h" namespace quic { namespace test { @@ -83,18 +85,28 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator { virtual void OnStreamFrameCoalesced(const QuicStreamFrame& /*frame*/) {} }; - // Set the peer address which the serialized packet will be sent to during the - // scope of this object. Upon exiting the scope, the original peer address is - // restored. + // Set the peer address and connection IDs with which the serialized packet + // will be sent to during the scope of this object. Upon exiting the scope, + // the original peer address and connection IDs are restored. class QUIC_EXPORT_PRIVATE ScopedPeerAddressContext { public: ScopedPeerAddressContext(QuicPacketCreator* creator, - QuicSocketAddress address); + QuicSocketAddress address, + bool update_connection_id); + + ScopedPeerAddressContext(QuicPacketCreator* creator, + QuicSocketAddress address, + const QuicConnectionId& client_connection_id, + const QuicConnectionId& server_connection_id, + bool update_connection_id); ~ScopedPeerAddressContext(); private: QuicPacketCreator* creator_; QuicSocketAddress old_peer_address_; + QuicConnectionId old_client_connection_id_; + QuicConnectionId old_server_connection_id_; + bool update_connection_id_; }; QuicPacketCreator(QuicConnectionId server_connection_id, @@ -247,7 +259,7 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator { // |payloads| is cleared. std::unique_ptr<SerializedPacket> SerializePathResponseConnectivityProbingPacket( - const QuicCircularDeque<QuicPathFrameBuffer>& payloads, + const quiche::QuicheCircularDeque<QuicPathFrameBuffer>& payloads, const bool is_padded); // Add PATH_RESPONSE to current packet, flush before or afterwards if needed. @@ -261,6 +273,16 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator { // Returns a dummy packet that is valid but contains no useful information. static SerializedPacket NoPacket(); + // Returns the server connection ID to send over the wire. + const QuicConnectionId& GetServerConnectionId() const { + return server_connection_id_; + } + + // Returns the client connection ID to send over the wire. + const QuicConnectionId& GetClientConnectionId() const { + return client_connection_id_; + } + // Returns the destination connection ID to send over the wire. QuicConnectionId GetDestinationConnectionId() const; @@ -287,6 +309,11 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator { void set_encryption_level(EncryptionLevel level); EncryptionLevel encryption_level() { return packet_.encryption_level; } + // Sets whether initial packets are protected with chaos. + void set_chaos_protection_enabled(bool chaos_protection_enabled) { + chaos_protection_enabled_ = chaos_protection_enabled; + } + // packet number of the last created packet, or 0 if no packets have been // created. QuicPacketNumber packet_number() const { return packet_.packet_number; } @@ -444,7 +471,7 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator { const QuicPacketHeader& header, char* buffer, size_t packet_length, - const QuicCircularDeque<QuicPathFrameBuffer>& payloads, + const quiche::QuicheCircularDeque<QuicPathFrameBuffer>& payloads, const bool is_padded, EncryptionLevel level); @@ -487,6 +514,13 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator { QuicPacketCreator* creator_; // Unowned. }; + // Attempts to build a data packet with chaos protection. If this packet isn't + // supposed to be protected or if serialization fails then absl::nullopt is + // returned. Otherwise returns the serialized length. + absl::optional<size_t> MaybeBuildDataPacketWithChaosProtection( + const QuicPacketHeader& header, + char* buffer); + // Creates a stream frame which fits into the current open packet. If // |data_size| is 0 and fin is true, the expected behavior is to consume // the fin. @@ -671,6 +705,9 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator { // accept. There is no limit for QUIC_CRYPTO connections, but QUIC+TLS // negotiates this during the handshake. QuicByteCount max_datagram_frame_size_; + + // Whether to attempt protecting initial packets with chaos. + bool chaos_protection_enabled_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator_test.cc index 5986bd2890f..57d1018d94c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator_test.cc @@ -19,6 +19,7 @@ #include "quic/core/crypto/quic_decrypter.h" #include "quic/core/crypto/quic_encrypter.h" #include "quic/core/frames/quic_stream_frame.h" +#include "quic/core/quic_connection_id.h" #include "quic/core/quic_data_writer.h" #include "quic/core/quic_simple_buffer_allocator.h" #include "quic/core/quic_types.h" @@ -34,13 +35,14 @@ #include "quic/test_tools/simple_quic_framer.h" #include "common/test_tools/quiche_test_utils.h" -using testing::_; -using testing::DoAll; -using testing::InSequence; -using testing::Invoke; -using testing::Return; -using testing::SaveArg; -using testing::StrictMock; +using ::testing::_; +using ::testing::AtLeast; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::SaveArg; +using ::testing::StrictMock; namespace quic { namespace test { @@ -269,6 +271,8 @@ class QuicPacketCreatorTest : public QuicTestWithParam<TestParams> { n * 2; } + void TestChaosProtection(bool enabled); + static constexpr QuicStreamOffset kOffset = 0u; char buffer_[kMaxOutgoingPacketSize]; @@ -751,7 +755,7 @@ TEST_P(QuicPacketCreatorTest, BuildPathResponsePacket1ResponseUnpadded) { }; // clang-format on std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); - QuicCircularDeque<QuicPathFrameBuffer> payloads; + quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; payloads.push_back(payload0); size_t length = creator_.BuildPathResponsePacket( header, buffer.get(), ABSL_ARRAYSIZE(packet), payloads, @@ -798,7 +802,7 @@ TEST_P(QuicPacketCreatorTest, BuildPathResponsePacket1ResponsePadded) { }; // clang-format on std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); - QuicCircularDeque<QuicPathFrameBuffer> payloads; + quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; payloads.push_back(payload0); size_t length = creator_.BuildPathResponsePacket( header, buffer.get(), ABSL_ARRAYSIZE(packet), payloads, @@ -848,7 +852,7 @@ TEST_P(QuicPacketCreatorTest, BuildPathResponsePacket3ResponsesUnpadded) { // clang-format on std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); - QuicCircularDeque<QuicPathFrameBuffer> payloads; + quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; payloads.push_back(payload0); payloads.push_back(payload1); payloads.push_back(payload2); @@ -902,7 +906,7 @@ TEST_P(QuicPacketCreatorTest, BuildPathResponsePacket3ResponsesPadded) { // clang-format on std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]); - QuicCircularDeque<QuicPathFrameBuffer> payloads; + quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; payloads.push_back(payload0); payloads.push_back(payload1); payloads.push_back(payload2); @@ -987,7 +991,7 @@ TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket1PayloadPadded) { creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); - QuicCircularDeque<QuicPathFrameBuffer> payloads; + quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; payloads.push_back(payload0); std::unique_ptr<SerializedPacket> encrypted( @@ -1017,7 +1021,7 @@ TEST_P(QuicPacketCreatorTest, creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); - QuicCircularDeque<QuicPathFrameBuffer> payloads; + quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; payloads.push_back(payload0); std::unique_ptr<SerializedPacket> encrypted( @@ -1047,7 +1051,7 @@ TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket2PayloadsPadded) { creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); - QuicCircularDeque<QuicPathFrameBuffer> payloads; + quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; payloads.push_back(payload0); payloads.push_back(payload1); @@ -1080,7 +1084,7 @@ TEST_P(QuicPacketCreatorTest, creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); - QuicCircularDeque<QuicPathFrameBuffer> payloads; + quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; payloads.push_back(payload0); payloads.push_back(payload1); @@ -1113,7 +1117,7 @@ TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket3PayloadsPadded) { creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); - QuicCircularDeque<QuicPathFrameBuffer> payloads; + quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; payloads.push_back(payload0); payloads.push_back(payload1); payloads.push_back(payload2); @@ -1149,7 +1153,7 @@ TEST_P(QuicPacketCreatorTest, creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); - QuicCircularDeque<QuicPathFrameBuffer> payloads; + quiche::QuicheCircularDeque<QuicPathFrameBuffer> payloads; payloads.push_back(payload0); payloads.push_back(payload1); payloads.push_back(payload2); @@ -1311,7 +1315,7 @@ TEST_P(QuicPacketCreatorTest, SerializeFrameShortData) { if (!GetParam().version_serialization) { creator_.StopSendingVersion(); } - std::string data("a"); + std::string data("Hello World!"); if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) { QuicStreamFrame stream_frame( QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), @@ -1338,15 +1342,51 @@ TEST_P(QuicPacketCreatorTest, SerializeFrameShortData) { } else { EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); } - if (client_framer_.version().HasHeaderProtection()) { - EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); - } EXPECT_CALL(framer_visitor_, OnPacketComplete()); } ProcessPacket(serialized); EXPECT_EQ(GetParam().version_serialization, header.version_flag); } +void QuicPacketCreatorTest::TestChaosProtection(bool enabled) { + if (!GetParam().version.UsesCryptoFrames()) { + return; + } + MockRandom mock_random(2); + QuicPacketCreatorPeer::SetRandom(&creator_, &mock_random); + creator_.set_chaos_protection_enabled(enabled); + std::string data("ChAoS_ThEoRy!"); + producer_.SaveCryptoData(ENCRYPTION_INITIAL, 0, data); + frames_.push_back( + QuicFrame(new QuicCryptoFrame(ENCRYPTION_INITIAL, 0, data.length()))); + frames_.push_back(QuicFrame(QuicPaddingFrame(33))); + SerializedPacket serialized = SerializeAllFrames(frames_); + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + if (enabled) { + EXPECT_CALL(framer_visitor_, OnCryptoFrame(_)).Times(AtLeast(2)); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)).Times(AtLeast(2)); + EXPECT_CALL(framer_visitor_, OnPingFrame(_)).Times(AtLeast(1)); + } else { + EXPECT_CALL(framer_visitor_, OnCryptoFrame(_)).Times(1); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)).Times(1); + EXPECT_CALL(framer_visitor_, OnPingFrame(_)).Times(0); + } + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + ProcessPacket(serialized); +} + +TEST_P(QuicPacketCreatorTest, ChaosProtectionEnabled) { + TestChaosProtection(/*enabled=*/true); +} + +TEST_P(QuicPacketCreatorTest, ChaosProtectionDisabled) { + TestChaosProtection(/*enabled=*/false); +} + TEST_P(QuicPacketCreatorTest, ConsumeDataLargerThanOneStreamFrame) { if (!GetParam().version_serialization) { creator_.StopSendingVersion(); @@ -1954,17 +1994,7 @@ TEST_P(QuicPacketCreatorTest, RetryToken) { creator_.SetRetryToken( std::string(retry_token_bytes, sizeof(retry_token_bytes))); - std::string data("a"); - if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) { - QuicStreamFrame stream_frame( - QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), - /*fin=*/false, 0u, absl::string_view()); - frames_.push_back(QuicFrame(stream_frame)); - } else { - producer_.SaveCryptoData(ENCRYPTION_INITIAL, 0, data); - frames_.push_back( - QuicFrame(new QuicCryptoFrame(ENCRYPTION_INITIAL, 0, data.length()))); - } + frames_.push_back(QuicFrame(QuicPingFrame())); SerializedPacket serialized = SerializeAllFrames(frames_); QuicPacketHeader header; @@ -1976,11 +2006,7 @@ TEST_P(QuicPacketCreatorTest, RetryToken) { EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)) .WillOnce(DoAll(SaveArg<0>(&header), Return(true))); - if (QuicVersionUsesCryptoFrames(client_framer_.transport_version())) { - EXPECT_CALL(framer_visitor_, OnCryptoFrame(_)); - } else { - EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); - } + EXPECT_CALL(framer_visitor_, OnPingFrame(_)); if (client_framer_.version().HasHeaderProtection()) { EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); } @@ -2261,6 +2287,32 @@ TEST_P(QuicPacketCreatorTest, SoftMaxPacketLength) { EXPECT_TRUE(creator_.HasPendingFrames()); } +TEST_P(QuicPacketCreatorTest, + ChangingEncryptionLevelRemovesSoftMaxPacketLength) { + if (!client_framer_.version().CanSendCoalescedPackets()) { + return; + } + // First set encryption level to forward secure which has the shortest header. + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); + const QuicByteCount previous_max_packet_length = creator_.max_packet_length(); + const size_t min_acceptable_packet_size = + GetPacketHeaderOverhead(client_framer_.transport_version()) + + QuicPacketCreator::MinPlaintextPacketSize(client_framer_.version()) + + GetEncryptionOverhead(); + // Then set the soft max packet length to the lowest allowed value. + creator_.SetSoftMaxPacketLength(min_acceptable_packet_size); + // Make sure that the low value was accepted. + EXPECT_EQ(creator_.max_packet_length(), min_acceptable_packet_size); + // Now set the encryption level to handshake which increases the header size. + creator_.set_encryption_level(ENCRYPTION_HANDSHAKE); + // Make sure that adding a frame removes the the soft max packet length. + QuicAckFrame ack_frame(InitAckFrame(1)); + frames_.push_back(QuicFrame(&ack_frame)); + SerializedPacket serialized = SerializeAllFrames(frames_); + EXPECT_EQ(serialized.encryption_level, ENCRYPTION_HANDSHAKE); + EXPECT_EQ(creator_.max_packet_length(), previous_max_packet_length); +} + class MockDelegate : public QuicPacketCreator::DelegateInterface { public: MockDelegate() {} @@ -3811,8 +3863,12 @@ TEST_F(QuicPacketCreatorMultiplePacketsTest, ExtraPaddingNeeded) { TEST_F(QuicPacketCreatorMultiplePacketsTest, PeerAddressContextWithSameAddress) { + QuicConnectionId client_connection_id = TestConnectionId(1); + QuicConnectionId server_connection_id = TestConnectionId(2); QuicSocketAddress peer_addr(QuicIpAddress::Any4(), 12345); creator_.SetDefaultPeerAddress(peer_addr); + creator_.SetClientConnectionId(client_connection_id); + creator_.SetServerConnectionId(server_connection_id); // Send some stream data. MakeIOVector("foo", &iov_); EXPECT_CALL(delegate_, ShouldGeneratePacket(_, _)) @@ -3824,8 +3880,12 @@ TEST_F(QuicPacketCreatorMultiplePacketsTest, EXPECT_EQ(3u, consumed.bytes_consumed); EXPECT_TRUE(creator_.HasPendingFrames()); { - // Set a different address via context which should trigger flush. - QuicPacketCreator::ScopedPeerAddressContext context(&creator_, peer_addr); + // Set the same address via context which should not trigger flush. + QuicPacketCreator::ScopedPeerAddressContext context( + &creator_, peer_addr, client_connection_id, server_connection_id, + /*update_connection_id=*/true); + ASSERT_EQ(client_connection_id, creator_.GetClientConnectionId()); + ASSERT_EQ(server_connection_id, creator_.GetServerConnectionId()); EXPECT_TRUE(creator_.HasPendingFrames()); // Queue another STREAM_FRAME. QuicConsumedData consumed = creator_.ConsumeData( @@ -3874,8 +3934,14 @@ TEST_F(QuicPacketCreatorMultiplePacketsTest, })); EXPECT_TRUE(creator_.HasPendingFrames()); { + QuicConnectionId client_connection_id = TestConnectionId(1); + QuicConnectionId server_connection_id = TestConnectionId(2); // Set a different address via context which should trigger flush. - QuicPacketCreator::ScopedPeerAddressContext context(&creator_, peer_addr1); + QuicPacketCreator::ScopedPeerAddressContext context( + &creator_, peer_addr1, client_connection_id, server_connection_id, + /*update_connection_id=*/true); + ASSERT_EQ(client_connection_id, creator_.GetClientConnectionId()); + ASSERT_EQ(server_connection_id, creator_.GetServerConnectionId()); EXPECT_FALSE(creator_.HasPendingFrames()); // Queue another STREAM_FRAME. QuicConsumedData consumed = creator_.ConsumeData( @@ -3891,9 +3957,15 @@ TEST_F(QuicPacketCreatorMultiplePacketsTest, TEST_F(QuicPacketCreatorMultiplePacketsTest, NestedPeerAddressContextWithDifferentAddress) { + QuicConnectionId client_connection_id1 = creator_.GetClientConnectionId(); + QuicConnectionId server_connection_id1 = creator_.GetServerConnectionId(); QuicSocketAddress peer_addr(QuicIpAddress::Any4(), 12345); creator_.SetDefaultPeerAddress(peer_addr); - QuicPacketCreator::ScopedPeerAddressContext context(&creator_, peer_addr); + QuicPacketCreator::ScopedPeerAddressContext context( + &creator_, peer_addr, client_connection_id1, server_connection_id1, + /*update_connection_id=*/true); + ASSERT_EQ(client_connection_id1, creator_.GetClientConnectionId()); + ASSERT_EQ(server_connection_id1, creator_.GetServerConnectionId()); // Send some stream data. MakeIOVector("foo", &iov_); @@ -3913,9 +3985,14 @@ TEST_F(QuicPacketCreatorMultiplePacketsTest, ASSERT_EQ(1u, packet.retransmittable_frames.size()); EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type); + QuicConnectionId client_connection_id2 = TestConnectionId(3); + QuicConnectionId server_connection_id2 = TestConnectionId(4); // Set up another context with a different address. - QuicPacketCreator::ScopedPeerAddressContext context(&creator_, - peer_addr1); + QuicPacketCreator::ScopedPeerAddressContext context( + &creator_, peer_addr1, client_connection_id2, server_connection_id2, + /*update_connection_id=*/true); + ASSERT_EQ(client_connection_id2, creator_.GetClientConnectionId()); + ASSERT_EQ(server_connection_id2, creator_.GetServerConnectionId()); MakeIOVector("foo", &iov_); EXPECT_CALL(delegate_, ShouldGeneratePacket(_, _)) .WillRepeatedly(Return(true)); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packets.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packets.cc index 5cc593f41bf..d625b5dcb6a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packets.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packets.cc @@ -15,7 +15,6 @@ #include "quic/core/quic_versions.h" #include "quic/platform/api/quic_flag_utils.h" #include "quic/platform/api/quic_flags.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_protocol_flags_list.h b/chromium/net/third_party/quiche/src/quic/core/quic_protocol_flags_list.h index 5187c61def4..56f83594b8f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_protocol_flags_list.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_protocol_flags_list.h @@ -249,4 +249,11 @@ QUIC_PROTOCOL_FLAG( true, "If true, QUIC QPACK decoder includes 32-bytes overheader per entry while " "comparing request/response header size against its upper limit.") + +QUIC_PROTOCOL_FLAG( + bool, + quic_reject_retry_token_in_initial_packet, + false, + "If true, always reject retry_token received in INITIAL packets") + #endif diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.cc index a1b78a4fb86..079778cd5d8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.cc @@ -205,11 +205,7 @@ QuicTime::Delta QuicReceivedPacketManager::GetMaxAckDelay( // before sending an ack. QuicTime::Delta ack_delay = std::min( local_max_ack_delay_, rtt_stats.min_rtt() * ack_decimation_delay_); - if (GetQuicReloadableFlag(quic_ack_delay_alarm_granularity)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_ack_delay_alarm_granularity); - ack_delay = std::max(ack_delay, kAlarmGranularity); - } - return ack_delay; + return std::max(ack_delay, kAlarmGranularity); } void QuicReceivedPacketManager::MaybeUpdateAckFrequency( diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager_test.cc index 3cfa0ce0a43..da586c67a3d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager_test.cc @@ -397,9 +397,6 @@ TEST_F(QuicReceivedPacketManagerTest, SendDelayedAckDecimation) { } TEST_F(QuicReceivedPacketManagerTest, SendDelayedAckDecimationMin1ms) { - if (!GetQuicReloadableFlag(quic_ack_delay_alarm_granularity)) { - return; - } EXPECT_FALSE(HasPendingAck()); // Seed the min_rtt with a kAlarmGranularity signal. rtt_stats_.UpdateRtt(kAlarmGranularity, QuicTime::Delta::Zero(), diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h index 13e2396fd86..781f6f95b90 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h @@ -19,7 +19,6 @@ #include "quic/core/congestion_control/send_algorithm_interface.h" #include "quic/core/congestion_control/uber_loss_algorithm.h" #include "quic/core/proto/cached_network_parameters_proto.h" -#include "quic/core/quic_circular_deque.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_sustained_bandwidth_recorder.h" #include "quic/core/quic_time.h" @@ -28,6 +27,7 @@ #include "quic/core/quic_unacked_packet_map.h" #include "quic/platform/api/quic_containers.h" #include "quic/platform/api/quic_export.h" +#include "common/quiche_circular_deque.h" namespace quic { @@ -673,7 +673,8 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { // The history of outstanding max_ack_delays sent to peer. Outstanding means // a max_ack_delay is sent as part of the last acked AckFrequencyFrame or // an unacked AckFrequencyFrame after that. - QuicCircularDeque<std::pair<QuicTime::Delta, /*sequence_number=*/uint64_t>> + quiche::QuicheCircularDeque< + std::pair<QuicTime::Delta, /*sequence_number=*/uint64_t>> in_use_sent_ack_delays_; // Latest received ack frame. diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_session.cc b/chromium/net/third_party/quiche/src/quic/core/quic_session.cc index 7c7aa4d077f..1bcb9dcb7bd 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_session.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_session.cc @@ -25,7 +25,7 @@ #include "quic/platform/api/quic_map_util.h" #include "quic/platform/api/quic_server_stats.h" #include "quic/platform/api/quic_stack_trace.h" -#include "common/platform/api/quiche_text_utils.h" +#include "common/quiche_text_utils.h" using spdy::SpdyPriority; @@ -686,11 +686,8 @@ bool QuicSession::WillingAndAbleToWrite() const { if (HasPendingHandshake()) { return true; } - if (GetQuicReloadableFlag(quic_fix_willing_and_able_to_write2)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_fix_willing_and_able_to_write2); - if (!IsEncryptionEstablished()) { - return false; - } + if (!IsEncryptionEstablished()) { + return false; } } if (control_frame_manager_.WillingToWrite() || @@ -773,10 +770,24 @@ QuicConsumedData QuicSession::WritevData( perspective() == Perspective::IS_CLIENT); QUIC_BUG_IF(quic_bug_12435_3, type == NOT_RETRANSMISSION) << ENDPOINT << "Try to send new data on stream " << id - << "before 1-RTT keys are available while 0-RTT is rejected."; + << "before 1-RTT keys are available while 0-RTT is rejected. " + "Version: " + << ParsedQuicVersionToString(version()); + } else if (version().UsesTls() || perspective() == Perspective::IS_SERVER) { + QUIC_BUG(quic_bug_10866_2) + << ENDPOINT << "Try to send data of stream " << id + << " before encryption is established. Version: " + << ParsedQuicVersionToString(version()); } else { - QUIC_BUG(quic_bug_10866_2) << ENDPOINT << "Try to send data of stream " - << id << " before encryption is established."; + // In QUIC crypto, this could happen when the client sends full CHLO and + // 0-RTT request, then receives an inchoate REJ and sends an inchoate + // CHLO. The client then gets the ACK of the inchoate CHLO or the client + // gets the full REJ and needs to verify the proof (before it sends the + // full CHLO), such that there is no outstanding crypto data. + // Retransmission alarm fires in TLP mode which tries to retransmit the + // 0-RTT request (without encryption). + QUIC_DLOG(INFO) << ENDPOINT << "Try to send data of stream " << id + << " before encryption is established."; } return QuicConsumedData(0, false); } @@ -920,15 +931,12 @@ void QuicSession::SendGoAway(QuicErrorCode error_code, const std::string& reason) { // GOAWAY frame is not supported in IETF QUIC. QUICHE_DCHECK(!VersionHasIetfQuicFrames(transport_version())); - if (GetQuicReloadableFlag(quic_encrypted_goaway)) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_encrypted_goaway, 1, 2); - if (!IsEncryptionEstablished()) { - QUIC_CODE_COUNT(quic_goaway_before_encryption_established); - connection_->CloseConnection( - error_code, reason, - ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); - return; - } + if (!IsEncryptionEstablished()) { + QUIC_CODE_COUNT(quic_goaway_before_encryption_established); + connection_->CloseConnection( + error_code, reason, + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; } if (transport_goaway_sent_) { return; @@ -1666,10 +1674,7 @@ void QuicSession::OnTlsHandshakeComplete() { // Server sends HANDSHAKE_DONE to signal confirmation of the handshake // to the client. control_frame_manager_.WriteOrBufferHandshakeDone(); - if (GetQuicReloadableFlag(quic_enable_token_based_address_validation) && - connection()->version().HasIetfQuicFrames()) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_token_based_address_validation, - 1, 2); + if (connection()->version().HasIetfQuicFrames()) { MaybeSendAddressToken(); } } @@ -1775,6 +1780,11 @@ void QuicSession::OnHandshakeCallbackDone() { } } +bool QuicSession::PacketFlusherAttached() const { + QUICHE_DCHECK(connection_->connected()); + return connection()->packet_creator().PacketFlusherAttached(); +} + void QuicSession::OnCryptoHandshakeMessageSent( const CryptoHandshakeMessage& /*message*/) {} @@ -2096,12 +2106,14 @@ void QuicSession::SendAckFrequency(const QuicAckFrequencyFrame& frame) { } void QuicSession::SendNewConnectionId(const QuicNewConnectionIdFrame& frame) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 1, 5); control_frame_manager_.WriteOrBufferNewConnectionId( frame.connection_id, frame.sequence_number, frame.retire_prior_to, frame.stateless_reset_token); } void QuicSession::SendRetireConnectionId(uint64_t sequence_number) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 2, 5); control_frame_manager_.WriteOrBufferRetireConnectionId(sequence_number); } @@ -2495,11 +2507,6 @@ QuicPacketLength QuicSession::GetGuaranteedLargestMessagePayload() const { return connection_->GetGuaranteedLargestMessagePayload(); } -void QuicSession::SendStopSending(QuicRstStreamErrorCode code, - QuicStreamId stream_id) { - control_frame_manager_.WriteOrBufferStopSending(code, stream_id); -} - QuicStreamId QuicSession::next_outgoing_bidirectional_stream_id() const { if (VersionHasIetfQuicFrames(transport_version())) { return ietf_streamid_manager_.next_outgoing_bidirectional_stream_id(); @@ -2609,15 +2616,19 @@ bool QuicSession::HasPendingPathValidation() const { return connection_->HasPendingPathValidation(); } -void QuicSession::MigratePath(const QuicSocketAddress& self_address, +bool QuicSession::MigratePath(const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, QuicPacketWriter* writer, bool owns_writer) { - connection_->MigratePath(self_address, peer_address, writer, owns_writer); + return connection_->MigratePath(self_address, peer_address, writer, + owns_writer); } bool QuicSession::ValidateToken(absl::string_view token) const { QUICHE_DCHECK_EQ(perspective_, Perspective::IS_SERVER); + if (GetQuicFlag(FLAGS_quic_reject_retry_token_in_initial_packet)) { + return false; + } if (token.empty() || token[0] != 0) { // Validate the prefix for token received in NEW_TOKEN frame. return false; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_session.h b/chromium/net/third_party/quiche/src/quic/core/quic_session.h index cf366cf5654..b6c1367019f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_session.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_session.h @@ -264,10 +264,6 @@ class QUIC_EXPORT_PRIVATE QuicSession // Sends a WINDOW_UPDATE frame. virtual void SendWindowUpdate(QuicStreamId id, QuicStreamOffset byte_offset); - // Create and transmit a STOP_SENDING frame - virtual void SendStopSending(QuicRstStreamErrorCode code, - QuicStreamId stream_id); - // Called by stream |stream_id| when it gets closed. virtual void OnStreamClosed(QuicStreamId stream_id); @@ -306,6 +302,7 @@ class QUIC_EXPORT_PRIVATE QuicSession bool is_resumption, std::string* error_details) override; void OnHandshakeCallbackDone() override; + bool PacketFlusherAttached() const override; // Implement StreamDelegateInterface. void OnStreamError(QuicErrorCode error_code, @@ -458,7 +455,7 @@ class QUIC_EXPORT_PRIVATE QuicSession bool HasPendingPathValidation() const; // Switch to the path described in |context| without validating the path. - void MigratePath(const QuicSocketAddress& self_address, + bool MigratePath(const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, QuicPacketWriter* writer, bool owns_writer); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h index cb6e3aa943c..4b23d3d82cd 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h @@ -6,13 +6,13 @@ #define QUICHE_QUIC_CORE_QUIC_STREAM_SEND_BUFFER_H_ #include "quic/core/frames/quic_stream_frame.h" -#include "quic/core/quic_circular_deque.h" #include "quic/core/quic_interval_deque.h" #include "quic/core/quic_interval_set.h" #include "quic/core/quic_types.h" #include "quic/platform/api/quic_iovec.h" #include "quic/platform/api/quic_mem_slice.h" #include "quic/platform/api/quic_mem_slice_span.h" +#include "common/quiche_circular_deque.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.cc index cf7340bdb65..88a51bd5e08 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.cc @@ -56,13 +56,10 @@ void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { (!CloseStreamAtOffset(frame.offset + data_len) || data_len == 0)) { return; } - if (GetQuicReloadableFlag(quic_accept_empty_stream_frame_with_no_fin)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_accept_empty_stream_frame_with_no_fin); - if (stream_->version().HasIetfQuicFrames() && data_len == 0) { - QUICHE_DCHECK(!frame.fin); - // Ignore empty frame with no fin. - return; - } + if (stream_->version().HasIetfQuicFrames() && data_len == 0) { + QUICHE_DCHECK(!frame.fin); + // Ignore empty frame with no fin. + return; } OnFrameData(byte_offset, data_len, frame.data_buffer); } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_test.cc index e8fe6f367e2..b097bc5bc93 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_test.cc @@ -252,8 +252,7 @@ TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameAndFinConsumed) { } TEST_F(QuicStreamSequencerTest, EmptyFrame) { - if (!GetQuicReloadableFlag(quic_accept_empty_stream_frame_with_no_fin) || - !stream_.version().HasIetfQuicFrames()) { + if (!stream_.version().HasIetfQuicFrames()) { EXPECT_CALL(stream_, OnUnrecoverableError(QUIC_EMPTY_STREAM_FRAME_NO_FIN, _)); } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_test.cc index 739113a9147..2d3298b6fc9 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_test.cc @@ -1634,8 +1634,7 @@ TEST_P(QuicStreamTest, RstStreamFrameChangesCloseOffset) { TEST_P(QuicStreamTest, EmptyStreamFrameWithNoFin) { Initialize(); QuicStreamFrame empty_stream_frame(stream_->id(), false, 0, ""); - if (GetQuicReloadableFlag(quic_accept_empty_stream_frame_with_no_fin) && - stream_->version().HasIetfQuicFrames()) { + if (stream_->version().HasIetfQuicFrames()) { EXPECT_CALL(*connection_, CloseConnection(QUIC_EMPTY_STREAM_FRAME_NO_FIN, _, _)) .Times(0); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_tag.cc b/chromium/net/third_party/quiche/src/quic/core/quic_tag.cc index a6305b086f3..32b7bf9cd76 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_tag.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_tag.cc @@ -12,7 +12,7 @@ #include "absl/strings/str_split.h" #include "quic/platform/api/quic_flag_utils.h" #include "quic/platform/api/quic_flags.h" -#include "common/platform/api/quiche_text_utils.h" +#include "common/quiche_text_utils.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc index d6fa2da7a10..dbaa38bdefe 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc @@ -18,12 +18,13 @@ #include "quic/core/quic_framer.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_utils.h" +#include "quic/platform/api/quic_bug_tracker.h" #include "quic/platform/api/quic_flag_utils.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_map_util.h" #include "quic/platform/api/quic_socket_address.h" -#include "common/platform/api/quiche_text_utils.h" +#include "common/quiche_text_utils.h" namespace quic { @@ -324,6 +325,13 @@ void QuicTimeWaitListManager::SendPublicReset( if (ietf_quic) { std::unique_ptr<QuicEncryptedPacket> ietf_reset_packet = BuildIetfStatelessResetPacket(connection_id, received_packet_length); + if (GetQuicRestartFlag(quic_fix_stateless_reset2) && + ietf_reset_packet == nullptr) { + // This could happen when trying to reject a short header packet of + // a connection which is in the time wait list (and with no termination + // packet). + return; + } QUIC_DVLOG(2) << "Dispatcher sending IETF reset packet for " << connection_id << std::endl << quiche::QuicheTextUtils::HexDump( @@ -381,6 +389,10 @@ QuicTimeWaitListManager::BuildIetfStatelessResetPacket( bool QuicTimeWaitListManager::SendOrQueuePacket( std::unique_ptr<QueuedPacket> packet, const QuicPerPacketContext* /*packet_context*/) { + if (packet == nullptr) { + QUIC_LOG(ERROR) << "Tried to send or queue a null packet"; + return true; + } if (WriteToWire(packet.get())) { // Allow the packet to be deleted upon leaving this function. return true; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h index 2018f723306..ac371f2c180 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h @@ -224,7 +224,7 @@ class QUIC_NO_EXPORT QuicTimeWaitListManager virtual bool SendOrQueuePacket(std::unique_ptr<QueuedPacket> packet, const QuicPerPacketContext* packet_context); - const QuicCircularDeque<std::unique_ptr<QueuedPacket>>& + const quiche::QuicheCircularDeque<std::unique_ptr<QueuedPacket>>& pending_packets_queue() const { return pending_packets_queue_; } @@ -318,7 +318,8 @@ class QUIC_NO_EXPORT QuicTimeWaitListManager // Pending termination packets that need to be sent out to the peer when we // are given a chance to write by the dispatcher. - QuicCircularDeque<std::unique_ptr<QueuedPacket>> pending_packets_queue_; + quiche::QuicheCircularDeque<std::unique_ptr<QueuedPacket>> + pending_packets_queue_; // Time period for which connection_ids should remain in time wait state. const QuicTime::Delta time_wait_period_; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc index d33e992e372..4e9f957182b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc @@ -19,6 +19,7 @@ #include "quic/core/quic_packet_writer.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_utils.h" +#include "quic/platform/api/quic_expect_bug.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/mock_quic_session_visitor.h" @@ -749,6 +750,24 @@ TEST_F(QuicTimeWaitListManagerTest, } } +// Regression test for b/184053898. +TEST_F(QuicTimeWaitListManagerTest, DonotCrashOnNullStatelessReset) { + // Received a packet with length < + // QuicFramer::GetMinStatelessResetPacketLength(), and this will result in a + // null stateless reset. + time_wait_list_manager_.SendPublicReset( + self_address_, peer_address_, TestConnectionId(1), + /*ietf_quic=*/true, + /*received_packet_length=*/ + QuicFramer::GetMinStatelessResetPacketLength() - 1, + /*packet_context=*/nullptr); +} + +TEST_F(QuicTimeWaitListManagerTest, SendOrQueueNullPacket) { + QuicTimeWaitListManagerPeer::SendOrQueuePacket(&time_wait_list_manager_, + nullptr, nullptr); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h b/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h index 75c835d6c49..676f054b1fb 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h @@ -7,15 +7,14 @@ #include <cstddef> #include <cstdint> -#include <deque> #include "absl/strings/str_cat.h" -#include "quic/core/quic_circular_deque.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_transmission_info.h" #include "quic/core/session_notifier_interface.h" #include "quic/platform/api/quic_export.h" #include "quic/platform/api/quic_flags.h" +#include "common/quiche_circular_deque.h" namespace quic { @@ -113,10 +112,10 @@ class QUIC_EXPORT_PRIVATE QuicUnackedPacketMap { QuicPacketNumber GetLeastUnacked() const; using const_iterator = - QuicCircularDeque<QuicTransmissionInfo>::const_iterator; + quiche::QuicheCircularDeque<QuicTransmissionInfo>::const_iterator; using const_reverse_iterator = - QuicCircularDeque<QuicTransmissionInfo>::const_reverse_iterator; - using iterator = QuicCircularDeque<QuicTransmissionInfo>::iterator; + quiche::QuicheCircularDeque<QuicTransmissionInfo>::const_reverse_iterator; + using iterator = quiche::QuicheCircularDeque<QuicTransmissionInfo>::iterator; const_iterator begin() const { return unacked_packets_.begin(); } const_iterator end() const { return unacked_packets_.end(); } @@ -300,7 +299,7 @@ class QUIC_EXPORT_PRIVATE QuicUnackedPacketMap { // If the old packet is acked before the new packet, then the old entry will // be removed from the map and the new entry's retransmittable frames will be // set to nullptr. - QuicCircularDeque<QuicTransmissionInfo> unacked_packets_; + quiche::QuicheCircularDeque<QuicTransmissionInfo> unacked_packets_; // The packet at the 0th index of unacked_packets_. QuicPacketNumber least_unacked_; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc b/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc index 27b74bb9430..1f0137a91bc 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc @@ -21,8 +21,8 @@ #include "quic/platform/api/quic_bug_tracker.h" #include "quic/platform/api/quic_flag_utils.h" #include "quic/platform/api/quic_flags.h" -#include "quic/platform/api/quic_prefetch.h" #include "common/platform/api/quiche_logging.h" +#include "common/platform/api/quiche_prefetch.h" #include "common/quiche_endian.h" namespace quic { @@ -266,9 +266,9 @@ void QuicUtils::CopyToBuffer(const struct iovec* iov, char* next_base = static_cast<char*>(iov[iovnum + 1].iov_base); // Prefetch 2 cachelines worth of data to get the prefetcher started; leave // it to the hardware prefetcher after that. - QuicPrefetchT0(next_base); + quiche::QuichePrefetchT0(next_base); if (iov[iovnum + 1].iov_len >= 64) { - QuicPrefetchT0(next_base + ABSL_CACHELINE_SIZE); + quiche::QuichePrefetchT0(next_base + ABSL_CACHELINE_SIZE); } } @@ -308,7 +308,6 @@ bool QuicUtils::IsRetransmittableFrame(QuicFrameType type) { case MTU_DISCOVERY_FRAME: case PATH_CHALLENGE_FRAME: case PATH_RESPONSE_FRAME: - case NEW_CONNECTION_ID_FRAME: return false; default: return true; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc b/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc index ab68ddd57d1..ba2ec7ee29e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc @@ -17,8 +17,8 @@ #include "quic/platform/api/quic_flag_utils.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/quiche_endian.h" +#include "common/quiche_text_utils.h" namespace quic { namespace { @@ -599,7 +599,6 @@ std::string AlpnForVersion(ParsedQuicVersion parsed_version) { void QuicVersionInitializeSupportForIetfDraft() { // Enable necessary flags. - SetQuicReloadableFlag(quic_fix_key_update_on_first_packet, true); } void QuicEnableVersion(const ParsedQuicVersion& version) { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.h b/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.h index 3379fcb39df..dbc2ca9173d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.h @@ -9,13 +9,13 @@ #include <cstdint> #include <utility> +#include "http2/core/priority_write_scheduler.h" #include "quic/core/quic_packets.h" #include "quic/platform/api/quic_bug_tracker.h" #include "quic/platform/api/quic_containers.h" #include "quic/platform/api/quic_export.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_map_util.h" -#include "spdy/core/priority_write_scheduler.h" namespace quic { @@ -78,7 +78,7 @@ class QUIC_EXPORT_PRIVATE QuicWriteBlockedList { bool IsStreamBlocked(QuicStreamId stream_id) const; private: - spdy::PriorityWriteScheduler<QuicStreamId> priority_write_scheduler_; + http2::PriorityWriteScheduler<QuicStreamId> priority_write_scheduler_; // If performing batch writes, this will be the stream ID of the stream doing // batch writes for this priority level. We will allow this stream to write diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_chlo_extractor.cc b/chromium/net/third_party/quiche/src/quic/core/tls_chlo_extractor.cc index ae85b07c251..ac1fb180f54 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_chlo_extractor.cc +++ b/chromium/net/third_party/quiche/src/quic/core/tls_chlo_extractor.cc @@ -17,7 +17,6 @@ #include "quic/core/quic_types.h" #include "quic/core/quic_versions.h" #include "quic/platform/api/quic_bug_tracker.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.cc index f8e321663ca..1b9de030860 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.cc +++ b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.cc @@ -17,7 +17,7 @@ #include "quic/core/quic_types.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_hostname_utils.h" -#include "common/platform/api/quiche_text_utils.h" +#include "common/quiche_text_utils.h" namespace quic { @@ -42,12 +42,10 @@ TlsClientHandshaker::TlsClientHandshaker( has_application_state_(has_application_state), crypto_config_(crypto_config), tls_connection_(crypto_config->ssl_ctx(), this) { - if (GetQuicReloadableFlag(quic_enable_token_based_address_validation)) { - std::string token = - crypto_config->LookupOrCreate(server_id)->source_address_token(); - if (!token.empty()) { - session->SetSourceAddressTokenToSend(token); - } + std::string token = + crypto_config->LookupOrCreate(server_id)->source_address_token(); + if (!token.empty()) { + session->SetSourceAddressTokenToSend(token); } } @@ -179,18 +177,16 @@ bool TlsClientHandshaker::SetAlpn() { } // Enable ALPS only for versions that use HTTP/3 frames. - if (enable_alps_) { - for (const std::string& alpn_string : alpns) { - ParsedQuicVersion version = ParseQuicVersionString(alpn_string); - if (!version.IsKnown() || !version.UsesHttp3()) { - continue; - } - if (SSL_add_application_settings( - ssl(), reinterpret_cast<const uint8_t*>(alpn_string.data()), - alpn_string.size(), nullptr, /* settings_len = */ 0) != 1) { - QUIC_BUG(quic_bug_10576_7) << "Failed to enable ALPS."; - return false; - } + for (const std::string& alpn_string : alpns) { + ParsedQuicVersion version = ParseQuicVersionString(alpn_string); + if (!version.IsKnown() || !version.UsesHttp3()) { + continue; + } + if (SSL_add_application_settings( + ssl(), reinterpret_cast<const uint8_t*>(alpn_string.data()), + alpn_string.size(), nullptr, /* settings_len = */ 0) != 1) { + QUIC_BUG(quic_bug_10576_7) << "Failed to enable ALPS."; + return false; } } @@ -502,20 +498,18 @@ void TlsClientHandshaker::FinishHandshake() { << "'"; // Parse ALPS extension. - if (enable_alps_) { - const uint8_t* alps_data; - size_t alps_length; - SSL_get0_peer_application_settings(ssl(), &alps_data, &alps_length); - if (alps_length > 0) { - auto error = session()->OnAlpsData(alps_data, alps_length); - if (error) { - // Calling CloseConnection() is safe even in case OnAlpsData() has - // already closed the connection. - CloseConnection( - QUIC_HANDSHAKE_FAILED, - absl::StrCat("Error processing ALPS data: ", error.value())); - return; - } + const uint8_t* alps_data; + size_t alps_length; + SSL_get0_peer_application_settings(ssl(), &alps_data, &alps_length); + if (alps_length > 0) { + auto error = session()->OnAlpsData(alps_data, alps_length); + if (error) { + // Calling CloseConnection() is safe even in case OnAlpsData() has + // already closed the connection. + CloseConnection( + QUIC_HANDSHAKE_FAILED, + absl::StrCat("Error processing ALPS data: ", error.value())); + return; } } diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.h index 8fb6da69d2c..eb39ccf9fab 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.h +++ b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.h @@ -168,9 +168,6 @@ class QUIC_EXPORT_PRIVATE TlsClientHandshaker std::unique_ptr<TransportParameters> received_transport_params_ = nullptr; std::unique_ptr<ApplicationState> received_application_state_ = nullptr; - - // Latched value of reloadable flag quic_enable_alps_client. - const bool enable_alps_ = GetQuicReloadableFlag(quic_enable_alps_client); }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker_test.cc b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker_test.cc index e498acb9a43..8365f633f0f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker_test.cc @@ -279,11 +279,7 @@ TEST_P(TlsClientHandshakerTest, ConnectedAfterHandshake) { TEST_P(TlsClientHandshakerTest, ConnectionClosedOnTlsError) { // Have client send ClientHello. stream()->CryptoConnect(); - if (GetQuicReloadableFlag(quic_send_tls_crypto_error_code)) { - EXPECT_CALL(*connection_, CloseConnection(QUIC_HANDSHAKE_FAILED, _, _, _)); - } else { - EXPECT_CALL(*connection_, CloseConnection(QUIC_HANDSHAKE_FAILED, _, _)); - } + EXPECT_CALL(*connection_, CloseConnection(QUIC_HANDSHAKE_FAILED, _, _, _)); // Send a zero-length ServerHello from server to client. char bogus_handshake_message[] = { @@ -621,23 +617,14 @@ TEST_P(TlsClientHandshakerTest, ServerRequiresCustomALPN) { .WillOnce([kTestAlpn](const std::vector<absl::string_view>& alpns) { return std::find(alpns.cbegin(), alpns.cend(), kTestAlpn); }); - if (GetQuicReloadableFlag(quic_send_tls_crypto_error_code)) { - EXPECT_CALL( - *server_connection_, - CloseConnection( - QUIC_HANDSHAKE_FAILED, - static_cast<QuicIetfTransportErrorCodes>(CRYPTO_ERROR_FIRST + 120), - "TLS handshake failure (ENCRYPTION_INITIAL) 120: " - "no application protocol", - _)); - } else { - EXPECT_CALL( - *server_connection_, - CloseConnection(QUIC_HANDSHAKE_FAILED, - "TLS handshake failure (ENCRYPTION_INITIAL) 120: " - "no application protocol", - _)); - } + + EXPECT_CALL(*server_connection_, + CloseConnection(QUIC_HANDSHAKE_FAILED, + static_cast<QuicIetfTransportErrorCodes>( + CRYPTO_ERROR_FIRST + 120), + "TLS handshake failure (ENCRYPTION_INITIAL) 120: " + "no application protocol", + _)); stream()->CryptoConnect(); crypto_test_utils::AdvanceHandshake(connection_, stream(), 0, diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc index bc8e0b48d4d..9ba5f3c12f6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc +++ b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc @@ -96,6 +96,13 @@ void TlsHandshaker::AdvanceHandshake() { return; } + QUICHE_BUG_IF(quic_tls_server_async_done_no_flusher, + SSL_is_server(ssl()) && add_packet_flusher_on_async_op_done_ && + !handshaker_delegate_->PacketFlusherAttached()) + << "is_server:" << SSL_is_server(ssl()) + << ", add_packet_flusher_on_async_op_done_:" + << add_packet_flusher_on_async_op_done_; + QUIC_VLOG(1) << ENDPOINT << "Continuing handshake"; int rv = SSL_do_handshake(ssl()); @@ -105,6 +112,7 @@ void TlsHandshaker::AdvanceHandshake() { // that case. If there are no unprocessed ServerHello, the retry will return a // non-positive number. if (retry_handshake_on_early_data_ && rv == 1 && SSL_in_early_data(ssl())) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_tls_retry_handshake_on_early_data, 1, 2); OnEnterEarlyData(); rv = SSL_do_handshake(ssl()); QUIC_VLOG(1) << ENDPOINT @@ -335,15 +343,10 @@ void TlsHandshaker::SendAlert(EncryptionLevel level, uint8_t desc) { "TLS handshake failure (", EncryptionLevelToString(level), ") ", static_cast<int>(desc), ": ", SSL_alert_desc_string_long(desc)); QUIC_DLOG(ERROR) << error_details; - if (GetQuicReloadableFlag(quic_send_tls_crypto_error_code)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_send_tls_crypto_error_code); - CloseConnection( - TlsAlertToQuicErrorCode(desc), - static_cast<QuicIetfTransportErrorCodes>(CRYPTO_ERROR_FIRST + desc), - error_details); - } else { - CloseConnection(QUIC_HANDSHAKE_FAILED, error_details); - } + CloseConnection( + TlsAlertToQuicErrorCode(desc), + static_cast<QuicIetfTransportErrorCodes>(CRYPTO_ERROR_FIRST + desc), + error_details); } } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h index 6bf48b548c4..0b5ddc36769 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h +++ b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h @@ -16,6 +16,7 @@ #include "quic/core/crypto/tls_connection.h" #include "quic/core/quic_session.h" #include "quic/platform/api/quic_export.h" +#include "quic/platform/api/quic_flags.h" namespace quic { @@ -167,6 +168,9 @@ class QUIC_EXPORT_PRIVATE TlsHandshaker : public TlsConnection::Delegate, // error code corresponding to the TLS alert description |desc|. void SendAlert(EncryptionLevel level, uint8_t desc) override; + const bool add_packet_flusher_on_async_op_done_ = + GetQuicReloadableFlag(quic_add_packet_flusher_on_async_op_done); + const bool retry_handshake_on_early_data_ = GetQuicReloadableFlag(quic_tls_retry_handshake_on_early_data); diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.cc index b628db1744a..efd4d275013 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.cc +++ b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.cc @@ -8,6 +8,7 @@ #include <string> #include "absl/base/macros.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "third_party/boringssl/src/include/openssl/pool.h" #include "third_party/boringssl/src/include/openssl/ssl.h" @@ -22,7 +23,6 @@ #include "quic/platform/api/quic_hostname_utils.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_server_stats.h" -#include "common/platform/api/quiche_text_utils.h" #define RECORD_LATENCY_IN_US(stat_name, latency, comment) \ do { \ @@ -47,8 +47,6 @@ void TlsServerHandshaker::DefaultProofSourceHandle::CancelPendingOperation() { QUIC_DVLOG(1) << "CancelPendingOperation. is_signature_pending=" << (signature_callback_ != nullptr); if (signature_callback_) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_tls_use_per_handshaker_proof_source, 3, - 3); signature_callback_->Cancel(); signature_callback_ = nullptr; } @@ -58,9 +56,11 @@ QuicAsyncStatus TlsServerHandshaker::DefaultProofSourceHandle::SelectCertificate( const QuicSocketAddress& server_address, const QuicSocketAddress& client_address, + absl::string_view /*ssl_capabilities*/, const std::string& hostname, absl::string_view /*client_hello*/, const std::string& /*alpn*/, + absl::optional<std::string> /*alps*/, const std::vector<uint8_t>& /*quic_transport_params*/, const absl::optional<std::vector<uint8_t>>& /*early_data_context*/) { if (!handshaker_ || !proof_source_) { @@ -73,7 +73,8 @@ TlsServerHandshaker::DefaultProofSourceHandle::SelectCertificate( proof_source_->GetCertChain(server_address, client_address, hostname); handshaker_->OnSelectCertificateDone( - /*ok=*/true, /*is_sync=*/true, chain.get()); + /*ok=*/true, /*is_sync=*/true, chain.get(), + /*handshake_hints=*/absl::string_view()); if (!handshaker_->select_cert_status().has_value()) { QUIC_BUG(quic_bug_12423_1) << "select_cert_status() has no value after a synchronous select cert"; @@ -119,35 +120,6 @@ QuicAsyncStatus TlsServerHandshaker::DefaultProofSourceHandle::ComputeSignature( return success ? QUIC_SUCCESS : QUIC_FAILURE; } -TlsServerHandshaker::SignatureCallback::SignatureCallback( - TlsServerHandshaker* handshaker) - : handshaker_(handshaker) { - QUICHE_DCHECK(!handshaker_->use_proof_source_handle_); -} - -void TlsServerHandshaker::SignatureCallback::Run( - bool ok, - std::string signature, - std::unique_ptr<ProofSource::Details> details) { - if (handshaker_ == nullptr) { - return; - } - if (ok) { - handshaker_->cert_verify_sig_ = std::move(signature); - handshaker_->proof_source_details_ = std::move(details); - } - int last_expected_ssl_error = handshaker_->expected_ssl_error(); - handshaker_->set_expected_ssl_error(SSL_ERROR_WANT_READ); - handshaker_->signature_callback_ = nullptr; - if (last_expected_ssl_error == SSL_ERROR_WANT_PRIVATE_KEY_OPERATION) { - handshaker_->AdvanceHandshakeFromCallback(); - } -} - -void TlsServerHandshaker::SignatureCallback::Cancel() { - handshaker_ = nullptr; -} - TlsServerHandshaker::DecryptCallback::DecryptCallback( TlsServerHandshaker* handshaker) : handshaker_(handshaker) {} @@ -217,13 +189,9 @@ TlsServerHandshaker::~TlsServerHandshaker() { } void TlsServerHandshaker::CancelOutstandingCallbacks() { - if (use_proof_source_handle_ && proof_source_handle_) { + if (proof_source_handle_) { proof_source_handle_->CancelPendingOperation(); } - if (signature_callback_) { - signature_callback_->Cancel(); - signature_callback_ = nullptr; - } if (ticket_decryption_callback_) { ticket_decryption_callback_->Cancel(); ticket_decryption_callback_ = nullptr; @@ -232,7 +200,6 @@ void TlsServerHandshaker::CancelOutstandingCallbacks() { std::unique_ptr<ProofSourceHandle> TlsServerHandshaker::MaybeCreateProofSourceHandle() { - QUICHE_DCHECK(use_proof_source_handle_); return std::make_unique<DefaultProofSourceHandle>(this, proof_source_); } @@ -384,6 +351,19 @@ TlsServerHandshaker::CreateCurrentOneRttEncrypter() { void TlsServerHandshaker::OverrideQuicConfigDefaults(QuicConfig* /*config*/) {} void TlsServerHandshaker::AdvanceHandshakeFromCallback() { + std::unique_ptr<QuicConnection::ScopedPacketFlusher> flusher; + if (add_packet_flusher_on_async_op_done_) { + if (session()->PacketFlusherAttached()) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_add_packet_flusher_on_async_op_done, 1, + 2); + } else { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_add_packet_flusher_on_async_op_done, 2, + 2); + } + flusher = std::make_unique<QuicConnection::ScopedPacketFlusher>( + session()->connection()); + } + AdvanceHandshake(); if (!is_connection_closed()) { handshaker_delegate()->OnHandshakeCallbackDone(); @@ -541,6 +521,7 @@ std::string TlsServerHandshaker::GetAcceptChValueForOrigin( void TlsServerHandshaker::FinishHandshake() { if (retry_handshake_on_early_data_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_tls_retry_handshake_on_early_data, 2, 2); QUICHE_DCHECK(!SSL_in_early_data(ssl())); } else { if (SSL_in_early_data(ssl())) { @@ -601,33 +582,19 @@ ssl_private_key_result_t TlsServerHandshaker::PrivateKeySign( uint16_t sig_alg, absl::string_view in) { QUICHE_DCHECK_EQ(expected_ssl_error(), SSL_ERROR_WANT_READ); - if (use_proof_source_handle_) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_tls_use_per_handshaker_proof_source, 2, - 3); - QuicAsyncStatus status = proof_source_handle_->ComputeSignature( - session()->connection()->self_address(), - session()->connection()->peer_address(), cert_selection_hostname(), - sig_alg, in, max_out); - if (status == QUIC_PENDING) { - set_expected_ssl_error(SSL_ERROR_WANT_PRIVATE_KEY_OPERATION); - if (async_op_timer_.has_value()) { - QUIC_CODE_COUNT( - quic_tls_server_computing_signature_while_another_op_pending); - } - async_op_timer_ = QuicTimeAccumulator(); - async_op_timer_->Start(now()); - } - return PrivateKeyComplete(out, out_len, max_out); - } - signature_callback_ = new SignatureCallback(this); - proof_source_->ComputeTlsSignature( + QuicAsyncStatus status = proof_source_handle_->ComputeSignature( session()->connection()->self_address(), - session()->connection()->peer_address(), cert_selection_hostname(), - sig_alg, in, std::unique_ptr<SignatureCallback>(signature_callback_)); - if (signature_callback_) { + session()->connection()->peer_address(), crypto_negotiated_params_->sni, + sig_alg, in, max_out); + if (status == QUIC_PENDING) { set_expected_ssl_error(SSL_ERROR_WANT_PRIVATE_KEY_OPERATION); - return ssl_private_key_retry; + if (async_op_timer_.has_value()) { + QUIC_CODE_COUNT( + quic_tls_server_computing_signature_while_another_op_pending); + } + async_op_timer_ = QuicTimeAccumulator(); + async_op_timer_->Start(now()); } return PrivateKeyComplete(out, out_len, max_out); } @@ -673,7 +640,6 @@ void TlsServerHandshaker::OnComputeSignatureDone( QUIC_DVLOG(1) << "OnComputeSignatureDone. ok:" << ok << ", is_sync:" << is_sync << ", len(signature):" << signature.size(); - QUICHE_DCHECK(use_proof_source_handle_); if (ok) { cert_verify_sig_ = std::move(signature); proof_source_details_ = std::move(details); @@ -795,24 +761,21 @@ ssl_select_cert_result_t TlsServerHandshaker::EarlySelectCertCallback( // EarlySelectCertCallback can be called twice from BoringSSL: If the first // call returns ssl_select_cert_retry, when cert selection completes, // SSL_do_handshake will call it again. - if (use_proof_source_handle_) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_tls_use_per_handshaker_proof_source, 1, - 3); - if (select_cert_status_.has_value()) { - // This is the second call, return the result directly. - QUIC_DVLOG(1) << "EarlySelectCertCallback called to continue handshake, " - "returning directly. success:" - << (select_cert_status_.value() == QUIC_SUCCESS); - return (select_cert_status_.value() == QUIC_SUCCESS) - ? ssl_select_cert_success - : ssl_select_cert_error; - } - // This is the first call. - select_cert_status_ = QUIC_PENDING; - proof_source_handle_ = MaybeCreateProofSourceHandle(); + if (select_cert_status_.has_value()) { + // This is the second call, return the result directly. + QUIC_DVLOG(1) << "EarlySelectCertCallback called to continue handshake, " + "returning directly. success:" + << (select_cert_status_.value() == QUIC_SUCCESS); + return (select_cert_status_.value() == QUIC_SUCCESS) + ? ssl_select_cert_success + : ssl_select_cert_error; } + // This is the first call. + select_cert_status_ = QUIC_PENDING; + proof_source_handle_ = MaybeCreateProofSourceHandle(); + if (!pre_shared_key_.empty()) { // TODO(b/154162689) add PSK support to QUIC+TLS. QUIC_BUG(quic_bug_10341_6) @@ -824,18 +787,17 @@ ssl_select_cert_result_t TlsServerHandshaker::EarlySelectCertCallback( // function do not work at this point, but SSL_get_servername does. const char* hostname = SSL_get_servername(ssl(), TLSEXT_NAMETYPE_host_name); if (hostname) { - hostname_ = hostname; crypto_negotiated_params_->sni = - QuicHostnameUtils::NormalizeHostname(hostname_); - if (!ValidateHostname(hostname_)) { + QuicHostnameUtils::NormalizeHostname(hostname); + if (!ValidateHostname(hostname)) { return ssl_select_cert_error; } - if (hostname_ != crypto_negotiated_params_->sni) { + if (hostname != crypto_negotiated_params_->sni) { QUIC_CODE_COUNT(quic_tls_server_hostname_diff); QUIC_LOG_EVERY_N_SEC(WARNING, 300) << "Raw and normalized hostnames differ, but both are valid SNIs. " "raw hostname:" - << hostname_ << ", normalized:" << crypto_negotiated_params_->sni; + << hostname << ", normalized:" << crypto_negotiated_params_->sni; } else { QUIC_CODE_COUNT(quic_tls_server_hostname_same); } @@ -843,97 +805,105 @@ ssl_select_cert_result_t TlsServerHandshaker::EarlySelectCertCallback( QUIC_LOG(INFO) << "No hostname indicated in SNI"; } - if (use_proof_source_handle_) { - std::string error_details; - if (!ProcessTransportParameters(client_hello, &error_details)) { - CloseConnection(QUIC_HANDSHAKE_FAILED, error_details); - return ssl_select_cert_error; - } - OverrideQuicConfigDefaults(session()->config()); - session()->OnConfigNegotiated(); - - auto set_transport_params_result = SetTransportParameters(); - if (!set_transport_params_result.success) { - QUIC_LOG(ERROR) << "Failed to set transport parameters"; - return ssl_select_cert_error; - } - - const QuicAsyncStatus status = proof_source_handle_->SelectCertificate( - session()->connection()->self_address(), - session()->connection()->peer_address(), cert_selection_hostname(), - absl::string_view( - reinterpret_cast<const char*>(client_hello->client_hello), - client_hello->client_hello_len), - AlpnForVersion(session()->version()), - set_transport_params_result.quic_transport_params, - set_transport_params_result.early_data_context); + std::string error_details; + if (!ProcessTransportParameters(client_hello, &error_details)) { + CloseConnection(QUIC_HANDSHAKE_FAILED, error_details); + return ssl_select_cert_error; + } + OverrideQuicConfigDefaults(session()->config()); + session()->OnConfigNegotiated(); - QUICHE_DCHECK_EQ(status, select_cert_status().value()); + auto set_transport_params_result = SetTransportParameters(); + if (!set_transport_params_result.success) { + QUIC_LOG(ERROR) << "Failed to set transport parameters"; + return ssl_select_cert_error; + } - if (status == QUIC_PENDING) { - set_expected_ssl_error(SSL_ERROR_PENDING_CERTIFICATE); - if (async_op_timer_.has_value()) { - QUIC_CODE_COUNT( - quic_tls_server_selecting_cert_while_another_op_pending); - } - async_op_timer_ = QuicTimeAccumulator(); - async_op_timer_->Start(now()); - return ssl_select_cert_retry; + bssl::UniquePtr<uint8_t> ssl_capabilities; + size_t ssl_capabilities_len = 0; + absl::string_view ssl_capabilities_view; + + absl::optional<std::string> alps; + if (use_handshake_hints_) { + QUIC_RELOADABLE_FLAG_COUNT(quic_tls_server_use_handshake_hints); + if (CryptoUtils::GetSSLCapabilities(ssl(), &ssl_capabilities, + &ssl_capabilities_len)) { + ssl_capabilities_view = absl::string_view( + reinterpret_cast<const char*>(ssl_capabilities.get()), + ssl_capabilities_len); } - if (status == QUIC_FAILURE) { + // Enable ALPS for the session's ALPN. + SetApplicationSettingsResult alps_result = + SetApplicationSettings(AlpnForVersion(session()->version())); + if (!alps_result.success) { return ssl_select_cert_error; } - - return ssl_select_cert_success; - } - - QuicReferenceCountedPointer<ProofSource::Chain> chain = - proof_source_->GetCertChain(session()->connection()->self_address(), - session()->connection()->peer_address(), - cert_selection_hostname()); - if (!chain || chain->certs.empty()) { - QUIC_LOG(ERROR) << "No certs provided for host. raw:" << hostname_ - << ", normalized:" << crypto_negotiated_params_->sni; - return ssl_select_cert_error; + alps = alps_result.alps_length > 0 + ? std::string(alps_result.alps_buffer.get(), + alps_result.alps_length) + : std::string(); } - CryptoBuffers cert_buffers = chain->ToCryptoBuffers(); - tls_connection_.SetCertChain(cert_buffers.value); - - std::string error_details; - if (!ProcessTransportParameters(client_hello, &error_details)) { - CloseConnection(QUIC_HANDSHAKE_FAILED, error_details); - return ssl_select_cert_error; + const QuicAsyncStatus status = proof_source_handle_->SelectCertificate( + session()->connection()->self_address(), + session()->connection()->peer_address(), ssl_capabilities_view, + crypto_negotiated_params_->sni, + absl::string_view( + reinterpret_cast<const char*>(client_hello->client_hello), + client_hello->client_hello_len), + AlpnForVersion(session()->version()), std::move(alps), + set_transport_params_result.quic_transport_params, + set_transport_params_result.early_data_context); + + QUICHE_DCHECK_EQ(status, select_cert_status().value()); + + if (status == QUIC_PENDING) { + set_expected_ssl_error(SSL_ERROR_PENDING_CERTIFICATE); + if (async_op_timer_.has_value()) { + QUIC_CODE_COUNT(quic_tls_server_selecting_cert_while_another_op_pending); + } + async_op_timer_ = QuicTimeAccumulator(); + async_op_timer_->Start(now()); + return ssl_select_cert_retry; } - OverrideQuicConfigDefaults(session()->config()); - session()->OnConfigNegotiated(); - if (!SetTransportParameters().success) { - QUIC_LOG(ERROR) << "Failed to set transport parameters"; + if (status == QUIC_FAILURE) { return ssl_select_cert_error; } - QUIC_DLOG(INFO) << "Set " << chain->certs.size() << " certs for server " - << "with hostname " << hostname_; return ssl_select_cert_success; } void TlsServerHandshaker::OnSelectCertificateDone( bool ok, bool is_sync, - const ProofSource::Chain* chain) { + const ProofSource::Chain* chain, + absl::string_view handshake_hints) { QUIC_DVLOG(1) << "OnSelectCertificateDone. ok:" << ok - << ", is_sync:" << is_sync; - QUICHE_DCHECK(use_proof_source_handle_); - + << ", is_sync:" << is_sync + << ", len(handshake_hints):" << handshake_hints.size(); select_cert_status_ = QUIC_FAILURE; if (ok) { if (chain && !chain->certs.empty()) { tls_connection_.SetCertChain(chain->ToCryptoBuffers().value); + if (use_handshake_hints_) { + if (!handshake_hints.empty() && + !SSL_set_handshake_hints( + ssl(), reinterpret_cast<const uint8_t*>(handshake_hints.data()), + handshake_hints.size())) { + // If |SSL_set_handshake_hints| fails, the ssl() object will remain + // intact, it is as if we didn't call it. The handshaker will + // continue to compute signature/decrypt ticket as normal. + QUIC_CODE_COUNT(quic_tls_server_set_handshake_hints_failed); + QUIC_DVLOG(1) << "SSL_set_handshake_hints failed"; + } + } select_cert_status_ = QUIC_SUCCESS; } else { - QUIC_LOG(ERROR) << "No certs provided for host '" << hostname_ << "'"; + QUIC_LOG(ERROR) << "No certs provided for host '" + << crypto_negotiated_params_->sni << "', server_address:" + << session()->connection()->self_address(); } } @@ -1008,40 +978,17 @@ int TlsServerHandshaker::SelectAlpn(const uint8_t** out, alpn_length); } + // TODO(wub): Remove QuicSession::SelectAlpn. QuicSessions should know the + // ALPN on construction. auto selected_alpn = session()->SelectAlpn(alpns); if (selected_alpn == alpns.end()) { QUIC_DLOG(ERROR) << "No known ALPN provided by client"; return SSL_TLSEXT_ERR_NOACK; } - // Enable ALPS for the selected ALPN protocol. - if (GetQuicReloadableFlag(quic_enable_alps_server)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_enable_alps_server); - - const uint8_t* alps_data = nullptr; - size_t alps_length = 0; - std::unique_ptr<char[]> buffer; - - const std::string& hostname = crypto_negotiated_params_->sni; - std::string accept_ch_value = GetAcceptChValueForOrigin(hostname); - - std::string origin; - if (GetQuicReloadableFlag(quic_alps_include_scheme_in_origin)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_alps_include_scheme_in_origin); - origin = "https://"; - } - origin.append(crypto_negotiated_params_->sni); - - if (!accept_ch_value.empty()) { - AcceptChFrame frame{{{std::move(origin), std::move(accept_ch_value)}}}; - alps_length = HttpEncoder::SerializeAcceptChFrame(frame, &buffer); - alps_data = reinterpret_cast<const uint8_t*>(buffer.get()); - } - - if (SSL_add_application_settings( - ssl(), reinterpret_cast<const uint8_t*>(selected_alpn->data()), - selected_alpn->size(), alps_data, alps_length) != 1) { - QUIC_DLOG(ERROR) << "Failed to enable ALPS"; + if (!use_handshake_hints_) { + // Enable ALPS for the selected ALPN protocol. + if (!SetApplicationSettings(*selected_alpn).success) { return SSL_TLSEXT_ERR_NOACK; } } @@ -1053,4 +1000,31 @@ int TlsServerHandshaker::SelectAlpn(const uint8_t** out, return SSL_TLSEXT_ERR_OK; } +TlsServerHandshaker::SetApplicationSettingsResult +TlsServerHandshaker::SetApplicationSettings(absl::string_view alpn) { + TlsServerHandshaker::SetApplicationSettingsResult result; + const uint8_t* alps_data = nullptr; + + const std::string& hostname = crypto_negotiated_params_->sni; + std::string accept_ch_value = GetAcceptChValueForOrigin(hostname); + std::string origin = absl::StrCat("https://", hostname); + + if (!accept_ch_value.empty()) { + AcceptChFrame frame{{{std::move(origin), std::move(accept_ch_value)}}}; + result.alps_length = + HttpEncoder::SerializeAcceptChFrame(frame, &result.alps_buffer); + alps_data = reinterpret_cast<const uint8_t*>(result.alps_buffer.get()); + } + + if (SSL_add_application_settings( + ssl(), reinterpret_cast<const uint8_t*>(alpn.data()), alpn.size(), + alps_data, result.alps_length) != 1) { + QUIC_DLOG(ERROR) << "Failed to enable ALPS"; + result.success = false; + } else { + result.success = true; + } + return result; +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h index 7850d3519a8..260b094bda7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h +++ b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h @@ -19,6 +19,7 @@ #include "quic/core/quic_types.h" #include "quic/core/tls_handshaker.h" #include "quic/platform/api/quic_export.h" +#include "quic/platform/api/quic_flags.h" namespace quic { @@ -88,25 +89,14 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker protected: // Creates a proof source handle for selecting cert and computing signature. - // Only called when |use_proof_source_handle_| is true. virtual std::unique_ptr<ProofSourceHandle> MaybeCreateProofSourceHandle(); - bool use_proof_source_handle() const { return use_proof_source_handle_; } - // Hook to allow the server to override parts of the QuicConfig based on SNI // before we generate transport parameters. virtual void OverrideQuicConfigDefaults(QuicConfig* config); virtual bool ValidateHostname(const std::string& hostname) const; - // The hostname to be used to select certificates and compute signatures. - // The function should only be called after a successful ValidateHostname(). - const std::string& cert_selection_hostname() const { - return use_normalized_sni_for_cert_selection_ - ? crypto_negotiated_params_->sni - : hostname_; - } - const TlsConnection* tls_connection() const override { return &tls_connection_; } @@ -177,7 +167,8 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker // ProofSourceHandleCallback implementation: void OnSelectCertificateDone(bool ok, bool is_sync, - const ProofSource::Chain* chain) override; + const ProofSource::Chain* chain, + absl::string_view handshake_hints) override; void OnComputeSignatureDone( bool ok, @@ -186,21 +177,6 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker std::unique_ptr<ProofSource::Details> details) override; private: - class QUIC_EXPORT_PRIVATE SignatureCallback - : public ProofSource::SignatureCallback { - public: - explicit SignatureCallback(TlsServerHandshaker* handshaker); - void Run(bool ok, - std::string signature, - std::unique_ptr<ProofSource::Details> details) override; - - // If called, Cancel causes the pending callback to be a no-op. - void Cancel(); - - private: - TlsServerHandshaker* handshaker_; - }; - class QUIC_EXPORT_PRIVATE DecryptCallback : public ProofSource::DecryptCallback { public: @@ -232,9 +208,11 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker QuicAsyncStatus SelectCertificate( const QuicSocketAddress& server_address, const QuicSocketAddress& client_address, + absl::string_view ssl_capabilities, const std::string& hostname, absl::string_view client_hello, const std::string& alpn, + absl::optional<std::string> alps, const std::vector<uint8_t>& quic_transport_params, const absl::optional<std::vector<uint8_t>>& early_data_context) override; @@ -302,6 +280,13 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker bool ProcessTransportParameters(const SSL_CLIENT_HELLO* client_hello, std::string* error_details); + struct QUIC_NO_EXPORT SetApplicationSettingsResult { + bool success = false; + std::unique_ptr<char[]> alps_buffer; + size_t alps_length = 0; + }; + SetApplicationSettingsResult SetApplicationSettings(absl::string_view alpn); + QuicConnectionStats& connection_stats() { return session()->connection()->mutable_stats(); } @@ -309,7 +294,6 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker std::unique_ptr<ProofSourceHandle> proof_source_handle_; ProofSource* proof_source_; - SignatureCallback* signature_callback_ = nullptr; // State to handle potentially asynchronous session ticket decryption. // |ticket_decryption_callback_| points to the non-owned callback that was @@ -328,7 +312,6 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker // nullopt means select cert hasn't started. absl::optional<QuicAsyncStatus> select_cert_status_; - std::string hostname_; std::string cert_verify_sig_; std::unique_ptr<ProofSource::Details> proof_source_details_; @@ -346,11 +329,9 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> crypto_negotiated_params_; TlsServerConnection tls_connection_; - const bool use_proof_source_handle_ = - GetQuicReloadableFlag(quic_tls_use_per_handshaker_proof_source); - const bool use_normalized_sni_for_cert_selection_ = - GetQuicReloadableFlag(quic_tls_use_normalized_sni_for_cert_selectioon); const QuicCryptoServerConfig* crypto_config_; // Unowned. + const bool use_handshake_hints_ = + GetQuicReloadableFlag(quic_tls_server_use_handshake_hints); }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker_test.cc b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker_test.cc index fbec2551227..d67c8791d2c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker_test.cc @@ -400,10 +400,6 @@ TEST_P(TlsServerHandshakerTest, ConnectedAfterTlsHandshake) { } TEST_P(TlsServerHandshakerTest, HandshakeWithAsyncSelectCertSuccess) { - if (!GetQuicReloadableFlag(quic_tls_use_per_handshaker_proof_source)) { - return; - } - InitializeServerWithFakeProofSourceHandle(); server_handshaker_->SetupProofSourceHandle( /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC, @@ -426,10 +422,6 @@ TEST_P(TlsServerHandshakerTest, HandshakeWithAsyncSelectCertSuccess) { } TEST_P(TlsServerHandshakerTest, HandshakeWithAsyncSelectCertFailure) { - if (!GetQuicReloadableFlag(quic_tls_use_per_handshaker_proof_source)) { - return; - } - InitializeServerWithFakeProofSourceHandle(); server_handshaker_->SetupProofSourceHandle( /*select_cert_action=*/FakeProofSourceHandle::Action::FAIL_ASYNC, @@ -449,10 +441,6 @@ TEST_P(TlsServerHandshakerTest, HandshakeWithAsyncSelectCertFailure) { } TEST_P(TlsServerHandshakerTest, HandshakeWithAsyncSelectCertAndSignature) { - if (!GetQuicReloadableFlag(quic_tls_use_per_handshaker_proof_source)) { - return; - } - InitializeServerWithFakeProofSourceHandle(); server_handshaker_->SetupProofSourceHandle( /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC, @@ -507,10 +495,6 @@ TEST_P(TlsServerHandshakerTest, HandshakeWithAsyncSignature) { } TEST_P(TlsServerHandshakerTest, CancelPendingSelectCert) { - if (!GetQuicReloadableFlag(quic_tls_use_per_handshaker_proof_source)) { - return; - } - InitializeServerWithFakeProofSourceHandle(); server_handshaker_->SetupProofSourceHandle( /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_ASYNC, @@ -557,10 +541,6 @@ TEST_P(TlsServerHandshakerTest, ExtractSNI) { } TEST_P(TlsServerHandshakerTest, HostnameForCertSelectionAndComputeSignature) { - if (!GetQuicReloadableFlag(quic_tls_use_per_handshaker_proof_source)) { - return; - } - // Client uses upper case letters in hostname. It is considered valid by // QuicHostnameUtils::IsValidSNI, but it should be normalized for cert // selection. @@ -577,23 +557,13 @@ TEST_P(TlsServerHandshakerTest, HostnameForCertSelectionAndComputeSignature) { EXPECT_EQ(server_stream()->crypto_negotiated_params().sni, "test.example.com"); - if (GetQuicReloadableFlag(quic_tls_use_normalized_sni_for_cert_selectioon)) { - EXPECT_EQ(last_select_cert_args().hostname, "test.example.com"); - EXPECT_EQ(last_compute_signature_args().hostname, "test.example.com"); - } else { - EXPECT_EQ(last_select_cert_args().hostname, "tEsT.EXAMPLE.CoM"); - EXPECT_EQ(last_compute_signature_args().hostname, "tEsT.EXAMPLE.CoM"); - } + EXPECT_EQ(last_select_cert_args().hostname, "test.example.com"); + EXPECT_EQ(last_compute_signature_args().hostname, "test.example.com"); } TEST_P(TlsServerHandshakerTest, ConnectionClosedOnTlsError) { - if (GetQuicReloadableFlag(quic_send_tls_crypto_error_code)) { - EXPECT_CALL(*server_connection_, - CloseConnection(QUIC_HANDSHAKE_FAILED, _, _, _)); - } else { - EXPECT_CALL(*server_connection_, - CloseConnection(QUIC_HANDSHAKE_FAILED, _, _)); - } + EXPECT_CALL(*server_connection_, + CloseConnection(QUIC_HANDSHAKE_FAILED, _, _, _)); // Send a zero-length ClientHello from client to server. char bogus_handshake_message[] = { @@ -601,6 +571,15 @@ TEST_P(TlsServerHandshakerTest, ConnectionClosedOnTlsError) { 1, // HandshakeType client_hello 0, 0, 0, // uint24 length }; + + // Install a packet flusher such that the packets generated by + // |server_connection_| in response to this handshake message are more likely + // to be coalesced and/or batched in the writer. + // + // This is required by TlsServerHandshaker because without the flusher, it + // tends to generate many small, uncoalesced packets, one per + // TlsHandshaker::WriteMessage. + QuicConnection::ScopedPacketFlusher flusher(server_connection_); server_stream()->crypto_message_parser()->ProcessInput( absl::string_view(bogus_handshake_message, ABSL_ARRAYSIZE(bogus_handshake_message)), @@ -613,23 +592,14 @@ TEST_P(TlsServerHandshakerTest, ClientSendingBadALPN) { const std::string kTestBadClientAlpn = "bad-client-alpn"; EXPECT_CALL(*client_session_, GetAlpnsToOffer()) .WillOnce(Return(std::vector<std::string>({kTestBadClientAlpn}))); - if (GetQuicReloadableFlag(quic_send_tls_crypto_error_code)) { - EXPECT_CALL( - *server_connection_, - CloseConnection( - QUIC_HANDSHAKE_FAILED, - static_cast<QuicIetfTransportErrorCodes>(CRYPTO_ERROR_FIRST + 120), - "TLS handshake failure (ENCRYPTION_INITIAL) 120: " - "no application protocol", - _)); - } else { - EXPECT_CALL( - *server_connection_, - CloseConnection(QUIC_HANDSHAKE_FAILED, - "TLS handshake failure (ENCRYPTION_INITIAL) 120: " - "no application protocol", - _)); - } + + EXPECT_CALL(*server_connection_, + CloseConnection(QUIC_HANDSHAKE_FAILED, + static_cast<QuicIetfTransportErrorCodes>( + CRYPTO_ERROR_FIRST + 120), + "TLS handshake failure (ENCRYPTION_INITIAL) 120: " + "no application protocol", + _)); AdvanceHandshakeWithFakeClient(); diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.cc b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.cc index 5ed0e08777a..8f20b33c183 100644 --- a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.cc @@ -120,14 +120,11 @@ void UberReceivedPacketManager::EnableMultiplePacketNumberSpacesSupport( } // In IETF QUIC, the peer is expected to acknowledge packets in Initial and // Handshake packets with minimal delay. - if (!GetQuicReloadableFlag(quic_delay_initial_ack) || - perspective == Perspective::IS_CLIENT) { + if (perspective == Perspective::IS_CLIENT) { // Delay the first server ACK, because server ACKs are padded to // full size and count towards the amplification limit. received_packet_managers_[INITIAL_DATA].set_local_max_ack_delay( kAlarmGranularity); - } else { - QUIC_RELOADABLE_FLAG_COUNT(quic_delay_initial_ack); } received_packet_managers_[HANDSHAKE_DATA].set_local_max_ack_delay( kAlarmGranularity); diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc index 05e4a33aedf..cf612feab1d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc @@ -478,17 +478,10 @@ TEST_F(UberReceivedPacketManagerTest, AckSendingDifferentPacketNumberSpaces) { MaybeUpdateAckTimeout(kInstigateAck, ENCRYPTION_INITIAL, 3); EXPECT_TRUE(HasPendingAck()); // Delayed ack is scheduled. - if (GetQuicReloadableFlag(quic_delay_initial_ack)) { - CheckAckTimeout(clock_.ApproximateNow() + - QuicTime::Delta::FromMilliseconds(25)); - // Send delayed handshake data ACK. - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(25)); - } else { - CheckAckTimeout(clock_.ApproximateNow() + - QuicTime::Delta::FromMilliseconds(1)); - // Send delayed handshake data ACK. - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); - } + CheckAckTimeout(clock_.ApproximateNow() + + QuicTime::Delta::FromMilliseconds(25)); + // Send delayed handshake data ACK. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(25)); CheckAckTimeout(clock_.ApproximateNow()); EXPECT_FALSE(HasPendingAck()); |