diff options
Diffstat (limited to 'chromium/net/third_party/quiche/src')
308 files changed, 12415 insertions, 4798 deletions
diff --git a/chromium/net/third_party/quiche/src/README.md b/chromium/net/third_party/quiche/src/README.md index 60f24da2b09..1a3502f12ac 100644 --- a/chromium/net/third_party/quiche/src/README.md +++ b/chromium/net/third_party/quiche/src/README.md @@ -25,4 +25,7 @@ Envoy to get started: * [Platform implementations in Envoy](https://github.com/envoyproxy/envoy/tree/master/source/common/quic/platform) * [Build file in Envoy](https://github.com/envoyproxy/envoy/blob/main/bazel/external/quiche.BUILD) +To contribute to QUICHE, follow instructions at +[CONTRIBUTING.md](CONTRIBUTING.md). + QUICHE is only supported on little-endian platforms. diff --git a/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_containers_impl.h b/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_containers_impl.h new file mode 100644 index 00000000000..56ce474b540 --- /dev/null +++ b/chromium/net/third_party/quiche/src/common/platform/default/quiche_platform_impl/quiche_containers_impl.h @@ -0,0 +1,22 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_CONTAINERS_IMPL_H_ +#define QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_CONTAINERS_IMPL_H_ + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Weverything" + +#include "absl/container/btree_set.h" + +#pragma clang diagnostic pop + +namespace quiche { + +template <typename Key, typename Compare> +using QuicheSmallOrderedSetImpl = absl::btree_set<Key, Compare>; + +} // namespace quiche + +#endif // QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_CONTAINERS_IMPL_H_ diff --git a/chromium/net/third_party/quiche/src/common/print_elements.h b/chromium/net/third_party/quiche/src/common/print_elements.h index f241a4b9509..7a0997f4ba3 100644 --- a/chromium/net/third_party/quiche/src/common/print_elements.h +++ b/chromium/net/third_party/quiche/src/common/print_elements.h @@ -20,11 +20,13 @@ QUICHE_EXPORT_PRIVATE inline std::string PrintElements(const T& container) { std::stringstream debug_string; debug_string << "{"; auto it = container.cbegin(); - debug_string << *it; - ++it; - while (it != container.cend()) { - debug_string << ", " << *it; + if (it != container.cend()) { + debug_string << *it; ++it; + while (it != container.cend()) { + debug_string << ", " << *it; + ++it; + } } debug_string << "}"; return debug_string.str(); diff --git a/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.cc b/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.cc index 4381749d430..becf5bf4133 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.cc @@ -1,5 +1,6 @@ #include "http2/adapter/callback_visitor.h" +#include "absl/strings/escaping.h" #include "http2/adapter/nghttp2_util.h" #include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" #include "common/quiche_endian.h" @@ -48,13 +49,16 @@ CallbackVisitor::CallbackVisitor(Perspective perspective, callbacks_ = MakeCallbacksPtr(c); } -ssize_t CallbackVisitor::OnReadyToSend(absl::string_view serialized) { +int64_t CallbackVisitor::OnReadyToSend(absl::string_view serialized) { if (!callbacks_->send_callback) { return kSendError; } - ssize_t result = callbacks_->send_callback( + int64_t result = callbacks_->send_callback( nullptr, ToUint8Ptr(serialized.data()), serialized.size(), 0, user_data_); - QUICHE_VLOG(1) << "CallbackVisitor::OnReadyToSend returning " << result; + QUICHE_VLOG(1) << "CallbackVisitor::OnReadyToSend called with " + << serialized.size() << " bytes, returning " << result; + QUICHE_VLOG(2) << (perspective_ == Perspective::kClient ? "Client" : "Server") + << " sending: [" << absl::CEscape(serialized) << "]"; if (result > 0) { return result; } else if (result == NGHTTP2_ERR_WOULDBLOCK) { @@ -68,13 +72,11 @@ void CallbackVisitor::OnConnectionError() { QUICHE_LOG(ERROR) << "OnConnectionError not implemented"; } -void CallbackVisitor::OnFrameHeader(Http2StreamId stream_id, - size_t length, - uint8_t type, - uint8_t flags) { - QUICHE_VLOG(1) << "CallbackVisitor::OnFrameHeader(stream_id: " << stream_id - << ", len: " << length << ", type: " << int(type) - << ", flags: " << int(flags) << ")"; +bool CallbackVisitor::OnFrameHeader(Http2StreamId stream_id, size_t length, + uint8_t type, uint8_t flags) { + QUICHE_VLOG(1) << "CallbackVisitor::OnFrameHeader(stream_id=" << stream_id + << ", type=" << int(type) << ", length=" << length + << ", flags=" << int(flags) << ")"; if (static_cast<FrameType>(type) == FrameType::CONTINUATION) { // Treat CONTINUATION as HEADERS QUICHE_DCHECK_EQ(current_frame_.hd.stream_id, stream_id); @@ -89,9 +91,11 @@ void CallbackVisitor::OnFrameHeader(Http2StreamId stream_id, hd.type = type; hd.flags = flags; if (callbacks_->on_begin_frame_callback) { - callbacks_->on_begin_frame_callback(nullptr, &hd, user_data_); + const int result = + callbacks_->on_begin_frame_callback(nullptr, &hd, user_data_); + return result == 0; } - return; + return true; } // The general strategy is to clear |current_frame_| at the start of a new // frame, accumulate frame information from the various callback events, then @@ -102,15 +106,17 @@ void CallbackVisitor::OnFrameHeader(Http2StreamId stream_id, current_frame_.hd.type = type; current_frame_.hd.flags = flags; if (callbacks_->on_begin_frame_callback) { - callbacks_->on_begin_frame_callback(nullptr, ¤t_frame_.hd, - user_data_); + const int result = callbacks_->on_begin_frame_callback( + nullptr, ¤t_frame_.hd, user_data_); + return result == 0; } + return true; } void CallbackVisitor::OnSettingsStart() {} void CallbackVisitor::OnSetting(Http2Setting setting) { - settings_.push_back({.settings_id = setting.id, .value = setting.value}); + settings_.push_back({setting.id, setting.value}); } void CallbackVisitor::OnSettingsEnd() { @@ -119,7 +125,9 @@ void CallbackVisitor::OnSettingsEnd() { QUICHE_VLOG(1) << "OnSettingsEnd, received settings of size " << current_frame_.settings.niv; if (callbacks_->on_frame_recv_callback) { - callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); + const int result = callbacks_->on_frame_recv_callback( + nullptr, ¤t_frame_, user_data_); + QUICHE_DCHECK_EQ(0, result); } settings_.clear(); } @@ -127,7 +135,9 @@ void CallbackVisitor::OnSettingsEnd() { void CallbackVisitor::OnSettingsAck() { // ACK is part of the flags, which were set in OnFrameHeader(). if (callbacks_->on_frame_recv_callback) { - callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); + const int result = callbacks_->on_frame_recv_callback( + nullptr, ¤t_frame_, user_data_); + QUICHE_DCHECK_EQ(0, result); } } @@ -182,35 +192,43 @@ Http2VisitorInterface::OnHeaderResult CallbackVisitor::OnHeaderForStream( return HEADER_OK; } -void CallbackVisitor::OnEndHeadersForStream(Http2StreamId /*stream_id*/) { +bool CallbackVisitor::OnEndHeadersForStream(Http2StreamId /*stream_id*/) { if (callbacks_->on_frame_recv_callback) { const int result = callbacks_->on_frame_recv_callback( nullptr, ¤t_frame_, user_data_); - QUICHE_DCHECK_EQ(0, result); + return result == 0; } + return true; } -void CallbackVisitor::OnBeginDataForStream(Http2StreamId /*stream_id*/, +bool CallbackVisitor::OnBeginDataForStream(Http2StreamId /*stream_id*/, size_t payload_length) { // TODO(b/181586191): Interpret padding, subtract padding from // |remaining_data_|. remaining_data_ = payload_length; if (remaining_data_ == 0 && callbacks_->on_frame_recv_callback) { - callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); + const int result = callbacks_->on_frame_recv_callback( + nullptr, ¤t_frame_, user_data_); + return result == 0; } + return true; } -void CallbackVisitor::OnDataForStream(Http2StreamId stream_id, +bool CallbackVisitor::OnDataForStream(Http2StreamId stream_id, absl::string_view data) { + int result = 0; if (callbacks_->on_data_chunk_recv_callback) { - callbacks_->on_data_chunk_recv_callback(nullptr, current_frame_.hd.flags, - stream_id, ToUint8Ptr(data.data()), - data.size(), user_data_); + result = callbacks_->on_data_chunk_recv_callback( + nullptr, current_frame_.hd.flags, stream_id, ToUint8Ptr(data.data()), + data.size(), user_data_); } remaining_data_ -= data.size(); - if (remaining_data_ == 0 && callbacks_->on_frame_recv_callback) { - callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); + if (result == 0 && remaining_data_ == 0 && + callbacks_->on_frame_recv_callback) { + result = callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, + user_data_); } + return result == 0; } void CallbackVisitor::OnEndStream(Http2StreamId /*stream_id*/) {} @@ -219,7 +237,9 @@ void CallbackVisitor::OnRstStream(Http2StreamId /*stream_id*/, Http2ErrorCode error_code) { current_frame_.rst_stream.error_code = static_cast<uint32_t>(error_code); if (callbacks_->on_frame_recv_callback) { - callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); + const int result = callbacks_->on_frame_recv_callback( + nullptr, ¤t_frame_, user_data_); + QUICHE_DCHECK_EQ(0, result); } } @@ -240,7 +260,9 @@ void CallbackVisitor::OnPriorityForStream(Http2StreamId /*stream_id*/, current_frame_.priority.pri_spec.weight = weight; current_frame_.priority.pri_spec.exclusive = exclusive; if (callbacks_->on_frame_recv_callback) { - callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); + const int result = callbacks_->on_frame_recv_callback( + nullptr, ¤t_frame_, user_data_); + QUICHE_DCHECK_EQ(0, result); } } @@ -250,7 +272,9 @@ void CallbackVisitor::OnPing(Http2PingId ping_id, bool /*is_ack*/) { std::memcpy(current_frame_.ping.opaque_data, &network_order_opaque_data, sizeof(network_order_opaque_data)); if (callbacks_->on_frame_recv_callback) { - callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); + const int result = callbacks_->on_frame_recv_callback( + nullptr, ¤t_frame_, user_data_); + QUICHE_DCHECK_EQ(0, result); } } @@ -259,7 +283,7 @@ void CallbackVisitor::OnPushPromiseForStream( QUICHE_LOG(DFATAL) << "Not implemented"; } -void CallbackVisitor::OnGoAway(Http2StreamId last_accepted_stream_id, +bool CallbackVisitor::OnGoAway(Http2StreamId last_accepted_stream_id, Http2ErrorCode error_code, absl::string_view opaque_data) { current_frame_.goaway.last_stream_id = last_accepted_stream_id; @@ -267,15 +291,20 @@ void CallbackVisitor::OnGoAway(Http2StreamId last_accepted_stream_id, current_frame_.goaway.opaque_data = ToUint8Ptr(opaque_data.data()); current_frame_.goaway.opaque_data_len = opaque_data.size(); if (callbacks_->on_frame_recv_callback) { - callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); + const int result = callbacks_->on_frame_recv_callback( + nullptr, ¤t_frame_, user_data_); + return result == 0; } + return true; } void CallbackVisitor::OnWindowUpdate(Http2StreamId /*stream_id*/, int window_increment) { current_frame_.window_update.window_size_increment = window_increment; if (callbacks_->on_frame_recv_callback) { - callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); + const int result = callbacks_->on_frame_recv_callback( + nullptr, ¤t_frame_, user_data_); + QUICHE_DCHECK_EQ(0, result); } } @@ -316,8 +345,8 @@ int CallbackVisitor::OnBeforeFrameSent(uint8_t frame_type, Http2StreamId stream_id, size_t length, uint8_t flags) { if (callbacks_->before_frame_send_callback) { - QUICHE_VLOG(1) << "OnBeforeFrameSent(type=" << int(frame_type) - << ", stream_id=" << stream_id << ", length=" << length + QUICHE_VLOG(1) << "OnBeforeFrameSent(stream_id=" << stream_id + << ", type=" << int(frame_type) << ", length=" << length << ", flags=" << int(flags) << ")"; nghttp2_frame frame; auto it = GetStreamInfo(stream_id); @@ -335,8 +364,8 @@ int CallbackVisitor::OnFrameSent(uint8_t frame_type, Http2StreamId stream_id, size_t length, uint8_t flags, uint32_t error_code) { if (callbacks_->on_frame_send_callback) { - QUICHE_VLOG(1) << "OnFrameSent(type=" << int(frame_type) - << ", stream_id=" << stream_id << ", length=" << length + QUICHE_VLOG(1) << "OnFrameSent(stream_id=" << stream_id + << ", type=" << int(frame_type) << ", length=" << length << ", flags=" << int(flags) << ", error_code=" << error_code << ")"; nghttp2_frame frame; @@ -349,14 +378,6 @@ int CallbackVisitor::OnFrameSent(uint8_t frame_type, Http2StreamId stream_id, return 0; } -void CallbackVisitor::OnReadyToSendDataForStream(Http2StreamId /*stream_id*/, - char* /*destination_buffer*/, - size_t /*length*/, - ssize_t* /*written*/, - bool* /*end_stream*/) { - QUICHE_LOG(DFATAL) << "Not implemented"; -} - bool CallbackVisitor::OnInvalidFrame(Http2StreamId stream_id, int error_code) { QUICHE_VLOG(1) << "OnInvalidFrame(" << stream_id << ", " << error_code << ")"; QUICHE_DCHECK_EQ(stream_id, current_frame_.hd.stream_id); @@ -367,30 +388,13 @@ bool CallbackVisitor::OnInvalidFrame(Http2StreamId stream_id, int error_code) { return true; } -void CallbackVisitor::OnReadyToSendMetadataForStream(Http2StreamId stream_id, - char* buffer, - size_t length, - ssize_t* written) { - if (callbacks_->pack_extension_callback) { - nghttp2_frame frame; - frame.hd.type = kMetadataFrameType; - frame.hd.stream_id = stream_id; - frame.hd.flags = 0; - frame.hd.length = 0; - *written = callbacks_->pack_extension_callback(nullptr, ToUint8Ptr(buffer), - length, &frame, user_data_); - } - QUICHE_VLOG(1) << "OnReadyToSendMetadataForStream(stream_id=" << stream_id - << ", length=" << length << ", written=" << *written << ")"; -} - void CallbackVisitor::OnBeginMetadataForStream(Http2StreamId stream_id, size_t payload_length) { QUICHE_VLOG(1) << "OnBeginMetadataForStream(stream_id=" << stream_id << ", payload_length=" << payload_length << ")"; } -void CallbackVisitor::OnMetadataForStream(Http2StreamId stream_id, +bool CallbackVisitor::OnMetadataForStream(Http2StreamId stream_id, absl::string_view metadata) { QUICHE_VLOG(1) << "OnMetadataForStream(stream_id=" << stream_id << ", len=" << metadata.size() << ")"; @@ -398,8 +402,9 @@ void CallbackVisitor::OnMetadataForStream(Http2StreamId stream_id, int result = callbacks_->on_extension_chunk_recv_callback( nullptr, ¤t_frame_.hd, ToUint8Ptr(metadata.data()), metadata.size(), user_data_); - QUICHE_DCHECK_EQ(0, result); + return result == 0; } + return true; } bool CallbackVisitor::OnMetadataEndForStream(Http2StreamId stream_id) { @@ -409,9 +414,10 @@ bool CallbackVisitor::OnMetadataEndForStream(Http2StreamId stream_id) { void* payload; int result = callbacks_->unpack_extension_callback( nullptr, &payload, ¤t_frame_.hd, user_data_); - if (callbacks_->on_frame_recv_callback) { + if (result == 0 && callbacks_->on_frame_recv_callback) { current_frame_.ext.payload = payload; - callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); + result = callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, + user_data_); } return (result == 0); } diff --git a/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.h b/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.h index b5afc83a103..f252989477b 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor.h @@ -1,6 +1,7 @@ #ifndef QUICHE_HTTP2_ADAPTER_CALLBACK_VISITOR_H_ #define QUICHE_HTTP2_ADAPTER_CALLBACK_VISITOR_H_ +#include <cstdint> #include <memory> #include <vector> @@ -21,11 +22,9 @@ class QUICHE_EXPORT_PRIVATE CallbackVisitor : public Http2VisitorInterface { const nghttp2_session_callbacks& callbacks, void* user_data); - ssize_t OnReadyToSend(absl::string_view serialized) override; + int64_t OnReadyToSend(absl::string_view serialized) override; void OnConnectionError() override; - void OnFrameHeader(Http2StreamId stream_id, - size_t length, - uint8_t type, + bool OnFrameHeader(Http2StreamId stream_id, size_t length, uint8_t type, uint8_t flags) override; void OnSettingsStart() override; void OnSetting(Http2Setting setting) override; @@ -35,10 +34,10 @@ class QUICHE_EXPORT_PRIVATE CallbackVisitor : public Http2VisitorInterface { OnHeaderResult OnHeaderForStream(Http2StreamId stream_id, absl::string_view name, absl::string_view value) override; - void OnEndHeadersForStream(Http2StreamId stream_id) override; - void OnBeginDataForStream(Http2StreamId stream_id, + bool OnEndHeadersForStream(Http2StreamId stream_id) override; + bool OnBeginDataForStream(Http2StreamId stream_id, size_t payload_length) override; - void OnDataForStream(Http2StreamId stream_id, + bool OnDataForStream(Http2StreamId stream_id, absl::string_view data) override; void OnEndStream(Http2StreamId stream_id) override; void OnRstStream(Http2StreamId stream_id, Http2ErrorCode error_code) override; @@ -51,7 +50,7 @@ class QUICHE_EXPORT_PRIVATE CallbackVisitor : public Http2VisitorInterface { void OnPing(Http2PingId ping_id, bool is_ack) override; void OnPushPromiseForStream(Http2StreamId stream_id, Http2StreamId promised_stream_id) override; - void OnGoAway(Http2StreamId last_accepted_stream_id, + bool OnGoAway(Http2StreamId last_accepted_stream_id, Http2ErrorCode error_code, absl::string_view opaque_data) override; void OnWindowUpdate(Http2StreamId stream_id, int window_increment) override; @@ -59,19 +58,10 @@ class QUICHE_EXPORT_PRIVATE CallbackVisitor : public Http2VisitorInterface { size_t length, uint8_t flags) override; int OnFrameSent(uint8_t frame_type, Http2StreamId stream_id, size_t length, uint8_t flags, uint32_t error_code) override; - void OnReadyToSendDataForStream(Http2StreamId stream_id, - char* destination_buffer, - size_t length, - ssize_t* written, - bool* end_stream) override; bool OnInvalidFrame(Http2StreamId stream_id, int error_code) override; - void OnReadyToSendMetadataForStream(Http2StreamId stream_id, - char* buffer, - size_t length, - ssize_t* written) override; void OnBeginMetadataForStream(Http2StreamId stream_id, size_t payload_length) override; - void OnMetadataForStream(Http2StreamId stream_id, + bool OnMetadataForStream(Http2StreamId stream_id, absl::string_view metadata) override; bool OnMetadataEndForStream(Http2StreamId stream_id) override; void OnErrorDebug(absl::string_view message) override; diff --git a/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor_test.cc index 2cb31ed24f4..eaf3f980cc7 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor_test.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/callback_visitor_test.cc @@ -155,28 +155,6 @@ TEST(ClientCallbackVisitorUnitTest, StreamFrames) { EXPECT_CALL(callbacks, OnStreamClose(5, NGHTTP2_REFUSED_STREAM)); visitor.OnCloseStream(5, Http2ErrorCode::REFUSED_STREAM); - - // Metadata events - constexpr size_t kMetadataBufferSize = 256; - char metadata_dest[kMetadataBufferSize]; - ssize_t written = 0; - - const absl::string_view kExampleFrame = "This is supposed to be metadata."; - EXPECT_CALL( - callbacks, - OnPackExtension(_, kMetadataBufferSize, - Field(&nghttp2_frame::hd, - HasFrameHeaderRef(7, kMetadataFrameType, _)))) - .WillOnce(testing::Invoke( - [kExampleFrame](uint8_t* buf, size_t /*len*/, - const nghttp2_frame* /*frame*/) -> ssize_t { - std::memcpy(buf, kExampleFrame.data(), kExampleFrame.size()); - return kExampleFrame.size(); - })); - visitor.OnReadyToSendMetadataForStream(7, metadata_dest, kMetadataBufferSize, - &written); - ASSERT_EQ(written, kExampleFrame.size()); - EXPECT_EQ(absl::string_view(metadata_dest, written), kExampleFrame); } TEST(ClientCallbackVisitorUnitTest, HeadersWithContinuation) { @@ -311,28 +289,6 @@ TEST(ServerCallbackVisitorUnitTest, StreamFrames) { EXPECT_CALL(callbacks, OnStreamClose(3, NGHTTP2_INTERNAL_ERROR)); visitor.OnCloseStream(3, Http2ErrorCode::INTERNAL_ERROR); - - // Metadata events - constexpr size_t kMetadataBufferSize = 256; - char metadata_dest[kMetadataBufferSize]; - ssize_t written = 0; - - const absl::string_view kExampleFrame = "This is supposed to be metadata."; - EXPECT_CALL( - callbacks, - OnPackExtension(_, kMetadataBufferSize, - Field(&nghttp2_frame::hd, - HasFrameHeaderRef(5, kMetadataFrameType, _)))) - .WillOnce(testing::Invoke( - [kExampleFrame](uint8_t* buf, size_t /*len*/, - const nghttp2_frame* /*frame*/) -> ssize_t { - std::memcpy(buf, kExampleFrame.data(), kExampleFrame.size()); - return kExampleFrame.size(); - })); - visitor.OnReadyToSendMetadataForStream(5, metadata_dest, kMetadataBufferSize, - &written); - ASSERT_EQ(written, kExampleFrame.size()); - EXPECT_EQ(absl::string_view(metadata_dest, written), kExampleFrame); } } // namespace diff --git a/chromium/net/third_party/quiche/src/http2/adapter/data_source.h b/chromium/net/third_party/quiche/src/http2/adapter/data_source.h index d86714148c3..63448b3e56e 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/data_source.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/data_source.h @@ -1,6 +1,7 @@ #ifndef QUICHE_HTTP2_ADAPTER_DATA_SOURCE_H_ #define QUICHE_HTTP2_ADAPTER_DATA_SOURCE_H_ +#include <cstdint> #include <string> #include <utility> @@ -15,12 +16,12 @@ class QUICHE_EXPORT_PRIVATE DataFrameSource { public: virtual ~DataFrameSource() {} - enum : ssize_t { kBlocked = 0, kError = -1 }; + enum : int64_t { kBlocked = 0, kError = -1 }; // Returns the number of bytes to send in the next DATA frame, and whether // this frame indicates the end of the data. Returns {kBlocked, false} if // blocked, {kError, false} on error. - virtual std::pair<ssize_t, bool> SelectPayloadLength(size_t max_length) = 0; + virtual std::pair<int64_t, bool> SelectPayloadLength(size_t max_length) = 0; // This method is called with a frame header and a payload length to send. The // source should send or buffer the entire frame and return true, or return @@ -37,11 +38,16 @@ class QUICHE_EXPORT_PRIVATE MetadataSource { public: virtual ~MetadataSource() {} + // Returns the number of frames of at most |max_frame_size| required to + // serialize the metadata for this source. Only required by the nghttp2 + // implementation. + virtual size_t NumFrames(size_t max_frame_size) const = 0; + // This method is called with a destination buffer and length. It should // return the number of payload bytes copied to |dest|, or a negative integer // to indicate an error, as well as a boolean indicating whether the metadata // has been completely copied. - virtual std::pair<ssize_t, bool> Pack(uint8_t* dest, size_t dest_len) = 0; + virtual std::pair<int64_t, bool> Pack(uint8_t* dest, size_t dest_len) = 0; }; } // namespace adapter diff --git a/chromium/net/third_party/quiche/src/http2/adapter/header_validator.cc b/chromium/net/third_party/quiche/src/http2/adapter/header_validator.cc new file mode 100644 index 00000000000..fef2e2cd343 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/header_validator.cc @@ -0,0 +1,99 @@ +#include "http2/adapter/header_validator.h" + +#include "absl/strings/escaping.h" +#include "common/platform/api/quiche_logging.h" + +namespace http2 { +namespace adapter { + +namespace { + +const absl::string_view kHttp2HeaderNameAllowedChars = + "!#$%&\'*+-.0123456789" + "^_`abcdefghijklmnopqrstuvwxyz|~"; + +const absl::string_view kHttp2HeaderValueAllowedChars = + "\t " + "!\"#$%&'()*+,-./" + "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" + "abcdefghijklmnopqrstuvwxyz{|}~"; + +const absl::string_view kHttp2StatusValueAllowedChars = "0123456789"; + +// TODO(birenroy): Support websocket requests, which contain an extra +// `:protocol` pseudo-header. +bool ValidateRequestHeaders(const std::vector<std::string>& pseudo_headers) { + static const std::vector<std::string>* kRequiredHeaders = + new std::vector<std::string>( + {":authority", ":method", ":path", ":scheme"}); + return pseudo_headers == *kRequiredHeaders; +} + +bool ValidateResponseHeaders(const std::vector<std::string>& pseudo_headers) { + static const std::vector<std::string>* kRequiredHeaders = + new std::vector<std::string>({":status"}); + return pseudo_headers == *kRequiredHeaders; +} + +bool ValidateResponseTrailers(const std::vector<std::string>& pseudo_headers) { + return pseudo_headers.empty(); +} + +} // namespace + +void HeaderValidator::StartHeaderBlock() { + pseudo_headers_.clear(); + status_.clear(); +} + +HeaderValidator::HeaderStatus HeaderValidator::ValidateSingleHeader( + absl::string_view key, absl::string_view value) { + if (key.empty()) { + return HEADER_NAME_EMPTY; + } + const absl::string_view validated_key = key[0] == ':' ? key.substr(1) : key; + if (validated_key.find_first_not_of(kHttp2HeaderNameAllowedChars) != + absl::string_view::npos) { + QUICHE_VLOG(2) << "invalid chars in header name: [" + << absl::CEscape(validated_key) << "]"; + return HEADER_NAME_INVALID_CHAR; + } + if (value.find_first_not_of(kHttp2HeaderValueAllowedChars) != + absl::string_view::npos) { + QUICHE_VLOG(2) << "invalid chars in header value: [" << absl::CEscape(value) + << "]"; + return HEADER_VALUE_INVALID_CHAR; + } + if (key[0] == ':') { + if (key == ":status") { + if (value.size() != 3 || + value.find_first_not_of(kHttp2StatusValueAllowedChars) != + absl::string_view::npos) { + QUICHE_VLOG(2) << "malformed status value: [" << absl::CEscape(value) + << "]"; + return HEADER_VALUE_INVALID_CHAR; + } + status_ = std::string(value); + } + pseudo_headers_.push_back(std::string(key)); + } + return HEADER_OK; +} + +// Returns true if all required pseudoheaders and no extra pseudoheaders are +// present for the given header type. +bool HeaderValidator::FinishHeaderBlock(HeaderType type) { + std::sort(pseudo_headers_.begin(), pseudo_headers_.end()); + switch (type) { + case HeaderType::REQUEST: + return ValidateRequestHeaders(pseudo_headers_); + case HeaderType::RESPONSE_100: + case HeaderType::RESPONSE: + return ValidateResponseHeaders(pseudo_headers_); + case HeaderType::RESPONSE_TRAILER: + return ValidateResponseTrailers(pseudo_headers_); + } +} + +} // namespace adapter +} // namespace http2 diff --git a/chromium/net/third_party/quiche/src/http2/adapter/header_validator.h b/chromium/net/third_party/quiche/src/http2/adapter/header_validator.h new file mode 100644 index 00000000000..2d7585e1a05 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/header_validator.h @@ -0,0 +1,49 @@ +#ifndef QUICHE_HTTP2_ADAPTER_HEADER_VALIDATOR_H_ +#define QUICHE_HTTP2_ADAPTER_HEADER_VALIDATOR_H_ + +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "common/platform/api/quiche_export.h" + +namespace http2 { +namespace adapter { + +enum class HeaderType : uint8_t { + REQUEST, + RESPONSE_100, + RESPONSE, + RESPONSE_TRAILER, +}; + +class QUICHE_EXPORT_PRIVATE HeaderValidator { + public: + HeaderValidator() {} + + void StartHeaderBlock(); + + enum HeaderStatus { + HEADER_OK, + HEADER_NAME_EMPTY, + HEADER_NAME_INVALID_CHAR, + HEADER_VALUE_INVALID_CHAR, + }; + HeaderStatus ValidateSingleHeader(absl::string_view key, + absl::string_view value); + + // Returns true if all required pseudoheaders and no extra pseudoheaders are + // present for the given header type. + bool FinishHeaderBlock(HeaderType type); + + absl::string_view status_header() const { return status_; } + + private: + std::vector<std::string> pseudo_headers_; + std::string status_; +}; + +} // namespace adapter +} // namespace http2 + +#endif // QUICHE_HTTP2_ADAPTER_HEADER_VALIDATOR_H_ diff --git a/chromium/net/third_party/quiche/src/http2/adapter/header_validator_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/header_validator_test.cc new file mode 100644 index 00000000000..01460c9a94b --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/header_validator_test.cc @@ -0,0 +1,213 @@ +#include "http2/adapter/header_validator.h" + +#include "absl/strings/str_cat.h" +#include "common/platform/api/quiche_test.h" + +namespace http2 { +namespace adapter { +namespace test { + +TEST(HeaderValidatorTest, HeaderNameEmpty) { + HeaderValidator v; + HeaderValidator::HeaderStatus status = v.ValidateSingleHeader("", "value"); + EXPECT_EQ(HeaderValidator::HEADER_NAME_EMPTY, status); +} + +TEST(HeaderValidatorTest, HeaderValueEmpty) { + HeaderValidator v; + HeaderValidator::HeaderStatus status = v.ValidateSingleHeader("name", ""); + EXPECT_EQ(HeaderValidator::HEADER_OK, status); +} + +TEST(HeaderValidatorTest, NameHasInvalidChar) { + HeaderValidator v; + for (const bool is_pseudo_header : {true, false}) { + // These characters should be allowed. (Not exhaustive.) + for (const char* c : {"!", "3", "a", "_", "|", "~"}) { + const std::string name = is_pseudo_header ? absl::StrCat(":met", c, "hod") + : absl::StrCat("na", c, "me"); + HeaderValidator::HeaderStatus status = + v.ValidateSingleHeader(name, "value"); + EXPECT_EQ(HeaderValidator::HEADER_OK, status); + } + // These should not. (Not exhaustive.) + for (const char* c : {"\\", "<", ";", "[", "=", " ", "\r", "\n", ",", "\"", + "\x1F", "\x91"}) { + const std::string name = is_pseudo_header ? absl::StrCat(":met", c, "hod") + : absl::StrCat("na", c, "me"); + HeaderValidator::HeaderStatus status = + v.ValidateSingleHeader(name, "value"); + EXPECT_EQ(HeaderValidator::HEADER_NAME_INVALID_CHAR, status); + } + // Uppercase characters in header names should not be allowed. + const std::string uc_name = is_pseudo_header ? ":Method" : "Name"; + HeaderValidator::HeaderStatus status = + v.ValidateSingleHeader(uc_name, "value"); + EXPECT_EQ(HeaderValidator::HEADER_NAME_INVALID_CHAR, status); + } +} + +TEST(HeaderValidatorTest, ValueHasInvalidChar) { + HeaderValidator v; + // These characters should be allowed. (Not exhaustive.) + for (const char* c : + {"!", "3", "a", "_", "|", "~", "\\", "<", ";", "[", "=", "A", "\t"}) { + HeaderValidator::HeaderStatus status = + v.ValidateSingleHeader("name", absl::StrCat("val", c, "ue")); + EXPECT_EQ(HeaderValidator::HEADER_OK, status); + } + // These should not. + for (const char* c : {"\r", "\n"}) { + HeaderValidator::HeaderStatus status = + v.ValidateSingleHeader("name", absl::StrCat("val", c, "ue")); + EXPECT_EQ(HeaderValidator::HEADER_VALUE_INVALID_CHAR, status); + } +} + +TEST(HeaderValidatorTest, StatusHasInvalidChar) { + HeaderValidator v; + + for (HeaderType type : {HeaderType::RESPONSE, HeaderType::RESPONSE_100}) { + // When `:status` has a non-digit value, validation will fail. + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_VALUE_INVALID_CHAR, + v.ValidateSingleHeader(":status", "bar")); + EXPECT_FALSE(v.FinishHeaderBlock(type)); + + // When `:status` is too short, validation will fail. + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_VALUE_INVALID_CHAR, + v.ValidateSingleHeader(":status", "10")); + EXPECT_FALSE(v.FinishHeaderBlock(type)); + + // When `:status` is too long, validation will fail. + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_VALUE_INVALID_CHAR, + v.ValidateSingleHeader(":status", "9000")); + EXPECT_FALSE(v.FinishHeaderBlock(type)); + + // When `:status` is just right, validation will succeed. + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":status", "400")); + EXPECT_TRUE(v.FinishHeaderBlock(type)); + } +} + +TEST(HeaderValidatorTest, RequestPseudoHeaders) { + HeaderValidator v; + const absl::string_view headers[] = {":authority", ":method", ":path", + ":scheme"}; + for (absl::string_view to_skip : headers) { + v.StartHeaderBlock(); + for (absl::string_view to_add : headers) { + if (to_add != to_skip) { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add, "foo")); + } + } + // When any pseudo-header is missing, final validation will fail. + EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST)); + } + + // When all pseudo-headers are present, final validation will succeed. + v.StartHeaderBlock(); + for (absl::string_view to_add : headers) { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add, "foo")); + } + EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST)); + + // When an extra pseudo-header is present, final validation will fail. + v.StartHeaderBlock(); + for (absl::string_view to_add : headers) { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add, "foo")); + } + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":extra", "blah")); + EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST)); + + // When a required pseudo-header is repeated, final validation will fail. + for (absl::string_view to_repeat : headers) { + v.StartHeaderBlock(); + for (absl::string_view to_add : headers) { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add, "foo")); + if (to_add == to_repeat) { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add, "foo")); + } + } + EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST)); + } +} + +TEST(HeaderValidatorTest, WebsocketPseudoHeaders) { + HeaderValidator v; + const absl::string_view headers[] = {":authority", ":method", ":path", + ":scheme"}; + v.StartHeaderBlock(); + for (absl::string_view to_add : headers) { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add, "foo")); + } + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":protocol", "websocket")); + // For now, `:protocol` is treated as an extra pseudo-header. + EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST)); +} + +TEST(HeaderValidatorTest, ResponsePseudoHeaders) { + HeaderValidator v; + + for (HeaderType type : {HeaderType::RESPONSE, HeaderType::RESPONSE_100}) { + // When `:status` is missing, validation will fail. + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, v.ValidateSingleHeader("foo", "bar")); + EXPECT_FALSE(v.FinishHeaderBlock(type)); + + // When all pseudo-headers are present, final validation will succeed. + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":status", "199")); + EXPECT_TRUE(v.FinishHeaderBlock(type)); + EXPECT_EQ("199", v.status_header()); + + // When `:status` is repeated, validation will fail. + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":status", "199")); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":status", "299")); + EXPECT_FALSE(v.FinishHeaderBlock(type)); + + // When an extra pseudo-header is present, final validation will fail. + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":status", "199")); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":extra", "blorp")); + EXPECT_FALSE(v.FinishHeaderBlock(type)); + } +} + +TEST(HeaderValidatorTest, ResponseTrailerPseudoHeaders) { + HeaderValidator v; + + // When no pseudo-headers are present, validation will succeed. + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, v.ValidateSingleHeader("foo", "bar")); + EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::RESPONSE_TRAILER)); + + // When any pseudo-header is present, final validation will fail. + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":status", "200")); + EXPECT_EQ(HeaderValidator::HEADER_OK, v.ValidateSingleHeader("foo", "bar")); + EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::RESPONSE_TRAILER)); +} + +} // namespace test +} // namespace adapter +} // namespace http2 diff --git a/chromium/net/third_party/quiche/src/http2/adapter/http2_adapter.h b/chromium/net/third_party/quiche/src/http2/adapter/http2_adapter.h index 789be98ae3b..8c231d1e6db 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/http2_adapter.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/http2_adapter.h @@ -1,6 +1,8 @@ #ifndef QUICHE_HTTP2_ADAPTER_HTTP2_ADAPTER_H_ #define QUICHE_HTTP2_ADAPTER_HTTP2_ADAPTER_H_ +#include <cstdint> + #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "absl/types/span.h" @@ -24,11 +26,16 @@ class QUICHE_EXPORT_PRIVATE Http2Adapter { Http2Adapter(const Http2Adapter&) = delete; Http2Adapter& operator=(const Http2Adapter&) = delete; + virtual ~Http2Adapter() {} + virtual bool IsServerSession() const = 0; + virtual bool want_read() const = 0; + virtual bool want_write() const = 0; + // Processes the incoming |bytes| as HTTP/2 and invokes callbacks on the // |visitor_| as appropriate. - virtual ssize_t ProcessBytes(absl::string_view bytes) = 0; + virtual int64_t ProcessBytes(absl::string_view bytes) = 0; // Submits the |settings| to be written to the peer, e.g., as part of the // HTTP/2 connection preface. @@ -64,7 +71,7 @@ class QUICHE_EXPORT_PRIVATE Http2Adapter { // Submits a sequence of METADATA frames for the given stream. A |stream_id| // of 0 indicates connection-level METADATA. - virtual void SubmitMetadata(Http2StreamId stream_id, + virtual void SubmitMetadata(Http2StreamId stream_id, size_t max_frame_size, std::unique_ptr<MetadataSource> source) = 0; // Invokes the visitor's OnReadyToSend() method for serialized frame data. @@ -142,7 +149,6 @@ class QUICHE_EXPORT_PRIVATE Http2Adapter { // Subclasses should expose a public factory method for constructing and // initializing (via Initialize()) adapter instances. explicit Http2Adapter(Http2VisitorInterface& visitor) : visitor_(visitor) {} - virtual ~Http2Adapter() {} // Accessors. Do not transfer ownership. Http2VisitorInterface& visitor() { return visitor_; } diff --git a/chromium/net/third_party/quiche/src/http2/adapter/http2_session.h b/chromium/net/third_party/quiche/src/http2/adapter/http2_session.h index 3b57d8f1aba..e88a555faf0 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/http2_session.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/http2_session.h @@ -18,7 +18,7 @@ class QUICHE_EXPORT_PRIVATE Http2Session { Http2Session() = default; virtual ~Http2Session() {} - virtual ssize_t ProcessBytes(absl::string_view bytes) = 0; + virtual int64_t ProcessBytes(absl::string_view bytes) = 0; virtual int Consume(Http2StreamId stream_id, size_t num_bytes) = 0; diff --git a/chromium/net/third_party/quiche/src/http2/adapter/http2_visitor_interface.h b/chromium/net/third_party/quiche/src/http2/adapter/http2_visitor_interface.h index 292021a23f4..4d12e42a380 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/http2_visitor_interface.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/http2_visitor_interface.h @@ -1,6 +1,7 @@ #ifndef QUICHE_HTTP2_ADAPTER_HTTP2_VISITOR_INTERFACE_H_ #define QUICHE_HTTP2_ADAPTER_HTTP2_VISITOR_INTERFACE_H_ +#include <cstdint> #include <vector> #include "absl/strings/string_view.h" @@ -51,18 +52,22 @@ class QUICHE_EXPORT_PRIVATE Http2VisitorInterface { Http2VisitorInterface& operator=(const Http2VisitorInterface&) = delete; virtual ~Http2VisitorInterface() = default; - static const ssize_t kSendBlocked = 0; - static const ssize_t kSendError = -1; + enum : int64_t { + kSendBlocked = 0, + kSendError = -1, + }; // Called when there are serialized frames to send. Should return how many // bytes were actually sent. May return kSendBlocked or kSendError. - virtual ssize_t OnReadyToSend(absl::string_view serialized) = 0; + virtual int64_t OnReadyToSend(absl::string_view serialized) = 0; // Called when a connection-level processing error has been encountered. virtual void OnConnectionError() = 0; // Called when the header for a frame is received. - virtual void OnFrameHeader(Http2StreamId /*stream_id*/, size_t /*length*/, - uint8_t /*type*/, uint8_t /*flags*/) {} + virtual bool OnFrameHeader(Http2StreamId /*stream_id*/, size_t /*length*/, + uint8_t /*type*/, uint8_t /*flags*/) { + return true; + } // Called when a non-ack SETTINGS frame is received. virtual void OnSettingsStart() = 0; @@ -103,16 +108,16 @@ class QUICHE_EXPORT_PRIVATE Http2VisitorInterface { // Called when the connection has received the complete header block for a // logical HEADERS frame on a stream (which may contain CONTINUATION frames, // transparent to the user). - virtual void OnEndHeadersForStream(Http2StreamId stream_id) = 0; + virtual bool OnEndHeadersForStream(Http2StreamId stream_id) = 0; // Called when the connection receives the beginning of a DATA frame. The data // payload will be provided via subsequent calls to OnDataForStream(). - virtual void OnBeginDataForStream(Http2StreamId stream_id, + virtual bool OnBeginDataForStream(Http2StreamId stream_id, size_t payload_length) = 0; // Called when the connection receives some |data| (as part of a DATA frame // payload) for a stream. - virtual void OnDataForStream(Http2StreamId stream_id, + virtual bool OnDataForStream(Http2StreamId stream_id, absl::string_view data) = 0; // Called when the peer sends the END_STREAM flag on a stream, indicating that @@ -142,7 +147,7 @@ class QUICHE_EXPORT_PRIVATE Http2VisitorInterface { Http2StreamId promised_stream_id) = 0; // Called when the connection receives a GOAWAY frame. - virtual void OnGoAway(Http2StreamId last_accepted_stream_id, + virtual bool OnGoAway(Http2StreamId last_accepted_stream_id, Http2ErrorCode error_code, absl::string_view opaque_data) = 0; @@ -163,31 +168,12 @@ class QUICHE_EXPORT_PRIVATE Http2VisitorInterface { size_t length, uint8_t flags, uint32_t error_code) = 0; - // Called when the connection is ready to send data for a stream. The - // implementation should write at most |length| bytes of the data payload to - // the |destination_buffer| and set |end_stream| to true IFF there will be no - // more data sent on this stream. Sets |written| to the number of bytes - // written to the |destination_buffer| or a negative value if an error occurs. - virtual void OnReadyToSendDataForStream(Http2StreamId stream_id, - char* destination_buffer, - size_t length, - ssize_t* written, - bool* end_stream) = 0; - // Called when the connection receives an invalid frame. |error_code| is a // negative integer error code generated by the library. A return value of // false will result in the connection entering an error state, with no // further frame processing possible. virtual bool OnInvalidFrame(Http2StreamId stream_id, int error_code) = 0; - // Called when the connection is ready to write metadata for |stream_id| to - // the wire. The implementation should write at most |length| bytes of the - // serialized metadata payload to the |buffer| and set |written| to the number - // of bytes written or a negative value if there was an error. - virtual void OnReadyToSendMetadataForStream(Http2StreamId stream_id, - char* buffer, size_t length, - ssize_t* written) = 0; - // Called when the connection receives the beginning of a METADATA frame // (which may itself be the middle of a logical metadata block). The metadata // payload will be provided via subsequent calls to OnMetadataForStream(). @@ -196,8 +182,8 @@ class QUICHE_EXPORT_PRIVATE Http2VisitorInterface { size_t payload_length) = 0; // Called when the connection receives |metadata| as part of a METADATA frame - // payload for a stream. - virtual void OnMetadataForStream(Http2StreamId stream_id, + // payload for a stream. Returns false if a fatal error has occurred. + virtual bool OnMetadataForStream(Http2StreamId stream_id, absl::string_view metadata) = 0; // Called when the connection has finished receiving a logical metadata block diff --git a/chromium/net/third_party/quiche/src/http2/adapter/mock_http2_visitor.h b/chromium/net/third_party/quiche/src/http2/adapter/mock_http2_visitor.h index 3daa8053962..1faf443339e 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/mock_http2_visitor.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/mock_http2_visitor.h @@ -1,6 +1,8 @@ #ifndef QUICHE_HTTP2_ADAPTER_MOCK_HTTP2_VISITOR_INTERFACE_H_ #define QUICHE_HTTP2_ADAPTER_MOCK_HTTP2_VISITOR_INTERFACE_H_ +#include <cstdint> + #include "http2/adapter/http2_visitor_interface.h" #include "common/platform/api/quiche_export.h" #include "common/platform/api/quiche_test.h" @@ -13,23 +15,26 @@ namespace test { class QUICHE_NO_EXPORT MockHttp2Visitor : public Http2VisitorInterface { public: MockHttp2Visitor() { + ON_CALL(*this, OnFrameHeader).WillByDefault(testing::Return(true)); ON_CALL(*this, OnBeginHeadersForStream) .WillByDefault(testing::Return(true)); ON_CALL(*this, OnHeaderForStream).WillByDefault(testing::Return(HEADER_OK)); + ON_CALL(*this, OnEndHeadersForStream).WillByDefault(testing::Return(true)); + ON_CALL(*this, OnBeginDataForStream).WillByDefault(testing::Return(true)); + ON_CALL(*this, OnDataForStream).WillByDefault(testing::Return(true)); + ON_CALL(*this, OnGoAway).WillByDefault(testing::Return(true)); ON_CALL(*this, OnInvalidFrame).WillByDefault(testing::Return(true)); + ON_CALL(*this, OnMetadataForStream).WillByDefault(testing::Return(true)); ON_CALL(*this, OnMetadataEndForStream).WillByDefault(testing::Return(true)); } - MOCK_METHOD(ssize_t, - OnReadyToSend, - (absl::string_view serialized), + MOCK_METHOD(int64_t, OnReadyToSend, (absl::string_view serialized), (override)); MOCK_METHOD(void, OnConnectionError, (), (override)); - MOCK_METHOD( - void, - OnFrameHeader, - (Http2StreamId stream_id, size_t length, uint8_t type, uint8_t flags), - (override)); + MOCK_METHOD(bool, OnFrameHeader, + (Http2StreamId stream_id, size_t length, uint8_t type, + uint8_t flags), + (override)); MOCK_METHOD(void, OnSettingsStart, (), (override)); MOCK_METHOD(void, OnSetting, (Http2Setting setting), (override)); MOCK_METHOD(void, OnSettingsEnd, (), (override)); @@ -42,20 +47,14 @@ class QUICHE_NO_EXPORT MockHttp2Visitor : public Http2VisitorInterface { absl::string_view value), (override)); - MOCK_METHOD(void, - OnEndHeadersForStream, - (Http2StreamId stream_id), + MOCK_METHOD(bool, OnEndHeadersForStream, (Http2StreamId stream_id), (override)); - MOCK_METHOD(void, - OnBeginDataForStream, - (Http2StreamId stream_id, size_t payload_length), - (override)); + MOCK_METHOD(bool, OnBeginDataForStream, + (Http2StreamId stream_id, size_t payload_length), (override)); - MOCK_METHOD(void, - OnDataForStream, - (Http2StreamId stream_id, absl::string_view data), - (override)); + MOCK_METHOD(bool, OnDataForStream, + (Http2StreamId stream_id, absl::string_view data), (override)); MOCK_METHOD(void, OnEndStream, (Http2StreamId stream_id), (override)); @@ -84,10 +83,8 @@ class QUICHE_NO_EXPORT MockHttp2Visitor : public Http2VisitorInterface { (Http2StreamId stream_id, Http2StreamId promised_stream_id), (override)); - MOCK_METHOD(void, - OnGoAway, - (Http2StreamId last_accepted_stream_id, - Http2ErrorCode error_code, + MOCK_METHOD(bool, OnGoAway, + (Http2StreamId last_accepted_stream_id, Http2ErrorCode error_code, absl::string_view opaque_data), (override)); @@ -110,27 +107,11 @@ class QUICHE_NO_EXPORT MockHttp2Visitor : public Http2VisitorInterface { (override)); MOCK_METHOD(void, - OnReadyToSendDataForStream, - (Http2StreamId stream_id, - char* destination_buffer, - size_t length, - ssize_t* written, - bool* end_stream), - (override)); - - MOCK_METHOD( - void, - OnReadyToSendMetadataForStream, - (Http2StreamId stream_id, char* buffer, size_t length, ssize_t* written), - (override)); - - MOCK_METHOD(void, OnBeginMetadataForStream, (Http2StreamId stream_id, size_t payload_length), (override)); - MOCK_METHOD(void, - OnMetadataForStream, + MOCK_METHOD(bool, OnMetadataForStream, (Http2StreamId stream_id, absl::string_view metadata), (override)); diff --git a/chromium/net/third_party/quiche/src/http2/adapter/mock_nghttp2_callbacks.h b/chromium/net/third_party/quiche/src/http2/adapter/mock_nghttp2_callbacks.h index 08d15be7d7d..98acad96e52 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/mock_nghttp2_callbacks.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/mock_nghttp2_callbacks.h @@ -1,6 +1,8 @@ #ifndef QUICHE_HTTP2_ADAPTER_MOCK_NGHTTP2_CALLBACKS_H_ #define QUICHE_HTTP2_ADAPTER_MOCK_NGHTTP2_CALLBACKS_H_ +#include <cstdint> + #include "absl/strings/string_view.h" #include "http2/adapter/nghttp2_util.h" #include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.cc index 9b444a3243d..5da518fefc9 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.cc @@ -12,6 +12,32 @@ namespace http2 { namespace adapter { +namespace { + +// A metadata source that deletes itself upon completion. +class SelfDeletingMetadataSource : public MetadataSource { + public: + explicit SelfDeletingMetadataSource(std::unique_ptr<MetadataSource> source) + : source_(std::move(source)) {} + + size_t NumFrames(size_t max_frame_size) const override { + return source_->NumFrames(max_frame_size); + } + + std::pair<int64_t, bool> Pack(uint8_t* dest, size_t dest_len) override { + const auto result = source_->Pack(dest, dest_len); + if (result.first < 0 || result.second) { + delete this; + } + return result; + } + + private: + std::unique_ptr<MetadataSource> source_; +}; + +} // anonymous namespace + /* static */ std::unique_ptr<NgHttp2Adapter> NgHttp2Adapter::CreateClientAdapter( Http2VisitorInterface& visitor, const nghttp2_option* options) { @@ -34,8 +60,8 @@ bool NgHttp2Adapter::IsServerSession() const { return result > 0; } -ssize_t NgHttp2Adapter::ProcessBytes(absl::string_view bytes) { - const ssize_t processed_bytes = session_->ProcessBytes(bytes); +int64_t NgHttp2Adapter::ProcessBytes(absl::string_view bytes) { + const int64_t processed_bytes = session_->ProcessBytes(bytes); if (processed_bytes < 0) { visitor_.OnConnectionError(); } @@ -90,9 +116,26 @@ void NgHttp2Adapter::SubmitWindowUpdate(Http2StreamId stream_id, stream_id, window_increment); } -void NgHttp2Adapter::SubmitMetadata( - Http2StreamId /*stream_id*/, std::unique_ptr<MetadataSource> /*source*/) { - QUICHE_LOG(DFATAL) << "Not implemented"; +void NgHttp2Adapter::SubmitMetadata(Http2StreamId stream_id, + size_t max_frame_size, + std::unique_ptr<MetadataSource> source) { + auto* wrapped_source = new SelfDeletingMetadataSource(std::move(source)); + const size_t num_frames = wrapped_source->NumFrames(max_frame_size); + size_t num_successes = 0; + for (size_t i = 1; i <= num_frames; ++i) { + const int result = nghttp2_submit_extension( + session_->raw_ptr(), kMetadataFrameType, + i == num_frames ? kMetadataEndFlag : 0, stream_id, wrapped_source); + if (result != 0) { + QUICHE_LOG(DFATAL) << "Failed to submit extension frame " << i << " of " + << num_frames; + break; + } + ++num_successes; + } + if (num_successes == 0) { + delete wrapped_source; + } } int NgHttp2Adapter::Send() { diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.h b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.h index 151f9a7ae5a..90782787867 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter.h @@ -1,6 +1,8 @@ #ifndef QUICHE_HTTP2_ADAPTER_NGHTTP2_ADAPTER_H_ #define QUICHE_HTTP2_ADAPTER_NGHTTP2_ADAPTER_H_ +#include <cstdint> + #include "absl/container/flat_hash_map.h" #include "http2/adapter/http2_adapter.h" #include "http2/adapter/http2_protocol.h" @@ -26,8 +28,10 @@ class QUICHE_EXPORT_PRIVATE NgHttp2Adapter : public Http2Adapter { Http2VisitorInterface& visitor, const nghttp2_option* options = nullptr); bool IsServerSession() const override; + bool want_read() const override { return session_->want_read(); } + bool want_write() const override { return session_->want_write(); } - ssize_t ProcessBytes(absl::string_view bytes) override; + int64_t ProcessBytes(absl::string_view bytes) override; void SubmitSettings(absl::Span<const Http2Setting> settings) override; void SubmitPriorityForStream(Http2StreamId stream_id, Http2StreamId parent_stream_id, @@ -49,7 +53,7 @@ class QUICHE_EXPORT_PRIVATE NgHttp2Adapter : public Http2Adapter { void SubmitRst(Http2StreamId stream_id, Http2ErrorCode error_code) override; - void SubmitMetadata(Http2StreamId stream_id, + void SubmitMetadata(Http2StreamId stream_id, size_t max_frame_size, std::unique_ptr<MetadataSource> source) override; int Send() override; @@ -84,10 +88,6 @@ class QUICHE_EXPORT_PRIVATE NgHttp2Adapter : public Http2Adapter { bool ResumeStream(Http2StreamId stream_id) override; - // TODO(b/181586191): Temporary accessor until equivalent functionality is - // available in this adapter class. - NgHttp2Session& session() { return *session_; } - private: NgHttp2Adapter(Http2VisitorInterface& visitor, Perspective perspective, const nghttp2_option* options); diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter_test.cc index 785e476c94d..ccfb1acd4f3 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter_test.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_adapter_test.cc @@ -1,7 +1,9 @@ #include "http2/adapter/nghttp2_adapter.h" +#include "http2/adapter/http2_protocol.h" #include "http2/adapter/mock_http2_visitor.h" #include "http2/adapter/nghttp2_test_utils.h" +#include "http2/adapter/oghttp2_util.h" #include "http2/adapter/test_frame_sequence.h" #include "http2/adapter/test_utils.h" #include "common/platform/api/quiche_test.h" @@ -48,8 +50,8 @@ TEST(NgHttp2AdapterTest, ClientConstruction) { testing::StrictMock<MockHttp2Visitor> visitor; auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); ASSERT_NE(nullptr, adapter); - EXPECT_TRUE(adapter->session().want_read()); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_read()); + EXPECT_FALSE(adapter->want_write()); EXPECT_FALSE(adapter->IsServerSession()); } @@ -81,7 +83,7 @@ TEST(NgHttp2AdapterTest, ClientHandlesFrames) { EXPECT_CALL(visitor, OnFrameHeader(0, 4, WINDOW_UPDATE, 0)); EXPECT_CALL(visitor, OnWindowUpdate(0, 1000)); - const ssize_t initial_result = adapter->ProcessBytes(initial_frames); + const int64_t initial_result = adapter->ProcessBytes(initial_frames); EXPECT_EQ(initial_frames.size(), initial_result); EXPECT_EQ(adapter->GetSendWindowSize(), kInitialFlowControlWindowSize + 1000); @@ -202,7 +204,7 @@ TEST(NgHttp2AdapterTest, ClientHandlesFrames) { EXPECT_CALL(visitor, OnFrameHeader(0, 19, GOAWAY, 0)); EXPECT_CALL(visitor, OnGoAway(5, Http2ErrorCode::ENHANCE_YOUR_CALM, "calm down!!")); - const ssize_t stream_result = adapter->ProcessBytes(stream_frames); + const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), stream_result); // First stream has received some data. @@ -229,7 +231,7 @@ TEST(NgHttp2AdapterTest, ClientHandlesFrames) { EXPECT_EQ(0, adapter->GetHighestReceivedStreamId()); // Even though the client recieved a GOAWAY, streams 1 and 5 are still active. - EXPECT_TRUE(adapter->session().want_read()); + EXPECT_TRUE(adapter->want_read()); EXPECT_CALL(visitor, OnFrameHeader(1, 0, DATA, 1)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 0)); @@ -248,10 +250,10 @@ TEST(NgHttp2AdapterTest, ClientHandlesFrames) { // After receiving END_STREAM for 1 and RST_STREAM for 5, the session no // longer expects reads. - EXPECT_FALSE(adapter->session().want_read()); + EXPECT_FALSE(adapter->want_read()); // Client will not have anything else to write. - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), testing::IsEmpty()); @@ -321,13 +323,13 @@ TEST(NgHttp2AdapterTest, ClientHandlesTrailers) { EXPECT_CALL(visitor, OnEndStream(1)); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR)); - const ssize_t stream_result = adapter->ProcessBytes(stream_frames); + const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), stream_result); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); @@ -401,13 +403,88 @@ TEST(NgHttp2AdapterTest, ClientHandlesMetadata) { EXPECT_CALL(visitor, OnEndStream(1)); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR)); - const ssize_t stream_result = adapter->ProcessBytes(stream_frames); + const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), stream_result); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); + result = adapter->Send(); + EXPECT_EQ(0, result); + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); +} + +TEST(NgHttp2AdapterTest, ClientHandlesMetadataWithError) { + DataSavingVisitor visitor; + auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); + + testing::InSequence s; + + const std::vector<const Header> headers1 = + ToHeaders({{":method", "GET"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}); + + const char* kSentinel1 = "arbitrary pointer 1"; + const int32_t stream_id1 = + adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1)); + ASSERT_GT(stream_id1, 0); + QUICHE_LOG(INFO) << "Created stream: " << stream_id1; + + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0)); + + int result = adapter->Send(); + EXPECT_EQ(0, result); + visitor.Clear(); + + const std::string stream_frames = + TestFrameSequence() + .ServerPreface() + .Metadata(0, "Example connection metadata") + .Headers(1, + {{":status", "200"}, + {"server", "my-fake-server"}, + {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, + /*fin=*/false) + .Metadata(1, "Example stream metadata") + .Data(1, "This is the response body.", true) + .Serialize(); + + // Server preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + + EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4)); + EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); + EXPECT_CALL(visitor, OnMetadataForStream(0, _)); + EXPECT_CALL(visitor, OnMetadataEndForStream(0)); + EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200")); + EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server")); + EXPECT_CALL(visitor, + OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT")); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4)); + EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _)); + EXPECT_CALL(visitor, OnMetadataForStream(1, _)) + .WillOnce(testing::Return(false)); + // Remaining frames are not processed due to the error. + EXPECT_CALL(visitor, OnConnectionError()); + + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + // The false return from OnMetadataForStream() results in a connection error. + EXPECT_EQ(stream_result, NGHTTP2_ERR_CALLBACK_FAILURE); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); + + EXPECT_TRUE(adapter->want_write()); + EXPECT_TRUE(adapter->want_read()); // Even after an error. Why? result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); @@ -481,7 +558,7 @@ TEST(NgHttp2AdapterTest, ClientHandlesInvalidTrailers) { // Bad status trailer will cause a PROTOCOL_ERROR. The header is never // delivered in an OnHeaderForStream callback. - const ssize_t stream_result = adapter->ProcessBytes(stream_frames); + const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), stream_result); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); @@ -490,7 +567,7 @@ TEST(NgHttp2AdapterTest, ClientHandlesInvalidTrailers) { EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, stream_id1, 4, 0x0, 1)); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::PROTOCOL_ERROR)); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, @@ -554,7 +631,7 @@ TEST(NgHttp2AdapterTest, ClientRstStreamWhileHandlingHeaders) { }), testing::Return(Http2VisitorInterface::HEADER_RST_STREAM))); - const ssize_t stream_result = adapter->ProcessBytes(stream_frames); + const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), stream_result); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); @@ -565,7 +642,7 @@ TEST(NgHttp2AdapterTest, ClientRstStreamWhileHandlingHeaders) { static_cast<int>(Http2ErrorCode::REFUSED_STREAM))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::REFUSED_STREAM)); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, @@ -627,13 +704,13 @@ TEST(NgHttp2AdapterTest, ClientConnectionErrorWhileHandlingHeaders) { testing::Return(Http2VisitorInterface::HEADER_CONNECTION_ERROR)); EXPECT_CALL(visitor, OnConnectionError()); - const ssize_t stream_result = adapter->ProcessBytes(stream_frames); + const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(-902 /* NGHTTP2_ERR_CALLBACK_FAILURE */, stream_result); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); @@ -690,13 +767,84 @@ TEST(NgHttp2AdapterTest, ClientRejectsHeaders) { // Rejecting headers leads to a connection error. EXPECT_CALL(visitor, OnConnectionError()); - const ssize_t stream_result = adapter->ProcessBytes(stream_frames); + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + EXPECT_EQ(NGHTTP2_ERR_CALLBACK_FAILURE, stream_result); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); + + EXPECT_TRUE(adapter->want_write()); + result = adapter->Send(); + EXPECT_EQ(0, result); + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); +} + +TEST(NgHttp2AdapterTest, ClientFailsOnGoAway) { + DataSavingVisitor visitor; + auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); + + testing::InSequence s; + + const std::vector<const Header> headers1 = + ToHeaders({{":method", "GET"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}); + + const char* kSentinel1 = "arbitrary pointer 1"; + const int32_t stream_id1 = + adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1)); + ASSERT_GT(stream_id1, 0); + QUICHE_LOG(INFO) << "Created stream: " << stream_id1; + + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0)); + + int result = adapter->Send(); + EXPECT_EQ(0, result); + absl::string_view data = visitor.data(); + EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); + data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); + EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::HEADERS})); + visitor.Clear(); + + const std::string stream_frames = + TestFrameSequence() + .ServerPreface() + .Headers(1, + {{":status", "200"}, + {"server", "my-fake-server"}, + {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, + /*fin=*/false) + .GoAway(1, Http2ErrorCode::INTERNAL_ERROR, "indigestion") + .Data(1, "This is the response body.") + .Serialize(); + + // Server preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + + EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200")); + EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server")); + EXPECT_CALL(visitor, + OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT")); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + EXPECT_CALL(visitor, OnFrameHeader(0, _, GOAWAY, 0)); + EXPECT_CALL(visitor, + OnGoAway(1, Http2ErrorCode::INTERNAL_ERROR, "indigestion")) + .WillOnce(testing::Return(false)); + EXPECT_CALL(visitor, OnConnectionError()); + + const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(NGHTTP2_ERR_CALLBACK_FAILURE, stream_result); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); @@ -721,10 +869,10 @@ TEST(NgHttp2AdapterTest, ClientSubmitRequest) { EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSettingsEnd()); - const ssize_t initial_result = adapter->ProcessBytes(initial_frames); + const int64_t initial_result = adapter->ProcessBytes(initial_frames); EXPECT_EQ(initial_frames.size(), initial_result); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); @@ -735,7 +883,7 @@ TEST(NgHttp2AdapterTest, ClientSubmitRequest) { visitor.Clear(); EXPECT_EQ(0, adapter->GetHpackEncoderDynamicTableSize()); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); const char* kSentinel = ""; const absl::string_view kBody = "This is an example request body."; auto body1 = absl::make_unique<TestDataFrameSource>(visitor, true); @@ -748,7 +896,7 @@ TEST(NgHttp2AdapterTest, ClientSubmitRequest) { {":path", "/this/is/request/one"}}), std::move(body1), const_cast<char*>(kSentinel)); EXPECT_GT(stream_id, 0); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0)); @@ -777,7 +925,7 @@ TEST(NgHttp2AdapterTest, ClientSubmitRequest) { spdy::SpdyFrameType::DATA})); EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody)); visitor.Clear(); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); stream_id = adapter->SubmitRequest(ToHeaders({{":method", "POST"}, @@ -786,7 +934,7 @@ TEST(NgHttp2AdapterTest, ClientSubmitRequest) { {":path", "/this/is/request/one"}}), nullptr, nullptr); EXPECT_GT(stream_id, 0); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); const char* kSentinel2 = "arbitrary pointer 2"; EXPECT_EQ(nullptr, adapter->GetStreamUserData(stream_id)); adapter->SetStreamUserData(stream_id, const_cast<char*>(kSentinel2)); @@ -827,10 +975,10 @@ TEST(NgHttp2AdapterTest, ClientSubmitRequestWithDataProvider) { EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSettingsEnd()); - const ssize_t initial_result = adapter->ProcessBytes(initial_frames); + const int64_t initial_result = adapter->ProcessBytes(initial_frames); EXPECT_EQ(initial_frames.size(), initial_result); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); @@ -840,7 +988,7 @@ TEST(NgHttp2AdapterTest, ClientSubmitRequestWithDataProvider) { EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); visitor.Clear(); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); const absl::string_view kBody = "This is an example request body."; // This test will use TestDataSource as the source of the body payload data. TestDataSource body1{kBody}; @@ -859,7 +1007,7 @@ TEST(NgHttp2AdapterTest, ClientSubmitRequestWithDataProvider) { {":path", "/this/is/request/one"}}), std::move(frame_source), nullptr); EXPECT_GT(stream_id, 0); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0)); @@ -870,7 +1018,7 @@ TEST(NgHttp2AdapterTest, ClientSubmitRequestWithDataProvider) { EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS, spdy::SpdyFrameType::DATA})); EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody)); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); } // This test verifies how nghttp2 behaves when a data source becomes @@ -898,7 +1046,7 @@ TEST(NgHttp2AdapterTest, ClientSubmitRequestWithDataProviderAndReadBlock) { {":path", "/this/is/request/one"}}), std::move(frame_source), nullptr); EXPECT_GT(stream_id, 0); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0)); @@ -912,23 +1060,23 @@ TEST(NgHttp2AdapterTest, ClientSubmitRequestWithDataProviderAndReadBlock) { serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::HEADERS})); visitor.Clear(); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); // Resume the deferred stream. body1.set_is_data_available(true); EXPECT_TRUE(adapter->ResumeStream(stream_id)); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, _, 0x1, 0)); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::DATA})); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); // Stream data is done, so this stream cannot be resumed. EXPECT_FALSE(adapter->ResumeStream(stream_id)); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); } // This test verifies how nghttp2 behaves when a data source is read block, then @@ -956,7 +1104,7 @@ TEST(NgHttp2AdapterTest, ClientSubmitRequestEmptyDataWithFin) { {":path", "/this/is/request/one"}}), std::move(frame_source), nullptr); EXPECT_GT(stream_id, 0); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0)); @@ -970,23 +1118,23 @@ TEST(NgHttp2AdapterTest, ClientSubmitRequestEmptyDataWithFin) { serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::HEADERS})); visitor.Clear(); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); // Resume the deferred stream. body1.set_is_data_available(true); EXPECT_TRUE(adapter->ResumeStream(stream_id)); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, 0, 0x1, 0)); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::DATA})); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); // Stream data is done, so this stream cannot be resumed. EXPECT_FALSE(adapter->ResumeStream(stream_id)); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); } // This test verifies how nghttp2 behaves when a connection becomes @@ -1013,13 +1161,13 @@ TEST(NgHttp2AdapterTest, ClientSubmitRequestWithDataProviderAndWriteBlock) { {":path", "/this/is/request/one"}}), std::move(frame_source), nullptr); EXPECT_GT(stream_id, 0); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); visitor.set_is_write_blocked(true); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), testing::IsEmpty()); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0)); @@ -1036,15 +1184,225 @@ TEST(NgHttp2AdapterTest, ClientSubmitRequestWithDataProviderAndWriteBlock) { serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::HEADERS, spdy::SpdyFrameType::DATA})); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); +} + +TEST(NgHttp2AdapterTest, SubmitMetadata) { + DataSavingVisitor visitor; + auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); + + auto source = absl::make_unique<TestMetadataSource>(ToHeaderBlock(ToHeaders( + {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}}))); + adapter->SubmitMetadata(1, 16384u, std::move(source)); + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4)); + EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0)); + + int result = adapter->Send(); + EXPECT_EQ(0, result); + absl::string_view serialized = visitor.data(); + EXPECT_THAT(serialized, + testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); + serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); + EXPECT_THAT( + serialized, + EqualsFrames({static_cast<spdy::SpdyFrameType>(kMetadataFrameType)})); + EXPECT_FALSE(adapter->want_write()); +} + +TEST(NgHttp2AdapterTest, SubmitMetadataMultipleFrames) { + DataSavingVisitor visitor; + auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); + + const auto kLargeValue = std::string(63 * 1024, 'a'); + auto source = absl::make_unique<TestMetadataSource>( + ToHeaderBlock(ToHeaders({{"large-value", kLargeValue}}))); + adapter->SubmitMetadata(1, 16384u, std::move(source)); + EXPECT_TRUE(adapter->want_write()); + + testing::InSequence seq; + EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4)); + EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0)); + + int result = adapter->Send(); + EXPECT_EQ(0, result); + absl::string_view serialized = visitor.data(); + EXPECT_THAT(serialized, + testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); + serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); + EXPECT_THAT( + serialized, + EqualsFrames({static_cast<spdy::SpdyFrameType>(kMetadataFrameType), + static_cast<spdy::SpdyFrameType>(kMetadataFrameType), + static_cast<spdy::SpdyFrameType>(kMetadataFrameType), + static_cast<spdy::SpdyFrameType>(kMetadataFrameType)})); + EXPECT_FALSE(adapter->want_write()); +} + +TEST(NgHttp2AdapterTest, SubmitConnectionMetadata) { + DataSavingVisitor visitor; + auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); + + auto source = absl::make_unique<TestMetadataSource>(ToHeaderBlock(ToHeaders( + {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}}))); + adapter->SubmitMetadata(0, 16384u, std::move(source)); + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(kMetadataFrameType, 0, _, 0x4)); + EXPECT_CALL(visitor, OnFrameSent(kMetadataFrameType, 0, _, 0x4, 0)); + + int result = adapter->Send(); + EXPECT_EQ(0, result); + absl::string_view serialized = visitor.data(); + EXPECT_THAT(serialized, + testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); + serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); + EXPECT_THAT( + serialized, + EqualsFrames({static_cast<spdy::SpdyFrameType>(kMetadataFrameType)})); + EXPECT_FALSE(adapter->want_write()); +} + +TEST(NgHttp2AdapterTest, ClientObeysMaxConcurrentStreams) { + DataSavingVisitor visitor; + auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); + int result = adapter->Send(); + EXPECT_EQ(0, result); + // Client preface does not appear to include the mandatory SETTINGS frame. + EXPECT_THAT(visitor.data(), + testing::StrEq(spdy::kHttp2ConnectionHeaderPrefix)); + visitor.Clear(); + + const std::string initial_frames = + TestFrameSequence() + .ServerPreface({{.id = MAX_CONCURRENT_STREAMS, .value = 1}}) + .Serialize(); + testing::InSequence s; + + // Server preface (SETTINGS with MAX_CONCURRENT_STREAMS) + EXPECT_CALL(visitor, OnFrameHeader(0, 6, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSetting); + EXPECT_CALL(visitor, OnSettingsEnd()); + + const int64_t initial_result = adapter->ProcessBytes(initial_frames); + EXPECT_EQ(initial_frames.size(), initial_result); + + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); + + result = adapter->Send(); + EXPECT_EQ(0, result); + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); + visitor.Clear(); + + EXPECT_FALSE(adapter->want_write()); + const absl::string_view kBody = "This is an example request body."; + auto body1 = absl::make_unique<TestDataFrameSource>(visitor, true); + body1->AppendPayload(kBody); + body1->EndData(); + const int stream_id = + adapter->SubmitRequest(ToHeaders({{":method", "POST"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}), + std::move(body1), nullptr); + EXPECT_GT(stream_id, 0); + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0)); + EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, _, 0x1, 0)); + + result = adapter->Send(); + EXPECT_EQ(0, result); + + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS, + spdy::SpdyFrameType::DATA})); + EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody)); + visitor.Clear(); + EXPECT_FALSE(adapter->want_write()); + + const int next_stream_id = + adapter->SubmitRequest(ToHeaders({{":method", "POST"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/two"}}), + nullptr, nullptr); + + // A new pending stream is created, but because of MAX_CONCURRENT_STREAMS, the + // session should not want to write it at the moment. + EXPECT_GT(next_stream_id, stream_id); + EXPECT_FALSE(adapter->want_write()); + + const std::string stream_frames = + TestFrameSequence() + .Headers(stream_id, + {{":status", "200"}, + {"server", "my-fake-server"}, + {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, + /*fin=*/false) + .Data(stream_id, "This is the response body.", /*fin=*/true) + .Serialize(); + + EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(stream_id)); + EXPECT_CALL(visitor, OnHeaderForStream(stream_id, ":status", "200")); + EXPECT_CALL(visitor, + OnHeaderForStream(stream_id, "server", "my-fake-server")); + EXPECT_CALL(visitor, OnHeaderForStream(stream_id, "date", + "Tue, 6 Apr 2021 12:54:01 GMT")); + EXPECT_CALL(visitor, OnEndHeadersForStream(stream_id)); + EXPECT_CALL(visitor, OnFrameHeader(stream_id, 26, DATA, 0x1)); + EXPECT_CALL(visitor, OnBeginDataForStream(stream_id, 26)); + EXPECT_CALL(visitor, + OnDataForStream(stream_id, "This is the response body.")); + EXPECT_CALL(visitor, OnEndStream(stream_id)); + EXPECT_CALL(visitor, OnCloseStream(stream_id, Http2ErrorCode::NO_ERROR)); + + // The first stream should close, which should make the session want to write + // the next stream. + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + EXPECT_EQ(stream_frames.size(), stream_result); + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, next_stream_id, _, 0x5)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, next_stream_id, _, 0x5, 0)); + + result = adapter->Send(); + EXPECT_EQ(0, result); + + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS})); + visitor.Clear(); + EXPECT_FALSE(adapter->want_write()); +} + +TEST(NgHttp2AdapterTest, FailureSendingConnectionPreface) { + DataSavingVisitor visitor; + auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor); + + visitor.set_has_write_error(); + EXPECT_CALL(visitor, OnConnectionError); + + int result = adapter->Send(); + EXPECT_EQ(result, NGHTTP2_ERR_CALLBACK_FAILURE); } TEST(NgHttp2AdapterTest, ServerConstruction) { testing::StrictMock<MockHttp2Visitor> visitor; auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); ASSERT_NE(nullptr, adapter); - EXPECT_TRUE(adapter->session().want_read()); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_read()); + EXPECT_FALSE(adapter->want_write()); EXPECT_TRUE(adapter->IsServerSession()); } @@ -1098,6 +1456,7 @@ TEST(NgHttp2AdapterTest, ServerHandlesFrames) { EXPECT_CALL(visitor, OnEndHeadersForStream(1)) .WillOnce(testing::InvokeWithoutArgs([&adapter, kSentinel1]() { adapter->SetStreamUserData(1, const_cast<char*>(kSentinel1)); + return true; })); EXPECT_CALL(visitor, OnFrameHeader(1, 4, WINDOW_UPDATE, 0)); EXPECT_CALL(visitor, OnWindowUpdate(1, 2000)); @@ -1118,7 +1477,7 @@ TEST(NgHttp2AdapterTest, ServerHandlesFrames) { EXPECT_CALL(visitor, OnFrameHeader(0, 8, PING, 0)); EXPECT_CALL(visitor, OnPing(47, false)); - const ssize_t result = adapter->ProcessBytes(frames); + const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), result); EXPECT_EQ(kSentinel1, adapter->GetStreamUserData(1)); @@ -1143,7 +1502,7 @@ TEST(NgHttp2AdapterTest, ServerHandlesFrames) { EXPECT_EQ(adapter->GetSendWindowSize(), kInitialFlowControlWindowSize + 1000); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); @@ -1199,10 +1558,10 @@ TEST(NgHttp2AdapterTest, ServerErrorWhileHandlingHeaders) { EXPECT_CALL(visitor, OnFrameHeader(0, 4, WINDOW_UPDATE, 0)); EXPECT_CALL(visitor, OnWindowUpdate(0, 2000)); - const ssize_t result = adapter->ProcessBytes(frames); + const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), result); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); @@ -1220,10 +1579,215 @@ TEST(NgHttp2AdapterTest, ServerErrorWhileHandlingHeaders) { spdy::SpdyFrameType::RST_STREAM})); } +TEST(NgHttp2AdapterTest, ServerErrorAfterHandlingHeaders) { + DataSavingVisitor visitor; + auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); + + const std::string frames = TestFrameSequence() + .ClientPreface() + .Headers(1, + {{":method", "POST"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}, + /*fin=*/false) + .WindowUpdate(1, 2000) + .Data(1, "This is the request body.") + .WindowUpdate(0, 2000) + .Serialize(); + testing::InSequence s; + + // Client preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + + EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "POST")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one")); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)) + .WillOnce(testing::Return(false)); + EXPECT_CALL(visitor, OnConnectionError()); + + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_EQ(-902, result); + + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); + + int send_result = adapter->Send(); + // Some bytes should have been serialized. + EXPECT_EQ(0, send_result); + // SETTINGS ack + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); +} + +// Exercises the case when a visitor chooses to reject a frame based solely on +// the frame header, which is a fatal error for the connection. +TEST(NgHttp2AdapterTest, ServerRejectsFrameHeader) { + DataSavingVisitor visitor; + auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); + + const std::string frames = TestFrameSequence() + .ClientPreface() + .Ping(64) + .Headers(1, + {{":method", "POST"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}, + /*fin=*/false) + .WindowUpdate(1, 2000) + .Data(1, "This is the request body.") + .WindowUpdate(0, 2000) + .Serialize(); + testing::InSequence s; + + // Client preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + + EXPECT_CALL(visitor, OnFrameHeader(0, 8, PING, 0)) + .WillOnce(testing::Return(false)); + EXPECT_CALL(visitor, OnConnectionError()); + + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_EQ(-902, result); + + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); + + int send_result = adapter->Send(); + // Some bytes should have been serialized. + EXPECT_EQ(0, send_result); + // SETTINGS ack + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); +} + +TEST(NgHttp2AdapterTest, ServerRejectsBeginningOfData) { + DataSavingVisitor visitor; + auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); + + const std::string frames = TestFrameSequence() + .ClientPreface() + .Headers(1, + {{":method", "POST"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}, + /*fin=*/false) + .Data(1, "This is the request body.") + .Headers(3, + {{":method", "GET"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/two"}}, + /*fin=*/true) + .RstStream(3, Http2ErrorCode::CANCEL) + .Ping(47) + .Serialize(); + testing::InSequence s; + + // Client preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + + EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "POST")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one")); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + EXPECT_CALL(visitor, OnFrameHeader(1, 25, DATA, 0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 25)) + .WillOnce(testing::Return(false)); + EXPECT_CALL(visitor, OnConnectionError()); + + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_EQ(NGHTTP2_ERR_CALLBACK_FAILURE, result); + + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); + + int send_result = adapter->Send(); + // Some bytes should have been serialized. + EXPECT_EQ(0, send_result); + // SETTINGS ack. + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); +} + +TEST(NgHttp2AdapterTest, ServerRejectsStreamData) { + DataSavingVisitor visitor; + auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); + + const std::string frames = TestFrameSequence() + .ClientPreface() + .Headers(1, + {{":method", "POST"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}, + /*fin=*/false) + .Data(1, "This is the request body.") + .Headers(3, + {{":method", "GET"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/two"}}, + /*fin=*/true) + .RstStream(3, Http2ErrorCode::CANCEL) + .Ping(47) + .Serialize(); + testing::InSequence s; + + // Client preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + + EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "POST")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one")); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + EXPECT_CALL(visitor, OnFrameHeader(1, 25, DATA, 0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 25)); + EXPECT_CALL(visitor, OnDataForStream(1, _)).WillOnce(testing::Return(false)); + EXPECT_CALL(visitor, OnConnectionError()); + + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_EQ(NGHTTP2_ERR_CALLBACK_FAILURE, result); + + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); + + int send_result = adapter->Send(); + // Some bytes should have been serialized. + EXPECT_EQ(0, send_result); + // SETTINGS ack. + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); +} + TEST(NgHttp2AdapterTest, ServerSubmitResponse) { DataSavingVisitor visitor; auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() .ClientPreface() @@ -1252,16 +1816,17 @@ TEST(NgHttp2AdapterTest, ServerSubmitResponse) { EXPECT_CALL(visitor, OnEndHeadersForStream(1)) .WillOnce(testing::InvokeWithoutArgs([&adapter, kSentinel1]() { adapter->SetStreamUserData(1, const_cast<char*>(kSentinel1)); + return true; })); EXPECT_CALL(visitor, OnEndStream(1)); - const ssize_t result = adapter->ProcessBytes(frames); + const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), result); EXPECT_EQ(1, adapter->GetHighestReceivedStreamId()); // Server will want to send a SETTINGS ack. - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); @@ -1273,7 +1838,7 @@ TEST(NgHttp2AdapterTest, ServerSubmitResponse) { EXPECT_EQ(0, adapter->GetHpackEncoderDynamicTableSize()); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); const absl::string_view kBody = "This is an example response body."; // A data fin is not sent so that the stream remains open, and the flow // control state can be verified. @@ -1285,7 +1850,7 @@ TEST(NgHttp2AdapterTest, ServerSubmitResponse) { {"x-comment", "I have no idea what you're talking about."}}), std::move(body1)); EXPECT_EQ(submit_result, 0); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); // Stream user data should have been set successfully after receiving headers. EXPECT_EQ(kSentinel1, adapter->GetStreamUserData(1)); @@ -1302,7 +1867,7 @@ TEST(NgHttp2AdapterTest, ServerSubmitResponse) { EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS, spdy::SpdyFrameType::DATA})); EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody)); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); // Some data was sent, so the remaining send window size should be less than // the default. @@ -1344,12 +1909,12 @@ TEST(NgHttp2AdapterTest, ServerSendsShutdown) { EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one")); EXPECT_CALL(visitor, OnEndHeadersForStream(1)); - const ssize_t result = adapter->ProcessBytes(frames); + const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), result); adapter->SubmitShutdownNotice(); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); @@ -1365,7 +1930,7 @@ TEST(NgHttp2AdapterTest, ServerSendsShutdown) { TEST(NgHttp2AdapterTest, ServerSendsTrailers) { DataSavingVisitor visitor; auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() .ClientPreface() @@ -1392,11 +1957,11 @@ TEST(NgHttp2AdapterTest, ServerSendsTrailers) { EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); - const ssize_t result = adapter->ProcessBytes(frames); + const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), result); // Server will want to send a SETTINGS ack. - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); @@ -1406,7 +1971,7 @@ TEST(NgHttp2AdapterTest, ServerSendsTrailers) { EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); visitor.Clear(); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); const absl::string_view kBody = "This is an example response body."; // The body source must indicate that the end of the body is not the end of @@ -1418,7 +1983,7 @@ TEST(NgHttp2AdapterTest, ServerSendsTrailers) { 1, ToHeaders({{":status", "200"}, {"x-comment", "Sure, sounds good."}}), std::move(body1)); EXPECT_EQ(submit_result, 0); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0)); @@ -1430,14 +1995,14 @@ TEST(NgHttp2AdapterTest, ServerSendsTrailers) { spdy::SpdyFrameType::DATA})); EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody)); visitor.Clear(); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); // The body source has been exhausted by the call to Send() above. int trailer_result = adapter->SubmitTrailer( 1, ToHeaders({{"final-status", "a-ok"}, {"x-comment", "trailers sure are cool"}})); ASSERT_EQ(trailer_result, 0); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x5)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0)); @@ -1451,7 +2016,7 @@ TEST(NgHttp2AdapterTest, ServerSendsTrailers) { TEST(NgHttp2AdapterTest, ClientSendsContinuation) { DataSavingVisitor visitor; auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() .ClientPreface() @@ -1480,14 +2045,14 @@ TEST(NgHttp2AdapterTest, ClientSendsContinuation) { EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); - const size_t result = adapter->ProcessBytes(frames); + const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), result); } TEST(NgHttp2AdapterTest, ClientSendsMetadataWithContinuation) { DataSavingVisitor visitor; auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() @@ -1537,7 +2102,7 @@ TEST(NgHttp2AdapterTest, ClientSendsMetadataWithContinuation) { EXPECT_CALL(visitor, OnMetadataForStream(1, _)); EXPECT_CALL(visitor, OnMetadataEndForStream(1)); - const size_t result = adapter->ProcessBytes(frames); + const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), result); EXPECT_EQ(TestFrameSequence::MetadataBlockForPayload( "Example connection metadata in multiple frames"), @@ -1550,7 +2115,7 @@ TEST(NgHttp2AdapterTest, ClientSendsMetadataWithContinuation) { TEST(NgHttp2AdapterTest, ServerSendsInvalidTrailers) { DataSavingVisitor visitor; auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() .ClientPreface() @@ -1577,7 +2142,7 @@ TEST(NgHttp2AdapterTest, ServerSendsInvalidTrailers) { EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); - const ssize_t result = adapter->ProcessBytes(frames); + const int64_t result = adapter->ProcessBytes(frames); EXPECT_EQ(frames.size(), result); const absl::string_view kBody = "This is an example response body."; @@ -1591,7 +2156,7 @@ TEST(NgHttp2AdapterTest, ServerSendsInvalidTrailers) { 1, ToHeaders({{":status", "200"}, {"x-comment", "Sure, sounds good."}}), std::move(body1)); EXPECT_EQ(submit_result, 0); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); @@ -1606,13 +2171,13 @@ TEST(NgHttp2AdapterTest, ServerSendsInvalidTrailers) { spdy::SpdyFrameType::DATA})); EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody)); visitor.Clear(); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); // The body source has been exhausted by the call to Send() above. int trailer_result = adapter->SubmitTrailer(1, ToHeaders({{":final-status", "a-ok"}})); ASSERT_EQ(trailer_result, 0); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x5)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0)); diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.cc index 4a2221ea2ee..648a5219d73 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.cc @@ -4,12 +4,13 @@ #include <cstring> #include "absl/strings/string_view.h" +#include "http2/adapter/data_source.h" #include "http2/adapter/http2_protocol.h" #include "http2/adapter/http2_visitor_interface.h" #include "http2/adapter/nghttp2_data_provider.h" #include "http2/adapter/nghttp2_util.h" -#include "third_party/nghttp2/nghttp2.h" #include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" +#include "common/platform/api/quiche_bug_tracker.h" #include "common/platform/api/quiche_logging.h" #include "common/quiche_endian.h" @@ -21,7 +22,7 @@ ssize_t OnReadyToSend(nghttp2_session* /* session */, const uint8_t* data, size_t length, int flags, void* user_data) { QUICHE_CHECK_NE(user_data, nullptr); auto* visitor = static_cast<Http2VisitorInterface*>(user_data); - const ssize_t result = visitor->OnReadyToSend(ToStringView(data, length)); + const int64_t result = visitor->OnReadyToSend(ToStringView(data, length)); QUICHE_VLOG(1) << "OnReadyToSend(length=" << length << ", flags=" << flags << ") returning " << result; if (result > 0) { @@ -38,14 +39,17 @@ int OnBeginFrame(nghttp2_session* /* session */, void* user_data) { QUICHE_CHECK_NE(user_data, nullptr); auto* visitor = static_cast<Http2VisitorInterface*>(user_data); - visitor->OnFrameHeader(header->stream_id, header->length, header->type, - header->flags); + bool result = visitor->OnFrameHeader(header->stream_id, header->length, + header->type, header->flags); + if (!result) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } if (header->type == NGHTTP2_DATA) { - visitor->OnBeginDataForStream(header->stream_id, header->length); + result = visitor->OnBeginDataForStream(header->stream_id, header->length); } else if (header->type == kMetadataFrameType) { visitor->OnBeginMetadataForStream(header->stream_id, header->length); } - return 0; + return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE; } int OnFrameReceived(nghttp2_session* /* session */, @@ -66,7 +70,10 @@ int OnFrameReceived(nghttp2_session* /* session */, break; case NGHTTP2_HEADERS: { if (frame->hd.flags & NGHTTP2_FLAG_END_HEADERS) { - visitor->OnEndHeadersForStream(stream_id); + const bool result = visitor->OnEndHeadersForStream(stream_id); + if (!result) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } } if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { visitor->OnEndStream(stream_id); @@ -94,8 +101,7 @@ int OnFrameReceived(nghttp2_session* /* session */, nghttp2_settings_entry entry = frame->settings.iv[i]; // The nghttp2_settings_entry uses int32_t for the ID; we must cast. visitor->OnSetting(Http2Setting{ - .id = static_cast<Http2SettingsId>(entry.settings_id), - .value = entry.value}); + static_cast<Http2SettingsId>(entry.settings_id), entry.value}); } visitor->OnSettingsEnd(); } @@ -121,9 +127,12 @@ int OnFrameReceived(nghttp2_session* /* session */, absl::string_view opaque_data( reinterpret_cast<const char*>(frame->goaway.opaque_data), frame->goaway.opaque_data_len); - visitor->OnGoAway(frame->goaway.last_stream_id, - ToHttp2ErrorCode(frame->goaway.error_code), - opaque_data); + const bool result = visitor->OnGoAway( + frame->goaway.last_stream_id, + ToHttp2ErrorCode(frame->goaway.error_code), opaque_data); + if (!result) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } break; } case NGHTTP2_WINDOW_UPDATE: { @@ -210,9 +219,9 @@ int OnDataChunk(nghttp2_session* /* session */, uint8_t /*flags*/, void* user_data) { QUICHE_CHECK_NE(user_data, nullptr); auto* visitor = static_cast<Http2VisitorInterface*>(user_data); - visitor->OnDataForStream( + const bool result = visitor->OnDataForStream( stream_id, absl::string_view(reinterpret_cast<const char*>(data), len)); - return 0; + return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE; } int OnStreamClosed(nghttp2_session* /* session */, @@ -235,8 +244,9 @@ int OnExtensionChunkReceived(nghttp2_session* /*session*/, << static_cast<int>(hd->type); return NGHTTP2_ERR_CANCEL; } - visitor->OnMetadataForStream(hd->stream_id, ToStringView(data, len)); - return 0; + const bool result = + visitor->OnMetadataForStream(hd->stream_id, ToStringView(data, len)); + return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE; } int OnUnpackExtensionCallback(nghttp2_session* /*session*/, void** /*payload*/, @@ -256,11 +266,21 @@ ssize_t OnPackExtensionCallback(nghttp2_session* /*session*/, uint8_t* buf, size_t len, const nghttp2_frame* frame, void* user_data) { QUICHE_CHECK_NE(user_data, nullptr); - auto* visitor = static_cast<Http2VisitorInterface*>(user_data); - ssize_t written = 0; - visitor->OnReadyToSendMetadataForStream( - frame->hd.stream_id, reinterpret_cast<char*>(buf), len, &written); - return written; + auto* source = static_cast<MetadataSource*>(frame->ext.payload); + if (source == nullptr) { + QUICHE_BUG(payload_is_nullptr) << "Extension frame payload for stream " + << frame->hd.stream_id << " is null!"; + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + const std::pair<int64_t, bool> result = source->Pack(buf, len); + if (result.first < 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + const bool end_metadata_flag = (frame->hd.flags & kMetadataEndFlag); + QUICHE_LOG_IF(DFATAL, result.second != end_metadata_flag) + << "Metadata ends: " << result.second + << " has kMetadataEndFlag: " << end_metadata_flag; + return result.first; } int OnError(nghttp2_session* /*session*/, int /*lib_error_code*/, diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.h b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.h index 696b6847509..75640ed7442 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.h @@ -1,6 +1,8 @@ #ifndef QUICHE_HTTP2_ADAPTER_NGHTTP2_CALLBACKS_H_ #define QUICHE_HTTP2_ADAPTER_NGHTTP2_CALLBACKS_H_ +#include <cstdint> + #include "http2/adapter/http2_protocol.h" #include "http2/adapter/nghttp2_util.h" #include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_data_provider.h b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_data_provider.h index 241bab91cdd..9e9119f2e93 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_data_provider.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_data_provider.h @@ -1,6 +1,9 @@ #ifndef QUICHE_HTTP2_ADAPTER_NGHTTP2_DATA_PROVIDER_H_ #define QUICHE_HTTP2_ADAPTER_NGHTTP2_DATA_PROVIDER_H_ +#include <cstdint> +#include <memory> + #include "http2/adapter/data_source.h" #include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.cc index 9868958bc67..13867bd978d 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.cc @@ -10,7 +10,7 @@ NgHttp2Session::NgHttp2Session(Perspective perspective, const nghttp2_option* options, void* userdata) : session_(MakeSessionPtr(nullptr)), perspective_(perspective) { nghttp2_session* session; - switch (perspective) { + switch (perspective_) { case Perspective::kClient: nghttp2_session_client_new2(&session, callbacks.get(), userdata, options); break; @@ -30,7 +30,7 @@ NgHttp2Session::~NgHttp2Session() { << " or pending writes: " << pending_writes; } -ssize_t NgHttp2Session::ProcessBytes(absl::string_view bytes) { +int64_t NgHttp2Session::ProcessBytes(absl::string_view bytes) { return nghttp2_session_mem_recv( session_.get(), reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size()); diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.h b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.h index 4339875588c..f84713f095c 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session.h @@ -1,6 +1,8 @@ #ifndef QUICHE_HTTP2_ADAPTER_NGHTTP2_SESSION_H_ #define QUICHE_HTTP2_ADAPTER_NGHTTP2_SESSION_H_ +#include <cstdint> + #include "http2/adapter/http2_session.h" #include "http2/adapter/nghttp2_util.h" #include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" @@ -18,7 +20,7 @@ class QUICHE_EXPORT_PRIVATE NgHttp2Session : public Http2Session { const nghttp2_option* options, void* userdata); ~NgHttp2Session() override; - ssize_t ProcessBytes(absl::string_view bytes) override; + int64_t ProcessBytes(absl::string_view bytes) override; int Consume(Http2StreamId stream_id, size_t num_bytes) override; diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session_test.cc index 487843bcc52..22103942d83 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session_test.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_session_test.cc @@ -6,6 +6,7 @@ #include "http2/adapter/test_frame_sequence.h" #include "http2/adapter/test_utils.h" #include "common/platform/api/quiche_test.h" +#include "common/platform/api/quiche_test_helpers.h" namespace http2 { namespace adapter { @@ -77,7 +78,7 @@ TEST_F(NgHttp2SessionTest, ClientHandlesFrames) { EXPECT_CALL(visitor_, OnFrameHeader(0, 4, WINDOW_UPDATE, 0)); EXPECT_CALL(visitor_, OnWindowUpdate(0, 1000)); - const ssize_t initial_result = session.ProcessBytes(initial_frames); + const int64_t initial_result = session.ProcessBytes(initial_frames); EXPECT_EQ(initial_frames.size(), initial_result); EXPECT_EQ(session.GetRemoteWindowSize(), @@ -176,7 +177,7 @@ TEST_F(NgHttp2SessionTest, ClientHandlesFrames) { EXPECT_CALL(visitor_, OnFrameHeader(0, 19, GOAWAY, 0)); EXPECT_CALL(visitor_, OnGoAway(5, Http2ErrorCode::ENHANCE_YOUR_CALM, "calm down!!")); - const ssize_t stream_result = session.ProcessBytes(stream_frames); + const int64_t stream_result = session.ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), stream_result); // Even though the client recieved a GOAWAY, streams 1 and 5 are still active. @@ -275,7 +276,7 @@ TEST_F(NgHttp2SessionTest, ServerHandlesFrames) { EXPECT_CALL(visitor_, OnFrameHeader(0, 8, PING, 0)); EXPECT_CALL(visitor_, OnPing(47, false)); - const ssize_t result = session.ProcessBytes(frames); + const int64_t result = session.ProcessBytes(frames); EXPECT_EQ(frames.size(), result); EXPECT_EQ(session.GetRemoteWindowSize(), @@ -298,6 +299,23 @@ TEST_F(NgHttp2SessionTest, ServerHandlesFrames) { spdy::SpdyFrameType::PING})); } +// Verifies that a null payload is caught by the OnPackExtensionCallback +// implementation. +TEST_F(NgHttp2SessionTest, NullPayload) { + NgHttp2Session session(Perspective::kClient, CreateCallbacks(), options_, + &visitor_); + + void* payload = nullptr; + const int result = nghttp2_submit_extension( + session.raw_ptr(), kMetadataFrameType, 0, 1, payload); + ASSERT_EQ(0, result); + EXPECT_TRUE(session.want_write()); + int send_result = -1; + EXPECT_QUICHE_BUG(send_result = nghttp2_session_send(session.raw_ptr()), + "Extension frame payload for stream 1 is null!"); + EXPECT_EQ(NGHTTP2_ERR_CALLBACK_FAILURE, send_result); +} + } // namespace } // namespace test } // namespace adapter diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_test_utils.h b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_test_utils.h index 9b772ffda63..abb11dde076 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_test_utils.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_test_utils.h @@ -1,6 +1,7 @@ #ifndef QUICHE_HTTP2_ADAPTER_NGHTTP2_TEST_UTILS_H_ #define QUICHE_HTTP2_ADAPTER_NGHTTP2_TEST_UTILS_H_ +#include <cstdint> #include <vector> #include "absl/strings/string_view.h" diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.cc b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.cc index efb811fc35c..4bfced670ee 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.cc @@ -131,11 +131,10 @@ class Nghttp2DataFrameSource : public DataFrameSource { send_data_(std::move(send_data)), user_data_(user_data) {} - std::pair<ssize_t, bool> SelectPayloadLength(size_t max_length) override { + std::pair<int64_t, bool> SelectPayloadLength(size_t max_length) override { const int32_t stream_id = 0; uint32_t data_flags = 0; - QUICHE_LOG(INFO) << "Invoking read callback"; - ssize_t result = provider_.read_callback( + int64_t result = provider_.read_callback( nullptr /* session */, stream_id, nullptr /* buf */, max_length, &data_flags, &provider_.source, nullptr /* user_data */); if (result == NGHTTP2_ERR_DEFERRED) { diff --git a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.cc b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.cc index 27f011ec62f..19841731a0e 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.cc @@ -34,7 +34,7 @@ bool OgHttp2Adapter::IsServerSession() const { return session_->IsServerSession(); } -ssize_t OgHttp2Adapter::ProcessBytes(absl::string_view bytes) { +int64_t OgHttp2Adapter::ProcessBytes(absl::string_view bytes) { return session_->ProcessBytes(bytes); } @@ -76,7 +76,10 @@ void OgHttp2Adapter::SubmitWindowUpdate(Http2StreamId stream_id, } void OgHttp2Adapter::SubmitMetadata(Http2StreamId stream_id, + size_t /* max_frame_size */, std::unique_ptr<MetadataSource> source) { + // Not necessary to pass max_frame_size along, since OgHttp2Session tracks the + // peer's advertised max frame size. session_->SubmitMetadata(stream_id, std::move(source)); } @@ -155,10 +158,6 @@ bool OgHttp2Adapter::ResumeStream(Http2StreamId stream_id) { return session_->ResumeStream(stream_id); } -const Http2Session& OgHttp2Adapter::session() const { - return *session_; -} - OgHttp2Adapter::OgHttp2Adapter(Http2VisitorInterface& visitor, Options options) : Http2Adapter(visitor), session_(absl::make_unique<OgHttp2Session>(visitor, std::move(options))) { diff --git a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.h b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.h index 9c7bad3c575..0aae5ab1990 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.h @@ -1,6 +1,7 @@ #ifndef QUICHE_HTTP2_ADAPTER_OGHTTP2_ADAPTER_H_ #define QUICHE_HTTP2_ADAPTER_OGHTTP2_ADAPTER_H_ +#include <cstdint> #include <memory> #include "http2/adapter/http2_adapter.h" @@ -17,11 +18,13 @@ class QUICHE_EXPORT_PRIVATE OgHttp2Adapter : public Http2Adapter { static std::unique_ptr<OgHttp2Adapter> Create(Http2VisitorInterface& visitor, Options options); - ~OgHttp2Adapter(); + ~OgHttp2Adapter() override; // From Http2Adapter. bool IsServerSession() const override; - ssize_t ProcessBytes(absl::string_view bytes) override; + bool want_read() const override { return session_->want_read(); } + bool want_write() const override { return session_->want_write(); } + int64_t ProcessBytes(absl::string_view bytes) override; void SubmitSettings(absl::Span<const Http2Setting> settings) override; void SubmitPriorityForStream(Http2StreamId stream_id, Http2StreamId parent_stream_id, @@ -34,7 +37,7 @@ class QUICHE_EXPORT_PRIVATE OgHttp2Adapter : public Http2Adapter { absl::string_view opaque_data) override; void SubmitWindowUpdate(Http2StreamId stream_id, int window_increment) override; - void SubmitMetadata(Http2StreamId stream_id, + void SubmitMetadata(Http2StreamId stream_id, size_t max_frame_size, std::unique_ptr<MetadataSource> source) override; int Send() override; int GetSendWindowSize() const override; @@ -61,8 +64,6 @@ class QUICHE_EXPORT_PRIVATE OgHttp2Adapter : public Http2Adapter { void* GetStreamUserData(Http2StreamId stream_id) override; bool ResumeStream(Http2StreamId stream_id) override; - const Http2Session& session() const; - private: OgHttp2Adapter(Http2VisitorInterface& visitor, Options options); diff --git a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter_test.cc index 03b45df6aa9..413db77bfff 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter_test.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter_test.cc @@ -1,6 +1,10 @@ #include "http2/adapter/oghttp2_adapter.h" +#include <string> + #include "absl/strings/str_join.h" +#include "http2/adapter/http2_protocol.h" +#include "http2/adapter/http2_visitor_interface.h" #include "http2/adapter/mock_http2_visitor.h" #include "http2/adapter/oghttp2_util.h" #include "http2/adapter/test_frame_sequence.h" @@ -124,13 +128,13 @@ TEST(OgHttp2AdapterClientTest, ClientHandlesTrailers) { EXPECT_CALL(visitor, OnEndStream(1)); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR)); - const size_t stream_result = adapter->ProcessBytes(stream_frames); - EXPECT_EQ(stream_frames.size(), stream_result); + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); @@ -208,13 +212,91 @@ TEST(OgHttp2AdapterClientTest, ClientHandlesMetadata) { EXPECT_CALL(visitor, OnEndStream(1)); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR)); - const size_t stream_result = adapter->ProcessBytes(stream_frames); - EXPECT_EQ(stream_frames.size(), stream_result); + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); + result = adapter->Send(); + EXPECT_EQ(0, result); + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); +} + +TEST(OgHttp2AdapterClientTest, ClientHandlesMetadataWithError) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + testing::InSequence s; + + const std::vector<const Header> headers1 = + ToHeaders({{":method", "GET"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}); + + const char* kSentinel1 = "arbitrary pointer 1"; + const int32_t stream_id1 = + adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1)); + ASSERT_GT(stream_id1, 0); + QUICHE_LOG(INFO) << "Created stream: " << stream_id1; + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0)); + + int result = adapter->Send(); + EXPECT_EQ(0, result); + visitor.Clear(); + + const std::string stream_frames = + TestFrameSequence() + .ServerPreface() + .Metadata(0, "Example connection metadata") + .Headers(1, + {{":status", "200"}, + {"server", "my-fake-server"}, + {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, + /*fin=*/false) + .Metadata(1, "Example stream metadata") + .Data(1, "This is the response body.", true) + .Serialize(); + + // Server preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + + EXPECT_CALL(visitor, OnFrameHeader(0, _, kMetadataFrameType, 4)); + EXPECT_CALL(visitor, OnBeginMetadataForStream(0, _)); + EXPECT_CALL(visitor, OnMetadataForStream(0, _)); + EXPECT_CALL(visitor, OnMetadataEndForStream(0)); + EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200")); + EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server")); + EXPECT_CALL(visitor, + OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT")); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + EXPECT_CALL(visitor, OnFrameHeader(1, _, kMetadataFrameType, 4)); + EXPECT_CALL(visitor, OnBeginMetadataForStream(1, _)); + EXPECT_CALL(visitor, OnMetadataForStream(1, _)) + .WillOnce(testing::Return(false)); + // Remaining frames are not processed due to the error. + EXPECT_CALL(visitor, OnConnectionError()); + + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + // Negative integer returned to indicate an error. + EXPECT_LT(stream_result, 0); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); + + EXPECT_FALSE(adapter->want_read()); + EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); @@ -284,8 +366,8 @@ TEST(OgHttp2AdapterClientTest, ClientRstStreamWhileHandlingHeaders) { EXPECT_CALL(visitor, OnBeginDataForStream(1, _)); EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body.")); - const size_t stream_result = adapter->ProcessBytes(stream_frames); - EXPECT_EQ(stream_frames.size(), stream_result); + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); @@ -295,7 +377,7 @@ TEST(OgHttp2AdapterClientTest, ClientRstStreamWhileHandlingHeaders) { static_cast<int>(Http2ErrorCode::REFUSED_STREAM))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR)); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, @@ -366,13 +448,13 @@ TEST(OgHttp2AdapterClientTest, ClientConnectionErrorWhileHandlingHeaders) { EXPECT_CALL(visitor, OnBeginDataForStream(1, _)); EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body.")); - const size_t stream_result = adapter->ProcessBytes(stream_frames); - EXPECT_EQ(stream_frames.size(), stream_result); + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + EXPECT_LT(stream_result, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); @@ -438,13 +520,13 @@ TEST(OgHttp2AdapterClientTest, ClientRejectsHeaders) { EXPECT_CALL(visitor, OnBeginDataForStream(1, _)); EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body.")); - const size_t stream_result = adapter->ProcessBytes(stream_frames); - EXPECT_EQ(stream_result, stream_frames.size()); + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + EXPECT_LT(stream_result, 0); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); @@ -518,8 +600,8 @@ TEST(OgHttp2AdapterClientTest, DISABLED_ClientHandlesInvalidTrailers) { // Bad status trailer will cause a PROTOCOL_ERROR. The header is never // delivered in an OnHeaderForStream callback. - const size_t stream_result = adapter->ProcessBytes(stream_frames); - EXPECT_EQ(stream_frames.size(), stream_result); + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result)); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); @@ -527,18 +609,235 @@ TEST(OgHttp2AdapterClientTest, DISABLED_ClientHandlesInvalidTrailers) { EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, stream_id1, 4, 0x0, 1)); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::PROTOCOL_ERROR)); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, spdy::SpdyFrameType::RST_STREAM})); } +TEST(OgHttp2AdapterClientTest, ClientFailsOnGoAway) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + testing::InSequence s; + + const std::vector<const Header> headers1 = + ToHeaders({{":method", "GET"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}); + + const char* kSentinel1 = "arbitrary pointer 1"; + const int32_t stream_id1 = + adapter->SubmitRequest(headers1, nullptr, const_cast<char*>(kSentinel1)); + ASSERT_GT(stream_id1, 0); + QUICHE_LOG(INFO) << "Created stream: " << stream_id1; + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0)); + + int result = adapter->Send(); + EXPECT_EQ(0, result); + absl::string_view data = visitor.data(); + EXPECT_THAT(data, testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); + data.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); + EXPECT_THAT(data, EqualsFrames({spdy::SpdyFrameType::SETTINGS, + spdy::SpdyFrameType::HEADERS})); + visitor.Clear(); + + const std::string stream_frames = + TestFrameSequence() + .ServerPreface() + .Headers(1, + {{":status", "200"}, + {"server", "my-fake-server"}, + {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, + /*fin=*/false) + .GoAway(1, Http2ErrorCode::INTERNAL_ERROR, "indigestion") + .Data(1, "This is the response body.") + .Serialize(); + + // Server preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + + EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200")); + EXPECT_CALL(visitor, OnHeaderForStream(1, "server", "my-fake-server")); + EXPECT_CALL(visitor, + OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT")); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + EXPECT_CALL(visitor, OnFrameHeader(0, _, GOAWAY, 0)); + // TODO(birenroy): Pass the GOAWAY opaque data through the oghttp2 stack. + EXPECT_CALL(visitor, OnGoAway(1, Http2ErrorCode::INTERNAL_ERROR, "")) + .WillOnce(testing::Return(false)); + EXPECT_CALL(visitor, OnConnectionError()); + + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + EXPECT_LT(stream_result, 0); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); + + EXPECT_TRUE(adapter->want_write()); + result = adapter->Send(); + EXPECT_EQ(0, result); + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS})); +} + +TEST(OgHttp2AdapterClientTest, ClientObeysMaxConcurrentStreams) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + EXPECT_FALSE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); + + // Even though the user has not queued any frames for the session, it should + // still send the connection preface. + int result = adapter->Send(); + EXPECT_EQ(0, result); + absl::string_view serialized = visitor.data(); + EXPECT_THAT(serialized, + testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix)); + serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix)); + // Initial SETTINGS. + EXPECT_THAT(serialized, EqualsFrames({SpdyFrameType::SETTINGS})); + visitor.Clear(); + + const std::string initial_frames = + TestFrameSequence() + .ServerPreface({{.id = MAX_CONCURRENT_STREAMS, .value = 1}}) + .Serialize(); + testing::InSequence s; + + // Server preface (SETTINGS with MAX_CONCURRENT_STREAMS) + EXPECT_CALL(visitor, OnFrameHeader(0, 6, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSetting); + // TODO(diannahu): Remove this duplicate call with a separate + // ExtensionVisitorInterface implementation. + EXPECT_CALL(visitor, OnSetting); + EXPECT_CALL(visitor, OnSettingsEnd()); + + const int64_t initial_result = adapter->ProcessBytes(initial_frames); + EXPECT_EQ(initial_frames.size(), static_cast<size_t>(initial_result)); + + // Session will want to write a SETTINGS ack. + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0)); + + result = adapter->Send(); + EXPECT_EQ(0, result); + EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS})); + visitor.Clear(); + + const std::string kBody = "This is an example request body."; + auto body1 = absl::make_unique<TestDataFrameSource>(visitor, true); + body1->AppendPayload(kBody); + body1->EndData(); + const int stream_id = + adapter->SubmitRequest(ToHeaders({{":method", "POST"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}), + std::move(body1), nullptr); + EXPECT_GT(stream_id, 0); + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id, _, 0x4)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id, _, 0x4, 0)); + EXPECT_CALL(visitor, OnFrameSent(DATA, stream_id, _, 0x1, 0)); + + result = adapter->Send(); + EXPECT_EQ(0, result); + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS, + spdy::SpdyFrameType::DATA})); + EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody)); + visitor.Clear(); + EXPECT_FALSE(adapter->want_write()); + + const int next_stream_id = + adapter->SubmitRequest(ToHeaders({{":method", "POST"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/two"}}), + nullptr, nullptr); + + // A new pending stream is created, but because of MAX_CONCURRENT_STREAMS, the + // session should not want to write it at the moment. + EXPECT_GT(next_stream_id, stream_id); + EXPECT_FALSE(adapter->want_write()); + + const std::string stream_frames = + TestFrameSequence() + .Headers(stream_id, + {{":status", "200"}, + {"server", "my-fake-server"}, + {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}}, + /*fin=*/false) + .Data(stream_id, "This is the response body.", /*fin=*/true) + .Serialize(); + + EXPECT_CALL(visitor, OnFrameHeader(stream_id, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(stream_id)); + EXPECT_CALL(visitor, OnHeaderForStream(stream_id, ":status", "200")); + EXPECT_CALL(visitor, + OnHeaderForStream(stream_id, "server", "my-fake-server")); + EXPECT_CALL(visitor, OnHeaderForStream(stream_id, "date", + "Tue, 6 Apr 2021 12:54:01 GMT")); + EXPECT_CALL(visitor, OnEndHeadersForStream(stream_id)); + EXPECT_CALL(visitor, OnFrameHeader(stream_id, 26, DATA, 0x1)); + EXPECT_CALL(visitor, OnBeginDataForStream(stream_id, 26)); + EXPECT_CALL(visitor, + OnDataForStream(stream_id, "This is the response body.")); + EXPECT_CALL(visitor, OnEndStream(stream_id)); + EXPECT_CALL(visitor, OnCloseStream(stream_id, Http2ErrorCode::NO_ERROR)); + + // The first stream should close, which should make the session want to write + // the next stream. + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result)); + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, next_stream_id, _, 0x5)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, next_stream_id, _, 0x5, 0)); + + result = adapter->Send(); + EXPECT_EQ(0, result); + + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS})); + visitor.Clear(); + EXPECT_FALSE(adapter->want_write()); +} + +TEST(OgHttp2AdapterClientTest, FailureSendingConnectionPreface) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options{.perspective = Perspective::kClient}; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + visitor.set_has_write_error(); + EXPECT_CALL(visitor, OnConnectionError); + + int result = adapter->Send(); + EXPECT_EQ(result, Http2VisitorInterface::kSendError); +} + TEST_F(OgHttp2AdapterTest, SubmitMetadata) { auto source = absl::make_unique<TestMetadataSource>(ToHeaderBlock(ToHeaders( {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}}))); - adapter_->SubmitMetadata(1, std::move(source)); - EXPECT_TRUE(adapter_->session().want_write()); + adapter_->SubmitMetadata(1, 16384u, std::move(source)); + EXPECT_TRUE(adapter_->want_write()); EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(http2_visitor_, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); @@ -551,14 +850,46 @@ TEST_F(OgHttp2AdapterTest, SubmitMetadata) { http2_visitor_.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, static_cast<spdy::SpdyFrameType>(kMetadataFrameType)})); - EXPECT_FALSE(adapter_->session().want_write()); + EXPECT_FALSE(adapter_->want_write()); +} + +TEST_F(OgHttp2AdapterTest, SubmitMetadataMultipleFrames) { + const auto kLargeValue = std::string(63 * 1024, 'a'); + auto source = absl::make_unique<TestMetadataSource>( + ToHeaderBlock(ToHeaders({{"large-value", kLargeValue}}))); + adapter_->SubmitMetadata(1, 16384u, std::move(source)); + EXPECT_TRUE(adapter_->want_write()); + + testing::InSequence seq; + EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); + EXPECT_CALL(http2_visitor_, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); + EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0)); + EXPECT_CALL(http2_visitor_, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0)); + EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0)); + EXPECT_CALL(http2_visitor_, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0)); + EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x0)); + EXPECT_CALL(http2_visitor_, OnFrameSent(kMetadataFrameType, 1, _, 0x0, 0)); + EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(kMetadataFrameType, 1, _, 0x4)); + EXPECT_CALL(http2_visitor_, OnFrameSent(kMetadataFrameType, 1, _, 0x4, 0)); + + int result = adapter_->Send(); + EXPECT_EQ(0, result); + absl::string_view serialized = http2_visitor_.data(); + EXPECT_THAT( + serialized, + EqualsFrames({spdy::SpdyFrameType::SETTINGS, + static_cast<spdy::SpdyFrameType>(kMetadataFrameType), + static_cast<spdy::SpdyFrameType>(kMetadataFrameType), + static_cast<spdy::SpdyFrameType>(kMetadataFrameType), + static_cast<spdy::SpdyFrameType>(kMetadataFrameType)})); + EXPECT_FALSE(adapter_->want_write()); } TEST_F(OgHttp2AdapterTest, SubmitConnectionMetadata) { auto source = absl::make_unique<TestMetadataSource>(ToHeaderBlock(ToHeaders( {{"query-cost", "is too darn high"}, {"secret-sauce", "hollandaise"}}))); - adapter_->SubmitMetadata(0, std::move(source)); - EXPECT_TRUE(adapter_->session().want_write()); + adapter_->SubmitMetadata(0, 16384u, std::move(source)); + EXPECT_TRUE(adapter_->want_write()); EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(http2_visitor_, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); @@ -571,7 +902,7 @@ TEST_F(OgHttp2AdapterTest, SubmitConnectionMetadata) { http2_visitor_.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, static_cast<spdy::SpdyFrameType>(kMetadataFrameType)})); - EXPECT_FALSE(adapter_->session().want_write()); + EXPECT_FALSE(adapter_->want_write()); } TEST_F(OgHttp2AdapterTest, GetSendWindowSize) { @@ -585,19 +916,19 @@ TEST_F(OgHttp2AdapterTest, MarkDataConsumedForStream) { } TEST_F(OgHttp2AdapterTest, TestSerialize) { - EXPECT_TRUE(adapter_->session().want_read()); - EXPECT_FALSE(adapter_->session().want_write()); + EXPECT_TRUE(adapter_->want_read()); + EXPECT_FALSE(adapter_->want_write()); adapter_->SubmitSettings( {{HEADER_TABLE_SIZE, 128}, {MAX_FRAME_SIZE, 128 << 10}}); - EXPECT_TRUE(adapter_->session().want_write()); + EXPECT_TRUE(adapter_->want_write()); adapter_->SubmitPriorityForStream(3, 1, 255, true); adapter_->SubmitRst(3, Http2ErrorCode::CANCEL); adapter_->SubmitPing(42); adapter_->SubmitGoAway(13, Http2ErrorCode::NO_ERROR, ""); adapter_->SubmitWindowUpdate(3, 127); - EXPECT_TRUE(adapter_->session().want_write()); + EXPECT_TRUE(adapter_->want_write()); EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(http2_visitor_, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); @@ -620,34 +951,34 @@ TEST_F(OgHttp2AdapterTest, TestSerialize) { EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::PRIORITY, SpdyFrameType::RST_STREAM, SpdyFrameType::PING, SpdyFrameType::GOAWAY, SpdyFrameType::WINDOW_UPDATE})); - EXPECT_FALSE(adapter_->session().want_write()); + EXPECT_FALSE(adapter_->want_write()); } TEST_F(OgHttp2AdapterTest, TestPartialSerialize) { - EXPECT_FALSE(adapter_->session().want_write()); + EXPECT_FALSE(adapter_->want_write()); adapter_->SubmitSettings( {{HEADER_TABLE_SIZE, 128}, {MAX_FRAME_SIZE, 128 << 10}}); adapter_->SubmitGoAway(13, Http2ErrorCode::NO_ERROR, "And don't come back!"); adapter_->SubmitPing(42); - EXPECT_TRUE(adapter_->session().want_write()); + EXPECT_TRUE(adapter_->want_write()); http2_visitor_.set_send_limit(20); EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(http2_visitor_, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); int result = adapter_->Send(); EXPECT_EQ(0, result); - EXPECT_TRUE(adapter_->session().want_write()); + EXPECT_TRUE(adapter_->want_write()); EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); EXPECT_CALL(http2_visitor_, OnFrameSent(GOAWAY, 0, _, 0x0, 0)); result = adapter_->Send(); EXPECT_EQ(0, result); - EXPECT_TRUE(adapter_->session().want_write()); + EXPECT_TRUE(adapter_->want_write()); EXPECT_CALL(http2_visitor_, OnBeforeFrameSent(PING, 0, _, 0x0)); EXPECT_CALL(http2_visitor_, OnFrameSent(PING, 0, _, 0x0, 0)); result = adapter_->Send(); EXPECT_EQ(0, result); - EXPECT_FALSE(adapter_->session().want_write()); + EXPECT_FALSE(adapter_->want_write()); EXPECT_THAT(http2_visitor_.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY, SpdyFrameType::PING})); @@ -657,7 +988,7 @@ TEST(OgHttp2AdapterServerTest, ClientSendsContinuation) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() .ClientPreface() @@ -686,15 +1017,15 @@ TEST(OgHttp2AdapterServerTest, ClientSendsContinuation) { EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); - const size_t result = adapter->ProcessBytes(frames); - EXPECT_EQ(frames.size(), result); + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_EQ(frames.size(), static_cast<size_t>(result)); } TEST(OgHttp2AdapterServerTest, ClientSendsMetadataWithContinuation) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() @@ -744,8 +1075,8 @@ TEST(OgHttp2AdapterServerTest, ClientSendsMetadataWithContinuation) { EXPECT_CALL(visitor, OnMetadataForStream(1, _)); EXPECT_CALL(visitor, OnMetadataEndForStream(1)); - const size_t result = adapter->ProcessBytes(frames); - EXPECT_EQ(frames.size(), result); + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_EQ(frames.size(), static_cast<size_t>(result)); EXPECT_EQ(TestFrameSequence::MetadataBlockForPayload( "Example connection metadata in multiple frames"), absl::StrJoin(visitor.GetMetadata(0), "")); @@ -758,7 +1089,7 @@ TEST(OgHttp2AdapterServerTest, ServerSendsInvalidTrailers) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; auto adapter = OgHttp2Adapter::Create(visitor, options); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); const std::string frames = TestFrameSequence() .ClientPreface() @@ -785,8 +1116,8 @@ TEST(OgHttp2AdapterServerTest, ServerSendsInvalidTrailers) { EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); - const size_t result = adapter->ProcessBytes(frames); - EXPECT_EQ(frames.size(), result); + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_EQ(frames.size(), static_cast<size_t>(result)); const absl::string_view kBody = "This is an example response body."; @@ -799,7 +1130,7 @@ TEST(OgHttp2AdapterServerTest, ServerSendsInvalidTrailers) { 1, ToHeaders({{":status", "200"}, {"x-comment", "Sure, sounds good."}}), std::move(body1)); EXPECT_EQ(submit_result, 0); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); @@ -817,14 +1148,14 @@ TEST(OgHttp2AdapterServerTest, ServerSendsInvalidTrailers) { spdy::SpdyFrameType::HEADERS, spdy::SpdyFrameType::DATA})); EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody)); visitor.Clear(); - EXPECT_FALSE(adapter->session().want_write()); + EXPECT_FALSE(adapter->want_write()); // The body source has been exhausted by the call to Send() above. EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR)); int trailer_result = adapter->SubmitTrailer(1, ToHeaders({{":final-status", "a-ok"}})); ASSERT_EQ(trailer_result, 0); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x5)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x5, 0)); @@ -875,10 +1206,10 @@ TEST(OgHttp2AdapterServerTest, ServerErrorWhileHandlingHeaders) { EXPECT_CALL(visitor, OnFrameHeader(0, 4, WINDOW_UPDATE, 0)); EXPECT_CALL(visitor, OnWindowUpdate(0, 2000)); - const size_t result = adapter->ProcessBytes(frames); - EXPECT_EQ(frames.size(), result); + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_EQ(frames.size(), static_cast<size_t>(result)); - EXPECT_TRUE(adapter->session().want_write()); + EXPECT_TRUE(adapter->want_write()); EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); @@ -899,6 +1230,227 @@ TEST(OgHttp2AdapterServerTest, ServerErrorWhileHandlingHeaders) { spdy::SpdyFrameType::RST_STREAM})); } +TEST(OgHttp2AdapterServerTest, ServerErrorAfterHandlingHeaders) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + const std::string frames = TestFrameSequence() + .ClientPreface() + .Headers(1, + {{":method", "POST"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}, + /*fin=*/false) + .WindowUpdate(1, 2000) + .Data(1, "This is the request body.") + .WindowUpdate(0, 2000) + .Serialize(); + testing::InSequence s; + + // Client preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + + EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "POST")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one")); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)) + .WillOnce(testing::Return(false)); + EXPECT_CALL(visitor, OnConnectionError()); + + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_LT(result, 0); + + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); + + int send_result = adapter->Send(); + // Some bytes should have been serialized. + EXPECT_EQ(0, send_result); + // SETTINGS and SETTINGS ack + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, + spdy::SpdyFrameType::SETTINGS})); +} + +// Exercises the case when a visitor chooses to reject a frame based solely on +// the frame header, which is a fatal error for the connection. +TEST(OgHttp2AdapterServerTest, ServerRejectsFrameHeader) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + const std::string frames = TestFrameSequence() + .ClientPreface() + .Ping(64) + .Headers(1, + {{":method", "POST"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}, + /*fin=*/false) + .WindowUpdate(1, 2000) + .Data(1, "This is the request body.") + .WindowUpdate(0, 2000) + .Serialize(); + testing::InSequence s; + + // Client preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + + EXPECT_CALL(visitor, OnFrameHeader(0, 8, PING, 0)) + .WillOnce(testing::Return(false)); + EXPECT_CALL(visitor, OnConnectionError()); + + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_LT(result, 0); + + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); + + int send_result = adapter->Send(); + // Some bytes should have been serialized. + EXPECT_EQ(0, send_result); + // SETTINGS and SETTINGS ack + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, + spdy::SpdyFrameType::SETTINGS})); +} + +TEST(OgHttp2AdapterServerTest, ServerRejectsBeginningOfData) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + const std::string frames = TestFrameSequence() + .ClientPreface() + .Headers(1, + {{":method", "POST"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}, + /*fin=*/false) + .Data(1, "This is the request body.") + .Headers(3, + {{":method", "GET"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/two"}}, + /*fin=*/true) + .RstStream(3, Http2ErrorCode::CANCEL) + .Ping(47) + .Serialize(); + testing::InSequence s; + + // Client preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + + EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "POST")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one")); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + EXPECT_CALL(visitor, OnFrameHeader(1, 25, DATA, 0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 25)) + .WillOnce(testing::Return(false)); + EXPECT_CALL(visitor, OnConnectionError()); + + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_LT(result, 0); + + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); + + int send_result = adapter->Send(); + // Some bytes should have been serialized. + EXPECT_EQ(0, send_result); + // SETTINGS and SETTINGS ack. + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, + spdy::SpdyFrameType::SETTINGS})); +} + +TEST(OgHttp2AdapterServerTest, ServerRejectsStreamData) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + const std::string frames = TestFrameSequence() + .ClientPreface() + .Headers(1, + {{":method", "POST"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}, + /*fin=*/false) + .Data(1, "This is the request body.") + .Headers(3, + {{":method", "GET"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/two"}}, + /*fin=*/true) + .RstStream(3, Http2ErrorCode::CANCEL) + .Ping(47) + .Serialize(); + testing::InSequence s; + + // Client preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + + EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "POST")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com")); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one")); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + EXPECT_CALL(visitor, OnFrameHeader(1, 25, DATA, 0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 25)); + EXPECT_CALL(visitor, OnDataForStream(1, _)).WillOnce(testing::Return(false)); + EXPECT_CALL(visitor, OnConnectionError()); + + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_LT(result, 0); + + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); + + int send_result = adapter->Send(); + // Some bytes should have been serialized. + EXPECT_EQ(0, send_result); + // SETTINGS and SETTINGS ack. + EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS, + spdy::SpdyFrameType::SETTINGS})); +} + } // namespace } // namespace test } // namespace adapter diff --git a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.cc b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.cc index 3a32b2e70ea..e3a696ba820 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.cc @@ -1,16 +1,28 @@ #include "http2/adapter/oghttp2_session.h" #include <tuple> +#include <utility> +#include "absl/memory/memory.h" #include "absl/strings/escaping.h" +#include "http2/adapter/http2_protocol.h" #include "http2/adapter/oghttp2_util.h" +#include "spdy/core/spdy_protocol.h" namespace http2 { namespace adapter { namespace { -const size_t kMaxMetadataFrameSize = 16384; +// #define OGHTTP2_DEBUG_TRACE 1 + +#ifdef OGHTTP2_DEBUG_TRACE +const bool kTraceLoggingEnabled = true; +#else +const bool kTraceLoggingEnabled = false; +#endif + +const uint32_t kMaxAllowedMetadataFrameSize = 65536u; // TODO(birenroy): Consider incorporating spdy::FlagsSerializionVisitor here. class FrameAttributeCollector : public spdy::SpdyFrameVisitor { @@ -107,28 +119,55 @@ class FrameAttributeCollector : public spdy::SpdyFrameVisitor { uint8_t flags_ = 0; }; +absl::string_view TracePerspectiveAsString(Perspective p) { + switch (p) { + case Perspective::kClient: + return "OGHTTP2_CLIENT"; + case Perspective::kServer: + return "OGHTTP2_SERVER"; + } +} + } // namespace void OgHttp2Session::PassthroughHeadersHandler::OnHeaderBlockStart() { + result_ = Http2VisitorInterface::HEADER_OK; const bool status = visitor_.OnBeginHeadersForStream(stream_id_); if (!status) { result_ = Http2VisitorInterface::HEADER_CONNECTION_ERROR; } + validator_.StartHeaderBlock(); } void OgHttp2Session::PassthroughHeadersHandler::OnHeader( absl::string_view key, absl::string_view value) { - if (result_ == Http2VisitorInterface::HEADER_OK) { - result_ = visitor_.OnHeaderForStream(stream_id_, key, value); + if (result_ != Http2VisitorInterface::HEADER_OK) { + QUICHE_VLOG(2) << "Early return; status not HEADER_OK"; + return; + } + const auto validation_result = validator_.ValidateSingleHeader(key, value); + if (validation_result != HeaderValidator::HEADER_OK) { + QUICHE_VLOG(2) << "RST_STREAM: invalid header found"; + result_ = Http2VisitorInterface::HEADER_RST_STREAM; + return; } + result_ = visitor_.OnHeaderForStream(stream_id_, key, value); } void OgHttp2Session::PassthroughHeadersHandler::OnHeaderBlockEnd( size_t /* uncompressed_header_bytes */, size_t /* compressed_header_bytes */) { if (result_ == Http2VisitorInterface::HEADER_OK) { - visitor_.OnEndHeadersForStream(stream_id_); + if (!validator_.FinishHeaderBlock(type_)) { + result_ = Http2VisitorInterface::HEADER_RST_STREAM; + } + } + if (result_ == Http2VisitorInterface::HEADER_OK) { + const bool result = visitor_.OnEndHeadersForStream(stream_id_); + if (!result) { + session_.decoder_.StopProcessing(); + } } else { session_.OnHeaderStatus(stream_id_, result_); } @@ -136,6 +175,12 @@ void OgHttp2Session::PassthroughHeadersHandler::OnHeaderBlockEnd( OgHttp2Session::OgHttp2Session(Http2VisitorInterface& visitor, Options options) : visitor_(visitor), + receive_logger_( + this, TracePerspectiveAsString(options.perspective), + []() { return kTraceLoggingEnabled; }, this), + send_logger_( + TracePerspectiveAsString(options.perspective), + []() { return kTraceLoggingEnabled; }, this), headers_handler_(*this, visitor), connection_window_manager_(kInitialFlowControlWindowSize, [this](size_t window_update_delta) { @@ -143,7 +188,7 @@ OgHttp2Session::OgHttp2Session(Http2VisitorInterface& visitor, Options options) window_update_delta); }), options_(options) { - decoder_.set_visitor(this); + decoder_.set_visitor(&receive_logger_); decoder_.set_extension_visitor(this); if (options_.perspective == Perspective::kServer) { remaining_preface_ = {spdy::kHttp2ConnectionHeaderPrefix, @@ -217,8 +262,8 @@ int OgHttp2Session::GetHpackDecoderDynamicTableSize() const { return decoder == nullptr ? 0 : decoder->GetDynamicTableSize(); } -ssize_t OgHttp2Session::ProcessBytes(absl::string_view bytes) { - ssize_t preface_consumed = 0; +int64_t OgHttp2Session::ProcessBytes(absl::string_view bytes) { + int64_t preface_consumed = 0; if (!remaining_preface_.empty()) { QUICHE_VLOG(2) << "Preface bytes remaining: " << remaining_preface_.size(); // decoder_ does not understand the client connection preface. @@ -228,7 +273,7 @@ ssize_t OgHttp2Session::ProcessBytes(absl::string_view bytes) { QUICHE_DLOG(INFO) << "Preface doesn't match! Expected: [" << absl::CEscape(remaining_preface_) << "], actual: [" << absl::CEscape(bytes) << "]"; - visitor_.OnConnectionError(); + LatchErrorAndNotify(); return -1; } remaining_preface_.remove_prefix(min_size); @@ -240,8 +285,14 @@ ssize_t OgHttp2Session::ProcessBytes(absl::string_view bytes) { } preface_consumed = min_size; } - ssize_t result = decoder_.ProcessInput(bytes.data(), bytes.size()); - return result < 0 ? result : result + preface_consumed; + int64_t result = decoder_.ProcessInput(bytes.data(), bytes.size()); + if (latched_error_) { + QUICHE_VLOG(2) << "ProcessBytes encountered an error."; + return -1; + } + const int64_t ret = result < 0 ? result : result + preface_consumed; + QUICHE_VLOG(2) << "ProcessBytes returning: " << ret; + return ret; } int OgHttp2Session::Consume(Http2StreamId stream_id, size_t num_bytes) { @@ -284,7 +335,7 @@ void OgHttp2Session::EnqueueFrame(std::unique_ptr<spdy::SpdyFrameIR> frame) { int OgHttp2Session::Send() { MaybeSetupPreface(); - ssize_t result = std::numeric_limits<ssize_t>::max(); + int64_t result = std::numeric_limits<int64_t>::max(); // Flush any serialized prefix. while (result > 0 && !serialized_prefix_.empty()) { result = visitor_.OnReadyToSend(serialized_prefix_); @@ -292,9 +343,13 @@ int OgHttp2Session::Send() { serialized_prefix_.erase(0, result); } } - if (!serialized_prefix_.empty()) { - return result < 0 ? result : 0; + if (result < 0) { + LatchErrorAndNotify(); + return result; + } else if (!serialized_prefix_.empty()) { + return 0; } + bool continue_writing = SendQueuedFrames(); while (continue_writing && !connection_metadata_.empty()) { continue_writing = SendMetadata(0, connection_metadata_); @@ -321,10 +376,11 @@ bool OgHttp2Session::SendQueuedFrames() { frame_ptr->Visit(&c); visitor_.OnBeforeFrameSent(c.frame_type(), c.stream_id(), c.length(), c.flags()); + frame_ptr->Visit(&send_logger_); spdy::SpdySerializedFrame frame = framer_.SerializeFrame(*frame_ptr); - const ssize_t result = visitor_.OnReadyToSend(absl::string_view(frame)); + const int64_t result = visitor_.OnReadyToSend(absl::string_view(frame)); if (result < 0) { - visitor_.OnConnectionError(); + LatchErrorAndNotify(); return false; } else if (result == 0) { // Write blocked. @@ -336,7 +392,7 @@ bool OgHttp2Session::SendQueuedFrames() { // If this endpoint is resetting the stream, the stream should be // closed. This endpoint is already aware of the outbound RST_STREAM and // its error code, so close with NO_ERROR. - visitor_.OnCloseStream(c.stream_id(), Http2ErrorCode::NO_ERROR); + CloseStream(c.stream_id(), Http2ErrorCode::NO_ERROR); } frames_.pop_front(); @@ -377,11 +433,12 @@ bool OgHttp2Session::WriteForStream(Http2StreamId stream_id) { return true; } bool source_can_produce = true; - int32_t available_window = std::min( - std::min(connection_send_window_, state.send_window), max_frame_payload_); + int32_t available_window = + std::min({connection_send_window_, state.send_window, + static_cast<int32_t>(max_frame_payload_)}); while (connection_can_write && available_window > 0 && state.outbound_body != nullptr) { - ssize_t length; + int64_t length; bool end_data; std::tie(length, end_data) = state.outbound_body->SelectPayloadLength(available_window); @@ -390,7 +447,8 @@ bool OgHttp2Session::WriteForStream(Http2StreamId stream_id) { break; } else if (length == DataFrameSource::kError) { source_can_produce = false; - visitor_.OnCloseStream(stream_id, Http2ErrorCode::INTERNAL_ERROR); + CloseStream(stream_id, Http2ErrorCode::INTERNAL_ERROR); + // No more work on the stream; it has been closed. break; } const bool fin = end_data ? state.outbound_body->send_fin() : false; @@ -411,9 +469,8 @@ bool OgHttp2Session::WriteForStream(Http2StreamId stream_id) { visitor_.OnFrameSent(/* DATA */ 0, stream_id, length, fin ? 0x1 : 0x0, 0); connection_send_window_ -= length; state.send_window -= length; - available_window = - std::min(std::min(connection_send_window_, state.send_window), - max_frame_payload_); + available_window = std::min({connection_send_window_, state.send_window, + static_cast<int32_t>(max_frame_payload_)}); } if (end_data) { bool sent_trailers = false; @@ -428,14 +485,17 @@ bool OgHttp2Session::WriteForStream(Http2StreamId stream_id) { } state.outbound_body = nullptr; if (fin || sent_trailers) { - MaybeCloseWithRstStream(stream_id, state); + if (MaybeCloseWithRstStream(stream_id, state)) { + // No more work on the stream; it has been closed. + break; + } } } } - // If the stream still has data to send, it should be marked as ready in the - // write scheduler. - if (source_can_produce && state.send_window > 0 && - state.outbound_body != nullptr) { + // If the stream still exists and has data to send, it should be marked as + // ready in the write scheduler. + if (stream_map_.contains(stream_id) && source_can_produce && + state.send_window > 0 && state.outbound_body != nullptr) { write_scheduler_.MarkStreamReady(stream_id, false); } // Streams can continue writing as long as the connection is not write-blocked @@ -445,19 +505,21 @@ bool OgHttp2Session::WriteForStream(Http2StreamId stream_id) { bool OgHttp2Session::SendMetadata(Http2StreamId stream_id, OgHttp2Session::MetadataSequence& sequence) { - auto payload_buffer = absl::make_unique<uint8_t[]>(kMaxMetadataFrameSize); + const uint32_t max_payload_size = + std::min(kMaxAllowedMetadataFrameSize, max_frame_payload_); + auto payload_buffer = absl::make_unique<uint8_t[]>(max_payload_size); while (!sequence.empty()) { MetadataSource& source = *sequence.front(); - ssize_t written; + int64_t written; bool end_metadata; std::tie(written, end_metadata) = - source.Pack(payload_buffer.get(), kMaxMetadataFrameSize); + source.Pack(payload_buffer.get(), max_payload_size); if (written < 0) { // Did not touch the connection, so perhaps writes are still possible. return true; } - QUICHE_DCHECK_LE(static_cast<size_t>(written), kMaxMetadataFrameSize); + QUICHE_DCHECK_LE(static_cast<size_t>(written), max_payload_size); auto payload = absl::string_view( reinterpret_cast<const char*>(payload_buffer.get()), written); EnqueueFrame(absl::make_unique<spdy::SpdyUnknownIR>( @@ -476,21 +538,15 @@ int32_t OgHttp2Session::SubmitRequest( // TODO(birenroy): return an error for the incorrect perspective const Http2StreamId stream_id = next_stream_id_; next_stream_id_ += 2; - // Convert headers to header block, create headers frame. - auto frame = - absl::make_unique<spdy::SpdyHeadersIR>(stream_id, ToHeaderBlock(headers)); - // Add data source and user data to stream state - auto iter = CreateStream(stream_id); - write_scheduler_.MarkStreamReady(stream_id, false); - if (data_source == nullptr) { - frame->set_fin(true); - iter->second.half_closed_local = true; + if (CanCreateStream()) { + StartRequest(stream_id, ToHeaderBlock(headers), std::move(data_source), + user_data); } else { - iter->second.outbound_body = std::move(data_source); + // TODO(diannahu): There should probably be a limit to the number of allowed + // pending streams. + pending_streams_.push_back( + {stream_id, ToHeaderBlock(headers), std::move(data_source), user_data}); } - iter->second.user_data = user_data; - // Enqueue headers frame - EnqueueFrame(std::move(frame)); return stream_id; } @@ -503,22 +559,17 @@ int OgHttp2Session::SubmitResponse( QUICHE_LOG(ERROR) << "Unable to find stream " << stream_id; return -501; // NGHTTP2_ERR_INVALID_ARGUMENT } - // Convert headers to header block, create headers frame - auto frame = - absl::make_unique<spdy::SpdyHeadersIR>(stream_id, ToHeaderBlock(headers)); - if (data_source == nullptr) { - frame->set_fin(true); + const bool end_stream = data_source == nullptr; + if (end_stream) { if (iter->second.half_closed_remote) { - visitor_.OnCloseStream(stream_id, Http2ErrorCode::NO_ERROR); + CloseStream(stream_id, Http2ErrorCode::NO_ERROR); } - // TODO(birenroy): the server adapter should probably delete stream state - // when calling visitor_.OnCloseStream. } else { // Add data source to stream state iter->second.outbound_body = std::move(data_source); write_scheduler_.MarkStreamReady(stream_id, false); } - EnqueueFrame(std::move(frame)); + SendHeaders(stream_id, ToHeaderBlock(headers), end_stream); return 0; } @@ -571,7 +622,7 @@ void OgHttp2Session::OnError(http2::Http2DecoderAdapter::SpdyFramerError error, QUICHE_VLOG(1) << "Error: " << http2::Http2DecoderAdapter::SpdyFramerErrorToString(error) << " details: " << detailed_error; - visitor_.OnConnectionError(); + LatchErrorAndNotify(); } void OgHttp2Session::OnCommonHeader(spdy::SpdyStreamId stream_id, @@ -580,19 +631,29 @@ void OgHttp2Session::OnCommonHeader(spdy::SpdyStreamId stream_id, uint8_t flags) { highest_received_stream_id_ = std::max(static_cast<Http2StreamId>(stream_id), highest_received_stream_id_); - visitor_.OnFrameHeader(stream_id, length, type, flags); + const bool result = visitor_.OnFrameHeader(stream_id, length, type, flags); + if (!result) { + decoder_.StopProcessing(); + } } void OgHttp2Session::OnDataFrameHeader(spdy::SpdyStreamId stream_id, size_t length, bool /*fin*/) { - visitor_.OnBeginDataForStream(stream_id, length); + const bool result = visitor_.OnBeginDataForStream(stream_id, length); + if (!result) { + decoder_.StopProcessing(); + } } void OgHttp2Session::OnStreamFrameData(spdy::SpdyStreamId stream_id, const char* data, size_t len) { MarkDataBuffered(stream_id, len); - visitor_.OnDataForStream(stream_id, absl::string_view(data, len)); + const bool result = + visitor_.OnDataForStream(stream_id, absl::string_view(data, len)); + if (!result) { + decoder_.StopProcessing(); + } } void OgHttp2Session::OnStreamEnd(spdy::SpdyStreamId stream_id) { @@ -605,7 +666,7 @@ void OgHttp2Session::OnStreamEnd(spdy::SpdyStreamId stream_id) { options_.perspective == Perspective::kClient) { // From the client's perspective, the stream can be closed if it's already // half_closed_local. - visitor_.OnCloseStream(stream_id, Http2ErrorCode::NO_ERROR); + CloseStream(stream_id, Http2ErrorCode::NO_ERROR); } } @@ -624,10 +685,27 @@ void OgHttp2Session::OnStreamPadding(spdy::SpdyStreamId /*stream_id*/, size_t spdy::SpdyHeadersHandlerInterface* OgHttp2Session::OnHeaderFrameStart( spdy::SpdyStreamId stream_id) { headers_handler_.set_stream_id(stream_id); + auto it = stream_map_.find(stream_id); + if (it != stream_map_.end()) { + headers_handler_.set_header_type( + NextHeaderType(it->second.received_header_type)); + } return &headers_handler_; } -void OgHttp2Session::OnHeaderFrameEnd(spdy::SpdyStreamId /*stream_id*/) { +void OgHttp2Session::OnHeaderFrameEnd(spdy::SpdyStreamId stream_id) { + auto it = stream_map_.find(stream_id); + if (it != stream_map_.end()) { + if (headers_handler_.header_type() == HeaderType::RESPONSE && + !headers_handler_.status_header().empty() && + headers_handler_.status_header()[0] == '1') { + // If response headers carried a 1xx response code, final response headers + // should still be forthcoming. + it->second.received_header_type = HeaderType::RESPONSE_100; + } else { + it->second.received_header_type = headers_handler_.header_type(); + } + } headers_handler_.set_stream_id(0); } @@ -640,9 +718,7 @@ void OgHttp2Session::OnRstStream(spdy::SpdyStreamId stream_id, write_scheduler_.UnregisterStream(stream_id); } visitor_.OnRstStream(stream_id, TranslateErrorCode(error_code)); - // TODO(birenroy): Consider bundling "close stream" behavior into a dedicated - // method that also cleans up the stream map. - visitor_.OnCloseStream(stream_id, TranslateErrorCode(error_code)); + CloseStream(stream_id, TranslateErrorCode(error_code)); } void OgHttp2Session::OnSettings() { @@ -653,6 +729,10 @@ void OgHttp2Session::OnSetting(spdy::SpdySettingsId id, uint32_t value) { visitor_.OnSetting({id, value}); if (id == kMetadataExtensionId) { peer_supports_metadata_ = (value != 0); + } else if (id == MAX_FRAME_SIZE) { + max_frame_payload_ = value; + } else if (id == MAX_CONCURRENT_STREAMS) { + max_outbound_concurrent_streams_ = value; } } @@ -674,8 +754,11 @@ void OgHttp2Session::OnPing(spdy::SpdyPingId unique_id, bool is_ack) { void OgHttp2Session::OnGoAway(spdy::SpdyStreamId last_accepted_stream_id, spdy::SpdyErrorCode error_code) { received_goaway_ = true; - visitor_.OnGoAway(last_accepted_stream_id, TranslateErrorCode(error_code), - ""); + const bool result = visitor_.OnGoAway(last_accepted_stream_id, + TranslateErrorCode(error_code), ""); + if (!result) { + decoder_.StopProcessing(); + } } bool OgHttp2Session::OnGoAwayFrameData(const char* /*goaway_data*/, size_t @@ -748,7 +831,7 @@ void OgHttp2Session::OnHeaderStatus( stream_id, spdy::ERROR_CODE_INTERNAL_ERROR)); } } else if (result == Http2VisitorInterface::HEADER_CONNECTION_ERROR) { - visitor_.OnConnectionError(); + LatchErrorAndNotify(); } } @@ -771,13 +854,17 @@ bool OgHttp2Session::OnFrameHeader(spdy::SpdyStreamId stream_id, size_t length, void OgHttp2Session::OnFramePayload(const char* data, size_t len) { if (metadata_length_ > 0) { QUICHE_DCHECK_LE(len, metadata_length_); - visitor_.OnMetadataForStream(metadata_stream_id_, - absl::string_view(data, len)); - metadata_length_ -= len; - if (metadata_length_ == 0 && end_metadata_) { - visitor_.OnMetadataEndForStream(metadata_stream_id_); - metadata_stream_id_ = 0; - end_metadata_ = false; + const bool success = visitor_.OnMetadataForStream( + metadata_stream_id_, absl::string_view(data, len)); + if (success) { + metadata_length_ -= len; + if (metadata_length_ == 0 && end_metadata_) { + visitor_.OnMetadataEndForStream(metadata_stream_id_); + metadata_stream_id_ = 0; + end_metadata_ = false; + } + } else { + decoder_.StopProcessing(); } } else { QUICHE_DLOG(INFO) << "Unexpected metadata payload for stream " @@ -808,6 +895,15 @@ void OgHttp2Session::SendWindowUpdate(Http2StreamId stream_id, absl::make_unique<spdy::SpdyWindowUpdateIR>(stream_id, update_delta)); } +void OgHttp2Session::SendHeaders(Http2StreamId stream_id, + spdy::SpdyHeaderBlock headers, + bool end_stream) { + auto frame = + absl::make_unique<spdy::SpdyHeadersIR>(stream_id, std::move(headers)); + frame->set_fin(end_stream); + EnqueueFrame(std::move(frame)); +} + void OgHttp2Session::SendTrailers(Http2StreamId stream_id, spdy::SpdyHeaderBlock trailers) { auto frame = @@ -816,22 +912,22 @@ void OgHttp2Session::SendTrailers(Http2StreamId stream_id, EnqueueFrame(std::move(frame)); } -void OgHttp2Session::MaybeCloseWithRstStream(Http2StreamId stream_id, +bool OgHttp2Session::MaybeCloseWithRstStream(Http2StreamId stream_id, StreamState& state) { state.half_closed_local = true; if (options_.perspective == Perspective::kServer) { if (state.half_closed_remote) { - visitor_.OnCloseStream(stream_id, Http2ErrorCode::NO_ERROR); + CloseStream(stream_id, Http2ErrorCode::NO_ERROR); + return true; } else { // Since the peer has not yet ended the stream, this endpoint should // send a RST_STREAM NO_ERROR. See RFC 7540 Section 8.1. EnqueueFrame(absl::make_unique<spdy::SpdyRstStreamIR>( stream_id, spdy::SpdyErrorCode::ERROR_CODE_NO_ERROR)); - // Enqueuing the RST_STREAM also invokes OnCloseStream. + // Sending the RST_STREAM also invokes OnCloseStream. } - // TODO(birenroy): the server adapter should probably delete stream state - // when calling visitor_.OnCloseStream. } + return false; } void OgHttp2Session::MarkDataBuffered(Http2StreamId stream_id, size_t bytes) { @@ -861,5 +957,56 @@ OgHttp2Session::StreamStateMap::iterator OgHttp2Session::CreateStream( return iter; } +void OgHttp2Session::StartRequest(Http2StreamId stream_id, + spdy::SpdyHeaderBlock headers, + std::unique_ptr<DataFrameSource> data_source, + void* user_data) { + auto iter = CreateStream(stream_id); + write_scheduler_.MarkStreamReady(stream_id, false); + const bool end_stream = data_source == nullptr; + if (end_stream) { + iter->second.half_closed_local = true; + } else { + iter->second.outbound_body = std::move(data_source); + } + iter->second.user_data = user_data; + SendHeaders(stream_id, std::move(headers), end_stream); +} + +void OgHttp2Session::CloseStream(Http2StreamId stream_id, + Http2ErrorCode error_code) { + visitor_.OnCloseStream(stream_id, error_code); + stream_map_.erase(stream_id); + + if (!pending_streams_.empty() && CanCreateStream()) { + PendingStreamState& pending_stream = pending_streams_.front(); + StartRequest(pending_stream.stream_id, std::move(pending_stream.headers), + std::move(pending_stream.data_source), + pending_stream.user_data); + pending_streams_.pop_front(); + } +} + +bool OgHttp2Session::CanCreateStream() const { + return stream_map_.size() < max_outbound_concurrent_streams_; +} + +HeaderType OgHttp2Session::NextHeaderType( + absl::optional<HeaderType> current_type) { + if (IsServerSession()) { + return HeaderType::REQUEST; + } else if (!current_type || + current_type.value() == HeaderType::RESPONSE_100) { + return HeaderType::RESPONSE; + } else { + return HeaderType::RESPONSE_TRAILER; + } +} + +void OgHttp2Session::LatchErrorAndNotify() { + latched_error_ = true; + visitor_.OnConnectionError(); +} + } // namespace adapter } // namespace http2 diff --git a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.h b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.h index 77b9da73c38..75f9c96868c 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session.h @@ -1,18 +1,23 @@ #ifndef QUICHE_HTTP2_ADAPTER_OGHTTP2_SESSION_H_ #define QUICHE_HTTP2_ADAPTER_OGHTTP2_SESSION_H_ +#include <cstdint> #include <list> #include "http2/adapter/data_source.h" +#include "http2/adapter/header_validator.h" +#include "http2/adapter/http2_protocol.h" #include "http2/adapter/http2_session.h" #include "http2/adapter/http2_util.h" #include "http2/adapter/http2_visitor_interface.h" #include "http2/adapter/window_manager.h" +#include "http2/core/http2_trace_logging.h" #include "http2/core/priority_write_scheduler.h" #include "common/platform/api/quiche_bug_tracker.h" #include "common/platform/api/quiche_export.h" #include "spdy/core/http2_frame_decoder_adapter.h" #include "spdy/core/spdy_framer.h" +#include "spdy/core/spdy_header_block.h" namespace http2 { namespace adapter { @@ -37,7 +42,8 @@ class QUICHE_EXPORT_PRIVATE OgHttp2Session // sent. void StartGracefulShutdown(); - // Invokes the visitor's OnReadyToSend() method for serialized frame data. + // Invokes the visitor's OnReadyToSend() method for serialized frames and + // DataFrameSource::Send() for data frames. int Send(); int32_t SubmitRequest(absl::Span<const Header> headers, @@ -84,9 +90,11 @@ class QUICHE_EXPORT_PRIVATE OgHttp2Session int GetHpackDecoderDynamicTableSize() const; // From Http2Session. - ssize_t ProcessBytes(absl::string_view bytes) override; + int64_t ProcessBytes(absl::string_view bytes) override; int Consume(Http2StreamId stream_id, size_t num_bytes) override; - bool want_read() const override { return !received_goaway_; } + bool want_read() const override { + return !received_goaway_ && !decoder_.HasError(); + } bool want_write() const override { return !frames_.empty() || !serialized_prefix_.empty() || write_scheduler_.HasReadyStreams() || !connection_metadata_.empty(); @@ -162,6 +170,7 @@ class QUICHE_EXPORT_PRIVATE OgHttp2Session private: using MetadataSequence = std::vector<std::unique_ptr<MetadataSource>>; + struct QUICHE_EXPORT_PRIVATE StreamState { StreamState(int32_t stream_receive_window, WindowManager::WindowUpdateListener listener) @@ -173,25 +182,43 @@ class QUICHE_EXPORT_PRIVATE OgHttp2Session std::unique_ptr<spdy::SpdyHeaderBlock> trailers; void* user_data = nullptr; int32_t send_window = kInitialFlowControlWindowSize; + absl::optional<HeaderType> received_header_type; bool half_closed_local = false; bool half_closed_remote = false; }; using StreamStateMap = absl::flat_hash_map<Http2StreamId, StreamState>; + struct QUICHE_EXPORT_PRIVATE PendingStreamState { + Http2StreamId stream_id; + spdy::SpdyHeaderBlock headers; + std::unique_ptr<DataFrameSource> data_source; + void* user_data = nullptr; + }; + class QUICHE_EXPORT_PRIVATE PassthroughHeadersHandler : public spdy::SpdyHeadersHandlerInterface { public: explicit PassthroughHeadersHandler(OgHttp2Session& session, Http2VisitorInterface& visitor) : session_(session), visitor_(visitor) {} + void set_stream_id(Http2StreamId stream_id) { stream_id_ = stream_id; result_ = Http2VisitorInterface::HEADER_OK; } + + void set_header_type(HeaderType type) { type_ = type; } + HeaderType header_type() const { return type_; } + void OnHeaderBlockStart() override; void OnHeader(absl::string_view key, absl::string_view value) override; void OnHeaderBlockEnd(size_t /* uncompressed_header_bytes */, size_t /* compressed_header_bytes */) override; + absl::string_view status_header() { + QUICHE_DCHECK(type_ == HeaderType::RESPONSE || + type_ == HeaderType::RESPONSE_100); + return validator_.status_header(); + } private: OgHttp2Session& session_; @@ -199,6 +226,9 @@ class QUICHE_EXPORT_PRIVATE OgHttp2Session Http2StreamId stream_id_ = 0; Http2VisitorInterface::OnHeaderResult result_ = Http2VisitorInterface::HEADER_OK; + // Validates header blocks according to the HTTP/2 specification. + HeaderValidator validator_; + HeaderType type_ = HeaderType::RESPONSE; }; // Queues the connection preface, if not already done. @@ -216,30 +246,61 @@ class QUICHE_EXPORT_PRIVATE OgHttp2Session bool SendMetadata(Http2StreamId stream_id, MetadataSequence& sequence); + void SendHeaders(Http2StreamId stream_id, spdy::SpdyHeaderBlock headers, + bool end_stream); + void SendTrailers(Http2StreamId stream_id, spdy::SpdyHeaderBlock trailers); // Encapsulates the RST_STREAM NO_ERROR behavior described in RFC 7540 - // Section 8.1. - void MaybeCloseWithRstStream(Http2StreamId stream_id, StreamState& state); + // Section 8.1. Returns true if the stream is closed. + bool MaybeCloseWithRstStream(Http2StreamId stream_id, StreamState& state); // Performs flow control accounting for data sent by the peer. void MarkDataBuffered(Http2StreamId stream_id, size_t bytes); - // Creates a stream and returns an iterator pointing to it. + // Creates a stream for `stream_id` if not already present and returns an + // iterator pointing to it. StreamStateMap::iterator CreateStream(Http2StreamId stream_id); + // Creates a stream for `stream_id`, stores the `data_source` and `user_data` + // in the stream state, and sends the `headers`. + void StartRequest(Http2StreamId stream_id, spdy::SpdyHeaderBlock headers, + std::unique_ptr<DataFrameSource> data_source, + void* user_data); + + // Closes the given `stream_id` with the given `error_code`. + void CloseStream(Http2StreamId stream_id, Http2ErrorCode error_code); + + // Calculates the next expected header type for a stream in a given state. + HeaderType NextHeaderType(absl::optional<HeaderType> current_type); + + // Returns true if the session can create a new stream. + bool CanCreateStream() const; + + void LatchErrorAndNotify(); + // Receives events when inbound frames are parsed. Http2VisitorInterface& visitor_; + // Logs received frames when enabled. + Http2TraceLogger receive_logger_; + // Logs sent frames when enabled. + Http2FrameLogger send_logger_; + // Encodes outbound frames. spdy::SpdyFramer framer_{spdy::SpdyFramer::ENABLE_COMPRESSION}; // Decodes inbound frames. http2::Http2DecoderAdapter decoder_; - // Maintains the state of all streams known to this session. + // Maintains the state of active streams known to this session. StreamStateMap stream_map_; + // Maintains the state of pending streams known to this session. A pending + // stream is kept in this list until it can be created while complying with + // `max_outbound_concurrent_streams_`. + std::list<PendingStreamState> pending_streams_; + // Maintains the queue of outbound frames, and any serialized bytes that have // not yet been consumed. std::list<std::unique_ptr<spdy::SpdyFrameIR>> frames_; @@ -266,10 +327,12 @@ class QUICHE_EXPORT_PRIVATE OgHttp2Session Http2StreamId highest_received_stream_id_ = 0; Http2StreamId metadata_stream_id_ = 0; size_t metadata_length_ = 0; - int connection_send_window_ = kInitialFlowControlWindowSize; + int32_t connection_send_window_ = kInitialFlowControlWindowSize; // The initial flow control receive window size for any newly created streams. - int stream_receive_window_limit_ = kInitialFlowControlWindowSize; - int max_frame_payload_ = 16384; + int32_t stream_receive_window_limit_ = kInitialFlowControlWindowSize; + uint32_t max_frame_payload_ = 16384u; + // The spec encourages a value of at least 100 concurrent streams. + uint32_t max_outbound_concurrent_streams_ = 100u; Options options_; bool received_goaway_ = false; bool queued_preface_ = false; @@ -278,6 +341,7 @@ class QUICHE_EXPORT_PRIVATE OgHttp2Session // Replace this with a stream ID, for multiple GOAWAY support. bool queued_goaway_ = false; + bool latched_error_ = false; }; } // namespace adapter diff --git a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session_test.cc index 3f24ea200ef..4fdee88aad2 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session_test.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_session_test.cc @@ -60,8 +60,8 @@ TEST(OgHttp2SessionTest, ClientHandlesFrames) { EXPECT_CALL(visitor, OnFrameHeader(0, 4, WINDOW_UPDATE, 0)); EXPECT_CALL(visitor, OnWindowUpdate(0, 1000)); - const size_t initial_result = session.ProcessBytes(initial_frames); - EXPECT_EQ(initial_frames.size(), initial_result); + const int64_t initial_result = session.ProcessBytes(initial_frames); + EXPECT_EQ(initial_frames.size(), static_cast<size_t>(initial_result)); EXPECT_EQ(session.GetRemoteWindowSize(), kInitialFlowControlWindowSize + 1000); @@ -116,8 +116,8 @@ TEST(OgHttp2SessionTest, ClientHandlesFrames) { EXPECT_CALL(visitor, OnCloseStream(3, Http2ErrorCode::INTERNAL_ERROR)); EXPECT_CALL(visitor, OnFrameHeader(0, 19, GOAWAY, 0)); EXPECT_CALL(visitor, OnGoAway(5, Http2ErrorCode::ENHANCE_YOUR_CALM, "")); - const size_t stream_result = session.ProcessBytes(stream_frames); - EXPECT_EQ(stream_frames.size(), stream_result); + const int64_t stream_result = session.ProcessBytes(stream_frames); + EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result)); EXPECT_EQ(3, session.GetHighestReceivedStreamId()); // The first stream is active and has received some data. @@ -231,8 +231,8 @@ TEST(OgHttp2SessionTest, ClientSubmitRequest) { EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSettingsEnd()); - const size_t initial_result = session.ProcessBytes(initial_frames); - EXPECT_EQ(initial_frames.size(), initial_result); + const int64_t initial_result = session.ProcessBytes(initial_frames); + EXPECT_EQ(initial_frames.size(), static_cast<size_t>(initial_result)); // Session will want to write a SETTINGS ack. EXPECT_TRUE(session.want_write()); @@ -550,6 +550,7 @@ TEST(OgHttp2SessionTest, ServerHandlesFrames) { EXPECT_CALL(visitor, OnEndHeadersForStream(1)) .WillOnce(testing::InvokeWithoutArgs([&session, kSentinel1]() { session.SetStreamUserData(1, const_cast<char*>(kSentinel1)); + return true; })); EXPECT_CALL(visitor, OnFrameHeader(1, 4, WINDOW_UPDATE, 0)); EXPECT_CALL(visitor, OnWindowUpdate(1, 2000)); @@ -570,8 +571,8 @@ TEST(OgHttp2SessionTest, ServerHandlesFrames) { EXPECT_CALL(visitor, OnFrameHeader(0, 8, PING, 0)); EXPECT_CALL(visitor, OnPing(47, false)); - const size_t result = session.ProcessBytes(frames); - EXPECT_EQ(frames.size(), result); + const int64_t result = session.ProcessBytes(frames); + EXPECT_EQ(frames.size(), static_cast<size_t>(result)); EXPECT_EQ(kSentinel1, session.GetStreamUserData(1)); @@ -587,11 +588,10 @@ TEST(OgHttp2SessionTest, ServerHandlesFrames) { EXPECT_GT(session.GetHpackDecoderDynamicTableSize(), 0); - // TODO(birenroy): drop stream state when streams are closed. It should no - // longer be possible to set user data. + // It should no longer be possible to set user data on a closed stream. const char* kSentinel3 = "another arbitrary pointer"; session.SetStreamUserData(3, const_cast<char*>(kSentinel3)); - EXPECT_EQ(kSentinel3, session.GetStreamUserData(3)); + EXPECT_EQ(nullptr, session.GetStreamUserData(3)); EXPECT_EQ(session.GetRemoteWindowSize(), kInitialFlowControlWindowSize + 1000); @@ -686,11 +686,12 @@ TEST(OgHttp2SessionTest, ServerSubmitResponse) { EXPECT_CALL(visitor, OnEndHeadersForStream(1)) .WillOnce(testing::InvokeWithoutArgs([&session, kSentinel1]() { session.SetStreamUserData(1, const_cast<char*>(kSentinel1)); + return true; })); EXPECT_CALL(visitor, OnEndStream(1)); - const size_t result = session.ProcessBytes(frames); - EXPECT_EQ(frames.size(), result); + const int64_t result = session.ProcessBytes(frames); + EXPECT_EQ(frames.size(), static_cast<size_t>(result)); EXPECT_EQ(1, session.GetHighestReceivedStreamId()); @@ -830,8 +831,8 @@ TEST(OgHttp2SessionTest, ServerSendsTrailers) { EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); - const size_t result = session.ProcessBytes(frames); - EXPECT_EQ(frames.size(), result); + const int64_t result = session.ProcessBytes(frames); + EXPECT_EQ(frames.size(), static_cast<size_t>(result)); // Server will want to send initial SETTINGS, and a SETTINGS ack. EXPECT_TRUE(session.want_write()); @@ -922,8 +923,8 @@ TEST(OgHttp2SessionTest, ServerQueuesTrailersWithResponse) { EXPECT_CALL(visitor, OnEndHeadersForStream(1)); EXPECT_CALL(visitor, OnEndStream(1)); - const size_t result = session.ProcessBytes(frames); - EXPECT_EQ(frames.size(), result); + const int64_t result = session.ProcessBytes(frames); + EXPECT_EQ(frames.size(), static_cast<size_t>(result)); // Server will want to send initial SETTINGS, and a SETTINGS ack. EXPECT_TRUE(session.want_write()); diff --git a/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.cc b/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.cc index 75f3749e62c..4dbe4e21ccc 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.cc @@ -7,7 +7,7 @@ namespace http2 { namespace adapter { namespace test { -ssize_t RecordingHttp2Visitor::OnReadyToSend(absl::string_view serialized) { +int64_t RecordingHttp2Visitor::OnReadyToSend(absl::string_view serialized) { events_.push_back(absl::StrFormat("OnReadyToSend %d", serialized.size())); return serialized.size(); } @@ -16,12 +16,12 @@ void RecordingHttp2Visitor::OnConnectionError() { events_.push_back("OnConnectionError"); } -void RecordingHttp2Visitor::OnFrameHeader(Http2StreamId stream_id, - size_t length, - uint8_t type, +bool RecordingHttp2Visitor::OnFrameHeader(Http2StreamId stream_id, + size_t length, uint8_t type, uint8_t flags) { events_.push_back(absl::StrFormat("OnFrameHeader %d %d %d %d", stream_id, length, type, flags)); + return true; } void RecordingHttp2Visitor::OnSettingsStart() { @@ -53,19 +53,22 @@ Http2VisitorInterface::OnHeaderResult RecordingHttp2Visitor::OnHeaderForStream( return HEADER_OK; } -void RecordingHttp2Visitor::OnEndHeadersForStream(Http2StreamId stream_id) { +bool RecordingHttp2Visitor::OnEndHeadersForStream(Http2StreamId stream_id) { events_.push_back(absl::StrFormat("OnEndHeadersForStream %d", stream_id)); + return true; } -void RecordingHttp2Visitor::OnBeginDataForStream(Http2StreamId stream_id, +bool RecordingHttp2Visitor::OnBeginDataForStream(Http2StreamId stream_id, size_t payload_length) { events_.push_back( absl::StrFormat("OnBeginDataForStream %d %d", stream_id, payload_length)); + return true; } -void RecordingHttp2Visitor::OnDataForStream(Http2StreamId stream_id, +bool RecordingHttp2Visitor::OnDataForStream(Http2StreamId stream_id, absl::string_view data) { events_.push_back(absl::StrFormat("OnDataForStream %d %s", stream_id, data)); + return true; } void RecordingHttp2Visitor::OnEndStream(Http2StreamId stream_id) { @@ -104,12 +107,13 @@ void RecordingHttp2Visitor::OnPushPromiseForStream( promised_stream_id)); } -void RecordingHttp2Visitor::OnGoAway(Http2StreamId last_accepted_stream_id, +bool RecordingHttp2Visitor::OnGoAway(Http2StreamId last_accepted_stream_id, Http2ErrorCode error_code, absl::string_view opaque_data) { events_.push_back( absl::StrFormat("OnGoAway %d %s %s", last_accepted_stream_id, Http2ErrorCodeToString(error_code), opaque_data)); + return true; } void RecordingHttp2Visitor::OnWindowUpdate(Http2StreamId stream_id, @@ -141,34 +145,17 @@ bool RecordingHttp2Visitor::OnInvalidFrame(Http2StreamId stream_id, return true; } -void RecordingHttp2Visitor::OnReadyToSendDataForStream( - Http2StreamId stream_id, char* /*destination_buffer*/, size_t length, - ssize_t* /*written*/, bool* /*end_stream*/) { - // TODO(b/181586191): Revisit this. The visitor is expected to write to the - // |destination_buffer| and set the other pointer values appropriately. - events_.push_back( - absl::StrFormat("OnReadyToSendDataForStream %d %d", stream_id, length)); -} - -void RecordingHttp2Visitor::OnReadyToSendMetadataForStream( - Http2StreamId stream_id, char* /*buffer*/, size_t length, - ssize_t* /*written*/) { - // TODO(b/181586191): Revisit this. The visitor is expected to write to the - // |buffer| and set *written appropriately. - events_.push_back(absl::StrFormat("OnReadyToSendMetadataForStream %d %d", - stream_id, length)); -} - void RecordingHttp2Visitor::OnBeginMetadataForStream(Http2StreamId stream_id, size_t payload_length) { events_.push_back(absl::StrFormat("OnBeginMetadataForStream %d %d", stream_id, payload_length)); } -void RecordingHttp2Visitor::OnMetadataForStream(Http2StreamId stream_id, +bool RecordingHttp2Visitor::OnMetadataForStream(Http2StreamId stream_id, absl::string_view metadata) { events_.push_back( absl::StrFormat("OnMetadataForStream %d %s", stream_id, metadata)); + return true; } bool RecordingHttp2Visitor::OnMetadataEndForStream(Http2StreamId stream_id) { diff --git a/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.h b/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.h index 25b92935b35..8946ad3499c 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor.h @@ -1,6 +1,7 @@ #ifndef QUICHE_HTTP2_ADAPTER_RECORDING_HTTP2_VISITOR_H_ #define QUICHE_HTTP2_ADAPTER_RECORDING_HTTP2_VISITOR_H_ +#include <cstdint> #include <list> #include <string> @@ -19,11 +20,9 @@ class QUICHE_NO_EXPORT RecordingHttp2Visitor : public Http2VisitorInterface { using EventSequence = std::list<Event>; // From Http2VisitorInterface - ssize_t OnReadyToSend(absl::string_view serialized) override; + int64_t OnReadyToSend(absl::string_view serialized) override; void OnConnectionError() override; - void OnFrameHeader(Http2StreamId stream_id, - size_t length, - uint8_t type, + bool OnFrameHeader(Http2StreamId stream_id, size_t length, uint8_t type, uint8_t flags) override; void OnSettingsStart() override; void OnSetting(Http2Setting setting) override; @@ -33,10 +32,10 @@ class QUICHE_NO_EXPORT RecordingHttp2Visitor : public Http2VisitorInterface { OnHeaderResult OnHeaderForStream(Http2StreamId stream_id, absl::string_view name, absl::string_view value) override; - void OnEndHeadersForStream(Http2StreamId stream_id) override; - void OnBeginDataForStream(Http2StreamId stream_id, + bool OnEndHeadersForStream(Http2StreamId stream_id) override; + bool OnBeginDataForStream(Http2StreamId stream_id, size_t payload_length) override; - void OnDataForStream(Http2StreamId stream_id, + bool OnDataForStream(Http2StreamId stream_id, absl::string_view data) override; void OnEndStream(Http2StreamId stream_id) override; void OnRstStream(Http2StreamId stream_id, Http2ErrorCode error_code) override; @@ -49,7 +48,7 @@ class QUICHE_NO_EXPORT RecordingHttp2Visitor : public Http2VisitorInterface { void OnPing(Http2PingId ping_id, bool is_ack) override; void OnPushPromiseForStream(Http2StreamId stream_id, Http2StreamId promised_stream_id) override; - void OnGoAway(Http2StreamId last_accepted_stream_id, + bool OnGoAway(Http2StreamId last_accepted_stream_id, Http2ErrorCode error_code, absl::string_view opaque_data) override; void OnWindowUpdate(Http2StreamId stream_id, int window_increment) override; @@ -58,18 +57,9 @@ class QUICHE_NO_EXPORT RecordingHttp2Visitor : public Http2VisitorInterface { int OnFrameSent(uint8_t frame_type, Http2StreamId stream_id, size_t length, uint8_t flags, uint32_t error_code) override; bool OnInvalidFrame(Http2StreamId stream_id, int error_code) override; - void OnReadyToSendDataForStream(Http2StreamId stream_id, - char* destination_buffer, - size_t length, - ssize_t* written, - bool* end_stream) override; - void OnReadyToSendMetadataForStream(Http2StreamId stream_id, - char* buffer, - size_t length, - ssize_t* written) override; void OnBeginMetadataForStream(Http2StreamId stream_id, size_t payload_length) override; - void OnMetadataForStream(Http2StreamId stream_id, + bool OnMetadataForStream(Http2StreamId stream_id, absl::string_view metadata) override; bool OnMetadataEndForStream(Http2StreamId stream_id) override; void OnErrorDebug(absl::string_view message) override; diff --git a/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor_test.cc index 8f278d75dcb..6763244d4c7 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor_test.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/recording_http2_visitor_test.cc @@ -85,11 +85,6 @@ TEST(RecordingHttp2VisitorTest, SameEventsProduceSameSequence) { visitor->OnPushPromiseForStream(stream_id, another_stream_id); visitor->OnGoAway(stream_id, error_code, some_string); visitor->OnWindowUpdate(stream_id, some_int); - visitor->OnReadyToSendDataForStream( - stream_id, /*destination_buffer=*/nullptr, length, /*written=*/nullptr, - /*end_stream=*/nullptr); - visitor->OnReadyToSendMetadataForStream(stream_id, /*buffer=*/nullptr, - length, /*written=*/nullptr); visitor->OnBeginMetadataForStream(stream_id, length); visitor->OnMetadataForStream(stream_id, some_string); visitor->OnMetadataForStream(stream_id, another_string); diff --git a/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.cc b/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.cc index bb1517d3d73..7a3f1157d0c 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.cc @@ -19,15 +19,15 @@ std::vector<const Header> ToHeaders( return out; } -TestFrameSequence& TestFrameSequence::ClientPreface() { +TestFrameSequence& TestFrameSequence::ClientPreface( + absl::Span<const Http2Setting> settings) { preface_ = spdy::kHttp2ConnectionHeaderPrefix; - frames_.push_back(absl::make_unique<spdy::SpdySettingsIR>()); - return *this; + return Settings(settings); } -TestFrameSequence& TestFrameSequence::ServerPreface() { - frames_.push_back(absl::make_unique<spdy::SpdySettingsIR>()); - return *this; +TestFrameSequence& TestFrameSequence::ServerPreface( + absl::Span<const Http2Setting> settings) { + return Settings(settings); } TestFrameSequence& TestFrameSequence::Data(Http2StreamId stream_id, @@ -51,12 +51,12 @@ TestFrameSequence& TestFrameSequence::RstStream(Http2StreamId stream_id, } TestFrameSequence& TestFrameSequence::Settings( - absl::Span<Http2Setting> values) { - auto settings = absl::make_unique<spdy::SpdySettingsIR>(); - for (const Http2Setting& setting : values) { - settings->AddSetting(setting.id, setting.value); + absl::Span<const Http2Setting> settings) { + auto settings_frame = absl::make_unique<spdy::SpdySettingsIR>(); + for (const Http2Setting& setting : settings) { + settings_frame->AddSetting(setting.id, setting.value); } - frames_.push_back(std::move(settings)); + frames_.push_back(std::move(settings_frame)); return *this; } @@ -102,10 +102,9 @@ TestFrameSequence& TestFrameSequence::Headers(Http2StreamId stream_id, // nonterminal HEADERS frame explicitly, so we'll need to use // SpdyUnknownIRs. For simplicity, and in order not to mess up HPACK state, // the payload will be uncompressed. - std::string encoded_block; spdy::HpackEncoder encoder; encoder.DisableCompression(); - encoder.EncodeHeaderSet(block, &encoded_block); + std::string encoded_block = encoder.EncodeHeaderBlock(block); const size_t pos = encoded_block.size() / 2; const uint8_t flags = fin ? 0x1 : 0x0; frames_.push_back(absl::make_unique<spdy::SpdyUnknownIR>( @@ -186,9 +185,7 @@ std::string TestFrameSequence::MetadataBlockForPayload( block["example-payload"] = payload; spdy::HpackEncoder encoder; encoder.DisableCompression(); - std::string encoded_payload; - encoder.EncodeHeaderSet(block, &encoded_payload); - return encoded_payload; + return encoder.EncodeHeaderBlock(block); } } // namespace test diff --git a/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.h b/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.h index 99740d38f9e..43e97db2824 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.h @@ -1,6 +1,7 @@ #ifndef QUICHE_HTTP2_ADAPTER_TEST_FRAME_SEQUENCE_H_ #define QUICHE_HTTP2_ADAPTER_TEST_FRAME_SEQUENCE_H_ +#include <cstdint> #include <memory> #include <string> #include <vector> @@ -20,14 +21,16 @@ class QUICHE_NO_EXPORT TestFrameSequence { public: TestFrameSequence() = default; - TestFrameSequence& ClientPreface(); - TestFrameSequence& ServerPreface(); + TestFrameSequence& ClientPreface( + absl::Span<const Http2Setting> settings = {}); + TestFrameSequence& ServerPreface( + absl::Span<const Http2Setting> settings = {}); TestFrameSequence& Data(Http2StreamId stream_id, absl::string_view payload, bool fin = false, absl::optional<int> padding_length = absl::nullopt); TestFrameSequence& RstStream(Http2StreamId stream_id, Http2ErrorCode error); - TestFrameSequence& Settings(absl::Span<Http2Setting> values); + TestFrameSequence& Settings(absl::Span<const Http2Setting> settings); TestFrameSequence& SettingsAck(); TestFrameSequence& Ping(Http2PingId id); TestFrameSequence& PingAck(Http2PingId id); diff --git a/chromium/net/third_party/quiche/src/http2/adapter/test_utils.cc b/chromium/net/third_party/quiche/src/http2/adapter/test_utils.cc index b8acddbb87f..80e113e820b 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/test_utils.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/test_utils.cc @@ -25,14 +25,14 @@ void TestDataFrameSource::AppendPayload(absl::string_view payload) { void TestDataFrameSource::EndData() { end_data_ = true; } -std::pair<ssize_t, bool> TestDataFrameSource::SelectPayloadLength( +std::pair<int64_t, bool> TestDataFrameSource::SelectPayloadLength( size_t max_length) { // The stream is done if there's no more data, or if |max_length| is at least // as large as the remaining data. const bool end_data = end_data_ && (current_fragment_.empty() || (payload_fragments_.size() == 1 && max_length >= current_fragment_.size())); - const ssize_t length = std::min(max_length, current_fragment_.size()); + const int64_t length = std::min(max_length, current_fragment_.size()); return {length, end_data}; } @@ -43,7 +43,7 @@ bool TestDataFrameSource::Send(absl::string_view frame_header, << " current_fragment_size: " << current_fragment_.size(); const std::string concatenated = absl::StrCat(frame_header, current_fragment_.substr(0, payload_length)); - const ssize_t result = visitor_.OnReadyToSend(concatenated); + const int64_t result = visitor_.OnReadyToSend(concatenated); if (result < 0) { // Write encountered error. visitor_.OnConnectionError(); @@ -77,9 +77,7 @@ bool TestDataFrameSource::Send(absl::string_view frame_header, std::string EncodeHeaders(const spdy::SpdyHeaderBlock& entries) { spdy::HpackEncoder encoder; encoder.DisableCompression(); - std::string result; - QUICHE_CHECK(encoder.EncodeHeaderSet(entries, &result)); - return result; + return encoder.EncodeHeaderBlock(entries); } TestMetadataSource::TestMetadataSource(const spdy::SpdyHeaderBlock& entries) @@ -87,7 +85,7 @@ TestMetadataSource::TestMetadataSource(const spdy::SpdyHeaderBlock& entries) remaining_ = encoded_entries_; } -std::pair<ssize_t, bool> TestMetadataSource::Pack(uint8_t* dest, +std::pair<int64_t, bool> TestMetadataSource::Pack(uint8_t* dest, size_t dest_len) { const size_t copied = std::min(dest_len, remaining_.size()); std::memcpy(dest, remaining_.data(), copied); diff --git a/chromium/net/third_party/quiche/src/http2/adapter/test_utils.h b/chromium/net/third_party/quiche/src/http2/adapter/test_utils.h index d92617fa3c9..ec7e95cc4de 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/test_utils.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/test_utils.h @@ -1,6 +1,7 @@ #ifndef QUICHE_HTTP2_ADAPTER_TEST_UTILS_H_ #define QUICHE_HTTP2_ADAPTER_TEST_UTILS_H_ +#include <cstdint> #include <string> #include <vector> @@ -21,7 +22,10 @@ namespace test { class QUICHE_NO_EXPORT DataSavingVisitor : public testing::StrictMock<MockHttp2Visitor> { public: - ssize_t OnReadyToSend(absl::string_view data) override { + int64_t OnReadyToSend(absl::string_view data) override { + if (has_write_error_) { + return kSendError; + } if (is_write_blocked_) { return kSendBlocked; } @@ -33,13 +37,16 @@ class QUICHE_NO_EXPORT DataSavingVisitor return to_accept; } - void OnMetadataForStream(Http2StreamId stream_id, + bool OnMetadataForStream(Http2StreamId stream_id, absl::string_view metadata) override { - testing::StrictMock<MockHttp2Visitor>::OnMetadataForStream(stream_id, - metadata); - auto result = - metadata_map_.try_emplace(stream_id, std::vector<std::string>()); - result.first->second.push_back(std::string(metadata)); + const bool ret = testing::StrictMock<MockHttp2Visitor>::OnMetadataForStream( + stream_id, metadata); + if (ret) { + auto result = + metadata_map_.try_emplace(stream_id, std::vector<std::string>()); + result.first->second.push_back(std::string(metadata)); + } + return ret; } const std::vector<std::string> GetMetadata(Http2StreamId stream_id) { @@ -59,11 +66,14 @@ class QUICHE_NO_EXPORT DataSavingVisitor bool is_write_blocked() const { return is_write_blocked_; } void set_is_write_blocked(bool value) { is_write_blocked_ = value; } + void set_has_write_error() { has_write_error_ = true; } + private: std::string data_; absl::flat_hash_map<Http2StreamId, std::vector<std::string>> metadata_map_; size_t send_limit_ = std::numeric_limits<size_t>::max(); bool is_write_blocked_ = false; + bool has_write_error_ = false; }; // A test DataFrameSource. Starts out in the empty, blocked state. @@ -74,7 +84,7 @@ class QUICHE_NO_EXPORT TestDataFrameSource : public DataFrameSource { void AppendPayload(absl::string_view payload); void EndData(); - std::pair<ssize_t, bool> SelectPayloadLength(size_t max_length) override; + std::pair<int64_t, bool> SelectPayloadLength(size_t max_length) override; bool Send(absl::string_view frame_header, size_t payload_length) override; bool send_fin() const override { return has_fin_; } @@ -92,7 +102,11 @@ class QUICHE_NO_EXPORT TestMetadataSource : public MetadataSource { public: explicit TestMetadataSource(const spdy::SpdyHeaderBlock& entries); - std::pair<ssize_t, bool> Pack(uint8_t* dest, size_t dest_len) override; + size_t NumFrames(size_t max_frame_size) const override { + // Round up to the next frame. + return (encoded_entries_.size() + max_frame_size - 1) / max_frame_size; + } + std::pair<int64_t, bool> Pack(uint8_t* dest, size_t dest_len) override; private: const std::string encoded_entries_; diff --git a/chromium/net/third_party/quiche/src/http2/adapter/window_manager.h b/chromium/net/third_party/quiche/src/http2/adapter/window_manager.h index f15982d466a..5a7701e91e0 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/window_manager.h +++ b/chromium/net/third_party/quiche/src/http2/adapter/window_manager.h @@ -1,6 +1,8 @@ #ifndef QUICHE_HTTP2_ADAPTER_WINDOW_MANAGER_H_ #define QUICHE_HTTP2_ADAPTER_WINDOW_MANAGER_H_ +#include <stddef.h> + #include <functional> #include "common/platform/api/quiche_export.h" diff --git a/chromium/net/third_party/quiche/src/http2/core/http2_trace_logging.cc b/chromium/net/third_party/quiche/src/http2/core/http2_trace_logging.cc new file mode 100644 index 00000000000..2c1bee38a08 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/core/http2_trace_logging.cc @@ -0,0 +1,454 @@ +#include "http2/core/http2_trace_logging.h" + +#include <cstdint> + +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "common/platform/api/quiche_bug_tracker.h" +#include "common/platform/api/quiche_logging.h" +#include "spdy/core/spdy_protocol.h" + +// Convenience macros for printing function arguments in log lines in the +// format arg_name=value. +#define FORMAT_ARG(arg) " " #arg "=" << arg +#define FORMAT_INT_ARG(arg) " " #arg "=" << static_cast<int>(arg) + +// Convenience macros for printing Spdy*IR attributes in log lines in the +// format attrib_name=value. +#define FORMAT_ATTR(ir, attrib) " " #attrib "=" << ir.attrib() +#define FORMAT_INT_ATTR(ir, attrib) \ + " " #attrib "=" << static_cast<int>(ir.attrib()) + +namespace { + +// Logs a container, using a user-provided object to log each individual item. +template <typename T, typename ItemLogger> +struct ContainerLogger { + explicit ContainerLogger(const T& c, ItemLogger l) + : container(c), item_logger(l) {} + + friend std::ostream& operator<<(std::ostream& out, + const ContainerLogger& logger) { + out << "["; + auto begin = logger.container.begin(); + for (auto it = begin; it != logger.container.end(); ++it) { + if (it != begin) { + out << ", "; + } + logger.item_logger.Log(out, *it); + } + out << "]"; + return out; + } + const T& container; + ItemLogger item_logger; +}; + +// Returns a ContainerLogger that will log |container| using |item_logger|. +template <typename T, typename ItemLogger> +auto LogContainer(const T& container, ItemLogger item_logger) + -> decltype(ContainerLogger<T, ItemLogger>(container, item_logger)) { + return ContainerLogger<T, ItemLogger>(container, item_logger); +} + +} // anonymous namespace + +#define FORMAT_HEADER_BLOCK(ir) \ + " header_block=" << LogContainer(ir.header_block(), LogHeaderBlockEntry()) + +namespace http2 { + +using spdy::SettingsMap; +using spdy::SpdyAltSvcIR; +using spdy::SpdyContinuationIR; +using spdy::SpdyDataIR; +using spdy::SpdyGoAwayIR; +using spdy::SpdyHeaderBlock; +using spdy::SpdyHeadersIR; +using spdy::SpdyPingIR; +using spdy::SpdyPriorityIR; +using spdy::SpdyPushPromiseIR; +using spdy::SpdyRstStreamIR; +using spdy::SpdySettingsIR; +using spdy::SpdyStreamId; +using spdy::SpdyUnknownIR; +using spdy::SpdyWindowUpdateIR; + +namespace { + +// Defines how elements of SpdyHeaderBlocks are logged. +struct LogHeaderBlockEntry { + void Log(std::ostream& out, + const SpdyHeaderBlock::value_type& entry) const { // NOLINT + out << "\"" << entry.first << "\": \"" << entry.second << "\""; + } +}; + +// Defines how elements of SettingsMap are logged. +struct LogSettingsEntry { + void Log(std::ostream& out, + const SettingsMap::value_type& entry) const { // NOLINT + out << spdy::SettingsIdToString(entry.first) << ": " << entry.second; + } +}; + +// Defines how elements of AlternativeServiceVector are logged. +struct LogAlternativeService { + void Log(std::ostream& out, + const spdy::SpdyAltSvcWireFormat::AlternativeService& altsvc) + const { // NOLINT + out << "{" + << "protocol_id=" << altsvc.protocol_id << " host=" << altsvc.host + << " port=" << altsvc.port + << " max_age_seconds=" << altsvc.max_age_seconds << " version="; + for (auto v : altsvc.version) { + out << v << ","; + } + out << "}"; + } +}; + +} // anonymous namespace + +Http2TraceLogger::Http2TraceLogger(SpdyFramerVisitorInterface* parent, + absl::string_view perspective, + std::function<bool()> is_enabled, + const void* connection_id) + : wrapped_(parent), + perspective_(perspective), + is_enabled_(std::move(is_enabled)), + connection_id_(connection_id) {} + +Http2TraceLogger::~Http2TraceLogger() { + if (recording_headers_handler_ != nullptr && + !recording_headers_handler_->decoded_block().empty()) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "connection_id=" << connection_id_ + << " Received headers that were never logged! keys/values:" + << recording_headers_handler_->decoded_block().DebugString(); + } +} + +void Http2TraceLogger::OnError(Http2DecoderAdapter::SpdyFramerError error, + std::string detailed_error) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnError:" << FORMAT_ARG(connection_id_) + << ", error=" << Http2DecoderAdapter::SpdyFramerErrorToString(error); + wrapped_->OnError(error, detailed_error); +} + +void Http2TraceLogger::OnCommonHeader(SpdyStreamId stream_id, size_t length, + uint8_t type, uint8_t flags) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnCommonHeader:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id) << FORMAT_ARG(length) << FORMAT_INT_ARG(type) + << FORMAT_INT_ARG(flags); + wrapped_->OnCommonHeader(stream_id, length, type, flags); +} + +void Http2TraceLogger::OnDataFrameHeader(SpdyStreamId stream_id, size_t length, + bool fin) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnDataFrameHeader:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id) << FORMAT_ARG(length) << FORMAT_ARG(fin); + wrapped_->OnDataFrameHeader(stream_id, length, fin); +} + +void Http2TraceLogger::OnStreamFrameData(SpdyStreamId stream_id, + const char* data, size_t len) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnStreamFrameData:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id) << FORMAT_ARG(len); + wrapped_->OnStreamFrameData(stream_id, data, len); +} + +void Http2TraceLogger::OnStreamEnd(SpdyStreamId stream_id) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnStreamEnd:" << FORMAT_ARG(connection_id_) << FORMAT_ARG(stream_id); + wrapped_->OnStreamEnd(stream_id); +} + +void Http2TraceLogger::OnStreamPadLength(SpdyStreamId stream_id, size_t value) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnStreamPadLength:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id) << FORMAT_ARG(value); + wrapped_->OnStreamPadLength(stream_id, value); +} + +void Http2TraceLogger::OnStreamPadding(SpdyStreamId stream_id, size_t len) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnStreamPadding:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id) << FORMAT_ARG(len); + wrapped_->OnStreamPadding(stream_id, len); +} + +spdy::SpdyHeadersHandlerInterface* Http2TraceLogger::OnHeaderFrameStart( + SpdyStreamId stream_id) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnHeaderFrameStart:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id); + spdy::SpdyHeadersHandlerInterface* result = + wrapped_->OnHeaderFrameStart(stream_id); + recording_headers_handler_ = + absl::make_unique<spdy::RecordingHeadersHandler>(result); + result = recording_headers_handler_.get(); + return result; +} + +void Http2TraceLogger::OnHeaderFrameEnd(SpdyStreamId stream_id) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnHeaderFrameEnd:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id); + LogReceivedHeaders(); + wrapped_->OnHeaderFrameEnd(stream_id); + recording_headers_handler_ = nullptr; +} + +void Http2TraceLogger::OnRstStream(SpdyStreamId stream_id, + SpdyErrorCode error_code) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnRstStream:" << FORMAT_ARG(connection_id_) << FORMAT_ARG(stream_id) + << " error_code=" << spdy::ErrorCodeToString(error_code); + wrapped_->OnRstStream(stream_id, error_code); +} + +void Http2TraceLogger::OnSettings() { wrapped_->OnSettings(); } + +void Http2TraceLogger::OnSetting(SpdySettingsId id, uint32_t value) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnSetting:" << FORMAT_ARG(connection_id_) + << " id=" << spdy::SettingsIdToString(id) << FORMAT_ARG(value); + wrapped_->OnSetting(id, value); +} + +void Http2TraceLogger::OnSettingsEnd() { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnSettingsEnd:" << FORMAT_ARG(connection_id_); + wrapped_->OnSettingsEnd(); +} + +void Http2TraceLogger::OnSettingsAck() { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnSettingsAck:" << FORMAT_ARG(connection_id_); + wrapped_->OnSettingsAck(); +} + +void Http2TraceLogger::OnPing(SpdyPingId unique_id, bool is_ack) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnPing:" << FORMAT_ARG(connection_id_) << FORMAT_ARG(unique_id) + << FORMAT_ARG(is_ack); + wrapped_->OnPing(unique_id, is_ack); +} + +void Http2TraceLogger::OnGoAway(SpdyStreamId last_accepted_stream_id, + SpdyErrorCode error_code) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnGoAway:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(last_accepted_stream_id) + << " error_code=" << spdy::ErrorCodeToString(error_code); + wrapped_->OnGoAway(last_accepted_stream_id, error_code); +} + +bool Http2TraceLogger::OnGoAwayFrameData(const char* goaway_data, size_t len) { + return wrapped_->OnGoAwayFrameData(goaway_data, len); +} + +void Http2TraceLogger::OnHeaders(SpdyStreamId stream_id, bool has_priority, + int weight, SpdyStreamId parent_stream_id, + bool exclusive, bool fin, bool end) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnHeaders:" << FORMAT_ARG(connection_id_) << FORMAT_ARG(stream_id) + << FORMAT_ARG(has_priority) << FORMAT_INT_ARG(weight) + << FORMAT_ARG(parent_stream_id) << FORMAT_ARG(exclusive) + << FORMAT_ARG(fin) << FORMAT_ARG(end); + wrapped_->OnHeaders(stream_id, has_priority, weight, parent_stream_id, + exclusive, fin, end); +} + +void Http2TraceLogger::OnWindowUpdate(SpdyStreamId stream_id, + int delta_window_size) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnWindowUpdate:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id) << FORMAT_ARG(delta_window_size); + wrapped_->OnWindowUpdate(stream_id, delta_window_size); +} + +void Http2TraceLogger::OnPushPromise(SpdyStreamId original_stream_id, + SpdyStreamId promised_stream_id, bool end) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnPushPromise:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(original_stream_id) << FORMAT_ARG(promised_stream_id) + << FORMAT_ARG(end); + wrapped_->OnPushPromise(original_stream_id, promised_stream_id, end); +} + +void Http2TraceLogger::OnContinuation(SpdyStreamId stream_id, bool end) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnContinuation:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id) << FORMAT_ARG(end); + wrapped_->OnContinuation(stream_id, end); +} + +void Http2TraceLogger::OnAltSvc( + SpdyStreamId stream_id, absl::string_view origin, + const SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnAltSvc:" << FORMAT_ARG(connection_id_) << FORMAT_ARG(stream_id) + << FORMAT_ARG(origin) << " altsvc_vector=" + << LogContainer(altsvc_vector, LogAlternativeService()); + wrapped_->OnAltSvc(stream_id, origin, altsvc_vector); +} + +void Http2TraceLogger::OnPriority(SpdyStreamId stream_id, + SpdyStreamId parent_stream_id, int weight, + bool exclusive) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnPriority:" << FORMAT_ARG(connection_id_) << FORMAT_ARG(stream_id) + << FORMAT_ARG(parent_stream_id) << FORMAT_INT_ARG(weight) + << FORMAT_ARG(exclusive); + wrapped_->OnPriority(stream_id, parent_stream_id, weight, exclusive); +} + +void Http2TraceLogger::OnPriorityUpdate( + SpdyStreamId prioritized_stream_id, + absl::string_view priority_field_value) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnPriorityUpdate:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(prioritized_stream_id) << FORMAT_ARG(priority_field_value); + wrapped_->OnPriorityUpdate(prioritized_stream_id, priority_field_value); +} + +bool Http2TraceLogger::OnUnknownFrame(SpdyStreamId stream_id, + uint8_t frame_type) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnUnknownFrame:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id) << FORMAT_INT_ARG(frame_type); + return wrapped_->OnUnknownFrame(stream_id, frame_type); +} + +void Http2TraceLogger::LogReceivedHeaders() const { + if (recording_headers_handler_ == nullptr) { + QUICHE_BUG(bug_2794_1) << "Cannot log headers before creating handler."; + return; + } + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Received headers;" << FORMAT_ARG(connection_id_) << " keys/values:" + << recording_headers_handler_->decoded_block().DebugString() + << " compressed_bytes=" + << recording_headers_handler_->compressed_header_bytes() + << " uncompressed_bytes=" + << recording_headers_handler_->uncompressed_header_bytes(); +} + +void Http2FrameLogger::VisitRstStream(const SpdyRstStreamIR& rst_stream) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyRstStreamIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(rst_stream, stream_id) + << " error_code=" << spdy::ErrorCodeToString(rst_stream.error_code()); +} + +void Http2FrameLogger::VisitSettings(const SpdySettingsIR& settings) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdySettingsIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(settings, is_ack) + << " values=" << LogContainer(settings.values(), LogSettingsEntry()); +} + +void Http2FrameLogger::VisitPing(const SpdyPingIR& ping) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyPingIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(ping, id) << FORMAT_ATTR(ping, is_ack); +} + +void Http2FrameLogger::VisitGoAway(const SpdyGoAwayIR& goaway) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyGoAwayIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(goaway, last_good_stream_id) + << " error_code=" << spdy::ErrorCodeToString(goaway.error_code()) + << FORMAT_ATTR(goaway, description); +} + +void Http2FrameLogger::VisitHeaders(const SpdyHeadersIR& headers) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyHeadersIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(headers, stream_id) << FORMAT_ATTR(headers, fin) + << FORMAT_ATTR(headers, has_priority) << FORMAT_INT_ATTR(headers, weight) + << FORMAT_ATTR(headers, parent_stream_id) + << FORMAT_ATTR(headers, exclusive) << FORMAT_ATTR(headers, padded) + << FORMAT_ATTR(headers, padding_payload_len) + << FORMAT_HEADER_BLOCK(headers); +} + +void Http2FrameLogger::VisitWindowUpdate( + const SpdyWindowUpdateIR& window_update) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyWindowUpdateIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(window_update, stream_id) + << FORMAT_ATTR(window_update, delta); +} + +void Http2FrameLogger::VisitPushPromise(const SpdyPushPromiseIR& push_promise) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyPushPromiseIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(push_promise, stream_id) << FORMAT_ATTR(push_promise, fin) + << FORMAT_ATTR(push_promise, promised_stream_id) + << FORMAT_ATTR(push_promise, padded) + << FORMAT_ATTR(push_promise, padding_payload_len) + << FORMAT_HEADER_BLOCK(push_promise); +} + +void Http2FrameLogger::VisitContinuation( + const SpdyContinuationIR& continuation) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyContinuationIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(continuation, stream_id) + << FORMAT_ATTR(continuation, end_headers); +} + +void Http2FrameLogger::VisitAltSvc(const SpdyAltSvcIR& altsvc) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyAltSvcIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(altsvc, stream_id) << FORMAT_ATTR(altsvc, origin) + << " altsvc_vector=" + << LogContainer(altsvc.altsvc_vector(), LogAlternativeService()); +} + +void Http2FrameLogger::VisitPriority(const SpdyPriorityIR& priority) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyPriorityIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(priority, stream_id) + << FORMAT_ATTR(priority, parent_stream_id) + << FORMAT_INT_ATTR(priority, weight) << FORMAT_ATTR(priority, exclusive); +} + +void Http2FrameLogger::VisitData(const SpdyDataIR& data) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyDataIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(data, stream_id) << FORMAT_ATTR(data, fin) + << " data_len=" << data.data_len() << FORMAT_ATTR(data, padded) + << FORMAT_ATTR(data, padding_payload_len); +} + +void Http2FrameLogger::VisitPriorityUpdate( + const spdy::SpdyPriorityUpdateIR& priority_update) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyPriorityUpdateIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(priority_update, stream_id) + << FORMAT_ATTR(priority_update, prioritized_stream_id) + << FORMAT_ATTR(priority_update, priority_field_value); +} + +void Http2FrameLogger::VisitAcceptCh( + const spdy::SpdyAcceptChIR& /*accept_ch*/) { + QUICHE_BUG(bug_2794_2) + << "Sending ACCEPT_CH frames is currently unimplemented."; +} + +void Http2FrameLogger::VisitUnknown(const SpdyUnknownIR& ir) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyUnknownIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(ir, stream_id) << FORMAT_INT_ATTR(ir, type) + << FORMAT_INT_ATTR(ir, flags) << FORMAT_ATTR(ir, length); +} + +} // namespace http2 diff --git a/chromium/net/third_party/quiche/src/http2/core/http2_trace_logging.h b/chromium/net/third_party/quiche/src/http2/core/http2_trace_logging.h new file mode 100644 index 00000000000..e1185bf0e58 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/core/http2_trace_logging.h @@ -0,0 +1,140 @@ +// Classes and utilities for supporting HTTP/2 trace logging, which logs +// information about all control and data frames sent and received over +// HTTP/2 connections. + +#ifndef QUICHE_HTTP2_CORE_HTTP2_TRACE_LOGGING_H_ +#define QUICHE_HTTP2_CORE_HTTP2_TRACE_LOGGING_H_ + +#include <cstdint> + +#include "absl/strings/string_view.h" +#include "common/platform/api/quiche_export.h" +#include "common/platform/api/quiche_logging.h" +#include "spdy/core/http2_frame_decoder_adapter.h" +#include "spdy/core/recording_headers_handler.h" +#include "spdy/core/spdy_headers_handler_interface.h" +#include "spdy/core/spdy_protocol.h" + +// Logging macro to use for all HTTP/2 trace logging. Iff trace logging is +// enabled, logs at level INFO with a common prefix prepended (to facilitate +// post-hoc filtering of trace logging output). +#define HTTP2_TRACE_LOG(perspective, is_enabled) \ + QUICHE_LOG_IF(INFO, is_enabled()) << "[HTTP2_TRACE " << perspective << "] " + +namespace http2 { + +// Intercepts deframing events to provide detailed logs. Intended to be used for +// manual debugging. +// +// Note any new methods in SpdyFramerVisitorInterface MUST be overridden here to +// properly forward the event. This could be ensured by making every event in +// SpdyFramerVisitorInterface a pure virtual. +class QUICHE_EXPORT_PRIVATE Http2TraceLogger + : public spdy::SpdyFramerVisitorInterface { + public: + typedef spdy::SpdyAltSvcWireFormat SpdyAltSvcWireFormat; + typedef spdy::SpdyErrorCode SpdyErrorCode; + typedef spdy::SpdyFramerVisitorInterface SpdyFramerVisitorInterface; + typedef spdy::SpdyPingId SpdyPingId; + typedef spdy::SpdyPriority SpdyPriority; + typedef spdy::SpdySettingsId SpdySettingsId; + typedef spdy::SpdyStreamId SpdyStreamId; + + Http2TraceLogger(SpdyFramerVisitorInterface* parent, + absl::string_view perspective, + std::function<bool()> is_enabled, const void* connection_id); + ~Http2TraceLogger() override; + + Http2TraceLogger(const Http2TraceLogger&) = delete; + Http2TraceLogger& operator=(const Http2TraceLogger&) = delete; + + void OnError(http2::Http2DecoderAdapter::SpdyFramerError error, + std::string detailed_error) override; + void OnCommonHeader(SpdyStreamId stream_id, size_t length, uint8_t type, + uint8_t flags) override; + spdy::SpdyHeadersHandlerInterface* OnHeaderFrameStart( + SpdyStreamId stream_id) override; + void OnHeaderFrameEnd(SpdyStreamId stream_id) override; + void OnDataFrameHeader(SpdyStreamId stream_id, size_t length, + bool fin) override; + void OnStreamFrameData(SpdyStreamId stream_id, const char* data, + size_t len) override; + void OnStreamEnd(SpdyStreamId stream_id) override; + void OnStreamPadLength(SpdyStreamId stream_id, size_t value) override; + void OnStreamPadding(SpdyStreamId stream_id, size_t len) override; + void OnRstStream(SpdyStreamId stream_id, SpdyErrorCode error_code) override; + void OnSetting(spdy::SpdySettingsId id, uint32_t value) override; + void OnPing(SpdyPingId unique_id, bool is_ack) override; + void OnSettings() override; + void OnSettingsEnd() override; + void OnSettingsAck() override; + void OnGoAway(SpdyStreamId last_accepted_stream_id, + SpdyErrorCode error_code) override; + bool OnGoAwayFrameData(const char* goaway_data, size_t len) override; + void OnHeaders(SpdyStreamId stream_id, bool has_priority, int weight, + SpdyStreamId parent_stream_id, bool exclusive, bool fin, + bool end) override; + void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override; + void OnPushPromise(SpdyStreamId stream_id, SpdyStreamId promised_stream_id, + bool end) override; + void OnContinuation(SpdyStreamId stream_id, bool end) override; + void OnAltSvc(SpdyStreamId stream_id, absl::string_view origin, + const SpdyAltSvcWireFormat::AlternativeServiceVector& + altsvc_vector) override; + void OnPriority(SpdyStreamId stream_id, SpdyStreamId parent_stream_id, + int weight, bool exclusive) override; + void OnPriorityUpdate(SpdyStreamId prioritized_stream_id, + absl::string_view priority_field_value) override; + bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override; + + private: + void LogReceivedHeaders() const; + + std::unique_ptr<spdy::RecordingHeadersHandler> recording_headers_handler_; + + SpdyFramerVisitorInterface* wrapped_; + const absl::string_view perspective_; + const std::function<bool()> is_enabled_; + const void* connection_id_; +}; + +// Visitor to log control frames that have been written. +class QUICHE_EXPORT_PRIVATE Http2FrameLogger : public spdy::SpdyFrameVisitor { + public: + // This class will preface all of its log messages with the value of + // |connection_id| in hexadecimal. + Http2FrameLogger(absl::string_view perspective, + std::function<bool()> is_enabled, const void* connection_id) + : perspective_(perspective), + is_enabled_(std::move(is_enabled)), + connection_id_(connection_id) {} + + Http2FrameLogger(const Http2FrameLogger&) = delete; + Http2FrameLogger& operator=(const Http2FrameLogger&) = delete; + + void VisitRstStream(const spdy::SpdyRstStreamIR& rst_stream) override; + void VisitSettings(const spdy::SpdySettingsIR& settings) override; + void VisitPing(const spdy::SpdyPingIR& ping) override; + void VisitGoAway(const spdy::SpdyGoAwayIR& goaway) override; + void VisitHeaders(const spdy::SpdyHeadersIR& headers) override; + void VisitWindowUpdate( + const spdy::SpdyWindowUpdateIR& window_update) override; + void VisitPushPromise(const spdy::SpdyPushPromiseIR& push_promise) override; + void VisitContinuation(const spdy::SpdyContinuationIR& continuation) override; + void VisitAltSvc(const spdy::SpdyAltSvcIR& altsvc) override; + void VisitPriority(const spdy::SpdyPriorityIR& priority) override; + void VisitData(const spdy::SpdyDataIR& data) override; + void VisitPriorityUpdate( + const spdy::SpdyPriorityUpdateIR& priority_update) override; + void VisitAcceptCh(const spdy::SpdyAcceptChIR& accept_ch) override; + void VisitUnknown(const spdy::SpdyUnknownIR& ir) override; + + private: + const absl::string_view perspective_; + const std::function<bool()> is_enabled_; + const void* connection_id_; +}; + +} // namespace http2 + +#endif // QUICHE_HTTP2_CORE_HTTP2_TRACE_LOGGING_H_ diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.h index 92177070acc..3ac581926a1 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.h @@ -69,6 +69,11 @@ class QUICHE_EXPORT_PRIVATE HpackDecoder { // decoding the SETTINGS ACK, and before the next HPACK block is decoded. void ApplyHeaderTableSizeSetting(uint32_t max_header_table_size); + // Returns the most recently applied value of SETTINGS_HEADER_TABLE_SIZE. + size_t GetCurrentHeaderTableSizeSetting() const { + return decoder_state_.GetCurrentHeaderTableSizeSetting(); + } + // Prepares the decoder for decoding a new HPACK block, and announces this to // its listener. Returns true if OK to continue with decoding, false if an // error has been detected, which for StartDecodingBlock means the error was diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.h index 5dcdc40bf99..3040c181471 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.h @@ -57,6 +57,11 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderState : public HpackWholeEntryListener { // decoding the SETTINGS ACK, and before the next HPACK block is decoded. void ApplyHeaderTableSizeSetting(uint32_t max_header_table_size); + // Returns the most recently applied value of SETTINGS_HEADER_TABLE_SIZE. + size_t GetCurrentHeaderTableSizeSetting() const { + return final_header_table_size_; + } + // OnHeaderBlockStart notifies this object that we're starting to decode the // HPACK payload of a HEADERS or PUSH_PROMISE frame. void OnHeaderBlockStart(); diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state_test.cc index 698c655c961..7e36a178a74 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state_test.cc @@ -427,8 +427,10 @@ TEST_F(HpackDecoderStateTest, OptionalTableSizeChanges) { // Confirm that required size updates are indeed required before headers. TEST_F(HpackDecoderStateTest, RequiredTableSizeChangeBeforeHeader) { + EXPECT_EQ(4096u, decoder_state_.GetCurrentHeaderTableSizeSetting()); decoder_state_.ApplyHeaderTableSizeSetting(1024); decoder_state_.ApplyHeaderTableSizeSetting(2048); + EXPECT_EQ(2048u, decoder_state_.GetCurrentHeaderTableSizeSetting()); // First provide the required update, and an allowed second update. SendStartAndVerifyCallback(); @@ -442,6 +444,7 @@ TEST_F(HpackDecoderStateTest, RequiredTableSizeChangeBeforeHeader) { // Another HPACK block, but this time missing the required size update. decoder_state_.ApplyHeaderTableSizeSetting(1024); + EXPECT_EQ(1024u, decoder_state_.GetCurrentHeaderTableSizeSetting()); SendStartAndVerifyCallback(); EXPECT_CALL(listener_, OnHeaderErrorDetected(Eq("Missing dynamic table size update"))); diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_test.cc index ce44ccb04b5..c160bfe30bf 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_test.cc @@ -961,11 +961,13 @@ TEST_P(HpackDecoderTest, ProcessesOptionalTableSizeUpdates) { // Confirm that the table size can be changed when required, but at most twice. TEST_P(HpackDecoderTest, ProcessesRequiredTableSizeUpdate) { + EXPECT_EQ(4096u, decoder_.GetCurrentHeaderTableSizeSetting()); // One update required, two allowed, one provided, followed by a header. decoder_.ApplyHeaderTableSizeSetting(1024); decoder_.ApplyHeaderTableSizeSetting(2048); EXPECT_EQ(Http2SettingsInfo::DefaultHeaderTableSize(), header_table_size_limit()); + EXPECT_EQ(2048u, decoder_.GetCurrentHeaderTableSizeSetting()); { HpackBlockBuilder hbb; hbb.AppendDynamicTableSizeUpdate(1024); @@ -979,6 +981,7 @@ TEST_P(HpackDecoderTest, ProcessesRequiredTableSizeUpdate) { // One update required, two allowed, two provided, followed by a header. decoder_.ApplyHeaderTableSizeSetting(1000); decoder_.ApplyHeaderTableSizeSetting(1500); + EXPECT_EQ(1500u, decoder_.GetCurrentHeaderTableSizeSetting()); { HpackBlockBuilder hbb; hbb.AppendDynamicTableSizeUpdate(500); @@ -994,6 +997,7 @@ TEST_P(HpackDecoderTest, ProcessesRequiredTableSizeUpdate) { // The third update is rejected, so the final size is 1000, not 500. decoder_.ApplyHeaderTableSizeSetting(500); decoder_.ApplyHeaderTableSizeSetting(1000); + EXPECT_EQ(1000u, decoder_.GetCurrentHeaderTableSizeSetting()); { HpackBlockBuilder hbb; hbb.AppendDynamicTableSizeUpdate(200); @@ -1011,6 +1015,7 @@ TEST_P(HpackDecoderTest, ProcessesRequiredTableSizeUpdate) { EXPECT_EQ(0u, current_header_table_size()); EXPECT_TRUE(header_entries_.empty()); } + EXPECT_EQ(1000u, decoder_.GetCurrentHeaderTableSizeSetting()); // Now that an error has been detected, StartDecodingBlock should return // false. EXPECT_FALSE(decoder_.StartDecodingBlock()); 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 657cad1a46a..240e9ddf754 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 @@ -23,21 +23,72 @@ std::ostream& operator<<(std::ostream& os, const SendTimeState& s) { return os; } -QuicByteCount MaxAckHeightTracker::Update(QuicBandwidth bandwidth_estimate, - QuicRoundTripCount round_trip_count, - QuicTime ack_time, - QuicByteCount bytes_acked) { - if (aggregation_epoch_start_time_ == QuicTime::Zero()) { +QuicByteCount MaxAckHeightTracker::Update( + QuicBandwidth bandwidth_estimate, bool is_new_max_bandwidth, + QuicRoundTripCount round_trip_count, + QuicPacketNumber last_sent_packet_number, + QuicPacketNumber last_acked_packet_number, QuicTime ack_time, + QuicByteCount bytes_acked) { + bool force_new_epoch = false; + + if (reduce_extra_acked_on_bandwidth_increase_ && is_new_max_bandwidth) { + // Save and clear existing entries. + ExtraAckedEvent best = max_ack_height_filter_.GetBest(); + ExtraAckedEvent second_best = max_ack_height_filter_.GetSecondBest(); + ExtraAckedEvent third_best = max_ack_height_filter_.GetThirdBest(); + max_ack_height_filter_.Clear(); + + // Reinsert the heights into the filter after recalculating. + QuicByteCount expected_bytes_acked = bandwidth_estimate * best.time_delta; + if (expected_bytes_acked < best.bytes_acked) { + best.extra_acked = best.bytes_acked - expected_bytes_acked; + max_ack_height_filter_.Update(best, best.round); + } + expected_bytes_acked = bandwidth_estimate * second_best.time_delta; + if (expected_bytes_acked < second_best.bytes_acked) { + QUICHE_DCHECK_LE(best.round, second_best.round); + second_best.extra_acked = second_best.bytes_acked - expected_bytes_acked; + max_ack_height_filter_.Update(second_best, second_best.round); + } + expected_bytes_acked = bandwidth_estimate * third_best.time_delta; + if (expected_bytes_acked < third_best.bytes_acked) { + QUICHE_DCHECK_LE(second_best.round, third_best.round); + third_best.extra_acked = third_best.bytes_acked - expected_bytes_acked; + max_ack_height_filter_.Update(third_best, third_best.round); + } + } + + // If any packet sent after the start of the epoch has been acked, start a new + // epoch. + if (start_new_aggregation_epoch_after_full_round_ && + last_sent_packet_number_before_epoch_.IsInitialized() && + last_acked_packet_number.IsInitialized() && + last_acked_packet_number > last_sent_packet_number_before_epoch_) { + QUIC_RELOADABLE_FLAG_COUNT( + quic_bbr_start_new_aggregation_epoch_after_a_full_round); + QUIC_DVLOG(3) << "Force starting a new aggregation epoch. " + "last_sent_packet_number_before_epoch_:" + << last_sent_packet_number_before_epoch_ + << ", last_acked_packet_number:" << last_acked_packet_number; + if (reduce_extra_acked_on_bandwidth_increase_) { + QUIC_BUG(quic_bwsampler_46) + << "A full round of aggregation should never " + << "pass with startup_include_extra_acked(B204) enabled."; + } + force_new_epoch = true; + } + if (aggregation_epoch_start_time_ == QuicTime::Zero() || force_new_epoch) { aggregation_epoch_bytes_ = bytes_acked; aggregation_epoch_start_time_ = ack_time; + last_sent_packet_number_before_epoch_ = last_sent_packet_number; ++num_ack_aggregation_epochs_; return 0; } // Compute how many bytes are expected to be delivered, assuming max bandwidth // is correct. - QuicByteCount expected_bytes_acked = - bandwidth_estimate * (ack_time - aggregation_epoch_start_time_); + QuicTime::Delta aggregation_delta = ack_time - aggregation_epoch_start_time_; + QuicByteCount expected_bytes_acked = bandwidth_estimate * aggregation_delta; // Reset the current aggregation epoch as soon as the ack arrival rate is less // than or equal to the max bandwidth. if (aggregation_epoch_bytes_ <= @@ -50,13 +101,13 @@ QuicByteCount MaxAckHeightTracker::Update(QuicBandwidth bandwidth_estimate, << ack_aggregation_bandwidth_threshold_ << ", expected_bytes_acked:" << expected_bytes_acked << ", bandwidth_estimate:" << bandwidth_estimate - << ", aggregation_duration:" - << (ack_time - aggregation_epoch_start_time_) + << ", aggregation_duration:" << aggregation_delta << ", new_aggregation_epoch:" << ack_time << ", new_aggregation_bytes_acked:" << bytes_acked; // Reset to start measuring a new aggregation epoch. aggregation_epoch_bytes_ = bytes_acked; aggregation_epoch_start_time_ = ack_time; + last_sent_packet_number_before_epoch_ = last_sent_packet_number; ++num_ack_aggregation_epochs_; return 0; } @@ -67,13 +118,17 @@ QuicByteCount MaxAckHeightTracker::Update(QuicBandwidth bandwidth_estimate, QuicByteCount extra_bytes_acked = aggregation_epoch_bytes_ - expected_bytes_acked; QUIC_DVLOG(3) << "Updating MaxAckHeight. ack_time:" << ack_time - << ", round trip count:" << round_trip_count + << ", last sent packet:" << last_sent_packet_number << ", bandwidth_estimate:" << bandwidth_estimate << ", bytes_acked:" << bytes_acked << ", expected_bytes_acked:" << expected_bytes_acked << ", aggregation_epoch_bytes_:" << aggregation_epoch_bytes_ << ", extra_bytes_acked:" << extra_bytes_acked; - max_ack_height_filter_.Update(extra_bytes_acked, round_trip_count); + ExtraAckedEvent new_event; + new_event.extra_acked = extra_bytes_acked; + new_event.bytes_acked = aggregation_epoch_bytes_; + new_event.time_delta = aggregation_delta; + max_ack_height_filter_.Update(new_event, round_trip_count); return extra_bytes_acked; } @@ -93,7 +148,8 @@ BandwidthSampler::BandwidthSampler( unacked_packet_map_(unacked_packet_map), max_ack_height_tracker_(max_height_tracker_window_length), total_bytes_acked_after_last_ack_event_(0), - overestimate_avoidance_(false) {} + overestimate_avoidance_(false), + limit_max_ack_height_tracker_by_send_rate_(false) {} BandwidthSampler::BandwidthSampler(const BandwidthSampler& other) : total_bytes_sent_(other.total_bytes_sent_), @@ -105,6 +161,7 @@ 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_), + last_acked_packet_(other.last_acked_packet_), 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_), @@ -115,7 +172,9 @@ BandwidthSampler::BandwidthSampler(const BandwidthSampler& other) max_ack_height_tracker_(other.max_ack_height_tracker_), total_bytes_acked_after_last_ack_event_( other.total_bytes_acked_after_last_ack_event_), - overestimate_avoidance_(other.overestimate_avoidance_) {} + overestimate_avoidance_(other.overestimate_avoidance_), + limit_max_ack_height_tracker_by_send_rate_( + other.limit_max_ack_height_tracker_by_send_rate_) {} void BandwidthSampler::EnableOverestimateAvoidance() { if (overestimate_avoidance_) { @@ -244,6 +303,7 @@ BandwidthSampler::OnCongestionEvent(QuicTime ack_time, } SendTimeState last_acked_packet_send_state; + QuicBandwidth max_send_rate = QuicBandwidth::Zero(); for (const auto& packet : acked_packets) { BandwidthSample sample = OnPacketAcknowledged(ack_time, packet.packet_number); @@ -260,6 +320,9 @@ BandwidthSampler::OnCongestionEvent(QuicTime ack_time, event_sample.sample_max_bandwidth = sample.bandwidth; event_sample.sample_is_app_limited = sample.state_at_send.is_app_limited; } + if (!sample.send_rate.IsInfinite()) { + max_send_rate = std::max(max_send_rate, sample.send_rate); + } const QuicByteCount inflight_sample = total_bytes_acked() - last_acked_packet_send_state.total_bytes_acked; if (inflight_sample > event_sample.sample_max_inflight) { @@ -282,15 +345,21 @@ BandwidthSampler::OnCongestionEvent(QuicTime ack_time, : last_acked_packet_send_state; } + bool is_new_max_bandwidth = event_sample.sample_max_bandwidth > max_bandwidth; max_bandwidth = std::max(max_bandwidth, event_sample.sample_max_bandwidth); - event_sample.extra_acked = OnAckEventEnd( - std::min(est_bandwidth_upper_bound, max_bandwidth), round_trip_count); + if (limit_max_ack_height_tracker_by_send_rate_) { + max_bandwidth = std::max(max_bandwidth, max_send_rate); + } + // TODO(ianswett): Why is the min being passed in here? + event_sample.extra_acked = + OnAckEventEnd(std::min(est_bandwidth_upper_bound, max_bandwidth), + is_new_max_bandwidth, round_trip_count); return event_sample; } QuicByteCount BandwidthSampler::OnAckEventEnd( - QuicBandwidth bandwidth_estimate, + QuicBandwidth bandwidth_estimate, bool is_new_max_bandwidth, QuicRoundTripCount round_trip_count) { const QuicByteCount newly_acked_bytes = total_bytes_acked_ - total_bytes_acked_after_last_ack_event_; @@ -299,9 +368,9 @@ QuicByteCount BandwidthSampler::OnAckEventEnd( return 0; } total_bytes_acked_after_last_ack_event_ = total_bytes_acked_; - QuicByteCount extra_acked = max_ack_height_tracker_.Update( - bandwidth_estimate, round_trip_count, last_acked_packet_ack_time_, + bandwidth_estimate, is_new_max_bandwidth, round_trip_count, + last_sent_packet_, last_acked_packet_, last_acked_packet_ack_time_, newly_acked_bytes); // If |extra_acked| is zero, i.e. this ack event marks the start of a new ack // aggregation epoch, save LessRecentPoint, which is the last ack point of the @@ -316,6 +385,7 @@ QuicByteCount BandwidthSampler::OnAckEventEnd( BandwidthSample BandwidthSampler::OnPacketAcknowledged( QuicTime ack_time, QuicPacketNumber packet_number) { + last_acked_packet_ = packet_number; ConnectionStateOnSentPacket* sent_packet_pointer = connection_state_map_.GetEntry(packet_number); if (sent_packet_pointer == nullptr) { @@ -412,6 +482,7 @@ BandwidthSample BandwidthSampler::OnPacketAcknowledgedInner( // means that the RTT measurements here can be artificially high, especially // on low bandwidth connections. sample.rtt = ack_time - sent_packet.sent_time; + sample.send_rate = send_rate; SentPacketToSendTimeState(sent_packet, &sample.state_at_send); if (sample.bandwidth.IsZero()) { 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 2d5924b06e1..814571746c0 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h @@ -9,6 +9,7 @@ #include "quic/core/congestion_control/windowed_filter.h" #include "quic/core/packet_number_indexed_queue.h" #include "quic/core/quic_bandwidth.h" +#include "quic/core/quic_packet_number.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_time.h" #include "quic/core/quic_types.h" @@ -77,20 +78,39 @@ struct QUIC_EXPORT_PRIVATE SendTimeState { QuicByteCount bytes_in_flight; }; +struct QUIC_NO_EXPORT ExtraAckedEvent { + // The excess bytes acknowlwedged in the time delta for this event. + QuicByteCount extra_acked = 0; + + // The bytes acknowledged and time delta from the event. + QuicByteCount bytes_acked = 0; + QuicTime::Delta time_delta = QuicTime::Delta::Zero(); + // The round trip of the event. + QuicRoundTripCount round = 0; + + inline bool operator>=(const ExtraAckedEvent& other) const { + return extra_acked >= other.extra_acked; + } + inline bool operator==(const ExtraAckedEvent& other) const { + return extra_acked == other.extra_acked; + } +}; + struct QUIC_EXPORT_PRIVATE BandwidthSample { // The bandwidth at that particular sample. Zero if no valid bandwidth sample // is available. - QuicBandwidth bandwidth; + QuicBandwidth bandwidth = QuicBandwidth::Zero(); // The RTT measurement at this particular sample. Zero if no RTT sample is // available. Does not correct for delayed ack time. - QuicTime::Delta rtt; + QuicTime::Delta rtt = QuicTime::Delta::Zero(); + + // |send_rate| is computed from the current packet being acked('P') and an + // earlier packet that is acked before P was sent. + QuicBandwidth send_rate = QuicBandwidth::Infinite(); // States captured when the packet was sent. SendTimeState state_at_send; - - BandwidthSample() - : bandwidth(QuicBandwidth::Zero()), rtt(QuicTime::Delta::Zero()) {} }; // MaxAckHeightTracker is part of the BandwidthSampler. It is called after every @@ -98,27 +118,42 @@ struct QUIC_EXPORT_PRIVATE BandwidthSample { class QUIC_EXPORT_PRIVATE MaxAckHeightTracker { public: explicit MaxAckHeightTracker(QuicRoundTripCount initial_filter_window) - : max_ack_height_filter_(initial_filter_window, 0, 0) {} + : max_ack_height_filter_(initial_filter_window, ExtraAckedEvent(), 0) {} - QuicByteCount Get() const { return max_ack_height_filter_.GetBest(); } + QuicByteCount Get() const { + return max_ack_height_filter_.GetBest().extra_acked; + } QuicByteCount Update(QuicBandwidth bandwidth_estimate, + bool is_new_max_bandwidth, QuicRoundTripCount round_trip_count, - QuicTime ack_time, - QuicByteCount bytes_acked); + QuicPacketNumber last_sent_packet_number, + QuicPacketNumber last_acked_packet_number, + QuicTime ack_time, QuicByteCount bytes_acked); void SetFilterWindowLength(QuicRoundTripCount length) { max_ack_height_filter_.SetWindowLength(length); } void Reset(QuicByteCount new_height, QuicRoundTripCount new_time) { - max_ack_height_filter_.Reset(new_height, new_time); + ExtraAckedEvent new_event; + new_event.extra_acked = new_height; + new_event.round = new_time; + max_ack_height_filter_.Reset(new_event, new_time); } void SetAckAggregationBandwidthThreshold(double threshold) { ack_aggregation_bandwidth_threshold_ = threshold; } + void SetStartNewAggregationEpochAfterFullRound(bool value) { + start_new_aggregation_epoch_after_full_round_ = value; + } + + void SetReduceExtraAckedOnBandwidthIncrease(bool value) { + reduce_extra_acked_on_bandwidth_increase_ = value; + } + double ack_aggregation_bandwidth_threshold() const { return ack_aggregation_bandwidth_threshold_; } @@ -130,20 +165,23 @@ class QUIC_EXPORT_PRIVATE MaxAckHeightTracker { private: // Tracks the maximum number of bytes acked faster than the estimated // bandwidth. - using MaxAckHeightFilter = WindowedFilter<QuicByteCount, - MaxFilter<QuicByteCount>, - QuicRoundTripCount, - QuicRoundTripCount>; + using MaxAckHeightFilter = + WindowedFilter<ExtraAckedEvent, MaxFilter<ExtraAckedEvent>, + QuicRoundTripCount, QuicRoundTripCount>; MaxAckHeightFilter max_ack_height_filter_; // The time this aggregation started and the number of bytes acked during it. QuicTime aggregation_epoch_start_time_ = QuicTime::Zero(); QuicByteCount aggregation_epoch_bytes_ = 0; + // The last sent packet number before the current aggregation epoch started. + QuicPacketNumber last_sent_packet_number_before_epoch_; // The number of ack aggregation epochs ever started, including the ongoing // one. Stats only. uint64_t num_ack_aggregation_epochs_ = 0; double ack_aggregation_bandwidth_threshold_ = GetQuicFlag(FLAGS_quic_ack_aggregation_bandwidth_threshold); + bool start_new_aggregation_epoch_after_full_round_ = false; + bool reduce_extra_acked_on_bandwidth_increase_ = false; }; // An interface common to any class that can provide bandwidth samples from the @@ -325,6 +363,7 @@ class QUIC_EXPORT_PRIVATE BandwidthSampler : public BandwidthSamplerInterface { QuicBandwidth est_bandwidth_upper_bound, QuicRoundTripCount round_trip_count) override; QuicByteCount OnAckEventEnd(QuicBandwidth bandwidth_estimate, + bool is_new_max_bandwidth, QuicRoundTripCount round_trip_count); void OnAppLimited() override; @@ -355,6 +394,18 @@ class QUIC_EXPORT_PRIVATE BandwidthSampler : public BandwidthSamplerInterface { max_ack_height_tracker_.Reset(new_height, new_time); } + void SetStartNewAggregationEpochAfterFullRound(bool value) { + max_ack_height_tracker_.SetStartNewAggregationEpochAfterFullRound(value); + } + + void SetLimitMaxAckHeightTrackerBySendRate(bool value) { + limit_max_ack_height_tracker_by_send_rate_ = value; + } + + void SetReduceExtraAckedOnBandwidthIncrease(bool value) { + max_ack_height_tracker_.SetReduceExtraAckedOnBandwidthIncrease(value); + } + // AckPoint represents a point on the ack line. struct QUIC_NO_EXPORT AckPoint { QuicTime ack_time = QuicTime::Zero(); @@ -528,6 +579,9 @@ class QUIC_EXPORT_PRIVATE BandwidthSampler : public BandwidthSamplerInterface { // The most recently sent packet. QuicPacketNumber last_sent_packet_; + // The most recently acked packet. + QuicPacketNumber last_acked_packet_; + // Indicates whether the bandwidth sampler is currently in an app-limited // phase. bool is_app_limited_; @@ -563,6 +617,9 @@ class QUIC_EXPORT_PRIVATE BandwidthSampler : public BandwidthSamplerInterface { // True if connection option 'BSAO' is set. bool overestimate_avoidance_; + + // True if connection option 'BBRB' is set. + bool limit_max_ack_height_tracker_by_send_rate_; }; } // namespace quic 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 67dad1cb675..0e0dac42d91 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 @@ -685,22 +685,23 @@ TEST_P(BandwidthSamplerTest, AckHeightRespectBandwidthEstimateUpperBound) { QuicBandwidth::FromBytesAndTimeDelta(kRegularPacketSize, time_between_packets); - // Send and ack packet 1. + // Send packets 1 to 4 and ack packet 1. SendPacket(1); clock_.AdvanceTime(time_between_packets); + SendPacket(2); + SendPacket(3); + SendPacket(4); BandwidthSampler::CongestionEventSample sample = OnCongestionEvent({1}, {}); EXPECT_EQ(first_packet_sending_rate, sample.sample_max_bandwidth); EXPECT_EQ(first_packet_sending_rate, max_bandwidth_); - // Send and ack packet 2, 3 and 4. + // Ack packet 2, 3 and 4, all of which uses S(1) to calculate ack rate since + // there were no acks at the time they were sent. round_trip_count_++; est_bandwidth_upper_bound_ = first_packet_sending_rate * 0.3; - SendPacket(2); - SendPacket(3); - SendPacket(4); clock_.AdvanceTime(time_between_packets); sample = OnCongestionEvent({2, 3, 4}, {}); - EXPECT_EQ(first_packet_sending_rate * 3, sample.sample_max_bandwidth); + EXPECT_EQ(first_packet_sending_rate * 2, sample.sample_max_bandwidth); EXPECT_EQ(max_bandwidth_, sample.sample_max_bandwidth); EXPECT_LT(2 * kRegularPacketSize, sample.extra_acked); @@ -710,6 +711,11 @@ class MaxAckHeightTrackerTest : public QuicTest { protected: MaxAckHeightTrackerTest() : tracker_(/*initial_filter_window=*/10) { tracker_.SetAckAggregationBandwidthThreshold(1.8); + + if (GetQuicReloadableFlag( + quic_bbr_start_new_aggregation_epoch_after_a_full_round)) { + tracker_.SetStartNewAggregationEpochAfterFullRound(true); + } } // Run a full aggregation episode, which is one or more aggregated acks, @@ -749,8 +755,9 @@ class MaxAckHeightTrackerTest : public QuicTest { QuicByteCount last_extra_acked = 0; for (QuicByteCount bytes = 0; bytes < aggregation_bytes; bytes += bytes_per_ack) { - QuicByteCount extra_acked = - tracker_.Update(bandwidth_, RoundTripCount(), now_, bytes_per_ack); + QuicByteCount extra_acked = tracker_.Update( + bandwidth_, true, RoundTripCount(), last_sent_packet_number_, + last_acked_packet_number_, now_, bytes_per_ack); QUIC_VLOG(1) << "T" << now_ << ": Update after " << bytes_per_ack << " bytes acked, " << extra_acked << " extra bytes acked"; // |extra_acked| should be 0 if either @@ -784,6 +791,8 @@ class MaxAckHeightTrackerTest : public QuicTest { QuicBandwidth bandwidth_ = QuicBandwidth::FromBytesPerSecond(10 * 1000); QuicTime now_ = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1); QuicTime::Delta rtt_ = QuicTime::Delta::FromMilliseconds(60); + QuicPacketNumber last_sent_packet_number_; + QuicPacketNumber last_acked_packet_number_; }; TEST_F(MaxAckHeightTrackerTest, VeryAggregatedLargeAck) { @@ -864,5 +873,26 @@ TEST_F(MaxAckHeightTrackerTest, NotAggregated) { EXPECT_LT(2u, tracker_.num_ack_aggregation_epochs()); } +TEST_F(MaxAckHeightTrackerTest, StartNewEpochAfterAFullRound) { + last_sent_packet_number_ = QuicPacketNumber(10); + AggregationEpisode(bandwidth_ * 2, QuicTime::Delta::FromMilliseconds(50), 100, + true); + + last_acked_packet_number_ = QuicPacketNumber(11); + // Update with a tiny bandwidth causes a very low expected bytes acked, which + // in turn causes the current epoch to continue if the |tracker_| doesn't + // check the packet numbers. + tracker_.Update(bandwidth_ * 0.1, true, RoundTripCount(), + last_sent_packet_number_, last_acked_packet_number_, now_, + 100); + + if (GetQuicReloadableFlag( + quic_bbr_start_new_aggregation_epoch_after_a_full_round)) { + EXPECT_EQ(2u, tracker_.num_ack_aggregation_epochs()); + } else { + EXPECT_EQ(1u, tracker_.num_ack_aggregation_epochs()); + } +} + } // namespace test } // namespace quic 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 6eb9b2ec940..b67b7df263f 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 @@ -102,6 +102,11 @@ void Bbr2NetworkModel::OnCongestionEventStart( lost_packets, MaxBandwidth(), bandwidth_lo(), RoundTripCount()); + if (sample.extra_acked == 0) { + cwnd_limited_before_aggregation_epoch_ = + congestion_event->prior_bytes_in_flight >= congestion_event->prior_cwnd; + } + if (sample.last_packet_send_state.is_valid) { congestion_event->last_packet_send_state = sample.last_packet_send_state; congestion_event->last_sample_is_app_limited = @@ -159,6 +164,10 @@ void Bbr2NetworkModel::OnCongestionEventStart( congestion_event->last_packet_send_state.total_bytes_acked; max_bytes_delivered_in_round_ = std::max(max_bytes_delivered_in_round_, bytes_delivered); + if (min_bytes_in_flight_in_round_ == 0 || + congestion_event->bytes_in_flight < min_bytes_in_flight_in_round_) { + min_bytes_in_flight_in_round_ = congestion_event->bytes_in_flight; + } } // |bandwidth_latest_| and |inflight_latest_| only increased within a round. @@ -325,14 +334,6 @@ bool Bbr2NetworkModel::MaybeExpireMinRtt( return true; } -bool Bbr2NetworkModel::IsCongestionWindowLimited( - const Bbr2CongestionEvent& congestion_event) const { - QuicByteCount prior_bytes_in_flight = congestion_event.bytes_in_flight + - congestion_event.bytes_acked + - congestion_event.bytes_lost; - return prior_bytes_in_flight >= congestion_event.prior_cwnd; -} - bool Bbr2NetworkModel::IsInflightTooHigh( const Bbr2CongestionEvent& congestion_event, int64_t max_loss_events) const { @@ -379,6 +380,7 @@ void Bbr2NetworkModel::OnNewRound() { bytes_lost_in_round_ = 0; loss_events_in_round_ = 0; max_bytes_delivered_in_round_ = 0; + min_bytes_in_flight_in_round_ = 0; } void Bbr2NetworkModel::cap_inflight_lo(QuicByteCount cap) { 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 d28036df543..d017b55b84c 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 @@ -9,6 +9,7 @@ #include <limits> #include "quic/core/congestion_control/bandwidth_sampler.h" +#include "quic/core/congestion_control/send_algorithm_interface.h" #include "quic/core/congestion_control/windowed_filter.h" #include "quic/core/quic_bandwidth.h" #include "quic/core/quic_packet_number.h" @@ -94,6 +95,10 @@ struct QUIC_EXPORT_PRIVATE Bbr2Params { // If false, exit STARTUP on loss only if bandwidth is below threshold. bool always_exit_startup_on_excess_loss = false; + // If true, inclue extra acked during STARTUP and proactively reduce extra + // acked when bandwidth increases. + bool startup_include_extra_acked = false; + /* * DRAIN parameters. */ @@ -130,6 +135,11 @@ struct QUIC_EXPORT_PRIVATE Bbr2Params { // Multiplier to get target inflight (as multiple of BDP) for PROBE_UP phase. float probe_bw_probe_inflight_gain = 1.25; + // When attempting to grow inflight_hi in PROBE_UP, check whether we are cwnd + // limited before the current aggregation epoch, instead of before the current + // ack event. + bool probe_bw_check_cwnd_limited_before_aggregation_epoch = false; + // Pacing gains. float probe_bw_probe_up_pacing_gain = 1.25; float probe_bw_probe_down_pacing_gain = 0.75; @@ -138,6 +148,13 @@ struct QUIC_EXPORT_PRIVATE Bbr2Params { float probe_bw_cwnd_gain = 2.0; /* + * PROBE_UP parameters. + */ + bool probe_up_includes_acks_after_cwnd_limited = false; + bool probe_up_dont_exit_if_no_queue_ = false; + bool probe_up_ignore_inflight_hi = false; + + /* * PROBE_RTT parameters. */ float probe_rtt_inflight_target_bdp_fraction = 0.5; @@ -386,6 +403,10 @@ class QUIC_EXPORT_PRIVATE Bbr2NetworkModel { return bandwidth_sampler_.max_ack_height(); } + bool cwnd_limited_before_aggregation_epoch() const { + return cwnd_limited_before_aggregation_epoch_; + } + void EnableOverestimateAvoidance() { bandwidth_sampler_.EnableOverestimateAvoidance(); } @@ -402,6 +423,22 @@ class QUIC_EXPORT_PRIVATE Bbr2NetworkModel { return bandwidth_sampler_.num_ack_aggregation_epochs(); } + void SetStartNewAggregationEpochAfterFullRound(bool value) { + bandwidth_sampler_.SetStartNewAggregationEpochAfterFullRound(value); + } + + void SetLimitMaxAckHeightTrackerBySendRate(bool value) { + bandwidth_sampler_.SetLimitMaxAckHeightTrackerBySendRate(value); + } + + void SetMaxAckHeightTrackerWindowLength(QuicRoundTripCount value) { + bandwidth_sampler_.SetMaxAckHeightTrackerWindowLength(value); + } + + void SetReduceExtraAckedOnBandwidthIncrease(bool value) { + bandwidth_sampler_.SetReduceExtraAckedOnBandwidthIncrease(value); + } + bool MaybeExpireMinRtt(const Bbr2CongestionEvent& congestion_event); QuicBandwidth BandwidthEstimate() const { @@ -412,9 +449,6 @@ class QUIC_EXPORT_PRIVATE Bbr2NetworkModel { return round_trip_counter_.Count(); } - bool IsCongestionWindowLimited( - const Bbr2CongestionEvent& congestion_event) const; - // Return true if the number of loss events exceeds max_loss_events and // fraction of bytes lost exceed the loss threshold. bool IsInflightTooHigh(const Bbr2CongestionEvent& congestion_event, @@ -459,6 +493,10 @@ class QUIC_EXPORT_PRIVATE Bbr2NetworkModel { return max_bytes_delivered_in_round_; } + QuicByteCount min_bytes_in_flight_in_round() const { + return min_bytes_in_flight_in_round_; + } + QuicPacketNumber end_of_app_limited_phase() const { return bandwidth_sampler_.end_of_app_limited_phase(); } @@ -529,6 +567,9 @@ class QUIC_EXPORT_PRIVATE Bbr2NetworkModel { // congestion event) was sent and acked, respectively. QuicByteCount max_bytes_delivered_in_round_ = 0; + // The minimum bytes in flight during this round. + QuicByteCount min_bytes_in_flight_in_round_ = 0; + // Max bandwidth in the current round. Updated once per congestion event. QuicBandwidth bandwidth_latest_ = QuicBandwidth::Zero(); // Max bandwidth of recent rounds. Updated once per round. @@ -546,6 +587,10 @@ class QUIC_EXPORT_PRIVATE Bbr2NetworkModel { float cwnd_gain_; float pacing_gain_; + // Whether we are cwnd limited prior to the start of the current aggregation + // epoch. + bool cwnd_limited_before_aggregation_epoch_ = false; + // STARTUP-centric fields which experimentally used by PROBE_UP. bool full_bandwidth_reached_ = false; QuicBandwidth full_bandwidth_baseline_ = QuicBandwidth::Zero(); 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 ce9443f7d1d..fbc7769ef9a 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 @@ -36,8 +36,7 @@ void Bbr2ProbeBwMode::Enter(QuicTime now, } Bbr2Mode Bbr2ProbeBwMode::OnCongestionEvent( - QuicByteCount prior_in_flight, - QuicTime event_time, + QuicByteCount prior_in_flight, QuicTime event_time, const AckedPacketVector& /*acked_packets*/, const LostPacketVector& /*lost_packets*/, const Bbr2CongestionEvent& congestion_event) { @@ -84,6 +83,11 @@ Limits<QuicByteCount> Bbr2ProbeBwMode::GetCwndLimits() const { return NoGreaterThan( std::min(model_->inflight_lo(), model_->inflight_hi_with_headroom())); } + if (Params().probe_up_ignore_inflight_hi && + cycle_.phase == CyclePhase::PROBE_UP) { + // Similar to STARTUP. + return NoGreaterThan(model_->inflight_lo()); + } return NoGreaterThan(std::min(model_->inflight_lo(), model_->inflight_hi())); } @@ -187,12 +191,20 @@ Bbr2ProbeBwMode::AdaptUpperBoundsResult Bbr2ProbeBwMode::MaybeAdaptUpperBounds( << congestion_event.last_packet_send_state.total_bytes_acked << ")"; } } + // TODO(ianswett): Inflight too high is really checking for loss, not + // inflight. if (model_->IsInflightTooHigh(congestion_event, Params().probe_bw_full_loss_count)) { if (cycle_.is_sample_from_probing) { cycle_.is_sample_from_probing = false; - - if (!send_state.is_app_limited) { + if (!send_state.is_app_limited || + Params().probe_up_dont_exit_if_no_queue_) { + if (send_state.is_app_limited) { + // If there's excess loss or a queue is building, exit even if the + // last sample was app limited. + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_no_probe_up_exit_if_no_queue, + 2, 2); + } const QuicByteCount inflight_target = sender_->GetTargetBytesInflight() * (1.0 - Params().beta); if (inflight_at_send >= inflight_target) { @@ -339,12 +351,53 @@ void Bbr2ProbeBwMode::RaiseInflightHighSlope() { void Bbr2ProbeBwMode::ProbeInflightHighUpward( const Bbr2CongestionEvent& congestion_event) { QUICHE_DCHECK_EQ(cycle_.phase, CyclePhase::PROBE_UP); - if (!model_->IsCongestionWindowLimited(congestion_event)) { - QUIC_DVLOG(3) << sender_ - << " Raising inflight_hi early return: Not cwnd limited."; - // Not fully utilizing cwnd, so can't safely grow. + if (Params().probe_up_ignore_inflight_hi) { + // When inflight_hi is disabled in PROBE_UP, it increases when + // the number of bytes delivered in a round is larger inflight_hi. return; } + if (Params().probe_bw_check_cwnd_limited_before_aggregation_epoch) { + if (!model_->cwnd_limited_before_aggregation_epoch()) { + QUIC_DVLOG(3) << sender_ + << " Raising inflight_hi early return: Not cwnd limited " + "before aggregation epoch."; + // Not fully utilizing cwnd, so can't safely grow. + return; + } + } else if (Params().probe_up_includes_acks_after_cwnd_limited) { + QUIC_RELOADABLE_FLAG_COUNT( + quic_bbr2_add_bytes_acked_after_inflight_hi_limited); + // Don't continue adding bytes to probe_up_acked if the sender was not + // app-limited after being inflight_hi limited at least once. + if (!cycle_.probe_up_app_limited_since_inflight_hi_limited_ || + congestion_event.last_sample_is_app_limited) { + cycle_.probe_up_app_limited_since_inflight_hi_limited_ = false; + if (congestion_event.prior_bytes_in_flight < + congestion_event.prior_cwnd) { + QUIC_DVLOG(3) << sender_ + << " Raising inflight_hi early return: Not cwnd limited."; + // Not fully utilizing cwnd, so can't safely grow. + return; + } + + if (congestion_event.prior_cwnd < model_->inflight_hi()) { + QUIC_DVLOG(3) + << sender_ + << " Raising inflight_hi early return: inflight_hi not fully used."; + // Not fully using inflight_hi, so don't grow it. + return; + } + } + // Start a new period of adding bytes_acked, because inflight_hi limited. + cycle_.probe_up_app_limited_since_inflight_hi_limited_ = true; + } else { + if (congestion_event.prior_bytes_in_flight < congestion_event.prior_cwnd) { + QUIC_DVLOG(3) << sender_ + << " Raising inflight_hi early return: Not cwnd limited."; + // Not fully utilizing cwnd, so can't safely grow. + return; + } + } if (congestion_event.prior_cwnd < model_->inflight_hi()) { QUIC_DVLOG(3) @@ -433,23 +486,32 @@ void Bbr2ProbeBwMode::UpdateProbeUp( } else if (cycle_.rounds_in_phase > 0) { const QuicByteCount bdp = model_->BDP(); QuicByteCount queuing_threshold_extra_bytes = 2 * kDefaultTCPMSS; - if (Params().add_ack_height_to_queueing_threshold) { - queuing_threshold_extra_bytes += model_->MaxAckHeight(); + if (Params().probe_up_dont_exit_if_no_queue_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_no_probe_up_exit_if_no_queue, 1, + 2); + is_queuing = congestion_event.end_of_round_trip && + model_->min_bytes_in_flight_in_round() > + (bdp * Params().probe_bw_probe_inflight_gain + + queuing_threshold_extra_bytes); + } else { + if (Params().add_ack_height_to_queueing_threshold) { + queuing_threshold_extra_bytes += model_->MaxAckHeight(); + } + QuicByteCount queuing_threshold = + (Params().probe_bw_probe_inflight_gain * bdp) + + queuing_threshold_extra_bytes; + + is_queuing = congestion_event.bytes_in_flight >= queuing_threshold; + + QUIC_DVLOG(3) << sender_ + << " Checking if building up a queue. prior_in_flight:" + << prior_in_flight + << ", post_in_flight:" << congestion_event.bytes_in_flight + << ", threshold:" << queuing_threshold + << ", is_queuing:" << is_queuing + << ", max_bw:" << model_->MaxBandwidth() + << ", min_rtt:" << model_->MinRtt(); } - QuicByteCount queuing_threshold = - (Params().probe_bw_probe_inflight_gain * bdp) + - queuing_threshold_extra_bytes; - - is_queuing = congestion_event.bytes_in_flight >= queuing_threshold; - - QUIC_DVLOG(3) << sender_ - << " Checking if building up a queue. prior_in_flight:" - << prior_in_flight - << ", post_in_flight:" << congestion_event.bytes_in_flight - << ", threshold:" << queuing_threshold - << ", is_queuing:" << is_queuing - << ", max_bw:" << model_->MaxBandwidth() - << ", min_rtt:" << model_->MinRtt(); } if (is_risky || is_queuing) { @@ -459,8 +521,7 @@ void Bbr2ProbeBwMode::UpdateProbeUp( } void Bbr2ProbeBwMode::EnterProbeDown(bool probed_too_high, - bool stopped_risky_probe, - QuicTime now) { + bool stopped_risky_probe, QuicTime now) { QUIC_DVLOG(2) << sender_ << " Phase change: " << cycle_.phase << " ==> " << CyclePhase::PROBE_DOWN << " after " << now - cycle_.phase_start_time << ", or " @@ -493,6 +554,7 @@ void Bbr2ProbeBwMode::EnterProbeDown(bool probed_too_high, Params().probe_bw_probe_max_rand_duration.ToMicroseconds())); cycle_.probe_up_bytes = std::numeric_limits<QuicByteCount>::max(); + cycle_.probe_up_app_limited_since_inflight_hi_limited_ = false; cycle_.has_advanced_max_bw = false; model_->RestartRoundEarly(); } @@ -600,9 +662,7 @@ std::ostream& operator<<(std::ostream& os, return os; } -const Bbr2Params& Bbr2ProbeBwMode::Params() const { - return sender_->Params(); -} +const Bbr2Params& Bbr2ProbeBwMode::Params() const { return sender_->Params(); } float Bbr2ProbeBwMode::PacingGainForPhase( Bbr2ProbeBwMode::CyclePhase phase) const { diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_probe_bw.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_probe_bw.h index 45b1df70c5c..832dfa14154 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_probe_bw.h +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_probe_bw.h @@ -118,6 +118,7 @@ class QUIC_EXPORT_PRIVATE Bbr2ProbeBwMode final : public Bbr2ModeBase { uint64_t probe_up_rounds = 0; QuicByteCount probe_up_bytes = std::numeric_limits<QuicByteCount>::max(); QuicByteCount probe_up_acked = 0; + bool probe_up_app_limited_since_inflight_hi_limited_ = false; // Whether max bandwidth filter window has advanced in this cycle. It is // advanced once per cycle. bool has_advanced_max_bw = false; 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 09b920e5759..1e276490eee 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 @@ -120,6 +120,16 @@ void Bbr2Sender::SetFromConfig(const QuicConfig& config, void Bbr2Sender::ApplyConnectionOptions( const QuicTagVector& connection_options) { + if (GetQuicReloadableFlag(quic_bbr2_extra_acked_window) && + ContainsQuicTag(connection_options, kBBR4)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_extra_acked_window, 1, 2); + model_.SetMaxAckHeightTrackerWindowLength(20); + } + if (GetQuicReloadableFlag(quic_bbr2_extra_acked_window) && + ContainsQuicTag(connection_options, kBBR5)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_extra_acked_window, 2, 2); + model_.SetMaxAckHeightTrackerWindowLength(40); + } if (ContainsQuicTag(connection_options, kBBQ2)) { params_.startup_cwnd_gain = 2.885; params_.drain_cwnd_gain = 2.885; @@ -158,6 +168,54 @@ void Bbr2Sender::ApplyConnectionOptions( if (ContainsQuicTag(connection_options, kBBQ9)) { params_.bw_lo_mode_ = Bbr2Params::QuicBandwidthLoMode::CWND_REDUCTION; } + if (GetQuicReloadableFlag( + quic_bbr2_check_cwnd_limited_before_aggregation_epoch) && + ContainsQuicTag(connection_options, kB201)) { + QUIC_RELOADABLE_FLAG_COUNT( + quic_bbr2_check_cwnd_limited_before_aggregation_epoch); + params_.probe_bw_check_cwnd_limited_before_aggregation_epoch = true; + } + if (GetQuicReloadableFlag(quic_bbr2_no_probe_up_exit_if_no_queue) && + ContainsQuicTag(connection_options, kB202)) { + params_.probe_up_dont_exit_if_no_queue_ = true; + } + if (GetQuicReloadableFlag(quic_bbr2_ignore_inflight_hi_in_probe_up) && + ContainsQuicTag(connection_options, kB203)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_ignore_inflight_hi_in_probe_up); + params_.probe_up_ignore_inflight_hi = true; + } + if (GetQuicReloadableFlag(quic_bbr2_startup_extra_acked) && + ContainsQuicTag(connection_options, kB204)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_startup_extra_acked, 1, 2); + model_.SetReduceExtraAckedOnBandwidthIncrease(true); + } + if (GetQuicReloadableFlag(quic_bbr2_startup_extra_acked) && + ContainsQuicTag(connection_options, kB205)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_startup_extra_acked, 2, 2); + params_.startup_include_extra_acked = true; + } + if (GetQuicReloadableFlag( + quic_bbr_start_new_aggregation_epoch_after_a_full_round) && + ContainsQuicTag(connection_options, kBBRA)) { + model_.SetStartNewAggregationEpochAfterFullRound(true); + } + if (GetQuicReloadableFlag(quic_bbr_use_send_rate_in_max_ack_height_tracker) && + ContainsQuicTag(connection_options, kBBRB)) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_bbr_use_send_rate_in_max_ack_height_tracker, 2, 2); + model_.SetLimitMaxAckHeightTrackerBySendRate(true); + } + if (GetQuicReloadableFlag( + quic_bbr2_add_bytes_acked_after_inflight_hi_limited) && + ContainsQuicTag(connection_options, kBBQ0)) { + params_.probe_up_includes_acks_after_cwnd_limited = true; + } + + if (GetQuicReloadableFlag(quic_bbr2_startup_probe_up_loss_events) && + ContainsQuicTag(connection_options, kB206)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_startup_probe_up_loss_events); + params_.startup_full_loss_count = params_.probe_bw_full_loss_count; + } } Limits<QuicByteCount> Bbr2Sender::GetCwndLimitsByMode() const { @@ -344,7 +402,7 @@ void Bbr2Sender::UpdateCongestionWindow(QuicByteCount bytes_acked) { QuicByteCount target_cwnd = GetTargetCongestionWindow(model_.cwnd_gain()); const QuicByteCount prior_cwnd = cwnd_; - if (model_.full_bandwidth_reached()) { + if (model_.full_bandwidth_reached() || Params().startup_include_extra_acked) { target_cwnd += model_.MaxAckHeight(); cwnd_ = std::min(prior_cwnd + bytes_acked, target_cwnd); } else if (prior_cwnd < target_cwnd || prior_cwnd < 2 * initial_cwnd_) { 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 ae0c8842c8e..490996bb087 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 @@ -202,6 +202,8 @@ class Bbr2DefaultTopologyTest : public Bbr2SimulatorTest { GetQuicFlag(FLAGS_quic_max_congestion_window), &random_, QuicConnectionPeer::GetStats(endpoint->connection()), old_sender); QuicConnectionPeer::SetSendAlgorithm(endpoint->connection(), sender); + const int kTestMaxPacketSize = 1350; + endpoint->connection()->SetMaxPacketLength(kTestMaxPacketSize); endpoint->RecordTrace(); return sender; } @@ -308,11 +310,15 @@ class Bbr2DefaultTopologyTest : public Bbr2SimulatorTest { } void SetConnectionOption(QuicTag option) { + SetConnectionOption(std::move(option), sender_); + } + + void SetConnectionOption(QuicTag option, Bbr2Sender* sender) { QuicConfig config; QuicTagVector options; options.push_back(option); QuicConfigPeer::SetReceivedConnectionOptions(&config, options); - sender_->SetFromConfig(config, Perspective::IS_SERVER); + sender->SetFromConfig(config, Perspective::IS_SERVER); } bool Bbr2ModeIsOneOf(const std::vector<Bbr2Mode>& expected_modes) const { @@ -436,6 +442,107 @@ TEST_F(Bbr2DefaultTopologyTest, SimpleTransferB2RC) { EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f); } +TEST_F(Bbr2DefaultTopologyTest, SimpleTransferB201) { + SetQuicReloadableFlag(quic_bbr2_check_cwnd_limited_before_aggregation_epoch, + true); + SetConnectionOption(kB201); + DefaultTopologyParams params; + CreateNetwork(params); + + // Transfer 12MB. + DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); + EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); + + EXPECT_APPROX_EQ(params.BottleneckBandwidth(), + sender_->ExportDebugState().bandwidth_hi, 0.01f); + + EXPECT_LE(sender_loss_rate_in_packets(), 0.05); + // The margin here is high, because the aggregation greatly increases + // smoothed rtt. + EXPECT_GE(params.RTT() * 4, rtt_stats()->smoothed_rtt()); + EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f); +} + +TEST_F(Bbr2DefaultTopologyTest, SimpleTransferB206) { + SetQuicReloadableFlag(quic_bbr2_startup_probe_up_loss_events, true); + SetConnectionOption(kB206); + DefaultTopologyParams params; + CreateNetwork(params); + + // Transfer 12MB. + DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); + EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); + + EXPECT_APPROX_EQ(params.BottleneckBandwidth(), + sender_->ExportDebugState().bandwidth_hi, 0.01f); + + EXPECT_LE(sender_loss_rate_in_packets(), 0.05); + // The margin here is high, because the aggregation greatly increases + // smoothed rtt. + EXPECT_GE(params.RTT() * 4, rtt_stats()->smoothed_rtt()); + EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f); +} + +TEST_F(Bbr2DefaultTopologyTest, SimpleTransferBBRB) { + SetQuicReloadableFlag(quic_bbr_use_send_rate_in_max_ack_height_tracker, true); + SetConnectionOption(kBBRB); + DefaultTopologyParams params; + CreateNetwork(params); + + // Transfer 12MB. + DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); + EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); + + EXPECT_APPROX_EQ(params.BottleneckBandwidth(), + sender_->ExportDebugState().bandwidth_hi, 0.01f); + + EXPECT_LE(sender_loss_rate_in_packets(), 0.05); + // The margin here is high, because the aggregation greatly increases + // smoothed rtt. + EXPECT_GE(params.RTT() * 4, rtt_stats()->smoothed_rtt()); + EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f); +} + +TEST_F(Bbr2DefaultTopologyTest, SimpleTransferBBR4) { + SetQuicReloadableFlag(quic_bbr2_extra_acked_window, true); + SetConnectionOption(kBBR4); + DefaultTopologyParams params; + CreateNetwork(params); + + // Transfer 12MB. + DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); + EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); + + EXPECT_APPROX_EQ(params.BottleneckBandwidth(), + sender_->ExportDebugState().bandwidth_hi, 0.01f); + + EXPECT_LE(sender_loss_rate_in_packets(), 0.05); + // The margin here is high, because the aggregation greatly increases + // smoothed rtt. + EXPECT_GE(params.RTT() * 4, rtt_stats()->smoothed_rtt()); + EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f); +} + +TEST_F(Bbr2DefaultTopologyTest, SimpleTransferBBR5) { + SetQuicReloadableFlag(quic_bbr2_extra_acked_window, true); + SetConnectionOption(kBBR5); + DefaultTopologyParams params; + CreateNetwork(params); + + // Transfer 12MB. + DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); + EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); + + EXPECT_APPROX_EQ(params.BottleneckBandwidth(), + sender_->ExportDebugState().bandwidth_hi, 0.01f); + + EXPECT_LE(sender_loss_rate_in_packets(), 0.05); + // The margin here is high, because the aggregation greatly increases + // smoothed rtt. + EXPECT_GE(params.RTT() * 4, rtt_stats()->smoothed_rtt()); + EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f); +} + TEST_F(Bbr2DefaultTopologyTest, SimpleTransferSmallBuffer) { DefaultTopologyParams params; params.switch_queue_capacity_in_bdp = 0.5; @@ -488,6 +595,34 @@ TEST_F(Bbr2DefaultTopologyTest, SimpleTransfer2RTTAggregationBytes) { EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f); } +TEST_F(Bbr2DefaultTopologyTest, SimpleTransfer2RTTAggregationBytesB201) { + SetQuicReloadableFlag(quic_bbr2_check_cwnd_limited_before_aggregation_epoch, + true); + SetConnectionOption(kB201); + DefaultTopologyParams params; + CreateNetwork(params); + // 2 RTTs of aggregation, with a max of 10kb. + EnableAggregation(10 * 1024, 2 * params.RTT()); + + // Transfer 12MB. + DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35)); + EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); + + // TODO(wub): Tighten the error bound once BSAO is default enabled. + EXPECT_APPROX_EQ(params.BottleneckBandwidth(), + sender_->ExportDebugState().bandwidth_hi, 0.5f); + + if (GetQuicReloadableFlag(quic_fix_pacing_sender_bursts)) { + EXPECT_LE(sender_loss_rate_in_packets(), 0.01); + } else { + EXPECT_LE(sender_loss_rate_in_packets(), 0.05); + } + // The margin here is high, because both link level aggregation and ack + // decimation can greatly increase smoothed rtt. + EXPECT_GE(params.RTT() * 5, rtt_stats()->smoothed_rtt()); + EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f); +} + TEST_F(Bbr2DefaultTopologyTest, SimpleTransferAckDecimation) { SetConnectionOption(kBSAO); DefaultTopologyParams params; @@ -567,6 +702,371 @@ TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncrease)) { sender_->ExportDebugState().bandwidth_hi, 0.02f); } +// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with BBQ0 +TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncreaseBBQ0)) { + SetQuicReloadableFlag(quic_bbr2_add_bytes_acked_after_inflight_hi_limited, + true); + SetConnectionOption(kBBQ0); + DefaultTopologyParams params; + params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000); + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100); + CreateNetwork(params); + + sender_endpoint_.AddBytesToTransfer(10 * 1024 * 1024); + + simulator_.RunFor(QuicTime::Delta::FromSeconds(15)); + EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); + QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow(); + + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_est, 0.1f); + EXPECT_LE(sender_loss_rate_in_packets(), 0.30); + + // Now increase the bottleneck bandwidth from 100Kbps to 10Mbps. + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000); + TestLink()->set_bandwidth(params.test_link.bandwidth); + + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_endpoint_.bytes_to_transfer() == 0; }, + QuicTime::Delta::FromSeconds(50)); + EXPECT_TRUE(simulator_result); + // Ensure the full bandwidth is discovered. + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_hi, 0.02f); +} + +// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with BBQ0 +// in the presence of ACK aggregation. +TEST_F(Bbr2DefaultTopologyTest, + QUIC_SLOW_TEST(BandwidthIncreaseBBQ0Aggregation)) { + SetQuicReloadableFlag(quic_bbr2_add_bytes_acked_after_inflight_hi_limited, + true); + SetConnectionOption(kBBQ0); + DefaultTopologyParams params; + params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000); + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100); + CreateNetwork(params); + + // 2 RTTs of aggregation, with a max of 10kb. + EnableAggregation(10 * 1024, 2 * params.RTT()); + + // Reduce the payload to 2MB because 10MB takes too long. + sender_endpoint_.AddBytesToTransfer(2 * 1024 * 1024); + + simulator_.RunFor(QuicTime::Delta::FromSeconds(15)); + EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); + QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow(); + + // This is much farther off when aggregation is present, + // Ideally BSAO or another option would fix this. + // TODO(ianswett) Make these bound tighter once overestimation is reduced. + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_est, 0.6f); + EXPECT_LE(sender_loss_rate_in_packets(), 0.35); + + // Now increase the bottleneck bandwidth from 100Kbps to 10Mbps. + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000); + TestLink()->set_bandwidth(params.test_link.bandwidth); + + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_endpoint_.bytes_to_transfer() == 0; }, + QuicTime::Delta::FromSeconds(50)); + EXPECT_TRUE(simulator_result); + // Ensure at least 10% of full bandwidth is discovered. + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_hi, 0.90f); +} + +// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B202 +TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncreaseB202)) { + SetQuicReloadableFlag(quic_bbr2_no_probe_up_exit_if_no_queue, true); + SetConnectionOption(kB202); + DefaultTopologyParams params; + params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000); + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100); + CreateNetwork(params); + + sender_endpoint_.AddBytesToTransfer(10 * 1024 * 1024); + + simulator_.RunFor(QuicTime::Delta::FromSeconds(15)); + EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); + QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow(); + + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_est, 0.1f); + EXPECT_LE(sender_loss_rate_in_packets(), 0.30); + + // Now increase the bottleneck bandwidth from 100Kbps to 10Mbps. + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000); + TestLink()->set_bandwidth(params.test_link.bandwidth); + + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_endpoint_.bytes_to_transfer() == 0; }, + QuicTime::Delta::FromSeconds(50)); + EXPECT_TRUE(simulator_result); + // Ensure the full bandwidth is discovered. + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_hi, 0.1f); +} + +// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B202 +// in the presence of ACK aggregation. +TEST_F(Bbr2DefaultTopologyTest, DISABLED_BandwidthIncreaseB202Aggregation) { + // TODO(b/201532297) Reenable this test. + SetQuicReloadableFlag(quic_bbr2_no_probe_up_exit_if_no_queue, true); + SetConnectionOption(kB202); + DefaultTopologyParams params; + params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000); + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100); + CreateNetwork(params); + + // 2 RTTs of aggregation, with a max of 10kb. + EnableAggregation(10 * 1024, 2 * params.RTT()); + + // Reduce the payload to 2MB because 10MB takes too long. + sender_endpoint_.AddBytesToTransfer(2 * 1024 * 1024); + + simulator_.RunFor(QuicTime::Delta::FromSeconds(15)); + EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); + QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow(); + + // This is much farther off when aggregation is present, + // Ideally BSAO or another option would fix this. + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_est, 0.45f); + EXPECT_LE(sender_loss_rate_in_packets(), 0.35); + + // Now increase the bottleneck bandwidth from 100Kbps to 10Mbps. + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000); + TestLink()->set_bandwidth(params.test_link.bandwidth); + + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_endpoint_.bytes_to_transfer() == 0; }, + QuicTime::Delta::FromSeconds(50)); + EXPECT_TRUE(simulator_result); + // Ensure at least 10% of full bandwidth is discovered. + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_hi, 0.92f); +} + +// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B203 +TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncreaseB203)) { + SetQuicReloadableFlag(quic_bbr2_ignore_inflight_hi_in_probe_up, true); + SetConnectionOption(kB203); + DefaultTopologyParams params; + params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000); + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100); + CreateNetwork(params); + + sender_endpoint_.AddBytesToTransfer(10 * 1024 * 1024); + + simulator_.RunFor(QuicTime::Delta::FromSeconds(15)); + EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); + QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow(); + + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_est, 0.1f); + EXPECT_LE(sender_loss_rate_in_packets(), 0.30); + + // Now increase the bottleneck bandwidth from 100Kbps to 10Mbps. + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000); + TestLink()->set_bandwidth(params.test_link.bandwidth); + + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_endpoint_.bytes_to_transfer() == 0; }, + QuicTime::Delta::FromSeconds(50)); + EXPECT_TRUE(simulator_result); + // Ensure the full bandwidth is discovered. + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_hi, 0.02f); +} + +// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B203 +// in the presence of ACK aggregation. +TEST_F(Bbr2DefaultTopologyTest, + QUIC_SLOW_TEST(BandwidthIncreaseB203Aggregation)) { + SetQuicReloadableFlag(quic_bbr2_ignore_inflight_hi_in_probe_up, true); + SetConnectionOption(kB203); + DefaultTopologyParams params; + params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000); + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100); + CreateNetwork(params); + + // 2 RTTs of aggregation, with a max of 10kb. + EnableAggregation(10 * 1024, 2 * params.RTT()); + + // Reduce the payload to 2MB because 10MB takes too long. + sender_endpoint_.AddBytesToTransfer(2 * 1024 * 1024); + + simulator_.RunFor(QuicTime::Delta::FromSeconds(15)); + EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); + QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow(); + + // This is much farther off when aggregation is present, + // Ideally BSAO or another option would fix this. + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_est, 0.60f); + EXPECT_LE(sender_loss_rate_in_packets(), 0.35); + + // Now increase the bottleneck bandwidth from 100Kbps to 10Mbps. + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000); + TestLink()->set_bandwidth(params.test_link.bandwidth); + + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_endpoint_.bytes_to_transfer() == 0; }, + QuicTime::Delta::FromSeconds(50)); + EXPECT_TRUE(simulator_result); + // Ensure at least 10% of full bandwidth is discovered. + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_hi, 0.91f); +} + +// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B204 +TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncreaseB204)) { + SetQuicReloadableFlag(quic_bbr2_startup_extra_acked, true); + SetConnectionOption(kB204); + DefaultTopologyParams params; + params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000); + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100); + CreateNetwork(params); + + sender_endpoint_.AddBytesToTransfer(10 * 1024 * 1024); + + simulator_.RunFor(QuicTime::Delta::FromSeconds(15)); + EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); + QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow(); + + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_est, 0.1f); + EXPECT_LE(sender_loss_rate_in_packets(), 0.25); + EXPECT_LE(sender_->ExportDebugState().max_ack_height, 2000u); + + // Now increase the bottleneck bandwidth from 100Kbps to 10Mbps. + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000); + TestLink()->set_bandwidth(params.test_link.bandwidth); + + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_endpoint_.bytes_to_transfer() == 0; }, + QuicTime::Delta::FromSeconds(50)); + EXPECT_TRUE(simulator_result); + // Ensure the full bandwidth is discovered. + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_hi, 0.02f); +} + +// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B204 +// in the presence of ACK aggregation. +TEST_F(Bbr2DefaultTopologyTest, + QUIC_SLOW_TEST(BandwidthIncreaseB204Aggregation)) { + SetQuicReloadableFlag(quic_bbr2_startup_extra_acked, true); + SetConnectionOption(kB204); + DefaultTopologyParams params; + params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000); + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100); + CreateNetwork(params); + + // 2 RTTs of aggregation, with a max of 10kb. + EnableAggregation(10 * 1024, 2 * params.RTT()); + + // Reduce the payload to 2MB because 10MB takes too long. + sender_endpoint_.AddBytesToTransfer(2 * 1024 * 1024); + + simulator_.RunFor(QuicTime::Delta::FromSeconds(15)); + EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); + QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow(); + + // This is much farther off when aggregation is present, + // Ideally BSAO or another option would fix this. + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_est, 0.55f); + EXPECT_LE(sender_loss_rate_in_packets(), 0.35); + EXPECT_LE(sender_->ExportDebugState().max_ack_height, 10000u); + + // Now increase the bottleneck bandwidth from 100Kbps to 10Mbps. + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000); + TestLink()->set_bandwidth(params.test_link.bandwidth); + + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_endpoint_.bytes_to_transfer() == 0; }, + QuicTime::Delta::FromSeconds(50)); + EXPECT_TRUE(simulator_result); + // Ensure at least 10% of full bandwidth is discovered. + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_hi, 0.95f); +} + +// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B205 +TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncreaseB205)) { + SetQuicReloadableFlag(quic_bbr2_startup_extra_acked, true); + SetConnectionOption(kB205); + DefaultTopologyParams params; + params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000); + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100); + CreateNetwork(params); + + sender_endpoint_.AddBytesToTransfer(10 * 1024 * 1024); + + simulator_.RunFor(QuicTime::Delta::FromSeconds(15)); + EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); + QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow(); + + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_est, 0.1f); + EXPECT_LE(sender_loss_rate_in_packets(), 0.10); + + // Now increase the bottleneck bandwidth from 100Kbps to 10Mbps. + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000); + TestLink()->set_bandwidth(params.test_link.bandwidth); + + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_endpoint_.bytes_to_transfer() == 0; }, + QuicTime::Delta::FromSeconds(50)); + EXPECT_TRUE(simulator_result); + // Ensure the full bandwidth is discovered. + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_hi, 0.1f); +} + +// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B205 +// in the presence of ACK aggregation. +TEST_F(Bbr2DefaultTopologyTest, + QUIC_SLOW_TEST(BandwidthIncreaseB205Aggregation)) { + SetQuicReloadableFlag(quic_bbr2_startup_extra_acked, true); + SetConnectionOption(kB205); + DefaultTopologyParams params; + params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000); + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100); + CreateNetwork(params); + + // 2 RTTs of aggregation, with a max of 10kb. + EnableAggregation(10 * 1024, 2 * params.RTT()); + + // Reduce the payload to 2MB because 10MB takes too long. + sender_endpoint_.AddBytesToTransfer(2 * 1024 * 1024); + + simulator_.RunFor(QuicTime::Delta::FromSeconds(15)); + EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT})); + QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow(); + + // This is much farther off when aggregation is present, + // Ideally BSAO or another option would fix this. + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_est, 0.45f); + EXPECT_LE(sender_loss_rate_in_packets(), 0.15); + + // Now increase the bottleneck bandwidth from 100Kbps to 10Mbps. + params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000); + TestLink()->set_bandwidth(params.test_link.bandwidth); + + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return sender_endpoint_.bytes_to_transfer() == 0; }, + QuicTime::Delta::FromSeconds(50)); + EXPECT_TRUE(simulator_result); + // Ensure at least 5% of full bandwidth is discovered. + EXPECT_APPROX_EQ(params.test_link.bandwidth, + sender_->ExportDebugState().bandwidth_hi, 0.9f); +} + // Test the number of losses incurred by the startup phase in a situation when // the buffer is less than BDP. TEST_F(Bbr2DefaultTopologyTest, PacketLossOnSmallBufferStartup) { @@ -911,7 +1411,10 @@ TEST_F(Bbr2DefaultTopologyTest, StartupStats) { ASSERT_FALSE(sender_->InSlowStart()); const QuicConnectionStats& stats = sender_connection_stats(); - EXPECT_EQ(1u, stats.slowstart_count); + // The test explicitly replaces the default-created send algorithm with the + // one created by the test. slowstart_count increaments every time a BBR + // sender is created. + EXPECT_GE(stats.slowstart_count, 1u); EXPECT_FALSE(stats.slowstart_duration.IsRunning()); EXPECT_THAT(stats.slowstart_duration.GetTotalElapsedTime(), AllOf(Ge(QuicTime::Delta::FromMilliseconds(500)), @@ -1381,6 +1884,12 @@ class Bbr2MultiSenderTest : public Bbr2SimulatorTest { kDefaultInitialCwndPackets, GetQuicFlag(FLAGS_quic_max_congestion_window), &random_, QuicConnectionPeer::GetStats(endpoint->connection()), nullptr); + if (GetQuicReloadableFlag( + quic_bbr_start_new_aggregation_epoch_after_a_full_round)) { + // TODO(ianswett): Add dedicated tests for this option until it becomes + // the default behavior. + SetConnectionOption(sender, kBBRA); + } QuicConnectionPeer::SetSendAlgorithm(endpoint->connection(), sender); endpoint->RecordTrace(); return sender; diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_startup.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_startup.cc index 1bd89c6024f..f882c02f599 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_startup.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr2_startup.cc @@ -17,12 +17,12 @@ Bbr2StartupMode::Bbr2StartupMode(const Bbr2Sender* sender, Bbr2NetworkModel* model, QuicTime now) : Bbr2ModeBase(sender, model) { - // Clear some startup stats if |sender_->connection_stats_| has been used by - // another sender, which happens e.g. when QuicConnection switch send - // algorithms. - sender_->connection_stats_->slowstart_count = 1; - sender_->connection_stats_->slowstart_duration = QuicTimeAccumulator(); - sender_->connection_stats_->slowstart_duration.Start(now); + // Increment, instead of reset startup stats, so we don't lose data recorded + // before QuicConnection switched send algorithm to BBRv2. + ++sender_->connection_stats_->slowstart_count; + if (!sender_->connection_stats_->slowstart_duration.IsRunning()) { + sender_->connection_stats_->slowstart_duration.Start(now); + } // Enter() is never called for Startup, so the gains needs to be set here. model_->set_pacing_gain(Params().startup_pacing_gain); model_->set_cwnd_gain(Params().startup_cwnd_gain); 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 940fe260731..ba7a35716a7 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 @@ -278,6 +278,17 @@ void BbrSender::ApplyConnectionOptions( if (ContainsQuicTag(connection_options, kBSAO)) { sampler_.EnableOverestimateAvoidance(); } + if (GetQuicReloadableFlag( + quic_bbr_start_new_aggregation_epoch_after_a_full_round) && + ContainsQuicTag(connection_options, kBBRA)) { + sampler_.SetStartNewAggregationEpochAfterFullRound(true); + } + if (GetQuicReloadableFlag(quic_bbr_use_send_rate_in_max_ack_height_tracker) && + ContainsQuicTag(connection_options, kBBRB)) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_bbr_use_send_rate_in_max_ack_height_tracker, 1, 2); + sampler_.SetLimitMaxAckHeightTrackerBySendRate(true); + } } void BbrSender::AdjustNetworkParameters(const NetworkParams& params) { 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 c1dbd22216e..ea6410ff753 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 @@ -185,16 +185,10 @@ class QUIC_EXPORT_PRIVATE BbrSender : public SendAlgorithmInterface { QuicRoundTripCount, QuicRoundTripCount>; - // Returns whether the connection has achieved full bandwidth required to exit - // the slow start. - bool IsAtFullBandwidth() const; // Computes the target congestion window using the specified gain. QuicByteCount GetTargetCongestionWindow(float gain) const; // The target congestion window during PROBE_RTT. QuicByteCount ProbeRttCongestionWindow() const; - // Returns true if the current min_rtt should be kept and we should not enter - // PROBE_RTT immediately. - bool ShouldExtendMinRttExpiry() const; bool MaybeUpdateMinRtt(QuicTime now, QuicTime::Delta sample_min_rtt); // Enters the STARTUP mode. 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 4a5cb0f0092..754cf20dfa7 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 @@ -10,6 +10,7 @@ #include <utility> #include "quic/core/congestion_control/rtt_stats.h" +#include "quic/core/crypto/crypto_protocol.h" #include "quic/core/quic_bandwidth.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_types.h" @@ -109,7 +110,13 @@ class BbrSenderTest : public QuicTest { receiver_multiplexer_("Receiver multiplexer", {&receiver_, &competing_receiver_}) { rtt_stats_ = bbr_sender_.connection()->sent_packet_manager().GetRttStats(); + const int kTestMaxPacketSize = 1350; + bbr_sender_.connection()->SetMaxPacketLength(kTestMaxPacketSize); sender_ = SetupBbrSender(&bbr_sender_); + if (GetQuicReloadableFlag( + quic_bbr_start_new_aggregation_epoch_after_a_full_round)) { + SetConnectionOption(kBBRA); + } clock_ = simulator_.GetClock(); } @@ -322,6 +329,39 @@ TEST_F(BbrSenderTest, SimpleTransfer) { EXPECT_APPROX_EQ(kTestRtt, rtt_stats_->smoothed_rtt(), 0.2f); } +TEST_F(BbrSenderTest, SimpleTransferBBRB) { + SetQuicReloadableFlag(quic_bbr_use_send_rate_in_max_ack_height_tracker, true); + SetConnectionOption(kBBRB); + CreateDefaultSetup(); + + // At startup make sure we are at the default. + EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); + // At startup make sure we can send. + EXPECT_TRUE(sender_->CanSend(0)); + // And that window is un-affected. + EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); + + // Verify that Sender is in slow start. + EXPECT_TRUE(sender_->InSlowStart()); + + // Verify that pacing rate is based on the initial RTT. + QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta( + 2.885 * kDefaultWindowTCP, rtt_stats_->initial_rtt()); + EXPECT_APPROX_EQ(expected_pacing_rate.ToBitsPerSecond(), + sender_->PacingRate(0).ToBitsPerSecond(), 0.01f); + + ASSERT_GE(kTestBdp, kDefaultWindowTCP + kDefaultTCPMSS); + + DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(30)); + EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode); + EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost); + EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited); + + // The margin here is quite high, since there exists a possibility that the + // connection just exited high gain cycle. + EXPECT_APPROX_EQ(kTestRtt, rtt_stats_->smoothed_rtt(), 0.2f); +} + // Test a simple transfer in a situation when the buffer is less than BDP. TEST_F(BbrSenderTest, SimpleTransferSmallBuffer) { CreateSmallBufferSetup(); diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.cc index 265a422817d..e4b6534e6fc 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.cc @@ -97,7 +97,8 @@ void PacingSender::OnPacketSent( GetQuicFlag(FLAGS_quic_lumpy_pacing_cwnd_fraction)) / kDefaultTCPMSS))); if (sender_->BandwidthEstimate() < - QuicBandwidth::FromKBitsPerSecond(1200)) { + QuicBandwidth::FromKBitsPerSecond( + GetQuicFlag(FLAGS_quic_lumpy_pacing_min_bandwidth_kbps))) { // Below 1.2Mbps, send 1 packet at once, because one full-sized packet // is about 10ms of queueing. lumpy_tokens_ = 1u; diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_test.cc index 4272c0bb173..d4c607063ac 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_test.cc @@ -175,6 +175,8 @@ class SendAlgorithmTest : public QuicTestWithParam<TestParams> { quic_sender_.RecordTrace(); QuicConnectionPeer::SetSendAlgorithm(quic_sender_.connection(), sender_); + const int kTestMaxPacketSize = 1350; + quic_sender_.connection()->SetMaxPacketLength(kTestMaxPacketSize); clock_ = simulator_.GetClock(); simulator_.set_random_generator(&random_); diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/windowed_filter.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/windowed_filter.h index 8949a9cab9a..a777ac5fc74 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/windowed_filter.h +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/windowed_filter.h @@ -71,6 +71,7 @@ class QUIC_EXPORT_PRIVATE WindowedFilter { WindowedFilter(TimeDeltaT window_length, T zero_value, TimeT zero_time) : window_length_(window_length), zero_value_(zero_value), + zero_time_(zero_time), estimates_{Sample(zero_value_, zero_time), Sample(zero_value_, zero_time), Sample(zero_value_, zero_time)} {} @@ -138,6 +139,8 @@ class QUIC_EXPORT_PRIVATE WindowedFilter { Sample(new_sample, new_time); } + void Clear() { Reset(zero_value_, zero_time_); } + T GetBest() const { return estimates_[0].sample; } T GetSecondBest() const { return estimates_[1].sample; } T GetThirdBest() const { return estimates_[2].sample; } @@ -152,6 +155,7 @@ class QUIC_EXPORT_PRIVATE WindowedFilter { TimeDeltaT window_length_; // Time length of window. T zero_value_; // Uninitialized value of T. + TimeT zero_time_; // Uninitialized value of TimeT. Sample estimates_[3]; // Best estimate is element 0. }; 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 d594f487549..f0bcd7f723e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h @@ -28,7 +28,8 @@ using ServerConfigID = std::string; // The following tags have been deprecated and should not be reused: // "1CON", "BBQ4", "NCON", "RCID", "SREJ", "TBKP", "TB10", "SCLS", "SMHL", -// "QNZR", "B2HI", "H2PR", "FIFO", "LIFO", "RRWS", "QNSP", "B2CL" +// "QNZR", "B2HI", "H2PR", "FIFO", "LIFO", "RRWS", "QNSP", "B2CL", "CHSP", +// "BPTE", "ACKD", "AKD2", "AKD4", "MAD1", "MAD4", "MAD5", "ACD0", "ACKQ" // clang-format off const QuicTag kCHLO = TAG('C', 'H', 'L', 'O'); // Client hello @@ -98,6 +99,11 @@ const QuicTag kBBR3 = TAG('B', 'B', 'R', '3'); // Fully drain the queue once const QuicTag kBBR4 = TAG('B', 'B', 'R', '4'); // 20 RTT ack aggregation const QuicTag kBBR5 = TAG('B', 'B', 'R', '5'); // 40 RTT ack aggregation const QuicTag kBBR9 = TAG('B', 'B', 'R', '9'); // DEPRECATED +const QuicTag kBBRA = TAG('B', 'B', 'R', 'A'); // Starts a new ack aggregation + // epoch if a full round has + // passed +const QuicTag kBBRB = TAG('B', 'B', 'R', 'B'); // Use send rate in BBR's + // MaxAckHeightTracker const QuicTag kBBRS = TAG('B', 'B', 'R', 'S'); // DEPRECATED const QuicTag kBBQ1 = TAG('B', 'B', 'Q', '1'); // BBR with lower 2.77 STARTUP // pacing and CWND gain. @@ -115,6 +121,8 @@ const QuicTag kBBQ7 = TAG('B', 'B', 'Q', '7'); // Reduce bw_lo by const QuicTag kBBQ8 = TAG('B', 'B', 'Q', '8'); // Reduce bw_lo by // bw_lo * bytes_lost/inflight const QuicTag kBBQ9 = TAG('B', 'B', 'Q', '9'); // Reduce bw_lo by +const QuicTag kBBQ0 = TAG('B', 'B', 'Q', '0'); // Increase bytes_acked in + // PROBE_UP when app limited. // bw_lo * bytes_lost/cwnd const QuicTag kRENO = TAG('R', 'E', 'N', 'O'); // Reno Congestion Control const QuicTag kTPCC = TAG('P', 'C', 'C', '\0'); // Performance-Oriented @@ -150,6 +158,18 @@ const QuicTag kBSAO = TAG('B', 'S', 'A', 'O'); // Avoid Overestimation in // aggregation const QuicTag kB2DL = TAG('B', '2', 'D', 'L'); // Increase inflight_hi based // on delievered, not inflight. +const QuicTag kB201 = TAG('B', '2', '0', '1'); // In PROBE_UP, check if cwnd + // limited before aggregation + // epoch, instead of ack event. +const QuicTag kB202 = TAG('B', '2', '0', '2'); // Do not exit PROBE_UP if + // inflight dips below 1.25*BW. +const QuicTag kB203 = TAG('B', '2', '0', '3'); // Ignore inflight_hi until + // PROBE_UP is exited. +const QuicTag kB206 = TAG('B', '2', '0', '6'); // Exit STARTUP after 2 losses. +const QuicTag kB204 = TAG('B', '2', '0', '4'); // Reduce extra acked when + // MaxBW incrases. +const QuicTag kB205 = TAG('B', '2', '0', '5'); // Add extra acked to CWND in + // STARTUP. const QuicTag kNTLP = TAG('N', 'T', 'L', 'P'); // No tail loss probe const QuicTag k1TLP = TAG('1', 'T', 'L', 'P'); // 1 tail loss probe const QuicTag k1RTO = TAG('1', 'R', 'T', 'O'); // Send 1 packet upon RTO @@ -162,24 +182,13 @@ const QuicTag kMIN4 = TAG('M', 'I', 'N', '4'); // Min CWND of 4 packets, const QuicTag kTLPR = TAG('T', 'L', 'P', 'R'); // Tail loss probe delay of // 0.5RTT. const QuicTag kMAD0 = TAG('M', 'A', 'D', '0'); // Ignore ack delay -const QuicTag kMAD1 = TAG('M', 'A', 'D', '1'); // 25ms initial max ack delay const QuicTag kMAD2 = TAG('M', 'A', 'D', '2'); // No min TLP const QuicTag kMAD3 = TAG('M', 'A', 'D', '3'); // No min RTO -const QuicTag kMAD4 = TAG('M', 'A', 'D', '4'); // IETF style TLP -const QuicTag kMAD5 = TAG('M', 'A', 'D', '5'); // IETF style TLP with 2x mult const QuicTag k1ACK = TAG('1', 'A', 'C', 'K'); // 1 fast ack for reordering -const QuicTag kACD0 = TAG('A', 'D', 'D', '0'); // Disable ack decimation -const QuicTag kACKD = TAG('A', 'C', 'K', 'D'); // Ack decimation style acking. -const QuicTag kAKD2 = TAG('A', 'K', 'D', '2'); // Ack decimation tolerating - // out of order packets. const QuicTag kAKD3 = TAG('A', 'K', 'D', '3'); // Ack decimation style acking // with 1/8 RTT acks. -const QuicTag kAKD4 = TAG('A', 'K', 'D', '4'); // Ack decimation with 1/8 RTT - // tolerating out of order. const QuicTag kAKDU = TAG('A', 'K', 'D', 'U'); // Unlimited number of packets // received before acking -const QuicTag kACKQ = TAG('A', 'C', 'K', 'Q'); // Send an immediate ack after - // 1 RTT of not receiving. const QuicTag kAFFE = TAG('A', 'F', 'F', 'E'); // Enable client receiving // AckFrequencyFrame. const QuicTag kAFF1 = TAG('A', 'F', 'F', '1'); // Use SRTT in building @@ -265,6 +274,8 @@ const QuicTag kAPTO = TAG('A', 'P', 'T', 'O'); // Use 1.5 * initial RTT before const QuicTag kELDT = TAG('E', 'L', 'D', 'T'); // Enable Loss Detection Tuning +// TODO(haoyuewang) Remove RVCM option once +// --quic_remove_connection_migration_connection_option is deprecated. const QuicTag kRVCM = TAG('R', 'V', 'C', 'M'); // Validate the new address // upon client address change. @@ -340,8 +351,8 @@ const QuicTag kMTUL = TAG('M', 'T', 'U', 'L'); // Low-target MTU discovery. const QuicTag kNSLC = TAG('N', 'S', 'L', 'C'); // Always send connection close // for idle timeout. -const QuicTag kCHSP = TAG('C', 'H', 'S', 'P'); // Chaos protection. -const QuicTag kBPTE = TAG('B', 'P', 'T', 'E'); // BoringSSL Permutes +const QuicTag kNCHP = TAG('N', 'C', 'H', 'P'); // No chaos protection. +const QuicTag kNBPE = TAG('N', 'B', 'P', 'E'); // No BoringSSL Permutes // TLS Extensions. // Proof types (i.e. certificate types) 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 9c12d8cf309..2f1fa3480c2 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 @@ -542,35 +542,6 @@ bool CryptoUtils::DeriveKeys(const ParsedQuicVersion& version, } // static -bool CryptoUtils::ExportKeyingMaterial(absl::string_view subkey_secret, - absl::string_view label, - absl::string_view context, - size_t result_len, - std::string* result) { - for (size_t i = 0; i < label.length(); i++) { - if (label[i] == '\0') { - QUIC_LOG(ERROR) << "ExportKeyingMaterial label may not contain NULs"; - return false; - } - } - // Create HKDF info input: null-terminated label + length-prefixed context - if (context.length() >= std::numeric_limits<uint32_t>::max()) { - QUIC_LOG(ERROR) << "Context value longer than 2^32"; - return false; - } - uint32_t context_length = static_cast<uint32_t>(context.length()); - std::string info = std::string(label); - info.push_back('\0'); - info.append(reinterpret_cast<char*>(&context_length), sizeof(context_length)); - info.append(context.data(), context.length()); - - QuicHKDF hkdf(subkey_secret, absl::string_view() /* no salt */, info, - result_len, 0 /* no fixed IV */, 0 /* no subkey secret */); - *result = std::string(hkdf.client_write_key()); - return true; -} - -// static uint64_t CryptoUtils::ComputeLeafCertHash(absl::string_view cert) { return QuicUtils::FNV1a_64_Hash(cert); } diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.h b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.h index 7aa32e1445a..15ffe064ea6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.h @@ -166,16 +166,6 @@ class QUIC_EXPORT_PRIVATE CryptoUtils { CrypterPair* crypters, std::string* subkey_secret); - // Performs key extraction to derive a new secret of |result_len| bytes - // dependent on |subkey_secret|, |label|, and |context|. Returns false if the - // parameters are invalid (e.g. |label| contains null bytes); returns true on - // success. - static bool ExportKeyingMaterial(absl::string_view subkey_secret, - absl::string_view label, - absl::string_view context, - size_t result_len, - std::string* result); - // Computes the FNV-1a hash of the provided DER-encoded cert for use in the // XLCT tag. static uint64_t ComputeLeafCertHash(absl::string_view cert); diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils_test.cc index 0f2df3a7f90..6c3d3846f82 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils_test.cc @@ -19,63 +19,6 @@ namespace { class CryptoUtilsTest : public QuicTest {}; -TEST_F(CryptoUtilsTest, TestExportKeyingMaterial) { - const struct TestVector { - // Input (strings of hexadecimal digits): - const char* subkey_secret; - const char* label; - const char* context; - size_t result_len; - - // Expected output (string of hexadecimal digits): - const char* expected; // Null if it should fail. - } test_vector[] = { - // Try a typical input - {"4823c1189ecc40fce888fbb4cf9ae6254f19ba12e6d9af54788f195a6f509ca3", - "e934f78d7a71dd85420fceeb8cea0317", - "b8d766b5d3c8aba0009c7ed3de553eba53b4de1030ea91383dcdf724cd8b7217", 32, - "a9979da0d5f1c1387d7cbe68f5c4163ddb445a03c4ad6ee72cb49d56726d679e"}, - // Don't let the label contain nulls - {"14fe51e082ffee7d1b4d8d4ab41f8c55", "3132333435363700", - "58585858585858585858585858585858", 16, nullptr}, - // Make sure nulls in the context are fine - {"d862c2e36b0a42f7827c67ebc8d44df7", "7a5b95e4e8378123", - "4142434445464700", 16, "12d418c6d0738a2e4d85b2d0170f76e1"}, - // ... and give a different result than without - {"d862c2e36b0a42f7827c67ebc8d44df7", "7a5b95e4e8378123", "41424344454647", - 16, "abfa1c479a6e3ffb98a11dee7d196408"}, - // Try weird lengths - {"d0ec8a34f6cc9a8c96", "49711798cc6251", - "933d4a2f30d22f089cfba842791116adc121e0", 23, - "c9a46ed0757bd1812f1f21b4d41e62125fec8364a21db7"}, - }; - - for (size_t i = 0; i < ABSL_ARRAYSIZE(test_vector); i++) { - // Decode the test vector. - std::string subkey_secret = - absl::HexStringToBytes(test_vector[i].subkey_secret); - std::string label = absl::HexStringToBytes(test_vector[i].label); - std::string context = absl::HexStringToBytes(test_vector[i].context); - size_t result_len = test_vector[i].result_len; - bool expect_ok = test_vector[i].expected != nullptr; - std::string expected; - if (expect_ok) { - expected = absl::HexStringToBytes(test_vector[i].expected); - } - - std::string result; - bool ok = CryptoUtils::ExportKeyingMaterial(subkey_secret, label, context, - result_len, &result); - EXPECT_EQ(expect_ok, ok); - if (expect_ok) { - EXPECT_EQ(result_len, result.length()); - quiche::test::CompareCharArraysWithHexError( - "HKDF output", result.data(), result.length(), expected.data(), - expected.length()); - } - } -} - TEST_F(CryptoUtilsTest, HandshakeFailureReasonToString) { EXPECT_STREQ("HANDSHAKE_OK", CryptoUtils::HandshakeFailureReasonToString(HANDSHAKE_OK)); diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.h b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.h index b0a24468a23..c4a1f2c53dc 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.h @@ -145,10 +145,13 @@ class QUIC_EXPORT_PRIVATE ProofSource { std::unique_ptr<Callback> callback) = 0; // Returns the certificate chain for |hostname| in leaf-first order. + // + // Sets *cert_matched_sni to true if the certificate matched the given + // hostname, false if a default cert not matching the hostname was used. virtual QuicReferenceCountedPointer<Chain> GetCertChain( const QuicSocketAddress& server_address, - const QuicSocketAddress& client_address, - const std::string& hostname) = 0; + const QuicSocketAddress& client_address, const std::string& hostname, + bool* cert_matched_sni) = 0; // Computes a signature using the private key of the certificate for // |hostname|. The value in |in| is signed using the algorithm specified by @@ -247,13 +250,16 @@ class QUIC_EXPORT_PRIVATE ProofSourceHandleCallback { // SSL_set_handshake_hints. // |ticket_encryption_key| (optional) encryption key to be used for minting // TLS resumption tickets. + // |cert_matched_sni| is true if the certificate matched the SNI hostname, + // false if a non-matching default cert was used. // // When called asynchronously(is_sync=false), this method will be responsible // to continue the handshake from where it left off. - virtual void OnSelectCertificateDone( - bool ok, bool is_sync, const ProofSource::Chain* chain, - absl::string_view handshake_hints, - absl::string_view ticket_encryption_key) = 0; + virtual void OnSelectCertificateDone(bool ok, bool is_sync, + const ProofSource::Chain* chain, + absl::string_view handshake_hints, + absl::string_view ticket_encryption_key, + bool cert_matched_sni) = 0; // Called when a ProofSourceHandle::ComputeSignature operation completes. virtual void OnComputeSignatureDone( 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 c7acd0056bc..986ee32ae05 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 @@ -53,7 +53,7 @@ void ProofSourceX509::GetProof( return; } - Certificate* certificate = GetCertificate(hostname); + Certificate* certificate = GetCertificate(hostname, &proof.cert_matched_sni); proof.signature = certificate->key.Sign(absl::string_view(payload.get(), payload_size), SSL_SIGN_RSA_PSS_RSAE_SHA256); @@ -63,9 +63,9 @@ void ProofSourceX509::GetProof( QuicReferenceCountedPointer<ProofSource::Chain> ProofSourceX509::GetCertChain( const QuicSocketAddress& /*server_address*/, - const QuicSocketAddress& /*client_address*/, - const std::string& hostname) { - return GetCertificate(hostname)->chain; + const QuicSocketAddress& /*client_address*/, const std::string& hostname, + bool* cert_matched_sni) { + return GetCertificate(hostname, cert_matched_sni)->chain; } void ProofSourceX509::ComputeTlsSignature( @@ -75,8 +75,9 @@ void ProofSourceX509::ComputeTlsSignature( uint16_t signature_algorithm, absl::string_view in, std::unique_ptr<ProofSource::SignatureCallback> callback) { - std::string signature = - GetCertificate(hostname)->key.Sign(in, signature_algorithm); + bool cert_matched_sni; + std::string signature = GetCertificate(hostname, &cert_matched_sni) + ->key.Sign(in, signature_algorithm); callback->Run(/*ok=*/!signature.empty(), signature, nullptr); } @@ -125,9 +126,10 @@ bool ProofSourceX509::AddCertificateChain( } ProofSourceX509::Certificate* ProofSourceX509::GetCertificate( - const std::string& hostname) const { + const std::string& hostname, bool* cert_matched_sni) const { auto it = certificate_map_.find(hostname); if (it != certificate_map_.end()) { + *cert_matched_sni = true; return it->second; } auto dot_pos = hostname.find('.'); @@ -135,9 +137,11 @@ ProofSourceX509::Certificate* ProofSourceX509::GetCertificate( std::string wildcard = absl::StrCat("*", hostname.substr(dot_pos)); it = certificate_map_.find(wildcard); if (it != certificate_map_.end()) { + *cert_matched_sni = true; return it->second; } } + *cert_matched_sni = false; return default_certificate_; } diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.h b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.h index 9ac676939a3..4b7ae51c83b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509.h @@ -37,8 +37,8 @@ class QUIC_EXPORT_PRIVATE ProofSourceX509 : public ProofSource { std::unique_ptr<Callback> callback) override; QuicReferenceCountedPointer<Chain> GetCertChain( const QuicSocketAddress& server_address, - const QuicSocketAddress& client_address, - const std::string& hostname) override; + const QuicSocketAddress& client_address, const std::string& hostname, + bool* cert_matched_sni) override; void ComputeTlsSignature( const QuicSocketAddress& server_address, const QuicSocketAddress& client_address, const std::string& hostname, @@ -65,7 +65,8 @@ class QUIC_EXPORT_PRIVATE ProofSourceX509 : public ProofSource { // Looks up certficiate for hostname, returns the default if no certificate is // found. - Certificate* GetCertificate(const std::string& hostname) const; + Certificate* GetCertificate(const std::string& hostname, + bool* cert_matched_sni) const; std::forward_list<Certificate> certificates_; Certificate* default_certificate_; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509_test.cc index 75a60993e15..1a9462f7dc3 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source_x509_test.cc @@ -58,8 +58,7 @@ TEST_F(ProofSourceX509Test, AddCertificateKeyMismatch) { ProofSourceX509::Create(test_chain_, std::move(*test_key_)); ASSERT_TRUE(proof_source != nullptr); test_key_ = CertificatePrivateKey::LoadFromDer(kTestCertificatePrivateKey); - bool result; - EXPECT_QUIC_BUG(result = proof_source->AddCertificateChain( + EXPECT_QUIC_BUG((void)proof_source->AddCertificateChain( wildcard_chain_, std::move(*test_key_)), "Private key does not match"); } @@ -72,40 +71,47 @@ TEST_F(ProofSourceX509Test, CertificateSelection) { std::move(*wildcard_key_))); // Default certificate. + bool cert_matched_sni; EXPECT_EQ(proof_source ->GetCertChain(QuicSocketAddress(), QuicSocketAddress(), - "unknown.test") + "unknown.test", &cert_matched_sni) ->certs[0], kTestCertificate); + EXPECT_FALSE(cert_matched_sni); // mail.example.org is explicitly a SubjectAltName in kTestCertificate. EXPECT_EQ(proof_source ->GetCertChain(QuicSocketAddress(), QuicSocketAddress(), - "mail.example.org") + "mail.example.org", &cert_matched_sni) ->certs[0], kTestCertificate); + EXPECT_TRUE(cert_matched_sni); // www.foo.test is in kWildcardCertificate. EXPECT_EQ(proof_source ->GetCertChain(QuicSocketAddress(), QuicSocketAddress(), - "www.foo.test") + "www.foo.test", &cert_matched_sni) ->certs[0], kWildcardCertificate); + EXPECT_TRUE(cert_matched_sni); // *.wildcard.test is in kWildcardCertificate. EXPECT_EQ(proof_source ->GetCertChain(QuicSocketAddress(), QuicSocketAddress(), - "www.wildcard.test") + "www.wildcard.test", &cert_matched_sni) ->certs[0], kWildcardCertificate); + EXPECT_TRUE(cert_matched_sni); EXPECT_EQ(proof_source ->GetCertChain(QuicSocketAddress(), QuicSocketAddress(), - "etc.wildcard.test") + "etc.wildcard.test", &cert_matched_sni) ->certs[0], kWildcardCertificate); + EXPECT_TRUE(cert_matched_sni); // wildcard.test itself is not in kWildcardCertificate. EXPECT_EQ(proof_source ->GetCertChain(QuicSocketAddress(), QuicSocketAddress(), - "wildcard.test") + "wildcard.test", &cert_matched_sni) ->certs[0], kTestCertificate); + EXPECT_FALSE(cert_matched_sni); } TEST_F(ProofSourceX509Test, TlsSignature) { diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.cc index 033ef3292b0..1e5f3c4a965 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.cc @@ -6,6 +6,7 @@ namespace quic { -QuicCryptoProof::QuicCryptoProof() : send_expect_ct_header(false) {} +QuicCryptoProof::QuicCryptoProof() + : send_expect_ct_header(false), cert_matched_sni(false) {} } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.h b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.h index 6ca87fb0a33..53e09612857 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.h @@ -22,6 +22,9 @@ struct QUIC_EXPORT_PRIVATE QuicCryptoProof { // Should the Expect-CT header be sent on the connection where the // certificate is used. bool send_expect_ct_header; + // Did the selected leaf certificate contain a SubjectAltName that included + // the requested SNI. + bool cert_matched_sni; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.cc index f2a88095aac..e2b4b93ef27 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 @@ -29,12 +29,10 @@ #include "quic/core/crypto/key_exchange.h" #include "quic/core/crypto/p256_key_exchange.h" #include "quic/core/crypto/proof_source.h" -#include "quic/core/crypto/proof_verifier.h" #include "quic/core/crypto/quic_decrypter.h" #include "quic/core/crypto/quic_encrypter.h" #include "quic/core/crypto/quic_hkdf.h" #include "quic/core/crypto/quic_random.h" -#include "quic/core/crypto/server_proof_verifier.h" #include "quic/core/crypto/tls_server_connection.h" #include "quic/core/proto/crypto_server_config_proto.h" #include "quic/core/proto/source_address_token_proto.h" @@ -244,7 +242,6 @@ QuicCryptoServerConfig::QuicCryptoServerConfig( primary_config_(nullptr), next_config_promotion_time_(QuicWallTime::Zero()), proof_source_(std::move(proof_source)), - client_cert_mode_(ClientCertMode::kNone), key_exchange_source_(std::move(key_exchange_source)), ssl_ctx_(TlsServerConnection::CreateSslCtx(proof_source_.get())), source_address_token_future_secs_(3600), @@ -1628,15 +1625,6 @@ QuicCryptoServerConfig::ParseConfigProtobuf( static_assert(sizeof(config->orbit) == kOrbitSize, "incorrect orbit size"); memcpy(config->orbit, orbit.data(), sizeof(config->orbit)); - if ((kexs_tags.size() != static_cast<size_t>(protobuf.key_size())) && - (!GetQuicRestartFlag(dont_fetch_quic_private_keys_from_leto) && - protobuf.key_size() == 0)) { - QUIC_LOG(WARNING) << "Server config has " << kexs_tags.size() - << " key exchange methods configured, but " - << protobuf.key_size() << " private keys"; - return nullptr; - } - QuicTagVector proof_demand_tags; if (msg->GetTaglist(kPDMD, &proof_demand_tags) == QUIC_NO_ERROR) { for (QuicTag tag : proof_demand_tags) { @@ -1761,23 +1749,6 @@ ProofSource* QuicCryptoServerConfig::proof_source() const { return proof_source_.get(); } -ServerProofVerifier* QuicCryptoServerConfig::proof_verifier() const { - return proof_verifier_.get(); -} - -void QuicCryptoServerConfig::set_proof_verifier( - std::unique_ptr<ServerProofVerifier> proof_verifier) { - proof_verifier_ = std::move(proof_verifier); -} - -ClientCertMode QuicCryptoServerConfig::client_cert_mode() const { - return client_cert_mode_; -} - -void QuicCryptoServerConfig::set_client_cert_mode(ClientCertMode mode) { - client_cert_mode_ = mode; -} - SSL_CTX* QuicCryptoServerConfig::ssl_ctx() const { return ssl_ctx_.get(); } diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h index 15ce0f276d7..d8e77e6d6de 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h @@ -20,10 +20,8 @@ #include "quic/core/crypto/crypto_secret_boxer.h" #include "quic/core/crypto/key_exchange.h" #include "quic/core/crypto/proof_source.h" -#include "quic/core/crypto/proof_verifier.h" #include "quic/core/crypto/quic_compressed_certs_cache.h" #include "quic/core/crypto/quic_crypto_proof.h" -#include "quic/core/crypto/server_proof_verifier.h" #include "quic/core/proto/cached_network_parameters_proto.h" #include "quic/core/proto/source_address_token_proto.h" #include "quic/core/quic_time.h" @@ -455,11 +453,6 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerConfig { } ProofSource* proof_source() const; - ServerProofVerifier* proof_verifier() const; - void set_proof_verifier(std::unique_ptr<ServerProofVerifier> proof_verifier); - - ClientCertMode client_cert_mode() const; - void set_client_cert_mode(ClientCertMode client_cert_mode); SSL_CTX* ssl_ctx() const; @@ -920,8 +913,6 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerConfig { // proof_source_ contains an object that can provide certificate chains and // signatures. std::unique_ptr<ProofSource> proof_source_; - std::unique_ptr<ServerProofVerifier> proof_verifier_; - ClientCertMode client_cert_mode_; // key_exchange_source_ contains an object that can provide key exchange // objects. 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 dd53ee7037c..bfd20b26c3b 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 @@ -17,9 +17,9 @@ TlsClientConnection::TlsClientConnection(SSL_CTX* ssl_ctx, // static bssl::UniquePtr<SSL_CTX> TlsClientConnection::CreateSslCtx( bool enable_early_data) { - bssl::UniquePtr<SSL_CTX> ssl_ctx = - TlsConnection::CreateSslCtx(SSL_VERIFY_PEER); + bssl::UniquePtr<SSL_CTX> ssl_ctx = TlsConnection::CreateSslCtx(); // Configure certificate verification. + SSL_CTX_set_custom_verify(ssl_ctx.get(), SSL_VERIFY_PEER, &VerifyCallback); int reverify_on_resume_enabled = 1; SSL_CTX_set_reverify_on_resume(ssl_ctx.get(), reverify_on_resume_enabled); 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 7a66e2f8f8d..fa9be976f69 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 @@ -106,6 +106,11 @@ TlsConnection::TlsConnection(SSL_CTX* ssl_ctx, ssl(), ssl_config_.signing_algorithm_prefs->data(), ssl_config_.signing_algorithm_prefs->size()); } + if (ssl_config.disable_ticket_support.has_value()) { + if (*ssl_config.disable_ticket_support) { + SSL_set_options(ssl(), SSL_OP_NO_TICKET); + } + } } void TlsConnection::EnableInfoCallback() { @@ -116,15 +121,12 @@ void TlsConnection::EnableInfoCallback() { } // static -bssl::UniquePtr<SSL_CTX> TlsConnection::CreateSslCtx(int cert_verify_mode) { +bssl::UniquePtr<SSL_CTX> TlsConnection::CreateSslCtx() { CRYPTO_library_init(); bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_with_buffers_method())); SSL_CTX_set_min_proto_version(ssl_ctx.get(), TLS1_3_VERSION); SSL_CTX_set_max_proto_version(ssl_ctx.get(), TLS1_3_VERSION); SSL_CTX_set_quic_method(ssl_ctx.get(), &kSslQuicMethod); - if (cert_verify_mode != SSL_VERIFY_NONE) { - SSL_CTX_set_custom_verify(ssl_ctx.get(), cert_verify_mode, &VerifyCallback); - } return ssl_ctx; } 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 f59eaa13150..c8d377e1745 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 @@ -108,12 +108,7 @@ class QUIC_EXPORT_PRIVATE TlsConnection { // Creates an SSL_CTX and configures it with the options that are appropriate // for both client and server. The caller is responsible for ownership of the // newly created struct. - // - // The provided |cert_verify_mode| is passed in as the |mode| argument for - // |SSL_CTX_set_verify|. See - // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_VERIFY_NONE - // for a description of possible values. - static bssl::UniquePtr<SSL_CTX> CreateSslCtx(int cert_verify_mode); + static bssl::UniquePtr<SSL_CTX> CreateSslCtx(); // From a given SSL* |ssl|, returns a pointer to the TlsConnection that it // belongs to. This helper method allows the callbacks set in BoringSSL to be @@ -121,11 +116,11 @@ class QUIC_EXPORT_PRIVATE TlsConnection { // callback. static TlsConnection* ConnectionFromSsl(const SSL* ssl); - private: - // Registered as the callback for SSL_CTX_set_custom_verify. The + // Registered as the callback for SSL(_CTX)_set_custom_verify. The // implementation is delegated to Delegate::VerifyCert. static enum ssl_verify_result_t VerifyCallback(SSL* ssl, uint8_t* out_alert); + private: // TlsConnection implements SSL_QUIC_METHOD, which provides the interface // between BoringSSL's TLS stack and a QUIC implementation. static const SSL_QUIC_METHOD kSslQuicMethod; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.cc index 2042c15aaf2..0da9bbc3e94 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/tls_server_connection.cc @@ -23,8 +23,12 @@ TlsServerConnection::TlsServerConnection(SSL_CTX* ssl_ctx, // static bssl::UniquePtr<SSL_CTX> TlsServerConnection::CreateSslCtx( ProofSource* proof_source) { - bssl::UniquePtr<SSL_CTX> ssl_ctx = - TlsConnection::CreateSslCtx(SSL_VERIFY_NONE); + bssl::UniquePtr<SSL_CTX> ssl_ctx = TlsConnection::CreateSslCtx(); + + // Server does not request/verify client certs by default. Individual server + // connections may call SSL_set_custom_verify on their SSL object to request + // client certs. + SSL_CTX_set_tlsext_servername_callback(ssl_ctx.get(), &TlsExtServernameCallback); SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), &SelectAlpnCallback, nullptr); 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 14df9c3a27c..75d4323bad4 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 @@ -22,6 +22,7 @@ #include "quic/core/quic_utils.h" #include "quic/core/quic_versions.h" #include "quic/platform/api/quic_bug_tracker.h" +#include "quic/platform/api/quic_flag_utils.h" namespace quic { @@ -609,15 +610,25 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, std::vector<uint8_t>* out) { std::string error_details; if (!in.AreValid(&error_details)) { - QUIC_BUG(quic_bug_10743_5) + QUIC_BUG(invalid transport parameters) << "Not serializing invalid transport parameters: " << error_details; return false; } if (in.version == 0 || (in.perspective == Perspective::IS_SERVER && in.supported_versions.empty())) { - QUIC_BUG(quic_bug_10743_6) << "Refusing to serialize without versions"; + QUIC_BUG(missing versions) << "Refusing to serialize without versions"; return false; } + TransportParameters::ParameterMap custom_parameters = in.custom_parameters; + for (const auto& kv : custom_parameters) { + if (kv.first % 31 == 27) { + // See the "Reserved Transport Parameters" section of RFC 9000. + QUIC_BUG(custom_parameters with GREASE) + << "Serializing custom_parameters with GREASE ID " << kv.first + << " is not allowed"; + return false; + } + } // Maximum length of the GREASE transport parameter (see below). static constexpr size_t kMaxGreaseLength = 16; @@ -637,8 +648,6 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, 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 @@ -663,8 +672,34 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, kTypeAndValueLength + // google_connection_options kTypeAndValueLength + // user_agent_id kTypeAndValueLength + // key_update_not_yet_supported - kTypeAndValueLength + // google-version - kGreaseParameterLength; // GREASE + kTypeAndValueLength; // google-version + + std::vector<TransportParameters::TransportParameterId> parameter_ids = { + TransportParameters::kOriginalDestinationConnectionId, + TransportParameters::kMaxIdleTimeout, + TransportParameters::kStatelessResetToken, + TransportParameters::kMaxPacketSize, + TransportParameters::kInitialMaxData, + TransportParameters::kInitialMaxStreamDataBidiLocal, + TransportParameters::kInitialMaxStreamDataBidiRemote, + TransportParameters::kInitialMaxStreamDataUni, + TransportParameters::kInitialMaxStreamsBidi, + TransportParameters::kInitialMaxStreamsUni, + TransportParameters::kAckDelayExponent, + TransportParameters::kMaxAckDelay, + TransportParameters::kMinAckDelay, + TransportParameters::kActiveConnectionIdLimit, + TransportParameters::kMaxDatagramFrameSize, + TransportParameters::kInitialRoundTripTime, + TransportParameters::kDisableActiveMigration, + TransportParameters::kPreferredAddress, + TransportParameters::kInitialSourceConnectionId, + TransportParameters::kRetrySourceConnectionId, + TransportParameters::kGoogleConnectionOptions, + TransportParameters::kGoogleUserAgentId, + TransportParameters::kGoogleKeyUpdateNotYetSupported, + TransportParameters::kGoogleQuicVersion, + }; size_t max_transport_param_length = kKnownTransportParamLength; // google_connection_options. @@ -680,269 +715,386 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, 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(); - } - - out->resize(max_transport_param_length); - QuicDataWriter writer(out->size(), reinterpret_cast<char*>(out->data())); - - // original_destination_connection_id - if (in.original_destination_connection_id.has_value()) { - QUICHE_DCHECK_EQ(Perspective::IS_SERVER, in.perspective); - QuicConnectionId original_destination_connection_id = - in.original_destination_connection_id.value(); - if (!writer.WriteVarInt62( - TransportParameters::kOriginalDestinationConnectionId) || - !writer.WriteStringPieceVarInt62( - absl::string_view(original_destination_connection_id.data(), - original_destination_connection_id.length()))) { - QUIC_BUG(quic_bug_10743_7) - << "Failed to write original_destination_connection_id " - << original_destination_connection_id << " for " << in; - return false; - } - } - - if (!in.max_idle_timeout_ms.Write(&writer)) { - QUIC_BUG(quic_bug_10743_8) << "Failed to write idle_timeout for " << in; - return false; - } - - // stateless_reset_token - if (!in.stateless_reset_token.empty()) { - QUICHE_DCHECK_EQ(kStatelessResetTokenLength, - in.stateless_reset_token.size()); - QUICHE_DCHECK_EQ(Perspective::IS_SERVER, in.perspective); - if (!writer.WriteVarInt62(TransportParameters::kStatelessResetToken) || - !writer.WriteStringPieceVarInt62(absl::string_view( - reinterpret_cast<const char*>(in.stateless_reset_token.data()), - in.stateless_reset_token.size()))) { - QUIC_BUG(quic_bug_10743_9) - << "Failed to write stateless_reset_token of length " - << in.stateless_reset_token.size() << " for " << in; - return false; - } - } - - if (!in.max_udp_payload_size.Write(&writer) || - !in.initial_max_data.Write(&writer) || - !in.initial_max_stream_data_bidi_local.Write(&writer) || - !in.initial_max_stream_data_bidi_remote.Write(&writer) || - !in.initial_max_stream_data_uni.Write(&writer) || - !in.initial_max_streams_bidi.Write(&writer) || - !in.initial_max_streams_uni.Write(&writer) || - !in.ack_delay_exponent.Write(&writer) || - !in.max_ack_delay.Write(&writer) || !in.min_ack_delay_us.Write(&writer) || - !in.active_connection_id_limit.Write(&writer) || - !in.max_datagram_frame_size.Write(&writer) || - !in.initial_round_trip_time_us.Write(&writer)) { - QUIC_BUG(quic_bug_10743_10) << "Failed to write integers for " << in; - return false; - } - - // disable_active_migration - if (in.disable_active_migration) { - if (!writer.WriteVarInt62(TransportParameters::kDisableActiveMigration) || - !writer.WriteVarInt62(/* transport parameter length */ 0)) { - QUIC_BUG(quic_bug_10743_11) - << "Failed to write disable_active_migration for " << in; - return false; - } - } - // preferred_address - if (in.preferred_address) { - std::string v4_address_bytes = - in.preferred_address->ipv4_socket_address.host().ToPackedString(); - std::string v6_address_bytes = - in.preferred_address->ipv6_socket_address.host().ToPackedString(); - if (v4_address_bytes.length() != 4 || v6_address_bytes.length() != 16 || - in.preferred_address->stateless_reset_token.size() != - kStatelessResetTokenLength) { - QUIC_BUG(quic_bug_10743_12) << "Bad lengths " << *in.preferred_address; - return false; - } - const uint64_t preferred_address_length = - v4_address_bytes.length() + /* IPv4 port */ sizeof(uint16_t) + - v6_address_bytes.length() + /* IPv6 port */ sizeof(uint16_t) + - /* connection ID length byte */ sizeof(uint8_t) + - in.preferred_address->connection_id.length() + - in.preferred_address->stateless_reset_token.size(); - if (!writer.WriteVarInt62(TransportParameters::kPreferredAddress) || - !writer.WriteVarInt62( - /* transport parameter length */ preferred_address_length) || - !writer.WriteStringPiece(v4_address_bytes) || - !writer.WriteUInt16(in.preferred_address->ipv4_socket_address.port()) || - !writer.WriteStringPiece(v6_address_bytes) || - !writer.WriteUInt16(in.preferred_address->ipv6_socket_address.port()) || - !writer.WriteUInt8(in.preferred_address->connection_id.length()) || - !writer.WriteBytes(in.preferred_address->connection_id.data(), - in.preferred_address->connection_id.length()) || - !writer.WriteBytes( - in.preferred_address->stateless_reset_token.data(), - in.preferred_address->stateless_reset_token.size())) { - QUIC_BUG(quic_bug_10743_13) - << "Failed to write preferred_address for " << in; - return false; - } - } + // Add a random GREASE transport parameter, as defined in the + // "Reserved Transport Parameters" section of RFC 9000. + // This forces receivers to support unexpected input. + QuicRandom* random = QuicRandom::GetInstance(); + // Transport parameter identifiers are 62 bits long so we need to + // ensure that the output of the computation below fits in 62 bits. + uint64_t grease_id64 = random->RandUint64() % ((1ULL << 62) - 31); + // Make sure grease_id % 31 == 27. Note that this is not uniformely + // distributed but is acceptable since no security depends on this + // randomness. + grease_id64 = (grease_id64 / 31) * 31 + 27; + TransportParameters::TransportParameterId grease_id = + static_cast<TransportParameters::TransportParameterId>(grease_id64); + const size_t grease_length = random->RandUint64() % kMaxGreaseLength; + QUICHE_DCHECK_GE(kMaxGreaseLength, grease_length); + char grease_contents[kMaxGreaseLength]; + random->RandBytes(grease_contents, grease_length); + custom_parameters[grease_id] = std::string(grease_contents, grease_length); - // initial_source_connection_id - if (in.initial_source_connection_id.has_value()) { - QuicConnectionId initial_source_connection_id = - in.initial_source_connection_id.value(); - if (!writer.WriteVarInt62( - TransportParameters::kInitialSourceConnectionId) || - !writer.WriteStringPieceVarInt62( - absl::string_view(initial_source_connection_id.data(), - initial_source_connection_id.length()))) { - QUIC_BUG(quic_bug_10743_14) - << "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()) { - QUICHE_DCHECK_EQ(Perspective::IS_SERVER, in.perspective); - QuicConnectionId retry_source_connection_id = - in.retry_source_connection_id.value(); - if (!writer.WriteVarInt62(TransportParameters::kRetrySourceConnectionId) || - !writer.WriteStringPieceVarInt62( - absl::string_view(retry_source_connection_id.data(), - retry_source_connection_id.length()))) { - QUIC_BUG(quic_bug_10743_15) - << "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, - "bad size"); - uint64_t connection_options_length = - in.google_connection_options.value().size() * 4; - if (!writer.WriteVarInt62(TransportParameters::kGoogleConnectionOptions) || - !writer.WriteVarInt62( - /* transport parameter length */ connection_options_length)) { - QUIC_BUG(quic_bug_10743_16) - << "Failed to write google_connection_options of length " - << connection_options_length << " for " << in; - return false; - } - for (const QuicTag& connection_option : - in.google_connection_options.value()) { - if (!writer.WriteTag(connection_option)) { - QUIC_BUG(quic_bug_10743_17) - << "Failed to write google_connection_option " - << QuicTagToString(connection_option) << " for " << in; - return false; - } - } - } - - // Google-specific user agent identifier. - if (in.user_agent_id.has_value()) { - if (!writer.WriteVarInt62(TransportParameters::kGoogleUserAgentId) || - !writer.WriteStringPieceVarInt62(in.user_agent_id.value())) { - QUIC_BUG(quic_bug_10743_18) - << "Failed to write Google user agent ID \"" - << in.user_agent_id.value() << "\" for " << in; - return false; - } - } - - // Google-specific indicator for key update not yet supported. - if (in.key_update_not_yet_supported) { - if (!writer.WriteVarInt62( - TransportParameters::kGoogleKeyUpdateNotYetSupported) || - !writer.WriteVarInt62(/* transport parameter length */ 0)) { - QUIC_BUG(quic_bug_10743_19) - << "Failed to write key_update_not_yet_supported for " << in; - return false; - } + // Custom parameters. + for (const auto& kv : custom_parameters) { + max_transport_param_length += kTypeAndValueLength + kv.second.length(); + parameter_ids.push_back(kv.first); } - // Google-specific version extension. - static_assert(sizeof(QuicVersionLabel) == sizeof(uint32_t), "bad length"); - uint64_t google_version_length = sizeof(in.version); - if (in.perspective == Perspective::IS_SERVER) { - google_version_length += - /* versions length */ sizeof(uint8_t) + - sizeof(QuicVersionLabel) * in.supported_versions.size(); - } - if (!writer.WriteVarInt62(TransportParameters::kGoogleQuicVersion) || - !writer.WriteVarInt62( - /* transport parameter length */ google_version_length) || - !writer.WriteUInt32(in.version)) { - QUIC_BUG(quic_bug_10743_20) - << "Failed to write Google version extension for " << in; - return false; - } - if (in.perspective == Perspective::IS_SERVER) { - if (!writer.WriteUInt8(sizeof(QuicVersionLabel) * - in.supported_versions.size())) { - QUIC_BUG(quic_bug_10743_21) - << "Failed to write versions length for " << in; - return false; - } - for (QuicVersionLabel version_label : in.supported_versions) { - if (!writer.WriteUInt32(version_label)) { - QUIC_BUG(quic_bug_10743_22) - << "Failed to write supported version for " << in; - return false; - } - } + // Randomize order of sent transport parameters by walking the array + // backwards and swapping each element with a random earlier one. + for (size_t i = parameter_ids.size() - 1; i > 0; i--) { + std::swap(parameter_ids[i], + parameter_ids[random->InsecureRandUint64() % (i + 1)]); } - for (const auto& kv : in.custom_parameters) { - const TransportParameters::TransportParameterId param_id = kv.first; - if (param_id % 31 == 27) { - // See the "Reserved Transport Parameters" section of - // draft-ietf-quic-transport. - QUIC_BUG(quic_bug_10743_23) - << "Serializing custom_parameters with GREASE ID " << param_id - << " is not allowed"; - return false; - } - if (!writer.WriteVarInt62(param_id) || - !writer.WriteStringPieceVarInt62(kv.second)) { - QUIC_BUG(quic_bug_10743_24) - << "Failed to write custom parameter " << param_id; - return false; - } - } + out->resize(max_transport_param_length); + QuicDataWriter writer(out->size(), reinterpret_cast<char*>(out->data())); - { - // Add a random GREASE transport parameter, as defined in the - // "Reserved Transport Parameters" section of draft-ietf-quic-transport. - // https://quicwg.org/base-drafts/draft-ietf-quic-transport.html - // This forces receivers to support unexpected input. - QuicRandom* random = QuicRandom::GetInstance(); - // Transport parameter identifiers are 62 bits long so we need to ensure - // that the output of the computation below fits in 62 bits. - uint64_t grease_id64 = random->RandUint64() % ((1ULL << 62) - 31); - // Make sure grease_id % 31 == 27. Note that this is not uniformely - // distributed but is acceptable since no security depends on this - // randomness. - grease_id64 = (grease_id64 / 31) * 31 + 27; - TransportParameters::TransportParameterId grease_id = - static_cast<TransportParameters::TransportParameterId>(grease_id64); - const size_t grease_length = random->RandUint64() % kMaxGreaseLength; - QUICHE_DCHECK_GE(kMaxGreaseLength, grease_length); - char grease_contents[kMaxGreaseLength]; - random->RandBytes(grease_contents, grease_length); - if (!writer.WriteVarInt62(grease_id) || - !writer.WriteStringPieceVarInt62( - absl::string_view(grease_contents, grease_length))) { - QUIC_BUG(quic_bug_10743_25) << "Failed to write GREASE parameter " - << TransportParameterIdToString(grease_id); - return false; + for (TransportParameters::TransportParameterId parameter_id : parameter_ids) { + switch (parameter_id) { + // original_destination_connection_id + case TransportParameters::kOriginalDestinationConnectionId: { + if (in.original_destination_connection_id.has_value()) { + QUICHE_DCHECK_EQ(Perspective::IS_SERVER, in.perspective); + QuicConnectionId original_destination_connection_id = + in.original_destination_connection_id.value(); + if (!writer.WriteVarInt62( + TransportParameters::kOriginalDestinationConnectionId) || + !writer.WriteStringPieceVarInt62(absl::string_view( + original_destination_connection_id.data(), + original_destination_connection_id.length()))) { + QUIC_BUG(Failed to write original_destination_connection_id) + << "Failed to write original_destination_connection_id " + << original_destination_connection_id << " for " << in; + return false; + } + } + } break; + // max_idle_timeout + case TransportParameters::kMaxIdleTimeout: { + if (!in.max_idle_timeout_ms.Write(&writer)) { + QUIC_BUG(Failed to write idle_timeout) + << "Failed to write idle_timeout for " << in; + return false; + } + } break; + // stateless_reset_token + case TransportParameters::kStatelessResetToken: { + if (!in.stateless_reset_token.empty()) { + QUICHE_DCHECK_EQ(kStatelessResetTokenLength, + in.stateless_reset_token.size()); + QUICHE_DCHECK_EQ(Perspective::IS_SERVER, in.perspective); + if (!writer.WriteVarInt62( + TransportParameters::kStatelessResetToken) || + !writer.WriteStringPieceVarInt62( + absl::string_view(reinterpret_cast<const char*>( + in.stateless_reset_token.data()), + in.stateless_reset_token.size()))) { + QUIC_BUG(Failed to write stateless_reset_token) + << "Failed to write stateless_reset_token of length " + << in.stateless_reset_token.size() << " for " << in; + return false; + } + } + } break; + // max_udp_payload_size + case TransportParameters::kMaxPacketSize: { + if (!in.max_udp_payload_size.Write(&writer)) { + QUIC_BUG(Failed to write max_udp_payload_size) + << "Failed to write max_udp_payload_size for " << in; + return false; + } + } break; + // initial_max_data + case TransportParameters::kInitialMaxData: { + if (!in.initial_max_data.Write(&writer)) { + QUIC_BUG(Failed to write initial_max_data) + << "Failed to write initial_max_data for " << in; + return false; + } + } break; + // initial_max_stream_data_bidi_local + case TransportParameters::kInitialMaxStreamDataBidiLocal: { + if (!in.initial_max_stream_data_bidi_local.Write(&writer)) { + QUIC_BUG(Failed to write initial_max_stream_data_bidi_local) + << "Failed to write initial_max_stream_data_bidi_local for " + << in; + return false; + } + } break; + // initial_max_stream_data_bidi_remote + case TransportParameters::kInitialMaxStreamDataBidiRemote: { + if (!in.initial_max_stream_data_bidi_remote.Write(&writer)) { + QUIC_BUG(Failed to write initial_max_stream_data_bidi_remote) + << "Failed to write initial_max_stream_data_bidi_remote for " + << in; + return false; + } + } break; + // initial_max_stream_data_uni + case TransportParameters::kInitialMaxStreamDataUni: { + if (!in.initial_max_stream_data_uni.Write(&writer)) { + QUIC_BUG(Failed to write initial_max_stream_data_uni) + << "Failed to write initial_max_stream_data_uni for " << in; + return false; + } + } break; + // initial_max_streams_bidi + case TransportParameters::kInitialMaxStreamsBidi: { + if (!in.initial_max_streams_bidi.Write(&writer)) { + QUIC_BUG(Failed to write initial_max_streams_bidi) + << "Failed to write initial_max_streams_bidi for " << in; + return false; + } + } break; + // initial_max_streams_uni + case TransportParameters::kInitialMaxStreamsUni: { + if (!in.initial_max_streams_uni.Write(&writer)) { + QUIC_BUG(Failed to write initial_max_streams_uni) + << "Failed to write initial_max_streams_uni for " << in; + return false; + } + } break; + // ack_delay_exponent + case TransportParameters::kAckDelayExponent: { + if (!in.ack_delay_exponent.Write(&writer)) { + QUIC_BUG(Failed to write ack_delay_exponent) + << "Failed to write ack_delay_exponent for " << in; + return false; + } + } break; + // max_ack_delay + case TransportParameters::kMaxAckDelay: { + if (!in.max_ack_delay.Write(&writer)) { + QUIC_BUG(Failed to write max_ack_delay) + << "Failed to write max_ack_delay for " << in; + return false; + } + } break; + // min_ack_delay_us + case TransportParameters::kMinAckDelay: { + if (!in.min_ack_delay_us.Write(&writer)) { + QUIC_BUG(Failed to write min_ack_delay_us) + << "Failed to write min_ack_delay_us for " << in; + return false; + } + } break; + // active_connection_id_limit + case TransportParameters::kActiveConnectionIdLimit: { + if (!in.active_connection_id_limit.Write(&writer)) { + QUIC_BUG(Failed to write active_connection_id_limit) + << "Failed to write active_connection_id_limit for " << in; + return false; + } + } break; + // max_datagram_frame_size + case TransportParameters::kMaxDatagramFrameSize: { + if (!in.max_datagram_frame_size.Write(&writer)) { + QUIC_BUG(Failed to write max_datagram_frame_size) + << "Failed to write max_datagram_frame_size for " << in; + return false; + } + } break; + // initial_round_trip_time_us + case TransportParameters::kInitialRoundTripTime: { + if (!in.initial_round_trip_time_us.Write(&writer)) { + QUIC_BUG(Failed to write initial_round_trip_time_us) + << "Failed to write initial_round_trip_time_us for " << in; + return false; + } + } break; + // disable_active_migration + case TransportParameters::kDisableActiveMigration: { + if (in.disable_active_migration) { + if (!writer.WriteVarInt62( + TransportParameters::kDisableActiveMigration) || + !writer.WriteVarInt62(/* transport parameter length */ 0)) { + QUIC_BUG(Failed to write disable_active_migration) + << "Failed to write disable_active_migration for " << in; + return false; + } + } + } break; + // preferred_address + case TransportParameters::kPreferredAddress: { + if (in.preferred_address) { + std::string v4_address_bytes = + in.preferred_address->ipv4_socket_address.host().ToPackedString(); + std::string v6_address_bytes = + in.preferred_address->ipv6_socket_address.host().ToPackedString(); + if (v4_address_bytes.length() != 4 || + v6_address_bytes.length() != 16 || + in.preferred_address->stateless_reset_token.size() != + kStatelessResetTokenLength) { + QUIC_BUG(quic_bug_10743_12) + << "Bad lengths " << *in.preferred_address; + return false; + } + const uint64_t preferred_address_length = + v4_address_bytes.length() + /* IPv4 port */ sizeof(uint16_t) + + v6_address_bytes.length() + /* IPv6 port */ sizeof(uint16_t) + + /* connection ID length byte */ sizeof(uint8_t) + + in.preferred_address->connection_id.length() + + in.preferred_address->stateless_reset_token.size(); + if (!writer.WriteVarInt62(TransportParameters::kPreferredAddress) || + !writer.WriteVarInt62( + /* transport parameter length */ preferred_address_length) || + !writer.WriteStringPiece(v4_address_bytes) || + !writer.WriteUInt16( + in.preferred_address->ipv4_socket_address.port()) || + !writer.WriteStringPiece(v6_address_bytes) || + !writer.WriteUInt16( + in.preferred_address->ipv6_socket_address.port()) || + !writer.WriteUInt8( + in.preferred_address->connection_id.length()) || + !writer.WriteBytes( + in.preferred_address->connection_id.data(), + in.preferred_address->connection_id.length()) || + !writer.WriteBytes( + in.preferred_address->stateless_reset_token.data(), + in.preferred_address->stateless_reset_token.size())) { + QUIC_BUG(Failed to write preferred_address) + << "Failed to write preferred_address for " << in; + return false; + } + } + } break; + // initial_source_connection_id + case TransportParameters::kInitialSourceConnectionId: { + if (in.initial_source_connection_id.has_value()) { + QuicConnectionId initial_source_connection_id = + in.initial_source_connection_id.value(); + if (!writer.WriteVarInt62( + TransportParameters::kInitialSourceConnectionId) || + !writer.WriteStringPieceVarInt62( + absl::string_view(initial_source_connection_id.data(), + initial_source_connection_id.length()))) { + QUIC_BUG(Failed to write initial_source_connection_id) + << "Failed to write initial_source_connection_id " + << initial_source_connection_id << " for " << in; + return false; + } + } + } break; + // retry_source_connection_id + case TransportParameters::kRetrySourceConnectionId: { + if (in.retry_source_connection_id.has_value()) { + QUICHE_DCHECK_EQ(Perspective::IS_SERVER, in.perspective); + QuicConnectionId retry_source_connection_id = + in.retry_source_connection_id.value(); + if (!writer.WriteVarInt62( + TransportParameters::kRetrySourceConnectionId) || + !writer.WriteStringPieceVarInt62( + absl::string_view(retry_source_connection_id.data(), + retry_source_connection_id.length()))) { + QUIC_BUG(Failed to write retry_source_connection_id) + << "Failed to write retry_source_connection_id " + << retry_source_connection_id << " for " << in; + return false; + } + } + } break; + // Google-specific connection options. + case TransportParameters::kGoogleConnectionOptions: { + if (in.google_connection_options.has_value()) { + static_assert( + sizeof(in.google_connection_options.value().front()) == 4, + "bad size"); + uint64_t connection_options_length = + in.google_connection_options.value().size() * 4; + if (!writer.WriteVarInt62( + TransportParameters::kGoogleConnectionOptions) || + !writer.WriteVarInt62( + /* transport parameter length */ connection_options_length)) { + QUIC_BUG(Failed to write google_connection_options) + << "Failed to write google_connection_options of length " + << connection_options_length << " for " << in; + return false; + } + for (const QuicTag& connection_option : + in.google_connection_options.value()) { + if (!writer.WriteTag(connection_option)) { + QUIC_BUG(Failed to write google_connection_option) + << "Failed to write google_connection_option " + << QuicTagToString(connection_option) << " for " << in; + return false; + } + } + } + } break; + // Google-specific user agent identifier. + case TransportParameters::kGoogleUserAgentId: { + if (in.user_agent_id.has_value()) { + if (!writer.WriteVarInt62(TransportParameters::kGoogleUserAgentId) || + !writer.WriteStringPieceVarInt62(in.user_agent_id.value())) { + QUIC_BUG(Failed to write Google user agent ID) + << "Failed to write Google user agent ID \"" + << in.user_agent_id.value() << "\" for " << in; + return false; + } + } + } break; + // Google-specific indicator for key update not yet supported. + case TransportParameters::kGoogleKeyUpdateNotYetSupported: { + if (in.key_update_not_yet_supported) { + if (!writer.WriteVarInt62( + TransportParameters::kGoogleKeyUpdateNotYetSupported) || + !writer.WriteVarInt62(/* transport parameter length */ 0)) { + QUIC_BUG(Failed to write key_update_not_yet_supported) + << "Failed to write key_update_not_yet_supported for " << in; + return false; + } + } + } break; + // Google-specific version extension. + case TransportParameters::kGoogleQuicVersion: { + static_assert(sizeof(QuicVersionLabel) == sizeof(uint32_t), + "bad length"); + uint64_t google_version_length = sizeof(in.version); + if (in.perspective == Perspective::IS_SERVER) { + google_version_length += + /* versions length */ sizeof(uint8_t) + + sizeof(QuicVersionLabel) * in.supported_versions.size(); + } + if (!writer.WriteVarInt62(TransportParameters::kGoogleQuicVersion) || + !writer.WriteVarInt62( + /* transport parameter length */ google_version_length) || + !writer.WriteUInt32(in.version)) { + QUIC_BUG(Failed to write Google version extension) + << "Failed to write Google version extension for " << in; + return false; + } + if (in.perspective == Perspective::IS_SERVER) { + if (!writer.WriteUInt8(sizeof(QuicVersionLabel) * + in.supported_versions.size())) { + QUIC_BUG(Failed to write versions length) + << "Failed to write versions length for " << in; + return false; + } + for (QuicVersionLabel version_label : in.supported_versions) { + if (!writer.WriteUInt32(version_label)) { + QUIC_BUG(Failed to write supported version) + << "Failed to write supported version for " << in; + return false; + } + } + } + } break; + // Custom parameters and GREASE. + default: { + auto it = custom_parameters.find(parameter_id); + if (it == custom_parameters.end()) { + QUIC_BUG(Unknown parameter) << "Unknown parameter " << parameter_id; + return false; + } + if (!writer.WriteVarInt62(parameter_id) || + !writer.WriteStringPieceVarInt62(it->second)) { + QUIC_BUG(Failed to write custom parameter) + << "Failed to write custom parameter " << parameter_id; + return false; + } + } break; } } 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 81707c51885..48af1c4223a 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 @@ -932,6 +932,52 @@ TEST_P(TransportParametersTest, VeryLongCustomParameter) { EXPECT_EQ(new_params, orig_params); } +TEST_P(TransportParametersTest, SerializationOrderIsRandom) { + TransportParameters orig_params; + orig_params.perspective = Perspective::IS_CLIENT; + orig_params.version = kFakeVersionLabel; + orig_params.max_idle_timeout_ms.set_value(kFakeIdleTimeoutMilliseconds); + orig_params.max_udp_payload_size.set_value(kMaxPacketSizeForTest); + orig_params.initial_max_data.set_value(kFakeInitialMaxData); + orig_params.initial_max_stream_data_bidi_local.set_value( + kFakeInitialMaxStreamDataBidiLocal); + orig_params.initial_max_stream_data_bidi_remote.set_value( + kFakeInitialMaxStreamDataBidiRemote); + orig_params.initial_max_stream_data_uni.set_value( + kFakeInitialMaxStreamDataUni); + orig_params.initial_max_streams_bidi.set_value(kFakeInitialMaxStreamsBidi); + orig_params.initial_max_streams_uni.set_value(kFakeInitialMaxStreamsUni); + orig_params.ack_delay_exponent.set_value(kAckDelayExponentForTest); + orig_params.max_ack_delay.set_value(kMaxAckDelayForTest); + orig_params.min_ack_delay_us.set_value(kMinAckDelayUsForTest); + orig_params.disable_active_migration = kFakeDisableMigration; + orig_params.active_connection_id_limit.set_value( + kActiveConnectionIdLimitForTest); + 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.key_update_not_yet_supported = kFakeKeyUpdateNotYetSupported; + orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value; + orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value; + + std::vector<uint8_t> first_serialized; + ASSERT_TRUE( + SerializeTransportParameters(version_, orig_params, &first_serialized)); + // Test that a subsequent serialization is different from the first. + // Run in a loop to avoid a failure in the unlikely event that randomization + // produces the same result multiple times. + for (int i = 0; i < 1000; i++) { + std::vector<uint8_t> serialized; + ASSERT_TRUE( + SerializeTransportParameters(version_, orig_params, &serialized)); + if (serialized != first_serialized) { + return; + } + } +} + class TransportParametersTicketSerializationTest : public QuicTest { protected: void SetUp() override { 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 038924e2165..43eabf7ef79 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 @@ -85,7 +85,8 @@ TEST_F(QuicFramesTest, RstStreamFrameToString) { std::ostringstream stream; stream << rst_stream; EXPECT_EQ( - "{ control_frame_id: 1, stream_id: 1, byte_offset: 3, error_code: 6 }\n", + "{ control_frame_id: 1, stream_id: 1, byte_offset: 3, error_code: 6, " + "ietf_error_code: 0 }\n", stream.str()); EXPECT_TRUE(IsControlFrame(frame.type)); } diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.cc index d70f217bc14..873868c3d5c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.cc +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.cc @@ -18,12 +18,23 @@ QuicRstStreamFrame::QuicRstStreamFrame(QuicControlFrameId control_frame_id, ietf_error_code(RstStreamErrorCodeToIetfResetStreamErrorCode(error_code)), byte_offset(bytes_written) {} +QuicRstStreamFrame::QuicRstStreamFrame(QuicControlFrameId control_frame_id, + QuicStreamId stream_id, + QuicResetStreamError error, + QuicStreamOffset bytes_written) + : control_frame_id(control_frame_id), + stream_id(stream_id), + error_code(error.internal_code()), + ietf_error_code(error.ietf_application_code()), + byte_offset(bytes_written) {} + std::ostream& operator<<(std::ostream& os, const QuicRstStreamFrame& rst_frame) { os << "{ control_frame_id: " << rst_frame.control_frame_id << ", stream_id: " << rst_frame.stream_id << ", byte_offset: " << rst_frame.byte_offset - << ", error_code: " << rst_frame.error_code << " }\n"; + << ", error_code: " << rst_frame.error_code + << ", ietf_error_code: " << rst_frame.ietf_error_code << " }\n"; return os; } diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.h index 9c9e42e4d38..744a227c8ff 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.h +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.h @@ -16,13 +16,14 @@ namespace quic { struct QUIC_EXPORT_PRIVATE QuicRstStreamFrame { QuicRstStreamFrame() = default; QuicRstStreamFrame(QuicControlFrameId control_frame_id, - QuicStreamId stream_id, - QuicRstStreamErrorCode error_code, + QuicStreamId stream_id, QuicRstStreamErrorCode error_code, + QuicStreamOffset bytes_written); + QuicRstStreamFrame(QuicControlFrameId control_frame_id, + QuicStreamId stream_id, QuicResetStreamError error, QuicStreamOffset bytes_written); friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( - std::ostream& os, - const QuicRstStreamFrame& r); + std::ostream& os, const QuicRstStreamFrame& r); // A unique identifier of this control frame. 0 when this frame is received, // and non-zero when sent. @@ -45,6 +46,11 @@ struct QUIC_EXPORT_PRIVATE QuicRstStreamFrame { // that stream. This can be done through normal termination (data packet with // FIN) or through a RST. QuicStreamOffset byte_offset = 0; + + // Returns a tuple of both |error_code| and |ietf_error_code|. + QuicResetStreamError error() const { + return QuicResetStreamError(error_code, ietf_error_code); + } }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.cc index 93eed75ccc4..396288ead41 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.cc +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.cc @@ -11,11 +11,16 @@ namespace quic { QuicStopSendingFrame::QuicStopSendingFrame(QuicControlFrameId control_frame_id, QuicStreamId stream_id, QuicRstStreamErrorCode error_code) + : QuicStopSendingFrame(control_frame_id, stream_id, + QuicResetStreamError::FromInternal(error_code)) {} + +QuicStopSendingFrame::QuicStopSendingFrame(QuicControlFrameId control_frame_id, + QuicStreamId stream_id, + QuicResetStreamError error) : control_frame_id(control_frame_id), stream_id(stream_id), - error_code(error_code), - ietf_error_code( - RstStreamErrorCodeToIetfResetStreamErrorCode(error_code)) {} + error_code(error.internal_code()), + ietf_error_code(error.ietf_application_code()) {} std::ostream& operator<<(std::ostream& os, const QuicStopSendingFrame& frame) { os << "{ control_frame_id: " << frame.control_frame_id diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.h index 60158b221d8..b575e756cb7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.h +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.h @@ -18,6 +18,8 @@ struct QUIC_EXPORT_PRIVATE QuicStopSendingFrame { QuicStopSendingFrame(QuicControlFrameId control_frame_id, QuicStreamId stream_id, QuicRstStreamErrorCode error_code); + QuicStopSendingFrame(QuicControlFrameId control_frame_id, + QuicStreamId stream_id, QuicResetStreamError error); friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( std::ostream& os, @@ -35,6 +37,11 @@ struct QUIC_EXPORT_PRIVATE QuicStopSendingFrame { // On-the-wire application error code of the frame. uint64_t ietf_error_code = 0; + + // Returns a tuple of both |error_code| and |ietf_error_code|. + QuicResetStreamError error() const { + return QuicResetStreamError(error_code, ietf_error_code); + } }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/capsule.cc b/chromium/net/third_party/quiche/src/quic/core/http/capsule.cc new file mode 100644 index 00000000000..c811bb84287 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/http/capsule.cc @@ -0,0 +1,629 @@ +// Copyright (c) 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "quic/core/http/capsule.h" + +#include <type_traits> + +#include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "quic/core/http/http_frames.h" +#include "quic/core/quic_data_reader.h" +#include "quic/core/quic_data_writer.h" +#include "quic/core/quic_types.h" +#include "quic/platform/api/quic_bug_tracker.h" +#include "common/platform/api/quiche_logging.h" + +namespace quic { + +std::string CapsuleTypeToString(CapsuleType capsule_type) { + switch (capsule_type) { + case CapsuleType::REGISTER_DATAGRAM_CONTEXT: + return "REGISTER_DATAGRAM_CONTEXT"; + case CapsuleType::CLOSE_DATAGRAM_CONTEXT: + return "CLOSE_DATAGRAM_CONTEXT"; + case CapsuleType::DATAGRAM: + return "DATAGRAM"; + case CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT: + return "REGISTER_DATAGRAM_NO_CONTEXT"; + case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: + return "CLOSE_WEBTRANSPORT_SESSION"; + } + return absl::StrCat("Unknown(", static_cast<uint64_t>(capsule_type), ")"); +} + +std::ostream& operator<<(std::ostream& os, const CapsuleType& capsule_type) { + os << CapsuleTypeToString(capsule_type); + return os; +} + +std::string DatagramFormatTypeToString( + DatagramFormatType datagram_format_type) { + switch (datagram_format_type) { + case DatagramFormatType::UDP_PAYLOAD: + return "UDP_PAYLOAD"; + case DatagramFormatType::WEBTRANSPORT: + return "WEBTRANSPORT"; + } + return absl::StrCat("Unknown(", static_cast<uint64_t>(datagram_format_type), + ")"); +} + +std::ostream& operator<<(std::ostream& os, + const DatagramFormatType& datagram_format_type) { + os << DatagramFormatTypeToString(datagram_format_type); + return os; +} + +std::string ContextCloseCodeToString(ContextCloseCode context_close_code) { + switch (context_close_code) { + case ContextCloseCode::CLOSE_NO_ERROR: + return "NO_ERROR"; + case ContextCloseCode::UNKNOWN_FORMAT: + return "UNKNOWN_FORMAT"; + case ContextCloseCode::DENIED: + return "DENIED"; + case ContextCloseCode::RESOURCE_LIMIT: + return "RESOURCE_LIMIT"; + } + return absl::StrCat("Unknown(", static_cast<uint64_t>(context_close_code), + ")"); +} + +std::ostream& operator<<(std::ostream& os, + const ContextCloseCode& context_close_code) { + os << ContextCloseCodeToString(context_close_code); + return os; +} + +Capsule::Capsule(CapsuleType capsule_type) : capsule_type_(capsule_type) { + switch (capsule_type) { + case CapsuleType::DATAGRAM: + static_assert(std::is_standard_layout<DatagramCapsule>::value && + std::is_trivially_destructible<DatagramCapsule>::value, + "All capsule structs must have these properties"); + datagram_capsule_ = DatagramCapsule(); + break; + case CapsuleType::REGISTER_DATAGRAM_CONTEXT: + static_assert( + std::is_standard_layout<RegisterDatagramContextCapsule>::value && + std::is_trivially_destructible< + RegisterDatagramContextCapsule>::value, + "All capsule structs must have these properties"); + register_datagram_context_capsule_ = RegisterDatagramContextCapsule(); + break; + case CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT: + static_assert( + std::is_standard_layout<RegisterDatagramNoContextCapsule>::value && + std::is_trivially_destructible< + RegisterDatagramNoContextCapsule>::value, + "All capsule structs must have these properties"); + register_datagram_no_context_capsule_ = + RegisterDatagramNoContextCapsule(); + break; + case CapsuleType::CLOSE_DATAGRAM_CONTEXT: + static_assert( + std::is_standard_layout<CloseDatagramContextCapsule>::value && + std::is_trivially_destructible< + CloseDatagramContextCapsule>::value, + "All capsule structs must have these properties"); + close_datagram_context_capsule_ = CloseDatagramContextCapsule(); + break; + case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: + static_assert( + std::is_standard_layout<CloseWebTransportSessionCapsule>::value && + std::is_trivially_destructible< + CloseWebTransportSessionCapsule>::value, + "All capsule structs must have these properties"); + close_web_transport_session_capsule_ = CloseWebTransportSessionCapsule(); + break; + default: + unknown_capsule_data_ = absl::string_view(); + break; + } +} + +// static +Capsule Capsule::Datagram(absl::optional<QuicDatagramContextId> context_id, + absl::string_view http_datagram_payload) { + Capsule capsule(CapsuleType::DATAGRAM); + capsule.datagram_capsule().context_id = context_id; + capsule.datagram_capsule().http_datagram_payload = http_datagram_payload; + return capsule; +} + +// static +Capsule Capsule::RegisterDatagramContext( + QuicDatagramContextId context_id, DatagramFormatType format_type, + absl::string_view format_additional_data) { + Capsule capsule(CapsuleType::REGISTER_DATAGRAM_CONTEXT); + capsule.register_datagram_context_capsule().context_id = context_id; + capsule.register_datagram_context_capsule().format_type = format_type; + capsule.register_datagram_context_capsule().format_additional_data = + format_additional_data; + return capsule; +} + +// static +Capsule Capsule::RegisterDatagramNoContext( + DatagramFormatType format_type, absl::string_view format_additional_data) { + Capsule capsule(CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT); + capsule.register_datagram_no_context_capsule().format_type = format_type; + capsule.register_datagram_no_context_capsule().format_additional_data = + format_additional_data; + return capsule; +} + +// static +Capsule Capsule::CloseDatagramContext(QuicDatagramContextId context_id, + ContextCloseCode close_code, + absl::string_view close_details) { + Capsule capsule(CapsuleType::CLOSE_DATAGRAM_CONTEXT); + capsule.close_datagram_context_capsule().context_id = context_id; + capsule.close_datagram_context_capsule().close_code = close_code; + capsule.close_datagram_context_capsule().close_details = close_details; + return capsule; +} + +// static +Capsule Capsule::CloseWebTransportSession(WebTransportSessionError error_code, + absl::string_view error_message) { + Capsule capsule(CapsuleType::CLOSE_WEBTRANSPORT_SESSION); + capsule.close_web_transport_session_capsule().error_code = error_code; + capsule.close_web_transport_session_capsule().error_message = error_message; + return capsule; +} + +// static +Capsule Capsule::Unknown(uint64_t capsule_type, + absl::string_view unknown_capsule_data) { + Capsule capsule(static_cast<CapsuleType>(capsule_type)); + capsule.unknown_capsule_data() = unknown_capsule_data; + return capsule; +} + +Capsule& Capsule::operator=(const Capsule& other) { + capsule_type_ = other.capsule_type_; + switch (capsule_type_) { + case CapsuleType::DATAGRAM: + datagram_capsule_ = other.datagram_capsule_; + break; + case CapsuleType::REGISTER_DATAGRAM_CONTEXT: + register_datagram_context_capsule_ = + other.register_datagram_context_capsule_; + break; + case CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT: + register_datagram_no_context_capsule_ = + other.register_datagram_no_context_capsule_; + break; + case CapsuleType::CLOSE_DATAGRAM_CONTEXT: + close_datagram_context_capsule_ = other.close_datagram_context_capsule_; + break; + case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: + close_web_transport_session_capsule_ = + other.close_web_transport_session_capsule_; + break; + default: + unknown_capsule_data_ = other.unknown_capsule_data_; + break; + } + return *this; +} + +Capsule::Capsule(const Capsule& other) : Capsule(other.capsule_type_) { + *this = other; +} + +bool Capsule::operator==(const Capsule& other) const { + if (capsule_type_ != other.capsule_type_) { + return false; + } + switch (capsule_type_) { + case CapsuleType::DATAGRAM: + return datagram_capsule_.context_id == + other.datagram_capsule_.context_id && + datagram_capsule_.http_datagram_payload == + other.datagram_capsule_.http_datagram_payload; + case CapsuleType::REGISTER_DATAGRAM_CONTEXT: + return register_datagram_context_capsule_.context_id == + other.register_datagram_context_capsule_.context_id && + register_datagram_context_capsule_.format_type == + other.register_datagram_context_capsule_.format_type && + register_datagram_context_capsule_.format_additional_data == + other.register_datagram_context_capsule_ + .format_additional_data; + case CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT: + return register_datagram_no_context_capsule_.format_type == + other.register_datagram_no_context_capsule_.format_type && + register_datagram_no_context_capsule_.format_additional_data == + other.register_datagram_no_context_capsule_ + .format_additional_data; + case CapsuleType::CLOSE_DATAGRAM_CONTEXT: + return close_datagram_context_capsule_.context_id == + other.close_datagram_context_capsule_.context_id && + close_datagram_context_capsule_.close_code == + other.close_datagram_context_capsule_.close_code && + close_datagram_context_capsule_.close_details == + other.close_datagram_context_capsule_.close_details; + case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: + return close_web_transport_session_capsule_.error_code == + other.close_web_transport_session_capsule_.error_code && + close_web_transport_session_capsule_.error_message == + other.close_web_transport_session_capsule_.error_message; + default: + return unknown_capsule_data_ == other.unknown_capsule_data_; + } +} + +std::string Capsule::ToString() const { + std::string rv = CapsuleTypeToString(capsule_type_); + switch (capsule_type_) { + case CapsuleType::DATAGRAM: + if (datagram_capsule_.context_id.has_value()) { + absl::StrAppend(&rv, "(", datagram_capsule_.context_id.value(), ")"); + } + absl::StrAppend( + &rv, "[", + absl::BytesToHexString(datagram_capsule_.http_datagram_payload), "]"); + break; + case CapsuleType::REGISTER_DATAGRAM_CONTEXT: + absl::StrAppend( + &rv, "(context_id=", register_datagram_context_capsule_.context_id, + ",format_type=", + DatagramFormatTypeToString( + register_datagram_context_capsule_.format_type), + "){", + absl::BytesToHexString( + register_datagram_context_capsule_.format_additional_data), + "}"); + break; + case CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT: + absl::StrAppend( + &rv, "(format_type=", + DatagramFormatTypeToString( + register_datagram_no_context_capsule_.format_type), + "){", + absl::BytesToHexString( + register_datagram_no_context_capsule_.format_additional_data), + "}"); + break; + case CapsuleType::CLOSE_DATAGRAM_CONTEXT: + absl::StrAppend( + &rv, "(context_id=", close_datagram_context_capsule_.context_id, + ",close_code=", + ContextCloseCodeToString(close_datagram_context_capsule_.close_code), + ",close_details=\"", + absl::BytesToHexString(close_datagram_context_capsule_.close_details), + "\")"); + break; + case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: + absl::StrAppend( + &rv, "(error_code=", close_web_transport_session_capsule_.error_code, + ",error_message=\"", + close_web_transport_session_capsule_.error_message, "\")"); + break; + default: + absl::StrAppend(&rv, "[", absl::BytesToHexString(unknown_capsule_data_), + "]"); + break; + } + return rv; +} + +std::ostream& operator<<(std::ostream& os, const Capsule& capsule) { + os << capsule.ToString(); + return os; +} + +CapsuleParser::CapsuleParser(Visitor* visitor) : visitor_(visitor) { + QUICHE_DCHECK_NE(visitor_, nullptr); +} + +QuicBuffer SerializeCapsule(const Capsule& capsule, + QuicBufferAllocator* allocator) { + QuicByteCount capsule_type_length = QuicDataWriter::GetVarInt62Len( + static_cast<uint64_t>(capsule.capsule_type())); + QuicByteCount capsule_data_length; + switch (capsule.capsule_type()) { + case CapsuleType::DATAGRAM: + capsule_data_length = + capsule.datagram_capsule().http_datagram_payload.length(); + if (capsule.datagram_capsule().context_id.has_value()) { + capsule_data_length += QuicDataWriter::GetVarInt62Len( + capsule.datagram_capsule().context_id.value()); + } + break; + case CapsuleType::REGISTER_DATAGRAM_CONTEXT: + capsule_data_length = + QuicDataWriter::GetVarInt62Len( + capsule.register_datagram_context_capsule().context_id) + + QuicDataWriter::GetVarInt62Len(static_cast<uint64_t>( + capsule.register_datagram_context_capsule().format_type)) + + capsule.register_datagram_context_capsule() + .format_additional_data.length(); + break; + case CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT: + capsule_data_length = + QuicDataWriter::GetVarInt62Len(static_cast<uint64_t>( + capsule.register_datagram_no_context_capsule().format_type)) + + capsule.register_datagram_no_context_capsule() + .format_additional_data.length(); + break; + case CapsuleType::CLOSE_DATAGRAM_CONTEXT: + capsule_data_length = + QuicDataWriter::GetVarInt62Len( + capsule.close_datagram_context_capsule().context_id) + + QuicDataWriter::GetVarInt62Len(static_cast<uint64_t>( + capsule.close_datagram_context_capsule().close_code)) + + capsule.close_datagram_context_capsule().close_details.length(); + break; + case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: + capsule_data_length = + sizeof(WebTransportSessionError) + + capsule.close_web_transport_session_capsule().error_message.size(); + break; + default: + capsule_data_length = capsule.unknown_capsule_data().length(); + break; + } + QuicByteCount capsule_length_length = + QuicDataWriter::GetVarInt62Len(capsule_data_length); + QuicByteCount total_capsule_length = + capsule_type_length + capsule_length_length + capsule_data_length; + QuicBuffer buffer(allocator, total_capsule_length); + QuicDataWriter writer(buffer.size(), buffer.data()); + if (!writer.WriteVarInt62(static_cast<uint64_t>(capsule.capsule_type()))) { + QUIC_BUG(capsule type write fail) << "Failed to write CAPSULE type"; + return QuicBuffer(); + } + if (!writer.WriteVarInt62(capsule_data_length)) { + QUIC_BUG(capsule length write fail) << "Failed to write CAPSULE length"; + return QuicBuffer(); + } + switch (capsule.capsule_type()) { + case CapsuleType::DATAGRAM: + if (capsule.datagram_capsule().context_id.has_value()) { + if (!writer.WriteVarInt62( + capsule.datagram_capsule().context_id.value())) { + QUIC_BUG(datagram capsule context ID write fail) + << "Failed to write DATAGRAM CAPSULE context ID"; + return QuicBuffer(); + } + } + if (!writer.WriteStringPiece( + capsule.datagram_capsule().http_datagram_payload)) { + QUIC_BUG(datagram capsule payload write fail) + << "Failed to write DATAGRAM CAPSULE payload"; + return QuicBuffer(); + } + break; + case CapsuleType::REGISTER_DATAGRAM_CONTEXT: + if (!writer.WriteVarInt62( + capsule.register_datagram_context_capsule().context_id)) { + QUIC_BUG(register context capsule context ID write fail) + << "Failed to write REGISTER_DATAGRAM_CONTEXT CAPSULE context ID"; + return QuicBuffer(); + } + if (!writer.WriteVarInt62(static_cast<uint64_t>( + capsule.register_datagram_context_capsule().format_type))) { + QUIC_BUG(register context capsule format type write fail) + << "Failed to write REGISTER_DATAGRAM_CONTEXT CAPSULE format type"; + return QuicBuffer(); + } + if (!writer.WriteStringPiece(capsule.register_datagram_context_capsule() + .format_additional_data)) { + QUIC_BUG(register context capsule additional data write fail) + << "Failed to write REGISTER_DATAGRAM_CONTEXT CAPSULE additional " + "data"; + return QuicBuffer(); + } + break; + case CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT: + if (!writer.WriteVarInt62(static_cast<uint64_t>( + capsule.register_datagram_no_context_capsule().format_type))) { + QUIC_BUG(register no context capsule format type write fail) + << "Failed to write REGISTER_DATAGRAM_NO_CONTEXT CAPSULE format " + "type"; + return QuicBuffer(); + } + if (!writer.WriteStringPiece( + capsule.register_datagram_no_context_capsule() + .format_additional_data)) { + QUIC_BUG(register no context capsule additional data write fail) + << "Failed to write REGISTER_DATAGRAM_NO_CONTEXT CAPSULE " + "additional data"; + return QuicBuffer(); + } + break; + case CapsuleType::CLOSE_DATAGRAM_CONTEXT: + if (!writer.WriteVarInt62( + capsule.close_datagram_context_capsule().context_id)) { + QUIC_BUG(close context capsule context ID write fail) + << "Failed to write CLOSE_DATAGRAM_CONTEXT CAPSULE context ID"; + return QuicBuffer(); + } + if (!writer.WriteVarInt62(static_cast<uint64_t>( + capsule.close_datagram_context_capsule().close_code))) { + QUIC_BUG(close context capsule close code write fail) + << "Failed to write CLOSE_DATAGRAM_CONTEXT CAPSULE close code"; + return QuicBuffer(); + } + if (!writer.WriteStringPiece( + capsule.close_datagram_context_capsule().close_details)) { + QUIC_BUG(close context capsule close details write fail) + << "Failed to write CLOSE_DATAGRAM_CONTEXT CAPSULE close details"; + return QuicBuffer(); + } + break; + case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: + if (!writer.WriteUInt32( + capsule.close_web_transport_session_capsule().error_code)) { + QUIC_BUG(close webtransport session capsule error code write fail) + << "Failed to write CLOSE_WEBTRANSPORT_SESSION error code"; + return QuicBuffer(); + } + if (!writer.WriteStringPiece( + capsule.close_web_transport_session_capsule().error_message)) { + QUIC_BUG(close webtransport session capsule error message write fail) + << "Failed to write CLOSE_WEBTRANSPORT_SESSION error message"; + return QuicBuffer(); + } + break; + default: + if (!writer.WriteStringPiece(capsule.unknown_capsule_data())) { + QUIC_BUG(capsule data write fail) << "Failed to write CAPSULE data"; + return QuicBuffer(); + } + break; + } + if (writer.remaining() != 0) { + QUIC_BUG(capsule write length mismatch) + << "CAPSULE serialization wrote " << writer.length() << " instead of " + << writer.capacity(); + return QuicBuffer(); + } + return buffer; +} + +bool CapsuleParser::IngestCapsuleFragment(absl::string_view capsule_fragment) { + if (parsing_error_occurred_) { + return false; + } + absl::StrAppend(&buffered_data_, capsule_fragment); + while (true) { + const size_t buffered_data_read = AttemptParseCapsule(); + if (parsing_error_occurred_) { + QUICHE_DCHECK_EQ(buffered_data_read, 0u); + buffered_data_.clear(); + return false; + } + if (buffered_data_read == 0) { + break; + } + buffered_data_.erase(0, buffered_data_read); + } + static constexpr size_t kMaxCapsuleBufferSize = 1024 * 1024; + if (buffered_data_.size() > kMaxCapsuleBufferSize) { + buffered_data_.clear(); + ReportParseFailure("Refusing to buffer too much capsule data"); + return false; + } + return true; +} + +size_t CapsuleParser::AttemptParseCapsule() { + QUICHE_DCHECK(!parsing_error_occurred_); + if (buffered_data_.empty()) { + return 0; + } + QuicDataReader capsule_fragment_reader(buffered_data_); + uint64_t capsule_type64; + if (!capsule_fragment_reader.ReadVarInt62(&capsule_type64)) { + QUIC_DVLOG(2) << "Partial read: not enough data to read capsule type"; + return 0; + } + absl::string_view capsule_data; + if (!capsule_fragment_reader.ReadStringPieceVarInt62(&capsule_data)) { + QUIC_DVLOG(2) << "Partial read: not enough data to read capsule length or " + "full capsule data"; + return 0; + } + QuicDataReader capsule_data_reader(capsule_data); + Capsule capsule(static_cast<CapsuleType>(capsule_type64)); + switch (capsule.capsule_type()) { + case CapsuleType::DATAGRAM: + if (datagram_context_id_present_) { + uint64_t context_id; + if (!capsule_data_reader.ReadVarInt62(&context_id)) { + ReportParseFailure("Unable to parse capsule DATAGRAM context ID"); + return 0; + } + capsule.datagram_capsule().context_id = context_id; + } + capsule.datagram_capsule().http_datagram_payload = + capsule_data_reader.ReadRemainingPayload(); + break; + case CapsuleType::REGISTER_DATAGRAM_CONTEXT: + if (!capsule_data_reader.ReadVarInt62( + &capsule.register_datagram_context_capsule().context_id)) { + ReportParseFailure( + "Unable to parse capsule REGISTER_DATAGRAM_CONTEXT context ID"); + return 0; + } + if (!capsule_data_reader.ReadVarInt62(reinterpret_cast<uint64_t*>( + &capsule.register_datagram_context_capsule().format_type))) { + ReportParseFailure( + "Unable to parse capsule REGISTER_DATAGRAM_CONTEXT format type"); + return 0; + } + capsule.register_datagram_context_capsule().format_additional_data = + capsule_data_reader.ReadRemainingPayload(); + break; + case CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT: + if (!capsule_data_reader.ReadVarInt62(reinterpret_cast<uint64_t*>( + &capsule.register_datagram_no_context_capsule().format_type))) { + ReportParseFailure( + "Unable to parse capsule REGISTER_DATAGRAM_NO_CONTEXT format type"); + return 0; + } + capsule.register_datagram_no_context_capsule().format_additional_data = + capsule_data_reader.ReadRemainingPayload(); + break; + case CapsuleType::CLOSE_DATAGRAM_CONTEXT: + if (!capsule_data_reader.ReadVarInt62( + &capsule.close_datagram_context_capsule().context_id)) { + ReportParseFailure( + "Unable to parse capsule CLOSE_DATAGRAM_CONTEXT context ID"); + return 0; + } + if (!capsule_data_reader.ReadVarInt62(reinterpret_cast<uint64_t*>( + &capsule.close_datagram_context_capsule().close_code))) { + ReportParseFailure( + "Unable to parse capsule CLOSE_DATAGRAM_CONTEXT close code"); + return 0; + } + capsule.close_datagram_context_capsule().close_details = + capsule_data_reader.ReadRemainingPayload(); + break; + case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: + if (!capsule_data_reader.ReadUInt32( + &capsule.close_web_transport_session_capsule().error_code)) { + ReportParseFailure( + "Unable to parse capsule CLOSE_WEBTRANSPORT_SESSION error code"); + return 0; + } + capsule.close_web_transport_session_capsule().error_message = + capsule_data_reader.ReadRemainingPayload(); + break; + default: + capsule.unknown_capsule_data() = + capsule_data_reader.ReadRemainingPayload(); + } + if (!visitor_->OnCapsule(capsule)) { + ReportParseFailure("Visitor failed to process capsule"); + return 0; + } + return capsule_fragment_reader.PreviouslyReadPayload().length(); +} + +void CapsuleParser::ReportParseFailure(const std::string& error_message) { + if (parsing_error_occurred_) { + QUIC_BUG(multiple parse errors) << "Experienced multiple parse failures"; + return; + } + parsing_error_occurred_ = true; + visitor_->OnCapsuleParseFailure(error_message); +} + +void CapsuleParser::ErrorIfThereIsRemainingBufferedData() { + if (parsing_error_occurred_) { + return; + } + if (!buffered_data_.empty()) { + ReportParseFailure("Incomplete capsule left at the end of the stream"); + } +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/capsule.h b/chromium/net/third_party/quiche/src/quic/core/http/capsule.h new file mode 100644 index 00000000000..5198b4670d4 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/http/capsule.h @@ -0,0 +1,254 @@ +// Copyright (c) 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_CORE_HTTP_CAPSULE_H_ +#define QUICHE_QUIC_CORE_HTTP_CAPSULE_H_ + +#include <cstdint> +#include <string> + +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "quic/core/quic_buffer_allocator.h" +#include "quic/core/quic_data_reader.h" +#include "quic/core/quic_types.h" +#include "common/platform/api/quiche_logging.h" + +namespace quic { + +enum class CapsuleType : uint64_t { + // Casing in this enum matches the IETF specification. + DATAGRAM = 0xff37a0, + REGISTER_DATAGRAM_CONTEXT = 0xff37a1, + REGISTER_DATAGRAM_NO_CONTEXT = 0xff37a2, + CLOSE_DATAGRAM_CONTEXT = 0xff37a3, + CLOSE_WEBTRANSPORT_SESSION = 0x2843, +}; + +QUIC_EXPORT_PRIVATE std::string CapsuleTypeToString(CapsuleType capsule_type); +QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, + const CapsuleType& capsule_type); + +enum class DatagramFormatType : uint64_t { + // Casing in this enum matches the IETF specification. + UDP_PAYLOAD = 0xff6f00, + WEBTRANSPORT = 0xff7c00, +}; + +QUIC_EXPORT_PRIVATE std::string DatagramFormatTypeToString( + DatagramFormatType datagram_format_type); +QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, const DatagramFormatType& datagram_format_type); + +enum class ContextCloseCode : uint64_t { + // Casing in this enum matches the IETF specification. + CLOSE_NO_ERROR = 0xff78a0, // NO_ERROR already exists in winerror.h. + UNKNOWN_FORMAT = 0xff78a1, + DENIED = 0xff78a2, + RESOURCE_LIMIT = 0xff78a3, +}; + +QUIC_EXPORT_PRIVATE std::string ContextCloseCodeToString( + ContextCloseCode context_close_code); +QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, const ContextCloseCode& context_close_code); + +struct QUIC_EXPORT_PRIVATE DatagramCapsule { + absl::optional<QuicDatagramContextId> context_id; + absl::string_view http_datagram_payload; +}; +struct QUIC_EXPORT_PRIVATE RegisterDatagramContextCapsule { + QuicDatagramContextId context_id; + DatagramFormatType format_type; + absl::string_view format_additional_data; +}; +struct QUIC_EXPORT_PRIVATE RegisterDatagramNoContextCapsule { + DatagramFormatType format_type; + absl::string_view format_additional_data; +}; +struct QUIC_EXPORT_PRIVATE CloseDatagramContextCapsule { + QuicDatagramContextId context_id; + ContextCloseCode close_code; + absl::string_view close_details; +}; +struct QUIC_EXPORT_PRIVATE CloseWebTransportSessionCapsule { + WebTransportSessionError error_code; + absl::string_view error_message; +}; + +// Capsule from draft-ietf-masque-h3-datagram. +// IMPORTANT NOTE: Capsule does not own any of the absl::string_view memory it +// points to. Strings saved into a capsule must outlive the capsule object. Any +// code that sees a capsule in a callback needs to either process it immediately +// or perform its own deep copy. +class QUIC_EXPORT_PRIVATE Capsule { + public: + static Capsule Datagram( + absl::optional<QuicDatagramContextId> context_id = absl::nullopt, + absl::string_view http_datagram_payload = absl::string_view()); + static Capsule RegisterDatagramContext( + QuicDatagramContextId context_id, DatagramFormatType format_type, + absl::string_view format_additional_data = absl::string_view()); + static Capsule RegisterDatagramNoContext( + DatagramFormatType format_type, + absl::string_view format_additional_data = absl::string_view()); + static Capsule CloseDatagramContext( + QuicDatagramContextId context_id, + ContextCloseCode close_code = ContextCloseCode::CLOSE_NO_ERROR, + absl::string_view close_details = absl::string_view()); + static Capsule CloseWebTransportSession( + WebTransportSessionError error_code = 0, + absl::string_view error_message = ""); + static Capsule Unknown( + uint64_t capsule_type, + absl::string_view unknown_capsule_data = absl::string_view()); + + explicit Capsule(CapsuleType capsule_type); + Capsule(const Capsule& other); + Capsule& operator=(const Capsule& other); + bool operator==(const Capsule& other) const; + + // Human-readable information string for debugging purposes. + std::string ToString() const; + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, + const Capsule& capsule); + + CapsuleType capsule_type() const { return capsule_type_; } + DatagramCapsule& datagram_capsule() { + QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::DATAGRAM); + return datagram_capsule_; + } + const DatagramCapsule& datagram_capsule() const { + QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::DATAGRAM); + return datagram_capsule_; + } + RegisterDatagramContextCapsule& register_datagram_context_capsule() { + QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::REGISTER_DATAGRAM_CONTEXT); + return register_datagram_context_capsule_; + } + const RegisterDatagramContextCapsule& register_datagram_context_capsule() + const { + QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::REGISTER_DATAGRAM_CONTEXT); + return register_datagram_context_capsule_; + } + RegisterDatagramNoContextCapsule& register_datagram_no_context_capsule() { + QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT); + return register_datagram_no_context_capsule_; + } + const RegisterDatagramNoContextCapsule& register_datagram_no_context_capsule() + const { + QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT); + return register_datagram_no_context_capsule_; + } + CloseDatagramContextCapsule& close_datagram_context_capsule() { + QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::CLOSE_DATAGRAM_CONTEXT); + return close_datagram_context_capsule_; + } + const CloseDatagramContextCapsule& close_datagram_context_capsule() const { + QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::CLOSE_DATAGRAM_CONTEXT); + return close_datagram_context_capsule_; + } + CloseWebTransportSessionCapsule& close_web_transport_session_capsule() { + QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::CLOSE_WEBTRANSPORT_SESSION); + return close_web_transport_session_capsule_; + } + const CloseWebTransportSessionCapsule& close_web_transport_session_capsule() + const { + QUICHE_DCHECK_EQ(capsule_type_, CapsuleType::CLOSE_WEBTRANSPORT_SESSION); + return close_web_transport_session_capsule_; + } + absl::string_view& unknown_capsule_data() { + QUICHE_DCHECK(capsule_type_ != CapsuleType::DATAGRAM && + capsule_type_ != CapsuleType::REGISTER_DATAGRAM_CONTEXT && + capsule_type_ != CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT && + capsule_type_ != CapsuleType::CLOSE_DATAGRAM_CONTEXT && + capsule_type_ != CapsuleType::CLOSE_WEBTRANSPORT_SESSION) + << capsule_type_; + return unknown_capsule_data_; + } + const absl::string_view& unknown_capsule_data() const { + QUICHE_DCHECK(capsule_type_ != CapsuleType::DATAGRAM && + capsule_type_ != CapsuleType::REGISTER_DATAGRAM_CONTEXT && + capsule_type_ != CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT && + capsule_type_ != CapsuleType::CLOSE_DATAGRAM_CONTEXT && + capsule_type_ != CapsuleType::CLOSE_WEBTRANSPORT_SESSION) + << capsule_type_; + return unknown_capsule_data_; + } + + private: + CapsuleType capsule_type_; + union { + DatagramCapsule datagram_capsule_; + RegisterDatagramContextCapsule register_datagram_context_capsule_; + RegisterDatagramNoContextCapsule register_datagram_no_context_capsule_; + CloseDatagramContextCapsule close_datagram_context_capsule_; + CloseWebTransportSessionCapsule close_web_transport_session_capsule_; + absl::string_view unknown_capsule_data_; + }; +}; + +namespace test { +class CapsuleParserPeer; +} // namespace test + +class QUIC_EXPORT_PRIVATE CapsuleParser { + public: + class QUIC_EXPORT_PRIVATE Visitor { + public: + virtual ~Visitor() {} + + // Called when a capsule has been successfully parsed. The return value + // indicates whether the contents of the capsule are valid: if false is + // returned, the parse operation will be considered failed and + // OnCapsuleParseFailure will be called. Note that since Capsule does not + // own the memory backing its string_views, that memory is only valid until + // this callback returns. Visitors that wish to access the capsule later + // MUST make a deep copy before this returns. + virtual bool OnCapsule(const Capsule& capsule) = 0; + + virtual void OnCapsuleParseFailure(const std::string& error_message) = 0; + }; + + // |visitor| must be non-null, and must outlive CapsuleParser. + explicit CapsuleParser(Visitor* visitor); + + void set_datagram_context_id_present(bool datagram_context_id_present) { + datagram_context_id_present_ = datagram_context_id_present; + } + + // Ingests a capsule fragment (any fragment of bytes from the capsule data + // stream) and parses and complete capsules it encounters. Returns false if a + // parsing error occurred. + bool IngestCapsuleFragment(absl::string_view capsule_fragment); + + void ErrorIfThereIsRemainingBufferedData(); + + friend class test::CapsuleParserPeer; + + private: + // Attempts to parse a single capsule from |buffered_data_|. If a full capsule + // is not available, returns 0. If a parsing error occurs, returns 0. + // Otherwise, returns the number of bytes in the parsed capsule. + size_t AttemptParseCapsule(); + void ReportParseFailure(const std::string& error_message); + + // Whether HTTP Datagram Context IDs are present. + bool datagram_context_id_present_ = false; + // Whether a parsing error has occurred. + bool parsing_error_occurred_ = false; + // Visitor which will receive callbacks, unowned. + Visitor* visitor_; + + std::string buffered_data_; +}; + +// Serializes |capsule| into a newly allocated buffer. +QUIC_EXPORT_PRIVATE QuicBuffer SerializeCapsule(const Capsule& capsule, + QuicBufferAllocator* allocator); + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_CAPSULE_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/http/capsule_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/capsule_test.cc new file mode 100644 index 00000000000..66cb3d07691 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/http/capsule_test.cc @@ -0,0 +1,330 @@ +// Copyright (c) 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "quic/core/http/capsule.h" + +#include <cstddef> +#include <deque> +#include <string> +#include <vector> + +#include "absl/strings/escaping.h" +#include "absl/strings/string_view.h" +#include "quic/platform/api/quic_test.h" +#include "quic/test_tools/quic_test_utils.h" +#include "common/test_tools/quiche_test_utils.h" + +using ::testing::_; +using ::testing::InSequence; +using ::testing::Return; + +namespace quic { +namespace test { + +class CapsuleParserPeer { + public: + static std::string* buffered_data(CapsuleParser* capsule_parser) { + return &capsule_parser->buffered_data_; + } +}; + +namespace { + +constexpr DatagramFormatType kFakeFormatType = + static_cast<DatagramFormatType>(0x123456); +constexpr ContextCloseCode kFakeCloseCode = + static_cast<ContextCloseCode>(0x654321); + +class MockCapsuleParserVisitor : public CapsuleParser::Visitor { + public: + MockCapsuleParserVisitor() { + ON_CALL(*this, OnCapsule(_)).WillByDefault(Return(true)); + } + ~MockCapsuleParserVisitor() override = default; + MOCK_METHOD(bool, OnCapsule, (const Capsule& capsule), (override)); + MOCK_METHOD(void, OnCapsuleParseFailure, (const std::string& error_message), + (override)); +}; + +class CapsuleTest : public QuicTest { + public: + CapsuleTest() : capsule_parser_(&visitor_) {} + + void ValidateParserIsEmpty() { + EXPECT_CALL(visitor_, OnCapsule(_)).Times(0); + EXPECT_CALL(visitor_, OnCapsuleParseFailure(_)).Times(0); + capsule_parser_.ErrorIfThereIsRemainingBufferedData(); + EXPECT_TRUE(CapsuleParserPeer::buffered_data(&capsule_parser_)->empty()); + } + + void TestSerialization(const Capsule& capsule, + const std::string& expected_bytes) { + QuicBuffer serialized_capsule = + SerializeCapsule(capsule, SimpleBufferAllocator::Get()); + quiche::test::CompareCharArraysWithHexError( + "Serialized capsule", serialized_capsule.data(), + serialized_capsule.size(), expected_bytes.data(), + expected_bytes.size()); + } + + ::testing::StrictMock<MockCapsuleParserVisitor> visitor_; + CapsuleParser capsule_parser_; +}; + +TEST_F(CapsuleTest, DatagramCapsule) { + std::string capsule_fragment = absl::HexStringToBytes( + "80ff37a0" // DATAGRAM capsule type + "08" // capsule length + "a1a2a3a4a5a6a7a8" // HTTP Datagram payload + ); + std::string datagram_payload = absl::HexStringToBytes("a1a2a3a4a5a6a7a8"); + Capsule expected_capsule = + Capsule::Datagram(/*context_id=*/absl::nullopt, datagram_payload); + { + EXPECT_CALL(visitor_, OnCapsule(expected_capsule)); + ASSERT_TRUE(capsule_parser_.IngestCapsuleFragment(capsule_fragment)); + } + ValidateParserIsEmpty(); + TestSerialization(expected_capsule, capsule_fragment); +} + +TEST_F(CapsuleTest, DatagramCapsuleWithContext) { + std::string capsule_fragment = absl::HexStringToBytes( + "80ff37a0" // DATAGRAM capsule type + "09" // capsule length + "04" // context ID + "a1a2a3a4a5a6a7a8" // HTTP Datagram payload + ); + capsule_parser_.set_datagram_context_id_present(true); + std::string datagram_payload = absl::HexStringToBytes("a1a2a3a4a5a6a7a8"); + Capsule expected_capsule = + Capsule::Datagram(/*context_id=*/4, datagram_payload); + { + EXPECT_CALL(visitor_, OnCapsule(expected_capsule)); + ASSERT_TRUE(capsule_parser_.IngestCapsuleFragment(capsule_fragment)); + } + ValidateParserIsEmpty(); + TestSerialization(expected_capsule, capsule_fragment); +} + +TEST_F(CapsuleTest, RegisterContextCapsule) { + std::string capsule_fragment = absl::HexStringToBytes( + "80ff37a1" // REGISTER_DATAGRAM_CONTEXT capsule type + "0d" // capsule length + "04" // context ID + "80123456" // 0x123456 datagram format type + "f1f2f3f4f5f6f7f8" // format additional data + ); + std::string format_additional_data = + absl::HexStringToBytes("f1f2f3f4f5f6f7f8"); + Capsule expected_capsule = Capsule::RegisterDatagramContext( + /*context_id=*/4, kFakeFormatType, format_additional_data); + { + EXPECT_CALL(visitor_, OnCapsule(expected_capsule)); + ASSERT_TRUE(capsule_parser_.IngestCapsuleFragment(capsule_fragment)); + } + ValidateParserIsEmpty(); + TestSerialization(expected_capsule, capsule_fragment); +} + +TEST_F(CapsuleTest, RegisterNoContextCapsule) { + std::string capsule_fragment = absl::HexStringToBytes( + "80ff37a2" // REGISTER_DATAGRAM_NO_CONTEXT capsule type + "0c" // capsule length + "80123456" // 0x123456 datagram format type + "f1f2f3f4f5f6f7f8" // format additional data + ); + std::string format_additional_data = + absl::HexStringToBytes("f1f2f3f4f5f6f7f8"); + Capsule expected_capsule = Capsule::RegisterDatagramNoContext( + kFakeFormatType, format_additional_data); + { + EXPECT_CALL(visitor_, OnCapsule(expected_capsule)); + ASSERT_TRUE(capsule_parser_.IngestCapsuleFragment(capsule_fragment)); + } + ValidateParserIsEmpty(); + TestSerialization(expected_capsule, capsule_fragment); +} + +TEST_F(CapsuleTest, CloseContextCapsule) { + std::string capsule_fragment = absl::HexStringToBytes( + "80ff37a3" // CLOSE_DATAGRAM_CONTEXT capsule type + "27" // capsule length + "04" // context ID + "80654321" // 0x654321 close code + ); + std::string close_details = "All your contexts are belong to us"; + capsule_fragment += close_details; + Capsule expected_capsule = Capsule::CloseDatagramContext( + /*context_id=*/4, kFakeCloseCode, close_details); + { + EXPECT_CALL(visitor_, OnCapsule(expected_capsule)); + ASSERT_TRUE(capsule_parser_.IngestCapsuleFragment(capsule_fragment)); + } + ValidateParserIsEmpty(); + TestSerialization(expected_capsule, capsule_fragment); +} + +TEST_F(CapsuleTest, CloseWebTransportStreamCapsule) { + std::string capsule_fragment = absl::HexStringToBytes( + "6843" // CLOSE_WEBTRANSPORT_STREAM capsule type + "09" // capsule length + "00001234" // 0x1234 error code + "68656c6c6f" // "hello" error message + ); + Capsule expected_capsule = Capsule::CloseWebTransportSession( + /*error_code=*/0x1234, /*error_message=*/"hello"); + { + EXPECT_CALL(visitor_, OnCapsule(expected_capsule)); + ASSERT_TRUE(capsule_parser_.IngestCapsuleFragment(capsule_fragment)); + } + ValidateParserIsEmpty(); + TestSerialization(expected_capsule, capsule_fragment); +} + +TEST_F(CapsuleTest, UnknownCapsule) { + std::string capsule_fragment = absl::HexStringToBytes( + "33" // unknown capsule type of 0x33 + "08" // capsule length + "a1a2a3a4a5a6a7a8" // unknown capsule data + ); + std::string unknown_capsule_data = absl::HexStringToBytes("a1a2a3a4a5a6a7a8"); + Capsule expected_capsule = Capsule::Unknown(0x33, unknown_capsule_data); + { + EXPECT_CALL(visitor_, OnCapsule(expected_capsule)); + ASSERT_TRUE(capsule_parser_.IngestCapsuleFragment(capsule_fragment)); + } + ValidateParserIsEmpty(); + TestSerialization(expected_capsule, capsule_fragment); +} + +TEST_F(CapsuleTest, TwoDatagramCapsules) { + std::string capsule_fragment = absl::HexStringToBytes( + "80ff37a0" // DATAGRAM capsule type + "08" // capsule length + "a1a2a3a4a5a6a7a8" // HTTP Datagram payload + "80ff37a0" // DATAGRAM capsule type + "08" // capsule length + "b1b2b3b4b5b6b7b8" // HTTP Datagram payload + ); + std::string datagram_payload1 = absl::HexStringToBytes("a1a2a3a4a5a6a7a8"); + std::string datagram_payload2 = absl::HexStringToBytes("b1b2b3b4b5b6b7b8"); + Capsule expected_capsule1 = + Capsule::Datagram(/*context_id=*/absl::nullopt, datagram_payload1); + Capsule expected_capsule2 = + Capsule::Datagram(/*context_id=*/absl::nullopt, datagram_payload2); + { + InSequence s; + EXPECT_CALL(visitor_, OnCapsule(expected_capsule1)); + EXPECT_CALL(visitor_, OnCapsule(expected_capsule2)); + ASSERT_TRUE(capsule_parser_.IngestCapsuleFragment(capsule_fragment)); + } + ValidateParserIsEmpty(); +} + +TEST_F(CapsuleTest, TwoDatagramCapsulesPartialReads) { + std::string capsule_fragment1 = absl::HexStringToBytes( + "80ff37a0" // first capsule DATAGRAM capsule type + "08" // frist capsule length + "a1a2a3a4" // first half of HTTP Datagram payload of first capsule + ); + std::string capsule_fragment2 = absl::HexStringToBytes( + "a5a6a7a8" // second half of HTTP Datagram payload 1 + "80ff37a0" // second capsule DATAGRAM capsule type + ); + std::string capsule_fragment3 = absl::HexStringToBytes( + "08" // second capsule length + "b1b2b3b4b5b6b7b8" // HTTP Datagram payload of second capsule + ); + capsule_parser_.ErrorIfThereIsRemainingBufferedData(); + std::string datagram_payload1 = absl::HexStringToBytes("a1a2a3a4a5a6a7a8"); + std::string datagram_payload2 = absl::HexStringToBytes("b1b2b3b4b5b6b7b8"); + Capsule expected_capsule1 = + Capsule::Datagram(/*context_id=*/absl::nullopt, datagram_payload1); + Capsule expected_capsule2 = + Capsule::Datagram(/*context_id=*/absl::nullopt, datagram_payload2); + { + InSequence s; + EXPECT_CALL(visitor_, OnCapsule(expected_capsule1)); + EXPECT_CALL(visitor_, OnCapsule(expected_capsule2)); + ASSERT_TRUE(capsule_parser_.IngestCapsuleFragment(capsule_fragment1)); + ASSERT_TRUE(capsule_parser_.IngestCapsuleFragment(capsule_fragment2)); + ASSERT_TRUE(capsule_parser_.IngestCapsuleFragment(capsule_fragment3)); + } + ValidateParserIsEmpty(); +} + +TEST_F(CapsuleTest, TwoDatagramCapsulesOneByteAtATime) { + std::string capsule_fragment = absl::HexStringToBytes( + "80ff37a0" // DATAGRAM capsule type + "08" // capsule length + "a1a2a3a4a5a6a7a8" // HTTP Datagram payload + "80ff37a0" // DATAGRAM capsule type + "08" // capsule length + "b1b2b3b4b5b6b7b8" // HTTP Datagram payload + ); + std::string datagram_payload1 = absl::HexStringToBytes("a1a2a3a4a5a6a7a8"); + std::string datagram_payload2 = absl::HexStringToBytes("b1b2b3b4b5b6b7b8"); + Capsule expected_capsule1 = + Capsule::Datagram(/*context_id=*/absl::nullopt, datagram_payload1); + Capsule expected_capsule2 = + Capsule::Datagram(/*context_id=*/absl::nullopt, datagram_payload2); + for (size_t i = 0; i < capsule_fragment.size(); i++) { + if (i < capsule_fragment.size() / 2 - 1) { + EXPECT_CALL(visitor_, OnCapsule(_)).Times(0); + ASSERT_TRUE( + capsule_parser_.IngestCapsuleFragment(capsule_fragment.substr(i, 1))); + } else if (i == capsule_fragment.size() / 2 - 1) { + EXPECT_CALL(visitor_, OnCapsule(expected_capsule1)); + ASSERT_TRUE( + capsule_parser_.IngestCapsuleFragment(capsule_fragment.substr(i, 1))); + EXPECT_TRUE(CapsuleParserPeer::buffered_data(&capsule_parser_)->empty()); + } else if (i < capsule_fragment.size() - 1) { + EXPECT_CALL(visitor_, OnCapsule(_)).Times(0); + ASSERT_TRUE( + capsule_parser_.IngestCapsuleFragment(capsule_fragment.substr(i, 1))); + } else { + EXPECT_CALL(visitor_, OnCapsule(expected_capsule2)); + ASSERT_TRUE( + capsule_parser_.IngestCapsuleFragment(capsule_fragment.substr(i, 1))); + EXPECT_TRUE(CapsuleParserPeer::buffered_data(&capsule_parser_)->empty()); + } + } + capsule_parser_.ErrorIfThereIsRemainingBufferedData(); + EXPECT_TRUE(CapsuleParserPeer::buffered_data(&capsule_parser_)->empty()); +} + +TEST_F(CapsuleTest, PartialCapsuleThenError) { + std::string capsule_fragment = absl::HexStringToBytes( + "80ff37a0" // DATAGRAM capsule type + "08" // capsule length + "a1a2a3a4" // first half of HTTP Datagram payload + ); + EXPECT_CALL(visitor_, OnCapsule(_)).Times(0); + { + EXPECT_CALL(visitor_, OnCapsuleParseFailure(_)).Times(0); + ASSERT_TRUE(capsule_parser_.IngestCapsuleFragment(capsule_fragment)); + } + { + EXPECT_CALL(visitor_, + OnCapsuleParseFailure( + "Incomplete capsule left at the end of the stream")); + capsule_parser_.ErrorIfThereIsRemainingBufferedData(); + } +} + +TEST_F(CapsuleTest, RejectOverlyLongCapsule) { + std::string capsule_fragment = absl::HexStringToBytes( + "33" // unknown capsule type of 0x33 + "80123456" // capsule length + ) + + std::string(1111111, '?'); + EXPECT_CALL(visitor_, OnCapsuleParseFailure( + "Refusing to buffer too much capsule data")); + EXPECT_FALSE(capsule_parser_.IngestCapsuleFragment(capsule_fragment)); +} + +} // namespace +} // namespace test +} // 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 e64d8e62581..54f6fa9fbdf 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 @@ -80,7 +80,7 @@ using ::testing::_; using ::testing::Assign; using ::testing::Invoke; using ::testing::NiceMock; -using testing::NotNull; +using ::testing::UnorderedElementsAreArray; namespace quic { namespace test { @@ -378,7 +378,10 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { copt.push_back(kILD0); } copt.push_back(kPLE1); - copt.push_back(kRVCM); + if (!GetQuicReloadableFlag( + quic_remove_connection_migration_connection_option)) { + copt.push_back(kRVCM); + } client_config_.SetConnectionOptionsToSend(copt); // Start the server first, because CreateQuicClient() attempts @@ -724,21 +727,37 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { } std::string ReadDataFromWebTransportStreamUntilFin( - WebTransportStream* stream) { + WebTransportStream* stream, MockStreamVisitor* visitor = nullptr) { + QuicStreamId id = stream->GetStreamId(); std::string buffer; + + // Try reading data if immediately available. + WebTransportStream::ReadResult result = stream->Read(&buffer); + if (result.fin) { + return buffer; + } + while (true) { bool can_read = false; - auto visitor = std::make_unique<MockStreamVisitor>(); + if (visitor == nullptr) { + auto visitor_owned = std::make_unique<MockStreamVisitor>(); + visitor = visitor_owned.get(); + stream->SetVisitor(std::move(visitor_owned)); + } EXPECT_CALL(*visitor, OnCanRead()).WillOnce(Assign(&can_read, true)); - stream->SetVisitor(std::move(visitor)); client_->WaitUntil(5000 /*ms*/, [&can_read]() { return can_read; }); if (!can_read) { - ADD_FAILURE() << "Waiting for readable data on stream " - << stream->GetStreamId() << " timed out"; + ADD_FAILURE() << "Waiting for readable data on stream " << id + << " timed out"; + return buffer; + } + if (GetClientSession()->GetOrCreateSpdyDataStream(id) == nullptr) { + ADD_FAILURE() << "Stream " << id + << " was deleted while waiting for incoming data"; return buffer; } - WebTransportStream::ReadResult result = stream->Read(&buffer); + result = stream->Read(&buffer); if (result.fin) { return buffer; } @@ -750,6 +769,19 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { } } + void ReadAllIncomingWebTransportUnidirectionalStreams( + WebTransportSession* session) { + while (true) { + WebTransportStream* received_stream = + session->AcceptIncomingUnidirectionalStream(); + if (received_stream == nullptr) { + break; + } + received_webtransport_unidirectional_streams_.push_back( + ReadDataFromWebTransportStreamUntilFin(received_stream)); + } + } + void WaitForNewConnectionIds() { // Wait until a new server CID is available for another migration. const auto* client_connection = GetClientConnection(); @@ -790,6 +822,7 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { int override_client_connection_id_length_ = -1; uint8_t expected_server_connection_id_length_; bool enable_web_transport_ = false; + std::vector<std::string> received_webtransport_unidirectional_streams_; }; // Run all end to end tests with all supported versions. @@ -799,6 +832,8 @@ INSTANTIATE_TEST_SUITE_P(EndToEndTests, ::testing::PrintToStringParamName()); TEST_P(EndToEndTest, HandshakeSuccessful) { + SetQuicReloadableFlag(quic_delay_sequencer_buffer_allocation_until_new_data, + true); ASSERT_TRUE(Initialize()); EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); ASSERT_TRUE(server_thread_); @@ -854,6 +889,51 @@ TEST_P(EndToEndTest, HandshakeSuccessful) { server_thread_->Resume(); } +TEST_P(EndToEndTest, ExportKeyingMaterial) { + ASSERT_TRUE(Initialize()); + if (!version_.UsesTls()) { + return; + } + const char* kExportLabel = "label"; + const int kExportLen = 30; + std::string client_keying_material_export, server_keying_material_export; + + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); + ASSERT_TRUE(server_thread_); + server_thread_->WaitForCryptoHandshakeConfirmed(); + + server_thread_->Pause(); + QuicSpdySession* server_session = GetServerSession(); + QuicCryptoStream* server_crypto_stream = nullptr; + if (server_session != nullptr) { + server_crypto_stream = + QuicSessionPeer::GetMutableCryptoStream(server_session); + } else { + ADD_FAILURE() << "Missing server session"; + } + if (server_crypto_stream != nullptr) { + ASSERT_TRUE(server_crypto_stream->ExportKeyingMaterial( + kExportLabel, /*context=*/"", kExportLen, + &server_keying_material_export)); + + } else { + ADD_FAILURE() << "Missing server crypto stream"; + } + server_thread_->Resume(); + + QuicSpdyClientSession* client_session = GetClientSession(); + ASSERT_TRUE(client_session); + QuicCryptoStream* client_crypto_stream = + QuicSessionPeer::GetMutableCryptoStream(client_session); + ASSERT_TRUE(client_crypto_stream); + ASSERT_TRUE(client_crypto_stream->ExportKeyingMaterial( + kExportLabel, /*context=*/"", kExportLen, + &client_keying_material_export)); + ASSERT_EQ(client_keying_material_export.size(), + static_cast<size_t>(kExportLen)); + EXPECT_EQ(client_keying_material_export, server_keying_material_export); +} + TEST_P(EndToEndTest, SimpleRequestResponse) { ASSERT_TRUE(Initialize()); @@ -5644,7 +5724,7 @@ class CopyingPacketWriter : public PacketDroppingTestWriter { std::vector<std::unique_ptr<QuicEncryptedPacket>> packets_; }; -TEST_P(EndToEndTest, ChaosProtection) { +TEST_P(EndToEndTest, ChaosProtectionDisabled) { if (!version_.UsesCryptoFrames()) { ASSERT_TRUE(Initialize()); return; @@ -5653,8 +5733,8 @@ TEST_P(EndToEndTest, ChaosProtection) { auto copying_writer = new CopyingPacketWriter(1); delete client_writer_; client_writer_ = copying_writer; - // Enable chaos protection and perform an HTTP request. - client_config_.SetClientConnectionOptions(QuicTagVector{kCHSP}); + // Disable chaos protection and perform an HTTP request. + client_config_.SetClientConnectionOptions(QuicTagVector{kNCHP}); ASSERT_TRUE(Initialize()); SendSynchronousFooRequestAndCheckResponse(); // Parse the saved packet to make sure it's valid. @@ -5667,31 +5747,15 @@ TEST_P(EndToEndTest, ChaosProtection) { // can inspect the contents of this packet. } -TEST_P(EndToEndTest, ChaosProtectionWithMultiPacketChlo) { - if (!version_.UsesCryptoFrames()) { - ASSERT_TRUE(Initialize()); - return; - } - // Enable chaos protection. - client_config_.SetClientConnectionOptions(QuicTagVector{kCHSP}); - // Add a transport parameter to make the client hello span multiple packets. - constexpr auto kCustomParameter = - static_cast<TransportParameters::TransportParameterId>(0xff34); - client_config_.custom_transport_parameters_to_send()[kCustomParameter] = - std::string(2000, '?'); - ASSERT_TRUE(Initialize()); - SendSynchronousFooRequestAndCheckResponse(); -} - -TEST_P(EndToEndTest, PermuteTlsExtensions) { +TEST_P(EndToEndTest, DisablePermuteTlsExtensions) { if (!version_.UsesTls()) { ASSERT_TRUE(Initialize()); return; } - // Enable TLS extension permutation and perform an HTTP request. - client_config_.SetClientConnectionOptions(QuicTagVector{kBPTE}); + // Disable TLS extension permutation and perform an HTTP request. + client_config_.SetClientConnectionOptions(QuicTagVector{kNBPE}); ASSERT_TRUE(Initialize()); - EXPECT_TRUE(GetClientSession()->permutes_tls_extensions()); + EXPECT_FALSE(GetClientSession()->permutes_tls_extensions()); SendSynchronousFooRequestAndCheckResponse(); } @@ -5884,7 +5948,7 @@ TEST_P(EndToEndTest, KeyUpdateInitiatedByBoth) { } TEST_P(EndToEndTest, KeyUpdateInitiatedByConfidentialityLimit) { - SetQuicFlag(FLAGS_quic_key_update_confidentiality_limit, 4U); + SetQuicFlag(FLAGS_quic_key_update_confidentiality_limit, 16U); if (!version_.UsesTls()) { // Key Update is only supported in TLS handshake. @@ -5910,9 +5974,11 @@ TEST_P(EndToEndTest, KeyUpdateInitiatedByConfidentialityLimit) { }, QuicTime::Delta::FromSeconds(5)); - SendSynchronousFooRequestAndCheckResponse(); - SendSynchronousFooRequestAndCheckResponse(); - SendSynchronousFooRequestAndCheckResponse(); + for (uint64_t i = 0; + i < GetQuicFlag(FLAGS_quic_key_update_confidentiality_limit); ++i) { + SendSynchronousFooRequestAndCheckResponse(); + } + // Don't know exactly how many packets will be sent in each request/response, // so just test that at least one key update occurred. EXPECT_LE(1u, client_connection->GetStats().key_update_count); @@ -6049,6 +6115,30 @@ TEST_P(EndToEndTest, WebTransportSessionSetup) { server_thread_->Resume(); } +TEST_P(EndToEndTest, WebTransportSessionSetupWithEchoWithSuffix) { + enable_web_transport_ = true; + ASSERT_TRUE(Initialize()); + + if (!version_.UsesHttp3()) { + return; + } + + // "/echoFoo" should be accepted as "echo" with "set-header" query. + WebTransportHttp3* web_transport = CreateWebTransportSession( + "/echoFoo?set-header=bar:baz", /*wait_for_server_response=*/true); + ASSERT_NE(web_transport, nullptr); + + server_thread_->Pause(); + QuicSpdySession* server_session = GetServerSession(); + EXPECT_TRUE(server_session->GetWebTransportSession(web_transport->id()) != + nullptr); + server_thread_->Resume(); + const spdy::SpdyHeaderBlock* response_headers = client_->response_headers(); + auto it = response_headers->find("bar"); + EXPECT_NE(it, response_headers->end()); + EXPECT_EQ(it->second, "baz"); +} + TEST_P(EndToEndTest, WebTransportSessionWithLoss) { enable_web_transport_ = true; // Enable loss to verify all permutations of receiving SETTINGS and @@ -6087,6 +6177,13 @@ TEST_P(EndToEndTest, WebTransportSessionUnidirectionalStream) { WebTransportStream* outgoing_stream = session->OpenOutgoingUnidirectionalStream(); ASSERT_TRUE(outgoing_stream != nullptr); + + auto stream_visitor = std::make_unique<NiceMock<MockStreamVisitor>>(); + bool data_acknowledged = false; + EXPECT_CALL(*stream_visitor, OnWriteSideInDataRecvdState()) + .WillOnce(Assign(&data_acknowledged, true)); + outgoing_stream->SetVisitor(std::move(stream_visitor)); + EXPECT_TRUE(outgoing_stream->Write("test")); EXPECT_TRUE(outgoing_stream->SendFin()); @@ -6102,6 +6199,10 @@ TEST_P(EndToEndTest, WebTransportSessionUnidirectionalStream) { WebTransportStream::ReadResult result = received_stream->Read(&received_data); EXPECT_EQ(received_data, "test"); EXPECT_TRUE(result.fin); + + client_->WaitUntil(2000, + [&data_acknowledged]() { return data_acknowledged; }); + EXPECT_TRUE(data_acknowledged); } TEST_P(EndToEndTest, WebTransportSessionUnidirectionalStreamSentEarly) { @@ -6152,11 +6253,24 @@ TEST_P(EndToEndTest, WebTransportSessionBidirectionalStream) { WebTransportStream* stream = session->OpenOutgoingBidirectionalStream(); ASSERT_TRUE(stream != nullptr); + + auto stream_visitor_owned = std::make_unique<NiceMock<MockStreamVisitor>>(); + MockStreamVisitor* stream_visitor = stream_visitor_owned.get(); + bool data_acknowledged = false; + EXPECT_CALL(*stream_visitor, OnWriteSideInDataRecvdState()) + .WillOnce(Assign(&data_acknowledged, true)); + stream->SetVisitor(std::move(stream_visitor_owned)); + EXPECT_TRUE(stream->Write("test")); EXPECT_TRUE(stream->SendFin()); - std::string received_data = ReadDataFromWebTransportStreamUntilFin(stream); + std::string received_data = + ReadDataFromWebTransportStreamUntilFin(stream, stream_visitor); EXPECT_EQ(received_data, "test"); + + client_->WaitUntil(2000, + [&data_acknowledged]() { return data_acknowledged; }); + EXPECT_TRUE(data_acknowledged); } TEST_P(EndToEndTest, WebTransportSessionBidirectionalStreamWithBuffering) { @@ -6239,6 +6353,183 @@ TEST_P(EndToEndTest, WebTransportDatagrams) { EXPECT_GT(received, 0); } +TEST_P(EndToEndTest, WebTransportSessionClose) { + enable_web_transport_ = true; + ASSERT_TRUE(Initialize()); + + if (!version_.UsesHttp3()) { + return; + } + + WebTransportHttp3* session = + CreateWebTransportSession("/echo", /*wait_for_server_response=*/true); + ASSERT_TRUE(session != nullptr); + NiceMock<MockClientVisitor>& visitor = SetupWebTransportVisitor(session); + + WebTransportStream* stream = session->OpenOutgoingBidirectionalStream(); + ASSERT_TRUE(stream != nullptr); + QuicStreamId stream_id = stream->GetStreamId(); + EXPECT_TRUE(stream->Write("test")); + // Keep stream open. + + bool close_received = false; + EXPECT_CALL(visitor, OnSessionClosed(42, "test error")) + .WillOnce(Assign(&close_received, true)); + session->CloseSession(42, "test error"); + client_->WaitUntil(2000, [&]() { return close_received; }); + EXPECT_TRUE(close_received); + + QuicSpdyStream* spdy_stream = + GetClientSession()->GetOrCreateSpdyDataStream(stream_id); + EXPECT_TRUE(spdy_stream == nullptr); +} + +TEST_P(EndToEndTest, WebTransportSessionCloseWithoutCapsule) { + enable_web_transport_ = true; + ASSERT_TRUE(Initialize()); + + if (!version_.UsesHttp3()) { + return; + } + + WebTransportHttp3* session = + CreateWebTransportSession("/echo", /*wait_for_server_response=*/true); + ASSERT_TRUE(session != nullptr); + NiceMock<MockClientVisitor>& visitor = SetupWebTransportVisitor(session); + + WebTransportStream* stream = session->OpenOutgoingBidirectionalStream(); + ASSERT_TRUE(stream != nullptr); + QuicStreamId stream_id = stream->GetStreamId(); + EXPECT_TRUE(stream->Write("test")); + // Keep stream open. + + bool close_received = false; + EXPECT_CALL(visitor, OnSessionClosed(0, "")) + .WillOnce(Assign(&close_received, true)); + session->CloseSessionWithFinOnlyForTests(); + client_->WaitUntil(2000, [&]() { return close_received; }); + EXPECT_TRUE(close_received); + + QuicSpdyStream* spdy_stream = + GetClientSession()->GetOrCreateSpdyDataStream(stream_id); + EXPECT_TRUE(spdy_stream == nullptr); +} + +TEST_P(EndToEndTest, WebTransportSessionReceiveClose) { + enable_web_transport_ = true; + ASSERT_TRUE(Initialize()); + + if (!version_.UsesHttp3()) { + return; + } + + WebTransportHttp3* session = CreateWebTransportSession( + "/session-close", /*wait_for_server_response=*/true); + ASSERT_TRUE(session != nullptr); + NiceMock<MockClientVisitor>& visitor = SetupWebTransportVisitor(session); + + WebTransportStream* stream = session->OpenOutgoingUnidirectionalStream(); + ASSERT_TRUE(stream != nullptr); + QuicStreamId stream_id = stream->GetStreamId(); + EXPECT_TRUE(stream->Write("42 test error")); + EXPECT_TRUE(stream->SendFin()); + + // Have some other streams open pending, to ensure they are closed properly. + stream = session->OpenOutgoingUnidirectionalStream(); + stream = session->OpenOutgoingBidirectionalStream(); + + bool close_received = false; + EXPECT_CALL(visitor, OnSessionClosed(42, "test error")) + .WillOnce(Assign(&close_received, true)); + client_->WaitUntil(2000, [&]() { return close_received; }); + EXPECT_TRUE(close_received); + + QuicSpdyStream* spdy_stream = + GetClientSession()->GetOrCreateSpdyDataStream(stream_id); + EXPECT_TRUE(spdy_stream == nullptr); +} + +TEST_P(EndToEndTest, WebTransportSessionStreamTermination) { + enable_web_transport_ = true; + ASSERT_TRUE(Initialize()); + + if (!version_.UsesHttp3()) { + return; + } + + WebTransportHttp3* session = + CreateWebTransportSession("/resets", /*wait_for_server_response=*/true); + ASSERT_TRUE(session != nullptr); + + NiceMock<MockClientVisitor>& visitor = SetupWebTransportVisitor(session); + EXPECT_CALL(visitor, OnIncomingUnidirectionalStreamAvailable()) + .WillRepeatedly([this, session]() { + ReadAllIncomingWebTransportUnidirectionalStreams(session); + }); + + WebTransportStream* stream = session->OpenOutgoingBidirectionalStream(); + QuicStreamId id1 = stream->GetStreamId(); + ASSERT_TRUE(stream != nullptr); + EXPECT_TRUE(stream->Write("test")); + stream->ResetWithUserCode(42); + + // This read fails if the stream is closed in both directions, since that + // results in stream object being deleted. + std::string received_data = ReadDataFromWebTransportStreamUntilFin(stream); + EXPECT_LE(received_data.size(), 4u); + + stream = session->OpenOutgoingBidirectionalStream(); + QuicStreamId id2 = stream->GetStreamId(); + ASSERT_TRUE(stream != nullptr); + EXPECT_TRUE(stream->Write("test")); + stream->SendStopSending(24); + + std::array<std::string, 2> expected_log = { + absl::StrCat("Received reset for stream ", id1, " with error code 42"), + absl::StrCat("Received stop sending for stream ", id2, + " with error code 24"), + }; + client_->WaitUntil(2000, [this, &expected_log]() { + return received_webtransport_unidirectional_streams_.size() >= + expected_log.size(); + }); + EXPECT_THAT(received_webtransport_unidirectional_streams_, + UnorderedElementsAreArray(expected_log)); + + // Since we closed the read side, cleanly closing the write side should result + // in the stream getting deleted. + ASSERT_TRUE(GetClientSession()->GetOrCreateSpdyDataStream(id2) != nullptr); + EXPECT_TRUE(stream->SendFin()); + EXPECT_TRUE(client_->WaitUntil(2000, [this, id2]() { + return GetClientSession()->GetOrCreateSpdyDataStream(id2) == nullptr; + })); +} + +TEST_P(EndToEndTest, WebTransportSession404) { + enable_web_transport_ = true; + ASSERT_TRUE(Initialize()); + + if (!version_.UsesHttp3()) { + return; + } + + WebTransportHttp3* session = CreateWebTransportSession( + "/does-not-exist", /*wait_for_server_response=*/false); + ASSERT_TRUE(session != nullptr); + QuicSpdyStream* connect_stream = client_->latest_created_stream(); + QuicStreamId connect_stream_id = connect_stream->id(); + + WebTransportStream* stream = session->OpenOutgoingBidirectionalStream(); + ASSERT_TRUE(stream != nullptr); + EXPECT_TRUE(stream->Write("test")); + EXPECT_TRUE(stream->SendFin()); + + EXPECT_TRUE(client_->WaitUntil(-1, [this, connect_stream_id]() { + return GetClientSession()->GetOrCreateSpdyDataStream(connect_stream_id) == + nullptr; + })); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_constants.cc b/chromium/net/third_party/quiche/src/quic/core/http/http_constants.cc index f3ed523647f..851fd9186a3 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_constants.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_constants.cc @@ -17,7 +17,8 @@ std::string H3SettingsToString(Http3AndQpackSettingsIdentifiers identifier) { RETURN_STRING_LITERAL(SETTINGS_QPACK_MAX_TABLE_CAPACITY); RETURN_STRING_LITERAL(SETTINGS_MAX_FIELD_SECTION_SIZE); RETURN_STRING_LITERAL(SETTINGS_QPACK_BLOCKED_STREAMS); - RETURN_STRING_LITERAL(SETTINGS_H3_DATAGRAM); + RETURN_STRING_LITERAL(SETTINGS_H3_DATAGRAM_DRAFT00); + RETURN_STRING_LITERAL(SETTINGS_H3_DATAGRAM_DRAFT04); RETURN_STRING_LITERAL(SETTINGS_WEBTRANS_DRAFT00); } return absl::StrCat("UNSUPPORTED_SETTINGS_TYPE(", identifier, ")"); 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 c13a1a8c2b2..ce03a1eea78 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 @@ -38,8 +38,10 @@ enum Http3AndQpackSettingsIdentifiers : uint64_t { // Same value as spdy::SETTINGS_MAX_HEADER_LIST_SIZE. SETTINGS_MAX_FIELD_SECTION_SIZE = 0x06, SETTINGS_QPACK_BLOCKED_STREAMS = 0x07, - // draft-ietf-masque-h3-datagram. - SETTINGS_H3_DATAGRAM = 0x276, + // draft-ietf-masque-h3-datagram-00. + SETTINGS_H3_DATAGRAM_DRAFT00 = 0x276, + // draft-ietf-masque-h3-datagram-04. + SETTINGS_H3_DATAGRAM_DRAFT04 = 0xffd277, // draft-ietf-webtrans-http3-00 SETTINGS_WEBTRANS_DRAFT00 = 0x2b603742, }; diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc index de86955739b..02aa123ed7a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc @@ -5,7 +5,6 @@ #include "quic/core/http/http_decoder.h" #include <cstdint> -#include <limits> #include "absl/base/attributes.h" #include "absl/strings/string_view.h" @@ -21,6 +20,17 @@ namespace quic { +namespace { + +// Limit on the payload length for frames that are buffered by HttpDecoder. +// If a frame header indicating a payload length exceeding this limit is +// received, HttpDecoder closes the connection. Does not apply to frames that +// are not buffered here but each payload fragment is immediately passed to +// Visitor, like HEADERS, DATA, and unknown frames. +constexpr QuicByteCount kPayloadLengthLimit = 1024 * 1024; + +} // anonymous namespace + HttpDecoder::HttpDecoder(Visitor* visitor) : HttpDecoder(visitor, Options()) {} HttpDecoder::HttpDecoder(Visitor* visitor, Options options) : visitor_(visitor), @@ -90,8 +100,11 @@ QuicByteCount HttpDecoder::ProcessInput(const char* data, QuicByteCount len) { QuicDataReader reader(data, len); bool continue_processing = true; - while (continue_processing && - (reader.BytesRemaining() != 0 || state_ == STATE_FINISH_PARSING)) { + // BufferOrParsePayload() and FinishParsing() may need to be called even if + // there is no more data so that they can finish processing the current frame. + while (continue_processing && (reader.BytesRemaining() != 0 || + state_ == STATE_BUFFER_OR_PARSE_PAYLOAD || + state_ == STATE_FINISH_PARSING)) { // |continue_processing| must have been set to false upon error. QUICHE_DCHECK_EQ(QUIC_NO_ERROR, error_); QUICHE_DCHECK_NE(STATE_ERROR, state_); @@ -103,11 +116,14 @@ QuicByteCount HttpDecoder::ProcessInput(const char* data, QuicByteCount len) { case STATE_READING_FRAME_LENGTH: continue_processing = ReadFrameLength(&reader); break; + case STATE_BUFFER_OR_PARSE_PAYLOAD: + continue_processing = BufferOrParsePayload(&reader); + break; case STATE_READING_FRAME_PAYLOAD: continue_processing = ReadFramePayload(&reader); break; case STATE_FINISH_PARSING: - continue_processing = FinishParsing(&reader); + continue_processing = FinishParsing(); break; case STATE_PARSING_NO_LONGER_POSSIBLE: continue_processing = false; @@ -227,11 +243,8 @@ bool HttpDecoder::ReadFrameLength(QuicDataReader* reader) { return false; } - if (current_frame_length_ > MaxFrameLength(current_frame_type_)) { - // MaxFrameLength() returns numeric_limits::max() - // if IsFrameBuffered() is false. - QUICHE_DCHECK(IsFrameBuffered()); - + if (IsFrameBuffered() && + current_frame_length_ > MaxFrameLength(current_frame_type_)) { RaiseError(QUIC_HTTP_FRAME_TOO_LARGE, "Frame is too large."); return false; } @@ -277,6 +290,12 @@ bool HttpDecoder::ReadFrameLength(QuicDataReader* reader) { } remaining_frame_length_ = current_frame_length_; + + if (IsFrameBuffered()) { + state_ = STATE_BUFFER_OR_PARSE_PAYLOAD; + return continue_processing; + } + state_ = (remaining_frame_length_ == 0) ? STATE_FINISH_PARSING : STATE_READING_FRAME_PAYLOAD; return continue_processing; @@ -301,6 +320,7 @@ bool HttpDecoder::IsFrameBuffered() { } bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) { + QUICHE_DCHECK(!IsFrameBuffered()); QUICHE_DCHECK_NE(0u, reader->BytesRemaining()); QUICHE_DCHECK_NE(0u, remaining_frame_length_); @@ -334,7 +354,7 @@ bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) { break; } case static_cast<uint64_t>(HttpFrameType::SETTINGS): { - continue_processing = BufferOrParsePayload(reader); + QUICHE_NOTREACHED(); break; } case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE): { @@ -342,19 +362,19 @@ bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) { break; } case static_cast<uint64_t>(HttpFrameType::GOAWAY): { - continue_processing = BufferOrParsePayload(reader); + QUICHE_NOTREACHED(); break; } case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID): { - continue_processing = BufferOrParsePayload(reader); + QUICHE_NOTREACHED(); break; } case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM): { - continue_processing = BufferOrParsePayload(reader); + QUICHE_NOTREACHED(); break; } case static_cast<uint64_t>(HttpFrameType::ACCEPT_CH): { - continue_processing = BufferOrParsePayload(reader); + QUICHE_NOTREACHED(); break; } default: { @@ -363,26 +383,15 @@ bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) { } } - if (IsFrameBuffered()) { - if (state_ != STATE_READING_FRAME_PAYLOAD) { - // BufferOrParsePayload() has advanced |state_|. - // TODO(bnc): Simplify state transitions. - QUICHE_DCHECK_EQ(STATE_READING_FRAME_TYPE, state_); - QUICHE_DCHECK_EQ(0u, remaining_frame_length_); - } - } else { - QUICHE_DCHECK(state_ == STATE_READING_FRAME_PAYLOAD); - } - - // BufferOrParsePayload() may have advanced |state_|. - if (state_ == STATE_READING_FRAME_PAYLOAD && remaining_frame_length_ == 0) { + if (remaining_frame_length_ == 0) { state_ = STATE_FINISH_PARSING; } return continue_processing; } -bool HttpDecoder::FinishParsing(QuicDataReader* reader) { +bool HttpDecoder::FinishParsing() { + QUICHE_DCHECK(!IsFrameBuffered()); QUICHE_DCHECK_EQ(0u, remaining_frame_length_); bool continue_processing = true; @@ -401,9 +410,7 @@ bool HttpDecoder::FinishParsing(QuicDataReader* reader) { break; } case static_cast<uint64_t>(HttpFrameType::SETTINGS): { - // If frame payload is not empty, FinishParsing() is skipped. - QUICHE_DCHECK_EQ(0u, current_frame_length_); - continue_processing = BufferOrParsePayload(reader); + QUICHE_NOTREACHED(); break; } case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE): { @@ -411,37 +418,33 @@ bool HttpDecoder::FinishParsing(QuicDataReader* reader) { break; } case static_cast<uint64_t>(HttpFrameType::GOAWAY): { - // If frame payload is not empty, FinishParsing() is skipped. - QUICHE_DCHECK_EQ(0u, current_frame_length_); - continue_processing = BufferOrParsePayload(reader); + QUICHE_NOTREACHED(); break; } case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID): { - // If frame payload is not empty, FinishParsing() is skipped. - QUICHE_DCHECK_EQ(0u, current_frame_length_); - continue_processing = BufferOrParsePayload(reader); + QUICHE_NOTREACHED(); break; } case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM): { - // If frame payload is not empty, FinishParsing() is skipped. - QUICHE_DCHECK_EQ(0u, current_frame_length_); - continue_processing = BufferOrParsePayload(reader); + QUICHE_NOTREACHED(); break; } case static_cast<uint64_t>(HttpFrameType::ACCEPT_CH): { - // If frame payload is not empty, FinishParsing() is skipped. - QUICHE_DCHECK_EQ(0u, current_frame_length_); - continue_processing = BufferOrParsePayload(reader); + QUICHE_NOTREACHED(); break; } default: continue_processing = visitor_->OnUnknownFrameEnd(); } + ResetForNextFrame(); + return continue_processing; +} + +void HttpDecoder::ResetForNextFrame() { current_length_field_length_ = 0; current_type_field_length_ = 0; state_ = STATE_READING_FRAME_TYPE; - return continue_processing; } bool HttpDecoder::HandleUnknownFramePayload(QuicDataReader* reader) { @@ -460,44 +463,39 @@ bool HttpDecoder::BufferOrParsePayload(QuicDataReader* reader) { QUICHE_DCHECK_EQ(current_frame_length_, buffer_.size() + remaining_frame_length_); - bool continue_processing = true; - if (buffer_.empty() && reader->BytesRemaining() >= current_frame_length_) { // |*reader| contains entire payload, which might be empty. remaining_frame_length_ = 0; QuicDataReader current_payload_reader(reader->PeekRemainingPayload().data(), current_frame_length_); - continue_processing = ParseEntirePayload(¤t_payload_reader); - reader->Seek(current_frame_length_); - } else { - if (buffer_.empty()) { - buffer_.reserve(current_frame_length_); - } + bool continue_processing = ParseEntirePayload(¤t_payload_reader); - // Buffer as much of the payload as |*reader| contains. - QuicByteCount bytes_to_read = std::min<QuicByteCount>( - remaining_frame_length_, reader->BytesRemaining()); - absl::StrAppend(&buffer_, reader->PeekRemainingPayload().substr( - /* pos = */ 0, bytes_to_read)); - reader->Seek(bytes_to_read); - remaining_frame_length_ -= bytes_to_read; + reader->Seek(current_frame_length_); + ResetForNextFrame(); + return continue_processing; + } - QUICHE_DCHECK_EQ(current_frame_length_, - buffer_.size() + remaining_frame_length_); + // Buffer as much of the payload as |*reader| contains. + QuicByteCount bytes_to_read = std::min<QuicByteCount>( + remaining_frame_length_, reader->BytesRemaining()); + absl::StrAppend(&buffer_, reader->PeekRemainingPayload().substr( + /* pos = */ 0, bytes_to_read)); + reader->Seek(bytes_to_read); + remaining_frame_length_ -= bytes_to_read; - if (remaining_frame_length_ > 0) { - QUICHE_DCHECK(reader->IsDoneReading()); - return true; - } + QUICHE_DCHECK_EQ(current_frame_length_, + buffer_.size() + remaining_frame_length_); - QuicDataReader buffer_reader(buffer_); - continue_processing = ParseEntirePayload(&buffer_reader); - buffer_.clear(); + if (remaining_frame_length_ > 0) { + QUICHE_DCHECK(reader->IsDoneReading()); + return false; } - current_length_field_length_ = 0; - current_type_field_length_ = 0; - state_ = STATE_READING_FRAME_TYPE; + QuicDataReader buffer_reader(buffer_); + bool continue_processing = ParseEntirePayload(&buffer_reader); + buffer_.clear(); + + ResetForNextFrame(); return continue_processing; } @@ -654,23 +652,22 @@ bool HttpDecoder::ParseAcceptChFrame(QuicDataReader* reader, } QuicByteCount HttpDecoder::MaxFrameLength(uint64_t frame_type) { + QUICHE_DCHECK(IsFrameBuffered()); + switch (frame_type) { case static_cast<uint64_t>(HttpFrameType::SETTINGS): - // This limit is arbitrary. - return 1024 * 1024; + return kPayloadLengthLimit; case static_cast<uint64_t>(HttpFrameType::GOAWAY): return VARIABLE_LENGTH_INTEGER_LENGTH_8; case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID): return VARIABLE_LENGTH_INTEGER_LENGTH_8; case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM): - // This limit is arbitrary. - return 1024 * 1024; + return kPayloadLengthLimit; case static_cast<uint64_t>(HttpFrameType::ACCEPT_CH): - // This limit is arbitrary. - return 1024 * 1024; + return kPayloadLengthLimit; default: - // Other frames require no data buffering, so it's safe to have no limit. - return std::numeric_limits<QuicByteCount>::max(); + QUICHE_NOTREACHED(); + return 0; } } diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h index 002ff68f008..118fbcf59f5 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h @@ -155,8 +155,14 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { enum HttpDecoderState { STATE_READING_FRAME_LENGTH, STATE_READING_FRAME_TYPE, + + // States used for buffered frame types + STATE_BUFFER_OR_PARSE_PAYLOAD, + + // States used for non-buffered frame types STATE_READING_FRAME_PAYLOAD, STATE_FINISH_PARSING, + STATE_PARSING_NO_LONGER_POSSIBLE, STATE_ERROR }; @@ -188,7 +194,10 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // empty, and it calls BufferOrParsePayload(). For other frame types, this // method directly calls visitor methods to signal that frame had been // received completely. Returns whether processing should continue. - bool FinishParsing(QuicDataReader* reader); + bool FinishParsing(); + + // Reset internal fields to prepare for reading next frame. + void ResetForNextFrame(); // Read payload of unknown frame from |reader| and call // Visitor::OnUnknownFramePayload(). Returns true decoding should continue, diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc index 0266632aae7..95d5913896b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc @@ -27,7 +27,6 @@ using ::testing::InSequence; using ::testing::Return; namespace quic { - namespace test { class HttpDecoderPeer { @@ -37,9 +36,11 @@ class HttpDecoderPeer { } }; -class MockVisitor : public HttpDecoder::Visitor { +namespace { + +class MockHttpDecoderVisitor : public HttpDecoder::Visitor { public: - ~MockVisitor() override = default; + ~MockHttpDecoderVisitor() override = default; // Called if an error is detected. MOCK_METHOD(void, OnError, (HttpDecoder*), (override)); @@ -164,7 +165,7 @@ class HttpDecoderTest : public QuicTest { return processed_bytes; } - testing::StrictMock<MockVisitor> visitor_; + testing::StrictMock<MockHttpDecoderVisitor> visitor_; HttpDecoder decoder_; }; @@ -626,6 +627,25 @@ TEST_F(HttpDecoderTest, MaxPushIdWithOverlyLargePayload) { EXPECT_EQ("Frame is too large.", decoder_.error_detail()); } +TEST_F(HttpDecoderTest, FrameWithOverlyLargePayload) { + // Regression test for b/193919867: Ensure that reading frames with incredibly + // large payload lengths does not lead to allocating unbounded memory. + constexpr size_t max_input_length = + /*max frame type varint length*/ sizeof(uint64_t) + + /*max frame length varint length*/ sizeof(uint64_t) + + /*one byte of payload*/ sizeof(uint8_t); + char input[max_input_length]; + for (uint64_t frame_type = 0; frame_type < 1025; frame_type++) { + ::testing::NiceMock<MockHttpDecoderVisitor> visitor; + HttpDecoder decoder(&visitor); + QuicDataWriter writer(max_input_length, input); + ASSERT_TRUE(writer.WriteVarInt62(frame_type)); // frame type. + ASSERT_TRUE(writer.WriteVarInt62(kVarInt62MaxValue)); // frame length. + ASSERT_TRUE(writer.WriteUInt8(0x00)); // one byte of payload. + EXPECT_NE(decoder.ProcessInput(input, writer.length()), 0u) << frame_type; + } +} + TEST_F(HttpDecoderTest, MalformedSettingsFrame) { char input[30]; QuicDataWriter writer(30, input); @@ -1049,7 +1069,7 @@ TEST_F(HttpDecoderTest, WebTransportStreamDisabled) { TEST(HttpDecoderTestNoFixture, WebTransportStream) { HttpDecoder::Options options; options.allow_web_transport_stream = true; - testing::StrictMock<MockVisitor> visitor; + testing::StrictMock<MockHttpDecoderVisitor> visitor; HttpDecoder decoder(&visitor, options); // WebTransport stream for session ID 0x104, with four bytes of extra data. @@ -1062,7 +1082,7 @@ TEST(HttpDecoderTestNoFixture, WebTransportStream) { TEST(HttpDecoderTestNoFixture, WebTransportStreamError) { HttpDecoder::Options options; options.allow_web_transport_stream = true; - testing::StrictMock<MockVisitor> visitor; + testing::StrictMock<MockHttpDecoderVisitor> visitor; HttpDecoder decoder(&visitor, options); std::string input = absl::HexStringToBytes("404100"); @@ -1111,6 +1131,6 @@ TEST_F(HttpDecoderTest, DecodeSettings) { EXPECT_FALSE(HttpDecoder::DecodeSettings(input.data(), input.size(), &out)); } +} // namespace } // namespace test - } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h index 0666c3ac02e..ec071f09c3f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h @@ -84,7 +84,8 @@ class QUIC_EXPORT_PRIVATE QuicClientPromisedInfo private: friend class test::QuicClientPromisedInfoPeer; - class QUIC_EXPORT_PRIVATE CleanupAlarm : public QuicAlarm::Delegate { + class QUIC_EXPORT_PRIVATE CleanupAlarm + : public QuicAlarm::DelegateWithoutContext { public: explicit CleanupAlarm(QuicClientPromisedInfo* promised) : promised_(promised) {} 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 9c164d0e448..3c6546fb8f3 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 @@ -24,7 +24,6 @@ QuicReceiveControlStream::QuicReceiveControlStream( QuicSpdySession* spdy_session) : QuicStream(pending, spdy_session, - READ_UNIDIRECTIONAL, /*is_static=*/true), settings_frame_received_(false), decoder_(this), 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 79555e05967..92831f2d14d 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 @@ -31,7 +31,7 @@ void QuicSendControlStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) { << "OnStreamReset() called for write unidirectional stream."; } -bool QuicSendControlStream::OnStopSending(QuicRstStreamErrorCode /* code */) { +bool QuicSendControlStream::OnStopSending(QuicResetStreamError /* code */) { stream_delegate()->OnStreamError( QUIC_HTTP_CLOSED_CRITICAL_STREAM, "STOP_SENDING received for send control stream"); 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 face80632af..d376a63c48b 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 @@ -31,7 +31,7 @@ class QUIC_EXPORT_PRIVATE QuicSendControlStream : public QuicStream { // Overriding QuicStream::OnStopSending() to make sure control stream is never // closed before connection. void OnStreamReset(const QuicRstStreamFrame& frame) override; - bool OnStopSending(QuicRstStreamErrorCode code) override; + bool OnStopSending(QuicResetStreamError code) override; // Send SETTINGS frame if it hasn't been sent yet. Settings frame must be the // first frame sent on this stream. diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc index 3066cb38871..59435852623 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc @@ -71,9 +71,7 @@ class QuicSendControlStreamTest : public QuicTestWithParam<TestParams> { public: QuicSendControlStreamTest() : connection_(new StrictMock<MockQuicConnection>( - &helper_, - &alarm_factory_, - perspective(), + &helper_, &alarm_factory_, perspective(), SupportedVersions(GetParam().version))), session_(connection_) { ON_CALL(session_, WritevData(_, _, _, _, _, _)) @@ -104,8 +102,7 @@ class QuicSendControlStreamTest : public QuicTestWithParam<TestParams> { QuicSendControlStream* send_control_stream_; }; -INSTANTIATE_TEST_SUITE_P(Tests, - QuicSendControlStreamTest, +INSTANTIATE_TEST_SUITE_P(Tests, QuicSendControlStreamTest, ::testing::ValuesIn(GetTestParams()), ::testing::PrintToStringParamName()); @@ -133,24 +130,27 @@ TEST_P(QuicSendControlStreamTest, WriteSettings) { "4040" // 0x40 as the reserved frame type "01" // 1 byte frame length "61"); // payload "a" - if (QuicSpdySessionPeer::ShouldNegotiateHttp3Datagram(&session_)) { + if (QuicSpdySessionPeer::LocalHttpDatagramSupport(&session_) == + HttpDatagramSupport::kDraft00And04) { expected_write_data = absl::HexStringToBytes( - "00" // stream type: control stream - "04" // frame type: SETTINGS frame - "0e" // frame length - "01" // SETTINGS_QPACK_MAX_TABLE_CAPACITY - "40ff" // 255 - "06" // SETTINGS_MAX_HEADER_LIST_SIZE - "4400" // 1024 - "07" // SETTINGS_QPACK_BLOCKED_STREAMS - "10" // 16 - "4040" // 0x40 as the reserved settings id - "14" // 20 - "4276" // SETTINGS_H3_DATAGRAM - "01" // 1 - "4040" // 0x40 as the reserved frame type - "01" // 1 byte frame length - "61"); // payload "a" + "00" // stream type: control stream + "04" // frame type: SETTINGS frame + "0e" // frame length + "01" // SETTINGS_QPACK_MAX_TABLE_CAPACITY + "40ff" // 255 + "06" // SETTINGS_MAX_HEADER_LIST_SIZE + "4400" // 1024 + "07" // SETTINGS_QPACK_BLOCKED_STREAMS + "10" // 16 + "4040" // 0x40 as the reserved settings id + "14" // 20 + "4276" // SETTINGS_H3_DATAGRAM_DRAFT00 + "01" // 1 + "800ffd277" // SETTINGS_H3_DATAGRAM_DRAFT04 + "01" // 1 + "4040" // 0x40 as the reserved frame type + "01" // 1 byte frame length + "61"); // payload "a" } auto buffer = std::make_unique<char[]>(expected_write_data.size()); @@ -216,7 +216,8 @@ TEST_P(QuicSendControlStreamTest, CloseControlStream) { Initialize(); EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_CLOSED_CRITICAL_STREAM, _, _)); - send_control_stream_->OnStopSending(QUIC_STREAM_CANCELLED); + send_control_stream_->OnStopSending( + QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED)); } TEST_P(QuicSendControlStreamTest, ReceiveDataOnSendControlStream) { 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 69d6cb47a9d..1f6ec744f53 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 @@ -286,15 +286,19 @@ QuicSSLConfig QuicServerSessionBase::GetSSLConfig() const { QUICHE_DCHECK(crypto_config_ && crypto_config_->proof_source()); QuicSSLConfig ssl_config = QuicSpdySession::GetSSLConfig(); - if (!GetQuicReloadableFlag(quic_tls_set_signature_algorithm_prefs) || - !crypto_config_ || !crypto_config_->proof_source()) { + + if (quic_tls_disable_resumption_refactor()) { + ssl_config.disable_ticket_support = + GetQuicFlag(FLAGS_quic_disable_server_tls_resumption); + } + + if (!crypto_config_ || !crypto_config_->proof_source()) { return ssl_config; } absl::InlinedVector<uint16_t, 8> signature_algorithms = crypto_config_->proof_source()->SupportedTlsSignatureAlgorithms(); if (!signature_algorithms.empty()) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_tls_set_signature_algorithm_prefs, 1, 2); ssl_config.signing_algorithm_prefs = std::move(signature_algorithms); } 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 ac7fc1e59c8..279ff60798b 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 @@ -17,6 +17,7 @@ #include "quic/core/quic_connection.h" #include "quic/core/quic_crypto_server_stream.h" #include "quic/core/quic_crypto_server_stream_base.h" +#include "quic/core/quic_types.h" #include "quic/core/quic_utils.h" #include "quic/core/tls_server_handshaker.h" #include "quic/platform/api/quic_expect_bug.h" @@ -93,8 +94,8 @@ class TestServerSession : public QuicServerSessionBase { } QuicSpdyStream* CreateIncomingStream(PendingStream* pending) override { - QuicSpdyStream* stream = new QuicSimpleServerStream( - pending, this, BIDIRECTIONAL, quic_simple_server_backend_); + QuicSpdyStream* stream = + new QuicSimpleServerStream(pending, this, quic_simple_server_backend_); ActivateStream(absl::WrapUnique(stream)); return stream; } 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 45bdd1d31b1..898398f59e3 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 @@ -185,8 +185,7 @@ bool QuicSpdyClientSession::ShouldCreateIncomingStream(QuicStreamId id) { QuicSpdyStream* QuicSpdyClientSession::CreateIncomingStream( PendingStream* pending) { - QuicSpdyStream* stream = - new QuicSpdyClientStream(pending, this, READ_UNIDIRECTIONAL); + QuicSpdyStream* stream = new QuicSpdyClientStream(pending, this); ActivateStream(absl::WrapUnique(stream)); return stream; } 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 d264ba64879..7391fdc79ae 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 @@ -31,9 +31,8 @@ QuicSpdyClientStream::QuicSpdyClientStream(QuicStreamId id, has_preliminary_headers_(false) {} QuicSpdyClientStream::QuicSpdyClientStream(PendingStream* pending, - QuicSpdyClientSession* session, - StreamType type) - : QuicSpdyStream(pending, session, type), + QuicSpdyClientSession* session) + : QuicSpdyStream(pending, session), content_length_(-1), response_code_(0), header_bytes_read_(0), @@ -62,8 +61,13 @@ void QuicSpdyClientStream::OnInitialHeadersComplete( if (web_transport() != nullptr) { web_transport()->HeadersReceived(response_headers_); if (!web_transport()->ready()) { - // Rejected due to status not being 200, or other reason. - WriteOrBufferData("", /*fin=*/true, nullptr); + // The request was rejected by WebTransport, typically due to not having a + // 2xx status. The reason we're using Reset() here rather than closing + // cleanly is that even if the server attempts to send us any form of body + // with a 4xx request, we've already set up the capsule parser, and we + // don't have any way to process anything from the response body in + // question. + Reset(QUIC_STREAM_CANCELLED); return; } } diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h index 231846508e4..8637d11bb43 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h @@ -25,8 +25,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdyClientStream : public QuicSpdyStream { QuicSpdyClientSession* session, StreamType type); QuicSpdyClientStream(PendingStream* pending, - QuicSpdyClientSession* spdy_session, - StreamType type); + QuicSpdyClientSession* spdy_session); QuicSpdyClientStream(const QuicSpdyClientStream&) = delete; QuicSpdyClientStream& operator=(const QuicSpdyClientStream&) = delete; ~QuicSpdyClientStream() override; diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.cc index c2e7845edb7..93f1c2bfe7b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.cc @@ -16,9 +16,8 @@ QuicSpdyServerStreamBase::QuicSpdyServerStreamBase(QuicStreamId id, : QuicSpdyStream(id, session, type) {} QuicSpdyServerStreamBase::QuicSpdyServerStreamBase(PendingStream* pending, - QuicSpdySession* session, - StreamType type) - : QuicSpdyStream(pending, session, type) {} + QuicSpdySession* session) + : QuicSpdyStream(pending, session) {} void QuicSpdyServerStreamBase::CloseWriteSide() { if (!fin_received() && !rst_received() && sequencer()->ignore_read_data() && diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.h index 252addfaab6..21ecc0baa65 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.h @@ -14,9 +14,7 @@ class QUIC_NO_EXPORT QuicSpdyServerStreamBase : public QuicSpdyStream { QuicSpdyServerStreamBase(QuicStreamId id, QuicSpdySession* session, StreamType type); - QuicSpdyServerStreamBase(PendingStream* pending, - QuicSpdySession* session, - StreamType type); + QuicSpdyServerStreamBase(PendingStream* pending, QuicSpdySession* session); QuicSpdyServerStreamBase(const QuicSpdyServerStreamBase&) = delete; QuicSpdyServerStreamBase& operator=(const QuicSpdyServerStreamBase&) = delete; diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base_test.cc index fe630de5108..aa41b3c37cd 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base_test.cc @@ -19,8 +19,7 @@ namespace { class TestQuicSpdyServerStream : public QuicSpdyServerStreamBase { public: - TestQuicSpdyServerStream(QuicStreamId id, - QuicSpdySession* session, + TestQuicSpdyServerStream(QuicStreamId id, QuicSpdySession* session, StreamType type) : QuicSpdyServerStreamBase(id, session, type) {} @@ -30,8 +29,7 @@ class TestQuicSpdyServerStream : public QuicSpdyServerStreamBase { class QuicSpdyServerStreamBaseTest : public QuicTest { protected: QuicSpdyServerStreamBaseTest() - : session_(new MockQuicConnection(&helper_, - &alarm_factory_, + : session_(new MockQuicConnection(&helper_, &alarm_factory_, Perspective::IS_SERVER)) { session_.Initialize(); session_.connection()->SetEncrypter( @@ -56,10 +54,15 @@ TEST_F(QuicSpdyServerStreamBaseTest, stream_->StopReading(); if (session_.version().UsesHttp3()) { - EXPECT_CALL(session_, MaybeSendStopSendingFrame(_, QUIC_STREAM_NO_ERROR)) + EXPECT_CALL(session_, + MaybeSendStopSendingFrame(_, QuicResetStreamError::FromInternal( + QUIC_STREAM_NO_ERROR))) .Times(1); } else { - EXPECT_CALL(session_, MaybeSendRstStreamFrame(_, QUIC_STREAM_NO_ERROR, _)) + EXPECT_CALL( + session_, + MaybeSendRstStreamFrame( + _, QuicResetStreamError::FromInternal(QUIC_STREAM_NO_ERROR), _)) .Times(1); } QuicStreamPeer::SetFinSent(stream_); @@ -73,9 +76,10 @@ TEST_F(QuicSpdyServerStreamBaseTest, EXPECT_CALL(session_, MaybeSendRstStreamFrame( _, - VersionHasIetfQuicFrames(session_.transport_version()) - ? QUIC_STREAM_CANCELLED - : QUIC_RST_ACKNOWLEDGEMENT, + QuicResetStreamError::FromInternal( + VersionHasIetfQuicFrames(session_.transport_version()) + ? QUIC_STREAM_CANCELLED + : QUIC_RST_ACKNOWLEDGEMENT), _)) .Times(1); QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(), 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 56b0db3ff48..0cd2644abcb 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 @@ -33,8 +33,6 @@ #include "spdy/core/http2_frame_decoder_adapter.h" using http2::Http2DecoderAdapter; -using spdy::HpackEntry; -using spdy::HpackHeaderTable; using spdy::Http2WeightToSpdy3Priority; using spdy::Spdy3PriorityToHttp2Weight; using spdy::SpdyErrorCode; @@ -195,8 +193,7 @@ class QuicSpdySession::SpdyFramerVisitor header_list_.Clear(); } - void OnStreamFrameData(SpdyStreamId /*stream_id*/, - const char* /*data*/, + void OnStreamFrameData(SpdyStreamId /*stream_id*/, const char* /*data*/, size_t /*len*/) override { QUICHE_DCHECK(!VersionUsesHttp3(session_->transport_version())); CloseConnection("SPDY DATA frame received.", @@ -284,8 +281,7 @@ class QuicSpdySession::SpdyFramerVisitor code); } - void OnDataFrameHeader(SpdyStreamId /*stream_id*/, - size_t /*length*/, + void OnDataFrameHeader(SpdyStreamId /*stream_id*/, size_t /*length*/, bool /*fin*/) override { QUICHE_DCHECK(!VersionUsesHttp3(session_->transport_version())); CloseConnection("SPDY DATA frame received.", @@ -318,13 +314,9 @@ class QuicSpdySession::SpdyFramerVisitor QUIC_INVALID_HEADERS_STREAM_DATA); } - void OnHeaders(SpdyStreamId stream_id, - bool has_priority, - int weight, - SpdyStreamId /* parent_stream_id */, - bool /* exclusive */, - bool fin, - bool /*end*/) override { + void OnHeaders(SpdyStreamId stream_id, bool has_priority, int weight, + SpdyStreamId /* parent_stream_id */, bool /* exclusive */, + bool fin, bool /*end*/) override { if (!session_->IsConnected()) { return; } @@ -352,8 +344,7 @@ class QuicSpdySession::SpdyFramerVisitor QUIC_INVALID_HEADERS_STREAM_DATA); } - void OnPushPromise(SpdyStreamId stream_id, - SpdyStreamId promised_stream_id, + void OnPushPromise(SpdyStreamId stream_id, SpdyStreamId promised_stream_id, bool /*end*/) override { QUICHE_DCHECK(!VersionUsesHttp3(session_->transport_version())); if (session_->perspective() != Perspective::IS_CLIENT) { @@ -369,10 +360,8 @@ class QuicSpdySession::SpdyFramerVisitor void OnContinuation(SpdyStreamId /*stream_id*/, bool /*end*/) override {} - void OnPriority(SpdyStreamId stream_id, - SpdyStreamId /* parent_id */, - int weight, - bool /* exclusive */) override { + void OnPriority(SpdyStreamId stream_id, SpdyStreamId /* parent_id */, + int weight, bool /* exclusive */) override { QUICHE_DCHECK(!VersionUsesHttp3(session_->transport_version())); if (!session_->IsConnected()) { return; @@ -395,10 +384,8 @@ class QuicSpdySession::SpdyFramerVisitor } // SpdyFramerDebugVisitorInterface implementation - void OnSendCompressedFrame(SpdyStreamId /*stream_id*/, - SpdyFrameType /*type*/, - size_t payload_len, - size_t frame_len) override { + void OnSendCompressedFrame(SpdyStreamId /*stream_id*/, SpdyFrameType /*type*/, + size_t payload_len, size_t frame_len) override { if (payload_len == 0) { QUIC_BUG(quic_bug_10360_1) << "Zero payload length."; return; @@ -470,9 +457,6 @@ QuicSpdySession::QuicSpdySession( h2_deframer_.set_visitor(spdy_framer_visitor_.get()); h2_deframer_.set_debug_visitor(spdy_framer_visitor_.get()); spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get()); - if (decline_server_push_stream_) { - QUIC_RELOADABLE_FLAG_COUNT(quic_decline_server_push_stream); - } } QuicSpdySession::~QuicSpdySession() { @@ -523,8 +507,17 @@ void QuicSpdySession::FillSettingsFrame() { qpack_maximum_blocked_streams_; settings_.values[SETTINGS_MAX_FIELD_SECTION_SIZE] = max_inbound_header_list_size_; - if (ShouldNegotiateHttp3Datagram() && version().UsesHttp3()) { - settings_.values[SETTINGS_H3_DATAGRAM] = 1; + if (version().UsesHttp3()) { + HttpDatagramSupport local_http_datagram_support = + LocalHttpDatagramSupport(); + if (local_http_datagram_support == HttpDatagramSupport::kDraft00 || + local_http_datagram_support == HttpDatagramSupport::kDraft00And04) { + settings_.values[SETTINGS_H3_DATAGRAM_DRAFT00] = 1; + } + if (local_http_datagram_support == HttpDatagramSupport::kDraft04 || + local_http_datagram_support == HttpDatagramSupport::kDraft00And04) { + settings_.values[SETTINGS_H3_DATAGRAM_DRAFT04] = 1; + } } if (WillNegotiateWebTransport()) { settings_.values[SETTINGS_WEBTRANS_DRAFT00] = 1; @@ -548,8 +541,7 @@ void QuicSpdySession::OnEncoderStreamError(QuicErrorCode error_code, } void QuicSpdySession::OnStreamHeadersPriority( - QuicStreamId stream_id, - const spdy::SpdyStreamPrecedence& precedence) { + QuicStreamId stream_id, const spdy::SpdyStreamPrecedence& precedence) { QuicSpdyStream* stream = GetOrCreateSpdyDataStream(stream_id); if (!stream) { // It's quite possible to receive headers after a stream has been reset. @@ -558,8 +550,7 @@ void QuicSpdySession::OnStreamHeadersPriority( stream->OnStreamHeadersPriority(precedence); } -void QuicSpdySession::OnStreamHeaderList(QuicStreamId stream_id, - bool fin, +void QuicSpdySession::OnStreamHeaderList(QuicStreamId stream_id, bool fin, size_t frame_len, const QuicHeaderList& header_list) { if (IsStaticStream(stream_id)) { @@ -598,8 +589,7 @@ void QuicSpdySession::OnStreamHeaderList(QuicStreamId stream_id, } void QuicSpdySession::OnPriorityFrame( - QuicStreamId stream_id, - const spdy::SpdyStreamPrecedence& precedence) { + QuicStreamId stream_id, const spdy::SpdyStreamPrecedence& precedence) { QuicSpdyStream* stream = GetOrCreateSpdyDataStream(stream_id); if (!stream) { // It's quite possible to receive a PRIORITY frame after a stream has been @@ -676,9 +666,7 @@ size_t QuicSpdySession::ProcessHeaderData(const struct iovec& iov) { } size_t QuicSpdySession::WriteHeadersOnHeadersStream( - QuicStreamId id, - SpdyHeaderBlock headers, - bool fin, + QuicStreamId id, SpdyHeaderBlock headers, bool fin, const spdy::SpdyStreamPrecedence& precedence, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { QUICHE_DCHECK(!VersionUsesHttp3(transport_version())); @@ -691,8 +679,7 @@ size_t QuicSpdySession::WriteHeadersOnHeadersStream( } size_t QuicSpdySession::WritePriority(QuicStreamId id, - QuicStreamId parent_stream_id, - int weight, + QuicStreamId parent_stream_id, int weight, bool exclusive) { QUICHE_DCHECK(!VersionUsesHttp3(transport_version())); SpdyPriorityIR priority_frame(id, parent_stream_id, weight, exclusive); @@ -764,7 +751,6 @@ bool QuicSpdySession::OnStreamsBlockedFrame( void QuicSpdySession::SendHttp3GoAway(QuicErrorCode error_code, const std::string& reason) { - QUICHE_DCHECK_EQ(perspective(), Perspective::IS_SERVER); QUICHE_DCHECK(VersionUsesHttp3(transport_version())); if (!IsEncryptionEstablished()) { QUIC_CODE_COUNT(quic_h3_goaway_before_encryption_established); @@ -871,8 +857,7 @@ QuicSpdyStream* QuicSpdySession::GetOrCreateSpdyDataStream( } void QuicSpdySession::OnNewEncryptionKeyAvailable( - EncryptionLevel level, - std::unique_ptr<QuicEncrypter> encrypter) { + EncryptionLevel level, std::unique_ptr<QuicEncrypter> encrypter) { QuicSession::OnNewEncryptionKeyAvailable(level, std::move(encrypter)); if (IsEncryptionEstablished()) { // Send H3 SETTINGs once encryption is established. @@ -880,13 +865,11 @@ void QuicSpdySession::OnNewEncryptionKeyAvailable( } } -bool QuicSpdySession::ShouldNegotiateWebTransport() { - return false; -} +bool QuicSpdySession::ShouldNegotiateWebTransport() { return false; } bool QuicSpdySession::WillNegotiateWebTransport() { - return ShouldNegotiateHttp3Datagram() && version().UsesHttp3() && - ShouldNegotiateWebTransport(); + return LocalHttpDatagramSupport() != HttpDatagramSupport::kNone && + version().UsesHttp3() && ShouldNegotiateWebTransport(); } // True if there are open HTTP requests. @@ -896,20 +879,20 @@ bool QuicSpdySession::ShouldKeepConnectionAlive() const { return GetNumActiveStreams() + pending_streams_size() > 0; } -bool QuicSpdySession::UsesPendingStreams() const { - // QuicSpdySession supports PendingStreams, therefore this method should - // eventually just return true. However, pending streams can only be used if - // unidirectional stream type is supported. - return VersionUsesHttp3(transport_version()); +bool QuicSpdySession::UsesPendingStreamForFrame(QuicFrameType type, + QuicStreamId stream_id) const { + // Pending streams can only be used to handle unidirectional stream with + // STREAM & RESET_STREAM frames in IETF QUIC. + return VersionUsesHttp3(transport_version()) && + (type == STREAM_FRAME || type == RST_STREAM_FRAME) && + QuicUtils::GetStreamType(stream_id, perspective(), + IsIncomingStream(stream_id), + version()) == READ_UNIDIRECTIONAL; } size_t QuicSpdySession::WriteHeadersOnHeadersStreamImpl( - QuicStreamId id, - spdy::SpdyHeaderBlock headers, - bool fin, - QuicStreamId parent_stream_id, - int weight, - bool exclusive, + QuicStreamId id, spdy::SpdyHeaderBlock headers, bool fin, + QuicStreamId parent_stream_id, int weight, bool exclusive, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { QUICHE_DCHECK(!VersionUsesHttp3(transport_version())); @@ -943,10 +926,8 @@ size_t QuicSpdySession::WriteHeadersOnHeadersStreamImpl( } void QuicSpdySession::OnPromiseHeaderList( - QuicStreamId /*stream_id*/, - QuicStreamId /*promised_stream_id*/, - size_t /*frame_len*/, - const QuicHeaderList& /*header_list*/) { + QuicStreamId /*stream_id*/, QuicStreamId /*promised_stream_id*/, + size_t /*frame_len*/, const QuicHeaderList& /*header_list*/) { std::string error = "OnPromiseHeaderList should be overridden in client code."; QUIC_BUG(quic_bug_10360_6) << error; @@ -976,8 +957,7 @@ bool QuicSpdySession::ResumeApplicationState(ApplicationState* cached_state) { } absl::optional<std::string> QuicSpdySession::OnAlpsData( - const uint8_t* alps_data, - size_t alps_length) { + const uint8_t* alps_data, size_t alps_length) { AlpsFrameDecoder alps_frame_decoder(this); HttpDecoder decoder(&alps_frame_decoder); decoder.ProcessInput(reinterpret_cast<const char*>(alps_data), alps_length); @@ -1045,6 +1025,21 @@ absl::optional<std::string> QuicSpdySession::OnSettingsFrameViaAlps( return absl::nullopt; } +bool QuicSpdySession::VerifySettingIsZeroOrOne(uint64_t id, uint64_t value) { + if (value == 0 || value == 1) { + return true; + } + std::string error_details = absl::StrCat( + "Received ", + H3SettingsToString(static_cast<Http3AndQpackSettingsIdentifiers>(id)), + " with invalid value ", value); + QUIC_PEER_BUG(bad received setting) << ENDPOINT << error_details; + // TODO(dschinazi) use QUIC_HTTP_INVALID_SETTING_VALUE instead of + // QUIC_HTTP_RECEIVE_SPDY_SETTING once cl/396439351 lands. + CloseConnectionWithDetails(QUIC_HTTP_RECEIVE_SPDY_SETTING, error_details); + return false; +} + bool QuicSpdySession::OnSetting(uint64_t id, uint64_t value) { any_settings_received_ = true; @@ -1129,24 +1124,47 @@ bool QuicSpdySession::OnSetting(uint64_t id, uint64_t value) { absl::StrCat("received HTTP/2 specific setting in HTTP/3 session: ", id)); return false; - case SETTINGS_H3_DATAGRAM: { - if (!ShouldNegotiateHttp3Datagram()) { + case SETTINGS_H3_DATAGRAM_DRAFT00: { + HttpDatagramSupport local_http_datagram_support = + LocalHttpDatagramSupport(); + if (local_http_datagram_support != HttpDatagramSupport::kDraft00 && + local_http_datagram_support != HttpDatagramSupport::kDraft00And04) { + break; + } + QUIC_DVLOG(1) << ENDPOINT + << "SETTINGS_H3_DATAGRAM_DRAFT00 received with value " + << value; + if (!version().UsesHttp3()) { + break; + } + if (!VerifySettingIsZeroOrOne(id, value)) { + return false; + } + if (value && http_datagram_support_ != HttpDatagramSupport::kDraft04) { + // If both draft-00 and draft-04 are supported, use draft-04. + http_datagram_support_ = HttpDatagramSupport::kDraft00; + } + break; + } + case SETTINGS_H3_DATAGRAM_DRAFT04: { + HttpDatagramSupport local_http_datagram_support = + LocalHttpDatagramSupport(); + if (local_http_datagram_support != HttpDatagramSupport::kDraft04 && + local_http_datagram_support != HttpDatagramSupport::kDraft00And04) { break; } - QUIC_DVLOG(1) << ENDPOINT << "SETTINGS_H3_DATAGRAM received with value " + QUIC_DVLOG(1) << ENDPOINT + << "SETTINGS_H3_DATAGRAM_DRAFT04 received with value " << value; if (!version().UsesHttp3()) { break; } - if (value != 0 && value != 1) { - std::string error_details = absl::StrCat( - "received SETTINGS_H3_DATAGRAM with invalid value ", value); - QUIC_PEER_BUG(quic_peer_bug_10360_7) << ENDPOINT << error_details; - CloseConnectionWithDetails(QUIC_HTTP_RECEIVE_SPDY_SETTING, - error_details); + if (!VerifySettingIsZeroOrOne(id, value)) { return false; } - h3_datagram_supported_ = !!value; + if (value) { + http_datagram_support_ = HttpDatagramSupport::kDraft04; + } break; } case SETTINGS_WEBTRANS_DRAFT00: @@ -1156,14 +1174,7 @@ bool QuicSpdySession::OnSetting(uint64_t id, uint64_t value) { QUIC_DVLOG(1) << ENDPOINT << "SETTINGS_ENABLE_WEBTRANSPORT received with value " << value; - if (value != 0 && value != 1) { - std::string error_details = absl::StrCat( - "received SETTINGS_ENABLE_WEBTRANSPORT with invalid value ", - value); - QUIC_PEER_BUG(invalid SETTINGS_ENABLE_WEBTRANSPORT value) - << ENDPOINT << error_details; - CloseConnectionWithDetails(QUIC_HTTP_RECEIVE_SPDY_SETTING, - error_details); + if (!VerifySettingIsZeroOrOne(id, value)) { return false; } peer_supports_webtransport_ = (value == 1); @@ -1236,8 +1247,7 @@ bool QuicSpdySession::ShouldReleaseHeadersStreamSequencerBuffer() { return false; } -void QuicSpdySession::OnHeaders(SpdyStreamId stream_id, - bool has_priority, +void QuicSpdySession::OnHeaders(SpdyStreamId stream_id, bool has_priority, const spdy::SpdyStreamPrecedence& precedence, bool fin) { if (has_priority) { @@ -1361,13 +1371,9 @@ QuicStream* QuicSpdySession::ProcessPendingStream(PendingStream* pending) { return receive_control_stream_; } case kServerPushStream: { // Push Stream. - if (decline_server_push_stream_) { - CloseConnectionWithDetails(QUIC_HTTP_RECEIVE_SERVER_PUSH, - "Received server push stream"); - return nullptr; - } - QuicSpdyStream* stream = CreateIncomingStream(pending); - return stream; + CloseConnectionWithDetails(QUIC_HTTP_RECEIVE_SERVER_PUSH, + "Received server push stream"); + return nullptr; } case kQpackEncoderStream: { // QPACK encoder stream. if (qpack_encoder_receive_stream_) { @@ -1423,7 +1429,9 @@ QuicStream* QuicSpdySession::ProcessPendingStream(PendingStream* pending) { default: break; } - MaybeSendStopSendingFrame(pending->id(), QUIC_STREAM_STREAM_CREATION_ERROR); + MaybeSendStopSendingFrame( + pending->id(), + QuicResetStreamError::FromInternal(QUIC_STREAM_STREAM_CREATION_ERROR)); pending->StopReading(); return nullptr; } @@ -1576,9 +1584,7 @@ void QuicSpdySession::CloseConnectionOnDuplicateHttp3UnidirectionalStreams( // static void QuicSpdySession::LogHeaderCompressionRatioHistogram( - bool using_qpack, - bool is_sent, - QuicByteCount compressed, + bool using_qpack, bool is_sent, QuicByteCount compressed, QuicByteCount uncompressed) { if (compressed <= 0 || uncompressed <= 0) { return; @@ -1624,15 +1630,25 @@ MessageStatus QuicSpdySession::SendHttp3Datagram( QuicDatagramStreamId stream_id, absl::optional<QuicDatagramContextId> context_id, absl::string_view payload) { + if (!SupportsH3Datagram()) { + QUIC_BUG(send http datagram too early) + << "Refusing to send HTTP Datagram before SETTINGS received"; + return MESSAGE_STATUS_INTERNAL_ERROR; + } + uint64_t stream_id_to_write = stream_id; + if (http_datagram_support_ != HttpDatagramSupport::kDraft00) { + // Stream ID is sent divided by four as per the specification. + stream_id_to_write /= kHttpDatagramStreamIdDivisor; + } size_t slice_length = - QuicDataWriter::GetVarInt62Len(stream_id) + payload.length(); + QuicDataWriter::GetVarInt62Len(stream_id_to_write) + payload.length(); if (context_id.has_value()) { slice_length += QuicDataWriter::GetVarInt62Len(context_id.value()); } QuicBuffer buffer(connection()->helper()->GetStreamSendBufferAllocator(), slice_length); QuicDataWriter writer(slice_length, buffer.data()); - if (!writer.WriteVarInt62(stream_id)) { + if (!writer.WriteVarInt62(stream_id_to_write)) { QUIC_BUG(h3 datagram stream ID write fail) << "Failed to write HTTP/3 datagram stream ID"; return MESSAGE_STATUS_INTERNAL_ERROR; @@ -1673,8 +1689,8 @@ void QuicSpdySession::UnregisterHttp3DatagramFlowId( void QuicSpdySession::OnMessageReceived(absl::string_view message) { QuicSession::OnMessageReceived(message); - if (!h3_datagram_supported_) { - QUIC_DLOG(ERROR) << "Ignoring unexpected received HTTP/3 datagram"; + if (!SupportsH3Datagram()) { + QUIC_DLOG(INFO) << "Ignoring unexpected received HTTP/3 datagram"; return; } QuicDataReader reader(message); @@ -1683,7 +1699,12 @@ void QuicSpdySession::OnMessageReceived(absl::string_view message) { QUIC_DLOG(ERROR) << "Failed to parse stream ID in received HTTP/3 datagram"; return; } - if (perspective() == Perspective::IS_SERVER) { + if (http_datagram_support_ != HttpDatagramSupport::kDraft00) { + // Stream ID is sent divided by four as per the specification. + stream_id64 *= kHttpDatagramStreamIdDivisor; + } + if (perspective() == Perspective::IS_SERVER && + http_datagram_support_ == HttpDatagramSupport::kDraft00) { auto it = h3_datagram_flow_id_to_stream_id_map_.find(stream_id64); if (it == h3_datagram_flow_id_to_stream_id_map_.end()) { QUIC_DLOG(INFO) << "Received unknown HTTP/3 datagram flow ID " @@ -1713,10 +1734,14 @@ void QuicSpdySession::OnMessageReceived(absl::string_view message) { } bool QuicSpdySession::SupportsWebTransport() { - return WillNegotiateWebTransport() && h3_datagram_supported_ && + return WillNegotiateWebTransport() && SupportsH3Datagram() && peer_supports_webtransport_; } +bool QuicSpdySession::SupportsH3Datagram() const { + return http_datagram_support_ != HttpDatagramSupport::kNone; +} + WebTransportHttp3* QuicSpdySession::GetWebTransportSession( WebTransportSessionId id) { if (!SupportsWebTransport()) { @@ -1747,8 +1772,7 @@ void QuicSpdySession::OnStreamWaitingForClientSettings(QuicStreamId id) { } void QuicSpdySession::AssociateIncomingWebTransportStreamWithSession( - WebTransportSessionId session_id, - QuicStreamId stream_id) { + WebTransportSessionId session_id, QuicStreamId stream_id) { if (QuicUtils::IsOutgoingStreamId(version(), stream_id, perspective())) { QUIC_BUG(AssociateIncomingWebTransportStreamWithSession got outgoing stream) << ENDPOINT @@ -1844,8 +1868,29 @@ void QuicSpdySession::DatagramObserver::OnDatagramProcessed( session_->OnDatagramProcessed(status); } -bool QuicSpdySession::ShouldNegotiateHttp3Datagram() { - return false; +HttpDatagramSupport QuicSpdySession::LocalHttpDatagramSupport() { + return HttpDatagramSupport::kNone; +} + +std::string HttpDatagramSupportToString( + HttpDatagramSupport http_datagram_support) { + switch (http_datagram_support) { + case HttpDatagramSupport::kNone: + return "None"; + case HttpDatagramSupport::kDraft00: + return "Draft00"; + case HttpDatagramSupport::kDraft04: + return "Draft04"; + case HttpDatagramSupport::kDraft00And04: + return "Draft00And04"; + } + return absl::StrCat("Unknown(", static_cast<int>(http_datagram_support), ")"); +} + +std::ostream& operator<<(std::ostream& os, + const HttpDatagramSupport& http_datagram_support) { + os << HttpDatagramSupportToString(http_datagram_support); + return os; } #undef ENDPOINT // undef for jumbo builds 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 78c132a6ad6..d8503cc05aa 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 @@ -6,6 +6,7 @@ #define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_SESSION_H_ #include <cstddef> +#include <cstdint> #include <list> #include <memory> #include <string> @@ -90,8 +91,8 @@ class QUIC_EXPORT_PRIVATE Http3DebugVisitor { virtual void OnDataFrameReceived(QuicStreamId /*stream_id*/, QuicByteCount /*payload_length*/) {} virtual void OnHeadersFrameReceived( - QuicStreamId /*stream_id*/, - QuicByteCount /*compressed_headers_length*/) {} + QuicStreamId /*stream_id*/, QuicByteCount /*compressed_headers_length*/) { + } virtual void OnHeadersDecoded(QuicStreamId /*stream_id*/, QuicHeaderList /*headers*/) {} @@ -119,6 +120,20 @@ class QUIC_EXPORT_PRIVATE Http3DebugVisitor { virtual void OnSettingsFrameResumed(const SettingsFrame& /*frame*/) {} }; +// Whether draft-ietf-masque-h3-datagram is supported on this session and if so +// which draft is currently in use. +enum class HttpDatagramSupport : uint8_t { + kNone = 0, // HTTP Datagrams are not supported for this session. + kDraft00 = 1, + kDraft04 = 2, + kDraft00And04 = 3, // only used locally, we only negotiate one draft. +}; + +QUIC_EXPORT_PRIVATE std::string HttpDatagramSupportToString( + HttpDatagramSupport http_datagram_support); +QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, const HttpDatagramSupport& http_datagram_support); + // A QUIC session for HTTP. class QUIC_EXPORT_PRIVATE QuicSpdySession : public QuicSession, @@ -126,8 +141,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession public QpackDecoder::EncoderStreamErrorDelegate { public: // Does not take ownership of |connection| or |visitor|. - QuicSpdySession(QuicConnection* connection, - QuicSession::Visitor* visitor, + QuicSpdySession(QuicConnection* connection, QuicSession::Visitor* visitor, const QuicConfig& config, const ParsedQuicVersionVector& supported_versions); QuicSpdySession(const QuicSpdySession&) = delete; @@ -148,14 +162,12 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // Called by |headers_stream_| when headers with a priority have been // received for a stream. This method will only be called for server streams. virtual void OnStreamHeadersPriority( - QuicStreamId stream_id, - const spdy::SpdyStreamPrecedence& precedence); + QuicStreamId stream_id, const spdy::SpdyStreamPrecedence& precedence); // Called by |headers_stream_| when headers have been completely received // for a stream. |fin| will be true if the fin flag was set in the headers // frame. - virtual void OnStreamHeaderList(QuicStreamId stream_id, - bool fin, + virtual void OnStreamHeaderList(QuicStreamId stream_id, bool fin, size_t frame_len, const QuicHeaderList& header_list); @@ -192,18 +204,14 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // If provided, |ack_notifier_delegate| will be registered to be notified when // we have seen ACKs for all packets resulting from this call. virtual size_t WriteHeadersOnHeadersStream( - QuicStreamId id, - spdy::SpdyHeaderBlock headers, - bool fin, + QuicStreamId id, spdy::SpdyHeaderBlock headers, bool fin, const spdy::SpdyStreamPrecedence& precedence, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); // Writes an HTTP/2 PRIORITY frame the to peer. Returns the size in bytes of // the resulting PRIORITY frame. - size_t WritePriority(QuicStreamId id, - QuicStreamId parent_stream_id, - int weight, - bool exclusive); + size_t WritePriority(QuicStreamId id, QuicStreamId parent_stream_id, + int weight, bool exclusive); // Writes an HTTP/3 PRIORITY_UPDATE frame to the peer. void WriteHttp3PriorityUpdate(const PriorityUpdateFrame& priority_update); @@ -349,8 +357,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // In order for measurements for different protocol to be comparable, the // caller must ensure that uncompressed size is the total length of header // names and values without any overhead. - static void LogHeaderCompressionRatioHistogram(bool using_qpack, - bool is_sent, + static void LogHeaderCompressionRatioHistogram(bool using_qpack, bool is_sent, QuicByteCount compressed, QuicByteCount uncompressed); @@ -374,9 +381,11 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // extension. virtual void OnAcceptChFrameReceivedViaAlps(const AcceptChFrame& /*frame*/); - // Whether HTTP/3 datagrams are supported on this session, based on received - // SETTINGS. - bool h3_datagram_supported() const { return h3_datagram_supported_; } + // Whether HTTP datagrams are supported on this session and which draft is in + // use, based on received SETTINGS. + HttpDatagramSupport http_datagram_support() const { + return http_datagram_support_; + } // This must not be used except by QuicSpdyStream::SendHttp3Datagram. MessageStatus SendHttp3Datagram( @@ -400,7 +409,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession bool SupportsWebTransport(); // Indicates whether both the peer and us support HTTP/3 Datagrams. - bool SupportsH3Datagram() { return h3_datagram_supported_; } + bool SupportsH3Datagram() const; // Indicates whether the HTTP/3 session will indicate WebTransport support to // the peer. @@ -414,7 +423,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // until the SETTINGS are received. Only works for HTTP/3. bool ShouldBufferRequestsUntilSettings() { return version().UsesHttp3() && perspective() == Perspective::IS_SERVER && - ShouldNegotiateHttp3Datagram(); + LocalHttpDatagramSupport() != HttpDatagramSupport::kNone; } // Returns if the incoming bidirectional streams should process data. This is @@ -427,8 +436,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // Links the specified stream with a WebTransport session. If the session is // not present, it is buffered until a corresponding stream is found. void AssociateIncomingWebTransportStreamWithSession( - WebTransportSessionId session_id, - QuicStreamId stream_id); + WebTransportSessionId session_id, QuicStreamId stream_id); void ProcessBufferedWebTransportStreamsForSession(WebTransportHttp3* session); @@ -478,7 +486,8 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession bool ShouldKeepConnectionAlive() const override; // Overridden to buffer incoming unidirectional streams for version 99. - bool UsesPendingStreams() const override; + bool UsesPendingStreamForFrame(QuicFrameType type, + QuicStreamId stream_id) const override; // Processes incoming unidirectional streams; parses the stream type, and // creates a new stream of the corresponding type. Returns the pointer to the @@ -486,17 +495,12 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession QuicStream* ProcessPendingStream(PendingStream* pending) override; size_t WriteHeadersOnHeadersStreamImpl( - QuicStreamId id, - spdy::SpdyHeaderBlock headers, - bool fin, - QuicStreamId parent_stream_id, - int weight, - bool exclusive, + QuicStreamId id, spdy::SpdyHeaderBlock headers, bool fin, + QuicStreamId parent_stream_id, int weight, bool exclusive, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); void OnNewEncryptionKeyAvailable( - EncryptionLevel level, - std::unique_ptr<QuicEncrypter> encrypter) override; + EncryptionLevel level, std::unique_ptr<QuicEncrypter> encrypter) override; // Sets the maximum size of the header compression table spdy_framer_ is // willing to use to encode header blocks. @@ -519,8 +523,9 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // Called whenever a datagram is dequeued or dropped from datagram_queue(). virtual void OnDatagramProcessed(absl::optional<MessageStatus> status); - // Returns true if HTTP/3 datagram extension should be supported. - virtual bool ShouldNegotiateHttp3Datagram(); + // Returns which version of the HTTP/3 datagram extension we should advertise + // in settings and accept remote settings for. + virtual HttpDatagramSupport LocalHttpDatagramSupport(); private: friend class test::QuicSpdySessionPeer; @@ -546,10 +551,8 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // The following methods are called by the SimpleVisitor. // Called when a HEADERS frame has been received. - void OnHeaders(spdy::SpdyStreamId stream_id, - bool has_priority, - const spdy::SpdyStreamPrecedence& precedence, - bool fin); + void OnHeaders(spdy::SpdyStreamId stream_id, bool has_priority, + const spdy::SpdyStreamPrecedence& precedence, bool fin); // Called when a PRIORITY frame has been received. void OnPriority(spdy::SpdyStreamId stream_id, @@ -567,6 +570,8 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession void FillSettingsFrame(); + bool VerifySettingIsZeroOrOne(uint64_t id, uint64_t value); + std::unique_ptr<QpackEncoder> qpack_encoder_; std::unique_ptr<QpackDecoder> qpack_decoder_; @@ -654,8 +659,9 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // frame has been sent yet. absl::optional<uint64_t> last_sent_http3_goaway_id_; - // Whether both this endpoint and our peer support HTTP/3 datagrams. - bool h3_datagram_supported_ = false; + // Whether both this endpoint and our peer support HTTP datagrams and which + // draft is in use for this session. + HttpDatagramSupport http_datagram_support_ = HttpDatagramSupport::kNone; // Whether the peer has indicated WebTransport support. bool peer_supports_webtransport_ = false; @@ -678,10 +684,6 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // Limited to kMaxUnassociatedWebTransportStreams; when the list is full, // oldest streams are evicated first. std::list<BufferedWebTransportStream> buffered_streams_; - - // Latched value of flag_quic_decline_server_push_stream. - const bool decline_server_push_stream_ = - GetQuicReloadableFlag(quic_decline_server_push_stream); }; } // 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 5a984323de4..5c3e5255cce 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 @@ -186,6 +186,14 @@ class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker { void OnConnectionClosed(QuicErrorCode /*error*/, ConnectionCloseSource /*source*/) override {} + SSL* GetSsl() const override { return nullptr; } + + bool ExportKeyingMaterial(absl::string_view /*label*/, + absl::string_view /*context*/, + size_t /*result_len*/, + std::string* /*result*/) override { + return false; + } private: using QuicCryptoStream::session; @@ -208,16 +216,15 @@ class TestStream : public QuicSpdyStream { TestStream(QuicStreamId id, QuicSpdySession* session, StreamType type) : QuicSpdyStream(id, session, type) {} - TestStream(PendingStream* pending, QuicSpdySession* session, StreamType type) - : QuicSpdyStream(pending, session, type) {} + TestStream(PendingStream* pending, QuicSpdySession* session) + : QuicSpdyStream(pending, session) {} using QuicStream::CloseWriteSide; void OnBodyAvailable() override {} MOCK_METHOD(void, OnCanWrite, (), (override)); - MOCK_METHOD(bool, - RetransmitStreamData, + MOCK_METHOD(bool, RetransmitStreamData, (QuicStreamOffset, QuicByteCount, bool, TransmissionType), (override)); @@ -227,9 +234,7 @@ class TestStream : public QuicSpdyStream { class TestSession : public QuicSpdySession { public: explicit TestSession(QuicConnection* connection) - : QuicSpdySession(connection, - nullptr, - DefaultQuicConfig(), + : QuicSpdySession(connection, nullptr, DefaultQuicConfig(), CurrentSupportedVersions()), crypto_stream_(this), writev_consumes_all_data_(false) { @@ -286,11 +291,7 @@ class TestSession : public QuicSpdySession { } TestStream* CreateIncomingStream(PendingStream* pending) override { - QuicStreamId id = pending->id(); - TestStream* stream = new TestStream( - pending, this, - DetermineStreamType(id, connection()->version(), perspective(), - /*is_incoming=*/true, BIDIRECTIONAL)); + TestStream* stream = new TestStream(pending, this); ActivateStream(absl::WrapUnique(stream)); return stream; } @@ -353,11 +354,11 @@ class TestSession : public QuicSpdySession { bool ShouldNegotiateWebTransport() override { return supports_webtransport_; } void set_supports_webtransport(bool value) { supports_webtransport_ = value; } - bool ShouldNegotiateHttp3Datagram() override { - return should_negotiate_h3_datagram_; + HttpDatagramSupport LocalHttpDatagramSupport() override { + return local_http_datagram_support_; } - void set_should_negotiate_h3_datagram(bool value) { - should_negotiate_h3_datagram_ = value; + void set_local_http_datagram_support(HttpDatagramSupport value) { + local_http_datagram_support_ = value; } MOCK_METHOD(void, OnAcceptChFrame, (const AcceptChFrame&), (override)); @@ -365,14 +366,14 @@ class TestSession : public QuicSpdySession { using QuicSession::closed_streams; using QuicSession::ShouldKeepConnectionAlive; using QuicSpdySession::ProcessPendingStream; - using QuicSpdySession::UsesPendingStreams; + using QuicSpdySession::UsesPendingStreamForFrame; private: StrictMock<TestCryptoStream> crypto_stream_; bool writev_consumes_all_data_; bool supports_webtransport_ = false; - bool should_negotiate_h3_datagram_ = false; + HttpDatagramSupport local_http_datagram_support_ = HttpDatagramSupport::kNone; }; class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { @@ -387,11 +388,9 @@ class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { protected: explicit QuicSpdySessionTestBase(Perspective perspective) - : connection_( - new StrictMock<MockQuicConnection>(&helper_, - &alarm_factory_, - perspective, - SupportedVersions(GetParam()))), + : connection_(new StrictMock<MockQuicConnection>( + &helper_, &alarm_factory_, perspective, + SupportedVersions(GetParam()))), session_(connection_) { session_.config()->SetInitialStreamFlowControlWindowToSend( kInitialStreamFlowControlWindowForTest); @@ -510,8 +509,7 @@ class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { } QuicStreamId StreamCountToId(QuicStreamCount stream_count, - Perspective perspective, - bool bidirectional) { + Perspective perspective, bool bidirectional) { // Calculate and build up stream ID rather than use // GetFirst... because the test that relies on this method // needs to do the stream count where #1 is 0/1/2/3, and not @@ -547,7 +545,7 @@ class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { void ReceiveWebTransportSettings() { SettingsFrame settings; - settings.values[SETTINGS_H3_DATAGRAM] = 1; + settings.values[SETTINGS_H3_DATAGRAM_DRAFT04] = 1; settings.values[SETTINGS_WEBTRANS_DRAFT00] = 1; std::string data = std::string(1, kControlStream) + EncodeSettings(settings); @@ -571,8 +569,14 @@ class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { headers.OnHeaderBlockStart(); headers.OnHeader(":method", "CONNECT"); headers.OnHeader(":protocol", "webtransport"); - headers.OnHeader("datagram-flow-id", absl::StrCat(session_id)); + if (session_.http_datagram_support() == HttpDatagramSupport::kDraft00) { + headers.OnHeader("datagram-flow-id", absl::StrCat(session_id)); + } stream->OnStreamHeaderList(/*fin=*/true, 0, headers); + if (session_.http_datagram_support() != HttpDatagramSupport::kDraft00) { + stream->OnCapsule( + Capsule::RegisterDatagramNoContext(DatagramFormatType::WEBTRANSPORT)); + } WebTransportHttp3* web_transport = session_.GetWebTransportSession(session_id); ASSERT_TRUE(web_transport != nullptr); @@ -592,6 +596,11 @@ class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { session_.OnStreamFrame(frame); } + void TestHttpDatagramSetting(HttpDatagramSupport local_support, + HttpDatagramSupport remote_support, + HttpDatagramSupport expected_support, + bool expected_datagram_supported); + MockQuicConnectionHelper helper_; MockAlarmFactory alarm_factory_; StrictMock<MockQuicConnection>* connection_; @@ -606,16 +615,29 @@ class QuicSpdySessionTestServer : public QuicSpdySessionTestBase { : QuicSpdySessionTestBase(Perspective::IS_SERVER) {} }; -INSTANTIATE_TEST_SUITE_P(Tests, - QuicSpdySessionTestServer, +INSTANTIATE_TEST_SUITE_P(Tests, QuicSpdySessionTestServer, ::testing::ValuesIn(AllSupportedVersions()), ::testing::PrintToStringParamName()); -TEST_P(QuicSpdySessionTestServer, UsesPendingStreams) { +TEST_P(QuicSpdySessionTestServer, UsesPendingStreamsForFrame) { if (!VersionUsesHttp3(transport_version())) { return; } - EXPECT_TRUE(session_.UsesPendingStreams()); + EXPECT_TRUE(session_.UsesPendingStreamForFrame( + STREAM_FRAME, QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_CLIENT))); + EXPECT_TRUE(session_.UsesPendingStreamForFrame( + RST_STREAM_FRAME, QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_CLIENT))); + EXPECT_FALSE(session_.UsesPendingStreamForFrame( + RST_STREAM_FRAME, QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_SERVER))); + EXPECT_FALSE(session_.UsesPendingStreamForFrame( + STOP_SENDING_FRAME, QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_CLIENT))); + EXPECT_FALSE(session_.UsesPendingStreamForFrame( + RST_STREAM_FRAME, QuicUtils::GetFirstBidirectionalStreamId( + transport_version(), Perspective::IS_CLIENT))); } TEST_P(QuicSpdySessionTestServer, PeerAddress) { @@ -1839,16 +1861,29 @@ class QuicSpdySessionTestClient : public QuicSpdySessionTestBase { : QuicSpdySessionTestBase(Perspective::IS_CLIENT) {} }; -INSTANTIATE_TEST_SUITE_P(Tests, - QuicSpdySessionTestClient, +INSTANTIATE_TEST_SUITE_P(Tests, QuicSpdySessionTestClient, ::testing::ValuesIn(AllSupportedVersions()), ::testing::PrintToStringParamName()); -TEST_P(QuicSpdySessionTestClient, UsesPendingStreams) { +TEST_P(QuicSpdySessionTestClient, UsesPendingStreamsForFrame) { if (!VersionUsesHttp3(transport_version())) { return; } - EXPECT_TRUE(session_.UsesPendingStreams()); + EXPECT_TRUE(session_.UsesPendingStreamForFrame( + STREAM_FRAME, QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_SERVER))); + EXPECT_TRUE(session_.UsesPendingStreamForFrame( + RST_STREAM_FRAME, QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_SERVER))); + EXPECT_FALSE(session_.UsesPendingStreamForFrame( + RST_STREAM_FRAME, QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_CLIENT))); + EXPECT_FALSE(session_.UsesPendingStreamForFrame( + STOP_SENDING_FRAME, QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_SERVER))); + EXPECT_FALSE(session_.UsesPendingStreamForFrame( + RST_STREAM_FRAME, QuicUtils::GetFirstBidirectionalStreamId( + transport_version(), Perspective::IS_SERVER))); } // Regression test for crbug.com/977581. @@ -2007,33 +2042,11 @@ TEST_P(QuicSpdySessionTestClient, Http3ServerPush) { std::string frame_type1 = absl::HexStringToBytes("01"); QuicStreamId stream_id1 = GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); - if (!GetQuicReloadableFlag(quic_decline_server_push_stream)) { - session_.OnStreamFrame(QuicStreamFrame(stream_id1, /* fin = */ false, - /* offset = */ 0, frame_type1)); - - EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); - QuicStream* stream = session_.GetOrCreateStream(stream_id1); - EXPECT_EQ(1u, QuicStreamPeer::bytes_consumed(stream)); - EXPECT_EQ(1u, session_.flow_controller()->bytes_consumed()); - - // The same stream type can be encoded differently. - std::string frame_type2 = absl::HexStringToBytes("80000001"); - QuicStreamId stream_id2 = - GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 1); - session_.OnStreamFrame(QuicStreamFrame(stream_id2, /* fin = */ false, - /* offset = */ 0, frame_type2)); - - EXPECT_EQ(2u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); - stream = session_.GetOrCreateStream(stream_id2); - EXPECT_EQ(4u, QuicStreamPeer::bytes_consumed(stream)); - EXPECT_EQ(5u, session_.flow_controller()->bytes_consumed()); - } else { - EXPECT_CALL(*connection_, - CloseConnection(QUIC_HTTP_RECEIVE_SERVER_PUSH, _, _)) - .Times(1); - session_.OnStreamFrame(QuicStreamFrame(stream_id1, /* fin = */ false, - /* offset = */ 0, frame_type1)); - } + EXPECT_CALL(*connection_, + CloseConnection(QUIC_HTTP_RECEIVE_SERVER_PUSH, _, _)) + .Times(1); + session_.OnStreamFrame(QuicStreamFrame(stream_id1, /* fin = */ false, + /* offset = */ 0, frame_type1)); } TEST_P(QuicSpdySessionTestClient, Http3ServerPushOutofOrderFrame) { @@ -2060,18 +2073,10 @@ TEST_P(QuicSpdySessionTestClient, Http3ServerPushOutofOrderFrame) { // Receiving some stream data without stream type does not open the stream. session_.OnStreamFrame(data2); EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); - - if (!GetQuicReloadableFlag(quic_decline_server_push_stream)) { - session_.OnStreamFrame(data1); - EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); - QuicStream* stream = session_.GetOrCreateStream(stream_id); - EXPECT_EQ(3u, stream->highest_received_byte_offset()); - } else { - EXPECT_CALL(*connection_, - CloseConnection(QUIC_HTTP_RECEIVE_SERVER_PUSH, _, _)) - .Times(1); - session_.OnStreamFrame(data1); - } + EXPECT_CALL(*connection_, + CloseConnection(QUIC_HTTP_RECEIVE_SERVER_PUSH, _, _)) + .Times(1); + session_.OnStreamFrame(data1); } TEST_P(QuicSpdySessionTestServer, OnStreamFrameLost) { @@ -2580,10 +2585,10 @@ TEST_P(QuicSpdySessionTestClient, ResetAfterInvalidIncomingStreamType) { return; } CompleteHandshake(); - ASSERT_TRUE(session_.UsesPendingStreams()); const QuicStreamId stream_id = GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); + ASSERT_TRUE(session_.UsesPendingStreamForFrame(STREAM_FRAME, stream_id)); // Payload consists of two bytes. The first byte is an unknown unidirectional // stream type. The second one would be the type of a push stream, but it @@ -2627,10 +2632,10 @@ TEST_P(QuicSpdySessionTestClient, FinAfterInvalidIncomingStreamType) { return; } CompleteHandshake(); - ASSERT_TRUE(session_.UsesPendingStreams()); const QuicStreamId stream_id = GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); + ASSERT_TRUE(session_.UsesPendingStreamForFrame(STREAM_FRAME, stream_id)); // Payload consists of two bytes. The first byte is an unknown unidirectional // stream type. The second one would be the type of a push stream, but it @@ -2666,10 +2671,10 @@ TEST_P(QuicSpdySessionTestClient, ResetInMiddleOfStreamType) { if (!VersionUsesHttp3(transport_version())) { return; } - ASSERT_TRUE(session_.UsesPendingStreams()); const QuicStreamId stream_id = GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); + ASSERT_TRUE(session_.UsesPendingStreamForFrame(STREAM_FRAME, stream_id)); // Payload is the first byte of a two byte varint encoding. std::string payload = absl::HexStringToBytes("40"); @@ -2694,10 +2699,10 @@ TEST_P(QuicSpdySessionTestClient, FinInMiddleOfStreamType) { if (!VersionUsesHttp3(transport_version())) { return; } - ASSERT_TRUE(session_.UsesPendingStreams()); const QuicStreamId stream_id = GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); + ASSERT_TRUE(session_.UsesPendingStreamForFrame(STREAM_FRAME, stream_id)); // Payload is the first byte of a two byte varint encoding with a FIN. std::string payload = absl::HexStringToBytes("40"); @@ -3417,16 +3422,32 @@ TEST_P(QuicSpdySessionTestClient, AlpsTwoSettingsFrame) { EXPECT_EQ("multiple SETTINGS frames", error.value()); } -TEST_P(QuicSpdySessionTestClient, H3DatagramSetting) { +void QuicSpdySessionTestBase::TestHttpDatagramSetting( + HttpDatagramSupport local_support, HttpDatagramSupport remote_support, + HttpDatagramSupport expected_support, bool expected_datagram_supported) { if (!version().UsesHttp3()) { return; } - session_.set_should_negotiate_h3_datagram(true); + session_.set_local_http_datagram_support(local_support); // HTTP/3 datagrams aren't supported before SETTINGS are received. - EXPECT_FALSE(session_.h3_datagram_supported()); + EXPECT_FALSE(session_.SupportsH3Datagram()); + EXPECT_EQ(session_.http_datagram_support(), HttpDatagramSupport::kNone); // Receive SETTINGS. SettingsFrame settings; - settings.values[SETTINGS_H3_DATAGRAM] = 1; + switch (remote_support) { + case HttpDatagramSupport::kNone: + break; + case HttpDatagramSupport::kDraft00: + settings.values[SETTINGS_H3_DATAGRAM_DRAFT00] = 1; + break; + case HttpDatagramSupport::kDraft04: + settings.values[SETTINGS_H3_DATAGRAM_DRAFT04] = 1; + break; + case HttpDatagramSupport::kDraft00And04: + settings.values[SETTINGS_H3_DATAGRAM_DRAFT00] = 1; + settings.values[SETTINGS_H3_DATAGRAM_DRAFT04] = 1; + break; + } std::string data = std::string(1, kControlStream) + EncodeSettings(settings); QuicStreamId stream_id = GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 3); @@ -3436,15 +3457,88 @@ TEST_P(QuicSpdySessionTestClient, H3DatagramSetting) { EXPECT_CALL(debug_visitor, OnPeerControlStreamCreated(stream_id)); EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(settings)); session_.OnStreamFrame(frame); - // HTTP/3 datagrams are now supported. - EXPECT_TRUE(session_.h3_datagram_supported()); + EXPECT_EQ(session_.http_datagram_support(), expected_support); + EXPECT_EQ(session_.SupportsH3Datagram(), expected_datagram_supported); +} + +TEST_P(QuicSpdySessionTestClient, HttpDatagramSettingLocal00Remote00) { + TestHttpDatagramSetting( + /*local_support=*/HttpDatagramSupport::kDraft00, + /*remote_support=*/HttpDatagramSupport::kDraft00, + /*expected_support=*/HttpDatagramSupport::kDraft00, + /*expected_datagram_supported=*/true); +} + +TEST_P(QuicSpdySessionTestClient, HttpDatagramSettingLocal00Remote04) { + TestHttpDatagramSetting( + /*local_support=*/HttpDatagramSupport::kDraft00, + /*remote_support=*/HttpDatagramSupport::kDraft04, + /*expected_support=*/HttpDatagramSupport::kNone, + /*expected_datagram_supported=*/false); +} + +TEST_P(QuicSpdySessionTestClient, HttpDatagramSettingLocal00Remote00And04) { + TestHttpDatagramSetting( + /*local_support=*/HttpDatagramSupport::kDraft00, + /*remote_support=*/HttpDatagramSupport::kDraft00And04, + /*expected_support=*/HttpDatagramSupport::kDraft00, + /*expected_datagram_supported=*/true); +} + +TEST_P(QuicSpdySessionTestClient, HttpDatagramSettingLocal04Remote00) { + TestHttpDatagramSetting( + /*local_support=*/HttpDatagramSupport::kDraft04, + /*remote_support=*/HttpDatagramSupport::kDraft00, + /*expected_support=*/HttpDatagramSupport::kNone, + /*expected_datagram_supported=*/false); +} + +TEST_P(QuicSpdySessionTestClient, HttpDatagramSettingLocal04Remote04) { + TestHttpDatagramSetting( + /*local_support=*/HttpDatagramSupport::kDraft04, + /*remote_support=*/HttpDatagramSupport::kDraft04, + /*expected_support=*/HttpDatagramSupport::kDraft04, + /*expected_datagram_supported=*/true); +} + +TEST_P(QuicSpdySessionTestClient, HttpDatagramSettingLocal04Remote00And04) { + TestHttpDatagramSetting( + /*local_support=*/HttpDatagramSupport::kDraft04, + /*remote_support=*/HttpDatagramSupport::kDraft00And04, + /*expected_support=*/HttpDatagramSupport::kDraft04, + /*expected_datagram_supported=*/true); +} + +TEST_P(QuicSpdySessionTestClient, HttpDatagramSettingLocal00And04Remote00) { + TestHttpDatagramSetting( + /*local_support=*/HttpDatagramSupport::kDraft00And04, + /*remote_support=*/HttpDatagramSupport::kDraft00, + /*expected_support=*/HttpDatagramSupport::kDraft00, + /*expected_datagram_supported=*/true); +} + +TEST_P(QuicSpdySessionTestClient, HttpDatagramSettingLocal00And04Remote04) { + TestHttpDatagramSetting( + /*local_support=*/HttpDatagramSupport::kDraft00And04, + /*remote_support=*/HttpDatagramSupport::kDraft04, + /*expected_support=*/HttpDatagramSupport::kDraft04, + /*expected_datagram_supported=*/true); +} + +TEST_P(QuicSpdySessionTestClient, + HttpDatagramSettingLocal00And04Remote00And04) { + TestHttpDatagramSetting( + /*local_support=*/HttpDatagramSupport::kDraft00And04, + /*remote_support=*/HttpDatagramSupport::kDraft00And04, + /*expected_support=*/HttpDatagramSupport::kDraft04, + /*expected_datagram_supported=*/true); } TEST_P(QuicSpdySessionTestClient, WebTransportSetting) { if (!version().UsesHttp3()) { return; } - session_.set_should_negotiate_h3_datagram(true); + session_.set_local_http_datagram_support(HttpDatagramSupport::kDraft00And04); session_.set_supports_webtransport(true); EXPECT_FALSE(session_.SupportsWebTransport()); @@ -3457,7 +3551,7 @@ TEST_P(QuicSpdySessionTestClient, WebTransportSetting) { CompleteHandshake(); SettingsFrame server_settings; - server_settings.values[SETTINGS_H3_DATAGRAM] = 1; + server_settings.values[SETTINGS_H3_DATAGRAM_DRAFT04] = 1; server_settings.values[SETTINGS_WEBTRANS_DRAFT00] = 1; std::string data = std::string(1, kControlStream) + EncodeSettings(server_settings); @@ -3474,7 +3568,7 @@ TEST_P(QuicSpdySessionTestClient, WebTransportSettingSetToZero) { if (!version().UsesHttp3()) { return; } - session_.set_should_negotiate_h3_datagram(true); + session_.set_local_http_datagram_support(HttpDatagramSupport::kDraft00And04); session_.set_supports_webtransport(true); EXPECT_FALSE(session_.SupportsWebTransport()); @@ -3487,7 +3581,7 @@ TEST_P(QuicSpdySessionTestClient, WebTransportSettingSetToZero) { CompleteHandshake(); SettingsFrame server_settings; - server_settings.values[SETTINGS_H3_DATAGRAM] = 1; + server_settings.values[SETTINGS_H3_DATAGRAM_DRAFT04] = 1; server_settings.values[SETTINGS_WEBTRANS_DRAFT00] = 0; std::string data = std::string(1, kControlStream) + EncodeSettings(server_settings); @@ -3504,7 +3598,7 @@ TEST_P(QuicSpdySessionTestServer, WebTransportSetting) { if (!version().UsesHttp3()) { return; } - session_.set_should_negotiate_h3_datagram(true); + session_.set_local_http_datagram_support(HttpDatagramSupport::kDraft00And04); session_.set_supports_webtransport(true); EXPECT_FALSE(session_.SupportsWebTransport()); @@ -3521,7 +3615,7 @@ TEST_P(QuicSpdySessionTestServer, BufferingIncomingStreams) { if (!version().UsesHttp3()) { return; } - session_.set_should_negotiate_h3_datagram(true); + session_.set_local_http_datagram_support(HttpDatagramSupport::kDraft00And04); session_.set_supports_webtransport(true); CompleteHandshake(); @@ -3554,7 +3648,7 @@ TEST_P(QuicSpdySessionTestServer, BufferingIncomingStreamsLimit) { if (!version().UsesHttp3()) { return; } - session_.set_should_negotiate_h3_datagram(true); + session_.set_local_http_datagram_support(HttpDatagramSupport::kDraft00And04); session_.set_supports_webtransport(true); CompleteHandshake(); @@ -3595,7 +3689,7 @@ TEST_P(QuicSpdySessionTestServer, ResetOutgoingWebTransportStreams) { if (!version().UsesHttp3()) { return; } - session_.set_should_negotiate_h3_datagram(true); + session_.set_local_http_datagram_support(HttpDatagramSupport::kDraft00And04); session_.set_supports_webtransport(true); CompleteHandshake(); 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 bc23474e72a..c25d33a2891 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 @@ -13,18 +13,20 @@ #include "absl/strings/numbers.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "quic/core/http/capsule.h" #include "quic/core/http/http_constants.h" #include "quic/core/http/http_decoder.h" +#include "quic/core/http/http_frames.h" #include "quic/core/http/quic_spdy_session.h" #include "quic/core/http/spdy_utils.h" #include "quic/core/http/web_transport_http3.h" #include "quic/core/qpack/qpack_decoder.h" #include "quic/core/qpack/qpack_encoder.h" -#include "quic/core/quic_error_codes.h" #include "quic/core/quic_types.h" #include "quic/core/quic_utils.h" #include "quic/core/quic_versions.h" #include "quic/core/quic_write_blocked_list.h" +#include "quic/core/web_transport_interface.h" #include "quic/platform/api/quic_bug_tracker.h" #include "quic/platform/api/quic_flag_utils.h" #include "quic/platform/api/quic_flags.h" @@ -129,13 +131,11 @@ class QuicSpdyStream::HttpDecoderVisitor : public HttpDecoder::Visitor { } void OnWebTransportStreamFrameType( - QuicByteCount header_length, - WebTransportSessionId session_id) override { + QuicByteCount header_length, WebTransportSessionId session_id) override { stream_->OnWebTransportStreamFrameType(header_length, session_id); } - bool OnUnknownFrameStart(uint64_t frame_type, - QuicByteCount header_length, + bool OnUnknownFrameStart(uint64_t frame_type, QuicByteCount header_length, QuicByteCount payload_length) override { return stream_->OnUnknownFrameStart(frame_type, header_length, payload_length); @@ -213,9 +213,8 @@ QuicSpdyStream::QuicSpdyStream(QuicStreamId id, QuicSpdySession* spdy_session, } QuicSpdyStream::QuicSpdyStream(PendingStream* pending, - QuicSpdySession* spdy_session, - StreamType type) - : QuicStream(pending, spdy_session, type, /*is_static=*/false), + QuicSpdySession* spdy_session) + : QuicStream(pending, spdy_session, /*is_static=*/false), spdy_session_(spdy_session), on_body_available_called_because_sequencer_is_closed_(false), visitor_(nullptr), @@ -250,8 +249,7 @@ QuicSpdyStream::QuicSpdyStream(PendingStream* pending, QuicSpdyStream::~QuicSpdyStream() {} size_t QuicSpdyStream::WriteHeaders( - SpdyHeaderBlock header_block, - bool fin, + SpdyHeaderBlock header_block, bool fin, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { if (!AssertNotWebTransportDataStream("writing headers")) { return 0; @@ -287,6 +285,16 @@ size_t QuicSpdyStream::WriteHeaders( SetFinSent(); CloseWriteSide(); } + + if (web_transport_ != nullptr && + session()->perspective() == Perspective::IS_CLIENT) { + // This will send a capsule and therefore needs to happen after headers have + // been sent. + RegisterHttp3DatagramContextId( + web_transport_->context_id(), DatagramFormatType::WEBTRANSPORT, + /*format_additional_data=*/absl::string_view(), web_transport_.get()); + } + return bytes_written; } @@ -356,8 +364,7 @@ size_t QuicSpdyStream::WriteTrailers( return bytes_written; } -QuicConsumedData QuicSpdyStream::WritevBody(const struct iovec* iov, - int count, +QuicConsumedData QuicSpdyStream::WritevBody(const struct iovec* iov, int count, bool fin) { QuicMemSliceStorage storage( iov, count, @@ -391,7 +398,7 @@ bool QuicSpdyStream::WriteDataFrameHeader(QuicByteCount data_length, if (can_write) { // Save one copy and allocation if send buffer can accomodate the header. QuicMemSlice header_slice(std::move(header)); - WriteMemSlices(QuicMemSliceSpan(&header_slice), false); + WriteMemSlices(absl::MakeSpan(&header_slice, 1), false); } else { QUICHE_DCHECK(force_write); WriteOrBufferData(header.AsStringView(), false, nullptr); @@ -399,26 +406,8 @@ bool QuicSpdyStream::WriteDataFrameHeader(QuicByteCount data_length, return true; } -QuicConsumedData QuicSpdyStream::WriteBodySlices(QuicMemSliceSpan slices, - bool fin) { - if (!VersionUsesHttp3(transport_version()) || slices.empty()) { - return WriteMemSlices(slices, fin); - } - - QuicConnection::ScopedPacketFlusher flusher(spdy_session_->connection()); - if (!WriteDataFrameHeader(slices.total_length(), /*force_write=*/false)) { - return {0, false}; - } - - QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id() - << " is writing DATA frame payload of length " - << slices.total_length(); - return WriteMemSlices(slices, fin); -} - QuicConsumedData QuicSpdyStream::WriteBodySlices( - absl::Span<QuicMemSlice> slices, - bool fin) { + absl::Span<QuicMemSlice> slices, bool fin) { if (!VersionUsesHttp3(transport_version()) || slices.empty()) { return WriteMemSlices(slices, fin); } @@ -477,9 +466,7 @@ bool QuicSpdyStream::HasBytesToRead() const { return body_manager_.HasBytesToRead(); } -void QuicSpdyStream::MarkTrailersConsumed() { - trailers_consumed_ = true; -} +void QuicSpdyStream::MarkTrailersConsumed() { trailers_consumed_ = true; } uint64_t QuicSpdyStream::total_body_bytes_read() const { if (VersionUsesHttp3(transport_version())) { @@ -501,14 +488,14 @@ void QuicSpdyStream::ConsumeHeaderList() { } if (body_manager_.HasBytesToRead()) { - OnBodyAvailable(); + HandleBodyAvailable(); return; } if (sequencer()->IsClosed() && !on_body_available_called_because_sequencer_is_closed_) { on_body_available_called_because_sequencer_is_closed_ = true; - OnBodyAvailable(); + HandleBodyAvailable(); } } @@ -519,8 +506,7 @@ void QuicSpdyStream::OnStreamHeadersPriority( SetPriority(precedence); } -void QuicSpdyStream::OnStreamHeaderList(bool fin, - size_t frame_len, +void QuicSpdyStream::OnStreamHeaderList(bool fin, size_t frame_len, const QuicHeaderList& header_list) { if (!spdy_session()->user_agent_id().has_value()) { std::string uaid; @@ -583,14 +569,14 @@ void QuicSpdyStream::OnHeadersDecoded(QuicHeaderList headers, } } -void QuicSpdyStream::OnHeaderDecodingError(absl::string_view error_message) { +void QuicSpdyStream::OnHeaderDecodingError(QuicErrorCode error_code, + absl::string_view error_message) { qpack_decoded_headers_accumulator_.reset(); std::string connection_close_error_message = absl::StrCat( "Error decoding ", headers_decompressed_ ? "trailers" : "headers", " on stream ", id(), ": ", error_message); - OnUnrecoverableError(QUIC_QPACK_DECOMPRESSION_FAILED, - connection_close_error_message); + OnUnrecoverableError(error_code, connection_close_error_message); } void QuicSpdyStream::MaybeSendPriorityUpdateFrame() { @@ -613,14 +599,10 @@ void QuicSpdyStream::MaybeSendPriorityUpdateFrame() { spdy_session_->WriteHttp3PriorityUpdate(priority_update); } -void QuicSpdyStream::OnHeadersTooLarge() { - Reset(QUIC_HEADERS_TOO_LARGE); -} +void QuicSpdyStream::OnHeadersTooLarge() { Reset(QUIC_HEADERS_TOO_LARGE); } void QuicSpdyStream::OnInitialHeadersComplete( - bool fin, - size_t /*frame_len*/, - const QuicHeaderList& header_list) { + bool fin, size_t /*frame_len*/, const QuicHeaderList& header_list) { // TODO(b/134706391): remove |fin| argument. headers_decompressed_ = true; header_list_ = header_list; @@ -646,8 +628,7 @@ void QuicSpdyStream::OnInitialHeadersComplete( } void QuicSpdyStream::OnPromiseHeaderList( - QuicStreamId /* promised_id */, - size_t /* frame_len */, + QuicStreamId /* promised_id */, size_t /* frame_len */, const QuicHeaderList& /*header_list */) { // To be overridden in QuicSpdyClientStream. Not supported on // server side. @@ -656,9 +637,7 @@ void QuicSpdyStream::OnPromiseHeaderList( } void QuicSpdyStream::OnTrailingHeadersComplete( - bool fin, - size_t /*frame_len*/, - const QuicHeaderList& header_list) { + bool fin, size_t /*frame_len*/, const QuicHeaderList& header_list) { // TODO(b/134706391): remove |fin| argument. QUICHE_DCHECK(!trailers_decompressed_); if (!VersionUsesHttp3(transport_version()) && fin_received()) { @@ -706,6 +685,12 @@ void QuicSpdyStream::OnPriorityFrame( void QuicSpdyStream::OnStreamReset(const QuicRstStreamFrame& frame) { if (web_transport_data_ != nullptr) { + WebTransportStreamVisitor* webtransport_visitor = + web_transport_data_->adapter.visitor(); + if (webtransport_visitor != nullptr) { + webtransport_visitor->OnResetStreamReceived( + Http3ErrorToWebTransportOrDefault(frame.ietf_error_code)); + } QuicStream::OnStreamReset(frame); return; } @@ -744,11 +729,11 @@ void QuicSpdyStream::OnStreamReset(const QuicRstStreamFrame& frame) { << "Received QUIC_STREAM_NO_ERROR, not discarding response"; set_rst_received(true); MaybeIncreaseHighestReceivedOffset(frame.byte_offset); - set_stream_error(frame.error_code); + set_stream_error(frame.error()); CloseWriteSide(); } -void QuicSpdyStream::Reset(QuicRstStreamErrorCode error) { +void QuicSpdyStream::ResetWithError(QuicResetStreamError error) { if (VersionUsesHttp3(transport_version()) && !fin_received() && spdy_session_->qpack_decoder() && web_transport_data_ == nullptr) { QUIC_CODE_COUNT_N(quic_abort_qpack_on_stream_reset, 2, 2); @@ -759,7 +744,30 @@ void QuicSpdyStream::Reset(QuicRstStreamErrorCode error) { } } - QuicStream::Reset(error); + QuicStream::ResetWithError(error); +} + +bool QuicSpdyStream::OnStopSending(QuicResetStreamError error) { + if (web_transport_data_ != nullptr) { + WebTransportStreamVisitor* visitor = web_transport_data_->adapter.visitor(); + if (visitor != nullptr) { + visitor->OnStopSendingReceived( + Http3ErrorToWebTransportOrDefault(error.ietf_application_code())); + } + } + + return QuicStream::OnStopSending(error); +} + +void QuicSpdyStream::OnWriteSideInDataRecvdState() { + if (web_transport_data_ != nullptr) { + WebTransportStreamVisitor* visitor = web_transport_data_->adapter.visitor(); + if (visitor != nullptr) { + visitor->OnWriteSideInDataRecvdState(); + } + } + + QuicStream::OnWriteSideInDataRecvdState(); } void QuicSpdyStream::OnDataAvailable() { @@ -769,7 +777,7 @@ void QuicSpdyStream::OnDataAvailable() { } if (!VersionUsesHttp3(transport_version())) { - OnBodyAvailable(); + HandleBodyAvailable(); return; } @@ -814,20 +822,20 @@ void QuicSpdyStream::OnDataAvailable() { } } - // Do not call OnBodyAvailable() until headers are consumed. + // Do not call HandleBodyAvailable() until headers are consumed. if (!FinishedReadingHeaders()) { return; } if (body_manager_.HasBytesToRead()) { - OnBodyAvailable(); + HandleBodyAvailable(); return; } if (sequencer()->IsClosed() && !on_body_available_called_because_sequencer_is_closed_) { on_body_available_called_because_sequencer_is_closed_ = true; - OnBodyAvailable(); + HandleBodyAvailable(); } } @@ -849,7 +857,7 @@ void QuicSpdyStream::OnClose() { } if (web_transport_ != nullptr) { - web_transport_->CloseAllAssociatedStreams(); + web_transport_->OnConnectStreamClosing(); } if (web_transport_data_ != nullptr) { WebTransportHttp3* web_transport = @@ -989,8 +997,7 @@ void QuicSpdyStream::OnStreamFrameRetransmitted(QuicStreamOffset offset, } QuicByteCount QuicSpdyStream::GetNumFrameHeadersInInterval( - QuicStreamOffset offset, - QuicByteCount data_length) const { + QuicStreamOffset offset, QuicByteCount data_length) const { QuicByteCount header_acked_length = 0; QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length); newly_acked.Intersection(unacked_frame_headers_offsets_); @@ -1061,8 +1068,7 @@ bool QuicSpdyStream::OnHeadersFrameEnd() { } void QuicSpdyStream::OnWebTransportStreamFrameType( - QuicByteCount header_length, - WebTransportSessionId session_id) { + QuicByteCount header_length, WebTransportSessionId session_id) { QUIC_DVLOG(1) << ENDPOINT << " Received WEBTRANSPORT_STREAM on stream " << id() << " for session " << session_id; sequencer()->MarkConsumed(header_length); @@ -1114,13 +1120,10 @@ bool QuicSpdyStream::OnUnknownFramePayload(absl::string_view payload) { return true; } -bool QuicSpdyStream::OnUnknownFrameEnd() { - return true; -} +bool QuicSpdyStream::OnUnknownFrameEnd() { return true; } size_t QuicSpdyStream::WriteHeadersImpl( - spdy::SpdyHeaderBlock header_block, - bool fin, + spdy::SpdyHeaderBlock header_block, bool fin, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { if (!VersionUsesHttp3(transport_version())) { return spdy_session_->WriteHeadersOnHeadersStream( @@ -1206,6 +1209,13 @@ void QuicSpdyStream::MaybeProcessReceivedWebTransportHeaders() { protocol = header_value; } if (header_name == "datagram-flow-id") { + if (spdy_session_->http_datagram_support() != + HttpDatagramSupport::kDraft00) { + QUIC_DLOG(ERROR) << ENDPOINT + << "Rejecting WebTransport due to unexpected " + "Datagram-Flow-Id header"; + return; + } if (flow_id.has_value() || header_value.empty()) { return; } @@ -1217,23 +1227,33 @@ void QuicSpdyStream::MaybeProcessReceivedWebTransportHeaders() { } } - if (method != "CONNECT" || protocol != "webtransport" || - !flow_id.has_value()) { + if (method != "CONNECT" || protocol != "webtransport") { return; } - RegisterHttp3DatagramFlowId(*flow_id); + if (spdy_session_->http_datagram_support() == HttpDatagramSupport::kDraft00) { + if (!flow_id.has_value()) { + QUIC_DLOG(ERROR) + << ENDPOINT + << "Rejecting WebTransport due to missing Datagram-Flow-Id header"; + return; + } + RegisterHttp3DatagramFlowId(*flow_id); + } web_transport_ = std::make_unique<WebTransportHttp3>(spdy_session_, this, id()); + if (spdy_session_->http_datagram_support() != HttpDatagramSupport::kDraft00) { + return; + } // If we're in draft-ietf-masque-h3-datagram-00 mode, pretend we also received - // a REGISTER_DATAGRAM_NO_CONTEXT capsule with no extensions. + // a REGISTER_DATAGRAM_NO_CONTEXT capsule. // TODO(b/181256914) remove this when we remove support for // draft-ietf-masque-h3-datagram-00 in favor of later drafts. - RegisterHttp3DatagramContextId(/*context_id=*/absl::nullopt, - Http3DatagramContextExtensions(), - web_transport_.get()); + RegisterHttp3DatagramContextId( + /*context_id=*/absl::nullopt, DatagramFormatType::WEBTRANSPORT, + /*format_additional_data=*/absl::string_view(), web_transport_.get()); } void QuicSpdyStream::MaybeProcessSentWebTransportHeaders( @@ -1255,14 +1275,12 @@ void QuicSpdyStream::MaybeProcessSentWebTransportHeaders( return; } - QuicDatagramStreamId stream_id = id(); - headers["datagram-flow-id"] = absl::StrCat(stream_id); + if (spdy_session_->http_datagram_support() == HttpDatagramSupport::kDraft00) { + headers["datagram-flow-id"] = absl::StrCat(id()); + } web_transport_ = std::make_unique<WebTransportHttp3>(spdy_session_, this, id()); - RegisterHttp3DatagramContextId(web_transport_->context_id(), - Http3DatagramContextExtensions(), - web_transport_.get()); } void QuicSpdyStream::OnCanWriteNewData() { @@ -1318,11 +1336,132 @@ void QuicSpdyStream::ConvertToWebTransportDataStream( } QuicSpdyStream::WebTransportDataStream::WebTransportDataStream( - QuicSpdyStream* stream, - WebTransportSessionId session_id) + QuicSpdyStream* stream, WebTransportSessionId session_id) : session_id(session_id), adapter(stream->spdy_session_, stream, stream->sequencer()) {} +void QuicSpdyStream::HandleReceivedDatagram( + absl::optional<QuicDatagramContextId> context_id, + absl::string_view payload) { + Http3DatagramVisitor* visitor; + if (context_id.has_value()) { + auto it = datagram_context_visitors_.find(context_id.value()); + if (it == datagram_context_visitors_.end()) { + QUIC_DLOG(ERROR) << ENDPOINT + << "Received datagram without any visitor for context " + << context_id.value(); + return; + } + visitor = it->second; + } else { + if (datagram_no_context_visitor_ == nullptr) { + QUIC_DLOG(ERROR) + << ENDPOINT << "Received datagram without any visitor for no context"; + return; + } + visitor = datagram_no_context_visitor_; + } + visitor->OnHttp3Datagram(id(), context_id, payload); +} + +bool QuicSpdyStream::OnCapsule(const Capsule& capsule) { + QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id() << " received capsule " + << capsule; + if (!headers_decompressed_) { + QUIC_PEER_BUG(capsule before headers) + << ENDPOINT << "Stream " << id() << " received capsule " << capsule + << " before headers"; + return false; + } + if (web_transport_ != nullptr && web_transport_->close_received()) { + QUIC_PEER_BUG(capsule after close) + << ENDPOINT << "Stream " << id() << " received capsule " << capsule + << " after CLOSE_WEBTRANSPORT_SESSION."; + return false; + } + switch (capsule.capsule_type()) { + case CapsuleType::DATAGRAM: { + HandleReceivedDatagram(capsule.datagram_capsule().context_id, + capsule.datagram_capsule().http_datagram_payload); + } break; + case CapsuleType::REGISTER_DATAGRAM_CONTEXT: + if (datagram_registration_visitor_ == nullptr) { + QUIC_DLOG(ERROR) << ENDPOINT << "Received capsule " << capsule + << " without any registration visitor"; + return false; + } + datagram_registration_visitor_->OnContextReceived( + id(), capsule.register_datagram_context_capsule().context_id, + capsule.register_datagram_context_capsule().format_type, + capsule.register_datagram_context_capsule().format_additional_data); + break; + case CapsuleType::REGISTER_DATAGRAM_NO_CONTEXT: + if (datagram_registration_visitor_ == nullptr) { + QUIC_DLOG(ERROR) << ENDPOINT << "Received capsule " << capsule + << " without any registration visitor"; + return false; + } + datagram_registration_visitor_->OnContextReceived( + id(), /*context_id=*/absl::nullopt, + capsule.register_datagram_no_context_capsule().format_type, + capsule.register_datagram_no_context_capsule() + .format_additional_data); + break; + case CapsuleType::CLOSE_DATAGRAM_CONTEXT: + if (datagram_registration_visitor_ == nullptr) { + QUIC_DLOG(ERROR) << ENDPOINT << "Received capsule " << capsule + << " without any registration visitor"; + return false; + } + datagram_registration_visitor_->OnContextClosed( + id(), capsule.close_datagram_context_capsule().context_id, + capsule.close_datagram_context_capsule().close_code, + capsule.close_datagram_context_capsule().close_details); + break; + case CapsuleType::CLOSE_WEBTRANSPORT_SESSION: + if (web_transport_ == nullptr) { + QUIC_DLOG(ERROR) << ENDPOINT << "Received capsule " << capsule + << " for a non-WebTransport stream."; + return false; + } + web_transport_->OnCloseReceived( + capsule.close_web_transport_session_capsule().error_code, + capsule.close_web_transport_session_capsule().error_message); + break; + } + return true; +} + +void QuicSpdyStream::OnCapsuleParseFailure(const std::string& error_message) { + QUIC_DLOG(ERROR) << ENDPOINT << "Capsule parse failure: " << error_message; + Reset(QUIC_BAD_APPLICATION_PAYLOAD); +} + +void QuicSpdyStream::WriteCapsule(const Capsule& capsule, bool fin) { + QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id() << " sending capsule " + << capsule; + QuicBuffer serialized_capsule = SerializeCapsule( + capsule, + spdy_session_->connection()->helper()->GetStreamSendBufferAllocator()); + QUICHE_DCHECK_GT(serialized_capsule.size(), 0u); + WriteOrBufferBody(serialized_capsule.AsStringView(), /*fin=*/fin); +} + +void QuicSpdyStream::WriteGreaseCapsule() { + // GREASE capsulde IDs have a form of 41 * N + 23. + QuicRandom* random = spdy_session_->connection()->random_generator(); + uint64_t type = random->InsecureRandUint64() >> 4; + type = (type / 41) * 41 + 23; + QUICHE_DCHECK_EQ((type - 23) % 41, 0u); + + constexpr size_t kMaxLength = 64; + size_t length = random->InsecureRandUint64() % kMaxLength; + std::string bytes(length, '\0'); + random->InsecureRandBytes(&bytes[0], bytes.size()); + Capsule capsule = Capsule::Unknown(type, bytes); + WriteCapsule(capsule, /*fin=*/false); +} + MessageStatus QuicSpdyStream::SendHttp3Datagram( absl::optional<QuicDatagramContextId> context_id, absl::string_view payload) { @@ -1338,8 +1477,15 @@ void QuicSpdyStream::RegisterHttp3DatagramRegistrationVisitor( << ENDPOINT << "Null datagram registration visitor for" << id(); return; } + if (datagram_registration_visitor_ != nullptr) { + QUIC_BUG(double datagram registration visitor) + << ENDPOINT << "Double datagram registration visitor for" << id(); + return; + } QUIC_DLOG(INFO) << ENDPOINT << "Registering datagram stream ID " << id(); datagram_registration_visitor_ = visitor; + QUICHE_DCHECK(!capsule_parser_); + capsule_parser_.reset(new CapsuleParser(this)); } void QuicSpdyStream::UnregisterHttp3DatagramRegistrationVisitor() { @@ -1363,24 +1509,27 @@ void QuicSpdyStream::MoveHttp3DatagramRegistration( void QuicSpdyStream::RegisterHttp3DatagramContextId( absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& /*extensions*/, + DatagramFormatType format_type, absl::string_view format_additional_data, Http3DatagramVisitor* visitor) { if (visitor == nullptr) { QUIC_BUG(null datagram visitor) << ENDPOINT << "Null datagram visitor for stream ID " << id() - << " context ID " << (context_id.has_value() ? context_id.value() : 0); + << " context ID " + << (context_id.has_value() ? absl::StrCat(context_id.value()) : "none"); return; } if (datagram_registration_visitor_ == nullptr) { QUIC_BUG(context registration without registration visitor) << ENDPOINT << "Cannot register context ID " - << (context_id.has_value() ? context_id.value() : 0) + << (context_id.has_value() ? absl::StrCat(context_id.value()) : "none") << " without registration visitor for stream ID " << id(); return; } QUIC_DLOG(INFO) << ENDPOINT << "Registering datagram context ID " - << (context_id.has_value() ? context_id.value() : 0) + << (context_id.has_value() ? absl::StrCat(context_id.value()) + : "none") << " with stream ID " << id(); + if (context_id.has_value()) { if (datagram_no_context_visitor_ != nullptr) { QUIC_BUG(h3 datagram context ID mix1) @@ -1392,28 +1541,52 @@ void QuicSpdyStream::RegisterHttp3DatagramContextId( } auto insertion_result = datagram_context_visitors_.insert({context_id.value(), visitor}); - QUIC_BUG_IF(h3 datagram double context registration, - !insertion_result.second) - << ENDPOINT << "Attempted to doubly register HTTP/3 stream ID " << id() - << " context ID " << context_id.value(); - return; - } - // Registration without a context ID. - if (!datagram_context_visitors_.empty()) { - QUIC_BUG(h3 datagram context ID mix2) - << ENDPOINT - << "Attempted to mix registrations with and without context IDs " - "for stream ID " - << id(); - return; - } - if (datagram_no_context_visitor_ != nullptr) { - QUIC_BUG(h3 datagram double no context registration) - << ENDPOINT << "Attempted to doubly register HTTP/3 stream ID " << id() - << " with no context ID"; - return; + if (!insertion_result.second) { + QUIC_BUG(h3 datagram double context registration) + << ENDPOINT << "Attempted to doubly register HTTP/3 stream ID " + << id() << " context ID " << context_id.value(); + return; + } + capsule_parser_->set_datagram_context_id_present(true); + } else { + // Registration without a context ID. + if (!datagram_context_visitors_.empty()) { + QUIC_BUG(h3 datagram context ID mix2) + << ENDPOINT + << "Attempted to mix registrations with and without context IDs " + "for stream ID " + << id(); + return; + } + if (datagram_no_context_visitor_ != nullptr) { + QUIC_BUG(h3 datagram double no context registration) + << ENDPOINT << "Attempted to doubly register HTTP/3 stream ID " + << id() << " with no context ID"; + return; + } + datagram_no_context_visitor_ = visitor; + capsule_parser_->set_datagram_context_id_present(false); + } + if (spdy_session_->http_datagram_support() == HttpDatagramSupport::kDraft04) { + const bool is_client = session()->perspective() == Perspective::IS_CLIENT; + if (context_id.has_value()) { + const bool is_client_context = context_id.value() % 2 == 0; + if (is_client == is_client_context) { + QuicConnection::ScopedPacketFlusher flusher( + spdy_session_->connection()); + WriteGreaseCapsule(); + WriteCapsule(Capsule::RegisterDatagramContext( + context_id.value(), format_type, format_additional_data)); + WriteGreaseCapsule(); + } + } else if (is_client) { + QuicConnection::ScopedPacketFlusher flusher(spdy_session_->connection()); + WriteGreaseCapsule(); + WriteCapsule(Capsule::RegisterDatagramNoContext(format_type, + format_additional_data)); + WriteGreaseCapsule(); + } } - datagram_no_context_visitor_ = visitor; } void QuicSpdyStream::UnregisterHttp3DatagramContextId( @@ -1421,26 +1594,31 @@ void QuicSpdyStream::UnregisterHttp3DatagramContextId( if (datagram_registration_visitor_ == nullptr) { QUIC_BUG(context unregistration without registration visitor) << ENDPOINT << "Cannot unregister context ID " - << (context_id.has_value() ? context_id.value() : 0) + << (context_id.has_value() ? absl::StrCat(context_id.value()) : "none") << " without registration visitor for stream ID " << id(); return; } QUIC_DLOG(INFO) << ENDPOINT << "Unregistering datagram context ID " - << (context_id.has_value() ? context_id.value() : 0) + << (context_id.has_value() ? absl::StrCat(context_id.value()) + : "none") << " with stream ID " << id(); if (context_id.has_value()) { size_t num_erased = datagram_context_visitors_.erase(context_id.value()); QUIC_BUG_IF(h3 datagram unregister unknown context, num_erased != 1) << "Attempted to unregister unknown HTTP/3 context ID " << context_id.value() << " on stream ID " << id(); - return; + } else { + // Unregistration without a context ID. + QUIC_BUG_IF(h3 datagram unknown context unregistration, + datagram_no_context_visitor_ == nullptr) + << "Attempted to unregister unknown no context on HTTP/3 stream ID " + << id(); + datagram_no_context_visitor_ = nullptr; + } + if (spdy_session_->http_datagram_support() == HttpDatagramSupport::kDraft04 && + context_id.has_value()) { + WriteCapsule(Capsule::CloseDatagramContext(context_id.value())); } - // Unregistration without a context ID. - QUIC_BUG_IF(h3 datagram unknown context unregistration, - datagram_no_context_visitor_ == nullptr) - << "Attempted to unregister unknown no context on HTTP/3 stream ID " - << id(); - datagram_no_context_visitor_ = nullptr; } void QuicSpdyStream::MoveHttp3DatagramContextIdRegistration( @@ -1449,12 +1627,13 @@ void QuicSpdyStream::MoveHttp3DatagramContextIdRegistration( if (datagram_registration_visitor_ == nullptr) { QUIC_BUG(context move without registration visitor) << ENDPOINT << "Cannot move context ID " - << (context_id.has_value() ? context_id.value() : 0) + << (context_id.has_value() ? absl::StrCat(context_id.value()) : "none") << " without registration visitor for stream ID " << id(); return; } QUIC_DLOG(INFO) << ENDPOINT << "Moving datagram context ID " - << (context_id.has_value() ? context_id.value() : 0) + << (context_id.has_value() ? absl::StrCat(context_id.value()) + : "none") << " with stream ID " << id(); if (context_id.has_value()) { QUIC_BUG_IF(h3 datagram move unknown context, @@ -1485,7 +1664,6 @@ QuicDatagramContextId QuicSpdyStream::GetNextDatagramContextId() { void QuicSpdyStream::OnDatagramReceived(QuicDataReader* reader) { absl::optional<QuicDatagramContextId> context_id; const bool context_id_present = !datagram_context_visitors_.empty(); - Http3DatagramVisitor* visitor; if (context_id_present) { QuicDatagramContextId parsed_context_id; if (!reader->ReadVarInt62(&parsed_context_id)) { @@ -1495,28 +1673,58 @@ void QuicSpdyStream::OnDatagramReceived(QuicDataReader* reader) { return; } context_id = parsed_context_id; - auto it = datagram_context_visitors_.find(parsed_context_id); - if (it == datagram_context_visitors_.end()) { - // TODO(b/181256914) buffer unknown HTTP/3 datagrams for a short - // period of time in case they were reordered. - QUIC_DLOG(ERROR) << "Received unknown HTTP/3 datagram context ID " - << parsed_context_id << " on stream ID " << id(); - return; - } - visitor = it->second; - } else { - if (datagram_no_context_visitor_ == nullptr) { - // TODO(b/181256914) buffer unknown HTTP/3 datagrams for a short - // period of time in case they were reordered. - QUIC_DLOG(ERROR) - << "Received HTTP/3 datagram without any registrations on stream ID " - << id(); - return; - } - visitor = datagram_no_context_visitor_; } absl::string_view payload = reader->ReadRemainingPayload(); - visitor->OnHttp3Datagram(id(), context_id, payload); + HandleReceivedDatagram(context_id, payload); +} + +QuicByteCount QuicSpdyStream::GetMaxDatagramSize( + absl::optional<QuicDatagramContextId> context_id) const { + QuicByteCount prefix_size = 0; + switch (spdy_session_->http_datagram_support()) { + case HttpDatagramSupport::kDraft00: + if (!datagram_flow_id_.has_value()) { + QUIC_BUG(GetMaxDatagramSize with no flow ID) + << "GetMaxDatagramSize() called when no flow ID available"; + break; + } + prefix_size = QuicDataWriter::GetVarInt62Len(*datagram_flow_id_); + break; + case HttpDatagramSupport::kDraft04: + prefix_size = + QuicDataWriter::GetVarInt62Len(id() / kHttpDatagramStreamIdDivisor); + break; + case HttpDatagramSupport::kNone: + case HttpDatagramSupport::kDraft00And04: + QUIC_BUG(GetMaxDatagramSize called with no datagram support) + << "GetMaxDatagramSize() called when no HTTP/3 datagram support has " + "been negotiated. Support value: " + << spdy_session_->http_datagram_support(); + break; + } + // If the logic above fails, use the largest possible value as the safe one. + if (prefix_size == 0) { + prefix_size = 8; + } + + if (context_id.has_value()) { + QUIC_BUG_IF( + context_id with draft00 in GetMaxDatagramSize, + spdy_session_->http_datagram_support() == HttpDatagramSupport::kDraft00) + << "GetMaxDatagramSize() called with a context ID specified, but " + "draft00 does not support contexts."; + prefix_size += QuicDataWriter::GetVarInt62Len(*context_id); + } + + QuicByteCount max_datagram_size = + session()->GetGuaranteedLargestMessagePayload(); + if (max_datagram_size < prefix_size) { + QUIC_BUG(max_datagram_size smaller than prefix_size) + << "GetGuaranteedLargestMessagePayload() returned a datagram size that " + "is not sufficient to fit stream and/or context ID into it."; + return 0; + } + return max_datagram_size - prefix_size; } void QuicSpdyStream::RegisterHttp3DatagramFlowId(QuicDatagramStreamId flow_id) { @@ -1524,5 +1732,33 @@ void QuicSpdyStream::RegisterHttp3DatagramFlowId(QuicDatagramStreamId flow_id) { spdy_session_->RegisterHttp3DatagramFlowId(datagram_flow_id_.value(), id()); } +void QuicSpdyStream::HandleBodyAvailable() { + if (!capsule_parser_) { + OnBodyAvailable(); + return; + } + while (body_manager_.HasBytesToRead()) { + iovec iov; + int num_iov = GetReadableRegions(&iov, /*iov_len=*/1); + if (num_iov == 0) { + break; + } + if (!capsule_parser_->IngestCapsuleFragment(absl::string_view( + reinterpret_cast<const char*>(iov.iov_base), iov.iov_len))) { + break; + } + MarkConsumed(iov.iov_len); + } + // If we received a FIN, make sure that there isn't a partial capsule buffered + // in the capsule parser. + if (sequencer()->IsClosed()) { + capsule_parser_->ErrorIfThereIsRemainingBufferedData(); + if (web_transport_ != nullptr) { + web_transport_->OnConnectStreamFinReceived(); + } + OnFinRead(); + } +} + #undef ENDPOINT // undef for jumbo builds } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h index 4df1196abeb..7ea2d1ddd7a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h @@ -19,17 +19,19 @@ #include "absl/base/attributes.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" +#include "quic/core/http/capsule.h" #include "quic/core/http/http_decoder.h" #include "quic/core/http/http_encoder.h" #include "quic/core/http/quic_header_list.h" #include "quic/core/http/quic_spdy_stream_body_manager.h" +#include "quic/core/http/web_transport_stream_adapter.h" #include "quic/core/qpack/qpack_decoded_headers_accumulator.h" +#include "quic/core/quic_error_codes.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_stream.h" #include "quic/core/quic_stream_sequencer.h" #include "quic/core/quic_types.h" #include "quic/core/web_transport_interface.h" -#include "quic/core/web_transport_stream_adapter.h" #include "quic/platform/api/quic_export.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_socket_address.h" @@ -46,11 +48,10 @@ class QuicStreamPeer; class QuicSpdySession; class WebTransportHttp3; -class QUIC_EXPORT_PRIVATE Http3DatagramContextExtensions {}; - // A QUIC stream that can send and receive HTTP2 (SPDY) headers. class QUIC_EXPORT_PRIVATE QuicSpdyStream : public QuicStream, + public CapsuleParser::Visitor, public QpackDecodedHeadersAccumulator::Visitor { public: // Visitor receives callbacks from the stream. @@ -71,12 +72,9 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream virtual ~Visitor() {} }; - QuicSpdyStream(QuicStreamId id, - QuicSpdySession* spdy_session, - StreamType type); - QuicSpdyStream(PendingStream* pending, - QuicSpdySession* spdy_session, + QuicSpdyStream(QuicStreamId id, QuicSpdySession* spdy_session, StreamType type); + QuicSpdyStream(PendingStream* pending, QuicSpdySession* spdy_session); QuicSpdyStream(const QuicSpdyStream&) = delete; QuicSpdyStream& operator=(const QuicSpdyStream&) = delete; ~QuicSpdyStream() override; @@ -95,14 +93,12 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream // Called by the session when decompressed headers have been completely // delivered to this stream. If |fin| is true, then this stream // should be closed; no more data will be sent by the peer. - virtual void OnStreamHeaderList(bool fin, - size_t frame_len, + virtual void OnStreamHeaderList(bool fin, size_t frame_len, const QuicHeaderList& header_list); // Called by the session when decompressed push promise headers have // been completely delivered to this stream. - virtual void OnPromiseHeaderList(QuicStreamId promised_id, - size_t frame_len, + virtual void OnPromiseHeaderList(QuicStreamId promised_id, size_t frame_len, const QuicHeaderList& header_list); // Called by the session when a PRIORITY frame has been been received for this @@ -112,8 +108,8 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream // Override the base class to not discard response when receiving // QUIC_STREAM_NO_ERROR. void OnStreamReset(const QuicRstStreamFrame& frame) override; - - void Reset(QuicRstStreamErrorCode error) override; + void ResetWithError(QuicResetStreamError error) override; + bool OnStopSending(QuicResetStreamError error) override; // Called by the sequencer when new data is available. Decodes the data and // calls OnBodyAvailable() to pass to the upper layer. @@ -127,8 +123,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream // number of bytes sent, including data sent on the encoder stream when using // QPACK. virtual size_t WriteHeaders( - spdy::SpdyHeaderBlock header_block, - bool fin, + spdy::SpdyHeaderBlock header_block, bool fin, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); // Sends |data| to the peer, or buffers if it can't be sent immediately. @@ -143,10 +138,8 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); // Override to report newly acked bytes via ack_listener_. - bool OnStreamFrameAcked(QuicStreamOffset offset, - QuicByteCount data_length, - bool fin_acked, - QuicTime::Delta ack_delay_time, + bool OnStreamFrameAcked(QuicStreamOffset offset, QuicByteCount data_length, + bool fin_acked, QuicTime::Delta ack_delay_time, QuicTime receive_timestamp, QuicByteCount* newly_acked_length) override; @@ -161,7 +154,6 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream // Does the same thing as WriteOrBufferBody except this method takes // memslicespan as the data input. Right now it only calls WriteMemSlices. - QuicConsumedData WriteBodySlices(QuicMemSliceSpan slices, bool fin); QuicConsumedData WriteBodySlices(absl::Span<QuicMemSlice> slices, bool fin); // Marks the trailers as consumed. This applies to the case where this object @@ -220,7 +212,8 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream // QpackDecodedHeadersAccumulator::Visitor implementation. void OnHeadersDecoded(QuicHeaderList headers, bool header_list_size_limit_exceeded) override; - void OnHeaderDecodingError(absl::string_view error_message) override; + void OnHeaderDecodingError(QuicErrorCode error_code, + absl::string_view error_message) override; QuicSpdySession* spdy_session() const { return spdy_session_; } @@ -253,6 +246,10 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream // rejected due to buffer being full. |write_size| must be non-zero. bool CanWriteNewBodyData(QuicByteCount write_size) const; + // From CapsuleParser::Visitor. + bool OnCapsule(const Capsule& capsule) override; + void OnCapsuleParseFailure(const std::string& error_message) override; + // Sends an HTTP/3 datagram. The stream and context IDs are not part of // |payload|. MessageStatus SendHttp3Datagram( @@ -282,7 +279,8 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream virtual void OnContextReceived( QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& extensions) = 0; + DatagramFormatType format_type, + absl::string_view format_additional_data) = 0; // Called when a CLOSE_DATAGRAM_CONTEXT capsule is received. Note that this // contains the stream ID even if flow IDs from @@ -290,7 +288,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream virtual void OnContextClosed( QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& extensions) = 0; + ContextCloseCode close_code, absl::string_view close_details) = 0; }; // Registers |visitor| to receive HTTP/3 datagram context registrations. This @@ -316,7 +314,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream // present, or always absent. void RegisterHttp3DatagramContextId( absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& extensions, + DatagramFormatType format_type, absl::string_view format_additional_data, Http3DatagramVisitor* visitor); // Unregisters an HTTP/3 datagram context ID. Must be called on a previously @@ -341,20 +339,25 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream void RegisterHttp3DatagramFlowId(QuicDatagramStreamId flow_id); + QuicByteCount GetMaxDatagramSize( + absl::optional<QuicDatagramContextId> context_id) const; + + // Writes |capsule| onto the DATA stream. + void WriteCapsule(const Capsule& capsule, bool fin = false); + + void WriteGreaseCapsule(); + protected: // Called when the received headers are too large. By default this will // reset the stream. virtual void OnHeadersTooLarge(); - virtual void OnInitialHeadersComplete(bool fin, - size_t frame_len, + virtual void OnInitialHeadersComplete(bool fin, size_t frame_len, const QuicHeaderList& header_list); - virtual void OnTrailingHeadersComplete(bool fin, - size_t frame_len, + virtual void OnTrailingHeadersComplete(bool fin, size_t frame_len, const QuicHeaderList& header_list); virtual size_t WriteHeadersImpl( - spdy::SpdyHeaderBlock header_block, - bool fin, + spdy::SpdyHeaderBlock header_block, bool fin, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); Visitor* visitor() { return visitor_; } @@ -366,6 +369,8 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream ack_listener_ = std::move(ack_listener); } + void OnWriteSideInDataRecvdState() override; + private: friend class test::QuicSpdyStreamPeer; friend class test::QuicStreamPeer; @@ -391,8 +396,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream bool OnHeadersFrameEnd(); void OnWebTransportStreamFrameType(QuicByteCount header_length, WebTransportSessionId session_id); - bool OnUnknownFrameStart(uint64_t frame_type, - QuicByteCount header_length, + bool OnUnknownFrameStart(uint64_t frame_type, QuicByteCount header_length, QuicByteCount payload_length); bool OnUnknownFramePayload(absl::string_view payload); bool OnUnknownFrameEnd(); @@ -410,6 +414,14 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream ABSL_MUST_USE_RESULT bool WriteDataFrameHeader(QuicByteCount data_length, bool force_write); + // Simply calls OnBodyAvailable() unless capsules are in use, in which case + // pass the capsule fragments to the capsule manager. + void HandleBodyAvailable(); + + // Called when a datagram frame or capsule is received. + void HandleReceivedDatagram(absl::optional<QuicDatagramContextId> context_id, + absl::string_view payload); + QuicSpdySession* spdy_session_; bool on_body_available_called_because_sequencer_is_closed_; @@ -450,6 +462,8 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream // the sequencer each time new stream data is processed. QuicSpdyStreamBodyManager body_manager_; + std::unique_ptr<CapsuleParser> capsule_parser_; + // Sequencer offset keeping track of how much data HttpDecoder has processed. // Initial value is zero for fresh streams, or sequencer()->NumBytesConsumed() // at time of construction if a PendingStream is converted to account for the 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 c19dc3c8431..48b654b9876 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 @@ -16,6 +16,7 @@ #include "absl/strings/string_view.h" #include "quic/core/crypto/null_encrypter.h" #include "quic/core/http/http_encoder.h" +#include "quic/core/http/quic_spdy_session.h" #include "quic/core/http/spdy_utils.h" #include "quic/core/http/web_transport_http3.h" #include "quic/core/quic_connection.h" @@ -167,6 +168,15 @@ class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker { MOCK_METHOD(bool, HasPendingRetransmission, (), (const, override)); + bool ExportKeyingMaterial(absl::string_view /*label*/, + absl::string_view /*context*/, + size_t /*result_len*/, + std::string* /*result*/) override { + return false; + } + + SSL* GetSsl() const override { return nullptr; } + private: using QuicCryptoStream::session; @@ -177,8 +187,7 @@ class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker { class TestStream : public QuicSpdyStream { public: - TestStream(QuicStreamId id, - QuicSpdySession* session, + TestStream(QuicStreamId id, QuicSpdySession* session, bool should_process_data) : QuicSpdyStream(id, session, BIDIRECTIONAL), should_process_data_(should_process_data), @@ -203,8 +212,7 @@ class TestStream : public QuicSpdyStream { MOCK_METHOD(void, WriteHeadersMock, (bool fin), ()); - size_t WriteHeadersImpl(spdy::SpdyHeaderBlock header_block, - bool fin, + size_t WriteHeadersImpl(spdy::SpdyHeaderBlock header_block, bool fin, QuicReferenceCountedPointer<QuicAckListenerInterface> /*ack_listener*/) override { saved_headers_ = std::move(header_block); @@ -226,8 +234,7 @@ class TestStream : public QuicSpdyStream { return QuicStream::sequencer(); } - void OnStreamHeaderList(bool fin, - size_t frame_len, + void OnStreamHeaderList(bool fin, size_t frame_len, const QuicHeaderList& header_list) override { headers_payload_length_ = frame_len; QuicSpdyStream::OnStreamHeaderList(fin, frame_len, header_list); @@ -259,16 +266,16 @@ class TestSession : public MockQuicSpdySession { bool ShouldNegotiateWebTransport() override { return enable_webtransport_; } void EnableWebTransport() { enable_webtransport_ = true; } - bool ShouldNegotiateHttp3Datagram() override { - return should_negotiate_h3_datagram_; + HttpDatagramSupport LocalHttpDatagramSupport() override { + return local_http_datagram_support_; } - void set_should_negotiate_h3_datagram(bool value) { - should_negotiate_h3_datagram_ = value; + void set_local_http_datagram_support(HttpDatagramSupport value) { + local_http_datagram_support_ = value; } private: bool enable_webtransport_ = false; - bool should_negotiate_h3_datagram_ = false; + HttpDatagramSupport local_http_datagram_support_ = HttpDatagramSupport::kNone; StrictMock<TestCryptoStream> crypto_stream_; }; @@ -280,8 +287,7 @@ class TestMockUpdateStreamSession : public MockQuicSpdySession { spdy::SpdyStreamPrecedence(QuicStream::kDefaultPriority)) {} void UpdateStreamPriority( - QuicStreamId id, - const spdy::SpdyStreamPrecedence& precedence) override { + QuicStreamId id, const spdy::SpdyStreamPrecedence& precedence) override { EXPECT_EQ(id, expected_stream_->id()); EXPECT_EQ(expected_precedence_, precedence); EXPECT_EQ(expected_precedence_, expected_stream_->precedence()); @@ -491,8 +497,7 @@ class QuicSpdyStreamTest : public QuicTestWithParam<ParsedQuicVersion> { SpdyHeaderBlock headers_; }; -INSTANTIATE_TEST_SUITE_P(Tests, - QuicSpdyStreamTest, +INSTANTIATE_TEST_SUITE_P(Tests, QuicSpdyStreamTest, ::testing::ValuesIn(AllSupportedVersions()), ::testing::PrintToStringParamName()); @@ -515,8 +520,11 @@ TEST_P(QuicSpdyStreamTest, ProcessTooLargeHeaderList) { stream_->OnStreamHeadersPriority( spdy::SpdyStreamPrecedence(kV3HighestPriority)); - EXPECT_CALL(*session_, MaybeSendRstStreamFrame(stream_->id(), - QUIC_HEADERS_TOO_LARGE, 0)); + EXPECT_CALL( + *session_, + MaybeSendRstStreamFrame( + stream_->id(), + QuicResetStreamError::FromInternal(QUIC_HEADERS_TOO_LARGE), 0)); stream_->OnStreamHeaderList(false, 1 << 20, headers); EXPECT_THAT(stream_->stream_error(), IsStreamError(QUIC_HEADERS_TOO_LARGE)); @@ -531,10 +539,14 @@ TEST_P(QuicSpdyStreamTest, ProcessTooLargeHeaderList) { QuicStreamFrame frame(stream_->id(), false, 0, headers); - EXPECT_CALL(*session_, - MaybeSendStopSendingFrame(stream_->id(), QUIC_HEADERS_TOO_LARGE)); - EXPECT_CALL(*session_, MaybeSendRstStreamFrame(stream_->id(), - QUIC_HEADERS_TOO_LARGE, 0)); + EXPECT_CALL(*session_, MaybeSendStopSendingFrame( + stream_->id(), QuicResetStreamError::FromInternal( + QUIC_HEADERS_TOO_LARGE))); + EXPECT_CALL( + *session_, + MaybeSendRstStreamFrame( + stream_->id(), + QuicResetStreamError::FromInternal(QUIC_HEADERS_TOO_LARGE), 0)); auto qpack_decoder_stream = QuicSpdySessionPeer::GetQpackDecoderSendStream(session_.get()); @@ -2467,10 +2479,14 @@ TEST_P(QuicSpdyStreamTest, HeaderDecodingUnblockedAfterStreamClosed) { /* offset = */ 1, _, _, _)); // Reset stream by this endpoint, for example, due to stream cancellation. - EXPECT_CALL(*session_, - MaybeSendStopSendingFrame(stream_->id(), QUIC_STREAM_CANCELLED)); - EXPECT_CALL(*session_, - MaybeSendRstStreamFrame(stream_->id(), QUIC_STREAM_CANCELLED, _)); + EXPECT_CALL(*session_, MaybeSendStopSendingFrame( + stream_->id(), QuicResetStreamError::FromInternal( + QUIC_STREAM_CANCELLED))); + EXPECT_CALL( + *session_, + MaybeSendRstStreamFrame( + stream_->id(), + QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED), _)); stream_->Reset(QUIC_STREAM_CANCELLED); // Deliver dynamic table entry to decoder. @@ -2575,8 +2591,7 @@ class QuicSpdyStreamIncrementalConsumptionTest : public QuicSpdyStreamTest { QuicStreamOffset consumed_bytes_; }; -INSTANTIATE_TEST_SUITE_P(Tests, - QuicSpdyStreamIncrementalConsumptionTest, +INSTANTIATE_TEST_SUITE_P(Tests, QuicSpdyStreamIncrementalConsumptionTest, ::testing::ValuesIn(AllSupportedVersions()), ::testing::PrintToStringParamName()); @@ -2914,10 +2929,14 @@ TEST_P(QuicSpdyStreamTest, StreamCancellationWhenStreamReset) { EXPECT_CALL(*session_, WritevData(qpack_decoder_stream->id(), /* write_length = */ 1, /* offset = */ 1, _, _, _)); - EXPECT_CALL(*session_, - MaybeSendStopSendingFrame(stream_->id(), QUIC_STREAM_CANCELLED)); - EXPECT_CALL(*session_, - MaybeSendRstStreamFrame(stream_->id(), QUIC_STREAM_CANCELLED, _)); + EXPECT_CALL(*session_, MaybeSendStopSendingFrame( + stream_->id(), QuicResetStreamError::FromInternal( + QUIC_STREAM_CANCELLED))); + EXPECT_CALL( + *session_, + MaybeSendRstStreamFrame( + stream_->id(), + QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED), _)); stream_->Reset(QUIC_STREAM_CANCELLED); } @@ -3009,23 +3028,28 @@ TEST_P(QuicSpdyStreamTest, TwoResetStreamFrames) { EXPECT_TRUE(stream_->read_side_closed()); EXPECT_FALSE(stream_->write_side_closed()); } else { - EXPECT_CALL(*session_, MaybeSendRstStreamFrame( - stream_->id(), QUIC_RST_ACKNOWLEDGEMENT, _)); + EXPECT_CALL( + *session_, + MaybeSendRstStreamFrame( + stream_->id(), + QuicResetStreamError::FromInternal(QUIC_RST_ACKNOWLEDGEMENT), _)); EXPECT_QUIC_BUG( stream_->OnStreamReset(rst_frame2), "The stream should've already sent RST in response to STOP_SENDING"); } } -TEST_P(QuicSpdyStreamTest, ProcessOutgoingWebTransportHeaders) { +TEST_P(QuicSpdyStreamTest, ProcessOutgoingWebTransportHeadersDatagramDraft00) { if (!UsesHttp3()) { return; } InitializeWithPerspective(kShouldProcessData, Perspective::IS_CLIENT); - session_->set_should_negotiate_h3_datagram(true); + session_->set_local_http_datagram_support(HttpDatagramSupport::kDraft00And04); session_->EnableWebTransport(); - QuicSpdySessionPeer::EnableWebTransport(*session_); + QuicSpdySessionPeer::EnableWebTransport(session_.get()); + QuicSpdySessionPeer::SetHttpDatagramSupport(session_.get(), + HttpDatagramSupport::kDraft00); EXPECT_CALL(*stream_, WriteHeadersMock(false)); EXPECT_CALL(*session_, WritevData(stream_->id(), _, _, _, _, _)) @@ -3040,15 +3064,68 @@ TEST_P(QuicSpdyStreamTest, ProcessOutgoingWebTransportHeaders) { EXPECT_EQ(stream_->id(), stream_->web_transport()->id()); } -TEST_P(QuicSpdyStreamTest, ProcessIncomingWebTransportHeaders) { +TEST_P(QuicSpdyStreamTest, ProcessOutgoingWebTransportHeadersDatagramDraft04) { + if (!UsesHttp3()) { + return; + } + + InitializeWithPerspective(kShouldProcessData, Perspective::IS_CLIENT); + session_->set_local_http_datagram_support(HttpDatagramSupport::kDraft00And04); + session_->EnableWebTransport(); + QuicSpdySessionPeer::EnableWebTransport(session_.get()); + QuicSpdySessionPeer::SetHttpDatagramSupport(session_.get(), + HttpDatagramSupport::kDraft04); + + EXPECT_CALL(*stream_, WriteHeadersMock(false)); + EXPECT_CALL(*session_, WritevData(stream_->id(), _, _, _, _, _)) + .Times(AnyNumber()); + + spdy::SpdyHeaderBlock headers; + headers[":method"] = "CONNECT"; + headers[":protocol"] = "webtransport"; + stream_->WriteHeaders(std::move(headers), /*fin=*/false, nullptr); + ASSERT_TRUE(stream_->web_transport() != nullptr); + EXPECT_EQ(stream_->id(), stream_->web_transport()->id()); +} + +TEST_P(QuicSpdyStreamTest, ProcessIncomingWebTransportHeadersDatagramDraft04) { + if (!UsesHttp3()) { + return; + } + + Initialize(kShouldProcessData); + session_->set_local_http_datagram_support(HttpDatagramSupport::kDraft00And04); + session_->EnableWebTransport(); + QuicSpdySessionPeer::EnableWebTransport(session_.get()); + QuicSpdySessionPeer::SetHttpDatagramSupport(session_.get(), + HttpDatagramSupport::kDraft04); + + headers_[":method"] = "CONNECT"; + headers_[":protocol"] = "webtransport"; + + stream_->OnStreamHeadersPriority( + spdy::SpdyStreamPrecedence(kV3HighestPriority)); + ProcessHeaders(false, headers_); + stream_->OnCapsule( + Capsule::RegisterDatagramNoContext(DatagramFormatType::WEBTRANSPORT)); + EXPECT_EQ("", stream_->data()); + EXPECT_FALSE(stream_->header_list().empty()); + EXPECT_FALSE(stream_->IsDoneReading()); + ASSERT_TRUE(stream_->web_transport() != nullptr); + EXPECT_EQ(stream_->id(), stream_->web_transport()->id()); +} + +TEST_P(QuicSpdyStreamTest, ProcessIncomingWebTransportHeadersDatagramDraft00) { if (!UsesHttp3()) { return; } Initialize(kShouldProcessData); - session_->set_should_negotiate_h3_datagram(true); + session_->set_local_http_datagram_support(HttpDatagramSupport::kDraft00And04); session_->EnableWebTransport(); - QuicSpdySessionPeer::EnableWebTransport(*session_); + QuicSpdySessionPeer::EnableWebTransport(session_.get()); + QuicSpdySessionPeer::SetHttpDatagramSupport(session_.get(), + HttpDatagramSupport::kDraft00); headers_[":method"] = "CONNECT"; headers_[":protocol"] = "webtransport"; @@ -3073,9 +3150,11 @@ TEST_P(QuicSpdyStreamTest, // draft-ietf-masque-h3-datagram-00 in favor of later drafts. Initialize(kShouldProcessData); - session_->set_should_negotiate_h3_datagram(true); + session_->set_local_http_datagram_support(HttpDatagramSupport::kDraft00And04); session_->EnableWebTransport(); - QuicSpdySessionPeer::EnableWebTransport(*session_); + QuicSpdySessionPeer::EnableWebTransport(session_.get()); + QuicSpdySessionPeer::SetHttpDatagramSupport(session_.get(), + HttpDatagramSupport::kDraft00); headers_[":method"] = "CONNECT"; headers_[":protocol"] = "webtransport"; @@ -3119,19 +3198,20 @@ TEST_P(QuicSpdyStreamTest, GetNextDatagramContextIdServer) { stream_->UnregisterHttp3DatagramRegistrationVisitor(); } -TEST_P(QuicSpdyStreamTest, H3DatagramRegistrationWithoutContext) { +TEST_P(QuicSpdyStreamTest, HttpDatagramRegistrationWithoutContextDraft00) { if (!UsesHttp3()) { return; } - Initialize(kShouldProcessData); - session_->set_should_negotiate_h3_datagram(true); - QuicSpdySessionPeer::SetH3DatagramSupported(session_.get(), true); + InitializeWithPerspective(kShouldProcessData, Perspective::IS_CLIENT); + session_->set_local_http_datagram_support(HttpDatagramSupport::kDraft00And04); + QuicSpdySessionPeer::SetHttpDatagramSupport(session_.get(), + HttpDatagramSupport::kDraft00); session_->RegisterHttp3DatagramFlowId(stream_->id(), stream_->id()); ::testing::NiceMock<MockHttp3DatagramRegistrationVisitor> h3_datagram_registration_visitor; SavingHttp3DatagramVisitor h3_datagram_visitor; absl::optional<QuicDatagramContextId> context_id; - Http3DatagramContextExtensions extensions; + absl::string_view format_additional_data; ASSERT_EQ(QuicDataWriter::GetVarInt62Len(stream_->id()), 1); std::array<char, 256> datagram; datagram[0] = stream_->id(); @@ -3140,8 +3220,9 @@ TEST_P(QuicSpdyStreamTest, H3DatagramRegistrationWithoutContext) { } stream_->RegisterHttp3DatagramRegistrationVisitor( &h3_datagram_registration_visitor); - stream_->RegisterHttp3DatagramContextId(context_id, extensions, - &h3_datagram_visitor); + stream_->RegisterHttp3DatagramContextId( + context_id, DatagramFormatType::UDP_PAYLOAD, format_additional_data, + &h3_datagram_visitor); session_->OnMessageReceived( absl::string_view(datagram.data(), datagram.size())); EXPECT_THAT(h3_datagram_visitor.received_h3_datagrams(), @@ -3168,23 +3249,96 @@ TEST_P(QuicSpdyStreamTest, H3DatagramRegistrationWithoutContext) { session_->UnregisterHttp3DatagramFlowId(stream_->id()); } -TEST_P(QuicSpdyStreamTest, H3DatagramRegistrationWithContext) { +TEST_P(QuicSpdyStreamTest, H3DatagramRegistrationWithoutContextDraft04) { if (!UsesHttp3()) { return; } - Initialize(kShouldProcessData); - session_->set_should_negotiate_h3_datagram(true); - QuicSpdySessionPeer::SetH3DatagramSupported(session_.get(), true); - session_->RegisterHttp3DatagramFlowId(stream_->id(), stream_->id()); + InitializeWithPerspective(kShouldProcessData, Perspective::IS_CLIENT); + session_->set_local_http_datagram_support(HttpDatagramSupport::kDraft04); + QuicSpdySessionPeer::SetHttpDatagramSupport(session_.get(), + HttpDatagramSupport::kDraft04); + ::testing::NiceMock<MockHttp3DatagramRegistrationVisitor> + h3_datagram_registration_visitor; + SavingHttp3DatagramVisitor h3_datagram_visitor; + absl::optional<QuicDatagramContextId> context_id; + absl::string_view format_additional_data; + ASSERT_EQ(QuicDataWriter::GetVarInt62Len(stream_->id()), 1); + std::array<char, 256> datagram; + datagram[0] = stream_->id(); + for (size_t i = 1; i < datagram.size(); i++) { + datagram[i] = i; + } + stream_->RegisterHttp3DatagramRegistrationVisitor( + &h3_datagram_registration_visitor); + + // Expect us to send a REGISTER_DATAGRAM_NO_CONTEXT capsule. + EXPECT_CALL(*session_, WritevData(stream_->id(), _, _, _, _, _)) + .Times(AtLeast(1)); + + stream_->RegisterHttp3DatagramContextId( + context_id, DatagramFormatType::UDP_PAYLOAD, format_additional_data, + &h3_datagram_visitor); + session_->OnMessageReceived( + absl::string_view(datagram.data(), datagram.size())); + EXPECT_THAT(h3_datagram_visitor.received_h3_datagrams(), + ElementsAre(SavingHttp3DatagramVisitor::SavedHttp3Datagram{ + stream_->id(), context_id, + std::string(&datagram[1], datagram.size() - 1)})); + // Test move. + ::testing::NiceMock<MockHttp3DatagramRegistrationVisitor> + h3_datagram_registration_visitor2; + stream_->MoveHttp3DatagramRegistration(&h3_datagram_registration_visitor2); + SavingHttp3DatagramVisitor h3_datagram_visitor2; + stream_->MoveHttp3DatagramContextIdRegistration(context_id, + &h3_datagram_visitor2); + EXPECT_TRUE(h3_datagram_visitor2.received_h3_datagrams().empty()); + session_->OnMessageReceived( + absl::string_view(datagram.data(), datagram.size())); + EXPECT_THAT(h3_datagram_visitor2.received_h3_datagrams(), + ElementsAre(SavingHttp3DatagramVisitor::SavedHttp3Datagram{ + stream_->id(), context_id, + std::string(&datagram[1], datagram.size() - 1)})); + // Cleanup. + stream_->UnregisterHttp3DatagramContextId(context_id); + stream_->UnregisterHttp3DatagramRegistrationVisitor(); +} + +TEST_P(QuicSpdyStreamTest, HttpDatagramRegistrationWithContext) { + if (!UsesHttp3()) { + return; + } + InitializeWithPerspective(kShouldProcessData, Perspective::IS_CLIENT); + session_->set_local_http_datagram_support(HttpDatagramSupport::kDraft00And04); + QuicSpdySessionPeer::SetHttpDatagramSupport(session_.get(), + HttpDatagramSupport::kDraft04); ::testing::NiceMock<MockHttp3DatagramRegistrationVisitor> h3_datagram_registration_visitor; SavingHttp3DatagramVisitor h3_datagram_visitor; absl::optional<QuicDatagramContextId> context_id = 42; - Http3DatagramContextExtensions extensions; + absl::string_view format_additional_data; + ASSERT_EQ(QuicDataWriter::GetVarInt62Len(stream_->id()), 1); + std::array<char, 256> datagram; + datagram[0] = stream_->id(); + datagram[1] = context_id.value(); + for (size_t i = 2; i < datagram.size(); i++) { + datagram[i] = i; + } stream_->RegisterHttp3DatagramRegistrationVisitor( &h3_datagram_registration_visitor); - stream_->RegisterHttp3DatagramContextId(context_id, extensions, - &h3_datagram_visitor); + + // Expect us to send a REGISTER_DATAGRAM_CONTEXT capsule. + EXPECT_CALL(*session_, WritevData(stream_->id(), _, _, _, _, _)) + .Times(AtLeast(1)); + + stream_->RegisterHttp3DatagramContextId( + context_id, DatagramFormatType::UDP_PAYLOAD, format_additional_data, + &h3_datagram_visitor); + session_->OnMessageReceived( + absl::string_view(datagram.data(), datagram.size())); + EXPECT_THAT(h3_datagram_visitor.received_h3_datagrams(), + ElementsAre(SavingHttp3DatagramVisitor::SavedHttp3Datagram{ + stream_->id(), context_id, + std::string(&datagram[2], datagram.size() - 2)})); // Test move. ::testing::NiceMock<MockHttp3DatagramRegistrationVisitor> h3_datagram_registration_visitor2; @@ -3192,7 +3346,18 @@ TEST_P(QuicSpdyStreamTest, H3DatagramRegistrationWithContext) { SavingHttp3DatagramVisitor h3_datagram_visitor2; stream_->MoveHttp3DatagramContextIdRegistration(context_id, &h3_datagram_visitor2); + EXPECT_TRUE(h3_datagram_visitor2.received_h3_datagrams().empty()); + session_->OnMessageReceived( + absl::string_view(datagram.data(), datagram.size())); + EXPECT_THAT(h3_datagram_visitor2.received_h3_datagrams(), + ElementsAre(SavingHttp3DatagramVisitor::SavedHttp3Datagram{ + stream_->id(), context_id, + std::string(&datagram[2], datagram.size() - 2)})); // Cleanup. + + // Expect us to send a CLOSE_DATAGRAM_CONTEXT capsule. + EXPECT_CALL(*session_, WritevData(stream_->id(), _, _, _, _, _)) + .Times(AtLeast(1)); stream_->UnregisterHttp3DatagramContextId(context_id); stream_->UnregisterHttp3DatagramRegistrationVisitor(); session_->UnregisterHttp3DatagramFlowId(stream_->id()); @@ -3203,8 +3368,9 @@ TEST_P(QuicSpdyStreamTest, SendHttp3Datagram) { return; } Initialize(kShouldProcessData); - session_->set_should_negotiate_h3_datagram(true); - QuicSpdySessionPeer::SetH3DatagramSupported(session_.get(), true); + session_->set_local_http_datagram_support(HttpDatagramSupport::kDraft00And04); + QuicSpdySessionPeer::SetHttpDatagramSupport(session_.get(), + HttpDatagramSupport::kDraft04); absl::optional<QuicDatagramContextId> context_id; std::string h3_datagram_payload = {1, 2, 3, 4, 5, 6}; EXPECT_CALL(*connection_, SendMessage(1, _, false)) @@ -3213,6 +3379,22 @@ TEST_P(QuicSpdyStreamTest, SendHttp3Datagram) { MESSAGE_STATUS_SUCCESS); } +TEST_P(QuicSpdyStreamTest, GetMaxDatagramSize) { + if (!UsesHttp3()) { + return; + } + Initialize(kShouldProcessData); + session_->set_local_http_datagram_support(HttpDatagramSupport::kDraft00And04); + QuicSpdySessionPeer::SetHttpDatagramSupport(session_.get(), + HttpDatagramSupport::kDraft04); + + QuicByteCount size = stream_->GetMaxDatagramSize(absl::nullopt); + QuicByteCount size_with_context = + stream_->GetMaxDatagramSize(/*context_id=*/1); + EXPECT_GT(size, 512u); + EXPECT_EQ(size - 1, size_with_context); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.cc b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.cc index 820950b31bd..a54d210b327 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.cc @@ -4,13 +4,17 @@ #include "quic/core/http/web_transport_http3.h" +#include <limits> #include <memory> #include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "quic/core/http/capsule.h" #include "quic/core/http/quic_spdy_session.h" #include "quic/core/http/quic_spdy_stream.h" #include "quic/core/quic_data_reader.h" #include "quic/core/quic_data_writer.h" +#include "quic/core/quic_error_codes.h" #include "quic/core/quic_stream.h" #include "quic/core/quic_types.h" #include "quic/core/quic_utils.h" @@ -25,7 +29,9 @@ namespace quic { namespace { class QUIC_NO_EXPORT NoopWebTransportVisitor : public WebTransportVisitor { - void OnSessionReady() override {} + void OnSessionReady(const spdy::SpdyHeaderBlock&) override {} + void OnSessionClosed(WebTransportSessionError /*error_code*/, + const std::string& /*error_message*/) override {} void OnIncomingBidirectionalStreamAvailable() override {} void OnIncomingUnidirectionalStreamAvailable() override {} void OnDatagramReceived(absl::string_view /*datagram*/) override {} @@ -68,7 +74,7 @@ void WebTransportHttp3::AssociateStream(QuicStreamId stream_id) { } } -void WebTransportHttp3::CloseAllAssociatedStreams() { +void WebTransportHttp3::OnConnectStreamClosing() { // Copy the stream list before iterating over it, as calls to ResetStream() // can potentially mutate the |session_| list. std::vector<QuicStreamId> streams(streams_.begin(), streams_.end()); @@ -81,22 +87,108 @@ void WebTransportHttp3::CloseAllAssociatedStreams() { connect_stream_->UnregisterHttp3DatagramContextId(context_id_); } connect_stream_->UnregisterHttp3DatagramRegistrationVisitor(); + + MaybeNotifyClose(); +} + +void WebTransportHttp3::CloseSession(WebTransportSessionError error_code, + absl::string_view error_message) { + if (close_sent_) { + QUIC_BUG(WebTransportHttp3 close sent twice) + << "Calling WebTransportHttp3::CloseSession() more than once is not " + "allowed."; + return; + } + close_sent_ = true; + + // There can be a race between us trying to send our close and peer sending + // one. If we received a close, however, we cannot send ours since we already + // closed the stream in response. + if (close_received_) { + QUIC_DLOG(INFO) << "Not sending CLOSE_WEBTRANSPORT_SESSION as we've " + "already sent one from peer."; + return; + } + + error_code_ = error_code; + error_message_ = std::string(error_message); + QuicConnection::ScopedPacketFlusher flusher( + connect_stream_->spdy_session()->connection()); + connect_stream_->WriteCapsule( + Capsule::CloseWebTransportSession(error_code, error_message), + /*fin=*/true); +} + +void WebTransportHttp3::OnCloseReceived(WebTransportSessionError error_code, + absl::string_view error_message) { + if (close_received_) { + QUIC_BUG(WebTransportHttp3 notified of close received twice) + << "WebTransportHttp3::OnCloseReceived() may be only called once."; + } + close_received_ = true; + + // If the peer has sent a close after we sent our own, keep the local error. + if (close_sent_) { + QUIC_DLOG(INFO) << "Ignoring received CLOSE_WEBTRANSPORT_SESSION as we've " + "already sent our own."; + return; + } + + error_code_ = error_code; + error_message_ = std::string(error_message); + connect_stream_->WriteOrBufferBody("", /*fin=*/true); + MaybeNotifyClose(); +} + +void WebTransportHttp3::OnConnectStreamFinReceived() { + // If we already received a CLOSE_WEBTRANSPORT_SESSION capsule, we don't need + // to do anything about receiving a FIN, since we already sent one in + // response. + if (close_received_) { + return; + } + close_received_ = true; + if (close_sent_) { + QUIC_DLOG(INFO) << "Ignoring received FIN as we've already sent our close."; + return; + } + + connect_stream_->WriteOrBufferBody("", /*fin=*/true); + MaybeNotifyClose(); +} + +void WebTransportHttp3::CloseSessionWithFinOnlyForTests() { + QUICHE_DCHECK(!close_sent_); + close_sent_ = true; + if (close_received_) { + return; + } + + connect_stream_->WriteOrBufferBody("", /*fin=*/true); } void WebTransportHttp3::HeadersReceived(const spdy::SpdyHeaderBlock& headers) { if (session_->perspective() == Perspective::IS_CLIENT) { - auto it = headers.find(":status"); - if (it == headers.end() || it->second != "200") { + int status_code; + if (!QuicSpdyStream::ParseHeaderStatusCode(headers, &status_code)) { QUIC_DVLOG(1) << ENDPOINT << "Received WebTransport headers from server without " - "status 200, rejecting."; + "a valid status code, rejecting."; + return; + } + bool valid_status = status_code >= 200 && status_code <= 299; + if (!valid_status) { + QUIC_DVLOG(1) << ENDPOINT + << "Received WebTransport headers from server with " + "status code " + << status_code << ", rejecting."; return; } } QUIC_DVLOG(1) << ENDPOINT << "WebTransport session " << id_ << " ready."; ready_ = true; - visitor_->OnSessionReady(); + visitor_->OnSessionReady(headers); session_->ProcessBufferedWebTransportStreamsForSession(this); } @@ -163,6 +255,10 @@ MessageStatus WebTransportHttp3::SendOrQueueDatagram(QuicMemSlice datagram) { context_id_, absl::string_view(datagram.data(), datagram.length())); } +QuicByteCount WebTransportHttp3::GetMaxDatagramSize() const { + return connect_stream_->GetMaxDatagramSize(context_id_); +} + void WebTransportHttp3::SetDatagramMaxTimeInQueue( QuicTime::Delta max_time_in_queue) { connect_stream_->SetMaxDatagramTimeInQueue(max_time_in_queue); @@ -178,13 +274,27 @@ void WebTransportHttp3::OnHttp3Datagram( void WebTransportHttp3::OnContextReceived( QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& /*extensions*/) { + DatagramFormatType format_type, absl::string_view format_additional_data) { if (stream_id != connect_stream_->id()) { QUIC_BUG(WT3 bad datagram context registration) << ENDPOINT << "Registered stream ID " << stream_id << ", expected " << connect_stream_->id(); return; } + if (format_type != DatagramFormatType::WEBTRANSPORT) { + QUIC_DLOG(INFO) << ENDPOINT << "Ignoring unexpected datagram format type " + << DatagramFormatTypeToString(format_type); + return; + } + if (!format_additional_data.empty()) { + QUIC_DLOG(ERROR) + << ENDPOINT + << "Received non-empty format additional data for context ID " + << (context_id_.has_value() ? context_id_.value() : 0) + << " on stream ID " << connect_stream_->id(); + session_->ResetStream(connect_stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD); + return; + } if (!context_is_known_) { context_is_known_ = true; context_id_ = context_id; @@ -206,15 +316,14 @@ void WebTransportHttp3::OnContextReceived( return; } context_currently_registered_ = true; - Http3DatagramContextExtensions reply_extensions; - connect_stream_->RegisterHttp3DatagramContextId(context_id_, - reply_extensions, this); + connect_stream_->RegisterHttp3DatagramContextId( + context_id_, format_type, format_additional_data, this); } } void WebTransportHttp3::OnContextClosed( QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& /*extensions*/) { + ContextCloseCode close_code, absl::string_view close_details) { if (stream_id != connect_stream_->id()) { QUIC_BUG(WT3 bad datagram context registration) << ENDPOINT << "Closed context on stream ID " << stream_id @@ -229,23 +338,31 @@ void WebTransportHttp3::OnContextClosed( << " on stream ID " << connect_stream_->id(); return; } - QUIC_DLOG(INFO) << ENDPOINT << "Received datagram context close on stream ID " - << connect_stream_->id() << ", resetting stream"; - session_->ResetStream(connect_stream_->id(), QUIC_STREAM_CANCELLED); + QUIC_DLOG(INFO) << ENDPOINT + << "Received datagram context close with close code " + << close_code << " close details \"" << close_details + << "\" on stream ID " << connect_stream_->id() + << ", resetting stream"; + session_->ResetStream(connect_stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD); +} + +void WebTransportHttp3::MaybeNotifyClose() { + if (close_notified_) { + return; + } + close_notified_ = true; + visitor_->OnSessionClosed(error_code_, error_message_); } WebTransportHttp3UnidirectionalStream::WebTransportHttp3UnidirectionalStream( - PendingStream* pending, - QuicSpdySession* session) - : QuicStream(pending, session, READ_UNIDIRECTIONAL, /*is_static=*/false), + PendingStream* pending, QuicSpdySession* session) + : QuicStream(pending, session, /*is_static=*/false), session_(session), adapter_(session, this, sequencer()), needs_to_send_preamble_(false) {} WebTransportHttp3UnidirectionalStream::WebTransportHttp3UnidirectionalStream( - QuicStreamId id, - QuicSpdySession* session, - WebTransportSessionId session_id) + QuicStreamId id, QuicSpdySession* session, WebTransportSessionId session_id) : QuicStream(id, session, /*is_static=*/false, WRITE_UNIDIRECTIONAL), session_(session), adapter_(session, this, sequencer()), @@ -334,4 +451,65 @@ void WebTransportHttp3UnidirectionalStream::OnClose() { session->OnStreamClosed(id()); } +void WebTransportHttp3UnidirectionalStream::OnStreamReset( + const QuicRstStreamFrame& frame) { + if (adapter_.visitor() != nullptr) { + adapter_.visitor()->OnResetStreamReceived( + Http3ErrorToWebTransportOrDefault(frame.ietf_error_code)); + } + QuicStream::OnStreamReset(frame); +} +bool WebTransportHttp3UnidirectionalStream::OnStopSending( + QuicResetStreamError error) { + if (adapter_.visitor() != nullptr) { + adapter_.visitor()->OnStopSendingReceived( + Http3ErrorToWebTransportOrDefault(error.ietf_application_code())); + } + return QuicStream::OnStopSending(error); +} +void WebTransportHttp3UnidirectionalStream::OnWriteSideInDataRecvdState() { + if (adapter_.visitor() != nullptr) { + adapter_.visitor()->OnWriteSideInDataRecvdState(); + } + + QuicStream::OnWriteSideInDataRecvdState(); +} + +namespace { +constexpr uint64_t kWebTransportMappedErrorCodeFirst = 0x52e4a40fa8db; +constexpr uint64_t kWebTransportMappedErrorCodeLast = 0x52e4a40fa9e2; +constexpr WebTransportStreamError kDefaultWebTransportError = 0; +} // namespace + +absl::optional<WebTransportStreamError> Http3ErrorToWebTransport( + uint64_t http3_error_code) { + // Ensure the code is within the valid range. + if (http3_error_code < kWebTransportMappedErrorCodeFirst || + http3_error_code > kWebTransportMappedErrorCodeLast) { + return absl::nullopt; + } + // Exclude GREASE codepoints. + if ((http3_error_code - 0x21) % 0x1f == 0) { + return absl::nullopt; + } + + uint64_t shifted = http3_error_code - kWebTransportMappedErrorCodeFirst; + uint64_t result = shifted - shifted / 0x1f; + QUICHE_DCHECK_LE(result, std::numeric_limits<uint8_t>::max()); + return result; +} + +WebTransportStreamError Http3ErrorToWebTransportOrDefault( + uint64_t http3_error_code) { + absl::optional<WebTransportStreamError> result = + Http3ErrorToWebTransport(http3_error_code); + return result.has_value() ? *result : kDefaultWebTransportError; +} + +uint64_t WebTransportErrorToHttp3( + WebTransportStreamError webtransport_error_code) { + return kWebTransportMappedErrorCodeFirst + webtransport_error_code + + webtransport_error_code / 0x1e; +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.h b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.h index df5c4f61bf7..7a72931874c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.h @@ -7,13 +7,15 @@ #include <memory> +#include "absl/base/attributes.h" #include "absl/container/flat_hash_set.h" #include "absl/types/optional.h" #include "quic/core/http/quic_spdy_session.h" +#include "quic/core/http/web_transport_stream_adapter.h" +#include "quic/core/quic_error_codes.h" #include "quic/core/quic_stream.h" #include "quic/core/quic_types.h" #include "quic/core/web_transport_interface.h" -#include "quic/core/web_transport_stream_adapter.h" #include "spdy/core/spdy_header_block.h" namespace quic { @@ -47,10 +49,21 @@ class QUIC_EXPORT_PRIVATE WebTransportHttp3 void AssociateStream(QuicStreamId stream_id); void OnStreamClosed(QuicStreamId stream_id) { streams_.erase(stream_id); } - void CloseAllAssociatedStreams(); + void OnConnectStreamClosing(); size_t NumberOfAssociatedStreams() { return streams_.size(); } + void CloseSession(WebTransportSessionError error_code, + absl::string_view error_message) override; + void OnCloseReceived(WebTransportSessionError error_code, + absl::string_view error_message); + void OnConnectStreamFinReceived(); + + // It is legal for WebTransport to be closed without a + // CLOSE_WEBTRANSPORT_SESSION capsule. We always send a capsule, but we still + // need to ensure we handle this case correctly. + void CloseSessionWithFinOnlyForTests(); + // Return the earliest incoming stream that has been received by the session // but has not been accepted. Returns nullptr if there are no incoming // streams. @@ -63,6 +76,7 @@ class QUIC_EXPORT_PRIVATE WebTransportHttp3 WebTransportStream* OpenOutgoingUnidirectionalStream() override; MessageStatus SendOrQueueDatagram(QuicMemSlice datagram) override; + QuicByteCount GetMaxDatagramSize() const override; void SetDatagramMaxTimeInQueue(QuicTime::Delta max_time_in_queue) override; // From QuicSpdyStream::Http3DatagramVisitor. @@ -71,14 +85,22 @@ class QUIC_EXPORT_PRIVATE WebTransportHttp3 absl::string_view payload) override; // From QuicSpdyStream::Http3DatagramRegistrationVisitor. - void OnContextReceived( - QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& extensions) override; - void OnContextClosed( - QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& extensions) override; + void OnContextReceived(QuicStreamId stream_id, + absl::optional<QuicDatagramContextId> context_id, + DatagramFormatType format_type, + absl::string_view format_additional_data) override; + void OnContextClosed(QuicStreamId stream_id, + absl::optional<QuicDatagramContextId> context_id, + ContextCloseCode close_code, + absl::string_view close_details) override; + + bool close_received() const { return close_received_; } private: + // Notifies the visitor that the connection has been closed. Ensures that the + // visitor is only ever called once. + void MaybeNotifyClose(); + QuicSpdySession* const session_; // Unowned. QuicSpdyStream* const connect_stream_; // Unowned. const WebTransportSessionId id_; @@ -95,6 +117,15 @@ class QUIC_EXPORT_PRIVATE WebTransportHttp3 absl::flat_hash_set<QuicStreamId> streams_; quiche::QuicheCircularDeque<QuicStreamId> incoming_bidirectional_streams_; quiche::QuicheCircularDeque<QuicStreamId> incoming_unidirectional_streams_; + + bool close_sent_ = false; + bool close_received_ = false; + bool close_notified_ = false; + + // Those are set to default values, which are used if the session is not + // closed cleanly using an appropriate capsule. + WebTransportSessionError error_code_ = 0; + std::string error_message_ = ""; }; class QUIC_EXPORT_PRIVATE WebTransportHttp3UnidirectionalStream @@ -115,6 +146,9 @@ class QUIC_EXPORT_PRIVATE WebTransportHttp3UnidirectionalStream void OnDataAvailable() override; void OnCanWriteNewData() override; void OnClose() override; + void OnStreamReset(const QuicRstStreamFrame& frame) override; + bool OnStopSending(QuicResetStreamError error) override; + void OnWriteSideInDataRecvdState() override; WebTransportStream* interface() { return &adapter_; } void SetUnblocked() { sequencer()->SetUnblocked(); } @@ -130,6 +164,20 @@ class QUIC_EXPORT_PRIVATE WebTransportHttp3UnidirectionalStream void MaybeCloseIncompleteStream(); }; +// Remaps HTTP/3 error code into a WebTransport error code. Returns nullopt if +// the provided code is outside of valid range. +QUIC_EXPORT_PRIVATE absl::optional<WebTransportStreamError> +Http3ErrorToWebTransport(uint64_t http3_error_code); + +// Same as above, but returns default error value (zero) when none could be +// mapped. +QUIC_EXPORT_PRIVATE WebTransportStreamError +Http3ErrorToWebTransportOrDefault(uint64_t http3_error_code); + +// Remaps WebTransport error code into an HTTP/3 error code. +QUIC_EXPORT_PRIVATE uint64_t +WebTransportErrorToHttp3(WebTransportStreamError webtransport_error_code); + } // namespace quic #endif // QUICHE_QUIC_CORE_HTTP_WEB_TRANSPORT_HTTP3_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3_test.cc new file mode 100644 index 00000000000..0cf5b946200 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3_test.cc @@ -0,0 +1,52 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "quic/core/http/web_transport_http3.h" + +#include <cstdint> +#include <limits> + +#include "absl/types/optional.h" +#include "quic/platform/api/quic_test.h" + +namespace quic { +namespace { + +using ::testing::Optional; + +TEST(WebTransportHttp3Test, ErrorCodesToHttp3) { + EXPECT_EQ(0x52e4a40fa8dbu, WebTransportErrorToHttp3(0x00)); + EXPECT_EQ(0x52e4a40fa9e2u, WebTransportErrorToHttp3(0xff)); + + EXPECT_EQ(0x52e4a40fa8f7u, WebTransportErrorToHttp3(0x1c)); + EXPECT_EQ(0x52e4a40fa8f8u, WebTransportErrorToHttp3(0x1d)); + // 0x52e4a40fa8f9 is a GREASE codepoint + EXPECT_EQ(0x52e4a40fa8fau, WebTransportErrorToHttp3(0x1e)); +} + +TEST(WebTransportHttp3Test, ErrorCodesToWebTransport) { + EXPECT_THAT(Http3ErrorToWebTransport(0x52e4a40fa8db), Optional(0x00)); + EXPECT_THAT(Http3ErrorToWebTransport(0x52e4a40fa9e2), Optional(0xff)); + + EXPECT_THAT(Http3ErrorToWebTransport(0x52e4a40fa8f7), Optional(0x1cu)); + EXPECT_THAT(Http3ErrorToWebTransport(0x52e4a40fa8f8), Optional(0x1du)); + EXPECT_THAT(Http3ErrorToWebTransport(0x52e4a40fa8f9), absl::nullopt); + EXPECT_THAT(Http3ErrorToWebTransport(0x52e4a40fa8fa), Optional(0x1eu)); + + EXPECT_EQ(Http3ErrorToWebTransport(0), absl::nullopt); + EXPECT_EQ(Http3ErrorToWebTransport(std::numeric_limits<uint64_t>::max()), + absl::nullopt); +} + +TEST(WebTransportHttp3Test, ErrorCodeRoundTrip) { + for (int error = 0; error < 256; error++) { + uint64_t http_error = WebTransportErrorToHttp3(error); + absl::optional<WebTransportStreamError> mapped_back = + quic::Http3ErrorToWebTransport(http_error); + EXPECT_THAT(mapped_back, Optional(error)); + } +} + +} // namespace +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/web_transport_stream_adapter.cc b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_stream_adapter.cc index 2470aacf4cc..bea13770756 100644 --- a/chromium/net/third_party/quiche/src/quic/core/web_transport_stream_adapter.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_stream_adapter.cc @@ -2,7 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "quic/core/web_transport_stream_adapter.h" +#include "quic/core/http/web_transport_stream_adapter.h" + +#include "quic/core/http/web_transport_http3.h" +#include "quic/core/quic_error_codes.h" namespace quic { @@ -45,7 +48,7 @@ bool WebTransportStreamAdapter::Write(absl::string_view data) { QuicMemSlice memslice(QuicBuffer::Copy( session_->connection()->helper()->GetStreamSendBufferAllocator(), data)); QuicConsumedData consumed = - stream_->WriteMemSlices(QuicMemSliceSpan(&memslice), /*fin=*/false); + stream_->WriteMemSlices(absl::MakeSpan(&memslice, 1), /*fin=*/false); if (consumed.bytes_consumed == data.size()) { return true; @@ -75,7 +78,7 @@ bool WebTransportStreamAdapter::SendFin() { QuicMemSlice empty; QuicConsumedData consumed = - stream_->WriteMemSlices(QuicMemSliceSpan(&empty), /*fin=*/true); + stream_->WriteMemSlices(absl::MakeSpan(&empty, 1), /*fin=*/true); QUICHE_DCHECK_EQ(consumed.bytes_consumed, 0u); return consumed.fin_consumed; } @@ -110,4 +113,15 @@ void WebTransportStreamAdapter::OnCanWriteNewData() { } } +void WebTransportStreamAdapter::ResetWithUserCode( + WebTransportStreamError error) { + stream_->ResetWriteSide(QuicResetStreamError( + QUIC_STREAM_CANCELLED, WebTransportErrorToHttp3(error))); +} + +void WebTransportStreamAdapter::SendStopSending(WebTransportStreamError error) { + stream_->SendStopSending(QuicResetStreamError( + QUIC_STREAM_CANCELLED, WebTransportErrorToHttp3(error))); +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/web_transport_stream_adapter.h b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_stream_adapter.h index 2e4704ba3e4..a761f6fac03 100644 --- a/chromium/net/third_party/quiche/src/quic/core/web_transport_stream_adapter.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_stream_adapter.h @@ -8,6 +8,7 @@ #include "quic/core/quic_session.h" #include "quic/core/quic_stream.h" #include "quic/core/quic_stream_sequencer.h" +#include "quic/core/quic_types.h" #include "quic/core/web_transport_interface.h" namespace quic { @@ -34,12 +35,11 @@ class QUIC_EXPORT_PRIVATE WebTransportStreamAdapter } QuicStreamId GetStreamId() const override { return stream_->id(); } - void ResetWithUserCode(QuicRstStreamErrorCode error) override { - stream_->Reset(error); - } + void ResetWithUserCode(WebTransportStreamError error) override; void ResetDueToInternalError() override { stream_->Reset(QUIC_STREAM_INTERNAL_ERROR); } + void SendStopSending(WebTransportStreamError error) override; void MaybeResetDueToStreamObjectGone() override { if (stream_->write_side_closed() && stream_->read_side_closed()) { return; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.cc index 9a5af7e42c3..4e50e728ee4 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.cc @@ -67,13 +67,13 @@ void QpackDecodedHeadersAccumulator::OnDecodingCompleted() { } void QpackDecodedHeadersAccumulator::OnDecodingErrorDetected( - absl::string_view error_message) { + QuicErrorCode error_code, absl::string_view error_message) { QUICHE_DCHECK(!error_detected_); QUICHE_DCHECK(!headers_decoded_); error_detected_ = true; // Might destroy |this|. - visitor_->OnHeaderDecodingError(error_message); + visitor_->OnHeaderDecodingError(error_code, error_message); } void QpackDecodedHeadersAccumulator::Decode(absl::string_view data) { diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h index b22da2a9d92..57d1839d904 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h @@ -11,6 +11,7 @@ #include "absl/strings/string_view.h" #include "quic/core/http/quic_header_list.h" #include "quic/core/qpack/qpack_progressive_decoder.h" +#include "quic/core/quic_error_codes.h" #include "quic/core/quic_types.h" #include "quic/platform/api/quic_export.h" @@ -45,7 +46,8 @@ class QUIC_EXPORT_PRIVATE QpackDecodedHeadersAccumulator bool header_list_size_limit_exceeded) = 0; // Called when an error has occurred. - virtual void OnHeaderDecodingError(absl::string_view error_message) = 0; + virtual void OnHeaderDecodingError(QuicErrorCode error_code, + absl::string_view error_message) = 0; }; QpackDecodedHeadersAccumulator(QuicStreamId id, @@ -59,7 +61,8 @@ class QUIC_EXPORT_PRIVATE QpackDecodedHeadersAccumulator void OnHeaderDecoded(absl::string_view name, absl::string_view value) override; void OnDecodingCompleted() override; - void OnDecodingErrorDetected(absl::string_view error_message) override; + void OnDecodingErrorDetected(QuicErrorCode error_code, + absl::string_view error_message) override; // Decode payload data. // Must not be called if an error has been detected. diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc index 217f866dce1..0aa08a05d5c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc @@ -46,9 +46,8 @@ class MockVisitor : public QpackDecodedHeadersAccumulator::Visitor { OnHeadersDecoded, (QuicHeaderList headers, bool header_list_size_limit_exceeded), (override)); - MOCK_METHOD(void, - OnHeaderDecodingError, - (absl::string_view error_message), + MOCK_METHOD(void, OnHeaderDecodingError, + (QuicErrorCode error_code, absl::string_view error_message), (override)); }; @@ -78,7 +77,8 @@ class QpackDecodedHeadersAccumulatorTest : public QuicTest { // HEADERS frame payload must have a complete Header Block Prefix. TEST_F(QpackDecodedHeadersAccumulatorTest, EmptyPayload) { EXPECT_CALL(visitor_, - OnHeaderDecodingError(Eq("Incomplete header data prefix."))); + OnHeaderDecodingError(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Incomplete header data prefix."))); accumulator_.EndHeaderBlock(); } @@ -87,7 +87,8 @@ TEST_F(QpackDecodedHeadersAccumulatorTest, TruncatedHeaderBlockPrefix) { accumulator_.Decode(absl::HexStringToBytes("00")); EXPECT_CALL(visitor_, - OnHeaderDecodingError(Eq("Incomplete header data prefix."))); + OnHeaderDecodingError(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Incomplete header data prefix."))); accumulator_.EndHeaderBlock(); } @@ -110,14 +111,16 @@ TEST_F(QpackDecodedHeadersAccumulatorTest, EmptyHeaderList) { TEST_F(QpackDecodedHeadersAccumulatorTest, TruncatedPayload) { accumulator_.Decode(absl::HexStringToBytes("00002366")); - EXPECT_CALL(visitor_, OnHeaderDecodingError(Eq("Incomplete header block."))); + EXPECT_CALL(visitor_, OnHeaderDecodingError(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Incomplete header block."))); accumulator_.EndHeaderBlock(); } // This payload is invalid because it refers to a non-existing static entry. TEST_F(QpackDecodedHeadersAccumulatorTest, InvalidPayload) { EXPECT_CALL(visitor_, - OnHeaderDecodingError(Eq("Static table entry not found."))); + OnHeaderDecodingError(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Static table entry not found."))); accumulator_.Decode(absl::HexStringToBytes("0000ff23ff24")); } @@ -241,7 +244,8 @@ TEST_F(QpackDecodedHeadersAccumulatorTest, qpack_decoder_.OnSetDynamicTableCapacity(kMaxDynamicTableCapacity); // Adding dynamic table entry unblocks decoding. Error is detected. - EXPECT_CALL(visitor_, OnHeaderDecodingError(Eq("Invalid relative index."))); + EXPECT_CALL(visitor_, OnHeaderDecodingError(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Invalid relative index."))); qpack_decoder_.OnInsertWithoutNameReference("foo", "bar"); } diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc index 772b1d25480..8f4b092ad51 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc @@ -49,8 +49,9 @@ class QpackDecoderTest : public QuicTestWithParam<FragmentMode> { void SetUp() override { // Destroy QpackProgressiveDecoder on error to test that it does not crash. // See https://crbug.com/1025209. - ON_CALL(handler_, OnDecodingErrorDetected(_)) - .WillByDefault(Invoke([this](absl::string_view /* error_message */) { + ON_CALL(handler_, OnDecodingErrorDetected(_, _)) + .WillByDefault(Invoke([this](QuicErrorCode /* error_code */, + absl::string_view /* error_message */) { progressive_decoder_.reset(); })); } @@ -114,7 +115,8 @@ INSTANTIATE_TEST_SUITE_P(All, TEST_P(QpackDecoderTest, NoPrefix) { EXPECT_CALL(handler_, - OnDecodingErrorDetected(Eq("Incomplete header data prefix."))); + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Incomplete header data prefix."))); // Header Data Prefix is at least two bytes long. DecodeHeaderBlock(absl::HexStringToBytes("00")); @@ -126,7 +128,8 @@ TEST_P(QpackDecoderTest, InvalidPrefix) { StartDecoding(); EXPECT_CALL(handler_, - OnDecodingErrorDetected(Eq("Encoded integer too large."))); + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Encoded integer too large."))); // Encoded Required Insert Count in Header Data Prefix is too large. DecodeData(absl::HexStringToBytes("ffffffffffffffffffffffffffff")); @@ -188,7 +191,8 @@ TEST_P(QpackDecoderTest, MultipleLiteralEntries) { // Name Length value is too large for varint decoder to decode. TEST_P(QpackDecoderTest, NameLenTooLargeForVarintDecoder) { EXPECT_CALL(handler_, - OnDecodingErrorDetected(Eq("Encoded integer too large."))); + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Encoded integer too large."))); DecodeHeaderBlock(absl::HexStringToBytes("000027ffffffffffffffffffff")); } @@ -196,7 +200,8 @@ TEST_P(QpackDecoderTest, NameLenTooLargeForVarintDecoder) { // Name Length value can be decoded by varint decoder but exceeds 1 MB limit. TEST_P(QpackDecoderTest, NameLenExceedsLimit) { EXPECT_CALL(handler_, - OnDecodingErrorDetected(Eq("String literal too long."))); + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("String literal too long."))); DecodeHeaderBlock(absl::HexStringToBytes("000027ffff7f")); } @@ -204,7 +209,8 @@ TEST_P(QpackDecoderTest, NameLenExceedsLimit) { // Value Length value is too large for varint decoder to decode. TEST_P(QpackDecoderTest, ValueLenTooLargeForVarintDecoder) { EXPECT_CALL(handler_, - OnDecodingErrorDetected(Eq("Encoded integer too large."))); + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Encoded integer too large."))); DecodeHeaderBlock( absl::HexStringToBytes("000023666f6f7fffffffffffffffffffff")); @@ -213,14 +219,29 @@ TEST_P(QpackDecoderTest, ValueLenTooLargeForVarintDecoder) { // Value Length value can be decoded by varint decoder but exceeds 1 MB limit. TEST_P(QpackDecoderTest, ValueLenExceedsLimit) { EXPECT_CALL(handler_, - OnDecodingErrorDetected(Eq("String literal too long."))); + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("String literal too long."))); DecodeHeaderBlock(absl::HexStringToBytes("000023666f6f7fffff7f")); } +TEST_P(QpackDecoderTest, LineFeedInValue) { + if (GetQuicReloadableFlag(quic_reject_invalid_chars_in_field_value)) { + EXPECT_CALL(handler_, + OnDecodingErrorDetected(QUIC_INVALID_CHARACTER_IN_FIELD_VALUE, + "Invalid character in field value.")); + } else { + EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ba\nr"))); + EXPECT_CALL(handler_, OnDecodingCompleted()); + } + + DecodeHeaderBlock(absl::HexStringToBytes("000023666f6f0462610a72")); +} + TEST_P(QpackDecoderTest, IncompleteHeaderBlock) { EXPECT_CALL(handler_, - OnDecodingErrorDetected(Eq("Incomplete header block."))); + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Incomplete header block."))); DecodeHeaderBlock(absl::HexStringToBytes("00002366")); } @@ -251,8 +272,9 @@ TEST_P(QpackDecoderTest, AlternatingHuffmanNonHuffman) { } TEST_P(QpackDecoderTest, HuffmanNameDoesNotHaveEOSPrefix) { - EXPECT_CALL(handler_, OnDecodingErrorDetected(absl::string_view( - "Error in Huffman-encoded string."))); + EXPECT_CALL(handler_, + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Error in Huffman-encoded string."))); // 'y' ends in 0b0 on the most significant bit of the last byte. // The remaining 7 bits must be a prefix of EOS, which is all 1s. @@ -261,8 +283,9 @@ TEST_P(QpackDecoderTest, HuffmanNameDoesNotHaveEOSPrefix) { } TEST_P(QpackDecoderTest, HuffmanValueDoesNotHaveEOSPrefix) { - EXPECT_CALL(handler_, OnDecodingErrorDetected(absl::string_view( - "Error in Huffman-encoded string."))); + EXPECT_CALL(handler_, + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Error in Huffman-encoded string."))); // 'e' ends in 0b101, taking up the 3 most significant bits of the last byte. // The remaining 5 bits must be a prefix of EOS, which is all 1s. @@ -271,8 +294,9 @@ TEST_P(QpackDecoderTest, HuffmanValueDoesNotHaveEOSPrefix) { } TEST_P(QpackDecoderTest, HuffmanNameEOSPrefixTooLong) { - EXPECT_CALL(handler_, OnDecodingErrorDetected(absl::string_view( - "Error in Huffman-encoded string."))); + EXPECT_CALL(handler_, + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Error in Huffman-encoded string."))); // The trailing EOS prefix must be at most 7 bits long. Appending one octet // with value 0xff is invalid, even though 0b111111111111111 (15 bits) is a @@ -282,8 +306,9 @@ TEST_P(QpackDecoderTest, HuffmanNameEOSPrefixTooLong) { } TEST_P(QpackDecoderTest, HuffmanValueEOSPrefixTooLong) { - EXPECT_CALL(handler_, OnDecodingErrorDetected(absl::string_view( - "Error in Huffman-encoded string."))); + EXPECT_CALL(handler_, + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Error in Huffman-encoded string."))); // The trailing EOS prefix must be at most 7 bits long. Appending one octet // with value 0xff is invalid, even though 0b1111111111111 (13 bits) is a @@ -321,7 +346,8 @@ TEST_P(QpackDecoderTest, TooHighStaticTableIndex) { // Addressing entry 99 should trigger an error. EXPECT_CALL(handler_, - OnDecodingErrorDetected(Eq("Static table entry not found."))); + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Static table entry not found."))); DecodeHeaderBlock(absl::HexStringToBytes("0000ff23ff24")); } @@ -430,6 +456,7 @@ TEST_P(QpackDecoderTest, DecreasingDynamicTableCapacityEvictsEntries) { DecodeEncoderStreamData(absl::HexStringToBytes("3f01")); EXPECT_CALL(handler_, OnDecodingErrorDetected( + QUIC_QPACK_DECOMPRESSION_FAILED, Eq("Dynamic table entry already evicted."))); DecodeHeaderBlock(absl::HexStringToBytes( @@ -497,7 +524,8 @@ TEST_P(QpackDecoderTest, EncoderStreamErrorTooLargeInteger) { } TEST_P(QpackDecoderTest, InvalidDynamicEntryWhenBaseIsZero) { - EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index."))); + EXPECT_CALL(handler_, OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Invalid relative index."))); // Set dynamic table capacity to 1024. DecodeEncoderStreamData(absl::HexStringToBytes("3fe107")); @@ -512,7 +540,8 @@ TEST_P(QpackDecoderTest, InvalidDynamicEntryWhenBaseIsZero) { } TEST_P(QpackDecoderTest, InvalidNegativeBase) { - EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Error calculating Base."))); + EXPECT_CALL(handler_, OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Error calculating Base."))); // Required Insert Count 1, Delta Base 1 with sign bit set, Base would // be 1 - 1 - 1 = -1, but it is not allowed to be negative. @@ -525,7 +554,8 @@ TEST_P(QpackDecoderTest, InvalidDynamicEntryByRelativeIndex) { // Add literal entry with name "foo" and value "bar". DecodeEncoderStreamData(absl::HexStringToBytes("6294e703626172")); - EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index."))); + EXPECT_CALL(handler_, OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Invalid relative index."))); DecodeHeaderBlock(absl::HexStringToBytes( "0200" // Required Insert Count 1 and Delta Base 0. @@ -533,7 +563,8 @@ TEST_P(QpackDecoderTest, InvalidDynamicEntryByRelativeIndex) { "81")); // Indexed Header Field instruction addressing relative index 1. // This is absolute index -1, which is invalid. - EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index."))); + EXPECT_CALL(handler_, OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Invalid relative index."))); DecodeHeaderBlock(absl::HexStringToBytes( "0200" // Required Insert Count 1 and Delta Base 0. @@ -554,6 +585,7 @@ TEST_P(QpackDecoderTest, EvictedDynamicTableEntry) { DecodeEncoderStreamData(absl::HexStringToBytes("00000000")); EXPECT_CALL(handler_, OnDecodingErrorDetected( + QUIC_QPACK_DECOMPRESSION_FAILED, Eq("Dynamic table entry already evicted."))); DecodeHeaderBlock(absl::HexStringToBytes( @@ -563,6 +595,7 @@ TEST_P(QpackDecoderTest, EvictedDynamicTableEntry) { // This is absolute index 1. Such entry does not exist. EXPECT_CALL(handler_, OnDecodingErrorDetected( + QUIC_QPACK_DECOMPRESSION_FAILED, Eq("Dynamic table entry already evicted."))); DecodeHeaderBlock(absl::HexStringToBytes( @@ -573,6 +606,7 @@ TEST_P(QpackDecoderTest, EvictedDynamicTableEntry) { // entry does not exist. EXPECT_CALL(handler_, OnDecodingErrorDetected( + QUIC_QPACK_DECOMPRESSION_FAILED, Eq("Dynamic table entry already evicted."))); DecodeHeaderBlock(absl::HexStringToBytes( @@ -583,6 +617,7 @@ TEST_P(QpackDecoderTest, EvictedDynamicTableEntry) { // does not exist. EXPECT_CALL(handler_, OnDecodingErrorDetected( + QUIC_QPACK_DECOMPRESSION_FAILED, Eq("Dynamic table entry already evicted."))); DecodeHeaderBlock(absl::HexStringToBytes( @@ -614,6 +649,7 @@ TEST_P(QpackDecoderTest, InvalidEncodedRequiredInsertCount) { // Required Insert Count is decoded modulo 2 * MaxEntries, that is, modulo 64. // A value of 1 cannot be encoded as 65 even though it has the same remainder. EXPECT_CALL(handler_, OnDecodingErrorDetected( + QUIC_QPACK_DECOMPRESSION_FAILED, Eq("Error decoding Required Insert Count."))); DecodeHeaderBlock(absl::HexStringToBytes("4100")); } @@ -622,6 +658,7 @@ TEST_P(QpackDecoderTest, InvalidEncodedRequiredInsertCount) { // after a Header Block Prefix with an invalid Encoded Required Insert Count. TEST_P(QpackDecoderTest, DataAfterInvalidEncodedRequiredInsertCount) { EXPECT_CALL(handler_, OnDecodingErrorDetected( + QUIC_QPACK_DECOMPRESSION_FAILED, Eq("Error decoding Required Insert Count."))); // Header Block Prefix followed by some extra data. DecodeHeaderBlock(absl::HexStringToBytes("410000")); @@ -666,7 +703,8 @@ TEST_P(QpackDecoderTest, NonZeroRequiredInsertCountButNoDynamicEntries) { EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("GET"))); EXPECT_CALL(handler_, - OnDecodingErrorDetected(Eq("Required Insert Count too large."))); + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Required Insert Count too large."))); DecodeHeaderBlock(absl::HexStringToBytes( "0200" // Required Insert Count is 1. @@ -682,6 +720,7 @@ TEST_P(QpackDecoderTest, AddressEntryNotAllowedByRequiredInsertCount) { EXPECT_CALL( handler_, OnDecodingErrorDetected( + QUIC_QPACK_DECOMPRESSION_FAILED, Eq("Absolute Index must be smaller than Required Insert Count."))); DecodeHeaderBlock(absl::HexStringToBytes( @@ -694,6 +733,7 @@ TEST_P(QpackDecoderTest, AddressEntryNotAllowedByRequiredInsertCount) { EXPECT_CALL( handler_, OnDecodingErrorDetected( + QUIC_QPACK_DECOMPRESSION_FAILED, Eq("Absolute Index must be smaller than Required Insert Count."))); DecodeHeaderBlock(absl::HexStringToBytes( @@ -707,6 +747,7 @@ TEST_P(QpackDecoderTest, AddressEntryNotAllowedByRequiredInsertCount) { EXPECT_CALL( handler_, OnDecodingErrorDetected( + QUIC_QPACK_DECOMPRESSION_FAILED, Eq("Absolute Index must be smaller than Required Insert Count."))); DecodeHeaderBlock(absl::HexStringToBytes( @@ -720,6 +761,7 @@ TEST_P(QpackDecoderTest, AddressEntryNotAllowedByRequiredInsertCount) { EXPECT_CALL( handler_, OnDecodingErrorDetected( + QUIC_QPACK_DECOMPRESSION_FAILED, Eq("Absolute Index must be smaller than Required Insert Count."))); DecodeHeaderBlock(absl::HexStringToBytes( @@ -743,7 +785,8 @@ TEST_P(QpackDecoderTest, PromisedRequiredInsertCountLargerThanActual) { EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); EXPECT_CALL(handler_, - OnDecodingErrorDetected(Eq("Required Insert Count too large."))); + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Required Insert Count too large."))); DecodeHeaderBlock(absl::HexStringToBytes( "0300" // Required Insert Count 2 and Delta Base 0. @@ -755,7 +798,8 @@ TEST_P(QpackDecoderTest, PromisedRequiredInsertCountLargerThanActual) { EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq(""))); EXPECT_CALL(handler_, - OnDecodingErrorDetected(Eq("Required Insert Count too large."))); + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Required Insert Count too large."))); DecodeHeaderBlock(absl::HexStringToBytes( "0300" // Required Insert Count 2 and Delta Base 0. @@ -767,7 +811,8 @@ TEST_P(QpackDecoderTest, PromisedRequiredInsertCountLargerThanActual) { EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); EXPECT_CALL(handler_, - OnDecodingErrorDetected(Eq("Required Insert Count too large."))); + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Required Insert Count too large."))); DecodeHeaderBlock(absl::HexStringToBytes( "0481" // Required Insert Count 3 and Delta Base 1 with sign bit set. @@ -779,7 +824,8 @@ TEST_P(QpackDecoderTest, PromisedRequiredInsertCountLargerThanActual) { EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq(""))); EXPECT_CALL(handler_, - OnDecodingErrorDetected(Eq("Required Insert Count too large."))); + OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Required Insert Count too large."))); DecodeHeaderBlock(absl::HexStringToBytes( "0481" // Required Insert Count 3 and Delta Base 1 with sign bit set. @@ -864,7 +910,8 @@ TEST_P(QpackDecoderTest, // Count of the header block. |handler_| methods are called immediately for // the already consumed part of the header block. EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); - EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index."))); + EXPECT_CALL(handler_, OnDecodingErrorDetected(QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Invalid relative index."))); DecodeEncoderStreamData(absl::HexStringToBytes("6294e703626172")); } @@ -905,8 +952,10 @@ TEST_P(QpackDecoderTest, TooManyBlockedStreams) { auto progressive_decoder1 = CreateProgressiveDecoder(/* stream_id = */ 1); progressive_decoder1->Decode(data); - EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq( - "Limit on number of blocked streams exceeded."))); + EXPECT_CALL(handler_, + OnDecodingErrorDetected( + QUIC_QPACK_DECOMPRESSION_FAILED, + Eq("Limit on number of blocked streams exceeded."))); auto progressive_decoder2 = CreateProgressiveDecoder(/* stream_id = */ 2); progressive_decoder2->Decode(data); diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc index 3e7d0ad5127..96a3da5103c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc @@ -12,20 +12,27 @@ #include "quic/core/qpack/qpack_index_conversions.h" #include "quic/core/qpack/qpack_instructions.h" #include "quic/core/qpack/qpack_required_insert_count.h" +#include "quic/platform/api/quic_flag_utils.h" +#include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" namespace quic { +namespace { + +// The value argument passed to OnHeaderDecoded() is from an entry in the static +// table. +constexpr bool kValueFromStaticTable = true; + +} // anonymous namespace + QpackProgressiveDecoder::QpackProgressiveDecoder( - QuicStreamId stream_id, - BlockedStreamLimitEnforcer* enforcer, - DecodingCompletedVisitor* visitor, - QpackDecoderHeaderTable* header_table, + QuicStreamId stream_id, BlockedStreamLimitEnforcer* enforcer, + DecodingCompletedVisitor* visitor, QpackDecoderHeaderTable* header_table, HeadersHandlerInterface* handler) : stream_id_(stream_id), - prefix_decoder_( - std::make_unique<QpackInstructionDecoder>(QpackPrefixLanguage(), - this)), + prefix_decoder_(std::make_unique<QpackInstructionDecoder>( + QpackPrefixLanguage(), this)), instruction_decoder_(QpackRequestStreamLanguage(), this), enforcer_(enforcer), visitor_(visitor), @@ -38,7 +45,13 @@ QpackProgressiveDecoder::QpackProgressiveDecoder( blocked_(false), decoding_(true), error_detected_(false), - cancelled_(false) {} + cancelled_(false), + reject_invalid_chars_in_field_value_( + GetQuicReloadableFlag(quic_reject_invalid_chars_in_field_value)) { + if (reject_invalid_chars_in_field_value_) { + QUIC_RELOADABLE_FLAG_COUNT(quic_reject_invalid_chars_in_field_value); + } +} QpackProgressiveDecoder::~QpackProgressiveDecoder() { if (blocked_ && !cancelled_) { @@ -89,14 +102,6 @@ void QpackProgressiveDecoder::EndHeaderBlock() { } } -void QpackProgressiveDecoder::OnError(absl::string_view error_message) { - QUICHE_DCHECK(!error_detected_); - - error_detected_ = true; - // Might destroy |this|. - handler_->OnDecodingErrorDetected(error_message); -} - bool QpackProgressiveDecoder::OnInstructionDecoded( const QpackInstruction* instruction) { if (instruction == QpackPrefixInstruction()) { @@ -126,10 +131,9 @@ bool QpackProgressiveDecoder::OnInstructionDecoded( void QpackProgressiveDecoder::OnInstructionDecodingError( QpackInstructionDecoder::ErrorCode /* error_code */, absl::string_view error_message) { - // Ignore |error_code|, because header block decoding errors trigger a - // RESET_STREAM frame which cannot carry an error code more granular than - // QPACK_DECOMPRESSION_FAILED. - OnError(error_message); + // Ignore |error_code| and always use QUIC_QPACK_DECOMPRESSION_FAILED to avoid + // having to define a new QuicErrorCode for every instruction decoder error. + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, error_message); } void QpackProgressiveDecoder::OnInsertCountReachedThreshold() { @@ -164,12 +168,13 @@ bool QpackProgressiveDecoder::DoIndexedHeaderFieldInstruction() { uint64_t absolute_index; if (!QpackRequestStreamRelativeIndexToAbsoluteIndex( instruction_decoder_.varint(), base_, &absolute_index)) { - OnError("Invalid relative index."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, "Invalid relative index."); return false; } if (absolute_index >= required_insert_count_) { - OnError("Absolute Index must be smaller than Required Insert Count."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, + "Absolute Index must be smaller than Required Insert Count."); return false; } @@ -180,36 +185,37 @@ bool QpackProgressiveDecoder::DoIndexedHeaderFieldInstruction() { auto entry = header_table_->LookupEntry(/* is_static = */ false, absolute_index); if (!entry) { - OnError("Dynamic table entry already evicted."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, + "Dynamic table entry already evicted."); return false; } header_table_->set_dynamic_table_entry_referenced(); - handler_->OnHeaderDecoded(entry->name(), entry->value()); - return true; + return OnHeaderDecoded(!kValueFromStaticTable, entry->name(), + entry->value()); } auto entry = header_table_->LookupEntry(/* is_static = */ true, instruction_decoder_.varint()); if (!entry) { - OnError("Static table entry not found."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, "Static table entry not found."); return false; } - handler_->OnHeaderDecoded(entry->name(), entry->value()); - return true; + return OnHeaderDecoded(kValueFromStaticTable, entry->name(), entry->value()); } bool QpackProgressiveDecoder::DoIndexedHeaderFieldPostBaseInstruction() { uint64_t absolute_index; if (!QpackPostBaseIndexToAbsoluteIndex(instruction_decoder_.varint(), base_, &absolute_index)) { - OnError("Invalid post-base index."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, "Invalid post-base index."); return false; } if (absolute_index >= required_insert_count_) { - OnError("Absolute Index must be smaller than Required Insert Count."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, + "Absolute Index must be smaller than Required Insert Count."); return false; } @@ -220,13 +226,13 @@ bool QpackProgressiveDecoder::DoIndexedHeaderFieldPostBaseInstruction() { auto entry = header_table_->LookupEntry(/* is_static = */ false, absolute_index); if (!entry) { - OnError("Dynamic table entry already evicted."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, + "Dynamic table entry already evicted."); return false; } header_table_->set_dynamic_table_entry_referenced(); - handler_->OnHeaderDecoded(entry->name(), entry->value()); - return true; + return OnHeaderDecoded(!kValueFromStaticTable, entry->name(), entry->value()); } bool QpackProgressiveDecoder::DoLiteralHeaderFieldNameReferenceInstruction() { @@ -234,12 +240,13 @@ bool QpackProgressiveDecoder::DoLiteralHeaderFieldNameReferenceInstruction() { uint64_t absolute_index; if (!QpackRequestStreamRelativeIndexToAbsoluteIndex( instruction_decoder_.varint(), base_, &absolute_index)) { - OnError("Invalid relative index."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, "Invalid relative index."); return false; } if (absolute_index >= required_insert_count_) { - OnError("Absolute Index must be smaller than Required Insert Count."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, + "Absolute Index must be smaller than Required Insert Count."); return false; } @@ -250,36 +257,38 @@ bool QpackProgressiveDecoder::DoLiteralHeaderFieldNameReferenceInstruction() { auto entry = header_table_->LookupEntry(/* is_static = */ false, absolute_index); if (!entry) { - OnError("Dynamic table entry already evicted."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, + "Dynamic table entry already evicted."); return false; } header_table_->set_dynamic_table_entry_referenced(); - handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value()); - return true; + return OnHeaderDecoded(!kValueFromStaticTable, entry->name(), + instruction_decoder_.value()); } auto entry = header_table_->LookupEntry(/* is_static = */ true, instruction_decoder_.varint()); if (!entry) { - OnError("Static table entry not found."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, "Static table entry not found."); return false; } - handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value()); - return true; + return OnHeaderDecoded(kValueFromStaticTable, entry->name(), + instruction_decoder_.value()); } bool QpackProgressiveDecoder::DoLiteralHeaderFieldPostBaseInstruction() { uint64_t absolute_index; if (!QpackPostBaseIndexToAbsoluteIndex(instruction_decoder_.varint(), base_, &absolute_index)) { - OnError("Invalid post-base index."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, "Invalid post-base index."); return false; } if (absolute_index >= required_insert_count_) { - OnError("Absolute Index must be smaller than Required Insert Count."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, + "Absolute Index must be smaller than Required Insert Count."); return false; } @@ -290,20 +299,19 @@ bool QpackProgressiveDecoder::DoLiteralHeaderFieldPostBaseInstruction() { auto entry = header_table_->LookupEntry(/* is_static = */ false, absolute_index); if (!entry) { - OnError("Dynamic table entry already evicted."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, + "Dynamic table entry already evicted."); return false; } header_table_->set_dynamic_table_entry_referenced(); - handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value()); - return true; + return OnHeaderDecoded(!kValueFromStaticTable, entry->name(), + instruction_decoder_.value()); } bool QpackProgressiveDecoder::DoLiteralHeaderFieldInstruction() { - handler_->OnHeaderDecoded(instruction_decoder_.name(), - instruction_decoder_.value()); - - return true; + return OnHeaderDecoded(!kValueFromStaticTable, instruction_decoder_.name(), + instruction_decoder_.value()); } bool QpackProgressiveDecoder::DoPrefixInstruction() { @@ -312,14 +320,15 @@ bool QpackProgressiveDecoder::DoPrefixInstruction() { if (!QpackDecodeRequiredInsertCount( prefix_decoder_->varint(), header_table_->max_entries(), header_table_->inserted_entry_count(), &required_insert_count_)) { - OnError("Error decoding Required Insert Count."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, + "Error decoding Required Insert Count."); return false; } const bool sign = prefix_decoder_->s_bit(); const uint64_t delta_base = prefix_decoder_->varint2(); if (!DeltaBaseToBase(sign, delta_base, &base_)) { - OnError("Error calculating Base."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, "Error calculating Base."); return false; } @@ -327,7 +336,8 @@ bool QpackProgressiveDecoder::DoPrefixInstruction() { if (required_insert_count_ > header_table_->inserted_entry_count()) { if (!enforcer_->OnStreamBlocked(stream_id_)) { - OnError("Limit on number of blocked streams exceeded."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, + "Limit on number of blocked streams exceeded."); return false; } blocked_ = true; @@ -337,6 +347,32 @@ bool QpackProgressiveDecoder::DoPrefixInstruction() { return true; } +bool QpackProgressiveDecoder::OnHeaderDecoded(bool value_from_static_table, + absl::string_view name, + absl::string_view value) { + // Skip test for static table entries as they are all known to be valid. + if (reject_invalid_chars_in_field_value_ && !value_from_static_table) { + // According to Section 10.3 of + // https://quicwg.org/base-drafts/draft-ietf-quic-http.html, + // "[...] HTTP/3 can transport field values that are not valid. While most + // values that can be encoded will not alter field parsing, carriage return + // (CR, ASCII 0x0d), line feed (LF, ASCII 0x0a), and the zero character + // (NUL, ASCII 0x00) might be exploited by an attacker if they are + // translated verbatim. Any request or response that contains a character + // not permitted in a field value MUST be treated as malformed [...]" + for (const auto c : value) { + if (c == '\0' || c == '\n' || c == '\r') { + OnError(QUIC_INVALID_CHARACTER_IN_FIELD_VALUE, + "Invalid character in field value."); + return false; + } + } + } + + handler_->OnHeaderDecoded(name, value); + return true; +} + void QpackProgressiveDecoder::FinishDecoding() { QUICHE_DCHECK(buffer_.empty()); QUICHE_DCHECK(!blocked_); @@ -347,17 +383,18 @@ void QpackProgressiveDecoder::FinishDecoding() { } if (!instruction_decoder_.AtInstructionBoundary()) { - OnError("Incomplete header block."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, "Incomplete header block."); return; } if (!prefix_decoded_) { - OnError("Incomplete header data prefix."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, "Incomplete header data prefix."); return; } if (required_insert_count_ != required_insert_count_so_far_) { - OnError("Required Insert Count too large."); + OnError(QUIC_QPACK_DECOMPRESSION_FAILED, + "Required Insert Count too large."); return; } @@ -365,6 +402,15 @@ void QpackProgressiveDecoder::FinishDecoding() { handler_->OnDecodingCompleted(); } +void QpackProgressiveDecoder::OnError(QuicErrorCode error_code, + absl::string_view error_message) { + QUICHE_DCHECK(!error_detected_); + + error_detected_ = true; + // Might destroy |this|. + handler_->OnDecodingErrorDetected(error_code, error_message); +} + bool QpackProgressiveDecoder::DeltaBaseToBase(bool sign, uint64_t delta_base, uint64_t* base) { diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h index ced6bf0c2dd..b077ab186cb 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h @@ -13,6 +13,7 @@ #include "quic/core/qpack/qpack_encoder_stream_receiver.h" #include "quic/core/qpack/qpack_header_table.h" #include "quic/core/qpack/qpack_instruction_decoder.h" +#include "quic/core/quic_error_codes.h" #include "quic/core/quic_types.h" #include "quic/platform/api/quic_export.h" @@ -46,7 +47,8 @@ class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder // Called when a decoding error has occurred. No other methods will be // called afterwards. Implementations are allowed to destroy // the QpackProgressiveDecoder instance synchronously. - virtual void OnDecodingErrorDetected(absl::string_view error_message) = 0; + virtual void OnDecodingErrorDetected(QuicErrorCode error_code, + absl::string_view error_message) = 0; }; // Interface for keeping track of blocked streams for the purpose of enforcing @@ -95,9 +97,6 @@ class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder // through Decode(). No methods must be called afterwards. void EndHeaderBlock(); - // Called on error. - void OnError(absl::string_view error_message); - // QpackInstructionDecoder::Delegate implementation. bool OnInstructionDecoded(const QpackInstruction* instruction) override; void OnInstructionDecodingError(QpackInstructionDecoder::ErrorCode error_code, @@ -115,9 +114,20 @@ class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder bool DoLiteralHeaderFieldInstruction(); bool DoPrefixInstruction(); + // Called when an entry is decoded. Performs validation and calls + // HeadersHandlerInterface::OnHeaderDecoded() or OnError() as needed. Returns + // true if header value is valid, false otherwise. Skips validation if + // |value_from_static_table| is true, because static table entries are always + // valid. + bool OnHeaderDecoded(bool value_from_static_table, absl::string_view name, + absl::string_view value); + // Called as soon as EndHeaderBlock() is called and decoding is not blocked. void FinishDecoding(); + // Called on error. + void OnError(QuicErrorCode error_code, absl::string_view error_message); + // Calculates Base from |required_insert_count_|, which must be set before // calling this method, and sign bit and Delta Base in the Header Data Prefix, // which are passed in as arguments. Returns true on success, false on @@ -166,6 +176,10 @@ class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder // True if QpackDecoderHeaderTable has been destroyed // while decoding is still blocked. bool cancelled_; + + // Latched value of + // gfe2_reloadable_flag_quic_reject_invalid_chars_in_field_value + const bool reject_invalid_chars_in_field_value_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.cc index 65e290f380d..1f75919d8f8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.cc @@ -11,8 +11,7 @@ namespace quic { QpackReceiveStream::QpackReceiveStream(PendingStream* pending, QuicSession* session, QpackStreamReceiver* receiver) - : QuicStream(pending, session, READ_UNIDIRECTIONAL, /*is_static=*/true), - receiver_(receiver) {} + : QuicStream(pending, session, /*is_static=*/true), receiver_(receiver) {} void QpackReceiveStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) { stream_delegate()->OnStreamError( diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.cc index 0d5d8af79ef..626d0639bf5 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.cc @@ -21,7 +21,7 @@ void QpackSendStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) { << "OnStreamReset() called for write unidirectional stream."; } -bool QpackSendStream::OnStopSending(QuicRstStreamErrorCode /* code */) { +bool QpackSendStream::OnStopSending(QuicResetStreamError /* code */) { stream_delegate()->OnStreamError( QUIC_HTTP_CLOSED_CRITICAL_STREAM, "STOP_SENDING received for QPACK send stream"); diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.h index fad159c30fe..086ad638d46 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_send_stream.h @@ -33,7 +33,7 @@ class QUIC_EXPORT_PRIVATE QpackSendStream : public QuicStream, // Overriding QuicStream::OnStopSending() to make sure QPACK stream is never // closed before connection. void OnStreamReset(const QuicRstStreamFrame& frame) override; - bool OnStopSending(QuicRstStreamErrorCode code) override; + bool OnStopSending(QuicResetStreamError code) override; // The send QPACK stream is write unidirectional, so this method // should never be called. 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 df4668b07b2..5a98951a630 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 @@ -119,7 +119,8 @@ TEST_P(QpackSendStreamTest, WriteStreamTypeOnlyFirstTime) { TEST_P(QpackSendStreamTest, StopSendingQpackStream) { EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_CLOSED_CRITICAL_STREAM, _, _)); - qpack_send_stream_->OnStopSending(QUIC_STREAM_CANCELLED); + qpack_send_stream_->OnStopSending( + QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED)); } TEST_P(QpackSendStreamTest, ReceiveDataOnSendStream) { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_alarm.cc b/chromium/net/third_party/quiche/src/quic/core/quic_alarm.cc index 81640a4c68e..7419159d18a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_alarm.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_alarm.cc @@ -4,6 +4,8 @@ #include "quic/core/quic_alarm.h" +#include <atomic> + #include "quic/platform/api/quic_bug_tracker.h" #include "quic/platform/api/quic_flag_utils.h" #include "quic/platform/api/quic_flags.h" @@ -15,14 +17,8 @@ QuicAlarm::QuicAlarm(QuicArenaScopedPtr<Delegate> delegate) : delegate_(std::move(delegate)), deadline_(QuicTime::Zero()) {} QuicAlarm::~QuicAlarm() { - if (GetQuicRestartFlag(quic_alarm_add_permanent_cancel) && IsSet()) { + if (IsSet()) { QUIC_CODE_COUNT(quic_alarm_not_cancelled_in_dtor); - static uint64_t hit_count = 0; - ++hit_count; - if ((hit_count & (hit_count - 1)) == 0) { - QUIC_LOG(ERROR) << "QuicAlarm not cancelled at destruction. " - << QuicStackTrace(); - } } } @@ -42,16 +38,6 @@ void QuicAlarm::Set(QuicTime new_deadline) { } void QuicAlarm::CancelInternal(bool permanent) { - if (!GetQuicRestartFlag(quic_alarm_add_permanent_cancel)) { - if (!IsSet()) { - // Don't try to cancel an alarm that hasn't been set. - return; - } - deadline_ = QuicTime::Zero(); - CancelImpl(); - return; - } - if (IsSet()) { deadline_ = QuicTime::Zero(); CancelImpl(); @@ -100,6 +86,11 @@ void QuicAlarm::Fire() { deadline_ = QuicTime::Zero(); if (!IsPermanentlyCancelled()) { + absl::optional<QuicConnectionContextSwitcher> context_switcher; + if (GetQuicReloadableFlag(quic_restore_connection_context_in_alarms)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_restore_connection_context_in_alarms); + context_switcher.emplace(delegate_->GetConnectionContext()); + } delegate_->OnAlarm(); } } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_alarm.h b/chromium/net/third_party/quiche/src/quic/core/quic_alarm.h index 5080fd4b263..26ebfe8ca4d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_alarm.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_alarm.h @@ -6,6 +6,7 @@ #define QUICHE_QUIC_CORE_QUIC_ALARM_H_ #include "quic/core/quic_arena_scoped_ptr.h" +#include "quic/core/quic_connection_context.h" #include "quic/core/quic_time.h" #include "quic/platform/api/quic_export.h" @@ -22,10 +23,39 @@ class QUIC_EXPORT_PRIVATE QuicAlarm { public: virtual ~Delegate() {} + // If the alarm belongs to a single QuicConnection, return the corresponding + // QuicConnection.context_. Note the context_ is the first member of + // QuicConnection, so it should outlive the delegate. + // Otherwise return nullptr. + // The OnAlarm function will be called under the connection context, if any. + virtual QuicConnectionContext* GetConnectionContext() = 0; + // Invoked when the alarm fires. virtual void OnAlarm() = 0; }; + // DelegateWithContext is a Delegate with a QuicConnectionContext* stored as a + // member variable. + class QUIC_EXPORT_PRIVATE DelegateWithContext : public Delegate { + public: + explicit DelegateWithContext(QuicConnectionContext* context) + : context_(context) {} + ~DelegateWithContext() override {} + QuicConnectionContext* GetConnectionContext() override { return context_; } + + private: + QuicConnectionContext* context_; + }; + + // DelegateWithoutContext is a Delegate that does not have a corresponding + // context. Typically this means one object of the child class deals with many + // connections. + class QUIC_EXPORT_PRIVATE DelegateWithoutContext : public Delegate { + public: + ~DelegateWithoutContext() override {} + QuicConnectionContext* GetConnectionContext() override { return nullptr; } + }; + explicit QuicAlarm(QuicArenaScopedPtr<Delegate> delegate); QuicAlarm(const QuicAlarm&) = delete; QuicAlarm& operator=(const QuicAlarm&) = delete; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_alarm_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_alarm_test.cc index ab070022eec..c5f0327d9c6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_alarm_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_alarm_test.cc @@ -4,21 +4,41 @@ #include "quic/core/quic_alarm.h" +#include "quic/core/quic_connection_context.h" #include "quic/platform/api/quic_expect_bug.h" #include "quic/platform/api/quic_test.h" +using testing::ElementsAre; using testing::Invoke; +using testing::Return; namespace quic { namespace test { namespace { +class TraceCollector : public QuicConnectionTracer { + public: + ~TraceCollector() override = default; + + void PrintLiteral(const char* literal) override { trace_.push_back(literal); } + + void PrintString(absl::string_view s) override { + trace_.push_back(std::string(s)); + } + + const std::vector<std::string>& trace() const { return trace_; } + + private: + std::vector<std::string> trace_; +}; + class MockDelegate : public QuicAlarm::Delegate { public: + MOCK_METHOD(QuicConnectionContext*, GetConnectionContext, (), (override)); MOCK_METHOD(void, OnAlarm, (), (override)); }; -class DestructiveDelegate : public QuicAlarm::Delegate { +class DestructiveDelegate : public QuicAlarm::DelegateWithoutContext { public: DestructiveDelegate() : alarm_(nullptr) {} @@ -120,35 +140,16 @@ TEST_F(QuicAlarmTest, PermanentCancel) { EXPECT_FALSE(alarm_.scheduled()); EXPECT_EQ(QuicTime::Zero(), alarm_.deadline()); - if (!GetQuicRestartFlag(quic_alarm_add_permanent_cancel)) { - alarm_.Set(deadline); - // When flag is false, PermanentCancel should work like a normal Cancel. - EXPECT_TRUE(alarm_.IsSet()); - EXPECT_TRUE(alarm_.scheduled()); - EXPECT_EQ(deadline, alarm_.deadline()); - EXPECT_FALSE(alarm_.IsPermanentlyCancelled()); - alarm_.PermanentCancel(); - } else { - EXPECT_QUIC_BUG(alarm_.Set(deadline), - "Set called after alarm is permanently cancelled"); - EXPECT_TRUE(alarm_.IsPermanentlyCancelled()); - } + EXPECT_QUIC_BUG(alarm_.Set(deadline), + "Set called after alarm is permanently cancelled"); + EXPECT_TRUE(alarm_.IsPermanentlyCancelled()); EXPECT_FALSE(alarm_.IsSet()); EXPECT_FALSE(alarm_.scheduled()); EXPECT_EQ(QuicTime::Zero(), alarm_.deadline()); - if (!GetQuicRestartFlag(quic_alarm_add_permanent_cancel)) { - alarm_.Update(deadline, QuicTime::Delta::Zero()); - EXPECT_TRUE(alarm_.IsSet()); - EXPECT_TRUE(alarm_.scheduled()); - EXPECT_EQ(deadline, alarm_.deadline()); - EXPECT_FALSE(alarm_.IsPermanentlyCancelled()); - alarm_.PermanentCancel(); - } else { - EXPECT_QUIC_BUG(alarm_.Update(deadline, QuicTime::Delta::Zero()), - "Update called after alarm is permanently cancelled"); - EXPECT_TRUE(alarm_.IsPermanentlyCancelled()); - } + EXPECT_QUIC_BUG(alarm_.Update(deadline, QuicTime::Delta::Zero()), + "Update called after alarm is permanently cancelled"); + EXPECT_TRUE(alarm_.IsPermanentlyCancelled()); EXPECT_FALSE(alarm_.IsSet()); EXPECT_FALSE(alarm_.scheduled()); EXPECT_EQ(QuicTime::Zero(), alarm_.deadline()); @@ -204,6 +205,67 @@ TEST_F(QuicAlarmTest, FireDestroysAlarm) { alarm->FireAlarm(); } +TEST_F(QuicAlarmTest, NullAlarmContext) { + QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7); + alarm_.Set(deadline); + + if (GetQuicReloadableFlag(quic_restore_connection_context_in_alarms)) { + EXPECT_CALL(*delegate_, GetConnectionContext()).WillOnce(Return(nullptr)); + } + + EXPECT_CALL(*delegate_, OnAlarm()).WillOnce(Invoke([] { + QUIC_TRACELITERAL("Alarm fired."); + })); + alarm_.FireAlarm(); +} + +TEST_F(QuicAlarmTest, AlarmContextWithNullTracer) { + QuicConnectionContext context; + ASSERT_EQ(context.tracer, nullptr); + + QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7); + alarm_.Set(deadline); + + if (GetQuicReloadableFlag(quic_restore_connection_context_in_alarms)) { + EXPECT_CALL(*delegate_, GetConnectionContext()).WillOnce(Return(&context)); + } + + EXPECT_CALL(*delegate_, OnAlarm()).WillOnce(Invoke([] { + QUIC_TRACELITERAL("Alarm fired."); + })); + alarm_.FireAlarm(); +} + +TEST_F(QuicAlarmTest, AlarmContextWithTracer) { + QuicConnectionContext context; + std::unique_ptr<TraceCollector> tracer = std::make_unique<TraceCollector>(); + const TraceCollector& tracer_ref = *tracer; + context.tracer = std::move(tracer); + + QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7); + alarm_.Set(deadline); + + if (GetQuicReloadableFlag(quic_restore_connection_context_in_alarms)) { + EXPECT_CALL(*delegate_, GetConnectionContext()).WillOnce(Return(&context)); + } + + EXPECT_CALL(*delegate_, OnAlarm()).WillOnce(Invoke([] { + QUIC_TRACELITERAL("Alarm fired."); + })); + + // Since |context| is not installed in the current thread, the messages before + // and after FireAlarm() should not be collected by |tracer|. + QUIC_TRACELITERAL("Should not be collected before alarm."); + alarm_.FireAlarm(); + QUIC_TRACELITERAL("Should not be collected after alarm."); + + if (GetQuicReloadableFlag(quic_restore_connection_context_in_alarms)) { + EXPECT_THAT(tracer_ref.trace(), ElementsAre("Alarm fired.")); + } else { + EXPECT_TRUE(tracer_ref.trace().empty()); + } +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.cc b/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.cc index 9312ea5deae..f235e45b30f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.cc @@ -24,7 +24,7 @@ static const size_t kMaxConnectionsWithoutCHLO = namespace { // This alarm removes expired entries in map each time this alarm fires. -class ConnectionExpireAlarm : public QuicAlarm::Delegate { +class ConnectionExpireAlarm : public QuicAlarm::DelegateWithoutContext { public: explicit ConnectionExpireAlarm(QuicBufferedPacketStore* store) : connection_store_(store) {} @@ -77,8 +77,7 @@ QuicBufferedPacketStore::QuicBufferedPacketStore( alarm_factory->CreateAlarm(new ConnectionExpireAlarm(this))) {} QuicBufferedPacketStore::~QuicBufferedPacketStore() { - if (GetQuicRestartFlag(quic_alarm_add_permanent_cancel) && - expiration_alarm_ != nullptr) { + if (expiration_alarm_ != nullptr) { expiration_alarm_->PermanentCancel(); } } 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 12bf186d318..f152e6e799a 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 @@ -47,6 +47,7 @@ #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_server_stats.h" #include "quic/platform/api/quic_socket_address.h" +#include "common/platform/api/quiche_flag_utils.h" #include "common/quiche_text_utils.h" namespace quic { @@ -71,6 +72,10 @@ class QuicConnectionAlarmDelegate : public QuicAlarm::Delegate { QuicConnectionAlarmDelegate& operator=(const QuicConnectionAlarmDelegate&) = delete; + QuicConnectionContext* GetConnectionContext() override { + return (connection_ == nullptr) ? nullptr : connection_->context(); + } + protected: QuicConnection* connection_; }; @@ -215,16 +220,11 @@ QuicConnection::QuicConnection( QuicConnectionId server_connection_id, QuicSocketAddress initial_self_address, QuicSocketAddress initial_peer_address, - QuicConnectionHelperInterface* helper, - QuicAlarmFactory* alarm_factory, - QuicPacketWriter* writer, - bool owns_writer, - Perspective perspective, + QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory, + QuicPacketWriter* writer, bool owns_writer, Perspective perspective, const ParsedQuicVersionVector& supported_versions) - : framer_(supported_versions, - helper->GetClock()->ApproximateNow(), - perspective, - server_connection_id.length()), + : framer_(supported_versions, helper->GetClock()->ApproximateNow(), + perspective, server_connection_id.length()), current_packet_content_(NO_FRAMES_RECEIVED), is_current_packet_connectivity_probing_(false), has_path_challenge_in_current_packet_(false), @@ -239,8 +239,7 @@ QuicConnection::QuicConnection( random_generator_(helper->GetRandomGenerator()), client_connection_id_is_set_(false), direct_peer_address_(initial_peer_address), - default_path_(initial_self_address, - QuicSocketAddress(), + default_path_(initial_self_address, QuicSocketAddress(), /*client_connection_id=*/EmptyQuicConnectionId(), server_connection_id, /*stateless_reset_token_received=*/false, @@ -269,23 +268,17 @@ QuicConnection::QuicConnection( ack_alarm_(alarm_factory_->CreateAlarm(arena_.New<AckAlarmDelegate>(this), &arena_)), retransmission_alarm_(alarm_factory_->CreateAlarm( - arena_.New<RetransmissionAlarmDelegate>(this), - &arena_)), - send_alarm_( - alarm_factory_->CreateAlarm(arena_.New<SendAlarmDelegate>(this), - &arena_)), - ping_alarm_( - alarm_factory_->CreateAlarm(arena_.New<PingAlarmDelegate>(this), - &arena_)), + arena_.New<RetransmissionAlarmDelegate>(this), &arena_)), + send_alarm_(alarm_factory_->CreateAlarm( + arena_.New<SendAlarmDelegate>(this), &arena_)), + ping_alarm_(alarm_factory_->CreateAlarm( + arena_.New<PingAlarmDelegate>(this), &arena_)), mtu_discovery_alarm_(alarm_factory_->CreateAlarm( - arena_.New<MtuDiscoveryAlarmDelegate>(this), - &arena_)), + arena_.New<MtuDiscoveryAlarmDelegate>(this), &arena_)), process_undecryptable_packets_alarm_(alarm_factory_->CreateAlarm( - arena_.New<ProcessUndecryptablePacketsAlarmDelegate>(this), - &arena_)), + arena_.New<ProcessUndecryptablePacketsAlarmDelegate>(this), &arena_)), discard_previous_one_rtt_keys_alarm_(alarm_factory_->CreateAlarm( - arena_.New<DiscardPreviousOneRttKeysAlarmDelegate>(this), - &arena_)), + arena_.New<DiscardPreviousOneRttKeysAlarmDelegate>(this), &arena_)), discard_zero_rtt_decryption_keys_alarm_(alarm_factory_->CreateAlarm( arena_.New<DiscardZeroRttDecryptionKeysAlarmDelegate>(this), &arena_)), @@ -293,10 +286,7 @@ QuicConnection::QuicConnection( debug_visitor_(nullptr), packet_creator_(server_connection_id, &framer_, random_generator_, this), last_received_packet_info_(clock_->ApproximateNow()), - sent_packet_manager_(perspective, - clock_, - random_generator_, - &stats_, + sent_packet_manager_(perspective, clock_, random_generator_, &stats_, GetDefaultCongestionControlType()), version_negotiated_(false), perspective_(perspective), @@ -319,12 +309,11 @@ QuicConnection::QuicConnection( processing_ack_frame_(false), supports_release_time_(false), release_time_into_future_(QuicTime::Delta::Zero()), - blackhole_detector_(this, &arena_, alarm_factory_), - idle_network_detector_(this, - clock_->ApproximateNow(), - &arena_, - alarm_factory_), - path_validator_(alarm_factory_, &arena_, this, random_generator_), + blackhole_detector_(this, &arena_, alarm_factory_, &context_), + idle_network_detector_(this, clock_->ApproximateNow(), &arena_, + alarm_factory_, &context_), + path_validator_(alarm_factory_, &arena_, this, random_generator_, + &context_), most_recent_frame_type_(NUM_FRAME_TYPES) { QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT || default_path_.self_address.IsInitialized()); @@ -335,10 +324,8 @@ QuicConnection::QuicConnection( support_multiple_connection_ids_ = version().HasIetfQuicFrames() && - GetQuicRestartFlag(quic_time_wait_list_support_multiple_cid_v2) && GetQuicRestartFlag( - quic_dispatcher_support_multiple_cid_per_connection_v2) && - GetQuicReloadableFlag(quic_connection_support_multiple_cids_v4); + quic_dispatcher_support_multiple_cid_per_connection_v2); QUIC_DLOG(INFO) << ENDPOINT << "Created connection with server connection ID " << server_connection_id @@ -535,8 +522,8 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { } else { SetNetworkTimeouts(config.max_time_before_crypto_handshake(), config.max_idle_time_before_crypto_handshake()); - if (config.HasClientRequestedIndependentOption(kCHSP, perspective_)) { - packet_creator_.set_chaos_protection_enabled(true); + if (config.HasClientRequestedIndependentOption(kNCHP, perspective_)) { + packet_creator_.set_chaos_protection_enabled(false); } } @@ -650,10 +637,17 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { if (config.HasClientSentConnectionOption(kDFER, perspective_)) { defer_send_in_response_to_packets_ = false; } + const bool remove_connection_migration_connection_option = + GetQuicReloadableFlag(quic_remove_connection_migration_connection_option); + if (remove_connection_migration_connection_option) { + QUIC_RELOADABLE_FLAG_COUNT( + quic_remove_connection_migration_connection_option); + } if (framer_.version().HasIetfQuicFrames() && use_path_validator_ && count_bytes_on_alternative_path_separately_ && GetQuicReloadableFlag(quic_server_reverse_validate_new_path3) && - config.HasClientSentConnectionOption(kRVCM, perspective_)) { + (remove_connection_migration_connection_option || + config.HasClientSentConnectionOption(kRVCM, perspective_))) { QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path3, 6, 6); validate_client_addresses_ = true; } @@ -664,7 +658,6 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { // 2) Client side's rollout can be protected by the same connection option. connection_migration_use_new_cid_ = support_multiple_connection_ids_ && validate_client_addresses_ && - group_path_response_and_challenge_sending_closer_ && GetQuicReloadableFlag(quic_drop_unsent_path_response) && GetQuicReloadableFlag(quic_connection_migration_use_new_cid_v2); if (config.HasReceivedMaxPacketSize()) { @@ -1705,17 +1698,8 @@ bool QuicConnection::OnPathChallengeFrame(const QuicPathChallengeFrame& frame) { } QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path3, 1, 6); { - // UpdatePacketStateAndReplyPathChallenge() may start reverse path - // validation, if so bundle the PATH_CHALLENGE together with the - // PATH_RESPONSE. This context needs to be out of scope before returning. // TODO(danzh) inline OnPathChallengeFrameInternal() once - // support_reverse_path_validation_ is deprecated. - auto context = - group_path_response_and_challenge_sending_closer_ - ? nullptr - : std::make_unique<QuicPacketCreator::ScopedPeerAddressContext>( - &packet_creator_, last_received_packet_info_.source_address, - /*update_connection_id=*/false); + // validate_client_addresses_ is deprecated. if (!OnPathChallengeFrameInternal(frame)) { return false; } @@ -1734,21 +1718,16 @@ bool QuicConnection::OnPathChallengeFrameInternal( debug_visitor_->OnPathChallengeFrame(frame); } - std::unique_ptr<QuicPacketCreator::ScopedPeerAddressContext> context; const QuicSocketAddress current_effective_peer_address = GetEffectivePeerAddressFromCurrentPacket(); - if (group_path_response_and_challenge_sending_closer_) { - QuicConnectionId client_cid, server_cid; - FindOnPathConnectionIds(last_received_packet_info_.destination_address, - current_effective_peer_address, &client_cid, - &server_cid); - context = std::make_unique<QuicPacketCreator::ScopedPeerAddressContext>( - &packet_creator_, last_received_packet_info_.source_address, client_cid, - server_cid, connection_migration_use_new_cid_); - } + QuicConnectionId client_cid, server_cid; + FindOnPathConnectionIds(last_received_packet_info_.destination_address, + current_effective_peer_address, &client_cid, + &server_cid); + QuicPacketCreator::ScopedPeerAddressContext context( + &packet_creator_, last_received_packet_info_.source_address, client_cid, + server_cid, connection_migration_use_new_cid_); if (should_proactively_validate_peer_address_on_path_challenge_) { - QUIC_RELOADABLE_FLAG_COUNT( - quic_group_path_response_and_challenge_sending_closer); // Conditions to proactively validate peer address: // The perspective is server // The PATH_CHALLENGE is received on an unvalidated alternative path. @@ -2042,11 +2021,7 @@ bool QuicConnection::OnNewConnectionIdFrameInner( if (perspective_ == Perspective::IS_SERVER) { OnClientConnectionIdAvailable(); } - QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_support_multiple_cids_v4, 1, 2); - if (ack_cid_frames_) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_ack_cid_frames, 1, 2); - MaybeUpdateAckTimeout(); - } + MaybeUpdateAckTimeout(); return true; } @@ -2104,13 +2079,9 @@ bool QuicConnection::OnRetireConnectionIdFrame( ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return false; } - QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_support_multiple_cids_v4, 2, 2); // Count successfully received RETIRE_CONNECTION_ID frames. QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 5, 6); - if (ack_cid_frames_) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_ack_cid_frames, 2, 2); - MaybeUpdateAckTimeout(); - } + MaybeUpdateAckTimeout(); return true; } @@ -2717,19 +2688,12 @@ void QuicConnection::OnUndecryptablePacket(const QuicEncryptedPacket& packet, bool QuicConnection::ShouldEnqueueUnDecryptablePacket( EncryptionLevel decryption_level, bool has_decryption_key) const { - if (!GetQuicReloadableFlag(quic_queue_until_handshake_complete) && - encryption_level_ == ENCRYPTION_FORWARD_SECURE) { - // We do not expect to install any further keys. - return false; - } if (has_decryption_key) { // We already have the key for this decryption level, therefore no // future keys will allow it be decrypted. return false; } - if (GetQuicReloadableFlag(quic_queue_until_handshake_complete) && - IsHandshakeComplete()) { - QUICHE_RELOADABLE_FLAG_COUNT(quic_queue_until_handshake_complete); + if (IsHandshakeComplete()) { // We do not expect to install any further keys. return false; } @@ -3007,7 +2971,7 @@ void QuicConnection::ReplaceInitialServerConnectionId( peer_issued_cid_manager_ = std::make_unique<QuicPeerIssuedConnectionIdManager>( kMinNumOfActiveConnectionIds, new_server_connection_id, clock_, - alarm_factory_, this); + alarm_factory_, this, context()); } } } @@ -3158,7 +3122,11 @@ bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) { if (perspective_ == Perspective::IS_SERVER && encryption_level_ == ENCRYPTION_INITIAL && last_size_ > packet_creator_.max_packet_length()) { - SetMaxPacketLength(last_size_); + if (GetQuicFlag(FLAGS_quic_use_lower_server_response_mtu_for_test)) { + SetMaxPacketLength(std::min(last_size_, QuicByteCount(1250))); + } else { + SetMaxPacketLength(last_size_); + } } return true; } @@ -3334,6 +3302,20 @@ bool QuicConnection::CanWrite(HasRetransmittableData retransmittable) { return false; } + if (GetQuicReloadableFlag(quic_suppress_write_mid_packet_processing) && + version().CanSendCoalescedPackets() && + framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_INITIAL) && + framer_.is_processing_packet()) { + QUIC_RELOADABLE_FLAG_COUNT(quic_suppress_write_mid_packet_processing); + // While we still have initial keys, suppress sending in mid of packet + // processing. + // TODO(fayang): always suppress sending while in the mid of packet + // processing. + QUIC_DVLOG(1) << ENDPOINT + << "Suppress sending in the mid of packet processing"; + return false; + } + if (fill_coalesced_packet_) { // Try to coalesce packet, only allow to write when creator is on soft max // packet length. Given the next created packet is going to fill current @@ -4080,7 +4062,7 @@ QuicConnection::MakeSelfIssuedConnectionIdManager() { perspective_ == Perspective::IS_CLIENT ? default_path_.client_connection_id : default_path_.server_connection_id, - clock_, alarm_factory_, this); + clock_, alarm_factory_, this, context()); } void QuicConnection::MaybeSendConnectionIdToClient() { @@ -4489,10 +4471,17 @@ void QuicConnection::MaybeProcessUndecryptablePackets() { iter = undecryptable_packets_.erase(iter); } - // Once forward secure encryption is in use, there will be no - // new keys installed and hence any undecryptable packets will - // never be able to be decrypted. - if (encryption_level_ == ENCRYPTION_FORWARD_SECURE) { + // Once handshake is complete, there will be no new keys installed and hence + // any undecryptable packets will never be able to be decrypted. + bool clear_undecryptable_packets = + encryption_level_ == ENCRYPTION_FORWARD_SECURE; + if (GetQuicReloadableFlag( + quic_clear_undecryptable_packets_on_handshake_complete)) { + QUIC_RELOADABLE_FLAG_COUNT( + quic_clear_undecryptable_packets_on_handshake_complete); + clear_undecryptable_packets = IsHandshakeComplete(); + } + if (clear_undecryptable_packets) { if (debug_visitor_ != nullptr) { for (const auto& undecryptable_packet : undecryptable_packets_) { debug_visitor_->OnUndecryptablePacket( @@ -5299,14 +5288,13 @@ void QuicConnection::StartEffectivePeerMigration(AddressChangeType type) { << "EffectivePeerMigration started without address change."; return; } + // There could be pending NEW_TOKEN_FRAME triggered by non-probing + // PATH_RESPONSE_FRAME in the same packet. if (packet_creator_.HasPendingFrames()) { - QUIC_BUG(bug_731_2) - << "Starts effective peer migration with pending frame types: " - << packet_creator_.GetPendingFramesInfo() << ". Address change type is " - << AddressChangeTypeToString(type) - << ". Current frame type: " << framer_.current_received_frame_type() - << ". Previous frame type: " - << framer_.previously_received_frame_type(); + packet_creator_.FlushCurrentPacket(); + if (!connected_) { + return; + } } // Action items: @@ -5363,17 +5351,18 @@ void QuicConnection::StartEffectivePeerMigration(AddressChangeType type) { std::move(alternative_path_.rtt_stats).value()); } } - - // Update to the new peer address. - if (packet_creator_.HasPendingFrames()) { - QUIC_BUG(bug_731_1) + if (packet_creator_.HasPendingFrames() || + packet_creator_.pending_padding_bytes() > 0) { + QUIC_BUG(quic_bug_5196) << "Starts effective peer migration with pending frame types: " - << packet_creator_.GetPendingFramesInfo() << ". Address change type is " - << AddressChangeTypeToString(type) + << packet_creator_.GetPendingFramesInfo() << ". pending_padding_bytes: " + << packet_creator_.pending_padding_bytes() + << ". Address change type is " << AddressChangeTypeToString(type) << ". Current frame type: " << framer_.current_received_frame_type() << ". Previous frame type: " << framer_.previously_received_frame_type(); } + // Update to the new peer address. UpdatePeerAddress(last_received_packet_info_.source_address); // Update the default path. if (IsAlternativePath(last_received_packet_info_.destination_address, @@ -5641,24 +5630,7 @@ bool QuicConnection::UpdatePacketContent(QuicFrameType type) { current_effective_peer_address, client_connection_id, last_packet_destination_connection_id_, stateless_reset_token_received, stateless_reset_token); - if (group_path_response_and_challenge_sending_closer_) { - should_proactively_validate_peer_address_on_path_challenge_ = true; - } else { - // Conditions to proactively validate peer address: - // The perspective is server - // The PATH_CHALLENGE is received on an unvalidated alternative path. - // The connection isn't validating migrated peer address, which is of - // higher prority. - QUIC_DVLOG(1) << "Proactively validate the effective peer address " - << current_effective_peer_address; - QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 1, 6); - ValidatePath(std::make_unique<ReversePathValidationContext>( - default_path_.self_address, - last_received_packet_info_.source_address, - current_effective_peer_address, this), - std::make_unique<ReversePathValidationResultDelegate>( - this, peer_address())); - } + should_proactively_validate_peer_address_on_path_challenge_ = true; } } MaybeUpdateBytesReceivedFromAlternativeAddress(last_size_); @@ -6075,6 +6047,9 @@ bool QuicConnection::FlushCoalescedPacket() { if (length == 0) { return false; } + if (debug_visitor_ != nullptr) { + debug_visitor_->OnCoalescedPacketSent(coalesced_packet_, length); + } QUIC_DVLOG(1) << ENDPOINT << "Sending coalesced packet " << coalesced_packet_.ToString(length); @@ -6084,9 +6059,6 @@ bool QuicConnection::FlushCoalescedPacket() { buffered_packets_.emplace_back( buffer, static_cast<QuicPacketLength>(length), coalesced_packet_.self_address(), coalesced_packet_.peer_address()); - if (debug_visitor_ != nullptr) { - debug_visitor_->OnCoalescedPacketSent(coalesced_packet_, length); - } return true; } @@ -6107,9 +6079,6 @@ bool QuicConnection::FlushCoalescedPacket() { coalesced_packet_.self_address(), coalesced_packet_.peer_address()); } } - if (debug_visitor_ != nullptr) { - debug_visitor_->OnCoalescedPacketSent(coalesced_packet_, length); - } // Account for added padding. if (length > coalesced_packet_.length()) { size_t padding_size = length - coalesced_packet_.length(); @@ -6167,7 +6136,7 @@ void QuicConnection::SetLargestReceivedPacketWithAck( } void QuicConnection::OnForwardProgressMade() { - if (GetQuicRestartFlag(quic_alarm_add_permanent_cancel) && !connected_) { + if (!connected_) { return; } if (is_path_degrading_) { @@ -6298,7 +6267,7 @@ void QuicConnection::set_client_connection_id( peer_issued_cid_manager_ = std::make_unique<QuicPeerIssuedConnectionIdManager>( kMinNumOfActiveConnectionIds, client_connection_id, clock_, - alarm_factory_, this); + alarm_factory_, this, context()); } else { // Note in Chromium client, set_client_connection_id is not called and // thus self_issued_cid_manager_ should be null. @@ -6872,6 +6841,24 @@ void QuicConnection::OnPathValidationFailureAtClient() { RetirePeerIssuedConnectionIdsNoLongerOnPath(); } +QuicConnectionId QuicConnection::GetOneActiveServerConnectionId() const { + if (perspective_ == Perspective::IS_CLIENT || + self_issued_cid_manager_ == nullptr) { + return connection_id(); + } + auto active_connection_ids = GetActiveServerConnectionIds(); + QUIC_BUG_IF(quic_bug_6944, active_connection_ids.empty()); + if (active_connection_ids.empty() || + std::find(active_connection_ids.begin(), active_connection_ids.end(), + connection_id()) != active_connection_ids.end()) { + return connection_id(); + } + QUICHE_CODE_COUNT(connection_id_on_default_path_has_been_retired); + auto active_connection_id = + self_issued_cid_manager_->GetOneActiveConnectionId(); + return active_connection_id; +} + std::vector<QuicConnectionId> QuicConnection::GetActiveServerConnectionIds() const { if (!support_multiple_connection_ids_ || @@ -6891,7 +6878,7 @@ void QuicConnection::CreateConnectionIdManager() { peer_issued_cid_manager_ = std::make_unique<QuicPeerIssuedConnectionIdManager>( kMinNumOfActiveConnectionIds, default_path_.server_connection_id, - clock_, alarm_factory_, this); + clock_, alarm_factory_, this, context()); } } else { if (!default_path_.server_connection_id.IsEmpty()) { 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 dea513a1768..5d8ff2717ca 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 @@ -263,10 +263,9 @@ class QUIC_EXPORT_PRIVATE QuicConnectionDebugVisitor const QuicFrames& /*nonretransmittable_frames*/, QuicTime /*sent_time*/) {} - // Called when a coalesced packet has been sent. + // Called when a coalesced packet is successfully serialized. virtual void OnCoalescedPacketSent( - const QuicCoalescedPacket& /*coalesced_packet*/, - size_t /*length*/) {} + const QuicCoalescedPacket& /*coalesced_packet*/, size_t /*length*/) {} // Called when a PING frame has been sent. virtual void OnPingSent() {} @@ -778,9 +777,12 @@ class QUIC_EXPORT_PRIVATE QuicConnection const QuicSocketAddress& effective_peer_address() const { return default_path_.peer_address; } + + // Returns the server connection ID used on the default path. const QuicConnectionId& connection_id() const { return default_path_.server_connection_id; } + const QuicConnectionId& client_connection_id() const { return default_path_.client_connection_id; } @@ -1225,6 +1227,12 @@ class QUIC_EXPORT_PRIVATE QuicConnection SendPingAtLevel(framer().GetEncryptionLevelToSendApplicationData()); } + // Returns one server connection ID that associates the current session in the + // session map. + virtual QuicConnectionId GetOneActiveServerConnectionId() const; + + // Returns all server connection IDs that have not been removed from the + // session map. virtual std::vector<QuicConnectionId> GetActiveServerConnectionIds() const; bool validate_client_address() const { return validate_client_addresses_; } @@ -2267,18 +2275,12 @@ class QUIC_EXPORT_PRIVATE QuicConnection // Enable this via reloadable flag once this feature is complete. bool connection_migration_use_new_cid_ = false; - const bool group_path_response_and_challenge_sending_closer_ = - GetQuicReloadableFlag( - quic_group_path_response_and_challenge_sending_closer); - const bool reset_per_packet_state_for_undecryptable_packets_ = GetQuicReloadableFlag( quic_reset_per_packet_state_for_undecryptable_packets); const bool add_missing_update_ack_timeout_ = GetQuicReloadableFlag(quic_add_missing_update_ack_timeout); - - const bool ack_cid_frames_ = GetQuicReloadableFlag(quic_ack_cid_frames); }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.cc index f7089b7ebfd..1138a2ed5d4 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.cc @@ -3,12 +3,14 @@ // found in the LICENSE file. #include "quic/core/quic_connection_id_manager.h" + #include <cstdio> #include "quic/core/quic_clock.h" #include "quic/core/quic_connection_id.h" #include "quic/core/quic_error_codes.h" #include "quic/core/quic_utils.h" +#include "common/platform/api/quiche_logging.h" namespace quic { @@ -22,11 +24,13 @@ QuicConnectionIdData::QuicConnectionIdData( namespace { -class RetirePeerIssuedConnectionIdAlarm : public QuicAlarm::Delegate { +class RetirePeerIssuedConnectionIdAlarm + : public QuicAlarm::DelegateWithContext { public: explicit RetirePeerIssuedConnectionIdAlarm( - QuicConnectionIdManagerVisitorInterface* visitor) - : visitor_(visitor) {} + QuicConnectionIdManagerVisitorInterface* visitor, + QuicConnectionContext* context) + : QuicAlarm::DelegateWithContext(context), visitor_(visitor) {} RetirePeerIssuedConnectionIdAlarm(const RetirePeerIssuedConnectionIdAlarm&) = delete; RetirePeerIssuedConnectionIdAlarm& operator=( @@ -61,13 +65,13 @@ std::vector<QuicConnectionIdData>::iterator FindConnectionIdData( QuicPeerIssuedConnectionIdManager::QuicPeerIssuedConnectionIdManager( size_t active_connection_id_limit, const QuicConnectionId& initial_peer_issued_connection_id, - const QuicClock* clock, - QuicAlarmFactory* alarm_factory, - QuicConnectionIdManagerVisitorInterface* visitor) + const QuicClock* clock, QuicAlarmFactory* alarm_factory, + QuicConnectionIdManagerVisitorInterface* visitor, + QuicConnectionContext* context) : active_connection_id_limit_(active_connection_id_limit), clock_(clock), retire_connection_id_alarm_(alarm_factory->CreateAlarm( - new RetirePeerIssuedConnectionIdAlarm(visitor))) { + new RetirePeerIssuedConnectionIdAlarm(visitor, context))) { QUICHE_DCHECK_GE(active_connection_id_limit_, 2u); QUICHE_DCHECK(!initial_peer_issued_connection_id.IsEmpty()); active_connection_id_data_.emplace_back<const QuicConnectionId&, uint64_t, @@ -249,11 +253,14 @@ void QuicPeerIssuedConnectionIdManager::ReplaceConnectionId( namespace { -class RetireSelfIssuedConnectionIdAlarmDelegate : public QuicAlarm::Delegate { +class RetireSelfIssuedConnectionIdAlarmDelegate + : public QuicAlarm::DelegateWithContext { public: explicit RetireSelfIssuedConnectionIdAlarmDelegate( - QuicSelfIssuedConnectionIdManager* connection_id_manager) - : connection_id_manager_(connection_id_manager) {} + QuicSelfIssuedConnectionIdManager* connection_id_manager, + QuicConnectionContext* context) + : QuicAlarm::DelegateWithContext(context), + connection_id_manager_(connection_id_manager) {} RetireSelfIssuedConnectionIdAlarmDelegate( const RetireSelfIssuedConnectionIdAlarmDelegate&) = delete; RetireSelfIssuedConnectionIdAlarmDelegate& operator=( @@ -269,15 +276,15 @@ class RetireSelfIssuedConnectionIdAlarmDelegate : public QuicAlarm::Delegate { QuicSelfIssuedConnectionIdManager::QuicSelfIssuedConnectionIdManager( size_t active_connection_id_limit, - const QuicConnectionId& initial_connection_id, - const QuicClock* clock, + const QuicConnectionId& initial_connection_id, const QuicClock* clock, QuicAlarmFactory* alarm_factory, - QuicConnectionIdManagerVisitorInterface* visitor) + QuicConnectionIdManagerVisitorInterface* visitor, + QuicConnectionContext* context) : active_connection_id_limit_(active_connection_id_limit), clock_(clock), visitor_(visitor), retire_connection_id_alarm_(alarm_factory->CreateAlarm( - new RetireSelfIssuedConnectionIdAlarmDelegate(this))), + new RetireSelfIssuedConnectionIdAlarmDelegate(this, context))), last_connection_id_(initial_connection_id), next_connection_id_sequence_number_(1u), last_connection_id_consumed_by_self_sequence_number_(0u) { @@ -372,6 +379,12 @@ QuicSelfIssuedConnectionIdManager::GetUnretiredConnectionIds() const { return unretired_ids; } +QuicConnectionId QuicSelfIssuedConnectionIdManager::GetOneActiveConnectionId() + const { + QUICHE_DCHECK(!active_connection_ids_.empty()); + return active_connection_ids_.front().first; +} + void QuicSelfIssuedConnectionIdManager::RetireConnectionId() { if (to_be_retired_connection_ids_.empty()) { QUIC_BUG(quic_bug_12420_1) diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.h index 5e7fc8ccf72..e23668df2c9 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.h @@ -60,9 +60,9 @@ class QUIC_EXPORT_PRIVATE QuicPeerIssuedConnectionIdManager { QuicPeerIssuedConnectionIdManager( size_t active_connection_id_limit, const QuicConnectionId& initial_peer_issued_connection_id, - const QuicClock* clock, - QuicAlarmFactory* alarm_factory, - QuicConnectionIdManagerVisitorInterface* visitor); + const QuicClock* clock, QuicAlarmFactory* alarm_factory, + QuicConnectionIdManagerVisitorInterface* visitor, + QuicConnectionContext* context); ~QuicPeerIssuedConnectionIdManager(); @@ -123,10 +123,10 @@ class QUIC_EXPORT_PRIVATE QuicSelfIssuedConnectionIdManager { public: QuicSelfIssuedConnectionIdManager( size_t active_connection_id_limit, - const QuicConnectionId& initial_connection_id, - const QuicClock* clock, + const QuicConnectionId& initial_connection_id, const QuicClock* clock, QuicAlarmFactory* alarm_factory, - QuicConnectionIdManagerVisitorInterface* visitor); + QuicConnectionIdManagerVisitorInterface* visitor, + QuicConnectionContext* context); virtual ~QuicSelfIssuedConnectionIdManager(); @@ -139,6 +139,8 @@ class QUIC_EXPORT_PRIVATE QuicSelfIssuedConnectionIdManager { std::vector<QuicConnectionId> GetUnretiredConnectionIds() const; + QuicConnectionId GetOneActiveConnectionId() const; + // Called when the retire_connection_id alarm_ fires. Removes the to be // retired connection ID locally. void RetireConnectionId(); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager_test.cc index 7baeb2b247e..fdb73daa85b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager_test.cc @@ -78,11 +78,9 @@ class TestPeerIssuedConnectionIdManagerVisitor class QuicPeerIssuedConnectionIdManagerTest : public QuicTest { public: QuicPeerIssuedConnectionIdManagerTest() - : peer_issued_cid_manager_(/*active_connection_id_limit=*/2, - initial_connection_id_, - &clock_, - &alarm_factory_, - &cid_manager_visitor_) { + : peer_issued_cid_manager_( + /*active_connection_id_limit=*/2, initial_connection_id_, &clock_, + &alarm_factory_, &cid_manager_visitor_, /*context=*/nullptr) { clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); cid_manager_visitor_.SetPeerIssuedConnectionIdManager( &peer_issued_cid_manager_); @@ -538,11 +536,9 @@ class TestSelfIssuedConnectionIdManagerVisitor class QuicSelfIssuedConnectionIdManagerTest : public QuicTest { public: QuicSelfIssuedConnectionIdManagerTest() - : cid_manager_(/*active_connection_id_limit*/ 2, - initial_connection_id_, - &clock_, - &alarm_factory_, - &cid_manager_visitor_) { + : cid_manager_(/*active_connection_id_limit*/ 2, initial_connection_id_, + &clock_, &alarm_factory_, &cid_manager_visitor_, + /*context=*/nullptr) { clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); retire_self_issued_cid_alarm_ = QuicConnectionIdManagerPeer::GetRetireSelfIssuedConnectionIdAlarm( 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 69666301c59..e525fd12683 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 @@ -179,33 +179,6 @@ class TestConnectionHelper : public QuicConnectionHelperInterface { SimpleBufferAllocator buffer_allocator_; }; -class TestAlarmFactory : public QuicAlarmFactory { - public: - class TestAlarm : public QuicAlarm { - public: - explicit TestAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate> delegate) - : QuicAlarm(std::move(delegate)) {} - - void SetImpl() override {} - void CancelImpl() override {} - using QuicAlarm::Fire; - }; - - TestAlarmFactory() {} - TestAlarmFactory(const TestAlarmFactory&) = delete; - TestAlarmFactory& operator=(const TestAlarmFactory&) = delete; - - QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override { - return new TestAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate)); - } - - QuicArenaScopedPtr<QuicAlarm> CreateAlarm( - QuicArenaScopedPtr<QuicAlarm::Delegate> delegate, - QuicConnectionArena* arena) override { - return arena->New<TestAlarm>(std::move(delegate)); - } -}; - class TestConnection : public QuicConnection { public: TestConnection(QuicConnectionId connection_id, @@ -826,10 +799,10 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { level); } - void ProcessFramesPacketWithAddresses(QuicFrames frames, - QuicSocketAddress self_address, - QuicSocketAddress peer_address, - EncryptionLevel level) { + std::unique_ptr<QuicReceivedPacket> ConstructPacket(QuicFrames frames, + EncryptionLevel level, + char* buffer, + size_t buffer_len) { QUICHE_DCHECK(peer_framer_.HasEncrypterOfEncryptionLevel(level)); peer_creator_.set_encryption_level(level); QuicPacketCreatorPeer::SetSendVersionInPacket( @@ -837,14 +810,23 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { level < ENCRYPTION_FORWARD_SECURE && connection_.perspective() == Perspective::IS_SERVER); - char buffer[kMaxOutgoingPacketSize]; SerializedPacket serialized_packet = - QuicPacketCreatorPeer::SerializeAllFrames( - &peer_creator_, frames, buffer, kMaxOutgoingPacketSize); + QuicPacketCreatorPeer::SerializeAllFrames(&peer_creator_, frames, + buffer, buffer_len); + return std::make_unique<QuicReceivedPacket>( + serialized_packet.encrypted_buffer, serialized_packet.encrypted_length, + clock_.Now()); + } + + void ProcessFramesPacketWithAddresses(QuicFrames frames, + QuicSocketAddress self_address, + QuicSocketAddress peer_address, + EncryptionLevel level) { + char buffer[kMaxOutgoingPacketSize]; connection_.ProcessUdpPacket( self_address, peer_address, - QuicReceivedPacket(serialized_packet.encrypted_buffer, - serialized_packet.encrypted_length, clock_.Now())); + *ConstructPacket(std::move(frames), level, buffer, + kMaxOutgoingPacketSize)); if (connection_.GetSendAlarm()->IsSet()) { connection_.GetSendAlarm()->Fire(); } @@ -1338,12 +1320,15 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { void set_perspective(Perspective perspective) { connection_.set_perspective(perspective); if (perspective == Perspective::IS_SERVER) { - QuicConfig config; - QuicTagVector connection_options; - connection_options.push_back(kRVCM); - config.SetInitialReceivedConnectionOptions(connection_options); - EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); - connection_.SetFromConfig(config); + if (!GetQuicReloadableFlag( + quic_remove_connection_migration_connection_option)) { + QuicConfig config; + QuicTagVector connection_options; + connection_options.push_back(kRVCM); + config.SetInitialReceivedConnectionOptions(connection_options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + } connection_.set_can_truncate_connection_ids(true); QuicConnectionPeer::SetNegotiatedVersion(&connection_); @@ -1442,6 +1427,9 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { } connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); peer_creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); + // Discard INITIAL key. + connection_.RemoveEncrypter(ENCRYPTION_INITIAL); + connection_.NeuterUnencryptedPackets(); // Prevent packets from being coalesced. EXPECT_CALL(visitor_, GetHandshakeState()) .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); @@ -1753,6 +1741,9 @@ TEST_P(QuicConnectionTest, PeerIpAddressChangeAtServer) { QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + // Discard INITIAL key. + connection_.RemoveEncrypter(ENCRYPTION_INITIAL); + connection_.NeuterUnencryptedPackets(); // Prevent packets from being coalesced. EXPECT_CALL(visitor_, GetHandshakeState()) .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); @@ -1966,6 +1957,9 @@ TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) { QuicConnectionPeer::SetAddressValidated(&connection_); } connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + // Discard INITIAL key. + connection_.RemoveEncrypter(ENCRYPTION_INITIAL); + connection_.NeuterUnencryptedPackets(); EXPECT_CALL(visitor_, GetHandshakeState()) .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); @@ -2073,6 +2067,66 @@ TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) { } } +// Regression test for b/196208556. +TEST_P(QuicConnectionTest, + ReversePathValidationResponseReceivedFromUnexpectedPeerAddress) { + set_perspective(Perspective::IS_SERVER); + if (!connection_.connection_migration_use_new_cid()) { + return; + } + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + connection_.CreateConnectionIdManager(); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + QuicConnectionPeer::SetPeerAddress(&connection_, kPeerAddress); + QuicConnectionPeer::SetEffectivePeerAddress(&connection_, kPeerAddress); + QuicConnectionPeer::SetAddressValidated(&connection_); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + + // Sends new server CID to client. + QuicConnectionId new_cid; + EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_)) + .WillOnce(Invoke([&](const QuicConnectionId& cid) { new_cid = cid; })); + EXPECT_CALL(visitor_, SendNewConnectionId(_)); + // Discard INITIAL key. + connection_.RemoveEncrypter(ENCRYPTION_INITIAL); + connection_.NeuterUnencryptedPackets(); + connection_.OnHandshakeComplete(); + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); + + // Process a non-probing packet to migrate to path 2 and kick off reverse path + // validation. + EXPECT_CALL(visitor_, OnConnectionMigration(IPV6_TO_IPV4_CHANGE)).Times(1); + const QuicSocketAddress kPeerAddress2 = + QuicSocketAddress(QuicIpAddress::Loopback4(), /*port=*/23456); + peer_creator_.SetServerConnectionId(new_cid); + ProcessFramesPacketWithAddresses({QuicFrame(QuicPingFrame())}, kSelfAddress, + kPeerAddress2, ENCRYPTION_FORWARD_SECURE); + EXPECT_FALSE(writer_->path_challenge_frames().empty()); + QuicPathFrameBuffer reverse_path_challenge_payload = + writer_->path_challenge_frames().front().data_buffer; + + // Receiveds a packet from path 3 with PATH_RESPONSE frame intended to + // validate path 2 and a non-probing frame. + { + QuicConnection::ScopedPacketFlusher flusher(&connection_); + const QuicSocketAddress kPeerAddress3 = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/56789); + auto ack_frame = InitAckFrame(1); + EXPECT_CALL(visitor_, OnConnectionMigration(IPV4_TO_IPV6_CHANGE)).Times(1); + EXPECT_CALL(visitor_, MaybeSendAddressToken()).WillOnce(Invoke([this]() { + connection_.SendControlFrame( + QuicFrame(new QuicNewTokenFrame(1, "new_token"))); + })); + ProcessFramesPacketWithAddresses({QuicFrame(new QuicPathResponseFrame( + 0, reverse_path_challenge_payload)), + QuicFrame(&ack_frame)}, + kSelfAddress, kPeerAddress3, + ENCRYPTION_FORWARD_SECURE); + } +} + TEST_P(QuicConnectionTest, ReversePathValidationFailureAtServer) { set_perspective(Perspective::IS_SERVER); if (!connection_.connection_migration_use_new_cid()) { @@ -2083,6 +2137,9 @@ TEST_P(QuicConnectionTest, ReversePathValidationFailureAtServer) { SetClientConnectionId(TestConnectionId(1)); connection_.CreateConnectionIdManager(); connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + // Discard INITIAL key. + connection_.RemoveEncrypter(ENCRYPTION_INITIAL); + connection_.NeuterUnencryptedPackets(); // Prevent packets from being coalesced. EXPECT_CALL(visitor_, GetHandshakeState()) .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); @@ -2739,7 +2796,7 @@ TEST_P(QuicConnectionTest, PeerAddressChangeAtClient) { TEST_P(QuicConnectionTest, MaxPacketSize) { EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); - EXPECT_EQ(1350u, connection_.max_packet_length()); + EXPECT_EQ(1250u, connection_.max_packet_length()); } TEST_P(QuicConnectionTest, PeerLowersMaxPacketSize) { @@ -2776,6 +2833,18 @@ TEST_P(QuicConnectionTest, SmallerServerMaxPacketSize) { EXPECT_EQ(1000u, connection.max_packet_length()); } +TEST_P(QuicConnectionTest, LowerServerResponseMtuTest) { + set_perspective(Perspective::IS_SERVER); + connection_.SetMaxPacketLength(1000); + EXPECT_EQ(1000u, connection_.max_packet_length()); + + SetQuicFlag(FLAGS_quic_use_lower_server_response_mtu_for_test, true); + EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(::testing::AtMost(1)); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(::testing::AtMost(1)); + ProcessCryptoPacketAtLevel(1, ENCRYPTION_INITIAL); + EXPECT_EQ(1250u, connection_.max_packet_length()); +} + TEST_P(QuicConnectionTest, IncreaseServerMaxPacketSize) { set_perspective(Perspective::IS_SERVER); connection_.SetMaxPacketLength(1000); @@ -3706,7 +3775,7 @@ TEST_P(QuicConnectionTest, LargeSendWithPendingAck) { EXPECT_TRUE(connection_.HasPendingAcks()); // Send data and ensure the ack is bundled. - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(8); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(9); size_t len = 10000; std::unique_ptr<char[]> data_array(new char[len]); memset(data_array.get(), '?', len); @@ -6485,7 +6554,6 @@ TEST_P(QuicConnectionTest, SendDelayedAckDecimationUnlimitedAggregation) { EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); QuicConfig config; QuicTagVector connection_options; - connection_options.push_back(kACKD); // No limit on the number of packets received before sending an ack. connection_options.push_back(kAKDU); config.SetConnectionOptionsToSend(connection_options); @@ -8816,7 +8884,7 @@ TEST_P(QuicConnectionTest, GetCurrentLargestMessagePayload) { // that the encryption overhead is constant across versions. connection_.SetEncrypter(ENCRYPTION_INITIAL, std::make_unique<TaggingEncrypter>(0x00)); - QuicPacketLength expected_largest_payload = 1319; + QuicPacketLength expected_largest_payload = 1219; if (connection_.version().SendsVariableLengthPacketNumberInLongHeader()) { expected_largest_payload += 3; } @@ -8851,7 +8919,7 @@ TEST_P(QuicConnectionTest, GetGuaranteedLargestMessagePayload) { // that the encryption overhead is constant across versions. connection_.SetEncrypter(ENCRYPTION_INITIAL, std::make_unique<TaggingEncrypter>(0x00)); - QuicPacketLength expected_largest_payload = 1319; + QuicPacketLength expected_largest_payload = 1219; if (connection_.version().HasLongHeaderLengths()) { expected_largest_payload -= 2; } @@ -10026,12 +10094,13 @@ TEST_P(QuicConnectionTest, AntiAmplificationLimit) { EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); // Receives packet 1. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); ProcessCryptoPacketAtLevel(1, ENCRYPTION_INITIAL); const size_t anti_amplification_factor = GetQuicFlag(FLAGS_quic_anti_amplification_factor); // Verify now packets can be sent. - for (size_t i = 0; i < anti_amplification_factor; ++i) { + for (size_t i = 1; i < anti_amplification_factor; ++i) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); connection_.SendCryptoDataWithString("foo", i * 3); // Verify retransmission alarm is not set if throttled by anti-amplification @@ -10044,10 +10113,11 @@ TEST_P(QuicConnectionTest, AntiAmplificationLimit) { connection_.SendCryptoDataWithString("foo", anti_amplification_factor * 3); // Receives packet 2. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); ProcessCryptoPacketAtLevel(2, ENCRYPTION_INITIAL); // Verify more packets can be sent. - for (size_t i = anti_amplification_factor; i < anti_amplification_factor * 2; - ++i) { + for (size_t i = anti_amplification_factor + 1; + i < anti_amplification_factor * 2; ++i) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); connection_.SendCryptoDataWithString("foo", i * 3); } @@ -10056,6 +10126,7 @@ TEST_P(QuicConnectionTest, AntiAmplificationLimit) { connection_.SendCryptoDataWithString("foo", 2 * anti_amplification_factor * 3); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); ProcessPacket(3); // Verify anti-amplification limit is gone after address validation. for (size_t i = 0; i < 100; ++i) { @@ -10092,11 +10163,12 @@ TEST_P(QuicConnectionTest, 3AntiAmplificationLimit) { EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); // Receives packet 1. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); ProcessCryptoPacketAtLevel(1, ENCRYPTION_INITIAL); const size_t anti_amplification_factor = 3; // Verify now packets can be sent. - for (size_t i = 0; i < anti_amplification_factor; ++i) { + for (size_t i = 1; i < anti_amplification_factor; ++i) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); connection_.SendCryptoDataWithString("foo", i * 3); // Verify retransmission alarm is not set if throttled by anti-amplification @@ -10109,10 +10181,11 @@ TEST_P(QuicConnectionTest, 3AntiAmplificationLimit) { connection_.SendCryptoDataWithString("foo", anti_amplification_factor * 3); // Receives packet 2. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); ProcessCryptoPacketAtLevel(2, ENCRYPTION_INITIAL); // Verify more packets can be sent. - for (size_t i = anti_amplification_factor; i < anti_amplification_factor * 2; - ++i) { + for (size_t i = anti_amplification_factor + 1; + i < anti_amplification_factor * 2; ++i) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); connection_.SendCryptoDataWithString("foo", i * 3); } @@ -10121,6 +10194,7 @@ TEST_P(QuicConnectionTest, 3AntiAmplificationLimit) { connection_.SendCryptoDataWithString("foo", 2 * anti_amplification_factor * 3); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); ProcessPacket(3); // Verify anti-amplification limit is gone after address validation. for (size_t i = 0; i < 100; ++i) { @@ -10157,11 +10231,12 @@ TEST_P(QuicConnectionTest, 10AntiAmplificationLimit) { EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); // Receives packet 1. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); ProcessCryptoPacketAtLevel(1, ENCRYPTION_INITIAL); const size_t anti_amplification_factor = 10; // Verify now packets can be sent. - for (size_t i = 0; i < anti_amplification_factor; ++i) { + for (size_t i = 1; i < anti_amplification_factor; ++i) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); connection_.SendCryptoDataWithString("foo", i * 3); // Verify retransmission alarm is not set if throttled by anti-amplification @@ -10174,10 +10249,11 @@ TEST_P(QuicConnectionTest, 10AntiAmplificationLimit) { connection_.SendCryptoDataWithString("foo", anti_amplification_factor * 3); // Receives packet 2. + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); ProcessCryptoPacketAtLevel(2, ENCRYPTION_INITIAL); // Verify more packets can be sent. - for (size_t i = anti_amplification_factor; i < anti_amplification_factor * 2; - ++i) { + for (size_t i = anti_amplification_factor + 1; + i < anti_amplification_factor * 2; ++i) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); connection_.SendCryptoDataWithString("foo", i * 3); } @@ -10186,6 +10262,7 @@ TEST_P(QuicConnectionTest, 10AntiAmplificationLimit) { connection_.SendCryptoDataWithString("foo", 2 * anti_amplification_factor * 3); + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); ProcessPacket(3); // Verify anti-amplification limit is gone after address validation. for (size_t i = 0; i < 100; ++i) { @@ -10941,6 +11018,11 @@ TEST_P(QuicConnectionTest, DonotChangeQueuedAcks) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)); connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + // Discard INITIAL key. + connection_.RemoveEncrypter(ENCRYPTION_INITIAL); + connection_.NeuterUnencryptedPackets(); + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_COMPLETE)); ProcessPacket(2); ProcessPacket(3); @@ -10996,7 +11078,7 @@ TEST_P(QuicConnectionTest, BundleAckWithImmediateResponse) { connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); EXPECT_CALL(visitor_, OnStreamFrame(_)).WillOnce(Invoke([this]() { - connection_.SendControlFrame(QuicFrame(new QuicWindowUpdateFrame(1, 0, 0))); + notifier_.WriteOrBufferWindowUpate(0, 0); })); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); ProcessDataPacket(1); @@ -12104,13 +12186,9 @@ TEST_P(QuicConnectionTest, SendPathChallengeUsingBlockedNewSocket) { new_writer.SetWritable(); // Write event on the default socket shouldn't make any difference. connection_.OnCanWrite(); - if (GetQuicReloadableFlag(quic_ack_cid_frames)) { - // A NEW_CONNECTION_ID frame is received in PathProbeTestInit and OnCanWrite - // will write a acking packet. - EXPECT_EQ(1u, writer_->packets_write_attempts()); - } else { - EXPECT_EQ(0u, writer_->packets_write_attempts()); - } + // A NEW_CONNECTION_ID frame is received in PathProbeTestInit and OnCanWrite + // will write a acking packet. + EXPECT_EQ(1u, writer_->packets_write_attempts()); EXPECT_EQ(1u, new_writer.packets_write_attempts()); } @@ -12691,7 +12769,7 @@ TEST_P(QuicConnectionTest, CoalescerHandlesInitialKeyDiscard) { connection_.SetEncrypter(ENCRYPTION_HANDSHAKE, std::make_unique<TaggingEncrypter>(0x02)); connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE); - connection_.SendCryptoDataWithString(std::string(1300, 'a'), 0); + connection_.SendCryptoDataWithString(std::string(1200, 'a'), 0); // Verify this packet is on hold. EXPECT_EQ(0u, writer_->packets_write_attempts()); } @@ -12718,7 +12796,7 @@ TEST_P(QuicConnectionTest, ZeroRttRejectionAndMissingInitialKeys) { connection_.SetEncrypter(ENCRYPTION_HANDSHAKE, std::make_unique<TaggingEncrypter>(0x03)); connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE); - connection_.SendCryptoStreamData(); + connection_.SendCryptoDataWithString("foo", 0, ENCRYPTION_HANDSHAKE); connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, std::make_unique<TaggingEncrypter>(0x04)); connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); @@ -12733,7 +12811,7 @@ TEST_P(QuicConnectionTest, ZeroRttRejectionAndMissingInitialKeys) { use_tagging_decrypter(); connection_.SetEncrypter(ENCRYPTION_INITIAL, std::make_unique<TaggingEncrypter>(0x01)); - connection_.SendCryptoStreamData(); + connection_.SendCryptoDataWithString("foo", 0, ENCRYPTION_INITIAL); // Send 0-RTT packet. connection_.SetEncrypter(ENCRYPTION_ZERO_RTT, std::make_unique<TaggingEncrypter>(0x02)); @@ -13702,6 +13780,8 @@ TEST_P(QuicConnectionTest, SingleAckInPacket) { connection_.RemoveEncrypter(ENCRYPTION_INITIAL); connection_.NeuterUnencryptedPackets(); connection_.OnHandshakeComplete(); + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_COMPLETE)); EXPECT_CALL(visitor_, OnStreamFrame(_)).WillOnce(Invoke([=]() { connection_.SendStreamData3(); @@ -14102,6 +14182,8 @@ TEST_P(QuicConnectionTest, PathChallengeBeforePeerIpAddressChangeAtServer) { // Verify the anti-amplification limit is lifted by sending a packet larger // than the anti-amplification limit. EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); + EXPECT_CALL(*send_algorithm_, PacingRate(_)) + .WillRepeatedly(Return(QuicBandwidth::Zero())); connection_.SendCryptoDataWithString(std::string(1200, 'a'), 0); EXPECT_EQ(1u, connection_.GetStats().num_validated_peer_migration); } @@ -14287,10 +14369,13 @@ TEST_P(QuicConnectionTest, TEST_P(QuicConnectionTest, PathValidationFailedOnClientDueToLackOfServerConnectionId) { - QuicConfig config; - config.SetConnectionOptionsToSend({kRVCM}); - EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); - connection_.SetFromConfig(config); + if (!GetQuicReloadableFlag( + quic_remove_connection_migration_connection_option)) { + QuicConfig config; + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + config.SetConnectionOptionsToSend({kRVCM}); + } if (!connection_.connection_migration_use_new_cid()) { return; } @@ -14312,10 +14397,13 @@ TEST_P(QuicConnectionTest, TEST_P(QuicConnectionTest, PathValidationFailedOnClientDueToLackOfClientConnectionIdTheSecondTime) { - QuicConfig config; - config.SetConnectionOptionsToSend({kRVCM}); - EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); - connection_.SetFromConfig(config); + if (!GetQuicReloadableFlag( + quic_remove_connection_migration_connection_option)) { + QuicConfig config; + config.SetConnectionOptionsToSend({kRVCM}); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + } if (!connection_.connection_migration_use_new_cid()) { return; } @@ -14401,10 +14489,13 @@ TEST_P(QuicConnectionTest, } TEST_P(QuicConnectionTest, ServerConnectionIdRetiredUponPathValidationFailure) { - QuicConfig config; - config.SetConnectionOptionsToSend({kRVCM}); - EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); - connection_.SetFromConfig(config); + if (!GetQuicReloadableFlag( + quic_remove_connection_migration_connection_option)) { + QuicConfig config; + config.SetConnectionOptionsToSend({kRVCM}); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + } if (!connection_.connection_migration_use_new_cid()) { return; } @@ -14448,10 +14539,13 @@ TEST_P(QuicConnectionTest, ServerConnectionIdRetiredUponPathValidationFailure) { TEST_P(QuicConnectionTest, MigratePathDirectlyFailedDueToLackOfServerConnectionId) { - QuicConfig config; - config.SetConnectionOptionsToSend({kRVCM}); - EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); - connection_.SetFromConfig(config); + if (!GetQuicReloadableFlag( + quic_remove_connection_migration_connection_option)) { + QuicConfig config; + config.SetConnectionOptionsToSend({kRVCM}); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + } if (!connection_.connection_migration_use_new_cid()) { return; } @@ -14468,10 +14562,13 @@ TEST_P(QuicConnectionTest, TEST_P(QuicConnectionTest, MigratePathDirectlyFailedDueToLackOfClientConnectionIdTheSecondTime) { - QuicConfig config; - config.SetConnectionOptionsToSend({kRVCM}); - EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); - connection_.SetFromConfig(config); + if (!GetQuicReloadableFlag( + quic_remove_connection_migration_connection_option)) { + QuicConfig config; + config.SetConnectionOptionsToSend({kRVCM}); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + } if (!connection_.connection_migration_use_new_cid()) { return; } @@ -14793,11 +14890,16 @@ TEST_P(QuicConnectionTest, } TEST_P(QuicConnectionTest, ServerRetireSelfIssuedConnectionId) { - if (!version().HasIetfQuicFrames() || - !connection_.connection_migration_use_new_cid()) { + if (!GetQuicReloadableFlag( + quic_remove_connection_migration_connection_option)) { + QuicConfig config; + config.SetConnectionOptionsToSend({kRVCM}); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + } + if (!connection_.connection_migration_use_new_cid()) { return; } - QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_); set_perspective(Perspective::IS_SERVER); connection_.CreateConnectionIdManager(); QuicConnectionId recorded_cid; @@ -14807,7 +14909,10 @@ TEST_P(QuicConnectionTest, ServerRetireSelfIssuedConnectionId) { QuicConnectionId cid0 = connection_id_; QuicConnectionId cid1; QuicConnectionId cid2; + EXPECT_EQ(connection_.connection_id(), cid0); + EXPECT_EQ(connection_.GetOneActiveServerConnectionId(), cid0); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_)) .WillOnce(Invoke(cid_recorder)); EXPECT_CALL(visitor_, SendNewConnectionId(_)); @@ -14818,23 +14923,63 @@ TEST_P(QuicConnectionTest, ServerRetireSelfIssuedConnectionId) { connection_.GetRetireSelfIssuedConnectionIdAlarm(); ASSERT_FALSE(retire_self_issued_cid_alarm->IsSet()); - QuicRetireConnectionIdFrame frame; - frame.sequence_number = 0u; + // Generate three packets with different connection IDs that will arrive out + // of order (2, 1, 3) later. + char buffers[3][kMaxOutgoingPacketSize]; + // Destination connection ID of packet1 is cid0. + auto packet1 = + ConstructPacket({QuicFrame(QuicPingFrame())}, ENCRYPTION_FORWARD_SECURE, + buffers[0], kMaxOutgoingPacketSize); + peer_creator_.SetServerConnectionId(cid1); + auto retire_cid_frame = std::make_unique<QuicRetireConnectionIdFrame>(); + retire_cid_frame->sequence_number = 0u; + // Destination connection ID of packet2 is cid1. + auto packet2 = ConstructPacket({QuicFrame(retire_cid_frame.release())}, + ENCRYPTION_FORWARD_SECURE, buffers[1], + kMaxOutgoingPacketSize); + // Destination connection ID of packet3 is cid1. + auto packet3 = + ConstructPacket({QuicFrame(QuicPingFrame())}, ENCRYPTION_FORWARD_SECURE, + buffers[2], kMaxOutgoingPacketSize); + + // Packet2 with RetireConnectionId frame trigers sending NewConnectionId + // immediately. EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_)) .WillOnce(Invoke(cid_recorder)); - // RetireConnectionId trigers sending NewConnectionId immediately. EXPECT_CALL(visitor_, SendNewConnectionId(_)); - EXPECT_TRUE(connection_.OnRetireConnectionIdFrame(frame)); + peer_creator_.SetServerConnectionId(cid1); + connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *packet2); cid2 = recorded_cid; // cid0 is not retired immediately. EXPECT_THAT(connection_.GetActiveServerConnectionIds(), ElementsAre(cid0, cid1, cid2)); ASSERT_TRUE(retire_self_issued_cid_alarm->IsSet()); + EXPECT_EQ(connection_.connection_id(), cid1); + EXPECT_TRUE(connection_.GetOneActiveServerConnectionId() == cid0 || + connection_.GetOneActiveServerConnectionId() == cid1 || + connection_.GetOneActiveServerConnectionId() == cid2); + + // Packet1 updates the connection ID on the default path but not the active + // connection ID. + connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *packet1); + EXPECT_EQ(connection_.connection_id(), cid0); + EXPECT_TRUE(connection_.GetOneActiveServerConnectionId() == cid0 || + connection_.GetOneActiveServerConnectionId() == cid1 || + connection_.GetOneActiveServerConnectionId() == cid2); + // cid0 is retired when the retire CID alarm fires. EXPECT_CALL(visitor_, OnServerConnectionIdRetired(cid0)); retire_self_issued_cid_alarm->Fire(); EXPECT_THAT(connection_.GetActiveServerConnectionIds(), ElementsAre(cid1, cid2)); + EXPECT_TRUE(connection_.GetOneActiveServerConnectionId() == cid1 || + connection_.GetOneActiveServerConnectionId() == cid2); + + // Packet3 updates the connection ID on the default path. + connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *packet3); + EXPECT_EQ(connection_.connection_id(), cid1); + EXPECT_TRUE(connection_.GetOneActiveServerConnectionId() == cid1 || + connection_.GetOneActiveServerConnectionId() == cid2); } TEST_P(QuicConnectionTest, PatchMissingClientConnectionIdOntoAlternativePath) { @@ -14962,6 +15107,9 @@ TEST_P(QuicConnectionTest, LostDataThenGetAcknowledged) { QuicConnectionPeer::SetAddressValidated(&connection_); } connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + // Discard INITIAL key. + connection_.RemoveEncrypter(ENCRYPTION_INITIAL); + connection_.NeuterUnencryptedPackets(); EXPECT_CALL(visitor_, GetHandshakeState()) .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); @@ -15202,13 +15350,15 @@ TEST_P(QuicConnectionTest, PingNotSentAt0RTTLevelWhenInitialAvailable) { } TEST_P(QuicConnectionTest, AckElicitingFrames) { - QuicConfig config; - config.SetConnectionOptionsToSend({kRVCM}); - EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); - connection_.SetFromConfig(config); + if (!GetQuicReloadableFlag( + quic_remove_connection_migration_connection_option)) { + QuicConfig config; + config.SetConnectionOptionsToSend({kRVCM}); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + connection_.SetFromConfig(config); + } if (!version().HasIetfQuicFrames() || !connection_.connection_migration_use_new_cid() || - !GetQuicReloadableFlag(quic_ack_cid_frames) || !GetQuicReloadableFlag(quic_add_missing_update_ack_timeout)) { return; } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_constants.h b/chromium/net/third_party/quiche/src/quic/core/quic_constants.h index 162de8909bd..c53459cf70d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_constants.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_constants.h @@ -28,7 +28,7 @@ const uint64_t kNumMicrosPerSecond = kNumMicrosPerMilli * kNumMillisPerSecond; // Default number of connections for N-connection emulation. const uint32_t kDefaultNumConnections = 2; // Default initial maximum size in bytes of a QUIC packet. -const QuicByteCount kDefaultMaxPacketSize = 1350; +const QuicByteCount kDefaultMaxPacketSize = 1250; // Default initial maximum size in bytes of a QUIC packet for servers. const QuicByteCount kDefaultServerMaxPacketSize = 1000; // Maximum transmission unit on Ethernet. @@ -301,6 +301,10 @@ enum : QuicDatagramContextId { kDatagramContextIdIncrement = 2, }; +enum : uint64_t { + kHttpDatagramStreamIdDivisor = 4, +}; + } // namespace quic #endif // QUICHE_QUIC_CORE_QUIC_CONSTANTS_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.cc index 42862494745..39e768c9ed5 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.cc @@ -59,8 +59,7 @@ void QuicControlFrameManager::WriteOrBufferQuicFrame(QuicFrame frame) { } void QuicControlFrameManager::WriteOrBufferRstStream( - QuicStreamId id, - QuicRstStreamErrorCode error, + QuicStreamId id, QuicResetStreamError error, QuicStreamOffset bytes_written) { QUIC_DVLOG(1) << "Writing RST_STREAM_FRAME"; WriteOrBufferQuicFrame((QuicFrame(new QuicRstStreamFrame( @@ -68,8 +67,7 @@ void QuicControlFrameManager::WriteOrBufferRstStream( } void QuicControlFrameManager::WriteOrBufferGoAway( - QuicErrorCode error, - QuicStreamId last_good_stream_id, + QuicErrorCode error, QuicStreamId last_good_stream_id, const std::string& reason) { QUIC_DVLOG(1) << "Writing GOAWAY_FRAME"; WriteOrBufferQuicFrame(QuicFrame(new QuicGoAwayFrame( @@ -77,8 +75,7 @@ void QuicControlFrameManager::WriteOrBufferGoAway( } void QuicControlFrameManager::WriteOrBufferWindowUpdate( - QuicStreamId id, - QuicStreamOffset byte_offset) { + QuicStreamId id, QuicStreamOffset byte_offset) { QUIC_DVLOG(1) << "Writing WINDOW_UPDATE_FRAME"; WriteOrBufferQuicFrame(QuicFrame( new QuicWindowUpdateFrame(++last_control_frame_id_, id, byte_offset))); @@ -107,11 +104,10 @@ void QuicControlFrameManager::WriteOrBufferMaxStreams(QuicStreamCount count, } void QuicControlFrameManager::WriteOrBufferStopSending( - QuicRstStreamErrorCode code, - QuicStreamId stream_id) { + QuicResetStreamError error, QuicStreamId stream_id) { QUIC_DVLOG(1) << "Writing STOP_SENDING_FRAME"; WriteOrBufferQuicFrame(QuicFrame( - new QuicStopSendingFrame(++last_control_frame_id_, stream_id, code))); + new QuicStopSendingFrame(++last_control_frame_id_, stream_id, error))); } void QuicControlFrameManager::WriteOrBufferHandshakeDone() { @@ -134,8 +130,7 @@ void QuicControlFrameManager::WriteOrBufferAckFrequency( } void QuicControlFrameManager::WriteOrBufferNewConnectionId( - const QuicConnectionId& connection_id, - uint64_t sequence_number, + const QuicConnectionId& connection_id, uint64_t sequence_number, uint64_t retire_prior_to, const StatelessResetToken& stateless_reset_token) { QUIC_DVLOG(1) << "Writing NEW_CONNECTION_ID frame"; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h index 962733a147d..e83fc3b1760 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h @@ -11,6 +11,7 @@ #include "absl/container/flat_hash_map.h" #include "quic/core/frames/quic_frame.h" #include "quic/core/quic_connection_id.h" +#include "quic/core/quic_error_codes.h" #include "quic/core/quic_types.h" #include "common/quiche_circular_deque.h" #include "common/quiche_linked_hash_map.h" @@ -53,8 +54,7 @@ class QUIC_EXPORT_PRIVATE QuicControlFrameManager { // Tries to send a WINDOW_UPDATE_FRAME. Buffers the frame if it cannot be sent // immediately. - void WriteOrBufferRstStream(QuicControlFrameId id, - QuicRstStreamErrorCode error, + void WriteOrBufferRstStream(QuicControlFrameId id, QuicResetStreamError error, QuicStreamOffset bytes_written); // Tries to send a GOAWAY_FRAME. Buffers the frame if it cannot be sent @@ -81,7 +81,7 @@ class QUIC_EXPORT_PRIVATE QuicControlFrameManager { // Tries to send an IETF-QUIC STOP_SENDING frame. The frame is buffered if it // can not be sent immediately. - void WriteOrBufferStopSending(QuicRstStreamErrorCode code, + void WriteOrBufferStopSending(QuicResetStreamError error, QuicStreamId stream_id); // Tries to send an HANDSHAKE_DONE frame. The frame is buffered if it can not @@ -96,8 +96,7 @@ class QUIC_EXPORT_PRIVATE QuicControlFrameManager { // Tries to send a NEW_CONNECTION_ID frame. The frame is buffered if it cannot // be sent immediately. void WriteOrBufferNewConnectionId( - const QuicConnectionId& connection_id, - uint64_t sequence_number, + const QuicConnectionId& connection_id, uint64_t sequence_number, uint64_t retire_prior_to, const StatelessResetToken& stateless_reset_token); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager_test.cc index 9d6b434ef31..6d208111a53 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager_test.cc @@ -66,12 +66,16 @@ class QuicControlFrameManagerTest : public QuicTest { EXPECT_FALSE(manager_->WillingToWrite()); EXPECT_CALL(*session_, WriteControlFrame(_, _)).WillOnce(Return(false)); - manager_->WriteOrBufferRstStream(kTestStreamId, QUIC_STREAM_CANCELLED, 0); + manager_->WriteOrBufferRstStream( + kTestStreamId, + QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED), 0); manager_->WriteOrBufferGoAway(QUIC_PEER_GOING_AWAY, kTestStreamId, "Going away."); manager_->WriteOrBufferWindowUpdate(kTestStreamId, 100); manager_->WriteOrBufferBlocked(kTestStreamId); - manager_->WriteOrBufferStopSending(kTestStopSendingCode, kTestStreamId); + manager_->WriteOrBufferStopSending( + QuicResetStreamError::FromInternal(kTestStopSendingCode), + kTestStreamId); number_of_frames_ = 5u; EXPECT_EQ(number_of_frames_, QuicControlFrameManagerPeer::QueueSize(manager_.get())); @@ -341,14 +345,18 @@ TEST_F(QuicControlFrameManagerTest, TooManyBufferedControlFrames) { // Write 995 control frames. EXPECT_CALL(*session_, WriteControlFrame(_, _)).WillOnce(Return(false)); for (size_t i = 0; i < 995; ++i) { - manager_->WriteOrBufferRstStream(kTestStreamId, QUIC_STREAM_CANCELLED, 0); + manager_->WriteOrBufferRstStream( + kTestStreamId, + QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED), 0); } // Verify write one more control frame causes connection close. EXPECT_CALL( *connection_, CloseConnection(QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES, _, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); - manager_->WriteOrBufferRstStream(kTestStreamId, QUIC_STREAM_CANCELLED, 0); + manager_->WriteOrBufferRstStream( + kTestStreamId, QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED), + 0); } } // namespace 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 e92606669de..d9f10350553 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 @@ -65,6 +65,13 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientHandshaker std::unique_ptr<ApplicationState> /*application_state*/) override { QUICHE_NOTREACHED(); } + bool ExportKeyingMaterial(absl::string_view /*label*/, + absl::string_view /*context*/, + size_t /*result_len*/, + std::string* /*result*/) override { + QUICHE_NOTREACHED(); + return false; + } // From QuicCryptoHandshaker void OnHandshakeMessage(const CryptoHandshakeMessage& message) override; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker_test.cc index ed5da6cb544..120e5dae233 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker_test.cc @@ -77,18 +77,20 @@ class DummyProofSource : public ProofSource { QuicTransportVersion /*transport_version*/, absl::string_view /*chlo_hash*/, std::unique_ptr<Callback> callback) override { - QuicReferenceCountedPointer<ProofSource::Chain> chain = - GetCertChain(server_address, client_address, hostname); + bool cert_matched_sni; + QuicReferenceCountedPointer<ProofSource::Chain> chain = GetCertChain( + server_address, client_address, hostname, &cert_matched_sni); QuicCryptoProof proof; proof.signature = "Dummy signature"; proof.leaf_cert_scts = "Dummy timestamp"; + proof.cert_matched_sni = cert_matched_sni; callback->Run(true, chain, proof, /*details=*/nullptr); } QuicReferenceCountedPointer<Chain> GetCertChain( const QuicSocketAddress& /*server_address*/, const QuicSocketAddress& /*client_address*/, - const std::string& /*hostname*/) override { + const std::string& /*hostname*/, bool* /*cert_matched_sni*/) override { std::vector<std::string> certs; certs.push_back("Dummy cert"); return QuicReferenceCountedPointer<ProofSource::Chain>( 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 78d2f10a563..fe6471ff2bc 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 @@ -43,11 +43,14 @@ QuicCryptoClientStream::QuicCryptoClientStream( server_id, this, session, std::move(verify_context), crypto_config, proof_handler); break; - case PROTOCOL_TLS1_3: - handshaker_ = std::make_unique<TlsClientHandshaker>( + case PROTOCOL_TLS1_3: { + auto handshaker = std::make_unique<TlsClientHandshaker>( server_id, this, session, std::move(verify_context), crypto_config, proof_handler, has_application_state); + tls_handshaker_ = handshaker.get(); + handshaker_ = std::move(handshaker); break; + } case PROTOCOL_UNSUPPORTED: QUIC_BUG(quic_bug_10296_1) << "Attempting to create QuicCryptoClientStream for unknown " @@ -125,6 +128,13 @@ QuicCryptoClientStream::CreateCurrentOneRttEncrypter() { return handshaker_->CreateCurrentOneRttEncrypter(); } +bool QuicCryptoClientStream::ExportKeyingMaterial(absl::string_view label, + absl::string_view context, + size_t result_len, + std::string* result) { + return handshaker_->ExportKeyingMaterial(label, context, result_len, result); +} + std::string QuicCryptoClientStream::chlo_hash() const { return handshaker_->chlo_hash(); } @@ -167,4 +177,8 @@ void QuicCryptoClientStream::SetServerApplicationStateForResumption( std::move(application_state)); } +SSL* QuicCryptoClientStream::GetSsl() const { + return tls_handshaker_ == nullptr ? nullptr : tls_handshaker_->ssl(); +} + } // 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 b3d3c2b2bc3..daea001302e 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 @@ -25,6 +25,8 @@ namespace test { class QuicCryptoClientStreamPeer; } // namespace test +class TlsClientHandshaker; + class QUIC_EXPORT_PRIVATE QuicCryptoClientStreamBase : public QuicCryptoStream { public: explicit QuicCryptoClientStreamBase(QuicSession* session); @@ -63,6 +65,14 @@ 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; + + bool ExportKeyingMaterial(absl::string_view /*label*/, + absl::string_view /*context*/, + size_t /*result_len*/, + std::string* /*result*/) override { + QUICHE_NOTREACHED(); + return false; + } }; class QUIC_EXPORT_PRIVATE QuicCryptoClientStream @@ -185,6 +195,13 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientStream // Called when application state is received. virtual void SetServerApplicationStateForResumption( std::unique_ptr<ApplicationState> application_state) = 0; + + // Called to obtain keying material export of length |result_len| with the + // given |label| and |context|. Returns false on failure. + virtual bool ExportKeyingMaterial(absl::string_view label, + absl::string_view context, + size_t result_len, + std::string* result) = 0; }; // ProofHandler is an interface that handles callbacks from the crypto @@ -250,7 +267,9 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientStream std::unique_ptr<QuicDecrypter> AdvanceKeysAndCreateCurrentOneRttDecrypter() override; std::unique_ptr<QuicEncrypter> CreateCurrentOneRttEncrypter() override; - + SSL* GetSsl() const override; + bool ExportKeyingMaterial(absl::string_view label, absl::string_view context, + size_t result_len, std::string* result) override; std::string chlo_hash() const; protected: @@ -261,6 +280,10 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientStream private: friend class test::QuicCryptoClientStreamPeer; std::unique_ptr<HandshakerInterface> handshaker_; + // Points to |handshaker_| if it uses TLS1.3. Otherwise, nullptr. + // TODO(danzh) change the type of |handshaker_| to TlsClientHandshaker after + // deprecating Google QUIC. + TlsClientHandshaker* tls_handshaker_{nullptr}; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc index 9bd9c7bc2b9..de6cb75cfb6 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 @@ -352,6 +352,10 @@ bool QuicCryptoServerStream::ShouldSendExpectCTHeader() const { return signed_config_->proof.send_expect_ct_header; } +bool QuicCryptoServerStream::DidCertMatchSni() const { + return signed_config_->proof.cert_matched_sni; +} + const ProofSource::Details* QuicCryptoServerStream::ProofSourceDetails() const { return proof_source_details_.get(); } @@ -507,4 +511,6 @@ const QuicSocketAddress QuicCryptoServerStream::GetClientAddress() { return session()->connection()->peer_address(); } +SSL* QuicCryptoServerStream::GetSsl() const { return nullptr; } + } // namespace quic 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 911ca7e789f..bf6f3e53e72 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 @@ -51,6 +51,7 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerStream std::string GetAddressToken() const override; bool ValidateAddressToken(absl::string_view token) const override; bool ShouldSendExpectCTHeader() const override; + bool DidCertMatchSni() const override; const ProofSource::Details* ProofSourceDetails() const override; // From QuicCryptoStream @@ -68,6 +69,7 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerStream std::unique_ptr<QuicDecrypter> AdvanceKeysAndCreateCurrentOneRttDecrypter() override; std::unique_ptr<QuicEncrypter> CreateCurrentOneRttEncrypter() override; + SSL* GetSsl() const override; // From QuicCryptoHandshaker void OnHandshakeMessage(const CryptoHandshakeMessage& message) override; 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 3ff9c37fa1e..967d6d11fb8 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 @@ -85,11 +85,22 @@ class QUIC_EXPORT_PRIVATE QuicCryptoServerStreamBase : public QuicCryptoStream { // configuration for the certificate used in the connection is accessible. virtual bool ShouldSendExpectCTHeader() const = 0; + // Return true if a cert was picked that matched the SNI hostname. + virtual bool DidCertMatchSni() const = 0; + // Returns the Details from the latest call to ProofSource::GetProof or // ProofSource::ComputeTlsSignature. Returns nullptr if no such call has been // made. The Details are owned by the QuicCryptoServerStreamBase and the // pointer is only valid while the owning object is still valid. virtual const ProofSource::Details* ProofSourceDetails() const = 0; + + bool ExportKeyingMaterial(absl::string_view /*label*/, + absl::string_view /*context*/, + size_t /*result_len*/, + std::string* /*result*/) override { + QUICHE_NOTREACHED(); + return false; + } }; // Creates an appropriate QuicCryptoServerStream for the provided parameters, 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 a14cef07f27..99fb2cd22e1 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 @@ -127,20 +127,6 @@ void QuicCryptoStream::OnDataAvailableInSequencer( } } -bool QuicCryptoStream::ExportKeyingMaterial(absl::string_view label, - absl::string_view context, - size_t result_len, - std::string* result) const { - if (!one_rtt_keys_available()) { - QUIC_DLOG(ERROR) << "ExportKeyingMaterial was called before forward-secure" - << "encryption was established."; - return false; - } - return CryptoUtils::ExportKeyingMaterial( - crypto_negotiated_params().subkey_secret, label, context, result_len, - result); -} - void QuicCryptoStream::WriteCryptoData(EncryptionLevel level, absl::string_view data) { if (!QuicVersionUsesCryptoFrames(session()->transport_version())) { 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 2a86a723d3c..b42b2d9e525 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 @@ -63,11 +63,12 @@ class QUIC_EXPORT_PRIVATE QuicCryptoStream : public QuicStream { // Performs key extraction to derive a new secret of |result_len| bytes // dependent on |label|, |context|, and the stream's negotiated subkey secret. // Returns false if the handshake has not been confirmed or the parameters are - // invalid (e.g. |label| contains null bytes); returns true on success. - bool ExportKeyingMaterial(absl::string_view label, - absl::string_view context, - size_t result_len, - std::string* result) const; + // invalid (e.g. |label| contains null bytes); returns true on success. This + // method is only supported for IETF QUIC and MUST NOT be called in gQUIC as + // that'll trigger an assert in DEBUG build. + virtual bool ExportKeyingMaterial(absl::string_view label, + absl::string_view context, + size_t result_len, std::string* result) = 0; // Writes |data| to the QuicStream at level |level|. virtual void WriteCryptoData(EncryptionLevel level, absl::string_view data); @@ -147,6 +148,11 @@ class QUIC_EXPORT_PRIVATE QuicCryptoStream : public QuicStream { // decrypter returned by AdvanceKeysAndCreateCurrentOneRttDecrypter(). virtual std::unique_ptr<QuicEncrypter> CreateCurrentOneRttEncrypter() = 0; + // Return the SSL struct object created by BoringSSL if the stream is using + // TLS1.3. Otherwise, return nullptr. + // This method is used in Envoy. + virtual SSL* GetSsl() const = 0; + // Called to cancel retransmission of unencrypted crypto stream data. void NeuterUnencryptedStreamData(); 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 545fdd8180f..e1bbf3599a7 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 @@ -79,6 +79,13 @@ class MockQuicCryptoStream : public QuicCryptoStream, std::unique_ptr<QuicEncrypter> CreateCurrentOneRttEncrypter() override { return nullptr; } + bool ExportKeyingMaterial(absl::string_view /*label*/, + absl::string_view /*context*/, + size_t /*result_len*/, + std::string* /*result*/) override { + return false; + } + SSL* GetSsl() const override { return nullptr; } private: QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue.cc b/chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue.cc index e6f3e6f427a..ebd74836597 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_datagram_queue.cc @@ -9,7 +9,6 @@ #include "quic/core/quic_session.h" #include "quic/core/quic_time.h" #include "quic/core/quic_types.h" -#include "quic/platform/api/quic_mem_slice_span.h" namespace quic { 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 11f4bcd4b3d..ad172810bbf 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 @@ -40,7 +40,7 @@ namespace { const QuicPacketLength kMinClientInitialPacketLength = 1200; // An alarm that informs the QuicDispatcher to delete old sessions. -class DeleteSessionsAlarm : public QuicAlarm::Delegate { +class DeleteSessionsAlarm : public QuicAlarm::DelegateWithoutContext { public: explicit DeleteSessionsAlarm(QuicDispatcher* dispatcher) : dispatcher_(dispatcher) {} @@ -54,6 +54,24 @@ class DeleteSessionsAlarm : public QuicAlarm::Delegate { QuicDispatcher* dispatcher_; }; +// An alarm that informs the QuicDispatcher to clear +// recent_stateless_reset_addresses_. +class ClearStatelessResetAddressesAlarm + : public QuicAlarm::DelegateWithoutContext { + public: + explicit ClearStatelessResetAddressesAlarm(QuicDispatcher* dispatcher) + : dispatcher_(dispatcher) {} + ClearStatelessResetAddressesAlarm(const DeleteSessionsAlarm&) = delete; + ClearStatelessResetAddressesAlarm& operator=(const DeleteSessionsAlarm&) = + delete; + + void OnAlarm() override { dispatcher_->ClearStatelessResetAddresses(); } + + private: + // Not owned. + QuicDispatcher* dispatcher_; +}; + // Collects packets serialized by a QuicPacketCreator in order // to be handed off to the time wait list manager. class PacketCollector : public QuicPacketCreator::DelegateInterface, @@ -159,7 +177,6 @@ class StatelessConnectionTerminator { SerializeConnectionClosePacket(error_code, error_details); time_wait_list_manager_->AddConnectionIdToTimeWait( - server_connection_id_, QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, TimeWaitConnectionInfo(ietf_quic, collector_.packets(), std::move(active_connection_ids), @@ -311,8 +328,7 @@ bool MaybeHandleLegacyVersionEncapsulation( } // namespace QuicDispatcher::QuicDispatcher( - const QuicConfig* config, - const QuicCryptoServerConfig* crypto_config, + const QuicConfig* config, const QuicCryptoServerConfig* crypto_config, QuicVersionManager* version_manager, std::unique_ptr<QuicConnectionHelperInterface> helper, std::unique_ptr<QuicCryptoServerStreamBase::Helper> session_helper, @@ -335,19 +351,22 @@ QuicDispatcher::QuicDispatcher( allow_short_initial_server_connection_ids_(false), expected_server_connection_id_length_( expected_server_connection_id_length), + clear_stateless_reset_addresses_alarm_(alarm_factory_->CreateAlarm( + new ClearStatelessResetAddressesAlarm(this))), should_update_expected_server_connection_id_length_(false) { QUIC_BUG_IF(quic_bug_12724_1, GetSupportedVersions().empty()) << "Trying to create dispatcher without any supported versions"; QUIC_DLOG(INFO) << "Created QuicDispatcher with versions: " << ParsedQuicVersionVectorToString(GetSupportedVersions()); - QUIC_RESTART_FLAG_COUNT(quic_alarm_add_permanent_cancel); } QuicDispatcher::~QuicDispatcher() { - if (GetQuicRestartFlag(quic_alarm_add_permanent_cancel) && - delete_sessions_alarm_ != nullptr) { + if (delete_sessions_alarm_ != nullptr) { delete_sessions_alarm_->PermanentCancel(); } + if (clear_stateless_reset_addresses_alarm_ != nullptr) { + clear_stateless_reset_addresses_alarm_->PermanentCancel(); + } reference_counted_session_map_.clear(); closed_ref_counted_session_list_.clear(); if (support_multiple_cid_per_connection_) { @@ -480,12 +499,54 @@ QuicConnectionId QuicDispatcher::ReplaceLongServerConnectionId( server_connection_id, expected_server_connection_id_length); } +namespace { +inline bool IsSourceUdpPortBlocked(uint16_t port) { + // TODO(dschinazi) make this function constexpr when we remove flag + // protection. + if (!GetQuicReloadableFlag(quic_blocked_ports)) { + return port == 0; + } + QUIC_RELOADABLE_FLAG_COUNT(quic_blocked_ports); + // These UDP source ports have been observed in large scale denial of service + // attacks and are not expected to ever carry user traffic, they are therefore + // blocked as a safety measure. See draft-ietf-quic-applicability for details. + constexpr uint16_t blocked_ports[] = { + 0, // We cannot send to port 0 so drop that source port. + 17, // Quote of the Day, can loop with QUIC. + 19, // Chargen, can loop with QUIC. + 53, // DNS, vulnerable to reflection attacks. + 111, // Portmap. + 123, // NTP, vulnerable to reflection attacks. + 137, // NETBIOS Name Service, + 128, // NETBIOS Datagram Service + 161, // SNMP. + 389, // CLDAP. + 500, // IKE, can loop with QUIC. + 1900, // SSDP, vulnerable to reflection attacks. + 5353, // mDNS, vulnerable to reflection attacks. + 11211, // memcache, vulnerable to reflection attacks. + // This list MUST be sorted in increasing order. + }; + constexpr size_t num_blocked_ports = ABSL_ARRAYSIZE(blocked_ports); + constexpr uint16_t highest_blocked_port = + blocked_ports[num_blocked_ports - 1]; + if (QUICHE_PREDICT_TRUE(port > highest_blocked_port)) { + // Early-return to skip comparisons for the majority of traffic. + return false; + } + for (size_t i = 0; i < num_blocked_ports; i++) { + if (port == blocked_ports[i]) { + return true; + } + } + return false; +} +} // namespace + bool QuicDispatcher::MaybeDispatchPacket( const ReceivedPacketInfo& packet_info) { - // Port zero is only allowed for unidirectional UDP, so is disallowed by QUIC. - // Given that we can't even send a reply rejecting the packet, just drop the - // packet. - if (packet_info.peer_address.port() == 0) { + if (IsSourceUdpPortBlocked(packet_info.peer_address.port())) { + // Silently drop the received packet. return true; } @@ -512,19 +573,16 @@ bool QuicDispatcher::MaybeDispatchPacket( return true; } - if (GetQuicReloadableFlag(quic_discard_packets_with_invalid_cid)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_discard_packets_with_invalid_cid); - if (packet_info.version_flag && packet_info.version.IsKnown() && - !QuicUtils::IsConnectionIdLengthValidForVersion( - server_connection_id.length(), - packet_info.version.transport_version)) { - QUIC_DLOG(INFO) << "Packet with destination connection ID " - << server_connection_id << " is invalid with version " - << packet_info.version; - // Drop the packet silently. - QUIC_CODE_COUNT(quic_dropped_invalid_initial_connection_id); - return true; - } + if (packet_info.version_flag && packet_info.version.IsKnown() && + !QuicUtils::IsConnectionIdLengthValidForVersion( + server_connection_id.length(), + packet_info.version.transport_version)) { + QUIC_DLOG(INFO) << "Packet with destination connection ID " + << server_connection_id << " is invalid with version " + << packet_info.version; + // Drop the packet silently. + QUIC_CODE_COUNT(quic_dropped_invalid_initial_connection_id); + return true; } // Packets with connection IDs for active connections are processed @@ -879,7 +937,7 @@ void QuicDispatcher::CleanUpSession(QuicConnectionId server_connection_id, QUIC_CODE_COUNT(quic_v44_add_to_time_wait_list_with_stateless_reset); } time_wait_list_manager_->AddConnectionIdToTimeWait( - server_connection_id, action, + action, TimeWaitConnectionInfo( connection->version().HasIetfInvariantHeader(), connection->termination_packets(), @@ -944,6 +1002,10 @@ void QuicDispatcher::DeleteSessions() { closed_ref_counted_session_list_.clear(); } +void QuicDispatcher::ClearStatelessResetAddresses() { + recent_stateless_reset_addresses_.clear(); +} + void QuicDispatcher::OnCanWrite() { // The socket is now writable. writer_->SetWritable(); @@ -1098,9 +1160,8 @@ void QuicDispatcher::StatelesslyTerminateConnection( << ", error_code:" << error_code << ", error_details:" << error_details; time_wait_list_manager_->AddConnectionIdToTimeWait( - server_connection_id, action, - TimeWaitConnectionInfo(format != GOOGLE_QUIC_PACKET, nullptr, - {server_connection_id})); + action, TimeWaitConnectionInfo(format != GOOGLE_QUIC_PACKET, nullptr, + {server_connection_id})); return; } @@ -1138,7 +1199,7 @@ void QuicDispatcher::StatelesslyTerminateConnection( /*ietf_quic=*/format != GOOGLE_QUIC_PACKET, use_length_prefix, /*versions=*/{})); time_wait_list_manager()->AddConnectionIdToTimeWait( - server_connection_id, QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, + QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, TimeWaitConnectionInfo(/*ietf_quic=*/format != GOOGLE_QUIC_PACKET, &termination_packets, {server_connection_id})); } @@ -1369,8 +1430,14 @@ bool QuicDispatcher::IsSupportedVersion(const ParsedQuicVersion version) { void QuicDispatcher::MaybeResetPacketsWithNoVersion( const ReceivedPacketInfo& packet_info) { QUICHE_DCHECK(!packet_info.version_flag); - if (GetQuicRestartFlag(quic_fix_stateless_reset2) && - packet_info.form != GOOGLE_QUIC_PACKET) { + // Do not send a stateless reset if a reset has been sent to this address + // recently. + if (recent_stateless_reset_addresses_.contains(packet_info.peer_address)) { + QUIC_CODE_COUNT(quic_donot_send_reset_repeatedly); + QUICHE_DCHECK(use_recent_reset_addresses_); + return; + } + if (packet_info.form != GOOGLE_QUIC_PACKET) { // Drop IETF packets smaller than the minimal stateless reset length. if (packet_info.packet.length() <= QuicFramer::GetMinStatelessResetPacketLength()) { @@ -1386,8 +1453,24 @@ void QuicDispatcher::MaybeResetPacketsWithNoVersion( QUIC_CODE_COUNT(drop_too_small_packets); return; } - // TODO(fayang): Consider rate limiting reset packets if reset packet size > - // packet_length. + } + if (use_recent_reset_addresses_) { + QUIC_RESTART_FLAG_COUNT(quic_use_recent_reset_addresses); + // Do not send a stateless reset if there are too many stateless reset + // addresses. + if (recent_stateless_reset_addresses_.size() >= + GetQuicFlag(FLAGS_quic_max_recent_stateless_reset_addresses)) { + QUIC_CODE_COUNT(quic_too_many_recent_reset_addresses); + return; + } + if (recent_stateless_reset_addresses_.empty()) { + clear_stateless_reset_addresses_alarm_->Update( + helper()->GetClock()->ApproximateNow() + + QuicTime::Delta::FromMilliseconds(GetQuicFlag( + FLAGS_quic_recent_stateless_reset_addresses_lifetime_ms)), + QuicTime::Delta::Zero()); + } + recent_stateless_reset_addresses_.emplace(packet_info.peer_address); } time_wait_list_manager()->SendPublicReset( 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 a6ba1a6e975..0073666fd62 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 @@ -131,6 +131,9 @@ class QUIC_NO_EXPORT QuicDispatcher // Deletes all sessions on the closed session list and clears the list. virtual void DeleteSessions(); + // Clear recent_stateless_reset_addresses_. + void ClearStatelessResetAddresses(); + using ConnectionIdMap = absl:: flat_hash_map<QuicConnectionId, QuicConnectionId, QuicConnectionIdHash>; @@ -473,12 +476,21 @@ class QUIC_NO_EXPORT QuicDispatcher // version does not allow variable length connection ID. uint8_t expected_server_connection_id_length_; + // Records client addresses that have been recently reset. + absl::flat_hash_set<QuicSocketAddress, QuicSocketAddressHash> + recent_stateless_reset_addresses_; + + // An alarm which clear recent_stateless_reset_addresses_. + std::unique_ptr<QuicAlarm> clear_stateless_reset_addresses_alarm_; + // If true, change expected_server_connection_id_length_ to be the received // destination connection ID length of all IETF long headers. bool should_update_expected_server_connection_id_length_; + const bool use_recent_reset_addresses_ = + GetQuicRestartFlag(quic_use_recent_reset_addresses); + const bool support_multiple_cid_per_connection_ = - GetQuicRestartFlag(quic_time_wait_list_support_multiple_cid_v2) && GetQuicRestartFlag( quic_dispatcher_support_multiple_cid_per_connection_v2); }; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc index 3a98be3df90..218096230c6 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 @@ -121,15 +121,12 @@ class TestDispatcher : public QuicDispatcher { public: TestDispatcher(const QuicConfig* config, const QuicCryptoServerConfig* crypto_config, - QuicVersionManager* version_manager, - QuicRandom* random) - : QuicDispatcher(config, - crypto_config, - version_manager, + QuicVersionManager* version_manager, QuicRandom* random) + : QuicDispatcher(config, crypto_config, version_manager, std::make_unique<MockQuicConnectionHelper>(), std::unique_ptr<QuicCryptoServerStreamBase::Helper>( new QuicSimpleCryptoServerStreamHelper()), - std::make_unique<MockAlarmFactory>(), + std::make_unique<TestAlarmFactory>(), kQuicDefaultConnectionIdLength), random_(random) {} @@ -500,6 +497,11 @@ class QuicDispatcherTestBase : public QuicTestWithParam<ParsedQuicVersion> { const QuicConnectionId& server_connection_id, const QuicConnectionId& client_connection_id); + TestAlarmFactory::TestAlarm* GetClearResetAddressesAlarm() { + return reinterpret_cast<TestAlarmFactory::TestAlarm*>( + QuicDispatcherPeer::GetClearResetAddressesAlarm(dispatcher_.get())); + } + ParsedQuicVersion version_; MockQuicConnectionHelper mock_helper_; MockAlarmFactory mock_alarm_factory_; @@ -895,7 +897,7 @@ TEST_P(QuicDispatcherTestAllVersions, TimeWaitListManager) { EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, connection_id, _, _, _)) .Times(1); - EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) + EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _)) .Times(0); ProcessPacket(client_address, connection_id, true, "data"); } @@ -911,7 +913,7 @@ TEST_P(QuicDispatcherTestAllVersions, NoVersionPacketToTimeWaitListManager) { EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, connection_id, _, _, _)) .Times(0); - EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) + EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _)) .Times(0); EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) .Times(1); @@ -933,7 +935,7 @@ TEST_P(QuicDispatcherTestAllVersions, EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _)) .Times(0); - EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) + EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _)) .Times(0); // Verify small packet is silently dropped. EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) @@ -944,6 +946,120 @@ TEST_P(QuicDispatcherTestAllVersions, dispatcher_->ProcessPacket(server_address_, client_address, packet2); } +TEST_P(QuicDispatcherTestOneVersion, DropPacketWithInvalidFlags) { + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + CreateTimeWaitListManager(); + uint8_t all_zero_packet[1200] = {}; + QuicReceivedPacket packet(reinterpret_cast<char*>(all_zero_packet), + sizeof(all_zero_packet), QuicTime::Zero()); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _)) + .Times(0); + EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _)) + .Times(0); + EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) + .Times(0); + dispatcher_->ProcessPacket(server_address_, client_address, packet); +} + +TEST_P(QuicDispatcherTestAllVersions, LimitResetsToSameClientAddress) { + CreateTimeWaitListManager(); + + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + QuicSocketAddress client_address2(QuicIpAddress::Loopback4(), 2); + QuicSocketAddress client_address3(QuicIpAddress::Loopback6(), 1); + QuicConnectionId connection_id = TestConnectionId(1); + + if (GetQuicRestartFlag(quic_use_recent_reset_addresses)) { + // Verify only one reset is sent to the address, although multiple packets + // are received. + EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) + .Times(1); + } else { + EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) + .Times(3); + } + ProcessPacket(client_address, connection_id, /*has_version_flag=*/false, + "data"); + ProcessPacket(client_address, connection_id, /*has_version_flag=*/false, + "data2"); + ProcessPacket(client_address, connection_id, /*has_version_flag=*/false, + "data3"); + + EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) + .Times(2); + ProcessPacket(client_address2, connection_id, /*has_version_flag=*/false, + "data"); + ProcessPacket(client_address3, connection_id, /*has_version_flag=*/false, + "data"); +} + +TEST_P(QuicDispatcherTestAllVersions, + StopSendingResetOnTooManyRecentAddresses) { + SetQuicFlag(FLAGS_quic_max_recent_stateless_reset_addresses, 2); + const size_t kTestLifeTimeMs = 10; + SetQuicFlag(FLAGS_quic_recent_stateless_reset_addresses_lifetime_ms, + kTestLifeTimeMs); + CreateTimeWaitListManager(); + + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); + QuicSocketAddress client_address2(QuicIpAddress::Loopback4(), 2); + QuicSocketAddress client_address3(QuicIpAddress::Loopback6(), 1); + QuicConnectionId connection_id = TestConnectionId(1); + + EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) + .Times(2); + EXPECT_FALSE(GetClearResetAddressesAlarm()->IsSet()); + ProcessPacket(client_address, connection_id, /*has_version_flag=*/false, + "data"); + const QuicTime expected_deadline = + mock_helper_.GetClock()->Now() + + QuicTime::Delta::FromMilliseconds(kTestLifeTimeMs); + if (GetQuicRestartFlag(quic_use_recent_reset_addresses)) { + ASSERT_TRUE(GetClearResetAddressesAlarm()->IsSet()); + EXPECT_EQ(expected_deadline, GetClearResetAddressesAlarm()->deadline()); + } else { + EXPECT_FALSE(GetClearResetAddressesAlarm()->IsSet()); + } + // Received no version packet 2 after 5ms. + mock_helper_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + ProcessPacket(client_address2, connection_id, /*has_version_flag=*/false, + "data"); + if (GetQuicRestartFlag(quic_use_recent_reset_addresses)) { + ASSERT_TRUE(GetClearResetAddressesAlarm()->IsSet()); + // Verify deadline does not change. + EXPECT_EQ(expected_deadline, GetClearResetAddressesAlarm()->deadline()); + } else { + EXPECT_FALSE(GetClearResetAddressesAlarm()->IsSet()); + } + if (GetQuicRestartFlag(quic_use_recent_reset_addresses)) { + // Verify reset gets throttled since there are too many recent addresses. + EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) + .Times(0); + } else { + EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) + .Times(1); + } + ProcessPacket(client_address3, connection_id, /*has_version_flag=*/false, + "data"); + + mock_helper_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + if (GetQuicRestartFlag(quic_use_recent_reset_addresses)) { + GetClearResetAddressesAlarm()->Fire(); + EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) + .Times(2); + } else { + EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) + .Times(3); + } + ProcessPacket(client_address, connection_id, /*has_version_flag=*/false, + "data"); + ProcessPacket(client_address2, connection_id, /*has_version_flag=*/false, + "data"); + ProcessPacket(client_address3, connection_id, /*has_version_flag=*/false, + "data"); +} + // Makes sure nine-byte connection IDs are replaced by 8-byte ones. TEST_P(QuicDispatcherTestAllVersions, LongConnectionIdLengthReplaced) { if (!version_.AllowsVariableLengthConnectionIds()) { @@ -1080,12 +1196,49 @@ TEST_P(QuicDispatcherTestAllVersions, ProcessPacketWithZeroPort) { .Times(0); EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _)) .Times(0); - EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) + EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _)) .Times(0); ProcessPacket(client_address, TestConnectionId(1), /*has_version_flag=*/true, "data"); } +TEST_P(QuicDispatcherTestAllVersions, ProcessPacketWithBlockedPort) { + SetQuicReloadableFlag(quic_blocked_ports, true); + CreateTimeWaitListManager(); + + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 17); + + // dispatcher_ should drop this packet. + EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(1), _, + client_address, _, _, _)) + .Times(0); + EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _)) + .Times(0); + EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _)) + .Times(0); + ProcessPacket(client_address, TestConnectionId(1), /*has_version_flag=*/true, + "data"); +} + +TEST_P(QuicDispatcherTestAllVersions, ProcessPacketWithNonBlockedPort) { + CreateTimeWaitListManager(); + + // Port 443 must not be blocked because it might be useful for proxies to send + // proxied traffic with source port 443 as that allows building a full QUIC + // proxy using a single UDP socket. + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 443); + + // dispatcher_ should not drop this packet. + 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_)))); + ProcessFirstFlight(client_address, TestConnectionId(1)); +} + TEST_P(QuicDispatcherTestAllVersions, DropPacketWithKnownVersionAndInvalidShortInitialConnectionId) { if (!version_.AllowsVariableLengthConnectionIds()) { @@ -1099,14 +1252,13 @@ TEST_P(QuicDispatcherTestAllVersions, EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _)) .Times(0); - EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) + EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _)) .Times(0); ProcessFirstFlight(client_address, EmptyQuicConnectionId()); } TEST_P(QuicDispatcherTestAllVersions, DropPacketWithKnownVersionAndInvalidInitialConnectionId) { - SetQuicReloadableFlag(quic_discard_packets_with_invalid_cid, true); CreateTimeWaitListManager(); QuicSocketAddress server_address; @@ -1116,7 +1268,7 @@ TEST_P(QuicDispatcherTestAllVersions, EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _)) .Times(0); - EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) + EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _)) .Times(0); absl::string_view cid_str = "123456789abcdefg123456789abcdefg"; QuicConnectionId invalid_connection_id(cid_str.data(), cid_str.length()); @@ -1538,7 +1690,7 @@ TEST_P(QuicDispatcherTestAllVersions, DoNotProcessSmallPacket) { EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, SendPacket(_, _, _)).Times(0); - EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) + EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _)) .Times(0); ProcessPacket(client_address, TestConnectionId(1), /*has_version_flag=*/true, version_, SerializeCHLO(), /*full_padding=*/false, @@ -1691,7 +1843,7 @@ TEST_P(QuicDispatcherTestStrayPacketConnectionId, EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _)) .Times(0); - EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) + EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _)) .Times(0); ProcessPacket(client_address, connection_id, true, "data", @@ -2006,7 +2158,6 @@ class QuicDispatcherSupportMultipleConnectionIdPerConnectionTest public: QuicDispatcherSupportMultipleConnectionIdPerConnectionTest() : QuicDispatcherTestBase(crypto_test_utils::ProofSourceForTesting()) { - SetQuicRestartFlag(quic_time_wait_list_support_multiple_cid_v2, true); SetQuicRestartFlag(quic_dispatcher_support_multiple_cid_per_connection_v2, true); dispatcher_ = std::make_unique<NiceMock<TestDispatcher>>( @@ -2318,7 +2469,7 @@ TEST_P(BufferedPacketStoreTest, // A bunch of non-CHLO should be buffered upon arrival. size_t kNumConnections = kMaxConnectionsWithoutCHLO + 1; for (size_t i = 1; i <= kNumConnections; ++i) { - QuicSocketAddress client_address(QuicIpAddress::Loopback4(), i); + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 20000 + i); QuicConnectionId conn_id = TestConnectionId(i); EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection( @@ -2336,7 +2487,7 @@ TEST_P(BufferedPacketStoreTest, kNumConnections); // Process CHLOs to create session for these connections. for (size_t i = 1; i <= kNumConnections; ++i) { - QuicSocketAddress client_address(QuicIpAddress::Loopback4(), i); + QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 20000 + i); QuicConnectionId conn_id = TestConnectionId(i); if (i == kNumConnections) { EXPECT_CALL(*dispatcher_, diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory_test.cc index e2268dbb502..d815fae8f4d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory_test.cc @@ -12,7 +12,7 @@ namespace quic { namespace test { namespace { -class TestDelegate : public QuicAlarm::Delegate { +class TestDelegate : public QuicAlarm::DelegateWithoutContext { public: TestDelegate() : fired_(false) {} 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 c3fcb887066..162cb04e4d0 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 @@ -275,6 +275,8 @@ const char* QuicErrorCodeToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_TLS_UNRECOGNIZED_NAME); RETURN_STRING_LITERAL(QUIC_TLS_CERTIFICATE_REQUIRED); + RETURN_STRING_LITERAL(QUIC_INVALID_CHARACTER_IN_FIELD_VALUE); + RETURN_STRING_LITERAL(QUIC_LAST_ERROR); // Intentionally have no default case, so we'll break the build // if we add errors and don't put them here. @@ -772,6 +774,8 @@ QuicErrorCodeToIetfMapping QuicErrorCodeToTransportErrorCode( return {true, static_cast<uint64_t>(CONNECTION_ID_LIMIT_ERROR)}; case QUIC_TOO_MANY_CONNECTION_ID_WAITING_TO_RETIRE: return {true, static_cast<uint64_t>(INTERNAL_ERROR)}; + case QUIC_INVALID_CHARACTER_IN_FIELD_VALUE: + return {false, static_cast<uint64_t>(QuicHttp3ErrorCode::MESSAGE_ERROR)}; case QUIC_LAST_ERROR: return {false, static_cast<uint64_t>(QUIC_LAST_ERROR)}; } @@ -939,6 +943,19 @@ QuicRstStreamErrorCode IetfResetStreamErrorCodeToRstStreamErrorCode( return QUIC_STREAM_UNKNOWN_APPLICATION_ERROR_CODE; } +// static +QuicResetStreamError QuicResetStreamError::FromInternal( + QuicRstStreamErrorCode code) { + return QuicResetStreamError( + code, RstStreamErrorCodeToIetfResetStreamErrorCode(code)); +} + +// static +QuicResetStreamError QuicResetStreamError::FromIetf(uint64_t code) { + return QuicResetStreamError( + IetfResetStreamErrorCodeToRstStreamErrorCode(code), code); +} + #undef RETURN_STRING_LITERAL // undef for jumbo builds } // namespace quic 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 53a881014ce..860f107e137 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 @@ -599,8 +599,11 @@ enum QuicErrorCode { QUIC_TLS_UNRECOGNIZED_NAME = 201, QUIC_TLS_CERTIFICATE_REQUIRED = 202, + // An HTTP field value containing an invalid character has been received. + QUIC_INVALID_CHARACTER_IN_FIELD_VALUE = 206, + // No error. Used as bound while iterating. - QUIC_LAST_ERROR = 206, + QUIC_LAST_ERROR = 207, }; // 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 @@ -609,6 +612,45 @@ static_assert(static_cast<uint64_t>(QUIC_LAST_ERROR) <= static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()), "QuicErrorCode exceeds four octets"); +// Represents a reason for resetting a stream in both gQUIC and IETF error code +// space. Both error codes have to be present. +class QUIC_EXPORT_PRIVATE QuicResetStreamError { + public: + // Constructs a QuicResetStreamError from QuicRstStreamErrorCode; the IETF + // error code is inferred. + static QuicResetStreamError FromInternal(QuicRstStreamErrorCode code); + // Constructs a QuicResetStreamError from an IETF error code; the internal + // error code is inferred. + static QuicResetStreamError FromIetf(uint64_t code); + // Constructs a QuicResetStreamError with no error. + static QuicResetStreamError NoError() { + return FromInternal(QUIC_STREAM_NO_ERROR); + } + + QuicResetStreamError(QuicRstStreamErrorCode internal_code, + uint64_t ietf_application_code) + : internal_code_(internal_code), + ietf_application_code_(ietf_application_code) {} + + QuicRstStreamErrorCode internal_code() const { return internal_code_; } + uint64_t ietf_application_code() const { return ietf_application_code_; } + + bool operator==(const QuicResetStreamError& other) const { + return internal_code() == other.internal_code() && + ietf_application_code() == other.ietf_application_code(); + } + + // Returns true if the object holds no error. + bool ok() const { return internal_code() == QUIC_STREAM_NO_ERROR; } + + private: + // Error code used in gQUIC. Even when IETF QUIC is in use, this needs to be + // populated as we use those internally. + QuicRstStreamErrorCode internal_code_; + // Application error code used in IETF QUIC. + uint64_t ietf_application_code_; +}; + // Convert TLS alert code to QuicErrorCode. QUIC_EXPORT_PRIVATE QuicErrorCode TlsAlertToQuicErrorCode(uint8_t desc); @@ -645,8 +687,7 @@ QUIC_EXPORT_PRIVATE std::string QuicIetfTransportErrorCodeString( QuicIetfTransportErrorCodes c); QUIC_EXPORT_PRIVATE std::ostream& operator<<( - std::ostream& os, - const QuicIetfTransportErrorCodes& c); + std::ostream& os, const QuicIetfTransportErrorCodes& c); // A transport error code (if is_transport_close is true) or application error // code (if is_transport_close is false) to be used in CONNECTION_CLOSE frames. @@ -678,6 +719,7 @@ enum class QuicHttp3ErrorCode { REQUEST_REJECTED = 0x10B, REQUEST_CANCELLED = 0x10C, REQUEST_INCOMPLETE = 0x10D, + MESSAGE_ERROR = 0x10E, CONNECT_ERROR = 0x10F, VERSION_FALLBACK = 0x110, }; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_flags_list.h b/chromium/net/third_party/quiche/src/quic/core/quic_flags_list.h index 9bb54b26abd..4399efaccf5 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_flags_list.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_flags_list.h @@ -6,8 +6,6 @@ #ifdef QUIC_FLAG -QUIC_FLAG(FLAGS_quic_restart_flag_dont_fetch_quic_private_keys_from_leto, false) - QUIC_FLAG(FLAGS_quic_restart_flag_quic_offload_pacing_to_usps2, false) // A testonly reloadable flag that will always default to false. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_testonly_default_false, false) @@ -17,38 +15,40 @@ QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_testonly_default_true, true) QUIC_FLAG(FLAGS_quic_restart_flag_quic_testonly_default_false, false) // A testonly restart flag that will always default to true. QUIC_FLAG(FLAGS_quic_restart_flag_quic_testonly_default_true, true) -// If true and a QUIC connection is traced, add ssl events to the trace. -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_trace_ssl_events, true) -// If true, GFE will explicitly configure its signature algorithm preference. -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_set_signature_algorithm_prefs, false) +// If bytes in flight has dipped below 1.25*MaxBW in the last round, do not exit PROBE_UP due to excess queue buildup. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_no_probe_up_exit_if_no_queue, true) +// If true and connection option B201 is used, check if cwnd limited before aggregation epoch, instead of ack event, in PROBE_UP phase. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_check_cwnd_limited_before_aggregation_epoch, true) +// If true, QPACK decoder rejects CR, LF, and NULL in field (header) values, and causes the stream to be reset with H3_MESSAGE_ERROR. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_reject_invalid_chars_in_field_value, true) // If true, QUIC will default enable MTU discovery at server, with a target of 1450 bytes. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_mtu_discovery_at_server, false) +// If true, QuicAlarms that belong to a single QuicConnection will fire under the corresponding QuicConnectionContext. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_restore_connection_context_in_alarms, true) // If true, QuicGsoBatchWriter will support release time if it is available and the process has the permission to do so. QUIC_FLAG(FLAGS_quic_restart_flag_quic_support_release_time_for_gso, false) -// If true, QuicIdleNetworkDetector::SetAlarm will become a noop if dectection has been stopped by QuicIdleNetworkDetector::StopDetection. -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_idle_network_detector_no_alarm_after_stopped, true) -// If true, TlsServerHandshaker::DefaultProofSourceHandle::DefaultSignatureCallback will run at most once. -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_run_default_signature_callback_once, true) // If true, abort async QPACK header decompression in QuicSpdyStream::Reset() and in QuicSpdyStream::OnStreamReset(). QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_abort_qpack_on_stream_reset, true) // If true, ack frequency frame can be sent from server to client. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_can_send_ack_frequency, true) // If true, add missing MaybeUpdateAckTimeout for ack-eliciting frames. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_add_missing_update_ack_timeout, true) -// If true, allow QuicAlarm to be permanently cancelled. -QUIC_FLAG(FLAGS_quic_restart_flag_quic_alarm_add_permanent_cancel, false) // If true, allow client to enable BBRv2 on server via connection option \'B2ON\'. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_allow_client_enabled_bbr_v2, false) -// If true, allow ticket open to be ignored in TlsServerHandshaker. Also fixes TlsServerHandshaker::ResumptionAttempted when handshake hints is used. -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_allow_ignore_ticket_open, true) +// If true, always starts a new ack aggregation epoch if a full round has passed since the start of the current epoch. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr_start_new_aggregation_epoch_after_a_full_round, true) +// If true, avoid calling reloadable flags in QuicVersionManager constructor by lazily initializing internal state. +QUIC_FLAG(FLAGS_quic_restart_flag_quic_lazy_quic_version_manager, true) +// If true, clear undecryptable packets on handshake complete. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_clear_undecryptable_packets_on_handshake_complete, true) // If true, close read side but not write side in QuicSpdyStream::OnStreamReset(). QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_on_stream_reset, true) // If true, default on PTO which unifies TLP + RTO loss recovery. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_on_pto, false) // If true, default-enable 5RTO blachole detection. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_enable_5rto_blackhole_detection2, true) -// If true, determine stateless reset packet length based on the received packet length. -QUIC_FLAG(FLAGS_quic_restart_flag_quic_fix_stateless_reset2, true) +// If true, delay block allocation in QuicStreamSequencerBuffer until there is actually new data available. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_delay_sequencer_buffer_allocation_until_new_data, false) // If true, disable QUIC version Q043. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_q043, false) // If true, disable QUIC version Q046. @@ -60,7 +60,7 @@ QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_rfcv1, false) // If true, disable QUIC version h3-29. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_draft_29, false) // If true, disable QUIC version h3-T051. -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_t051, false) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_t051, true) // If true, disable blackhole detection on server side. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_server_blackhole_detection, false) // If true, discard INITIAL packet if the key has been dropped. @@ -75,58 +75,68 @@ QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_donot_rearm_pto_on_application_data_du QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_drop_unsent_path_response, true) // If true, enable server retransmittable on wire PING. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_server_on_wire_ping, true) +// If true, ietf connection migration is no longer conditioned on connection option RVCM. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_remove_connection_migration_connection_option, true) // If true, ignore peer_max_ack_delay during handshake. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_ignore_peer_max_ack_delay_during_handshake, true) -// If true, include non-default port in the origin field of the ACCEPT_CH frame in ALPS. -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_include_port_in_alps_origin, true) // If true, include stream information in idle timeout connection close detail. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_add_stream_info_to_idle_close_detail, true) -// If true, increase the size of stream sequencer buffer block container on demand. -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_allocate_stream_sequencer_buffer_blocks_on_demand, false) // If true, pass the received PATH_RESPONSE payload to path validator to move forward the path validation. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator, true) -// If true, quic connection sends/recieives NewConnectionId & RetireConnectionId frames. -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_connection_support_multiple_cids_v4, true) -// If true, quic dispatcher discards packets with invalid server connection ID. -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_discard_packets_with_invalid_cid, true) // If true, quic dispatcher supports one connection to use multiple connection IDs. QUIC_FLAG(FLAGS_quic_restart_flag_quic_dispatcher_support_multiple_cid_per_connection_v2, true) -// If true, receiving server push stream will trigger QUIC connection close. -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_decline_server_push_stream, false) +// If true, record addresses that server has sent reset to recently, and do not send reset if the address lives in the set. +QUIC_FLAG(FLAGS_quic_restart_flag_quic_use_recent_reset_addresses, true) +// If true, refactor how QUIC TLS server disables resumption. No behavior change. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_disable_resumption_refactor, true) // If true, require handshake confirmation for QUIC connections, functionally disabling 0-rtt handshakes. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_require_handshake_confirmation, false) // If true, reset per packet state before processing undecryptable packets. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_reset_per_packet_state_for_undecryptable_packets, true) +// If true, respect FLAGS_quic_time_wait_list_max_pending_packets as the upper bound of queued packets in time wait list. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_add_upperbound_for_queued_packets, true) +// If true, restore connection context in various callbacks in TlsServerHandshaker. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_restore_connection_context_in_callbacks, true) // If true, send PATH_RESPONSE upon receiving PATH_CHALLENGE regardless of perspective. --gfe2_reloadable_flag_quic_start_peer_migration_earlier has to be true before turn on this flag. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_path_response2, true) // If true, set burst token to 2 in cwnd bootstrapping experiment. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_conservative_bursts, false) // If true, stop resetting ideal_next_packet_send_time_ in pacing sender. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_donot_reset_ideal_next_packet_send_time, false) -// If true, time_wait_list can support multiple connection IDs. -QUIC_FLAG(FLAGS_quic_restart_flag_quic_time_wait_list_support_multiple_cid_v2, true) -// If true, update ACK timeout for NEW_CONNECTION_ID and RETIRE_CONNECTION_ID frames. -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_ack_cid_frames, true) -// If true, upon receiving path challenge, send path response and reverse path challenge in the same function. -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_group_path_response_and_challenge_sending_closer, true) +// If true, suppress crypto data write in mid of packet processing. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_suppress_write_mid_packet_processing, true) // If true, use BBRv2 as the default congestion controller. Takes precedence over --quic_default_to_bbr. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_to_bbr_v2, false) +// If true, use max(max_bw, send_rate) as the estimated bandwidth in QUIC\'s MaxAckHeightTracker. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr_use_send_rate_in_max_ack_height_tracker, true) // If true, use new connection ID in connection migration. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_connection_migration_use_new_cid_v2, true) // If true, uses conservative cwnd gain and pacing gain when cwnd gets bootstrapped. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_conservative_cwnd_and_pacing_gains, false) // If true, validate that peer owns the new address once the server detects peer migration or is probed from that address, and also apply anti-amplification limit while sending to that address. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_server_reverse_validate_new_path3, true) -// Queue packets to attempt decryption later until the handshake is complete. -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_queue_until_handshake_complete, true) +// When receiving STOP_SENDING, send a RESET_STREAM with a matching error code. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_match_ietf_reset_code, false) // When the STMP connection option is sent by the client, timestamps in the QUIC ACK frame are sent and processed. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_timestamps, false) +// When the flag is true, exit STARTUP after the same number of loss events as PROBE_UP. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_startup_probe_up_loss_events, true) +// When true, QuicDispatcher will silently drop incoming packets whose UDP source port is on the blocklist. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_blocked_ports, false) // When true, defaults to BBR congestion control instead of Cubic. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_to_bbr, false) // When true, prevents QUIC\'s PacingSender from generating bursts when the congestion controller is CWND limited and not pacing limited. QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_pacing_sender_bursts, false) // When true, set the initial congestion control window from connection options in QuicSentPacketManager rather than TcpCubicSenderBytes. -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_unified_iw_options, false) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_unified_iw_options, true) +// When true, the B203 connection option causes the Bbr2Sender to ignore inflight_hi during PROBE_UP and increase it when the bytes delivered without loss are higher. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_ignore_inflight_hi_in_probe_up, true) +// When true, the B204 connection option enables extra acked in STARTUP, but also adds new logic to decrease it whenever max bandwidth increases. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_startup_extra_acked, true) +// When true, the BBQ0 connection option causes QUIC BBR2 to add bytes_acked to probe_up_acked if the connection hasn\'t been app-limited since inflight_hi was utilized. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_add_bytes_acked_after_inflight_hi_limited, true) +// When true, the BBR4 copt sets the extra_acked window to 20 RTTs and BBR5 sets it to 40 RTTs. +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_extra_acked_window, true) #endif diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_framer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_framer.cc index 24b2be86484..4e0f639ae2d 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 @@ -412,6 +412,7 @@ QuicFramer::QuicFramer(const ParsedQuicVersionVector& supported_versions, perspective_(perspective), validate_flags_(true), process_timestamps_(false), + receive_timestamps_exponent_(0), creation_time_(creation_time), last_timestamp_(QuicTime::Delta::Zero()), support_key_update_for_connection_(false), @@ -1300,81 +1301,45 @@ size_t QuicFramer::GetMinStatelessResetPacketLength() { // static std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildIetfStatelessResetPacket( - QuicConnectionId /*connection_id*/, - size_t received_packet_length, + QuicConnectionId /*connection_id*/, size_t received_packet_length, StatelessResetToken stateless_reset_token) { QUIC_DVLOG(1) << "Building IETF stateless reset packet."; - if (GetQuicRestartFlag(quic_fix_stateless_reset2)) { - if (received_packet_length <= GetMinStatelessResetPacketLength()) { - QUICHE_DLOG(ERROR) - << "Tried to build stateless reset packet with received packet " - "length " - << received_packet_length; - return nullptr; - } - // To ensure stateless reset is indistinguishable from a valid packet, - // include the max connection ID length. - size_t len = std::min(received_packet_length - 1, - GetMinStatelessResetPacketLength() + 1 + - kQuicMaxConnectionIdWithLengthPrefixLength); - std::unique_ptr<char[]> buffer(new char[len]); - QuicDataWriter writer(len, buffer.get()); - // Append random bytes. This randomness only exists to prevent middleboxes - // from comparing the entire packet to a known value. Therefore it has no - // cryptographic use, and does not need a secure cryptographic pseudo-random - // number generator. It's therefore safe to use WriteInsecureRandomBytes. - if (!writer.WriteInsecureRandomBytes(QuicRandom::GetInstance(), - len - kStatelessResetTokenLength)) { - QUIC_BUG(362045737_2) << "Failed to append random bytes of length: " - << len - kStatelessResetTokenLength; - return nullptr; - } - // Change first 2 fixed bits to 01. - buffer[0] &= ~FLAGS_LONG_HEADER; - buffer[0] |= FLAGS_FIXED_BIT; - - // Append stateless reset token. - if (!writer.WriteBytes(&stateless_reset_token, - sizeof(stateless_reset_token))) { - QUIC_BUG(362045737_3) << "Failed to write stateless reset token"; - return nullptr; - } - QUIC_RESTART_FLAG_COUNT(quic_fix_stateless_reset2); - return std::make_unique<QuicEncryptedPacket>(buffer.release(), len, - /*owns_buffer=*/true); + if (received_packet_length <= GetMinStatelessResetPacketLength()) { + QUICHE_DLOG(ERROR) + << "Tried to build stateless reset packet with received packet " + "length " + << received_packet_length; + return nullptr; } - - size_t len = kPacketHeaderTypeSize + kMinRandomBytesLengthInStatelessReset + - sizeof(stateless_reset_token); + // To ensure stateless reset is indistinguishable from a valid packet, + // include the max connection ID length. + size_t len = std::min(received_packet_length - 1, + GetMinStatelessResetPacketLength() + 1 + + kQuicMaxConnectionIdWithLengthPrefixLength); std::unique_ptr<char[]> buffer(new char[len]); QuicDataWriter writer(len, buffer.get()); - - uint8_t type = 0; - type |= FLAGS_FIXED_BIT; - type |= FLAGS_SHORT_HEADER_RESERVED_1; - type |= FLAGS_SHORT_HEADER_RESERVED_2; - type |= PacketNumberLengthToOnWireValue(PACKET_1BYTE_PACKET_NUMBER); - - // Append type byte. - if (!writer.WriteUInt8(type)) { - return nullptr; - } - // Append random bytes. This randomness only exists to prevent middleboxes // from comparing the entire packet to a known value. Therefore it has no // cryptographic use, and does not need a secure cryptographic pseudo-random // number generator. It's therefore safe to use WriteInsecureRandomBytes. if (!writer.WriteInsecureRandomBytes(QuicRandom::GetInstance(), - kMinRandomBytesLengthInStatelessReset)) { + len - kStatelessResetTokenLength)) { + QUIC_BUG(362045737_2) << "Failed to append random bytes of length: " + << len - kStatelessResetTokenLength; return nullptr; } + // Change first 2 fixed bits to 01. + buffer[0] &= ~FLAGS_LONG_HEADER; + buffer[0] |= FLAGS_FIXED_BIT; // Append stateless reset token. if (!writer.WriteBytes(&stateless_reset_token, sizeof(stateless_reset_token))) { + QUIC_BUG(362045737_3) << "Failed to write stateless reset token"; return nullptr; } - return std::make_unique<QuicEncryptedPacket>(buffer.release(), len, true); + return std::make_unique<QuicEncryptedPacket>(buffer.release(), len, + /*owns_buffer=*/true); } // static @@ -3172,11 +3137,14 @@ bool QuicFramer::IsIetfFrameTypeExpectedForEncryptionLevel( case ENCRYPTION_INITIAL: case ENCRYPTION_HANDSHAKE: return frame_type == IETF_CRYPTO || frame_type == IETF_ACK || + frame_type == IETF_ACK_ECN || + frame_type == IETF_ACK_RECEIVE_TIMESTAMPS || frame_type == IETF_PING || frame_type == IETF_PADDING || frame_type == IETF_CONNECTION_CLOSE; case ENCRYPTION_ZERO_RTT: - return !(frame_type == IETF_ACK || frame_type == IETF_CRYPTO || - frame_type == IETF_HANDSHAKE_DONE || + return !(frame_type == IETF_ACK || frame_type == IETF_ACK_ECN || + frame_type == IETF_ACK_RECEIVE_TIMESTAMPS || + frame_type == IETF_CRYPTO || frame_type == IETF_HANDSHAKE_DONE || frame_type == IETF_NEW_TOKEN || frame_type == IETF_PATH_RESPONSE || frame_type == IETF_RETIRE_CONNECTION_ID); @@ -3448,6 +3416,14 @@ bool QuicFramer::ProcessIetfFrameData(QuicDataReader* reader, } break; } + case IETF_ACK_RECEIVE_TIMESTAMPS: + if (!process_timestamps_) { + set_detailed_error("Unsupported frame type."); + QUIC_DLOG(WARNING) + << ENDPOINT << "IETF_ACK_RECEIVE_TIMESTAMPS not supported"; + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + ABSL_FALLTHROUGH_INTENDED; case IETF_ACK_ECN: case IETF_ACK: { QuicAckFrame frame; @@ -4115,7 +4091,12 @@ bool QuicFramer::ProcessIetfAckFrame(QuicDataReader* reader, ack_block_count--; } - if (frame_type == IETF_ACK_ECN) { + if (frame_type == IETF_ACK_RECEIVE_TIMESTAMPS) { + QUICHE_DCHECK(process_timestamps_); + if (!ProcessIetfTimestampsInAckFrame(ack_frame->largest_acked, reader)) { + return false; + } + } else if (frame_type == IETF_ACK_ECN) { ack_frame->ecn_counters_populated = true; if (!reader->ReadVarInt62(&ack_frame->ect_0_count)) { set_detailed_error("Unable to read ack ect_0_count."); @@ -4145,6 +4126,76 @@ bool QuicFramer::ProcessIetfAckFrame(QuicDataReader* reader, return true; } +bool QuicFramer::ProcessIetfTimestampsInAckFrame(QuicPacketNumber largest_acked, + QuicDataReader* reader) { + uint64_t timestamp_range_count; + if (!reader->ReadVarInt62(×tamp_range_count)) { + set_detailed_error("Unable to read receive timestamp range count."); + return false; + } + if (timestamp_range_count == 0) { + return true; + } + + QuicPacketNumber packet_number = largest_acked; + + // Iterate through all timestamp ranges, each of which represents a block of + // contiguous packets for which receive timestamps are being reported. Each + // range is of the form: + // + // Timestamp Range { + // Gap (i), + // Timestamp Delta Count (i), + // Timestamp Delta (i) ..., + // } + for (uint64_t i = 0; i < timestamp_range_count; i++) { + uint64_t gap; + if (!reader->ReadVarInt62(&gap)) { + set_detailed_error("Unable to read receive timestamp gap."); + return false; + } + if (packet_number.ToUint64() < gap) { + set_detailed_error("Receive timestamp gap too high."); + return false; + } + packet_number = packet_number - gap; + uint64_t timestamp_count; + if (!reader->ReadVarInt62(×tamp_count)) { + set_detailed_error("Unable to read receive timestamp count."); + return false; + } + if (packet_number.ToUint64() < timestamp_count) { + set_detailed_error("Receive timestamp count too high."); + return false; + } + for (uint64_t j = 0; j < timestamp_count; j++) { + uint64_t timestamp_delta; + if (!reader->ReadVarInt62(×tamp_delta)) { + set_detailed_error("Unable to read receive timestamp delta."); + return false; + } + // The first timestamp delta is relative to framer creation time; whereas + // subsequent deltas are relative to the previous delta in decreasing + // packet order. + timestamp_delta = timestamp_delta << receive_timestamps_exponent_; + if (i == 0 && j == 0) { + last_timestamp_ = CalculateTimestampFromWire(timestamp_delta); + } else { + last_timestamp_ = last_timestamp_ - + QuicTime::Delta::FromMicroseconds(timestamp_delta); + if (last_timestamp_ < QuicTime::Delta::Zero()) { + set_detailed_error("Receive timestamp delta too high."); + return false; + } + } + visitor_->OnAckTimestamp(packet_number - j, + creation_time_ + last_timestamp_); + } + packet_number = packet_number - (timestamp_count - 1); + } + return true; +} + bool QuicFramer::ProcessStopWaitingFrame(QuicDataReader* reader, const QuicPacketHeader& header, QuicStopWaitingFrame* stop_waiting) { @@ -6557,6 +6608,24 @@ QuicErrorCode QuicFramer::ParsePublicHeaderDispatcher( return QUIC_INVALID_PACKET_HEADER; } const uint8_t first_byte = reader.PeekByte(); + if ((first_byte & FLAGS_LONG_HEADER) == 0 && + (first_byte & FLAGS_FIXED_BIT) == 0 && + (first_byte & FLAGS_DEMULTIPLEXING_BIT) == 0) { + // All versions of Google QUIC up to and including Q043 set + // FLAGS_DEMULTIPLEXING_BIT to one on all client-to-server packets. Q044 + // and Q045 were never default-enabled in production. All subsequent + // versions of Google QUIC (starting with Q046) require FLAGS_FIXED_BIT to + // be set to one on all packets. All versions of IETF QUIC (since + // draft-ietf-quic-transport-17 which was earlier than the first IETF QUIC + // version that was deployed in production by any implementation) also + // require FLAGS_FIXED_BIT to be set to one on all packets. If a packet + // has the FLAGS_LONG_HEADER bit set to one, it could be a first flight + // from an unknown future version that allows the other two bits to be set + // to zero. Based on this, packets that have all three of those bits set + // to zero are known to be invalid. + *detailed_error = "Invalid flags."; + return QUIC_INVALID_PACKET_HEADER; + } const bool ietf_format = QuicUtils::IsIetfPacketHeader(first_byte); uint8_t unused_first_byte; QuicVariableLengthIntegerLength retry_token_length_length; 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 31cc756990b..a6820eaa9d6 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 @@ -313,6 +313,11 @@ class QUIC_EXPORT_PRIVATE QuicFramer { process_timestamps_ = process_timestamps; } + // Sets the exponent to use when writing/reading ACK receive timestamps. + void set_receive_timestamps_exponent(uint32_t exponent) { + receive_timestamps_exponent_ = exponent; + } + // Pass a UDP packet into the framer for parsing. // Return true if the packet was processed successfully. |packet| must be a // single, complete UDP packet (not a frame of a packet). This packet @@ -854,6 +859,8 @@ class QUIC_EXPORT_PRIVATE QuicFramer { bool ProcessIetfAckFrame(QuicDataReader* reader, uint64_t frame_type, QuicAckFrame* ack_frame); + bool ProcessIetfTimestampsInAckFrame(QuicPacketNumber largest_acked, + QuicDataReader* reader); bool ProcessStopWaitingFrame(QuicDataReader* reader, const QuicPacketHeader& header, QuicStopWaitingFrame* stop_waiting); @@ -1118,6 +1125,8 @@ class QUIC_EXPORT_PRIVATE QuicFramer { DiversificationNonce last_nonce_; // If true, send and process timestamps in the ACK frame. bool process_timestamps_; + // The exponent to use when writing/reading ACK receive timestamps. + uint32_t receive_timestamps_exponent_; // The creation time of the connection, used to calculate timestamps. QuicTime creation_time_; // The last timestamp received if process_timestamps_ is true. 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 f36bf5c78c7..f630867b083 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 @@ -37,6 +37,7 @@ #include "common/test_tools/quiche_test_utils.h" using testing::_; +using testing::ContainerEq; using testing::Return; namespace quic { @@ -358,7 +359,9 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { ack_frames_.push_back(std::make_unique<QuicAckFrame>(ack_frame)); if (VersionHasIetfQuicFrames(transport_version_)) { EXPECT_TRUE(IETF_ACK == framer_->current_received_frame_type() || - IETF_ACK_ECN == framer_->current_received_frame_type()); + IETF_ACK_ECN == framer_->current_received_frame_type() || + IETF_ACK_RECEIVE_TIMESTAMPS == + framer_->current_received_frame_type()); } else { EXPECT_EQ(0u, framer_->current_received_frame_type()); } @@ -370,7 +373,9 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { ack_frames_[ack_frames_.size() - 1]->packets.AddRange(start, end); if (VersionHasIetfQuicFrames(transport_version_)) { EXPECT_TRUE(IETF_ACK == framer_->current_received_frame_type() || - IETF_ACK_ECN == framer_->current_received_frame_type()); + IETF_ACK_ECN == framer_->current_received_frame_type() || + IETF_ACK_RECEIVE_TIMESTAMPS == + framer_->current_received_frame_type()); } else { EXPECT_EQ(0u, framer_->current_received_frame_type()); } @@ -381,7 +386,14 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { QuicTime timestamp) override { ack_frames_[ack_frames_.size() - 1]->received_packet_times.push_back( std::make_pair(packet_number, timestamp)); - EXPECT_EQ(0u, framer_->current_received_frame_type()); + if (VersionHasIetfQuicFrames(transport_version_)) { + EXPECT_TRUE(IETF_ACK == framer_->current_received_frame_type() || + IETF_ACK_ECN == framer_->current_received_frame_type() || + IETF_ACK_RECEIVE_TIMESTAMPS == + framer_->current_received_frame_type()); + } else { + EXPECT_EQ(0u, framer_->current_received_frame_type()); + } return true; } @@ -896,6 +908,11 @@ class QuicFramerTest : public QuicTestWithParam<ParsedQuicVersion> { ((n - 1) * QuicUtils::StreamIdDelta(transport_version)); } + QuicTime CreationTimePlus(uint64_t offset_us) { + return framer_.creation_time() + + QuicTime::Delta::FromMicroseconds(offset_us); + } + test::TestEncrypter* encrypter_; test::TestDecrypter* decrypter_; ParsedQuicVersion version_; @@ -1286,6 +1303,27 @@ TEST_P(QuicFramerTest, LongPacketHeaderWithBothConnectionIds) { EXPECT_EQ(FramerTestConnectionIdPlusOne(), source_connection_id); } +TEST_P(QuicFramerTest, AllZeroPacketParsingFails) { + unsigned char packet[1200] = {}; + QuicEncryptedPacket encrypted(AsChars(packet), ABSL_ARRAYSIZE(packet), false); + PacketHeaderFormat format = GOOGLE_QUIC_PACKET; + QuicLongHeaderType long_packet_type = INVALID_PACKET_TYPE; + bool version_flag = false; + QuicConnectionId destination_connection_id, source_connection_id; + QuicVersionLabel version_label = 0; + std::string detailed_error = ""; + bool retry_token_present, use_length_prefix; + absl::string_view retry_token; + ParsedQuicVersion parsed_version = UnsupportedQuicVersion(); + const QuicErrorCode error_code = QuicFramer::ParsePublicHeaderDispatcher( + encrypted, kQuicDefaultConnectionIdLength, &format, &long_packet_type, + &version_flag, &use_length_prefix, &version_label, &parsed_version, + &destination_connection_id, &source_connection_id, &retry_token_present, + &retry_token, &detailed_error); + EXPECT_EQ(error_code, QUIC_INVALID_PACKET_HEADER); + EXPECT_EQ(detailed_error, "Invalid flags."); +} + TEST_P(QuicFramerTest, ParsePublicHeader) { // clang-format off unsigned char packet[] = { @@ -2139,7 +2177,7 @@ TEST_P(QuicFramerTest, PaddingFrame) { 0x00, 0x00, }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -2171,8 +2209,8 @@ TEST_P(QuicFramerTest, PaddingFrame) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; p_size = ABSL_ARRAYSIZE(packet46); @@ -2260,7 +2298,7 @@ TEST_P(QuicFramerTest, StreamFrame) { 'r', 'l', 'd', '!'}}, }; - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -2293,7 +2331,7 @@ TEST_P(QuicFramerTest, StreamFrame) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) - ? packet99 + ? packet_ietf : (framer_.version().HasIetfInvariantHeader() ? packet46 : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); @@ -2583,7 +2621,7 @@ TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) { 'r', 'l', 'd', '!'}}, }; - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -2616,7 +2654,7 @@ TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) - ? packet99 + ? packet_ietf : (framer_.version().HasIetfInvariantHeader() ? packet46 : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); @@ -2702,7 +2740,7 @@ TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) { 'r', 'l', 'd', '!'}}, }; - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -2735,7 +2773,7 @@ TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) - ? packet99 + ? packet_ietf : (framer_.version().HasIetfInvariantHeader() ? packet46 : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); @@ -2880,7 +2918,7 @@ TEST_P(QuicFramerTest, StreamFrameWithVersion) { 'r', 'l', 'd', '!'}}, }; - PacketFragments packet99 = { + PacketFragments packet_ietf = { // public flags (long header with packet type ZERO_RTT_PROTECTED and // 4-byte packet number) {"", @@ -2934,7 +2972,7 @@ TEST_P(QuicFramerTest, StreamFrameWithVersion) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) - ? packet99 + ? packet_ietf : (framer_.version().HasLongHeaderLengths() ? packet49 : (framer_.version().HasIetfInvariantHeader() ? packet46 @@ -3129,7 +3167,7 @@ TEST_P(QuicFramerTest, AckFrameOneAckBlock) { {0x00}} }; - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short packet, 4 byte packet number) {"", {0x43}}, @@ -3167,7 +3205,7 @@ TEST_P(QuicFramerTest, AckFrameOneAckBlock) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) - ? packet99 + ? packet_ietf : (framer_.version().HasIetfInvariantHeader() ? packet46 : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); @@ -3250,7 +3288,7 @@ TEST_P(QuicFramerTest, FirstAckFrameUnderflow) { {0x00}} }; - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -3280,7 +3318,7 @@ TEST_P(QuicFramerTest, FirstAckFrameUnderflow) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) - ? packet99 + ? packet_ietf : (framer_.version().HasIetfInvariantHeader() ? packet46 : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); @@ -3300,7 +3338,7 @@ TEST_P(QuicFramerTest, ThirdAckBlockUnderflowGap) { } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -3340,12 +3378,12 @@ TEST_P(QuicFramerTest, ThirdAckBlockUnderflowGap) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); EXPECT_EQ( framer_.detailed_error(), "Underflow with gap block length 30 previous ack block start is 30."); - CheckFramingBoundaries(packet99, QUIC_INVALID_ACK_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_ACK_DATA); } // This test checks that the ack frame processor correctly identifies @@ -3360,7 +3398,7 @@ TEST_P(QuicFramerTest, ThirdAckBlockUnderflowAck) { } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -3398,11 +3436,11 @@ TEST_P(QuicFramerTest, ThirdAckBlockUnderflowAck) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); EXPECT_EQ(framer_.detailed_error(), "Underflow with ack block length 31 latest ack block end is 25."); - CheckFramingBoundaries(packet99, QUIC_INVALID_ACK_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_ACK_DATA); } // Tests a variety of ack block wrap scenarios. For example, if the @@ -3418,7 +3456,7 @@ TEST_P(QuicFramerTest, AckBlockUnderflowGapWrap) { } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -3452,11 +3490,11 @@ TEST_P(QuicFramerTest, AckBlockUnderflowGapWrap) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); EXPECT_EQ(framer_.detailed_error(), "Underflow with gap block length 2 previous ack block start is 1."); - CheckFramingBoundaries(packet99, QUIC_INVALID_ACK_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_ACK_DATA); } // As AckBlockUnderflowGapWrap, but in this test, it's the ack @@ -3470,7 +3508,7 @@ TEST_P(QuicFramerTest, AckBlockUnderflowAckWrap) { } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -3504,11 +3542,11 @@ TEST_P(QuicFramerTest, AckBlockUnderflowAckWrap) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); EXPECT_EQ(framer_.detailed_error(), "Underflow with ack block length 10 latest ack block end is 1."); - CheckFramingBoundaries(packet99, QUIC_INVALID_ACK_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_ACK_DATA); } // An ack block that acks the entire range, 1...0x3fffffffffffffff @@ -3521,7 +3559,7 @@ TEST_P(QuicFramerTest, AckBlockAcksEverything) { } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -3552,7 +3590,7 @@ TEST_P(QuicFramerTest, AckBlockAcksEverything) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_EQ(1u, visitor_.ack_frames_.size()); const QuicAckFrame& frame = *visitor_.ack_frames_[0]; @@ -3737,7 +3775,7 @@ TEST_P(QuicFramerTest, AckFrameOneAckBlockMaxLength) { {0x00}} }; - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -3767,7 +3805,7 @@ TEST_P(QuicFramerTest, AckFrameOneAckBlockMaxLength) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) - ? packet99 + ? packet_ietf : (framer_.version().HasIetfInvariantHeader() ? packet46 : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); @@ -3929,7 +3967,7 @@ TEST_P(QuicFramerTest, AckFrameTwoTimeStampsMultipleAckBlocks) { { 0x32, 0x10 }}, }; - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", { 0x43 }}, @@ -3940,9 +3978,9 @@ TEST_P(QuicFramerTest, AckFrameTwoTimeStampsMultipleAckBlocks) { {"", { 0x12, 0x34, 0x56, 0x78 }}, - // frame type (IETF_ACK frame) + // frame type (IETF_ACK_RECEIVE_TIMESTAMPS frame) {"", - { 0x02 }}, + { 0x22 }}, // largest acked {"Unable to read largest acked.", { kVarInt62TwoBytes + 0x12, 0x34 }}, // = 4660 @@ -3983,12 +4021,24 @@ TEST_P(QuicFramerTest, AckFrameTwoTimeStampsMultipleAckBlocks) { // ack block length. { "Unable to read ack block value.", { kVarInt62OneByte + 0x03 }}, // block is 3 packets. + + // Receive Timestamps. + { "Unable to read receive timestamp range count.", + { kVarInt62OneByte + 0x01 }}, + { "Unable to read receive timestamp gap.", + { kVarInt62OneByte + 0x01 }}, + { "Unable to read receive timestamp count.", + { kVarInt62OneByte + 0x02 }}, + { "Unable to read receive timestamp delta.", + { kVarInt62FourBytes + 0x36, 0x54, 0x32, 0x10 }}, + { "Unable to read receive timestamp delta.", + { kVarInt62TwoBytes + 0x32, 0x10 }}, }; // clang-format on PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) - ? packet99 + ? packet_ietf : (framer_.version().HasIetfInvariantHeader() ? packet46 : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( @@ -4009,12 +4059,331 @@ TEST_P(QuicFramerTest, AckFrameTwoTimeStampsMultipleAckBlocks) { EXPECT_EQ(kSmallLargestObserved, LargestAcked(frame)); ASSERT_EQ(4254u, frame.packets.NumPacketsSlow()); EXPECT_EQ(4u, frame.packets.NumIntervals()); - if (VersionHasIetfQuicFrames(framer_.transport_version())) { - EXPECT_EQ(0u, frame.received_packet_times.size()); - } else { - EXPECT_EQ(2u, frame.received_packet_times.size()); + EXPECT_EQ(2u, frame.received_packet_times.size()); +} + +TEST_P(QuicFramerTest, AckFrameMultipleReceiveTimestampRanges) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; } - CheckFramingBoundaries(fragments, QUIC_INVALID_ACK_DATA); + SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); + // clang-format off + PacketFragments packet_ietf = { + // 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 (IETF_ACK_RECEIVE_TIMESTAMPS frame) + {"", + { 0x22 }}, + // largest acked + {"Unable to read largest acked.", + { kVarInt62TwoBytes + 0x12, 0x34 }}, // = 4660 + // Zero delta time. + {"Unable to read ack delay time.", + { kVarInt62OneByte + 0x00 }}, + // number of additional ack blocks + {"Unable to read ack block count.", + { kVarInt62OneByte + 0x00 }}, + // first ack block length. + {"Unable to read first ack block length.", + { kVarInt62OneByte + 0x00 }}, // 1st block length = 1 + + // Receive Timestamps. + { "Unable to read receive timestamp range count.", + { kVarInt62OneByte + 0x03 }}, + + // Timestamp range 1 (three packets). + { "Unable to read receive timestamp gap.", + { kVarInt62OneByte + 0x02 }}, + { "Unable to read receive timestamp count.", + { kVarInt62OneByte + 0x03 }}, + { "Unable to read receive timestamp delta.", + { kVarInt62FourBytes + 0x29, 0xff, 0xff, 0xff}}, + { "Unable to read receive timestamp delta.", + { kVarInt62TwoBytes + 0x11, 0x11 }}, + { "Unable to read receive timestamp delta.", + { kVarInt62OneByte + 0x01}}, + + // Timestamp range 2 (one packet). + { "Unable to read receive timestamp gap.", + { kVarInt62OneByte + 0x07 }}, + { "Unable to read receive timestamp count.", + { kVarInt62OneByte + 0x01 }}, + { "Unable to read receive timestamp delta.", + { kVarInt62TwoBytes + 0x10, 0x00 }}, + + // Timestamp range 3 (two packets). + { "Unable to read receive timestamp gap.", + { kVarInt62OneByte + 0x0a }}, + { "Unable to read receive timestamp count.", + { kVarInt62OneByte + 0x02 }}, + { "Unable to read receive timestamp delta.", + { kVarInt62OneByte + 0x10 }}, + { "Unable to read receive timestamp delta.", + { kVarInt62TwoBytes + 0x01, 0x00 }}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet_ietf)); + + framer_.set_process_timestamps(true); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + EXPECT_THAT(framer_.error(), IsQuicNoError()); + ASSERT_TRUE(visitor_.header_.get()); + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + + EXPECT_THAT(frame.received_packet_times, + ContainerEq(PacketTimeVector{ + // Timestamp Range 1. + {LargestAcked(frame) - 2, CreationTimePlus(0x29ffffff)}, + {LargestAcked(frame) - 3, CreationTimePlus(0x29ffeeee)}, + {LargestAcked(frame) - 4, CreationTimePlus(0x29ffeeed)}, + // Timestamp Range 2. + {LargestAcked(frame) - 11, CreationTimePlus(0x29ffdeed)}, + // Timestamp Range 3. + {LargestAcked(frame) - 21, CreationTimePlus(0x29ffdedd)}, + {LargestAcked(frame) - 22, CreationTimePlus(0x29ffdddd)}, + })); +} + +TEST_P(QuicFramerTest, AckFrameReceiveTimestampWithExponent) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); + // clang-format off + PacketFragments packet_ietf = { + // 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 (IETF_ACK_RECEIVE_TIMESTAMPS frame) + {"", + { 0x22 }}, + // largest acked + {"Unable to read largest acked.", + { kVarInt62TwoBytes + 0x12, 0x34 }}, // = 4660 + // Zero delta time. + {"Unable to read ack delay time.", + { kVarInt62OneByte + 0x00 }}, + // number of additional ack blocks + {"Unable to read ack block count.", + { kVarInt62OneByte + 0x00 }}, + // first ack block length. + {"Unable to read first ack block length.", + { kVarInt62OneByte + 0x00 }}, // 1st block length = 1 + + // Receive Timestamps. + { "Unable to read receive timestamp range count.", + { kVarInt62OneByte + 0x01 }}, + { "Unable to read receive timestamp gap.", + { kVarInt62OneByte + 0x00 }}, + { "Unable to read receive timestamp count.", + { kVarInt62OneByte + 0x03 }}, + { "Unable to read receive timestamp delta.", + { kVarInt62TwoBytes + 0x29, 0xff}}, + { "Unable to read receive timestamp delta.", + { kVarInt62TwoBytes + 0x11, 0x11 }}, + { "Unable to read receive timestamp delta.", + { kVarInt62OneByte + 0x01}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet_ietf)); + + framer_.set_receive_timestamps_exponent(3); + framer_.set_process_timestamps(true); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + EXPECT_THAT(framer_.error(), IsQuicNoError()); + ASSERT_TRUE(visitor_.header_.get()); + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + + EXPECT_THAT(frame.received_packet_times, + ContainerEq(PacketTimeVector{ + // Timestamp Range 1. + {LargestAcked(frame), CreationTimePlus(0x29ff << 3)}, + {LargestAcked(frame) - 1, CreationTimePlus(0x18ee << 3)}, + {LargestAcked(frame) - 2, CreationTimePlus(0x18ed << 3)}, + })); +} + +TEST_P(QuicFramerTest, AckFrameReceiveTimestampGapTooHigh) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); + // clang-format off + PacketFragments packet_ietf = { + // 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 (IETF_ACK_RECEIVE_TIMESTAMPS frame) + {"", + { 0x22 }}, + // largest acked + {"Unable to read largest acked.", + { kVarInt62TwoBytes + 0x12, 0x34 }}, // = 4660 + // Zero delta time. + {"Unable to read ack delay time.", + { kVarInt62OneByte + 0x00 }}, + // number of additional ack blocks + {"Unable to read ack block count.", + { kVarInt62OneByte + 0x00 }}, + // first ack block length. + {"Unable to read first ack block length.", + { kVarInt62OneByte + 0x00 }}, // 1st block length = 1 + + // Receive Timestamps. + { "Unable to read receive timestamp range count.", + { kVarInt62OneByte + 0x01 }}, + { "Unable to read receive timestamp gap.", + { kVarInt62FourBytes + 0x12, 0x34, 0x56, 0x79 }}, + { "Unable to read receive timestamp count.", + { kVarInt62OneByte + 0x01 }}, + { "Unable to read receive timestamp delta.", + { kVarInt62TwoBytes + 0x29, 0xff}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet_ietf)); + + framer_.set_process_timestamps(true); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_TRUE(absl::StartsWith(framer_.detailed_error(), + "Receive timestamp gap too high.")); +} + +TEST_P(QuicFramerTest, AckFrameReceiveTimestampCountTooHigh) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); + // clang-format off + PacketFragments packet_ietf = { + // 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 (IETF_ACK_RECEIVE_TIMESTAMPS frame) + {"", + { 0x22 }}, + // largest acked + {"Unable to read largest acked.", + { kVarInt62TwoBytes + 0x12, 0x34 }}, // = 4660 + // Zero delta time. + {"Unable to read ack delay time.", + { kVarInt62OneByte + 0x00 }}, + // number of additional ack blocks + {"Unable to read ack block count.", + { kVarInt62OneByte + 0x00 }}, + // first ack block length. + {"Unable to read first ack block length.", + { kVarInt62OneByte + 0x00 }}, // 1st block length = 1 + + // Receive Timestamps. + { "Unable to read receive timestamp range count.", + { kVarInt62OneByte + 0x01 }}, + { "Unable to read receive timestamp gap.", + { kVarInt62OneByte + 0x02 }}, + { "Unable to read receive timestamp count.", + { kVarInt62OneByte + 0x02 }}, + { "Unable to read receive timestamp delta.", + { kVarInt62OneByte + 0x0a}}, + { "Unable to read receive timestamp delta.", + { kVarInt62OneByte + 0x0b}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet_ietf)); + + framer_.set_process_timestamps(true); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_TRUE(absl::StartsWith(framer_.detailed_error(), + "Receive timestamp delta too high.")); +} + +TEST_P(QuicFramerTest, AckFrameReceiveTimestampDeltaTooHigh) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); + // clang-format off + PacketFragments packet_ietf = { + // 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 (IETF_ACK_RECEIVE_TIMESTAMPS frame) + {"", + { 0x22 }}, + // largest acked + {"Unable to read largest acked.", + { kVarInt62TwoBytes + 0x12, 0x34 }}, // = 4660 + // Zero delta time. + {"Unable to read ack delay time.", + { kVarInt62OneByte + 0x00 }}, + // number of additional ack blocks + {"Unable to read ack block count.", + { kVarInt62OneByte + 0x00 }}, + // first ack block length. + {"Unable to read first ack block length.", + { kVarInt62OneByte + 0x00 }}, // 1st block length = 1 + + // Receive Timestamps. + { "Unable to read receive timestamp range count.", + { kVarInt62OneByte + 0x01 }}, + { "Unable to read receive timestamp gap.", + { kVarInt62OneByte + 0x02 }}, + { "Unable to read receive timestamp count.", + { kVarInt62FourBytes + 0x12, 0x34, 0x56, 0x77 }}, + { "Unable to read receive timestamp delta.", + { kVarInt62TwoBytes + 0x29, 0xff}}, + }; + // clang-format on + + std::unique_ptr<QuicEncryptedPacket> encrypted( + AssemblePacketFromFragments(packet_ietf)); + + framer_.set_process_timestamps(true); + EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); + EXPECT_TRUE(absl::StartsWith(framer_.detailed_error(), + "Receive timestamp count too high.")); } TEST_P(QuicFramerTest, AckFrameTimeStampDeltaTooHigh) { @@ -4319,7 +4688,7 @@ TEST_P(QuicFramerTest, RstStreamFrame) { {0x00, 0x00, 0x00, 0x06}} }; - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -4347,7 +4716,7 @@ TEST_P(QuicFramerTest, RstStreamFrame) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) - ? packet99 + ? packet_ietf : (framer_.version().HasIetfInvariantHeader() ? packet46 : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); @@ -4424,7 +4793,7 @@ TEST_P(QuicFramerTest, ConnectionCloseFrame) { } }; - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -4458,7 +4827,7 @@ TEST_P(QuicFramerTest, ConnectionCloseFrame) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) - ? packet99 + ? packet_ietf : (framer_.version().HasIetfInvariantHeader() ? packet46 : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); @@ -4549,7 +4918,7 @@ TEST_P(QuicFramerTest, ConnectionCloseFrameWithUnknownErrorCode) { } }; - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -4583,7 +4952,7 @@ TEST_P(QuicFramerTest, ConnectionCloseFrameWithUnknownErrorCode) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) - ? packet99 + ? packet_ietf : (framer_.version().HasIetfInvariantHeader() ? packet46 : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); @@ -4679,7 +5048,7 @@ TEST_P(QuicFramerTest, ConnectionCloseFrameWithExtractedInfoIgnoreGCuic) { } }; - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -4713,7 +5082,7 @@ TEST_P(QuicFramerTest, ConnectionCloseFrameWithExtractedInfoIgnoreGCuic) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) - ? packet99 + ? packet_ietf : (framer_.version().HasIetfInvariantHeader() ? packet46 : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); @@ -4756,7 +5125,7 @@ TEST_P(QuicFramerTest, ApplicationCloseFrame) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -4786,7 +5155,7 @@ TEST_P(QuicFramerTest, ApplicationCloseFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -4805,7 +5174,7 @@ TEST_P(QuicFramerTest, ApplicationCloseFrame) { ASSERT_EQ(0u, visitor_.ack_frames_.size()); - CheckFramingBoundaries(packet99, QUIC_INVALID_CONNECTION_CLOSE_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_CONNECTION_CLOSE_DATA); } // Check that we can extract an error code from an application close. @@ -4817,7 +5186,7 @@ TEST_P(QuicFramerTest, ApplicationCloseFrameExtract) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -4848,7 +5217,7 @@ TEST_P(QuicFramerTest, ApplicationCloseFrameExtract) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -4867,7 +5236,7 @@ TEST_P(QuicFramerTest, ApplicationCloseFrameExtract) { ASSERT_EQ(0u, visitor_.ack_frames_.size()); - CheckFramingBoundaries(packet99, QUIC_INVALID_CONNECTION_CLOSE_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_CONNECTION_CLOSE_DATA); } TEST_P(QuicFramerTest, GoAwayFrame) { @@ -5131,7 +5500,7 @@ TEST_P(QuicFramerTest, MaxDataFrame) { } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -5152,7 +5521,7 @@ TEST_P(QuicFramerTest, MaxDataFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -5165,7 +5534,7 @@ TEST_P(QuicFramerTest, MaxDataFrame) { visitor_.window_update_frame_.stream_id); EXPECT_EQ(kStreamOffset, visitor_.window_update_frame_.max_data); - CheckFramingBoundaries(packet99, QUIC_INVALID_MAX_DATA_FRAME_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_MAX_DATA_FRAME_DATA); } TEST_P(QuicFramerTest, MaxStreamDataFrame) { @@ -5175,7 +5544,7 @@ TEST_P(QuicFramerTest, MaxStreamDataFrame) { } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -5199,7 +5568,7 @@ TEST_P(QuicFramerTest, MaxStreamDataFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -5211,7 +5580,7 @@ TEST_P(QuicFramerTest, MaxStreamDataFrame) { EXPECT_EQ(kStreamId, visitor_.window_update_frame_.stream_id); EXPECT_EQ(kStreamOffset, visitor_.window_update_frame_.max_data); - CheckFramingBoundaries(packet99, QUIC_INVALID_MAX_STREAM_DATA_FRAME_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_MAX_STREAM_DATA_FRAME_DATA); } TEST_P(QuicFramerTest, BlockedFrame) { @@ -5253,7 +5622,7 @@ TEST_P(QuicFramerTest, BlockedFrame) { {0x01, 0x02, 0x03, 0x04}}, }; - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -5277,7 +5646,7 @@ TEST_P(QuicFramerTest, BlockedFrame) { PacketFragments& fragments = VersionHasIetfQuicFrames(framer_.transport_version()) - ? packet99 + ? packet_ietf : (framer_.version().HasIetfInvariantHeader() ? packet46 : packet); std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); @@ -5330,7 +5699,7 @@ TEST_P(QuicFramerTest, PingFrame) { 0x07, }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -5345,11 +5714,11 @@ TEST_P(QuicFramerTest, PingFrame) { QuicEncryptedPacket encrypted( AsChars(VersionHasIetfQuicFrames(framer_.transport_version()) - ? packet99 + ? packet_ietf : (framer_.version().HasIetfInvariantHeader() ? packet46 : packet)), VersionHasIetfQuicFrames(framer_.transport_version()) - ? ABSL_ARRAYSIZE(packet99) + ? ABSL_ARRAYSIZE(packet_ietf) : (framer_.version().HasIetfInvariantHeader() ? ABSL_ARRAYSIZE(packet46) : ABSL_ARRAYSIZE(packet)), @@ -5476,7 +5845,7 @@ TEST_P(QuicFramerTest, MessageFrame) { {{}, {'m', 'e', 's', 's', 'a', 'g', 'e', '2'}}, }; - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -5506,7 +5875,7 @@ TEST_P(QuicFramerTest, MessageFrame) { std::unique_ptr<QuicEncryptedPacket> encrypted; if (VersionHasIetfQuicFrames(framer_.transport_version())) { - encrypted = AssemblePacketFromFragments(packet99); + encrypted = AssemblePacketFromFragments(packet_ietf); } else { encrypted = AssemblePacketFromFragments(packet46); } @@ -5523,7 +5892,7 @@ TEST_P(QuicFramerTest, MessageFrame) { EXPECT_EQ(8u, visitor_.message_frames_[1]->message_length); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - CheckFramingBoundaries(packet99, QUIC_INVALID_MESSAGE_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_MESSAGE_DATA); } else { CheckFramingBoundaries(packet46, QUIC_INVALID_MESSAGE_DATA); } @@ -6153,7 +6522,7 @@ TEST_P(QuicFramerTest, BuildPaddingFramePacket) { 0x00, 0x00, 0x00, 0x00 }; - unsigned char packet99[kMaxOutgoingPacketSize] = { + unsigned char packet_ietf[kMaxOutgoingPacketSize] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -6169,7 +6538,7 @@ TEST_P(QuicFramerTest, BuildPaddingFramePacket) { unsigned char* p = packet; if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; + p = packet_ietf; } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; } @@ -6258,7 +6627,7 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithNewPaddingFrame) { 0x00, 0x00, }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -6292,8 +6661,8 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithNewPaddingFrame) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; p_size = ABSL_ARRAYSIZE(packet46); @@ -6342,7 +6711,7 @@ TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) { 0x00, 0x00, 0x00, 0x00 }; - unsigned char packet99[kMaxOutgoingPacketSize] = { + unsigned char packet_ietf[kMaxOutgoingPacketSize] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -6358,7 +6727,7 @@ TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) { unsigned char* p = packet; if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; + p = packet_ietf; } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; } @@ -6417,7 +6786,7 @@ TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) { 0x00, 0x00, 0x00, 0x00 }; - unsigned char packet99[kMaxOutgoingPacketSize] = { + unsigned char packet_ietf[kMaxOutgoingPacketSize] = { // type (short header, 2 byte packet number) 0x41, // connection_id @@ -6433,7 +6802,7 @@ TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) { unsigned char* p = packet; if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; + p = packet_ietf; } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; } @@ -6492,7 +6861,7 @@ TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) { 0x00, 0x00, 0x00, 0x00 }; - unsigned char packet99[kMaxOutgoingPacketSize] = { + unsigned char packet_ietf[kMaxOutgoingPacketSize] = { // type (short header, 1 byte packet number) 0x40, // connection_id @@ -6508,7 +6877,7 @@ TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) { unsigned char* p = packet; if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; + p = packet_ietf; } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; } @@ -6588,7 +6957,7 @@ TEST_P(QuicFramerTest, BuildStreamFramePacket) { 'r', 'l', 'd', '!', }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -6616,8 +6985,8 @@ TEST_P(QuicFramerTest, BuildStreamFramePacket) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; p_size = ABSL_ARRAYSIZE(packet46); @@ -6712,7 +7081,7 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (long header with packet type ZERO_RTT_PROTECTED) 0xD3, // version tag @@ -6746,8 +7115,8 @@ TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasLongHeaderLengths()) { p = packet49; p_size = ABSL_ARRAYSIZE(packet49); @@ -6803,7 +7172,7 @@ TEST_P(QuicFramerTest, BuildCryptoFramePacket) { 'r', 'l', 'd', '!', }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -6828,8 +7197,8 @@ TEST_P(QuicFramerTest, BuildCryptoFramePacket) { unsigned char* packet = packet48; size_t packet_size = ABSL_ARRAYSIZE(packet48); if (framer_.version().HasIetfQuicFrames()) { - packet = packet99; - packet_size = ABSL_ARRAYSIZE(packet99); + packet = packet_ietf; + packet_size = ABSL_ARRAYSIZE(packet_ietf); } std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); @@ -6874,7 +7243,7 @@ TEST_P(QuicFramerTest, CryptoFrame) { 'r', 'l', 'd', '!'}}, }; - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -6903,7 +7272,7 @@ TEST_P(QuicFramerTest, CryptoFrame) { // clang-format on PacketFragments& fragments = - framer_.version().HasIetfQuicFrames() ? packet99 : packet48; + framer_.version().HasIetfQuicFrames() ? packet_ietf : packet48; std::unique_ptr<QuicEncryptedPacket> encrypted( AssemblePacketFromFragments(fragments)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); @@ -7078,7 +7447,7 @@ TEST_P(QuicFramerTest, BuildAckFramePacketOneAckBlock) { 0x00, }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -7101,8 +7470,8 @@ TEST_P(QuicFramerTest, BuildAckFramePacketOneAckBlock) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; p_size = ABSL_ARRAYSIZE(packet46); @@ -7171,7 +7540,7 @@ TEST_P(QuicFramerTest, BuildAckFramePacketOneAckBlockMaxLength) { }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -7194,8 +7563,8 @@ TEST_P(QuicFramerTest, BuildAckFramePacketOneAckBlockMaxLength) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; p_size = ABSL_ARRAYSIZE(packet46); @@ -7304,7 +7673,7 @@ TEST_P(QuicFramerTest, BuildAckFramePacketMultipleAckBlocks) { 0x00, }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -7342,8 +7711,8 @@ TEST_P(QuicFramerTest, BuildAckFramePacketMultipleAckBlocks) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; p_size = ABSL_ARRAYSIZE(packet46); @@ -7563,7 +7932,7 @@ TEST_P(QuicFramerTest, BuildAckFramePacketMaxAckBlocks) { 0x00, }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -7651,8 +8020,8 @@ TEST_P(QuicFramerTest, BuildAckFramePacketMaxAckBlocks) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; p_size = ABSL_ARRAYSIZE(packet46); @@ -7762,7 +8131,7 @@ TEST_P(QuicFramerTest, BuildRstFramePacketQuic) { 0x05, 0x06, 0x07, 0x08, }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short packet, 4 byte packet number) 0x43, // connection_id @@ -7789,8 +8158,8 @@ TEST_P(QuicFramerTest, BuildRstFramePacketQuic) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; p_size = ABSL_ARRAYSIZE(packet46); @@ -7857,7 +8226,7 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) { 'n', }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -7884,8 +8253,8 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; p_size = ABSL_ARRAYSIZE(packet46); @@ -7961,7 +8330,7 @@ TEST_P(QuicFramerTest, BuildCloseFramePacketExtendedInfo) { 'n', }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -7990,8 +8359,8 @@ TEST_P(QuicFramerTest, BuildCloseFramePacketExtendedInfo) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; p_size = ABSL_ARRAYSIZE(packet46); @@ -8116,7 +8485,7 @@ TEST_P(QuicFramerTest, BuildTruncatedCloseFramePacket) { 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -8171,8 +8540,8 @@ TEST_P(QuicFramerTest, BuildTruncatedCloseFramePacket) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; p_size = ABSL_ARRAYSIZE(packet46); @@ -8206,7 +8575,7 @@ TEST_P(QuicFramerTest, BuildApplicationCloseFramePacket) { // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -8232,8 +8601,8 @@ TEST_P(QuicFramerTest, BuildApplicationCloseFramePacket) { ASSERT_TRUE(data != nullptr); quiche::test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), AsChars(packet99), - ABSL_ARRAYSIZE(packet99)); + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); } TEST_P(QuicFramerTest, BuildTruncatedApplicationCloseFramePacket) { @@ -8259,7 +8628,7 @@ TEST_P(QuicFramerTest, BuildTruncatedApplicationCloseFramePacket) { QuicFrames frames = {QuicFrame(&app_close_frame)}; // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -8313,8 +8682,8 @@ TEST_P(QuicFramerTest, BuildTruncatedApplicationCloseFramePacket) { ASSERT_TRUE(data != nullptr); quiche::test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), AsChars(packet99), - ABSL_ARRAYSIZE(packet99)); + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); } TEST_P(QuicFramerTest, BuildGoAwayPacket) { @@ -8585,7 +8954,7 @@ TEST_P(QuicFramerTest, BuildWindowUpdatePacket) { 0x55, 0x66, 0x77, 0x88, }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -8609,8 +8978,8 @@ TEST_P(QuicFramerTest, BuildWindowUpdatePacket) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; p_size = ABSL_ARRAYSIZE(packet46); @@ -8639,7 +9008,7 @@ TEST_P(QuicFramerTest, BuildMaxStreamDataPacket) { QuicFrames frames = {QuicFrame(&window_update_frame)}; // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -8661,8 +9030,8 @@ TEST_P(QuicFramerTest, BuildMaxStreamDataPacket) { ASSERT_TRUE(data != nullptr); quiche::test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), AsChars(packet99), - ABSL_ARRAYSIZE(packet99)); + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); } TEST_P(QuicFramerTest, BuildMaxDataPacket) { @@ -8685,7 +9054,7 @@ TEST_P(QuicFramerTest, BuildMaxDataPacket) { QuicFrames frames = {QuicFrame(&window_update_frame)}; // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -8705,8 +9074,8 @@ TEST_P(QuicFramerTest, BuildMaxDataPacket) { ASSERT_TRUE(data != nullptr); quiche::test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), AsChars(packet99), - ABSL_ARRAYSIZE(packet99)); + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); } TEST_P(QuicFramerTest, BuildBlockedPacket) { @@ -8760,7 +9129,7 @@ TEST_P(QuicFramerTest, BuildBlockedPacket) { 0x01, 0x02, 0x03, 0x04, }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short packet, 4 byte packet number) 0x43, // connection_id @@ -8781,8 +9150,8 @@ TEST_P(QuicFramerTest, BuildBlockedPacket) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; p_size = ABSL_ARRAYSIZE(packet46); @@ -8827,7 +9196,7 @@ TEST_P(QuicFramerTest, BuildPingPacket) { 0x07, }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -8842,7 +9211,7 @@ TEST_P(QuicFramerTest, BuildPingPacket) { unsigned char* p = packet; if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; + p = packet_ietf; } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; } @@ -8973,7 +9342,7 @@ TEST_P(QuicFramerTest, BuildMessagePacket) { 'm', 'e', 's', 's', 'a', 'g', 'e', '2' }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -8996,7 +9365,7 @@ TEST_P(QuicFramerTest, BuildMessagePacket) { unsigned char* p = packet46; if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; + p = packet_ietf; } std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); @@ -9043,7 +9412,7 @@ TEST_P(QuicFramerTest, BuildMtuDiscoveryPacket) { 0x07, }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -9061,7 +9430,7 @@ TEST_P(QuicFramerTest, BuildMtuDiscoveryPacket) { unsigned char* p = packet; if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; + p = packet_ietf; } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; } @@ -9238,8 +9607,7 @@ TEST_P(QuicFramerTest, BuildPublicResetPacketWithEndpointId) { } TEST_P(QuicFramerTest, BuildIetfStatelessResetPacket) { - if (GetQuicRestartFlag(quic_fix_stateless_reset2)) { - // clang-format off + // clang-format off unsigned char packet[] = { // 1st byte 01XX XXXX 0x40, @@ -9249,76 +9617,45 @@ TEST_P(QuicFramerTest, BuildIetfStatelessResetPacket) { 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f }; - // clang-format on - - // Build the minimal stateless reset packet. - std::unique_ptr<QuicEncryptedPacket> data( - framer_.BuildIetfStatelessResetPacket( - FramerTestConnectionId(), - QuicFramer::GetMinStatelessResetPacketLength() + 1, - kTestStatelessResetToken)); - ASSERT_TRUE(data); - EXPECT_EQ(QuicFramer::GetMinStatelessResetPacketLength(), data->length()); - // Verify the first 2 bits are 01. - EXPECT_FALSE(data->data()[0] & FLAGS_LONG_HEADER); - EXPECT_TRUE(data->data()[0] & FLAGS_FIXED_BIT); - // Verify stateless reset token. - quiche::test::CompareCharArraysWithHexError( - "constructed packet", - data->data() + data->length() - kStatelessResetTokenLength, - kStatelessResetTokenLength, - AsChars(packet) + ABSL_ARRAYSIZE(packet) - kStatelessResetTokenLength, - kStatelessResetTokenLength); - - // Packets with length <= minimal stateless reset does not trigger stateless - // reset. - std::unique_ptr<QuicEncryptedPacket> data2( - framer_.BuildIetfStatelessResetPacket( - FramerTestConnectionId(), - QuicFramer::GetMinStatelessResetPacketLength(), - kTestStatelessResetToken)); - ASSERT_FALSE(data2); - - // Do not send stateless reset >= minimal stateless reset + 1 + max - // connection ID length. - std::unique_ptr<QuicEncryptedPacket> data3( - framer_.BuildIetfStatelessResetPacket(FramerTestConnectionId(), 1000, - kTestStatelessResetToken)); - ASSERT_TRUE(data3); - EXPECT_EQ(QuicFramer::GetMinStatelessResetPacketLength() + 1 + - kQuicMaxConnectionIdWithLengthPrefixLength, - data3->length()); - return; - } - // clang-format off - unsigned char packet[] = { - // type (short header, 1 byte packet number) - 0x70, - // random packet number - 0xFE, - // stateless reset token - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, - }; // clang-format on + // Build the minimal stateless reset packet. std::unique_ptr<QuicEncryptedPacket> data( - framer_.BuildIetfStatelessResetPacket(FramerTestConnectionId(), 0, - kTestStatelessResetToken)); - ASSERT_TRUE(data != nullptr); - // Skip packet number byte which is random in stateless reset packet. - quiche::test::CompareCharArraysWithHexError( - "constructed packet", data->data(), 1, AsChars(packet), 1); - const size_t random_bytes_length = - data->length() - kPacketHeaderTypeSize - kStatelessResetTokenLength; - EXPECT_EQ(kMinRandomBytesLengthInStatelessReset, random_bytes_length); - // Verify stateless reset token is correct. + framer_.BuildIetfStatelessResetPacket( + FramerTestConnectionId(), + QuicFramer::GetMinStatelessResetPacketLength() + 1, + kTestStatelessResetToken)); + ASSERT_TRUE(data); + EXPECT_EQ(QuicFramer::GetMinStatelessResetPacketLength(), data->length()); + // Verify the first 2 bits are 01. + EXPECT_FALSE(data->data()[0] & FLAGS_LONG_HEADER); + EXPECT_TRUE(data->data()[0] & FLAGS_FIXED_BIT); + // Verify stateless reset token. quiche::test::CompareCharArraysWithHexError( "constructed packet", data->data() + data->length() - kStatelessResetTokenLength, kStatelessResetTokenLength, AsChars(packet) + ABSL_ARRAYSIZE(packet) - kStatelessResetTokenLength, kStatelessResetTokenLength); + + // Packets with length <= minimal stateless reset does not trigger stateless + // reset. + std::unique_ptr<QuicEncryptedPacket> data2( + framer_.BuildIetfStatelessResetPacket( + FramerTestConnectionId(), + QuicFramer::GetMinStatelessResetPacketLength(), + kTestStatelessResetToken)); + ASSERT_FALSE(data2); + + // Do not send stateless reset >= minimal stateless reset + 1 + max + // connection ID length. + std::unique_ptr<QuicEncryptedPacket> data3( + framer_.BuildIetfStatelessResetPacket(FramerTestConnectionId(), 1000, + kTestStatelessResetToken)); + ASSERT_TRUE(data3); + EXPECT_EQ(QuicFramer::GetMinStatelessResetPacketLength() + 1 + + kQuicMaxConnectionIdWithLengthPrefixLength, + data3->length()); } TEST_P(QuicFramerTest, EncryptPacket) { @@ -9758,7 +10095,7 @@ TEST_P(QuicFramerTest, StopPacketProcessing) { 0x9A, 0xBE, }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -9810,8 +10147,8 @@ TEST_P(QuicFramerTest, StopPacketProcessing) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; p_size = ABSL_ARRAYSIZE(packet46); @@ -9927,7 +10264,7 @@ TEST_P(QuicFramerTest, IetfBlockedFrame) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -9947,7 +10284,7 @@ TEST_P(QuicFramerTest, IetfBlockedFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -9958,7 +10295,7 @@ TEST_P(QuicFramerTest, IetfBlockedFrame) { EXPECT_EQ(kStreamOffset, visitor_.blocked_frame_.offset); - CheckFramingBoundaries(packet99, QUIC_INVALID_BLOCKED_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_BLOCKED_DATA); } TEST_P(QuicFramerTest, BuildIetfBlockedPacket) { @@ -9979,7 +10316,7 @@ TEST_P(QuicFramerTest, BuildIetfBlockedPacket) { QuicFrames frames = {QuicFrame(&frame)}; // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -9998,8 +10335,8 @@ TEST_P(QuicFramerTest, BuildIetfBlockedPacket) { ASSERT_TRUE(data != nullptr); quiche::test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), AsChars(packet99), - ABSL_ARRAYSIZE(packet99)); + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); } TEST_P(QuicFramerTest, IetfStreamBlockedFrame) { @@ -10010,7 +10347,7 @@ TEST_P(QuicFramerTest, IetfStreamBlockedFrame) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -10032,7 +10369,7 @@ TEST_P(QuicFramerTest, IetfStreamBlockedFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -10044,7 +10381,7 @@ TEST_P(QuicFramerTest, IetfStreamBlockedFrame) { EXPECT_EQ(kStreamId, visitor_.blocked_frame_.stream_id); EXPECT_EQ(kStreamOffset, visitor_.blocked_frame_.offset); - CheckFramingBoundaries(packet99, QUIC_INVALID_STREAM_BLOCKED_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_STREAM_BLOCKED_DATA); } TEST_P(QuicFramerTest, BuildIetfStreamBlockedPacket) { @@ -10065,7 +10402,7 @@ TEST_P(QuicFramerTest, BuildIetfStreamBlockedPacket) { QuicFrames frames = {QuicFrame(&frame)}; // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -10086,8 +10423,8 @@ TEST_P(QuicFramerTest, BuildIetfStreamBlockedPacket) { ASSERT_TRUE(data != nullptr); quiche::test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), AsChars(packet99), - ABSL_ARRAYSIZE(packet99)); + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); } TEST_P(QuicFramerTest, BiDiMaxStreamsFrame) { @@ -10098,7 +10435,7 @@ TEST_P(QuicFramerTest, BiDiMaxStreamsFrame) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -10118,7 +10455,7 @@ TEST_P(QuicFramerTest, BiDiMaxStreamsFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -10129,7 +10466,7 @@ TEST_P(QuicFramerTest, BiDiMaxStreamsFrame) { EXPECT_EQ(3u, visitor_.max_streams_frame_.stream_count); EXPECT_FALSE(visitor_.max_streams_frame_.unidirectional); - CheckFramingBoundaries(packet99, QUIC_MAX_STREAMS_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_MAX_STREAMS_DATA); } TEST_P(QuicFramerTest, UniDiMaxStreamsFrame) { @@ -10140,7 +10477,7 @@ TEST_P(QuicFramerTest, UniDiMaxStreamsFrame) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -10158,7 +10495,7 @@ TEST_P(QuicFramerTest, UniDiMaxStreamsFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); @@ -10170,7 +10507,7 @@ TEST_P(QuicFramerTest, UniDiMaxStreamsFrame) { EXPECT_EQ(3u, visitor_.max_streams_frame_.stream_count); EXPECT_TRUE(visitor_.max_streams_frame_.unidirectional); - CheckFramingBoundaries(packet99, QUIC_MAX_STREAMS_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_MAX_STREAMS_DATA); } TEST_P(QuicFramerTest, ServerUniDiMaxStreamsFrame) { @@ -10181,7 +10518,7 @@ TEST_P(QuicFramerTest, ServerUniDiMaxStreamsFrame) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -10201,7 +10538,7 @@ TEST_P(QuicFramerTest, ServerUniDiMaxStreamsFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -10212,7 +10549,7 @@ TEST_P(QuicFramerTest, ServerUniDiMaxStreamsFrame) { EXPECT_EQ(3u, visitor_.max_streams_frame_.stream_count); EXPECT_TRUE(visitor_.max_streams_frame_.unidirectional); - CheckFramingBoundaries(packet99, QUIC_MAX_STREAMS_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_MAX_STREAMS_DATA); } TEST_P(QuicFramerTest, ClientUniDiMaxStreamsFrame) { @@ -10223,7 +10560,7 @@ TEST_P(QuicFramerTest, ClientUniDiMaxStreamsFrame) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -10241,7 +10578,7 @@ TEST_P(QuicFramerTest, ClientUniDiMaxStreamsFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); @@ -10253,7 +10590,7 @@ TEST_P(QuicFramerTest, ClientUniDiMaxStreamsFrame) { EXPECT_EQ(3u, visitor_.max_streams_frame_.stream_count); EXPECT_TRUE(visitor_.max_streams_frame_.unidirectional); - CheckFramingBoundaries(packet99, QUIC_MAX_STREAMS_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_MAX_STREAMS_DATA); } // The following four tests ensure that the framer can deserialize a stream @@ -10270,7 +10607,7 @@ TEST_P(QuicFramerTest, BiDiMaxStreamsFrameTooBig) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -10287,8 +10624,8 @@ TEST_P(QuicFramerTest, BiDiMaxStreamsFrameTooBig) { }; // clang-format on - QuicEncryptedPacket encrypted(AsChars(packet99), ABSL_ARRAYSIZE(packet99), - false); + QuicEncryptedPacket encrypted(AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); ASSERT_TRUE(visitor_.header_.get()); @@ -10308,7 +10645,7 @@ TEST_P(QuicFramerTest, ClientBiDiMaxStreamsFrameTooBig) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // Test runs in client mode, no connection id @@ -10324,8 +10661,8 @@ TEST_P(QuicFramerTest, ClientBiDiMaxStreamsFrameTooBig) { }; // clang-format on - QuicEncryptedPacket encrypted(AsChars(packet99), ABSL_ARRAYSIZE(packet99), - false); + QuicEncryptedPacket encrypted(AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf), false); QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); @@ -10347,7 +10684,7 @@ TEST_P(QuicFramerTest, ServerUniDiMaxStreamsFrameTooBig) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -10364,8 +10701,8 @@ TEST_P(QuicFramerTest, ServerUniDiMaxStreamsFrameTooBig) { }; // clang-format on - QuicEncryptedPacket encrypted(AsChars(packet99), ABSL_ARRAYSIZE(packet99), - false); + QuicEncryptedPacket encrypted(AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -10386,7 +10723,7 @@ TEST_P(QuicFramerTest, ClientUniDiMaxStreamsFrameTooBig) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // Test runs in client mode, no connection id @@ -10402,8 +10739,8 @@ TEST_P(QuicFramerTest, ClientUniDiMaxStreamsFrameTooBig) { }; // clang-format on - QuicEncryptedPacket encrypted(AsChars(packet99), ABSL_ARRAYSIZE(packet99), - false); + QuicEncryptedPacket encrypted(AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf), false); QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); @@ -10426,7 +10763,7 @@ TEST_P(QuicFramerTest, MaxStreamsFrameZeroCount) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -10440,8 +10777,8 @@ TEST_P(QuicFramerTest, MaxStreamsFrameZeroCount) { }; // clang-format on - QuicEncryptedPacket encrypted(AsChars(packet99), ABSL_ARRAYSIZE(packet99), - false); + QuicEncryptedPacket encrypted(AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf), false); EXPECT_TRUE(framer_.ProcessPacket(encrypted)); } @@ -10453,7 +10790,7 @@ TEST_P(QuicFramerTest, ServerBiDiStreamsBlockedFrame) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -10473,7 +10810,7 @@ TEST_P(QuicFramerTest, ServerBiDiStreamsBlockedFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -10485,7 +10822,7 @@ TEST_P(QuicFramerTest, ServerBiDiStreamsBlockedFrame) { EXPECT_EQ(0u, visitor_.max_streams_frame_.stream_count); EXPECT_TRUE(visitor_.max_streams_frame_.unidirectional); - CheckFramingBoundaries(packet99, QUIC_MAX_STREAMS_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_MAX_STREAMS_DATA); } TEST_P(QuicFramerTest, BiDiStreamsBlockedFrame) { @@ -10496,7 +10833,7 @@ TEST_P(QuicFramerTest, BiDiStreamsBlockedFrame) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -10517,7 +10854,7 @@ TEST_P(QuicFramerTest, BiDiStreamsBlockedFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -10529,7 +10866,7 @@ TEST_P(QuicFramerTest, BiDiStreamsBlockedFrame) { EXPECT_EQ(3u, visitor_.streams_blocked_frame_.stream_count); EXPECT_FALSE(visitor_.streams_blocked_frame_.unidirectional); - CheckFramingBoundaries(packet99, QUIC_STREAMS_BLOCKED_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_STREAMS_BLOCKED_DATA); } TEST_P(QuicFramerTest, UniDiStreamsBlockedFrame) { @@ -10540,7 +10877,7 @@ TEST_P(QuicFramerTest, UniDiStreamsBlockedFrame) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -10561,7 +10898,7 @@ TEST_P(QuicFramerTest, UniDiStreamsBlockedFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -10572,7 +10909,7 @@ TEST_P(QuicFramerTest, UniDiStreamsBlockedFrame) { EXPECT_EQ(3u, visitor_.streams_blocked_frame_.stream_count); EXPECT_TRUE(visitor_.streams_blocked_frame_.unidirectional); - CheckFramingBoundaries(packet99, QUIC_STREAMS_BLOCKED_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_STREAMS_BLOCKED_DATA); } TEST_P(QuicFramerTest, ClientUniDiStreamsBlockedFrame) { @@ -10583,7 +10920,7 @@ TEST_P(QuicFramerTest, ClientUniDiStreamsBlockedFrame) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -10602,7 +10939,7 @@ TEST_P(QuicFramerTest, ClientUniDiStreamsBlockedFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); @@ -10614,7 +10951,7 @@ TEST_P(QuicFramerTest, ClientUniDiStreamsBlockedFrame) { EXPECT_EQ(3u, visitor_.streams_blocked_frame_.stream_count); EXPECT_TRUE(visitor_.streams_blocked_frame_.unidirectional); - CheckFramingBoundaries(packet99, QUIC_STREAMS_BLOCKED_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_STREAMS_BLOCKED_DATA); } // Check that when we get a STREAMS_BLOCKED frame that specifies too large @@ -10629,7 +10966,7 @@ TEST_P(QuicFramerTest, StreamsBlockedFrameTooBig) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // Test runs in client mode, no connection id @@ -10645,8 +10982,8 @@ TEST_P(QuicFramerTest, StreamsBlockedFrameTooBig) { }; // clang-format on - QuicEncryptedPacket encrypted(AsChars(packet99), ABSL_ARRAYSIZE(packet99), - false); + QuicEncryptedPacket encrypted(AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf), false); QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); EXPECT_FALSE(framer_.ProcessPacket(encrypted)); @@ -10665,7 +11002,7 @@ TEST_P(QuicFramerTest, StreamsBlockedFrameZeroCount) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -10686,7 +11023,7 @@ TEST_P(QuicFramerTest, StreamsBlockedFrameZeroCount) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -10698,7 +11035,7 @@ TEST_P(QuicFramerTest, StreamsBlockedFrameZeroCount) { EXPECT_EQ(0u, visitor_.streams_blocked_frame_.stream_count); EXPECT_TRUE(visitor_.streams_blocked_frame_.unidirectional); - CheckFramingBoundaries(packet99, QUIC_STREAMS_BLOCKED_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_STREAMS_BLOCKED_DATA); } TEST_P(QuicFramerTest, BuildBiDiStreamsBlockedPacket) { @@ -10720,7 +11057,7 @@ TEST_P(QuicFramerTest, BuildBiDiStreamsBlockedPacket) { QuicFrames frames = {QuicFrame(frame)}; // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -10739,8 +11076,8 @@ TEST_P(QuicFramerTest, BuildBiDiStreamsBlockedPacket) { ASSERT_TRUE(data != nullptr); quiche::test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), AsChars(packet99), - ABSL_ARRAYSIZE(packet99)); + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); } TEST_P(QuicFramerTest, BuildUniStreamsBlockedPacket) { @@ -10762,7 +11099,7 @@ TEST_P(QuicFramerTest, BuildUniStreamsBlockedPacket) { QuicFrames frames = {QuicFrame(frame)}; // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -10781,8 +11118,8 @@ TEST_P(QuicFramerTest, BuildUniStreamsBlockedPacket) { ASSERT_TRUE(data != nullptr); quiche::test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), AsChars(packet99), - ABSL_ARRAYSIZE(packet99)); + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); } TEST_P(QuicFramerTest, BuildBiDiMaxStreamsPacket) { @@ -10804,7 +11141,7 @@ TEST_P(QuicFramerTest, BuildBiDiMaxStreamsPacket) { QuicFrames frames = {QuicFrame(frame)}; // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -10823,8 +11160,8 @@ TEST_P(QuicFramerTest, BuildBiDiMaxStreamsPacket) { ASSERT_TRUE(data != nullptr); quiche::test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), AsChars(packet99), - ABSL_ARRAYSIZE(packet99)); + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); } TEST_P(QuicFramerTest, BuildUniDiMaxStreamsPacket) { @@ -10849,7 +11186,7 @@ TEST_P(QuicFramerTest, BuildUniDiMaxStreamsPacket) { QuicFrames frames = {QuicFrame(frame)}; // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -10868,8 +11205,8 @@ TEST_P(QuicFramerTest, BuildUniDiMaxStreamsPacket) { ASSERT_TRUE(data != nullptr); quiche::test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), AsChars(packet99), - ABSL_ARRAYSIZE(packet99)); + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); } TEST_P(QuicFramerTest, NewConnectionIdFrame) { @@ -10879,7 +11216,7 @@ TEST_P(QuicFramerTest, NewConnectionIdFrame) { } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -10908,7 +11245,7 @@ TEST_P(QuicFramerTest, NewConnectionIdFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -10928,7 +11265,7 @@ TEST_P(QuicFramerTest, NewConnectionIdFrame) { ASSERT_EQ(0u, visitor_.ack_frames_.size()); - CheckFramingBoundaries(packet99, QUIC_INVALID_NEW_CONNECTION_ID_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_NEW_CONNECTION_ID_DATA); } TEST_P(QuicFramerTest, NewConnectionIdFrameVariableLength) { @@ -10938,7 +11275,7 @@ TEST_P(QuicFramerTest, NewConnectionIdFrameVariableLength) { } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -10967,7 +11304,7 @@ TEST_P(QuicFramerTest, NewConnectionIdFrameVariableLength) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -10987,7 +11324,7 @@ TEST_P(QuicFramerTest, NewConnectionIdFrameVariableLength) { ASSERT_EQ(0u, visitor_.ack_frames_.size()); - CheckFramingBoundaries(packet99, QUIC_INVALID_NEW_CONNECTION_ID_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_NEW_CONNECTION_ID_DATA); } // Verifies that parsing a NEW_CONNECTION_ID frame with a length above the @@ -10999,7 +11336,7 @@ TEST_P(QuicFramerTest, InvalidLongNewConnectionIdFrame) { } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -11035,7 +11372,7 @@ TEST_P(QuicFramerTest, InvalidLongNewConnectionIdFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsError(QUIC_INVALID_NEW_CONNECTION_ID_DATA)); EXPECT_EQ("Invalid new connection ID length for version.", @@ -11051,7 +11388,7 @@ TEST_P(QuicFramerTest, InvalidRetirePriorToNewConnectionIdFrame) { } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -11080,7 +11417,7 @@ TEST_P(QuicFramerTest, InvalidRetirePriorToNewConnectionIdFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsError(QUIC_INVALID_NEW_CONNECTION_ID_DATA)); EXPECT_EQ("Retire_prior_to > sequence_number.", framer_.detailed_error()); @@ -11109,7 +11446,7 @@ TEST_P(QuicFramerTest, BuildNewConnectionIdFramePacket) { QuicFrames frames = {QuicFrame(&frame)}; // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -11137,8 +11474,8 @@ TEST_P(QuicFramerTest, BuildNewConnectionIdFramePacket) { ASSERT_TRUE(data != nullptr); quiche::test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), AsChars(packet99), - ABSL_ARRAYSIZE(packet99)); + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); } TEST_P(QuicFramerTest, NewTokenFrame) { @@ -11244,7 +11581,7 @@ TEST_P(QuicFramerTest, IetfStopSendingFrame) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -11266,7 +11603,7 @@ TEST_P(QuicFramerTest, IetfStopSendingFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -11281,7 +11618,7 @@ TEST_P(QuicFramerTest, IetfStopSendingFrame) { EXPECT_EQ(static_cast<uint64_t>(0x7654), visitor_.stop_sending_frame_.ietf_error_code); - CheckFramingBoundaries(packet99, QUIC_INVALID_STOP_SENDING_FRAME_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_STOP_SENDING_FRAME_DATA); } TEST_P(QuicFramerTest, BuildIetfStopSendingPacket) { @@ -11304,7 +11641,7 @@ TEST_P(QuicFramerTest, BuildIetfStopSendingPacket) { QuicFrames frames = {QuicFrame(&frame)}; // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -11325,8 +11662,8 @@ TEST_P(QuicFramerTest, BuildIetfStopSendingPacket) { ASSERT_TRUE(data != nullptr); quiche::test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), AsChars(packet99), - ABSL_ARRAYSIZE(packet99)); + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); } TEST_P(QuicFramerTest, IetfPathChallengeFrame) { @@ -11337,7 +11674,7 @@ TEST_P(QuicFramerTest, IetfPathChallengeFrame) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -11357,7 +11694,7 @@ TEST_P(QuicFramerTest, IetfPathChallengeFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -11369,7 +11706,7 @@ TEST_P(QuicFramerTest, IetfPathChallengeFrame) { EXPECT_EQ(QuicPathFrameBuffer({{0, 1, 2, 3, 4, 5, 6, 7}}), visitor_.path_challenge_frame_.data_buffer); - CheckFramingBoundaries(packet99, QUIC_INVALID_PATH_CHALLENGE_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_PATH_CHALLENGE_DATA); } TEST_P(QuicFramerTest, BuildIetfPathChallengePacket) { @@ -11389,7 +11726,7 @@ TEST_P(QuicFramerTest, BuildIetfPathChallengePacket) { QuicFrames frames = {QuicFrame(&frame)}; // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -11408,8 +11745,8 @@ TEST_P(QuicFramerTest, BuildIetfPathChallengePacket) { ASSERT_TRUE(data != nullptr); quiche::test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), AsChars(packet99), - ABSL_ARRAYSIZE(packet99)); + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); } TEST_P(QuicFramerTest, IetfPathResponseFrame) { @@ -11420,7 +11757,7 @@ TEST_P(QuicFramerTest, IetfPathResponseFrame) { SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -11440,7 +11777,7 @@ TEST_P(QuicFramerTest, IetfPathResponseFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -11452,7 +11789,7 @@ TEST_P(QuicFramerTest, IetfPathResponseFrame) { EXPECT_EQ(QuicPathFrameBuffer({{0, 1, 2, 3, 4, 5, 6, 7}}), visitor_.path_response_frame_.data_buffer); - CheckFramingBoundaries(packet99, QUIC_INVALID_PATH_RESPONSE_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_PATH_RESPONSE_DATA); } TEST_P(QuicFramerTest, BuildIetfPathResponsePacket) { @@ -11472,7 +11809,7 @@ TEST_P(QuicFramerTest, BuildIetfPathResponsePacket) { QuicFrames frames = {QuicFrame(&frame)}; // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -11491,8 +11828,8 @@ TEST_P(QuicFramerTest, BuildIetfPathResponsePacket) { ASSERT_TRUE(data != nullptr); quiche::test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), AsChars(packet99), - ABSL_ARRAYSIZE(packet99)); + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); } TEST_P(QuicFramerTest, GetRetransmittableControlFrameSize) { @@ -12222,7 +12559,7 @@ TEST_P(QuicFramerTest, RetireConnectionIdFrame) { } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - PacketFragments packet99 = { + PacketFragments packet_ietf = { // type (short header, 4 byte packet number) {"", {0x43}}, @@ -12242,7 +12579,7 @@ TEST_P(QuicFramerTest, RetireConnectionIdFrame) { // clang-format on std::unique_ptr<QuicEncryptedPacket> encrypted( - AssemblePacketFromFragments(packet99)); + AssemblePacketFromFragments(packet_ietf)); EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -12257,7 +12594,7 @@ TEST_P(QuicFramerTest, RetireConnectionIdFrame) { ASSERT_EQ(0u, visitor_.ack_frames_.size()); - CheckFramingBoundaries(packet99, QUIC_INVALID_RETIRE_CONNECTION_ID_DATA); + CheckFramingBoundaries(packet_ietf, QUIC_INVALID_RETIRE_CONNECTION_ID_DATA); } TEST_P(QuicFramerTest, BuildRetireConnectionIdFramePacket) { @@ -12278,7 +12615,7 @@ TEST_P(QuicFramerTest, BuildRetireConnectionIdFramePacket) { QuicFrames frames = {QuicFrame(&frame)}; // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -12297,8 +12634,8 @@ TEST_P(QuicFramerTest, BuildRetireConnectionIdFramePacket) { ASSERT_TRUE(data != nullptr); quiche::test::CompareCharArraysWithHexError( - "constructed packet", data->data(), data->length(), AsChars(packet99), - ABSL_ARRAYSIZE(packet99)); + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); } TEST_P(QuicFramerTest, AckFrameWithInvalidLargestObserved) { @@ -12344,7 +12681,7 @@ TEST_P(QuicFramerTest, AckFrameWithInvalidLargestObserved) { 0x00 }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -12368,8 +12705,8 @@ TEST_P(QuicFramerTest, AckFrameWithInvalidLargestObserved) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; } @@ -12422,7 +12759,7 @@ TEST_P(QuicFramerTest, FirstAckBlockJustUnderFlow) { 0x00 }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -12446,8 +12783,8 @@ TEST_P(QuicFramerTest, FirstAckBlockJustUnderFlow) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; p_size = ABSL_ARRAYSIZE(packet46); @@ -12522,7 +12859,7 @@ TEST_P(QuicFramerTest, ThirdAckBlockJustUnderflow) { 0x00 }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -12554,8 +12891,8 @@ TEST_P(QuicFramerTest, ThirdAckBlockJustUnderflow) { unsigned char* p = packet; size_t p_size = ABSL_ARRAYSIZE(packet); if (VersionHasIetfQuicFrames(framer_.transport_version())) { - p = packet99; - p_size = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_size = ABSL_ARRAYSIZE(packet_ietf); } else if (framer_.version().HasIetfInvariantHeader()) { p = packet46; p_size = ABSL_ARRAYSIZE(packet46); @@ -12636,7 +12973,7 @@ TEST_P(QuicFramerTest, CoalescedPacket) { 'O', '_', 'W', 'O', 'R', 'L', 'D', '?', }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // first coalesced packet // public flags (long header with packet type ZERO_RTT_PROTECTED and // 4-byte packet number) @@ -12701,8 +13038,8 @@ TEST_P(QuicFramerTest, CoalescedPacket) { unsigned char* p = packet; size_t p_length = ABSL_ARRAYSIZE(packet); if (framer_.version().HasIetfQuicFrames()) { - p = packet99; - p_length = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_length = ABSL_ARRAYSIZE(packet_ietf); } QuicEncryptedPacket encrypted(AsChars(p), p_length, false); @@ -12778,7 +13115,7 @@ TEST_P(QuicFramerTest, CoalescedPacketWithUdpPadding) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // first coalesced packet // public flags (long header with packet type ZERO_RTT_PROTECTED and // 4-byte packet number) @@ -12820,8 +13157,8 @@ TEST_P(QuicFramerTest, CoalescedPacketWithUdpPadding) { unsigned char* p = packet; size_t p_length = ABSL_ARRAYSIZE(packet); if (framer_.version().HasIetfQuicFrames()) { - p = packet99; - p_length = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_length = ABSL_ARRAYSIZE(packet_ietf); } QuicEncryptedPacket encrypted(AsChars(p), p_length, false); @@ -12906,7 +13243,7 @@ TEST_P(QuicFramerTest, CoalescedPacketWithDifferentVersion) { 'O', '_', 'W', 'O', 'R', 'L', 'D', '?', }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // first coalesced packet // public flags (long header with packet type ZERO_RTT_PROTECTED and // 4-byte packet number) @@ -12971,8 +13308,8 @@ TEST_P(QuicFramerTest, CoalescedPacketWithDifferentVersion) { unsigned char* p = packet; size_t p_length = ABSL_ARRAYSIZE(packet); if (framer_.version().HasIetfQuicFrames()) { - p = packet99; - p_length = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_length = ABSL_ARRAYSIZE(packet_ietf); } QuicEncryptedPacket encrypted(AsChars(p), p_length, false); @@ -13278,7 +13615,7 @@ TEST_P(QuicFramerTest, UndecryptableCoalescedPacket) { 'O', '_', 'W', 'O', 'R', 'L', 'D', '?', }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // first coalesced packet // public flags (long header with packet type HANDSHAKE and // 4-byte packet number) @@ -13344,8 +13681,8 @@ TEST_P(QuicFramerTest, UndecryptableCoalescedPacket) { unsigned char* p = packet; size_t p_length = ABSL_ARRAYSIZE(packet); if (framer_.version().HasIetfQuicFrames()) { - p = packet99; - p_length = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_length = ABSL_ARRAYSIZE(packet_ietf); } QuicEncryptedPacket encrypted(AsChars(p), p_length, false); @@ -13446,7 +13783,7 @@ TEST_P(QuicFramerTest, MismatchedCoalescedPacket) { 'O', '_', 'W', 'O', 'R', 'L', 'D', '?', }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // first coalesced packet // public flags (long header with packet type ZERO_RTT_PROTECTED and // 4-byte packet number) @@ -13511,8 +13848,8 @@ TEST_P(QuicFramerTest, MismatchedCoalescedPacket) { unsigned char* p = packet; size_t p_length = ABSL_ARRAYSIZE(packet); if (framer_.version().HasIetfQuicFrames()) { - p = packet99; - p_length = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_length = ABSL_ARRAYSIZE(packet_ietf); } QuicEncryptedPacket encrypted(AsChars(p), p_length, false); @@ -13575,7 +13912,7 @@ TEST_P(QuicFramerTest, InvalidCoalescedPacket) { 0xD3, // version would be here but we cut off the invalid coalesced header. }; - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // first coalesced packet // public flags (long header with packet type ZERO_RTT_PROTECTED and // 4-byte packet number) @@ -13616,8 +13953,8 @@ TEST_P(QuicFramerTest, InvalidCoalescedPacket) { unsigned char* p = packet; size_t p_length = ABSL_ARRAYSIZE(packet); if (framer_.version().HasIetfQuicFrames()) { - p = packet99; - p_length = ABSL_ARRAYSIZE(packet99); + p = packet_ietf; + p_length = ABSL_ARRAYSIZE(packet_ietf); } QuicEncryptedPacket encrypted(AsChars(p), p_length, false); @@ -13809,7 +14146,7 @@ TEST_P(QuicFramerTest, MultiplePacketNumberSpaces) { // padding frame 0x00, }; - unsigned char long_header_packet99[] = { + unsigned char long_header_packet_ietf[] = { // public flags (long header with packet type ZERO_RTT_PROTECTED and // 4-byte packet number) 0xD3, @@ -13844,8 +14181,8 @@ TEST_P(QuicFramerTest, MultiplePacketNumberSpaces) { ABSL_ARRAYSIZE(long_header_packet), false))); } else { EXPECT_TRUE(framer_.ProcessPacket( - QuicEncryptedPacket(AsChars(long_header_packet99), - ABSL_ARRAYSIZE(long_header_packet99), false))); + QuicEncryptedPacket(AsChars(long_header_packet_ietf), + ABSL_ARRAYSIZE(long_header_packet_ietf), false))); } EXPECT_THAT(framer_.error(), IsQuicNoError()); @@ -14633,7 +14970,7 @@ TEST_P(QuicFramerTest, OverlyLargeAckDelay) { } SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); // clang-format off - unsigned char packet99[] = { + unsigned char packet_ietf[] = { // type (short header, 4 byte packet number) 0x43, // connection_id @@ -14654,8 +14991,8 @@ TEST_P(QuicFramerTest, OverlyLargeAckDelay) { }; // clang-format on - framer_.ProcessPacket( - QuicEncryptedPacket(AsChars(packet99), ABSL_ARRAYSIZE(packet99), false)); + framer_.ProcessPacket(QuicEncryptedPacket( + AsChars(packet_ietf), ABSL_ARRAYSIZE(packet_ietf), false)); ASSERT_EQ(1u, visitor_.ack_frames_.size()); // Verify ack_delay_time is set correctly. EXPECT_EQ(QuicTime::Delta::Infinite(), diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.cc b/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.cc index ac6799ee46d..fceb1e7e9b0 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.cc @@ -11,10 +11,11 @@ namespace quic { namespace { -class AlarmDelegate : public QuicAlarm::Delegate { +class AlarmDelegate : public QuicAlarm::DelegateWithContext { public: - explicit AlarmDelegate(QuicIdleNetworkDetector* detector) - : detector_(detector) {} + explicit AlarmDelegate(QuicIdleNetworkDetector* detector, + QuicConnectionContext* context) + : QuicAlarm::DelegateWithContext(context), detector_(detector) {} AlarmDelegate(const AlarmDelegate&) = delete; AlarmDelegate& operator=(const AlarmDelegate&) = delete; @@ -28,20 +29,15 @@ class AlarmDelegate : public QuicAlarm::Delegate { QuicIdleNetworkDetector::QuicIdleNetworkDetector( Delegate* delegate, QuicTime now, QuicConnectionArena* arena, - QuicAlarmFactory* alarm_factory) + QuicAlarmFactory* alarm_factory, QuicConnectionContext* context) : delegate_(delegate), start_time_(now), handshake_timeout_(QuicTime::Delta::Infinite()), time_of_last_received_packet_(now), time_of_first_packet_sent_after_receiving_(QuicTime::Zero()), idle_network_timeout_(QuicTime::Delta::Infinite()), - alarm_( - alarm_factory->CreateAlarm(arena->New<AlarmDelegate>(this), arena)) { - if (no_alarm_after_stopped_) { - QUIC_RELOADABLE_FLAG_COUNT_N( - quic_idle_network_detector_no_alarm_after_stopped, 1, 2); - } -} + alarm_(alarm_factory->CreateAlarm( + arena->New<AlarmDelegate>(this, context), arena)) {} void QuicIdleNetworkDetector::OnAlarm() { if (handshake_timeout_.IsInfinite()) { @@ -99,10 +95,7 @@ void QuicIdleNetworkDetector::OnPacketReceived(QuicTime now) { } void QuicIdleNetworkDetector::SetAlarm() { - if (no_alarm_after_stopped_ && stopped_) { - QUIC_RELOADABLE_FLAG_COUNT_N( - quic_idle_network_detector_no_alarm_after_stopped, 2, 2); - + if (stopped_) { // TODO(wub): If this QUIC_BUG fires, it indicates a problem in the // QuicConnection, which somehow called this function while disconnected. // That problem needs to be fixed. diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.h b/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.h index 1665104a186..2381c12cdc6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector.h @@ -36,10 +36,10 @@ class QUIC_EXPORT_PRIVATE QuicIdleNetworkDetector { virtual void OnIdleNetworkDetected() = 0; }; - QuicIdleNetworkDetector(Delegate* delegate, - QuicTime now, + QuicIdleNetworkDetector(Delegate* delegate, QuicTime now, QuicConnectionArena* arena, - QuicAlarmFactory* alarm_factory); + QuicAlarmFactory* alarm_factory, + QuicConnectionContext* context); void OnAlarm(); @@ -111,9 +111,6 @@ class QUIC_EXPORT_PRIVATE QuicIdleNetworkDetector { // Whether |StopDetection| has been called. bool stopped_ = false; - - const bool no_alarm_after_stopped_ = - GetQuicReloadableFlag(quic_idle_network_detector_no_alarm_after_stopped); }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector_test.cc index 11d0cf76777..a6fd5d28ce7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_idle_network_detector_test.cc @@ -32,7 +32,8 @@ class QuicIdleNetworkDetectorTest : public QuicTest { QuicIdleNetworkDetectorTest() { clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); detector_ = std::make_unique<QuicIdleNetworkDetector>( - &delegate_, clock_.Now(), &arena_, &alarm_factory_); + &delegate_, clock_.Now(), &arena_, &alarm_factory_, + /*context=*/nullptr); alarm_ = static_cast<MockAlarmFactory::TestAlarm*>( QuicIdleNetworkDetectorTestPeer::GetAlarm(detector_.get())); } @@ -185,20 +186,12 @@ TEST_F(QuicIdleNetworkDetectorTest, ShorterIdleTimeoutOnSentPacket) { TEST_F(QuicIdleNetworkDetectorTest, NoAlarmAfterStopped) { detector_->StopDetection(); - if (GetQuicReloadableFlag( - quic_idle_network_detector_no_alarm_after_stopped)) { - EXPECT_QUIC_BUG( - detector_->SetTimeouts( - /*handshake_timeout=*/QuicTime::Delta::FromSeconds(30), - /*idle_network_timeout=*/QuicTime::Delta::FromSeconds(20)), - "SetAlarm called after stopped"); - EXPECT_FALSE(alarm_->IsSet()); - } else { - detector_->SetTimeouts( - /*handshake_timeout=*/QuicTime::Delta::FromSeconds(30), - /*idle_network_timeout=*/QuicTime::Delta::FromSeconds(20)); - EXPECT_TRUE(alarm_->IsSet()); - } + EXPECT_QUIC_BUG( + detector_->SetTimeouts( + /*handshake_timeout=*/QuicTime::Delta::FromSeconds(30), + /*idle_network_timeout=*/QuicTime::Delta::FromSeconds(20)), + "SetAlarm called after stopped"); + EXPECT_FALSE(alarm_->IsSet()); } } // namespace diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_mtu_discovery.h b/chromium/net/third_party/quiche/src/quic/core/quic_mtu_discovery.h index b16cc6aedca..dddd33de7dd 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_mtu_discovery.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_mtu_discovery.h @@ -27,9 +27,9 @@ static_assert(kMtuDiscoveryAttempts + 8 < 8 * sizeof(QuicPacketNumber), static_assert(kPacketsBetweenMtuProbesBase < (1 << 8), "The initial number of packets between MTU probes is too high"); -// The incresed packet size targeted when doing path MTU discovery. -const QuicByteCount kMtuDiscoveryTargetPacketSizeHigh = 1450; -const QuicByteCount kMtuDiscoveryTargetPacketSizeLow = 1430; +// The increased packet size targeted when doing path MTU discovery. +const QuicByteCount kMtuDiscoveryTargetPacketSizeHigh = 1400; +const QuicByteCount kMtuDiscoveryTargetPacketSizeLow = 1380; static_assert(kMtuDiscoveryTargetPacketSizeLow <= kMaxOutgoingPacketSize, "MTU discovery target is too large"); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.cc b/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.cc index 4dca50832ac..2d9fc2d3a61 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.cc @@ -10,10 +10,11 @@ namespace quic { namespace { -class AlarmDelegate : public QuicAlarm::Delegate { +class AlarmDelegate : public QuicAlarm::DelegateWithContext { public: - explicit AlarmDelegate(QuicNetworkBlackholeDetector* detector) - : detector_(detector) {} + explicit AlarmDelegate(QuicNetworkBlackholeDetector* detector, + QuicConnectionContext* context) + : QuicAlarm::DelegateWithContext(context), detector_(detector) {} AlarmDelegate(const AlarmDelegate&) = delete; AlarmDelegate& operator=(const AlarmDelegate&) = delete; @@ -26,12 +27,11 @@ class AlarmDelegate : public QuicAlarm::Delegate { } // namespace QuicNetworkBlackholeDetector::QuicNetworkBlackholeDetector( - Delegate* delegate, - QuicConnectionArena* arena, - QuicAlarmFactory* alarm_factory) + Delegate* delegate, QuicConnectionArena* arena, + QuicAlarmFactory* alarm_factory, QuicConnectionContext* context) : delegate_(delegate), - alarm_( - alarm_factory->CreateAlarm(arena->New<AlarmDelegate>(this), arena)) {} + alarm_(alarm_factory->CreateAlarm( + arena->New<AlarmDelegate>(this, context), arena)) {} void QuicNetworkBlackholeDetector::OnAlarm() { QuicTime next_deadline = GetEarliestDeadline(); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.h b/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.h index 82753f934a6..db52e8faa4f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.h @@ -40,9 +40,9 @@ class QUIC_EXPORT_PRIVATE QuicNetworkBlackholeDetector { virtual void OnPathMtuReductionDetected() = 0; }; - QuicNetworkBlackholeDetector(Delegate* delegate, - QuicConnectionArena* arena, - QuicAlarmFactory* alarm_factory); + QuicNetworkBlackholeDetector(Delegate* delegate, QuicConnectionArena* arena, + QuicAlarmFactory* alarm_factory, + QuicConnectionContext* context); // Called to stop all detections. If |permanent|, the alarm will be cancelled // permanently and future calls to RestartDetection will be no-op. diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector_test.cc index 66bebfd455f..764ee9a1f5d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_network_blackhole_detector_test.cc @@ -33,7 +33,7 @@ const size_t kBlackholeDelayInSeconds = 10; class QuicNetworkBlackholeDetectorTest : public QuicTest { public: QuicNetworkBlackholeDetectorTest() - : detector_(&delegate_, &arena_, &alarm_factory_), + : detector_(&delegate_, &arena_, &alarm_factory_, /*context=*/nullptr), alarm_(static_cast<MockAlarmFactory::TestAlarm*>( QuicNetworkBlackholeDetectorPeer::GetAlarm(&detector_))), path_degrading_delay_( diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_one_block_arena.h b/chromium/net/third_party/quiche/src/quic/core/quic_one_block_arena.h index 53882ead440..02aae871e02 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_one_block_arena.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_one_block_arena.h @@ -69,7 +69,7 @@ class QUIC_EXPORT_PRIVATE QuicOneBlockArena { // QuicConnections currently use around 1KB of polymorphic types which would // ordinarily be on the heap. Instead, store them inline in an arena. -using QuicConnectionArena = QuicOneBlockArena<1056>; +using QuicConnectionArena = QuicOneBlockArena<1152>; } // 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 94cf48a1d51..19168fb79ac 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 @@ -109,8 +109,7 @@ QuicPacketCreator::QuicPacketCreator(QuicConnectionId server_connection_id, delegate) {} QuicPacketCreator::QuicPacketCreator(QuicConnectionId server_connection_id, - QuicFramer* framer, - QuicRandom* random, + QuicFramer* framer, QuicRandom* random, DelegateInterface* delegate) : delegate_(delegate), debug_delegate_(nullptr), @@ -123,11 +122,7 @@ QuicPacketCreator::QuicPacketCreator(QuicConnectionId server_connection_id, packet_size_(0), server_connection_id_(server_connection_id), client_connection_id_(EmptyQuicConnectionId()), - packet_(QuicPacketNumber(), - PACKET_1BYTE_PACKET_NUMBER, - nullptr, - 0, - false, + packet_(QuicPacketNumber(), PACKET_1BYTE_PACKET_NUMBER, nullptr, 0, false, false), pending_padding_bytes_(0), needs_full_padding_(false), @@ -136,7 +131,9 @@ QuicPacketCreator::QuicPacketCreator(QuicConnectionId server_connection_id, fully_pad_crypto_handshake_packets_(true), latched_hard_max_packet_length_(0), max_datagram_frame_size_(0), - chaos_protection_enabled_(false) { + chaos_protection_enabled_( + GetQuicFlag(FLAGS_quic_enable_chaos_protection) && + framer->perspective() == Perspective::IS_CLIENT) { SetMaxPacketLength(kDefaultMaxPacketSize); if (!framer_->version().UsesTls()) { // QUIC+TLS negotiates the maximum datagram frame size via the 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 3dc1a4b184b..55385641426 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 @@ -1863,7 +1863,7 @@ TEST_P(QuicPacketCreatorTest, GetGuaranteedLargestMessagePayload) { if (version.UsesTls()) { creator_.SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize); } - QuicPacketLength expected_largest_payload = 1319; + QuicPacketLength expected_largest_payload = 1219; if (version.HasLongHeaderLengths()) { expected_largest_payload -= 2; } @@ -1914,7 +1914,7 @@ TEST_P(QuicPacketCreatorTest, GetCurrentLargestMessagePayload) { if (version.UsesTls()) { creator_.SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize); } - QuicPacketLength expected_largest_payload = 1319; + QuicPacketLength expected_largest_payload = 1219; if (version.SendsVariableLengthPacketNumberInLongHeader()) { expected_largest_payload += 3; } @@ -3249,7 +3249,7 @@ TEST_F(QuicPacketCreatorMultiplePacketsTest, PacketTransmissionType) { // The first ConsumeData will fill the packet without flush. creator_.SetTransmissionType(LOSS_RETRANSMISSION); - size_t data_len = 1324; + size_t data_len = 1224; CreateData(data_len); QuicStreamId stream1_id = QuicUtils::GetFirstBidirectionalStreamId( framer_.transport_version(), Perspective::IS_CLIENT); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_path_validator.cc b/chromium/net/third_party/quiche/src/quic/core/quic_path_validator.cc index bb83e9472f7..658dfefdce9 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_path_validator.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_path_validator.cc @@ -10,10 +10,12 @@ namespace quic { -class RetryAlarmDelegate : public QuicAlarm::Delegate { +class RetryAlarmDelegate : public QuicAlarm::DelegateWithContext { public: - explicit RetryAlarmDelegate(QuicPathValidator* path_validator) - : path_validator_(path_validator) {} + explicit RetryAlarmDelegate(QuicPathValidator* path_validator, + QuicConnectionContext* context) + : QuicAlarm::DelegateWithContext(context), + path_validator_(path_validator) {} RetryAlarmDelegate(const RetryAlarmDelegate&) = delete; RetryAlarmDelegate& operator=(const RetryAlarmDelegate&) = delete; @@ -32,12 +34,12 @@ std::ostream& operator<<(std::ostream& os, QuicPathValidator::QuicPathValidator(QuicAlarmFactory* alarm_factory, QuicConnectionArena* arena, SendDelegate* send_delegate, - QuicRandom* random) + QuicRandom* random, + QuicConnectionContext* context) : send_delegate_(send_delegate), random_(random), - retry_timer_( - alarm_factory->CreateAlarm(arena->New<RetryAlarmDelegate>(this), - arena)), + retry_timer_(alarm_factory->CreateAlarm( + arena->New<RetryAlarmDelegate>(this, context), arena)), retry_count_(0u) {} void QuicPathValidator::OnPathResponse(const QuicPathFrameBuffer& probing_data, diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_path_validator.h b/chromium/net/third_party/quiche/src/quic/core/quic_path_validator.h index 9ea1554efa4..9fe2cef9884 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_path_validator.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_path_validator.h @@ -13,6 +13,7 @@ #include "quic/core/quic_alarm_factory.h" #include "quic/core/quic_arena_scoped_ptr.h" #include "quic/core/quic_clock.h" +#include "quic/core/quic_connection_context.h" #include "quic/core/quic_one_block_arena.h" #include "quic/core/quic_packet_writer.h" #include "quic/core/quic_types.h" @@ -107,10 +108,9 @@ class QUIC_EXPORT_PRIVATE QuicPathValidator { std::unique_ptr<QuicPathValidationContext> context) = 0; }; - QuicPathValidator(QuicAlarmFactory* alarm_factory, - QuicConnectionArena* arena, - SendDelegate* delegate, - QuicRandom* random); + QuicPathValidator(QuicAlarmFactory* alarm_factory, QuicConnectionArena* arena, + SendDelegate* delegate, QuicRandom* random, + QuicConnectionContext* context); // Send PATH_CHALLENGE and start the retry timer. void StartPathValidation(std::unique_ptr<QuicPathValidationContext> context, diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_path_validator_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_path_validator_test.cc index 1dfafc3b77a..28e12593e94 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_path_validator_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_path_validator_test.cc @@ -48,11 +48,10 @@ class MockSendDelegate : public QuicPathValidator::SendDelegate { class QuicPathValidatorTest : public QuicTest { public: QuicPathValidatorTest() - : path_validator_(&alarm_factory_, &arena_, &send_delegate_, &random_), - context_(new MockQuicPathValidationContext(self_address_, - peer_address_, - effective_peer_address_, - &writer_)), + : path_validator_(&alarm_factory_, &arena_, &send_delegate_, &random_, + /*context=*/nullptr), + context_(new MockQuicPathValidationContext( + self_address_, peer_address_, effective_peer_address_, &writer_)), result_delegate_( new testing::StrictMock<MockQuicPathValidationResultDelegate>()) { clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_protocol_flags_list.h b/chromium/net/third_party/quiche/src/quic/core/quic_protocol_flags_list.h index 56f83594b8f..3e327139a18 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_protocol_flags_list.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_protocol_flags_list.h @@ -44,6 +44,26 @@ QUIC_PROTOCOL_FLAG(int64_t, "Time period for which a given connection_id should live in " "the time-wait state.") +// This number is relatively conservative. For example, there are at most 1K +// queued stateless resets, which consume 1K * 21B = 21KB. +QUIC_PROTOCOL_FLAG( + uint64_t, quic_time_wait_list_max_pending_packets, 1024, + "Upper limit of pending packets in time wait list when writer is blocked.") + +// Stop sending a reset if the recorded number of addresses that server has +// recently sent stateless reset to exceeds this limit. +QUIC_PROTOCOL_FLAG(uint64_t, quic_max_recent_stateless_reset_addresses, 1024, + "Max number of recorded recent reset addresses.") + +// After this timeout, recent reset addresses will be cleared. +// FLAGS_quic_max_recent_stateless_reset_addresses * (1000ms / +// FLAGS_quic_recent_stateless_reset_addresses_lifetime_ms) is roughly the max +// reset per second. For example, 1024 * (1000ms / 1000ms) = 1K reset per +// second. +QUIC_PROTOCOL_FLAG( + uint64_t, quic_recent_stateless_reset_addresses_lifetime_ms, 1000, + "Max time that a client address lives in recent reset addresses set.") + QUIC_PROTOCOL_FLAG(double, quic_bbr_cwnd_gain, 2.0f, @@ -77,6 +97,11 @@ QUIC_PROTOCOL_FLAG( "Congestion window fraction that the pacing sender allows in bursts " "during pacing.") +QUIC_PROTOCOL_FLAG( + int32_t, quic_lumpy_pacing_min_bandwidth_kbps, 1200, + "The minimum estimated client bandwidth below which the pacing sender will " + "not allow bursts.") + QUIC_PROTOCOL_FLAG(int32_t, quic_max_pace_time_into_future_ms, 10, @@ -103,6 +128,10 @@ QUIC_PROTOCOL_FLAG(bool, true, "If true, use random greased settings and frames.") +QUIC_PROTOCOL_FLAG( + bool, quic_enable_chaos_protection, true, + "If true, use chaos protection to randomize client initials.") + QUIC_PROTOCOL_FLAG(int64_t, quic_max_tracked_packet_count, 10000, @@ -256,4 +285,6 @@ QUIC_PROTOCOL_FLAG( false, "If true, always reject retry_token received in INITIAL packets") +QUIC_PROTOCOL_FLAG(bool, quic_use_lower_server_response_mtu_for_test, false, + "If true, cap server response packet size at 1250.") #endif diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager_test.cc index da586c67a3d..9d6b6644421 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager_test.cc @@ -434,7 +434,6 @@ TEST_F(QuicReceivedPacketManagerTest, EXPECT_FALSE(HasPendingAck()); QuicConfig config; QuicTagVector connection_options; - connection_options.push_back(kACKD); // No limit on the number of packets received before sending an ack. connection_options.push_back(kAKDU); config.SetConnectionOptionsToSend(connection_options); 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 fd1190bde7b..d9d384f381f 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 @@ -257,18 +257,22 @@ void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) { // Initial window. if (GetQuicReloadableFlag(quic_unified_iw_options)) { if (config.HasClientRequestedIndependentOption(kIW03, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_unified_iw_options, 1, 4); initial_congestion_window_ = 3; send_algorithm_->SetInitialCongestionWindowInPackets(3); } if (config.HasClientRequestedIndependentOption(kIW10, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_unified_iw_options, 2, 4); initial_congestion_window_ = 10; send_algorithm_->SetInitialCongestionWindowInPackets(10); } if (config.HasClientRequestedIndependentOption(kIW20, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_unified_iw_options, 3, 4); initial_congestion_window_ = 20; send_algorithm_->SetInitialCongestionWindowInPackets(20); } if (config.HasClientRequestedIndependentOption(kIW50, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_unified_iw_options, 4, 4); initial_congestion_window_ = 50; send_algorithm_->SetInitialCongestionWindowInPackets(50); } 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 aef3fc255a8..e2677377846 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 @@ -12,6 +12,7 @@ #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "quic/core/frames/quic_ack_frequency_frame.h" +#include "quic/core/frames/quic_window_update_frame.h" #include "quic/core/quic_connection.h" #include "quic/core/quic_connection_context.h" #include "quic/core/quic_error_codes.h" @@ -41,6 +42,12 @@ class ClosedStreamsCleanUpDelegate : public QuicAlarm::Delegate { ClosedStreamsCleanUpDelegate& operator=(const ClosedStreamsCleanUpDelegate&) = delete; + QuicConnectionContext* GetConnectionContext() override { + return (session_->connection() == nullptr) + ? nullptr + : session_->connection()->context(); + } + void OnAlarm() override { session_->CleanUpClosedStreams(); } private: @@ -53,22 +60,14 @@ class ClosedStreamsCleanUpDelegate : public QuicAlarm::Delegate { (perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ") QuicSession::QuicSession( - QuicConnection* connection, - Visitor* owner, - const QuicConfig& config, + QuicConnection* connection, Visitor* owner, const QuicConfig& config, const ParsedQuicVersionVector& supported_versions, QuicStreamCount num_expected_unidirectional_static_streams) - : QuicSession(connection, - owner, - config, - supported_versions, - num_expected_unidirectional_static_streams, - nullptr) {} + : QuicSession(connection, owner, config, supported_versions, + num_expected_unidirectional_static_streams, nullptr) {} QuicSession::QuicSession( - QuicConnection* connection, - Visitor* owner, - const QuicConfig& config, + QuicConnection* connection, Visitor* owner, const QuicConfig& config, const ParsedQuicVersionVector& supported_versions, QuicStreamCount num_expected_unidirectional_static_streams, std::unique_ptr<QuicDatagramQueue::Observer> datagram_observer) @@ -77,14 +76,10 @@ QuicSession::QuicSession( visitor_(owner), write_blocked_streams_(connection->transport_version()), config_(config), - stream_id_manager_(perspective(), - connection->transport_version(), + stream_id_manager_(perspective(), connection->transport_version(), kDefaultMaxStreamsPerConnection, config_.GetMaxBidirectionalStreamsToSend()), - ietf_streamid_manager_(perspective(), - connection->version(), - this, - 0, + ietf_streamid_manager_(perspective(), connection->version(), this, 0, num_expected_unidirectional_static_streams, config_.GetMaxBidirectionalStreamsToSend(), config_.GetMaxUnidirectionalStreamsToSend() + @@ -94,15 +89,13 @@ QuicSession::QuicSession( num_static_streams_(0), num_zombie_streams_(0), flow_controller_( - this, - QuicUtils::GetInvalidStreamId(connection->transport_version()), + this, QuicUtils::GetInvalidStreamId(connection->transport_version()), /*is_connection_flow_controller*/ true, connection->version().AllowsLowFlowControlLimits() ? 0 : kMinimumFlowControlSendWindow, config_.GetInitialSessionFlowControlWindowToSend(), - kSessionReceiveWindowLimit, - perspective() == Perspective::IS_SERVER, + kSessionReceiveWindowLimit, perspective() == Perspective::IS_SERVER, nullptr), currently_writing_stream_id_(0), transport_goaway_sent_(false), @@ -141,8 +134,8 @@ void QuicSession::Initialize() { connection_->set_can_receive_ack_frequency_frame(); config_.SetMinAckDelayMs(kDefaultMinAckDelayTimeMs); } - if (config_.HasClientRequestedIndependentOption(kBPTE, perspective_)) { - permutes_tls_extensions_ = true; + if (config_.HasClientRequestedIndependentOption(kNBPE, perspective_)) { + permutes_tls_extensions_ = false; } } @@ -167,13 +160,13 @@ void QuicSession::Initialize() { } QuicSession::~QuicSession() { - if (GetQuicRestartFlag(quic_alarm_add_permanent_cancel) && - closed_streams_clean_up_alarm_ != nullptr) { + if (closed_streams_clean_up_alarm_ != nullptr) { closed_streams_clean_up_alarm_->PermanentCancel(); } } -void QuicSession::PendingStreamOnStreamFrame(const QuicStreamFrame& frame) { +PendingStream* QuicSession::PendingStreamOnStreamFrame( + const QuicStreamFrame& frame) { QUICHE_DCHECK(VersionUsesHttp3(transport_version())); QuicStreamId stream_id = frame.stream_id; @@ -184,27 +177,63 @@ void QuicSession::PendingStreamOnStreamFrame(const QuicStreamFrame& frame) { QuicStreamOffset final_byte_offset = frame.offset + frame.data_length; OnFinalByteOffsetReceived(stream_id, final_byte_offset); } - return; + return nullptr; } pending->OnStreamFrame(frame); if (!connection()->connected()) { - return; + return nullptr; } + return pending; +} + +void QuicSession::MaybeProcessPendingStream(PendingStream* pending) { + QUICHE_DCHECK(pending != nullptr); + QuicStreamId stream_id = pending->id(); + absl::optional<QuicResetStreamError> stop_sending_error_code = + pending->GetStopSendingErrorCode(); QuicStream* stream = ProcessPendingStream(pending); if (stream != nullptr) { // The pending stream should now be in the scope of normal streams. QUICHE_DCHECK(IsClosedStream(stream_id) || IsOpenStream(stream_id)) << "Stream " << stream_id << " not created"; pending_stream_map_.erase(stream_id); + if (stop_sending_error_code) { + stream->OnStopSending(*stop_sending_error_code); + if (!connection()->connected()) { + return; + } + } stream->OnStreamCreatedFromPendingStream(); return; } + // At this point, none of the bytes has been successfully consumed by the + // application layer. We should close the pending stream even if it is + // bidirectionl as no application will be able to write in a bidirectional + // stream with zero byte as input. if (pending->sequencer()->IsClosed()) { ClosePendingStream(stream_id); } } +void QuicSession::PendingStreamOnWindowUpdateFrame( + const QuicWindowUpdateFrame& frame) { + QUICHE_DCHECK(VersionUsesHttp3(transport_version())); + PendingStream* pending = GetOrCreatePendingStream(frame.stream_id); + if (pending) { + pending->OnWindowUpdateFrame(frame); + } +} + +void QuicSession::PendingStreamOnStopSendingFrame( + const QuicStopSendingFrame& frame) { + QUICHE_DCHECK(VersionUsesHttp3(transport_version())); + PendingStream* pending = GetOrCreatePendingStream(frame.stream_id); + if (pending) { + pending->OnStopSending(frame.error()); + } +} + void QuicSession::OnStreamFrame(const QuicStreamFrame& frame) { QuicStreamId stream_id = frame.stream_id; if (stream_id == QuicUtils::GetInvalidStreamId(transport_version())) { @@ -214,12 +243,11 @@ void QuicSession::OnStreamFrame(const QuicStreamFrame& frame) { return; } - if (UsesPendingStreams() && - QuicUtils::GetStreamType(stream_id, perspective(), - IsIncomingStream(stream_id), - version()) == READ_UNIDIRECTIONAL && - stream_map_.find(stream_id) == stream_map_.end()) { - PendingStreamOnStreamFrame(frame); + if (ShouldProcessFrameByPendingStream(STREAM_FRAME, stream_id)) { + PendingStream* pending = PendingStreamOnStreamFrame(frame); + if (pending != nullptr && ShouldProcessPendingStreamImmediately()) { + MaybeProcessPendingStream(pending); + } return; } @@ -278,6 +306,10 @@ void QuicSession::OnStopSendingFrame(const QuicStopSendingFrame& frame) { if (visitor_) { visitor_->OnStopSendingReceived(frame); } + if (ShouldProcessFrameByPendingStream(STOP_SENDING_FRAME, stream_id)) { + PendingStreamOnStopSendingFrame(frame); + return; + } QuicStream* stream = GetOrCreateStream(stream_id); if (!stream) { @@ -285,7 +317,7 @@ void QuicSession::OnStopSendingFrame(const QuicStopSendingFrame& frame) { return; } - stream->OnStopSending(frame.error_code); + stream->OnStopSending(frame.error()); } void QuicSession::OnPacketDecrypted(EncryptionLevel level) { @@ -325,11 +357,10 @@ void QuicSession::PendingStreamOnRstStream(const QuicRstStreamFrame& frame) { } pending->OnRstStreamFrame(frame); - // Pending stream is currently read only. We can safely close the stream. - QUICHE_DCHECK_EQ( - READ_UNIDIRECTIONAL, - QuicUtils::GetStreamType(pending->id(), perspective(), - /*peer_initiated = */ true, version())); + // At this point, none of the bytes has been consumed by the application + // layer. It is safe to close the pending stream even if it is bidirectionl as + // no application will be able to write in a bidirectional stream with zero + // byte as input. ClosePendingStream(stream_id); } @@ -356,11 +387,7 @@ void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) { visitor_->OnRstStreamReceived(frame); } - if (UsesPendingStreams() && - QuicUtils::GetStreamType(stream_id, perspective(), - IsIncomingStream(stream_id), - version()) == READ_UNIDIRECTIONAL && - stream_map_.find(stream_id) == stream_map_.end()) { + if (ShouldProcessFrameByPendingStream(RST_STREAM_FRAME, stream_id)) { PendingStreamOnRstStream(frame); return; } @@ -444,7 +471,7 @@ void QuicSession::OnConnectionClosed(const QuicConnectionCloseFrame& frame, closed_streams_clean_up_alarm_->Cancel(); if (visitor_) { - visitor_->OnConnectionClosed(connection_->connection_id(), + visitor_->OnConnectionClosed(connection_->GetOneActiveServerConnectionId(), frame.quic_error_code, frame.error_details, source); } @@ -483,9 +510,7 @@ void QuicSession::OnPathDegrading() {} void QuicSession::OnForwardProgressMadeAfterPathDegrading() {} -bool QuicSession::AllowSelfAddressChange() const { - return false; -} +bool QuicSession::AllowSelfAddressChange() const { return false; } void QuicSession::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { // Stream may be closed by the time we receive a WINDOW_UPDATE, so we can't @@ -513,6 +538,11 @@ void QuicSession::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { return; } + if (ShouldProcessFrameByPendingStream(WINDOW_UPDATE_FRAME, stream_id)) { + PendingStreamOnWindowUpdateFrame(frame); + return; + } + QuicStream* stream = GetOrCreateStream(stream_id); if (stream != nullptr) { stream->OnWindowUpdateFrame(frame); @@ -808,8 +838,7 @@ QuicConsumedData QuicSession::WritevData(QuicStreamId id, size_t write_length, return data; } -size_t QuicSession::SendCryptoData(EncryptionLevel level, - size_t write_length, +size_t QuicSession::SendCryptoData(EncryptionLevel level, size_t write_length, QuicStreamOffset offset, TransmissionType type) { QUICHE_DCHECK(QuicVersionUsesCryptoFrames(transport_version())); @@ -866,12 +895,12 @@ void QuicSession::ResetStream(QuicStreamId id, QuicRstStreamErrorCode error) { } QuicConnection::ScopedPacketFlusher flusher(connection()); - MaybeSendStopSendingFrame(id, error); - MaybeSendRstStreamFrame(id, error, 0); + MaybeSendStopSendingFrame(id, QuicResetStreamError::FromInternal(error)); + MaybeSendRstStreamFrame(id, QuicResetStreamError::FromInternal(error), 0); } void QuicSession::MaybeSendRstStreamFrame(QuicStreamId id, - QuicRstStreamErrorCode error, + QuicResetStreamError error, QuicStreamOffset bytes_written) { if (!connection()->connected()) { return; @@ -882,11 +911,11 @@ void QuicSession::MaybeSendRstStreamFrame(QuicStreamId id, control_frame_manager_.WriteOrBufferRstStream(id, error, bytes_written); } - connection_->OnStreamReset(id, error); + connection_->OnStreamReset(id, error.internal_code()); } void QuicSession::MaybeSendStopSendingFrame(QuicStreamId id, - QuicRstStreamErrorCode error) { + QuicResetStreamError error) { if (!connection()->connected()) { return; } @@ -956,8 +985,7 @@ void QuicSession::SendMaxStreams(QuicStreamCount stream_count, } void QuicSession::InsertLocallyClosedStreamsHighestOffset( - const QuicStreamId id, - QuicStreamOffset offset) { + const QuicStreamId id, QuicStreamOffset offset) { locally_closed_streams_highest_offset_[id] = offset; } @@ -1045,9 +1073,14 @@ void QuicSession::ClosePendingStream(QuicStreamId stream_id) { } } +bool QuicSession::ShouldProcessFrameByPendingStream(QuicFrameType type, + QuicStreamId id) const { + return UsesPendingStreamForFrame(type, id) && + stream_map_.find(id) == stream_map_.end(); +} + void QuicSession::OnFinalByteOffsetReceived( - QuicStreamId stream_id, - QuicStreamOffset final_byte_offset) { + QuicStreamId stream_id, QuicStreamOffset final_byte_offset) { auto it = locally_closed_streams_highest_offset_.find(stream_id); if (it == locally_closed_streams_highest_offset_.end()) { return; @@ -1308,8 +1341,7 @@ void QuicSession::OnConfigNegotiated() { } absl::optional<std::string> QuicSession::OnAlpsData( - const uint8_t* /*alps_data*/, - size_t /*alps_length*/) { + const uint8_t* /*alps_data*/, size_t /*alps_length*/) { return absl::nullopt; } @@ -1537,10 +1569,8 @@ void QuicSession::OnNewSessionFlowControlWindow(QuicStreamOffset new_window) { } bool QuicSession::OnNewDecryptionKeyAvailable( - EncryptionLevel level, - std::unique_ptr<QuicDecrypter> decrypter, - bool set_alternative_decrypter, - bool latch_once_used) { + EncryptionLevel level, std::unique_ptr<QuicDecrypter> decrypter, + bool set_alternative_decrypter, bool latch_once_used) { if (connection_->version().handshake_protocol == PROTOCOL_TLS1_3 && !connection()->framer().HasEncrypterOfEncryptionLevel( QuicUtils::GetEncryptionLevel( @@ -1563,8 +1593,7 @@ bool QuicSession::OnNewDecryptionKeyAvailable( } void QuicSession::OnNewEncryptionKeyAvailable( - EncryptionLevel level, - std::unique_ptr<QuicEncrypter> encrypter) { + EncryptionLevel level, std::unique_ptr<QuicEncrypter> encrypter) { connection()->SetEncrypter(level, std::move(encrypter)); if (connection_->version().handshake_protocol != PROTOCOL_TLS1_3) { return; @@ -1731,8 +1760,7 @@ bool QuicSession::FillTransportParameters(TransportParameters* params) { } QuicErrorCode QuicSession::ProcessTransportParameters( - const TransportParameters& params, - bool is_resumption, + const TransportParameters& params, bool is_resumption, std::string* error_details) { return config_.ProcessTransportParameters(params, is_resumption, error_details); @@ -1760,8 +1788,7 @@ void QuicSession::OnCryptoHandshakeMessageReceived( const CryptoHandshakeMessage& /*message*/) {} void QuicSession::RegisterStreamPriority( - QuicStreamId id, - bool is_static, + QuicStreamId id, bool is_static, const spdy::SpdyStreamPrecedence& precedence) { write_blocked_streams()->RegisterStream(id, is_static, precedence); } @@ -1771,14 +1798,11 @@ void QuicSession::UnregisterStreamPriority(QuicStreamId id, bool is_static) { } void QuicSession::UpdateStreamPriority( - QuicStreamId id, - const spdy::SpdyStreamPrecedence& new_precedence) { + QuicStreamId id, const spdy::SpdyStreamPrecedence& new_precedence) { write_blocked_streams()->UpdateStreamPriority(id, new_precedence); } -QuicConfig* QuicSession::config() { - return &config_; -} +QuicConfig* QuicSession::config() { return &config_; } void QuicSession::ActivateStream(std::unique_ptr<QuicStream> stream) { QuicStreamId stream_id = stream->id(); @@ -1989,8 +2013,7 @@ void QuicSession::DeleteConnection() { } bool QuicSession::MaybeSetStreamPriority( - QuicStreamId stream_id, - const spdy::SpdyStreamPrecedence& precedence) { + QuicStreamId stream_id, const spdy::SpdyStreamPrecedence& precedence) { auto active_stream = stream_map_.find(stream_id); if (active_stream != stream_map_.end()) { active_stream->second->SetPriority(precedence); @@ -2089,13 +2112,17 @@ void QuicSession::SendRetireConnectionId(uint64_t sequence_number) { void QuicSession::OnServerConnectionIdIssued( const QuicConnectionId& server_connection_id) { - visitor_->OnNewConnectionIdSent(connection_->connection_id(), - server_connection_id); + if (visitor_) { + visitor_->OnNewConnectionIdSent( + connection_->GetOneActiveServerConnectionId(), server_connection_id); + } } void QuicSession::OnServerConnectionIdRetired( const QuicConnectionId& server_connection_id) { - visitor_->OnConnectionIdRetired(server_connection_id); + if (visitor_) { + visitor_->OnConnectionIdRetired(server_connection_id); + } } bool QuicSession::IsConnectionFlowControlBlocked() const { @@ -2468,9 +2495,7 @@ void QuicSession::OnMessageLost(QuicMessageId message_id) { << " is considered lost"; } -void QuicSession::CleanUpClosedStreams() { - closed_streams_.clear(); -} +void QuicSession::CleanUpClosedStreams() { closed_streams_.clear(); } QuicPacketLength QuicSession::GetCurrentLargestMessagePayload() const { return connection_->GetCurrentLargestMessagePayload(); @@ -2579,6 +2604,21 @@ EncryptionLevel QuicSession::GetEncryptionLevelToSendApplicationData() const { return connection_->framer().GetEncryptionLevelToSendApplicationData(); } +void QuicSession::ProcessAllPendingStreams() { + std::vector<PendingStream*> pending_streams; + pending_streams.reserve(pending_stream_map_.size()); + for (auto it = pending_stream_map_.cbegin(); it != pending_stream_map_.cend(); + ++it) { + pending_streams.push_back(it->second.get()); + } + for (auto* pending_stream : pending_streams) { + MaybeProcessPendingStream(pending_stream); + if (!connection()->connected()) { + return; + } + } +} + void QuicSession::ValidatePath( std::unique_ptr<QuicPathValidationContext> context, std::unique_ptr<QuicPathValidator::ResultDelegate> result_delegate) { @@ -2591,8 +2631,7 @@ bool QuicSession::HasPendingPathValidation() const { bool QuicSession::MigratePath(const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, - QuicPacketWriter* writer, - bool owns_writer) { + QuicPacketWriter* writer, bool owns_writer) { return connection_->MigratePath(self_address, peer_address, writer, owns_writer); } 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 9eb7bd1614f..8789526e6d4 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 @@ -20,6 +20,8 @@ #include "absl/types/span.h" #include "quic/core/crypto/tls_connection.h" #include "quic/core/frames/quic_ack_frequency_frame.h" +#include "quic/core/frames/quic_stop_sending_frame.h" +#include "quic/core/frames/quic_window_update_frame.h" #include "quic/core/handshaker_delegate_interface.h" #include "quic/core/legacy_quic_stream_id_manager.h" #include "quic/core/quic_connection.h" @@ -97,13 +99,11 @@ class QUIC_EXPORT_PRIVATE QuicSession }; // Does not take ownership of |connection| or |visitor|. - QuicSession(QuicConnection* connection, - Visitor* owner, + QuicSession(QuicConnection* connection, Visitor* owner, const QuicConfig& config, const ParsedQuicVersionVector& supported_versions, QuicStreamCount num_expected_unidirectional_static_streams); - QuicSession(QuicConnection* connection, - Visitor* owner, + QuicSession(QuicConnection* connection, Visitor* owner, const QuicConfig& config, const ParsedQuicVersionVector& supported_versions, QuicStreamCount num_expected_unidirectional_static_streams, @@ -183,14 +183,12 @@ class QUIC_EXPORT_PRIVATE QuicSession QuicStreamOffset offset, QuicByteCount data_length, QuicDataWriter* writer) override; - bool WriteCryptoData(EncryptionLevel level, - QuicStreamOffset offset, + bool WriteCryptoData(EncryptionLevel level, QuicStreamOffset offset, QuicByteCount data_length, QuicDataWriter* writer) override; // SessionNotifierInterface methods: - bool OnFrameAcked(const QuicFrame& frame, - QuicTime::Delta ack_delay_time, + bool OnFrameAcked(const QuicFrame& frame, QuicTime::Delta ack_delay_time, QuicTime receive_timestamp) override; void OnStreamFrameRetransmitted(const QuicStreamFrame& frame) override; void OnFrameLost(const QuicFrame& frame) override; @@ -299,8 +297,7 @@ class QUIC_EXPORT_PRIVATE QuicSession bool set_alternative_decrypter, bool latch_once_used) override; void OnNewEncryptionKeyAvailable( - EncryptionLevel level, - std::unique_ptr<QuicEncrypter> encrypter) override; + EncryptionLevel level, std::unique_ptr<QuicEncrypter> encrypter) override; void SetDefaultEncryptionLevel(EncryptionLevel level) override; void OnTlsHandshakeComplete() override; void DiscardOldDecryptionKey(EncryptionLevel level) override; @@ -323,8 +320,7 @@ class QUIC_EXPORT_PRIVATE QuicSession std::string error_details) override; // Sets priority in the write blocked list. void RegisterStreamPriority( - QuicStreamId id, - bool is_static, + QuicStreamId id, bool is_static, const spdy::SpdyStreamPrecedence& precedence) override; // Clears priority from the write blocked list. void UnregisterStreamPriority(QuicStreamId id, bool is_static) override; @@ -343,8 +339,7 @@ class QUIC_EXPORT_PRIVATE QuicSession TransmissionType type, EncryptionLevel level) override; - size_t SendCryptoData(EncryptionLevel level, - size_t write_length, + size_t SendCryptoData(EncryptionLevel level, size_t write_length, QuicStreamOffset offset, TransmissionType type) override; @@ -466,8 +461,7 @@ class QUIC_EXPORT_PRIVATE QuicSession // Switch to the path described in |context| without validating the path. bool MigratePath(const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, - QuicPacketWriter* writer, - bool owns_writer); + QuicPacketWriter* writer, bool owns_writer); // Returns the largest payload that will fit into a single MESSAGE frame. // Because overhead can vary during a connection, this method should be @@ -588,12 +582,12 @@ class QUIC_EXPORT_PRIVATE QuicSession // Does actual work of sending RESET_STREAM, if the stream type allows. // Also informs the connection so that pending stream frames can be flushed. virtual void MaybeSendRstStreamFrame(QuicStreamId id, - QuicRstStreamErrorCode error, + QuicResetStreamError error, QuicStreamOffset bytes_written); // Sends a STOP_SENDING frame if the stream type allows. virtual void MaybeSendStopSendingFrame(QuicStreamId id, - QuicRstStreamErrorCode error); + QuicResetStreamError error); // Returns the encryption level to send application data. EncryptionLevel GetEncryptionLevelToSendApplicationData() const; @@ -623,6 +617,14 @@ class QUIC_EXPORT_PRIVATE QuicSession virtual QuicSSLConfig GetSSLConfig() const { return QuicSSLConfig(); } + // Get latched flag value. + bool quic_tls_disable_resumption_refactor() const { + return quic_tls_disable_resumption_refactor_; + } + + // Try converting all pending streams to normal streams. + void ProcessAllPendingStreams(); + protected: using StreamMap = absl::flat_hash_map<QuicStreamId, std::unique_ptr<QuicStream>>; @@ -675,11 +677,17 @@ class QUIC_EXPORT_PRIVATE QuicSession virtual void OnFinalByteOffsetReceived(QuicStreamId id, QuicStreamOffset final_byte_offset); - // Returns true if incoming unidirectional streams should be buffered until - // the first byte of the stream arrives. - // If a subclass returns true here, it should make sure to implement - // ProcessPendingStream(). - virtual bool UsesPendingStreams() const { return false; } + // Returns true if a frame with the given type and id can be prcoessed by a + // PendingStream. However, the frame will always be processed by a QuicStream + // if one exists with the given stream_id. + virtual bool UsesPendingStreamForFrame(QuicFrameType /*type*/, + QuicStreamId /*stream_id*/) const { + return false; + } + + // Returns true if a pending stream should be converted to a real stream after + // a corresponding STREAM_FRAME is received. + virtual bool ShouldProcessPendingStreamImmediately() const { return true; } spdy::SpdyPriority GetSpdyPriorityofStream(QuicStreamId stream_id) const { return write_blocked_streams_.GetSpdyPriorityofStream(stream_id); @@ -825,6 +833,7 @@ class QUIC_EXPORT_PRIVATE QuicSession // closed. QuicStream* GetStream(QuicStreamId id) const; + // Can return NULL, e.g., if the stream has been closed before. PendingStream* GetOrCreatePendingStream(QuicStreamId stream_id); // Let streams and control frame managers retransmit lost data, returns true @@ -837,14 +846,30 @@ class QUIC_EXPORT_PRIVATE QuicSession // Closes the pending stream |stream_id| before it has been created. void ClosePendingStream(QuicStreamId stream_id); - // Creates or gets pending stream, feeds it with |frame|, and processes the - // pending stream. - void PendingStreamOnStreamFrame(const QuicStreamFrame& frame); + // Whether the frame with given type and id should be feed to a pending + // stream. + bool ShouldProcessFrameByPendingStream(QuicFrameType type, + QuicStreamId id) const; + + // Process the pending stream if possible. + void MaybeProcessPendingStream(PendingStream* pending); + + // Creates or gets pending stream, feeds it with |frame|, and returns the + // pending stream. Can return NULL, e.g., if the stream ID is invalid. + PendingStream* PendingStreamOnStreamFrame(const QuicStreamFrame& frame); // Creates or gets pending strea, feed it with |frame|, and closes the pending // stream. void PendingStreamOnRstStream(const QuicRstStreamFrame& frame); + // Creates or gets pending stream, feeds it with |frame|, and records the + // max_data in the pending stream. + void PendingStreamOnWindowUpdateFrame(const QuicWindowUpdateFrame& frame); + + // Creates or gets pending stream, feeds it with |frame|, and records the + // ietf_error_code in the pending stream. + void PendingStreamOnStopSendingFrame(const QuicStopSendingFrame& frame); + // Keep track of highest received byte offset of locally closed streams, while // waiting for a definitive final highest offset from the peer. absl::flat_hash_map<QuicStreamId, QuicStreamOffset> @@ -953,7 +978,10 @@ class QUIC_EXPORT_PRIVATE QuicSession bool liveness_testing_in_progress_; // Whether BoringSSL randomizes the order of TLS extensions. - bool permutes_tls_extensions_ = false; + bool permutes_tls_extensions_ = true; + + const bool quic_tls_disable_resumption_refactor_ = + GetQuicReloadableFlag(quic_tls_disable_resumption_refactor); }; } // 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 84b9e2edc7a..b2f62749fce 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 @@ -23,13 +23,13 @@ #include "quic/core/quic_data_writer.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_stream.h" +#include "quic/core/quic_types.h" #include "quic/core/quic_utils.h" #include "quic/core/quic_versions.h" #include "quic/platform/api/quic_expect_bug.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_mem_slice_storage.h" #include "quic/platform/api/quic_test.h" -#include "quic/platform/api/quic_test_mem_slice_vector.h" #include "quic/test_tools/mock_quic_session_visitor.h" #include "quic/test_tools/quic_config_peer.h" #include "quic/test_tools/quic_connection_peer.h" @@ -168,6 +168,15 @@ class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker { void OnConnectionClosed(QuicErrorCode /*error*/, ConnectionCloseSource /*source*/) override {} + bool ExportKeyingMaterial(absl::string_view /*label*/, + absl::string_view /*context*/, + size_t /*result_len*/, + std::string* /*result*/) override { + return false; + } + + SSL* GetSsl() const override { return nullptr; } + private: using QuicCryptoStream::session; @@ -187,8 +196,8 @@ class TestStream : public QuicStream { StreamType type) : QuicStream(id, session, is_static, type) {} - TestStream(PendingStream* pending, QuicSession* session, StreamType type) - : QuicStream(pending, session, type, /*is_static=*/false) {} + TestStream(PendingStream* pending, QuicSession* session) + : QuicStream(pending, session, /*is_static=*/false) {} using QuicStream::CloseWriteSide; using QuicStream::WriteMemSlices; @@ -279,11 +288,7 @@ class TestSession : public QuicSession { } TestStream* CreateIncomingStream(PendingStream* pending) override { - QuicStreamId id = pending->id(); - TestStream* stream = new TestStream( - pending, this, - DetermineStreamType(id, connection()->version(), perspective(), - /*is_incoming=*/true, BIDIRECTIONAL)); + TestStream* stream = new TestStream(pending, this); ActivateStream(absl::WrapUnique(stream)); ++num_incoming_streams_created_; return stream; @@ -293,6 +298,9 @@ class TestSession : public QuicSession { // test that the session handles pending streams correctly in terms of // receiving stream frames. QuicStream* ProcessPendingStream(PendingStream* pending) override { + if (pending->is_bidirectional()) { + return CreateIncomingStream(pending); + } struct iovec iov; if (pending->sequencer()->GetReadableRegion(&iov)) { // Create TestStream once the first byte is received. @@ -368,12 +376,45 @@ class TestSession : public QuicSession { GetEncryptionLevelToSendApplicationData()); } - bool UsesPendingStreams() const override { return uses_pending_streams_; } + bool UsesPendingStreamForFrame(QuicFrameType type, + QuicStreamId stream_id) const override { + if (!uses_pending_streams_) { + return false; + } + // Uses pending stream for STREAM/RST_STREAM frames with unidirectional read + // stream and uses pending stream for + // STREAM/RST_STREAM/STOP_SENDING/WINDOW_UPDATE frames with bidirectional + // stream. + bool is_incoming_stream = IsIncomingStream(stream_id); + StreamType stream_type = QuicUtils::GetStreamType( + stream_id, perspective(), is_incoming_stream, version()); + switch (type) { + case STREAM_FRAME: + ABSL_FALLTHROUGH_INTENDED; + case RST_STREAM_FRAME: + return is_incoming_stream; + case STOP_SENDING_FRAME: + ABSL_FALLTHROUGH_INTENDED; + case WINDOW_UPDATE_FRAME: + return stream_type == BIDIRECTIONAL; + default: + return false; + } + } + + bool ShouldProcessPendingStreamImmediately() const override { + return process_pending_stream_immediately_; + } void set_uses_pending_streams(bool uses_pending_streams) { uses_pending_streams_ = uses_pending_streams; } + void set_process_pending_stream_immediately( + bool process_pending_stream_immediately) { + process_pending_stream_immediately_ = process_pending_stream_immediately; + } + int num_incoming_streams_created() const { return num_incoming_streams_created_; } @@ -390,6 +431,7 @@ class TestSession : public QuicSession { bool writev_consumes_all_data_; bool uses_pending_streams_; + bool process_pending_stream_immediately_ = true; QuicFrame save_frame_; int num_incoming_streams_created_; }; @@ -1784,6 +1826,7 @@ TEST_P(QuicSessionTestServer, PendingStreams) { return; } session_.set_uses_pending_streams(true); + session_.set_process_pending_stream_immediately(true); QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId( transport_version(), Perspective::IS_CLIENT); @@ -1798,11 +1841,50 @@ TEST_P(QuicSessionTestServer, PendingStreams) { EXPECT_EQ(1, session_.num_incoming_streams_created()); } +TEST_P(QuicSessionTestServer, BufferAllIncomingStreams) { + if (!VersionUsesHttp3(transport_version())) { + return; + } + session_.set_uses_pending_streams(true); + session_.set_process_pending_stream_immediately(false); + + QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_CLIENT); + QuicStreamFrame data1(stream_id, true, 10, absl::string_view("HT")); + session_.OnStreamFrame(data1); + EXPECT_TRUE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); + EXPECT_EQ(0, session_.num_incoming_streams_created()); + // Read unidirectional stream is still buffered when the first byte arrives. + QuicStreamFrame data2(stream_id, false, 0, absl::string_view("HT")); + session_.OnStreamFrame(data2); + EXPECT_TRUE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); + EXPECT_EQ(0, session_.num_incoming_streams_created()); + + // Bidirectional stream is buffered. + QuicStreamId bidirectional_stream_id = + QuicUtils::GetFirstBidirectionalStreamId(transport_version(), + Perspective::IS_CLIENT); + QuicStreamFrame data3(bidirectional_stream_id, false, 0, + absl::string_view("HT")); + session_.OnStreamFrame(data3); + EXPECT_TRUE( + QuicSessionPeer::GetPendingStream(&session_, bidirectional_stream_id)); + EXPECT_EQ(0, session_.num_incoming_streams_created()); + + session_.ProcessAllPendingStreams(); + // Both bidirectional and read-unidirectional streams are unbuffered. + EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); + EXPECT_FALSE( + QuicSessionPeer::GetPendingStream(&session_, bidirectional_stream_id)); + EXPECT_EQ(2, session_.num_incoming_streams_created()); +} + TEST_P(QuicSessionTestServer, RstPendingStreams) { if (!VersionUsesHttp3(transport_version())) { return; } session_.set_uses_pending_streams(true); + session_.set_process_pending_stream_immediately(false); QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId( transport_version(), Perspective::IS_CLIENT); @@ -1824,6 +1906,27 @@ TEST_P(QuicSessionTestServer, RstPendingStreams) { EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); EXPECT_EQ(0, session_.num_incoming_streams_created()); EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); + + session_.ProcessAllPendingStreams(); + // Bidirectional stream is buffered. + QuicStreamId bidirectional_stream_id = + QuicUtils::GetFirstBidirectionalStreamId(transport_version(), + Perspective::IS_CLIENT); + QuicStreamFrame data3(bidirectional_stream_id, false, 0, + absl::string_view("HT")); + session_.OnStreamFrame(data3); + EXPECT_TRUE( + QuicSessionPeer::GetPendingStream(&session_, bidirectional_stream_id)); + EXPECT_EQ(0, session_.num_incoming_streams_created()); + + // Bidirectional pending stream is removed after RST_STREAM is received. + QuicRstStreamFrame rst2(kInvalidControlFrameId, bidirectional_stream_id, + QUIC_ERROR_PROCESSING_STREAM, 12); + session_.OnRstStream(rst2); + EXPECT_FALSE( + QuicSessionPeer::GetPendingStream(&session_, bidirectional_stream_id)); + EXPECT_EQ(0, session_.num_incoming_streams_created()); + EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); } TEST_P(QuicSessionTestServer, OnFinPendingStreams) { @@ -1831,6 +1934,7 @@ TEST_P(QuicSessionTestServer, OnFinPendingStreams) { return; } session_.set_uses_pending_streams(true); + session_.set_process_pending_stream_immediately(true); QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId( transport_version(), Perspective::IS_CLIENT); @@ -1840,9 +1944,30 @@ TEST_P(QuicSessionTestServer, OnFinPendingStreams) { EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); EXPECT_EQ(0, session_.num_incoming_streams_created()); EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); + + session_.set_process_pending_stream_immediately(false); + // Bidirectional pending stream remains after Fin is received. + // Bidirectional stream is buffered. + QuicStreamId bidirectional_stream_id = + QuicUtils::GetFirstBidirectionalStreamId(transport_version(), + Perspective::IS_CLIENT); + QuicStreamFrame data2(bidirectional_stream_id, true, 0, + absl::string_view("HT")); + session_.OnStreamFrame(data2); + EXPECT_TRUE( + QuicSessionPeer::GetPendingStream(&session_, bidirectional_stream_id)); + EXPECT_EQ(0, session_.num_incoming_streams_created()); + + session_.ProcessAllPendingStreams(); + EXPECT_FALSE( + QuicSessionPeer::GetPendingStream(&session_, bidirectional_stream_id)); + EXPECT_EQ(1, session_.num_incoming_streams_created()); + QuicStream* bidirectional_stream = + QuicSessionPeer::GetStream(&session_, bidirectional_stream_id); + EXPECT_TRUE(bidirectional_stream->fin_received()); } -TEST_P(QuicSessionTestServer, PendingStreamOnWindowUpdate) { +TEST_P(QuicSessionTestServer, UnidirectionalPendingStreamOnWindowUpdate) { if (!VersionUsesHttp3(transport_version())) { return; } @@ -1864,6 +1989,80 @@ TEST_P(QuicSessionTestServer, PendingStreamOnWindowUpdate) { session_.OnWindowUpdateFrame(window_update_frame); } +TEST_P(QuicSessionTestServer, BidirectionalPendingStreamOnWindowUpdate) { + if (!VersionUsesHttp3(transport_version())) { + return; + } + + session_.set_uses_pending_streams(true); + session_.set_process_pending_stream_immediately(false); + QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId( + transport_version(), Perspective::IS_CLIENT); + QuicStreamFrame data(stream_id, true, 10, absl::string_view("HT")); + session_.OnStreamFrame(data); + QuicWindowUpdateFrame window_update_frame(kInvalidControlFrameId, stream_id, + kDefaultFlowControlSendWindow * 2); + session_.OnWindowUpdateFrame(window_update_frame); + EXPECT_TRUE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); + EXPECT_EQ(0, session_.num_incoming_streams_created()); + + session_.ProcessAllPendingStreams(); + EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); + EXPECT_EQ(1, session_.num_incoming_streams_created()); + QuicStream* bidirectional_stream = + QuicSessionPeer::GetStream(&session_, stream_id); + QuicByteCount send_window = + QuicStreamPeer::SendWindowSize(bidirectional_stream); + EXPECT_EQ(send_window, kDefaultFlowControlSendWindow * 2); +} + +TEST_P(QuicSessionTestServer, UnidirectionalPendingStreamOnStopSending) { + if (!VersionUsesHttp3(transport_version())) { + return; + } + + session_.set_uses_pending_streams(true); + QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId( + transport_version(), Perspective::IS_CLIENT); + QuicStreamFrame data1(stream_id, true, 10, absl::string_view("HT")); + session_.OnStreamFrame(data1); + EXPECT_TRUE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); + EXPECT_EQ(0, session_.num_incoming_streams_created()); + QuicStopSendingFrame stop_sending_frame(kInvalidControlFrameId, stream_id, + QUIC_STREAM_CANCELLED); + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_INVALID_STREAM_ID, + "Received STOP_SENDING for a read-only stream", _)); + session_.OnStopSendingFrame(stop_sending_frame); +} + +TEST_P(QuicSessionTestServer, BidirectionalPendingStreamOnStopSending) { + if (!VersionUsesHttp3(transport_version())) { + return; + } + + session_.set_uses_pending_streams(true); + session_.set_process_pending_stream_immediately(false); + QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId( + transport_version(), Perspective::IS_CLIENT); + QuicStreamFrame data(stream_id, true, 0, absl::string_view("HT")); + session_.OnStreamFrame(data); + QuicStopSendingFrame stop_sending_frame(kInvalidControlFrameId, stream_id, + QUIC_STREAM_CANCELLED); + session_.OnStopSendingFrame(stop_sending_frame); + EXPECT_TRUE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); + EXPECT_EQ(0, session_.num_incoming_streams_created()); + + EXPECT_CALL(*connection_, OnStreamReset(stream_id, _)); + session_.ProcessAllPendingStreams(); + EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); + EXPECT_EQ(1, session_.num_incoming_streams_created()); + QuicStream* bidirectional_stream = + QuicSessionPeer::GetStream(&session_, stream_id); + EXPECT_TRUE(bidirectional_stream->write_side_closed()); +} + TEST_P(QuicSessionTestServer, DrainingStreamsDoNotCountAsOpened) { // Verify that a draining stream (which has received a FIN but not consumed // it) does not count against the open quota (because it is closed from the @@ -2571,12 +2770,11 @@ TEST_P(QuicSessionTestServer, WriteMemSlicesOnReadUnidirectionalStream) { CloseConnection( QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM, _, _)) .Times(1); - char data[1024]; - std::vector<std::pair<char*, size_t>> buffers; - buffers.push_back(std::make_pair(data, ABSL_ARRAYSIZE(data))); - buffers.push_back(std::make_pair(data, ABSL_ARRAYSIZE(data))); - QuicTestMemSliceVector vector(buffers); - stream4->WriteMemSlices(vector.span(), false); + std::string data(1024, 'a'); + std::vector<QuicMemSlice> buffers; + buffers.push_back(MemSliceFromString(data)); + buffers.push_back(MemSliceFromString(data)); + stream4->WriteMemSlices(absl::MakeSpan(buffers), false); } // Test code that tests that an incoming stream frame with a new (not previously 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 cde50ef214d..f055bdf2e9f 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 @@ -15,11 +15,13 @@ #include "quic/core/quic_session.h" #include "quic/core/quic_types.h" #include "quic/core/quic_utils.h" +#include "quic/core/quic_versions.h" #include "quic/platform/api/quic_bug_tracker.h" #include "quic/platform/api/quic_flag_utils.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_mem_slice.h" +#include "common/platform/api/quiche_logging.h" using spdy::SpdyPriority; @@ -118,9 +120,12 @@ PendingStream::PendingStream(QuicStreamId id, QuicSession* session) stream_delegate_(session), stream_bytes_read_(0), fin_received_(false), + is_bidirectional_(QuicUtils::GetStreamType(id, session->perspective(), + /*peer_initiated = */ true, + session->version()) == + BIDIRECTIONAL), connection_flow_controller_(session->flow_controller()), - flow_controller_(session, - id, + flow_controller_(session, id, /*is_connection_flow_controller*/ false, GetReceivedFlowControlWindow(session, id), GetInitialStreamFlowControlWindowToSend(session, id), @@ -134,9 +139,7 @@ void PendingStream::OnDataAvailable() { // QuicSession::ProcessPendingStream() can read it. } -void PendingStream::OnFinRead() { - QUICHE_DCHECK(sequencer_.IsClosed()); -} +void PendingStream::OnFinRead() { QUICHE_DCHECK(sequencer_.IsClosed()); } void PendingStream::AddBytesConsumed(QuicByteCount bytes) { // It will be called when the metadata of the stream is consumed. @@ -144,7 +147,7 @@ void PendingStream::AddBytesConsumed(QuicByteCount bytes) { connection_flow_controller_->AddBytesConsumed(bytes); } -void PendingStream::Reset(QuicRstStreamErrorCode /*error*/) { +void PendingStream::ResetWithError(QuicResetStreamError /*error*/) { // Currently PendingStream is only read-unidirectional. It shouldn't send // Reset. QUIC_NOTREACHED(); @@ -161,13 +164,9 @@ void PendingStream::OnUnrecoverableError(QuicErrorCode error, stream_delegate_->OnStreamError(error, ietf_error, details); } -QuicStreamId PendingStream::id() const { - return id_; -} +QuicStreamId PendingStream::id() const { return id_; } -ParsedQuicVersion PendingStream::version() const { - return version_; -} +ParsedQuicVersion PendingStream::version() const { return version_; } void PendingStream::OnStreamFrame(const QuicStreamFrame& frame) { QUICHE_DCHECK_EQ(frame.stream_id, id_); @@ -252,6 +251,11 @@ void PendingStream::OnRstStreamFrame(const QuicRstStreamFrame& frame) { } } +void PendingStream::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { + QUICHE_DCHECK(is_bidirectional_); + flow_controller_.UpdateSendWindowOffset(frame.max_data); +} + bool PendingStream::MaybeIncreaseHighestReceivedOffset( QuicStreamOffset new_offset) { uint64_t increment = @@ -268,6 +272,13 @@ bool PendingStream::MaybeIncreaseHighestReceivedOffset( return true; } +void PendingStream::OnStopSending( + QuicResetStreamError stop_sending_error_code) { + if (!stop_sending_error_code_) { + stop_sending_error_code_ = stop_sending_error_code; + } +} + void PendingStream::MarkConsumed(QuicByteCount num_bytes) { sequencer_.MarkConsumed(num_bytes); } @@ -277,19 +288,17 @@ void PendingStream::StopReading() { sequencer_.StopReading(); } -QuicStream::QuicStream(PendingStream* pending, - QuicSession* session, - StreamType type, +QuicStream::QuicStream(PendingStream* pending, QuicSession* session, bool is_static) - : QuicStream(pending->id_, - session, - std::move(pending->sequencer_), + : QuicStream(pending->id_, session, std::move(pending->sequencer_), is_static, - type, - pending->stream_bytes_read_, - pending->fin_received_, + QuicUtils::GetStreamType(pending->id_, session->perspective(), + /*peer_initiated = */ true, + session->version()), + pending->stream_bytes_read_, pending->fin_received_, std::move(pending->flow_controller_), pending->connection_flow_controller_) { + QUICHE_DCHECK(session->version().HasIetfQuicFrames()); sequencer_.set_stream(this); } @@ -317,26 +326,15 @@ absl::optional<QuicFlowController> FlowController(QuicStreamId id, } // namespace -QuicStream::QuicStream(QuicStreamId id, - QuicSession* session, - bool is_static, +QuicStream::QuicStream(QuicStreamId id, QuicSession* session, bool is_static, StreamType type) - : QuicStream(id, - session, - QuicStreamSequencer(this), - is_static, - type, - 0, - false, - FlowController(id, session, type), + : QuicStream(id, session, QuicStreamSequencer(this), is_static, type, 0, + false, FlowController(id, session, type), session->flow_controller()) {} -QuicStream::QuicStream(QuicStreamId id, - QuicSession* session, - QuicStreamSequencer sequencer, - bool is_static, - StreamType type, - uint64_t stream_bytes_read, +QuicStream::QuicStream(QuicStreamId id, QuicSession* session, + QuicStreamSequencer sequencer, bool is_static, + StreamType type, uint64_t stream_bytes_read, bool fin_received, absl::optional<QuicFlowController> flow_controller, QuicFlowController* connection_flow_controller) @@ -346,10 +344,11 @@ QuicStream::QuicStream(QuicStreamId id, stream_delegate_(session), precedence_(CalculateDefaultPriority(session)), stream_bytes_read_(stream_bytes_read), - stream_error_(QUIC_STREAM_NO_ERROR), + stream_error_(QuicResetStreamError::NoError()), connection_error_(QUIC_NO_ERROR), read_side_closed_(false), write_side_closed_(false), + write_side_data_recvd_state_notified_(false), fin_buffered_(false), fin_sent_(false), fin_outstanding_(false), @@ -371,8 +370,7 @@ QuicStream::QuicStream(QuicStreamId id, was_draining_(false), type_(VersionHasIetfQuicFrames(session->transport_version()) && type != CRYPTO - ? QuicUtils::GetStreamType(id_, - session->perspective(), + ? QuicUtils::GetStreamType(id_, session->perspective(), session->IsIncomingStream(id_), session->version()) : type), @@ -489,7 +487,7 @@ void QuicStream::OnStreamFrame(const QuicStreamFrame& frame) { sequencer_.OnStreamFrame(frame); } -bool QuicStream::OnStopSending(QuicRstStreamErrorCode code) { +bool QuicStream::OnStopSending(QuicResetStreamError error) { // Do not reset the stream if all data has been sent and acknowledged. if (write_side_closed() && !IsWaitingForAcks()) { QUIC_DVLOG(1) << ENDPOINT @@ -507,8 +505,14 @@ bool QuicStream::OnStopSending(QuicRstStreamErrorCode code) { return false; } - stream_error_ = code; - MaybeSendRstStream(code); + stream_error_ = error; + if (GetQuicReloadableFlag(quic_match_ietf_reset_code)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_match_ietf_reset_code); + MaybeSendRstStream(error); + } else { + MaybeSendRstStream( + QuicResetStreamError::FromInternal(error.internal_code())); + } return true; } @@ -553,7 +557,7 @@ void QuicStream::OnStreamReset(const QuicRstStreamFrame& frame) { return; } - stream_error_ = frame.error_code; + stream_error_ = frame.error(); // Google QUIC closes both sides of the stream in response to a // RESET_STREAM, IETF QUIC closes only the read side. if (!VersionHasIetfQuicFrames(transport_version())) { @@ -568,7 +572,8 @@ void QuicStream::OnConnectionClosed(QuicErrorCode error, return; } if (error != QUIC_NO_ERROR) { - stream_error_ = QUIC_STREAM_CONNECTION_ERROR; + stream_error_ = + QuicResetStreamError::FromInternal(QUIC_STREAM_CONNECTION_ERROR); connection_error_ = error; } @@ -593,6 +598,10 @@ void QuicStream::SetFinSent() { } void QuicStream::Reset(QuicRstStreamErrorCode error) { + ResetWithError(QuicResetStreamError::FromInternal(error)); +} + +void QuicStream::ResetWithError(QuicResetStreamError error) { stream_error_ = error; QuicConnection::ScopedPacketFlusher flusher(session()->connection()); MaybeSendStopSending(error); @@ -600,7 +609,24 @@ void QuicStream::Reset(QuicRstStreamErrorCode error) { if (read_side_closed_ && write_side_closed_ && !IsWaitingForAcks()) { session()->MaybeCloseZombieStream(id_); - return; + } +} + +void QuicStream::ResetWriteSide(QuicResetStreamError error) { + stream_error_ = error; + MaybeSendRstStream(error); + + if (read_side_closed_ && write_side_closed_ && !IsWaitingForAcks()) { + session()->MaybeCloseZombieStream(id_); + } +} + +void QuicStream::SendStopSending(QuicResetStreamError error) { + stream_error_ = error; + MaybeSendStopSending(error); + + if (read_side_closed_ && write_side_closed_ && !IsWaitingForAcks()) { + session()->MaybeCloseZombieStream(id_); } } @@ -628,8 +654,7 @@ void QuicStream::SetPriority(const spdy::SpdyStreamPrecedence& precedence) { } void QuicStream::WriteOrBufferData( - absl::string_view data, - bool fin, + absl::string_view data, bool fin, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { QUIC_BUG_IF(quic_bug_12570_4, QuicUtils::IsCryptoStreamId(transport_version(), id_)) @@ -740,17 +765,12 @@ void QuicStream::MaybeSendBlocked() { } } -QuicConsumedData QuicStream::WriteMemSlices(QuicMemSliceSpan span, bool fin) { - return WriteMemSlicesInner(MemSliceSpanWrapper(span), fin); +QuicConsumedData QuicStream::WriteMemSlice(QuicMemSlice span, bool fin) { + return WriteMemSlices(absl::MakeSpan(&span, 1), fin); } QuicConsumedData QuicStream::WriteMemSlices(absl::Span<QuicMemSlice> span, bool fin) { - return WriteMemSlicesInner(MemSliceSpanWrapper(span), fin); -} - -QuicConsumedData QuicStream::WriteMemSlicesInner(MemSliceSpanWrapper span, - bool fin) { QuicConsumedData consumed_data(0, false); if (span.empty() && !fin) { QUIC_BUG(quic_bug_10586_6) << "span.empty() && !fin"; @@ -778,7 +798,7 @@ QuicConsumedData QuicStream::WriteMemSlicesInner(MemSliceSpanWrapper span, if (!span.empty()) { // Buffer all data if buffered data size is below limit. QuicStreamOffset offset = send_buffer_.stream_offset(); - consumed_data.bytes_consumed = span.SaveTo(send_buffer_); + consumed_data.bytes_consumed = send_buffer_.SaveMemSliceSpan(span); if (offset > send_buffer_.stream_offset() || kMaxStreamLength < send_buffer_.stream_offset()) { QUIC_BUG(quic_bug_10586_8) << "Write too many data via stream " << id_; @@ -841,12 +861,12 @@ void QuicStream::CloseWriteSide() { } } -void QuicStream::MaybeSendStopSending(QuicRstStreamErrorCode error) { +void QuicStream::MaybeSendStopSending(QuicResetStreamError error) { if (stop_sending_sent_) { return; } - if (!session()->version().UsesHttp3() && error != QUIC_STREAM_NO_ERROR) { + if (!session()->version().UsesHttp3() && !error.ok()) { // In gQUIC, RST with error closes both read and write side. return; } @@ -854,21 +874,21 @@ void QuicStream::MaybeSendStopSending(QuicRstStreamErrorCode error) { if (session()->version().UsesHttp3()) { session()->MaybeSendStopSendingFrame(id(), error); } else { - QUICHE_DCHECK_EQ(QUIC_STREAM_NO_ERROR, error); - session()->MaybeSendRstStreamFrame(id(), QUIC_STREAM_NO_ERROR, + QUICHE_DCHECK_EQ(QUIC_STREAM_NO_ERROR, error.internal_code()); + session()->MaybeSendRstStreamFrame(id(), QuicResetStreamError::NoError(), stream_bytes_written()); } stop_sending_sent_ = true; CloseReadSide(); } -void QuicStream::MaybeSendRstStream(QuicRstStreamErrorCode error) { +void QuicStream::MaybeSendRstStream(QuicResetStreamError error) { if (rst_sent_) { return; } if (!session()->version().UsesHttp3()) { - QUIC_BUG_IF(quic_bug_12570_5, error == QUIC_STREAM_NO_ERROR); + QUIC_BUG_IF(quic_bug_12570_5, error.ok()); stop_sending_sent_ = true; CloseReadSide(); } @@ -882,9 +902,7 @@ bool QuicStream::HasBufferedData() const { return send_buffer_.stream_offset() > stream_bytes_written(); } -ParsedQuicVersion QuicStream::version() const { - return session_->version(); -} +ParsedQuicVersion QuicStream::version() const { return session_->version(); } QuicTransportVersion QuicStream::transport_version() const { return session_->transport_version(); @@ -1065,8 +1083,7 @@ void QuicStream::AddRandomPaddingAfterFin() { } bool QuicStream::OnStreamFrameAcked(QuicStreamOffset offset, - QuicByteCount data_length, - bool fin_acked, + QuicByteCount data_length, bool fin_acked, QuicTime::Delta /*ack_delay_time*/, QuicTime /*receive_timestamp*/, QuicByteCount* newly_acked_length) { @@ -1090,6 +1107,11 @@ bool QuicStream::OnStreamFrameAcked(QuicStreamOffset offset, fin_outstanding_ = false; fin_lost_ = false; } + if (!IsWaitingForAcks() && write_side_closed_ && + !write_side_data_recvd_state_notified_) { + OnWriteSideInDataRecvdState(); + write_side_data_recvd_state_notified_ = true; + } if (!IsWaitingForAcks() && read_side_closed_ && write_side_closed_) { session_->MaybeCloseZombieStream(id_); } @@ -1106,8 +1128,7 @@ void QuicStream::OnStreamFrameRetransmitted(QuicStreamOffset offset, } void QuicStream::OnStreamFrameLost(QuicStreamOffset offset, - QuicByteCount data_length, - bool fin_lost) { + QuicByteCount data_length, bool fin_lost) { QUIC_DVLOG(1) << ENDPOINT << "stream " << id_ << " Losting " << "[" << offset << ", " << offset + data_length << "]" << " fin = " << fin_lost; @@ -1120,8 +1141,7 @@ void QuicStream::OnStreamFrameLost(QuicStreamOffset offset, } bool QuicStream::RetransmitStreamData(QuicStreamOffset offset, - QuicByteCount data_length, - bool fin, + QuicByteCount data_length, bool fin, TransmissionType type) { QUICHE_DCHECK(type == PTO_RETRANSMISSION || type == RTO_RETRANSMISSION || type == TLP_RETRANSMISSION || type == PROBING_RETRANSMISSION); @@ -1178,7 +1198,7 @@ bool QuicStream::RetransmitStreamData(QuicStreamOffset offset, } bool QuicStream::IsWaitingForAcks() const { - return (!rst_sent_ || stream_error_ == QUIC_STREAM_NO_ERROR) && + return (!rst_sent_ || stream_error_.ok()) && (send_buffer_.stream_bytes_outstanding() || fin_outstanding_); } @@ -1385,9 +1405,7 @@ bool QuicStream::HasDeadlinePassed() const { return true; } -void QuicStream::OnDeadlinePassed() { - Reset(QUIC_STREAM_TTL_EXPIRED); -} +void QuicStream::OnDeadlinePassed() { Reset(QUIC_STREAM_TTL_EXPIRED); } bool QuicStream::IsFlowControlBlocked() const { if (!flow_controller_.has_value()) { 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 6374d8ab6f6..dc129a28ee6 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 @@ -25,6 +25,8 @@ #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "absl/types/span.h" +#include "quic/core/frames/quic_rst_stream_frame.h" +#include "quic/core/quic_error_codes.h" #include "quic/core/quic_flow_controller.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_stream_send_buffer.h" @@ -34,7 +36,6 @@ #include "quic/core/stream_delegate_interface.h" #include "quic/platform/api/quic_export.h" #include "quic/platform/api/quic_mem_slice.h" -#include "quic/platform/api/quic_mem_slice_span.h" #include "quic/platform/api/quic_reference_counted.h" #include "spdy/core/spdy_protocol.h" @@ -60,7 +61,7 @@ class QUIC_EXPORT_PRIVATE PendingStream void OnDataAvailable() override; void OnFinRead() override; void AddBytesConsumed(QuicByteCount bytes) override; - void Reset(QuicRstStreamErrorCode error) override; + void ResetWithError(QuicResetStreamError error) override; void OnUnrecoverableError(QuicErrorCode error, const std::string& details) override; void OnUnrecoverableError(QuicErrorCode error, @@ -73,10 +74,21 @@ class QUIC_EXPORT_PRIVATE PendingStream // If the data violates flow control, the connection will be closed. void OnStreamFrame(const QuicStreamFrame& frame); + bool is_bidirectional() const { return is_bidirectional_; } + // Stores the final byte offset from |frame|. // If the final offset violates flow control, the connection will be closed. void OnRstStreamFrame(const QuicRstStreamFrame& frame); + void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame); + + void OnStopSending(QuicResetStreamError stop_sending_error_code); + + // The error code received from QuicStopSendingFrame (if any). + const absl::optional<QuicResetStreamError>& GetStopSendingErrorCode() const { + return stop_sending_error_code_; + } + // Returns the number of bytes read on this stream. uint64_t stream_bytes_read() { return stream_bytes_read_; } @@ -109,12 +121,17 @@ class QUIC_EXPORT_PRIVATE PendingStream // True if a frame containing a fin has been received. bool fin_received_; + // True if this pending stream is backing a bidirectional stream. + bool is_bidirectional_; + // Connection-level flow controller. Owned by the session. QuicFlowController* connection_flow_controller_; // Stream-level flow controller. QuicFlowController flow_controller_; // Stores the buffered frames. QuicStreamSequencer sequencer_; + // The error code received from QuicStopSendingFrame (if any). + absl::optional<QuicResetStreamError> stop_sending_error_code_; }; class QUIC_EXPORT_PRIVATE QuicStream @@ -136,14 +153,9 @@ class QUIC_EXPORT_PRIVATE QuicStream // |type| indicates whether the stream is bidirectional, read unidirectional // or write unidirectional. // TODO(fayang): Remove |type| when IETF stream ID numbering fully kicks in. - QuicStream(QuicStreamId id, - QuicSession* session, - bool is_static, + QuicStream(QuicStreamId id, QuicSession* session, bool is_static, StreamType type); - QuicStream(PendingStream* pending, - QuicSession* session, - StreamType type, - bool is_static); + QuicStream(PendingStream* pending, QuicSession* session, bool is_static); QuicStream(const QuicStream&) = delete; QuicStream& operator=(const QuicStream&) = delete; @@ -162,7 +174,16 @@ class QUIC_EXPORT_PRIVATE QuicStream // Called by the subclass or the sequencer to reset the stream from this // end. - void Reset(QuicRstStreamErrorCode error) override; + void ResetWithError(QuicResetStreamError error) override; + // Convenience wrapper for the method above. + // TODO(b/200606367): switch all calls to using QuicResetStreamError + // interface. + void Reset(QuicRstStreamErrorCode error); + + // Reset() sends both RESET_STREAM and STOP_SENDING; the two methods below + // allow to send only one of those. + void ResetWriteSide(QuicResetStreamError error); + void SendStopSending(QuicResetStreamError error); // Called by the subclass or the sequencer to close the entire connection from // this end. @@ -210,7 +231,9 @@ class QUIC_EXPORT_PRIVATE QuicStream // Number of bytes available to read. QuicByteCount ReadableBytes() const; - QuicRstStreamErrorCode stream_error() const { return stream_error_; } + QuicRstStreamErrorCode stream_error() const { + return stream_error_.internal_code(); + } QuicErrorCode connection_error() const { return connection_error_; } bool reading_stopped() const { @@ -292,31 +315,26 @@ class QUIC_EXPORT_PRIVATE QuicStream // session, write_side_closed() becomes true, otherwise fin_buffered_ becomes // true. void WriteOrBufferData( - absl::string_view data, - bool fin, + absl::string_view data, bool fin, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); // Sends |data| to connection with specified |level|. void WriteOrBufferDataAtLevel( - absl::string_view data, - bool fin, - EncryptionLevel level, + absl::string_view data, bool fin, EncryptionLevel level, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); // Adds random padding after the fin is consumed for this stream. void AddRandomPaddingAfterFin(); // Write |data_length| of data starts at |offset| from send buffer. - bool WriteStreamData(QuicStreamOffset offset, - QuicByteCount data_length, + bool WriteStreamData(QuicStreamOffset offset, QuicByteCount data_length, QuicDataWriter* writer); // Called when data [offset, offset + data_length) is acked. |fin_acked| // indicates whether the fin is acked. Returns true and updates // |newly_acked_length| if any new stream data (including fin) gets acked. virtual bool OnStreamFrameAcked(QuicStreamOffset offset, - QuicByteCount data_length, - bool fin_acked, + QuicByteCount data_length, bool fin_acked, QuicTime::Delta ack_delay_time, QuicTime receive_timestamp, QuicByteCount* newly_acked_length); @@ -330,15 +348,13 @@ class QUIC_EXPORT_PRIVATE QuicStream // Called when data [offset, offset + data_length) is considered as lost. // |fin_lost| indicates whether the fin is considered as lost. virtual void OnStreamFrameLost(QuicStreamOffset offset, - QuicByteCount data_length, - bool fin_lost); + QuicByteCount data_length, bool fin_lost); // Called to retransmit outstanding portion in data [offset, offset + // data_length) and |fin| with Transmission |type|. // Returns true if all data gets retransmitted. virtual bool RetransmitStreamData(QuicStreamOffset offset, - QuicByteCount data_length, - bool fin, + QuicByteCount data_length, bool fin, TransmissionType type); // Sets deadline of this stream to be now + |ttl|, returns true if the setting @@ -349,9 +365,8 @@ class QUIC_EXPORT_PRIVATE QuicStream // the wire. This method has all-or-nothing semantics: if the write buffer is // not full, all of the memslices in |span| are moved into it; otherwise, // nothing happens. - // TODO(vasilvv): deprecate and remove QuicMemSliceSpan version. - QuicConsumedData WriteMemSlices(QuicMemSliceSpan span, bool fin); QuicConsumedData WriteMemSlices(absl::Span<QuicMemSlice> span, bool fin); + QuicConsumedData WriteMemSlice(QuicMemSlice span, bool fin); // Returns true if any stream data is lost (including fin) and needs to be // retransmitted. @@ -361,14 +376,13 @@ class QUIC_EXPORT_PRIVATE QuicStream // outstanding or fin is outstanding (if |fin| is true). Returns false // otherwise. bool IsStreamFrameOutstanding(QuicStreamOffset offset, - QuicByteCount data_length, - bool fin) const; + QuicByteCount data_length, bool fin) const; StreamType type() const { return type_; } // Handle received StopSending frame. Returns true if the processing finishes // gracefully. - virtual bool OnStopSending(QuicRstStreamErrorCode code); + virtual bool OnStopSending(QuicResetStreamError error); // Returns true if the stream is static. bool is_static() const { return is_static_; } @@ -393,8 +407,7 @@ class QUIC_EXPORT_PRIVATE QuicStream // Called when data of [offset, offset + data_length] is buffered in send // buffer. virtual void OnDataBuffered( - QuicStreamOffset /*offset*/, - QuicByteCount /*data_length*/, + QuicStreamOffset /*offset*/, QuicByteCount /*data_length*/, const QuicReferenceCountedPointer<QuicAckListenerInterface>& /*ack_listener*/) {} @@ -431,10 +444,18 @@ class QUIC_EXPORT_PRIVATE QuicStream void SetFinSent(); // Send STOP_SENDING if it hasn't been sent yet. - void MaybeSendStopSending(QuicRstStreamErrorCode error); + void MaybeSendStopSending(QuicResetStreamError error); // Send RESET_STREAM if it hasn't been sent yet. - void MaybeSendRstStream(QuicRstStreamErrorCode error); + void MaybeSendRstStream(QuicResetStreamError error); + + // Convenience warppers for two methods above. + void MaybeSendRstStream(QuicRstStreamErrorCode error) { + MaybeSendRstStream(QuicResetStreamError::FromInternal(error)); + } + void MaybeSendStopSending(QuicRstStreamErrorCode error) { + MaybeSendStopSending(QuicResetStreamError::FromInternal(error)); + } // Close the write side of the socket. Further writes will fail. // Can be called by the subclass or internally. @@ -442,7 +463,7 @@ class QUIC_EXPORT_PRIVATE QuicStream virtual void CloseWriteSide(); void set_rst_received(bool rst_received) { rst_received_ = rst_received; } - void set_stream_error(QuicRstStreamErrorCode error) { stream_error_ = error; } + void set_stream_error(QuicResetStreamError error) { stream_error_ = error; } StreamDelegateInterface* stream_delegate() { return stream_delegate_; } @@ -462,6 +483,11 @@ class QUIC_EXPORT_PRIVATE QuicStream QuicStreamSendBuffer& send_buffer() { return send_buffer_; } + // Called when the write side of the stream is closed, and all of the outgoing + // data has been acknowledged. This corresponds to the "Data Recvd" state of + // RFC 9000. + virtual void OnWriteSideInDataRecvdState() {} + // Return the current flow control send window in bytes. absl::optional<QuicByteCount> GetSendWindow() const; absl::optional<QuicByteCount> GetReceiveWindow() const; @@ -470,33 +496,9 @@ class QUIC_EXPORT_PRIVATE QuicStream friend class test::QuicStreamPeer; friend class QuicStreamUtils; - // Wraps around either QuicMemSliceSpan or absl::Span<QuicMemSlice>. - // TODO(vasilvv): delete this after QuicMemSliceSpan is gone. - class QUIC_EXPORT_PRIVATE MemSliceSpanWrapper { - public: - explicit MemSliceSpanWrapper(QuicMemSliceSpan span) : old_(span) {} - explicit MemSliceSpanWrapper(absl::Span<QuicMemSlice> span) : new_(span) {} - - bool empty() { return old_.has_value() ? old_->empty() : new_.empty(); } - QuicByteCount SaveTo(QuicStreamSendBuffer& send_buffer) { - if (old_.has_value()) { - return send_buffer.SaveMemSliceSpan(*old_); - } - return send_buffer.SaveMemSliceSpan(new_); - } - - private: - absl::optional<QuicMemSliceSpan> old_; - absl::Span<QuicMemSlice> new_; - }; - - QuicStream(QuicStreamId id, - QuicSession* session, - QuicStreamSequencer sequencer, - bool is_static, - StreamType type, - uint64_t stream_bytes_read, - bool fin_received, + QuicStream(QuicStreamId id, QuicSession* session, + QuicStreamSequencer sequencer, bool is_static, StreamType type, + uint64_t stream_bytes_read, bool fin_received, absl::optional<QuicFlowController> flow_controller, QuicFlowController* connection_flow_controller); @@ -518,8 +520,6 @@ class QUIC_EXPORT_PRIVATE QuicStream // Returns true if deadline_ has passed. bool HasDeadlinePassed() const; - QuicConsumedData WriteMemSlicesInner(MemSliceSpanWrapper span, bool fin); - QuicStreamSequencer sequencer_; QuicStreamId id_; // Pointer to the owning QuicSession object. @@ -534,7 +534,7 @@ class QUIC_EXPORT_PRIVATE QuicStream // Stream error code received from a RstStreamFrame or error code sent by the // visitor or sequencer in the RstStreamFrame. - QuicRstStreamErrorCode stream_error_; + QuicResetStreamError stream_error_; // Connection error code due to which the stream was closed. |stream_error_| // is set to |QUIC_STREAM_CONNECTION_ERROR| when this happens and consumers // should check |connection_error_|. @@ -545,6 +545,9 @@ class QUIC_EXPORT_PRIVATE QuicStream // True if the write side is closed, and further writes should fail. bool write_side_closed_; + // True if OnWriteSideInDataRecvdState() has already been called. + bool write_side_data_recvd_state_notified_; + // True if the subclass has written a FIN with WriteOrBufferData, but it was // buffered in queued_data_ rather than being sent to the session. bool fin_buffered_; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.cc index 1e32e544cd1..b2ac6bfc7c4 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.cc @@ -92,11 +92,6 @@ void QuicStreamSendBuffer::SaveMemSlice(QuicMemSlice slice) { stream_offset_ += length; } -QuicByteCount QuicStreamSendBuffer::SaveMemSliceSpan(QuicMemSliceSpan span) { - return span.ConsumeAll( - [&](QuicMemSlice slice) { SaveMemSlice(std::move(slice)); }); -} - QuicByteCount QuicStreamSendBuffer::SaveMemSliceSpan( absl::Span<QuicMemSlice> span) { QuicByteCount total = 0; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h index f91e4760870..bf50217cefd 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h @@ -12,7 +12,6 @@ #include "quic/core/quic_types.h" #include "quic/platform/api/quic_iovec.h" #include "quic/platform/api/quic_mem_slice.h" -#include "quic/platform/api/quic_mem_slice_span.h" #include "common/quiche_circular_deque.h" namespace quic { @@ -80,7 +79,6 @@ class QUIC_EXPORT_PRIVATE QuicStreamSendBuffer { void SaveMemSlice(QuicMemSlice slice); // Save all slices in |span| to send buffer. Return total bytes saved. - QuicByteCount SaveMemSliceSpan(QuicMemSliceSpan span); QuicByteCount SaveMemSliceSpan(absl::Span<QuicMemSlice> span); // Called when |bytes_consumed| bytes has been consumed by the stream. diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer_test.cc index e1a2358acba..9aaec4d2c6a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer_test.cc @@ -13,7 +13,6 @@ #include "quic/platform/api/quic_expect_bug.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_test.h" -#include "quic/platform/api/quic_test_mem_slice_vector.h" #include "quic/test_tools/quic_stream_send_buffer_peer.h" #include "quic/test_tools/quic_test_utils.h" @@ -321,14 +320,13 @@ TEST_F(QuicStreamSendBufferTest, SaveMemSliceSpan) { SimpleBufferAllocator allocator; QuicStreamSendBuffer send_buffer(&allocator); - char data[1024]; - std::vector<std::pair<char*, size_t>> buffers; + std::string data(1024, 'a'); + std::vector<QuicMemSlice> buffers; for (size_t i = 0; i < 10; ++i) { - buffers.push_back(std::make_pair(data, 1024)); + buffers.push_back(MemSliceFromString(data)); } - QuicTestMemSliceVector vector(buffers); - EXPECT_EQ(10 * 1024u, send_buffer.SaveMemSliceSpan(vector.span())); + EXPECT_EQ(10 * 1024u, send_buffer.SaveMemSliceSpan(absl::MakeSpan(buffers))); EXPECT_EQ(10u, send_buffer.size()); } @@ -336,15 +334,13 @@ TEST_F(QuicStreamSendBufferTest, SaveEmptyMemSliceSpan) { SimpleBufferAllocator allocator; QuicStreamSendBuffer send_buffer(&allocator); - char data[1024]; - std::vector<std::pair<char*, size_t>> buffers; + std::string data(1024, 'a'); + std::vector<QuicMemSlice> buffers; for (size_t i = 0; i < 10; ++i) { - buffers.push_back(std::make_pair(data, 1024)); + buffers.push_back(MemSliceFromString(data)); } - buffers.push_back(std::make_pair(nullptr, 0)); - QuicTestMemSliceVector vector(buffers); - EXPECT_EQ(10 * 1024u, send_buffer.SaveMemSliceSpan(vector.span())); + EXPECT_EQ(10 * 1024u, send_buffer.SaveMemSliceSpan(absl::MakeSpan(buffers))); // Verify the empty slice does not get saved. EXPECT_EQ(10u, send_buffer.size()); } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.cc index 88a51bd5e08..1dbe5df9310 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.cc @@ -236,7 +236,8 @@ void QuicStreamSequencer::MarkConsumed(size_t num_bytes_consumed) { << "Invalid argument to MarkConsumed." << " expect to consume: " << num_bytes_consumed << ", but not enough bytes available. " << DebugString(); - stream_->Reset(QUIC_ERROR_PROCESSING_STREAM); + stream_->ResetWithError( + QuicResetStreamError::FromInternal(QUIC_ERROR_PROCESSING_STREAM)); return; } stream_->AddBytesConsumed(num_bytes_consumed); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.h b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.h index b7ea49dac05..fe96f253dad 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.h @@ -37,7 +37,7 @@ class QUIC_EXPORT_PRIVATE QuicStreamSequencer { virtual void AddBytesConsumed(QuicByteCount bytes) = 0; // Called when an error has occurred which should result in the stream // being reset. - virtual void Reset(QuicRstStreamErrorCode error) = 0; + virtual void ResetWithError(QuicResetStreamError error) = 0; // Called when an error has occurred which should result in the connection // being closed. virtual void OnUnrecoverableError(QuicErrorCode error, diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.cc index 871b832b168..78c55c7523d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.cc @@ -46,9 +46,7 @@ QuicStreamSequencerBuffer::QuicStreamSequencerBuffer(size_t max_capacity_bytes) current_blocks_count_(0u), total_bytes_read_(0), blocks_(nullptr) { - if (allocate_blocks_on_demand_) { - QUICHE_DCHECK_GE(max_blocks_count_, kInitialBlockCount); - } + QUICHE_DCHECK_GE(max_blocks_count_, kInitialBlockCount); Clear(); } @@ -58,9 +56,7 @@ QuicStreamSequencerBuffer::~QuicStreamSequencerBuffer() { void QuicStreamSequencerBuffer::Clear() { if (blocks_ != nullptr) { - size_t blocks_to_clear = - allocate_blocks_on_demand_ ? current_blocks_count_ : max_blocks_count_; - for (size_t i = 0; i < blocks_to_clear; ++i) { + for (size_t i = 0; i < current_blocks_count_; ++i) { if (blocks_[i] != nullptr) { RetireBlock(i); } @@ -129,9 +125,7 @@ QuicErrorCode QuicStreamSequencerBuffer::OnStreamData( *error_details = "Received data beyond available range."; return QUIC_INTERNAL_ERROR; } - if (allocate_blocks_on_demand_) { - QUIC_RELOADABLE_FLAG_COUNT( - quic_allocate_stream_sequencer_buffer_blocks_on_demand); + if (!delay_allocation_until_new_data_) { MaybeAddMoreBlocks(starting_offset + size); } @@ -148,6 +142,11 @@ QuicErrorCode QuicStreamSequencerBuffer::OnStreamData( *error_details = "Too many data intervals received for this stream."; return QUIC_TOO_MANY_STREAM_DATA_INTERVALS; } + if (delay_allocation_until_new_data_) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_delay_sequencer_buffer_allocation_until_new_data, 1, 2); + MaybeAddMoreBlocks(starting_offset + size); + } size_t bytes_copy = 0; if (!CopyStreamData(starting_offset, data, &bytes_copy, error_details)) { @@ -171,6 +170,11 @@ QuicErrorCode QuicStreamSequencerBuffer::OnStreamData( *error_details = "Too many data intervals received for this stream."; return QUIC_TOO_MANY_STREAM_DATA_INTERVALS; } + if (delay_allocation_until_new_data_) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_delay_sequencer_buffer_allocation_until_new_data, 2, 2); + MaybeAddMoreBlocks(starting_offset + size); + } for (const auto& interval : newly_received) { const QuicStreamOffset copy_offset = interval.min(); const QuicByteCount copy_length = interval.max() - interval.min(); @@ -202,8 +206,7 @@ bool QuicStreamSequencerBuffer::CopyStreamData(QuicStreamOffset offset, while (source_remaining > 0) { const size_t write_block_num = GetBlockIndex(offset); const size_t write_block_offset = GetInBlockOffset(offset); - size_t current_blocks_count = - allocate_blocks_on_demand_ ? current_blocks_count_ : max_blocks_count_; + size_t current_blocks_count = current_blocks_count_; QUICHE_DCHECK_GT(current_blocks_count, write_block_num); size_t block_capacity = GetBlockCapacity(write_block_num); @@ -215,15 +218,6 @@ bool QuicStreamSequencerBuffer::CopyStreamData(QuicStreamOffset offset, bytes_avail = total_bytes_read_ + max_buffer_capacity_bytes_ - offset; } - if (!allocate_blocks_on_demand_) { - if (blocks_ == nullptr) { - blocks_.reset(new BufferBlock*[max_blocks_count_]()); - for (size_t i = 0; i < max_blocks_count_; ++i) { - blocks_[i] = nullptr; - } - } - } - if (write_block_num >= current_blocks_count) { *error_details = absl::StrCat( "QuicStreamSequencerBuffer error: OnStreamData() exceed array bounds." diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h index 05d41b8149c..83ce01bd366 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h @@ -229,10 +229,6 @@ class QUIC_EXPORT_PRIVATE QuicStreamSequencerBuffer { // Number of bytes read out of buffer. QuicStreamOffset total_bytes_read_; - // Whether size of blocks_ grows on demand. - bool allocate_blocks_on_demand_ = GetQuicReloadableFlag( - quic_allocate_stream_sequencer_buffer_blocks_on_demand); - // An ordered, variable-length list of blocks, with the length limited // such that the number of blocks never exceeds max_blocks_count_. // Each list entry can hold up to kBlockSizeBytes bytes. @@ -243,6 +239,9 @@ class QUIC_EXPORT_PRIVATE QuicStreamSequencerBuffer { // Currently received data. QuicIntervalSet<QuicStreamOffset> bytes_received_; + + bool delay_allocation_until_new_data_ = GetQuicReloadableFlag( + quic_delay_sequencer_buffer_allocation_until_new_data); }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer_test.cc index dc21a097441..212c25f7366 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer_test.cc @@ -1091,8 +1091,6 @@ TEST_F(QuicStreamSequencerBufferRandomIOTest, RandomWriteAndConsumeInPlace) { } TEST_F(QuicStreamSequencerBufferTest, GrowBlockSizeOnDemand) { - SetQuicReloadableFlag(quic_allocate_stream_sequencer_buffer_blocks_on_demand, - true); max_capacity_bytes_ = 1024 * kBlockSizeBytes; std::string source_of_one_block(kBlockSizeBytes, 'a'); Initialize(); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_test.cc index b097bc5bc93..0a15e96d3c4 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_test.cc @@ -43,7 +43,7 @@ class MockStream : public QuicStreamSequencer::StreamInterface { QuicIetfTransportErrorCodes ietf_error, const std::string& details), (override)); - MOCK_METHOD(void, Reset, (QuicRstStreamErrorCode error), (override)); + MOCK_METHOD(void, ResetWithError, (QuicResetStreamError error), (override)); MOCK_METHOD(void, AddBytesConsumed, (QuicByteCount bytes), (override)); QuicStreamId id() const override { return 1; } @@ -566,7 +566,8 @@ TEST_F(QuicStreamSequencerTest, MarkConsumedError) { // Now, attempt to mark consumed more data than was readable and expect the // stream to be closed. - EXPECT_CALL(stream_, Reset(QUIC_ERROR_PROCESSING_STREAM)); + EXPECT_CALL(stream_, ResetWithError(QuicResetStreamError::FromInternal( + QUIC_ERROR_PROCESSING_STREAM))); EXPECT_QUIC_BUG(sequencer_->MarkConsumed(4), "Invalid argument to MarkConsumed." " expect to consume: 4, but not enough bytes available."); 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 e69b657177d..9d48b1fa295 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 @@ -26,7 +26,6 @@ #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_mem_slice_storage.h" #include "quic/platform/api/quic_test.h" -#include "quic/platform/api/quic_test_mem_slice_vector.h" #include "quic/test_tools/quic_config_peer.h" #include "quic/test_tools/quic_connection_peer.h" #include "quic/test_tools/quic_flow_controller_peer.h" @@ -59,16 +58,15 @@ class TestStream : public QuicStream { sequencer()->set_level_triggered(true); } - TestStream(PendingStream* pending, - QuicSession* session, - StreamType type, - bool is_static) - : QuicStream(pending, session, type, is_static) {} + TestStream(PendingStream* pending, QuicSession* session, bool is_static) + : QuicStream(pending, session, is_static) {} MOCK_METHOD(void, OnDataAvailable, (), (override)); MOCK_METHOD(void, OnCanWriteNewData, (), (override)); + MOCK_METHOD(void, OnWriteSideInDataRecvdState, (), (override)); + using QuicStream::CanWriteNewData; using QuicStream::CanWriteNewDataAfterData; using QuicStream::CloseWriteSide; @@ -88,11 +86,11 @@ class QuicStreamTest : public QuicTestWithParam<ParsedQuicVersion> { : zero_(QuicTime::Delta::Zero()), supported_versions_(AllSupportedVersions()) {} - void Initialize() { + void Initialize(Perspective perspective = Perspective::IS_SERVER) { ParsedQuicVersionVector version_vector; version_vector.push_back(GetParam()); connection_ = new StrictMock<MockQuicConnection>( - &helper_, &alarm_factory_, Perspective::IS_SERVER, version_vector); + &helper_, &alarm_factory_, perspective, version_vector); connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); session_ = std::make_unique<StrictMock<MockQuicSession>>(connection_); session_->Initialize(); @@ -133,12 +131,9 @@ class QuicStreamTest : public QuicTestWithParam<ParsedQuicVersion> { } QuicConsumedData CloseStreamOnWriteError( - QuicStreamId id, - QuicByteCount /*write_length*/, - QuicStreamOffset /*offset*/, - StreamSendingState /*state*/, - TransmissionType /*type*/, - absl::optional<EncryptionLevel> /*level*/) { + QuicStreamId id, QuicByteCount /*write_length*/, + QuicStreamOffset /*offset*/, StreamSendingState /*state*/, + TransmissionType /*type*/, absl::optional<EncryptionLevel> /*level*/) { session_->ResetStream(id, QUIC_STREAM_CANCELLED); return QuicConsumedData(1, false); } @@ -167,33 +162,59 @@ class QuicStreamTest : public QuicTestWithParam<ParsedQuicVersion> { QuicStreamId kTestStreamId = GetNthClientInitiatedBidirectionalStreamId(GetParam().transport_version, 1); + const QuicStreamId kTestPendingStreamId = + GetNthClientInitiatedUnidirectionalStreamId(GetParam().transport_version, + 1); }; -INSTANTIATE_TEST_SUITE_P(QuicStreamTests, - QuicStreamTest, +INSTANTIATE_TEST_SUITE_P(QuicStreamTests, QuicStreamTest, ::testing::ValuesIn(AllSupportedVersions()), ::testing::PrintToStringParamName()); -TEST_P(QuicStreamTest, PendingStreamStaticness) { +using PendingStreamTest = QuicStreamTest; + +INSTANTIATE_TEST_SUITE_P(PendingStreamTests, PendingStreamTest, + ::testing::ValuesIn(CurrentSupportedHttp3Versions()), + ::testing::PrintToStringParamName()); + +TEST_P(PendingStreamTest, PendingStreamStaticness) { Initialize(); - PendingStream pending(kTestStreamId + 2, session_.get()); - TestStream stream(&pending, session_.get(), StreamType::BIDIRECTIONAL, false); + PendingStream pending(kTestPendingStreamId, session_.get()); + TestStream stream(&pending, session_.get(), false); EXPECT_FALSE(stream.is_static()); - PendingStream pending2(kTestStreamId + 3, session_.get()); - TestStream stream2(&pending2, session_.get(), StreamType::BIDIRECTIONAL, - true); + PendingStream pending2(kTestPendingStreamId + 4, session_.get()); + TestStream stream2(&pending2, session_.get(), true); EXPECT_TRUE(stream2.is_static()); } -TEST_P(QuicStreamTest, PendingStreamTooMuchData) { +TEST_P(PendingStreamTest, PendingStreamType) { + Initialize(); + + PendingStream pending(kTestPendingStreamId, session_.get()); + TestStream stream(&pending, session_.get(), false); + EXPECT_EQ(stream.type(), READ_UNIDIRECTIONAL); +} + +TEST_P(PendingStreamTest, PendingStreamTypeOnClient) { + Initialize(Perspective::IS_CLIENT); + + QuicStreamId server_initiated_pending_stream_id = + GetNthServerInitiatedUnidirectionalStreamId(session_->transport_version(), + 1); + PendingStream pending(server_initiated_pending_stream_id, session_.get()); + TestStream stream(&pending, session_.get(), false); + EXPECT_EQ(stream.type(), READ_UNIDIRECTIONAL); +} + +TEST_P(PendingStreamTest, PendingStreamTooMuchData) { Initialize(); - PendingStream pending(kTestStreamId + 2, session_.get()); + PendingStream pending(kTestPendingStreamId, session_.get()); // Receive a stream frame that violates flow control: the byte offset is // higher than the receive window offset. - QuicStreamFrame frame(kTestStreamId + 2, false, + QuicStreamFrame frame(kTestPendingStreamId, false, kInitialSessionFlowControlWindowForTest + 1, "."); // Stream should not accept the frame, and the connection should be closed. @@ -202,29 +223,43 @@ TEST_P(QuicStreamTest, PendingStreamTooMuchData) { pending.OnStreamFrame(frame); } -TEST_P(QuicStreamTest, PendingStreamTooMuchDataInRstStream) { +TEST_P(PendingStreamTest, PendingStreamTooMuchDataInRstStream) { Initialize(); - PendingStream pending(kTestStreamId + 2, session_.get()); + PendingStream pending1(kTestPendingStreamId, session_.get()); // Receive a rst stream frame that violates flow control: the byte offset is // higher than the receive window offset. - QuicRstStreamFrame frame(kInvalidControlFrameId, kTestStreamId + 2, - QUIC_STREAM_CANCELLED, - kInitialSessionFlowControlWindowForTest + 1); + QuicRstStreamFrame frame1(kInvalidControlFrameId, kTestPendingStreamId, + QUIC_STREAM_CANCELLED, + kInitialSessionFlowControlWindowForTest + 1); // Pending stream should not accept the frame, and the connection should be // closed. EXPECT_CALL(*connection_, CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _)); - pending.OnRstStreamFrame(frame); + pending1.OnRstStreamFrame(frame1); + + QuicStreamId bidirection_stream_id = QuicUtils::GetFirstBidirectionalStreamId( + session_->transport_version(), Perspective::IS_CLIENT); + PendingStream pending2(bidirection_stream_id, session_.get()); + // Receive a rst stream frame that violates flow control: the byte offset is + // higher than the receive window offset. + QuicRstStreamFrame frame2(kInvalidControlFrameId, bidirection_stream_id, + QUIC_STREAM_CANCELLED, + kInitialSessionFlowControlWindowForTest + 1); + // Bidirectional Pending stream should not accept the frame, and the + // connection should be closed. + EXPECT_CALL(*connection_, + CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _)); + pending2.OnRstStreamFrame(frame2); } -TEST_P(QuicStreamTest, PendingStreamRstStream) { +TEST_P(PendingStreamTest, PendingStreamRstStream) { Initialize(); - PendingStream pending(kTestStreamId + 2, session_.get()); + PendingStream pending(kTestPendingStreamId, session_.get()); QuicStreamOffset final_byte_offset = 7; - QuicRstStreamFrame frame(kInvalidControlFrameId, kTestStreamId + 2, + QuicRstStreamFrame frame(kInvalidControlFrameId, kTestPendingStreamId, QUIC_STREAM_CANCELLED, final_byte_offset); // Pending stream should accept the frame and not close the connection. @@ -232,19 +267,47 @@ TEST_P(QuicStreamTest, PendingStreamRstStream) { pending.OnRstStreamFrame(frame); } -TEST_P(QuicStreamTest, FromPendingStream) { +TEST_P(PendingStreamTest, PendingStreamWindowUpdate) { + Initialize(); + + QuicStreamId bidirection_stream_id = QuicUtils::GetFirstBidirectionalStreamId( + session_->transport_version(), Perspective::IS_CLIENT); + PendingStream pending(bidirection_stream_id, session_.get()); + QuicWindowUpdateFrame frame(kInvalidControlFrameId, bidirection_stream_id, + kDefaultFlowControlSendWindow * 2); + pending.OnWindowUpdateFrame(frame); + TestStream stream(&pending, session_.get(), false); + + EXPECT_EQ(QuicStreamPeer::SendWindowSize(&stream), + kDefaultFlowControlSendWindow * 2); +} + +TEST_P(PendingStreamTest, PendingStreamStopSending) { Initialize(); - PendingStream pending(kTestStreamId + 2, session_.get()); + QuicStreamId bidirection_stream_id = QuicUtils::GetFirstBidirectionalStreamId( + session_->transport_version(), Perspective::IS_CLIENT); + PendingStream pending(bidirection_stream_id, session_.get()); + QuicResetStreamError error = + QuicResetStreamError::FromInternal(QUIC_STREAM_INTERNAL_ERROR); + pending.OnStopSending(error); + EXPECT_TRUE(pending.GetStopSendingErrorCode()); + auto actual_error = *pending.GetStopSendingErrorCode(); + EXPECT_EQ(actual_error, error); +} - QuicStreamFrame frame(kTestStreamId + 2, false, 2, "."); +TEST_P(PendingStreamTest, FromPendingStream) { + Initialize(); + + PendingStream pending(kTestPendingStreamId, session_.get()); + + QuicStreamFrame frame(kTestPendingStreamId, false, 2, "."); pending.OnStreamFrame(frame); pending.OnStreamFrame(frame); - QuicStreamFrame frame2(kTestStreamId + 2, true, 3, "."); + QuicStreamFrame frame2(kTestPendingStreamId, true, 3, "."); pending.OnStreamFrame(frame2); - TestStream stream(&pending, session_.get(), StreamType::READ_UNIDIRECTIONAL, - false); + TestStream stream(&pending, session_.get(), false); EXPECT_EQ(3, stream.num_frames_received()); EXPECT_EQ(3u, stream.stream_bytes_read()); EXPECT_EQ(1, stream.num_duplicate_frames_received()); @@ -254,19 +317,18 @@ TEST_P(QuicStreamTest, FromPendingStream) { session_->flow_controller()->highest_received_byte_offset()); } -TEST_P(QuicStreamTest, FromPendingStreamThenData) { +TEST_P(PendingStreamTest, FromPendingStreamThenData) { Initialize(); - PendingStream pending(kTestStreamId + 2, session_.get()); + PendingStream pending(kTestPendingStreamId, session_.get()); - QuicStreamFrame frame(kTestStreamId + 2, false, 2, "."); + QuicStreamFrame frame(kTestPendingStreamId, false, 2, "."); pending.OnStreamFrame(frame); - auto stream = new TestStream(&pending, session_.get(), - StreamType::READ_UNIDIRECTIONAL, false); + auto stream = new TestStream(&pending, session_.get(), false); session_->ActivateStream(absl::WrapUnique(stream)); - QuicStreamFrame frame2(kTestStreamId + 2, true, 3, "."); + QuicStreamFrame frame2(kTestPendingStreamId, true, 3, "."); stream->OnStreamFrame(frame2); EXPECT_EQ(2, stream->num_frames_received()); @@ -865,6 +927,7 @@ TEST_P(QuicStreamTest, StreamWaitsForAcks) { EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size()); // FIN is acked. + EXPECT_CALL(*stream_, OnWriteSideInDataRecvdState()); EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 0, true, QuicTime::Delta::Zero(), QuicTime::Zero(), &newly_acked_length)); @@ -908,6 +971,7 @@ TEST_P(QuicStreamTest, StreamDataGetAckedOutOfOrder) { // FIN is not acked yet. EXPECT_TRUE(stream_->IsWaitingForAcks()); EXPECT_TRUE(session_->HasUnackedStreamData()); + EXPECT_CALL(*stream_, OnWriteSideInDataRecvdState()); EXPECT_TRUE(stream_->OnStreamFrameAcked(27, 0, true, QuicTime::Delta::Zero(), QuicTime::Zero(), &newly_acked_length)); @@ -977,8 +1041,11 @@ TEST_P(QuicStreamTest, RstFrameReceivedStreamNotFinishSending) { QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(), QUIC_STREAM_CANCELLED, 9); - EXPECT_CALL(*session_, MaybeSendRstStreamFrame(stream_->id(), - QUIC_RST_ACKNOWLEDGEMENT, 9)); + EXPECT_CALL( + *session_, + MaybeSendRstStreamFrame( + stream_->id(), + QuicResetStreamError::FromInternal(QUIC_RST_ACKNOWLEDGEMENT), 9)); stream_->OnStreamReset(rst_frame); EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); // Stream stops waiting for acks as it does not finish sending and rst is @@ -1020,8 +1087,11 @@ TEST_P(QuicStreamTest, ConnectionClosed) { stream_->WriteOrBufferData(kData1, false, nullptr); EXPECT_TRUE(stream_->IsWaitingForAcks()); EXPECT_TRUE(session_->HasUnackedStreamData()); - EXPECT_CALL(*session_, MaybeSendRstStreamFrame(stream_->id(), - QUIC_RST_ACKNOWLEDGEMENT, 9)); + EXPECT_CALL( + *session_, + MaybeSendRstStreamFrame( + stream_->id(), + QuicResetStreamError::FromInternal(QUIC_RST_ACKNOWLEDGEMENT), 9)); QuicConnectionPeer::SetConnectionClose(connection_); stream_->OnConnectionClosed(QUIC_INTERNAL_ERROR, ConnectionCloseSource::FROM_SELF); @@ -1243,26 +1313,20 @@ TEST_P(QuicStreamTest, WriteMemSlices) { TEST_P(QuicStreamTest, WriteMemSlicesReachStreamLimit) { Initialize(); QuicStreamPeer::SetStreamBytesWritten(kMaxStreamLength - 5u, stream_); - char data[5]; std::vector<std::pair<char*, size_t>> buffers; - buffers.push_back(std::make_pair(data, ABSL_ARRAYSIZE(data))); - QuicTestMemSliceVector vector1(buffers); - QuicMemSliceSpan span1 = vector1.span(); + QuicMemSlice slice1 = MemSliceFromString("12345"); EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillOnce(InvokeWithoutArgs([this]() { return session_->ConsumeData(stream_->id(), 5u, 0u, NO_FIN, NOT_RETRANSMISSION, absl::nullopt); })); // There is no buffered data before, all data should be consumed. - QuicConsumedData consumed = stream_->WriteMemSlices(span1, false); + QuicConsumedData consumed = stream_->WriteMemSlice(std::move(slice1), false); EXPECT_EQ(5u, consumed.bytes_consumed); - std::vector<std::pair<char*, size_t>> buffers2; - buffers2.push_back(std::make_pair(data, 1u)); - QuicTestMemSliceVector vector2(buffers); - QuicMemSliceSpan span2 = vector2.span(); + QuicMemSlice slice2 = MemSliceFromString("6"); EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _)); - EXPECT_QUIC_BUG(stream_->WriteMemSlices(span2, false), + EXPECT_QUIC_BUG(stream_->WriteMemSlice(std::move(slice2), false), "Write too many data via stream"); } @@ -1313,6 +1377,7 @@ TEST_P(QuicStreamTest, StreamDataGetAckedMultipleTimes) { EXPECT_TRUE(session_->HasUnackedStreamData()); // Ack Fin. + EXPECT_CALL(*stream_, OnWriteSideInDataRecvdState()).Times(1); EXPECT_TRUE(stream_->OnStreamFrameAcked(27, 0, true, QuicTime::Delta::Zero(), QuicTime::Zero(), &newly_acked_length)); @@ -1549,10 +1614,14 @@ TEST_P(QuicStreamTest, ResetStreamOnTtlExpiresRetransmitLostData) { // Verify stream gets reset because TTL expires. if (session_->version().UsesHttp3()) { EXPECT_CALL(*session_, - MaybeSendStopSendingFrame(_, QUIC_STREAM_TTL_EXPIRED)) + MaybeSendStopSendingFrame(_, QuicResetStreamError::FromInternal( + QUIC_STREAM_TTL_EXPIRED))) .Times(1); } - EXPECT_CALL(*session_, MaybeSendRstStreamFrame(_, QUIC_STREAM_TTL_EXPIRED, _)) + EXPECT_CALL( + *session_, + MaybeSendRstStreamFrame( + _, QuicResetStreamError::FromInternal(QUIC_STREAM_TTL_EXPIRED), _)) .Times(1); stream_->OnCanWrite(); } @@ -1573,10 +1642,14 @@ TEST_P(QuicStreamTest, ResetStreamOnTtlExpiresEarlyRetransmitData) { // Verify stream gets reset because TTL expires. if (session_->version().UsesHttp3()) { EXPECT_CALL(*session_, - MaybeSendStopSendingFrame(_, QUIC_STREAM_TTL_EXPIRED)) + MaybeSendStopSendingFrame(_, QuicResetStreamError::FromInternal( + QUIC_STREAM_TTL_EXPIRED))) .Times(1); } - EXPECT_CALL(*session_, MaybeSendRstStreamFrame(_, QUIC_STREAM_TTL_EXPIRED, _)) + EXPECT_CALL( + *session_, + MaybeSendRstStreamFrame( + _, QuicResetStreamError::FromInternal(QUIC_STREAM_TTL_EXPIRED), _)) .Times(1); stream_->RetransmitStreamData(0, 100, false, PTO_RETRANSMISSION); } @@ -1647,6 +1720,15 @@ TEST_P(QuicStreamTest, EmptyStreamFrameWithNoFin) { stream_->OnStreamFrame(empty_stream_frame); } +TEST_P(QuicStreamTest, SendRstWithCustomIetfCode) { + Initialize(); + QuicResetStreamError error(QUIC_STREAM_CANCELLED, 0x1234abcd); + EXPECT_CALL(*session_, MaybeSendRstStreamFrame(kTestStreamId, error, _)) + .Times(1); + stream_->ResetWithError(error); + EXPECT_TRUE(rst_sent()); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time.cc b/chromium/net/third_party/quiche/src/quic/core/quic_time.cc index daf8e299dcd..08e0906c7cb 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_time.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_time.cc @@ -21,11 +21,11 @@ std::string QuicTime::Delta::ToDebuggingValue() const { // For debugging purposes, always display the value with the highest precision // available. - if (absolute_value > kSecondInMicroseconds && + if (absolute_value >= kSecondInMicroseconds && absolute_value % kSecondInMicroseconds == 0) { return absl::StrCat(time_offset_ / kSecondInMicroseconds, "s"); } - if (absolute_value > kMillisecondInMicroseconds && + if (absolute_value >= kMillisecondInMicroseconds && absolute_value % kMillisecondInMicroseconds == 0) { return absl::StrCat(time_offset_ / kMillisecondInMicroseconds, "ms"); } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_time_test.cc index eaaf75568d8..e5bb2857d0a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_time_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_test.cc @@ -87,8 +87,11 @@ TEST_F(QuicTimeDeltaTest, DebuggingValue) { const QuicTime::Delta one_ms = QuicTime::Delta::FromMilliseconds(1); const QuicTime::Delta one_s = QuicTime::Delta::FromSeconds(1); + EXPECT_EQ("1s", one_s.ToDebuggingValue()); EXPECT_EQ("3s", (3 * one_s).ToDebuggingValue()); + EXPECT_EQ("1ms", one_ms.ToDebuggingValue()); EXPECT_EQ("3ms", (3 * one_ms).ToDebuggingValue()); + EXPECT_EQ("1us", one_us.ToDebuggingValue()); EXPECT_EQ("3us", (3 * one_us).ToDebuggingValue()); EXPECT_EQ("3001us", (3 * one_ms + one_us).ToDebuggingValue()); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc index 12f5ef9aae2..c92b6368272 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc @@ -30,7 +30,7 @@ namespace quic { // A very simple alarm that just informs the QuicTimeWaitListManager to clean // up old connection_ids. This alarm should be cancelled and deleted before // the QuicTimeWaitListManager is deleted. -class ConnectionIdCleanUpAlarm : public QuicAlarm::Delegate { +class ConnectionIdCleanUpAlarm : public QuicAlarm::DelegateWithoutContext { public: explicit ConnectionIdCleanUpAlarm( QuicTimeWaitListManager* time_wait_list_manager) @@ -91,9 +91,6 @@ QuicTimeWaitListManager::~QuicTimeWaitListManager() { QuicTimeWaitListManager::ConnectionIdMap::iterator QuicTimeWaitListManager::FindConnectionIdDataInMap( const QuicConnectionId& connection_id) { - if (!use_indirect_connection_id_map_) { - return connection_id_map_.find(connection_id); - } auto it = indirect_connection_id_map_.find(connection_id); if (it == indirect_connection_id_map_.end()) { return connection_id_map_.end(); @@ -106,12 +103,8 @@ void QuicTimeWaitListManager::AddConnectionIdDataToMap( int num_packets, TimeWaitAction action, TimeWaitConnectionInfo info) { - if (use_indirect_connection_id_map_) { - QUIC_RESTART_FLAG_COUNT_N(quic_time_wait_list_support_multiple_cid_v2, 1, - 3); - for (const auto& cid : info.active_connection_ids) { - indirect_connection_id_map_[cid] = canonical_connection_id; - } + for (const auto& cid : info.active_connection_ids) { + indirect_connection_id_map_[cid] = canonical_connection_id; } ConnectionIdData data(num_packets, clock_->ApproximateNow(), action, std::move(info)); @@ -121,24 +114,18 @@ void QuicTimeWaitListManager::AddConnectionIdDataToMap( void QuicTimeWaitListManager::RemoveConnectionDataFromMap( ConnectionIdMap::iterator it) { - if (use_indirect_connection_id_map_) { - QUIC_RESTART_FLAG_COUNT_N(quic_time_wait_list_support_multiple_cid_v2, 2, - 3); - for (const auto& cid : it->second.info.active_connection_ids) { - indirect_connection_id_map_.erase(cid); - } + for (const auto& cid : it->second.info.active_connection_ids) { + indirect_connection_id_map_.erase(cid); } connection_id_map_.erase(it); } void QuicTimeWaitListManager::AddConnectionIdToTimeWait( - QuicConnectionId connection_id, TimeWaitAction action, TimeWaitConnectionInfo info) { QUICHE_DCHECK(!info.active_connection_ids.empty()); const QuicConnectionId& canonical_connection_id = - use_indirect_connection_id_map_ ? info.active_connection_ids.front() - : connection_id; + info.active_connection_ids.front(); QUICHE_DCHECK(action != SEND_TERMINATION_PACKETS || !info.termination_packets.empty()); QUICHE_DCHECK(action != DO_NOTHING || info.ietf_quic); @@ -154,26 +141,18 @@ void QuicTimeWaitListManager::AddConnectionIdToTimeWait( GetQuicFlag(FLAGS_quic_time_wait_list_max_connections); QUICHE_DCHECK(connection_id_map_.empty() || num_connections() < static_cast<size_t>(max_connections)); - if (use_indirect_connection_id_map_ && new_connection_id) { - QUIC_RESTART_FLAG_COUNT_N(quic_time_wait_list_support_multiple_cid_v2, 3, - 3); + if (new_connection_id) { for (const auto& cid : info.active_connection_ids) { visitor_->OnConnectionAddedToTimeWaitList(cid); } } AddConnectionIdDataToMap(canonical_connection_id, num_packets, action, std::move(info)); - if (!use_indirect_connection_id_map_ && new_connection_id) { - visitor_->OnConnectionAddedToTimeWaitList(canonical_connection_id); - } } bool QuicTimeWaitListManager::IsConnectionIdInTimeWait( QuicConnectionId connection_id) const { - if (use_indirect_connection_id_map_) { - return indirect_connection_id_map_.contains(connection_id); - } - return connection_id_map_.contains(connection_id); + return indirect_connection_id_map_.contains(connection_id); } void QuicTimeWaitListManager::OnBlockedWriterCanWrite() { @@ -324,8 +303,7 @@ void QuicTimeWaitListManager::SendPublicReset( if (ietf_quic) { std::unique_ptr<QuicEncryptedPacket> ietf_reset_packet = BuildIetfStatelessResetPacket(connection_id, received_packet_length); - if (GetQuicRestartFlag(quic_fix_stateless_reset2) && - ietf_reset_packet == nullptr) { + if (ietf_reset_packet == nullptr) { // This could happen when trying to reject a short header packet of // a connection which is in the time wait list (and with no termination // packet). @@ -392,6 +370,13 @@ bool QuicTimeWaitListManager::SendOrQueuePacket( QUIC_LOG(ERROR) << "Tried to send or queue a null packet"; return true; } + if (GetQuicReloadableFlag(quic_add_upperbound_for_queued_packets) && + pending_packets_queue_.size() >= + GetQuicFlag(FLAGS_quic_time_wait_list_max_pending_packets)) { + // There are too many pending packets. + QUIC_CODE_COUNT(quic_too_many_pending_packets_in_time_wait); + return true; + } if (WriteToWire(packet.get())) { // Allow the packet to be deleted upon leaving this function. return true; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h index fe15159d98c..a0844840876 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h @@ -99,14 +99,13 @@ class QUIC_NO_EXPORT QuicTimeWaitListManager QuicTimeWaitListManager& operator=(const QuicTimeWaitListManager&) = delete; ~QuicTimeWaitListManager() override; - // Adds the given connection_id to time wait state for time_wait_period_. - // If |termination_packets| are provided, copies of these packets will be sent - // when a packet with this connection ID is processed. Any termination packets - // will be move from |termination_packets| and will become owned by the - // manager. |action| specifies what the time wait list manager should do when - // processing packets of the connection. - virtual void AddConnectionIdToTimeWait(QuicConnectionId connection_id, - TimeWaitAction action, + // Adds the connection IDs in info to time wait state for time_wait_period_. + // If |info|.termination_packets are provided, copies of these packets will be + // sent when a packet with one of these connection IDs is processed. Any + // termination packets will be move from |info|.termination_packets and will + // become owned by the manager. |action| specifies what the time wait list + // manager should do when processing packets of the connection. + virtual void AddConnectionIdToTimeWait(TimeWaitAction action, TimeWaitConnectionInfo info); // Returns true if the connection_id is in time wait state, false otherwise. @@ -337,11 +336,6 @@ class QUIC_NO_EXPORT QuicTimeWaitListManager // Interface that manages blocked writers. Visitor* visitor_; - - // When this is default true, remove the connection_id argument of - // AddConnectionIdToTimeWait. - bool use_indirect_connection_id_map_ = - GetQuicRestartFlag(quic_time_wait_list_support_multiple_cid_v2); }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc index f47bdf2fed8..36105358e99 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc @@ -157,7 +157,7 @@ class QuicTimeWaitListManagerTest : public QuicTest { termination_packets.push_back(std::unique_ptr<QuicEncryptedPacket>( new QuicEncryptedPacket(nullptr, 0, false))); time_wait_list_manager_.AddConnectionIdToTimeWait( - connection_id, QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, + QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, TimeWaitConnectionInfo(false, &termination_packets, {connection_id})); } @@ -167,9 +167,8 @@ class QuicTimeWaitListManagerTest : public QuicTest { QuicTimeWaitListManager::TimeWaitAction action, std::vector<std::unique_ptr<QuicEncryptedPacket>>* packets) { time_wait_list_manager_.AddConnectionIdToTimeWait( - connection_id, action, - TimeWaitConnectionInfo(version.HasIetfInvariantHeader(), packets, - {connection_id})); + action, TimeWaitConnectionInfo(version.HasIetfInvariantHeader(), + packets, {connection_id})); } bool IsConnectionIdInTimeWait(QuicConnectionId connection_id) { @@ -450,10 +449,6 @@ TEST_F(QuicTimeWaitListManagerTest, CleanUpOldConnectionIds) { TEST_F(QuicTimeWaitListManagerTest, CleanUpOldConnectionIdsForMultipleConnectionIdsPerConnection) { - if (!GetQuicRestartFlag(quic_time_wait_list_support_multiple_cid_v2)) { - return; - } - connection_id_ = TestConnectionId(7); const size_t kConnectionCloseLength = 100; EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_)); @@ -467,7 +462,7 @@ TEST_F(QuicTimeWaitListManagerTest, std::vector<QuicConnectionId> active_connection_ids{connection_id_, TestConnectionId(8)}; time_wait_list_manager_.AddConnectionIdToTimeWait( - connection_id_, QuicTimeWaitListManager::SEND_CONNECTION_CLOSE_PACKETS, + QuicTimeWaitListManager::SEND_CONNECTION_CLOSE_PACKETS, TimeWaitConnectionInfo(/*ietf_quic=*/true, &termination_packets, active_connection_ids, QuicTime::Delta::Zero())); @@ -675,7 +670,7 @@ TEST_F(QuicTimeWaitListManagerTest, std::unique_ptr<QuicEncryptedPacket>(new QuicEncryptedPacket( new char[kConnectionCloseLength], kConnectionCloseLength, true))); time_wait_list_manager_.AddConnectionIdToTimeWait( - connection_id_, QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, + QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, TimeWaitConnectionInfo(/*ietf_quic=*/true, &termination_packets, {connection_id_})); @@ -701,7 +696,7 @@ TEST_F(QuicTimeWaitListManagerTest, new char[kConnectionCloseLength], kConnectionCloseLength, true))); // Add a CONNECTION_CLOSE termination packet. time_wait_list_manager_.AddConnectionIdToTimeWait( - connection_id_, QuicTimeWaitListManager::SEND_CONNECTION_CLOSE_PACKETS, + QuicTimeWaitListManager::SEND_CONNECTION_CLOSE_PACKETS, TimeWaitConnectionInfo(/*ietf_quic=*/true, &termination_packets, {connection_id_})); EXPECT_CALL(writer_, WritePacket(_, kConnectionCloseLength, @@ -717,10 +712,6 @@ TEST_F(QuicTimeWaitListManagerTest, TEST_F(QuicTimeWaitListManagerTest, SendConnectionClosePacketsForMultipleConnectionIds) { - if (!GetQuicRestartFlag(quic_time_wait_list_support_multiple_cid_v2)) { - return; - } - connection_id_ = TestConnectionId(7); const size_t kConnectionCloseLength = 100; EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_)); @@ -734,7 +725,7 @@ TEST_F(QuicTimeWaitListManagerTest, std::vector<QuicConnectionId> active_connection_ids{connection_id_, TestConnectionId(8)}; time_wait_list_manager_.AddConnectionIdToTimeWait( - connection_id_, QuicTimeWaitListManager::SEND_CONNECTION_CLOSE_PACKETS, + QuicTimeWaitListManager::SEND_CONNECTION_CLOSE_PACKETS, TimeWaitConnectionInfo(/*ietf_quic=*/true, &termination_packets, active_connection_ids, QuicTime::Delta::Zero())); @@ -768,6 +759,37 @@ TEST_F(QuicTimeWaitListManagerTest, SendOrQueueNullPacket) { nullptr, nullptr); } +TEST_F(QuicTimeWaitListManagerTest, TooManyPendingPackets) { + SetQuicFlag(FLAGS_quic_time_wait_list_max_pending_packets, 5); + const size_t kNumOfUnProcessablePackets = 2048; + EXPECT_CALL(visitor_, OnWriteBlocked(&time_wait_list_manager_)) + .Times(testing::AnyNumber()); + // Write block for the next packets. + EXPECT_CALL(writer_, + WritePacket(_, _, self_address_.host(), peer_address_, _)) + .With(Args<0, 1>(PublicResetPacketEq(TestConnectionId(1)))) + .WillOnce(DoAll(Assign(&writer_is_blocked_, true), + Return(WriteResult(WRITE_STATUS_BLOCKED, EAGAIN)))); + for (size_t i = 0; i < kNumOfUnProcessablePackets; ++i) { + time_wait_list_manager_.SendPublicReset( + self_address_, peer_address_, TestConnectionId(1), + /*ietf_quic=*/true, + /*received_packet_length=*/ + QuicFramer::GetMinStatelessResetPacketLength() + 1, + /*packet_context=*/nullptr); + } + if (GetQuicReloadableFlag(quic_add_upperbound_for_queued_packets)) { + // Verify pending packet queue size is limited. + EXPECT_EQ(5u, QuicTimeWaitListManagerPeer::PendingPacketsQueueSize( + &time_wait_list_manager_)); + } else { + // The pending packet queue grows unbounded. + EXPECT_EQ(kNumOfUnProcessablePackets, + QuicTimeWaitListManagerPeer::PendingPacketsQueueSize( + &time_wait_list_manager_)); + } +} + } // namespace } // namespace test } // namespace quic 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 eb4db074ef4..edfa78b5e76 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 @@ -56,6 +56,10 @@ using StatelessResetToken = std::array<char, kStatelessResetTokenLength>; // WebTransport session IDs are stream IDs. using WebTransportSessionId = uint64_t; +// WebTransport stream reset codes are 8-bit. +using WebTransportStreamError = uint8_t; +// WebTransport session error codes are 32-bit. +using WebTransportSessionError = uint32_t; enum : size_t { kQuicPathFrameBufferSize = 8 }; using QuicPathFrameBuffer = std::array<uint8_t, kQuicPathFrameBufferSize>; @@ -79,8 +83,7 @@ struct QUIC_EXPORT_PRIVATE QuicConsumedData { // default gtest object printer to read uninitialize memory. So we need // to teach gtest how to print this object. QUIC_EXPORT_PRIVATE friend std::ostream& operator<<( - std::ostream& os, - const QuicConsumedData& s); + std::ostream& os, const QuicConsumedData& s); // How many bytes were consumed. size_t bytes_consumed; @@ -198,8 +201,7 @@ QUIC_EXPORT_PRIVATE std::string TransmissionTypeToString( TransmissionType transmission_type); QUIC_EXPORT_PRIVATE std::ostream& operator<<( - std::ostream& os, - TransmissionType transmission_type); + std::ostream& os, TransmissionType transmission_type); enum HasRetransmittableData : uint8_t { NO_RETRANSMITTABLE_DATA, @@ -220,8 +222,7 @@ enum class ConnectionCloseSource { FROM_PEER, FROM_SELF }; QUIC_EXPORT_PRIVATE std::string ConnectionCloseSourceToString( ConnectionCloseSource connection_close_source); QUIC_EXPORT_PRIVATE std::ostream& operator<<( - std::ostream& os, - const ConnectionCloseSource& connection_close_source); + std::ostream& os, const ConnectionCloseSource& connection_close_source); // Should a connection be closed silently or not. enum class ConnectionCloseBehavior { @@ -233,8 +234,7 @@ enum class ConnectionCloseBehavior { QUIC_EXPORT_PRIVATE std::string ConnectionCloseBehaviorToString( ConnectionCloseBehavior connection_close_behavior); QUIC_EXPORT_PRIVATE std::ostream& operator<<( - std::ostream& os, - const ConnectionCloseBehavior& connection_close_behavior); + std::ostream& os, const ConnectionCloseBehavior& connection_close_behavior); enum QuicFrameType : uint8_t { // Regular frame types. The values set here cannot change without the @@ -338,7 +338,12 @@ enum QuicIetfFrameType : uint64_t { IETF_EXTENSION_MESSAGE_V99 = 0x31, // An QUIC extension frame for sender control of acknowledgement delays - IETF_ACK_FREQUENCY = 0xaf + IETF_ACK_FREQUENCY = 0xaf, + + // A QUIC extension frame which augments the IETF_ACK frame definition with + // packet receive timestamps. + // TODO(ianswett): Determine a proper value to replace this temporary value. + IETF_ACK_RECEIVE_TIMESTAMPS = 0x22, }; QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, const QuicIetfFrameType& c); @@ -574,8 +579,7 @@ struct QUIC_EXPORT_PRIVATE AckedPacket { receive_timestamp(receive_timestamp) {} friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( - std::ostream& os, - const AckedPacket& acked_packet); + std::ostream& os, const AckedPacket& acked_packet); QuicPacketNumber packet_number; // Number of bytes sent in the packet that was acknowledged. @@ -595,8 +599,7 @@ struct QUIC_EXPORT_PRIVATE LostPacket { : packet_number(packet_number), bytes_lost(bytes_lost) {} friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( - std::ostream& os, - const LostPacket& lost_packet); + std::ostream& os, const LostPacket& lost_packet); QuicPacketNumber packet_number; // Number of bytes sent in the packet that was lost. @@ -681,7 +684,7 @@ enum WriteStreamDataResult { WRITE_FAILED, // Trying to write nonexistent data of a stream }; -enum StreamType { +enum StreamType : uint8_t { // Bidirectional streams allow for data to be sent in both directions. BIDIRECTIONAL, @@ -744,8 +747,7 @@ enum QuicConnectionCloseType { }; QUIC_EXPORT_PRIVATE std::ostream& operator<<( - std::ostream& os, - const QuicConnectionCloseType type); + std::ostream& os, const QuicConnectionCloseType type); QUIC_EXPORT_PRIVATE std::string QuicConnectionCloseTypeString( QuicConnectionCloseType type); @@ -838,6 +840,9 @@ QUIC_EXPORT_PRIVATE std::string KeyUpdateReasonString(KeyUpdateReason reason); struct QUIC_NO_EXPORT QuicSSLConfig { // Whether TLS early data should be enabled. If not set, default to enabled. absl::optional<bool> early_data_enabled; + // Whether TLS session tickets are supported. If not set, default to + // supported. + absl::optional<bool> disable_ticket_support; // If set, used to configure the SSL object with // SSL_set_signing_algorithm_prefs. absl::optional<absl::InlinedVector<uint16_t, 8>> signing_algorithm_prefs; 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 9f9919c1be9..dada106a6a4 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 @@ -159,7 +159,13 @@ void QuicUnackedPacketMap::AddSentPacket(SerializedPacket* mutable_packet, largest_sent_largest_acked_.UpdateMax(packet.largest_acked); if (!measure_rtt) { - QUIC_BUG_IF(quic_bug_12645_2, set_in_flight); + QUIC_BUG_IF(quic_bug_12645_2, set_in_flight) + << "Packet " << mutable_packet->packet_number << ", transmission type " + << TransmissionTypeToString(mutable_packet->transmission_type) + << ", retransmittable frames: " + << QuicFramesToString(mutable_packet->retransmittable_frames) + << ", nonretransmittable_frames: " + << QuicFramesToString(mutable_packet->nonretransmittable_frames); info.state = NOT_CONTRIBUTING_RTT; } 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 911370f2618..58c0c51d55a 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,17 +15,27 @@ namespace quic { QuicVersionManager::QuicVersionManager( ParsedQuicVersionVector supported_versions) - : disable_version_rfcv1_(GetQuicReloadableFlag(quic_disable_version_rfcv1)), + : lazy_(GetQuicRestartFlag(quic_lazy_quic_version_manager)), + disable_version_rfcv1_( + lazy_ ? true : GetQuicReloadableFlag(quic_disable_version_rfcv1)), disable_version_draft_29_( - GetQuicReloadableFlag(quic_disable_version_draft_29)), - disable_version_t051_(GetQuicReloadableFlag(quic_disable_version_t051)), - disable_version_q050_(GetQuicReloadableFlag(quic_disable_version_q050)), - disable_version_q046_(GetQuicReloadableFlag(quic_disable_version_q046)), - disable_version_q043_(GetQuicReloadableFlag(quic_disable_version_q043)), + lazy_ ? true : GetQuicReloadableFlag(quic_disable_version_draft_29)), + disable_version_t051_( + lazy_ ? true : GetQuicReloadableFlag(quic_disable_version_t051)), + disable_version_q050_( + lazy_ ? true : GetQuicReloadableFlag(quic_disable_version_q050)), + disable_version_q046_( + lazy_ ? true : GetQuicReloadableFlag(quic_disable_version_q046)), + disable_version_q043_( + lazy_ ? true : GetQuicReloadableFlag(quic_disable_version_q043)), allowed_supported_versions_(std::move(supported_versions)) { static_assert(SupportedVersions().size() == 6u, "Supported versions out of sync"); - RefilterSupportedVersions(); + if (lazy_) { + QUIC_RESTART_FLAG_COUNT(quic_lazy_quic_version_manager); + } else { + RefilterSupportedVersions(); + } } QuicVersionManager::~QuicVersionManager() {} @@ -36,6 +46,12 @@ const ParsedQuicVersionVector& QuicVersionManager::GetSupportedVersions() { } const ParsedQuicVersionVector& +QuicVersionManager::GetSupportedVersionsWithOnlyHttp3() { + MaybeRefilterSupportedVersions(); + return filtered_supported_versions_with_http3_; +} + +const ParsedQuicVersionVector& QuicVersionManager::GetSupportedVersionsWithQuicCrypto() { MaybeRefilterSupportedVersions(); return filtered_supported_versions_with_quic_crypto_; @@ -76,6 +92,7 @@ void QuicVersionManager::MaybeRefilterSupportedVersions() { void QuicVersionManager::RefilterSupportedVersions() { filtered_supported_versions_ = FilterSupportedVersions(allowed_supported_versions_); + filtered_supported_versions_with_http3_.clear(); filtered_supported_versions_with_quic_crypto_.clear(); filtered_transport_versions_.clear(); filtered_supported_alpns_.clear(); @@ -86,6 +103,9 @@ void QuicVersionManager::RefilterSupportedVersions() { transport_version) == filtered_transport_versions_.end()) { filtered_transport_versions_.push_back(transport_version); } + if (version.UsesHttp3()) { + filtered_supported_versions_with_http3_.push_back(version); + } if (version.handshake_protocol == PROTOCOL_QUIC_CRYPTO) { filtered_supported_versions_with_quic_crypto_.push_back(version); } 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 da73f37b910..501ecc5c373 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 @@ -22,6 +22,9 @@ class QUIC_EXPORT_PRIVATE QuicVersionManager { // as the versions passed to the constructor. const ParsedQuicVersionVector& GetSupportedVersions(); + // Returns currently supported versions using HTTP/3. + const ParsedQuicVersionVector& GetSupportedVersionsWithOnlyHttp3(); + // Returns currently supported versions using QUIC crypto. const ParsedQuicVersionVector& GetSupportedVersionsWithQuicCrypto(); @@ -33,22 +36,31 @@ class QUIC_EXPORT_PRIVATE QuicVersionManager { // If the value of any reloadable flag is different from the cached value, // re-filter |filtered_supported_versions_| and update the cached flag values. // Otherwise, does nothing. + // TODO(dschinazi): Make private when deprecating + // FLAGS_gfe2_restart_flag_quic_disable_old_alt_svc_format. void MaybeRefilterSupportedVersions(); // Refilters filtered_supported_versions_. virtual void RefilterSupportedVersions(); + // RefilterSupportedVersions() must be called before calling this method. + // TODO(dschinazi): Remove when deprecating + // FLAGS_gfe2_restart_flag_quic_disable_old_alt_svc_format. const QuicTransportVersionVector& filtered_transport_versions() const { return filtered_transport_versions_; } - // Mechanism for subclasses to add custom ALPNs to the supported list. - // Should be called in constructor and RefilterSupportedVersions. + // Subclasses may add custom ALPNs to the supported list by overriding + // RefilterSupportedVersions() to first call + // QuicVersionManager::RefilterSupportedVersions() then AddCustomAlpn(). + // Must not be called elsewhere. void AddCustomAlpn(const std::string& alpn); - bool disable_version_q050() const { return disable_version_q050_; } - private: + // Cached value of gfe2_restart_flag_quic_lazy_quic_version_manager for + // brevity. + const bool lazy_; + // Cached value of reloadable flags. // quic_disable_version_rfcv1 flag bool disable_version_rfcv1_; @@ -64,10 +76,18 @@ class QUIC_EXPORT_PRIVATE QuicVersionManager { bool disable_version_q043_; // The list of versions that may be supported. - ParsedQuicVersionVector allowed_supported_versions_; + const ParsedQuicVersionVector allowed_supported_versions_; + + // The following vectors are calculated from reloadable flags by + // RefilterSupportedVersions(). It is performed lazily when first needed, and + // after that, since the calculation is relatively expensive, only if the flag + // values change. + // This vector contains QUIC versions which are currently supported based on // flags. ParsedQuicVersionVector filtered_supported_versions_; + // Currently supported versions using HTTP/3. + ParsedQuicVersionVector filtered_supported_versions_with_http3_; // Currently supported versions using QUIC crypto. ParsedQuicVersionVector filtered_supported_versions_with_quic_crypto_; // This vector contains the transport versions from 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 5776b3916c3..75ceed46b8f 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 @@ -37,6 +37,7 @@ TEST_F(QuicVersionManagerTest, QuicVersionManager) { EXPECT_EQ(FilterSupportedVersions(AllSupportedVersions()), manager.GetSupportedVersions()); + EXPECT_TRUE(manager.GetSupportedVersionsWithOnlyHttp3().empty()); EXPECT_EQ(CurrentSupportedVersionsWithQuicCrypto(), manager.GetSupportedVersionsWithQuicCrypto()); EXPECT_THAT(manager.GetSupportedAlpns(), @@ -51,6 +52,9 @@ TEST_F(QuicVersionManagerTest, QuicVersionManager) { manager.GetSupportedVersionsWithQuicCrypto().size()); EXPECT_EQ(FilterSupportedVersions(AllSupportedVersions()), manager.GetSupportedVersions()); + EXPECT_EQ(1u, manager.GetSupportedVersionsWithOnlyHttp3().size()); + EXPECT_EQ(CurrentSupportedHttp3Versions(), + manager.GetSupportedVersionsWithOnlyHttp3()); EXPECT_EQ(CurrentSupportedVersionsWithQuicCrypto(), manager.GetSupportedVersionsWithQuicCrypto()); EXPECT_THAT(manager.GetSupportedAlpns(), @@ -65,6 +69,9 @@ TEST_F(QuicVersionManagerTest, QuicVersionManager) { manager.GetSupportedVersionsWithQuicCrypto().size()); EXPECT_EQ(FilterSupportedVersions(AllSupportedVersions()), manager.GetSupportedVersions()); + EXPECT_EQ(1u, manager.GetSupportedVersionsWithOnlyHttp3().size()); + EXPECT_EQ(CurrentSupportedHttp3Versions(), + manager.GetSupportedVersionsWithOnlyHttp3()); EXPECT_EQ(CurrentSupportedVersionsWithQuicCrypto(), manager.GetSupportedVersionsWithQuicCrypto()); EXPECT_THAT(manager.GetSupportedAlpns(), 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 c6d04552f43..4b1cf31a8e0 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 @@ -177,7 +177,7 @@ class QUIC_NO_EXPORT TlsChloExtractor void OnDataAvailable() override; void OnFinRead() override {} void AddBytesConsumed(QuicByteCount /*bytes*/) override {} - void Reset(QuicRstStreamErrorCode /*error*/) override {} + void ResetWithError(QuicResetStreamError /*error*/) override {} void OnUnrecoverableError(QuicErrorCode error, const std::string& details) override; void OnUnrecoverableError(QuicErrorCode error, 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 737f449c59f..50ba8148342 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 @@ -74,13 +74,14 @@ bool TlsClientHandshaker::CryptoConnect() { // TODO(b/193650832) Add SetFromConfig to QUIC handshakers and remove reliance // on session pointer. - if (session()->permutes_tls_extensions()) { - // Ask BoringSSL to randomize the order of TLS extensions. + const bool permutes_tls_extensions = session()->permutes_tls_extensions(); + if (!permutes_tls_extensions) { + QUIC_DLOG(INFO) << "Disabling TLS extension permutation"; + } #if BORINGSSL_API_VERSION >= 16 - QUIC_DLOG(INFO) << "Enabling TLS extension permutation"; - SSL_set_permute_extensions(ssl(), true); + // Ask BoringSSL to randomize the order of TLS extensions. + SSL_set_permute_extensions(ssl(), permutes_tls_extensions); #endif // BORINGSSL_API_VERSION - } // Set the SNI to send, if any. SSL_set_connect_state(ssl()); @@ -325,6 +326,13 @@ std::string TlsClientHandshaker::chlo_hash() const { return ""; } +bool TlsClientHandshaker::ExportKeyingMaterial(absl::string_view label, + absl::string_view context, + size_t result_len, + std::string* result) { + return ExportKeyingMaterialForLabel(label, context, result_len, result); +} + bool TlsClientHandshaker::encryption_established() const { return encryption_established_; } 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 eb39ccf9fab..4543f9b2007 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 @@ -50,6 +50,8 @@ class QUIC_EXPORT_PRIVATE TlsClientHandshaker bool ReceivedInchoateReject() const override; int num_scup_messages_received() const override; std::string chlo_hash() const override; + bool ExportKeyingMaterial(absl::string_view label, absl::string_view context, + size_t result_len, std::string* result) override; // From QuicCryptoClientStream::HandshakerInterface and TlsHandshaker bool encryption_established() const override; @@ -81,8 +83,9 @@ class QUIC_EXPORT_PRIVATE TlsClientHandshaker void AllowEmptyAlpnForTests() { allow_empty_alpn_for_tests_ = true; } void AllowInvalidSNIForTests() { allow_invalid_sni_for_tests_ = true; } - SSL* GetSslForTests() { return tls_connection_.ssl(); } - const SSL* GetSslForTests() const { return tls_connection_.ssl(); } + + // Make the SSL object from BoringSSL publicly accessible. + using TlsHandshaker::ssl; protected: const TlsConnection* tls_connection() const override { diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc index 4b2d717befc..dee634ba2cc 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc +++ b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc @@ -328,6 +328,23 @@ std::unique_ptr<QuicEncrypter> TlsHandshaker::CreateCurrentOneRttEncrypter() { return encrypter; } +bool TlsHandshaker::ExportKeyingMaterialForLabel(absl::string_view label, + absl::string_view context, + size_t result_len, + std::string* result) { + // TODO(haoyuewang) Adding support of keying material export when 0-RTT is + // accepted. + if (SSL_in_init(ssl())) { + return false; + } + result->resize(result_len); + return SSL_export_keying_material( + ssl(), reinterpret_cast<uint8_t*>(&*result->begin()), result_len, + label.data(), label.size(), + reinterpret_cast<const uint8_t*>(context.data()), context.size(), + !context.empty()) == 1; +} + void TlsHandshaker::WriteMessage(EncryptionLevel level, absl::string_view data) { stream_->WriteCryptoData(level, data); diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h index 72ae0993983..33355649550 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h +++ b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h @@ -53,6 +53,9 @@ class QUIC_EXPORT_PRIVATE TlsHandshaker : public TlsConnection::Delegate, std::unique_ptr<QuicDecrypter> AdvanceKeysAndCreateCurrentOneRttDecrypter(); std::unique_ptr<QuicEncrypter> CreateCurrentOneRttEncrypter(); virtual HandshakeState GetHandshakeState() const = 0; + bool ExportKeyingMaterialForLabel(absl::string_view label, + absl::string_view context, + size_t result_len, std::string* result); protected: // Called when a new message is received on the crypto stream and is available 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 b2817b15b22..4e47a6395ce 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 @@ -77,13 +77,15 @@ TlsServerHandshaker::DefaultProofSourceHandle::SelectCertificate( return QUIC_FAILURE; } + bool cert_matched_sni; QuicReferenceCountedPointer<ProofSource::Chain> chain = - proof_source_->GetCertChain(server_address, client_address, hostname); + proof_source_->GetCertChain(server_address, client_address, hostname, + &cert_matched_sni); handshaker_->OnSelectCertificateDone( /*ok=*/true, /*is_sync=*/true, chain.get(), /*handshake_hints=*/absl::string_view(), - /*ticket_encryption_key=*/absl::string_view()); + /*ticket_encryption_key=*/absl::string_view(), cert_matched_sni); if (!handshaker_->select_cert_status().has_value()) { QUIC_BUG(quic_bug_12423_1) << "select_cert_status() has no value after a synchronous select cert"; @@ -138,26 +140,42 @@ void TlsServerHandshaker::DecryptCallback::Run(std::vector<uint8_t> plaintext) { // The callback was cancelled before we could run. return; } - handshaker_->decrypted_session_ticket_ = std::move(plaintext); + + TlsServerHandshaker* handshaker = handshaker_; + handshaker_ = nullptr; + + handshaker->decrypted_session_ticket_ = std::move(plaintext); + const bool is_async = + (handshaker->expected_ssl_error() == SSL_ERROR_PENDING_TICKET); + + absl::optional<QuicConnectionContextSwitcher> context_switcher; + if (handshaker->restore_connection_context_in_callbacks_) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_tls_restore_connection_context_in_callbacks, 1, 3); + if (is_async) { + context_switcher.emplace(handshaker->connection_context()); + } + QUIC_TRACESTRING( + absl::StrCat("TLS ticket decryption done. len(decrypted_ticket):", + handshaker->decrypted_session_ticket_.size())); + } + // DecryptCallback::Run could be called synchronously. When that happens, we // are currently in the middle of a call to AdvanceHandshake. - // (AdvanceHandshake called SSL_do_handshake, which through some layers called - // SessionTicketOpen, which called TicketCrypter::Decrypt, which synchronously - // called this function.) In that case, the handshake will continue to be - // processed when this function returns. + // (AdvanceHandshake called SSL_do_handshake, which through some layers + // called SessionTicketOpen, which called TicketCrypter::Decrypt, which + // synchronously called this function.) In that case, the handshake will + // continue to be processed when this function returns. // - // When this callback is called asynchronously (i.e. the ticket decryption is - // pending), TlsServerHandshaker is not actively processing handshake + // When this callback is called asynchronously (i.e. the ticket decryption + // is pending), TlsServerHandshaker is not actively processing handshake // messages. We need to have it resume processing handshake messages by // calling AdvanceHandshake. - if (handshaker_->expected_ssl_error() == SSL_ERROR_PENDING_TICKET) { - handshaker_->AdvanceHandshakeFromCallback(); - } - // The TicketDecrypter took ownership of this callback when Decrypt was - // called. Once the callback returns, it will be deleted. Remove the - // (non-owning) pointer to the callback from the handshaker so the handshaker - // doesn't have an invalid pointer hanging around. - handshaker_->ticket_decryption_callback_ = nullptr; + if (is_async) { + handshaker->AdvanceHandshakeFromCallback(); + } + + handshaker->ticket_decryption_callback_ = nullptr; } void TlsServerHandshaker::DecryptCallback::Cancel() { @@ -188,13 +206,15 @@ TlsServerHandshaker::TlsServerHandshaker( } SSL_set_quic_use_legacy_codepoint(ssl(), use_legacy_extension); - if (GetQuicFlag(FLAGS_quic_disable_server_tls_resumption)) { - SSL_set_options(ssl(), SSL_OP_NO_TICKET); + if (!session->quic_tls_disable_resumption_refactor()) { + if (GetQuicFlag(FLAGS_quic_disable_server_tls_resumption)) { + SSL_set_options(ssl(), SSL_OP_NO_TICKET); + } + } else { + QUIC_RELOADABLE_FLAG_COUNT(quic_tls_disable_resumption_refactor); } - if (GetQuicReloadableFlag(quic_trace_ssl_events) && - session->connection()->context()->tracer) { - QUIC_RELOADABLE_FLAG_COUNT(quic_trace_ssl_events); + if (session->connection()->context()->tracer) { tls_connection_.EnableInfoCallback(); } } @@ -337,10 +357,19 @@ bool TlsServerHandshaker::ShouldSendExpectCTHeader() const { return false; } +bool TlsServerHandshaker::DidCertMatchSni() const { return cert_matched_sni_; } + const ProofSource::Details* TlsServerHandshaker::ProofSourceDetails() const { return proof_source_details_.get(); } +bool TlsServerHandshaker::ExportKeyingMaterial(absl::string_view label, + absl::string_view context, + size_t result_len, + std::string* result) { + return ExportKeyingMaterialForLabel(label, context, result_len, result); +} + void TlsServerHandshaker::OnConnectionClosed(QuicErrorCode error, ConnectionCloseSource source) { TlsHandshaker::OnConnectionClosed(error, source); @@ -663,6 +692,19 @@ void TlsServerHandshaker::OnComputeSignatureDone( QUIC_DVLOG(1) << "OnComputeSignatureDone. ok:" << ok << ", is_sync:" << is_sync << ", len(signature):" << signature.size(); + absl::optional<QuicConnectionContextSwitcher> context_switcher; + if (restore_connection_context_in_callbacks_) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_tls_restore_connection_context_in_callbacks, 2, 3); + + if (!is_sync) { + context_switcher.emplace(connection_context()); + } + + QUIC_TRACESTRING(absl::StrCat("TLS compute signature done. ok:", ok, + ", len(signature):", signature.size())); + } + if (ok) { cert_verify_sig_ = std::move(signature); proof_source_details_ = std::move(details); @@ -713,7 +755,7 @@ ssl_ticket_aead_result_t TlsServerHandshaker::SessionTicketOpen( absl::string_view in) { QUICHE_DCHECK(proof_source_->GetTicketCrypter()); - if (allow_ignore_ticket_open_ && ignore_ticket_open_) { + if (ignore_ticket_open_) { // SetIgnoreTicketOpen has been called. Typically this means the caller is // using handshake hints and expect the hints to contain ticket decryption // results. @@ -722,21 +764,20 @@ ssl_ticket_aead_result_t TlsServerHandshaker::SessionTicketOpen( } if (!ticket_decryption_callback_) { - if (!allow_ignore_ticket_open_) { - ticket_received_ = true; - } ticket_decryption_callback_ = new DecryptCallback(this); proof_source_->GetTicketCrypter()->Decrypt( in, std::unique_ptr<DecryptCallback>(ticket_decryption_callback_)); + // Decrypt can run the callback synchronously. In that case, the callback // will clear the ticket_decryption_callback_ pointer, and instead of - // returning ssl_ticket_aead_retry, we should continue processing to return - // the decrypted ticket. + // returning ssl_ticket_aead_retry, we should continue processing to + // return the decrypted ticket. // // If the callback is not run synchronously, return ssl_ticket_aead_retry // and when the callback is complete this function will be run again to // return the result. if (ticket_decryption_callback_) { + QUICHE_DCHECK(!ticket_decryption_callback_->IsDone()); set_expected_ssl_error(SSL_ERROR_PENDING_TICKET); if (async_op_timer_.has_value()) { QUIC_CODE_COUNT( @@ -744,10 +785,16 @@ ssl_ticket_aead_result_t TlsServerHandshaker::SessionTicketOpen( } async_op_timer_ = QuicTimeAccumulator(); async_op_timer_->Start(now()); - return ssl_ticket_aead_retry; } } + // If the async ticket decryption is pending, either started by this + // SessionTicketOpen call or one that happened earlier, return + // ssl_ticket_aead_retry. + if (ticket_decryption_callback_ && !ticket_decryption_callback_->IsDone()) { + return ssl_ticket_aead_retry; + } + ssl_ticket_aead_result_t result = FinalizeSessionTicketOpen(out, out_len, max_out_len); @@ -817,8 +864,7 @@ ssl_select_cert_result_t TlsServerHandshaker::EarlySelectCertCallback( return ssl_select_cert_error; } - if (allow_ignore_ticket_open_) { - QUIC_RELOADABLE_FLAG_COUNT(quic_tls_allow_ignore_ticket_open); + { const uint8_t* unused_extension_bytes; size_t unused_extension_len; ticket_received_ = SSL_early_callback_ctx_extension_get( @@ -919,15 +965,30 @@ ssl_select_cert_result_t TlsServerHandshaker::EarlySelectCertCallback( void TlsServerHandshaker::OnSelectCertificateDone( bool ok, bool is_sync, const ProofSource::Chain* chain, - absl::string_view handshake_hints, - absl::string_view ticket_encryption_key) { + absl::string_view handshake_hints, absl::string_view ticket_encryption_key, + bool cert_matched_sni) { QUIC_DVLOG(1) << "OnSelectCertificateDone. ok:" << ok << ", is_sync:" << is_sync << ", len(handshake_hints):" << handshake_hints.size() << ", len(ticket_encryption_key):" << ticket_encryption_key.size(); + absl::optional<QuicConnectionContextSwitcher> context_switcher; + if (restore_connection_context_in_callbacks_) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_tls_restore_connection_context_in_callbacks, 3, 3); + + if (!is_sync) { + context_switcher.emplace(connection_context()); + } + + QUIC_TRACESTRING(absl::StrCat( + "TLS select certificate done: ok:", ok, + ", len(handshake_hints):", handshake_hints.size(), + ", len(ticket_encryption_key):", ticket_encryption_key.size())); + } ticket_encryption_key_ = std::string(ticket_encryption_key); select_cert_status_ = QUIC_FAILURE; + cert_matched_sni_ = cert_matched_sni; if (ok) { if (chain && !chain->certs.empty()) { tls_connection_.SetCertChain(chain->ToCryptoBuffers().value); @@ -945,7 +1006,9 @@ void TlsServerHandshaker::OnSelectCertificateDone( } else { QUIC_LOG(ERROR) << "No certs provided for host '" << crypto_negotiated_params_->sni << "', server_address:" - << session()->connection()->self_address(); + << session()->connection()->self_address() + << ", client_address:" + << session()->connection()->peer_address(); } } @@ -1047,14 +1110,11 @@ TlsServerHandshaker::SetApplicationSettings(absl::string_view alpn) { const std::string& hostname = crypto_negotiated_params_->sni; std::string accept_ch_value = GetAcceptChValueForHostname(hostname); std::string origin = absl::StrCat("https://", hostname); - if (GetQuicReloadableFlag(quic_include_port_in_alps_origin)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_include_port_in_alps_origin); - uint16_t port = session()->self_address().port(); - if (port != kDefaultPort) { - // This should be rare in production, but useful for test servers. - QUIC_CODE_COUNT(quic_server_alps_non_default_port); - absl::StrAppend(&origin, ":", port); - } + uint16_t port = session()->self_address().port(); + if (port != kDefaultPort) { + // This should be rare in production, but useful for test servers. + QUIC_CODE_COUNT(quic_server_alps_non_default_port); + absl::StrAppend(&origin, ":", port); } if (!accept_ch_value.empty()) { @@ -1075,4 +1135,6 @@ TlsServerHandshaker::SetApplicationSettings(absl::string_view alpn) { return result; } +SSL* TlsServerHandshaker::GetSsl() const { return ssl(); } + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h index 998cc15ad7c..b7179a76607 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 @@ -62,7 +62,11 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker bool ValidateAddressToken(absl::string_view token) const override; void OnNewTokenReceived(absl::string_view token) override; bool ShouldSendExpectCTHeader() const override; + bool DidCertMatchSni() const override; const ProofSource::Details* ProofSourceDetails() const override; + bool ExportKeyingMaterial(absl::string_view label, absl::string_view context, + size_t result_len, std::string* result) override; + SSL* GetSsl() const override; // From QuicCryptoServerStreamBase and TlsHandshaker ssl_early_data_reason_t EarlyDataReason() const override; @@ -169,10 +173,11 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker bool HasValidSignature(size_t max_signature_size) const; // ProofSourceHandleCallback implementation: - void OnSelectCertificateDone( - bool ok, bool is_sync, const ProofSource::Chain* chain, - absl::string_view handshake_hints, - absl::string_view ticket_encryption_key) override; + void OnSelectCertificateDone(bool ok, bool is_sync, + const ProofSource::Chain* chain, + absl::string_view handshake_hints, + absl::string_view ticket_encryption_key, + bool cert_matched_sni) override; void OnComputeSignatureDone( bool ok, @@ -188,9 +193,6 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker void SetIgnoreTicketOpen(bool value) { ignore_ticket_open_ = value; } - const bool allow_ignore_ticket_open_ = - GetQuicReloadableFlag(quic_tls_allow_ignore_ticket_open); - private: class QUIC_EXPORT_PRIVATE DecryptCallback : public ProofSource::DecryptCallback { @@ -201,6 +203,11 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker // If called, Cancel causes the pending callback to be a no-op. void Cancel(); + // Return true if either + // - Cancel() has been called. + // - Run() has been called, or is in the middle of it. + bool IsDone() const { return handshaker_ == nullptr; } + private: TlsServerHandshaker* handshaker_; }; @@ -259,22 +266,12 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker return; } - if (GetQuicReloadableFlag(quic_run_default_signature_callback_once)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_run_default_signature_callback_once); - DefaultProofSourceHandle* handle = handle_; - handle_ = nullptr; - - handle->signature_callback_ = nullptr; - if (handle->handshaker_ != nullptr) { - handle->handshaker_->OnComputeSignatureDone( - ok, is_sync_, std::move(signature), std::move(details)); - } - return; - } + DefaultProofSourceHandle* handle = handle_; + handle_ = nullptr; - handle_->signature_callback_ = nullptr; - if (handle_->handshaker_ != nullptr) { - handle_->handshaker_->OnComputeSignatureDone( + handle->signature_callback_ = nullptr; + if (handle->handshaker_ != nullptr) { + handle->handshaker_->OnComputeSignatureDone( ok, is_sync_, std::move(signature), std::move(details)); } } @@ -321,6 +318,11 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker } QuicTime now() const { return session()->GetClock()->Now(); } + QuicConnectionContext* connection_context() { + QUICHE_DCHECK(restore_connection_context_in_callbacks_); + return session()->connection()->context(); + } + std::unique_ptr<ProofSourceHandle> proof_source_handle_; ProofSource* proof_source_; @@ -365,6 +367,10 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker crypto_negotiated_params_; TlsServerConnection tls_connection_; const QuicCryptoServerConfig* crypto_config_; // Unowned. + const bool restore_connection_context_in_callbacks_ = + GetQuicReloadableFlag(quic_tls_restore_connection_context_in_callbacks); + + bool cert_matched_sni_ = false; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker_test.cc b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker_test.cc index 6f8db123c99..bf68c8513f9 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 @@ -101,6 +101,7 @@ class TestTlsServerHandshaker : public TlsServerHandshaker { return fake_proof_source_handle_; } + using TlsServerHandshaker::AdvanceHandshake; using TlsServerHandshaker::expected_ssl_error; private: @@ -565,7 +566,7 @@ TEST_P(TlsServerHandshakerTest, SSLConfigForCertSelection) { InitializeServerWithFakeProofSourceHandle(); // Disable early data. - server_session_->ssl_config()->early_data_enabled = false; + server_session_->set_early_data_enabled(false); server_handshaker_->SetupProofSourceHandle( /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, @@ -708,6 +709,41 @@ TEST_P(TlsServerHandshakerTest, ResumptionWithAsyncDecryptCallback) { EXPECT_TRUE(server_stream()->ResumptionAttempted()); } +TEST_P(TlsServerHandshakerTest, AdvanceHandshakeDuringAsyncDecryptCallback) { + if (GetParam().disable_resumption) { + return; + } + + // Do the first handshake + InitializeFakeClient(); + CompleteCryptoHandshake(); + ExpectHandshakeSuccessful(); + + ticket_crypter_->SetRunCallbacksAsync(true); + // Now do another handshake + InitializeServerWithFakeProofSourceHandle(); + server_handshaker_->SetupProofSourceHandle( + /*select_cert_action=*/FakeProofSourceHandle::Action::DELEGATE_SYNC, + /*compute_signature_action=*/FakeProofSourceHandle::Action:: + DELEGATE_SYNC); + InitializeFakeClient(); + + AdvanceHandshakeWithFakeClient(); + + // Ensure an async DecryptCallback is now pending. + ASSERT_EQ(ticket_crypter_->NumPendingCallbacks(), 1u); + + { + QuicConnection::ScopedPacketFlusher flusher(server_connection_); + server_handshaker_->AdvanceHandshake(); + } + + // This will delete |server_handshaker_|. + server_session_ = nullptr; + + ticket_crypter_->RunPendingCallback(0); // Should not crash. +} + TEST_P(TlsServerHandshakerTest, ResumptionWithFailingDecryptCallback) { if (GetParam().disable_resumption) { return; diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc index cf612feab1d..77731fb2d47 100644 --- a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc @@ -368,7 +368,6 @@ TEST_F(UberReceivedPacketManagerTest, EXPECT_FALSE(HasPendingAck()); QuicConfig config; QuicTagVector connection_options; - connection_options.push_back(kACKD); // No limit on the number of packets received before sending an ack. connection_options.push_back(kAKDU); config.SetConnectionOptionsToSend(connection_options); diff --git a/chromium/net/third_party/quiche/src/quic/core/web_transport_interface.h b/chromium/net/third_party/quiche/src/quic/core/web_transport_interface.h index 154d242b1e8..102fd1fccef 100644 --- a/chromium/net/third_party/quiche/src/quic/core/web_transport_interface.h +++ b/chromium/net/third_party/quiche/src/quic/core/web_transport_interface.h @@ -16,6 +16,7 @@ #include "quic/core/quic_datagram_queue.h" #include "quic/core/quic_types.h" #include "quic/platform/api/quic_export.h" +#include "spdy/core/spdy_header_block.h" namespace quic { @@ -28,6 +29,14 @@ class QUIC_EXPORT_PRIVATE WebTransportStreamVisitor { virtual void OnCanRead() = 0; // Called whenever the stream is not write-blocked and can accept new data. virtual void OnCanWrite() = 0; + + // Called when RESET_STREAM is received for the stream. + virtual void OnResetStreamReceived(WebTransportStreamError error) = 0; + // Called when STOP_SENDING is received for the stream. + virtual void OnStopSendingReceived(WebTransportStreamError error) = 0; + // Called when the write side of the stream is closed and all of the data sent + // has been acknowledged ("Data Recvd" state of RFC 9000). + virtual void OnWriteSideInDataRecvdState() = 0; }; // A stream (either bidirectional or unidirectional) that is contained within a @@ -66,9 +75,9 @@ class QUIC_EXPORT_PRIVATE WebTransportStream { virtual QuicStreamId GetStreamId() const = 0; // Resets the stream with the specified error code. - // TODO(b/184048994): change the error code type based on IETF consensus. - virtual void ResetWithUserCode(QuicRstStreamErrorCode error) = 0; + virtual void ResetWithUserCode(WebTransportStreamError error) = 0; virtual void ResetDueToInternalError() = 0; + virtual void SendStopSending(WebTransportStreamError error) = 0; // Called when the owning object has been garbage-collected. virtual void MaybeResetDueToStreamObjectGone() = 0; @@ -84,7 +93,11 @@ class QUIC_EXPORT_PRIVATE WebTransportVisitor { // Notifies the visitor when the session is ready to exchange application // data. - virtual void OnSessionReady() = 0; + virtual void OnSessionReady(const spdy::SpdyHeaderBlock& headers) = 0; + + // Notifies the visitor when the session has been closed. + virtual void OnSessionClosed(WebTransportSessionError error_code, + const std::string& error_message) = 0; // Notifies the visitor when a new stream has been received. The stream in // question can be retrieved using AcceptIncomingBidirectionalStream() or @@ -105,6 +118,11 @@ class QUIC_EXPORT_PRIVATE WebTransportSession { public: virtual ~WebTransportSession() {} + // Closes the WebTransport session in question with the specified |error_code| + // and |error_message|. + virtual void CloseSession(WebTransportSessionError error_code, + absl::string_view error_message) = 0; + // Return the earliest incoming stream that has been received by the session // but has not been accepted. Returns nullptr if there are no incoming // streams. @@ -120,6 +138,9 @@ class QUIC_EXPORT_PRIVATE WebTransportSession { virtual WebTransportStream* OpenOutgoingUnidirectionalStream() = 0; virtual MessageStatus SendOrQueueDatagram(QuicMemSlice datagram) = 0; + // Returns a conservative estimate of the largest datagram size that the + // session would be able to send. + virtual QuicByteCount GetMaxDatagramSize() const = 0; // Sets the largest duration that a datagram can spend in the queue before // being silently dropped. virtual void SetDatagramMaxTimeInQueue(QuicTime::Delta max_time_in_queue) = 0; diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_client_session.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_client_session.cc index 0d865692e46..19269688a0d 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_client_session.cc +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_client_session.cc @@ -3,7 +3,9 @@ // found in the LICENSE file. #include "quic/masque/masque_client_session.h" + #include "absl/algorithm/container.h" +#include "absl/strings/str_cat.h" #include "quic/core/http/spdy_utils.h" #include "quic/core/quic_data_reader.h" #include "quic/core/quic_utils.h" @@ -11,20 +13,13 @@ namespace quic { MasqueClientSession::MasqueClientSession( - MasqueMode masque_mode, - const QuicConfig& config, + MasqueMode masque_mode, const QuicConfig& config, const ParsedQuicVersionVector& supported_versions, - QuicConnection* connection, - const QuicServerId& server_id, + QuicConnection* connection, const QuicServerId& server_id, QuicCryptoClientConfig* crypto_config, - QuicClientPushPromiseIndex* push_promise_index, - Owner* owner) - : QuicSpdyClientSession(config, - supported_versions, - connection, - server_id, - crypto_config, - push_promise_index), + QuicClientPushPromiseIndex* push_promise_index, Owner* owner) + : QuicSpdyClientSession(config, supported_versions, connection, server_id, + crypto_config, push_promise_index), masque_mode_(masque_mode), owner_(owner), compression_engine_(this) {} @@ -101,7 +96,9 @@ MasqueClientSession::GetOrCreateConnectUdpClientState( headers[":scheme"] = "masque"; headers[":path"] = "/"; headers[":authority"] = target_server_address.ToString(); - SpdyUtils::AddDatagramFlowIdHeader(&headers, stream->id()); + if (http_datagram_support() == HttpDatagramSupport::kDraft00) { + SpdyUtils::AddDatagramFlowIdHeader(&headers, stream->id()); + } size_t bytes_sent = stream->SendRequest(std::move(headers), /*body=*/"", /*fin=*/false); if (bytes_sent == 0) { @@ -118,8 +115,7 @@ MasqueClientSession::GetOrCreateConnectUdpClientState( void MasqueClientSession::SendPacket( QuicConnectionId client_connection_id, - QuicConnectionId server_connection_id, - absl::string_view packet, + QuicConnectionId server_connection_id, absl::string_view packet, const QuicSocketAddress& target_server_address, EncapsulatedClientSession* encapsulated_client_session) { if (masque_mode_ == MasqueMode::kLegacy) { @@ -142,8 +138,8 @@ void MasqueClientSession::SendPacket( << " compressed with stream ID " << connect_udp->stream()->id() << " context ID " << (connect_udp->context_id().has_value() - ? connect_udp->context_id().value() - : 0) + ? absl::StrCat(connect_udp->context_id().value()) + : "none") << " and got message status " << MessageStatusToString(message_status); } @@ -182,8 +178,8 @@ void MasqueClientSession::UnregisterConnectionId( QUIC_DLOG(INFO) << "Removing state for stream ID " << it->stream()->id() << " context ID " << (it->context_id().has_value() - ? it->context_id().value() - : 0); + ? absl::StrCat(it->context_id().value()) + : "none"); auto* stream = it->stream(); it = connect_udp_client_states_.erase(it); if (!stream->write_side_closed()) { @@ -196,8 +192,7 @@ void MasqueClientSession::UnregisterConnectionId( } void MasqueClientSession::OnConnectionClosed( - const QuicConnectionCloseFrame& frame, - ConnectionCloseSource source) { + const QuicConnectionCloseFrame& frame, ConnectionCloseSource source) { QuicSpdyClientSession::OnConnectionClosed(frame, source); // Close all encapsulated sessions. for (const auto& client_state : connect_udp_client_states_) { @@ -224,8 +219,8 @@ void MasqueClientSession::OnStreamClosed(QuicStreamId stream_id) { QUIC_DLOG(INFO) << "Stream " << stream_id << " was closed, removing state for context ID " << (it->context_id().has_value() - ? it->context_id().value() - : 0); + ? absl::StrCat(it->context_id().value()) + : "none"); auto* encapsulated_client_session = it->encapsulated_client_session(); it = connect_udp_client_states_.erase(it); encapsulated_client_session->CloseConnection( @@ -241,6 +236,7 @@ void MasqueClientSession::OnStreamClosed(QuicStreamId stream_id) { } bool MasqueClientSession::OnSettingsFrame(const SettingsFrame& frame) { + QUIC_DLOG(INFO) << "Received SETTINGS: " << frame; if (!QuicSpdyClientSession::OnSettingsFrame(frame)) { QUIC_DLOG(ERROR) << "Failed to parse received settings"; return false; @@ -249,6 +245,7 @@ bool MasqueClientSession::OnSettingsFrame(const SettingsFrame& frame) { QUIC_DLOG(ERROR) << "Refusing to use MASQUE without HTTP/3 Datagrams"; return false; } + QUIC_DLOG(INFO) << "Using HTTP Datagram: " << http_datagram_support(); owner_->OnSettingsReceived(); return true; } @@ -266,9 +263,9 @@ MasqueClientSession::ConnectUdpClientState::ConnectUdpClientState( target_server_address_(target_server_address) { QUICHE_DCHECK_NE(masque_session_, nullptr); this->stream()->RegisterHttp3DatagramRegistrationVisitor(this); - Http3DatagramContextExtensions extensions; - this->stream()->RegisterHttp3DatagramContextId(this->context_id(), extensions, - this); + this->stream()->RegisterHttp3DatagramContextId( + this->context_id(), DatagramFormatType::UDP_PAYLOAD, + /*format_additional_data=*/absl::string_view(), this); } MasqueClientSession::ConnectUdpClientState::~ConnectUdpClientState() { @@ -308,24 +305,40 @@ void MasqueClientSession::ConnectUdpClientState::OnHttp3Datagram( QUIC_DVLOG(1) << "Sent " << payload.size() << " bytes to connection for stream ID " << stream_id << " context ID " - << (context_id.has_value() ? context_id.value() : 0); + << (context_id.has_value() ? absl::StrCat(context_id.value()) + : "none"); } void MasqueClientSession::ConnectUdpClientState::OnContextReceived( QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& /*extensions*/) { + DatagramFormatType format_type, absl::string_view format_additional_data) { if (stream_id != stream_->id()) { QUIC_BUG(MASQUE client bad datagram context registration) << "Registered stream ID " << stream_id << ", expected " << stream_->id(); return; } + if (format_type != DatagramFormatType::UDP_PAYLOAD) { + QUIC_DLOG(INFO) << "Ignoring unexpected datagram format type " + << DatagramFormatTypeToString(format_type); + return; + } + if (!format_additional_data.empty()) { + QUIC_DLOG(ERROR) + << "Received non-empty format additional data for context ID " + << (context_id_.has_value() ? context_id_.value() : 0) + << " on stream ID " << stream()->id(); + masque_session_->ResetStream(stream()->id(), QUIC_STREAM_CANCELLED); + return; + } if (context_id != context_id_) { - QUIC_DLOG(INFO) << "Ignoring unexpected context ID " - << (context_id.has_value() ? context_id.value() : 0) - << " instead of " - << (context_id_.has_value() ? context_id_.value() : 0) - << " on stream ID " << stream_->id(); + QUIC_DLOG(INFO) + << "Ignoring unexpected context ID " + << (context_id.has_value() ? absl::StrCat(context_id.value()) : "none") + << " instead of " + << (context_id_.has_value() ? absl::StrCat(context_id_.value()) + : "none") + << " on stream ID " << stream_->id(); return; } // Do nothing since the client registers first and we currently ignore @@ -334,7 +347,7 @@ void MasqueClientSession::ConnectUdpClientState::OnContextReceived( void MasqueClientSession::ConnectUdpClientState::OnContextClosed( QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& /*extensions*/) { + ContextCloseCode close_code, absl::string_view close_details) { if (stream_id != stream_->id()) { QUIC_BUG(MASQUE client bad datagram context registration) << "Closed context on stream ID " << stream_id << ", expected " @@ -342,15 +355,18 @@ void MasqueClientSession::ConnectUdpClientState::OnContextClosed( return; } if (context_id != context_id_) { - QUIC_DLOG(INFO) << "Ignoring unexpected close of context ID " - << (context_id.has_value() ? context_id.value() : 0) - << " instead of " - << (context_id_.has_value() ? context_id_.value() : 0) - << " on stream ID " << stream_->id(); + QUIC_DLOG(INFO) + << "Ignoring unexpected close of context ID " + << (context_id.has_value() ? absl::StrCat(context_id.value()) : "none") + << " instead of " + << (context_id_.has_value() ? absl::StrCat(context_id_.value()) + : "none") + << " on stream ID " << stream_->id(); return; } - QUIC_DLOG(INFO) << "Received datagram context close on stream ID " - << stream_->id() << ", closing stream"; + QUIC_DLOG(INFO) << "Received datagram context close with close code " + << close_code << " close details \"" << close_details + << "\" on stream ID " << stream_->id() << ", closing stream"; masque_session_->ResetStream(stream_->id(), QUIC_STREAM_CANCELLED); } diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_client_session.h b/chromium/net/third_party/quiche/src/quic/masque/masque_client_session.h index 1c130873340..662783a8cdc 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_client_session.h +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_client_session.h @@ -144,14 +144,14 @@ class QUIC_NO_EXPORT MasqueClientSession : public QuicSpdyClientSession { absl::string_view payload) override; // From QuicSpdyStream::Http3DatagramRegistrationVisitor. - void OnContextReceived( - QuicStreamId stream_id, - absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& extensions) override; - void OnContextClosed( - QuicStreamId stream_id, - absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& extensions) override; + void OnContextReceived(QuicStreamId stream_id, + absl::optional<QuicDatagramContextId> context_id, + DatagramFormatType format_type, + absl::string_view format_additional_data) override; + void OnContextClosed(QuicStreamId stream_id, + absl::optional<QuicDatagramContextId> context_id, + ContextCloseCode close_code, + absl::string_view close_details) override; private: QuicSpdyClientStream* stream_; // Unowned. @@ -161,7 +161,9 @@ class QUIC_NO_EXPORT MasqueClientSession : public QuicSpdyClientSession { QuicSocketAddress target_server_address_; }; - bool ShouldNegotiateHttp3Datagram() override { return true; } + HttpDatagramSupport LocalHttpDatagramSupport() override { + return HttpDatagramSupport::kDraft00And04; + } const ConnectUdpClientState* GetOrCreateConnectUdpClientState( const QuicSocketAddress& target_server_address, diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.cc index ec475205ac8..2b835cf05f3 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.cc +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.cc @@ -68,7 +68,7 @@ std::unique_ptr<MasqueEpollClient> MasqueEpollClient::Create( return nullptr; } - masque_client->set_initial_max_packet_length(kDefaultMaxPacketSize); + masque_client->set_initial_max_packet_length(kMasqueMaxOuterPacketSize); masque_client->set_drop_response_body(false); if (!masque_client->Initialize()) { QUIC_LOG(ERROR) << "Failed to initialize masque_client"; diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_server_session.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_server_session.cc index effc17ff316..693f0094a27 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_server_session.cc +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_server_session.cc @@ -196,19 +196,20 @@ std::unique_ptr<QuicBackendResponse> MasqueServerSession::HandleMasqueRequest( } if (scheme.empty()) { return CreateBackendErrorResponse("400", "Empty scheme"); - return nullptr; } if (method != "CONNECT-UDP") { QUIC_DLOG(ERROR) << "MASQUE request with bad method \"" << method << "\""; return CreateBackendErrorResponse("400", "Bad method"); } - absl::optional<QuicDatagramStreamId> flow_id = - SpdyUtils::ParseDatagramFlowIdHeader(request_headers); - if (!flow_id.has_value()) { - QUIC_DLOG(ERROR) - << "MASQUE request with bad or missing DatagramFlowId header"; - return CreateBackendErrorResponse("400", - "Bad or missing DatagramFlowId header"); + absl::optional<QuicDatagramStreamId> flow_id; + if (http_datagram_support() == HttpDatagramSupport::kDraft00) { + flow_id = SpdyUtils::ParseDatagramFlowIdHeader(request_headers); + if (!flow_id.has_value()) { + QUIC_DLOG(ERROR) + << "MASQUE request with bad or missing DatagramFlowId header"; + return CreateBackendErrorResponse( + "400", "Bad or missing DatagramFlowId header"); + } } QuicUrl url(absl::StrCat("https://", authority)); if (!url.IsValid() || url.PathParamsQuery() != "/") { @@ -235,7 +236,9 @@ std::unique_ptr<QuicBackendResponse> MasqueServerSession::HandleMasqueRequest( info_list, freeaddrinfo); QuicSocketAddress target_server_address(info_list->ai_addr, info_list->ai_addrlen); - QUIC_DLOG(INFO) << "Got CONNECT_UDP request flow_id=" << *flow_id + QUIC_DLOG(INFO) << "Got CONNECT_UDP request on stream ID " + << request_handler->stream_id() << " flow_id=" + << (flow_id.has_value() ? absl::StrCat(*flow_id) : "none") << " target_server_address=\"" << target_server_address << "\""; @@ -264,20 +267,27 @@ std::unique_ptr<QuicBackendResponse> MasqueServerSession::HandleMasqueRequest( << request_handler->stream_id(); return CreateBackendErrorResponse("500", "Bad stream type"); } - stream->RegisterHttp3DatagramFlowId(*flow_id); + if (flow_id.has_value()) { + stream->RegisterHttp3DatagramFlowId(*flow_id); + } connect_udp_server_states_.push_back( ConnectUdpServerState(stream, context_id, target_server_address, fd_wrapper.extract_fd(), this)); - // TODO(b/181256914) remove this when we drop support for - // draft-ietf-masque-h3-datagram-00 in favor of later drafts. - Http3DatagramContextExtensions extensions; - stream->RegisterHttp3DatagramContextId(context_id, extensions, - &connect_udp_server_states_.back()); + if (http_datagram_support() == HttpDatagramSupport::kDraft00) { + // TODO(b/181256914) remove this when we drop support for + // draft-ietf-masque-h3-datagram-00 in favor of later drafts. + stream->RegisterHttp3DatagramContextId( + context_id, DatagramFormatType::UDP_PAYLOAD, + /*format_additional_data=*/absl::string_view(), + &connect_udp_server_states_.back()); + } spdy::Http2HeaderBlock response_headers; response_headers[":status"] = "200"; - SpdyUtils::AddDatagramFlowIdHeader(&response_headers, *flow_id); + if (flow_id.has_value()) { + SpdyUtils::AddDatagramFlowIdHeader(&response_headers, *flow_id); + } auto response = std::make_unique<QuicBackendResponse>(); response->set_response_type(QuicBackendResponse::INCOMPLETE_RESPONSE); response->set_headers(std::move(response_headers)); @@ -424,6 +434,19 @@ std::string MasqueServerSession::Name() const { return std::string("MasqueServerSession-") + connection_id().ToString(); } +bool MasqueServerSession::OnSettingsFrame(const SettingsFrame& frame) { + QUIC_DLOG(INFO) << "Received SETTINGS: " << frame; + if (!QuicSimpleServerSession::OnSettingsFrame(frame)) { + return false; + } + if (!SupportsH3Datagram()) { + QUIC_DLOG(ERROR) << "Refusing to use MASQUE without HTTP Datagrams"; + return false; + } + QUIC_DLOG(INFO) << "Using HTTP Datagram: " << http_datagram_support(); + return true; +} + MasqueServerSession::ConnectUdpServerState::ConnectUdpServerState( QuicSpdyStream* stream, absl::optional<QuicDatagramContextId> context_id, const QuicSocketAddress& target_server_address, QuicUdpSocketFd fd, @@ -440,10 +463,10 @@ MasqueServerSession::ConnectUdpServerState::ConnectUdpServerState( MasqueServerSession::ConnectUdpServerState::~ConnectUdpServerState() { if (stream() != nullptr) { - stream()->UnregisterHttp3DatagramRegistrationVisitor(); if (context_registered_) { stream()->UnregisterHttp3DatagramContextId(context_id()); } + stream()->UnregisterHttp3DatagramRegistrationVisitor(); } if (fd_ == kQuicInvalidSocketFd) { return; @@ -503,39 +526,55 @@ void MasqueServerSession::ConnectUdpServerState::OnHttp3Datagram( void MasqueServerSession::ConnectUdpServerState::OnContextReceived( QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& /*extensions*/) { + DatagramFormatType format_type, absl::string_view format_additional_data) { if (stream_id != stream()->id()) { QUIC_BUG(MASQUE server bad datagram context registration) << "Registered stream ID " << stream_id << ", expected " << stream()->id(); return; } + if (format_type != DatagramFormatType::UDP_PAYLOAD) { + QUIC_DLOG(INFO) << "Ignoring unexpected datagram format type " + << DatagramFormatTypeToString(format_type); + return; + } + if (!format_additional_data.empty()) { + QUIC_DLOG(ERROR) + << "Received non-empty format additional data for context ID " + << (context_id_.has_value() ? context_id_.value() : 0) + << " on stream ID " << stream()->id(); + masque_session_->ResetStream(stream()->id(), QUIC_STREAM_CANCELLED); + return; + } if (!context_received_) { context_received_ = true; context_id_ = context_id; } if (context_id != context_id_) { - QUIC_DLOG(INFO) << "Ignoring unexpected context ID " - << (context_id.has_value() ? context_id.value() : 0) - << " instead of " - << (context_id_.has_value() ? context_id_.value() : 0) - << " on stream ID " << stream()->id(); + QUIC_DLOG(INFO) + << "Ignoring unexpected context ID " + << (context_id.has_value() ? absl::StrCat(context_id.value()) : "none") + << " instead of " + << (context_id_.has_value() ? absl::StrCat(context_id_.value()) + : "none") + << " on stream ID " << stream()->id(); return; } if (context_registered_) { QUIC_BUG(MASQUE server double datagram context registration) << "Try to re-register stream ID " << stream_id << " context ID " - << (context_id_.has_value() ? context_id_.value() : 0); + << (context_id_.has_value() ? absl::StrCat(context_id_.value()) + : "none"); return; } context_registered_ = true; - Http3DatagramContextExtensions reply_extensions; - stream()->RegisterHttp3DatagramContextId(context_id_, reply_extensions, this); + stream()->RegisterHttp3DatagramContextId(context_id_, format_type, + format_additional_data, this); } void MasqueServerSession::ConnectUdpServerState::OnContextClosed( QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& /*extensions*/) { + ContextCloseCode close_code, absl::string_view close_details) { if (stream_id != stream()->id()) { QUIC_BUG(MASQUE server bad datagram context registration) << "Closed context on stream ID " << stream_id << ", expected " @@ -543,15 +582,18 @@ void MasqueServerSession::ConnectUdpServerState::OnContextClosed( return; } if (context_id != context_id_) { - QUIC_DLOG(INFO) << "Ignoring unexpected close of context ID " - << (context_id.has_value() ? context_id.value() : 0) - << " instead of " - << (context_id_.has_value() ? context_id_.value() : 0) - << " on stream ID " << stream()->id(); + QUIC_DLOG(INFO) + << "Ignoring unexpected close of context ID " + << (context_id.has_value() ? absl::StrCat(context_id.value()) : "none") + << " instead of " + << (context_id_.has_value() ? absl::StrCat(context_id_.value()) + : "none") + << " on stream ID " << stream()->id(); return; } - QUIC_DLOG(INFO) << "Received datagram context close on stream ID " - << stream()->id() << ", closing stream"; + QUIC_DLOG(INFO) << "Received datagram context close with close code " + << close_code << " close details \"" << close_details + << "\" on stream ID " << stream()->id() << ", closing stream"; masque_session_->ResetStream(stream()->id(), QUIC_STREAM_CANCELLED); } diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_server_session.h b/chromium/net/third_party/quiche/src/quic/masque/masque_server_session.h index 1bec14c5464..b3c327f5446 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_server_session.h +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_server_session.h @@ -117,14 +117,14 @@ class QUIC_NO_EXPORT MasqueServerSession absl::string_view payload) override; // From QuicSpdyStream::Http3DatagramRegistrationVisitor. - void OnContextReceived( - QuicStreamId stream_id, - absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& extensions) override; - void OnContextClosed( - QuicStreamId stream_id, - absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& extensions) override; + void OnContextReceived(QuicStreamId stream_id, + absl::optional<QuicDatagramContextId> context_id, + DatagramFormatType format_type, + absl::string_view format_additional_data) override; + void OnContextClosed(QuicStreamId stream_id, + absl::optional<QuicDatagramContextId> context_id, + ContextCloseCode close_code, + absl::string_view close_details) override; private: QuicSpdyStream* stream_; @@ -136,7 +136,11 @@ class QUIC_NO_EXPORT MasqueServerSession bool context_registered_ = false; }; - bool ShouldNegotiateHttp3Datagram() override { return true; } + // From QuicSpdySession. + bool OnSettingsFrame(const SettingsFrame& frame) override; + HttpDatagramSupport LocalHttpDatagramSupport() override { + return HttpDatagramSupport::kDraft00And04; + } MasqueServerBackend* masque_server_backend_; // Unowned. Visitor* owner_; // Unowned. diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_utils.h b/chromium/net/third_party/quiche/src/quic/masque/masque_utils.h index 8113047768c..abaeda7fa43 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_utils.h +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_utils.h @@ -18,7 +18,10 @@ QUIC_NO_EXPORT ParsedQuicVersionVector MasqueSupportedVersions(); QUIC_NO_EXPORT QuicConfig MasqueEncapsulatedConfig(); // Maximum packet size for encapsulated connections. -enum : QuicByteCount { kMasqueMaxEncapsulatedPacketSize = 1300 }; +enum : QuicByteCount { + kMasqueMaxEncapsulatedPacketSize = 1300, + kMasqueMaxOuterPacketSize = 1350, +}; // Mode that MASQUE is operating in. enum class MasqueMode : uint8_t { diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_containers.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_containers.h index eb16bfa18c3..19795e71a6f 100644 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_containers.h +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_containers.h @@ -5,7 +5,7 @@ #ifndef QUICHE_QUIC_PLATFORM_API_QUIC_CONTAINERS_H_ #define QUICHE_QUIC_PLATFORM_API_QUIC_CONTAINERS_H_ -#include "net/quic/platform/impl/quic_containers_impl.h" +#include "quiche_platform_impl/quiche_containers_impl.h" namespace quic { @@ -15,7 +15,7 @@ namespace quic { // // DOES NOT GUARANTEE POINTER OR ITERATOR STABILITY! template <typename Key, typename Compare = std::less<Key>> -using QuicSmallOrderedSet = QuicSmallOrderedSetImpl<Key, Compare>; +using QuicSmallOrderedSet = ::quiche::QuicheSmallOrderedSetImpl<Key, Compare>; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h deleted file mode 100644 index c33707a1763..00000000000 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 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_PLATFORM_API_QUIC_MEM_SLICE_SPAN_H_ -#define QUICHE_QUIC_PLATFORM_API_QUIC_MEM_SLICE_SPAN_H_ - -#include "absl/strings/string_view.h" -#include "quic/platform/api/quic_export.h" -#include "net/quic/platform/impl/quic_mem_slice_span_impl.h" - -namespace quic { - -// QuicMemSliceSpan is effectively wrapper around an array of data structures -// used as QuicMemSlice. So it could implemented with: -// QuicMemSlice* slices_; -// size_t num_slices_; -// But for efficiency reasons, the actual implementation is an array of -// platform-specific objects. This could avoid the translation from -// platform-specific object to QuicMemSlice. -// QuicMemSliceSpan does not own the underling data buffers. -class QUIC_EXPORT_PRIVATE QuicMemSliceSpan { - public: - explicit QuicMemSliceSpan(QuicMemSliceSpanImpl impl) : impl_(impl) {} - - // Constructs a span with a single QuicMemSlice. - explicit QuicMemSliceSpan(QuicMemSlice* slice) : impl_(slice->impl()) {} - - QuicMemSliceSpan(const QuicMemSliceSpan& other) = default; - QuicMemSliceSpan& operator=(const QuicMemSliceSpan& other) = default; - QuicMemSliceSpan(QuicMemSliceSpan&& other) = default; - QuicMemSliceSpan& operator=(QuicMemSliceSpan&& other) = default; - - ~QuicMemSliceSpan() = default; - - template <typename ConsumeFunction> - QuicByteCount ConsumeAll(ConsumeFunction consume) { - return impl_.ConsumeAll(consume); - } - - // Return data of the span at |index| by the form of a - // absl::string_view. - absl::string_view GetData(int index) { return impl_.GetData(index); } - - // Return the total length of the data inside the span. - QuicByteCount total_length() { return impl_.total_length(); } - - // Return total number of slices in the span. - size_t NumSlices() { return impl_.NumSlices(); } - - bool empty() const { return impl_.empty(); } - - private: - QuicMemSliceSpanImpl impl_; -}; - -} // namespace quic - -#endif // QUICHE_QUIC_PLATFORM_API_QUIC_MEM_SLICE_SPAN_H_ diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span_test.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span_test.cc deleted file mode 100644 index 99a11df5a2e..00000000000 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span_test.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 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 "quic/platform/api/quic_mem_slice_span.h" - -#include "quic/core/quic_simple_buffer_allocator.h" -#include "quic/core/quic_types.h" -#include "quic/platform/api/quic_flags.h" -#include "quic/platform/api/quic_test.h" -#include "quic/platform/api/quic_test_mem_slice_vector.h" - -namespace quic { -namespace test { -namespace { - -class QuicMemSliceSpanImplTest : public QuicTest { - public: - QuicMemSliceSpanImplTest() { - for (size_t i = 0; i < 10; ++i) { - buffers_.push_back(std::make_pair(data_, 1024)); - } - } - - char data_[1024]; - std::vector<std::pair<char*, size_t>> buffers_; -}; - -TEST_F(QuicMemSliceSpanImplTest, ConsumeAll) { - SimpleBufferAllocator allocator; - QuicTestMemSliceVector vector(buffers_); - - int num_slices = 0; - QuicByteCount bytes_consumed = - vector.span().ConsumeAll([&](QuicMemSlice slice) { - EXPECT_EQ(data_, slice.data()); - EXPECT_EQ(1024u, slice.length()); - ++num_slices; - }); - - EXPECT_EQ(10 * 1024u, bytes_consumed); - EXPECT_EQ(10, num_slices); -} - -} // namespace -} // namespace test -} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.cc new file mode 100644 index 00000000000..87bce7f07fc --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.cc @@ -0,0 +1,35 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "quic/platform/api/quic_mem_slice_storage.h" + +#include "quic/core/quic_utils.h" + +namespace quic { + +QuicMemSliceStorage::QuicMemSliceStorage(const struct iovec* iov, int iov_count, + QuicBufferAllocator* allocator, + const QuicByteCount max_slice_len) { + if (iov == nullptr) { + return; + } + QuicByteCount write_len = 0; + for (int i = 0; i < iov_count; ++i) { + write_len += iov[i].iov_len; + } + QUICHE_DCHECK_LT(0u, write_len); + + size_t io_offset = 0; + while (write_len > 0) { + size_t slice_len = std::min(write_len, max_slice_len); + QuicBuffer buffer(allocator, slice_len); + QuicUtils::CopyToBuffer(iov, iov_count, io_offset, slice_len, + buffer.data()); + storage_.push_back(QuicMemSlice(std::move(buffer))); + write_len -= slice_len; + io_offset += slice_len; + } +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h index 3ec1fc4503c..4cccee3045f 100644 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h @@ -5,8 +5,14 @@ #ifndef QUICHE_QUIC_PLATFORM_API_QUIC_MEM_SLICE_STORAGE_H_ #define QUICHE_QUIC_PLATFORM_API_QUIC_MEM_SLICE_STORAGE_H_ +#include <vector> + +#include "absl/types/span.h" +#include "quic/core/quic_buffer_allocator.h" +#include "quic/core/quic_types.h" #include "quic/platform/api/quic_export.h" -#include "net/quic/platform/impl/quic_mem_slice_storage_impl.h" +#include "quic/platform/api/quic_iovec.h" +#include "quic/platform/api/quic_mem_slice.h" namespace quic { @@ -14,11 +20,9 @@ namespace quic { // use cases such as turning into QuicMemSliceSpan. class QUIC_EXPORT_PRIVATE QuicMemSliceStorage { public: - QuicMemSliceStorage(const struct iovec* iov, - int iov_count, + QuicMemSliceStorage(const struct iovec* iov, int iov_count, QuicBufferAllocator* allocator, - const QuicByteCount max_slice_len) - : impl_(iov, iov_count, allocator, max_slice_len) {} + const QuicByteCount max_slice_len); QuicMemSliceStorage(const QuicMemSliceStorage& other) = default; QuicMemSliceStorage& operator=(const QuicMemSliceStorage& other) = default; @@ -28,12 +32,10 @@ class QUIC_EXPORT_PRIVATE QuicMemSliceStorage { ~QuicMemSliceStorage() = default; // Return a QuicMemSliceSpan form of the storage. - QuicMemSliceSpan ToSpan() { return impl_.ToSpan(); } - - void Append(QuicMemSlice slice) { impl_.Append(std::move(*slice.impl())); } + absl::Span<QuicMemSlice> ToSpan() { return absl::MakeSpan(storage_); } private: - QuicMemSliceStorageImpl impl_; + std::vector<QuicMemSlice> storage_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage_test.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage_test.cc index 086e218d312..4bef774a0c6 100644 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage_test.cc +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage_test.cc @@ -6,7 +6,6 @@ #include "quic/core/quic_simple_buffer_allocator.h" #include "quic/platform/api/quic_test.h" -#include "quic/platform/api/quic_test_mem_slice_vector.h" namespace quic { namespace test { @@ -28,8 +27,8 @@ TEST_F(QuicMemSliceStorageImplTest, SingleIov) { struct iovec iov = {const_cast<char*>(body.data()), body.length()}; QuicMemSliceStorage storage(&iov, 1, &allocator, 1024); auto span = storage.ToSpan(); - EXPECT_EQ("ccc", span.GetData(0)); - EXPECT_NE(static_cast<const void*>(span.GetData(0).data()), body.data()); + EXPECT_EQ("ccc", span[0].AsStringView()); + EXPECT_NE(static_cast<const void*>(span[0].data()), body.data()); } TEST_F(QuicMemSliceStorageImplTest, MultipleIovInSingleSlice) { @@ -41,7 +40,7 @@ TEST_F(QuicMemSliceStorageImplTest, MultipleIovInSingleSlice) { QuicMemSliceStorage storage(iov, 2, &allocator, 1024); auto span = storage.ToSpan(); - EXPECT_EQ("aaabbbb", span.GetData(0)); + EXPECT_EQ("aaabbbb", span[0].AsStringView()); } TEST_F(QuicMemSliceStorageImplTest, MultipleIovInMultipleSlice) { @@ -53,26 +52,8 @@ TEST_F(QuicMemSliceStorageImplTest, MultipleIovInMultipleSlice) { QuicMemSliceStorage storage(iov, 2, &allocator, 4); auto span = storage.ToSpan(); - EXPECT_EQ("aaaa", span.GetData(0)); - EXPECT_EQ("bbbb", span.GetData(1)); -} - -TEST_F(QuicMemSliceStorageImplTest, AppendMemSlices) { - std::string body1(3, 'a'); - std::string body2(4, 'b'); - std::vector<std::pair<char*, size_t>> buffers; - buffers.push_back( - std::make_pair(const_cast<char*>(body1.data()), body1.length())); - buffers.push_back( - std::make_pair(const_cast<char*>(body2.data()), body2.length())); - QuicTestMemSliceVector mem_slices(buffers); - - QuicMemSliceStorage storage(nullptr, 0, nullptr, 0); - mem_slices.span().ConsumeAll( - [&storage](QuicMemSlice slice) { storage.Append(std::move(slice)); }); - - EXPECT_EQ("aaa", storage.ToSpan().GetData(0)); - EXPECT_EQ("bbbb", storage.ToSpan().GetData(1)); + EXPECT_EQ("aaaa", span[0].AsStringView()); + EXPECT_EQ("bbbb", span[1].AsStringView()); } } // namespace diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_socket_address.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_socket_address.cc index 4c60c1e6da1..f0b667cae53 100644 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_socket_address.cc +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_socket_address.cc @@ -15,6 +15,23 @@ namespace quic { +namespace { + +uint32_t HashIP(const QuicIpAddress& ip) { + if (ip.IsIPv4()) { + return ip.GetIPv4().s_addr; + } + if (ip.IsIPv6()) { + auto v6addr = ip.GetIPv6(); + const uint32_t* v6_as_ints = + reinterpret_cast<const uint32_t*>(&v6addr.s6_addr); + return v6_as_ints[0] ^ v6_as_ints[1] ^ v6_as_ints[2] ^ v6_as_ints[3]; + } + return 0; +} + +} // namespace + QuicSocketAddress::QuicSocketAddress(QuicIpAddress address, uint16_t port) : host_(address), port_(port) {} @@ -131,4 +148,11 @@ sockaddr_storage QuicSocketAddress::generic_address() const { return result.storage; } +uint32_t QuicSocketAddress::Hash() const { + uint32_t value = 0; + value ^= HashIP(host_); + value ^= port_ | (port_ << 16); + return value; +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_socket_address.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_socket_address.h index 0831985e6a4..df6b9b7cf83 100644 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_socket_address.h +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_socket_address.h @@ -37,6 +37,9 @@ class QUIC_EXPORT_PRIVATE QuicSocketAddress { uint16_t port() const; sockaddr_storage generic_address() const; + // Hashes this address to an uint32_t. + uint32_t Hash() const; + private: QuicIpAddress host_; uint16_t port_ = 0; @@ -48,6 +51,13 @@ inline std::ostream& operator<<(std::ostream& os, return os; } +class QUIC_EXPORT_PRIVATE QuicSocketAddressHash { + public: + size_t operator()(QuicSocketAddress const& address) const noexcept { + return address.Hash(); + } +}; + } // namespace quic #endif // QUICHE_QUIC_PLATFORM_API_QUIC_SOCKET_ADDRESS_H_ diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_test.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_test.h index 58896f76d5e..644c25501b5 100644 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_test.h +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_test.h @@ -7,6 +7,7 @@ #include "quic/platform/api/quic_logging.h" #include "net/quic/platform/impl/quic_test_impl.h" +#include "common/platform/api/quiche_test.h" using QuicFlagSaver = QuicFlagSaverImpl; diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h deleted file mode 100644 index 17d5853523c..00000000000 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 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_PLATFORM_API_QUIC_TEST_MEM_SLICE_VECTOR_H_ -#define QUICHE_QUIC_PLATFORM_API_QUIC_TEST_MEM_SLICE_VECTOR_H_ - -#include <utility> - -#include "quic/platform/api/quic_mem_slice_span.h" -#include "net/quic/platform/impl/quic_test_mem_slice_vector_impl.h" - -namespace quic { -namespace test { -// QuicTestMemSliceVector is a test only class which creates a vector of -// platform-specific data structure (used as QuicMemSlice) from an array of data -// buffers. QuicTestMemSliceVector does not own the underlying data buffer. -// Tests using QuicTestMemSliceVector need to make sure the actual data buffers -// outlive QuicTestMemSliceVector, and QuicTestMemSliceVector outlive the -// returned QuicMemSliceSpan. -class QUIC_NO_EXPORT QuicTestMemSliceVector { - public: - explicit QuicTestMemSliceVector(std::vector<std::pair<char*, size_t>> buffers) - : impl_(std::move(buffers)) {} - - QuicMemSliceSpan span() { return QuicMemSliceSpan(impl_.span()); } - - private: - QuicTestMemSliceVectorImpl impl_; -}; - -} // namespace test -} // namespace quic - -#endif // QUICHE_QUIC_PLATFORM_API_QUIC_TEST_MEM_SLICE_VECTOR_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device.cc b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device.cc index 65197cdd4d9..3768a439bec 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device.cc @@ -24,26 +24,25 @@ namespace quic { const int kInvalidFd = -1; -TunDevice::TunDevice(const std::string& interface_name, - int mtu, - bool persist, - bool setup_tun, - KernelInterface* kernel) +TunTapDevice::TunTapDevice(const std::string& interface_name, int mtu, + bool persist, bool setup_tun, bool is_tap, + KernelInterface* kernel) : interface_name_(interface_name), mtu_(mtu), persist_(persist), setup_tun_(setup_tun), + is_tap_(is_tap), file_descriptor_(kInvalidFd), kernel_(*kernel) {} -TunDevice::~TunDevice() { +TunTapDevice::~TunTapDevice() { if (!persist_) { Down(); } CloseDevice(); } -bool TunDevice::Init() { +bool TunTapDevice::Init() { if (interface_name_.empty() || interface_name_.size() >= IFNAMSIZ) { QUIC_BUG(quic_bug_10995_1) << "interface_name must be nonempty and shorter than " << IFNAMSIZ; @@ -63,7 +62,7 @@ bool TunDevice::Init() { // TODO(pengg): might be better to use netlink socket, once we have a library to // use -bool TunDevice::Up() { +bool TunTapDevice::Up() { if (!setup_tun_) { return true; } @@ -79,7 +78,7 @@ bool TunDevice::Up() { // TODO(pengg): might be better to use netlink socket, once we have a library to // use -bool TunDevice::Down() { +bool TunTapDevice::Down() { if (!setup_tun_) { return true; } @@ -93,11 +92,9 @@ bool TunDevice::Down() { return NetdeviceIoctl(SIOCSIFFLAGS, reinterpret_cast<void*>(&if_request)); } -int TunDevice::GetFileDescriptor() const { - return file_descriptor_; -} +int TunTapDevice::GetFileDescriptor() const { return file_descriptor_; } -bool TunDevice::OpenDevice() { +bool TunTapDevice::OpenDevice() { if (file_descriptor_ != kInvalidFd) { CloseDevice(); } @@ -113,7 +110,12 @@ bool TunDevice::OpenDevice() { // destroy the device and create a new one, but that deletes any existing // routing associated with the interface, which makes the meaning of the // 'persist' bit ambiguous. - if_request.ifr_flags = IFF_TUN | IFF_MULTI_QUEUE | IFF_NO_PI; + if_request.ifr_flags = IFF_MULTI_QUEUE | IFF_NO_PI; + if (is_tap_) { + if_request.ifr_flags |= IFF_TAP; + } else { + if_request.ifr_flags |= IFF_TUN; + } // When the device is running with IFF_MULTI_QUEUE set, each call to open will // create a queue which can be used to read/write packets from/to the device. @@ -154,7 +156,7 @@ bool TunDevice::OpenDevice() { // TODO(pengg): might be better to use netlink socket, once we have a library to // use -bool TunDevice::ConfigureInterface() { +bool TunTapDevice::ConfigureInterface() { if (!setup_tun_) { return true; } @@ -174,7 +176,7 @@ bool TunDevice::ConfigureInterface() { return true; } -bool TunDevice::CheckFeatures(int tun_device_fd) { +bool TunTapDevice::CheckFeatures(int tun_device_fd) { unsigned int actual_features; if (kernel_.ioctl(tun_device_fd, TUNGETFEATURES, &actual_features) != 0) { QUIC_PLOG(WARNING) << "Failed to TUNGETFEATURES"; @@ -191,7 +193,7 @@ bool TunDevice::CheckFeatures(int tun_device_fd) { return true; } -bool TunDevice::NetdeviceIoctl(int request, void* argp) { +bool TunTapDevice::NetdeviceIoctl(int request, void* argp) { int fd = kernel_.socket(AF_INET6, SOCK_DGRAM, 0); if (fd < 0) { QUIC_PLOG(WARNING) << "Failed to create AF_INET6 socket."; @@ -207,7 +209,7 @@ bool TunDevice::NetdeviceIoctl(int request, void* argp) { return true; } -void TunDevice::CloseDevice() { +void TunTapDevice::CloseDevice() { if (file_descriptor_ != kInvalidFd) { kernel_.close(file_descriptor_); file_descriptor_ = kInvalidFd; diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device.h b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device.h index 129e1d121c6..66c06d204e6 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device.h +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device.h @@ -13,7 +13,7 @@ namespace quic { -class TunDevice : public TunDeviceInterface { +class TunTapDevice : public TunDeviceInterface { public: // This represents a tun device created in the OS kernel, which is a virtual // network interface that any packets sent to it can be read by a user space @@ -32,13 +32,10 @@ class TunDevice : public TunDeviceInterface { // routing rules go away. // // The caller should own kernel and make sure it outlives this. - TunDevice(const std::string& interface_name, - int mtu, - bool persist, - bool setup_tun, - KernelInterface* kernel); + TunTapDevice(const std::string& interface_name, int mtu, bool persist, + bool setup_tun, bool is_tap, KernelInterface* kernel); - ~TunDevice() override; + ~TunTapDevice() override; // Actually creates/reopens and configures the device. bool Init() override; @@ -50,7 +47,7 @@ class TunDevice : public TunDeviceInterface { bool Down() override; // Closes the open file descriptor for the TUN device (if one exists). - // It is safe to reinitialize and reuse this TunDevice after calling + // It is safe to reinitialize and reuse this TunTapDevice after calling // CloseDevice. void CloseDevice() override; @@ -75,6 +72,7 @@ class TunDevice : public TunDeviceInterface { const int mtu_; const bool persist_; const bool setup_tun_; + const bool is_tap_; int file_descriptor_; KernelInterface& kernel_; }; diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_interface.h b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_interface.h index 3c6f9624960..e88efa97186 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_interface.h +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_interface.h @@ -24,7 +24,7 @@ class TunDeviceInterface { virtual bool Down() = 0; // Closes the open file descriptor for the TUN device (if one exists). - // It is safe to reinitialize and reuse this TunDevice after calling + // It is safe to reinitialize and reuse this TunTapDevice after calling // CloseDevice. virtual void CloseDevice() = 0; diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger.cc b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger.cc index 4783e9cc9c3..3381cd2b3f4 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger.cc @@ -4,27 +4,36 @@ #include "quic/qbone/bonnet/tun_device_packet_exchanger.h" +#include <netinet/icmp6.h> +#include <netinet/ip6.h> + #include <utility> #include "absl/strings/str_cat.h" +#include "quic/qbone/platform/icmp_packet.h" +#include "quic/qbone/platform/netlink_interface.h" +#include "quic/qbone/qbone_constants.h" namespace quic { TunDevicePacketExchanger::TunDevicePacketExchanger( - size_t mtu, - KernelInterface* kernel, - QbonePacketExchanger::Visitor* visitor, - size_t max_pending_packets, - StatsInterface* stats) + size_t mtu, KernelInterface* kernel, NetlinkInterface* netlink, + QbonePacketExchanger::Visitor* visitor, size_t max_pending_packets, + bool is_tap, StatsInterface* stats, absl::string_view ifname) : QbonePacketExchanger(visitor, max_pending_packets), mtu_(mtu), kernel_(kernel), - stats_(stats) {} + netlink_(netlink), + ifname_(ifname), + is_tap_(is_tap), + stats_(stats) { + if (is_tap_) { + mtu_ += ETH_HLEN; + } +} -bool TunDevicePacketExchanger::WritePacket(const char* packet, - size_t size, - bool* blocked, - std::string* error) { +bool TunDevicePacketExchanger::WritePacket(const char* packet, size_t size, + bool* blocked, std::string* error) { *blocked = false; if (fd_ < 0) { *error = absl::StrCat("Invalid file descriptor of the TUN device: ", fd_); @@ -32,7 +41,11 @@ bool TunDevicePacketExchanger::WritePacket(const char* packet, return false; } - int result = kernel_->write(fd_, packet, size); + auto buffer = std::make_unique<QuicData>(packet, size); + if (is_tap_) { + buffer = ApplyL2Headers(*buffer); + } + int result = kernel_->write(fd_, buffer->data(), buffer->length()); if (result == -1) { if (errno == EWOULDBLOCK || errno == EAGAIN) { // The tunnel is blocked. Note that this does not mean the receive buffer @@ -50,8 +63,7 @@ bool TunDevicePacketExchanger::WritePacket(const char* packet, } std::unique_ptr<QuicData> TunDevicePacketExchanger::ReadPacket( - bool* blocked, - std::string* error) { + bool* blocked, std::string* error) { *blocked = false; if (fd_ < 0) { *error = absl::StrCat("Invalid file descriptor of the TUN device: ", fd_); @@ -72,17 +84,147 @@ std::unique_ptr<QuicData> TunDevicePacketExchanger::ReadPacket( } return nullptr; } - stats_->OnPacketRead(result); - return std::make_unique<QuicData>(read_buffer.release(), result, true); -} -void TunDevicePacketExchanger::set_file_descriptor(int fd) { - fd_ = fd; + auto buffer = std::make_unique<QuicData>(read_buffer.release(), result, true); + if (is_tap_) { + buffer = ConsumeL2Headers(*buffer); + } + if (buffer) { + stats_->OnPacketRead(buffer->length()); + } + return buffer; } +void TunDevicePacketExchanger::set_file_descriptor(int fd) { fd_ = fd; } + const TunDevicePacketExchanger::StatsInterface* TunDevicePacketExchanger::stats_interface() const { return stats_; } +std::unique_ptr<QuicData> TunDevicePacketExchanger::ApplyL2Headers( + const QuicData& l3_packet) { + if (is_tap_ && !mac_initialized_) { + NetlinkInterface::LinkInfo link_info{}; + if (netlink_->GetLinkInfo(ifname_, &link_info)) { + memcpy(tap_mac_, link_info.hardware_address, ETH_ALEN); + mac_initialized_ = true; + } else { + QUIC_LOG_EVERY_N_SEC(ERROR, 30) + << "Unable to get link info for: " << ifname_; + } + } + + const auto l2_packet_size = l3_packet.length() + ETH_HLEN; + auto l2_buffer = std::make_unique<char[]>(l2_packet_size); + + // Populate the Ethernet header + auto* hdr = reinterpret_cast<ethhdr*>(l2_buffer.get()); + // Set src & dst to my own address + memcpy(hdr->h_dest, tap_mac_, ETH_ALEN); + memcpy(hdr->h_source, tap_mac_, ETH_ALEN); + // Assume ipv6 for now + // TODO(b/195113643): Support additional protocols. + hdr->h_proto = absl::ghtons(ETH_P_IPV6); + + // Copy the l3 packet into buffer, just after the ethernet header. + memcpy(l2_buffer.get() + ETH_HLEN, l3_packet.data(), l3_packet.length()); + + return std::make_unique<QuicData>(l2_buffer.release(), l2_packet_size, true); +} + +std::unique_ptr<QuicData> TunDevicePacketExchanger::ConsumeL2Headers( + const QuicData& l2_packet) { + if (l2_packet.length() < ETH_HLEN) { + // Packet is too short for ethernet headers. Drop it. + return nullptr; + } + auto* hdr = reinterpret_cast<const ethhdr*>(l2_packet.data()); + if (hdr->h_proto != absl::ghtons(ETH_P_IPV6)) { + return nullptr; + } + constexpr auto kIp6PrefixLen = ETH_HLEN + sizeof(ip6_hdr); + constexpr auto kIcmp6PrefixLen = kIp6PrefixLen + sizeof(icmp6_hdr); + if (l2_packet.length() < kIp6PrefixLen) { + // Packet is too short to be ipv6. Drop it. + return nullptr; + } + auto* ip_hdr = reinterpret_cast<const ip6_hdr*>(l2_packet.data() + ETH_HLEN); + const bool is_icmp = ip_hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt == IPPROTO_ICMPV6; + + bool is_neighbor_solicit = false; + if (is_icmp) { + if (l2_packet.length() < kIcmp6PrefixLen) { + // Packet is too short to be icmp6. Drop it. + return nullptr; + } + is_neighbor_solicit = + reinterpret_cast<const icmp6_hdr*>(l2_packet.data() + kIp6PrefixLen) + ->icmp6_type == ND_NEIGHBOR_SOLICIT; + } + + if (is_neighbor_solicit) { + // If we've received a neighbor solicitation, craft an advertisement to + // respond with and write it back to the local interface. + auto* icmp6_payload = l2_packet.data() + kIcmp6PrefixLen; + + QuicIpAddress target_address( + *reinterpret_cast<const in6_addr*>(icmp6_payload)); + if (target_address != *QboneConstants::GatewayAddress()) { + // Only respond to solicitations for our gateway address + return nullptr; + } + + // Neighbor Advertisement crafted per: + // https://datatracker.ietf.org/doc/html/rfc4861#section-4.4 + // + // Using the Target link-layer address option defined at: + // https://datatracker.ietf.org/doc/html/rfc4861#section-4.6.1 + constexpr size_t kIcmpv6OptionSize = 8; + const int payload_size = sizeof(in6_addr) + kIcmpv6OptionSize; + auto payload = std::make_unique<char[]>(payload_size); + // Place the solicited IPv6 address at the beginning of the response payload + memcpy(payload.get(), icmp6_payload, sizeof(in6_addr)); + // Setup the Target link-layer address option: + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Type | Length | Link-Layer Address ... + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + int pos = sizeof(in6_addr); + payload[pos++] = ND_OPT_TARGET_LINKADDR; // Type + payload[pos++] = 1; // Length in units of 8 octets + memcpy(&payload[pos], tap_mac_, ETH_ALEN); // This interfaces' MAC address + + // Populate the ICMPv6 header + icmp6_hdr response_hdr{}; + response_hdr.icmp6_type = ND_NEIGHBOR_ADVERT; + // Set the solicited bit to true + response_hdr.icmp6_dataun.icmp6_un_data8[0] = 64; + // Craft the full ICMPv6 packet and then ship it off to WritePacket + // to have it frame it with L2 headers and send it back to the requesting + // neighbor. + CreateIcmpPacket(ip_hdr->ip6_src, ip_hdr->ip6_src, response_hdr, + absl::string_view(payload.get(), payload_size), + [this](absl::string_view packet) { + bool blocked; + std::string error; + WritePacket(packet.data(), packet.size(), &blocked, + &error); + }); + // Do not forward the neighbor solicitation through the tunnel since it's + // link-local. + return nullptr; + } + + // If this isn't a Neighbor Solicitation, remove the L2 headers and forward + // it as though it were an L3 packet. + const auto l3_packet_size = l2_packet.length() - ETH_HLEN; + auto shift_buffer = std::make_unique<char[]>(l3_packet_size); + memcpy(shift_buffer.get(), l2_packet.data() + ETH_HLEN, l3_packet_size); + + return std::make_unique<QuicData>(shift_buffer.release(), l3_packet_size, + true); +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger.h b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger.h index 115f5b580c6..3136b42862a 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger.h +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger.h @@ -5,8 +5,11 @@ #ifndef QUICHE_QUIC_QBONE_BONNET_TUN_DEVICE_PACKET_EXCHANGER_H_ #define QUICHE_QUIC_QBONE_BONNET_TUN_DEVICE_PACKET_EXCHANGER_H_ +#include <linux/if_ether.h> + #include "quic/core/quic_packets.h" #include "quic/qbone/platform/kernel_interface.h" +#include "quic/qbone/platform/netlink_interface.h" #include "quic/qbone/qbone_client_interface.h" #include "quic/qbone/qbone_packet_exchanger.h" @@ -42,11 +45,11 @@ class TunDevicePacketExchanger : public QbonePacketExchanger { // the TUN device become blocked. // |stats| is notified about packet read/write statistics. It is not owned, // but should outlive objects of this class. - TunDevicePacketExchanger(size_t mtu, - KernelInterface* kernel, + TunDevicePacketExchanger(size_t mtu, KernelInterface* kernel, + NetlinkInterface* netlink, QbonePacketExchanger::Visitor* visitor, - size_t max_pending_packets, - StatsInterface* stats); + size_t max_pending_packets, bool is_tap, + StatsInterface* stats, absl::string_view ifname); void set_file_descriptor(int fd); @@ -63,9 +66,19 @@ class TunDevicePacketExchanger : public QbonePacketExchanger { bool* blocked, std::string* error) override; + std::unique_ptr<QuicData> ApplyL2Headers(const QuicData& l3_packet); + + std::unique_ptr<QuicData> ConsumeL2Headers(const QuicData& l2_packet); + int fd_ = -1; size_t mtu_; KernelInterface* kernel_; + NetlinkInterface* netlink_; + const std::string ifname_; + + const bool is_tap_; + uint8_t tap_mac_[ETH_ALEN]{}; + bool mac_initialized_ = false; StatsInterface* stats_; }; diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger_test.cc index c8f3ff0c861..8abf9a35fe6 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger_test.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_packet_exchanger_test.cc @@ -30,11 +30,9 @@ class MockVisitor : public QbonePacketExchanger::Visitor { class TunDevicePacketExchangerTest : public QuicTest { protected: TunDevicePacketExchangerTest() - : exchanger_(kMtu, - &mock_kernel_, - &mock_visitor_, - kMaxPendingPackets, - &mock_stats_) { + : exchanger_(kMtu, &mock_kernel_, nullptr, &mock_visitor_, + kMaxPendingPackets, false, &mock_stats_, + absl::string_view()) { exchanger_.set_file_descriptor(kFd); } diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_test.cc index 6a02f07062e..18b818ccd51 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_test.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/tun_device_test.cc @@ -120,10 +120,10 @@ class TunDeviceTest : public QuicTest { int next_fd_ = 100; }; -// A TunDevice can be initialized and up +// A TunTapDevice can be initialized and up TEST_F(TunDeviceTest, BasicWorkFlow) { SetInitExpectations(/* mtu = */ 1500, /* persist = */ false); - TunDevice tun_device(kDeviceName, 1500, false, true, &mock_kernel_); + TunTapDevice tun_device(kDeviceName, 1500, false, true, false, &mock_kernel_); EXPECT_TRUE(tun_device.Init()); EXPECT_GT(tun_device.GetFileDescriptor(), -1); @@ -136,7 +136,7 @@ TEST_F(TunDeviceTest, FailToOpenTunDevice) { SetInitExpectations(/* mtu = */ 1500, /* persist = */ false); EXPECT_CALL(mock_kernel_, open(StrEq("/dev/net/tun"), _)) .WillOnce(Return(-1)); - TunDevice tun_device(kDeviceName, 1500, false, true, &mock_kernel_); + TunTapDevice tun_device(kDeviceName, 1500, false, true, false, &mock_kernel_); EXPECT_FALSE(tun_device.Init()); EXPECT_EQ(tun_device.GetFileDescriptor(), -1); ExpectDown(false); @@ -145,7 +145,7 @@ TEST_F(TunDeviceTest, FailToOpenTunDevice) { TEST_F(TunDeviceTest, FailToCheckFeature) { SetInitExpectations(/* mtu = */ 1500, /* persist = */ false); EXPECT_CALL(mock_kernel_, ioctl(_, TUNGETFEATURES, _)).WillOnce(Return(-1)); - TunDevice tun_device(kDeviceName, 1500, false, true, &mock_kernel_); + TunTapDevice tun_device(kDeviceName, 1500, false, true, false, &mock_kernel_); EXPECT_FALSE(tun_device.Init()); EXPECT_EQ(tun_device.GetFileDescriptor(), -1); ExpectDown(false); @@ -159,7 +159,7 @@ TEST_F(TunDeviceTest, TooFewFeature) { *actual_features = IFF_TUN | IFF_ONE_QUEUE; return 0; })); - TunDevice tun_device(kDeviceName, 1500, false, true, &mock_kernel_); + TunTapDevice tun_device(kDeviceName, 1500, false, true, false, &mock_kernel_); EXPECT_FALSE(tun_device.Init()); EXPECT_EQ(tun_device.GetFileDescriptor(), -1); ExpectDown(false); @@ -168,7 +168,7 @@ TEST_F(TunDeviceTest, TooFewFeature) { TEST_F(TunDeviceTest, FailToSetFlag) { SetInitExpectations(/* mtu = */ 1500, /* persist = */ true); EXPECT_CALL(mock_kernel_, ioctl(_, TUNSETIFF, _)).WillOnce(Return(-1)); - TunDevice tun_device(kDeviceName, 1500, true, true, &mock_kernel_); + TunTapDevice tun_device(kDeviceName, 1500, true, true, false, &mock_kernel_); EXPECT_FALSE(tun_device.Init()); EXPECT_EQ(tun_device.GetFileDescriptor(), -1); } @@ -176,7 +176,7 @@ TEST_F(TunDeviceTest, FailToSetFlag) { TEST_F(TunDeviceTest, FailToPersistDevice) { SetInitExpectations(/* mtu = */ 1500, /* persist = */ true); EXPECT_CALL(mock_kernel_, ioctl(_, TUNSETPERSIST, _)).WillOnce(Return(-1)); - TunDevice tun_device(kDeviceName, 1500, true, true, &mock_kernel_); + TunTapDevice tun_device(kDeviceName, 1500, true, true, false, &mock_kernel_); EXPECT_FALSE(tun_device.Init()); EXPECT_EQ(tun_device.GetFileDescriptor(), -1); } @@ -184,7 +184,7 @@ TEST_F(TunDeviceTest, FailToPersistDevice) { TEST_F(TunDeviceTest, FailToOpenSocket) { SetInitExpectations(/* mtu = */ 1500, /* persist = */ true); EXPECT_CALL(mock_kernel_, socket(AF_INET6, _, _)).WillOnce(Return(-1)); - TunDevice tun_device(kDeviceName, 1500, true, true, &mock_kernel_); + TunTapDevice tun_device(kDeviceName, 1500, true, true, false, &mock_kernel_); EXPECT_FALSE(tun_device.Init()); EXPECT_EQ(tun_device.GetFileDescriptor(), -1); } @@ -192,14 +192,14 @@ TEST_F(TunDeviceTest, FailToOpenSocket) { TEST_F(TunDeviceTest, FailToSetMtu) { SetInitExpectations(/* mtu = */ 1500, /* persist = */ true); EXPECT_CALL(mock_kernel_, ioctl(_, SIOCSIFMTU, _)).WillOnce(Return(-1)); - TunDevice tun_device(kDeviceName, 1500, true, true, &mock_kernel_); + TunTapDevice tun_device(kDeviceName, 1500, true, true, false, &mock_kernel_); EXPECT_FALSE(tun_device.Init()); EXPECT_EQ(tun_device.GetFileDescriptor(), -1); } TEST_F(TunDeviceTest, FailToUp) { SetInitExpectations(/* mtu = */ 1500, /* persist = */ true); - TunDevice tun_device(kDeviceName, 1500, true, true, &mock_kernel_); + TunTapDevice tun_device(kDeviceName, 1500, true, true, false, &mock_kernel_); EXPECT_TRUE(tun_device.Init()); EXPECT_GT(tun_device.GetFileDescriptor(), -1); diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet.cc index 50e3f2b6d78..0acd71216bf 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet.cc @@ -17,7 +17,10 @@ constexpr size_t kIPv6AddressSize = sizeof(in6_addr); constexpr size_t kIPv6HeaderSize = sizeof(ip6_hdr); constexpr size_t kICMPv6HeaderSize = sizeof(icmp6_hdr); constexpr size_t kIPv6MinPacketSize = 1280; -constexpr size_t kIcmpTtl = 64; + +// Hop limit set to 255 to satisfy: +// https://datatracker.ietf.org/doc/html/rfc4861#section-11.2 +constexpr size_t kIcmpTtl = 255; constexpr size_t kICMPv6BodyMaxSize = kIPv6MinPacketSize - kIPv6HeaderSize - kICMPv6HeaderSize; diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet_test.cc index 9721e36aa40..1e6a2e92a77 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet_test.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/icmp_packet_test.cc @@ -37,8 +37,8 @@ constexpr uint8_t kReferenceICMPPacket[] = { 0x00, 0x40, // Next header is 58 0x3a, - // Hop limit is 64 - 0x40, + // Hop limit is 255 + 0xFF, // Source address of fe80:1:2:3:4::1 0xfe, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink.cc index 985632ab72c..2f4bbe27124 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink.cc @@ -5,6 +5,7 @@ #include "quic/qbone/platform/netlink.h" #include <linux/fib_rules.h> + #include <utility> #include "absl/base/attributes.h" @@ -13,6 +14,7 @@ #include "quic/platform/api/quic_ip_address.h" #include "quic/platform/api/quic_logging.h" #include "quic/qbone/platform/rtnetlink_message.h" +#include "quic/qbone/qbone_constants.h" namespace quic { @@ -570,10 +572,17 @@ bool Netlink::ChangeRoute(Netlink::Verb verb, // This is the source address to use in the IP packet should this routing rule // is used. if (preferred_source.IsInitialized()) { + auto src_str = preferred_source.ToPackedString(); message.AppendAttribute(RTA_PREFSRC, - reinterpret_cast<const void*>( - preferred_source.ToPackedString().c_str()), - preferred_source.ToPackedString().size()); + reinterpret_cast<const void*>(src_str.c_str()), + src_str.size()); + } + + if (verb != Verb::kRemove) { + auto gateway_str = QboneConstants::GatewayAddress()->ToPackedString(); + message.AppendAttribute(RTA_GATEWAY, + reinterpret_cast<const void*>(gateway_str.c_str()), + gateway_str.size()); } if (!Send(message.BuildIoVec().get(), message.IoVecSize())) { diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink_test.cc index 16d2a19dc60..f6fe7cda9df 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink_test.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink_test.cc @@ -564,6 +564,15 @@ TEST_F(NetlinkTest, ChangeRouteAdd) { EXPECT_EQ(preferred_ip, address); break; } + case RTA_GATEWAY: { + const auto* raw_address = + reinterpret_cast<const char*>(RTA_DATA(rta)); + ASSERT_EQ(sizeof(struct in6_addr), RTA_PAYLOAD(rta)); + QuicIpAddress address; + address.FromPackedString(raw_address, RTA_PAYLOAD(rta)); + EXPECT_EQ(*QboneConstants::GatewayAddress(), address); + break; + } case RTA_OIF: { ASSERT_EQ(sizeof(int), RTA_PAYLOAD(rta)); const auto* interface_index = @@ -591,7 +600,7 @@ TEST_F(NetlinkTest, ChangeRouteAdd) { } ++num_rta; } - EXPECT_EQ(4, num_rta); + EXPECT_EQ(5, num_rta); }); EXPECT_TRUE(netlink->ChangeRoute( Netlink::Verb::kAdd, QboneConstants::kQboneRouteTableId, subnet, @@ -728,6 +737,15 @@ TEST_F(NetlinkTest, ChangeRouteReplace) { EXPECT_EQ(preferred_ip, address); break; } + case RTA_GATEWAY: { + const auto* raw_address = + reinterpret_cast<const char*>(RTA_DATA(rta)); + ASSERT_EQ(sizeof(struct in6_addr), RTA_PAYLOAD(rta)); + QuicIpAddress address; + address.FromPackedString(raw_address, RTA_PAYLOAD(rta)); + EXPECT_EQ(*QboneConstants::GatewayAddress(), address); + break; + } case RTA_OIF: { ASSERT_EQ(sizeof(int), RTA_PAYLOAD(rta)); const auto* interface_index = @@ -755,7 +773,7 @@ TEST_F(NetlinkTest, ChangeRouteReplace) { } ++num_rta; } - EXPECT_EQ(4, num_rta); + EXPECT_EQ(5, num_rta); }); EXPECT_TRUE(netlink->ChangeRoute( Netlink::Verb::kReplace, QboneConstants::kQboneRouteTableId, subnet, diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_constants.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_constants.cc index fb92af7319f..f54ebc3edef 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_constants.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_constants.cc @@ -19,7 +19,7 @@ QuicStreamId QboneConstants::GetControlStreamId(QuicTransportVersion version) { const QuicIpAddress* QboneConstants::TerminatorLocalAddress() { static auto* terminator_address = []() { - QuicIpAddress* address = new QuicIpAddress; + auto* address = new QuicIpAddress; // 0x71 0x62 0x6f 0x6e 0x65 is 'qbone' in ascii. address->FromString("fe80::71:626f:6e65"); return address; @@ -33,4 +33,13 @@ const IpRange* QboneConstants::TerminatorLocalAddressRange() { return range; } +const QuicIpAddress* QboneConstants::GatewayAddress() { + static auto* gateway_address = []() { + auto* address = new QuicIpAddress; + address->FromString("fe80::1"); + return address; + }(); + return gateway_address; +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_constants.h b/chromium/net/third_party/quiche/src/quic/qbone/qbone_constants.h index 8053013abf9..141bb6c00d2 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_constants.h +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_constants.h @@ -25,6 +25,9 @@ struct QboneConstants { static const QuicIpAddress* TerminatorLocalAddress(); // The IPRange containing the TerminatorLocalAddress static const IpRange* TerminatorLocalAddressRange(); + // The gateway address to provide when configuring routes to the QBONE + // interface + static const QuicIpAddress* GatewayAddress(); }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_test.cc index 9887f79a46e..c3ae3be1fd1 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_test.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_test.cc @@ -79,9 +79,9 @@ class IndirectionProofSource : public ProofSource { absl::string_view chlo_hash, std::unique_ptr<Callback> callback) override { if (!proof_source_) { - QuicReferenceCountedPointer<ProofSource::Chain> chain = - GetCertChain(server_address, client_address, hostname); QuicCryptoProof proof; + QuicReferenceCountedPointer<ProofSource::Chain> chain = GetCertChain( + server_address, client_address, hostname, &proof.cert_matched_sni); callback->Run(/*ok=*/false, chain, proof, /*details=*/nullptr); return; } @@ -92,13 +92,13 @@ class IndirectionProofSource : public ProofSource { QuicReferenceCountedPointer<Chain> GetCertChain( const QuicSocketAddress& server_address, - const QuicSocketAddress& client_address, - const std::string& hostname) override { + const QuicSocketAddress& client_address, const std::string& hostname, + bool* cert_matched_sni) override { if (!proof_source_) { return QuicReferenceCountedPointer<Chain>(); } - return proof_source_->GetCertChain(server_address, client_address, - hostname); + return proof_source_->GetCertChain(server_address, client_address, hostname, + cert_matched_sni); } void ComputeTlsSignature( 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 fc2ce819d90..8e724a4195a 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 @@ -31,11 +31,8 @@ using ::testing::StrictMock; class MockQuicSession : public QboneSessionBase { public: MockQuicSession(QuicConnection* connection, const QuicConfig& config) - : QboneSessionBase(connection, - nullptr /*visitor*/, - config, - CurrentSupportedVersions(), - nullptr /*writer*/) {} + : QboneSessionBase(connection, nullptr /*visitor*/, config, + CurrentSupportedVersions(), nullptr /*writer*/) {} ~MockQuicSession() override {} @@ -56,16 +53,12 @@ class MockQuicSession : public QboneSessionBase { } // Called by QuicStream when they want to close stream. - MOCK_METHOD(void, - MaybeSendRstStreamFrame, - (QuicStreamId stream_id, - QuicRstStreamErrorCode error, + MOCK_METHOD(void, MaybeSendRstStreamFrame, + (QuicStreamId stream_id, QuicResetStreamError error, QuicStreamOffset bytes_written), (override)); - MOCK_METHOD(void, - MaybeSendStopSendingFrame, - (QuicStreamId stream_id, QuicRstStreamErrorCode error), - (override)); + MOCK_METHOD(void, MaybeSendStopSendingFrame, + (QuicStreamId stream_id, QuicResetStreamError error), (override)); // Sets whether data is written to buffer, or else if this is write blocked. void set_writable(bool writable) { writable_ = writable; } @@ -104,8 +97,7 @@ class DummyPacketWriter : public QuicPacketWriter { DummyPacketWriter() {} // QuicPacketWriter overrides. - WriteResult WritePacket(const char* buffer, - size_t buf_len, + WriteResult WritePacket(const char* buffer, size_t buf_len, const QuicIpAddress& self_address, const QuicSocketAddress& peer_address, PerPacketOptions* options) override { @@ -181,8 +173,7 @@ class QboneReadOnlyStreamTest : public ::testing::Test, SimpleBufferAllocator buffer_allocator_; MockClock clock_; const QuicStreamId kStreamId = QuicUtils::GetFirstUnidirectionalStreamId( - CurrentSupportedVersions()[0].transport_version, - Perspective::IS_CLIENT); + CurrentSupportedVersions()[0].transport_version, Perspective::IS_CLIENT); }; // Read an entire string. @@ -240,9 +231,13 @@ TEST_F(QboneReadOnlyStreamTest, ReadBufferedTooLarge) { std::string packet = "0123456789"; int iterations = (QboneConstants::kMaxQbonePacketBytes / packet.size()) + 2; EXPECT_CALL(*session_, MaybeSendStopSendingFrame( - kStreamId, QUIC_BAD_APPLICATION_PAYLOAD)); - EXPECT_CALL(*session_, MaybeSendRstStreamFrame( - kStreamId, QUIC_BAD_APPLICATION_PAYLOAD, _)); + kStreamId, QuicResetStreamError::FromInternal( + QUIC_BAD_APPLICATION_PAYLOAD))); + EXPECT_CALL( + *session_, + MaybeSendRstStreamFrame( + kStreamId, + QuicResetStreamError::FromInternal(QUIC_BAD_APPLICATION_PAYLOAD), _)); for (int i = 0; i < iterations; ++i) { QuicStreamFrame frame(kStreamId, i == (iterations - 1), i * packet.size(), packet); diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.cc index 17f9194abfe..1a2160b0e9d 100644 --- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.cc +++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.cc @@ -242,7 +242,7 @@ void QuicTransportClientSession::SendClientIndication() { } ready_ = true; - visitor_->OnSessionReady(); + visitor_->OnSessionReady(spdy::SpdyHeaderBlock()); } void QuicTransportClientSession::OnMessageReceived(absl::string_view message) { diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.h b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.h index ad29a150b58..87854575bb0 100644 --- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.h +++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.h @@ -17,6 +17,7 @@ #include "quic/core/quic_crypto_client_stream.h" #include "quic/core/quic_crypto_stream.h" #include "quic/core/quic_datagram_queue.h" +#include "quic/core/quic_error_codes.h" #include "quic/core/quic_server_id.h" #include "quic/core/quic_session.h" #include "quic/core/quic_stream.h" @@ -113,6 +114,17 @@ class QUIC_EXPORT_PRIVATE QuicTransportClientSession void OnProofVerifyDetailsAvailable( const ProofVerifyDetails& verify_details) override; + void CloseSession(WebTransportSessionError /*error_code*/, + absl::string_view error_message) override { + connection()->CloseConnection( + QUIC_NO_ERROR, std::string(error_message), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + } + + QuicByteCount GetMaxDatagramSize() const override { + return GetGuaranteedLargestMessagePayload(); + } + protected: class QUIC_EXPORT_PRIVATE ClientIndication : public QuicStream { public: diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session_test.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session_test.cc index 8e6a0e27882..90c449cb746 100644 --- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session_test.cc +++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session_test.cc @@ -104,7 +104,7 @@ TEST_F(QuicTransportClientSessionTest, SuccessfulConnection) { "\0\x01" // length "/"; // value - EXPECT_CALL(visitor_, OnSessionReady()); + EXPECT_CALL(visitor_, OnSessionReady(_)); Connect(); EXPECT_TRUE(session_->IsSessionReady()); diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_integration_test.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_integration_test.cc index 36607b3918e..7bb8479af31 100644 --- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_integration_test.cc +++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_integration_test.cc @@ -212,7 +212,7 @@ class QuicTransportIntegrationTest : public QuicTest { TEST_F(QuicTransportIntegrationTest, SuccessfulHandshake) { CreateDefaultEndpoints("/discard"); WireUpEndpoints(); - EXPECT_CALL(*client_->visitor(), OnSessionReady()); + EXPECT_CALL(*client_->visitor(), OnSessionReady(_)); RunHandshake(); EXPECT_TRUE(client_->session()->IsSessionReady()); EXPECT_TRUE(server_->session()->IsSessionReady()); diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h index 87ef5fb3b83..5d574afe3c4 100644 --- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h +++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h @@ -10,11 +10,11 @@ #include "absl/base/attributes.h" #include "absl/strings/string_view.h" +#include "quic/core/http/web_transport_stream_adapter.h" #include "quic/core/quic_session.h" #include "quic/core/quic_stream.h" #include "quic/core/quic_types.h" #include "quic/core/web_transport_interface.h" -#include "quic/core/web_transport_stream_adapter.h" #include "quic/quic_transport/quic_transport_session_interface.h" namespace quic { @@ -50,12 +50,15 @@ class QUIC_EXPORT_PRIVATE QuicTransportStream : public QuicStream, QuicStreamId GetStreamId() const override { return id(); } - void ResetWithUserCode(QuicRstStreamErrorCode error) override { - adapter_.ResetWithUserCode(error); + void ResetWithUserCode(WebTransportStreamError /*error*/) override { + adapter_.ResetWithUserCode(0); } void ResetDueToInternalError() override { adapter_.ResetDueToInternalError(); } + void SendStopSending(WebTransportStreamError /*error*/) override { + adapter_.SendStopSending(0); + } void MaybeResetDueToStreamObjectGone() override { adapter_.MaybeResetDueToStreamObjectGone(); } 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 d28b4faff85..febd8d30b32 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 @@ -634,22 +634,6 @@ void CompareClientAndServerKeys(QuicCryptoClientStream* client, "subkey secret", client_subkey_secret.data(), client_subkey_secret.length(), server_subkey_secret.data(), server_subkey_secret.length()); - - const char kSampleLabel[] = "label"; - const char kSampleContext[] = "context"; - const size_t kSampleOutputLength = 32; - std::string client_key_extraction; - std::string server_key_extraction; - EXPECT_TRUE(client->ExportKeyingMaterial(kSampleLabel, kSampleContext, - kSampleOutputLength, - &client_key_extraction)); - EXPECT_TRUE(server->ExportKeyingMaterial(kSampleLabel, kSampleContext, - kSampleOutputLength, - &server_key_extraction)); - quiche::test::CompareCharArraysWithHexError( - "sample key extraction", client_key_extraction.data(), - client_key_extraction.length(), server_key_extraction.data(), - server_key_extraction.length()); } QuicTag ParseTag(const char* tagstr) { diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.cc b/chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.cc index fad128d9a39..1f17c8a1f35 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.cc @@ -22,7 +22,9 @@ void FailingProofSource::GetProof(const QuicSocketAddress& /*server_address*/, QuicReferenceCountedPointer<ProofSource::Chain> FailingProofSource::GetCertChain(const QuicSocketAddress& /*server_address*/, const QuicSocketAddress& /*client_address*/, - const std::string& /*hostname*/) { + const std::string& /*hostname*/, + bool* cert_matched_sni) { + *cert_matched_sni = false; return QuicReferenceCountedPointer<Chain>(); } diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.h b/chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.h index 447b77066bd..db5a19714cb 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.h @@ -23,8 +23,8 @@ class FailingProofSource : public ProofSource { QuicReferenceCountedPointer<Chain> GetCertChain( const QuicSocketAddress& server_address, - const QuicSocketAddress& client_address, - const std::string& hostname) override; + const QuicSocketAddress& client_address, const std::string& hostname, + bool* cert_matched_sni) override; void ComputeTlsSignature( const QuicSocketAddress& server_address, diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.cc b/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.cc index 1109d659fac..0f7cb194df6 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.cc @@ -96,9 +96,10 @@ void FakeProofSource::GetProof( QuicReferenceCountedPointer<ProofSource::Chain> FakeProofSource::GetCertChain( const QuicSocketAddress& server_address, - const QuicSocketAddress& client_address, - const std::string& hostname) { - return delegate_->GetCertChain(server_address, client_address, hostname); + const QuicSocketAddress& client_address, const std::string& hostname, + bool* cert_matched_sni) { + return delegate_->GetCertChain(server_address, client_address, hostname, + cert_matched_sni); } void FakeProofSource::ComputeTlsSignature( diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.h b/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.h index c088d43a9bb..b135129c08c 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.h @@ -43,8 +43,8 @@ class FakeProofSource : public ProofSource { std::unique_ptr<ProofSource::Callback> callback) override; QuicReferenceCountedPointer<Chain> GetCertChain( const QuicSocketAddress& server_address, - const QuicSocketAddress& client_address, - const std::string& hostname) override; + const QuicSocketAddress& client_address, const std::string& hostname, + bool* cert_matched_sni) override; void ComputeTlsSignature( const QuicSocketAddress& server_address, const QuicSocketAddress& client_address, diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source_handle.cc b/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source_handle.cc index f34247e9bae..8c1e7f870f1 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source_handle.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source_handle.cc @@ -94,19 +94,23 @@ QuicAsyncStatus FakeProofSourceHandle::SelectCertificate( callback()->OnSelectCertificateDone( /*ok=*/false, /*is_sync=*/true, nullptr, /*handshake_hints=*/absl::string_view(), - /*ticket_encryption_key=*/absl::string_view()); + /*ticket_encryption_key=*/absl::string_view(), + /*cert_matched_sni=*/false); return QUIC_FAILURE; } QUICHE_DCHECK(select_cert_action_ == Action::DELEGATE_SYNC); + bool cert_matched_sni; QuicReferenceCountedPointer<ProofSource::Chain> chain = - delegate_->GetCertChain(server_address, client_address, hostname); + delegate_->GetCertChain(server_address, client_address, hostname, + &cert_matched_sni); bool ok = chain && !chain->certs.empty(); callback_->OnSelectCertificateDone( ok, /*is_sync=*/true, chain.get(), /*handshake_hints=*/absl::string_view(), - /*ticket_encryption_key=*/absl::string_view()); + /*ticket_encryption_key=*/absl::string_view(), + /*cert_matched_sni=*/cert_matched_sni); return ok ? QUIC_SUCCESS : QUIC_FAILURE; } @@ -182,16 +186,19 @@ void FakeProofSourceHandle::SelectCertOperation::Run() { /*ok=*/false, /*is_sync=*/false, nullptr, /*handshake_hints=*/absl::string_view(), - /*ticket_encryption_key=*/absl::string_view()); + /*ticket_encryption_key=*/absl::string_view(), + /*cert_matched_sni=*/false); } else if (action_ == Action::DELEGATE_ASYNC) { + bool cert_matched_sni; QuicReferenceCountedPointer<ProofSource::Chain> chain = delegate_->GetCertChain(args_.server_address, args_.client_address, - args_.hostname); + args_.hostname, &cert_matched_sni); bool ok = chain && !chain->certs.empty(); callback_->OnSelectCertificateDone( ok, /*is_sync=*/false, chain.get(), /*handshake_hints=*/absl::string_view(), - /*ticket_encryption_key=*/absl::string_view()); + /*ticket_encryption_key=*/absl::string_view(), + /*cert_matched_sni=*/cert_matched_sni); } else { QUIC_BUG(quic_bug_10139_1) << "Unexpected action: " << static_cast<int>(action_); diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.cc b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.cc index 6274f201d86..63858c94a1a 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.cc @@ -18,9 +18,9 @@ MockTimeWaitListManager::MockTimeWaitListManager( : QuicTimeWaitListManager(writer, visitor, clock, alarm_factory) { // Though AddConnectionIdToTimeWait is mocked, we want to retain its // functionality. - EXPECT_CALL(*this, AddConnectionIdToTimeWait(_, _, _)) + EXPECT_CALL(*this, AddConnectionIdToTimeWait(_, _)) .Times(testing::AnyNumber()); - ON_CALL(*this, AddConnectionIdToTimeWait(_, _, _)) + ON_CALL(*this, AddConnectionIdToTimeWait(_, _)) .WillByDefault( Invoke(this, &MockTimeWaitListManager:: QuicTimeWaitListManager_AddConnectionIdToTimeWait)); diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h index 12bf0801082..1d3cfd51a70 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h @@ -19,19 +19,15 @@ class MockTimeWaitListManager : public QuicTimeWaitListManager { QuicAlarmFactory* alarm_factory); ~MockTimeWaitListManager() override; - MOCK_METHOD(void, - AddConnectionIdToTimeWait, - (QuicConnectionId connection_id, - QuicTimeWaitListManager::TimeWaitAction action, + MOCK_METHOD(void, AddConnectionIdToTimeWait, + (QuicTimeWaitListManager::TimeWaitAction action, quic::TimeWaitConnectionInfo info), (override)); void QuicTimeWaitListManager_AddConnectionIdToTimeWait( - QuicConnectionId connection_id, QuicTimeWaitListManager::TimeWaitAction action, quic::TimeWaitConnectionInfo info) { - QuicTimeWaitListManager::AddConnectionIdToTimeWait(connection_id, action, - std::move(info)); + QuicTimeWaitListManager::AddConnectionIdToTimeWait(action, std::move(info)); } MOCK_METHOD(void, diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.cc index 76dd778734c..079b2286d40 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.cc @@ -18,7 +18,7 @@ const int32_t kMinSuccesfulWritesAfterPacketLoss = 2; // An alarm that is scheduled if a blocked socket is simulated to indicate // it's writable again. -class WriteUnblockedAlarm : public QuicAlarm::Delegate { +class WriteUnblockedAlarm : public QuicAlarm::DelegateWithoutContext { public: explicit WriteUnblockedAlarm(PacketDroppingTestWriter* writer) : writer_(writer) {} @@ -34,7 +34,7 @@ class WriteUnblockedAlarm : public QuicAlarm::Delegate { // An alarm that is scheduled every time a new packet is to be written at a // later point. -class DelayAlarm : public QuicAlarm::Delegate { +class DelayAlarm : public QuicAlarm::DelegateWithoutContext { public: explicit DelayAlarm(PacketDroppingTestWriter* writer) : writer_(writer) {} diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_decoder_test_utils.cc b/chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_decoder_test_utils.cc index ab76c445d02..37f182a767e 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_decoder_test_utils.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_decoder_test_utils.cc @@ -37,7 +37,7 @@ void TestHeadersHandler::OnDecodingCompleted() { } void TestHeadersHandler::OnDecodingErrorDetected( - absl::string_view error_message) { + QuicErrorCode /*error_code*/, absl::string_view error_message) { ASSERT_FALSE(decoding_completed_); ASSERT_FALSE(decoding_error_detected_); diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_decoder_test_utils.h b/chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_decoder_test_utils.h index 2220bd4e37a..b6bff7802c8 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_decoder_test_utils.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_decoder_test_utils.h @@ -10,6 +10,7 @@ #include "absl/strings/string_view.h" #include "quic/core/qpack/qpack_decoder.h" #include "quic/core/qpack/qpack_progressive_decoder.h" +#include "quic/core/quic_error_codes.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/qpack/qpack_test_utils.h" #include "spdy/core/spdy_header_block.h" @@ -51,7 +52,8 @@ class TestHeadersHandler void OnHeaderDecoded(absl::string_view name, absl::string_view value) override; void OnDecodingCompleted() override; - void OnDecodingErrorDetected(absl::string_view error_message) override; + void OnDecodingErrorDetected(QuicErrorCode error_code, + absl::string_view error_message) override; // Release decoded header list. Must only be called if decoding is complete // and no errors have been detected. @@ -81,9 +83,8 @@ class MockHeadersHandler (absl::string_view name, absl::string_view value), (override)); MOCK_METHOD(void, OnDecodingCompleted, (), (override)); - MOCK_METHOD(void, - OnDecodingErrorDetected, - (absl::string_view error_message), + MOCK_METHOD(void, OnDecodingErrorDetected, + (QuicErrorCode error_code, absl::string_view error_message), (override)); }; @@ -95,7 +96,8 @@ class NoOpHeadersHandler void OnHeaderDecoded(absl::string_view /*name*/, absl::string_view /*value*/) override {} void OnDecodingCompleted() override {} - void OnDecodingErrorDetected(absl::string_view /*error_message*/) override {} + void OnDecodingErrorDetected(QuicErrorCode /*error_code*/, + absl::string_view /*error_message*/) override {} }; void QpackDecode( 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 43c345094ef..6125f67adce 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 @@ -528,5 +528,12 @@ QuicConnectionPeer::GetSelfIssuedConnectionIdManager( return connection->self_issued_cid_manager_.get(); } +// static +std::unique_ptr<QuicSelfIssuedConnectionIdManager> +QuicConnectionPeer::MakeSelfIssuedConnectionIdManager( + QuicConnection* connection) { + return connection->MakeSelfIssuedConnectionIdManager(); +} + } // 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 99b8b94fd74..849ede6cf93 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 @@ -216,6 +216,9 @@ class QuicConnectionPeer { static QuicSelfIssuedConnectionIdManager* GetSelfIssuedConnectionIdManager( QuicConnection* connection); + + static std::unique_ptr<QuicSelfIssuedConnectionIdManager> + MakeSelfIssuedConnectionIdManager(QuicConnection* connection); }; } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.cc index 8002170ad90..341d5acfc84 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.cc @@ -133,5 +133,11 @@ const QuicSession* QuicDispatcherPeer::FindSession( : it->second.get(); } +// static +QuicAlarm* QuicDispatcherPeer::GetClearResetAddressesAlarm( + QuicDispatcher* dispatcher) { + return dispatcher->clear_stateless_reset_addresses_alarm_.get(); +} + } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.h index fc8ab2014ae..ec83be4f9f2 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.h @@ -76,6 +76,8 @@ class QuicDispatcherPeer { // Find the corresponding session if exsits. static const QuicSession* FindSession(const QuicDispatcher* dispatcher, QuicConnectionId id); + + static QuicAlarm* GetClearResetAddressesAlarm(QuicDispatcher* dispatcher); }; } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.cc index a27f073e993..f0f99ee989e 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.cc @@ -40,18 +40,14 @@ spdy::SpdyFramer* QuicSpdySessionPeer::GetSpdyFramer(QuicSpdySession* session) { } void QuicSpdySessionPeer::SetMaxInboundHeaderListSize( - QuicSpdySession* session, - size_t max_inbound_header_size) { + QuicSpdySession* session, size_t max_inbound_header_size) { session->set_max_inbound_header_list_size(max_inbound_header_size); } // static size_t QuicSpdySessionPeer::WriteHeadersOnHeadersStream( - QuicSpdySession* session, - QuicStreamId id, - spdy::SpdyHeaderBlock headers, - bool fin, - const spdy::SpdyStreamPrecedence& precedence, + QuicSpdySession* session, QuicStreamId id, spdy::SpdyHeaderBlock headers, + bool fin, const spdy::SpdyStreamPrecedence& precedence, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { return session->WriteHeadersOnHeadersStream( id, std::move(headers), fin, precedence, std::move(ack_listener)); @@ -100,22 +96,22 @@ QpackReceiveStream* QuicSpdySessionPeer::GetQpackEncoderReceiveStream( } // static -void QuicSpdySessionPeer::SetH3DatagramSupported(QuicSpdySession* session, - bool h3_datagram_supported) { - session->h3_datagram_supported_ = h3_datagram_supported; +void QuicSpdySessionPeer::SetHttpDatagramSupport( + QuicSpdySession* session, HttpDatagramSupport http_datagram_support) { + session->http_datagram_support_ = http_datagram_support; } // static -bool QuicSpdySessionPeer::ShouldNegotiateHttp3Datagram( +HttpDatagramSupport QuicSpdySessionPeer::LocalHttpDatagramSupport( QuicSpdySession* session) { - return session->ShouldNegotiateHttp3Datagram(); + return session->LocalHttpDatagramSupport(); } // static -void QuicSpdySessionPeer::EnableWebTransport(QuicSpdySession& session) { - QUICHE_DCHECK(session.WillNegotiateWebTransport()); - session.h3_datagram_supported_ = true; - session.peer_supports_webtransport_ = true; +void QuicSpdySessionPeer::EnableWebTransport(QuicSpdySession* session) { + QUICHE_DCHECK(session->WillNegotiateWebTransport()); + SetHttpDatagramSupport(session, HttpDatagramSupport::kDraft04); + session->peer_supports_webtransport_ = true; } } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h index 0d06c4ddfad..ba28e67c0ee 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h @@ -7,6 +7,7 @@ #include "quic/core/http/quic_receive_control_stream.h" #include "quic/core/http/quic_send_control_stream.h" +#include "quic/core/http/quic_spdy_session.h" #include "quic/core/qpack/qpack_receive_stream.h" #include "quic/core/qpack/qpack_send_stream.h" #include "quic/core/quic_packets.h" @@ -16,7 +17,6 @@ namespace quic { class QuicHeadersStream; -class QuicSpdySession; namespace test { @@ -32,11 +32,8 @@ class QuicSpdySessionPeer { static void SetMaxInboundHeaderListSize(QuicSpdySession* session, size_t max_inbound_header_size); static size_t WriteHeadersOnHeadersStream( - QuicSpdySession* session, - QuicStreamId id, - spdy::SpdyHeaderBlock headers, - bool fin, - const spdy::SpdyStreamPrecedence& precedence, + QuicSpdySession* session, QuicStreamId id, spdy::SpdyHeaderBlock headers, + bool fin, const spdy::SpdyStreamPrecedence& precedence, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); // |session| can't be nullptr. static QuicStreamId GetNextOutgoingUnidirectionalStreamId( @@ -50,10 +47,10 @@ class QuicSpdySessionPeer { QuicSpdySession* session); static QpackReceiveStream* GetQpackEncoderReceiveStream( QuicSpdySession* session); - static void SetH3DatagramSupported(QuicSpdySession* session, - bool h3_datagram_supported); - static bool ShouldNegotiateHttp3Datagram(QuicSpdySession* session); - static void EnableWebTransport(QuicSpdySession& session); + static void SetHttpDatagramSupport(QuicSpdySession* session, + HttpDatagramSupport http_datagram_support); + static HttpDatagramSupport LocalHttpDatagramSupport(QuicSpdySession* session); + static void EnableWebTransport(QuicSpdySession* session); }; } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.cc index 01ada82a741..a950f0ba323 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.cc @@ -46,8 +46,7 @@ bool QuicStreamSequencerBufferPeer::IsBlockArrayEmpty() { return true; } - size_t count = buffer_->allocate_blocks_on_demand_ ? current_blocks_count() - : max_blocks_count(); + size_t count = current_blocks_count(); for (size_t i = 0; i < count; i++) { if (buffer_->blocks_[i] != nullptr) { return false; diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_backend.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_backend.cc index eb419b607f5..3cfe9279a3a 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_backend.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_backend.cc @@ -7,16 +7,64 @@ #include <cstring> #include <memory> +#include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" #include "absl/strings/string_view.h" #include "quic/core/quic_buffer_allocator.h" #include "quic/core/quic_simple_buffer_allocator.h" #include "quic/core/web_transport_interface.h" #include "quic/platform/api/quic_mem_slice.h" +#include "quic/test_tools/web_transport_resets_backend.h" #include "quic/tools/web_transport_test_visitors.h" namespace quic { namespace test { +namespace { + +// SessionCloseVisitor implements the "/session-close" endpoint. If the client +// sends a unidirectional stream of format "code message" to this endpoint, it +// will close the session with the corresponding error code and error message. +// For instance, sending "42 test error" will cause it to be closed with code 42 +// and message "test error". +class SessionCloseVisitor : public WebTransportVisitor { + public: + SessionCloseVisitor(WebTransportSession* session) : session_(session) {} + + void OnSessionReady(const spdy::SpdyHeaderBlock& /*headers*/) override {} + void OnSessionClosed(WebTransportSessionError /*error_code*/, + const std::string& /*error_message*/) override {} + + void OnIncomingBidirectionalStreamAvailable() override {} + void OnIncomingUnidirectionalStreamAvailable() override { + WebTransportStream* stream = session_->AcceptIncomingUnidirectionalStream(); + if (stream == nullptr) { + return; + } + stream->SetVisitor( + std::make_unique<WebTransportUnidirectionalEchoReadVisitor>( + stream, [this](const std::string& data) { + std::pair<absl::string_view, absl::string_view> parsed = + absl::StrSplit(data, absl::MaxSplits(' ', 1)); + WebTransportSessionError error_code = 0; + bool success = absl::SimpleAtoi(parsed.first, &error_code); + QUICHE_DCHECK(success) << data; + session_->CloseSession(error_code, parsed.second); + })); + stream->visitor()->OnCanRead(); + } + + void OnDatagramReceived(absl::string_view /*datagram*/) override {} + + void OnCanCreateNewOutgoingBidirectionalStream() override {} + void OnCanCreateNewOutgoingUnidirectionalStream() override {} + + private: + WebTransportSession* session_; // Not owned. +}; + +} // namespace + QuicSimpleServerBackend::WebTransportResponse QuicTestBackend::ProcessWebTransportRequest( const spdy::Http2HeaderBlock& request_headers, @@ -33,13 +81,38 @@ QuicTestBackend::ProcessWebTransportRequest( return response; } absl::string_view path = path_it->second; - if (path == "/echo") { + // Match any "/echo.*" pass, e.g. "/echo_foobar" + if (absl::StartsWith(path, "/echo")) { WebTransportResponse response; response.response_headers[":status"] = "200"; + // Add response headers if the paramer has "set-header=XXX:YYY" query. + GURL url = GURL(absl::StrCat("https://localhost", path)); + const std::vector<std::string>& params = absl::StrSplit(url.query(), '&'); + for (const auto& param : params) { + absl::string_view param_view = param; + if (absl::ConsumePrefix(¶m_view, "set-header=")) { + const std::vector<absl::string_view> header_value = + absl::StrSplit(param_view, ':'); + if (header_value.size() == 2 && + !absl::StartsWith(header_value[0], ":")) { + response.response_headers[header_value[0]] = header_value[1]; + } + } + } + response.visitor = std::make_unique<EchoWebTransportSessionVisitor>(session); return response; } + if (path == "/resets") { + return WebTransportResetsBackend(request_headers, session); + } + if (path == "/session-close") { + WebTransportResponse response; + response.response_headers[":status"] = "200"; + response.visitor = std::make_unique<SessionCloseVisitor>(session); + return response; + } WebTransportResponse response; response.response_headers[":status"] = "404"; 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 126b87e039c..9a7fd85b280 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 @@ -1312,20 +1312,6 @@ StreamType DetermineStreamType(QuicStreamId id, : default_type; } -QuicMemSliceSpan MakeSpan(QuicBufferAllocator* allocator, - absl::string_view message_data, - QuicMemSliceStorage* storage) { - if (message_data.length() == 0) { - *storage = - QuicMemSliceStorage(nullptr, 0, allocator, kMaxOutgoingPacketSize); - return storage->ToSpan(); - } - struct iovec iov = {const_cast<char*>(message_data.data()), - message_data.length()}; - *storage = QuicMemSliceStorage(&iov, 1, allocator, kMaxOutgoingPacketSize); - return storage->ToSpan(); -} - QuicMemSlice MemSliceFromString(absl::string_view data) { if (data.empty()) { return QuicMemSlice(); 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 06f3eeca7c2..e746ddb33e1 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 @@ -24,6 +24,7 @@ #include "quic/core/http/quic_spdy_session.h" #include "quic/core/quic_connection.h" #include "quic/core/quic_connection_id.h" +#include "quic/core/quic_error_codes.h" #include "quic/core/quic_framer.h" #include "quic/core/quic_packet_writer.h" #include "quic/core/quic_path_validator.h" @@ -103,26 +104,17 @@ void DisableQuicVersionsWithTls(); // constructed packet, the framer must be set to use NullDecrypter. QuicEncryptedPacket* ConstructEncryptedPacket( QuicConnectionId destination_connection_id, - QuicConnectionId source_connection_id, - bool version_flag, - bool reset_flag, - uint64_t packet_number, - const std::string& data, - bool full_padding, + QuicConnectionId source_connection_id, bool version_flag, bool reset_flag, + uint64_t packet_number, const std::string& data, bool full_padding, QuicConnectionIdIncluded destination_connection_id_included, QuicConnectionIdIncluded source_connection_id_included, QuicPacketNumberLength packet_number_length, - ParsedQuicVersionVector* versions, - Perspective perspective); + ParsedQuicVersionVector* versions, Perspective perspective); QuicEncryptedPacket* ConstructEncryptedPacket( QuicConnectionId destination_connection_id, - QuicConnectionId source_connection_id, - bool version_flag, - bool reset_flag, - uint64_t packet_number, - const std::string& data, - bool full_padding, + QuicConnectionId source_connection_id, bool version_flag, bool reset_flag, + uint64_t packet_number, const std::string& data, bool full_padding, QuicConnectionIdIncluded destination_connection_id_included, QuicConnectionIdIncluded source_connection_id_included, QuicPacketNumberLength packet_number_length, @@ -134,11 +126,8 @@ QuicEncryptedPacket* ConstructEncryptedPacket( // constructed packet, the framer must be set to use NullDecrypter. QuicEncryptedPacket* ConstructEncryptedPacket( QuicConnectionId destination_connection_id, - QuicConnectionId source_connection_id, - bool version_flag, - bool reset_flag, - uint64_t packet_number, - const std::string& data, + QuicConnectionId source_connection_id, bool version_flag, bool reset_flag, + uint64_t packet_number, const std::string& data, QuicConnectionIdIncluded destination_connection_id_included, QuicConnectionIdIncluded source_connection_id_included, QuicPacketNumberLength packet_number_length, @@ -147,11 +136,8 @@ QuicEncryptedPacket* ConstructEncryptedPacket( // This form assumes |versions| == nullptr. QuicEncryptedPacket* ConstructEncryptedPacket( QuicConnectionId destination_connection_id, - QuicConnectionId source_connection_id, - bool version_flag, - bool reset_flag, - uint64_t packet_number, - const std::string& data, + QuicConnectionId source_connection_id, bool version_flag, bool reset_flag, + uint64_t packet_number, const std::string& data, QuicConnectionIdIncluded destination_connection_id_included, QuicConnectionIdIncluded source_connection_id_included, QuicPacketNumberLength packet_number_length); @@ -161,11 +147,8 @@ QuicEncryptedPacket* ConstructEncryptedPacket( // |versions| == nullptr. QuicEncryptedPacket* ConstructEncryptedPacket( QuicConnectionId destination_connection_id, - QuicConnectionId source_connection_id, - bool version_flag, - bool reset_flag, - uint64_t packet_number, - const std::string& data); + QuicConnectionId source_connection_id, bool version_flag, bool reset_flag, + uint64_t packet_number, const std::string& data); // Creates a client-to-server ZERO-RTT packet that will fail to decrypt. std::unique_ptr<QuicEncryptedPacket> GetUndecryptableEarlyPacket( @@ -175,8 +158,7 @@ std::unique_ptr<QuicEncryptedPacket> GetUndecryptableEarlyPacket( // Constructs a received packet for testing. The caller must take ownership // of the returned pointer. QuicReceivedPacket* ConstructReceivedPacket( - const QuicEncryptedPacket& encrypted_packet, - QuicTime receipt_time); + const QuicEncryptedPacket& encrypted_packet, QuicTime receipt_time); // Create an encrypted packet for testing whose data portion erroneous. // The specific way the data portion is erroneous is not specified, but @@ -185,15 +167,11 @@ QuicReceivedPacket* ConstructReceivedPacket( // constructed packet, the framer must be set to use NullDecrypter. QuicEncryptedPacket* ConstructMisFramedEncryptedPacket( QuicConnectionId destination_connection_id, - QuicConnectionId source_connection_id, - bool version_flag, - bool reset_flag, - uint64_t packet_number, - const std::string& data, + QuicConnectionId source_connection_id, bool version_flag, bool reset_flag, + uint64_t packet_number, const std::string& data, QuicConnectionIdIncluded destination_connection_id_included, QuicConnectionIdIncluded source_connection_id_included, - QuicPacketNumberLength packet_number_length, - ParsedQuicVersion version, + QuicPacketNumberLength packet_number_length, ParsedQuicVersion version, Perspective perspective); // Returns QuicConfig set to default values. @@ -228,8 +206,7 @@ QuicAckFrame MakeAckFrameWithAckBlocks(size_t num_ack_blocks, // Testing convenice method to construct a QuicAckFrame with |largest_acked|, // ack blocks of width 1 packet and |gap_size|. -QuicAckFrame MakeAckFrameWithGaps(uint64_t gap_size, - size_t max_num_gaps, +QuicAckFrame MakeAckFrameWithGaps(uint64_t gap_size, size_t max_num_gaps, uint64_t largest_acked); // Returns the encryption level that corresponds to the header type in @@ -241,15 +218,12 @@ EncryptionLevel HeaderToEncryptionLevel(const QuicPacketHeader& header); // is populated with the fields in |header| and |frames|, or is nullptr if the // packet could not be created. std::unique_ptr<QuicPacket> BuildUnsizedDataPacket( - QuicFramer* framer, - const QuicPacketHeader& header, + QuicFramer* framer, const QuicPacketHeader& header, const QuicFrames& frames); // Returns a QuicPacket that is owned by the caller, and of size |packet_size|. std::unique_ptr<QuicPacket> BuildUnsizedDataPacket( - QuicFramer* framer, - const QuicPacketHeader& header, - const QuicFrames& frames, - size_t packet_size); + QuicFramer* framer, const QuicPacketHeader& header, + const QuicFrames& frames, size_t packet_size); // Compute SHA-1 hash of the supplied std::string. std::string Sha1Hash(absl::string_view data); @@ -297,21 +271,14 @@ class MockFramerVisitor : public QuicFramerVisitorInterface { MOCK_METHOD(void, OnError, (QuicFramer*), (override)); // The constructor sets this up to return false by default. - MOCK_METHOD(bool, - OnProtocolVersionMismatch, - (ParsedQuicVersion version), + MOCK_METHOD(bool, OnProtocolVersionMismatch, (ParsedQuicVersion version), (override)); MOCK_METHOD(void, OnPacket, (), (override)); - MOCK_METHOD(void, - OnPublicResetPacket, - (const QuicPublicResetPacket& header), - (override)); - MOCK_METHOD(void, - OnVersionNegotiationPacket, - (const QuicVersionNegotiationPacket& packet), + MOCK_METHOD(void, OnPublicResetPacket, (const QuicPublicResetPacket& header), (override)); - MOCK_METHOD(void, - OnRetryPacket, + MOCK_METHOD(void, OnVersionNegotiationPacket, + (const QuicVersionNegotiationPacket& packet), (override)); + MOCK_METHOD(void, OnRetryPacket, (QuicConnectionId original_connection_id, QuicConnectionId new_connection_id, absl::string_view retry_token, @@ -319,133 +286,75 @@ class MockFramerVisitor : public QuicFramerVisitorInterface { absl::string_view retry_without_tag), (override)); // The constructor sets this up to return true by default. - MOCK_METHOD(bool, - OnUnauthenticatedHeader, - (const QuicPacketHeader& header), + MOCK_METHOD(bool, OnUnauthenticatedHeader, (const QuicPacketHeader& header), (override)); // The constructor sets this up to return true by default. - MOCK_METHOD(bool, - OnUnauthenticatedPublicHeader, - (const QuicPacketHeader& header), + MOCK_METHOD(bool, OnUnauthenticatedPublicHeader, + (const QuicPacketHeader& header), (override)); + MOCK_METHOD(void, OnDecryptedPacket, (size_t length, EncryptionLevel level), (override)); - MOCK_METHOD(void, - OnDecryptedPacket, - (size_t length, EncryptionLevel level), + MOCK_METHOD(bool, OnPacketHeader, (const QuicPacketHeader& header), (override)); - MOCK_METHOD(bool, - OnPacketHeader, - (const QuicPacketHeader& header), + MOCK_METHOD(void, OnCoalescedPacket, (const QuicEncryptedPacket& packet), (override)); - MOCK_METHOD(void, - OnCoalescedPacket, - (const QuicEncryptedPacket& packet), - (override)); - MOCK_METHOD(void, - OnUndecryptablePacket, + MOCK_METHOD(void, OnUndecryptablePacket, (const QuicEncryptedPacket& packet, - EncryptionLevel decryption_level, - bool has_decryption_key), + EncryptionLevel decryption_level, bool has_decryption_key), (override)); MOCK_METHOD(bool, OnStreamFrame, (const QuicStreamFrame& frame), (override)); MOCK_METHOD(bool, OnCryptoFrame, (const QuicCryptoFrame& frame), (override)); - MOCK_METHOD(bool, - OnAckFrameStart, - (QuicPacketNumber, QuicTime::Delta), + MOCK_METHOD(bool, OnAckFrameStart, (QuicPacketNumber, QuicTime::Delta), (override)); - MOCK_METHOD(bool, - OnAckRange, - (QuicPacketNumber, QuicPacketNumber), + MOCK_METHOD(bool, OnAckRange, (QuicPacketNumber, QuicPacketNumber), (override)); MOCK_METHOD(bool, OnAckTimestamp, (QuicPacketNumber, QuicTime), (override)); MOCK_METHOD(bool, OnAckFrameEnd, (QuicPacketNumber), (override)); - MOCK_METHOD(bool, - OnStopWaitingFrame, - (const QuicStopWaitingFrame& frame), + MOCK_METHOD(bool, OnStopWaitingFrame, (const QuicStopWaitingFrame& frame), (override)); - MOCK_METHOD(bool, - OnPaddingFrame, - (const QuicPaddingFrame& frame), + MOCK_METHOD(bool, OnPaddingFrame, (const QuicPaddingFrame& frame), (override)); MOCK_METHOD(bool, OnPingFrame, (const QuicPingFrame& frame), (override)); - MOCK_METHOD(bool, - OnRstStreamFrame, - (const QuicRstStreamFrame& frame), - (override)); - MOCK_METHOD(bool, - OnConnectionCloseFrame, - (const QuicConnectionCloseFrame& frame), + MOCK_METHOD(bool, OnRstStreamFrame, (const QuicRstStreamFrame& frame), (override)); - MOCK_METHOD(bool, - OnNewConnectionIdFrame, - (const QuicNewConnectionIdFrame& frame), + MOCK_METHOD(bool, OnConnectionCloseFrame, + (const QuicConnectionCloseFrame& frame), (override)); + MOCK_METHOD(bool, OnNewConnectionIdFrame, + (const QuicNewConnectionIdFrame& frame), (override)); + MOCK_METHOD(bool, OnRetireConnectionIdFrame, + (const QuicRetireConnectionIdFrame& frame), (override)); + MOCK_METHOD(bool, OnNewTokenFrame, (const QuicNewTokenFrame& frame), (override)); - MOCK_METHOD(bool, - OnRetireConnectionIdFrame, - (const QuicRetireConnectionIdFrame& frame), + MOCK_METHOD(bool, OnStopSendingFrame, (const QuicStopSendingFrame& frame), (override)); - MOCK_METHOD(bool, - OnNewTokenFrame, - (const QuicNewTokenFrame& frame), + MOCK_METHOD(bool, OnPathChallengeFrame, (const QuicPathChallengeFrame& frame), (override)); - MOCK_METHOD(bool, - OnStopSendingFrame, - (const QuicStopSendingFrame& frame), - (override)); - MOCK_METHOD(bool, - OnPathChallengeFrame, - (const QuicPathChallengeFrame& frame), - (override)); - MOCK_METHOD(bool, - OnPathResponseFrame, - (const QuicPathResponseFrame& frame), + MOCK_METHOD(bool, OnPathResponseFrame, (const QuicPathResponseFrame& frame), (override)); MOCK_METHOD(bool, OnGoAwayFrame, (const QuicGoAwayFrame& frame), (override)); - MOCK_METHOD(bool, - OnMaxStreamsFrame, - (const QuicMaxStreamsFrame& frame), - (override)); - MOCK_METHOD(bool, - OnStreamsBlockedFrame, - (const QuicStreamsBlockedFrame& frame), + MOCK_METHOD(bool, OnMaxStreamsFrame, (const QuicMaxStreamsFrame& frame), (override)); - MOCK_METHOD(bool, - OnWindowUpdateFrame, - (const QuicWindowUpdateFrame& frame), + MOCK_METHOD(bool, OnStreamsBlockedFrame, + (const QuicStreamsBlockedFrame& frame), (override)); + MOCK_METHOD(bool, OnWindowUpdateFrame, (const QuicWindowUpdateFrame& frame), (override)); - MOCK_METHOD(bool, - OnBlockedFrame, - (const QuicBlockedFrame& frame), + MOCK_METHOD(bool, OnBlockedFrame, (const QuicBlockedFrame& frame), (override)); - MOCK_METHOD(bool, - OnMessageFrame, - (const QuicMessageFrame& frame), + MOCK_METHOD(bool, OnMessageFrame, (const QuicMessageFrame& frame), (override)); - MOCK_METHOD(bool, - OnHandshakeDoneFrame, - (const QuicHandshakeDoneFrame& frame), + MOCK_METHOD(bool, OnHandshakeDoneFrame, (const QuicHandshakeDoneFrame& frame), (override)); - MOCK_METHOD(bool, - OnAckFrequencyFrame, - (const QuicAckFrequencyFrame& frame), + MOCK_METHOD(bool, OnAckFrequencyFrame, (const QuicAckFrequencyFrame& frame), (override)); MOCK_METHOD(void, OnPacketComplete, (), (override)); - MOCK_METHOD(bool, - IsValidStatelessResetToken, - (const StatelessResetToken&), + MOCK_METHOD(bool, IsValidStatelessResetToken, (const StatelessResetToken&), (const, override)); - MOCK_METHOD(void, - OnAuthenticatedIetfStatelessResetPacket, - (const QuicIetfStatelessResetPacket&), - (override)); + MOCK_METHOD(void, OnAuthenticatedIetfStatelessResetPacket, + (const QuicIetfStatelessResetPacket&), (override)); MOCK_METHOD(void, OnKeyUpdate, (KeyUpdateReason), (override)); MOCK_METHOD(void, OnDecryptedFirstPacketInKeyPhase, (), (override)); MOCK_METHOD(std::unique_ptr<QuicDecrypter>, - AdvanceKeysAndCreateCurrentOneRttDecrypter, - (), - (override)); - MOCK_METHOD(std::unique_ptr<QuicEncrypter>, - CreateCurrentOneRttEncrypter, - (), + AdvanceKeysAndCreateCurrentOneRttDecrypter, (), (override)); + MOCK_METHOD(std::unique_ptr<QuicEncrypter>, CreateCurrentOneRttEncrypter, (), (override)); }; @@ -529,97 +438,66 @@ class MockQuicConnectionVisitor : public QuicConnectionVisitorInterface { MOCK_METHOD(void, OnStreamFrame, (const QuicStreamFrame& frame), (override)); MOCK_METHOD(void, OnCryptoFrame, (const QuicCryptoFrame& frame), (override)); - MOCK_METHOD(void, - OnWindowUpdateFrame, - (const QuicWindowUpdateFrame& frame), + MOCK_METHOD(void, OnWindowUpdateFrame, (const QuicWindowUpdateFrame& frame), (override)); - MOCK_METHOD(void, - OnBlockedFrame, - (const QuicBlockedFrame& frame), + MOCK_METHOD(void, OnBlockedFrame, (const QuicBlockedFrame& frame), (override)); MOCK_METHOD(void, OnRstStream, (const QuicRstStreamFrame& frame), (override)); MOCK_METHOD(void, OnGoAway, (const QuicGoAwayFrame& frame), (override)); MOCK_METHOD(void, OnMessageReceived, (absl::string_view message), (override)); MOCK_METHOD(void, OnHandshakeDoneReceived, (), (override)); MOCK_METHOD(void, OnNewTokenReceived, (absl::string_view token), (override)); - MOCK_METHOD(void, - OnConnectionClosed, + MOCK_METHOD(void, OnConnectionClosed, (const QuicConnectionCloseFrame& frame, ConnectionCloseSource source), (override)); MOCK_METHOD(void, OnWriteBlocked, (), (override)); MOCK_METHOD(void, OnCanWrite, (), (override)); MOCK_METHOD(bool, SendProbingData, (), (override)); - MOCK_METHOD(bool, - ValidateStatelessReset, + MOCK_METHOD(bool, ValidateStatelessReset, (const quic::QuicSocketAddress&, const quic::QuicSocketAddress&), (override)); MOCK_METHOD(void, OnCongestionWindowChange, (QuicTime now), (override)); - MOCK_METHOD(void, - OnConnectionMigration, - (AddressChangeType type), + 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, ShouldKeepConnectionAlive, (), (const, override)); MOCK_METHOD(std::string, GetStreamsInfoForLogging, (), (const, override)); - MOCK_METHOD(void, - OnSuccessfulVersionNegotiation, - (const ParsedQuicVersion& version), - (override)); - MOCK_METHOD(void, - OnPacketReceived, + MOCK_METHOD(void, OnSuccessfulVersionNegotiation, + (const ParsedQuicVersion& version), (override)); + MOCK_METHOD(void, OnPacketReceived, (const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, bool is_connectivity_probe), (override)); MOCK_METHOD(void, OnAckNeedsRetransmittableFrame, (), (override)); - MOCK_METHOD(void, - SendAckFrequency, - (const QuicAckFrequencyFrame& frame), - (override)); - MOCK_METHOD(void, - SendNewConnectionId, - (const QuicNewConnectionIdFrame& frame), - (override)); - MOCK_METHOD(void, - SendRetireConnectionId, - (uint64_t sequence_number), + MOCK_METHOD(void, SendAckFrequency, (const QuicAckFrequencyFrame& frame), (override)); - MOCK_METHOD(void, - OnServerConnectionIdIssued, - (const QuicConnectionId& server_connection_id), - (override)); - MOCK_METHOD(void, - OnServerConnectionIdRetired, - (const QuicConnectionId& server_connection_id), + MOCK_METHOD(void, SendNewConnectionId, + (const QuicNewConnectionIdFrame& frame), (override)); + MOCK_METHOD(void, SendRetireConnectionId, (uint64_t sequence_number), (override)); + MOCK_METHOD(void, OnServerConnectionIdIssued, + (const QuicConnectionId& server_connection_id), (override)); + MOCK_METHOD(void, OnServerConnectionIdRetired, + (const QuicConnectionId& server_connection_id), (override)); MOCK_METHOD(bool, AllowSelfAddressChange, (), (const, override)); MOCK_METHOD(HandshakeState, GetHandshakeState, (), (const, override)); - MOCK_METHOD(bool, - OnMaxStreamsFrame, - (const QuicMaxStreamsFrame& frame), - (override)); - MOCK_METHOD(bool, - OnStreamsBlockedFrame, - (const QuicStreamsBlockedFrame& frame), + MOCK_METHOD(bool, OnMaxStreamsFrame, (const QuicMaxStreamsFrame& frame), (override)); - MOCK_METHOD(void, - OnStopSendingFrame, - (const QuicStopSendingFrame& frame), + MOCK_METHOD(bool, OnStreamsBlockedFrame, + (const QuicStreamsBlockedFrame& frame), (override)); + MOCK_METHOD(void, OnStopSendingFrame, (const QuicStopSendingFrame& frame), (override)); MOCK_METHOD(void, OnPacketDecrypted, (EncryptionLevel), (override)); MOCK_METHOD(void, OnOneRttPacketAcknowledged, (), (override)); MOCK_METHOD(void, OnHandshakePacketSent, (), (override)); MOCK_METHOD(void, OnKeyUpdate, (KeyUpdateReason), (override)); MOCK_METHOD(std::unique_ptr<QuicDecrypter>, - AdvanceKeysAndCreateCurrentOneRttDecrypter, - (), - (override)); - MOCK_METHOD(std::unique_ptr<QuicEncrypter>, - CreateCurrentOneRttEncrypter, - (), + AdvanceKeysAndCreateCurrentOneRttDecrypter, (), (override)); + MOCK_METHOD(std::unique_ptr<QuicEncrypter>, CreateCurrentOneRttEncrypter, (), (override)); MOCK_METHOD(void, BeforeConnectionCloseSent, (), (override)); MOCK_METHOD(bool, ValidateToken, (absl::string_view), (const, override)); @@ -672,36 +550,57 @@ class MockAlarmFactory : public QuicAlarmFactory { } }; +class TestAlarmFactory : public QuicAlarmFactory { + public: + class TestAlarm : public QuicAlarm { + public: + explicit TestAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate> delegate) + : QuicAlarm(std::move(delegate)) {} + + void SetImpl() override {} + void CancelImpl() override {} + using QuicAlarm::Fire; + }; + + TestAlarmFactory() {} + TestAlarmFactory(const TestAlarmFactory&) = delete; + TestAlarmFactory& operator=(const TestAlarmFactory&) = delete; + + QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override { + return new TestAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate)); + } + + QuicArenaScopedPtr<QuicAlarm> CreateAlarm( + QuicArenaScopedPtr<QuicAlarm::Delegate> delegate, + QuicConnectionArena* arena) override { + return arena->New<TestAlarm>(std::move(delegate)); + } +}; + class MockQuicConnection : public QuicConnection { public: // Uses a ConnectionId of 42 and 127.0.0.1:123. MockQuicConnection(MockQuicConnectionHelper* helper, - MockAlarmFactory* alarm_factory, - Perspective perspective); + MockAlarmFactory* alarm_factory, Perspective perspective); // Uses a ConnectionId of 42. MockQuicConnection(QuicSocketAddress address, MockQuicConnectionHelper* helper, - MockAlarmFactory* alarm_factory, - Perspective perspective); + MockAlarmFactory* alarm_factory, Perspective perspective); // Uses 127.0.0.1:123. MockQuicConnection(QuicConnectionId connection_id, MockQuicConnectionHelper* helper, - MockAlarmFactory* alarm_factory, - Perspective perspective); + MockAlarmFactory* alarm_factory, Perspective perspective); // Uses a ConnectionId of 42, and 127.0.0.1:123. MockQuicConnection(MockQuicConnectionHelper* helper, - MockAlarmFactory* alarm_factory, - Perspective perspective, + MockAlarmFactory* alarm_factory, Perspective perspective, const ParsedQuicVersionVector& supported_versions); - MockQuicConnection(QuicConnectionId connection_id, - QuicSocketAddress address, + MockQuicConnection(QuicConnectionId connection_id, QuicSocketAddress address, MockQuicConnectionHelper* helper, - MockAlarmFactory* alarm_factory, - Perspective perspective, + MockAlarmFactory* alarm_factory, Perspective perspective, const ParsedQuicVersionVector& supported_versions); MockQuicConnection(const MockQuicConnection&) = delete; MockQuicConnection& operator=(const MockQuicConnection&) = delete; @@ -713,64 +612,45 @@ class MockQuicConnection : public QuicConnection { // will advance the time of the MockClock. void AdvanceTime(QuicTime::Delta delta); - MOCK_METHOD(void, - ProcessUdpPacket, + MOCK_METHOD(void, ProcessUdpPacket, (const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, const QuicReceivedPacket& packet), (override)); - MOCK_METHOD(void, - CloseConnection, - (QuicErrorCode error, - const std::string& details, + MOCK_METHOD(void, CloseConnection, + (QuicErrorCode error, const std::string& details, ConnectionCloseBehavior connection_close_behavior), (override)); - MOCK_METHOD(void, - CloseConnection, - (QuicErrorCode error, - QuicIetfTransportErrorCodes ietf_error, + MOCK_METHOD(void, CloseConnection, + (QuicErrorCode error, QuicIetfTransportErrorCodes ietf_error, const std::string& details, ConnectionCloseBehavior connection_close_behavior), (override)); - MOCK_METHOD(void, - SendConnectionClosePacket, - (QuicErrorCode error, - QuicIetfTransportErrorCodes ietf_error, + MOCK_METHOD(void, SendConnectionClosePacket, + (QuicErrorCode error, QuicIetfTransportErrorCodes ietf_error, const std::string& details), (override)); MOCK_METHOD(void, OnCanWrite, (), (override)); - MOCK_METHOD(void, - SendConnectivityProbingResponsePacket, - (const QuicSocketAddress& peer_address), - (override)); - MOCK_METHOD(bool, - SendConnectivityProbingPacket, + MOCK_METHOD(void, SendConnectivityProbingResponsePacket, + (const QuicSocketAddress& peer_address), (override)); + MOCK_METHOD(bool, SendConnectivityProbingPacket, (QuicPacketWriter*, const QuicSocketAddress& peer_address), (override)); - MOCK_METHOD(void, - OnSendConnectionState, - (const CachedNetworkParameters&), - (override)); - MOCK_METHOD(void, - ResumeConnectionState, - (const CachedNetworkParameters&, bool), + MOCK_METHOD(void, OnSendConnectionState, (const CachedNetworkParameters&), (override)); + MOCK_METHOD(void, ResumeConnectionState, + (const CachedNetworkParameters&, bool), (override)); MOCK_METHOD(void, SetMaxPacingRate, (QuicBandwidth), (override)); - MOCK_METHOD(void, - OnStreamReset, - (QuicStreamId, QuicRstStreamErrorCode), + MOCK_METHOD(void, OnStreamReset, (QuicStreamId, QuicRstStreamErrorCode), (override)); MOCK_METHOD(bool, SendControlFrame, (const QuicFrame& frame), (override)); MOCK_METHOD(MessageStatus, SendMessage, (QuicMessageId, absl::Span<QuicMemSlice>, bool), (override)); - MOCK_METHOD(bool, - SendPathChallenge, - (const QuicPathFrameBuffer&, - const QuicSocketAddress&, - const QuicSocketAddress&, - const QuicSocketAddress&, + MOCK_METHOD(bool, SendPathChallenge, + (const QuicPathFrameBuffer&, const QuicSocketAddress&, + const QuicSocketAddress&, const QuicSocketAddress&, QuicPacketWriter*), (override)); @@ -782,8 +662,7 @@ class MockQuicConnection : public QuicConnection { void ReallyOnCanWrite() { QuicConnection::OnCanWrite(); } void ReallyCloseConnection( - QuicErrorCode error, - const std::string& details, + QuicErrorCode error, const std::string& details, ConnectionCloseBehavior connection_close_behavior) { // Call the 4-param method directly instead of the 3-param method, so that // it doesn't invoke the virtual 4-param method causing the mock 4-param @@ -793,8 +672,7 @@ class MockQuicConnection : public QuicConnection { } void ReallyCloseConnection4( - QuicErrorCode error, - QuicIetfTransportErrorCodes ietf_error, + QuicErrorCode error, QuicIetfTransportErrorCodes ietf_error, const std::string& details, ConnectionCloseBehavior connection_close_behavior) { QuicConnection::CloseConnection(error, ietf_error, details, @@ -820,8 +698,7 @@ class MockQuicConnection : public QuicConnection { } bool ReallySendConnectivityProbingPacket( - QuicPacketWriter* probing_writer, - const QuicSocketAddress& peer_address) { + QuicPacketWriter* probing_writer, const QuicSocketAddress& peer_address) { return QuicConnection::SendConnectivityProbingPacket(probing_writer, peer_address); } @@ -835,18 +712,12 @@ class MockQuicConnection : public QuicConnection { return QuicConnection::OnPathResponseFrame(frame); } - MOCK_METHOD(bool, - OnPathResponseFrame, - (const QuicPathResponseFrame&), + MOCK_METHOD(bool, OnPathResponseFrame, (const QuicPathResponseFrame&), (override)); - MOCK_METHOD(bool, - OnStopSendingFrame, - (const QuicStopSendingFrame& frame), - (override)); - MOCK_METHOD(size_t, - SendCryptoData, - (EncryptionLevel, size_t, QuicStreamOffset), + MOCK_METHOD(bool, OnStopSendingFrame, (const QuicStopSendingFrame& frame), (override)); + MOCK_METHOD(size_t, SendCryptoData, + (EncryptionLevel, size_t, QuicStreamOffset), (override)); size_t QuicConnection_SendCryptoData(EncryptionLevel level, size_t write_length, QuicStreamOffset offset) { @@ -892,59 +763,47 @@ class MockQuicSession : public QuicSession { const QuicCryptoStream* GetCryptoStream() const override; void SetCryptoStream(QuicCryptoStream* crypto_stream); - MOCK_METHOD(void, - OnConnectionClosed, + MOCK_METHOD(void, OnConnectionClosed, (const QuicConnectionCloseFrame& frame, ConnectionCloseSource source), (override)); MOCK_METHOD(QuicStream*, CreateIncomingStream, (QuicStreamId id), (override)); - MOCK_METHOD(QuicSpdyStream*, - CreateIncomingStream, - (PendingStream*), + MOCK_METHOD(QuicSpdyStream*, CreateIncomingStream, (PendingStream*), (override)); MOCK_METHOD(QuicConsumedData, WritevData, (QuicStreamId id, size_t write_length, QuicStreamOffset offset, StreamSendingState state, TransmissionType type, EncryptionLevel level), (override)); - MOCK_METHOD(bool, - WriteControlFrame, - (const QuicFrame& frame, TransmissionType type), - (override)); - MOCK_METHOD(void, - MaybeSendRstStreamFrame, - (QuicStreamId stream_id, - QuicRstStreamErrorCode error, + MOCK_METHOD(bool, WriteControlFrame, + (const QuicFrame& frame, TransmissionType type), (override)); + MOCK_METHOD(void, MaybeSendRstStreamFrame, + (QuicStreamId stream_id, QuicResetStreamError error, QuicStreamOffset bytes_written), (override)); - MOCK_METHOD(void, - MaybeSendStopSendingFrame, - (QuicStreamId stream_id, QuicRstStreamErrorCode error), - (override)); + MOCK_METHOD(void, MaybeSendStopSendingFrame, + (QuicStreamId stream_id, QuicResetStreamError error), (override)); MOCK_METHOD(bool, ShouldKeepConnectionAlive, (), (const, override)); MOCK_METHOD(std::vector<std::string>, GetAlpnsToOffer, (), (const, override)); - MOCK_METHOD(std::vector<absl::string_view>::const_iterator, - SelectAlpn, - (const std::vector<absl::string_view>&), - (const, override)); + MOCK_METHOD(std::vector<absl::string_view>::const_iterator, SelectAlpn, + (const std::vector<absl::string_view>&), (const, override)); MOCK_METHOD(void, OnAlpnSelected, (absl::string_view), (override)); using QuicSession::ActivateStream; // Returns a QuicConsumedData that indicates all of |write_length| (and |fin| // if set) has been consumed. - QuicConsumedData ConsumeData(QuicStreamId id, - size_t write_length, + QuicConsumedData ConsumeData(QuicStreamId id, size_t write_length, QuicStreamOffset offset, - StreamSendingState state, - TransmissionType type, + StreamSendingState state, TransmissionType type, absl::optional<EncryptionLevel> level); void ReallyMaybeSendRstStreamFrame(QuicStreamId id, QuicRstStreamErrorCode error, QuicStreamOffset bytes_written) { - QuicSession::MaybeSendRstStreamFrame(id, error, bytes_written); + QuicSession::MaybeSendRstStreamFrame( + id, QuicResetStreamError::FromInternal(error), bytes_written); } private: @@ -985,6 +844,13 @@ class MockQuicCryptoStream : public QuicCryptoStream { std::unique_ptr<QuicEncrypter> CreateCurrentOneRttEncrypter() override { return nullptr; } + bool ExportKeyingMaterial(absl::string_view /*label*/, + absl::string_view /*context*/, + size_t /*result_len*/, + std::string* /*result*/) override { + return false; + } + SSL* GetSsl() const override { return nullptr; } private: QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_; @@ -1012,26 +878,17 @@ class MockQuicSpdySession : public QuicSpdySession { } // From QuicSession. - MOCK_METHOD(void, - OnConnectionClosed, + MOCK_METHOD(void, OnConnectionClosed, (const QuicConnectionCloseFrame& frame, ConnectionCloseSource source), (override)); - MOCK_METHOD(QuicSpdyStream*, - CreateIncomingStream, - (QuicStreamId id), + MOCK_METHOD(QuicSpdyStream*, CreateIncomingStream, (QuicStreamId id), (override)); - MOCK_METHOD(QuicSpdyStream*, - CreateIncomingStream, - (PendingStream*), + MOCK_METHOD(QuicSpdyStream*, CreateIncomingStream, (PendingStream*), (override)); - MOCK_METHOD(QuicSpdyStream*, - CreateOutgoingBidirectionalStream, - (), + MOCK_METHOD(QuicSpdyStream*, CreateOutgoingBidirectionalStream, (), (override)); - MOCK_METHOD(QuicSpdyStream*, - CreateOutgoingUnidirectionalStream, - (), + MOCK_METHOD(QuicSpdyStream*, CreateOutgoingUnidirectionalStream, (), (override)); MOCK_METHOD(bool, ShouldCreateIncomingStream, (QuicStreamId id), (override)); MOCK_METHOD(bool, ShouldCreateOutgoingBidirectionalStream, (), (override)); @@ -1041,53 +898,37 @@ class MockQuicSpdySession : public QuicSpdySession { StreamSendingState state, TransmissionType type, EncryptionLevel level), (override)); - MOCK_METHOD(void, - MaybeSendRstStreamFrame, - (QuicStreamId stream_id, - QuicRstStreamErrorCode error, + MOCK_METHOD(void, MaybeSendRstStreamFrame, + (QuicStreamId stream_id, QuicResetStreamError error, QuicStreamOffset bytes_written), (override)); - MOCK_METHOD(void, - MaybeSendStopSendingFrame, - (QuicStreamId stream_id, QuicRstStreamErrorCode error), - (override)); - MOCK_METHOD(void, - SendWindowUpdate, - (QuicStreamId id, QuicStreamOffset byte_offset), - (override)); + MOCK_METHOD(void, MaybeSendStopSendingFrame, + (QuicStreamId stream_id, QuicResetStreamError error), (override)); + MOCK_METHOD(void, SendWindowUpdate, + (QuicStreamId id, QuicStreamOffset byte_offset), (override)); MOCK_METHOD(void, SendBlocked, (QuicStreamId id), (override)); - MOCK_METHOD(void, - OnStreamHeadersPriority, + MOCK_METHOD(void, OnStreamHeadersPriority, (QuicStreamId stream_id, const spdy::SpdyStreamPrecedence& precedence), (override)); - MOCK_METHOD(void, - OnStreamHeaderList, - (QuicStreamId stream_id, - bool fin, - size_t frame_len, + MOCK_METHOD(void, OnStreamHeaderList, + (QuicStreamId stream_id, bool fin, size_t frame_len, const QuicHeaderList& header_list), (override)); - MOCK_METHOD(void, - OnPromiseHeaderList, - (QuicStreamId stream_id, - QuicStreamId promised_stream_id, - size_t frame_len, - const QuicHeaderList& header_list), + MOCK_METHOD(void, OnPromiseHeaderList, + (QuicStreamId stream_id, QuicStreamId promised_stream_id, + size_t frame_len, const QuicHeaderList& header_list), (override)); - MOCK_METHOD(void, - OnPriorityFrame, + MOCK_METHOD(void, OnPriorityFrame, (QuicStreamId id, const spdy::SpdyStreamPrecedence& precedence), (override)); MOCK_METHOD(void, OnCongestionWindowChange, (QuicTime now), (override)); // Returns a QuicConsumedData that indicates all of |write_length| (and |fin| // if set) has been consumed. - QuicConsumedData ConsumeData(QuicStreamId id, - size_t write_length, + QuicConsumedData ConsumeData(QuicStreamId id, size_t write_length, QuicStreamOffset offset, - StreamSendingState state, - TransmissionType type, + StreamSendingState state, TransmissionType type, absl::optional<EncryptionLevel> level); using QuicSession::ActivateStream; @@ -1102,73 +943,45 @@ class MockHttp3DebugVisitor : public Http3DebugVisitor { MOCK_METHOD(void, OnQpackEncoderStreamCreated, (QuicStreamId), (override)); MOCK_METHOD(void, OnQpackDecoderStreamCreated, (QuicStreamId), (override)); MOCK_METHOD(void, OnPeerControlStreamCreated, (QuicStreamId), (override)); - MOCK_METHOD(void, - OnPeerQpackEncoderStreamCreated, - (QuicStreamId), + MOCK_METHOD(void, OnPeerQpackEncoderStreamCreated, (QuicStreamId), (override)); - MOCK_METHOD(void, - OnPeerQpackDecoderStreamCreated, - (QuicStreamId), + MOCK_METHOD(void, OnPeerQpackDecoderStreamCreated, (QuicStreamId), (override)); - MOCK_METHOD(void, - OnSettingsFrameReceivedViaAlps, - (const SettingsFrame&), + MOCK_METHOD(void, OnSettingsFrameReceivedViaAlps, (const SettingsFrame&), (override)); - MOCK_METHOD(void, - OnAcceptChFrameReceivedViaAlps, - (const AcceptChFrame&), + MOCK_METHOD(void, OnAcceptChFrameReceivedViaAlps, (const AcceptChFrame&), (override)); - MOCK_METHOD(void, - OnSettingsFrameReceived, - (const SettingsFrame&), + MOCK_METHOD(void, OnSettingsFrameReceived, (const SettingsFrame&), (override)); MOCK_METHOD(void, OnGoAwayFrameReceived, (const GoAwayFrame&), (override)); - MOCK_METHOD(void, - OnMaxPushIdFrameReceived, - (const MaxPushIdFrame&), + MOCK_METHOD(void, OnMaxPushIdFrameReceived, (const MaxPushIdFrame&), (override)); - MOCK_METHOD(void, - OnPriorityUpdateFrameReceived, - (const PriorityUpdateFrame&), + MOCK_METHOD(void, OnPriorityUpdateFrameReceived, (const PriorityUpdateFrame&), (override)); - MOCK_METHOD(void, - OnAcceptChFrameReceived, - (const AcceptChFrame&), + MOCK_METHOD(void, OnAcceptChFrameReceived, (const AcceptChFrame&), (override)); - MOCK_METHOD(void, - OnDataFrameReceived, - (QuicStreamId, QuicByteCount), - (override)); - MOCK_METHOD(void, - OnHeadersFrameReceived, - (QuicStreamId, QuicByteCount), + MOCK_METHOD(void, OnDataFrameReceived, (QuicStreamId, QuicByteCount), (override)); - MOCK_METHOD(void, - OnHeadersDecoded, - (QuicStreamId, QuicHeaderList), + MOCK_METHOD(void, OnHeadersFrameReceived, (QuicStreamId, QuicByteCount), (override)); - MOCK_METHOD(void, - OnUnknownFrameReceived, - (QuicStreamId, uint64_t, QuicByteCount), + MOCK_METHOD(void, OnHeadersDecoded, (QuicStreamId, QuicHeaderList), (override)); + MOCK_METHOD(void, OnUnknownFrameReceived, + (QuicStreamId, uint64_t, QuicByteCount), (override)); MOCK_METHOD(void, OnSettingsFrameSent, (const SettingsFrame&), (override)); MOCK_METHOD(void, OnGoAwayFrameSent, (QuicStreamId), (override)); MOCK_METHOD(void, OnMaxPushIdFrameSent, (const MaxPushIdFrame&), (override)); - MOCK_METHOD(void, - OnPriorityUpdateFrameSent, - (const PriorityUpdateFrame&), + MOCK_METHOD(void, OnPriorityUpdateFrameSent, (const PriorityUpdateFrame&), (override)); MOCK_METHOD(void, OnDataFrameSent, (QuicStreamId, QuicByteCount), (override)); - MOCK_METHOD(void, - OnHeadersFrameSent, - (QuicStreamId, const spdy::SpdyHeaderBlock&), - (override)); + MOCK_METHOD(void, OnHeadersFrameSent, + (QuicStreamId, const spdy::SpdyHeaderBlock&), (override)); }; class TestQuicSpdyServerSession : public QuicServerSessionBase { @@ -1184,26 +997,16 @@ class TestQuicSpdyServerSession : public QuicServerSessionBase { delete; ~TestQuicSpdyServerSession() override; - MOCK_METHOD(QuicSpdyStream*, - CreateIncomingStream, - (QuicStreamId id), + MOCK_METHOD(QuicSpdyStream*, CreateIncomingStream, (QuicStreamId id), (override)); - MOCK_METHOD(QuicSpdyStream*, - CreateIncomingStream, - (PendingStream*), + MOCK_METHOD(QuicSpdyStream*, CreateIncomingStream, (PendingStream*), (override)); - MOCK_METHOD(QuicSpdyStream*, - CreateOutgoingBidirectionalStream, - (), + MOCK_METHOD(QuicSpdyStream*, CreateOutgoingBidirectionalStream, (), (override)); - MOCK_METHOD(QuicSpdyStream*, - CreateOutgoingUnidirectionalStream, - (), + MOCK_METHOD(QuicSpdyStream*, CreateOutgoingUnidirectionalStream, (), (override)); - MOCK_METHOD(std::vector<absl::string_view>::const_iterator, - SelectAlpn, - (const std::vector<absl::string_view>&), - (const, override)); + MOCK_METHOD(std::vector<absl::string_view>::const_iterator, SelectAlpn, + (const std::vector<absl::string_view>&), (const, override)); MOCK_METHOD(void, OnAlpnSelected, (absl::string_view), (override)); std::unique_ptr<QuicCryptoServerStreamBase> CreateQuicCryptoServerStream( const QuicCryptoServerConfig* crypto_config, @@ -1215,14 +1018,22 @@ class TestQuicSpdyServerSession : public QuicServerSessionBase { MockQuicCryptoServerStreamHelper* helper() { return &helper_; } - QuicSSLConfig GetSSLConfig() const override { return ssl_config_; } + QuicSSLConfig GetSSLConfig() const override { + QuicSSLConfig ssl_config = QuicServerSessionBase::GetSSLConfig(); + if (early_data_enabled_.has_value()) { + ssl_config.early_data_enabled = *early_data_enabled_; + } + return ssl_config; + } - QuicSSLConfig* ssl_config() { return &ssl_config_; } + void set_early_data_enabled(bool enabled) { early_data_enabled_ = enabled; } private: MockQuicSessionVisitor visitor_; MockQuicCryptoServerStreamHelper helper_; - QuicSSLConfig ssl_config_; + // If not nullopt, override the early_data_enabled value from base class' + // ssl_config. + absl::optional<bool> early_data_enabled_; }; // A test implementation of QuicClientPushPromiseIndex::Delegate. @@ -1262,31 +1073,19 @@ class TestQuicSpdyClientSession : public QuicSpdyClientSessionBase { bool IsAuthorized(const std::string& authority) override; // QuicSpdyClientSessionBase - MOCK_METHOD(void, - OnProofValid, - (const QuicCryptoClientConfig::CachedState& cached), - (override)); - MOCK_METHOD(void, - OnProofVerifyDetailsAvailable, - (const ProofVerifyDetails& verify_details), - (override)); + MOCK_METHOD(void, OnProofValid, + (const QuicCryptoClientConfig::CachedState& cached), (override)); + MOCK_METHOD(void, OnProofVerifyDetailsAvailable, + (const ProofVerifyDetails& verify_details), (override)); // TestQuicSpdyClientSession - MOCK_METHOD(QuicSpdyStream*, - CreateIncomingStream, - (QuicStreamId id), + MOCK_METHOD(QuicSpdyStream*, CreateIncomingStream, (QuicStreamId id), (override)); - MOCK_METHOD(QuicSpdyStream*, - CreateIncomingStream, - (PendingStream*), + MOCK_METHOD(QuicSpdyStream*, CreateIncomingStream, (PendingStream*), (override)); - MOCK_METHOD(QuicSpdyStream*, - CreateOutgoingBidirectionalStream, - (), + MOCK_METHOD(QuicSpdyStream*, CreateOutgoingBidirectionalStream, (), (override)); - MOCK_METHOD(QuicSpdyStream*, - CreateOutgoingUnidirectionalStream, - (), + MOCK_METHOD(QuicSpdyStream*, CreateOutgoingUnidirectionalStream, (), (override)); MOCK_METHOD(bool, ShouldCreateIncomingStream, (QuicStreamId id), (override)); MOCK_METHOD(bool, ShouldCreateOutgoingBidirectionalStream, (), (override)); @@ -1326,24 +1125,17 @@ class MockPacketWriter : public QuicPacketWriter { MockPacketWriter& operator=(const MockPacketWriter&) = delete; ~MockPacketWriter() override; - MOCK_METHOD(WriteResult, - WritePacket, - (const char*, - size_t buf_len, - const QuicIpAddress& self_address, - const QuicSocketAddress& peer_address, - PerPacketOptions*), + MOCK_METHOD(WriteResult, WritePacket, + (const char*, size_t buf_len, const QuicIpAddress& self_address, + const QuicSocketAddress& peer_address, PerPacketOptions*), (override)); MOCK_METHOD(bool, IsWriteBlocked, (), (const, override)); MOCK_METHOD(void, SetWritable, (), (override)); - MOCK_METHOD(QuicByteCount, - GetMaxPacketSize, - (const QuicSocketAddress& peer_address), - (const, override)); + MOCK_METHOD(QuicByteCount, GetMaxPacketSize, + (const QuicSocketAddress& peer_address), (const, override)); MOCK_METHOD(bool, SupportsReleaseTime, (), (const, override)); MOCK_METHOD(bool, IsBatchMode, (), (const, override)); - MOCK_METHOD(QuicPacketBuffer, - GetNextWriteLocation, + MOCK_METHOD(QuicPacketBuffer, GetNextWriteLocation, (const QuicIpAddress& self_address, const QuicSocketAddress& peer_address), (override)); @@ -1357,32 +1149,19 @@ class MockSendAlgorithm : public SendAlgorithmInterface { MockSendAlgorithm& operator=(const MockSendAlgorithm&) = delete; ~MockSendAlgorithm() override; - MOCK_METHOD(void, - SetFromConfig, - (const QuicConfig& config, Perspective perspective), - (override)); - MOCK_METHOD(void, - ApplyConnectionOptions, - (const QuicTagVector& connection_options), - (override)); - MOCK_METHOD(void, - SetInitialCongestionWindowInPackets, - (QuicPacketCount packets), - (override)); - MOCK_METHOD(void, - OnCongestionEvent, - (bool rtt_updated, - QuicByteCount bytes_in_flight, - QuicTime event_time, - const AckedPacketVector& acked_packets, + MOCK_METHOD(void, SetFromConfig, + (const QuicConfig& config, Perspective perspective), (override)); + MOCK_METHOD(void, ApplyConnectionOptions, + (const QuicTagVector& connection_options), (override)); + MOCK_METHOD(void, SetInitialCongestionWindowInPackets, + (QuicPacketCount packets), (override)); + MOCK_METHOD(void, OnCongestionEvent, + (bool rtt_updated, QuicByteCount bytes_in_flight, + QuicTime event_time, const AckedPacketVector& acked_packets, const LostPacketVector& lost_packets), (override)); - MOCK_METHOD(void, - OnPacketSent, - (QuicTime, - QuicByteCount, - QuicPacketNumber, - QuicByteCount, + MOCK_METHOD(void, OnPacketSent, + (QuicTime, QuicByteCount, QuicPacketNumber, QuicByteCount, HasRetransmittableData), (override)); MOCK_METHOD(void, OnPacketNeutered, (QuicPacketNumber), (override)); @@ -1397,18 +1176,12 @@ class MockSendAlgorithm : public SendAlgorithmInterface { MOCK_METHOD(bool, InRecovery, (), (const, override)); MOCK_METHOD(bool, ShouldSendProbingPacket, (), (const, override)); MOCK_METHOD(QuicByteCount, GetSlowStartThreshold, (), (const, override)); - MOCK_METHOD(CongestionControlType, - GetCongestionControlType, - (), + MOCK_METHOD(CongestionControlType, GetCongestionControlType, (), (const, override)); - MOCK_METHOD(void, - AdjustNetworkParameters, - (const NetworkParams&), + MOCK_METHOD(void, AdjustNetworkParameters, (const NetworkParams&), (override)); MOCK_METHOD(void, OnApplicationLimited, (QuicByteCount), (override)); - MOCK_METHOD(void, - PopulateConnectionStats, - (QuicConnectionStats*), + MOCK_METHOD(void, PopulateConnectionStats, (QuicConnectionStats*), (const, override)); }; @@ -1419,28 +1192,19 @@ class MockLossAlgorithm : public LossDetectionInterface { MockLossAlgorithm& operator=(const MockLossAlgorithm&) = delete; ~MockLossAlgorithm() override; - MOCK_METHOD(void, - SetFromConfig, - (const QuicConfig& config, Perspective perspective), - (override)); + MOCK_METHOD(void, SetFromConfig, + (const QuicConfig& config, Perspective perspective), (override)); - MOCK_METHOD(DetectionStats, - DetectLosses, - (const QuicUnackedPacketMap& unacked_packets, - QuicTime time, + MOCK_METHOD(DetectionStats, DetectLosses, + (const QuicUnackedPacketMap& unacked_packets, QuicTime time, const RttStats& rtt_stats, QuicPacketNumber largest_recently_acked, - const AckedPacketVector& packets_acked, - LostPacketVector*), + const AckedPacketVector& packets_acked, LostPacketVector*), (override)); MOCK_METHOD(QuicTime, GetLossTimeout, (), (const, override)); - MOCK_METHOD(void, - SpuriousLossDetected, - (const QuicUnackedPacketMap&, - const RttStats&, - QuicTime, - QuicPacketNumber, - QuicPacketNumber), + MOCK_METHOD(void, SpuriousLossDetected, + (const QuicUnackedPacketMap&, const RttStats&, QuicTime, + QuicPacketNumber, QuicPacketNumber), (override)); MOCK_METHOD(void, OnConfigNegotiated, (), (override)); @@ -1456,14 +1220,10 @@ class MockAckListener : public QuicAckListenerInterface { MockAckListener(const MockAckListener&) = delete; MockAckListener& operator=(const MockAckListener&) = delete; - MOCK_METHOD(void, - OnPacketAcked, - (int acked_bytes, QuicTime::Delta ack_delay_time), - (override)); + MOCK_METHOD(void, OnPacketAcked, + (int acked_bytes, QuicTime::Delta ack_delay_time), (override)); - MOCK_METHOD(void, - OnPacketRetransmitted, - (int retransmitted_bytes), + MOCK_METHOD(void, OnPacketRetransmitted, (int retransmitted_bytes), (override)); protected: @@ -1488,29 +1248,18 @@ class MockQuicConnectionDebugVisitor : public QuicConnectionDebugVisitor { MockQuicConnectionDebugVisitor(); ~MockQuicConnectionDebugVisitor() override; - MOCK_METHOD(void, - OnPacketSent, - (QuicPacketNumber, - QuicPacketLength, - bool, - TransmissionType, - EncryptionLevel, - const QuicFrames&, - const QuicFrames&, - QuicTime), + MOCK_METHOD(void, OnPacketSent, + (QuicPacketNumber, QuicPacketLength, bool, TransmissionType, + EncryptionLevel, const QuicFrames&, const QuicFrames&, QuicTime), (override)); - MOCK_METHOD(void, - OnCoalescedPacketSent, - (const QuicCoalescedPacket&, size_t), + MOCK_METHOD(void, OnCoalescedPacketSent, (const QuicCoalescedPacket&, size_t), (override)); MOCK_METHOD(void, OnPingSent, (), (override)); - MOCK_METHOD(void, - OnPacketReceived, - (const QuicSocketAddress&, - const QuicSocketAddress&, + MOCK_METHOD(void, OnPacketReceived, + (const QuicSocketAddress&, const QuicSocketAddress&, const QuicEncryptedPacket&), (override)); @@ -1518,83 +1267,57 @@ class MockQuicConnectionDebugVisitor : public QuicConnectionDebugVisitor { MOCK_METHOD(void, OnProtocolVersionMismatch, (ParsedQuicVersion), (override)); - MOCK_METHOD(void, - OnPacketHeader, - (const QuicPacketHeader& header, - QuicTime receive_time, + MOCK_METHOD(void, OnPacketHeader, + (const QuicPacketHeader& header, QuicTime receive_time, EncryptionLevel level), (override)); - MOCK_METHOD(void, - OnSuccessfulVersionNegotiation, - (const ParsedQuicVersion&), + MOCK_METHOD(void, OnSuccessfulVersionNegotiation, (const ParsedQuicVersion&), (override)); MOCK_METHOD(void, OnStreamFrame, (const QuicStreamFrame&), (override)); MOCK_METHOD(void, OnCryptoFrame, (const QuicCryptoFrame&), (override)); - MOCK_METHOD(void, - OnStopWaitingFrame, - (const QuicStopWaitingFrame&), + MOCK_METHOD(void, OnStopWaitingFrame, (const QuicStopWaitingFrame&), (override)); MOCK_METHOD(void, OnRstStreamFrame, (const QuicRstStreamFrame&), (override)); - MOCK_METHOD(void, - OnConnectionCloseFrame, - (const QuicConnectionCloseFrame&), + MOCK_METHOD(void, OnConnectionCloseFrame, (const QuicConnectionCloseFrame&), (override)); MOCK_METHOD(void, OnBlockedFrame, (const QuicBlockedFrame&), (override)); - MOCK_METHOD(void, - OnNewConnectionIdFrame, - (const QuicNewConnectionIdFrame&), + MOCK_METHOD(void, OnNewConnectionIdFrame, (const QuicNewConnectionIdFrame&), (override)); - MOCK_METHOD(void, - OnRetireConnectionIdFrame, - (const QuicRetireConnectionIdFrame&), - (override)); + MOCK_METHOD(void, OnRetireConnectionIdFrame, + (const QuicRetireConnectionIdFrame&), (override)); MOCK_METHOD(void, OnNewTokenFrame, (const QuicNewTokenFrame&), (override)); MOCK_METHOD(void, OnMessageFrame, (const QuicMessageFrame&), (override)); - MOCK_METHOD(void, - OnStopSendingFrame, - (const QuicStopSendingFrame&), + MOCK_METHOD(void, OnStopSendingFrame, (const QuicStopSendingFrame&), (override)); - MOCK_METHOD(void, - OnPathChallengeFrame, - (const QuicPathChallengeFrame&), + MOCK_METHOD(void, OnPathChallengeFrame, (const QuicPathChallengeFrame&), (override)); - MOCK_METHOD(void, - OnPathResponseFrame, - (const QuicPathResponseFrame&), + MOCK_METHOD(void, OnPathResponseFrame, (const QuicPathResponseFrame&), (override)); - MOCK_METHOD(void, - OnPublicResetPacket, - (const QuicPublicResetPacket&), + MOCK_METHOD(void, OnPublicResetPacket, (const QuicPublicResetPacket&), (override)); - MOCK_METHOD(void, - OnVersionNegotiationPacket, - (const QuicVersionNegotiationPacket&), - (override)); + MOCK_METHOD(void, OnVersionNegotiationPacket, + (const QuicVersionNegotiationPacket&), (override)); - MOCK_METHOD(void, - OnTransportParametersSent, - (const TransportParameters&), + MOCK_METHOD(void, OnTransportParametersSent, (const TransportParameters&), (override)); - MOCK_METHOD(void, - OnTransportParametersReceived, - (const TransportParameters&), + MOCK_METHOD(void, OnTransportParametersReceived, (const TransportParameters&), (override)); MOCK_METHOD(void, OnZeroRttRejected, (int), (override)); @@ -1606,14 +1329,11 @@ class MockReceivedPacketManager : public QuicReceivedPacketManager { explicit MockReceivedPacketManager(QuicConnectionStats* stats); ~MockReceivedPacketManager() override; - MOCK_METHOD(void, - RecordPacketReceived, + MOCK_METHOD(void, RecordPacketReceived, (const QuicPacketHeader& header, QuicTime receipt_time), (override)); MOCK_METHOD(bool, IsMissing, (QuicPacketNumber packet_number), (override)); - MOCK_METHOD(bool, - IsAwaitingPacket, - (QuicPacketNumber packet_number), + MOCK_METHOD(bool, IsAwaitingPacket, (QuicPacketNumber packet_number), (const, override)); MOCK_METHOD(bool, HasNewMissingPackets, (), (const, override)); MOCK_METHOD(bool, ack_frame_updated, (), (const, override)); @@ -1629,22 +1349,15 @@ class MockPacketCreatorDelegate : public QuicPacketCreator::DelegateInterface { MOCK_METHOD(QuicPacketBuffer, GetPacketBuffer, (), (override)); MOCK_METHOD(void, OnSerializedPacket, (SerializedPacket), (override)); - MOCK_METHOD(void, - OnUnrecoverableError, - (QuicErrorCode, const std::string&), + MOCK_METHOD(void, OnUnrecoverableError, (QuicErrorCode, const std::string&), (override)); - MOCK_METHOD(bool, - ShouldGeneratePacket, + MOCK_METHOD(bool, ShouldGeneratePacket, (HasRetransmittableData retransmittable, IsHandshake handshake), (override)); - MOCK_METHOD(const QuicFrames, - MaybeBundleAckOpportunistically, - (), - (override)); - MOCK_METHOD(SerializedPacketFate, - GetSerializedPacketFate, - (bool, EncryptionLevel), + MOCK_METHOD(const QuicFrames, MaybeBundleAckOpportunistically, (), (override)); + MOCK_METHOD(SerializedPacketFate, GetSerializedPacketFate, + (bool, EncryptionLevel), (override)); }; class MockSessionNotifier : public SessionNotifierInterface { @@ -1652,19 +1365,13 @@ class MockSessionNotifier : public SessionNotifierInterface { MockSessionNotifier(); ~MockSessionNotifier() override; - MOCK_METHOD(bool, - OnFrameAcked, - (const QuicFrame&, QuicTime::Delta, QuicTime), + MOCK_METHOD(bool, OnFrameAcked, (const QuicFrame&, QuicTime::Delta, QuicTime), (override)); - MOCK_METHOD(void, - OnStreamFrameRetransmitted, - (const QuicStreamFrame&), + MOCK_METHOD(void, OnStreamFrameRetransmitted, (const QuicStreamFrame&), (override)); MOCK_METHOD(void, OnFrameLost, (const QuicFrame&), (override)); - MOCK_METHOD(void, - RetransmitFrames, - (const QuicFrames&, TransmissionType type), - (override)); + MOCK_METHOD(void, RetransmitFrames, + (const QuicFrames&, TransmissionType type), (override)); MOCK_METHOD(bool, IsFrameOutstanding, (const QuicFrame&), (const, override)); MOCK_METHOD(bool, HasUnackedCryptoData, (), (const, override)); MOCK_METHOD(bool, HasUnackedStreamData, (), (const, override)); @@ -1676,8 +1383,7 @@ class MockQuicPathValidationContext : public QuicPathValidationContext { const QuicSocketAddress& peer_address, const QuicSocketAddress& effective_peer_address, QuicPacketWriter* writer) - : QuicPathValidationContext(self_address, - peer_address, + : QuicPathValidationContext(self_address, peer_address, effective_peer_address), writer_(writer) {} QuicPacketWriter* WriterToUse() override { return writer_; } @@ -1689,15 +1395,11 @@ class MockQuicPathValidationContext : public QuicPathValidationContext { class MockQuicPathValidationResultDelegate : public QuicPathValidator::ResultDelegate { public: - MOCK_METHOD(void, - OnPathValidationSuccess, - (std::unique_ptr<QuicPathValidationContext>), - (override)); + MOCK_METHOD(void, OnPathValidationSuccess, + (std::unique_ptr<QuicPathValidationContext>), (override)); - MOCK_METHOD(void, - OnPathValidationFailure, - (std::unique_ptr<QuicPathValidationContext>), - (override)); + MOCK_METHOD(void, OnPathValidationFailure, + (std::unique_ptr<QuicPathValidationContext>), (override)); }; class QuicCryptoClientStreamPeer { @@ -1724,11 +1426,9 @@ class QuicCryptoClientStreamPeer { // client_session: Pointer reference for the newly created client // session. The new object will be owned by the caller. void CreateClientSessionForTest( - QuicServerId server_id, - QuicTime::Delta connection_start_time, + QuicServerId server_id, QuicTime::Delta connection_start_time, const ParsedQuicVersionVector& supported_versions, - MockQuicConnectionHelper* helper, - MockAlarmFactory* alarm_factory, + MockQuicConnectionHelper* helper, MockAlarmFactory* alarm_factory, QuicCryptoClientConfig* crypto_client_config, PacketSavingConnection** client_connection, TestQuicSpdyClientSession** client_session); @@ -1749,11 +1449,9 @@ void CreateClientSessionForTest( // server_session: Pointer reference for the newly created server // session. The new object will be owned by the caller. void CreateServerSessionForTest( - QuicServerId server_id, - QuicTime::Delta connection_start_time, + QuicServerId server_id, QuicTime::Delta connection_start_time, ParsedQuicVersionVector supported_versions, - MockQuicConnectionHelper* helper, - MockAlarmFactory* alarm_factory, + MockQuicConnectionHelper* helper, MockAlarmFactory* alarm_factory, QuicCryptoServerConfig* crypto_server_config, QuicCompressedCertsCache* compressed_certs_cache, PacketSavingConnection** server_connection, @@ -1805,30 +1503,18 @@ inline void MakeIOVector(absl::string_view str, struct iovec* iov) { // HTTP stream numbering scheme (i.e. whether one or two QUIC streams are used // per HTTP transaction). QuicStreamId GetNthClientInitiatedBidirectionalStreamId( - QuicTransportVersion version, - int n); + QuicTransportVersion version, int n); QuicStreamId GetNthServerInitiatedBidirectionalStreamId( - QuicTransportVersion version, - int n); + QuicTransportVersion version, int n); QuicStreamId GetNthServerInitiatedUnidirectionalStreamId( - QuicTransportVersion version, - int n); + QuicTransportVersion version, int n); QuicStreamId GetNthClientInitiatedUnidirectionalStreamId( - QuicTransportVersion version, - int n); + QuicTransportVersion version, int n); -StreamType DetermineStreamType(QuicStreamId id, - ParsedQuicVersion version, - Perspective perspective, - bool is_incoming, +StreamType DetermineStreamType(QuicStreamId id, ParsedQuicVersion version, + Perspective perspective, bool is_incoming, StreamType default_type); -// Utility function that stores message_data in |storage| and returns a -// QuicMemSliceSpan. -QuicMemSliceSpan MakeSpan(QuicBufferAllocator* allocator, - absl::string_view message_data, - QuicMemSliceStorage* storage); - // Creates a MemSlice using a singleton trivial buffer allocator. Performs a // copy. QuicMemSlice MemSliceFromString(absl::string_view data); @@ -1842,15 +1528,12 @@ MATCHER_P(ReceivedPacketInfoConnectionIdEquals, destination_connection_id, "") { return arg.destination_connection_id == destination_connection_id; } -MATCHER_P2(InRange, min, max, "") { - return arg >= min && arg <= max; -} +MATCHER_P2(InRange, min, max, "") { return arg >= min && arg <= max; } // A GMock matcher that prints expected and actual QuicErrorCode strings // upon failure. Example usage: // EXPECT_THAT(stream_->connection_error(), IsError(QUIC_INTERNAL_ERROR)); -MATCHER_P(IsError, - expected, +MATCHER_P(IsError, expected, absl::StrCat(negation ? "isn't equal to " : "is equal to ", QuicErrorCodeToString(expected))) { *result_listener << QuicErrorCodeToString(static_cast<QuicErrorCode>(arg)); @@ -1869,8 +1552,7 @@ MATCHER(IsQuicNoError, // A GMock matcher that prints expected and actual QuicRstStreamErrorCode // strings upon failure. Example usage: // EXPECT_THAT(stream_->stream_error(), IsStreamError(QUIC_INTERNAL_ERROR)); -MATCHER_P(IsStreamError, - expected, +MATCHER_P(IsStreamError, expected, absl::StrCat(negation ? "isn't equal to " : "is equal to ", QuicRstStreamErrorCodeToString(expected))) { *result_listener << QuicRstStreamErrorCodeToString(arg); @@ -1908,12 +1590,9 @@ class TaggingEncrypter : public QuicEncrypter { return true; } - bool EncryptPacket(uint64_t packet_number, - absl::string_view associated_data, - absl::string_view plaintext, - char* output, - size_t* output_length, - size_t max_output_length) override; + bool EncryptPacket(uint64_t packet_number, absl::string_view associated_data, + absl::string_view plaintext, char* output, + size_t* output_length, size_t max_output_length) override; std::string GenerateHeaderProtectionMask( absl::string_view /*sample*/) override { @@ -1978,12 +1657,9 @@ class TaggingDecrypter : public QuicDecrypter { return true; } - bool DecryptPacket(uint64_t packet_number, - absl::string_view associated_data, - absl::string_view ciphertext, - char* output, - size_t* output_length, - size_t max_output_length) override; + bool DecryptPacket(uint64_t packet_number, absl::string_view associated_data, + absl::string_view ciphertext, char* output, + size_t* output_length, size_t max_output_length) override; std::string GenerateHeaderProtectionMask( QuicDataReader* /*sample_reader*/) override { @@ -2040,8 +1716,7 @@ class TestPacketWriter : public QuicPacketWriter { }; public: - TestPacketWriter(ParsedQuicVersion version, - MockClock* clock, + TestPacketWriter(ParsedQuicVersion version, MockClock* clock, Perspective perspective); TestPacketWriter(const TestPacketWriter&) = delete; @@ -2050,8 +1725,7 @@ class TestPacketWriter : public QuicPacketWriter { ~TestPacketWriter() override; // QuicPacketWriter interface - WriteResult WritePacket(const char* buffer, - size_t buf_len, + WriteResult WritePacket(const char* buffer, size_t buf_len, const QuicIpAddress& self_address, const QuicSocketAddress& peer_address, PerPacketOptions* options) override; @@ -2277,8 +1951,7 @@ class TestPacketWriter : public QuicPacketWriter { // contents of the destination connection ID passed in to // WriteClientVersionNegotiationProbePacket. bool ParseClientVersionNegotiationProbePacket( - const char* packet_bytes, - size_t packet_length, + const char* packet_bytes, size_t packet_length, char* destination_connection_id_bytes, uint8_t* destination_connection_id_length_out); @@ -2291,8 +1964,7 @@ bool ParseClientVersionNegotiationProbePacket( // the source connection ID, and must point to |source_connection_id_length| // bytes of memory. bool WriteServerVersionNegotiationProbeResponse( - char* packet_bytes, - size_t* packet_length_out, + char* packet_bytes, size_t* packet_length_out, const char* source_connection_id_bytes, uint8_t source_connection_id_length); @@ -2330,13 +2002,14 @@ class MockHttp3DatagramRegistrationVisitor MOCK_METHOD(void, OnContextReceived, (QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& extensions), + DatagramFormatType format_type, + absl::string_view format_additional_data), (override)); MOCK_METHOD(void, OnContextClosed, (QuicStreamId stream_id, absl::optional<QuicDatagramContextId> context_id, - const Http3DatagramContextExtensions& extensions), + ContextCloseCode close_code, absl::string_view close_details), (override)); }; diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.cc index 8f614e8d89d..cbcd36e1550 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.cc @@ -36,5 +36,11 @@ bool QuicTimeWaitListManagerPeer::SendOrQueuePacket( return manager->SendOrQueuePacket(std::move(packet), packet_context); } +// static +size_t QuicTimeWaitListManagerPeer::PendingPacketsQueueSize( + QuicTimeWaitListManager* manager) { + return manager->pending_packets_queue_.size(); +} + } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.h index 4e0a4a707ad..a314e039591 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.h @@ -26,6 +26,8 @@ class QuicTimeWaitListManagerPeer { QuicTimeWaitListManager* manager, std::unique_ptr<QuicTimeWaitListManager::QueuedPacket> packet, const QuicPerPacketContext* packet_context); + + static size_t PendingPacketsQueueSize(QuicTimeWaitListManager* manager); }; } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_transport_test_tools.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_transport_test_tools.h index df52093e05a..5babfd280cb 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_transport_test_tools.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_transport_test_tools.h @@ -14,7 +14,9 @@ namespace test { class MockClientVisitor : public WebTransportVisitor { public: - MOCK_METHOD(void, OnSessionReady, (), (override)); + MOCK_METHOD(void, OnSessionReady, (const spdy::SpdyHeaderBlock&), (override)); + MOCK_METHOD(void, OnSessionClosed, + (WebTransportSessionError, const std::string&), (override)); MOCK_METHOD(void, OnIncomingBidirectionalStreamAvailable, (), (override)); MOCK_METHOD(void, OnIncomingUnidirectionalStreamAvailable, (), (override)); MOCK_METHOD(void, OnDatagramReceived, (absl::string_view), (override)); @@ -32,6 +34,12 @@ class MockStreamVisitor : public WebTransportStreamVisitor { public: MOCK_METHOD(void, OnCanRead, (), (override)); MOCK_METHOD(void, OnCanWrite, (), (override)); + + MOCK_METHOD(void, OnResetStreamReceived, (WebTransportStreamError error), + (override)); + MOCK_METHOD(void, OnStopSendingReceived, (WebTransportStreamError error), + (override)); + MOCK_METHOD(void, OnWriteSideInDataRecvdState, (), (override)); }; } // namespace test diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.cc index a1383b0e41d..ce498ca2d95 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.cc @@ -111,6 +111,21 @@ void SimpleSessionNotifier::WriteOrBufferRstStream( WriteBufferedControlFrames(); } +void SimpleSessionNotifier::WriteOrBufferWindowUpate( + QuicStreamId id, QuicStreamOffset byte_offset) { + QUIC_DVLOG(1) << "Writing WINDOW_UPDATE"; + const bool had_buffered_data = + HasBufferedStreamData() || HasBufferedControlFrames(); + QuicControlFrameId control_frame_id = ++last_control_frame_id_; + control_frames_.emplace_back(( + QuicFrame(new QuicWindowUpdateFrame(control_frame_id, id, byte_offset)))); + if (had_buffered_data) { + QUIC_DLOG(WARNING) << "Connection is write blocked"; + return; + } + WriteBufferedControlFrames(); +} + void SimpleSessionNotifier::WriteOrBufferPing() { QUIC_DVLOG(1) << "Writing PING_FRAME"; const bool had_buffered_data = @@ -175,8 +190,7 @@ void SimpleSessionNotifier::OnCanWrite() { !RetransmitLostStreamData()) { return; } - // Write buffered control frames. - if (!WriteBufferedControlFrames()) { + if (!WriteBufferedCryptoData() || !WriteBufferedControlFrames()) { return; } // Write new data. @@ -666,6 +680,26 @@ bool SimpleSessionNotifier::RetransmitLostStreamData() { return !HasLostStreamData(); } +bool SimpleSessionNotifier::WriteBufferedCryptoData() { + for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) { + const StreamState& state = crypto_state_[i]; + QuicIntervalSet<QuicStreamOffset> buffered_crypto_data(0, + state.bytes_total); + buffered_crypto_data.Difference(crypto_bytes_transferred_[i]); + for (const auto& interval : buffered_crypto_data) { + size_t bytes_written = connection_->SendCryptoData( + static_cast<EncryptionLevel>(i), interval.Length(), interval.min()); + crypto_state_[i].bytes_sent += bytes_written; + crypto_bytes_transferred_[i].Add(interval.min(), + interval.min() + bytes_written); + if (bytes_written < interval.Length()) { + return false; + } + } + } + return true; +} + bool SimpleSessionNotifier::WriteBufferedControlFrames() { while (HasBufferedControlFrames()) { QuicFrame frame_to_send = 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 8bdadb2f45d..5712884fcd2 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 @@ -34,6 +34,10 @@ class SimpleSessionNotifier : public SessionNotifierInterface { void WriteOrBufferRstStream(QuicStreamId id, QuicRstStreamErrorCode error, QuicStreamOffset bytes_written); + + // Tries to write WINDOW_UPDATE. + void WriteOrBufferWindowUpate(QuicStreamId id, QuicStreamOffset byte_offset); + // Tries to write PING. void WriteOrBufferPing(); @@ -127,6 +131,8 @@ class SimpleSessionNotifier : public SessionNotifierInterface { bool WriteBufferedControlFrames(); + bool WriteBufferedCryptoData(); + bool IsControlFrameOutstanding(const QuicFrame& frame) const; bool HasBufferedControlFrames() const; diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.h b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.h index 7e4be833690..dd810e7e2f5 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.h @@ -73,7 +73,7 @@ class Queue : public Actor, public UnconstrainedPortInterface { }; // Alarm handler for aggregation timeout. - class AggregationAlarmDelegate : public QuicAlarm::Delegate { + class AggregationAlarmDelegate : public QuicAlarm::DelegateWithoutContext { public: explicit AggregationAlarmDelegate(Queue* queue); diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator.h b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator.h index 8f31717e712..b4298f53e50 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator.h @@ -88,7 +88,7 @@ class Simulator : public QuicConnectionHelperInterface { }; // The delegate used for RunFor(). - class RunForDelegate : public QuicAlarm::Delegate { + class RunForDelegate : public QuicAlarm::DelegateWithoutContext { public: explicit RunForDelegate(bool* run_for_should_stop); void OnAlarm() override; diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator_test.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator_test.cc index a71649eb4b5..fe841703cf4 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator_test.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator_test.cc @@ -475,7 +475,7 @@ class AlarmToggler : public Actor { }; // Counts the number of times an alarm has fired. -class CounterDelegate : public QuicAlarm::Delegate { +class CounterDelegate : public QuicAlarm::DelegateWithoutContext { public: explicit CounterDelegate(size_t* counter) : counter_(counter) {} diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/web_transport_resets_backend.cc b/chromium/net/third_party/quiche/src/quic/test_tools/web_transport_resets_backend.cc new file mode 100644 index 00000000000..512619fee0a --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/test_tools/web_transport_resets_backend.cc @@ -0,0 +1,113 @@ +// Copyright (c) 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "quic/test_tools/web_transport_resets_backend.h" + +#include <memory> + +#include "quic/core/web_transport_interface.h" +#include "quic/tools/web_transport_test_visitors.h" +#include "common/quiche_circular_deque.h" + +namespace quic { +namespace test { + +namespace { + +class ResetsVisitor; + +class BidirectionalEchoVisitorWithLogging + : public WebTransportBidirectionalEchoVisitor { + public: + BidirectionalEchoVisitorWithLogging(WebTransportStream* stream, + ResetsVisitor* session_visitor) + : WebTransportBidirectionalEchoVisitor(stream), + session_visitor_(session_visitor) {} + + void OnResetStreamReceived(WebTransportStreamError error) override; + void OnStopSendingReceived(WebTransportStreamError error) override; + + private: + ResetsVisitor* session_visitor_; // Not owned. +}; + +class ResetsVisitor : public WebTransportVisitor { + public: + ResetsVisitor(WebTransportSession* session) : session_(session) {} + + void OnSessionReady(const spdy::SpdyHeaderBlock& /*headers*/) override {} + void OnSessionClosed(WebTransportSessionError /*error_code*/, + const std::string& /*error_message*/) override {} + + void OnIncomingBidirectionalStreamAvailable() override { + while (true) { + WebTransportStream* stream = + session_->AcceptIncomingBidirectionalStream(); + if (stream == nullptr) { + return; + } + stream->SetVisitor( + std::make_unique<BidirectionalEchoVisitorWithLogging>(stream, this)); + stream->visitor()->OnCanRead(); + } + } + void OnIncomingUnidirectionalStreamAvailable() override {} + + void OnDatagramReceived(absl::string_view /*datagram*/) override {} + + void OnCanCreateNewOutgoingBidirectionalStream() override {} + void OnCanCreateNewOutgoingUnidirectionalStream() override { + MaybeSendLogsBack(); + } + + void Log(std::string line) { + log_.push_back(std::move(line)); + MaybeSendLogsBack(); + } + + private: + void MaybeSendLogsBack() { + while (!log_.empty() && + session_->CanOpenNextOutgoingUnidirectionalStream()) { + WebTransportStream* stream = session_->OpenOutgoingUnidirectionalStream(); + stream->SetVisitor( + std::make_unique<WebTransportUnidirectionalEchoWriteVisitor>( + stream, log_.front())); + log_.pop_front(); + stream->visitor()->OnCanWrite(); + } + } + + WebTransportSession* session_; // Not owned. + quiche::QuicheCircularDeque<std::string> log_; +}; + +void BidirectionalEchoVisitorWithLogging::OnResetStreamReceived( + WebTransportStreamError error) { + session_visitor_->Log(absl::StrCat("Received reset for stream ", + stream()->GetStreamId(), + " with error code ", error)); + WebTransportBidirectionalEchoVisitor::OnResetStreamReceived(error); +} +void BidirectionalEchoVisitorWithLogging::OnStopSendingReceived( + WebTransportStreamError error) { + session_visitor_->Log(absl::StrCat("Received stop sending for stream ", + stream()->GetStreamId(), + " with error code ", error)); + WebTransportBidirectionalEchoVisitor::OnStopSendingReceived(error); +} + +} // namespace + +QuicSimpleServerBackend::WebTransportResponse WebTransportResetsBackend( + const spdy::Http2HeaderBlock& /*request_headers*/, + WebTransportSession* session) { + QuicSimpleServerBackend::WebTransportResponse response; + response.response_headers[":status"] = "200"; + response.visitor = std::make_unique<ResetsVisitor>(session); + return response; +} + +} // namespace test +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/web_transport_resets_backend.h b/chromium/net/third_party/quiche/src/quic/test_tools/web_transport_resets_backend.h new file mode 100644 index 00000000000..dda06be521f --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/test_tools/web_transport_resets_backend.h @@ -0,0 +1,23 @@ +// Copyright (c) 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_TEST_TOOLS_WEB_TRANSPORT_RESETS_BACKEND_H_ +#define QUICHE_QUIC_TEST_TOOLS_WEB_TRANSPORT_RESETS_BACKEND_H_ + +#include "quic/test_tools/quic_test_backend.h" + +namespace quic { +namespace test { + +// A backend for testing RESET_STREAM/STOP_SENDING behavior. Provides +// bidirectional echo streams; whenever one of those receives RESET_STREAM or +// STOP_SENDING, a log message is sent as a unidirectional stream. +QuicSimpleServerBackend::WebTransportResponse WebTransportResetsBackend( + const spdy::Http2HeaderBlock& request_headers, + WebTransportSession* session); + +} // namespace test +} // namespace quic + +#endif // QUICHE_QUIC_TEST_TOOLS_WEB_TRANSPORT_RESETS_BACKEND_H_ diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend_test.cc index 71f5dc5bbb4..79dac6bf539 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend_test.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend_test.cc @@ -77,7 +77,13 @@ TEST_F(QuicMemoryCacheBackendTest, AddResponse) { EXPECT_EQ(response->trailers(), response_trailers); } -TEST_F(QuicMemoryCacheBackendTest, ReadsCacheDir) { +// TODO(crbug.com/1249712) This test is failing on iOS. +#if defined(OS_IOS) +#define MAYBE_ReadsCacheDir DISABLED_ReadsCacheDir +#else +#define MAYBE_ReadsCacheDir ReadsCacheDir +#endif +TEST_F(QuicMemoryCacheBackendTest, MAYBE_ReadsCacheDir) { cache_.InitializeBackend(CacheDirectory()); const Response* response = cache_.GetResponse("test.example.com", "/index.html"); @@ -89,21 +95,43 @@ TEST_F(QuicMemoryCacheBackendTest, ReadsCacheDir) { EXPECT_LT(0U, response->body().length()); } -TEST_F(QuicMemoryCacheBackendTest, ReadsCacheDirWithServerPushResource) { +// TODO(crbug.com/1249712) This test is failing on iOS. +#if defined(OS_IOS) +#define MAYBE_ReadsCacheDirWithServerPushResource \ + DISABLED_ReadsCacheDirWithServerPushResource +#else +#define MAYBE_ReadsCacheDirWithServerPushResource \ + ReadsCacheDirWithServerPushResource +#endif +TEST_F(QuicMemoryCacheBackendTest, MAYBE_ReadsCacheDirWithServerPushResource) { cache_.InitializeBackend(CacheDirectory() + "_with_push"); std::list<ServerPushInfo> resources = cache_.GetServerPushResources("test.example.com/"); ASSERT_EQ(1UL, resources.size()); } -TEST_F(QuicMemoryCacheBackendTest, ReadsCacheDirWithServerPushResources) { +// TODO(crbug.com/1249712) This test is failing on iOS. +#if defined(OS_IOS) +#define MAYBE_ReadsCacheDirWithServerPushResources \ + DISABLED_ReadsCacheDirWithServerPushResources +#else +#define MAYBE_ReadsCacheDirWithServerPushResources \ + ReadsCacheDirWithServerPushResources +#endif +TEST_F(QuicMemoryCacheBackendTest, MAYBE_ReadsCacheDirWithServerPushResources) { cache_.InitializeBackend(CacheDirectory() + "_with_push"); std::list<ServerPushInfo> resources = cache_.GetServerPushResources("test.example.com/index2.html"); ASSERT_EQ(2UL, resources.size()); } -TEST_F(QuicMemoryCacheBackendTest, UsesOriginalUrl) { +// TODO(crbug.com/1249712) This test is failing on iOS. +#if defined(OS_IOS) +#define MAYBE_UsesOriginalUrl DISABLED_UsesOriginalUrl +#else +#define MAYBE_UsesOriginalUrl UsesOriginalUrl +#endif +TEST_F(QuicMemoryCacheBackendTest, MAYBE_UsesOriginalUrl) { cache_.InitializeBackend(CacheDirectory()); const Response* response = cache_.GetResponse("test.example.com", "/site_map.html"); @@ -115,7 +143,13 @@ TEST_F(QuicMemoryCacheBackendTest, UsesOriginalUrl) { EXPECT_LT(0U, response->body().length()); } -TEST_F(QuicMemoryCacheBackendTest, UsesOriginalUrlOnly) { +// TODO(crbug.com/1249712) This test is failing on iOS. +#if defined(OS_IOS) +#define MAYBE_UsesOriginalUrlOnly DISABLED_UsesOriginalUrlOnly +#else +#define MAYBE_UsesOriginalUrlOnly UsesOriginalUrlOnly +#endif +TEST_F(QuicMemoryCacheBackendTest, MAYBE_UsesOriginalUrlOnly) { // Tests that if the URL cannot be inferred correctly from the path // because the directory does not include the hostname, that the // X-Original-Url header's value will be used. diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.cc index 07c46289da8..47c521bf5b5 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.cc @@ -54,8 +54,9 @@ bool QuicSimpleClientSession::ShouldNegotiateWebTransport() { return enable_web_transport_; } -bool QuicSimpleClientSession::ShouldNegotiateHttp3Datagram() { - return enable_web_transport_; +HttpDatagramSupport QuicSimpleClientSession::LocalHttpDatagramSupport() { + return enable_web_transport_ ? HttpDatagramSupport::kDraft04 + : HttpDatagramSupport::kNone; } } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.h index 124e56f8bf1..1a6e694287a 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.h @@ -30,7 +30,7 @@ class QuicSimpleClientSession : public QuicSpdyClientSession { std::unique_ptr<QuicSpdyClientStream> CreateClientStream() override; bool ShouldNegotiateWebTransport() override; - bool ShouldNegotiateHttp3Datagram() override; + HttpDatagramSupport LocalHttpDatagramSupport() override; private: const bool drop_response_body_; 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 688ca9ff24b..d1e9a73e635 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 @@ -10,6 +10,7 @@ #include "quic/core/http/quic_server_initiated_spdy_stream.h" #include "quic/core/http/quic_spdy_session.h" #include "quic/core/quic_connection.h" +#include "quic/core/quic_types.h" #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" @@ -18,30 +19,21 @@ namespace quic { QuicSimpleServerSession::QuicSimpleServerSession( - const QuicConfig& config, - const ParsedQuicVersionVector& supported_versions, - QuicConnection* connection, - QuicSession::Visitor* visitor, + const QuicConfig& config, const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, QuicSession::Visitor* visitor, QuicCryptoServerStreamBase::Helper* helper, const QuicCryptoServerConfig* crypto_config, QuicCompressedCertsCache* compressed_certs_cache, QuicSimpleServerBackend* quic_simple_server_backend) - : QuicServerSessionBase(config, - supported_versions, - connection, - visitor, - helper, - crypto_config, - compressed_certs_cache), + : QuicServerSessionBase(config, supported_versions, connection, visitor, + helper, crypto_config, compressed_certs_cache), highest_promised_stream_id_( QuicUtils::GetInvalidStreamId(connection->transport_version())), quic_simple_server_backend_(quic_simple_server_backend) { QUICHE_DCHECK(quic_simple_server_backend_); } -QuicSimpleServerSession::~QuicSimpleServerSession() { - DeleteConnection(); -} +QuicSimpleServerSession::~QuicSimpleServerSession() { DeleteConnection(); } std::unique_ptr<QuicCryptoServerStreamBase> QuicSimpleServerSession::CreateQuicCryptoServerStream( @@ -75,8 +67,8 @@ QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream(QuicStreamId id) { QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream( PendingStream* pending) { - QuicSpdyStream* stream = new QuicSimpleServerStream( - pending, this, BIDIRECTIONAL, quic_simple_server_backend_); + QuicSpdyStream* stream = + new QuicSimpleServerStream(pending, this, quic_simple_server_backend_); ActivateStream(absl::WrapUnique(stream)); return stream; } @@ -144,15 +136,15 @@ void QuicSimpleServerSession::HandleRstOnValidNonexistentStream( promised_streams_[index].is_cancelled = true; } } - control_frame_manager().WriteOrBufferRstStream(frame.stream_id, - QUIC_RST_ACKNOWLEDGEMENT, 0); + control_frame_manager().WriteOrBufferRstStream( + frame.stream_id, + QuicResetStreamError::FromInternal(QUIC_RST_ACKNOWLEDGEMENT), 0); connection()->OnStreamReset(frame.stream_id, QUIC_RST_ACKNOWLEDGEMENT); } } spdy::Http2HeaderBlock QuicSimpleServerSession::SynthesizePushRequestHeaders( - std::string request_url, - QuicBackendResponse::ServerPushInfo resource, + std::string request_url, QuicBackendResponse::ServerPushInfo resource, const spdy::Http2HeaderBlock& original_request_headers) { QuicUrl push_request_url = resource.request_url; diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.h index 2cc3dcf524f..9746f2697cc 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.h @@ -96,9 +96,11 @@ class QuicSimpleServerSession : public QuicServerSessionBase { bool ShouldNegotiateWebTransport() override { return quic_simple_server_backend_->SupportsWebTransport(); } - bool ShouldNegotiateHttp3Datagram() override { - return QuicServerSessionBase::ShouldNegotiateHttp3Datagram() || - ShouldNegotiateWebTransport(); + HttpDatagramSupport LocalHttpDatagramSupport() override { + if (ShouldNegotiateWebTransport()) { + return HttpDatagramSupport::kDraft00And04; + } + return QuicServerSessionBase::LocalHttpDatagramSupport(); } private: diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc index 5cd365298f3..d7e2a70eb46 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc @@ -38,11 +38,9 @@ QuicSimpleServerStream::QuicSimpleServerStream( } QuicSimpleServerStream::QuicSimpleServerStream( - PendingStream* pending, - QuicSpdySession* session, - StreamType type, + PendingStream* pending, QuicSpdySession* session, QuicSimpleServerBackend* quic_simple_server_backend) - : QuicSpdyServerStreamBase(pending, session, type), + : QuicSpdyServerStreamBase(pending, session), content_length_(-1), generate_bytes_length_(0), quic_simple_server_backend_(quic_simple_server_backend) { diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h index 6395345518b..d138492a2ce 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h @@ -25,7 +25,6 @@ class QuicSimpleServerStream : public QuicSpdyServerStreamBase, QuicSimpleServerBackend* quic_simple_server_backend); QuicSimpleServerStream(PendingStream* pending, QuicSpdySession* session, - StreamType type, QuicSimpleServerBackend* quic_simple_server_backend); QuicSimpleServerStream(const QuicSimpleServerStream&) = delete; QuicSimpleServerStream& operator=(const QuicSimpleServerStream&) = delete; 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 5c9cd2274d9..aa0770399f9 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 @@ -48,13 +48,9 @@ const size_t kDataFrameHeaderLength = 2; class TestStream : public QuicSimpleServerStream { public: - TestStream(QuicStreamId stream_id, - QuicSpdySession* session, - StreamType type, + TestStream(QuicStreamId stream_id, QuicSpdySession* session, StreamType type, QuicSimpleServerBackend* quic_simple_server_backend) - : QuicSimpleServerStream(stream_id, - session, - type, + : QuicSimpleServerStream(stream_id, session, type, quic_simple_server_backend) {} ~TestStream() override = default; @@ -62,8 +58,7 @@ class TestStream : public QuicSimpleServerStream { MOCK_METHOD(void, WriteHeadersMock, (bool fin), ()); MOCK_METHOD(void, WriteEarlyHintsHeadersMock, (bool fin), ()); - size_t WriteHeaders(spdy::Http2HeaderBlock header_block, - bool fin, + size_t WriteHeaders(spdy::Http2HeaderBlock header_block, bool fin, QuicReferenceCountedPointer<QuicAckListenerInterface> /*ack_listener*/) override { if (header_block[":status"] == "103") { @@ -116,18 +111,13 @@ class MockQuicSimpleServerSession : public QuicSimpleServerSession { const size_t kMaxStreamsForTest = 100; MockQuicSimpleServerSession( - QuicConnection* connection, - MockQuicSessionVisitor* owner, + QuicConnection* connection, MockQuicSessionVisitor* owner, MockQuicCryptoServerStreamHelper* helper, QuicCryptoServerConfig* crypto_config, QuicCompressedCertsCache* compressed_certs_cache, QuicSimpleServerBackend* quic_simple_server_backend) - : QuicSimpleServerSession(DefaultQuicConfig(), - CurrentSupportedVersions(), - connection, - owner, - helper, - crypto_config, + : QuicSimpleServerSession(DefaultQuicConfig(), CurrentSupportedVersions(), + connection, owner, helper, crypto_config, compressed_certs_cache, quic_simple_server_backend) { if (VersionHasIetfQuicFrames(connection->transport_version())) { @@ -148,47 +138,35 @@ class MockQuicSimpleServerSession : public QuicSimpleServerSession { delete; ~MockQuicSimpleServerSession() override = default; - MOCK_METHOD(void, - OnConnectionClosed, + MOCK_METHOD(void, OnConnectionClosed, (const QuicConnectionCloseFrame& frame, ConnectionCloseSource source), (override)); - MOCK_METHOD(QuicSpdyStream*, - CreateIncomingStream, - (QuicStreamId id), + MOCK_METHOD(QuicSpdyStream*, CreateIncomingStream, (QuicStreamId id), (override)); MOCK_METHOD(QuicConsumedData, WritevData, (QuicStreamId id, size_t write_length, QuicStreamOffset offset, StreamSendingState state, TransmissionType type, EncryptionLevel level), (override)); - MOCK_METHOD(void, - OnStreamHeaderList, - (QuicStreamId stream_id, - bool fin, - size_t frame_len, + MOCK_METHOD(void, OnStreamHeaderList, + (QuicStreamId stream_id, bool fin, size_t frame_len, const QuicHeaderList& header_list), (override)); - MOCK_METHOD(void, - OnStreamHeadersPriority, + MOCK_METHOD(void, OnStreamHeadersPriority, (QuicStreamId stream_id, const spdy::SpdyStreamPrecedence& precedence), (override)); - MOCK_METHOD(void, - MaybeSendRstStreamFrame, - (QuicStreamId stream_id, - QuicRstStreamErrorCode error, + MOCK_METHOD(void, MaybeSendRstStreamFrame, + (QuicStreamId stream_id, QuicResetStreamError error, QuicStreamOffset bytes_written), (override)); - MOCK_METHOD(void, - MaybeSendStopSendingFrame, - (QuicStreamId stream_id, QuicRstStreamErrorCode error), - (override)); + MOCK_METHOD(void, MaybeSendStopSendingFrame, + (QuicStreamId stream_id, QuicResetStreamError error), (override)); using QuicSession::ActivateStream; - QuicConsumedData ConsumeData(QuicStreamId id, - size_t write_length, + QuicConsumedData ConsumeData(QuicStreamId id, size_t write_length, QuicStreamOffset offset, StreamSendingState state, TransmissionType /*type*/, @@ -211,23 +189,17 @@ class MockQuicSimpleServerSession : public QuicSimpleServerSession { class QuicSimpleServerStreamTest : public QuicTestWithParam<ParsedQuicVersion> { public: QuicSimpleServerStreamTest() - : connection_( - new StrictMock<MockQuicConnection>(&helper_, - &alarm_factory_, - Perspective::IS_SERVER, - SupportedVersions(GetParam()))), + : connection_(new StrictMock<MockQuicConnection>( + &helper_, &alarm_factory_, Perspective::IS_SERVER, + SupportedVersions(GetParam()))), crypto_config_(new QuicCryptoServerConfig( - QuicCryptoServerConfig::TESTING, - QuicRandom::GetInstance(), + QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(), crypto_test_utils::ProofSourceForTesting(), KeyExchangeSource::Default())), compressed_certs_cache_( QuicCompressedCertsCache::kQuicCompressedCertsCacheSize), - session_(connection_, - &session_owner_, - &session_helper_, - crypto_config_.get(), - &compressed_certs_cache_, + session_(connection_, &session_owner_, &session_helper_, + crypto_config_.get(), &compressed_certs_cache_, &memory_cache_backend_), quic_response_(new QuicBackendResponse), body_("hello world") { @@ -298,8 +270,7 @@ class QuicSimpleServerStreamTest : public QuicTestWithParam<ParsedQuicVersion> { QuicHeaderList header_list_; }; -INSTANTIATE_TEST_SUITE_P(Tests, - QuicSimpleServerStreamTest, +INSTANTIATE_TEST_SUITE_P(Tests, QuicSimpleServerStreamTest, ::testing::ValuesIn(AllSupportedVersions()), ::testing::PrintToStringParamName()); @@ -350,10 +321,15 @@ TEST_P(QuicSimpleServerStreamTest, SendQuicRstStreamNoErrorInStopReading) { stream_->CloseWriteSide(); if (session_.version().UsesHttp3()) { - EXPECT_CALL(session_, MaybeSendStopSendingFrame(_, QUIC_STREAM_NO_ERROR)) + EXPECT_CALL(session_, + MaybeSendStopSendingFrame(_, QuicResetStreamError::FromInternal( + QUIC_STREAM_NO_ERROR))) .Times(1); } else { - EXPECT_CALL(session_, MaybeSendRstStreamFrame(_, QUIC_STREAM_NO_ERROR, _)) + EXPECT_CALL( + session_, + MaybeSendRstStreamFrame( + _, QuicResetStreamError::FromInternal(QUIC_STREAM_NO_ERROR), _)) .Times(1); } stream_->StopReading(); @@ -480,11 +456,16 @@ TEST_P(QuicSimpleServerStreamTest, SendPushResponseWith404Response) { InSequence s; if (session_.version().UsesHttp3()) { - EXPECT_CALL(session_, MaybeSendStopSendingFrame(promised_stream->id(), - QUIC_STREAM_CANCELLED)); + EXPECT_CALL(session_, + MaybeSendStopSendingFrame( + promised_stream->id(), + QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED))); } - EXPECT_CALL(session_, MaybeSendRstStreamFrame(promised_stream->id(), - QUIC_STREAM_CANCELLED, 0)); + EXPECT_CALL( + session_, + MaybeSendRstStreamFrame( + promised_stream->id(), + QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED), 0)); promised_stream->DoSendResponse(); } @@ -695,11 +676,14 @@ TEST_P(QuicSimpleServerStreamTest, .Times(AnyNumber()); } - EXPECT_CALL(session_, MaybeSendRstStreamFrame(_, - session_.version().UsesHttp3() - ? QUIC_STREAM_CANCELLED - : QUIC_RST_ACKNOWLEDGEMENT, - _)) + EXPECT_CALL( + session_, + MaybeSendRstStreamFrame( + _, + session_.version().UsesHttp3() + ? QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED) + : QuicResetStreamError::FromInternal(QUIC_RST_ACKNOWLEDGEMENT), + _)) .Times(1); QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(), QUIC_STREAM_CANCELLED, 1234); diff --git a/chromium/net/third_party/quiche/src/quic/tools/web_transport_test_visitors.h b/chromium/net/third_party/quiche/src/quic/tools/web_transport_test_visitors.h index e348750a474..b1f22469e12 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/web_transport_test_visitors.h +++ b/chromium/net/third_party/quiche/src/quic/tools/web_transport_test_visitors.h @@ -29,6 +29,10 @@ class WebTransportDiscardVisitor : public WebTransportStreamVisitor { void OnCanWrite() override {} + void OnResetStreamReceived(WebTransportStreamError /*error*/) override {} + void OnStopSendingReceived(WebTransportStreamError /*error*/) override {} + void OnWriteSideInDataRecvdState() override {} + private: WebTransportStream* stream_; }; @@ -51,6 +55,10 @@ class WebTransportBidirectionalEchoVisitor : public WebTransportStreamVisitor { } void OnCanWrite() override { + if (stop_sending_received_) { + return; + } + if (!buffer_.empty()) { bool success = stream_->Write(buffer_); QUIC_DVLOG(1) << "Attempted writing on WebTransport bidirectional stream " @@ -69,10 +77,26 @@ class WebTransportBidirectionalEchoVisitor : public WebTransportStreamVisitor { } } + void OnResetStreamReceived(WebTransportStreamError /*error*/) override { + // Send FIN in response to a stream reset. We want to test that we can + // operate one side of the stream cleanly while the other is reset, thus + // replying with a FIN rather than a RESET_STREAM is more appropriate here. + send_fin_ = true; + OnCanWrite(); + } + void OnStopSendingReceived(WebTransportStreamError /*error*/) override { + stop_sending_received_ = true; + } + void OnWriteSideInDataRecvdState() override {} + + protected: + WebTransportStream* stream() { return stream_; } + private: WebTransportStream* stream_; std::string buffer_; bool send_fin_ = false; + bool stop_sending_received_ = false; }; // Buffers all of the data and calls |callback| with the entirety of the stream @@ -100,6 +124,10 @@ class WebTransportUnidirectionalEchoReadVisitor void OnCanWrite() override { QUIC_NOTREACHED(); } + void OnResetStreamReceived(WebTransportStreamError /*error*/) override {} + void OnStopSendingReceived(WebTransportStreamError /*error*/) override {} + void OnWriteSideInDataRecvdState() override {} + private: WebTransportStream* stream_; std::string buffer_; @@ -129,6 +157,10 @@ class WebTransportUnidirectionalEchoWriteVisitor QUICHE_DCHECK(fin_sent); } + void OnResetStreamReceived(WebTransportStreamError /*error*/) override {} + void OnStopSendingReceived(WebTransportStreamError /*error*/) override {} + void OnWriteSideInDataRecvdState() override {} + private: WebTransportStream* stream_; std::string data_; @@ -141,12 +173,15 @@ class EchoWebTransportSessionVisitor : public WebTransportVisitor { EchoWebTransportSessionVisitor(WebTransportSession* session) : session_(session) {} - void OnSessionReady() override { + void OnSessionReady(const spdy::SpdyHeaderBlock&) override { if (session_->CanOpenNextOutgoingBidirectionalStream()) { OnCanCreateNewOutgoingBidirectionalStream(); } } + void OnSessionClosed(WebTransportSessionError /*error_code*/, + const std::string& /*error_message*/) override {} + void OnIncomingBidirectionalStreamAvailable() override { while (true) { WebTransportStream* stream = diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.cc index 03db87d2e51..aaee6009740 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.cc @@ -29,6 +29,10 @@ void HpackDecoderAdapter::ApplyHeaderTableSizeSetting(size_t size_setting) { hpack_decoder_.ApplyHeaderTableSizeSetting(size_setting); } +size_t HpackDecoderAdapter::GetCurrentHeaderTableSizeSetting() const { + return hpack_decoder_.GetCurrentHeaderTableSizeSetting(); +} + void HpackDecoderAdapter::HandleControlFrameHeadersStart( SpdyHeadersHandlerInterface* handler) { QUICHE_DVLOG(2) << "HpackDecoderAdapter::HandleControlFrameHeadersStart"; @@ -85,12 +89,8 @@ bool HpackDecoderAdapter::HandleControlFrameHeadersData( return true; } -bool HpackDecoderAdapter::HandleControlFrameHeadersComplete( - size_t* compressed_len) { +bool HpackDecoderAdapter::HandleControlFrameHeadersComplete() { QUICHE_DVLOG(2) << "HpackDecoderAdapter::HandleControlFrameHeadersComplete"; - if (compressed_len != nullptr) { - *compressed_len = listener_adapter_.total_hpack_bytes(); - } if (!hpack_decoder_.EndDecodingBlock()) { QUICHE_DVLOG(3) << "EndDecodingBlock returned false"; error_ = hpack_decoder_.error(); diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.h b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.h index 43a762bb757..c4bc99154fe 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.h +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.h @@ -39,6 +39,9 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderAdapter { // Called upon acknowledgement of SETTINGS_HEADER_TABLE_SIZE. void ApplyHeaderTableSizeSetting(size_t size_setting); + // Returns the most recently applied value of SETTINGS_HEADER_TABLE_SIZE. + size_t GetCurrentHeaderTableSizeSetting() const; + // If a SpdyHeadersHandlerInterface is provided, the decoder will emit // headers to it rather than accumulating them in a SpdyHeaderBlock. // Does not take ownership of the handler, but does use the pointer until @@ -56,10 +59,7 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderAdapter { // buffered block that was accumulated in HandleControlFrameHeadersData(), // to support subsequent calculation of compression percentage. // Discards the handler supplied at the start of decoding the block. - // TODO(jamessynge): Determine if compressed_len is needed; it is used to - // produce UUMA stat Net.SpdyHpackDecompressionPercentage, but only for - // deprecated SPDY3. - bool HandleControlFrameHeadersComplete(size_t* compressed_len); + bool HandleControlFrameHeadersComplete(); // Accessor for the most recently decoded headers block. Valid until the next // call to HandleControlFrameHeadersData(). diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter_test.cc index bab6c481e78..f1f64e6a8c2 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter_test.cc @@ -141,11 +141,8 @@ class HpackDecoderAdapterTest return decoder_.HandleControlFrameHeadersData(str.data(), str.size()); } - bool HandleControlFrameHeadersComplete(size_t* size) { - bool rc = decoder_.HandleControlFrameHeadersComplete(size); - if (size != nullptr) { - EXPECT_EQ(*size, bytes_passed_in_); - } + bool HandleControlFrameHeadersComplete() { + bool rc = decoder_.HandleControlFrameHeadersComplete(); return rc; } @@ -172,22 +169,18 @@ class HpackDecoderAdapterTest decode_has_failed_ = true; return false; } - // Want to get out the number of compressed bytes that were decoded, - // so pass in a pointer if no handler. - size_t total_hpack_bytes = 0; if (start_choice_ == START_WITH_HANDLER) { - if (!HandleControlFrameHeadersComplete(nullptr)) { + if (!HandleControlFrameHeadersComplete()) { decode_has_failed_ = true; return false; } - total_hpack_bytes = handler_.compressed_header_bytes(); + EXPECT_EQ(handler_.compressed_header_bytes(), bytes_passed_in_); } else { - if (!HandleControlFrameHeadersComplete(&total_hpack_bytes)) { + if (!HandleControlFrameHeadersComplete()) { decode_has_failed_ = true; return false; } } - EXPECT_EQ(total_hpack_bytes, bytes_passed_in_); if (check_decoded_size && start_choice_ == START_WITH_HANDLER) { EXPECT_EQ(handler_.uncompressed_header_bytes(), SizeOfHeaders(decoded_block())); @@ -274,6 +267,12 @@ INSTANTIATE_TEST_SUITE_P( ::testing::Combine(::testing::Values(START_WITH_HANDLER), ::testing::Bool())); +TEST_P(HpackDecoderAdapterTest, ApplyHeaderTableSizeSetting) { + EXPECT_EQ(4096u, decoder_.GetCurrentHeaderTableSizeSetting()); + decoder_.ApplyHeaderTableSizeSetting(12 * 1024); + EXPECT_EQ(12288u, decoder_.GetCurrentHeaderTableSizeSetting()); +} + TEST_P(HpackDecoderAdapterTest, AddHeaderDataWithHandleControlFrameHeadersData) { // The hpack decode buffer size is limited in size. This test verifies that @@ -352,8 +351,7 @@ TEST_P(HpackDecoderAdapterTest, HeaderBlockTooLong) { // entire block successfully. HandleControlFrameHeadersStart(); EXPECT_TRUE(HandleControlFrameHeadersData(hbb.buffer())); - size_t total_bytes; - EXPECT_TRUE(HandleControlFrameHeadersComplete(&total_bytes)); + EXPECT_TRUE(HandleControlFrameHeadersComplete()); // When a total byte limit is imposed, the decoder bails before the end of the // block. @@ -387,9 +385,7 @@ TEST_P(HpackDecoderAdapterTest, DecodeWithIncompleteData) { // Add the needed data. EXPECT_TRUE(HandleControlFrameHeadersData("\x04gggs")); - size_t size = 0; - EXPECT_TRUE(HandleControlFrameHeadersComplete(&size)); - EXPECT_EQ(24u, size); + EXPECT_TRUE(HandleControlFrameHeadersComplete()); expected_headers.push_back({"spam", "gggs"}); @@ -429,7 +425,7 @@ TEST_P(HpackDecoderAdapterTest, HandleHeaderRepresentation) { decoder_peer_.HandleHeaderRepresentation("cookie", " fin!"); // Finish and emit all headers. - decoder_.HandleControlFrameHeadersComplete(nullptr); + decoder_.HandleControlFrameHeadersComplete(); // Resulting decoded headers are in the same order as the inputs. EXPECT_THAT( @@ -503,8 +499,8 @@ TEST_P(HpackDecoderAdapterTest, ContextUpdateMaximumSize) { output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); output_stream.AppendUint32(126); - output_stream.TakeString(&input); - EXPECT_TRUE(DecodeHeaderBlock(absl::string_view(input))); + input = output_stream.TakeString(); + EXPECT_TRUE(DecodeHeaderBlock(input)); EXPECT_EQ(126u, decoder_peer_.header_table_size_limit()); } { @@ -513,8 +509,8 @@ TEST_P(HpackDecoderAdapterTest, ContextUpdateMaximumSize) { output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); output_stream.AppendUint32(kDefaultHeaderTableSizeSetting); - output_stream.TakeString(&input); - EXPECT_TRUE(DecodeHeaderBlock(absl::string_view(input))); + input = output_stream.TakeString(); + EXPECT_TRUE(DecodeHeaderBlock(input)); EXPECT_EQ(kDefaultHeaderTableSizeSetting, decoder_peer_.header_table_size_limit()); } @@ -524,8 +520,8 @@ TEST_P(HpackDecoderAdapterTest, ContextUpdateMaximumSize) { output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); output_stream.AppendUint32(kDefaultHeaderTableSizeSetting + 1); - output_stream.TakeString(&input); - EXPECT_FALSE(DecodeHeaderBlock(absl::string_view(input))); + input = output_stream.TakeString(); + EXPECT_FALSE(DecodeHeaderBlock(input)); EXPECT_EQ(kDefaultHeaderTableSizeSetting, decoder_peer_.header_table_size_limit()); } @@ -542,8 +538,8 @@ TEST_P(HpackDecoderAdapterTest, TwoTableSizeUpdates) { output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); output_stream.AppendUint32(122); - output_stream.TakeString(&input); - EXPECT_TRUE(DecodeHeaderBlock(absl::string_view(input))); + input = output_stream.TakeString(); + EXPECT_TRUE(DecodeHeaderBlock(input)); EXPECT_EQ(122u, decoder_peer_.header_table_size_limit()); } } @@ -561,9 +557,9 @@ TEST_P(HpackDecoderAdapterTest, ThreeTableSizeUpdatesError) { output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); output_stream.AppendUint32(15); - output_stream.TakeString(&input); + input = output_stream.TakeString(); - EXPECT_FALSE(DecodeHeaderBlock(absl::string_view(input))); + EXPECT_FALSE(DecodeHeaderBlock(input)); EXPECT_EQ(10u, decoder_peer_.header_table_size_limit()); } } @@ -580,9 +576,9 @@ TEST_P(HpackDecoderAdapterTest, TableSizeUpdateSecondError) { output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); output_stream.AppendUint32(123); - output_stream.TakeString(&input); + input = output_stream.TakeString(); - EXPECT_FALSE(DecodeHeaderBlock(absl::string_view(input))); + EXPECT_FALSE(DecodeHeaderBlock(input)); EXPECT_EQ(kDefaultHeaderTableSizeSetting, decoder_peer_.header_table_size_limit()); } @@ -603,9 +599,9 @@ TEST_P(HpackDecoderAdapterTest, TableSizeUpdateFirstThirdError) { output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); output_stream.AppendUint32(125); - output_stream.TakeString(&input); + input = output_stream.TakeString(); - EXPECT_FALSE(DecodeHeaderBlock(absl::string_view(input))); + EXPECT_FALSE(DecodeHeaderBlock(input)); EXPECT_EQ(60u, decoder_peer_.header_table_size_limit()); } } @@ -719,9 +715,8 @@ TEST_P(HpackDecoderAdapterTest, BasicC31) { expected_header_set[":path"] = "/"; expected_header_set[":authority"] = "www.example.com"; - std::string encoded_header_set; - EXPECT_TRUE( - encoder.EncodeHeaderSet(expected_header_set, &encoded_header_set)); + std::string encoded_header_set = + encoder.EncodeHeaderBlock(expected_header_set); EXPECT_TRUE(DecodeHeaderBlock(encoded_header_set)); EXPECT_EQ(expected_header_set, decoded_block()); 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 ccf17da20b2..1821d7cfbfc 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 @@ -84,8 +84,7 @@ HpackEncoder::HpackEncoder() HpackEncoder::~HpackEncoder() = default; -bool HpackEncoder::EncodeHeaderSet(const SpdyHeaderBlock& header_set, - std::string* output) { +std::string HpackEncoder::EncodeHeaderBlock(const SpdyHeaderBlock& header_set) { // Separate header set into pseudo-headers and regular headers. Representations pseudo_headers; Representations regular_headers; @@ -104,11 +103,8 @@ bool HpackEncoder::EncodeHeaderSet(const SpdyHeaderBlock& header_set, } } - { - RepresentationIterator iter(pseudo_headers, regular_headers); - EncodeRepresentations(&iter, output); - } - return true; + RepresentationIterator iter(pseudo_headers, regular_headers); + return EncodeRepresentations(&iter); } void HpackEncoder::ApplyHeaderTableSizeSetting(size_t size_setting) { @@ -123,8 +119,7 @@ void HpackEncoder::ApplyHeaderTableSizeSetting(size_t size_setting) { should_emit_table_size_ = true; } -void HpackEncoder::EncodeRepresentations(RepresentationIterator* iter, - std::string* output) { +std::string HpackEncoder::EncodeRepresentations(RepresentationIterator* iter) { MaybeEmitTableSize(); while (iter->HasNext()) { const auto header = iter->Next(); @@ -144,7 +139,7 @@ void HpackEncoder::EncodeRepresentations(RepresentationIterator* iter, } } - output_stream_.TakeString(output); + return output_stream_.TakeString(); } void HpackEncoder::EmitIndex(size_t index) { @@ -283,9 +278,8 @@ class HpackEncoder::Encoderator : public ProgressiveEncoder { // Returns true iff more remains to encode. bool HasNext() const override { return has_next_; } - // Encodes up to max_encoded_bytes of the current header block into the - // given output string. - void Next(size_t max_encoded_bytes, std::string* output) override; + // Encodes and returns up to max_encoded_bytes of the current header block. + std::string Next(size_t max_encoded_bytes) override; private: HpackEncoder* encoder_; @@ -338,8 +332,7 @@ HpackEncoder::Encoderator::Encoderator(const Representations& representations, encoder_->MaybeEmitTableSize(); } -void HpackEncoder::Encoderator::Next(size_t max_encoded_bytes, - std::string* output) { +std::string HpackEncoder::Encoderator::Next(size_t max_encoded_bytes) { QUICHE_BUG_IF(spdy_bug_61_1, !has_next_) << "Encoderator::Next called with nothing left to encode."; const bool enable_compression = encoder_->enable_compression_; @@ -365,7 +358,7 @@ void HpackEncoder::Encoderator::Next(size_t max_encoded_bytes, } has_next_ = encoder_->output_stream_.size() > max_encoded_bytes; - encoder_->output_stream_.BoundedTakeString(max_encoded_bytes, output); + return encoder_->output_stream_.BoundedTakeString(max_encoded_bytes); } std::unique_ptr<HpackEncoder::ProgressiveEncoder> HpackEncoder::EncodeHeaderSet( diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h index 5e2ea03980c..05292976b64 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h @@ -49,9 +49,8 @@ class QUICHE_EXPORT_PRIVATE HpackEncoder { HpackEncoder& operator=(const HpackEncoder&) = delete; ~HpackEncoder(); - // Encodes the given header set into the given string. Returns - // whether or not the encoding was successful. - bool EncodeHeaderSet(const SpdyHeaderBlock& header_set, std::string* output); + // Encodes and returns the given header set as a string. + std::string EncodeHeaderBlock(const SpdyHeaderBlock& header_set); class QUICHE_EXPORT_PRIVATE ProgressiveEncoder { public: @@ -60,9 +59,8 @@ class QUICHE_EXPORT_PRIVATE HpackEncoder { // Returns true iff more remains to encode. virtual bool HasNext() const = 0; - // Encodes up to max_encoded_bytes of the current header block into the - // given output string. - virtual void Next(size_t max_encoded_bytes, std::string* output) = 0; + // Encodes and returns up to max_encoded_bytes of the current header block. + virtual std::string Next(size_t max_encoded_bytes) = 0; }; // Returns a ProgressiveEncoder which must be outlived by both the given @@ -106,7 +104,7 @@ class QUICHE_EXPORT_PRIVATE HpackEncoder { class Encoderator; // Encodes a sequence of header name-value pairs as a single header block. - void EncodeRepresentations(RepresentationIterator* iter, std::string* output); + std::string EncodeRepresentations(RepresentationIterator* iter); // Emits a static/dynamic indexed representation (Section 7.1). void EmitIndex(size_t index); 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 dfd8d8fea2c..c47bf979499 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 @@ -45,7 +45,7 @@ class HpackEncoderPeer { HpackHeaderTablePeer table_peer() { return HpackHeaderTablePeer(table()); } void EmitString(absl::string_view str) { encoder_->EmitString(str); } void TakeString(std::string* out) { - encoder_->output_stream_.TakeString(out); + *out = encoder_->output_stream_.TakeString(); } static void CookieToCrumbs(absl::string_view cookie, std::vector<absl::string_view>* out) { @@ -71,10 +71,9 @@ class HpackEncoderPeer { // TODO(dahollings): Remove or clean up these methods when deprecating // non-incremental encoding path. - static bool EncodeHeaderSet(HpackEncoder* encoder, - const SpdyHeaderBlock& header_set, - std::string* output) { - return encoder->EncodeHeaderSet(header_set, output); + static std::string EncodeHeaderBlock(HpackEncoder* encoder, + const SpdyHeaderBlock& header_set) { + return encoder->EncodeHeaderBlock(header_set); } static bool EncodeIncremental(HpackEncoder* encoder, @@ -82,12 +81,11 @@ class HpackEncoderPeer { std::string* output) { std::unique_ptr<HpackEncoder::ProgressiveEncoder> encoderator = encoder->EncodeHeaderSet(header_set); - std::string output_buffer; http2::test::Http2Random random; - encoderator->Next(random.UniformInRange(0, 16), &output_buffer); + std::string output_buffer = encoderator->Next(random.UniformInRange(0, 16)); while (encoderator->HasNext()) { - std::string second_buffer; - encoderator->Next(random.UniformInRange(0, 16), &second_buffer); + std::string second_buffer = + encoderator->Next(random.UniformInRange(0, 16)); output_buffer.append(second_buffer); } *output = std::move(output_buffer); @@ -99,12 +97,11 @@ class HpackEncoderPeer { std::string* output) { std::unique_ptr<HpackEncoder::ProgressiveEncoder> encoderator = encoder->EncodeRepresentations(representations); - std::string output_buffer; http2::test::Http2Random random; - encoderator->Next(random.UniformInRange(0, 16), &output_buffer); + std::string output_buffer = encoderator->Next(random.UniformInRange(0, 16)); while (encoderator->HasNext()) { - std::string second_buffer; - encoderator->Next(random.UniformInRange(0, 16), &second_buffer); + std::string second_buffer = + encoderator->Next(random.UniformInRange(0, 16)); output_buffer.append(second_buffer); } *output = std::move(output_buffer); @@ -219,12 +216,12 @@ class HpackEncoderTest : public QuicheTestWithParam<EncodeStrategy> { return r; } void CompareWithExpectedEncoding(const SpdyHeaderBlock& header_set) { - std::string expected_out, actual_out; - expected_.TakeString(&expected_out); + std::string actual_out; + std::string expected_out = expected_.TakeString(); switch (strategy_) { case kDefault: - EXPECT_TRUE(test::HpackEncoderPeer::EncodeHeaderSet( - &encoder_, header_set, &actual_out)); + actual_out = + test::HpackEncoderPeer::EncodeHeaderBlock(&encoder_, header_set); break; case kIncremental: EXPECT_TRUE(test::HpackEncoderPeer::EncodeIncremental( @@ -238,8 +235,8 @@ class HpackEncoderTest : public QuicheTestWithParam<EncodeStrategy> { EXPECT_EQ(expected_out, actual_out); } void CompareWithExpectedEncoding(const Representations& representations) { - std::string expected_out, actual_out; - expected_.TakeString(&expected_out); + std::string actual_out; + std::string expected_out = expected_.TakeString(); EXPECT_TRUE(test::HpackEncoderPeer::EncodeRepresentations( &encoder_, representations, &actual_out)); EXPECT_EQ(expected_out, actual_out); @@ -460,8 +457,8 @@ TEST_P(HpackEncoderTest, StringsDynamicallySelectHuffmanCoding) { expected_.AppendUint32(6); expected_.AppendBytes("@@@@@@"); - std::string expected_out, actual_out; - expected_.TakeString(&expected_out); + std::string actual_out; + std::string expected_out = expected_.TakeString(); peer_.TakeString(&actual_out); EXPECT_EQ(expected_out, actual_out); } diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.cc index bce24fa610d..291ad3d6d67 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.cc @@ -68,31 +68,32 @@ std::string* HpackOutputStream::MutableString() { return &buffer_; } -void HpackOutputStream::TakeString(std::string* output) { +std::string HpackOutputStream::TakeString() { // This must hold, since all public functions cause the buffer to // end on a byte boundary. QUICHE_DCHECK_EQ(bit_offset_, 0u); - buffer_.swap(*output); - buffer_.clear(); + std::string out = std::move(buffer_); + buffer_ = {}; bit_offset_ = 0; + return out; } -void HpackOutputStream::BoundedTakeString(size_t max_size, - std::string* output) { +std::string HpackOutputStream::BoundedTakeString(size_t max_size) { if (buffer_.size() > max_size) { // Save off overflow bytes to temporary string (causes a copy). - std::string overflow(buffer_.data() + max_size, buffer_.size() - max_size); + std::string overflow = buffer_.substr(max_size); // Resize buffer down to the given limit. buffer_.resize(max_size); // Give buffer to output string. - *output = std::move(buffer_); + std::string out = std::move(buffer_); // Reset to contain overflow. buffer_ = std::move(overflow); + return out; } else { - TakeString(output); + return TakeString(); } } diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h index ec657b54064..ade1cd1ae68 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h @@ -51,12 +51,12 @@ class QUICHE_EXPORT_PRIVATE HpackOutputStream { // Return pointer to internal buffer. |bit_offset_| needs to be zero. std::string* MutableString(); - // Swaps the internal buffer with |output|, then resets state. - void TakeString(std::string* output); + // Returns the internal buffer as a string, then resets state. + std::string TakeString(); - // Gives up to |max_size| bytes of the internal buffer to |output|. Resets + // Returns up to |max_size| bytes of the internal buffer. Resets // internal state with the overflow. - void BoundedTakeString(size_t max_size, std::string* output); + std::string BoundedTakeString(size_t max_size); // Size in bytes of stream's internal buffer. size_t size() const { return buffer_.size(); } diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream_test.cc index ea05ecedf5d..3d8c35db344 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream_test.cc @@ -37,8 +37,7 @@ TEST(HpackOutputStreamTest, AppendBits) { output_stream.AppendBits(0x0, 7); - std::string str; - output_stream.TakeString(&str); + std::string str = output_stream.TakeString(); EXPECT_EQ(expected_str, str); } @@ -50,8 +49,7 @@ std::string EncodeUint32(uint8_t N, uint32_t I) { output_stream.AppendBits(0x00, 8 - N); } output_stream.AppendUint32(I); - std::string str; - output_stream.TakeString(&str); + std::string str = output_stream.TakeString(); return str; } @@ -236,8 +234,7 @@ TEST(HpackOutputStreamTest, AppendUint32PreservesUpperBits) { HpackOutputStream output_stream; output_stream.AppendBits(0x7f, 7); output_stream.AppendUint32(0x01); - std::string str; - output_stream.TakeString(&str); + std::string str = output_stream.TakeString(); EXPECT_EQ(std::string("\xff\x00", 2), str); } @@ -247,8 +244,7 @@ TEST(HpackOutputStreamTest, AppendBytes) { output_stream.AppendBytes("buffer1"); output_stream.AppendBytes("buffer2"); - std::string str; - output_stream.TakeString(&str); + std::string str = output_stream.TakeString(); EXPECT_EQ("buffer1buffer2", str); } @@ -258,16 +254,15 @@ TEST(HpackOutputStreamTest, BoundedTakeString) { output_stream.AppendBytes("buffer12"); output_stream.AppendBytes("buffer456"); - std::string str; - output_stream.BoundedTakeString(9, &str); + std::string str = output_stream.BoundedTakeString(9); EXPECT_EQ("buffer12b", str); output_stream.AppendBits(0x7f, 7); output_stream.AppendUint32(0x11); - output_stream.BoundedTakeString(9, &str); + str = output_stream.BoundedTakeString(9); EXPECT_EQ("uffer456\xff", str); - output_stream.BoundedTakeString(9, &str); + str = output_stream.BoundedTakeString(9); EXPECT_EQ("\x10", str); } @@ -280,8 +275,7 @@ TEST(HpackOutputStreamTest, MutableString) { output_stream.AppendBytes("foo"); output_stream.MutableString()->append("bar"); - std::string str; - output_stream.TakeString(&str); + std::string str = output_stream.TakeString(); EXPECT_EQ("12foobar", str); } diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_round_trip_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_round_trip_test.cc index 047ae7292e6..42a4f2ea44d 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_round_trip_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_round_trip_test.cc @@ -31,8 +31,7 @@ class HpackRoundTripTest : public QuicheTestWithParam<InputSizeParam> { } bool RoundTrip(const SpdyHeaderBlock& header_set) { - std::string encoded; - encoder_.EncodeHeaderSet(header_set, &encoded); + std::string encoded = encoder_.EncodeHeaderBlock(header_set); bool success = true; if (GetParam() == ALL_INPUT) { @@ -58,7 +57,7 @@ class HpackRoundTripTest : public QuicheTestWithParam<InputSizeParam> { } if (success) { - success = decoder_.HandleControlFrameHeadersComplete(nullptr); + success = decoder_.HandleControlFrameHeadersComplete(); } EXPECT_EQ(header_set, decoder_.decoded_block()); diff --git a/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.cc b/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.cc index e2e311dda07..0691f6b7bb9 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.cc @@ -1104,7 +1104,7 @@ void Http2DecoderAdapter::CommonHpackFragmentEnd() { << frame_header(); has_expected_frame_type_ = false; auto* decoder = GetHpackDecoder(); - if (decoder->HandleControlFrameHeadersComplete(nullptr)) { + if (decoder->HandleControlFrameHeadersComplete()) { visitor()->OnHeaderFrameEnd(stream_id()); } else { SetSpdyErrorAndNotify( diff --git a/chromium/net/third_party/quiche/src/spdy/core/metadata_extension.cc b/chromium/net/third_party/quiche/src/spdy/core/metadata_extension.cc index 12f6af14843..1683dcc4ff2 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/metadata_extension.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/metadata_extension.cc @@ -55,9 +55,9 @@ std::unique_ptr<spdy::SpdyFrameIR> MetadataFrameSequence::Next() { if (!progressive_encoder_->HasNext()) { return nullptr; } - std::string payload; // METADATA frames obey the HTTP/2 maximum frame size. - progressive_encoder_->Next(spdy::kHttp2DefaultFramePayloadLimit, &payload); + std::string payload = + progressive_encoder_->Next(spdy::kHttp2DefaultFramePayloadLimit); const bool end_metadata = (!progressive_encoder_->HasNext()); const uint8_t flags = end_metadata ? MetadataVisitor::kEndMetadataFlag : 0; return absl::make_unique<spdy::SpdyUnknownIR>( diff --git a/chromium/net/third_party/quiche/src/spdy/core/metadata_extension_test.cc b/chromium/net/third_party/quiche/src/spdy/core/metadata_extension_test.cc index 07a5ee1113f..7538c317274 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/metadata_extension_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/metadata_extension_test.cc @@ -106,7 +106,7 @@ TEST_F(MetadataExtensionTest, MetadataIgnoredWithoutExtension) { ASSERT_TRUE(frame != nullptr); while (frame != nullptr) { const size_t frame_size = framer.SerializeFrame(*frame, &test_buffer_); - ASSERT_GT(frame_size, 0); + ASSERT_GT(frame_size, 0u); ASSERT_FALSE(deframer.HasError()); ASSERT_EQ(frame_size, test_buffer_.Size()); EXPECT_EQ(frame_size, deframer.ProcessInput(kBuffer, frame_size)); @@ -151,14 +151,14 @@ TEST_F(MetadataExtensionTest, MetadataPayloadEndToEnd) { ASSERT_TRUE(frame != nullptr); while (frame != nullptr) { const size_t frame_size = framer.SerializeFrame(*frame, &test_buffer_); - ASSERT_GT(frame_size, 0); + ASSERT_GT(frame_size, 0u); ASSERT_FALSE(deframer.HasError()); ASSERT_EQ(frame_size, test_buffer_.Size()); EXPECT_EQ(frame_size, deframer.ProcessInput(kBuffer, frame_size)); test_buffer_.Reset(); frame = sequence->Next(); } - EXPECT_EQ(1, received_count_); + EXPECT_EQ(1u, received_count_); auto it = received_payload_map_.find(3); ASSERT_TRUE(it != received_payload_map_.end()); EXPECT_EQ(payload_block, it->second); @@ -201,7 +201,7 @@ TEST_F(MetadataExtensionTest, MetadataPayloadInterleaved) { for (auto frame : {frame1.get(), frame2.get()}) { if (frame != nullptr) { const size_t frame_size = framer.SerializeFrame(*frame, &test_buffer_); - ASSERT_GT(frame_size, 0); + ASSERT_GT(frame_size, 0u); ASSERT_FALSE(deframer.HasError()); ASSERT_EQ(frame_size, test_buffer_.Size()); EXPECT_EQ(frame_size, deframer.ProcessInput(kBuffer, frame_size)); @@ -211,7 +211,7 @@ TEST_F(MetadataExtensionTest, MetadataPayloadInterleaved) { frame1 = sequence1->Next(); frame2 = sequence2->Next(); } - EXPECT_EQ(2, received_count_); + EXPECT_EQ(2u, received_count_); auto it = received_payload_map_.find(3); ASSERT_TRUE(it != received_payload_map_.end()); EXPECT_EQ(payload1, it->second); diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.cc index f1cca0da3b1..de37a1208b7 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.cc @@ -39,15 +39,12 @@ bool ParsePositiveIntegerImpl(absl::string_view::const_iterator c, SpdyAltSvcWireFormat::AlternativeService::AlternativeService() = default; SpdyAltSvcWireFormat::AlternativeService::AlternativeService( - const std::string& protocol_id, - const std::string& host, - uint16_t port, - uint32_t max_age, - VersionVector version) + const std::string& protocol_id, const std::string& host, uint16_t port, + uint32_t max_age_seconds, VersionVector version) : protocol_id(protocol_id), host(host), port(port), - max_age(max_age), + max_age_seconds(max_age_seconds), version(std::move(version)) {} SpdyAltSvcWireFormat::AlternativeService::~AlternativeService() = default; @@ -113,7 +110,7 @@ bool SpdyAltSvcWireFormat::ParseHeaderFieldValue( } ++c; // Parse parameters. - uint32_t max_age = 86400; + uint32_t max_age_seconds = 86400; VersionVector version; absl::string_view::const_iterator parameters_end = std::find(c, value.end(), ','); @@ -147,7 +144,8 @@ bool SpdyAltSvcWireFormat::ParseHeaderFieldValue( return false; } if (parameter_name == "ma") { - if (!ParsePositiveInteger32(parameter_value_begin, c, &max_age)) { + if (!ParsePositiveInteger32(parameter_value_begin, c, + &max_age_seconds)) { return false; } } else if (!is_ietf_format_quic && parameter_name == "v") { @@ -204,7 +202,8 @@ bool SpdyAltSvcWireFormat::ParseHeaderFieldValue( version.push_back(quic_version); } } - altsvc_vector->emplace_back(protocol_id, host, port, max_age, version); + altsvc_vector->emplace_back(protocol_id, host, port, max_age_seconds, + version); for (; c != value.end() && (*c == ' ' || *c == '\t' || *c == ','); ++c) { } } @@ -266,8 +265,8 @@ std::string SpdyAltSvcWireFormat::SerializeHeaderFieldValue( value.push_back(c); } absl::StrAppend(&value, ":", altsvc.port, "\""); - if (altsvc.max_age != 86400) { - absl::StrAppend(&value, "; ma=", altsvc.max_age); + if (altsvc.max_age_seconds != 86400) { + absl::StrAppend(&value, "; ma=", altsvc.max_age_seconds); } if (!altsvc.version.empty()) { if (is_ietf_format_quic) { diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h index a8e10948a8c..664dc1c15cf 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h @@ -35,15 +35,13 @@ class QUICHE_EXPORT_PRIVATE SpdyAltSvcWireFormat { // Default is 0: invalid port. uint16_t port = 0; // Default is one day. - uint32_t max_age = 86400; + uint32_t max_age_seconds = 86400; // Default is empty: unspecified version. VersionVector version; AlternativeService(); - AlternativeService(const std::string& protocol_id, - const std::string& host, - uint16_t port, - uint32_t max_age, + AlternativeService(const std::string& protocol_id, const std::string& host, + uint16_t port, uint32_t max_age_seconds, VersionVector version); AlternativeService(const AlternativeService& other); ~AlternativeService(); @@ -51,7 +49,7 @@ class QUICHE_EXPORT_PRIVATE SpdyAltSvcWireFormat { bool operator==(const AlternativeService& other) const { return protocol_id == other.protocol_id && host == other.host && port == other.port && version == other.version && - max_age == other.max_age; + max_age_seconds == other.max_age_seconds; } }; // An empty vector means alternative services should be cleared for given diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format_test.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format_test.cc index 497f19dad86..70e708b20cf 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format_test.cc @@ -30,13 +30,15 @@ class SpdyAltSvcWireFormatPeer { } static bool ParsePositiveInteger16(absl::string_view::const_iterator c, absl::string_view::const_iterator end, - uint16_t* max_age) { - return SpdyAltSvcWireFormat::ParsePositiveInteger16(c, end, max_age); + uint16_t* max_age_seconds) { + return SpdyAltSvcWireFormat::ParsePositiveInteger16(c, end, + max_age_seconds); } static bool ParsePositiveInteger32(absl::string_view::const_iterator c, absl::string_view::const_iterator end, - uint32_t* max_age) { - return SpdyAltSvcWireFormat::ParsePositiveInteger32(c, end, max_age); + uint32_t* max_age_seconds) { + return SpdyAltSvcWireFormat::ParsePositiveInteger32(c, end, + max_age_seconds); } static char HexDigitToInt(char c) { return SpdyAltSvcWireFormat::HexDigitToInt(c); @@ -79,7 +81,7 @@ void FuzzHeaderFieldValue( header_field_value->append(" "); } if (i & 3 << 3) { - expected_altsvc->max_age = 1111; + expected_altsvc->max_age_seconds = 1111; header_field_value->append(";"); if (i & 1 << 3) { header_field_value->append(" "); @@ -114,7 +116,7 @@ void FuzzHeaderFieldValue( } } if (i & 1 << 8) { - expected_altsvc->max_age = 999999999; + expected_altsvc->max_age_seconds = 999999999; header_field_value->append("; Ma=999999999"); } if (i & 1 << 9) { @@ -148,7 +150,7 @@ void FuzzAlternativeService(int i, } expected_header_field_value->append(":42\""); if (i & 1 << 1) { - altsvc->max_age = 1111; + altsvc->max_age_seconds = 1111; expected_header_field_value->append("; ma=1111"); } if (i & 1 << 2) { @@ -165,7 +167,7 @@ TEST(SpdyAltSvcWireFormatTest, DefaultValues) { EXPECT_EQ("", altsvc.protocol_id); EXPECT_EQ("", altsvc.host); EXPECT_EQ(0u, altsvc.port); - EXPECT_EQ(86400u, altsvc.max_age); + EXPECT_EQ(86400u, altsvc.max_age_seconds); EXPECT_TRUE(altsvc.version.empty()); } @@ -196,7 +198,8 @@ TEST(SpdyAltSvcWireFormatTest, ParseHeaderFieldValue) { EXPECT_EQ(expected_altsvc.protocol_id, altsvc_vector[0].protocol_id); EXPECT_EQ(expected_altsvc.host, altsvc_vector[0].host); EXPECT_EQ(expected_altsvc.port, altsvc_vector[0].port); - EXPECT_EQ(expected_altsvc.max_age, altsvc_vector[0].max_age); + EXPECT_EQ(expected_altsvc.max_age_seconds, + altsvc_vector[0].max_age_seconds); EXPECT_EQ(expected_altsvc.version, altsvc_vector[0].version); // Roundtrip test starting with |altsvc_vector|. @@ -210,7 +213,8 @@ TEST(SpdyAltSvcWireFormatTest, ParseHeaderFieldValue) { roundtrip_altsvc_vector[0].protocol_id); EXPECT_EQ(expected_altsvc.host, roundtrip_altsvc_vector[0].host); EXPECT_EQ(expected_altsvc.port, roundtrip_altsvc_vector[0].port); - EXPECT_EQ(expected_altsvc.max_age, roundtrip_altsvc_vector[0].max_age); + EXPECT_EQ(expected_altsvc.max_age_seconds, + roundtrip_altsvc_vector[0].max_age_seconds); EXPECT_EQ(expected_altsvc.version, roundtrip_altsvc_vector[0].version); } } @@ -240,7 +244,8 @@ TEST(SpdyAltSvcWireFormatTest, ParseHeaderFieldValueMultiple) { altsvc_vector[j].protocol_id); EXPECT_EQ(expected_altsvc_vector[j].host, altsvc_vector[j].host); EXPECT_EQ(expected_altsvc_vector[j].port, altsvc_vector[j].port); - EXPECT_EQ(expected_altsvc_vector[j].max_age, altsvc_vector[j].max_age); + EXPECT_EQ(expected_altsvc_vector[j].max_age_seconds, + altsvc_vector[j].max_age_seconds); EXPECT_EQ(expected_altsvc_vector[j].version, altsvc_vector[j].version); } @@ -258,8 +263,8 @@ TEST(SpdyAltSvcWireFormatTest, ParseHeaderFieldValueMultiple) { roundtrip_altsvc_vector[j].host); EXPECT_EQ(expected_altsvc_vector[j].port, roundtrip_altsvc_vector[j].port); - EXPECT_EQ(expected_altsvc_vector[j].max_age, - roundtrip_altsvc_vector[j].max_age); + EXPECT_EQ(expected_altsvc_vector[j].max_age_seconds, + roundtrip_altsvc_vector[j].max_age_seconds); EXPECT_EQ(expected_altsvc_vector[j].version, roundtrip_altsvc_vector[j].version); } @@ -290,7 +295,7 @@ TEST(SpdyAltSvcWireFormatTest, RoundTrip) { EXPECT_EQ(altsvc.protocol_id, parsed_altsvc_vector[0].protocol_id); EXPECT_EQ(altsvc.host, parsed_altsvc_vector[0].host); EXPECT_EQ(altsvc.port, parsed_altsvc_vector[0].port); - EXPECT_EQ(altsvc.max_age, parsed_altsvc_vector[0].max_age); + EXPECT_EQ(altsvc.max_age_seconds, parsed_altsvc_vector[0].max_age_seconds); EXPECT_EQ(altsvc.version, parsed_altsvc_vector[0].version); // Test SerializeHeaderFieldValue(). @@ -325,7 +330,7 @@ TEST(SpdyAltSvcWireFormatTest, RoundTripMultiple) { EXPECT_EQ(expected_it->protocol_id, parsed_it->protocol_id); EXPECT_EQ(expected_it->host, parsed_it->host); EXPECT_EQ(expected_it->port, parsed_it->port); - EXPECT_EQ(expected_it->max_age, parsed_it->max_age); + EXPECT_EQ(expected_it->max_age_seconds, parsed_it->max_age_seconds); EXPECT_EQ(expected_it->version, parsed_it->version); } @@ -566,7 +571,7 @@ TEST(SpdyAltSvcWireFormatTest, ParseIPLiteral) { EXPECT_EQ("quic", altsvc_vector[0].protocol_id); EXPECT_EQ("[2003:8:0:16::509d:9615]", altsvc_vector[0].host); EXPECT_EQ(443u, altsvc_vector[0].port); - EXPECT_EQ(60u, altsvc_vector[0].max_age); + EXPECT_EQ(60u, altsvc_vector[0].max_age_seconds); EXPECT_THAT(altsvc_vector[0].version, ::testing::ElementsAre(36, 35)); } diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.cc index 1b6eb8a6a9f..d1d0102b364 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.cc @@ -301,8 +301,8 @@ size_t SpdyFramer::SpdyFrameIterator::NextFrame(ZeroCopyOutputBuffer* output) { const size_t size_without_block = is_first_frame_ ? GetFrameSizeSansBlock() : kContinuationFrameMinimumSize; - std::string encoding; - encoder_->Next(kHttp2MaxControlFrameSendSize - size_without_block, &encoding); + std::string encoding = + encoder_->Next(kHttp2MaxControlFrameSendSize - size_without_block); has_next_frame_ = encoder_->HasNext(); if (framer_->debug_visitor_ != nullptr) { @@ -577,7 +577,8 @@ void SpdyFramer::SerializeHeadersBuilderHelper(const SpdyHeadersIR& headers, *size = *size + 5; } - GetHpackEncoder()->EncodeHeaderSet(headers.header_block(), hpack_encoding); + *hpack_encoding = + GetHpackEncoder()->EncodeHeaderBlock(headers.header_block()); *size = *size + hpack_encoding->size(); if (*size > kHttp2MaxControlFrameSendSize) { *size = *size + GetNumberRequiredContinuationFrames(*size) * @@ -670,8 +671,8 @@ void SpdyFramer::SerializePushPromiseBuilderHelper( *size = *size + push_promise.padding_payload_len(); } - GetHpackEncoder()->EncodeHeaderSet(push_promise.header_block(), - hpack_encoding); + *hpack_encoding = + GetHpackEncoder()->EncodeHeaderBlock(push_promise.header_block()); *size = *size + hpack_encoding->size(); if (*size > kHttp2MaxControlFrameSendSize) { *size = *size + GetNumberRequiredContinuationFrames(*size) * diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer_test.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer_test.cc index 138c4873b79..058c26fa246 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer_test.cc @@ -1112,10 +1112,9 @@ TEST_P(SpdyFramerTest, MultiValueHeader) { // TODO(jgraettinger): If this pattern appears again, move to test class. Http2HeaderBlock header_set; header_set["name"] = value; - std::string buffer; HpackEncoder encoder; encoder.DisableCompression(); - encoder.EncodeHeaderSet(header_set, &buffer); + std::string buffer = encoder.EncodeHeaderBlock(header_set); // Frame builder with plentiful buffer size. SpdyFrameBuilder frame(1024); frame.BeginNewFrame(SpdyFrameType::HEADERS, @@ -2401,10 +2400,9 @@ TEST_P(SpdyFramerTest, CreateContinuationUncompressed) { Http2HeaderBlock header_block; header_block["bar"] = "foo"; header_block["foo"] = "bar"; - std::string buffer; HpackEncoder encoder; encoder.DisableCompression(); - encoder.EncodeHeaderSet(header_block, &buffer); + std::string buffer = encoder.EncodeHeaderBlock(header_block); SpdyContinuationIR continuation(/* stream_id = */ 42); continuation.take_encoding(std::move(buffer)); |