diff options
Diffstat (limited to 'chromium/net/third_party')
269 files changed, 12664 insertions, 9670 deletions
diff --git a/chromium/net/third_party/mozilla_security_manager/nsNSSCertificateDB.cpp b/chromium/net/third_party/mozilla_security_manager/nsNSSCertificateDB.cpp index 7174f9947b7..39e9a6267d3 100644 --- a/chromium/net/third_party/mozilla_security_manager/nsNSSCertificateDB.cpp +++ b/chromium/net/third_party/mozilla_security_manager/nsNSSCertificateDB.cpp @@ -44,6 +44,7 @@ #include <secerr.h> #include "base/logging.h" +#include "base/notreached.h" #include "crypto/scoped_nss_types.h" #include "net/base/net_errors.h" #include "net/cert/x509_certificate.h" diff --git a/chromium/net/third_party/quiche/BUILD.gn b/chromium/net/third_party/quiche/BUILD.gn index 59a75e2132a..a715ea8e2cc 100644 --- a/chromium/net/third_party/quiche/BUILD.gn +++ b/chromium/net/third_party/quiche/BUILD.gn @@ -23,6 +23,7 @@ source_set("quiche") { "src/common/platform/api/quiche_str_cat.h", "src/common/platform/api/quiche_string_piece.h", "src/common/platform/api/quiche_text_utils.h", + "src/common/platform/api/quiche_time_utils.h", "src/common/platform/api/quiche_unordered_containers.h", "src/common/quiche_data_reader.cc", "src/common/quiche_data_reader.h", @@ -253,6 +254,8 @@ source_set("quiche") { "src/quic/core/crypto/transport_parameters.h", "src/quic/core/frames/quic_ack_frame.cc", "src/quic/core/frames/quic_ack_frame.h", + "src/quic/core/frames/quic_ack_frequency_frame.cc", + "src/quic/core/frames/quic_ack_frequency_frame.h", "src/quic/core/frames/quic_blocked_frame.cc", "src/quic/core/frames/quic_blocked_frame.h", "src/quic/core/frames/quic_connection_close_frame.cc", @@ -431,6 +434,8 @@ source_set("quiche") { "src/quic/core/quic_interval.h", "src/quic/core/quic_interval_deque.h", "src/quic/core/quic_interval_set.h", + "src/quic/core/quic_legacy_version_encapsulator.cc", + "src/quic/core/quic_legacy_version_encapsulator.h", "src/quic/core/quic_lru_cache.h", "src/quic/core/quic_mtu_discovery.cc", "src/quic/core/quic_mtu_discovery.h", @@ -548,6 +553,8 @@ source_set("quiche") { "src/quic/quic_transport/quic_transport_session_interface.h", "src/quic/quic_transport/quic_transport_stream.cc", "src/quic/quic_transport/quic_transport_stream.h", + "src/quic/quic_transport/web_transport_fingerprint_proof_verifier.cc", + "src/quic/quic_transport/web_transport_fingerprint_proof_verifier.h", "src/spdy/core/fifo_write_scheduler.h", "src/spdy/core/hpack/hpack_constants.cc", "src/spdy/core/hpack/hpack_constants.h", @@ -1230,6 +1237,7 @@ source_set("quiche_tests") { "src/common/platform/api/quiche_endian_test.cc", "src/common/platform/api/quiche_str_cat_test.cc", "src/common/platform/api/quiche_text_utils_test.cc", + "src/common/platform/api/quiche_time_utils_test.cc", "src/quic/core/congestion_control/bbr_sender_test.cc", "src/quic/core/congestion_control/cubic_bytes_test.cc", "src/quic/core/congestion_control/general_loss_algorithm_test.cc", @@ -1329,6 +1337,7 @@ source_set("quiche_tests") { "src/quic/core/quic_interval_deque_test.cc", "src/quic/core/quic_interval_set_test.cc", "src/quic/core/quic_interval_test.cc", + "src/quic/core/quic_legacy_version_encapsulator_test.cc", "src/quic/core/quic_lru_cache_test.cc", "src/quic/core/quic_network_blackhole_detector_test.cc", "src/quic/core/quic_one_block_arena_test.cc", @@ -1374,6 +1383,7 @@ source_set("quiche_tests") { "src/quic/quic_transport/quic_transport_integration_test.cc", "src/quic/quic_transport/quic_transport_server_session_test.cc", "src/quic/quic_transport/quic_transport_stream_test.cc", + "src/quic/quic_transport/web_transport_fingerprint_proof_verifier_test.cc", "src/quic/test_tools/crypto_test_utils_test.cc", "src/quic/test_tools/mock_quic_time_wait_list_manager.cc", "src/quic/test_tools/mock_quic_time_wait_list_manager.h", @@ -1426,58 +1436,6 @@ source_set("quiche_tests") { "src/spdy/platform/api/spdy_test_helpers.h", ] - # Disable building Quartc tests on iOS as they appear to be flaky there. - if (!is_ios) { - sources += [ - "src/quic/quartc/counting_packet_filter.h", - "src/quic/quartc/quartc_connection_helper.cc", - "src/quic/quartc/quartc_connection_helper.h", - "src/quic/quartc/quartc_crypto_helpers.cc", - "src/quic/quartc/quartc_crypto_helpers.h", - "src/quic/quartc/quartc_dispatcher.cc", - "src/quic/quartc/quartc_dispatcher.h", - "src/quic/quartc/quartc_endpoint.cc", - "src/quic/quartc/quartc_endpoint.h", - "src/quic/quartc/quartc_endpoint_test.cc", - "src/quic/quartc/quartc_factory.cc", - "src/quic/quartc/quartc_factory.h", - "src/quic/quartc/quartc_fakes.h", - "src/quic/quartc/quartc_interval_counter.h", - "src/quic/quartc/quartc_interval_counter_test.cc", - "src/quic/quartc/quartc_multiplexer.cc", - "src/quic/quartc/quartc_multiplexer.h", - "src/quic/quartc/quartc_multiplexer_test.cc", - "src/quic/quartc/quartc_packet_writer.cc", - "src/quic/quartc/quartc_packet_writer.h", - "src/quic/quartc/quartc_session.cc", - "src/quic/quartc/quartc_session.h", - "src/quic/quartc/quartc_session_test.cc", - "src/quic/quartc/quartc_stream.cc", - "src/quic/quartc/quartc_stream.h", - "src/quic/quartc/quartc_stream_test.cc", - "src/quic/quartc/simulated_packet_transport.cc", - "src/quic/quartc/simulated_packet_transport.h", - "src/quic/quartc/simulated_packet_transport_test.cc", - "src/quic/quartc/test/bidi_test_runner.cc", - "src/quic/quartc/test/bidi_test_runner.h", - "src/quic/quartc/test/quartc_bidi_test.cc", - "src/quic/quartc/test/quartc_competing_endpoint.cc", - "src/quic/quartc/test/quartc_competing_endpoint.h", - "src/quic/quartc/test/quartc_data_source.cc", - "src/quic/quartc/test/quartc_data_source.h", - "src/quic/quartc/test/quartc_data_source_test.cc", - "src/quic/quartc/test/quartc_peer.cc", - "src/quic/quartc/test/quartc_peer.h", - "src/quic/quartc/test/quartc_peer_test.cc", - "src/quic/quartc/test/quic_trace_interceptor.cc", - "src/quic/quartc/test/quic_trace_interceptor.h", - "src/quic/quartc/test/random_delay_link.cc", - "src/quic/quartc/test/random_delay_link.h", - "src/quic/quartc/test/random_packet_filter.cc", - "src/quic/quartc/test/random_packet_filter.h", - ] - } - deps = [ "//net", "//net:quic_test_tools", diff --git a/chromium/net/third_party/quiche/src/common/platform/api/quiche_optional.h b/chromium/net/third_party/quiche/src/common/platform/api/quiche_optional.h index 141c0e49cd8..d5d3dac3538 100644 --- a/chromium/net/third_party/quiche/src/common/platform/api/quiche_optional.h +++ b/chromium/net/third_party/quiche/src/common/platform/api/quiche_optional.h @@ -13,7 +13,8 @@ namespace quiche { template <typename T> using QuicheOptional = QuicheOptionalImpl<T>; -#define QuicheNullOpt QuicheNullOptImpl + +#define QUICHE_NULLOPT QUICHE_NULLOPT_IMPL } // namespace quiche diff --git a/chromium/net/third_party/quiche/src/common/platform/api/quiche_time_utils.h b/chromium/net/third_party/quiche/src/common/platform/api/quiche_time_utils.h new file mode 100644 index 00000000000..7319568172b --- /dev/null +++ b/chromium/net/third_party/quiche/src/common/platform/api/quiche_time_utils.h @@ -0,0 +1,31 @@ +// Copyright 2020 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_COMMON_PLATFORM_API_QUICHE_TIME_UTILS_H_ +#define QUICHE_COMMON_PLATFORM_API_QUICHE_TIME_UTILS_H_ + +#include <cstdint> + +#include "net/quiche/common/platform/impl/quiche_time_utils_impl.h" + +namespace quiche { + +// Converts a civil time specified in UTC into a number of seconds since the +// Unix epoch. This function is strict about validity of accepted dates. For +// instance, it will reject February 29 on non-leap years, or 25 hours in a day. +// As a notable exception, 60 seconds is accepted to deal with potential leap +// seconds. If the date predates Unix epoch, nullopt will be returned. +inline QuicheOptional<int64_t> QuicheUtcDateTimeToUnixSeconds(int year, + int month, + int day, + int hour, + int minute, + int second) { + return QuicheUtcDateTimeToUnixSecondsImpl(year, month, day, hour, minute, + second); +} + +} // namespace quiche + +#endif // QUICHE_COMMON_PLATFORM_API_QUICHE_TIME_UTILS_H_ diff --git a/chromium/net/third_party/quiche/src/common/platform/api/quiche_time_utils_test.cc b/chromium/net/third_party/quiche/src/common/platform/api/quiche_time_utils_test.cc new file mode 100644 index 00000000000..3ae296dbd81 --- /dev/null +++ b/chromium/net/third_party/quiche/src/common/platform/api/quiche_time_utils_test.cc @@ -0,0 +1,53 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/common/platform/api/quiche_time_utils.h" + +#include "net/third_party/quiche/src/common/platform/api/quiche_optional.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_test.h" + +namespace quiche { +namespace { + +TEST(QuicheTimeUtilsTest, Basic) { + EXPECT_EQ(1, QuicheUtcDateTimeToUnixSeconds(1970, 1, 1, 0, 0, 1)); + EXPECT_EQ(365 * 86400, QuicheUtcDateTimeToUnixSeconds(1971, 1, 1, 0, 0, 0)); + // Some arbitrary timestamps closer to the present, compared to the output of + // "Date(...).getTime()" from the JavaScript console. + EXPECT_EQ(1152966896, + QuicheUtcDateTimeToUnixSeconds(2006, 7, 15, 12, 34, 56)); + EXPECT_EQ(1591130001, QuicheUtcDateTimeToUnixSeconds(2020, 6, 2, 20, 33, 21)); + + EXPECT_EQ(QUICHE_NULLOPT, + QuicheUtcDateTimeToUnixSeconds(1970, 2, 29, 0, 0, 1)); + EXPECT_NE(QUICHE_NULLOPT, + QuicheUtcDateTimeToUnixSeconds(1972, 2, 29, 0, 0, 1)); +} + +TEST(QuicheTimeUtilsTest, Bounds) { + EXPECT_EQ(QUICHE_NULLOPT, + QuicheUtcDateTimeToUnixSeconds(1970, 1, 32, 0, 0, 1)); + EXPECT_EQ(QUICHE_NULLOPT, + QuicheUtcDateTimeToUnixSeconds(1970, 4, 31, 0, 0, 1)); + EXPECT_EQ(QUICHE_NULLOPT, + QuicheUtcDateTimeToUnixSeconds(1970, 1, 0, 0, 0, 1)); + EXPECT_EQ(QUICHE_NULLOPT, + QuicheUtcDateTimeToUnixSeconds(1970, 13, 1, 0, 0, 1)); + EXPECT_EQ(QUICHE_NULLOPT, + QuicheUtcDateTimeToUnixSeconds(1970, 0, 1, 0, 0, 1)); + EXPECT_EQ(QUICHE_NULLOPT, + QuicheUtcDateTimeToUnixSeconds(1970, 1, 1, 24, 0, 0)); + EXPECT_EQ(QUICHE_NULLOPT, + QuicheUtcDateTimeToUnixSeconds(1970, 1, 1, 0, 60, 0)); +} + +TEST(QuicheTimeUtilsTest, LeapSecond) { + EXPECT_EQ(QuicheUtcDateTimeToUnixSeconds(2015, 6, 30, 23, 59, 60), + QuicheUtcDateTimeToUnixSeconds(2015, 7, 1, 0, 0, 0)); + EXPECT_EQ(QuicheUtcDateTimeToUnixSeconds(2015, 6, 30, 25, 59, 60), + QUICHE_NULLOPT); +} + +} // namespace +} // namespace quiche diff --git a/chromium/net/third_party/quiche/src/common/quiche_data_reader.cc b/chromium/net/third_party/quiche/src/common/quiche_data_reader.cc index 344501378b5..2242fea6437 100644 --- a/chromium/net/third_party/quiche/src/common/quiche_data_reader.cc +++ b/chromium/net/third_party/quiche/src/common/quiche_data_reader.cc @@ -10,6 +10,7 @@ #include "net/third_party/quiche/src/common/platform/api/quiche_logging.h" #include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h" #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h" namespace quiche { @@ -119,6 +120,15 @@ bool QuicheDataReader::ReadTag(uint32_t* tag) { return ReadBytes(tag, sizeof(*tag)); } +bool QuicheDataReader::ReadDecimal64(size_t num_digits, uint64_t* result) { + quiche::QuicheStringPiece digits; + if (!ReadStringPiece(&digits, num_digits)) { + return false; + } + + return QuicheTextUtils::StringToUint64(digits, result); +} + quiche::QuicheStringPiece QuicheDataReader::ReadRemainingPayload() { quiche::QuicheStringPiece payload = PeekRemainingPayload(); pos_ = len_; diff --git a/chromium/net/third_party/quiche/src/common/quiche_data_reader.h b/chromium/net/third_party/quiche/src/common/quiche_data_reader.h index cf62a164708..f74f90d19e2 100644 --- a/chromium/net/third_party/quiche/src/common/quiche_data_reader.h +++ b/chromium/net/third_party/quiche/src/common/quiche_data_reader.h @@ -87,6 +87,11 @@ class QUICHE_EXPORT_PRIVATE QuicheDataReader { // endian. bool ReadTag(uint32_t* tag); + // Reads a sequence of a fixed number of decimal digits, parses them as an + // unsigned integer and returns them as a uint64_t. Forwards internal + // iterator on success, may forward it even in case of failure. + bool ReadDecimal64(size_t num_digits, uint64_t* result); + // Returns the remaining payload as a quiche::QuicheStringPiece. // // NOTE: Does not copy but rather references strings in the underlying buffer. 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 new file mode 100644 index 00000000000..919f76b3b80 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.cc @@ -0,0 +1,177 @@ +// 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 "net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.h" +#include <cstdint> + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" + +namespace quic { + +QuicBatchWriterBase::QuicBatchWriterBase( + std::unique_ptr<QuicBatchWriterBuffer> batch_buffer) + : write_blocked_(false), batch_buffer_(std::move(batch_buffer)) {} + +WriteResult QuicBatchWriterBase::WritePacket( + const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options) { + const WriteResult result = + InternalWritePacket(buffer, buf_len, self_address, peer_address, options); + if (result.status == WRITE_STATUS_BLOCKED) { + write_blocked_ = true; + } + return result; +} + +uint64_t QuicBatchWriterBase::GetReleaseTime( + const PerPacketOptions* options) const { + DCHECK(SupportsReleaseTime()); + + if (options == nullptr) { + return 0; + } + + if ((options->release_time_delay.IsZero() || options->allow_burst) && + !buffered_writes().empty()) { + // Send as soon as possible, but no sooner than the last buffered packet. + return buffered_writes().back().release_time; + } + + // Send according to the release time delay. + return NowInNanosForReleaseTime() + + options->release_time_delay.ToMicroseconds() * 1000; +} + +WriteResult QuicBatchWriterBase::InternalWritePacket( + const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options) { + if (buf_len > kMaxOutgoingPacketSize) { + return WriteResult(WRITE_STATUS_MSG_TOO_BIG, EMSGSIZE); + } + + uint64_t release_time = SupportsReleaseTime() ? GetReleaseTime(options) : 0; + + const CanBatchResult can_batch_result = CanBatch( + buffer, buf_len, self_address, peer_address, options, release_time); + + bool buffered = false; + bool flush = can_batch_result.must_flush; + + if (can_batch_result.can_batch) { + QuicBatchWriterBuffer::PushResult push_result = + batch_buffer_->PushBufferedWrite(buffer, buf_len, self_address, + peer_address, options, release_time); + if (push_result.succeeded) { + buffered = true; + // If there's no space left after the packet is buffered, force a flush. + flush = flush || (batch_buffer_->GetNextWriteLocation() == nullptr); + } else { + // If there's no space without this packet, force a flush. + flush = true; + } + } + + if (!flush) { + return WriteResult(WRITE_STATUS_OK, 0); + } + + size_t num_buffered_packets = buffered_writes().size(); + const FlushImplResult flush_result = CheckedFlush(); + const WriteResult& result = flush_result.write_result; + QUIC_DVLOG(1) << "Internally flushed " << flush_result.num_packets_sent + << " out of " << num_buffered_packets + << " packets. WriteResult=" << result; + + if (result.status != WRITE_STATUS_OK) { + if (IsWriteBlockedStatus(result.status)) { + return WriteResult( + buffered ? WRITE_STATUS_BLOCKED_DATA_BUFFERED : WRITE_STATUS_BLOCKED, + result.error_code); + } + + // Drop all packets, including the one being written. + size_t dropped_packets = + buffered ? buffered_writes().size() : buffered_writes().size() + 1; + + batch_buffer().Clear(); + WriteResult result_with_dropped = result; + result_with_dropped.dropped_packets = + dropped_packets > std::numeric_limits<uint16_t>::max() + ? std::numeric_limits<uint16_t>::max() + : static_cast<uint16_t>(dropped_packets); + return result_with_dropped; + } + + if (!buffered) { + QuicBatchWriterBuffer::PushResult push_result = + batch_buffer_->PushBufferedWrite(buffer, buf_len, self_address, + peer_address, options, release_time); + buffered = push_result.succeeded; + + // Since buffered_writes has been emptied, this write must have been + // buffered successfully. + QUIC_BUG_IF(!buffered) << "Failed to push to an empty batch buffer." + << " self_addr:" << self_address.ToString() + << ", peer_addr:" << peer_address.ToString() + << ", buf_len:" << buf_len; + } + + return result; +} + +QuicBatchWriterBase::FlushImplResult QuicBatchWriterBase::CheckedFlush() { + if (buffered_writes().empty()) { + return FlushImplResult{WriteResult(WRITE_STATUS_OK, 0), + /*num_packets_sent=*/0, /*bytes_written=*/0}; + } + + const FlushImplResult flush_result = FlushImpl(); + + // Either flush_result.write_result.status is not WRITE_STATUS_OK, or it is + // WRITE_STATUS_OK and batch_buffer is empty. + DCHECK(flush_result.write_result.status != WRITE_STATUS_OK || + buffered_writes().empty()); + + // Flush should never return WRITE_STATUS_BLOCKED_DATA_BUFFERED. + DCHECK(flush_result.write_result.status != + WRITE_STATUS_BLOCKED_DATA_BUFFERED); + + return flush_result; +} + +WriteResult QuicBatchWriterBase::Flush() { + size_t num_buffered_packets = buffered_writes().size(); + FlushImplResult flush_result = CheckedFlush(); + QUIC_DVLOG(1) << "Externally flushed " << flush_result.num_packets_sent + << " out of " << num_buffered_packets + << " packets. WriteResult=" << flush_result.write_result; + + if (IsWriteError(flush_result.write_result.status)) { + if (buffered_writes().size() > std::numeric_limits<uint16_t>::max()) { + flush_result.write_result.dropped_packets = + std::numeric_limits<uint16_t>::max(); + } else { + flush_result.write_result.dropped_packets = + static_cast<uint16_t>(buffered_writes().size()); + } + // Treat all errors as non-retryable fatal errors. Drop all buffered packets + // to avoid sending them and getting the same error again. + batch_buffer().Clear(); + } + + if (flush_result.write_result.status == WRITE_STATUS_BLOCKED) { + write_blocked_ = true; + } + return flush_result.write_result; +} + +} // namespace quic 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 new file mode 100644 index 00000000000..0213894f227 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.h @@ -0,0 +1,149 @@ +// 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_PLATFORM_IMPL_BATCH_WRITER_QUIC_BATCH_WRITER_BASE_H_ +#define QUICHE_QUIC_PLATFORM_IMPL_BATCH_WRITER_QUIC_BATCH_WRITER_BASE_H_ + +#include <cstdint> +#include "net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" + +namespace quic { + +// QuicBatchWriterBase implements logic common to all derived batch writers, +// including maintaining write blockage state and a skeleton implemention of +// WritePacket(). +// A derived batch writer must override the FlushImpl() function to send all +// buffered writes in a batch. It must also override the CanBatch() function +// to control whether/when a WritePacket() call should flush. +class QUIC_EXPORT_PRIVATE QuicBatchWriterBase : public QuicPacketWriter { + public: + explicit QuicBatchWriterBase( + std::unique_ptr<QuicBatchWriterBuffer> batch_buffer); + + // ATTENTION: If this write triggered a flush, and the flush failed, all + // buffered packets will be dropped to allow the next write to work. The + // number of dropped packets can be found in WriteResult.dropped_packets. + WriteResult WritePacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options) override; + + bool IsWriteBlocked() const final { return write_blocked_; } + + void SetWritable() final { write_blocked_ = false; } + + QuicByteCount GetMaxPacketSize( + const QuicSocketAddress& peer_address) const final { + return kMaxOutgoingPacketSize; + } + + bool SupportsReleaseTime() const { return false; } + + bool IsBatchMode() const final { return true; } + + QuicPacketBuffer GetNextWriteLocation( + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address) final { + // No need to explicitly delete QuicBatchWriterBuffer. + return {batch_buffer_->GetNextWriteLocation(), nullptr}; + } + + WriteResult Flush() final; + + protected: + const QuicBatchWriterBuffer& batch_buffer() const { return *batch_buffer_; } + QuicBatchWriterBuffer& batch_buffer() { return *batch_buffer_; } + + const QuicCircularDeque<BufferedWrite>& buffered_writes() const { + return batch_buffer_->buffered_writes(); + } + + // Get the current time in nanos which is understood by the sending api for + // releasing packets in the future. + virtual uint64_t NowInNanosForReleaseTime() const { + DCHECK(false) << "Should not be called since release time is unsupported."; + return 0; + } + + // Given the release delay in |options| and the state of |batch_buffer_|, get + // the absolute release time. + uint64_t GetReleaseTime(const PerPacketOptions* options) const; + + struct QUIC_EXPORT_PRIVATE CanBatchResult { + CanBatchResult(bool can_batch, bool must_flush) + : can_batch(can_batch), must_flush(must_flush) {} + // Whether this write can be batched with existing buffered writes. + bool can_batch; + // If |can_batch|, whether the caller must flush after this packet is + // buffered. + // Always true if not |can_batch|. + bool must_flush; + }; + + // Given the existing buffered writes(in buffered_writes()), whether a new + // write(in the arguments) can be batched. + virtual CanBatchResult CanBatch(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + const PerPacketOptions* options, + uint64_t release_time) const = 0; + + struct QUIC_EXPORT_PRIVATE FlushImplResult { + // The return value of the Flush() interface, which is: + // - WriteResult(WRITE_STATUS_OK, <bytes_flushed>) if all buffered writes + // were sent successfully. + // - WRITE_STATUS_BLOCKED or WRITE_STATUS_ERROR, if the batch write is + // blocked or returned an error while sending. If a portion of buffered + // writes were sent successfully, |FlushImplResult.num_packets_sent| and + // |FlushImplResult.bytes_written| contain the number of successfully sent + // packets and their total bytes. + WriteResult write_result; + int num_packets_sent; + // If write_result.status == WRITE_STATUS_OK, |bytes_written| will be equal + // to write_result.bytes_written. Otherwise |bytes_written| will be the + // number of bytes written before WRITE_BLOCK or WRITE_ERROR happened. + int bytes_written; + }; + + // Send all buffered writes(in buffered_writes()) in a batch. + // buffered_writes() is guaranteed to be non-empty when this function is + // called. + virtual FlushImplResult FlushImpl() = 0; + + private: + WriteResult InternalWritePacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + PerPacketOptions* options); + + // Calls FlushImpl() and check its post condition. + FlushImplResult CheckedFlush(); + + bool write_blocked_; + std::unique_ptr<QuicBatchWriterBuffer> batch_buffer_; +}; + +// QuicUdpBatchWriter is a batch writer backed by a UDP socket. +class QUIC_EXPORT_PRIVATE QuicUdpBatchWriter : public QuicBatchWriterBase { + public: + QuicUdpBatchWriter(std::unique_ptr<QuicBatchWriterBuffer> batch_buffer, + int fd) + : QuicBatchWriterBase(std::move(batch_buffer)), fd_(fd) {} + + int fd() const { return fd_; } + + private: + const int fd_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_PLATFORM_IMPL_BATCH_WRITER_QUIC_BATCH_WRITER_BASE_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.cc b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.cc new file mode 100644 index 00000000000..62261393256 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.cc @@ -0,0 +1,154 @@ +// 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 "net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.h" + +#include <sstream> + +namespace quic { + +QuicBatchWriterBuffer::QuicBatchWriterBuffer() { + memset(buffer_, 0, sizeof(buffer_)); +} + +void QuicBatchWriterBuffer::Clear() { + buffered_writes_.clear(); +} + +std::string QuicBatchWriterBuffer::DebugString() const { + std::ostringstream os; + os << "{ buffer: " << static_cast<const void*>(buffer_) + << " buffer_end: " << static_cast<const void*>(buffer_end()) + << " buffered_writes_.size(): " << buffered_writes_.size() + << " next_write_loc: " << static_cast<const void*>(GetNextWriteLocation()) + << " SizeInUse: " << SizeInUse() << " }"; + return os.str(); +} + +bool QuicBatchWriterBuffer::Invariants() const { + // Buffers in buffered_writes_ should not overlap, and collectively they + // should cover a continuous prefix of buffer_. + const char* next_buffer = buffer_; + for (auto iter = buffered_writes_.begin(); iter != buffered_writes_.end(); + ++iter) { + if ((iter->buffer != next_buffer) || + (iter->buffer + iter->buf_len > buffer_end())) { + return false; + } + next_buffer += iter->buf_len; + } + + return (next_buffer - buffer_) == SizeInUse(); +} + +char* QuicBatchWriterBuffer::GetNextWriteLocation() const { + const char* next_loc = + buffered_writes_.empty() + ? buffer_ + : buffered_writes_.back().buffer + buffered_writes_.back().buf_len; + if (buffer_end() - next_loc < kMaxOutgoingPacketSize) { + return nullptr; + } + return const_cast<char*>(next_loc); +} + +QuicBatchWriterBuffer::PushResult QuicBatchWriterBuffer::PushBufferedWrite( + const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + const PerPacketOptions* options, + uint64_t release_time) { + DCHECK(Invariants()); + DCHECK_LE(buf_len, kMaxOutgoingPacketSize); + + PushResult result = {/*succeeded=*/false, /*buffer_copied=*/false}; + char* next_write_location = GetNextWriteLocation(); + if (next_write_location == nullptr) { + return result; + } + + if (buffer != next_write_location) { + if (IsExternalBuffer(buffer, buf_len)) { + memcpy(next_write_location, buffer, buf_len); + } else if (IsInternalBuffer(buffer, buf_len)) { + memmove(next_write_location, buffer, buf_len); + } else { + QUIC_BUG << "Buffer[" << static_cast<const void*>(buffer) << ", " + << static_cast<const void*>(buffer + buf_len) + << ") overlaps with internal buffer[" + << static_cast<const void*>(buffer_) << ", " + << static_cast<const void*>(buffer_end()) << ")"; + return result; + } + result.buffer_copied = true; + } else { + // In place push, do nothing. + } + buffered_writes_.emplace_back( + next_write_location, buf_len, self_address, peer_address, + options ? options->Clone() : std::unique_ptr<PerPacketOptions>(), + release_time); + + DCHECK(Invariants()); + + result.succeeded = true; + return result; +} + +void QuicBatchWriterBuffer::UndoLastPush() { + if (!buffered_writes_.empty()) { + buffered_writes_.pop_back(); + } +} + +QuicBatchWriterBuffer::PopResult QuicBatchWriterBuffer::PopBufferedWrite( + int32_t num_buffered_writes) { + DCHECK(Invariants()); + DCHECK_GE(num_buffered_writes, 0); + DCHECK_LE(num_buffered_writes, buffered_writes_.size()); + + PopResult result = {/*num_buffers_popped=*/0, + /*moved_remaining_buffers=*/false}; + + result.num_buffers_popped = std::max<int32_t>(num_buffered_writes, 0); + result.num_buffers_popped = + std::min<int32_t>(result.num_buffers_popped, buffered_writes_.size()); + buffered_writes_.pop_front_n(result.num_buffers_popped); + + if (!buffered_writes_.empty()) { + // If not all buffered writes are erased, the remaining ones will not cover + // a continuous prefix of buffer_. We'll fix it by moving the remaining + // buffers to the beginning of buffer_ and adjust the buffer pointers in all + // remaining buffered writes. + // This should happen very rarely, about once per write block. + result.moved_remaining_buffers = true; + const char* buffer_before_move = buffered_writes_.front().buffer; + size_t buffer_len_to_move = buffered_writes_.back().buffer + + buffered_writes_.back().buf_len - + buffer_before_move; + memmove(buffer_, buffer_before_move, buffer_len_to_move); + + size_t distance_to_move = buffer_before_move - buffer_; + for (BufferedWrite& buffered_write : buffered_writes_) { + buffered_write.buffer -= distance_to_move; + } + + DCHECK_EQ(buffer_, buffered_writes_.front().buffer); + } + DCHECK(Invariants()); + + return result; +} + +size_t QuicBatchWriterBuffer::SizeInUse() const { + if (buffered_writes_.empty()) { + return 0; + } + + return buffered_writes_.back().buffer + buffered_writes_.back().buf_len - + buffer_; +} + +} // namespace quic 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 new file mode 100644 index 00000000000..a441ec3c9d6 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.h @@ -0,0 +1,95 @@ +// 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_PLATFORM_IMPL_BATCH_WRITER_QUIC_BATCH_WRITER_BUFFER_H_ +#define QUICHE_QUIC_PLATFORM_IMPL_BATCH_WRITER_QUIC_BATCH_WRITER_BUFFER_H_ + +#include "net/third_party/quiche/src/quic/core/quic_circular_deque.h" +#include "net/third_party/quiche/src/quic/core/quic_linux_socket_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_aligned.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" + +namespace quic { + +// QuicBatchWriterBuffer manages an internal buffer to hold data from multiple +// packets. Packet data are placed continuously within the internal buffer such +// that they can be sent by a QuicGsoBatchWriter. +// This class can also be used by a QuicBatchWriter which uses sendmmsg, +// although it is not optimized for that use case. +class QUIC_EXPORT_PRIVATE QuicBatchWriterBuffer { + public: + QuicBatchWriterBuffer(); + + // Clear all buffered writes, but leave the internal buffer intact. + void Clear(); + + char* GetNextWriteLocation() const; + + // Push a buffered write to the back. + struct QUIC_EXPORT_PRIVATE PushResult { + bool succeeded; + // True in one of the following cases: + // 1) The packet buffer is external and copied to the internal buffer, or + // 2) The packet buffer is from the internal buffer and moved within it. + // This only happens if PopBufferedWrite is called in the middle of a + // in-place push. + // Only valid if |succeeded| is true. + bool buffer_copied; + }; + + PushResult PushBufferedWrite(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + const PerPacketOptions* options, + uint64_t release_time); + + void UndoLastPush(); + + // Pop |num_buffered_writes| buffered writes from the front. + // |num_buffered_writes| will be capped to [0, buffered_writes().size()] + // before it is used. + struct QUIC_EXPORT_PRIVATE PopResult { + int32_t num_buffers_popped; + // True if after |num_buffers_popped| buffers are popped from front, the + // remaining buffers are moved to the beginning of the internal buffer. + // This should normally be false. + bool moved_remaining_buffers; + }; + PopResult PopBufferedWrite(int32_t num_buffered_writes); + + const QuicCircularDeque<BufferedWrite>& buffered_writes() const { + return buffered_writes_; + } + + bool IsExternalBuffer(const char* buffer, size_t buf_len) const { + return (buffer + buf_len) <= buffer_ || buffer >= buffer_end(); + } + bool IsInternalBuffer(const char* buffer, size_t buf_len) const { + return buffer >= buffer_ && (buffer + buf_len) <= buffer_end(); + } + + // Number of bytes used in |buffer_|. + // PushBufferedWrite() increases this; PopBufferedWrite decreases this. + size_t SizeInUse() const; + + // Rounded up from |kMaxGsoPacketSize|, which is the maximum allowed + // size of a GSO packet. + static const size_t kBufferSize = 64 * 1024; + + std::string DebugString() const; + + protected: + // Whether the invariants of the buffer are upheld. For debug & test only. + bool Invariants() const; + const char* const buffer_end() const { return buffer_ + sizeof(buffer_); } + QUIC_CACHELINE_ALIGNED char buffer_[kBufferSize]; + QuicCircularDeque<BufferedWrite> buffered_writes_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_PLATFORM_IMPL_BATCH_WRITER_QUIC_BATCH_WRITER_BUFFER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer_test.cc b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer_test.cc new file mode 100644 index 00000000000..4cdb747ab7c --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer_test.cc @@ -0,0 +1,281 @@ +// 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 "net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.h" +#include <memory> +#include <string> + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +class QUIC_EXPORT_PRIVATE TestQuicBatchWriterBuffer + : public QuicBatchWriterBuffer { + public: + using QuicBatchWriterBuffer::buffer_; + using QuicBatchWriterBuffer::buffered_writes_; +}; + +static const size_t kBatchBufferSize = QuicBatchWriterBuffer::kBufferSize; + +class QuicBatchWriterBufferTest : public QuicTest { + public: + QuicBatchWriterBufferTest() { SwitchToNewBuffer(); } + + void SwitchToNewBuffer() { + batch_buffer_ = std::make_unique<TestQuicBatchWriterBuffer>(); + } + + // Fill packet_buffer_ with kMaxOutgoingPacketSize bytes of |c|s. + char* FillPacketBuffer(char c) { + return FillPacketBuffer(c, packet_buffer_, kMaxOutgoingPacketSize); + } + + // Fill |packet_buffer| with kMaxOutgoingPacketSize bytes of |c|s. + char* FillPacketBuffer(char c, char* packet_buffer) { + return FillPacketBuffer(c, packet_buffer, kMaxOutgoingPacketSize); + } + + // Fill |packet_buffer| with |buf_len| bytes of |c|s. + char* FillPacketBuffer(char c, char* packet_buffer, size_t buf_len) { + memset(packet_buffer, c, buf_len); + return packet_buffer; + } + + void CheckBufferedWriteContent(int buffered_write_index, + char buffer_content, + size_t buf_len, + const QuicIpAddress& self_addr, + const QuicSocketAddress& peer_addr, + const PerPacketOptions* options) { + const BufferedWrite& buffered_write = + batch_buffer_->buffered_writes()[buffered_write_index]; + EXPECT_EQ(buf_len, buffered_write.buf_len); + for (size_t i = 0; i < buf_len; ++i) { + EXPECT_EQ(buffer_content, buffered_write.buffer[i]); + if (buffer_content != buffered_write.buffer[i]) { + break; + } + } + EXPECT_EQ(self_addr, buffered_write.self_address); + EXPECT_EQ(peer_addr, buffered_write.peer_address); + if (options == nullptr) { + EXPECT_EQ(nullptr, buffered_write.options); + } else { + EXPECT_EQ(options->release_time_delay, + buffered_write.options->release_time_delay); + } + } + + protected: + std::unique_ptr<TestQuicBatchWriterBuffer> batch_buffer_; + QuicIpAddress self_addr_; + QuicSocketAddress peer_addr_; + uint64_t release_time_ = 0; + char packet_buffer_[kMaxOutgoingPacketSize]; +}; + +class BufferSizeSequence { + public: + explicit BufferSizeSequence( + std::vector<std::pair<std::vector<size_t>, size_t>> stages) + : stages_(std::move(stages)), + total_buf_len_(0), + stage_index_(0), + sequence_index_(0) {} + + size_t Next() { + const std::vector<size_t>& seq = stages_[stage_index_].first; + size_t buf_len = seq[sequence_index_++ % seq.size()]; + total_buf_len_ += buf_len; + if (stages_[stage_index_].second <= total_buf_len_) { + stage_index_ = std::min(stage_index_ + 1, stages_.size() - 1); + } + return buf_len; + } + + private: + const std::vector<std::pair<std::vector<size_t>, size_t>> stages_; + size_t total_buf_len_; + size_t stage_index_; + size_t sequence_index_; +}; + +// Test in-place pushes. A in-place push is a push with a buffer address that is +// equal to the result of GetNextWriteLocation(). +TEST_F(QuicBatchWriterBufferTest, InPlacePushes) { + std::vector<BufferSizeSequence> buffer_size_sequences = { + // Push large writes until the buffer is near full, then switch to 1-byte + // writes. This covers the edge cases when detecting insufficient buffer. + BufferSizeSequence({{{1350}, kBatchBufferSize - 3000}, {{1}, 1e6}}), + // A sequence that looks real. + BufferSizeSequence({{{1, 39, 97, 150, 1350, 1350, 1350, 1350}, 1e6}}), + }; + + for (auto& buffer_size_sequence : buffer_size_sequences) { + SwitchToNewBuffer(); + int64_t num_push_failures = 0; + + while (batch_buffer_->SizeInUse() < kBatchBufferSize) { + size_t buf_len = buffer_size_sequence.Next(); + const bool has_enough_space = + (kBatchBufferSize - batch_buffer_->SizeInUse() >= + kMaxOutgoingPacketSize); + + char* buffer = batch_buffer_->GetNextWriteLocation(); + + if (has_enough_space) { + EXPECT_EQ(batch_buffer_->buffer_ + batch_buffer_->SizeInUse(), buffer); + } else { + EXPECT_EQ(nullptr, buffer); + } + + SCOPED_TRACE(testing::Message() + << "Before Push: buf_len=" << buf_len + << ", has_enough_space=" << has_enough_space + << ", batch_buffer=" << batch_buffer_->DebugString()); + + auto push_result = batch_buffer_->PushBufferedWrite( + buffer, buf_len, self_addr_, peer_addr_, nullptr, release_time_); + if (!push_result.succeeded) { + ++num_push_failures; + } + EXPECT_EQ(has_enough_space, push_result.succeeded); + EXPECT_FALSE(push_result.buffer_copied); + if (!has_enough_space) { + break; + } + } + // Expect one and only one failure from the final push operation. + EXPECT_EQ(1, num_push_failures); + } +} + +// Test some in-place pushes mixed with pushes with external buffers. +TEST_F(QuicBatchWriterBufferTest, MixedPushes) { + // First, a in-place push. + char* buffer = batch_buffer_->GetNextWriteLocation(); + auto push_result = batch_buffer_->PushBufferedWrite( + FillPacketBuffer('A', buffer), kDefaultMaxPacketSize, self_addr_, + peer_addr_, nullptr, release_time_); + EXPECT_TRUE(push_result.succeeded); + EXPECT_FALSE(push_result.buffer_copied); + CheckBufferedWriteContent(0, 'A', kDefaultMaxPacketSize, self_addr_, + peer_addr_, nullptr); + + // Then a push with external buffer. + push_result = batch_buffer_->PushBufferedWrite( + FillPacketBuffer('B'), kDefaultMaxPacketSize, self_addr_, peer_addr_, + nullptr, release_time_); + EXPECT_TRUE(push_result.succeeded); + EXPECT_TRUE(push_result.buffer_copied); + CheckBufferedWriteContent(1, 'B', kDefaultMaxPacketSize, self_addr_, + peer_addr_, nullptr); + + // Then another in-place push. + buffer = batch_buffer_->GetNextWriteLocation(); + push_result = batch_buffer_->PushBufferedWrite( + FillPacketBuffer('C', buffer), kDefaultMaxPacketSize, self_addr_, + peer_addr_, nullptr, release_time_); + EXPECT_TRUE(push_result.succeeded); + EXPECT_FALSE(push_result.buffer_copied); + CheckBufferedWriteContent(2, 'C', kDefaultMaxPacketSize, self_addr_, + peer_addr_, nullptr); + + // Then another push with external buffer. + push_result = batch_buffer_->PushBufferedWrite( + FillPacketBuffer('D'), kDefaultMaxPacketSize, self_addr_, peer_addr_, + nullptr, release_time_); + EXPECT_TRUE(push_result.succeeded); + EXPECT_TRUE(push_result.buffer_copied); + CheckBufferedWriteContent(3, 'D', kDefaultMaxPacketSize, self_addr_, + peer_addr_, nullptr); +} + +TEST_F(QuicBatchWriterBufferTest, PopAll) { + const int kNumBufferedWrites = 10; + for (int i = 0; i < kNumBufferedWrites; ++i) { + EXPECT_TRUE(batch_buffer_ + ->PushBufferedWrite(packet_buffer_, kDefaultMaxPacketSize, + self_addr_, peer_addr_, nullptr, + release_time_) + .succeeded); + } + EXPECT_EQ(kNumBufferedWrites, batch_buffer_->buffered_writes().size()); + + auto pop_result = batch_buffer_->PopBufferedWrite(kNumBufferedWrites); + EXPECT_EQ(0, batch_buffer_->buffered_writes().size()); + EXPECT_EQ(kNumBufferedWrites, pop_result.num_buffers_popped); + EXPECT_FALSE(pop_result.moved_remaining_buffers); +} + +TEST_F(QuicBatchWriterBufferTest, PopPartial) { + const int kNumBufferedWrites = 10; + for (int i = 0; i < kNumBufferedWrites; ++i) { + EXPECT_TRUE(batch_buffer_ + ->PushBufferedWrite(FillPacketBuffer('A' + i), + kDefaultMaxPacketSize - i, self_addr_, + peer_addr_, nullptr, release_time_) + .succeeded); + } + + for (int i = 0; + i < kNumBufferedWrites && !batch_buffer_->buffered_writes().empty(); + ++i) { + const size_t size_before_pop = batch_buffer_->buffered_writes().size(); + const size_t expect_size_after_pop = + size_before_pop < i ? 0 : size_before_pop - i; + batch_buffer_->PopBufferedWrite(i); + ASSERT_EQ(expect_size_after_pop, batch_buffer_->buffered_writes().size()); + const char first_write_content = + 'A' + kNumBufferedWrites - expect_size_after_pop; + const size_t first_write_len = + kDefaultMaxPacketSize - kNumBufferedWrites + expect_size_after_pop; + for (int j = 0; j < expect_size_after_pop; ++j) { + CheckBufferedWriteContent(j, first_write_content + j, first_write_len - j, + self_addr_, peer_addr_, nullptr); + } + } +} + +TEST_F(QuicBatchWriterBufferTest, InPlacePushWithPops) { + // First, a in-place push. + char* buffer = batch_buffer_->GetNextWriteLocation(); + const size_t first_packet_len = 2; + auto push_result = batch_buffer_->PushBufferedWrite( + FillPacketBuffer('A', buffer, first_packet_len), first_packet_len, + self_addr_, peer_addr_, nullptr, release_time_); + EXPECT_TRUE(push_result.succeeded); + EXPECT_FALSE(push_result.buffer_copied); + CheckBufferedWriteContent(0, 'A', first_packet_len, self_addr_, peer_addr_, + nullptr); + + // Simulate the case where the writer wants to do another in-place push, but + // can't do so because it can't be batched with the first buffer. + buffer = batch_buffer_->GetNextWriteLocation(); + const size_t second_packet_len = 1350; + + // Flush the first buffer. + auto pop_result = batch_buffer_->PopBufferedWrite(1); + EXPECT_EQ(1, pop_result.num_buffers_popped); + EXPECT_FALSE(pop_result.moved_remaining_buffers); + + // Now the second push. + push_result = batch_buffer_->PushBufferedWrite( + FillPacketBuffer('B', buffer, second_packet_len), second_packet_len, + self_addr_, peer_addr_, nullptr, release_time_); + EXPECT_TRUE(push_result.succeeded); + EXPECT_TRUE(push_result.buffer_copied); + CheckBufferedWriteContent(0, 'B', second_packet_len, self_addr_, peer_addr_, + nullptr); +} + +} // namespace +} // namespace test +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_test.cc b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_test.cc new file mode 100644 index 00000000000..583b2485c4c --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_test.cc @@ -0,0 +1,78 @@ +// 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 "net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_test.h" +#include "net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer.h" +#include "net/third_party/quiche/src/quic/core/batch_writer/quic_sendmmsg_batch_writer.h" + +namespace quic { +namespace test { +namespace { + +class QuicGsoBatchWriterIOTestDelegate + : public QuicUdpBatchWriterIOTestDelegate { + public: + bool ShouldSkip(const QuicUdpBatchWriterIOTestParams& params) override { + QuicUdpSocketApi socket_api; + int fd = + socket_api.Create(params.address_family, + /*receive_buffer_size=*/kDefaultSocketReceiveBuffer, + /*send_buffer_size=*/kDefaultSocketReceiveBuffer); + if (fd < 0) { + QUIC_LOG(ERROR) << "CreateSocket() failed: " << strerror(errno); + return false; // Let the test fail rather than skip it. + } + const bool gso_not_supported = + QuicLinuxSocketUtils::GetUDPSegmentSize(fd) < 0; + socket_api.Destroy(fd); + + if (gso_not_supported) { + QUIC_LOG(WARNING) << "Test skipped since GSO is not supported."; + return true; + } + + QUIC_LOG(WARNING) << "OK: GSO is supported."; + return false; + } + + void ResetWriter(int fd) override { + writer_ = std::make_unique<QuicGsoBatchWriter>( + std::make_unique<QuicBatchWriterBuffer>(), fd); + } + + QuicUdpBatchWriter* GetWriter() override { return writer_.get(); } + + private: + std::unique_ptr<QuicGsoBatchWriter> writer_; +}; + +INSTANTIATE_TEST_SUITE_P( + QuicGsoBatchWriterTest, + QuicUdpBatchWriterIOTest, + testing::ValuesIn( + MakeQuicBatchWriterTestParams<QuicGsoBatchWriterIOTestDelegate>())); + +class QuicSendmmsgBatchWriterIOTestDelegate + : public QuicUdpBatchWriterIOTestDelegate { + public: + void ResetWriter(int fd) override { + writer_ = std::make_unique<QuicSendmmsgBatchWriter>( + std::make_unique<QuicBatchWriterBuffer>(), fd); + } + + QuicUdpBatchWriter* GetWriter() override { return writer_.get(); } + + private: + std::unique_ptr<QuicSendmmsgBatchWriter> writer_; +}; + +INSTANTIATE_TEST_SUITE_P( + QuicSendmmsgBatchWriterTest, + QuicUdpBatchWriterIOTest, + testing::ValuesIn(MakeQuicBatchWriterTestParams< + QuicSendmmsgBatchWriterIOTestDelegate>())); + +} // namespace +} // namespace test +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_test.h b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_test.h new file mode 100644 index 00000000000..b4f36b9f3ca --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_test.h @@ -0,0 +1,284 @@ +// 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_PLATFORM_IMPL_BATCH_WRITER_QUIC_BATCH_WRITER_TEST_H_ +#define QUICHE_QUIC_PLATFORM_IMPL_BATCH_WRITER_QUIC_BATCH_WRITER_TEST_H_ + +#include <sys/socket.h> +#include <sys/types.h> + +#include <iostream> +#include <utility> + +#include "net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.h" +#include "net/third_party/quiche/src/quic/core/quic_udp_socket.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { + +static bool IsAddressFamilySupported(int address_family) { + static auto check_function = [](int address_family) { + int fd = socket(address_family, SOCK_STREAM, 0); + if (fd < 0) { + QUIC_LOG(ERROR) << "address_family not supported: " << address_family + << ", error: " << strerror(errno); + EXPECT_EQ(EAFNOSUPPORT, errno); + return false; + } + close(fd); + return true; + }; + + if (address_family == AF_INET) { + static const bool ipv4_supported = check_function(AF_INET); + return ipv4_supported; + } + + static const bool ipv6_supported = check_function(AF_INET6); + return ipv6_supported; +} + +static bool CreateSocket(int family, QuicSocketAddress* address, int* fd) { + if (family == AF_INET) { + *address = QuicSocketAddress(QuicIpAddress::Loopback4(), 0); + } else { + DCHECK_EQ(family, AF_INET6); + *address = QuicSocketAddress(QuicIpAddress::Loopback6(), 0); + } + + QuicUdpSocketApi socket_api; + *fd = socket_api.Create(family, + /*receive_buffer_size=*/kDefaultSocketReceiveBuffer, + /*send_buffer_size=*/kDefaultSocketReceiveBuffer); + if (*fd < 0) { + QUIC_LOG(ERROR) << "CreateSocket() failed: " << strerror(errno); + return false; + } + socket_api.EnableDroppedPacketCount(*fd); + + if (!socket_api.Bind(*fd, *address)) { + QUIC_LOG(ERROR) << "Bind failed: " << strerror(errno); + return false; + } + + if (address->FromSocket(*fd) != 0) { + QUIC_LOG(ERROR) << "Unable to get self address. Error: " + << strerror(errno); + return false; + } + return true; +} + +struct QuicUdpBatchWriterIOTestParams; +class QUIC_EXPORT_PRIVATE QuicUdpBatchWriterIOTestDelegate { + public: + virtual ~QuicUdpBatchWriterIOTestDelegate() {} + + virtual bool ShouldSkip(const QuicUdpBatchWriterIOTestParams& params) { + return false; + } + + virtual void ResetWriter(int fd) = 0; + + virtual QuicUdpBatchWriter* GetWriter() = 0; +}; + +struct QUIC_EXPORT_PRIVATE QuicUdpBatchWriterIOTestParams { + // Use shared_ptr because gtest makes copies of test params. + std::shared_ptr<QuicUdpBatchWriterIOTestDelegate> delegate; + int address_family; + int data_size; + int packet_size; + + QUIC_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, + const QuicUdpBatchWriterIOTestParams& p) { + os << "{ address_family: " << p.address_family + << " data_size: " << p.data_size << " packet_size: " << p.packet_size + << " }"; + return os; + } +}; + +template <class QuicUdpBatchWriterIOTestDelegateT> +static std::vector<QuicUdpBatchWriterIOTestParams> +MakeQuicBatchWriterTestParams() { + static_assert(std::is_base_of<QuicUdpBatchWriterIOTestDelegate, + QuicUdpBatchWriterIOTestDelegateT>::value, + "<QuicUdpBatchWriterIOTestDelegateT> needs to derive from " + "QuicUdpBatchWriterIOTestDelegate"); + + std::vector<QuicUdpBatchWriterIOTestParams> params; + for (int address_family : {AF_INET, AF_INET6}) { + for (int data_size : {1, 150, 1500, 15000, 64000, 512 * 1024}) { + for (int packet_size : {1, 50, 1350, 1452}) { + if (packet_size <= data_size && (data_size / packet_size < 2000)) { + params.push_back( + {std::make_unique<QuicUdpBatchWriterIOTestDelegateT>(), + address_family, data_size, packet_size}); + } + } + } + } + return params; +} + +// QuicUdpBatchWriterIOTest is a value parameterized test fixture that can be +// used by tests of derived classes of QuicUdpBatchWriter, to verify basic +// packet IO capabilities. +class QUIC_EXPORT_PRIVATE QuicUdpBatchWriterIOTest + : public QuicTestWithParam<QuicUdpBatchWriterIOTestParams> { + protected: + QuicUdpBatchWriterIOTest() + : address_family_(GetParam().address_family), + data_size_(GetParam().data_size), + packet_size_(GetParam().packet_size), + self_socket_(-1), + peer_socket_(-1) { + QUIC_LOG(INFO) << "QuicUdpBatchWriterIOTestParams: " << GetParam(); + EXPECT_TRUE(address_family_ == AF_INET || address_family_ == AF_INET6); + EXPECT_LE(packet_size_, data_size_); + EXPECT_LE(packet_size_, sizeof(packet_buffer_)); + } + + ~QuicUdpBatchWriterIOTest() override { + if (self_socket_ > 0) { + close(self_socket_); + } + if (peer_socket_ > 0) { + close(peer_socket_); + } + } + + // Whether this test should be skipped. A test is passed if skipped. + // A test can be skipped when e.g. it exercises a kernel feature that is not + // available on the system. + bool ShouldSkip() { + if (!IsAddressFamilySupported(address_family_)) { + QUIC_LOG(WARNING) + << "Test skipped since address_family is not supported."; + return true; + } + + return GetParam().delegate->ShouldSkip(GetParam()); + } + + // Initialize a test. + // To fail the test in Initialize, use ASSERT_xx macros. + void Initialize() { + ASSERT_TRUE(CreateSocket(address_family_, &self_address_, &self_socket_)); + ASSERT_TRUE(CreateSocket(address_family_, &peer_address_, &peer_socket_)); + + QUIC_DLOG(INFO) << "Self address: " << self_address_.ToString() << ", fd " + << self_socket_; + QUIC_DLOG(INFO) << "Peer address: " << peer_address_.ToString() << ", fd " + << peer_socket_; + GetParam().delegate->ResetWriter(self_socket_); + } + + QuicUdpBatchWriter* GetWriter() { return GetParam().delegate->GetWriter(); } + + void ValidateWrite() { + char this_packet_content = '\0'; + int this_packet_size; + int num_writes = 0; + int bytes_flushed = 0; + WriteResult result; + + for (int bytes_sent = 0; bytes_sent < data_size_; + bytes_sent += this_packet_size, ++this_packet_content) { + this_packet_size = std::min(packet_size_, data_size_ - bytes_sent); + memset(&packet_buffer_[0], this_packet_content, this_packet_size); + + result = GetWriter()->WritePacket(&packet_buffer_[0], this_packet_size, + self_address_.host(), peer_address_, + nullptr); + + ASSERT_EQ(WRITE_STATUS_OK, result.status) << strerror(result.error_code); + bytes_flushed += result.bytes_written; + ++num_writes; + + QUIC_DVLOG(1) << "[write #" << num_writes + << "] this_packet_size: " << this_packet_size + << ", total_bytes_sent: " << bytes_sent + this_packet_size + << ", bytes_flushed: " << bytes_flushed + << ", pkt content:" << std::hex << int(this_packet_content); + } + + result = GetWriter()->Flush(); + ASSERT_EQ(WRITE_STATUS_OK, result.status) << strerror(result.error_code); + bytes_flushed += result.bytes_written; + ASSERT_EQ(data_size_, bytes_flushed); + + QUIC_LOG(INFO) << "Sent " << data_size_ << " bytes in " << num_writes + << " writes."; + } + + void ValidateRead() { + char this_packet_content = '\0'; + int this_packet_size; + int packets_received = 0; + for (int bytes_received = 0; bytes_received < data_size_; + bytes_received += this_packet_size, ++this_packet_content) { + this_packet_size = std::min(packet_size_, data_size_ - bytes_received); + SCOPED_TRACE(testing::Message() + << "Before ReadPacket: bytes_received=" << bytes_received + << ", this_packet_size=" << this_packet_size); + + QuicUdpSocketApi::ReadPacketResult result; + result.packet_buffer = {&packet_buffer_[0], sizeof(packet_buffer_)}; + result.control_buffer = {&control_buffer_[0], sizeof(control_buffer_)}; + QuicUdpSocketApi().ReadPacket( + peer_socket_, + quic::BitMask64(QuicUdpPacketInfoBit::V4_SELF_IP, + QuicUdpPacketInfoBit::V6_SELF_IP, + QuicUdpPacketInfoBit::PEER_ADDRESS), + &result); + ASSERT_TRUE(result.ok); + ASSERT_TRUE( + result.packet_info.HasValue(QuicUdpPacketInfoBit::PEER_ADDRESS)); + QuicSocketAddress read_peer_address = result.packet_info.peer_address(); + QuicIpAddress read_self_address = read_peer_address.host().IsIPv6() + ? result.packet_info.self_v6_ip() + : result.packet_info.self_v4_ip(); + + EXPECT_EQ(read_self_address, peer_address_.host()); + EXPECT_EQ(read_peer_address, self_address_); + for (int i = 0; i < this_packet_size; ++i) { + EXPECT_EQ(this_packet_content, packet_buffer_[i]); + } + packets_received += this_packet_size; + } + + QUIC_LOG(INFO) << "Received " << data_size_ << " bytes in " + << packets_received << " packets."; + } + + QuicSocketAddress self_address_; + QuicSocketAddress peer_address_; + char packet_buffer_[1500]; + char control_buffer_[kDefaultUdpPacketControlBufferSize]; + int address_family_; + const int data_size_; + const int packet_size_; + int self_socket_; + int peer_socket_; +}; + +TEST_P(QuicUdpBatchWriterIOTest, WriteAndRead) { + if (ShouldSkip()) { + return; + } + + Initialize(); + + ValidateWrite(); + ValidateRead(); +} + +} // namespace test +} // namespace quic + +#endif // QUICHE_QUIC_PLATFORM_IMPL_BATCH_WRITER_QUIC_BATCH_WRITER_TEST_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer.cc b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer.cc new file mode 100644 index 00000000000..56c0e10171f --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer.cc @@ -0,0 +1,122 @@ +// 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 "net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer.h" + +#include <time.h> +#include <ctime> + +#include "net/third_party/quiche/src/quic/core/quic_linux_socket_utils.h" + +namespace quic { + +QuicGsoBatchWriter::QuicGsoBatchWriter( + std::unique_ptr<QuicBatchWriterBuffer> batch_buffer, + int fd) + : QuicGsoBatchWriter(std::move(batch_buffer), fd, CLOCK_MONOTONIC) {} + +QuicGsoBatchWriter::QuicGsoBatchWriter( + std::unique_ptr<QuicBatchWriterBuffer> batch_buffer, + int fd, + clockid_t clockid_for_release_time) + : QuicUdpBatchWriter(std::move(batch_buffer), fd), + clockid_for_release_time_(clockid_for_release_time), + supports_release_time_( + GetQuicRestartFlag(quic_support_release_time_for_gso) && + QuicLinuxSocketUtils::EnableReleaseTime(fd, + clockid_for_release_time)) { + if (supports_release_time_) { + QUIC_RESTART_FLAG_COUNT(quic_support_release_time_for_gso); + QUIC_LOG_FIRST_N(INFO, 5) << "Release time is enabled."; + } else { + QUIC_LOG_FIRST_N(INFO, 5) << "Release time is not enabled."; + } +} + +QuicGsoBatchWriter::QuicGsoBatchWriter( + std::unique_ptr<QuicBatchWriterBuffer> batch_buffer, + int fd, + clockid_t clockid_for_release_time, + ReleaseTimeForceEnabler enabler) + : QuicUdpBatchWriter(std::move(batch_buffer), fd), + clockid_for_release_time_(clockid_for_release_time), + supports_release_time_(true) { + QUIC_DLOG(INFO) << "Release time forcefully enabled."; +} + +QuicGsoBatchWriter::CanBatchResult QuicGsoBatchWriter::CanBatch( + const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + const PerPacketOptions* options, + uint64_t release_time) const { + // If there is nothing buffered already, this write will be included in this + // batch. + if (buffered_writes().empty()) { + return CanBatchResult(/*can_batch=*/true, /*must_flush=*/false); + } + + // The new write can be batched if all of the following are true: + // [0] The total number of the GSO segments(one write=one segment, including + // the new write) must not exceed |max_segments|. + // [1] It has the same source and destination addresses as already buffered + // writes. + // [2] It won't cause this batch to exceed kMaxGsoPacketSize. + // [3] Already buffered writes all have the same length. + // [4] Length of already buffered writes must >= length of the new write. + // [5] The new packet has the same release time as buffered writes. + const BufferedWrite& first = buffered_writes().front(); + const BufferedWrite& last = buffered_writes().back(); + size_t max_segments = MaxSegments(first.buf_len); + bool can_batch = + buffered_writes().size() < max_segments && // [0] + last.self_address == self_address && // [1] + last.peer_address == peer_address && // [1] + batch_buffer().SizeInUse() + buf_len <= kMaxGsoPacketSize && // [2] + first.buf_len == last.buf_len && // [3] + first.buf_len >= buf_len && // [4] + (!SupportsReleaseTime() || first.release_time == release_time); // [5] + + // A flush is required if any of the following is true: + // [a] The new write can't be batched. + // [b] Length of the new write is different from the length of already + // buffered writes. + // [c] The total number of the GSO segments, including the new write, reaches + // |max_segments|. + bool must_flush = (!can_batch) || // [a] + (last.buf_len != buf_len) || // [b] + (buffered_writes().size() + 1 == max_segments); // [c] + return CanBatchResult(can_batch, must_flush); +} + +uint64_t QuicGsoBatchWriter::NowInNanosForReleaseTime() const { + struct timespec ts; + + if (clock_gettime(clockid_for_release_time_, &ts) != 0) { + return 0; + } + + return ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec; +} + +// static +void QuicGsoBatchWriter::BuildCmsg(QuicMsgHdr* hdr, + const QuicIpAddress& self_address, + uint16_t gso_size, + uint64_t release_time) { + hdr->SetIpInNextCmsg(self_address); + if (gso_size > 0) { + *hdr->GetNextCmsgData<uint16_t>(SOL_UDP, UDP_SEGMENT) = gso_size; + } + if (release_time != 0) { + *hdr->GetNextCmsgData<uint64_t>(SOL_SOCKET, SO_TXTIME) = release_time; + } +} + +QuicGsoBatchWriter::FlushImplResult QuicGsoBatchWriter::FlushImpl() { + return InternalFlushImpl<kCmsgSpace>(BuildCmsg); +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer.h b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer.h new file mode 100644 index 00000000000..64c7e02fdd4 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer.h @@ -0,0 +1,114 @@ +// 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_PLATFORM_IMPL_BATCH_WRITER_QUIC_GSO_BATCH_WRITER_H_ +#define QUICHE_QUIC_PLATFORM_IMPL_BATCH_WRITER_QUIC_GSO_BATCH_WRITER_H_ + +#include "net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.h" + +namespace quic { + +// QuicGsoBatchWriter sends QUIC packets in batches, using UDP socket's generic +// segmentation offload(GSO) capability. +class QUIC_EXPORT_PRIVATE QuicGsoBatchWriter : public QuicUdpBatchWriter { + public: + QuicGsoBatchWriter(std::unique_ptr<QuicBatchWriterBuffer> batch_buffer, + int fd); + + // |clockid_for_release_time|: FQ qdisc requires CLOCK_MONOTONIC, EDF requires + // CLOCK_TAI. + QuicGsoBatchWriter(std::unique_ptr<QuicBatchWriterBuffer> batch_buffer, + int fd, + clockid_t clockid_for_release_time); + + bool SupportsReleaseTime() const final { return supports_release_time_; } + + CanBatchResult CanBatch(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + const PerPacketOptions* options, + uint64_t release_time) const override; + + FlushImplResult FlushImpl() override; + + protected: + // Test only constructor to forcefully enable release time. + struct QUIC_EXPORT_PRIVATE ReleaseTimeForceEnabler {}; + QuicGsoBatchWriter(std::unique_ptr<QuicBatchWriterBuffer> batch_buffer, + int fd, + clockid_t clockid_for_release_time, + ReleaseTimeForceEnabler enabler); + + uint64_t NowInNanosForReleaseTime() const override; + + static size_t MaxSegments(size_t gso_size) { + // Max segments should be the min of UDP_MAX_SEGMENTS(64) and + // (((64KB - sizeof(ip hdr) - sizeof(udp hdr)) / MSS) + 1), in the typical + // case of IPv6 packets with 1500-byte MTU, the result is + // ((64KB - 40 - 8) / (1500 - 48)) + 1 = 46 + // However, due a kernel bug, the limit is much lower for tiny gso_sizes. + return gso_size <= 2 ? 16 : 45; + } + + static const int kCmsgSpace = + kCmsgSpaceForIp + kCmsgSpaceForSegmentSize + kCmsgSpaceForTxTime; + static void BuildCmsg(QuicMsgHdr* hdr, + const QuicIpAddress& self_address, + uint16_t gso_size, + uint64_t release_time); + + template <size_t CmsgSpace, typename CmsgBuilderT> + FlushImplResult InternalFlushImpl(CmsgBuilderT cmsg_builder) { + DCHECK(!IsWriteBlocked()); + DCHECK(!buffered_writes().empty()); + + FlushImplResult result = {WriteResult(WRITE_STATUS_OK, 0), + /*num_packets_sent=*/0, /*bytes_written=*/0}; + WriteResult& write_result = result.write_result; + + int total_bytes = batch_buffer().SizeInUse(); + const BufferedWrite& first = buffered_writes().front(); + char cbuf[CmsgSpace]; + QuicMsgHdr hdr(first.buffer, total_bytes, first.peer_address, cbuf, + sizeof(cbuf)); + + uint16_t gso_size = buffered_writes().size() > 1 ? first.buf_len : 0; + cmsg_builder(&hdr, first.self_address, gso_size, first.release_time); + + write_result = QuicLinuxSocketUtils::WritePacket(fd(), hdr); + QUIC_DVLOG(1) << "Write GSO packet result: " << write_result + << ", fd: " << fd() + << ", self_address: " << first.self_address.ToString() + << ", peer_address: " << first.peer_address.ToString() + << ", num_segments: " << buffered_writes().size() + << ", total_bytes: " << total_bytes + << ", gso_size: " << gso_size; + + // All segments in a GSO packet share the same fate - if the write failed, + // none of them are sent, and it's not needed to call PopBufferedWrite(). + if (write_result.status != WRITE_STATUS_OK) { + return result; + } + + result.num_packets_sent = buffered_writes().size(); + + write_result.bytes_written = total_bytes; + result.bytes_written = total_bytes; + + batch_buffer().PopBufferedWrite(buffered_writes().size()); + + QUIC_BUG_IF(!buffered_writes().empty()) + << "All packets should have been written on a successful return"; + return result; + } + + private: + const clockid_t clockid_for_release_time_; + const bool supports_release_time_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_PLATFORM_IMPL_BATCH_WRITER_QUIC_GSO_BATCH_WRITER_H_ 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 new file mode 100644 index 00000000000..c7d695ab4f5 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer_test.cc @@ -0,0 +1,461 @@ +// 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 "net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer.h" + +#include <cstdint> +#include <limits> +#include <memory> +#include <utility> + +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_mock_syscall_wrapper.h" + +using testing::_; +using testing::Invoke; +using testing::StrictMock; + +namespace quic { +namespace test { +namespace { + +size_t PacketLength(const msghdr* msg) { + size_t length = 0; + for (size_t i = 0; i < msg->msg_iovlen; ++i) { + length += msg->msg_iov[i].iov_len; + } + return length; +} + +uint64_t MillisToNanos(uint64_t milliseconds) { + return milliseconds * 1000000; +} + +class QUIC_EXPORT_PRIVATE TestQuicGsoBatchWriter : public QuicGsoBatchWriter { + public: + using QuicGsoBatchWriter::batch_buffer; + using QuicGsoBatchWriter::buffered_writes; + using QuicGsoBatchWriter::CanBatch; + using QuicGsoBatchWriter::CanBatchResult; + using QuicGsoBatchWriter::GetReleaseTime; + using QuicGsoBatchWriter::MaxSegments; + using QuicGsoBatchWriter::QuicGsoBatchWriter; + + static std::unique_ptr<TestQuicGsoBatchWriter> + NewInstanceWithReleaseTimeSupport() { + return std::unique_ptr<TestQuicGsoBatchWriter>(new TestQuicGsoBatchWriter( + std::make_unique<QuicBatchWriterBuffer>(), + /*fd=*/-1, CLOCK_MONOTONIC, ReleaseTimeForceEnabler())); + } + + uint64_t NowInNanosForReleaseTime() const override { + return MillisToNanos(forced_release_time_ms_); + } + + void ForceReleaseTimeMs(uint64_t forced_release_time_ms) { + forced_release_time_ms_ = forced_release_time_ms; + } + + private: + uint64_t forced_release_time_ms_ = 1; +}; + +struct QUIC_EXPORT_PRIVATE TestPerPacketOptions : public PerPacketOptions { + std::unique_ptr<quic::PerPacketOptions> Clone() const override { + return std::make_unique<TestPerPacketOptions>(*this); + } +}; + +// TestBufferedWrite is a copy-constructible BufferedWrite. +struct QUIC_EXPORT_PRIVATE TestBufferedWrite : public BufferedWrite { + using BufferedWrite::BufferedWrite; + TestBufferedWrite(const TestBufferedWrite& other) + : BufferedWrite(other.buffer, + other.buf_len, + other.self_address, + other.peer_address, + other.options ? other.options->Clone() + : std::unique_ptr<PerPacketOptions>(), + other.release_time) {} +}; + +// Pointed to by all instances of |BatchCriteriaTestData|. Content not used. +static char unused_packet_buffer[kMaxOutgoingPacketSize]; + +struct QUIC_EXPORT_PRIVATE BatchCriteriaTestData { + BatchCriteriaTestData(size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + uint64_t release_time, + bool can_batch, + bool must_flush) + : buffered_write(unused_packet_buffer, + buf_len, + self_address, + peer_address, + std::unique_ptr<PerPacketOptions>(), + release_time), + can_batch(can_batch), + must_flush(must_flush) {} + + TestBufferedWrite buffered_write; + // Expected value of CanBatchResult.can_batch when batching |buffered_write|. + bool can_batch; + // Expected value of CanBatchResult.must_flush when batching |buffered_write|. + bool must_flush; +}; + +std::vector<BatchCriteriaTestData> BatchCriteriaTestData_SizeDecrease() { + const QuicIpAddress self_addr; + const QuicSocketAddress peer_addr; + std::vector<BatchCriteriaTestData> test_data_table = { + // clang-format off + // buf_len self_addr peer_addr t_rel can_batch must_flush + {1350, self_addr, peer_addr, 0, true, false}, + {1350, self_addr, peer_addr, 0, true, false}, + {1350, self_addr, peer_addr, 0, true, false}, + {39, self_addr, peer_addr, 0, true, true}, + {39, self_addr, peer_addr, 0, false, true}, + {1350, self_addr, peer_addr, 0, false, true}, + // clang-format on + }; + return test_data_table; +} + +std::vector<BatchCriteriaTestData> BatchCriteriaTestData_SizeIncrease() { + const QuicIpAddress self_addr; + const QuicSocketAddress peer_addr; + std::vector<BatchCriteriaTestData> test_data_table = { + // clang-format off + // buf_len self_addr peer_addr t_rel can_batch must_flush + {1350, self_addr, peer_addr, 0, true, false}, + {1350, self_addr, peer_addr, 0, true, false}, + {1350, self_addr, peer_addr, 0, true, false}, + {1351, self_addr, peer_addr, 0, false, true}, + // clang-format on + }; + return test_data_table; +} + +std::vector<BatchCriteriaTestData> BatchCriteriaTestData_AddressChange() { + const QuicIpAddress self_addr1 = QuicIpAddress::Loopback4(); + const QuicIpAddress self_addr2 = QuicIpAddress::Loopback6(); + const QuicSocketAddress peer_addr1(self_addr1, 666); + const QuicSocketAddress peer_addr2(self_addr1, 777); + const QuicSocketAddress peer_addr3(self_addr2, 666); + const QuicSocketAddress peer_addr4(self_addr2, 777); + std::vector<BatchCriteriaTestData> test_data_table = { + // clang-format off + // buf_len self_addr peer_addr t_rel can_batch must_flush + {1350, self_addr1, peer_addr1, 0, true, false}, + {1350, self_addr1, peer_addr1, 0, true, false}, + {1350, self_addr1, peer_addr1, 0, true, false}, + {1350, self_addr2, peer_addr1, 0, false, true}, + {1350, self_addr1, peer_addr2, 0, false, true}, + {1350, self_addr1, peer_addr3, 0, false, true}, + {1350, self_addr1, peer_addr4, 0, false, true}, + {1350, self_addr1, peer_addr4, 0, false, true}, + // clang-format on + }; + return test_data_table; +} + +std::vector<BatchCriteriaTestData> BatchCriteriaTestData_ReleaseTime1() { + const QuicIpAddress self_addr; + const QuicSocketAddress peer_addr; + std::vector<BatchCriteriaTestData> test_data_table = { + // clang-format off + // buf_len self_addr peer_addr t_rel can_batch must_flush + {1350, self_addr, peer_addr, 5, true, false}, + {1350, self_addr, peer_addr, 5, true, false}, + {1350, self_addr, peer_addr, 5, true, false}, + {1350, self_addr, peer_addr, 9, false, true}, + // clang-format on + }; + return test_data_table; +} + +std::vector<BatchCriteriaTestData> BatchCriteriaTestData_ReleaseTime2() { + const QuicIpAddress self_addr; + const QuicSocketAddress peer_addr; + std::vector<BatchCriteriaTestData> test_data_table = { + // clang-format off + // buf_len self_addr peer_addr t_rel can_batch must_flush + {1350, self_addr, peer_addr, 0, true, false}, + {1350, self_addr, peer_addr, 0, true, false}, + {1350, self_addr, peer_addr, 0, true, false}, + {1350, self_addr, peer_addr, 9, false, true}, + // clang-format on + }; + return test_data_table; +} + +std::vector<BatchCriteriaTestData> BatchCriteriaTestData_MaxSegments( + size_t gso_size) { + const QuicIpAddress self_addr; + const QuicSocketAddress peer_addr; + std::vector<BatchCriteriaTestData> test_data_table; + size_t max_segments = TestQuicGsoBatchWriter::MaxSegments(gso_size); + for (int i = 0; i < max_segments; ++i) { + bool is_last_in_batch = (i + 1 == max_segments); + test_data_table.push_back({gso_size, self_addr, peer_addr, + /*release_time=*/0, true, is_last_in_batch}); + } + test_data_table.push_back( + {gso_size, self_addr, peer_addr, /*release_time=*/0, false, true}); + return test_data_table; +} + +class QuicGsoBatchWriterTest : public QuicTest { + protected: + WriteResult WritePacket(QuicGsoBatchWriter* writer, size_t packet_size) { + return writer->WritePacket(&packet_buffer_[0], packet_size, self_address_, + peer_address_, nullptr); + } + + WriteResult WritePacketWithOptions(QuicGsoBatchWriter* writer, + PerPacketOptions* options) { + return writer->WritePacket(&packet_buffer_[0], 1350, self_address_, + peer_address_, options); + } + + QuicIpAddress self_address_ = QuicIpAddress::Any4(); + QuicSocketAddress peer_address_{QuicIpAddress::Any4(), 443}; + char packet_buffer_[1500]; + StrictMock<MockQuicSyscallWrapper> mock_syscalls_; + ScopedGlobalSyscallWrapperOverride syscall_override_{&mock_syscalls_}; +}; + +TEST_F(QuicGsoBatchWriterTest, BatchCriteria) { + std::unique_ptr<TestQuicGsoBatchWriter> writer; + + std::vector<std::vector<BatchCriteriaTestData>> test_data_tables; + test_data_tables.emplace_back(BatchCriteriaTestData_SizeDecrease()); + test_data_tables.emplace_back(BatchCriteriaTestData_SizeIncrease()); + test_data_tables.emplace_back(BatchCriteriaTestData_AddressChange()); + test_data_tables.emplace_back(BatchCriteriaTestData_ReleaseTime1()); + test_data_tables.emplace_back(BatchCriteriaTestData_ReleaseTime2()); + test_data_tables.emplace_back(BatchCriteriaTestData_MaxSegments(1)); + test_data_tables.emplace_back(BatchCriteriaTestData_MaxSegments(2)); + test_data_tables.emplace_back(BatchCriteriaTestData_MaxSegments(1350)); + + for (size_t i = 0; i < test_data_tables.size(); ++i) { + writer = TestQuicGsoBatchWriter::NewInstanceWithReleaseTimeSupport(); + + const auto& test_data_table = test_data_tables[i]; + for (size_t j = 0; j < test_data_table.size(); ++j) { + const BatchCriteriaTestData& test_data = test_data_table[j]; + SCOPED_TRACE(testing::Message() << "i=" << i << ", j=" << j); + TestQuicGsoBatchWriter::CanBatchResult result = writer->CanBatch( + test_data.buffered_write.buffer, test_data.buffered_write.buf_len, + test_data.buffered_write.self_address, + test_data.buffered_write.peer_address, + /*options=*/nullptr, test_data.buffered_write.release_time); + + ASSERT_EQ(test_data.can_batch, result.can_batch); + ASSERT_EQ(test_data.must_flush, result.must_flush); + + if (result.can_batch) { + ASSERT_TRUE( + writer->batch_buffer() + .PushBufferedWrite(test_data.buffered_write.buffer, + test_data.buffered_write.buf_len, + test_data.buffered_write.self_address, + test_data.buffered_write.peer_address, + /*options=*/nullptr, + test_data.buffered_write.release_time) + .succeeded); + } + } + } +} + +TEST_F(QuicGsoBatchWriterTest, WriteSuccess) { + TestQuicGsoBatchWriter writer(std::make_unique<QuicBatchWriterBuffer>(), + /*fd=*/-1); + + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 1000)); + + EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _)) + .WillOnce(Invoke([](int sockfd, const msghdr* msg, int flags) { + EXPECT_EQ(1100u, PacketLength(msg)); + return 1100; + })); + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 1100), WritePacket(&writer, 100)); + ASSERT_EQ(0u, writer.batch_buffer().SizeInUse()); + ASSERT_EQ(0u, writer.buffered_writes().size()); +} + +TEST_F(QuicGsoBatchWriterTest, WriteBlockDataNotBuffered) { + TestQuicGsoBatchWriter writer(std::make_unique<QuicBatchWriterBuffer>(), + /*fd=*/-1); + + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100)); + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100)); + + EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _)) + .WillOnce(Invoke([](int sockfd, const msghdr* msg, int flags) { + EXPECT_EQ(200u, PacketLength(msg)); + errno = EWOULDBLOCK; + return -1; + })); + ASSERT_EQ(WriteResult(WRITE_STATUS_BLOCKED, EWOULDBLOCK), + WritePacket(&writer, 150)); + ASSERT_EQ(200u, writer.batch_buffer().SizeInUse()); + ASSERT_EQ(2u, writer.buffered_writes().size()); +} + +TEST_F(QuicGsoBatchWriterTest, WriteBlockDataBuffered) { + TestQuicGsoBatchWriter writer(std::make_unique<QuicBatchWriterBuffer>(), + /*fd=*/-1); + + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100)); + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100)); + + EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _)) + .WillOnce(Invoke([](int sockfd, const msghdr* msg, int flags) { + EXPECT_EQ(250u, PacketLength(msg)); + errno = EWOULDBLOCK; + return -1; + })); + ASSERT_EQ(WriteResult(WRITE_STATUS_BLOCKED_DATA_BUFFERED, EWOULDBLOCK), + WritePacket(&writer, 50)); + ASSERT_EQ(250u, writer.batch_buffer().SizeInUse()); + ASSERT_EQ(3u, writer.buffered_writes().size()); +} + +TEST_F(QuicGsoBatchWriterTest, WriteErrorWithoutDataBuffered) { + TestQuicGsoBatchWriter writer(std::make_unique<QuicBatchWriterBuffer>(), + /*fd=*/-1); + + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100)); + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100)); + + EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _)) + .WillOnce(Invoke([](int sockfd, const msghdr* msg, int flags) { + EXPECT_EQ(200u, PacketLength(msg)); + errno = EPERM; + return -1; + })); + WriteResult error_result = WritePacket(&writer, 150); + ASSERT_EQ(WriteResult(WRITE_STATUS_ERROR, EPERM), error_result); + + ASSERT_EQ(3u, error_result.dropped_packets); + ASSERT_EQ(0u, writer.batch_buffer().SizeInUse()); + ASSERT_EQ(0u, writer.buffered_writes().size()); +} + +TEST_F(QuicGsoBatchWriterTest, WriteErrorAfterDataBuffered) { + TestQuicGsoBatchWriter writer(std::make_unique<QuicBatchWriterBuffer>(), + /*fd=*/-1); + + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100)); + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100)); + + EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _)) + .WillOnce(Invoke([](int sockfd, const msghdr* msg, int flags) { + EXPECT_EQ(250u, PacketLength(msg)); + errno = EPERM; + return -1; + })); + WriteResult error_result = WritePacket(&writer, 50); + ASSERT_EQ(WriteResult(WRITE_STATUS_ERROR, EPERM), error_result); + + ASSERT_EQ(3u, error_result.dropped_packets); + ASSERT_EQ(0u, writer.batch_buffer().SizeInUse()); + ASSERT_EQ(0u, writer.buffered_writes().size()); +} + +TEST_F(QuicGsoBatchWriterTest, FlushError) { + TestQuicGsoBatchWriter writer(std::make_unique<QuicBatchWriterBuffer>(), + /*fd=*/-1); + + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100)); + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100)); + + EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _)) + .WillOnce(Invoke([](int sockfd, const msghdr* msg, int flags) { + EXPECT_EQ(200u, PacketLength(msg)); + errno = EINVAL; + return -1; + })); + WriteResult error_result = writer.Flush(); + ASSERT_EQ(WriteResult(WRITE_STATUS_ERROR, EINVAL), error_result); + + ASSERT_EQ(2u, error_result.dropped_packets); + ASSERT_EQ(0u, writer.batch_buffer().SizeInUse()); + ASSERT_EQ(0u, writer.buffered_writes().size()); +} + +TEST_F(QuicGsoBatchWriterTest, ReleaseTimeNullOptions) { + auto writer = TestQuicGsoBatchWriter::NewInstanceWithReleaseTimeSupport(); + EXPECT_EQ(0, writer->GetReleaseTime(nullptr)); +} + +TEST_F(QuicGsoBatchWriterTest, ReleaseTime) { + const WriteResult write_buffered(WRITE_STATUS_OK, 0); + + auto writer = TestQuicGsoBatchWriter::NewInstanceWithReleaseTimeSupport(); + + TestPerPacketOptions options; + EXPECT_TRUE(options.release_time_delay.IsZero()); + EXPECT_FALSE(options.allow_burst); + EXPECT_EQ(MillisToNanos(1), writer->GetReleaseTime(&options)); + + // The 1st packet has no delay. + ASSERT_EQ(write_buffered, WritePacketWithOptions(writer.get(), &options)); + EXPECT_EQ(MillisToNanos(1), writer->buffered_writes().back().release_time); + + // The 2nd packet has some delay, but allows burst. + options.release_time_delay = QuicTime::Delta::FromMilliseconds(3); + options.allow_burst = true; + ASSERT_EQ(write_buffered, WritePacketWithOptions(writer.get(), &options)); + EXPECT_EQ(MillisToNanos(1), writer->buffered_writes().back().release_time); + + // The 3rd packet has more delay and does not allow burst. + // The first 2 packets are flushed due to different release time. + EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _)) + .WillOnce(Invoke([](int sockfd, const msghdr* msg, int flags) { + EXPECT_EQ(2700u, PacketLength(msg)); + errno = 0; + return 0; + })); + options.release_time_delay = QuicTime::Delta::FromMilliseconds(5); + options.allow_burst = false; + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 2700), + WritePacketWithOptions(writer.get(), &options)); + EXPECT_EQ(MillisToNanos(6), writer->buffered_writes().back().release_time); + + // The 4th packet has same delay, but allows burst. + options.allow_burst = true; + ASSERT_EQ(write_buffered, WritePacketWithOptions(writer.get(), &options)); + EXPECT_EQ(MillisToNanos(6), writer->buffered_writes().back().release_time); + + // The 5th packet has same delay, allows burst, but is shorter. + // Packets 3,4 and 5 are flushed. + EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _)) + .WillOnce(Invoke([](int sockfd, const msghdr* msg, int flags) { + EXPECT_EQ(3000u, PacketLength(msg)); + errno = 0; + return 0; + })); + options.allow_burst = true; + EXPECT_EQ(MillisToNanos(6), writer->GetReleaseTime(&options)); + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 3000), + writer->WritePacket(&packet_buffer_[0], 300, self_address_, + peer_address_, &options)); + EXPECT_TRUE(writer->buffered_writes().empty()); + + // Pretend 1ms has elapsed and the 6th packet has 1ms less delay. In other + // words, the release time should still be the same as packets 3-5. + writer->ForceReleaseTimeMs(2); + options.release_time_delay = QuicTime::Delta::FromMilliseconds(4); + ASSERT_EQ(write_buffered, WritePacketWithOptions(writer.get(), &options)); + EXPECT_EQ(MillisToNanos(6), writer->buffered_writes().back().release_time); +} + +} // namespace +} // namespace test +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_sendmmsg_batch_writer.cc b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_sendmmsg_batch_writer.cc new file mode 100644 index 00000000000..efd21791c4d --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_sendmmsg_batch_writer.cc @@ -0,0 +1,83 @@ +// 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 "net/third_party/quiche/src/quic/core/batch_writer/quic_sendmmsg_batch_writer.h" + +namespace quic { + +QuicSendmmsgBatchWriter::QuicSendmmsgBatchWriter( + std::unique_ptr<QuicBatchWriterBuffer> batch_buffer, + int fd) + : QuicUdpBatchWriter(std::move(batch_buffer), fd) {} + +QuicSendmmsgBatchWriter::CanBatchResult QuicSendmmsgBatchWriter::CanBatch( + const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + const PerPacketOptions* options, + uint64_t release_time) const { + return CanBatchResult(/*can_batch=*/true, /*must_flush=*/false); +} + +QuicSendmmsgBatchWriter::FlushImplResult QuicSendmmsgBatchWriter::FlushImpl() { + return InternalFlushImpl( + kCmsgSpaceForIp, + [](QuicMMsgHdr* mhdr, int i, const BufferedWrite& buffered_write) { + mhdr->SetIpInNextCmsg(i, buffered_write.self_address); + }); +} + +QuicSendmmsgBatchWriter::FlushImplResult +QuicSendmmsgBatchWriter::InternalFlushImpl(size_t cmsg_space, + const CmsgBuilder& cmsg_builder) { + DCHECK(!IsWriteBlocked()); + DCHECK(!buffered_writes().empty()); + + FlushImplResult result = {WriteResult(WRITE_STATUS_OK, 0), + /*num_packets_sent=*/0, /*bytes_written=*/0}; + WriteResult& write_result = result.write_result; + + auto first = buffered_writes().cbegin(); + const auto last = buffered_writes().cend(); + while (first != last) { + QuicMMsgHdr mhdr(first, last, cmsg_space, cmsg_builder); + + int num_packets_sent; + write_result = QuicLinuxSocketUtils::WriteMultiplePackets( + fd(), &mhdr, &num_packets_sent); + QUIC_DVLOG(1) << "WriteMultiplePackets sent " << num_packets_sent + << " out of " << mhdr.num_msgs() + << " packets. WriteResult=" << write_result; + + if (write_result.status != WRITE_STATUS_OK) { + DCHECK_EQ(0, num_packets_sent); + break; + } else if (num_packets_sent == 0) { + QUIC_BUG << "WriteMultiplePackets returned OK, but no packets were sent."; + write_result = WriteResult(WRITE_STATUS_ERROR, EIO); + break; + } + + first += num_packets_sent; + + result.num_packets_sent += num_packets_sent; + result.bytes_written += write_result.bytes_written; + } + + // Call PopBufferedWrite() even if write_result.status is not WRITE_STATUS_OK, + // to deal with partial writes. + batch_buffer().PopBufferedWrite(result.num_packets_sent); + + if (write_result.status != WRITE_STATUS_OK) { + return result; + } + + QUIC_BUG_IF(!buffered_writes().empty()) + << "All packets should have been written on a successful return"; + write_result.bytes_written = result.bytes_written; + return result; +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_sendmmsg_batch_writer.h b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_sendmmsg_batch_writer.h new file mode 100644 index 00000000000..26a728e676a --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_sendmmsg_batch_writer.h @@ -0,0 +1,35 @@ +// 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_PLATFORM_IMPL_QUIC_SENDMMSG_BATCH_WRITER_H_ +#define QUICHE_QUIC_PLATFORM_IMPL_QUIC_SENDMMSG_BATCH_WRITER_H_ + +#include "net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.h" +#include "net/third_party/quiche/src/quic/core/quic_linux_socket_utils.h" + +namespace quic { + +class QUIC_EXPORT_PRIVATE QuicSendmmsgBatchWriter : public QuicUdpBatchWriter { + public: + QuicSendmmsgBatchWriter(std::unique_ptr<QuicBatchWriterBuffer> batch_buffer, + int fd); + + CanBatchResult CanBatch(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + const PerPacketOptions* options, + uint64_t release_time) const override; + + FlushImplResult FlushImpl() override; + + protected: + typedef QuicMMsgHdr::ControlBufferInitializer CmsgBuilder; + FlushImplResult InternalFlushImpl(size_t cmsg_space, + const CmsgBuilder& cmsg_builder); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_PLATFORM_IMPL_QUIC_SENDMMSG_BATCH_WRITER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_sendmmsg_batch_writer_test.cc b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_sendmmsg_batch_writer_test.cc new file mode 100644 index 00000000000..4dcc33c91d3 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_sendmmsg_batch_writer_test.cc @@ -0,0 +1,15 @@ +// 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 "net/third_party/quiche/src/quic/core/batch_writer/quic_sendmmsg_batch_writer.h" + +namespace quic { +namespace test { +namespace { + +// Add tests here. + +} // namespace +} // namespace test +} // namespace quic 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 b4907d4a020..1605b264c63 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 @@ -11,6 +11,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_ack_frequency_frame.h" #include "net/third_party/quiche/src/quic/core/quic_framer.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" @@ -77,6 +78,7 @@ class ChloFramerVisitor : public QuicFramerVisitorInterface, bool OnPaddingFrame(const QuicPaddingFrame& frame) override; bool OnMessageFrame(const QuicMessageFrame& frame) override; bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) override; + bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& farme) override; void OnPacketComplete() override {} bool IsValidStatelessResetToken(QuicUint128 token) const override; void OnAuthenticatedIetfStatelessResetPacket( @@ -289,6 +291,11 @@ bool ChloFramerVisitor::OnHandshakeDoneFrame( return true; } +bool ChloFramerVisitor::OnAckFrequencyFrame( + const QuicAckFrequencyFrame& /*frame*/) { + return true; +} + bool ChloFramerVisitor::IsValidStatelessResetToken( QuicUint128 /*token*/) const { return false; diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.cc index f885c94794c..6eacef38e2e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.cc @@ -87,7 +87,7 @@ BandwidthSampler::BandwidthSampler( total_bytes_sent_at_last_acked_packet_(0), last_acked_packet_sent_time_(QuicTime::Zero()), last_acked_packet_ack_time_(QuicTime::Zero()), - is_app_limited_(started_as_app_limited_), + is_app_limited_(true), connection_state_map_(), max_tracked_packets_(GetQuicFlag(FLAGS_quic_max_tracked_packet_count)), unacked_packet_map_(unacked_packet_map), @@ -105,7 +105,6 @@ BandwidthSampler::BandwidthSampler(const BandwidthSampler& other) last_acked_packet_sent_time_(other.last_acked_packet_sent_time_), last_acked_packet_ack_time_(other.last_acked_packet_ack_time_), last_sent_packet_(other.last_sent_packet_), - started_as_app_limited_(other.started_as_app_limited_), is_app_limited_(other.is_app_limited_), end_of_app_limited_phase_(other.end_of_app_limited_phase_), connection_state_map_(other.connection_state_map_), @@ -321,23 +320,13 @@ BandwidthSample BandwidthSampler::OnPacketAcknowledgedInner( recent_ack_points_.Update(ack_time, total_bytes_acked_); } - if (started_as_app_limited_) { - if (is_app_limited_) { - // Exit app-limited phase in two cases: - // (1) end_of_app_limited_phase_ is not initialized, i.e., so far all - // packets are sent while there are buffered packets or pending data. - // (2) The current acked packet is after the sent packet marked as the end - // of the app limit phase. - if (!end_of_app_limited_phase_.IsInitialized() || - packet_number > end_of_app_limited_phase_) { - QUIC_RELOADABLE_FLAG_COUNT(quic_bw_sampler_app_limited_starting_value); - is_app_limited_ = false; - } - } - } else { - // Exit app-limited phase once a packet that was sent while the connection - // is not app-limited is acknowledged. - if (is_app_limited_ && end_of_app_limited_phase_.IsInitialized() && + if (is_app_limited_) { + // Exit app-limited phase in two cases: + // (1) end_of_app_limited_phase_ is not initialized, i.e., so far all + // packets are sent while there are buffered packets or pending data. + // (2) The current acked packet is after the sent packet marked as the end + // of the app limit phase. + if (!end_of_app_limited_phase_.IsInitialized() || packet_number > end_of_app_limited_phase_) { is_app_limited_ = false; } 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 b3c07364705..f32eabf0694 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 @@ -528,10 +528,6 @@ class QUIC_EXPORT_PRIVATE BandwidthSampler : public BandwidthSamplerInterface { // The most recently sent packet. QuicPacketNumber last_sent_packet_; - // Indicates whether the bandwidth sampler is started in app-limited phase. - const bool started_as_app_limited_ = - GetQuicReloadableFlag(quic_bw_sampler_app_limited_starting_value); - // Indicates whether the bandwidth sampler is currently in an app-limited // phase. bool is_app_limited_; @@ -565,8 +561,7 @@ class QUIC_EXPORT_PRIVATE BandwidthSampler : public BandwidthSamplerInterface { MaxAckHeightTracker max_ack_height_tracker_; QuicByteCount total_bytes_acked_after_last_ack_event_; - // True if --quic_avoid_overestimate_bandwidth_with_aggregation=true and - // connection option 'BSAO' is set. + // True if connection option 'BSAO' is set. bool overestimate_avoidance_; }; diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler_test.cc index 5dda9986965..f689553bc5b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler_test.cc @@ -34,8 +34,18 @@ const QuicByteCount kRegularPacketSize = 1280; static_assert((kRegularPacketSize & 31) == 0, "kRegularPacketSize has to be five times divisible by 2"); +struct TestParameters { + bool overestimate_avoidance; +}; + +// Used by ::testing::PrintToStringParamName(). +std::string PrintToString(const TestParameters& p) { + return p.overestimate_avoidance ? "enable_overestimate_avoidance" + : "no_enable_overestimate_avoidance"; +} + // A test fixture with utility methods for BandwidthSampler tests. -class BandwidthSamplerTest : public QuicTest { +class BandwidthSamplerTest : public QuicTestWithParam<TestParameters> { protected: BandwidthSamplerTest() : sampler_(nullptr, /*max_height_tracker_window_length=*/0), @@ -46,8 +56,7 @@ class BandwidthSamplerTest : public QuicTest { round_trip_count_(0) { // Ensure that the clock does not start at zero. clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation)) { + if (GetParam().overestimate_avoidance) { sampler_.EnableOverestimateAvoidance(); } } @@ -170,8 +179,15 @@ class BandwidthSamplerTest : public QuicTest { } }; +INSTANTIATE_TEST_SUITE_P( + BandwidthSamplerTests, + BandwidthSamplerTest, + testing::Values(TestParameters{/*overestimate_avoidance=*/false}, + TestParameters{/*overestimate_avoidance=*/true}), + testing::PrintToStringParamName()); + // Test the sampler in a simple stop-and-wait sender setting. -TEST_F(BandwidthSamplerTest, SendAndWait) { +TEST_P(BandwidthSamplerTest, SendAndWait) { QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(10); QuicBandwidth expected_bandwidth = QuicBandwidth::FromBytesPerSecond(kRegularPacketSize * 100); @@ -200,7 +216,7 @@ TEST_F(BandwidthSamplerTest, SendAndWait) { EXPECT_EQ(0u, bytes_in_flight_); } -TEST_F(BandwidthSamplerTest, SendTimeState) { +TEST_P(BandwidthSamplerTest, SendTimeState) { QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(10); // Send packets 1-5. @@ -266,7 +282,7 @@ TEST_F(BandwidthSamplerTest, SendTimeState) { // Test the sampler during regular windowed sender scenario with fixed // CWND of 20. -TEST_F(BandwidthSamplerTest, SendPaced) { +TEST_P(BandwidthSamplerTest, SendPaced) { const QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(1); QuicBandwidth expected_bandwidth = @@ -288,7 +304,7 @@ TEST_F(BandwidthSamplerTest, SendPaced) { } // Test the sampler in a scenario where 50% of packets is consistently lost. -TEST_F(BandwidthSamplerTest, SendWithLosses) { +TEST_P(BandwidthSamplerTest, SendWithLosses) { const QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(1); QuicBandwidth expected_bandwidth = @@ -333,7 +349,7 @@ TEST_F(BandwidthSamplerTest, SendWithLosses) { // congestion controlled (specifically, non-retransmittable data is not // congestion controlled). Should be functionally consistent in behavior with // the SendWithLosses test. -TEST_F(BandwidthSamplerTest, NotCongestionControlled) { +TEST_P(BandwidthSamplerTest, NotCongestionControlled) { const QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(1); QuicBandwidth expected_bandwidth = @@ -382,7 +398,7 @@ TEST_F(BandwidthSamplerTest, NotCongestionControlled) { // Simulate a situation where ACKs arrive in burst and earlier than usual, thus // producing an ACK rate which is higher than the original send rate. -TEST_F(BandwidthSamplerTest, CompressedAck) { +TEST_P(BandwidthSamplerTest, CompressedAck) { const QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(1); QuicBandwidth expected_bandwidth = @@ -410,7 +426,7 @@ TEST_F(BandwidthSamplerTest, CompressedAck) { } // Tests receiving ACK packets in the reverse order. -TEST_F(BandwidthSamplerTest, ReorderedAck) { +TEST_P(BandwidthSamplerTest, ReorderedAck) { const QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(1); QuicBandwidth expected_bandwidth = @@ -441,7 +457,7 @@ TEST_F(BandwidthSamplerTest, ReorderedAck) { } // Test the app-limited logic. -TEST_F(BandwidthSamplerTest, AppLimited) { +TEST_P(BandwidthSamplerTest, AppLimited) { const QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(1); QuicBandwidth expected_bandwidth = @@ -508,7 +524,7 @@ TEST_F(BandwidthSamplerTest, AppLimited) { } // Test the samples taken at the first flight of packets sent. -TEST_F(BandwidthSamplerTest, FirstRoundTrip) { +TEST_P(BandwidthSamplerTest, FirstRoundTrip) { const QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(1); const QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(800); @@ -543,7 +559,7 @@ TEST_F(BandwidthSamplerTest, FirstRoundTrip) { } // Test sampler's ability to remove obsolete packets. -TEST_F(BandwidthSamplerTest, RemoveObsoletePackets) { +TEST_P(BandwidthSamplerTest, RemoveObsoletePackets) { SendPacket(1); SendPacket(2); SendPacket(3); @@ -566,7 +582,7 @@ TEST_F(BandwidthSamplerTest, RemoveObsoletePackets) { EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_)); } -TEST_F(BandwidthSamplerTest, NeuterPacket) { +TEST_P(BandwidthSamplerTest, NeuterPacket) { SendPacket(1); EXPECT_EQ(0u, sampler_.total_bytes_neutered()); @@ -589,7 +605,7 @@ TEST_F(BandwidthSamplerTest, NeuterPacket) { EXPECT_EQ(0u, sample.extra_acked); } -TEST_F(BandwidthSamplerTest, CongestionEventSampleDefaultValues) { +TEST_P(BandwidthSamplerTest, CongestionEventSampleDefaultValues) { // Make sure a default constructed CongestionEventSample has the correct // initial values for BandwidthSampler::OnCongestionEvent() to work. BandwidthSampler::CongestionEventSample sample; @@ -602,7 +618,7 @@ TEST_F(BandwidthSamplerTest, CongestionEventSampleDefaultValues) { } // 1) Send 2 packets, 2) Ack both in 1 event, 3) Repeat. -TEST_F(BandwidthSamplerTest, TwoAckedPacketsPerEvent) { +TEST_P(BandwidthSamplerTest, TwoAckedPacketsPerEvent) { QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(10); QuicBandwidth sending_rate = QuicBandwidth::FromBytesAndTimeDelta( kRegularPacketSize, time_between_packets); @@ -631,7 +647,7 @@ TEST_F(BandwidthSamplerTest, TwoAckedPacketsPerEvent) { } } -TEST_F(BandwidthSamplerTest, LoseEveryOtherPacket) { +TEST_P(BandwidthSamplerTest, LoseEveryOtherPacket) { QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(10); QuicBandwidth sending_rate = QuicBandwidth::FromBytesAndTimeDelta( kRegularPacketSize, time_between_packets); @@ -663,7 +679,7 @@ TEST_F(BandwidthSamplerTest, LoseEveryOtherPacket) { } } -TEST_F(BandwidthSamplerTest, AckHeightRespectBandwidthEstimateUpperBound) { +TEST_P(BandwidthSamplerTest, AckHeightRespectBandwidthEstimateUpperBound) { QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(10); QuicBandwidth first_packet_sending_rate = QuicBandwidth::FromBytesAndTimeDelta(kRegularPacketSize, @@ -693,10 +709,7 @@ TEST_F(BandwidthSamplerTest, AckHeightRespectBandwidthEstimateUpperBound) { class MaxAckHeightTrackerTest : public QuicTest { protected: MaxAckHeightTrackerTest() : tracker_(/*initial_filter_window=*/10) { - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation)) { - tracker_.SetAckAggregationBandwidthThreshold(1.8); - } + tracker_.SetAckAggregationBandwidthThreshold(1.8); } // Run a full aggregation episode, which is one or more aggregated acks, 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 90df9a0c0da..cdd0cb54c16 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 @@ -110,7 +110,7 @@ void Bbr2NetworkModel::OnCongestionEventStart( // b) all packets in |acked_packets| did not generate valid samples. (e.g. ack // of ack-only packets). In both cases, total_bytes_acked() will not change. if (prior_bytes_acked != total_bytes_acked()) { - QUIC_BUG_IF(sample.sample_max_bandwidth.IsZero()) + QUIC_LOG_IF(WARNING, sample.sample_max_bandwidth.IsZero()) << total_bytes_acked() - prior_bytes_acked << " bytes from " << acked_packets.size() << " packets have been acked, but sample_max_bandwidth is zero."; 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 e110a4c3297..ddef7722939 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 @@ -184,6 +184,9 @@ struct QUIC_EXPORT_PRIVATE Bbr2Params { // Can be enabled by connection option 'B2LO'. bool ignore_inflight_lo = false; + + // Can be enabled by connection optoin 'B2HI'. + bool limit_inflight_hi_by_cwnd = false; }; class QUIC_EXPORT_PRIVATE RoundTripCounter { 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 6fb7eee8059..f6a09388604 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 @@ -198,9 +198,9 @@ Bbr2ProbeBwMode::AdaptUpperBoundsResult Bbr2ProbeBwMode::MaybeAdaptUpperBounds( cycle_.is_sample_from_probing = false; if (!send_state.is_app_limited) { - QuicByteCount inflight_at_send = BytesInFlight(send_state); + const QuicByteCount inflight_at_send = BytesInFlight(send_state); - QuicByteCount inflight_target = + const QuicByteCount inflight_target = sender_->GetTargetBytesInflight() * (1.0 - Params().beta); if (inflight_at_send >= inflight_target) { // The new code does not change behavior. @@ -209,7 +209,20 @@ Bbr2ProbeBwMode::AdaptUpperBoundsResult Bbr2ProbeBwMode::MaybeAdaptUpperBounds( // The new code actually cuts inflight_hi slower than before. QUIC_CODE_COUNT(quic_bbr2_cut_inflight_hi_gradually_in_effect); } - model_->set_inflight_hi(std::max(inflight_at_send, inflight_target)); + if (Params().limit_inflight_hi_by_cwnd) { + const QuicByteCount cwnd_target = + sender_->GetCongestionWindow() * (1.0 - Params().beta); + if (inflight_at_send >= cwnd_target) { + // The new code does not change behavior. + QUIC_CODE_COUNT(quic_bbr2_cut_inflight_hi_cwnd_noop); + } else { + // The new code actually cuts inflight_hi slower than before. + QUIC_CODE_COUNT(quic_bbr2_cut_inflight_hi_cwnd_in_effect); + } + model_->set_inflight_hi(std::max(inflight_at_send, cwnd_target)); + } else { + model_->set_inflight_hi(std::max(inflight_at_send, inflight_target)); + } } QUIC_DVLOG(3) << sender_ << " " << cycle_.phase 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 8c0171bc9bf..d1b3159f271 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 @@ -95,11 +95,7 @@ void Bbr2Sender::SetFromConfig(const QuicConfig& config, if (config.HasClientRequestedIndependentOption(kBBR9, perspective)) { params_.flexible_app_limited = true; } - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation) && - config.HasClientRequestedIndependentOption(kBSAO, perspective)) { - QUIC_RELOADABLE_FLAG_COUNT_N( - quic_avoid_overestimate_bandwidth_with_aggregation, 4, 4); + if (config.HasClientRequestedIndependentOption(kBSAO, perspective)) { model_.EnableOverestimateAvoidance(); } if (config.HasClientRequestedIndependentOption(kB2NA, perspective)) { @@ -127,14 +123,18 @@ void Bbr2Sender::SetFromConfig(const QuicConfig& config, QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_ignore_inflight_lo); params_.ignore_inflight_lo = true; } + if (GetQuicReloadableFlag(quic_bbr2_limit_inflight_hi) && + config.HasClientRequestedIndependentOption(kB2HI, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_limit_inflight_hi); + params_.limit_inflight_hi_by_cwnd = true; + } ApplyConnectionOptions(config.ClientRequestedIndependentOptions(perspective)); } void Bbr2Sender::ApplyConnectionOptions( const QuicTagVector& connection_options) { - if (GetQuicReloadableFlag(quic_bbr2_lower_startup_cwnd_gain) && - ContainsQuicTag(connection_options, kBBQ2)) { + if (ContainsQuicTag(connection_options, kBBQ2)) { // 2 is the lower, derived gain for CWND. params_.startup_cwnd_gain = 2; params_.drain_cwnd_gain = 2; 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 f803ab68cdf..205d3563f90 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 @@ -427,10 +427,7 @@ TEST_F(Bbr2DefaultTopologyTest, SimpleTransferSmallBuffer) { } TEST_F(Bbr2DefaultTopologyTest, SimpleTransfer2RTTAggregationBytes) { - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation)) { - SetConnectionOption(kBSAO); - } + SetConnectionOption(kBSAO); DefaultTopologyParams params; CreateNetwork(params); // 2 RTTs of aggregation, with a max of 10kb. @@ -440,17 +437,9 @@ TEST_F(Bbr2DefaultTopologyTest, SimpleTransfer2RTTAggregationBytes) { DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation)) { - EXPECT_APPROX_EQ(params.BottleneckBandwidth(), - sender_->ExportDebugState().bandwidth_hi, 0.01f); - } else { - EXPECT_LE(params.BottleneckBandwidth() * 0.99f, - sender_->ExportDebugState().bandwidth_hi); - // TODO(b/36022633): Bandwidth sampler overestimates with aggregation. - EXPECT_GE(params.BottleneckBandwidth() * 1.5f, - sender_->ExportDebugState().bandwidth_hi); - } + 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. @@ -459,10 +448,7 @@ TEST_F(Bbr2DefaultTopologyTest, SimpleTransfer2RTTAggregationBytes) { } TEST_F(Bbr2DefaultTopologyTest, SimpleTransferAckDecimation) { - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation)) { - SetConnectionOption(kBSAO); - } + SetConnectionOption(kBSAO); // Enable Ack Decimation on the receiver. QuicConnectionPeer::SetAckMode(receiver_endpoint_.connection(), AckMode::ACK_DECIMATION); @@ -473,17 +459,9 @@ TEST_F(Bbr2DefaultTopologyTest, SimpleTransferAckDecimation) { DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation)) { - EXPECT_APPROX_EQ(params.BottleneckBandwidth(), - sender_->ExportDebugState().bandwidth_hi, 0.01f); - } else { - EXPECT_LE(params.BottleneckBandwidth() * 0.99f, - sender_->ExportDebugState().bandwidth_hi); - // TODO(b/36022633): Bandwidth sampler overestimates with aggregation. - EXPECT_GE(params.BottleneckBandwidth() * 1.1f, - sender_->ExportDebugState().bandwidth_hi); - } + EXPECT_APPROX_EQ(params.BottleneckBandwidth(), + sender_->ExportDebugState().bandwidth_hi, 0.01f); + EXPECT_LE(sender_loss_rate_in_packets(), 0.001); EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); // The margin here is high, because the aggregation greatly increases diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc index f150ec14787..6c6f3efd2ad 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc @@ -32,10 +32,6 @@ const float kDefaultHighGain = 2.885f; const float kDerivedHighGain = 2.773f; // The newly derived CWND gain for STARTUP, 2. const float kDerivedHighCWNDGain = 2.0f; -// The gain used in STARTUP after loss has been detected. -// 1.5 is enough to allow for 25% exogenous loss and still observe a 25% growth -// in measured bandwidth. -const float kStartupAfterLossGain = 1.5f; // The cycle of gains used during the PROBE_BW stage. const float kPacingGain[] = {1.25, 0.75, 1, 1, 1, 1, 1, 1}; @@ -106,8 +102,6 @@ BbrSender::BbrSender(QuicTime now, congestion_window_gain_constant_( static_cast<float>(GetQuicFlag(FLAGS_quic_bbr_cwnd_gain))), num_startup_rtts_(kRoundTripsWithoutGrowthBeforeExitingStartup), - exit_startup_on_loss_( - GetQuicReloadableFlag(quic_bbr_default_exit_startup_on_loss)), cycle_current_offset_(0), last_cycle_start_(QuicTime::Zero()), is_at_full_bandwidth_(false), @@ -138,10 +132,7 @@ BbrSender::BbrSender(QuicTime now, stats_->slowstart_duration = QuicTimeAccumulator(); } EnterStartupMode(now); - if (exit_startup_on_loss_) { - QUIC_RELOADABLE_FLAG_COUNT(quic_bbr_default_exit_startup_on_loss); - set_high_cwnd_gain(kDerivedHighCWNDGain); - } + set_high_cwnd_gain(kDerivedHighCWNDGain); } BbrSender::~BbrSender() {} @@ -203,14 +194,8 @@ QuicByteCount BbrSender::GetCongestionWindow() const { return ProbeRttCongestionWindow(); } - if (exit_startup_on_loss_) { - if (InRecovery()) { - return std::min(congestion_window_, recovery_window_); - } - } else { - if (InRecovery() && !(rate_based_startup_ && mode_ == STARTUP)) { - return std::min(congestion_window_, recovery_window_); - } + if (InRecovery()) { + return std::min(congestion_window_, recovery_window_); } return congestion_window_; @@ -265,17 +250,9 @@ void BbrSender::SetFromConfig(const QuicConfig& config, if (config.HasClientRequestedIndependentOption(k2RTT, perspective)) { num_startup_rtts_ = 2; } - if (!exit_startup_on_loss_ && - config.HasClientRequestedIndependentOption(kBBRS, perspective)) { - slower_startup_ = true; - } if (config.HasClientRequestedIndependentOption(kBBR3, perspective)) { drain_to_target_ = true; } - if (!exit_startup_on_loss_ && - config.HasClientRequestedIndependentOption(kBBS1, perspective)) { - rate_based_startup_ = true; - } if (GetQuicReloadableFlag(quic_bbr_mitigate_overly_large_bandwidth_sample)) { if (config.HasClientRequestedIndependentOption(kBWM3, perspective)) { bytes_lost_multiplier_with_network_parameters_adjusted_ = 3; @@ -313,11 +290,7 @@ void BbrSender::SetFromConfig(const QuicConfig& config, max_congestion_window_with_network_parameters_adjusted_ = 100 * kDefaultTCPMSS; } - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation) && - config.HasClientRequestedIndependentOption(kBSAO, perspective)) { - QUIC_RELOADABLE_FLAG_COUNT_N( - quic_avoid_overestimate_bandwidth_with_aggregation, 3, 4); + if (config.HasClientRequestedIndependentOption(kBSAO, perspective)) { sampler_.EnableOverestimateAvoidance(); } @@ -325,14 +298,7 @@ void BbrSender::SetFromConfig(const QuicConfig& config, } void BbrSender::ApplyConnectionOptions( - const QuicTagVector& connection_options) { - if (ContainsQuicTag(connection_options, kLRTT)) { - exit_startup_on_loss_ = true; - } - if (ContainsQuicTag(connection_options, kBBQ2)) { - set_high_cwnd_gain(kDerivedHighCWNDGain); - } -} + const QuicTagVector& /*connection_options*/) {} void BbrSender::AdjustNetworkParameters(const NetworkParams& params) { const QuicBandwidth& bandwidth = params.bandwidth; @@ -352,12 +318,16 @@ void BbrSender::AdjustNetworkParameters(const NetworkParams& params) { // Ignore bad bandwidth samples. return; } + + auto cwnd_bootstrapping_rtt = params.quic_bbr_donot_inject_bandwidth + ? GetMinRtt() + : rtt_stats_->SmoothedOrInitialRtt(); const QuicByteCount new_cwnd = std::max( kMinInitialCongestionWindow * kDefaultTCPMSS, std::min(max_congestion_window_with_network_parameters_adjusted_, - bandwidth * (params.quic_bbr_donot_inject_bandwidth - ? GetMinRtt() - : rtt_stats_->SmoothedOrInitialRtt()))); + bandwidth * cwnd_bootstrapping_rtt)); + + stats_->cwnd_bootstrapping_rtt_us = cwnd_bootstrapping_rtt.ToMicroseconds(); if (!rtt_stats_->smoothed_rtt().IsZero()) { QUIC_CODE_COUNT(quic_smoothed_rtt_available); } else if (rtt_stats_->initial_rtt() != @@ -434,7 +404,7 @@ void BbrSender::OnCongestionEvent(bool /*rtt_updated*/, // ack-only packets). In both cases, sampler_.total_bytes_acked() will not // change. if (total_bytes_acked_before != sampler_.total_bytes_acked()) { - QUIC_BUG_IF(sample.sample_max_bandwidth.IsZero()) + QUIC_LOG_IF(WARNING, sample.sample_max_bandwidth.IsZero()) << sampler_.total_bytes_acked() - total_bytes_acked_before << " bytes from " << acked_packets.size() << " packets have been acked, but sample_max_bandwidth is zero."; @@ -502,13 +472,10 @@ QuicTime::Delta BbrSender::GetMinRtt() const { if (!min_rtt_.IsZero()) { return min_rtt_; } - if (GetQuicReloadableFlag(quic_bbr_use_available_min_rtt)) { - // min_rtt could be available if the handshake packet gets neutered then - // gets acknowledged. This could only happen for QUIC crypto where we do not - // drop keys. - return rtt_stats_->MinOrInitialRtt(); - } - return rtt_stats_->initial_rtt(); + // min_rtt could be available if the handshake packet gets neutered then + // gets acknowledged. This could only happen for QUIC crypto where we do not + // drop keys. + return rtt_stats_->MinOrInitialRtt(); } QuicByteCount BbrSender::GetTargetCongestionWindow(float gain) const { @@ -675,10 +642,6 @@ void BbrSender::OnExitStartup(QuicTime now) { bool BbrSender::ShouldExitStartupDueToLoss( const SendTimeState& last_packet_send_state) const { - if (!exit_startup_on_loss_) { - return false; - } - if (num_loss_events_in_round_ < GetQuicFlag(FLAGS_quic_bbr2_default_startup_full_loss_count) || !last_packet_send_state.is_valid) { @@ -749,7 +712,7 @@ void BbrSender::UpdateRecoveryState(QuicPacketNumber last_acked_packet, bool has_losses, bool is_round_start) { // Disable recovery in startup, if loss-based exit is enabled. - if (exit_startup_on_loss_ && !is_at_full_bandwidth_) { + if (!is_at_full_bandwidth_) { return; } @@ -843,16 +806,6 @@ void BbrSender::CalculatePacingRate(QuicByteCount bytes_lost) { } } - if (!exit_startup_on_loss_) { - // Slow the pacing rate in STARTUP once loss has ever been detected. - const bool has_ever_detected_loss = end_recovery_at_.IsInitialized(); - if (slower_startup_ && has_ever_detected_loss && - has_non_app_limited_sample_) { - pacing_rate_ = kStartupAfterLossGain * BandwidthEstimate(); - return; - } - } - // Do not decrease the pacing rate during startup. pacing_rate_ = std::max(pacing_rate_, target_rate); } @@ -898,10 +851,6 @@ void BbrSender::CalculateCongestionWindow(QuicByteCount bytes_acked, void BbrSender::CalculateRecoveryWindow(QuicByteCount bytes_acked, QuicByteCount bytes_lost) { - if (!exit_startup_on_loss_ && rate_based_startup_ && mode_ == STARTUP) { - return; - } - if (recovery_state_ == NOT_IN_RECOVERY) { return; } diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h index c7285d76f24..446f0ddff8d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h @@ -326,13 +326,6 @@ class QUIC_EXPORT_PRIVATE BbrSender : public SendAlgorithmInterface { // The number of RTTs to stay in STARTUP mode. Defaults to 3. QuicRoundTripCount num_startup_rtts_; - // Latched value of --quic_bbr_default_exit_startup_on_loss. - // If true, exit startup if all of the following conditions are met: - // - 1RTT has passed with no bandwidth increase, - // - Some number of congestion events happened with loss, in the last round. - // - Some amount of inflight bytes (at the start of the last round) are lost. - bool exit_startup_on_loss_; - // Number of round-trips in PROBE_BW mode, used for determining the current // pacing gain cycle. int cycle_current_offset_; diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender_test.cc index e50a7c04c61..88559185087 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender_test.cc @@ -115,6 +115,9 @@ class BbrSenderTest : public QuicTest { } void SetUp() override { + SetQuicReloadableFlag(quic_fix_bbr_cwnd_in_bandwidth_resumption, true); + SetQuicReloadableFlag(quic_bbr_fix_pacing_rate, true); + SetQuicReloadableFlag(quic_bbr_donot_inject_bandwidth, true); if (GetQuicFlag(FLAGS_quic_bbr_test_regression_mode) == "regress") { SendAlgorithmTestResult expected; ASSERT_TRUE(LoadSendAlgorithmTestResult(&expected)); @@ -389,10 +392,7 @@ TEST_F(BbrSenderTest, RemoveBytesLostInRecovery) { // Test a simple long data transfer with 2 rtts of aggregation. TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes) { - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation)) { - SetConnectionOption(kBSAO); - } + SetConnectionOption(kBSAO); CreateDefaultSetup(); // 2 RTTs of aggregation, with a max of 10kb. EnableAggregation(10 * 1024, 2 * kTestRtt); @@ -401,20 +401,10 @@ TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes) { DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); EXPECT_TRUE(sender_->ExportDebugState().mode == BbrSender::PROBE_BW || sender_->ExportDebugState().mode == BbrSender::PROBE_RTT); - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation)) { - EXPECT_APPROX_EQ(kTestLinkBandwidth, - sender_->ExportDebugState().max_bandwidth, 0.01f); - } else { - // It's possible to read a bandwidth as much as 50% too high with - // aggregation. - EXPECT_LE(kTestLinkBandwidth * 0.93f, - sender_->ExportDebugState().max_bandwidth); - // TODO(ianswett): Tighten this bound once we understand why BBR is - // overestimating bandwidth with aggregation. b/36022633 - EXPECT_GE(kTestLinkBandwidth * 1.5f, - sender_->ExportDebugState().max_bandwidth); - } + + EXPECT_APPROX_EQ(kTestLinkBandwidth, + sender_->ExportDebugState().max_bandwidth, 0.01f); + // The margin here is high, because the aggregation greatly increases // smoothed rtt. EXPECT_GE(kTestRtt * 4, rtt_stats_->smoothed_rtt()); @@ -423,10 +413,7 @@ TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes) { // Test a simple long data transfer with 2 rtts of aggregation. TEST_F(BbrSenderTest, SimpleTransferAckDecimation) { - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation)) { - SetConnectionOption(kBSAO); - } + SetConnectionOption(kBSAO); // Decrease the CWND gain so extra CWND is required with stretch acks. SetQuicFlag(FLAGS_quic_bbr_cwnd_gain, 1.0); sender_ = new BbrSender( @@ -446,20 +433,9 @@ TEST_F(BbrSenderTest, SimpleTransferAckDecimation) { DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation)) { - EXPECT_APPROX_EQ(kTestLinkBandwidth, - sender_->ExportDebugState().max_bandwidth, 0.01f); - } else { - // It's possible to read a bandwidth as much as 50% too high with - // aggregation. - EXPECT_LE(kTestLinkBandwidth * 0.93f, - sender_->ExportDebugState().max_bandwidth); - // TODO(ianswett): Tighten this bound once we understand why BBR is - // overestimating bandwidth with aggregation. b/36022633 - EXPECT_GE(kTestLinkBandwidth * 1.5f, - sender_->ExportDebugState().max_bandwidth); - } + EXPECT_APPROX_EQ(kTestLinkBandwidth, + sender_->ExportDebugState().max_bandwidth, 0.01f); + // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures // bandwidth higher than the link rate. EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); @@ -471,10 +447,7 @@ TEST_F(BbrSenderTest, SimpleTransferAckDecimation) { // Test a simple long data transfer with 2 rtts of aggregation. TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes20RTTWindow) { - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation)) { - SetConnectionOption(kBSAO); - } + SetConnectionOption(kBSAO); // Disable Ack Decimation on the receiver, because it can increase srtt. QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING); CreateDefaultSetup(); @@ -486,20 +459,10 @@ TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes20RTTWindow) { DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); EXPECT_TRUE(sender_->ExportDebugState().mode == BbrSender::PROBE_BW || sender_->ExportDebugState().mode == BbrSender::PROBE_RTT); - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation)) { - EXPECT_APPROX_EQ(kTestLinkBandwidth, - sender_->ExportDebugState().max_bandwidth, 0.01f); - } else { - // It's possible to read a bandwidth as much as 50% too high with - // aggregation. - EXPECT_LE(kTestLinkBandwidth * 0.93f, - sender_->ExportDebugState().max_bandwidth); - // TODO(ianswett): Tighten this bound once we understand why BBR is - // overestimating bandwidth with aggregation. b/36022633 - EXPECT_GE(kTestLinkBandwidth * 1.5f, - sender_->ExportDebugState().max_bandwidth); - } + + EXPECT_APPROX_EQ(kTestLinkBandwidth, + sender_->ExportDebugState().max_bandwidth, 0.01f); + // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures // bandwidth higher than the link rate. // The margin here is high, because the aggregation greatly increases @@ -510,10 +473,7 @@ TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes20RTTWindow) { // Test a simple long data transfer with 2 rtts of aggregation. TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes40RTTWindow) { - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation)) { - SetConnectionOption(kBSAO); - } + SetConnectionOption(kBSAO); // Disable Ack Decimation on the receiver, because it can increase srtt. QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING); CreateDefaultSetup(); @@ -525,20 +485,10 @@ TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes40RTTWindow) { DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); EXPECT_TRUE(sender_->ExportDebugState().mode == BbrSender::PROBE_BW || sender_->ExportDebugState().mode == BbrSender::PROBE_RTT); - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation)) { - EXPECT_APPROX_EQ(kTestLinkBandwidth, - sender_->ExportDebugState().max_bandwidth, 0.01f); - } else { - // It's possible to read a bandwidth as much as 50% too high with - // aggregation. - EXPECT_LE(kTestLinkBandwidth * 0.93f, - sender_->ExportDebugState().max_bandwidth); - // TODO(ianswett): Tighten this bound once we understand why BBR is - // overestimating bandwidth with aggregation. b/36022633 - EXPECT_GE(kTestLinkBandwidth * 1.5f, - sender_->ExportDebugState().max_bandwidth); - } + + EXPECT_APPROX_EQ(kTestLinkBandwidth, + sender_->ExportDebugState().max_bandwidth, 0.01f); + // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures // bandwidth higher than the link rate. // The margin here is high, because the aggregation greatly increases @@ -667,17 +617,10 @@ TEST_F(BbrSenderTest, Drain) { EXPECT_APPROX_EQ(sender_->BandwidthEstimate() * (1 / 2.885f), sender_->PacingRate(0), 0.01f); - if (!GetQuicReloadableFlag(quic_bbr_default_exit_startup_on_loss)) { - // BBR uses CWND gain of 2.88 during STARTUP, hence it will fill the buffer - // with approximately 1.88 BDPs. Here, we use 1.5 to give some margin for - // error. - EXPECT_GE(queue->bytes_queued(), 1.5 * kTestBdp); - } else { - // BBR uses CWND gain of 2 during STARTUP, hence it will fill the buffer - // with approximately 1 BDP. Here, we use 0.8 to give some margin for - // error. - EXPECT_GE(queue->bytes_queued(), 0.8 * kTestBdp); - } + // BBR uses CWND gain of 2 during STARTUP, hence it will fill the buffer + // with approximately 1 BDP. Here, we use 0.8 to give some margin for + // error. + EXPECT_GE(queue->bytes_queued(), 0.8 * kTestBdp); // Observe increased RTT due to bufferbloat. const QuicTime::Delta queueing_delay = @@ -935,9 +878,6 @@ TEST_F(BbrSenderTest, SimpleTransfer2RTTStartup) { TEST_F(BbrSenderTest, SimpleTransferExitStartupOnLoss) { CreateDefaultSetup(); - if (!GetQuicReloadableFlag(quic_bbr_default_exit_startup_on_loss)) { - SetConnectionOption(kLRTT); - } EXPECT_EQ(3u, sender_->num_startup_rtts()); // Run until the full bandwidth is reached and check how many rounds it was. @@ -965,9 +905,6 @@ TEST_F(BbrSenderTest, SimpleTransferExitStartupOnLoss) { TEST_F(BbrSenderTest, SimpleTransferExitStartupOnLossSmallBuffer) { CreateSmallBufferSetup(); - if (!GetQuicReloadableFlag(quic_bbr_default_exit_startup_on_loss)) { - SetConnectionOption(kLRTT); - } EXPECT_EQ(3u, sender_->num_startup_rtts()); // Run until the full bandwidth is reached and check how many rounds it was. @@ -1021,9 +958,6 @@ TEST_F(BbrSenderTest, DerivedPacingGainStartup) { TEST_F(BbrSenderTest, DerivedCWNDGainStartup) { CreateSmallBufferSetup(); - if (!GetQuicReloadableFlag(quic_bbr_default_exit_startup_on_loss)) { - SetConnectionOption(kBBQ2); - } EXPECT_EQ(3u, sender_->num_startup_rtts()); // Verify that Sender is in slow start. EXPECT_TRUE(sender_->InSlowStart()); diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.cc index f00045e7b8e..666e7ea7c5a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.cc @@ -12,6 +12,20 @@ namespace quic { +namespace { +float DetectionResponseTime(QuicTime::Delta rtt, + QuicTime send_time, + QuicTime detection_time) { + if (detection_time <= send_time || rtt.IsZero()) { + // Time skewed, assume a very fast detection where |detection_time| is + // |send_time| + |rtt|. + return 1.0; + } + float send_to_detection_us = (detection_time - send_time).ToMicroseconds(); + return send_to_detection_us / rtt.ToMicroseconds(); +} +} // namespace + GeneralLossAlgorithm::GeneralLossAlgorithm() : loss_detection_timeout_(QuicTime::Zero()), reordering_shift_(kDefaultLossDelayShift), @@ -56,7 +70,7 @@ LossDetectionInterface::DetectionStats GeneralLossAlgorithm::DetectLosses( QuicTime::Delta max_rtt = std::max(rtt_stats.previous_srtt(), rtt_stats.latest_rtt()); max_rtt = std::max(kAlarmGranularity, max_rtt); - QuicTime::Delta loss_delay = max_rtt + (max_rtt >> reordering_shift_); + const QuicTime::Delta loss_delay = max_rtt + (max_rtt >> reordering_shift_); QuicPacketNumber packet_number = unacked_packets.GetLeastUnacked(); auto it = unacked_packets.begin(); if (least_in_flight_.IsInitialized() && least_in_flight_ >= packet_number) { @@ -100,12 +114,18 @@ LossDetectionInterface::DetectionStats GeneralLossAlgorithm::DetectLosses( if (!skip_packet_threshold_detection && largest_newly_acked - packet_number >= reordering_threshold_) { packets_lost->push_back(LostPacket(packet_number, it->bytes_sent)); + detection_stats.total_loss_detection_response_time += + DetectionResponseTime(max_rtt, it->sent_time, time); continue; } // Time threshold loss detection. QuicTime when_lost = it->sent_time + loss_delay; if (time < when_lost) { + if (time >= + it->sent_time + max_rtt + (max_rtt >> (reordering_shift_ + 1))) { + ++detection_stats.sent_packets_num_borderline_time_reorderings; + } loss_detection_timeout_ = when_lost; if (!least_in_flight_.IsInitialized()) { // At this point, packet_number is in flight and not detected as lost. @@ -114,6 +134,8 @@ LossDetectionInterface::DetectionStats GeneralLossAlgorithm::DetectLosses( break; } packets_lost->push_back(LostPacket(packet_number, it->bytes_sent)); + detection_stats.total_loss_detection_response_time += + DetectionResponseTime(max_rtt, it->sent_time, time); } if (!least_in_flight_.IsInitialized()) { // There is no in flight packet. diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h index ee8ba64c2d7..950831e4594 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h @@ -58,6 +58,11 @@ class QUIC_EXPORT_PRIVATE GeneralLossAlgorithm : public LossDetectionInterface { << "Unexpected call to GeneralLossAlgorithm::OnMinRttAvailable"; } + void OnUserAgentIdKnown() override { + DCHECK(false) + << "Unexpected call to GeneralLossAlgorithm::OnUserAgentIdKnown"; + } + void OnConnectionClosed() override { DCHECK(false) << "Unexpected call to GeneralLossAlgorithm::OnConnectionClosed"; diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm_test.cc index 1a58ade2695..be33c25857c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm_test.cc @@ -62,14 +62,17 @@ class GeneralLossAlgorithmTest : public QuicTest { const AckedPacketVector& packets_acked, const std::vector<uint64_t>& losses_expected) { return VerifyLosses(largest_newly_acked, packets_acked, losses_expected, + quiche::QuicheOptional<QuicPacketCount>(), quiche::QuicheOptional<QuicPacketCount>()); } - void VerifyLosses(uint64_t largest_newly_acked, - const AckedPacketVector& packets_acked, - const std::vector<uint64_t>& losses_expected, - quiche::QuicheOptional<QuicPacketCount> - max_sequence_reordering_expected) { + void VerifyLosses( + uint64_t largest_newly_acked, + const AckedPacketVector& packets_acked, + const std::vector<uint64_t>& losses_expected, + quiche::QuicheOptional<QuicPacketCount> max_sequence_reordering_expected, + quiche::QuicheOptional<QuicPacketCount> + num_borderline_time_reorderings_expected) { unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace( APPLICATION_DATA, QuicPacketNumber(largest_newly_acked)); LostPacketVector lost_packets; @@ -80,6 +83,10 @@ class GeneralLossAlgorithmTest : public QuicTest { EXPECT_EQ(stats.sent_packets_max_sequence_reordering, max_sequence_reordering_expected.value()); } + if (num_borderline_time_reorderings_expected.has_value()) { + EXPECT_EQ(stats.sent_packets_num_borderline_time_reorderings, + num_borderline_time_reorderings_expected.value()); + } ASSERT_EQ(losses_expected.size(), lost_packets.size()); for (size_t i = 0; i < losses_expected.size(); ++i) { EXPECT_EQ(lost_packets[i].packet_number, @@ -104,19 +111,19 @@ TEST_F(GeneralLossAlgorithmTest, NackRetransmit1Packet) { unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2)); packets_acked.push_back(AckedPacket( QuicPacketNumber(2), kMaxOutgoingPacketSize, QuicTime::Zero())); - VerifyLosses(2, packets_acked, std::vector<uint64_t>{}, 1); + VerifyLosses(2, packets_acked, std::vector<uint64_t>{}, 1, 0); packets_acked.clear(); // No loss on two acks. unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3)); packets_acked.push_back(AckedPacket( QuicPacketNumber(3), kMaxOutgoingPacketSize, QuicTime::Zero())); - VerifyLosses(3, packets_acked, std::vector<uint64_t>{}, 2); + VerifyLosses(3, packets_acked, std::vector<uint64_t>{}, 2, 0); packets_acked.clear(); // Loss on three acks. unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4)); packets_acked.push_back(AckedPacket( QuicPacketNumber(4), kMaxOutgoingPacketSize, QuicTime::Zero())); - VerifyLosses(4, packets_acked, {1}, 3); + VerifyLosses(4, packets_acked, {1}, 3, 0); EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); } @@ -176,7 +183,12 @@ TEST_F(GeneralLossAlgorithmTest, EarlyRetransmit1Packet) { EXPECT_EQ(clock_.Now() + 1.25 * rtt_stats_.smoothed_rtt(), loss_algorithm_.GetLossTimeout()); - clock_.AdvanceTime(1.25 * rtt_stats_.latest_rtt()); + clock_.AdvanceTime(1.13 * rtt_stats_.latest_rtt()); + // If reordering_shift increases by one we should have detected a loss. + VerifyLosses(2, packets_acked, {}, /*max_sequence_reordering_expected=*/1, + /*num_borderline_time_reorderings_expected=*/1); + + clock_.AdvanceTime(0.13 * rtt_stats_.latest_rtt()); VerifyLosses(2, packets_acked, {1}); EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout()); } diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h index 8d91976de45..6799de97ae5 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h @@ -29,6 +29,10 @@ class QUIC_EXPORT_PRIVATE LossDetectionInterface { struct QUIC_NO_EXPORT DetectionStats { // Maximum sequence reordering observed in newly acked packets. QuicPacketCount sent_packets_max_sequence_reordering = 0; + QuicPacketCount sent_packets_num_borderline_time_reorderings = 0; + // Total detection response time for lost packets from this detection. + // See QuicConnectionStats for the definition of detection response time. + float total_loss_detection_response_time = 0.0; }; // Called when a new ack arrives or the loss alarm fires. @@ -56,6 +60,8 @@ class QUIC_EXPORT_PRIVATE LossDetectionInterface { virtual void OnMinRttAvailable() = 0; + virtual void OnUserAgentIdKnown() = 0; + virtual void OnConnectionClosed() = 0; }; diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.cc index 0f294d6b94e..5debad62c6b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.cc @@ -54,6 +54,10 @@ LossDetectionInterface::DetectionStats UberLossAlgorithm::DetectLosses( overall_stats.sent_packets_max_sequence_reordering = std::max(overall_stats.sent_packets_max_sequence_reordering, stats.sent_packets_max_sequence_reordering); + overall_stats.sent_packets_num_borderline_time_reorderings += + stats.sent_packets_num_borderline_time_reorderings; + overall_stats.total_loss_detection_response_time += + stats.total_loss_detection_response_time; } return overall_stats; @@ -96,11 +100,27 @@ void UberLossAlgorithm::SetLossDetectionTuner( } void UberLossAlgorithm::MaybeStartTuning() { - if (tuner_started_ || !tuning_enabled_ || !min_rtt_available_) { + if (tuner_started_ || !tuning_enabled_ || !min_rtt_available_ || + !user_agent_known_) { return; } tuner_started_ = tuner_->Start(&tuned_parameters_); + if (!tuner_started_) { + return; + } + + if (tuned_parameters_.reordering_shift.has_value() && + tuned_parameters_.reordering_threshold.has_value()) { + QUIC_DLOG(INFO) << "Setting reordering shift to " + << *tuned_parameters_.reordering_shift + << ", and reordering threshold to " + << *tuned_parameters_.reordering_threshold; + SetReorderingShift(*tuned_parameters_.reordering_shift); + SetReorderingThreshold(*tuned_parameters_.reordering_threshold); + } else { + QUIC_BUG << "Tuner started but some parameters are missing"; + } } void UberLossAlgorithm::OnConfigNegotiated() {} @@ -110,6 +130,11 @@ void UberLossAlgorithm::OnMinRttAvailable() { MaybeStartTuning(); } +void UberLossAlgorithm::OnUserAgentIdKnown() { + user_agent_known_ = true; + MaybeStartTuning(); +} + void UberLossAlgorithm::OnConnectionClosed() { if (tuner_ != nullptr && tuner_started_) { tuner_->Finish(tuned_parameters_); @@ -151,6 +176,10 @@ QuicPacketCount UberLossAlgorithm::GetPacketReorderingThreshold() const { return general_loss_algorithms_[APPLICATION_DATA].reordering_threshold(); } +int UberLossAlgorithm::GetPacketReorderingShift() const { + return general_loss_algorithms_[APPLICATION_DATA].reordering_shift(); +} + void UberLossAlgorithm::DisablePacketThresholdForRuntPackets() { for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) { general_loss_algorithms_[i].disable_packet_threshold_for_runt_packets(); diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.h index 86b652572b7..0d1453c07b3 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.h +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.h @@ -7,6 +7,7 @@ #include "net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/common/platform/api/quiche_optional.h" namespace quic { @@ -72,6 +73,7 @@ class QUIC_EXPORT_PRIVATE UberLossAlgorithm : public LossDetectionInterface { std::unique_ptr<LossDetectionTunerInterface> tuner); void OnConfigNegotiated() override; void OnMinRttAvailable() override; + void OnUserAgentIdKnown() override; void OnConnectionClosed() override; // Sets reordering_shift for all packet number spaces. @@ -93,12 +95,25 @@ class QUIC_EXPORT_PRIVATE UberLossAlgorithm : public LossDetectionInterface { // Always 3 when adaptive reordering is not enabled. QuicPacketCount GetPacketReorderingThreshold() const; + // Get the packet reordering shift from the APPLICATION_DATA PN space. + int GetPacketReorderingShift() const; + // Disable packet threshold loss detection for *runt* packets. void DisablePacketThresholdForRuntPackets(); // Called to reset loss detection of |space|. void ResetLossDetection(PacketNumberSpace space); + bool use_adaptive_reordering_threshold() const { + return general_loss_algorithms_[APPLICATION_DATA] + .use_adaptive_reordering_threshold(); + } + + bool use_adaptive_time_threshold() const { + return general_loss_algorithms_[APPLICATION_DATA] + .use_adaptive_time_threshold(); + } + private: friend class test::QuicSentPacketManagerPeer; @@ -112,6 +127,10 @@ class QUIC_EXPORT_PRIVATE UberLossAlgorithm : public LossDetectionInterface { LossDetectionParameters tuned_parameters_; bool tuner_started_ = false; bool min_rtt_available_ = false; + // If flag is false, set |user_agent_known_| to true, so loss detection tuner + // will start once SetFromConfig is called and min rtt is available. + bool user_agent_known_ = + !GetQuicReloadableFlag(quic_save_user_agent_in_quic_session); bool tuning_enabled_ = false; // Whether tuning is enabled by config. }; diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm_test.cc index 6fd949f317a..e6635402d68 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm_test.cc @@ -4,9 +4,12 @@ #include "net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.h" +#include <memory> #include <utility> #include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" #include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" @@ -204,6 +207,143 @@ TEST_F(UberLossAlgorithmTest, PacketInLimbo) { VerifyLosses(6, packets_acked_, std::vector<uint64_t>{2}); } +class TestLossTuner : public LossDetectionTunerInterface { + public: + TestLossTuner(bool forced_start_result, + LossDetectionParameters forced_parameters) + : forced_start_result_(forced_start_result), + forced_parameters_(std::move(forced_parameters)) {} + + ~TestLossTuner() override = default; + + bool Start(LossDetectionParameters* params) override { + start_called_ = true; + *params = forced_parameters_; + return forced_start_result_; + } + + void Finish(const LossDetectionParameters& /*params*/) override {} + + bool start_called() const { return start_called_; } + + private: + bool forced_start_result_; + LossDetectionParameters forced_parameters_; + bool start_called_ = false; +}; + +// Verify the parameters are changed if first call SetFromConfig(), then call +// OnMinRttAvailable(). +TEST_F(UberLossAlgorithmTest, LossDetectionTuning_SetFromConfigFirst) { + const int old_reordering_shift = loss_algorithm_.GetPacketReorderingShift(); + const QuicPacketCount old_reordering_threshold = + loss_algorithm_.GetPacketReorderingThreshold(); + + loss_algorithm_.OnUserAgentIdKnown(); + + // Not owned. + TestLossTuner* test_tuner = new TestLossTuner( + /*forced_start_result=*/true, + LossDetectionParameters{ + /*reordering_shift=*/old_reordering_shift + 1, + /*reordering_threshold=*/old_reordering_threshold * 2}); + loss_algorithm_.SetLossDetectionTuner( + std::unique_ptr<LossDetectionTunerInterface>(test_tuner)); + + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(kELDT); + config.SetInitialReceivedConnectionOptions(connection_options); + loss_algorithm_.SetFromConfig(config, Perspective::IS_SERVER); + + // MinRtt was not available when SetFromConfig was called. + EXPECT_FALSE(test_tuner->start_called()); + EXPECT_EQ(old_reordering_shift, loss_algorithm_.GetPacketReorderingShift()); + EXPECT_EQ(old_reordering_threshold, + loss_algorithm_.GetPacketReorderingThreshold()); + + // Tuning should start when MinRtt becomes available. + loss_algorithm_.OnMinRttAvailable(); + EXPECT_TRUE(test_tuner->start_called()); + EXPECT_NE(old_reordering_shift, loss_algorithm_.GetPacketReorderingShift()); + EXPECT_NE(old_reordering_threshold, + loss_algorithm_.GetPacketReorderingThreshold()); +} + +// Verify the parameters are changed if first call OnMinRttAvailable(), then +// call SetFromConfig(). +TEST_F(UberLossAlgorithmTest, LossDetectionTuning_OnMinRttAvailableFirst) { + const int old_reordering_shift = loss_algorithm_.GetPacketReorderingShift(); + const QuicPacketCount old_reordering_threshold = + loss_algorithm_.GetPacketReorderingThreshold(); + + loss_algorithm_.OnUserAgentIdKnown(); + + // Not owned. + TestLossTuner* test_tuner = new TestLossTuner( + /*forced_start_result=*/true, + LossDetectionParameters{ + /*reordering_shift=*/old_reordering_shift + 1, + /*reordering_threshold=*/old_reordering_threshold * 2}); + loss_algorithm_.SetLossDetectionTuner( + std::unique_ptr<LossDetectionTunerInterface>(test_tuner)); + + loss_algorithm_.OnMinRttAvailable(); + EXPECT_FALSE(test_tuner->start_called()); + EXPECT_EQ(old_reordering_shift, loss_algorithm_.GetPacketReorderingShift()); + EXPECT_EQ(old_reordering_threshold, + loss_algorithm_.GetPacketReorderingThreshold()); + + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(kELDT); + config.SetInitialReceivedConnectionOptions(connection_options); + // Should start tuning since MinRtt is available. + loss_algorithm_.SetFromConfig(config, Perspective::IS_SERVER); + + EXPECT_TRUE(test_tuner->start_called()); + EXPECT_NE(old_reordering_shift, loss_algorithm_.GetPacketReorderingShift()); + EXPECT_NE(old_reordering_threshold, + loss_algorithm_.GetPacketReorderingThreshold()); +} + +// Verify the parameters are not changed if Tuner.Start() returns false. +TEST_F(UberLossAlgorithmTest, LossDetectionTuning_StartFailed) { + const int old_reordering_shift = loss_algorithm_.GetPacketReorderingShift(); + const QuicPacketCount old_reordering_threshold = + loss_algorithm_.GetPacketReorderingThreshold(); + + loss_algorithm_.OnUserAgentIdKnown(); + + // Not owned. + TestLossTuner* test_tuner = new TestLossTuner( + /*forced_start_result=*/false, + LossDetectionParameters{ + /*reordering_shift=*/old_reordering_shift + 1, + /*reordering_threshold=*/old_reordering_threshold * 2}); + loss_algorithm_.SetLossDetectionTuner( + std::unique_ptr<LossDetectionTunerInterface>(test_tuner)); + + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(kELDT); + config.SetInitialReceivedConnectionOptions(connection_options); + loss_algorithm_.SetFromConfig(config, Perspective::IS_SERVER); + + // MinRtt was not available when SetFromConfig was called. + EXPECT_FALSE(test_tuner->start_called()); + EXPECT_EQ(old_reordering_shift, loss_algorithm_.GetPacketReorderingShift()); + EXPECT_EQ(old_reordering_threshold, + loss_algorithm_.GetPacketReorderingThreshold()); + + // Parameters should not change since test_tuner->Start() returns false. + loss_algorithm_.OnMinRttAvailable(); + EXPECT_TRUE(test_tuner->start_called()); + EXPECT_EQ(old_reordering_shift, loss_algorithm_.GetPacketReorderingShift()); + EXPECT_EQ(old_reordering_threshold, + loss_algorithm_.GetPacketReorderingThreshold()); +} + } // namespace } // namespace test } // namespace quic 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 0f17d121ca0..af6b54c5863 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 @@ -4,6 +4,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/certificate_view.h" +#include <algorithm> #include <cstdint> #include <memory> #include <string> @@ -17,6 +18,8 @@ #include "third_party/boringssl/src/include/openssl/rsa.h" #include "third_party/boringssl/src/include/openssl/ssl.h" #include "net/third_party/quiche/src/quic/core/crypto/boring_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" @@ -24,10 +27,17 @@ #include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h" #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" #include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_time_utils.h" +#include "net/third_party/quiche/src/common/quiche_data_reader.h" + +namespace { + +using ::quiche::QuicheOptional; +using ::quiche::QuicheStringPiece; +using ::quiche::QuicheTextUtils; // The literals below were encoded using `ascii2der | xxd -i`. The comments // above the literals are the contents in the der2ascii syntax. -namespace { // X.509 version 3 (version numbering starts with zero). // INTEGER { 2 } @@ -94,22 +104,54 @@ PublicKeyType PublicKeyTypeFromSignatureAlgorithm( namespace quic { +QuicheOptional<quic::QuicWallTime> ParseDerTime(unsigned tag, + QuicheStringPiece payload) { + if (tag != CBS_ASN1_GENERALIZEDTIME && tag != CBS_ASN1_UTCTIME) { + QUIC_BUG << "Invalid tag supplied for a DER timestamp"; + return QUICHE_NULLOPT; + } + + const size_t year_length = tag == CBS_ASN1_GENERALIZEDTIME ? 4 : 2; + uint64_t year, month, day, hour, minute, second; + quiche::QuicheDataReader reader(payload); + if (!reader.ReadDecimal64(year_length, &year) || + !reader.ReadDecimal64(2, &month) || !reader.ReadDecimal64(2, &day) || + !reader.ReadDecimal64(2, &hour) || !reader.ReadDecimal64(2, &minute) || + !reader.ReadDecimal64(2, &second) || + reader.ReadRemainingPayload() != "Z") { + QUIC_DLOG(WARNING) << "Failed to parse the DER timestamp"; + return QUICHE_NULLOPT; + } + + if (tag == CBS_ASN1_UTCTIME) { + DCHECK_LE(year, 100u); + year += (year >= 50) ? 1900 : 2000; + } + + const QuicheOptional<int64_t> unix_time = + quiche::QuicheUtcDateTimeToUnixSeconds(year, month, day, hour, minute, + second); + if (!unix_time.has_value() || *unix_time < 0) { + return QUICHE_NULLOPT; + } + return QuicWallTime::FromUNIXSeconds(*unix_time); +} + PemReadResult ReadNextPemMessage(std::istream* input) { - constexpr quiche::QuicheStringPiece kPemBegin = "-----BEGIN "; - constexpr quiche::QuicheStringPiece kPemEnd = "-----END "; - constexpr quiche::QuicheStringPiece kPemDashes = "-----"; + constexpr QuicheStringPiece kPemBegin = "-----BEGIN "; + constexpr QuicheStringPiece kPemEnd = "-----END "; + constexpr QuicheStringPiece kPemDashes = "-----"; std::string line_buffer, encoded_message_contents, expected_end; bool pending_message = false; PemReadResult result; while (std::getline(*input, line_buffer)) { - quiche::QuicheStringPiece line(line_buffer); - quiche::QuicheTextUtils::RemoveLeadingAndTrailingWhitespace(&line); + QuicheStringPiece line(line_buffer); + QuicheTextUtils::RemoveLeadingAndTrailingWhitespace(&line); // Handle BEGIN lines. - if (!pending_message && - quiche::QuicheTextUtils::StartsWith(line, kPemBegin) && - quiche::QuicheTextUtils::EndsWith(line, kPemDashes)) { + if (!pending_message && QuicheTextUtils::StartsWith(line, kPemBegin) && + QuicheTextUtils::EndsWith(line, kPemDashes)) { result.type = std::string( line.substr(kPemBegin.size(), line.size() - kPemDashes.size() - kPemBegin.size())); @@ -120,8 +162,8 @@ PemReadResult ReadNextPemMessage(std::istream* input) { // Handle END lines. if (pending_message && line == expected_end) { - quiche::QuicheOptional<std::string> data = - quiche::QuicheTextUtils::Base64Decode(encoded_message_contents); + QuicheOptional<std::string> data = + QuicheTextUtils::Base64Decode(encoded_message_contents); if (data.has_value()) { result.status = PemReadResult::kOk; result.contents = data.value(); @@ -137,11 +179,11 @@ PemReadResult ReadNextPemMessage(std::istream* input) { } bool eof_reached = input->eof() && !pending_message; return PemReadResult{ - .status = (eof_reached ? PemReadResult::kEof : PemReadResult::kError)}; + (eof_reached ? PemReadResult::kEof : PemReadResult::kError), "", ""}; } std::unique_ptr<CertificateView> CertificateView::ParseSingleCertificate( - quiche::QuicheStringPiece certificate) { + QuicheStringPiece certificate) { std::unique_ptr<CertificateView> result(new CertificateView()); CBS top = StringPieceToCbs(certificate); @@ -215,6 +257,25 @@ std::unique_ptr<CertificateView> CertificateView::ParseSingleCertificate( return nullptr; } + unsigned not_before_tag, not_after_tag; + CBS not_before, not_after; + if (!CBS_get_any_asn1(&validity, ¬_before, ¬_before_tag) || + !CBS_get_any_asn1(&validity, ¬_after, ¬_after_tag) || + CBS_len(&validity) != 0) { + QUIC_DLOG(WARNING) << "Failed to extract the validity dates"; + return nullptr; + } + QuicheOptional<QuicWallTime> not_before_parsed = + ParseDerTime(not_before_tag, CbsToStringPiece(not_before)); + QuicheOptional<QuicWallTime> not_after_parsed = + ParseDerTime(not_after_tag, CbsToStringPiece(not_after)); + if (!not_before_parsed.has_value() || !not_after_parsed.has_value()) { + QUIC_DLOG(WARNING) << "Failed to parse validity dates"; + return nullptr; + } + result->validity_start_ = *not_before_parsed; + result->validity_end_ = *not_after_parsed; + result->public_key_.reset(EVP_parse_public_key(&spki)); if (result->public_key_ == nullptr) { QUIC_DLOG(WARNING) << "Failed to parse the public key"; @@ -286,7 +347,7 @@ bool CertificateView::ParseExtensions(CBS extensions) { return false; } - quiche::QuicheStringPiece alt_name = CbsToStringPiece(alt_name_cbs); + QuicheStringPiece alt_name = CbsToStringPiece(alt_name_cbs); QuicIpAddress ip_address; // GeneralName ::= CHOICE { switch (alt_name_tag) { @@ -306,8 +367,8 @@ bool CertificateView::ParseExtensions(CBS extensions) { break; default: - QUIC_DLOG(WARNING) << "Invalid subjectAltName tag"; - return false; + QUIC_DLOG(INFO) << "Unknown subjectAltName tag " << alt_name_tag; + continue; } } } @@ -355,8 +416,8 @@ bool CertificateView::ValidatePublicKeyParameters() { } } -bool CertificateView::VerifySignature(quiche::QuicheStringPiece data, - quiche::QuicheStringPiece signature, +bool CertificateView::VerifySignature(QuicheStringPiece data, + QuicheStringPiece signature, uint16_t signature_algorithm) const { if (PublicKeyTypeFromSignatureAlgorithm(signature_algorithm) != PublicKeyTypeFromKey(public_key_.get())) { @@ -386,7 +447,7 @@ bool CertificateView::VerifySignature(quiche::QuicheStringPiece data, } std::unique_ptr<CertificatePrivateKey> CertificatePrivateKey::LoadFromDer( - quiche::QuicheStringPiece private_key) { + QuicheStringPiece private_key) { std::unique_ptr<CertificatePrivateKey> result(new CertificatePrivateKey()); CBS private_key_cbs = StringPieceToCbs(private_key); result->private_key_.reset(EVP_parse_private_key(&private_key_cbs)); @@ -423,7 +484,7 @@ std::unique_ptr<CertificatePrivateKey> CertificatePrivateKey::LoadPemFromStream( return nullptr; } -std::string CertificatePrivateKey::Sign(quiche::QuicheStringPiece input, +std::string CertificatePrivateKey::Sign(QuicheStringPiece input, uint16_t signature_algorithm) { if (PublicKeyTypeFromSignatureAlgorithm(signature_algorithm) != PublicKeyTypeFromKey(private_key_.get())) { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/certificate_view.h b/chromium/net/third_party/quiche/src/quic/core/crypto/certificate_view.h index 286226d7e20..13bb5ff14ae 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/certificate_view.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/certificate_view.h @@ -13,8 +13,11 @@ #include "third_party/boringssl/src/include/openssl/bytestring.h" #include "third_party/boringssl/src/include/openssl/evp.h" #include "net/third_party/quiche/src/quic/core/crypto/boring_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_optional.h" #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" namespace quic { @@ -46,6 +49,8 @@ class QUIC_EXPORT_PRIVATE CertificateView { // without parsing them. Returns an empty vector if any parsing error occurs. static std::vector<std::string> LoadPemFromStream(std::istream* input); + QuicWallTime validity_start() const { return validity_start_; } + QuicWallTime validity_end() const { return validity_end_; } const EVP_PKEY* public_key() const { return public_key_.get(); } const std::vector<quiche::QuicheStringPiece>& subject_alt_name_domains() @@ -64,6 +69,9 @@ class QUIC_EXPORT_PRIVATE CertificateView { private: CertificateView() = default; + QuicWallTime validity_start_ = QuicWallTime::Zero(); + QuicWallTime validity_end_ = QuicWallTime::Zero(); + // Public key parsed from SPKI. bssl::UniquePtr<EVP_PKEY> public_key_; @@ -103,6 +111,12 @@ class QUIC_EXPORT_PRIVATE CertificatePrivateKey { bssl::UniquePtr<EVP_PKEY> private_key_; }; +// Parses a DER time based on the specified ASN.1 tag. Exposed primarily for +// testing. +QUIC_EXPORT_PRIVATE quiche::QuicheOptional<quic::QuicWallTime> ParseDerTime( + unsigned tag, + quiche::QuicheStringPiece payload); + } // namespace quic #endif // QUICHE_QUIC_CORE_CRYPTO_CERTIFICATE_VIEW_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/certificate_view_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/certificate_view_test.cc index b0b52f14f3b..ad512143243 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/certificate_view_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/certificate_view_test.cc @@ -8,19 +8,23 @@ #include <sstream> #include "third_party/boringssl/src/include/openssl/base.h" +#include "third_party/boringssl/src/include/openssl/bytestring.h" #include "third_party/boringssl/src/include/openssl/evp.h" #include "third_party/boringssl/src/include/openssl/ssl.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" #include "net/third_party/quiche/src/quic/test_tools/test_certificates.h" #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_time_utils.h" namespace quic { namespace test { namespace { -using testing::ElementsAre; -using testing::HasSubstr; +using ::testing::ElementsAre; +using ::testing::HasSubstr; +using ::testing::Optional; TEST(CertificateViewTest, PemParser) { std::stringstream stream(kTestCertificatePem); @@ -45,6 +49,24 @@ TEST(CertificateViewTest, Parse) { EXPECT_THAT(view->subject_alt_name_ips(), ElementsAre(QuicIpAddress::Loopback4())); EXPECT_EQ(EVP_PKEY_id(view->public_key()), EVP_PKEY_RSA); + + const QuicWallTime validity_start = QuicWallTime::FromUNIXSeconds( + *quiche::QuicheUtcDateTimeToUnixSeconds(2020, 1, 30, 18, 13, 59)); + EXPECT_EQ(view->validity_start(), validity_start); + const QuicWallTime validity_end = QuicWallTime::FromUNIXSeconds( + *quiche::QuicheUtcDateTimeToUnixSeconds(2020, 2, 2, 18, 13, 59)); + EXPECT_EQ(view->validity_end(), validity_end); +} + +TEST(CertificateViewTest, ParseCertWithUnknownSanType) { + std::stringstream stream(kTestCertWithUnknownSanTypePem); + PemReadResult result = ReadNextPemMessage(&stream); + EXPECT_EQ(result.status, PemReadResult::kOk); + EXPECT_EQ(result.type, "CERTIFICATE"); + + std::unique_ptr<CertificateView> view = + CertificateView::ParseSingleCertificate(result.contents); + EXPECT_TRUE(view != nullptr); } TEST(CertificateViewTest, PemSingleCertificate) { @@ -109,6 +131,47 @@ TEST(CertificateViewTest, PrivateKeyPem) { EXPECT_TRUE(legacy_key->MatchesPublicKey(*view)); } +TEST(CertificateViewTest, DerTime) { + EXPECT_THAT(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024Z"), + Optional(QuicWallTime::FromUNIXSeconds(24))); + EXPECT_THAT(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19710101000024Z"), + Optional(QuicWallTime::FromUNIXSeconds(365 * 86400 + 24))); + EXPECT_THAT(ParseDerTime(CBS_ASN1_UTCTIME, "700101000024Z"), + Optional(QuicWallTime::FromUNIXSeconds(24))); + EXPECT_TRUE(ParseDerTime(CBS_ASN1_UTCTIME, "200101000024Z").has_value()); + + EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, ""), QUICHE_NULLOPT); + EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024.001Z"), + QUICHE_NULLOPT); + EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024Q"), + QUICHE_NULLOPT); + EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024-0500"), + QUICHE_NULLOPT); + EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "700101000024ZZ"), + QUICHE_NULLOPT); + EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024.00Z"), + QUICHE_NULLOPT); + EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024.Z"), + QUICHE_NULLOPT); + EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "197O0101000024Z"), + QUICHE_NULLOPT); + EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024.0O1Z"), + QUICHE_NULLOPT); + EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "-9700101000024Z"), + QUICHE_NULLOPT); + EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "1970-101000024Z"), + QUICHE_NULLOPT); + + EXPECT_TRUE(ParseDerTime(CBS_ASN1_UTCTIME, "490101000024Z").has_value()); + // This should parse as 1950, which predates UNIX epoch. + EXPECT_FALSE(ParseDerTime(CBS_ASN1_UTCTIME, "500101000024Z").has_value()); + + EXPECT_THAT(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101230000Z"), + Optional(QuicWallTime::FromUNIXSeconds(23 * 3600))); + EXPECT_EQ(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101240000Z"), + QUICHE_NULLOPT); +} + } // namespace } // namespace test } // namespace quic 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 172fd822d0b..4beec0bdf90 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 @@ -125,6 +125,8 @@ const QuicTag kB2CL = TAG('B', '2', 'C', 'L'); // For BBRv2, allow PROBE_BW // cwnd to be below BDP + ack // height. const QuicTag kB2LO = TAG('B', '2', 'L', 'O'); // Ignore inflight_lo in BBR2 +const QuicTag kB2HI = TAG('B', '2', 'H', 'I'); // Limit inflight_hi reduction + // based on CWND. const QuicTag kBSAO = TAG('B', 'S', 'A', 'O'); // Avoid Overestimation in // Bandwidth Sampler with ack // aggregation @@ -160,7 +162,13 @@ const QuicTag kACKQ = TAG('A', 'C', 'K', 'Q'); // Send an immediate ack after // 1 RTT of not receiving. const QuicTag kSSLR = TAG('S', 'S', 'L', 'R'); // Slow Start Large Reduction. const QuicTag kNPRR = TAG('N', 'P', 'R', 'R'); // Pace at unity instead of PRR +const QuicTag k2RTO = TAG('2', 'R', 'T', 'O'); // Close connection on 2 RTOs +const QuicTag k3RTO = TAG('3', 'R', 'T', 'O'); // Close connection on 3 RTOs +const QuicTag k4RTO = TAG('4', 'R', 'T', 'O'); // Close connection on 4 RTOs const QuicTag k5RTO = TAG('5', 'R', 'T', 'O'); // Close connection on 5 RTOs +const QuicTag k6RTO = TAG('6', 'R', 'T', 'O'); // Close connection on 6 RTOs +const QuicTag kCBHD = TAG('C', 'B', 'H', 'D'); // Client only blackhole + // detection. const QuicTag kCONH = TAG('C', 'O', 'N', 'H'); // Conservative Handshake // Retransmissions. const QuicTag kLFAK = TAG('L', 'F', 'A', 'K'); // Don't invoke FACK on the @@ -225,6 +233,8 @@ const QuicTag kPLE2 = TAG('P', 'L', 'E', '2'); // Arm the 1st PTO with // earliest in flight sent time // and at least 1.5*srtt from // last sent packet. +const QuicTag kAPTO = TAG('A', 'P', 'T', 'O'); // Use 1.5 * initial RTT before + // any RTT sample is available. const QuicTag kELDT = TAG('E', 'L', 'D', 'T'); // Enable Loss Detection Tuning @@ -329,6 +339,9 @@ const QuicTag kCFCW = TAG('C', 'F', 'C', 'W'); // Initial session/connection // flow control receive window. const QuicTag kUAID = TAG('U', 'A', 'I', 'D'); // Client's User Agent ID. const QuicTag kXLCT = TAG('X', 'L', 'C', 'T'); // Expected leaf certificate. +const QuicTag kQLVE = TAG('Q', 'L', 'V', 'E'); // Legacy Version + // Encapsulation. +const QuicTag kQNZR = TAG('Q', 'N', 'Z', 'R'); // Turn off QUIC crypto 0-RTT. const QuicTag kMAD = TAG('M', 'A', 'D', 0); // Max Ack Delay (IETF QUIC) 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 ee9dc53cf64..58d39b8f7d6 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 @@ -508,6 +508,12 @@ TEST_P(CryptoServerTest, RejectTooLargeButValidSTK) { } TEST_P(CryptoServerTest, TooSmall) { + if (GetQuicReloadableFlag(quic_dont_pad_chlo)) { + // This test validates that non-padded CHLOs are rejected, it cannot pass + // when the padding is no longer required. + // TODO(dschinazi) remove this test when we deprecate quic_dont_pad_chlo. + return; + } ShouldFailMentioning( "too small", crypto_test_utils::CreateCHLO( {{"PDMD", "X509"}, {"VER\0", client_version_string_}})); 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 b6c6b7ae434..3127edec87b 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 @@ -117,10 +117,16 @@ namespace { // Salt from https://tools.ietf.org/html/draft-ietf-quic-tls-25#section-5.2 // and https://tools.ietf.org/html/draft-ietf-quic-tls-27#section-5.2 +// and https://tools.ietf.org/html/draft-ietf-quic-tls-28#section-5.2 const uint8_t kDraft25InitialSalt[] = {0xc3, 0xee, 0xf7, 0x12, 0xc7, 0x2e, 0xbb, 0x5a, 0x11, 0xa7, 0xd2, 0x43, 0x2b, 0xb4, 0x63, 0x65, 0xbe, 0xf9, 0xf5, 0x02}; +// Salt from https://tools.ietf.org/html/draft-ietf-quic-tls-29#section-5.2 +const uint8_t kDraft29InitialSalt[] = {0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, + 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, + 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99}; + // Salts used by deployed versions of QUIC. When introducing a new version, // generate a new salt by running `openssl rand -hex 20`. @@ -135,7 +141,7 @@ const uint8_t kT050Salt[] = {0x7f, 0xf5, 0x79, 0xe5, 0xac, 0xd0, 0x72, const uint8_t* InitialSaltForVersion(const ParsedQuicVersion& version, size_t* out_len) { - static_assert(SupportedVersions().size() == 8u, + static_assert(SupportedVersions().size() == 9u, "Supported versions out of sync with initial encryption salts"); switch (version.handshake_protocol) { case PROTOCOL_QUIC_CRYPTO: @@ -166,6 +172,9 @@ const uint8_t* InitialSaltForVersion(const ParsedQuicVersion& version, // draft-27 uses the same salt as draft-25. *out_len = QUICHE_ARRAYSIZE(kDraft25InitialSalt); return kDraft25InitialSalt; + case QUIC_VERSION_IETF_DRAFT_29: + *out_len = QUICHE_ARRAYSIZE(kDraft29InitialSalt); + return kDraft29InitialSalt; default: QUIC_BUG << "No initial obfuscation salt for version " << version; } @@ -183,11 +192,20 @@ const char kPreSharedKeyLabel[] = "QUIC PSK"; // Retry Integrity Protection Keys and Nonces. // https://tools.ietf.org/html/draft-ietf-quic-tls-25#section-5.8 // https://tools.ietf.org/html/draft-ietf-quic-tls-27#section-5.8 +// https://tools.ietf.org/html/draft-ietf-quic-tls-28#section-5.8 const uint8_t kDraft25RetryIntegrityKey[] = {0x4d, 0x32, 0xec, 0xdb, 0x2a, 0x21, 0x33, 0xc8, 0x41, 0xe4, 0x04, 0x3d, 0xf2, 0x7d, 0x44, 0x30}; const uint8_t kDraft25RetryIntegrityNonce[] = { 0x4d, 0x16, 0x11, 0xd0, 0x55, 0x13, 0xa5, 0x52, 0xc5, 0x87, 0xd5, 0x75}; + +// https://tools.ietf.org/html/draft-ietf-quic-tls-29#section-5.8 +const uint8_t kDraft29RetryIntegrityKey[] = {0xcc, 0xce, 0x18, 0x7e, 0xd0, 0x9a, + 0x09, 0xd0, 0x57, 0x28, 0x15, 0x5a, + 0x6c, 0xb9, 0x6b, 0xe1}; +const uint8_t kDraft29RetryIntegrityNonce[] = { + 0xe5, 0x49, 0x30, 0xf9, 0x7f, 0x21, 0x36, 0xf0, 0x53, 0x0a, 0x8c, 0x1c}; + // Keys used by Google versions of QUIC. When introducing a new version, // generate a new key by running `openssl rand -hex 16`. const uint8_t kT050RetryIntegrityKey[] = {0xc9, 0x2d, 0x32, 0x3d, 0x9c, 0xe3, @@ -227,6 +245,15 @@ bool RetryIntegrityKeysForVersion(const ParsedQuicVersion& version, QUICHE_ARRAYSIZE(kDraft25RetryIntegrityNonce)); return true; } + if (version == ParsedQuicVersion::Draft29()) { + *key = quiche::QuicheStringPiece( + reinterpret_cast<const char*>(kDraft29RetryIntegrityKey), + QUICHE_ARRAYSIZE(kDraft29RetryIntegrityKey)); + *nonce = quiche::QuicheStringPiece( + reinterpret_cast<const char*>(kDraft29RetryIntegrityNonce), + QUICHE_ARRAYSIZE(kDraft29RetryIntegrityNonce)); + return true; + } QUIC_BUG << "Attempted to get retry integrity keys for version " << version; return false; } diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.cc index 6afc65f557a..e432682bddd 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.cc @@ -104,8 +104,8 @@ bool ProofSourceX509::AddCertificateChain( } certificates_.push_front(Certificate{ - .chain = chain, - .key = std::move(key), + chain, + std::move(key), }); Certificate* certificate = &certificates_.front(); 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 7f8edc22cfe..9314e99f293 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 @@ -67,7 +67,10 @@ QuicCryptoClientConfig::QuicCryptoClientConfig( std::unique_ptr<SessionCache> session_cache) : proof_verifier_(std::move(proof_verifier)), session_cache_(std::move(session_cache)), - ssl_ctx_(TlsClientConnection::CreateSslCtx()) { + enable_zero_rtt_for_tls_( + GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls)), + ssl_ctx_(TlsClientConnection::CreateSslCtx(enable_zero_rtt_for_tls_)), + disable_chlo_padding_(GetQuicReloadableFlag(quic_dont_pad_chlo)) { DCHECK(proof_verifier_.get()); SetDefaults(); } @@ -132,16 +135,6 @@ QuicCryptoClientConfig::CachedState::GetServerConfig() const { return scfg_.get(); } -void QuicCryptoClientConfig::CachedState::add_server_designated_connection_id( - QuicConnectionId connection_id) { - server_designated_connection_ids_.push(connection_id); -} - -bool QuicCryptoClientConfig::CachedState::has_server_designated_connection_id() - const { - return !server_designated_connection_ids_.empty(); -} - void QuicCryptoClientConfig::CachedState::add_server_nonce( const std::string& server_nonce) { server_nonces_.push(server_nonce); @@ -204,9 +197,6 @@ void QuicCryptoClientConfig::CachedState::InvalidateServerConfig() { server_config_.clear(); scfg_.reset(); SetProofInvalid(); - QuicQueue<QuicConnectionId> empty_queue; - using std::swap; - swap(server_designated_connection_ids_, empty_queue); } void QuicCryptoClientConfig::CachedState::SetProof( @@ -249,9 +239,6 @@ void QuicCryptoClientConfig::CachedState::Clear() { proof_verify_details_.reset(); scfg_.reset(); ++generation_counter_; - QuicQueue<QuicConnectionId> empty_queue; - using std::swap; - swap(server_designated_connection_ids_, empty_queue); } void QuicCryptoClientConfig::CachedState::ClearProof() { @@ -370,7 +357,6 @@ void QuicCryptoClientConfig::CachedState::InitializeFrom( chlo_hash_ = other.chlo_hash_; server_config_sig_ = other.server_config_sig_; server_config_valid_ = other.server_config_valid_; - server_designated_connection_ids_ = other.server_designated_connection_ids_; expiration_time_ = other.expiration_time_; if (other.proof_verify_details_ != nullptr) { proof_verify_details_.reset(other.proof_verify_details_->Clone()); @@ -378,18 +364,6 @@ void QuicCryptoClientConfig::CachedState::InitializeFrom( ++generation_counter_; } -QuicConnectionId -QuicCryptoClientConfig::CachedState::GetNextServerDesignatedConnectionId() { - if (server_designated_connection_ids_.empty()) { - QUIC_BUG - << "Attempting to consume a connection id that was never designated."; - return EmptyQuicConnectionId(); - } - const QuicConnectionId next_id = server_designated_connection_ids_.front(); - server_designated_connection_ids_.pop(); - return next_id; -} - std::string QuicCryptoClientConfig::CachedState::GetNextServerNonce() { if (server_nonces_.empty()) { QUIC_BUG @@ -447,7 +421,7 @@ void QuicCryptoClientConfig::FillInchoateClientHello( CryptoHandshakeMessage* out) const { out->set_tag(kCHLO); // TODO(rch): Remove this when we remove quic_use_chlo_packet_size flag. - if (pad_inchoate_hello_) { + if (pad_inchoate_hello_ && !disable_chlo_padding_) { out->set_minimum_size(kClientHelloMinimumSize); } else { out->set_minimum_size(1); @@ -536,7 +510,7 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( FillInchoateClientHello(server_id, preferred_version, cached, rand, /* demand_x509_proof= */ true, out_params, out); - if (pad_full_hello_) { + if (pad_full_hello_ && !disable_chlo_padding_) { out->set_minimum_size(kClientHelloMinimumSize); } else { out->set_minimum_size(1); diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h index e3867c8e7b2..ce0efe530c7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h @@ -76,6 +76,10 @@ class QUIC_EXPORT_PRIVATE SessionCache { virtual std::unique_ptr<QuicResumptionState> Lookup( const QuicServerId& server_id, const SSL_CTX* ctx) = 0; + + // Called when 0-RTT is rejected. Disables early data for all the TLS tickets + // associated with |server_id|. + virtual void ClearEarlyData(const QuicServerId& server_id) = 0; }; // QuicCryptoClientConfig contains crypto-related configuration settings for a @@ -171,20 +175,6 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { void set_cert_sct(quiche::QuicheStringPiece cert_sct); - // Adds the connection ID to the queue of server-designated connection-ids. - void add_server_designated_connection_id(QuicConnectionId connection_id); - - // If true, the crypto config contains at least one connection ID specified - // by the server, and the client should use one of these IDs when initiating - // the next connection. - bool has_server_designated_connection_id() const; - - // This function should only be called when - // has_server_designated_connection_id is true. Returns the next - // connection_id specified by the server and removes it from the - // queue of ids. - QuicConnectionId GetNextServerDesignatedConnectionId(); - // Adds the servernonce to the queue of server nonces. void add_server_nonce(const std::string& server_nonce); @@ -238,10 +228,6 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { // scfg contains the cached, parsed value of |server_config|. mutable std::unique_ptr<CryptoHandshakeMessage> scfg_; - // TODO(jokulik): Consider using a hash-set as extra book-keeping to ensure - // that no connection-id is added twice. Also, consider keeping the server - // nonces and connection_ids together in one queue. - QuicQueue<QuicConnectionId> server_designated_connection_ids_; QuicQueue<std::string> server_nonces_; }; @@ -368,6 +354,8 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { void set_proof_source(std::unique_ptr<ProofSource> proof_source); SSL_CTX* ssl_ctx() const; + bool early_data_enabled_for_tls() const { return enable_zero_rtt_for_tls_; } + // Initialize the CachedState from |canonical_crypto_config| for the // |canonical_server_id| as the initial CachedState for |server_id|. We will // copy config data only if |canonical_crypto_config| has valid proof. @@ -409,6 +397,10 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { bool pad_full_hello() const { return pad_full_hello_; } void set_pad_full_hello(bool new_value) { pad_full_hello_ = new_value; } + void set_disable_chlo_padding(bool disabled) { + disable_chlo_padding_ = disabled; + } + private: // Sets the members to reasonable, default values. void SetDefaults(); @@ -450,6 +442,9 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { std::unique_ptr<ProofVerifier> proof_verifier_; std::unique_ptr<SessionCache> session_cache_; std::unique_ptr<ProofSource> proof_source_; + + // Latched value of reloadable flag quic_enable_zero_rtt_for_tls + bool enable_zero_rtt_for_tls_; bssl::UniquePtr<SSL_CTX> ssl_ctx_; // The |user_agent_id_| passed in QUIC's CHLO message. @@ -475,6 +470,7 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { // other means of verifying the client. bool pad_inchoate_hello_ = true; bool pad_full_hello_ = true; + bool disable_chlo_padding_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config_test.cc index 4b6dcd71bcb..c995e8a87c0 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config_test.cc @@ -81,46 +81,6 @@ TEST_F(QuicCryptoClientConfigTest, CachedState_SetProofVerifyDetails) { EXPECT_EQ(details, state.proof_verify_details()); } -TEST_F(QuicCryptoClientConfigTest, CachedState_ServerDesignatedConnectionId) { - QuicCryptoClientConfig::CachedState state; - EXPECT_FALSE(state.has_server_designated_connection_id()); - - uint64_t conn_id = 1234; - QuicConnectionId connection_id = TestConnectionId(conn_id); - state.add_server_designated_connection_id(connection_id); - EXPECT_TRUE(state.has_server_designated_connection_id()); - EXPECT_EQ(connection_id, state.GetNextServerDesignatedConnectionId()); - EXPECT_FALSE(state.has_server_designated_connection_id()); - - // Allow the ID to be set multiple times. It's unusual that this would - // happen, but not impossible. - connection_id = TestConnectionId(++conn_id); - state.add_server_designated_connection_id(connection_id); - EXPECT_TRUE(state.has_server_designated_connection_id()); - EXPECT_EQ(connection_id, state.GetNextServerDesignatedConnectionId()); - connection_id = TestConnectionId(++conn_id); - state.add_server_designated_connection_id(connection_id); - EXPECT_EQ(connection_id, state.GetNextServerDesignatedConnectionId()); - EXPECT_FALSE(state.has_server_designated_connection_id()); - - // Test FIFO behavior. - const QuicConnectionId first_cid = TestConnectionId(0xdeadbeef); - const QuicConnectionId second_cid = TestConnectionId(0xfeedbead); - state.add_server_designated_connection_id(first_cid); - state.add_server_designated_connection_id(second_cid); - EXPECT_TRUE(state.has_server_designated_connection_id()); - EXPECT_EQ(first_cid, state.GetNextServerDesignatedConnectionId()); - EXPECT_EQ(second_cid, state.GetNextServerDesignatedConnectionId()); -} - -TEST_F(QuicCryptoClientConfigTest, CachedState_ServerIdConsumedBeforeSet) { - QuicCryptoClientConfig::CachedState state; - EXPECT_FALSE(state.has_server_designated_connection_id()); - EXPECT_QUIC_BUG(state.GetNextServerDesignatedConnectionId(), - "Attempting to consume a connection id " - "that was never designated."); -} - TEST_F(QuicCryptoClientConfigTest, CachedState_ServerNonce) { QuicCryptoClientConfig::CachedState state; EXPECT_FALSE(state.has_server_nonce()); @@ -170,7 +130,6 @@ TEST_F(QuicCryptoClientConfigTest, CachedState_InitializeFrom) { EXPECT_EQ(state.source_address_token(), other.source_address_token()); EXPECT_EQ(state.certs(), other.certs()); EXPECT_EQ(1u, other.generation_counter()); - EXPECT_FALSE(state.has_server_designated_connection_id()); EXPECT_FALSE(state.has_server_nonce()); } @@ -200,7 +159,11 @@ TEST_F(QuicCryptoClientConfigTest, InchoateChlo) { EXPECT_TRUE(msg.GetStringPiece(kALPN, &alpn)); EXPECT_EQ("hq", alpn); - EXPECT_EQ(msg.minimum_size(), 1024u); + if (GetQuicReloadableFlag(quic_dont_pad_chlo)) { + EXPECT_EQ(msg.minimum_size(), 1u); + } else { + EXPECT_EQ(msg.minimum_size(), 1024u); + } } TEST_F(QuicCryptoClientConfigTest, InchoateChloIsNotPadded) { @@ -532,7 +495,6 @@ TEST_F(QuicCryptoClientConfigTest, ProcessReject) { AllSupportedTransportVersions().front(), "", &cached, out_params, &error), IsQuicNoError()); - EXPECT_FALSE(cached.has_server_designated_connection_id()); EXPECT_FALSE(cached.has_server_nonce()); } 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 9cb089bf0e8..c07b8d3bf11 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 @@ -1211,10 +1211,14 @@ void QuicCryptoServerConfig::EvaluateClientHello( const CryptoHandshakeMessage& client_hello = client_hello_state->client_hello; ClientHelloInfo* info = &(client_hello_state->info); - if (validate_chlo_size_ && client_hello.size() < kClientHelloMinimumSize) { - helper.ValidationComplete(QUIC_CRYPTO_INVALID_VALUE_LENGTH, - "Client hello too small", nullptr); - return; + if (GetQuicReloadableFlag(quic_dont_pad_chlo)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_dont_pad_chlo, 1, 2); + } else { + if (validate_chlo_size_ && client_hello.size() < kClientHelloMinimumSize) { + helper.ValidationComplete(QUIC_CRYPTO_INVALID_VALUE_LENGTH, + "Client hello too small", nullptr); + return; + } } if (client_hello.GetStringPiece(kSNI, &info->sni) && diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.cc index 7d112245b3c..79088473bbc 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.cc @@ -11,7 +11,8 @@ TlsClientConnection::TlsClientConnection(SSL_CTX* ssl_ctx, Delegate* delegate) delegate_(delegate) {} // static -bssl::UniquePtr<SSL_CTX> TlsClientConnection::CreateSslCtx() { +bssl::UniquePtr<SSL_CTX> TlsClientConnection::CreateSslCtx( + bool enable_early_data) { bssl::UniquePtr<SSL_CTX> ssl_ctx = TlsConnection::CreateSslCtx(); // Configure certificate verification. SSL_CTX_set_custom_verify(ssl_ctx.get(), SSL_VERIFY_PEER, &VerifyCallback); @@ -22,6 +23,8 @@ bssl::UniquePtr<SSL_CTX> TlsClientConnection::CreateSslCtx() { SSL_CTX_set_session_cache_mode( ssl_ctx.get(), SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL); SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback); + + SSL_CTX_set_early_data_enabled(ssl_ctx.get(), enable_early_data); return ssl_ctx; } diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.h b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.h index 035f420a835..a7ef209792f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_client_connection.h @@ -39,7 +39,7 @@ class QUIC_EXPORT_PRIVATE TlsClientConnection : public TlsConnection { TlsClientConnection(SSL_CTX* ssl_ctx, Delegate* delegate); // Creates and configures an SSL_CTX that is appropriate for clients to use. - static bssl::UniquePtr<SSL_CTX> CreateSslCtx(); + static bssl::UniquePtr<SSL_CTX> CreateSslCtx(bool enable_early_data); private: // Registered as the callback for SSL_CTX_set_custom_verify. The diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.cc index 75d28c55d60..8e4d391db4b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.cc @@ -114,28 +114,6 @@ const SSL_QUIC_METHOD TlsConnection::kSslQuicMethod{ TlsConnection::SendAlertCallback}; // static -int TlsConnection::SetEncryptionSecretCallback( - SSL* ssl, - enum ssl_encryption_level_t level, - const uint8_t* read_key, - const uint8_t* write_key, - size_t key_length) { - // TODO(nharper): replace these vectors and memcpys with spans (which - // unfortunately doesn't yet exist in quic/platform/api). - std::vector<uint8_t> read_secret(key_length), write_secret(key_length); - memcpy(read_secret.data(), read_key, key_length); - memcpy(write_secret.data(), write_key, key_length); - TlsConnection::Delegate* delegate = ConnectionFromSsl(ssl)->delegate_; - const SSL_CIPHER* cipher = SSL_get_pending_cipher(ssl); - delegate->SetWriteSecret(QuicEncryptionLevel(level), cipher, write_secret); - if (!delegate->SetReadSecret(QuicEncryptionLevel(level), cipher, - read_secret)) { - return 0; - } - return 1; -} - -// static int TlsConnection::SetReadSecretCallback(SSL* ssl, enum ssl_encryption_level_t level, const SSL_CIPHER* cipher, diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.h b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.h index d65f63c68e5..15a4f4f9a3a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_connection.h @@ -101,11 +101,6 @@ class QUIC_EXPORT_PRIVATE TlsConnection { static const SSL_QUIC_METHOD kSslQuicMethod; // The following static functions make up the members of kSslQuicMethod: - static int SetEncryptionSecretCallback(SSL* ssl, - enum ssl_encryption_level_t level, - const uint8_t* read_key, - const uint8_t* write_key, - size_t key_length); static int SetReadSecretCallback(SSL* ssl, enum ssl_encryption_level_t level, const SSL_CIPHER* cipher, diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.cc index 3e286adc84b..330a14d5ea2 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.cc @@ -10,6 +10,8 @@ #include <memory> #include <utility> +#include "third_party/boringssl/src/include/openssl/digest.h" +#include "third_party/boringssl/src/include/openssl/sha.h" #include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h" #include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h" #include "net/third_party/quiche/src/quic/core/quic_connection_id.h" @@ -31,8 +33,8 @@ namespace quic { // which parameter is encoded. The supported draft version is noted in // transport_parameters.h. enum TransportParameters::TransportParameterId : uint64_t { - kOriginalConnectionId = 0, - kIdleTimeout = 1, + kOriginalDestinationConnectionId = 0, + kMaxIdleTimeout = 1, kStatelessResetToken = 2, kMaxPacketSize = 3, kInitialMaxData = 4, @@ -43,15 +45,18 @@ enum TransportParameters::TransportParameterId : uint64_t { kInitialMaxStreamsUni = 9, kAckDelayExponent = 0xa, kMaxAckDelay = 0xb, - kDisableMigration = 0xc, + kDisableActiveMigration = 0xc, kPreferredAddress = 0xd, kActiveConnectionIdLimit = 0xe, + kInitialSourceConnectionId = 0xf, + kRetrySourceConnectionId = 0x10, kMaxDatagramFrameSize = 0x20, kInitialRoundTripTime = 0x3127, kGoogleConnectionOptions = 0x3128, kGoogleUserAgentId = 0x3129, + kGoogleSupportHandshakeDone = 0x312A, // Only used in T050. kGoogleQuicParam = 18257, // Used for non-standard Google-specific params. kGoogleQuicVersion = 18258, // Used to transmit version and supported_versions. @@ -74,14 +79,14 @@ constexpr uint64_t kDefaultActiveConnectionIdLimitTransportParam = 2; std::string TransportParameterIdToString( TransportParameters::TransportParameterId param_id) { switch (param_id) { - case TransportParameters::kOriginalConnectionId: - return "original_connection_id"; - case TransportParameters::kIdleTimeout: - return "idle_timeout"; + case TransportParameters::kOriginalDestinationConnectionId: + return "original_destination_connection_id"; + case TransportParameters::kMaxIdleTimeout: + return "max_idle_timeout"; case TransportParameters::kStatelessResetToken: return "stateless_reset_token"; case TransportParameters::kMaxPacketSize: - return "max_packet_size"; + return "max_udp_payload_size"; case TransportParameters::kInitialMaxData: return "initial_max_data"; case TransportParameters::kInitialMaxStreamDataBidiLocal: @@ -98,12 +103,16 @@ std::string TransportParameterIdToString( return "ack_delay_exponent"; case TransportParameters::kMaxAckDelay: return "max_ack_delay"; - case TransportParameters::kDisableMigration: - return "disable_migration"; + case TransportParameters::kDisableActiveMigration: + return "disable_active_migration"; case TransportParameters::kPreferredAddress: return "preferred_address"; case TransportParameters::kActiveConnectionIdLimit: return "active_connection_id_limit"; + case TransportParameters::kInitialSourceConnectionId: + return "initial_source_connection_id"; + case TransportParameters::kRetrySourceConnectionId: + return "retry_source_connection_id"; case TransportParameters::kMaxDatagramFrameSize: return "max_datagram_frame_size"; case TransportParameters::kInitialRoundTripTime: @@ -112,6 +121,8 @@ std::string TransportParameterIdToString( return "google_connection_options"; case TransportParameters::kGoogleUserAgentId: return "user_agent_id"; + case TransportParameters::kGoogleSupportHandshakeDone: + return "support_handshake_done"; case TransportParameters::kGoogleQuicParam: return "google"; case TransportParameters::kGoogleQuicVersion: @@ -123,8 +134,8 @@ std::string TransportParameterIdToString( bool TransportParameterIdIsKnown( TransportParameters::TransportParameterId param_id) { switch (param_id) { - case TransportParameters::kOriginalConnectionId: - case TransportParameters::kIdleTimeout: + case TransportParameters::kOriginalDestinationConnectionId: + case TransportParameters::kMaxIdleTimeout: case TransportParameters::kStatelessResetToken: case TransportParameters::kMaxPacketSize: case TransportParameters::kInitialMaxData: @@ -135,13 +146,16 @@ bool TransportParameterIdIsKnown( case TransportParameters::kInitialMaxStreamsUni: case TransportParameters::kAckDelayExponent: case TransportParameters::kMaxAckDelay: - case TransportParameters::kDisableMigration: + case TransportParameters::kDisableActiveMigration: case TransportParameters::kPreferredAddress: case TransportParameters::kActiveConnectionIdLimit: + case TransportParameters::kInitialSourceConnectionId: + case TransportParameters::kRetrySourceConnectionId: case TransportParameters::kMaxDatagramFrameSize: case TransportParameters::kInitialRoundTripTime: case TransportParameters::kGoogleConnectionOptions: case TransportParameters::kGoogleUserAgentId: + case TransportParameters::kGoogleSupportHandshakeDone: case TransportParameters::kGoogleQuicParam: case TransportParameters::kGoogleQuicVersion: return true; @@ -401,18 +415,18 @@ std::string TransportParameters::ToString() const { rv += " supported_versions " + QuicVersionLabelVectorToString(supported_versions); } - if (original_connection_id.has_value()) { - rv += " " + TransportParameterIdToString(kOriginalConnectionId) + " " + - original_connection_id.value().ToString(); + if (original_destination_connection_id.has_value()) { + rv += " " + TransportParameterIdToString(kOriginalDestinationConnectionId) + + " " + original_destination_connection_id.value().ToString(); } - rv += idle_timeout_milliseconds.ToString(/*for_use_in_list=*/true); + rv += max_idle_timeout_ms.ToString(/*for_use_in_list=*/true); if (!stateless_reset_token.empty()) { rv += " " + TransportParameterIdToString(kStatelessResetToken) + " " + quiche::QuicheTextUtils::HexEncode( reinterpret_cast<const char*>(stateless_reset_token.data()), stateless_reset_token.size()); } - rv += max_packet_size.ToString(/*for_use_in_list=*/true); + rv += max_udp_payload_size.ToString(/*for_use_in_list=*/true); rv += initial_max_data.ToString(/*for_use_in_list=*/true); rv += initial_max_stream_data_bidi_local.ToString(/*for_use_in_list=*/true); rv += initial_max_stream_data_bidi_remote.ToString(/*for_use_in_list=*/true); @@ -421,14 +435,22 @@ std::string TransportParameters::ToString() const { rv += initial_max_streams_uni.ToString(/*for_use_in_list=*/true); rv += ack_delay_exponent.ToString(/*for_use_in_list=*/true); rv += max_ack_delay.ToString(/*for_use_in_list=*/true); - if (disable_migration) { - rv += " " + TransportParameterIdToString(kDisableMigration); + if (disable_active_migration) { + rv += " " + TransportParameterIdToString(kDisableActiveMigration); } if (preferred_address) { rv += " " + TransportParameterIdToString(kPreferredAddress) + " " + preferred_address->ToString(); } rv += active_connection_id_limit.ToString(/*for_use_in_list=*/true); + if (initial_source_connection_id.has_value()) { + rv += " " + TransportParameterIdToString(kInitialSourceConnectionId) + " " + + initial_source_connection_id.value().ToString(); + } + if (retry_source_connection_id.has_value()) { + rv += " " + TransportParameterIdToString(kRetrySourceConnectionId) + " " + + retry_source_connection_id.value().ToString(); + } rv += max_datagram_frame_size.ToString(/*for_use_in_list=*/true); rv += initial_round_trip_time_us.ToString(/*for_use_in_list=*/true); if (google_connection_options.has_value()) { @@ -447,12 +469,24 @@ std::string TransportParameters::ToString() const { rv += " " + TransportParameterIdToString(kGoogleUserAgentId) + " \"" + user_agent_id.value() + "\""; } + if (support_handshake_done) { + rv += " " + TransportParameterIdToString(kGoogleSupportHandshakeDone); + } if (google_quic_params) { rv += " " + TransportParameterIdToString(kGoogleQuicParam); } for (const auto& kv : custom_parameters) { rv += " 0x" + quiche::QuicheTextUtils::Hex(static_cast<uint32_t>(kv.first)); - rv += "=" + quiche::QuicheTextUtils::HexEncode(kv.second); + rv += "="; + static constexpr size_t kMaxPrintableLength = 32; + if (kv.second.length() <= kMaxPrintableLength) { + rv += quiche::QuicheTextUtils::HexEncode(kv.second); + } else { + quiche::QuicheStringPiece truncated(kv.second.data(), + kMaxPrintableLength); + rv += quiche::QuicheStrCat(quiche::QuicheTextUtils::HexEncode(truncated), + "...(length ", kv.second.length(), ")"); + } } rv += "]"; return rv; @@ -460,11 +494,11 @@ std::string TransportParameters::ToString() const { TransportParameters::TransportParameters() : version(0), - idle_timeout_milliseconds(kIdleTimeout), - max_packet_size(kMaxPacketSize, - kDefaultMaxPacketSizeTransportParam, - kMinMaxPacketSizeTransportParam, - kVarInt62MaxValue), + max_idle_timeout_ms(kMaxIdleTimeout), + max_udp_payload_size(kMaxPacketSize, + kDefaultMaxPacketSizeTransportParam, + kMinMaxPacketSizeTransportParam, + kVarInt62MaxValue), initial_max_data(kInitialMaxData), initial_max_stream_data_bidi_local(kInitialMaxStreamDataBidiLocal), initial_max_stream_data_bidi_remote(kInitialMaxStreamDataBidiRemote), @@ -479,13 +513,14 @@ TransportParameters::TransportParameters() kDefaultMaxAckDelayTransportParam, 0, kMaxMaxAckDelayTransportParam), - disable_migration(false), + disable_active_migration(false), active_connection_id_limit(kActiveConnectionIdLimit, kDefaultActiveConnectionIdLimitTransportParam, kMinActiveConnectionIdLimitTransportParam, kVarInt62MaxValue), max_datagram_frame_size(kMaxDatagramFrameSize), - initial_round_trip_time_us(kInitialRoundTripTime) + initial_round_trip_time_us(kInitialRoundTripTime), + support_handshake_done(false) // Important note: any new transport parameters must be added // to TransportParameters::AreValid, SerializeTransportParameters and // ParseTransportParameters, TransportParameters's custom copy constructor, the @@ -496,10 +531,11 @@ TransportParameters::TransportParameters(const TransportParameters& other) : perspective(other.perspective), version(other.version), supported_versions(other.supported_versions), - original_connection_id(other.original_connection_id), - idle_timeout_milliseconds(other.idle_timeout_milliseconds), + original_destination_connection_id( + other.original_destination_connection_id), + max_idle_timeout_ms(other.max_idle_timeout_ms), stateless_reset_token(other.stateless_reset_token), - max_packet_size(other.max_packet_size), + max_udp_payload_size(other.max_udp_payload_size), initial_max_data(other.initial_max_data), initial_max_stream_data_bidi_local( other.initial_max_stream_data_bidi_local), @@ -510,12 +546,15 @@ TransportParameters::TransportParameters(const TransportParameters& other) initial_max_streams_uni(other.initial_max_streams_uni), ack_delay_exponent(other.ack_delay_exponent), max_ack_delay(other.max_ack_delay), - disable_migration(other.disable_migration), + disable_active_migration(other.disable_active_migration), active_connection_id_limit(other.active_connection_id_limit), + initial_source_connection_id(other.initial_source_connection_id), + retry_source_connection_id(other.retry_source_connection_id), max_datagram_frame_size(other.max_datagram_frame_size), initial_round_trip_time_us(other.initial_round_trip_time_us), google_connection_options(other.google_connection_options), user_agent_id(other.user_agent_id), + support_handshake_done(other.support_handshake_done), custom_parameters(other.custom_parameters) { if (other.preferred_address) { preferred_address = std::make_unique<TransportParameters::PreferredAddress>( @@ -530,11 +569,11 @@ TransportParameters::TransportParameters(const TransportParameters& other) bool TransportParameters::operator==(const TransportParameters& rhs) const { if (!(perspective == rhs.perspective && version == rhs.version && supported_versions == rhs.supported_versions && - original_connection_id == rhs.original_connection_id && - idle_timeout_milliseconds.value() == - rhs.idle_timeout_milliseconds.value() && + original_destination_connection_id == + rhs.original_destination_connection_id && + max_idle_timeout_ms.value() == rhs.max_idle_timeout_ms.value() && stateless_reset_token == rhs.stateless_reset_token && - max_packet_size.value() == rhs.max_packet_size.value() && + max_udp_payload_size.value() == rhs.max_udp_payload_size.value() && initial_max_data.value() == rhs.initial_max_data.value() && initial_max_stream_data_bidi_local.value() == rhs.initial_max_stream_data_bidi_local.value() && @@ -548,15 +587,18 @@ bool TransportParameters::operator==(const TransportParameters& rhs) const { rhs.initial_max_streams_uni.value() && ack_delay_exponent.value() == rhs.ack_delay_exponent.value() && max_ack_delay.value() == rhs.max_ack_delay.value() && - disable_migration == rhs.disable_migration && + disable_active_migration == rhs.disable_active_migration && active_connection_id_limit.value() == rhs.active_connection_id_limit.value() && + initial_source_connection_id == rhs.initial_source_connection_id && + retry_source_connection_id == rhs.retry_source_connection_id && max_datagram_frame_size.value() == rhs.max_datagram_frame_size.value() && initial_round_trip_time_us.value() == rhs.initial_round_trip_time_us.value() && google_connection_options == rhs.google_connection_options && user_agent_id == rhs.user_agent_id && + support_handshake_done == rhs.support_handshake_done && custom_parameters == rhs.custom_parameters)) { return false; } @@ -591,8 +633,8 @@ bool TransportParameters::AreValid(std::string* error_details) const { return false; } if (perspective == Perspective::IS_CLIENT && - original_connection_id.has_value()) { - *error_details = "Client cannot send original connection ID"; + original_destination_connection_id.has_value()) { + *error_details = "Client cannot send original_destination_connection_id"; return false; } if (!stateless_reset_token.empty() && @@ -619,6 +661,11 @@ bool TransportParameters::AreValid(std::string* error_details) const { *error_details = "Internal preferred address family failure"; return false; } + if (perspective == Perspective::IS_CLIENT && + retry_source_connection_id.has_value()) { + *error_details = "Client cannot send retry_source_connection_id"; + return false; + } for (const auto& kv : custom_parameters) { if (TransportParameterIdIsKnown(kv.first)) { *error_details = quiche::QuicheStrCat( @@ -637,7 +684,7 @@ bool TransportParameters::AreValid(std::string* error_details) const { return false; } const bool ok = - idle_timeout_milliseconds.IsValid() && max_packet_size.IsValid() && + max_idle_timeout_ms.IsValid() && max_udp_payload_size.IsValid() && initial_max_data.IsValid() && initial_max_stream_data_bidi_local.IsValid() && initial_max_stream_data_bidi_remote.IsValid() && @@ -669,11 +716,78 @@ bool SerializeTransportParameters(ParsedQuicVersion version, return false; } - // Empirically transport parameters generally fit within 128 bytes. - // For now we hope this will be enough. - // TODO(dschinazi) make this grow if needed. - static const size_t kMaxTransportParametersLength = 4096; - out->resize(kMaxTransportParametersLength); + // Maximum length of the GREASE transport parameter (see below). + static constexpr size_t kMaxGreaseLength = 16; + + // Empirically transport parameters generally fit within 128 bytes, but we + // need to allocate the size up front. Integer transport parameters + // have a maximum encoded length of 24 bytes (3 variable length integers), + // other transport parameters have a length of 16 + the maximum value length. + static constexpr size_t kTypeAndValueLength = 2 * sizeof(uint64_t); + static constexpr size_t kIntegerParameterLength = + kTypeAndValueLength + sizeof(uint64_t); + static constexpr size_t kStatelessResetParameterLength = + kTypeAndValueLength + 16 /* stateless reset token length */; + static constexpr size_t kConnectionIdParameterLength = + kTypeAndValueLength + 255 /* maximum connection ID length */; + static constexpr size_t kPreferredAddressParameterLength = + kTypeAndValueLength + 4 /*IPv4 address */ + 2 /* IPv4 port */ + + 16 /* IPv6 address */ + 1 /* Connection ID length */ + + 255 /* maximum connection ID length */ + 16 /* stateless reset token */; + static constexpr size_t kGreaseParameterLength = + kTypeAndValueLength + kMaxGreaseLength; + static constexpr size_t kKnownTransportParamLength = + kConnectionIdParameterLength + // original_destination_connection_id + kIntegerParameterLength + // max_idle_timeout + kStatelessResetParameterLength + // stateless_reset_token + kIntegerParameterLength + // max_udp_payload_size + kIntegerParameterLength + // initial_max_data + kIntegerParameterLength + // initial_max_stream_data_bidi_local + kIntegerParameterLength + // initial_max_stream_data_bidi_remote + kIntegerParameterLength + // initial_max_stream_data_uni + kIntegerParameterLength + // initial_max_streams_bidi + kIntegerParameterLength + // initial_max_streams_uni + kIntegerParameterLength + // ack_delay_exponent + kIntegerParameterLength + // max_ack_delay + kTypeAndValueLength + // disable_active_migration + kPreferredAddressParameterLength + // preferred_address + kIntegerParameterLength + // active_connection_id_limit + kConnectionIdParameterLength + // initial_source_connection_id + kConnectionIdParameterLength + // retry_source_connection_id + kIntegerParameterLength + // max_datagram_frame_size + kIntegerParameterLength + // initial_round_trip_time_us + kTypeAndValueLength + // google_connection_options + kTypeAndValueLength + // user_agent_id + kTypeAndValueLength + // support_handshake_done + kTypeAndValueLength + // google + kTypeAndValueLength + // google-version + kGreaseParameterLength; // GREASE + + size_t max_transport_param_length = kKnownTransportParamLength; + // google_connection_options. + if (in.google_connection_options.has_value()) { + max_transport_param_length += + in.google_connection_options.value().size() * sizeof(QuicTag); + } + // user_agent_id. + if (in.user_agent_id.has_value()) { + max_transport_param_length += in.user_agent_id.value().length(); + } + // Google-specific version extension. + max_transport_param_length += + sizeof(in.version) + 1 /* versions length */ + + in.supported_versions.size() * sizeof(QuicVersionLabel); + // Custom parameters. + for (const auto& kv : in.custom_parameters) { + max_transport_param_length += kTypeAndValueLength + kv.second.length(); + } + // Google-specific non-standard parameter. + if (in.google_quic_params) { + max_transport_param_length += + in.google_quic_params->GetSerialized().length(); + } + + out->resize(max_transport_param_length); QuicDataWriter writer(out->size(), reinterpret_cast<char*>(out->data())); if (!version.HasVarIntTransportParams()) { @@ -688,24 +802,27 @@ bool SerializeTransportParameters(ParsedQuicVersion version, } } - // original_connection_id - if (in.original_connection_id.has_value()) { + // original_destination_connection_id + if (in.original_destination_connection_id.has_value()) { DCHECK_EQ(Perspective::IS_SERVER, in.perspective); - QuicConnectionId original_connection_id = in.original_connection_id.value(); + QuicConnectionId original_destination_connection_id = + in.original_destination_connection_id.value(); if (!WriteTransportParameterId( - &writer, TransportParameters::kOriginalConnectionId, version) || + &writer, TransportParameters::kOriginalDestinationConnectionId, + version) || !WriteTransportParameterStringPiece( &writer, - quiche::QuicheStringPiece(original_connection_id.data(), - original_connection_id.length()), + quiche::QuicheStringPiece( + original_destination_connection_id.data(), + original_destination_connection_id.length()), version)) { - QUIC_BUG << "Failed to write original_connection_id " - << in.original_connection_id.value() << " for " << in; + QUIC_BUG << "Failed to write original_destination_connection_id " + << original_destination_connection_id << " for " << in; return false; } } - if (!in.idle_timeout_milliseconds.Write(&writer, version)) { + if (!in.max_idle_timeout_ms.Write(&writer, version)) { QUIC_BUG << "Failed to write idle_timeout for " << in; return false; } @@ -728,7 +845,7 @@ bool SerializeTransportParameters(ParsedQuicVersion version, } } - if (!in.max_packet_size.Write(&writer, version) || + if (!in.max_udp_payload_size.Write(&writer, version) || !in.initial_max_data.Write(&writer, version) || !in.initial_max_stream_data_bidi_local.Write(&writer, version) || !in.initial_max_stream_data_bidi_remote.Write(&writer, version) || @@ -744,12 +861,12 @@ bool SerializeTransportParameters(ParsedQuicVersion version, return false; } - // disable_migration - if (in.disable_migration) { + // disable_active_migration + if (in.disable_active_migration) { if (!WriteTransportParameterId( - &writer, TransportParameters::kDisableMigration, version) || + &writer, TransportParameters::kDisableActiveMigration, version) || !WriteTransportParameterLength(&writer, /*length=*/0, version)) { - QUIC_BUG << "Failed to write disable_migration for " << in; + QUIC_BUG << "Failed to write disable_active_migration for " << in; return false; } } @@ -791,6 +908,42 @@ bool SerializeTransportParameters(ParsedQuicVersion version, } } + // initial_source_connection_id + if (in.initial_source_connection_id.has_value()) { + QuicConnectionId initial_source_connection_id = + in.initial_source_connection_id.value(); + if (!WriteTransportParameterId( + &writer, TransportParameters::kInitialSourceConnectionId, + version) || + !WriteTransportParameterStringPiece( + &writer, + quiche::QuicheStringPiece(initial_source_connection_id.data(), + initial_source_connection_id.length()), + version)) { + QUIC_BUG << "Failed to write initial_source_connection_id " + << initial_source_connection_id << " for " << in; + return false; + } + } + + // retry_source_connection_id + if (in.retry_source_connection_id.has_value()) { + DCHECK_EQ(Perspective::IS_SERVER, in.perspective); + QuicConnectionId retry_source_connection_id = + in.retry_source_connection_id.value(); + if (!WriteTransportParameterId( + &writer, TransportParameters::kRetrySourceConnectionId, version) || + !WriteTransportParameterStringPiece( + &writer, + quiche::QuicheStringPiece(retry_source_connection_id.data(), + retry_source_connection_id.length()), + version)) { + QUIC_BUG << "Failed to write retry_source_connection_id " + << retry_source_connection_id << " for " << in; + return false; + } + } + // Google-specific connection options. if (in.google_connection_options.has_value()) { static_assert(sizeof(in.google_connection_options.value().front()) == 4, @@ -827,6 +980,17 @@ bool SerializeTransportParameters(ParsedQuicVersion version, } } + // Google-specific support handshake done. + if (in.support_handshake_done) { + if (!WriteTransportParameterId( + &writer, TransportParameters::kGoogleSupportHandshakeDone, + version) || + !WriteTransportParameterLength(&writer, /*length=*/0, version)) { + QUIC_BUG << "Failed to write support_handshake_done for " << in; + return false; + } + } + // Google-specific non-standard parameter. if (in.google_quic_params) { const QuicData& serialized_google_quic_params = @@ -910,7 +1074,6 @@ bool SerializeTransportParameters(ParsedQuicVersion version, << grease_id64 << " invalid for " << version; TransportParameters::TransportParameterId grease_id = static_cast<TransportParameters::TransportParameterId>(grease_id64); - const size_t kMaxGreaseLength = 16; const size_t grease_length = random->RandUint64() % kMaxGreaseLength; DCHECK_GE(kMaxGreaseLength, grease_length); char grease_contents[kMaxGreaseLength]; @@ -989,41 +1152,43 @@ bool ParseTransportParameters(ParsedQuicVersion version, QuicDataReader value_reader(value); bool parse_success = true; switch (param_id) { - case TransportParameters::kOriginalConnectionId: { - if (out->original_connection_id.has_value()) { - *error_details = "Received a second original connection ID"; + case TransportParameters::kOriginalDestinationConnectionId: { + if (out->original_destination_connection_id.has_value()) { + *error_details = + "Received a second original_destination_connection_id"; return false; } const size_t connection_id_length = value_reader.BytesRemaining(); if (!QuicUtils::IsConnectionIdLengthValidForVersion( connection_id_length, version.transport_version)) { *error_details = quiche::QuicheStrCat( - "Received original connection ID of invalid length ", + "Received original_destination_connection_id of invalid length ", connection_id_length); return false; } - QuicConnectionId original_connection_id; - if (!value_reader.ReadConnectionId(&original_connection_id, + QuicConnectionId original_destination_connection_id; + if (!value_reader.ReadConnectionId(&original_destination_connection_id, connection_id_length)) { - *error_details = "Failed to read original connection ID"; + *error_details = "Failed to read original_destination_connection_id"; return false; } - out->original_connection_id = original_connection_id; + out->original_destination_connection_id = + original_destination_connection_id; } break; - case TransportParameters::kIdleTimeout: + case TransportParameters::kMaxIdleTimeout: parse_success = - out->idle_timeout_milliseconds.Read(&value_reader, error_details); + out->max_idle_timeout_ms.Read(&value_reader, error_details); break; case TransportParameters::kStatelessResetToken: { if (!out->stateless_reset_token.empty()) { - *error_details = "Received a second stateless reset token"; + *error_details = "Received a second stateless_reset_token"; return false; } quiche::QuicheStringPiece stateless_reset_token = value_reader.ReadRemainingPayload(); if (stateless_reset_token.length() != kStatelessResetTokenLength) { *error_details = quiche::QuicheStrCat( - "Received stateless reset token of invalid length ", + "Received stateless_reset_token of invalid length ", stateless_reset_token.length()); return false; } @@ -1032,7 +1197,8 @@ bool ParseTransportParameters(ParsedQuicVersion version, stateless_reset_token.data() + stateless_reset_token.length()); } break; case TransportParameters::kMaxPacketSize: - parse_success = out->max_packet_size.Read(&value_reader, error_details); + parse_success = + out->max_udp_payload_size.Read(&value_reader, error_details); break; case TransportParameters::kInitialMaxData: parse_success = @@ -1065,12 +1231,12 @@ bool ParseTransportParameters(ParsedQuicVersion version, case TransportParameters::kMaxAckDelay: parse_success = out->max_ack_delay.Read(&value_reader, error_details); break; - case TransportParameters::kDisableMigration: - if (out->disable_migration) { - *error_details = "Received a second disable migration"; + case TransportParameters::kDisableActiveMigration: + if (out->disable_active_migration) { + *error_details = "Received a second disable_active_migration"; return false; } - out->disable_migration = true; + out->disable_active_migration = true; break; case TransportParameters::kPreferredAddress: { TransportParameters::PreferredAddress preferred_address; @@ -1087,7 +1253,7 @@ bool ParseTransportParameters(ParsedQuicVersion version, &preferred_address.connection_id) || !value_reader.ReadBytes(&preferred_address.stateless_reset_token[0], kStatelessResetTokenLength)) { - *error_details = "Failed to read preferred address"; + *error_details = "Failed to read preferred_address"; return false; } preferred_address.ipv4_socket_address = @@ -1096,13 +1262,13 @@ bool ParseTransportParameters(ParsedQuicVersion version, QuicSocketAddress(QuicIpAddress(ipv6_address), ipv6_port); if (!preferred_address.ipv4_socket_address.host().IsIPv4() || !preferred_address.ipv6_socket_address.host().IsIPv6()) { - *error_details = "Received preferred addresses of bad families " + + *error_details = "Received preferred_address of bad families " + preferred_address.ToString(); return false; } if (!QuicUtils::IsConnectionIdValidForVersion( preferred_address.connection_id, version.transport_version)) { - *error_details = "Received invalid preferred address connection ID " + + *error_details = "Received invalid preferred_address connection ID " + preferred_address.ToString(); return false; } @@ -1114,6 +1280,48 @@ bool ParseTransportParameters(ParsedQuicVersion version, parse_success = out->active_connection_id_limit.Read(&value_reader, error_details); break; + case TransportParameters::kInitialSourceConnectionId: { + if (out->initial_source_connection_id.has_value()) { + *error_details = "Received a second initial_source_connection_id"; + return false; + } + const size_t connection_id_length = value_reader.BytesRemaining(); + if (!QuicUtils::IsConnectionIdLengthValidForVersion( + connection_id_length, version.transport_version)) { + *error_details = quiche::QuicheStrCat( + "Received initial_source_connection_id of invalid length ", + connection_id_length); + return false; + } + QuicConnectionId initial_source_connection_id; + if (!value_reader.ReadConnectionId(&initial_source_connection_id, + connection_id_length)) { + *error_details = "Failed to read initial_source_connection_id"; + return false; + } + out->initial_source_connection_id = initial_source_connection_id; + } break; + case TransportParameters::kRetrySourceConnectionId: { + if (out->retry_source_connection_id.has_value()) { + *error_details = "Received a second retry_source_connection_id"; + return false; + } + const size_t connection_id_length = value_reader.BytesRemaining(); + if (!QuicUtils::IsConnectionIdLengthValidForVersion( + connection_id_length, version.transport_version)) { + *error_details = quiche::QuicheStrCat( + "Received retry_source_connection_id of invalid length ", + connection_id_length); + return false; + } + QuicConnectionId retry_source_connection_id; + if (!value_reader.ReadConnectionId(&retry_source_connection_id, + connection_id_length)) { + *error_details = "Failed to read retry_source_connection_id"; + return false; + } + out->retry_source_connection_id = retry_source_connection_id; + } break; case TransportParameters::kMaxDatagramFrameSize: parse_success = out->max_datagram_frame_size.Read(&value_reader, error_details); @@ -1124,14 +1332,14 @@ bool ParseTransportParameters(ParsedQuicVersion version, break; case TransportParameters::kGoogleConnectionOptions: { if (out->google_connection_options.has_value()) { - *error_details = "Received a second Google connection options"; + *error_details = "Received a second google_connection_options"; return false; } out->google_connection_options = QuicTagVector{}; while (!value_reader.IsDoneReading()) { QuicTag connection_option; if (!value_reader.ReadTag(&connection_option)) { - *error_details = "Failed to read a Google connection option"; + *error_details = "Failed to read a google_connection_options"; return false; } out->google_connection_options.value().push_back(connection_option); @@ -1139,11 +1347,18 @@ bool ParseTransportParameters(ParsedQuicVersion version, } break; case TransportParameters::kGoogleUserAgentId: if (out->user_agent_id.has_value()) { - *error_details = "Received a second user agent ID"; + *error_details = "Received a second user_agent_id"; return false; } out->user_agent_id = std::string(value_reader.ReadRemainingPayload()); break; + case TransportParameters::kGoogleSupportHandshakeDone: + if (out->support_handshake_done) { + *error_details = "Received a second support_handshake_done"; + return false; + } + out->support_handshake_done = true; + break; case TransportParameters::kGoogleQuicParam: { if (out->google_quic_params) { *error_details = "Received a second Google parameter"; @@ -1208,4 +1423,87 @@ bool ParseTransportParameters(ParsedQuicVersion version, return true; } +namespace { + +bool DigestUpdateIntegerParam( + EVP_MD_CTX* hash_ctx, + const TransportParameters::IntegerParameter& param) { + uint64_t value = param.value(); + return EVP_DigestUpdate(hash_ctx, &value, sizeof(value)); +} + +} // namespace + +bool SerializeTransportParametersForTicket( + const TransportParameters& in, + const std::vector<uint8_t>& application_data, + std::vector<uint8_t>* out) { + std::string error_details; + if (!in.AreValid(&error_details)) { + QUIC_BUG << "Not serializing invalid transport parameters: " + << error_details; + return false; + } + + out->resize(SHA256_DIGEST_LENGTH + 1); + const uint8_t serialization_version = 0; + (*out)[0] = serialization_version; + + bssl::ScopedEVP_MD_CTX hash_ctx; + // Write application data: + uint64_t app_data_len = application_data.size(); + const uint64_t parameter_version = 0; + // The format of the input to the hash function is as follows: + // - The application data, prefixed with a 64-bit length field. + // - Transport parameters: + // - A 64-bit version field indicating which version of encoding is used + // for transport parameters. + // - A list of 64-bit integers representing the relevant parameters. + // + // When changing which parameters are included, additional parameters can be + // added to the end of the list without changing the version field. New + // parameters that are variable length must be length prefixed. If + // parameters are removed from the list, the version field must be + // incremented. + // + // Integers happen to be written in host byte order, not network byte order. + if (!EVP_DigestInit(hash_ctx.get(), EVP_sha256()) || + !EVP_DigestUpdate(hash_ctx.get(), &app_data_len, sizeof(app_data_len)) || + !EVP_DigestUpdate(hash_ctx.get(), application_data.data(), + application_data.size()) || + !EVP_DigestUpdate(hash_ctx.get(), ¶meter_version, + sizeof(parameter_version))) { + QUIC_BUG << "Unexpected failure of EVP_Digest functions when hashing " + "Transport Parameters for ticket"; + return false; + } + + // Write transport parameters specified by draft-ietf-quic-transport-28, + // section 7.4.1, that are remembered for 0-RTT. + if (!DigestUpdateIntegerParam(hash_ctx.get(), in.initial_max_data) || + !DigestUpdateIntegerParam(hash_ctx.get(), + in.initial_max_stream_data_bidi_local) || + !DigestUpdateIntegerParam(hash_ctx.get(), + in.initial_max_stream_data_bidi_remote) || + !DigestUpdateIntegerParam(hash_ctx.get(), + in.initial_max_stream_data_uni) || + !DigestUpdateIntegerParam(hash_ctx.get(), in.initial_max_streams_bidi) || + !DigestUpdateIntegerParam(hash_ctx.get(), in.initial_max_streams_uni) || + !DigestUpdateIntegerParam(hash_ctx.get(), + in.active_connection_id_limit)) { + QUIC_BUG << "Unexpected failure of EVP_Digest functions when hashing " + "Transport Parameters for ticket"; + return false; + } + uint8_t disable_active_migration = in.disable_active_migration ? 1 : 0; + if (!EVP_DigestUpdate(hash_ctx.get(), &disable_active_migration, + sizeof(disable_active_migration)) || + !EVP_DigestFinal(hash_ctx.get(), out->data() + 1, nullptr)) { + QUIC_BUG << "Unexpected failure of EVP_Digest functions when hashing " + "Transport Parameters for ticket"; + return false; + } + return true; +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.h b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.h index 9dec484a552..093bb09de2a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.h @@ -28,7 +28,7 @@ struct QUIC_EXPORT_PRIVATE TransportParameters { // The identifier used to differentiate transport parameters. enum TransportParameterId : uint64_t; // A map used to specify custom parameters. - using ParameterMap = QuicUnorderedMap<TransportParameterId, std::string>; + using ParameterMap = QuicHashMap<TransportParameterId, std::string>; // Represents an individual QUIC transport parameter that only encodes a // variable length integer. Can only be created inside the constructor for // TransportParameters. @@ -132,17 +132,17 @@ struct QUIC_EXPORT_PRIVATE TransportParameters { // The value of the Destination Connection ID field from the first // Initial packet sent by the client. - quiche::QuicheOptional<QuicConnectionId> original_connection_id; + quiche::QuicheOptional<QuicConnectionId> original_destination_connection_id; - // Idle timeout expressed in milliseconds. - IntegerParameter idle_timeout_milliseconds; + // Maximum idle timeout expressed in milliseconds. + IntegerParameter max_idle_timeout_ms; // Stateless reset token used in verifying stateless resets. std::vector<uint8_t> stateless_reset_token; // Limits the size of packets that the endpoint is willing to receive. // This indicates that packets larger than this limit will be dropped. - IntegerParameter max_packet_size; + IntegerParameter max_udp_payload_size; // Contains the initial value for the maximum amount of data that can // be sent on the connection. @@ -171,7 +171,7 @@ struct QUIC_EXPORT_PRIVATE TransportParameters { IntegerParameter max_ack_delay; // Indicates lack of support for connection migration. - bool disable_migration; + bool disable_active_migration; // Used to effect a change in server address at the end of the handshake. std::unique_ptr<PreferredAddress> preferred_address; @@ -180,6 +180,14 @@ struct QUIC_EXPORT_PRIVATE TransportParameters { // to store. IntegerParameter active_connection_id_limit; + // The value that the endpoint included in the Source Connection ID field of + // the first Initial packet it sent. + quiche::QuicheOptional<QuicConnectionId> initial_source_connection_id; + + // The value that the server included in the Source Connection ID field of a + // Retry packet it sent. + quiche::QuicheOptional<QuicConnectionId> retry_source_connection_id; + // Indicates support for the DATAGRAM frame and the maximum frame size that // the sender accepts. See draft-ietf-quic-datagram. IntegerParameter max_datagram_frame_size; @@ -194,6 +202,9 @@ struct QUIC_EXPORT_PRIVATE TransportParameters { // Google-specific user agent identifier. quiche::QuicheOptional<std::string> user_agent_id; + // Google-specific handshake done support. This is only used for T050. + bool support_handshake_done; + // Transport parameters used by Google QUIC but not IETF QUIC. This is // serialized into a TransportParameter struct with a TransportParameterId of // kGoogleQuicParamId. @@ -235,6 +246,19 @@ QUIC_EXPORT_PRIVATE bool ParseTransportParameters(ParsedQuicVersion version, TransportParameters* out, std::string* error_details); +// Serializes |in| and |application_data| in a deterministic format so that +// multiple calls to SerializeTransportParametersForTicket with the same inputs +// will generate the same output, and if the inputs differ, then the output will +// differ. The output of this function is used by the server in +// SSL_set_quic_early_data_context to determine whether early data should be +// accepted: Early data will only be accepted if the inputs to this function +// match what they were on the connection that issued an early data capable +// ticket. +QUIC_EXPORT_PRIVATE bool SerializeTransportParametersForTicket( + const TransportParameters& in, + const std::vector<uint8_t>& application_data, + std::vector<uint8_t>* out); + } // namespace quic #endif // QUICHE_QUIC_CORE_CRYPTO_TRANSPORT_PARAMETERS_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters_test.cc index 1fc1f5c5c93..39919532dc6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters_test.cc @@ -44,6 +44,7 @@ const uint64_t kFakeInitialRoundTripTime = 53; const uint8_t kFakePreferredStatelessResetTokenData[16] = { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F}; +const bool kFakeSupportHandshakeDone = true; const auto kCustomParameter1 = static_cast<TransportParameters::TransportParameterId>(0xffcd); @@ -52,10 +53,18 @@ const auto kCustomParameter2 = static_cast<TransportParameters::TransportParameterId>(0xff34); const char* kCustomParameter2Value = "bar"; -QuicConnectionId CreateFakeOriginalConnectionId() { +QuicConnectionId CreateFakeOriginalDestinationConnectionId() { return TestConnectionId(0x1337); } +QuicConnectionId CreateFakeInitialSourceConnectionId() { + return TestConnectionId(0x2345); +} + +QuicConnectionId CreateFakeRetrySourceConnectionId() { + return TestConnectionId(0x9876); +} + QuicConnectionId CreateFakePreferredConnectionId() { return TestConnectionId(0xBEEF); } @@ -151,8 +160,8 @@ TEST_P(TransportParametersTest, Comparator) { new_params.perspective = Perspective::IS_CLIENT; orig_params.version = kFakeVersionLabel; new_params.version = kFakeVersionLabel; - orig_params.disable_migration = true; - new_params.disable_migration = true; + orig_params.disable_active_migration = true; + new_params.disable_active_migration = true; EXPECT_EQ(orig_params, new_params); EXPECT_TRUE(orig_params == new_params); EXPECT_FALSE(orig_params != new_params); @@ -172,12 +181,12 @@ TEST_P(TransportParametersTest, Comparator) { EXPECT_FALSE(orig_params != new_params); // Test comparison on IntegerParameters. - orig_params.max_packet_size.set_value(kFakeMaxPacketSize); - new_params.max_packet_size.set_value(kFakeMaxPacketSize + 1); + orig_params.max_udp_payload_size.set_value(kFakeMaxPacketSize); + new_params.max_udp_payload_size.set_value(kFakeMaxPacketSize + 1); EXPECT_NE(orig_params, new_params); EXPECT_FALSE(orig_params == new_params); EXPECT_TRUE(orig_params != new_params); - new_params.max_packet_size.set_value(kFakeMaxPacketSize); + new_params.max_udp_payload_size.set_value(kFakeMaxPacketSize); EXPECT_EQ(orig_params, new_params); EXPECT_TRUE(orig_params == new_params); EXPECT_FALSE(orig_params != new_params); @@ -222,6 +231,23 @@ TEST_P(TransportParametersTest, Comparator) { EXPECT_EQ(orig_params, new_params); EXPECT_TRUE(orig_params == new_params); EXPECT_FALSE(orig_params != new_params); + + // Test comparison on connection IDs. + orig_params.initial_source_connection_id = + CreateFakeInitialSourceConnectionId(); + new_params.initial_source_connection_id = QUICHE_NULLOPT; + EXPECT_NE(orig_params, new_params); + EXPECT_FALSE(orig_params == new_params); + EXPECT_TRUE(orig_params != new_params); + new_params.initial_source_connection_id = TestConnectionId(0xbadbad); + EXPECT_NE(orig_params, new_params); + EXPECT_FALSE(orig_params == new_params); + EXPECT_TRUE(orig_params != new_params); + new_params.initial_source_connection_id = + CreateFakeInitialSourceConnectionId(); + EXPECT_EQ(orig_params, new_params); + EXPECT_TRUE(orig_params == new_params); + EXPECT_FALSE(orig_params != new_params); } TEST_P(TransportParametersTest, CopyConstructor) { @@ -230,10 +256,11 @@ TEST_P(TransportParametersTest, CopyConstructor) { orig_params.version = kFakeVersionLabel; orig_params.supported_versions.push_back(kFakeVersionLabel); orig_params.supported_versions.push_back(kFakeVersionLabel2); - orig_params.original_connection_id = CreateFakeOriginalConnectionId(); - orig_params.idle_timeout_milliseconds.set_value(kFakeIdleTimeoutMilliseconds); + orig_params.original_destination_connection_id = + CreateFakeOriginalDestinationConnectionId(); + orig_params.max_idle_timeout_ms.set_value(kFakeIdleTimeoutMilliseconds); orig_params.stateless_reset_token = CreateFakeStatelessResetToken(); - orig_params.max_packet_size.set_value(kFakeMaxPacketSize); + orig_params.max_udp_payload_size.set_value(kFakeMaxPacketSize); orig_params.initial_max_data.set_value(kFakeInitialMaxData); orig_params.initial_max_stream_data_bidi_local.set_value( kFakeInitialMaxStreamDataBidiLocal); @@ -245,13 +272,17 @@ TEST_P(TransportParametersTest, CopyConstructor) { orig_params.initial_max_streams_uni.set_value(kFakeInitialMaxStreamsUni); orig_params.ack_delay_exponent.set_value(kFakeAckDelayExponent); orig_params.max_ack_delay.set_value(kFakeMaxAckDelay); - orig_params.disable_migration = kFakeDisableMigration; + orig_params.disable_active_migration = kFakeDisableMigration; orig_params.preferred_address = CreateFakePreferredAddress(); orig_params.active_connection_id_limit.set_value( kFakeActiveConnectionIdLimit); + orig_params.initial_source_connection_id = + CreateFakeInitialSourceConnectionId(); + orig_params.retry_source_connection_id = CreateFakeRetrySourceConnectionId(); orig_params.initial_round_trip_time_us.set_value(kFakeInitialRoundTripTime); orig_params.google_connection_options = CreateFakeGoogleConnectionOptions(); orig_params.user_agent_id = CreateFakeUserAgentId(); + orig_params.support_handshake_done = kFakeSupportHandshakeDone; orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value; orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value; @@ -263,8 +294,8 @@ TEST_P(TransportParametersTest, RoundTripClient) { TransportParameters orig_params; orig_params.perspective = Perspective::IS_CLIENT; orig_params.version = kFakeVersionLabel; - orig_params.idle_timeout_milliseconds.set_value(kFakeIdleTimeoutMilliseconds); - orig_params.max_packet_size.set_value(kFakeMaxPacketSize); + orig_params.max_idle_timeout_ms.set_value(kFakeIdleTimeoutMilliseconds); + orig_params.max_udp_payload_size.set_value(kFakeMaxPacketSize); orig_params.initial_max_data.set_value(kFakeInitialMaxData); orig_params.initial_max_stream_data_bidi_local.set_value( kFakeInitialMaxStreamDataBidiLocal); @@ -276,12 +307,15 @@ TEST_P(TransportParametersTest, RoundTripClient) { orig_params.initial_max_streams_uni.set_value(kFakeInitialMaxStreamsUni); orig_params.ack_delay_exponent.set_value(kFakeAckDelayExponent); orig_params.max_ack_delay.set_value(kFakeMaxAckDelay); - orig_params.disable_migration = kFakeDisableMigration; + orig_params.disable_active_migration = kFakeDisableMigration; orig_params.active_connection_id_limit.set_value( kFakeActiveConnectionIdLimit); + orig_params.initial_source_connection_id = + CreateFakeInitialSourceConnectionId(); orig_params.initial_round_trip_time_us.set_value(kFakeInitialRoundTripTime); orig_params.google_connection_options = CreateFakeGoogleConnectionOptions(); orig_params.user_agent_id = CreateFakeUserAgentId(); + orig_params.support_handshake_done = kFakeSupportHandshakeDone; orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value; orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value; @@ -305,10 +339,11 @@ TEST_P(TransportParametersTest, RoundTripServer) { orig_params.version = kFakeVersionLabel; orig_params.supported_versions.push_back(kFakeVersionLabel); orig_params.supported_versions.push_back(kFakeVersionLabel2); - orig_params.original_connection_id = CreateFakeOriginalConnectionId(); - orig_params.idle_timeout_milliseconds.set_value(kFakeIdleTimeoutMilliseconds); + orig_params.original_destination_connection_id = + CreateFakeOriginalDestinationConnectionId(); + orig_params.max_idle_timeout_ms.set_value(kFakeIdleTimeoutMilliseconds); orig_params.stateless_reset_token = CreateFakeStatelessResetToken(); - orig_params.max_packet_size.set_value(kFakeMaxPacketSize); + orig_params.max_udp_payload_size.set_value(kFakeMaxPacketSize); orig_params.initial_max_data.set_value(kFakeInitialMaxData); orig_params.initial_max_stream_data_bidi_local.set_value( kFakeInitialMaxStreamDataBidiLocal); @@ -320,10 +355,13 @@ TEST_P(TransportParametersTest, RoundTripServer) { orig_params.initial_max_streams_uni.set_value(kFakeInitialMaxStreamsUni); orig_params.ack_delay_exponent.set_value(kFakeAckDelayExponent); orig_params.max_ack_delay.set_value(kFakeMaxAckDelay); - orig_params.disable_migration = kFakeDisableMigration; + orig_params.disable_active_migration = kFakeDisableMigration; orig_params.preferred_address = CreateFakePreferredAddress(); orig_params.active_connection_id_limit.set_value( kFakeActiveConnectionIdLimit); + orig_params.initial_source_connection_id = + CreateFakeInitialSourceConnectionId(); + orig_params.retry_source_connection_id = CreateFakeRetrySourceConnectionId(); orig_params.google_connection_options = CreateFakeGoogleConnectionOptions(); std::vector<uint8_t> serialized; @@ -354,10 +392,10 @@ TEST_P(TransportParametersTest, AreValid) { params.perspective = Perspective::IS_CLIENT; EXPECT_TRUE(params.AreValid(&error_details)); EXPECT_TRUE(error_details.empty()); - params.idle_timeout_milliseconds.set_value(kFakeIdleTimeoutMilliseconds); + params.max_idle_timeout_ms.set_value(kFakeIdleTimeoutMilliseconds); EXPECT_TRUE(params.AreValid(&error_details)); EXPECT_TRUE(error_details.empty()); - params.idle_timeout_milliseconds.set_value(601000); + params.max_idle_timeout_ms.set_value(601000); EXPECT_TRUE(params.AreValid(&error_details)); EXPECT_TRUE(error_details.empty()); } @@ -367,27 +405,27 @@ TEST_P(TransportParametersTest, AreValid) { params.perspective = Perspective::IS_CLIENT; EXPECT_TRUE(params.AreValid(&error_details)); EXPECT_TRUE(error_details.empty()); - params.max_packet_size.set_value(1200); + params.max_udp_payload_size.set_value(1200); EXPECT_TRUE(params.AreValid(&error_details)); EXPECT_TRUE(error_details.empty()); - params.max_packet_size.set_value(65535); + params.max_udp_payload_size.set_value(65535); EXPECT_TRUE(params.AreValid(&error_details)); EXPECT_TRUE(error_details.empty()); - params.max_packet_size.set_value(9999999); + params.max_udp_payload_size.set_value(9999999); EXPECT_TRUE(params.AreValid(&error_details)); EXPECT_TRUE(error_details.empty()); - params.max_packet_size.set_value(0); + params.max_udp_payload_size.set_value(0); error_details = ""; EXPECT_FALSE(params.AreValid(&error_details)); - EXPECT_EQ( - error_details, - "Invalid transport parameters [Client max_packet_size 0 (Invalid)]"); - params.max_packet_size.set_value(1199); + EXPECT_EQ(error_details, + "Invalid transport parameters [Client max_udp_payload_size 0 " + "(Invalid)]"); + params.max_udp_payload_size.set_value(1199); error_details = ""; EXPECT_FALSE(params.AreValid(&error_details)); - EXPECT_EQ( - error_details, - "Invalid transport parameters [Client max_packet_size 1199 (Invalid)]"); + EXPECT_EQ(error_details, + "Invalid transport parameters [Client max_udp_payload_size 1199 " + "(Invalid)]"); } { TransportParameters params; @@ -436,9 +474,9 @@ TEST_P(TransportParametersTest, NoClientParamsWithStatelessResetToken) { TransportParameters orig_params; orig_params.perspective = Perspective::IS_CLIENT; orig_params.version = kFakeVersionLabel; - orig_params.idle_timeout_milliseconds.set_value(kFakeIdleTimeoutMilliseconds); + orig_params.max_idle_timeout_ms.set_value(kFakeIdleTimeoutMilliseconds); orig_params.stateless_reset_token = CreateFakeStatelessResetToken(); - orig_params.max_packet_size.set_value(kFakeMaxPacketSize); + orig_params.max_udp_payload_size.set_value(kFakeMaxPacketSize); std::vector<uint8_t> out; bool ok; @@ -452,12 +490,12 @@ TEST_P(TransportParametersTest, NoClientParamsWithStatelessResetToken) { TEST_P(TransportParametersTest, ParseClientParams) { // clang-format off const uint8_t kClientParamsOld[] = { - 0x00, 0x6a, // length of the parameters array that follows - // idle_timeout + 0x00, 0x7A, // length of the parameters array that follows + // max_idle_timeout 0x00, 0x01, // parameter id 0x00, 0x02, // length 0x6e, 0xec, // value - // max_packet_size + // max_udp_payload_size 0x00, 0x03, // parameter id 0x00, 0x02, // length 0x63, 0x29, // value @@ -493,13 +531,17 @@ TEST_P(TransportParametersTest, ParseClientParams) { 0x00, 0x0b, // parameter id 0x00, 0x01, // length 0x33, // value - // disable_migration + // disable_active_migration 0x00, 0x0c, // parameter id 0x00, 0x00, // length // active_connection_id_limit 0x00, 0x0e, // parameter id 0x00, 0x01, // length 0x34, // value + // initial_source_connection_id + 0x00, 0x0f, // parameter id + 0x00, 0x08, // length + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x45, // initial_round_trip_time_us 0x31, 0x27, // parameter id 0x00, 0x01, // length @@ -514,17 +556,20 @@ TEST_P(TransportParametersTest, ParseClientParams) { 0x31, 0x29, // parameter id 0x00, 0x08, // length 'F', 'a', 'k', 'e', 'U', 'A', 'I', 'D', // value + // support_handshake_done + 0x31, 0x2A, // parameter id + 0x00, 0x00, // value // Google version extension 0x47, 0x52, // parameter id 0x00, 0x04, // length 0x01, 0x23, 0x45, 0x67, // initial version }; const uint8_t kClientParams[] = { - // idle_timeout + // max_idle_timeout 0x01, // parameter id 0x02, // length 0x6e, 0xec, // value - // max_packet_size + // max_udp_payload_size 0x03, // parameter id 0x02, // length 0x63, 0x29, // value @@ -560,13 +605,17 @@ TEST_P(TransportParametersTest, ParseClientParams) { 0x0b, // parameter id 0x01, // length 0x33, // value - // disable_migration + // disable_active_migration 0x0c, // parameter id 0x00, // length // active_connection_id_limit 0x0e, // parameter id 0x01, // length 0x34, // value + // initial_source_connection_id + 0x0f, // parameter id + 0x08, // length + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x45, // initial_round_trip_time_us 0x71, 0x27, // parameter id 0x01, // length @@ -581,6 +630,9 @@ TEST_P(TransportParametersTest, ParseClientParams) { 0x71, 0x29, // parameter id 0x08, // length 'F', 'a', 'k', 'e', 'U', 'A', 'I', 'D', // value + // support_handshake_done + 0x71, 0x2A, // parameter id + 0x00, // length // Google version extension 0x80, 0x00, 0x47, 0x52, // parameter id 0x04, // length @@ -604,11 +656,11 @@ TEST_P(TransportParametersTest, ParseClientParams) { EXPECT_EQ(Perspective::IS_CLIENT, new_params.perspective); EXPECT_EQ(kFakeVersionLabel, new_params.version); EXPECT_TRUE(new_params.supported_versions.empty()); - EXPECT_FALSE(new_params.original_connection_id.has_value()); + EXPECT_FALSE(new_params.original_destination_connection_id.has_value()); EXPECT_EQ(kFakeIdleTimeoutMilliseconds, - new_params.idle_timeout_milliseconds.value()); + new_params.max_idle_timeout_ms.value()); EXPECT_TRUE(new_params.stateless_reset_token.empty()); - EXPECT_EQ(kFakeMaxPacketSize, new_params.max_packet_size.value()); + EXPECT_EQ(kFakeMaxPacketSize, new_params.max_udp_payload_size.value()); EXPECT_EQ(kFakeInitialMaxData, new_params.initial_max_data.value()); EXPECT_EQ(kFakeInitialMaxStreamDataBidiLocal, new_params.initial_max_stream_data_bidi_local.value()); @@ -622,9 +674,13 @@ TEST_P(TransportParametersTest, ParseClientParams) { new_params.initial_max_streams_uni.value()); EXPECT_EQ(kFakeAckDelayExponent, new_params.ack_delay_exponent.value()); EXPECT_EQ(kFakeMaxAckDelay, new_params.max_ack_delay.value()); - EXPECT_EQ(kFakeDisableMigration, new_params.disable_migration); + EXPECT_EQ(kFakeDisableMigration, new_params.disable_active_migration); EXPECT_EQ(kFakeActiveConnectionIdLimit, new_params.active_connection_id_limit.value()); + ASSERT_TRUE(new_params.initial_source_connection_id.has_value()); + EXPECT_EQ(CreateFakeInitialSourceConnectionId(), + new_params.initial_source_connection_id.value()); + EXPECT_FALSE(new_params.retry_source_connection_id.has_value()); EXPECT_EQ(kFakeInitialRoundTripTime, new_params.initial_round_trip_time_us.value()); ASSERT_TRUE(new_params.google_connection_options.has_value()); @@ -632,6 +688,7 @@ TEST_P(TransportParametersTest, ParseClientParams) { new_params.google_connection_options.value()); ASSERT_TRUE(new_params.user_agent_id.has_value()); EXPECT_EQ(CreateFakeUserAgentId(), new_params.user_agent_id.value()); + EXPECT_TRUE(new_params.support_handshake_done); } TEST_P(TransportParametersTest, @@ -639,7 +696,7 @@ TEST_P(TransportParametersTest, // clang-format off const uint8_t kClientParamsWithFullTokenOld[] = { 0x00, 0x26, // length parameters array that follows - // idle_timeout + // max_idle_timeout 0x00, 0x01, // parameter id 0x00, 0x02, // length 0x6e, 0xec, // value @@ -648,7 +705,7 @@ TEST_P(TransportParametersTest, 0x00, 0x10, // length 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, - // max_packet_size + // max_udp_payload_size 0x00, 0x03, // parameter id 0x00, 0x02, // length 0x63, 0x29, // value @@ -658,7 +715,7 @@ TEST_P(TransportParametersTest, 0x40, 0x65, // value }; const uint8_t kClientParamsWithFullToken[] = { - // idle_timeout + // max_idle_timeout 0x01, // parameter id 0x02, // length 0x6e, 0xec, // value @@ -667,7 +724,7 @@ TEST_P(TransportParametersTest, 0x10, // length 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, - // max_packet_size + // max_udp_payload_size 0x03, // parameter id 0x02, // length 0x63, 0x29, // value @@ -698,14 +755,14 @@ TEST_P(TransportParametersTest, // clang-format off const uint8_t kClientParamsWithEmptyTokenOld[] = { 0x00, 0x16, // length parameters array that follows - // idle_timeout + // max_idle_timeout 0x00, 0x01, // parameter id 0x00, 0x02, // length 0x6e, 0xec, // value // stateless_reset_token 0x00, 0x02, // parameter id 0x00, 0x00, - // max_packet_size + // max_udp_payload_size 0x00, 0x03, // parameter id 0x00, 0x02, // length 0x63, 0x29, // value @@ -715,14 +772,14 @@ TEST_P(TransportParametersTest, 0x40, 0x65, // value }; const uint8_t kClientParamsWithEmptyToken[] = { - // idle_timeout + // max_idle_timeout 0x01, // parameter id 0x02, // length 0x6e, 0xec, // value // stateless_reset_token 0x02, // parameter id 0x00, // length - // max_packet_size + // max_udp_payload_size 0x03, // parameter id 0x02, // length 0x63, 0x29, // value @@ -746,36 +803,36 @@ TEST_P(TransportParametersTest, client_params, client_params_length, &out_params, &error_details)); EXPECT_EQ(error_details, - "Received stateless reset token of invalid length 0"); + "Received stateless_reset_token of invalid length 0"); } TEST_P(TransportParametersTest, ParseClientParametersRepeated) { // clang-format off const uint8_t kClientParamsRepeatedOld[] = { 0x00, 0x12, // length parameters array that follows - // idle_timeout + // max_idle_timeout 0x00, 0x01, // parameter id 0x00, 0x02, // length 0x6e, 0xec, // value - // max_packet_size + // max_udp_payload_size 0x00, 0x03, // parameter id 0x00, 0x02, // length 0x63, 0x29, // value - // idle_timeout (repeated) + // max_idle_timeout (repeated) 0x00, 0x01, // parameter id 0x00, 0x02, // length 0x6e, 0xec, // value }; const uint8_t kClientParamsRepeated[] = { - // idle_timeout + // max_idle_timeout 0x01, // parameter id 0x02, // length 0x6e, 0xec, // value - // max_packet_size + // max_udp_payload_size 0x03, // parameter id 0x02, // length 0x63, 0x29, // value - // idle_timeout (repeated) + // max_idle_timeout (repeated) 0x01, // parameter id 0x02, // length 0x6e, 0xec, // value @@ -793,18 +850,18 @@ TEST_P(TransportParametersTest, ParseClientParametersRepeated) { EXPECT_FALSE(ParseTransportParameters(version_, Perspective::IS_CLIENT, client_params, client_params_length, &out_params, &error_details)); - EXPECT_EQ(error_details, "Received a second idle_timeout"); + EXPECT_EQ(error_details, "Received a second max_idle_timeout"); } TEST_P(TransportParametersTest, ParseServerParams) { // clang-format off const uint8_t kServerParamsOld[] = { - 0x00, 0xb7, // length of parameters array that follows - // original_connection_id + 0x00, 0xd3, // length of parameters array that follows + // original_destination_connection_id 0x00, 0x00, // parameter id 0x00, 0x08, // length 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x37, - // idle_timeout + // max_idle_timeout 0x00, 0x01, // parameter id 0x00, 0x02, // length 0x6e, 0xec, // value @@ -813,7 +870,7 @@ TEST_P(TransportParametersTest, ParseServerParams) { 0x00, 0x10, // length 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, - // max_packet_size + // max_udp_payload_size 0x00, 0x03, // parameter id 0x00, 0x02, // length 0x63, 0x29, // value @@ -849,7 +906,7 @@ TEST_P(TransportParametersTest, ParseServerParams) { 0x00, 0x0b, // parameter id 0x00, 0x01, // length 0x33, // value - // disable_migration + // disable_active_migration 0x00, 0x0c, // parameter id 0x00, 0x00, // length // preferred_address @@ -868,12 +925,23 @@ TEST_P(TransportParametersTest, ParseServerParams) { 0x00, 0x0e, // parameter id 0x00, 0x01, // length 0x34, // value + // initial_source_connection_id + 0x00, 0x0f, // parameter id + 0x00, 0x08, // length + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x45, + // retry_source_connection_id + 0x00, 0x10, // parameter id + 0x00, 0x08, // length + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x76, // google_connection_options 0x31, 0x28, // parameter id 0x00, 0x0c, // length 'A', 'L', 'P', 'N', // value 'E', 'F', 'G', 0x00, 'H', 'I', 'J', 0xff, + // support_handshake_done + 0x31, 0x2A, // parameter id + 0x00, 0x00, // value // Google version extension 0x47, 0x52, // parameter id 0x00, 0x0d, // length @@ -883,11 +951,11 @@ TEST_P(TransportParametersTest, ParseServerParams) { 0x89, 0xab, 0xcd, 0xef, }; const uint8_t kServerParams[] = { - // original_connection_id + // original_destination_connection_id 0x00, // parameter id 0x08, // length 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x37, - // idle_timeout + // max_idle_timeout 0x01, // parameter id 0x02, // length 0x6e, 0xec, // value @@ -896,7 +964,7 @@ TEST_P(TransportParametersTest, ParseServerParams) { 0x10, // length 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, - // max_packet_size + // max_udp_payload_size 0x03, // parameter id 0x02, // length 0x63, 0x29, // value @@ -932,7 +1000,7 @@ TEST_P(TransportParametersTest, ParseServerParams) { 0x0b, // parameter id 0x01, // length 0x33, // value - // disable_migration + // disable_active_migration 0x0c, // parameter id 0x00, // length // preferred_address @@ -951,12 +1019,23 @@ TEST_P(TransportParametersTest, ParseServerParams) { 0x0e, // parameter id 0x01, // length 0x34, // value + // initial_source_connection_id + 0x0f, // parameter id + 0x08, // length + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x45, + // retry_source_connection_id + 0x10, // parameter id + 0x08, // length + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x76, // google_connection_options 0x71, 0x28, // parameter id 0x0c, // length 'A', 'L', 'P', 'N', // value 'E', 'F', 'G', 0x00, 'H', 'I', 'J', 0xff, + // support_handshake_done + 0x71, 0x2A, // parameter id + 0x00, // length // Google version extension 0x80, 0x00, 0x47, 0x52, // parameter id 0x0d, // length @@ -985,13 +1064,13 @@ TEST_P(TransportParametersTest, ParseServerParams) { EXPECT_EQ(2u, new_params.supported_versions.size()); EXPECT_EQ(kFakeVersionLabel, new_params.supported_versions[0]); EXPECT_EQ(kFakeVersionLabel2, new_params.supported_versions[1]); - ASSERT_TRUE(new_params.original_connection_id.has_value()); - EXPECT_EQ(CreateFakeOriginalConnectionId(), - new_params.original_connection_id.value()); + ASSERT_TRUE(new_params.original_destination_connection_id.has_value()); + EXPECT_EQ(CreateFakeOriginalDestinationConnectionId(), + new_params.original_destination_connection_id.value()); EXPECT_EQ(kFakeIdleTimeoutMilliseconds, - new_params.idle_timeout_milliseconds.value()); + new_params.max_idle_timeout_ms.value()); EXPECT_EQ(CreateFakeStatelessResetToken(), new_params.stateless_reset_token); - EXPECT_EQ(kFakeMaxPacketSize, new_params.max_packet_size.value()); + EXPECT_EQ(kFakeMaxPacketSize, new_params.max_udp_payload_size.value()); EXPECT_EQ(kFakeInitialMaxData, new_params.initial_max_data.value()); EXPECT_EQ(kFakeInitialMaxStreamDataBidiLocal, new_params.initial_max_stream_data_bidi_local.value()); @@ -1005,7 +1084,7 @@ TEST_P(TransportParametersTest, ParseServerParams) { new_params.initial_max_streams_uni.value()); EXPECT_EQ(kFakeAckDelayExponent, new_params.ack_delay_exponent.value()); EXPECT_EQ(kFakeMaxAckDelay, new_params.max_ack_delay.value()); - EXPECT_EQ(kFakeDisableMigration, new_params.disable_migration); + EXPECT_EQ(kFakeDisableMigration, new_params.disable_active_migration); ASSERT_NE(nullptr, new_params.preferred_address.get()); EXPECT_EQ(CreateFakeV4SocketAddress(), new_params.preferred_address->ipv4_socket_address); @@ -1017,21 +1096,28 @@ TEST_P(TransportParametersTest, ParseServerParams) { new_params.preferred_address->stateless_reset_token); EXPECT_EQ(kFakeActiveConnectionIdLimit, new_params.active_connection_id_limit.value()); + ASSERT_TRUE(new_params.initial_source_connection_id.has_value()); + EXPECT_EQ(CreateFakeInitialSourceConnectionId(), + new_params.initial_source_connection_id.value()); + ASSERT_TRUE(new_params.retry_source_connection_id.has_value()); + EXPECT_EQ(CreateFakeRetrySourceConnectionId(), + new_params.retry_source_connection_id.value()); ASSERT_TRUE(new_params.google_connection_options.has_value()); EXPECT_EQ(CreateFakeGoogleConnectionOptions(), new_params.google_connection_options.value()); EXPECT_FALSE(new_params.user_agent_id.has_value()); + EXPECT_TRUE(new_params.support_handshake_done); } TEST_P(TransportParametersTest, ParseServerParametersRepeated) { // clang-format off const uint8_t kServerParamsRepeatedOld[] = { 0x00, 0x2c, // length of parameters array that follows - // original_connection_id + // original_destination_connection_id 0x00, 0x00, // parameter id 0x00, 0x08, // length 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x37, - // idle_timeout + // max_idle_timeout 0x00, 0x01, // parameter id 0x00, 0x02, // length 0x6e, 0xec, // value @@ -1040,17 +1126,17 @@ TEST_P(TransportParametersTest, ParseServerParametersRepeated) { 0x00, 0x10, // length 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, - // idle_timeout (repeated) + // max_idle_timeout (repeated) 0x00, 0x01, // parameter id 0x00, 0x02, // length 0x6e, 0xec, // value }; const uint8_t kServerParamsRepeated[] = { - // original_connection_id + // original_destination_connection_id 0x00, // parameter id 0x08, // length 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x37, - // idle_timeout + // max_idle_timeout 0x01, // parameter id 0x02, // length 0x6e, 0xec, // value @@ -1059,7 +1145,7 @@ TEST_P(TransportParametersTest, ParseServerParametersRepeated) { 0x10, // length 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, - // idle_timeout (repeated) + // max_idle_timeout (repeated) 0x01, // parameter id 0x02, // length 0x6e, 0xec, // value @@ -1077,7 +1163,7 @@ TEST_P(TransportParametersTest, ParseServerParametersRepeated) { EXPECT_FALSE(ParseTransportParameters(version_, Perspective::IS_SERVER, server_params, server_params_length, &out_params, &error_details)); - EXPECT_EQ(error_details, "Received a second idle_timeout"); + EXPECT_EQ(error_details, "Received a second max_idle_timeout"); } TEST_P(TransportParametersTest, @@ -1085,10 +1171,10 @@ TEST_P(TransportParametersTest, // clang-format off const uint8_t kServerParamsEmptyOriginalConnectionIdOld[] = { 0x00, 0x1e, // length of parameters array that follows - // original_connection_id + // original_destination_connection_id 0x00, 0x00, // parameter id 0x00, 0x00, // length - // idle_timeout + // max_idle_timeout 0x00, 0x01, // parameter id 0x00, 0x02, // length 0x6e, 0xec, // value @@ -1099,10 +1185,10 @@ TEST_P(TransportParametersTest, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, }; const uint8_t kServerParamsEmptyOriginalConnectionId[] = { - // original_connection_id + // original_destination_connection_id 0x00, // parameter id 0x00, // length - // idle_timeout + // max_idle_timeout 0x01, // parameter id 0x02, // length 0x6e, 0xec, // value @@ -1129,15 +1215,16 @@ TEST_P(TransportParametersTest, server_params, server_params_length, &out_params, &error_details)) << error_details; - ASSERT_TRUE(out_params.original_connection_id.has_value()); - EXPECT_EQ(out_params.original_connection_id.value(), EmptyQuicConnectionId()); + ASSERT_TRUE(out_params.original_destination_connection_id.has_value()); + EXPECT_EQ(out_params.original_destination_connection_id.value(), + EmptyQuicConnectionId()); } TEST_P(TransportParametersTest, CryptoHandshakeMessageRoundtrip) { TransportParameters orig_params; orig_params.perspective = Perspective::IS_CLIENT; orig_params.version = kFakeVersionLabel; - orig_params.max_packet_size.set_value(kFakeMaxPacketSize); + orig_params.max_udp_payload_size.set_value(kFakeMaxPacketSize); orig_params.google_quic_params = std::make_unique<CryptoHandshakeMessage>(); const std::string kTestString = "test string"; @@ -1166,7 +1253,132 @@ TEST_P(TransportParametersTest, CryptoHandshakeMessageRoundtrip) { IsQuicNoError()); EXPECT_EQ(test_value, kTestValue); EXPECT_EQ(kFakeVersionLabel, new_params.version); - EXPECT_EQ(kFakeMaxPacketSize, new_params.max_packet_size.value()); + EXPECT_EQ(kFakeMaxPacketSize, new_params.max_udp_payload_size.value()); +} + +TEST_P(TransportParametersTest, VeryLongCustomParameter) { + // Ensure we can handle a 70KB custom parameter on both send and receive. + size_t custom_value_length = 70000; + if (!version_.HasVarIntTransportParams()) { + // These versions encode lengths as uint16 so they cannot send as much. + custom_value_length = 65000; + } + std::string custom_value(custom_value_length, '?'); + TransportParameters orig_params; + orig_params.perspective = Perspective::IS_CLIENT; + orig_params.version = kFakeVersionLabel; + orig_params.custom_parameters[kCustomParameter1] = custom_value; + + std::vector<uint8_t> serialized; + ASSERT_TRUE(SerializeTransportParameters(version_, orig_params, &serialized)); + + TransportParameters new_params; + std::string error_details; + ASSERT_TRUE(ParseTransportParameters(version_, Perspective::IS_CLIENT, + serialized.data(), serialized.size(), + &new_params, &error_details)) + << error_details; + EXPECT_TRUE(error_details.empty()); + RemoveGreaseParameters(&new_params); + EXPECT_EQ(new_params, orig_params); +} + +class TransportParametersTicketSerializationTest : public QuicTest { + protected: + void SetUp() override { + original_params_.perspective = Perspective::IS_SERVER; + original_params_.version = kFakeVersionLabel; + original_params_.supported_versions.push_back(kFakeVersionLabel); + original_params_.supported_versions.push_back(kFakeVersionLabel2); + original_params_.original_destination_connection_id = + CreateFakeOriginalDestinationConnectionId(); + original_params_.max_idle_timeout_ms.set_value( + kFakeIdleTimeoutMilliseconds); + original_params_.stateless_reset_token = CreateFakeStatelessResetToken(); + original_params_.max_udp_payload_size.set_value(kFakeMaxPacketSize); + original_params_.initial_max_data.set_value(kFakeInitialMaxData); + original_params_.initial_max_stream_data_bidi_local.set_value( + kFakeInitialMaxStreamDataBidiLocal); + original_params_.initial_max_stream_data_bidi_remote.set_value( + kFakeInitialMaxStreamDataBidiRemote); + original_params_.initial_max_stream_data_uni.set_value( + kFakeInitialMaxStreamDataUni); + original_params_.initial_max_streams_bidi.set_value( + kFakeInitialMaxStreamsBidi); + original_params_.initial_max_streams_uni.set_value( + kFakeInitialMaxStreamsUni); + original_params_.ack_delay_exponent.set_value(kFakeAckDelayExponent); + original_params_.max_ack_delay.set_value(kFakeMaxAckDelay); + original_params_.disable_active_migration = kFakeDisableMigration; + original_params_.preferred_address = CreateFakePreferredAddress(); + original_params_.active_connection_id_limit.set_value( + kFakeActiveConnectionIdLimit); + original_params_.initial_source_connection_id = + CreateFakeInitialSourceConnectionId(); + original_params_.retry_source_connection_id = + CreateFakeRetrySourceConnectionId(); + original_params_.google_connection_options = + CreateFakeGoogleConnectionOptions(); + + ASSERT_TRUE(SerializeTransportParametersForTicket( + original_params_, application_state_, &original_serialized_params_)); + } + + TransportParameters original_params_; + std::vector<uint8_t> application_state_ = {0, 1}; + std::vector<uint8_t> original_serialized_params_; +}; + +TEST_F(TransportParametersTicketSerializationTest, + StatelessResetTokenDoesntChangeOutput) { + // Test that changing the stateless reset token doesn't change the ticket + // serialization. + TransportParameters new_params = original_params_; + new_params.stateless_reset_token = CreateFakePreferredStatelessResetToken(); + EXPECT_NE(new_params, original_params_); + + std::vector<uint8_t> serialized; + ASSERT_TRUE(SerializeTransportParametersForTicket( + new_params, application_state_, &serialized)); + EXPECT_EQ(original_serialized_params_, serialized); +} + +TEST_F(TransportParametersTicketSerializationTest, + ConnectionIDDoesntChangeOutput) { + // Changing original destination CID doesn't change serialization. + TransportParameters new_params = original_params_; + new_params.original_destination_connection_id = TestConnectionId(0xCAFE); + EXPECT_NE(new_params, original_params_); + + std::vector<uint8_t> serialized; + ASSERT_TRUE(SerializeTransportParametersForTicket( + new_params, application_state_, &serialized)); + EXPECT_EQ(original_serialized_params_, serialized); +} + +TEST_F(TransportParametersTicketSerializationTest, StreamLimitChangesOutput) { + // Changing a stream limit does change the serialization. + TransportParameters new_params = original_params_; + new_params.initial_max_stream_data_bidi_local.set_value( + kFakeInitialMaxStreamDataBidiLocal + 1); + EXPECT_NE(new_params, original_params_); + + std::vector<uint8_t> serialized; + ASSERT_TRUE(SerializeTransportParametersForTicket( + new_params, application_state_, &serialized)); + EXPECT_NE(original_serialized_params_, serialized); +} + +TEST_F(TransportParametersTicketSerializationTest, + ApplicationStateChangesOutput) { + // Changing the application state changes the serialization. + std::vector<uint8_t> new_application_state = {0}; + EXPECT_NE(new_application_state, application_state_); + + std::vector<uint8_t> serialized; + ASSERT_TRUE(SerializeTransportParametersForTicket( + original_params_, new_application_state, &serialized)); + EXPECT_NE(original_serialized_params_, serialized); } } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frequency_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frequency_frame.cc new file mode 100644 index 00000000000..b8c7efafa98 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frequency_frame.cc @@ -0,0 +1,20 @@ +// Copyright (c) 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/frames/quic_ack_frequency_frame.h" +#include <cstdint> +#include <limits> + +namespace quic { + +std::ostream& operator<<(std::ostream& os, const QuicAckFrequencyFrame& frame) { + os << "{ control_frame_id: " << frame.control_frame_id + << ", sequence_number: " << frame.sequence_number + << ", packet_tolerance: " << frame.packet_tolerance + << ", max_ack_delay_ms: " << frame.max_ack_delay.ToMilliseconds() + << ", ignore_order: " << frame.ignore_order << " }\n"; + return os; +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frequency_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frequency_frame.h new file mode 100644 index 00000000000..52b70daff2e --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frequency_frame.h @@ -0,0 +1,45 @@ +// Copyright (c) 2020 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_FRAMES_QUIC_ACK_FREQUENCY_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_ACK_FREQUENCY_FRAME_H_ + +#include <cstdint> +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// A frame that allows sender control of acknowledgement delays. +struct QUIC_EXPORT_PRIVATE QuicAckFrequencyFrame { + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicAckFrequencyFrame& ack_frequency_frame); + + // A unique identifier of this control frame. 0 when this frame is received, + // and non-zero when sent. + QuicControlFrameId control_frame_id = kInvalidControlFrameId; + + // If true, do not ack immediately upon observeation of packet reordering. + bool ignore_order = false; + + // Sequence number assigned to the ACK_FREQUENCY frame by the sender to allow + // receivers to ignore obsolete frames. + uint64_t sequence_number = 0; + + // The maximum number of ack-eliciting packets after which the receiver sends + // an acknowledgement. Invald if == 0. + uint64_t packet_tolerance = 0; + + // The maximum time that ack packets can be delayed. + QuicTime::Delta max_ack_delay = QuicTime::Delta::Zero(); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_ACK_FREQUENCY_FRAME_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.cc index 81b3e18a6b2..bef5f286d23 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.cc +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.cc @@ -32,8 +32,7 @@ QuicCryptoFrame::~QuicCryptoFrame() {} std::ostream& operator<<(std::ostream& os, const QuicCryptoFrame& stream_frame) { - os << "{ level: " << EncryptionLevelToString(stream_frame.level) - << ", offset: " << stream_frame.offset + os << "{ level: " << stream_frame.level << ", offset: " << stream_frame.offset << ", length: " << stream_frame.data_length << " }\n"; return os; } diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.cc index d40202f1930..99b0963a2dc 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.cc +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.cc @@ -6,6 +6,7 @@ #include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h" #include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" @@ -75,6 +76,9 @@ QuicFrame::QuicFrame(QuicMessageFrame* frame) QuicFrame::QuicFrame(QuicNewTokenFrame* frame) : type(NEW_TOKEN_FRAME), new_token_frame(frame) {} +QuicFrame::QuicFrame(QuicAckFrequencyFrame* frame) + : type(ACK_FREQUENCY_FRAME), ack_frequency_frame(frame) {} + void DeleteFrames(QuicFrames* frames) { for (QuicFrame& frame : *frames) { DeleteFrame(&frame); @@ -136,7 +140,9 @@ void DeleteFrame(QuicFrame* frame) { case NEW_TOKEN_FRAME: delete frame->new_token_frame; break; - + case ACK_FREQUENCY_FRAME: + delete frame->ack_frequency_frame; + break; case NUM_FRAME_TYPES: DCHECK(false) << "Cannot delete type: " << frame->type; } @@ -164,6 +170,7 @@ bool IsControlFrame(QuicFrameType type) { case PING_FRAME: case STOP_SENDING_FRAME: case HANDSHAKE_DONE_FRAME: + case ACK_FREQUENCY_FRAME: return true; default: return false; @@ -190,6 +197,8 @@ QuicControlFrameId GetControlFrameId(const QuicFrame& frame) { return frame.stop_sending_frame->control_frame_id; case HANDSHAKE_DONE_FRAME: return frame.handshake_done_frame.control_frame_id; + case ACK_FREQUENCY_FRAME: + return frame.ack_frequency_frame->control_frame_id; default: return kInvalidControlFrameId; } @@ -224,6 +233,9 @@ void SetControlFrameId(QuicControlFrameId control_frame_id, QuicFrame* frame) { case HANDSHAKE_DONE_FRAME: frame->handshake_done_frame.control_frame_id = control_frame_id; return; + case ACK_FREQUENCY_FRAME: + frame->ack_frequency_frame->control_frame_id = control_frame_id; + return; default: QUIC_BUG << "Try to set control frame id of a frame without control frame id"; @@ -261,6 +273,9 @@ QuicFrame CopyRetransmittableControlFrame(const QuicFrame& frame) { copy = QuicFrame( QuicHandshakeDoneFrame(frame.handshake_done_frame.control_frame_id)); break; + case ACK_FREQUENCY_FRAME: + copy = QuicFrame(new QuicAckFrequencyFrame(*frame.ack_frequency_frame)); + break; default: QUIC_BUG << "Try to copy a non-retransmittable control frame: " << frame; copy = QuicFrame(QuicPingFrame(kInvalidControlFrameId)); @@ -352,6 +367,9 @@ QuicFrame CopyQuicFrame(QuicBufferAllocator* allocator, copy = QuicFrame( QuicHandshakeDoneFrame(frame.handshake_done_frame.control_frame_id)); break; + case ACK_FREQUENCY_FRAME: + copy = QuicFrame(new QuicAckFrequencyFrame(*frame.ack_frequency_frame)); + break; default: QUIC_BUG << "Cannot copy frame: " << frame; copy = QuicFrame(QuicPingFrame(kInvalidControlFrameId)); @@ -451,6 +469,9 @@ std::ostream& operator<<(std::ostream& os, const QuicFrame& frame) { case HANDSHAKE_DONE_FRAME: os << "type { HANDSHAKE_DONE_FRAME } " << frame.handshake_done_frame; break; + case ACK_FREQUENCY_FRAME: + os << "type { ACK_FREQUENCY_FRAME } " << *(frame.ack_frequency_frame); + break; default: { QUIC_LOG(ERROR) << "Unknown frame type: " << frame.type; break; diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.h index 756b69f1db0..a8aabfc4ca5 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.h +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.h @@ -9,6 +9,7 @@ #include <vector> #include "net/third_party/quiche/src/quic/core/frames/quic_ack_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_ack_frequency_frame.h" #include "net/third_party/quiche/src/quic/core/frames/quic_blocked_frame.h" #include "net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h" #include "net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.h" @@ -62,6 +63,7 @@ struct QUIC_EXPORT_PRIVATE QuicFrame { explicit QuicFrame(QuicStopSendingFrame* frame); explicit QuicFrame(QuicMessageFrame* message_frame); explicit QuicFrame(QuicCryptoFrame* crypto_frame); + explicit QuicFrame(QuicAckFrequencyFrame* ack_frequency_frame); QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(std::ostream& os, const QuicFrame& frame); @@ -102,6 +104,7 @@ struct QUIC_EXPORT_PRIVATE QuicFrame { QuicStopSendingFrame* stop_sending_frame; QuicMessageFrame* message_frame; QuicCryptoFrame* crypto_frame; + QuicAckFrequencyFrame* ack_frequency_frame; QuicNewTokenFrame* new_token_frame; }; }; diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_frames_test.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frames_test.cc index 22322ed8ee2..3f04c8c0704 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_frames_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frames_test.cc @@ -15,6 +15,7 @@ #include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h" #include "net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h" #include "net/third_party/quiche/src/quic/core/quic_interval.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" @@ -243,6 +244,25 @@ TEST_F(QuicFramesTest, HandshakeDoneFrameToString) { EXPECT_TRUE(IsControlFrame(frame.type)); } +TEST_F(QuicFramesTest, QuicAckFreuqncyFrameToString) { + QuicAckFrequencyFrame ack_frequency_frame; + ack_frequency_frame.sequence_number = 1; + ack_frequency_frame.packet_tolerance = 2; + ack_frequency_frame.max_ack_delay = QuicTime::Delta::FromMilliseconds(25); + ack_frequency_frame.ignore_order = false; + QuicFrame frame(&ack_frequency_frame); + ASSERT_EQ(ACK_FREQUENCY_FRAME, frame.type); + SetControlFrameId(6, &frame); + EXPECT_EQ(6u, GetControlFrameId(frame)); + std::ostringstream stream; + stream << *frame.ack_frequency_frame; + EXPECT_EQ( + "{ control_frame_id: 6, sequence_number: 1, packet_tolerance: 2, " + "max_ack_delay_ms: 25, ignore_order: 0 }\n", + stream.str()); + EXPECT_TRUE(IsControlFrame(frame.type)); +} + TEST_F(QuicFramesTest, StreamFrameToString) { QuicStreamFrame frame; frame.stream_id = 1; @@ -558,6 +578,9 @@ TEST_F(QuicFramesTest, CopyQuicFrames) { case HANDSHAKE_DONE_FRAME: frames.push_back(QuicFrame(QuicHandshakeDoneFrame())); break; + case ACK_FREQUENCY_FRAME: + frames.push_back(QuicFrame(new QuicAckFrequencyFrame())); + break; default: ASSERT_TRUE(false) << "Please fix CopyQuicFrames if a new frame type is added."; 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 0e8e0454483..bfc87610fd4 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 @@ -5,6 +5,7 @@ #ifndef QUICHE_QUIC_CORE_HANDSHAKER_DELEGATE_INTERFACE_H_ #define QUICHE_QUIC_CORE_HANDSHAKER_DELEGATE_INTERFACE_H_ +#include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" namespace quic { @@ -54,6 +55,22 @@ class QUIC_EXPORT_PRIVATE HandshakerDelegateInterface { // encryption level and 2) a server successfully processes a forward secure // packet. virtual void NeuterHandshakeData() = 0; + + // Called when 0-RTT data is rejected by the server. This is only called in + // TLS handshakes and only called on clients. + virtual void OnZeroRttRejected() = 0; + + // Fills in |params| with values from the delegate's QuicConfig. + // Returns whether the operation succeeded. + virtual bool FillTransportParameters(TransportParameters* params) = 0; + + // Read |params| and apply the values to the delegate's QuicConfig. + // On failure, returns a QuicErrorCode and saves a detailed error in + // |error_details|. + virtual QuicErrorCode ProcessTransportParameters( + const TransportParameters& params, + bool is_resumption, + std::string* error_details) = 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 782394427f4..8d274d03739 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 @@ -37,7 +37,9 @@ #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" #include "net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h" #include "net/third_party/quiche/src/quic/test_tools/packet_reordering_writer.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_encoder_peer.h" #include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_encoder_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_header_table_peer.h" #include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_test_utils.h" #include "net/third_party/quiche/src/quic/test_tools/quic_client_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" @@ -55,6 +57,7 @@ #include "net/third_party/quiche/src/quic/test_tools/quic_test_server.h" #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" #include "net/third_party/quiche/src/quic/test_tools/server_thread.h" +#include "net/third_party/quiche/src/quic/test_tools/simple_session_cache.h" #include "net/third_party/quiche/src/quic/tools/quic_backend_response.h" #include "net/third_party/quiche/src/quic/tools/quic_client.h" #include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h" @@ -70,6 +73,9 @@ using spdy::SpdyFramer; using spdy::SpdyHeaderBlock; using spdy::SpdySerializedFrame; using spdy::SpdySettingsIR; +using ::testing::_; +using ::testing::Invoke; +using ::testing::NiceMock; namespace quic { namespace test { @@ -77,6 +83,7 @@ namespace { const char kFooResponseBody[] = "Artichoke hearts make me happy."; const char kBarResponseBody[] = "Palm hearts are pretty delicious, also."; +const char kTestUserAgentId[] = "quic/core/http/end_to_end_test.cc"; const float kSessionToStreamRatio = 1.5; // Run all tests with the cross products of all versions. @@ -191,6 +198,16 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { AddToCache("/foo", 200, kFooResponseBody); AddToCache("/bar", 200, kBarResponseBody); + // Enable fixes for bugs found in tests and prod. + SetQuicReloadableFlag(quic_donot_change_queued_ack, true); + SetQuicReloadableFlag(quic_fix_last_inflight_packets_sent_time, true); + SetQuicReloadableFlag(quic_fix_server_pto_timeout, true); + SetQuicReloadableFlag(quic_do_not_retransmit_immediately_on_zero_rtt_reject, + true); + + SetQuicReloadableFlag(quic_support_handshake_done_in_t050, true); + SetQuicReloadableFlag(quic_enable_tls_resumption, true); + SetQuicReloadableFlag(quic_enable_zero_rtt_for_tls, true); } ~EndToEndTest() override { QuicRecyclePort(server_address_.port()); } @@ -203,7 +220,9 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { QuicTestClient* client = new QuicTestClient(server_address_, server_hostname_, client_config_, client_supported_versions_, - crypto_test_utils::ProofVerifierForTesting()); + crypto_test_utils::ProofVerifierForTesting(), + std::make_unique<SimpleSessionCache>()); + client->SetUserAgentID(kTestUserAgentId); client->UseWriter(writer); if (!pre_shared_key_client_.empty()) { client->client()->SetPreSharedKey(pre_shared_key_client_); @@ -441,6 +460,13 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { EXPECT_EQ(0u, server_stats.packets_lost); } EXPECT_EQ(0u, server_stats.packets_discarded); + if (GetQuicReloadableFlag(quic_save_user_agent_in_quic_session)) { + EXPECT_EQ( + GetServerSession()->user_agent_id().value_or("MissingUserAgent"), + kTestUserAgentId); + } else { + EXPECT_FALSE(GetServerSession()->user_agent_id().has_value()); + } // TODO(ianswett): Restore the check for packets_dropped equals 0. // The expect for packets received is equal to packets processed fails // due to version negotiation packets. @@ -529,7 +555,7 @@ INSTANTIATE_TEST_SUITE_P(EndToEndTests, TEST_P(EndToEndTest, HandshakeSuccessful) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); server_thread_->WaitForCryptoHandshakeConfirmed(); QuicCryptoStream* crypto_stream = QuicSessionPeer::GetMutableCryptoStream(GetClientSession()); @@ -568,7 +594,7 @@ TEST_P(EndToEndTest, SimpleRequestResponse) { TEST_P(EndToEndTest, HandshakeConfirmed) { ASSERT_TRUE(Initialize()); - if (!version_.HasHandshakeDone()) { + if (!version_.UsesTls()) { return; } EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); @@ -599,44 +625,27 @@ TEST_P(EndToEndTest, SendAndReceiveCoalescedPackets) { // Simple transaction, but set a non-default ack delay at the client // and ensure it gets to the server. TEST_P(EndToEndTest, SimpleRequestResponseWithAckDelayChange) { - if (version_.UsesTls()) { - // TODO(b/155316241): Enable this test for TLS. - Initialize(); - return; - } // Force the ACK delay to be something other than the default. - // Note that it is sent only if doing IETF QUIC. - client_config_.SetMaxAckDelayToSendMs(kDefaultDelayedAckTimeMs + 100u); + constexpr uint32_t kClientMaxAckDelay = kDefaultDelayedAckTimeMs + 100u; + client_config_.SetMaxAckDelayToSendMs(kClientMaxAckDelay); ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); EXPECT_EQ("200", client_->response_headers()->find(":status")->second); EXPECT_FALSE(client_->client()->EarlyDataAccepted()); EXPECT_FALSE(client_->client()->ReceivedInchoateReject()); - if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) { - EXPECT_EQ(kDefaultDelayedAckTimeMs + 100u, - GetSentPacketManagerFromFirstServerSession() - ->peer_max_ack_delay() - .ToMilliseconds()); - } else { - EXPECT_EQ(kDefaultDelayedAckTimeMs, - GetSentPacketManagerFromFirstServerSession() - ->peer_max_ack_delay() - .ToMilliseconds()); - } + EXPECT_EQ(kClientMaxAckDelay, GetSentPacketManagerFromFirstServerSession() + ->peer_max_ack_delay() + .ToMilliseconds()); } // Simple transaction, but set a non-default ack exponent at the client // and ensure it gets to the server. TEST_P(EndToEndTest, SimpleRequestResponseWithAckExponentChange) { - if (version_.UsesTls()) { - // TODO(b/155316241): Enable this test for TLS. - Initialize(); - return; - } - const uint32_t kClientAckDelayExponent = kDefaultAckDelayExponent + 100u; + const uint32_t kClientAckDelayExponent = 19; + EXPECT_NE(kClientAckDelayExponent, kDefaultAckDelayExponent); // Force the ACK exponent to be something other than the default. - // Note that it is sent only if doing IETF QUIC. + // Note that it is sent only with QUIC+TLS. client_config_.SetAckDelayExponentToSend(kClientAckDelayExponent); ASSERT_TRUE(Initialize()); @@ -645,12 +654,12 @@ TEST_P(EndToEndTest, SimpleRequestResponseWithAckExponentChange) { EXPECT_FALSE(client_->client()->EarlyDataAccepted()); EXPECT_FALSE(client_->client()->ReceivedInchoateReject()); - if (VersionHasIetfQuicFrames(version_.transport_version)) { - // Should be only for IETF QUIC. + if (version_.UsesTls()) { + // Should be only sent with QUIC+TLS. EXPECT_EQ(kClientAckDelayExponent, GetServerConnection()->framer().peer_ack_delay_exponent()); } else { - // No change for Google QUIC. + // No change for QUIC_CRYPTO. EXPECT_EQ(kDefaultAckDelayExponent, GetServerConnection()->framer().peer_ack_delay_exponent()); } @@ -662,9 +671,9 @@ TEST_P(EndToEndTest, SimpleRequestResponseWithAckExponentChange) { TEST_P(EndToEndTest, SimpleRequestResponseForcedVersionNegotiation) { client_supported_versions_.insert(client_supported_versions_.begin(), QuicVersionReservedForNegotiation()); - testing::NiceMock<MockQuicConnectionDebugVisitor> visitor; + NiceMock<MockQuicConnectionDebugVisitor> visitor; connection_debug_visitor_ = &visitor; - EXPECT_CALL(visitor, OnVersionNegotiationPacket(testing::_)).Times(1); + EXPECT_CALL(visitor, OnVersionNegotiationPacket(_)).Times(1); ASSERT_TRUE(Initialize()); ASSERT_TRUE(ServerSendsVersionNegotiation()); @@ -1103,7 +1112,7 @@ TEST_P(EndToEndTest, PostMissingBytes) { TEST_P(EndToEndTest, LargePostNoPacketLoss) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); // 1 MB body. std::string body(1024 * 1024, 'a'); @@ -1124,7 +1133,7 @@ TEST_P(EndToEndTest, LargePostNoPacketLoss1sRTT) { ASSERT_TRUE(Initialize()); SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(1000)); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); // 100 KB body. std::string body(100 * 1024, 'a'); @@ -1145,9 +1154,7 @@ TEST_P(EndToEndTest, LargePostWithPacketLoss) { // brutal. SetPacketLossPercentage(5); ASSERT_TRUE(Initialize()); - - // Wait for the server SHLO before upping the packet loss. - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForHandshakeConfirmed()); SetPacketLossPercentage(30); // 10 KB body. @@ -1166,9 +1173,7 @@ TEST_P(EndToEndTest, LargePostWithPacketLoss) { // Regression test for b/80090281. TEST_P(EndToEndTest, LargePostWithPacketLossAndAlwaysBundleWindowUpdates) { ASSERT_TRUE(Initialize()); - - // Wait for the server SHLO before upping the packet loss. - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForHandshakeConfirmed()); server_thread_->WaitForCryptoHandshakeConfirmed(); // Normally server only bundles a retransmittable frame once every other @@ -1199,9 +1204,7 @@ TEST_P(EndToEndTest, LargePostWithPacketLossAndBlockedSocket) { // b/10126687 is fixed, losing handshake packets is pretty brutal. SetPacketLossPercentage(5); ASSERT_TRUE(Initialize()); - - // Wait for the server SHLO before upping the packet loss. - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForHandshakeConfirmed()); SetPacketLossPercentage(10); client_writer_->set_fake_blocked_socket_percentage(10); @@ -1219,8 +1222,7 @@ TEST_P(EndToEndTest, LargePostWithPacketLossAndBlockedSocket) { TEST_P(EndToEndTest, LargePostNoPacketLossWithDelayAndReordering) { ASSERT_TRUE(Initialize()); - - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForHandshakeConfirmed()); // Both of these must be called when the writer is not actively used. SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); SetReorderPercentage(30); @@ -1238,11 +1240,6 @@ TEST_P(EndToEndTest, LargePostNoPacketLossWithDelayAndReordering) { } TEST_P(EndToEndTest, LargePostZeroRTTFailure) { - if (version_.UsesTls()) { - // TODO(b/152551499): Re-enable this test when TLS supports 0-RTT. - Initialize(); - return; - } // Send a request and then disconnect. This prepares the client to attempt // a 0-RTT handshake for the next request. ASSERT_TRUE(Initialize()); @@ -1265,7 +1262,7 @@ TEST_P(EndToEndTest, LargePostZeroRTTFailure) { // The 0-RTT handshake should succeed. client_->Connect(); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); ASSERT_TRUE(client_->client()->connected()); EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(headers, body)); @@ -1281,7 +1278,7 @@ TEST_P(EndToEndTest, LargePostZeroRTTFailure) { StartServer(); client_->Connect(); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); ASSERT_TRUE(client_->client()->connected()); EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(headers, body)); @@ -1294,11 +1291,6 @@ TEST_P(EndToEndTest, LargePostZeroRTTFailure) { } TEST_P(EndToEndTest, SynchronousRequestZeroRTTFailure) { - if (version_.UsesTls()) { - // TODO(b/152551499): Re-enable this test when TLS supports 0-RTT. - Initialize(); - return; - } // Send a request and then disconnect. This prepares the client to attempt // a 0-RTT handshake for the next request. ASSERT_TRUE(Initialize()); @@ -1313,7 +1305,7 @@ TEST_P(EndToEndTest, SynchronousRequestZeroRTTFailure) { // The 0-RTT handshake should succeed. client_->Connect(); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); ASSERT_TRUE(client_->client()->connected()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); @@ -1328,7 +1320,7 @@ TEST_P(EndToEndTest, SynchronousRequestZeroRTTFailure) { StartServer(); client_->Connect(); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); ASSERT_TRUE(client_->client()->connected()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); @@ -1360,14 +1352,10 @@ TEST_P(EndToEndTest, LargePostSynchronousRequest) { EXPECT_FALSE(client_->client()->ReceivedInchoateReject()); client_->Disconnect(); - if (version_.UsesTls()) { - // TODO(b/152551499): remove this when TLS supports 0-RTT. - return; - } // The 0-RTT handshake should succeed. client_->Connect(); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); ASSERT_TRUE(client_->client()->connected()); EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(headers, body)); @@ -1383,7 +1371,7 @@ TEST_P(EndToEndTest, LargePostSynchronousRequest) { StartServer(); client_->Connect(); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); ASSERT_TRUE(client_->client()->connected()); EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(headers, body)); @@ -1413,7 +1401,7 @@ TEST_P(EndToEndTest, SetInitialReceivedConnectionOptions) { initial_received_options)); ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); server_thread_->WaitForCryptoHandshakeConfirmed(); EXPECT_FALSE(server_config_.SetInitialReceivedConnectionOptions( @@ -1438,7 +1426,7 @@ TEST_P(EndToEndTest, LargePostSmallBandwidthLargeBuffer) { server_writer_->set_max_bandwidth_and_buffer_size( QuicBandwidth::FromBytesPerSecond(256 * 1024), 256 * 1024); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); // 1 MB body. std::string body(1024 * 1024, 'a'); @@ -1462,7 +1450,7 @@ TEST_P(EndToEndTest, DoNotSetSendAlarmIfConnectionFlowControlBlocked) { // an infinite loop in the EpollServer, as the alarm fires and is immediately // rescheduled. ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); // Ensure both stream and connection level are flow control blocked by setting // the send window offset to 0. @@ -1498,7 +1486,7 @@ TEST_P(EndToEndTest, DoNotSetSendAlarmIfConnectionFlowControlBlocked) { TEST_P(EndToEndTest, InvalidStream) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); std::string body(kMaxOutgoingPacketSize, 'a'); SpdyHeaderBlock headers; @@ -1523,7 +1511,7 @@ TEST_P(EndToEndTest, InvalidStream) { // with overly large headers. TEST_P(EndToEndTest, LargeHeaders) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); std::string body(kMaxOutgoingPacketSize, 'a'); SpdyHeaderBlock headers; @@ -1552,7 +1540,7 @@ TEST_P(EndToEndTest, LargeHeaders) { TEST_P(EndToEndTest, EarlyResponseWithQuicStreamNoError) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); std::string large_body(1024 * 1024, 'a'); SpdyHeaderBlock headers; @@ -1618,7 +1606,7 @@ TEST_P(EndToEndTest, MaxDynamicStreamsLimitRespected) { // not properly set up. return; } - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); // Make the client misbehave after negotiation. const int kServerMaxStreams = kMaxStreamsMinimumIncrement + 1; @@ -1654,7 +1642,7 @@ TEST_P(EndToEndTest, SetIndependentMaxDynamicStreamsLimits) { server_config_.SetMaxUnidirectionalStreamsToSend(kServerMaxDynamicStreams); ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); // The client has received the server's limit and vice versa. QuicSpdyClientSession* client_session = GetClientSession(); @@ -1711,7 +1699,7 @@ TEST_P(EndToEndTest, SetIndependentMaxDynamicStreamsLimits) { TEST_P(EndToEndTest, NegotiateCongestionControl) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); CongestionControlType expected_congestion_control_type = kRenoBytes; switch (GetParam().congestion_control_tag) { @@ -1745,7 +1733,7 @@ TEST_P(EndToEndTest, ClientSuggestsRTT) { client_config_.SetInitialRoundTripTimeUsToSend(kInitialRTT.ToMicroseconds()); ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); server_thread_->WaitForCryptoHandshakeConfirmed(); // Pause the server so we can access the server's internals without races. @@ -1774,7 +1762,7 @@ TEST_P(EndToEndTest, ClientSuggestsIgnoredRTT) { client_config_.SetConnectionOptionsToSend(options); ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); server_thread_->WaitForCryptoHandshakeConfirmed(); // Pause the server so we can access the server's internals without races. @@ -1801,7 +1789,7 @@ TEST_P(EndToEndTest, MaxInitialRTT) { kMaxInitialRoundTripTimeUs); ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); server_thread_->WaitForCryptoHandshakeConfirmed(); // Pause the server so we can access the server's internals without races. @@ -1827,7 +1815,7 @@ TEST_P(EndToEndTest, MinInitialRTT) { client_config_.SetInitialRoundTripTimeUsToSend(0); ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); server_thread_->WaitForCryptoHandshakeConfirmed(); // Pause the server so we can access the server's internals without races. @@ -1908,7 +1896,7 @@ TEST_P(EndToEndTest, ResetConnection) { EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); EXPECT_EQ("200", client_->response_headers()->find(":status")->second); client_->ResetConnection(); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar")); EXPECT_EQ("200", client_->response_headers()->find(":status")->second); } @@ -1923,7 +1911,7 @@ TEST_P(EndToEndTest, MaxStreamsUberTest) { AddToCache("/large_response", 200, large_body); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); SetPacketLossPercentage(10); for (int i = 0; i < max_streams; ++i) { @@ -1942,7 +1930,7 @@ TEST_P(EndToEndTest, StreamCancelErrorTest) { AddToCache("/small_response", 200, small_body); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); QuicSession* session = GetClientSession(); // Lose the request. @@ -1952,11 +1940,7 @@ TEST_P(EndToEndTest, StreamCancelErrorTest) { // Transmit the cancel, and ensure the connection is torn down properly. SetPacketLossPercentage(0); QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0); - if (session->break_close_loop()) { - session->ResetStream(stream_id, QUIC_STREAM_CANCELLED, 0); - } else { - session->SendRstStream(stream_id, QUIC_STREAM_CANCELLED, 0); - } + session->ResetStream(stream_id, QUIC_STREAM_CANCELLED, 0); // WaitForEvents waits 50ms and returns true if there are outstanding // requests. @@ -2050,7 +2034,7 @@ TEST_P(EndToEndTest, NegotiatedInitialCongestionWindow) { ASSERT_TRUE(Initialize()); // Values are exchanged during crypto handshake, so wait for that to finish. - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); server_thread_->WaitForCryptoHandshakeConfirmed(); server_thread_->Pause(); @@ -2060,11 +2044,6 @@ TEST_P(EndToEndTest, NegotiatedInitialCongestionWindow) { } TEST_P(EndToEndTest, DifferentFlowControlWindows) { - if (version_.UsesTls()) { - // TODO(b/155316241): Enable this test for TLS. - Initialize(); - return; - } // Client and server can set different initial flow control receive windows. // These are sent in CHLO/SHLO. Tests that these values are exchanged properly // in the crypto handshake. @@ -2081,7 +2060,7 @@ TEST_P(EndToEndTest, DifferentFlowControlWindows) { ASSERT_TRUE(Initialize()); // Values are exchanged during crypto handshake, so wait for that to finish. - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); server_thread_->WaitForCryptoHandshakeConfirmed(); // Open a data stream to make sure the stream level flow control is updated. @@ -2089,17 +2068,28 @@ TEST_P(EndToEndTest, DifferentFlowControlWindows) { WriteHeadersOnStream(stream); stream->WriteOrBufferBody("hello", false); - // Client should have the right values for server's receive window. - EXPECT_EQ(kServerStreamIFCW, - client_->client() - ->client_session() - ->config() - ->ReceivedInitialStreamFlowControlWindowBytes()); - EXPECT_EQ(kServerSessionIFCW, - client_->client() - ->client_session() - ->config() - ->ReceivedInitialSessionFlowControlWindowBytes()); + if (!version_.UsesTls()) { + // IFWA only exists with QUIC_CRYPTO. + // Client should have the right values for server's receive window. + ASSERT_TRUE(client_->client() + ->client_session() + ->config() + ->HasReceivedInitialStreamFlowControlWindowBytes()); + EXPECT_EQ(kServerStreamIFCW, + client_->client() + ->client_session() + ->config() + ->ReceivedInitialStreamFlowControlWindowBytes()); + ASSERT_TRUE(client_->client() + ->client_session() + ->config() + ->HasReceivedInitialSessionFlowControlWindowBytes()); + EXPECT_EQ(kServerSessionIFCW, + client_->client() + ->client_session() + ->config() + ->ReceivedInitialSessionFlowControlWindowBytes()); + } EXPECT_EQ(kServerStreamIFCW, QuicFlowControllerPeer::SendWindowOffset( stream->flow_controller())); EXPECT_EQ(kServerSessionIFCW, QuicFlowControllerPeer::SendWindowOffset( @@ -2107,23 +2097,24 @@ TEST_P(EndToEndTest, DifferentFlowControlWindows) { // Server should have the right values for client's receive window. server_thread_->Pause(); - QuicSession* session = GetServerSession(); - EXPECT_EQ(kClientStreamIFCW, - session->config()->ReceivedInitialStreamFlowControlWindowBytes()); - EXPECT_EQ(kClientSessionIFCW, - session->config()->ReceivedInitialSessionFlowControlWindowBytes()); + QuicConfig server_config = *GetServerSession()->config(); EXPECT_EQ(kClientSessionIFCW, QuicFlowControllerPeer::SendWindowOffset( - session->flow_controller())); + GetServerSession()->flow_controller())); server_thread_->Resume(); + if (version_.UsesTls()) { + // IFWA only exists with QUIC_CRYPTO. + return; + } + ASSERT_TRUE(server_config.HasReceivedInitialStreamFlowControlWindowBytes()); + EXPECT_EQ(kClientStreamIFCW, + server_config.ReceivedInitialStreamFlowControlWindowBytes()); + ASSERT_TRUE(server_config.HasReceivedInitialSessionFlowControlWindowBytes()); + EXPECT_EQ(kClientSessionIFCW, + server_config.ReceivedInitialSessionFlowControlWindowBytes()); } // Test negotiation of IFWA connection option. TEST_P(EndToEndTest, NegotiatedServerInitialFlowControlWindow) { - if (version_.UsesTls()) { - // TODO(b/155316241): Enable this test for TLS. - Initialize(); - return; - } const uint32_t kClientStreamIFCW = 123456; const uint32_t kClientSessionIFCW = 234567; set_client_initial_stream_flow_control_receive_window(kClientStreamIFCW); @@ -2142,7 +2133,7 @@ TEST_P(EndToEndTest, NegotiatedServerInitialFlowControlWindow) { ASSERT_TRUE(Initialize()); // Values are exchanged during crypto handshake, so wait for that to finish. - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); server_thread_->WaitForCryptoHandshakeConfirmed(); // Open a data stream to make sure the stream level flow control is updated. @@ -2150,17 +2141,28 @@ TEST_P(EndToEndTest, NegotiatedServerInitialFlowControlWindow) { WriteHeadersOnStream(stream); stream->WriteOrBufferBody("hello", false); - // Client should have the right values for server's receive window. - EXPECT_EQ(kExpectedStreamIFCW, - client_->client() - ->client_session() - ->config() - ->ReceivedInitialStreamFlowControlWindowBytes()); - EXPECT_EQ(kExpectedSessionIFCW, - client_->client() - ->client_session() - ->config() - ->ReceivedInitialSessionFlowControlWindowBytes()); + if (!version_.UsesTls()) { + // IFWA only exists with QUIC_CRYPTO. + // Client should have the right values for server's receive window. + ASSERT_TRUE(client_->client() + ->client_session() + ->config() + ->HasReceivedInitialStreamFlowControlWindowBytes()); + EXPECT_EQ(kExpectedStreamIFCW, + client_->client() + ->client_session() + ->config() + ->ReceivedInitialStreamFlowControlWindowBytes()); + ASSERT_TRUE(client_->client() + ->client_session() + ->config() + ->HasReceivedInitialSessionFlowControlWindowBytes()); + EXPECT_EQ(kExpectedSessionIFCW, + client_->client() + ->client_session() + ->config() + ->ReceivedInitialSessionFlowControlWindowBytes()); + } EXPECT_EQ(kExpectedStreamIFCW, QuicFlowControllerPeer::SendWindowOffset( stream->flow_controller())); EXPECT_EQ(kExpectedSessionIFCW, QuicFlowControllerPeer::SendWindowOffset( @@ -2182,7 +2184,7 @@ TEST_P(EndToEndTest, HeadersAndCryptoStreamsNoConnectionFlowControl) { // Wait for crypto handshake to finish. This should have contributed to the // crypto stream flow control window, but not affected the session flow // control window. - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); server_thread_->WaitForCryptoHandshakeConfirmed(); QuicCryptoStream* crypto_stream = @@ -2236,7 +2238,7 @@ TEST_P(EndToEndTest, FlowControlsSynced) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); server_thread_->WaitForCryptoHandshakeConfirmed(); QuicSpdySession* const client_session = GetClientSession(); @@ -2353,28 +2355,26 @@ TEST_P(EndToEndTest, RequestWithNoBodyWillNeverSendStreamFrameWithFIN) { server_thread_->Resume(); } -// A TestAckListener verifies that exactly |bytes_to_ack| bytes are acked during -// its lifetime. +// TestAckListener counts how many bytes are acked during its lifetime. class TestAckListener : public QuicAckListenerInterface { public: - explicit TestAckListener(int bytes_to_ack) : bytes_to_ack_(bytes_to_ack) {} + TestAckListener() {} void OnPacketAcked(int acked_bytes, QuicTime::Delta /*delta_largest_observed*/) override { - ASSERT_LE(acked_bytes, bytes_to_ack_); - bytes_to_ack_ -= acked_bytes; + total_bytes_acked_ += acked_bytes; } void OnPacketRetransmitted(int /*retransmitted_bytes*/) override {} - bool has_been_notified() const { return bytes_to_ack_ == 0; } + int total_bytes_acked() const { return total_bytes_acked_; } protected: // Object is ref counted. - ~TestAckListener() override { EXPECT_EQ(0, bytes_to_ack_); } + ~TestAckListener() override {} private: - int bytes_to_ack_; + int total_bytes_acked_ = 0; }; class TestResponseListener : public QuicSpdyClientBase::ResponseListener { @@ -2388,22 +2388,35 @@ class TestResponseListener : public QuicSpdyClientBase::ResponseListener { } }; -// TODO(dschinazi) Fix this test's flakiness in Chrome. -TEST_P( - EndToEndTest, - QUIC_TEST_DISABLED_IN_CHROME(AckNotifierWithPacketLossAndBlockedSocket)) { +TEST_P(EndToEndTest, AckNotifierWithPacketLossAndBlockedSocket) { // Verify that even in the presence of packet loss and occasionally blocked // socket, an AckNotifierDelegate will get informed that the data it is // interested in has been ACKed. This tests end-to-end ACK notification, and // demonstrates that retransmissions do not break this functionality. SetPacketLossPercentage(5); ASSERT_TRUE(Initialize()); - // Wait for the server SHLO before upping the packet loss. - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForHandshakeConfirmed()); SetPacketLossPercentage(30); client_writer_->set_fake_blocked_socket_percentage(10); + // Wait for SETTINGS frame from server that sets QPACK dynamic table capacity + // to make sure request headers will be compressed using the dynamic table. + if (version_.UsesHttp3()) { + while (true) { + // Waits for up to 50 ms. + client_->client()->WaitForEvents(); + if (!GetClientSession() || !GetClientSession()->qpack_encoder()) { + continue; + } + QpackHeaderTable* header_table = + QpackEncoderPeer::header_table(GetClientSession()->qpack_encoder()); + if (QpackHeaderTablePeer::dynamic_table_capacity(header_table) > 0) { + break; + } + } + } + // Create a POST request and send the headers only. SpdyHeaderBlock headers; headers[":method"] = "POST"; @@ -2413,14 +2426,11 @@ TEST_P( client_->SendMessage(headers, "", /*fin=*/false); - // Size of headers on the request stream. Zero if headers are sent on the - // header stream. + // Size of headers on the request stream. This is zero if headers are sent on + // the header stream. size_t header_size = 0; - if (VersionUsesHttp3(client_->client() - ->client_session() - ->connection() - ->transport_version())) { - // Determine size of compressed headers. + if (version_.UsesHttp3()) { + // Determine size of headers after QPACK compression in both scenarios. NoopDecoderStreamErrorDelegate decoder_stream_error_delegate; NoopQpackStreamSenderDelegate encoder_stream_sender_delegate; QpackEncoder qpack_encoder(&decoder_stream_error_delegate); @@ -2432,8 +2442,8 @@ TEST_P( qpack_encoder.SetDynamicTableCapacity(kDefaultQpackMaxDynamicTableCapacity); qpack_encoder.SetMaximumBlockedStreams(kDefaultMaximumBlockedStreams); - std::string encoded_headers = - qpack_encoder.EncodeHeaderList(/* stream_id = */ 0, headers, nullptr); + std::string encoded_headers = qpack_encoder.EncodeHeaderList( + /* stream_id = */ 0, headers, nullptr); header_size = encoded_headers.size(); } @@ -2442,9 +2452,11 @@ TEST_P( std::string request_string = "a request body bigger than one packet" + std::string(kMaxOutgoingPacketSize, '.'); + const int expected_bytes_acked = header_size + request_string.length(); + // The TestAckListener will cause a failure if not notified. QuicReferenceCountedPointer<TestAckListener> ack_listener( - new TestAckListener(header_size + request_string.length())); + new TestAckListener()); // Send the request, and register the delegate for ACKs. client_->SendData(request_string, true, ack_listener); @@ -2456,17 +2468,20 @@ TEST_P( client_->SendSynchronousRequest("/bar"); // Make sure the delegate does get the notification it expects. - while (!ack_listener->has_been_notified()) { + while (ack_listener->total_bytes_acked() < expected_bytes_acked) { // Waits for up to 50 ms. client_->client()->WaitForEvents(); } + EXPECT_EQ(ack_listener->total_bytes_acked(), expected_bytes_acked) + << " header_size " << header_size << " request length " + << request_string.length(); } // Send a public reset from the server. TEST_P(EndToEndTest, ServerSendPublicReset) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); QuicConnection* client_connection = GetClientConnection(); QuicConfig* config = client_->client()->session()->config(); EXPECT_TRUE(config->HasReceivedStatelessResetToken()); @@ -2504,7 +2519,7 @@ TEST_P(EndToEndTest, ServerSendPublicReset) { TEST_P(EndToEndTest, ServerSendPublicResetWithDifferentConnectionId) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); QuicConnection* client_connection = GetClientConnection(); QuicConfig* config = client_->client()->session()->config(); EXPECT_TRUE(config->HasReceivedStatelessResetToken()); @@ -2517,7 +2532,7 @@ TEST_P(EndToEndTest, ServerSendPublicResetWithDifferentConnectionId) { QuicFramer framer(server_supported_versions_, QuicTime::Zero(), Perspective::IS_SERVER, kQuicDefaultConnectionIdLength); std::unique_ptr<QuicEncryptedPacket> packet; - testing::NiceMock<MockQuicConnectionDebugVisitor> visitor; + NiceMock<MockQuicConnectionDebugVisitor> visitor; GetClientConnection()->set_debug_visitor(&visitor); if (VersionHasIetfInvariantHeader(client_connection->transport_version())) { packet = framer.BuildIetfStatelessResetPacket(incorrect_connection_id, @@ -2581,7 +2596,7 @@ TEST_P(EndToEndTest, ClientSendPublicResetWithDifferentConnectionId) { TEST_P(EndToEndTest, ServerSendVersionNegotiationWithDifferentConnectionId) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); // Send the version negotiation packet. QuicConnection* client_connection = GetClientConnection(); @@ -2593,7 +2608,7 @@ TEST_P(EndToEndTest, ServerSendVersionNegotiationWithDifferentConnectionId) { VersionHasIetfInvariantHeader(client_connection->transport_version()), client_connection->version().HasLengthPrefixedConnectionIds(), server_supported_versions_)); - testing::NiceMock<MockQuicConnectionDebugVisitor> visitor; + NiceMock<MockQuicConnectionDebugVisitor> visitor; client_connection->set_debug_visitor(&visitor); EXPECT_CALL(visitor, OnIncorrectConnectionId(incorrect_connection_id)) .Times(1); @@ -2733,7 +2748,7 @@ TEST_P(EndToEndTest, BadEncryptedData) { TEST_P(EndToEndTest, CanceledStreamDoesNotBecomeZombie) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); // Lose the request. SetPacketLossPercentage(100); SpdyHeaderBlock headers; @@ -2934,7 +2949,7 @@ TEST_P(EndToEndTest, EarlyResponseFinRecording) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); // A POST that gets an early error response, after the headers are received // and before the body is received, due to invalid content-length. @@ -2982,7 +2997,7 @@ TEST_P(EndToEndTest, EarlyResponseFinRecording) { TEST_P(EndToEndTest, Trailers) { // Test sending and receiving HTTP/2 Trailers (trailing HEADERS frames). ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); // Set reordering to ensure that Trailers arriving before body is ok. SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); @@ -3014,6 +3029,7 @@ class EndToEndTestServerPush : public EndToEndTest { const size_t kNumMaxStreams = 10; EndToEndTestServerPush() : EndToEndTest() { + SetQuicFlag(FLAGS_quic_enable_http3_server_push, true); client_config_.SetMaxBidirectionalStreamsToSend(kNumMaxStreams); server_config_.SetMaxBidirectionalStreamsToSend(kNumMaxStreams); client_config_.SetMaxUnidirectionalStreamsToSend(kNumMaxStreams); @@ -3068,7 +3084,7 @@ INSTANTIATE_TEST_SUITE_P(EndToEndTestsServerPush, TEST_P(EndToEndTestServerPush, ServerPush) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); @@ -3092,10 +3108,7 @@ TEST_P(EndToEndTestServerPush, ServerPush) { EXPECT_EQ(kBody, client_->SendSynchronousRequest( "https://example.com/push_example")); QuicStreamSequencer* sequencer; - if (!VersionUsesHttp3(client_->client() - ->client_session() - ->connection() - ->transport_version())) { + if (!version_.UsesHttp3()) { QuicHeadersStream* headers_stream = QuicSpdySessionPeer::GetHeadersStream(GetClientSession()); sequencer = QuicStreamPeer::sequencer(headers_stream); @@ -3113,27 +3126,23 @@ TEST_P(EndToEndTestServerPush, ServerPush) { QUIC_DVLOG(1) << "response body " << response_body; EXPECT_EQ(expected_body, response_body); } - if (!VersionUsesHttp3(client_->client() - ->client_session() - ->connection() - ->transport_version())) { + if (!version_.UsesHttp3()) { EXPECT_FALSE( QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer)); } } TEST_P(EndToEndTestServerPush, ServerPushUnderLimit) { - if (version_.UsesTls()) { - // TODO(b/155316241): Enable this test for TLS. - Initialize(); - return; - } // 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()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); + if (version_.UsesHttp3()) { + static_cast<QuicSpdySession*>(client_->client()->session()) + ->SetMaxPushId(kMaxQuicStreamId); + } // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); @@ -3177,9 +3186,10 @@ TEST_P(EndToEndTestServerPush, ServerPushUnderLimit) { } TEST_P(EndToEndTestServerPush, ServerPushOverLimitNonBlocking) { - if (version_.UsesTls()) { - // TODO(b/155316241): Enable this test for TLS. - Initialize(); + if (version_.UsesHttp3()) { + // TODO(b/142504641): Re-enable this test when we support push streams + // arriving before the corresponding promises. + ASSERT_TRUE(Initialize()); return; } // Tests that when streams are not blocked by flow control or congestion @@ -3187,15 +3197,10 @@ TEST_P(EndToEndTestServerPush, ServerPushOverLimitNonBlocking) { // streams should still work because all response streams get closed // immediately after pushing resources. ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); - - if (VersionUsesHttp3(client_->client() - ->client_session() - ->connection() - ->transport_version())) { - // TODO(b/142504641): Re-enable this test when we support push streams - // arriving before the corresponding promises. - return; + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); + if (version_.UsesHttp3()) { + static_cast<QuicSpdySession*>(client_->client()->session()) + ->SetMaxPushId(kMaxQuicStreamId); } // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. @@ -3240,11 +3245,6 @@ TEST_P(EndToEndTestServerPush, ServerPushOverLimitNonBlocking) { } TEST_P(EndToEndTestServerPush, ServerPushOverLimitWithBlocking) { - if (version_.UsesTls()) { - // TODO(b/155316241): Enable this test for TLS. - 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 @@ -3261,7 +3261,11 @@ TEST_P(EndToEndTestServerPush, ServerPushOverLimitWithBlocking) { kBodySize * kNumMaxStreams + 1024); ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); + if (version_.UsesHttp3()) { + static_cast<QuicSpdySession*>(client_->client()->session()) + ->SetMaxPushId(kMaxQuicStreamId); + } // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); @@ -3293,9 +3297,13 @@ TEST_P(EndToEndTestServerPush, ServerPushOverLimitWithBlocking) { // Check server session to see if it has max number of outgoing streams opened // though more resources need to be pushed. - server_thread_->Pause(); - EXPECT_EQ(kNumMaxStreams, GetServerSession()->GetNumOpenOutgoingStreams()); - server_thread_->Resume(); + if (!version_.HasIetfQuicFrames()) { + server_thread_->Pause(); + EXPECT_EQ(kNumMaxStreams, + QuicSessionPeer::GetStreamIdManager(GetServerSession()) + ->num_open_outgoing_streams()); + server_thread_->Resume(); + } EXPECT_EQ(1u, client_->num_requests()); EXPECT_EQ(1u, client_->num_responses()); @@ -3334,7 +3342,7 @@ TEST_P(EndToEndTest, DISABLED_TestHugePostWithPacketLoss) { // within a short time. client_->epoll_server()->set_timeout_in_us(0); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); SetPacketLossPercentage(1); // To avoid storing the whole request body in memory, use a loop to repeatedly // send body size of kSizeBytes until the whole request body size is reached. @@ -3392,7 +3400,7 @@ TEST_P(EndToEndTest, DISABLED_TestHugeResponseWithPacketLoss) { initialized_ = true; ASSERT_TRUE(client_->client()->connected()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); SetPacketLossPercentage(1); client_->SendRequest("/huge_response"); client_->WaitForResponse(); @@ -3402,7 +3410,7 @@ TEST_P(EndToEndTest, DISABLED_TestHugeResponseWithPacketLoss) { // Regression test for b/111515567 TEST_P(EndToEndTest, AgreeOnStopWaiting) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); QuicConnection* client_connection = GetClientConnection(); server_thread_->Pause(); @@ -3420,7 +3428,7 @@ TEST_P(EndToEndTest, AgreeOnStopWaitingWithNoStopWaitingOption) { options.push_back(kNSTP); client_config_.SetConnectionOptionsToSend(options); ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); QuicConnection* client_connection = GetClientConnection(); server_thread_->Pause(); @@ -3451,6 +3459,13 @@ TEST_P(EndToEndTest, ReleaseHeadersStreamBufferWhenIdle) { TEST_P(EndToEndTest, WayTooLongRequestHeaders) { ASSERT_TRUE(Initialize()); + if (version_.UsesTls() && !version_.UsesHttp3()) { + // In T050, it took relatively long time for HPACK to compress the header + // while server will detect blackhole on NST message. + // TODO(b/157248143): remove this when the HPACK compression issue is + // understood. + return; + } SpdyHeaderBlock headers; headers[":method"] = "GET"; headers[":path"] = "/foo"; @@ -3493,7 +3508,7 @@ class WindowUpdateObserver : public QuicConnectionDebugVisitor { TEST_P(EndToEndTest, WindowUpdateInAck) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); WindowUpdateObserver observer; QuicConnection* client_connection = GetClientConnection(); client_connection->set_debug_visitor(&observer); @@ -3514,7 +3529,7 @@ TEST_P(EndToEndTest, WindowUpdateInAck) { TEST_P(EndToEndTest, SendStatelessResetTokenInShlo) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); QuicConfig* config = client_->client()->session()->config(); EXPECT_TRUE(config->HasReceivedStatelessResetToken()); EXPECT_EQ(QuicUtils::GenerateStatelessResetToken( @@ -3526,10 +3541,6 @@ TEST_P(EndToEndTest, SendStatelessResetTokenInShlo) { // Regression test for b/116200989. TEST_P(EndToEndTest, SendStatelessResetIfServerConnectionClosedLocallyDuringHandshake) { - if (!GetQuicReloadableFlag(quic_notify_handshaker_on_connection_close)) { - ASSERT_TRUE(Initialize()); - return; - } connect_to_server_on_initialize_ = false; ASSERT_TRUE(Initialize()); @@ -3674,8 +3685,7 @@ TEST_P(EndToEndTest, QUIC_TEST_DISABLED_IN_CHROME(PreSharedKeyMismatch)) { // 2. Crypto handshake has not completed, Initialize() returns true. The call // to WaitForCryptoHandshakeConfirmed() will wait for the handshake and // return whether it is successful. - ASSERT_FALSE(Initialize() && - client_->client()->WaitForCryptoHandshakeConfirmed()); + ASSERT_FALSE(Initialize() && client_->client()->WaitForOneRttKeysAvailable()); EXPECT_THAT(client_->connection_error(), IsError(QUIC_HANDSHAKE_TIMEOUT)); } @@ -3696,8 +3706,7 @@ TEST_P(EndToEndTest, QUIC_TEST_DISABLED_IN_CHROME(PreSharedKeyNoClient)) { return; } - ASSERT_FALSE(Initialize() && - client_->client()->WaitForCryptoHandshakeConfirmed()); + ASSERT_FALSE(Initialize() && client_->client()->WaitForOneRttKeysAvailable()); EXPECT_THAT(client_->connection_error(), IsError(QUIC_HANDSHAKE_TIMEOUT)); } @@ -3718,8 +3727,7 @@ TEST_P(EndToEndTest, QUIC_TEST_DISABLED_IN_CHROME(PreSharedKeyNoServer)) { return; } - ASSERT_FALSE(Initialize() && - client_->client()->WaitForCryptoHandshakeConfirmed()); + ASSERT_FALSE(Initialize() && client_->client()->WaitForOneRttKeysAvailable()); EXPECT_THAT(client_->connection_error(), IsError(QUIC_HANDSHAKE_TIMEOUT)); } @@ -3738,7 +3746,7 @@ TEST_P(EndToEndTest, RequestAndStreamRstInOnePacket) { server_hostname_, "/test_url", std::move(response_headers), response_body, QuicBackendResponse::INCOMPLETE_RESPONSE); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); client_->WaitForDelayedAcks(); QuicSession* session = GetClientSession(); @@ -3760,7 +3768,7 @@ TEST_P(EndToEndTest, RequestAndStreamRstInOnePacket) { TEST_P(EndToEndTest, ResetStreamOnTtlExpires) { ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForHandshakeConfirmed()); SetPacketLossPercentage(30); QuicSpdyClientStream* stream = client_->GetOrCreateStream(); @@ -3781,7 +3789,7 @@ TEST_P(EndToEndTest, SendMessages) { return; } ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); QuicSession* client_session = GetClientSession(); QuicConnection* client_connection = client_session->connection(); @@ -3911,16 +3919,14 @@ TEST_P(EndToEndPacketReorderingTest, ReorderedConnectivityProbing) { server_connection->GetStats().num_connectivity_probing_received); server_thread_->Resume(); - EXPECT_EQ( + // Server definitely responded to the connectivity probing. Sometime it also + // sends a padded ping that is not a connectivity probing, which is recognized + // as connectivity probing because client's self address is ANY. + EXPECT_LE( 1u, GetClientConnection()->GetStats().num_connectivity_probing_received); } TEST_P(EndToEndPacketReorderingTest, Buffer0RttRequest) { - if (version_.UsesTls()) { - // TODO(b/152551499): Re-enable this test when TLS supports 0-RTT. - Initialize(); - return; - } ASSERT_TRUE(Initialize()); // Finish one request to make sure handshake established. client_->SendSynchronousRequest("/foo"); @@ -3933,7 +3939,7 @@ TEST_P(EndToEndPacketReorderingTest, Buffer0RttRequest) { // Only send out a CHLO. client_->client()->Initialize(); client_->client()->StartConnect(); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); ASSERT_TRUE(client_->client()->connected()); // Send a request before handshake finishes. @@ -3947,14 +3953,10 @@ TEST_P(EndToEndPacketReorderingTest, Buffer0RttRequest) { client_->WaitForResponse(); EXPECT_EQ(kBarResponseBody, client_->response_body()); QuicConnectionStats client_stats = GetClientConnection()->GetStats(); - if (GetQuicReloadableFlag(quic_advance_ack_timeout_update)) { - // Client sends CHLO in packet 1 and retransmitted in packet 2. Because of - // the delay, server processes packet 2 and later drops packet 1. ACK is - // bundled with SHLO, such that 1 can be detected loss by time threshold. - EXPECT_LE(0u, client_stats.packets_lost); - } else { - EXPECT_EQ(0u, client_stats.packets_lost); - } + // Client sends CHLO in packet 1 and retransmitted in packet 2. Because of + // the delay, server processes packet 2 and later drops packet 1. ACK is + // bundled with SHLO, such that 1 can be detected loss by time threshold. + EXPECT_LE(0u, client_stats.packets_lost); EXPECT_TRUE(client_->client()->EarlyDataAccepted()); } @@ -4010,7 +4012,7 @@ TEST_P(EndToEndTest, SimpleStopSendingTest) { server_hostname_, "/test_url", std::move(response_headers), response_body, kStopSendingTestCode); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); client_->WaitForDelayedAcks(); QuicSession* session = GetClientSession(); @@ -4188,7 +4190,7 @@ TEST_P(EndToEndTest, TooBigStreamIdClosesConnection) { // Only runs for IETF QUIC. return; } - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); std::string body(kMaxOutgoingPacketSize, 'a'); SpdyHeaderBlock headers; @@ -4216,17 +4218,15 @@ TEST_P(EndToEndTest, TooBigStreamIdClosesConnection) { } TEST_P(EndToEndTest, TestMaxPushId) { - if (version_.UsesTls() || - !VersionHasIetfQuicFrames(version_.transport_version)) { - // TODO(b/155316241): Enable this test for TLS. - // Only runs for IETF QUIC. + if (!version_.HasIetfQuicFrames()) { + // MaxPushId is only implemented for IETF QUIC. Initialize(); return; } - // Has to be before version test, see EndToEndTest::TearDown() + SetQuicFlag(FLAGS_quic_enable_http3_server_push, true); ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); static_cast<QuicSpdySession*>(client_->client()->session()) ->SetMaxPushId(kMaxQuicStreamId); @@ -4239,18 +4239,135 @@ TEST_P(EndToEndTest, TestMaxPushId) { ->CanCreatePushStreamWithId(kMaxQuicStreamId)); } -TEST_P(EndToEndTest, DISABLED_CustomTransportParameters) { - // TODO(b/155316241): Enable this test. +TEST_P(EndToEndTest, CustomTransportParameters) { + if (!version_.UsesTls()) { + // Custom transport parameters are only supported with TLS. + ASSERT_TRUE(Initialize()); + return; + } constexpr auto kCustomParameter = static_cast<TransportParameters::TransportParameterId>(0xff34); client_config_.custom_transport_parameters_to_send()[kCustomParameter] = "test"; + NiceMock<MockQuicConnectionDebugVisitor> visitor; + connection_debug_visitor_ = &visitor; + EXPECT_CALL(visitor, OnTransportParametersSent(_)) + .WillOnce(Invoke([kCustomParameter]( + const TransportParameters& transport_parameters) { + ASSERT_NE(transport_parameters.custom_parameters.find(kCustomParameter), + transport_parameters.custom_parameters.end()); + EXPECT_EQ(transport_parameters.custom_parameters.at(kCustomParameter), + "test"); + })); + EXPECT_CALL(visitor, OnTransportParametersReceived(_)).Times(1); ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); - EXPECT_EQ(server_config_.received_custom_transport_parameters().at( + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); + + server_thread_->Pause(); + QuicConfig server_config = *GetServerSession()->config(); + if (GetQuicReloadableFlag(quic_save_user_agent_in_quic_session)) { + EXPECT_EQ(GetServerSession()->user_agent_id().value_or("MissingUserAgent"), + kTestUserAgentId); + } else { + EXPECT_FALSE(GetServerSession()->user_agent_id().has_value()); + } + server_thread_->Resume(); + ASSERT_NE(server_config.received_custom_transport_parameters().find( kCustomParameter), - "test"); + server_config.received_custom_transport_parameters().end()); + EXPECT_EQ( + server_config.received_custom_transport_parameters().at(kCustomParameter), + "test"); +} + +TEST_P(EndToEndTest, LegacyVersionEncapsulation) { + if (!version_.HasLongHeaderLengths()) { + // Decapsulating Legacy Version Encapsulation packets from these versions + // is not currently supported in QuicDispatcher. + ASSERT_TRUE(Initialize()); + return; + } + SetQuicReloadableFlag(quic_dont_pad_chlo, true); + SetQuicReloadableFlag(quic_dispatcher_legacy_version_encapsulation, true); + client_config_.SetClientConnectionOptions(QuicTagVector{kQLVE}); + ASSERT_TRUE(Initialize()); + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + EXPECT_GT(GetClientConnection() + ->GetStats() + .sent_legacy_version_encapsulated_packets, + 0u); +} + +TEST_P(EndToEndTest, LegacyVersionEncapsulationWithMultiPacketChlo) { + if (!version_.HasLongHeaderLengths()) { + // Decapsulating Legacy Version Encapsulation packets from these versions + // is not currently supported in QuicDispatcher. + ASSERT_TRUE(Initialize()); + return; + } + if (!version_.UsesTls()) { + // This test uses custom transport parameters to increase the size of the + // CHLO, and those are only supported with TLS. + ASSERT_TRUE(Initialize()); + return; + } + SetQuicReloadableFlag(quic_dont_pad_chlo, true); + SetQuicReloadableFlag(quic_dispatcher_legacy_version_encapsulation, true); + client_config_.SetClientConnectionOptions(QuicTagVector{kQLVE}); + constexpr auto kCustomParameter = + static_cast<TransportParameters::TransportParameterId>(0xff34); + client_config_.custom_transport_parameters_to_send()[kCustomParameter] = + std::string(2000, '?'); + ASSERT_TRUE(Initialize()); + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + EXPECT_GT(GetClientConnection() + ->GetStats() + .sent_legacy_version_encapsulated_packets, + 0u); +} + +TEST_P(EndToEndTest, LegacyVersionEncapsulationWithVersionNegotiation) { + if (!version_.HasLongHeaderLengths()) { + // Decapsulating Legacy Version Encapsulation packets from these versions + // is not currently supported in QuicDispatcher. + ASSERT_TRUE(Initialize()); + return; + } + client_supported_versions_.insert(client_supported_versions_.begin(), + QuicVersionReservedForNegotiation()); + SetQuicReloadableFlag(quic_dont_pad_chlo, true); + SetQuicReloadableFlag(quic_dispatcher_legacy_version_encapsulation, true); + client_config_.SetClientConnectionOptions(QuicTagVector{kQLVE}); + ASSERT_TRUE(Initialize()); + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + EXPECT_GT(GetClientConnection() + ->GetStats() + .sent_legacy_version_encapsulated_packets, + 0u); +} + +TEST_P(EndToEndTest, LegacyVersionEncapsulationWithLoss) { + if (!version_.HasLongHeaderLengths()) { + // Decapsulating Legacy Version Encapsulation packets from these versions + // is not currently supported in QuicDispatcher. + ASSERT_TRUE(Initialize()); + return; + } + SetPacketLossPercentage(30); + SetQuicReloadableFlag(quic_dont_pad_chlo, true); + SetQuicReloadableFlag(quic_dispatcher_legacy_version_encapsulation, true); + client_config_.SetClientConnectionOptions(QuicTagVector{kQLVE}); + ASSERT_TRUE(Initialize()); + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + EXPECT_GT(GetClientConnection() + ->GetStats() + .sent_legacy_version_encapsulated_packets, + 0u); } } // namespace diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_constants.h b/chromium/net/third_party/quiche/src/quic/core/http/http_constants.h index 17afe1bea19..285d3792d7a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_constants.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_constants.h @@ -42,6 +42,7 @@ const QuicByteCount kDefaultQpackMaxDynamicTableCapacity = 64 * 1024; // 64 KB // SETTINGS_QPACK_BLOCKED_STREAMS. const uint64_t kDefaultMaximumBlockedStreams = 100; +const char kUserAgentHeaderName[] = "user-agent"; } // namespace quic #endif // QUICHE_QUIC_CORE_HTTP_HTTP_CONSTANTS_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info_test.cc index a5996260d23..0836f9a0d56 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info_test.cc @@ -227,9 +227,6 @@ TEST_F(QuicClientPromisedInfoTest, PushPromiseMismatch) { EXPECT_CALL(*connection_, SendControlFrame(_)); EXPECT_CALL(*connection_, OnStreamReset(promise_id_, QUIC_PROMISE_VARY_MISMATCH)); - if (!session_.break_close_loop()) { - EXPECT_CALL(session_, CloseStream(promise_id_)); - } promised->HandleClientRequest(client_request_, &delegate); } @@ -305,9 +302,6 @@ TEST_F(QuicClientPromisedInfoTest, PushPromiseWaitCancels) { session_.GetOrCreateStream(promise_id_); // Cancel the promised stream. - if (!session_.break_close_loop()) { - EXPECT_CALL(session_, CloseStream(promise_id_)); - } EXPECT_CALL(*connection_, SendControlFrame(_)); EXPECT_CALL(*connection_, OnStreamReset(promise_id_, QUIC_STREAM_CANCELLED)); promised->Cancel(); @@ -331,17 +325,10 @@ TEST_F(QuicClientPromisedInfoTest, PushPromiseDataClosed) { promise_stream->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), headers); - if (!session_.break_close_loop()) { - EXPECT_CALL(session_, CloseStream(promise_id_)); - } EXPECT_CALL(*connection_, SendControlFrame(_)); EXPECT_CALL(*connection_, OnStreamReset(promise_id_, QUIC_STREAM_PEER_GOING_AWAY)); - if (session_.break_close_loop()) { - session_.ResetStream(promise_id_, QUIC_STREAM_PEER_GOING_AWAY, 0); - } else { - session_.SendRstStream(promise_id_, QUIC_STREAM_PEER_GOING_AWAY, 0); - } + session_.ResetStream(promise_id_, QUIC_STREAM_PEER_GOING_AWAY, 0); // Now initiate rendezvous. TestPushPromiseDelegate delegate(/*match=*/true); 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 d24e3ddaa88..27de70d3fa4 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 @@ -131,8 +131,7 @@ bool QuicReceiveControlStream::OnSettingsFrameStart( bool QuicReceiveControlStream::OnSettingsFrame(const SettingsFrame& frame) { QUIC_DVLOG(1) << "Control Stream " << id() << " received settings frame: " << frame; - spdy_session_->OnSettingsFrame(frame); - return true; + return spdy_session_->OnSettingsFrame(frame); } bool QuicReceiveControlStream::OnDataFrameStart(QuicByteCount /*header_length*/, diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.cc index 64c306a4431..59574a146b9 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.cc @@ -18,18 +18,12 @@ namespace quic { -QuicSendControlStream::QuicSendControlStream( - QuicStreamId id, - QuicSpdySession* spdy_session, - uint64_t qpack_maximum_dynamic_table_capacity, - uint64_t qpack_maximum_blocked_streams, - uint64_t max_inbound_header_list_size) +QuicSendControlStream::QuicSendControlStream(QuicStreamId id, + QuicSpdySession* spdy_session, + const SettingsFrame& settings) : QuicStream(id, spdy_session, /*is_static = */ true, WRITE_UNIDIRECTIONAL), settings_sent_(false), - qpack_maximum_dynamic_table_capacity_( - qpack_maximum_dynamic_table_capacity), - qpack_maximum_blocked_streams_(qpack_maximum_blocked_streams), - max_inbound_header_list_size_(max_inbound_header_list_size), + settings_(settings), spdy_session_(spdy_session) {} void QuicSendControlStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) { @@ -56,13 +50,7 @@ void QuicSendControlStream::MaybeSendSettingsFrame() { WriteOrBufferData(quiche::QuicheStringPiece(writer.data(), writer.length()), false, nullptr); - SettingsFrame settings; - settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = - qpack_maximum_dynamic_table_capacity_; - settings.values[SETTINGS_QPACK_BLOCKED_STREAMS] = - qpack_maximum_blocked_streams_; - settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = - max_inbound_header_list_size_; + SettingsFrame settings = settings_; // https://tools.ietf.org/html/draft-ietf-quic-http-25#section-7.2.4.1 // specifies that setting identifiers of 0x1f * N + 0x21 are reserved and // greasing should be attempted. diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h index 03bd1f747aa..c14254f4cb7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h @@ -23,9 +23,7 @@ class QUIC_EXPORT_PRIVATE QuicSendControlStream : public QuicStream { // only be accessed through the session. QuicSendControlStream(QuicStreamId id, QuicSpdySession* session, - uint64_t qpack_maximum_dynamic_table_capacity, - uint64_t qpack_maximum_blocked_streams, - uint64_t max_inbound_header_list_size); + const SettingsFrame& settings); QuicSendControlStream(const QuicSendControlStream&) = delete; QuicSendControlStream& operator=(const QuicSendControlStream&) = delete; ~QuicSendControlStream() override = default; @@ -59,12 +57,8 @@ class QUIC_EXPORT_PRIVATE QuicSendControlStream : public QuicStream { // Track if a settings frame is already sent. bool settings_sent_; - // SETTINGS_QPACK_MAX_TABLE_CAPACITY value to send. - const uint64_t qpack_maximum_dynamic_table_capacity_; - // SETTINGS_QPACK_BLOCKED_STREAMS value to send. - const uint64_t qpack_maximum_blocked_streams_; - // SETTINGS_MAX_HEADER_LIST_SIZE value to send. - const uint64_t max_inbound_header_list_size_; + // SETTINGS values to send. + const SettingsFrame settings_; QuicSpdySession* const spdy_session_; }; 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 27db719fccd..ef367d98cb5 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 @@ -39,6 +39,9 @@ void QuicServerSessionBase::Initialize() { crypto_stream_ = CreateQuicCryptoServerStream(crypto_config_, compressed_certs_cache_); QuicSpdySession::Initialize(); + if (GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls)) { + SendSettingsToCryptoStream(); + } } void QuicServerSessionBase::OnConfigNegotiated() { @@ -260,4 +263,19 @@ int32_t QuicServerSessionBase::BandwidthToCachedParameterBytesPerSecond( bandwidth.ToBytesPerSecond(), std::numeric_limits<uint32_t>::max())); } +void QuicServerSessionBase::SendSettingsToCryptoStream() { + if (!version().UsesTls()) { + return; + } + std::unique_ptr<char[]> buffer; + QuicByteCount buffer_size = + HttpEncoder::SerializeSettingsFrame(settings(), &buffer); + + std::unique_ptr<ApplicationState> serialized_settings = + std::make_unique<ApplicationState>(buffer.get(), + buffer.get() + buffer_size); + GetMutableCryptoStream()->SetServerApplicationStateForResumption( + std::move(serialized_settings)); +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.h index bf7a5bbb693..b4a467fedff 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.h @@ -99,6 +99,11 @@ class QUIC_EXPORT_PRIVATE QuicServerSessionBase : public QuicSpdySession { friend class test::QuicServerSessionBasePeer; friend class test::QuicSimpleServerSessionPeer; + // Informs the QuicCryptoStream of the SETTINGS that will be used on this + // connection, so that the server crypto stream knows whether to accept 0-RTT + // data. + void SendSettingsToCryptoStream(); + const QuicCryptoServerConfig* crypto_config_; // The cache which contains most recently compressed certs. 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 c482856b61b..6af67a656f4 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 @@ -157,6 +157,9 @@ class QuicServerSessionBaseTest : public QuicTestWithParam<ParsedQuicVersion> { QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow( session_->config(), kMinimumFlowControlSendWindow); session_->OnConfigNegotiated(); + if (connection_->version().SupportsAntiAmplificationLimit()) { + QuicConnectionPeer::SetAddressValidated(connection_); + } } QuicStreamId GetNthClientInitiatedBidirectionalId(int n) { @@ -349,6 +352,7 @@ TEST_P(QuicServerSessionBaseTest, MaxOpenStreams) { // more than the negotiated stream limit to deal with rare cases where a // client FIN/RST is lost. + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_->OnConfigNegotiated(); if (!VersionHasIetfQuicFrames(transport_version())) { // The slightly increased stream limit is set during config negotiation. It @@ -401,6 +405,7 @@ TEST_P(QuicServerSessionBaseTest, MaxAvailableBidirectionalStreams) { // streams available. The server accepts slightly more than the negotiated // stream limit to deal with rare cases where a client FIN/RST is lost. + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_->OnConfigNegotiated(); const size_t kAvailableStreamLimit = session_->MaxAvailableBidirectionalStreams(); @@ -506,6 +511,7 @@ TEST_P(QuicServerSessionBaseTest, BandwidthEstimates) { QuicTagVector copt; copt.push_back(kBWRE); QuicConfigPeer::SetReceivedConnectionOptions(session_->config(), copt); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_->OnConfigNegotiated(); EXPECT_TRUE( QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get())); @@ -696,6 +702,7 @@ TEST_P(QuicServerSessionBaseTest, BandwidthMaxEnablesResumption) { QuicTagVector copt; copt.push_back(kBWMX); QuicConfigPeer::SetReceivedConnectionOptions(session_->config(), copt); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_->OnConfigNegotiated(); EXPECT_TRUE( QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get())); @@ -704,6 +711,7 @@ TEST_P(QuicServerSessionBaseTest, BandwidthMaxEnablesResumption) { TEST_P(QuicServerSessionBaseTest, NoBandwidthResumptionByDefault) { EXPECT_FALSE( QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get())); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_->OnConfigNegotiated(); EXPECT_FALSE( QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get())); diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.cc index 6127e1b682e..066adb9f859 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.cc @@ -39,6 +39,13 @@ QuicSpdyClientSession::~QuicSpdyClientSession() = default; void QuicSpdyClientSession::Initialize() { crypto_stream_ = CreateQuicCryptoStream(); + if (config()->HasClientRequestedIndependentOption(kQLVE, + Perspective::IS_CLIENT)) { + connection()->EnableLegacyVersionEncapsulation(server_id_.host()); + // Legacy Version Encapsulation needs CHLO padding to be disabled. + // TODO(dschinazi) remove this line once we deprecate quic_dont_pad_chlo. + crypto_config_->set_disable_chlo_padding(true); + } QuicSpdyClientSessionBase::Initialize(); } @@ -152,7 +159,7 @@ bool QuicSpdyClientSession::ShouldCreateIncomingStream(QuicStreamId id) { } if (VersionHasIetfQuicFrames(transport_version()) && - QuicUtils::IsBidirectionalStreamId(id)) { + QuicUtils::IsBidirectionalStreamId(id, version())) { connection()->CloseConnection( QUIC_HTTP_SERVER_INITIATED_BIDIRECTIONAL_STREAM, "Server created bidirectional stream.", @@ -186,7 +193,7 @@ QuicSpdyClientSession::CreateQuicCryptoStream() { return std::make_unique<QuicCryptoClientStream>( server_id_, this, crypto_config_->proof_verifier()->CreateDefaultContext(), crypto_config_, - this, /*has_application_state = */ true); + this, /*has_application_state = */ version().UsesHttp3()); } bool QuicSpdyClientSession::IsAuthorized(const std::string& /*authority*/) { diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc index 63de57c31bc..b433c56f687 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc @@ -204,26 +204,20 @@ void QuicSpdyClientSessionBase::ResetPromised( QuicStreamId id, QuicRstStreamErrorCode error_code) { DCHECK(QuicUtils::IsServerInitiatedStreamId(transport_version(), id)); - if (break_close_loop()) { - ResetStream(id, error_code, 0); - } else { - SendRstStream(id, error_code, 0); - } + ResetStream(id, error_code, 0); if (!IsOpenStream(id) && !IsClosedStream(id)) { MaybeIncreaseLargestPeerStreamId(id); } } -void QuicSpdyClientSessionBase::CloseStreamInner(QuicStreamId stream_id, - bool rst_sent) { - QuicSpdySession::CloseStreamInner(stream_id, rst_sent); +void QuicSpdyClientSessionBase::CloseStream(QuicStreamId stream_id) { + QuicSpdySession::CloseStream(stream_id); if (!VersionUsesHttp3(transport_version())) { headers_stream()->MaybeReleaseSequencerBuffer(); } } void QuicSpdyClientSessionBase::OnStreamClosed(QuicStreamId stream_id) { - DCHECK(break_close_loop()); QuicSpdySession::OnStreamClosed(stream_id); if (!VersionUsesHttp3(transport_version())) { headers_stream()->MaybeReleaseSequencerBuffer(); @@ -234,15 +228,55 @@ bool QuicSpdyClientSessionBase::ShouldReleaseHeadersStreamSequencerBuffer() { return !HasActiveRequestStreams() && promised_by_id_.empty(); } -void QuicSpdyClientSessionBase::OnSettingsFrame(const SettingsFrame& frame) { - QuicSpdySession::OnSettingsFrame(frame); +bool QuicSpdyClientSessionBase::ShouldKeepConnectionAlive() const { + return QuicSpdySession::ShouldKeepConnectionAlive() || + num_outgoing_draining_streams() > 0; +} + +bool QuicSpdyClientSessionBase::OnSettingsFrame(const SettingsFrame& frame) { + if (!was_zero_rtt_rejected()) { + if (max_outbound_header_list_size() != std::numeric_limits<size_t>::max() && + frame.values.find(SETTINGS_MAX_HEADER_LIST_SIZE) == + frame.values.end()) { + CloseConnectionWithDetails( + QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH, + "Server accepted 0-RTT but omitted non-default " + "SETTINGS_MAX_HEADER_LIST_SIZE"); + return false; + } + + if (qpack_encoder()->maximum_blocked_streams() != 0 && + frame.values.find(SETTINGS_QPACK_BLOCKED_STREAMS) == + frame.values.end()) { + CloseConnectionWithDetails( + QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH, + "Server accepted 0-RTT but omitted non-default " + "SETTINGS_QPACK_BLOCKED_STREAMS"); + return false; + } + + if (qpack_encoder()->MaximumDynamicTableCapacity() != 0 && + frame.values.find(SETTINGS_QPACK_MAX_TABLE_CAPACITY) == + frame.values.end()) { + CloseConnectionWithDetails( + QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH, + "Server accepted 0-RTT but omitted non-default " + "SETTINGS_QPACK_MAX_TABLE_CAPACITY"); + return false; + } + } + + if (!QuicSpdySession::OnSettingsFrame(frame)) { + return false; + } std::unique_ptr<char[]> buffer; QuicByteCount frame_length = HttpEncoder::SerializeSettingsFrame(frame, &buffer); auto serialized_data = std::make_unique<ApplicationState>( buffer.get(), buffer.get() + frame_length); - static_cast<QuicCryptoClientStreamBase*>(GetMutableCryptoStream()) - ->OnApplicationState(std::move(serialized_data)); + GetMutableCryptoStream()->SetServerApplicationStateForResumption( + std::move(serialized_data)); + return true; } } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h index a109d185a46..31924793f6b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h @@ -102,7 +102,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdyClientSessionBase void ResetPromised(QuicStreamId id, QuicRstStreamErrorCode error_code); // Release headers stream's sequencer buffer if it's empty. - void CloseStreamInner(QuicStreamId stream_id, bool rst_sent) override; + void CloseStream(QuicStreamId stream_id) override; // Release headers stream's sequencer buffer if it's empty. void OnStreamClosed(QuicStreamId stream_id) override; @@ -110,6 +110,9 @@ class QUIC_EXPORT_PRIVATE QuicSpdyClientSessionBase // Returns true if there are no active requests and no promised streams. bool ShouldReleaseHeadersStreamSequencerBuffer() override; + // Override to wait for all received responses to be consumed by application. + bool ShouldKeepConnectionAlive() const override; + size_t get_max_promises() const { return max_open_incoming_unidirectional_streams() * kMaxPromisedStreamsMultiplier; @@ -120,7 +123,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdyClientSessionBase } // Override to serialize the settings and pass it down to the handshaker. - void OnSettingsFrame(const SettingsFrame& frame) override; + bool OnSettingsFrame(const SettingsFrame& frame) override; private: // For QuicSpdyClientStream to detect that a response corresponds to a 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 7d4ae810115..3464a7eda90 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 @@ -16,10 +16,12 @@ #include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h" #include "net/third_party/quiche/src/quic/core/http/spdy_server_push_utils.h" #include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/quic/core/quic_versions.h" #include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" @@ -38,12 +40,13 @@ #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" using spdy::SpdyHeaderBlock; -using testing::_; -using testing::AnyNumber; -using testing::AtLeast; -using testing::AtMost; -using testing::Invoke; -using testing::Truly; +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::AtLeast; +using ::testing::AtMost; +using ::testing::Invoke; +using ::testing::StrictMock; +using ::testing::Truly; namespace quic { namespace test { @@ -93,8 +96,12 @@ class QuicSpdyClientSessionTest : public QuicTestWithParam<ParsedQuicVersion> { QuicUtils::GetInvalidStreamId(GetParam().transport_version)) { auto client_cache = std::make_unique<test::SimpleSessionCache>(); client_session_cache_ = client_cache.get(); - crypto_config_ = std::make_unique<QuicCryptoClientConfig>( + SetQuicReloadableFlag(quic_enable_tls_resumption, true); + SetQuicReloadableFlag(quic_enable_zero_rtt_for_tls, true); + SetQuicReloadableFlag(quic_fix_gquic_stream_type, true); + client_crypto_config_ = std::make_unique<QuicCryptoClientConfig>( crypto_test_utils::ProofVerifierForTesting(), std::move(client_cache)); + server_crypto_config_ = crypto_test_utils::CryptoServerConfigForTesting(); Initialize(); // Advance the time, because timers do not like uninitialized times. connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); @@ -107,14 +114,16 @@ class QuicSpdyClientSessionTest : public QuicTestWithParam<ParsedQuicVersion> { void Initialize() { session_.reset(); - connection_ = new PacketSavingConnection(&helper_, &alarm_factory_, - Perspective::IS_CLIENT, - SupportedVersions(GetParam())); + connection_ = new ::testing::NiceMock<PacketSavingConnection>( + &helper_, &alarm_factory_, Perspective::IS_CLIENT, + SupportedVersions(GetParam())); session_ = std::make_unique<TestQuicSpdyClientSession>( DefaultQuicConfig(), SupportedVersions(GetParam()), connection_, - QuicServerId(kServerHostname, kPort, false), crypto_config_.get(), - &push_promise_index_); + QuicServerId(kServerHostname, kPort, false), + client_crypto_config_.get(), &push_promise_index_); session_->Initialize(); + crypto_stream_ = static_cast<QuicCryptoClientStream*>( + session_->GetMutableCryptoStream()); push_promise_[":path"] = "/bar"; push_promise_[":authority"] = "www.google.com"; push_promise_[":method"] = "GET"; @@ -157,13 +166,11 @@ class QuicSpdyClientSessionTest : public QuicTestWithParam<ParsedQuicVersion> { void CompleteCryptoHandshake(uint32_t server_max_incoming_streams) { if (VersionHasIetfQuicFrames(connection_->transport_version())) { EXPECT_CALL(*connection_, SendControlFrame(_)) - .Times(testing::AnyNumber()) + .Times(::testing::AnyNumber()) .WillRepeatedly(Invoke( this, &QuicSpdyClientSessionTest::ClearMaxStreamsControlFrame)); } session_->CryptoConnect(); - QuicCryptoClientStream* stream = static_cast<QuicCryptoClientStream*>( - session_->GetMutableCryptoStream()); QuicConfig config = DefaultQuicConfig(); if (VersionHasIetfQuicFrames(connection_->transport_version())) { config.SetMaxUnidirectionalStreamsToSend(server_max_incoming_streams); @@ -171,32 +178,45 @@ class QuicSpdyClientSessionTest : public QuicTestWithParam<ParsedQuicVersion> { } else { config.SetMaxBidirectionalStreamsToSend(server_max_incoming_streams); } - SetQuicReloadableFlag(quic_enable_tls_resumption, true); - SetQuicReloadableFlag(quic_enable_zero_rtt_for_tls, true); - std::unique_ptr<QuicCryptoServerConfig> crypto_config = - crypto_test_utils::CryptoServerConfigForTesting(); crypto_test_utils::HandshakeWithFakeServer( - &config, crypto_config.get(), &helper_, &alarm_factory_, connection_, - stream, AlpnForVersion(connection_->version())); + &config, server_crypto_config_.get(), &helper_, &alarm_factory_, + connection_, crypto_stream_, AlpnForVersion(connection_->version())); } void CreateConnection() { - connection_ = new PacketSavingConnection(&helper_, &alarm_factory_, - Perspective::IS_CLIENT, - SupportedVersions(GetParam())); + connection_ = new ::testing::NiceMock<PacketSavingConnection>( + &helper_, &alarm_factory_, Perspective::IS_CLIENT, + SupportedVersions(GetParam())); // Advance the time, because timers do not like uninitialized times. connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); session_ = std::make_unique<TestQuicSpdyClientSession>( DefaultQuicConfig(), SupportedVersions(GetParam()), connection_, - QuicServerId(kServerHostname, kPort, false), crypto_config_.get(), - &push_promise_index_); + QuicServerId(kServerHostname, kPort, false), + client_crypto_config_.get(), &push_promise_index_); session_->Initialize(); + crypto_stream_ = static_cast<QuicCryptoClientStream*>( + session_->GetMutableCryptoStream()); } - std::unique_ptr<QuicCryptoClientConfig> crypto_config_; + void CompleteFirstConnection() { + CompleteCryptoHandshake(); + EXPECT_FALSE(session_->GetCryptoStream()->IsResumption()); + if (session_->version().UsesHttp3()) { + SettingsFrame settings; + settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 2; + settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = 5; + settings.values[256] = 4; // unknown setting + session_->OnSettingsFrame(settings); + } + } + + // Owned by |session_|. + QuicCryptoClientStream* crypto_stream_; + std::unique_ptr<QuicCryptoServerConfig> server_crypto_config_; + std::unique_ptr<QuicCryptoClientConfig> client_crypto_config_; MockQuicConnectionHelper helper_; MockAlarmFactory alarm_factory_; - PacketSavingConnection* connection_; + ::testing::NiceMock<PacketSavingConnection>* connection_; std::unique_ptr<TestQuicSpdyClientSession> session_; QuicClientPushPromiseIndex push_promise_index_; SpdyHeaderBlock push_promise_; @@ -219,8 +239,6 @@ TEST_P(QuicSpdyClientSessionTest, NoEncryptionAfterInitialEncryption) { if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) { // This test relies on resumption and is QUIC crypto specific, so it is // disabled for TLS. - // TODO(nharper): Add support for resumption to the TLS handshake, and fix - // this test to not rely on QUIC crypto. return; } // Complete a handshake in order to prime the crypto config for 0-RTT. @@ -229,7 +247,6 @@ TEST_P(QuicSpdyClientSessionTest, NoEncryptionAfterInitialEncryption) { // Now create a second session using the same crypto config. Initialize(); - EXPECT_CALL(*connection_, OnCanWrite()); // Starting the handshake should move immediately to encryption // established and will allow streams to be created. session_->CryptoConnect(); @@ -257,7 +274,7 @@ TEST_P(QuicSpdyClientSessionTest, NoEncryptionAfterInitialEncryption) { char data[] = "hello world"; EXPECT_QUIC_BUG( session_->WritevData(stream->id(), QUICHE_ARRAYSIZE(data), 0, NO_FIN, - NOT_RETRANSMISSION, QuicheNullOpt), + NOT_RETRANSMISSION, QUICHE_NULLOPT), "Client: Try to send data of stream"); } @@ -343,11 +360,7 @@ TEST_P(QuicSpdyClientSessionTest, ResetAndTrailers) { .Times(AtLeast(1)) .WillRepeatedly(Invoke(&ClearControlFrame)); EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(1); - if (session_->break_close_loop()) { - session_->ResetStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY, 0); - } else { - session_->SendRstStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY, 0); - } + session_->ResetStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY, 0); // A new stream cannot be created as the reset stream still counts as an open // outgoing stream until closed by the server. @@ -399,11 +412,7 @@ TEST_P(QuicSpdyClientSessionTest, ReceivedMalformedTrailersAfterSendingRst) { .Times(AtLeast(1)) .WillRepeatedly(Invoke(&ClearControlFrame)); EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(1); - if (session_->break_close_loop()) { - session_->ResetStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY, 0); - } else { - session_->SendRstStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY, 0); - } + session_->ResetStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY, 0); // The stream receives trailers with final byte offset, but the header value // is non-numeric and should be treated as malformed. @@ -853,12 +862,7 @@ TEST_P(QuicSpdyClientSessionTest, ResetPromised) { EXPECT_CALL(*connection_, SendControlFrame(_)); EXPECT_CALL(*connection_, OnStreamReset(promised_stream_id_, QUIC_STREAM_PEER_GOING_AWAY)); - if (session_->break_close_loop()) { - session_->ResetStream(promised_stream_id_, QUIC_STREAM_PEER_GOING_AWAY, 0); - } else { - session_->SendRstStream(promised_stream_id_, QUIC_STREAM_PEER_GOING_AWAY, - 0); - } + session_->ResetStream(promised_stream_id_, QUIC_STREAM_PEER_GOING_AWAY, 0); QuicClientPromisedInfo* promised = session_->GetPromisedById(promised_stream_id_); EXPECT_NE(promised, nullptr); @@ -983,44 +987,50 @@ TEST_P(QuicSpdyClientSessionTest, OnSettingsFrame) { } TEST_P(QuicSpdyClientSessionTest, IetfZeroRttSetup) { - // This feature is HTTP/3 only - if (!VersionUsesHttp3(session_->transport_version())) { + // This feature is TLS-only. + if (session_->version().UsesQuicCrypto()) { return; } - CompleteCryptoHandshake(); - EXPECT_FALSE(session_->GetCryptoStream()->IsResumption()); - SettingsFrame settings; - settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 2; - settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = 5; - settings.values[256] = 4; // unknown setting - session_->OnSettingsFrame(settings); + + CompleteFirstConnection(); CreateConnection(); // Session configs should be in initial state. - EXPECT_EQ(0u, session_->flow_controller()->send_window_offset()); - EXPECT_EQ(std::numeric_limits<size_t>::max(), - session_->max_outbound_header_list_size()); + if (session_->version().UsesHttp3()) { + EXPECT_EQ(0u, session_->flow_controller()->send_window_offset()); + EXPECT_EQ(std::numeric_limits<size_t>::max(), + session_->max_outbound_header_list_size()); + } else { + EXPECT_EQ(kMinimumFlowControlSendWindow, + session_->flow_controller()->send_window_offset()); + } session_->CryptoConnect(); + EXPECT_TRUE(session_->IsEncryptionEstablished()); + EXPECT_EQ(ENCRYPTION_ZERO_RTT, session_->connection()->encryption_level()); // The client session should have a basic setup ready before the handshake // succeeds. EXPECT_EQ(kInitialSessionFlowControlWindowForTest, session_->flow_controller()->send_window_offset()); - auto* id_manager = QuicSessionPeer::v99_streamid_manager(session_.get()); - EXPECT_EQ(kDefaultMaxStreamsPerConnection, - id_manager->max_outgoing_bidirectional_streams()); - EXPECT_EQ( - kDefaultMaxStreamsPerConnection + kHttp3StaticUnidirectionalStreamCount, - id_manager->max_outgoing_unidirectional_streams()); - auto* control_stream = - QuicSpdySessionPeer::GetSendControlStream(session_.get()); - EXPECT_EQ(kInitialStreamFlowControlWindowForTest, - control_stream->flow_controller()->send_window_offset()); - EXPECT_EQ(5u, session_->max_outbound_header_list_size()); + if (session_->version().UsesHttp3()) { + auto* id_manager = QuicSessionPeer::v99_streamid_manager(session_.get()); + EXPECT_EQ(kDefaultMaxStreamsPerConnection, + id_manager->max_outgoing_bidirectional_streams()); + EXPECT_EQ( + kDefaultMaxStreamsPerConnection + kHttp3StaticUnidirectionalStreamCount, + id_manager->max_outgoing_unidirectional_streams()); + auto* control_stream = + QuicSpdySessionPeer::GetSendControlStream(session_.get()); + EXPECT_EQ(kInitialStreamFlowControlWindowForTest, + control_stream->flow_controller()->send_window_offset()); + EXPECT_EQ(5u, session_->max_outbound_header_list_size()); + } else { + auto* id_manager = QuicSessionPeer::GetStreamIdManager(session_.get()); + EXPECT_EQ(kDefaultMaxStreamsPerConnection, + id_manager->max_open_outgoing_streams()); + } // Complete the handshake with a different config. - QuicCryptoClientStream* stream = - static_cast<QuicCryptoClientStream*>(session_->GetMutableCryptoStream()); QuicConfig config = DefaultQuicConfig(); config.SetInitialMaxStreamDataBytesUnidirectionalToSend( kInitialStreamFlowControlWindowForTest + 1); @@ -1028,23 +1038,415 @@ TEST_P(QuicSpdyClientSessionTest, IetfZeroRttSetup) { kInitialSessionFlowControlWindowForTest + 1); config.SetMaxBidirectionalStreamsToSend(kDefaultMaxStreamsPerConnection + 1); config.SetMaxUnidirectionalStreamsToSend(kDefaultMaxStreamsPerConnection + 1); - SetQuicReloadableFlag(quic_enable_tls_resumption, true); - std::unique_ptr<QuicCryptoServerConfig> crypto_config = - crypto_test_utils::CryptoServerConfigForTesting(); crypto_test_utils::HandshakeWithFakeServer( - &config, crypto_config.get(), &helper_, &alarm_factory_, connection_, - stream, AlpnForVersion(connection_->version())); + &config, server_crypto_config_.get(), &helper_, &alarm_factory_, + connection_, crypto_stream_, AlpnForVersion(connection_->version())); EXPECT_TRUE(session_->GetCryptoStream()->IsResumption()); EXPECT_EQ(kInitialSessionFlowControlWindowForTest + 1, session_->flow_controller()->send_window_offset()); - EXPECT_EQ(kDefaultMaxStreamsPerConnection + 1, - id_manager->max_outgoing_bidirectional_streams()); - EXPECT_EQ(kDefaultMaxStreamsPerConnection + - kHttp3StaticUnidirectionalStreamCount + 1, - id_manager->max_outgoing_unidirectional_streams()); - EXPECT_EQ(kInitialStreamFlowControlWindowForTest + 1, - control_stream->flow_controller()->send_window_offset()); + if (session_->version().UsesHttp3()) { + auto* id_manager = QuicSessionPeer::v99_streamid_manager(session_.get()); + auto* control_stream = + QuicSpdySessionPeer::GetSendControlStream(session_.get()); + EXPECT_EQ(kDefaultMaxStreamsPerConnection + 1, + id_manager->max_outgoing_bidirectional_streams()); + EXPECT_EQ(kDefaultMaxStreamsPerConnection + + kHttp3StaticUnidirectionalStreamCount + 1, + id_manager->max_outgoing_unidirectional_streams()); + EXPECT_EQ(kInitialStreamFlowControlWindowForTest + 1, + control_stream->flow_controller()->send_window_offset()); + } else { + auto* id_manager = QuicSessionPeer::GetStreamIdManager(session_.get()); + EXPECT_EQ(kDefaultMaxStreamsPerConnection + 1, + id_manager->max_open_outgoing_streams()); + } + + EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); + // Let the session receive a new SETTINGS frame to complete the second + // connection. + if (session_->version().UsesHttp3()) { + SettingsFrame settings; + settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 2; + settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = 5; + settings.values[256] = 4; // unknown setting + session_->OnSettingsFrame(settings); + } +} + +// Regression test for b/159168475 +TEST_P(QuicSpdyClientSessionTest, RetransmitDataOnZeroRttReject) { + SetQuicReloadableFlag(quic_do_not_retransmit_immediately_on_zero_rtt_reject, + true); + // This feature is TLS-only. + if (session_->version().UsesQuicCrypto()) { + return; + } + + CompleteFirstConnection(); + + // Create a second connection, but disable 0-RTT on the server. + CreateConnection(); + ON_CALL(*connection_, OnCanWrite()) + .WillByDefault( + testing::Invoke(connection_, &MockQuicConnection::ReallyOnCanWrite)); + EXPECT_CALL(*connection_, OnCanWrite()).Times(0); + + QuicConfig config = DefaultQuicConfig(); + config.SetMaxUnidirectionalStreamsToSend(kDefaultMaxStreamsPerConnection); + config.SetMaxBidirectionalStreamsToSend(kDefaultMaxStreamsPerConnection); + SSL_CTX_set_early_data_enabled(server_crypto_config_->ssl_ctx(), false); + + // Packets will be written: CHLO, HTTP/3 SETTINGS (H/3 only), and request + // data. + EXPECT_CALL(*connection_, + OnPacketSent(ENCRYPTION_INITIAL, NOT_RETRANSMISSION)); + EXPECT_CALL(*connection_, + OnPacketSent(ENCRYPTION_ZERO_RTT, NOT_RETRANSMISSION)) + .Times(session_->version().UsesHttp3() ? 2 : 1); + session_->CryptoConnect(); + EXPECT_TRUE(session_->IsEncryptionEstablished()); + EXPECT_EQ(ENCRYPTION_ZERO_RTT, session_->connection()->encryption_level()); + QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream(); + ASSERT_TRUE(stream); + stream->WriteOrBufferData("hello", true, nullptr); + + // When handshake is done, the client sends 2 packet: HANDSHAKE FINISHED, and + // coalesced retransmission of HTTP/3 SETTINGS and request data. + EXPECT_CALL(*connection_, + OnPacketSent(ENCRYPTION_HANDSHAKE, NOT_RETRANSMISSION)); + // TODO(b/158027651): change transmission type to ALL_ZERO_RTT_RETRANSMISSION. + EXPECT_CALL(*connection_, + OnPacketSent(ENCRYPTION_FORWARD_SECURE, LOSS_RETRANSMISSION)); + crypto_test_utils::HandshakeWithFakeServer( + &config, server_crypto_config_.get(), &helper_, &alarm_factory_, + connection_, crypto_stream_, AlpnForVersion(connection_->version())); + EXPECT_TRUE(session_->GetCryptoStream()->IsResumption()); +} + +// When IETF QUIC 0-RTT is rejected, a server-sent fresh transport params is +// available. If the new transport params reduces stream/flow control limit to +// lower than what the client has already used, connection will be closed. +TEST_P(QuicSpdyClientSessionTest, ZeroRttRejectReducesStreamLimitTooMuch) { + // This feature is TLS-only. + if (session_->version().UsesQuicCrypto()) { + return; + } + + CompleteFirstConnection(); + + // Create a second connection, but disable 0-RTT on the server. + CreateConnection(); + QuicConfig config = DefaultQuicConfig(); + // Server doesn't allow any bidirectional streams. + config.SetMaxBidirectionalStreamsToSend(0); + SSL_CTX_set_early_data_enabled(server_crypto_config_->ssl_ctx(), false); + session_->CryptoConnect(); + EXPECT_TRUE(session_->IsEncryptionEstablished()); + QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream(); + ASSERT_TRUE(stream); + + if (session_->version().UsesHttp3()) { + EXPECT_CALL( + *connection_, + CloseConnection( + QUIC_ZERO_RTT_UNRETRANSMITTABLE, + "Server rejected 0-RTT, aborting because new bidirectional initial " + "stream limit 0 is less than current open streams: 1", + _)) + .WillOnce(testing::Invoke(connection_, + &MockQuicConnection::ReallyCloseConnection)); + } else { + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_INTERNAL_ERROR, + "Server rejected 0-RTT, aborting because new stream " + "limit 0 is less than current open streams: 1", + _)) + .WillOnce(testing::Invoke(connection_, + &MockQuicConnection::ReallyCloseConnection)); + } + EXPECT_CALL(*connection_, CloseConnection(QUIC_HANDSHAKE_FAILED, _, _)); + + crypto_test_utils::HandshakeWithFakeServer( + &config, server_crypto_config_.get(), &helper_, &alarm_factory_, + connection_, crypto_stream_, AlpnForVersion(connection_->version())); +} + +TEST_P(QuicSpdyClientSessionTest, + ZeroRttRejectReducesStreamFlowControlTooMuch) { + // This feature is TLS-only. + if (session_->version().UsesQuicCrypto()) { + return; + } + + CompleteFirstConnection(); + + // Create a second connection, but disable 0-RTT on the server. + CreateConnection(); + QuicConfig config = DefaultQuicConfig(); + // Server doesn't allow any outgoing streams. + config.SetInitialMaxStreamDataBytesIncomingBidirectionalToSend(2); + config.SetInitialMaxStreamDataBytesUnidirectionalToSend(1); + SSL_CTX_set_early_data_enabled(server_crypto_config_->ssl_ctx(), false); + session_->CryptoConnect(); + EXPECT_TRUE(session_->IsEncryptionEstablished()); + QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream(); + ASSERT_TRUE(stream); + // Let the stream write more than 1 byte of data. + stream->WriteOrBufferData("hello", true, nullptr); + + if (session_->version().UsesHttp3()) { + // Both control stream and the request stream will report errors. + EXPECT_CALL(*connection_, + CloseConnection(QUIC_ZERO_RTT_UNRETRANSMITTABLE, _, _)) + .Times(2) + .WillOnce(testing::Invoke(connection_, + &MockQuicConnection::ReallyCloseConnection)); + } else { + EXPECT_CALL(*connection_, + CloseConnection( + QUIC_ZERO_RTT_UNRETRANSMITTABLE, + "Server rejected 0-RTT, aborting because new stream max " + "data 2 for stream 3 is less than currently used: 5", + _)) + .Times(1) + .WillOnce(testing::Invoke(connection_, + &MockQuicConnection::ReallyCloseConnection)); + } + EXPECT_CALL(*connection_, CloseConnection(QUIC_HANDSHAKE_FAILED, _, _)); + + crypto_test_utils::HandshakeWithFakeServer( + &config, server_crypto_config_.get(), &helper_, &alarm_factory_, + connection_, crypto_stream_, AlpnForVersion(connection_->version())); +} + +TEST_P(QuicSpdyClientSessionTest, + ZeroRttRejectReducesSessionFlowControlTooMuch) { + // This feature is TLS-only. + if (session_->version().UsesQuicCrypto()) { + return; + } + + CompleteFirstConnection(); + + // Create a second connection, but disable 0-RTT on the server. + CreateConnection(); + QuicConfig config = DefaultQuicConfig(); + // Server doesn't allow minimum data in session. + config.SetInitialSessionFlowControlWindowToSend( + kMinimumFlowControlSendWindow); + SSL_CTX_set_early_data_enabled(server_crypto_config_->ssl_ctx(), false); + session_->CryptoConnect(); + EXPECT_TRUE(session_->IsEncryptionEstablished()); + QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream(); + ASSERT_TRUE(stream); + std::string data_to_send(kMinimumFlowControlSendWindow + 1, 'x'); + // Let the stream write some data. + stream->WriteOrBufferData(data_to_send, true, nullptr); + + EXPECT_CALL(*connection_, + CloseConnection(QUIC_ZERO_RTT_UNRETRANSMITTABLE, _, _)) + .WillOnce(testing::Invoke(connection_, + &MockQuicConnection::ReallyCloseConnection)); + EXPECT_CALL(*connection_, CloseConnection(QUIC_HANDSHAKE_FAILED, _, _)); + + crypto_test_utils::HandshakeWithFakeServer( + &config, server_crypto_config_.get(), &helper_, &alarm_factory_, + connection_, crypto_stream_, AlpnForVersion(connection_->version())); +} + +TEST_P(QuicSpdyClientSessionTest, SetMaxPushIdBeforeEncryptionEstablished) { + // 0-RTT is TLS-only, MAX_PUSH_ID frame is HTTP/3-only. + if (!session_->version().UsesTls() || !session_->version().UsesHttp3()) { + return; + } + + CompleteFirstConnection(); + + CreateConnection(); + StrictMock<MockHttp3DebugVisitor> debug_visitor; + session_->set_debug_visitor(&debug_visitor); + + // No MAX_PUSH_ID frame is sent before encryption is established. + session_->SetMaxPushId(5); + + EXPECT_FALSE(session_->IsEncryptionEstablished()); + EXPECT_FALSE(session_->OneRttKeysAvailable()); + EXPECT_EQ(ENCRYPTION_INITIAL, session_->connection()->encryption_level()); + + // MAX_PUSH_ID frame is sent upon encryption establishment with the value set + // by the earlier SetMaxPushId() call. + EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_)); + EXPECT_CALL(debug_visitor, OnMaxPushIdFrameSent(_)) + .WillOnce(Invoke( + [](const MaxPushIdFrame& frame) { EXPECT_EQ(5u, frame.push_id); })); + session_->CryptoConnect(); + testing::Mock::VerifyAndClearExpectations(&debug_visitor); + + EXPECT_TRUE(session_->IsEncryptionEstablished()); + EXPECT_FALSE(session_->OneRttKeysAvailable()); + EXPECT_EQ(ENCRYPTION_ZERO_RTT, session_->connection()->encryption_level()); + + // Another SetMaxPushId() call with the same value does not trigger sending + // another MAX_PUSH_ID frame. + session_->SetMaxPushId(5); + + // Calling SetMaxPushId() with a different value results in sending another + // MAX_PUSH_ID frame. + EXPECT_CALL(debug_visitor, OnMaxPushIdFrameSent(_)) + .WillOnce(Invoke( + [](const MaxPushIdFrame& frame) { EXPECT_EQ(10u, frame.push_id); })); + session_->SetMaxPushId(10); + testing::Mock::VerifyAndClearExpectations(&debug_visitor); + + QuicConfig config = DefaultQuicConfig(); + crypto_test_utils::HandshakeWithFakeServer( + &config, server_crypto_config_.get(), &helper_, &alarm_factory_, + connection_, crypto_stream_, AlpnForVersion(connection_->version())); + + EXPECT_TRUE(session_->IsEncryptionEstablished()); + EXPECT_TRUE(session_->OneRttKeysAvailable()); + EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, + session_->connection()->encryption_level()); + EXPECT_TRUE(session_->GetCryptoStream()->IsResumption()); +} + +TEST_P(QuicSpdyClientSessionTest, SetMaxPushIdAfterEncryptionEstablished) { + // 0-RTT is TLS-only, MAX_PUSH_ID frame is HTTP/3-only. + if (!session_->version().UsesTls() || !session_->version().UsesHttp3()) { + return; + } + + CompleteFirstConnection(); + + CreateConnection(); + StrictMock<MockHttp3DebugVisitor> debug_visitor; + session_->set_debug_visitor(&debug_visitor); + + EXPECT_FALSE(session_->IsEncryptionEstablished()); + EXPECT_FALSE(session_->OneRttKeysAvailable()); + EXPECT_EQ(ENCRYPTION_INITIAL, session_->connection()->encryption_level()); + + // No MAX_PUSH_ID frame is sent if SetMaxPushId() has not been called. + EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_)); + session_->CryptoConnect(); + testing::Mock::VerifyAndClearExpectations(&debug_visitor); + + EXPECT_TRUE(session_->IsEncryptionEstablished()); + EXPECT_FALSE(session_->OneRttKeysAvailable()); + EXPECT_EQ(ENCRYPTION_ZERO_RTT, session_->connection()->encryption_level()); + + // Calling SetMaxPushId() for the first time after encryption is established + // results in sending a MAX_PUSH_ID frame. + EXPECT_CALL(debug_visitor, OnMaxPushIdFrameSent(_)) + .WillOnce(Invoke( + [](const MaxPushIdFrame& frame) { EXPECT_EQ(5u, frame.push_id); })); + session_->SetMaxPushId(5); + testing::Mock::VerifyAndClearExpectations(&debug_visitor); + + // Another SetMaxPushId() call with the same value does not trigger sending + // another MAX_PUSH_ID frame. + session_->SetMaxPushId(5); + + // Calling SetMaxPushId() with a different value results in sending another + // MAX_PUSH_ID frame. + EXPECT_CALL(debug_visitor, OnMaxPushIdFrameSent(_)) + .WillOnce(Invoke( + [](const MaxPushIdFrame& frame) { EXPECT_EQ(10u, frame.push_id); })); + session_->SetMaxPushId(10); + testing::Mock::VerifyAndClearExpectations(&debug_visitor); + + QuicConfig config = DefaultQuicConfig(); + crypto_test_utils::HandshakeWithFakeServer( + &config, server_crypto_config_.get(), &helper_, &alarm_factory_, + connection_, crypto_stream_, AlpnForVersion(connection_->version())); + + EXPECT_TRUE(session_->IsEncryptionEstablished()); + EXPECT_TRUE(session_->OneRttKeysAvailable()); + EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, + session_->connection()->encryption_level()); + EXPECT_TRUE(session_->GetCryptoStream()->IsResumption()); +} + +TEST_P(QuicSpdyClientSessionTest, BadSettingsInZeroRttResumption) { + if (!session_->version().UsesHttp3()) { + return; + } + + CompleteFirstConnection(); + + CreateConnection(); + CompleteCryptoHandshake(); + EXPECT_TRUE(session_->GetCryptoStream()->EarlyDataAccepted()); + + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH, _, _)) + .WillOnce(testing::Invoke(connection_, + &MockQuicConnection::ReallyCloseConnection)); + // Let the session receive a different SETTINGS frame. + SettingsFrame settings; + settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 1; + settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = 5; + settings.values[256] = 4; // unknown setting + session_->OnSettingsFrame(settings); +} + +TEST_P(QuicSpdyClientSessionTest, BadSettingsInZeroRttRejection) { + if (!session_->version().UsesHttp3()) { + return; + } + + CompleteFirstConnection(); + + CreateConnection(); + SSL_CTX_set_early_data_enabled(server_crypto_config_->ssl_ctx(), false); + session_->CryptoConnect(); + EXPECT_TRUE(session_->IsEncryptionEstablished()); + QuicConfig config = DefaultQuicConfig(); + crypto_test_utils::HandshakeWithFakeServer( + &config, server_crypto_config_.get(), &helper_, &alarm_factory_, + connection_, crypto_stream_, AlpnForVersion(connection_->version())); + EXPECT_FALSE(session_->GetCryptoStream()->EarlyDataAccepted()); + + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH, _, _)) + .WillOnce(testing::Invoke(connection_, + &MockQuicConnection::ReallyCloseConnection)); + // Let the session receive a different SETTINGS frame. + SettingsFrame settings; + settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 2; + // setting on SETTINGS_MAX_HEADER_LIST_SIZE is reduced. + settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = 4; + settings.values[256] = 4; // unknown setting + session_->OnSettingsFrame(settings); +} + +TEST_P(QuicSpdyClientSessionTest, ServerAcceptsZeroRttButOmitSetting) { + if (!session_->version().UsesHttp3()) { + return; + } + + CompleteFirstConnection(); + + CreateConnection(); + CompleteCryptoHandshake(); + EXPECT_TRUE(session_->GetMutableCryptoStream()->EarlyDataAccepted()); + + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH, _, _)) + .WillOnce(testing::Invoke(connection_, + &MockQuicConnection::ReallyCloseConnection)); + // Let the session receive a different SETTINGS frame. + SettingsFrame settings; + settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 1; + // Intentionally omit SETTINGS_MAX_HEADER_LIST_SIZE which was previously sent + // with a non-zero value. + settings.values[256] = 4; // unknown setting + session_->OnSettingsFrame(settings); } } // namespace diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.cc index 7232f76c755..f50e756d1b3 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.cc @@ -53,24 +53,41 @@ void QuicSpdyClientStream::OnInitialHeadersComplete( if (!SpdyUtils::CopyAndValidateHeaders(header_list, &content_length_, &response_headers_)) { QUIC_DLOG(ERROR) << "Failed to parse header list: " - << header_list.DebugString(); + << header_list.DebugString() << " on stream " << id(); Reset(QUIC_BAD_APPLICATION_PAYLOAD); return; } if (!ParseHeaderStatusCode(response_headers_, &response_code_)) { QUIC_DLOG(ERROR) << "Received invalid response code: " - << response_headers_[":status"].as_string(); + << response_headers_[":status"].as_string() + << " on stream " << id(); + Reset(QUIC_BAD_APPLICATION_PAYLOAD); + return; + } + + if (response_code_ == 101) { + // 101 "Switching Protocols" is forbidden in HTTP/3 as per the + // "HTTP Upgrade" section of draft-ietf-quic-http. + QUIC_DLOG(ERROR) << "Received forbidden 101 response code" + << " on stream " << id(); Reset(QUIC_BAD_APPLICATION_PAYLOAD); return; } - if (response_code_ == 100 && !has_preliminary_headers_) { - // These are preliminary 100 Continue headers, not the actual response - // headers. + if (response_code_ >= 100 && response_code_ < 200) { + // These are Informational 1xx headers, not the actual response headers. + QUIC_DLOG(INFO) << "Received informational response code: " + << response_headers_[":status"].as_string() << " on stream " + << id(); set_headers_decompressed(false); - has_preliminary_headers_ = true; - preliminary_headers_ = std::move(response_headers_); + if (response_code_ == 100 && !has_preliminary_headers_) { + // This is 100 Continue, save it to enable "Expect: 100-continue". + has_preliminary_headers_ = true; + preliminary_headers_ = std::move(response_headers_); + } else { + response_headers_.clear(); + } } ConsumeHeaderList(); diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream_test.cc index fdc0e298371..1aba0005981 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream_test.cc @@ -133,17 +133,77 @@ TEST_P(QuicSpdyClientStreamTest, TestFraming) { EXPECT_EQ(body_, stream_->data()); } -TEST_P(QuicSpdyClientStreamTest, TestFraming100Continue) { +TEST_P(QuicSpdyClientStreamTest, Test100ContinueBeforeSuccessful) { + // First send 100 Continue. headers_[":status"] = "100"; auto headers = AsHeaderList(headers_); stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), headers); - stream_->OnStreamFrame( - QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, body_)); EXPECT_EQ("100", stream_->preliminary_headers().find(":status")->second); EXPECT_EQ(0u, stream_->response_headers().size()); EXPECT_EQ(100, stream_->response_code()); EXPECT_EQ("", stream_->data()); + // Then send 200 OK. + headers_[":status"] = "200"; + headers = AsHeaderList(headers_); + stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + HttpEncoder::SerializeDataFrameHeader(body_.length(), &buffer); + std::string header = std::string(buffer.get(), header_length); + std::string data = + connection_->version().UsesHttp3() ? header + body_ : body_; + stream_->OnStreamFrame( + QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data)); + // Make sure the 200 response got parsed correctly. + EXPECT_EQ("200", stream_->response_headers().find(":status")->second); + EXPECT_EQ(200, stream_->response_code()); + EXPECT_EQ(body_, stream_->data()); + // Make sure the 100 response is still available. + EXPECT_EQ("100", stream_->preliminary_headers().find(":status")->second); +} + +TEST_P(QuicSpdyClientStreamTest, TestUnknownInformationalBeforeSuccessful) { + // First send 199, an unknown Informational (1XX). + headers_[":status"] = "199"; + auto headers = AsHeaderList(headers_); + stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + EXPECT_EQ(0u, stream_->response_headers().size()); + EXPECT_EQ(199, stream_->response_code()); + EXPECT_EQ("", stream_->data()); + // Then send 200 OK. + headers_[":status"] = "200"; + headers = AsHeaderList(headers_); + stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + HttpEncoder::SerializeDataFrameHeader(body_.length(), &buffer); + std::string header = std::string(buffer.get(), header_length); + std::string data = + connection_->version().UsesHttp3() ? header + body_ : body_; + stream_->OnStreamFrame( + QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data)); + // Make sure the 200 response got parsed correctly. + EXPECT_EQ("200", stream_->response_headers().find(":status")->second); + EXPECT_EQ(200, stream_->response_code()); + EXPECT_EQ(body_, stream_->data()); +} + +TEST_P(QuicSpdyClientStreamTest, TestReceiving101) { + // 101 "Switching Protocols" is forbidden in HTTP/3 as per the + // "HTTP Upgrade" section of draft-ietf-quic-http. + headers_[":status"] = "101"; + EXPECT_CALL(*connection_, SendControlFrame(_)); + EXPECT_CALL(*connection_, + OnStreamReset(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD)); + auto headers = AsHeaderList(headers_); + stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(), + headers); + EXPECT_THAT(stream_->stream_error(), + IsStreamError(QUIC_BAD_APPLICATION_PAYLOAD)); } TEST_P(QuicSpdyClientStreamTest, TestFramingOnePacket) { 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 27643c87294..d6b5a0e7629 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 @@ -434,6 +434,7 @@ QuicSpdySession::~QuicSpdySession() { void QuicSpdySession::Initialize() { QuicSession::Initialize(); + FillSettingsFrame(); if (!VersionUsesHttp3(transport_version())) { if (perspective() == Perspective::IS_SERVER) { set_largest_peer_created_stream_id( @@ -464,6 +465,15 @@ void QuicSpdySession::Initialize() { 2 * max_inbound_header_list_size_); } +void QuicSpdySession::FillSettingsFrame() { + settings_.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = + qpack_maximum_dynamic_table_capacity_; + settings_.values[SETTINGS_QPACK_BLOCKED_STREAMS] = + qpack_maximum_blocked_streams_; + settings_.values[SETTINGS_MAX_HEADER_LIST_SIZE] = + max_inbound_header_list_size_; +} + void QuicSpdySession::OnDecoderStreamError( quiche::QuicheStringPiece error_message) { DCHECK(VersionUsesHttp3(transport_version())); @@ -548,7 +558,7 @@ void QuicSpdySession::OnPriorityFrame( bool QuicSpdySession::OnPriorityUpdateForRequestStream(QuicStreamId stream_id, int urgency) { if (perspective() == Perspective::IS_CLIENT || - !QuicUtils::IsBidirectionalStreamId(stream_id) || + !QuicUtils::IsBidirectionalStreamId(stream_id, version()) || !QuicUtils::IsClientInitiatedStreamId(transport_version(), stream_id)) { return true; } @@ -647,7 +657,7 @@ void QuicSpdySession::WriteHttp3PriorityUpdate( void QuicSpdySession::OnHttp3GoAway(QuicStreamId stream_id) { DCHECK_EQ(perspective(), Perspective::IS_CLIENT); - if (!QuicUtils::IsBidirectionalStreamId(stream_id) || + if (!QuicUtils::IsBidirectionalStreamId(stream_id, version()) || IsIncomingStream(stream_id)) { CloseConnectionWithDetails( QUIC_INVALID_STREAM_ID, @@ -736,9 +746,9 @@ void QuicSpdySession::SendInitialData() { } QuicConnection::ScopedPacketFlusher flusher(connection()); send_control_stream_->MaybeSendSettingsFrame(); - if (perspective() == Perspective::IS_CLIENT && !http3_max_push_id_sent_) { + if (perspective() == Perspective::IS_CLIENT && max_push_id_.has_value() && + !http3_max_push_id_sent_) { SendMaxPushId(); - http3_max_push_id_sent_ = true; } } @@ -856,7 +866,7 @@ void QuicSpdySession::OnPromiseHeaderList( ConnectionCloseBehavior::SILENT_CLOSE); } -bool QuicSpdySession::SetApplicationState(ApplicationState* cached_state) { +bool QuicSpdySession::ResumeApplicationState(ApplicationState* cached_state) { DCHECK_EQ(perspective(), Perspective::IS_CLIENT); DCHECK(VersionUsesHttp3(transport_version())); @@ -874,52 +884,102 @@ bool QuicSpdySession::SetApplicationState(ApplicationState* cached_state) { return true; } -void QuicSpdySession::OnSettingsFrame(const SettingsFrame& frame) { +bool QuicSpdySession::OnSettingsFrame(const SettingsFrame& frame) { DCHECK(VersionUsesHttp3(transport_version())); if (debug_visitor_ != nullptr) { debug_visitor_->OnSettingsFrameReceived(frame); } for (const auto& setting : frame.values) { - OnSetting(setting.first, setting.second); + if (!OnSetting(setting.first, setting.second)) { + return false; + } } + return true; } -void QuicSpdySession::OnSetting(uint64_t id, uint64_t value) { +bool QuicSpdySession::OnSetting(uint64_t id, uint64_t value) { if (VersionUsesHttp3(transport_version())) { // SETTINGS frame received on the control stream. switch (id) { - case SETTINGS_QPACK_MAX_TABLE_CAPACITY: + case SETTINGS_QPACK_MAX_TABLE_CAPACITY: { QUIC_DVLOG(1) << ENDPOINT << "SETTINGS_QPACK_MAX_TABLE_CAPACITY received with value " << value; // Communicate |value| to encoder, because it is used for encoding // Required Insert Count. - qpack_encoder_->SetMaximumDynamicTableCapacity(value); + bool success = qpack_encoder_->SetMaximumDynamicTableCapacity(value); + if (GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls) && !success) { + CloseConnectionWithDetails( + was_zero_rtt_rejected() + ? QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH + : QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH, + quiche::QuicheStrCat( + was_zero_rtt_rejected() + ? "Server rejected 0-RTT, aborting because " + : "", + "Server sent an SETTINGS_QPACK_MAX_TABLE_CAPACITY: ", value, + "while current value is: ", + qpack_encoder_->MaximumDynamicTableCapacity())); + return false; + } // However, limit the dynamic table capacity to // |qpack_maximum_dynamic_table_capacity_|. qpack_encoder_->SetDynamicTableCapacity( std::min(value, qpack_maximum_dynamic_table_capacity_)); break; + } case SETTINGS_MAX_HEADER_LIST_SIZE: QUIC_DVLOG(1) << ENDPOINT << "SETTINGS_MAX_HEADER_LIST_SIZE received with value " << value; + if (GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls) && + max_outbound_header_list_size_ != + std::numeric_limits<size_t>::max() && + max_outbound_header_list_size_ > value) { + CloseConnectionWithDetails( + was_zero_rtt_rejected() + ? QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH + : QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH, + quiche::QuicheStrCat( + was_zero_rtt_rejected() + ? "Server rejected 0-RTT, aborting because " + : "", + "Server sent an SETTINGS_MAX_HEADER_LIST_SIZE: ", value, + "which reduces current value: ", + max_outbound_header_list_size_)); + return false; + } max_outbound_header_list_size_ = value; break; - case SETTINGS_QPACK_BLOCKED_STREAMS: + case SETTINGS_QPACK_BLOCKED_STREAMS: { QUIC_DVLOG(1) << ENDPOINT << "SETTINGS_QPACK_BLOCKED_STREAMS received with value " << value; - qpack_encoder_->SetMaximumBlockedStreams(value); + bool success = qpack_encoder_->SetMaximumBlockedStreams(value); + if (GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls) && !success) { + CloseConnectionWithDetails( + was_zero_rtt_rejected() + ? QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH + : QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH, + quiche::QuicheStrCat( + was_zero_rtt_rejected() + ? "Server rejected 0-RTT, aborting because " + : "", + "Server sent an SETTINGS_QPACK_BLOCKED_STREAMS: ", value, + "which reduces current value: ", + qpack_encoder_->maximum_blocked_streams())); + return false; + } break; + } default: QUIC_DVLOG(1) << ENDPOINT << "Unknown setting identifier " << id << " received with value " << value; // Ignore unknown settings. break; } - return; + return true; } // SETTINGS frame received on the headers stream. @@ -942,7 +1002,7 @@ void QuicSpdySession::OnSetting(uint64_t id, uint64_t value) { quiche::QuicheStrCat("Invalid value for SETTINGS_ENABLE_PUSH: ", value)); } - return; + return true; } QUIC_DVLOG(1) << ENDPOINT << "SETTINGS_ENABLE_PUSH received with value " << value; @@ -977,6 +1037,7 @@ void QuicSpdySession::OnSetting(uint64_t id, uint64_t value) { id)); } } + return true; } bool QuicSpdySession::ShouldReleaseHeadersStreamSequencerBuffer() { @@ -1076,11 +1137,8 @@ void QuicSpdySession::CloseConnectionWithDetails(QuicErrorCode error, } bool QuicSpdySession::HasActiveRequestStreams() const { - DCHECK_GE(static_cast<size_t>(stream_map().size()), - num_incoming_static_streams() + num_outgoing_static_streams()); - return stream_map().size() - num_incoming_static_streams() - - num_outgoing_static_streams() > - 0; + DCHECK_GE(static_cast<size_t>(stream_map().size()), num_static_streams()); + return stream_map().size() - num_static_streams() > 0; } bool QuicSpdySession::ProcessPendingStream(PendingStream* pending) { @@ -1176,9 +1234,7 @@ void QuicSpdySession::MaybeInitializeHttp3UnidirectionalStreams() { DCHECK(VersionUsesHttp3(transport_version())); if (!send_control_stream_ && CanOpenNextOutgoingUnidirectionalStream()) { auto send_control = std::make_unique<QuicSendControlStream>( - GetNextOutgoingUnidirectionalStreamId(), this, - qpack_maximum_dynamic_table_capacity_, qpack_maximum_blocked_streams_, - max_inbound_header_list_size_); + GetNextOutgoingUnidirectionalStreamId(), this, settings_); send_control_stream_ = send_control.get(); ActivateStream(std::move(send_control)); if (debug_visitor_) { @@ -1231,15 +1287,19 @@ void QuicSpdySession::SetMaxPushId(PushId max_push_id) { ietf_server_push_enabled_ = true; if (max_push_id_.has_value()) { - QUIC_DVLOG(1) << "Setting max_push_id to: " << max_push_id + if (max_push_id == max_push_id_.value()) { + QUIC_DVLOG(1) << "Not changing max_push_id: " << max_push_id; + return; + } + + QUIC_DVLOG(1) << "Setting max_push_id to: " << max_push_id << " from: " << max_push_id_.value(); } else { - QUIC_DVLOG(1) << "Setting max_push_id to: " << max_push_id - << " from unset"; + QUIC_DVLOG(1) << "Setting max_push_id to: " << max_push_id << " from unset"; } max_push_id_ = max_push_id; - if (OneRttKeysAvailable()) { + if (IsEncryptionEstablished()) { SendMaxPushId(); } } @@ -1281,9 +1341,8 @@ void QuicSpdySession::SendMaxPushId() { DCHECK(VersionUsesHttp3(transport_version())); DCHECK_EQ(Perspective::IS_CLIENT, perspective()); - if (max_push_id_.has_value()) { - send_control_stream_->SendMaxPushIdFrame(max_push_id_.value()); - } + send_control_stream_->SendMaxPushIdFrame(max_push_id_.value()); + http3_max_push_id_sent_ = true; } void QuicSpdySession::EnableServerPush() { 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 703dffaf41e..4d6ee6370e3 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 @@ -247,10 +247,14 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession bool server_push_enabled() const; // Called when the control stream receives HTTP/3 SETTINGS. - virtual void OnSettingsFrame(const SettingsFrame& frame); + // Returns false in case of 0-RTT if received settings are incompatible with + // cached values, true otherwise. + virtual bool OnSettingsFrame(const SettingsFrame& frame); - // Called when a setting is parsed from an incoming SETTINGS frame. - void OnSetting(uint64_t id, uint64_t value); + // Called when a SETTINGS is parsed from an incoming SETTINGS frame. + // Returns false in case of 0-RTT if received SETTINGS is incompatible with + // cached value, true otherwise. + bool OnSetting(uint64_t id, uint64_t value); // Return true if this session wants to release headers stream's buffer // aggressively. @@ -378,7 +382,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession void OnStreamCreated(QuicSpdyStream* stream); // Decode SETTINGS from |cached_state| and apply it to the session. - bool SetApplicationState(ApplicationState* cached_state) override; + bool ResumeApplicationState(ApplicationState* cached_state) override; protected: // Override CreateIncomingStream(), CreateOutgoingBidirectionalStream() and @@ -443,11 +447,11 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession return receive_control_stream_; } + const SettingsFrame& settings() const { return settings_; } + // Initializes HTTP/3 unidirectional streams if not yet initialzed. virtual void MaybeInitializeHttp3UnidirectionalStreams(); - void SendMaxPushId(); - private: friend class test::QuicSpdySessionPeer; @@ -468,10 +472,18 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession void CloseConnectionOnDuplicateHttp3UnidirectionalStreams( quiche::QuicheStringPiece type); - // Sends any data which should be sent at the start of a connection, - // including the initial SETTINGS frame, etc. + // Sends any data which should be sent at the start of a connection, including + // the initial SETTINGS frame, and (when IETF QUIC is used) also a MAX_PUSH_ID + // frame if SetMaxPushId() had been called before encryption was established. + // When using 0-RTT, this method is called twice: once when encryption is + // established, and again when 1-RTT keys are available. void SendInitialData(); + // Send a MAX_PUSH_ID frame. Used in IETF QUIC only. + void SendMaxPushId(); + + void FillSettingsFrame(); + std::unique_ptr<QpackEncoder> qpack_encoder_; std::unique_ptr<QpackDecoder> qpack_decoder_; @@ -489,6 +501,8 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession QpackSendStream* qpack_encoder_send_stream_; QpackSendStream* qpack_decoder_send_stream_; + SettingsFrame settings_; + // Maximum dynamic table capacity as defined at // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#maximum-dynamic-table-capacity // for the decoding context. Value will be sent via @@ -533,12 +547,17 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // Server push is enabled for a client by calling SetMaxPushId(). bool ietf_server_push_enabled_; - // Used in IETF QUIC only. Unset until a MAX_PUSH_ID frame is received/sent. - // For a server, the push ID in the most recently received MAX_PUSH_ID frame. - // For a client before 1-RTT keys are available, the push ID to be sent in the - // initial MAX_PUSH_ID frame. - // For a client after 1-RTT keys are available, the push ID in the most - // recently sent MAX_PUSH_ID frame. + // Used in IETF QUIC only. + // For a server: + // the push ID in the most recently received MAX_PUSH_ID frame, + // or unset if no MAX_PUSH_ID frame has been received. + // For a client: + // unset until SetMaxPushId() is called; + // before encryption is established, the push ID to be sent in the initial + // MAX_PUSH_ID frame; + // after encryption is established, the push ID in the most recently sent + // MAX_PUSH_ID frame. + // Once set, never goes back to unset. quiche::QuicheOptional<PushId> max_push_id_; // An integer used for live check. The indicator is assigned a value in @@ -554,14 +573,14 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // If the endpoint has sent HTTP/3 GOAWAY frame. bool http3_goaway_sent_; - // If SendMaxPushId() has been called from SendInitialData(). Note that a - // MAX_PUSH_ID frame is only sent if SetMaxPushId() had been called - // beforehand. + // Only used by a client, only with IETF QUIC. True if a MAX_PUSH_ID frame + // has been sent, in which case |max_push_id_| has the value sent in the most + // recent MAX_PUSH_ID frame. Once true, never goes back to false. bool http3_max_push_id_sent_; // Priority values received in PRIORITY_UPDATE frames for streams that are not // open yet. - QuicUnorderedMap<QuicStreamId, int> buffered_stream_priorities_; + QuicHashMap<QuicStreamId, int> buffered_stream_priorities_; }; } // namespace quic 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 8b78d7f0883..b2bcaa3bf28 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 @@ -91,6 +91,17 @@ class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker { kInitialStreamFlowControlWindowForTest); session()->config()->SetInitialSessionFlowControlWindowToSend( kInitialSessionFlowControlWindowForTest); + if (session()->version().AuthenticatesHandshakeConnectionIds()) { + if (session()->perspective() == Perspective::IS_CLIENT) { + session()->config()->SetOriginalConnectionIdToSend( + session()->connection()->connection_id()); + session()->config()->SetInitialSourceConnectionIdToSend( + session()->connection()->connection_id()); + } else { + session()->config()->SetInitialSourceConnectionIdToSend( + session()->connection()->client_connection_id()); + } + } if (session()->connection()->version().handshake_protocol == PROTOCOL_TLS1_3) { TransportParameters transport_parameters; @@ -129,6 +140,8 @@ class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker { HandshakeState GetHandshakeState() const override { return one_rtt_keys_available() ? HANDSHAKE_COMPLETE : HANDSHAKE_START; } + void SetServerApplicationStateForResumption( + std::unique_ptr<ApplicationState> /*application_state*/) override {} const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const override { return *params_; @@ -197,6 +210,9 @@ class TestSession : public QuicSpdySession { this->connection()->SetEncrypter( ENCRYPTION_FORWARD_SECURE, std::make_unique<NullEncrypter>(connection->perspective())); + if (this->connection()->version().SupportsAntiAmplificationLimit()) { + QuicConnectionPeer::SetAddressValidated(this->connection()); + } } ~TestSession() override { DeleteConnection(); } @@ -226,7 +242,7 @@ class TestSession : public QuicSpdySession { TestStream* CreateIncomingStream(QuicStreamId id) override { // Enforce the limit on the number of open streams. if (!VersionHasIetfQuicFrames(connection()->transport_version()) && - GetNumOpenIncomingStreams() + 1 > + stream_id_manager().num_open_incoming_streams() + 1 > max_open_incoming_bidirectional_streams()) { connection()->CloseConnection( QUIC_TOO_MANY_OPEN_STREAMS, "Too many streams!", @@ -235,9 +251,8 @@ class TestSession : public QuicSpdySession { } else { TestStream* stream = new TestStream( id, this, - DetermineStreamType(id, connection()->transport_version(), - perspective(), /*is_incoming=*/true, - BIDIRECTIONAL)); + DetermineStreamType(id, connection()->version(), perspective(), + /*is_incoming=*/true, BIDIRECTIONAL)); ActivateStream(QuicWrapUnique(stream)); return stream; } @@ -245,11 +260,10 @@ class TestSession : public QuicSpdySession { TestStream* CreateIncomingStream(PendingStream* pending) override { QuicStreamId id = pending->id(); - TestStream* stream = - new TestStream(pending, this, - DetermineStreamType( - id, connection()->transport_version(), perspective(), - /*is_incoming=*/true, BIDIRECTIONAL)); + TestStream* stream = new TestStream( + pending, this, + DetermineStreamType(id, connection()->version(), perspective(), + /*is_incoming=*/true, BIDIRECTIONAL)); ActivateStream(QuicWrapUnique(stream)); return stream; } @@ -299,7 +313,7 @@ class TestSession : public QuicSpdySession { MakeIOVector("not empty", &iov); QuicStreamPeer::SendBuffer(stream).SaveStreamData(&iov, 1, 0, 9); QuicConsumedData consumed = - WritevData(stream->id(), 9, 0, FIN, NOT_RETRANSMISSION, QuicheNullOpt); + WritevData(stream->id(), 9, 0, FIN, NOT_RETRANSMISSION, QUICHE_NULLOPT); QuicStreamPeer::SendBuffer(stream).OnStreamDataConsumed( consumed.bytes_consumed); return consumed; @@ -308,7 +322,7 @@ class TestSession : public QuicSpdySession { QuicConsumedData SendLargeFakeData(QuicStream* stream, int bytes) { DCHECK(writev_consumes_all_data_); return WritevData(stream->id(), bytes, 0, FIN, NOT_RETRANSMISSION, - QuicheNullOpt); + QUICHE_NULLOPT); } using QuicSession::closed_streams; @@ -1599,6 +1613,7 @@ TEST_P(QuicSpdySessionTestServer, TooLowUnidirectionalStreamLimitHttp3) { return; } QuicConfigPeer::SetReceivedMaxUnidirectionalStreams(session_.config(), 2u); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); EXPECT_CALL( *connection_, @@ -1613,6 +1628,7 @@ TEST_P(QuicSpdySessionTestServer, CustomFlowControlWindow) { copt.push_back(kIFW7); QuicConfigPeer::SetReceivedConnectionOptions(session_.config(), copt); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_.OnConfigNegotiated(); EXPECT_EQ(192 * 1024u, QuicFlowControllerPeer::ReceiveWindowSize( session_.flow_controller())); @@ -2985,6 +3001,26 @@ TEST_P(QuicSpdySessionTestServer, PeerClosesCriticalReceiveStream) { } } +TEST_P(QuicSpdySessionTestServer, + H3ControlStreamsLimitedByConnectionFlowControl) { + if (!VersionUsesHttp3(transport_version())) { + return; + } + // Ensure connection level flow control blockage. + QuicFlowControllerPeer::SetSendWindowOffset(session_.flow_controller(), 0); + EXPECT_TRUE(session_.IsConnectionFlowControlBlocked()); + + QuicSendControlStream* send_control_stream = + QuicSpdySessionPeer::GetSendControlStream(&session_); + // Mark send_control stream write blocked. + session_.MarkConnectionLevelWriteBlocked(send_control_stream->id()); + if (GetQuicReloadableFlag(quic_fix_willing_and_able_to_write)) { + EXPECT_FALSE(session_.WillingAndAbleToWrite()); + } else { + EXPECT_TRUE(session_.WillingAndAbleToWrite()); + } +} + TEST_P(QuicSpdySessionTestServer, PeerClosesCriticalSendStream) { if (!VersionUsesHttp3(transport_version())) { return; 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 51f7c5c9525..4c979c1784f 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 @@ -529,6 +529,21 @@ void QuicSpdyStream::OnStreamHeadersPriority( void QuicSpdyStream::OnStreamHeaderList(bool fin, size_t frame_len, const QuicHeaderList& header_list) { + if (GetQuicReloadableFlag(quic_save_user_agent_in_quic_session)) { + if (!spdy_session()->user_agent_id().has_value()) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_save_user_agent_in_quic_session, 3, 3); + std::string uaid; + for (const auto& kv : header_list) { + if (quiche::QuicheTextUtils::ToLower(kv.first) == + kUserAgentHeaderName) { + uaid = kv.second; + break; + } + } + spdy_session()->SetUserAgentId(std::move(uaid)); + } + } + // TODO(b/134706391): remove |fin| argument. // When using Google QUIC, an empty header list indicates that the size limit // has been exceeded. 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 bafb0f282f0..e769a9d6027 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 @@ -78,6 +78,17 @@ class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker { kInitialStreamFlowControlWindowForTest); session()->config()->SetInitialSessionFlowControlWindowToSend( kInitialSessionFlowControlWindowForTest); + if (session()->version().AuthenticatesHandshakeConnectionIds()) { + if (session()->perspective() == Perspective::IS_CLIENT) { + session()->config()->SetOriginalConnectionIdToSend( + session()->connection()->connection_id()); + session()->config()->SetInitialSourceConnectionIdToSend( + session()->connection()->connection_id()); + } else { + session()->config()->SetInitialSourceConnectionIdToSend( + session()->connection()->client_connection_id()); + } + } if (session()->connection()->version().handshake_protocol == PROTOCOL_TLS1_3) { TransportParameters transport_parameters; @@ -116,6 +127,8 @@ class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker { HandshakeState GetHandshakeState() const override { return one_rtt_keys_available() ? HANDSHAKE_COMPLETE : HANDSHAKE_START; } + void SetServerApplicationStateForResumption( + std::unique_ptr<ApplicationState> /*application_state*/) override {} const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const override { return *params_; @@ -324,6 +337,9 @@ class QuicSpdyStreamTest : public QuicTestWithParam<ParsedQuicVersion> { &helper_, &alarm_factory_, perspective, SupportedVersions(GetParam())); session_ = std::make_unique<StrictMock<TestSession>>(connection_); session_->Initialize(); + if (connection_->version().SupportsAntiAmplificationLimit()) { + QuicConnectionPeer::SetAddressValidated(connection_); + } connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); ON_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillByDefault( diff --git a/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.cc b/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.cc index 6120f850bbc..af4de8b1c8c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.cc @@ -31,42 +31,20 @@ LegacyQuicStreamIdManager::LegacyQuicStreamIdManager( : QuicUtils::GetCryptoStreamId(transport_version_)) : QuicUtils::GetInvalidStreamId(transport_version_)), num_open_incoming_streams_(0), - num_open_outgoing_streams_(0), - handles_accounting_( - GetQuicReloadableFlag(quic_stream_id_manager_handles_accounting)) { - if (handles_accounting_) { - QUIC_RELOADABLE_FLAG_COUNT(quic_stream_id_manager_handles_accounting); - } -} + num_open_outgoing_streams_(0) {} LegacyQuicStreamIdManager::~LegacyQuicStreamIdManager() {} -bool LegacyQuicStreamIdManager::CanOpenNextOutgoingStream( - size_t current_num_open_outgoing_streams) const { - if (handles_accounting_) { - DCHECK_LE(num_open_outgoing_streams_, max_open_outgoing_streams_); - QUIC_DLOG_IF(INFO, num_open_outgoing_streams_ == max_open_outgoing_streams_) - << "Failed to create a new outgoing stream. " - << "Already " << num_open_outgoing_streams_ << " open."; - return num_open_outgoing_streams_ < max_open_outgoing_streams_; - } - if (current_num_open_outgoing_streams >= max_open_outgoing_streams_) { - QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. " - << "Already " << current_num_open_outgoing_streams - << " open."; - return false; - } - return true; +bool LegacyQuicStreamIdManager::CanOpenNextOutgoingStream() const { + DCHECK_LE(num_open_outgoing_streams_, max_open_outgoing_streams_); + QUIC_DLOG_IF(INFO, num_open_outgoing_streams_ == max_open_outgoing_streams_) + << "Failed to create a new outgoing stream. " + << "Already " << num_open_outgoing_streams_ << " open."; + return num_open_outgoing_streams_ < max_open_outgoing_streams_; } -bool LegacyQuicStreamIdManager::CanOpenIncomingStream( - size_t current_num_open_incoming_streams) const { - if (handles_accounting_) { - return num_open_incoming_streams_ < max_open_incoming_streams_; - } - // Check if the new number of open streams would cause the number of - // open streams to exceed the limit. - return current_num_open_incoming_streams < max_open_incoming_streams_; +bool LegacyQuicStreamIdManager::CanOpenIncomingStream() const { + return num_open_incoming_streams_ < max_open_incoming_streams_; } bool LegacyQuicStreamIdManager::MaybeIncreaseLargestPeerStreamId( @@ -121,7 +99,6 @@ QuicStreamId LegacyQuicStreamIdManager::GetNextOutgoingStreamId() { } void LegacyQuicStreamIdManager::ActivateStream(bool is_incoming) { - DCHECK(handles_accounting_); if (is_incoming) { ++num_open_incoming_streams_; return; @@ -130,7 +107,6 @@ void LegacyQuicStreamIdManager::ActivateStream(bool is_incoming) { } void LegacyQuicStreamIdManager::OnStreamClosed(bool is_incoming) { - DCHECK(handles_accounting_); if (is_incoming) { QUIC_BUG_IF(num_open_incoming_streams_ == 0); --num_open_incoming_streams_; diff --git a/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.h b/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.h index 6c1309ebbc9..01315872fb8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.h @@ -29,11 +29,10 @@ class QUIC_EXPORT_PRIVATE LegacyQuicStreamIdManager { ~LegacyQuicStreamIdManager(); // Returns true if the next outgoing stream ID can be allocated. - bool CanOpenNextOutgoingStream( - size_t current_num_open_outgoing_streams) const; + bool CanOpenNextOutgoingStream() const; // Returns true if a new incoming stream can be opened. - bool CanOpenIncomingStream(size_t current_num_open_incoming_streams) const; + bool CanOpenIncomingStream() const; // Returns false when increasing the largest created stream id to |id| would // violate the limit, so the connection should be closed. @@ -95,8 +94,6 @@ class QUIC_EXPORT_PRIVATE LegacyQuicStreamIdManager { return num_open_outgoing_streams_; } - bool handles_accounting() const { return handles_accounting_; } - private: friend class test::QuicSessionPeer; @@ -118,16 +115,11 @@ class QUIC_EXPORT_PRIVATE LegacyQuicStreamIdManager { QuicStreamId largest_peer_created_stream_id_; - // A counter for peer initiated open streams. Used when handles_accounting_ is - // true. + // A counter for peer initiated open streams. size_t num_open_incoming_streams_; - // A counter for self initiated open streams. Used when handles_accounting_ is - // true. + // A counter for self initiated open streams. size_t num_open_outgoing_streams_; - - // Latched value of quic_stream_id_manager_handles_accounting. - const bool handles_accounting_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager_test.cc index 00654b48c45..b7ba1d27035 100644 --- a/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager_test.cc @@ -78,33 +78,21 @@ INSTANTIATE_TEST_SUITE_P(Tests, ::testing::PrintToStringParamName()); TEST_P(LegacyQuicStreamIdManagerTest, CanOpenNextOutgoingStream) { - if (GetQuicReloadableFlag(quic_stream_id_manager_handles_accounting)) { - for (size_t i = 0; i < manager_.max_open_outgoing_streams() - 1; ++i) { - manager_.ActivateStream(/*is_incoming=*/false); - } - } - EXPECT_TRUE(manager_.CanOpenNextOutgoingStream( - manager_.max_open_outgoing_streams() - 1)); - if (GetQuicReloadableFlag(quic_stream_id_manager_handles_accounting)) { + for (size_t i = 0; i < manager_.max_open_outgoing_streams() - 1; ++i) { manager_.ActivateStream(/*is_incoming=*/false); } - EXPECT_FALSE( - manager_.CanOpenNextOutgoingStream(manager_.max_open_outgoing_streams())); + EXPECT_TRUE(manager_.CanOpenNextOutgoingStream()); + manager_.ActivateStream(/*is_incoming=*/false); + EXPECT_FALSE(manager_.CanOpenNextOutgoingStream()); } TEST_P(LegacyQuicStreamIdManagerTest, CanOpenIncomingStream) { - if (GetQuicReloadableFlag(quic_stream_id_manager_handles_accounting)) { - for (size_t i = 0; i < manager_.max_open_incoming_streams() - 1; ++i) { - manager_.ActivateStream(/*is_incoming=*/true); - } - } - EXPECT_TRUE( - manager_.CanOpenIncomingStream(manager_.max_open_incoming_streams() - 1)); - if (GetQuicReloadableFlag(quic_stream_id_manager_handles_accounting)) { + for (size_t i = 0; i < manager_.max_open_incoming_streams() - 1; ++i) { manager_.ActivateStream(/*is_incoming=*/true); } - EXPECT_FALSE( - manager_.CanOpenIncomingStream(manager_.max_open_incoming_streams())); + EXPECT_TRUE(manager_.CanOpenIncomingStream()); + manager_.ActivateStream(/*is_incoming=*/true); + EXPECT_FALSE(manager_.CanOpenIncomingStream()); } TEST_P(LegacyQuicStreamIdManagerTest, AvailableStreams) { 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 f1659a64fc9..ecb56993381 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 @@ -77,7 +77,7 @@ class QUIC_EXPORT_PRIVATE QpackBlockingManager { // same time. Use std::list instead of QuicCircularDeque because it has lower // memory footprint when holding few elements. using HeaderBlocksForStream = std::list<IndexSet>; - using HeaderBlocks = QuicUnorderedMap<QuicStreamId, HeaderBlocksForStream>; + using HeaderBlocks = QuicHashMap<QuicStreamId, HeaderBlocksForStream>; // Increase or decrease the reference count for each index in |indices|. void IncreaseReferenceCounts(const IndexSet& indices); 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 db7ec794268..67adf1268eb 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 @@ -378,9 +378,10 @@ std::string QpackEncoder::EncodeHeaderList( return SecondPassEncode(std::move(instructions), required_insert_count); } -void QpackEncoder::SetMaximumDynamicTableCapacity( +bool QpackEncoder::SetMaximumDynamicTableCapacity( uint64_t maximum_dynamic_table_capacity) { - header_table_.SetMaximumDynamicTableCapacity(maximum_dynamic_table_capacity); + return header_table_.SetMaximumDynamicTableCapacity( + maximum_dynamic_table_capacity); } void QpackEncoder::SetDynamicTableCapacity(uint64_t dynamic_table_capacity) { @@ -392,8 +393,12 @@ void QpackEncoder::SetDynamicTableCapacity(uint64_t dynamic_table_capacity) { DCHECK(success); } -void QpackEncoder::SetMaximumBlockedStreams(uint64_t maximum_blocked_streams) { +bool QpackEncoder::SetMaximumBlockedStreams(uint64_t maximum_blocked_streams) { + if (maximum_blocked_streams < maximum_blocked_streams_) { + return false; + } maximum_blocked_streams_ = maximum_blocked_streams; + return true; } void QpackEncoder::OnInsertCountIncrement(uint64_t increment) { 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 0f1d14ca539..202fc6d8ef1 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 @@ -63,7 +63,10 @@ class QUIC_EXPORT_PRIVATE QpackEncoder // measured in bytes. Called when SETTINGS_QPACK_MAX_TABLE_CAPACITY is // received. Encoder needs to know this value so that it can calculate // MaxEntries, used as a modulus to encode Required Insert Count. - void SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity); + // Returns true if |maximum_dynamic_table_capacity| is set for the first time + // or if it doesn't change current value. The setting is not changed when + // returning false. + bool SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity); // Set dynamic table capacity to |dynamic_table_capacity|. // |dynamic_table_capacity| must not exceed maximum dynamic table capacity. @@ -72,7 +75,9 @@ class QUIC_EXPORT_PRIVATE QpackEncoder // Set maximum number of blocked streams. // Called when SETTINGS_QPACK_BLOCKED_STREAMS is received. - void SetMaximumBlockedStreams(uint64_t maximum_blocked_streams); + // Returns true if |maximum_blocked_streams| doesn't decrease current value. + // The setting is not changed when returning false. + bool SetMaximumBlockedStreams(uint64_t maximum_blocked_streams); // QpackDecoderStreamReceiver::Delegate implementation void OnInsertCountIncrement(uint64_t increment) override; @@ -94,6 +99,12 @@ class QUIC_EXPORT_PRIVATE QpackEncoder return header_table_.dynamic_table_entry_referenced(); } + uint64_t maximum_blocked_streams() const { return maximum_blocked_streams_; } + + uint64_t MaximumDynamicTableCapacity() const { + return header_table_.maximum_dynamic_table_capacity(); + } + private: friend class test::QpackEncoderPeer; 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 472db893540..29e71488bda 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 @@ -186,16 +186,15 @@ bool QpackHeaderTable::SetDynamicTableCapacity(uint64_t capacity) { return true; } -void QpackHeaderTable::SetMaximumDynamicTableCapacity( +bool QpackHeaderTable::SetMaximumDynamicTableCapacity( uint64_t maximum_dynamic_table_capacity) { - // This method can only be called once: in the decoding context, shortly after - // construction; in the encoding context, upon receiving the SETTINGS frame. - DCHECK_EQ(0u, dynamic_table_capacity_); - DCHECK_EQ(0u, maximum_dynamic_table_capacity_); - DCHECK_EQ(0u, max_entries_); - - maximum_dynamic_table_capacity_ = maximum_dynamic_table_capacity; - max_entries_ = maximum_dynamic_table_capacity / 32; + 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_; } void QpackHeaderTable::RegisterObserver(uint64_t required_insert_count, 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 e3fb97504b3..bed1cc84afa 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 @@ -97,7 +97,10 @@ class QUIC_EXPORT_PRIVATE QpackHeaderTable { // value can be set upon connection establishment, whereas in the encoding // context it can be set when the SETTINGS frame is received. // This method must only be called at most once. - void SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity); + // Returns true if |maximum_dynamic_table_capacity| is set for the first time + // or if it doesn't change current value. The setting is not changed when + // returning false. + bool SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity); // Get |maximum_dynamic_table_capacity_|. uint64_t maximum_dynamic_table_capacity() const { diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream_test.cc index c77a6218bca..63e0f12e3cc 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream_test.cc @@ -7,6 +7,7 @@ #include "net/third_party/quiche/src/quic/core/http/http_constants.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" #include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" #include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h" @@ -66,6 +67,9 @@ class QpackSendStreamTest : public QuicTestWithParam<TestParams> { SupportedVersions(GetParam().version))), session_(connection_) { session_.Initialize(); + if (connection_->version().SupportsAntiAmplificationLimit()) { + QuicConnectionPeer::SetAddressValidated(connection_); + } QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow( session_.config(), kMinimumFlowControlSendWindow); QuicConfigPeer::SetReceivedInitialMaxStreamDataBytesUnidirectional( diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_coalesced_packet.cc b/chromium/net/third_party/quiche/src/quic/core/quic_coalesced_packet.cc index c18369eac9b..3b22180750d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_coalesced_packet.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_coalesced_packet.cc @@ -61,7 +61,7 @@ bool QuicCoalescedPacket::MaybeCoalescePacket( return false; } QUIC_DVLOG(1) << "Successfully coalesced packet: encryption_level: " - << EncryptionLevelToString(packet.encryption_level) + << packet.encryption_level << ", encrypted_length: " << packet.encrypted_length << ", current length: " << length_ << ", max_packet_length: " << max_packet_length_; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_config.cc b/chromium/net/third_party/quiche/src/quic/core/quic_config.cc index 7c781f4def0..30a5e2ba568 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_config.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_config.cc @@ -434,7 +434,7 @@ QuicConfig::QuicConfig() max_undecryptable_packets_(0), connection_options_(kCOPT, PRESENCE_OPTIONAL), client_connection_options_(kCLOP, PRESENCE_OPTIONAL), - idle_timeout_to_send_(QuicTime::Delta::Infinite()), + max_idle_timeout_to_send_(QuicTime::Delta::Infinite()), max_bidirectional_streams_(kMIBS, PRESENCE_REQUIRED), max_unidirectional_streams_(kMIUS, PRESENCE_OPTIONAL), bytes_for_connection_id_(kTCID, PRESENCE_OPTIONAL), @@ -447,12 +447,13 @@ QuicConfig::QuicConfig() initial_stream_flow_control_window_bytes_(kSFCW, PRESENCE_OPTIONAL), initial_session_flow_control_window_bytes_(kCFCW, PRESENCE_OPTIONAL), connection_migration_disabled_(kNCMR, PRESENCE_OPTIONAL), + support_handshake_done_(0, PRESENCE_OPTIONAL), alternate_server_address_ipv6_(kASAD, PRESENCE_OPTIONAL), alternate_server_address_ipv4_(kASAD, PRESENCE_OPTIONAL), stateless_reset_token_(kSRST, PRESENCE_OPTIONAL), max_ack_delay_ms_(kMAD, PRESENCE_OPTIONAL), ack_delay_exponent_(kADE, PRESENCE_OPTIONAL), - max_packet_size_(0, PRESENCE_OPTIONAL), + max_udp_payload_size_(0, PRESENCE_OPTIONAL), max_datagram_frame_size_(0, PRESENCE_OPTIONAL), active_connection_id_limit_(0, PRESENCE_OPTIONAL) { SetDefaults(); @@ -543,17 +544,17 @@ void QuicConfig::SetIdleNetworkTimeout(QuicTime::Delta idle_network_timeout) { QUIC_BUG << "Invalid idle network timeout " << idle_network_timeout; return; } - idle_timeout_to_send_ = idle_network_timeout; + max_idle_timeout_to_send_ = idle_network_timeout; } QuicTime::Delta QuicConfig::IdleNetworkTimeout() const { // TODO(b/152032210) add a QUIC_BUG to ensure that is not called before we've // received the peer's values. This is true in production code but not in all // of our tests that use a fake QuicConfig. - if (!received_idle_timeout_.has_value()) { - return idle_timeout_to_send_; + if (!received_max_idle_timeout_.has_value()) { + return max_idle_timeout_to_send_; } - return received_idle_timeout_.value(); + return received_max_idle_timeout_.value(); } void QuicConfig::SetMaxBidirectionalStreamsToSend(uint32_t max_streams) { @@ -592,7 +593,7 @@ void QuicConfig::SetMaxAckDelayToSendMs(uint32_t max_ack_delay_ms) { return max_ack_delay_ms_.SetSendValue(max_ack_delay_ms); } -uint32_t QuicConfig::GetMaxAckDelayToToSendMs() const { +uint32_t QuicConfig::GetMaxAckDelayToSendMs() const { return max_ack_delay_ms_.GetSendValue(); } @@ -620,20 +621,20 @@ uint32_t QuicConfig::ReceivedAckDelayExponent() const { return ack_delay_exponent_.GetReceivedValue(); } -void QuicConfig::SetMaxPacketSizeToSend(uint64_t max_packet_size) { - max_packet_size_.SetSendValue(max_packet_size); +void QuicConfig::SetMaxPacketSizeToSend(uint64_t max_udp_payload_size) { + max_udp_payload_size_.SetSendValue(max_udp_payload_size); } uint64_t QuicConfig::GetMaxPacketSizeToSend() const { - return max_packet_size_.GetSendValue(); + return max_udp_payload_size_.GetSendValue(); } bool QuicConfig::HasReceivedMaxPacketSize() const { - return max_packet_size_.HasReceivedValue(); + return max_udp_payload_size_.HasReceivedValue(); } uint64_t QuicConfig::ReceivedMaxPacketSize() const { - return max_packet_size_.GetReceivedValue(); + return max_udp_payload_size_.GetReceivedValue(); } void QuicConfig::SetMaxDatagramFrameSizeToSend( @@ -832,6 +833,19 @@ bool QuicConfig::DisableConnectionMigration() const { return connection_migration_disabled_.HasReceivedValue(); } +void QuicConfig::SetSupportHandshakeDone() { + support_handshake_done_.SetSendValue(1); +} + +bool QuicConfig::HandshakeDoneSupported() const { + return support_handshake_done_.HasSendValue() && + support_handshake_done_.GetSendValue() > 0; +} + +bool QuicConfig::PeerSupportsHandshakeDone() const { + return support_handshake_done_.HasReceivedValue(); +} + void QuicConfig::SetIPv6AlternateServerAddressToSend( const QuicSocketAddress& alternate_server_address_ipv6) { if (!alternate_server_address_ipv6.host().IsIPv6()) { @@ -871,12 +885,13 @@ const QuicSocketAddress& QuicConfig::ReceivedIPv4AlternateServerAddress() } void QuicConfig::SetOriginalConnectionIdToSend( - const QuicConnectionId& original_connection_id) { - original_connection_id_to_send_ = original_connection_id; + const QuicConnectionId& original_destination_connection_id) { + original_destination_connection_id_to_send_ = + original_destination_connection_id; } bool QuicConfig::HasReceivedOriginalConnectionId() const { - return received_original_connection_id_.has_value(); + return received_original_destination_connection_id_.has_value(); } QuicConnectionId QuicConfig::ReceivedOriginalConnectionId() const { @@ -884,7 +899,41 @@ QuicConnectionId QuicConfig::ReceivedOriginalConnectionId() const { QUIC_BUG << "No received original connection ID"; return EmptyQuicConnectionId(); } - return received_original_connection_id_.value(); + return received_original_destination_connection_id_.value(); +} + +void QuicConfig::SetInitialSourceConnectionIdToSend( + const QuicConnectionId& initial_source_connection_id) { + initial_source_connection_id_to_send_ = initial_source_connection_id; +} + +bool QuicConfig::HasReceivedInitialSourceConnectionId() const { + return received_initial_source_connection_id_.has_value(); +} + +QuicConnectionId QuicConfig::ReceivedInitialSourceConnectionId() const { + if (!HasReceivedInitialSourceConnectionId()) { + QUIC_BUG << "No received initial source connection ID"; + return EmptyQuicConnectionId(); + } + return received_initial_source_connection_id_.value(); +} + +void QuicConfig::SetRetrySourceConnectionIdToSend( + const QuicConnectionId& retry_source_connection_id) { + retry_source_connection_id_to_send_ = retry_source_connection_id; +} + +bool QuicConfig::HasReceivedRetrySourceConnectionId() const { + return received_retry_source_connection_id_.has_value(); +} + +QuicConnectionId QuicConfig::ReceivedRetrySourceConnectionId() const { + if (!HasReceivedRetrySourceConnectionId()) { + QUIC_BUG << "No received retry source connection ID"; + return EmptyQuicConnectionId(); + } + return received_retry_source_connection_id_.value(); } void QuicConfig::SetStatelessResetTokenToSend( @@ -938,14 +987,16 @@ void QuicConfig::ToHandshakeMessage( // the one received. Additionally, when QUIC_CRYPTO is used, the server // MUST send an idle timeout no greater than the idle timeout it received // from the client. We therefore send the received value if it is lower. - QuicFixedUint32 idle_timeout_seconds(kICSL, PRESENCE_REQUIRED); - uint32_t idle_timeout_to_send_seconds = idle_timeout_to_send_.ToSeconds(); - if (received_idle_timeout_.has_value() && - received_idle_timeout_->ToSeconds() < idle_timeout_to_send_seconds) { - idle_timeout_to_send_seconds = received_idle_timeout_->ToSeconds(); + QuicFixedUint32 max_idle_timeout_seconds(kICSL, PRESENCE_REQUIRED); + uint32_t max_idle_timeout_to_send_seconds = + max_idle_timeout_to_send_.ToSeconds(); + if (received_max_idle_timeout_.has_value() && + received_max_idle_timeout_->ToSeconds() < + max_idle_timeout_to_send_seconds) { + max_idle_timeout_to_send_seconds = received_max_idle_timeout_->ToSeconds(); } - idle_timeout_seconds.SetSendValue(idle_timeout_to_send_seconds); - idle_timeout_seconds.ToHandshakeMessage(out); + max_idle_timeout_seconds.SetSendValue(max_idle_timeout_to_send_seconds); + max_idle_timeout_seconds.ToHandshakeMessage(out); // Do not need a version check here, max...bi... will encode // as "MIDS" -- the max initial dynamic streams tag -- if @@ -955,8 +1006,15 @@ void QuicConfig::ToHandshakeMessage( max_unidirectional_streams_.ToHandshakeMessage(out); ack_delay_exponent_.ToHandshakeMessage(out); } - if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_negotiate_ack_delay_time, 1, 4); + if (GetQuicReloadableFlag(quic_dont_send_max_ack_delay_if_default)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_dont_send_max_ack_delay_if_default); + if (max_ack_delay_ms_.GetSendValue() != kDefaultDelayedAckTimeMs) { + // Only send max ack delay if it is using a non-default value, because + // the default value is used by QuicSentPacketManager if it is not + // sent during the handshake, and we want to save bytes. + max_ack_delay_ms_.ToHandshakeMessage(out); + } + } else { max_ack_delay_ms_.ToHandshakeMessage(out); } bytes_for_connection_id_.ToHandshakeMessage(out); @@ -986,12 +1044,12 @@ QuicErrorCode QuicConfig::ProcessPeerHello( // the one received. Additionally, when QUIC_CRYPTO is used, the server // MUST send an idle timeout no greater than the idle timeout it received // from the client. - QuicFixedUint32 idle_timeout_seconds(kICSL, PRESENCE_REQUIRED); - error = idle_timeout_seconds.ProcessPeerHello(peer_hello, hello_type, - error_details); + QuicFixedUint32 max_idle_timeout_seconds(kICSL, PRESENCE_REQUIRED); + error = max_idle_timeout_seconds.ProcessPeerHello(peer_hello, hello_type, + error_details); if (error == QUIC_NO_ERROR) { - if (idle_timeout_seconds.GetReceivedValue() > - idle_timeout_to_send_.ToSeconds()) { + if (max_idle_timeout_seconds.GetReceivedValue() > + max_idle_timeout_to_send_.ToSeconds()) { // The received value is higher than ours, ignore it if from the client // and raise an error if from the server. if (hello_type == SERVER) { @@ -1000,8 +1058,8 @@ QuicErrorCode QuicConfig::ProcessPeerHello( "Invalid value received for " + QuicTagToString(kICSL); } } else { - received_idle_timeout_ = QuicTime::Delta::FromSeconds( - idle_timeout_seconds.GetReceivedValue()); + received_max_idle_timeout_ = QuicTime::Delta::FromSeconds( + max_idle_timeout_seconds.GetReceivedValue()); } } } @@ -1056,9 +1114,7 @@ QuicErrorCode QuicConfig::ProcessPeerHello( error_details); } - if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time) && - error == QUIC_NO_ERROR) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_negotiate_ack_delay_time, 2, 4); + if (error == QUIC_NO_ERROR) { error = max_ack_delay_ms_.ProcessPeerHello(peer_hello, hello_type, error_details); } @@ -1073,12 +1129,13 @@ QuicErrorCode QuicConfig::ProcessPeerHello( } bool QuicConfig::FillTransportParameters(TransportParameters* params) const { - if (original_connection_id_to_send_.has_value()) { - params->original_connection_id = original_connection_id_to_send_.value(); + if (original_destination_connection_id_to_send_.has_value()) { + params->original_destination_connection_id = + original_destination_connection_id_to_send_.value(); } - params->idle_timeout_milliseconds.set_value( - idle_timeout_to_send_.ToMilliseconds()); + params->max_idle_timeout_ms.set_value( + max_idle_timeout_to_send_.ToMilliseconds()); if (stateless_reset_token_.HasSendValue()) { QuicUint128 stateless_reset_token = stateless_reset_token_.GetSendValue(); @@ -1088,7 +1145,7 @@ bool QuicConfig::FillTransportParameters(TransportParameters* params) const { sizeof(stateless_reset_token)); } - params->max_packet_size.set_value(GetMaxPacketSizeToSend()); + params->max_udp_payload_size.set_value(GetMaxPacketSizeToSend()); params->max_datagram_frame_size.set_value(GetMaxDatagramFrameSizeToSend()); params->initial_max_data.set_value( GetInitialSessionFlowControlWindowToSend()); @@ -1108,14 +1165,12 @@ bool QuicConfig::FillTransportParameters(TransportParameters* params) const { GetMaxBidirectionalStreamsToSend()); params->initial_max_streams_uni.set_value( GetMaxUnidirectionalStreamsToSend()); - if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_negotiate_ack_delay_time, 3, 4); - params->max_ack_delay.set_value(kDefaultDelayedAckTimeMs); - } + params->max_ack_delay.set_value(GetMaxAckDelayToSendMs()); params->ack_delay_exponent.set_value(GetAckDelayExponentToSend()); - params->disable_migration = + params->disable_active_migration = connection_migration_disabled_.HasSendValue() && connection_migration_disabled_.GetSendValue() != 0; + params->support_handshake_done = HandshakeDoneSupported(); if (alternate_server_address_ipv6_.HasSendValue() || alternate_server_address_ipv4_.HasSendValue()) { @@ -1138,6 +1193,16 @@ bool QuicConfig::FillTransportParameters(TransportParameters* params) const { active_connection_id_limit_.GetSendValue()); } + if (initial_source_connection_id_to_send_.has_value()) { + params->initial_source_connection_id = + initial_source_connection_id_to_send_.value(); + } + + if (retry_source_connection_id_to_send_.has_value()) { + params->retry_source_connection_id = + retry_source_connection_id_to_send_.value(); + } + if (GetQuicRestartFlag(quic_google_transport_param_send_new)) { QUIC_RESTART_FLAG_COUNT_N(quic_google_transport_param_send_new, 1, 3); if (initial_round_trip_time_us_.HasSendValue()) { @@ -1171,18 +1236,19 @@ QuicErrorCode QuicConfig::ProcessTransportParameters( HelloType hello_type, bool is_resumption, std::string* error_details) { - if (!is_resumption && params.original_connection_id.has_value()) { - received_original_connection_id_ = params.original_connection_id.value(); + if (!is_resumption && params.original_destination_connection_id.has_value()) { + received_original_destination_connection_id_ = + params.original_destination_connection_id.value(); } - if (params.idle_timeout_milliseconds.value() > 0 && - params.idle_timeout_milliseconds.value() < - static_cast<uint64_t>(idle_timeout_to_send_.ToMilliseconds())) { + if (params.max_idle_timeout_ms.value() > 0 && + params.max_idle_timeout_ms.value() < + static_cast<uint64_t>(max_idle_timeout_to_send_.ToMilliseconds())) { // An idle timeout of zero indicates it is disabled. // We also ignore values higher than ours which will cause us to use the // smallest value between ours and our peer's. - received_idle_timeout_ = QuicTime::Delta::FromMilliseconds( - params.idle_timeout_milliseconds.value()); + received_max_idle_timeout_ = + QuicTime::Delta::FromMilliseconds(params.max_idle_timeout_ms.value()); } if (!is_resumption && !params.stateless_reset_token.empty()) { @@ -1198,8 +1264,8 @@ QuicErrorCode QuicConfig::ProcessTransportParameters( stateless_reset_token_.SetReceivedValue(stateless_reset_token); } - if (params.max_packet_size.IsValid()) { - max_packet_size_.SetReceivedValue(params.max_packet_size.value()); + if (params.max_udp_payload_size.IsValid()) { + max_udp_payload_size_.SetReceivedValue(params.max_udp_payload_size.value()); } if (params.max_datagram_frame_size.IsValid()) { @@ -1233,10 +1299,7 @@ QuicErrorCode QuicConfig::ProcessTransportParameters( params.initial_max_stream_data_uni.value()); if (!is_resumption) { - if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_negotiate_ack_delay_time, 4, 4); - max_ack_delay_ms_.SetReceivedValue(params.max_ack_delay.value()); - } + max_ack_delay_ms_.SetReceivedValue(params.max_ack_delay.value()); if (params.ack_delay_exponent.IsValid()) { ack_delay_exponent_.SetReceivedValue(params.ack_delay_exponent.value()); } @@ -1252,13 +1315,27 @@ QuicErrorCode QuicConfig::ProcessTransportParameters( } } - if (params.disable_migration) { + if (params.disable_active_migration) { connection_migration_disabled_.SetReceivedValue(1u); } + if (params.support_handshake_done) { + support_handshake_done_.SetReceivedValue(1u); + } active_connection_id_limit_.SetReceivedValue( params.active_connection_id_limit.value()); + if (!is_resumption) { + if (params.initial_source_connection_id.has_value()) { + received_initial_source_connection_id_ = + params.initial_source_connection_id.value(); + } + if (params.retry_source_connection_id.has_value()) { + received_retry_source_connection_id_ = + params.retry_source_connection_id.value(); + } + } + bool google_params_already_parsed = false; if (GetQuicRestartFlag(quic_google_transport_param_send_new)) { QUIC_RESTART_FLAG_COUNT_N(quic_google_transport_param_send_new, 2, 3); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_config.h b/chromium/net/third_party/quiche/src/quic/core/quic_config.h index 4d2bacce383..6f041b82f45 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_config.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_config.h @@ -382,6 +382,11 @@ class QUIC_EXPORT_PRIVATE QuicConfig { void SetDisableConnectionMigration(); bool DisableConnectionMigration() const; + // Support handshake done. + void SetSupportHandshakeDone(); + bool HandshakeDoneSupported() const; + bool PeerSupportsHandshakeDone() const; + // IPv6 alternate server address. void SetIPv6AlternateServerAddressToSend( const QuicSocketAddress& alternate_server_address_ipv6); @@ -394,9 +399,9 @@ class QUIC_EXPORT_PRIVATE QuicConfig { bool HasReceivedIPv4AlternateServerAddress() const; const QuicSocketAddress& ReceivedIPv4AlternateServerAddress() const; - // Original connection ID. + // Original destination connection ID. void SetOriginalConnectionIdToSend( - const QuicConnectionId& original_connection_id); + const QuicConnectionId& original_destination_connection_id); bool HasReceivedOriginalConnectionId() const; QuicConnectionId ReceivedOriginalConnectionId() const; @@ -411,7 +416,7 @@ class QUIC_EXPORT_PRIVATE QuicConfig { // The received delay is the value received from // the peer (QuicSentPacketManager::peer_max_ack_delay_). void SetMaxAckDelayToSendMs(uint32_t max_ack_delay_ms); - uint32_t GetMaxAckDelayToToSendMs() const; + uint32_t GetMaxAckDelayToSendMs() const; bool HasReceivedMaxAckDelayMs() const; uint32_t ReceivedMaxAckDelayMs() const; @@ -420,8 +425,8 @@ class QUIC_EXPORT_PRIVATE QuicConfig { bool HasReceivedAckDelayExponent() const; uint32_t ReceivedAckDelayExponent() const; - // IETF QUIC max_packet_size transport parameter. - void SetMaxPacketSizeToSend(uint64_t max_packet_size); + // IETF QUIC max_udp_payload_size transport parameter. + void SetMaxPacketSizeToSend(uint64_t max_udp_payload_size); uint64_t GetMaxPacketSizeToSend() const; bool HasReceivedMaxPacketSize() const; uint64_t ReceivedMaxPacketSize() const; @@ -438,6 +443,18 @@ class QUIC_EXPORT_PRIVATE QuicConfig { bool HasReceivedActiveConnectionIdLimit() const; uint64_t ReceivedActiveConnectionIdLimit() const; + // Initial source connection ID. + void SetInitialSourceConnectionIdToSend( + const QuicConnectionId& initial_source_connection_id); + bool HasReceivedInitialSourceConnectionId() const; + QuicConnectionId ReceivedInitialSourceConnectionId() const; + + // Retry source connection ID. + void SetRetrySourceConnectionIdToSend( + const QuicConnectionId& retry_source_connection_id); + bool HasReceivedRetrySourceConnectionId() const; + QuicConnectionId ReceivedRetrySourceConnectionId() const; + bool negotiated() const; void SetCreateSessionTagIndicators(QuicTagVector tags); @@ -501,12 +518,12 @@ class QUIC_EXPORT_PRIVATE QuicConfig { QuicFixedTagVector connection_options_; // Connection options which only affect the client side. QuicFixedTagVector client_connection_options_; - // Idle network timeout. + // Maximum idle network timeout. // Uses the max_idle_timeout transport parameter in IETF QUIC. - // Note that received_idle_timeout_ is only populated if we receive the + // Note that received_max_idle_timeout_ is only populated if we receive the // peer's value, which isn't guaranteed in IETF QUIC as sending is optional. - QuicTime::Delta idle_timeout_to_send_; - quiche::QuicheOptional<QuicTime::Delta> received_idle_timeout_; + QuicTime::Delta max_idle_timeout_to_send_; + quiche::QuicheOptional<QuicTime::Delta> received_max_idle_timeout_; // Maximum number of dynamic streams that a Google QUIC connection // can support or the maximum number of bidirectional streams that // an IETF QUIC connection can support. @@ -554,6 +571,10 @@ class QUIC_EXPORT_PRIVATE QuicConfig { // Uses the disable_active_migration transport parameter in IETF QUIC. QuicFixedUint32 connection_migration_disabled_; + // Whether handshake done is supported. Only used in T050. + // Uses the support_handshake_done transport parameter in IETF QUIC. + QuicFixedUint32 support_handshake_done_; + // Alternate server addresses the client could connect to. // Uses the preferred_address transport parameter in IETF QUIC. // Note that when QUIC_CRYPTO is in use, only one of the addresses is sent. @@ -583,8 +604,8 @@ class QUIC_EXPORT_PRIVATE QuicConfig { QuicFixedUint32 ack_delay_exponent_; // Maximum packet size in bytes. - // Uses the max_packet_size transport parameter in IETF QUIC. - QuicFixedUint62 max_packet_size_; + // Uses the max_udp_payload_size transport parameter in IETF QUIC. + QuicFixedUint62 max_udp_payload_size_; // Maximum DATAGRAM/MESSAGE frame size in bytes. // Uses the max_datagram_frame_size transport parameter in IETF QUIC. @@ -594,10 +615,28 @@ class QUIC_EXPORT_PRIVATE QuicConfig { // Uses the active_connection_id_limit transport parameter in IETF QUIC. QuicFixedUint62 active_connection_id_limit_; - // Sent by the server when it has previously sent a RETRY packet. - // Uses the original_connection_id transport parameter in IETF QUIC. - quiche::QuicheOptional<QuicConnectionId> original_connection_id_to_send_; - quiche::QuicheOptional<QuicConnectionId> received_original_connection_id_; + // The value of the Destination Connection ID field from the first + // Initial packet sent by the client. + // Uses the original_destination_connection_id transport parameter in + // IETF QUIC. + quiche::QuicheOptional<QuicConnectionId> + original_destination_connection_id_to_send_; + quiche::QuicheOptional<QuicConnectionId> + received_original_destination_connection_id_; + + // The value that the endpoint included in the Source Connection ID field of + // the first Initial packet it sent. + // Uses the initial_source_connection_id transport parameter in IETF QUIC. + quiche::QuicheOptional<QuicConnectionId> + initial_source_connection_id_to_send_; + quiche::QuicheOptional<QuicConnectionId> + received_initial_source_connection_id_; + + // The value that the server included in the Source Connection ID field of a + // Retry packet it sent. + // Uses the retry_source_connection_id transport parameter in IETF QUIC. + quiche::QuicheOptional<QuicConnectionId> retry_source_connection_id_to_send_; + quiche::QuicheOptional<QuicConnectionId> received_retry_source_connection_id_; // Custom transport parameters that can be sent and received in the TLS // handshake. diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_config_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_config_test.cc index 4aa10ef40b0..050a091a8d2 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_config_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_config_test.cc @@ -180,12 +180,8 @@ TEST_P(QuicConfigTest, ProcessClientHello) { 2 * kInitialStreamFlowControlWindowForTest); EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(), 2 * kInitialSessionFlowControlWindowForTest); - if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) { - EXPECT_TRUE(config_.HasReceivedMaxAckDelayMs()); - EXPECT_EQ(kTestMaxAckDelayMs, config_.ReceivedMaxAckDelayMs()); - } else { - EXPECT_FALSE(config_.HasReceivedMaxAckDelayMs()); - } + EXPECT_TRUE(config_.HasReceivedMaxAckDelayMs()); + EXPECT_EQ(kTestMaxAckDelayMs, config_.ReceivedMaxAckDelayMs()); // IETF QUIC stream limits should not be received in QUIC crypto messages. EXPECT_FALSE( @@ -238,12 +234,8 @@ TEST_P(QuicConfigTest, ProcessServerHello) { EXPECT_FALSE(config_.HasReceivedIPv6AlternateServerAddress()); EXPECT_TRUE(config_.HasReceivedStatelessResetToken()); EXPECT_EQ(kTestResetToken, config_.ReceivedStatelessResetToken()); - if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) { - EXPECT_TRUE(config_.HasReceivedMaxAckDelayMs()); - EXPECT_EQ(kTestMaxAckDelayMs, config_.ReceivedMaxAckDelayMs()); - } else { - EXPECT_FALSE(config_.HasReceivedMaxAckDelayMs()); - } + EXPECT_TRUE(config_.HasReceivedMaxAckDelayMs()); + EXPECT_EQ(kTestMaxAckDelayMs, config_.ReceivedMaxAckDelayMs()); // IETF QUIC stream limits should not be received in QUIC crypto messages. EXPECT_FALSE( @@ -442,7 +434,7 @@ TEST_P(QuicConfigTest, IncomingLargeIdleTimeoutTransportParameter) { // Since the received value is above ours, we should then use ours. config_.SetIdleNetworkTimeout(quic::QuicTime::Delta::FromSeconds(60)); TransportParameters params; - params.idle_timeout_milliseconds.set_value(120000); + params.max_idle_timeout_ms.set_value(120000); std::string error_details = "foobar"; EXPECT_THAT(config_.ProcessTransportParameters( @@ -468,6 +460,10 @@ TEST_P(QuicConfigTest, FillTransportParams) { config_.SetMaxDatagramFrameSizeToSend(kMaxDatagramFrameSizeForTest); config_.SetActiveConnectionIdLimitToSend(kFakeActiveConnectionIdLimit); + config_.SetOriginalConnectionIdToSend(TestConnectionId(0x1111)); + config_.SetInitialSourceConnectionIdToSend(TestConnectionId(0x2222)); + config_.SetRetrySourceConnectionIdToSend(TestConnectionId(0x3333)); + TransportParameters params; config_.FillTransportParameters(¶ms); @@ -479,13 +475,23 @@ TEST_P(QuicConfigTest, FillTransportParams) { params.initial_max_stream_data_uni.value()); EXPECT_EQ(static_cast<uint64_t>(kMaximumIdleTimeoutSecs * 1000), - params.idle_timeout_milliseconds.value()); + params.max_idle_timeout_ms.value()); - EXPECT_EQ(kMaxPacketSizeForTest, params.max_packet_size.value()); + EXPECT_EQ(kMaxPacketSizeForTest, params.max_udp_payload_size.value()); EXPECT_EQ(kMaxDatagramFrameSizeForTest, params.max_datagram_frame_size.value()); EXPECT_EQ(kFakeActiveConnectionIdLimit, params.active_connection_id_limit.value()); + + ASSERT_TRUE(params.original_destination_connection_id.has_value()); + EXPECT_EQ(TestConnectionId(0x1111), + params.original_destination_connection_id.value()); + ASSERT_TRUE(params.initial_source_connection_id.has_value()); + EXPECT_EQ(TestConnectionId(0x2222), + params.initial_source_connection_id.value()); + ASSERT_TRUE(params.retry_source_connection_id.has_value()); + EXPECT_EQ(TestConnectionId(0x3333), + params.retry_source_connection_id.value()); } TEST_P(QuicConfigTest, ProcessTransportParametersServer) { @@ -493,7 +499,6 @@ TEST_P(QuicConfigTest, ProcessTransportParametersServer) { // TransportParameters are only used for QUIC+TLS. return; } - SetQuicReloadableFlag(quic_negotiate_ack_delay_time, true); TransportParameters params; params.initial_max_stream_data_bidi_local.set_value( @@ -502,13 +507,16 @@ TEST_P(QuicConfigTest, ProcessTransportParametersServer) { 3 * kMinimumFlowControlSendWindow); params.initial_max_stream_data_uni.set_value(4 * kMinimumFlowControlSendWindow); - params.max_packet_size.set_value(kMaxPacketSizeForTest); + params.max_udp_payload_size.set_value(kMaxPacketSizeForTest); params.max_datagram_frame_size.set_value(kMaxDatagramFrameSizeForTest); params.initial_max_streams_bidi.set_value(kDefaultMaxStreamsPerConnection); params.stateless_reset_token = CreateFakeStatelessResetToken(); params.max_ack_delay.set_value(kFakeMaxAckDelay); params.ack_delay_exponent.set_value(kFakeAckDelayExponent); params.active_connection_id_limit.set_value(kFakeActiveConnectionIdLimit); + params.original_destination_connection_id = TestConnectionId(0x1111); + params.initial_source_connection_id = TestConnectionId(0x2222); + params.retry_source_connection_id = TestConnectionId(0x3333); std::string error_details; EXPECT_THAT(config_.ProcessTransportParameters( @@ -544,11 +552,15 @@ TEST_P(QuicConfigTest, ProcessTransportParametersServer) { config_.ReceivedMaxBidirectionalStreams()); EXPECT_FALSE(config_.DisableConnectionMigration()); + EXPECT_FALSE(config_.PeerSupportsHandshakeDone()); // The following config shouldn't be processed because of resumption. EXPECT_FALSE(config_.HasReceivedStatelessResetToken()); EXPECT_FALSE(config_.HasReceivedMaxAckDelayMs()); EXPECT_FALSE(config_.HasReceivedAckDelayExponent()); + EXPECT_FALSE(config_.HasReceivedOriginalConnectionId()); + EXPECT_FALSE(config_.HasReceivedInitialSourceConnectionId()); + EXPECT_FALSE(config_.HasReceivedRetrySourceConnectionId()); // Let the config process another slightly tweaked transport paramters. // Note that the values for flow control and stream limit cannot be smaller @@ -559,11 +571,12 @@ TEST_P(QuicConfigTest, ProcessTransportParametersServer) { 4 * kMinimumFlowControlSendWindow); params.initial_max_stream_data_uni.set_value(5 * kMinimumFlowControlSendWindow); - params.max_packet_size.set_value(2 * kMaxPacketSizeForTest); + params.max_udp_payload_size.set_value(2 * kMaxPacketSizeForTest); params.max_datagram_frame_size.set_value(2 * kMaxDatagramFrameSizeForTest); params.initial_max_streams_bidi.set_value(2 * kDefaultMaxStreamsPerConnection); - params.disable_migration = true; + params.disable_active_migration = true; + params.support_handshake_done = true; EXPECT_THAT(config_.ProcessTransportParameters( params, SERVER, /* is_resumption = */ false, &error_details), @@ -598,6 +611,7 @@ TEST_P(QuicConfigTest, ProcessTransportParametersServer) { config_.ReceivedMaxBidirectionalStreams()); EXPECT_TRUE(config_.DisableConnectionMigration()); + EXPECT_TRUE(config_.PeerSupportsHandshakeDone()); ASSERT_TRUE(config_.HasReceivedStatelessResetToken()); ASSERT_TRUE(config_.HasReceivedMaxAckDelayMs()); @@ -609,6 +623,15 @@ TEST_P(QuicConfigTest, ProcessTransportParametersServer) { ASSERT_TRUE(config_.HasReceivedActiveConnectionIdLimit()); EXPECT_EQ(config_.ReceivedActiveConnectionIdLimit(), kFakeActiveConnectionIdLimit); + + ASSERT_TRUE(config_.HasReceivedOriginalConnectionId()); + EXPECT_EQ(config_.ReceivedOriginalConnectionId(), TestConnectionId(0x1111)); + ASSERT_TRUE(config_.HasReceivedInitialSourceConnectionId()); + EXPECT_EQ(config_.ReceivedInitialSourceConnectionId(), + TestConnectionId(0x2222)); + ASSERT_TRUE(config_.HasReceivedRetrySourceConnectionId()); + EXPECT_EQ(config_.ReceivedRetrySourceConnectionId(), + TestConnectionId(0x3333)); } TEST_P(QuicConfigTest, DisableMigrationTransportParameter) { @@ -617,7 +640,7 @@ TEST_P(QuicConfigTest, DisableMigrationTransportParameter) { return; } TransportParameters params; - params.disable_migration = true; + params.disable_active_migration = true; std::string error_details; EXPECT_THAT(config_.ProcessTransportParameters( params, SERVER, /* is_resumption = */ false, &error_details), 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 4f089a44fe9..4a21277e942 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 @@ -25,6 +25,7 @@ #include "net/third_party/quiche/src/quic/core/quic_connection_id.h" #include "net/third_party/quiche/src/quic/core/quic_constants.h" #include "net/third_party/quiche/src/quic/core/quic_error_codes.h" +#include "net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" @@ -33,6 +34,7 @@ #include "net/third_party/quiche/src/quic/platform/api/quic_exported_stats.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" #include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" #include "net/third_party/quiche/src/quic/platform/api/quic_string_utils.h" @@ -107,20 +109,6 @@ class SendAlarmDelegate : public QuicAlarm::Delegate { QuicConnection* connection_; }; -class PathDegradingAlarmDelegate : public QuicAlarm::Delegate { - public: - explicit PathDegradingAlarmDelegate(QuicConnection* connection) - : connection_(connection) {} - PathDegradingAlarmDelegate(const PathDegradingAlarmDelegate&) = delete; - PathDegradingAlarmDelegate& operator=(const PathDegradingAlarmDelegate&) = - delete; - - void OnAlarm() override { connection_->OnPathDegradingTimeout(); } - - private: - QuicConnection* connection_; -}; - class TimeoutAlarmDelegate : public QuicAlarm::Delegate { public: explicit TimeoutAlarmDelegate(QuicConnection* connection) @@ -260,7 +248,7 @@ QuicConnection::QuicConnection( send_version_negotiation_packet_with_prefixed_lengths_(false), idle_timeout_connection_close_behavior_( ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET), - close_connection_after_five_rtos_(false), + num_rtos_for_blackhole_detection_(0), uber_received_packet_manager_(&stats_), stop_waiting_count_(0), pending_retransmission_alarm_(false), @@ -327,7 +315,6 @@ QuicConnection::QuicConnection( supports_release_time_(false), release_time_into_future_(QuicTime::Delta::Zero()), drop_incoming_retry_packets_(false), - max_consecutive_ptos_(0), bytes_received_before_address_validation_(0), bytes_sent_before_address_validation_(0), address_validated_(false), @@ -335,7 +322,8 @@ QuicConnection::QuicConnection( idle_network_detector_(this, clock_->ApproximateNow(), &arena_, - alarm_factory_) { + alarm_factory_), + support_handshake_done_(version().HasHandshakeDone()) { QUIC_DLOG(INFO) << ENDPOINT << "Created connection with server connection ID " << server_connection_id << " and version: " << ParsedQuicVersionToString(version()); @@ -345,9 +333,6 @@ QuicConnection::QuicConnection( << "QuicConnection: attempted to use server connection ID " << server_connection_id << " which is invalid with version " << QuicVersionToString(transport_version()); - if (advance_ack_timeout_update_) { - QUIC_RELOADABLE_FLAG_COUNT(quic_advance_ack_timeout_update); - } framer_.set_visitor(this); stats_.connection_creation_time = clock_->ApproximateNow(); // TODO(ianswett): Supply the NetworkChangeVisitor as a constructor argument @@ -377,6 +362,9 @@ QuicConnection::QuicConnection( if (perspective_ == Perspective::IS_SERVER) { SetVersionNegotiated(); } + if (default_enable_5rto_blackhole_detection_) { + num_rtos_for_blackhole_detection_ = 5; + } } void QuicConnection::InstallInitialCrypters(QuicConnectionId connection_id) { @@ -402,50 +390,158 @@ void QuicConnection::ClearQueuedPackets() { buffered_packets_.clear(); } -void QuicConnection::SetFromConfig(const QuicConfig& config) { - if (config.negotiated()) { - // Handshake complete, set handshake timeout to Infinite. - SetNetworkTimeouts(QuicTime::Delta::Infinite(), - config.IdleNetworkTimeout()); - idle_timeout_connection_close_behavior_ = - ConnectionCloseBehavior::SILENT_CLOSE; - if (original_connection_id_.has_value()) { - DCHECK_EQ(perspective_, Perspective::IS_CLIENT); - // We received a RETRY packet, validate that the |original_connection_id| - // from the config matches the one from the RETRY. - if (!config.HasReceivedOriginalConnectionId() || - config.ReceivedOriginalConnectionId() != - original_connection_id_.value()) { +bool QuicConnection::ValidateConfigConnectionIdsOld(const QuicConfig& config) { + // This function validates connection IDs as defined in IETF draft-27 and + // earlier. + DCHECK(config.negotiated()); + DCHECK(!version().AuthenticatesHandshakeConnectionIds()); + if (original_destination_connection_id_.has_value() && + retry_source_connection_id_.has_value()) { + DCHECK_EQ(perspective_, Perspective::IS_CLIENT); + // We received a RETRY packet, validate that the original destination + // connection ID from the config matches the one from the RETRY. + if (!config.HasReceivedOriginalConnectionId() || + config.ReceivedOriginalConnectionId() != + original_destination_connection_id_.value()) { + std::string received_value; + if (config.HasReceivedOriginalConnectionId()) { + received_value = config.ReceivedOriginalConnectionId().ToString(); + } else { + received_value = "none"; + } + std::string error_details = quiche::QuicheStrCat( + "Bad original_connection_id: expected ", + original_destination_connection_id_.value().ToString(), ", received ", + received_value, ", RETRY used ", server_connection_id_.ToString()); + CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details, + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + } else { + // We did not receive a RETRY packet, make sure we did not receive the + // original_destination_connection_id transport parameter. + if (config.HasReceivedOriginalConnectionId()) { + std::string error_details = quiche::QuicheStrCat( + "Bad original_connection_id: did not receive RETRY but received ", + config.ReceivedOriginalConnectionId().ToString()); + CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details, + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + } + return true; +} + +bool QuicConnection::ValidateConfigConnectionIds(const QuicConfig& config) { + DCHECK(config.negotiated()); + if (!version().UsesTls()) { + // QUIC+TLS is required to transmit connection ID transport parameters. + return true; + } + if (!version().AuthenticatesHandshakeConnectionIds()) { + return ValidateConfigConnectionIdsOld(config); + } + // This function validates connection IDs as defined in IETF draft-28 and + // later. + + // Validate initial_source_connection_id. + QuicConnectionId expected_initial_source_connection_id; + if (perspective_ == Perspective::IS_CLIENT) { + expected_initial_source_connection_id = server_connection_id_; + } else { + expected_initial_source_connection_id = client_connection_id_; + } + if (!config.HasReceivedInitialSourceConnectionId() || + config.ReceivedInitialSourceConnectionId() != + expected_initial_source_connection_id) { + std::string received_value; + if (config.HasReceivedInitialSourceConnectionId()) { + received_value = config.ReceivedInitialSourceConnectionId().ToString(); + } else { + received_value = "none"; + } + std::string error_details = + quiche::QuicheStrCat("Bad initial_source_connection_id: expected ", + expected_initial_source_connection_id.ToString(), + ", received ", received_value); + CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details, + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + if (perspective_ == Perspective::IS_CLIENT) { + // Validate original_destination_connection_id. + if (!config.HasReceivedOriginalConnectionId() || + config.ReceivedOriginalConnectionId() != + GetOriginalDestinationConnectionId()) { + std::string received_value; + if (config.HasReceivedOriginalConnectionId()) { + received_value = config.ReceivedOriginalConnectionId().ToString(); + } else { + received_value = "none"; + } + std::string error_details = quiche::QuicheStrCat( + "Bad original_destination_connection_id: expected ", + GetOriginalDestinationConnectionId().ToString(), ", received ", + received_value); + CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details, + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + // Validate retry_source_connection_id. + if (retry_source_connection_id_.has_value()) { + // We received a RETRY packet, validate that the retry source + // connection ID from the config matches the one from the RETRY. + if (!config.HasReceivedRetrySourceConnectionId() || + config.ReceivedRetrySourceConnectionId() != + retry_source_connection_id_.value()) { std::string received_value; - if (config.HasReceivedOriginalConnectionId()) { - received_value = config.ReceivedOriginalConnectionId().ToString(); + if (config.HasReceivedRetrySourceConnectionId()) { + received_value = config.ReceivedRetrySourceConnectionId().ToString(); } else { received_value = "none"; } - std::string error_details = quiche::QuicheStrCat( - "Bad original_connection_id: expected ", - original_connection_id_.value().ToString(), ", received ", - received_value, ", RETRY used ", server_connection_id_.ToString()); + std::string error_details = + quiche::QuicheStrCat("Bad retry_source_connection_id: expected ", + retry_source_connection_id_.value().ToString(), + ", received ", received_value); CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); - return; + return false; } } else { // We did not receive a RETRY packet, make sure we did not receive the - // original_connection_id transport parameter. - if (config.HasReceivedOriginalConnectionId()) { + // retry_source_connection_id transport parameter. + if (config.HasReceivedRetrySourceConnectionId()) { std::string error_details = quiche::QuicheStrCat( - "Bad original_connection_id: did not receive RETRY but received ", - config.ReceivedOriginalConnectionId().ToString()); + "Bad retry_source_connection_id: did not receive RETRY but " + "received ", + config.ReceivedRetrySourceConnectionId().ToString()); CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); - return; + return false; } } + } + return true; +} + +void QuicConnection::SetFromConfig(const QuicConfig& config) { + if (config.negotiated()) { + // Handshake complete, set handshake timeout to Infinite. + SetNetworkTimeouts(QuicTime::Delta::Infinite(), + config.IdleNetworkTimeout()); + idle_timeout_connection_close_behavior_ = + ConnectionCloseBehavior::SILENT_CLOSE; + if (!ValidateConfigConnectionIds(config)) { + return; + } } else { SetNetworkTimeouts(config.max_time_before_crypto_handshake(), config.max_idle_time_before_crypto_handshake()); } + if (config.HandshakeDoneSupported()) { + support_handshake_done_ = true; + } sent_packet_manager_.SetFromConfig(config); if (config.HasReceivedBytesForConnectionId() && @@ -461,24 +557,41 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { if (config.HasClientRequestedIndependentOption(kMTUL, perspective_)) { SetMtuDiscoveryTarget(kMtuDiscoveryTargetPacketSizeLow); } + if (default_enable_5rto_blackhole_detection_) { + if (config.HasClientRequestedIndependentOption(kCBHD, perspective_)) { + QUIC_CODE_COUNT(quic_client_only_blackhole_detection); + blackhole_detection_disabled_ = true; + } + if (config.HasClientSentConnectionOption(k2RTO, perspective_)) { + QUIC_CODE_COUNT(quic_2rto_blackhole_detection); + num_rtos_for_blackhole_detection_ = 2; + } + if (config.HasClientSentConnectionOption(k3RTO, perspective_)) { + QUIC_CODE_COUNT(quic_3rto_blackhole_detection); + num_rtos_for_blackhole_detection_ = 3; + } + if (config.HasClientSentConnectionOption(k4RTO, perspective_)) { + QUIC_CODE_COUNT(quic_4rto_blackhole_detection); + num_rtos_for_blackhole_detection_ = 4; + } + if (config.HasClientSentConnectionOption(k6RTO, perspective_)) { + QUIC_CODE_COUNT(quic_6rto_blackhole_detection); + num_rtos_for_blackhole_detection_ = 6; + } + } if (debug_visitor_ != nullptr) { debug_visitor_->OnSetFromConfig(config); } uber_received_packet_manager_.SetFromConfig(config, perspective_); if (config.HasClientSentConnectionOption(k5RTO, perspective_)) { - close_connection_after_five_rtos_ = true; + num_rtos_for_blackhole_detection_ = 5; } if (sent_packet_manager_.pto_enabled()) { - if (config.HasClientSentConnectionOption(k6PTO, perspective_)) { - max_consecutive_ptos_ = 5; - QUIC_CODE_COUNT(quic_close_connection_6pto); - } - if (config.HasClientSentConnectionOption(k7PTO, perspective_)) { - max_consecutive_ptos_ = 6; - } - if (config.HasClientSentConnectionOption(k8PTO, perspective_)) { - max_consecutive_ptos_ = 7; + if (config.HasClientSentConnectionOption(k6PTO, perspective_) || + config.HasClientSentConnectionOption(k7PTO, perspective_) || + config.HasClientSentConnectionOption(k8PTO, perspective_)) { + num_rtos_for_blackhole_detection_ = 5; } } if (config.HasClientSentConnectionOption(kNSTP, perspective_)) { @@ -502,8 +615,7 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { } if (config.HasReceivedMaxPacketSize()) { peer_max_packet_size_ = config.ReceivedMaxPacketSize(); - packet_creator_.SetMaxPacketLength( - GetLimitedMaxPacketSize(packet_creator_.max_packet_length())); + MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); } if (config.HasReceivedMaxDatagramFrameSize()) { packet_creator_.SetMaxDatagramFrameSize( @@ -519,6 +631,29 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { } } +void QuicConnection::EnableLegacyVersionEncapsulation( + const std::string& server_name) { + if (perspective_ != Perspective::IS_CLIENT) { + QUIC_BUG << "Cannot enable Legacy Version Encapsulation on the server"; + return; + } + if (legacy_version_encapsulation_enabled_) { + QUIC_BUG << "Do not call EnableLegacyVersionEncapsulation twice"; + return; + } + if (!QuicHostnameUtils::IsValidSNI(server_name)) { + // Legacy Version Encapsulation is only used when SNI is transmitted. + QUIC_DLOG(INFO) + << "Refusing to use Legacy Version Encapsulation with invalid SNI \"" + << server_name << "\""; + return; + } + QUIC_DLOG(INFO) << "Enabling Legacy Version Encapsulation with SNI \"" + << server_name << "\""; + legacy_version_encapsulation_enabled_ = true; + legacy_version_encapsulation_sni_ = server_name; +} + void QuicConnection::ApplyConnectionOptions( const QuicTagVector& connection_options) { sent_packet_manager_.ApplyConnectionOptions(connection_options); @@ -605,6 +740,7 @@ void QuicConnection::OnPublicResetPacket(const QuicPublicResetPacket& packet) { // here. (Check for a bug regression.) DCHECK_EQ(server_connection_id_, packet.connection_id); DCHECK_EQ(perspective_, Perspective::IS_CLIENT); + DCHECK(!VersionHasIetfInvariantHeader(transport_version())); if (debug_visitor_ != nullptr) { debug_visitor_->OnPublicResetPacket(packet); } @@ -718,8 +854,12 @@ void QuicConnection::OnRetryPacket( << server_connection_id_ << " with " << new_connection_id << ", received token " << quiche::QuicheTextUtils::HexEncode(retry_token); - DCHECK(!original_connection_id_.has_value()); - original_connection_id_ = server_connection_id_; + if (!original_destination_connection_id_.has_value()) { + original_destination_connection_id_ = server_connection_id_; + } + DCHECK(!retry_source_connection_id_.has_value()) + << retry_source_connection_id_.value(); + retry_source_connection_id_ = new_connection_id; server_connection_id_ = new_connection_id; packet_creator_.SetServerConnectionId(server_connection_id_); packet_creator_.SetRetryToken(retry_token); @@ -738,11 +878,27 @@ bool QuicConnection::HasIncomingConnectionId(QuicConnectionId connection_id) { return false; } -void QuicConnection::AddIncomingConnectionId(QuicConnectionId connection_id) { - if (HasIncomingConnectionId(connection_id)) { - return; +void QuicConnection::SetOriginalDestinationConnectionId( + const QuicConnectionId& original_destination_connection_id) { + QUIC_DLOG(INFO) << "Setting original_destination_connection_id to " + << original_destination_connection_id + << " on connection with server_connection_id " + << server_connection_id_; + DCHECK_NE(original_destination_connection_id, server_connection_id_); + if (!HasIncomingConnectionId(original_destination_connection_id)) { + incoming_connection_ids_.push_back(original_destination_connection_id); + } + InstallInitialCrypters(original_destination_connection_id); + DCHECK(!original_destination_connection_id_.has_value()) + << original_destination_connection_id_.value(); + original_destination_connection_id_ = original_destination_connection_id; +} + +QuicConnectionId QuicConnection::GetOriginalDestinationConnectionId() { + if (original_destination_connection_id_.has_value()) { + return original_destination_connection_id_.value(); } - incoming_connection_ids_.push_back(connection_id); + return server_connection_id_; } bool QuicConnection::OnUnauthenticatedPublicHeader( @@ -841,6 +997,35 @@ void QuicConnection::OnSuccessfulVersionNegotiation() { } } +void QuicConnection::OnSuccessfulMigrationAfterProbing() { + DCHECK_EQ(perspective_, Perspective::IS_CLIENT); + if (IsPathDegrading()) { + // If path was previously degrading, and migration is successful after + // probing, restart the path degrading and blackhole detection. + OnForwardProgressMade(); + } + // TODO(b/159074035): notify SentPacketManger with RTT sample from probing and + // reset cwnd if this is a successful network migration. +} + +void QuicConnection::OnTransportParametersSent( + const TransportParameters& transport_parameters) const { + if (debug_visitor_ != nullptr) { + debug_visitor_->OnTransportParametersSent(transport_parameters); + } +} + +void QuicConnection::OnTransportParametersReceived( + const TransportParameters& transport_parameters) const { + if (debug_visitor_ != nullptr) { + debug_visitor_->OnTransportParametersReceived(transport_parameters); + } +} + +bool QuicConnection::HasPendingAcks() const { + return ack_alarm_->IsSet(); +} + void QuicConnection::OnDecryptedPacket(EncryptionLevel level) { last_decrypted_packet_level_ = level; last_packet_decrypted_ = true; @@ -849,13 +1034,10 @@ void QuicConnection::OnDecryptedPacket(EncryptionLevel level) { // Address is validated by successfully processing a HANDSHAKE packet. address_validated_ = true; } - if (extend_idle_time_on_decryptable_packets_) { - QUIC_RELOADABLE_FLAG_COUNT(quic_extend_idle_time_on_decryptable_packets); - if (use_idle_network_detector_) { - idle_network_detector_.OnPacketReceived(time_of_last_received_packet_); - } else { - time_of_last_decryptable_packet_ = time_of_last_received_packet_; - } + if (use_idle_network_detector_) { + idle_network_detector_.OnPacketReceived(time_of_last_received_packet_); + } else { + time_of_last_decryptable_packet_ = time_of_last_received_packet_; } visitor_->OnPacketDecrypted(level); @@ -923,6 +1105,9 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { --stats_.packets_dropped; QUIC_DVLOG(1) << ENDPOINT << "Received packet header: " << header; last_header_ = header; + if (!stats_.first_decrypted_packet.IsInitialized()) { + stats_.first_decrypted_packet = last_header_.packet_number; + } // Record packet receipt to populate ack info before processing stream // frames, since the processing may result in sending a bundled ack. @@ -962,14 +1147,9 @@ bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) { ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return false; } - if (advance_ack_timeout_update_) { - MaybeUpdateAckTimeout(); - } + MaybeUpdateAckTimeout(); visitor_->OnStreamFrame(frame); stats_.stream_bytes_received += frame.data_length; - if (!advance_ack_timeout_update_) { - should_last_packet_instigate_acks_ = true; - } consecutive_retransmittable_on_wire_ping_count_ = 0; return connected_; } @@ -984,13 +1164,8 @@ bool QuicConnection::OnCryptoFrame(const QuicCryptoFrame& frame) { if (debug_visitor_ != nullptr) { debug_visitor_->OnCryptoFrame(frame); } - if (advance_ack_timeout_update_) { - MaybeUpdateAckTimeout(); - } + MaybeUpdateAckTimeout(); visitor_->OnCryptoFrame(frame); - if (!advance_ack_timeout_update_) { - should_last_packet_instigate_acks_ = true; - } return connected_; } @@ -1032,11 +1207,6 @@ bool QuicConnection::OnAckFrameStart(QuicPacketNumber largest_acked, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return false; } - - if (!GetLargestAckedPacket().IsInitialized() || - largest_acked > GetLargestAckedPacket()) { - visitor_->OnForwardProgressConfirmed(); - } processing_ack_frame_ = true; sent_packet_manager_.OnAckFrameStart(largest_acked, ack_delay_time, GetTimeOfLastReceivedPacket()); @@ -1177,11 +1347,7 @@ bool QuicConnection::OnPingFrame(const QuicPingFrame& frame) { if (debug_visitor_ != nullptr) { debug_visitor_->OnPingFrame(frame); } - if (advance_ack_timeout_update_) { - MaybeUpdateAckTimeout(); - } else { - should_last_packet_instigate_acks_ = true; - } + MaybeUpdateAckTimeout(); return true; } @@ -1223,13 +1389,8 @@ bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) { << "RST_STREAM_FRAME received for stream: " << frame.stream_id << " with error: " << QuicRstStreamErrorCodeToString(frame.error_code); - if (advance_ack_timeout_update_) { - MaybeUpdateAckTimeout(); - } + MaybeUpdateAckTimeout(); visitor_->OnRstStream(frame); - if (!advance_ack_timeout_update_) { - should_last_packet_instigate_acks_ = true; - } return connected_; } @@ -1260,11 +1421,7 @@ bool QuicConnection::OnPathChallengeFrame(const QuicPathChallengeFrame& frame) { // response. received_path_challenge_payloads_.push_back(frame.data_buffer); - if (advance_ack_timeout_update_) { - MaybeUpdateAckTimeout(); - } else { - should_last_packet_instigate_acks_ = true; - } + MaybeUpdateAckTimeout(); return true; } @@ -1272,11 +1429,7 @@ bool QuicConnection::OnPathResponseFrame(const QuicPathResponseFrame& frame) { if (debug_visitor_ != nullptr) { debug_visitor_->OnPathResponseFrame(frame); } - if (advance_ack_timeout_update_) { - MaybeUpdateAckTimeout(); - } else { - should_last_packet_instigate_acks_ = true; - } + MaybeUpdateAckTimeout(); if (!transmitted_connectivity_probe_payload_ || *transmitted_connectivity_probe_payload_ != frame.data_buffer) { // Is not for the probe we sent, ignore it. @@ -1363,13 +1516,8 @@ bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) { << frame.last_good_stream_id << " and error: " << QuicErrorCodeToString(frame.error_code) << " and reason: " << frame.reason_phrase; - if (advance_ack_timeout_update_) { - MaybeUpdateAckTimeout(); - } + MaybeUpdateAckTimeout(); visitor_->OnGoAway(frame); - if (!advance_ack_timeout_update_) { - should_last_packet_instigate_acks_ = true; - } return connected_; } @@ -1384,13 +1532,8 @@ bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { debug_visitor_->OnWindowUpdateFrame(frame, GetTimeOfLastReceivedPacket()); } QUIC_DVLOG(1) << ENDPOINT << "WINDOW_UPDATE_FRAME received " << frame; - if (advance_ack_timeout_update_) { - MaybeUpdateAckTimeout(); - } + MaybeUpdateAckTimeout(); visitor_->OnWindowUpdateFrame(frame); - if (!advance_ack_timeout_update_) { - should_last_packet_instigate_acks_ = true; - } return connected_; } @@ -1427,19 +1570,20 @@ bool QuicConnection::OnMessageFrame(const QuicMessageFrame& frame) { if (debug_visitor_ != nullptr) { debug_visitor_->OnMessageFrame(frame); } - if (advance_ack_timeout_update_) { - MaybeUpdateAckTimeout(); - } + MaybeUpdateAckTimeout(); visitor_->OnMessageReceived( quiche::QuicheStringPiece(frame.data, frame.message_length)); - if (!advance_ack_timeout_update_) { - should_last_packet_instigate_acks_ = true; - } return connected_; } bool QuicConnection::OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) { - DCHECK(connected_ && VersionHasIetfQuicFrames(transport_version())); + DCHECK(connected_); + if (!support_handshake_done_) { + CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, + "Handshake done frame is unsupported", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } if (perspective_ == Perspective::IS_SERVER) { CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, @@ -1455,16 +1599,17 @@ bool QuicConnection::OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) { if (debug_visitor_ != nullptr) { debug_visitor_->OnHandshakeDoneFrame(frame); } - if (advance_ack_timeout_update_) { - MaybeUpdateAckTimeout(); - } + MaybeUpdateAckTimeout(); visitor_->OnHandshakeDoneReceived(); - if (!advance_ack_timeout_update_) { - should_last_packet_instigate_acks_ = true; - } return connected_; } +bool QuicConnection::OnAckFrequencyFrame( + const QuicAckFrequencyFrame& /*frame*/) { + // TODO(b/148614353): implement this fully. + QUIC_LOG_EVERY_N_SEC(ERROR, 120) << "Get unexpected AckFrequencyFrame."; + return false; +} bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) { DCHECK(connected_); @@ -1477,14 +1622,9 @@ bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) { } QUIC_DLOG(INFO) << ENDPOINT << "BLOCKED_FRAME received for stream: " << frame.stream_id; - if (advance_ack_timeout_update_) { - MaybeUpdateAckTimeout(); - } + MaybeUpdateAckTimeout(); visitor_->OnBlockedFrame(frame); stats_.blocked_frames_received++; - if (!advance_ack_timeout_update_) { - should_last_packet_instigate_acks_ = true; - } return connected_; } @@ -1556,7 +1696,7 @@ void QuicConnection::OnPacketComplete() { // For IETF QUIC, it is guaranteed that TLS will give connection the // corresponding write key before read key. In other words, connection should // never process a packet while an ACK for it cannot be encrypted. - if (!advance_ack_timeout_update_ || !should_last_packet_instigate_acks_) { + if (!should_last_packet_instigate_acks_) { uber_received_packet_manager_.MaybeUpdateAckTimeout( should_last_packet_instigate_acks_, last_decrypted_packet_level_, last_header_.packet_number, GetTimeOfLastReceivedPacket(), @@ -1576,6 +1716,14 @@ void QuicConnection::OnAuthenticatedIetfStatelessResetPacket( const QuicIetfStatelessResetPacket& /*packet*/) { // TODO(fayang): Add OnAuthenticatedIetfStatelessResetPacket to // debug_visitor_. + DCHECK(VersionHasIetfInvariantHeader(transport_version())); + DCHECK_EQ(perspective_, Perspective::IS_CLIENT); + if (!visitor_->ValidateStatelessReset(last_packet_destination_address_, + last_packet_source_address_)) { + // This packet is received on a probing path. Do not close connection. + return; + } + const std::string error_details = "Received stateless reset."; QUIC_CODE_COUNT(quic_tear_down_local_connection_on_stateless_reset); TearDownLocalConnectionState(QUIC_PUBLIC_RESET, error_details, @@ -1693,6 +1841,29 @@ void QuicConnection::SendVersionNegotiationPacket(bool ietf_quic, pending_version_negotiation_packet_ = false; } +void QuicConnection::MaybeActivateLegacyVersionEncapsulation() { + if (!legacy_version_encapsulation_enabled_) { + return; + } + DCHECK(!legacy_version_encapsulation_in_progress_); + QUIC_BUG_IF(!packet_creator_.CanSetMaxPacketLength()) + << "Cannot activate Legacy Version Encapsulation mid-packet"; + QUIC_BUG_IF(coalesced_packet_.length() != 0u) + << "Cannot activate Legacy Version Encapsulation mid-coalesced-packet"; + legacy_version_encapsulation_in_progress_ = true; + MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); +} +void QuicConnection::MaybeDisactivateLegacyVersionEncapsulation() { + if (!legacy_version_encapsulation_in_progress_) { + return; + } + // Flush any remaining packet before disactivating encapsulation. + packet_creator_.FlushCurrentPacket(); + DCHECK(legacy_version_encapsulation_enabled_); + legacy_version_encapsulation_in_progress_ = false; + MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); +} + size_t QuicConnection::SendCryptoData(EncryptionLevel level, size_t write_length, QuicStreamOffset offset) { @@ -1700,11 +1871,21 @@ size_t QuicConnection::SendCryptoData(EncryptionLevel level, QUIC_BUG << "Attempt to send empty crypto frame"; return 0; } - if (!ShouldGeneratePacket(HAS_RETRANSMITTABLE_DATA, IS_HANDSHAKE)) { + if (!GetQuicReloadableFlag(quic_fix_checking_should_generate_packet) && + !ShouldGeneratePacket(HAS_RETRANSMITTABLE_DATA, IS_HANDSHAKE)) { return 0; } - ScopedPacketFlusher flusher(this); - return packet_creator_.ConsumeCryptoData(level, write_length, offset); + if (level == ENCRYPTION_INITIAL) { + MaybeActivateLegacyVersionEncapsulation(); + } + size_t consumed_length; + { + ScopedPacketFlusher flusher(this); + consumed_length = + packet_creator_.ConsumeCryptoData(level, write_length, offset); + } // Added scope ensures packets are flushed before continuing. + MaybeDisactivateLegacyVersionEncapsulation(); + return consumed_length; } QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id, @@ -1716,13 +1897,24 @@ QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id, return QuicConsumedData(0, false); } - // Opportunistically bundle an ack with every outgoing packet. - // Particularly, we want to bundle with handshake packets since we don't know - // which decrypter will be used on an ack packet following a handshake - // packet (a handshake packet from client to server could result in a REJ or a - // SHLO from the server, leading to two different decrypters at the server.) - ScopedPacketFlusher flusher(this); - return packet_creator_.ConsumeData(id, write_length, offset, state); + if (packet_creator_.encryption_level() == ENCRYPTION_INITIAL && + QuicUtils::IsCryptoStreamId(transport_version(), id)) { + MaybeActivateLegacyVersionEncapsulation(); + } + QuicConsumedData consumed_data(0, false); + { + // Opportunistically bundle an ack with every outgoing packet. + // Particularly, we want to bundle with handshake packets since we don't + // know which decrypter will be used on an ack packet following a handshake + // packet (a handshake packet from client to server could result in a REJ or + // a SHLO from the server, leading to two different decrypters at the + // server.) + ScopedPacketFlusher flusher(this); + consumed_data = + packet_creator_.ConsumeData(id, write_length, offset, state); + } // Added scope ensures packets are flushed before continuing. + MaybeDisactivateLegacyVersionEncapsulation(); + return consumed_data; } bool QuicConnection::SendControlFrame(const QuicFrame& frame) { @@ -1734,8 +1926,7 @@ bool QuicConnection::SendControlFrame(const QuicFrame& frame) { // anti-amplification limit is used, client needs to send something to avoid // handshake deadlock. QUIC_DVLOG(1) << ENDPOINT << "Failed to send control frame: " << frame - << " at encryption level: " - << EncryptionLevelToString(encryption_level_); + << " at encryption level: " << encryption_level_; return false; } ScopedPacketFlusher flusher(this); @@ -1795,10 +1986,6 @@ const QuicConnectionStats& QuicConnection::GetStats() { return stats_; } -void QuicConnection::ResetHasNonAppLimitedSampleAfterHandshakeCompletion() { - stats_.has_non_app_limited_sample = false; -} - void QuicConnection::OnCoalescedPacket(const QuicEncryptedPacket& packet) { QueueCoalescedPacket(packet); } @@ -1809,39 +1996,85 @@ void QuicConnection::OnUndecryptablePacket(const QuicEncryptedPacket& packet, QUIC_DVLOG(1) << ENDPOINT << "Received undecryptable packet of length " << packet.length() << " with" << (has_decryption_key ? "" : "out") << " key at level " - << EncryptionLevelToString(decryption_level) + << decryption_level << " while connection is at encryption level " - << EncryptionLevelToString(encryption_level_); + << encryption_level_; DCHECK(EncryptionLevelIsValid(decryption_level)); if (encryption_level_ != ENCRYPTION_FORWARD_SECURE) { ++stats_.undecryptable_packets_received_before_handshake_complete; } - bool should_enqueue = true; + const bool should_enqueue = + ShouldEnqueueUnDecryptablePacket(decryption_level, has_decryption_key); + if (should_enqueue) { + QueueUndecryptablePacket(packet, decryption_level); + } + + if (debug_visitor_ != nullptr) { + debug_visitor_->OnUndecryptablePacket(decryption_level, + /*dropped=*/!should_enqueue); + } +} + +bool QuicConnection::ShouldEnqueueUnDecryptablePacket( + EncryptionLevel decryption_level, + bool has_decryption_key) const { if (encryption_level_ == ENCRYPTION_FORWARD_SECURE) { // We do not expect to install any further keys. - should_enqueue = false; - } else if (undecryptable_packets_.size() >= max_undecryptable_packets_) { + return false; + } + if (undecryptable_packets_.size() >= max_undecryptable_packets_) { // We do not queue more than max_undecryptable_packets_ packets. - should_enqueue = false; - } else if (has_decryption_key) { + return false; + } + if (has_decryption_key) { // We already have the key for this decryption level, therefore no // future keys will allow it be decrypted. - should_enqueue = false; - } else if (version().KnowsWhichDecrypterToUse() && - decryption_level <= encryption_level_) { + return false; + } + if (version().KnowsWhichDecrypterToUse() && + decryption_level <= encryption_level_) { // On versions that know which decrypter to use, we install keys in order // so we will not get newer keys for lower encryption levels. - should_enqueue = false; + return false; } + return true; +} - if (should_enqueue) { - QueueUndecryptablePacket(packet, decryption_level); +std::string QuicConnection::UndecryptablePacketsInfo() const { + std::string info = quiche::QuicheStrCat( + "num_undecryptable_packets: ", undecryptable_packets_.size(), " {"); + for (const auto& packet : undecryptable_packets_) { + info = quiche::QuicheStrCat( + info, "[", EncryptionLevelToString(packet.encryption_level), ", ", + packet.packet->length(), ", ", packet.processed, "]"); + } + info = quiche::QuicheStrCat(info, "}"); + return info; +} + +void QuicConnection::MaybeUpdatePacketCreatorMaxPacketLengthAndPadding() { + QuicByteCount max_packet_length = GetLimitedMaxPacketSize(long_term_mtu_); + if (legacy_version_encapsulation_in_progress_) { + DCHECK(legacy_version_encapsulation_enabled_); + const QuicByteCount minimum_overhead = + QuicLegacyVersionEncapsulator::GetMinimumOverhead( + legacy_version_encapsulation_sni_); + if (max_packet_length < minimum_overhead) { + QUIC_BUG << "Cannot apply Legacy Version Encapsulation overhead because " + << "max_packet_length " << max_packet_length + << " < minimum_overhead " << minimum_overhead; + legacy_version_encapsulation_in_progress_ = false; + legacy_version_encapsulation_enabled_ = false; + MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); + return; + } + max_packet_length -= minimum_overhead; } - - if (debug_visitor_ != nullptr) { - debug_visitor_->OnUndecryptablePacket(decryption_level, - /*dropped=*/!should_enqueue); + packet_creator_.SetMaxPacketLength(max_packet_length); + if (legacy_version_encapsulation_enabled_) { + packet_creator_.set_disable_padding_override( + legacy_version_encapsulation_in_progress_); } } @@ -1899,14 +2132,9 @@ void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address, << " too far from current time:" << clock_->ApproximateNow().ToDebuggingValue(); } - if (!extend_idle_time_on_decryptable_packets_ && use_idle_network_detector_) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_idle_network_detector, 1, 6); - idle_network_detector_.OnPacketReceived(packet.receipt_time()); - } else { - time_of_last_received_packet_ = packet.receipt_time(); - } + time_of_last_received_packet_ = packet.receipt_time(); QUIC_DVLOG(1) << ENDPOINT << "time of last received packet: " - << GetTimeOfLastReceivedPacket().ToDebuggingValue(); + << packet.receipt_time().ToDebuggingValue(); ScopedPacketFlusher flusher(this); if (!framer_.ProcessPacket(packet)) { @@ -2040,6 +2268,9 @@ bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) { QUIC_DLOG(INFO) << ENDPOINT << "Replacing connection ID " << server_connection_id_ << " with " << header.source_connection_id; + if (!original_destination_connection_id_.has_value()) { + original_destination_connection_id_ = server_connection_id_; + } server_connection_id_ = header.source_connection_id; packet_creator_.SetServerConnectionId(server_connection_id_); } @@ -2122,12 +2353,7 @@ void QuicConnection::WriteQueuedPackets() { // TODO(wub): Reduce max packet size to a safe default, or the actual MTU. mtu_discoverer_.Disable(); mtu_discovery_alarm_->Cancel(); - if (GetQuicReloadableFlag( - quic_ignore_msg_too_big_from_buffered_packets)) { - QUIC_RELOADABLE_FLAG_COUNT( - quic_ignore_msg_too_big_from_buffered_packets); - buffered_packets_.pop_front(); - } + buffered_packets_.pop_front(); continue; } if (IsWriteError(result.status)) { @@ -2156,17 +2382,26 @@ void QuicConnection::SendProbingRetransmissions() { } } -void QuicConnection::RetransmitUnackedPackets( - TransmissionType retransmission_type) { - sent_packet_manager_.RetransmitUnackedPackets(retransmission_type); +void QuicConnection::RetransmitZeroRttPackets() { + sent_packet_manager_.RetransmitZeroRttPackets(); - WriteIfNotBlocked(); + if (!GetQuicReloadableFlag( + quic_do_not_retransmit_immediately_on_zero_rtt_reject)) { + WriteIfNotBlocked(); + } } void QuicConnection::NeuterUnencryptedPackets() { sent_packet_manager_.NeuterUnencryptedPackets(); // This may have changed the retransmission timer, so re-arm it. SetRetransmissionAlarm(); + if (default_enable_5rto_blackhole_detection_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_default_enable_5rto_blackhole_detection2, + 1, 3); + // Consider this as forward progress since this is called when initial key + // gets discarded (or previous unencrypted data is not needed anymore). + OnForwardProgressMade(); + } if (SupportsMultiplePacketNumberSpaces()) { // Stop sending ack of initial packet number space. uber_received_packet_manager_.ResetAckStates(ENCRYPTION_INITIAL); @@ -2179,9 +2414,13 @@ void QuicConnection::NeuterUnencryptedPackets() { bool QuicConnection::ShouldGeneratePacket( HasRetransmittableData retransmittable, IsHandshake handshake) { + DCHECK(handshake != IS_HANDSHAKE || + QuicVersionUsesCryptoFrames(transport_version())) + << ENDPOINT + << "Handshake in STREAM frames should not check ShouldGeneratePacket"; // We should serialize handshake packets immediately to ensure that they // end up sent at the right encryption level. - if (handshake == IS_HANDSHAKE) { + if (!move_amplification_limit_ && handshake == IS_HANDSHAKE) { if (LimitedByAmplificationFactor()) { // Server is constrained by the amplification restriction. QUIC_DVLOG(1) << ENDPOINT << "Constrained by amplification restriction"; @@ -2209,9 +2448,8 @@ const QuicFrames QuicConnection::MaybeBundleAckOpportunistically() { QuicFrame updated_ack_frame = GetUpdatedAckFrame(); QUIC_BUG_IF(updated_ack_frame.ack_frame->packets.Empty()) << ENDPOINT << "Attempted to opportunistically bundle an empty " - << EncryptionLevelToString(encryption_level_) << " ACK, " - << (has_pending_ack ? "" : "!") << "has_pending_ack, stop_waiting_count_ " - << stop_waiting_count_; + << encryption_level_ << " ACK, " << (has_pending_ack ? "" : "!") + << "has_pending_ack, stop_waiting_count_ " << stop_waiting_count_; frames.push_back(updated_ack_frame); if (!no_stop_waiting_frames_) { @@ -2227,6 +2465,14 @@ bool QuicConnection::CanWrite(HasRetransmittableData retransmittable) { return false; } + if (move_amplification_limit_ && LimitedByAmplificationFactor()) { + // Server is constrained by the amplification restriction. + QUIC_RELOADABLE_FLAG_COUNT(quic_move_amplification_limit); + QUIC_CODE_COUNT(quic_throttled_by_amplification_limit); + QUIC_DVLOG(1) << ENDPOINT << "Constrained by amplification restriction"; + return false; + } + if (sent_packet_manager_.pending_timer_transmission_count() > 0) { // Force sending the retransmissions for HANDSHAKE, TLP, RTO, PROBING cases. return true; @@ -2305,7 +2551,7 @@ bool QuicConnection::WritePacket(SerializedPacket* packet) { // Termination packets are encrypted and saved, so don't exit early. const bool is_termination_packet = IsTerminationPacket(*packet); QuicPacketNumber packet_number = packet->packet_number; - const QuicPacketLength encrypted_length = packet->encrypted_length; + QuicPacketLength encrypted_length = packet->encrypted_length; // Termination packets are eventually owned by TimeWaitListManager. // Others are deleted at the end of this call. if (is_termination_packet) { @@ -2320,18 +2566,21 @@ bool QuicConnection::WritePacket(SerializedPacket* packet) { } DCHECK_LE(encrypted_length, kMaxOutgoingPacketSize); - if (!is_mtu_discovery) { - DCHECK_LE(encrypted_length, packet_creator_.max_packet_length()); - } + DCHECK(is_mtu_discovery || + encrypted_length <= packet_creator_.max_packet_length()) + << " encrypted_length=" << encrypted_length + << " > packet_creator max_packet_length=" + << packet_creator_.max_packet_length(); QUIC_DVLOG(1) << ENDPOINT << "Sending packet " << packet_number << " : " << (IsRetransmittable(*packet) == HAS_RETRANSMITTABLE_DATA ? "data bearing " : " ack only ") - << ", encryption level: " - << EncryptionLevelToString(packet->encryption_level) + << ", encryption level: " << packet->encryption_level << ", encrypted length:" << encrypted_length - << ", fate: " << SerializedPacketFateToString(fate); - QUIC_DVLOG(2) << ENDPOINT << "packet(" << packet_number << "): " << std::endl + << ", fate: " << fate; + QUIC_DVLOG(2) << ENDPOINT << packet->encryption_level << " packet number " + << packet_number << " of length " << encrypted_length << ": " + << std::endl << quiche::QuicheTextUtils::HexDump(quiche::QuicheStringPiece( packet->encrypted_buffer, encrypted_length)); @@ -2377,6 +2626,14 @@ bool QuicConnection::WritePacket(SerializedPacket* packet) { buffered_packets_.emplace_back(*packet, self_address(), peer_address()); break; case SEND_TO_WRITER: + // At this point, packet->release_encrypted_buffer is either nullptr, + // meaning |packet->encrypted_buffer| is a stack buffer, or not-nullptr, + /// meaning it's a writer-allocated buffer. Note that connectivity probing + // packets do not use this function, so setting release_encrypted_buffer + // to nullptr will not cause probing packets to be leaked. + // + // writer_->WritePacket transfers buffer ownership back to the writer. + packet->release_encrypted_buffer = nullptr; result = writer_->WritePacket(packet->encrypted_buffer, encrypted_length, self_address().host(), peer_address(), per_packet_options_); @@ -2396,6 +2653,48 @@ bool QuicConnection::WritePacket(SerializedPacket* packet) { // write error has been handled. QUIC_BUG_IF(!version().CanSendCoalescedPackets()); return false; + case LEGACY_VERSION_ENCAPSULATE: { + DCHECK(!is_mtu_discovery); + DCHECK_EQ(perspective_, Perspective::IS_CLIENT); + DCHECK_EQ(packet->encryption_level, ENCRYPTION_INITIAL); + DCHECK(legacy_version_encapsulation_enabled_); + DCHECK(legacy_version_encapsulation_in_progress_); + QuicPacketLength encapsulated_length = + QuicLegacyVersionEncapsulator::Encapsulate( + legacy_version_encapsulation_sni_, + quiche::QuicheStringPiece(packet->encrypted_buffer, + packet->encrypted_length), + server_connection_id_, framer_.creation_time(), + GetLimitedMaxPacketSize(long_term_mtu_), + const_cast<char*>(packet->encrypted_buffer)); + if (encapsulated_length != 0) { + stats_.sent_legacy_version_encapsulated_packets++; + packet->encrypted_length = encapsulated_length; + encrypted_length = encapsulated_length; + QUIC_DVLOG(2) + << ENDPOINT + << "Successfully performed Legacy Version Encapsulation on " + << packet->encryption_level << " packet number " << packet_number + << " of length " << encrypted_length << ": " << std::endl + << quiche::QuicheTextUtils::HexDump(quiche::QuicheStringPiece( + packet->encrypted_buffer, encrypted_length)); + } else { + QUIC_BUG << ENDPOINT + << "Failed to perform Legacy Version Encapsulation on " + << packet->encryption_level << " packet number " + << packet_number << " of length " << encrypted_length; + } + if (!buffered_packets_.empty() || HandleWriteBlocked()) { + // Buffer the packet. + buffered_packets_.emplace_back(*packet, self_address(), peer_address()); + } else { // Send the packet to the writer. + // writer_->WritePacket transfers buffer ownership back to the writer. + packet->release_encrypted_buffer = nullptr; + result = writer_->WritePacket(packet->encrypted_buffer, + encrypted_length, self_address().host(), + peer_address(), per_packet_options_); + } + } break; default: DCHECK(false); break; @@ -2499,6 +2798,11 @@ bool QuicConnection::WritePacket(SerializedPacket* packet) { const bool in_flight = sent_packet_manager_.OnPacketSent( packet, packet_send_time, packet->transmission_type, IsRetransmittable(*packet)); + QUIC_BUG_IF(default_enable_5rto_blackhole_detection_ && + blackhole_detector_.IsDetectionInProgress() && + !sent_packet_manager_.HasInFlightPackets()) + << ENDPOINT + << "Trying to start blackhole detection without no bytes in flight"; if (in_flight || !retransmission_alarm_->IsSet()) { SetRetransmissionAlarm(); @@ -2620,11 +2924,11 @@ void QuicConnection::OnWriteError(int error_code) { } } -char* QuicConnection::GetPacketBuffer() { +QuicPacketBuffer QuicConnection::GetPacketBuffer() { if (version().CanSendCoalescedPackets() && !IsHandshakeConfirmed()) { // Do not use writer's packet buffer for coalesced packets which may contain // multiple QUIC packets. - return nullptr; + return {nullptr, nullptr}; } return writer_->GetNextWriteLocation(self_address().host(), peer_address()); } @@ -2700,6 +3004,11 @@ void QuicConnection::OnHandshakeComplete() { sent_packet_manager_.SetHandshakeConfirmed(); // This may have changed the retransmission timer, so re-arm it. SetRetransmissionAlarm(); + if (default_enable_5rto_blackhole_detection_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_default_enable_5rto_blackhole_detection2, + 2, 3); + OnForwardProgressMade(); + } if (!SupportsMultiplePacketNumberSpaces()) { // The client should immediately ack the SHLO to confirm the handshake is // complete with the server. @@ -2753,11 +3062,6 @@ void QuicConnection::SendAck() { visitor_->OnAckNeedsRetransmittableFrame(); } -void QuicConnection::OnPathDegradingTimeout() { - is_path_degrading_ = true; - visitor_->OnPathDegrading(); -} - void QuicConnection::OnRetransmissionTimeout() { #ifndef NDEBUG if (sent_packet_manager_.unacked_packets().empty()) { @@ -2784,6 +3088,13 @@ void QuicConnection::OnRetransmissionTimeout() { debug_visitor_->OnNPacketNumbersSkipped(num_packet_numbers_to_skip); } } + if (default_enable_5rto_blackhole_detection_ && + !sent_packet_manager_.HasInFlightPackets() && + blackhole_detector_.IsDetectionInProgress()) { + // Stop detection in quiescence. + DCHECK_EQ(QuicSentPacketManager::LOSS_MODE, retransmission_mode); + blackhole_detector_.StopDetection(); + } WriteIfNotBlocked(); // A write failure can result in the connection being closed, don't attempt to @@ -2817,8 +3128,9 @@ void QuicConnection::OnRetransmissionTimeout() { if (retransmission_mode == QuicSentPacketManager::PTO_MODE) { sent_packet_manager_.AdjustPendingTimerTransmissions(); } - if (retransmission_mode != QuicSentPacketManager::LOSS_MODE) { - // When timer fires in TLP or RTO mode, ensure 1) at least one packet is + if (retransmission_mode != QuicSentPacketManager::LOSS_MODE && + retransmission_mode != QuicSentPacketManager::HANDSHAKE_MODE) { + // When timer fires in TLP/RTO/PTO mode, ensure 1) at least one packet is // created, or there is data to send and available credit (such that // packets will be sent eventually). QUIC_BUG_IF(packet_creator_.packet_number() == @@ -2859,8 +3171,7 @@ void QuicConnection::SetDiversificationNonce( void QuicConnection::SetDefaultEncryptionLevel(EncryptionLevel level) { QUIC_DVLOG(1) << ENDPOINT << "Setting default encryption level from " - << EncryptionLevelToString(encryption_level_) << " to " - << EncryptionLevelToString(level); + << encryption_level_ << " to " << level; if (level != encryption_level_ && packet_creator_.HasPendingFrames()) { // Flush all queued frames when encryption level changes. ScopedPacketFlusher flusher(this); @@ -2936,27 +3247,79 @@ void QuicConnection::MaybeProcessUndecryptablePackets() { return; } - while (connected_ && !undecryptable_packets_.empty()) { - // Making sure there is no pending frames when processing next undecrypted - // packet because the queued ack frame may change. - packet_creator_.FlushCurrentPacket(); - if (!connected_) { - return; + if (GetQuicReloadableFlag(quic_fix_undecryptable_packets)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_fix_undecryptable_packets); + auto iter = undecryptable_packets_.begin(); + while (connected_ && iter != undecryptable_packets_.end()) { + // Making sure there is no pending frames when processing next undecrypted + // packet because the queued ack frame may change. + packet_creator_.FlushCurrentPacket(); + if (!connected_) { + return; + } + UndecryptablePacket* undecryptable_packet = &*iter; + ++iter; + if (undecryptable_packet->processed) { + continue; + } + QUIC_DVLOG(1) << ENDPOINT << "Attempting to process undecryptable packet"; + if (debug_visitor_ != nullptr) { + debug_visitor_->OnAttemptingToProcessUndecryptablePacket( + undecryptable_packet->encryption_level); + } + if (framer_.ProcessPacket(*undecryptable_packet->packet)) { + QUIC_DVLOG(1) << ENDPOINT << "Processed undecryptable packet!"; + undecryptable_packet->processed = true; + ++stats_.packets_processed; + continue; + } + const bool has_decryption_key = + version().KnowsWhichDecrypterToUse() && + framer_.HasDecrypterOfEncryptionLevel( + undecryptable_packet->encryption_level); + if (framer_.error() == QUIC_DECRYPTION_FAILURE && + ShouldEnqueueUnDecryptablePacket( + undecryptable_packet->encryption_level, has_decryption_key)) { + QUIC_DVLOG(1) + << ENDPOINT + << "Need to attempt to process this undecryptable packet later"; + continue; + } + undecryptable_packet->processed = true; } - QUIC_DVLOG(1) << ENDPOINT << "Attempting to process undecryptable packet"; - const auto& undecryptable_packet = undecryptable_packets_.front(); - if (debug_visitor_ != nullptr) { - debug_visitor_->OnAttemptingToProcessUndecryptablePacket( - undecryptable_packet.encryption_level); + // Remove processed packets. We cannot remove elements in the while loop + // above because currently QuicCircularDeque does not support removing + // mid elements. + while (!undecryptable_packets_.empty()) { + if (!undecryptable_packets_.front().processed) { + break; + } + undecryptable_packets_.pop_front(); } - if (!framer_.ProcessPacket(*undecryptable_packet.packet) && - framer_.error() == QUIC_DECRYPTION_FAILURE) { - QUIC_DVLOG(1) << ENDPOINT << "Unable to process undecryptable packet..."; - break; + } else { + while (connected_ && !undecryptable_packets_.empty()) { + // Making sure there is no pending frames when processing next undecrypted + // packet because the queued ack frame may change. + packet_creator_.FlushCurrentPacket(); + if (!connected_) { + return; + } + QUIC_DVLOG(1) << ENDPOINT << "Attempting to process undecryptable packet"; + const auto& undecryptable_packet = undecryptable_packets_.front(); + if (debug_visitor_ != nullptr) { + debug_visitor_->OnAttemptingToProcessUndecryptablePacket( + undecryptable_packet.encryption_level); + } + if (!framer_.ProcessPacket(*undecryptable_packet.packet) && + framer_.error() == QUIC_DECRYPTION_FAILURE) { + QUIC_DVLOG(1) << ENDPOINT + << "Unable to process undecryptable packet..."; + break; + } + QUIC_DVLOG(1) << ENDPOINT << "Processed undecryptable packet!"; + ++stats_.packets_processed; + undecryptable_packets_.pop_front(); } - QUIC_DVLOG(1) << ENDPOINT << "Processed undecryptable packet!"; - ++stats_.packets_processed; - undecryptable_packets_.pop_front(); } // Once forward secure encryption is in use, there will be no @@ -3074,8 +3437,8 @@ void QuicConnection::SendConnectionClosePacket(QuicErrorCode error, if (!framer_.HasEncrypterOfEncryptionLevel(level)) { continue; } - QUIC_DLOG(INFO) << ENDPOINT << "Sending connection close packet at level: " - << EncryptionLevelToString(level); + QUIC_DLOG(INFO) << ENDPOINT + << "Sending connection close packet at level: " << level; SetDefaultEncryptionLevel(level); // Bundle an ACK of the corresponding packet number space for debugging // purpose. @@ -3156,7 +3519,7 @@ QuicByteCount QuicConnection::max_packet_length() const { void QuicConnection::SetMaxPacketLength(QuicByteCount length) { long_term_mtu_ = length; - packet_creator_.SetMaxPacketLength(GetLimitedMaxPacketSize(length)); + MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); } bool QuicConnection::HasQueuedData() const { @@ -3164,22 +3527,6 @@ bool QuicConnection::HasQueuedData() const { packet_creator_.HasPendingFrames() || !buffered_packets_.empty(); } -bool QuicConnection::CanWriteStreamData() { - // Don't write stream data if there are negotiation or queued data packets - // to send. Otherwise, continue and bundle as many frames as possible. - if (pending_version_negotiation_packet_ || !buffered_packets_.empty()) { - return false; - } - - IsHandshake pending_handshake = - visitor_->HasPendingHandshake() ? IS_HANDSHAKE : NOT_HANDSHAKE; - // Sending queued packets may have caused the socket to become write blocked, - // or the congestion manager to prohibit sending. If we've sent everything - // we had queued and we're still not blocked, let the visitor know it can - // write more. - return ShouldGeneratePacket(HAS_RETRANSMITTABLE_DATA, pending_handshake); -} - void QuicConnection::SetNetworkTimeouts(QuicTime::Delta handshake_timeout, QuicTime::Delta idle_timeout) { QUIC_BUG_IF(idle_timeout > handshake_timeout) @@ -3868,15 +4215,7 @@ void QuicConnection::PostProcessAfterAckFrame(bool send_stop_waiting, // have a better estimate of the current rtt than when it was set. SetRetransmissionAlarm(); if (acked_new_packet) { - is_path_degrading_ = false; - if (sent_packet_manager_.HasInFlightPackets()) { - // Restart detections if forward progress has been made. - blackhole_detector_.RestartDetection(GetPathDegradingDeadline(), - GetNetworkBlackholeDeadline()); - } else { - // Stop detections in quiecense. - blackhole_detector_.StopDetection(); - } + OnForwardProgressMade(); } if (send_stop_waiting) { @@ -3963,7 +4302,7 @@ EncryptionLevel QuicConnection::GetConnectionCloseEncryptionLevel() const { // A forward secure packet has been received. QUIC_BUG_IF(encryption_level_ != ENCRYPTION_FORWARD_SECURE) << ENDPOINT << "Unexpected connection close encryption level " - << EncryptionLevelToString(encryption_level_); + << encryption_level_; return ENCRYPTION_FORWARD_SECURE; } if (framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_ZERO_RTT)) { @@ -3979,25 +4318,68 @@ EncryptionLevel QuicConnection::GetConnectionCloseEncryptionLevel() const { return ENCRYPTION_INITIAL; } +void QuicConnection::MaybeBundleCryptoDataWithAckOfSpace( + PacketNumberSpace space) { + DCHECK(SupportsMultiplePacketNumberSpaces()); + QUIC_BUG_IF(space == APPLICATION_DATA); + const QuicTime ack_timeout = + uber_received_packet_manager_.GetAckTimeout(space); + if (!ack_timeout.IsInitialized() || + (ack_timeout > clock_->ApproximateNow() && + ack_timeout > uber_received_packet_manager_.GetEarliestAckTimeout())) { + // No pending ACK of space. + return; + } + if (coalesced_packet_.length() > 0) { + // Do not bundle CRYPTO data if the ACK could be coalesced with other + // packets. + return; + } + + sent_packet_manager_.RetransmitDataOfSpaceIfAny(space); +} + void QuicConnection::SendAllPendingAcks() { DCHECK(SupportsMultiplePacketNumberSpaces()); QUIC_DVLOG(1) << ENDPOINT << "Trying to send all pending ACKs"; ack_alarm_->Cancel(); + QuicTime earliest_ack_timeout = + uber_received_packet_manager_.GetEarliestAckTimeout(); + QUIC_BUG_IF(!earliest_ack_timeout.IsInitialized()); + if (GetQuicReloadableFlag(quic_bundle_crypto_data_with_initial_ack)) { + // On the server side, sends INITIAL data with INITIAL ACK. On the client + // side, sends HANDSHAKE data (containing client Finished) with HANDSHAKE + // ACK. + PacketNumberSpace space = + perspective() == Perspective::IS_SERVER ? INITIAL_DATA : HANDSHAKE_DATA; + MaybeBundleCryptoDataWithAckOfSpace(space); + earliest_ack_timeout = + uber_received_packet_manager_.GetEarliestAckTimeout(); + if (!earliest_ack_timeout.IsInitialized()) { + QUIC_RELOADABLE_FLAG_COUNT(quic_bundle_crypto_data_with_initial_ack); + return; + } + } // Latches current encryption level. const EncryptionLevel current_encryption_level = encryption_level_; for (int8_t i = INITIAL_DATA; i <= APPLICATION_DATA; ++i) { const QuicTime ack_timeout = uber_received_packet_manager_.GetAckTimeout( static_cast<PacketNumberSpace>(i)); - if (!ack_timeout.IsInitialized() || - ack_timeout > clock_->ApproximateNow()) { + if (!ack_timeout.IsInitialized()) { continue; } - if (!framer_.HasEncrypterOfEncryptionLevel( - QuicUtils::GetEncryptionLevel(static_cast<PacketNumberSpace>(i)))) { - QUIC_BUG << ENDPOINT << "Cannot send ACKs for packet number space " - << PacketNumberSpaceToString(static_cast<PacketNumberSpace>(i)) - << " without corresponding encrypter"; - continue; + if (GetQuicReloadableFlag(quic_always_send_earliest_ack)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_always_send_earliest_ack); + } + if (ack_timeout > clock_->ApproximateNow()) { + if (!GetQuicReloadableFlag(quic_always_send_earliest_ack)) { + continue; + } + if (ack_timeout > earliest_ack_timeout) { + // Always send the earliest ACK to make forward progress in case alarm + // fires early. + continue; + } } QUIC_DVLOG(1) << ENDPOINT << "Sending ACK of packet number space " << PacketNumberSpaceToString( @@ -4011,8 +4393,12 @@ void QuicConnection::SendAllPendingAcks() { const bool flushed = packet_creator_.FlushAckFrame(frames); if (!flushed) { // Connection is write blocked. - QUIC_BUG_IF(!writer_->IsWriteBlocked()) - << "Writer not blocked, but ACK not flushed for packet space:" << i; + QUIC_BUG_IF( + !writer_->IsWriteBlocked() && + (!move_amplification_limit_ || !LimitedByAmplificationFactor())) + << "Writer not blocked and not throttled by amplification factor, " + "but ACK not flushed for packet space:" + << i; break; } ResetAckStates(); @@ -4024,7 +4410,13 @@ void QuicConnection::SendAllPendingAcks() { uber_received_packet_manager_.GetEarliestAckTimeout(); if (timeout.IsInitialized()) { // If there are ACKs pending, re-arm ack alarm. - ack_alarm_->Set(timeout); + if (update_ack_alarm_in_send_all_pending_acks_) { + QUIC_RELOADABLE_FLAG_COUNT( + quic_update_ack_alarm_in_send_all_pending_acks); + ack_alarm_->Update(timeout, kAlarmGranularity); + } else { + ack_alarm_->Set(timeout); + } } // Only try to bundle retransmittable data with ACK frame if default // encryption level is forward secure. @@ -4066,13 +4458,15 @@ bool QuicConnection::FlushCoalescedPacket() { if (coalesced_packet_.length() == 0) { return true; } - QUIC_DVLOG(1) << ENDPOINT << "Sending coalesced packet"; + char buffer[kMaxOutgoingPacketSize]; const size_t length = packet_creator_.SerializeCoalescedPacket( coalesced_packet_, buffer, coalesced_packet_.max_packet_length()); if (length == 0) { return false; } + QUIC_DVLOG(1) << ENDPOINT << "Sending coalesced packet " + << coalesced_packet_.ToString(length); if (!buffered_packets_.empty() || HandleWriteBlocked()) { QUIC_DVLOG(1) << ENDPOINT @@ -4158,6 +4552,26 @@ void QuicConnection::SetLargestReceivedPacketWithAck( } } +void QuicConnection::OnForwardProgressMade() { + if (is_path_degrading_) { + visitor_->OnForwardProgressMadeAfterPathDegrading(); + is_path_degrading_ = false; + } + if (sent_packet_manager_.HasInFlightPackets()) { + // Restart detections if forward progress has been made. + blackhole_detector_.RestartDetection(GetPathDegradingDeadline(), + GetNetworkBlackholeDeadline()); + } else { + // Stop detections in quiecense. + blackhole_detector_.StopDetection(); + } + QUIC_BUG_IF(default_enable_5rto_blackhole_detection_ && + blackhole_detector_.IsDetectionInProgress() && + !sent_packet_manager_.HasInFlightPackets()) + << ENDPOINT + << "Trying to start blackhole detection without no bytes in flight"; +} + QuicPacketNumber QuicConnection::GetLargestReceivedPacketWithAck() const { if (SupportsMultiplePacketNumberSpaces()) { return largest_seen_packets_with_ack_[QuicUtils::GetPacketNumberSpace( @@ -4187,21 +4601,28 @@ bool QuicConnection::EnforceAntiAmplificationLimit() const { bool QuicConnection::LimitedByAmplificationFactor() const { return EnforceAntiAmplificationLimit() && bytes_sent_before_address_validation_ >= - GetQuicFlag(FLAGS_quic_anti_amplification_factor) * + anti_amplification_factor_ * bytes_received_before_address_validation_; } SerializedPacketFate QuicConnection::DeterminePacketFate( bool is_mtu_discovery) { - if (version().CanSendCoalescedPackets() && !IsHandshakeConfirmed() && - !is_mtu_discovery) { - // Before receiving ACK for any 1-RTT packets, always try to coalesce - // packet (except MTU discovery packet). - return COALESCE; + if (legacy_version_encapsulation_in_progress_) { + DCHECK(!is_mtu_discovery); + return LEGACY_VERSION_ENCAPSULATE; } - // Packet cannot be coalesced, flush existing coalesced packet. - if (version().CanSendCoalescedPackets() && !FlushCoalescedPacket()) { - return FAILED_TO_WRITE_COALESCED_PACKET; + if (version().CanSendCoalescedPackets()) { + // Disable coalescing when Legacy Version Encapsulation is in use to avoid + // having to reframe encapsulated packets. + if (!IsHandshakeConfirmed() && !is_mtu_discovery) { + // Before receiving ACK for any 1-RTT packets, always try to coalesce + // packet (except MTU discovery packet). + return COALESCE; + } + // Packet cannot be coalesced, flush existing coalesced packet. + if (!FlushCoalescedPacket()) { + return FAILED_TO_WRITE_COALESCED_PACKET; + } } if (!buffered_packets_.empty() || HandleWriteBlocked()) { return BUFFER; @@ -4269,6 +4690,11 @@ void QuicConnection::OnPathDegradingDetected() { } void QuicConnection::OnBlackholeDetected() { + QUIC_BUG_IF(default_enable_5rto_blackhole_detection_ && + !sent_packet_manager_.HasInFlightPackets()) + << ENDPOINT + << "Closing connection because of blackhole, but there is no bytes in " + "flight"; CloseConnection(QUIC_TOO_MANY_RTOS, "Network blackhole detected.", ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); } @@ -4278,10 +4704,14 @@ void QuicConnection::OnHandshakeTimeout() { DCHECK(use_idle_network_detector_); const QuicTime::Delta duration = clock_->ApproximateNow() - stats_.connection_creation_time; - const std::string error_details = quiche::QuicheStrCat( + std::string error_details = quiche::QuicheStrCat( "Handshake timeout expired after ", duration.ToDebuggingValue(), ". Timeout:", idle_network_detector_.handshake_timeout().ToDebuggingValue()); + if (perspective() == Perspective::IS_CLIENT && version().UsesTls()) { + error_details = + quiche::QuicheStrCat(error_details, UndecryptablePacketsInfo()); + } QUIC_DVLOG(1) << ENDPOINT << error_details; CloseConnection(QUIC_HANDSHAKE_TIMEOUT, error_details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); @@ -4311,7 +4741,6 @@ void QuicConnection::OnIdleNetworkDetected() { } void QuicConnection::MaybeUpdateAckTimeout() { - DCHECK(advance_ack_timeout_update_); if (should_last_packet_instigate_acks_) { return; } @@ -4345,20 +4774,27 @@ QuicTime QuicConnection::GetNetworkBlackholeDeadline() const { if (!ShouldDetectBlackhole()) { return QuicTime::Zero(); } + DCHECK_LT(0u, num_rtos_for_blackhole_detection_); return clock_->ApproximateNow() + - sent_packet_manager_.GetNetworkBlackholeDelay(); + sent_packet_manager_.GetNetworkBlackholeDelay( + num_rtos_for_blackhole_detection_); } bool QuicConnection::ShouldDetectBlackhole() const { - if (!connected_) { + if (!connected_ || blackhole_detection_disabled_) { return false; } // No blackhole detection before handshake completes. + if (default_enable_5rto_blackhole_detection_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_default_enable_5rto_blackhole_detection2, + 3, 3); + return IsHandshakeComplete(); + } + if (!GetHandshakeTimeout().IsInfinite()) { return false; } - return close_connection_after_five_rtos_ || - (sent_packet_manager_.pto_enabled() && max_consecutive_ptos_ > 0); + return num_rtos_for_blackhole_detection_ > 0; } QuicTime::Delta QuicConnection::GetHandshakeTimeout() const { @@ -4372,12 +4808,9 @@ QuicTime QuicConnection::GetTimeOfLastReceivedPacket() const { if (use_idle_network_detector_) { return idle_network_detector_.time_of_last_received_packet(); } - if (extend_idle_time_on_decryptable_packets_) { - DCHECK(time_of_last_decryptable_packet_ == time_of_last_received_packet_ || - !last_packet_decrypted_); - return time_of_last_decryptable_packet_; - } - return time_of_last_received_packet_; + DCHECK(time_of_last_decryptable_packet_ == time_of_last_received_packet_ || + !last_packet_decrypted_); + return time_of_last_decryptable_packet_; } #undef ENDPOINT // undef for jumbo builds 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 6586fb2870a..c0f722dd0c8 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 @@ -26,6 +26,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" +#include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h" #include "net/third_party/quiche/src/quic/core/frames/quic_max_streams_frame.h" #include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters_proto.h" #include "net/third_party/quiche/src/quic/core/quic_alarm.h" @@ -130,6 +131,12 @@ class QUIC_EXPORT_PRIVATE QuicConnectionVisitorInterface { // bandwidth. Returns true if data was sent, false otherwise. virtual bool SendProbingData() = 0; + // Called when stateless reset packet is received. Returns true if the + // connection needs to be closed. + virtual bool ValidateStatelessReset( + const quic::QuicSocketAddress& self_address, + const quic::QuicSocketAddress& peer_address) = 0; + // Called when the connection experiences a change in congestion window. virtual void OnCongestionWindowChange(QuicTime now) = 0; @@ -139,6 +146,9 @@ class QUIC_EXPORT_PRIVATE QuicConnectionVisitorInterface { // Called when the peer seems unreachable over the current path. virtual void OnPathDegrading() = 0; + // Called when forward progress made after path degrading. + virtual void OnForwardProgressMadeAfterPathDegrading() = 0; + // Called when the connection sends ack after // max_consecutive_num_packets_with_no_retransmittable_frames_ consecutive not // retransmittable packets sent. To instigate an ack from peer, a @@ -155,9 +165,6 @@ class QUIC_EXPORT_PRIVATE QuicConnectionVisitorInterface { // or yielded to other connections. virtual bool WillingAndAbleToWrite() const = 0; - // Called to ask if any handshake messages are pending in this visitor. - virtual bool HasPendingHandshake() const = 0; - // Called to ask if the connection should be kept alive and prevented // from timing out, for example if there are outstanding application // transactions expecting a response. @@ -170,10 +177,6 @@ class QUIC_EXPORT_PRIVATE QuicConnectionVisitorInterface { // Called to get current handshake state. virtual HandshakeState GetHandshakeState() const = 0; - // Called when an ACK is received with a larger |largest_acked| than - // previously observed. - virtual void OnForwardProgressConfirmed() = 0; - // Called when a STOP_SENDING frame has been received. virtual void OnStopSendingFrame(const QuicStopSendingFrame& frame) = 0; @@ -341,6 +344,14 @@ class QUIC_EXPORT_PRIVATE QuicConnectionDebugVisitor // Called when |count| packet numbers have been skipped. virtual void OnNPacketNumbersSkipped(QuicPacketCount /*count*/) {} + + // Called for QUIC+TLS versions when we send transport parameters. + virtual void OnTransportParametersSent( + const TransportParameters& /*transport_parameters*/) {} + + // Called for QUIC+TLS versions when we receive transport parameters. + virtual void OnTransportParametersReceived( + const TransportParameters& /*transport_parameters*/) {} }; class QUIC_EXPORT_PRIVATE QuicConnectionHelperInterface { @@ -458,11 +469,6 @@ class QUIC_EXPORT_PRIVATE QuicConnection // Returns statistics tracked for this connection. const QuicConnectionStats& GetStats(); - // Mark stats_.has_non_app_limited_sample as false. - // TODO(b/151166631) Remove this once the proper fix in b/151166631 is rolled - // out. - void ResetHasNonAppLimitedSampleAfterHandshakeCompletion(); - // Processes an incoming UDP packet (consisting of a QuicEncryptedPacket) from // the peer. // In a client, the packet may be "stray" and have a different connection ID @@ -578,6 +584,7 @@ class QUIC_EXPORT_PRIVATE QuicConnection bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override; bool OnMessageFrame(const QuicMessageFrame& frame) override; bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) override; + bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) override; void OnPacketComplete() override; bool IsValidStatelessResetToken(QuicUint128 token) const override; void OnAuthenticatedIetfStatelessResetPacket( @@ -587,7 +594,7 @@ class QUIC_EXPORT_PRIVATE QuicConnection bool ShouldGeneratePacket(HasRetransmittableData retransmittable, IsHandshake handshake) override; const QuicFrames MaybeBundleAckOpportunistically() override; - char* GetPacketBuffer() override; + QuicPacketBuffer GetPacketBuffer() override; void OnSerializedPacket(SerializedPacket packet) override; void OnUnrecoverableError(QuicErrorCode error, const std::string& error_details) override; @@ -669,14 +676,10 @@ class QUIC_EXPORT_PRIVATE QuicConnection return server_supported_versions_; } - // Testing only. + bool HasQueuedPackets() const { return !buffered_packets_.empty(); } + // Testing only. TODO(ianswett): Use a peer instead. size_t NumQueuedPackets() const { return buffered_packets_.size(); } - // Returns true if the underlying UDP socket is writable, there is - // no queued data and the connection is not congestion-control - // blocked. - bool CanWriteStreamData(); - // Returns true if the connection has queued packets or frames. bool HasQueuedData() const; @@ -695,19 +698,13 @@ class QUIC_EXPORT_PRIVATE QuicConnection // Sets up a packet with an QuicAckFrame and sends it out. void SendAck(); - // Called when the path degrading alarm fires. - void OnPathDegradingTimeout(); - // Called when an RTO fires. Resets the retransmission alarm if there are // remaining unacked packets. void OnRetransmissionTimeout(); - // Retransmits all unacked packets with retransmittable frames if - // |retransmission_type| is ALL_UNACKED_PACKETS, otherwise retransmits only - // initially encrypted packets. Used when the negotiated protocol version is - // different from what was initially assumed and when the initial encryption - // changes. - void RetransmitUnackedPackets(TransmissionType retransmission_type); + // Retransmits all sent 0-RTT encrypted packets. Called when new 0-RTT or + // 1-RTT key is available. + void RetransmitZeroRttPackets(); // Calls |sent_packet_manager_|'s NeuterUnencryptedPackets. Used when the // connection becomes forward secure and hasn't received acks for all packets. @@ -942,9 +939,13 @@ class QUIC_EXPORT_PRIVATE QuicConnection // Returns the largest received packet number sent by peer. QuicPacketNumber GetLargestReceivedPacket() const; - // Adds the connection ID to a set of connection IDs that are accepted as - // destination on incoming packets. - void AddIncomingConnectionId(QuicConnectionId connection_id); + // Sets the original destination connection ID on the connection. + // This is called by QuicDispatcher when it has replaced the connection ID. + void SetOriginalDestinationConnectionId( + const QuicConnectionId& original_destination_connection_id); + + // Returns the original destination connection ID used for this connection. + QuicConnectionId GetOriginalDestinationConnectionId(); // Called when ACK alarm goes off. Sends ACKs of those packet number spaces // which have expired ACK timeout. Only used when this connection supports @@ -965,6 +966,30 @@ class QUIC_EXPORT_PRIVATE QuicConnection // Called when version is considered negotiated. void OnSuccessfulVersionNegotiation(); + // Called when self migration succeeds after probing. + void OnSuccessfulMigrationAfterProbing(); + + // Called for QUIC+TLS versions when we send transport parameters. + void OnTransportParametersSent( + const TransportParameters& transport_parameters) const; + + // Called for QUIC+TLS versions when we receive transport parameters. + void OnTransportParametersReceived( + const TransportParameters& transport_parameters) const; + + // Returns true if ack_alarm_ is set. + bool HasPendingAcks() const; + + size_t anti_amplification_factor() const { + return anti_amplification_factor_; + } + + void OnUserAgentIdKnown() { sent_packet_manager_.OnUserAgentIdKnown(); } + + // Enables Legacy Version Encapsulation using |server_name| as SNI. + // Can only be set if this is a client connection. + void EnableLegacyVersionEncapsulation(const std::string& server_name); + protected: // Calls cancel() on all the alarms owned by this connection. void CancelAllAlarms(); @@ -1075,12 +1100,16 @@ class QUIC_EXPORT_PRIVATE QuicConnection struct QUIC_EXPORT_PRIVATE UndecryptablePacket { UndecryptablePacket(const QuicEncryptedPacket& packet, EncryptionLevel encryption_level) - : packet(packet.Clone()), encryption_level(encryption_level) {} + : packet(packet.Clone()), + encryption_level(encryption_level), + processed(false) {} std::unique_ptr<QuicEncryptedPacket> packet; - // Currently, |encryption_level| is only used for logging and does not - // affect processing of the packet. EncryptionLevel encryption_level; + // This gets set to true if 1) connection sucessfully processed the packet + // or 2) connection failed to process the packet and will not try to process + // it later. + bool processed; }; // Notifies the visitor of the close and marks the connection as disconnected. @@ -1237,6 +1266,10 @@ class QUIC_EXPORT_PRIVATE QuicConnection // received packet number which contains an ACK frame. void SetLargestReceivedPacketWithAck(QuicPacketNumber new_value); + // Called when new packets have been acknowledged or old keys have been + // discarded. + void OnForwardProgressMade(); + // Returns largest received packet number which contains an ACK frame. QuicPacketNumber GetLargestReceivedPacketWithAck() const; @@ -1285,6 +1318,30 @@ class QUIC_EXPORT_PRIVATE QuicConnection QuicTime::Delta GetHandshakeTimeout() const; QuicTime GetTimeOfLastReceivedPacket() const; + // Validate connection IDs used during the handshake. Closes the connection + // on validation failure. + bool ValidateConfigConnectionIds(const QuicConfig& config); + bool ValidateConfigConnectionIdsOld(const QuicConfig& config); + + // Called when ACK alarm goes off. Try to bundle crypto data with the ACK of + // |space|. + void MaybeBundleCryptoDataWithAckOfSpace(PacketNumberSpace space); + + // Returns true if an undecryptable packet of |decryption_level| should be + // buffered (such that connection can try to decrypt it later). + bool ShouldEnqueueUnDecryptablePacket(EncryptionLevel decryption_level, + bool has_decryption_key) const; + + // Returns string which contains undecryptable packets information. + std::string UndecryptablePacketsInfo() const; + + // Sets the max packet length on the packet creator if needed. + void MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); + + // Sets internal state to enable or disable Legacy Version Encapsulation. + void MaybeActivateLegacyVersionEncapsulation(); + void MaybeDisactivateLegacyVersionEncapsulation(); + QuicFramer framer_; // Contents received in the current packet, especially used to identify @@ -1394,9 +1451,8 @@ class QUIC_EXPORT_PRIVATE QuicConnection // a connection close packet is sent, but not after. ConnectionCloseBehavior idle_timeout_connection_close_behavior_; - // When true, close the QUIC connection after 5 RTOs. Due to the min rto of - // 200ms, this is over 5 seconds. - bool close_connection_after_five_rtos_; + // When > 0, close the QUIC connection after this number of RTOs. + size_t num_rtos_for_blackhole_detection_; UberReceivedPacketManager uber_received_packet_manager_; @@ -1601,18 +1657,19 @@ class QUIC_EXPORT_PRIVATE QuicConnection // vector to improve performance since it is expected to be very small. std::vector<QuicConnectionId> incoming_connection_ids_; - // When we receive a RETRY packet, we replace |server_connection_id_| with the - // value from the RETRY packet and save off the original value of - // |server_connection_id_| into |original_connection_id_| for validation. - quiche::QuicheOptional<QuicConnectionId> original_connection_id_; + // When we receive a RETRY packet or some INITIAL packets, we replace + // |server_connection_id_| with the value from that packet and save off the + // original value of |server_connection_id_| into + // |original_destination_connection_id_| for validation. + quiche::QuicheOptional<QuicConnectionId> original_destination_connection_id_; + + // After we receive a RETRY packet, |retry_source_connection_id_| contains + // the source connection ID from that packet. + quiche::QuicheOptional<QuicConnectionId> retry_source_connection_id_; // Indicates whether received RETRY packets should be dropped. bool drop_incoming_retry_packets_; - // If max_consecutive_ptos_ > 0, close connection if consecutive PTOs is - // greater than max_consecutive_ptos. - size_t max_consecutive_ptos_; - // Bytes received before address validation. Only used when // EnforceAntiAmplificationLimit returns true. size_t bytes_received_before_address_validation_; @@ -1644,14 +1701,40 @@ class QUIC_EXPORT_PRIVATE QuicConnection QuicIdleNetworkDetector idle_network_detector_; + bool blackhole_detection_disabled_ = false; + + // True if this connection supports handshake done frame. + bool support_handshake_done_; + const bool use_idle_network_detector_ = GetQuicReloadableFlag(quic_use_idle_network_detector); - const bool extend_idle_time_on_decryptable_packets_ = - GetQuicReloadableFlag(quic_extend_idle_time_on_decryptable_packets); - - const bool advance_ack_timeout_update_ = - GetQuicReloadableFlag(quic_advance_ack_timeout_update); + const bool update_ack_alarm_in_send_all_pending_acks_ = + GetQuicReloadableFlag(quic_update_ack_alarm_in_send_all_pending_acks); + + const bool move_amplification_limit_ = + GetQuicReloadableFlag(quic_move_amplification_limit); + + // TODO(fayang): Change the default value of quic_anti_amplification_factor to + // 5 when deprecating quic_move_amplification_limit. + // TODO(b/153892665): Change the default value of + // quic_anti_amplification_factor back to 3 when cert compression is + // supported. + const size_t anti_amplification_factor_ = + move_amplification_limit_ + ? 5 + : GetQuicFlag(FLAGS_quic_anti_amplification_factor); + + const bool default_enable_5rto_blackhole_detection_ = + GetQuicReloadableFlag(quic_default_enable_5rto_blackhole_detection2); + + // Whether the Legacy Version Encapsulation feature is enabled. + bool legacy_version_encapsulation_enabled_ = false; + // Whether we are in the middle of sending a packet using Legacy Version + // Encapsulation. + bool legacy_version_encapsulation_in_progress_ = false; + // SNI to send when using Legacy Version Encapsulation. + std::string legacy_version_encapsulation_sni_; }; } // namespace quic 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 89f7e546d88..191918c9887 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 @@ -53,6 +53,8 @@ std::ostream& operator<<(std::ostream& os, const QuicConnectionStats& s) { os << " num_coalesced_packets_processed: " << s.num_coalesced_packets_processed; os << " num_ack_aggregation_epochs: " << s.num_ack_aggregation_epochs; + os << " sent_legacy_version_encapsulated_packets: " + << s.sent_legacy_version_encapsulated_packets; os << " }"; return os; 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 3b0c85d2228..3579be0f6f4 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 @@ -47,9 +47,21 @@ struct QUIC_EXPORT_PRIVATE QuicConnectionStats { QuicPacketCount packets_lost = 0; QuicPacketCount packet_spuriously_detected_lost = 0; - // The sum of the detection time of all lost packets. The detection time of a - // lost packet is defined as: T(detection) - T(send). - QuicTime::Delta total_loss_detection_time = QuicTime::Delta::Zero(); + // The sum of loss detection response times of all lost packets, in number of + // round trips. + // Given a packet detected as lost: + // T(S) T(1Rtt) T(D) + // |_________________________________|_______| + // Where + // T(S) is the time when the packet is sent. + // T(1Rtt) is one rtt after T(S), using the rtt at the time of detection. + // T(D) is the time of detection, i.e. when the packet is declared as lost. + // The loss detection response time is defined as + // (T(D) - T(S)) / (T(1Rtt) - T(S)) + // + // The average loss detection response time is this number divided by + // |packets_lost|. Smaller result means detection is faster. + float total_loss_detection_response_time = 0.0; // Number of times this connection went through the slow start phase. uint32_t slowstart_count = 0; @@ -90,6 +102,7 @@ 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; QuicBandwidth estimated_bandwidth = QuicBandwidth::Zero(); @@ -104,6 +117,9 @@ struct QUIC_EXPORT_PRIVATE QuicConnectionStats { // Maximum sequence reordering observed from acked packets. QuicPacketCount sent_packets_max_sequence_reordering = 0; + // Number of times that a packet is not detected as lost per reordering_shift, + // but would have been if the reordering_shift increases by one. + QuicPacketCount sent_packets_num_borderline_time_reorderings = 0; // The following stats are used only in TcpCubicSender. // The number of loss events from TCP's perspective. Each loss event includes @@ -136,6 +152,16 @@ struct QUIC_EXPORT_PRIVATE QuicConnectionStats { // Whether there is any non app-limited bandwidth sample. bool has_non_app_limited_sample = false; + + // Packet number of first decrypted packet. + QuicPacketNumber first_decrypted_packet; + + // Max consecutive retransmission timeout before making forward progress. + size_t max_consecutive_rto_with_forward_progress = 0; + + // Number of sent packets that were encapsulated using Legacy Version + // Encapsulation. + QuicPacketCount sent_legacy_version_encapsulated_packets = 0; }; } // namespace quic 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 24f233ef233..7fc45caefb3 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 @@ -18,6 +18,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" #include "net/third_party/quiche/src/quic/core/quic_connection_id.h" +#include "net/third_party/quiche/src/quic/core/quic_constants.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" @@ -28,6 +29,7 @@ #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" #include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" #include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" #include "net/third_party/quiche/src/quic/test_tools/mock_random.h" @@ -48,7 +50,6 @@ using testing::_; using testing::AnyNumber; using testing::AtLeast; using testing::DoAll; -using testing::Exactly; using testing::Ge; using testing::IgnoreResult; using testing::InSequence; @@ -336,6 +337,11 @@ class TestAlarmFactory : public QuicAlarmFactory { }; class TestPacketWriter : public QuicPacketWriter { + struct PacketBuffer { + QUIC_CACHELINE_ALIGNED char buffer[1500]; + bool in_use = false; + }; + public: TestPacketWriter(ParsedQuicVersion version, MockClock* clock) : version_(version), @@ -344,16 +350,40 @@ class TestPacketWriter : public QuicPacketWriter { QuicFramerPeer::SetLastSerializedServerConnectionId(framer_.framer(), TestConnectionId()); framer_.framer()->SetInitialObfuscators(TestConnectionId()); + + for (int i = 0; i < 128; ++i) { + PacketBuffer* p = new PacketBuffer(); + packet_buffer_pool_.push_back(p); + packet_buffer_pool_index_[p->buffer] = p; + packet_buffer_free_list_.push_back(p); + } } TestPacketWriter(const TestPacketWriter&) = delete; TestPacketWriter& operator=(const TestPacketWriter&) = delete; + ~TestPacketWriter() override { + EXPECT_EQ(packet_buffer_pool_.size(), packet_buffer_free_list_.size()) + << packet_buffer_pool_.size() - packet_buffer_free_list_.size() + << " out of " << packet_buffer_pool_.size() + << " packet buffers have been leaked."; + for (auto p : packet_buffer_pool_) { + delete p; + } + } + // QuicPacketWriter interface WriteResult WritePacket(const char* buffer, size_t buf_len, const QuicIpAddress& /*self_address*/, const QuicSocketAddress& /*peer_address*/, PerPacketOptions* /*options*/) override { + // If the buffer is allocated from the pool, return it back to the pool. + // Note the buffer content doesn't change. + if (packet_buffer_pool_index_.find(const_cast<char*>(buffer)) != + packet_buffer_pool_index_.end()) { + FreePacketBuffer(buffer); + } + QuicEncryptedPacket packet(buffer, buf_len); ++packets_write_attempts_; @@ -440,10 +470,15 @@ class TestPacketWriter : public QuicPacketWriter { bool IsBatchMode() const override { return is_batch_mode_; } - char* GetNextWriteLocation( + QuicPacketBuffer GetNextWriteLocation( const QuicIpAddress& /*self_address*/, const QuicSocketAddress& /*peer_address*/) override { - return nullptr; + if (GetQuicReloadableFlag(quic_avoid_leak_writer_buffer)) { + return {AllocPacketBuffer(), + [this](const char* p) { FreePacketBuffer(p); }}; + } + // Do not use writer buffer for serializing packets. + return {nullptr, nullptr}; } WriteResult Flush() override { @@ -591,6 +626,23 @@ class TestPacketWriter : public QuicPacketWriter { SimpleQuicFramer* framer() { return &framer_; } private: + char* AllocPacketBuffer() { + PacketBuffer* p = packet_buffer_free_list_.front(); + EXPECT_FALSE(p->in_use); + p->in_use = true; + packet_buffer_free_list_.pop_front(); + return p->buffer; + } + + void FreePacketBuffer(const char* buffer) { + auto iter = packet_buffer_pool_index_.find(const_cast<char*>(buffer)); + ASSERT_TRUE(iter != packet_buffer_pool_index_.end()); + PacketBuffer* p = iter->second; + ASSERT_TRUE(p->in_use); + p->in_use = false; + packet_buffer_free_list_.push_back(p); + } + ParsedQuicVersion version_; SimpleQuicFramer framer_; size_t last_packet_size_ = 0; @@ -619,6 +671,12 @@ class TestPacketWriter : public QuicPacketWriter { QuicTime::Delta write_pause_time_delta_ = QuicTime::Delta::Zero(); QuicByteCount max_packet_size_ = kMaxOutgoingPacketSize; bool supports_release_time_ = false; + // Used to verify writer-allocated packet buffers are properly released. + std::vector<PacketBuffer*> packet_buffer_pool_; + // Buffer address => Address of the owning PacketBuffer. + QuicHashMap<char*, PacketBuffer*> packet_buffer_pool_index_; + // Indices in packet_buffer_pool_ that are not allocated. + std::list<PacketBuffer*> packet_buffer_free_list_; }; class TestConnection : public QuicConnection { @@ -739,7 +797,7 @@ class TestConnection : public QuicConnection { // Ensures the connection can write stream data before writing. QuicConsumedData EnsureWritableAndSendStreamData5() { - EXPECT_TRUE(CanWriteStreamData()); + EXPECT_TRUE(CanWrite(HAS_RETRANSMITTABLE_DATA)); return SendStreamData5(); } @@ -1091,14 +1149,12 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(AnyNumber()); EXPECT_CALL(visitor_, WillingAndAbleToWrite()).Times(AnyNumber()); EXPECT_CALL(visitor_, OnPacketDecrypted(_)).Times(AnyNumber()); - EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber()); EXPECT_CALL(visitor_, OnCanWrite()) .WillRepeatedly(Invoke(¬ifier_, &SimpleSessionNotifier::OnCanWrite)); EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) .WillRepeatedly(Return(false)); EXPECT_CALL(visitor_, OnCongestionWindowChange(_)).Times(AnyNumber()); EXPECT_CALL(visitor_, OnPacketReceived(_, _, _)).Times(AnyNumber()); - EXPECT_CALL(visitor_, OnForwardProgressConfirmed()).Times(AnyNumber()); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)).Times(AnyNumber()); EXPECT_CALL(visitor_, OnOneRttPacketAcknowledged()) .Times(testing::AtMost(1)); @@ -1317,9 +1373,8 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { level); } - size_t ProcessCryptoPacketAtLevel(uint64_t number, - EncryptionLevel /*level*/) { - QuicPacketHeader header = ConstructPacketHeader(number, ENCRYPTION_INITIAL); + size_t ProcessCryptoPacketAtLevel(uint64_t number, EncryptionLevel level) { + QuicPacketHeader header = ConstructPacketHeader(number, level); QuicFrames frames; if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { frames.push_back(QuicFrame(&crypto_frame_)); @@ -1329,10 +1384,10 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { frames.push_back(QuicFrame(QuicPaddingFrame(-1))); std::unique_ptr<QuicPacket> packet = ConstructPacket(header, frames); char buffer[kMaxOutgoingPacketSize]; - peer_creator_.set_encryption_level(ENCRYPTION_INITIAL); - size_t encrypted_length = peer_framer_.EncryptPayload( - ENCRYPTION_INITIAL, QuicPacketNumber(number), *packet, buffer, - kMaxOutgoingPacketSize); + peer_creator_.set_encryption_level(level); + size_t encrypted_length = + peer_framer_.EncryptPayload(level, QuicPacketNumber(number), *packet, + buffer, kMaxOutgoingPacketSize); connection_.ProcessUdpPacket( kSelfAddress, kPeerAddress, QuicReceivedPacket(buffer, encrypted_length, clock_.Now(), false)); @@ -1694,6 +1749,9 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { void MtuDiscoveryTestInit() { set_perspective(Perspective::IS_SERVER); QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + if (version().SupportsAntiAmplificationLimit()) { + QuicConnectionPeer::SetAddressValidated(&connection_); + } connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); peer_creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); // QuicFramer::GetMaxPlaintextSize uses the smallest max plaintext size @@ -1712,9 +1770,21 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { EXPECT_TRUE(connection_.connected()); } + void PathProbeTestInit(Perspective perspective) { + set_perspective(perspective); + EXPECT_EQ(connection_.perspective(), perspective); + if (perspective == Perspective::IS_SERVER) { + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + } + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + peer_creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); + } + void TestClientRetryHandling(bool invalid_retry_tag, - bool missing_id_in_config, - bool wrong_id_in_config); + bool missing_original_id_in_config, + bool wrong_original_id_in_config, + bool missing_retry_id_in_config, + bool wrong_retry_id_in_config); QuicConnectionId connection_id_; QuicFramer framer_; @@ -1941,6 +2011,9 @@ TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) { set_perspective(Perspective::IS_SERVER); QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); + if (version().SupportsAntiAmplificationLimit()) { + QuicConnectionPeer::SetAddressValidated(&connection_); + } // Clear direct_peer_address. QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); @@ -2022,9 +2095,7 @@ TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) { } TEST_P(QuicConnectionTest, ReceivePathProbeWithNoAddressChangeAtServer) { - set_perspective(Perspective::IS_SERVER); - QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); - EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); + PathProbeTestInit(Perspective::IS_SERVER); // Clear direct_peer_address. QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); @@ -2068,9 +2139,6 @@ TEST_P(QuicConnectionTest, ReceivePathProbeWithNoAddressChangeAtServer) { // Regression test for b/150161358. TEST_P(QuicConnectionTest, BufferedMtuPacketTooBig) { - if (!GetQuicReloadableFlag(quic_ignore_msg_too_big_from_buffered_packets)) { - return; - } EXPECT_CALL(visitor_, OnWriteBlocked()).Times(1); writer_->SetWriteBlocked(); @@ -2138,9 +2206,7 @@ TEST_P(QuicConnectionTest, DiscardQueuedPacketsAfterConnectionClose) { // in IETF version: receive a packet contains PATH CHALLENGE with peer address // change. TEST_P(QuicConnectionTest, ReceivePathProbingAtServer) { - set_perspective(Perspective::IS_SERVER); - QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); - EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); + PathProbeTestInit(Perspective::IS_SERVER); // Clear direct_peer_address. QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); @@ -2278,9 +2344,7 @@ TEST_P(QuicConnectionTest, ReceivePaddedPingWithPortChangeAtServer) { } TEST_P(QuicConnectionTest, ReceiveReorderedPathProbingAtServer) { - set_perspective(Perspective::IS_SERVER); - QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); - EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); + PathProbeTestInit(Perspective::IS_SERVER); // Clear direct_peer_address. QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); @@ -2336,9 +2400,7 @@ TEST_P(QuicConnectionTest, ReceiveReorderedPathProbingAtServer) { } TEST_P(QuicConnectionTest, MigrateAfterProbingAtServer) { - set_perspective(Perspective::IS_SERVER); - QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); - EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); + PathProbeTestInit(Perspective::IS_SERVER); // Clear direct_peer_address. QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); @@ -2393,8 +2455,7 @@ TEST_P(QuicConnectionTest, MigrateAfterProbingAtServer) { TEST_P(QuicConnectionTest, ReceivePaddedPingAtClient) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - set_perspective(Perspective::IS_CLIENT); - EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); + PathProbeTestInit(Perspective::IS_CLIENT); // Clear direct_peer_address. QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); @@ -2442,8 +2503,7 @@ TEST_P(QuicConnectionTest, ReceiveConnectivityProbingResponseAtClient) { return; } EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - set_perspective(Perspective::IS_CLIENT); - EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); + PathProbeTestInit(Perspective::IS_CLIENT); // Clear direct_peer_address. QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); @@ -3129,9 +3189,12 @@ TEST_P(QuicConnectionTest, BasicSending) { if (connection_.SupportsMultiplePacketNumberSpaces()) { return; } + const QuicConnectionStats& stats = connection_.GetStats(); + EXPECT_FALSE(stats.first_decrypted_packet.IsInitialized()); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacket(1); + EXPECT_EQ(QuicPacketNumber(1), stats.first_decrypted_packet); QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 2); QuicPacketNumber last_packet; SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet); // Packet 1 @@ -3215,6 +3278,7 @@ TEST_P(QuicConnectionTest, BasicSending) { } else { EXPECT_EQ(QuicPacketNumber(7u), least_unacked()); } + EXPECT_EQ(QuicPacketNumber(1), stats.first_decrypted_packet); } // QuicConnection should record the packet sent-time prior to sending the @@ -3480,8 +3544,7 @@ TEST_P(QuicConnectionTest, LargeSendWithPendingAck) { // Processs a PING frame. ProcessFramePacket(QuicFrame(QuicPingFrame())); // Ensure that this has caused the ACK alarm to be set. - QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_); - EXPECT_TRUE(ack_alarm->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); // Send data and ensure the ack is bundled. EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(8); @@ -3506,7 +3569,7 @@ TEST_P(QuicConnectionTest, LargeSendWithPendingAck) { writer_->stream_frames()[0]->stream_id); EXPECT_TRUE(writer_->stream_frames()[0]->fin); // Ensure the ack alarm was cancelled when the ack was sent. - EXPECT_FALSE(ack_alarm->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, OnCanWrite) { @@ -4241,6 +4304,12 @@ TEST_P(QuicConnectionTest, TailLossProbeDelayForNonStreamDataInTLPR) { options.push_back(kTLPR); config.SetConnectionOptionsToSend(options); QuicConfigPeer::SetNegotiated(&config, true); + if (connection_.version().AuthenticatesHandshakeConnectionIds()) { + QuicConfigPeer::SetReceivedOriginalConnectionId( + &config, connection_.connection_id()); + QuicConfigPeer::SetReceivedInitialSourceConnectionId( + &config, connection_.connection_id()); + } connection_.SetFromConfig(config); connection_.SetMaxTailLossProbes(1); @@ -4414,47 +4483,6 @@ TEST_P(QuicConnectionTest, RtoWithNoDataToRetransmit) { } } -TEST_P(QuicConnectionTest, RetransmitWithSameEncryptionLevel) { - use_tagging_decrypter(); - - // A TaggingEncrypter puts kTagSize copies of the given byte (0x01 here) at - // the end of the packet. We can test this to check which encrypter was used. - connection_.SetEncrypter(ENCRYPTION_INITIAL, - std::make_unique<TaggingEncrypter>(0x01)); - QuicByteCount packet_size; - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) - .WillOnce(SaveArg<3>(&packet_size)); - connection_.SendCryptoDataWithString("foo", 0); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AnyNumber()); - EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet()); - - connection_.SetEncrypter(ENCRYPTION_ZERO_RTT, - std::make_unique<TaggingEncrypter>(0x02)); - connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); - SendStreamDataToPeer(3, "foo", 0, NO_FIN, nullptr); - EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet()); - - { - InSequence s; - EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, QuicPacketNumber(3), _, _)); - EXPECT_CALL(*send_algorithm_, - OnPacketSent(_, _, QuicPacketNumber(4), _, _)); - } - - // Manually mark both packets for retransmission. - connection_.RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION); - if (!connection_.version().CanSendCoalescedPackets()) { - // Packet should have been sent with ENCRYPTION_INITIAL. - // If connection can send coalesced packet, both retransmissions will be - // coalesced in the same UDP datagram. - EXPECT_EQ(0x01010101u, writer_->final_bytes_of_previous_packet()); - } - - // Packet should have been sent with ENCRYPTION_ZERO_RTT. - EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet()); -} - TEST_P(QuicConnectionTest, SendHandshakeMessages) { use_tagging_decrypter(); // A TaggingEncrypter puts kTagSize copies of the given byte (0x01 here) at @@ -4514,6 +4542,8 @@ TEST_P(QuicConnectionTest, } TEST_P(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) { + SetQuicReloadableFlag(quic_do_not_retransmit_immediately_on_zero_rtt_reject, + true); use_tagging_decrypter(); connection_.SetEncrypter(ENCRYPTION_INITIAL, std::make_unique<TaggingEncrypter>(0x01)); @@ -4526,9 +4556,9 @@ TEST_P(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) { connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); SendStreamDataToPeer(2, "bar", 0, NO_FIN, nullptr); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); - - connection_.RetransmitUnackedPackets(ALL_INITIAL_RETRANSMISSION); + EXPECT_FALSE(notifier_.HasLostStreamData()); + connection_.RetransmitZeroRttPackets(); + EXPECT_TRUE(notifier_.HasLostStreamData()); } TEST_P(QuicConnectionTest, BufferNonDecryptablePackets) { @@ -4739,7 +4769,7 @@ TEST_P(QuicConnectionTest, InitialTimeout) { EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); EXPECT_FALSE(connection_.connected()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); @@ -4794,7 +4824,7 @@ TEST_P(QuicConnectionTest, IdleTimeoutAfterFirstSentPacket) { EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); EXPECT_FALSE(connection_.connected()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); @@ -4838,7 +4868,7 @@ TEST_P(QuicConnectionTest, IdleTimeoutAfterSendTwoPackets) { EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); EXPECT_FALSE(connection_.connected()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); @@ -4885,7 +4915,7 @@ TEST_P(QuicConnectionTest, HandshakeTimeout) { EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); EXPECT_FALSE(connection_.connected()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); @@ -5764,6 +5794,12 @@ TEST_P(QuicConnectionTest, TimeoutAfterSendAfterHandshake) { config.ProcessPeerHello(msg, CLIENT, &error_details); EXPECT_THAT(error, IsQuicNoError()); + if (connection_.version().AuthenticatesHandshakeConnectionIds()) { + QuicConfigPeer::SetReceivedOriginalConnectionId( + &config, connection_.connection_id()); + QuicConfigPeer::SetReceivedInitialSourceConnectionId( + &config, connection_.connection_id()); + } connection_.SetFromConfig(config); const QuicTime::Delta default_idle_timeout = @@ -5910,6 +5946,12 @@ TEST_P(QuicConnectionTest, TimeoutAfterSendSilentCloseWithOpenStreams) { config.ProcessPeerHello(msg, CLIENT, &error_details); EXPECT_THAT(error, IsQuicNoError()); + if (connection_.version().AuthenticatesHandshakeConnectionIds()) { + QuicConfigPeer::SetReceivedOriginalConnectionId( + &config, connection_.connection_id()); + QuicConfigPeer::SetReceivedInitialSourceConnectionId( + &config, connection_.connection_id()); + } connection_.SetFromConfig(config); const QuicTime::Delta default_idle_timeout = @@ -6074,6 +6116,16 @@ TEST_P(QuicConnectionTest, TimeoutAfter5ClientRTOs) { connection_options.push_back(k5RTO); config.SetConnectionOptionsToSend(connection_options); QuicConfigPeer::SetNegotiated(&config, true); + if (GetQuicReloadableFlag(quic_default_enable_5rto_blackhole_detection2)) { + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_COMPLETE)); + } + if (connection_.version().AuthenticatesHandshakeConnectionIds()) { + QuicConfigPeer::SetReceivedOriginalConnectionId( + &config, connection_.connection_id()); + QuicConfigPeer::SetReceivedInitialSourceConnectionId( + &config, connection_.connection_id()); + } connection_.SetFromConfig(config); // Send stream data. @@ -6212,7 +6264,7 @@ TEST_P(QuicConnectionTest, LoopThroughSendingPacketsWithTruncation) { TEST_P(QuicConnectionTest, SendDelayedAck) { QuicTime ack_time = clock_.ApproximateNow() + DefaultDelayedAckTime(); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); const uint8_t tag = 0x07; SetDecrypter(ENCRYPTION_ZERO_RTT, std::make_unique<StrictTaggingDecrypter>(tag)); @@ -6227,7 +6279,7 @@ TEST_P(QuicConnectionTest, SendDelayedAck) { ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); // Check if delayed ack timer is running for the expected interval. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // Simulate delayed ack alarm firing. clock_.AdvanceTime(DefaultDelayedAckTime()); @@ -6242,7 +6294,7 @@ TEST_P(QuicConnectionTest, SendDelayedAck) { EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, SendDelayedAfterQuiescence) { @@ -6251,7 +6303,7 @@ TEST_P(QuicConnectionTest, SendDelayedAfterQuiescence) { // The beginning of the connection counts as quiescence. QuicTime ack_time = clock_.ApproximateNow() + kAlarmGranularity; EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); const uint8_t tag = 0x07; SetDecrypter(ENCRYPTION_ZERO_RTT, std::make_unique<StrictTaggingDecrypter>(tag)); @@ -6266,7 +6318,7 @@ TEST_P(QuicConnectionTest, SendDelayedAfterQuiescence) { ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); // Check if delayed ack timer is running for the expected interval. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // Simulate delayed ack alarm firing. clock_.AdvanceTime(DefaultDelayedAckTime()); @@ -6281,7 +6333,7 @@ TEST_P(QuicConnectionTest, SendDelayedAfterQuiescence) { EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // Process another packet immedately after sending the ack and expect the // ack alarm to be set delayed ack time in the future. @@ -6290,7 +6342,7 @@ TEST_P(QuicConnectionTest, SendDelayedAfterQuiescence) { ProcessDataPacketAtLevel(2, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); // Check if delayed ack timer is running for the expected interval. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // Simulate delayed ack alarm firing. clock_.AdvanceTime(DefaultDelayedAckTime()); @@ -6305,7 +6357,7 @@ TEST_P(QuicConnectionTest, SendDelayedAfterQuiescence) { EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // Wait 1 second and ensure the ack alarm is set to 1ms in the future. clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); @@ -6314,7 +6366,7 @@ TEST_P(QuicConnectionTest, SendDelayedAfterQuiescence) { ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); // Check if delayed ack timer is running for the expected interval. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); } @@ -6331,7 +6383,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimation) { QuicTime ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(kMinRttMs / 4); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); const uint8_t tag = 0x07; SetDecrypter(ENCRYPTION_ZERO_RTT, std::make_unique<StrictTaggingDecrypter>(tag)); @@ -6346,7 +6398,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimation) { EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); } - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used // instead of ENCRYPTION_INITIAL. EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); @@ -6354,12 +6406,12 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimation) { ENCRYPTION_ZERO_RTT); // Check if delayed ack timer is running for the expected interval. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // The 10th received packet causes an ack to be sent. for (int i = 0; i < 9; ++i) { - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); @@ -6374,7 +6426,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimation) { EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, SendDelayedAckAckDecimationAfterQuiescence) { @@ -6391,7 +6443,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckAckDecimationAfterQuiescence) { QuicTime ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); const uint8_t tag = 0x07; SetDecrypter(ENCRYPTION_ZERO_RTT, std::make_unique<StrictTaggingDecrypter>(tag)); @@ -6406,7 +6458,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckAckDecimationAfterQuiescence) { ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); // Check if delayed ack timer is running for the expected interval. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // Simulate delayed ack alarm firing. clock_.AdvanceTime(DefaultDelayedAckTime()); @@ -6421,7 +6473,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckAckDecimationAfterQuiescence) { EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // Process another packet immedately after sending the ack and expect the // ack alarm to be set delayed ack time in the future. @@ -6430,7 +6482,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckAckDecimationAfterQuiescence) { ProcessDataPacketAtLevel(2, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); // Check if delayed ack timer is running for the expected interval. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // Simulate delayed ack alarm firing. clock_.AdvanceTime(DefaultDelayedAckTime()); @@ -6445,7 +6497,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckAckDecimationAfterQuiescence) { EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // Wait 1 second and enesure the ack alarm is set to 1ms in the future. clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); @@ -6454,7 +6506,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckAckDecimationAfterQuiescence) { ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); // Check if delayed ack timer is running for the expected interval. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // Process enough packets to get into ack decimation behavior. @@ -6467,7 +6519,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckAckDecimationAfterQuiescence) { EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(4 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); } - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used // instead of ENCRYPTION_INITIAL. EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); @@ -6475,12 +6527,12 @@ TEST_P(QuicConnectionTest, SendDelayedAckAckDecimationAfterQuiescence) { ENCRYPTION_ZERO_RTT); // Check if delayed ack timer is running for the expected interval. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // The 10th received packet causes an ack to be sent. for (int i = 0; i < 9; ++i) { - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); @@ -6495,7 +6547,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckAckDecimationAfterQuiescence) { EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // Wait 1 second and enesure the ack alarm is set to 1ms in the future. clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); @@ -6505,7 +6557,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckAckDecimationAfterQuiescence) { ENCRYPTION_ZERO_RTT); // Check if delayed ack timer is running for the expected interval. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); } @@ -6529,7 +6581,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationUnlimitedAggregation) { QuicTime ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(kMinRttMs / 4); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); const uint8_t tag = 0x07; SetDecrypter(ENCRYPTION_ZERO_RTT, std::make_unique<StrictTaggingDecrypter>(tag)); @@ -6544,7 +6596,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationUnlimitedAggregation) { EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); } - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used // instead of ENCRYPTION_INITIAL. EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); @@ -6552,19 +6604,19 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationUnlimitedAggregation) { ENCRYPTION_ZERO_RTT); // Check if delayed ack timer is running for the expected interval. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // 18 packets will not cause an ack to be sent. 19 will because when // stop waiting frames are in use, we ack every 20 packets no matter what. for (int i = 0; i < 18; ++i) { - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); } // The delayed ack timer should still be set to the expected deadline. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); } @@ -6582,7 +6634,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationEighthRtt) { QuicTime ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(kMinRttMs / 8); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); const uint8_t tag = 0x07; SetDecrypter(ENCRYPTION_ZERO_RTT, std::make_unique<StrictTaggingDecrypter>(tag)); @@ -6597,7 +6649,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationEighthRtt) { EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); } - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used // instead of ENCRYPTION_INITIAL. EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); @@ -6605,12 +6657,12 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationEighthRtt) { ENCRYPTION_ZERO_RTT); // Check if delayed ack timer is running for the expected interval. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // The 10th received packet causes an ack to be sent. for (int i = 0; i < 9; ++i) { - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); @@ -6625,7 +6677,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationEighthRtt) { EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReordering) { @@ -6641,7 +6693,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReordering) { QuicTime ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(kMinRttMs / 4); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); const uint8_t tag = 0x07; SetDecrypter(ENCRYPTION_ZERO_RTT, std::make_unique<StrictTaggingDecrypter>(tag)); @@ -6656,7 +6708,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReordering) { EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); } - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // Receive one packet out of order and then the rest in order. // The loop leaves a one packet gap between acks sent to simulate some loss. @@ -6666,13 +6718,13 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReordering) { ProcessDataPacketAtLevel(kFirstDecimatedPacket + 9 + (j * 11), !kHasStopWaiting, ENCRYPTION_ZERO_RTT); ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5); - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // The 10th received packet causes an ack to be sent. writer_->Reset(); for (int i = 0; i < 9; ++i) { - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); // The ACK shouldn't be sent until the 10th packet is processed. EXPECT_TRUE(writer_->ack_frames().empty()); @@ -6689,7 +6741,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReordering) { EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } } @@ -6706,7 +6758,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithLargeReordering) { QuicTime ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(kMinRttMs / 4); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); const uint8_t tag = 0x07; SetDecrypter(ENCRYPTION_ZERO_RTT, std::make_unique<StrictTaggingDecrypter>(tag)); @@ -6721,7 +6773,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithLargeReordering) { EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); } - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used // instead of ENCRYPTION_INITIAL. EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); @@ -6729,7 +6781,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithLargeReordering) { ENCRYPTION_ZERO_RTT); // Check if delayed ack timer is running for the expected interval. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // Process packet 10 first and ensure the alarm is one eighth min_rtt. @@ -6737,12 +6789,12 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithLargeReordering) { ProcessDataPacketAtLevel(kFirstDecimatedPacket + 19, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5); - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // The 10th received packet causes an ack to be sent. for (int i = 0; i < 8; ++i) { - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); @@ -6756,11 +6808,11 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithLargeReordering) { EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // The next packet received in order will cause an immediate ack, // because it fills a hole. - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(kFirstDecimatedPacket + 10, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); @@ -6773,7 +6825,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithLargeReordering) { EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReorderingEighthRtt) { @@ -6790,7 +6842,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReorderingEighthRtt) { QuicTime ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(kMinRttMs / 8); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); const uint8_t tag = 0x07; SetDecrypter(ENCRYPTION_ZERO_RTT, std::make_unique<StrictTaggingDecrypter>(tag)); @@ -6805,7 +6857,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReorderingEighthRtt) { EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); } - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used // instead of ENCRYPTION_INITIAL. EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); @@ -6813,7 +6865,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReorderingEighthRtt) { ENCRYPTION_ZERO_RTT); // Check if delayed ack timer is running for the expected interval. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // Process packet 10 first and ensure the alarm is one eighth min_rtt. @@ -6821,12 +6873,12 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReorderingEighthRtt) { ProcessDataPacketAtLevel(kFirstDecimatedPacket + 9, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5); - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // The 10th received packet causes an ack to be sent. for (int i = 0; i < 8; ++i) { - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); @@ -6841,7 +6893,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReorderingEighthRtt) { EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, @@ -6859,7 +6911,7 @@ TEST_P(QuicConnectionTest, QuicTime ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(kMinRttMs / 8); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); const uint8_t tag = 0x07; SetDecrypter(ENCRYPTION_ZERO_RTT, std::make_unique<StrictTaggingDecrypter>(tag)); @@ -6874,7 +6926,7 @@ TEST_P(QuicConnectionTest, EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); } - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used // instead of ENCRYPTION_INITIAL. EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); @@ -6882,7 +6934,7 @@ TEST_P(QuicConnectionTest, ENCRYPTION_ZERO_RTT); // Check if delayed ack timer is running for the expected interval. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // Process packet 10 first and ensure the alarm is one eighth min_rtt. @@ -6890,12 +6942,12 @@ TEST_P(QuicConnectionTest, ProcessDataPacketAtLevel(kFirstDecimatedPacket + 19, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5); - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // The 10th received packet causes an ack to be sent. for (int i = 0; i < 8; ++i) { - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); @@ -6909,11 +6961,11 @@ TEST_P(QuicConnectionTest, EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // The next packet received in order will cause an immediate ack, // because it fills a hole. - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); ProcessDataPacketAtLevel(kFirstDecimatedPacket + 10, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); @@ -6926,27 +6978,27 @@ TEST_P(QuicConnectionTest, EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, SendDelayedAckOnHandshakeConfirmed) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); ProcessPacket(1); // Check that ack is sent and that delayed ack alarm is set. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); QuicTime ack_time = clock_.ApproximateNow() + DefaultDelayedAckTime(); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // Completing the handshake as the server does nothing. QuicConnectionPeer::SetPerspective(&connection_, Perspective::IS_SERVER); connection_.OnHandshakeComplete(); - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline()); // Complete the handshake as the client decreases the delayed ack time to 0ms. QuicConnectionPeer::SetPerspective(&connection_, Perspective::IS_CLIENT); connection_.OnHandshakeComplete(); - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); if (connection_.SupportsMultiplePacketNumberSpaces()) { EXPECT_EQ(clock_.ApproximateNow() + DefaultDelayedAckTime(), connection_.GetAckAlarm()->deadline()); @@ -6969,7 +7021,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckOnSecondPacket) { EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, NoAckOnOldNacks) { @@ -7001,7 +7053,7 @@ TEST_P(QuicConnectionTest, NoAckOnOldNacks) { ProcessPacket(6); padding_frame_count = writer_->padding_frames().size(); EXPECT_EQ(padding_frame_count, writer_->frame_count()); - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingPacket) { @@ -7025,7 +7077,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingPacket) { EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingCryptoPacket) { @@ -7045,7 +7097,7 @@ TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingCryptoPacket) { EXPECT_EQ(4u, writer_->frame_count()); EXPECT_FALSE(writer_->stop_waiting_frames().empty()); } - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, BlockAndBufferOnFirstCHLOPacketOfTwo) { @@ -7053,17 +7105,29 @@ TEST_P(QuicConnectionTest, BlockAndBufferOnFirstCHLOPacketOfTwo) { ProcessPacket(1); BlockOnNextWrite(); writer_->set_is_write_blocked_data_buffered(true); + if (GetQuicReloadableFlag(quic_move_amplification_limit) && + QuicVersionUsesCryptoFrames(connection_.transport_version())) { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + } else { + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); + } connection_.SendCryptoDataWithString("foo", 0); EXPECT_TRUE(writer_->IsWriteBlocked()); EXPECT_FALSE(connection_.HasQueuedData()); connection_.SendCryptoDataWithString("bar", 3); EXPECT_TRUE(writer_->IsWriteBlocked()); - EXPECT_TRUE(connection_.HasQueuedData()); + if (GetQuicReloadableFlag(quic_move_amplification_limit) && + QuicVersionUsesCryptoFrames(connection_.transport_version())) { + // CRYPTO frames are not flushed when writer is blocked. + EXPECT_FALSE(connection_.HasQueuedData()); + } else { + EXPECT_TRUE(connection_.HasQueuedData()); + } } TEST_P(QuicConnectionTest, BundleAckForSecondCHLO) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); EXPECT_CALL(visitor_, OnCanWrite()) .WillOnce(IgnoreResult(InvokeWithoutArgs( &connection_, &TestConnection::SendCryptoStreamData))); @@ -7092,12 +7156,12 @@ TEST_P(QuicConnectionTest, BundleAckForSecondCHLO) { EXPECT_EQ(1u, writer_->padding_frames().size()); ASSERT_FALSE(writer_->ack_frames().empty()); EXPECT_EQ(QuicPacketNumber(2u), LargestAcked(writer_->ack_frames().front())); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, BundleAckForSecondCHLOTwoPacketReject) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // Process two packets from the crypto stream, which is frame1_'s default, // simulating a 2 packet reject. @@ -7136,7 +7200,7 @@ TEST_P(QuicConnectionTest, BundleAckForSecondCHLOTwoPacketReject) { EXPECT_EQ(1u, writer_->padding_frames().size()); ASSERT_FALSE(writer_->ack_frames().empty()); EXPECT_EQ(QuicPacketNumber(2u), LargestAcked(writer_->ack_frames().front())); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, BundleAckWithDataOnIncomingAck) { @@ -7171,7 +7235,7 @@ TEST_P(QuicConnectionTest, BundleAckWithDataOnIncomingAck) { // Check that no packet is sent and the ack alarm isn't set. EXPECT_EQ(0u, writer_->frame_count()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); writer_->Reset(); // Send the same ack, but send both data and an ack together. @@ -7199,7 +7263,7 @@ TEST_P(QuicConnectionTest, BundleAckWithDataOnIncomingAck) { LargestAcked(writer_->ack_frames().front())); } EXPECT_EQ(1u, writer_->stream_frames().size()); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, NoAckSentForClose) { @@ -7221,7 +7285,7 @@ TEST_P(QuicConnectionTest, SendWhenDisconnected) { connection_.CloseConnection(QUIC_PEER_GOING_AWAY, "no reason", ConnectionCloseBehavior::SILENT_CLOSE); EXPECT_FALSE(connection_.connected()); - EXPECT_FALSE(connection_.CanWriteStreamData()); + EXPECT_FALSE(connection_.CanWrite(HAS_RETRANSMITTABLE_DATA)); std::unique_ptr<QuicPacket> packet = ConstructDataPacket(1, !kHasStopWaiting, ENCRYPTION_INITIAL); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _)) @@ -7245,7 +7309,7 @@ TEST_P(QuicConnectionTest, SendConnectivityProbingWhenDisconnected) { connection_.CloseConnection(QUIC_PEER_GOING_AWAY, "no reason", ConnectionCloseBehavior::SILENT_CLOSE); EXPECT_FALSE(connection_.connected()); - EXPECT_FALSE(connection_.CanWriteStreamData()); + EXPECT_FALSE(connection_.CanWrite(HAS_RETRANSMITTABLE_DATA)); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _)) .Times(0); @@ -7260,7 +7324,7 @@ TEST_P(QuicConnectionTest, SendConnectivityProbingWhenDisconnected) { } TEST_P(QuicConnectionTest, WriteBlockedAfterClientSendsConnectivityProbe) { - EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); + PathProbeTestInit(Perspective::IS_CLIENT); TestPacketWriter probing_writer(version(), &clock_); // Block next write so that sending connectivity probe will encounter a // blocked write when send a connectivity probe to the peer. @@ -7276,8 +7340,7 @@ TEST_P(QuicConnectionTest, WriteBlockedAfterClientSendsConnectivityProbe) { } TEST_P(QuicConnectionTest, WriterBlockedAfterServerSendsConnectivityProbe) { - set_perspective(Perspective::IS_SERVER); - QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + PathProbeTestInit(Perspective::IS_SERVER); // Block next write so that sending connectivity probe will encounter a // blocked write when send a connectivity probe to the peer. @@ -7293,7 +7356,7 @@ TEST_P(QuicConnectionTest, WriterBlockedAfterServerSendsConnectivityProbe) { } TEST_P(QuicConnectionTest, WriterErrorWhenClientSendsConnectivityProbe) { - EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); + PathProbeTestInit(Perspective::IS_CLIENT); TestPacketWriter probing_writer(version(), &clock_); probing_writer.SetShouldWriteFail(); @@ -7308,8 +7371,7 @@ TEST_P(QuicConnectionTest, WriterErrorWhenClientSendsConnectivityProbe) { } TEST_P(QuicConnectionTest, WriterErrorWhenServerSendsConnectivityProbe) { - set_perspective(Perspective::IS_SERVER); - QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + PathProbeTestInit(Perspective::IS_SERVER); writer_->SetShouldWriteFail(); // Connection should not be closed if a connectivity probe is failed to be @@ -7356,6 +7418,7 @@ TEST_P(QuicConnectionTest, IetfStatelessReset) { kTestStatelessResetToken)); std::unique_ptr<QuicReceivedPacket> received( ConstructReceivedPacket(*packet, QuicTime::Zero())); + EXPECT_CALL(visitor_, ValidateStatelessReset(_, _)).WillOnce(Return(true)); EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_PEER)) .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame)); connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *received); @@ -7629,6 +7692,7 @@ TEST_P(QuicConnectionTest, ConnectionCloseWhenWriteBlocked) { } TEST_P(QuicConnectionTest, OnPacketSentDebugVisitor) { + PathProbeTestInit(Perspective::IS_CLIENT); MockQuicConnectionDebugVisitor debug_visitor; connection_.set_debug_visitor(&debug_visitor); @@ -7681,8 +7745,7 @@ TEST_P(QuicConnectionTest, WindowUpdateInstigateAcks) { ProcessFramePacket(QuicFrame(window_update)); // Ensure that this has caused the ACK alarm to be set. - QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_); - EXPECT_TRUE(ack_alarm->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, BlockedFrameInstigateAcks) { @@ -7695,8 +7758,7 @@ TEST_P(QuicConnectionTest, BlockedFrameInstigateAcks) { ProcessFramePacket(QuicFrame(blocked)); // Ensure that this has caused the ACK alarm to be set. - QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_); - EXPECT_TRUE(ack_alarm->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, ReevaluateTimeUntilSendOnAck) { @@ -7838,32 +7900,8 @@ TEST_P(QuicConnectionTest, SetRetransmissionAlarmForCryptoPacket) { connection_.GetRetransmissionAlarm()->Fire(); } -TEST_P(QuicConnectionTest, PathDegradingAlarmForCryptoPacket) { - EXPECT_TRUE(connection_.connected()); - EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); - EXPECT_FALSE(connection_.IsPathDegrading()); - - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); - connection_.SendCryptoStreamData(); - - EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); - EXPECT_FALSE(connection_.IsPathDegrading()); - QuicTime::Delta delay = QuicConnectionPeer::GetSentPacketManager(&connection_) - ->GetPathDegradingDelay(); - EXPECT_EQ(delay, connection_.GetBlackholeDetectorAlarm()->deadline() - - clock_.ApproximateNow()); - - // Fire the path degrading alarm, path degrading signal should be sent to - // the visitor. - EXPECT_CALL(visitor_, OnPathDegrading()); - clock_.AdvanceTime(delay); - connection_.PathDegradingTimeout(); - EXPECT_TRUE(connection_.IsPathDegrading()); - EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); -} - // Includes regression test for b/69979024. -TEST_P(QuicConnectionTest, PathDegradingAlarmForNonCryptoPackets) { +TEST_P(QuicConnectionTest, PathDegradingDetectionForNonCryptoPackets) { EXPECT_TRUE(connection_.connected()); EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); EXPECT_FALSE(connection_.IsPathDegrading()); @@ -7874,21 +7912,21 @@ TEST_P(QuicConnectionTest, PathDegradingAlarmForNonCryptoPackets) { for (int i = 0; i < 2; ++i) { // Send a packet. Now there's a retransmittable packet on the wire, so the - // path degrading alarm should be set. + // path degrading detection should be set. connection_.SendStreamDataWithString( GetNthClientInitiatedStreamId(1, connection_.transport_version()), data, offset, NO_FIN); offset += data_size; EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); - // Check the deadline of the path degrading alarm. + // Check the deadline of the path degrading detection. QuicTime::Delta delay = QuicConnectionPeer::GetSentPacketManager(&connection_) ->GetPathDegradingDelay(); EXPECT_EQ(delay, connection_.GetBlackholeDetectorAlarm()->deadline() - clock_.ApproximateNow()); - // Send a second packet. The path degrading alarm's deadline should remain - // the same. + // Send a second packet. The path degrading detection's deadline should + // remain the same. // Regression test for b/69979024. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); QuicTime prev_deadline = @@ -7902,7 +7940,7 @@ TEST_P(QuicConnectionTest, PathDegradingAlarmForNonCryptoPackets) { connection_.GetBlackholeDetectorAlarm()->deadline()); // Now receive an ACK of the first packet. This should advance the path - // degrading alarm's deadline since forward progress has been made. + // degrading detection's deadline since forward progress has been made. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); if (i == 0) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); @@ -7912,7 +7950,7 @@ TEST_P(QuicConnectionTest, PathDegradingAlarmForNonCryptoPackets) { {{QuicPacketNumber(1u + 2u * i), QuicPacketNumber(2u + 2u * i)}}); ProcessAckPacket(&frame); EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); - // Check the deadline of the path degrading alarm. + // Check the deadline of the path degrading detection. delay = QuicConnectionPeer::GetSentPacketManager(&connection_) ->GetPathDegradingDelay(); EXPECT_EQ(delay, connection_.GetBlackholeDetectorAlarm()->deadline() - @@ -7921,7 +7959,7 @@ TEST_P(QuicConnectionTest, PathDegradingAlarmForNonCryptoPackets) { if (i == 0) { // Now receive an ACK of the second packet. Since there are no more // retransmittable packets on the wire, this should cancel the path - // degrading alarm. + // degrading detection. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}}); @@ -8010,7 +8048,7 @@ TEST_P(QuicConnectionTest, RetransmittableOnWireSetsPingAlarm) { // This test verifies that the connection marks path as degrading and does not // spin timer to detect path degrading when a new packet is sent on the // degraded path. -TEST_P(QuicConnectionTest, NoPathDegradingAlarmIfPathIsDegrading) { +TEST_P(QuicConnectionTest, NoPathDegradingDetectionIfPathIsDegrading) { EXPECT_TRUE(connection_.connected()); EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); EXPECT_FALSE(connection_.IsPathDegrading()); @@ -8024,13 +8062,13 @@ TEST_P(QuicConnectionTest, NoPathDegradingAlarmIfPathIsDegrading) { connection_.SendStreamDataWithString(1, data, offset, NO_FIN); offset += data_size; EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); - // Check the deadline of the path degrading alarm. + // Check the deadline of the path degrading detection. QuicTime::Delta delay = QuicConnectionPeer::GetSentPacketManager(&connection_) ->GetPathDegradingDelay(); EXPECT_EQ(delay, connection_.GetBlackholeDetectorAlarm()->deadline() - clock_.ApproximateNow()); - // Send a second packet. The path degrading alarm's deadline should remain + // Send a second packet. The path degrading detection's deadline should remain // the same. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); QuicTime prev_deadline = connection_.GetBlackholeDetectorAlarm()->deadline(); @@ -8040,7 +8078,7 @@ TEST_P(QuicConnectionTest, NoPathDegradingAlarmIfPathIsDegrading) { EXPECT_EQ(prev_deadline, connection_.GetBlackholeDetectorAlarm()->deadline()); // Now receive an ACK of the first packet. This should advance the path - // degrading alarm's deadline since forward progress has been made. + // degrading detection's deadline since forward progress has been made. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); @@ -8054,8 +8092,8 @@ TEST_P(QuicConnectionTest, NoPathDegradingAlarmIfPathIsDegrading) { EXPECT_EQ(delay, connection_.GetBlackholeDetectorAlarm()->deadline() - clock_.ApproximateNow()); - // Advance time to the path degrading alarm's deadline and simulate - // firing the path degrading alarm. This path will be considered as + // Advance time to the path degrading detection's deadline and simulate + // firing the path degrading detection. This path will be considered as // degrading. clock_.AdvanceTime(delay); EXPECT_CALL(visitor_, OnPathDegrading()).Times(1); @@ -8065,7 +8103,7 @@ TEST_P(QuicConnectionTest, NoPathDegradingAlarmIfPathIsDegrading) { clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); - // Send a third packet. The path degrading alarm is no longer set but path + // Send a third packet. The path degrading detection is no longer set but path // should still be marked as degrading. connection_.SendStreamDataWithString(1, data, offset, NO_FIN); offset += data_size; @@ -8141,6 +8179,7 @@ TEST_P(QuicConnectionTest, UnmarkPathDegradingOnForwardProgress) { // degrading. And will set a timer to detect new path degrading. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + EXPECT_CALL(visitor_, OnForwardProgressMadeAfterPathDegrading()).Times(1); frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}}); ProcessAckPacket(&frame); EXPECT_FALSE(connection_.IsPathDegrading()); @@ -8382,8 +8421,7 @@ TEST_P(QuicConnectionTest, DoNotForceSendingAckOnPacketTooLarge) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); // Send an ack by simulating delayed ack alarm firing. ProcessPacket(1); - QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_); - EXPECT_TRUE(ack_alarm->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); connection_.GetAckAlarm()->Fire(); // Simulate data packet causes write error. EXPECT_CALL(visitor_, OnConnectionClosed(_, _)); @@ -8973,45 +9011,6 @@ TEST_P(QuicConnectionTest, ResetBackOffRetransmitableOnWireTimeout) { connection_.GetPingAlarm()->deadline() - clock_.ApproximateNow()); } -TEST_P(QuicConnectionTest, OnForwardProgressConfirmed) { - EXPECT_CALL(visitor_, OnForwardProgressConfirmed()).Times(Exactly(0)); - EXPECT_TRUE(connection_.connected()); - - const char data[] = "data"; - size_t data_size = strlen(data); - QuicStreamOffset offset = 0; - - // Send two packets. - connection_.SendStreamDataWithString(1, data, offset, NO_FIN); - offset += data_size; - connection_.SendStreamDataWithString(1, data, offset, NO_FIN); - offset += data_size; - - // Ack packet 1. This increases the largest_acked to 1, so - // OnForwardProgressConfirmed() should be called - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); - EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); - EXPECT_CALL(visitor_, OnForwardProgressConfirmed()); - QuicAckFrame frame = - InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}}); - ProcessAckPacket(&frame); - - // Ack packet 1 again. largest_acked remains at 1, so - // OnForwardProgressConfirmed() should not be called. - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); - frame = InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}}); - ProcessAckPacket(&frame); - - // Ack packet 2. This increases the largest_acked to 2, so - // OnForwardProgressConfirmed() should be called. - clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); - EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); - EXPECT_CALL(visitor_, OnForwardProgressConfirmed()); - frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}}); - ProcessAckPacket(&frame); -} - TEST_P(QuicConnectionTest, ValidStatelessResetToken) { const QuicUint128 kTestToken = 1010101; const QuicUint128 kWrongTestToken = 1010100; @@ -9231,6 +9230,102 @@ TEST_P(QuicConnectionTest, PathChallengeResponse) { sizeof(challenge_data))); } +TEST_P(QuicConnectionTest, + RestartPathDegradingDetectionAfterMigrationWithProbe) { + // TODO(b/150095484): add test coverage for IETF to verify that client takes + // PATH RESPONSE with peer address change as correct validation on the new + // path. + if (GetParam().version.HasIetfQuicFrames()) { + return; + } + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + PathProbeTestInit(Perspective::IS_CLIENT); + + // Clear direct_peer_address and effective_peer_address. + QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); + QuicConnectionPeer::SetEffectivePeerAddress(&connection_, + QuicSocketAddress()); + EXPECT_FALSE(connection_.effective_peer_address().IsInitialized()); + + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) + .WillRepeatedly(Return(true)); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); + EXPECT_FALSE(connection_.IsPathDegrading()); + EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); + + if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { + EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); + } else { + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + } + ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, + kPeerAddress); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + + // Send data and verify the path degrading detection is set. + const char data[] = "data"; + size_t data_size = strlen(data); + QuicStreamOffset offset = 0; + connection_.SendStreamDataWithString(1, data, offset, NO_FIN); + offset += data_size; + + // Verify the path degrading detection is in progress. + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); + EXPECT_FALSE(connection_.IsPathDegrading()); + QuicTime ddl = connection_.GetBlackholeDetectorAlarm()->deadline(); + + // Simulate the firing of path degrading. + clock_.AdvanceTime(ddl - clock_.ApproximateNow()); + EXPECT_CALL(visitor_, OnPathDegrading()).Times(1); + connection_.PathDegradingTimeout(); + EXPECT_TRUE(connection_.IsPathDegrading()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); + + // Simulate path degrading handling by sending a probe on an alternet path. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + TestPacketWriter probing_writer(version(), &clock_); + connection_.SendConnectivityProbingPacket(&probing_writer, + connection_.peer_address()); + // Verify that path degrading detection is not reset. + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); + + // Simulate successful path degrading handling by receiving probe response. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20)); + + if (!GetParam().version.HasIetfQuicFrames()) { + EXPECT_CALL(visitor_, + OnPacketReceived(_, _, /*is_connectivity_probe=*/true)) + .Times(1); + } else { + EXPECT_CALL(visitor_, OnPacketReceived(_, _, _)).Times(0); + } + const QuicSocketAddress kNewSelfAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); + + std::unique_ptr<SerializedPacket> probing_packet = ConstructProbingPacket(); + std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( + QuicEncryptedPacket(probing_packet->encrypted_buffer, + probing_packet->encrypted_length), + clock_.Now())); + uint64_t num_probing_received = + connection_.GetStats().num_connectivity_probing_received; + ProcessReceivedPacket(kNewSelfAddress, kPeerAddress, *received); + + EXPECT_EQ(num_probing_received + 1, + connection_.GetStats().num_connectivity_probing_received); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + EXPECT_TRUE(connection_.IsPathDegrading()); + + // Verify new path degrading detection is activated. + EXPECT_CALL(visitor_, OnForwardProgressMadeAfterPathDegrading()).Times(1); + connection_.OnSuccessfulMigrationAfterProbing(); + EXPECT_FALSE(connection_.IsPathDegrading()); + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); +} + // Regression test for b/110259444 TEST_P(QuicConnectionTest, DoNotScheduleSpuriousAckAlarm) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); @@ -9238,9 +9333,8 @@ TEST_P(QuicConnectionTest, DoNotScheduleSpuriousAckAlarm) { writer_->SetWriteBlocked(); ProcessPacket(1); - QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_); // Verify ack alarm is set. - EXPECT_TRUE(ack_alarm->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); // Fire the ack alarm, verify no packet is sent because the writer is blocked. EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); connection_.GetAckAlarm()->Fire(); @@ -9249,7 +9343,7 @@ TEST_P(QuicConnectionTest, DoNotScheduleSpuriousAckAlarm) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); ProcessPacket(2); // Verify ack alarm is not set. - EXPECT_FALSE(ack_alarm->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, DisablePacingOffloadConnectionOptions) { @@ -9428,7 +9522,7 @@ TEST_P(QuicConnectionTest, MultiplePacketNumberSpacesBasicReceiving) { use_tagging_decrypter(); // Receives packet 1000 in initial data. ProcessCryptoPacketAtLevel(1000, ENCRYPTION_INITIAL); - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, std::make_unique<TaggingEncrypter>(0x02)); SetDecrypter(ENCRYPTION_ZERO_RTT, @@ -9437,13 +9531,13 @@ TEST_P(QuicConnectionTest, MultiplePacketNumberSpacesBasicReceiving) { std::make_unique<TaggingEncrypter>(0x02)); // Receives packet 1000 in application data. ProcessDataPacketAtLevel(1000, false, ENCRYPTION_ZERO_RTT); - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); connection_.SendApplicationDataAtLevel(ENCRYPTION_ZERO_RTT, 5, "data", 0, NO_FIN); // Verify application data ACK gets bundled with outgoing data. EXPECT_EQ(2u, writer_->frame_count()); // Make sure ACK alarm is still set because initial data is not ACKed. - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); // Receive packet 1001 in application data. ProcessDataPacketAtLevel(1001, false, ENCRYPTION_ZERO_RTT); clock_.AdvanceTime(DefaultRetransmissionTime()); @@ -9452,10 +9546,10 @@ TEST_P(QuicConnectionTest, MultiplePacketNumberSpacesBasicReceiving) { connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, std::make_unique<TaggingEncrypter>(0x02)); connection_.GetAckAlarm()->Fire(); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); // Receives more packets in application data. ProcessDataPacketAtLevel(1002, false, ENCRYPTION_ZERO_RTT); - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); peer_framer_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, std::make_unique<TaggingEncrypter>(0x02)); @@ -9464,7 +9558,7 @@ TEST_P(QuicConnectionTest, MultiplePacketNumberSpacesBasicReceiving) { // Verify zero rtt and forward secure packets get acked in the same packet. EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); ProcessDataPacket(1003); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } TEST_P(QuicConnectionTest, CancelAckAlarmOnWriteBlocked) { @@ -9479,7 +9573,7 @@ TEST_P(QuicConnectionTest, CancelAckAlarmOnWriteBlocked) { use_tagging_decrypter(); // Receives packet 1000 in initial data. ProcessCryptoPacketAtLevel(1000, ENCRYPTION_INITIAL); - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, std::make_unique<TaggingEncrypter>(0x02)); SetDecrypter(ENCRYPTION_ZERO_RTT, @@ -9488,7 +9582,7 @@ TEST_P(QuicConnectionTest, CancelAckAlarmOnWriteBlocked) { std::make_unique<TaggingEncrypter>(0x02)); // Receives packet 1000 in application data. ProcessDataPacketAtLevel(1000, false, ENCRYPTION_ZERO_RTT); - EXPECT_TRUE(connection_.GetAckAlarm()->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); writer_->SetWriteBlocked(); EXPECT_CALL(visitor_, OnWriteBlocked()).Times(AnyNumber()); @@ -9500,13 +9594,13 @@ TEST_P(QuicConnectionTest, CancelAckAlarmOnWriteBlocked) { std::make_unique<TaggingEncrypter>(0x02)); connection_.GetAckAlarm()->Fire(); // Verify ACK alarm is not set. - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); writer_->SetWritable(); // Verify 2 ACKs are sent when connection gets unblocked. EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); connection_.OnCanWrite(); - EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } // Make sure a packet received with the right client connection ID is processed. @@ -9618,12 +9712,11 @@ TEST_P(QuicConnectionTest, CheckConnectedBeforeFlush) { } ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, kPeerAddress); - QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_); - EXPECT_TRUE(ack_alarm->IsSet()); + EXPECT_TRUE(connection_.HasPendingAcks()); ProcessFramePacketWithAddresses(QuicFrame(connection_close_frame.release()), kSelfAddress, kPeerAddress); // Verify ack alarm is not set. - EXPECT_FALSE(ack_alarm->IsSet()); + EXPECT_FALSE(connection_.HasPendingAcks()); } // Verify that a packet containing three coalesced packets is parsed correctly. @@ -9861,8 +9954,18 @@ TEST_P(QuicConnectionTest, CloseConnectionAfter6ClientPTOs) { connection_options.push_back(k6PTO); config.SetConnectionOptionsToSend(connection_options); QuicConfigPeer::SetNegotiated(&config, true); + if (connection_.version().AuthenticatesHandshakeConnectionIds()) { + QuicConfigPeer::SetReceivedOriginalConnectionId( + &config, connection_.connection_id()); + QuicConfigPeer::SetReceivedInitialSourceConnectionId( + &config, connection_.connection_id()); + } EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); connection_.SetFromConfig(config); + if (GetQuicReloadableFlag(quic_default_enable_5rto_blackhole_detection2)) { + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_COMPLETE)); + } EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); // Send stream data. @@ -9902,8 +10005,18 @@ TEST_P(QuicConnectionTest, CloseConnectionAfter7ClientPTOs) { connection_options.push_back(k7PTO); config.SetConnectionOptionsToSend(connection_options); QuicConfigPeer::SetNegotiated(&config, true); + if (connection_.version().AuthenticatesHandshakeConnectionIds()) { + QuicConfigPeer::SetReceivedOriginalConnectionId( + &config, connection_.connection_id()); + QuicConfigPeer::SetReceivedInitialSourceConnectionId( + &config, connection_.connection_id()); + } EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); connection_.SetFromConfig(config); + if (GetQuicReloadableFlag(quic_default_enable_5rto_blackhole_detection2)) { + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_COMPLETE)); + } EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); // Send stream data. @@ -9941,9 +10054,19 @@ TEST_P(QuicConnectionTest, CloseConnectionAfter8ClientPTOs) { connection_options.push_back(k2PTO); connection_options.push_back(k8PTO); QuicConfigPeer::SetNegotiated(&config, true); + if (connection_.version().AuthenticatesHandshakeConnectionIds()) { + QuicConfigPeer::SetReceivedOriginalConnectionId( + &config, connection_.connection_id()); + QuicConfigPeer::SetReceivedInitialSourceConnectionId( + &config, connection_.connection_id()); + } config.SetConnectionOptionsToSend(connection_options); EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); connection_.SetFromConfig(config); + if (GetQuicReloadableFlag(quic_default_enable_5rto_blackhole_detection2)) { + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_COMPLETE)); + } EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); // Send stream data. @@ -10009,7 +10132,7 @@ TEST_P(QuicConnectionTest, DeprecateHandshakeMode) { EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); })); connection_.GetRetransmissionAlarm()->Fire(); EXPECT_EQ(1u, connection_.GetStats().pto_count); - EXPECT_EQ(0u, connection_.GetStats().crypto_retransmit_count); + EXPECT_EQ(1u, connection_.GetStats().crypto_retransmit_count); EXPECT_EQ(1u, writer_->ping_frames().size()); } @@ -10023,13 +10146,17 @@ TEST_P(QuicConnectionTest, AntiAmplificationLimit) { // Verify no data can be sent at the beginning because bytes received is 0. EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); connection_.SendCryptoDataWithString("foo", 0); + if (GetQuicReloadableFlag(quic_move_amplification_limit)) { + EXPECT_FALSE(connection_.CanWrite(HAS_RETRANSMITTABLE_DATA)); + EXPECT_FALSE(connection_.CanWrite(NO_RETRANSMITTABLE_DATA)); + } EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); // Receives packet 1. ProcessCryptoPacketAtLevel(1, ENCRYPTION_INITIAL); const size_t anti_amplification_factor = - GetQuicFlag(FLAGS_quic_anti_amplification_factor); + connection_.anti_amplification_factor(); // Verify now packets can be sent. for (size_t i = 0; i < anti_amplification_factor; ++i) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); @@ -10064,6 +10191,48 @@ TEST_P(QuicConnectionTest, AntiAmplificationLimit) { } } +TEST_P(QuicConnectionTest, AckPendingWithAmplificationLimited) { + if (!connection_.version().SupportsAntiAmplificationLimit() || + !GetQuicReloadableFlag(quic_move_amplification_limit)) { + return; + } + EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); + EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(AnyNumber()); + set_perspective(Perspective::IS_SERVER); + use_tagging_decrypter(); + connection_.SetEncrypter(ENCRYPTION_INITIAL, + std::make_unique<TaggingEncrypter>(0x01)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); + // Receives packet 1. + ProcessCryptoPacketAtLevel(1, ENCRYPTION_INITIAL); + connection_.SetEncrypter(ENCRYPTION_HANDSHAKE, + std::make_unique<TaggingEncrypter>(0x02)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE); + EXPECT_TRUE(connection_.HasPendingAcks()); + // Send response in different encryption level and cause amplification factor + // throttled. + size_t i = 0; + while (connection_.CanWrite(HAS_RETRANSMITTABLE_DATA)) { + connection_.SendCryptoDataWithString(std::string(1024, 'a'), i * 1024, + ENCRYPTION_HANDSHAKE); + ++i; + } + // Verify ACK is still pending. + EXPECT_TRUE(connection_.HasPendingAcks()); + + // Fire ACK alarm and verify ACK cannot be sent due to amplification factor. + clock_.AdvanceTime(connection_.GetAckAlarm()->deadline() - clock_.Now()); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + connection_.GetAckAlarm()->Fire(); + // Verify ACK alarm is cancelled. + EXPECT_FALSE(connection_.HasPendingAcks()); + + // Receives packet 2 and verify ACK gets flushed. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + ProcessCryptoPacketAtLevel(2, ENCRYPTION_INITIAL); + EXPECT_FALSE(writer_->ack_frames().empty()); +} + TEST_P(QuicConnectionTest, ConnectionCloseFrameType) { if (!VersionHasIetfQuicFrames(version().transport_version)) { // Test relevent only for IETF QUIC. @@ -10202,6 +10371,49 @@ TEST_P(QuicConnectionTest, SendCoalescedPackets) { EXPECT_NE(nullptr, writer_->coalesced_packet()); } +TEST_P(QuicConnectionTest, LegacyVersionEncapsulation) { + connection_.EnableLegacyVersionEncapsulation("test.example.org"); + + MockQuicConnectionDebugVisitor debug_visitor; + connection_.set_debug_visitor(&debug_visitor); + EXPECT_CALL(debug_visitor, OnPacketSent(_, _, _)).Times(1); + + // Our TestPacketWriter normally parses the sent packet using the version + // from the connection, so here we need to tell it to use the encapsulation + // version, and reset the initial decrypter for that version. + writer_->framer()->SetSupportedVersions( + SupportedVersions(LegacyVersionForEncapsulation())); + writer_->framer()->framer()->SetInitialObfuscators( + connection_.connection_id()); + + { + QuicConnection::ScopedPacketFlusher flusher(&connection_); + connection_.SendCryptoDataWithString("TEST_CRYPTO_DATA", /*offset=*/0); + } + + EXPECT_EQ(1u, writer_->packets_write_attempts()); + // Verify that the packet is fully padded. + EXPECT_EQ(connection_.max_packet_length(), writer_->last_packet_size()); + + // Check that the connection stats show Legacy Version Encapsulation was used. + EXPECT_GT(connection_.GetStats().sent_legacy_version_encapsulated_packets, + 0u); + + // Verify that the sent packet was in fact encapsulated, and check header. + const QuicPacketHeader& encapsulated_header = writer_->last_packet_header(); + EXPECT_TRUE(encapsulated_header.version_flag); + EXPECT_EQ(encapsulated_header.version, LegacyVersionForEncapsulation()); + EXPECT_EQ(encapsulated_header.destination_connection_id, + connection_.connection_id()); + + // Encapsulated packet should contain a stream frame for the crypto stream, + // optionally padding, and nothing else. + EXPECT_EQ(0u, writer_->crypto_frames().size()); + EXPECT_EQ(1u, writer_->stream_frames().size()); + EXPECT_EQ(writer_->frame_count(), writer_->framer()->padding_frames().size() + + writer_->stream_frames().size()); +} + TEST_P(QuicConnectionTest, ClientReceivedHandshakeDone) { if (!connection_.version().HasHandshakeDone()) { return; @@ -10299,14 +10511,20 @@ TEST_P(QuicConnectionTest, MultiplePacketNumberSpacePto) { EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet()); } -void QuicConnectionTest::TestClientRetryHandling(bool invalid_retry_tag, - bool missing_id_in_config, - bool wrong_id_in_config) { +void QuicConnectionTest::TestClientRetryHandling( + bool invalid_retry_tag, + bool missing_original_id_in_config, + bool wrong_original_id_in_config, + bool missing_retry_id_in_config, + bool wrong_retry_id_in_config) { if (invalid_retry_tag) { - ASSERT_FALSE(missing_id_in_config); - ASSERT_FALSE(wrong_id_in_config); + ASSERT_FALSE(missing_original_id_in_config); + ASSERT_FALSE(wrong_original_id_in_config); + ASSERT_FALSE(missing_retry_id_in_config); + ASSERT_FALSE(wrong_retry_id_in_config); } else { - ASSERT_FALSE(missing_id_in_config && wrong_id_in_config); + ASSERT_FALSE(missing_original_id_in_config && wrong_original_id_in_config); + ASSERT_FALSE(missing_retry_id_in_config && wrong_retry_id_in_config); } if (!version().HasRetryIntegrityTag()) { return; @@ -10321,15 +10539,20 @@ void QuicConnectionTest::TestClientRetryHandling(bool invalid_retry_tag, 0xff, 0xff, 0x00, 0x00, 0x1b, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0xa5, 0x23, 0xcb, 0x5b, 0xa5, 0x24, 0x69, 0x5f, 0x65, 0x69, 0xf2, 0x93, 0xa1, 0x35, 0x9d, 0x8e}; + char retry_packet29[] = { + 0xff, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, + 0x42, 0x62, 0xb5, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0xd1, 0x69, 0x26, 0xd8, + 0x1f, 0x6f, 0x9c, 0xa2, 0x95, 0x3a, 0x8a, 0xa4, 0x57, 0x5e, 0x1e, 0x49}; char* retry_packet; size_t retry_packet_length; - if (version() == - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_27)) { + if (version() == ParsedQuicVersion::Draft29()) { + retry_packet = retry_packet29; + retry_packet_length = QUICHE_ARRAYSIZE(retry_packet29); + } else if (version() == ParsedQuicVersion::Draft27()) { retry_packet = retry_packet27; retry_packet_length = QUICHE_ARRAYSIZE(retry_packet27); - } else if (version() == - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_25)) { + } else if (version() == ParsedQuicVersion::Draft25()) { retry_packet = retry_packet25; retry_packet_length = QUICHE_ARRAYSIZE(retry_packet25); } else { @@ -10360,11 +10583,17 @@ void QuicConnectionTest::TestClientRetryHandling(bool invalid_retry_tag, } QuicConnectionId config_original_connection_id = original_connection_id; - if (wrong_id_in_config) { + if (wrong_original_id_in_config) { // Flip the first bit of the connection ID. ASSERT_FALSE(config_original_connection_id.IsEmpty()); config_original_connection_id.mutable_data()[0] ^= 0x80; } + QuicConnectionId config_retry_source_connection_id = new_connection_id; + if (wrong_retry_id_in_config) { + // Flip the first bit of the connection ID. + ASSERT_FALSE(config_retry_source_connection_id.IsEmpty()); + config_retry_source_connection_id.mutable_data()[0] ^= 0x80; + } // Make sure the connection uses the connection ID from the test vectors, QuicConnectionPeer::SetServerConnectionId(&connection_, @@ -10397,11 +10626,21 @@ void QuicConnectionTest::TestClientRetryHandling(bool invalid_retry_tag, // Test validating the original_connection_id from the config. QuicConfig received_config; QuicConfigPeer::SetNegotiated(&received_config, true); - if (!missing_id_in_config) { + if (connection_.version().AuthenticatesHandshakeConnectionIds()) { + QuicConfigPeer::SetReceivedInitialSourceConnectionId( + &received_config, connection_.connection_id()); + if (!missing_retry_id_in_config) { + QuicConfigPeer::SetReceivedRetrySourceConnectionId( + &received_config, config_retry_source_connection_id); + } + } + if (!missing_original_id_in_config) { QuicConfigPeer::SetReceivedOriginalConnectionId( &received_config, config_original_connection_id); } - if (missing_id_in_config || wrong_id_in_config) { + + if (missing_original_id_in_config || wrong_original_id_in_config || + missing_retry_id_in_config || wrong_retry_id_in_config) { EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)) .Times(1); @@ -10412,8 +10651,9 @@ void QuicConnectionTest::TestClientRetryHandling(bool invalid_retry_tag, } EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)).Times(AnyNumber()); connection_.SetFromConfig(received_config); - if (missing_id_in_config || wrong_id_in_config) { - EXPECT_FALSE(connection_.connected()); + if (missing_original_id_in_config || wrong_original_id_in_config || + missing_retry_id_in_config || wrong_retry_id_in_config) { + ASSERT_FALSE(connection_.connected()); TestConnectionCloseQuicErrorCode(IETF_QUIC_PROTOCOL_VIOLATION); } else { EXPECT_TRUE(connection_.connected()); @@ -10422,31 +10662,74 @@ void QuicConnectionTest::TestClientRetryHandling(bool invalid_retry_tag, TEST_P(QuicConnectionTest, ClientParsesRetry) { TestClientRetryHandling(/*invalid_retry_tag=*/false, - /*missing_id_in_config=*/false, - /*wrong_id_in_config=*/false); + /*missing_original_id_in_config=*/false, + /*wrong_original_id_in_config=*/false, + /*missing_retry_id_in_config=*/false, + /*wrong_retry_id_in_config=*/false); } -TEST_P(QuicConnectionTest, ClientParsesInvalidRetry) { +TEST_P(QuicConnectionTest, ClientParsesRetryInvalidTag) { TestClientRetryHandling(/*invalid_retry_tag=*/true, - /*missing_id_in_config=*/false, - /*wrong_id_in_config=*/false); + /*missing_original_id_in_config=*/false, + /*wrong_original_id_in_config=*/false, + /*missing_retry_id_in_config=*/false, + /*wrong_retry_id_in_config=*/false); } -TEST_P(QuicConnectionTest, ClientParsesRetryMissingId) { +TEST_P(QuicConnectionTest, ClientParsesRetryMissingOriginalId) { TestClientRetryHandling(/*invalid_retry_tag=*/false, - /*missing_id_in_config=*/true, - /*wrong_id_in_config=*/false); + /*missing_original_id_in_config=*/true, + /*wrong_original_id_in_config=*/false, + /*missing_retry_id_in_config=*/false, + /*wrong_retry_id_in_config=*/false); } -TEST_P(QuicConnectionTest, ClientParsesRetryWrongId) { +TEST_P(QuicConnectionTest, ClientParsesRetryWrongOriginalId) { TestClientRetryHandling(/*invalid_retry_tag=*/false, - /*missing_id_in_config=*/false, - /*wrong_id_in_config=*/true); + /*missing_original_id_in_config=*/false, + /*wrong_original_id_in_config=*/true, + /*missing_retry_id_in_config=*/false, + /*wrong_retry_id_in_config=*/false); +} + +TEST_P(QuicConnectionTest, ClientParsesRetryMissingRetryId) { + if (!connection_.version().AuthenticatesHandshakeConnectionIds()) { + // Versions that do not authenticate connection IDs never send the + // retry_source_connection_id transport parameter. + return; + } + TestClientRetryHandling(/*invalid_retry_tag=*/false, + /*missing_original_id_in_config=*/false, + /*wrong_original_id_in_config=*/false, + /*missing_retry_id_in_config=*/true, + /*wrong_retry_id_in_config=*/false); +} + +TEST_P(QuicConnectionTest, ClientParsesRetryWrongRetryId) { + if (!connection_.version().AuthenticatesHandshakeConnectionIds()) { + // Versions that do not authenticate connection IDs never send the + // retry_source_connection_id transport parameter. + return; + } + TestClientRetryHandling(/*invalid_retry_tag=*/false, + /*missing_original_id_in_config=*/false, + /*wrong_original_id_in_config=*/false, + /*missing_retry_id_in_config=*/false, + /*wrong_retry_id_in_config=*/true); } TEST_P(QuicConnectionTest, ClientReceivesOriginalConnectionIdWithoutRetry) { - // Make sure that receiving the original_connection_id transport parameter - // fails the handshake when no RETRY packet was received before it. + if (!connection_.version().UsesTls()) { + // QUIC+TLS is required to transmit connection ID transport parameters. + return; + } + if (connection_.version().AuthenticatesHandshakeConnectionIds()) { + // Versions that authenticate connection IDs always send the + // original_destination_connection_id transport parameter. + return; + } + // Make sure that receiving the original_destination_connection_id transport + // parameter fails the handshake when no RETRY packet was received before it. QuicConfig received_config; QuicConfigPeer::SetNegotiated(&received_config, true); QuicConfigPeer::SetReceivedOriginalConnectionId(&received_config, @@ -10459,6 +10742,26 @@ TEST_P(QuicConnectionTest, ClientReceivesOriginalConnectionIdWithoutRetry) { TestConnectionCloseQuicErrorCode(IETF_QUIC_PROTOCOL_VIOLATION); } +TEST_P(QuicConnectionTest, ClientReceivesRetrySourceConnectionIdWithoutRetry) { + if (!connection_.version().AuthenticatesHandshakeConnectionIds()) { + // Versions that do not authenticate connection IDs never send the + // retry_source_connection_id transport parameter. + return; + } + // Make sure that receiving the retry_source_connection_id transport parameter + // fails the handshake when no RETRY packet was received before it. + QuicConfig received_config; + QuicConfigPeer::SetNegotiated(&received_config, true); + QuicConfigPeer::SetReceivedRetrySourceConnectionId(&received_config, + TestConnectionId(0x12345)); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)).Times(AnyNumber()); + EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)) + .Times(1); + connection_.SetFromConfig(received_config); + EXPECT_FALSE(connection_.connected()); + TestConnectionCloseQuicErrorCode(IETF_QUIC_PROTOCOL_VIOLATION); +} + // Regression test for http://crbug/1047977 TEST_P(QuicConnectionTest, MaxStreamsFrameCausesConnectionClose) { if (!VersionHasIetfQuicFrames(connection_.transport_version())) { @@ -10640,28 +10943,14 @@ TEST_P(QuicConnectionTest, DonotExtendIdleTimeOnUndecryptablePackets) { peer_framer_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, std::make_unique<TaggingEncrypter>(tag)); ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_FORWARD_SECURE); - if (GetQuicReloadableFlag(quic_extend_idle_time_on_decryptable_packets)) { - // Verify deadline does not get extended. - EXPECT_EQ(initial_deadline, connection_.GetTimeoutAlarm()->deadline()); - } - if (GetQuicReloadableFlag(quic_extend_idle_time_on_decryptable_packets)) { - EXPECT_CALL(visitor_, OnConnectionClosed(_, _)).Times(1); - } else { - EXPECT_CALL(visitor_, OnConnectionClosed(_, _)).Times(0); - } + // Verify deadline does not get extended. + EXPECT_EQ(initial_deadline, connection_.GetTimeoutAlarm()->deadline()); + EXPECT_CALL(visitor_, OnConnectionClosed(_, _)).Times(1); QuicTime::Delta delay = initial_deadline - clock_.ApproximateNow(); clock_.AdvanceTime(delay); - if (GetQuicReloadableFlag(quic_extend_idle_time_on_decryptable_packets)) { - connection_.GetTimeoutAlarm()->Fire(); - } - if (GetQuicReloadableFlag(quic_extend_idle_time_on_decryptable_packets)) { - // Verify connection gets closed. - EXPECT_FALSE(connection_.connected()); - } else { - // Verify the timeout alarm deadline is updated. - EXPECT_TRUE(connection_.connected()); - EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); - } + connection_.GetTimeoutAlarm()->Fire(); + // Verify connection gets closed. + EXPECT_FALSE(connection_.connected()); } TEST_P(QuicConnectionTest, BundleAckWithImmediateResponse) { @@ -10673,15 +10962,440 @@ TEST_P(QuicConnectionTest, BundleAckWithImmediateResponse) { })); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); ProcessDataPacket(1); - QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_); - if (GetQuicReloadableFlag(quic_advance_ack_timeout_update)) { - // Verify ACK is bundled with WINDOW_UPDATE. - EXPECT_FALSE(writer_->ack_frames().empty()); - EXPECT_FALSE(ack_alarm->IsSet()); + // Verify ACK is bundled with WINDOW_UPDATE. + EXPECT_FALSE(writer_->ack_frames().empty()); + EXPECT_FALSE(connection_.HasPendingAcks()); +} + +TEST_P(QuicConnectionTest, AckAlarmFiresEarly) { + if (!connection_.SupportsMultiplePacketNumberSpaces()) { + return; + } + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { + EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); + } + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + use_tagging_decrypter(); + // Receives packet 1000 in initial data. + ProcessCryptoPacketAtLevel(1000, ENCRYPTION_INITIAL); + EXPECT_TRUE(connection_.HasPendingAcks()); + + peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, + std::make_unique<TaggingEncrypter>(0x02)); + SetDecrypter(ENCRYPTION_ZERO_RTT, + std::make_unique<StrictTaggingDecrypter>(0x02)); + connection_.SetEncrypter(ENCRYPTION_INITIAL, + std::make_unique<TaggingEncrypter>(0x02)); + // Receives packet 1000 in application data. + ProcessDataPacketAtLevel(1000, false, ENCRYPTION_ZERO_RTT); + EXPECT_TRUE(connection_.HasPendingAcks()); + // Verify ACK deadline does not change. + EXPECT_EQ(clock_.ApproximateNow() + kAlarmGranularity, + connection_.GetAckAlarm()->deadline()); + + // Ack alarm fires early. + if (GetQuicReloadableFlag(quic_always_send_earliest_ack)) { + // Verify the earliest ACK is flushed. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); } else { - // ACK is pending. - EXPECT_TRUE(writer_->ack_frames().empty()); - EXPECT_TRUE(ack_alarm->IsSet()); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); + } + connection_.GetAckAlarm()->Fire(); + EXPECT_TRUE(connection_.HasPendingAcks()); + if (GetQuicReloadableFlag(quic_always_send_earliest_ack)) { + EXPECT_EQ(clock_.ApproximateNow() + DefaultDelayedAckTime(), + connection_.GetAckAlarm()->deadline()); + } else { + // No forward progress has been made. + EXPECT_EQ(clock_.ApproximateNow() + kAlarmGranularity, + connection_.GetAckAlarm()->deadline()); + } +} + +TEST_P(QuicConnectionTest, ClientOnlyBlackholeDetectionClient) { + if (!GetQuicReloadableFlag(quic_default_enable_5rto_blackhole_detection2)) { + return; + } + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(kCBHD); + config.SetConnectionOptionsToSend(connection_options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_COMPLETE)); + EXPECT_FALSE(connection_.GetBlackholeDetectorAlarm()->IsSet()); + // Send stream data. + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, FIN, nullptr); + // Verify blackhole detection is in progress. + EXPECT_TRUE(connection_.GetBlackholeDetectorAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, ClientOnlyBlackholeDetectionServer) { + if (!GetQuicReloadableFlag(quic_default_enable_5rto_blackhole_detection2)) { + return; + } + set_perspective(Perspective::IS_SERVER); + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + if (version().SupportsAntiAmplificationLimit()) { + QuicConnectionPeer::SetAddressValidated(&connection_); + } + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(kCBHD); + config.SetInitialReceivedConnectionOptions(connection_options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_COMPLETE)); + EXPECT_FALSE(connection_.GetBlackholeDetectorAlarm()->IsSet()); + // Send stream data. + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, FIN, nullptr); + // Verify blackhole detection is disabled. + EXPECT_FALSE(connection_.GetBlackholeDetectorAlarm()->IsSet()); +} + +TEST_P(QuicConnectionTest, 2RtoBlackholeDetection) { + if (!GetQuicReloadableFlag(quic_default_enable_5rto_blackhole_detection2)) { + return; + } + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(k2RTO); + config.SetConnectionOptionsToSend(connection_options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + const size_t kMinRttMs = 40; + RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs), + QuicTime::Delta::Zero(), QuicTime::Zero()); + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_COMPLETE)); + EXPECT_FALSE(connection_.GetBlackholeDetectorAlarm()->IsSet()); + // Send stream data. + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, FIN, nullptr); + // Verify blackhole delay is expected. + EXPECT_EQ(clock_.Now() + + connection_.sent_packet_manager().GetNetworkBlackholeDelay(2), + QuicConnectionPeer::GetBlackholeDetectionDeadline(&connection_)); +} + +TEST_P(QuicConnectionTest, 3RtoBlackholeDetection) { + if (!GetQuicReloadableFlag(quic_default_enable_5rto_blackhole_detection2)) { + return; + } + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(k3RTO); + config.SetConnectionOptionsToSend(connection_options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + const size_t kMinRttMs = 40; + RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs), + QuicTime::Delta::Zero(), QuicTime::Zero()); + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_COMPLETE)); + EXPECT_FALSE(connection_.GetBlackholeDetectorAlarm()->IsSet()); + // Send stream data. + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, FIN, nullptr); + // Verify blackhole delay is expected. + EXPECT_EQ(clock_.Now() + + connection_.sent_packet_manager().GetNetworkBlackholeDelay(3), + QuicConnectionPeer::GetBlackholeDetectionDeadline(&connection_)); +} + +TEST_P(QuicConnectionTest, 4RtoBlackholeDetection) { + if (!GetQuicReloadableFlag(quic_default_enable_5rto_blackhole_detection2)) { + return; + } + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(k4RTO); + config.SetConnectionOptionsToSend(connection_options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + const size_t kMinRttMs = 40; + RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs), + QuicTime::Delta::Zero(), QuicTime::Zero()); + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_COMPLETE)); + EXPECT_FALSE(connection_.GetBlackholeDetectorAlarm()->IsSet()); + // Send stream data. + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, FIN, nullptr); + // Verify blackhole delay is expected. + EXPECT_EQ(clock_.Now() + + connection_.sent_packet_manager().GetNetworkBlackholeDelay(4), + QuicConnectionPeer::GetBlackholeDetectionDeadline(&connection_)); +} + +TEST_P(QuicConnectionTest, 6RtoBlackholeDetection) { + if (!GetQuicReloadableFlag(quic_default_enable_5rto_blackhole_detection2)) { + return; + } + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(k6RTO); + config.SetConnectionOptionsToSend(connection_options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + const size_t kMinRttMs = 40; + RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs), + QuicTime::Delta::Zero(), QuicTime::Zero()); + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_COMPLETE)); + EXPECT_FALSE(connection_.GetBlackholeDetectorAlarm()->IsSet()); + // Send stream data. + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, FIN, nullptr); + // Verify blackhole delay is expected. + EXPECT_EQ(clock_.Now() + + connection_.sent_packet_manager().GetNetworkBlackholeDelay(6), + QuicConnectionPeer::GetBlackholeDetectionDeadline(&connection_)); +} + +// Regresstion test for b/158491591. +TEST_P(QuicConnectionTest, MadeForwardProgressOnDiscardingKeys) { + if (!connection_.SupportsMultiplePacketNumberSpaces()) { + return; + } + use_tagging_decrypter(); + // Send handshake packet. + connection_.SetEncrypter(ENCRYPTION_HANDSHAKE, + std::make_unique<TaggingEncrypter>(0x02)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE); + EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(1); + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(k5RTO); + config.SetConnectionOptionsToSend(connection_options); + QuicConfigPeer::SetNegotiated(&config, true); + if (GetQuicReloadableFlag(quic_default_enable_5rto_blackhole_detection2)) { + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_COMPLETE)); + } + if (connection_.version().AuthenticatesHandshakeConnectionIds()) { + QuicConfigPeer::SetReceivedOriginalConnectionId( + &config, connection_.connection_id()); + QuicConfigPeer::SetReceivedInitialSourceConnectionId( + &config, connection_.connection_id()); + } + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + + connection_.SendCryptoDataWithString("foo", 0, ENCRYPTION_HANDSHAKE); + EXPECT_TRUE(connection_.BlackholeDetectionInProgress()); + // Discard handshake keys. + connection_.OnHandshakeComplete(); + if (GetQuicReloadableFlag(quic_default_enable_5rto_blackhole_detection2)) { + // Verify blackhole detection stops. + EXPECT_FALSE(connection_.BlackholeDetectionInProgress()); + } else { + // Problematic: although there is nothing in flight, blackhole detection is + // still in progress. + EXPECT_TRUE(connection_.BlackholeDetectionInProgress()); + } +} + +TEST_P(QuicConnectionTest, ProcessUndecryptablePacketsBasedOnEncryptionLevel) { + if (!connection_.SupportsMultiplePacketNumberSpaces()) { + return; + } + // SetFromConfig is always called after construction from InitializeSession. + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(AnyNumber()); + QuicConfig config; + connection_.SetFromConfig(config); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); + connection_.RemoveDecrypter(ENCRYPTION_FORWARD_SECURE); + use_tagging_decrypter(); + + peer_framer_.SetEncrypter(ENCRYPTION_HANDSHAKE, + std::make_unique<TaggingEncrypter>(0x01)); + peer_framer_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<TaggingEncrypter>(0x02)); + + for (uint64_t i = 1; i <= 3; ++i) { + ProcessDataPacketAtLevel(i, !kHasStopWaiting, ENCRYPTION_HANDSHAKE); + } + ProcessDataPacketAtLevel(4, !kHasStopWaiting, ENCRYPTION_FORWARD_SECURE); + for (uint64_t j = 5; j <= 7; ++j) { + ProcessDataPacketAtLevel(j, !kHasStopWaiting, ENCRYPTION_HANDSHAKE); + } + EXPECT_EQ(7u, QuicConnectionPeer::NumUndecryptablePackets(&connection_)); + EXPECT_FALSE(connection_.GetProcessUndecryptablePacketsAlarm()->IsSet()); + SetDecrypter(ENCRYPTION_HANDSHAKE, + std::make_unique<StrictTaggingDecrypter>(0x01)); + EXPECT_TRUE(connection_.GetProcessUndecryptablePacketsAlarm()->IsSet()); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE); + connection_.SetEncrypter(ENCRYPTION_HANDSHAKE, + std::make_unique<TaggingEncrypter>(0x01)); + if (GetQuicReloadableFlag(quic_fix_undecryptable_packets)) { + // Verify all ENCRYPTION_HANDSHAKE packets get processed. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(6); + } else { + // Verify packets before 4 get processed. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(3); + } + connection_.GetProcessUndecryptablePacketsAlarm()->Fire(); + EXPECT_EQ(4u, QuicConnectionPeer::NumUndecryptablePackets(&connection_)); + + SetDecrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<StrictTaggingDecrypter>(0x02)); + EXPECT_TRUE(connection_.GetProcessUndecryptablePacketsAlarm()->IsSet()); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<TaggingEncrypter>(0x02)); + if (GetQuicReloadableFlag(quic_fix_undecryptable_packets)) { + // Verify the 1-RTT packet gets processed. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + } else { + // Verify all packets get processed. + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(4); + } + connection_.GetProcessUndecryptablePacketsAlarm()->Fire(); + EXPECT_EQ(0u, QuicConnectionPeer::NumUndecryptablePackets(&connection_)); +} + +TEST_P(QuicConnectionTest, ServerBundlesInitialDataWithInitialAck) { + 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(); + // Receives packet 1000 in initial data. + ProcessCryptoPacketAtLevel(1000, ENCRYPTION_INITIAL); + EXPECT_TRUE(connection_.HasPendingAcks()); + + connection_.SetEncrypter(ENCRYPTION_INITIAL, + std::make_unique<TaggingEncrypter>(0x01)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); + connection_.SendCryptoDataWithString("foo", 0, ENCRYPTION_INITIAL); + QuicTime expected_pto_time = + connection_.sent_packet_manager().GetRetransmissionTime(); + + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + connection_.SetEncrypter(ENCRYPTION_HANDSHAKE, + std::make_unique<TaggingEncrypter>(0x02)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE); + EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(1); + connection_.SendCryptoDataWithString("foo", 0, ENCRYPTION_HANDSHAKE); + // Verify PTO time does not change. + EXPECT_EQ(expected_pto_time, + connection_.sent_packet_manager().GetRetransmissionTime()); + + // Receives packet 1001 in initial data. + ProcessCryptoPacketAtLevel(1001, ENCRYPTION_INITIAL); + EXPECT_TRUE(connection_.HasPendingAcks()); + // Receives packet 1002 in initial data. + ProcessCryptoPacketAtLevel(1002, ENCRYPTION_INITIAL); + EXPECT_FALSE(writer_->ack_frames().empty()); + if (GetQuicReloadableFlag(quic_bundle_crypto_data_with_initial_ack)) { + // Verify CRYPTO frame is bundled with INITIAL ACK. + EXPECT_FALSE(writer_->crypto_frames().empty()); + // Verify PTO time changes. + EXPECT_NE(expected_pto_time, + connection_.sent_packet_manager().GetRetransmissionTime()); + } else { + EXPECT_TRUE(writer_->crypto_frames().empty()); + // Verify PTO time does not change. + EXPECT_EQ(expected_pto_time, + connection_.sent_packet_manager().GetRetransmissionTime()); + } +} + +TEST_P(QuicConnectionTest, ClientBundlesHandshakeDataWithHandshakeAck) { + if (!connection_.SupportsMultiplePacketNumberSpaces()) { + return; + } + EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); + if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { + EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); + } + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); + use_tagging_decrypter(); + connection_.SetEncrypter(ENCRYPTION_HANDSHAKE, + std::make_unique<TaggingEncrypter>(0x02)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE); + SetDecrypter(ENCRYPTION_HANDSHAKE, + std::make_unique<StrictTaggingDecrypter>(0x02)); + peer_framer_.SetEncrypter(ENCRYPTION_HANDSHAKE, + std::make_unique<TaggingEncrypter>(0x02)); + // Receives packet 1000 in handshake data. + ProcessCryptoPacketAtLevel(1000, ENCRYPTION_HANDSHAKE); + EXPECT_TRUE(connection_.HasPendingAcks()); + + EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(2); + connection_.SendCryptoDataWithString("foo", 0, ENCRYPTION_HANDSHAKE); + + // Receives packet 1001 in handshake data. + ProcessCryptoPacketAtLevel(1001, ENCRYPTION_HANDSHAKE); + EXPECT_TRUE(connection_.HasPendingAcks()); + // Receives packet 1002 in handshake data. + ProcessCryptoPacketAtLevel(1002, ENCRYPTION_HANDSHAKE); + EXPECT_FALSE(writer_->ack_frames().empty()); + if (GetQuicReloadableFlag(quic_bundle_crypto_data_with_initial_ack)) { + // Verify CRYPTO frame is bundled with HANDSHAKE ACK. + EXPECT_FALSE(writer_->crypto_frames().empty()); + } else { + EXPECT_TRUE(writer_->crypto_frames().empty()); + } +} + +// Regresstion test for b/156232673. +TEST_P(QuicConnectionTest, CoalescePacketOfLowerEncryptionLevel) { + if (!connection_.version().CanSendCoalescedPackets()) { + return; + } + if (GetQuicReloadableFlag(quic_fix_min_crypto_frame_size)) { + EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(1); + } else { + EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(0); + } + { + QuicConnection::ScopedPacketFlusher flusher(&connection_); + use_tagging_decrypter(); + connection_.SetEncrypter(ENCRYPTION_HANDSHAKE, + std::make_unique<TaggingEncrypter>(0x01)); + connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<TaggingEncrypter>(0x02)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + SendStreamDataToPeer(2, std::string(1286, 'a'), 0, NO_FIN, nullptr); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE); + // Try to coalesce a HANDSHAKE packet after 1-RTT packet. + if (GetQuicReloadableFlag(quic_fix_min_crypto_frame_size)) { + // Verify soft max packet length gets resumed and handshake packet gets + // successfully sent. + connection_.SendCryptoDataWithString("a", 0, ENCRYPTION_HANDSHAKE); + } else { + // Problematic: creator thinks there is space to consume 1-byte, however, + // extra paddings make the serialization fail because of + // MinPlaintextPacketSize. + EXPECT_CALL(visitor_, + OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)); + EXPECT_QUIC_BUG( + connection_.SendCryptoDataWithString("a", 0, ENCRYPTION_HANDSHAKE), + "AppendPaddingFrame of 3 failed"); + } } } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.cc index e93fd071c07..a769da9cd60 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.cc @@ -171,6 +171,12 @@ size_t QuicCryptoClientHandshaker::BufferSizeLimitForLevel( return QuicCryptoHandshaker::BufferSizeLimitForLevel(level); } +void QuicCryptoClientHandshaker::OnConnectionClosed( + QuicErrorCode /*error*/, + ConnectionCloseSource /*source*/) { + next_state_ = STATE_CONNECTION_CLOSED; +} + void QuicCryptoClientHandshaker::HandleServerConfigUpdateMessage( const CryptoHandshakeMessage& server_config_update) { DCHECK(server_config_update.tag() == kSCUP); @@ -236,6 +242,9 @@ void QuicCryptoClientHandshaker::DoHandshakeLoop( break; case STATE_NONE: QUIC_NOTREACHED(); + return; + case STATE_CONNECTION_CLOSED: + rv = QUIC_FAILURE; return; // We are done. } } while (rv != QUIC_PENDING && next_state_ != STATE_NONE); @@ -281,7 +290,9 @@ void QuicCryptoClientHandshaker::DoSendCHLO( // inchoate or subsequent hello. session()->config()->ToHandshakeMessage(&out, session()->transport_version()); - if (!cached->IsComplete(session()->connection()->clock()->WallNow())) { + if (!cached->IsComplete(session()->connection()->clock()->WallNow()) || + session()->config()->HasClientRequestedIndependentOption( + kQNZR, session()->perspective())) { crypto_config_->FillInchoateClientHello( server_id_, session()->supported_versions().front(), cached, session()->connection()->random_generator(), diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.h index 5ba93f24848..90f011dd053 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.h @@ -53,9 +53,9 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientHandshaker void OnOneRttPacketAcknowledged() override {} void OnHandshakePacketSent() override {} void OnConnectionClosed(QuicErrorCode /*error*/, - ConnectionCloseSource /*source*/) override {} + ConnectionCloseSource /*source*/) override; void OnHandshakeDoneReceived() override; - void OnApplicationState( + void SetServerApplicationStateForResumption( std::unique_ptr<ApplicationState> /*application_state*/) override { QUICHE_NOTREACHED(); } @@ -103,6 +103,7 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientHandshaker STATE_RECV_SHLO, STATE_INITIALIZE_SCUP, STATE_NONE, + STATE_CONNECTION_CLOSED, }; // Handles new server config and optional source-address token provided by the diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.cc index 36dc4cdb4c3..62a261d1fc1 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.cc @@ -126,9 +126,10 @@ void QuicCryptoClientStream::OnHandshakeDoneReceived() { handshaker_->OnHandshakeDoneReceived(); } -void QuicCryptoClientStream::OnApplicationState( +void QuicCryptoClientStream::SetServerApplicationStateForResumption( std::unique_ptr<ApplicationState> application_state) { - handshaker_->OnApplicationState(std::move(application_state)); + handshaker_->SetServerApplicationStateForResumption( + std::move(application_state)); } } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h index 23f83c7e390..be99fb2b949 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h @@ -63,9 +63,6 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientStreamBase : public QuicCryptoStream { // client. Does not count update messages that were received prior // to handshake confirmation. virtual int num_scup_messages_received() const = 0; - - virtual void OnApplicationState( - std::unique_ptr<ApplicationState> application_state) = 0; }; class QUIC_EXPORT_PRIVATE QuicCryptoClientStream @@ -167,7 +164,7 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientStream virtual void OnHandshakeDoneReceived() = 0; // Called when application state is received. - virtual void OnApplicationState( + virtual void SetServerApplicationStateForResumption( std::unique_ptr<ApplicationState> application_state) = 0; }; @@ -223,10 +220,9 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientStream ConnectionCloseSource source) override; void OnHandshakeDoneReceived() override; HandshakeState GetHandshakeState() const override; - size_t BufferSizeLimitForLevel(EncryptionLevel level) const override; - - void OnApplicationState( + void SetServerApplicationStateForResumption( std::unique_ptr<ApplicationState> application_state) override; + size_t BufferSizeLimitForLevel(EncryptionLevel level) const override; std::string chlo_hash() const; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream_test.cc index 6542382d670..7f6d7770eb6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream_test.cc @@ -162,6 +162,25 @@ TEST_F(QuicCryptoClientStreamTest, ExpiredServerConfig) { EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level()); } +TEST_F(QuicCryptoClientStreamTest, ClientTurnedOffZeroRtt) { + // Seed the config with a cached server config. + CompleteCryptoHandshake(); + + // Recreate connection with the new config. + CreateConnection(); + + // Set connection option. + QuicTagVector options; + options.push_back(kQNZR); + session_->config()->SetClientConnectionOptions(options); + + EXPECT_CALL(*session_, OnProofValid(testing::_)); + stream()->CryptoConnect(); + // Check that a client hello was sent. + ASSERT_EQ(1u, connection_->encrypted_packets_.size()); + EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level()); +} + TEST_F(QuicCryptoClientStreamTest, ClockSkew) { // Test that if the client's clock is skewed with respect to the server, // the handshake succeeds. In the past, the client would get the server @@ -299,7 +318,6 @@ TEST_F(QuicCryptoClientStreamTest, ServerConfigUpdateWithCert) { // Recreate connection with the new config and verify a 0-RTT attempt. CreateConnection(); - EXPECT_CALL(*connection_, OnCanWrite()); EXPECT_CALL(*session_, OnProofValid(testing::_)); EXPECT_CALL(*session_, OnProofVerifyDetailsAvailable(testing::_)) .Times(testing::AnyNumber()); 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 66f82298476..6c840671daf 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 @@ -8,6 +8,7 @@ #include <string> #include "third_party/boringssl/src/include/openssl/sha.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" #include "net/third_party/quiche/src/common/platform/api/quiche_arraysize.h" #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" #include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h" @@ -298,6 +299,11 @@ bool QuicCryptoServerStream::IsZeroRtt() const { num_handshake_messages_with_server_nonces_ == 0; } +bool QuicCryptoServerStream::IsResumption() const { + // QUIC Crypto doesn't have a non-0-RTT resumption mode. + return IsZeroRtt(); +} + int QuicCryptoServerStream::NumServerConfigUpdateMessagesSent() const { return num_server_config_update_messages_sent_; } @@ -307,7 +313,7 @@ QuicCryptoServerStream::PreviousCachedNetworkParams() const { return previous_cached_network_params_.get(); } -bool QuicCryptoServerStream::ZeroRttAttempted() const { +bool QuicCryptoServerStream::ResumptionAttempted() const { return zero_rtt_attempted_; } @@ -370,6 +376,12 @@ HandshakeState QuicCryptoServerStream::GetHandshakeState() const { return one_rtt_packet_decrypted_ ? HANDSHAKE_COMPLETE : HANDSHAKE_START; } +void QuicCryptoServerStream::SetServerApplicationStateForResumption( + std::unique_ptr<ApplicationState> /*state*/) { + // QUIC Crypto doesn't need to remember any application state as part of doing + // 0-RTT resumption, so this function is a no-op. +} + size_t QuicCryptoServerStream::BufferSizeLimitForLevel( EncryptionLevel level) const { return QuicCryptoHandshaker::BufferSizeLimitForLevel(level); @@ -389,6 +401,17 @@ void QuicCryptoServerStream::ProcessClientHello( nullptr); return; } + + if (GetQuicReloadableFlag(quic_save_user_agent_in_quic_session)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_save_user_agent_in_quic_session, 1, 3); + quiche::QuicheStringPiece user_agent_id; + message.GetStringPiece(quic::kUAID, &user_agent_id); + if (!session()->user_agent_id().has_value()) { + std::string uaid = user_agent_id.empty() ? "" : user_agent_id.data(); + session()->SetUserAgentId(std::move(uaid)); + } + } + if (!result->info.server_nonce.empty()) { ++num_handshake_messages_with_server_nonces_; } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h index 52d4874994d..9ed7764a078 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h @@ -35,9 +35,10 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerStream void SendServerConfigUpdate( const CachedNetworkParameters* cached_network_params) override; bool IsZeroRtt() const override; + bool IsResumption() const override; + bool ResumptionAttempted() const override; int NumServerConfigUpdateMessagesSent() const override; const CachedNetworkParameters* PreviousCachedNetworkParams() const override; - bool ZeroRttAttempted() const override; void SetPreviousCachedNetworkParams( CachedNetworkParameters cached_network_params) override; void OnPacketDecrypted(EncryptionLevel level) override; @@ -55,6 +56,8 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerStream const override; CryptoMessageParser* crypto_message_parser() override; HandshakeState GetHandshakeState() const override; + void SetServerApplicationStateForResumption( + std::unique_ptr<ApplicationState> state) override; size_t BufferSizeLimitForLevel(EncryptionLevel level) const override; // From QuicCryptoHandshaker diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_base.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_base.h index cdf12a3143d..540b7a42174 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_base.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_base.h @@ -62,8 +62,17 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerStreamBase : public QuicCryptoStream { virtual void SendServerConfigUpdate( const CachedNetworkParameters* cached_network_params) = 0; + // Returns true if the connection was a successful 0-RTT resumption. virtual bool IsZeroRtt() const = 0; - virtual bool ZeroRttAttempted() const = 0; + + // Returns true if the connection was the result of a resumption handshake, + // whether 0-RTT or not. + virtual bool IsResumption() const = 0; + + // Returns true if the client attempted a resumption handshake, whether or not + // the resumption actually occurred. + virtual bool ResumptionAttempted() const = 0; + virtual const CachedNetworkParameters* PreviousCachedNetworkParams() const = 0; virtual void SetPreviousCachedNetworkParams( 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 debfc3b0f52..9ff7dc05d47 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 @@ -226,7 +226,7 @@ TEST_F(QuicCryptoServerStreamTest, ZeroRTT) { // Do a first handshake in order to prime the client config with the server's // information. AdvanceHandshakeWithFakeClient(); - EXPECT_FALSE(server_stream()->ZeroRttAttempted()); + EXPECT_FALSE(server_stream()->ResumptionAttempted()); // Now do another handshake, hopefully in 0-RTT. QUIC_LOG(INFO) << "Resetting for 0-RTT handshake attempt"; @@ -247,7 +247,7 @@ TEST_F(QuicCryptoServerStreamTest, ZeroRTT) { client_connection_, client_stream(), server_connection_, server_stream()); EXPECT_EQ(1, client_stream()->num_sent_client_hellos()); - EXPECT_TRUE(server_stream()->ZeroRttAttempted()); + EXPECT_TRUE(server_stream()->ResumptionAttempted()); } TEST_F(QuicCryptoServerStreamTest, FailByPolicy) { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.cc index 20704fb627a..59a012224e8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.cc @@ -268,6 +268,12 @@ void QuicCryptoStream::WritePendingCryptoRetransmission() { level, pending.length, pending.offset, HANDSHAKE_RETRANSMISSION); send_buffer->OnStreamDataRetransmitted(pending.offset, bytes_consumed); if (bytes_consumed < pending.length) { + if (GetQuicReloadableFlag( + quic_fix_write_pending_crypto_retransmission)) { + QUIC_RELOADABLE_FLAG_COUNT( + quic_fix_write_pending_crypto_retransmission); + return; + } break; } } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.h index 49d37042390..54d1b2525c2 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.h @@ -100,6 +100,21 @@ class QUIC_EXPORT_PRIVATE QuicCryptoStream : public QuicStream { // Returns current handshake state. virtual HandshakeState GetHandshakeState() const = 0; + // Called to provide the server-side application state that must be checked + // when performing a 0-RTT TLS resumption. + // + // On a client, this may be called at any time; 0-RTT tickets will not be + // cached until this function is called. When a 0-RTT resumption is attempted, + // QuicSession::SetApplicationState will be called with the state provided by + // a call to this function on a previous connection. + // + // On a server, this function must be called before commencing the handshake, + // otherwise 0-RTT tickets will not be issued. On subsequent connections, + // 0-RTT will be rejected if the data passed into this function does not match + // the data passed in on the connection where the 0-RTT ticket was issued. + virtual void SetServerApplicationStateForResumption( + std::unique_ptr<ApplicationState> state) = 0; + // Returns the maximum number of bytes that can be buffered at a particular // encryption level |level|. virtual size_t BufferSizeLimitForLevel(EncryptionLevel level) const; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream_test.cc index 5f414e6a98a..f763d2d4782 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream_test.cc @@ -61,6 +61,8 @@ class MockQuicCryptoStream : public QuicCryptoStream, void OnHandshakePacketSent() override {} void OnHandshakeDoneReceived() override {} HandshakeState GetHandshakeState() const override { return HANDSHAKE_START; } + void SetServerApplicationStateForResumption( + std::unique_ptr<ApplicationState> /*application_state*/) override {} private: QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_; @@ -419,7 +421,7 @@ TEST_F(QuicCryptoStreamTest, RetransmitStreamData) { .WillOnce(InvokeWithoutArgs([this]() { return session_.ConsumeData( QuicUtils::GetCryptoStreamId(connection_->transport_version()), 150, - 1350, NO_FIN, HANDSHAKE_RETRANSMISSION, QuicheNullOpt); + 1350, NO_FIN, HANDSHAKE_RETRANSMISSION, QUICHE_NULLOPT); })); EXPECT_FALSE(stream_->RetransmitStreamData(1350, 1350, false, diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_default_packet_writer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_default_packet_writer.cc index 42220050885..b34bed49fcf 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_default_packet_writer.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_default_packet_writer.cc @@ -54,10 +54,10 @@ bool QuicDefaultPacketWriter::IsBatchMode() const { return false; } -char* QuicDefaultPacketWriter::GetNextWriteLocation( +QuicPacketBuffer QuicDefaultPacketWriter::GetNextWriteLocation( const QuicIpAddress& /*self_address*/, const QuicSocketAddress& /*peer_address*/) { - return nullptr; + return {nullptr, nullptr}; } WriteResult QuicDefaultPacketWriter::Flush() { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_default_packet_writer.h b/chromium/net/third_party/quiche/src/quic/core/quic_default_packet_writer.h index 36d4d8aa267..5388cae2314 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_default_packet_writer.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_default_packet_writer.h @@ -35,8 +35,9 @@ class QUIC_EXPORT_PRIVATE QuicDefaultPacketWriter : public QuicPacketWriter { const QuicSocketAddress& peer_address) const override; bool SupportsReleaseTime() const override; bool IsBatchMode() const override; - char* GetNextWriteLocation(const QuicIpAddress& self_address, - const QuicSocketAddress& peer_address) override; + QuicPacketBuffer GetNextWriteLocation( + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address) override; WriteResult Flush() override; void set_fd(int fd) { fd_ = fd; } 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 c0d39700f41..afdec636120 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 @@ -68,9 +68,9 @@ class PacketCollector : public QuicPacketCreator::DelegateInterface, serialized_packet.encrypted_length, true)); } - char* GetPacketBuffer() override { + QuicPacketBuffer GetPacketBuffer() override { // Let QuicPacketCreator to serialize packets on stack buffer. - return nullptr; + return {nullptr, nullptr}; } void OnUnrecoverableError(QuicErrorCode /*error*/, @@ -181,21 +181,113 @@ class StatelessConnectionTerminator { // Class which extracts the ALPN from a QUIC_CRYPTO CHLO packet. class ChloAlpnExtractor : public ChloExtractor::Delegate { public: - void OnChlo(QuicTransportVersion /*version*/, + void OnChlo(QuicTransportVersion version, QuicConnectionId /*server_connection_id*/, const CryptoHandshakeMessage& chlo) override { quiche::QuicheStringPiece alpn_value; if (chlo.GetStringPiece(kALPN, &alpn_value)) { alpn_ = std::string(alpn_value); } + if (GetQuicReloadableFlag(quic_dispatcher_legacy_version_encapsulation)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_dispatcher_legacy_version_encapsulation, + 1, 3); + if (version == LegacyVersionForEncapsulation().transport_version) { + quiche::QuicheStringPiece qlve_value; + if (chlo.GetStringPiece(kQLVE, &qlve_value)) { + legacy_version_encapsulation_inner_packet_ = std::string(qlve_value); + } + } + } } std::string&& ConsumeAlpn() { return std::move(alpn_); } + std::string&& ConsumeLegacyVersionEncapsulationInnerPacket() { + DCHECK(GetQuicReloadableFlag(quic_dispatcher_legacy_version_encapsulation)); + return std::move(legacy_version_encapsulation_inner_packet_); + } + private: std::string alpn_; + std::string legacy_version_encapsulation_inner_packet_; }; +bool MaybeHandleLegacyVersionEncapsulation( + QuicDispatcher* dispatcher, + ChloAlpnExtractor* alpn_extractor, + const ReceivedPacketInfo& packet_info) { + DCHECK(GetQuicReloadableFlag(quic_dispatcher_legacy_version_encapsulation)); + std::string legacy_version_encapsulation_inner_packet = + alpn_extractor->ConsumeLegacyVersionEncapsulationInnerPacket(); + if (legacy_version_encapsulation_inner_packet.empty()) { + // This CHLO did not contain the Legacy Version Encapsulation tag. + return false; + } + PacketHeaderFormat format; + QuicLongHeaderType long_packet_type; + bool version_present; + bool has_length_prefix; + QuicVersionLabel version_label; + ParsedQuicVersion parsed_version = ParsedQuicVersion::Unsupported(); + QuicConnectionId destination_connection_id, source_connection_id; + bool retry_token_present; + quiche::QuicheStringPiece retry_token; + std::string detailed_error; + const QuicErrorCode error = QuicFramer::ParsePublicHeaderDispatcher( + QuicEncryptedPacket(legacy_version_encapsulation_inner_packet.data(), + legacy_version_encapsulation_inner_packet.length()), + kQuicDefaultConnectionIdLength, &format, &long_packet_type, + &version_present, &has_length_prefix, &version_label, &parsed_version, + &destination_connection_id, &source_connection_id, &retry_token_present, + &retry_token, &detailed_error); + if (error != QUIC_NO_ERROR) { + QUIC_DLOG(ERROR) + << "Failed to parse Legacy Version Encapsulation inner packet:" + << detailed_error; + return false; + } + if (destination_connection_id != packet_info.destination_connection_id) { + // We enforce that the inner and outer connection IDs match to make sure + // this never impacts routing of packets. + QUIC_DLOG(ERROR) << "Ignoring Legacy Version Encapsulation packet " + "with mismatched connection ID " + << destination_connection_id << " vs " + << packet_info.destination_connection_id; + return false; + } + if (legacy_version_encapsulation_inner_packet.length() >= + packet_info.packet.length()) { + QUIC_BUG << "Inner packet cannot be larger than outer " + << legacy_version_encapsulation_inner_packet.length() << " vs " + << packet_info.packet.length(); + return false; + } + + QUIC_DVLOG(1) << "Extracted a Legacy Version Encapsulation " + << legacy_version_encapsulation_inner_packet.length() + << " byte packet of version " << parsed_version; + + // Append zeroes to the end of the packet. This will ensure that + // we use the right number of bytes for calculating anti-amplification + // limits. Note that this only works for long headers of versions that carry + // long header lengths, since they'll ignore any trailing zeroes. We still + // do this for all packets to ensure version negotiation works. + legacy_version_encapsulation_inner_packet.append( + packet_info.packet.length() - + legacy_version_encapsulation_inner_packet.length(), + 0x00); + + // Process the inner packet as if it had been received by itself. + QuicReceivedPacket received_encapsulated_packet( + legacy_version_encapsulation_inner_packet.data(), + legacy_version_encapsulation_inner_packet.length(), + packet_info.packet.receipt_time()); + dispatcher->ProcessPacket(packet_info.self_address, packet_info.peer_address, + received_encapsulated_packet); + QUIC_CODE_COUNT(quic_legacy_version_encapsulation_decapsulated); + return true; +} + } // namespace QuicDispatcher::QuicDispatcher( @@ -307,30 +399,54 @@ void QuicDispatcher::ProcessPacket(const QuicSocketAddress& self_address, } QuicConnectionId QuicDispatcher::MaybeReplaceServerConnectionId( - QuicConnectionId server_connection_id, - ParsedQuicVersion version) const { - if (server_connection_id.length() == expected_server_connection_id_length_) { + const QuicConnectionId& server_connection_id, + const ParsedQuicVersion& version) const { + const uint8_t server_connection_id_length = server_connection_id.length(); + if (server_connection_id_length == expected_server_connection_id_length_) { return server_connection_id; } DCHECK(version.AllowsVariableLengthConnectionIds()); - - QuicConnectionId new_connection_id = - GenerateNewServerConnectionId(version, server_connection_id); + QuicConnectionId new_connection_id; + if (server_connection_id_length < expected_server_connection_id_length_) { + new_connection_id = ReplaceShortServerConnectionId( + version, server_connection_id, expected_server_connection_id_length_); + // Verify that ReplaceShortServerConnectionId is deterministic. + DCHECK_EQ(new_connection_id, ReplaceShortServerConnectionId( + version, server_connection_id, + expected_server_connection_id_length_)); + } else { + new_connection_id = ReplaceLongServerConnectionId( + version, server_connection_id, expected_server_connection_id_length_); + // Verify that ReplaceLongServerConnectionId is deterministic. + DCHECK_EQ(new_connection_id, ReplaceLongServerConnectionId( + version, server_connection_id, + expected_server_connection_id_length_)); + } DCHECK_EQ(expected_server_connection_id_length_, new_connection_id.length()); - // Verify that GenerateNewServerConnectionId is deterministic. - DCHECK_EQ(new_connection_id, - GenerateNewServerConnectionId(version, server_connection_id)); - QUIC_DLOG(INFO) << "Replacing incoming connection ID " << server_connection_id << " with " << new_connection_id; return new_connection_id; } -QuicConnectionId QuicDispatcher::GenerateNewServerConnectionId( - ParsedQuicVersion /*version*/, - QuicConnectionId connection_id) const { - return QuicUtils::CreateReplacementConnectionId(connection_id); +QuicConnectionId QuicDispatcher::ReplaceShortServerConnectionId( + const ParsedQuicVersion& /*version*/, + const QuicConnectionId& server_connection_id, + uint8_t expected_server_connection_id_length) const { + DCHECK_LT(server_connection_id.length(), + expected_server_connection_id_length); + return QuicUtils::CreateReplacementConnectionId( + server_connection_id, expected_server_connection_id_length); +} + +QuicConnectionId QuicDispatcher::ReplaceLongServerConnectionId( + const ParsedQuicVersion& /*version*/, + const QuicConnectionId& server_connection_id, + uint8_t expected_server_connection_id_length) const { + DCHECK_GT(server_connection_id.length(), + expected_server_connection_id_length); + return QuicUtils::CreateReplacementConnectionId( + server_connection_id, expected_server_connection_id_length); } bool QuicDispatcher::MaybeDispatchPacket( @@ -367,6 +483,26 @@ bool QuicDispatcher::MaybeDispatchPacket( auto it = session_map_.find(server_connection_id); if (it != session_map_.end()) { DCHECK(!buffered_packets_.HasBufferedPackets(server_connection_id)); + if (GetQuicReloadableFlag(quic_dispatcher_legacy_version_encapsulation)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_dispatcher_legacy_version_encapsulation, + 2, 3); + 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. + ChloAlpnExtractor 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; @@ -521,6 +657,29 @@ void QuicDispatcher::ProcessHeader(ReceivedPacketInfo* packet_info) { BufferEarlyPacket(*packet_info); break; } + if (GetQuicReloadableFlag(quic_dont_pad_chlo)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_dont_pad_chlo, 2, 2); + // We only apply this check for versions that do not use the IETF + // invariant header because those versions are already checked in + // QuicDispatcher::MaybeDispatchPacket. + if (packet_info->version_flag && + !packet_info->version.HasIetfInvariantHeader() && + crypto_config()->validate_chlo_size() && + packet_info->packet.length() < kMinClientInitialPacketLength) { + QUIC_DVLOG(1) << "Dropping CHLO packet which is too short, length: " + << packet_info->packet.length(); + QUIC_CODE_COUNT(quic_drop_small_chlo_packets); + break; + } + } + if (GetQuicReloadableFlag(quic_dispatcher_legacy_version_encapsulation)) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_dispatcher_legacy_version_encapsulation, 3, 3); + if (MaybeHandleLegacyVersionEncapsulation(this, &alpn_extractor, + *packet_info)) { + break; + } + } ProcessChlo({alpn_extractor.ConsumeAlpn()}, packet_info); } break; case kFateTimeWait: @@ -568,36 +727,6 @@ std::string QuicDispatcher::SelectAlpn(const std::vector<std::string>& alpns) { QuicDispatcher::QuicPacketFate QuicDispatcher::ValidityChecks( const ReceivedPacketInfo& packet_info) { if (!packet_info.version_flag) { - // The Android network conformance test contains a UDP test that sends a - // 12-byte packet with the following format: - // - 0x0c (public flags: 8-byte connection ID, 1-byte packet number) - // - randomized 8-byte connection ID - // - 0x01 (1-byte packet number) - // - 0x00 (private flags) - // - 0x07 (PING frame). - // That packet is invalid and we would normally drop it but in order to - // unblock this conformance testing we have the following workaround that - // will be removed once the fixed test is deployed. - // TODO(b/139691956) Remove this workaround once fixed test is deployed. - if (!GetQuicReloadableFlag( - quic_remove_android_conformance_test_workaround) && - packet_info.packet.length() == 12 && - packet_info.packet.data()[0] == 0x0c && - packet_info.packet.data()[9] == 0x01 && - packet_info.packet.data()[10] == 0x00 && - packet_info.packet.data()[11] == 0x07) { - QUIC_DLOG(INFO) << "Received Android UDP network conformance test " - "packet with connection ID " - << packet_info.destination_connection_id; - // Respond with a public reset that the test will know how to parse - // then return kFateDrop to stop processing of this packet. - time_wait_list_manager()->SendPublicReset( - packet_info.self_address, packet_info.peer_address, - packet_info.destination_connection_id, - /*ietf_quic=*/false, GetPerPacketContext()); - return kFateDrop; - } - QUIC_DLOG(INFO) << "Packet without version arrived for unknown connection ID " << packet_info.destination_connection_id; @@ -884,8 +1013,8 @@ void QuicDispatcher::ProcessBufferedChlos(size_t max_connections_to_create) { CreateQuicSession(server_connection_id, packets.front().peer_address, alpn, packet_list.version); if (original_connection_id != server_connection_id) { - session->connection()->AddIncomingConnectionId(original_connection_id); - session->connection()->InstallInitialCrypters(original_connection_id); + session->connection()->SetOriginalDestinationConnectionId( + original_connection_id); } QUIC_DLOG(INFO) << "Created new session for " << server_connection_id; @@ -976,10 +1105,16 @@ void QuicDispatcher::ProcessChlo(const std::vector<std::string>& alpns, std::unique_ptr<QuicSession> session = CreateQuicSession(packet_info->destination_connection_id, packet_info->peer_address, alpn, packet_info->version); - DCHECK(session); + if (QUIC_PREDICT_FALSE(session == nullptr)) { + QUIC_BUG << "CreateQuicSession returned nullptr for " + << packet_info->destination_connection_id << " from " + << packet_info->peer_address << " ALPN \"" << alpn << "\" version " + << packet_info->version; + return; + } if (original_connection_id != packet_info->destination_connection_id) { - session->connection()->AddIncomingConnectionId(original_connection_id); - session->connection()->InstallInitialCrypters(original_connection_id); + session->connection()->SetOriginalDestinationConnectionId( + original_connection_id); } QUIC_DLOG(INFO) << "Created new session for " << packet_info->destination_connection_id; 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 73ab3ec4fd0..f0c35eae152 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 @@ -103,30 +103,17 @@ class QUIC_NO_EXPORT QuicDispatcher void OnConnectionAddedToTimeWaitList( QuicConnectionId server_connection_id) override; - using SessionMap = QuicUnorderedMap<QuicConnectionId, - std::unique_ptr<QuicSession>, - QuicConnectionIdHash>; + using SessionMap = QuicHashMap<QuicConnectionId, + std::unique_ptr<QuicSession>, + QuicConnectionIdHash>; const SessionMap& session_map() const { return session_map_; } // Deletes all sessions on the closed session list and clears the list. virtual void DeleteSessions(); - using ConnectionIdMap = QuicUnorderedMap<QuicConnectionId, - QuicConnectionId, - QuicConnectionIdHash>; - - // The largest packet number we expect to receive with a connection - // ID for a connection that is not established yet. The current design will - // send a handshake and then up to 50 or so data packets, and then it may - // resend the handshake packet up to 10 times. (Retransmitted packets are - // sent with unique packet numbers.) - static const uint64_t kMaxReasonableInitialPacketNumber = 100; - static_assert(kMaxReasonableInitialPacketNumber >= - kInitialCongestionWindow + 10, - "kMaxReasonableInitialPacketNumber is unreasonably small " - "relative to kInitialCongestionWindow."); - + using ConnectionIdMap = + QuicHashMap<QuicConnectionId, QuicConnectionId, QuicConnectionIdHash>; // QuicBufferedPacketStore::VisitorInterface implementation. void OnExpiredPackets(QuicConnectionId server_connection_id, @@ -162,11 +149,29 @@ class QUIC_NO_EXPORT QuicDispatcher virtual bool MaybeDispatchPacket(const ReceivedPacketInfo& packet_info); // Generate a connection ID with a length that is expected by the dispatcher. + // Called only when |server_connection_id| is shorter than + // |expected_connection_id_length|. // Note that this MUST produce a deterministic result (calling this method // with two connection IDs that are equal must produce the same result). - virtual QuicConnectionId GenerateNewServerConnectionId( - ParsedQuicVersion version, - QuicConnectionId connection_id) const; + // Note that this is not used in general operation because our default + // |expected_server_connection_id_length| is 8, and the IETF specification + // requires clients to use an initial length of at least 8. However, we + // allow disabling that requirement via + // |allow_short_initial_server_connection_ids_|. + virtual QuicConnectionId ReplaceShortServerConnectionId( + const ParsedQuicVersion& version, + const QuicConnectionId& server_connection_id, + uint8_t expected_server_connection_id_length) const; + + // Generate a connection ID with a length that is expected by the dispatcher. + // Called only when |server_connection_id| is longer than + // |expected_connection_id_length|. + // Note that this MUST produce a deterministic result (calling this method + // with two connection IDs that are equal must produce the same result). + virtual QuicConnectionId ReplaceLongServerConnectionId( + const ParsedQuicVersion& version, + const QuicConnectionId& server_connection_id, + uint8_t expected_server_connection_id_length) const; // Values to be returned by ValidityChecks() to indicate what should be done // with a packet. Fates with greater values are considered to be higher @@ -308,11 +313,12 @@ class QUIC_NO_EXPORT QuicDispatcher std::string SelectAlpn(const std::vector<std::string>& alpns); // If the connection ID length is different from what the dispatcher expects, - // replace the connection ID with a random one of the right length, - // and save it to make sure the mapping is persistent. + // replace the connection ID with one of the right length. + // Note that this MUST produce a deterministic result (calling this method + // with two connection IDs that are equal must produce the same result). QuicConnectionId MaybeReplaceServerConnectionId( - QuicConnectionId server_connection_id, - ParsedQuicVersion version) const; + const QuicConnectionId& server_connection_id, + const ParsedQuicVersion& version) const; private: friend class test::QuicDispatcherPeer; 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 814462e9d01..ef4190d470a 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 @@ -580,6 +580,73 @@ TEST_P(QuicDispatcherTestAllVersions, TlsMultiPacketClientHelloWithReordering) { TestTlsMultiPacketClientHello(/*add_reordering=*/true); } +TEST_P(QuicDispatcherTestAllVersions, LegacyVersionEncapsulation) { + if (!version_.HasLongHeaderLengths()) { + // Decapsulating Legacy Version Encapsulation packets from these versions + // is not currently supported in QuicDispatcher. + return; + } + SetQuicReloadableFlag(quic_dont_pad_chlo, true); + SetQuicReloadableFlag(quic_dispatcher_legacy_version_encapsulation, true); + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + QuicConnectionId server_connection_id = TestConnectionId(); + QuicConfig client_config = DefaultQuicConfig(); + client_config.SetClientConnectionOptions(QuicTagVector{kQLVE}); + std::vector<std::unique_ptr<QuicReceivedPacket>> packets = + GetFirstFlightOfPackets(version_, client_config, server_connection_id); + ASSERT_EQ(packets.size(), 1u); + + // Validate that Legacy Version Encapsulation is actually being used by + // checking the version of the packet before processing it. + PacketHeaderFormat format = IETF_QUIC_LONG_HEADER_PACKET; + QuicLongHeaderType long_packet_type; + bool version_present; + bool has_length_prefix; + QuicVersionLabel version_label; + ParsedQuicVersion parsed_version = ParsedQuicVersion::Unsupported(); + QuicConnectionId destination_connection_id, source_connection_id; + bool retry_token_present; + quiche::QuicheStringPiece retry_token; + std::string detailed_error; + const QuicErrorCode error = QuicFramer::ParsePublicHeaderDispatcher( + QuicEncryptedPacket(packets[0]->data(), packets[0]->length()), + kQuicDefaultConnectionIdLength, &format, &long_packet_type, + &version_present, &has_length_prefix, &version_label, &parsed_version, + &destination_connection_id, &source_connection_id, &retry_token_present, + &retry_token, &detailed_error); + ASSERT_THAT(error, IsQuicNoError()) << detailed_error; + EXPECT_EQ(format, GOOGLE_QUIC_PACKET); + EXPECT_TRUE(version_present); + EXPECT_FALSE(has_length_prefix); + EXPECT_EQ(parsed_version, LegacyVersionForEncapsulation()); + EXPECT_EQ(destination_connection_id, server_connection_id); + EXPECT_EQ(source_connection_id, EmptyQuicConnectionId()); + EXPECT_FALSE(retry_token_present); + EXPECT_TRUE(detailed_error.empty()); + + // Processing the packet should create a new session. + EXPECT_CALL(*dispatcher_, + CreateQuicSession(server_connection_id, client_address, + Eq(ExpectedAlpn()), _)) + .WillOnce(Return(ByMove(CreateSession( + dispatcher_.get(), config_, server_connection_id, client_address, + &mock_helper_, &mock_alarm_factory_, &crypto_config_, + QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)))); + EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), + ProcessUdpPacket(_, _, _)) + .Times(2); + + ProcessReceivedPacket(packets[0]->Clone(), client_address, version_, + server_connection_id); + EXPECT_EQ(dispatcher_->session_map().size(), 1u); + + // Processing the same packet a second time should also be routed by the + // dispatcher to the right connection (we expect ProcessUdpPacket to be + // called twice, see the EXPECT_CALL above). + ProcessReceivedPacket(std::move(packets[0]), client_address, version_, + server_connection_id); +} + TEST_P(QuicDispatcherTestAllVersions, ProcessPackets) { QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); @@ -1010,57 +1077,20 @@ TEST_P(QuicDispatcherTestAllVersions, ProcessFirstFlight(client_address, EmptyQuicConnectionId()); } -TEST_P(QuicDispatcherTestAllVersions, OKSeqNoPacketProcessed) { - if (version_.UsesTls()) { - // QUIC+TLS allows clients to start with any packet number. - return; - } - QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); - QuicConnectionId connection_id = TestConnectionId(1); - - EXPECT_CALL(*dispatcher_, - CreateQuicSession(TestConnectionId(1), client_address, - Eq(ExpectedAlpn()), _)) - .WillOnce(Return(ByMove(CreateSession( - dispatcher_.get(), config_, TestConnectionId(1), client_address, - &mock_helper_, &mock_alarm_factory_, &crypto_config_, - QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)))); - EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()), - ProcessUdpPacket(_, _, _)) - .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) { - ValidatePacket(TestConnectionId(1), packet); - }))); - - // A packet whose packet number is the largest that is allowed to start a - // connection. - EXPECT_CALL(*dispatcher_, - ShouldCreateOrBufferPacketForConnection( - ReceivedPacketInfoConnectionIdEquals(connection_id))); - ProcessPacket(client_address, connection_id, true, SerializeCHLO(), - CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, - QuicDispatcher::kMaxReasonableInitialPacketNumber); -} - TEST_P(QuicDispatcherTestOneVersion, VersionsChangeInFlight) { VerifyVersionNotSupported(QuicVersionReservedForNegotiation()); for (ParsedQuicVersion version : CurrentSupportedVersions()) { VerifyVersionSupported(version); + QuicDisableVersion(version); + VerifyVersionNotSupported(version); + QuicEnableVersion(version); + VerifyVersionSupported(version); } - - // Turn off version Q050. - SetQuicReloadableFlag(quic_disable_version_q050, true); - VerifyVersionNotSupported( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50)); - - // Turn on version Q050. - SetQuicReloadableFlag(quic_disable_version_q050, false); - VerifyVersionSupported( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50)); } TEST_P(QuicDispatcherTestOneVersion, RejectDeprecatedVersionsWithVersionNegotiation) { - static_assert(quic::SupportedVersions().size() == 8u, + static_assert(quic::SupportedVersions().size() == 9u, "Please add deprecated versions to this test"); QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); CreateTimeWaitListManager(); @@ -1275,55 +1305,6 @@ TEST_P(QuicDispatcherTestOneVersion, VersionNegotiationProbeEndToEnd) { destination_connection_id_bytes, sizeof(destination_connection_id_bytes)); } -TEST_P(QuicDispatcherTestOneVersion, AndroidConformanceTestOld) { - if (GetQuicReloadableFlag(quic_remove_android_conformance_test_workaround)) { - // TODO(b/139691956) Remove this test once the flag is deprecated. - return; - } - SavingWriter* saving_writer = new SavingWriter(); - // dispatcher_ takes ownership of saving_writer. - QuicDispatcherPeer::UseWriter(dispatcher_.get(), saving_writer); - - QuicTimeWaitListManager* time_wait_list_manager = new QuicTimeWaitListManager( - saving_writer, dispatcher_.get(), mock_helper_.GetClock(), - &mock_alarm_factory_); - // dispatcher_ takes ownership of time_wait_list_manager. - QuicDispatcherPeer::SetTimeWaitListManager(dispatcher_.get(), - time_wait_list_manager); - // clang-format off - static const unsigned char packet[] = { - // Android UDP network conformance test packet as it was before this change: - // https://android-review.googlesource.com/c/platform/cts/+/1104285 - 0x0c, // public flags: 8-byte connection ID, 1-byte packet number - 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, // 8-byte connection ID - 0x01, // 1-byte packet number - 0x00, // private flags - 0x07, // PING frame - }; - // clang-format on - - QuicEncryptedPacket encrypted(reinterpret_cast<const char*>(packet), - sizeof(packet), false); - std::unique_ptr<QuicReceivedPacket> received_packet( - ConstructReceivedPacket(encrypted, mock_helper_.GetClock()->Now())); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0); - - QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); - dispatcher_->ProcessPacket(server_address_, client_address, *received_packet); - ASSERT_EQ(1u, saving_writer->packets()->size()); - - // The Android UDP network conformance test directly checks that bytes 1-9 - // of the response match the connection ID that was sent. - static const char connection_id_bytes[] = {0x71, 0x72, 0x73, 0x74, - 0x75, 0x76, 0x77, 0x78}; - ASSERT_GE((*(saving_writer->packets()))[0]->length(), - 1u + sizeof(connection_id_bytes)); - quiche::test::CompareCharArraysWithHexError( - "response connection ID", &(*(saving_writer->packets()))[0]->data()[1], - sizeof(connection_id_bytes), connection_id_bytes, - sizeof(connection_id_bytes)); -} - TEST_P(QuicDispatcherTestOneVersion, AndroidConformanceTest) { // WARNING: do not remove or modify this test without making sure that we // still have adequate coverage for the Android conformance test. @@ -1373,8 +1354,12 @@ TEST_P(QuicDispatcherTestOneVersion, AndroidConformanceTest) { } TEST_P(QuicDispatcherTestAllVersions, DoNotProcessSmallPacket) { - if (!version_.HasIetfInvariantHeader()) { - // We only drop small packets when using IETF_QUIC_LONG_HEADER_PACKET. + if (!version_.HasIetfInvariantHeader() && + !GetQuicReloadableFlag(quic_dont_pad_chlo)) { + // When quic_dont_pad_chlo is false, we only drop small packets when using + // IETF_QUIC_LONG_HEADER_PACKET. When quic_dont_pad_chlo is true, we drop + // small packets for all versions. + // TODO(dschinazi) remove this early return when we deprecate the flag. return; } CreateTimeWaitListManager(); @@ -2340,7 +2325,8 @@ TEST_P(BufferedPacketStoreTest, ReceiveCHLOForBufferedConnection) { // Regression test for b/117874922. TEST_P(BufferedPacketStoreTest, ProcessBufferedChloWithDifferentVersion) { // Ensure the preferred version is not supported by the server. - SetQuicReloadableFlag(quic_enable_version_draft_27, false); + QuicDisableVersion(AllSupportedVersions().front()); + uint64_t last_connection_id = kMaxNumSessionsToCreate + 5; ParsedQuicVersionVector supported_versions = CurrentSupportedVersions(); for (uint64_t conn_id = 1; conn_id <= last_connection_id; ++conn_id) { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.cc b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.cc index 6afccfece10..1c0984aae17 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.cc @@ -203,6 +203,8 @@ const char* QuicErrorCodeToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_HTTP_DUPLICATE_SETTING_IDENTIFIER); RETURN_STRING_LITERAL(QUIC_HTTP_INVALID_MAX_PUSH_ID); RETURN_STRING_LITERAL(QUIC_HTTP_STREAM_LIMIT_TOO_LOW); + RETURN_STRING_LITERAL(QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH); + RETURN_STRING_LITERAL(QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH); RETURN_STRING_LITERAL(QUIC_HPACK_INDEX_VARINT_ERROR); RETURN_STRING_LITERAL(QUIC_HPACK_NAME_LENGTH_VARINT_ERROR); RETURN_STRING_LITERAL(QUIC_HPACK_VALUE_LENGTH_VARINT_ERROR); @@ -221,6 +223,9 @@ const char* QuicErrorCodeToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_HPACK_TRUNCATED_BLOCK); RETURN_STRING_LITERAL(QUIC_HPACK_FRAGMENT_TOO_LONG); RETURN_STRING_LITERAL(QUIC_HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT); + RETURN_STRING_LITERAL(QUIC_ZERO_RTT_UNRETRANSMITTABLE); + RETURN_STRING_LITERAL(QUIC_ZERO_RTT_REJECTION_LIMIT_REDUCED); + RETURN_STRING_LITERAL(QUIC_ZERO_RTT_RESUMPTION_LIMIT_REDUCED); RETURN_STRING_LITERAL(QUIC_LAST_ERROR); // Intentionally have no default case, so we'll break the build @@ -567,6 +572,10 @@ QuicErrorCodeToIetfMapping QuicErrorCodeToTransportErrorCode( case QUIC_HTTP_STREAM_LIMIT_TOO_LOW: return {false, static_cast<uint64_t>( QuicHttp3ErrorCode::GENERAL_PROTOCOL_ERROR)}; + case QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH: + return {false, static_cast<uint64_t>(QuicHttp3ErrorCode::SETTINGS_ERROR)}; + case QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH: + return {true, static_cast<uint64_t>(INTERNAL_ERROR)}; case QUIC_HPACK_INDEX_VARINT_ERROR: return {true, static_cast<uint64_t>(INTERNAL_ERROR)}; case QUIC_HPACK_NAME_LENGTH_VARINT_ERROR: @@ -599,6 +608,12 @@ QuicErrorCodeToIetfMapping QuicErrorCodeToTransportErrorCode( return {true, static_cast<uint64_t>(INTERNAL_ERROR)}; case QUIC_HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT: return {true, static_cast<uint64_t>(INTERNAL_ERROR)}; + case QUIC_ZERO_RTT_UNRETRANSMITTABLE: + return {true, static_cast<uint64_t>(INTERNAL_ERROR)}; + case QUIC_ZERO_RTT_REJECTION_LIMIT_REDUCED: + return {true, static_cast<uint64_t>(INTERNAL_ERROR)}; + case QUIC_ZERO_RTT_RESUMPTION_LIMIT_REDUCED: + return {true, static_cast<uint64_t>(PROTOCOL_VIOLATION)}; case QUIC_LAST_ERROR: return {false, static_cast<uint64_t>(QUIC_LAST_ERROR)}; } 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 f057fea1028..4c13f2c8382 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 @@ -431,6 +431,12 @@ enum QuicErrorCode { QUIC_HTTP_INVALID_MAX_PUSH_ID = 159, // Received unidirectional stream limit is lower than required by HTTP/3. QUIC_HTTP_STREAM_LIMIT_TOO_LOW = 160, + // Received mismatched SETTINGS frame from HTTP/3 connection where early data + // is accepted. Server violated the HTTP/3 spec. + QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH = 164, + // Received mismatched SETTINGS frame from HTTP/3 connection where early data + // is rejected. Our implementation currently doesn't support it. + QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH = 165, // HPACK header block decoding errors. // Index varint beyond implementation limit. @@ -466,8 +472,20 @@ enum QuicErrorCode { // Total compressed HPACK data size exceeds limit. QUIC_HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT = 150, + // Stream/flow control limit from 1-RTT handshake is too low to retransmit + // 0-RTT data. This is our implentation error. We could in theory keep the + // connection alive but chose not to for simplicity. + QUIC_ZERO_RTT_UNRETRANSMITTABLE = 161, + // Stream/flow control limit from 0-RTT rejection reduces cached limit. + // This is our implentation error. We could in theory keep the connection + // alive but chose not to for simplicity. + QUIC_ZERO_RTT_REJECTION_LIMIT_REDUCED = 162, + // Stream/flow control limit from 0-RTT resumption reduces cached limit. + // This is the peer violating QUIC spec. + QUIC_ZERO_RTT_RESUMPTION_LIMIT_REDUCED = 163, + // No error. Used as bound while iterating. - QUIC_LAST_ERROR = 161, + QUIC_LAST_ERROR = 166, }; // QuicErrorCodes is encoded as four octets on-the-wire when doing Google QUIC, // or a varint62 when doing IETF QUIC. Ensure that its value does not exceed diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller.h b/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller.h index 29c3c02d1a3..daad3d9d560 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller.h @@ -91,6 +91,8 @@ class QUIC_EXPORT_PRIVATE QuicFlowController QuicByteCount bytes_consumed() const { return bytes_consumed_; } + QuicByteCount bytes_sent() const { return bytes_sent_; } + QuicStreamOffset send_window_offset() const { return send_window_offset_; } QuicStreamOffset highest_received_byte_offset() const { 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 b05b4576d8e..139896560d6 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 @@ -6,6 +6,7 @@ #include <cstddef> #include <cstdint> +#include <limits> #include <memory> #include <string> #include <utility> @@ -20,6 +21,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_ack_frequency_frame.h" #include "net/third_party/quiche/src/quic/core/quic_connection_id.h" #include "net/third_party/quiche/src/quic/core/quic_constants.h" #include "net/third_party/quiche/src/quic/core/quic_data_reader.h" @@ -28,6 +30,7 @@ #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_socket_address_coder.h" #include "net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/quic/core/quic_versions.h" @@ -622,6 +625,17 @@ size_t QuicFramer::GetStopSendingFrameSize(const QuicStopSendingFrame& frame) { } // static +size_t QuicFramer::GetAckFrequencyFrameSize( + const QuicAckFrequencyFrame& frame) { + return QuicDataWriter::GetVarInt62Len(IETF_ACK_FREQUENCY) + + QuicDataWriter::GetVarInt62Len(frame.sequence_number) + + QuicDataWriter::GetVarInt62Len(frame.packet_tolerance) + + QuicDataWriter::GetVarInt62Len(frame.max_ack_delay.ToMicroseconds()) + + // One byte for encoding boolean + 1; +} + +// static size_t QuicFramer::GetPathChallengeFrameSize( const QuicPathChallengeFrame& frame) { return kQuicFrameTypeSize + sizeof(frame.data_buffer); @@ -675,7 +689,8 @@ size_t QuicFramer::GetRetransmittableControlFrameSize( case HANDSHAKE_DONE_FRAME: // HANDSHAKE_DONE has no payload. return kQuicFrameTypeSize; - + case ACK_FREQUENCY_FRAME: + return GetAckFrequencyFrameSize(*frame.ack_frequency_frame); case STREAM_FRAME: case ACK_FRAME: case STOP_WAITING_FRAME: @@ -1013,6 +1028,9 @@ size_t QuicFramer::BuildDataPacket(const QuicPacketHeader& header, return 0; } break; + case HANDSHAKE_DONE_FRAME: + // HANDSHAKE_DONE has no payload. + break; default: RaiseError(QUIC_INVALID_FRAME_DATA); QUIC_BUG << "QUIC_INVALID_FRAME_DATA"; @@ -1034,8 +1052,8 @@ size_t QuicFramer::AppendIetfFrames(const QuicFrames& frames, for (const QuicFrame& frame : frames) { // Determine if we should write stream frame length in header. const bool last_frame_in_packet = i == frames.size() - 1; - if (!AppendIetfTypeByte(frame, last_frame_in_packet, writer)) { - QUIC_BUG << "AppendIetfTypeByte failed: " << detailed_error(); + if (!AppendIetfFrameType(frame, last_frame_in_packet, writer)) { + QUIC_BUG << "AppendIetfFrameType failed: " << detailed_error(); return 0; } @@ -1182,6 +1200,12 @@ size_t QuicFramer::AppendIetfFrames(const QuicFrames& frames, case HANDSHAKE_DONE_FRAME: // HANDSHAKE_DONE has no payload. break; + case ACK_FREQUENCY_FRAME: + if (!AppendAckFrequencyFrame(*frame.ack_frequency_frame, writer)) { + QUIC_BUG << "AppendAckFrequencyFrame failed: " << detailed_error(); + return 0; + } + break; default: set_detailed_error("Tried to append unknown frame type."); RaiseError(QUIC_INVALID_FRAME_DATA); @@ -2004,6 +2028,10 @@ bool QuicFramer::HasEncrypterOfEncryptionLevel(EncryptionLevel level) const { return encrypter_[level] != nullptr; } +bool QuicFramer::HasDecrypterOfEncryptionLevel(EncryptionLevel level) const { + return decrypter_[level] != nullptr; +} + bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header, QuicDataWriter* writer, size_t* length_field_offset) { @@ -2986,6 +3014,19 @@ bool QuicFramer::ProcessFrameData(QuicDataReader* reader, } break; } + case HANDSHAKE_DONE_FRAME: { + // HANDSHAKE_DONE has no payload. + QuicHandshakeDoneFrame handshake_done_frame; + QUIC_DVLOG(2) << ENDPOINT << "Processing handshake done frame " + << handshake_done_frame; + if (!visitor_->OnHandshakeDoneFrame(handshake_done_frame)) { + QUIC_DVLOG(1) << ENDPOINT + << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } default: set_detailed_error("Illegal frame type."); @@ -3329,7 +3370,20 @@ bool QuicFramer::ProcessIetfFrameData(QuicDataReader* reader, << handshake_done_frame; break; } - + case IETF_ACK_FREQUENCY: { + QuicAckFrequencyFrame frame; + if (!ProcessAckFrequencyFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + QUIC_DVLOG(2) << ENDPOINT << "Processing IETF ack frequency frame " + << frame; + if (!visitor_->OnAckFrequencyFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } default: set_detailed_error("Illegal frame type."); QUIC_DLOG(WARNING) @@ -3509,6 +3563,47 @@ bool QuicFramer::ProcessCryptoFrame(QuicDataReader* reader, return true; } +bool QuicFramer::ProcessAckFrequencyFrame(QuicDataReader* reader, + QuicAckFrequencyFrame* frame) { + if (!reader->ReadVarInt62(&frame->sequence_number)) { + set_detailed_error("Unable to read sequence number."); + return false; + } + + if (!reader->ReadVarInt62(&frame->packet_tolerance)) { + set_detailed_error("Unable to read packet tolerance."); + return false; + } + if (frame->packet_tolerance == 0) { + set_detailed_error("Invalid packet tolerance."); + return false; + } + uint64_t max_ack_delay_us; + if (!reader->ReadVarInt62(&max_ack_delay_us)) { + set_detailed_error("Unable to read max_ack_delay_us."); + return false; + } + constexpr uint64_t kMaxAckDelayUsBound = 1u << 24; + if (max_ack_delay_us > kMaxAckDelayUsBound) { + set_detailed_error("Invalid max_ack_delay_us."); + return false; + } + frame->max_ack_delay = QuicTime::Delta::FromMicroseconds(max_ack_delay_us); + + uint8_t ignore_order; + if (!reader->ReadUInt8(&ignore_order)) { + set_detailed_error("Unable to read ignore_order."); + return false; + } + if (ignore_order > 1) { + set_detailed_error("Invalid ignore_order."); + return false; + } + frame->ignore_order = ignore_order; + + return true; +} + bool QuicFramer::ProcessAckFrame(QuicDataReader* reader, uint8_t frame_type) { const bool has_ack_blocks = ExtractBit(frame_type, kQuicHasMultipleAckBlocksOffset); @@ -4097,8 +4192,7 @@ void QuicFramer::SetDecrypter(EncryptionLevel level, DCHECK_GE(level, decrypter_level_); DCHECK(!version_.KnowsWhichDecrypterToUse()); QUIC_DVLOG(1) << ENDPOINT << "Setting decrypter from level " - << EncryptionLevelToString(decrypter_level_) << " to " - << EncryptionLevelToString(level); + << decrypter_level_ << " to " << level; decrypter_[decrypter_level_] = nullptr; decrypter_[level] = std::move(decrypter); decrypter_level_ = level; @@ -4111,8 +4205,7 @@ void QuicFramer::SetAlternativeDecrypter( DCHECK_NE(level, decrypter_level_); DCHECK(!version_.KnowsWhichDecrypterToUse()); QUIC_DVLOG(1) << ENDPOINT << "Setting alternative decrypter from level " - << EncryptionLevelToString(alternative_decrypter_level_) - << " to " << EncryptionLevelToString(level); + << alternative_decrypter_level_ << " to " << level; if (alternative_decrypter_level_ != NUM_ENCRYPTION_LEVELS) { decrypter_[alternative_decrypter_level_] = nullptr; } @@ -4124,15 +4217,13 @@ void QuicFramer::SetAlternativeDecrypter( void QuicFramer::InstallDecrypter(EncryptionLevel level, std::unique_ptr<QuicDecrypter> decrypter) { DCHECK(version_.KnowsWhichDecrypterToUse()); - QUIC_DVLOG(1) << ENDPOINT << "Installing decrypter at level " - << EncryptionLevelToString(level); + QUIC_DVLOG(1) << ENDPOINT << "Installing decrypter at level " << level; decrypter_[level] = std::move(decrypter); } void QuicFramer::RemoveDecrypter(EncryptionLevel level) { DCHECK(version_.KnowsWhichDecrypterToUse()); - QUIC_DVLOG(1) << ENDPOINT << "Removing decrypter at level " - << EncryptionLevelToString(level); + QUIC_DVLOG(1) << ENDPOINT << "Removing decrypter at level " << level; decrypter_[level] = nullptr; } @@ -4156,14 +4247,12 @@ void QuicFramer::SetEncrypter(EncryptionLevel level, std::unique_ptr<QuicEncrypter> encrypter) { DCHECK_GE(level, 0); DCHECK_LT(level, NUM_ENCRYPTION_LEVELS); - QUIC_DVLOG(1) << ENDPOINT << "Setting encrypter at level " - << EncryptionLevelToString(level); + QUIC_DVLOG(1) << ENDPOINT << "Setting encrypter at level " << level; encrypter_[level] = std::move(encrypter); } void QuicFramer::RemoveEncrypter(EncryptionLevel level) { - QUIC_DVLOG(1) << ENDPOINT << "Removing encrypter of " - << EncryptionLevelToString(level); + QUIC_DVLOG(1) << ENDPOINT << "Removing encrypter of " << level; encrypter_[level] = nullptr; } @@ -4185,7 +4274,7 @@ size_t QuicFramer::EncryptInPlace(EncryptionLevel level, if (encrypter_[level] == nullptr) { QUIC_BUG << ENDPOINT << "Attempted to encrypt in place without encrypter at level " - << EncryptionLevelToString(level); + << level; RaiseError(QUIC_ENCRYPTION_FAILURE); return 0; } @@ -4248,7 +4337,7 @@ bool QuicFramer::ApplyHeaderProtection(EncryptionLevel level, QUIC_BUG << ENDPOINT << "Attempted to apply header protection without encrypter at level " - << EncryptionLevelToString(level) << " using " << version_; + << level << " using " << version_; return false; } @@ -4318,7 +4407,7 @@ bool QuicFramer::RemoveHeaderProtection(QuicDataReader* reader, QUIC_DVLOG(1) << ENDPOINT << "No decrypter available for removing header protection at level " - << EncryptionLevelToString(expected_decryption_level); + << expected_decryption_level; return false; } @@ -4439,7 +4528,7 @@ size_t QuicFramer::EncryptPayload(EncryptionLevel level, DCHECK(packet_number.IsInitialized()); if (encrypter_[level] == nullptr) { QUIC_BUG << ENDPOINT << "Attempted to encrypt without encrypter at level " - << EncryptionLevelToString(level); + << level; RaiseError(QUIC_ENCRYPTION_FAILURE); return 0; } @@ -4449,6 +4538,14 @@ size_t QuicFramer::EncryptPayload(EncryptionLevel level, // Copy in the header, because the encrypter only populates the encrypted // plaintext content. const size_t ad_len = associated_data.length(); + if (packet.length() < ad_len) { + QUIC_BUG << ENDPOINT + << "packet is shorter than associated data length. version:" + << version() << ", packet length:" << packet.length() + << ", associated data length:" << ad_len; + RaiseError(QUIC_ENCRYPTION_FAILURE); + return 0; + } memmove(buffer, associated_data.data(), ad_len); // Encrypt the plaintext into the buffer. size_t output_length = 0; @@ -4474,7 +4571,7 @@ size_t QuicFramer::GetCiphertextSize(EncryptionLevel level, if (encrypter_[level] == nullptr) { QUIC_BUG << ENDPOINT << "Attempted to get ciphertext size without encrypter at level " - << EncryptionLevelToString(level) << " using " << version_; + << level << " using " << version_; return plaintext_size; } return encrypter_[level]->GetCiphertextSize(plaintext_size); @@ -4729,7 +4826,7 @@ bool QuicFramer::AppendTypeByte(const QuicFrame& frame, bool last_frame_in_packet, QuicDataWriter* writer) { if (VersionHasIetfQuicFrames(version_.transport_version)) { - return AppendIetfTypeByte(frame, last_frame_in_packet, writer); + return AppendIetfFrameType(frame, last_frame_in_packet, writer); } uint8_t type_byte = 0; switch (frame.type) { @@ -4785,9 +4882,9 @@ bool QuicFramer::AppendTypeByte(const QuicFrame& frame, return writer->WriteUInt8(type_byte); } -bool QuicFramer::AppendIetfTypeByte(const QuicFrame& frame, - bool last_frame_in_packet, - QuicDataWriter* writer) { +bool QuicFramer::AppendIetfFrameType(const QuicFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* writer) { uint8_t type_byte = 0; switch (frame.type) { case PADDING_FRAME: @@ -4805,7 +4902,9 @@ bool QuicFramer::AppendIetfTypeByte(const QuicFrame& frame, type_byte = IETF_CONNECTION_CLOSE; break; default: - set_detailed_error("Invalid QuicConnectionCloseFrame type."); + set_detailed_error(quiche::QuicheStrCat( + "Invalid QuicConnectionCloseFrame type: ", + static_cast<int>(frame.connection_close_frame->close_type))); return RaiseError(QUIC_INTERNAL_ERROR); } break; @@ -4890,12 +4989,15 @@ bool QuicFramer::AppendIetfTypeByte(const QuicFrame& frame, case HANDSHAKE_DONE_FRAME: type_byte = IETF_HANDSHAKE_DONE; break; + case ACK_FREQUENCY_FRAME: + type_byte = IETF_ACK_FREQUENCY; + break; default: QUIC_BUG << "Attempt to generate a frame type for an unsupported value: " << frame.type; return false; } - return writer->WriteUInt8(type_byte); + return writer->WriteVarInt62(type_byte); } // static @@ -5107,6 +5209,29 @@ bool QuicFramer::AppendCryptoFrame(const QuicCryptoFrame& frame, return true; } +bool QuicFramer::AppendAckFrequencyFrame(const QuicAckFrequencyFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteVarInt62(frame.sequence_number)) { + set_detailed_error("Writing sequence number failed."); + return false; + } + if (!writer->WriteVarInt62(frame.packet_tolerance)) { + set_detailed_error("Writing packet tolerance failed."); + return false; + } + if (!writer->WriteVarInt62( + static_cast<uint64_t>(frame.max_ack_delay.ToMicroseconds()))) { + set_detailed_error("Writing max_ack_delay_us failed."); + return false; + } + if (!writer->WriteUInt8(static_cast<uint8_t>(frame.ignore_order))) { + set_detailed_error("Writing ignore_order failed."); + return false; + } + + return true; +} + void QuicFramer::set_version(const ParsedQuicVersion version) { DCHECK(IsSupportedVersion(version)) << ParsedQuicVersionToString(version); version_ = version; @@ -5365,7 +5490,7 @@ bool QuicFramer::AppendIetfAckFrameAndTypeByte(const QuicAckFrame& frame, QuicDataWriter::GetVarInt62Len(frame.ecn_ce_count)); } - if (!writer->WriteUInt8(type)) { + if (!writer->WriteVarInt62(type)) { set_detailed_error("No room for frame-type"); return false; } 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 8ace715d285..8e63fc25722 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 @@ -218,6 +218,9 @@ class QUIC_EXPORT_PRIVATE QuicFramerVisitorInterface { // Called when a handshake done frame has been parsed. virtual bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) = 0; + // Called when an AckFrequencyFrame has been parsed. + virtual bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) = 0; + // Called when a packet has been completely processed. virtual void OnPacketComplete() = 0; @@ -321,6 +324,8 @@ class QUIC_EXPORT_PRIVATE QuicFramer { // Size in bytes of all reset stream frame fields. static size_t GetRstStreamFrameSize(QuicTransportVersion version, const QuicRstStreamFrame& frame); + // Size in bytes of all ack frenquency frame fields. + static size_t GetAckFrequencyFrameSize(const QuicAckFrequencyFrame& frame); // Size in bytes of all connection close frame fields, including the error // details. static size_t GetConnectionCloseFrameSize( @@ -481,14 +486,16 @@ class QUIC_EXPORT_PRIVATE QuicFramer { bool AppendTypeByte(const QuicFrame& frame, bool last_frame_in_packet, QuicDataWriter* writer); - bool AppendIetfTypeByte(const QuicFrame& frame, - bool last_frame_in_packet, - QuicDataWriter* writer); + bool AppendIetfFrameType(const QuicFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* writer); size_t AppendIetfFrames(const QuicFrames& frames, QuicDataWriter* writer); bool AppendStreamFrame(const QuicStreamFrame& frame, bool last_frame_in_packet, QuicDataWriter* writer); bool AppendCryptoFrame(const QuicCryptoFrame& frame, QuicDataWriter* writer); + bool AppendAckFrequencyFrame(const QuicAckFrequencyFrame& frame, + QuicDataWriter* writer); // SetDecrypter sets the primary decrypter, replacing any that already exists. // If an alternative decrypter is in place then the function DCHECKs. This is @@ -571,6 +578,8 @@ class QUIC_EXPORT_PRIVATE QuicFramer { // Returns true if encrypter of |level| is available. bool HasEncrypterOfEncryptionLevel(EncryptionLevel level) const; + // Returns true if decrypter of |level| is available. + bool HasDecrypterOfEncryptionLevel(EncryptionLevel level) const; void set_validate_flags(bool value) { validate_flags_ = value; } @@ -915,7 +924,8 @@ class QUIC_EXPORT_PRIVATE QuicFramer { bool ProcessCryptoFrame(QuicDataReader* reader, EncryptionLevel encryption_level, QuicCryptoFrame* frame); - + bool ProcessAckFrequencyFrame(QuicDataReader* reader, + QuicAckFrequencyFrame* frame); // IETF frame appending methods. All methods append the type byte as well. bool AppendIetfStreamFrame(const QuicStreamFrame& frame, bool last_frame_in_packet, 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 fb9c2eb87bc..5a5a8fa1a8a 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 @@ -406,6 +406,15 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { return true; } + bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) override { + ++frame_count_; + ack_frequency_frames_.emplace_back( + std::make_unique<QuicAckFrequencyFrame>(frame)); + DCHECK(VersionHasIetfQuicFrames(transport_version_)); + EXPECT_EQ(IETF_ACK_FREQUENCY, framer_->current_received_frame_type()); + return true; + } + void OnPacketComplete() override { ++complete_packets_; } bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override { @@ -574,6 +583,7 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { std::vector<std::unique_ptr<QuicPingFrame>> ping_frames_; std::vector<std::unique_ptr<QuicMessageFrame>> message_frames_; std::vector<std::unique_ptr<QuicHandshakeDoneFrame>> handshake_done_frames_; + std::vector<std::unique_ptr<QuicAckFrequencyFrame>> ack_frequency_frames_; std::vector<std::unique_ptr<QuicEncryptedPacket>> coalesced_packets_; std::vector<std::unique_ptr<QuicEncryptedPacket>> undecryptable_packets_; std::vector<EncryptionLevel> undecryptable_decryption_levels_; @@ -5159,6 +5169,52 @@ TEST_P(QuicFramerTest, HandshakeDoneFrame) { EXPECT_EQ(1u, visitor_.handshake_done_frames_.size()); } +TEST_P(QuicFramerTest, ParseAckFrequencyFrame) { + SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); + // clang-format off + unsigned char packet[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // ack frequency frame type (which needs two bytes as it is > 0x3F) + 0x40, 0xAF, + // sequence_number + 0x11, + // packet_tolerance + 0x02, + // max_ack_delay_us = 2'5000 us + 0x80, 0x00, 0x61, 0xA8, + // ignore_order + 0x01 + }; + // clang-format on + + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + + QuicEncryptedPacket encrypted(AsChars(packet), QUICHE_ARRAYSIZE(packet), + false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_THAT(framer_.error(), IsQuicNoError()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + ASSERT_EQ(1u, visitor_.ack_frequency_frames_.size()); + const auto& frame = visitor_.ack_frequency_frames_.front(); + EXPECT_EQ(17u, frame->sequence_number); + EXPECT_EQ(2u, frame->packet_tolerance); + EXPECT_EQ(2'5000u, frame->max_ack_delay.ToMicroseconds()); + EXPECT_EQ(true, frame->ignore_order); +} + TEST_P(QuicFramerTest, MessageFrame) { if (!VersionSupportsMessageFrames(framer_.transport_version())) { return; @@ -8607,6 +8663,53 @@ TEST_P(QuicFramerTest, BuildHandshakeDonePacket) { QUICHE_ARRAYSIZE(packet)); } +TEST_P(QuicFramerTest, BuildAckFrequencyPacket) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicAckFrequencyFrame ack_frequency_frame; + ack_frequency_frame.sequence_number = 3; + ack_frequency_frame.packet_tolerance = 5; + ack_frequency_frame.max_ack_delay = QuicTime::Delta::FromMicroseconds(0x3fff); + ack_frequency_frame.ignore_order = false; + QuicFrames frames = {QuicFrame(&ack_frequency_frame)}; + + // clang-format off + unsigned char packet[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (Ack Frequency frame) + 0x40, 0xaf, + // sequence number + 0x03, + // packet tolerance + 0x05, + // max_ack_delay_us + 0x7f, 0xff, + // ignore_oder + 0x00 + }; + // clang-format on + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + quiche::test::CompareCharArraysWithHexError( + "constructed packet", data->data(), data->length(), AsChars(packet), + QUICHE_ARRAYSIZE(packet)); +} + TEST_P(QuicFramerTest, BuildMessagePacket) { if (!VersionSupportsMessageFrames(framer_.transport_version())) { return; @@ -9013,6 +9116,24 @@ TEST_P(QuicFramerTest, EncryptPacket) { EXPECT_TRUE(CheckEncryption(packet_number, raw.get())); } +// Regression test for b/158014497. +TEST_P(QuicFramerTest, EncryptEmptyPacket) { + auto packet = std::make_unique<QuicPacket>( + new char[100], 0, true, PACKET_8BYTE_CONNECTION_ID, + PACKET_0BYTE_CONNECTION_ID, + /*includes_version=*/true, + /*includes_diversification_nonce=*/true, PACKET_1BYTE_PACKET_NUMBER, + VARIABLE_LENGTH_INTEGER_LENGTH_0, + /*retry_token_length=*/0, VARIABLE_LENGTH_INTEGER_LENGTH_0); + char buffer[kMaxOutgoingPacketSize]; + size_t encrypted_length = 1; + EXPECT_QUIC_BUG(encrypted_length = framer_.EncryptPayload( + ENCRYPTION_INITIAL, kPacketNumber, *packet, buffer, + kMaxOutgoingPacketSize), + "packet is shorter than associated data length"); + EXPECT_EQ(0u, encrypted_length); +} + TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) { QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); QuicPacketNumber packet_number = kPacketNumber; 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 new file mode 100644 index 00000000000..816b8c79c50 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator.cc @@ -0,0 +1,145 @@ +// Copyright (c) 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h" +#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h" + +namespace quic { + +QuicLegacyVersionEncapsulator::QuicLegacyVersionEncapsulator( + QuicPacketBuffer packet_buffer) + : packet_buffer_(packet_buffer) {} + +QuicLegacyVersionEncapsulator::~QuicLegacyVersionEncapsulator() {} + +// static +QuicByteCount QuicLegacyVersionEncapsulator::GetMinimumOverhead( + quiche::QuicheStringPiece sni) { + // The number 52 is the sum of: + // - Flags (1 byte) + // - Server Connection ID (8 bytes) + // - Version (4 bytes) + // - Packet Number (1 byte) + // - Message Authentication Hash (12 bytes) + // - Frame Type (1 byte) + // - Stream ID (1 byte) + // - ClientHello tag (4 bytes) + // - ClientHello num tags (2 bytes) + // - Padding (2 bytes) + // - SNI tag (4 bytes) + // - SNI end offset (4 bytes) + // - QLVE tag (4 bytes) + // - QLVE end offset (4 bytes) + return 52 + sni.length(); +} + +QuicPacketBuffer QuicLegacyVersionEncapsulator::GetPacketBuffer() { + return packet_buffer_; +} + +void QuicLegacyVersionEncapsulator::OnSerializedPacket( + SerializedPacket serialized_packet) { + if (encrypted_length_ != 0) { + unrecoverable_failure_encountered_ = true; + QUIC_BUG << "OnSerializedPacket called twice"; + return; + } + if (serialized_packet.encrypted_length == 0) { + unrecoverable_failure_encountered_ = true; + QUIC_BUG << "OnSerializedPacket called with empty packet"; + return; + } + encrypted_length_ = serialized_packet.encrypted_length; +} + +void QuicLegacyVersionEncapsulator::OnUnrecoverableError( + QuicErrorCode error, + const std::string& error_details) { + unrecoverable_failure_encountered_ = true; + QUIC_BUG << "QuicLegacyVersionEncapsulator received error " << error << ": " + << error_details; +} + +bool QuicLegacyVersionEncapsulator::ShouldGeneratePacket( + HasRetransmittableData /*retransmittable*/, + IsHandshake /*handshake*/) { + return true; +} + +const QuicFrames +QuicLegacyVersionEncapsulator::MaybeBundleAckOpportunistically() { + // We do not want to ever include any ACKs here, return an empty array. + return QuicFrames(); +} + +// static +QuicPacketLength QuicLegacyVersionEncapsulator::Encapsulate( + quiche::QuicheStringPiece sni, + quiche::QuicheStringPiece inner_packet, + const QuicConnectionId& server_connection_id, + QuicTime creation_time, + QuicByteCount outer_max_packet_length, + char* out) { + if (outer_max_packet_length > kMaxOutgoingPacketSize) { + outer_max_packet_length = kMaxOutgoingPacketSize; + } + CryptoHandshakeMessage outer_chlo; + outer_chlo.set_tag(kCHLO); + outer_chlo.SetStringPiece(kSNI, sni); + outer_chlo.SetStringPiece(kQLVE, inner_packet); + const QuicData& serialized_outer_chlo = outer_chlo.GetSerialized(); + DCHECK(!LegacyVersionForEncapsulation().UsesCryptoFrames()); + DCHECK(LegacyVersionForEncapsulation().UsesQuicCrypto()); + QuicStreamFrame outer_stream_frame( + QuicUtils::GetCryptoStreamId( + LegacyVersionForEncapsulation().transport_version), + /*fin=*/false, + /*offset=*/0, serialized_outer_chlo.AsStringPiece()); + QuicFramer outer_framer( + ParsedQuicVersionVector{LegacyVersionForEncapsulation()}, creation_time, + Perspective::IS_CLIENT, kQuicDefaultConnectionIdLength); + outer_framer.SetInitialObfuscators(server_connection_id); + char outer_encrypted_packet[kMaxOutgoingPacketSize]; + QuicPacketBuffer outer_packet_buffer(outer_encrypted_packet, nullptr); + QuicLegacyVersionEncapsulator creator_delegate(outer_packet_buffer); + QuicPacketCreator outer_creator(server_connection_id, &outer_framer, + &creator_delegate); + outer_creator.SetMaxPacketLength(outer_max_packet_length); + outer_creator.set_encryption_level(ENCRYPTION_INITIAL); + outer_creator.SetTransmissionType(NOT_RETRANSMISSION); + if (!outer_creator.AddPaddedSavedFrame(QuicFrame(outer_stream_frame), + NOT_RETRANSMISSION)) { + QUIC_BUG << "Failed to add Legacy Version Encapsulation stream frame " + "(max packet length is " + << outer_creator.max_packet_length() << ") " << outer_stream_frame; + return 0; + } + outer_creator.FlushCurrentPacket(); + const QuicPacketLength encrypted_length = creator_delegate.encrypted_length_; + if (creator_delegate.unrecoverable_failure_encountered_ || + encrypted_length == 0) { + QUIC_BUG << "Failed to perform Legacy Version Encapsulation of " + << inner_packet.length() << " bytes"; + return 0; + } + if (encrypted_length > kMaxOutgoingPacketSize) { + QUIC_BUG << "Legacy Version Encapsulation outer creator generated a " + "packet with unexpected length " + << encrypted_length; + return 0; + } + + QUIC_DLOG(INFO) << "Successfully performed Legacy Version Encapsulation from " + << inner_packet.length() << " bytes to " << encrypted_length; + + // Replace our current packet with the encapsulated one. + memcpy(out, outer_encrypted_packet, encrypted_length); + return encrypted_length; +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator.h b/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator.h new file mode 100644 index 00000000000..3d9c1564506 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator.h @@ -0,0 +1,71 @@ +// Copyright (c) 2020 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_LEGACY_VERSION_ENCAPSULATOR_H_ +#define QUICHE_QUIC_CORE_QUIC_LEGACY_VERSION_ENCAPSULATOR_H_ + +#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" + +namespace quic { + +// QuicLegacyVersionEncapsulator is responsible for encapsulation of packets +// using Legacy Version Encapsulation. + +class QUIC_EXPORT_PRIVATE QuicLegacyVersionEncapsulator + : public QuicPacketCreator::DelegateInterface { + public: + // Encapsulates |inner_packet| into a new encapsulated packet that uses a + // CHLO of version LegacyVersionForEncapsulation() with server name |sni| + // exposed and using |server_connection_id|. The packet will be padded up to + // |outer_max_packet_length| bytes if necessary. On failure, returns 0. On + // success, returns the length of the outer encapsulated packet, and copies + // the contents of the encapsulated packet to |out|. |out| must point to a + // valid memory buffer capable of holding kMaxOutgoingPacketSize bytes. + static QuicPacketLength Encapsulate( + quiche::QuicheStringPiece sni, + quiche::QuicheStringPiece inner_packet, + const QuicConnectionId& server_connection_id, + QuicTime creation_time, + QuicByteCount outer_max_packet_length, + char* out); + + // Returns the number of bytes of minimum overhead caused by Legacy Version + // Encapsulation, based on the length of the provided server name |sni|. + // The overhead may be higher due to extra padding added. + static QuicByteCount GetMinimumOverhead(quiche::QuicheStringPiece sni); + + // Overrides for QuicPacketCreator::DelegateInterface. + QuicPacketBuffer GetPacketBuffer() override; + void OnSerializedPacket(SerializedPacket serialized_packet) override; + void OnUnrecoverableError(QuicErrorCode error, + const std::string& error_details) override; + bool ShouldGeneratePacket(HasRetransmittableData retransmittable, + IsHandshake handshake) override; + const QuicFrames MaybeBundleAckOpportunistically() override; + + ~QuicLegacyVersionEncapsulator() override; + + private: + explicit QuicLegacyVersionEncapsulator(QuicPacketBuffer packet_buffer); + + // Disallow copy, move and assignment. + QuicLegacyVersionEncapsulator(const QuicLegacyVersionEncapsulator&) = delete; + QuicLegacyVersionEncapsulator(QuicLegacyVersionEncapsulator&&) = delete; + QuicLegacyVersionEncapsulator& operator=( + const QuicLegacyVersionEncapsulator&) = delete; + QuicLegacyVersionEncapsulator& operator=(QuicLegacyVersionEncapsulator&&) = + delete; + + QuicPacketBuffer packet_buffer_; + QuicPacketLength encrypted_length_ = 0; + bool unrecoverable_failure_encountered_ = false; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_LEGACY_VERSION_ENCAPSULATOR_H_ 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 new file mode 100644 index 00000000000..a71b2f44eb5 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator_test.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator.h" + +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h" + +namespace quic { +namespace test { +namespace { + +class QuicLegacyVersionEncapsulatorTest + : public QuicTestWithParam<ParsedQuicVersion> { + protected: + QuicLegacyVersionEncapsulatorTest() + : version_(GetParam()), + sni_("test.example.org"), + server_connection_id_(TestConnectionId()), + outer_max_packet_length_(kMaxOutgoingPacketSize), + encapsulated_length_(0) {} + + void Encapsulate(const std::string& inner_packet) { + encapsulated_length_ = QuicLegacyVersionEncapsulator::Encapsulate( + sni_, inner_packet, server_connection_id_, QuicTime::Zero(), + outer_max_packet_length_, outer_buffer_); + } + + void CheckEncapsulation() { + ASSERT_NE(encapsulated_length_, 0u); + ASSERT_EQ(encapsulated_length_, outer_max_packet_length_); + // Verify that the encapsulated packet parses as encapsulated. + PacketHeaderFormat format = IETF_QUIC_LONG_HEADER_PACKET; + QuicLongHeaderType long_packet_type; + bool version_present; + bool has_length_prefix; + QuicVersionLabel version_label; + ParsedQuicVersion parsed_version = ParsedQuicVersion::Unsupported(); + QuicConnectionId destination_connection_id, source_connection_id; + bool retry_token_present; + quiche::QuicheStringPiece retry_token; + std::string detailed_error; + const QuicErrorCode error = QuicFramer::ParsePublicHeaderDispatcher( + QuicEncryptedPacket(outer_buffer_, encapsulated_length_), + kQuicDefaultConnectionIdLength, &format, &long_packet_type, + &version_present, &has_length_prefix, &version_label, &parsed_version, + &destination_connection_id, &source_connection_id, &retry_token_present, + &retry_token, &detailed_error); + ASSERT_THAT(error, IsQuicNoError()) << detailed_error; + EXPECT_EQ(format, GOOGLE_QUIC_PACKET); + EXPECT_TRUE(version_present); + EXPECT_FALSE(has_length_prefix); + EXPECT_EQ(parsed_version, LegacyVersionForEncapsulation()); + EXPECT_EQ(destination_connection_id, server_connection_id_); + EXPECT_EQ(source_connection_id, EmptyQuicConnectionId()); + EXPECT_FALSE(retry_token_present); + EXPECT_TRUE(detailed_error.empty()); + } + + QuicByteCount Overhead() { + return QuicLegacyVersionEncapsulator::GetMinimumOverhead(sni_); + } + + ParsedQuicVersion version_; + std::string sni_; + QuicConnectionId server_connection_id_; + QuicByteCount outer_max_packet_length_; + char outer_buffer_[kMaxOutgoingPacketSize]; + QuicPacketLength encapsulated_length_; +}; + +INSTANTIATE_TEST_SUITE_P(QuicLegacyVersionEncapsulatorTests, + QuicLegacyVersionEncapsulatorTest, + ::testing::ValuesIn(AllSupportedVersions()), + ::testing::PrintToStringParamName()); + +TEST_P(QuicLegacyVersionEncapsulatorTest, Simple) { + Encapsulate("TEST_INNER_PACKET"); + CheckEncapsulation(); +} + +TEST_P(QuicLegacyVersionEncapsulatorTest, TooBig) { + std::string inner_packet(kMaxOutgoingPacketSize, '?'); + EXPECT_QUIC_BUG(Encapsulate(inner_packet), "Legacy Version Encapsulation"); + ASSERT_EQ(encapsulated_length_, 0u); +} + +TEST_P(QuicLegacyVersionEncapsulatorTest, BarelyFits) { + QuicByteCount inner_size = kMaxOutgoingPacketSize - Overhead(); + std::string inner_packet(inner_size, '?'); + Encapsulate(inner_packet); + CheckEncapsulation(); +} + +TEST_P(QuicLegacyVersionEncapsulatorTest, DoesNotQuiteFit) { + QuicByteCount inner_size = 1 + kMaxOutgoingPacketSize - Overhead(); + std::string inner_packet(inner_size, '?'); + EXPECT_QUIC_BUG(Encapsulate(inner_packet), "Legacy Version Encapsulation"); + ASSERT_EQ(encapsulated_length_, 0u); +} + +} // namespace +} // namespace test +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils.cc b/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils.cc new file mode 100644 index 00000000000..6dd74c3b19c --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils.cc @@ -0,0 +1,314 @@ +// 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 "net/third_party/quiche/src/quic/core/quic_linux_socket_utils.h" + +#include <linux/net_tstamp.h> +#include <netinet/in.h> +#include <cstdint> + +#include "net/third_party/quiche/src/quic/core/quic_syscall_wrapper.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" + +namespace quic { + +QuicMsgHdr::QuicMsgHdr(const char* buffer, + size_t buf_len, + const QuicSocketAddress& peer_address, + char* cbuf, + size_t cbuf_size) + : iov_{const_cast<char*>(buffer), buf_len}, + cbuf_(cbuf), + cbuf_size_(cbuf_size), + cmsg_(nullptr) { + // Only support unconnected sockets. + DCHECK(peer_address.IsInitialized()); + + raw_peer_address_ = peer_address.generic_address(); + hdr_.msg_name = &raw_peer_address_; + hdr_.msg_namelen = raw_peer_address_.ss_family == AF_INET + ? sizeof(sockaddr_in) + : sizeof(sockaddr_in6); + + hdr_.msg_iov = &iov_; + hdr_.msg_iovlen = 1; + hdr_.msg_flags = 0; + + hdr_.msg_control = nullptr; + hdr_.msg_controllen = 0; +} + +void QuicMsgHdr::SetIpInNextCmsg(const QuicIpAddress& self_address) { + if (!self_address.IsInitialized()) { + return; + } + + if (self_address.IsIPv4()) { + QuicLinuxSocketUtils::SetIpInfoInCmsgData( + self_address, GetNextCmsgData<in_pktinfo>(IPPROTO_IP, IP_PKTINFO)); + } else { + QuicLinuxSocketUtils::SetIpInfoInCmsgData( + self_address, GetNextCmsgData<in6_pktinfo>(IPPROTO_IPV6, IPV6_PKTINFO)); + } +} + +void* QuicMsgHdr::GetNextCmsgDataInternal(int cmsg_level, + int cmsg_type, + size_t data_size) { + // msg_controllen needs to be increased first, otherwise CMSG_NXTHDR will + // return nullptr. + hdr_.msg_controllen += CMSG_SPACE(data_size); + DCHECK_LE(hdr_.msg_controllen, cbuf_size_); + + if (cmsg_ == nullptr) { + DCHECK_EQ(nullptr, hdr_.msg_control); + memset(cbuf_, 0, cbuf_size_); + hdr_.msg_control = cbuf_; + cmsg_ = CMSG_FIRSTHDR(&hdr_); + } else { + DCHECK_NE(nullptr, hdr_.msg_control); + cmsg_ = CMSG_NXTHDR(&hdr_, cmsg_); + } + + DCHECK_NE(nullptr, cmsg_) << "Insufficient control buffer space"; + + cmsg_->cmsg_len = CMSG_LEN(data_size); + cmsg_->cmsg_level = cmsg_level; + cmsg_->cmsg_type = cmsg_type; + + return CMSG_DATA(cmsg_); +} + +void QuicMMsgHdr::InitOneHeader(int i, const BufferedWrite& buffered_write) { + mmsghdr* mhdr = GetMMsgHdr(i); + msghdr* hdr = &mhdr->msg_hdr; + iovec* iov = GetIov(i); + + iov->iov_base = const_cast<char*>(buffered_write.buffer); + iov->iov_len = buffered_write.buf_len; + hdr->msg_iov = iov; + hdr->msg_iovlen = 1; + hdr->msg_control = nullptr; + hdr->msg_controllen = 0; + + // Only support unconnected sockets. + DCHECK(buffered_write.peer_address.IsInitialized()); + + sockaddr_storage* peer_address_storage = GetPeerAddressStorage(i); + *peer_address_storage = buffered_write.peer_address.generic_address(); + hdr->msg_name = peer_address_storage; + hdr->msg_namelen = peer_address_storage->ss_family == AF_INET + ? sizeof(sockaddr_in) + : sizeof(sockaddr_in6); +} + +void QuicMMsgHdr::SetIpInNextCmsg(int i, const QuicIpAddress& self_address) { + if (!self_address.IsInitialized()) { + return; + } + + if (self_address.IsIPv4()) { + QuicLinuxSocketUtils::SetIpInfoInCmsgData( + self_address, GetNextCmsgData<in_pktinfo>(i, IPPROTO_IP, IP_PKTINFO)); + } else { + QuicLinuxSocketUtils::SetIpInfoInCmsgData( + self_address, + GetNextCmsgData<in6_pktinfo>(i, IPPROTO_IPV6, IPV6_PKTINFO)); + } +} + +void* QuicMMsgHdr::GetNextCmsgDataInternal(int i, + int cmsg_level, + int cmsg_type, + size_t data_size) { + mmsghdr* mhdr = GetMMsgHdr(i); + msghdr* hdr = &mhdr->msg_hdr; + cmsghdr*& cmsg = *GetCmsgHdr(i); + + // msg_controllen needs to be increased first, otherwise CMSG_NXTHDR will + // return nullptr. + hdr->msg_controllen += CMSG_SPACE(data_size); + DCHECK_LE(hdr->msg_controllen, cbuf_size_); + + if (cmsg == nullptr) { + DCHECK_EQ(nullptr, hdr->msg_control); + hdr->msg_control = GetCbuf(i); + cmsg = CMSG_FIRSTHDR(hdr); + } else { + DCHECK_NE(nullptr, hdr->msg_control); + cmsg = CMSG_NXTHDR(hdr, cmsg); + } + + DCHECK_NE(nullptr, cmsg) << "Insufficient control buffer space"; + + cmsg->cmsg_len = CMSG_LEN(data_size); + cmsg->cmsg_level = cmsg_level; + cmsg->cmsg_type = cmsg_type; + + return CMSG_DATA(cmsg); +} + +int QuicMMsgHdr::num_bytes_sent(int num_packets_sent) { + DCHECK_LE(0, num_packets_sent); + DCHECK_LE(num_packets_sent, num_msgs_); + + int bytes_sent = 0; + iovec* iov = GetIov(0); + for (int i = 0; i < num_packets_sent; ++i) { + bytes_sent += iov[i].iov_len; + } + return bytes_sent; +} + +// static +int QuicLinuxSocketUtils::GetUDPSegmentSize(int fd) { + int optval; + socklen_t optlen = sizeof(optval); + int rc = getsockopt(fd, SOL_UDP, UDP_SEGMENT, &optval, &optlen); + if (rc < 0) { + QUIC_LOG_EVERY_N_SEC(INFO, 10) + << "getsockopt(UDP_SEGMENT) failed: " << strerror(errno); + return -1; + } + QUIC_LOG_EVERY_N_SEC(INFO, 10) + << "getsockopt(UDP_SEGMENT) returned segment size: " << optval; + return optval; +} + +// static +bool QuicLinuxSocketUtils::EnableReleaseTime(int fd, clockid_t clockid) { + // TODO(wub): Change to sock_txtime once it is available in linux/net_tstamp.h + struct LinuxSockTxTime { + clockid_t clockid; /* reference clockid */ + uint32_t flags; /* flags defined by enum txtime_flags */ + }; + + LinuxSockTxTime so_txtime_val{clockid, 0}; + + if (setsockopt(fd, SOL_SOCKET, SO_TXTIME, &so_txtime_val, + sizeof(so_txtime_val)) != 0) { + QUIC_LOG_EVERY_N_SEC(INFO, 10) + << "setsockopt(SOL_SOCKET,SO_TXTIME) failed: " << strerror(errno); + return false; + } + + return true; +} + +// static +bool QuicLinuxSocketUtils::GetTtlFromMsghdr(struct msghdr* hdr, int* ttl) { + if (hdr->msg_controllen > 0) { + struct cmsghdr* cmsg; + for (cmsg = CMSG_FIRSTHDR(hdr); cmsg != nullptr; + cmsg = CMSG_NXTHDR(hdr, cmsg)) { + if ((cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) || + (cmsg->cmsg_level == IPPROTO_IPV6 && + cmsg->cmsg_type == IPV6_HOPLIMIT)) { + *ttl = *(reinterpret_cast<int*>(CMSG_DATA(cmsg))); + return true; + } + } + } + return false; +} + +// static +void QuicLinuxSocketUtils::SetIpInfoInCmsgData( + const QuicIpAddress& self_address, + void* cmsg_data) { + DCHECK(self_address.IsInitialized()); + const std::string& address_str = self_address.ToPackedString(); + if (self_address.IsIPv4()) { + in_pktinfo* pktinfo = static_cast<in_pktinfo*>(cmsg_data); + pktinfo->ipi_ifindex = 0; + memcpy(&pktinfo->ipi_spec_dst, address_str.c_str(), address_str.length()); + } else if (self_address.IsIPv6()) { + in6_pktinfo* pktinfo = static_cast<in6_pktinfo*>(cmsg_data); + memcpy(&pktinfo->ipi6_addr, address_str.c_str(), address_str.length()); + } else { + QUIC_BUG << "Unrecognized IPAddress"; + } +} + +// static +size_t QuicLinuxSocketUtils::SetIpInfoInCmsg(const QuicIpAddress& self_address, + cmsghdr* cmsg) { + std::string address_string; + if (self_address.IsIPv4()) { + cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo)); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)); + memset(pktinfo, 0, sizeof(in_pktinfo)); + pktinfo->ipi_ifindex = 0; + address_string = self_address.ToPackedString(); + memcpy(&pktinfo->ipi_spec_dst, address_string.c_str(), + address_string.length()); + return sizeof(in_pktinfo); + } else if (self_address.IsIPv6()) { + cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo)); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)); + memset(pktinfo, 0, sizeof(in6_pktinfo)); + address_string = self_address.ToPackedString(); + memcpy(&pktinfo->ipi6_addr, address_string.c_str(), + address_string.length()); + return sizeof(in6_pktinfo); + } else { + QUIC_BUG << "Unrecognized IPAddress"; + return 0; + } +} + +// static +WriteResult QuicLinuxSocketUtils::WritePacket(int fd, const QuicMsgHdr& hdr) { + int rc; + do { + rc = GetGlobalSyscallWrapper()->Sendmsg(fd, hdr.hdr(), 0); + } while (rc < 0 && errno == EINTR); + if (rc >= 0) { + return WriteResult(WRITE_STATUS_OK, rc); + } + return WriteResult((errno == EAGAIN || errno == EWOULDBLOCK) + ? WRITE_STATUS_BLOCKED + : WRITE_STATUS_ERROR, + errno); +} + +// static +WriteResult QuicLinuxSocketUtils::WriteMultiplePackets(int fd, + QuicMMsgHdr* mhdr, + int* num_packets_sent) { + *num_packets_sent = 0; + + if (mhdr->num_msgs() <= 0) { + return WriteResult(WRITE_STATUS_ERROR, EINVAL); + } + + int rc; + do { + rc = GetGlobalSyscallWrapper()->Sendmmsg(fd, mhdr->mhdr(), mhdr->num_msgs(), + 0); + } while (rc < 0 && errno == EINTR); + + if (rc > 0) { + *num_packets_sent = rc; + + return WriteResult(WRITE_STATUS_OK, mhdr->num_bytes_sent(rc)); + } else if (rc == 0) { + QUIC_BUG << "sendmmsg returned 0, returning WRITE_STATUS_ERROR. errno: " + << errno; + errno = EIO; + } + + return WriteResult((errno == EAGAIN || errno == EWOULDBLOCK) + ? WRITE_STATUS_BLOCKED + : WRITE_STATUS_ERROR, + errno); +} + +} // namespace quic 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 new file mode 100644 index 00000000000..40916579fad --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils.h @@ -0,0 +1,298 @@ +// 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_PLATFORM_IMPL_QUIC_LINUX_SOCKET_UTILS_H_ +#define QUICHE_QUIC_PLATFORM_IMPL_QUIC_LINUX_SOCKET_UTILS_H_ + +#include <errno.h> +#include <stddef.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <deque> +#include <functional> +#include <iterator> +#include <memory> +#include <type_traits> +#include <utility> + +#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" + +#ifndef SOL_UDP +#define SOL_UDP 17 +#endif + +#ifndef UDP_SEGMENT +#define UDP_SEGMENT 103 +#endif + +#ifndef UDP_MAX_SEGMENTS +#define UDP_MAX_SEGMENTS (1 << 6UL) +#endif + +#ifndef SO_TXTIME +#define SO_TXTIME 61 +#endif + +namespace quic { + +const int kCmsgSpaceForIpv4 = CMSG_SPACE(sizeof(in_pktinfo)); +const int kCmsgSpaceForIpv6 = CMSG_SPACE(sizeof(in6_pktinfo)); +// kCmsgSpaceForIp should be big enough to hold both IPv4 and IPv6 packet info. +const int kCmsgSpaceForIp = (kCmsgSpaceForIpv4 < kCmsgSpaceForIpv6) + ? kCmsgSpaceForIpv6 + : kCmsgSpaceForIpv4; + +const int kCmsgSpaceForSegmentSize = CMSG_SPACE(sizeof(uint16_t)); + +const int kCmsgSpaceForTxTime = CMSG_SPACE(sizeof(uint64_t)); + +const int kCmsgSpaceForTTL = CMSG_SPACE(sizeof(int)); + +// QuicMsgHdr is used to build msghdr objects that can be used send packets via +// ::sendmsg. +// +// Example: +// // cbuf holds control messages(cmsgs). The size is determined from what +// // cmsgs will be set for this msghdr. +// char cbuf[kCmsgSpaceForIp + kCmsgSpaceForSegmentSize]; +// QuicMsgHdr hdr(packet_buf, packet_buf_len, peer_addr, cbuf, sizeof(cbuf)); +// +// // Set IP in cmsgs. +// hdr.SetIpInNextCmsg(self_addr); +// +// // Set GSO size in cmsgs. +// *hdr.GetNextCmsgData<uint16_t>(SOL_UDP, UDP_SEGMENT) = 1200; +// +// QuicLinuxSocketUtils::WritePacket(fd, hdr); +class QUIC_EXPORT_PRIVATE QuicMsgHdr { + public: + QuicMsgHdr(const char* buffer, + size_t buf_len, + const QuicSocketAddress& peer_address, + char* cbuf, + size_t cbuf_size); + + // Set IP info in the next cmsg. Both IPv4 and IPv6 are supported. + void SetIpInNextCmsg(const QuicIpAddress& self_address); + + template <typename DataType> + DataType* GetNextCmsgData(int cmsg_level, int cmsg_type) { + return reinterpret_cast<DataType*>( + GetNextCmsgDataInternal(cmsg_level, cmsg_type, sizeof(DataType))); + } + + const msghdr* hdr() const { return &hdr_; } + + protected: + void* GetNextCmsgDataInternal(int cmsg_level, + int cmsg_type, + size_t data_size); + + msghdr hdr_; + iovec iov_; + sockaddr_storage raw_peer_address_; + char* cbuf_; + const size_t cbuf_size_; + // The last cmsg populated so far. nullptr means nothing has been populated. + cmsghdr* cmsg_; +}; + +// BufferedWrite holds all information needed to send a packet. +struct QUIC_EXPORT_PRIVATE BufferedWrite { + BufferedWrite(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address) + : BufferedWrite(buffer, + buf_len, + self_address, + peer_address, + std::unique_ptr<PerPacketOptions>(), + /*release_time=*/0) {} + + BufferedWrite(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, + std::unique_ptr<PerPacketOptions> options, + uint64_t release_time) + : buffer(buffer), + buf_len(buf_len), + self_address(self_address), + peer_address(peer_address), + options(std::move(options)), + release_time(release_time) {} + + const char* buffer; // Not owned. + size_t buf_len; + QuicIpAddress self_address; + QuicSocketAddress peer_address; + std::unique_ptr<PerPacketOptions> options; + + // The release time according to the owning packet writer's clock, which is + // often not a QuicClock. Calculated from packet writer's Now() and the + // release time delay in |options|. + // 0 means it can be sent at the same time as the previous packet in a batch, + // or can be sent Now() if this is the first packet of a batch. + uint64_t release_time; +}; + +// QuicMMsgHdr is used to build mmsghdr objects that can be used to send +// multiple packets at once via ::sendmmsg. +// +// Example: +// QuicCircularDeque<BufferedWrite> buffered_writes; +// ... (Populate buffered_writes) ... +// +// QuicMMsgHdr mhdr( +// buffered_writes.begin(), buffered_writes.end(), kCmsgSpaceForIp, +// [](QuicMMsgHdr* mhdr, int i, const BufferedWrite& buffered_write) { +// mhdr->SetIpInNextCmsg(i, buffered_write.self_address); +// }); +// +// int num_packets_sent; +// QuicSocketUtils::WriteMultiplePackets(fd, &mhdr, &num_packets_sent); +class QUIC_EXPORT_PRIVATE QuicMMsgHdr { + public: + typedef std::function< + void(QuicMMsgHdr* mhdr, int i, const BufferedWrite& buffered_write)> + ControlBufferInitializer; + template <typename IteratorT> + QuicMMsgHdr(const IteratorT& first, + const IteratorT& last, + size_t cbuf_size, + ControlBufferInitializer cbuf_initializer) + : num_msgs_(std::distance(first, last)), cbuf_size_(cbuf_size) { + static_assert( + std::is_same<typename std::iterator_traits<IteratorT>::value_type, + BufferedWrite>::value, + "Must iterate over a collection of BufferedWrite."); + + DCHECK_LE(0, num_msgs_); + if (num_msgs_ == 0) { + return; + } + + storage_.reset(new char[StorageSize()]); + memset(&storage_[0], 0, StorageSize()); + + int i = -1; + for (auto it = first; it != last; ++it) { + ++i; + + InitOneHeader(i, *it); + if (cbuf_initializer) { + cbuf_initializer(this, i, *it); + } + } + } + + void SetIpInNextCmsg(int i, const QuicIpAddress& self_address); + + template <typename DataType> + DataType* GetNextCmsgData(int i, int cmsg_level, int cmsg_type) { + return reinterpret_cast<DataType*>( + GetNextCmsgDataInternal(i, cmsg_level, cmsg_type, sizeof(DataType))); + } + + mmsghdr* mhdr() { return GetMMsgHdr(0); } + + int num_msgs() const { return num_msgs_; } + + // Get the total number of bytes in the first |num_packets_sent| packets. + int num_bytes_sent(int num_packets_sent); + + protected: + void InitOneHeader(int i, const BufferedWrite& buffered_write); + + void* GetNextCmsgDataInternal(int i, + int cmsg_level, + int cmsg_type, + size_t data_size); + + size_t StorageSize() const { + return num_msgs_ * + (sizeof(mmsghdr) + sizeof(iovec) + sizeof(sockaddr_storage) + + sizeof(cmsghdr*) + cbuf_size_); + } + + mmsghdr* GetMMsgHdr(int i) { + auto* first = reinterpret_cast<mmsghdr*>(&storage_[0]); + return &first[i]; + } + + iovec* GetIov(int i) { + auto* first = reinterpret_cast<iovec*>(GetMMsgHdr(num_msgs_)); + return &first[i]; + } + + sockaddr_storage* GetPeerAddressStorage(int i) { + auto* first = reinterpret_cast<sockaddr_storage*>(GetIov(num_msgs_)); + return &first[i]; + } + + cmsghdr** GetCmsgHdr(int i) { + auto* first = reinterpret_cast<cmsghdr**>(GetPeerAddressStorage(num_msgs_)); + return &first[i]; + } + + char* GetCbuf(int i) { + auto* first = reinterpret_cast<char*>(GetCmsgHdr(num_msgs_)); + return &first[i * cbuf_size_]; + } + + const int num_msgs_; + // Size of cmsg buffer for each message. + const size_t cbuf_size_; + // storage_ holds the memory of + // |num_msgs_| mmsghdr + // |num_msgs_| iovec + // |num_msgs_| sockaddr_storage, for peer addresses + // |num_msgs_| cmsghdr* + // |num_msgs_| cbuf, each of size cbuf_size + std::unique_ptr<char[]> storage_; +}; + +class QUIC_EXPORT_PRIVATE QuicLinuxSocketUtils { + public: + // Return the UDP segment size of |fd|, 0 means segment size has not been set + // on this socket. If GSO is not supported, return -1. + static int GetUDPSegmentSize(int fd); + + // Enable release time on |fd|. + static bool EnableReleaseTime(int fd, clockid_t clockid); + + // If the msghdr contains an IP_TTL entry, this will set ttl to the correct + // value and return true. Otherwise it will return false. + static bool GetTtlFromMsghdr(struct msghdr* hdr, int* ttl); + + // Set IP(self_address) in |cmsg_data|. Does not touch other fields in the + // containing cmsghdr. + static void SetIpInfoInCmsgData(const QuicIpAddress& self_address, + void* cmsg_data); + + // A helper for WritePacket which fills in the cmsg with the supplied self + // address. + // Returns the length of the packet info structure used. + static size_t SetIpInfoInCmsg(const QuicIpAddress& self_address, + cmsghdr* cmsg); + + // Writes the packet in |hdr| to the socket, using ::sendmsg. + static WriteResult WritePacket(int fd, const QuicMsgHdr& hdr); + + // Writes the packets in |mhdr| to the socket, using ::sendmmsg if available. + static WriteResult WriteMultiplePackets(int fd, + QuicMMsgHdr* mhdr, + int* num_packets_sent); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_PLATFORM_IMPL_QUIC_LINUX_SOCKET_UTILS_H_ 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 new file mode 100644 index 00000000000..32ad12f1473 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils_test.cc @@ -0,0 +1,329 @@ +// 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 "net/third_party/quiche/src/quic/core/quic_linux_socket_utils.h" + +#include <netinet/in.h> +#include <stdint.h> +#include <cstddef> +#include <sstream> +#include <vector> + +#include <string> + +#include "net/third_party/quiche/src/quic/core/quic_circular_deque.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_mock_syscall_wrapper.h" + +using testing::_; +using testing::InSequence; +using testing::Invoke; + +namespace quic { +namespace test { +namespace { + +class QuicLinuxSocketUtilsTest : public QuicTest { + protected: + WriteResult TestWriteMultiplePackets( + int fd, + const QuicCircularDeque<BufferedWrite>::const_iterator& first, + const QuicCircularDeque<BufferedWrite>::const_iterator& last, + int* num_packets_sent) { + QuicMMsgHdr mhdr( + first, last, kCmsgSpaceForIp, + [](QuicMMsgHdr* mhdr, int i, const BufferedWrite& buffered_write) { + mhdr->SetIpInNextCmsg(i, buffered_write.self_address); + }); + + WriteResult res = + QuicLinuxSocketUtils::WriteMultiplePackets(fd, &mhdr, num_packets_sent); + return res; + } + + MockQuicSyscallWrapper mock_syscalls_; + ScopedGlobalSyscallWrapperOverride syscall_override_{&mock_syscalls_}; +}; + +void CheckIpAndTtlInCbuf(msghdr* hdr, + const void* cbuf, + const QuicIpAddress& self_addr, + int ttl) { + const bool is_ipv4 = self_addr.IsIPv4(); + const size_t ip_cmsg_space = is_ipv4 ? kCmsgSpaceForIpv4 : kCmsgSpaceForIpv6; + + EXPECT_EQ(cbuf, hdr->msg_control); + EXPECT_EQ(ip_cmsg_space + CMSG_SPACE(sizeof(uint16_t)), hdr->msg_controllen); + + cmsghdr* cmsg = CMSG_FIRSTHDR(hdr); + EXPECT_EQ(cmsg->cmsg_len, is_ipv4 ? CMSG_LEN(sizeof(in_pktinfo)) + : CMSG_LEN(sizeof(in6_pktinfo))); + EXPECT_EQ(cmsg->cmsg_level, is_ipv4 ? IPPROTO_IP : IPPROTO_IPV6); + EXPECT_EQ(cmsg->cmsg_type, is_ipv4 ? IP_PKTINFO : IPV6_PKTINFO); + + const std::string& self_addr_str = self_addr.ToPackedString(); + if (is_ipv4) { + in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)); + EXPECT_EQ(0, memcmp(&pktinfo->ipi_spec_dst, self_addr_str.c_str(), + self_addr_str.length())); + } else { + in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)); + EXPECT_EQ(0, memcmp(&pktinfo->ipi6_addr, self_addr_str.c_str(), + self_addr_str.length())); + } + + cmsg = CMSG_NXTHDR(hdr, cmsg); + EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int))); + EXPECT_EQ(cmsg->cmsg_level, is_ipv4 ? IPPROTO_IP : IPPROTO_IPV6); + EXPECT_EQ(cmsg->cmsg_type, is_ipv4 ? IP_TTL : IPV6_HOPLIMIT); + EXPECT_EQ(ttl, *reinterpret_cast<int*>(CMSG_DATA(cmsg))); + + EXPECT_EQ(nullptr, CMSG_NXTHDR(hdr, cmsg)); +} + +void CheckMsghdrWithoutCbuf(const msghdr* hdr, + const void* buffer, + size_t buf_len, + const QuicSocketAddress& peer_addr) { + EXPECT_EQ( + peer_addr.host().IsIPv4() ? sizeof(sockaddr_in) : sizeof(sockaddr_in6), + hdr->msg_namelen); + sockaddr_storage peer_generic_addr = peer_addr.generic_address(); + EXPECT_EQ(0, memcmp(hdr->msg_name, &peer_generic_addr, hdr->msg_namelen)); + EXPECT_EQ(1u, hdr->msg_iovlen); + EXPECT_EQ(buffer, hdr->msg_iov->iov_base); + EXPECT_EQ(buf_len, hdr->msg_iov->iov_len); + EXPECT_EQ(0, hdr->msg_flags); + EXPECT_EQ(nullptr, hdr->msg_control); + EXPECT_EQ(0u, hdr->msg_controllen); +} + +void CheckIpAndGsoSizeInCbuf(msghdr* hdr, + const void* cbuf, + const QuicIpAddress& self_addr, + uint16_t gso_size) { + const bool is_ipv4 = self_addr.IsIPv4(); + const size_t ip_cmsg_space = is_ipv4 ? kCmsgSpaceForIpv4 : kCmsgSpaceForIpv6; + + EXPECT_EQ(cbuf, hdr->msg_control); + EXPECT_EQ(ip_cmsg_space + CMSG_SPACE(sizeof(uint16_t)), hdr->msg_controllen); + + cmsghdr* cmsg = CMSG_FIRSTHDR(hdr); + EXPECT_EQ(cmsg->cmsg_len, is_ipv4 ? CMSG_LEN(sizeof(in_pktinfo)) + : CMSG_LEN(sizeof(in6_pktinfo))); + EXPECT_EQ(cmsg->cmsg_level, is_ipv4 ? IPPROTO_IP : IPPROTO_IPV6); + EXPECT_EQ(cmsg->cmsg_type, is_ipv4 ? IP_PKTINFO : IPV6_PKTINFO); + + const std::string& self_addr_str = self_addr.ToPackedString(); + if (is_ipv4) { + in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)); + EXPECT_EQ(0, memcmp(&pktinfo->ipi_spec_dst, self_addr_str.c_str(), + self_addr_str.length())); + } else { + in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)); + EXPECT_EQ(0, memcmp(&pktinfo->ipi6_addr, self_addr_str.c_str(), + self_addr_str.length())); + } + + cmsg = CMSG_NXTHDR(hdr, cmsg); + EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(uint16_t))); + EXPECT_EQ(cmsg->cmsg_level, SOL_UDP); + EXPECT_EQ(cmsg->cmsg_type, UDP_SEGMENT); + EXPECT_EQ(gso_size, *reinterpret_cast<uint16_t*>(CMSG_DATA(cmsg))); + + EXPECT_EQ(nullptr, CMSG_NXTHDR(hdr, cmsg)); +} + +TEST_F(QuicLinuxSocketUtilsTest, QuicMsgHdr) { + QuicSocketAddress peer_addr(QuicIpAddress::Loopback4(), 1234); + char packet_buf[1024]; + + QuicMsgHdr quic_hdr(packet_buf, sizeof(packet_buf), peer_addr, nullptr, 0); + CheckMsghdrWithoutCbuf(quic_hdr.hdr(), packet_buf, sizeof(packet_buf), + peer_addr); + + for (bool is_ipv4 : {true, false}) { + QuicIpAddress self_addr = + is_ipv4 ? QuicIpAddress::Loopback4() : QuicIpAddress::Loopback6(); + char cbuf[kCmsgSpaceForIp + kCmsgSpaceForTTL]; + QuicMsgHdr quic_hdr(packet_buf, sizeof(packet_buf), peer_addr, cbuf, + sizeof(cbuf)); + msghdr* hdr = const_cast<msghdr*>(quic_hdr.hdr()); + + EXPECT_EQ(nullptr, hdr->msg_control); + EXPECT_EQ(0u, hdr->msg_controllen); + + quic_hdr.SetIpInNextCmsg(self_addr); + EXPECT_EQ(cbuf, hdr->msg_control); + const size_t ip_cmsg_space = + is_ipv4 ? kCmsgSpaceForIpv4 : kCmsgSpaceForIpv6; + EXPECT_EQ(ip_cmsg_space, hdr->msg_controllen); + + if (is_ipv4) { + *quic_hdr.GetNextCmsgData<int>(IPPROTO_IP, IP_TTL) = 32; + } else { + *quic_hdr.GetNextCmsgData<int>(IPPROTO_IPV6, IPV6_HOPLIMIT) = 32; + } + + CheckIpAndTtlInCbuf(hdr, cbuf, self_addr, 32); + } +} + +TEST_F(QuicLinuxSocketUtilsTest, QuicMMsgHdr) { + QuicCircularDeque<BufferedWrite> buffered_writes; + char packet_buf1[1024]; + char packet_buf2[512]; + buffered_writes.emplace_back( + packet_buf1, sizeof(packet_buf1), QuicIpAddress::Loopback4(), + QuicSocketAddress(QuicIpAddress::Loopback4(), 4)); + buffered_writes.emplace_back( + packet_buf2, sizeof(packet_buf2), QuicIpAddress::Loopback6(), + QuicSocketAddress(QuicIpAddress::Loopback6(), 6)); + + QuicMMsgHdr quic_mhdr_without_cbuf(buffered_writes.begin(), + buffered_writes.end(), 0, nullptr); + for (size_t i = 0; i < buffered_writes.size(); ++i) { + const BufferedWrite& bw = buffered_writes[i]; + CheckMsghdrWithoutCbuf(&quic_mhdr_without_cbuf.mhdr()[i].msg_hdr, bw.buffer, + bw.buf_len, bw.peer_address); + } + + QuicMMsgHdr quic_mhdr_with_cbuf( + buffered_writes.begin(), buffered_writes.end(), + kCmsgSpaceForIp + kCmsgSpaceForSegmentSize, + [](QuicMMsgHdr* mhdr, int i, const BufferedWrite& buffered_write) { + mhdr->SetIpInNextCmsg(i, buffered_write.self_address); + *mhdr->GetNextCmsgData<uint16_t>(i, SOL_UDP, UDP_SEGMENT) = 1300; + }); + for (size_t i = 0; i < buffered_writes.size(); ++i) { + const BufferedWrite& bw = buffered_writes[i]; + msghdr* hdr = &quic_mhdr_with_cbuf.mhdr()[i].msg_hdr; + CheckIpAndGsoSizeInCbuf(hdr, hdr->msg_control, bw.self_address, 1300); + } +} + +TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_NoPacketsToSend) { + int num_packets_sent; + QuicCircularDeque<BufferedWrite> buffered_writes; + + EXPECT_CALL(mock_syscalls_, Sendmmsg(_, _, _, _)).Times(0); + + EXPECT_EQ(WriteResult(WRITE_STATUS_ERROR, EINVAL), + TestWriteMultiplePackets(1, buffered_writes.begin(), + buffered_writes.end(), &num_packets_sent)); +} + +TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_WriteBlocked) { + int num_packets_sent; + QuicCircularDeque<BufferedWrite> buffered_writes; + buffered_writes.emplace_back(nullptr, 0, QuicIpAddress(), + QuicSocketAddress(QuicIpAddress::Any4(), 0)); + + EXPECT_CALL(mock_syscalls_, Sendmmsg(_, _, _, _)) + .WillOnce(Invoke([](int /*fd*/, mmsghdr* /*msgvec*/, + unsigned int /*vlen*/, int /*flags*/) { + errno = EWOULDBLOCK; + return -1; + })); + + EXPECT_EQ(WriteResult(WRITE_STATUS_BLOCKED, EWOULDBLOCK), + TestWriteMultiplePackets(1, buffered_writes.begin(), + buffered_writes.end(), &num_packets_sent)); + EXPECT_EQ(0, num_packets_sent); +} + +TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_WriteError) { + int num_packets_sent; + QuicCircularDeque<BufferedWrite> buffered_writes; + buffered_writes.emplace_back(nullptr, 0, QuicIpAddress(), + QuicSocketAddress(QuicIpAddress::Any4(), 0)); + + EXPECT_CALL(mock_syscalls_, Sendmmsg(_, _, _, _)) + .WillOnce(Invoke([](int /*fd*/, mmsghdr* /*msgvec*/, + unsigned int /*vlen*/, int /*flags*/) { + errno = EPERM; + return -1; + })); + + EXPECT_EQ(WriteResult(WRITE_STATUS_ERROR, EPERM), + TestWriteMultiplePackets(1, buffered_writes.begin(), + buffered_writes.end(), &num_packets_sent)); + EXPECT_EQ(0, num_packets_sent); +} + +TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_WriteSuccess) { + int num_packets_sent; + QuicCircularDeque<BufferedWrite> buffered_writes; + const int kNumBufferedWrites = 10; + static_assert(kNumBufferedWrites < 256, "Must be less than 256"); + std::vector<std::string> buffer_holder; + for (int i = 0; i < kNumBufferedWrites; ++i) { + size_t buf_len = (i + 1) * 2; + std::ostringstream buffer_ostream; + while (buffer_ostream.str().length() < buf_len) { + buffer_ostream << i; + } + buffer_holder.push_back(buffer_ostream.str().substr(0, buf_len - 1) + '$'); + + buffered_writes.emplace_back(buffer_holder.back().data(), buf_len, + QuicIpAddress(), + QuicSocketAddress(QuicIpAddress::Any4(), 0)); + + // Leave the first self_address uninitialized. + if (i != 0) { + ASSERT_TRUE(buffered_writes.back().self_address.FromString("127.0.0.1")); + } + + std::ostringstream peer_ip_ostream; + QuicIpAddress peer_ip_address; + peer_ip_ostream << "127.0.1." << i + 1; + ASSERT_TRUE(peer_ip_address.FromString(peer_ip_ostream.str())); + buffered_writes.back().peer_address = + QuicSocketAddress(peer_ip_address, i + 1); + } + + InSequence s; + + for (int expected_num_packets_sent : {1, 2, 3, 10}) { + SCOPED_TRACE(testing::Message() + << "expected_num_packets_sent=" << expected_num_packets_sent); + EXPECT_CALL(mock_syscalls_, Sendmmsg(_, _, _, _)) + .WillOnce(Invoke( + [&](int /*fd*/, mmsghdr* msgvec, unsigned int vlen, int /*flags*/) { + EXPECT_LE(static_cast<unsigned int>(expected_num_packets_sent), + vlen); + for (unsigned int i = 0; i < vlen; ++i) { + const BufferedWrite& buffered_write = buffered_writes[i]; + const msghdr& hdr = msgvec[i].msg_hdr; + EXPECT_EQ(1u, hdr.msg_iovlen); + EXPECT_EQ(buffered_write.buffer, hdr.msg_iov->iov_base); + EXPECT_EQ(buffered_write.buf_len, hdr.msg_iov->iov_len); + sockaddr_storage expected_peer_address = + buffered_write.peer_address.generic_address(); + EXPECT_EQ(0, memcmp(&expected_peer_address, hdr.msg_name, + sizeof(sockaddr_storage))); + EXPECT_EQ(buffered_write.self_address.IsInitialized(), + hdr.msg_control != nullptr); + } + return expected_num_packets_sent; + })) + .RetiresOnSaturation(); + + int expected_bytes_written = 0; + for (auto it = buffered_writes.cbegin(); + it != buffered_writes.cbegin() + expected_num_packets_sent; ++it) { + expected_bytes_written += it->buf_len; + } + + EXPECT_EQ( + WriteResult(WRITE_STATUS_OK, expected_bytes_written), + TestWriteMultiplePackets(1, buffered_writes.cbegin(), + buffered_writes.cend(), &num_packets_sent)); + EXPECT_EQ(expected_num_packets_sent, num_packets_sent); + } +} + +} // namespace +} // namespace test +} // namespace quic 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 0ae78d22fa6..c1f8982f705 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 @@ -47,10 +47,10 @@ QuicLongHeaderType EncryptionlevelToLongHeaderType(EncryptionLevel level) { case ENCRYPTION_FORWARD_SECURE: QUIC_BUG << "Try to derive long header type for packet with encryption level: " - << EncryptionLevelToString(level); + << level; return INVALID_PACKET_TYPE; default: - QUIC_BUG << EncryptionLevelToString(level); + QUIC_BUG << level; return INVALID_PACKET_TYPE; } } @@ -165,6 +165,8 @@ void QuicPacketCreator::SetMaxPacketLength(QuicByteCount length) { if (length == max_packet_length_) { return; } + QUIC_DVLOG(1) << "Updating packet creator max packet length from " + << max_packet_length_ << " to " << length; max_packet_length_ = length; max_plaintext_size_ = framer_->GetMaxPlaintextSize(max_packet_length_); @@ -198,6 +200,8 @@ void QuicPacketCreator::SetSoftMaxPacketLength(QuicByteCount length) { } if (framer_->GetMaxPlaintextSize(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"; return; } @@ -281,6 +285,10 @@ bool QuicPacketCreator::ConsumeCryptoDataToFillCurrentPacket( bool needs_full_padding, TransmissionType transmission_type, QuicFrame* frame) { + QUIC_DVLOG(2) << "ConsumeCryptoDataToFillCurrentPacket " << level + << " write_length " << write_length << " offset " << offset + << (needs_full_padding ? " needs_full_padding" : "") << " " + << transmission_type; if (!CreateCryptoFrame(level, write_length, offset, frame)) { return false; } @@ -423,8 +431,17 @@ bool QuicPacketCreator::CreateCryptoFrame(EncryptionLevel level, QuicFrame* frame) { size_t min_frame_size = QuicFramer::GetMinCryptoFrameSize(write_length, offset); - if (BytesFree() <= min_frame_size && - (!RemoveSoftMaxPacketLength() || BytesFree() <= min_frame_size)) { + size_t min_plaintext_bytes = min_frame_size; + if (fix_min_crypto_frame_size_ && queued_frames_.empty()) { + // TODO(fayang): to make this more general, we need to move the checking + // when trying to add a frame when GetSerializedFrameLength. Remove soft + // limit if current packet needs extra padding and the space is too small. + QUIC_RELOADABLE_FLAG_COUNT(quic_fix_min_crypto_frame_size); + min_plaintext_bytes = + std::max(min_frame_size, MinPlaintextPacketSize(framer_->version())); + } + if (BytesFree() <= min_plaintext_bytes && + (!RemoveSoftMaxPacketLength() || BytesFree() <= min_plaintext_bytes)) { return false; } size_t max_write_length = BytesFree() - min_frame_size; @@ -439,12 +456,23 @@ void QuicPacketCreator::FlushCurrentPacket() { } QUIC_CACHELINE_ALIGNED char stack_buffer[kMaxOutgoingPacketSize]; - char* serialized_packet_buffer = delegate_->GetPacketBuffer(); - if (serialized_packet_buffer == nullptr) { - serialized_packet_buffer = stack_buffer; + QuicOwnedPacketBuffer external_buffer(delegate_->GetPacketBuffer()); + + if (!avoid_leak_writer_buffer_ && external_buffer.release_buffer != nullptr) { + // This is not a flag count because it is incremented when flag is false. + QUIC_CODE_COUNT(quic_avoid_leak_writer_buffer_flag_false_noop_1); + + // Setting it to nullptr to keep the behavior unchanged when flag is false. + external_buffer.release_buffer = nullptr; + } + + if (external_buffer.buffer == nullptr) { + external_buffer.buffer = stack_buffer; + external_buffer.release_buffer = nullptr; } - SerializePacket(serialized_packet_buffer, kMaxOutgoingPacketSize); + DCHECK_EQ(nullptr, packet_.encrypted_buffer); + SerializePacket(std::move(external_buffer), kMaxOutgoingPacketSize); OnSerializedPacket(); } @@ -471,6 +499,12 @@ void QuicPacketCreator::ClearPacket() { packet_.transmission_type = NOT_RETRANSMISSION; packet_.encrypted_buffer = nullptr; packet_.encrypted_length = 0; + if (avoid_leak_writer_buffer_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_avoid_leak_writer_buffer, 2, 3); + QUIC_BUG_IF(packet_.release_encrypted_buffer != nullptr) + << "packet_.release_encrypted_buffer should be empty"; + packet_.release_encrypted_buffer = nullptr; + } DCHECK(packet_.retransmittable_frames.empty()); DCHECK(packet_.nonretransmittable_frames.empty()); packet_.largest_acked.Clear(); @@ -514,7 +548,7 @@ size_t QuicPacketCreator::ReserializeInitialPacketInCoalescedPacket( return 0; } } - SerializePacket(buffer, buffer_len); + SerializePacket(QuicOwnedPacketBuffer(buffer, nullptr), buffer_len); const size_t encrypted_length = packet_.encrypted_length; // Clear frames in packet_. No need to DeleteFrames since frames are owned by // initial_packet. @@ -533,16 +567,29 @@ void QuicPacketCreator::CreateAndSerializeStreamFrame( TransmissionType transmission_type, size_t* num_bytes_consumed) { DCHECK(queued_frames_.empty()); + DCHECK(!QuicUtils::IsCryptoStreamId(transport_version(), id)); // Write out the packet header QuicPacketHeader header; FillPacketHeader(&header); QUIC_CACHELINE_ALIGNED char stack_buffer[kMaxOutgoingPacketSize]; - char* encrypted_buffer = delegate_->GetPacketBuffer(); - if (encrypted_buffer == nullptr) { - encrypted_buffer = stack_buffer; + QuicOwnedPacketBuffer packet_buffer(delegate_->GetPacketBuffer()); + + if (!avoid_leak_writer_buffer_ && packet_buffer.release_buffer != nullptr) { + // This is not a flag count because it is incremented when flag is false. + QUIC_CODE_COUNT(quic_avoid_leak_writer_buffer_flag_false_noop_2); + + // Setting it to nullptr to keep the behavior unchanged when flag is false. + packet_buffer.release_buffer = nullptr; } + if (packet_buffer.buffer == nullptr) { + packet_buffer.buffer = stack_buffer; + packet_buffer.release_buffer = nullptr; + } + + char* encrypted_buffer = packet_buffer.buffer; + QuicDataWriter writer(kMaxOutgoingPacketSize, encrypted_buffer); size_t length_field_offset = 0; if (!framer_->AppendPacketHeader(header, &writer, &length_field_offset)) { @@ -609,6 +656,9 @@ void QuicPacketCreator::CreateAndSerializeStreamFrame( packet_.transmission_type = transmission_type; + DCHECK(packet_.encryption_level == ENCRYPTION_FORWARD_SECURE || + packet_.encryption_level == ENCRYPTION_ZERO_RTT) + << packet_.encryption_level; size_t encrypted_length = framer_->EncryptInPlace( packet_.encryption_level, packet_.packet_number, GetStartOfEncryptedData(framer_->transport_version(), header), @@ -623,6 +673,14 @@ void QuicPacketCreator::CreateAndSerializeStreamFrame( packet_size_ = 0; packet_.encrypted_buffer = encrypted_buffer; packet_.encrypted_length = encrypted_length; + if (avoid_leak_writer_buffer_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_avoid_leak_writer_buffer, 3, 3); + packet_.release_encrypted_buffer = std::move(packet_buffer).release_buffer; + } else { + // If flag --quic_avoid_leak_writer_buffer is false, the release function + // should be empty. + DCHECK(packet_buffer.release_buffer == nullptr); + } packet_.retransmittable_frames.push_back(QuicFrame(frame)); OnSerializedPacket(); } @@ -674,6 +732,9 @@ size_t QuicPacketCreator::BytesFree() { } size_t QuicPacketCreator::PacketSize() { + if (update_packet_size_) { + return queued_frames_.empty() ? PacketHeaderSize() : packet_size_; + } if (!queued_frames_.empty()) { return packet_size_; } @@ -691,7 +752,7 @@ bool QuicPacketCreator::AddPaddedSavedFrame( return false; } -void QuicPacketCreator::SerializePacket(char* encrypted_buffer, +void QuicPacketCreator::SerializePacket(QuicOwnedPacketBuffer encrypted_buffer, size_t encrypted_buffer_len) { DCHECK_LT(0u, encrypted_buffer_len); QUIC_BUG_IF(queued_frames_.empty() && pending_padding_bytes_ == 0) @@ -704,14 +765,13 @@ void QuicPacketCreator::SerializePacket(char* encrypted_buffer, QUIC_DVLOG(2) << ENDPOINT << "Serializing packet " << header << QuicFramesToString(queued_frames_) << " at encryption_level " - << EncryptionLevelToString(packet_.encryption_level); + << packet_.encryption_level; if (!framer_->HasEncrypterOfEncryptionLevel(packet_.encryption_level)) { QUIC_BUG << ENDPOINT << "Attempting to serialize " << header << QuicFramesToString(queued_frames_) - << " at missing encryption_level " - << EncryptionLevelToString(packet_.encryption_level) << " using " - << framer_->version(); + << " at missing encryption_level " << packet_.encryption_level + << " using " << framer_->version(); return; } @@ -719,10 +779,18 @@ void QuicPacketCreator::SerializePacket(char* encrypted_buffer, // 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, + framer_->BuildDataPacket(header, queued_frames_, encrypted_buffer.buffer, packet_size_, packet_.encryption_level); if (length == 0) { - QUIC_BUG << "Failed to serialize " << queued_frames_.size() << " frames."; + QUIC_BUG << "Failed to serialize " << QuicFramesToString(queued_frames_) + << " at encryption_level: " << packet_.encryption_level + << ", needs_full_padding_: " << needs_full_padding_ + << ", packet_.num_padding_bytes: " << packet_.num_padding_bytes + << ", pending_padding_bytes_: " << pending_padding_bytes_ + << ", latched_hard_max_packet_length_: " + << latched_hard_max_packet_length_ + << ", max_packet_length_: " << max_packet_length_ + << ", header: " << header; return; } @@ -741,7 +809,7 @@ void QuicPacketCreator::SerializePacket(char* encrypted_buffer, const size_t encrypted_length = framer_->EncryptInPlace( packet_.encryption_level, packet_.packet_number, GetStartOfEncryptedData(framer_->transport_version(), header), length, - encrypted_buffer_len, encrypted_buffer); + encrypted_buffer_len, encrypted_buffer.buffer); if (encrypted_length == 0) { QUIC_BUG << "Failed to encrypt packet number " << packet_.packet_number; return; @@ -749,8 +817,17 @@ void QuicPacketCreator::SerializePacket(char* encrypted_buffer, packet_size_ = 0; queued_frames_.clear(); - packet_.encrypted_buffer = encrypted_buffer; + packet_.encrypted_buffer = encrypted_buffer.buffer; packet_.encrypted_length = encrypted_length; + if (avoid_leak_writer_buffer_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_avoid_leak_writer_buffer, 1, 3); + packet_.release_encrypted_buffer = + std::move(encrypted_buffer).release_buffer; + } else { + // If flag --quic_avoid_leak_writer_buffer is false, the release function + // should be empty. + DCHECK(encrypted_buffer.release_buffer == nullptr); + } } std::unique_ptr<QuicEncryptedPacket> @@ -785,6 +862,7 @@ QuicPacketCreator::SerializeConnectivityProbingPacket() { header, buffer.get(), max_plaintext_size_, packet_.encryption_level); DCHECK(length); + DCHECK_EQ(packet_.encryption_level, ENCRYPTION_FORWARD_SECURE); const size_t encrypted_length = framer_->EncryptInPlace( packet_.encryption_level, packet_.packet_number, GetStartOfEncryptedData(framer_->transport_version(), header), length, @@ -824,6 +902,7 @@ QuicPacketCreator::SerializePathChallengeConnectivityProbingPacket( packet_.encryption_level); DCHECK(length); + DCHECK_EQ(packet_.encryption_level, ENCRYPTION_FORWARD_SECURE); const size_t encrypted_length = framer_->EncryptInPlace( packet_.encryption_level, packet_.packet_number, GetStartOfEncryptedData(framer_->transport_version(), header), length, @@ -865,6 +944,7 @@ QuicPacketCreator::SerializePathResponseConnectivityProbingPacket( payloads, is_padded, packet_.encryption_level); DCHECK(length); + DCHECK_EQ(packet_.encryption_level, ENCRYPTION_FORWARD_SECURE); const size_t encrypted_length = framer_->EncryptInPlace( packet_.encryption_level, packet_.packet_number, GetStartOfEncryptedData(framer_->transport_version(), header), length, @@ -1180,9 +1260,9 @@ QuicConsumedData QuicPacketCreator::ConsumeData(QuicStreamId id, write_length - total_bytes_consumed > kMaxOutgoingPacketSize && latched_hard_max_packet_length_ == 0; - while (!run_fast_path && delegate_->ShouldGeneratePacket( - HAS_RETRANSMITTABLE_DATA, - has_handshake ? IS_HANDSHAKE : NOT_HANDSHAKE)) { + while (!run_fast_path && + (has_handshake || delegate_->ShouldGeneratePacket( + HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE))) { QuicFrame frame; bool needs_full_padding = has_handshake && fully_pad_crypto_handshake_packets_; @@ -1268,6 +1348,8 @@ QuicConsumedData QuicPacketCreator::ConsumeDataFastPath( size_t QuicPacketCreator::ConsumeCryptoData(EncryptionLevel level, size_t write_length, QuicStreamOffset offset) { + QUIC_DVLOG(2) << "ConsumeCryptoData " << level << " write_length " + << write_length << " offset " << offset; QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when " "generator tries to write crypto data."; MaybeBundleAckOpportunistically(); @@ -1282,7 +1364,13 @@ size_t QuicPacketCreator::ConsumeCryptoData(EncryptionLevel level, size_t total_bytes_consumed = 0; - while (total_bytes_consumed < write_length) { + while (total_bytes_consumed < write_length && + (!GetQuicReloadableFlag(quic_fix_checking_should_generate_packet) || + delegate_->ShouldGeneratePacket(HAS_RETRANSMITTABLE_DATA, + IS_HANDSHAKE))) { + if (GetQuicReloadableFlag(quic_fix_checking_should_generate_packet)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_fix_checking_should_generate_packet); + } QuicFrame frame; if (!ConsumeCryptoDataToFillCurrentPacket( level, write_length - total_bytes_consumed, @@ -1484,7 +1572,7 @@ void QuicPacketCreator::FillPacketHeader(QuicPacketHeader* header) { bool QuicPacketCreator::AddFrame(const QuicFrame& frame, TransmissionType transmission_type) { QUIC_DVLOG(1) << ENDPOINT << "Adding frame with transmission type " - << TransmissionTypeToString(transmission_type) << ": " << frame; + << transmission_type << ": " << frame; if (frame.type == STREAM_FRAME && !QuicUtils::IsCryptoStreamId(framer_->transport_version(), frame.stream_frame.stream_id) && @@ -1518,10 +1606,15 @@ bool QuicPacketCreator::AddFrame(const QuicFrame& frame, /* last_frame_in_packet= */ true, GetPacketNumberLength()); } if (frame_len == 0) { - // Current open packet is full. + QUIC_DVLOG(1) << "Flushing because current open packet is full when adding " + << frame; FlushCurrentPacket(); return false; } + if (update_packet_size_ && queued_frames_.empty()) { + QUIC_RELOADABLE_FLAG_COUNT(quic_update_packet_size); + packet_size_ = PacketHeaderSize(); + } DCHECK_LT(0u, packet_size_); packet_size_ += ExpansionOnNewFrame() + frame_len; @@ -1641,6 +1734,10 @@ void QuicPacketCreator::MaybeAddPadding() { } } + if (disable_padding_override_) { + needs_full_padding_ = false; + } + // Header protection requires a minimum plaintext packet size. size_t extra_padding_bytes = 0; if (framer_->version().HasHeaderProtection()) { @@ -1675,8 +1772,7 @@ void QuicPacketCreator::MaybeAddPadding() { bool success = AddFrame(QuicFrame(QuicPaddingFrame(padding_bytes)), packet_.transmission_type); QUIC_BUG_IF(!success) << "Failed to add padding_bytes: " << padding_bytes - << " transmission_type: " - << TransmissionTypeToString(packet_.transmission_type); + << " transmission_type: " << packet_.transmission_type; } bool QuicPacketCreator::IncludeNonceInPublicHeader() const { 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 7695587f9f5..76d65574e51 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 @@ -41,9 +41,9 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator { public: virtual ~DelegateInterface() {} // Get a buffer of kMaxOutgoingPacketSize bytes to serialize the next - // packet. If return nullptr, QuicPacketCreator will serialize on a stack - // buffer. - virtual char* GetPacketBuffer() = 0; + // packet. If the return value's buffer is nullptr, QuicPacketCreator will + // serialize on a stack buffer. + virtual QuicPacketBuffer GetPacketBuffer() = 0; // Called when a packet is serialized. Delegate take the ownership of // |serialized_packet|. virtual void OnSerializedPacket(SerializedPacket serialized_packet) = 0; @@ -142,12 +142,14 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator { // Returns true if current open packet can accommodate more stream frames of // stream |id| at |offset| and data length |data_size|, false otherwise. + // TODO(fayang): mark this const when deprecating quic_update_packet_size. bool HasRoomForStreamFrame(QuicStreamId id, QuicStreamOffset offset, size_t data_size); // Returns true if current open packet can accommodate a message frame of // |length|. + // TODO(fayang): mark this const when deprecating quic_update_packet_size. bool HasRoomForMessageFrame(QuicByteCount length); // Serializes all added frames into a single packet and invokes the delegate_ @@ -179,6 +181,7 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator { // frames in the packet. Since stream frames are slightly smaller when they // are the last frame in a packet, this method will return a different // value than max_packet_size - PacketSize(), in this case. + // TODO(fayang): mark this const when deprecating quic_update_packet_size. size_t BytesFree(); // Returns the number of bytes that the packet will expand by if a new frame @@ -191,6 +194,7 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator { // if serialized with the current frames. Adding a frame to the packet // may change the serialized length of existing frames, as per the comment // in BytesFree. + // TODO(fayang): mark this const when deprecating quic_update_packet_size. size_t PacketSize(); // Tries to add |frame| to the packet creator's list of frames to be @@ -255,6 +259,7 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator { void set_encryption_level(EncryptionLevel level) { packet_.encryption_level = level; } + EncryptionLevel encryption_level() { return packet_.encryption_level; } // packet number of the last created packet, or 0 if no packets have been // created. @@ -429,6 +434,10 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator { char* buffer, size_t buffer_len); + void set_disable_padding_override(bool should_disable_padding) { + disable_padding_override_ = should_disable_padding; + } + private: friend class test::QuicPacketCreatorPeer; @@ -459,7 +468,8 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator { // retransmitted to packet_.retransmittable_frames. All frames must fit into // a single packet. // Fails if |encrypted_buffer_len| isn't long enough for the encrypted packet. - void SerializePacket(char* encrypted_buffer, size_t encrypted_buffer_len); + void SerializePacket(QuicOwnedPacketBuffer encrypted_buffer, + size_t encrypted_buffer_len); // Called after a new SerialiedPacket is created to call the delegate's // OnSerializedPacket and reset state. @@ -551,7 +561,8 @@ class QUIC_EXPORT_PRIVATE QuicPacketCreator { // Frames to be added to the next SerializedPacket QuicFrames queued_frames_; - // packet_size should never be read directly, use PacketSize() instead. + // Serialization size of header + frames. If there is no queued frames, + // packet_size_ is 0. // TODO(ianswett): Move packet_size_ into SerializedPacket once // QuicEncryptedPacket has been flattened into SerializedPacket. size_t packet_size_; @@ -597,6 +608,17 @@ 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_; + + const bool avoid_leak_writer_buffer_ = + GetQuicReloadableFlag(quic_avoid_leak_writer_buffer); + + const bool fix_min_crypto_frame_size_ = + GetQuicReloadableFlag(quic_fix_min_crypto_frame_size); + + // When true, this will override the padding generation code to disable it. + bool disable_padding_override_ = false; + + bool update_packet_size_ = GetQuicReloadableFlag(quic_update_packet_size); }; } // 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 d1beea7c01d..394b82ec364 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 @@ -162,7 +162,8 @@ class QuicPacketCreatorTest : public QuicTestWithParam<TestParams> { connection_id_.length()), data_("foo"), creator_(connection_id_, &client_framer_, &delegate_, &producer_) { - EXPECT_CALL(delegate_, GetPacketBuffer()).WillRepeatedly(Return(nullptr)); + EXPECT_CALL(delegate_, GetPacketBuffer()) + .WillRepeatedly(Return(QuicPacketBuffer())); creator_.SetEncrypter(ENCRYPTION_INITIAL, std::make_unique<NullEncrypter>( Perspective::IS_CLIENT)); creator_.SetEncrypter(ENCRYPTION_HANDSHAKE, std::make_unique<NullEncrypter>( @@ -930,40 +931,36 @@ TEST_P(QuicPacketCreatorTest, BuildPathResponsePacket3ResponsesPadded) { } TEST_P(QuicPacketCreatorTest, SerializeConnectivityProbingPacket) { - for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { - EncryptionLevel level = static_cast<EncryptionLevel>(i); - - creator_.set_encryption_level(level); + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); - std::unique_ptr<SerializedPacket> encrypted; + std::unique_ptr<SerializedPacket> encrypted; + if (VersionHasIetfQuicFrames(creator_.transport_version())) { + QuicPathFrameBuffer payload = { + {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xfe}}; + encrypted = + creator_.SerializePathChallengeConnectivityProbingPacket(&payload); + } else { + encrypted = creator_.SerializeConnectivityProbingPacket(); + } + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); if (VersionHasIetfQuicFrames(creator_.transport_version())) { - QuicPathFrameBuffer payload = { - {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xfe}}; - encrypted = - creator_.SerializePathChallengeConnectivityProbingPacket(&payload); + EXPECT_CALL(framer_visitor_, OnPathChallengeFrame(_)); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); } else { - encrypted = creator_.SerializeConnectivityProbingPacket(); - } - { - InSequence s; - EXPECT_CALL(framer_visitor_, OnPacket()); - EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); - EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); - EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); - EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); - if (VersionHasIetfQuicFrames(creator_.transport_version())) { - EXPECT_CALL(framer_visitor_, OnPathChallengeFrame(_)); - EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); - } else { - EXPECT_CALL(framer_visitor_, OnPingFrame(_)); - EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); - } - EXPECT_CALL(framer_visitor_, OnPacketComplete()); + EXPECT_CALL(framer_visitor_, OnPingFrame(_)); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); } - // QuicFramerPeer::SetPerspective(&client_framer_, Perspective::IS_SERVER); - server_framer_.ProcessPacket(QuicEncryptedPacket( - encrypted->encrypted_buffer, encrypted->encrypted_length)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); } + // QuicFramerPeer::SetPerspective(&client_framer_, Perspective::IS_SERVER); + server_framer_.ProcessPacket(QuicEncryptedPacket( + encrypted->encrypted_buffer, encrypted->encrypted_length)); } TEST_P(QuicPacketCreatorTest, SerializePathChallengeProbePacket) { @@ -973,28 +970,24 @@ TEST_P(QuicPacketCreatorTest, SerializePathChallengeProbePacket) { QuicPathFrameBuffer payload = { {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; - for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { - EncryptionLevel level = static_cast<EncryptionLevel>(i); - - creator_.set_encryption_level(level); + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); - std::unique_ptr<SerializedPacket> encrypted( - creator_.SerializePathChallengeConnectivityProbingPacket(&payload)); - { - InSequence s; - EXPECT_CALL(framer_visitor_, OnPacket()); - EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); - EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); - EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); - EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); - EXPECT_CALL(framer_visitor_, OnPathChallengeFrame(_)); - EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); - EXPECT_CALL(framer_visitor_, OnPacketComplete()); - } - // QuicFramerPeer::SetPerspective(&client_framer_, Perspective::IS_SERVER); - server_framer_.ProcessPacket(QuicEncryptedPacket( - encrypted->encrypted_buffer, encrypted->encrypted_length)); + std::unique_ptr<SerializedPacket> encrypted( + creator_.SerializePathChallengeConnectivityProbingPacket(&payload)); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnPathChallengeFrame(_)); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); } + // QuicFramerPeer::SetPerspective(&client_framer_, Perspective::IS_SERVER); + server_framer_.ProcessPacket(QuicEncryptedPacket( + encrypted->encrypted_buffer, encrypted->encrypted_length)); } TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket1PayloadPadded) { @@ -1004,30 +997,26 @@ TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket1PayloadPadded) { QuicPathFrameBuffer payload0 = { {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; - for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { - EncryptionLevel level = static_cast<EncryptionLevel>(i); - creator_.set_encryption_level(level); + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); - QuicCircularDeque<QuicPathFrameBuffer> payloads; - payloads.push_back(payload0); + QuicCircularDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); - std::unique_ptr<SerializedPacket> encrypted( - creator_.SerializePathResponseConnectivityProbingPacket(payloads, - true)); - { - InSequence s; - EXPECT_CALL(framer_visitor_, OnPacket()); - EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); - EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); - EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); - EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); - EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)); - EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); - EXPECT_CALL(framer_visitor_, OnPacketComplete()); - } - server_framer_.ProcessPacket(QuicEncryptedPacket( - encrypted->encrypted_buffer, encrypted->encrypted_length)); + std::unique_ptr<SerializedPacket> encrypted( + creator_.SerializePathResponseConnectivityProbingPacket(payloads, true)); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); } + server_framer_.ProcessPacket(QuicEncryptedPacket( + encrypted->encrypted_buffer, encrypted->encrypted_length)); } TEST_P(QuicPacketCreatorTest, @@ -1038,29 +1027,25 @@ TEST_P(QuicPacketCreatorTest, QuicPathFrameBuffer payload0 = { {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}}; - for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { - EncryptionLevel level = static_cast<EncryptionLevel>(i); - creator_.set_encryption_level(level); + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); - QuicCircularDeque<QuicPathFrameBuffer> payloads; - payloads.push_back(payload0); + QuicCircularDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); - std::unique_ptr<SerializedPacket> encrypted( - creator_.SerializePathResponseConnectivityProbingPacket(payloads, - false)); - { - InSequence s; - EXPECT_CALL(framer_visitor_, OnPacket()); - EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); - EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); - EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); - EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); - EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)); - EXPECT_CALL(framer_visitor_, OnPacketComplete()); - } - server_framer_.ProcessPacket(QuicEncryptedPacket( - encrypted->encrypted_buffer, encrypted->encrypted_length)); + std::unique_ptr<SerializedPacket> encrypted( + creator_.SerializePathResponseConnectivityProbingPacket(payloads, false)); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); } + server_framer_.ProcessPacket(QuicEncryptedPacket( + encrypted->encrypted_buffer, encrypted->encrypted_length)); } TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket2PayloadsPadded) { @@ -1072,31 +1057,27 @@ TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket2PayloadsPadded) { QuicPathFrameBuffer payload1 = { {0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde}}; - for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { - EncryptionLevel level = static_cast<EncryptionLevel>(i); - creator_.set_encryption_level(level); + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); - QuicCircularDeque<QuicPathFrameBuffer> payloads; - payloads.push_back(payload0); - payloads.push_back(payload1); + QuicCircularDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + payloads.push_back(payload1); - std::unique_ptr<SerializedPacket> encrypted( - creator_.SerializePathResponseConnectivityProbingPacket(payloads, - true)); - { - InSequence s; - EXPECT_CALL(framer_visitor_, OnPacket()); - EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); - EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); - EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); - EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); - EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(2); - EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); - EXPECT_CALL(framer_visitor_, OnPacketComplete()); - } - server_framer_.ProcessPacket(QuicEncryptedPacket( - encrypted->encrypted_buffer, encrypted->encrypted_length)); + std::unique_ptr<SerializedPacket> encrypted( + creator_.SerializePathResponseConnectivityProbingPacket(payloads, true)); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(2); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); } + server_framer_.ProcessPacket(QuicEncryptedPacket( + encrypted->encrypted_buffer, encrypted->encrypted_length)); } TEST_P(QuicPacketCreatorTest, @@ -1109,30 +1090,26 @@ TEST_P(QuicPacketCreatorTest, QuicPathFrameBuffer payload1 = { {0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde}}; - for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { - EncryptionLevel level = static_cast<EncryptionLevel>(i); - creator_.set_encryption_level(level); + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); - QuicCircularDeque<QuicPathFrameBuffer> payloads; - payloads.push_back(payload0); - payloads.push_back(payload1); + QuicCircularDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + payloads.push_back(payload1); - std::unique_ptr<SerializedPacket> encrypted( - creator_.SerializePathResponseConnectivityProbingPacket(payloads, - false)); - { - InSequence s; - EXPECT_CALL(framer_visitor_, OnPacket()); - EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); - EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); - EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); - EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); - EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(2); - EXPECT_CALL(framer_visitor_, OnPacketComplete()); - } - server_framer_.ProcessPacket(QuicEncryptedPacket( - encrypted->encrypted_buffer, encrypted->encrypted_length)); + std::unique_ptr<SerializedPacket> encrypted( + creator_.SerializePathResponseConnectivityProbingPacket(payloads, false)); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(2); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); } + server_framer_.ProcessPacket(QuicEncryptedPacket( + encrypted->encrypted_buffer, encrypted->encrypted_length)); } TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket3PayloadsPadded) { @@ -1146,32 +1123,28 @@ TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket3PayloadsPadded) { QuicPathFrameBuffer payload2 = { {0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde, 0xad}}; - for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { - EncryptionLevel level = static_cast<EncryptionLevel>(i); - creator_.set_encryption_level(level); + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); - QuicCircularDeque<QuicPathFrameBuffer> payloads; - payloads.push_back(payload0); - payloads.push_back(payload1); - payloads.push_back(payload2); + QuicCircularDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + payloads.push_back(payload1); + payloads.push_back(payload2); - std::unique_ptr<SerializedPacket> encrypted( - creator_.SerializePathResponseConnectivityProbingPacket(payloads, - true)); - { - InSequence s; - EXPECT_CALL(framer_visitor_, OnPacket()); - EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); - EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); - EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); - EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); - EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(3); - EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); - EXPECT_CALL(framer_visitor_, OnPacketComplete()); - } - server_framer_.ProcessPacket(QuicEncryptedPacket( - encrypted->encrypted_buffer, encrypted->encrypted_length)); + std::unique_ptr<SerializedPacket> encrypted( + creator_.SerializePathResponseConnectivityProbingPacket(payloads, true)); + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(3); + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); } + server_framer_.ProcessPacket(QuicEncryptedPacket( + encrypted->encrypted_buffer, encrypted->encrypted_length)); } TEST_P(QuicPacketCreatorTest, @@ -1186,30 +1159,26 @@ TEST_P(QuicPacketCreatorTest, QuicPathFrameBuffer payload2 = { {0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde, 0xad}}; - for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { - EncryptionLevel level = static_cast<EncryptionLevel>(i); - creator_.set_encryption_level(level); + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); - QuicCircularDeque<QuicPathFrameBuffer> payloads; - payloads.push_back(payload0); - payloads.push_back(payload1); - payloads.push_back(payload2); + QuicCircularDeque<QuicPathFrameBuffer> payloads; + payloads.push_back(payload0); + payloads.push_back(payload1); + payloads.push_back(payload2); - std::unique_ptr<SerializedPacket> encrypted( - creator_.SerializePathResponseConnectivityProbingPacket(payloads, - false)); - InSequence s; - EXPECT_CALL(framer_visitor_, OnPacket()); - EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); - EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); - EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); - EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); - EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(3); - EXPECT_CALL(framer_visitor_, OnPacketComplete()); + std::unique_ptr<SerializedPacket> encrypted( + creator_.SerializePathResponseConnectivityProbingPacket(payloads, false)); + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_)); + EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); + EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_)); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(3); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); - server_framer_.ProcessPacket(QuicEncryptedPacket( - encrypted->encrypted_buffer, encrypted->encrypted_length)); - } + server_framer_.ProcessPacket(QuicEncryptedPacket( + encrypted->encrypted_buffer, encrypted->encrypted_length)); } TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthLeastAwaiting) { @@ -1499,6 +1468,7 @@ TEST_P(QuicPacketCreatorTest, AddFrameAndFlush) { } TEST_P(QuicPacketCreatorTest, SerializeAndSendStreamFrame) { + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); if (!GetParam().version_serialization) { creator_.StopSendingVersion(); } @@ -1534,6 +1504,7 @@ TEST_P(QuicPacketCreatorTest, SerializeStreamFrameWithPadding) { // Regression test to check that CreateAndSerializeStreamFrame uses a // correctly formatted stream frame header when appending padding. + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); if (!GetParam().version_serialization) { creator_.StopSendingVersion(); } @@ -1725,9 +1696,10 @@ TEST_P(QuicPacketCreatorTest, ConsumeDataAndRandomPadding) { TEST_P(QuicPacketCreatorTest, FlushWithExternalBuffer) { creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); - char external_buffer[kMaxOutgoingPacketSize]; - char* expected_buffer = external_buffer; - EXPECT_CALL(delegate_, GetPacketBuffer()).WillOnce(Return(expected_buffer)); + char* buffer = new char[kMaxOutgoingPacketSize]; + QuicPacketBuffer external_buffer = {buffer, + [](const char* p) { delete[] p; }}; + EXPECT_CALL(delegate_, GetPacketBuffer()).WillOnce(Return(external_buffer)); QuicFrame frame; MakeIOVector("test", &iov_); @@ -1738,10 +1710,13 @@ TEST_P(QuicPacketCreatorTest, FlushWithExternalBuffer) { /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame)); EXPECT_CALL(delegate_, OnSerializedPacket(_)) - .WillOnce(Invoke([expected_buffer](SerializedPacket serialized_packet) { - EXPECT_EQ(expected_buffer, serialized_packet.encrypted_buffer); + .WillOnce(Invoke([&external_buffer](SerializedPacket serialized_packet) { + EXPECT_EQ(external_buffer.buffer, serialized_packet.encrypted_buffer); })); creator_.FlushCurrentPacket(); + if (!GetQuicReloadableFlag(quic_avoid_leak_writer_buffer)) { + delete[] buffer; + } } // Test for error found in @@ -2311,7 +2286,7 @@ class MockDelegate : public QuicPacketCreator::DelegateInterface { MaybeBundleAckOpportunistically, (), (override)); - MOCK_METHOD(char*, GetPacketBuffer, (), (override)); + MOCK_METHOD(QuicPacketBuffer, GetPacketBuffer, (), (override)); MOCK_METHOD(void, OnSerializedPacket, (SerializedPacket), (override)); MOCK_METHOD(void, OnUnrecoverableError, @@ -2466,7 +2441,8 @@ class QuicPacketCreatorMultiplePacketsTest : public QuicTest { &delegate_, &producer_), ack_frame_(InitAckFrame(1)) { - EXPECT_CALL(delegate_, GetPacketBuffer()).WillRepeatedly(Return(nullptr)); + EXPECT_CALL(delegate_, GetPacketBuffer()) + .WillRepeatedly(Return(QuicPacketBuffer())); creator_.SetEncrypter( ENCRYPTION_FORWARD_SECURE, std::make_unique<NullEncrypter>(Perspective::IS_CLIENT)); @@ -2679,6 +2655,37 @@ TEST_F(QuicPacketCreatorMultiplePacketsTest, ConsumeCryptoData) { CheckPacketContains(contents, 0); } +TEST_F(QuicPacketCreatorMultiplePacketsTest, + ConsumeCryptoDataCheckShouldGeneratePacket) { + delegate_.SetCanNotWrite(); + + if (GetQuicReloadableFlag(quic_fix_checking_should_generate_packet)) { + EXPECT_CALL(delegate_, OnSerializedPacket(_)).Times(0); + } else { + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce( + Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket)); + } + std::string data = "crypto data"; + size_t consumed_bytes = + creator_.ConsumeCryptoData(ENCRYPTION_INITIAL, data, 0); + creator_.Flush(); + if (GetQuicReloadableFlag(quic_fix_checking_should_generate_packet)) { + EXPECT_EQ(0u, consumed_bytes); + } else { + EXPECT_EQ(data.length(), consumed_bytes); + } + EXPECT_FALSE(creator_.HasPendingFrames()); + EXPECT_FALSE(creator_.HasPendingRetransmittableFrames()); + if (GetQuicReloadableFlag(quic_fix_checking_should_generate_packet)) { + return; + } + PacketContents contents; + contents.num_crypto_frames = 1; + contents.num_padding_frames = 1; + CheckPacketContains(contents, 0); +} + TEST_F(QuicPacketCreatorMultiplePacketsTest, ConsumeData_NotWritable) { delegate_.SetCanNotWrite(); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer.h b/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer.h index ab29e15aa88..6efdb6005d8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer.h @@ -125,17 +125,22 @@ class QUIC_EXPORT_PRIVATE QuicPacketWriter { // True=Batch mode. False=PassThrough mode. virtual bool IsBatchMode() const = 0; - // PassThrough mode: Return nullptr. + // PassThrough mode: Return {nullptr, nullptr} // // Batch mode: - // Return the starting address for the next packet's data. A minimum of + // Return the QuicPacketBuffer for the next packet. A minimum of // kMaxOutgoingPacketSize is guaranteed to be available from the returned - // address. If the internal buffer does not have enough space, nullptr is - // returned. All arguments should be identical to the follow-up call to - // |WritePacket|, they are here to allow advanced packet memory management in - // packet writers, e.g. one packet buffer pool per |peer_address|. - virtual char* GetNextWriteLocation(const QuicIpAddress& self_address, - const QuicSocketAddress& peer_address) = 0; + // address. If the internal buffer does not have enough space, + // {nullptr, nullptr} is returned. All arguments should be identical to the + // follow-up call to |WritePacket|, they are here to allow advanced packet + // memory management in packet writers, e.g. one packet buffer pool per + // |peer_address|. + // + // If QuicPacketBuffer.release_buffer is !nullptr, it should be called iff + // the caller does not call WritePacket for the returned buffer. + virtual QuicPacketBuffer GetNextWriteLocation( + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address) = 0; // PassThrough mode: Return WriteResult(WRITE_STATUS_OK, 0). // diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.cc index f1f25d871bf..d2b54d1b175 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.cc @@ -45,7 +45,7 @@ bool QuicPacketWriterWrapper::IsBatchMode() const { return writer_->IsBatchMode(); } -char* QuicPacketWriterWrapper::GetNextWriteLocation( +QuicPacketBuffer QuicPacketWriterWrapper::GetNextWriteLocation( const QuicIpAddress& self_address, const QuicSocketAddress& peer_address) { return writer_->GetNextWriteLocation(self_address, peer_address); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h b/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h index 0b331aec6f8..afd360580b4 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h @@ -35,8 +35,9 @@ class QUIC_NO_EXPORT QuicPacketWriterWrapper : public QuicPacketWriter { const QuicSocketAddress& peer_address) const override; bool SupportsReleaseTime() const override; bool IsBatchMode() const override; - char* GetNextWriteLocation(const QuicIpAddress& self_address, - const QuicSocketAddress& peer_address) override; + QuicPacketBuffer GetNextWriteLocation( + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address) override; WriteResult Flush() override; // Takes ownership of |writer|. diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packets.h b/chromium/net/third_party/quiche/src/quic/core/quic_packets.h index 04522e8538b..3e37a200c21 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packets.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packets.h @@ -379,6 +379,8 @@ struct QUIC_EXPORT_PRIVATE SerializedPacket { SerializedPacket(SerializedPacket&& other); ~SerializedPacket(); + // TODO(wub): replace |encrypted_buffer|+|release_encrypted_buffer| by a + // QuicOwnedPacketBuffer. // Not owned if |release_encrypted_buffer| is nullptr. Otherwise it is // released by |release_encrypted_buffer| on destruction. const char* encrypted_buffer; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.cc index 92401b0db72..43730135c14 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.cc @@ -32,9 +32,6 @@ static const size_t kMaxRetransmissionsOnTimeout = 2; // The path degrading delay is the sum of this number of consecutive RTO delays. const size_t kNumRetransmissionDelaysForPathDegradingDelay = 2; -// The blachkhole delay is the sum of this number of consecutive RTO delays. -const size_t kNumRetransmissionDelaysForBlackholeDelay = 5; - // Ensure the handshake timer isnt't faster than 10ms. // This limits the tenth retransmitted packet to 10s after the initial CHLO. static const int64_t kMinHandshakeTimeoutMs = 10; @@ -110,7 +107,8 @@ QuicSentPacketManager::QuicSentPacketManager( one_rtt_packet_acked_(false), one_rtt_packet_sent_(false), first_pto_srtt_multiplier_(0), - use_standard_deviation_for_pto_(false) { + use_standard_deviation_for_pto_(false), + pto_multiplier_without_rtt_samples_(3) { SetSendAlgorithm(congestion_control_type); if (pto_enabled_) { QUIC_RELOADABLE_FLAG_COUNT_N(quic_default_on_pto, 1, 2); @@ -200,6 +198,9 @@ void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) { } else if (config.HasClientSentConnectionOption(kPLE2, perspective)) { first_pto_srtt_multiplier_ = 1.5; } + if (config.HasClientSentConnectionOption(kAPTO, perspective)) { + pto_multiplier_without_rtt_samples_ = 1.5; + } if (config.HasClientSentConnectionOption(kPSDA, perspective)) { use_standard_deviation_for_pto_ = true; rtt_stats_.EnableStandardDeviationCalculation(); @@ -403,6 +404,17 @@ void QuicSentPacketManager::PostProcessNewlyAckedPackets( } } } + // Records the max consecutive RTO or PTO before forward progress has been + // made. + if (consecutive_rto_count_ > + stats_->max_consecutive_rto_with_forward_progress) { + stats_->max_consecutive_rto_with_forward_progress = + consecutive_rto_count_; + } else if (consecutive_pto_count_ > + stats_->max_consecutive_rto_with_forward_progress) { + stats_->max_consecutive_rto_with_forward_progress = + consecutive_pto_count_; + } // Reset all retransmit counters any time a new packet is acked. consecutive_rto_count_ = 0; consecutive_tlp_count_ = 0; @@ -442,40 +454,27 @@ void QuicSentPacketManager::MaybeInvokeCongestionEvent( } } -void QuicSentPacketManager::RetransmitUnackedPackets( - TransmissionType retransmission_type) { - DCHECK(retransmission_type == ALL_UNACKED_RETRANSMISSION || - retransmission_type == ALL_INITIAL_RETRANSMISSION); +void QuicSentPacketManager::RetransmitZeroRttPackets() { QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); for (QuicUnackedPacketMap::iterator it = unacked_packets_.begin(); it != unacked_packets_.end(); ++it, ++packet_number) { - if ((retransmission_type == ALL_UNACKED_RETRANSMISSION || - it->encryption_level == ENCRYPTION_ZERO_RTT)) { + if (it->encryption_level == ENCRYPTION_ZERO_RTT) { if (it->in_flight) { // Remove 0-RTT packets and packets of the wrong version from flight, // because neither can be processed by the peer. unacked_packets_.RemoveFromInFlight(&*it); } if (unacked_packets_.HasRetransmittableFrames(*it)) { - MarkForRetransmission(packet_number, retransmission_type); + MarkForRetransmission(packet_number, ALL_ZERO_RTT_RETRANSMISSION); } } } - if (retransmission_type == ALL_UNACKED_RETRANSMISSION && - unacked_packets_.bytes_in_flight() > 0) { - QUIC_BUG << "RetransmitUnackedPackets should remove all packets from flight" - << ", bytes_in_flight:" << unacked_packets_.bytes_in_flight(); - } } void QuicSentPacketManager::NeuterUnencryptedPackets() { for (QuicPacketNumber packet_number : unacked_packets_.NeuterUnencryptedPackets()) { - if (avoid_overestimate_bandwidth_with_aggregation_) { - QUIC_RELOADABLE_FLAG_COUNT_N( - quic_avoid_overestimate_bandwidth_with_aggregation, 1, 4); - send_algorithm_->OnPacketNeutered(packet_number); - } + send_algorithm_->OnPacketNeutered(packet_number); } if (handshake_mode_disabled_) { consecutive_pto_count_ = 0; @@ -486,11 +485,7 @@ void QuicSentPacketManager::NeuterUnencryptedPackets() { void QuicSentPacketManager::NeuterHandshakePackets() { for (QuicPacketNumber packet_number : unacked_packets_.NeuterHandshakePackets()) { - if (avoid_overestimate_bandwidth_with_aggregation_) { - QUIC_RELOADABLE_FLAG_COUNT_N( - quic_avoid_overestimate_bandwidth_with_aggregation, 2, 4); - send_algorithm_->OnPacketNeutered(packet_number); - } + send_algorithm_->OnPacketNeutered(packet_number); } if (handshake_mode_disabled_) { consecutive_pto_count_ = 0; @@ -573,7 +568,7 @@ void QuicSentPacketManager::MarkForRetransmission( QUIC_BUG_IF(transmission_type != LOSS_RETRANSMISSION && transmission_type != RTO_RETRANSMISSION && !unacked_packets_.HasRetransmittableFrames(*transmission_info)) - << "transmission_type: " << TransmissionTypeToString(transmission_type); + << "transmission_type: " << transmission_type; // Handshake packets should never be sent as probing retransmissions. DCHECK(!transmission_info->has_crypto_handshake || transmission_type != PROBING_RETRANSMISSION); @@ -646,8 +641,7 @@ void QuicSentPacketManager::MarkPacketHandled(QuicPacketNumber packet_number, // Record as a spurious retransmission if this packet is a // retransmission and no new data gets acked. QUIC_DVLOG(1) << "Detect spurious retransmitted packet " << packet_number - << " transmission type: " - << TransmissionTypeToString(info->transmission_type); + << " transmission type: " << info->transmission_type; RecordOneSpuriousRetransmission(*info); } } @@ -754,6 +748,9 @@ QuicSentPacketManager::OnRetransmissionTimeout() { case PTO_MODE: QUIC_DVLOG(1) << ENDPOINT << "PTO mode"; ++stats_->pto_count; + if (handshake_mode_disabled_ && !ShouldArmPtoForApplicationData()) { + ++stats_->crypto_retransmit_count; + } ++consecutive_pto_count_; pending_timer_transmission_count_ = max_probe_packets_per_pto_; return PTO_MODE; @@ -919,6 +916,26 @@ void QuicSentPacketManager::StartExponentialBackoffAfterNthPto( pto_exponential_backoff_start_point_ = exponential_backoff_start_point; } +void QuicSentPacketManager::RetransmitDataOfSpaceIfAny( + PacketNumberSpace space) { + DCHECK(supports_multiple_packet_number_spaces()); + if (!unacked_packets_.GetLastInFlightPacketSentTime(space).IsInitialized()) { + // No in flight data of space. + return; + } + QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); + for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin(); + it != unacked_packets_.end(); ++it, ++packet_number) { + if (it->state == OUTSTANDING && + unacked_packets_.HasRetransmittableFrames(*it) && + unacked_packets_.GetPacketNumberSpace(it->encryption_level) == space) { + DCHECK(it->in_flight); + MarkForRetransmission(packet_number, PTO_RETRANSMISSION); + return; + } + } +} + QuicSentPacketManager::RetransmissionTimeoutMode QuicSentPacketManager::GetRetransmissionMode() const { DCHECK(unacked_packets_.HasInFlightPackets() || @@ -958,14 +975,16 @@ void QuicSentPacketManager::InvokeLossDetection(QuicTime time) { detection_stats.sent_packets_max_sequence_reordering; } + stats_->sent_packets_num_borderline_time_reorderings += + detection_stats.sent_packets_num_borderline_time_reorderings; + + stats_->total_loss_detection_response_time += + detection_stats.total_loss_detection_response_time; + for (const LostPacket& packet : packets_lost_) { QuicTransmissionInfo* info = unacked_packets_.GetMutableTransmissionInfo(packet.packet_number); ++stats_->packets_lost; - if (time > info->sent_time) { - stats_->total_loss_detection_time = - stats_->total_loss_detection_time + (time - info->sent_time); - } if (debug_delegate_ != nullptr) { debug_delegate_->OnPacketLoss(packet.packet_number, info->encryption_level, LOSS_RETRANSMISSION, @@ -1041,6 +1060,16 @@ const QuicTime QuicSentPacketManager::GetRetransmissionTime() const { // Do not set the timer if there is any credit left. return QuicTime::Zero(); } + PacketNumberSpace packet_number_space; + if (GetQuicReloadableFlag(quic_fix_server_pto_timeout) && + supports_multiple_packet_number_spaces() && + unacked_packets_.perspective() == Perspective::IS_SERVER && + !GetEarliestPacketSentTimeForPto(&packet_number_space).IsInitialized()) { + // Do not set the timer on the server side if the only in flight packets are + // half RTT data. + QUIC_RELOADABLE_FLAG_COUNT(quic_fix_server_pto_timeout); + return QuicTime::Zero(); + } switch (GetRetransmissionMode()) { case HANDSHAKE_MODE: return unacked_packets_.GetLastCryptoPacketSentTime() + @@ -1131,9 +1160,10 @@ const QuicTime::Delta QuicSentPacketManager::GetPathDegradingDelay() const { max_tail_loss_probes_ + kNumRetransmissionDelaysForPathDegradingDelay); } -const QuicTime::Delta QuicSentPacketManager::GetNetworkBlackholeDelay() const { +const QuicTime::Delta QuicSentPacketManager::GetNetworkBlackholeDelay( + int8_t num_rtos_for_blackhole_detection) const { return GetNConsecutiveRetransmissionTimeoutDelay( - max_tail_loss_probes_ + kNumRetransmissionDelaysForBlackholeDelay); + max_tail_loss_probes_ + num_rtos_for_blackhole_detection); } const QuicTime::Delta QuicSentPacketManager::GetCryptoRetransmissionDelay() @@ -1202,8 +1232,9 @@ const QuicTime::Delta QuicSentPacketManager::GetProbeTimeoutDelay() const { if (rtt_stats_.smoothed_rtt().IsZero()) { // Respect kMinHandshakeTimeoutMs to avoid a potential amplification attack. QUIC_BUG_IF(rtt_stats_.initial_rtt().IsZero()); - return std::max(3 * rtt_stats_.initial_rtt(), - QuicTime::Delta::FromMilliseconds(kMinHandshakeTimeoutMs)) * + return std::max( + pto_multiplier_without_rtt_samples_ * rtt_stats_.initial_rtt(), + QuicTime::Delta::FromMilliseconds(kMinHandshakeTimeoutMs)) * (1 << consecutive_pto_count_); } const QuicTime::Delta rtt_var = use_standard_deviation_for_pto_ @@ -1354,8 +1385,7 @@ AckResult QuicSentPacketManager::OnAckFrameEnd( << ", least_unacked: " << unacked_packets_.GetLeastUnacked() << ", packets_acked_: " << packets_acked_; } else { - QUIC_PEER_BUG << "Received " - << EncryptionLevelToString(ack_decrypted_level) + QUIC_PEER_BUG << "Received " << ack_decrypted_level << " ack for unackable packet: " << acked_packet.packet_number << " with state: " << QuicUtils::SentPacketStateToString(info->state); @@ -1368,8 +1398,7 @@ AckResult QuicSentPacketManager::OnAckFrameEnd( } continue; } - QUIC_DVLOG(1) << ENDPOINT << "Got an " - << EncryptionLevelToString(ack_decrypted_level) + QUIC_DVLOG(1) << ENDPOINT << "Got an " << ack_decrypted_level << " ack for packet " << acked_packet.packet_number << " , state: " << QuicUtils::SentPacketStateToString(info->state); 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 371fa814537..72307b3b838 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 @@ -140,15 +140,11 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { // TODO(fayang): Rename this function to OnHandshakeComplete. void SetHandshakeConfirmed(); - // Requests retransmission of all unacked packets of |retransmission_type|. - // The behavior of this method depends on the value of |retransmission_type|: - // ALL_UNACKED_RETRANSMISSION - All unacked packets will be retransmitted. - // This can happen, for example, after a version negotiation packet has been - // received and all packets needs to be retransmitted with the new version. - // ALL_INITIAL_RETRANSMISSION - Only initially encrypted packets will be - // retransmitted. This can happen, for example, when a CHLO has been rejected - // and the previously encrypted data needs to be encrypted with a new key. - void RetransmitUnackedPackets(TransmissionType retransmission_type); + // Requests retransmission of all unacked 0-RTT packets. + // Only initially encrypted packets will be retransmitted. This can happen, + // for example, when a CHLO has been rejected and the previously encrypted + // data needs to be encrypted with a new key. + void RetransmitZeroRttPackets(); // Notify the sent packet manager of an external network measurement or // prediction for either |bandwidth| or |rtt|; either can be empty. @@ -216,7 +212,8 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { const QuicTime::Delta GetPathDegradingDelay() const; // Returns the current delay for detecting network blackhole. - const QuicTime::Delta GetNetworkBlackholeDelay() const; + const QuicTime::Delta GetNetworkBlackholeDelay( + int8_t num_rtos_for_blackhole_detection) const; const RttStats* GetRttStats() const { return &rtt_stats_; } @@ -397,6 +394,9 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { void StartExponentialBackoffAfterNthPto( size_t exponential_backoff_start_point); + // Called to retransmit in flight packet of |space| if any. + void RetransmitDataOfSpaceIfAny(PacketNumberSpace space); + bool supports_multiple_packet_number_spaces() const { return unacked_packets_.supports_multiple_packet_number_spaces(); } @@ -411,6 +411,8 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { bool one_rtt_packet_acked() const { return one_rtt_packet_acked_; } + void OnUserAgentIdKnown() { loss_algorithm_->OnUserAgentIdKnown(); } + private: friend class test::QuicConnectionPeer; friend class test::QuicSentPacketManagerPeer; @@ -657,8 +659,9 @@ class QUIC_EXPORT_PRIVATE QuicSentPacketManager { // calculating PTO timeout. bool use_standard_deviation_for_pto_; - const bool avoid_overestimate_bandwidth_with_aggregation_ = - GetQuicReloadableFlag(quic_avoid_overestimate_bandwidth_with_aggregation); + // The multiplier for caculating PTO timeout before any RTT sample is + // available. + float pto_multiplier_without_rtt_samples_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager_test.cc index 79fc798568b..97d83f57933 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager_test.cc @@ -7,6 +7,7 @@ #include <memory> #include <utility> +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" #include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" @@ -621,7 +622,7 @@ TEST_F(QuicSentPacketManagerTest, RetransmitTwiceThenAckFirst) { // know packet 2 is a spurious until it gets acked. EXPECT_EQ(1u, stats_.packets_spuriously_retransmitted); EXPECT_EQ(1u, stats_.packets_lost); - EXPECT_LT(QuicTime::Delta::Zero(), stats_.total_loss_detection_time); + EXPECT_LT(0.0, stats_.total_loss_detection_response_time); EXPECT_LE(1u, stats_.sent_packets_max_sequence_reordering); } @@ -942,6 +943,7 @@ TEST_F(QuicSentPacketManagerTest, TailLossProbeThenRTO) { manager_.OnRetransmissionTimeout(); EXPECT_EQ(2u, stats_.tlp_count); EXPECT_EQ(1u, stats_.rto_count); + EXPECT_EQ(0u, stats_.max_consecutive_rto_with_forward_progress); // There are 2 RTO retransmissions. EXPECT_EQ(104 * kDefaultLength, manager_.GetBytesInFlight()); QuicPacketNumber largest_acked = QuicPacketNumber(103); @@ -965,6 +967,7 @@ TEST_F(QuicSentPacketManagerTest, TailLossProbeThenRTO) { // All packets before 103 should be lost. // Packet 104 is still in flight. EXPECT_EQ(1000u, manager_.GetBytesInFlight()); + EXPECT_EQ(1u, stats_.max_consecutive_rto_with_forward_progress); } TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeout) { @@ -1019,54 +1022,6 @@ TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeout) { EXPECT_FALSE(manager_.HasUnackedCryptoPackets()); } -TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeoutVersionNegotiation) { - // Send 2 crypto packets and 3 data packets. - const size_t kNumSentCryptoPackets = 2; - for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) { - SendCryptoPacket(i); - } - const size_t kNumSentDataPackets = 3; - for (size_t i = 1; i <= kNumSentDataPackets; ++i) { - SendDataPacket(kNumSentCryptoPackets + i); - } - EXPECT_TRUE(manager_.HasUnackedCryptoPackets()); - - EXPECT_CALL(notifier_, RetransmitFrames(_, _)) - .Times(2) - .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(6); })) - .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(7); })); - manager_.OnRetransmissionTimeout(); - EXPECT_TRUE(manager_.HasUnackedCryptoPackets()); - - // Now act like a version negotiation packet arrived, which would cause all - // unacked packets to be retransmitted. - // Mark packets [1, 7] lost. And the frames in 6 and 7 are same as packets 1 - // and 2, respectively. - EXPECT_CALL(notifier_, OnFrameLost(_)).Times(7); - manager_.RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION); - - // Ensure the first two pending packets are the crypto retransmits. - RetransmitCryptoPacket(8); - RetransmitCryptoPacket(9); - RetransmitDataPacket(10, ALL_UNACKED_RETRANSMISSION); - RetransmitDataPacket(11, ALL_UNACKED_RETRANSMISSION); - RetransmitDataPacket(12, ALL_UNACKED_RETRANSMISSION); - - EXPECT_EQ(QuicPacketNumber(1u), manager_.GetLeastUnacked()); - // Least unacked isn't raised until an ack is received, so ack the - // crypto packets. - uint64_t acked[] = {8, 9}; - ExpectAcksAndLosses(true, acked, QUICHE_ARRAYSIZE(acked), nullptr, 0); - manager_.OnAckFrameStart(QuicPacketNumber(9), QuicTime::Delta::Infinite(), - clock_.Now()); - manager_.OnAckRange(QuicPacketNumber(8), QuicPacketNumber(10)); - EXPECT_EQ(PACKETS_NEWLY_ACKED, - manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(1), - ENCRYPTION_INITIAL)); - EXPECT_CALL(notifier_, HasUnackedCryptoData()).WillRepeatedly(Return(false)); - EXPECT_EQ(QuicPacketNumber(10u), manager_.GetLeastUnacked()); -} - TEST_F(QuicSentPacketManagerTest, CryptoHandshakeSpuriousRetransmission) { // Send 1 crypto packet. SendCryptoPacket(1); @@ -1119,28 +1074,6 @@ TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeoutUnsentDataPacket) { } TEST_F(QuicSentPacketManagerTest, - CryptoHandshakeRetransmissionThenRetransmitAll) { - // Send 1 crypto packet. - SendCryptoPacket(1); - - EXPECT_TRUE(manager_.HasUnackedCryptoPackets()); - - // Retransmit the crypto packet as 2. - EXPECT_CALL(notifier_, RetransmitFrames(_, _)) - .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(2); })); - manager_.OnRetransmissionTimeout(); - // Now retransmit all the unacked packets, which occurs when there is a - // version negotiation. - EXPECT_CALL(notifier_, OnFrameLost(_)).Times(2); - manager_.RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION); - // Both packets 1 and 2 are unackable. - EXPECT_FALSE(manager_.unacked_packets().IsUnacked(QuicPacketNumber(1))); - EXPECT_FALSE(manager_.unacked_packets().IsUnacked(QuicPacketNumber(2))); - EXPECT_TRUE(manager_.HasUnackedCryptoPackets()); - EXPECT_FALSE(manager_.HasInFlightPackets()); -} - -TEST_F(QuicSentPacketManagerTest, CryptoHandshakeRetransmissionThenNeuterAndAck) { // Send 1 crypto packet. SendCryptoPacket(1); @@ -2732,6 +2665,7 @@ TEST_F(QuicSentPacketManagerTest, ComputingProbeTimeout) { manager_.OnRetransmissionTimeout(); EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now())); EXPECT_EQ(1u, stats_.pto_count); + EXPECT_EQ(0u, stats_.max_consecutive_rto_with_forward_progress); // Verify two probe packets get sent. EXPECT_CALL(notifier_, RetransmitFrames(_, _)) @@ -2765,6 +2699,7 @@ TEST_F(QuicSentPacketManagerTest, ComputingProbeTimeout) { // Verify PTO is correctly re-armed based on sent time of packet 4. EXPECT_EQ(sent_time + expected_pto_delay, manager_.GetRetransmissionTime()); + EXPECT_EQ(1u, stats_.max_consecutive_rto_with_forward_progress); } TEST_F(QuicSentPacketManagerTest, SendOneProbePacket) { @@ -2811,7 +2746,7 @@ TEST_F(QuicSentPacketManagerTest, SendOneProbePacket) { TEST_F(QuicSentPacketManagerTest, DisableHandshakeModeClient) { QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); - manager_.EnableIetfPtoAndLossDetection(); + manager_.EnableMultiplePacketNumberSpacesSupport(); // Send CHLO. SendCryptoPacket(1); EXPECT_NE(QuicTime::Zero(), manager_.GetRetransmissionTime()); @@ -3340,6 +3275,7 @@ TEST_F(QuicSentPacketManagerTest, ClientMultiplePacketNumberSpacePtoTimeout) { manager_.OnRetransmissionTimeout(); EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now())); EXPECT_EQ(1u, stats_.pto_count); + EXPECT_EQ(1u, stats_.crypto_retransmit_count); // Verify probe packet gets sent. EXPECT_CALL(notifier_, RetransmitFrames(_, _)) @@ -3823,11 +3759,7 @@ TEST_F(QuicSentPacketManagerTest, SetHandshakeConfirmed) { return true; })); - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation)) { - EXPECT_CALL(*send_algorithm_, OnPacketNeutered(QuicPacketNumber(2))) - .Times(1); - } + EXPECT_CALL(*send_algorithm_, OnPacketNeutered(QuicPacketNumber(2))).Times(1); manager_.SetHandshakeConfirmed(); } @@ -3841,11 +3773,8 @@ TEST_F(QuicSentPacketManagerTest, NeuterUnencryptedPackets) { .WillOnce(Return(false)) .WillOnce(Return(true)); EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false)); - if (GetQuicReloadableFlag( - quic_avoid_overestimate_bandwidth_with_aggregation)) { - EXPECT_CALL(*send_algorithm_, OnPacketNeutered(QuicPacketNumber(1))) - .Times(1); - } + + EXPECT_CALL(*send_algorithm_, OnPacketNeutered(QuicPacketNumber(1))).Times(1); manager_.NeuterUnencryptedPackets(); } @@ -3911,7 +3840,7 @@ TEST_F(QuicSentPacketManagerTest, GetPathDegradingDelay) { // Regression test for b/154050235. TEST_F(QuicSentPacketManagerTest, ExponentialBackoffWithNoRttMeasurement) { QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); - manager_.EnableIetfPtoAndLossDetection(); + manager_.EnableMultiplePacketNumberSpacesSupport(); RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(kInitialRttMs), rtt_stats->initial_rtt()); @@ -3939,7 +3868,7 @@ TEST_F(QuicSentPacketManagerTest, ExponentialBackoffWithNoRttMeasurement) { } TEST_F(QuicSentPacketManagerTest, PtoDelayWithTinyInitialRtt) { - manager_.EnableIetfPtoAndLossDetection(); + manager_.EnableMultiplePacketNumberSpacesSupport(); RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); // Assume client provided a tiny initial RTT. rtt_stats->set_initial_rtt(QuicTime::Delta::FromMicroseconds(1)); @@ -3998,6 +3927,199 @@ TEST_F(QuicSentPacketManagerTest, HandshakeAckCausesInitialKeyDropping) { manager_.MaybeSendProbePackets(); } +// Regression test for b/156487311 +TEST_F(QuicSentPacketManagerTest, ClearLastInflightPacketsSentTime) { + manager_.EnableMultiplePacketNumberSpacesSupport(); + EXPECT_CALL(*send_algorithm_, PacingRate(_)) + .WillRepeatedly(Return(QuicBandwidth::Zero())); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) + .WillRepeatedly(Return(10 * kDefaultTCPMSS)); + + // Send INITIAL 1. + SendDataPacket(1, ENCRYPTION_INITIAL); + const QuicTime packet1_sent_time = clock_.Now(); + // Send HANDSHAKE 2. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + SendDataPacket(2, ENCRYPTION_HANDSHAKE); + SendDataPacket(3, ENCRYPTION_HANDSHAKE); + SendDataPacket(4, ENCRYPTION_HANDSHAKE); + const QuicTime packet2_sent_time = clock_.Now(); + + // Send half RTT 5. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + SendDataPacket(5, ENCRYPTION_FORWARD_SECURE); + + // Received ACK for INITIAL 1. + ExpectAck(1); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(90)); + manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_EQ(PACKETS_NEWLY_ACKED, + manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(1), + ENCRYPTION_INITIAL)); + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + int pto_rttvar_multiplier = + GetQuicReloadableFlag(quic_default_on_pto) ? 2 : 4; + const QuicTime::Delta pto_delay = + rtt_stats->smoothed_rtt() + + pto_rttvar_multiplier * rtt_stats->mean_deviation() + + QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs); + if (GetQuicReloadableFlag(quic_fix_last_inflight_packets_sent_time)) { + // Verify PTO is armed based on handshake data. + EXPECT_EQ(packet2_sent_time + pto_delay, manager_.GetRetransmissionTime()); + } else { + // Problematic: PTO is still armed based on initial data. + EXPECT_EQ(packet1_sent_time + pto_delay, manager_.GetRetransmissionTime()); + clock_.AdvanceTime(pto_delay); + manager_.OnRetransmissionTimeout(); + // Nothing to retransmit in INITIAL space. + EXPECT_CALL(notifier_, RetransmitFrames(_, _)).Times(0); + manager_.MaybeSendProbePackets(); + // PING packet gets sent. + SendPingPacket(6, ENCRYPTION_INITIAL); + manager_.AdjustPendingTimerTransmissions(); + + // Verify PTO is armed based on packet 2. + EXPECT_EQ(packet2_sent_time + pto_delay * 2, + manager_.GetRetransmissionTime()); + clock_.AdvanceTime(pto_delay * 2); + manager_.OnRetransmissionTimeout(); + EXPECT_CALL(notifier_, RetransmitFrames(_, _)).Times(testing::AtLeast(1)); + manager_.MaybeSendProbePackets(); + } +} + +// Regression test for b/157895910. +TEST_F(QuicSentPacketManagerTest, EarliestSentTimeNotInitializedWhenPtoFires) { + SetQuicReloadableFlag(quic_fix_last_inflight_packets_sent_time, true); + manager_.EnableMultiplePacketNumberSpacesSupport(); + EXPECT_CALL(*send_algorithm_, PacingRate(_)) + .WillRepeatedly(Return(QuicBandwidth::Zero())); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) + .WillRepeatedly(Return(10 * kDefaultTCPMSS)); + + // Send INITIAL 1. + SendDataPacket(1, ENCRYPTION_INITIAL); + + // Send HANDSHAKE packets. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + SendDataPacket(2, ENCRYPTION_HANDSHAKE); + SendDataPacket(3, ENCRYPTION_HANDSHAKE); + SendDataPacket(4, ENCRYPTION_HANDSHAKE); + + // Send half RTT packet. + SendDataPacket(5, ENCRYPTION_FORWARD_SECURE); + + // Received ACK for INITIAL packet 1. + ExpectAck(1); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(90)); + manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)); + EXPECT_EQ(PACKETS_NEWLY_ACKED, + manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(1), + ENCRYPTION_INITIAL)); + + // Received ACK for HANDSHAKE packets. + uint64_t acked[] = {2, 3, 4}; + ExpectAcksAndLosses(true, acked, QUICHE_ARRAYSIZE(acked), nullptr, 0); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(90)); + manager_.OnAckFrameStart(QuicPacketNumber(4), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(5)); + EXPECT_EQ(PACKETS_NEWLY_ACKED, + manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(4), + ENCRYPTION_HANDSHAKE)); + if (GetQuicReloadableFlag(quic_fix_server_pto_timeout)) { + // Verify PTO will not be armed. + EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime()); + return; + } + // PTO fires but there is nothing to send. + EXPECT_NE(QuicTime::Zero(), manager_.GetRetransmissionTime()); + manager_.OnRetransmissionTimeout(); + EXPECT_QUIC_BUG(manager_.MaybeSendProbePackets(), + "earlist_sent_time not initialized when trying to send PTO " + "retransmissions"); +} + +TEST_F(QuicSentPacketManagerTest, MaybeRetransmitInitialData) { + manager_.EnableMultiplePacketNumberSpacesSupport(); + EXPECT_CALL(*send_algorithm_, PacingRate(_)) + .WillRepeatedly(Return(QuicBandwidth::Zero())); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) + .WillRepeatedly(Return(10 * kDefaultTCPMSS)); + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(100), + QuicTime::Delta::Zero(), QuicTime::Zero()); + QuicTime::Delta srtt = rtt_stats->smoothed_rtt(); + + // Send packet 1. + SendDataPacket(1, ENCRYPTION_INITIAL); + QuicTime packet1_sent_time = clock_.Now(); + + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + // Send packets 2 and 3. + SendDataPacket(2, ENCRYPTION_HANDSHAKE); + QuicTime packet2_sent_time = clock_.Now(); + SendDataPacket(3, ENCRYPTION_HANDSHAKE); + // Verify PTO is correctly set based on packet 1. + int pto_rttvar_multiplier = + GetQuicReloadableFlag(quic_default_on_pto) ? 2 : 4; + QuicTime::Delta expected_pto_delay = + srtt + pto_rttvar_multiplier * rtt_stats->mean_deviation() + + QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs); + EXPECT_EQ(packet1_sent_time + expected_pto_delay, + manager_.GetRetransmissionTime()); + + // Assume connection is going to send INITIAL ACK. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(4, type, ENCRYPTION_INITIAL); + }))); + manager_.RetransmitDataOfSpaceIfAny(INITIAL_DATA); + // Verify PTO is re-armed based on packet 2. + EXPECT_EQ(packet2_sent_time + expected_pto_delay, + manager_.GetRetransmissionTime()); + + // Connection is going to send another INITIAL ACK. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(5, type, ENCRYPTION_INITIAL); + }))); + manager_.RetransmitDataOfSpaceIfAny(INITIAL_DATA); + // Verify PTO does not change. + EXPECT_EQ(packet2_sent_time + expected_pto_delay, + manager_.GetRetransmissionTime()); +} + +TEST_F(QuicSentPacketManagerTest, + AggressivePtoBeforeAnyRttSamplesAreAvailable) { + manager_.EnableMultiplePacketNumberSpacesSupport(); + EXPECT_CALL(*send_algorithm_, PacingRate(_)) + .WillRepeatedly(Return(QuicBandwidth::Zero())); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) + .WillRepeatedly(Return(10 * kDefaultTCPMSS)); + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + + QuicConfig config; + QuicTagVector options; + options.push_back(kAPTO); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + + // Send INITIAL 1. + SendDataPacket(1, ENCRYPTION_INITIAL); + // Verify retransmission timeout is expected. + EXPECT_EQ(clock_.Now() + 1.5 * rtt_stats->initial_rtt(), + manager_.GetRetransmissionTime()); +} + } // namespace } // namespace test } // namespace quic 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 0f00ff3843d..59e6cc8093e 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 @@ -73,12 +73,9 @@ QuicSession::QuicSession( config_.GetMaxBidirectionalStreamsToSend(), config_.GetMaxUnidirectionalStreamsToSend() + num_expected_unidirectional_static_streams), - num_dynamic_incoming_streams_(0), - num_draining_incoming_streams_(0), - num_draining_outgoing_streams_(0), - num_outgoing_static_streams_(0), - num_incoming_static_streams_(0), - num_locally_closed_incoming_streams_highest_offset_(0), + num_draining_streams_(0), + num_outgoing_draining_streams_(0), + num_static_streams_(0), flow_controller_( this, QuicUtils::GetInvalidStreamId(connection->transport_version()), @@ -101,10 +98,9 @@ QuicSession::QuicSession( use_http2_priority_write_scheduler_(false), is_configured_(false), enable_round_robin_scheduling_(false), - deprecate_draining_streams_( - GetQuicReloadableFlag(quic_deprecate_draining_streams)), - break_close_loop_( - GetQuicReloadableFlag(quic_break_session_stream_close_loop)) { + was_zero_rtt_rejected_(false), + fix_gquic_stream_type_( + GetQuicReloadableFlag(quic_fix_gquic_stream_type)) { closed_streams_clean_up_alarm_ = QuicWrapUnique<QuicAlarm>(connection_->alarm_factory()->CreateAlarm( new ClosedStreamsCleanUpDelegate(this))); @@ -117,9 +113,6 @@ QuicSession::QuicSession( config_.GetMaxUnidirectionalStreamsToSend() + num_expected_unidirectional_static_streams); } - if (break_close_loop_) { - QUIC_RELOADABLE_FLAG_COUNT(quic_break_session_stream_close_loop); - } } void QuicSession::Initialize() { @@ -127,7 +120,11 @@ void QuicSession::Initialize() { connection_->SetSessionNotifier(this); connection_->SetDataProducer(this); connection_->SetFromConfig(config_); - + if (GetQuicReloadableFlag(quic_support_handshake_done_in_t050) && + version().UsesTls() && !version().HasHandshakeDone() && + perspective_ == Perspective::IS_CLIENT) { + config_.SetSupportHandshakeDone(); + } // On the server side, version negotiation has been done by the dispatcher, // and the server session is created with the right version. if (perspective() == Perspective::IS_SERVER) { @@ -144,16 +141,6 @@ void QuicSession::Initialize() { QuicSession::~QuicSession() { QUIC_LOG_IF(WARNING, !zombie_streams_.empty()) << "Still have zombie streams"; - QUIC_LOG_IF(WARNING, num_locally_closed_incoming_streams_highest_offset() > - stream_id_manager_.max_open_incoming_streams()) - << "Surprisingly high number of locally closed peer initiated streams" - "still waiting for final byte offset: " - << num_locally_closed_incoming_streams_highest_offset(); - QUIC_LOG_IF(WARNING, GetNumLocallyClosedOutgoingStreamsHighestOffset() > - stream_id_manager_.max_open_outgoing_streams()) - << "Surprisingly high number of locally closed self initiated streams" - "still waiting for final byte offset: " - << GetNumLocallyClosedOutgoingStreamsHighestOffset(); } void QuicSession::PendingStreamOnStreamFrame(const QuicStreamFrame& frame) { @@ -197,8 +184,8 @@ void QuicSession::OnStreamFrame(const QuicStreamFrame& frame) { if (UsesPendingStreams() && QuicUtils::GetStreamType(stream_id, perspective(), - IsIncomingStream(stream_id)) == - READ_UNIDIRECTIONAL && + IsIncomingStream(stream_id), + version()) == READ_UNIDIRECTIONAL && stream_map_.find(stream_id) == stream_map_.end()) { PendingStreamOnStreamFrame(frame); return; @@ -245,8 +232,8 @@ void QuicSession::OnStopSendingFrame(const QuicStopSendingFrame& frame) { // If stream_id is READ_UNIDIRECTIONAL, close the connection. if (QuicUtils::GetStreamType(stream_id, perspective(), - IsIncomingStream(stream_id)) == - READ_UNIDIRECTIONAL) { + IsIncomingStream(stream_id), + version()) == READ_UNIDIRECTIONAL) { QUIC_DVLOG(1) << ENDPOINT << "Received STOP_SENDING for a read-only stream_id: " << stream_id << "."; @@ -311,7 +298,7 @@ void QuicSession::PendingStreamOnRstStream(const QuicRstStreamFrame& frame) { // Pending stream is currently read only. We can safely close the stream. DCHECK_EQ(READ_UNIDIRECTIONAL, QuicUtils::GetStreamType(pending->id(), perspective(), - /*peer_initiated = */ true)); + /*peer_initiated = */ true, version())); ClosePendingStream(stream_id); } @@ -326,8 +313,8 @@ void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) { if (VersionHasIetfQuicFrames(transport_version()) && QuicUtils::GetStreamType(stream_id, perspective(), - IsIncomingStream(stream_id)) == - WRITE_UNIDIRECTIONAL) { + IsIncomingStream(stream_id), + version()) == WRITE_UNIDIRECTIONAL) { connection()->CloseConnection( QUIC_INVALID_STREAM_ID, "Received RESET_STREAM for a write-only stream", ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); @@ -340,8 +327,8 @@ void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) { if (UsesPendingStreams() && QuicUtils::GetStreamType(stream_id, perspective(), - IsIncomingStream(stream_id)) == - READ_UNIDIRECTIONAL && + IsIncomingStream(stream_id), + version()) == READ_UNIDIRECTIONAL && stream_map_.find(stream_id) == stream_map_.end()) { PendingStreamOnRstStream(frame); return; @@ -398,10 +385,7 @@ void QuicSession::OnConnectionClosed(const QuicConnectionCloseFrame& frame, on_closed_frame_ = frame; } - if (GetQuicReloadableFlag(quic_notify_handshaker_on_connection_close)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_notify_handshaker_on_connection_close); - GetMutableCryptoStream()->OnConnectionClosed(frame.quic_error_code, source); - } + GetMutableCryptoStream()->OnConnectionClosed(frame.quic_error_code, source); // Copy all non static streams in a new map for the ease of deleting. QuicSmallMap<QuicStreamId, QuicStream*, 10> non_static_streams; @@ -460,12 +444,12 @@ void QuicSession::OnPacketReceived(const QuicSocketAddress& /*self_address*/, void QuicSession::OnPathDegrading() {} +void QuicSession::OnForwardProgressMadeAfterPathDegrading() {} + bool QuicSession::AllowSelfAddressChange() const { return false; } -void QuicSession::OnForwardProgressConfirmed() {} - void QuicSession::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { // Stream may be closed by the time we receive a WINDOW_UPDATE, so we can't // assume that it still exists. @@ -483,8 +467,8 @@ void QuicSession::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { if (VersionHasIetfQuicFrames(transport_version()) && QuicUtils::GetStreamType(stream_id, perspective(), - IsIncomingStream(stream_id)) == - READ_UNIDIRECTIONAL) { + IsIncomingStream(stream_id), + version()) == READ_UNIDIRECTIONAL) { connection()->CloseConnection( QUIC_WINDOW_UPDATE_RECEIVED_ON_READ_UNIDIRECTIONAL_STREAM, "WindowUpdateFrame received on READ_UNIDIRECTIONAL stream.", @@ -619,7 +603,7 @@ void QuicSession::OnCanWrite() { ConnectionCloseBehavior::SILENT_CLOSE); return; } - if (!connection_->CanWriteStreamData()) { + if (!CanWriteStreamData()) { return; } currently_writing_stream_id_ = write_blocked_streams_.PopFront(); @@ -663,11 +647,26 @@ bool QuicSession::WillingAndAbleToWrite() const { HasPendingHandshake()) { return true; } - return control_frame_manager_.WillingToWrite() || - !streams_with_pending_retransmission_.empty() || - write_blocked_streams_.HasWriteBlockedSpecialStream() || - (!flow_controller_.IsBlocked() && - write_blocked_streams_.HasWriteBlockedDataStreams()); + if (control_frame_manager_.WillingToWrite() || + !streams_with_pending_retransmission_.empty()) { + return true; + } + if (!GetQuicReloadableFlag(quic_fix_willing_and_able_to_write)) { + return write_blocked_streams_.HasWriteBlockedSpecialStream() || + (!flow_controller_.IsBlocked() && + write_blocked_streams_.HasWriteBlockedDataStreams()); + } + QUIC_RELOADABLE_FLAG_COUNT(quic_fix_willing_and_able_to_write); + if (flow_controller_.IsBlocked()) { + if (VersionUsesHttp3(transport_version())) { + return false; + } + // Crypto and headers streams are not blocked by connection level flow + // control. + return write_blocked_streams_.HasWriteBlockedSpecialStream(); + } + return write_blocked_streams_.HasWriteBlockedSpecialStream() || + write_blocked_streams_.HasWriteBlockedDataStreams(); } bool QuicSession::HasPendingHandshake() const { @@ -681,12 +680,6 @@ bool QuicSession::HasPendingHandshake() const { QuicUtils::GetCryptoStreamId(transport_version())); } -uint64_t QuicSession::GetNumOpenDynamicStreams() const { - return stream_map_.size() - GetNumDrainingStreams() + - locally_closed_streams_highest_offset_.size() - - num_incoming_static_streams_ - num_outgoing_static_streams_; -} - void QuicSession::ProcessUdpPacket(const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, const QuicReceivedPacket& packet) { @@ -763,22 +756,11 @@ void QuicSession::SendRstStream(QuicStreamId id, connection_->OnStreamReset(id, error); } - - if (break_close_loop_) { - return; - } - - if (error != QUIC_STREAM_NO_ERROR && QuicContainsKey(zombie_streams_, id)) { - OnStreamDoneWaitingForAcks(id); - return; - } - CloseStreamInner(id, true); } void QuicSession::ResetStream(QuicStreamId id, QuicRstStreamErrorCode error, QuicStreamOffset bytes_written) { - DCHECK(break_close_loop_); QuicStream* stream = GetStream(id); if (stream != nullptr && stream->is_static()) { connection()->CloseConnection( @@ -800,8 +782,8 @@ void QuicSession::MaybeSendRstStreamFrame(QuicStreamId id, QuicStreamOffset bytes_written) { DCHECK(connection()->connected()); if (!VersionHasIetfQuicFrames(transport_version()) || - QuicUtils::GetStreamType(id, perspective(), IsIncomingStream(id)) != - READ_UNIDIRECTIONAL) { + QuicUtils::GetStreamType(id, perspective(), IsIncomingStream(id), + version()) != READ_UNIDIRECTIONAL) { control_frame_manager_.WriteOrBufferRstStream(id, error, bytes_written); } } @@ -810,8 +792,8 @@ void QuicSession::MaybeSendStopSendingFrame(QuicStreamId id, QuicRstStreamErrorCode error) { DCHECK(connection()->connected()); if (VersionHasIetfQuicFrames(transport_version()) && - QuicUtils::GetStreamType(id, perspective(), IsIncomingStream(id)) != - WRITE_UNIDIRECTIONAL) { + QuicUtils::GetStreamType(id, perspective(), IsIncomingStream(id), + version()) != WRITE_UNIDIRECTIONAL) { control_frame_manager_.WriteOrBufferStopSending(error, id); } } @@ -854,24 +836,11 @@ void QuicSession::SendMaxStreams(QuicStreamCount stream_count, } void QuicSession::CloseStream(QuicStreamId stream_id) { - CloseStreamInner(stream_id, false); -} - -void QuicSession::InsertLocallyClosedStreamsHighestOffset( - const QuicStreamId id, - QuicStreamOffset offset) { - locally_closed_streams_highest_offset_[id] = offset; - if (IsIncomingStream(id)) { - ++num_locally_closed_incoming_streams_highest_offset_; - } -} - -void QuicSession::CloseStreamInner(QuicStreamId stream_id, bool rst_sent) { QUIC_DVLOG(1) << ENDPOINT << "Closing stream " << stream_id; StreamMap::iterator it = stream_map_.find(stream_id); if (it == stream_map_.end()) { - // When CloseStreamInner has been called recursively (via + // When CloseStream has been called recursively (via // QuicStream::OnClose), the stream will already have been deleted // from stream_map_, so return immediately. QUIC_DVLOG(1) << ENDPOINT << "Stream is already closed: " << stream_id; @@ -887,89 +856,18 @@ void QuicSession::CloseStreamInner(QuicStreamId stream_id, bool rst_sent) { ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return; } - if (break_close_loop_) { - stream->CloseReadSide(); - stream->CloseWriteSide(); - return; - } - StreamType type = stream->type(); - - // Tell the stream that a RST has been sent. - if (rst_sent) { - stream->set_rst_sent(true); - } - - if (stream->IsWaitingForAcks()) { - zombie_streams_[stream->id()] = std::move(it->second); - } else { - // Clean up the stream since it is no longer waiting for acks. - streams_waiting_for_acks_.erase(stream->id()); - closed_streams_.push_back(std::move(it->second)); - // Do not retransmit data of a closed stream. - streams_with_pending_retransmission_.erase(stream_id); - if (!closed_streams_clean_up_alarm_->IsSet()) { - closed_streams_clean_up_alarm_->Set( - connection_->clock()->ApproximateNow()); - } - } - - // If we haven't received a FIN or RST for this stream, we need to keep track - // of the how many bytes the stream's flow controller believes it has - // received, for accurate connection level flow control accounting. - const bool had_fin_or_rst = stream->HasReceivedFinalOffset(); - if (!had_fin_or_rst) { - InsertLocallyClosedStreamsHighestOffset( - stream_id, stream->flow_controller()->highest_received_byte_offset()); - } - bool stream_was_draining = false; - if (deprecate_draining_streams_) { - stream_was_draining = stream->was_draining(); - QUIC_DVLOG_IF(1, stream_was_draining) - << ENDPOINT << "Stream " << stream_id << " was draining"; - } - stream_map_.erase(it); - if (IsIncomingStream(stream_id)) { - --num_dynamic_incoming_streams_; - } - if (!deprecate_draining_streams_) { - stream_was_draining = - draining_streams_.find(stream_id) != draining_streams_.end(); - } - if (stream_was_draining) { - if (IsIncomingStream(stream_id)) { - QUIC_BUG_IF(num_draining_incoming_streams_ == 0); - --num_draining_incoming_streams_; - } else if (deprecate_draining_streams_) { - QUIC_BUG_IF(num_draining_outgoing_streams_ == 0); - --num_draining_outgoing_streams_; - } - draining_streams_.erase(stream_id); - } else if (VersionHasIetfQuicFrames(transport_version())) { - // Stream was not draining, but we did have a fin or rst, so we can now - // free the stream ID if version 99. - if (had_fin_or_rst && connection_->connected()) { - // Do not bother informing stream ID manager if connection is closed. - v99_streamid_manager_.OnStreamClosed(stream_id); - } - } else if (stream_id_manager_.handles_accounting() && had_fin_or_rst && - connection_->connected()) { - stream_id_manager_.OnStreamClosed( - /*is_incoming=*/IsIncomingStream(stream_id)); - } - - stream->OnClose(); + stream->CloseReadSide(); + stream->CloseWriteSide(); +} - if (!stream_was_draining && !IsIncomingStream(stream_id) && had_fin_or_rst && - !VersionHasIetfQuicFrames(transport_version())) { - // Streams that first became draining already called OnCanCreate... - // This covers the case where the stream went directly to being closed. - OnCanCreateNewOutgoingStream(type != BIDIRECTIONAL); - } +void QuicSession::InsertLocallyClosedStreamsHighestOffset( + const QuicStreamId id, + QuicStreamOffset offset) { + locally_closed_streams_highest_offset_[id] = offset; } void QuicSession::OnStreamClosed(QuicStreamId stream_id) { QUIC_DVLOG(1) << ENDPOINT << "Closing stream: " << stream_id; - DCHECK(break_close_loop_); StreamMap::iterator it = stream_map_.find(stream_id); if (it == stream_map_.end()) { QUIC_DVLOG(1) << ENDPOINT << "Stream is already closed: " << stream_id; @@ -1002,48 +900,38 @@ void QuicSession::OnStreamClosed(QuicStreamId stream_id) { InsertLocallyClosedStreamsHighestOffset( stream_id, stream->flow_controller()->highest_received_byte_offset()); stream_map_.erase(it); - if (IsIncomingStream(stream_id)) { - --num_dynamic_incoming_streams_; - } return; } - bool stream_was_draining = false; - if (deprecate_draining_streams_) { - stream_was_draining = stream->was_draining(); - QUIC_DVLOG_IF(1, stream_was_draining) - << ENDPOINT << "Stream " << stream_id << " was draining"; - } + const bool stream_was_draining = stream->was_draining(); + QUIC_DVLOG_IF(1, stream_was_draining) + << ENDPOINT << "Stream " << stream_id << " was draining"; stream_map_.erase(it); - if (IsIncomingStream(stream_id)) { - --num_dynamic_incoming_streams_; - } - if (!deprecate_draining_streams_) { - stream_was_draining = - draining_streams_.find(stream_id) != draining_streams_.end(); - } if (stream_was_draining) { - if (IsIncomingStream(stream_id)) { - QUIC_BUG_IF(num_draining_incoming_streams_ == 0); - --num_draining_incoming_streams_; - } else if (deprecate_draining_streams_) { - QUIC_BUG_IF(num_draining_outgoing_streams_ == 0); - --num_draining_outgoing_streams_; + QUIC_BUG_IF(num_draining_streams_ == 0); + --num_draining_streams_; + if (!IsIncomingStream(stream_id)) { + QUIC_BUG_IF(num_outgoing_draining_streams_ == 0); + --num_outgoing_draining_streams_; } - draining_streams_.erase(stream_id); // Stream Id manager has been informed with draining streams. return; } - if (!connection_->connected()) { + if (!GetQuicReloadableFlag(quic_notify_stream_id_manager_when_disconnected) && + !connection_->connected()) { // Do not bother informing stream ID manager if connection has been // disconnected. return; } - if (stream_id_manager_.handles_accounting() && - !VersionHasIetfQuicFrames(transport_version())) { + if (!VersionHasIetfQuicFrames(transport_version())) { stream_id_manager_.OnStreamClosed( /*is_incoming=*/IsIncomingStream(stream_id)); } + if (GetQuicReloadableFlag(quic_notify_stream_id_manager_when_disconnected) && + !connection_->connected()) { + QUIC_RELOADABLE_FLAG_COUNT(quic_notify_stream_id_manager_when_disconnected); + return; + } if (IsIncomingStream(stream_id)) { // Stream Id manager is only interested in peer initiated stream IDs. if (VersionHasIetfQuicFrames(transport_version())) { @@ -1090,13 +978,11 @@ void QuicSession::OnFinalByteOffsetReceived( flow_controller_.AddBytesConsumed(offset_diff); locally_closed_streams_highest_offset_.erase(it); - if (stream_id_manager_.handles_accounting() && - !VersionHasIetfQuicFrames(transport_version())) { + if (!VersionHasIetfQuicFrames(transport_version())) { stream_id_manager_.OnStreamClosed( /*is_incoming=*/IsIncomingStream(stream_id)); } if (IsIncomingStream(stream_id)) { - --num_locally_closed_incoming_streams_highest_offset_; if (VersionHasIetfQuicFrames(transport_version())) { v99_streamid_manager_.OnStreamClosed(stream_id); } @@ -1120,6 +1006,21 @@ bool QuicSession::OneRttKeysAvailable() const { } void QuicSession::OnConfigNegotiated() { + // In versions with TLS, the configs will be set twice if 0-RTT is available. + // In the second config setting, 1-RTT keys are guaranteed to be available. + if (GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls) && + version().UsesTls() && is_configured_ && + connection_->encryption_level() != ENCRYPTION_FORWARD_SECURE) { + QUIC_BUG + << ENDPOINT + << "1-RTT keys missing when config is negotiated for the second time."; + connection_->CloseConnection( + QUIC_INTERNAL_ERROR, + "1-RTT keys missing when config is negotiated for the second time.", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + QUIC_DVLOG(1) << ENDPOINT << "OnConfigNegotiated"; connection_->SetFromConfig(config_); @@ -1128,6 +1029,19 @@ void QuicSession::OnConfigNegotiated() { if (config_.HasReceivedMaxBidirectionalStreams()) { max_streams = config_.ReceivedMaxBidirectionalStreams(); } + if (was_zero_rtt_rejected_ && + max_streams < + v99_streamid_manager_.outgoing_bidirectional_stream_count()) { + connection_->CloseConnection( + QUIC_ZERO_RTT_UNRETRANSMITTABLE, + quiche::QuicheStrCat( + "Server rejected 0-RTT, aborting because new bidirectional " + "initial stream limit ", + max_streams, " is less than current open streams: ", + v99_streamid_manager_.outgoing_bidirectional_stream_count()), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } QUIC_DVLOG(1) << ENDPOINT << "Setting Bidirectional outgoing_max_streams_ to " << max_streams; @@ -1135,8 +1049,12 @@ void QuicSession::OnConfigNegotiated() { max_streams < v99_streamid_manager_.max_outgoing_bidirectional_streams()) { connection_->CloseConnection( - QUIC_MAX_STREAMS_ERROR, + was_zero_rtt_rejected_ ? QUIC_ZERO_RTT_REJECTION_LIMIT_REDUCED + : QUIC_ZERO_RTT_RESUMPTION_LIMIT_REDUCED, quiche::QuicheStrCat( + was_zero_rtt_rejected_ + ? "Server rejected 0-RTT, aborting because " + : "", "new bidirectional limit ", max_streams, " decreases the current limit: ", v99_streamid_manager_.max_outgoing_bidirectional_streams()), @@ -1152,11 +1070,30 @@ void QuicSession::OnConfigNegotiated() { if (config_.HasReceivedMaxUnidirectionalStreams()) { max_streams = config_.ReceivedMaxUnidirectionalStreams(); } + + if (was_zero_rtt_rejected_ && + max_streams < + v99_streamid_manager_.outgoing_unidirectional_stream_count()) { + connection_->CloseConnection( + QUIC_ZERO_RTT_UNRETRANSMITTABLE, + quiche::QuicheStrCat( + "Server rejected 0-RTT, aborting because new unidirectional " + "initial stream limit ", + max_streams, " is less than current open streams: ", + v99_streamid_manager_.outgoing_unidirectional_stream_count()), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + if (max_streams < v99_streamid_manager_.max_outgoing_unidirectional_streams()) { connection_->CloseConnection( - QUIC_MAX_STREAMS_ERROR, + was_zero_rtt_rejected_ ? QUIC_ZERO_RTT_REJECTION_LIMIT_REDUCED + : QUIC_ZERO_RTT_RESUMPTION_LIMIT_REDUCED, quiche::QuicheStrCat( + was_zero_rtt_rejected_ + ? "Server rejected 0-RTT, aborting because " + : "", "new unidirectional limit ", max_streams, " decreases the current limit: ", v99_streamid_manager_.max_outgoing_unidirectional_streams()), @@ -1177,6 +1114,17 @@ void QuicSession::OnConfigNegotiated() { } QUIC_DVLOG(1) << ENDPOINT << "Setting max_open_outgoing_streams_ to " << max_streams; + if (was_zero_rtt_rejected_ && + max_streams < stream_id_manager_.num_open_outgoing_streams()) { + connection_->CloseConnection( + QUIC_INTERNAL_ERROR, + quiche::QuicheStrCat( + "Server rejected 0-RTT, aborting because new stream limit ", + max_streams, " is less than current open streams: ", + stream_id_manager_.num_open_outgoing_streams()), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } stream_id_manager_.set_max_open_outgoing_streams(max_streams); } @@ -1271,11 +1219,16 @@ void QuicSession::OnConfigNegotiated() { OnNewSessionFlowControlWindow( config_.ReceivedInitialSessionFlowControlWindowBytes()); } + is_configured_ = true; connection()->OnConfigNegotiated(); // Ask flow controllers to try again since the config could have unblocked us. - if (connection_->version().AllowsLowFlowControlLimits()) { + // Or if this session is configured on TLS enabled QUIC versions, + // attempt to retransmit 0-RTT data if there's any. + if (connection_->version().AllowsLowFlowControlLimits() || + (GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls) && + version().UsesTls())) { OnCanWrite(); } } @@ -1373,8 +1326,15 @@ void QuicSession::OnNewStreamUnidirectionalFlowControlWindow( // Inform all existing outgoing unidirectional streams about the new window. for (auto const& kv : stream_map_) { const QuicStreamId id = kv.first; - if (QuicUtils::IsBidirectionalStreamId(id)) { - continue; + if (fix_gquic_stream_type_ && !version().HasIetfQuicFrames()) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_fix_gquic_stream_type, 1, 3); + if (kv.second->type() == BIDIRECTIONAL) { + continue; + } + } else { + if (QuicUtils::IsBidirectionalStreamId(id, version())) { + continue; + } } if (!QuicUtils::IsOutgoingStreamId(connection_->version(), id, perspective())) { @@ -1382,6 +1342,9 @@ void QuicSession::OnNewStreamUnidirectionalFlowControlWindow( } QUIC_DVLOG(1) << ENDPOINT << "Informing unidirectional stream " << id << " of new stream flow control window " << new_window; + if (!ValidateStreamFlowControlLimit(new_window, kv.second.get())) { + return; + } if (!kv.second->ConfigSendWindowOffset(new_window)) { return; } @@ -1397,8 +1360,15 @@ void QuicSession::OnNewStreamOutgoingBidirectionalFlowControlWindow( // Inform all existing outgoing bidirectional streams about the new window. for (auto const& kv : stream_map_) { const QuicStreamId id = kv.first; - if (!QuicUtils::IsBidirectionalStreamId(id)) { - continue; + if (fix_gquic_stream_type_ && !version().HasIetfQuicFrames()) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_fix_gquic_stream_type, 2, 3); + if (kv.second->type() != BIDIRECTIONAL) { + continue; + } + } else { + if (!QuicUtils::IsBidirectionalStreamId(id, version())) { + continue; + } } if (!QuicUtils::IsOutgoingStreamId(connection_->version(), id, perspective())) { @@ -1406,6 +1376,9 @@ void QuicSession::OnNewStreamOutgoingBidirectionalFlowControlWindow( } QUIC_DVLOG(1) << ENDPOINT << "Informing outgoing bidirectional stream " << id << " of new stream flow control window " << new_window; + if (!ValidateStreamFlowControlLimit(new_window, kv.second.get())) { + return; + } if (!kv.second->ConfigSendWindowOffset(new_window)) { return; } @@ -1421,8 +1394,15 @@ void QuicSession::OnNewStreamIncomingBidirectionalFlowControlWindow( // Inform all existing incoming bidirectional streams about the new window. for (auto const& kv : stream_map_) { const QuicStreamId id = kv.first; - if (!QuicUtils::IsBidirectionalStreamId(id)) { - continue; + if (fix_gquic_stream_type_ && !version().HasIetfQuicFrames()) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_fix_gquic_stream_type, 3, 3); + if (kv.second->type() != BIDIRECTIONAL) { + continue; + } + } else { + if (!QuicUtils::IsBidirectionalStreamId(id, version())) { + continue; + } } if (QuicUtils::IsOutgoingStreamId(connection_->version(), id, perspective())) { @@ -1430,39 +1410,92 @@ void QuicSession::OnNewStreamIncomingBidirectionalFlowControlWindow( } QUIC_DVLOG(1) << ENDPOINT << "Informing incoming bidirectional stream " << id << " of new stream flow control window " << new_window; + if (!ValidateStreamFlowControlLimit(new_window, kv.second.get())) { + return; + } if (!kv.second->ConfigSendWindowOffset(new_window)) { return; } } } +bool QuicSession::ValidateStreamFlowControlLimit(QuicStreamOffset new_window, + const QuicStream* stream) { + if (was_zero_rtt_rejected_ && + new_window < stream->flow_controller()->bytes_sent()) { + QUIC_BUG_IF(perspective() == Perspective::IS_SERVER) + << "Server should never receive configs twice."; + connection_->CloseConnection( + QUIC_ZERO_RTT_UNRETRANSMITTABLE, + quiche::QuicheStrCat( + "Server rejected 0-RTT, aborting because new stream max data ", + new_window, " for stream ", stream->id(), + " is less than currently used: ", + stream->flow_controller()->bytes_sent()), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + + if (version().AllowsLowFlowControlLimits() && + new_window < stream->flow_controller()->send_window_offset()) { + QUIC_BUG_IF(perspective() == Perspective::IS_SERVER) + << "Server should never receive configs twice."; + connection_->CloseConnection( + was_zero_rtt_rejected_ ? QUIC_ZERO_RTT_REJECTION_LIMIT_REDUCED + : QUIC_ZERO_RTT_RESUMPTION_LIMIT_REDUCED, + quiche::QuicheStrCat( + was_zero_rtt_rejected_ ? "Server rejected 0-RTT, aborting because " + : "", + "new stream max data ", new_window, " decreases current limit: ", + stream->flow_controller()->send_window_offset()), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + return true; +} + void QuicSession::OnNewSessionFlowControlWindow(QuicStreamOffset new_window) { QUIC_DVLOG(1) << ENDPOINT << "OnNewSessionFlowControlWindow " << new_window; - bool close_connection = false; - if (!connection_->version().AllowsLowFlowControlLimits()) { - if (new_window < kMinimumFlowControlSendWindow) { - close_connection = true; - QUIC_LOG_FIRST_N(ERROR, 1) - << "Peer sent us an invalid session flow control send window: " - << new_window << ", below default: " << kMinimumFlowControlSendWindow; - } - } else if (perspective_ == Perspective::IS_CLIENT && - new_window < flow_controller_.send_window_offset()) { - // The client receives a lower limit than remembered, violating - // https://tools.ietf.org/html/draft-ietf-quic-transport-27#section-7.3.1 - QUIC_LOG_FIRST_N(ERROR, 1) - << "Peer sent us an invalid session flow control send window: " - << new_window - << ", below current: " << flow_controller_.send_window_offset(); - close_connection = true; + + if (was_zero_rtt_rejected_ && new_window < flow_controller_.bytes_sent()) { + std::string error_details = quiche::QuicheStrCat( + "Server rejected 0-RTT. Aborting because the client received session " + "flow control send window: ", + new_window, + ", which is below currently used: ", flow_controller_.bytes_sent()); + QUIC_LOG(ERROR) << error_details; + connection_->CloseConnection( + QUIC_ZERO_RTT_UNRETRANSMITTABLE, error_details, + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; } - if (close_connection) { + if (!connection_->version().AllowsLowFlowControlLimits() && + new_window < kMinimumFlowControlSendWindow) { + std::string error_details = quiche::QuicheStrCat( + "Peer sent us an invalid session flow control send window: ", + new_window, ", below minimum: ", kMinimumFlowControlSendWindow); + QUIC_LOG_FIRST_N(ERROR, 1) << error_details; connection_->CloseConnection( - QUIC_FLOW_CONTROL_INVALID_WINDOW, - quiche::QuicheStrCat("New connection window too low: ", new_window), + QUIC_FLOW_CONTROL_INVALID_WINDOW, error_details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return; } + if (perspective_ == Perspective::IS_CLIENT && + new_window < flow_controller_.send_window_offset()) { + // The client receives a lower limit than remembered, violating + // https://tools.ietf.org/html/draft-ietf-quic-transport-27#section-7.3.1 + std::string error_details = quiche::QuicheStrCat( + was_zero_rtt_rejected_ ? "Server rejected 0-RTT, aborting because " + : "", + "new session max data ", new_window, + " decreases current limit: ", flow_controller_.send_window_offset()); + QUIC_LOG(ERROR) << error_details; + connection_->CloseConnection( + was_zero_rtt_rejected_ ? QUIC_ZERO_RTT_REJECTION_LIMIT_REDUCED + : QUIC_ZERO_RTT_RESUMPTION_LIMIT_REDUCED, + error_details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } flow_controller_.UpdateSendWindowOffset(new_window); } @@ -1497,31 +1530,17 @@ void QuicSession::OnNewEncryptionKeyAvailable( EncryptionLevel level, std::unique_ptr<QuicEncrypter> encrypter) { connection()->SetEncrypter(level, std::move(encrypter)); - - if (connection_->version().handshake_protocol == PROTOCOL_TLS1_3 && - (perspective() == Perspective::IS_CLIENT || - GetQuicReloadableFlag(quic_change_default_encryption_level))) { - QUIC_DVLOG(1) << ENDPOINT << "Set default encryption level to " - << EncryptionLevelToString(level); - QUIC_RELOADABLE_FLAG_COUNT(quic_change_default_encryption_level); - connection()->SetDefaultEncryptionLevel(level); + if (connection_->version().handshake_protocol != PROTOCOL_TLS1_3) { return; } - if (connection_->version().handshake_protocol == PROTOCOL_TLS1_3 && - level == ENCRYPTION_FORWARD_SECURE) { - // Set connection's default encryption level once 1-RTT write key is - // available. - QUIC_DVLOG(1) << ENDPOINT << "Set default encryption level to " - << EncryptionLevelToString(level); - connection()->SetDefaultEncryptionLevel(level); - } + QUIC_DVLOG(1) << ENDPOINT << "Set default encryption level to " << level; + connection()->SetDefaultEncryptionLevel(level); } void QuicSession::SetDefaultEncryptionLevel(EncryptionLevel level) { DCHECK_EQ(PROTOCOL_QUIC_CRYPTO, connection_->version().handshake_protocol); - QUIC_DVLOG(1) << ENDPOINT << "Set default encryption level to " - << EncryptionLevelToString(level); + QUIC_DVLOG(1) << ENDPOINT << "Set default encryption level to " << level; connection()->SetDefaultEncryptionLevel(level); switch (level) { @@ -1531,7 +1550,7 @@ void QuicSession::SetDefaultEncryptionLevel(EncryptionLevel level) { if (perspective() == Perspective::IS_CLIENT) { // Retransmit old 0-RTT data (if any) with the new 0-RTT keys, since // they can't be decrypted by the server. - connection_->RetransmitUnackedPackets(ALL_INITIAL_RETRANSMISSION); + connection_->RetransmitZeroRttPackets(); // Given any streams blocked by encryption a chance to write. OnCanWrite(); } @@ -1541,13 +1560,9 @@ void QuicSession::SetDefaultEncryptionLevel(EncryptionLevel level) { case ENCRYPTION_FORWARD_SECURE: QUIC_BUG_IF(!config_.negotiated()) << ENDPOINT << "Handshake confirmed without parameter negotiation."; - if (!GetQuicReloadableFlag(quic_bw_sampler_app_limited_starting_value)) { - connection_->ResetHasNonAppLimitedSampleAfterHandshakeCompletion(); - } break; default: - QUIC_BUG << "Unknown encryption level: " - << EncryptionLevelToString(level); + QUIC_BUG << "Unknown encryption level: " << level; } } @@ -1557,15 +1572,17 @@ void QuicSession::OnOneRttKeysAvailable() { << ENDPOINT << "Handshake completes without cipher suite negotiation."; QUIC_BUG_IF(!config_.negotiated()) << ENDPOINT << "Handshake completes without parameter negotiation."; - if (connection()->version().HasHandshakeDone() && + if ((connection()->version().HasHandshakeDone() || + (GetQuicReloadableFlag(quic_support_handshake_done_in_t050) && + config_.PeerSupportsHandshakeDone())) && perspective_ == Perspective::IS_SERVER) { + if (!connection()->version().HasHandshakeDone()) { + QUIC_RELOADABLE_FLAG_COUNT(quic_support_handshake_done_in_t050); + } // Server sends HANDSHAKE_DONE to signal confirmation of the handshake // to the client. control_frame_manager_.WriteOrBufferHandshakeDone(); } - if (!GetQuicReloadableFlag(quic_bw_sampler_app_limited_starting_value)) { - connection_->ResetHasNonAppLimitedSampleAfterHandshakeCompletion(); - } } void QuicSession::DiscardOldDecryptionKey(EncryptionLevel level) { @@ -1576,8 +1593,7 @@ void QuicSession::DiscardOldDecryptionKey(EncryptionLevel level) { } void QuicSession::DiscardOldEncryptionKey(EncryptionLevel level) { - QUIC_DVLOG(1) << ENDPOINT << "Discard keys of " - << EncryptionLevelToString(level); + QUIC_DVLOG(1) << ENDPOINT << "Discard keys of " << level; if (connection()->version().handshake_protocol == PROTOCOL_TLS1_3) { connection()->RemoveEncrypter(level); } @@ -1594,8 +1610,7 @@ void QuicSession::DiscardOldEncryptionKey(EncryptionLevel level) { QUIC_BUG << "Tries to drop 1-RTT keys"; break; default: - QUIC_BUG << "Unknown encryption level: " - << EncryptionLevelToString(level); + QUIC_BUG << "Unknown encryption level: " << level; } } @@ -1603,6 +1618,46 @@ void QuicSession::NeuterHandshakeData() { connection()->OnHandshakeComplete(); } +void QuicSession::OnZeroRttRejected() { + was_zero_rtt_rejected_ = true; + connection_->RetransmitZeroRttPackets(); + if (connection_->encryption_level() == ENCRYPTION_FORWARD_SECURE) { + QUIC_BUG << "1-RTT keys already available when 0-RTT is rejected."; + connection_->CloseConnection( + QUIC_INTERNAL_ERROR, + "1-RTT keys already available when 0-RTT is rejected.", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + } +} + +bool QuicSession::FillTransportParameters(TransportParameters* params) { + if (version().AuthenticatesHandshakeConnectionIds()) { + if (perspective() == Perspective::IS_SERVER) { + config_.SetOriginalConnectionIdToSend( + connection_->GetOriginalDestinationConnectionId()); + config_.SetInitialSourceConnectionIdToSend(connection_->connection_id()); + } else { + config_.SetInitialSourceConnectionIdToSend( + connection_->client_connection_id()); + } + } + return config_.FillTransportParameters(params); +} + +QuicErrorCode QuicSession::ProcessTransportParameters( + const TransportParameters& params, + bool is_resumption, + std::string* error_details) { + HelloType hello_type; + if (perspective_ == Perspective::IS_CLIENT) { + hello_type = SERVER; + } else { + hello_type = CLIENT; + } + return config_.ProcessTransportParameters(params, hello_type, is_resumption, + error_details); +} + void QuicSession::OnCryptoHandshakeMessageSent( const CryptoHandshakeMessage& /*message*/) {} @@ -1648,14 +1703,11 @@ void QuicSession::ActivateStream(std::unique_ptr<QuicStream> stream) { << ". activating stream " << stream_id; DCHECK(!QuicContainsKey(stream_map_, stream_id)); stream_map_[stream_id] = std::move(stream); - if (IsIncomingStream(stream_id)) { - is_static ? ++num_incoming_static_streams_ - : ++num_dynamic_incoming_streams_; - } else if (is_static) { - ++num_outgoing_static_streams_; + if (is_static) { + ++num_static_streams_; + return; } - if (stream_id_manager_.handles_accounting() && !is_static && - !VersionHasIetfQuicFrames(transport_version())) { + if (!VersionHasIetfQuicFrames(transport_version())) { // Do not inform stream ID manager of static streams. stream_id_manager_.ActivateStream( /*is_incoming=*/IsIncomingStream(stream_id)); @@ -1678,8 +1730,7 @@ QuicStreamId QuicSession::GetNextOutgoingUnidirectionalStreamId() { bool QuicSession::CanOpenNextOutgoingBidirectionalStream() { if (!VersionHasIetfQuicFrames(transport_version())) { - return stream_id_manager_.CanOpenNextOutgoingStream( - GetNumOpenOutgoingStreams()); + return stream_id_manager_.CanOpenNextOutgoingStream(); } if (v99_streamid_manager_.CanOpenNextOutgoingBidirectionalStream()) { return true; @@ -1695,8 +1746,7 @@ bool QuicSession::CanOpenNextOutgoingBidirectionalStream() { bool QuicSession::CanOpenNextOutgoingUnidirectionalStream() { if (!VersionHasIetfQuicFrames(transport_version())) { - return stream_id_manager_.CanOpenNextOutgoingStream( - GetNumOpenOutgoingStreams()); + return stream_id_manager_.CanOpenNextOutgoingStream(); } if (v99_streamid_manager_.CanOpenNextOutgoingUnidirectionalStream()) { return true; @@ -1745,19 +1795,11 @@ QuicStream* QuicSession::GetOrCreateStream(const QuicStreamId stream_id) { return nullptr; } - if (!VersionHasIetfQuicFrames(transport_version())) { - // TODO(fayang): Let LegacyQuicStreamIdManager count open streams and make - // CanOpenIncomingStream interface consistent with that of v99. - if (!stream_id_manager_.CanOpenIncomingStream( - GetNumOpenIncomingStreams())) { - // Refuse to open the stream. - if (break_close_loop_) { - ResetStream(stream_id, QUIC_REFUSED_STREAM, 0); - } else { - SendRstStream(stream_id, QUIC_REFUSED_STREAM, 0); - } - return nullptr; - } + if (!VersionHasIetfQuicFrames(transport_version()) && + !stream_id_manager_.CanOpenIncomingStream()) { + // Refuse to open the stream. + ResetStream(stream_id, QUIC_REFUSED_STREAM, 0); + return nullptr; } return CreateIncomingStream(stream_id); @@ -1765,48 +1807,17 @@ QuicStream* QuicSession::GetOrCreateStream(const QuicStreamId stream_id) { void QuicSession::StreamDraining(QuicStreamId stream_id, bool unidirectional) { DCHECK(QuicContainsKey(stream_map_, stream_id)); - if (deprecate_draining_streams_) { - QUIC_RELOADABLE_FLAG_COUNT(quic_deprecate_draining_streams); - QUIC_DVLOG(1) << ENDPOINT << "Stream " << stream_id << " is draining"; - if (VersionHasIetfQuicFrames(transport_version())) { - v99_streamid_manager_.OnStreamClosed(stream_id); - } else if (stream_id_manager_.handles_accounting()) { - stream_id_manager_.OnStreamClosed( - /*is_incoming=*/IsIncomingStream(stream_id)); - } - if (IsIncomingStream(stream_id)) { - ++num_draining_incoming_streams_; - return; - } - ++num_draining_outgoing_streams_; - OnCanCreateNewOutgoingStream(unidirectional); - return; - } - if (!QuicContainsKey(draining_streams_, stream_id)) { - draining_streams_.insert(stream_id); - if (IsIncomingStream(stream_id)) { - ++num_draining_incoming_streams_; - } - if (VersionHasIetfQuicFrames(transport_version())) { - v99_streamid_manager_.OnStreamClosed(stream_id); - } else if (stream_id_manager_.handles_accounting()) { - stream_id_manager_.OnStreamClosed( - /*is_incoming=*/IsIncomingStream(stream_id)); - } + QUIC_DVLOG(1) << ENDPOINT << "Stream " << stream_id << " is draining"; + if (VersionHasIetfQuicFrames(transport_version())) { + v99_streamid_manager_.OnStreamClosed(stream_id); + } else { + stream_id_manager_.OnStreamClosed( + /*is_incoming=*/IsIncomingStream(stream_id)); } + ++num_draining_streams_; if (!IsIncomingStream(stream_id)) { - // Inform application that a stream is available. - if (VersionHasIetfQuicFrames(transport_version())) { - OnCanCreateNewOutgoingStream( - !QuicUtils::IsBidirectionalStreamId(stream_id)); - } else { - QuicStream* stream = GetStream(stream_id); - if (!stream) { - QUIC_BUG << "Stream doesn't exist when draining."; - return; - } - OnCanCreateNewOutgoingStream(stream->type() != BIDIRECTIONAL); - } + ++num_outgoing_draining_streams_; + OnCanCreateNewOutgoingStream(unidirectional); } } @@ -1924,46 +1935,15 @@ bool QuicSession::IsStaticStream(QuicStreamId id) const { return it->second->is_static(); } -size_t QuicSession::GetNumOpenIncomingStreams() const { - DCHECK(!VersionHasIetfQuicFrames(transport_version())); - if (stream_id_manager_.handles_accounting()) { - return stream_id_manager_.num_open_incoming_streams(); - } - return num_dynamic_incoming_streams_ - num_draining_incoming_streams_ + - num_locally_closed_incoming_streams_highest_offset_; -} - -size_t QuicSession::GetNumOpenOutgoingStreams() const { - DCHECK(!VersionHasIetfQuicFrames(transport_version())); - if (stream_id_manager_.handles_accounting()) { - return stream_id_manager_.num_open_outgoing_streams(); - } - DCHECK_GE(GetNumDynamicOutgoingStreams() + - GetNumLocallyClosedOutgoingStreamsHighestOffset(), - GetNumDrainingOutgoingStreams()); - return GetNumDynamicOutgoingStreams() + - GetNumLocallyClosedOutgoingStreamsHighestOffset() - - GetNumDrainingOutgoingStreams(); -} - size_t QuicSession::GetNumActiveStreams() const { - if (!VersionHasIetfQuicFrames(transport_version()) && - stream_id_manager_.handles_accounting()) { + if (!VersionHasIetfQuicFrames(transport_version())) { // Exclude locally_closed_streams when determine whether to keep connection // alive. return stream_id_manager_.num_open_incoming_streams() + stream_id_manager_.num_open_outgoing_streams() - locally_closed_streams_highest_offset_.size(); } - return stream_map_.size() - GetNumDrainingStreams() - - num_incoming_static_streams_ - num_outgoing_static_streams_; -} - -size_t QuicSession::GetNumDrainingStreams() const { - if (deprecate_draining_streams_) { - return num_draining_incoming_streams_ + num_draining_outgoing_streams_; - } - return draining_streams_.size(); + return stream_map_.size() - num_draining_streams_ - num_static_streams_; } void QuicSession::MarkConnectionLevelWriteBlocked(QuicStreamId id) { @@ -1994,31 +1974,6 @@ void QuicSession::SendPing() { control_frame_manager_.WritePing(); } -size_t QuicSession::GetNumDynamicOutgoingStreams() const { - DCHECK_GE( - static_cast<size_t>(stream_map_.size() + pending_stream_map_.size()), - num_dynamic_incoming_streams_ + num_outgoing_static_streams_ + - num_incoming_static_streams_); - return stream_map_.size() + pending_stream_map_.size() - - num_dynamic_incoming_streams_ - num_outgoing_static_streams_ - - num_incoming_static_streams_; -} - -size_t QuicSession::GetNumDrainingOutgoingStreams() const { - if (deprecate_draining_streams_) { - return num_draining_outgoing_streams_; - } - DCHECK_GE(draining_streams_.size(), num_draining_incoming_streams_); - return draining_streams_.size() - num_draining_incoming_streams_; -} - -size_t QuicSession::GetNumLocallyClosedOutgoingStreamsHighestOffset() const { - DCHECK_GE(locally_closed_streams_highest_offset_.size(), - num_locally_closed_incoming_streams_highest_offset_); - return locally_closed_streams_highest_offset_.size() - - num_locally_closed_incoming_streams_highest_offset_; -} - bool QuicSession::IsConnectionFlowControlBlocked() const { return flow_controller_.IsBlocked(); } @@ -2273,6 +2228,18 @@ QuicUint128 QuicSession::GetStatelessResetToken() const { return QuicUtils::GenerateStatelessResetToken(connection_->connection_id()); } +bool QuicSession::CanWriteStreamData() const { + // Don't write stream data if there are queued data packets. + if (connection_->HasQueuedPackets()) { + return false; + } + // Immediately write handshake data. + if (HasPendingHandshake()) { + return true; + } + return connection_->CanWrite(HAS_RETRANSMITTABLE_DATA); +} + bool QuicSession::RetransmitLostData() { QuicConnection::ScopedPacketFlusher retransmission_flusher(connection_); // Retransmit crypto data first. @@ -2305,7 +2272,7 @@ bool QuicSession::RetransmitLostData() { } } while (!streams_with_pending_retransmission_.empty()) { - if (!connection_->CanWriteStreamData()) { + if (!CanWriteStreamData()) { break; } // Retransmit lost data on headers and data streams. 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 5a1ebbc602c..9233402d4ea 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 @@ -33,6 +33,7 @@ #include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_optional.h" #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" namespace quic { @@ -110,17 +111,21 @@ class QUIC_EXPORT_PRIVATE QuicSession bool is_connectivity_probe) override; void OnCanWrite() override; bool SendProbingData() override; + bool ValidateStatelessReset( + const quic::QuicSocketAddress& /*self_address*/, + const quic::QuicSocketAddress& /*peer_address*/) override { + return true; + } void OnCongestionWindowChange(QuicTime /*now*/) override {} void OnConnectionMigration(AddressChangeType /*type*/) override {} // Adds a connection level WINDOW_UPDATE frame. void OnAckNeedsRetransmittableFrame() override; void SendPing() override; bool WillingAndAbleToWrite() const override; - bool HasPendingHandshake() const override; void OnPathDegrading() override; + void OnForwardProgressMadeAfterPathDegrading() override; bool AllowSelfAddressChange() const override; HandshakeState GetHandshakeState() const override; - void OnForwardProgressConfirmed() override; bool OnMaxStreamsFrame(const QuicMaxStreamsFrame& frame) override; bool OnStreamsBlockedFrame(const QuicStreamsBlockedFrame& frame) override; void OnStopSendingFrame(const QuicStopSendingFrame& frame) override; @@ -255,6 +260,11 @@ class QUIC_EXPORT_PRIVATE QuicSession void DiscardOldEncryptionKey(EncryptionLevel level) override; void NeuterUnencryptedData() override; void NeuterHandshakeData() override; + void OnZeroRttRejected() override; + bool FillTransportParameters(TransportParameters* params) override; + QuicErrorCode ProcessTransportParameters(const TransportParameters& params, + bool is_resumption, + std::string* error_details) override; // Implement StreamDelegateInterface. void OnStreamError(QuicErrorCode error_code, @@ -322,33 +332,6 @@ class QUIC_EXPORT_PRIVATE QuicSession // never counting unfinished streams. size_t GetNumActiveStreams() const; - // Returns the number of currently draining streams. - size_t GetNumDrainingStreams() const; - - // Returns the number of currently open peer initiated streams, excluding - // static streams. - // TODO(fayang): remove this and instead use - // LegacyStreamIdManager::num_open_incoming_streams() in tests when - // deprecating quic_stream_id_manager_handles_accounting. - size_t GetNumOpenIncomingStreams() const; - - // Returns the number of currently open self initiated streams, excluding - // static streams. - // TODO(fayang): remove this and instead use - // LegacyStreamIdManager::num_open_outgoing_streams() in tests when - // deprecating quic_stream_id_manager_handles_accounting. - size_t GetNumOpenOutgoingStreams() const; - - // Returns the number of open peer initiated static streams. - size_t num_incoming_static_streams() const { - return num_incoming_static_streams_; - } - - // Returns the number of open self initiated static streams. - size_t num_outgoing_static_streams() const { - return num_outgoing_static_streams_; - } - // Add the stream to the session's write-blocked list because it is blocked by // connection-level flow control but not by its own stream-level flow control. // The stream will be given a chance to write when a connection-level @@ -363,6 +346,10 @@ class QUIC_EXPORT_PRIVATE QuicSession // Called when stream |id| is newly waiting for acks. void OnStreamWaitingForAcks(QuicStreamId id); + // Returns true if there is pending handshake data in the crypto stream. + // TODO(ianswett): Make this private or remove. + bool HasPendingHandshake() const; + // Returns true if the session has data to be sent, either queued in the // connection, or in a write-blocked stream. bool HasDataToWrite() const; @@ -435,12 +422,6 @@ class QUIC_EXPORT_PRIVATE QuicSession // Return true if given stream is peer initiated. bool IsIncomingStream(QuicStreamId id) const; - size_t GetNumLocallyClosedOutgoingStreamsHighestOffset() const; - - size_t num_locally_closed_incoming_streams_highest_offset() const { - return num_locally_closed_incoming_streams_highest_offset_; - } - // Record errors when a connection is closed at the server side, should only // be called from server's perspective. // Noop if |error| is QUIC_NO_ERROR. @@ -478,27 +459,30 @@ class QUIC_EXPORT_PRIVATE QuicSession // uses TLS handshake. virtual void OnAlpnSelected(quiche::QuicheStringPiece alpn); - bool deprecate_draining_streams() const { - return deprecate_draining_streams_; - } - - bool break_close_loop() const { return break_close_loop_; } - // Called on clients by the crypto handshaker to provide application state // necessary for sending application data in 0-RTT. The state provided here is // the same state that was provided to the crypto handshaker in - // QuicCryptoClientStream::OnApplicationState on a previous connection. - // Application protocols that require state to be carried over from the - // previous connection to support 0-RTT data must implement this method to - // ingest this state. For example, an HTTP/3 QuicSession would implement this - // function to process the remembered server SETTINGS frame and apply those - // SETTINGS to 0-RTT data. This function returns true if the application state - // has been successfully processed, and false if there was an error processing - // the cached state and the connection should be closed. - virtual bool SetApplicationState(ApplicationState* /*cached_state*/) { + // QuicCryptoStream::SetServerApplicationStateForResumption on a previous + // connection. Application protocols that require state to be carried over + // from the previous connection to support 0-RTT data must implement this + // method to ingest this state. For example, an HTTP/3 QuicSession would + // implement this function to process the remembered server SETTINGS and apply + // those SETTINGS to 0-RTT data. This function returns true if the application + // state has been successfully processed, and false if there was an error + // processing the cached state and the connection should be closed. + virtual bool ResumeApplicationState(ApplicationState* /*cached_state*/) { return true; } + const quiche::QuicheOptional<std::string> user_agent_id() const { + return user_agent_id_; + } + + void SetUserAgentId(std::string user_agent_id) { + user_agent_id_ = std::move(user_agent_id); + connection()->OnUserAgentIdKnown(); + } + protected: using StreamMap = QuicSmallMap<QuicStreamId, std::unique_ptr<QuicStream>, 10>; @@ -541,18 +525,10 @@ class QUIC_EXPORT_PRIVATE QuicSession bool CanOpenNextOutgoingBidirectionalStream(); bool CanOpenNextOutgoingUnidirectionalStream(); - // Returns the number of open dynamic streams. - uint64_t GetNumOpenDynamicStreams() const; - // Returns the maximum bidirectional streams parameter sent with the handshake // as a transport parameter, or in the most recent MAX_STREAMS frame. QuicStreamCount GetAdvertisedMaxIncomingBidirectionalStreams() const; - // Performs the work required to close |stream_id|. If |rst_sent| then a - // Reset Stream frame has already been sent for this stream. - // TODO(fayang): Remove CloseStreamInner. - virtual void CloseStreamInner(QuicStreamId stream_id, bool rst_sent); - // When a stream is closed locally, it may not yet know how many bytes the // peer sent on that stream. // When this data arrives (via stream frame w. FIN, trailing headers, or RST) @@ -589,10 +565,6 @@ class QUIC_EXPORT_PRIVATE QuicSession return &write_blocked_streams_; } - size_t GetNumDynamicOutgoingStreams() const; - - size_t GetNumDrainingOutgoingStreams() const; - // Returns true if the stream is still active. bool IsOpenStream(QuicStreamId id); @@ -630,6 +602,14 @@ class QUIC_EXPORT_PRIVATE QuicSession QuicDatagramQueue* datagram_queue() { return &datagram_queue_; } + size_t num_static_streams() const { return num_static_streams_; } + + bool was_zero_rtt_rejected() const { return was_zero_rtt_rejected_; } + + size_t num_outgoing_draining_streams() const { + return num_outgoing_draining_streams_; + } + // Processes the stream type information of |pending| depending on // different kinds of sessions' own rules. Returns true if the pending stream // is converted into a normal stream. @@ -704,6 +684,9 @@ class QUIC_EXPORT_PRIVATE QuicSession // if all lost data is retransmitted. Returns false otherwise. bool RetransmitLostData(); + // Returns true if stream data should be written. + bool CanWriteStreamData() const; + // Closes the pending stream |stream_id| before it has been created. void ClosePendingStream(QuicStreamId stream_id); @@ -720,6 +703,12 @@ class QUIC_EXPORT_PRIVATE QuicSession QuicRstStreamErrorCode error, QuicStreamOffset bytes_written); + // Closes the connection and returns false if |new_window| is lower than + // |stream|'s current flow control window. + // Returns true otherwise. + bool ValidateStreamFlowControlLimit(QuicStreamOffset new_window, + const QuicStream* stream); + // Sends a STOP_SENDING frame if the stream type allows. void MaybeSendStopSendingFrame(QuicStreamId id, QuicRstStreamErrorCode error); @@ -756,13 +745,6 @@ class QUIC_EXPORT_PRIVATE QuicSession // which are waiting for the first byte of payload to arrive. PendingStreamMap pending_stream_map_; - // Set of stream ids that are "draining" -- a FIN has been sent and received, - // but the stream object still exists because not all the received data has - // been consumed. - // TODO(fayang): Remove draining_streams_ when deprecate - // quic_deprecate_draining_streams. - QuicHashSet<QuicStreamId> draining_streams_; - // Set of stream ids that are waiting for acks excluding crypto stream id. QuicHashSet<QuicStreamId> streams_waiting_for_acks_; @@ -774,37 +756,16 @@ class QUIC_EXPORT_PRIVATE QuicSession // Manages stream IDs for version99/IETF QUIC UberQuicStreamIdManager v99_streamid_manager_; - // A counter for peer initiated dynamic streams which are in the stream_map_. - // TODO(fayang): Remove this when deprecating - // quic_stream_id_manager_handles_accounting. - size_t num_dynamic_incoming_streams_; + // A counter for streams which have sent and received FIN but waiting for + // application to consume data. + size_t num_draining_streams_; - // A counter for peer initiated streams which have sent and received FIN but + // A counter for self initiated streams which have sent and received FIN but // waiting for application to consume data. - // TODO(fayang): Remove this when deprecating - // quic_stream_id_manager_handles_accounting. - size_t num_draining_incoming_streams_; + size_t num_outgoing_draining_streams_; - // A counter for self initiated streams which have sent and received FIN but - // waiting for application to consume data. Only used when - // deprecate_draining_streams_ is true. - // TODO(fayang): Remove this when deprecating - // quic_stream_id_manager_handles_accounting. - size_t num_draining_outgoing_streams_; - - // A counter for self initiated static streams which are in - // stream_map_. - size_t num_outgoing_static_streams_; - - // A counter for peer initiated static streams which are in - // stream_map_. - size_t num_incoming_static_streams_; - - // A counter for peer initiated streams which are in the - // locally_closed_streams_highest_offset_. - // TODO(fayang): Remove this when deprecating - // quic_stream_id_manager_handles_accounting. - size_t num_locally_closed_incoming_streams_highest_offset_; + // A counter for static streams which are in stream_map_. + size_t num_static_streams_; // Received information for a connection close. QuicConnectionCloseFrame on_closed_frame_; @@ -842,6 +803,8 @@ class QUIC_EXPORT_PRIVATE QuicSession // list may be a superset of the connection framer's supported versions. ParsedQuicVersionVector supported_versions_; + quiche::QuicheOptional<std::string> user_agent_id_; + // If true, write_blocked_streams_ uses HTTP2 (tree-style) priority write // scheduler. bool use_http2_priority_write_scheduler_; @@ -853,11 +816,11 @@ class QUIC_EXPORT_PRIVATE QuicSession // If true, enables round robin scheduling. bool enable_round_robin_scheduling_; - // Latched value of quic_deprecate_draining_streams. - const bool deprecate_draining_streams_; + // Whether the session has received a 0-RTT rejection (QUIC+TLS only). + bool was_zero_rtt_rejected_; - // Latched value of quic_break_session_stream_close_loop. - const bool break_close_loop_; + // Latched value of flag quic_fix_gquic_stream_type. + const bool fix_gquic_stream_type_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc index 9d5e977d0a6..12a48300bdb 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc @@ -10,6 +10,7 @@ #include <utility> #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" +#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h" #include "net/third_party/quiche/src/quic/core/frames/quic_max_streams_frame.h" @@ -77,6 +78,17 @@ class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker { kInitialStreamFlowControlWindowForTest); session()->config()->SetInitialSessionFlowControlWindowToSend( kInitialSessionFlowControlWindowForTest); + if (session()->version().AuthenticatesHandshakeConnectionIds()) { + if (session()->perspective() == Perspective::IS_CLIENT) { + session()->config()->SetOriginalConnectionIdToSend( + session()->connection()->connection_id()); + session()->config()->SetInitialSourceConnectionIdToSend( + session()->connection()->connection_id()); + } else { + session()->config()->SetInitialSourceConnectionIdToSend( + session()->connection()->client_connection_id()); + } + } if (session()->connection()->version().handshake_protocol == PROTOCOL_TLS1_3) { TransportParameters transport_parameters; @@ -123,6 +135,8 @@ class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker { HandshakeState GetHandshakeState() const override { return one_rtt_keys_available() ? HANDSHAKE_COMPLETE : HANDSHAKE_START; } + void SetServerApplicationStateForResumption( + std::unique_ptr<ApplicationState> /*application_state*/) override {} MOCK_METHOD(void, OnCanWrite, (), (override)); bool HasPendingCryptoRetransmission() const override { return false; } @@ -183,6 +197,9 @@ class TestSession : public QuicSession { this->connection()->SetEncrypter( ENCRYPTION_FORWARD_SECURE, std::make_unique<NullEncrypter>(connection->perspective())); + if (this->connection()->version().SupportsAntiAmplificationLimit()) { + QuicConnectionPeer::SetAddressValidated(this->connection()); + } } ~TestSession() override { DeleteConnection(); } @@ -216,7 +233,7 @@ class TestSession : public QuicSession { TestStream* CreateIncomingStream(QuicStreamId id) override { // Enforce the limit on the number of open streams. if (!VersionHasIetfQuicFrames(connection()->transport_version()) && - GetNumOpenIncomingStreams() + 1 > + stream_id_manager().num_open_incoming_streams() + 1 > max_open_incoming_bidirectional_streams()) { // No need to do this test for version 99; it's done by // QuicSession::GetOrCreateStream. @@ -226,11 +243,10 @@ class TestSession : public QuicSession { return nullptr; } - TestStream* stream = - new TestStream(id, this, - DetermineStreamType( - id, connection()->transport_version(), perspective(), - /*is_incoming=*/true, BIDIRECTIONAL)); + TestStream* stream = new TestStream( + id, this, + DetermineStreamType(id, connection()->version(), perspective(), + /*is_incoming=*/true, BIDIRECTIONAL)); ActivateStream(QuicWrapUnique(stream)); ++num_incoming_streams_created_; return stream; @@ -239,8 +255,7 @@ class TestSession : public QuicSession { TestStream* CreateIncomingStream(PendingStream* pending) override { QuicStreamId id = pending->id(); TestStream* stream = new TestStream( - pending, DetermineStreamType(id, connection()->transport_version(), - perspective(), + pending, DetermineStreamType(id, connection()->version(), perspective(), /*is_incoming=*/true, BIDIRECTIONAL)); ActivateStream(QuicWrapUnique(stream)); ++num_incoming_streams_created_; @@ -309,7 +324,7 @@ class TestSession : public QuicSession { MakeIOVector("not empty", &iov); QuicStreamPeer::SendBuffer(stream).SaveStreamData(&iov, 1, 0, 9); QuicConsumedData consumed = - WritevData(stream->id(), 9, 0, FIN, NOT_RETRANSMISSION, QuicheNullOpt); + WritevData(stream->id(), 9, 0, FIN, NOT_RETRANSMISSION, QUICHE_NULLOPT); QuicStreamPeer::SendBuffer(stream).OnStreamDataConsumed( consumed.bytes_consumed); return consumed; @@ -326,7 +341,7 @@ class TestSession : public QuicSession { QuicConsumedData SendLargeFakeData(QuicStream* stream, int bytes) { DCHECK(writev_consumes_all_data_); return WritevData(stream->id(), bytes, 0, FIN, NOT_RETRANSMISSION, - QuicheNullOpt); + QUICHE_NULLOPT); } bool UsesPendingStreams() const override { return uses_pending_streams_; } @@ -421,15 +436,15 @@ class QuicSessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { void CloseStream(QuicStreamId id) { if (VersionHasIetfQuicFrames(transport_version())) { - if (QuicUtils::GetStreamType(id, session_.perspective(), - session_.IsIncomingStream(id)) == - READ_UNIDIRECTIONAL) { + if (QuicUtils::GetStreamType( + id, session_.perspective(), session_.IsIncomingStream(id), + connection_->version()) == READ_UNIDIRECTIONAL) { // Verify reset is not sent for READ_UNIDIRECTIONAL streams. EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0); EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(0); - } else if (QuicUtils::GetStreamType(id, session_.perspective(), - session_.IsIncomingStream(id)) == - WRITE_UNIDIRECTIONAL) { + } else if (QuicUtils::GetStreamType( + id, session_.perspective(), session_.IsIncomingStream(id), + connection_->version()) == WRITE_UNIDIRECTIONAL) { // Verify RESET_STREAM but not STOP_SENDING is sent for write-only // stream. EXPECT_CALL(*connection_, SendControlFrame(_)) @@ -554,6 +569,11 @@ class QuicSessionTestServer : public QuicSessionTestBase { kQuicDefaultConnectionIdLength) { client_framer_.set_visitor(&framer_visitor_); client_framer_.SetInitialObfuscators(TestConnectionId()); + if (client_framer_.version().KnowsWhichDecrypterToUse()) { + client_framer_.InstallDecrypter( + ENCRYPTION_FORWARD_SECURE, + std::make_unique<NullDecrypter>(Perspective::IS_CLIENT)); + } } QuicPathFrameBuffer path_frame_buffer1_; @@ -595,6 +615,7 @@ TEST_P(QuicSessionTestServer, OneRttKeysAvailable) { if (connection_->version().HasHandshakeDone()) { EXPECT_CALL(*connection_, SendControlFrame(_)); } + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_.GetMutableCryptoStream()->OnHandshakeMessage(message); EXPECT_TRUE(session_.OneRttKeysAvailable()); } @@ -990,6 +1011,7 @@ TEST_P(QuicSessionTestServer, Http2Priority) { QuicTagVector copt; copt.push_back(kH2PR); QuicConfigPeer::SetReceivedConnectionOptions(session_.config(), copt); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_.OnConfigNegotiated(); ASSERT_TRUE(session_.use_http2_priority_write_scheduler()); @@ -1072,6 +1094,7 @@ TEST_P(QuicSessionTestServer, RoundRobinScheduling) { QuicTagVector copt; copt.push_back(kRRWS); QuicConfigPeer::SetReceivedConnectionOptions(session_.config(), copt); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_.OnConfigNegotiated(); session_.set_writev_consumes_all_data(true); @@ -1117,6 +1140,7 @@ TEST_P(QuicSessionTestServer, OnCanWriteBundlesStreams) { CryptoHandshakeMessage msg; MockPacketWriter* writer = static_cast<MockPacketWriter*>( QuicConnectionPeer::GetWriter(session_.connection())); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_.GetMutableCryptoStream()->OnHandshakeMessage(msg); // Drive congestion control manually. @@ -1428,6 +1452,7 @@ TEST_P(QuicSessionTestServer, InvalidGoAway) { // Test that server session will send a connectivity probe in response to a // connectivity probe on the same path. TEST_P(QuicSessionTestServer, ServerReplyToConnectivityProbe) { + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); QuicSocketAddress old_peer_address = QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort); EXPECT_EQ(old_peer_address, session_.peer_address()); @@ -1461,6 +1486,7 @@ TEST_P(QuicSessionTestServer, ServerReplyToConnectivityProbes) { if (!VersionHasIetfQuicFrames(transport_version())) { return; } + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); QuicSocketAddress old_peer_address = QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort); EXPECT_EQ(old_peer_address, session_.peer_address()); @@ -1495,6 +1521,7 @@ TEST_P(QuicSessionTestServer, IncreasedTimeoutAfterCryptoHandshake) { EXPECT_CALL(*connection_, SendControlFrame(_)); } CryptoHandshakeMessage msg; + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_.GetMutableCryptoStream()->OnHandshakeMessage(msg); EXPECT_EQ(kMaximumIdleTimeoutSecs + 3, QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); @@ -1775,6 +1802,7 @@ TEST_P(QuicSessionTestServer, InvalidStreamFlowControlWindowInHandshake) { } else { EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); } + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_.OnConfigNegotiated(); } @@ -1784,6 +1812,7 @@ TEST_P(QuicSessionTestServer, CustomFlowControlWindow) { copt.push_back(kIFW7); QuicConfigPeer::SetReceivedConnectionOptions(session_.config(), copt); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_.OnConfigNegotiated(); EXPECT_EQ(192 * 1024u, QuicFlowControllerPeer::ReceiveWindowSize( session_.flow_controller())); @@ -2047,8 +2076,13 @@ TEST_P(QuicSessionTestClient, InvalidSessionFlowControlWindowInHandshake) { const uint32_t kInvalidWindow = kMinimumFlowControlSendWindow - 1; QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(session_.config(), kInvalidWindow); - EXPECT_CALL(*connection_, - CloseConnection(QUIC_FLOW_CONTROL_INVALID_WINDOW, _, _)); + EXPECT_CALL( + *connection_, + CloseConnection(connection_->version().AllowsLowFlowControlLimits() + ? QUIC_ZERO_RTT_RESUMPTION_LIMIT_REDUCED + : QUIC_FLOW_CONTROL_INVALID_WINDOW, + _, _)); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_.OnConfigNegotiated(); } @@ -2059,7 +2093,9 @@ TEST_P(QuicSessionTestClient, InvalidBidiStreamLimitInHandshake) { } QuicConfigPeer::SetReceivedMaxBidirectionalStreams( session_.config(), kDefaultMaxStreamsPerConnection - 1); - EXPECT_CALL(*connection_, CloseConnection(QUIC_MAX_STREAMS_ERROR, _, _)); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_ZERO_RTT_RESUMPTION_LIMIT_REDUCED, _, _)); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_.OnConfigNegotiated(); } @@ -2070,7 +2106,9 @@ TEST_P(QuicSessionTestClient, InvalidUniStreamLimitInHandshake) { } QuicConfigPeer::SetReceivedMaxUnidirectionalStreams( session_.config(), kDefaultMaxStreamsPerConnection - 1); - EXPECT_CALL(*connection_, CloseConnection(QUIC_MAX_STREAMS_ERROR, _, _)); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_ZERO_RTT_RESUMPTION_LIMIT_REDUCED, _, _)); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_.OnConfigNegotiated(); } @@ -2088,6 +2126,8 @@ TEST_P(QuicSessionTestClient, InvalidStreamFlowControlWindowInHandshake) { .WillOnce( Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _)); + + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_.OnConfigNegotiated(); } @@ -2430,14 +2470,9 @@ TEST_P(QuicSessionTestServer, RetransmitLostDataCausesConnectionClose) { session_.OnFrameLost(QuicFrame(frame)); // Retransmit stream data causes connection close. Stream has not sent fin // yet, so an RST is sent. - if (session_.break_close_loop()) { - EXPECT_CALL(*stream, OnCanWrite()).WillOnce(Invoke([this, stream]() { - session_.CloseStream(stream->id()); - })); - } else { - EXPECT_CALL(*stream, OnCanWrite()) - .WillOnce(Invoke(stream, &QuicStream::OnClose)); - } + EXPECT_CALL(*stream, OnCanWrite()).WillOnce(Invoke([this, stream]() { + session_.CloseStream(stream->id()); + })); if (VersionHasIetfQuicFrames(transport_version())) { // Once for the RST_STREAM, once for the STOP_SENDING EXPECT_CALL(*connection_, SendControlFrame(_)) @@ -2466,6 +2501,7 @@ TEST_P(QuicSessionTestServer, SendMessage) { EXPECT_CALL(*connection_, SendControlFrame(_)); } CryptoHandshakeMessage handshake_message; + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); session_.GetMutableCryptoStream()->OnHandshakeMessage(handshake_message); EXPECT_TRUE(session_.OneRttKeysAvailable()); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream.cc index 40a67bba21c..6318ac1ecff 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream.cc @@ -46,7 +46,7 @@ QuicByteCount GetInitialStreamFlowControlWindowToSend(QuicSession* session, // Unidirectional streams (v99 only). if (VersionHasIetfQuicFrames(version.transport_version) && - !QuicUtils::IsBidirectionalStreamId(stream_id)) { + !QuicUtils::IsBidirectionalStreamId(stream_id, version)) { return session->config() ->GetInitialMaxStreamDataBytesUnidirectionalToSend(); } @@ -74,7 +74,7 @@ QuicByteCount GetReceivedFlowControlWindow(QuicSession* session, // Unidirectional streams (v99 only). if (VersionHasIetfQuicFrames(version.transport_version) && - !QuicUtils::IsBidirectionalStreamId(stream_id)) { + !QuicUtils::IsBidirectionalStreamId(stream_id, version)) { if (session->config() ->HasReceivedInitialMaxStreamDataBytesUnidirectional()) { return session->config() @@ -359,7 +359,8 @@ QuicStream::QuicStream(QuicStreamId id, type != CRYPTO ? QuicUtils::GetStreamType(id_, session->perspective(), - session->IsIncomingStream(id_)) + session->IsIncomingStream(id_), + session->version()) : type), perspective_(session->perspective()) { if (type_ == WRITE_UNIDIRECTIONAL) { @@ -432,15 +433,13 @@ void QuicStream::OnStreamFrame(const QuicStreamFrame& frame) { return; } - if (frame.fin) { - if (!session_->deprecate_draining_streams() || !fin_received_) { - fin_received_ = true; - if (fin_sent_) { - DCHECK(!was_draining_ || !session_->deprecate_draining_streams()); - session_->StreamDraining(id_, - /*unidirectional=*/type_ != BIDIRECTIONAL); - was_draining_ = true; - } + if (frame.fin && !fin_received_) { + fin_received_ = true; + if (fin_sent_) { + DCHECK(!was_draining_); + session_->StreamDraining(id_, + /*unidirectional=*/type_ != BIDIRECTIONAL); + was_draining_ = true; } } @@ -582,14 +581,12 @@ void QuicStream::Reset(QuicRstStreamErrorCode error) { stream_error_ = error; session()->SendRstStream(id(), error, stream_bytes_written()); rst_sent_ = true; - if (session_->break_close_loop()) { - if (read_side_closed_ && write_side_closed_ && !IsWaitingForAcks()) { - session()->OnStreamDoneWaitingForAcks(id_); - return; - } - CloseReadSide(); - CloseWriteSide(); + if (read_side_closed_ && write_side_closed_ && !IsWaitingForAcks()) { + session()->OnStreamDoneWaitingForAcks(id_); + return; } + CloseReadSide(); + CloseWriteSide(); } void QuicStream::OnUnrecoverableError(QuicErrorCode error, @@ -781,12 +778,8 @@ void QuicStream::CloseReadSide() { if (write_side_closed_) { QUIC_DVLOG(1) << ENDPOINT << "Closing stream " << id(); - if (session_->break_close_loop()) { - session_->OnStreamClosed(id()); - OnClose(); - } else { - session_->CloseStream(id()); - } + session_->OnStreamClosed(id()); + OnClose(); } } @@ -799,12 +792,8 @@ void QuicStream::CloseWriteSide() { write_side_closed_ = true; if (read_side_closed_) { QUIC_DVLOG(1) << ENDPOINT << "Closing stream " << id(); - if (session_->break_close_loop()) { - session_->OnStreamClosed(id()); - OnClose(); - } else { - session_->CloseStream(id()); - } + session_->OnStreamClosed(id()); + OnClose(); } } @@ -827,12 +816,7 @@ void QuicStream::StopReading() { } void QuicStream::OnClose() { - if (session()->break_close_loop()) { - DCHECK(read_side_closed_ && write_side_closed_); - } else { - CloseReadSide(); - CloseWriteSide(); - } + DCHECK(read_side_closed_ && write_side_closed_); if (!fin_sent_ && !rst_sent_) { // For flow control accounting, tell the peer how many bytes have been @@ -946,16 +930,11 @@ bool QuicStream::ConfigSendWindowOffset(QuicStreamOffset new_offset) { << "ConfigSendWindowOffset called on stream without flow control"; return false; } - if (perspective_ == Perspective::IS_CLIENT && - session()->version().AllowsLowFlowControlLimits() && - new_offset < flow_controller_->send_window_offset()) { - OnUnrecoverableError( - QUIC_FLOW_CONTROL_INVALID_WINDOW, - quiche::QuicheStrCat("New stream max data ", new_offset, - " decreases current limit: ", - flow_controller_->send_window_offset())); - return false; - } + + QUIC_BUG_IF(session()->version().AllowsLowFlowControlLimits() && + new_offset < flow_controller_->send_window_offset()) + << ENDPOINT << "The new offset " << new_offset + << " decreases current offset " << flow_controller_->send_window_offset(); if (flow_controller_->UpdateSendWindowOffset(new_offset)) { // Let session unblock this stream. session_->MarkConnectionLevelWriteBlocked(id_); @@ -1048,7 +1027,7 @@ bool QuicStream::RetransmitStreamData(QuicStreamOffset offset, stream_bytes_written()); consumed = stream_delegate_->WritevData( id_, retransmission_length, retransmission_offset, - can_bundle_fin ? FIN : NO_FIN, type, QuicheNullOpt); + can_bundle_fin ? FIN : NO_FIN, type, QUICHE_NULLOPT); QUIC_DVLOG(1) << ENDPOINT << "stream " << id_ << " is forced to retransmit stream data [" << retransmission_offset << ", " @@ -1070,7 +1049,7 @@ bool QuicStream::RetransmitStreamData(QuicStreamOffset offset, QUIC_DVLOG(1) << ENDPOINT << "stream " << id_ << " retransmits fin only frame."; consumed = stream_delegate_->WritevData(id_, 0, stream_bytes_written(), FIN, - type, QuicheNullOpt); + type, QUICHE_NULLOPT); if (!consumed.fin_consumed) { return false; } @@ -1148,7 +1127,7 @@ void QuicStream::WriteBufferedData() { } QuicConsumedData consumed_data = stream_delegate_->WritevData(id(), write_length, stream_bytes_written(), - state, NOT_RETRANSMISSION, QuicheNullOpt); + state, NOT_RETRANSMISSION, QUICHE_NULLOPT); OnStreamDataConsumed(consumed_data.bytes_consumed); @@ -1228,7 +1207,7 @@ void QuicStream::WritePendingRetransmission() { << " retransmits fin only frame."; consumed = stream_delegate_->WritevData(id_, 0, stream_bytes_written(), FIN, - LOSS_RETRANSMISSION, QuicheNullOpt); + LOSS_RETRANSMISSION, QUICHE_NULLOPT); fin_lost_ = !consumed.fin_consumed; if (fin_lost_) { // Connection is write blocked. @@ -1243,7 +1222,7 @@ void QuicStream::WritePendingRetransmission() { (pending.offset + pending.length == stream_bytes_written()); consumed = stream_delegate_->WritevData( id_, pending.length, pending.offset, can_bundle_fin ? FIN : NO_FIN, - LOSS_RETRANSMISSION, QuicheNullOpt); + LOSS_RETRANSMISSION, QUICHE_NULLOPT); QUIC_DVLOG(1) << ENDPOINT << "stream " << id_ << " tries to retransmit stream data [" << pending.offset << ", " << pending.offset + pending.length @@ -1302,6 +1281,22 @@ void QuicStream::SendStopSending(uint16_t code) { session_->SendStopSending(code, id_); } +QuicFlowController* QuicStream::flow_controller() { + if (flow_controller_.has_value()) { + return &flow_controller_.value(); + } + QUIC_BUG << "Trying to access non-existent flow controller."; + return nullptr; +} + +const QuicFlowController* QuicStream::flow_controller() const { + if (flow_controller_.has_value()) { + return &flow_controller_.value(); + } + QUIC_BUG << "Trying to access non-existent flow controller."; + return nullptr; +} + // static spdy::SpdyStreamPrecedence QuicStream::CalculateDefaultPriority( const QuicSession* session) { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream.h b/chromium/net/third_party/quiche/src/quic/core/quic_stream.h index fe9d04c021f..7a847f09ec1 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream.h @@ -165,14 +165,6 @@ class QUIC_EXPORT_PRIVATE QuicStream // stream to write any pending data. virtual void OnCanWrite(); - // Called just before the object is destroyed. - // The object should not be accessed after OnClose is called. - // Sends a RST_STREAM with code QUIC_RST_ACKNOWLEDGEMENT if neither a FIN nor - // a RST_STREAM has been sent. - // TODO(fayang): move this to protected when deprecating - // quic_break_session_stream_close_loop. - virtual void OnClose(); - // Called by the session when the endpoint receives a RST_STREAM from the // peer. virtual void OnStreamReset(const QuicRstStreamFrame& frame); @@ -237,7 +229,9 @@ class QUIC_EXPORT_PRIVATE QuicStream int num_frames_received() const; int num_duplicate_frames_received() const; - QuicFlowController* flow_controller() { return &*flow_controller_; } + QuicFlowController* flow_controller(); + + const QuicFlowController* flow_controller() const; // Called when endpoint receives a frame which could increase the highest // offset. @@ -381,6 +375,12 @@ class QUIC_EXPORT_PRIVATE QuicStream const QuicReferenceCountedPointer<QuicAckListenerInterface>& /*ack_listener*/) {} + // Called just before the object is destroyed. + // The object should not be accessed after OnClose is called. + // Sends a RST_STREAM with code QUIC_RST_ACKNOWLEDGEMENT if neither a FIN nor + // a RST_STREAM has been sent. + virtual void OnClose(); + // True if buffered data in send buffer is below buffered_data_threshold_. bool CanWriteNewData() const; @@ -539,8 +539,7 @@ class QUIC_EXPORT_PRIVATE QuicStream // If initialized, reset this stream at this deadline. QuicTime deadline_; - // True if this stream has entered draining state. Only used when - // quic_deprecate_draining_streams is true. + // True if this stream has entered draining state. bool was_draining_; // Indicates whether this stream is bidirectional, read unidirectional or diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc index ba31decd2b9..528d9557ec4 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc @@ -55,6 +55,14 @@ bool QuicStreamIdManager::OnStreamsBlockedFrame( " exceeds incoming max stream ", incoming_advertised_max_streams_); return false; } + if (GetQuicReloadableFlag(quic_stop_sending_duplicate_max_streams)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_stop_sending_duplicate_max_streams); + DCHECK_LE(incoming_advertised_max_streams_, incoming_actual_max_streams_); + if (incoming_advertised_max_streams_ == incoming_actual_max_streams_) { + // We have told peer about current max. + return true; + } + } if (frame.stream_count < incoming_actual_max_streams_) { // Peer thinks it's blocked on a stream count that is less than our current // max. Inform the peer of the correct stream count. @@ -104,12 +112,17 @@ void QuicStreamIdManager::MaybeSendMaxStreamsFrame() { } void QuicStreamIdManager::SendMaxStreamsFrame() { + if (GetQuicReloadableFlag(quic_stop_sending_duplicate_max_streams)) { + QUIC_BUG_IF(incoming_advertised_max_streams_ >= + incoming_actual_max_streams_); + } incoming_advertised_max_streams_ = incoming_actual_max_streams_; delegate_->SendMaxStreams(incoming_advertised_max_streams_, unidirectional_); } void QuicStreamIdManager::OnStreamClosed(QuicStreamId stream_id) { - DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_); + DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id, version_), + unidirectional_); if (QuicUtils::IsOutgoingStreamId(version_, stream_id, perspective_)) { // Nothing to do for outgoing streams. return; @@ -147,7 +160,8 @@ bool QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId( const QuicStreamId stream_id, std::string* error_details) { // |stream_id| must be an incoming stream of the right directionality. - DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id), unidirectional_); + DCHECK_NE(QuicUtils::IsBidirectionalStreamId(stream_id, version_), + unidirectional_); DCHECK_NE(QuicUtils::IsServerInitiatedStreamId(version_.transport_version, stream_id), perspective_ == Perspective::IS_SERVER); @@ -193,7 +207,7 @@ bool QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId( } bool QuicStreamIdManager::IsAvailableStream(QuicStreamId id) const { - DCHECK_NE(QuicUtils::IsBidirectionalStreamId(id), unidirectional_); + DCHECK_NE(QuicUtils::IsBidirectionalStreamId(id, version_), unidirectional_); if (QuicUtils::IsOutgoingStreamId(version_, id, perspective_)) { // Stream IDs under next_ougoing_stream_id_ are either open or previously // open but now closed. diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager_test.cc index 2179ba2c860..689d4112d85 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager_test.cc @@ -139,13 +139,18 @@ TEST_P(QuicStreamIdManagerTest, CheckMaxStreamsBadValuesOverMaxFailsOutgoing) { } // Check the case of the stream count in a STREAMS_BLOCKED frame is less than -// the count most recently advertised in a MAX_STREAMS frame. This should cause -// a MAX_STREAMS frame with the most recently advertised count to be sent. +// the count most recently advertised in a MAX_STREAMS frame. TEST_P(QuicStreamIdManagerTest, ProcessStreamsBlockedOk) { QuicStreamCount stream_count = stream_id_manager_.incoming_initial_max_open_streams(); QuicStreamsBlockedFrame frame(0, stream_count - 1, IsUnidirectional()); - EXPECT_CALL(delegate_, SendMaxStreams(stream_count, IsUnidirectional())); + if (GetQuicReloadableFlag(quic_stop_sending_duplicate_max_streams)) { + // We have notified peer about current max. + EXPECT_CALL(delegate_, SendMaxStreams(stream_count, IsUnidirectional())) + .Times(0); + } else { + EXPECT_CALL(delegate_, SendMaxStreams(stream_count, IsUnidirectional())); + } std::string error_details; EXPECT_TRUE(stream_id_manager_.OnStreamsBlockedFrame(frame, &error_details)); } @@ -398,7 +403,12 @@ TEST_P(QuicStreamIdManagerTest, StreamsBlockedEdgeConditions) { // Check that receipt of a STREAMS BLOCKED with stream-count = 0 invokes a // MAX STREAMS, count = 123, when the MaxOpen... is set to 123. EXPECT_CALL(delegate_, SendMaxStreams(123u, IsUnidirectional())); - stream_id_manager_.SetMaxOpenIncomingStreams(123); + if (GetQuicReloadableFlag(quic_stop_sending_duplicate_max_streams)) { + QuicStreamIdManagerPeer::set_incoming_actual_max_streams( + &stream_id_manager_, 123); + } else { + stream_id_manager_.SetMaxOpenIncomingStreams(123); + } frame.stream_count = 0; EXPECT_TRUE(stream_id_manager_.OnStreamsBlockedFrame(frame, &error_details)); } 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 f30a3be7216..356a77d2f59 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 @@ -305,7 +305,7 @@ TEST_P(QuicStreamTest, BlockIfOnlySomeDataConsumed) { EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), 1u, 0u, NO_FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); stream_->WriteOrBufferData(quiche::QuicheStringPiece(kData1, 2), false, nullptr); @@ -324,7 +324,7 @@ TEST_P(QuicStreamTest, BlockIfFinNotConsumedWithData) { EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), 2u, 0u, NO_FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); stream_->WriteOrBufferData(quiche::QuicheStringPiece(kData1, 2), true, nullptr); @@ -372,7 +372,7 @@ TEST_P(QuicStreamTest, WriteOrBufferData) { EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), kDataLen - 1, 0u, NO_FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); stream_->WriteOrBufferData(kData1, false, nullptr); @@ -388,7 +388,8 @@ TEST_P(QuicStreamTest, WriteOrBufferData) { EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), kDataLen - 1, kDataLen - 1, - NO_FIN, NOT_RETRANSMISSION, QuicheNullOpt); + NO_FIN, NOT_RETRANSMISSION, + QUICHE_NULLOPT); })); EXPECT_CALL(*stream_, OnCanWriteNewData()); stream_->OnCanWrite(); @@ -398,7 +399,8 @@ TEST_P(QuicStreamTest, WriteOrBufferData) { EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), 2u, 2 * kDataLen - 2, - NO_FIN, NOT_RETRANSMISSION, QuicheNullOpt); + NO_FIN, NOT_RETRANSMISSION, + QUICHE_NULLOPT); })); EXPECT_CALL(*stream_, OnCanWriteNewData()); stream_->OnCanWrite(); @@ -445,7 +447,7 @@ TEST_P(QuicStreamTest, RstAlwaysSentIfNoFinSent) { EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), 1u, 0u, NO_FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); stream_->WriteOrBufferData(quiche::QuicheStringPiece(kData1, 1), false, nullptr); @@ -455,12 +457,8 @@ TEST_P(QuicStreamTest, RstAlwaysSentIfNoFinSent) { // Now close the stream, and expect that we send a RST. EXPECT_CALL(*session_, SendRstStream(_, _, _)); - if (session_->break_close_loop()) { - stream_->CloseReadSide(); - stream_->CloseWriteSide(); - } else { - stream_->OnClose(); - } + stream_->CloseReadSide(); + stream_->CloseWriteSide(); EXPECT_FALSE(session_->HasUnackedStreamData()); EXPECT_FALSE(fin_sent()); EXPECT_TRUE(rst_sent()); @@ -479,7 +477,7 @@ TEST_P(QuicStreamTest, RstNotSentIfFinSent) { EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), 1u, 0u, FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); stream_->WriteOrBufferData(quiche::QuicheStringPiece(kData1, 1), true, nullptr); @@ -487,12 +485,8 @@ TEST_P(QuicStreamTest, RstNotSentIfFinSent) { EXPECT_FALSE(rst_sent()); // Now close the stream, and expect that we do not send a RST. - if (session_->break_close_loop()) { - stream_->CloseReadSide(); - stream_->CloseWriteSide(); - } else { - stream_->OnClose(); - } + stream_->CloseReadSide(); + stream_->CloseWriteSide(); EXPECT_TRUE(fin_sent()); EXPECT_FALSE(rst_sent()); } @@ -516,12 +510,8 @@ TEST_P(QuicStreamTest, OnlySendOneRst) { // Now close the stream (any further resets being sent would break the // expectation above). - if (session_->break_close_loop()) { - stream_->CloseReadSide(); - stream_->CloseWriteSide(); - } else { - stream_->OnClose(); - } + stream_->CloseReadSide(); + stream_->CloseWriteSide(); EXPECT_FALSE(fin_sent()); EXPECT_TRUE(rst_sent()); } @@ -657,12 +647,8 @@ TEST_P(QuicStreamTest, InvalidFinalByteOffsetFromRst) { CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _)); stream_->OnStreamReset(rst_frame); EXPECT_TRUE(stream_->HasReceivedFinalOffset()); - if (session_->break_close_loop()) { - stream_->CloseReadSide(); - stream_->CloseWriteSide(); - } else { - stream_->OnClose(); - } + stream_->CloseReadSide(); + stream_->CloseWriteSide(); } TEST_P(QuicStreamTest, FinalByteOffsetFromZeroLengthStreamFrame) { @@ -760,13 +746,13 @@ TEST_P(QuicStreamTest, SetDrainingIncomingOutgoing) { EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), 2u, 0u, FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); stream_->WriteOrBufferData(quiche::QuicheStringPiece(kData1, 2), true, nullptr); EXPECT_TRUE(stream_->write_side_closed()); - EXPECT_EQ(1u, session_->GetNumDrainingStreams()); + EXPECT_EQ(1u, QuicSessionPeer::GetNumDrainingStreams(session_.get())); EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); } @@ -778,7 +764,7 @@ TEST_P(QuicStreamTest, SetDrainingOutgoingIncoming) { EXPECT_CALL(*session_, WritevData(kTestStreamId, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), 2u, 0u, FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); stream_->WriteOrBufferData(quiche::QuicheStringPiece(kData1, 2), true, nullptr); @@ -794,7 +780,7 @@ TEST_P(QuicStreamTest, SetDrainingOutgoingIncoming) { EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_)); EXPECT_FALSE(stream_->reading_stopped()); - EXPECT_EQ(1u, session_->GetNumDrainingStreams()); + EXPECT_EQ(1u, QuicSessionPeer::GetNumDrainingStreams(session_.get())); EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(session_.get())); } @@ -1054,7 +1040,7 @@ TEST_P(QuicStreamTest, WriteBufferedData) { EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), 100u, 0u, NO_FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); stream_->WriteOrBufferData(data, false, nullptr); stream_->WriteOrBufferData(data, false, nullptr); @@ -1067,7 +1053,7 @@ TEST_P(QuicStreamTest, WriteBufferedData) { EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), 100, 100u, NO_FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); // Buffered data size > threshold, do not ask upper layer for more data. EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(0); @@ -1082,7 +1068,7 @@ TEST_P(QuicStreamTest, WriteBufferedData) { EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this, data_to_write]() { return session_->ConsumeData(stream_->id(), data_to_write, 200u, NO_FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); // Buffered data size < threshold, ask upper layer for more data. EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(1); @@ -1132,7 +1118,7 @@ TEST_P(QuicStreamTest, WriteBufferedData) { EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this, data_to_write]() { return session_->ConsumeData(stream_->id(), data_to_write, 0u, NO_FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(1); @@ -1194,7 +1180,7 @@ TEST_P(QuicStreamTest, WriteMemSlices) { EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), 100u, 0u, NO_FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); // There is no buffered data before, all data should be consumed. QuicConsumedData consumed = stream_->WriteMemSlices(span1, false); @@ -1217,7 +1203,7 @@ TEST_P(QuicStreamTest, WriteMemSlices) { EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this, data_to_write]() { return session_->ConsumeData(stream_->id(), data_to_write, 100u, NO_FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(1); stream_->OnCanWrite(); @@ -1254,7 +1240,7 @@ TEST_P(QuicStreamTest, WriteMemSlicesReachStreamLimit) { EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), 5u, 0u, NO_FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); // There is no buffered data before, all data should be consumed. QuicConsumedData consumed = stream_->WriteMemSlices(span1, false); @@ -1392,7 +1378,7 @@ TEST_P(QuicStreamTest, OnStreamFrameLost) { EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), 9u, 18u, FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); stream_->OnCanWrite(); EXPECT_FALSE(stream_->HasPendingRetransmission()); @@ -1422,7 +1408,7 @@ TEST_P(QuicStreamTest, CannotBundleLostFin) { EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), 9u, 0u, NO_FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillOnce(Return(QuicConsumedData(0, true))); @@ -1511,7 +1497,7 @@ TEST_P(QuicStreamTest, RetransmitStreamData) { EXPECT_CALL(*session_, WritevData(stream_->id(), 10, 0, NO_FIN, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), 8, 0u, NO_FIN, - NOT_RETRANSMISSION, QuicheNullOpt); + NOT_RETRANSMISSION, QUICHE_NULLOPT); })); EXPECT_FALSE(stream_->RetransmitStreamData(0, 18, true, PTO_RETRANSMISSION)); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_syscall_wrapper.cc b/chromium/net/third_party/quiche/src/quic/core/quic_syscall_wrapper.cc new file mode 100644 index 00000000000..b2404c6ee97 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/quic_syscall_wrapper.cc @@ -0,0 +1,49 @@ +// 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 "net/third_party/quiche/src/quic/core/quic_syscall_wrapper.h" + +#include <atomic> +#include <cerrno> + +namespace quic { +namespace { +std::atomic<QuicSyscallWrapper*> global_syscall_wrapper(new QuicSyscallWrapper); +} // namespace + +ssize_t QuicSyscallWrapper::Sendmsg(int sockfd, const msghdr* msg, int flags) { + return ::sendmsg(sockfd, msg, flags); +} + +int QuicSyscallWrapper::Sendmmsg(int sockfd, + mmsghdr* msgvec, + unsigned int vlen, + int flags) { +#if defined(__linux__) && !defined(__ANDROID__) + return ::sendmmsg(sockfd, msgvec, vlen, flags); +#else + errno = ENOSYS; + return -1; +#endif +} + +QuicSyscallWrapper* GetGlobalSyscallWrapper() { + return global_syscall_wrapper.load(); +} + +void SetGlobalSyscallWrapper(QuicSyscallWrapper* wrapper) { + global_syscall_wrapper.store(wrapper); +} + +ScopedGlobalSyscallWrapperOverride::ScopedGlobalSyscallWrapperOverride( + QuicSyscallWrapper* wrapper_in_scope) + : original_wrapper_(GetGlobalSyscallWrapper()) { + SetGlobalSyscallWrapper(wrapper_in_scope); +} + +ScopedGlobalSyscallWrapperOverride::~ScopedGlobalSyscallWrapperOverride() { + SetGlobalSyscallWrapper(original_wrapper_); +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_syscall_wrapper.h b/chromium/net/third_party/quiche/src/quic/core/quic_syscall_wrapper.h new file mode 100644 index 00000000000..3a80ac6f6cf --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/quic_syscall_wrapper.h @@ -0,0 +1,49 @@ +// 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_PLATFORM_IMPL_QUIC_SYSCALL_WRAPPER_H_ +#define QUICHE_QUIC_PLATFORM_IMPL_QUIC_SYSCALL_WRAPPER_H_ + +#include <sys/socket.h> +#include <sys/types.h> + +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +struct mmsghdr; +namespace quic { + +// QuicSyscallWrapper is a pass-through proxy to the real syscalls. +class QUIC_EXPORT_PRIVATE QuicSyscallWrapper { + public: + virtual ~QuicSyscallWrapper() = default; + + virtual ssize_t Sendmsg(int sockfd, const msghdr* msg, int flags); + + virtual int Sendmmsg(int sockfd, + mmsghdr* msgvec, + unsigned int vlen, + int flags); +}; + +// A global instance of QuicSyscallWrapper, used by some socket util functions. +QuicSyscallWrapper* GetGlobalSyscallWrapper(); + +// Change the global QuicSyscallWrapper to |wrapper|, for testing. +void SetGlobalSyscallWrapper(QuicSyscallWrapper* wrapper); + +// ScopedGlobalSyscallWrapperOverride changes the global QuicSyscallWrapper +// during its lifetime, for testing. +class QUIC_EXPORT_PRIVATE ScopedGlobalSyscallWrapperOverride { + public: + explicit ScopedGlobalSyscallWrapperOverride( + QuicSyscallWrapper* wrapper_in_scope); + ~ScopedGlobalSyscallWrapperOverride(); + + private: + QuicSyscallWrapper* original_wrapper_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_PLATFORM_IMPL_QUIC_SYSCALL_WRAPPER_H_ 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 270383d27ae..d6c2f6c25de 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 @@ -74,4 +74,35 @@ bool ContainsQuicTag(const QuicTagVector& tag_vector, QuicTag tag) { tag_vector.end(); } +QuicTag ParseQuicTag(quiche::QuicheStringPiece tag_string) { + quiche::QuicheTextUtils::RemoveLeadingAndTrailingWhitespace(&tag_string); + std::string tag_bytes; + if (tag_string.length() == 8) { + tag_bytes = quiche::QuicheTextUtils::HexDecode(tag_string); + tag_string = tag_bytes; + } + QuicTag tag = 0; + // Iterate over every character from right to left. + for (auto it = tag_string.rbegin(); it != tag_string.rend(); ++it) { + // The cast here is required on platforms where char is signed. + unsigned char token_char = static_cast<unsigned char>(*it); + tag <<= 8; + tag |= token_char; + } + return tag; +} + +QuicTagVector ParseQuicTagVector(quiche::QuicheStringPiece tags_string) { + QuicTagVector tag_vector; + quiche::QuicheTextUtils::RemoveLeadingAndTrailingWhitespace(&tags_string); + if (!tags_string.empty()) { + std::vector<quiche::QuicheStringPiece> tag_strings = + quiche::QuicheTextUtils::Split(tags_string, ','); + for (quiche::QuicheStringPiece tag_string : tag_strings) { + tag_vector.push_back(ParseQuicTag(tag_string)); + } + } + return tag_vector; +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_tag.h b/chromium/net/third_party/quiche/src/quic/core/quic_tag.h index 71fcdd597f4..7ba6cfc5f39 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_tag.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_tag.h @@ -10,6 +10,7 @@ #include <vector> #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" namespace quic { @@ -48,6 +49,17 @@ QUIC_EXPORT_PRIVATE bool FindMutualQuicTag(const QuicTagVector& our_tags, // treat it as a number if not. QUIC_EXPORT_PRIVATE std::string QuicTagToString(QuicTag tag); +// Utility function that converts a string of the form "ABCD" to its +// corresponding QuicTag. Note that tags that are less than four characters +// long are right-padded with zeroes. Tags that contain non-ASCII characters +// are represented as 8-character-long hexadecimal strings. +QUIC_EXPORT_PRIVATE QuicTag ParseQuicTag(quiche::QuicheStringPiece tag_string); + +// Utility function that converts a string of the form "ABCD,EFGH" to a vector +// of the form {kABCD,kEFGH}. Note the caveats on ParseQuicTag. +QUIC_EXPORT_PRIVATE QuicTagVector +ParseQuicTagVector(quiche::QuicheStringPiece tags_string); + } // namespace quic #endif // QUICHE_QUIC_CORE_QUIC_TAG_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_tag_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_tag_test.cc index 3d58133eeb7..13c899edd41 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_tag_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_tag_test.cc @@ -33,6 +33,48 @@ TEST_F(QuicTagTest, MakeQuicTag) { EXPECT_EQ('D', bytes[3]); } +TEST_F(QuicTagTest, ParseQuicTag) { + QuicTag tag_abcd = MakeQuicTag('A', 'B', 'C', 'D'); + EXPECT_EQ(ParseQuicTag("ABCD"), tag_abcd); + EXPECT_EQ(ParseQuicTag("ABCDE"), tag_abcd); + QuicTag tag_efgh = MakeQuicTag('E', 'F', 'G', 'H'); + EXPECT_EQ(ParseQuicTag("EFGH"), tag_efgh); + QuicTag tag_ijk = MakeQuicTag('I', 'J', 'K', 0); + EXPECT_EQ(ParseQuicTag("IJK"), tag_ijk); + QuicTag tag_l = MakeQuicTag('L', 0, 0, 0); + EXPECT_EQ(ParseQuicTag("L"), tag_l); + QuicTag tag_hex = MakeQuicTag('M', 'N', 'O', static_cast<char>(255)); + EXPECT_EQ(ParseQuicTag("4d4e4fff"), tag_hex); + EXPECT_EQ(ParseQuicTag("4D4E4FFF"), tag_hex); + QuicTag tag_with_numbers = MakeQuicTag('P', 'Q', '1', '2'); + EXPECT_EQ(ParseQuicTag("PQ12"), tag_with_numbers); + QuicTag tag_with_custom_chars = MakeQuicTag('r', '$', '_', '7'); + EXPECT_EQ(ParseQuicTag("r$_7"), tag_with_custom_chars); + QuicTag tag_zero = 0; + EXPECT_EQ(ParseQuicTag(""), tag_zero); + QuicTagVector tag_vector; + EXPECT_EQ(ParseQuicTagVector(""), tag_vector); + EXPECT_EQ(ParseQuicTagVector(" "), tag_vector); + tag_vector.push_back(tag_abcd); + EXPECT_EQ(ParseQuicTagVector("ABCD"), tag_vector); + tag_vector.push_back(tag_efgh); + EXPECT_EQ(ParseQuicTagVector("ABCD,EFGH"), tag_vector); + tag_vector.push_back(tag_ijk); + EXPECT_EQ(ParseQuicTagVector("ABCD,EFGH,IJK"), tag_vector); + tag_vector.push_back(tag_l); + EXPECT_EQ(ParseQuicTagVector("ABCD,EFGH,IJK,L"), tag_vector); + tag_vector.push_back(tag_hex); + EXPECT_EQ(ParseQuicTagVector("ABCD,EFGH,IJK,L,4d4e4fff"), tag_vector); + tag_vector.push_back(tag_with_numbers); + EXPECT_EQ(ParseQuicTagVector("ABCD,EFGH,IJK,L,4d4e4fff,PQ12"), tag_vector); + tag_vector.push_back(tag_with_custom_chars); + EXPECT_EQ(ParseQuicTagVector("ABCD,EFGH,IJK,L,4d4e4fff,PQ12,r$_7"), + tag_vector); + tag_vector.push_back(tag_zero); + EXPECT_EQ(ParseQuicTagVector("ABCD,EFGH,IJK,L,4d4e4fff,PQ12,r$_7,"), + tag_vector); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time.h b/chromium/net/third_party/quiche/src/quic/core/quic_time.h index adecbcd1514..079b59a6bdc 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_time.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_time.h @@ -184,6 +184,14 @@ class QUIC_EXPORT_PRIVATE QuicWallTime { QUIC_TIME_WARN_UNUSED_RESULT QuicWallTime Subtract(QuicTime::Delta delta) const; + bool operator==(const QuicWallTime& other) const { + return microseconds_ == other.microseconds_; + } + + QuicTime::Delta operator-(const QuicWallTime& rhs) const { + return QuicTime::Delta::FromMicroseconds(microseconds_ - rhs.microseconds_); + } + private: explicit constexpr QuicWallTime(uint64_t microseconds) : microseconds_(microseconds) {} diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor.cc b/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor.cc index edbce0c709c..eeaf297c1ca 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor.cc @@ -64,6 +64,7 @@ void QuicTraceVisitor::OnPacketSent(const SerializedPacket& serialized_packet, case BLOCKED_FRAME: case PING_FRAME: case HANDSHAKE_DONE_FRAME: + case ACK_FREQUENCY_FRAME: PopulateFrameInfo(frame, event->add_frames()); break; @@ -218,6 +219,7 @@ void QuicTraceVisitor::PopulateFrameInfo(const QuicFrame& frame, case MESSAGE_FRAME: case CRYPTO_FRAME: case NEW_TOKEN_FRAME: + case ACK_FREQUENCY_FRAME: break; case NUM_FRAME_TYPES: diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_types.cc b/chromium/net/third_party/quiche/src/quic/core/quic_types.cc index 36614cd8f0a..a52571e3905 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_types.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_types.cc @@ -173,8 +173,7 @@ std::string TransmissionTypeToString(TransmissionType transmission_type) { switch (transmission_type) { RETURN_STRING_LITERAL(NOT_RETRANSMISSION); RETURN_STRING_LITERAL(HANDSHAKE_RETRANSMISSION); - RETURN_STRING_LITERAL(ALL_UNACKED_RETRANSMISSION); - RETURN_STRING_LITERAL(ALL_INITIAL_RETRANSMISSION); + RETURN_STRING_LITERAL(ALL_ZERO_RTT_RETRANSMISSION); RETURN_STRING_LITERAL(LOSS_RETRANSMISSION); RETURN_STRING_LITERAL(RTO_RETRANSMISSION); RETURN_STRING_LITERAL(TLP_RETRANSMISSION); @@ -191,6 +190,11 @@ std::string TransmissionTypeToString(TransmissionType transmission_type) { } } +std::ostream& operator<<(std::ostream& os, TransmissionType transmission_type) { + os << TransmissionTypeToString(transmission_type); + return os; +} + std::string PacketHeaderFormatToString(PacketHeaderFormat format) { switch (format) { RETURN_STRING_LITERAL(IETF_QUIC_LONG_HEADER_PACKET); @@ -261,11 +265,17 @@ std::string SerializedPacketFateToString(SerializedPacketFate fate) { RETURN_STRING_LITERAL(BUFFER); RETURN_STRING_LITERAL(SEND_TO_WRITER); RETURN_STRING_LITERAL(FAILED_TO_WRITE_COALESCED_PACKET); + RETURN_STRING_LITERAL(LEGACY_VERSION_ENCAPSULATE); default: return quiche::QuicheStrCat("Unknown(", static_cast<int>(fate), ")"); } } +std::ostream& operator<<(std::ostream& os, SerializedPacketFate fate) { + os << SerializedPacketFateToString(fate); + return os; +} + std::string EncryptionLevelToString(EncryptionLevel level) { switch (level) { RETURN_STRING_LITERAL(ENCRYPTION_INITIAL); @@ -278,6 +288,11 @@ std::string EncryptionLevelToString(EncryptionLevel level) { } } +std::ostream& operator<<(std::ostream& os, EncryptionLevel level) { + os << EncryptionLevelToString(level); + return os; +} + std::string QuicConnectionCloseTypeString(QuicConnectionCloseType type) { switch (type) { RETURN_STRING_LITERAL(GOOGLE_QUIC_CONNECTION_CLOSE); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_types.h b/chromium/net/third_party/quiche/src/quic/core/quic_types.h index f2919ef3766..0c736f93178 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_types.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_types.h @@ -163,21 +163,24 @@ struct QUIC_EXPORT_PRIVATE WriteResult { enum TransmissionType : int8_t { NOT_RETRANSMISSION, FIRST_TRANSMISSION_TYPE = NOT_RETRANSMISSION, - HANDSHAKE_RETRANSMISSION, // Retransmits due to handshake timeouts. - // TODO(fayang): remove ALL_UNACKED_RETRANSMISSION. - ALL_UNACKED_RETRANSMISSION, // Retransmits all unacked packets. - ALL_INITIAL_RETRANSMISSION, // Retransmits all initially encrypted packets. - LOSS_RETRANSMISSION, // Retransmits due to loss detection. - RTO_RETRANSMISSION, // Retransmits due to retransmit time out. - TLP_RETRANSMISSION, // Tail loss probes. - PTO_RETRANSMISSION, // Retransmission due to probe timeout. - PROBING_RETRANSMISSION, // Retransmission in order to probe bandwidth. + HANDSHAKE_RETRANSMISSION, // Retransmits due to handshake timeouts. + ALL_ZERO_RTT_RETRANSMISSION, // Retransmits all packets encrypted with 0-RTT + // key. + LOSS_RETRANSMISSION, // Retransmits due to loss detection. + RTO_RETRANSMISSION, // Retransmits due to retransmit time out. + TLP_RETRANSMISSION, // Tail loss probes. + PTO_RETRANSMISSION, // Retransmission due to probe timeout. + PROBING_RETRANSMISSION, // Retransmission in order to probe bandwidth. LAST_TRANSMISSION_TYPE = PROBING_RETRANSMISSION, }; QUIC_EXPORT_PRIVATE std::string TransmissionTypeToString( TransmissionType transmission_type); +QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + TransmissionType transmission_type); + enum HasRetransmittableData : uint8_t { NO_RETRANSMITTABLE_DATA, HAS_RETRANSMITTABLE_DATA, @@ -224,6 +227,8 @@ enum QuicFrameType : uint8_t { STOP_WAITING_FRAME = 6, PING_FRAME = 7, CRYPTO_FRAME = 8, + // TODO(b/157935330): stop hard coding this when deprecate T050. + HANDSHAKE_DONE_FRAME = 9, // STREAM and ACK frames are special frames. They are encoded differently on // the wire and their values do not need to be stable. @@ -245,7 +250,7 @@ enum QuicFrameType : uint8_t { MESSAGE_FRAME, NEW_TOKEN_FRAME, RETIRE_CONNECTION_ID_FRAME, - HANDSHAKE_DONE_FRAME, + ACK_FREQUENCY_FRAME, NUM_FRAME_TYPES }; @@ -305,6 +310,9 @@ enum QuicIetfFrameType : uint8_t { IETF_EXTENSION_MESSAGE = 0x21, IETF_EXTENSION_MESSAGE_NO_LENGTH_V99 = 0x30, IETF_EXTENSION_MESSAGE_V99 = 0x31, + + // An QUIC extension frame for sender control of acknowledgement delays + IETF_ACK_FREQUENCY = 0xaf }; QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, const QuicIetfFrameType& c); @@ -440,6 +448,9 @@ inline bool EncryptionLevelIsValid(EncryptionLevel level) { QUIC_EXPORT_PRIVATE std::string EncryptionLevelToString(EncryptionLevel level); +QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, + EncryptionLevel level); + // Enumeration of whether a server endpoint will request a client certificate, // and whether that endpoint requires a valid client certificate to establish a // connection. @@ -687,11 +698,16 @@ enum SerializedPacketFate : uint8_t { SEND_TO_WRITER, // Send packet to writer. FAILED_TO_WRITE_COALESCED_PACKET, // Packet cannot be coalesced, error occurs // when sending existing coalesced packet. + LEGACY_VERSION_ENCAPSULATE, // Perform Legacy Version Encapsulation on this + // packet. }; QUIC_EXPORT_PRIVATE std::string SerializedPacketFateToString( SerializedPacketFate fate); +QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, + const SerializedPacketFate fate); + // There are three different forms of CONNECTION_CLOSE. enum QuicConnectionCloseType { GOOGLE_QUIC_CONNECTION_CLOSE = 0, @@ -734,6 +750,45 @@ struct QUIC_NO_EXPORT NextReleaseTimeResult { bool allow_burst; }; +// QuicPacketBuffer bundles a buffer and a function that releases it. Note +// it does not assume ownership of buffer, i.e. it doesn't release the buffer on +// destruction. +struct QUIC_NO_EXPORT QuicPacketBuffer { + QuicPacketBuffer() = default; + + QuicPacketBuffer(char* buffer, + std::function<void(const char*)> release_buffer) + : buffer(buffer), release_buffer(std::move(release_buffer)) {} + + char* buffer = nullptr; + std::function<void(const char*)> release_buffer; +}; + +// QuicOwnedPacketBuffer is a QuicPacketBuffer that assumes buffer ownership. +struct QUIC_NO_EXPORT QuicOwnedPacketBuffer : public QuicPacketBuffer { + QuicOwnedPacketBuffer(const QuicOwnedPacketBuffer&) = delete; + QuicOwnedPacketBuffer& operator=(const QuicOwnedPacketBuffer&) = delete; + + QuicOwnedPacketBuffer(char* buffer, + std::function<void(const char*)> release_buffer) + : QuicPacketBuffer(buffer, std::move(release_buffer)) {} + + QuicOwnedPacketBuffer(QuicOwnedPacketBuffer&& owned_buffer) + : QuicPacketBuffer(std::move(owned_buffer)) { + // |owned_buffer| does not own a buffer any more. + owned_buffer.buffer = nullptr; + } + + explicit QuicOwnedPacketBuffer(QuicPacketBuffer&& packet_buffer) + : QuicPacketBuffer(std::move(packet_buffer)) {} + + ~QuicOwnedPacketBuffer() { + if (release_buffer != nullptr && buffer != nullptr) { + release_buffer(buffer); + } + } +}; + } // namespace quic #endif // QUICHE_QUIC_CORE_QUIC_TYPES_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket.h b/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket.h index 04f84357dab..258de080118 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket.h @@ -163,10 +163,14 @@ class QUIC_EXPORT_PRIVATE QuicUdpSocketApi { public: // Creates a non-blocking udp socket, sets the receive/send buffer and enable // receiving of self ip addresses on read. - // Return kQuicInvalidSocketFd if failed. + // If address_family == AF_INET6 and ipv6_only is true, receiving of IPv4 self + // addresses is disabled. This is only necessary for IPv6 sockets on iOS - all + // other platforms can ignore this parameter. Return kQuicInvalidSocketFd if + // failed. QuicUdpSocketFd Create(int address_family, int receive_buffer_size, - int send_buffer_size); + int send_buffer_size, + bool ipv6_only = false); // Closes |fd|. No-op if |fd| equals to kQuicInvalidSocketFd. void Destroy(QuicUdpSocketFd fd); @@ -238,7 +242,8 @@ class QUIC_EXPORT_PRIVATE QuicUdpSocketApi { bool SetupSocket(QuicUdpSocketFd fd, int address_family, int receive_buffer_size, - int send_buffer_size); + int send_buffer_size, + bool ipv6_only); bool EnableReceiveSelfIpAddressForV4(QuicUdpSocketFd fd); bool EnableReceiveSelfIpAddressForV6(QuicUdpSocketFd fd); }; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket_posix.cc b/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket_posix.cc index b6c58cdb1d3..c5ab345f433 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket_posix.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket_posix.cc @@ -239,7 +239,8 @@ bool NextCmsg(msghdr* hdr, QuicUdpSocketFd QuicUdpSocketApi::Create(int address_family, int receive_buffer_size, - int send_buffer_size) { + int send_buffer_size, + bool ipv6_only) { // DCHECK here so the program exits early(before reading packets) in debug // mode. This should have been a static_assert, however it can't be done on // ios/osx because CMSG_SPACE isn't a constant expression there. @@ -250,7 +251,8 @@ QuicUdpSocketFd QuicUdpSocketApi::Create(int address_family, return kQuicInvalidSocketFd; } - if (!SetupSocket(fd, address_family, receive_buffer_size, send_buffer_size)) { + if (!SetupSocket(fd, address_family, receive_buffer_size, send_buffer_size, + ipv6_only)) { Destroy(fd); return kQuicInvalidSocketFd; } @@ -261,7 +263,8 @@ QuicUdpSocketFd QuicUdpSocketApi::Create(int address_family, bool QuicUdpSocketApi::SetupSocket(QuicUdpSocketFd fd, int address_family, int receive_buffer_size, - int send_buffer_size) { + int send_buffer_size, + bool ipv6_only) { // Receive buffer size. if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &receive_buffer_size, sizeof(receive_buffer_size)) != 0) { @@ -276,14 +279,20 @@ bool QuicUdpSocketApi::SetupSocket(QuicUdpSocketFd fd, return false; } - if (!EnableReceiveSelfIpAddressForV4(fd)) { - QUIC_LOG_FIRST_N(ERROR, 100) << "Failed to enable receiving of self v4 ip"; - return false; + if (!(address_family == AF_INET6 && ipv6_only)) { + if (!EnableReceiveSelfIpAddressForV4(fd)) { + QUIC_LOG_FIRST_N(ERROR, 100) + << "Failed to enable receiving of self v4 ip"; + return false; + } } - if (address_family == AF_INET6 && !EnableReceiveSelfIpAddressForV6(fd)) { - QUIC_LOG_FIRST_N(ERROR, 100) << "Failed to enable receiving of self v6 ip"; - return false; + if (address_family == AF_INET6) { + if (!EnableReceiveSelfIpAddressForV6(fd)) { + QUIC_LOG_FIRST_N(ERROR, 100) + << "Failed to enable receiving of self v6 ip"; + return false; + } } return true; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket_test.cc index e43fb10c40b..f54b0edd073 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket_test.cc @@ -3,6 +3,11 @@ // found in the LICENSE file. #include "net/third_party/quiche/src/quic/core/quic_udp_socket.h" +#include <sys/socket.h> + +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif #include "net/third_party/quiche/src/quic/core/quic_constants.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" @@ -15,33 +20,49 @@ struct ReadBuffers { char control_buffer[512]; char packet_buffer[1536]; }; + +// Allows IPv6-specific testing. +struct TestParameters { + // If true, the test will only use IPv6. If false, IPv4 will be used if + // possible, with IPv6 used as a fallback. + bool force_ipv6; + // The value of ipv6_only to be used in QuicUdpSocketApi::Create calls. + bool ipv6_only; +}; } // namespace -class QuicUdpSocketTest : public QuicTest { +class QuicUdpSocketTest : public QuicTestWithParam<TestParameters> { protected: void SetUp() override { - // Try creating AF_INET socket, if it fails because of unsupported address - // family then tests are being run under IPv6-only environment, initialize - // address family to use for running the test under as AF_INET6 otherwise - // initialize it as AF_INET. - address_family_ = AF_INET; - fd_client_ = - api_.Create(address_family_, - /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, - /*send_buffer_size =*/kDefaultSocketReceiveBuffer); + const TestParameters& parameters = GetParam(); + if (!parameters.force_ipv6) { + // Try creating AF_INET socket, if it fails because of unsupported address + // family then tests are being run under IPv6-only environment, initialize + // address family to use for running the test under as AF_INET6 otherwise + // initialize it as AF_INET. + address_family_ = AF_INET; + fd_client_ = + api_.Create(address_family_, + /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, + /*send_buffer_size =*/kDefaultSocketReceiveBuffer, + /*ipv6_only =*/parameters.ipv6_only); + } if (fd_client_ == kQuicInvalidSocketFd) { + // Either AF_INET is unsupported, or force_ipv6 is true. address_family_ = AF_INET6; fd_client_ = api_.Create(address_family_, /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, - /*send_buffer_size =*/kDefaultSocketReceiveBuffer); + /*send_buffer_size =*/kDefaultSocketReceiveBuffer, + /*ipv6_only =*/parameters.ipv6_only); } ASSERT_NE(fd_client_, kQuicInvalidSocketFd); fd_server_ = api_.Create(address_family_, /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, - /*send_buffer_size =*/kDefaultSocketReceiveBuffer); + /*send_buffer_size =*/kDefaultSocketReceiveBuffer, + /*ipv6_only =*/parameters.ipv6_only); ASSERT_NE(fd_server_, kQuicInvalidSocketFd); ASSERT_TRUE( @@ -114,8 +135,8 @@ class QuicUdpSocketTest : public QuicTest { } QuicUdpSocketApi api_; - QuicUdpSocketFd fd_client_; - QuicUdpSocketFd fd_server_; + QuicUdpSocketFd fd_client_ = kQuicInvalidSocketFd; + QuicUdpSocketFd fd_server_ = kQuicInvalidSocketFd; QuicSocketAddress server_address_; int address_family_; char client_packet_buffer_[kEthernetMTU] = {0}; @@ -123,7 +144,22 @@ class QuicUdpSocketTest : public QuicTest { char server_control_buffer_[512] = {0}; }; -TEST_F(QuicUdpSocketTest, ReadPacketResultReset) { +INSTANTIATE_TEST_SUITE_P( + PlatformIndependent, + QuicUdpSocketTest, + testing::Values(TestParameters{/*force_ipv6 =*/false, /*ipv6_only =*/false}, + TestParameters{/*force_ipv6 =*/false, /*ipv6_only =*/true}, + TestParameters{/*force_ipv6 =*/true, /*ipv6_only =*/true})); + +#ifndef TARGET_OS_IPHONE +// IPv6 on iOS is known to fail without ipv6_only, so should not be tested. +INSTANTIATE_TEST_SUITE_P(NonIos, + QuicUdpSocketTest, + testing::Values(TestParameters{/*force_ipv6 =*/true, + /*ipv6_only =*/false})); +#endif + +TEST_P(QuicUdpSocketTest, ReadPacketResultReset) { QuicUdpSocketApi::ReadPacketResult result; result.packet_info.SetDroppedPackets(100); result.packet_buffer.buffer_len = 100; @@ -137,7 +173,7 @@ TEST_F(QuicUdpSocketTest, ReadPacketResultReset) { EXPECT_EQ(200u, result.packet_buffer.buffer_len); } -TEST_F(QuicUdpSocketTest, ReadPacketOnly) { +TEST_P(QuicUdpSocketTest, ReadPacketOnly) { const size_t kPacketSize = 512; memset(client_packet_buffer_, '-', kPacketSize); ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), @@ -150,7 +186,7 @@ TEST_F(QuicUdpSocketTest, ReadPacketOnly) { ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); } -TEST_F(QuicUdpSocketTest, ReadTruncated) { +TEST_P(QuicUdpSocketTest, ReadTruncated) { const size_t kPacketSize = kDefaultMaxPacketSize + 1; memset(client_packet_buffer_, '*', kPacketSize); ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), @@ -161,7 +197,7 @@ TEST_F(QuicUdpSocketTest, ReadTruncated) { ASSERT_FALSE(read_result.ok); } -TEST_F(QuicUdpSocketTest, ReadDroppedPackets) { +TEST_P(QuicUdpSocketTest, ReadDroppedPackets) { const size_t kPacketSize = kDefaultMaxPacketSize; memset(client_packet_buffer_, '-', kPacketSize); ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), @@ -193,7 +229,7 @@ TEST_F(QuicUdpSocketTest, ReadDroppedPackets) { } } -TEST_F(QuicUdpSocketTest, ReadSelfIp) { +TEST_P(QuicUdpSocketTest, ReadSelfIp) { const QuicUdpPacketInfoBit self_ip_bit = (address_family_ == AF_INET) ? QuicUdpPacketInfoBit::V4_SELF_IP : QuicUdpPacketInfoBit::V6_SELF_IP; @@ -214,7 +250,7 @@ TEST_F(QuicUdpSocketTest, ReadSelfIp) { : read_result.packet_info.self_v6_ip()); } -TEST_F(QuicUdpSocketTest, ReadReceiveTimestamp) { +TEST_P(QuicUdpSocketTest, ReadReceiveTimestamp) { const size_t kPacketSize = kDefaultMaxPacketSize; memset(client_packet_buffer_, '-', kPacketSize); ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), @@ -249,7 +285,7 @@ TEST_F(QuicUdpSocketTest, ReadReceiveTimestamp) { QuicWallTime::FromUNIXSeconds(1577836800).IsBefore(recv_timestamp)); } -TEST_F(QuicUdpSocketTest, Ttl) { +TEST_P(QuicUdpSocketTest, Ttl) { const size_t kPacketSize = 512; memset(client_packet_buffer_, '$', kPacketSize); ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), @@ -282,7 +318,7 @@ TEST_F(QuicUdpSocketTest, Ttl) { EXPECT_EQ(13, read_result.packet_info.ttl()); } -TEST_F(QuicUdpSocketTest, ReadMultiplePackets) { +TEST_P(QuicUdpSocketTest, ReadMultiplePackets) { const QuicUdpPacketInfoBit self_ip_bit = (address_family_ == AF_INET) ? QuicUdpPacketInfoBit::V4_SELF_IP : QuicUdpPacketInfoBit::V6_SELF_IP; @@ -335,7 +371,7 @@ TEST_F(QuicUdpSocketTest, ReadMultiplePackets) { } } -TEST_F(QuicUdpSocketTest, ReadMultiplePacketsSomeTruncated) { +TEST_P(QuicUdpSocketTest, ReadMultiplePacketsSomeTruncated) { const QuicUdpPacketInfoBit self_ip_bit = (address_family_ == AF_INET) ? QuicUdpPacketInfoBit::V4_SELF_IP : QuicUdpPacketInfoBit::V6_SELF_IP; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.cc b/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.cc index 8276f1c287c..f06d0746e40 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.cc @@ -28,6 +28,7 @@ QuicUnackedPacketMap::QuicUnackedPacketMap(Perspective perspective) : perspective_(perspective), least_unacked_(FirstSendingPacketNumber()), bytes_in_flight_(0), + bytes_in_flight_per_packet_number_space_{0, 0, 0}, packets_in_flight_(0), last_inflight_packet_sent_time_(QuicTime::Zero()), last_inflight_packets_sent_time_{{QuicTime::Zero()}, @@ -72,6 +73,7 @@ void QuicUnackedPacketMap::AddSentPacket(SerializedPacket* packet, const PacketNumberSpace packet_number_space = GetPacketNumberSpace(info.encryption_level); bytes_in_flight_ += bytes_sent; + bytes_in_flight_per_packet_number_space_[packet_number_space] += bytes_sent; ++packets_in_flight_; info.in_flight = true; largest_sent_retransmittable_packets_[packet_number_space] = packet_number; @@ -195,6 +197,27 @@ void QuicUnackedPacketMap::RemoveFromInFlight(QuicTransmissionInfo* info) { QUIC_BUG_IF(packets_in_flight_ == 0); bytes_in_flight_ -= info->bytes_sent; --packets_in_flight_; + + const PacketNumberSpace packet_number_space = + GetPacketNumberSpace(info->encryption_level); + if (bytes_in_flight_per_packet_number_space_[packet_number_space] < + info->bytes_sent) { + QUIC_BUG << "bytes_in_flight: " + << bytes_in_flight_per_packet_number_space_[packet_number_space] + << " is smaller than bytes_sent: " << info->bytes_sent + << " for packet number space: " + << PacketNumberSpaceToString(packet_number_space); + bytes_in_flight_per_packet_number_space_[packet_number_space] = 0; + } else { + bytes_in_flight_per_packet_number_space_[packet_number_space] -= + info->bytes_sent; + } + if (GetQuicReloadableFlag(quic_fix_last_inflight_packets_sent_time) && + bytes_in_flight_per_packet_number_space_[packet_number_space] == 0) { + QUIC_RELOADABLE_FLAG_COUNT(quic_fix_last_inflight_packets_sent_time); + last_inflight_packets_sent_time_[packet_number_space] = QuicTime::Zero(); + } + info->in_flight = false; } } @@ -230,7 +253,12 @@ QuicUnackedPacketMap::NeuterUnencryptedPackets() { } } if (supports_multiple_packet_number_spaces_) { - last_inflight_packets_sent_time_[INITIAL_DATA] = QuicTime::Zero(); + if (GetQuicReloadableFlag(quic_fix_last_inflight_packets_sent_time)) { + DCHECK_EQ(QuicTime::Zero(), + last_inflight_packets_sent_time_[INITIAL_DATA]); + } else { + last_inflight_packets_sent_time_[INITIAL_DATA] = QuicTime::Zero(); + } } return neutered_packets; } @@ -254,7 +282,12 @@ QuicUnackedPacketMap::NeuterHandshakePackets() { } } if (supports_multiple_packet_number_spaces()) { - last_inflight_packets_sent_time_[HANDSHAKE_DATA] = QuicTime::Zero(); + if (GetQuicReloadableFlag(quic_fix_last_inflight_packets_sent_time)) { + DCHECK_EQ(QuicTime::Zero(), + last_inflight_packets_sent_time_[HANDSHAKE_DATA]); + } else { + last_inflight_packets_sent_time_[HANDSHAKE_DATA] = QuicTime::Zero(); + } } return neutered_packets; } 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 bcf5061927a..43e900802f4 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 @@ -281,6 +281,9 @@ class QUIC_EXPORT_PRIVATE QuicUnackedPacketMap { QuicPacketNumber least_unacked_; QuicByteCount bytes_in_flight_; + // Bytes in flight per packet number space. + QuicByteCount + bytes_in_flight_per_packet_number_space_[NUM_PACKET_NUMBER_SPACES]; QuicPacketCount packets_in_flight_; // Time that the last inflight packet was sent. 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 60354f332fa..de4350390b0 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 @@ -6,6 +6,7 @@ #include <algorithm> #include <cstdint> +#include <cstring> #include <string> #include "net/third_party/quiche/src/quic/core/quic_connection_id.h" @@ -335,8 +336,7 @@ bool QuicUtils::ContainsFrameType(const QuicFrames& frames, SentPacketState QuicUtils::RetransmissionTypeToPacketState( TransmissionType retransmission_type) { switch (retransmission_type) { - case ALL_UNACKED_RETRANSMISSION: - case ALL_INITIAL_RETRANSMISSION: + case ALL_ZERO_RTT_RETRANSMISSION: return UNACKABLE; case HANDSHAKE_RETRANSMISSION: return HANDSHAKE_RETRANSMITTED; @@ -351,8 +351,7 @@ SentPacketState QuicUtils::RetransmissionTypeToPacketState( case PROBING_RETRANSMISSION: return PROBE_RETRANSMITTED; default: - QUIC_BUG << TransmissionTypeToString(retransmission_type) - << " is not a retransmission_type"; + QUIC_BUG << retransmission_type << " is not a retransmission_type"; return UNACKABLE; } } @@ -429,15 +428,21 @@ bool QuicUtils::IsOutgoingStreamId(ParsedQuicVersion version, } // static -bool QuicUtils::IsBidirectionalStreamId(QuicStreamId id) { +bool QuicUtils::IsBidirectionalStreamId(QuicStreamId id, + ParsedQuicVersion version) { + DCHECK(!GetQuicReloadableFlag(quic_fix_gquic_stream_type) || + version.HasIetfQuicFrames()); return id % 4 < 2; } // static StreamType QuicUtils::GetStreamType(QuicStreamId id, Perspective perspective, - bool peer_initiated) { - if (IsBidirectionalStreamId(id)) { + bool peer_initiated, + ParsedQuicVersion version) { + DCHECK(!GetQuicReloadableFlag(quic_fix_gquic_stream_type) || + version.HasIetfQuicFrames()); + if (IsBidirectionalStreamId(id, version)) { return BIDIRECTIONAL; } @@ -491,11 +496,37 @@ QuicStreamId QuicUtils::GetFirstUnidirectionalStreamId( // static QuicConnectionId QuicUtils::CreateReplacementConnectionId( - QuicConnectionId connection_id) { - const uint64_t connection_id_hash = FNV1a_64_Hash( + const QuicConnectionId& connection_id) { + return CreateReplacementConnectionId(connection_id, + kQuicDefaultConnectionIdLength); +} + +// static +QuicConnectionId QuicUtils::CreateReplacementConnectionId( + const QuicConnectionId& connection_id, + uint8_t expected_connection_id_length) { + if (expected_connection_id_length == 0) { + return EmptyQuicConnectionId(); + } + const uint64_t connection_id_hash64 = FNV1a_64_Hash( + quiche::QuicheStringPiece(connection_id.data(), connection_id.length())); + if (expected_connection_id_length <= sizeof(uint64_t)) { + return QuicConnectionId( + reinterpret_cast<const char*>(&connection_id_hash64), + expected_connection_id_length); + } + char new_connection_id_data[255] = {}; + const QuicUint128 connection_id_hash128 = FNV1a_128_Hash( quiche::QuicheStringPiece(connection_id.data(), connection_id.length())); - return QuicConnectionId(reinterpret_cast<const char*>(&connection_id_hash), - sizeof(connection_id_hash)); + static_assert(sizeof(connection_id_hash64) + sizeof(connection_id_hash128) <= + sizeof(new_connection_id_data), + "bad size"); + memcpy(new_connection_id_data, &connection_id_hash64, + sizeof(connection_id_hash64)); + memcpy(new_connection_id_data + sizeof(connection_id_hash64), + &connection_id_hash128, sizeof(connection_id_hash128)); + return QuicConnectionId(new_connection_id_data, + expected_connection_id_length); } // static @@ -603,7 +634,7 @@ PacketNumberSpace QuicUtils::GetPacketNumberSpace( return APPLICATION_DATA; default: QUIC_BUG << "Try to get packet number space of encryption level: " - << EncryptionLevelToString(encryption_level); + << encryption_level; return NUM_PACKET_NUMBER_SPACES; } } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_utils.h b/chromium/net/third_party/quiche/src/quic/core/quic_utils.h index eec9a5f25df..9e190e0b386 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_utils.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_utils.h @@ -146,14 +146,16 @@ class QUIC_EXPORT_PRIVATE QuicUtils { // Returns true if |id| is considered as bidirectional stream ID. Only used in // v99. - static bool IsBidirectionalStreamId(QuicStreamId id); + static bool IsBidirectionalStreamId(QuicStreamId id, + ParsedQuicVersion version); // Returns stream type. Either |perspective| or |peer_initiated| would be // enough together with |id|. This method enforces that the three parameters // are consistent. Only used in v99. static StreamType GetStreamType(QuicStreamId id, Perspective perspective, - bool peer_initiated); + bool peer_initiated, + ParsedQuicVersion version); // Returns the delta between consecutive stream IDs of the same type. static QuicStreamId StreamIdDelta(QuicTransportVersion version); @@ -168,11 +170,19 @@ class QUIC_EXPORT_PRIVATE QuicUtils { QuicTransportVersion version, Perspective perspective); - // Generates a 64bit connection ID derived from the input connection ID. + // Generates a connection ID of length |expected_connection_id_length| + // derived from |connection_id|. // This is guaranteed to be deterministic (calling this method with two // connection IDs that are equal is guaranteed to produce the same result). static QuicConnectionId CreateReplacementConnectionId( - QuicConnectionId connection_id); + const QuicConnectionId& connection_id, + uint8_t expected_connection_id_length); + + // Generates a 64bit connection ID derived from |connection_id|. + // This is guaranteed to be deterministic (calling this method with two + // connection IDs that are equal is guaranteed to produce the same result). + static QuicConnectionId CreateReplacementConnectionId( + const QuicConnectionId& connection_id); // Generates a random 64bit connection ID. static QuicConnectionId CreateRandomConnectionId(); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc index 5b2186acde4..041cd9856a1 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc @@ -125,8 +125,7 @@ TEST_F(QuicUtilsTest, RetransmissionTypeToPacketState) { EXPECT_EQ(HANDSHAKE_RETRANSMITTED, state); } else if (i == LOSS_RETRANSMISSION) { EXPECT_EQ(LOST, state); - } else if (i == ALL_UNACKED_RETRANSMISSION || - i == ALL_INITIAL_RETRANSMISSION) { + } else if (i == ALL_ZERO_RTT_RETRANSMISSION) { EXPECT_EQ(UNACKABLE, state); } else if (i == TLP_RETRANSMISSION) { EXPECT_EQ(TLP_RETRANSMITTED, state); @@ -180,6 +179,23 @@ TEST_F(QuicUtilsTest, ReplacementConnectionIdIsDeterministic) { EXPECT_EQ(connection_id72a, connection_id72b); EXPECT_EQ(QuicUtils::CreateReplacementConnectionId(connection_id72a), QuicUtils::CreateReplacementConnectionId(connection_id72b)); + // Test variant with custom length. + EXPECT_EQ(QuicUtils::CreateReplacementConnectionId(connection_id64a, 7), + QuicUtils::CreateReplacementConnectionId(connection_id64b, 7)); + EXPECT_EQ(QuicUtils::CreateReplacementConnectionId(connection_id64a, 9), + QuicUtils::CreateReplacementConnectionId(connection_id64b, 9)); + EXPECT_EQ(QuicUtils::CreateReplacementConnectionId(connection_id64a, 16), + QuicUtils::CreateReplacementConnectionId(connection_id64b, 16)); + EXPECT_EQ(QuicUtils::CreateReplacementConnectionId(connection_id72a, 7), + QuicUtils::CreateReplacementConnectionId(connection_id72b, 7)); + EXPECT_EQ(QuicUtils::CreateReplacementConnectionId(connection_id72a, 9), + QuicUtils::CreateReplacementConnectionId(connection_id72b, 9)); + EXPECT_EQ(QuicUtils::CreateReplacementConnectionId(connection_id72a, 16), + QuicUtils::CreateReplacementConnectionId(connection_id72b, 16)); + EXPECT_EQ(QuicUtils::CreateReplacementConnectionId(connection_id72a, 32), + QuicUtils::CreateReplacementConnectionId(connection_id72b, 32)); + EXPECT_EQ(QuicUtils::CreateReplacementConnectionId(connection_id72a, 255), + QuicUtils::CreateReplacementConnectionId(connection_id72b, 255)); } TEST_F(QuicUtilsTest, ReplacementConnectionIdLengthIsCorrect) { @@ -191,6 +207,22 @@ TEST_F(QuicUtilsTest, ReplacementConnectionIdLengthIsCorrect) { QuicUtils::CreateReplacementConnectionId(connection_id); EXPECT_EQ(kQuicDefaultConnectionIdLength, replacement_connection_id.length()); + // Test variant with custom length. + QuicConnectionId replacement_connection_id7 = + QuicUtils::CreateReplacementConnectionId(connection_id, 7); + EXPECT_EQ(7, replacement_connection_id7.length()); + QuicConnectionId replacement_connection_id9 = + QuicUtils::CreateReplacementConnectionId(connection_id, 9); + EXPECT_EQ(9, replacement_connection_id9.length()); + QuicConnectionId replacement_connection_id16 = + QuicUtils::CreateReplacementConnectionId(connection_id, 16); + EXPECT_EQ(16, replacement_connection_id16.length()); + QuicConnectionId replacement_connection_id32 = + QuicUtils::CreateReplacementConnectionId(connection_id, 32); + EXPECT_EQ(32, replacement_connection_id32.length()); + QuicConnectionId replacement_connection_id255 = + QuicUtils::CreateReplacementConnectionId(connection_id, 255); + EXPECT_EQ(255, replacement_connection_id255.length()); } } @@ -205,6 +237,17 @@ TEST_F(QuicUtilsTest, ReplacementConnectionIdHasEntropy) { EXPECT_NE(connection_id_i, connection_id_j); EXPECT_NE(QuicUtils::CreateReplacementConnectionId(connection_id_i), QuicUtils::CreateReplacementConnectionId(connection_id_j)); + // Test variant with custom length. + EXPECT_NE(QuicUtils::CreateReplacementConnectionId(connection_id_i, 7), + QuicUtils::CreateReplacementConnectionId(connection_id_j, 7)); + EXPECT_NE(QuicUtils::CreateReplacementConnectionId(connection_id_i, 9), + QuicUtils::CreateReplacementConnectionId(connection_id_j, 9)); + EXPECT_NE(QuicUtils::CreateReplacementConnectionId(connection_id_i, 16), + QuicUtils::CreateReplacementConnectionId(connection_id_j, 16)); + EXPECT_NE(QuicUtils::CreateReplacementConnectionId(connection_id_i, 32), + QuicUtils::CreateReplacementConnectionId(connection_id_j, 32)); + EXPECT_NE(QuicUtils::CreateReplacementConnectionId(connection_id_i, 255), + QuicUtils::CreateReplacementConnectionId(connection_id_j, 255)); } } } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.cc index 0a014bebe78..90c49823d99 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.cc @@ -15,18 +15,20 @@ namespace quic { QuicVersionManager::QuicVersionManager( ParsedQuicVersionVector supported_versions) - : enable_version_draft_27_( - GetQuicReloadableFlag(quic_enable_version_draft_27)), - enable_version_draft_25_( - GetQuicReloadableFlag(quic_enable_version_draft_25_v3)), + : enable_version_draft_29_( + GetQuicReloadableFlag(quic_enable_version_draft_29)), + disable_version_draft_27_( + GetQuicReloadableFlag(quic_disable_version_draft_27)), + disable_version_draft_25_( + GetQuicReloadableFlag(quic_disable_version_draft_25)), disable_version_q050_(GetQuicReloadableFlag(quic_disable_version_q050)), - enable_version_t050_(GetQuicReloadableFlag(quic_enable_version_t050_v2)), + disable_version_t050_(GetQuicReloadableFlag(quic_disable_version_t050)), disable_version_q049_(GetQuicReloadableFlag(quic_disable_version_q049)), disable_version_q048_(GetQuicReloadableFlag(quic_disable_version_q048)), disable_version_q046_(GetQuicReloadableFlag(quic_disable_version_q046)), disable_version_q043_(GetQuicReloadableFlag(quic_disable_version_q043)), allowed_supported_versions_(std::move(supported_versions)) { - static_assert(SupportedVersions().size() == 8u, + static_assert(SupportedVersions().size() == 9u, "Supported versions out of sync"); RefilterSupportedVersions(); } @@ -56,16 +58,18 @@ const std::vector<std::string>& QuicVersionManager::GetSupportedAlpns() { } void QuicVersionManager::MaybeRefilterSupportedVersions() { - static_assert(SupportedVersions().size() == 8u, + static_assert(SupportedVersions().size() == 9u, "Supported versions out of sync"); - if (enable_version_draft_27_ != - GetQuicReloadableFlag(quic_enable_version_draft_27) || - enable_version_draft_25_ != - GetQuicReloadableFlag(quic_enable_version_draft_25_v3) || + if (enable_version_draft_29_ != + GetQuicReloadableFlag(quic_enable_version_draft_29) || + disable_version_draft_27_ != + GetQuicReloadableFlag(quic_disable_version_draft_27) || + disable_version_draft_25_ != + GetQuicReloadableFlag(quic_disable_version_draft_25) || disable_version_q050_ != GetQuicReloadableFlag(quic_disable_version_q050) || - enable_version_t050_ != - GetQuicReloadableFlag(quic_enable_version_t050_v2) || + disable_version_t050_ != + GetQuicReloadableFlag(quic_disable_version_t050) || disable_version_q049_ != GetQuicReloadableFlag(quic_disable_version_q049) || disable_version_q048_ != @@ -74,12 +78,14 @@ void QuicVersionManager::MaybeRefilterSupportedVersions() { GetQuicReloadableFlag(quic_disable_version_q046) || disable_version_q043_ != GetQuicReloadableFlag(quic_disable_version_q043)) { - enable_version_draft_27_ = - GetQuicReloadableFlag(quic_enable_version_draft_27); - enable_version_draft_25_ = - GetQuicReloadableFlag(quic_enable_version_draft_25_v3); + enable_version_draft_29_ = + GetQuicReloadableFlag(quic_enable_version_draft_29); + disable_version_draft_27_ = + GetQuicReloadableFlag(quic_disable_version_draft_27); + disable_version_draft_25_ = + GetQuicReloadableFlag(quic_disable_version_draft_25); disable_version_q050_ = GetQuicReloadableFlag(quic_disable_version_q050); - enable_version_t050_ = GetQuicReloadableFlag(quic_enable_version_t050_v2); + disable_version_t050_ = GetQuicReloadableFlag(quic_disable_version_t050); disable_version_q049_ = GetQuicReloadableFlag(quic_disable_version_q049); disable_version_q048_ = GetQuicReloadableFlag(quic_disable_version_q048); disable_version_q046_ = GetQuicReloadableFlag(quic_disable_version_q046); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.h index c5111edf260..e6bb8f6b6bd 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.h @@ -52,14 +52,16 @@ class QUIC_EXPORT_PRIVATE QuicVersionManager { private: // Cached value of reloadable flags. - // quic_enable_version_draft_27 flag - bool enable_version_draft_27_; - // quic_enable_version_draft_25_v3 flag - bool enable_version_draft_25_; + // quic_enable_version_draft_29 flag + bool enable_version_draft_29_; + // quic_disable_version_draft_27 flag + bool disable_version_draft_27_; + // quic_disable_version_draft_25 flag + bool disable_version_draft_25_; // quic_disable_version_q050 flag bool disable_version_q050_; - // quic_enable_version_t050_v2 flag - bool enable_version_t050_; + // quic_disable_version_t050 flag + bool disable_version_t050_; // quic_disable_version_q049 flag bool disable_version_q049_; // quic_disable_version_q048 flag diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager_test.cc index 3a8e98ff520..4687b791fd9 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager_test.cc @@ -18,11 +18,12 @@ namespace { class QuicVersionManagerTest : public QuicTest {}; TEST_F(QuicVersionManagerTest, QuicVersionManager) { - static_assert(SupportedVersions().size() == 8u, + static_assert(SupportedVersions().size() == 9u, "Supported versions out of sync"); - SetQuicReloadableFlag(quic_enable_version_draft_27, false); - SetQuicReloadableFlag(quic_enable_version_draft_25_v3, false); - SetQuicReloadableFlag(quic_enable_version_t050_v2, false); + SetQuicReloadableFlag(quic_enable_version_draft_29, false); + SetQuicReloadableFlag(quic_disable_version_draft_27, true); + SetQuicReloadableFlag(quic_disable_version_draft_25, true); + SetQuicReloadableFlag(quic_disable_version_t050, false); SetQuicReloadableFlag(quic_disable_version_q050, false); SetQuicReloadableFlag(quic_disable_version_q049, false); SetQuicReloadableFlag(quic_disable_version_q048, false); @@ -32,6 +33,8 @@ TEST_F(QuicVersionManagerTest, QuicVersionManager) { ParsedQuicVersionVector expected_parsed_versions; expected_parsed_versions.push_back( + ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_50)); + expected_parsed_versions.push_back( ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50)); expected_parsed_versions.push_back( ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_49)); @@ -43,59 +46,56 @@ TEST_F(QuicVersionManagerTest, QuicVersionManager) { ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43)); EXPECT_EQ(expected_parsed_versions, manager.GetSupportedVersions()); - EXPECT_EQ(expected_parsed_versions, - manager.GetSupportedVersionsWithQuicCrypto()); EXPECT_EQ(FilterSupportedVersions(AllSupportedVersions()), manager.GetSupportedVersions()); EXPECT_EQ(CurrentSupportedVersionsWithQuicCrypto(), manager.GetSupportedVersionsWithQuicCrypto()); - EXPECT_THAT( - manager.GetSupportedAlpns(), - ElementsAre("h3-Q050", "h3-Q049", "h3-Q048", "h3-Q046", "h3-Q043")); + EXPECT_THAT(manager.GetSupportedAlpns(), + ElementsAre("h3-T050", "h3-Q050", "h3-Q049", "h3-Q048", "h3-Q046", + "h3-Q043")); - SetQuicReloadableFlag(quic_enable_version_draft_27, true); - expected_parsed_versions.insert( - expected_parsed_versions.begin(), - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_27)); + SetQuicReloadableFlag(quic_enable_version_draft_29, true); + expected_parsed_versions.insert(expected_parsed_versions.begin(), + ParsedQuicVersion::Draft29()); EXPECT_EQ(expected_parsed_versions, manager.GetSupportedVersions()); - EXPECT_EQ(expected_parsed_versions.size() - 1, + EXPECT_EQ(expected_parsed_versions.size() - 2, manager.GetSupportedVersionsWithQuicCrypto().size()); EXPECT_EQ(FilterSupportedVersions(AllSupportedVersions()), manager.GetSupportedVersions()); EXPECT_EQ(CurrentSupportedVersionsWithQuicCrypto(), manager.GetSupportedVersionsWithQuicCrypto()); EXPECT_THAT(manager.GetSupportedAlpns(), - ElementsAre("h3-27", "h3-Q050", "h3-Q049", "h3-Q048", "h3-Q046", - "h3-Q043")); + ElementsAre("h3-29", "h3-T050", "h3-Q050", "h3-Q049", "h3-Q048", + "h3-Q046", "h3-Q043")); - SetQuicReloadableFlag(quic_enable_version_draft_25_v3, true); + SetQuicReloadableFlag(quic_disable_version_draft_27, false); expected_parsed_versions.insert( expected_parsed_versions.begin() + 1, - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_25)); + ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_27)); EXPECT_EQ(expected_parsed_versions, manager.GetSupportedVersions()); - EXPECT_EQ(expected_parsed_versions.size() - 2, + EXPECT_EQ(expected_parsed_versions.size() - 3, manager.GetSupportedVersionsWithQuicCrypto().size()); + EXPECT_EQ(FilterSupportedVersions(AllSupportedVersions()), + manager.GetSupportedVersions()); EXPECT_EQ(CurrentSupportedVersionsWithQuicCrypto(), manager.GetSupportedVersionsWithQuicCrypto()); EXPECT_THAT(manager.GetSupportedAlpns(), - ElementsAre("h3-27", "h3-25", "h3-Q050", "h3-Q049", "h3-Q048", - "h3-Q046", "h3-Q043")); + ElementsAre("h3-29", "h3-27", "h3-T050", "h3-Q050", "h3-Q049", + "h3-Q048", "h3-Q046", "h3-Q043")); - SetQuicReloadableFlag(quic_enable_version_t050_v2, true); + SetQuicReloadableFlag(quic_disable_version_draft_25, false); expected_parsed_versions.insert( expected_parsed_versions.begin() + 2, - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_50)); + ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_25)); EXPECT_EQ(expected_parsed_versions, manager.GetSupportedVersions()); - EXPECT_EQ(expected_parsed_versions.size() - 3, + EXPECT_EQ(expected_parsed_versions.size() - 4, manager.GetSupportedVersionsWithQuicCrypto().size()); - EXPECT_EQ(FilterSupportedVersions(AllSupportedVersions()), - manager.GetSupportedVersions()); EXPECT_EQ(CurrentSupportedVersionsWithQuicCrypto(), manager.GetSupportedVersionsWithQuicCrypto()); EXPECT_THAT(manager.GetSupportedAlpns(), - ElementsAre("h3-27", "h3-25", "h3-T050", "h3-Q050", "h3-Q049", - "h3-Q048", "h3-Q046", "h3-Q043")); + ElementsAre("h3-29", "h3-27", "h3-25", "h3-T050", "h3-Q050", + "h3-Q049", "h3-Q048", "h3-Q046", "h3-Q043")); } } // namespace 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 bf93e8383d2..461a4882e2b 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 @@ -39,6 +39,35 @@ QuicVersionLabel CreateRandomVersionLabelForNegotiation() { return result; } +void SetVersionFlag(const ParsedQuicVersion& version, bool should_enable) { + static_assert(SupportedVersions().size() == 9u, + "Supported versions out of sync"); + const bool enable = should_enable; + const bool disable = !should_enable; + if (version == ParsedQuicVersion::Draft29()) { + SetQuicReloadableFlag(quic_enable_version_draft_29, enable); + } else if (version == ParsedQuicVersion::Draft27()) { + SetQuicReloadableFlag(quic_disable_version_draft_27, disable); + } else if (version == ParsedQuicVersion::Draft25()) { + SetQuicReloadableFlag(quic_disable_version_draft_25, disable); + } else if (version == ParsedQuicVersion::T050()) { + SetQuicReloadableFlag(quic_disable_version_t050, disable); + } else if (version == ParsedQuicVersion::Q050()) { + SetQuicReloadableFlag(quic_disable_version_q050, disable); + } else if (version == ParsedQuicVersion::Q049()) { + SetQuicReloadableFlag(quic_disable_version_q049, disable); + } else if (version == ParsedQuicVersion::Q048()) { + SetQuicReloadableFlag(quic_disable_version_q048, disable); + } else if (version == ParsedQuicVersion::Q046()) { + SetQuicReloadableFlag(quic_disable_version_q046, disable); + } else if (version == ParsedQuicVersion::Q043()) { + SetQuicReloadableFlag(quic_disable_version_q043, disable); + } else { + QUIC_BUG << "Cannot " << (should_enable ? "en" : "dis") << "able version " + << version; + } +} + } // namespace bool ParsedQuicVersion::IsKnown() const { @@ -158,6 +187,11 @@ bool ParsedQuicVersion::HasVarIntTransportParams() const { return transport_version >= QUIC_VERSION_IETF_DRAFT_27; } +bool ParsedQuicVersion::AuthenticatesHandshakeConnectionIds() const { + DCHECK(IsKnown()); + return transport_version > QUIC_VERSION_IETF_DRAFT_27; +} + bool ParsedQuicVersion::UsesTls() const { DCHECK(IsKnown()); return handshake_protocol == PROTOCOL_TLS1_3; @@ -211,7 +245,7 @@ QuicVersionLabel CreateQuicVersionLabel(ParsedQuicVersion parsed_version) { << parsed_version.handshake_protocol; return 0; } - static_assert(SupportedVersions().size() == 8u, + static_assert(SupportedVersions().size() == 9u, "Supported versions out of sync"); switch (parsed_version.transport_version) { case QUIC_VERSION_43: @@ -236,6 +270,12 @@ QuicVersionLabel CreateQuicVersionLabel(ParsedQuicVersion parsed_version) { } QUIC_BUG << "QUIC_VERSION_IETF_DRAFT_27 requires TLS"; return 0; + case QUIC_VERSION_IETF_DRAFT_29: + if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3) { + return MakeVersionLabel(0xff, 0x00, 0x00, 29); + } + QUIC_BUG << "QUIC_VERSION_IETF_DRAFT_29 requires TLS"; + return 0; case QUIC_VERSION_RESERVED_FOR_NEGOTIATION: return CreateRandomVersionLabelForNegotiation(); default: @@ -391,15 +431,20 @@ ParsedQuicVersionVector FilterSupportedVersions( ParsedQuicVersionVector versions) { ParsedQuicVersionVector filtered_versions; filtered_versions.reserve(versions.size()); - for (ParsedQuicVersion version : versions) { - if (version.transport_version == QUIC_VERSION_IETF_DRAFT_27) { + for (const ParsedQuicVersion& version : versions) { + if (version.transport_version == QUIC_VERSION_IETF_DRAFT_29) { + QUIC_BUG_IF(version.handshake_protocol != PROTOCOL_TLS1_3); + if (GetQuicReloadableFlag(quic_enable_version_draft_29)) { + filtered_versions.push_back(version); + } + } else if (version.transport_version == QUIC_VERSION_IETF_DRAFT_27) { QUIC_BUG_IF(version.handshake_protocol != PROTOCOL_TLS1_3); - if (GetQuicReloadableFlag(quic_enable_version_draft_27)) { + if (!GetQuicReloadableFlag(quic_disable_version_draft_27)) { filtered_versions.push_back(version); } } else if (version.transport_version == QUIC_VERSION_IETF_DRAFT_25) { QUIC_BUG_IF(version.handshake_protocol != PROTOCOL_TLS1_3); - if (GetQuicReloadableFlag(quic_enable_version_draft_25_v3)) { + if (!GetQuicReloadableFlag(quic_disable_version_draft_25)) { filtered_versions.push_back(version); } } else if (version.transport_version == QUIC_VERSION_50) { @@ -408,7 +453,7 @@ ParsedQuicVersionVector FilterSupportedVersions( filtered_versions.push_back(version); } } else { - if (GetQuicReloadableFlag(quic_enable_version_t050_v2)) { + if (!GetQuicReloadableFlag(quic_disable_version_t050)) { filtered_versions.push_back(version); } } @@ -518,7 +563,7 @@ HandshakeProtocol QuicVersionLabelToHandshakeProtocol( return #x std::string QuicVersionToString(QuicTransportVersion transport_version) { - static_assert(SupportedTransportVersions().size() == 7u, + static_assert(SupportedTransportVersions().size() == 8u, "Supported versions out of sync"); switch (transport_version) { RETURN_STRING_LITERAL(QUIC_VERSION_43); @@ -528,6 +573,7 @@ std::string QuicVersionToString(QuicTransportVersion transport_version) { RETURN_STRING_LITERAL(QUIC_VERSION_50); RETURN_STRING_LITERAL(QUIC_VERSION_IETF_DRAFT_25); RETURN_STRING_LITERAL(QUIC_VERSION_IETF_DRAFT_27); + RETURN_STRING_LITERAL(QUIC_VERSION_IETF_DRAFT_29); RETURN_STRING_LITERAL(QUIC_VERSION_UNSUPPORTED); RETURN_STRING_LITERAL(QUIC_VERSION_RESERVED_FOR_NEGOTIATION); } @@ -621,21 +667,25 @@ bool QuicVersionLabelUses4BitConnectionIdLength( } ParsedQuicVersion UnsupportedQuicVersion() { - return ParsedQuicVersion(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED); + return ParsedQuicVersion::Unsupported(); } ParsedQuicVersion QuicVersionReservedForNegotiation() { - return ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, - QUIC_VERSION_RESERVED_FOR_NEGOTIATION); + return ParsedQuicVersion::ReservedForNegotiation(); +} + +ParsedQuicVersion LegacyVersionForEncapsulation() { + return ParsedQuicVersion::Q043(); } std::string AlpnForVersion(ParsedQuicVersion parsed_version) { if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3) { - if (parsed_version.transport_version == QUIC_VERSION_IETF_DRAFT_25) { - return "h3-25"; - } - if (parsed_version.transport_version == QUIC_VERSION_IETF_DRAFT_27) { + if (parsed_version.transport_version == QUIC_VERSION_IETF_DRAFT_29) { + return "h3-29"; + } else if (parsed_version.transport_version == QUIC_VERSION_IETF_DRAFT_27) { return "h3-27"; + } else if (parsed_version.transport_version == QUIC_VERSION_IETF_DRAFT_25) { + return "h3-25"; } } return "h3-" + ParsedQuicVersionToString(parsed_version); @@ -643,32 +693,21 @@ std::string AlpnForVersion(ParsedQuicVersion parsed_version) { void QuicVersionInitializeSupportForIetfDraft() { // Enable necessary flags. + SetQuicReloadableFlag(quic_enable_tls_resumption, true); + SetQuicReloadableFlag(quic_enable_zero_rtt_for_tls, true); } -void QuicEnableVersion(ParsedQuicVersion parsed_version) { - static_assert(SupportedVersions().size() == 8u, - "Supported versions out of sync"); - if (parsed_version.transport_version == QUIC_VERSION_IETF_DRAFT_27) { - QUIC_BUG_IF(parsed_version.handshake_protocol != PROTOCOL_TLS1_3); - SetQuicReloadableFlag(quic_enable_version_draft_27, true); - } else if (parsed_version.transport_version == QUIC_VERSION_IETF_DRAFT_25) { - QUIC_BUG_IF(parsed_version.handshake_protocol != PROTOCOL_TLS1_3); - SetQuicReloadableFlag(quic_enable_version_draft_25_v3, true); - } else if (parsed_version.transport_version == QUIC_VERSION_50) { - if (parsed_version.handshake_protocol == PROTOCOL_QUIC_CRYPTO) { - SetQuicReloadableFlag(quic_disable_version_q050, false); - } else { - SetQuicReloadableFlag(quic_enable_version_t050_v2, true); - } - } else if (parsed_version.transport_version == QUIC_VERSION_49) { - SetQuicReloadableFlag(quic_disable_version_q049, false); - } else if (parsed_version.transport_version == QUIC_VERSION_48) { - SetQuicReloadableFlag(quic_disable_version_q048, false); - } else if (parsed_version.transport_version == QUIC_VERSION_46) { - SetQuicReloadableFlag(quic_disable_version_q046, false); - } else if (parsed_version.transport_version == QUIC_VERSION_43) { - SetQuicReloadableFlag(quic_disable_version_q043, false); - } +void QuicEnableVersion(const ParsedQuicVersion& version) { + SetVersionFlag(version, /*should_enable=*/true); +} + +void QuicDisableVersion(const ParsedQuicVersion& version) { + SetVersionFlag(version, /*should_enable=*/false); +} + +bool QuicVersionIsEnabled(const ParsedQuicVersion& version) { + ParsedQuicVersionVector current = CurrentSupportedVersions(); + return std::find(current.begin(), current.end(), version) != current.end(); } #undef RETURN_STRING_LITERAL // undef for jumbo builds diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_versions.h b/chromium/net/third_party/quiche/src/quic/core/quic_versions.h index 5f3a9689198..341d39896cb 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_versions.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_versions.h @@ -4,13 +4,21 @@ // Definitions and utility functions related to handling of QUIC versions. // -// QUIC version is a four-byte tag that can be represented in memory as a -// QuicVersionLabel type (which is an alias to uint32_t). In actuality, all -// versions supported by this implementation have the following format: -// [QT]0\d\d -// e.g. Q046. Q or T distinguishes the type of handshake used (Q for QUIC -// Crypto handshake, T for TLS-based handshake), and the two digits at the end -// is the actual numeric value of transport version used by the code. +// QUIC versions are encoded over the wire as an opaque 32bit field. The wire +// encoding is represented in memory as a QuicVersionLabel type (which is an +// alias to uint32_t). Conceptual versions are represented in memory as +// ParsedQuicVersion. +// +// We currently support two kinds of QUIC versions, GoogleQUIC and IETF QUIC. +// +// All GoogleQUIC versions use a wire encoding that matches the following regex +// when converted to ASCII: "[QT]0\d\d" (e.g. Q050). Q or T distinguishes the +// type of handshake used (Q for the QUIC_CRYPTO handshake, T for the QUIC+TLS +// handshake), and the two digits at the end contain the numeric value of +// the transport version used. +// +// All IETF QUIC versions use the wire encoding described in: +// https://tools.ietf.org/html/draft-ietf-quic-transport #ifndef QUICHE_QUIC_CORE_QUIC_VERSIONS_H_ #define QUICHE_QUIC_CORE_QUIC_VERSIONS_H_ @@ -25,11 +33,12 @@ namespace quic { -// The available versions of QUIC. The numeric value of the enum is guaranteed -// to match the number in the name. The versions not currently supported are -// documented in comments. -// -// See go/new-quic-version for more details on how to roll out new versions. +// The list of existing QUIC transport versions. Note that QUIC versions are +// sent over the wire as an encoding of ParsedQuicVersion, which requires a +// QUIC transport version and handshake protocol. For transport versions of the +// form QUIC_VERSION_XX where XX is decimal, the enum numeric value is +// guaranteed to match the name. Older deprecated transport versions are +// documented in comments below. enum QuicTransportVersion { // Special case to indicate unknown/unsupported QUIC version. QUIC_VERSION_UNSUPPORTED = 0, @@ -110,6 +119,8 @@ enum QuicTransportVersion { QUIC_VERSION_50 = 50, // Header protection and initial obfuscators. QUIC_VERSION_IETF_DRAFT_25 = 70, // draft-ietf-quic-transport-25. QUIC_VERSION_IETF_DRAFT_27 = 71, // draft-ietf-quic-transport-27. + // Number 72 used to represent draft-ietf-quic-transport-28. + QUIC_VERSION_IETF_DRAFT_29 = 73, // draft-ietf-quic-transport-29. // Version 99 was a dumping ground for IETF QUIC changes which were not yet // yet ready for production between 2018-02 and 2020-02. @@ -123,13 +134,10 @@ enum QuicTransportVersion { }; // This array contains QUIC transport versions which we currently support. -// This should be ordered such that the highest supported version is the first -// element, with subsequent elements in descending order (versions can be -// skipped as necessary). -// -// See go/new-quic-version for more details on how to roll out new versions. -constexpr std::array<QuicTransportVersion, 7> SupportedTransportVersions() { - return {QUIC_VERSION_IETF_DRAFT_27, +// DEPRECATED. Use SupportedVersions() instead. +constexpr std::array<QuicTransportVersion, 8> SupportedTransportVersions() { + return {QUIC_VERSION_IETF_DRAFT_29, + QUIC_VERSION_IETF_DRAFT_27, QUIC_VERSION_IETF_DRAFT_25, QUIC_VERSION_50, QUIC_VERSION_49, @@ -191,7 +199,8 @@ QUIC_EXPORT_PRIVATE constexpr bool ParsedQuicVersionIsValid( case PROTOCOL_QUIC_CRYPTO: return transport_version != QUIC_VERSION_UNSUPPORTED && transport_version != QUIC_VERSION_IETF_DRAFT_25 && - transport_version != QUIC_VERSION_IETF_DRAFT_27; + transport_version != QUIC_VERSION_IETF_DRAFT_27 && + transport_version != QUIC_VERSION_IETF_DRAFT_29; case PROTOCOL_TLS1_3: // The TLS handshake is only deployable if CRYPTO frames are also used. // We explicitly removed support for T048 and T049 to reduce test load. @@ -242,6 +251,51 @@ struct QUIC_EXPORT_PRIVATE ParsedQuicVersion { transport_version != other.transport_version; } + static constexpr ParsedQuicVersion Draft29() { + return ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_29); + } + + static constexpr ParsedQuicVersion Draft27() { + return ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_27); + } + + static constexpr ParsedQuicVersion Draft25() { + return ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_25); + } + + static constexpr ParsedQuicVersion T050() { + return ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_50); + } + + static constexpr ParsedQuicVersion Q050() { + return ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50); + } + + static constexpr ParsedQuicVersion Q049() { + return ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_49); + } + + static constexpr ParsedQuicVersion Q048() { + return ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_48); + } + + static constexpr ParsedQuicVersion Q046() { + return ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46); + } + + static constexpr ParsedQuicVersion Q043() { + return ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43); + } + + static constexpr ParsedQuicVersion Unsupported() { + return ParsedQuicVersion(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED); + } + + static constexpr ParsedQuicVersion ReservedForNegotiation() { + return ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, + QUIC_VERSION_RESERVED_FOR_NEGOTIATION); + } + // Returns whether our codebase understands this version. This should only be // called on valid versions, see ParsedQuicVersionIsValid. Assuming the // version is valid, IsKnown returns whether the version is not @@ -331,6 +385,10 @@ struct QUIC_EXPORT_PRIVATE ParsedQuicVersion { // encoding transport parameter types and lengths. bool HasVarIntTransportParams() const; + // Returns true if this version uses transport parameters to authenticate all + // the connection IDs used during the handshake. + bool AuthenticatesHandshakeConnectionIds() const; + // Returns whether this version uses PROTOCOL_TLS1_3. bool UsesTls() const; @@ -342,6 +400,10 @@ QUIC_EXPORT_PRIVATE ParsedQuicVersion UnsupportedQuicVersion(); QUIC_EXPORT_PRIVATE ParsedQuicVersion QuicVersionReservedForNegotiation(); +// Outer version used when encapsulating other packets using the Legacy Version +// Encapsulation feature. +QUIC_EXPORT_PRIVATE ParsedQuicVersion LegacyVersionForEncapsulation(); + QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, const ParsedQuicVersion& version); @@ -365,16 +427,13 @@ constexpr std::array<HandshakeProtocol, 2> SupportedHandshakeProtocols() { return {PROTOCOL_TLS1_3, PROTOCOL_QUIC_CRYPTO}; } -constexpr std::array<ParsedQuicVersion, 8> SupportedVersions() { +constexpr std::array<ParsedQuicVersion, 9> SupportedVersions() { return { - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_27), - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_25), - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_50), - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50), - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_49), - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_48), - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46), - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43), + ParsedQuicVersion::Draft29(), ParsedQuicVersion::Draft27(), + ParsedQuicVersion::Draft25(), ParsedQuicVersion::T050(), + ParsedQuicVersion::Q050(), ParsedQuicVersion::Q049(), + ParsedQuicVersion::Q048(), ParsedQuicVersion::Q046(), + ParsedQuicVersion::Q043(), }; } @@ -604,8 +663,14 @@ QUIC_EXPORT_PRIVATE std::string AlpnForVersion( // correct flags. QUIC_EXPORT_PRIVATE void QuicVersionInitializeSupportForIetfDraft(); -// Enables the flags required to support this version of QUIC. -QUIC_EXPORT_PRIVATE void QuicEnableVersion(ParsedQuicVersion parsed_version); +// Configures the flags required to enable support for this version of QUIC. +QUIC_EXPORT_PRIVATE void QuicEnableVersion(const ParsedQuicVersion& version); + +// Configures the flags required to disable support for this version of QUIC. +QUIC_EXPORT_PRIVATE void QuicDisableVersion(const ParsedQuicVersion& version); + +// Returns whether support for this version of QUIC is currently enabled. +QUIC_EXPORT_PRIVATE bool QuicVersionIsEnabled(const ParsedQuicVersion& version); } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_versions_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_versions_test.cc index 3a07221ab94..c7ebb82a4ed 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_versions_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_versions_test.cc @@ -84,10 +84,8 @@ TEST_F(QuicVersionsTest, KnownAndValid) { } TEST_F(QuicVersionsTest, Features) { - ParsedQuicVersion parsed_version_q043 = - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43); - ParsedQuicVersion parsed_version_draft_27 = - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_27); + ParsedQuicVersion parsed_version_q043 = ParsedQuicVersion::Q043(); + ParsedQuicVersion parsed_version_draft_27 = ParsedQuicVersion::Draft27(); EXPECT_TRUE(parsed_version_q043.IsKnown()); EXPECT_FALSE(parsed_version_q043.KnowsWhichDecrypterToUse()); @@ -208,35 +206,28 @@ TEST_F(QuicVersionsTest, QuicVersionLabelToHandshakeProtocol) { } TEST_F(QuicVersionsTest, ParseQuicVersionLabel) { - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43), + EXPECT_EQ(ParsedQuicVersion::Q043(), ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '3'))); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46), + EXPECT_EQ(ParsedQuicVersion::Q046(), ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '6'))); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_48), + EXPECT_EQ(ParsedQuicVersion::Q048(), ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '8'))); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50), + EXPECT_EQ(ParsedQuicVersion::Q050(), ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '5', '0'))); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_50), + EXPECT_EQ(ParsedQuicVersion::T050(), ParseQuicVersionLabel(MakeVersionLabel('T', '0', '5', '0'))); } TEST_F(QuicVersionsTest, ParseQuicVersionString) { - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43), - ParseQuicVersionString("Q043")); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46), + EXPECT_EQ(ParsedQuicVersion::Q043(), ParseQuicVersionString("Q043")); + EXPECT_EQ(ParsedQuicVersion::Q046(), ParseQuicVersionString("QUIC_VERSION_46")); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46), - ParseQuicVersionString("46")); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46), - ParseQuicVersionString("Q046")); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_48), - ParseQuicVersionString("Q048")); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50), - ParseQuicVersionString("Q050")); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50), - ParseQuicVersionString("50")); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50), - ParseQuicVersionString("h3-Q050")); + EXPECT_EQ(ParsedQuicVersion::Q046(), ParseQuicVersionString("46")); + EXPECT_EQ(ParsedQuicVersion::Q046(), ParseQuicVersionString("Q046")); + EXPECT_EQ(ParsedQuicVersion::Q048(), ParseQuicVersionString("Q048")); + EXPECT_EQ(ParsedQuicVersion::Q050(), ParseQuicVersionString("Q050")); + EXPECT_EQ(ParsedQuicVersion::Q050(), ParseQuicVersionString("50")); + EXPECT_EQ(ParsedQuicVersion::Q050(), ParseQuicVersionString("h3-Q050")); EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("")); EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("Q 46")); @@ -244,18 +235,14 @@ TEST_F(QuicVersionsTest, ParseQuicVersionString) { EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("99")); EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("70")); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_50), - ParseQuicVersionString("T050")); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_50), - ParseQuicVersionString("h3-T050")); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_27), - ParseQuicVersionString("ff00001b")); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_27), - ParseQuicVersionString("h3-27")); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_25), - ParseQuicVersionString("ff000019")); - EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_25), - ParseQuicVersionString("h3-25")); + EXPECT_EQ(ParsedQuicVersion::T050(), ParseQuicVersionString("T050")); + EXPECT_EQ(ParsedQuicVersion::T050(), ParseQuicVersionString("h3-T050")); + EXPECT_EQ(ParsedQuicVersion::Draft29(), ParseQuicVersionString("ff00001d")); + EXPECT_EQ(ParsedQuicVersion::Draft29(), ParseQuicVersionString("h3-29")); + EXPECT_EQ(ParsedQuicVersion::Draft27(), ParseQuicVersionString("ff00001b")); + EXPECT_EQ(ParsedQuicVersion::Draft27(), ParseQuicVersionString("h3-27")); + EXPECT_EQ(ParsedQuicVersion::Draft25(), ParseQuicVersionString("ff000019")); + EXPECT_EQ(ParsedQuicVersion::Draft25(), ParseQuicVersionString("h3-25")); } TEST_F(QuicVersionsTest, ParseQuicVersionVectorString) { @@ -266,6 +253,7 @@ TEST_F(QuicVersionsTest, ParseQuicVersionVectorString) { QUIC_VERSION_IETF_DRAFT_25); ParsedQuicVersion version_draft_27(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_27); + ParsedQuicVersion version_draft_29 = ParsedQuicVersion::Draft29(); EXPECT_THAT(ParseQuicVersionVectorString(""), IsEmpty()); @@ -286,6 +274,8 @@ TEST_F(QuicVersionsTest, ParseQuicVersionVectorString) { ElementsAre(version_draft_25, version_draft_27)); EXPECT_THAT(ParseQuicVersionVectorString("h3-27,h3-25"), ElementsAre(version_draft_27, version_draft_25)); + EXPECT_THAT(ParseQuicVersionVectorString("h3-29,h3-27"), + ElementsAre(version_draft_29, version_draft_27)); EXPECT_THAT(ParseQuicVersionVectorString("h3-27,50"), ElementsAre(version_draft_27, version_q050)); @@ -332,22 +322,17 @@ TEST_F(QuicVersionsTest, ParseQuicVersionVectorString) { TEST_F(QuicVersionsTest, CreateQuicVersionLabel) { EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '3'), - CreateQuicVersionLabel( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43))); + CreateQuicVersionLabel(ParsedQuicVersion::Q043())); EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '6'), - CreateQuicVersionLabel( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46))); + CreateQuicVersionLabel(ParsedQuicVersion::Q046())); EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '8'), - CreateQuicVersionLabel( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_48))); + CreateQuicVersionLabel(ParsedQuicVersion::Q048())); EXPECT_EQ(MakeVersionLabel('Q', '0', '5', '0'), - CreateQuicVersionLabel( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50))); + CreateQuicVersionLabel(ParsedQuicVersion::Q050())); // Test a TLS version: EXPECT_EQ(MakeVersionLabel('T', '0', '5', '0'), - CreateQuicVersionLabel( - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_50))); + CreateQuicVersionLabel(ParsedQuicVersion::T050())); // Make sure the negotiation reserved version is in the IETF reserved space. EXPECT_EQ(MakeVersionLabel(0xda, 0x5a, 0x3a, 0x3a) & 0x0f0f0f0f, @@ -442,97 +427,29 @@ TEST_F(QuicVersionsTest, ParsedQuicVersionToString) { } TEST_F(QuicVersionsTest, FilterSupportedVersionsAllVersions) { - static_assert(SupportedVersions().size() == 8u, - "Supported versions out of sync"); - SetQuicReloadableFlag(quic_enable_version_draft_27, true); - SetQuicReloadableFlag(quic_enable_version_draft_25_v3, true); - SetQuicReloadableFlag(quic_enable_version_t050_v2, true); - SetQuicReloadableFlag(quic_disable_version_q050, false); - SetQuicReloadableFlag(quic_disable_version_q049, false); - SetQuicReloadableFlag(quic_disable_version_q048, false); - SetQuicReloadableFlag(quic_disable_version_q046, false); - SetQuicReloadableFlag(quic_disable_version_q043, false); - - ParsedQuicVersionVector expected_parsed_versions; - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_27)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_25)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_50)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_49)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_48)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43)); - - ASSERT_EQ(expected_parsed_versions, - FilterSupportedVersions(AllSupportedVersions())); - ASSERT_EQ(expected_parsed_versions, AllSupportedVersions()); -} - -TEST_F(QuicVersionsTest, FilterSupportedVersionsNo99) { - static_assert(SupportedVersions().size() == 8u, - "Supported versions out of sync"); - SetQuicReloadableFlag(quic_enable_version_draft_27, false); - SetQuicReloadableFlag(quic_enable_version_draft_25_v3, true); - SetQuicReloadableFlag(quic_enable_version_t050_v2, true); - SetQuicReloadableFlag(quic_disable_version_q050, false); - SetQuicReloadableFlag(quic_disable_version_q049, false); - SetQuicReloadableFlag(quic_disable_version_q048, false); - SetQuicReloadableFlag(quic_disable_version_q046, false); - SetQuicReloadableFlag(quic_disable_version_q043, false); - + for (const ParsedQuicVersion& version : AllSupportedVersions()) { + QuicEnableVersion(version); + } ParsedQuicVersionVector expected_parsed_versions; - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_25)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_50)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_49)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_48)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43)); - - ASSERT_EQ(expected_parsed_versions, + for (const ParsedQuicVersion& version : SupportedVersions()) { + expected_parsed_versions.push_back(version); + } + EXPECT_EQ(expected_parsed_versions, FilterSupportedVersions(AllSupportedVersions())); + EXPECT_EQ(expected_parsed_versions, AllSupportedVersions()); } -TEST_F(QuicVersionsTest, FilterSupportedVersionsNoFlags) { - static_assert(SupportedVersions().size() == 8u, - "Supported versions out of sync"); - SetQuicReloadableFlag(quic_enable_version_draft_27, false); - SetQuicReloadableFlag(quic_enable_version_draft_25_v3, false); - SetQuicReloadableFlag(quic_enable_version_t050_v2, false); - SetQuicReloadableFlag(quic_disable_version_q050, false); - SetQuicReloadableFlag(quic_disable_version_q049, false); - SetQuicReloadableFlag(quic_disable_version_q048, false); - SetQuicReloadableFlag(quic_disable_version_q046, false); - SetQuicReloadableFlag(quic_disable_version_q043, false); - +TEST_F(QuicVersionsTest, FilterSupportedVersionsWithoutFirstVersion) { + for (const ParsedQuicVersion& version : AllSupportedVersions()) { + QuicEnableVersion(version); + } + QuicDisableVersion(AllSupportedVersions().front()); ParsedQuicVersionVector expected_parsed_versions; - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_49)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_48)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46)); - expected_parsed_versions.push_back( - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43)); - - ASSERT_EQ(expected_parsed_versions, + for (const ParsedQuicVersion& version : SupportedVersions()) { + expected_parsed_versions.push_back(version); + } + expected_parsed_versions.erase(expected_parsed_versions.begin()); + EXPECT_EQ(expected_parsed_versions, FilterSupportedVersions(AllSupportedVersions())); } @@ -540,10 +457,11 @@ TEST_F(QuicVersionsTest, LookUpVersionByIndex) { QuicTransportVersionVector all_versions = {QUIC_VERSION_43}; int version_count = all_versions.size(); for (int i = -5; i <= version_count + 1; ++i) { + QuicTransportVersionVector index = VersionOfIndex(all_versions, i); if (i >= 0 && i < version_count) { - EXPECT_EQ(all_versions[i], VersionOfIndex(all_versions, i)[0]); + EXPECT_EQ(all_versions[i], index[0]); } else { - EXPECT_EQ(QUIC_VERSION_UNSUPPORTED, VersionOfIndex(all_versions, i)[0]); + EXPECT_EQ(QUIC_VERSION_UNSUPPORTED, index[0]); } } } @@ -552,11 +470,11 @@ TEST_F(QuicVersionsTest, LookUpParsedVersionByIndex) { ParsedQuicVersionVector all_versions = AllSupportedVersions(); int version_count = all_versions.size(); for (int i = -5; i <= version_count + 1; ++i) { + ParsedQuicVersionVector index = ParsedVersionOfIndex(all_versions, i); if (i >= 0 && i < version_count) { - EXPECT_EQ(all_versions[i], ParsedVersionOfIndex(all_versions, i)[0]); + EXPECT_EQ(all_versions[i], index[0]); } else { - EXPECT_EQ(UnsupportedQuicVersion(), - ParsedVersionOfIndex(all_versions, i)[0]); + EXPECT_EQ(UnsupportedQuicVersion(), index[0]); } } } @@ -575,7 +493,7 @@ TEST_F(QuicVersionsTest, ParsedVersionsToTransportVersions) { // yet a typo was made in doing the #defines and it was caught // only in some test far removed from here... Better safe than sorry. TEST_F(QuicVersionsTest, CheckTransportVersionNumbersForTypos) { - static_assert(SupportedTransportVersions().size() == 7u, + static_assert(SupportedTransportVersions().size() == 8u, "Supported versions out of sync"); EXPECT_EQ(QUIC_VERSION_43, 43); EXPECT_EQ(QUIC_VERSION_46, 46); @@ -584,23 +502,18 @@ TEST_F(QuicVersionsTest, CheckTransportVersionNumbersForTypos) { EXPECT_EQ(QUIC_VERSION_50, 50); EXPECT_EQ(QUIC_VERSION_IETF_DRAFT_25, 70); EXPECT_EQ(QUIC_VERSION_IETF_DRAFT_27, 71); + EXPECT_EQ(QUIC_VERSION_IETF_DRAFT_29, 73); } TEST_F(QuicVersionsTest, AlpnForVersion) { - static_assert(SupportedVersions().size() == 8u, + static_assert(SupportedVersions().size() == 9u, "Supported versions out of sync"); - ParsedQuicVersion parsed_version_q048 = - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_48); - ParsedQuicVersion parsed_version_q049 = - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_49); - ParsedQuicVersion parsed_version_q050 = - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50); - ParsedQuicVersion parsed_version_t050 = - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_50); - ParsedQuicVersion parsed_version_draft_25 = - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_25); - ParsedQuicVersion parsed_version_draft_27 = - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_27); + ParsedQuicVersion parsed_version_q048 = ParsedQuicVersion::Q048(); + ParsedQuicVersion parsed_version_q049 = ParsedQuicVersion::Q049(); + ParsedQuicVersion parsed_version_q050 = ParsedQuicVersion::Q050(); + ParsedQuicVersion parsed_version_t050 = ParsedQuicVersion::T050(); + ParsedQuicVersion parsed_version_draft_25 = ParsedQuicVersion::Draft25(); + ParsedQuicVersion parsed_version_draft_27 = ParsedQuicVersion::Draft27(); EXPECT_EQ("h3-Q048", AlpnForVersion(parsed_version_q048)); EXPECT_EQ("h3-Q049", AlpnForVersion(parsed_version_q049)); @@ -608,54 +521,16 @@ TEST_F(QuicVersionsTest, AlpnForVersion) { EXPECT_EQ("h3-T050", AlpnForVersion(parsed_version_t050)); EXPECT_EQ("h3-25", AlpnForVersion(parsed_version_draft_25)); EXPECT_EQ("h3-27", AlpnForVersion(parsed_version_draft_27)); + EXPECT_EQ("h3-29", AlpnForVersion(ParsedQuicVersion::Draft29())); } -TEST_F(QuicVersionsTest, QuicEnableVersion) { - static_assert(SupportedVersions().size() == 8u, - "Supported versions out of sync"); - ParsedQuicVersion parsed_version_draft_27 = - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_27); - ParsedQuicVersion parsed_version_draft_25 = - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_25); - ParsedQuicVersion parsed_version_q050 = - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50); - ParsedQuicVersion parsed_version_t050 = - ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_50); - - { - QuicFlagSaver flag_saver; - SetQuicReloadableFlag(quic_enable_version_draft_27, false); - QuicEnableVersion(parsed_version_draft_27); - EXPECT_TRUE(GetQuicReloadableFlag(quic_enable_version_draft_27)); - } - - { - QuicFlagSaver flag_saver; - SetQuicReloadableFlag(quic_enable_version_draft_25_v3, false); - QuicEnableVersion(parsed_version_draft_25); - EXPECT_TRUE(GetQuicReloadableFlag(quic_enable_version_draft_25_v3)); - } - - { - QuicFlagSaver flag_saver; - SetQuicReloadableFlag(quic_disable_version_q050, true); - QuicEnableVersion(parsed_version_q050); - EXPECT_FALSE(GetQuicReloadableFlag(quic_disable_version_q050)); - } - - { - QuicFlagSaver flag_saver; - SetQuicReloadableFlag(quic_enable_version_t050_v2, false); - QuicEnableVersion(parsed_version_t050); - EXPECT_TRUE(GetQuicReloadableFlag(quic_enable_version_t050_v2)); - } - - { +TEST_F(QuicVersionsTest, QuicVersionEnabling) { + for (const ParsedQuicVersion& version : AllSupportedVersions()) { QuicFlagSaver flag_saver; - for (const ParsedQuicVersion& version : SupportedVersions()) { - QuicEnableVersion(version); - } - ASSERT_EQ(AllSupportedVersions(), CurrentSupportedVersions()); + QuicDisableVersion(version); + EXPECT_FALSE(QuicVersionIsEnabled(version)); + QuicEnableVersion(version); + EXPECT_TRUE(QuicVersionIsEnabled(version)); } } @@ -676,7 +551,8 @@ TEST_F(QuicVersionsTest, SupportedVersionsHasCorrectList) { SupportedTransportVersions()) { SCOPED_TRACE(index); if (ParsedQuicVersionIsValid(handshake_protocol, transport_version)) { - EXPECT_EQ(SupportedVersions()[index], + ParsedQuicVersion version = SupportedVersions()[index]; + EXPECT_EQ(version, ParsedQuicVersion(handshake_protocol, transport_version)); index++; } diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_chlo_extractor.h b/chromium/net/third_party/quiche/src/quic/core/tls_chlo_extractor.h index 1762566d70a..b50d2e236ab 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_chlo_extractor.h +++ b/chromium/net/third_party/quiche/src/quic/core/tls_chlo_extractor.h @@ -9,6 +9,7 @@ #include <string> #include <vector> #include "third_party/boringssl/src/include/openssl/ssl.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_ack_frequency_frame.h" #include "net/third_party/quiche/src/quic/core/quic_framer.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h" @@ -151,6 +152,9 @@ class QUIC_NO_EXPORT TlsChloExtractor bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& /*frame*/) override { return true; } + bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& /*frame*/) override { + return true; + } void OnPacketComplete() override {} bool IsValidStatelessResetToken(QuicUint128 /*token*/) const override { return true; diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_chlo_extractor_test.cc b/chromium/net/third_party/quiche/src/quic/core/tls_chlo_extractor_test.cc index ba57ad02bb9..955e58c0ee6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_chlo_extractor_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/tls_chlo_extractor_test.cc @@ -48,8 +48,9 @@ class TlsChloExtractorTest : public QuicTestWithParam<ParsedQuicVersion> { void ValidateChloDetails() { EXPECT_TRUE(tls_chlo_extractor_.HasParsedFullChlo()); - ASSERT_EQ(tls_chlo_extractor_.alpns().size(), 1u); - EXPECT_EQ(tls_chlo_extractor_.alpns()[0], AlpnForVersion(version_)); + std::vector<std::string> alpns = tls_chlo_extractor_.alpns(); + ASSERT_EQ(alpns.size(), 1u); + EXPECT_EQ(alpns[0], AlpnForVersion(version_)); EXPECT_EQ(tls_chlo_extractor_.server_name(), TestHostname()); } 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 d4e8ed023b1..23a74e4fc28 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 @@ -12,6 +12,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h" #include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.h" #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" @@ -67,6 +68,7 @@ TlsClientHandshaker::TlsClientHandshaker( pre_shared_key_(crypto_config->pre_shared_key()), crypto_negotiated_params_(new QuicCryptoNegotiatedParameters), has_application_state_(has_application_state), + attempting_zero_rtt_(crypto_config->early_data_enabled_for_tls()), tls_connection_(crypto_config->ssl_ctx(), this) {} TlsClientHandshaker::~TlsClientHandshaker() { @@ -114,17 +116,16 @@ bool TlsClientHandshaker::CryptoConnect() { } // Set a session to resume, if there is one. + std::unique_ptr<QuicResumptionState> cached_state; if (session_cache_) { - std::unique_ptr<QuicResumptionState> cached_state = - session_cache_->Lookup(server_id_, SSL_get_SSL_CTX(ssl())); - if (cached_state) { - SSL_set_session(ssl(), cached_state->tls_session.get()); - if (GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls) && - VersionHasIetfQuicFrames(session()->transport_version()) && - SSL_SESSION_early_data_capable(cached_state->tls_session.get())) { - if (!PrepareZeroRttConfig(cached_state.get())) { - return false; - } + cached_state = session_cache_->Lookup(server_id_, SSL_get_SSL_CTX(ssl())); + } + if (cached_state) { + SSL_set_session(ssl(), cached_state->tls_session.get()); + if (attempting_zero_rtt_ && + SSL_SESSION_early_data_capable(cached_state->tls_session.get())) { + if (!PrepareZeroRttConfig(cached_state.get())) { + return false; } } } @@ -137,8 +138,8 @@ bool TlsClientHandshaker::CryptoConnect() { bool TlsClientHandshaker::PrepareZeroRttConfig( QuicResumptionState* cached_state) { std::string error_details; - if (session()->config()->ProcessTransportParameters( - *(cached_state->transport_params), SERVER, + if (handshaker_delegate()->ProcessTransportParameters( + *(cached_state->transport_params), /*is_resumption = */ true, &error_details) != QUIC_NO_ERROR) { QUIC_BUG << "Unable to parse cached transport parameters."; CloseConnection(QUIC_HANDSHAKE_FAILED, @@ -148,7 +149,7 @@ bool TlsClientHandshaker::PrepareZeroRttConfig( session()->OnConfigNegotiated(); if (has_application_state_) { - if (!session()->SetApplicationState(cached_state->application_state)) { + if (!session()->ResumeApplicationState(cached_state->application_state)) { QUIC_BUG << "Unable to parse cached application state."; CloseConnection(QUIC_HANDSHAKE_FAILED, "Client failed to parse cached application state."); @@ -204,7 +205,7 @@ bool TlsClientHandshaker::SetTransportParameters() { params.version = CreateQuicVersionLabel(session()->supported_versions().front()); - if (!session()->config()->FillTransportParameters(¶ms)) { + if (!handshaker_delegate()->FillTransportParameters(¶ms)) { return false; } if (GetQuicRestartFlag(quic_google_transport_param_send_new)) { @@ -216,6 +217,9 @@ bool TlsClientHandshaker::SetTransportParameters() { params.google_quic_params->SetStringPiece(kUAID, user_agent_id_); } + // Notify QuicConnectionDebugVisitor. + session()->connection()->OnTransportParametersSent(params); + std::vector<uint8_t> param_bytes; return SerializeTransportParameters(session()->connection()->version(), params, ¶m_bytes) && @@ -244,6 +248,10 @@ bool TlsClientHandshaker::ProcessTransportParameters( return false; } + // Notify QuicConnectionDebugVisitor. + session()->connection()->OnTransportParametersReceived( + *received_transport_params_); + // When interoperating with non-Google implementations that do not send // the version extension, set it to what we expect. if (received_transport_params_->version == 0) { @@ -264,8 +272,8 @@ bool TlsClientHandshaker::ProcessTransportParameters( received_transport_params_->supported_versions, session()->connection()->server_supported_versions(), error_details) != QUIC_NO_ERROR || - session()->config()->ProcessTransportParameters( - *received_transport_params_, SERVER, /* is_resumption = */ false, + handshaker_delegate()->ProcessTransportParameters( + *received_transport_params_, /* is_resumption = */ false, error_details) != QUIC_NO_ERROR) { DCHECK(!error_details->empty()); return false; @@ -378,9 +386,12 @@ void TlsClientHandshaker::SetWriteSecret( if (state_ == STATE_CONNECTION_CLOSED) { return; } - if (level == ENCRYPTION_FORWARD_SECURE) { + if (level == ENCRYPTION_FORWARD_SECURE || level == ENCRYPTION_ZERO_RTT) { encryption_established_ = true; } + if (level == ENCRYPTION_FORWARD_SECURE) { + handshaker_delegate()->DiscardOldEncryptionKey(ENCRYPTION_ZERO_RTT); + } TlsHandshaker::SetWriteSecret(level, cipher, write_secret); } @@ -421,6 +432,10 @@ void TlsClientHandshaker::AdvanceHandshake() { } int ssl_error = SSL_get_error(ssl(), rv); bool should_close = true; + if (ssl_error == SSL_ERROR_EARLY_DATA_REJECTED) { + HandleZeroRttReject(); + return; + } switch (state_) { // TODO(b/153726130): handle the case where the server rejects early data. case STATE_HANDSHAKE_RUNNING: @@ -449,6 +464,15 @@ void TlsClientHandshaker::CloseConnection(QuicErrorCode error, } void TlsClientHandshaker::FinishHandshake() { + if (SSL_in_early_data(ssl())) { + // SSL_do_handshake returns after sending the ClientHello if the session is + // 0-RTT-capable, which means that FinishHandshake will get called twice - + // the first time after sending the ClientHello, and the second time after + // the handshake is complete. If we're in the first time FinishHandshake is + // called, we can't do any end-of-handshake processing, so we return early + // from this function. + return; + } QUIC_LOG(INFO) << "Client: handshake finished"; state_ = STATE_HANDSHAKE_COMPLETE; // Fill crypto_negotiated_params_: @@ -498,6 +522,17 @@ void TlsClientHandshaker::FinishHandshake() { handshaker_delegate()->OnOneRttKeysAvailable(); } +void TlsClientHandshaker::HandleZeroRttReject() { + QUIC_LOG(INFO) << "0-RTT handshake attempted but was rejected by the server"; + DCHECK(session_cache_); + // Disable encrytion to block outgoing data until 1-RTT keys are available. + encryption_established_ = false; + handshaker_delegate()->OnZeroRttRejected(); + SSL_reset_early_data_reject(ssl()); + session_cache_->ClearEarlyData(server_id_); + AdvanceHandshake(); +} + enum ssl_verify_result_t TlsClientHandshaker::VerifyCert(uint8_t* out_alert) { if (verify_result_ != ssl_verify_retry || state_ == STATE_CERT_VERIFY_PENDING) { @@ -584,7 +619,7 @@ void TlsClientHandshaker::WriteMessage(EncryptionLevel level, TlsHandshaker::WriteMessage(level, data); } -void TlsClientHandshaker::OnApplicationState( +void TlsClientHandshaker::SetServerApplicationStateForResumption( std::unique_ptr<ApplicationState> application_state) { DCHECK_EQ(STATE_HANDSHAKE_COMPLETE, state_); received_application_state_ = std::move(application_state); 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 cc601219dfa..573c055c48c 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 @@ -71,7 +71,7 @@ class QUIC_EXPORT_PRIVATE TlsClientHandshaker void WriteMessage(EncryptionLevel level, quiche::QuicheStringPiece data) override; - void OnApplicationState( + void SetServerApplicationStateForResumption( std::unique_ptr<ApplicationState> application_state) override; void AllowEmptyAlpnForTests() { allow_empty_alpn_for_tests_ = true; } @@ -124,6 +124,7 @@ class QUIC_EXPORT_PRIVATE TlsClientHandshaker bool SetTransportParameters(); bool ProcessTransportParameters(std::string* error_details); void FinishHandshake(); + void HandleZeroRttReject(); // Called when server completes handshake (i.e., either handshake done is // received or 1-RTT packet gets acknowledged). @@ -175,6 +176,7 @@ class QUIC_EXPORT_PRIVATE TlsClientHandshaker bool allow_invalid_sni_for_tests_ = false; const bool has_application_state_; + bool attempting_zero_rtt_; TlsClientConnection tls_connection_; 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 68c413fbb63..47989a75f5e 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 @@ -8,13 +8,18 @@ #include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_server_id.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" #include "net/third_party/quiche/src/quic/test_tools/simple_session_cache.h" @@ -167,13 +172,13 @@ class TlsClientHandshakerTest : public QuicTestWithParam<ParsedQuicVersion> { TlsClientHandshakerTest() : supported_versions_({GetParam()}), server_id_(kServerHostname, kServerPort, false), - crypto_config_(std::make_unique<QuicCryptoClientConfig>( - std::make_unique<TestProofVerifier>(), - std::make_unique<test::SimpleSessionCache>())), server_compressed_certs_cache_( QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) { SetQuicReloadableFlag(quic_enable_tls_resumption, true); SetQuicReloadableFlag(quic_enable_zero_rtt_for_tls, true); + crypto_config_ = std::make_unique<QuicCryptoClientConfig>( + std::make_unique<TestProofVerifier>(), + std::make_unique<test::SimpleSessionCache>()); server_crypto_config_ = crypto_test_utils::CryptoServerConfigForTesting(); CreateConnection(); } @@ -342,6 +347,72 @@ TEST_P(TlsClientHandshakerTest, Resumption) { EXPECT_TRUE(stream()->IsResumption()); } +TEST_P(TlsClientHandshakerTest, ZeroRttResumption) { + // Finish establishing the first connection: + CompleteCryptoHandshake(); + + EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); + EXPECT_TRUE(stream()->encryption_established()); + EXPECT_TRUE(stream()->one_rtt_keys_available()); + EXPECT_FALSE(stream()->IsResumption()); + + // Create a second connection + CreateConnection(); + CompleteCryptoHandshake(); + + // TODO(b/152551499): Add a test that checks we have keys after calling + // stream()->CryptoConnect(). + EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); + EXPECT_TRUE(stream()->encryption_established()); + EXPECT_TRUE(stream()->one_rtt_keys_available()); + EXPECT_TRUE(stream()->IsResumption()); + EXPECT_TRUE(stream()->EarlyDataAccepted()); +} + +// TODO(b/152551499): Also test resumption getting rejected. +TEST_P(TlsClientHandshakerTest, ZeroRttRejection) { + // Finish establishing the first connection: + CompleteCryptoHandshake(); + + EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); + EXPECT_TRUE(stream()->encryption_established()); + EXPECT_TRUE(stream()->one_rtt_keys_available()); + EXPECT_FALSE(stream()->IsResumption()); + + // Create a second connection, but disable 0-RTT on the server. + SSL_CTX_set_early_data_enabled(server_crypto_config_->ssl_ctx(), false); + CreateConnection(); + + // 4 packets will be sent in this connection: initial handshake packet, 0-RTT + // packet containing SETTINGS, handshake packet upon 0-RTT rejection, 0-RTT + // packet retransmission. + EXPECT_CALL(*connection_, + OnPacketSent(ENCRYPTION_INITIAL, NOT_RETRANSMISSION)); + if (VersionUsesHttp3(session_->transport_version())) { + EXPECT_CALL(*connection_, + OnPacketSent(ENCRYPTION_ZERO_RTT, NOT_RETRANSMISSION)); + } + EXPECT_CALL(*connection_, + OnPacketSent(ENCRYPTION_HANDSHAKE, NOT_RETRANSMISSION)); + if (VersionUsesHttp3(session_->transport_version())) { + // TODO(b/158027651): change transmission type to + // ALL_ZERO_RTT_RETRANSMISSION. + EXPECT_CALL(*connection_, + OnPacketSent(ENCRYPTION_FORWARD_SECURE, LOSS_RETRANSMISSION)); + } + + CompleteCryptoHandshake(); + + QuicFramer* framer = QuicConnectionPeer::GetFramer(connection_); + EXPECT_EQ(nullptr, QuicFramerPeer::GetEncrypter(framer, ENCRYPTION_ZERO_RTT)); + + EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); + EXPECT_TRUE(stream()->encryption_established()); + EXPECT_TRUE(stream()->one_rtt_keys_available()); + EXPECT_TRUE(stream()->IsResumption()); + EXPECT_FALSE(stream()->EarlyDataAccepted()); +} + TEST_P(TlsClientHandshakerTest, ClientSendsNoSNI) { // Reconfigure client to sent an empty server hostname. The crypto config also // needs to be recreated to use a FakeProofVerifier since the server's cert @@ -423,7 +494,6 @@ TEST_P(TlsClientHandshakerTest, BadTransportParams) { if (!connection_->version().UsesHttp3()) { return; } - SetQuicReloadableFlag(quic_notify_handshaker_on_connection_close, true); // Finish establishing the first connection: CompleteCryptoHandshake(); @@ -438,7 +508,8 @@ TEST_P(TlsClientHandshakerTest, BadTransportParams) { config.SetMaxBidirectionalStreamsToSend( config.GetMaxBidirectionalStreamsToSend() - 1); - EXPECT_CALL(*connection_, CloseConnection(QUIC_MAX_STREAMS_ERROR, _, _)) + EXPECT_CALL(*connection_, + CloseConnection(QUIC_ZERO_RTT_REJECTION_LIMIT_REDUCED, _, _)) .WillOnce(testing::Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); // Close connection will be called again in the handshaker, but this will be diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker_test.cc b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker_test.cc index 5a2bd6400aa..caf9a9c6b1b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker_test.cc @@ -192,6 +192,8 @@ class TestQuicCryptoStream : public QuicCryptoStream { HandshakeState GetHandshakeState() const override { return handshaker()->GetHandshakeState(); } + void SetServerApplicationStateForResumption( + std::unique_ptr<ApplicationState> /*application_state*/) override {} const std::vector<std::pair<std::string, EncryptionLevel>>& pending_writes() { return pending_writes_; 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 5bd5b3d642d..69b523723d8 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 @@ -13,7 +13,9 @@ #include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h" #include "net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_arraysize.h" #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h" namespace quic { @@ -123,8 +125,15 @@ void TlsServerHandshaker::SendServerConfigUpdate( } bool TlsServerHandshaker::IsZeroRtt() const { - // TODO(nharper): Support 0-RTT with TLS 1.3 in QUIC. - return false; + return SSL_early_data_accepted(ssl()); +} + +bool TlsServerHandshaker::IsResumption() const { + return SSL_session_reused(ssl()); +} + +bool TlsServerHandshaker::ResumptionAttempted() const { + return ticket_received_; } int TlsServerHandshaker::NumServerConfigUpdateMessagesSent() const { @@ -137,11 +146,6 @@ TlsServerHandshaker::PreviousCachedNetworkParams() const { return nullptr; } -bool TlsServerHandshaker::ZeroRttAttempted() const { - // TODO(nharper): Support 0-RTT with TLS 1.3 in QUIC. - return false; -} - void TlsServerHandshaker::SetPreviousCachedNetworkParams( CachedNetworkParameters /*cached_network_params*/) {} @@ -194,6 +198,11 @@ HandshakeState TlsServerHandshaker::GetHandshakeState() const { return HANDSHAKE_START; } +void TlsServerHandshaker::SetServerApplicationStateForResumption( + std::unique_ptr<ApplicationState> state) { + application_state_ = std::move(state); +} + size_t TlsServerHandshaker::BufferSizeLimitForLevel( EncryptionLevel level) const { return TlsHandshaker::BufferSizeLimitForLevel(level); @@ -201,19 +210,6 @@ size_t TlsServerHandshaker::BufferSizeLimitForLevel( void TlsServerHandshaker::OverrideQuicConfigDefaults(QuicConfig* /*config*/) {} -bool TlsServerHandshaker::SetReadSecret( - EncryptionLevel level, - const SSL_CIPHER* cipher, - const std::vector<uint8_t>& read_secret) { - if (level != ENCRYPTION_FORWARD_SECURE || one_rtt_keys_available_) { - return TlsHandshaker::SetReadSecret(level, cipher, read_secret); - } - // Delay setting read secret for ENCRYPTION_FORWARD_SECURE until handshake - // completes. - app_data_read_secret_ = read_secret; - return true; -} - void TlsServerHandshaker::AdvanceHandshake() { if (state_ == STATE_CONNECTION_CLOSED) { QUIC_LOG(INFO) << "TlsServerHandshaker received handshake message after " @@ -248,8 +244,8 @@ void TlsServerHandshaker::AdvanceHandshake() { should_close = true; } if (should_close && state_ != STATE_CONNECTION_CLOSED) { - QUIC_LOG(WARNING) << "SSL_do_handshake failed; SSL_get_error returns " - << ssl_error << ", state_ = " << state_; + QUIC_VLOG(1) << "SSL_do_handshake failed; SSL_get_error returns " + << ssl_error << ", state_ = " << state_; ERR_print_errors_fp(stderr); CloseConnection(QUIC_HANDSHAKE_FAILED, "Server observed TLS handshake failure"); @@ -284,6 +280,9 @@ bool TlsServerHandshaker::ProcessTransportParameters( return false; } + // Notify QuicConnectionDebugVisitor. + session()->connection()->OnTransportParametersReceived(client_params); + // When interoperating with non-Google implementations that do not send // the version extension, set it to what we expect. if (client_params.version == 0) { @@ -294,12 +293,26 @@ bool TlsServerHandshaker::ProcessTransportParameters( if (CryptoUtils::ValidateClientHelloVersion( client_params.version, session()->connection()->version(), session()->supported_versions(), error_details) != QUIC_NO_ERROR || - session()->config()->ProcessTransportParameters( - client_params, CLIENT, /* is_resumption = */ false, error_details) != + handshaker_delegate()->ProcessTransportParameters( + client_params, /* is_resumption = */ false, error_details) != QUIC_NO_ERROR) { return false; } ProcessAdditionalTransportParameters(client_params); + if (GetQuicReloadableFlag(quic_save_user_agent_in_quic_session) && + !session()->user_agent_id().has_value()) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_save_user_agent_in_quic_session, 2, 3); + + if (client_params.user_agent_id.has_value()) { + session()->SetUserAgentId(client_params.user_agent_id.value()); + } else if (client_params.google_quic_params) { + quiche::QuicheStringPiece user_agent_id; + client_params.google_quic_params->GetStringPiece(kUAID, &user_agent_id); + if (!user_agent_id.empty()) { + session()->SetUserAgentId(user_agent_id.data()); + } + } + } return true; } @@ -312,12 +325,13 @@ bool TlsServerHandshaker::SetTransportParameters() { server_params.version = CreateQuicVersionLabel(session()->connection()->version()); - if (!session()->config()->FillTransportParameters(&server_params)) { + if (!handshaker_delegate()->FillTransportParameters(&server_params)) { return false; } - // TODO(nharper): Provide an actual value for the stateless reset token. - server_params.stateless_reset_token.resize(16); + // Notify QuicConnectionDebugVisitor. + session()->connection()->OnTransportParametersSent(server_params); + std::vector<uint8_t> server_params_bytes; if (!SerializeTransportParameters(session()->connection()->version(), server_params, &server_params_bytes) || @@ -325,6 +339,17 @@ bool TlsServerHandshaker::SetTransportParameters() { server_params_bytes.size()) != 1) { return false; } + if (application_state_) { + std::vector<uint8_t> early_data_context; + if (!SerializeTransportParametersForTicket( + server_params, *application_state_, &early_data_context)) { + QUIC_BUG << "Failed to serialize Transport Parameters for ticket."; + return false; + } + SSL_set_quic_early_data_context(ssl(), early_data_context.data(), + early_data_context.size()); + application_state_.reset(nullptr); + } return true; } @@ -332,8 +357,7 @@ void TlsServerHandshaker::SetWriteSecret( EncryptionLevel level, const SSL_CIPHER* cipher, const std::vector<uint8_t>& write_secret) { - if (GetQuicReloadableFlag(quic_notify_handshaker_on_connection_close) && - state_ == STATE_CONNECTION_CLOSED) { + if (state_ == STATE_CONNECTION_CLOSED) { return; } if (level == ENCRYPTION_FORWARD_SECURE) { @@ -349,6 +373,16 @@ void TlsServerHandshaker::SetWriteSecret( } void TlsServerHandshaker::FinishHandshake() { + if (SSL_in_early_data(ssl())) { + // If the server accepts early data, SSL_do_handshake returns success twice: + // once after processing the ClientHello and sending the server's first + // flight, and then again after the handshake is complete. This results in + // FinishHandshake getting called twice. On the first call to + // FinishHandshake, we don't have any confirmation that the client is live, + // so all end of handshake processing is deferred until the handshake is + // actually complete. + return; + } if (!valid_alpn_received_) { QUIC_DLOG(ERROR) << "Server: handshake finished without receiving a known ALPN"; @@ -363,21 +397,10 @@ void TlsServerHandshaker::FinishHandshake() { state_ = STATE_HANDSHAKE_COMPLETE; one_rtt_keys_available_ = true; - const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl()); - - if (!app_data_read_secret_.empty()) { - if (!SetReadSecret(ENCRYPTION_FORWARD_SECURE, cipher, - app_data_read_secret_)) { - QUIC_BUG << "Failed to set forward secure read key."; - CloseConnection(QUIC_HANDSHAKE_FAILED, "Failed to set app data read key"); - return; - } - app_data_read_secret_.clear(); - } - handshaker_delegate()->OnOneRttKeysAvailable(); handshaker_delegate()->DiscardOldEncryptionKey(ENCRYPTION_HANDSHAKE); handshaker_delegate()->DiscardOldDecryptionKey(ENCRYPTION_HANDSHAKE); + handshaker_delegate()->DiscardOldDecryptionKey(ENCRYPTION_ZERO_RTT); } ssl_private_key_result_t TlsServerHandshaker::PrivateKeySign( @@ -446,6 +469,7 @@ ssl_ticket_aead_result_t TlsServerHandshaker::SessionTicketOpen( DCHECK(proof_source_->GetTicketCrypter()); if (!ticket_decryption_callback_) { + ticket_received_ = true; ticket_decryption_callback_ = new DecryptCallback(this); proof_source_->GetTicketCrypter()->Decrypt( in, std::unique_ptr<DecryptCallback>(ticket_decryption_callback_)); 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 c62dbbbe47c..13b734e3a78 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 @@ -40,9 +40,10 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker void SendServerConfigUpdate( const CachedNetworkParameters* cached_network_params) override; bool IsZeroRtt() const override; + bool IsResumption() const override; + bool ResumptionAttempted() const override; int NumServerConfigUpdateMessagesSent() const override; const CachedNetworkParameters* PreviousCachedNetworkParams() const override; - bool ZeroRttAttempted() const override; void SetPreviousCachedNetworkParams( CachedNetworkParameters cached_network_params) override; void OnPacketDecrypted(EncryptionLevel level) override; @@ -60,6 +61,8 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker const override; CryptoMessageParser* crypto_message_parser() override; HandshakeState GetHandshakeState() const override; + void SetServerApplicationStateForResumption( + std::unique_ptr<ApplicationState> state) override; size_t BufferSizeLimitForLevel(EncryptionLevel level) const override; void SetWriteSecret(EncryptionLevel level, const SSL_CIPHER* cipher, @@ -81,13 +84,6 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker virtual void ProcessAdditionalTransportParameters( const TransportParameters& /*params*/) {} - // Override of TlsHandshaker::SetReadSecret so that setting the read secret - // for ENCRYPTION_FORWARD_SECURE can be delayed until the handshake is - // complete. - bool SetReadSecret(EncryptionLevel level, - const SSL_CIPHER* cipher, - const std::vector<uint8_t>& read_secret) override; - // Called when a new message is received on the crypto stream and is available // for the TLS stack to read. void AdvanceHandshake() override; @@ -181,19 +177,21 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker // |decrypted_session_ticket_| contains the decrypted session ticket after the // callback has run but before it is passed to BoringSSL. std::vector<uint8_t> decrypted_session_ticket_; + // |ticket_received_| tracks whether we received a resumption ticket from the + // client. It does not matter whether we were able to decrypt said ticket or + // if we actually resumed a session with it - the presence of this ticket + // indicates that the client attempted a resumption. + bool ticket_received_ = false; std::string hostname_; std::string cert_verify_sig_; std::unique_ptr<ProofSource::Details> proof_source_details_; + std::unique_ptr<ApplicationState> application_state_; + // Pre-shared key used during the handshake. std::string pre_shared_key_; - // Used to hold the ENCRYPTION_FORWARD_SECURE read secret until the handshake - // is complete. This is temporary until - // https://bugs.chromium.org/p/boringssl/issues/detail?id=303 is resolved. - std::vector<uint8_t> app_data_read_secret_; - bool encryption_established_ = false; bool one_rtt_keys_available_ = false; bool valid_alpn_received_ = false; 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 a71338c5f5b..70550fcb21d 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 @@ -47,9 +47,12 @@ class TlsServerHandshakerTest : public QuicTest { TlsServerHandshakerTest() : server_compressed_certs_cache_( QuicCompressedCertsCache::kQuicCompressedCertsCacheSize), - server_id_(kServerHostname, kServerPort, false), - client_crypto_config_(crypto_test_utils::ProofVerifierForTesting(), - std::make_unique<test::SimpleSessionCache>()) { + server_id_(kServerHostname, kServerPort, false) { + SetQuicReloadableFlag(quic_enable_tls_resumption, true); + SetQuicReloadableFlag(quic_enable_zero_rtt_for_tls, true); + client_crypto_config_ = std::make_unique<QuicCryptoClientConfig>( + crypto_test_utils::ProofVerifierForTesting(), + std::make_unique<test::SimpleSessionCache>()); InitializeServerConfig(); InitializeServer(); InitializeFakeClient(); @@ -65,7 +68,6 @@ class TlsServerHandshakerTest : public QuicTest { } void InitializeServerConfig() { - SetQuicReloadableFlag(quic_enable_tls_resumption, true); auto ticket_crypter = std::make_unique<TestTicketCrypter>(); ticket_crypter_ = ticket_crypter.get(); auto proof_source = std::make_unique<FakeProofSource>(); @@ -126,7 +128,7 @@ class TlsServerHandshakerTest : public QuicTest { CreateClientSessionForTest( server_id_, QuicTime::Delta::FromSeconds(100000), supported_versions_, helpers_.back().get(), alarm_factories_.back().get(), - &client_crypto_config_, &client_connection_, &client_session); + client_crypto_config_.get(), &client_connection_, &client_session); const std::string default_alpn = AlpnForVersion(client_connection_->version()); ON_CALL(*client_session, GetAlpnsToOffer()) @@ -212,7 +214,7 @@ class TlsServerHandshakerTest : public QuicTest { // Client state. PacketSavingConnection* client_connection_; - QuicCryptoClientConfig client_crypto_config_; + std::unique_ptr<QuicCryptoClientConfig> client_crypto_config_; std::unique_ptr<TestQuicSpdyClientSession> client_session_; crypto_test_utils::FakeClientOptions client_options_; @@ -383,6 +385,8 @@ TEST_F(TlsServerHandshakerTest, Resumption) { CompleteCryptoHandshake(); ExpectHandshakeSuccessful(); EXPECT_FALSE(client_stream()->IsResumption()); + EXPECT_FALSE(server_stream()->IsResumption()); + EXPECT_FALSE(server_stream()->ResumptionAttempted()); // Now do another handshake InitializeServer(); @@ -390,6 +394,8 @@ TEST_F(TlsServerHandshakerTest, Resumption) { CompleteCryptoHandshake(); ExpectHandshakeSuccessful(); EXPECT_TRUE(client_stream()->IsResumption()); + EXPECT_TRUE(server_stream()->IsResumption()); + EXPECT_TRUE(server_stream()->ResumptionAttempted()); } TEST_F(TlsServerHandshakerTest, ResumptionWithAsyncDecryptCallback) { @@ -411,6 +417,8 @@ TEST_F(TlsServerHandshakerTest, ResumptionWithAsyncDecryptCallback) { CompleteCryptoHandshake(); ExpectHandshakeSuccessful(); EXPECT_TRUE(client_stream()->IsResumption()); + EXPECT_TRUE(server_stream()->IsResumption()); + EXPECT_TRUE(server_stream()->ResumptionAttempted()); } TEST_F(TlsServerHandshakerTest, ResumptionWithFailingDecryptCallback) { @@ -426,6 +434,8 @@ TEST_F(TlsServerHandshakerTest, ResumptionWithFailingDecryptCallback) { CompleteCryptoHandshake(); ExpectHandshakeSuccessful(); EXPECT_FALSE(client_stream()->IsResumption()); + EXPECT_FALSE(server_stream()->IsResumption()); + EXPECT_TRUE(server_stream()->ResumptionAttempted()); } TEST_F(TlsServerHandshakerTest, ResumptionWithFailingAsyncDecryptCallback) { @@ -448,6 +458,8 @@ TEST_F(TlsServerHandshakerTest, ResumptionWithFailingAsyncDecryptCallback) { CompleteCryptoHandshake(); ExpectHandshakeSuccessful(); EXPECT_FALSE(client_stream()->IsResumption()); + EXPECT_FALSE(server_stream()->IsResumption()); + EXPECT_TRUE(server_stream()->ResumptionAttempted()); } TEST_F(TlsServerHandshakerTest, HandshakeFailsWithFailingProofSource) { @@ -462,6 +474,53 @@ TEST_F(TlsServerHandshakerTest, HandshakeFailsWithFailingProofSource) { EXPECT_EQ(moved_messages_counts_.second, 0u); } +TEST_F(TlsServerHandshakerTest, ZeroRttResumption) { + std::vector<uint8_t> application_state = {0, 1, 2, 3}; + + // Do the first handshake + server_stream()->SetServerApplicationStateForResumption( + std::make_unique<ApplicationState>(application_state)); + InitializeFakeClient(); + CompleteCryptoHandshake(); + ExpectHandshakeSuccessful(); + EXPECT_FALSE(client_stream()->IsResumption()); + EXPECT_FALSE(server_stream()->IsZeroRtt()); + + // Now do another handshake + InitializeServer(); + server_stream()->SetServerApplicationStateForResumption( + std::make_unique<ApplicationState>(application_state)); + InitializeFakeClient(); + CompleteCryptoHandshake(); + ExpectHandshakeSuccessful(); + EXPECT_TRUE(client_stream()->IsResumption()); + EXPECT_TRUE(server_stream()->IsZeroRtt()); +} + +TEST_F(TlsServerHandshakerTest, ZeroRttRejectOnApplicationStateChange) { + std::vector<uint8_t> original_application_state = {1, 2}; + std::vector<uint8_t> new_application_state = {3, 4}; + + // Do the first handshake + server_stream()->SetServerApplicationStateForResumption( + std::make_unique<ApplicationState>(original_application_state)); + InitializeFakeClient(); + CompleteCryptoHandshake(); + ExpectHandshakeSuccessful(); + EXPECT_FALSE(client_stream()->IsResumption()); + EXPECT_FALSE(server_stream()->IsZeroRtt()); + + // Do another handshake, but change the application state + InitializeServer(); + server_stream()->SetServerApplicationStateForResumption( + std::make_unique<ApplicationState>(new_application_state)); + InitializeFakeClient(); + CompleteCryptoHandshake(); + ExpectHandshakeSuccessful(); + EXPECT_TRUE(client_stream()->IsResumption()); + EXPECT_FALSE(server_stream()->IsZeroRtt()); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.cc b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.cc index 6951b2cea3f..2f33594e042 100644 --- a/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.cc @@ -17,7 +17,8 @@ UberQuicStreamIdManager::UberQuicStreamIdManager( QuicStreamCount max_open_outgoing_unidirectional_streams, QuicStreamCount max_open_incoming_bidirectional_streams, QuicStreamCount max_open_incoming_unidirectional_streams) - : bidirectional_stream_id_manager_(delegate, + : version_(version), + bidirectional_stream_id_manager_(delegate, /*unidirectional=*/false, perspective, version, @@ -69,7 +70,7 @@ QuicStreamId UberQuicStreamIdManager::GetNextOutgoingUnidirectionalStreamId() { bool UberQuicStreamIdManager::MaybeIncreaseLargestPeerStreamId( QuicStreamId id, std::string* error_details) { - if (QuicUtils::IsBidirectionalStreamId(id)) { + if (QuicUtils::IsBidirectionalStreamId(id, version_)) { return bidirectional_stream_id_manager_.MaybeIncreaseLargestPeerStreamId( id, error_details); } @@ -78,7 +79,7 @@ bool UberQuicStreamIdManager::MaybeIncreaseLargestPeerStreamId( } void UberQuicStreamIdManager::OnStreamClosed(QuicStreamId id) { - if (QuicUtils::IsBidirectionalStreamId(id)) { + if (QuicUtils::IsBidirectionalStreamId(id, version_)) { bidirectional_stream_id_manager_.OnStreamClosed(id); return; } @@ -97,7 +98,7 @@ bool UberQuicStreamIdManager::OnStreamsBlockedFrame( } bool UberQuicStreamIdManager::IsAvailableStream(QuicStreamId id) const { - if (QuicUtils::IsBidirectionalStreamId(id)) { + if (QuicUtils::IsBidirectionalStreamId(id, version_)) { return bidirectional_stream_id_manager_.IsAvailableStream(id); } return unidirectional_stream_id_manager_.IsAvailableStream(id); @@ -162,4 +163,14 @@ UberQuicStreamIdManager::advertised_max_incoming_unidirectional_streams() return unidirectional_stream_id_manager_.incoming_advertised_max_streams(); } +QuicStreamCount UberQuicStreamIdManager::outgoing_bidirectional_stream_count() + const { + return bidirectional_stream_id_manager_.outgoing_stream_count(); +} + +QuicStreamCount UberQuicStreamIdManager::outgoing_unidirectional_stream_count() + const { + return unidirectional_stream_id_manager_.outgoing_stream_count(); +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h index b1fc1260987..0e03b42c8d6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h @@ -87,10 +87,14 @@ class QUIC_EXPORT_PRIVATE UberQuicStreamIdManager { QuicStreamCount advertised_max_incoming_bidirectional_streams() const; QuicStreamCount advertised_max_incoming_unidirectional_streams() const; + QuicStreamCount outgoing_bidirectional_stream_count() const; + QuicStreamCount outgoing_unidirectional_stream_count() const; + private: friend class test::QuicSessionPeer; friend class test::UberQuicStreamIdManagerPeer; + ParsedQuicVersion version_; // Manages stream IDs of bidirectional streams. QuicStreamIdManager bidirectional_stream_id_manager_; diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager_test.cc index 7cae0a6e1de..b3dfedcf71f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager_test.cc @@ -287,18 +287,32 @@ TEST_P(UberQuicStreamIdManagerTest, OnStreamsBlockedFrame) { QuicStreamsBlockedFrame frame(kInvalidControlFrameId, stream_count, /*unidirectional=*/false); - EXPECT_CALL(delegate_, - SendMaxStreams(manager_.max_incoming_bidirectional_streams(), - frame.unidirectional)); + if (GetQuicReloadableFlag(quic_stop_sending_duplicate_max_streams)) { + EXPECT_CALL(delegate_, + SendMaxStreams(manager_.max_incoming_bidirectional_streams(), + frame.unidirectional)) + .Times(0); + } else { + EXPECT_CALL(delegate_, + SendMaxStreams(manager_.max_incoming_bidirectional_streams(), + frame.unidirectional)); + } EXPECT_TRUE(manager_.OnStreamsBlockedFrame(frame, nullptr)); stream_count = manager_.advertised_max_incoming_unidirectional_streams() - 1; frame.stream_count = stream_count; frame.unidirectional = true; - EXPECT_CALL(delegate_, - SendMaxStreams(manager_.max_incoming_unidirectional_streams(), - frame.unidirectional)); + if (GetQuicReloadableFlag(quic_stop_sending_duplicate_max_streams)) { + EXPECT_CALL(delegate_, + SendMaxStreams(manager_.max_incoming_unidirectional_streams(), + frame.unidirectional)) + .Times(0); + } else { + EXPECT_CALL(delegate_, + SendMaxStreams(manager_.max_incoming_unidirectional_streams(), + frame.unidirectional)); + } EXPECT_TRUE(manager_.OnStreamsBlockedFrame(frame, nullptr)); } diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_encapsulated_epoll_client.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_encapsulated_epoll_client.cc index 7c9749aa374..1f3986284ac 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_encapsulated_epoll_client.cc +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_encapsulated_epoll_client.cc @@ -47,10 +47,10 @@ class MasquePacketWriter : public QuicPacketWriter { bool SupportsReleaseTime() const override { return false; } bool IsBatchMode() const override { return false; } - char* GetNextWriteLocation( + QuicPacketBuffer GetNextWriteLocation( const QuicIpAddress& /*self_address*/, const QuicSocketAddress& /*peer_address*/) override { - return nullptr; + return {nullptr, nullptr}; } WriteResult Flush() override { return WriteResult(WRITE_STATUS_OK, 0); } diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_session.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_session.cc index 52ba763118f..98d8a51faa7 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_session.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_session.cc @@ -94,8 +94,7 @@ void QboneClientSession::OnProofVerifyDetailsAvailable( const ProofVerifyDetails& verify_details) {} bool QboneClientSession::HasActiveRequests() const { - return (stream_map().size() - num_incoming_static_streams() - - num_outgoing_static_streams()) > 0; + return (stream_map().size() - num_static_streams()) > 0; } } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_test.cc index cdb611c9209..91be62a3c6c 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_test.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_test.cc @@ -139,13 +139,6 @@ class QuicQboneDispatcher : public QuicDispatcher { return session; } - QuicConnectionId GenerateNewServerConnectionId( - ParsedQuicVersion version, - QuicConnectionId connection_id) const override { - char connection_id_bytes[kQuicDefaultConnectionIdLength] = {}; - return QuicConnectionId(connection_id_bytes, sizeof(connection_id_bytes)); - } - private: QbonePacketWriter* writer_; }; @@ -248,7 +241,7 @@ TEST_P(QboneClientTest, SendDataFromClient) { crypto_test_utils::ProofVerifierForTesting()); ASSERT_TRUE(client.Initialize()); ASSERT_TRUE(client.Connect()); - ASSERT_TRUE(client.WaitForCryptoHandshakeConfirmed()); + ASSERT_TRUE(client.WaitForOneRttKeysAvailable()); client.SendData(TestPacketIn("hello")); client.SendData(TestPacketIn("world")); client.WaitForWriteToFlush(); diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream_test.cc index 10edaf6650d..976847de5f1 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream_test.cc @@ -131,9 +131,10 @@ class DummyPacketWriter : public QuicPacketWriter { bool IsBatchMode() const override { return false; } - char* GetNextWriteLocation(const QuicIpAddress& self_address, - const QuicSocketAddress& peer_address) override { - return nullptr; + QuicPacketBuffer GetNextWriteLocation( + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address) override { + return {nullptr, nullptr}; } WriteResult Flush() override { return WriteResult(WRITE_STATUS_OK, 0); } diff --git a/chromium/net/third_party/quiche/src/quic/quartc/counting_packet_filter.h b/chromium/net/third_party/quiche/src/quic/quartc/counting_packet_filter.h deleted file mode 100644 index 4c7c27041ba..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/counting_packet_filter.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2018 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_QUARTC_COUNTING_PACKET_FILTER_H_ -#define QUICHE_QUIC_QUARTC_COUNTING_PACKET_FILTER_H_ - -#include <string> - -#include "net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.h" -#include "net/third_party/quiche/src/quic/test_tools/simulator/port.h" -#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h" - -namespace quic { -namespace simulator { - -// Simple packet filter which drops the first N packets it observes. -class CountingPacketFilter : public simulator::PacketFilter { - public: - CountingPacketFilter(simulator::Simulator* simulator, - const std::string& name, - simulator::Endpoint* endpoint) - : PacketFilter(simulator, name, endpoint) {} - - void set_packets_to_drop(int count) { packets_to_drop_ = count; } - - protected: - bool FilterPacket(const simulator::Packet& /*packet*/) override { - if (packets_to_drop_ > 0) { - --packets_to_drop_; - return false; - } - return true; - } - - private: - int packets_to_drop_ = 0; -}; - -} // namespace simulator -} // namespace quic - -#endif // QUICHE_QUIC_QUARTC_COUNTING_PACKET_FILTER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.cc deleted file mode 100644 index da74858d69b..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h" - -namespace quic { - -QuartcConnectionHelper::QuartcConnectionHelper(const QuicClock* clock, - QuicRandom* random) - : clock_(clock), random_(random) {} - -const QuicClock* QuartcConnectionHelper::GetClock() const { - return clock_; -} - -QuicRandom* QuartcConnectionHelper::GetRandomGenerator() { - return random_; -} - -QuicBufferAllocator* QuartcConnectionHelper::GetStreamSendBufferAllocator() { - return &buffer_allocator_; -} - -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h deleted file mode 100644 index 72cc707aced..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2017 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_QUARTC_QUARTC_CONNECTION_HELPER_H_ -#define QUICHE_QUIC_QUARTC_QUARTC_CONNECTION_HELPER_H_ - -#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" -#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h" -#include "net/third_party/quiche/src/quic/core/quic_clock.h" -#include "net/third_party/quiche/src/quic/core/quic_connection.h" -#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" - -namespace quic { - -// Simple implementation of QuicConnectionHelperInterface for Quartc. -class QuartcConnectionHelper : public QuicConnectionHelperInterface { - public: - QuartcConnectionHelper(const QuicClock* clock, QuicRandom* random); - - // QuicConnectionHelperInterface overrides. - const QuicClock* GetClock() const override; - QuicRandom* GetRandomGenerator() override; - QuicBufferAllocator* GetStreamSendBufferAllocator() override; - - private: - const QuicClock* clock_; - QuicRandom* random_; - SimpleBufferAllocator buffer_allocator_; -}; - -} // namespace quic - -#endif // QUICHE_QUIC_QUARTC_QUARTC_CONNECTION_HELPER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.cc deleted file mode 100644 index 14645f83da0..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.cc +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h" - -#include <utility> - -#include "net/third_party/quiche/src/quic/core/quic_utils.h" - -namespace quic { - -void DummyProofSource::GetProof(const QuicSocketAddress& server_address, - const QuicSocketAddress& client_address, - const std::string& hostname, - const std::string& /*server_config*/, - QuicTransportVersion /*transport_version*/, - quiche::QuicheStringPiece /*chlo_hash*/, - std::unique_ptr<Callback> callback) { - QuicReferenceCountedPointer<ProofSource::Chain> chain = - GetCertChain(server_address, client_address, hostname); - QuicCryptoProof proof; - proof.signature = "Dummy signature"; - proof.leaf_cert_scts = "Dummy timestamp"; - callback->Run(true, chain, proof, nullptr /* details */); -} - -QuicReferenceCountedPointer<DummyProofSource::Chain> -DummyProofSource::GetCertChain(const QuicSocketAddress& /*server_address*/, - const QuicSocketAddress& /*client_address*/, - const std::string& /*hostname*/) { - std::vector<std::string> certs; - certs.push_back(kDummyCertName); - return QuicReferenceCountedPointer<ProofSource::Chain>( - new ProofSource::Chain(certs)); -} - -void DummyProofSource::ComputeTlsSignature( - const QuicSocketAddress& /*server_address*/, - const QuicSocketAddress& /*client_address*/, - const std::string& /*hostname*/, - uint16_t /*signature_algorithm*/, - quiche::QuicheStringPiece /*in*/, - std::unique_ptr<SignatureCallback> callback) { - callback->Run(true, "Dummy signature", /*details=*/nullptr); -} - -QuicAsyncStatus InsecureProofVerifier::VerifyProof( - const std::string& /*hostname*/, - const uint16_t /*port*/, - const std::string& /*server_config*/, - QuicTransportVersion /*transport_version*/, - quiche::QuicheStringPiece /*chlo_hash*/, - const std::vector<std::string>& /*certs*/, - const std::string& /*cert_sct*/, - const std::string& /*signature*/, - const ProofVerifyContext* /*context*/, - std::string* /*error_details*/, - std::unique_ptr<ProofVerifyDetails>* /*verify_details*/, - std::unique_ptr<ProofVerifierCallback> /*callback*/) { - return QUIC_SUCCESS; -} - -QuicAsyncStatus InsecureProofVerifier::VerifyCertChain( - const std::string& /*hostname*/, - const uint16_t /*port*/, - const std::vector<std::string>& /*certs*/, - const std::string& /*ocsp_response*/, - const std::string& /*cert_sct*/, - const ProofVerifyContext* /*context*/, - std::string* /*error_details*/, - std::unique_ptr<ProofVerifyDetails>* /*details*/, - std::unique_ptr<ProofVerifierCallback> /*callback*/) { - return QUIC_SUCCESS; -} - -std::unique_ptr<ProofVerifyContext> -InsecureProofVerifier::CreateDefaultContext() { - return nullptr; -} - -bool QuartcCryptoServerStreamHelper::CanAcceptClientHello( - const CryptoHandshakeMessage& /*message*/, - const QuicSocketAddress& /*client_address*/, - const QuicSocketAddress& /*peer_address*/, - const QuicSocketAddress& /*self_address*/, - std::string* /*error_details*/) const { - return true; -} - -std::unique_ptr<QuicCryptoClientConfig> CreateCryptoClientConfig( - quiche::QuicheStringPiece pre_shared_key) { - auto config = std::make_unique<QuicCryptoClientConfig>( - std::make_unique<InsecureProofVerifier>()); - config->set_pad_inchoate_hello(false); - config->set_pad_full_hello(false); - if (!pre_shared_key.empty()) { - config->set_pre_shared_key(pre_shared_key); - } - return config; -} - -CryptoServerConfig CreateCryptoServerConfig( - QuicRandom* random, - const QuicClock* clock, - quiche::QuicheStringPiece pre_shared_key) { - CryptoServerConfig crypto_server_config; - - // Generate a random source address token secret. For long-running servers - // it's better to not regenerate it for each connection to enable zero-RTT - // handshakes, but for transient clients it does not matter. - char source_address_token_secret[kInputKeyingMaterialLength]; - random->RandBytes(source_address_token_secret, kInputKeyingMaterialLength); - auto config = std::make_unique<QuicCryptoServerConfig>( - std::string(source_address_token_secret, kInputKeyingMaterialLength), - random, std::make_unique<DummyProofSource>(), - KeyExchangeSource::Default()); - - // We run QUIC over ICE, and ICE is verifying remote side with STUN pings. - // We disable source address token validation in order to allow for 0-rtt - // setup (plus source ip addresses are changing even during the connection - // when ICE is used). - config->set_validate_source_address_token(false); - - // Effectively disables the anti-amplification measures (we don't need - // them because we use ICE, and we need to disable them because we disable - // padding of crypto packets). - // This multiplier must be large enough so that the crypto handshake packet - // (approx. 300 bytes) multiplied by this multiplier is larger than a fully - // sized packet (currently 1200 bytes). - // 1500 is a bit extreme: if you can imagine sending a 1 byte packet, and - // your largest MTU would be below 1500 bytes, 1500*1 >= - // any_packet_that_you_can_imagine_sending. - // (again, we hardcode packet size to 1200, so we are not dealing with jumbo - // frames). - config->set_chlo_multiplier(1500); - - // We are sending small client hello, we must not validate its size. - config->set_validate_chlo_size(false); - - // Provide server with serialized config string to prove ownership. - QuicCryptoServerConfig::ConfigOptions options; - // The |message| is used to handle the return value of AddDefaultConfig - // which is raw pointer of the CryptoHandshakeMessage. - std::unique_ptr<CryptoHandshakeMessage> message( - config->AddDefaultConfig(random, clock, options)); - config->set_pad_rej(false); - config->set_pad_shlo(false); - if (!pre_shared_key.empty()) { - config->set_pre_shared_key(pre_shared_key); - } - crypto_server_config.config = std::move(config); - const QuicData& data = message->GetSerialized(); - - crypto_server_config.serialized_crypto_config = - std::string(data.data(), data.length()); - return crypto_server_config; -} - -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h deleted file mode 100644 index 806786f2f3d..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 2017 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_QUARTC_QUARTC_CRYPTO_HELPERS_H_ -#define QUICHE_QUIC_QUARTC_QUARTC_CRYPTO_HELPERS_H_ - -#include <string> - -#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h" -#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h" -#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h" -#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h" -#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h" -#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" -#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream_base.h" -#include "net/third_party/quiche/src/quic/core/quic_versions.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" - -namespace quic { - -// Never, ever, change this certificate name. You will break 0-rtt handshake if -// you do. -static constexpr char kDummyCertName[] = "Dummy cert"; - -struct CryptoServerConfig { - std::unique_ptr<QuicCryptoServerConfig> config; - std::string serialized_crypto_config; -}; - -// Length of HKDF input keying material, equal to its number of bytes. -// https://tools.ietf.org/html/rfc5869#section-2.2. -// TODO(zhihuang): Verify that input keying material length is correct. -constexpr size_t kInputKeyingMaterialLength = 32; - -// Used by QuicCryptoServerConfig to provide dummy proof credentials. -// TODO(zhihuang): Remove when secure P2P QUIC handshake is possible. -class DummyProofSource : public ProofSource { - public: - DummyProofSource() {} - ~DummyProofSource() override {} - - // ProofSource overrides. - void GetProof(const QuicSocketAddress& server_address, - const QuicSocketAddress& client_address, - const std::string& hostname, - const std::string& server_config, - QuicTransportVersion transport_version, - quiche::QuicheStringPiece chlo_hash, - std::unique_ptr<Callback> callback) override; - - QuicReferenceCountedPointer<Chain> GetCertChain( - const QuicSocketAddress& server_address, - const QuicSocketAddress& client_address, - const std::string& hostname) override; - - void ComputeTlsSignature( - const QuicSocketAddress& server_address, - const QuicSocketAddress& client_address, - const std::string& hostname, - uint16_t signature_algorithm, - quiche::QuicheStringPiece in, - std::unique_ptr<SignatureCallback> callback) override; - - TicketCrypter* GetTicketCrypter() override { return nullptr; } -}; - -// Used by QuicCryptoClientConfig to ignore the peer's credentials -// and establish an insecure QUIC connection. -// TODO(zhihuang): Remove when secure P2P QUIC handshake is possible. -class InsecureProofVerifier : public ProofVerifier { - public: - InsecureProofVerifier() {} - ~InsecureProofVerifier() override {} - - // ProofVerifier overrides. - QuicAsyncStatus VerifyProof( - const std::string& hostname, - const uint16_t port, - const std::string& server_config, - QuicTransportVersion transport_version, - quiche::QuicheStringPiece chlo_hash, - const std::vector<std::string>& certs, - const std::string& cert_sct, - const std::string& signature, - const ProofVerifyContext* context, - std::string* error_details, - std::unique_ptr<ProofVerifyDetails>* verify_details, - std::unique_ptr<ProofVerifierCallback> callback) override; - - QuicAsyncStatus VerifyCertChain( - const std::string& hostname, - const uint16_t port, - const std::vector<std::string>& certs, - const std::string& ocsp_response, - const std::string& cert_sct, - const ProofVerifyContext* context, - std::string* error_details, - std::unique_ptr<ProofVerifyDetails>* details, - std::unique_ptr<ProofVerifierCallback> callback) override; - - std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override; -}; - -// Implementation of the server-side crypto stream helper. -class QuartcCryptoServerStreamHelper - : public QuicCryptoServerStreamBase::Helper { - public: - bool CanAcceptClientHello(const CryptoHandshakeMessage& message, - const QuicSocketAddress& client_address, - const QuicSocketAddress& peer_address, - const QuicSocketAddress& self_address, - std::string* error_details) const override; -}; - -std::unique_ptr<QuicCryptoClientConfig> CreateCryptoClientConfig( - quiche::QuicheStringPiece pre_shared_key); - -CryptoServerConfig CreateCryptoServerConfig( - QuicRandom* random, - const QuicClock* clock, - quiche::QuicheStringPiece pre_shared_key); - -} // namespace quic - -#endif // QUICHE_QUIC_QUARTC_QUARTC_CRYPTO_HELPERS_H_ diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.cc deleted file mode 100644 index c5c4c4a742f..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h" - -#include "net/third_party/quiche/src/quic/core/quic_utils.h" -#include "net/third_party/quiche/src/quic/core/quic_versions.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_factory.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" - -namespace quic { - -QuartcDispatcher::QuartcDispatcher( - std::unique_ptr<QuicConfig> config, - std::unique_ptr<QuicCryptoServerConfig> crypto_config, - QuicVersionManager* version_manager, - std::unique_ptr<QuicConnectionHelperInterface> helper, - std::unique_ptr<QuicCryptoServerStreamBase::Helper> session_helper, - std::unique_ptr<QuicAlarmFactory> alarm_factory, - std::unique_ptr<QuartcPacketWriter> packet_writer, - Delegate* delegate) - : QuicDispatcher( - config.get(), - crypto_config.get(), - version_manager, - std::move(helper), - std::move(session_helper), - std::move(alarm_factory), - QuicUtils::CreateZeroConnectionId( - version_manager->GetSupportedVersions()[0].transport_version) - .length()), - owned_quic_config_(std::move(config)), - owned_crypto_config_(std::move(crypto_config)), - delegate_(delegate), - packet_writer_(packet_writer.get()) { - // Allow incoming packets to set our expected connection ID length. - SetShouldUpdateExpectedServerConnectionIdLength(true); - // Allow incoming packets with connection ID lengths shorter than allowed. - SetAllowShortInitialServerConnectionIds(true); - // QuicDispatcher takes ownership of the writer. - QuicDispatcher::InitializeWithWriter(packet_writer.release()); - // NB: This must happen *after* InitializeWithWriter. It can call us back - // with OnTransportCanWrite() immediately, and the dispatcher needs to be - // fully initialized to handle that. - packet_writer_->SetPacketTransportDelegate(this); -} - -QuartcDispatcher::~QuartcDispatcher() { - packet_writer_->SetPacketTransportDelegate(nullptr); -} - -std::unique_ptr<QuicSession> QuartcDispatcher::CreateQuicSession( - QuicConnectionId connection_id, - const QuicSocketAddress& client_address, - quiche::QuicheStringPiece /*alpn*/, - const ParsedQuicVersion& version) { - // Make our expected connection ID non-mutable since we have a connection. - SetShouldUpdateExpectedServerConnectionIdLength(false); - std::unique_ptr<QuicConnection> connection = CreateQuicConnection( - connection_id, client_address, helper(), alarm_factory(), writer(), - Perspective::IS_SERVER, ParsedQuicVersionVector{version}); - auto session = std::make_unique<QuartcServerSession>( - std::move(connection), /*visitor=*/this, config(), GetSupportedVersions(), - helper()->GetClock(), crypto_config(), compressed_certs_cache(), - session_helper()); - delegate_->OnSessionCreated(session.get()); - return session; -} - -void QuartcDispatcher::OnTransportCanWrite() { - OnCanWrite(); -} - -void QuartcDispatcher::OnTransportReceived(const char* data, size_t data_len) { - // QuartcPacketTransport does not surface real peer addresses, so the - // dispatcher uses a dummy address when processing incoming packets. Note that - // the dispatcher refuses to process anything with port 0. - static const QuicSocketAddress* dummy_address = - new QuicSocketAddress(QuicIpAddress::Any4(), /*port=*/1); - - QuicReceivedPacket packet(data, data_len, helper()->GetClock()->Now()); - ProcessPacket(/*self_address=*/*dummy_address, - /*peer_address=*/*dummy_address, packet); -} - -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h deleted file mode 100644 index ca4fe2d6147..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2017 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_QUARTC_QUARTC_DISPATCHER_H_ -#define QUICHE_QUIC_QUARTC_QUARTC_DISPATCHER_H_ - -#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h" -#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h" -#include "net/third_party/quiche/src/quic/core/quic_config.h" -#include "net/third_party/quiche/src/quic/core/quic_connection.h" -#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" -#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream_base.h" -#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h" -#include "net/third_party/quiche/src/quic/core/quic_version_manager.h" -#include "net/third_party/quiche/src/quic/core/quic_versions.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_session.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" - -namespace quic { - -class QuartcDispatcher : public QuicDispatcher, - QuartcPacketTransport::Delegate { - public: - class Delegate { - public: - virtual ~Delegate() = default; - virtual void OnSessionCreated(QuartcSession* session) = 0; - }; - - QuartcDispatcher( - std::unique_ptr<QuicConfig> config, - std::unique_ptr<QuicCryptoServerConfig> crypto_config, - QuicVersionManager* version_manager, - std::unique_ptr<QuicConnectionHelperInterface> helper, - std::unique_ptr<QuicCryptoServerStreamBase::Helper> session_helper, - std::unique_ptr<QuicAlarmFactory> alarm_factory, - std::unique_ptr<QuartcPacketWriter> packet_writer, - Delegate* delegate); - ~QuartcDispatcher() override; - - std::unique_ptr<QuicSession> CreateQuicSession( - QuicConnectionId server_connection_id, - const QuicSocketAddress& client_address, - quiche::QuicheStringPiece alpn, - const ParsedQuicVersion& version) override; - - // TODO(b/124399417): Override GenerateNewServerConnectionId and request a - // zero-length connection id when the QUIC server perspective supports it. - - // QuartcPacketTransport::Delegate overrides. - void OnTransportCanWrite() override; - void OnTransportReceived(const char* data, size_t data_len) override; - - private: - // Members owned by QuartcDispatcher but not QuicDispatcher. - std::unique_ptr<QuicConfig> owned_quic_config_; - std::unique_ptr<QuicCryptoServerConfig> owned_crypto_config_; - - // Delegate invoked when the dispatcher creates a new session. - Delegate* delegate_; - - // The packet writer used by this dispatcher. Owned by the base class, but - // the base class upcasts it to QuicPacketWriter (which prevents detaching the - // transport delegate without a downcast). - QuartcPacketWriter* packet_writer_; -}; - -} // namespace quic - -#endif // QUICHE_QUIC_QUARTC_QUARTC_DISPATCHER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.cc deleted file mode 100644 index ca8fad6c9c4..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.cc +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h" - -#include <utility> - -#include "net/third_party/quiche/src/quic/core/quic_version_manager.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" - -namespace quic { - -namespace { - -// Wrapper around a QuicAlarmFactory which delegates to the wrapped factory. -// Usee to convert an unowned pointer into an owned pointer, so that the new -// "owner" does not delete the underlying factory. Note that this is only valid -// when the unowned pointer is already guaranteed to outlive the new "owner". -class QuartcAlarmFactoryWrapper : public QuicAlarmFactory { - public: - explicit QuartcAlarmFactoryWrapper(QuicAlarmFactory* impl) : impl_(impl) {} - - QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override; - QuicArenaScopedPtr<QuicAlarm> CreateAlarm( - QuicArenaScopedPtr<QuicAlarm::Delegate> delegate, - QuicConnectionArena* arena) override; - - private: - QuicAlarmFactory* impl_; -}; - -QuicAlarm* QuartcAlarmFactoryWrapper::CreateAlarm( - QuicAlarm::Delegate* delegate) { - return impl_->CreateAlarm(delegate); -} - -QuicArenaScopedPtr<QuicAlarm> QuartcAlarmFactoryWrapper::CreateAlarm( - QuicArenaScopedPtr<QuicAlarm::Delegate> delegate, - QuicConnectionArena* arena) { - return impl_->CreateAlarm(std::move(delegate), arena); -} - -} // namespace - -QuartcClientEndpoint::QuartcClientEndpoint( - QuicAlarmFactory* alarm_factory, - const QuicClock* clock, - QuicRandom* random, - QuartcEndpoint::Delegate* delegate, - const QuartcSessionConfig& config, - quiche::QuicheStringPiece serialized_server_config, - std::unique_ptr<QuicVersionManager> version_manager) - : alarm_factory_(alarm_factory), - clock_(clock), - delegate_(delegate), - serialized_server_config_(serialized_server_config), - version_manager_(version_manager ? std::move(version_manager) - : std::make_unique<QuicVersionManager>( - AllSupportedVersions())), - create_session_alarm_(QuicWrapUnique( - alarm_factory_->CreateAlarm(new CreateSessionDelegate(this)))), - connection_helper_( - std::make_unique<QuartcConnectionHelper>(clock_, random)), - config_(config) {} - -void QuartcClientEndpoint::Connect(QuartcPacketTransport* packet_transport) { - packet_transport_ = packet_transport; - // For the first attempt to connect, use any version that the client supports. - current_versions_ = version_manager_->GetSupportedVersions(); - create_session_alarm_->Set(clock_->Now()); -} - -void QuartcClientEndpoint::OnCreateSessionAlarm() { - session_ = CreateQuartcClientSession( - config_, clock_, alarm_factory_, connection_helper_.get(), - current_versions_, serialized_server_config_, packet_transport_); - session_->SetDelegate(this); - delegate_->OnSessionCreated(session_.get()); -} - -void QuartcClientEndpoint::OnCryptoHandshakeComplete() { - delegate_->OnCryptoHandshakeComplete(); -} - -void QuartcClientEndpoint::OnConnectionWritable() { - delegate_->OnConnectionWritable(); -} - -void QuartcClientEndpoint::OnIncomingStream(QuartcStream* stream) { - delegate_->OnIncomingStream(stream); -} - -void QuartcClientEndpoint::OnCongestionControlChange( - QuicBandwidth bandwidth_estimate, - QuicBandwidth pacing_rate, - QuicTime::Delta latest_rtt) { - delegate_->OnCongestionControlChange(bandwidth_estimate, pacing_rate, - latest_rtt); -} - -void QuartcClientEndpoint::OnConnectionClosed( - const QuicConnectionCloseFrame& frame, - ConnectionCloseSource source) { - // First, see if we can restart the session with a mutually-supported version. - if (frame.quic_error_code == QUIC_INVALID_VERSION && session_ && - session_->connection() && - !session_->connection()->server_supported_versions().empty()) { - for (const auto& client_version : - version_manager_->GetSupportedVersions()) { - if (QuicContainsValue(session_->connection()->server_supported_versions(), - client_version)) { - // Found a mutually-supported version. Reconnect using that version. - current_versions_.clear(); - current_versions_.push_back(client_version); - create_session_alarm_->Set(clock_->Now()); - return; - } - } - } - - // Permanent version negotiation errors are forwarded to the |delegate_|, - // along with all other errors. - delegate_->OnConnectionClosed(frame, source); -} - -void QuartcClientEndpoint::OnMessageReceived( - quiche::QuicheStringPiece message) { - delegate_->OnMessageReceived(message); -} - -void QuartcClientEndpoint::OnMessageSent(int64_t datagram_id) { - delegate_->OnMessageSent(datagram_id); -} - -void QuartcClientEndpoint::OnMessageAcked(int64_t datagram_id, - QuicTime receive_timestamp) { - delegate_->OnMessageAcked(datagram_id, receive_timestamp); -} - -void QuartcClientEndpoint::OnMessageLost(int64_t datagram_id) { - delegate_->OnMessageLost(datagram_id); -} - -QuartcServerEndpoint::QuartcServerEndpoint( - QuicAlarmFactory* alarm_factory, - const QuicClock* clock, - QuicRandom* random, - QuartcEndpoint::Delegate* delegate, - const QuartcSessionConfig& config, - std::unique_ptr<QuicVersionManager> version_manager) - : alarm_factory_(alarm_factory), - delegate_(delegate), - config_(config), - version_manager_(version_manager ? std::move(version_manager) - : std::make_unique<QuicVersionManager>( - AllSupportedVersions())), - pre_connection_helper_( - std::make_unique<QuartcConnectionHelper>(clock, random)), - crypto_config_( - CreateCryptoServerConfig(pre_connection_helper_->GetRandomGenerator(), - clock, - config.pre_shared_key)) {} - -void QuartcServerEndpoint::Connect(QuartcPacketTransport* packet_transport) { - DCHECK(pre_connection_helper_ != nullptr); - dispatcher_ = std::make_unique<QuartcDispatcher>( - std::make_unique<QuicConfig>(CreateQuicConfig(config_)), - std::move(crypto_config_.config), version_manager_.get(), - std::move(pre_connection_helper_), - std::make_unique<QuartcCryptoServerStreamHelper>(), - std::make_unique<QuartcAlarmFactoryWrapper>(alarm_factory_), - std::make_unique<QuartcPacketWriter>(packet_transport, - config_.max_packet_size), - this); - // The dispatcher requires at least one call to |ProcessBufferedChlos| to - // set the number of connections it is allowed to create. - dispatcher_->ProcessBufferedChlos(/*max_connections_to_create=*/1); -} - -void QuartcServerEndpoint::OnSessionCreated(QuartcSession* session) { - session->SetDelegate(delegate_); - delegate_->OnSessionCreated(session); -} - -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.h deleted file mode 100644 index ea1a63f0e33..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.h +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright (c) 2017 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_QUARTC_QUARTC_ENDPOINT_H_ -#define QUICHE_QUIC_QUARTC_QUARTC_ENDPOINT_H_ - -#include <string> - -#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h" -#include "net/third_party/quiche/src/quic/core/quic_clock.h" -#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_factory.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" - -namespace quic { - -// Endpoint (client or server) in a peer-to-peer Quartc connection. -class QuartcEndpoint { - public: - class Delegate : public QuartcSession::Delegate { - public: - virtual ~Delegate() = default; - - // Called when an endpoint creates a new session, before any packets are - // processed or sent. The callee should perform any additional - // configuration required, such as setting up congestion control, before - // returning. |session| is owned by the endpoint, but remains safe to use - // until another call to |OnSessionCreated| or |OnConnectionClosed| occurs, - // at which point previous session may be destroyed. - // - // Callees must not change the |session|'s delegate. The Endpoint itself - // manages the delegate and will forward calls. - // - // New calls to |OnSessionCreated| will only occur prior to - // |OnConnectionWritable|, during initial connection negotiation. - virtual void OnSessionCreated(QuartcSession* session) = 0; - }; - - virtual ~QuartcEndpoint() = default; - - // Connects the endpoint using the given session config. After |Connect| is - // called, the endpoint will asynchronously create a session, then call - // |Delegate::OnSessionCreated|. - virtual void Connect(QuartcPacketTransport* packet_transport) = 0; -}; - -// Implementation of QuartcEndpoint which immediately (but asynchronously) -// creates a session by scheduling a QuicAlarm. Only suitable for use with the -// client perspective. -class QuartcClientEndpoint : public QuartcEndpoint, - public QuartcSession::Delegate { - public: - // |alarm_factory|, |clock|, and |delegate| are owned by the caller and must - // outlive the endpoint. - QuartcClientEndpoint( - QuicAlarmFactory* alarm_factory, - const QuicClock* clock, - QuicRandom* random, - QuartcEndpoint::Delegate* delegate, - const QuartcSessionConfig& config, - quiche::QuicheStringPiece serialized_server_config, - std::unique_ptr<QuicVersionManager> version_manager = nullptr); - - void Connect(QuartcPacketTransport* packet_transport) override; - - // QuartcSession::Delegate overrides. - void OnCryptoHandshakeComplete() override; - void OnConnectionWritable() override; - void OnIncomingStream(QuartcStream* stream) override; - void OnCongestionControlChange(QuicBandwidth bandwidth_estimate, - QuicBandwidth pacing_rate, - QuicTime::Delta latest_rtt) override; - void OnConnectionClosed(const QuicConnectionCloseFrame& frame, - ConnectionCloseSource source) override; - void OnMessageReceived(quiche::QuicheStringPiece message) override; - void OnMessageSent(int64_t datagram_id) override; - void OnMessageAcked(int64_t datagram_id, QuicTime receive_timestamp) override; - void OnMessageLost(int64_t datagram_id) override; - - private: - friend class CreateSessionDelegate; - class CreateSessionDelegate : public QuicAlarm::Delegate { - public: - CreateSessionDelegate(QuartcClientEndpoint* endpoint) - : endpoint_(endpoint) {} - - void OnAlarm() override { endpoint_->OnCreateSessionAlarm(); } - - private: - QuartcClientEndpoint* endpoint_; - }; - - // Callback which occurs when |create_session_alarm_| fires. - void OnCreateSessionAlarm(); - - // Implementation of QuicAlarmFactory used by this endpoint. Unowned. - QuicAlarmFactory* alarm_factory_; - - // Implementation of QuicClock used by this endpoint. Unowned. - const QuicClock* clock_; - - // Delegate which receives callbacks for newly created sessions. - QuartcEndpoint::Delegate* delegate_; - - // Server config. If valid, used to perform a 0-RTT connection. - const std::string serialized_server_config_; - - // Version manager. May be injected to control version negotiation in tests. - std::unique_ptr<QuicVersionManager> version_manager_; - - // Versions to be used when the next session is created. The session will - // choose one of these versions for its connection attempt. - // - // If the connection does not succeed, the client session MAY try again using - // another version from this list, or it MAY simply fail with a - // QUIC_INVALID_VERSION error. The latter occurs when it is not possible to - // upgrade a connection in-place (for example, if the way stream ids are - // allocated changes between versions). This failure mode is handled by - // narrowing |current_versions_| to one of that is mutually-supported and - // reconnecting (with a new session). - ParsedQuicVersionVector current_versions_; - - // Alarm for creating sessions asynchronously. The alarm is set when - // Connect() is called. When it fires, the endpoint creates a session and - // calls the delegate. - std::unique_ptr<QuicAlarm> create_session_alarm_; - - // Helper used by QuicConnection. - std::unique_ptr<QuicConnectionHelperInterface> connection_helper_; - - // Config to be used for new sessions. - QuartcSessionConfig config_; - - // The currently-active session. Nullptr until |Connect| and - // |Delegate::OnSessionCreated| are called. - std::unique_ptr<QuartcSession> session_; - - QuartcPacketTransport* packet_transport_; -}; - -// Implementation of QuartcEndpoint which uses a QuartcDispatcher to listen for -// an incoming CHLO and create a session when one arrives. Only suitable for -// use with the server perspective. -class QuartcServerEndpoint : public QuartcEndpoint, - public QuartcDispatcher::Delegate { - public: - QuartcServerEndpoint( - QuicAlarmFactory* alarm_factory, - const QuicClock* clock, - QuicRandom* random, - QuartcEndpoint::Delegate* delegate, - const QuartcSessionConfig& config, - std::unique_ptr<QuicVersionManager> version_manager = nullptr); - - // Implements QuartcEndpoint. - void Connect(QuartcPacketTransport* packet_transport) override; - - // Implements QuartcDispatcher::Delegate. - void OnSessionCreated(QuartcSession* session) override; - - // Accessor to retrieve the server crypto config. May only be called after - // Connect(). - quiche::QuicheStringPiece server_crypto_config() const { - return crypto_config_.serialized_crypto_config; - } - - const std::vector<ParsedQuicVersion> GetSupportedQuicVersions() const { - return version_manager_->GetSupportedVersions(); - } - - private: - // Implementation of QuicAlarmFactory used by this endpoint. Unowned. - QuicAlarmFactory* alarm_factory_; - - // Delegate which receives callbacks for newly created sessions. - QuartcEndpoint::Delegate* delegate_; - - // Config to be used for new sessions. - QuartcSessionConfig config_; - - // Version manager. May be injected to control version negotiation in tests. - std::unique_ptr<QuicVersionManager> version_manager_; - - // QuartcDispatcher waits for an incoming CHLO, then either rejects it or - // creates a session to respond to it. The dispatcher owns all sessions it - // creates. - std::unique_ptr<QuartcDispatcher> dispatcher_; - - // This field is only available before connection was started. - std::unique_ptr<QuartcConnectionHelper> pre_connection_helper_; - - // A configuration, containing public key, that may need to be passed to the - // client to enable 0rtt. - CryptoServerConfig crypto_config_; -}; - -} // namespace quic - -#endif // QUICHE_QUIC_QUARTC_QUARTC_ENDPOINT_H_ diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint_test.cc deleted file mode 100644 index d2f94202aa1..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint_test.cc +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h" - -#include <utility> - -#include "net/third_party/quiche/src/quic/core/quic_versions.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_fakes.h" -#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h" -#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" -#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h" -#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h" - -namespace quic { -namespace { - -class QuartcEndpointTest : public QuicTest { - protected: - QuartcEndpointTest() - : client_transport_(&simulator_, - "client_transport", - "server_transport", - 10 * kDefaultMaxPacketSize), - server_transport_(&simulator_, - "server_transport", - "client_transport", - 10 * kDefaultMaxPacketSize), - client_server_link_(&client_transport_, - &server_transport_, - QuicBandwidth::FromKBitsPerSecond(10000), - QuicTime::Delta::FromMilliseconds(1)), - server_endpoint_delegate_(&server_stream_delegate_, - simulator_.GetClock()), - server_endpoint_(std::make_unique<QuartcServerEndpoint>( - simulator_.GetAlarmFactory(), - simulator_.GetClock(), - simulator_.GetRandomGenerator(), - &server_endpoint_delegate_, - QuartcSessionConfig())), - client_endpoint_delegate_(&client_stream_delegate_, - simulator_.GetClock()), - client_endpoint_(std::make_unique<QuartcClientEndpoint>( - simulator_.GetAlarmFactory(), - simulator_.GetClock(), - simulator_.GetRandomGenerator(), - &client_endpoint_delegate_, - QuartcSessionConfig(), - /*serialized_server_config=*/"")) { - // Make sure these versions are enabled since some tests use them. - SetQuicReloadableFlag(quic_disable_version_q043, false); - SetQuicReloadableFlag(quic_disable_version_q046, false); - } - - simulator::Simulator simulator_; - - simulator::SimulatedQuartcPacketTransport client_transport_; - simulator::SimulatedQuartcPacketTransport server_transport_; - simulator::SymmetricLink client_server_link_; - - FakeQuartcStreamDelegate server_stream_delegate_; - FakeQuartcEndpointDelegate server_endpoint_delegate_; - - std::unique_ptr<QuartcServerEndpoint> server_endpoint_; - - FakeQuartcStreamDelegate client_stream_delegate_; - FakeQuartcEndpointDelegate client_endpoint_delegate_; - - std::unique_ptr<QuartcClientEndpoint> client_endpoint_; -}; - -// After calling Connect, the client endpoint must wait for an async callback. -// The callback occurs after a finite amount of time and produces a session. -TEST_F(QuartcEndpointTest, ClientCreatesSessionAsynchronously) { - client_endpoint_->Connect(&client_transport_); - - EXPECT_EQ(client_endpoint_delegate_.session(), nullptr); - - EXPECT_TRUE(simulator_.RunUntil( - [this] { return client_endpoint_delegate_.session() != nullptr; })); -} - -// Tests that the server can negotiate for an older QUIC version if the client -// attempts to connect using a newer version. -TEST_F(QuartcEndpointTest, - QUIC_TEST_DISABLED_IN_CHROME(ServerNegotiatesForOldVersion)) { - // Reset the client endpoint to prefer version 46 but also be capable of - // speaking version 43. - ParsedQuicVersionVector client_versions; - client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46}); - client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43}); - client_endpoint_ = std::make_unique<QuartcClientEndpoint>( - simulator_.GetAlarmFactory(), simulator_.GetClock(), - simulator_.GetRandomGenerator(), &client_endpoint_delegate_, - QuartcSessionConfig(), - /*serialized_server_config=*/"", - std::make_unique<QuicVersionManager>(client_versions)); - - // Reset the server endpoint to only speak version 43. - ParsedQuicVersionVector server_versions; - server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43}); - server_endpoint_ = std::make_unique<QuartcServerEndpoint>( - simulator_.GetAlarmFactory(), simulator_.GetClock(), - simulator_.GetRandomGenerator(), &server_endpoint_delegate_, - QuartcSessionConfig(), - std::make_unique<QuicVersionManager>(server_versions)); - - // The endpoints should be able to establish a connection using version 46. - server_endpoint_->Connect(&server_transport_); - client_endpoint_->Connect(&client_transport_); - - ASSERT_TRUE(simulator_.RunUntil([this] { - return client_endpoint_delegate_.session() != nullptr && - client_endpoint_delegate_.session()->IsEncryptionEstablished() && - server_endpoint_delegate_.session() != nullptr && - server_endpoint_delegate_.session()->IsEncryptionEstablished(); - })); - EXPECT_EQ(client_endpoint_delegate_.session()->connection()->version(), - server_versions[0]); - EXPECT_EQ(server_endpoint_delegate_.session()->connection()->version(), - server_versions[0]); -} - -// Tests that the server can accept connections from clients that use older -// QUIC versions. -TEST_F(QuartcEndpointTest, - QUIC_TEST_DISABLED_IN_CHROME(ServerAcceptsOldVersion)) { - // Reset the client endpoint to only speak version 43. - ParsedQuicVersionVector client_versions; - client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43}); - client_endpoint_ = std::make_unique<QuartcClientEndpoint>( - simulator_.GetAlarmFactory(), simulator_.GetClock(), - simulator_.GetRandomGenerator(), &client_endpoint_delegate_, - QuartcSessionConfig(), - /*serialized_server_config=*/"", - std::make_unique<QuicVersionManager>(client_versions)); - - // Reset the server endpoint to prefer version 46 but also be capable of - // speaking version 43. - ParsedQuicVersionVector server_versions; - server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46}); - server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43}); - server_endpoint_ = std::make_unique<QuartcServerEndpoint>( - simulator_.GetAlarmFactory(), simulator_.GetClock(), - simulator_.GetRandomGenerator(), &server_endpoint_delegate_, - QuartcSessionConfig(), - std::make_unique<QuicVersionManager>(server_versions)); - - // The endpoints should be able to establish a connection using version 46. - server_endpoint_->Connect(&server_transport_); - client_endpoint_->Connect(&client_transport_); - - ASSERT_TRUE(simulator_.RunUntil([this] { - return client_endpoint_delegate_.session() != nullptr && - client_endpoint_delegate_.session()->IsEncryptionEstablished() && - server_endpoint_delegate_.session() != nullptr && - server_endpoint_delegate_.session()->IsEncryptionEstablished(); - })); - EXPECT_EQ(client_endpoint_delegate_.session()->connection()->version(), - client_versions[0]); - EXPECT_EQ(server_endpoint_delegate_.session()->connection()->version(), - client_versions[0]); -} - -// Tests that version negotiation fails when the client and server support -// completely disjoint sets of versions. -TEST_F(QuartcEndpointTest, - QUIC_TEST_DISABLED_IN_CHROME(VersionNegotiationWithDisjointVersions)) { - // Reset the client endpoint to only speak version 43. - ParsedQuicVersionVector client_versions; - client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43}); - client_endpoint_ = std::make_unique<QuartcClientEndpoint>( - simulator_.GetAlarmFactory(), simulator_.GetClock(), - simulator_.GetRandomGenerator(), &client_endpoint_delegate_, - QuartcSessionConfig(), - /*serialized_server_config=*/"", - std::make_unique<QuicVersionManager>(client_versions)); - - // Reset the server endpoint to only speak version 46. - ParsedQuicVersionVector server_versions; - server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46}); - server_endpoint_ = std::make_unique<QuartcServerEndpoint>( - simulator_.GetAlarmFactory(), simulator_.GetClock(), - simulator_.GetRandomGenerator(), &server_endpoint_delegate_, - QuartcSessionConfig(), - std::make_unique<QuicVersionManager>(server_versions)); - - // The endpoints should be unable to establish a connection. - server_endpoint_->Connect(&server_transport_); - client_endpoint_->Connect(&client_transport_); - - // Note that the error is reported from the client and *not* the server. The - // server sees an invalid version, sends a version negotiation packet, and - // never gets a response, because the client stops sending when it can't find - // a mutually supported versions. - ASSERT_TRUE(simulator_.RunUntil([this] { - return client_endpoint_delegate_.session() != nullptr && - client_endpoint_delegate_.session()->error() != QUIC_NO_ERROR; - })); - EXPECT_THAT(client_endpoint_delegate_.session()->error(), - test::IsError(QUIC_INVALID_VERSION)); -} - -// Tests that the client endpoint can create a new session in order to continue -// version negotiation. -TEST_F(QuartcEndpointTest, - QUIC_TEST_DISABLED_IN_CHROME(CreatesNewSessionWhenRequired)) { - // Reset the client endpoint to prefer version 46 but also be capable of - // speaking version 43. - ParsedQuicVersionVector client_versions; - client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46}); - client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43}); - client_endpoint_ = std::make_unique<QuartcClientEndpoint>( - simulator_.GetAlarmFactory(), simulator_.GetClock(), - simulator_.GetRandomGenerator(), &client_endpoint_delegate_, - QuartcSessionConfig(), - /*serialized_server_config=*/"", - std::make_unique<QuicVersionManager>(client_versions)); - - // Reset the server endpoint to only speak version 43. - ParsedQuicVersionVector server_versions; - server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43}); - server_endpoint_ = std::make_unique<QuartcServerEndpoint>( - simulator_.GetAlarmFactory(), simulator_.GetClock(), - simulator_.GetRandomGenerator(), &server_endpoint_delegate_, - QuartcSessionConfig(), - std::make_unique<QuicVersionManager>(server_versions)); - - // The endpoints should be able to establish a connection using version 46. - server_endpoint_->Connect(&server_transport_); - client_endpoint_->Connect(&client_transport_); - - ASSERT_TRUE(simulator_.RunUntil([this] { - return client_endpoint_delegate_.session() != nullptr && - client_endpoint_delegate_.session()->IsEncryptionEstablished() && - server_endpoint_delegate_.session() != nullptr && - server_endpoint_delegate_.session()->IsEncryptionEstablished(); - })); - EXPECT_EQ(client_endpoint_delegate_.session()->connection()->version(), - server_versions[0]); - EXPECT_EQ(server_endpoint_delegate_.session()->connection()->version(), - server_versions[0]); - - EXPECT_EQ(2, client_endpoint_delegate_.num_sessions_created()); -} - -} // namespace -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.cc deleted file mode 100644 index 280d936b25a..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.cc +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/quartc/quartc_factory.h" - -#include <utility> - -#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" -#include "net/third_party/quiche/src/quic/core/quic_utils.h" -#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" -#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" -#include "net/third_party/quiche/src/quic/core/uber_received_packet_manager.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_session.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" - -namespace quic { - -std::unique_ptr<QuartcSession> CreateQuartcClientSession( - const QuartcSessionConfig& quartc_session_config, - const QuicClock* clock, - QuicAlarmFactory* alarm_factory, - QuicConnectionHelperInterface* connection_helper, - const ParsedQuicVersionVector& supported_versions, - quiche::QuicheStringPiece server_crypto_config, - QuartcPacketTransport* packet_transport) { - DCHECK(packet_transport); - - // QuartcSession will eventually own both |writer| and |quic_connection|. - auto writer = std::make_unique<QuartcPacketWriter>( - packet_transport, quartc_session_config.max_packet_size); - - // While the QuicConfig is not directly used by the connection, creating it - // also sets flag values which must be set before creating the connection. - QuicConfig quic_config = CreateQuicConfig(quartc_session_config); - - // |dummy_id| and |dummy_address| are used because Quartc network layer will - // not use these two. - QuicConnectionId dummy_id = QuicUtils::CreateZeroConnectionId( - supported_versions[0].transport_version); - QuicSocketAddress dummy_address(QuicIpAddress::Any4(), /*port=*/0); - std::unique_ptr<QuicConnection> quic_connection = CreateQuicConnection( - dummy_id, dummy_address, connection_helper, alarm_factory, writer.get(), - Perspective::IS_CLIENT, supported_versions); - - // Quartc sets its own ack delay; get that ack delay and copy it over - // to the QuicConfig so that it can be properly advertised to the peer - // via transport parameter negotiation. - quic_config.SetMaxAckDelayToSendMs(quic_connection->received_packet_manager() - .max_ack_delay() - .ToMilliseconds()); - - return std::make_unique<QuartcClientSession>( - std::move(quic_connection), quic_config, supported_versions, clock, - std::move(writer), - CreateCryptoClientConfig(quartc_session_config.pre_shared_key), - server_crypto_config); -} - -void ConfigureGlobalQuicSettings() { - // Ensure that we don't drop data because QUIC streams refuse to buffer it. - // TODO(b/120099046): Replace this with correct handling of WriteMemSlices(). - SetQuicFlag(FLAGS_quic_buffered_data_threshold, - std::numeric_limits<int>::max()); - - // Enable and request QUIC to include receive timestamps in ACK frames. - SetQuicReloadableFlag(quic_send_timestamps, true); - - // Enable ACK_DECIMATION_WITH_REORDERING. It requires ack_decimation to be - // false. - SetQuicReloadableFlag(quic_enable_ack_decimation, false); - - // Note: flag settings have no effect for Exoblaze builds since - // SetQuicReloadableFlag() gets stubbed out. - SetQuicReloadableFlag(quic_unified_iw_options, true); // Enable IWXX opts. - SetQuicReloadableFlag(quic_bbr_flexible_app_limited, true); // Enable BBR9. -} - -QuicConfig CreateQuicConfig(const QuartcSessionConfig& quartc_session_config) { - // TODO(b/124398962): Figure out a better way to initialize QUIC flags. - // Creating a config shouldn't have global side-effects on flags. However, - // this has the advantage of ensuring that flag values stay in sync with the - // options requested by configs, so simply splitting the config and flag - // settings doesn't seem preferable. - ConfigureGlobalQuicSettings(); - - QuicTagVector copt; - copt.push_back(kNSTP); - - // Enable and request QUIC to include receive timestamps in ACK frames. - copt.push_back(kSTMP); - - // Enable ACK_DECIMATION_WITH_REORDERING. It requires ack_decimation to be - // false. - copt.push_back(kAKD2); - - // Use unlimited decimation in order to reduce number of unbundled ACKs. - copt.push_back(kAKDU); - - // Enable time-based loss detection. - copt.push_back(kTIME); - - copt.push_back(kBBR3); // Stay in low-gain until in-flight < BDP. - copt.push_back(kBBR5); // 40 RTT ack aggregation. - copt.push_back(kBBR9); // Ignore app-limited if enough data is in flight. - copt.push_back(kBBQ1); // 2.773 pacing gain in STARTUP. - copt.push_back(kBBQ2); // 2.0 CWND gain in STARTUP. - copt.push_back(k1RTT); // Exit STARTUP after 1 RTT with no gains. - copt.push_back(kIW10); // 10-packet (14600 byte) initial cwnd. - - if (!quartc_session_config.enable_tail_loss_probe) { - copt.push_back(kNTLP); - } - - // TODO(b/112192153): Test and possible enable slower startup when pipe - // filling is ready to use. Slower startup is kBBRS. - - QuicConfig quic_config; - - // Use the limits for the session & stream flow control. The default 16KB - // limit leads to significantly undersending (not reaching BWE on the outgoing - // bitrate) due to blocked frames, and it leads to high latency (and one-way - // delay). Setting it to its limits is not going to cause issues (our streams - // are small generally, and if we were to buffer 24MB it wouldn't be the end - // of the world). We can consider setting different limits in future (e.g. 1MB - // stream, 1.5MB session). It's worth noting that on 1mbps bitrate, limit of - // 24MB can capture approx 4 minutes of the call, and the default increase in - // size of the window (half of the window size) is approximately 2 minutes of - // the call. - quic_config.SetInitialSessionFlowControlWindowToSend( - kSessionReceiveWindowLimit); - quic_config.SetInitialStreamFlowControlWindowToSend( - kStreamReceiveWindowLimit); - quic_config.SetConnectionOptionsToSend(copt); - quic_config.SetClientConnectionOptions(copt); - if (quartc_session_config.max_time_before_crypto_handshake > - QuicTime::Delta::Zero()) { - quic_config.set_max_time_before_crypto_handshake( - quartc_session_config.max_time_before_crypto_handshake); - } - if (quartc_session_config.max_idle_time_before_crypto_handshake > - QuicTime::Delta::Zero()) { - quic_config.set_max_idle_time_before_crypto_handshake( - quartc_session_config.max_idle_time_before_crypto_handshake); - } - if (quartc_session_config.idle_network_timeout > QuicTime::Delta::Zero()) { - quic_config.SetIdleNetworkTimeout( - quartc_session_config.idle_network_timeout); - } - - // The ICE transport provides a unique 5-tuple for each connection. Save - // overhead by omitting the connection id. - quic_config.SetBytesForConnectionIdToSend(0); - - // Allow up to 1000 incoming streams at once. Quartc streams typically contain - // one audio or video frame and close immediately. However, when a video frame - // becomes larger than one packet, there is some delay between the start and - // end of each stream. The default maximum of 100 only leaves about 1 second - // of headroom (Quartc sends ~30 video frames per second) before QUIC starts - // to refuse incoming streams. Back-pressure should clear backlogs of - // incomplete streams, but targets 1 second for recovery. Increasing the - // number of open streams gives sufficient headroom to recover before QUIC - // refuses new streams. - quic_config.SetMaxBidirectionalStreamsToSend(1000); - - return quic_config; -} - -std::unique_ptr<QuicConnection> CreateQuicConnection( - QuicConnectionId connection_id, - const QuicSocketAddress& peer_address, - QuicConnectionHelperInterface* connection_helper, - QuicAlarmFactory* alarm_factory, - QuicPacketWriter* packet_writer, - Perspective perspective, - ParsedQuicVersionVector supported_versions) { - auto quic_connection = std::make_unique<QuicConnection>( - connection_id, peer_address, connection_helper, alarm_factory, - packet_writer, - /*owns_writer=*/false, perspective, supported_versions); - quic_connection->SetMaxPacketLength( - packet_writer->GetMaxPacketSize(peer_address)); - - QuicSentPacketManager& sent_packet_manager = - quic_connection->sent_packet_manager(); - UberReceivedPacketManager& received_packet_manager = - quic_connection->received_packet_manager(); - - // Default delayed ack time is 25ms. - // If data packets are sent less often (e.g. because p-time was modified), - // we would force acks to be sent every 25ms regardless, increasing - // overhead. Since generally we guarantee a packet every 20ms, changing - // this value should have miniscule effect on quality on good connections, - // but on poor connections, changing this number significantly reduced the - // number of ack-only packets. - // The p-time can go up to as high as 120ms, and when it does, it's - // when the low overhead is the most important thing. Ideally it should be - // above 120ms, but it cannot be higher than 0.5*RTO, which equals to 100ms. - received_packet_manager.set_max_ack_delay( - QuicTime::Delta::FromMilliseconds(100)); - sent_packet_manager.set_peer_max_ack_delay( - QuicTime::Delta::FromMilliseconds(100)); - - quic_connection->set_fill_up_link_during_probing(true); - - // We start ack decimation after 15 packets. Typically, we would see - // 1-2 crypto handshake packets, one media packet, and 10 probing packets. - // We want to get acks for the probing packets as soon as possible, - // but we can start using ack decimation right after first probing completes. - // The default was to not start ack decimation for the first 100 packets. - quic_connection->set_min_received_before_ack_decimation(15); - - return quic_connection; -} - -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.h deleted file mode 100644 index 665b8e7111d..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2017 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_QUARTC_QUARTC_FACTORY_H_ -#define QUICHE_QUIC_QUARTC_QUARTC_FACTORY_H_ - -#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h" -#include "net/third_party/quiche/src/quic/core/quic_connection.h" -#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_session.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" - -namespace quic { - -struct QuartcSessionConfig { - // If a pre-shared cryptographic key is available for this session, specify it - // here. This value will only be used if non-empty. - std::string pre_shared_key; - - // The maximum size of the packet can be written with the packet writer. - // 1200 bytes by default. - QuicPacketLength max_packet_size = 1200; - - // Timeouts for the crypto handshake. Set them to higher values to - // prevent closing the session before it started on a slow network. - // Zero entries are ignored and QUIC defaults are used in that case. - QuicTime::Delta max_idle_time_before_crypto_handshake = - QuicTime::Delta::Zero(); - QuicTime::Delta max_time_before_crypto_handshake = QuicTime::Delta::Zero(); - QuicTime::Delta idle_network_timeout = QuicTime::Delta::Zero(); - - // Tail loss probes (TLP) are enabled by default, but it may be useful to - // disable them in tests. We can also consider disabling them in production - // if we discover that tail loss probes add overhead in low bitrate audio. - bool enable_tail_loss_probe = true; -}; - -// Creates a new QuartcClientSession using the given configuration. -std::unique_ptr<QuartcSession> CreateQuartcClientSession( - const QuartcSessionConfig& quartc_session_config, - const QuicClock* clock, - QuicAlarmFactory* alarm_factory, - QuicConnectionHelperInterface* connection_helper, - const ParsedQuicVersionVector& supported_versions, - quiche::QuicheStringPiece server_crypto_config, - QuartcPacketTransport* packet_transport); - -// Configures global settings, such as supported quic versions. -// Must execute on QUIC thread. -void ConfigureGlobalQuicSettings(); - -// Must execute on QUIC thread. -QuicConfig CreateQuicConfig(const QuartcSessionConfig& quartc_session_config); - -std::unique_ptr<QuicConnection> CreateQuicConnection( - QuicConnectionId connection_id, - const QuicSocketAddress& peer_address, - QuicConnectionHelperInterface* connection_helper, - QuicAlarmFactory* alarm_factory, - QuicPacketWriter* packet_writer, - Perspective perspective, - ParsedQuicVersionVector supported_versions); - -} // namespace quic - -#endif // QUICHE_QUIC_QUARTC_QUARTC_FACTORY_H_ diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_fakes.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_fakes.h deleted file mode 100644 index 94bf1add1e7..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_fakes.h +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) 2017 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_QUARTC_QUARTC_FAKES_H_ -#define QUICHE_QUIC_QUARTC_QUARTC_FAKES_H_ - -#include <string> - -#include "net/third_party/quiche/src/quic/core/quic_clock.h" -#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" -#include "net/third_party/quiche/src/quic/core/quic_types.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_session.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_stream.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" - -namespace quic { - -class FakeQuartcEndpointDelegate : public QuartcEndpoint::Delegate { - public: - explicit FakeQuartcEndpointDelegate(QuartcStream::Delegate* stream_delegate, - const QuicClock* clock) - : stream_delegate_(stream_delegate), clock_(clock) {} - - void OnSessionCreated(QuartcSession* session) override { - CHECK_NE(session, nullptr); - session_ = session; - session_->StartCryptoHandshake(); - ++num_sessions_created_; - } - - void OnConnectionWritable() override { - QUIC_LOG(INFO) << "Connection writable!"; - if (!writable_time_.IsInitialized()) { - writable_time_ = clock_->Now(); - } - } - - // Called when peers have established forward-secure encryption - void OnCryptoHandshakeComplete() override { - QUIC_LOG(INFO) << "Crypto handshake complete!"; - crypto_handshake_time_ = clock_->Now(); - } - - // Called when connection closes locally, or remotely by peer. - void OnConnectionClosed(const QuicConnectionCloseFrame& /*frame*/, - ConnectionCloseSource /*source*/) override { - connected_ = false; - } - - // Called when an incoming QUIC stream is created. - void OnIncomingStream(QuartcStream* quartc_stream) override { - last_incoming_stream_ = quartc_stream; - last_incoming_stream_->SetDelegate(stream_delegate_); - } - - void OnMessageReceived(quiche::QuicheStringPiece message) override { - incoming_messages_.emplace_back(message); - } - - void OnMessageSent(int64_t datagram_id) override { - sent_datagram_ids_.push_back(datagram_id); - } - - void OnMessageAcked(int64_t datagram_id, - QuicTime receive_timestamp) override { - acked_datagram_id_to_receive_timestamp_.emplace(datagram_id, - receive_timestamp); - } - - void OnMessageLost(int64_t datagram_id) override { - lost_datagram_ids_.push_back(datagram_id); - } - - void OnCongestionControlChange(QuicBandwidth /*bandwidth_estimate*/, - QuicBandwidth /*pacing_rate*/, - QuicTime::Delta /*latest_rtt*/) override {} - - QuartcSession* session() { return session_; } - - int num_sessions_created() const { return num_sessions_created_; } - - QuartcStream* last_incoming_stream() const { return last_incoming_stream_; } - - // Returns all received messages. - const std::vector<std::string>& incoming_messages() const { - return incoming_messages_; - } - - // Returns all sent datagram ids in the order sent. - const std::vector<int64_t>& sent_datagram_ids() const { - return sent_datagram_ids_; - } - - // Returns all ACKEd datagram ids in the order ACKs were received. - const std::map<int64_t, QuicTime>& acked_datagram_id_to_receive_timestamp() - const { - return acked_datagram_id_to_receive_timestamp_; - } - - const std::vector<int64_t>& lost_datagram_ids() const { - return lost_datagram_ids_; - } - - bool connected() const { return connected_; } - QuicTime writable_time() const { return writable_time_; } - QuicTime crypto_handshake_time() const { return crypto_handshake_time_; } - - private: - // Current session. - QuartcSession* session_ = nullptr; - - // Number of new sessions created by the endpoint. - int num_sessions_created_ = 0; - - QuartcStream* last_incoming_stream_; - std::vector<std::string> incoming_messages_; - std::vector<int64_t> sent_datagram_ids_; - std::map<int64_t, QuicTime> acked_datagram_id_to_receive_timestamp_; - std::vector<int64_t> lost_datagram_ids_; - bool connected_ = true; - QuartcStream::Delegate* stream_delegate_; - QuicTime writable_time_ = QuicTime::Zero(); - QuicTime crypto_handshake_time_ = QuicTime::Zero(); - const QuicClock* clock_; -}; - -class FakeQuartcStreamDelegate : public QuartcStream::Delegate { - public: - size_t OnReceived(QuartcStream* stream, - iovec* iov, - size_t iov_length, - bool /*fin*/) override { - size_t bytes_consumed = 0; - for (size_t i = 0; i < iov_length; ++i) { - received_data_[stream->id()] += std::string( - static_cast<const char*>(iov[i].iov_base), iov[i].iov_len); - bytes_consumed += iov[i].iov_len; - } - return bytes_consumed; - } - - void OnClose(QuartcStream* stream) override { - errors_[stream->id()] = stream->stream_error(); - } - - void OnBufferChanged(QuartcStream* /*stream*/) override {} - - bool has_data() { return !received_data_.empty(); } - std::map<QuicStreamId, std::string> data() { return received_data_; } - - QuicRstStreamErrorCode stream_error(QuicStreamId id) { return errors_[id]; } - - private: - std::map<QuicStreamId, std::string> received_data_; - std::map<QuicStreamId, QuicRstStreamErrorCode> errors_; -}; - -} // namespace quic - -#endif // QUICHE_QUIC_QUARTC_QUARTC_FAKES_H_ diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter.h deleted file mode 100644 index fe3b083c695..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter.h +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) 2018 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_QUARTC_QUARTC_INTERVAL_COUNTER_H_ -#define QUICHE_QUIC_QUARTC_QUARTC_INTERVAL_COUNTER_H_ - -#include <stddef.h> -#include <vector> - -#include "net/third_party/quiche/src/quic/core/quic_interval.h" -#include "net/third_party/quiche/src/quic/core/quic_interval_set.h" - -namespace quic { - -// QuartcIntervalCounter counts the number of times each value appears within -// a set of potentially overlapping intervals. -// -// QuartcIntervalCounter is not intended for widespread use. Consider replacing -// it with a full interval-map if more use cases arise. -// -// QuartcIntervalCounter is only suitable for cases where the maximum count is -// expected to remain low. (For example, counting the number of times the same -// portions of stream data are lost.) It is inefficient when the maximum count -// becomes high. -template <typename T> -class QuartcIntervalCounter { - public: - // Adds |interval| to the counter. The count associated with each value in - // |interval| is incremented by one. |interval| may overlap with previous - // intervals added to the counter. - // - // For each possible value: - // - If the value is present in both |interval| and the counter, the count - // associated with that value is incremented by one. - // - If the value is present in |interval| but not counter, the count - // associated with that value is set to one (incremented from zero). - // - If the value is absent from |interval|, the count is unchanged. - // - // Time complexity is O(|MaxCount| * the complexity of adding an interval to a - // QuicIntervalSet). - void AddInterval(QuicInterval<T> interval); - - // Removes an interval from the counter. This method may be called to prune - // irrelevant intervals from the counter. This is useful to prevent unbounded - // growth. - // - // Time complexity is O(|MaxCount| * the complexity of removing an interval - // from a QuicIntervalSet). - void RemoveInterval(QuicInterval<T> interval); - - // Returns the maximum number of times any single value has appeared in - // intervals added to the counter. - // - // Time complexity is constant. - size_t MaxCount() const { return intervals_by_count_.size(); } - - // Returns the maximum number of times a particular value has appeared in - // intervals added to the counter. - // - // Time complexity is O(|MaxCount| * log(number of non-contiguous intervals)). - size_t Count(const T& value) const; - - private: - // Each entry in this vector represents the intervals of values counted at - // least i + 1 times, where i is the index of the entry. - // - // Whenever an interval is added to the counter, each value in the interval is - // added to the first entry which does not already contain that value. If - // part of an interval is already present in the last entry, a new entry is - // added containing that part. - // - // Note that this means each value present in one of the interval sets will be - // present in all previous sets. - std::vector<QuicIntervalSet<T>> intervals_by_count_; -}; - -template <typename T> -void QuartcIntervalCounter<T>::AddInterval(QuicInterval<T> interval) { - // After the Nth iteration, |leftover| contains the parts of |interval| that - // are already present in the first N entries. These parts of |interval| have - // been added to the counter more than N times. - QuicIntervalSet<T> leftover(interval); - for (auto& intervals : intervals_by_count_) { - QuicIntervalSet<T> tmp = leftover; - leftover.Intersection(intervals); - intervals.Union(tmp); - } - - // Whatever ranges are still in |leftover| are already in all the entries - // Add a new entry containing |leftover|. - if (!leftover.Empty()) { - intervals_by_count_.push_back(leftover); - } -} - -template <typename T> -void QuartcIntervalCounter<T>::RemoveInterval(QuicInterval<T> interval) { - // Remove the interval from each entry in the vector, popping any entries that - // become empty. - for (size_t i = intervals_by_count_.size(); i > 0; --i) { - intervals_by_count_[i - 1].Difference(interval); - if (intervals_by_count_[i - 1].Empty()) { - intervals_by_count_.pop_back(); - } - } -} - -template <typename T> -size_t QuartcIntervalCounter<T>::Count(const T& value) const { - // The index of the last entry containing |value| gives its count. - for (size_t i = intervals_by_count_.size(); i > 0; --i) { - if (intervals_by_count_[i - 1].Contains(value)) { - return i; - } - } - return 0; -} - -} // namespace quic - -#endif // QUICHE_QUIC_QUARTC_QUARTC_INTERVAL_COUNTER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter_test.cc deleted file mode 100644 index 028aaf2dab6..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter_test.cc +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/quartc/quartc_interval_counter.h" - -#include "net/third_party/quiche/src/quic/core/quic_interval.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" - -namespace quic { -namespace { - -class QuartcIntervalCounterTest : public QuicTest { - protected: - QuartcIntervalCounter<int> counter_; -}; - -void ExpectCount(const QuartcIntervalCounter<int>& counter, - QuicInterval<int> interval, - size_t count) { - for (int i = interval.min(); i < interval.max(); ++i) { - EXPECT_EQ(counter.Count(i), count) << "i=" << i; - } -} - -TEST_F(QuartcIntervalCounterTest, InitiallyEmpty) { - EXPECT_EQ(counter_.MaxCount(), 0u); -} - -TEST_F(QuartcIntervalCounterTest, SameInterval) { - counter_.AddInterval(QuicInterval<int>(0, 6)); - EXPECT_EQ(counter_.MaxCount(), 1u); - ExpectCount(counter_, QuicInterval<int>(0, 6), 1); - - counter_.AddInterval(QuicInterval<int>(0, 6)); - EXPECT_EQ(counter_.MaxCount(), 2u); - ExpectCount(counter_, QuicInterval<int>(0, 6), 2); -} - -TEST_F(QuartcIntervalCounterTest, DisjointIntervals) { - counter_.AddInterval(QuicInterval<int>(0, 5)); - EXPECT_EQ(counter_.MaxCount(), 1u); - ExpectCount(counter_, QuicInterval<int>(0, 5), 1); - ExpectCount(counter_, QuicInterval<int>(5, 10), 0); - - counter_.AddInterval(QuicInterval<int>(5, 10)); - EXPECT_EQ(counter_.MaxCount(), 1u); - ExpectCount(counter_, QuicInterval<int>(0, 5), 1); - ExpectCount(counter_, QuicInterval<int>(5, 10), 1); -} - -TEST_F(QuartcIntervalCounterTest, OverlappingIntervals) { - counter_.AddInterval(QuicInterval<int>(0, 6)); - EXPECT_EQ(counter_.MaxCount(), 1u); - ExpectCount(counter_, QuicInterval<int>(0, 6), 1); - ExpectCount(counter_, QuicInterval<int>(6, 10), 0); - - counter_.AddInterval(QuicInterval<int>(5, 10)); - EXPECT_EQ(counter_.MaxCount(), 2u); - ExpectCount(counter_, QuicInterval<int>(0, 5), 1); - EXPECT_EQ(counter_.Count(5), 2u); - ExpectCount(counter_, QuicInterval<int>(6, 10), 1); -} - -TEST_F(QuartcIntervalCounterTest, IntervalsWithGapThenOverlap) { - counter_.AddInterval(QuicInterval<int>(0, 4)); - EXPECT_EQ(counter_.MaxCount(), 1u); - ExpectCount(counter_, QuicInterval<int>(0, 4), 1); - ExpectCount(counter_, QuicInterval<int>(4, 10), 0); - - counter_.AddInterval(QuicInterval<int>(7, 10)); - EXPECT_EQ(counter_.MaxCount(), 1u); - ExpectCount(counter_, QuicInterval<int>(0, 4), 1); - ExpectCount(counter_, QuicInterval<int>(4, 7), 0); - ExpectCount(counter_, QuicInterval<int>(7, 10), 1); - - counter_.AddInterval(QuicInterval<int>(3, 8)); - EXPECT_EQ(counter_.MaxCount(), 2u); - ExpectCount(counter_, QuicInterval<int>(0, 3), 1); - EXPECT_EQ(counter_.Count(3), 2u); - ExpectCount(counter_, QuicInterval<int>(4, 7), 1); - EXPECT_EQ(counter_.Count(7), 2u); - ExpectCount(counter_, QuicInterval<int>(8, 10), 1); -} - -TEST_F(QuartcIntervalCounterTest, RemoveIntervals) { - counter_.AddInterval(QuicInterval<int>(0, 5)); - EXPECT_EQ(counter_.MaxCount(), 1u); - ExpectCount(counter_, QuicInterval<int>(0, 5), 1); - - counter_.AddInterval(QuicInterval<int>(4, 10)); - EXPECT_EQ(counter_.MaxCount(), 2u); - ExpectCount(counter_, QuicInterval<int>(0, 4), 1); - EXPECT_EQ(counter_.Count(4), 2u); - ExpectCount(counter_, QuicInterval<int>(5, 10), 1); - - counter_.RemoveInterval(QuicInterval<int>(0, 5)); - EXPECT_EQ(counter_.MaxCount(), 1u); - ExpectCount(counter_, QuicInterval<int>(0, 5), 0); - ExpectCount(counter_, QuicInterval<int>(5, 10), 1); - - counter_.RemoveInterval(QuicInterval<int>(5, 10)); - EXPECT_EQ(counter_.MaxCount(), 0u); - ExpectCount(counter_, QuicInterval<int>(0, 10), 0); -} - -} // namespace -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer.cc deleted file mode 100644 index 376fac02c8c..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer.cc +++ /dev/null @@ -1,271 +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 "net/third_party/quiche/src/quic/quartc/quartc_multiplexer.h" - -#include <cstdint> -#include <utility> - -#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h" -#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" - -namespace quic { - -QuartcSendChannel::QuartcSendChannel(QuartcMultiplexer* multiplexer, - uint64_t id, - QuicBufferAllocator* allocator, - Delegate* delegate) - : multiplexer_(multiplexer), - id_(id), - encoded_length_(QuicDataWriter::GetVarInt62Len(id_)), - allocator_(allocator), - delegate_(delegate) {} - -QuartcStream* QuartcSendChannel::CreateOutgoingBidirectionalStream() { - if (!session_) { - QUIC_LOG(DFATAL) << "Session is not ready to write yet; channel_id=" << id_; - return nullptr; - } - QuicMemSlice id_slice = EncodeChannelId(); - - QuartcStream* stream = session_->CreateOutgoingBidirectionalStream(); - QuicConsumedData consumed = - stream->WriteMemSlices(QuicMemSliceSpan(&id_slice), /*fin=*/false); - DCHECK_EQ(consumed.bytes_consumed, encoded_length_); - return stream; -} - -bool QuartcSendChannel::SendOrQueueMessage(QuicMemSliceSpan message, - int64_t datagram_id) { - if (!session_) { - QUIC_LOG(DFATAL) << "Session is not ready to write yet; channel_id=" << id_ - << "datagram size=" << message.total_length(); - return false; - } - QuicMemSliceStorage storage(nullptr, 0, nullptr, 0); // Empty storage. - storage.Append(EncodeChannelId()); - - message.ConsumeAll( - [&storage](QuicMemSlice slice) { storage.Append(std::move(slice)); }); - - // Allocate a unique datagram id so that notifications can be routed back to - // the right send channel. - int64_t unique_datagram_id = multiplexer_->AllocateDatagramId(this); - multiplexer_to_user_datagram_ids_[unique_datagram_id] = datagram_id; - - return session_->SendOrQueueMessage(storage.ToSpan(), unique_datagram_id); -} - -void QuartcSendChannel::OnMessageSent(int64_t datagram_id) { - // Map back to the caller-chosen |datagram_id|. - datagram_id = multiplexer_to_user_datagram_ids_[datagram_id]; - delegate_->OnMessageSent(datagram_id); -} - -void QuartcSendChannel::OnMessageAcked(int64_t datagram_id, - QuicTime receive_timestamp) { - // Map back to the caller-chosen |datagram_id|. - auto it = multiplexer_to_user_datagram_ids_.find(datagram_id); - if (it == multiplexer_to_user_datagram_ids_.end()) { - QUIC_LOG(DFATAL) << "Datagram acked/lost multiple times; datagram_id=" - << datagram_id; - return; - } - delegate_->OnMessageAcked(it->second, receive_timestamp); - multiplexer_to_user_datagram_ids_.erase(it); -} - -void QuartcSendChannel::OnMessageLost(int64_t datagram_id) { - // Map back to the caller-chosen |datagram_id|. - auto it = multiplexer_to_user_datagram_ids_.find(datagram_id); - if (it == multiplexer_to_user_datagram_ids_.end()) { - QUIC_LOG(DFATAL) << "Datagram acked/lost multiple times; datagram_id=" - << datagram_id; - return; - } - delegate_->OnMessageLost(it->second); - multiplexer_to_user_datagram_ids_.erase(it); -} - -void QuartcSendChannel::OnSessionCreated(QuartcSession* session) { - session_ = session; -} - -QuicMemSlice QuartcSendChannel::EncodeChannelId() { - QuicUniqueBufferPtr buffer = MakeUniqueBuffer(allocator_, encoded_length_); - QuicDataWriter writer(encoded_length_, buffer.get()); - writer.WriteVarInt62(id_); - return QuicMemSlice(std::move(buffer), encoded_length_); -} - -QuartcMultiplexer::QuartcMultiplexer( - QuicBufferAllocator* allocator, - QuartcSessionEventDelegate* session_delegate, - QuartcReceiveChannel* default_receive_channel) - : allocator_(allocator), - session_delegate_(session_delegate), - default_receive_channel_(default_receive_channel) { - CHECK_NE(session_delegate_, nullptr); - CHECK_NE(default_receive_channel_, nullptr); -} - -QuartcSendChannel* QuartcMultiplexer::CreateSendChannel( - uint64_t channel_id, - QuartcSendChannel::Delegate* delegate) { - send_channels_.push_back(std::make_unique<QuartcSendChannel>( - this, channel_id, allocator_, delegate)); - if (session_) { - send_channels_.back()->OnSessionCreated(session_); - } - return send_channels_.back().get(); -} - -void QuartcMultiplexer::RegisterReceiveChannel(uint64_t channel_id, - QuartcReceiveChannel* channel) { - if (channel == nullptr) { - receive_channels_.erase(channel_id); - return; - } - auto& registered_channel = receive_channels_[channel_id]; - if (registered_channel) { - QUIC_LOG(DFATAL) << "Attempted to overwrite existing channel_id=" - << channel_id; - return; - } - registered_channel = channel; -} - -int64_t QuartcMultiplexer::AllocateDatagramId(QuartcSendChannel* channel) { - send_channels_by_datagram_id_[next_datagram_id_] = channel; - return next_datagram_id_++; -} - -void QuartcMultiplexer::OnSessionCreated(QuartcSession* session) { - for (auto& channel : send_channels_) { - channel->OnSessionCreated(session); - } - session_ = session; - session_delegate_->OnSessionCreated(session); -} - -void QuartcMultiplexer::OnCryptoHandshakeComplete() { - session_delegate_->OnCryptoHandshakeComplete(); -} - -void QuartcMultiplexer::OnConnectionWritable() { - session_delegate_->OnConnectionWritable(); -} - -void QuartcMultiplexer::OnIncomingStream(QuartcStream* stream) { - stream->SetDelegate(this); -} - -void QuartcMultiplexer::OnCongestionControlChange( - QuicBandwidth bandwidth_estimate, - QuicBandwidth pacing_rate, - QuicTime::Delta latest_rtt) { - session_delegate_->OnCongestionControlChange(bandwidth_estimate, pacing_rate, - latest_rtt); -} - -void QuartcMultiplexer::OnConnectionClosed( - const QuicConnectionCloseFrame& frame, - ConnectionCloseSource source) { - session_delegate_->OnConnectionClosed(frame, source); -} - -void QuartcMultiplexer::OnMessageReceived(quiche::QuicheStringPiece message) { - QuicDataReader reader(message); - QuicVariableLengthIntegerLength channel_id_length = - reader.PeekVarInt62Length(); - - uint64_t channel_id; - if (!reader.ReadVarInt62(&channel_id)) { - QUIC_LOG(DFATAL) << "Received message without properly encoded channel id"; - return; - } - - QuartcReceiveChannel* channel = default_receive_channel_; - auto it = receive_channels_.find(channel_id); - if (it != receive_channels_.end()) { - channel = it->second; - } - - channel->OnMessageReceived(channel_id, message.substr(channel_id_length)); -} - -void QuartcMultiplexer::OnMessageSent(int64_t datagram_id) { - auto it = send_channels_by_datagram_id_.find(datagram_id); - if (it == send_channels_by_datagram_id_.end()) { - return; - } - it->second->OnMessageSent(datagram_id); -} - -void QuartcMultiplexer::OnMessageAcked(int64_t datagram_id, - QuicTime receive_timestamp) { - auto it = send_channels_by_datagram_id_.find(datagram_id); - if (it == send_channels_by_datagram_id_.end()) { - return; - } - it->second->OnMessageAcked(datagram_id, receive_timestamp); - send_channels_by_datagram_id_.erase(it); -} - -void QuartcMultiplexer::OnMessageLost(int64_t datagram_id) { - auto it = send_channels_by_datagram_id_.find(datagram_id); - if (it == send_channels_by_datagram_id_.end()) { - return; - } - it->second->OnMessageLost(datagram_id); - send_channels_by_datagram_id_.erase(it); -} - -size_t QuartcMultiplexer::OnReceived(QuartcStream* stream, - iovec* iov, - size_t iov_length, - bool /*fin*/) { - if (iov == nullptr || iov_length <= 0) { - return 0; - } - - QuicDataReader reader(static_cast<char*>(iov[0].iov_base), iov[0].iov_len); - QuicVariableLengthIntegerLength channel_id_length = - reader.PeekVarInt62Length(); - - uint64_t channel_id; - if (reader.BytesRemaining() >= channel_id_length) { - // Fast path, have enough data to read immediately. - if (!reader.ReadVarInt62(&channel_id)) { - return 0; - } - } else { - // Slow path, need to coalesce multiple iovecs. - std::string data; - for (size_t i = 0; i < iov_length; ++i) { - data += std::string(static_cast<char*>(iov[i].iov_base), iov[i].iov_len); - } - QuicDataReader combined_reader(data); - if (!combined_reader.ReadVarInt62(&channel_id)) { - return 0; - } - } - - QuartcReceiveChannel* channel = default_receive_channel_; - auto it = receive_channels_.find(channel_id); - if (it != receive_channels_.end()) { - channel = it->second; - } - channel->OnIncomingStream(channel_id, stream); - return channel_id_length; -} - -void QuartcMultiplexer::OnClose(QuartcStream* /*stream*/) {} - -void QuartcMultiplexer::OnBufferChanged(QuartcStream* /*stream*/) {} - -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer.h deleted file mode 100644 index 1e6c2e5dbdf..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer.h +++ /dev/null @@ -1,190 +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_QUARTC_QUARTC_MULTIPLEXER_H_ -#define QUICHE_QUIC_QUARTC_QUARTC_MULTIPLEXER_H_ - -#include <cstdint> - -#include "net/third_party/quiche/src/quic/core/quic_time.h" -#include "net/third_party/quiche/src/quic/core/quic_types.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_session.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_stream.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" - -namespace quic { - -class QuartcMultiplexer; - -// A single, multiplexed send channel within a Quartc session. A send channel -// wraps send-side operations with an outgoing multiplex id. -class QuartcSendChannel { - public: - class Delegate { - public: - virtual ~Delegate() = default; - - // Called when a message with |datagram_id| is sent by this channel. - virtual void OnMessageSent(int64_t datagram_id) = 0; - - // Called when a message sent on this channel with |datagram_id| is acked. - // |receive_timestamp| indicates when the peer received this message, - // according to the peer's clock. - virtual void OnMessageAcked(int64_t datagram_id, - QuicTime receive_timestamp) = 0; - - // Called when a message sent on this channel with |datagram_id| is lost. - virtual void OnMessageLost(int64_t datagram_id) = 0; - }; - - QuartcSendChannel(QuartcMultiplexer* multiplexer, - uint64_t id, - QuicBufferAllocator* allocator, - Delegate* delegate); - virtual ~QuartcSendChannel() = default; - - // Creates a new, outgoing stream on this channel. - // - // Automatically writes the channel id to the start of the stream. The caller - // SHOULD create a |ScopedPacketFlusher| before calling this function to - // prevent the channel id from being sent by itself. - QuartcStream* CreateOutgoingBidirectionalStream(); - - // Writes |message| to the session. Prepends the channel's send id before any - // following message data. - bool SendOrQueueMessage(QuicMemSliceSpan message, int64_t datagram_id); - - // Gets the current largest message payload for this channel. Returns the - // largest payload size supported by the session minus overhead required to - // encode this channel's send id. - QuicPacketLength GetCurrentLargestMessagePayload() const; - - // The following are called by the multiplexer to deliver message - // notifications. The |datagram_id| passed to these is unique per-message, - // and must be translated back to the sender's chosen datagram_id. - void OnMessageSent(int64_t datagram_id); - void OnMessageAcked(int64_t datagram_id, QuicTime receive_timestamp); - void OnMessageLost(int64_t datagram_id); - void OnSessionCreated(QuartcSession* session); - - private: - // Creates a mem slice containing a varint-62 encoded channel id. - QuicMemSlice EncodeChannelId(); - - QuartcMultiplexer* const multiplexer_; - const uint64_t id_; - const QuicVariableLengthIntegerLength encoded_length_; - QuicBufferAllocator* const allocator_; - Delegate* const delegate_; - - QuartcSession* session_; - - // Map of multiplexer-chosen to user/caller-specified datagram ids. The user - // may specify any number as a datagram's id. This number does not have to be - // unique across channels (nor even within a single channel). In order - // to demux sent, acked, and lost messages, the multiplexer assigns a globally - // unique id to each message. This map is used to restore the original caller - // datagram id before issuing callbacks. - QuicHashMap<int64_t, int64_t> multiplexer_to_user_datagram_ids_; -}; - -// A single, multiplexed receive channel within a Quartc session. A receive -// channel is a delegate which accepts incoming streams and datagrams on one (or -// more) channel ids. -class QuartcReceiveChannel { - public: - virtual ~QuartcReceiveChannel() = default; - - // Called when a new incoming stream arrives on this channel. - virtual void OnIncomingStream(uint64_t channel_id, QuartcStream* stream) = 0; - - // Called when a message is recieved by this channel. - virtual void OnMessageReceived(uint64_t channel_id, - quiche::QuicheStringPiece message) = 0; -}; - -// Delegate for session-wide events. -class QuartcSessionEventDelegate { - public: - virtual ~QuartcSessionEventDelegate() = default; - - virtual void OnSessionCreated(QuartcSession* session) = 0; - virtual void OnCryptoHandshakeComplete() = 0; - virtual void OnConnectionWritable() = 0; - virtual void OnCongestionControlChange(QuicBandwidth bandwidth_estimate, - QuicBandwidth pacing_rate, - QuicTime::Delta latest_rtt) = 0; - virtual void OnConnectionClosed(const QuicConnectionCloseFrame& frame, - ConnectionCloseSource source) = 0; -}; - -// A multiplexer capable of sending and receiving data on multiple channels. -class QuartcMultiplexer : public QuartcEndpoint::Delegate, - public QuartcStream::Delegate { - public: - // Creates a new multiplexer. |session_delegate| handles all session-wide - // events, while |default_receive_channel| handles incoming data on unknown - // or unregistered channel ids. Neither |session_delegate| nor - // |default_receive_channel| may be nullptr, and both must outlive the - // multiplexer. - QuartcMultiplexer(QuicBufferAllocator* allocator, - QuartcSessionEventDelegate* session_delegate, - QuartcReceiveChannel* default_receive_channel); - - // Creates a new send channel. The channel is owned by the multiplexer, and - // references to it must not outlive the multiplexer. - QuartcSendChannel* CreateSendChannel(uint64_t channel_id, - QuartcSendChannel::Delegate* delegate); - - // Registers a receiver for incoming data on |channel_id|. - void RegisterReceiveChannel(uint64_t channel_id, - QuartcReceiveChannel* channel); - - // Allocates a datagram id to |channel|. - int64_t AllocateDatagramId(QuartcSendChannel* channel); - - // QuartcEndpoint::Delegate overrides. - void OnSessionCreated(QuartcSession* session) override; - - // QuartcSession::Delegate overrides. - void OnCryptoHandshakeComplete() override; - void OnConnectionWritable() override; - void OnIncomingStream(QuartcStream* stream) override; - void OnCongestionControlChange(QuicBandwidth bandwidth_estimate, - QuicBandwidth pacing_rate, - QuicTime::Delta latest_rtt) override; - void OnConnectionClosed(const QuicConnectionCloseFrame& frame, - ConnectionCloseSource source) override; - void OnMessageReceived(quiche::QuicheStringPiece message) override; - void OnMessageSent(int64_t datagram_id) override; - void OnMessageAcked(int64_t datagram_id, QuicTime receive_timestamp) override; - void OnMessageLost(int64_t datagram_id) override; - - // QuartcStream::Delegate overrides. - size_t OnReceived(QuartcStream* stream, - iovec* iov, - size_t iov_length, - bool fin) override; - void OnClose(QuartcStream* stream) override; - void OnBufferChanged(QuartcStream* stream) override; - - private: - QuicBufferAllocator* const allocator_; - QuartcSessionEventDelegate* const session_delegate_; - - QuartcSession* session_ = nullptr; - std::vector<std::unique_ptr<QuartcSendChannel>> send_channels_; - QuicHashMap<uint64_t, QuartcReceiveChannel*> receive_channels_; - QuartcReceiveChannel* default_receive_channel_ = nullptr; - - int64_t next_datagram_id_ = 1; - QuicHashMap<int64_t, QuartcSendChannel*> send_channels_by_datagram_id_; -}; - -} // namespace quic - -#endif // QUICHE_QUIC_QUARTC_QUARTC_MULTIPLEXER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer_test.cc deleted file mode 100644 index 024b67d8a94..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_multiplexer_test.cc +++ /dev/null @@ -1,496 +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 "net/third_party/quiche/src/quic/quartc/quartc_multiplexer.h" - -#include <memory> -#include <utility> - -#include "net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h" -#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" -#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h" -#include "net/third_party/quiche/src/quic/core/quic_time.h" -#include "net/third_party/quiche/src/quic/core/quic_types.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h" -#include "net/third_party/quiche/src/quic/quartc/counting_packet_filter.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_fakes.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_session.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_stream.h" -#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h" -#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" -#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h" -#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" - -namespace quic { -namespace { - -using ::testing::ElementsAreArray; -using ::testing::Gt; -using ::testing::IsEmpty; -using ::testing::Pair; - -constexpr QuicTime::Delta kPropagationDelay = - QuicTime::Delta::FromMilliseconds(10); - -class FakeSessionEventDelegate : public QuartcSessionEventDelegate { - public: - void OnSessionCreated(QuartcSession* session) override { - session->StartCryptoHandshake(); - session_ = session; - } - - void OnConnectionWritable() override { ++writable_count_; } - - void OnCryptoHandshakeComplete() override { ++handshake_count_; } - - void OnConnectionClosed(const QuicConnectionCloseFrame& frame, - ConnectionCloseSource source) override { - error_ = frame.quic_error_code; - close_source_ = source; - } - - void OnCongestionControlChange(QuicBandwidth bandwidth_estimate, - QuicBandwidth pacing_rate, - QuicTime::Delta latest_rtt) override { - latest_bandwidth_estimate_ = bandwidth_estimate; - latest_pacing_rate_ = pacing_rate; - latest_rtt_ = latest_rtt; - } - - QuartcSession* session() { return session_; } - int writable_count() const { return writable_count_; } - int handshake_count() const { return handshake_count_; } - QuicErrorCode error() const { return error_; } - ConnectionCloseSource close_source() const { return close_source_; } - QuicBandwidth latest_bandwidth_estimate() const { - return latest_bandwidth_estimate_; - } - QuicBandwidth latest_pacing_rate() const { return latest_pacing_rate_; } - QuicTime::Delta latest_rtt() const { return latest_rtt_; } - - private: - QuartcSession* session_ = nullptr; - int writable_count_ = 0; - int handshake_count_ = 0; - QuicErrorCode error_ = QUIC_NO_ERROR; - ConnectionCloseSource close_source_; - QuicBandwidth latest_bandwidth_estimate_ = QuicBandwidth::Zero(); - QuicBandwidth latest_pacing_rate_ = QuicBandwidth::Zero(); - QuicTime::Delta latest_rtt_ = QuicTime::Delta::Zero(); -}; - -class FakeSendDelegate : public QuartcSendChannel::Delegate { - public: - void OnMessageSent(int64_t datagram_id) override { - datagrams_sent_.push_back(datagram_id); - } - - void OnMessageAcked(int64_t datagram_id, - QuicTime receive_timestamp) override { - datagrams_acked_.push_back({datagram_id, receive_timestamp}); - } - - void OnMessageLost(int64_t datagram_id) override { - datagrams_lost_.push_back(datagram_id); - } - - const std::vector<int64_t>& datagrams_sent() const { return datagrams_sent_; } - const std::vector<std::pair<int64_t, QuicTime>>& datagrams_acked() const { - return datagrams_acked_; - } - const std::vector<int64_t>& datagrams_lost() const { return datagrams_lost_; } - - private: - std::vector<int64_t> datagrams_sent_; - std::vector<std::pair<int64_t, QuicTime>> datagrams_acked_; - std::vector<int64_t> datagrams_lost_; -}; - -class FakeReceiveDelegate : public QuartcReceiveChannel, - public QuartcStream::Delegate { - public: - const std::vector<std::pair<uint64_t, std::string>> messages_received() - const { - return messages_received_; - } - - void OnIncomingStream(uint64_t channel_id, QuartcStream* stream) override { - stream->SetDelegate(this); - stream_to_channel_id_[stream] = channel_id; - } - - void OnMessageReceived(uint64_t channel_id, - quiche::QuicheStringPiece message) override { - messages_received_.emplace_back(channel_id, message); - } - - // Stream delegate overrides. - size_t OnReceived(QuartcStream* stream, - iovec* iov, - size_t iov_length, - bool fin) override { - if (!fin) { - return 0; - } - - size_t bytes = 0; - std::string message; - for (size_t i = 0; i < iov_length; ++i) { - message += - std::string(static_cast<char*>(iov[i].iov_base), iov[i].iov_len); - bytes += iov[i].iov_len; - } - QUIC_LOG(INFO) << "Received " << bytes << " byte message on channel " - << stream_to_channel_id_[stream]; - messages_received_.emplace_back(stream_to_channel_id_[stream], message); - return bytes; - } - - void OnClose(QuartcStream* stream) override { - stream_to_channel_id_.erase(stream); - } - - void OnBufferChanged(QuartcStream* /*stream*/) override {} - - private: - std::vector<std::pair<uint64_t, std::string>> messages_received_; - QuicUnorderedMap<QuartcStream*, uint64_t> stream_to_channel_id_; -}; - -class QuartcMultiplexerTest : public QuicTest { - public: - QuartcMultiplexerTest() - : simulator_(), - client_transport_(&simulator_, - "client_transport", - "server_transport", - 10 * kDefaultMaxPacketSize), - server_transport_(&simulator_, - "server_transport", - "client_transport", - 10 * kDefaultMaxPacketSize), - client_filter_(&simulator_, "client_filter", &client_transport_), - client_server_link_(&client_filter_, - &server_transport_, - QuicBandwidth::FromKBitsPerSecond(10 * 1000), - kPropagationDelay), - client_multiplexer_(simulator_.GetStreamSendBufferAllocator(), - &client_session_delegate_, - &client_default_receiver_), - server_multiplexer_(simulator_.GetStreamSendBufferAllocator(), - &server_session_delegate_, - &server_default_receiver_), - client_endpoint_(std::make_unique<QuartcClientEndpoint>( - simulator_.GetAlarmFactory(), - simulator_.GetClock(), - simulator_.GetRandomGenerator(), - &client_multiplexer_, - quic::QuartcSessionConfig(), - /*serialized_server_config=*/"")), - server_endpoint_(std::make_unique<QuartcServerEndpoint>( - simulator_.GetAlarmFactory(), - simulator_.GetClock(), - simulator_.GetRandomGenerator(), - &server_multiplexer_, - quic::QuartcSessionConfig())) { - // TODO(b/150224094): Re-enable TLS handshake. - // TODO(b/150236522): Parametrize by QUIC version. - quic::test::DisableQuicVersionsWithTls(); - } - - void Connect() { - client_endpoint_->Connect(&client_transport_); - server_endpoint_->Connect(&server_transport_); - ASSERT_TRUE(simulator_.RunUntil([this]() { - return client_session_delegate_.writable_count() > 0 && - server_session_delegate_.writable_count() > 0; - })); - } - - void Disconnect() { - client_session_delegate_.session()->CloseConnection("test"); - server_session_delegate_.session()->CloseConnection("test"); - } - - protected: - QuartcMultiplexer* client_multiplexer() { return &client_multiplexer_; } - - QuartcMultiplexer* server_multiplexer() { return &server_multiplexer_; } - - simulator::Simulator simulator_; - - simulator::SimulatedQuartcPacketTransport client_transport_; - simulator::SimulatedQuartcPacketTransport server_transport_; - simulator::CountingPacketFilter client_filter_; - simulator::SymmetricLink client_server_link_; - - FakeSessionEventDelegate client_session_delegate_; - FakeSessionEventDelegate server_session_delegate_; - - FakeReceiveDelegate client_default_receiver_; - FakeReceiveDelegate server_default_receiver_; - - QuartcMultiplexer client_multiplexer_; - QuartcMultiplexer server_multiplexer_; - - std::unique_ptr<QuartcClientEndpoint> client_endpoint_; - std::unique_ptr<QuartcServerEndpoint> server_endpoint_; -}; - -TEST_F(QuartcMultiplexerTest, MultiplexMessages) { - Connect(); - - FakeSendDelegate send_delegate_1; - QuartcSendChannel* send_channel_1 = - client_multiplexer()->CreateSendChannel(1, &send_delegate_1); - FakeSendDelegate send_delegate_2; - QuartcSendChannel* send_channel_2 = - client_multiplexer()->CreateSendChannel(2, &send_delegate_2); - - FakeReceiveDelegate receive_delegate_1; - server_multiplexer()->RegisterReceiveChannel(1, &receive_delegate_1); - - int num_messages = 10; - std::vector<std::pair<uint64_t, std::string>> messages_1; - messages_1.reserve(num_messages); - std::vector<std::pair<uint64_t, std::string>> messages_2; - messages_2.reserve(num_messages); - std::vector<int64_t> messages_sent_1; - std::vector<int64_t> messages_sent_2; - std::vector<testing::Matcher<std::pair<int64_t, QuicTime>>> ack_matchers_1; - std::vector<testing::Matcher<std::pair<int64_t, QuicTime>>> ack_matchers_2; - for (int i = 0; i < num_messages; ++i) { - messages_1.emplace_back(1, quiche::QuicheStrCat("message for 1: ", i)); - test::QuicTestMemSliceVector slice_1( - {std::make_pair(const_cast<char*>(messages_1.back().second.data()), - messages_1.back().second.size())}); - send_channel_1->SendOrQueueMessage(slice_1.span(), i); - messages_sent_1.push_back(i); - ack_matchers_1.push_back(Pair(i, Gt(QuicTime::Zero()))); - - messages_2.emplace_back(2, quiche::QuicheStrCat("message for 2: ", i)); - test::QuicTestMemSliceVector slice_2( - {std::make_pair(const_cast<char*>(messages_2.back().second.data()), - messages_2.back().second.size())}); - // Use i + 5 as the datagram id for channel 2, so that some of the ids - // overlap and some are disjoint. - send_channel_2->SendOrQueueMessage(slice_2.span(), i + 5); - messages_sent_2.push_back(i + 5); - ack_matchers_2.push_back(Pair(i + 5, Gt(QuicTime::Zero()))); - } - - EXPECT_TRUE(simulator_.RunUntil([&send_delegate_1, &send_delegate_2]() { - return send_delegate_1.datagrams_acked().size() == 10 && - send_delegate_2.datagrams_acked().size() == 10; - })); - - EXPECT_EQ(send_delegate_1.datagrams_sent(), messages_sent_1); - EXPECT_EQ(send_delegate_2.datagrams_sent(), messages_sent_2); - - EXPECT_EQ(receive_delegate_1.messages_received(), messages_1); - EXPECT_EQ(server_default_receiver_.messages_received(), messages_2); - - EXPECT_THAT(send_delegate_1.datagrams_acked(), - ElementsAreArray(ack_matchers_1)); - EXPECT_THAT(send_delegate_2.datagrams_acked(), - ElementsAreArray(ack_matchers_2)); -} - -TEST_F(QuartcMultiplexerTest, MultiplexStreams) { - FakeSendDelegate send_delegate_1; - QuartcSendChannel* send_channel_1 = - client_multiplexer()->CreateSendChannel(1, &send_delegate_1); - FakeSendDelegate send_delegate_2; - QuartcSendChannel* send_channel_2 = - client_multiplexer()->CreateSendChannel(2, &send_delegate_2); - - FakeQuartcStreamDelegate fake_send_stream_delegate; - - FakeReceiveDelegate receive_delegate_1; - server_multiplexer()->RegisterReceiveChannel(1, &receive_delegate_1); - - Connect(); - - int num_messages = 10; - std::vector<std::pair<uint64_t, std::string>> messages_1; - messages_1.reserve(num_messages); - std::vector<std::pair<uint64_t, std::string>> messages_2; - messages_2.reserve(num_messages); - for (int i = 0; i < num_messages; ++i) { - messages_1.emplace_back(1, quiche::QuicheStrCat("message for 1: ", i)); - test::QuicTestMemSliceVector slice_1( - {std::make_pair(const_cast<char*>(messages_1.back().second.data()), - messages_1.back().second.size())}); - QuartcStream* stream_1 = - send_channel_1->CreateOutgoingBidirectionalStream(); - stream_1->SetDelegate(&fake_send_stream_delegate); - stream_1->WriteMemSlices(slice_1.span(), /*fin=*/true); - - messages_2.emplace_back(2, quiche::QuicheStrCat("message for 2: ", i)); - test::QuicTestMemSliceVector slice_2( - {std::make_pair(const_cast<char*>(messages_2.back().second.data()), - messages_2.back().second.size())}); - QuartcStream* stream_2 = - send_channel_2->CreateOutgoingBidirectionalStream(); - stream_2->SetDelegate(&fake_send_stream_delegate); - stream_2->WriteMemSlices(slice_2.span(), /*fin=*/true); - } - - EXPECT_TRUE(simulator_.RunUntilOrTimeout( - [this, &receive_delegate_1]() { - return receive_delegate_1.messages_received().size() == 10 && - server_default_receiver_.messages_received().size() == 10; - }, - QuicTime::Delta::FromSeconds(5))); - - EXPECT_EQ(receive_delegate_1.messages_received(), messages_1); - EXPECT_EQ(server_default_receiver_.messages_received(), messages_2); -} - -// Tests that datagram-lost callbacks are invoked on the right send channel -// delegate, and that they work with overlapping datagram ids. -TEST_F(QuartcMultiplexerTest, MultiplexLostDatagrams) { - Connect(); - ASSERT_TRUE(simulator_.RunUntil([this]() { - return client_session_delegate_.handshake_count() > 0 && - server_session_delegate_.handshake_count() > 0; - })); - - // Just drop everything we try to send. - client_filter_.set_packets_to_drop(30); - - FakeSendDelegate send_delegate_1; - QuartcSendChannel* send_channel_1 = - client_multiplexer()->CreateSendChannel(1, &send_delegate_1); - FakeSendDelegate send_delegate_2; - QuartcSendChannel* send_channel_2 = - client_multiplexer()->CreateSendChannel(2, &send_delegate_2); - - FakeQuartcStreamDelegate fake_send_stream_delegate; - - FakeReceiveDelegate receive_delegate_1; - server_multiplexer()->RegisterReceiveChannel(1, &receive_delegate_1); - - int num_messages = 10; - std::vector<std::pair<uint64_t, std::string>> messages_1; - messages_1.reserve(num_messages); - std::vector<std::pair<uint64_t, std::string>> messages_2; - messages_2.reserve(num_messages); - std::vector<int64_t> messages_sent_1; - std::vector<int64_t> messages_sent_2; - for (int i = 0; i < num_messages; ++i) { - messages_1.emplace_back(1, quiche::QuicheStrCat("message for 1: ", i)); - test::QuicTestMemSliceVector slice_1( - {std::make_pair(const_cast<char*>(messages_1.back().second.data()), - messages_1.back().second.size())}); - send_channel_1->SendOrQueueMessage(slice_1.span(), i); - messages_sent_1.push_back(i); - - messages_2.emplace_back(2, quiche::QuicheStrCat("message for 2: ", i)); - test::QuicTestMemSliceVector slice_2( - {std::make_pair(const_cast<char*>(messages_2.back().second.data()), - messages_2.back().second.size())}); - // Use i + 5 as the datagram id for channel 2, so that some of the ids - // overlap and some are disjoint. - send_channel_2->SendOrQueueMessage(slice_2.span(), i + 5); - messages_sent_2.push_back(i + 5); - } - - // Now send something retransmittable to prompt loss detection. - // If we never send anything retransmittable, we will never get acks, and - // never detect losses. - messages_1.emplace_back( - 1, quiche::QuicheStrCat("message for 1: ", num_messages)); - test::QuicTestMemSliceVector slice( - {std::make_pair(const_cast<char*>(messages_1.back().second.data()), - messages_1.back().second.size())}); - QuartcStream* stream_1 = send_channel_1->CreateOutgoingBidirectionalStream(); - stream_1->SetDelegate(&fake_send_stream_delegate); - stream_1->WriteMemSlices(slice.span(), /*fin=*/true); - - EXPECT_TRUE(simulator_.RunUntilOrTimeout( - [&send_delegate_1, &send_delegate_2]() { - return send_delegate_1.datagrams_lost().size() == 10 && - send_delegate_2.datagrams_lost().size() == 10; - }, - QuicTime::Delta::FromSeconds(60))); - - EXPECT_EQ(send_delegate_1.datagrams_lost(), messages_sent_1); - EXPECT_EQ(send_delegate_2.datagrams_lost(), messages_sent_2); - - EXPECT_THAT(send_delegate_1.datagrams_acked(), IsEmpty()); - EXPECT_THAT(send_delegate_2.datagrams_acked(), IsEmpty()); - - EXPECT_THAT(receive_delegate_1.messages_received(), IsEmpty()); - EXPECT_THAT(server_default_receiver_.messages_received(), IsEmpty()); -} - -TEST_F(QuartcMultiplexerTest, UnregisterReceiveChannel) { - Connect(); - - FakeSendDelegate send_delegate; - QuartcSendChannel* send_channel = - client_multiplexer()->CreateSendChannel(1, &send_delegate); - FakeQuartcStreamDelegate fake_send_stream_delegate; - - FakeReceiveDelegate receive_delegate; - server_multiplexer()->RegisterReceiveChannel(1, &receive_delegate); - server_multiplexer()->RegisterReceiveChannel(1, nullptr); - - int num_messages = 10; - std::vector<std::pair<uint64_t, std::string>> messages; - messages.reserve(num_messages); - std::vector<int64_t> messages_sent; - std::vector<testing::Matcher<std::pair<int64_t, QuicTime>>> ack_matchers; - for (int i = 0; i < num_messages; ++i) { - messages.emplace_back(1, quiche::QuicheStrCat("message for 1: ", i)); - test::QuicTestMemSliceVector slice( - {std::make_pair(const_cast<char*>(messages.back().second.data()), - messages.back().second.size())}); - send_channel->SendOrQueueMessage(slice.span(), i); - messages_sent.push_back(i); - ack_matchers.push_back(Pair(i, Gt(QuicTime::Zero()))); - } - - EXPECT_TRUE(simulator_.RunUntil([&send_delegate]() { - return send_delegate.datagrams_acked().size() == 10; - })); - - EXPECT_EQ(send_delegate.datagrams_sent(), messages_sent); - EXPECT_EQ(server_default_receiver_.messages_received(), messages); - EXPECT_THAT(send_delegate.datagrams_acked(), ElementsAreArray(ack_matchers)); -} - -TEST_F(QuartcMultiplexerTest, CloseEvent) { - Connect(); - Disconnect(); - - EXPECT_THAT(client_session_delegate_.error(), - test::IsError(QUIC_CONNECTION_CANCELLED)); - EXPECT_THAT(server_session_delegate_.error(), - test::IsError(QUIC_CONNECTION_CANCELLED)); -} - -TEST_F(QuartcMultiplexerTest, CongestionEvent) { - Connect(); - ASSERT_TRUE(simulator_.RunUntil([this]() { - return client_session_delegate_.handshake_count() > 0 && - server_session_delegate_.handshake_count() > 0; - })); - - EXPECT_GT(client_session_delegate_.latest_bandwidth_estimate(), - QuicBandwidth::Zero()); - EXPECT_GT(client_session_delegate_.latest_pacing_rate(), - QuicBandwidth::Zero()); - EXPECT_GT(client_session_delegate_.latest_rtt(), QuicTime::Delta::Zero()); -} - -} // namespace -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.cc deleted file mode 100644 index f67cc774ea8..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.cc +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h" - -#include <utility> - -namespace quic { - -std::unique_ptr<PerPacketOptions> QuartcPerPacketOptions::Clone() const { - return std::make_unique<QuartcPerPacketOptions>(*this); -} - -QuartcPacketWriter::QuartcPacketWriter(QuartcPacketTransport* packet_transport, - QuicByteCount max_packet_size) - : packet_transport_(packet_transport), max_packet_size_(max_packet_size) {} - -WriteResult QuartcPacketWriter::WritePacket( - const char* buffer, - size_t buf_len, - const QuicIpAddress& /*self_address*/, - const QuicSocketAddress& /*peer_address*/, - PerPacketOptions* options) { - DCHECK(packet_transport_); - - QuartcPacketTransport::PacketInfo info; - QuartcPerPacketOptions* quartc_options = - static_cast<QuartcPerPacketOptions*>(options); - if (quartc_options && quartc_options->connection) { - info.packet_number = - quartc_options->connection->packet_creator().packet_number(); - } - int bytes_written = packet_transport_->Write(buffer, buf_len, info); - if (bytes_written <= 0) { - writable_ = false; - return WriteResult(WRITE_STATUS_BLOCKED, EWOULDBLOCK); - } - return WriteResult(WRITE_STATUS_OK, bytes_written); -} - -bool QuartcPacketWriter::IsWriteBlocked() const { - return !writable_; -} - -QuicByteCount QuartcPacketWriter::GetMaxPacketSize( - const QuicSocketAddress& /*peer_address*/) const { - return max_packet_size_; -} - -void QuartcPacketWriter::SetWritable() { - writable_ = true; -} - -bool QuartcPacketWriter::SupportsReleaseTime() const { - return false; -} - -bool QuartcPacketWriter::IsBatchMode() const { - return false; -} - -char* QuartcPacketWriter::GetNextWriteLocation( - const QuicIpAddress& /*self_address*/, - const QuicSocketAddress& /*peer_address*/) { - return nullptr; -} - -WriteResult QuartcPacketWriter::Flush() { - return WriteResult(WRITE_STATUS_OK, 0); -} - -void QuartcPacketWriter::SetPacketTransportDelegate( - QuartcPacketTransport::Delegate* delegate) { - packet_transport_->SetDelegate(delegate); -} - -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h deleted file mode 100644 index f3eb885fd53..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2017 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_QUARTC_QUARTC_PACKET_WRITER_H_ -#define QUICHE_QUIC_QUARTC_QUARTC_PACKET_WRITER_H_ - -#include "net/third_party/quiche/src/quic/core/quic_connection.h" -#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" -#include "net/third_party/quiche/src/quic/core/quic_types.h" - -namespace quic { - -// Send and receive packets, like a virtual UDP socket. For example, this -// could be implemented by WebRTC's IceTransport. -class QuartcPacketTransport { - public: - // Additional metadata provided for each packet written. - struct PacketInfo { - QuicPacketNumber packet_number; - }; - - // Delegate for packet transport callbacks. Note that the delegate is not - // thread-safe. Packet transport implementations must ensure that callbacks - // are synchronized with all other work done by QUIC. - class Delegate { - public: - virtual ~Delegate() = default; - - // Called whenever the transport can write. - virtual void OnTransportCanWrite() = 0; - - // Called when the transport receives a packet. - virtual void OnTransportReceived(const char* data, size_t data_len) = 0; - }; - - virtual ~QuartcPacketTransport() {} - - // Called by the QuartcPacketWriter when writing packets to the network. - // Return the number of written bytes. Return 0 if the write is blocked. - virtual int Write(const char* buffer, - size_t buf_len, - const PacketInfo& info) = 0; - - // Sets the delegate which must be called when the transport can write or - // a packet is received. QUIC sets |delegate| to a nonnull pointer when it - // is ready to process incoming packets and sets |delegate| to nullptr before - // QUIC is deleted. Implementations may assume |delegate| remains valid until - // it is set to nullptr. - virtual void SetDelegate(Delegate* delegate) = 0; -}; - -struct QuartcPerPacketOptions : public PerPacketOptions { - std::unique_ptr<PerPacketOptions> Clone() const override; - - // The connection which is sending this packet. - QuicConnection* connection = nullptr; -}; - -// Implements a QuicPacketWriter using a QuartcPacketTransport, which allows a -// QuicConnection to use (for example), a WebRTC IceTransport. -class QuartcPacketWriter : public QuicPacketWriter { - public: - QuartcPacketWriter(QuartcPacketTransport* packet_transport, - QuicByteCount max_packet_size); - ~QuartcPacketWriter() override {} - - // The QuicConnection calls WritePacket and the QuicPacketWriter writes them - // to the QuartcSession::PacketTransport. - WriteResult WritePacket(const char* buffer, - size_t buf_len, - const QuicIpAddress& self_address, - const QuicSocketAddress& peer_address, - PerPacketOptions* options) override; - - // Whether the underneath |transport_| is blocked. If this returns true, - // outgoing QUIC packets are queued by QuicConnection until SetWritable() is - // called. - bool IsWriteBlocked() const override; - - // Maximum size of the QUIC packet which can be written. Users such as WebRTC - // can set the value through the QuartcFactoryConfig without updating the QUIC - // code. - QuicByteCount GetMaxPacketSize( - const QuicSocketAddress& peer_address) const override; - - // Sets the packet writer to a writable (non-blocked) state. - void SetWritable() override; - - bool SupportsReleaseTime() const override; - - bool IsBatchMode() const override; - - char* GetNextWriteLocation(const QuicIpAddress& self_address, - const QuicSocketAddress& peer_address) override; - - WriteResult Flush() override; - - void SetPacketTransportDelegate(QuartcPacketTransport::Delegate* delegate); - - private: - // QuartcPacketWriter will not own the transport. - QuartcPacketTransport* packet_transport_; - // The maximum size of the packet can be written by this writer. - QuicByteCount max_packet_size_; - - // Whether packets can be written. - bool writable_ = false; -}; - -} // namespace quic - -#endif // QUICHE_QUIC_QUARTC_QUARTC_PACKET_WRITER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.cc deleted file mode 100644 index 0a7fca933bf..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.cc +++ /dev/null @@ -1,470 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/quartc/quartc_session.h" - -#include <utility> - -#include "net/third_party/quiche/src/quic/core/quic_utils.h" -#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" -#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" - -namespace quic { -namespace { - -// Arbitrary server port number for net::QuicCryptoClientConfig. -const int kQuicServerPort = 0; - -} // namespace - -QuartcSession::QuartcSession(std::unique_ptr<QuicConnection> connection, - Visitor* visitor, - const QuicConfig& config, - const ParsedQuicVersionVector& supported_versions, - const QuicClock* clock) - : QuicSession(connection.get(), - visitor, - config, - supported_versions, - /*num_expected_unidirectional_static_streams = */ 0), - connection_(std::move(connection)), - clock_(clock), - per_packet_options_(std::make_unique<QuartcPerPacketOptions>()) { - per_packet_options_->connection = connection_.get(); - connection_->set_per_packet_options(per_packet_options_.get()); -} - -QuartcSession::~QuartcSession() {} - -QuartcStream* QuartcSession::CreateOutgoingBidirectionalStream() { - // Use default priority for incoming QUIC streams. - // TODO(zhihuang): Determine if this value is correct. - return ActivateDataStream(CreateDataStream( - GetNextOutgoingBidirectionalStreamId(), QuicStream::kDefaultPriority)); -} - -bool QuartcSession::SendOrQueueMessage(QuicMemSliceSpan message, - int64_t datagram_id) { - if (!CanSendMessage()) { - QUIC_LOG(ERROR) << "Quic session does not support SendMessage"; - return false; - } - - if (message.total_length() > GetCurrentLargestMessagePayload()) { - QUIC_LOG(ERROR) << "Message is too big, message_size=" - << message.total_length() - << ", GetCurrentLargestMessagePayload=" - << GetCurrentLargestMessagePayload(); - return false; - } - - // There may be other messages in send queue, so we have to add message - // to the queue and call queue processing helper. - QueuedMessage queued_message; - queued_message.datagram_id = datagram_id; - message.ConsumeAll([&queued_message](QuicMemSlice slice) { - queued_message.message.Append(std::move(slice)); - }); - send_message_queue_.push_back(std::move(queued_message)); - - ProcessSendMessageQueue(); - - return true; -} - -void QuartcSession::ProcessSendMessageQueue() { - QuicConnection::ScopedPacketFlusher flusher(connection()); - while (!send_message_queue_.empty()) { - QueuedMessage& it = send_message_queue_.front(); - QuicMemSliceSpan span = it.message.ToSpan(); - const size_t message_size = span.total_length(); - MessageResult result = SendMessage(span); - - // Handle errors. - switch (result.status) { - case MESSAGE_STATUS_SUCCESS: { - QUIC_VLOG(1) << "Quartc message sent, message_id=" << result.message_id - << ", message_size=" << message_size; - - auto element = message_to_datagram_id_.find(result.message_id); - - DCHECK(element == message_to_datagram_id_.end()) - << "Mapped message_id already exists, message_id=" - << result.message_id << ", datagram_id=" << element->second; - - message_to_datagram_id_[result.message_id] = it.datagram_id; - - // Notify that datagram was sent. - session_delegate_->OnMessageSent(it.datagram_id); - } break; - - // If connection is congestion controlled or not writable yet, stop - // send loop and we'll retry again when we get OnCanWrite notification. - case MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED: - case MESSAGE_STATUS_BLOCKED: - QUIC_VLOG(1) << "Quartc message not sent because connection is blocked" - << ", message will be retried later, status=" - << result.status << ", message_size=" << message_size; - - return; - - // Other errors are unexpected. We do not propagate error to Quartc, - // because writes can be delayed. - case MESSAGE_STATUS_UNSUPPORTED: - case MESSAGE_STATUS_TOO_LARGE: - case MESSAGE_STATUS_INTERNAL_ERROR: - QUIC_DLOG(DFATAL) - << "Failed to send quartc message due to unexpected error" - << ", message will not be retried, status=" << result.status - << ", message_size=" << message_size; - break; - } - - send_message_queue_.pop_front(); - } -} - -void QuartcSession::OnCanWrite() { - // TODO(b/119640244): Since we currently use messages for audio and streams - // for video, it makes sense to process queued messages first, then call quic - // core OnCanWrite, which will resend queued streams. Long term we may need - // better solution especially if quic connection is used for both data and - // media. - - // Process quartc messages that were previously blocked. - ProcessSendMessageQueue(); - - QuicSession::OnCanWrite(); -} - -bool QuartcSession::SendProbingData() { - if (QuicSession::SendProbingData()) { - return true; - } - - // Set transmission type to PROBING_RETRANSMISSION such that the packets will - // be padded to full. - SetTransmissionType(PROBING_RETRANSMISSION); - // TODO(mellem): this sent PING will be retransmitted if it is lost which is - // not ideal. Consider to send stream data as probing data instead. - SendPing(); - return true; -} - -void QuartcSession::SetDefaultEncryptionLevel(EncryptionLevel level) { - QuicSession::SetDefaultEncryptionLevel(level); - switch (level) { - case ENCRYPTION_INITIAL: - break; - case ENCRYPTION_ZERO_RTT: - if (connection()->perspective() == Perspective::IS_CLIENT) { - DCHECK(IsEncryptionEstablished()); - DCHECK(session_delegate_); - session_delegate_->OnConnectionWritable(); - } - break; - case ENCRYPTION_HANDSHAKE: - break; - case ENCRYPTION_FORWARD_SECURE: - // On the server, handshake confirmed is the first time when you can start - // writing packets. - DCHECK(IsEncryptionEstablished()); - DCHECK(OneRttKeysAvailable()); - - DCHECK(session_delegate_); - session_delegate_->OnConnectionWritable(); - session_delegate_->OnCryptoHandshakeComplete(); - break; - default: - QUIC_BUG << "Unknown encryption level: " - << EncryptionLevelToString(level); - } -} - -void QuartcSession::OnOneRttKeysAvailable() { - QuicSession::OnOneRttKeysAvailable(); - // On the server, handshake confirmed is the first time when you can start - // writing packets. - DCHECK(IsEncryptionEstablished()); - DCHECK(OneRttKeysAvailable()); - - DCHECK(session_delegate_); - session_delegate_->OnConnectionWritable(); - session_delegate_->OnCryptoHandshakeComplete(); -} - -void QuartcSession::CancelStream(QuicStreamId stream_id) { - ResetQuartcStream(stream_id, QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED); -} - -void QuartcSession::ResetQuartcStream(QuicStreamId stream_id, - QuicRstStreamErrorCode error) { - if (!IsOpenStream(stream_id)) { - return; - } - QuicStream* stream = QuicSession::GetOrCreateStream(stream_id); - if (stream) { - stream->Reset(error); - } -} - -void QuartcSession::OnCongestionWindowChange(QuicTime /*now*/) { - DCHECK(session_delegate_); - const RttStats* rtt_stats = connection_->sent_packet_manager().GetRttStats(); - - QuicBandwidth bandwidth_estimate = - connection_->sent_packet_manager().BandwidthEstimate(); - - QuicByteCount in_flight = - connection_->sent_packet_manager().GetBytesInFlight(); - QuicBandwidth pacing_rate = - connection_->sent_packet_manager().GetSendAlgorithm()->PacingRate( - in_flight); - - session_delegate_->OnCongestionControlChange(bandwidth_estimate, pacing_rate, - rtt_stats->latest_rtt()); -} - -bool QuartcSession::ShouldKeepConnectionAlive() const { - // TODO(mellem): Quartc may want different keepalive logic than HTTP. - return GetNumActiveStreams() > 0; -} - -void QuartcSession::OnConnectionClosed(const QuicConnectionCloseFrame& frame, - ConnectionCloseSource source) { - QuicSession::OnConnectionClosed(frame, source); - DCHECK(session_delegate_); - session_delegate_->OnConnectionClosed(frame, source); -} - -void QuartcSession::CloseConnection(const std::string& details) { - connection_->CloseConnection( - QuicErrorCode::QUIC_CONNECTION_CANCELLED, details, - ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); -} - -void QuartcSession::SetDelegate(Delegate* session_delegate) { - if (session_delegate_) { - QUIC_LOG(WARNING) << "The delegate for the session has already been set."; - } - session_delegate_ = session_delegate; - DCHECK(session_delegate_); -} - -void QuartcSession::OnTransportCanWrite() { - connection()->writer()->SetWritable(); - if (HasDataToWrite()) { - connection()->OnCanWrite(); - } -} - -void QuartcSession::OnTransportReceived(const char* data, size_t data_len) { - QuicReceivedPacket packet(data, data_len, clock_->Now()); - ProcessUdpPacket(connection()->self_address(), connection()->peer_address(), - packet); -} - -void QuartcSession::OnMessageReceived(quiche::QuicheStringPiece message) { - session_delegate_->OnMessageReceived(message); -} - -void QuartcSession::OnMessageAcked(QuicMessageId message_id, - QuicTime receive_timestamp) { - auto element = message_to_datagram_id_.find(message_id); - - if (element == message_to_datagram_id_.end()) { - return; - } - - session_delegate_->OnMessageAcked(/*datagram_id=*/element->second, - receive_timestamp); - - // Free up space -- we should never see message_id again. - message_to_datagram_id_.erase(element); -} - -void QuartcSession::OnMessageLost(QuicMessageId message_id) { - auto it = message_to_datagram_id_.find(message_id); - if (it == message_to_datagram_id_.end()) { - return; - } - - session_delegate_->OnMessageLost(/*datagram_id=*/it->second); - - // Free up space. - message_to_datagram_id_.erase(it); -} - -QuicStream* QuartcSession::CreateIncomingStream(QuicStreamId id) { - return ActivateDataStream(CreateDataStream(id, QuicStream::kDefaultPriority)); -} - -QuicStream* QuartcSession::CreateIncomingStream(PendingStream* /*pending*/) { - QUIC_NOTREACHED(); - return nullptr; -} - -std::unique_ptr<QuartcStream> QuartcSession::CreateDataStream( - QuicStreamId id, - spdy::SpdyPriority priority) { - if (GetCryptoStream() == nullptr || - !GetCryptoStream()->encryption_established()) { - // Encryption not active so no stream created - return nullptr; - } - return InitializeDataStream(std::make_unique<QuartcStream>(id, this), - priority); -} - -std::unique_ptr<QuartcStream> QuartcSession::InitializeDataStream( - std::unique_ptr<QuartcStream> stream, - spdy::SpdyPriority priority) { - // Register the stream to the QuicWriteBlockedList. |priority| is clamped - // between 0 and 7, with 0 being the highest priority and 7 the lowest - // priority. - write_blocked_streams()->UpdateStreamPriority( - stream->id(), spdy::SpdyStreamPrecedence(priority)); - - if (IsIncomingStream(stream->id())) { - DCHECK(session_delegate_); - // Incoming streams need to be registered with the session_delegate_. - session_delegate_->OnIncomingStream(stream.get()); - } - return stream; -} - -QuartcStream* QuartcSession::ActivateDataStream( - std::unique_ptr<QuartcStream> stream) { - // Transfer ownership of the data stream to the session via ActivateStream(). - QuartcStream* raw = stream.release(); - if (raw) { - // Make QuicSession take ownership of the stream. - ActivateStream(std::unique_ptr<QuicStream>(raw)); - } - return raw; -} - -QuartcClientSession::QuartcClientSession( - std::unique_ptr<QuicConnection> connection, - const QuicConfig& config, - const ParsedQuicVersionVector& supported_versions, - const QuicClock* clock, - std::unique_ptr<QuartcPacketWriter> packet_writer, - std::unique_ptr<QuicCryptoClientConfig> client_crypto_config, - quiche::QuicheStringPiece server_crypto_config) - : QuartcSession(std::move(connection), - /*visitor=*/nullptr, - config, - supported_versions, - clock), - packet_writer_(std::move(packet_writer)), - client_crypto_config_(std::move(client_crypto_config)), - server_config_(server_crypto_config) { - DCHECK_EQ(QuartcSession::connection()->perspective(), Perspective::IS_CLIENT); -} - -QuartcClientSession::~QuartcClientSession() { - // The client session is the packet transport delegate, so it must be unset - // before the session is deleted. - packet_writer_->SetPacketTransportDelegate(nullptr); -} - -void QuartcClientSession::Initialize() { - DCHECK(crypto_stream_) << "Do not call QuartcSession::Initialize(), call " - "StartCryptoHandshake() instead."; - QuartcSession::Initialize(); - - // QUIC is ready to process incoming packets after Initialize(). - // Set the packet transport delegate to begin receiving packets. - packet_writer_->SetPacketTransportDelegate(this); -} - -const QuicCryptoStream* QuartcClientSession::GetCryptoStream() const { - return crypto_stream_.get(); -} - -QuicCryptoStream* QuartcClientSession::GetMutableCryptoStream() { - return crypto_stream_.get(); -} - -void QuartcClientSession::StartCryptoHandshake() { - QuicServerId server_id(/*host=*/"", kQuicServerPort, - /*privacy_mode_enabled=*/false); - - if (!server_config_.empty()) { - QuicCryptoServerConfig::ConfigOptions options; - - std::string error; - QuicWallTime now = clock()->WallNow(); - QuicCryptoClientConfig::CachedState::ServerConfigState result = - client_crypto_config_->LookupOrCreate(server_id)->SetServerConfig( - server_config_, now, - /*expiry_time=*/now.Add(QuicTime::Delta::Infinite()), &error); - - if (result == QuicCryptoClientConfig::CachedState::SERVER_CONFIG_VALID) { - DCHECK_EQ(error, ""); - client_crypto_config_->LookupOrCreate(server_id)->SetProof( - std::vector<std::string>{kDummyCertName}, /*cert_sct=*/"", - /*chlo_hash=*/"", /*signature=*/"anything"); - } else { - QUIC_LOG(DFATAL) << "Unable to set server config, error=" << error; - } - } - - crypto_stream_ = std::make_unique<QuicCryptoClientStream>( - server_id, this, - client_crypto_config_->proof_verifier()->CreateDefaultContext(), - client_crypto_config_.get(), this, /*has_application_state = */ true); - Initialize(); - crypto_stream_->CryptoConnect(); -} - -void QuartcClientSession::OnProofValid( - const QuicCryptoClientConfig::CachedState& /*cached*/) { - // TODO(zhihuang): Handle the proof verification. -} - -void QuartcClientSession::OnProofVerifyDetailsAvailable( - const ProofVerifyDetails& /*verify_details*/) { - // TODO(zhihuang): Handle the proof verification. -} - -QuartcServerSession::QuartcServerSession( - std::unique_ptr<QuicConnection> connection, - Visitor* visitor, - const QuicConfig& config, - const ParsedQuicVersionVector& supported_versions, - const QuicClock* clock, - const QuicCryptoServerConfig* server_crypto_config, - QuicCompressedCertsCache* const compressed_certs_cache, - QuicCryptoServerStreamBase::Helper* const stream_helper) - : QuartcSession(std::move(connection), - visitor, - config, - supported_versions, - clock), - server_crypto_config_(server_crypto_config), - compressed_certs_cache_(compressed_certs_cache), - stream_helper_(stream_helper) { - DCHECK_EQ(QuartcSession::connection()->perspective(), Perspective::IS_SERVER); -} - -const QuicCryptoStream* QuartcServerSession::GetCryptoStream() const { - return crypto_stream_.get(); -} - -QuicCryptoStream* QuartcServerSession::GetMutableCryptoStream() { - return crypto_stream_.get(); -} - -void QuartcServerSession::StartCryptoHandshake() { - crypto_stream_ = CreateCryptoServerStream( - server_crypto_config_, compressed_certs_cache_, this, stream_helper_); - Initialize(); -} - -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.h deleted file mode 100644 index 66ddfc3617f..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.h +++ /dev/null @@ -1,339 +0,0 @@ -// Copyright (c) 2017 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_QUARTC_QUARTC_SESSION_H_ -#define QUICHE_QUIC_QUARTC_QUARTC_SESSION_H_ - -#include <memory> -#include <string> - -#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h" -#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream_base.h" -#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" -#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" -#include "net/third_party/quiche/src/quic/core/quic_session.h" -#include "net/third_party/quiche/src/quic/core/quic_types.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_stream.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" - -namespace quic { - -// QuartcSession owns and manages a QUIC connection. -class QuartcSession : public QuicSession, - public QuartcPacketTransport::Delegate { - public: - QuartcSession(std::unique_ptr<QuicConnection> connection, - Visitor* visitor, - const QuicConfig& config, - const ParsedQuicVersionVector& supported_versions, - const QuicClock* clock); - QuartcSession(const QuartcSession&) = delete; - QuartcSession& operator=(const QuartcSession&) = delete; - ~QuartcSession() override; - - // QuicSession overrides. - QuartcStream* CreateOutgoingBidirectionalStream(); - - // Sends short unreliable message using quic message frame (message must fit - // in one quic packet). If connection is blocked by congestion control, - // message will be queued and resent later after receiving an OnCanWrite - // notification. - // - // Message size must be <= GetLargestMessagePayload(). - // - // Supported in quic version 45 or later. - // - // Returns false and logs error if message is too long or session does not - // support SendMessage API. Other unexpected errors during send will not be - // returned, because messages can be sent later if connection is congestion - // controlled. - // - // |datagram_id| is used to notify when message was sent in - // Delegate::OnMessageSent. - // - // TODO(sukhanov): We can not use QUIC message ID for notifications, because - // QUIC does not take ownership of messages and if connection is congestion - // controlled, message is not sent and does not get message id until it is - // sent successfully. It also creates problem of flow control between - // messages and streams if they are used together. We discussed it with QUIC - // team and there are multiple solutions, but for now we have to use our - // own datagram identification. - bool SendOrQueueMessage(QuicMemSliceSpan message, int64_t datagram_id); - - // Returns largest message payload acceptable in SendQuartcMessage. - QuicPacketLength GetCurrentLargestMessagePayload() const { - return connection()->GetCurrentLargestMessagePayload(); - } - - // Return true if transport support message frame. - bool CanSendMessage() const { - return VersionSupportsMessageFrames(transport_version()); - } - - void SetDefaultEncryptionLevel(EncryptionLevel level) override; - void OnOneRttKeysAvailable() override; - - // QuicConnectionVisitorInterface overrides. - void OnCongestionWindowChange(QuicTime now) override; - bool ShouldKeepConnectionAlive() const override; - - void OnCanWrite() override; - bool SendProbingData() override; - - void OnConnectionClosed(const QuicConnectionCloseFrame& frame, - ConnectionCloseSource source) override; - - // QuartcSession methods. - virtual void StartCryptoHandshake() = 0; - - // Closes the connection with the given human-readable error details. - // The connection closes with the QUIC_CONNECTION_CANCELLED error code to - // indicate the application closed it. - // - // Informs the peer that the connection has been closed. This prevents the - // peer from waiting until the connection times out. - // - // Cleans up the underlying QuicConnection's state. Closing the connection - // makes it safe to delete the QuartcSession. - void CloseConnection(const std::string& details); - - // If the given stream is still open, sends a reset frame to cancel it. - // Note: This method cancels a stream by QuicStreamId rather than by pointer - // (or by a method on QuartcStream) because QuartcSession (and not - // the caller) owns the streams. Streams may finish and be deleted before the - // caller tries to cancel them, rendering the caller's pointers invalid. - void CancelStream(QuicStreamId stream_id); - - // Callbacks called by the QuartcSession to notify the user of the - // QuartcSession of certain events. - class Delegate { - public: - virtual ~Delegate() {} - - // Called when the crypto handshake is complete. Crypto handshake on the - // client is only completed _after_ SHLO is received, but we can actually - // start sending media data right after CHLO is sent. - virtual void OnCryptoHandshakeComplete() = 0; - - // Connection can be writable even before crypto handshake is complete. - // In particular, on the client, we can start sending data after sending - // full CHLO, without waiting for SHLO. This reduces a send delay by 1-rtt. - // - // This may be called multiple times. - virtual void OnConnectionWritable() = 0; - - // Called when a new stream is received from the remote endpoint. - virtual void OnIncomingStream(QuartcStream* stream) = 0; - - // Called when network parameters change in response to an ack frame. - virtual void OnCongestionControlChange(QuicBandwidth bandwidth_estimate, - QuicBandwidth pacing_rate, - QuicTime::Delta latest_rtt) = 0; - - // Called when the connection is closed. This means all of the streams will - // be closed and no new streams can be created. - virtual void OnConnectionClosed(const QuicConnectionCloseFrame& frame, - ConnectionCloseSource source) = 0; - - // Called when message (sent as SendMessage) is received. - virtual void OnMessageReceived(quiche::QuicheStringPiece message) = 0; - - // Called when message is sent to QUIC. - // - // Takes into account delay due to congestion control, but does not take - // into account any additional socket delays. - // - // Passed |datagram_id| is the same used in SendOrQueueMessage. - // - // TODO(sukhanov): We can take into account socket delay, but it's not clear - // if it's worth doing if we eventually plan to move congestion control to - // QUIC in QRTP model. If we need to do it, mellem@ thinks it's fairly - // strtaightforward: QUIC does not know about socket delay, but ICE does. We - // can tell ICE the QUIC packet number for each packet sent, and it will - // echo it back to us when the packet actually goes out. We just need to - // plumb that signal up to RTP's congestion control. - virtual void OnMessageSent(int64_t datagram_id) = 0; - - // Called when message with |datagram_id| gets acked. |receive_timestamp| - // indicates when the peer received this message, according to its own - // clock. - virtual void OnMessageAcked(int64_t datagram_id, - QuicTime receive_timestamp) = 0; - - // Called when message with |datagram_id| is lost. - virtual void OnMessageLost(int64_t datagram_id) = 0; - - // TODO(zhihuang): Add proof verification. - }; - - // The |delegate| is not owned by QuartcSession. - void SetDelegate(Delegate* session_delegate); - - // Called when CanWrite() changes from false to true. - void OnTransportCanWrite() override; - - // Called when a packet has been received and should be handled by the - // QuicConnection. - void OnTransportReceived(const char* data, size_t data_len) override; - - void OnMessageReceived(quiche::QuicheStringPiece message) override; - - // Called when message with |message_id| gets acked. - void OnMessageAcked(QuicMessageId message_id, - QuicTime receive_timestamp) override; - - void OnMessageLost(QuicMessageId message_id) override; - - // Returns number of queued (not sent) messages submitted by - // SendOrQueueMessage. Messages are queued if connection is congestion - // controlled. - size_t send_message_queue_size() const { return send_message_queue_.size(); } - - protected: - // QuicSession override. - QuicStream* CreateIncomingStream(QuicStreamId id) override; - QuicStream* CreateIncomingStream(PendingStream* pending) override; - - std::unique_ptr<QuartcStream> CreateDataStream(QuicStreamId id, - spdy::SpdyPriority priority); - // Activates a QuartcStream. The session takes ownership of the stream, but - // returns an unowned pointer to the stream for convenience. - QuartcStream* ActivateDataStream(std::unique_ptr<QuartcStream> stream); - - void ResetQuartcStream(QuicStreamId stream_id, QuicRstStreamErrorCode error); - - const QuicClock* clock() { return clock_; } - - private: - std::unique_ptr<QuartcStream> InitializeDataStream( - std::unique_ptr<QuartcStream> stream, - spdy::SpdyPriority priority); - - // Holds message until it's sent. - struct QueuedMessage { - QueuedMessage() : message(nullptr, 0, nullptr, 0), datagram_id(0) {} - - QuicMemSliceStorage message; - int64_t datagram_id; - }; - - void ProcessSendMessageQueue(); - - // Take ownership of the QuicConnection. Note: if |connection_| changes, - // the new value of |connection_| must be given to |packet_writer_| before any - // packets are written. Otherwise, |packet_writer_| will crash. - std::unique_ptr<QuicConnection> connection_; - - // For recording packet receipt time - const QuicClock* clock_; - - // Not owned by QuartcSession. - Delegate* session_delegate_ = nullptr; - - // Options passed to the packet writer for each packet. - std::unique_ptr<QuartcPerPacketOptions> per_packet_options_; - - // Queue of pending messages sent by SendQuartcMessage that were not sent - // yet or blocked by congestion control. Messages are queued in the order - // of sent by SendOrQueueMessage(). - QuicCircularDeque<QueuedMessage> send_message_queue_; - - // Maps message ids to datagram ids, so we could translate message ACKs - // received from QUIC to datagram ACKs that are propagated up the stack. - QuicHashMap<QuicMessageId, int64_t> message_to_datagram_id_; -}; - -class QuartcClientSession : public QuartcSession, - public QuicCryptoClientStream::ProofHandler { - public: - QuartcClientSession( - std::unique_ptr<QuicConnection> connection, - const QuicConfig& config, - const ParsedQuicVersionVector& supported_versions, - const QuicClock* clock, - std::unique_ptr<QuartcPacketWriter> packet_writer, - std::unique_ptr<QuicCryptoClientConfig> client_crypto_config, - quiche::QuicheStringPiece server_crypto_config); - QuartcClientSession(const QuartcClientSession&) = delete; - QuartcClientSession& operator=(const QuartcClientSession&) = delete; - - ~QuartcClientSession() override; - - // Initialize should not be called on a QuartcSession. Instead, call - // StartCryptoHandshake(). - // TODO(mellem): Move creation of the crypto stream into Initialize() and - // remove StartCryptoHandshake() to bring QuartcSession in line with other - // implementations of QuicSession, which can be started by calling - // Initialize(). - void Initialize() override; - - // Accessors for the client crypto stream. - QuicCryptoStream* GetMutableCryptoStream() override; - const QuicCryptoStream* GetCryptoStream() const override; - - // Initializes the session and sends a handshake. - void StartCryptoHandshake() override; - - // ProofHandler overrides. - void OnProofValid(const QuicCryptoClientConfig::CachedState& cached) override; - - // Called by the client crypto handshake when proof verification details - // become available, either because proof verification is complete, or when - // cached details are used. - void OnProofVerifyDetailsAvailable( - const ProofVerifyDetails& verify_details) override; - - private: - // Packet writer used by |connection_|. - std::unique_ptr<QuartcPacketWriter> packet_writer_; - - // Config for QUIC crypto stream. - std::unique_ptr<QuicCryptoClientConfig> client_crypto_config_; - - // Client perspective crypto stream. - std::unique_ptr<QuicCryptoClientStream> crypto_stream_; - - const std::string server_config_; -}; - -class QuartcServerSession : public QuartcSession { - public: - QuartcServerSession(std::unique_ptr<QuicConnection> connection, - Visitor* visitor, - const QuicConfig& config, - const ParsedQuicVersionVector& supported_versions, - const QuicClock* clock, - const QuicCryptoServerConfig* server_crypto_config, - QuicCompressedCertsCache* const compressed_certs_cache, - QuicCryptoServerStreamBase::Helper* const stream_helper); - QuartcServerSession(const QuartcServerSession&) = delete; - QuartcServerSession& operator=(const QuartcServerSession&) = delete; - - // Accessors for the server crypto stream. - QuicCryptoStream* GetMutableCryptoStream() override; - const QuicCryptoStream* GetCryptoStream() const override; - - // Initializes the session and prepares to receive a handshake. - void StartCryptoHandshake() override; - - private: - // Config for QUIC crypto stream. - const QuicCryptoServerConfig* server_crypto_config_; - - // Used by QUIC crypto server stream to track most recently compressed certs. - QuicCompressedCertsCache* const compressed_certs_cache_; - - // This helper is needed to create QuicCryptoServerStream. - QuicCryptoServerStreamBase::Helper* const stream_helper_; - - // Server perspective crypto stream. - std::unique_ptr<QuicCryptoServerStreamBase> crypto_stream_; -}; - -} // namespace quic - -#endif // QUICHE_QUIC_QUARTC_QUARTC_SESSION_H_ diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_session_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session_test.cc deleted file mode 100644 index 93172e7c55d..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_session_test.cc +++ /dev/null @@ -1,680 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/quartc/quartc_session.h" - -#include <utility> - -#include "net/third_party/quiche/src/quic/core/quic_clock.h" -#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" -#include "net/third_party/quiche/src/quic/core/quic_types.h" -#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" -#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_string_utils.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h" -#include "net/third_party/quiche/src/quic/quartc/counting_packet_filter.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_fakes.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h" -#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h" -#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" -#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" -#include "net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.h" -#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" - -namespace quic { - -namespace { - -using ::testing::ElementsAre; -using ::testing::ElementsAreArray; -using ::testing::Gt; -using ::testing::Pair; - -constexpr QuicTime::Delta kPropagationDelay = - QuicTime::Delta::FromMilliseconds(10); -// Propagation delay and a bit, but no more than full RTT. -constexpr QuicTime::Delta kPropagationDelayAndABit = - QuicTime::Delta::FromMilliseconds(12); - -static QuicByteCount kDefaultMaxPacketSize = 1200; - -test::QuicTestMemSliceVector CreateMemSliceVector( - quiche::QuicheStringPiece data) { - return test::QuicTestMemSliceVector( - {std::pair<char*, size_t>(const_cast<char*>(data.data()), data.size())}); -} - -class QuartcSessionTest : public QuicTest { - public: - ~QuartcSessionTest() override {} - - void Init(bool create_client_endpoint = true) { - // TODO(b/150224094): Re-enable TLS handshake. - // TODO(b/150236522): Parametrize by QUIC version. - quic::test::DisableQuicVersionsWithTls(); - - client_transport_ = - std::make_unique<simulator::SimulatedQuartcPacketTransport>( - &simulator_, "client_transport", "server_transport", - 10 * kDefaultMaxPacketSize); - server_transport_ = - std::make_unique<simulator::SimulatedQuartcPacketTransport>( - &simulator_, "server_transport", "client_transport", - 10 * kDefaultMaxPacketSize); - - client_filter_ = std::make_unique<simulator::CountingPacketFilter>( - &simulator_, "client_filter", client_transport_.get()); - - client_server_link_ = std::make_unique<simulator::SymmetricLink>( - client_filter_.get(), server_transport_.get(), - QuicBandwidth::FromKBitsPerSecond(10 * 1000), kPropagationDelay); - - client_stream_delegate_ = std::make_unique<FakeQuartcStreamDelegate>(); - client_session_delegate_ = std::make_unique<FakeQuartcEndpointDelegate>( - client_stream_delegate_.get(), simulator_.GetClock()); - - server_stream_delegate_ = std::make_unique<FakeQuartcStreamDelegate>(); - server_session_delegate_ = std::make_unique<FakeQuartcEndpointDelegate>( - server_stream_delegate_.get(), simulator_.GetClock()); - - // No 0-rtt setup, because server config is empty. - // CannotCreateDataStreamBeforeHandshake depends on 1-rtt setup. - if (create_client_endpoint) { - client_endpoint_ = std::make_unique<QuartcClientEndpoint>( - simulator_.GetAlarmFactory(), simulator_.GetClock(), - simulator_.GetRandomGenerator(), client_session_delegate_.get(), - quic::QuartcSessionConfig(), - /*serialized_server_config=*/""); - } - server_endpoint_ = std::make_unique<QuartcServerEndpoint>( - simulator_.GetAlarmFactory(), simulator_.GetClock(), - simulator_.GetRandomGenerator(), server_session_delegate_.get(), - quic::QuartcSessionConfig()); - } - - // Note that input session config will apply to both server and client. - // Perspective and packet_transport will be overwritten. - void CreateClientAndServerSessions( - const QuartcSessionConfig& /*session_config*/, - bool init = true) { - if (init) { - Init(); - } - - server_endpoint_->Connect(server_transport_.get()); - client_endpoint_->Connect(client_transport_.get()); - - CHECK(simulator_.RunUntil([this] { - return client_session_delegate_->session() != nullptr && - server_session_delegate_->session() != nullptr; - })); - - client_peer_ = client_session_delegate_->session(); - server_peer_ = server_session_delegate_->session(); - } - - // Runs all tasks scheduled in the next 200 ms. - void RunTasks() { simulator_.RunFor(QuicTime::Delta::FromMilliseconds(200)); } - - void AwaitHandshake() { - simulator_.RunUntil([this] { - return client_peer_->OneRttKeysAvailable() && - server_peer_->OneRttKeysAvailable(); - }); - } - - // Test handshake establishment and sending/receiving of data for two - // directions. - void TestSendReceiveStreams() { - ASSERT_TRUE(server_peer_->OneRttKeysAvailable()); - ASSERT_TRUE(client_peer_->OneRttKeysAvailable()); - ASSERT_TRUE(server_peer_->IsEncryptionEstablished()); - ASSERT_TRUE(client_peer_->IsEncryptionEstablished()); - - // Now we can establish encrypted outgoing stream. - QuartcStream* outgoing_stream = - server_peer_->CreateOutgoingBidirectionalStream(); - QuicStreamId stream_id = outgoing_stream->id(); - ASSERT_NE(nullptr, outgoing_stream); - EXPECT_TRUE(server_peer_->ShouldKeepConnectionAlive()); - - outgoing_stream->SetDelegate(server_stream_delegate_.get()); - - // Send a test message from peer 1 to peer 2. - test::QuicTestMemSliceVector data = CreateMemSliceVector("Hello"); - outgoing_stream->WriteMemSlices(data.span(), /*fin=*/false); - RunTasks(); - - // Wait for peer 2 to receive messages. - ASSERT_TRUE(client_stream_delegate_->has_data()); - - QuartcStream* incoming = client_session_delegate_->last_incoming_stream(); - ASSERT_TRUE(incoming); - EXPECT_EQ(incoming->id(), stream_id); - EXPECT_TRUE(client_peer_->ShouldKeepConnectionAlive()); - - EXPECT_EQ(client_stream_delegate_->data()[stream_id], "Hello"); - // Send a test message from peer 2 to peer 1. - test::QuicTestMemSliceVector response = CreateMemSliceVector("Response"); - incoming->WriteMemSlices(response.span(), /*fin=*/false); - RunTasks(); - // Wait for peer 1 to receive messages. - ASSERT_TRUE(server_stream_delegate_->has_data()); - - EXPECT_EQ(server_stream_delegate_->data()[stream_id], "Response"); - } - - // Test sending/receiving of messages for two directions. - void TestSendReceiveMessage() { - ASSERT_TRUE(server_peer_->CanSendMessage()); - ASSERT_TRUE(client_peer_->CanSendMessage()); - - // Disable probing retransmissions such that the first message from either - // side can be sent without being queued. - client_peer_->connection()->set_fill_up_link_during_probing(false); - server_peer_->connection()->set_fill_up_link_during_probing(false); - - int64_t server_datagram_id = 111; - int64_t client_datagram_id = 222; - - // Send message from peer 1 to peer 2. - test::QuicTestMemSliceVector message = - CreateMemSliceVector("Message from server"); - ASSERT_TRUE( - server_peer_->SendOrQueueMessage(message.span(), server_datagram_id)); - - // First message in each direction should not be queued. - EXPECT_EQ(server_peer_->send_message_queue_size(), 0u); - - // Wait for peer 2 to receive message. - RunTasks(); - - EXPECT_THAT(client_session_delegate_->incoming_messages(), - testing::ElementsAre("Message from server")); - - EXPECT_THAT(server_session_delegate_->sent_datagram_ids(), - testing::ElementsAre(server_datagram_id)); - - EXPECT_THAT( - server_session_delegate_->acked_datagram_id_to_receive_timestamp(), - ElementsAre(Pair(server_datagram_id, Gt(QuicTime::Zero())))); - - // Send message from peer 2 to peer 1. - message = CreateMemSliceVector("Message from client"); - ASSERT_TRUE( - client_peer_->SendOrQueueMessage(message.span(), client_datagram_id)); - - // First message in each direction should not be queued. - EXPECT_EQ(client_peer_->send_message_queue_size(), 0u); - - // Wait for peer 1 to receive message. - RunTasks(); - - EXPECT_THAT(server_session_delegate_->incoming_messages(), - testing::ElementsAre("Message from client")); - - EXPECT_THAT(client_session_delegate_->sent_datagram_ids(), - testing::ElementsAre(client_datagram_id)); - - EXPECT_THAT( - client_session_delegate_->acked_datagram_id_to_receive_timestamp(), - ElementsAre(Pair(client_datagram_id, Gt(QuicTime::Zero())))); - } - - // Test for sending multiple messages that also result in queueing. - // This is one-way test, which is run in given direction. - void TestSendReceiveQueuedMessages(bool direction_from_server) { - // Send until queue_size number of messages are queued. - constexpr size_t queue_size = 10; - - ASSERT_TRUE(server_peer_->CanSendMessage()); - ASSERT_TRUE(client_peer_->CanSendMessage()); - - QuartcSession* const peer_sending = - direction_from_server ? server_peer_ : client_peer_; - - FakeQuartcEndpointDelegate* const delegate_receiving = - direction_from_server ? client_session_delegate_.get() - : server_session_delegate_.get(); - - FakeQuartcEndpointDelegate* const delegate_sending = - direction_from_server ? server_session_delegate_.get() - : client_session_delegate_.get(); - - // There should be no messages in the queue before we start sending. - EXPECT_EQ(peer_sending->send_message_queue_size(), 0u); - - // Send messages from peer 1 to peer 2 until required number of messages - // are queued in unsent message queue. - std::vector<std::string> sent_messages; - std::vector<int64_t> sent_datagram_ids; - int64_t current_datagram_id = 0; - while (peer_sending->send_message_queue_size() < queue_size) { - sent_messages.push_back(quiche::QuicheStrCat("Sending message, index=", - sent_messages.size())); - ASSERT_TRUE(peer_sending->SendOrQueueMessage( - CreateMemSliceVector(sent_messages.back()).span(), - current_datagram_id)); - - sent_datagram_ids.push_back(current_datagram_id); - ++current_datagram_id; - } - - // Wait for peer 2 to receive all messages. - RunTasks(); - - std::vector<testing::Matcher<std::pair<int64_t, QuicTime>>> ack_matchers; - for (int64_t id : sent_datagram_ids) { - ack_matchers.push_back(Pair(id, Gt(QuicTime::Zero()))); - } - EXPECT_EQ(delegate_receiving->incoming_messages(), sent_messages); - EXPECT_EQ(delegate_sending->sent_datagram_ids(), sent_datagram_ids); - EXPECT_THAT(delegate_sending->acked_datagram_id_to_receive_timestamp(), - ElementsAreArray(ack_matchers)); - } - - // Test sending long messages: - // - message of maximum allowed length should succeed - // - message of > maximum allowed length should fail. - void TestSendLongMessage() { - ASSERT_TRUE(server_peer_->CanSendMessage()); - ASSERT_TRUE(client_peer_->CanSendMessage()); - - // Send message of maximum allowed length. - std::string message_max_long = - std::string(server_peer_->GetCurrentLargestMessagePayload(), 'A'); - test::QuicTestMemSliceVector message = - CreateMemSliceVector(message_max_long); - ASSERT_TRUE( - server_peer_->SendOrQueueMessage(message.span(), /*datagram_id=*/0)); - - // Send long message which should fail. - std::string message_too_long = - std::string(server_peer_->GetCurrentLargestMessagePayload() + 1, 'B'); - message = CreateMemSliceVector(message_too_long); - ASSERT_FALSE( - server_peer_->SendOrQueueMessage(message.span(), /*datagram_id=*/0)); - - // Wait for peer 2 to receive message. - RunTasks(); - - // Client should only receive one message of allowed length. - EXPECT_THAT(client_session_delegate_->incoming_messages(), - testing::ElementsAre(message_max_long)); - } - - // Test that client and server are not connected after handshake failure. - void TestDisconnectAfterFailedHandshake() { - EXPECT_TRUE(!client_session_delegate_->connected()); - EXPECT_TRUE(!server_session_delegate_->connected()); - - EXPECT_FALSE(client_peer_->IsEncryptionEstablished()); - EXPECT_FALSE(client_peer_->OneRttKeysAvailable()); - - EXPECT_FALSE(server_peer_->IsEncryptionEstablished()); - EXPECT_FALSE(server_peer_->OneRttKeysAvailable()); - } - - protected: - simulator::Simulator simulator_; - - std::unique_ptr<simulator::SimulatedQuartcPacketTransport> client_transport_; - std::unique_ptr<simulator::SimulatedQuartcPacketTransport> server_transport_; - std::unique_ptr<simulator::CountingPacketFilter> client_filter_; - std::unique_ptr<simulator::SymmetricLink> client_server_link_; - - std::unique_ptr<FakeQuartcStreamDelegate> client_stream_delegate_; - std::unique_ptr<FakeQuartcEndpointDelegate> client_session_delegate_; - std::unique_ptr<FakeQuartcStreamDelegate> server_stream_delegate_; - std::unique_ptr<FakeQuartcEndpointDelegate> server_session_delegate_; - - std::unique_ptr<QuartcClientEndpoint> client_endpoint_; - std::unique_ptr<QuartcServerEndpoint> server_endpoint_; - - QuartcSession* client_peer_ = nullptr; - QuartcSession* server_peer_ = nullptr; -}; - -TEST_F(QuartcSessionTest, SendReceiveStreams) { - CreateClientAndServerSessions(QuartcSessionConfig()); - AwaitHandshake(); - TestSendReceiveStreams(); -} - -TEST_F(QuartcSessionTest, SendReceiveMessages) { - CreateClientAndServerSessions(QuartcSessionConfig()); - AwaitHandshake(); - TestSendReceiveMessage(); -} - -TEST_F(QuartcSessionTest, SendReceiveQueuedMessages) { - CreateClientAndServerSessions(QuartcSessionConfig()); - AwaitHandshake(); - TestSendReceiveQueuedMessages(/*direction_from_server=*/true); - TestSendReceiveQueuedMessages(/*direction_from_server=*/false); -} - -TEST_F(QuartcSessionTest, SendMultiMemSliceMessage) { - CreateClientAndServerSessions(QuartcSessionConfig()); - AwaitHandshake(); - ASSERT_TRUE(server_peer_->CanSendMessage()); - - std::vector<std::pair<char*, size_t>> buffers; - char first_piece[] = "Hello, "; - char second_piece[] = "world!"; - buffers.emplace_back(first_piece, 7); - buffers.emplace_back(second_piece, 6); - test::QuicTestMemSliceVector message(buffers); - ASSERT_TRUE( - server_peer_->SendOrQueueMessage(message.span(), /*datagram_id=*/1)); - - // Wait for the client to receive the message. - RunTasks(); - - // The message is not fragmented along MemSlice boundaries. - EXPECT_THAT(client_session_delegate_->incoming_messages(), - testing::ElementsAre("Hello, world!")); -} - -TEST_F(QuartcSessionTest, SendMessageFails) { - CreateClientAndServerSessions(QuartcSessionConfig()); - AwaitHandshake(); - TestSendLongMessage(); -} - -TEST_F(QuartcSessionTest, TestCryptoHandshakeCanWriteTriggers) { - CreateClientAndServerSessions(QuartcSessionConfig()); - - AwaitHandshake(); - - RunTasks(); - - ASSERT_TRUE(client_session_delegate_->writable_time().IsInitialized()); - ASSERT_TRUE( - client_session_delegate_->crypto_handshake_time().IsInitialized()); - // On client, we are writable 1-rtt before crypto handshake is complete. - ASSERT_LT(client_session_delegate_->writable_time(), - client_session_delegate_->crypto_handshake_time()); - - ASSERT_TRUE(server_session_delegate_->writable_time().IsInitialized()); - ASSERT_TRUE( - server_session_delegate_->crypto_handshake_time().IsInitialized()); - // On server, the writable time and crypto handshake are the same. (when SHLO - // is sent). - ASSERT_EQ(server_session_delegate_->writable_time(), - server_session_delegate_->crypto_handshake_time()); -} - -TEST_F(QuartcSessionTest, PreSharedKeyHandshake) { - QuartcSessionConfig config; - config.pre_shared_key = "foo"; - CreateClientAndServerSessions(config); - AwaitHandshake(); - TestSendReceiveStreams(); - TestSendReceiveMessage(); -} - -// Test that data streams are not created before handshake. -TEST_F(QuartcSessionTest, CannotCreateDataStreamBeforeHandshake) { - CreateClientAndServerSessions(QuartcSessionConfig()); - EXPECT_EQ(nullptr, server_peer_->CreateOutgoingBidirectionalStream()); - EXPECT_EQ(nullptr, client_peer_->CreateOutgoingBidirectionalStream()); -} - -TEST_F(QuartcSessionTest, CancelQuartcStream) { - CreateClientAndServerSessions(QuartcSessionConfig()); - AwaitHandshake(); - ASSERT_TRUE(client_peer_->OneRttKeysAvailable()); - ASSERT_TRUE(server_peer_->OneRttKeysAvailable()); - - QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream(); - ASSERT_NE(nullptr, stream); - - uint32_t id = stream->id(); - EXPECT_FALSE(client_peer_->IsClosedStream(id)); - stream->SetDelegate(client_stream_delegate_.get()); - client_peer_->CancelStream(id); - EXPECT_EQ(stream->stream_error(), - QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED); - EXPECT_TRUE(client_peer_->IsClosedStream(id)); -} - -// TODO(b/112561077): This is the wrong layer for this test. We should write a -// test specifically for QuartcPacketWriter with a stubbed-out -// QuartcPacketTransport and remove -// SimulatedQuartcPacketTransport::last_packet_number(). -TEST_F(QuartcSessionTest, WriterGivesPacketNumberToTransport) { - CreateClientAndServerSessions(QuartcSessionConfig()); - AwaitHandshake(); - ASSERT_TRUE(client_peer_->OneRttKeysAvailable()); - ASSERT_TRUE(server_peer_->OneRttKeysAvailable()); - - QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream(); - stream->SetDelegate(client_stream_delegate_.get()); - - test::QuicTestMemSliceVector stream_data = CreateMemSliceVector("Hello"); - stream->WriteMemSlices(stream_data.span(), /*fin=*/false); - RunTasks(); - - // The transport should see the latest packet number sent by QUIC. - EXPECT_EQ( - client_transport_->last_packet_number(), - client_peer_->connection()->sent_packet_manager().GetLargestSentPacket()); -} - -TEST_F(QuartcSessionTest, CloseConnection) { - CreateClientAndServerSessions(QuartcSessionConfig()); - AwaitHandshake(); - ASSERT_TRUE(client_peer_->OneRttKeysAvailable()); - ASSERT_TRUE(server_peer_->OneRttKeysAvailable()); - - client_peer_->CloseConnection("Connection closed by client"); - EXPECT_FALSE(client_session_delegate_->connected()); - RunTasks(); - EXPECT_FALSE(server_session_delegate_->connected()); -} - -TEST_F(QuartcSessionTest, StreamRetransmissionEnabled) { - CreateClientAndServerSessions(QuartcSessionConfig()); - AwaitHandshake(); - ASSERT_TRUE(client_peer_->OneRttKeysAvailable()); - ASSERT_TRUE(server_peer_->OneRttKeysAvailable()); - - QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream(); - QuicStreamId stream_id = stream->id(); - stream->SetDelegate(client_stream_delegate_.get()); - stream->set_cancel_on_loss(false); - - client_filter_->set_packets_to_drop(1); - - test::QuicTestMemSliceVector stream_data = CreateMemSliceVector("Hello"); - stream->WriteMemSlices(stream_data.span(), /*fin=*/false); - RunTasks(); - - // Stream data should make it despite packet loss. - ASSERT_TRUE(server_stream_delegate_->has_data()); - EXPECT_EQ(server_stream_delegate_->data()[stream_id], "Hello"); -} - -TEST_F(QuartcSessionTest, StreamRetransmissionDisabled) { - // Disable tail loss probe, otherwise test maybe flaky because dropped - // message will be retransmitted to detect tail loss. - QuartcSessionConfig session_config; - session_config.enable_tail_loss_probe = false; - CreateClientAndServerSessions(session_config); - - // Disable probing retransmissions, otherwise test maybe flaky because dropped - // message will be retransmitted to to probe for more bandwidth. - client_peer_->connection()->set_fill_up_link_during_probing(false); - - AwaitHandshake(); - ASSERT_TRUE(client_peer_->OneRttKeysAvailable()); - ASSERT_TRUE(server_peer_->OneRttKeysAvailable()); - - // The client sends an ACK for the crypto handshake next. This must be - // flushed before we set the filter to drop the next packet, in order to - // ensure that the filter drops a data-bearing packet instead of just an ack. - RunTasks(); - - QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream(); - QuicStreamId stream_id = stream->id(); - stream->SetDelegate(client_stream_delegate_.get()); - stream->set_cancel_on_loss(true); - - client_filter_->set_packets_to_drop(1); - - test::QuicTestMemSliceVector stream_data = CreateMemSliceVector("Hello"); - stream->WriteMemSlices(stream_data.span(), /*fin=*/false); - simulator_.RunFor(QuicTime::Delta::FromMilliseconds(1)); - - // Send another packet to trigger loss detection. - QuartcStream* stream_1 = client_peer_->CreateOutgoingBidirectionalStream(); - stream_1->SetDelegate(client_stream_delegate_.get()); - - test::QuicTestMemSliceVector stream_data_1 = - CreateMemSliceVector("Second message"); - stream_1->WriteMemSlices(stream_data_1.span(), /*fin=*/false); - RunTasks(); - - // QUIC should try to retransmit the first stream by loss detection. Instead, - // it will cancel itself. - EXPECT_THAT(server_stream_delegate_->data()[stream_id], testing::IsEmpty()); - - EXPECT_TRUE(client_peer_->IsClosedStream(stream_id)); - EXPECT_TRUE(server_peer_->IsClosedStream(stream_id)); - EXPECT_THAT(client_stream_delegate_->stream_error(stream_id), - test::IsStreamError(QUIC_STREAM_CANCELLED)); - EXPECT_THAT(server_stream_delegate_->stream_error(stream_id), - test::IsStreamError(QUIC_STREAM_CANCELLED)); -} - -TEST_F(QuartcSessionTest, LostDatagramNotifications) { - // Disable tail loss probe, otherwise test maybe flaky because dropped - // message will be retransmitted to detect tail loss. - QuartcSessionConfig session_config; - session_config.enable_tail_loss_probe = false; - CreateClientAndServerSessions(session_config); - - // Disable probing retransmissions, otherwise test maybe flaky because dropped - // message will be retransmitted to to probe for more bandwidth. - client_peer_->connection()->set_fill_up_link_during_probing(false); - server_peer_->connection()->set_fill_up_link_during_probing(false); - - AwaitHandshake(); - ASSERT_TRUE(client_peer_->OneRttKeysAvailable()); - ASSERT_TRUE(server_peer_->OneRttKeysAvailable()); - - // The client sends an ACK for the crypto handshake next. This must be - // flushed before we set the filter to drop the next packet, in order to - // ensure that the filter drops a data-bearing packet instead of just an ack. - RunTasks(); - - // Drop the next packet. - client_filter_->set_packets_to_drop(1); - - test::QuicTestMemSliceVector message = - CreateMemSliceVector("This message will be lost"); - ASSERT_TRUE(client_peer_->SendOrQueueMessage(message.span(), 1)); - - RunTasks(); - - // Send another packet to elicit an ack and trigger loss detection. - message = CreateMemSliceVector("This message will arrive"); - ASSERT_TRUE(client_peer_->SendOrQueueMessage(message.span(), 2)); - - RunTasks(); - - EXPECT_THAT(server_session_delegate_->incoming_messages(), - ElementsAre("This message will arrive")); - EXPECT_THAT(client_session_delegate_->sent_datagram_ids(), ElementsAre(1, 2)); - EXPECT_THAT( - client_session_delegate_->acked_datagram_id_to_receive_timestamp(), - ElementsAre(Pair(2, Gt(QuicTime::Zero())))); - EXPECT_THAT(client_session_delegate_->lost_datagram_ids(), ElementsAre(1)); -} - -TEST_F(QuartcSessionTest, ServerRegistersAsWriteBlocked) { - // Initialize client and server session, but with the server write-blocked. - Init(); - server_transport_->SetWritable(false); - CreateClientAndServerSessions(QuartcSessionConfig(), /*init=*/false); - - // Let the client send a few copies of the CHLO. The server can't respond, as - // it's still write-blocked. - RunTasks(); - - // Making the server's transport writable should trigger a callback that - // reaches the server session, allowing it to write packets. - server_transport_->SetWritable(true); - - // Now the server should respond with the SHLO, encryption should be - // established, and data should flow normally. - // Note that if the server is *not* correctly registered as write-blocked, - // it will crash here (see b/124527328 for details). - AwaitHandshake(); - TestSendReceiveStreams(); -} - -TEST_F(QuartcSessionTest, PreSharedKeyHandshakeIs0RTT) { - QuartcSessionConfig session_config; - session_config.pre_shared_key = "foo"; - - // Client endpoint is created below. Destructing client endpoint - // causes issues with the simulator. - Init(/*create_client_endpoint=*/false); - - server_endpoint_->Connect(server_transport_.get()); - - client_endpoint_ = std::make_unique<QuartcClientEndpoint>( - simulator_.GetAlarmFactory(), simulator_.GetClock(), - simulator_.GetRandomGenerator(), client_session_delegate_.get(), - QuartcSessionConfig(), - // This is the key line here. It passes through the server config - // from the server to the client. - server_endpoint_->server_crypto_config()); - - client_endpoint_->Connect(client_transport_.get()); - - // Running for 1ms. This is shorter than the RTT, so the - // client session should be created, but server won't be created yet. - simulator_.RunFor(QuicTime::Delta::FromMilliseconds(1)); - - client_peer_ = client_session_delegate_->session(); - server_peer_ = server_session_delegate_->session(); - - ASSERT_NE(client_peer_, nullptr); - ASSERT_EQ(server_peer_, nullptr); - - // Write data to the client before running tasks. This should be sent by the - // client and received by the server if the handshake is 0RTT. - // If this test fails, add 'RunTasks()' above, and see what error is sent - // by the server in the rejection message. - QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream(); - ASSERT_NE(stream, nullptr); - QuicStreamId stream_id = stream->id(); - stream->SetDelegate(client_stream_delegate_.get()); - - char message[] = "Hello in 0RTTs!"; - test::QuicTestMemSliceVector data({std::make_pair(message, strlen(message))}); - stream->WriteMemSlices(data.span(), /*fin=*/false); - - // This will now run the rest of the connection. But the - // Server peer will receive the CHLO and message after 1 delay. - simulator_.RunFor(kPropagationDelayAndABit); - - // If we can decrypt the data, it means that 0 rtt was successful. - // This is because we waited only a propagation delay. So if the decryption - // failed, we would send sREJ instead of SHLO, but it wouldn't be delivered to - // the client yet. - ASSERT_TRUE(server_stream_delegate_->has_data()); - EXPECT_EQ(server_stream_delegate_->data()[stream_id], message); -} - -} // namespace - -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.cc deleted file mode 100644 index 01494c5ddf5..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.cc +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/quartc/quartc_stream.h" - -#include <memory> -#include <utility> - -#include "net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h" -#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" -#include "net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h" -#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h" -#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h" -#include "net/third_party/quiche/src/quic/core/quic_types.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" - -namespace quic { - -QuartcStream::QuartcStream(QuicStreamId id, QuicSession* session) - : QuicStream(id, session, /*is_static=*/false, BIDIRECTIONAL) { - sequencer()->set_level_triggered(true); -} - -QuartcStream::~QuartcStream() {} - -void QuartcStream::OnDataAvailable() { - size_t bytes_consumed = 0; - do { - bool fin = sequencer()->ReadableBytes() + sequencer()->NumBytesConsumed() == - sequencer()->close_offset(); - - // Upper bound on number of readable regions. Each complete block's worth - // of data crosses at most one region boundary. The remainder may cross one - // more boundary. Number of regions is one more than the number of region - // boundaries crossed. - size_t iov_length = sequencer()->ReadableBytes() / - QuicStreamSequencerBuffer::kBlockSizeBytes + - 2; - std::unique_ptr<iovec[]> iovecs = std::make_unique<iovec[]>(iov_length); - iov_length = sequencer()->GetReadableRegions(iovecs.get(), iov_length); - - bytes_consumed = delegate_->OnReceived(this, iovecs.get(), iov_length, fin); - sequencer()->MarkConsumed(bytes_consumed); - if (sequencer()->IsClosed()) { - OnFinRead(); - break; - } - } while (bytes_consumed > 0); -} - -void QuartcStream::OnClose() { - QuicStream::OnClose(); - DCHECK(delegate_); - delegate_->OnClose(this); -} - -void QuartcStream::OnStreamDataConsumed(QuicByteCount bytes_consumed) { - QuicStream::OnStreamDataConsumed(bytes_consumed); - - if (delegate_) { - delegate_->OnBufferChanged(this); - } -} - -void QuartcStream::OnDataBuffered( - QuicStreamOffset /*offset*/, - QuicByteCount /*data_length*/, - const QuicReferenceCountedPointer< - QuicAckListenerInterface>& /*ack_listener*/) { - if (delegate_) { - delegate_->OnBufferChanged(this); - } -} - -bool QuartcStream::OnStreamFrameAcked(QuicStreamOffset offset, - QuicByteCount data_length, - bool fin_acked, - QuicTime::Delta ack_delay_time, - QuicTime receive_timestamp, - QuicByteCount* newly_acked_length) { - // Previous losses of acked data are no longer relevant to the retransmission - // count. Once data is acked, it will never be retransmitted. - lost_frame_counter_.RemoveInterval( - QuicInterval<QuicStreamOffset>(offset, offset + data_length)); - - return QuicStream::OnStreamFrameAcked(offset, data_length, fin_acked, - ack_delay_time, receive_timestamp, - newly_acked_length); -} - -void QuartcStream::OnStreamFrameRetransmitted(QuicStreamOffset offset, - QuicByteCount data_length, - bool fin_retransmitted) { - QuicStream::OnStreamFrameRetransmitted(offset, data_length, - fin_retransmitted); - - DCHECK(delegate_); - delegate_->OnBufferChanged(this); -} - -void QuartcStream::OnStreamFrameLost(QuicStreamOffset offset, - QuicByteCount data_length, - bool fin_lost) { - QuicStream::OnStreamFrameLost(offset, data_length, fin_lost); - - lost_frame_counter_.AddInterval( - QuicInterval<QuicStreamOffset>(offset, offset + data_length)); - - DCHECK(delegate_); - delegate_->OnBufferChanged(this); -} - -void QuartcStream::OnCanWrite() { - if (lost_frame_counter_.MaxCount() > - static_cast<size_t>(max_retransmission_count_) && - HasPendingRetransmission()) { - Reset(QUIC_STREAM_CANCELLED); - return; - } - QuicStream::OnCanWrite(); -} - -bool QuartcStream::cancel_on_loss() { - return max_retransmission_count_ == 0; -} - -void QuartcStream::set_cancel_on_loss(bool cancel_on_loss) { - if (cancel_on_loss) { - max_retransmission_count_ = 0; - } else { - max_retransmission_count_ = std::numeric_limits<int>::max(); - } -} - -int QuartcStream::max_retransmission_count() const { - return max_retransmission_count_; -} - -void QuartcStream::set_max_retransmission_count(int max_retransmission_count) { - max_retransmission_count_ = max_retransmission_count; -} - -QuicByteCount QuartcStream::BytesPendingRetransmission() { - if (lost_frame_counter_.MaxCount() > - static_cast<size_t>(max_retransmission_count_)) { - return 0; // Lost bytes will never be retransmitted. - } - QuicByteCount bytes = 0; - for (const auto& interval : send_buffer().pending_retransmissions()) { - bytes += interval.Length(); - } - return bytes; -} - -QuicStreamOffset QuartcStream::ReadOffset() { - return sequencer()->NumBytesConsumed(); -} - -void QuartcStream::FinishWriting() { - WriteOrBufferData(quiche::QuicheStringPiece(nullptr, 0), true, nullptr); -} - -void QuartcStream::SetDelegate(Delegate* delegate) { - delegate_ = delegate; - DCHECK(delegate_); -} - -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.h deleted file mode 100644 index 7f3c28d005c..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.h +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) 2017 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_QUARTC_QUARTC_STREAM_H_ -#define QUICHE_QUIC_QUARTC_QUARTC_STREAM_H_ - -#include <stddef.h> -#include <limits> - -#include "net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h" -#include "net/third_party/quiche/src/quic/core/quic_session.h" -#include "net/third_party/quiche/src/quic/core/quic_stream.h" -#include "net/third_party/quiche/src/quic/core/quic_types.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h" -#include "net/quic/platform/impl/quic_export_impl.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_interval_counter.h" - -namespace quic { - -// Sends and receives data with a particular QUIC stream ID, reliably and -// in-order. To send/receive data out of order, use separate streams. To -// send/receive unreliably, close a stream after reliability is no longer -// needed. -class QuartcStream : public QuicStream { - public: - QuartcStream(QuicStreamId id, QuicSession* session); - - ~QuartcStream() override; - - // QuicStream overrides. - void OnDataAvailable() override; - - void OnClose() override; - - void OnStreamDataConsumed(QuicByteCount bytes_consumed) override; - - void OnDataBuffered( - QuicStreamOffset offset, - QuicByteCount data_length, - const QuicReferenceCountedPointer<QuicAckListenerInterface>& ack_listener) - override; - - bool OnStreamFrameAcked(QuicStreamOffset offset, - QuicByteCount data_length, - bool fin_acked, - QuicTime::Delta ack_delay_time, - QuicTime receive_timestamp, - QuicByteCount* newly_acked_length) override; - - void OnStreamFrameRetransmitted(QuicStreamOffset offset, - QuicByteCount data_length, - bool fin_retransmitted) override; - - void OnStreamFrameLost(QuicStreamOffset offset, - QuicByteCount data_length, - bool fin_lost) override; - - void OnCanWrite() override; - - // QuartcStream interface methods. - - // Whether the stream should be cancelled instead of retransmitted on loss. - // If set to true, the stream will reset itself instead of retransmitting lost - // stream frames. Defaults to false. Setting it to true is equivalent to - // setting |max_retransmission_count| to zero. - bool cancel_on_loss(); - void set_cancel_on_loss(bool cancel_on_loss); - - // Maximum number of times this stream's data may be retransmitted. Each byte - // of stream data may be retransmitted this many times. If any byte (or range - // of bytes) is lost and would be retransmitted more than this number of - // times, the stream resets itself instead of retransmitting the data again. - // Setting this value to zero disables retransmissions. - // - // Note that this limit applies only to stream data, not to the FIN bit. If - // only the FIN bit needs to be retransmitted, there is no benefit to - // cancelling the stream and sending a reset frame instead. - int max_retransmission_count() const; - void set_max_retransmission_count(int max_retransmission_count); - - QuicByteCount BytesPendingRetransmission(); - - // Returns the current read offset for this stream. During a call to - // Delegate::OnReceived, this value is the offset of the first byte read. - QuicStreamOffset ReadOffset(); - - // Marks this stream as finished writing. Asynchronously sends a FIN and - // closes the write-side. It is not necessary to call FinishWriting() if the - // last call to Write() sends a FIN. - void FinishWriting(); - - // Implemented by the user of the QuartcStream to receive incoming - // data and be notified of state changes. - class Delegate { - public: - virtual ~Delegate() {} - - // Called when the stream receives data. |iov| is a pointer to the first of - // |iov_length| readable regions. |iov| points to readable data within - // |stream|'s sequencer buffer. QUIC may modify or delete this data after - // the application consumes it. |fin| indicates the end of stream data. - // Returns the number of bytes consumed. May return 0 if the delegate is - // unable to consume any bytes at this time. - virtual size_t OnReceived(QuartcStream* stream, - iovec* iov, - size_t iov_length, - bool fin) = 0; - - // Called when the stream is closed, either locally or by the remote - // endpoint. Streams close when (a) fin bits are both sent and received, - // (b) Close() is called, or (c) the stream is reset. - // TODO(zhihuang) Creates a map from the integer error_code to WebRTC native - // error code. - virtual void OnClose(QuartcStream* stream) = 0; - - // Called when the contents of the stream's buffer changes. - virtual void OnBufferChanged(QuartcStream* stream) = 0; - }; - - // The |delegate| is not owned by QuartcStream. - void SetDelegate(Delegate* delegate); - - private: - Delegate* delegate_ = nullptr; - - // Maximum number of times this stream's data may be retransmitted. - int max_retransmission_count_ = std::numeric_limits<int>::max(); - - // Counter which tracks the number of times each frame has been lost - // (accounting for the possibility of overlapping frames). - // - // If the maximum count of any lost frame exceeds |max_retransmission_count_|, - // the stream will cancel itself on the next attempt to retransmit data (the - // next call to |OnCanWrite|). - QuartcIntervalCounter<QuicStreamOffset> lost_frame_counter_; -}; - -} // namespace quic - -#endif // QUICHE_QUIC_QUARTC_QUARTC_STREAM_H_ diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream_test.cc deleted file mode 100644 index b387e7f9723..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream_test.cc +++ /dev/null @@ -1,656 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/quartc/quartc_stream.h" - -#include <memory> -#include <string> -#include <type_traits> -#include <utility> - -#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" -#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h" -#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h" -#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h" -#include "net/third_party/quiche/src/quic/core/quic_clock.h" -#include "net/third_party/quiche/src/quic/core/quic_config.h" -#include "net/third_party/quiche/src/quic/core/quic_connection.h" -#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" -#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" -#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" -#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" -#include "net/third_party/quiche/src/quic/core/quic_session.h" -#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" -#include "net/third_party/quiche/src/quic/core/quic_time.h" -#include "net/third_party/quiche/src/quic/core/quic_types.h" -#include "net/third_party/quiche/src/quic/core/quic_utils.h" -#include "net/third_party/quiche/src/quic/core/quic_versions.h" -#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_factory.h" -#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" -#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_endian.h" -#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" - -using ::quic::test::IsQuicStreamNoError; -using ::quic::test::IsStreamError; - -namespace quic { - -namespace { - -static const QuicStreamId kStreamId = 5; - -// MockQuicSession that does not create streams and writes data from -// QuicStream to a string. -class MockQuicSession : public QuicSession { - public: - MockQuicSession(QuicConnection* connection, - const QuicConfig& config, - std::string* write_buffer) - : QuicSession(connection, - nullptr /*visitor*/, - config, - CurrentSupportedVersions(), - /*num_expected_unidirectional_static_streams = */ 0), - write_buffer_(write_buffer) {} - - ~MockQuicSession() override {} - - // Writes outgoing data from QuicStream to a string. - QuicConsumedData WritevData( - QuicStreamId id, - size_t write_length, - QuicStreamOffset offset, - StreamSendingState state, - TransmissionType /*type*/, - quiche::QuicheOptional<EncryptionLevel> /*level*/) override { - if (!writable_) { - return QuicConsumedData(0, false); - } - - // WritevData does not pass down a iovec, data is saved in stream before - // data is consumed. Retrieve data from stream. - char* buf = new char[write_length]; - QuicDataWriter writer(write_length, buf, quiche::NETWORK_BYTE_ORDER); - QuicStream* stream = GetOrCreateStream(id); - DCHECK(stream); - if (write_length > 0) { - stream->WriteStreamData(offset, write_length, &writer); - } - write_buffer_->append(buf, write_length); - delete[] buf; - return QuicConsumedData(write_length, state != StreamSendingState::NO_FIN); - } - - QuartcStream* CreateIncomingStream(QuicStreamId /*id*/) override { - return nullptr; - } - - QuartcStream* CreateIncomingStream(PendingStream* /*pending*/) override { - return nullptr; - } - - const QuicCryptoStream* GetCryptoStream() const override { return nullptr; } - QuicCryptoStream* GetMutableCryptoStream() override { return nullptr; } - bool ShouldKeepConnectionAlive() const override { - return GetNumActiveStreams() > 0; - } - - // Called by QuicStream when they want to close stream. - void SendRstStream(QuicStreamId /*id*/, - QuicRstStreamErrorCode /*error*/, - QuicStreamOffset /*bytes_written*/) override {} - - // Sets whether data is written to buffer, or else if this is write blocked. - void set_writable(bool writable) { writable_ = writable; } - - // Tracks whether the stream is write blocked and its priority. - void RegisterReliableStream(QuicStreamId stream_id, - spdy::SpdyPriority priority) { - write_blocked_streams()->RegisterStream( - stream_id, - /*is_static_stream=*/false, spdy::SpdyStreamPrecedence(priority)); - } - - // The session take ownership of the stream. - void ActivateReliableStream(std::unique_ptr<QuicStream> stream) { - ActivateStream(std::move(stream)); - } - - private: - // Stores written data from ReliableQuicStreamAdapter. - std::string* write_buffer_; - // Whether data is written to write_buffer_. - bool writable_ = true; -}; - -// Packet writer that does nothing. This is required for QuicConnection but -// isn't used for writing data. -class DummyPacketWriter : public QuicPacketWriter { - public: - DummyPacketWriter() {} - - // QuicPacketWriter overrides. - WriteResult WritePacket(const char* /*buffer*/, - size_t /*buf_len*/, - const QuicIpAddress& /*self_address*/, - const QuicSocketAddress& /*peer_address*/, - PerPacketOptions* /*options*/) override { - return WriteResult(WRITE_STATUS_ERROR, 0); - } - - bool IsWriteBlocked() const override { return false; } - - void SetWritable() override {} - - QuicByteCount GetMaxPacketSize( - const QuicSocketAddress& /*peer_address*/) const override { - return 0; - } - - bool SupportsReleaseTime() const override { return false; } - - bool IsBatchMode() const override { return false; } - - char* GetNextWriteLocation( - const QuicIpAddress& /*self_address*/, - const QuicSocketAddress& /*peer_address*/) override { - return nullptr; - } - - WriteResult Flush() override { return WriteResult(WRITE_STATUS_OK, 0); } -}; - -class MockQuartcStreamDelegate : public QuartcStream::Delegate { - public: - MockQuartcStreamDelegate(QuicStreamId id, std::string* read_buffer) - : id_(id), read_buffer_(read_buffer) {} - - void OnBufferChanged(QuartcStream* stream) override { - last_bytes_buffered_ = stream->BufferedDataBytes(); - last_bytes_pending_retransmission_ = stream->BytesPendingRetransmission(); - } - - size_t OnReceived(QuartcStream* stream, - iovec* iov, - size_t iov_length, - bool /*fin*/) override { - EXPECT_EQ(id_, stream->id()); - EXPECT_EQ(stream->ReadOffset(), read_buffer_->size()); - size_t bytes_consumed = 0; - for (size_t i = 0; i < iov_length; ++i) { - read_buffer_->append(static_cast<const char*>(iov[i].iov_base), - iov[i].iov_len); - bytes_consumed += iov[i].iov_len; - } - return bytes_consumed; - } - - void OnClose(QuartcStream* /*stream*/) override { closed_ = true; } - - bool closed() { return closed_; } - - QuicByteCount last_bytes_buffered() { return last_bytes_buffered_; } - QuicByteCount last_bytes_pending_retransmission() { - return last_bytes_pending_retransmission_; - } - - protected: - QuicStreamId id_; - // Data read by the QuicStream. - std::string* read_buffer_; - // Whether the QuicStream is closed. - bool closed_ = false; - - // Last amount of data observed as buffered. - QuicByteCount last_bytes_buffered_ = 0; - QuicByteCount last_bytes_pending_retransmission_ = 0; -}; - -class QuartcStreamTest : public QuicTestWithParam<ParsedQuicVersion>, - public QuicConnectionHelperInterface { - public: - QuartcStreamTest() : version_(GetParam()) {} - - ~QuartcStreamTest() override = default; - - void CreateReliableQuicStream() { - // Arbitrary values for QuicConnection. - Perspective perspective = Perspective::IS_SERVER; - QuicIpAddress ip; - ip.FromString("0.0.0.0"); - bool owns_writer = true; - - alarm_factory_ = std::make_unique<test::MockAlarmFactory>(); - - connection_ = std::make_unique<QuicConnection>( - QuicUtils::CreateZeroConnectionId(version_.transport_version), - QuicSocketAddress(ip, 0), this /*QuicConnectionHelperInterface*/, - alarm_factory_.get(), new DummyPacketWriter(), owns_writer, perspective, - ParsedQuicVersionVector{version_}); - clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); - session_ = std::make_unique<MockQuicSession>(connection_.get(), - QuicConfig(), &write_buffer_); - mock_stream_delegate_ = - std::make_unique<MockQuartcStreamDelegate>(kStreamId, &read_buffer_); - stream_ = new QuartcStream(kStreamId, session_.get()); - stream_->SetDelegate(mock_stream_delegate_.get()); - session_->ActivateReliableStream(std::unique_ptr<QuartcStream>(stream_)); - } - - const QuicClock* GetClock() const override { return &clock_; } - - QuicRandom* GetRandomGenerator() override { - return QuicRandom::GetInstance(); - } - - QuicBufferAllocator* GetStreamSendBufferAllocator() override { - return &buffer_allocator_; - } - - protected: - const ParsedQuicVersion version_; - // The QuicSession will take the ownership. - QuartcStream* stream_; - std::unique_ptr<MockQuartcStreamDelegate> mock_stream_delegate_; - std::unique_ptr<MockQuicSession> session_; - // Data written by the ReliableQuicStreamAdapterTest. - std::string write_buffer_; - // Data read by the ReliableQuicStreamAdapterTest. - std::string read_buffer_; - std::unique_ptr<QuicAlarmFactory> alarm_factory_; - std::unique_ptr<QuicConnection> connection_; - // Used to implement the QuicConnectionHelperInterface. - SimpleBufferAllocator buffer_allocator_; - MockClock clock_; -}; - -// TODO(b/150224094): Enable versions with TLS handshake. -INSTANTIATE_TEST_SUITE_P( - Tests, - QuartcStreamTest, - ::testing::ValuesIn(CurrentSupportedVersionsWithQuicCrypto()), - ::testing::PrintToStringParamName()); - -// Write an entire string. -TEST_P(QuartcStreamTest, WriteDataWhole) { - CreateReliableQuicStream(); - char message[] = "Foo bar"; - test::QuicTestMemSliceVector data({std::make_pair(message, 7)}); - stream_->WriteMemSlices(data.span(), /*fin=*/false); - EXPECT_EQ("Foo bar", write_buffer_); -} - -// Write part of a string. -TEST_P(QuartcStreamTest, WriteDataPartial) { - CreateReliableQuicStream(); - char message[] = "Foo bar"; - test::QuicTestMemSliceVector data({std::make_pair(message, 5)}); - stream_->WriteMemSlices(data.span(), /*fin=*/false); - EXPECT_EQ("Foo b", write_buffer_); -} - -// Test that a QuartcStream buffers writes correctly. -TEST_P(QuartcStreamTest, StreamBuffersData) { - CreateReliableQuicStream(); - - char message[] = "Foo bar"; - test::QuicTestMemSliceVector data({std::make_pair(message, 7)}); - - // The stream is not yet writable, so data will be buffered. - session_->set_writable(false); - stream_->WriteMemSlices(data.span(), /*fin=*/false); - - // Check that data is buffered. - EXPECT_TRUE(stream_->HasBufferedData()); - EXPECT_EQ(7u, stream_->BufferedDataBytes()); - - // Check that the stream told its delegate about the buffer change. - EXPECT_EQ(7u, mock_stream_delegate_->last_bytes_buffered()); - - // Check that none of the data was written yet. - // Note that |write_buffer_| actually holds data written by the QuicSession - // (not data buffered by the stream). - EXPECT_EQ(0ul, write_buffer_.size()); - - char message1[] = "xyzzy"; - test::QuicTestMemSliceVector data1({std::make_pair(message1, 5)}); - - // More writes go into the buffer. - stream_->WriteMemSlices(data1.span(), /*fin=*/false); - - EXPECT_TRUE(stream_->HasBufferedData()); - EXPECT_EQ(12u, stream_->BufferedDataBytes()); - EXPECT_EQ(12u, mock_stream_delegate_->last_bytes_buffered()); - EXPECT_EQ(0ul, write_buffer_.size()); - - // The stream becomes writable, so it sends the buffered data. - session_->set_writable(true); - stream_->OnCanWrite(); - - EXPECT_FALSE(stream_->HasBufferedData()); - EXPECT_EQ(0u, stream_->BufferedDataBytes()); - EXPECT_EQ(0u, mock_stream_delegate_->last_bytes_buffered()); - EXPECT_EQ("Foo barxyzzy", write_buffer_); -} - -// Finish writing to a stream. -// It delivers the fin bit and closes the write-side as soon as possible. -TEST_P(QuartcStreamTest, FinishWriting) { - CreateReliableQuicStream(); - - session_->set_writable(false); - stream_->FinishWriting(); - EXPECT_FALSE(stream_->fin_sent()); - - // Fin is sent as soon as the stream becomes writable. - session_->set_writable(true); - stream_->OnCanWrite(); - EXPECT_TRUE(stream_->fin_sent()); - EXPECT_TRUE(stream_->write_side_closed()); -} - -// Read an entire string. -TEST_P(QuartcStreamTest, ReadDataWhole) { - CreateReliableQuicStream(); - QuicStreamFrame frame(kStreamId, false, 0, "Hello, World!"); - stream_->OnStreamFrame(frame); - - EXPECT_EQ("Hello, World!", read_buffer_); -} - -// Read part of a string. -TEST_P(QuartcStreamTest, ReadDataPartial) { - CreateReliableQuicStream(); - QuicStreamFrame frame(kStreamId, false, 0, "Hello, World!"); - frame.data_length = 5; - stream_->OnStreamFrame(frame); - - EXPECT_EQ("Hello", read_buffer_); -} - -// Streams do not call OnReceived() after StopReading(). -// Note: this is tested here because Quartc relies on this behavior. -TEST_P(QuartcStreamTest, StopReading) { - CreateReliableQuicStream(); - stream_->StopReading(); - - QuicStreamFrame frame(kStreamId, false, 0, "Hello, World!"); - stream_->OnStreamFrame(frame); - - EXPECT_EQ(0ul, read_buffer_.size()); - - QuicStreamFrame frame2(kStreamId, true, 0, "Hello, World!"); - stream_->OnStreamFrame(frame2); - - EXPECT_EQ(0ul, read_buffer_.size()); - EXPECT_TRUE(stream_->fin_received()); -} - -// Test that closing the stream results in a callback. -TEST_P(QuartcStreamTest, CloseStream) { - CreateReliableQuicStream(); - EXPECT_FALSE(mock_stream_delegate_->closed()); - if (GetQuicReloadableFlag(quic_break_session_stream_close_loop)) { - stream_->CloseWriteSide(); - stream_->CloseReadSide(); - } else { - stream_->OnClose(); - } - EXPECT_TRUE(mock_stream_delegate_->closed()); -} - -// Both sending and receiving fin automatically closes a stream. -TEST_P(QuartcStreamTest, CloseOnFins) { - CreateReliableQuicStream(); - QuicStreamFrame frame(kStreamId, true, 0, 0); - stream_->OnStreamFrame(frame); - - test::QuicTestMemSliceVector data({}); - stream_->WriteMemSlices(data.span(), /*fin=*/true); - - // Check that the OnClose() callback occurred. - EXPECT_TRUE(mock_stream_delegate_->closed()); -} - -TEST_P(QuartcStreamTest, TestCancelOnLossDisabled) { - CreateReliableQuicStream(); - - // This should be the default state. - EXPECT_FALSE(stream_->cancel_on_loss()); - - char message[] = "Foo bar"; - test::QuicTestMemSliceVector data({std::make_pair(message, 7)}); - stream_->WriteMemSlices(data.span(), /*fin=*/false); - - EXPECT_EQ("Foo bar", write_buffer_); - - stream_->OnStreamFrameLost(0, 7, false); - stream_->OnCanWrite(); - - EXPECT_EQ("Foo barFoo bar", write_buffer_); - EXPECT_THAT(stream_->stream_error(), IsQuicStreamNoError()); -} - -TEST_P(QuartcStreamTest, TestCancelOnLossEnabled) { - CreateReliableQuicStream(); - stream_->set_cancel_on_loss(true); - - char message[] = "Foo bar"; - test::QuicTestMemSliceVector data({std::make_pair(message, 7)}); - stream_->WriteMemSlices(data.span(), /*fin=*/false); - - EXPECT_EQ("Foo bar", write_buffer_); - - stream_->OnStreamFrameLost(0, 7, false); - stream_->OnCanWrite(); - - EXPECT_EQ("Foo bar", write_buffer_); - EXPECT_THAT(stream_->stream_error(), IsStreamError(QUIC_STREAM_CANCELLED)); -} - -TEST_P(QuartcStreamTest, MaxRetransmissionsAbsent) { - CreateReliableQuicStream(); - - // This should be the default state. - EXPECT_EQ(stream_->max_retransmission_count(), - std::numeric_limits<int>::max()); - - char message[] = "Foo bar"; - test::QuicTestMemSliceVector data({std::make_pair(message, 7)}); - stream_->WriteMemSlices(data.span(), /*fin=*/false); - - EXPECT_EQ("Foo bar", write_buffer_); - - stream_->OnStreamFrameLost(0, 7, false); - stream_->OnCanWrite(); - - EXPECT_EQ("Foo barFoo bar", write_buffer_); - EXPECT_THAT(stream_->stream_error(), IsQuicStreamNoError()); -} - -TEST_P(QuartcStreamTest, MaxRetransmissionsSet) { - CreateReliableQuicStream(); - stream_->set_max_retransmission_count(2); - - char message[] = "Foo bar"; - test::QuicTestMemSliceVector data({std::make_pair(message, 7)}); - stream_->WriteMemSlices(data.span(), /*fin=*/false); - - EXPECT_EQ("Foo bar", write_buffer_); - - stream_->OnStreamFrameLost(0, 7, false); - stream_->OnCanWrite(); - - EXPECT_EQ("Foo barFoo bar", write_buffer_); - - stream_->OnStreamFrameLost(0, 7, false); - stream_->OnCanWrite(); - - EXPECT_EQ("Foo barFoo barFoo bar", write_buffer_); - - stream_->OnStreamFrameLost(0, 7, false); - stream_->OnCanWrite(); - - EXPECT_EQ("Foo barFoo barFoo bar", write_buffer_); - EXPECT_THAT(stream_->stream_error(), IsStreamError(QUIC_STREAM_CANCELLED)); -} - -TEST_P(QuartcStreamTest, MaxRetransmissionsDisjointFrames) { - CreateReliableQuicStream(); - stream_->set_max_retransmission_count(2); - - char message[] = "Foo bar"; - test::QuicTestMemSliceVector data({std::make_pair(message, 7)}); - stream_->WriteMemSlices(data.span(), /*fin=*/false); - - EXPECT_EQ("Foo bar", write_buffer_); - - // Retransmit bytes [0, 3]. - stream_->OnStreamFrameLost(0, 4, false); - stream_->OnCanWrite(); - - EXPECT_EQ("Foo barFoo ", write_buffer_); - - // Retransmit bytes [4, 6]. Everything has been retransmitted once. - stream_->OnStreamFrameLost(4, 3, false); - stream_->OnCanWrite(); - - EXPECT_EQ("Foo barFoo bar", write_buffer_); - - // Retransmit bytes [0, 6]. Everything can be retransmitted a second time. - stream_->OnStreamFrameLost(0, 7, false); - stream_->OnCanWrite(); - - EXPECT_EQ("Foo barFoo barFoo bar", write_buffer_); -} - -TEST_P(QuartcStreamTest, MaxRetransmissionsOverlappingFrames) { - CreateReliableQuicStream(); - stream_->set_max_retransmission_count(2); - - char message[] = "Foo bar"; - test::QuicTestMemSliceVector data({std::make_pair(message, 7)}); - stream_->WriteMemSlices(data.span(), /*fin=*/false); - - EXPECT_EQ("Foo bar", write_buffer_); - - // Retransmit bytes 0 to 3. - stream_->OnStreamFrameLost(0, 4, false); - stream_->OnCanWrite(); - - EXPECT_EQ("Foo barFoo ", write_buffer_); - - // Retransmit bytes 3 to 6. Byte 3 has been retransmitted twice. - stream_->OnStreamFrameLost(3, 4, false); - stream_->OnCanWrite(); - - EXPECT_EQ("Foo barFoo bar", write_buffer_); - - // Retransmit byte 3 a third time. This should cause cancellation. - stream_->OnStreamFrameLost(3, 1, false); - stream_->OnCanWrite(); - - EXPECT_EQ("Foo barFoo bar", write_buffer_); - EXPECT_THAT(stream_->stream_error(), IsStreamError(QUIC_STREAM_CANCELLED)); -} - -TEST_P(QuartcStreamTest, MaxRetransmissionsWithAckedFrame) { - CreateReliableQuicStream(); - stream_->set_max_retransmission_count(1); - - char message[] = "Foo bar"; - test::QuicTestMemSliceVector data({std::make_pair(message, 7)}); - stream_->WriteMemSlices(data.span(), /*fin=*/false); - - EXPECT_EQ("Foo bar", write_buffer_); - - // Retransmit bytes [0, 7). - stream_->OnStreamFrameLost(0, 7, false); - stream_->OnCanWrite(); - - EXPECT_EQ("Foo barFoo bar", write_buffer_); - - // Ack bytes [0, 7). These bytes should be pruned from the data tracked by - // the stream. - QuicByteCount newly_acked_length = 0; - stream_->OnStreamFrameAcked(0, 7, false, QuicTime::Delta::FromMilliseconds(1), - QuicTime::Zero(), &newly_acked_length); - EXPECT_EQ(7u, newly_acked_length); - stream_->OnCanWrite(); - - EXPECT_EQ("Foo barFoo bar", write_buffer_); - - // Retransmit bytes [0, 7) again. - // QUIC will never mark frames as lost after they've been acked, but this lets - // us test that QuartcStream stopped tracking these bytes after the acked. - stream_->OnStreamFrameLost(0, 7, false); - stream_->OnCanWrite(); - - // QuartcStream should be cancelled, but it stopped tracking the lost bytes - // after they were acked, so it's not. - EXPECT_THAT(stream_->stream_error(), IsQuicStreamNoError()); -} - -TEST_P(QuartcStreamTest, TestBytesPendingRetransmission) { - CreateReliableQuicStream(); - stream_->set_cancel_on_loss(false); - - char message[] = "Foo bar"; - test::QuicTestMemSliceVector data({std::make_pair(message, 7)}); - stream_->WriteMemSlices(data.span(), /*fin=*/false); - - EXPECT_EQ("Foo bar", write_buffer_); - - stream_->OnStreamFrameLost(0, 4, false); - EXPECT_EQ(stream_->BytesPendingRetransmission(), 4u); - EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 4u); - - stream_->OnStreamFrameLost(4, 3, false); - EXPECT_EQ(stream_->BytesPendingRetransmission(), 7u); - EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 7u); - - stream_->OnCanWrite(); - EXPECT_EQ(stream_->BytesPendingRetransmission(), 0u); - EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 0u); - - EXPECT_EQ("Foo barFoo bar", write_buffer_); - EXPECT_THAT(stream_->stream_error(), IsQuicStreamNoError()); -} - -TEST_P(QuartcStreamTest, TestBytesPendingRetransmissionWithCancelOnLoss) { - CreateReliableQuicStream(); - stream_->set_cancel_on_loss(true); - - char message[] = "Foo bar"; - test::QuicTestMemSliceVector data({std::make_pair(message, 7)}); - stream_->WriteMemSlices(data.span(), /*fin=*/false); - - EXPECT_EQ("Foo bar", write_buffer_); - - stream_->OnStreamFrameLost(0, 4, false); - EXPECT_EQ(stream_->BytesPendingRetransmission(), 0u); - EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 0u); - - stream_->OnStreamFrameLost(4, 3, false); - EXPECT_EQ(stream_->BytesPendingRetransmission(), 0u); - EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 0u); - - stream_->OnCanWrite(); - EXPECT_EQ(stream_->BytesPendingRetransmission(), 0u); - EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 0u); - - EXPECT_EQ("Foo bar", write_buffer_); - EXPECT_THAT(stream_->stream_error(), IsStreamError(QUIC_STREAM_CANCELLED)); -} - -} // namespace - -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.cc b/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.cc deleted file mode 100644 index 263c21395e0..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.cc +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h" - -#include <utility> - -#include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h" - -namespace quic { -namespace simulator { - -SimulatedQuartcPacketTransport::SimulatedQuartcPacketTransport( - Simulator* simulator, - const std::string& name, - const std::string& peer_name, - QuicByteCount queue_capacity) - : Endpoint(simulator, name), - peer_name_(peer_name), - egress_queue_(simulator, - quiche::QuicheStringPrintf("%s (TX Queue)", name.c_str()), - queue_capacity) { - egress_queue_.set_listener_interface(this); -} - -int SimulatedQuartcPacketTransport::Write(const char* buffer, - size_t buf_len, - const PacketInfo& info) { - if (!writable_) { - return 0; - } - if (egress_queue_.bytes_queued() + buf_len > egress_queue_.capacity()) { - return 0; - } - - last_packet_number_ = info.packet_number; - - auto packet = std::make_unique<Packet>(); - packet->contents = std::string(buffer, buf_len); - packet->size = buf_len; - packet->tx_timestamp = clock_->Now(); - packet->source = name(); - packet->destination = peer_name_; - - egress_queue_.AcceptPacket(std::move(packet)); - return buf_len; -} - -void SimulatedQuartcPacketTransport::SetDelegate(Delegate* delegate) { - delegate_ = delegate; - Schedule(clock_->Now()); -} - -UnconstrainedPortInterface* SimulatedQuartcPacketTransport::GetRxPort() { - return this; -} - -void SimulatedQuartcPacketTransport::SetTxPort(ConstrainedPortInterface* port) { - egress_queue_.set_tx_port(port); - Schedule(clock_->Now()); -} - -void SimulatedQuartcPacketTransport::AcceptPacket( - std::unique_ptr<Packet> packet) { - // Simulated switches broadcast packets to all ports if the cannot determine - // the recipient, so we need to drop packets that aren't intended for us. - if (packet->destination != name()) { - return; - } - - if (delegate_) { - delegate_->OnTransportReceived(packet->contents.data(), packet->size); - } -} - -void SimulatedQuartcPacketTransport::OnPacketDequeued() { - if (delegate_ && writable_) { - delegate_->OnTransportCanWrite(); - } -} - -void SimulatedQuartcPacketTransport::Act() { - if (delegate_ && writable_) { - delegate_->OnTransportCanWrite(); - } -} - -void SimulatedQuartcPacketTransport::SetWritable(bool writable) { - writable_ = writable; - if (writable_) { - // May need to call |Delegate::OnTransportCanWrite|. - Schedule(clock_->Now()); - } -} - -} // namespace simulator -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h b/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h deleted file mode 100644 index 185668b528a..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2018 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_QUARTC_SIMULATED_PACKET_TRANSPORT_H_ -#define QUICHE_QUIC_QUARTC_SIMULATED_PACKET_TRANSPORT_H_ - -#include <string> - -#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h" -#include "net/third_party/quiche/src/quic/test_tools/simulator/port.h" -#include "net/third_party/quiche/src/quic/test_tools/simulator/queue.h" - -namespace quic { -namespace simulator { - -// Simulated implementation of QuartcPacketTransport. This packet transport -// implementation connects Quartc to a QUIC simulator's network fabric. -// Assumes that its caller and delegate run on the same thread as the network -// simulation and therefore require no additional synchronization. -class SimulatedQuartcPacketTransport : public Endpoint, - public QuartcPacketTransport, - public UnconstrainedPortInterface, - public Queue::ListenerInterface { - public: - SimulatedQuartcPacketTransport(Simulator* simulator, - const std::string& name, - const std::string& peer_name, - QuicByteCount queue_capacity); - - // QuartcPacketTransport methods. - int Write(const char* buffer, - size_t buf_len, - const PacketInfo& info) override; - void SetDelegate(Delegate* delegate) override; - - // Simulation methods below. These are implementation details. - - // Endpoint methods. Called by the simulation to connect the transport. - UnconstrainedPortInterface* GetRxPort() override; - void SetTxPort(ConstrainedPortInterface* port) override; - - // UnconstrainedPortInterface method. Called by the simulation to deliver a - // packet to this transport. - void AcceptPacket(std::unique_ptr<Packet> packet) override; - - // Queue::ListenerInterface method. Called when the internal egress queue has - // dispatched a packet and may have room for more. - void OnPacketDequeued() override; - - // Actor method. The transport schedules this to run when the delegate is set - // in order to trigger an initial call to |Delegate::OnTransportCanWrite()|. - // (The Quartc packet writer starts in a blocked state and needs an initial - // callback to unblock it.) - void Act() override; - - // Changes whether the transport is writable. If |writable| is false, the - // transport will reject calls to |Write| and will not call - // |Delegate::OnTransportCanWrite|. If |writable| is true, the transport will - // allow calls to |Write| and will call |Delegate::OnTransportCanWrite| - // whenever it is able to write another packet. - void SetWritable(bool writable); - - // Last packet number sent over this simulated transport. - // TODO(b/112561077): Reorganize tests so that this method can be deleted. - // This exists purely for use by quartc_session_test.cc, to test that the - // packet writer passes packet numbers to the transport. - QuicPacketNumber last_packet_number() { return last_packet_number_; } - - private: - std::string peer_name_; - Delegate* delegate_ = nullptr; - Queue egress_queue_; - QuicPacketNumber last_packet_number_; - - // Controls whether the transport is considered to be writable. Used to - // simulate behavior that arises when the transport is blocked. - bool writable_ = true; -}; - -} // namespace simulator -} // namespace quic - -#endif // QUICHE_QUIC_QUARTC_SIMULATED_PACKET_TRANSPORT_H_ diff --git a/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport_test.cc deleted file mode 100644 index 24a4f4fc49f..00000000000 --- a/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport_test.cc +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h" - -#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" -#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h" -#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h" -#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h" - -namespace quic { -namespace simulator { -namespace { - -using ::testing::ElementsAre; - -const QuicBandwidth kDefaultBandwidth = - QuicBandwidth::FromKBitsPerSecond(10 * 1000); -const QuicTime::Delta kDefaultPropagationDelay = - QuicTime::Delta::FromMilliseconds(20); -const QuicByteCount kDefaultBdp = kDefaultBandwidth * kDefaultPropagationDelay; -const QuicByteCount kDefaultPacketSize = 1200; -const QuicPacketCount kDefaultQueueLength = 10; - -class FakeDelegate : public QuartcPacketTransport::Delegate { - public: - explicit FakeDelegate(QuartcPacketTransport* transport) - : transport_(transport) { - transport_->SetDelegate(this); - } - - ~FakeDelegate() { transport_->SetDelegate(nullptr); } - - void OnTransportCanWrite() override { - while (!packets_to_send_.empty()) { - const std::string& packet = packets_to_send_.front(); - if (transport_->Write(packet.data(), packet.size(), - QuartcPacketTransport::PacketInfo()) < - static_cast<int>(packet.size())) { - ++write_blocked_count_; - return; - } - packets_to_send_.pop(); - } - } - - void OnTransportReceived(const char* data, size_t data_len) override { - packets_received_.emplace_back(data, data_len); - } - - void AddPacketToSend(const std::string& packet) { - packets_to_send_.push(packet); - } - - size_t packets_to_send() { return packets_to_send_.size(); } - const std::vector<std::string>& packets_received() { - return packets_received_; - } - int write_blocked_count() { return write_blocked_count_; } - - private: - QuartcPacketTransport* const transport_ = nullptr; - std::queue<std::string> packets_to_send_; - std::vector<std::string> packets_received_; - int write_blocked_count_ = 0; -}; - -class SimulatedPacketTransportTest : public QuicTest { - protected: - SimulatedPacketTransportTest() - : switch_(&simulator_, "Switch", /*port_count=*/8, 2 * kDefaultBdp), - client_(&simulator_, - "sender", - "receiver", - kDefaultQueueLength * kDefaultPacketSize), - server_(&simulator_, - "receiver", - "sender", - kDefaultQueueLength * kDefaultPacketSize), - client_link_(&client_, - switch_.port(1), - kDefaultBandwidth, - kDefaultPropagationDelay), - server_link_(&server_, - switch_.port(2), - kDefaultBandwidth, - kDefaultPropagationDelay), - client_delegate_(&client_), - server_delegate_(&server_) {} - - Simulator simulator_; - Switch switch_; - - SimulatedQuartcPacketTransport client_; - SimulatedQuartcPacketTransport server_; - - SymmetricLink client_link_; - SymmetricLink server_link_; - - FakeDelegate client_delegate_; - FakeDelegate server_delegate_; -}; - -TEST_F(SimulatedPacketTransportTest, OneWayTransmission) { - std::string packet_1(kDefaultPacketSize, 'a'); - std::string packet_2(kDefaultPacketSize, 'b'); - client_delegate_.AddPacketToSend(packet_1); - client_delegate_.AddPacketToSend(packet_2); - - simulator_.RunUntil( - [this] { return client_delegate_.packets_to_send() == 0; }); - simulator_.RunFor(3 * kDefaultPropagationDelay); - - EXPECT_THAT(server_delegate_.packets_received(), - ElementsAre(packet_1, packet_2)); - EXPECT_THAT(client_delegate_.packets_received(), ElementsAre()); -} - -TEST_F(SimulatedPacketTransportTest, TwoWayTransmission) { - std::string packet_1(kDefaultPacketSize, 'a'); - std::string packet_2(kDefaultPacketSize, 'b'); - std::string packet_3(kDefaultPacketSize, 'c'); - std::string packet_4(kDefaultPacketSize, 'd'); - - client_delegate_.AddPacketToSend(packet_1); - client_delegate_.AddPacketToSend(packet_2); - server_delegate_.AddPacketToSend(packet_3); - server_delegate_.AddPacketToSend(packet_4); - - simulator_.RunUntil( - [this] { return client_delegate_.packets_to_send() == 0; }); - simulator_.RunUntil( - [this] { return server_delegate_.packets_to_send() == 0; }); - simulator_.RunFor(3 * kDefaultPropagationDelay); - - EXPECT_THAT(server_delegate_.packets_received(), - ElementsAre(packet_1, packet_2)); - EXPECT_THAT(client_delegate_.packets_received(), - ElementsAre(packet_3, packet_4)); -} - -TEST_F(SimulatedPacketTransportTest, TestWriteBlocked) { - // Add 10 packets beyond what fits in the egress queue. - std::vector<std::string> packets; - for (unsigned int i = 0; i < kDefaultQueueLength + 10; ++i) { - packets.push_back(std::string(kDefaultPacketSize, 'a' + i)); - client_delegate_.AddPacketToSend(packets.back()); - } - - simulator_.RunUntil( - [this] { return client_delegate_.packets_to_send() == 0; }); - simulator_.RunFor(3 * kDefaultPropagationDelay); - - // Each of the 10 packets in excess of the sender's egress queue length will - // block the first time |client_delegate_| tries to write them. - EXPECT_EQ(client_delegate_.write_blocked_count(), 10); - EXPECT_EQ(server_delegate_.packets_received(), packets); -} - -} // namespace -} // namespace simulator -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.cc index 401ae7e4278..0fb988aed37 100644 --- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.cc @@ -23,7 +23,8 @@ QuicTransportStream::QuicTransportStream( /*is_static=*/false, QuicUtils::GetStreamType(id, session->connection()->perspective(), - session->IsIncomingStream(id))), + session->IsIncomingStream(id), + session->version())), session_interface_(session_interface) {} size_t QuicTransportStream::Read(char* buffer, size_t buffer_size) { diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/web_transport_fingerprint_proof_verifier.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/web_transport_fingerprint_proof_verifier.cc new file mode 100644 index 00000000000..81466de1240 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/quic_transport/web_transport_fingerprint_proof_verifier.cc @@ -0,0 +1,221 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/quic_transport/web_transport_fingerprint_proof_verifier.h" + +#include <cstdint> +#include <memory> + +#include "third_party/boringssl/src/include/openssl/sha.h" +#include "net/third_party/quiche/src/quic/core/crypto/certificate_view.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h" + +namespace quic { +namespace { + +constexpr size_t kFingerprintLength = SHA256_DIGEST_LENGTH * 3 - 1; + +constexpr std::array<char, 16> kHexDigits = {'0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f'}; + +// Assumes that the character is normalized to lowercase beforehand. +bool IsNormalizedHexDigit(char c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'); +} + +void NormalizeFingerprint(CertificateFingerprint& fingerprint) { + fingerprint.fingerprint = + quiche::QuicheTextUtils::ToLower(fingerprint.fingerprint); +} + +} // namespace + +constexpr char CertificateFingerprint::kSha256[]; + +std::string ComputeSha256Fingerprint(quiche::QuicheStringPiece input) { + std::vector<uint8_t> raw_hash; + raw_hash.resize(SHA256_DIGEST_LENGTH); + SHA256(reinterpret_cast<const uint8_t*>(input.data()), input.size(), + raw_hash.data()); + + std::string output; + output.resize(kFingerprintLength); + for (size_t i = 0; i < output.size(); i++) { + uint8_t hash_byte = raw_hash[i / 3]; + switch (i % 3) { + case 0: + output[i] = kHexDigits[hash_byte >> 4]; + break; + case 1: + output[i] = kHexDigits[hash_byte & 0xf]; + break; + case 2: + output[i] = ':'; + break; + } + } + return output; +} + +ProofVerifyDetails* WebTransportFingerprintProofVerifier::Details::Clone() + const { + return new Details(*this); +} + +WebTransportFingerprintProofVerifier::WebTransportFingerprintProofVerifier( + const QuicClock* clock, + int max_validity_days) + : clock_(clock), + max_validity_days_(max_validity_days), + // Add an extra second to max validity to accomodate various edge cases. + max_validity_( + QuicTime::Delta::FromSeconds(max_validity_days * 86400 + 1)) {} + +bool WebTransportFingerprintProofVerifier::AddFingerprint( + CertificateFingerprint fingerprint) { + NormalizeFingerprint(fingerprint); + if (fingerprint.algorithm != CertificateFingerprint::kSha256) { + QUIC_DLOG(WARNING) << "Algorithms other than SHA-256 are not supported"; + return false; + } + if (fingerprint.fingerprint.size() != kFingerprintLength) { + QUIC_DLOG(WARNING) << "Invalid fingerprint length"; + return false; + } + for (size_t i = 0; i < fingerprint.fingerprint.size(); i++) { + char current = fingerprint.fingerprint[i]; + if (i % 3 == 2) { + if (current != ':') { + QUIC_DLOG(WARNING) + << "Missing colon separator between the bytes of the hash"; + return false; + } + } else { + if (!IsNormalizedHexDigit(current)) { + QUIC_DLOG(WARNING) << "Fingerprint must be in hexadecimal"; + return false; + } + } + } + + fingerprints_.push_back(fingerprint); + return true; +} + +QuicAsyncStatus WebTransportFingerprintProofVerifier::VerifyProof( + const std::string& /*hostname*/, + const uint16_t /*port*/, + const std::string& /*server_config*/, + QuicTransportVersion /*transport_version*/, + quiche::QuicheStringPiece /*chlo_hash*/, + const std::vector<std::string>& /*certs*/, + const std::string& /*cert_sct*/, + const std::string& /*signature*/, + const ProofVerifyContext* /*context*/, + std::string* error_details, + std::unique_ptr<ProofVerifyDetails>* details, + std::unique_ptr<ProofVerifierCallback> /*callback*/) { + *error_details = + "QUIC crypto certificate verification is not supported in " + "WebTransportFingerprintProofVerifier"; + QUIC_BUG << *error_details; + *details = std::make_unique<Details>(Status::kInternalError); + return QUIC_FAILURE; +} + +QuicAsyncStatus WebTransportFingerprintProofVerifier::VerifyCertChain( + const std::string& /*hostname*/, + const uint16_t /*port*/, + const std::vector<std::string>& certs, + const std::string& /*ocsp_response*/, + const std::string& /*cert_sct*/, + const ProofVerifyContext* /*context*/, + std::string* error_details, + std::unique_ptr<ProofVerifyDetails>* details, + std::unique_ptr<ProofVerifierCallback> /*callback*/) { + if (certs.empty()) { + *details = std::make_unique<Details>(Status::kInternalError); + *error_details = "No certificates provided"; + return QUIC_FAILURE; + } + + if (!HasKnownFingerprint(certs[0])) { + *details = std::make_unique<Details>(Status::kUnknownFingerprint); + *error_details = "Certificate does not match any fingerprint"; + return QUIC_FAILURE; + } + + std::unique_ptr<CertificateView> view = + CertificateView::ParseSingleCertificate(certs[0]); + if (view == nullptr) { + *details = std::make_unique<Details>(Status::kCertificateParseFailure); + *error_details = "Failed to parse the certificate"; + return QUIC_FAILURE; + } + + if (!HasValidExpiry(*view)) { + *details = std::make_unique<Details>(Status::kExpiryTooLong); + *error_details = quiche::QuicheStrCat( + "Certificate expiry exceeds the configured limit of ", + max_validity_days_, " days"); + return QUIC_FAILURE; + } + + if (!IsWithinValidityPeriod(*view)) { + *details = std::make_unique<Details>(Status::kExpired); + *error_details = + "Certificate has expired or has validity listed in the future"; + return QUIC_FAILURE; + } + + *details = std::make_unique<Details>(Status::kValidCertificate); + return QUIC_SUCCESS; +} + +std::unique_ptr<ProofVerifyContext> +WebTransportFingerprintProofVerifier::CreateDefaultContext() { + return nullptr; +} + +bool WebTransportFingerprintProofVerifier::HasKnownFingerprint( + quiche::QuicheStringPiece der_certificate) { + // https://wicg.github.io/web-transport/#verify-a-certificate-fingerprint + const std::string fingerprint = ComputeSha256Fingerprint(der_certificate); + for (const CertificateFingerprint& reference : fingerprints_) { + if (reference.algorithm != CertificateFingerprint::kSha256) { + QUIC_BUG << "Unexpected non-SHA-256 hash"; + continue; + } + if (fingerprint == reference.fingerprint) { + return true; + } + } + return false; +} + +bool WebTransportFingerprintProofVerifier::HasValidExpiry( + const CertificateView& certificate) { + if (!certificate.validity_start().IsBefore(certificate.validity_end())) { + return false; + } + + const QuicTime::Delta duration_seconds = + certificate.validity_end() - certificate.validity_start(); + return duration_seconds <= max_validity_; +} + +bool WebTransportFingerprintProofVerifier::IsWithinValidityPeriod( + const CertificateView& certificate) { + QuicWallTime now = clock_->WallNow(); + return now.IsAfter(certificate.validity_start()) && + now.IsBefore(certificate.validity_end()); +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/web_transport_fingerprint_proof_verifier.h b/chromium/net/third_party/quiche/src/quic/quic_transport/web_transport_fingerprint_proof_verifier.h new file mode 100644 index 00000000000..7e4358de60d --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/quic_transport/web_transport_fingerprint_proof_verifier.h @@ -0,0 +1,119 @@ +// Copyright 2020 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_QUIC_TRANSPORT_FINGERPRINT_PROOF_VERIFIER_H_ +#define QUICHE_QUIC_QUIC_TRANSPORT_FINGERPRINT_PROOF_VERIFIER_H_ + +#include <vector> + +#include "net/third_party/quiche/src/quic/core/crypto/certificate_view.h" +#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h" +#include "net/third_party/quiche/src/quic/core/quic_clock.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" + +namespace quic { + +// Represents a fingerprint of an X.509 certificate in a format based on +// https://w3c.github.io/webrtc-pc/#dom-rtcdtlsfingerprint. +struct QUIC_EXPORT_PRIVATE CertificateFingerprint { + static constexpr char kSha256[] = "sha-256"; + + // An algorithm described by one of the names in + // https://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xhtml + std::string algorithm; + // Hex-encoded, colon-separated fingerprint of the certificate. For example, + // "12:3d:5b:71:8c:54:df:85:7e:bd:e3:7c:66:da:f9:db:6a:94:8f:85:cb:6e:44:7f:09:3e:05:f2:dd:d4:f7:86" + std::string fingerprint; +}; + +// Computes a SHA-256 fingerprint of the specified input formatted in the same +// format as CertificateFingerprint::fingerprint would contain. +QUIC_EXPORT_PRIVATE std::string ComputeSha256Fingerprint( + quiche::QuicheStringPiece input); + +// WebTransportFingerprintProofVerifier verifies the server leaf certificate +// against a supplied list of certificate fingerprints following the procedure +// described in the WebTransport specification. The certificate is deemed +// trusted if it matches a fingerprint in the list, has expiry dates that are +// not too long and has not expired. Only the leaf is checked, the rest of the +// chain is ignored. Reference specification: +// https://wicg.github.io/web-transport/#dom-quictransportconfiguration-server_certificate_fingerprints +class QUIC_EXPORT_PRIVATE WebTransportFingerprintProofVerifier + : public ProofVerifier { + public: + // Note: the entries in this list may be logged into a UMA histogram, and thus + // should not be renumbered. + enum class Status { + kValidCertificate = 0, + kUnknownFingerprint = 1, + kCertificateParseFailure = 2, + kExpiryTooLong = 3, + kExpired = 4, + kInternalError = 5, + + kMaxValue = kInternalError, + }; + + class QUIC_EXPORT_PRIVATE Details : public ProofVerifyDetails { + public: + explicit Details(Status status) : status_(status) {} + Status status() const { return status_; } + + ProofVerifyDetails* Clone() const override; + + private: + const Status status_; + }; + + // |clock| is used to check if the certificate has expired. It is not owned + // and must outlive the object. |max_validity_days| is the maximum time for + // which the certificate is allowed to be valid. + WebTransportFingerprintProofVerifier(const QuicClock* clock, + int max_validity_days); + + // Adds a certificate fingerprint to be trusted. The fingerprints are + // case-insensitive and are validated internally; the function returns true if + // the validation passes. + bool AddFingerprint(CertificateFingerprint fingerprint); + + // ProofVerifier implementation. + QuicAsyncStatus VerifyProof( + const std::string& hostname, + const uint16_t port, + const std::string& server_config, + QuicTransportVersion transport_version, + quiche::QuicheStringPiece chlo_hash, + const std::vector<std::string>& certs, + const std::string& cert_sct, + const std::string& signature, + const ProofVerifyContext* context, + std::string* error_details, + std::unique_ptr<ProofVerifyDetails>* details, + std::unique_ptr<ProofVerifierCallback> callback) override; + QuicAsyncStatus VerifyCertChain( + const std::string& hostname, + const uint16_t port, + const std::vector<std::string>& certs, + const std::string& ocsp_response, + const std::string& cert_sct, + const ProofVerifyContext* context, + std::string* error_details, + std::unique_ptr<ProofVerifyDetails>* details, + std::unique_ptr<ProofVerifierCallback> callback) override; + std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override; + + private: + bool HasKnownFingerprint(quiche::QuicheStringPiece der_certificate); + bool HasValidExpiry(const CertificateView& certificate); + bool IsWithinValidityPeriod(const CertificateView& certificate); + + const QuicClock* clock_; // Unowned. + const int max_validity_days_; + const QuicTime::Delta max_validity_; + std::vector<CertificateFingerprint> fingerprints_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QUIC_TRANSPORT_FINGERPRINT_PROOF_VERIFIER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/web_transport_fingerprint_proof_verifier_test.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/web_transport_fingerprint_proof_verifier_test.cc new file mode 100644 index 00000000000..f9f27e5db69 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/quic_transport/web_transport_fingerprint_proof_verifier_test.cc @@ -0,0 +1,183 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/quic_transport/web_transport_fingerprint_proof_verifier.h" + +#include <memory> + +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" +#include "net/third_party/quiche/src/quic/test_tools/test_certificates.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" + +namespace quic { +namespace test { +namespace { + +using ::testing::HasSubstr; + +// 2020-02-01 12:35:56 UTC +constexpr QuicTime::Delta kValidTime = QuicTime::Delta::FromSeconds(1580560556); + +struct VerifyResult { + QuicAsyncStatus status; + WebTransportFingerprintProofVerifier::Status detailed_status; + std::string error; +}; + +class WebTransportFingerprintProofVerifierTest : public QuicTest { + public: + WebTransportFingerprintProofVerifierTest() { + clock_.AdvanceTime(kValidTime); + verifier_ = std::make_unique<WebTransportFingerprintProofVerifier>( + &clock_, /*max_validity_days=*/365); + AddTestCertificate(); + } + + protected: + VerifyResult Verify(quiche::QuicheStringPiece certificate) { + VerifyResult result; + std::unique_ptr<ProofVerifyDetails> details; + result.status = verifier_->VerifyCertChain( + /*hostname=*/"", /*port=*/0, + std::vector<std::string>{std::string(certificate)}, + /*ocsp_response=*/"", + /*cert_sct=*/"", + /*context=*/nullptr, &result.error, &details, + /*callback=*/nullptr); + result.detailed_status = + static_cast<WebTransportFingerprintProofVerifier::Details*>( + details.get()) + ->status(); + return result; + } + + void AddTestCertificate() { + EXPECT_TRUE(verifier_->AddFingerprint( + CertificateFingerprint{CertificateFingerprint::kSha256, + ComputeSha256Fingerprint(kTestCertificate)})); + } + + MockClock clock_; + std::unique_ptr<WebTransportFingerprintProofVerifier> verifier_; +}; + +TEST_F(WebTransportFingerprintProofVerifierTest, Sha256Fingerprint) { + // Computed using `openssl x509 -fingerprint -sha256`. + EXPECT_EQ(ComputeSha256Fingerprint(kTestCertificate), + "f2:e5:46:5e:2b:f7:ec:d6:f6:30:66:a5:a3:75:11:73:4a:a0:eb:7c:47:01:" + "0e:86:d6:75:8e:d4:f4:fa:1b:0f"); +} + +TEST_F(WebTransportFingerprintProofVerifierTest, SimpleFingerprint) { + VerifyResult result = Verify(kTestCertificate); + EXPECT_EQ(result.status, QUIC_SUCCESS); + EXPECT_EQ(result.detailed_status, + WebTransportFingerprintProofVerifier::Status::kValidCertificate); + + result = Verify(kWildcardCertificate); + EXPECT_EQ(result.status, QUIC_FAILURE); + EXPECT_EQ(result.detailed_status, + WebTransportFingerprintProofVerifier::Status::kUnknownFingerprint); + + result = Verify("Some random text"); + EXPECT_EQ(result.status, QUIC_FAILURE); +} + +TEST_F(WebTransportFingerprintProofVerifierTest, Validity) { + // Validity periods of kTestCertificate, according to `openssl x509 -text`: + // Not Before: Jan 30 18:13:59 2020 GMT + // Not After : Feb 2 18:13:59 2020 GMT + + // 2020-01-29 19:00:00 UTC + constexpr QuicTime::Delta kStartTime = + QuicTime::Delta::FromSeconds(1580324400); + clock_.Reset(); + clock_.AdvanceTime(kStartTime); + + VerifyResult result = Verify(kTestCertificate); + EXPECT_EQ(result.status, QUIC_FAILURE); + EXPECT_EQ(result.detailed_status, + WebTransportFingerprintProofVerifier::Status::kExpired); + + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(86400)); + result = Verify(kTestCertificate); + EXPECT_EQ(result.status, QUIC_SUCCESS); + EXPECT_EQ(result.detailed_status, + WebTransportFingerprintProofVerifier::Status::kValidCertificate); + + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(4 * 86400)); + result = Verify(kTestCertificate); + EXPECT_EQ(result.status, QUIC_FAILURE); + EXPECT_EQ(result.detailed_status, + WebTransportFingerprintProofVerifier::Status::kExpired); +} + +TEST_F(WebTransportFingerprintProofVerifierTest, MaxValidity) { + verifier_ = std::make_unique<WebTransportFingerprintProofVerifier>( + &clock_, /*max_validity_days=*/2); + AddTestCertificate(); + VerifyResult result = Verify(kTestCertificate); + EXPECT_EQ(result.status, QUIC_FAILURE); + EXPECT_EQ(result.detailed_status, + WebTransportFingerprintProofVerifier::Status::kExpiryTooLong); + EXPECT_THAT(result.error, HasSubstr("limit of 2 days")); + + // kTestCertificate is valid for exactly four days. + verifier_ = std::make_unique<WebTransportFingerprintProofVerifier>( + &clock_, /*max_validity_days=*/4); + AddTestCertificate(); + result = Verify(kTestCertificate); + EXPECT_EQ(result.status, QUIC_SUCCESS); + EXPECT_EQ(result.detailed_status, + WebTransportFingerprintProofVerifier::Status::kValidCertificate); +} + +TEST_F(WebTransportFingerprintProofVerifierTest, InvalidCertificate) { + constexpr quiche::QuicheStringPiece kInvalidCertificate = "Hello, world!"; + ASSERT_TRUE(verifier_->AddFingerprint( + {CertificateFingerprint::kSha256, + ComputeSha256Fingerprint(kInvalidCertificate)})); + + VerifyResult result = Verify(kInvalidCertificate); + EXPECT_EQ(result.status, QUIC_FAILURE); + EXPECT_EQ( + result.detailed_status, + WebTransportFingerprintProofVerifier::Status::kCertificateParseFailure); +} + +TEST_F(WebTransportFingerprintProofVerifierTest, AddCertificate) { + // Accept all-uppercase fingerprints. + verifier_ = std::make_unique<WebTransportFingerprintProofVerifier>( + &clock_, /*max_validity_days=*/365); + EXPECT_TRUE(verifier_->AddFingerprint( + {CertificateFingerprint::kSha256, + "F2:E5:46:5E:2B:F7:EC:D6:F6:30:66:A5:A3:75:11:73:4A:A0:EB:" + "7C:47:01:0E:86:D6:75:8E:D4:F4:FA:1B:0F"})); + EXPECT_EQ(Verify(kTestCertificate).detailed_status, + WebTransportFingerprintProofVerifier::Status::kValidCertificate); + + // Reject unknown hash algorithms. + EXPECT_FALSE(verifier_->AddFingerprint( + {"sha-1", + "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"})); + // Reject invalid length. + EXPECT_FALSE(verifier_->AddFingerprint( + {CertificateFingerprint::kSha256, "00:00:00:00"})); + // Reject missing colons. + EXPECT_FALSE(verifier_->AddFingerprint( + {CertificateFingerprint::kSha256, + "00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00." + "00.00.00.00.00.00.00.00.00.00.00.00.00"})); + // Reject non-hex symbols. + EXPECT_FALSE(verifier_->AddFingerprint( + {CertificateFingerprint::kSha256, + "zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:" + "zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz:zz"})); +} + +} // namespace +} // namespace test +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.cc b/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.cc index 486444183c9..eacbca5287c 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.cc @@ -242,7 +242,12 @@ int HandshakeWithFakeServer(QuicConfig* server_quic_config, TestQuicSpdyServerSession server_session( server_conn, *server_quic_config, client_conn->supported_versions(), crypto_config, &compressed_certs_cache); + // Call SetServerApplicationStateForResumption so that the fake server + // supports 0-RTT in TLS. server_session.Initialize(); + server_session.GetMutableCryptoStream() + ->SetServerApplicationStateForResumption( + std::make_unique<ApplicationState>()); EXPECT_CALL(*server_session.helper(), CanAcceptClientHello(testing::_, testing::_, testing::_, testing::_, testing::_)) @@ -404,9 +409,6 @@ std::pair<size_t, size_t> AdvanceHandshake(PacketSavingConnection* client_conn, QUIC_LOG(INFO) << "Processing " << server_conn->encrypted_packets_.size() - server_i << " packets server->client"; - if (server_conn->encrypted_packets_.size() - server_i == 2) { - QUIC_LOG(INFO) << "here"; - } MovePackets(server_conn, &server_i, client, client_conn, Perspective::IS_CLIENT); @@ -574,7 +576,7 @@ void CompareCrypters(const QuicEncrypter* encrypter, std::string label) { if (encrypter == nullptr || decrypter == nullptr) { ADD_FAILURE() << "Expected non-null crypters; have " << encrypter << " and " - << decrypter; + << decrypter << " for " << label; return; } quiche::QuicheStringPiece encrypter_key = encrypter->GetKey(); @@ -605,7 +607,8 @@ void CompareClientAndServerKeys(QuicCryptoClientStream* client, const QuicDecrypter* server_decrypter( QuicFramerPeer::GetDecrypter(server_framer, level)); if (level == ENCRYPTION_FORWARD_SECURE || - !((level == ENCRYPTION_HANDSHAKE || client_encrypter == nullptr) && + !((level == ENCRYPTION_HANDSHAKE || level == ENCRYPTION_ZERO_RTT || + client_encrypter == nullptr) && server_decrypter == nullptr)) { CompareCrypters(client_encrypter, server_decrypter, "client " + EncryptionLevelString(level) + " write"); @@ -616,7 +619,8 @@ void CompareClientAndServerKeys(QuicCryptoClientStream* client, QuicFramerPeer::GetDecrypter(client_framer, level)); if (level == ENCRYPTION_FORWARD_SECURE || !(server_encrypter == nullptr && - (level == ENCRYPTION_HANDSHAKE || client_decrypter == nullptr))) { + (level == ENCRYPTION_HANDSHAKE || level == ENCRYPTION_ZERO_RTT || + client_decrypter == nullptr))) { CompareCrypters(server_encrypter, client_decrypter, "server " + EncryptionLevelString(level) + " write"); } @@ -748,10 +752,11 @@ void MovePackets(PacketSavingConnection* source_conn, QuicConnectionPeer::AddBytesReceived( dest_conn, source_conn->encrypted_packets_[index]->length()); if (!framer.ProcessPacket(*source_conn->encrypted_packets_[index])) { - // The framer will be unable to decrypt forward-secure packets sent after - // the handshake is complete. Don't treat them as handshake packets. + // The framer will be unable to decrypt zero-rtt packets sent during + // handshake or forward-secure packets sent after the handshake is + // complete. Don't treat them as handshake packets. QuicConnectionPeer::SwapCrypters(dest_conn, framer.framer()); - break; + continue; } QuicConnectionPeer::SwapCrypters(dest_conn, framer.framer()); dest_conn->OnDecryptedPacket(framer.last_decrypted_level()); diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/first_flight.h b/chromium/net/third_party/quiche/src/quic/test_tools/first_flight.h index b2a4ebd36be..ad5f792e0b9 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/first_flight.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/first_flight.h @@ -49,10 +49,10 @@ class QUIC_NO_EXPORT DelegatedPacketWriter : public QuicPacketWriter { } bool SupportsReleaseTime() const override { return false; } bool IsBatchMode() const override { return false; } - char* GetNextWriteLocation( + QuicPacketBuffer GetNextWriteLocation( const QuicIpAddress& /*self_address*/, const QuicSocketAddress& /*peer_address*/) override { - return nullptr; + return {nullptr, nullptr}; } WriteResult Flush() override { return WriteResult(WRITE_STATUS_OK, 0); } diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_clock.cc b/chromium/net/third_party/quiche/src/quic/test_tools/mock_clock.cc index 1761dd9af32..21c080a3c4c 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/mock_clock.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_clock.cc @@ -14,6 +14,10 @@ void MockClock::AdvanceTime(QuicTime::Delta delta) { now_ = now_ + delta; } +void MockClock::Reset() { + now_ = QuicTime::Zero(); +} + QuicTime MockClock::Now() const { return now_; } diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_clock.h b/chromium/net/third_party/quiche/src/quic/test_tools/mock_clock.h index 4bd51e9d6d6..2ce2e966a9c 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/mock_clock.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_clock.h @@ -24,6 +24,8 @@ class MockClock : public QuicClock { // Advances the current time by |delta|, which may be negative. void AdvanceTime(QuicTime::Delta delta); + // Resets time back to zero. + void Reset(); private: QuicTime now_; diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h b/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h index 75b9a5dcb30..f066e9191e4 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h @@ -54,12 +54,12 @@ class PacketDroppingTestWriter : public QuicPacketWriterWrapper { void SetWritable() override; - char* GetNextWriteLocation( + QuicPacketBuffer GetNextWriteLocation( const QuicIpAddress& /*self_address*/, const QuicSocketAddress& /*peer_address*/) override { // If the wrapped writer supports zero-copy, disable it, because it is not // compatible with delayed writes in this class. - return nullptr; + return {nullptr, nullptr}; } // Writes out any packet which should have been sent by now diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.cc index 3e752154f27..52325d16650 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.cc @@ -94,8 +94,8 @@ void QuicConfigPeer::SetReceivedStatelessResetToken(QuicConfig* config, // static void QuicConfigPeer::SetReceivedMaxPacketSize(QuicConfig* config, - uint32_t max_packet_size) { - config->max_packet_size_.SetReceivedValue(max_packet_size); + uint32_t max_udp_payload_size) { + config->max_udp_payload_size_.SetReceivedValue(max_udp_payload_size); } // static @@ -106,8 +106,23 @@ void QuicConfigPeer::SetNegotiated(QuicConfig* config, bool negotiated) { // static void QuicConfigPeer::SetReceivedOriginalConnectionId( QuicConfig* config, - const QuicConnectionId& original_connection_id) { - config->received_original_connection_id_ = original_connection_id; + const QuicConnectionId& original_destination_connection_id) { + config->received_original_destination_connection_id_ = + original_destination_connection_id; +} + +// static +void QuicConfigPeer::SetReceivedInitialSourceConnectionId( + QuicConfig* config, + const QuicConnectionId& initial_source_connection_id) { + config->received_initial_source_connection_id_ = initial_source_connection_id; +} + +// static +void QuicConfigPeer::SetReceivedRetrySourceConnectionId( + QuicConfig* config, + const QuicConnectionId& retry_source_connection_id) { + config->received_retry_source_connection_id_ = retry_source_connection_id; } // static @@ -117,5 +132,10 @@ void QuicConfigPeer::SetReceivedMaxDatagramFrameSize( config->max_datagram_frame_size_.SetReceivedValue(max_datagram_frame_size); } +// static +void QuicConfigPeer::DisableSupportHandshakeDone(QuicConfig* config) { + config->support_handshake_done_.SetSendValue(0); +} + } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.h index c435f2282da..109bd6453b3 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.h @@ -58,16 +58,25 @@ class QuicConfigPeer { QuicUint128 token); static void SetReceivedMaxPacketSize(QuicConfig* config, - uint32_t max_packet_size); + uint32_t max_udp_payload_size); static void SetNegotiated(QuicConfig* config, bool negotiated); static void SetReceivedOriginalConnectionId( QuicConfig* config, - const QuicConnectionId& original_connection_id); + const QuicConnectionId& original_destination_connection_id); + + static void SetReceivedInitialSourceConnectionId( + QuicConfig* config, + const QuicConnectionId& initial_source_connection_id); + + static void SetReceivedRetrySourceConnectionId( + QuicConfig* config, + const QuicConnectionId& retry_source_connection_id); static void SetReceivedMaxDatagramFrameSize(QuicConfig* config, uint64_t max_datagram_frame_size); + static void DisableSupportHandshakeDone(QuicConfig* config); }; } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.cc index d276f6327ea..54616b2e407 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.cc @@ -377,5 +377,10 @@ void QuicConnectionPeer::SetServerConnectionId( connection->InstallInitialCrypters(server_connection_id); } +// static +size_t QuicConnectionPeer::NumUndecryptablePackets(QuicConnection* connection) { + return connection->undecryptable_packets_.size(); +} + } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h index 9882f62ee35..a5b94ef2c53 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h @@ -147,6 +147,8 @@ class QuicConnectionPeer { static void SetServerConnectionId( QuicConnection* connection, const QuicConnectionId& server_connection_id); + + static size_t NumUndecryptablePackets(QuicConnection* connection); }; } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_mock_syscall_wrapper.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_mock_syscall_wrapper.cc new file mode 100644 index 00000000000..3cce97eed80 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_mock_syscall_wrapper.cc @@ -0,0 +1,22 @@ +// 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 "net/third_party/quiche/src/quic/test_tools/quic_mock_syscall_wrapper.h" + +using testing::_; +using testing::Invoke; + +namespace quic { +namespace test { + +MockQuicSyscallWrapper::MockQuicSyscallWrapper(QuicSyscallWrapper* delegate) { + ON_CALL(*this, Sendmsg(_, _, _)) + .WillByDefault(Invoke(delegate, &QuicSyscallWrapper::Sendmsg)); + + ON_CALL(*this, Sendmmsg(_, _, _, _)) + .WillByDefault(Invoke(delegate, &QuicSyscallWrapper::Sendmmsg)); +} + +} // namespace test +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_mock_syscall_wrapper.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_mock_syscall_wrapper.h new file mode 100644 index 00000000000..8dac5ad00c9 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_mock_syscall_wrapper.h @@ -0,0 +1,37 @@ +// 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_PLATFORM_IMPL_QUIC_MOCK_SYSCALL_WRAPPER_H_ +#define QUICHE_QUIC_PLATFORM_IMPL_QUIC_MOCK_SYSCALL_WRAPPER_H_ + +#include "net/third_party/quiche/src/quic/core/quic_syscall_wrapper.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { + +class MockQuicSyscallWrapper : public QuicSyscallWrapper { + public: + // Create a standard mock object. + MockQuicSyscallWrapper() = default; + + // Create a 'mockable' object that delegates everything to |delegate| by + // default. + explicit MockQuicSyscallWrapper(QuicSyscallWrapper* delegate); + + MOCK_METHOD(ssize_t, + Sendmsg, + (int sockfd, const msghdr*, int flags), + (override)); + + MOCK_METHOD(int, + Sendmmsg, + (int sockfd, mmsghdr*, unsigned int vlen, int flags), + (override)); +}; + +} // namespace test +} // namespace quic + +#endif // QUICHE_QUIC_PLATFORM_IMPL_QUIC_MOCK_SYSCALL_WRAPPER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.cc index 44fed8809ec..151575a0db0 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.cc @@ -111,7 +111,7 @@ SerializedPacket QuicPacketCreatorPeer::SerializeAllFrames( bool success = creator->AddFrame(frame, NOT_RETRANSMISSION); DCHECK(success); } - creator->SerializePacket(buffer, buffer_len); + creator->SerializePacket(QuicOwnedPacketBuffer(buffer, nullptr), buffer_len); SerializedPacket packet = std::move(creator->packet_); // The caller takes ownership of the QuicEncryptedPacket. creator->packet_.encrypted_buffer = nullptr; diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc index ab28828cc14..ea9dc062fd1 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc @@ -242,12 +242,17 @@ size_t QuicSessionPeer::GetNumOpenDynamicStreams(QuicSession* session) { } } // Exclude draining streams. - result -= session->GetNumDrainingStreams(); + result -= session->num_draining_streams_; // Add locally closed streams. result += session->locally_closed_streams_highest_offset_.size(); return result; } +// static +size_t QuicSessionPeer::GetNumDrainingStreams(QuicSession* session) { + return session->num_draining_streams_; +} + } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.h index ffb6a46446d..061f16da3a7 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.h @@ -82,6 +82,7 @@ class QuicSessionPeer { static void set_is_configured(QuicSession* session, bool value); static void SetPerspective(QuicSession* session, Perspective perspective); static size_t GetNumOpenDynamicStreams(QuicSession* session); + static size_t GetNumDrainingStreams(QuicSession* session); }; } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc index 22e65b822ee..503f58539b2 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc @@ -210,6 +210,22 @@ MockableQuicClient::MockableQuicClient( const ParsedQuicVersionVector& supported_versions, QuicEpollServer* epoll_server, std::unique_ptr<ProofVerifier> proof_verifier) + : MockableQuicClient(server_address, + server_id, + config, + supported_versions, + epoll_server, + std::move(proof_verifier), + nullptr) {} + +MockableQuicClient::MockableQuicClient( + QuicSocketAddress server_address, + const QuicServerId& server_id, + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QuicEpollServer* epoll_server, + std::unique_ptr<ProofVerifier> proof_verifier, + std::unique_ptr<SessionCache> session_cache) : QuicClient( server_address, server_id, @@ -218,8 +234,8 @@ MockableQuicClient::MockableQuicClient( epoll_server, std::make_unique<MockableQuicClientEpollNetworkHelper>(epoll_server, this), - QuicWrapUnique( - new RecordingProofVerifier(std::move(proof_verifier)))), + QuicWrapUnique(new RecordingProofVerifier(std::move(proof_verifier))), + std::move(session_cache)), override_server_connection_id_(EmptyQuicConnectionId()), server_connection_id_overridden_(false), override_client_connection_id_(EmptyQuicConnectionId()), @@ -342,6 +358,24 @@ QuicTestClient::QuicTestClient( Initialize(); } +QuicTestClient::QuicTestClient( + QuicSocketAddress server_address, + const std::string& server_hostname, + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + std::unique_ptr<ProofVerifier> proof_verifier, + std::unique_ptr<SessionCache> session_cache) + : client_(new MockableQuicClient( + server_address, + QuicServerId(server_hostname, server_address.port(), false), + config, + supported_versions, + &epoll_server_, + std::move(proof_verifier), + std::move(session_cache))) { + Initialize(); +} + QuicTestClient::QuicTestClient() = default; QuicTestClient::~QuicTestClient() { @@ -390,13 +424,8 @@ ssize_t QuicTestClient::SendRequestAndRstTogether(const std::string& uri) { QuicStreamId stream_id = GetNthClientInitiatedBidirectionalStreamId( session->transport_version(), 0); QuicStream* stream = session->GetOrCreateStream(stream_id); - if (session->break_close_loop()) { - session->ResetStream(stream_id, QUIC_STREAM_CANCELLED, - stream->stream_bytes_written()); - } else { - session->SendRstStream(stream_id, QUIC_STREAM_CANCELLED, - stream->stream_bytes_written()); - } + session->ResetStream(stream_id, QUIC_STREAM_CANCELLED, + stream->stream_bytes_written()); return ret; } @@ -962,7 +991,7 @@ void QuicTestClient::WaitForDelayedAcks() { const QuicClock* clock = client()->client_session()->connection()->clock(); QuicTime wait_until = clock->ApproximateNow() + kWaitDuration; - while (clock->ApproximateNow() < wait_until) { + while (connected() && clock->ApproximateNow() < wait_until) { // This waits for up to 50 ms. client()->WaitForEvents(); } diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.h index f74974f4231..41e30b13dbb 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.h @@ -49,6 +49,14 @@ class MockableQuicClient : public QuicClient { const ParsedQuicVersionVector& supported_versions, QuicEpollServer* epoll_server, std::unique_ptr<ProofVerifier> proof_verifier); + + MockableQuicClient(QuicSocketAddress server_address, + const QuicServerId& server_id, + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QuicEpollServer* epoll_server, + std::unique_ptr<ProofVerifier> proof_verifier, + std::unique_ptr<SessionCache> session_cache); MockableQuicClient(const MockableQuicClient&) = delete; MockableQuicClient& operator=(const MockableQuicClient&) = delete; @@ -100,6 +108,12 @@ class QuicTestClient : public QuicSpdyStream::Visitor, const QuicConfig& config, const ParsedQuicVersionVector& supported_versions, std::unique_ptr<ProofVerifier> proof_verifier); + QuicTestClient(QuicSocketAddress server_address, + const std::string& server_hostname, + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + std::unique_ptr<ProofVerifier> proof_verifier, + std::unique_ptr<SessionCache> session_cache); ~QuicTestClient() override; @@ -188,9 +202,10 @@ class QuicTestClient : public QuicSpdyStream::Visitor, void WaitForInitialResponse() { WaitForInitialResponseForMs(-1); } // Returns once at least one complete response or a connection close has been - // received from the server, or once the timeout expires. -1 means no timeout. - // If responses are received for multiple (say 2) streams, next - // WaitForResponseForMs will return immediately. + // received from the server, or once the timeout expires. + // Passing in a timeout value of -1 disables the timeout. If multiple + // responses are received while the client is waiting, subsequent calls to + // this function will return immediately. void WaitForResponseForMs(int timeout_ms) { WaitUntil(timeout_ms, [this]() { return !closed_stream_states_.empty(); }); if (response_complete()) { diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.cc index 7d88ccdaae7..41fb995e5e3 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.cc @@ -419,6 +419,11 @@ bool NoOpFramerVisitor::OnHandshakeDoneFrame( return true; } +bool NoOpFramerVisitor::OnAckFrequencyFrame( + const QuicAckFrequencyFrame& /*frame*/) { + return true; +} + bool NoOpFramerVisitor::IsValidStatelessResetToken( QuicUint128 /*token*/) const { return false; @@ -566,6 +571,7 @@ void PacketSavingConnection::SendOrQueuePacket(SerializedPacket packet) { clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); // Transfer ownership of the packet to the SentPacketManager and the // ack notifier to the AckNotifierManager. + OnPacketSent(packet.encryption_level, packet.transmission_type); QuicConnectionPeer::GetSentPacketManager(this)->OnPacketSent( &packet, clock_.ApproximateNow(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); @@ -755,8 +761,8 @@ TestQuicSpdyClientSession::TestQuicSpdyClientSession( &push_promise_index_, config, supported_versions) { - // TODO(b/153726130): Consider adding OnApplicationState calls in tests and - // set |has_application_state| to true. + // TODO(b/153726130): Consider adding SetServerApplicationStateForResumption + // calls in tests and set |has_application_state| to true. crypto_stream_ = std::make_unique<QuicCryptoClientStream>( server_id, this, crypto_test_utils::ProofVerifyContextForTesting(), crypto_config, this, /*has_application_state = */ false); @@ -799,7 +805,7 @@ MockPacketWriter::MockPacketWriter() { .WillByDefault(testing::Return(kMaxOutgoingPacketSize)); ON_CALL(*this, IsBatchMode()).WillByDefault(testing::Return(false)); ON_CALL(*this, GetNextWriteLocation(_, _)) - .WillByDefault(testing::Return(nullptr)); + .WillByDefault(testing::Return(QuicPacketBuffer())); ON_CALL(*this, Flush()) .WillByDefault(testing::Return(WriteResult(WRITE_STATUS_OK, 0))); ON_CALL(*this, SupportsReleaseTime()).WillByDefault(testing::Return(false)); @@ -841,9 +847,9 @@ ParsedQuicVersion QuicVersionMin() { } void DisableQuicVersionsWithTls() { - SetQuicReloadableFlag(quic_enable_version_draft_27, false); - SetQuicReloadableFlag(quic_enable_version_draft_25_v3, false); - SetQuicReloadableFlag(quic_enable_version_t050_v2, false); + for (const ParsedQuicVersion& version : AllSupportedVersionsWithTls()) { + QuicDisableVersion(version); + } } QuicEncryptedPacket* ConstructEncryptedPacket( @@ -1255,12 +1261,12 @@ QuicStreamId GetNthClientInitiatedUnidirectionalStreamId( } StreamType DetermineStreamType(QuicStreamId id, - QuicTransportVersion version, + ParsedQuicVersion version, Perspective perspective, bool is_incoming, StreamType default_type) { - return VersionHasIetfQuicFrames(version) - ? QuicUtils::GetStreamType(id, perspective, is_incoming) + return version.HasIetfQuicFrames() + ? QuicUtils::GetStreamType(id, perspective, is_incoming, version) : default_type; } diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.h index 7d1d9db4459..2af8aeb3224 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.h @@ -15,6 +15,7 @@ #include "net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h" #include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h" +#include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h" #include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h" #include "net/third_party/quiche/src/quic/core/http/quic_server_session_base.h" #include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h" @@ -395,6 +396,10 @@ class MockFramerVisitor : public QuicFramerVisitorInterface { OnHandshakeDoneFrame, (const QuicHandshakeDoneFrame& frame), (override)); + MOCK_METHOD(bool, + OnAckFrequencyFrame, + (const QuicAckFrequencyFrame& frame), + (override)); MOCK_METHOD(void, OnPacketComplete, (), (override)); MOCK_METHOD(bool, IsValidStatelessResetToken, @@ -459,6 +464,7 @@ class NoOpFramerVisitor : public QuicFramerVisitorInterface { bool OnBlockedFrame(const QuicBlockedFrame& frame) override; bool OnMessageFrame(const QuicMessageFrame& frame) override; bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) override; + bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) override; void OnPacketComplete() override {} bool IsValidStatelessResetToken(QuicUint128 token) const override; void OnAuthenticatedIetfStatelessResetPacket( @@ -498,14 +504,18 @@ class MockQuicConnectionVisitor : public QuicConnectionVisitorInterface { MOCK_METHOD(void, OnWriteBlocked, (), (override)); MOCK_METHOD(void, OnCanWrite, (), (override)); MOCK_METHOD(bool, SendProbingData, (), (override)); + MOCK_METHOD(bool, + ValidateStatelessReset, + (const quic::QuicSocketAddress&, const quic::QuicSocketAddress&), + (override)); MOCK_METHOD(void, OnCongestionWindowChange, (QuicTime now), (override)); MOCK_METHOD(void, OnConnectionMigration, (AddressChangeType type), (override)); MOCK_METHOD(void, OnPathDegrading, (), (override)); + MOCK_METHOD(void, OnForwardProgressMadeAfterPathDegrading, (), (override)); MOCK_METHOD(bool, WillingAndAbleToWrite, (), (const, override)); - MOCK_METHOD(bool, HasPendingHandshake, (), (const, override)); MOCK_METHOD(bool, ShouldKeepConnectionAlive, (), (const, override)); MOCK_METHOD(void, OnSuccessfulVersionNegotiation, @@ -521,7 +531,6 @@ class MockQuicConnectionVisitor : public QuicConnectionVisitorInterface { MOCK_METHOD(void, SendPing, (), (override)); MOCK_METHOD(bool, AllowSelfAddressChange, (), (const, override)); MOCK_METHOD(HandshakeState, GetHandshakeState, (), (const, override)); - MOCK_METHOD(void, OnForwardProgressConfirmed, (), (override)); MOCK_METHOD(bool, OnMaxStreamsFrame, (const QuicMaxStreamsFrame& frame), @@ -672,6 +681,8 @@ class MockQuicConnection : public QuicConnection { QuicConnection::OnError(framer); } + void ReallyOnCanWrite() { QuicConnection::OnCanWrite(); } + void ReallyCloseConnection( QuicErrorCode error, const std::string& details, @@ -738,6 +749,8 @@ class PacketSavingConnection : public MockQuicConnection { void SendOrQueuePacket(SerializedPacket packet) override; + MOCK_METHOD(void, OnPacketSent, (EncryptionLevel, TransmissionType)); + std::vector<std::unique_ptr<QuicEncryptedPacket>> encrypted_packets_; MockClock clock_; }; @@ -797,7 +810,6 @@ class MockQuicSession : public QuicSession { MOCK_METHOD(void, OnAlpnSelected, (quiche::QuicheStringPiece), (override)); using QuicSession::ActivateStream; - using QuicSession::GetNumDrainingStreams; // Returns a QuicConsumedData that indicates all of |write_length| (and |fin| // if set) has been consumed. @@ -834,6 +846,8 @@ class MockQuicCryptoStream : public QuicCryptoStream { void OnHandshakePacketSent() override {} void OnHandshakeDoneReceived() override {} HandshakeState GetHandshakeState() const override { return HANDSHAKE_START; } + void SetServerApplicationStateForResumption( + std::unique_ptr<ApplicationState> /*application_state*/) override {} private: QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_; @@ -1182,7 +1196,7 @@ class MockPacketWriter : public QuicPacketWriter { (const, override)); MOCK_METHOD(bool, SupportsReleaseTime, (), (const, override)); MOCK_METHOD(bool, IsBatchMode, (), (const, override)); - MOCK_METHOD(char*, + MOCK_METHOD(QuicPacketBuffer, GetNextWriteLocation, (const QuicIpAddress& self_address, const QuicSocketAddress& peer_address), @@ -1285,6 +1299,7 @@ class MockLossAlgorithm : public LossDetectionInterface { MOCK_METHOD(void, OnConfigNegotiated, (), (override)); MOCK_METHOD(void, OnMinRttAvailable, (), (override)); + MOCK_METHOD(void, OnUserAgentIdKnown, (), (override)); MOCK_METHOD(void, OnConnectionClosed, (), (override)); }; @@ -1415,6 +1430,16 @@ class MockQuicConnectionDebugVisitor : public QuicConnectionDebugVisitor { OnVersionNegotiationPacket, (const QuicVersionNegotiationPacket&), (override)); + + MOCK_METHOD(void, + OnTransportParametersSent, + (const TransportParameters&), + (override)); + + MOCK_METHOD(void, + OnTransportParametersReceived, + (const TransportParameters&), + (override)); }; class MockReceivedPacketManager : public QuicReceivedPacketManager { @@ -1443,7 +1468,7 @@ class MockPacketCreatorDelegate : public QuicPacketCreator::DelegateInterface { delete; ~MockPacketCreatorDelegate() override; - MOCK_METHOD(char*, GetPacketBuffer, (), (override)); + MOCK_METHOD(QuicPacketBuffer, GetPacketBuffer, (), (override)); MOCK_METHOD(void, OnSerializedPacket, (SerializedPacket), (override)); MOCK_METHOD(void, OnUnrecoverableError, @@ -1600,7 +1625,7 @@ QuicStreamId GetNthClientInitiatedUnidirectionalStreamId( int n); StreamType DetermineStreamType(QuicStreamId id, - QuicTransportVersion version, + ParsedQuicVersion version, Perspective perspective, bool is_incoming, StreamType default_type); diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.cc index 3d2652fa7ed..8ec56dc9ed8 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.cc @@ -204,6 +204,11 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface { return true; } + bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) override { + ack_frequency_frames_.push_back(frame); + return true; + } + void OnPacketComplete() override {} bool IsValidStatelessResetToken(QuicUint128 /*token*/) const override { @@ -295,6 +300,7 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface { std::vector<QuicNewTokenFrame> new_token_frames_; std::vector<QuicMessageFrame> message_frames_; std::vector<QuicHandshakeDoneFrame> handshake_done_frames_; + std::vector<QuicAckFrequencyFrame> ack_frequency_frames_; std::vector<std::unique_ptr<std::string>> stream_data_; std::vector<std::unique_ptr<std::string>> crypto_data_; EncryptionLevel last_decrypted_level_; diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_cache.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_cache.cc index 6a0cafb4177..94b8764a7bf 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_cache.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_cache.cc @@ -34,6 +34,12 @@ std::unique_ptr<QuicResumptionState> SimpleSessionCache::Lookup( if (it == cache_entries_.end()) { return nullptr; } + + if (!it->second.session) { + cache_entries_.erase(it); + return nullptr; + } + auto state = std::make_unique<QuicResumptionState>(); state->tls_session = std::move(it->second.session); state->application_state = it->second.application_state.get(); @@ -41,5 +47,10 @@ std::unique_ptr<QuicResumptionState> SimpleSessionCache::Lookup( return state; } +void SimpleSessionCache::ClearEarlyData(const QuicServerId& /*server_id*/) { + // The simple session cache only stores 1 SSL ticket per entry, so no need to + // do anything here. +} + } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_cache.h b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_cache.h index cfe3f4a5454..6043a439e4d 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_cache.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_cache.h @@ -28,6 +28,7 @@ class SimpleSessionCache : public SessionCache { const ApplicationState* application_state) override; std::unique_ptr<QuicResumptionState> Lookup(const QuicServerId& server_id, const SSL_CTX* ctx) override; + void ClearEarlyData(const QuicServerId& server_id) override; private: struct Entry { diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h index 1237f424499..65afacebf3c 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h @@ -78,6 +78,7 @@ class SimpleSessionNotifier : public SessionNotifierInterface { bool IsFrameOutstanding(const QuicFrame& frame) const override; bool HasUnackedCryptoData() const override; bool HasUnackedStreamData() const override; + bool HasLostStreamData() const; private: struct StreamState { @@ -124,8 +125,6 @@ class SimpleSessionNotifier : public SessionNotifierInterface { bool HasBufferedControlFrames() const; - bool HasLostStreamData() const; - bool StreamHasBufferedData(QuicStreamId id) const; QuicCircularDeque<QuicFrame> control_frames_; diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.cc index 3816fd4b0c0..52f7fe683b7 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.cc @@ -20,6 +20,7 @@ Queue::Queue(Simulator* simulator, std::string name, QuicByteCount capacity) aggregation_timeout_(QuicTime::Delta::Infinite()), current_bundle_(0), current_bundle_bytes_(0), + tx_port_(nullptr), listener_(nullptr) { aggregation_timeout_alarm_.reset(simulator_->GetAlarmFactory()->CreateAlarm( new AggregationAlarmDelegate(this))); @@ -116,7 +117,12 @@ void Queue::ScheduleNextPacketDequeue() { return; } - Schedule(clock_->Now() + tx_port_->TimeUntilAvailable()); + QuicTime::Delta time_until_available = QuicTime::Delta::Zero(); + if (tx_port_) { + time_until_available = tx_port_->TimeUntilAvailable(); + } + + Schedule(clock_->Now() + time_until_available); } } // namespace simulator diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.cc index c908f982abd..aba7b46a649 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.cc @@ -11,6 +11,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" #include "net/third_party/quiche/src/quic/core/quic_data_writer.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test_output.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" #include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h" @@ -55,6 +56,7 @@ QuicEndpoint::QuicEndpoint(Simulator* simulator, // Skip version negotiation. test::QuicConnectionPeer::SetNegotiatedVersion(connection_.get()); } + test::QuicConnectionPeer::SetAddressValidated(connection_.get()); connection_->SetDataProducer(&producer_); connection_->SetSessionNotifier(this); notifier_ = std::make_unique<test::SimpleSessionNotifier>(connection_.get()); @@ -74,6 +76,17 @@ QuicEndpoint::QuicEndpoint(Simulator* simulator, peer_hello, perspective == Perspective::IS_CLIENT ? SERVER : CLIENT, &error); DCHECK_EQ(error_code, QUIC_NO_ERROR) << "Configuration failed: " << error; + if (connection_->version().AuthenticatesHandshakeConnectionIds()) { + if (connection_->perspective() == Perspective::IS_CLIENT) { + test::QuicConfigPeer::SetReceivedOriginalConnectionId( + &config, connection_->connection_id()); + test::QuicConfigPeer::SetReceivedInitialSourceConnectionId( + &config, connection_->connection_id()); + } else { + test::QuicConfigPeer::SetReceivedInitialSourceConnectionId( + &config, connection_->client_connection_id()); + } + } connection_->SetFromConfig(config); } @@ -153,9 +166,6 @@ bool QuicEndpoint::WillingAndAbleToWrite() const { } return bytes_to_transfer_ != 0; } -bool QuicEndpoint::HasPendingHandshake() const { - return false; -} bool QuicEndpoint::ShouldKeepConnectionAlive() const { return true; } diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h index 8b9fd1f480d..cb3c38644f7 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h @@ -51,8 +51,12 @@ class QuicEndpoint : public QuicEndpointBase, void OnCryptoFrame(const QuicCryptoFrame& frame) override; void OnCanWrite() override; bool SendProbingData() override; + bool ValidateStatelessReset( + const quic::QuicSocketAddress& /*self_address*/, + const quic::QuicSocketAddress& /*peer_address*/) override { + return true; + } bool WillingAndAbleToWrite() const override; - bool HasPendingHandshake() const override; bool ShouldKeepConnectionAlive() const override; void OnWindowUpdateFrame(const QuicWindowUpdateFrame& /*frame*/) override {} @@ -72,11 +76,11 @@ class QuicEndpoint : public QuicEndpointBase, void OnCongestionWindowChange(QuicTime /*now*/) override {} void OnConnectionMigration(AddressChangeType /*type*/) override {} void OnPathDegrading() override {} + void OnForwardProgressMadeAfterPathDegrading() override {} void OnAckNeedsRetransmittableFrame() override {} void SendPing() override {} bool AllowSelfAddressChange() const override; HandshakeState GetHandshakeState() const override; - void OnForwardProgressConfirmed() override {} bool OnMaxStreamsFrame(const QuicMaxStreamsFrame& /*frame*/) override { return true; } diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_base.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_base.cc index 21815d8fe31..c05740d6f00 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_base.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_base.cc @@ -178,10 +178,10 @@ bool QuicEndpointBase::Writer::IsBatchMode() const { return false; } -char* QuicEndpointBase::Writer::GetNextWriteLocation( +QuicPacketBuffer QuicEndpointBase::Writer::GetNextWriteLocation( const QuicIpAddress& /*self_address*/, const QuicSocketAddress& /*peer_address*/) { - return nullptr; + return {nullptr, nullptr}; } WriteResult QuicEndpointBase::Writer::Flush() { diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_base.h b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_base.h index f4fe33be429..c9be24eb089 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_base.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_base.h @@ -87,8 +87,9 @@ class QuicEndpointBase : public Endpoint, const QuicSocketAddress& peer_address) const override; bool SupportsReleaseTime() const override; bool IsBatchMode() const override; - char* GetNextWriteLocation(const QuicIpAddress& self_address, - const QuicSocketAddress& peer_address) override; + QuicPacketBuffer GetNextWriteLocation( + const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address) override; WriteResult Flush() override; private: diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/test_certificates.cc b/chromium/net/third_party/quiche/src/quic/test_tools/test_certificates.cc index 2a733b43ae9..d179d006329 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/test_certificates.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/test_certificates.cc @@ -249,6 +249,34 @@ hTXMooR/wD7an6gtnXD8ixCh7bP0TyPiBhNsUb12WrvSEAm/UyciQbQlR7P+K0Z7 Cmn1Mj4hQ+pT0t+pw/DMOw== -----END CERTIFICATE-----)"; +QUIC_CONST_INIT const char kTestCertWithUnknownSanTypePem[] = + R"(-----BEGIN CERTIFICATE----- +MIIEYTCCA0mgAwIBAgIJAILStmLgUUcVMA0GCSqGSIb3DQEBCwUAMHYxCzAJBgNV +BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNp +c2NvMQ0wCwYDVQQKDARMeWZ0MRkwFwYDVQQLDBBMeWZ0IEVuZ2luZWVyaW5nMRAw +DgYDVQQDDAdUZXN0IENBMB4XDTE4MTIxNzIwMTgwMFoXDTIwMTIxNjIwMTgwMFow +gaYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1T +YW4gRnJhbmNpc2NvMQ0wCwYDVQQKDARMeWZ0MRkwFwYDVQQLDBBMeWZ0IEVuZ2lu +ZWVyaW5nMRowGAYDVQQDDBFUZXN0IEJhY2tlbmQgVGVhbTEkMCIGCSqGSIb3DQEJ +ARYVYmFja2VuZC10ZWFtQGx5ZnQuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAuvPdQdmwZongPAgQho/Vipd3PZWrQ6BKxIb4l/RvqtVP321IUTLs +4vVwpXoYJ+12L+XOO3jCInszs53tHjFpTI1GE8/sasmgR6LRr2krwSoVRHPqUoc9 +tzkDG1SzKP2TRTi1MTI3FO+TnLFahntO9Zstxhv1Epz5GZ/xQLE0/LLoRYzcynL/ +iflk18iL1KM8i0Hy4cKjclOaUdnh2nh753iJfxCSb5wJfx4FH1qverYHHT6FopYR +V40Cg0yYXcYo8yNwrg+EBY8QAT2JOMDokXNKbZpmVKiBlh0QYMX6BBiW249v3sYl +3Ve+fZvCkle3W0xP0xJw8PdX0NRbvGOrBQIDAQABo4HAMIG9MAwGA1UdEwEB/wQC +MAAwCwYDVR0PBAQDAgXgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATBB +BgNVHREEOjA4hh5zcGlmZmU6Ly9seWZ0LmNvbS9iYWNrZW5kLXRlYW2CCGx5ZnQu +Y29tggx3d3cubHlmdC5jb20wHQYDVR0OBBYEFLHmMm0DV9jCHJSWVRwyPYpBw62r +MB8GA1UdIwQYMBaAFBQz1vaSbPuePL++7GTMqLAMtk3kMA0GCSqGSIb3DQEBCwUA +A4IBAQAwx3/M2o00W8GlQ3OT4y/hQGb5K2aytxx8QeSmJaaZTJbvaHhe0x3/fLgq +uWrW3WEWFtwasilySjOrFOtB9UNmJmNOHSJD3Bslbv5htRaWnoFPCXdwZtVMdoTq +IHIQqLoos/xj3kVD5sJSYySrveMeKaeUILTkb5ZubSivye1X2yiJLR7AtuwuiMio +CdIOqhn6xJqYhT7z0IhdKpLNPk4w1tBZSKOXqzrXS4uoJgTC67hWslWWZ2VC6IvZ +FmKuuGZamCCj6F1QF2IjMVM8evl84hEnN0ajdkA/QWnil9kcWvBm15Ho+oTvvJ7s +M8MD3RDSq/90FSiME4vbyNEyTmj0 +-----END CERTIFICATE-----)"; + QUIC_CONST_INIT const char kTestCertificatePrivateKeyRaw[] = { '\x30', '\x82', '\x04', '\xbc', '\x02', '\x01', '\x00', '\x30', '\x0d', '\x06', '\x09', '\x2a', '\x86', '\x48', '\x86', '\xf7', '\x0d', '\x01', diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/test_certificates.h b/chromium/net/third_party/quiche/src/quic/test_tools/test_certificates.h index ec4a4d407d5..e7d3035a610 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/test_certificates.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/test_certificates.h @@ -20,6 +20,10 @@ QUIC_CONST_INIT extern const char kTestCertificatePem[]; // |kTestCertificatePem| with a PEM-encoded root appended to the end. QUIC_CONST_INIT extern const char kTestCertificateChainPem[]; +// PEM-encoded certificate that contains a subjectAltName with an +// unknown/unsupported type. +QUIC_CONST_INIT extern const char kTestCertWithUnknownSanTypePem[]; + // DER-encoded private key for |kTestCertificate|. QUIC_CONST_INIT extern const quiche::QuicheStringPiece kTestCertificatePrivateKey; diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc index 11edb195a9d..82b549faeef 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc @@ -235,7 +235,7 @@ void QuicClientBase::WaitForStreamToClose(QuicStreamId id) { } } -bool QuicClientBase::WaitForCryptoHandshakeConfirmed() { +bool QuicClientBase::WaitForOneRttKeysAvailable() { DCHECK(connected()); while (connected() && !session_->OneRttKeysAvailable()) { @@ -247,6 +247,19 @@ bool QuicClientBase::WaitForCryptoHandshakeConfirmed() { return connected(); } +bool QuicClientBase::WaitForHandshakeConfirmed() { + if (!session_->connection()->version().HasHandshakeDone()) { + return WaitForOneRttKeysAvailable(); + } + while (connected() && session_->GetHandshakeState() < HANDSHAKE_CONFIRMED) { + WaitForEvents(); + } + + // If the handshake fails due to a timeout, the connection will be closed. + QUIC_LOG_IF(ERROR, !connected()) << "Handshake with server failed."; + return connected(); +} + bool QuicClientBase::connected() const { return session_.get() && session_->connection() && session_->connection()->connected(); @@ -290,21 +303,7 @@ QuicErrorCode QuicClientBase::connection_error() const { } QuicConnectionId QuicClientBase::GetNextConnectionId() { - QuicConnectionId server_designated_id = GetNextServerDesignatedConnectionId(); - return !server_designated_id.IsEmpty() ? server_designated_id - : GenerateNewConnectionId(); -} - -QuicConnectionId QuicClientBase::GetNextServerDesignatedConnectionId() { - QuicCryptoClientConfig::CachedState* cached = - crypto_config_.LookupOrCreate(server_id_); - // If the cached state indicates that we should use a server-designated - // connection ID, then return that connection ID. - CHECK(cached != nullptr) << "QuicClientCryptoConfig::LookupOrCreate returned " - << "unexpected nullptr."; - return cached->has_server_designated_connection_id() - ? cached->GetNextServerDesignatedConnectionId() - : EmptyQuicConnectionId(); + return GenerateNewConnectionId(); } QuicConnectionId QuicClientBase::GenerateNewConnectionId() { diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.h b/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.h index 0a40b82d64a..842afc6f14c 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.h @@ -102,9 +102,14 @@ class QuicClientBase { // Wait for events until the stream with the given ID is closed. void WaitForStreamToClose(QuicStreamId id); - // Wait for events until the handshake is confirmed. - // Returns true if the crypto handshake succeeds, false otherwise. - QUIC_MUST_USE_RESULT bool WaitForCryptoHandshakeConfirmed(); + // Wait for 1-RTT keys become available. + // Returns true once 1-RTT keys are available, false otherwise. + QUIC_MUST_USE_RESULT bool WaitForOneRttKeysAvailable(); + + // Wait for handshake state proceeds to HANDSHAKE_CONFIRMED. + // In QUIC crypto, this does the same as WaitForOneRttKeysAvailable, while in + // TLS, this waits for HANDSHAKE_DONE frame is received. + QUIC_MUST_USE_RESULT bool WaitForHandshakeConfirmed(); // Wait up to 50ms, and handle any events which occur. // Returns true if there are any outstanding requests. @@ -271,10 +276,6 @@ class QuicClientBase { // returned. Otherwise, the next random ID will be returned. QuicConnectionId GetNextConnectionId(); - // Returns the next server-designated ConnectionId from the cached config for - // |server_id_|, if it exists. Otherwise, returns 0. - QuicConnectionId GetNextServerDesignatedConnectionId(); - // Generates a new, random connection ID (as opposed to a server-designated // connection ID). virtual QuicConnectionId GenerateNewConnectionId(); diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_interop_test_bin.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client_interop_test_bin.cc index 803e313ea24..e6b9e73d473 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_client_interop_test_bin.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_interop_test_bin.cc @@ -41,8 +41,13 @@ enum class Feature { kConnectionClose, // The connection was established using TLS resumption. kResumption, + // 0-RTT data is being sent and acted on. + kZeroRtt, // A RETRY packet was successfully processed. kRetry, + // A handshake using a ClientHello that spans multiple packets completed + // successfully. + kQuantum, // Second row of features (anything else protocol-related) // We switched to a different port and the server migrated to it. @@ -68,8 +73,12 @@ char MatrixLetter(Feature f) { return 'C'; case Feature::kResumption: return 'R'; + case Feature::kZeroRtt: + return 'Z'; case Feature::kRetry: return 'S'; + case Feature::kQuantum: + return 'Q'; case Feature::kRebinding: return 'B'; case Feature::kHttp3: @@ -90,14 +99,23 @@ class QuicClientInteropRunner : QuicConnectionDebugVisitor { // Attempts a resumption using |client| by disconnecting and reconnecting. If // resumption is successful, |features_| is modified to add // Feature::kResumption to it, otherwise it is left unmodified. - void AttemptResumption(QuicClient* client); + void AttemptResumption(QuicClient* client, const std::string& authority); void AttemptRequest(QuicSocketAddress addr, std::string authority, QuicServerId server_id, ParsedQuicVersion version, bool test_version_negotiation, - bool attempt_rebind); + bool attempt_rebind, + bool attempt_multi_packet_chlo); + + // Constructs a SpdyHeaderBlock containing the pseudo-headers needed to make a + // GET request to "/" on the hostname |authority|. + spdy::SpdyHeaderBlock ConstructHeaderBlock(const std::string& authority); + + // Sends an HTTP request represented by |header_block| using |client|. + void SendRequest(QuicClient* client, + const spdy::SpdyHeaderBlock& header_block); void OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override { switch (frame.close_type) { @@ -134,20 +152,37 @@ class QuicClientInteropRunner : QuicConnectionDebugVisitor { std::set<Feature> features_; }; -void QuicClientInteropRunner::AttemptResumption(QuicClient* client) { +void QuicClientInteropRunner::AttemptResumption(QuicClient* client, + const std::string& authority) { client->Disconnect(); if (!client->Initialize()) { QUIC_LOG(ERROR) << "Failed to reinitialize client"; return; } - if (!client->Connect() || !client->session()->OneRttKeysAvailable()) { + if (!client->Connect()) { + return; + } + + bool zero_rtt_attempt = !client->session()->OneRttKeysAvailable(); + + spdy::SpdyHeaderBlock header_block = ConstructHeaderBlock(authority); + SendRequest(client, header_block); + + if (!client->session()->OneRttKeysAvailable()) { return; } + if (static_cast<QuicCryptoClientStream*>( test::QuicSessionPeer::GetMutableCryptoStream(client->session())) ->IsResumption()) { InsertFeature(Feature::kResumption); } + if (static_cast<QuicCryptoClientStream*>( + test::QuicSessionPeer::GetMutableCryptoStream(client->session())) + ->EarlyDataAccepted() && + zero_rtt_attempt && client->latest_response_code() != -1) { + InsertFeature(Feature::kZeroRtt); + } } void QuicClientInteropRunner::AttemptRequest(QuicSocketAddress addr, @@ -155,7 +190,8 @@ void QuicClientInteropRunner::AttemptRequest(QuicSocketAddress addr, QuicServerId server_id, ParsedQuicVersion version, bool test_version_negotiation, - bool attempt_rebind) { + bool attempt_rebind, + bool attempt_multi_packet_chlo) { ParsedQuicVersionVector versions = {version}; if (test_version_negotiation) { versions.insert(versions.begin(), QuicVersionReservedForNegotiation()); @@ -168,6 +204,15 @@ void QuicClientInteropRunner::AttemptRequest(QuicSocketAddress addr, QuicConfig config; QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(20); config.SetIdleNetworkTimeout(timeout); + if (attempt_multi_packet_chlo) { + // Make the ClientHello span multiple packets by adding a custom transport + // parameter. + constexpr auto kCustomParameter = + static_cast<TransportParameters::TransportParameterId>(0x173E); + std::string custom_value(2000, '?'); + config.custom_transport_parameters_to_send()[kCustomParameter] = + custom_value; + } auto client = std::make_unique<QuicClient>( addr, server_id, versions, config, &epoll_server, std::move(proof_verifier), std::move(session_cache)); @@ -192,34 +237,28 @@ void QuicClientInteropRunner::AttemptRequest(QuicSocketAddress addr, if (test_version_negotiation && !connect_result) { // Failed to negotiate version, retry without version negotiation. AttemptRequest(addr, authority, server_id, version, - /*test_version_negotiation=*/false, attempt_rebind); + /*test_version_negotiation=*/false, attempt_rebind, + attempt_multi_packet_chlo); return; } if (!client->session()->OneRttKeysAvailable()) { + if (attempt_multi_packet_chlo) { + // Failed to handshake with multi-packet client hello, retry without it. + AttemptRequest(addr, authority, server_id, version, + test_version_negotiation, attempt_rebind, + /*attempt_multi_packet_chlo=*/false); + return; + } return; } InsertFeature(Feature::kHandshake); - - // Construct and send a request. - spdy::SpdyHeaderBlock header_block; - header_block[":method"] = "GET"; - header_block[":scheme"] = "https"; - header_block[":authority"] = authority; - header_block[":path"] = "/"; - client->set_store_response(true); - client->SendRequestAndWaitForResponse(header_block, "", /*fin=*/true); - - client_stats = connection->GetStats(); - QuicSentPacketManager* sent_packet_manager = - test::QuicConnectionPeer::GetSentPacketManager(connection); - const bool received_forward_secure_ack = - sent_packet_manager != nullptr && - sent_packet_manager->GetLargestAckedPacket(ENCRYPTION_FORWARD_SECURE) - .IsInitialized(); - if (client_stats.stream_bytes_received > 0 && received_forward_secure_ack) { - InsertFeature(Feature::kStreamData); + if (attempt_multi_packet_chlo) { + InsertFeature(Feature::kQuantum); } + spdy::SpdyHeaderBlock header_block = ConstructHeaderBlock(authority); + SendRequest(client.get(), header_block); + if (!client->connected()) { return; } @@ -238,7 +277,8 @@ void QuicClientInteropRunner::AttemptRequest(QuicSocketAddress addr, if (!client->connected()) { // Rebinding does not work, retry without attempting it. AttemptRequest(addr, authority, server_id, version, - test_version_negotiation, /*attempt_rebind=*/false); + test_version_negotiation, /*attempt_rebind=*/false, + attempt_multi_packet_chlo); return; } InsertFeature(Feature::kRebinding); @@ -259,7 +299,41 @@ void QuicClientInteropRunner::AttemptRequest(QuicSocketAddress addr, InsertFeature(Feature::kConnectionClose); } - AttemptResumption(client.get()); + AttemptResumption(client.get(), authority); +} + +spdy::SpdyHeaderBlock QuicClientInteropRunner::ConstructHeaderBlock( + const std::string& authority) { + // Construct and send a request. + spdy::SpdyHeaderBlock header_block; + header_block[":method"] = "GET"; + header_block[":scheme"] = "https"; + header_block[":authority"] = authority; + header_block[":path"] = "/"; + return header_block; +} + +void QuicClientInteropRunner::SendRequest( + QuicClient* client, + const spdy::SpdyHeaderBlock& header_block) { + client->set_store_response(true); + client->SendRequestAndWaitForResponse(header_block, "", /*fin=*/true); + + QuicConnection* connection = client->session()->connection(); + if (connection == nullptr) { + QUIC_LOG(ERROR) << "No QuicConnection object"; + return; + } + QuicConnectionStats client_stats = connection->GetStats(); + QuicSentPacketManager* sent_packet_manager = + test::QuicConnectionPeer::GetSentPacketManager(connection); + const bool received_forward_secure_ack = + sent_packet_manager != nullptr && + sent_packet_manager->GetLargestAckedPacket(ENCRYPTION_FORWARD_SECURE) + .IsInitialized(); + if (client_stats.stream_bytes_received > 0 && received_forward_secure_ack) { + InsertFeature(Feature::kStreamData); + } } std::set<Feature> ServerSupport(std::string dns_host, @@ -293,7 +367,8 @@ std::set<Feature> ServerSupport(std::string dns_host, runner.AttemptRequest(addr, authority, server_id, version, /*test_version_negotiation=*/true, - /*attempt_rebind=*/true); + /*attempt_rebind=*/true, + /*attempt_multi_packet_chlo=*/true); return runner.features(); } diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_epoll_client_factory.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_epoll_client_factory.cc index 1751f4173ed..38dc582c1f3 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_epoll_client_factory.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_epoll_client_factory.cc @@ -22,6 +22,7 @@ std::unique_ptr<QuicSpdyClientBase> QuicEpollClientFactory::CreateClient( std::string host_for_lookup, uint16_t port, ParsedQuicVersionVector versions, + const QuicConfig& config, std::unique_ptr<ProofVerifier> verifier) { QuicSocketAddress addr = tools::LookupAddress(host_for_lookup, quiche::QuicheStrCat(port)); @@ -30,8 +31,9 @@ std::unique_ptr<QuicSpdyClientBase> QuicEpollClientFactory::CreateClient( return nullptr; } QuicServerId server_id(host_for_handshake, port, false); - return std::make_unique<QuicClient>(addr, server_id, versions, &epoll_server_, - std::move(verifier)); + return std::make_unique<QuicClient>(addr, server_id, versions, config, + &epoll_server_, std::move(verifier), + nullptr); } } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_epoll_client_factory.h b/chromium/net/third_party/quiche/src/quic/tools/quic_epoll_client_factory.h index 2ee26d9703c..392bd6cc47c 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_epoll_client_factory.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_epoll_client_factory.h @@ -18,6 +18,7 @@ class QuicEpollClientFactory : public QuicToyClient::ClientFactory { std::string host_for_lookup, uint16_t port, ParsedQuicVersionVector versions, + const QuicConfig& config, std::unique_ptr<ProofVerifier> verifier) override; private: diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc index 798b66e418e..3192d303c96 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc @@ -207,6 +207,10 @@ class QuicPacketPrinter : public QuicFramerVisitorInterface { std::cerr << "OnHandshakeDoneFrame: " << frame; return true; } + bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) override { + std::cerr << "OnAckFrequencyFrame: " << frame; + return true; + } void OnPacketComplete() override { std::cerr << "OnPacketComplete\n"; } bool IsValidStatelessResetToken(QuicUint128 /*token*/) const override { std::cerr << "IsValidStatelessResetToken\n"; diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.cc index f84a248cda8..387b3135d4d 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.cc @@ -158,7 +158,7 @@ void QuicSimpleServerSession::HandleRstOnValidNonexistentStream( // index for it in promised_streams_ can be calculated. QuicStreamId next_stream_id = next_outgoing_unidirectional_stream_id(); if (VersionHasIetfQuicFrames(transport_version())) { - DCHECK(!QuicUtils::IsBidirectionalStreamId(frame.stream_id)); + DCHECK(!QuicUtils::IsBidirectionalStreamId(frame.stream_id, version())); } DCHECK_GE(frame.stream_id, next_stream_id); size_t index = (frame.stream_id - next_stream_id) / @@ -243,9 +243,9 @@ void QuicSimpleServerSession::OnCanCreateNewOutgoingStream( } void QuicSimpleServerSession::MaybeInitializeHttp3UnidirectionalStreams() { - size_t previous_static_stream_count = num_outgoing_static_streams(); + size_t previous_static_stream_count = num_static_streams(); QuicSpdySession::MaybeInitializeHttp3UnidirectionalStreams(); - size_t current_static_stream_count = num_outgoing_static_streams(); + size_t current_static_stream_count = num_static_streams(); DCHECK_GE(current_static_stream_count, previous_static_stream_count); highest_promised_stream_id_ += QuicUtils::StreamIdDelta(transport_version()) * diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc index 3dfaa1e3d22..5c8408428e7 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc @@ -18,6 +18,7 @@ #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" #include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h" @@ -239,6 +240,9 @@ class QuicSimpleServerStreamTest : public QuicTestWithParam<ParsedQuicVersion> { session_.config()->SetInitialSessionFlowControlWindowToSend( kInitialSessionFlowControlWindowForTest); session_.Initialize(); + if (connection_->version().SupportsAntiAmplificationLimit()) { + QuicConnectionPeer::SetAddressValidated(connection_); + } stream_ = new StrictMock<TestStream>( GetNthClientInitiatedBidirectionalStreamId( connection_->transport_version(), 0), diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.cc index 3301ef5d014..1e6cc54043a 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.cc @@ -5,6 +5,7 @@ #include "net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.h" #include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" namespace quic { @@ -24,20 +25,40 @@ QuicTcpLikeTraceConverter::StreamOffsetSegment::StreamOffsetSegment( QuicTcpLikeTraceConverter::StreamInfo::StreamInfo() : fin(false) {} +QuicIntervalSet<uint64_t> QuicTcpLikeTraceConverter::OnCryptoFrameSent( + EncryptionLevel level, + QuicStreamOffset offset, + QuicByteCount data_length) { + if (level >= NUM_ENCRYPTION_LEVELS) { + QUIC_BUG << "Invalid encryption level"; + return {}; + } + return OnFrameSent(offset, data_length, /*fin=*/false, + &crypto_frames_info_[level]); +} + QuicIntervalSet<uint64_t> QuicTcpLikeTraceConverter::OnStreamFrameSent( QuicStreamId stream_id, QuicStreamOffset offset, QuicByteCount data_length, bool fin) { + return OnFrameSent( + offset, data_length, fin, + &streams_info_.emplace(stream_id, StreamInfo()).first->second); +} + +QuicIntervalSet<uint64_t> QuicTcpLikeTraceConverter::OnFrameSent( + QuicStreamOffset offset, + QuicByteCount data_length, + bool fin, + StreamInfo* info) { QuicIntervalSet<uint64_t> connection_offsets; if (fin) { // Stream fin consumes a connection offset. ++data_length; } - StreamInfo* stream_info = - &streams_info_.emplace(stream_id, StreamInfo()).first->second; // Get connection offsets of retransmission data in this frame. - for (const auto& segment : stream_info->segments) { + for (const auto& segment : info->segments) { QuicInterval<QuicStreamOffset> retransmission(offset, offset + data_length); retransmission.IntersectWith(segment.stream_data); if (retransmission.Empty()) { @@ -50,15 +71,13 @@ QuicIntervalSet<uint64_t> QuicTcpLikeTraceConverter::OnStreamFrameSent( connection_offset + retransmission.Length()); } - if (stream_info->fin) { + if (info->fin) { return connection_offsets; } // Get connection offsets of new data in this frame. QuicStreamOffset least_unsent_offset = - stream_info->segments.empty() - ? 0 - : stream_info->segments.back().stream_data.max(); + info->segments.empty() ? 0 : info->segments.back().stream_data.max(); if (least_unsent_offset >= offset + data_length) { return connection_offsets; } @@ -68,20 +87,17 @@ QuicIntervalSet<uint64_t> QuicTcpLikeTraceConverter::OnStreamFrameSent( QuicByteCount new_data_length = offset + data_length - new_data_offset; connection_offsets.Add(connection_offset_, connection_offset_ + new_data_length); - if (!stream_info->segments.empty() && - new_data_offset == least_unsent_offset && - connection_offset_ == - stream_info->segments.back().connection_offset + - stream_info->segments.back().stream_data.Length()) { + if (!info->segments.empty() && new_data_offset == least_unsent_offset && + connection_offset_ == info->segments.back().connection_offset + + info->segments.back().stream_data.Length()) { // Extend the last segment if both stream and connection offsets are // contiguous. - stream_info->segments.back().stream_data.SetMax(new_data_offset + - new_data_length); + info->segments.back().stream_data.SetMax(new_data_offset + new_data_length); } else { - stream_info->segments.emplace_back(new_data_offset, connection_offset_, - new_data_length); + info->segments.emplace_back(new_data_offset, connection_offset_, + new_data_length); } - stream_info->fin = fin; + info->fin = fin; connection_offset_ += new_data_length; return connection_offsets; diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.h b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.h index 46dd7954a25..1380296e0ea 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.h @@ -37,6 +37,12 @@ class QuicTcpLikeTraceConverter { ~QuicTcpLikeTraceConverter() {} + // Called when a crypto frame is sent. Returns the corresponding connection + // offsets. + QuicIntervalSet<uint64_t> OnCryptoFrameSent(EncryptionLevel level, + QuicStreamOffset offset, + QuicByteCount data_length); + // Called when a stream frame is sent. Returns the corresponding connection // offsets. QuicIntervalSet<uint64_t> OnStreamFrameSent(QuicStreamId stream_id, @@ -59,6 +65,14 @@ class QuicTcpLikeTraceConverter { bool fin; }; + // Called when frame with |offset|, |data_length| and |fin| has been sent. + // Update |info| and returns connection offsets. + QuicIntervalSet<uint64_t> OnFrameSent(QuicStreamOffset offset, + QuicByteCount data_length, + bool fin, + StreamInfo* info); + + StreamInfo crypto_frames_info_[NUM_ENCRYPTION_LEVELS]; QuicHashMap<QuicStreamId, StreamInfo> streams_info_; QuicHashMap<QuicControlFrameId, QuicInterval<uint64_t>> control_frames_info_; diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter_test.cc index a6ce81a8dcd..fa751ae885e 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter_test.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter_test.cc @@ -98,6 +98,27 @@ TEST(QuicTcpLikeTraceConverterTest, FuzzerTest) { EXPECT_EQ(expected, converter.OnStreamFrameSent(1, 50, 600, false)); } +TEST(QuicTcpLikeTraceConverterTest, OnCryptoFrameSent) { + QuicTcpLikeTraceConverter converter; + + EXPECT_EQ(QuicIntervalSet<uint64_t>(0, 100), + converter.OnCryptoFrameSent(ENCRYPTION_INITIAL, 0, 100)); + EXPECT_EQ(QuicIntervalSet<uint64_t>(100, 200), + converter.OnStreamFrameSent(1, 0, 100, false)); + EXPECT_EQ(QuicIntervalSet<uint64_t>(200, 300), + converter.OnStreamFrameSent(1, 100, 100, false)); + EXPECT_EQ(QuicIntervalSet<uint64_t>(300, 400), + converter.OnCryptoFrameSent(ENCRYPTION_HANDSHAKE, 0, 100)); + EXPECT_EQ(QuicIntervalSet<uint64_t>(400, 500), + converter.OnCryptoFrameSent(ENCRYPTION_HANDSHAKE, 100, 100)); + + // Verify crypto frame retransmission works as intended. + EXPECT_EQ(QuicIntervalSet<uint64_t>(0, 100), + converter.OnCryptoFrameSent(ENCRYPTION_INITIAL, 0, 100)); + EXPECT_EQ(QuicIntervalSet<uint64_t>(400, 500), + converter.OnCryptoFrameSent(ENCRYPTION_HANDSHAKE, 100, 100)); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.cc index 0018f0524b7..b42f967caf0 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.cc @@ -109,6 +109,20 @@ DEFINE_QUIC_COMMAND_LINE_FLAG( "versions are offered in the handshake. Also supports wire versions " "such as Q043 or T099."); +DEFINE_QUIC_COMMAND_LINE_FLAG( + std::string, + connection_options, + "", + "Connection options as ASCII tags separated by commas, " + "e.g. \"ABCD,EFGH\""); + +DEFINE_QUIC_COMMAND_LINE_FLAG( + std::string, + client_connection_options, + "", + "Client connection options as ASCII tags separated by commas, " + "e.g. \"ABCD,EFGH\""); + DEFINE_QUIC_COMMAND_LINE_FLAG(bool, quic_ietf_draft, false, @@ -232,9 +246,22 @@ int QuicToyClient::SendRequestsAndPrintResponses( proof_verifier = quic::CreateDefaultProofVerifier(url.host()); } + QuicConfig config; + std::string connection_options_string = GetQuicFlag(FLAGS_connection_options); + if (!connection_options_string.empty()) { + config.SetConnectionOptionsToSend( + ParseQuicTagVector(connection_options_string)); + } + std::string client_connection_options_string = + GetQuicFlag(FLAGS_client_connection_options); + if (!client_connection_options_string.empty()) { + config.SetClientConnectionOptions( + ParseQuicTagVector(client_connection_options_string)); + } + // Build the client, and try to connect. std::unique_ptr<QuicSpdyClientBase> client = client_factory_->CreateClient( - url.host(), host, port, versions, std::move(proof_verifier)); + url.host(), host, port, versions, config, std::move(proof_verifier)); if (client == nullptr) { std::cerr << "Failed to create client." << std::endl; diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.h b/chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.h index 1a201225a58..d9d8ecaf87a 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_toy_client.h @@ -26,6 +26,7 @@ class QuicToyClient { std::string host_for_lookup, uint16_t port, ParsedQuicVersionVector versions, + const QuicConfig& config, std::unique_ptr<ProofVerifier> verifier) = 0; }; diff --git a/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler.h b/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler.h index a2c69890459..7b318911007 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler.h +++ b/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler.h @@ -29,6 +29,8 @@ class FifoWriteScheduler : public WriteScheduler<StreamIdType> { const StreamPrecedenceType& precedence) override; void UnregisterStream(StreamIdType stream_id) override; bool StreamRegistered(StreamIdType stream_id) const override; + // Stream precedence is available but note that it is not used for scheduling + // in this scheduler. StreamPrecedenceType GetStreamPrecedence( StreamIdType stream_id) const override; void UpdateStreamPrecedence(StreamIdType stream_id, @@ -51,20 +53,26 @@ class FifoWriteScheduler : public WriteScheduler<StreamIdType> { std::string DebugString() const override; private: + struct StreamInfo { + SpdyPriority priority; + int64_t event_time; // read/write event time (us since Unix epoch). + }; + std::set<StreamIdType> ready_streams_; - // This map maps stream ID to read/write event time (us since Unix epoch). - std::map<StreamIdType, int64_t> registered_streams_; + std::map<StreamIdType, StreamInfo> registered_streams_; }; template <typename StreamIdType> void FifoWriteScheduler<StreamIdType>::RegisterStream( StreamIdType stream_id, - const StreamPrecedenceType& /*precedence*/) { + const StreamPrecedenceType& precedence) { if (StreamRegistered(stream_id)) { SPDY_BUG << "Stream " << stream_id << " already registered"; return; } - registered_streams_.emplace_hint(registered_streams_.end(), stream_id, 0); + registered_streams_.emplace_hint( + registered_streams_.end(), stream_id, + StreamInfo{/*priority=*/precedence.spdy3_priority(), /*event_time=*/0}); } template <typename StreamIdType> @@ -88,14 +96,26 @@ bool FifoWriteScheduler<StreamIdType>::StreamRegistered( template <typename StreamIdType> typename FifoWriteScheduler<StreamIdType>::StreamPrecedenceType FifoWriteScheduler<StreamIdType>::GetStreamPrecedence( - StreamIdType /*stream_id*/) const { - return StreamPrecedenceType(kV3LowestPriority); + StreamIdType stream_id) const { + auto it = registered_streams_.find(stream_id); + if (it == registered_streams_.end()) { + SPDY_DVLOG(1) << "Stream " << stream_id << " not registered"; + return StreamPrecedenceType(kV3LowestPriority); + } + return StreamPrecedenceType(it->second.priority); } template <typename StreamIdType> void FifoWriteScheduler<StreamIdType>::UpdateStreamPrecedence( - StreamIdType /*stream_id*/, - const StreamPrecedenceType& /*precedence*/) {} + StreamIdType stream_id, + const StreamPrecedenceType& precedence) { + auto it = registered_streams_.find(stream_id); + if (it == registered_streams_.end()) { + SPDY_DVLOG(1) << "Stream " << stream_id << " not registered"; + return; + } + it->second.priority = precedence.spdy3_priority(); +} template <typename StreamIdType> std::vector<StreamIdType> FifoWriteScheduler<StreamIdType>::GetStreamChildren( @@ -109,7 +129,7 @@ void FifoWriteScheduler<StreamIdType>::RecordStreamEventTime( int64_t now_in_usec) { auto it = registered_streams_.find(stream_id); if (it != registered_streams_.end()) { - it->second = now_in_usec; + it->second.event_time = now_in_usec; } else { SPDY_BUG << "Stream " << stream_id << " is not registered"; } @@ -128,7 +148,8 @@ int64_t FifoWriteScheduler<StreamIdType>::GetLatestEventWithPrecedence( if (stream_id <= it->first) { break; } - latest_event_time_us = std::max(latest_event_time_us, it->second); + latest_event_time_us = + std::max(latest_event_time_us, it->second.event_time); } return latest_event_time_us; } diff --git a/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler_test.cc b/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler_test.cc index 950b6414d1d..d1dd066d6e9 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler_test.cc @@ -80,6 +80,24 @@ TEST(FifoWriteSchedulerTest, GetLatestEventTest) { EXPECT_EQ(0, fifo.GetLatestEventWithPrecedence(1)); } +TEST(FifoWriteSchedulerTest, GetStreamPrecedence) { + FifoWriteScheduler<SpdyStreamId> fifo; + // Return lowest priority for unknown stream. + EXPECT_EQ(kV3LowestPriority, fifo.GetStreamPrecedence(1).spdy3_priority()); + + fifo.RegisterStream(1, SpdyStreamPrecedence(3)); + EXPECT_TRUE(fifo.GetStreamPrecedence(1).is_spdy3_priority()); + EXPECT_EQ(3, fifo.GetStreamPrecedence(1).spdy3_priority()); + + // Redundant registration shouldn't change stream priority. + EXPECT_SPDY_BUG(fifo.RegisterStream(1, SpdyStreamPrecedence(4)), + "Stream 1 already registered"); + EXPECT_EQ(3, fifo.GetStreamPrecedence(1).spdy3_priority()); + + fifo.UpdateStreamPrecedence(1, SpdyStreamPrecedence(5)); + EXPECT_EQ(5, fifo.GetStreamPrecedence(1).spdy3_priority()); +} + } // namespace test } // namespace spdy diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.cc index e3bf8f64c15..71b94a37f3f 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.cc @@ -176,16 +176,9 @@ void HpackEncoder::EmitNonIndexedLiteral(const Representation& representation, SPDY_DVLOG(2) << "Emitting nonindexed literal: (" << representation.first << ", " << representation.second << ")"; output_stream_.AppendPrefix(kLiteralNoIndexOpcode); - if (GetSpdyReloadableFlag(spdy_hpack_use_indexed_name)) { - SPDY_CODE_COUNT(spdy_hpack_use_indexed_name); - const HpackEntry* name_entry = - header_table_.GetByName(representation.first); - if (enable_compression && name_entry != nullptr) { - output_stream_.AppendUint32(header_table_.IndexOf(name_entry)); - } else { - output_stream_.AppendUint32(0); - EmitString(representation.first); - } + const HpackEntry* name_entry = header_table_.GetByName(representation.first); + if (enable_compression && name_entry != nullptr) { + output_stream_.AppendUint32(header_table_.IndexOf(name_entry)); } else { output_stream_.AppendUint32(0); EmitString(representation.first); diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder_test.cc index 2b1efe973b8..f6f9f87a1c0 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder_test.cc @@ -274,12 +274,8 @@ TEST_F(HpackEncoderTestBase, EncodeRepresentations) { {"accept", "text/html, text/plain,application/xml"}, {"cookie", "val4"}, {"withnul", quiche::QuicheStringPiece("one\0two", 7)}}; - if (GetSpdyReloadableFlag(spdy_hpack_use_indexed_name)) { - ExpectNonIndexedLiteralWithNameIndex(peer_.table()->GetByName(":path"), - "/home"); - } else { - ExpectNonIndexedLiteral(":path", "/home"); - } + ExpectNonIndexedLiteralWithNameIndex(peer_.table()->GetByName(":path"), + "/home"); ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "val1"); ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "val2"); ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "val3"); @@ -566,12 +562,8 @@ TEST_P(HpackEncoderTest, PseudoHeadersFirst) { // Headers are indexed in the order in which they were added. // This entry pushes "cookie: a=bb" back to 63. - if (GetSpdyReloadableFlag(spdy_hpack_use_indexed_name)) { - ExpectNonIndexedLiteralWithNameIndex(peer_.table()->GetByName(":path"), - "/spam/eggs.html"); - } else { - ExpectNonIndexedLiteral(":path", "/spam/eggs.html"); - } + ExpectNonIndexedLiteralWithNameIndex(peer_.table()->GetByName(":path"), + "/spam/eggs.html"); ExpectIndexedLiteral(peer_.table()->GetByName(":authority"), "www.example.com"); ExpectIndexedLiteral("-foo", "bar"); diff --git a/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler.h b/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler.h index 405ccf5387b..d2e7fcfb847 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler.h +++ b/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler.h @@ -5,6 +5,7 @@ #ifndef QUICHE_SPDY_CORE_LIFO_WRITE_SCHEDULER_H_ #define QUICHE_SPDY_CORE_LIFO_WRITE_SCHEDULER_H_ +#include <cstdint> #include <map> #include <set> #include <string> @@ -40,15 +41,13 @@ class LifoWriteScheduler : public WriteScheduler<StreamIdType> { return registered_streams_.find(stream_id) != registered_streams_.end(); } - // Stream precedence is not supported by this scheduler. + // Stream precedence is available but note that it is not used for scheduling + // in this scheduler. StreamPrecedenceType GetStreamPrecedence( - StreamIdType /*stream_id*/) const override { - return StreamPrecedenceType(kV3LowestPriority); - } + StreamIdType stream_id) const override; - void UpdateStreamPrecedence( - StreamIdType /*stream_id*/, - const StreamPrecedenceType& /*precedence*/) override {} + void UpdateStreamPrecedence(StreamIdType stream_id, + const StreamPrecedenceType& precedence) override; std::vector<StreamIdType> GetStreamChildren( StreamIdType /*stream_id*/) const override { @@ -85,19 +84,26 @@ class LifoWriteScheduler : public WriteScheduler<StreamIdType> { private: friend class test::LifoWriteSchedulerPeer<StreamIdType>; + struct StreamInfo { + SpdyPriority priority; + int64_t event_time; // read/write event time (us since Unix epoch). + }; + std::set<StreamIdType> ready_streams_; - std::map<StreamIdType, int64_t> registered_streams_; + std::map<StreamIdType, StreamInfo> registered_streams_; }; template <typename StreamIdType> void LifoWriteScheduler<StreamIdType>::RegisterStream( StreamIdType stream_id, - const StreamPrecedenceType& /*precedence*/) { + const StreamPrecedenceType& precedence) { if (StreamRegistered(stream_id)) { SPDY_BUG << "Stream " << stream_id << " already registered"; return; } - registered_streams_.emplace_hint(registered_streams_.end(), stream_id, 0); + registered_streams_.emplace_hint( + registered_streams_.end(), stream_id, + StreamInfo{/*priority=*/precedence.spdy3_priority(), /*event_time=*/0}); } template <typename StreamIdType> @@ -112,12 +118,36 @@ void LifoWriteScheduler<StreamIdType>::UnregisterStream( } template <typename StreamIdType> +typename LifoWriteScheduler<StreamIdType>::StreamPrecedenceType +LifoWriteScheduler<StreamIdType>::GetStreamPrecedence( + StreamIdType stream_id) const { + auto it = registered_streams_.find(stream_id); + if (it == registered_streams_.end()) { + SPDY_DVLOG(1) << "Stream " << stream_id << " not registered"; + return StreamPrecedenceType(kV3LowestPriority); + } + return StreamPrecedenceType(it->second.priority); +} + +template <typename StreamIdType> +void LifoWriteScheduler<StreamIdType>::UpdateStreamPrecedence( + StreamIdType stream_id, + const StreamPrecedenceType& precedence) { + auto it = registered_streams_.find(stream_id); + if (it == registered_streams_.end()) { + SPDY_DVLOG(1) << "Stream " << stream_id << " not registered"; + return; + } + it->second.priority = precedence.spdy3_priority(); +} + +template <typename StreamIdType> void LifoWriteScheduler<StreamIdType>::RecordStreamEventTime( StreamIdType stream_id, int64_t now_in_usec) { auto it = registered_streams_.find(stream_id); if (it != registered_streams_.end()) { - it->second = now_in_usec; + it->second.event_time = now_in_usec; } else { SPDY_BUG << "Stream " << stream_id << " is not registered"; } @@ -134,8 +164,8 @@ int64_t LifoWriteScheduler<StreamIdType>::GetLatestEventWithPrecedence( for (auto it = registered_streams_.rbegin(); it != registered_streams_.rend(); ++it) { if (stream_id < it->first) { - if (it->second > latest_event_time_us) { - latest_event_time_us = it->second; + if (it->second.event_time > latest_event_time_us) { + latest_event_time_us = it->second.event_time; } } else { break; diff --git a/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler_test.cc b/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler_test.cc index 1d7ecbf8e32..744ee136521 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler_test.cc @@ -151,6 +151,24 @@ TEST(LifoWriteSchedulerTest, GetLatestEventTest) { "Stream 11 is not registered"); } +TEST(LifoWriteSchedulerTest, GetStreamPrecedence) { + LifoWriteScheduler<SpdyStreamId> lifo; + // Return lowest priority for unknown stream. + EXPECT_EQ(kV3LowestPriority, lifo.GetStreamPrecedence(1).spdy3_priority()); + + lifo.RegisterStream(1, SpdyStreamPrecedence(3)); + EXPECT_TRUE(lifo.GetStreamPrecedence(1).is_spdy3_priority()); + EXPECT_EQ(3, lifo.GetStreamPrecedence(1).spdy3_priority()); + + // Redundant registration shouldn't change stream priority. + EXPECT_SPDY_BUG(lifo.RegisterStream(1, SpdyStreamPrecedence(4)), + "Stream 1 already registered"); + EXPECT_EQ(3, lifo.GetStreamPrecedence(1).spdy3_priority()); + + lifo.UpdateStreamPrecedence(1, SpdyStreamPrecedence(5)); + EXPECT_EQ(5, lifo.GetStreamPrecedence(1).spdy3_priority()); +} + } // namespace test } // namespace spdy |