diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-09-01 11:08:40 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-10-01 12:16:21 +0000 |
commit | 03c549e0392f92c02536d3f86d5e1d8dfa3435ac (patch) | |
tree | fe49d170a929b34ba82cd10db1a0bd8e3760fa4b /chromium/net/third_party/quiche | |
parent | 5d013f5804a0d91fcf6c626b2d6fb6eca5c845b0 (diff) | |
download | qtwebengine-chromium-03c549e0392f92c02536d3f86d5e1d8dfa3435ac.tar.gz |
BASELINE: Update Chromium to 91.0.4472.160
Change-Id: I0def1f08a2412aeed79a9ab95dd50eb5c3f65f31
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/net/third_party/quiche')
425 files changed, 12184 insertions, 7233 deletions
diff --git a/chromium/net/third_party/quiche/BUILD.gn b/chromium/net/third_party/quiche/BUILD.gn index e74d858ff3b..7b567969912 100644 --- a/chromium/net/third_party/quiche/BUILD.gn +++ b/chromium/net/third_party/quiche/BUILD.gn @@ -45,7 +45,6 @@ source_set("quiche") { "src/common/platform/api/quiche_flag_utils.h", "src/common/platform/api/quiche_flags.h", "src/common/platform/api/quiche_logging.h", - "src/common/platform/api/quiche_string_piece.h", "src/common/platform/api/quiche_text_utils.h", "src/common/platform/api/quiche_time_utils.h", "src/common/quiche_data_reader.cc", @@ -123,8 +122,6 @@ source_set("quiche") { "src/http2/hpack/decoder/hpack_whole_entry_listener.cc", "src/http2/hpack/decoder/hpack_whole_entry_listener.h", "src/http2/hpack/hpack_static_table_entries.inc", - "src/http2/hpack/hpack_string.cc", - "src/http2/hpack/hpack_string.h", "src/http2/hpack/http2_hpack_constants.cc", "src/http2/hpack/http2_hpack_constants.h", "src/http2/hpack/huffman/hpack_huffman_decoder.cc", @@ -346,10 +343,16 @@ source_set("quiche") { "src/quic/core/http/quic_receive_control_stream.h", "src/quic/core/http/quic_send_control_stream.cc", "src/quic/core/http/quic_send_control_stream.h", + "src/quic/core/http/quic_server_initiated_spdy_stream.cc", + "src/quic/core/http/quic_server_initiated_spdy_stream.h", "src/quic/core/http/quic_server_session_base.cc", "src/quic/core/http/quic_server_session_base.h", + "src/quic/core/http/quic_spdy_client_session.cc", + "src/quic/core/http/quic_spdy_client_session.h", "src/quic/core/http/quic_spdy_client_session_base.cc", "src/quic/core/http/quic_spdy_client_session_base.h", + "src/quic/core/http/quic_spdy_client_stream.cc", + "src/quic/core/http/quic_spdy_client_stream.h", "src/quic/core/http/quic_spdy_session.cc", "src/quic/core/http/quic_spdy_session.h", "src/quic/core/http/quic_spdy_stream.cc", @@ -360,6 +363,8 @@ source_set("quiche") { "src/quic/core/http/spdy_server_push_utils.h", "src/quic/core/http/spdy_utils.cc", "src/quic/core/http/spdy_utils.h", + "src/quic/core/http/web_transport_http3.cc", + "src/quic/core/http/web_transport_http3.h", "src/quic/core/legacy_quic_stream_id_manager.cc", "src/quic/core/legacy_quic_stream_id_manager.h", "src/quic/core/packet_number_indexed_queue.h", @@ -428,6 +433,8 @@ source_set("quiche") { "src/quic/core/quic_connection.h", "src/quic/core/quic_connection_id.cc", "src/quic/core/quic_connection_id.h", + "src/quic/core/quic_connection_id_manager.cc", + "src/quic/core/quic_connection_id_manager.h", "src/quic/core/quic_connection_stats.cc", "src/quic/core/quic_connection_stats.h", "src/quic/core/quic_constants.cc", @@ -535,6 +542,8 @@ source_set("quiche") { "src/quic/core/uber_quic_stream_id_manager.h", "src/quic/core/uber_received_packet_manager.cc", "src/quic/core/uber_received_packet_manager.h", + "src/quic/core/web_transport_stream_adapter.cc", + "src/quic/core/web_transport_stream_adapter.h", "src/quic/platform/api/quic_bug_tracker.h", "src/quic/platform/api/quic_client_stats.h", "src/quic/platform/api/quic_containers.h", @@ -560,7 +569,6 @@ source_set("quiche") { "src/quic/platform/api/quic_mutex.cc", "src/quic/platform/api/quic_mutex.h", "src/quic/platform/api/quic_prefetch.h", - "src/quic/platform/api/quic_ptr_util.h", "src/quic/platform/api/quic_reference_counted.h", "src/quic/platform/api/quic_server_stats.h", "src/quic/platform/api/quic_sleep.h", @@ -568,7 +576,6 @@ source_set("quiche") { "src/quic/platform/api/quic_socket_address.h", "src/quic/platform/api/quic_stack_trace.h", "src/quic/platform/api/quic_thread.h", - "src/quic/platform/api/quic_uint128.h", "src/quic/quic_transport/quic_transport_client_session.cc", "src/quic/quic_transport/quic_transport_client_session.h", "src/quic/quic_transport/quic_transport_protocol.h", @@ -579,7 +586,6 @@ source_set("quiche") { "src/quic/quic_transport/quic_transport_stream.h", "src/quic/quic_transport/web_transport_fingerprint_proof_verifier.cc", "src/quic/quic_transport/web_transport_fingerprint_proof_verifier.h", - "src/spdy/core/fifo_write_scheduler.h", "src/spdy/core/hpack/hpack_constants.cc", "src/spdy/core/hpack/hpack_constants.h", "src/spdy/core/hpack/hpack_decoder_adapter.cc", @@ -597,7 +603,6 @@ source_set("quiche") { "src/spdy/core/http2_frame_decoder_adapter.cc", "src/spdy/core/http2_frame_decoder_adapter.h", "src/spdy/core/http2_priority_write_scheduler.h", - "src/spdy/core/lifo_write_scheduler.h", "src/spdy/core/priority_write_scheduler.h", "src/spdy/core/recording_headers_handler.cc", "src/spdy/core/recording_headers_handler.h", @@ -616,6 +621,8 @@ source_set("quiche") { "src/spdy/core/spdy_header_storage.h", "src/spdy/core/spdy_headers_handler_interface.h", "src/spdy/core/spdy_intrusive_list.h", + "src/spdy/core/spdy_no_op_visitor.cc", + "src/spdy/core/spdy_no_op_visitor.h", "src/spdy/core/spdy_pinnable_buffer_piece.cc", "src/spdy/core/spdy_pinnable_buffer_piece.h", "src/spdy/core/spdy_prefixed_buffer_reader.cc", @@ -631,7 +638,6 @@ source_set("quiche") { "src/spdy/platform/api/spdy_estimate_memory_usage.h", "src/spdy/platform/api/spdy_flags.h", "src/spdy/platform/api/spdy_logging.h", - "src/spdy/platform/api/spdy_mem_slice.h", "src/spdy/platform/api/spdy_string_utils.h", ] } @@ -949,6 +955,8 @@ source_set("quic_test_tools_core") { "src/quic/test_tools/quic_stream_sequencer_peer.h", "src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.cc", "src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h", + "src/quic/test_tools/quic_test_backend.cc", + "src/quic/test_tools/quic_test_backend.h", "src/quic/test_tools/quic_test_utils.cc", "src/quic/test_tools/quic_test_utils.h", "src/quic/test_tools/quic_time_wait_list_manager_peer.cc", @@ -1044,10 +1052,6 @@ source_set("simple_quic_tools_core") { sources = [ "src/quic/core/chlo_extractor.cc", "src/quic/core/chlo_extractor.h", - "src/quic/core/http/quic_spdy_client_session.cc", - "src/quic/core/http/quic_spdy_client_session.h", - "src/quic/core/http/quic_spdy_client_stream.cc", - "src/quic/core/http/quic_spdy_client_stream.h", "src/quic/core/http/quic_spdy_server_stream_base.cc", "src/quic/core/http/quic_spdy_server_stream_base.h", "src/quic/core/quic_buffered_packet_store.cc", @@ -1224,7 +1228,6 @@ source_set("quiche_tests") { "src/http2/hpack/decoder/hpack_string_collector.h", "src/http2/hpack/decoder/hpack_string_decoder_test.cc", "src/http2/hpack/decoder/hpack_whole_entry_buffer_test.cc", - "src/http2/hpack/hpack_string_test.cc", "src/http2/hpack/http2_hpack_constants_test.cc", "src/http2/hpack/huffman/hpack_huffman_decoder_test.cc", "src/http2/hpack/huffman/hpack_huffman_encoder_test.cc", @@ -1344,6 +1347,7 @@ source_set("quiche_tests") { "src/quic/core/quic_circular_deque_test.cc", "src/quic/core/quic_coalesced_packet_test.cc", "src/quic/core/quic_config_test.cc", + "src/quic/core/quic_connection_id_manager_test.cc", "src/quic/core/quic_connection_id_test.cc", "src/quic/core/quic_connection_test.cc", "src/quic/core/quic_control_frame_manager_test.cc", @@ -1420,7 +1424,6 @@ source_set("quiche_tests") { "src/spdy/core/array_output_buffer.cc", "src/spdy/core/array_output_buffer.h", "src/spdy/core/array_output_buffer_test.cc", - "src/spdy/core/fifo_write_scheduler_test.cc", "src/spdy/core/hpack/hpack_decoder_adapter_test.cc", "src/spdy/core/hpack/hpack_encoder_test.cc", "src/spdy/core/hpack/hpack_entry_test.cc", @@ -1429,7 +1432,6 @@ source_set("quiche_tests") { "src/spdy/core/hpack/hpack_round_trip_test.cc", "src/spdy/core/hpack/hpack_static_table_test.cc", "src/spdy/core/http2_priority_write_scheduler_test.cc", - "src/spdy/core/lifo_write_scheduler_test.cc", "src/spdy/core/mock_spdy_framer_visitor.cc", "src/spdy/core/mock_spdy_framer_visitor.h", "src/spdy/core/priority_write_scheduler_test.cc", @@ -1440,8 +1442,6 @@ source_set("quiche_tests") { "src/spdy/core/spdy_header_block_test.cc", "src/spdy/core/spdy_header_storage_test.cc", "src/spdy/core/spdy_intrusive_list_test.cc", - "src/spdy/core/spdy_no_op_visitor.cc", - "src/spdy/core/spdy_no_op_visitor.h", "src/spdy/core/spdy_pinnable_buffer_piece_test.cc", "src/spdy/core/spdy_prefixed_buffer_reader_test.cc", "src/spdy/core/spdy_protocol_test.cc", @@ -1450,7 +1450,6 @@ source_set("quiche_tests") { "src/spdy/core/spdy_simple_arena_test.cc", "src/spdy/core/spdy_test_utils.cc", "src/spdy/core/spdy_test_utils.h", - "src/spdy/platform/api/spdy_mem_slice_test.cc", "src/spdy/platform/api/spdy_string_utils_test.cc", "src/spdy/platform/api/spdy_test_helpers.h", ] @@ -1490,10 +1489,6 @@ source_set("quiche_tests") { "//net:epoll_server", "//net:epoll_server_test_tools", ] - - if (!is_chromeos_ash) { - sources += [ "src/quic/core/quic_udp_socket_test.cc" ] - } } } diff --git a/chromium/net/third_party/quiche/src/common/platform/api/quiche_bug_tracker.h b/chromium/net/third_party/quiche/src/common/platform/api/quiche_bug_tracker.h new file mode 100644 index 00000000000..0e8e456209a --- /dev/null +++ b/chromium/net/third_party/quiche/src/common/platform/api/quiche_bug_tracker.h @@ -0,0 +1,15 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_COMMON_PLATFORM_API_QUICHE_BUG_TRACKER_H_ +#define QUICHE_COMMON_PLATFORM_API_QUICHE_BUG_TRACKER_H_ + +#include "net/quiche/common/platform/impl/quiche_bug_tracker_impl.h" + +#define QUICHE_BUG QUICHE_BUG_IMPL +#define QUICHE_BUG_IF QUICHE_BUG_IF_IMPL +#define QUICHE_PEER_BUG QUICHE_PEER_BUG_IMPL +#define QUICHE_PEER_BUG_IF QUICHE_PEER_BUG_IF_IMPL + +#endif // QUICHE_COMMON_PLATFORM_API_QUICHE_BUG_TRACKER_H_ diff --git a/chromium/net/third_party/quiche/src/common/platform/api/quiche_logging.h b/chromium/net/third_party/quiche/src/common/platform/api/quiche_logging.h index e3278ebfc37..940d492199d 100644 --- a/chromium/net/third_party/quiche/src/common/platform/api/quiche_logging.h +++ b/chromium/net/third_party/quiche/src/common/platform/api/quiche_logging.h @@ -40,6 +40,7 @@ #define QUICHE_LOG_ERROR_IS_ON() QUICHE_LOG_ERROR_IS_ON_IMPL() #define QUICHE_CHECK(condition) QUICHE_CHECK_IMPL(condition) +#define QUICHE_CHECK_OK(condition) QUICHE_CHECK_OK_IMPL(condition) #define QUICHE_CHECK_EQ(val1, val2) QUICHE_CHECK_EQ_IMPL(val1, val2) #define QUICHE_CHECK_NE(val1, val2) QUICHE_CHECK_NE_IMPL(val1, val2) #define QUICHE_CHECK_LE(val1, val2) QUICHE_CHECK_LE_IMPL(val1, val2) diff --git a/chromium/net/third_party/quiche/src/common/platform/api/quiche_string_piece.h b/chromium/net/third_party/quiche/src/common/platform/api/quiche_string_piece.h deleted file mode 100644 index ca58aae6d68..00000000000 --- a/chromium/net/third_party/quiche/src/common/platform/api/quiche_string_piece.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef QUICHE_COMMON_PLATFORM_API_QUICHE_STRING_PIECE_H_ -#define QUICHE_COMMON_PLATFORM_API_QUICHE_STRING_PIECE_H_ - -#include "absl/strings/string_view.h" -#include "net/quiche/common/platform/impl/quiche_string_piece_impl.h" - -namespace quiche { - -inline size_t QuicheHashStringPair(absl::string_view a, absl::string_view b) { - return QuicheHashStringPairImpl(a, b); -} - -} // namespace quiche - -#endif // QUICHE_COMMON_PLATFORM_API_QUICHE_STRING_PIECE_H_ diff --git a/chromium/net/third_party/quiche/src/common/platform/api/quiche_text_utils.h b/chromium/net/third_party/quiche/src/common/platform/api/quiche_text_utils.h index 1d3a86eb440..42b4115a5de 100644 --- a/chromium/net/third_party/quiche/src/common/platform/api/quiche_text_utils.h +++ b/chromium/net/third_party/quiche/src/common/platform/api/quiche_text_utils.h @@ -28,17 +28,6 @@ class QUICHE_EXPORT QuicheTextUtils { quiche::QuicheTextUtilsImpl::RemoveLeadingAndTrailingWhitespace(data); } - // Returns a new string representing |in|. - static std::string Uint64ToString(uint64_t in) { - return quiche::QuicheTextUtilsImpl::Uint64ToString(in); - } - - // This converts a uint32 into an 8-character hexidecimal - // representation. Return value: 8 characters of ASCII string. - static std::string Hex(uint32_t v) { - return quiche::QuicheTextUtilsImpl::Hex(v); - } - // Base64 encodes with no padding |data_len| bytes of |data| into |output|. static void Base64Encode(const uint8_t* data, size_t data_len, diff --git a/chromium/net/third_party/quiche/src/common/platform/api/quiche_text_utils_test.cc b/chromium/net/third_party/quiche/src/common/platform/api/quiche_text_utils_test.cc index 634e7354c93..d6d1f4d443e 100644 --- a/chromium/net/third_party/quiche/src/common/platform/api/quiche_text_utils_test.cc +++ b/chromium/net/third_party/quiche/src/common/platform/api/quiche_text_utils_test.cc @@ -33,11 +33,6 @@ TEST_F(QuicheTextUtilsTest, RemoveLeadingAndTrailingWhitespace) { } } -TEST_F(QuicheTextUtilsTest, Uint64ToString) { - EXPECT_EQ("123", quiche::QuicheTextUtils::Uint64ToString(123)); - EXPECT_EQ("1234", quiche::QuicheTextUtils::Uint64ToString(1234)); -} - TEST_F(QuicheTextUtilsTest, HexDump) { // Verify output of the HexDump method is as expected. char packet[] = { diff --git a/chromium/net/third_party/quiche/src/common/quiche_endian.h b/chromium/net/third_party/quiche/src/common/quiche_endian.h index f322b044062..032e2da4b93 100644 --- a/chromium/net/third_party/quiche/src/common/quiche_endian.h +++ b/chromium/net/third_party/quiche/src/common/quiche_endian.h @@ -19,8 +19,7 @@ enum Endianness { }; // Provide utility functions that convert from/to network order (big endian) -// to/from host order (can be either little or big endian depending on the -// platform). +// to/from host order (little endian). class QUICHE_EXPORT_PRIVATE QuicheEndian { public: // Convert |x| from host order (little endian) to network order (big endian). @@ -41,9 +40,6 @@ class QUICHE_EXPORT_PRIVATE QuicheEndian { static uint32_t NetToHost32(uint32_t x) { return HostToNet32(x); } static uint64_t NetToHost64(uint64_t x) { return HostToNet64(x); } - // Returns true if current host order is little endian. - static bool HostIsLittleEndian() { return true; } - // Left public for tests. template <typename T> static T PortableByteSwap(T input) { diff --git a/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server.cc b/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server.cc index 09831dbf764..28c301eb1d9 100644 --- a/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server.cc +++ b/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server.cc @@ -444,7 +444,7 @@ void SimpleEpollServer::RegisterAlarm(int64_t timeout_time_in_us, AlarmCB* ac) { EPOLL_VLOG(4) << "RegisteringAlarm " << ac << " at : " << timeout_time_in_us; CHECK(ac); if (all_alarms_.find(ac) != all_alarms_.end()) { - EPOLL_BUG << "Alarm already exists"; + EPOLL_BUG(epoll_bug_1_1) << "Alarm already exists"; } auto alarm_iter = alarm_map_.insert(std::make_pair(timeout_time_in_us, ac)); diff --git a/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server.h b/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server.h index d2d36837826..85b3a08f1bc 100644 --- a/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server.h +++ b/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server.h @@ -487,6 +487,7 @@ class EPOLL_EXPORT_PRIVATE SimpleEpollServer { // Summary: // Returns true when the SimpleEpollServer() is being destroyed. bool in_shutdown() const { return in_shutdown_; } + bool ShutdownCalled() const { return in_shutdown(); } // Compatibility stub. void Shutdown() {} 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 new file mode 100644 index 00000000000..5ea6045af22 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/http2_adapter.h @@ -0,0 +1,99 @@ +#ifndef QUICHE_HTTP2_ADAPTER_HTTP2_ADAPTER_H_ +#define QUICHE_HTTP2_ADAPTER_HTTP2_ADAPTER_H_ + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "absl/types/span.h" +#include "http2/adapter/http2_protocol.h" +#include "http2/adapter/http2_session.h" +#include "http2/adapter/http2_visitor_interface.h" + +namespace http2 { +namespace adapter { + +// Http2Adapter is an HTTP/2-processing class that exposes an interface similar +// to the nghttp2 library for processing the HTTP/2 wire format. As nghttp2 +// parses HTTP/2 frames and invokes callbacks on Http2Adapter, Http2Adapter then +// invokes corresponding callbacks on its passed-in Http2VisitorInterface. +// Http2Adapter is a base class shared between client-side and server-side +// implementations. +class Http2Adapter { + public: + enum class Perspective { + kClient, + kServer, + }; + + Http2Adapter(const Http2Adapter&) = delete; + Http2Adapter& operator=(const Http2Adapter&) = delete; + + // Processes the incoming |bytes| as HTTP/2 and invokes callbacks on the + // |visitor_| as appropriate. + virtual ssize_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. + virtual void SubmitSettings(absl::Span<const Http2Setting> settings) = 0; + + // Submits a PRIORITY frame for the given stream. + virtual void SubmitPriorityForStream(Http2StreamId stream_id, + Http2StreamId parent_stream_id, + int weight, + bool exclusive) = 0; + + // Submits a PING on the connection. Note that nghttp2 automatically submits + // PING acks upon receiving non-ack PINGs from the peer, so callers only use + // this method to originate PINGs. See nghttp2_option_set_no_auto_ping_ack(). + virtual void SubmitPing(Http2PingId ping_id) = 0; + + // Submits a GOAWAY on the connection. Note that |last_accepted_stream_id| + // refers to stream IDs initiated by the peer. For client-side, this last + // stream ID must be even (or 0); for server-side, this last stream ID must be + // odd (or 0). To submit a GOAWAY with |last_accepted_stream_id| with the + // maximum stream ID, signaling imminent connection termination, call + // SubmitShutdownNotice() instead (though this is only possible server-side). + virtual void SubmitGoAway(Http2StreamId last_accepted_stream_id, + Http2ErrorCode error_code, + absl::string_view opaque_data) = 0; + + // Submits a WINDOW_UPDATE for the given stream (a |stream_id| of 0 indicates + // a connection-level WINDOW_UPDATE). + virtual void SubmitWindowUpdate(Http2StreamId stream_id, + int window_increment) = 0; + + // Submits a METADATA frame for the given stream (a |stream_id| of 0 indicates + // connection-level METADATA). If |fin|, the frame will also have the + // END_METADATA flag set. + virtual void SubmitMetadata(Http2StreamId stream_id, bool fin) = 0; + + // Returns serialized bytes for writing to the wire. + // Writes should be submitted to Http2Adapter first, so that Http2Adapter + // has data to serialize and return in this method. + virtual std::string GetBytesToWrite(absl::optional<size_t> max_bytes) = 0; + + // Returns the connection-level flow control window for the peer. + virtual int GetPeerConnectionWindow() const = 0; + + // Marks the given amount of data as consumed for the given stream, which + // enables the nghttp2 layer to trigger WINDOW_UPDATEs as appropriate. + virtual void MarkDataConsumedForStream(Http2StreamId stream_id, + size_t num_bytes) = 0; + + protected: + // 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_; } + + private: + // Http2Adapter will invoke callbacks upon the |visitor_| while processing. + Http2VisitorInterface& visitor_; +}; + +} // namespace adapter +} // namespace http2 + +#endif // QUICHE_HTTP2_ADAPTER_HTTP2_ADAPTER_H_ diff --git a/chromium/net/third_party/quiche/src/http2/adapter/http2_protocol.cc b/chromium/net/third_party/quiche/src/http2/adapter/http2_protocol.cc new file mode 100644 index 00000000000..18e8985ff4b --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/http2_protocol.cc @@ -0,0 +1,68 @@ +#include "http2/adapter/http2_protocol.h" + +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +namespace http2 { +namespace adapter { + +const char kHttp2MethodPseudoHeader[] = ":method"; +const char kHttp2SchemePseudoHeader[] = ":scheme"; +const char kHttp2AuthorityPseudoHeader[] = ":authority"; +const char kHttp2PathPseudoHeader[] = ":path"; +const char kHttp2StatusPseudoHeader[] = ":status"; + +absl::string_view Http2SettingsIdToString(uint16_t id) { + switch (id) { + case Http2KnownSettingsId::HEADER_TABLE_SIZE: + return "SETTINGS_HEADER_TABLE_SIZE"; + case Http2KnownSettingsId::ENABLE_PUSH: + return "SETTINGS_ENABLE_PUSH"; + case Http2KnownSettingsId::MAX_CONCURRENT_STREAMS: + return "SETTINGS_MAX_CONCURRENT_STREAMS"; + case Http2KnownSettingsId::INITIAL_WINDOW_SIZE: + return "SETTINGS_INITIAL_WINDOW_SIZE"; + case Http2KnownSettingsId::MAX_FRAME_SIZE: + return "SETTINGS_MAX_FRAME_SIZE"; + case Http2KnownSettingsId::MAX_HEADER_LIST_SIZE: + return "SETTINGS_MAX_HEADER_LIST_SIZE"; + } + return "SETTINGS_UNKNOWN"; +} + +absl::string_view Http2ErrorCodeToString(Http2ErrorCode error_code) { + switch (error_code) { + case Http2ErrorCode::NO_ERROR: + return "NO_ERROR"; + case Http2ErrorCode::PROTOCOL_ERROR: + return "PROTOCOL_ERROR"; + case Http2ErrorCode::INTERNAL_ERROR: + return "INTERNAL_ERROR"; + case Http2ErrorCode::FLOW_CONTROL_ERROR: + return "FLOW_CONTROL_ERROR"; + case Http2ErrorCode::SETTINGS_TIMEOUT: + return "SETTINGS_TIMEOUT"; + case Http2ErrorCode::STREAM_CLOSED: + return "STREAM_CLOSED"; + case Http2ErrorCode::FRAME_SIZE_ERROR: + return "FRAME_SIZE_ERROR"; + case Http2ErrorCode::REFUSED_STREAM: + return "REFUSED_STREAM"; + case Http2ErrorCode::CANCEL: + return "CANCEL"; + case Http2ErrorCode::COMPRESSION_ERROR: + return "COMPRESSION_ERROR"; + case Http2ErrorCode::CONNECT_ERROR: + return "CONNECT_ERROR"; + case Http2ErrorCode::ENHANCE_YOUR_CALM: + return "ENHANCE_YOUR_CALM"; + case Http2ErrorCode::INADEQUATE_SECURITY: + return "INADEQUATE_SECURITY"; + case Http2ErrorCode::HTTP_1_1_REQUIRED: + return "HTTP_1_1_REQUIRED"; + } + return "UNKNOWN_ERROR"; +} + +} // namespace adapter +} // namespace http2 diff --git a/chromium/net/third_party/quiche/src/http2/adapter/http2_protocol.h b/chromium/net/third_party/quiche/src/http2/adapter/http2_protocol.h new file mode 100644 index 00000000000..49e5f161620 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/http2_protocol.h @@ -0,0 +1,105 @@ +#ifndef QUICHE_HTTP2_ADAPTER_HTTP2_PROTOCOL_H_ +#define QUICHE_HTTP2_ADAPTER_HTTP2_PROTOCOL_H_ + +#include <cstdint> +#include <string> +#include <utility> + +#include "base/integral_types.h" +#include "absl/base/attributes.h" +#include "absl/strings/string_view.h" + +namespace http2 { +namespace adapter { + +// Represents an HTTP/2 stream ID, consistent with nghttp2. +using Http2StreamId = int32_t; + +// Represents an HTTP/2 SETTINGS parameter as specified in RFC 7540 Section 6.5. +using Http2SettingsId = uint16_t; + +// Represents the payload of an HTTP/2 PING frame. +using Http2PingId = uint64_t; + +// Represents an HTTP/2 header field. A header field is a key-value pair with +// lowercase keys (as specified in RFC 7540 Section 8.1.2). +using Header = std::pair<std::string, std::string>; + +// Represents an HTTP/2 SETTINGS key-value parameter. +struct Http2Setting { + Http2SettingsId id; + uint32_t value; +}; + +// The maximum possible stream ID. +const Http2StreamId kMaxStreamId = 0x7FFFFFFF; + +// The stream ID that represents the connection (e.g., for connection-level flow +// control updates). +const Http2StreamId kConnectionStreamId = 0; + +// The default value for the size of the largest frame payload, according to RFC +// 7540 Section 6.5.2 (SETTINGS_MAX_FRAME_SIZE). +const int kDefaultFramePayloadSizeLimit = 16 * 1024; + +// The default value for the initial stream flow control window size, according +// to RFC 7540 Section 6.9.2. +const int kDefaultInitialStreamWindowSize = 64 * 1024 - 1; + +// The pseudo-header fields as specified in RFC 7540 Section 8.1.2.3 (request) +// and Section 8.1.2.4 (response). +ABSL_CONST_INIT extern const char kHttp2MethodPseudoHeader[]; +ABSL_CONST_INIT extern const char kHttp2SchemePseudoHeader[]; +ABSL_CONST_INIT extern const char kHttp2AuthorityPseudoHeader[]; +ABSL_CONST_INIT extern const char kHttp2PathPseudoHeader[]; +ABSL_CONST_INIT extern const char kHttp2StatusPseudoHeader[]; + +// HTTP/2 error codes as specified in RFC 7540 Section 7. +enum class Http2ErrorCode { + NO_ERROR = 0x0, + PROTOCOL_ERROR = 0x1, + INTERNAL_ERROR = 0x2, + FLOW_CONTROL_ERROR = 0x3, + SETTINGS_TIMEOUT = 0x4, + STREAM_CLOSED = 0x5, + FRAME_SIZE_ERROR = 0x6, + REFUSED_STREAM = 0x7, + CANCEL = 0x8, + COMPRESSION_ERROR = 0x9, + CONNECT_ERROR = 0xA, + ENHANCE_YOUR_CALM = 0xB, + INADEQUATE_SECURITY = 0xC, + HTTP_1_1_REQUIRED = 0xD, + MAX_ERROR_CODE = HTTP_1_1_REQUIRED, +}; + +// The SETTINGS parameters defined in RFC 7540 Section 6.5.2. Endpoints may send +// SETTINGS parameters outside of these definitions as per RFC 7540 Section 5.5. +// This is explicitly an enum instead of an enum class for ease of implicit +// conversion to the underlying Http2SettingsId type and use with non-standard +// extension SETTINGS parameters. +enum Http2KnownSettingsId : Http2SettingsId { + HEADER_TABLE_SIZE = 0x1, + MIN_SETTING = HEADER_TABLE_SIZE, + ENABLE_PUSH = 0x2, + MAX_CONCURRENT_STREAMS = 0x3, + INITIAL_WINDOW_SIZE = 0x4, + MAX_FRAME_SIZE = 0x5, + MAX_HEADER_LIST_SIZE = 0x6, + MAX_SETTING = MAX_HEADER_LIST_SIZE +}; + +// Returns a human-readable string representation of the given SETTINGS |id| for +// logging/debugging. Returns "SETTINGS_UNKNOWN" for IDs outside of the RFC 7540 +// Section 6.5.2 definitions. +absl::string_view Http2SettingsIdToString(uint16_t id); + +// Returns a human-readable string representation of the given |error_code| for +// logging/debugging. Returns "UNKNOWN_ERROR" for errors outside of RFC 7540 +// Section 7 definitions. +absl::string_view Http2ErrorCodeToString(Http2ErrorCode error_code); + +} // namespace adapter +} // namespace http2 + +#endif // QUICHE_HTTP2_ADAPTER_HTTP2_PROTOCOL_H_ 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 new file mode 100644 index 00000000000..ddbed4477b5 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/http2_session.h @@ -0,0 +1,41 @@ +#ifndef QUICHE_HTTP2_ADAPTER_HTTP2_SESSION_H_ +#define QUICHE_HTTP2_ADAPTER_HTTP2_SESSION_H_ + +#include <cstdint> + +#include "absl/strings/string_view.h" +#include "http2/adapter/http2_protocol.h" + +namespace http2 { +namespace adapter { + +struct Http2SessionCallbacks {}; + +// A class to represent the state of a single HTTP/2 connection. +class Http2Session { + public: + Http2Session() = default; + virtual ~Http2Session() {} + + virtual ssize_t ProcessBytes(absl::string_view bytes) = 0; + + virtual int Consume(Http2StreamId stream_id, size_t num_bytes) = 0; + + virtual bool want_read() const = 0; + virtual bool want_write() const = 0; + virtual int GetRemoteWindowSize() const = 0; +}; + +class Http2Options { + public: + Http2Options() = default; + virtual ~Http2Options() {} + + // This method returns an opaque reference to the underlying type. + virtual void* GetOptions() = 0; +}; + +} // namespace adapter +} // namespace http2 + +#endif // QUICHE_HTTP2_ADAPTER_HTTP2_SESSION_H_ diff --git a/chromium/net/third_party/quiche/src/http2/adapter/http2_util.cc b/chromium/net/third_party/quiche/src/http2/adapter/http2_util.cc new file mode 100644 index 00000000000..40deeb29332 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/http2_util.cc @@ -0,0 +1,40 @@ +#include "http2/adapter/http2_util.h" + +namespace http2 { +namespace adapter { + +spdy::SpdyErrorCode TranslateErrorCode(Http2ErrorCode code) { + switch (code) { + case Http2ErrorCode::NO_ERROR: + return spdy::ERROR_CODE_NO_ERROR; + case Http2ErrorCode::PROTOCOL_ERROR: + return spdy::ERROR_CODE_PROTOCOL_ERROR; + case Http2ErrorCode::INTERNAL_ERROR: + return spdy::ERROR_CODE_INTERNAL_ERROR; + case Http2ErrorCode::FLOW_CONTROL_ERROR: + return spdy::ERROR_CODE_FLOW_CONTROL_ERROR; + case Http2ErrorCode::SETTINGS_TIMEOUT: + return spdy::ERROR_CODE_SETTINGS_TIMEOUT; + case Http2ErrorCode::STREAM_CLOSED: + return spdy::ERROR_CODE_STREAM_CLOSED; + case Http2ErrorCode::FRAME_SIZE_ERROR: + return spdy::ERROR_CODE_FRAME_SIZE_ERROR; + case Http2ErrorCode::REFUSED_STREAM: + return spdy::ERROR_CODE_REFUSED_STREAM; + case Http2ErrorCode::CANCEL: + return spdy::ERROR_CODE_CANCEL; + case Http2ErrorCode::COMPRESSION_ERROR: + return spdy::ERROR_CODE_COMPRESSION_ERROR; + case Http2ErrorCode::CONNECT_ERROR: + return spdy::ERROR_CODE_CONNECT_ERROR; + case Http2ErrorCode::ENHANCE_YOUR_CALM: + return spdy::ERROR_CODE_ENHANCE_YOUR_CALM; + case Http2ErrorCode::INADEQUATE_SECURITY: + return spdy::ERROR_CODE_INADEQUATE_SECURITY; + case Http2ErrorCode::HTTP_1_1_REQUIRED: + return spdy::ERROR_CODE_HTTP_1_1_REQUIRED; + } +} + +} // namespace adapter +} // namespace http2 diff --git a/chromium/net/third_party/quiche/src/http2/adapter/http2_util.h b/chromium/net/third_party/quiche/src/http2/adapter/http2_util.h new file mode 100644 index 00000000000..e9ae2a59561 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/http2_util.h @@ -0,0 +1,15 @@ +#ifndef QUICHE_HTTP2_ADAPTER_HTTP2_UTIL_H_ +#define QUICHE_HTTP2_ADAPTER_HTTP2_UTIL_H_ + +#include "http2/adapter/http2_protocol.h" +#include "spdy/core/spdy_protocol.h" + +namespace http2 { +namespace adapter { + +spdy::SpdyErrorCode TranslateErrorCode(Http2ErrorCode code); + +} // namespace adapter +} // namespace http2 + +#endif // QUICHE_HTTP2_ADAPTER_HTTP2_UTIL_H_ 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 new file mode 100644 index 00000000000..765e9f43896 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/http2_visitor_interface.h @@ -0,0 +1,178 @@ +#ifndef QUICHE_HTTP2_ADAPTER_HTTP2_VISITOR_INTERFACE_H_ +#define QUICHE_HTTP2_ADAPTER_HTTP2_VISITOR_INTERFACE_H_ + +#include <vector> + +#include "absl/strings/string_view.h" +#include "http2/adapter/http2_protocol.h" + +namespace http2 { +namespace adapter { + +// Http2VisitorInterface contains callbacks for receiving HTTP/2-level events. A +// processor like NghttpAdapter parses HTTP/2 frames and invokes the callbacks +// on an instance of this interface. Prefer a void return type for these +// callbacks, instead setting output parameters as needed. +// +// Example sequences of calls/events: +// GET: +// - OnBeginHeadersForStream() +// - OnHeaderForStream() +// - OnEndHeadersForStream() +// - OnEndStream() +// +// POST: +// - OnBeginHeadersForStream() +// - OnHeaderForStream() +// - OnEndHeadersForStream() +// - OnBeginDataForStream() +// - OnDataForStream() +// - OnEndStream() +// +// Request canceled mid-stream, e.g, with error code CANCEL: +// - OnBeginHeadersForStream() +// - OnHeaderForStream() +// - OnEndHeadersForStream() +// - OnRstStream() +// - OnAbortStream() +// +// Request closed mid-stream, e.g., with error code NO_ERROR: +// - OnBeginHeadersForStream() +// - OnHeaderForStream() +// - OnEndHeadersForStream() +// - OnRstStream() +// - OnCloseStream() +// +// More details are at RFC 7540 (go/http2spec), and more examples are at +// http://google3/net/http2/server/lib/internal/h2/nghttp2/nghttp2_server_adapter_test.cc. +class Http2VisitorInterface { + public: + Http2VisitorInterface(const Http2VisitorInterface&) = delete; + Http2VisitorInterface& operator=(const Http2VisitorInterface&) = delete; + virtual ~Http2VisitorInterface() = default; + + // Called when a connection-level processing error has been encountered. + virtual void OnConnectionError() = 0; + + // Called when a non-ack SETTINGS frame is received. + virtual void OnSettingsStart() = 0; + + // Called for each SETTINGS id-value pair. + virtual void OnSetting(Http2Setting setting) = 0; + + // Called at the end of a non-ack SETTINGS frame. + virtual void OnSettingsEnd() = 0; + + // Called when a SETTINGS ack frame is received. + virtual void OnSettingsAck() = 0; + + // Called when the connection receives the header block for a HEADERS frame on + // a stream but has not yet parsed individual headers. + virtual void OnBeginHeadersForStream(Http2StreamId stream_id) = 0; + + // Called when the connection receives the header |key| and |value| for a + // stream. The HTTP/2 pseudo-headers defined in RFC 7540 Sections 8.1.2.3 and + // 8.1.2.4 are also conveyed in this callback. This method is called after + // OnBeginHeadersForStream(). + virtual void OnHeaderForStream(Http2StreamId stream_id, absl::string_view key, + absl::string_view value) = 0; + + // 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; + + // 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, + 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, + absl::string_view data) = 0; + + // Called when the peer sends the END_STREAM flag on a stream, indicating that + // the peer will not send additional headers or data for that stream. + virtual void OnEndStream(Http2StreamId stream_id) = 0; + + // Called when the connection receives a RST_STREAM for a stream. This call + // will be followed by either OnCloseStream() or OnAbortStream(). + virtual void OnRstStream(Http2StreamId stream_id, + Http2ErrorCode error_code) = 0; + + // Called when a stream is closed with error code NO_ERROR. Compare with + // OnAbortStream(). + virtual void OnCloseStream(Http2StreamId stream_id) = 0; + + // Called when a stream is aborted, i.e., closed for the reason indicated by + // the given |error_code|, where error_code != NO_ERROR. Compare with + // OnCloseStream(). + virtual void OnAbortStream(Http2StreamId stream_id, + Http2ErrorCode error_code) = 0; + + // Called when the connection receives a PRIORITY frame. + virtual void OnPriorityForStream(Http2StreamId stream_id, + Http2StreamId parent_stream_id, int weight, + bool exclusive) = 0; + + // Called when the connection receives a PING frame. + virtual void OnPing(Http2PingId ping_id, bool is_ack) = 0; + + // Called when the connection receives a PUSH_PROMISE frame. The server push + // request headers follow in calls to OnHeaderForStream() with |stream_id|. + virtual void OnPushPromiseForStream(Http2StreamId stream_id, + Http2StreamId promised_stream_id) = 0; + + // Called when the connection receives a GOAWAY frame. + virtual void OnGoAway(Http2StreamId last_accepted_stream_id, + Http2ErrorCode error_code, + absl::string_view opaque_data) = 0; + + // Called when the connection receives a WINDOW_UPDATE frame. For + // connection-level window updates, the |stream_id| will be 0. + virtual void OnWindowUpdate(Http2StreamId stream_id, + int window_increment) = 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 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(). + virtual void OnBeginMetadataForStream(Http2StreamId stream_id, + 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, + absl::string_view metadata) = 0; + + // Called when the connection has finished receiving a logical metadata block + // for a stream. Note that there may be multiple metadata blocks for a stream. + virtual void OnMetadataEndForStream(Http2StreamId stream_id) = 0; + + protected: + Http2VisitorInterface() = default; +}; + +} // namespace adapter +} // namespace http2 + +#endif // QUICHE_HTTP2_ADAPTER_HTTP2_VISITOR_INTERFACE_H_ 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 new file mode 100644 index 00000000000..a5c83712fd7 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/mock_http2_visitor.h @@ -0,0 +1,124 @@ +#ifndef QUICHE_HTTP2_ADAPTER_MOCK_HTTP2_VISITOR_INTERFACE_H_ +#define QUICHE_HTTP2_ADAPTER_MOCK_HTTP2_VISITOR_INTERFACE_H_ + +#include "http2/adapter/http2_visitor_interface.h" +#include "common/platform/api/quiche_test.h" + +namespace http2 { +namespace adapter { +namespace test { + +// A mock visitor class, for use in tests. +class MockHttp2Visitor : public Http2VisitorInterface { + public: + MockHttp2Visitor() = default; + + MOCK_METHOD(void, OnConnectionError, (), (override)); + MOCK_METHOD(void, OnSettingsStart, (), (override)); + MOCK_METHOD(void, OnSetting, (Http2Setting setting), (override)); + MOCK_METHOD(void, OnSettingsEnd, (), (override)); + MOCK_METHOD(void, OnSettingsAck, (), (override)); + MOCK_METHOD(void, + OnBeginHeadersForStream, + (Http2StreamId stream_id), + (override)); + + MOCK_METHOD(void, + OnHeaderForStream, + (Http2StreamId stream_id, + absl::string_view key, + absl::string_view value), + (override)); + + MOCK_METHOD(void, + OnEndHeadersForStream, + (Http2StreamId stream_id), + (override)); + + MOCK_METHOD(void, + OnBeginDataForStream, + (Http2StreamId stream_id, size_t payload_length), + (override)); + + MOCK_METHOD(void, + OnDataForStream, + (Http2StreamId stream_id, absl::string_view data), + (override)); + + MOCK_METHOD(void, OnEndStream, (Http2StreamId stream_id), (override)); + + MOCK_METHOD(void, + OnRstStream, + (Http2StreamId stream_id, Http2ErrorCode error_code), + (override)); + + MOCK_METHOD(void, OnCloseStream, (Http2StreamId stream_id), (override)); + + MOCK_METHOD(void, + OnAbortStream, + (Http2StreamId stream_id, Http2ErrorCode error_code), + (override)); + + MOCK_METHOD(void, + OnPriorityForStream, + (Http2StreamId stream_id, + Http2StreamId parent_stream_id, + int weight, + bool exclusive), + (override)); + + MOCK_METHOD(void, OnPing, (Http2PingId ping_id, bool is_ack), (override)); + + MOCK_METHOD(void, + OnPushPromiseForStream, + (Http2StreamId stream_id, Http2StreamId promised_stream_id), + (override)); + + MOCK_METHOD(void, + OnGoAway, + (Http2StreamId last_accepted_stream_id, + Http2ErrorCode error_code, + absl::string_view opaque_data), + (override)); + + MOCK_METHOD(void, + OnWindowUpdate, + (Http2StreamId stream_id, int window_increment), + (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, + (Http2StreamId stream_id, absl::string_view metadata), + (override)); + + MOCK_METHOD(void, + OnMetadataEndForStream, + (Http2StreamId stream_id), + (override)); +}; + +} // namespace test +} // namespace adapter +} // namespace http2 + +#endif // QUICHE_HTTP2_ADAPTER_MOCK_HTTP2_VISITOR_INTERFACE_H_ 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 new file mode 100644 index 00000000000..337f230b796 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.cc @@ -0,0 +1,189 @@ +#include "http2/adapter/nghttp2_callbacks.h" + +#include <cstdint> +#include <cstring> + +#include "absl/strings/string_view.h" +#include "http2/adapter/http2_protocol.h" +#include "http2/adapter/http2_visitor_interface.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_logging.h" +#include "common/quiche_endian.h" + +namespace http2 { +namespace adapter { + +int OnBeginFrame(nghttp2_session* /* session */, + const nghttp2_frame_hd* header, + void* user_data) { + auto* visitor = static_cast<Http2VisitorInterface*>(user_data); + if (header->type == NGHTTP2_DATA) { + visitor->OnBeginDataForStream(header->stream_id, header->length); + } + return 0; +} + +int OnFrameReceived(nghttp2_session* /* session */, + const nghttp2_frame* frame, + void* user_data) { + auto* visitor = static_cast<Http2VisitorInterface*>(user_data); + const Http2StreamId stream_id = frame->hd.stream_id; + switch (frame->hd.type) { + // The beginning of the DATA frame is handled in OnBeginFrame(), and the + // beginning of the header block is handled in client/server-specific + // callbacks. This callback handles the point at which the entire logical + // frame has been received and processed. + case NGHTTP2_DATA: + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + visitor->OnEndStream(stream_id); + } + break; + case NGHTTP2_HEADERS: { + if (frame->hd.flags & NGHTTP2_FLAG_END_HEADERS) { + visitor->OnEndHeadersForStream(stream_id); + } + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + visitor->OnEndStream(stream_id); + } + break; + } + case NGHTTP2_PRIORITY: { + nghttp2_priority_spec priority_spec = frame->priority.pri_spec; + visitor->OnPriorityForStream(stream_id, priority_spec.stream_id, + priority_spec.weight, + priority_spec.exclusive != 0); + break; + } + case NGHTTP2_RST_STREAM: { + visitor->OnRstStream(stream_id, + ToHttp2ErrorCode(frame->rst_stream.error_code)); + break; + } + case NGHTTP2_SETTINGS: + if (frame->hd.flags & NGHTTP2_FLAG_ACK) { + visitor->OnSettingsAck(); + } else { + visitor->OnSettingsStart(); + for (int i = 0; i < frame->settings.niv; ++i) { + 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}); + } + visitor->OnSettingsEnd(); + } + break; + case NGHTTP2_PUSH_PROMISE: + // This case is handled by headers-related callbacks: + // 1. visitor->OnPushPromiseForStream() is invoked in the client-side + // OnHeadersStart() adapter callback, as nghttp2 only allows clients + // to receive PUSH_PROMISE frames. + // 2. visitor->OnHeaderForStream() is invoked for each server push + // request header in the PUSH_PROMISE header block. + // 3. This switch statement is reached once all server push request + // headers have been parsed. + break; + case NGHTTP2_PING: { + Http2PingId ping_id; + std::memcpy(&ping_id, frame->ping.opaque_data, sizeof(Http2PingId)); + visitor->OnPing(quiche::QuicheEndian::NetToHost64(ping_id), + (frame->hd.flags & NGHTTP2_FLAG_ACK) != 0); + break; + } + case NGHTTP2_GOAWAY: { + 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); + break; + } + case NGHTTP2_WINDOW_UPDATE: { + visitor->OnWindowUpdate(stream_id, + frame->window_update.window_size_increment); + break; + } + case NGHTTP2_CONTINUATION: + // This frame type should not be passed to any callbacks, according to + // https://nghttp2.org/documentation/enums.html#c.NGHTTP2_CONTINUATION. + QUICHE_LOG(ERROR) << "Unexpected receipt of NGHTTP2_CONTINUATION type!"; + break; + case NGHTTP2_ALTSVC: + break; + case NGHTTP2_ORIGIN: + break; + } + + return 0; +} + +int OnBeginHeaders(nghttp2_session* /* session */, + const nghttp2_frame* frame, + void* user_data) { + auto* visitor = static_cast<Http2VisitorInterface*>(user_data); + visitor->OnBeginHeadersForStream(frame->hd.stream_id); + return 0; +} + +int OnHeader(nghttp2_session* /* session */, + const nghttp2_frame* frame, + nghttp2_rcbuf* name, + nghttp2_rcbuf* value, + uint8_t flags, + void* user_data) { + auto* visitor = static_cast<Http2VisitorInterface*>(user_data); + visitor->OnHeaderForStream(frame->hd.stream_id, ToStringView(name), + ToStringView(value)); + return 0; +} + +int OnDataChunk(nghttp2_session* /* session */, + uint8_t flags, + Http2StreamId stream_id, + const uint8_t* data, + size_t len, + void* user_data) { + auto* visitor = static_cast<Http2VisitorInterface*>(user_data); + visitor->OnDataForStream( + stream_id, absl::string_view(reinterpret_cast<const char*>(data), len)); + return 0; +} + +int OnStreamClosed(nghttp2_session* /* session */, + Http2StreamId stream_id, + uint32_t error_code, + void* user_data) { + auto* visitor = static_cast<Http2VisitorInterface*>(user_data); + if (error_code == static_cast<uint32_t>(Http2ErrorCode::NO_ERROR)) { + visitor->OnCloseStream(stream_id); + } else { + visitor->OnAbortStream(stream_id, ToHttp2ErrorCode(error_code)); + } + return 0; +} + +ssize_t OnReadyToReadDataForStream(nghttp2_session* /* session */, + Http2StreamId stream_id, + uint8_t* dest_buffer, + size_t max_length, + uint32_t* data_flags, + nghttp2_data_source* source, + void* user_data) { + auto* visitor = static_cast<Http2VisitorInterface*>(source->ptr); + ssize_t bytes_to_send = 0; + bool end_stream = false; + visitor->OnReadyToSendDataForStream(stream_id, + reinterpret_cast<char*>(dest_buffer), + max_length, &bytes_to_send, &end_stream); + if (bytes_to_send >= 0 && end_stream) { + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + } + return bytes_to_send; +} + +} // namespace adapter +} // namespace http2 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 new file mode 100644 index 00000000000..7e6ce7c38d2 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_callbacks.h @@ -0,0 +1,52 @@ +#ifndef QUICHE_HTTP2_ADAPTER_NGHTTP2_CALLBACKS_H_ +#define QUICHE_HTTP2_ADAPTER_NGHTTP2_CALLBACKS_H_ + +#include "http2/adapter/http2_protocol.h" +#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" + +namespace http2 { +namespace adapter { + +// The following functions are nghttp2 callbacks that Nghttp2Adapter sets at the +// beginning of its lifetime. It is expected that |user_data| holds an +// Http2VisitorInterface. + +// Callback once a frame header has been received. +int OnBeginFrame(nghttp2_session* session, const nghttp2_frame_hd* header, + void* user_data); + +// Callback once a complete frame has been received. +int OnFrameReceived(nghttp2_session* session, const nghttp2_frame* frame, + void* user_data); + +// Callback at the start of a frame carrying headers. +int OnBeginHeaders(nghttp2_session* session, + const nghttp2_frame* frame, + void* user_data); + +// Callback once a name-value header has been received. +int OnHeader(nghttp2_session* session, const nghttp2_frame* frame, + nghttp2_rcbuf* name, nghttp2_rcbuf* value, uint8_t flags, + void* user_data); + +// Callback once a chunk of data (from a DATA frame payload) has been received. +int OnDataChunk(nghttp2_session* session, uint8_t flags, + Http2StreamId stream_id, const uint8_t* data, size_t len, + void* user_data); + +// Callback once a stream has been closed. +int OnStreamClosed(nghttp2_session* session, Http2StreamId stream_id, + uint32_t error_code, void* user_data); + +// Callback once nghttp2 is ready to read data from |source| into |dest_buffer|. +ssize_t OnReadyToReadDataForStream(nghttp2_session* session, + Http2StreamId stream_id, + uint8_t* dest_buffer, size_t max_length, + uint32_t* data_flags, + nghttp2_data_source* source, + void* user_data); + +} // namespace adapter +} // namespace http2 + +#endif // QUICHE_HTTP2_ADAPTER_NGHTTP2_CALLBACKS_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 new file mode 100644 index 00000000000..e1341481ae5 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.cc @@ -0,0 +1,82 @@ +#include "http2/adapter/nghttp2_util.h" + +#include <cstdint> + +#include "absl/strings/string_view.h" +#include "http2/adapter/http2_protocol.h" +#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" +#include "common/platform/api/quiche_logging.h" + +namespace http2 { +namespace adapter { + +uint8_t* ToUint8Ptr(char* str) { return reinterpret_cast<uint8_t*>(str); } +uint8_t* ToUint8Ptr(const char* str) { + return const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(str)); +} + +absl::string_view ToStringView(nghttp2_rcbuf* rc_buffer) { + nghttp2_vec buffer = nghttp2_rcbuf_get_buf(rc_buffer); + return absl::string_view(reinterpret_cast<const char*>(buffer.base), + buffer.len); +} + +absl::string_view ToStringView(uint8_t* pointer, size_t length) { + return absl::string_view(reinterpret_cast<const char*>(pointer), length); +} + +std::vector<nghttp2_nv> GetRequestNghttp2Nvs(absl::Span<const Header> headers) { + const int num_headers = headers.size(); + auto nghttp2_nvs = std::vector<nghttp2_nv>(num_headers); + for (int i = 0; i < num_headers; ++i) { + nghttp2_nv header; + header.name = ToUint8Ptr(&headers[i].first[0]); + header.namelen = headers[i].first.size(); + header.value = ToUint8Ptr(&headers[i].second[0]); + header.valuelen = headers[i].second.size(); + header.flags = NGHTTP2_FLAG_NONE; + nghttp2_nvs.push_back(std::move(header)); + } + + return nghttp2_nvs; +} + +std::vector<nghttp2_nv> GetResponseNghttp2Nvs( + const spdy::Http2HeaderBlock& headers, + absl::string_view response_code) { + // Allocate enough for all headers and also the :status pseudoheader. + const int num_headers = headers.size(); + auto nghttp2_nvs = std::vector<nghttp2_nv>(num_headers + 1); + + // Add the :status pseudoheader first. + nghttp2_nv status; + status.name = ToUint8Ptr(kHttp2StatusPseudoHeader); + status.namelen = strlen(kHttp2StatusPseudoHeader); + status.value = ToUint8Ptr(response_code.data()); + status.valuelen = response_code.size(); + status.flags = NGHTTP2_FLAG_NONE; + nghttp2_nvs.push_back(std::move(status)); + + // Add the remaining headers. + for (const auto header_pair : headers) { + nghttp2_nv header; + header.name = ToUint8Ptr(header_pair.first.data()); + header.namelen = header_pair.first.size(); + header.value = ToUint8Ptr(header_pair.second.data()); + header.valuelen = header_pair.second.size(); + header.flags = NGHTTP2_FLAG_NONE; + nghttp2_nvs.push_back(std::move(header)); + } + + return nghttp2_nvs; +} + +Http2ErrorCode ToHttp2ErrorCode(uint32_t wire_error_code) { + if (wire_error_code > static_cast<int>(Http2ErrorCode::MAX_ERROR_CODE)) { + return Http2ErrorCode::INTERNAL_ERROR; + } + return static_cast<Http2ErrorCode>(wire_error_code); +} + +} // namespace adapter +} // namespace http2 diff --git a/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.h b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.h new file mode 100644 index 00000000000..3fcbfcd068c --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/nghttp2_util.h @@ -0,0 +1,48 @@ +// Various utility/conversion functions for compatibility with the nghttp2 API. + +#ifndef QUICHE_HTTP2_ADAPTER_NGHTTP2_UTIL_H_ +#define QUICHE_HTTP2_ADAPTER_NGHTTP2_UTIL_H_ + +#include <cstdint> +#include <vector> + +#include "absl/strings/string_view.h" +#include "absl/types/span.h" +#include "http2/adapter/http2_protocol.h" +#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" +#include "spdy/core/spdy_header_block.h" + +namespace http2 { +namespace adapter { + +// Return codes to represent various errors. +inline constexpr int kStreamCallbackFailureStatus = + NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; +inline constexpr int kCancelStatus = NGHTTP2_ERR_CANCEL; + +uint8_t* ToUint8Ptr(char* str); +uint8_t* ToUint8Ptr(const char* str); + +absl::string_view ToStringView(nghttp2_rcbuf* rc_buffer); +absl::string_view ToStringView(uint8_t* pointer, size_t length); + +// Returns the nghttp2 header structure from the given request |headers|, which +// must have the correct pseudoheaders preceding other headers. +std::vector<nghttp2_nv> GetRequestNghttp2Nvs(absl::Span<const Header> headers); + +// Returns the nghttp2 header structure from the given response |headers|, with +// the :status pseudoheader first based on the given |response_code|. The +// |response_code| is passed in separately from |headers| for lifetime reasons. +std::vector<nghttp2_nv> GetResponseNghttp2Nvs( + const spdy::Http2HeaderBlock& headers, + absl::string_view response_code); + +// Returns the HTTP/2 error code corresponding to the raw wire value, as defined +// in RFC 7540 Section 7. Unrecognized error codes are treated as INTERNAL_ERROR +// based on the RFC 7540 Section 7 suggestion. +Http2ErrorCode ToHttp2ErrorCode(uint32_t wire_error_code); + +} // namespace adapter +} // namespace http2 + +#endif // QUICHE_HTTP2_ADAPTER_NGHTTP2_UTIL_H_ 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 new file mode 100644 index 00000000000..7b7d2a2229b --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.cc @@ -0,0 +1,164 @@ +#include "http2/adapter/oghttp2_adapter.h" + +#include <list> + +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" +#include "http2/adapter/http2_util.h" +#include "http2/adapter/window_manager.h" +#include "spdy/core/spdy_framer.h" +#include "spdy/core/spdy_protocol.h" + +namespace http2 { +namespace adapter { + +namespace { + +using spdy::SpdyFrameIR; +using spdy::SpdyGoAwayIR; +using spdy::SpdyPingIR; +using spdy::SpdyPriorityIR; +using spdy::SpdySettingsIR; +using spdy::SpdyWindowUpdateIR; + +} // namespace + +struct StreamState { + WindowManager window_manager; +}; + +class OgHttp2Adapter::OgHttp2Session : public Http2Session { + public: + OgHttp2Session(Http2VisitorInterface& /*visitor*/, Options /*options*/) {} + ~OgHttp2Session() override {} + + ssize_t ProcessBytes(absl::string_view bytes) override { + SPDY_BUG(oghttp2_process_bytes) << "Not implemented"; + return 0; + } + + int Consume(Http2StreamId stream_id, size_t num_bytes) override { + auto it = stream_map_.find(stream_id); + if (it == stream_map_.end()) { + // TODO(b/181586191): LOG_ERROR rather than SPDY_BUG. + SPDY_BUG(stream_consume_notfound) + << "Stream " << stream_id << " not found"; + } else { + it->second.window_manager.MarkDataFlushed(num_bytes); + } + return 0; // Remove? + } + + bool want_read() const override { return false; } + bool want_write() const override { + return !frames_.empty() || !serialized_prefix_.empty(); + } + int GetRemoteWindowSize() const override { + SPDY_BUG(peer_window_not_updated) << "Not implemented"; + return peer_window_; + } + + void EnqueueFrame(std::unique_ptr<spdy::SpdyFrameIR> frame) { + frames_.push_back(std::move(frame)); + } + + std::string GetBytesToWrite(absl::optional<size_t> max_bytes) { + const size_t serialized_max = + max_bytes ? max_bytes.value() : std::numeric_limits<size_t>::max(); + std::string serialized = std::move(serialized_prefix_); + while (serialized.size() < serialized_max && !frames_.empty()) { + spdy::SpdySerializedFrame frame = + framer_.SerializeFrame(*frames_.front()); + absl::StrAppend(&serialized, absl::string_view(frame)); + frames_.pop_front(); + } + if (serialized.size() > serialized_max) { + serialized_prefix_ = serialized.substr(serialized_max); + serialized.resize(serialized_max); + } + return serialized; + } + + private: + spdy::SpdyFramer framer_{spdy::SpdyFramer::ENABLE_COMPRESSION}; + absl::flat_hash_map<Http2StreamId, StreamState> stream_map_; + std::list<std::unique_ptr<SpdyFrameIR>> frames_; + std::string serialized_prefix_; + int peer_window_ = 65535; +}; + +/* static */ +std::unique_ptr<OgHttp2Adapter> OgHttp2Adapter::Create( + Http2VisitorInterface& visitor, + Options options) { + // Using `new` to access a non-public constructor. + return absl::WrapUnique(new OgHttp2Adapter(visitor, std::move(options))); +} + +OgHttp2Adapter::~OgHttp2Adapter() {} + +ssize_t OgHttp2Adapter::ProcessBytes(absl::string_view bytes) { + return session_->ProcessBytes(bytes); +} + +void OgHttp2Adapter::SubmitSettings(absl::Span<const Http2Setting> settings) { + auto settings_ir = absl::make_unique<SpdySettingsIR>(); + for (const Http2Setting& setting : settings) { + settings_ir->AddSetting(setting.id, setting.value); + } + session_->EnqueueFrame(std::move(settings_ir)); +} + +void OgHttp2Adapter::SubmitPriorityForStream(Http2StreamId stream_id, + Http2StreamId parent_stream_id, + int weight, + bool exclusive) { + session_->EnqueueFrame(absl::make_unique<SpdyPriorityIR>( + stream_id, parent_stream_id, weight, exclusive)); +} + +void OgHttp2Adapter::SubmitPing(Http2PingId ping_id) { + session_->EnqueueFrame(absl::make_unique<SpdyPingIR>(ping_id)); +} + +void OgHttp2Adapter::SubmitGoAway(Http2StreamId last_accepted_stream_id, + Http2ErrorCode error_code, + absl::string_view opaque_data) { + session_->EnqueueFrame(absl::make_unique<SpdyGoAwayIR>( + last_accepted_stream_id, TranslateErrorCode(error_code), + std::string(opaque_data))); +} +void OgHttp2Adapter::SubmitWindowUpdate(Http2StreamId stream_id, + int window_increment) { + session_->EnqueueFrame( + absl::make_unique<SpdyWindowUpdateIR>(stream_id, window_increment)); +} + +void OgHttp2Adapter::SubmitMetadata(Http2StreamId stream_id, bool fin) { + SPDY_BUG(oghttp2_submit_metadata) << "Not implemented"; +} + +std::string OgHttp2Adapter::GetBytesToWrite(absl::optional<size_t> max_bytes) { + return session_->GetBytesToWrite(max_bytes); +} + +int OgHttp2Adapter::GetPeerConnectionWindow() const { + return session_->GetRemoteWindowSize(); +} + +void OgHttp2Adapter::MarkDataConsumedForStream(Http2StreamId stream_id, + size_t num_bytes) { + session_->Consume(stream_id, num_bytes); +} + +const Http2Session& OgHttp2Adapter::session() const { + return *session_; +} + +OgHttp2Adapter::OgHttp2Adapter(Http2VisitorInterface& visitor, Options options) + : Http2Adapter(visitor), + session_(absl::make_unique<OgHttp2Session>(visitor, std::move(options))) { +} + +} // namespace adapter +} // namespace http2 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 new file mode 100644 index 00000000000..1f32cfc6ddd --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter.h @@ -0,0 +1,53 @@ +#ifndef QUICHE_HTTP2_ADAPTER_OGHTTP2_ADAPTER_H_ +#define QUICHE_HTTP2_ADAPTER_OGHTTP2_ADAPTER_H_ + +#include <memory> + +#include "http2/adapter/http2_adapter.h" +#include "http2/adapter/http2_session.h" + +namespace http2 { +namespace adapter { + +class OgHttp2Adapter : public Http2Adapter { + public: + struct Options { + Perspective context; + }; + static std::unique_ptr<OgHttp2Adapter> Create(Http2VisitorInterface& visitor, + Options options); + + ~OgHttp2Adapter(); + + // From Http2Adapter. + ssize_t ProcessBytes(absl::string_view bytes) override; + void SubmitSettings(absl::Span<const Http2Setting> settings) override; + void SubmitPriorityForStream(Http2StreamId stream_id, + Http2StreamId parent_stream_id, + int weight, + bool exclusive) override; + void SubmitPing(Http2PingId ping_id) override; + void SubmitGoAway(Http2StreamId last_accepted_stream_id, + Http2ErrorCode error_code, + absl::string_view opaque_data) override; + void SubmitWindowUpdate(Http2StreamId stream_id, + int window_increment) override; + void SubmitMetadata(Http2StreamId stream_id, bool fin) override; + std::string GetBytesToWrite(absl::optional<size_t> max_bytes) override; + int GetPeerConnectionWindow() const override; + void MarkDataConsumedForStream(Http2StreamId stream_id, + size_t num_bytes) override; + + const Http2Session& session() const; + + private: + OgHttp2Adapter(Http2VisitorInterface& visitor, Options options); + + class OgHttp2Session; + std::unique_ptr<OgHttp2Session> session_; +}; + +} // namespace adapter +} // namespace http2 + +#endif // QUICHE_HTTP2_ADAPTER_OGHTTP2_ADAPTER_H_ 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 new file mode 100644 index 00000000000..3c04c06d7ec --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/oghttp2_adapter_test.cc @@ -0,0 +1,88 @@ +#include "http2/adapter/oghttp2_adapter.h" + +#include "http2/adapter/mock_http2_visitor.h" +#include "http2/adapter/test_utils.h" +#include "common/platform/api/quiche_test.h" +#include "spdy/platform/api/spdy_test_helpers.h" + +namespace http2 { +namespace adapter { +namespace test { +namespace { + +class OgHttp2AdapterTest : public testing::Test { + protected: + void SetUp() override { + OgHttp2Adapter::Options options; + adapter_ = OgHttp2Adapter::Create(http2_visitor_, options); + } + + testing::StrictMock<MockHttp2Visitor> http2_visitor_; + std::unique_ptr<OgHttp2Adapter> adapter_; +}; + +TEST_F(OgHttp2AdapterTest, ProcessBytes) { + EXPECT_SPDY_BUG(adapter_->ProcessBytes("fake data"), "Not implemented"); +} + +TEST_F(OgHttp2AdapterTest, SubmitMetadata) { + EXPECT_SPDY_BUG(adapter_->SubmitMetadata(3, true), "Not implemented"); +} + +TEST_F(OgHttp2AdapterTest, GetPeerConnectionWindow) { + int peer_window = 0; + EXPECT_SPDY_BUG(peer_window = adapter_->GetPeerConnectionWindow(), + "Not implemented"); + EXPECT_GT(peer_window, 0); +} + +TEST_F(OgHttp2AdapterTest, MarkDataConsumedForStream) { + EXPECT_SPDY_BUG(adapter_->MarkDataConsumedForStream(1, 11), + "Stream 1 not found"); +} + +TEST_F(OgHttp2AdapterTest, TestSerialize) { + EXPECT_FALSE(adapter_->session().want_read()); + EXPECT_FALSE(adapter_->session().want_write()); + + adapter_->SubmitSettings( + {{HEADER_TABLE_SIZE, 128}, {MAX_FRAME_SIZE, 128 << 10}}); + EXPECT_TRUE(adapter_->session().want_write()); + + adapter_->SubmitPriorityForStream(3, 1, 255, true); + adapter_->SubmitPing(42); + adapter_->SubmitGoAway(13, Http2ErrorCode::NO_ERROR, ""); + adapter_->SubmitWindowUpdate(3, 127); + EXPECT_TRUE(adapter_->session().want_write()); + + EXPECT_THAT(adapter_->GetBytesToWrite(absl::nullopt), + ContainsFrames( + {spdy::SpdyFrameType::SETTINGS, spdy::SpdyFrameType::PRIORITY, + spdy::SpdyFrameType::PING, spdy::SpdyFrameType::GOAWAY, + spdy::SpdyFrameType::WINDOW_UPDATE})); + EXPECT_FALSE(adapter_->session().want_write()); +} + +TEST_F(OgHttp2AdapterTest, TestPartialSerialize) { + EXPECT_FALSE(adapter_->session().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()); + + const std::string first_part = adapter_->GetBytesToWrite(10); + EXPECT_TRUE(adapter_->session().want_write()); + const std::string second_part = adapter_->GetBytesToWrite(absl::nullopt); + EXPECT_FALSE(adapter_->session().want_write()); + EXPECT_THAT( + absl::StrCat(first_part, second_part), + ContainsFrames({spdy::SpdyFrameType::SETTINGS, + spdy::SpdyFrameType::GOAWAY, spdy::SpdyFrameType::PING})); +} + +} // namespace +} // namespace test +} // namespace adapter +} // namespace http2 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 new file mode 100644 index 00000000000..46d33b61798 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.cc @@ -0,0 +1,129 @@ +#include "http2/adapter/test_frame_sequence.h" + +#include "http2/adapter/http2_util.h" +#include "spdy/core/spdy_framer.h" + +namespace http2 { +namespace adapter { +namespace test { + +TestFrameSequence& TestFrameSequence::ClientPreface() { + preface_ = spdy::kHttp2ConnectionHeaderPrefix; + frames_.push_back(absl::make_unique<spdy::SpdySettingsIR>()); + return *this; +} + +TestFrameSequence& TestFrameSequence::ServerPreface() { + frames_.push_back(absl::make_unique<spdy::SpdySettingsIR>()); + return *this; +} + +TestFrameSequence& TestFrameSequence::Data(Http2StreamId stream_id, + absl::string_view payload, + bool fin, + absl::optional<int> padding_length) { + auto data = absl::make_unique<spdy::SpdyDataIR>(stream_id, payload); + data->set_fin(fin); + if (padding_length) { + data->set_padding_len(padding_length.value()); + } + frames_.push_back(std::move(data)); + return *this; +} + +TestFrameSequence& TestFrameSequence::RstStream(Http2StreamId stream_id, + Http2ErrorCode error) { + frames_.push_back(absl::make_unique<spdy::SpdyRstStreamIR>( + stream_id, TranslateErrorCode(error))); + return *this; +} + +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); + } + frames_.push_back(std::move(settings)); + return *this; +} + +TestFrameSequence& TestFrameSequence::SettingsAck() { + auto settings = absl::make_unique<spdy::SpdySettingsIR>(); + settings->set_is_ack(true); + frames_.push_back(std::move(settings)); + return *this; +} + +TestFrameSequence& TestFrameSequence::Ping(Http2PingId id) { + frames_.push_back(absl::make_unique<spdy::SpdyPingIR>(id)); + return *this; +} + +TestFrameSequence& TestFrameSequence::PingAck(Http2PingId id) { + auto ping = absl::make_unique<spdy::SpdyPingIR>(id); + ping->set_is_ack(true); + frames_.push_back(std::move(ping)); + return *this; +} + +TestFrameSequence& TestFrameSequence::GoAway(Http2StreamId last_good_stream_id, + Http2ErrorCode error, + absl::string_view payload) { + frames_.push_back(absl::make_unique<spdy::SpdyGoAwayIR>( + last_good_stream_id, TranslateErrorCode(error), std::string(payload))); + return *this; +} + +TestFrameSequence& TestFrameSequence::Headers(Http2StreamId stream_id, + spdy::Http2HeaderBlock block, + bool fin) { + auto headers = + absl::make_unique<spdy::SpdyHeadersIR>(stream_id, std::move(block)); + headers->set_fin(fin); + frames_.push_back(std::move(headers)); + return *this; +} + +TestFrameSequence& TestFrameSequence::Headers(Http2StreamId stream_id, + absl::Span<const Header> headers, + bool fin) { + spdy::SpdyHeaderBlock block; + for (const Header& header : headers) { + block[header.first] = header.second; + } + return Headers(stream_id, std::move(block), fin); +} + +TestFrameSequence& TestFrameSequence::WindowUpdate(Http2StreamId stream_id, + int32_t delta) { + frames_.push_back( + absl::make_unique<spdy::SpdyWindowUpdateIR>(stream_id, delta)); + return *this; +} + +TestFrameSequence& TestFrameSequence::Priority(Http2StreamId stream_id, + Http2StreamId parent_stream_id, + int weight, + bool exclusive) { + frames_.push_back(absl::make_unique<spdy::SpdyPriorityIR>( + stream_id, parent_stream_id, weight, exclusive)); + return *this; +} + +std::string TestFrameSequence::Serialize() { + std::string result; + if (!preface_.empty()) { + result = preface_; + } + spdy::SpdyFramer framer(spdy::SpdyFramer::ENABLE_COMPRESSION); + for (const auto& frame : frames_) { + spdy::SpdySerializedFrame f = framer.SerializeFrame(*frame); + absl::StrAppend(&result, absl::string_view(f)); + } + return result; +} + +} // namespace test +} // namespace adapter +} // namespace http2 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 new file mode 100644 index 00000000000..dd110c1a729 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/test_frame_sequence.h @@ -0,0 +1,56 @@ +#ifndef QUICHE_HTTP2_ADAPTER_TEST_FRAME_SEQUENCE_H_ +#define QUICHE_HTTP2_ADAPTER_TEST_FRAME_SEQUENCE_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "http2/adapter/http2_protocol.h" +#include "spdy/core/spdy_protocol.h" + +namespace http2 { +namespace adapter { +namespace test { + +class TestFrameSequence { + public: + TestFrameSequence() = default; + + TestFrameSequence& ClientPreface(); + TestFrameSequence& ServerPreface(); + 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& SettingsAck(); + TestFrameSequence& Ping(Http2PingId id); + TestFrameSequence& PingAck(Http2PingId id); + TestFrameSequence& GoAway(Http2StreamId last_good_stream_id, + Http2ErrorCode error, + absl::string_view payload = ""); + TestFrameSequence& Headers(Http2StreamId stream_id, + spdy::Http2HeaderBlock block, + bool fin = false); + TestFrameSequence& Headers(Http2StreamId stream_id, + absl::Span<const Header> headers, + bool fin = false); + TestFrameSequence& WindowUpdate(Http2StreamId stream_id, int32_t delta); + TestFrameSequence& Priority(Http2StreamId stream_id, + Http2StreamId parent_stream_id, + int weight, + bool exclusive); + + std::string Serialize(); + + private: + std::string preface_; + std::vector<std::unique_ptr<spdy::SpdyFrameIR>> frames_; +}; + +} // namespace test +} // namespace adapter +} // namespace http2 + +#endif // QUICHE_HTTP2_ADAPTER_TEST_FRAME_SEQUENCE_H_ 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 new file mode 100644 index 00000000000..9e1db9954f2 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/test_utils.cc @@ -0,0 +1,127 @@ +#include "http2/adapter/test_utils.h" + +#include "spdy/core/spdy_frame_reader.h" + +namespace http2 { +namespace adapter { +namespace test { +namespace { + +using TypeAndOptionalLength = + std::pair<spdy::SpdyFrameType, absl::optional<size_t>>; + +std::vector<std::pair<const char*, std::string>> LogFriendly( + const std::vector<TypeAndOptionalLength>& types_and_lengths) { + std::vector<std::pair<const char*, std::string>> out; + out.reserve(types_and_lengths.size()); + for (const auto type_and_length : types_and_lengths) { + out.push_back({spdy::FrameTypeToString(type_and_length.first), + type_and_length.second + ? absl::StrCat(type_and_length.second.value()) + : "<unspecified>"}); + } + return out; +} + +// Custom gMock matcher, used to determine if a particular type of frame +// is in a string. This is useful in tests where we want to show that a +// particular control frame type is serialized for sending to the peer. +class SpdyControlFrameMatcher + : public testing::MatcherInterface<const std::string> { + public: + explicit SpdyControlFrameMatcher( + std::vector<TypeAndOptionalLength> types_and_lengths) + : expected_types_and_lengths_(std::move(types_and_lengths)) {} + + bool MatchAndExplain(const std::string s, + testing::MatchResultListener* listener) const override { + spdy::SpdyFrameReader reader(s.data(), s.size()); + + for (TypeAndOptionalLength expected : expected_types_and_lengths_) { + if (!MatchAndExplainOneFrame(expected.first, expected.second, &reader, + listener)) { + return false; + } + } + return true; + } + + bool MatchAndExplainOneFrame(spdy::SpdyFrameType expected_type, + absl::optional<size_t> expected_length, + spdy::SpdyFrameReader* reader, + testing::MatchResultListener* listener) const { + uint32_t payload_length; + if (!reader->ReadUInt24(&payload_length)) { + *listener << "; unable to read length field for expected_type " + << FrameTypeToString(expected_type) << ". data too short!"; + return false; + } + + if (expected_length && payload_length != expected_length.value()) { + *listener << "; actual length: " << payload_length + << " but expected length: " << expected_length.value(); + return false; + } + + uint8_t raw_type; + if (!reader->ReadUInt8(&raw_type)) { + *listener << "; unable to read type field for expected_type " + << FrameTypeToString(expected_type) << ". data too short!"; + return false; + } + + if (!spdy::IsDefinedFrameType(raw_type)) { + *listener << "; expected type " << FrameTypeToString(expected_type) + << " but raw type " << static_cast<int>(raw_type) + << " is not a defined frame type!"; + return false; + } + + spdy::SpdyFrameType actual_type = spdy::ParseFrameType(raw_type); + if (actual_type != expected_type) { + *listener << "; actual type: " << FrameTypeToString(actual_type) + << " but expected type: " << FrameTypeToString(expected_type); + return false; + } + + // Seek past flags (1B), stream ID (4B), and payload. Reach the next frame. + reader->Seek(5 + payload_length); + return true; + } + + void DescribeTo(std::ostream* os) const override { + *os << "Data contains frames of types in sequence " + << LogFriendly(expected_types_and_lengths_); + } + + void DescribeNegationTo(std::ostream* os) const override { + *os << "Data does not contain frames of types in sequence " + << LogFriendly(expected_types_and_lengths_); + } + + private: + const std::vector<TypeAndOptionalLength> expected_types_and_lengths_; +}; + +} // namespace + +testing::Matcher<const std::string> ContainsFrames( + std::vector<std::pair<spdy::SpdyFrameType, absl::optional<size_t>>> + types_and_lengths) { + return MakeMatcher(new SpdyControlFrameMatcher(std::move(types_and_lengths))); +} + +testing::Matcher<const std::string> ContainsFrames( + std::vector<spdy::SpdyFrameType> types) { + std::vector<std::pair<spdy::SpdyFrameType, absl::optional<size_t>>> + types_and_lengths; + types_and_lengths.reserve(types.size()); + for (spdy::SpdyFrameType type : types) { + types_and_lengths.push_back({type, absl::nullopt}); + } + return MakeMatcher(new SpdyControlFrameMatcher(std::move(types_and_lengths))); +} + +} // namespace test +} // namespace adapter +} // namespace http2 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 new file mode 100644 index 00000000000..6276bf1fe45 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/test_utils.h @@ -0,0 +1,29 @@ +#ifndef QUICHE_HTTP2_ADAPTER_TEST_UTILS_H_ +#define QUICHE_HTTP2_ADAPTER_TEST_UTILS_H_ + +#include <string> +#include <vector> + +#include "common/platform/api/quiche_test.h" +#include "spdy/core/spdy_protocol.h" + +namespace http2 { +namespace adapter { +namespace test { + +// Matcher that checks whether a string contains HTTP/2 frames of the specified +// ordered sequence of types and lengths. +testing::Matcher<const std::string> ContainsFrames( + std::vector<std::pair<spdy::SpdyFrameType, absl::optional<size_t>>> + types_and_lengths); + +// Matcher that checks whether a string contains HTTP/2 frames of the specified +// ordered sequence of types. +testing::Matcher<const std::string> ContainsFrames( + std::vector<spdy::SpdyFrameType> types); + +} // namespace test +} // namespace adapter +} // namespace http2 + +#endif // QUICHE_HTTP2_ADAPTER_TEST_UTILS_H_ diff --git a/chromium/net/third_party/quiche/src/http2/adapter/test_utils_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/test_utils_test.cc new file mode 100644 index 00000000000..e8f4ebda350 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/test_utils_test.cc @@ -0,0 +1,96 @@ +#include "http2/adapter/test_utils.h" + +#include "common/platform/api/quiche_test.h" +#include "spdy/core/spdy_framer.h" + +namespace http2 { +namespace adapter { +namespace test { +namespace { + +using spdy::SpdyFramer; + +TEST(ContainsFrames, Empty) { + EXPECT_THAT("", ContainsFrames(std::vector<spdy::SpdyFrameType>{})); +} + +TEST(ContainsFrames, SingleFrameWithLength) { + SpdyFramer framer{SpdyFramer::ENABLE_COMPRESSION}; + + spdy::SpdyPingIR ping{511}; + EXPECT_THAT(framer.SerializeFrame(ping), + ContainsFrames({{spdy::SpdyFrameType::PING, 8}})); + + spdy::SpdyWindowUpdateIR window_update{1, 101}; + EXPECT_THAT(framer.SerializeFrame(window_update), + ContainsFrames({{spdy::SpdyFrameType::WINDOW_UPDATE, 4}})); + + spdy::SpdyDataIR data{3, "Some example data, ha ha!"}; + EXPECT_THAT(framer.SerializeFrame(data), + ContainsFrames({{spdy::SpdyFrameType::DATA, 25}})); +} + +TEST(ContainsFrames, SingleFrameWithoutLength) { + SpdyFramer framer{SpdyFramer::ENABLE_COMPRESSION}; + + spdy::SpdyRstStreamIR rst_stream{7, spdy::ERROR_CODE_REFUSED_STREAM}; + EXPECT_THAT( + framer.SerializeFrame(rst_stream), + ContainsFrames({{spdy::SpdyFrameType::RST_STREAM, absl::nullopt}})); + + spdy::SpdyGoAwayIR goaway{13, spdy::ERROR_CODE_ENHANCE_YOUR_CALM, + "Consider taking some deep breaths."}; + EXPECT_THAT(framer.SerializeFrame(goaway), + ContainsFrames({{spdy::SpdyFrameType::GOAWAY, absl::nullopt}})); + + spdy::Http2HeaderBlock block; + block[":method"] = "GET"; + block[":path"] = "/example"; + block[":authority"] = "example.com"; + spdy::SpdyHeadersIR headers{17, std::move(block)}; + EXPECT_THAT(framer.SerializeFrame(headers), + ContainsFrames({{spdy::SpdyFrameType::HEADERS, absl::nullopt}})); +} + +TEST(ContainsFrames, MultipleFrames) { + SpdyFramer framer{SpdyFramer::ENABLE_COMPRESSION}; + + spdy::SpdyPingIR ping{511}; + spdy::SpdyWindowUpdateIR window_update{1, 101}; + spdy::SpdyDataIR data{3, "Some example data, ha ha!"}; + spdy::SpdyRstStreamIR rst_stream{7, spdy::ERROR_CODE_REFUSED_STREAM}; + spdy::SpdyGoAwayIR goaway{13, spdy::ERROR_CODE_ENHANCE_YOUR_CALM, + "Consider taking some deep breaths."}; + spdy::Http2HeaderBlock block; + block[":method"] = "GET"; + block[":path"] = "/example"; + block[":authority"] = "example.com"; + spdy::SpdyHeadersIR headers{17, std::move(block)}; + + const std::string frame_sequence = + absl::StrCat(absl::string_view(framer.SerializeFrame(ping)), + absl::string_view(framer.SerializeFrame(window_update)), + absl::string_view(framer.SerializeFrame(data)), + absl::string_view(framer.SerializeFrame(rst_stream)), + absl::string_view(framer.SerializeFrame(goaway)), + absl::string_view(framer.SerializeFrame(headers))); + EXPECT_THAT( + frame_sequence, + ContainsFrames({{spdy::SpdyFrameType::PING, absl::nullopt}, + {spdy::SpdyFrameType::WINDOW_UPDATE, absl::nullopt}, + {spdy::SpdyFrameType::DATA, 25}, + {spdy::SpdyFrameType::RST_STREAM, absl::nullopt}, + {spdy::SpdyFrameType::GOAWAY, 42}, + {spdy::SpdyFrameType::HEADERS, 19}})); + EXPECT_THAT( + frame_sequence, + ContainsFrames( + {spdy::SpdyFrameType::PING, spdy::SpdyFrameType::WINDOW_UPDATE, + spdy::SpdyFrameType::DATA, spdy::SpdyFrameType::RST_STREAM, + spdy::SpdyFrameType::GOAWAY, spdy::SpdyFrameType::HEADERS})); +} + +} // namespace +} // namespace test +} // namespace adapter +} // namespace http2 diff --git a/chromium/net/third_party/quiche/src/http2/adapter/window_manager.cc b/chromium/net/third_party/quiche/src/http2/adapter/window_manager.cc new file mode 100644 index 00000000000..a1b92d5bf2c --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/window_manager.cc @@ -0,0 +1,99 @@ +#include "http2/adapter/window_manager.h" + +#include <utility> + +#include "common/platform/api/quiche_logging.h" +#include "spdy/platform/api/spdy_bug_tracker.h" + +namespace http2 { +namespace adapter { + +WindowManager::WindowManager(size_t window_size_limit, + WindowUpdateListener listener) + : limit_(window_size_limit), window_(window_size_limit), buffered_(0), + listener_(std::move(listener)) {} + +void WindowManager::OnWindowSizeLimitChange(const size_t new_limit) { + QUICHE_VLOG(2) << "WindowManager@" << this + << " OnWindowSizeLimitChange from old limit of " << limit_ + << " to new limit of " << new_limit; + if (new_limit > limit_) { + window_ += (new_limit - limit_); + } else { + SPDY_BUG(H2 window decrease) + << "Window size limit decrease not currently supported."; + } + limit_ = new_limit; +} + +void WindowManager::SetWindowSizeLimit(size_t new_limit) { + QUICHE_VLOG(2) << "WindowManager@" << this + << " SetWindowSizeLimit from old limit of " << limit_ + << " to new limit of " << new_limit; + limit_ = new_limit; + MaybeNotifyListener(); +} + +bool WindowManager::MarkDataBuffered(size_t bytes) { + QUICHE_VLOG(2) << "WindowManager@" << this << " window: " << window_ + << " bytes: " << bytes; + if (window_ < bytes) { + QUICHE_VLOG(2) << "WindowManager@" << this << " window underflow " + << "window: " << window_ << " bytes: " << bytes; + window_ = 0; + } else { + window_ -= bytes; + } + buffered_ += bytes; + if (window_ == 0) { + // If data hasn't been flushed in a while there may be space available. + MaybeNotifyListener(); + } + return window_ > 0; +} + +void WindowManager::MarkDataFlushed(size_t bytes) { + QUICHE_VLOG(2) << "WindowManager@" << this << " buffered: " << buffered_ + << " bytes: " << bytes; + if (buffered_ < bytes) { + SPDY_BUG(bug_2816_1) << "WindowManager@" << this << " buffered underflow " + << "buffered_: " << buffered_ << " bytes: " << bytes; + buffered_ = 0; + } else { + buffered_ -= bytes; + } + MaybeNotifyListener(); +} + +void WindowManager::MaybeNotifyListener() { + if (buffered_ + window_ > limit_) { + QUICHE_LOG(ERROR) << "Flow control violation; limit: " << limit_ + << " buffered: " << buffered_ << " window: " << window_; + return; + } + // For the sake of efficiency, we want to send window updates if less than + // half of the max quota is available to the peer at any point in time. + // http://google3/gfe/gfe2/stubby/autobahn_fd_wrapper.cc?l=1180-1183&rcl=307416556 + const size_t kDesiredMinWindow = limit_ / 2; + const size_t kDesiredMinDelta = limit_ / 3; + const size_t delta = limit_ - (buffered_ + window_); + bool send_update = false; + if (delta >= kDesiredMinDelta) { + // This particular window update was sent because the available delta + // exceeded the desired minimum. + send_update = true; + } else if (window_ < kDesiredMinWindow) { + // This particular window update was sent because the quota available to the + // peer at this moment is less than the desired minimum. + send_update = true; + } + if (send_update && delta > 0) { + QUICHE_VLOG(2) << "WindowManager@" << this + << " Informing listener of delta: " << delta; + listener_(delta); + window_ += delta; + } +} + +} // namespace adapter +} // namespace http2 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 new file mode 100644 index 00000000000..277c24f960e --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/window_manager.h @@ -0,0 +1,74 @@ +#ifndef QUICHE_HTTP2_ADAPTER_WINDOW_MANAGER_H_ +#define QUICHE_HTTP2_ADAPTER_WINDOW_MANAGER_H_ + +#include <functional> + +namespace http2 { +namespace adapter { + +namespace test { +class WindowManagerPeer; +} + +// This class keeps track of a HTTP/2 flow control window, notifying a listener +// when a window update needs to be sent. This class is not thread-safe. +class WindowManager { + public: + // A WindowUpdateListener is invoked when it is time to send a window update. + typedef std::function<void(size_t)> WindowUpdateListener; + + WindowManager(size_t window_size_limit, + WindowUpdateListener listener); + + size_t CurrentWindowSize() const { return window_; } + size_t WindowSizeLimit() const { return limit_; } + + // Called when the window size limit is changed (typically via settings) but + // no window update should be sent. + void OnWindowSizeLimitChange(size_t new_limit); + + // Sets the window size limit to |new_limit| and notifies the listener to + // update as necessary. + void SetWindowSizeLimit(size_t new_limit); + + // Increments the running total of data bytes buffered. Returns true iff there + // is more window remaining. + bool MarkDataBuffered(size_t bytes); + + // Increments the running total of data bytes that have been flushed or + // dropped. Invokes the listener if the current window is smaller than some + // threshold and there is quota available to send. + void MarkDataFlushed(size_t bytes); + + // Convenience method, used when incoming data is immediately dropped or + // ignored. + void MarkWindowConsumed(size_t bytes) { + MarkDataBuffered(bytes); + MarkDataFlushed(bytes); + } + + private: + friend class test::WindowManagerPeer; + + void MaybeNotifyListener(); + + // The upper bound on the flow control window. The GFE attempts to maintain a + // window of this size at the peer as data is proxied through. + size_t limit_; + + // The current flow control window that has not been advertised to the peer + // and not yet consumed. The peer can send this many bytes before becoming + // blocked. + size_t window_; + + // The amount of data already buffered, which should count against the flow + // control window upper bound. + size_t buffered_; + + WindowUpdateListener listener_; +}; + +} // namespace adapter +} // namespace http2 + +#endif // QUICHE_HTTP2_ADAPTER_WINDOW_MANAGER_H_ diff --git a/chromium/net/third_party/quiche/src/http2/adapter/window_manager_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/window_manager_test.cc new file mode 100644 index 00000000000..549fd1c3e1a --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/adapter/window_manager_test.cc @@ -0,0 +1,171 @@ +#include "http2/adapter/window_manager.h" + +#include <list> + +#include "absl/functional/bind_front.h" +#include "http2/test_tools/http2_random.h" +#include "common/platform/api/quiche_test.h" +#include "spdy/platform/api/spdy_test_helpers.h" + +namespace http2 { +namespace adapter { +namespace test { + +// Use the peer to access private vars of WindowManager. +class WindowManagerPeer { + public: + explicit WindowManagerPeer(const WindowManager& wm) : wm_(wm) {} + + size_t buffered() { + return wm_.buffered_; + } + + private: + const WindowManager& wm_; +}; + +namespace { + +class WindowManagerTest : public ::testing::Test { + protected: + WindowManagerTest() + : wm_(kDefaultLimit, absl::bind_front(&WindowManagerTest::OnCall, this)), + peer_(wm_) {} + + void OnCall(size_t s) { + call_sequence_.push_back(s); + } + + const size_t kDefaultLimit = 32 * 1024 * 3; + std::list<size_t> call_sequence_; + WindowManager wm_; + WindowManagerPeer peer_; + ::http2::test::Http2Random random_; +}; + +// A few no-op calls. +TEST_F(WindowManagerTest, NoOps) { + wm_.SetWindowSizeLimit(kDefaultLimit); + wm_.SetWindowSizeLimit(0); + wm_.SetWindowSizeLimit(kDefaultLimit); + wm_.MarkDataBuffered(0); + wm_.MarkDataFlushed(0); + EXPECT_TRUE(call_sequence_.empty()); +} + +// This test verifies that WindowManager does not notify its listener when data +// is only buffered, and never flushed. +TEST_F(WindowManagerTest, DataOnlyBuffered) { + size_t total = 0; + while (total < kDefaultLimit) { + size_t s = std::min<size_t>(kDefaultLimit - total, random_.Uniform(1024)); + total += s; + wm_.MarkDataBuffered(s); + } + EXPECT_THAT(call_sequence_, ::testing::IsEmpty()); +} + +// This test verifies that WindowManager does notify its listener when data is +// buffered and subsequently flushed. +TEST_F(WindowManagerTest, DataBufferedAndFlushed) { + size_t total_buffered = 0; + size_t total_flushed = 0; + while (call_sequence_.empty()) { + size_t buffered = + std::min<size_t>(kDefaultLimit - total_buffered, random_.Uniform(1024)); + wm_.MarkDataBuffered(buffered); + total_buffered += buffered; + EXPECT_TRUE(call_sequence_.empty()); + size_t flushed = random_.Uniform(total_buffered - total_flushed); + wm_.MarkDataFlushed(flushed); + total_flushed += flushed; + } + // If WindowManager decided to send an update, at least one third of the + // window must have been consumed by buffered data. + EXPECT_GE(total_buffered, kDefaultLimit / 3); +} + +// Window manager should avoid window underflow. +TEST_F(WindowManagerTest, AvoidWindowUnderflow) { + EXPECT_EQ(wm_.CurrentWindowSize(), wm_.WindowSizeLimit()); + // Don't buffer more than the total window! + wm_.MarkDataBuffered(wm_.WindowSizeLimit() + 1); + EXPECT_EQ(wm_.CurrentWindowSize(), 0); +} + +// Window manager should GFE_BUG and avoid buffered underflow. +TEST_F(WindowManagerTest, AvoidBufferedUnderflow) { + EXPECT_EQ(peer_.buffered(), 0); + // Don't flush more than has been buffered! + EXPECT_SPDY_BUG(wm_.MarkDataFlushed(1), "buffered underflow"); + EXPECT_EQ(peer_.buffered(), 0); + + wm_.MarkDataBuffered(42); + EXPECT_EQ(peer_.buffered(), 42); + // Don't flush more than has been buffered! + EXPECT_SPDY_BUG(wm_.MarkDataFlushed(43), "buffered underflow"); + EXPECT_EQ(peer_.buffered(), 0); +} + +// This test verifies that WindowManager notifies its listener when window is +// consumed (data is ignored or immediately dropped). +TEST_F(WindowManagerTest, WindowConsumed) { + size_t consumed = kDefaultLimit / 3 - 1; + wm_.MarkWindowConsumed(consumed); + EXPECT_TRUE(call_sequence_.empty()); + const size_t extra = 1; + wm_.MarkWindowConsumed(extra); + EXPECT_THAT(call_sequence_, testing::ElementsAre(consumed + extra)); +} + +// This test verifies that WindowManager notifies its listener when the window +// size limit is increased. +TEST_F(WindowManagerTest, ListenerCalledOnSizeUpdate) { + wm_.SetWindowSizeLimit(kDefaultLimit - 1024); + EXPECT_TRUE(call_sequence_.empty()); + wm_.SetWindowSizeLimit(kDefaultLimit * 5); + // Because max(outstanding window, previous limit) is kDefaultLimit, it is + // only appropriate to increase the window by kDefaultLimit * 4. + EXPECT_THAT(call_sequence_, testing::ElementsAre(kDefaultLimit * 4)); +} + +// This test verifies that when data is buffered and then the limit is +// decreased, WindowManager only notifies the listener once any outstanding +// window has been consumed. +TEST_F(WindowManagerTest, WindowUpdateAfterLimitDecreased) { + wm_.MarkDataBuffered(kDefaultLimit - 1024); + wm_.SetWindowSizeLimit(kDefaultLimit - 2048); + + // Now there are 2048 bytes of window outstanding beyond the current limit, + // and we have 1024 bytes of data buffered beyond the current limit. This is + // intentional, to be sure that WindowManager works properly if the limit is + // decreased at runtime. + + wm_.MarkDataFlushed(512); + EXPECT_TRUE(call_sequence_.empty()); + wm_.MarkDataFlushed(512); + EXPECT_TRUE(call_sequence_.empty()); + wm_.MarkDataFlushed(512); + EXPECT_TRUE(call_sequence_.empty()); + wm_.MarkDataFlushed(1024); + EXPECT_THAT(call_sequence_, testing::ElementsAre(512)); +} + +// For normal behavior, we only call MaybeNotifyListener() when data is +// flushed. But if window runs out entirely, we still need to call +// MaybeNotifyListener() to avoid becoming artificially blocked when data isn't +// being flushed. +TEST_F(WindowManagerTest, ZeroWindowNotification) { + // Consume a byte of window, but not enough to trigger an update. + wm_.MarkWindowConsumed(1); + + // Buffer the remaining window. + wm_.MarkDataBuffered(kDefaultLimit - 1); + // Listener is notified of the remaining byte of possible window. + EXPECT_THAT(call_sequence_, testing::ElementsAre(1)); +} + +} // namespace +} // namespace test +} // namespace adapter +} // namespace http2 diff --git a/chromium/net/third_party/quiche/src/http2/decoder/decode_status.cc b/chromium/net/third_party/quiche/src/http2/decoder/decode_status.cc index 60d682551a2..a5d2c6fedb9 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/decode_status.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/decode_status.cc @@ -21,7 +21,7 @@ std::ostream& operator<<(std::ostream& out, DecodeStatus v) { // Since the value doesn't come over the wire, only a programming bug should // result in reaching this point. int unknown = static_cast<int>(v); - HTTP2_BUG << "Unknown DecodeStatus " << unknown; + HTTP2_BUG(http2_bug_147_1) << "Unknown DecodeStatus " << unknown; return out << "DecodeStatus(" << unknown << ")"; } diff --git a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.cc index 449d6493802..55dea39cdb2 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.cc @@ -28,7 +28,7 @@ std::ostream& operator<<(std::ostream& out, Http2FrameDecoder::State v) { // Since the value doesn't come over the wire, only a programming bug should // result in reaching this point. int unknown = static_cast<int>(v); - HTTP2_BUG << "Http2FrameDecoder::State " << unknown; + HTTP2_BUG(http2_bug_155_1) << "Http2FrameDecoder::State " << unknown; return out << "Http2FrameDecoder::State(" << unknown << ")"; } @@ -158,12 +158,7 @@ DecodeStatus Http2FrameDecoder::StartDecodingPayload(DecodeBuffer* db) { break; case Http2FrameType::PRIORITY_UPDATE: - if (GetHttp2RestartFlag(http2_parse_priority_update_frame)) { - HTTP2_RESTART_FLAG_COUNT_N(http2_parse_priority_update_frame, 1, 2); - status = StartDecodingPriorityUpdatePayload(&subset); - } else { - status = StartDecodingUnknownPayload(&subset); - } + status = StartDecodingPriorityUpdatePayload(&subset); break; default: @@ -237,12 +232,7 @@ DecodeStatus Http2FrameDecoder::ResumeDecodingPayload(DecodeBuffer* db) { break; case Http2FrameType::PRIORITY_UPDATE: - if (GetHttp2RestartFlag(http2_parse_priority_update_frame)) { - HTTP2_RESTART_FLAG_COUNT_N(http2_parse_priority_update_frame, 2, 2); - status = ResumeDecodingPriorityUpdatePayload(&subset); - } else { - status = ResumeDecodingUnknownPayload(&subset); - } + status = ResumeDecodingPriorityUpdatePayload(&subset); break; default: diff --git a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.h b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.h index 53e4bbe7c2b..295a0b04c7f 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.h +++ b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.h @@ -77,6 +77,12 @@ class QUICHE_EXPORT_PRIVATE Http2FrameDecoder { // Returns kDecodeError if the frame's padding or length wasn't valid (i.e. if // the decoder called either the listener's OnPaddingTooLong or // OnFrameSizeError method). + // + // If the decode buffer contains the entirety of a frame payload or field, + // then the corresponding Http2FrameDecoderListener::On*Payload(), + // OnHpackFragment(), OnGoAwayOpaqueData(), or OnAltSvcValueData() method is + // guaranteed to be called exactly once, with the entire payload or field in a + // single chunk. DecodeStatus DecodeFrame(DecodeBuffer* db); ////////////////////////////////////////////////////////////////////////////// diff --git a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_test.cc index 0b0b4153d90..b09a5d086c5 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_test.cc @@ -558,16 +558,9 @@ TEST_F(Http2FrameDecoderTest, PriorityUpdatePayload) { }; Http2FrameHeader header(7, Http2FrameType::PRIORITY_UPDATE, 0, 0); - if (GetHttp2RestartFlag(http2_parse_priority_update_frame)) { - FrameParts expected(header, "abc"); - expected.SetOptPriorityUpdate(Http2PriorityUpdateFields{5}); - EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); - } else { - FrameParts expected(header, absl::string_view("\x00\x00\x00\x05" - "abc", - 7)); - EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); - } + FrameParts expected(header, "abc"); + expected.SetOptPriorityUpdate(Http2PriorityUpdateFields{5}); + EXPECT_TRUE(DecodePayloadAndValidateSeveralWays(kFrameData, expected)); } TEST_F(Http2FrameDecoderTest, UnknownPayload) { diff --git a/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder.cc index b8f29bc3177..43ade26797f 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder.cc @@ -21,7 +21,8 @@ namespace http2 { uint32_t Http2StructureDecoder::IncompleteStart(DecodeBuffer* db, uint32_t target_size) { if (target_size > sizeof buffer_) { - HTTP2_BUG << "target_size too large for buffer: " << target_size; + HTTP2_BUG(http2_bug_154_1) + << "target_size too large for buffer: " << target_size; return 0; } const uint32_t num_to_copy = db->MinLengthRemaining(target_size); @@ -53,8 +54,9 @@ bool Http2StructureDecoder::ResumeFillingBuffer(DecodeBuffer* db, << ": target_size=" << target_size << "; offset_=" << offset_ << "; db->Remaining=" << db->Remaining(); if (target_size < offset_) { - HTTP2_BUG << "Already filled buffer_! target_size=" << target_size - << " offset_=" << offset_; + HTTP2_BUG(http2_bug_154_2) + << "Already filled buffer_! target_size=" << target_size + << " offset_=" << offset_; return false; } const uint32_t needed = target_size - offset_; @@ -74,8 +76,9 @@ bool Http2StructureDecoder::ResumeFillingBuffer(DecodeBuffer* db, << "; *remaining_payload=" << *remaining_payload << "; db->Remaining=" << db->Remaining(); if (target_size < offset_) { - HTTP2_BUG << "Already filled buffer_! target_size=" << target_size - << " offset_=" << offset_; + HTTP2_BUG(http2_bug_154_3) + << "Already filled buffer_! target_size=" << target_size + << " offset_=" << offset_; return false; } const uint32_t needed = target_size - offset_; diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder.cc index 2b341047e9f..39d3fc9efbd 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder.cc @@ -31,7 +31,8 @@ std::ostream& operator<<(std::ostream& out, // Since the value doesn't come over the wire, only a programming bug should // result in reaching this point. int unknown = static_cast<int>(v); - HTTP2_BUG << "Invalid AltSvcPayloadDecoder::PayloadState: " << unknown; + HTTP2_BUG(http2_bug_163_1) + << "Invalid AltSvcPayloadDecoder::PayloadState: " << unknown; return out << "AltSvcPayloadDecoder::PayloadState(" << unknown << ")"; } @@ -104,7 +105,7 @@ DecodeStatus AltSvcPayloadDecoder::ResumeDecodingPayload( payload_state_ = PayloadState::kMaybeDecodedStruct; continue; } - HTTP2_BUG << "PayloadState: " << payload_state_; + HTTP2_BUG(http2_bug_163_2) << "PayloadState: " << payload_state_; } } diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder.cc index 03e442dc0b8..5868d43fe35 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder.cc @@ -29,7 +29,8 @@ std::ostream& operator<<(std::ostream& out, // Since the value doesn't come over the wire, only a programming bug should // result in reaching this point. int unknown = static_cast<int>(v); - HTTP2_BUG << "Invalid DataPayloadDecoder::PayloadState: " << unknown; + HTTP2_BUG(http2_bug_174_1) + << "Invalid DataPayloadDecoder::PayloadState: " << unknown; return out << "DataPayloadDecoder::PayloadState(" << unknown << ")"; } @@ -120,7 +121,7 @@ DecodeStatus DataPayloadDecoder::ResumeDecodingPayload(FrameDecoderState* state, payload_state_ = PayloadState::kSkipPadding; return DecodeStatus::kDecodeInProgress; } - HTTP2_BUG << "PayloadState: " << payload_state_; + HTTP2_BUG(http2_bug_174_2) << "PayloadState: " << payload_state_; return DecodeStatus::kDecodeError; } diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder.cc index 17b7e7dd534..a2cfd09673b 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder.cc @@ -31,7 +31,8 @@ std::ostream& operator<<(std::ostream& out, // Since the value doesn't come over the wire, only a programming bug should // result in reaching this point. int unknown = static_cast<int>(v); - HTTP2_BUG << "Invalid GoAwayPayloadDecoder::PayloadState: " << unknown; + HTTP2_BUG(http2_bug_167_1) + << "Invalid GoAwayPayloadDecoder::PayloadState: " << unknown; return out << "GoAwayPayloadDecoder::PayloadState(" << unknown << ")"; } @@ -114,7 +115,7 @@ DecodeStatus GoAwayPayloadDecoder::ResumeDecodingPayload( payload_state_ = PayloadState::kHandleFixedFieldsStatus; continue; } - HTTP2_BUG << "PayloadState: " << payload_state_; + HTTP2_BUG(http2_bug_167_2) << "PayloadState: " << payload_state_; } } diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder.cc index 9b219d71427..a312fb944b6 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder.cc @@ -33,7 +33,8 @@ std::ostream& operator<<(std::ostream& out, // Since the value doesn't come over the wire, only a programming bug should // result in reaching this point. int unknown = static_cast<int>(v); - HTTP2_BUG << "Invalid HeadersPayloadDecoder::PayloadState: " << unknown; + HTTP2_BUG(http2_bug_189_1) + << "Invalid HeadersPayloadDecoder::PayloadState: " << unknown; return out << "HeadersPayloadDecoder::PayloadState(" << unknown << ")"; } @@ -170,7 +171,7 @@ DecodeStatus HeadersPayloadDecoder::ResumeDecodingPayload( payload_state_ = PayloadState::kReadPayload; continue; } - HTTP2_BUG << "PayloadState: " << payload_state_; + HTTP2_BUG(http2_bug_189_2) << "PayloadState: " << payload_state_; } } diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h index 464b0cb1d59..feea5e5ef0f 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h +++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h @@ -385,7 +385,7 @@ class AbstractPaddablePayloadDecoderTest auto& listener = listener_; Validator validator = [header, expected_missing_length, &listener]( - const DecodeBuffer& input, + const DecodeBuffer&, DecodeStatus status) -> ::testing::AssertionResult { VERIFY_EQ(DecodeStatus::kDecodeError, status); VERIFY_FALSE(listener.IsInProgress()); diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_update_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_update_payload_decoder.cc index d115c358b37..bf7821625cf 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_update_payload_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_update_payload_decoder.cc @@ -31,8 +31,8 @@ std::ostream& operator<<(std::ostream& out, // Since the value doesn't come over the wire, only a programming bug should // result in reaching this point. int unknown = static_cast<int>(v); - HTTP2_BUG << "Invalid PriorityUpdatePayloadDecoder::PayloadState: " - << unknown; + HTTP2_BUG(http2_bug_173_1) + << "Invalid PriorityUpdatePayloadDecoder::PayloadState: " << unknown; return out << "PriorityUpdatePayloadDecoder::PayloadState(" << unknown << ")"; } @@ -118,7 +118,7 @@ DecodeStatus PriorityUpdatePayloadDecoder::ResumeDecodingPayload( payload_state_ = PayloadState::kHandleFixedFieldsStatus; continue; } - HTTP2_BUG << "PayloadState: " << payload_state_; + HTTP2_BUG(http2_bug_173_2) << "PayloadState: " << payload_state_; } } diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_update_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_update_payload_decoder_test.cc index 39d0afc98be..41b7dc85bce 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_update_payload_decoder_test.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_update_payload_decoder_test.cc @@ -61,31 +61,14 @@ struct Listener : public FramePartsCollector { } }; -// Avoid initialization of test class when flag is false, because base class -// method AbstractPayloadDecoderTest::SetUp() crashes if -// IsSupportedHttp2FrameType(PRIORITY_UPDATE) returns false. -std::vector<bool> GetTestParams() { - if (GetHttp2RestartFlag(http2_parse_priority_update_frame)) { - return {true}; // Actual Boolean value is ignored. - } else { - return {}; - } -} - class PriorityUpdatePayloadDecoderTest : public AbstractPayloadDecoderTest<PriorityUpdatePayloadDecoder, PriorityUpdatePayloadDecoderPeer, - Listener>, - public ::testing::WithParamInterface<bool> {}; - -INSTANTIATE_TEST_SUITE_P(MaybeRunTest, - PriorityUpdatePayloadDecoderTest, - ::testing::ValuesIn(GetTestParams())); -GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PriorityUpdatePayloadDecoderTest); + Listener> {}; // Confirm we get an error if the payload is not long enough to hold // Http2PriorityUpdateFields. -TEST_P(PriorityUpdatePayloadDecoderTest, Truncated) { +TEST_F(PriorityUpdatePayloadDecoderTest, Truncated) { auto approve_size = [](size_t size) { return size != Http2PriorityUpdateFields::EncodedSize(); }; @@ -98,9 +81,9 @@ class PriorityUpdatePayloadLengthTests : public AbstractPayloadDecoderTest<PriorityUpdatePayloadDecoder, PriorityUpdatePayloadDecoderPeer, Listener>, - public ::testing::WithParamInterface<std::tuple<uint32_t, bool>> { + public ::testing::WithParamInterface<uint32_t> { protected: - PriorityUpdatePayloadLengthTests() : length_(std::get<0>(GetParam())) { + PriorityUpdatePayloadLengthTests() : length_(GetParam()) { HTTP2_VLOG(1) << "################ length_=" << length_ << " ################"; } @@ -108,12 +91,9 @@ class PriorityUpdatePayloadLengthTests const uint32_t length_; }; -INSTANTIATE_TEST_SUITE_P( - VariousLengths, - PriorityUpdatePayloadLengthTests, - ::testing::Combine(::testing::Values(0, 1, 2, 3, 4, 5, 6), - ::testing::ValuesIn(GetTestParams()))); -GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PriorityUpdatePayloadLengthTests); +INSTANTIATE_TEST_SUITE_P(VariousLengths, + PriorityUpdatePayloadLengthTests, + ::testing::Values(0, 1, 2, 3, 4, 5, 6)); TEST_P(PriorityUpdatePayloadLengthTests, ValidLength) { Http2PriorityUpdateFields priority_update; diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder.cc index efb15208341..95fb5f5e662 100644 --- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder.cc @@ -155,7 +155,7 @@ DecodeStatus PushPromisePayloadDecoder::ResumeDecodingPayload( payload_state_ = PayloadState::kResumeDecodingPushPromiseFields; return status; } - HTTP2_BUG << "PayloadState: " << payload_state_; + HTTP2_BUG(http2_bug_183_1) << "PayloadState: " << payload_state_; } } diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.cc index 9c63f03c3af..0a4c71cb754 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.cc @@ -10,7 +10,6 @@ #include "http2/platform/api/http2_flag_utils.h" #include "http2/platform/api/http2_flags.h" #include "http2/platform/api/http2_logging.h" -#include "http2/platform/api/http2_string_utils.h" namespace http2 { @@ -56,7 +55,7 @@ DecodeStatus HpackBlockDecoder::Decode(DecodeBuffer* db) { std::string HpackBlockDecoder::DebugString() const { return absl::StrCat( "HpackBlockDecoder(", entry_decoder_.DebugString(), ", listener@", - Http2Hex(reinterpret_cast<intptr_t>(listener_)), + absl::Hex(reinterpret_cast<intptr_t>(listener_)), (before_entry_ ? ", between entries)" : ", in an entry)")); } diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.cc index 45d7a78b3df..0f1336ed29e 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.cc @@ -21,11 +21,6 @@ HpackDecoder::HpackDecoder(HpackDecoderListener* listener, HpackDecoder::~HpackDecoder() = default; -void HpackDecoder::set_tables_debug_listener( - HpackDecoderTablesDebugListener* debug_listener) { - decoder_state_.set_tables_debug_listener(debug_listener); -} - void HpackDecoder::set_max_string_size_bytes(size_t max_string_size_bytes) { entry_buffer_.set_max_string_size_bytes(max_string_size_bytes); } 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 b5fdecd0d67..01d32a38ac1 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 @@ -45,11 +45,6 @@ class QUICHE_EXPORT_PRIVATE HpackDecoder { HpackDecoder(const HpackDecoder&) = delete; HpackDecoder& operator=(const HpackDecoder&) = delete; - // Set listener to be notified of insertions into the HPACK dynamic table, - // and uses of those entries. - void set_tables_debug_listener( - HpackDecoderTablesDebugListener* debug_listener); - // max_string_size specifies the maximum size of an on-the-wire string (name // or value, plain or Huffman encoded) that will be accepted. See sections // 5.1 and 5.2 of RFC 7541. This is a defense against OOM attacks; HTTP/2 diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_listener.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_listener.cc index a665be8c62d..d1dea19330e 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_listener.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_listener.cc @@ -13,8 +13,8 @@ HpackDecoderNoOpListener::HpackDecoderNoOpListener() = default; HpackDecoderNoOpListener::~HpackDecoderNoOpListener() = default; void HpackDecoderNoOpListener::OnHeaderListStart() {} -void HpackDecoderNoOpListener::OnHeader(const HpackString& /*name*/, - const HpackString& /*value*/) {} +void HpackDecoderNoOpListener::OnHeader(const std::string& /*name*/, + const std::string& /*value*/) {} void HpackDecoderNoOpListener::OnHeaderListEnd() {} void HpackDecoderNoOpListener::OnHeaderErrorDetected( absl::string_view /*error_message*/) {} diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_listener.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_listener.h index bbb05449715..d341025c82c 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_listener.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_listener.h @@ -9,7 +9,6 @@ #define QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_LISTENER_H_ #include "absl/strings/string_view.h" -#include "http2/hpack/hpack_string.h" #include "http2/hpack/http2_hpack_constants.h" #include "common/platform/api/quiche_export.h" @@ -28,7 +27,7 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderListener { // Called for each header name-value pair that is decoded, in the order they // appear in the HPACK block. Multiple values for a given key will be emitted // as multiple calls to OnHeader. - virtual void OnHeader(const HpackString& name, const HpackString& value) = 0; + virtual void OnHeader(const std::string& name, const std::string& value) = 0; // OnHeaderListEnd is called after successfully decoding an HPACK block into // an HTTP/2 header list. Will only be called once per block, even if it @@ -49,7 +48,7 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderNoOpListener ~HpackDecoderNoOpListener() override; void OnHeaderListStart() override; - void OnHeader(const HpackString& name, const HpackString& value) override; + void OnHeader(const std::string& name, const std::string& value) override; void OnHeaderListEnd() override; void OnHeaderErrorDetected(absl::string_view error_message) override; diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.cc index 528f1b69893..3140a19e5f5 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.cc @@ -4,7 +4,8 @@ #include "http2/hpack/decoder/hpack_decoder_state.h" -#include "http2/hpack/hpack_string.h" +#include <utility> + #include "http2/http2_constants.h" #include "http2/platform/api/http2_logging.h" #include "http2/platform/api/http2_macros.h" @@ -12,11 +13,11 @@ namespace http2 { namespace { -HpackString ExtractHpackString(HpackDecoderStringBuffer* string_buffer) { +std::string ExtractString(HpackDecoderStringBuffer* string_buffer) { if (string_buffer->IsBuffered()) { - return HpackString(string_buffer->ReleaseString()); + return string_buffer->ReleaseString(); } else { - auto result = HpackString(string_buffer->str()); + auto result = std::string(string_buffer->str()); string_buffer->Reset(); return result; } @@ -34,11 +35,6 @@ HpackDecoderState::HpackDecoderState(HpackDecoderListener* listener) error_(HpackDecodingError::kOk) {} HpackDecoderState::~HpackDecoderState() = default; -void HpackDecoderState::set_tables_debug_listener( - HpackDecoderTablesDebugListener* debug_listener) { - decoder_tables_.set_debug_listener(debug_listener); -} - void HpackDecoderState::ApplyHeaderTableSizeSetting( uint32_t header_table_size) { HTTP2_DVLOG(2) << "HpackDecoderState::ApplyHeaderTableSizeSetting(" @@ -114,10 +110,10 @@ void HpackDecoderState::OnNameIndexAndLiteralValue( allow_dynamic_table_size_update_ = false; const HpackStringPair* entry = decoder_tables_.Lookup(name_index); if (entry != nullptr) { - HpackString value(ExtractHpackString(value_buffer)); + std::string value(ExtractString(value_buffer)); listener_->OnHeader(entry->name, value); if (entry_type == HpackEntryType::kIndexedLiteralHeader) { - decoder_tables_.Insert(entry->name, value); + decoder_tables_.Insert(entry->name, std::move(value)); } } else { ReportError(HpackDecodingError::kInvalidNameIndex, ""); @@ -138,11 +134,11 @@ void HpackDecoderState::OnLiteralNameAndValue( return; } allow_dynamic_table_size_update_ = false; - HpackString name(ExtractHpackString(name_buffer)); - HpackString value(ExtractHpackString(value_buffer)); + std::string name(ExtractString(name_buffer)); + std::string value(ExtractString(value_buffer)); listener_->OnHeader(name, value); if (entry_type == HpackEntryType::kIndexedLiteralHeader) { - decoder_tables_.Insert(name, value); + decoder_tables_.Insert(std::move(name), std::move(value)); } } 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 cb2ff6d18eb..14b4184bd5a 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 @@ -43,11 +43,6 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderState : public HpackWholeEntryListener { // The listener may be changed at any time. HpackDecoderListener* listener() const { return listener_; } - // Set listener to be notified of insertions into the HPACK dynamic table, - // and uses of those entries. - void set_tables_debug_listener( - HpackDecoderTablesDebugListener* debug_listener); - // ApplyHeaderTableSizeSetting notifies this object that this endpoint has // received a SETTINGS ACK frame acknowledging an earlier SETTINGS frame from // this endpoint specifying a new value for SETTINGS_HEADER_TABLE_SIZE (the 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 500156595e0..698c655c961 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 @@ -10,7 +10,6 @@ #include <vector> #include "absl/strings/string_view.h" -#include "http2/hpack/hpack_string.h" #include "http2/hpack/http2_hpack_constants.h" #include "http2/http2_constants.h" #include "http2/platform/api/http2_logging.h" @@ -36,10 +35,12 @@ namespace { class MockHpackDecoderListener : public HpackDecoderListener { public: - MOCK_METHOD0(OnHeaderListStart, void()); - MOCK_METHOD2(OnHeader, - void(const HpackString& name, const HpackString& value)); - MOCK_METHOD0(OnHeaderListEnd, void()); + MOCK_METHOD(void, OnHeaderListStart, (), (override)); + MOCK_METHOD(void, + OnHeader, + (const std::string& name, const std::string& value), + (override)); + MOCK_METHOD(void, OnHeaderListEnd, (), (override)); MOCK_METHOD(void, OnHeaderErrorDetected, (absl::string_view error_message), @@ -155,8 +156,8 @@ class HpackDecoderStateTest : public QuicheTest { const HpackStringPair* entry = Lookup(dynamic_index + kFirstDynamicTableIndex - 1); VERIFY_NE(entry, nullptr); - VERIFY_EQ(entry->name.ToStringPiece(), name); - VERIFY_EQ(entry->value.ToStringPiece(), value); + VERIFY_EQ(entry->name, name); + VERIFY_EQ(entry->value, value); return AssertionSuccess(); } AssertionResult VerifyNoEntry(size_t dynamic_index) { diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.cc index 2a2184664d2..8f2243d7f6b 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.cc @@ -25,7 +25,8 @@ std::ostream& operator<<(std::ostream& out, // Since the value doesn't come over the wire, only a programming bug should // result in reaching this point. int unknown = static_cast<int>(v); - HTTP2_BUG << "Invalid HpackDecoderStringBuffer::State: " << unknown; + HTTP2_BUG(http2_bug_50_1) + << "Invalid HpackDecoderStringBuffer::State: " << unknown; return out << "HpackDecoderStringBuffer::State(" << unknown << ")"; } @@ -44,7 +45,8 @@ std::ostream& operator<<(std::ostream& out, // Since the value doesn't come over the wire, only a programming bug should // result in reaching this point. auto v2 = static_cast<int>(v); - HTTP2_BUG << "Invalid HpackDecoderStringBuffer::Backing: " << v2; + HTTP2_BUG(http2_bug_50_2) + << "Invalid HpackDecoderStringBuffer::Backing: " << v2; return out << "HpackDecoderStringBuffer::Backing(" << v2 << ")"; } diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.cc index 1b2cc11c7c3..9f50ec78490 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.cc @@ -4,6 +4,7 @@ #include "http2/hpack/decoder/hpack_decoder_tables.h" +#include "absl/strings/str_cat.h" #include "http2/hpack/http2_hpack_constants.h" #include "http2/platform/api/http2_logging.h" @@ -34,8 +35,23 @@ const std::vector<HpackStringPair>* GetStaticTable() { } // namespace -HpackDecoderTablesDebugListener::HpackDecoderTablesDebugListener() = default; -HpackDecoderTablesDebugListener::~HpackDecoderTablesDebugListener() = default; +HpackStringPair::HpackStringPair(std::string name, std::string value) + : name(std::move(name)), value(std::move(value)) { + HTTP2_DVLOG(3) << DebugString() << " ctor"; +} + +HpackStringPair::~HpackStringPair() { + HTTP2_DVLOG(3) << DebugString() << " dtor"; +} + +std::string HpackStringPair::DebugString() const { + return absl::StrCat("HpackStringPair(name=", name, ", value=", value, ")"); +} + +std::ostream& operator<<(std::ostream& os, const HpackStringPair& p) { + os << p.DebugString(); + return os; +} HpackDecoderStaticTable::HpackDecoderStaticTable( const std::vector<HpackStringPair>* table) @@ -50,13 +66,8 @@ const HpackStringPair* HpackDecoderStaticTable::Lookup(size_t index) const { return nullptr; } -HpackDecoderDynamicTable::HpackDecoderTableEntry::HpackDecoderTableEntry( - const HpackString& name, - const HpackString& value) - : HpackStringPair(name, value) {} - HpackDecoderDynamicTable::HpackDecoderDynamicTable() - : insert_count_(kFirstDynamicTableIndex - 1), debug_listener_(nullptr) {} + : insert_count_(kFirstDynamicTableIndex - 1) {} HpackDecoderDynamicTable::~HpackDecoderDynamicTable() = default; void HpackDecoderDynamicTable::DynamicTableSizeUpdate(size_t size_limit) { @@ -69,12 +80,12 @@ void HpackDecoderDynamicTable::DynamicTableSizeUpdate(size_t size_limit) { // TODO(jamessynge): Check somewhere before here that names received from the // peer are valid (e.g. are lower-case, no whitespace, etc.). -void HpackDecoderDynamicTable::Insert(const HpackString& name, - const HpackString& value) { - HpackDecoderTableEntry entry(name, value); +void HpackDecoderDynamicTable::Insert(std::string name, std::string value) { + HpackStringPair entry(std::move(name), std::move(value)); size_t entry_size = entry.size(); HTTP2_DVLOG(2) << "InsertEntry of size=" << entry_size - << "\n name: " << name << "\n value: " << value; + << "\n name: " << entry.name + << "\n value: " << entry.value; if (entry_size > size_limit_) { HTTP2_DVLOG(2) << "InsertEntry: entry larger than table, removing " << table_.size() << " entries, of total size " @@ -84,11 +95,6 @@ void HpackDecoderDynamicTable::Insert(const HpackString& name, return; } ++insert_count_; - if (debug_listener_ != nullptr) { - entry.time_added = debug_listener_->OnEntryInserted(entry, insert_count_); - HTTP2_DVLOG(2) << "OnEntryInserted returned time_added=" << entry.time_added - << " for insert_count_=" << insert_count_; - } size_t insert_limit = size_limit_ - entry_size; EnsureSizeNoMoreThan(insert_limit); table_.push_front(entry); @@ -100,13 +106,7 @@ void HpackDecoderDynamicTable::Insert(const HpackString& name, const HpackStringPair* HpackDecoderDynamicTable::Lookup(size_t index) const { if (index < table_.size()) { - const HpackDecoderTableEntry& entry = table_[index]; - if (debug_listener_ != nullptr) { - size_t insert_count_of_index = insert_count_ + table_.size() - index; - debug_listener_->OnUseEntry(entry, insert_count_of_index, - entry.time_added); - } - return &entry; + return &table_[index]; } return nullptr; } @@ -137,11 +137,6 @@ void HpackDecoderDynamicTable::RemoveLastEntry() { HpackDecoderTables::HpackDecoderTables() = default; HpackDecoderTables::~HpackDecoderTables() = default; -void HpackDecoderTables::set_debug_listener( - HpackDecoderTablesDebugListener* debug_listener) { - dynamic_table_.set_debug_listener(debug_listener); -} - const HpackStringPair* HpackDecoderTables::Lookup(size_t index) const { if (index < kFirstDynamicTableIndex) { return static_table_.Lookup(index); diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.h index 5f56cf2f01b..f75916d67df 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.h +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.h @@ -19,11 +19,13 @@ #include <stddef.h> #include <cstdint> +#include <iosfwd> +#include <string> +#include <utility> #include <vector> -#include "http2/hpack/hpack_string.h" #include "http2/http2_constants.h" -#include "http2/platform/api/http2_containers.h" +#include "quic/core/quic_circular_deque.h" #include "common/platform/api/quiche_export.h" namespace http2 { @@ -31,37 +33,23 @@ namespace test { class HpackDecoderTablesPeer; } // namespace test -// HpackDecoderTablesDebugListener supports a QUIC experiment, enabling -// the gathering of information about the time-line of use of HPACK -// dynamic table entries. -class QUICHE_EXPORT_PRIVATE HpackDecoderTablesDebugListener { - public: - HpackDecoderTablesDebugListener(); - virtual ~HpackDecoderTablesDebugListener(); - - HpackDecoderTablesDebugListener(const HpackDecoderTablesDebugListener&) = - delete; - HpackDecoderTablesDebugListener& operator=( - const HpackDecoderTablesDebugListener&) = delete; - - // The entry has been inserted into the dynamic table. insert_count starts at - // 62 because 61 is the last index in the static table; insert_count increases - // by 1 with each insert into the dynamic table; it is not incremented when - // when a entry is too large to fit into the dynamic table at all (which has - // the effect of emptying the dynamic table). - // Returns a value that can be used as time_added in OnUseEntry. - virtual int64_t OnEntryInserted(const HpackStringPair& entry, - size_t insert_count) = 0; - - // The entry has been used, either for the name or for the name and value. - // insert_count is the same as passed to OnEntryInserted when entry was - // inserted to the dynamic table, and time_added is the value that was - // returned by OnEntryInserted. - virtual void OnUseEntry(const HpackStringPair& entry, - size_t insert_count, - int64_t time_added) = 0; +struct QUICHE_EXPORT_PRIVATE HpackStringPair { + HpackStringPair(std::string name, std::string value); + ~HpackStringPair(); + + // Returns the size of a header entry with this name and value, per the RFC: + // http://httpwg.org/specs/rfc7541.html#calculating.table.size + size_t size() const { return 32 + name.size() + value.size(); } + + std::string DebugString() const; + + const std::string name; + const std::string value; }; +QUICHE_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, + const HpackStringPair& p); + // See http://httpwg.org/specs/rfc7541.html#static.table.definition for the // contents, and http://httpwg.org/specs/rfc7541.html#index.address.space for // info about accessing the static table. @@ -93,13 +81,6 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderDynamicTable { HpackDecoderDynamicTable(const HpackDecoderDynamicTable&) = delete; HpackDecoderDynamicTable& operator=(const HpackDecoderDynamicTable&) = delete; - // Set the listener to be notified of insertions into this table, and later - // uses of those entries. Added for evaluation of changes to QUIC's use - // of HPACK. - void set_debug_listener(HpackDecoderTablesDebugListener* debug_listener) { - debug_listener_ = debug_listener; - } - // Sets a new size limit, received from the peer; performs evictions if // necessary to ensure that the current size does not exceed the new limit. // The caller needs to have validated that size_limit does not @@ -108,7 +89,7 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderDynamicTable { // Insert entry if possible. // If entry is too large to insert, then dynamic table will be empty. - void Insert(const HpackString& name, const HpackString& value); + void Insert(std::string name, std::string value); // If index is valid, returns a pointer to the entry, otherwise returns // nullptr. @@ -119,10 +100,6 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderDynamicTable { private: friend class test::HpackDecoderTablesPeer; - struct HpackDecoderTableEntry : public HpackStringPair { - HpackDecoderTableEntry(const HpackString& name, const HpackString& value); - int64_t time_added; - }; // Drop older entries to ensure the size is not greater than limit. void EnsureSizeNoMoreThan(size_t limit); @@ -130,7 +107,7 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderDynamicTable { // Removes the oldest dynamic table entry. void RemoveLastEntry(); - Http2Deque<HpackDecoderTableEntry> table_; + quic::QuicCircularDeque<HpackStringPair> table_; // The last received DynamicTableSizeUpdate value, initialized to // SETTINGS_HEADER_TABLE_SIZE. @@ -141,7 +118,6 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderDynamicTable { // insert_count_ and debug_listener_ are used by a QUIC experiment; remove // when the experiment is done. size_t insert_count_; - HpackDecoderTablesDebugListener* debug_listener_; }; class QUICHE_EXPORT_PRIVATE HpackDecoderTables { @@ -152,11 +128,6 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderTables { HpackDecoderTables(const HpackDecoderTables&) = delete; HpackDecoderTables& operator=(const HpackDecoderTables&) = delete; - // Set the listener to be notified of insertions into the dynamic table, and - // later uses of those entries. Added for evaluation of changes to QUIC's use - // of HPACK. - void set_debug_listener(HpackDecoderTablesDebugListener* debug_listener); - // Sets a new size limit, received from the peer; performs evictions if // necessary to ensure that the current size does not exceed the new limit. // The caller needs to have validated that size_limit does not @@ -167,10 +138,8 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderTables { // Insert entry if possible. // If entry is too large to insert, then dynamic table will be empty. - // TODO(jamessynge): Add methods for moving the string(s) into the table, - // or for otherwise avoiding unnecessary copies. - void Insert(const HpackString& name, const HpackString& value) { - dynamic_table_.Insert(name, value); + void Insert(std::string name, std::string value) { + dynamic_table_.Insert(std::move(name), std::move(value)); } // If index is valid, returns a pointer to the entry, otherwise returns diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables_test.cc index a3e4ee097a0..b7ed5273264 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables_test.cc @@ -205,7 +205,7 @@ class HpackDecoderTablesTest : public HpackDecoderStaticTableTest { // move up by 1 index. AssertionResult Insert(const std::string& name, const std::string& value) { size_t old_count = num_dynamic_entries(); - tables_.Insert(HpackString(name), HpackString(value)); + tables_.Insert(name, value); FakeInsert(name, value); VERIFY_EQ(old_count + 1, fake_dynamic_table_.size()); FakeTrim(dynamic_size_limit()); 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 dc6ab5813cf..ce44ccb04b5 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 @@ -15,7 +15,6 @@ #include "http2/hpack/decoder/hpack_decoder_listener.h" #include "http2/hpack/decoder/hpack_decoder_state.h" #include "http2/hpack/decoder/hpack_decoder_tables.h" -#include "http2/hpack/hpack_string.h" #include "http2/hpack/http2_hpack_constants.h" #include "http2/hpack/tools/hpack_block_builder.h" #include "http2/hpack/tools/hpack_example.h" @@ -63,10 +62,12 @@ typedef std::vector<HpackHeaderEntry> HpackHeaderEntries; // and with VerifyDynamicTableContents. class MockHpackDecoderListener : public HpackDecoderListener { public: - MOCK_METHOD0(OnHeaderListStart, void()); - MOCK_METHOD2(OnHeader, - void(const HpackString& name, const HpackString& value)); - MOCK_METHOD0(OnHeaderListEnd, void()); + MOCK_METHOD(void, OnHeaderListStart, (), (override)); + MOCK_METHOD(void, + OnHeader, + (const std::string& name, const std::string& value), + (override)); + MOCK_METHOD(void, OnHeaderListEnd, (), (override)); MOCK_METHOD(void, OnHeaderErrorDetected, (absl::string_view error_message), @@ -95,10 +96,10 @@ class HpackDecoderTest : public QuicheTestWithParam<bool>, // Called for each header name-value pair that is decoded, in the order they // appear in the HPACK block. Multiple values for a given key will be emitted // as multiple calls to OnHeader. - void OnHeader(const HpackString& name, const HpackString& value) override { + void OnHeader(const std::string& name, const std::string& value) override { ASSERT_TRUE(saw_start_); ASSERT_FALSE(saw_end_); - header_entries_.emplace_back(name.ToString(), value.ToString()); + header_entries_.emplace_back(name, value); } // OnHeaderBlockEnd is called after successfully decoding an HPACK block. Will @@ -190,8 +191,8 @@ class HpackDecoderTest : public QuicheTestWithParam<bool>, const HpackStringPair* entry = Lookup(dynamic_index + kFirstDynamicTableIndex - 1); VERIFY_NE(entry, nullptr); - VERIFY_EQ(entry->name.ToStringPiece(), name); - VERIFY_EQ(entry->value.ToStringPiece(), value); + VERIFY_EQ(entry->name, name); + VERIFY_EQ(entry->value, value); return AssertionSuccess(); } AssertionResult VerifyNoEntry(size_t dynamic_index) { diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.cc index a228b6a274e..de7d2e2edac 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.cc @@ -4,10 +4,10 @@ #include "http2/hpack/decoder/hpack_entry_collector.h" +#include "absl/strings/str_cat.h" #include "http2/hpack/decoder/hpack_string_collector.h" #include "http2/hpack/http2_hpack_constants.h" #include "http2/platform/api/http2_logging.h" -#include "http2/platform/api/http2_string_utils.h" #include "http2/platform/api/http2_test_helpers.h" #include "common/platform/api/quiche_test.h" @@ -254,25 +254,25 @@ std::string HpackEntryCollector::ToString() const { if (header_type_ == kInvalidHeaderType) { result += "<unset>"; } else { - Http2StrAppend(&result, header_type_); + absl::StrAppend(&result, header_type_); } } if (index_ != 0) { - Http2StrAppend(&result, " Index=", index_); + absl::StrAppend(&result, " Index=", index_); } if (!name_.IsClear()) { - Http2StrAppend(&result, " Name", name_.ToString()); + absl::StrAppend(&result, " Name", name_.ToString()); } if (!value_.IsClear()) { - Http2StrAppend(&result, " Value", value_.ToString()); + absl::StrAppend(&result, " Value", value_.ToString()); } if (!started_) { EXPECT_FALSE(ended_); - Http2StrAppend(&result, " !started"); + absl::StrAppend(&result, " !started"); } else if (!ended_) { - Http2StrAppend(&result, " !ended"); + absl::StrAppend(&result, " !ended"); } else { - Http2StrAppend(&result, " Complete"); + absl::StrAppend(&result, " Complete"); } return result; } diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc index 4e4a4da6da3..deb07a6dcdc 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc @@ -86,7 +86,7 @@ DecodeStatus HpackEntryDecoder::Start(DecodeBuffer* db, return status; } - HTTP2_BUG << "Unreachable"; + HTTP2_BUG(http2_bug_63_1) << "Unreachable"; return DecodeStatus::kDecodeError; } @@ -252,7 +252,7 @@ bool HpackEntryDecoder::DispatchOnType(HpackEntryDecoderListener* listener) { return true; } - HTTP2_BUG << "Unreachable, entry_type=" << entry_type; + HTTP2_BUG(http2_bug_63_2) << "Unreachable, entry_type=" << entry_type; return true; } diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.cc index 99a0770556f..bdfaddc6229 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.cc @@ -9,7 +9,6 @@ #include "http2/platform/api/http2_flag_utils.h" #include "http2/platform/api/http2_flags.h" #include "http2/platform/api/http2_logging.h" -#include "http2/platform/api/http2_string_utils.h" namespace http2 { @@ -354,7 +353,8 @@ DecodeStatus HpackEntryTypeDecoder::Start(DecodeBuffer* db) { // All of those bits are 1, so the varint extends into another byte. return varint_decoder_.StartExtended(7, db); } - HTTP2_BUG << "Unreachable, byte=" << std::hex << static_cast<uint32_t>(byte); + HTTP2_BUG(http2_bug_66_1) + << "Unreachable, byte=" << std::hex << static_cast<uint32_t>(byte); HTTP2_CODE_COUNT_N(decompress_failure_3, 17, 23); return DecodeStatus::kDecodeError; } diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.cc index 178096ccee2..eeed643d3ff 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.cc @@ -9,7 +9,8 @@ #include <iosfwd> #include <ostream> -#include "http2/platform/api/http2_string_utils.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" #include "http2/platform/api/http2_test_helpers.h" #include "common/platform/api/quiche_test.h" @@ -69,7 +70,7 @@ void HpackStringCollector::OnStringData(const char* data, size_t length) { absl::string_view sp(data, length); EXPECT_TRUE(IsInProgress()) << ToString(); EXPECT_LE(sp.size(), len) << ToString(); - Http2StrAppend(&s, sp); + absl::StrAppend(&s, sp); EXPECT_LE(s.size(), len) << ToString(); } @@ -116,7 +117,7 @@ std::ostream& operator<<(std::ostream& out, const HpackStringCollector& v) { if (!v.s.empty() && v.len != v.s.size()) { out << " (" << v.s.size() << ")"; } - return out << ", String=\"" << Http2HexEscape(v.s) << "\")"; + return out << ", String=\"" << absl::CHexEscape(v.s) << "\")"; } } // namespace test diff --git a/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.cc b/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.cc deleted file mode 100644 index fb9691cf9a6..00000000000 --- a/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.cc +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2016 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 "http2/hpack/hpack_string.h" - -#include <utility> - -#include "absl/strings/str_cat.h" -#include "http2/platform/api/http2_logging.h" - -namespace http2 { - -HpackString::HpackString(const char* data) : str_(data) {} -HpackString::HpackString(absl::string_view str) : str_(std::string(str)) {} -HpackString::HpackString(std::string str) : str_(std::move(str)) {} -HpackString::HpackString(const HpackString& other) = default; -HpackString::~HpackString() = default; - -absl::string_view HpackString::ToStringPiece() const { - return str_; -} - -bool HpackString::operator==(const HpackString& other) const { - return str_ == other.str_; -} -bool HpackString::operator==(absl::string_view str) const { - return str == str_; -} - -bool operator==(absl::string_view a, const HpackString& b) { - return b == a; -} -bool operator!=(absl::string_view a, const HpackString& b) { - return !(b == a); -} -bool operator!=(const HpackString& a, const HpackString& b) { - return !(a == b); -} -bool operator!=(const HpackString& a, absl::string_view b) { - return !(a == b); -} -std::ostream& operator<<(std::ostream& out, const HpackString& v) { - return out << v.ToString(); -} - -HpackStringPair::HpackStringPair(const HpackString& name, - const HpackString& value) - : name(name), value(value) { - HTTP2_DVLOG(3) << DebugString() << " ctor"; -} - -HpackStringPair::HpackStringPair(absl::string_view name, - absl::string_view value) - : name(name), value(value) { - HTTP2_DVLOG(3) << DebugString() << " ctor"; -} - -HpackStringPair::~HpackStringPair() { - HTTP2_DVLOG(3) << DebugString() << " dtor"; -} - -std::string HpackStringPair::DebugString() const { - return absl::StrCat("HpackStringPair(name=", name.ToString(), - ", value=", value.ToString(), ")"); -} - -std::ostream& operator<<(std::ostream& os, const HpackStringPair& p) { - os << p.DebugString(); - return os; -} - -} // namespace http2 diff --git a/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.h b/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.h deleted file mode 100644 index 78960467c99..00000000000 --- a/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2016 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_HTTP2_HPACK_HPACK_STRING_H_ -#define QUICHE_HTTP2_HPACK_HPACK_STRING_H_ - -// HpackString is currently a very simple container for a string, but allows us -// to relatively easily experiment with alternate string storage mechanisms for -// handling strings to be encoded with HPACK, or decoded from HPACK, such as -// a ref-counted string. - -#include <stddef.h> - -#include <iosfwd> -#include <string> - -#include "absl/strings/string_view.h" -#include "common/platform/api/quiche_export.h" - -namespace http2 { - -class QUICHE_EXPORT_PRIVATE HpackString { - public: - explicit HpackString(const char* data); - explicit HpackString(absl::string_view str); - explicit HpackString(std::string str); - HpackString(const HpackString& other); - - // Not sure yet whether this move ctor is required/sensible. - HpackString(HpackString&& other) = default; - - ~HpackString(); - - size_t size() const { return str_.size(); } - const std::string& ToString() const { return str_; } - absl::string_view ToStringPiece() const; - - bool operator==(const HpackString& other) const; - - bool operator==(absl::string_view str) const; - - private: - std::string str_; -}; - -QUICHE_EXPORT_PRIVATE bool operator==(absl::string_view a, - const HpackString& b); -QUICHE_EXPORT_PRIVATE bool operator!=(absl::string_view a, - const HpackString& b); -QUICHE_EXPORT_PRIVATE bool operator!=(const HpackString& a, - const HpackString& b); -QUICHE_EXPORT_PRIVATE bool operator!=(const HpackString& a, - absl::string_view b); -QUICHE_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out, - const HpackString& v); - -struct QUICHE_EXPORT_PRIVATE HpackStringPair { - HpackStringPair(const HpackString& name, const HpackString& value); - HpackStringPair(absl::string_view name, absl::string_view value); - ~HpackStringPair(); - - // Returns the size of a header entry with this name and value, per the RFC: - // http://httpwg.org/specs/rfc7541.html#calculating.table.size - size_t size() const { return 32 + name.size() + value.size(); } - - std::string DebugString() const; - - const HpackString name; - const HpackString value; -}; - -QUICHE_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, - const HpackStringPair& p); - -} // namespace http2 - -#endif // QUICHE_HTTP2_HPACK_HPACK_STRING_H_ diff --git a/chromium/net/third_party/quiche/src/http2/hpack/hpack_string_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/hpack_string_test.cc deleted file mode 100644 index c8462e82d03..00000000000 --- a/chromium/net/third_party/quiche/src/http2/hpack/hpack_string_test.cc +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2016 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 "http2/hpack/hpack_string.h" - -// Tests of HpackString. - -#include <utility> - -#include "http2/platform/api/http2_logging.h" -#include "http2/platform/api/http2_test_helpers.h" -#include "common/platform/api/quiche_test.h" - -using ::testing::AssertionFailure; -using ::testing::AssertionResult; -using ::testing::AssertionSuccess; - -namespace http2 { -namespace test { -namespace { - -const char kStr0[] = "s0: Some string to be copied into another string."; -const char kStr1[] = "S1 - some string to be copied into yet another string."; - -class HpackStringTest : public QuicheTest { - protected: - AssertionResult VerifyNotEqual(HpackString* actual, - const std::string& not_expected_str) { - const char* not_expected_ptr = not_expected_str.c_str(); - absl::string_view not_expected_sp(not_expected_str); - - VERIFY_NE(*actual, not_expected_ptr); - VERIFY_NE(*actual, not_expected_sp); - VERIFY_NE(*actual, not_expected_str); - VERIFY_NE(actual->ToStringPiece(), not_expected_sp); - - if (!(not_expected_ptr != *actual)) { - return AssertionFailure(); - } - if (!(not_expected_sp != *actual)) { - return AssertionFailure(); - } - if (!(not_expected_str != *actual)) { - return AssertionFailure(); - } - if (!(not_expected_sp != actual->ToStringPiece())) { - return AssertionFailure(); - } - - return AssertionSuccess(); - } - - AssertionResult VerifyEqual(HpackString* actual, - const std::string& expected_str) { - VERIFY_EQ(actual->size(), expected_str.size()); - - const char* expected_ptr = expected_str.c_str(); - const absl::string_view expected_sp(expected_str); - - VERIFY_EQ(*actual, expected_ptr); - VERIFY_EQ(*actual, expected_sp); - VERIFY_EQ(*actual, expected_str); - VERIFY_EQ(actual->ToStringPiece(), expected_sp); - - if (!(expected_sp == *actual)) { - return AssertionFailure(); - } - if (!(expected_ptr == *actual)) { - return AssertionFailure(); - } - if (!(expected_str == *actual)) { - return AssertionFailure(); - } - if (!(expected_sp == actual->ToStringPiece())) { - return AssertionFailure(); - } - - return AssertionSuccess(); - } -}; - -TEST_F(HpackStringTest, CharArrayConstructor) { - HpackString hs0(kStr0); - EXPECT_TRUE(VerifyEqual(&hs0, kStr0)); - EXPECT_TRUE(VerifyNotEqual(&hs0, kStr1)); - - HpackString hs1(kStr1); - EXPECT_TRUE(VerifyEqual(&hs1, kStr1)); - EXPECT_TRUE(VerifyNotEqual(&hs1, kStr0)); -} - -TEST_F(HpackStringTest, StringPieceConstructor) { - absl::string_view sp0(kStr0); - HpackString hs0(sp0); - EXPECT_TRUE(VerifyEqual(&hs0, kStr0)); - EXPECT_TRUE(VerifyNotEqual(&hs0, kStr1)); - - absl::string_view sp1(kStr1); - HpackString hs1(sp1); - EXPECT_TRUE(VerifyEqual(&hs1, kStr1)); - EXPECT_TRUE(VerifyNotEqual(&hs1, kStr0)); -} - -TEST_F(HpackStringTest, MoveStringConstructor) { - std::string str0(kStr0); - HpackString hs0(str0); - EXPECT_TRUE(VerifyEqual(&hs0, kStr0)); - EXPECT_TRUE(VerifyNotEqual(&hs0, kStr1)); - - std::string str1(kStr1); - HpackString hs1(str1); - EXPECT_TRUE(VerifyEqual(&hs1, kStr1)); - EXPECT_TRUE(VerifyNotEqual(&hs1, kStr0)); -} - -TEST_F(HpackStringTest, CopyConstructor) { - absl::string_view sp0(kStr0); - HpackString hs0(sp0); - HpackString hs1(hs0); - EXPECT_EQ(hs0, hs1); - - EXPECT_TRUE(VerifyEqual(&hs0, kStr0)); - EXPECT_TRUE(VerifyEqual(&hs1, kStr0)); - - EXPECT_TRUE(VerifyNotEqual(&hs0, kStr1)); - EXPECT_TRUE(VerifyNotEqual(&hs1, kStr1)); -} - -TEST_F(HpackStringTest, MoveConstructor) { - absl::string_view sp0(kStr0); - HpackString hs0(sp0); - EXPECT_TRUE(VerifyEqual(&hs0, kStr0)); - EXPECT_TRUE(VerifyNotEqual(&hs0, "")); - - HpackString hs1(std::move(hs0)); - EXPECT_NE(hs0, hs1); - - EXPECT_TRUE(VerifyEqual(&hs1, kStr0)); - EXPECT_TRUE(VerifyEqual(&hs0, "")); - EXPECT_TRUE(VerifyNotEqual(&hs1, "")); - - HTTP2_LOG(INFO) << hs0; - HTTP2_LOG(INFO) << hs1; -} - -} // namespace -} // namespace test -} // namespace http2 diff --git a/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.cc b/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.cc index 8f85304b8d5..3dbda955f0d 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.cc @@ -46,7 +46,7 @@ void HpackBlockBuilder::AppendEntryTypeAndVarint(HpackEntryType entry_type, prefix_length = 4; break; default: - HTTP2_BUG << "Unreached, entry_type=" << entry_type; + HTTP2_BUG(http2_bug_110_1) << "Unreached, entry_type=" << entry_type; high_bits = 0; prefix_length = 0; break; diff --git a/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.cc b/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.cc index 25f44c905dc..00664b27f14 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/tools/hpack_example.cc @@ -40,9 +40,9 @@ void HpackExampleToStringOrDie(absl::string_view example, std::string* output) { example.remove_prefix(pos + 1); continue; } - HTTP2_BUG << "Can't parse byte " << static_cast<int>(c0) - << absl::StrCat(" (0x", Http2Hex(c0), ")") - << "\nExample: " << example; + HTTP2_BUG(http2_bug_107_1) + << "Can't parse byte " << static_cast<int>(c0) + << absl::StrCat(" (0x", absl::Hex(c0), ")") << "\nExample: " << example; } QUICHE_CHECK_LT(0u, output->size()) << "Example is empty."; } diff --git a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.cc index 5ba0b74245a..a9e6e74cb07 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.cc @@ -5,7 +5,6 @@ #include "http2/hpack/varint/hpack_varint_decoder.h" #include "absl/strings/str_cat.h" -#include "http2/platform/api/http2_string_utils.h" namespace http2 { diff --git a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_round_trip_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_round_trip_test.cc index 7b73edb8d94..a8ba99a8a31 100644 --- a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_round_trip_test.cc +++ b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_round_trip_test.cc @@ -162,7 +162,7 @@ class HpackVarintRoundTripTest : public RandomDecoderTest { for (const uint64_t value : values) { Encode(value, prefix_length); // Sets buffer_. - std::string msg = absl::StrCat("value=", value, " (0x", Http2Hex(value), + std::string msg = absl::StrCat("value=", value, " (0x", absl::Hex(value), "), prefix_length=", prefix_length, ", expected_bytes=", expected_bytes, "\n", Http2HexDump(buffer_)); diff --git a/chromium/net/third_party/quiche/src/http2/http2_constants.cc b/chromium/net/third_party/quiche/src/http2/http2_constants.cc index d333cf45ecf..2faf2f94ebe 100644 --- a/chromium/net/third_party/quiche/src/http2/http2_constants.cc +++ b/chromium/net/third_party/quiche/src/http2/http2_constants.cc @@ -53,7 +53,7 @@ std::string Http2FrameFlagsToString(Http2FrameType type, uint8_t flags) { if (!s.empty()) { s.push_back('|'); } - Http2StrAppend(&s, v); + absl::StrAppend(&s, v); flags ^= bit; }; if (flags & 0x01) { @@ -123,7 +123,7 @@ std::string Http2ErrorCodeToString(uint32_t v) { case 0xd: return "HTTP_1_1_REQUIRED"; } - return absl::StrCat("UnknownErrorCode(0x", Http2Hex(v), ")"); + return absl::StrCat("UnknownErrorCode(0x", absl::Hex(v), ")"); } std::string Http2ErrorCodeToString(Http2ErrorCode v) { return Http2ErrorCodeToString(static_cast<uint32_t>(v)); @@ -144,7 +144,7 @@ std::string Http2SettingsParameterToString(uint32_t v) { case 0x6: return "MAX_HEADER_LIST_SIZE"; } - return absl::StrCat("UnknownSettingsParameter(0x", Http2Hex(v), ")"); + return absl::StrCat("UnknownSettingsParameter(0x", absl::Hex(v), ")"); } std::string Http2SettingsParameterToString(Http2SettingsParameter v) { return Http2SettingsParameterToString(static_cast<uint32_t>(v)); diff --git a/chromium/net/third_party/quiche/src/http2/http2_constants.h b/chromium/net/third_party/quiche/src/http2/http2_constants.h index cd05eb45d1b..65e55dbc1db 100644 --- a/chromium/net/third_party/quiche/src/http2/http2_constants.h +++ b/chromium/net/third_party/quiche/src/http2/http2_constants.h @@ -48,12 +48,8 @@ enum class Http2FrameType : uint8_t { // Is the frame type known/supported? inline bool IsSupportedHttp2FrameType(uint32_t v) { - if (GetHttp2RestartFlag(http2_parse_priority_update_frame) && - v == static_cast<uint32_t>(Http2FrameType::PRIORITY_UPDATE)) { - return true; - } - - return v <= static_cast<uint32_t>(Http2FrameType::ALTSVC); + return v <= static_cast<uint32_t>(Http2FrameType::ALTSVC) || + v == static_cast<uint32_t>(Http2FrameType::PRIORITY_UPDATE); } inline bool IsSupportedHttp2FrameType(Http2FrameType v) { return IsSupportedHttp2FrameType(static_cast<uint32_t>(v)); diff --git a/chromium/net/third_party/quiche/src/http2/http2_structures_test.cc b/chromium/net/third_party/quiche/src/http2/http2_structures_test.cc index ffd67b3dacc..44712bc13b9 100644 --- a/chromium/net/third_party/quiche/src/http2/http2_structures_test.cc +++ b/chromium/net/third_party/quiche/src/http2/http2_structures_test.cc @@ -21,7 +21,6 @@ #include "absl/strings/str_cat.h" #include "http2/http2_structures_test_util.h" -#include "http2/platform/api/http2_string_utils.h" #include "http2/platform/api/http2_test_helpers.h" #include "http2/test_tools/http2_random.h" #include "common/platform/api/quiche_test.h" diff --git a/chromium/net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h b/chromium/net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h index 93ad67717f1..6f2fe51418e 100644 --- a/chromium/net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h +++ b/chromium/net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h @@ -5,10 +5,16 @@ #ifndef QUICHE_HTTP2_PLATFORM_API_HTTP2_BUG_TRACKER_H_ #define QUICHE_HTTP2_PLATFORM_API_HTTP2_BUG_TRACKER_H_ -#include "net/http2/platform/impl/http2_bug_tracker_impl.h" +#include "common/platform/api/quiche_bug_tracker.h" + +#define HTTP2_BUG QUICHE_BUG +#define HTTP2_BUG_IF QUICHE_BUG_IF + +// V2 macros are the same as all the HTTP2_BUG flavor above, but they take a +// bug_id parameter. +#define HTTP2_BUG_V2 QUICHE_BUG +#define HTTP2_BUG_IF_V2 QUICHE_BUG_IF -#define HTTP2_BUG HTTP2_BUG_IMPL -#define HTTP2_BUG_IF HTTP2_BUG_IF_IMPL #define FLAGS_http2_always_log_bugs_for_tests \ FLAGS_http2_always_log_bugs_for_tests_IMPL diff --git a/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils.h b/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils.h index ec421b7efb9..087bd254a96 100644 --- a/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils.h +++ b/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils.h @@ -15,11 +15,6 @@ namespace http2 { template <typename... Args> -inline void Http2StrAppend(std::string* output, const Args&... args) { - Http2StrAppendImpl(output, std::forward<const Args&>(args)...); -} - -template <typename... Args> inline std::string Http2StringPrintf(const Args&... args) { return Http2StringPrintfImpl(std::forward<const Args&>(args)...); } @@ -36,16 +31,6 @@ inline std::string Http2HexDump(absl::string_view data) { return Http2HexDumpImpl(data); } -inline std::string Http2HexEscape(absl::string_view data) { - return Http2HexEscapeImpl(data); -} - -template <typename Number> -inline std::string Http2Hex(Number number) { - static_assert(std::is_integral<Number>::value, "Number has to be an int"); - return Http2HexImpl(number); -} - } // namespace http2 #endif // QUICHE_HTTP2_PLATFORM_API_HTTP2_STRING_UTILS_H_ diff --git a/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils_test.cc b/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils_test.cc index 1621b52d82a..9ee7869edd3 100644 --- a/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils_test.cc +++ b/chromium/net/third_party/quiche/src/http2/platform/api/http2_string_utils_test.cc @@ -4,110 +4,12 @@ #include "http2/platform/api/http2_string_utils.h" -#include <cstdint> - -#include "absl/strings/string_view.h" #include "common/platform/api/quiche_test.h" namespace http2 { namespace test { namespace { -TEST(Http2StringUtilsTest, Http2StrAppend) { - // No arguments on empty string. - std::string output; - Http2StrAppend(&output); - EXPECT_TRUE(output.empty()); - - // Single string-like argument. - const char kFoo[] = "foo"; - const std::string string_foo(kFoo); - const absl::string_view stringpiece_foo(string_foo); - Http2StrAppend(&output, kFoo); - EXPECT_EQ("foo", output); - Http2StrAppend(&output, string_foo); - EXPECT_EQ("foofoo", output); - Http2StrAppend(&output, stringpiece_foo); - EXPECT_EQ("foofoofoo", output); - - // No arguments on non-empty string. - Http2StrAppend(&output); - EXPECT_EQ("foofoofoo", output); - - output.clear(); - - // Two string-like arguments. - const char kBar[] = "bar"; - const absl::string_view stringpiece_bar(kBar); - const std::string string_bar(kBar); - Http2StrAppend(&output, kFoo, kBar); - EXPECT_EQ("foobar", output); - Http2StrAppend(&output, kFoo, string_bar); - EXPECT_EQ("foobarfoobar", output); - Http2StrAppend(&output, kFoo, stringpiece_bar); - EXPECT_EQ("foobarfoobarfoobar", output); - Http2StrAppend(&output, string_foo, kBar); - EXPECT_EQ("foobarfoobarfoobarfoobar", output); - - output.clear(); - - Http2StrAppend(&output, string_foo, string_bar); - EXPECT_EQ("foobar", output); - Http2StrAppend(&output, string_foo, stringpiece_bar); - EXPECT_EQ("foobarfoobar", output); - Http2StrAppend(&output, stringpiece_foo, kBar); - EXPECT_EQ("foobarfoobarfoobar", output); - Http2StrAppend(&output, stringpiece_foo, string_bar); - EXPECT_EQ("foobarfoobarfoobarfoobar", output); - - output.clear(); - - Http2StrAppend(&output, stringpiece_foo, stringpiece_bar); - EXPECT_EQ("foobar", output); - - // Many-many arguments. - Http2StrAppend(&output, "foo", "bar", "baz", "qux", "quux", "quuz", "corge", - "grault", "garply", "waldo", "fred", "plugh", "xyzzy", "thud"); - EXPECT_EQ( - "foobarfoobarbazquxquuxquuzcorgegraultgarplywaldofredplughxyzzythud", - output); - - output.clear(); - - // Numerical arguments. - const int16_t i = 1; - const uint64_t u = 8; - const double d = 3.1415; - - Http2StrAppend(&output, i, " ", u); - EXPECT_EQ("1 8", output); - Http2StrAppend(&output, d, i, i, u, i); - EXPECT_EQ("1 83.14151181", output); - Http2StrAppend(&output, "i: ", i, ", u: ", u, ", d: ", d); - EXPECT_EQ("1 83.14151181i: 1, u: 8, d: 3.1415", output); - - output.clear(); - - // Boolean arguments. - const bool t = true; - const bool f = false; - - Http2StrAppend(&output, t); - EXPECT_EQ("1", output); - Http2StrAppend(&output, f); - EXPECT_EQ("10", output); - Http2StrAppend(&output, f, t, t, f); - EXPECT_EQ("100110", output); - - output.clear(); - - // Mixed string-like, numerical, and Boolean arguments. - Http2StrAppend(&output, kFoo, i, string_foo, f, u, t, stringpiece_bar, d, t); - EXPECT_EQ("foo1foo081bar3.14151", output); - Http2StrAppend(&output, d, t, t, string_bar, i, u, kBar, t, d, f); - EXPECT_EQ("foo1foo081bar3.141513.141511bar18bar13.14150", output); -} - TEST(Http2StringUtilsTest, Http2StringPrintf) { EXPECT_EQ("", Http2StringPrintf("%s", "")); EXPECT_EQ("foobar", Http2StringPrintf("%sbar", "foo")); diff --git a/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.cc b/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.cc index 1a148709665..a053beb10df 100644 --- a/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.cc +++ b/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.cc @@ -6,9 +6,9 @@ #include <type_traits> +#include "absl/strings/escaping.h" #include "http2/http2_structures_test_util.h" #include "http2/platform/api/http2_logging.h" -#include "http2/platform/api/http2_string_utils.h" #include "http2/platform/api/http2_test_helpers.h" #include "common/platform/api/quiche_test.h" @@ -442,16 +442,16 @@ void FrameParts::OnFrameSizeError(const Http2FrameHeader& header) { void FrameParts::OutputTo(std::ostream& out) const { out << "FrameParts{\n frame_header_: " << frame_header_ << "\n"; if (!payload_.empty()) { - out << " payload_=\"" << Http2HexEscape(payload_) << "\"\n"; + out << " payload_=\"" << absl::CHexEscape(payload_) << "\"\n"; } if (!padding_.empty()) { - out << " padding_=\"" << Http2HexEscape(padding_) << "\"\n"; + out << " padding_=\"" << absl::CHexEscape(padding_) << "\"\n"; } if (!altsvc_origin_.empty()) { - out << " altsvc_origin_=\"" << Http2HexEscape(altsvc_origin_) << "\"\n"; + out << " altsvc_origin_=\"" << absl::CHexEscape(altsvc_origin_) << "\"\n"; } if (!altsvc_value_.empty()) { - out << " altsvc_value_=\"" << Http2HexEscape(altsvc_value_) << "\"\n"; + out << " altsvc_value_=\"" << absl::CHexEscape(altsvc_value_) << "\"\n"; } if (opt_priority_) { out << " priority=" << opt_priority_.value() << "\n"; diff --git a/chromium/net/third_party/quiche/src/http2/tools/http2_frame_builder.cc b/chromium/net/third_party/quiche/src/http2/tools/http2_frame_builder.cc index d6cd11059f4..1481594fbe5 100644 --- a/chromium/net/third_party/quiche/src/http2/tools/http2_frame_builder.cc +++ b/chromium/net/third_party/quiche/src/http2/tools/http2_frame_builder.cc @@ -11,7 +11,7 @@ #include <netinet/in.h> // for htonl, htons #endif -#include "http2/platform/api/http2_string_utils.h" +#include "absl/strings/str_cat.h" #include "common/platform/api/quiche_test.h" namespace http2 { @@ -31,7 +31,7 @@ Http2FrameBuilder::Http2FrameBuilder(const Http2FrameHeader& v) { } void Http2FrameBuilder::Append(absl::string_view s) { - Http2StrAppend(&buffer_, s); + absl::StrAppend(&buffer_, s); } void Http2FrameBuilder::AppendBytes(const void* data, uint32_t num_bytes) { diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.cc b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.cc index b2fd80c9a72..8b4a27f0866 100644 --- a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.cc +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_base.cc @@ -7,7 +7,6 @@ #include "quic/platform/api/quic_export.h" #include "quic/platform/api/quic_flags.h" -#include "quic/platform/api/quic_ptr_util.h" #include "quic/platform/api/quic_server_stats.h" namespace quic { @@ -121,10 +120,10 @@ WriteResult QuicBatchWriterBase::InternalWritePacket( // Since buffered_writes has been emptied, this write must have been // buffered successfully. - QUIC_BUG_IF(!buffered) << "Failed to push to an empty batch buffer." - << " self_addr:" << self_address.ToString() - << ", peer_addr:" << peer_address.ToString() - << ", buf_len:" << buf_len; + QUIC_BUG_IF(quic_bug_10826_1, !buffered) + << "Failed to push to an empty batch buffer." + << " self_addr:" << self_address.ToString() + << ", peer_addr:" << peer_address.ToString() << ", buf_len:" << buf_len; } result.send_time_offset = release_time.release_time_offset; diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.cc b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.cc index 0de792dded8..8a450d78114 100644 --- a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.cc +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.cc @@ -75,11 +75,12 @@ QuicBatchWriterBuffer::PushResult QuicBatchWriterBuffer::PushBufferedWrite( } else if (IsInternalBuffer(buffer, buf_len)) { memmove(next_write_location, buffer, buf_len); } else { - QUIC_BUG << "Buffer[" << static_cast<const void*>(buffer) << ", " - << static_cast<const void*>(buffer + buf_len) - << ") overlaps with internal buffer[" - << static_cast<const void*>(buffer_) << ", " - << static_cast<const void*>(buffer_end()) << ")"; + QUIC_BUG(quic_bug_10831_1) + << "Buffer[" << static_cast<const void*>(buffer) << ", " + << static_cast<const void*>(buffer + buf_len) + << ") overlaps with internal buffer[" + << static_cast<const void*>(buffer_) << ", " + << static_cast<const void*>(buffer_end()) << ")"; return result; } result.buffer_copied = true; diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer.h b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer.h index 75683d17b6d..f87e9f8e78d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer.h +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_gso_batch_writer.h @@ -100,7 +100,7 @@ class QUIC_EXPORT_PRIVATE QuicGsoBatchWriter : public QuicUdpBatchWriter { batch_buffer().PopBufferedWrite(buffered_writes().size()); - QUIC_BUG_IF(!buffered_writes().empty()) + QUIC_BUG_IF(quic_bug_12544_1, !buffered_writes().empty()) << "All packets should have been written on a successful return"; return result; } diff --git a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_sendmmsg_batch_writer.cc b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_sendmmsg_batch_writer.cc index ec8e4ae55be..d5c03298607 100644 --- a/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_sendmmsg_batch_writer.cc +++ b/chromium/net/third_party/quiche/src/quic/core/batch_writer/quic_sendmmsg_batch_writer.cc @@ -55,7 +55,8 @@ QuicSendmmsgBatchWriter::InternalFlushImpl(size_t cmsg_space, QUICHE_DCHECK_EQ(0, num_packets_sent); break; } else if (num_packets_sent == 0) { - QUIC_BUG << "WriteMultiplePackets returned OK, but no packets were sent."; + QUIC_BUG(quic_bug_10825_1) + << "WriteMultiplePackets returned OK, but no packets were sent."; write_result = WriteResult(WRITE_STATUS_ERROR, EIO); break; } @@ -74,7 +75,7 @@ QuicSendmmsgBatchWriter::InternalFlushImpl(size_t cmsg_space, return result; } - QUIC_BUG_IF(!buffered_writes().empty()) + QUIC_BUG_IF(quic_bug_12537_1, !buffered_writes().empty()) << "All packets should have been written on a successful return"; write_result.bytes_written = result.bytes_written; return result; diff --git a/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.cc b/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.cc index 198d455b724..48fe9a82c05 100644 --- a/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.cc +++ b/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.cc @@ -15,6 +15,7 @@ #include "quic/core/crypto/quic_encrypter.h" #include "quic/core/frames/quic_ack_frequency_frame.h" #include "quic/core/quic_framer.h" +#include "quic/core/quic_types.h" #include "quic/core/quic_utils.h" #include "common/platform/api/quiche_text_utils.h" @@ -81,7 +82,8 @@ class ChloFramerVisitor : public QuicFramerVisitorInterface, bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) override; bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& farme) override; void OnPacketComplete() override {} - bool IsValidStatelessResetToken(QuicUint128 token) const override; + bool IsValidStatelessResetToken( + const StatelessResetToken& token) const override; void OnAuthenticatedIetfStatelessResetPacket( const QuicIetfStatelessResetPacket& /*packet*/) override {} void OnKeyUpdate(KeyUpdateReason /*reason*/) override; @@ -303,7 +305,7 @@ bool ChloFramerVisitor::OnAckFrequencyFrame( } bool ChloFramerVisitor::IsValidStatelessResetToken( - QuicUint128 /*token*/) const { + const StatelessResetToken& /*token*/) const { return false; } diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.cc index babb6e35f0a..657cad1a46a 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 @@ -171,42 +171,43 @@ void BandwidthSampler::OnPacketSent( if (unacked_packet_map_ != nullptr && !unacked_packet_map_->empty()) { QuicPacketNumber maybe_least_unacked = unacked_packet_map_->GetLeastUnacked(); - QUIC_BUG << "BandwidthSampler in-flight packet map has exceeded maximum " - "number of tracked packets(" - << max_tracked_packets_ - << "). First tracked: " << connection_state_map_.first_packet() - << "; last tracked: " << connection_state_map_.last_packet() - << "; entry_slots_used: " - << connection_state_map_.entry_slots_used() - << "; number_of_present_entries: " - << connection_state_map_.number_of_present_entries() - << "; packet number: " << packet_number - << "; unacked_map: " << unacked_packet_map_->DebugString() - << "; total_bytes_sent: " << total_bytes_sent_ - << "; total_bytes_acked: " << total_bytes_acked_ - << "; total_bytes_lost: " << total_bytes_lost_ - << "; total_bytes_neutered: " << total_bytes_neutered_ - << "; last_acked_packet_sent_time: " - << last_acked_packet_sent_time_ - << "; total_bytes_sent_at_last_acked_packet: " - << total_bytes_sent_at_last_acked_packet_ - << "; least_unacked_packet_info: " - << (unacked_packet_map_->IsUnacked(maybe_least_unacked) - ? unacked_packet_map_ - ->GetTransmissionInfo(maybe_least_unacked) - .DebugString() - : "n/a"); + QUIC_BUG(quic_bug_10437_1) + << "BandwidthSampler in-flight packet map has exceeded maximum " + "number of tracked packets(" + << max_tracked_packets_ + << "). First tracked: " << connection_state_map_.first_packet() + << "; last tracked: " << connection_state_map_.last_packet() + << "; entry_slots_used: " << connection_state_map_.entry_slots_used() + << "; number_of_present_entries: " + << connection_state_map_.number_of_present_entries() + << "; packet number: " << packet_number + << "; unacked_map: " << unacked_packet_map_->DebugString() + << "; total_bytes_sent: " << total_bytes_sent_ + << "; total_bytes_acked: " << total_bytes_acked_ + << "; total_bytes_lost: " << total_bytes_lost_ + << "; total_bytes_neutered: " << total_bytes_neutered_ + << "; last_acked_packet_sent_time: " << last_acked_packet_sent_time_ + << "; total_bytes_sent_at_last_acked_packet: " + << total_bytes_sent_at_last_acked_packet_ + << "; least_unacked_packet_info: " + << (unacked_packet_map_->IsUnacked(maybe_least_unacked) + ? unacked_packet_map_ + ->GetTransmissionInfo(maybe_least_unacked) + .DebugString() + : "n/a"); } else { - QUIC_BUG << "BandwidthSampler in-flight packet map has exceeded maximum " - "number of tracked packets."; + QUIC_BUG(quic_bug_10437_2) + << "BandwidthSampler in-flight packet map has exceeded maximum " + "number of tracked packets."; } } bool success = connection_state_map_.Emplace(packet_number, sent_time, bytes, bytes_in_flight + bytes, *this); - QUIC_BUG_IF(!success) << "BandwidthSampler failed to insert the packet " - "into the map, most likely because it's already " - "in it."; + QUIC_BUG_IF(quic_bug_10437_3, !success) + << "BandwidthSampler failed to insert the packet " + "into the map, most likely because it's already " + "in it."; } void BandwidthSampler::OnPacketNeutered(QuicPacketNumber packet_number) { @@ -355,7 +356,8 @@ BandwidthSample BandwidthSampler::OnPacketAcknowledgedInner( // current packet was sent. In that case, there is no bandwidth sample to // make. if (sent_packet.last_acked_packet_sent_time == QuicTime::Zero()) { - QUIC_BUG << "sent_packet.last_acked_packet_sent_time is zero"; + QUIC_BUG(quic_bug_10437_4) + << "sent_packet.last_acked_packet_sent_time is zero"; return BandwidthSample(); } @@ -427,8 +429,8 @@ BandwidthSample BandwidthSampler::OnPacketAcknowledgedInner( bool BandwidthSampler::ChooseA0Point(QuicByteCount total_bytes_acked, AckPoint* a0) { if (a0_candidates_.empty()) { - QUIC_BUG << "No A0 point candicates. total_bytes_acked:" - << total_bytes_acked; + QUIC_BUG(quic_bug_10437_5) + << "No A0 point candicates. total_bytes_acked:" << total_bytes_acked; return false; } 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 dfb834cd05b..d24e50efdbc 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 @@ -9,6 +9,7 @@ #include "quic/core/quic_time.h" #include "quic/core/quic_types.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 { @@ -188,7 +189,6 @@ void Bbr2NetworkModel::OnCongestionEventStart( void Bbr2NetworkModel::AdaptLowerBounds( const Bbr2CongestionEvent& congestion_event) { if (Params().bw_lo_mode_ != Bbr2Params::DEFAULT) { - QUICHE_DCHECK(Params().bw_startup); if (congestion_event.bytes_lost == 0) { return; } @@ -202,6 +202,10 @@ void Bbr2NetworkModel::AdaptLowerBounds( if (bandwidth_lo_.IsInfinite()) { bandwidth_lo_ = MaxBandwidth(); } + // Save bandwidth_lo_ if it hasn't already been saved. + if (prior_bandwidth_lo_.IsZero()) { + prior_bandwidth_lo_ = bandwidth_lo_; + } switch (Params().bw_lo_mode_) { case Bbr2Params::MIN_RTT_REDUCTION: bandwidth_lo_ = @@ -227,12 +231,13 @@ void Bbr2NetworkModel::AdaptLowerBounds( static_cast<double>(congestion_event.prior_cwnd)); break; case Bbr2Params::DEFAULT: - QUIC_BUG << "Unreachable case DEFAULT."; + QUIC_BUG(quic_bug_10466_1) << "Unreachable case DEFAULT."; } if (pacing_gain_ > Params().startup_full_bw_threshold) { - // In STARTUP, pacing_gain_ is applied to bandwidth_lo_, so this backs - // that multiplication out to allow the pacing rate to decrease, - // but not below bandwidth_latest_ * startup_full_bw_threshold. + // In STARTUP, pacing_gain_ is applied to bandwidth_lo_ in + // UpdatePacingRate, so this backs that multiplication out to allow the + // pacing rate to decrease, but not below + // bandwidth_latest_ * startup_full_bw_threshold. bandwidth_lo_ = std::max(bandwidth_lo_, bandwidth_latest_ * @@ -241,6 +246,15 @@ void Bbr2NetworkModel::AdaptLowerBounds( // Ensure bandwidth_lo isn't lower than bandwidth_latest_. bandwidth_lo_ = std::max(bandwidth_lo_, bandwidth_latest_); } + // If it's the end of the round, ensure bandwidth_lo doesn't decrease more + // than beta. + if (GetQuicReloadableFlag(quic_bbr2_fix_bw_lo_mode) && + congestion_event.end_of_round_trip) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_fix_bw_lo_mode, 2, 2); + bandwidth_lo_ = + std::max(bandwidth_lo_, prior_bandwidth_lo_ * (1.0 - Params().beta)); + prior_bandwidth_lo_ = QuicBandwidth::Zero(); + } // This early return ignores inflight_lo as well. return; } @@ -273,13 +287,7 @@ void Bbr2NetworkModel::OnCongestionEventFinish( QuicPacketNumber least_unacked_packet, const Bbr2CongestionEvent& congestion_event) { if (congestion_event.end_of_round_trip) { - if (!reset_max_bytes_delivered_) { - bytes_lost_in_round_ = 0; - loss_events_in_round_ = 0; - } else { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_reset_max_bytes_delivered, 1, 2); - OnNewRound(); - } + OnNewRound(); } bandwidth_sampler_.RemoveObsoletePackets(least_unacked_packet); @@ -354,19 +362,11 @@ bool Bbr2NetworkModel::IsInflightTooHigh( } void Bbr2NetworkModel::RestartRoundEarly() { - if (!reset_max_bytes_delivered_) { - bytes_lost_in_round_ = 0; - loss_events_in_round_ = 0; - max_bytes_delivered_in_round_ = 0; - } else { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_reset_max_bytes_delivered, 2, 2); - OnNewRound(); - } + OnNewRound(); round_trip_counter_.RestartRound(); } void Bbr2NetworkModel::OnNewRound() { - QUICHE_DCHECK(reset_max_bytes_delivered_); bytes_lost_in_round_ = 0; loss_events_in_round_ = 0; max_bytes_delivered_in_round_ = 0; 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 8b53c1b741d..8f5d4185dfc 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 @@ -208,9 +208,6 @@ struct QUIC_EXPORT_PRIVATE Bbr2Params { // Set the pacing gain to 25% larger than the recent BW increase in STARTUP. bool decrease_startup_pacing_at_end_of_round = false; - - // Latch the flag for quic_bbr2_bw_startup. - const bool bw_startup = GetQuicReloadableFlag(quic_bbr2_bw_startup); }; class QUIC_EXPORT_PRIVATE RoundTripCounter { @@ -538,6 +535,9 @@ class QUIC_EXPORT_PRIVATE Bbr2NetworkModel { QuicBandwidth bandwidth_latest_ = QuicBandwidth::Zero(); // Max bandwidth of recent rounds. Updated once per round. QuicBandwidth bandwidth_lo_ = bandwidth_lo_default(); + // bandwidth_lo_ at the beginning of a round with loss. Only used when the + // bw_lo_mode is non-default. + QuicBandwidth prior_bandwidth_lo_ = QuicBandwidth::Zero(); // Max inflight in the current round. Updated once per congestion event. QuicByteCount inflight_latest_ = 0; @@ -552,8 +552,6 @@ class QUIC_EXPORT_PRIVATE Bbr2NetworkModel { bool full_bandwidth_reached_ = false; QuicBandwidth full_bandwidth_baseline_ = QuicBandwidth::Zero(); QuicRoundTripCount rounds_without_bandwidth_growth_ = 0; - const bool reset_max_bytes_delivered_ = - GetQuicReloadableFlag(quic_bbr2_reset_max_bytes_delivered); }; enum class Bbr2Mode : uint8_t { 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 78bd06d4b24..9045d7e034b 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 @@ -205,10 +205,10 @@ Bbr2ProbeBwMode::AdaptUpperBoundsResult Bbr2ProbeBwMode::MaybeAdaptUpperBounds( model_->total_bytes_acked() - congestion_event.last_packet_send_state.total_bytes_acked; } else { - QUIC_BUG << "Total_bytes_acked(" << model_->total_bytes_acked() - << ") < send_state.total_bytes_acked(" - << congestion_event.last_packet_send_state.total_bytes_acked - << ")"; + QUIC_BUG(quic_bug_10436_1) + << "Total_bytes_acked(" << model_->total_bytes_acked() + << ") < send_state.total_bytes_acked(" + << congestion_event.last_packet_send_state.total_bytes_acked << ")"; } } if (model_->IsInflightTooHigh(congestion_event, @@ -394,8 +394,9 @@ void Bbr2ProbeBwMode::ProbeInflightHighUpward( model_->set_inflight_hi(new_inflight_hi); } else { - QUIC_BUG << "Not growing inflight_hi due to wrap around. Old value:" - << model_->inflight_hi() << ", new value:" << new_inflight_hi; + QUIC_BUG(quic_bug_10436_2) + << "Not growing inflight_hi due to wrap around. Old value:" + << model_->inflight_hi() << ", new value:" << new_inflight_hi; } } 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 a29d7aea3c2..d0f61277283 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 @@ -105,19 +105,12 @@ void Bbr2Sender::SetFromConfig(const QuicConfig& config, config.HasClientRequestedIndependentOption(kB2CL, perspective)) { params_.avoid_too_low_probe_bw_cwnd = false; } - if (GetQuicReloadableFlag(quic_bbr2_fewer_startup_round_trips) && - config.HasClientRequestedIndependentOption(k1RTT, perspective)) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_fewer_startup_round_trips, 1, 2); + if (config.HasClientRequestedIndependentOption(k1RTT, perspective)) { params_.startup_full_bw_rounds = 1; } - if (GetQuicReloadableFlag(quic_bbr2_fewer_startup_round_trips) && - config.HasClientRequestedIndependentOption(k2RTT, perspective)) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_fewer_startup_round_trips, 2, 2); + if (config.HasClientRequestedIndependentOption(k2RTT, perspective)) { params_.startup_full_bw_rounds = 2; } - if (config.HasClientRequestedIndependentOption(kB2LO, perspective)) { - params_.ignore_inflight_lo = true; - } if (config.HasClientRequestedIndependentOption(kB2HR, perspective)) { params_.inflight_hi_headroom = 0.15; } @@ -133,9 +126,10 @@ void Bbr2Sender::ApplyConnectionOptions( if (ContainsQuicTag(connection_options, kBBQ2)) { params_.startup_cwnd_gain = 2.885; params_.drain_cwnd_gain = 2.885; - if (params_.bw_startup) { - model_.set_cwnd_gain(params_.startup_cwnd_gain); - } + model_.set_cwnd_gain(params_.startup_cwnd_gain); + } + if (ContainsQuicTag(connection_options, kB2LO)) { + params_.ignore_inflight_lo = true; } if (ContainsQuicTag(connection_options, kB2NE)) { params_.always_exit_startup_on_excess_loss = false; @@ -146,9 +140,7 @@ void Bbr2Sender::ApplyConnectionOptions( if (ContainsQuicTag(connection_options, kB2H2)) { params_.limit_inflight_hi_by_max_delivered = true; } - if (GetQuicReloadableFlag(quic_bbr2_use_bytes_delivered) && - ContainsQuicTag(connection_options, kB2DL)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_use_bytes_delivered); + if (ContainsQuicTag(connection_options, kB2DL)) { params_.use_bytes_delivered_for_inflight_hi = true; } if (ContainsQuicTag(connection_options, kB2RC)) { @@ -157,20 +149,16 @@ void Bbr2Sender::ApplyConnectionOptions( if (ContainsQuicTag(connection_options, kBSAO)) { model_.EnableOverestimateAvoidance(); } - if (params_.bw_startup && ContainsQuicTag(connection_options, kBBQ6)) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_bw_startup, 1, 4); + if (ContainsQuicTag(connection_options, kBBQ6)) { params_.decrease_startup_pacing_at_end_of_round = true; } - if (params_.bw_startup && ContainsQuicTag(connection_options, kBBQ7)) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_bw_startup, 2, 4); + if (ContainsQuicTag(connection_options, kBBQ7)) { params_.bw_lo_mode_ = Bbr2Params::QuicBandwidthLoMode::MIN_RTT_REDUCTION; } - if (params_.bw_startup && ContainsQuicTag(connection_options, kBBQ8)) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_bw_startup, 3, 4); + if (ContainsQuicTag(connection_options, kBBQ8)) { params_.bw_lo_mode_ = Bbr2Params::QuicBandwidthLoMode::INFLIGHT_REDUCTION; } - if (params_.bw_startup && ContainsQuicTag(connection_options, kBBQ9)) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_bw_startup, 4, 4); + if (ContainsQuicTag(connection_options, kBBQ9)) { params_.bw_lo_mode_ = Bbr2Params::QuicBandwidthLoMode::CWND_REDUCTION; } } @@ -248,6 +236,16 @@ void Bbr2Sender::OnCongestionEvent(bool /*rtt_updated*/, model_.OnCongestionEventStart(event_time, acked_packets, lost_packets, &congestion_event); + if (InSlowStart()) { + if (!lost_packets.empty()) { + connection_stats_->slowstart_packets_lost += lost_packets.size(); + connection_stats_->slowstart_bytes_lost += congestion_event.bytes_lost; + } + if (congestion_event.end_of_round_trip) { + ++connection_stats_->slowstart_num_rtts; + } + } + // Number of mode changes allowed for this congestion event. int mode_changes_allowed = kMaxModeChangesPerCongestionEvent; while (true) { @@ -266,16 +264,19 @@ void Bbr2Sender::OnCongestionEvent(bool /*rtt_updated*/, BBR2_MODE_DISPATCH(Enter(event_time, &congestion_event)); --mode_changes_allowed; if (mode_changes_allowed < 0) { - QUIC_BUG << "Exceeded max number of mode changes per congestion event."; + QUIC_BUG(quic_bug_10443_1) + << "Exceeded max number of mode changes per congestion event."; break; } } UpdatePacingRate(congestion_event.bytes_acked); - QUIC_BUG_IF(pacing_rate_.IsZero()) << "Pacing rate must not be zero!"; + QUIC_BUG_IF(quic_bug_10443_2, pacing_rate_.IsZero()) + << "Pacing rate must not be zero!"; UpdateCongestionWindow(congestion_event.bytes_acked); - QUIC_BUG_IF(cwnd_ == 0u) << "Congestion window must not be zero!"; + QUIC_BUG_IF(quic_bug_10443_3, cwnd_ == 0u) + << "Congestion window must not be zero!"; model_.OnCongestionEventFinish(unacked_packets_->GetLeastUnacked(), congestion_event); @@ -320,9 +321,17 @@ void Bbr2Sender::UpdatePacingRate(QuicByteCount bytes_acked) { } QuicBandwidth target_rate = model_.pacing_gain() * model_.BandwidthEstimate(); - if (model_.full_bandwidth_reached() || - params_.decrease_startup_pacing_at_end_of_round || - params_.bw_lo_mode_ != Bbr2Params::DEFAULT) { + if (model_.full_bandwidth_reached()) { + pacing_rate_ = target_rate; + return; + } + if (params_.decrease_startup_pacing_at_end_of_round && + model_.pacing_gain() < Params().startup_pacing_gain) { + pacing_rate_ = target_rate; + return; + } + if (params_.bw_lo_mode_ != Bbr2Params::DEFAULT && + model_.loss_events_in_round() > 0) { pacing_rate_ = target_rate; return; } @@ -378,6 +387,10 @@ void Bbr2Sender::OnPacketSent(QuicTime sent_time, << ", total_acked:" << model_.total_bytes_acked() << ", total_lost:" << model_.total_bytes_lost() << " @ " << sent_time; + if (InSlowStart()) { + ++connection_stats_->slowstart_packets_sent; + connection_stats_->slowstart_bytes_sent += bytes; + } if (bytes_in_flight == 0 && params().avoid_unnecessary_probe_rtt) { OnExitQuiescence(sent_time); } 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 9cd5966e91f..e26760e0bf9 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 @@ -122,9 +122,6 @@ class DefaultTopologyParams { class Bbr2SimulatorTest : public QuicTest { protected: Bbr2SimulatorTest() : simulator_(&random_) { - // Enable this for all tests because it moves where cwnd and pacing gain - // are initialized. - SetQuicReloadableFlag(quic_bbr2_bw_startup, true); // Prevent the server(receiver), which only sends acks, from closing // connection due to too many outstanding packets. SetQuicFlag(FLAGS_quic_max_tracked_packet_count, 1000000); @@ -596,7 +593,6 @@ TEST_F(Bbr2DefaultTopologyTest, PacketLossOnSmallBufferStartup) { // Test the number of losses decreases with packet-conservation pacing. TEST_F(Bbr2DefaultTopologyTest, PacketLossBBQ6SmallBufferStartup) { - SetQuicReloadableFlag(quic_bbr2_bw_startup, true); SetConnectionOption(kBBQ2); // Increase CWND gain. SetConnectionOption(kBBQ6); DefaultTopologyParams params; @@ -612,7 +608,6 @@ TEST_F(Bbr2DefaultTopologyTest, PacketLossBBQ6SmallBufferStartup) { // Test the number of losses decreases with min_rtt packet-conservation pacing. TEST_F(Bbr2DefaultTopologyTest, PacketLossBBQ7SmallBufferStartup) { - SetQuicReloadableFlag(quic_bbr2_bw_startup, true); SetConnectionOption(kBBQ2); // Increase CWND gain. SetConnectionOption(kBBQ7); DefaultTopologyParams params; @@ -628,7 +623,6 @@ TEST_F(Bbr2DefaultTopologyTest, PacketLossBBQ7SmallBufferStartup) { // Test the number of losses decreases with Inflight packet-conservation pacing. TEST_F(Bbr2DefaultTopologyTest, PacketLossBBQ8SmallBufferStartup) { - SetQuicReloadableFlag(quic_bbr2_bw_startup, true); SetConnectionOption(kBBQ2); // Increase CWND gain. SetConnectionOption(kBBQ8); DefaultTopologyParams params; @@ -644,7 +638,6 @@ TEST_F(Bbr2DefaultTopologyTest, PacketLossBBQ8SmallBufferStartup) { // Test the number of losses decreases with CWND packet-conservation pacing. TEST_F(Bbr2DefaultTopologyTest, PacketLossBBQ9SmallBufferStartup) { - SetQuicReloadableFlag(quic_bbr2_bw_startup, true); SetConnectionOption(kBBQ2); // Increase CWND gain. SetConnectionOption(kBBQ9); DefaultTopologyParams params; 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 0f6b80ff233..5a9a532e688 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 @@ -23,16 +23,14 @@ Bbr2StartupMode::Bbr2StartupMode(const Bbr2Sender* sender, sender_->connection_stats_->slowstart_count = 1; sender_->connection_stats_->slowstart_duration = QuicTimeAccumulator(); sender_->connection_stats_->slowstart_duration.Start(now); - if (sender->Params().bw_startup) { - // 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); - } + // 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); } void Bbr2StartupMode::Enter(QuicTime /*now*/, const Bbr2CongestionEvent* /*congestion_event*/) { - QUIC_BUG << "Bbr2StartupMode::Enter should not be called"; + QUIC_BUG(quic_bug_10463_1) << "Bbr2StartupMode::Enter should not be called"; } void Bbr2StartupMode::Leave(QuicTime now, @@ -63,7 +61,6 @@ Bbr2Mode Bbr2StartupMode::OnCongestionEvent( if (Params().decrease_startup_pacing_at_end_of_round) { QUICHE_DCHECK_GT(model_->pacing_gain(), 0); - QUICHE_DCHECK(Params().bw_startup); if (congestion_event.end_of_round_trip && !congestion_event.last_sample_is_app_limited) { // Multiply by startup_pacing_gain, so if the bandwidth doubles, @@ -92,10 +89,6 @@ Bbr2Mode Bbr2StartupMode::OnCongestionEvent( } max_bw_at_round_beginning_ = model_->MaxBandwidth(); } - } else if (!Params().bw_startup) { - // When the flag is enabled, set these in the constructor. - model_->set_pacing_gain(Params().startup_pacing_gain); - model_->set_cwnd_gain(Params().startup_cwnd_gain); } // TODO(wub): Maybe implement STARTUP => PROBE_RTT. diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.cc index 521aa4a7a10..f9b7ac42c09 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.cc @@ -69,9 +69,9 @@ LossDetectionInterface::DetectionStats GeneralLossAlgorithm::DetectLosses( auto it = unacked_packets.begin(); if (least_in_flight_.IsInitialized() && least_in_flight_ >= packet_number) { if (least_in_flight_ > unacked_packets.largest_sent_packet() + 1) { - QUIC_BUG << "least_in_flight: " << least_in_flight_ - << " is greater than largest_sent_packet + 1: " - << unacked_packets.largest_sent_packet() + 1; + QUIC_BUG(quic_bug_10430_1) << "least_in_flight: " << least_in_flight_ + << " is greater than largest_sent_packet + 1: " + << unacked_packets.largest_sent_packet() + 1; } else { it += (least_in_flight_ - packet_number); packet_number = least_in_flight_; @@ -181,7 +181,7 @@ void GeneralLossAlgorithm::Initialize(PacketNumberSpace packet_number_space, LossDetectionInterface* parent) { parent_ = parent; if (packet_number_space_ < NUM_PACKET_NUMBER_SPACES) { - QUIC_BUG << "Cannot switch packet_number_space"; + QUIC_BUG(quic_bug_10430_2) << "Cannot switch packet_number_space"; return; } diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h index 1911e385348..0047b0f0448 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h @@ -79,7 +79,7 @@ class QUIC_EXPORT_PRIVATE RttStats { // Sets an initial RTT to be used for SmoothedRtt before any RTT updates. void set_initial_rtt(QuicTime::Delta initial_rtt) { if (initial_rtt.ToMicroseconds() <= 0) { - QUIC_BUG << "Attempt to set initial rtt to <= 0."; + QUIC_BUG(quic_bug_10453_1) << "Attempt to set initial rtt to <= 0."; return; } initial_rtt_ = initial_rtt; diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.cc index 77691fdcf09..562da98c414 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.cc @@ -361,7 +361,8 @@ void TcpCubicSenderBytes::MaybeIncreaseCwnd( QuicByteCount acked_bytes, QuicByteCount prior_in_flight, QuicTime event_time) { - QUIC_BUG_IF(InRecovery()) << "Never increase the CWND during recovery."; + QUIC_BUG_IF(quic_bug_10439_1, InRecovery()) + << "Never increase the CWND during recovery."; // Do not increase the congestion window unless the sender is close to using // the current window. if (!IsCwndLimited(prior_in_flight)) { diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.cc index 3668487eac9..2bf8d864b00 100644 --- a/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.cc +++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.cc @@ -93,7 +93,8 @@ void UberLossAlgorithm::SpuriousLossDetected( void UberLossAlgorithm::SetLossDetectionTuner( std::unique_ptr<LossDetectionTunerInterface> tuner) { if (tuner_ != nullptr) { - QUIC_BUG << "LossDetectionTuner can only be set once when session begins."; + QUIC_BUG(quic_bug_10469_1) + << "LossDetectionTuner can only be set once when session begins."; return; } tuner_ = std::move(tuner); @@ -119,7 +120,8 @@ void UberLossAlgorithm::MaybeStartTuning() { SetReorderingShift(*tuned_parameters_.reordering_shift); SetReorderingThreshold(*tuned_parameters_.reordering_threshold); } else { - QUIC_BUG << "Tuner started but some parameters are missing"; + QUIC_BUG(quic_bug_10469_2) + << "Tuner started but some parameters are missing"; } } @@ -204,7 +206,7 @@ void UberLossAlgorithm::DisablePacketThresholdForRuntPackets() { void UberLossAlgorithm::ResetLossDetection(PacketNumberSpace space) { if (space >= NUM_PACKET_NUMBER_SPACES) { - QUIC_BUG << "Invalid packet number space: " << space; + QUIC_BUG(quic_bug_10469_3) << "Invalid packet number space: " << space; return; } general_loss_algorithms_[space].Reset(); diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.cc index 32a2ebfc0e0..69724ba88fc 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.cc @@ -88,7 +88,8 @@ bool AeadBaseDecrypter::SetKey(absl::string_view key) { bool AeadBaseDecrypter::SetNoncePrefix(absl::string_view nonce_prefix) { if (use_ietf_nonce_construction_) { - QUIC_BUG << "Attempted to set nonce prefix on IETF QUIC crypter"; + QUIC_BUG(quic_bug_10709_1) + << "Attempted to set nonce prefix on IETF QUIC crypter"; return false; } QUICHE_DCHECK_EQ(nonce_prefix.size(), nonce_size_ - sizeof(QuicPacketNumber)); @@ -101,7 +102,7 @@ bool AeadBaseDecrypter::SetNoncePrefix(absl::string_view nonce_prefix) { bool AeadBaseDecrypter::SetIV(absl::string_view iv) { if (!use_ietf_nonce_construction_) { - QUIC_BUG << "Attempted to set IV on Google QUIC crypter"; + QUIC_BUG(quic_bug_10709_2) << "Attempted to set IV on Google QUIC crypter"; return false; } QUICHE_DCHECK_EQ(iv.size(), nonce_size_); @@ -158,7 +159,8 @@ bool AeadBaseDecrypter::DecryptPacket(uint64_t packet_number, } if (have_preliminary_key_) { - QUIC_BUG << "Unable to decrypt while key diversification is pending"; + QUIC_BUG(quic_bug_10709_3) + << "Unable to decrypt while key diversification is pending"; return false; } diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.cc index a7100c5fa24..f4878e5a6d0 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.cc @@ -78,7 +78,8 @@ bool AeadBaseEncrypter::SetKey(absl::string_view key) { bool AeadBaseEncrypter::SetNoncePrefix(absl::string_view nonce_prefix) { if (use_ietf_nonce_construction_) { - QUIC_BUG << "Attempted to set nonce prefix on IETF QUIC crypter"; + QUIC_BUG(quic_bug_10634_1) + << "Attempted to set nonce prefix on IETF QUIC crypter"; return false; } QUICHE_DCHECK_EQ(nonce_prefix.size(), nonce_size_ - sizeof(QuicPacketNumber)); @@ -91,7 +92,7 @@ bool AeadBaseEncrypter::SetNoncePrefix(absl::string_view nonce_prefix) { bool AeadBaseEncrypter::SetIV(absl::string_view iv) { if (!use_ietf_nonce_construction_) { - QUIC_BUG << "Attempted to set IV on Google QUIC crypter"; + QUIC_BUG(quic_bug_10634_2) << "Attempted to set IV on Google QUIC crypter"; return false; } QUICHE_DCHECK_EQ(iv.size(), nonce_size_); diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.cc index 95cd509267a..fd21c7342bf 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.cc @@ -12,12 +12,12 @@ namespace quic { bool AesBaseDecrypter::SetHeaderProtectionKey(absl::string_view key) { if (key.size() != GetKeySize()) { - QUIC_BUG << "Invalid key size for header protection"; + QUIC_BUG(quic_bug_10649_1) << "Invalid key size for header protection"; return false; } if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(key.data()), key.size() * 8, &pne_key_) != 0) { - QUIC_BUG << "Unexpected failure of AES_set_encrypt_key"; + QUIC_BUG(quic_bug_10649_2) << "Unexpected failure of AES_set_encrypt_key"; return false; } return true; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.cc index 663056dd41e..fb25a334e47 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.cc @@ -12,12 +12,13 @@ namespace quic { bool AesBaseEncrypter::SetHeaderProtectionKey(absl::string_view key) { if (key.size() != GetKeySize()) { - QUIC_BUG << "Invalid key size for header protection: " << key.size(); + QUIC_BUG(quic_bug_10726_1) + << "Invalid key size for header protection: " << key.size(); return false; } if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(key.data()), key.size() * 8, &pne_key_) != 0) { - QUIC_BUG << "Unexpected failure of AES_set_encrypt_key"; + QUIC_BUG(quic_bug_10726_2) << "Unexpected failure of AES_set_encrypt_key"; return false; } return true; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/certificate_view.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/certificate_view.cc index 7a20602337d..c21dd202b2d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/certificate_view.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/certificate_view.cc @@ -495,8 +495,9 @@ bool CertificateView::VerifySignature(absl::string_view data, uint16_t signature_algorithm) const { if (PublicKeyTypeFromSignatureAlgorithm(signature_algorithm) != PublicKeyTypeFromKey(public_key_.get())) { - QUIC_BUG << "Mismatch between the requested signature algorithm and the " - "type of the public key."; + QUIC_BUG(quic_bug_10640_1) + << "Mismatch between the requested signature algorithm and the " + "type of the public key."; return false; } @@ -587,8 +588,9 @@ skip: std::string CertificatePrivateKey::Sign(absl::string_view input, uint16_t signature_algorithm) { if (!ValidForSignatureAlgorithm(signature_algorithm)) { - QUIC_BUG << "Mismatch between the requested signature algorithm and the " - "type of the private key."; + QUIC_BUG(quic_bug_10640_2) + << "Mismatch between the requested signature algorithm and the " + "type of the private key."; return ""; } diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.cc index 1b8af25c451..b1be3d6155f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.cc @@ -17,7 +17,7 @@ namespace quic { bool ChaChaBaseDecrypter::SetHeaderProtectionKey(absl::string_view key) { if (key.size() != GetKeySize()) { - QUIC_BUG << "Invalid key size for header protection"; + QUIC_BUG(quic_bug_10620_1) << "Invalid key size for header protection"; return false; } memcpy(pne_key_, key.data(), key.size()); diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.cc index f667f1cdc8a..1dfe8e6193f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.cc @@ -15,7 +15,7 @@ namespace quic { bool ChaChaBaseEncrypter::SetHeaderProtectionKey(absl::string_view key) { if (key.size() != GetKeySize()) { - QUIC_BUG << "Invalid key size for header protection"; + QUIC_BUG(quic_bug_10656_1) << "Invalid key size for header protection"; return false; } memcpy(pne_key_, key.data(), key.size()); diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.cc index 9628ed904fe..6dcb989b9e8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.cc @@ -8,6 +8,7 @@ #include <string> #include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" #include "quic/core/crypto/crypto_framer.h" @@ -16,7 +17,6 @@ #include "quic/core/quic_socket_address_coder.h" #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_map_util.h" -#include "common/platform/api/quiche_text_utils.h" #include "common/quiche_endian.h" namespace quic { @@ -221,9 +221,10 @@ QuicErrorCode CryptoHandshakeMessage::GetUint64(QuicTag tag, return GetPOD(tag, out, sizeof(uint64_t)); } -QuicErrorCode CryptoHandshakeMessage::GetUint128(QuicTag tag, - QuicUint128* out) const { - return GetPOD(tag, out, sizeof(QuicUint128)); +QuicErrorCode CryptoHandshakeMessage::GetStatelessResetToken( + QuicTag tag, + StatelessResetToken* out) const { + return GetPOD(tag, out, kStatelessResetTokenLength); } size_t CryptoHandshakeMessage::size() const { @@ -296,7 +297,7 @@ std::string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const { if (it->second.size() == 4) { uint32_t value; memcpy(&value, it->second.data(), sizeof(value)); - ret += quiche::QuicheTextUtils::Uint64ToString(value); + absl::StrAppend(&ret, value); done = true; } break; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h index 61fb75735c6..9550c153e49 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h @@ -13,8 +13,8 @@ #include "absl/strings/string_view.h" #include "quic/core/quic_packets.h" +#include "quic/core/quic_types.h" #include "quic/platform/api/quic_export.h" -#include "quic/platform/api/quic_uint128.h" namespace quic { @@ -110,7 +110,9 @@ class QUIC_EXPORT_PRIVATE CryptoHandshakeMessage { absl::string_view* out) const; QuicErrorCode GetUint32(QuicTag tag, uint32_t* out) const; QuicErrorCode GetUint64(QuicTag tag, uint64_t* out) const; - QuicErrorCode GetUint128(QuicTag tag, QuicUint128* out) const; + + QuicErrorCode GetStatelessResetToken(QuicTag tag, + StatelessResetToken* out) const; // size returns 4 (message tag) + 2 (uint16_t, number of entries) + // (4 (tag) + 4 (end offset))*tag_value_map_.size() + ∑ value sizes. 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 99f537213b7..e08d90b1c08 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 @@ -267,6 +267,9 @@ 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 +const QuicTag kRVCM = TAG('R', 'V', 'C', 'M'); // Validate the new address + // upon client address change. + // Optional support of truncated Connection IDs. If sent by a peer, the value // is the minimum number of bytes allowed for the connection ID sent to the // peer. diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc index 323aec10316..7414eda8692 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc @@ -12,6 +12,7 @@ #include "absl/base/macros.h" #include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "third_party/boringssl/src/include/openssl/sha.h" #include "quic/core/crypto/cert_compressor.h" @@ -57,24 +58,23 @@ const char kOldConfigId[] = "old-config-id"; } // namespace struct TestParams { - TestParams(ParsedQuicVersionVector supported_versions) - : supported_versions(std::move(supported_versions)) {} - friend std::ostream& operator<<(std::ostream& os, const TestParams& p) { os << " versions: " - << ParsedQuicVersionVectorToString(p.supported_versions) << " }"; + << ParsedQuicVersionVectorToString(p.supported_versions) + << " } allow_sni_without_dots: " << p.allow_sni_without_dots; return os; } // Versions supported by client and server. ParsedQuicVersionVector supported_versions; + bool allow_sni_without_dots; }; // Used by ::testing::PrintToStringParamName(). std::string PrintToString(const TestParams& p) { std::string rv = ParsedQuicVersionVectorToString(p.supported_versions); std::replace(rv.begin(), rv.end(), ',', '_'); - return rv; + return absl::StrCat(rv, "_allow_sni_without_dots_", p.allow_sni_without_dots); } // Constructs various test permutations. @@ -84,7 +84,9 @@ std::vector<TestParams> GetTestParams() { // Start with all versions, remove highest on each iteration. ParsedQuicVersionVector supported_versions = AllSupportedVersions(); while (!supported_versions.empty()) { - params.push_back(TestParams(supported_versions)); + for (bool allow_sni_without_dots : {false, true}) { + params.push_back({supported_versions, allow_sni_without_dots}); + } supported_versions.erase(supported_versions.begin()); } @@ -108,6 +110,8 @@ class CryptoServerTest : public QuicTestWithParam<TestParams> { signed_config_(new QuicSignedServerConfig), chlo_packet_size_(kDefaultMaxPacketSize) { supported_versions_ = GetParam().supported_versions; + SetQuicReloadableFlag(quic_and_tls_allow_sni_without_dots, + GetParam().allow_sni_without_dots); config_.set_enable_serving_sct(true); client_version_ = supported_versions_.front(); @@ -377,27 +381,34 @@ INSTANTIATE_TEST_SUITE_P(CryptoServerTests, TEST_P(CryptoServerTest, BadSNI) { // clang-format off - static const char* const kBadSNIs[] = { + std::vector<std::string> badSNIs = { "", - "foo", "#00", "#ff00", "127.0.0.1", "ffee::1", }; + if (!GetParam().allow_sni_without_dots) { + badSNIs.push_back("foo"); + } // clang-format on - for (size_t i = 0; i < ABSL_ARRAYSIZE(kBadSNIs); i++) { - CryptoHandshakeMessage msg = - crypto_test_utils::CreateCHLO({{"PDMD", "X509"}, - {"SNI", kBadSNIs[i]}, - {"VER\0", client_version_string_}}, - kClientHelloMinimumSize); + for (const std::string& bad_sni : badSNIs) { + CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO( + {{"PDMD", "X509"}, {"SNI", bad_sni}, {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); ShouldFailMentioning("SNI", msg); const HandshakeFailureReason kRejectReasons[] = { SERVER_CONFIG_INCHOATE_HELLO_FAILURE}; CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons)); } + + if (GetParam().allow_sni_without_dots) { + CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO( + {{"PDMD", "X509"}, {"SNI", "foo"}, {"VER\0", client_version_string_}}, + kClientHelloMinimumSize); + ShouldSucceed(msg); + } } TEST_P(CryptoServerTest, DefaultCert) { 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 a2e205bbbb2..2c8435035c9 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 @@ -176,7 +176,8 @@ const uint8_t* InitialSaltForVersion(const ParsedQuicVersion& version, *out_len = ABSL_ARRAYSIZE(kReservedForNegotiationSalt); return kReservedForNegotiationSalt; } - QUIC_BUG << "No initial obfuscation salt for version " << version; + QUIC_BUG(quic_bug_10699_1) + << "No initial obfuscation salt for version " << version; *out_len = ABSL_ARRAYSIZE(kReservedForNegotiationSalt); return kReservedForNegotiationSalt; } @@ -219,8 +220,9 @@ bool RetryIntegrityKeysForVersion(const ParsedQuicVersion& version, static_assert(SupportedVersions().size() == 6u, "Supported versions out of sync with retry integrity keys"); if (!version.UsesTls()) { - QUIC_BUG << "Attempted to get retry integrity keys for invalid version " - << version; + QUIC_BUG(quic_bug_10699_2) + << "Attempted to get retry integrity keys for invalid version " + << version; return false; } else if (version == ParsedQuicVersion::RFCv1()) { *key = absl::string_view( @@ -256,7 +258,8 @@ bool RetryIntegrityKeysForVersion(const ParsedQuicVersion& version, ABSL_ARRAYSIZE(kReservedForNegotiationRetryIntegrityNonce)); return true; } - QUIC_BUG << "Attempted to get retry integrity keys for version " << version; + QUIC_BUG(quic_bug_10699_3) + << "Attempted to get retry integrity keys for version " << version; return false; } @@ -277,8 +280,8 @@ void CryptoUtils::CreateInitialObfuscators(Perspective perspective, crypters->decrypter = std::make_unique<NullDecrypter>(perspective); return; } - QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion( - connection_id, version.transport_version)) + QUIC_BUG_IF(quic_bug_12871_1, !QuicUtils::IsConnectionIdValidForVersion( + connection_id, version.transport_version)) << "CreateTlsInitialCrypters: attempted to use connection ID " << connection_id << " which is invalid with version " << version; const EVP_MD* hash = EVP_sha256(); @@ -292,7 +295,7 @@ void CryptoUtils::CreateInitialObfuscators(Perspective perspective, HKDF_extract(handshake_secret.data(), &handshake_secret_len, hash, reinterpret_cast<const uint8_t*>(connection_id.data()), connection_id.length(), salt, salt_len); - QUIC_BUG_IF(!hkdf_extract_success) + QUIC_BUG_IF(quic_bug_12871_2, !hkdf_extract_success) << "HKDF_extract failed when creating initial crypters"; handshake_secret.resize(handshake_secret_len); @@ -325,18 +328,21 @@ bool CryptoUtils::ValidateRetryIntegrityTag( absl::string_view integrity_tag) { unsigned char computed_integrity_tag[kRetryIntegrityTagLength]; if (integrity_tag.length() != ABSL_ARRAYSIZE(computed_integrity_tag)) { - QUIC_BUG << "Invalid retry integrity tag length " << integrity_tag.length(); + QUIC_BUG(quic_bug_10699_4) + << "Invalid retry integrity tag length " << integrity_tag.length(); return false; } char retry_pseudo_packet[kMaxIncomingPacketSize + 256]; QuicDataWriter writer(ABSL_ARRAYSIZE(retry_pseudo_packet), retry_pseudo_packet); if (!writer.WriteLengthPrefixedConnectionId(original_connection_id)) { - QUIC_BUG << "Failed to write original connection ID in retry pseudo packet"; + QUIC_BUG(quic_bug_10699_5) + << "Failed to write original connection ID in retry pseudo packet"; return false; } if (!writer.WriteStringPiece(retry_without_tag)) { - QUIC_BUG << "Failed to write retry without tag in retry pseudo packet"; + QUIC_BUG(quic_bug_10699_6) + << "Failed to write retry without tag in retry pseudo packet"; return false; } absl::string_view key; @@ -351,7 +357,7 @@ bool CryptoUtils::ValidateRetryIntegrityTag( absl::string_view plaintext; // Plaintext is empty. if (!crypter.Encrypt(nonce, associated_data, plaintext, computed_integrity_tag)) { - QUIC_BUG << "Failed to compute retry integrity tag"; + QUIC_BUG(quic_bug_10699_7) << "Failed to compute retry integrity tag"; return false; } if (CRYPTO_memcmp(computed_integrity_tag, integrity_tag.data(), @@ -484,7 +490,8 @@ bool CryptoUtils::DeriveKeys(const ParsedQuicVersion& version, } case Diversification::PENDING: { if (perspective == Perspective::IS_SERVER) { - QUIC_BUG << "Pending diversification is only for clients."; + QUIC_BUG(quic_bug_10699_8) + << "Pending diversification is only for clients."; return false; } @@ -502,7 +509,8 @@ bool CryptoUtils::DeriveKeys(const ParsedQuicVersion& version, } case Diversification::NOW: { if (perspective == Perspective::IS_CLIENT) { - QUIC_BUG << "Immediate diversification is only for servers."; + QUIC_BUG(quic_bug_10699_9) + << "Immediate diversification is only for servers."; return false; } @@ -734,7 +742,8 @@ std::string CryptoUtils::EarlyDataReasonToString( RETURN_STRING_LITERAL(ssl_early_data_quic_parameter_mismatch); } #endif - QUIC_BUG_IF(reason < 0 || reason > ssl_early_data_reason_max_value) + QUIC_BUG_IF(quic_bug_12871_3, + reason < 0 || reason > ssl_early_data_reason_max_value) << "Unknown ssl_early_data_reason_t " << reason; return "unknown ssl_early_data_reason_t"; } diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.cc index aefbcf5817a..bdf3a081d02 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.cc @@ -8,11 +8,11 @@ #include <cstring> #include <string> +#include "absl/memory/memory.h" #include "absl/strings/string_view.h" #include "third_party/boringssl/src/include/openssl/curve25519.h" #include "quic/core/crypto/quic_random.h" #include "quic/platform/api/quic_bug_tracker.h" -#include "quic/platform/api/quic_ptr_util.h" namespace quic { @@ -25,7 +25,7 @@ std::unique_ptr<Curve25519KeyExchange> Curve25519KeyExchange::New( QuicRandom* rand) { std::unique_ptr<Curve25519KeyExchange> result = New(Curve25519KeyExchange::NewPrivateKey(rand)); - QUIC_BUG_IF(result == nullptr); + QUIC_BUG_IF(quic_bug_12891_1, result == nullptr); return result; } @@ -47,7 +47,9 @@ std::unique_ptr<Curve25519KeyExchange> Curve25519KeyExchange::New( return nullptr; } - auto ka = QuicWrapUnique(new Curve25519KeyExchange); + // Use absl::WrapUnique(new) instead of std::make_unique because + // Curve25519KeyExchange has a private constructor. + auto ka = absl::WrapUnique(new Curve25519KeyExchange); memcpy(ka->private_key_, private_key.data(), X25519_PRIVATE_KEY_LEN); X25519_public_from_private(ka->public_key_, ka->private_key_); return ka; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/key_exchange.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/key_exchange.cc index 411dd3b9bb5..c4e66c80e73 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/key_exchange.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/key_exchange.cc @@ -19,7 +19,8 @@ std::unique_ptr<SynchronousKeyExchange> CreateLocalSynchronousKeyExchange( case kP256: return P256KeyExchange::New(private_key); default: - QUIC_BUG << "Unknown key exchange method: " << QuicTagToString(type); + QUIC_BUG(quic_bug_10712_1) + << "Unknown key exchange method: " << QuicTagToString(type); return nullptr; } } @@ -35,7 +36,8 @@ std::unique_ptr<SynchronousKeyExchange> CreateLocalSynchronousKeyExchange( return P256KeyExchange::New(); break; default: - QUIC_BUG << "Unknown key exchange method: " << QuicTagToString(type); + QUIC_BUG(quic_bug_10712_2) + << "Unknown key exchange method: " << QuicTagToString(type); return nullptr; } } diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter.cc index 5e0c9fb9fc0..fd611bd3695 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter.cc @@ -6,11 +6,11 @@ #include <cstdint> +#include "absl/numeric/int128.h" #include "absl/strings/string_view.h" #include "quic/core/quic_data_reader.h" #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_bug_tracker.h" -#include "quic/platform/api/quic_uint128.h" #include "common/quiche_endian.h" namespace quic { @@ -35,13 +35,13 @@ bool NullDecrypter::SetHeaderProtectionKey(absl::string_view key) { } bool NullDecrypter::SetPreliminaryKey(absl::string_view /*key*/) { - QUIC_BUG << "Should not be called"; + QUIC_BUG(quic_bug_10652_1) << "Should not be called"; return false; } bool NullDecrypter::SetDiversificationNonce( const DiversificationNonce& /*nonce*/) { - QUIC_BUG << "Should not be called"; + QUIC_BUG(quic_bug_10652_2) << "Should not be called"; return true; } @@ -53,7 +53,7 @@ bool NullDecrypter::DecryptPacket(uint64_t /*packet_number*/, size_t max_output_length) { QuicDataReader reader(ciphertext.data(), ciphertext.length(), quiche::HOST_BYTE_ORDER); - QuicUint128 hash; + absl::uint128 hash; if (!ReadHash(&reader, &hash)) { return false; @@ -61,7 +61,8 @@ bool NullDecrypter::DecryptPacket(uint64_t /*packet_number*/, absl::string_view plaintext = reader.ReadRemainingPayload(); if (plaintext.length() > max_output_length) { - QUIC_BUG << "Output buffer must be larger than the plaintext."; + QUIC_BUG(quic_bug_10652_3) + << "Output buffer must be larger than the plaintext."; return false; } if (hash != ComputeHash(associated_data, plaintext)) { @@ -106,19 +107,19 @@ QuicPacketCount NullDecrypter::GetIntegrityLimit() const { return std::numeric_limits<QuicPacketCount>::max(); } -bool NullDecrypter::ReadHash(QuicDataReader* reader, QuicUint128* hash) { +bool NullDecrypter::ReadHash(QuicDataReader* reader, absl::uint128* hash) { uint64_t lo; uint32_t hi; if (!reader->ReadUInt64(&lo) || !reader->ReadUInt32(&hi)) { return false; } - *hash = MakeQuicUint128(hi, lo); + *hash = absl::MakeUint128(hi, lo); return true; } -QuicUint128 NullDecrypter::ComputeHash(const absl::string_view data1, - const absl::string_view data2) const { - QuicUint128 correct_hash; +absl::uint128 NullDecrypter::ComputeHash(const absl::string_view data1, + const absl::string_view data2) const { + absl::uint128 correct_hash; if (perspective_ == Perspective::IS_CLIENT) { // Peer is a server. correct_hash = QuicUtils::FNV1a_128_Hash_Three(data1, data2, "Server"); @@ -126,7 +127,7 @@ QuicUint128 NullDecrypter::ComputeHash(const absl::string_view data1, // Peer is a client. correct_hash = QuicUtils::FNV1a_128_Hash_Three(data1, data2, "Client"); } - QuicUint128 mask = MakeQuicUint128(UINT64_C(0x0), UINT64_C(0xffffffff)); + absl::uint128 mask = absl::MakeUint128(UINT64_C(0x0), UINT64_C(0xffffffff)); mask <<= 96; correct_hash &= ~mask; return correct_hash; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter.h index fd06d3a6ee8..b36fa82848b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter.h @@ -8,11 +8,11 @@ #include <cstddef> #include <cstdint> +#include "absl/numeric/int128.h" #include "absl/strings/string_view.h" #include "quic/core/crypto/quic_decrypter.h" #include "quic/core/quic_types.h" #include "quic/platform/api/quic_export.h" -#include "quic/platform/api/quic_uint128.h" namespace quic { @@ -53,9 +53,9 @@ class QUIC_EXPORT_PRIVATE NullDecrypter : public QuicDecrypter { QuicPacketCount GetIntegrityLimit() const override; private: - bool ReadHash(QuicDataReader* reader, QuicUint128* hash); - QuicUint128 ComputeHash(absl::string_view data1, - absl::string_view data2) const; + bool ReadHash(QuicDataReader* reader, absl::uint128* hash); + absl::uint128 ComputeHash(absl::string_view data1, + absl::string_view data2) const; Perspective perspective_; }; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/null_encrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/null_encrypter.cc index dd9cfd9eb95..a38532148b6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/null_encrypter.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/null_encrypter.cc @@ -4,6 +4,7 @@ #include "quic/core/crypto/null_encrypter.h" +#include "absl/numeric/int128.h" #include "absl/strings/string_view.h" #include "quic/core/quic_data_writer.h" #include "quic/core/quic_utils.h" @@ -41,7 +42,7 @@ bool NullEncrypter::EncryptPacket(uint64_t /*packet_number*/, if (max_output_length < len) { return false; } - QuicUint128 hash; + absl::uint128 hash; if (perspective_ == Perspective::IS_SERVER) { hash = QuicUtils::FNV1a_128_Hash_Three(associated_data, plaintext, "Server"); diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.cc index 0ce7a24fa8c..2a13c2e5a5c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.cc @@ -10,13 +10,13 @@ #include <string> #include <utility> +#include "absl/memory/memory.h" #include "absl/strings/string_view.h" #include "third_party/boringssl/src/include/openssl/ec.h" #include "third_party/boringssl/src/include/openssl/ecdh.h" #include "third_party/boringssl/src/include/openssl/err.h" #include "third_party/boringssl/src/include/openssl/evp.h" #include "quic/platform/api/quic_logging.h" -#include "quic/platform/api/quic_ptr_util.h" namespace quic { @@ -57,7 +57,7 @@ std::unique_ptr<P256KeyExchange> P256KeyExchange::New(absl::string_view key) { return nullptr; } - return QuicWrapUnique( + return absl::WrapUnique( new P256KeyExchange(std::move(private_key), public_key)); } 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 06c694000cf..d61df716c3a 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 @@ -88,18 +88,20 @@ bool ProofSourceX509::AddCertificateChain( QuicReferenceCountedPointer<Chain> chain, CertificatePrivateKey key) { if (chain->certs.empty()) { - QUIC_BUG << "Empty certificate chain supplied."; + QUIC_BUG(quic_bug_10644_1) << "Empty certificate chain supplied."; return false; } std::unique_ptr<CertificateView> leaf = CertificateView::ParseSingleCertificate(chain->certs[0]); if (leaf == nullptr) { - QUIC_BUG << "Unable to parse X.509 leaf certificate in the supplied chain."; + QUIC_BUG(quic_bug_10644_2) + << "Unable to parse X.509 leaf certificate in the supplied chain."; return false; } if (!key.MatchesPublicKey(*leaf)) { - QUIC_BUG << "Private key does not match the leaf certificate."; + QUIC_BUG(quic_bug_10644_3) + << "Private key does not match the leaf certificate."; return false; } diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache_test.cc index 5b94e54e08c..8af7f373ea7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache_test.cc @@ -6,10 +6,10 @@ #include <string> +#include "absl/strings/str_cat.h" #include "quic/core/crypto/cert_compressor.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/crypto_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { @@ -85,8 +85,7 @@ TEST_F(QuicCompressedCertsCacheTest, CacheMissDueToEviction) { for (unsigned int i = 0; i < QuicCompressedCertsCache::kQuicCompressedCertsCacheSize; i++) { EXPECT_EQ(certs_cache_.Size(), i + 1); - certs_cache_.Insert(chain, quiche::QuicheTextUtils::Uint64ToString(i), "", - quiche::QuicheTextUtils::Uint64ToString(i)); + certs_cache_.Insert(chain, absl::StrCat(i), "", absl::StrCat(i)); } EXPECT_EQ(certs_cache_.MaxSize(), certs_cache_.Size()); diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.cc index 45db92bd494..c886cbe989e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.cc @@ -9,6 +9,7 @@ #include <string> #include "absl/base/macros.h" +#include "absl/memory/memory.h" #include "absl/strings/match.h" #include "absl/strings/string_view.h" #include "third_party/boringssl/src/include/openssl/ssl.h" @@ -33,7 +34,6 @@ #include "quic/platform/api/quic_hostname_utils.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_map_util.h" -#include "quic/platform/api/quic_ptr_util.h" #include "common/platform/api/quiche_text_utils.h" namespace quic { @@ -134,15 +134,6 @@ QuicCryptoClientConfig::CachedState::GetServerConfig() const { return scfg_.get(); } -void QuicCryptoClientConfig::CachedState::add_server_nonce( - const std::string& server_nonce) { - server_nonces_.push(server_nonce); -} - -bool QuicCryptoClientConfig::CachedState::has_server_nonce() const { - return !server_nonces_.empty(); -} - QuicCryptoClientConfig::CachedState::ServerConfigState QuicCryptoClientConfig::CachedState::SetServerConfig( absl::string_view server_config, @@ -363,17 +354,6 @@ void QuicCryptoClientConfig::CachedState::InitializeFrom( ++generation_counter_; } -std::string QuicCryptoClientConfig::CachedState::GetNextServerNonce() { - if (server_nonces_.empty()) { - QUIC_BUG - << "Attempting to consume a server nonce that was never designated."; - return ""; - } - const std::string server_nonce = server_nonces_.front(); - server_nonces_.pop(); - return server_nonce; -} - void QuicCryptoClientConfig::SetDefaults() { // Key exchange methods. kexs = {kC255, kP256}; @@ -395,7 +375,7 @@ QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::LookupOrCreate( } CachedState* cached = new CachedState; - cached_states_.insert(std::make_pair(server_id, QuicWrapUnique(cached))); + cached_states_.insert(std::make_pair(server_id, absl::WrapUnique(cached))); bool cache_populated = PopulateFromCanonicalConfig(server_id, cached); QUIC_CLIENT_HISTOGRAM_BOOL( "QuicCryptoClientConfig.PopulatedFromCanonicalConfig", cache_populated, @@ -495,8 +475,9 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( CryptoHandshakeMessage* out, std::string* error_details) const { QUICHE_DCHECK(error_details != nullptr); - QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion( - connection_id, preferred_version.transport_version)) + QUIC_BUG_IF(quic_bug_12943_2, + !QuicUtils::IsConnectionIdValidForVersion( + connection_id, preferred_version.transport_version)) << "FillClientHello: attempted to use connection ID " << connection_id << " which is invalid with version " << preferred_version; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h index b02f621b97c..bb0626094db 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h @@ -175,18 +175,6 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { void set_cert_sct(absl::string_view cert_sct); - // Adds the servernonce to the queue of server nonces. - void add_server_nonce(const std::string& server_nonce); - - // If true, the crypto config contains at least one server nonce, and the - // client should use one of these nonces. - bool has_server_nonce() const; - - // This function should only be called when has_server_nonce is true. - // Returns the next server_nonce specified by the server and removes it - // from the queue of nonces. - std::string GetNextServerNonce(); - // SetProofVerifyDetails takes ownership of |details|. void SetProofVerifyDetails(ProofVerifyDetails* details); @@ -227,8 +215,6 @@ class QUIC_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { // scfg contains the cached, parsed value of |server_config|. mutable std::unique_ptr<CryptoHandshakeMessage> scfg_; - - QuicQueue<std::string> server_nonces_; }; // Used to filter server ids for partial config deletion. diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config_test.cc index 536083fb70d..6dd09677fb9 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config_test.cc @@ -81,45 +81,6 @@ TEST_F(QuicCryptoClientConfigTest, CachedState_SetProofVerifyDetails) { EXPECT_EQ(details, state.proof_verify_details()); } -TEST_F(QuicCryptoClientConfigTest, CachedState_ServerNonce) { - QuicCryptoClientConfig::CachedState state; - EXPECT_FALSE(state.has_server_nonce()); - - std::string server_nonce = "nonce_1"; - state.add_server_nonce(server_nonce); - EXPECT_TRUE(state.has_server_nonce()); - EXPECT_EQ(server_nonce, state.GetNextServerNonce()); - EXPECT_FALSE(state.has_server_nonce()); - - // Allow the ID to be set multiple times. It's unusual that this would - // happen, but not impossible. - server_nonce = "nonce_2"; - state.add_server_nonce(server_nonce); - EXPECT_TRUE(state.has_server_nonce()); - EXPECT_EQ(server_nonce, state.GetNextServerNonce()); - server_nonce = "nonce_3"; - state.add_server_nonce(server_nonce); - EXPECT_EQ(server_nonce, state.GetNextServerNonce()); - EXPECT_FALSE(state.has_server_nonce()); - - // Test FIFO behavior. - const std::string first_nonce = "first_nonce"; - const std::string second_nonce = "second_nonce"; - state.add_server_nonce(first_nonce); - state.add_server_nonce(second_nonce); - EXPECT_TRUE(state.has_server_nonce()); - EXPECT_EQ(first_nonce, state.GetNextServerNonce()); - EXPECT_EQ(second_nonce, state.GetNextServerNonce()); -} - -TEST_F(QuicCryptoClientConfigTest, CachedState_ServerNonceConsumedBeforeSet) { - QuicCryptoClientConfig::CachedState state; - EXPECT_FALSE(state.has_server_nonce()); - EXPECT_QUIC_BUG(state.GetNextServerNonce(), - "Attempting to consume a server nonce " - "that was never designated."); -} - TEST_F(QuicCryptoClientConfigTest, CachedState_InitializeFrom) { QuicCryptoClientConfig::CachedState state; QuicCryptoClientConfig::CachedState other; @@ -130,7 +91,6 @@ TEST_F(QuicCryptoClientConfigTest, CachedState_InitializeFrom) { EXPECT_EQ(state.source_address_token(), other.source_address_token()); EXPECT_EQ(state.certs(), other.certs()); EXPECT_EQ(1u, other.generation_counter()); - EXPECT_FALSE(state.has_server_nonce()); } TEST_F(QuicCryptoClientConfigTest, InchoateChlo) { @@ -492,7 +452,6 @@ TEST_F(QuicCryptoClientConfigTest, ProcessReject) { AllSupportedVersionsWithQuicCrypto().front().transport_version, "", &cached, out_params, &error), IsQuicNoError()); - EXPECT_FALSE(cached.has_server_nonce()); } TEST_F(QuicCryptoClientConfigTest, ProcessRejectWithLongTTL) { 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 da04f4f1ca1..4f19533b008 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 @@ -140,7 +140,7 @@ class ValidateClientHelloHelper { delete; ~ValidateClientHelloHelper() { - QUIC_BUG_IF(done_cb_ != nullptr) + QUIC_BUG_IF(quic_bug_12963_1, done_cb_ != nullptr) << "Deleting ValidateClientHelloHelper with a pending callback."; } @@ -155,7 +155,8 @@ class ValidateClientHelloHelper { } void DetachCallback() { - QUIC_BUG_IF(done_cb_ == nullptr) << "Callback already detached."; + QUIC_BUG_IF(quic_bug_10630_1, done_cb_ == nullptr) + << "Callback already detached."; done_cb_ = nullptr; } @@ -737,12 +738,29 @@ void QuicCryptoServerConfig::ProcessClientHelloAfterGetProof( std::unique_ptr<ProofSource::Details> proof_source_details, std::unique_ptr<ProcessClientHelloContext> context, const Configs& configs) const { - QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion( - context->connection_id(), context->transport_version())) + QUIC_BUG_IF(quic_bug_12963_2, + !QuicUtils::IsConnectionIdValidForVersion( + context->connection_id(), context->transport_version())) << "ProcessClientHelloAfterGetProof: attempted to use connection ID " << context->connection_id() << " which is invalid with version " << context->version(); + if (context->validate_chlo_result()->postpone_cert_validate_for_server && + context->info().reject_reasons.empty()) { + QUIC_RELOADABLE_FLAG_COUNT(quic_crypto_postpone_cert_validate_for_server); + if (!context->signed_config() || !context->signed_config()->chain) { + // No chain. + context->validate_chlo_result()->info.reject_reasons.push_back( + SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE); + } else if (!ValidateExpectedLeafCertificate( + context->client_hello(), + context->signed_config()->chain->certs)) { + // Has chain but leaf is invalid. + context->validate_chlo_result()->info.reject_reasons.push_back( + INVALID_EXPECTED_LEAF_CERTIFICATE); + } + } + if (found_error) { context->Fail(QUIC_HANDSHAKE_FAILED, "Failed to get proof"); return; @@ -824,8 +842,9 @@ void QuicCryptoServerConfig::ProcessClientHelloAfterCalculateSharedKeys( absl::string_view public_value, std::unique_ptr<ProcessClientHelloContext> context, const Configs& configs) const { - QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion( - context->connection_id(), context->transport_version())) + QUIC_BUG_IF(quic_bug_12963_3, + !QuicUtils::IsConnectionIdValidForVersion( + context->connection_id(), context->transport_version())) << "ProcessClientHelloAfterCalculateSharedKeys:" " attempted to use connection ID " << context->connection_id() << " which is invalid with version " @@ -835,9 +854,7 @@ void QuicCryptoServerConfig::ProcessClientHelloAfterCalculateSharedKeys( // If we are already using the fallback config, or there is no fallback // config to use, just bail out of the handshake. if (configs.fallback == nullptr || - context->signed_config()->config == configs.fallback || - !GetQuicReloadableFlag( - send_quic_fallback_server_config_on_leto_error)) { + context->signed_config()->config == configs.fallback) { context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, "Failed to calculate shared key"); } else { @@ -1134,9 +1151,10 @@ void QuicCryptoServerConfig::SelectNewPrimaryConfig( if (configs.empty()) { if (primary_config_ != nullptr) { - QUIC_BUG << "No valid QUIC server config. Keeping the current config."; + QUIC_BUG(quic_bug_10630_2) + << "No valid QUIC server config. Keeping the current config."; } else { - QUIC_BUG << "No valid QUIC server config."; + QUIC_BUG(quic_bug_10630_3) << "No valid QUIC server config."; } return; } @@ -1276,13 +1294,15 @@ void QuicCryptoServerConfig::EvaluateClientHello( // No valid source address token. } - QuicReferenceCountedPointer<ProofSource::Chain> chain = - proof_source_->GetCertChain(server_address, client_address, - std::string(info->sni)); - if (!chain) { - info->reject_reasons.push_back(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE); - } else if (!ValidateExpectedLeafCertificate(client_hello, chain->certs)) { - info->reject_reasons.push_back(INVALID_EXPECTED_LEAF_CERTIFICATE); + if (!client_hello_state->postpone_cert_validate_for_server) { + QuicReferenceCountedPointer<ProofSource::Chain> chain = + proof_source_->GetCertChain(server_address, client_address, + std::string(info->sni)); + if (!chain) { + info->reject_reasons.push_back(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE); + } else if (!ValidateExpectedLeafCertificate(client_hello, chain->certs)) { + info->reject_reasons.push_back(INVALID_EXPECTED_LEAF_CERTIFICATE); + } } if (info->client_nonce.size() != kNonceSize) { @@ -1455,7 +1475,8 @@ void QuicCryptoServerConfig::BuildRejection( // The client may have requested a certificate chain. if (!ClientDemandsX509Proof(context.client_hello())) { - QUIC_BUG << "x509 certificates not supported in proof demand"; + QUIC_BUG(quic_bug_10630_4) + << "x509 certificates not supported in proof demand"; return; } 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 4f3ad0819e4..464f2bbfc36 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h @@ -98,6 +98,9 @@ class QUIC_EXPORT_PRIVATE ValidateClientHelloResultCallback { // Populated if the CHLO STK contained a CachedNetworkParameters proto. CachedNetworkParameters cached_network_params; + const bool postpone_cert_validate_for_server = + GetQuicReloadableFlag(quic_crypto_postpone_cert_validate_for_server); + protected: ~Result() override; }; diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_decrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_decrypter.cc index 02be1a1cd26..bd3a0c385e7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_decrypter.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_decrypter.cc @@ -56,7 +56,7 @@ std::unique_ptr<QuicDecrypter> QuicDecrypter::CreateFromCipherSuite( case TLS1_CK_CHACHA20_POLY1305_SHA256: return std::make_unique<ChaCha20Poly1305TlsDecrypter>(); default: - QUIC_BUG << "TLS cipher suite is unknown to QUIC"; + QUIC_BUG(quic_bug_10660_1) << "TLS cipher suite is unknown to QUIC"; return nullptr; } } diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_encrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_encrypter.cc index 4f7f7c9cd6d..70a936743a2 100644 --- a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_encrypter.cc +++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_encrypter.cc @@ -53,7 +53,7 @@ std::unique_ptr<QuicEncrypter> QuicEncrypter::CreateFromCipherSuite( case TLS1_CK_CHACHA20_POLY1305_SHA256: return std::make_unique<ChaCha20Poly1305TlsEncrypter>(); default: - QUIC_BUG << "TLS cipher suite is unknown to QUIC"; + QUIC_BUG(quic_bug_10711_1) << "TLS cipher suite is unknown to QUIC"; return nullptr; } } 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 9b5636c0824..381de7eeafb 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 @@ -62,7 +62,8 @@ EncryptionLevel TlsConnection::QuicEncryptionLevel( case ssl_encryption_application: return ENCRYPTION_FORWARD_SECURE; default: - QUIC_BUG << "Invalid ssl_encryption_level_t " << static_cast<int>(level); + QUIC_BUG(quic_bug_10698_1) + << "Invalid ssl_encryption_level_t " << static_cast<int>(level); return ENCRYPTION_INITIAL; } } @@ -80,7 +81,8 @@ enum ssl_encryption_level_t TlsConnection::BoringEncryptionLevel( case ENCRYPTION_FORWARD_SECURE: return ssl_encryption_application; default: - QUIC_BUG << "Invalid encryption level " << static_cast<int>(level); + QUIC_BUG(quic_bug_10698_2) + << "Invalid encryption level " << static_cast<int>(level); return ssl_encryption_initial; } } 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 f4d0ac2ed4f..8926909cd78 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 @@ -27,26 +27,22 @@ bssl::UniquePtr<SSL_CTX> TlsServerConnection::CreateSslCtx( // We don't actually need the TicketCrypter here, but we need to know // whether it's set. if (proof_source->GetTicketCrypter()) { - QUIC_RESTART_FLAG_COUNT_N(quic_session_tickets_always_enabled, 1, 3); + QUIC_CODE_COUNT(quic_session_tickets_enabled); SSL_CTX_set_ticket_aead_method(ssl_ctx.get(), &TlsServerConnection::kSessionTicketMethod); } else if (!GetQuicRestartFlag(quic_session_tickets_always_enabled)) { - QUIC_RESTART_FLAG_COUNT_N(quic_session_tickets_always_enabled, 2, 3); + QUIC_CODE_COUNT(quic_session_tickets_disabled_by_flag); SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_NO_TICKET); } else { - QUIC_RESTART_FLAG_COUNT_N(quic_session_tickets_always_enabled, 3, 3); + QUIC_CODE_COUNT(quic_session_tickets_disabled); } - if (GetQuicRestartFlag(quic_enable_zero_rtt_for_tls_v2) && - (proof_source->GetTicketCrypter() || - GetQuicRestartFlag(quic_session_tickets_always_enabled))) { + if (proof_source->GetTicketCrypter() || + GetQuicRestartFlag(quic_session_tickets_always_enabled)) { SSL_CTX_set_early_data_enabled(ssl_ctx.get(), 1); } SSL_CTX_set_select_certificate_cb( ssl_ctx.get(), &TlsServerConnection::EarlySelectCertCallback); - if (GetQuicRestartFlag(quic_tls_prefer_server_cipher_and_curve_list)) { - QUIC_RESTART_FLAG_COUNT(quic_tls_prefer_server_cipher_and_curve_list); - SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE); - } + SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE); return ssl_ctx; } 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 8cbd06e66b2..14df9c3a27c 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,7 +22,6 @@ #include "quic/core/quic_utils.h" #include "quic/core/quic_versions.h" #include "quic/platform/api/quic_bug_tracker.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { @@ -75,7 +74,6 @@ constexpr uint64_t kMaxAckDelayExponentTransportParam = 20; constexpr uint64_t kDefaultAckDelayExponentTransportParam = 3; constexpr uint64_t kMaxMaxAckDelayTransportParam = 16383; constexpr uint64_t kDefaultMaxAckDelayTransportParam = 25; -constexpr size_t kStatelessResetTokenLength = 16; constexpr uint64_t kMinActiveConnectionIdLimitTransportParam = 2; constexpr uint64_t kDefaultActiveConnectionIdLimitTransportParam = 2; @@ -131,7 +129,7 @@ std::string TransportParameterIdToString( case TransportParameters::kMinAckDelay: return "min_ack_delay_us"; } - return "Unknown(" + quiche::QuicheTextUtils::Uint64ToString(param_id) + ")"; + return absl::StrCat("Unknown(", param_id, ")"); } bool TransportParameterIdIsKnown( @@ -212,17 +210,17 @@ bool TransportParameters::IntegerParameter::Write( return true; } if (!writer->WriteVarInt62(param_id_)) { - QUIC_BUG << "Failed to write param_id for " << *this; + QUIC_BUG(quic_bug_10743_1) << "Failed to write param_id for " << *this; return false; } const QuicVariableLengthIntegerLength value_length = QuicDataWriter::GetVarInt62Len(value_); if (!writer->WriteVarInt62(value_length)) { - QUIC_BUG << "Failed to write value_length for " << *this; + QUIC_BUG(quic_bug_10743_2) << "Failed to write value_length for " << *this; return false; } if (!writer->WriteVarInt62(value_, value_length)) { - QUIC_BUG << "Failed to write value for " << *this; + QUIC_BUG(quic_bug_10743_3) << "Failed to write value for " << *this; return false; } return true; @@ -257,8 +255,7 @@ std::string TransportParameters::IntegerParameter::ToString( return ""; } std::string rv = for_use_in_list ? " " : ""; - rv += TransportParameterIdToString(param_id_) + " "; - rv += quiche::QuicheTextUtils::Uint64ToString(value_); + absl::StrAppend(&rv, TransportParameterIdToString(param_id_), " ", value_); if (!IsValid()) { rv += " (Invalid)"; } @@ -387,8 +384,8 @@ std::string TransportParameters::ToString() const { rv += " " + TransportParameterIdToString(kGoogleKeyUpdateNotYetSupported); } for (const auto& kv : custom_parameters) { - rv += " 0x" + quiche::QuicheTextUtils::Hex(static_cast<uint32_t>(kv.first)); - rv += "="; + absl::StrAppend(&rv, " 0x", absl::Hex(static_cast<uint32_t>(kv.first)), + "="); static constexpr size_t kMaxPrintableLength = 32; if (kv.second.length() <= kMaxPrintableLength) { rv += absl::BytesToHexString(kv.second); @@ -563,7 +560,7 @@ bool TransportParameters::AreValid(std::string* error_details) const { if (preferred_address && (!preferred_address->ipv4_socket_address.host().IsIPv4() || !preferred_address->ipv6_socket_address.host().IsIPv6())) { - QUIC_BUG << "Preferred address family failure"; + QUIC_BUG(quic_bug_10743_4) << "Preferred address family failure"; *error_details = "Internal preferred address family failure"; return false; } @@ -612,13 +609,13 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, std::vector<uint8_t>* out) { std::string error_details; if (!in.AreValid(&error_details)) { - QUIC_BUG << "Not serializing invalid transport parameters: " - << error_details; + QUIC_BUG(quic_bug_10743_5) + << "Not serializing invalid transport parameters: " << error_details; return false; } if (in.version == 0 || (in.perspective == Perspective::IS_SERVER && in.supported_versions.empty())) { - QUIC_BUG << "Refusing to serialize without versions"; + QUIC_BUG(quic_bug_10743_6) << "Refusing to serialize without versions"; return false; } @@ -701,14 +698,15 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, !writer.WriteStringPieceVarInt62( absl::string_view(original_destination_connection_id.data(), original_destination_connection_id.length()))) { - QUIC_BUG << "Failed to write original_destination_connection_id " - << original_destination_connection_id << " for " << in; + 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 << "Failed to write idle_timeout for " << in; + QUIC_BUG(quic_bug_10743_8) << "Failed to write idle_timeout for " << in; return false; } @@ -721,8 +719,9 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, !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 of length " - << in.stateless_reset_token.size() << " for " << in; + QUIC_BUG(quic_bug_10743_9) + << "Failed to write stateless_reset_token of length " + << in.stateless_reset_token.size() << " for " << in; return false; } } @@ -739,7 +738,7 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, !in.active_connection_id_limit.Write(&writer) || !in.max_datagram_frame_size.Write(&writer) || !in.initial_round_trip_time_us.Write(&writer)) { - QUIC_BUG << "Failed to write integers for " << in; + QUIC_BUG(quic_bug_10743_10) << "Failed to write integers for " << in; return false; } @@ -747,7 +746,8 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, if (in.disable_active_migration) { if (!writer.WriteVarInt62(TransportParameters::kDisableActiveMigration) || !writer.WriteVarInt62(/* transport parameter length */ 0)) { - QUIC_BUG << "Failed to write disable_active_migration for " << in; + QUIC_BUG(quic_bug_10743_11) + << "Failed to write disable_active_migration for " << in; return false; } } @@ -761,7 +761,7 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, if (v4_address_bytes.length() != 4 || v6_address_bytes.length() != 16 || in.preferred_address->stateless_reset_token.size() != kStatelessResetTokenLength) { - QUIC_BUG << "Bad lengths " << *in.preferred_address; + QUIC_BUG(quic_bug_10743_12) << "Bad lengths " << *in.preferred_address; return false; } const uint64_t preferred_address_length = @@ -783,7 +783,8 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, !writer.WriteBytes( in.preferred_address->stateless_reset_token.data(), in.preferred_address->stateless_reset_token.size())) { - QUIC_BUG << "Failed to write preferred_address for " << in; + QUIC_BUG(quic_bug_10743_13) + << "Failed to write preferred_address for " << in; return false; } } @@ -797,8 +798,9 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, !writer.WriteStringPieceVarInt62( absl::string_view(initial_source_connection_id.data(), initial_source_connection_id.length()))) { - QUIC_BUG << "Failed to write initial_source_connection_id " - << initial_source_connection_id << " for " << in; + QUIC_BUG(quic_bug_10743_14) + << "Failed to write initial_source_connection_id " + << initial_source_connection_id << " for " << in; return false; } } @@ -812,8 +814,9 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, !writer.WriteStringPieceVarInt62( absl::string_view(retry_source_connection_id.data(), retry_source_connection_id.length()))) { - QUIC_BUG << "Failed to write retry_source_connection_id " - << retry_source_connection_id << " for " << in; + QUIC_BUG(quic_bug_10743_15) + << "Failed to write retry_source_connection_id " + << retry_source_connection_id << " for " << in; return false; } } @@ -827,15 +830,17 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, if (!writer.WriteVarInt62(TransportParameters::kGoogleConnectionOptions) || !writer.WriteVarInt62( /* transport parameter length */ connection_options_length)) { - QUIC_BUG << "Failed to write google_connection_options of length " - << connection_options_length << " for " << in; + 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 << "Failed to write google_connection_option " - << QuicTagToString(connection_option) << " for " << in; + QUIC_BUG(quic_bug_10743_17) + << "Failed to write google_connection_option " + << QuicTagToString(connection_option) << " for " << in; return false; } } @@ -845,8 +850,9 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, 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 \"" - << in.user_agent_id.value() << "\" for " << in; + QUIC_BUG(quic_bug_10743_18) + << "Failed to write Google user agent ID \"" + << in.user_agent_id.value() << "\" for " << in; return false; } } @@ -856,7 +862,8 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, if (!writer.WriteVarInt62( TransportParameters::kGoogleKeyUpdateNotYetSupported) || !writer.WriteVarInt62(/* transport parameter length */ 0)) { - QUIC_BUG << "Failed to write key_update_not_yet_supported for " << in; + QUIC_BUG(quic_bug_10743_19) + << "Failed to write key_update_not_yet_supported for " << in; return false; } } @@ -873,18 +880,21 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, !writer.WriteVarInt62( /* transport parameter length */ google_version_length) || !writer.WriteUInt32(in.version)) { - QUIC_BUG << "Failed to write Google version extension for " << in; + 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 << "Failed to write versions length for " << in; + 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 << "Failed to write supported version for " << in; + QUIC_BUG(quic_bug_10743_22) + << "Failed to write supported version for " << in; return false; } } @@ -895,13 +905,15 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, if (param_id % 31 == 27) { // See the "Reserved Transport Parameters" section of // draft-ietf-quic-transport. - QUIC_BUG << "Serializing custom_parameters with GREASE ID " << param_id - << " is not allowed"; + 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 << "Failed to write custom parameter " << param_id; + QUIC_BUG(quic_bug_10743_24) + << "Failed to write custom parameter " << param_id; return false; } } @@ -928,8 +940,8 @@ bool SerializeTransportParameters(ParsedQuicVersion /*version*/, if (!writer.WriteVarInt62(grease_id) || !writer.WriteStringPieceVarInt62( absl::string_view(grease_contents, grease_length))) { - QUIC_BUG << "Failed to write GREASE parameter " - << TransportParameterIdToString(grease_id); + QUIC_BUG(quic_bug_10743_25) << "Failed to write GREASE parameter " + << TransportParameterIdToString(grease_id); return false; } } @@ -1253,8 +1265,8 @@ bool SerializeTransportParametersForTicket( std::vector<uint8_t>* out) { std::string error_details; if (!in.AreValid(&error_details)) { - QUIC_BUG << "Not serializing invalid transport parameters: " - << error_details; + QUIC_BUG(quic_bug_10743_26) + << "Not serializing invalid transport parameters: " << error_details; return false; } @@ -1286,8 +1298,9 @@ bool SerializeTransportParametersForTicket( application_data.size()) || !EVP_DigestUpdate(hash_ctx.get(), ¶meter_version, sizeof(parameter_version))) { - QUIC_BUG << "Unexpected failure of EVP_Digest functions when hashing " - "Transport Parameters for ticket"; + QUIC_BUG(quic_bug_10743_27) + << "Unexpected failure of EVP_Digest functions when hashing " + "Transport Parameters for ticket"; return false; } @@ -1304,16 +1317,18 @@ bool SerializeTransportParametersForTicket( !DigestUpdateIntegerParam(hash_ctx.get(), in.initial_max_streams_uni) || !DigestUpdateIntegerParam(hash_ctx.get(), in.active_connection_id_limit)) { - QUIC_BUG << "Unexpected failure of EVP_Digest functions when hashing " - "Transport Parameters for ticket"; + QUIC_BUG(quic_bug_10743_28) + << "Unexpected failure of EVP_Digest functions when hashing " + "Transport Parameters for ticket"; return false; } uint8_t disable_active_migration = in.disable_active_migration ? 1 : 0; if (!EVP_DigestUpdate(hash_ctx.get(), &disable_active_migration, sizeof(disable_active_migration)) || !EVP_DigestFinal(hash_ctx.get(), out->data() + 1, nullptr)) { - QUIC_BUG << "Unexpected failure of EVP_Digest functions when hashing " - "Transport Parameters for ticket"; + QUIC_BUG(quic_bug_10743_29) + << "Unexpected failure of EVP_Digest functions when hashing " + "Transport Parameters for ticket"; return false; } return true; 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 3d23ae6a33b..81707c51885 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 @@ -448,7 +448,7 @@ TEST_P(TransportParametersTest, NoClientParamsWithStatelessResetToken) { orig_params.max_udp_payload_size.set_value(kMaxPacketSizeForTest); std::vector<uint8_t> out; - bool ok; + bool ok = true; EXPECT_QUIC_BUG( ok = SerializeTransportParameters(version_, orig_params, &out), "Not serializing invalid transport parameters: Client cannot send " diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frame.cc index 7c112c2c5e0..724833e02f0 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frame.cc +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frame.cc @@ -96,7 +96,7 @@ bool PacketNumberQueue::RemoveUpTo(QuicPacketNumber higher) { void PacketNumberQueue::RemoveSmallestInterval() { // TODO(wub): Move this QUIC_BUG to upper level. - QUIC_BUG_IF(packet_number_intervals_.Size() < 2) + QUIC_BUG_IF(quic_bug_12614_1, packet_number_intervals_.Size() < 2) << (Empty() ? "No intervals to remove." : "Can't remove the last interval."); packet_number_intervals_.PopFront(); @@ -171,7 +171,7 @@ std::ostream& operator<<(std::ostream& os, const PacketNumberQueue& q) { (interval.max() - interval.min() > kMaxPrintRange)) { // If min>max, it's really a bug, so QUIC_BUG it to // catch it in development. - QUIC_BUG_IF(interval.min() >= interval.max()) + QUIC_BUG_IF(quic_bug_12614_2, interval.min() >= interval.max()) << "Ack Range minimum (" << interval.min() << "Not less than max (" << interval.max() << ")"; // print range as min...max rather than full list. diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.cc index 0eee910af5d..e17c3e76c7c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.cc +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.cc @@ -7,6 +7,7 @@ #include <memory> #include "quic/core/quic_constants.h" +#include "quic/core/quic_error_codes.h" #include "quic/core/quic_types.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.cc index e54e2544c0c..ad10d601c31 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.cc +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.cc @@ -267,7 +267,7 @@ void SetControlFrameId(QuicControlFrameId control_frame_id, QuicFrame* frame) { frame->new_token_frame->control_frame_id = control_frame_id; return; default: - QUIC_BUG + QUIC_BUG(quic_bug_12594_1) << "Try to set control frame id of a frame without control frame id"; } } @@ -318,7 +318,8 @@ QuicFrame CopyRetransmittableControlFrame(const QuicFrame& frame) { copy = QuicFrame(new QuicNewTokenFrame(*frame.new_token_frame)); break; default: - QUIC_BUG << "Try to copy a non-retransmittable control frame: " << frame; + QUIC_BUG(quic_bug_10533_1) + << "Try to copy a non-retransmittable control frame: " << frame; copy = QuicFrame(QuicPingFrame(kInvalidControlFrameId)); break; } @@ -412,7 +413,7 @@ QuicFrame CopyQuicFrame(QuicBufferAllocator* allocator, copy = QuicFrame(new QuicAckFrequencyFrame(*frame.ack_frequency_frame)); break; default: - QUIC_BUG << "Cannot copy frame: " << frame; + QUIC_BUG(quic_bug_10533_2) << "Cannot copy frame: " << frame; copy = QuicFrame(QuicPingFrame(kInvalidControlFrameId)); break; } 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 d5ca0a4d624..c11be9eb016 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 @@ -116,7 +116,8 @@ TEST_F(QuicFramesTest, NewConnectionIdFrameToString) { new_connection_id_frame.connection_id = TestConnectionId(2); new_connection_id_frame.sequence_number = 2u; new_connection_id_frame.retire_prior_to = 1u; - new_connection_id_frame.stateless_reset_token = MakeQuicUint128(0, 1); + new_connection_id_frame.stateless_reset_token = + StatelessResetToken{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; std::ostringstream stream; stream << new_connection_id_frame; EXPECT_EQ( diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.cc index fa99dfe7b6b..b4c68ff78dd 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.cc +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.cc @@ -10,7 +10,7 @@ QuicNewConnectionIdFrame::QuicNewConnectionIdFrame( QuicControlFrameId control_frame_id, QuicConnectionId connection_id, QuicConnectionIdSequenceNumber sequence_number, - const QuicUint128 stateless_reset_token, + StatelessResetToken stateless_reset_token, uint64_t retire_prior_to) : control_frame_id(control_frame_id), connection_id(connection_id), diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.h index 4cf0258ca10..9c3d2053d34 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.h +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.h @@ -11,7 +11,6 @@ #include "quic/core/quic_constants.h" #include "quic/core/quic_error_codes.h" #include "quic/core/quic_types.h" -#include "quic/platform/api/quic_uint128.h" namespace quic { @@ -20,7 +19,7 @@ struct QUIC_EXPORT_PRIVATE QuicNewConnectionIdFrame { QuicNewConnectionIdFrame(QuicControlFrameId control_frame_id, QuicConnectionId connection_id, QuicConnectionIdSequenceNumber sequence_number, - const QuicUint128 stateless_reset_token, + StatelessResetToken stateless_reset_token, uint64_t retire_prior_to); friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( @@ -32,7 +31,7 @@ struct QUIC_EXPORT_PRIVATE QuicNewConnectionIdFrame { QuicControlFrameId control_frame_id = kInvalidControlFrameId; QuicConnectionId connection_id = EmptyQuicConnectionId(); QuicConnectionIdSequenceNumber sequence_number = 0; - QuicUint128 stateless_reset_token; + StatelessResetToken stateless_reset_token; uint64_t retire_prior_to; }; diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_retire_connection_id_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_retire_connection_id_frame.h index c6fc4117d18..3a8a4dac986 100644 --- a/chromium/net/third_party/quiche/src/quic/core/frames/quic_retire_connection_id_frame.h +++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_retire_connection_id_frame.h @@ -10,7 +10,6 @@ #include "quic/core/quic_constants.h" #include "quic/core/quic_error_codes.h" #include "quic/core/quic_types.h" -#include "quic/platform/api/quic_uint128.h" namespace quic { 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 646425a674c..d70f217bc14 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 @@ -4,6 +4,8 @@ #include "quic/core/frames/quic_rst_stream_frame.h" +#include "quic/core/quic_error_codes.h" + namespace quic { QuicRstStreamFrame::QuicRstStreamFrame(QuicControlFrameId control_frame_id, 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 ad02337b5ef..93eed75ccc4 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 @@ -4,6 +4,8 @@ #include "quic/core/frames/quic_stop_sending_frame.h" +#include "quic/core/quic_error_codes.h" + namespace quic { QuicStopSendingFrame::QuicStopSendingFrame(QuicControlFrameId control_frame_id, 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 2000733ba9a..aceebe1b33b 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 @@ -16,6 +16,7 @@ #include "quic/core/crypto/null_encrypter.h" #include "quic/core/http/http_constants.h" #include "quic/core/http/quic_spdy_client_stream.h" +#include "quic/core/http/web_transport_http3.h" #include "quic/core/quic_data_writer.h" #include "quic/core/quic_epoll_connection_helper.h" #include "quic/core/quic_error_codes.h" @@ -24,6 +25,7 @@ #include "quic/core/quic_packet_writer_wrapper.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_session.h" +#include "quic/core/quic_types.h" #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_epoll.h" #include "quic/platform/api/quic_error_code_wrappers.h" @@ -55,9 +57,11 @@ #include "quic/test_tools/quic_stream_id_manager_peer.h" #include "quic/test_tools/quic_stream_peer.h" #include "quic/test_tools/quic_stream_sequencer_peer.h" +#include "quic/test_tools/quic_test_backend.h" #include "quic/test_tools/quic_test_client.h" #include "quic/test_tools/quic_test_server.h" #include "quic/test_tools/quic_test_utils.h" +#include "quic/test_tools/quic_transport_test_tools.h" #include "quic/test_tools/server_thread.h" #include "quic/test_tools/simple_session_cache.h" #include "quic/tools/quic_backend_response.h" @@ -66,7 +70,6 @@ #include "quic/tools/quic_server.h" #include "quic/tools/quic_simple_client_stream.h" #include "quic/tools/quic_simple_server_stream.h" -#include "common/platform/api/quiche_text_utils.h" using spdy::kV3LowestPriority; using spdy::SpdyFramer; @@ -74,6 +77,7 @@ using spdy::SpdyHeaderBlock; using spdy::SpdySerializedFrame; using spdy::SpdySettingsIR; using ::testing::_; +using ::testing::Assign; using ::testing::Invoke; using ::testing::NiceMock; @@ -198,7 +202,6 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { AddToCache("/foo", 200, kFooResponseBody); AddToCache("/bar", 200, kBarResponseBody); // Enable fixes for bugs found in tests and prod. - SetQuicRestartFlag(quic_enable_zero_rtt_for_tls_v2, true); } ~EndToEndTest() override { QuicRecyclePort(server_address_.port()); } @@ -221,6 +224,7 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { client->UseConnectionIdLength(override_server_connection_id_length_); client->UseClientConnectionIdLength(override_client_connection_id_length_); client->client()->set_connection_debug_visitor(connection_debug_visitor_); + client->client()->set_enable_web_transport(enable_web_transport_); client->Connect(); return client; } @@ -357,6 +361,11 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { } bool Initialize() { + if (enable_web_transport_) { + SetQuicReloadableFlag(quic_h3_datagram, true); + memory_cache_backend_.set_enable_webtransport(true); + } + QuicTagVector copt; server_config_.SetConnectionOptionsToSend(copt); copt = client_extra_copts_; @@ -369,6 +378,7 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { copt.push_back(kILD0); } copt.push_back(kPLE1); + copt.push_back(kRVCM); client_config_.SetConnectionOptionsToSend(copt); // Start the server first, because CreateQuicClient() attempts @@ -669,6 +679,77 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { return WaitForFooResponseAndCheckIt(client_.get()); } + WebTransportHttp3* CreateWebTransportSession(const std::string& path, + bool wait_for_server_response) { + // Wait until we receive the settings from the server indicating + // WebTransport support. + client_->WaitUntil( + 2000, [this]() { return GetClientSession()->SupportsWebTransport(); }); + if (!GetClientSession()->SupportsWebTransport()) { + return nullptr; + } + + spdy::SpdyHeaderBlock headers; + headers[":scheme"] = "https"; + headers[":authority"] = "localhost"; + headers[":path"] = path; + headers[":method"] = "CONNECT"; + headers[":protocol"] = "webtransport"; + + client_->SendMessage(headers, "", /*fin=*/false); + QuicSpdyStream* stream = client_->latest_created_stream(); + if (stream->web_transport() == nullptr) { + return nullptr; + } + WebTransportSessionId id = client_->latest_created_stream()->id(); + QuicSpdySession* client_session = GetClientSession(); + if (client_session->GetWebTransportSession(id) == nullptr) { + return nullptr; + } + WebTransportHttp3* session = client_session->GetWebTransportSession(id); + if (wait_for_server_response) { + client_->WaitUntil(-1, + [stream]() { return stream->headers_decompressed(); }); + EXPECT_TRUE(session->ready()); + } + return session; + } + + NiceMock<MockClientVisitor>& SetupWebTransportVisitor( + WebTransportHttp3* session) { + auto visitor_owned = std::make_unique<NiceMock<MockClientVisitor>>(); + NiceMock<MockClientVisitor>& visitor = *visitor_owned; + session->SetVisitor(std::move(visitor_owned)); + return visitor; + } + + std::string ReadDataFromWebTransportStreamUntilFin( + WebTransportStream* stream) { + std::string buffer; + while (true) { + bool can_read = false; + auto visitor = std::make_unique<MockStreamVisitor>(); + 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"; + return buffer; + } + + WebTransportStream::ReadResult result = stream->Read(&buffer); + if (result.fin) { + return buffer; + } + if (result.bytes_read == 0) { + ADD_FAILURE() << "No progress made while reading from stream " + << stream->GetStreamId(); + return buffer; + } + } + } + ScopedEnvironmentForThreads environment_; bool initialized_; // If true, the Initialize() function will create |client_| and starts to @@ -677,7 +758,7 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { bool connect_to_server_on_initialize_; QuicSocketAddress server_address_; std::string server_hostname_; - QuicMemoryCacheBackend memory_cache_backend_; + QuicTestBackend memory_cache_backend_; std::unique_ptr<ServerThread> server_thread_; std::unique_ptr<QuicTestClient> client_; QuicConnectionDebugVisitor* connection_debug_visitor_ = nullptr; @@ -696,6 +777,7 @@ class EndToEndTest : public QuicTestWithParam<TestParams> { int override_server_connection_id_length_ = -1; int override_client_connection_id_length_ = -1; uint8_t expected_server_connection_id_length_; + bool enable_web_transport_ = false; }; // Run all end to end tests with all supported versions. @@ -1794,8 +1876,11 @@ TEST_P(EndToEndTest, RetransmissionAfterZeroRTTRejectBeforeOneRtt) { ON_CALL(visitor, OnZeroRttRejected(_)).WillByDefault(Invoke([this]() { EXPECT_FALSE(GetClientSession()->IsEncryptionEstablished()); - // Trigger an OnCanWrite() to make sure no unencrypted data will be written. - GetClientSession()->OnCanWrite(); + if (!GetQuicReloadableFlag(quic_donot_write_mid_packet_processing)) { + // Trigger an OnCanWrite() to make sure no unencrypted data will be + // written. + GetClientSession()->OnCanWrite(); + } })); // The 0-RTT handshake should fail. @@ -2386,6 +2471,50 @@ TEST_P(EndToEndTest, ResetConnection) { SendSynchronousBarRequestAndCheckResponse(); } +// Regression test for b/180737158. +TEST_P( + EndToEndTest, + HalfRttResponseBlocksShloRetransmissionWithoutTokenBasedAddressValidation) { + // Turn off token based address validation to make the server get constrained + // by amplification factor during handshake. + // TODO(fayang): Keep this test while deprecating + // quic_enable_token_based_address_validation. For example, consider always + // rejecting the received address token. + SetQuicReloadableFlag(quic_enable_token_based_address_validation, false); + ASSERT_TRUE(Initialize()); + if (!version_.SupportsAntiAmplificationLimit()) { + return; + } + // Perform a full 1-RTT handshake to get the new session ticket such that the + // next connection will perform a 0-RTT handshake. + EXPECT_TRUE(client_->client()->WaitForHandshakeConfirmed()); + client_->Disconnect(); + + server_thread_->Pause(); + // Drop the 1st server packet which is the coalesced INITIAL + HANDSHAKE + + // 1RTT. + PacketDroppingTestWriter* writer = new PacketDroppingTestWriter(); + writer->set_fake_drop_first_n_packets(1); + QuicDispatcherPeer::UseWriter( + QuicServerPeer::GetDispatcher(server_thread_->server()), writer); + server_thread_->Resume(); + + // Large response (100KB) for 0-RTT request. + std::string large_body(102400, 'a'); + AddToCache("/large_response", 200, large_body); + if (GetQuicReloadableFlag(quic_preempt_stream_data_with_handshake_packet)) { + SendSynchronousRequestAndCheckResponse(client_.get(), "/large_response", + large_body); + } else { + // Server consistently gets constrained by amplification factor, hence PTO + // never gets armed. The CHLO retransmission would trigger the + // retransmission of SHLO, however, the ENCRYPTION_HANDSHAKE packet NEVER + // gets retransmitted since half RTT data consumes the remaining space in + // the coalescer. + EXPECT_EQ("", client_->SendSynchronousRequest("/large_response")); + } +} + TEST_P(EndToEndTest, MaxStreamsUberTest) { // Connect with lower fake packet loss than we'd like to test. Until // b/10126687 is fixed, losing handshake packets is pretty brutal. @@ -3209,7 +3338,8 @@ TEST_P(EndToEndTest, ServerSendPublicReset) { QuicConfig* config = client_session->config(); ASSERT_TRUE(config); EXPECT_TRUE(config->HasReceivedStatelessResetToken()); - QuicUint128 stateless_reset_token = config->ReceivedStatelessResetToken(); + StatelessResetToken stateless_reset_token = + config->ReceivedStatelessResetToken(); // Send the public reset. QuicConnection* client_connection = GetClientConnection(); @@ -3221,8 +3351,8 @@ TEST_P(EndToEndTest, ServerSendPublicReset) { Perspective::IS_SERVER, kQuicDefaultConnectionIdLength); std::unique_ptr<QuicEncryptedPacket> packet; if (version_.HasIetfInvariantHeader()) { - packet = framer.BuildIetfStatelessResetPacket(connection_id, - stateless_reset_token); + packet = framer.BuildIetfStatelessResetPacket( + connection_id, /*received_packet_length=*/100, stateless_reset_token); } else { packet = framer.BuildPublicResetPacket(header); } @@ -3251,7 +3381,8 @@ TEST_P(EndToEndTest, ServerSendPublicResetWithDifferentConnectionId) { QuicConfig* config = client_session->config(); ASSERT_TRUE(config); EXPECT_TRUE(config->HasReceivedStatelessResetToken()); - QuicUint128 stateless_reset_token = config->ReceivedStatelessResetToken(); + StatelessResetToken stateless_reset_token = + config->ReceivedStatelessResetToken(); // Send the public reset. QuicConnection* client_connection = GetClientConnection(); ASSERT_TRUE(client_connection); @@ -3265,8 +3396,9 @@ TEST_P(EndToEndTest, ServerSendPublicResetWithDifferentConnectionId) { NiceMock<MockQuicConnectionDebugVisitor> visitor; client_connection->set_debug_visitor(&visitor); if (version_.HasIetfInvariantHeader()) { - packet = framer.BuildIetfStatelessResetPacket(incorrect_connection_id, - stateless_reset_token); + packet = framer.BuildIetfStatelessResetPacket( + incorrect_connection_id, /*received_packet_length=*/100, + stateless_reset_token); EXPECT_CALL(visitor, OnIncorrectConnectionId(incorrect_connection_id)) .Times(0); } else { @@ -3518,8 +3650,7 @@ class ServerStreamWithErrorResponseBody : public QuicSimpleServerStream { QUIC_DLOG(INFO) << "Sending error response for stream " << id(); SpdyHeaderBlock headers; headers[":status"] = "500"; - headers["content-length"] = - quiche::QuicheTextUtils::Uint64ToString(response_body_.size()); + headers["content-length"] = absl::StrCat(response_body_.size()); // This method must call CloseReadSide to cause the test case, StopReading // is not sufficient. QuicStreamPeer::CloseReadSide(this); @@ -3736,8 +3867,7 @@ TEST_P(EndToEndTest, Trailers) { SpdyHeaderBlock headers; headers[":status"] = "200"; - headers["content-length"] = - quiche::QuicheTextUtils::Uint64ToString(kBody.size()); + headers["content-length"] = absl::StrCat(kBody.size()); SpdyHeaderBlock trailers; trailers["some-trailing-header"] = "trailing-header-value"; @@ -3750,13 +3880,11 @@ TEST_P(EndToEndTest, Trailers) { EXPECT_EQ(trailers, client_->response_trailers()); } -// TODO(b/151749109): Test server push for IETF QUIC. class EndToEndTestServerPush : public EndToEndTest { protected: const size_t kNumMaxStreams = 10; EndToEndTestServerPush() : EndToEndTest() { - SetQuicFlag(FLAGS_quic_enable_http3_server_push, true); client_config_.SetMaxBidirectionalStreamsToSend(kNumMaxStreams); server_config_.SetMaxBidirectionalStreamsToSend(kNumMaxStreams); client_config_.SetMaxUnidirectionalStreamsToSend(kNumMaxStreams); @@ -3791,8 +3919,7 @@ class EndToEndTestServerPush : public EndToEndTest { : absl::StrCat("This is server push response body for ", url); SpdyHeaderBlock response_headers; response_headers[":status"] = "200"; - response_headers["content-length"] = - quiche::QuicheTextUtils::Uint64ToString(body.size()); + response_headers["content-length"] = absl::StrCat(body.size()); push_resources.push_back(QuicBackendResponse::ServerPushInfo( resource_url, std::move(response_headers), kV3LowestPriority, body)); } @@ -3869,12 +3996,11 @@ TEST_P(EndToEndTestServerPush, ServerPushUnderLimit) { // them with requests later. ASSERT_TRUE(Initialize()); - EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); if (version_.UsesHttp3()) { - static_cast<QuicSpdySession*>(client_->client()->session()) - ->SetMaxPushId(kMaxQuicStreamId); + return; } + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); SetReorderPercentage(30); @@ -3918,8 +4044,6 @@ TEST_P(EndToEndTestServerPush, ServerPushUnderLimit) { TEST_P(EndToEndTestServerPush, ServerPushOverLimitNonBlocking) { if (version_.UsesHttp3()) { - // TODO(b/142504641): Re-enable this test when we support push streams - // arriving before the corresponding promises. ASSERT_TRUE(Initialize()); return; } @@ -3929,10 +4053,6 @@ TEST_P(EndToEndTestServerPush, ServerPushOverLimitNonBlocking) { // immediately after pushing resources. ASSERT_TRUE(Initialize()); EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); - if (version_.UsesHttp3()) { - static_cast<QuicSpdySession*>(client_->client()->session()) - ->SetMaxPushId(kMaxQuicStreamId); - } // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); @@ -3974,6 +4094,11 @@ TEST_P(EndToEndTestServerPush, ServerPushOverLimitNonBlocking) { } TEST_P(EndToEndTestServerPush, ServerPushOverLimitWithBlocking) { + if (version_.UsesHttp3()) { + ASSERT_TRUE(Initialize()); + return; + } + // Tests that when server tries to send more large resources(large enough to // be blocked by flow control window or congestion control window) than max // open outgoing streams , server can open upto max number of outgoing @@ -3991,10 +4116,6 @@ TEST_P(EndToEndTestServerPush, ServerPushOverLimitWithBlocking) { ASSERT_TRUE(Initialize()); EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); - if (version_.UsesHttp3()) { - static_cast<QuicSpdySession*>(client_->client()->session()) - ->SetMaxPushId(kMaxQuicStreamId); - } // Set reordering to ensure that body arriving before PUSH_PROMISE is ok. SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2)); @@ -4092,8 +4213,7 @@ TEST_P(EndToEndTest, DISABLED_TestHugePostWithPacketLoss) { headers[":path"] = "/foo"; headers[":scheme"] = "https"; headers[":authority"] = server_hostname_; - headers["content-length"] = - quiche::QuicheTextUtils::Uint64ToString(request_body_size_bytes); + headers["content-length"] = absl::StrCat(request_body_size_bytes); client_->SendMessage(headers, "", /*fin=*/false); @@ -4320,7 +4440,11 @@ TEST_P(EndToEndTest, client_.reset(CreateQuicClient(client_writer_)); EXPECT_EQ("", client_->SendSynchronousRequest("/foo")); - EXPECT_THAT(client_->connection_error(), IsError(QUIC_HANDSHAKE_FAILED)); + if (GetQuicReloadableFlag(quic_fix_dispatcher_sent_error_code)) { + EXPECT_THAT(client_->connection_error(), IsError(QUIC_PACKET_WRITE_ERROR)); + } else { + EXPECT_THAT(client_->connection_error(), IsError(QUIC_HANDSHAKE_FAILED)); + } } // Regression test for b/116200989. @@ -4418,7 +4542,7 @@ TEST_P(EndToEndTest, PreSharedKey) { if (version_.UsesTls()) { // TODO(b/154162689) add PSK support to QUIC+TLS. - bool ok; + bool ok = true; EXPECT_QUIC_BUG(ok = Initialize(), "QUIC client pre-shared keys not yet supported with TLS"); EXPECT_FALSE(ok); @@ -4441,7 +4565,7 @@ TEST_P(EndToEndTest, QUIC_TEST_DISABLED_IN_CHROME(PreSharedKeyMismatch)) { if (version_.UsesTls()) { // TODO(b/154162689) add PSK support to QUIC+TLS. - bool ok; + bool ok = true; EXPECT_QUIC_BUG(ok = Initialize(), "QUIC client pre-shared keys not yet supported with TLS"); EXPECT_FALSE(ok); @@ -4468,7 +4592,7 @@ TEST_P(EndToEndTest, QUIC_TEST_DISABLED_IN_CHROME(PreSharedKeyNoClient)) { if (version_.UsesTls()) { // TODO(b/154162689) add PSK support to QUIC+TLS. - bool ok; + bool ok = true; EXPECT_QUIC_BUG(ok = Initialize(), "QUIC server pre-shared keys not yet supported with TLS"); EXPECT_FALSE(ok); @@ -4489,7 +4613,7 @@ TEST_P(EndToEndTest, QUIC_TEST_DISABLED_IN_CHROME(PreSharedKeyNoServer)) { if (version_.UsesTls()) { // TODO(b/154162689) add PSK support to QUIC+TLS. - bool ok; + bool ok = true; EXPECT_QUIC_BUG(ok = Initialize(), "QUIC client pre-shared keys not yet supported with TLS"); EXPECT_FALSE(ok); @@ -4508,9 +4632,8 @@ TEST_P(EndToEndTest, RequestAndStreamRstInOnePacket) { // (and the FIN) after the response body. std::string response_body(1305, 'a'); SpdyHeaderBlock response_headers; - response_headers[":status"] = quiche::QuicheTextUtils::Uint64ToString(200); - response_headers["content-length"] = - quiche::QuicheTextUtils::Uint64ToString(response_body.length()); + response_headers[":status"] = absl::StrCat(200); + response_headers["content-length"] = absl::StrCat(response_body.length()); memory_cache_backend_.AddSpecialResponse( server_hostname_, "/test_url", std::move(response_headers), response_body, QuicBackendResponse::INCOMPLETE_RESPONSE); @@ -5127,34 +5250,6 @@ TEST_P(EndToEndTest, TooBigStreamIdClosesConnection) { IS_IETF_STREAM_FRAME(client_session->transport_close_frame_type())); } -TEST_P(EndToEndTest, TestMaxPushId) { - if (!version_.HasIetfQuicFrames()) { - // MaxPushId is only implemented for IETF QUIC. - Initialize(); - return; - } - SetQuicFlag(FLAGS_quic_enable_http3_server_push, true); - ASSERT_TRUE(Initialize()); - - EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); - QuicSpdyClientSession* client_session = GetClientSession(); - ASSERT_TRUE(client_session); - client_session->SetMaxPushId(kMaxQuicStreamId); - - client_->SendSynchronousRequest("/foo"); - - EXPECT_TRUE(client_session->CanCreatePushStreamWithId(kMaxQuicStreamId)); - - server_thread_->Pause(); - QuicSpdySession* server_session = GetServerSession(); - if (server_session != nullptr) { - EXPECT_TRUE(server_session->CanCreatePushStreamWithId(kMaxQuicStreamId)); - } else { - ADD_FAILURE() << "Missing server session"; - } - server_thread_->Resume(); -} - TEST_P(EndToEndTest, CustomTransportParameters) { if (!version_.UsesTls()) { // Custom transport parameters are only supported with TLS. @@ -5624,6 +5719,213 @@ TEST_P(EndToEndTest, TlsResumptionDisabledOnTheFly) { ADD_FAILURE() << "Client should not have 10 resumption tickets."; } +TEST_P(EndToEndTest, WebTransportSessionSetup) { + enable_web_transport_ = true; + ASSERT_TRUE(Initialize()); + + if (!version_.UsesHttp3()) { + return; + } + + WebTransportHttp3* web_transport = + CreateWebTransportSession("/echo", /*wait_for_server_response=*/true); + + server_thread_->Pause(); + QuicSpdySession* server_session = GetServerSession(); + EXPECT_TRUE(server_session->GetWebTransportSession(web_transport->id()) != + nullptr); + server_thread_->Resume(); +} + +TEST_P(EndToEndTest, WebTransportSessionWithLoss) { + enable_web_transport_ = true; + // Enable loss to verify all permutations of receiving SETTINGS and + // request/response data. + SetPacketLossPercentage(30); + ASSERT_TRUE(Initialize()); + + if (!version_.UsesHttp3()) { + return; + } + + WebTransportHttp3* web_transport = + CreateWebTransportSession("/echo", /*wait_for_server_response=*/true); + + server_thread_->Pause(); + QuicSpdySession* server_session = GetServerSession(); + EXPECT_TRUE(server_session->GetWebTransportSession(web_transport->id()) != + nullptr); + server_thread_->Resume(); +} + +TEST_P(EndToEndTest, WebTransportSessionUnidirectionalStream) { + 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* outgoing_stream = + session->OpenOutgoingUnidirectionalStream(); + ASSERT_TRUE(outgoing_stream != nullptr); + EXPECT_TRUE(outgoing_stream->Write("test")); + EXPECT_TRUE(outgoing_stream->SendFin()); + + bool stream_received = false; + EXPECT_CALL(visitor, OnIncomingUnidirectionalStreamAvailable()) + .WillOnce(Assign(&stream_received, true)); + client_->WaitUntil(2000, [&stream_received]() { return stream_received; }); + EXPECT_TRUE(stream_received); + WebTransportStream* received_stream = + session->AcceptIncomingUnidirectionalStream(); + ASSERT_TRUE(received_stream != nullptr); + std::string received_data; + WebTransportStream::ReadResult result = received_stream->Read(&received_data); + EXPECT_EQ(received_data, "test"); + EXPECT_TRUE(result.fin); +} + +TEST_P(EndToEndTest, WebTransportSessionUnidirectionalStreamSentEarly) { + enable_web_transport_ = true; + SetPacketLossPercentage(30); + ASSERT_TRUE(Initialize()); + + if (!version_.UsesHttp3()) { + return; + } + + WebTransportHttp3* session = + CreateWebTransportSession("/echo", /*wait_for_server_response=*/false); + ASSERT_TRUE(session != nullptr); + NiceMock<MockClientVisitor>& visitor = SetupWebTransportVisitor(session); + + WebTransportStream* outgoing_stream = + session->OpenOutgoingUnidirectionalStream(); + ASSERT_TRUE(outgoing_stream != nullptr); + EXPECT_TRUE(outgoing_stream->Write("test")); + EXPECT_TRUE(outgoing_stream->SendFin()); + + bool stream_received = false; + EXPECT_CALL(visitor, OnIncomingUnidirectionalStreamAvailable()) + .WillOnce(Assign(&stream_received, true)); + client_->WaitUntil(5000, [&stream_received]() { return stream_received; }); + EXPECT_TRUE(stream_received); + WebTransportStream* received_stream = + session->AcceptIncomingUnidirectionalStream(); + ASSERT_TRUE(received_stream != nullptr); + std::string received_data; + WebTransportStream::ReadResult result = received_stream->Read(&received_data); + EXPECT_EQ(received_data, "test"); + EXPECT_TRUE(result.fin); +} + +TEST_P(EndToEndTest, WebTransportSessionBidirectionalStream) { + enable_web_transport_ = true; + ASSERT_TRUE(Initialize()); + + if (!version_.UsesHttp3()) { + return; + } + + WebTransportHttp3* session = + CreateWebTransportSession("/echo", /*wait_for_server_response=*/true); + ASSERT_TRUE(session != nullptr); + + WebTransportStream* stream = session->OpenOutgoingBidirectionalStream(); + ASSERT_TRUE(stream != nullptr); + EXPECT_TRUE(stream->Write("test")); + EXPECT_TRUE(stream->SendFin()); + + std::string received_data = ReadDataFromWebTransportStreamUntilFin(stream); + EXPECT_EQ(received_data, "test"); +} + +TEST_P(EndToEndTest, WebTransportSessionBidirectionalStreamWithBuffering) { + enable_web_transport_ = true; + SetPacketLossPercentage(30); + ASSERT_TRUE(Initialize()); + + if (!version_.UsesHttp3()) { + return; + } + + WebTransportHttp3* session = + CreateWebTransportSession("/echo", /*wait_for_server_response=*/false); + ASSERT_TRUE(session != nullptr); + + WebTransportStream* stream = session->OpenOutgoingBidirectionalStream(); + ASSERT_TRUE(stream != nullptr); + EXPECT_TRUE(stream->Write("test")); + EXPECT_TRUE(stream->SendFin()); + + std::string received_data = ReadDataFromWebTransportStreamUntilFin(stream); + EXPECT_EQ(received_data, "test"); +} + +TEST_P(EndToEndTest, WebTransportSessionServerBidirectionalStream) { + enable_web_transport_ = true; + ASSERT_TRUE(Initialize()); + + if (!version_.UsesHttp3()) { + return; + } + + WebTransportHttp3* session = + CreateWebTransportSession("/echo", /*wait_for_server_response=*/false); + ASSERT_TRUE(session != nullptr); + NiceMock<MockClientVisitor>& visitor = SetupWebTransportVisitor(session); + + bool stream_received = false; + EXPECT_CALL(visitor, OnIncomingBidirectionalStreamAvailable()) + .WillOnce(Assign(&stream_received, true)); + client_->WaitUntil(5000, [&stream_received]() { return stream_received; }); + EXPECT_TRUE(stream_received); + + WebTransportStream* stream = session->AcceptIncomingBidirectionalStream(); + ASSERT_TRUE(stream != nullptr); + EXPECT_TRUE(stream->Write("test")); + EXPECT_TRUE(stream->SendFin()); + + std::string received_data = ReadDataFromWebTransportStreamUntilFin(stream); + EXPECT_EQ(received_data, "test"); +} + +TEST_P(EndToEndTest, WebTransportDatagrams) { + 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); + + SimpleBufferAllocator allocator; + for (int i = 0; i < 10; i++) { + absl::string_view datagram = "test"; + auto buffer = MakeUniqueBuffer(&allocator, datagram.size()); + memcpy(buffer.get(), datagram.data(), datagram.size()); + QuicMemSlice slice(std::move(buffer), datagram.size()); + session->SendOrQueueDatagram(std::move(slice)); + } + + int received = 0; + EXPECT_CALL(visitor, OnDatagramReceived(_)).WillRepeatedly([&received]() { + received++; + }); + client_->WaitUntil(5000, [&received]() { return received > 0; }); + EXPECT_GT(received, 0); +} + } // 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 b3262d25b7d..f3ed523647f 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,10 +17,14 @@ 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_WEBTRANS_DRAFT00); } return absl::StrCat("UNSUPPORTED_SETTINGS_TYPE(", identifier, ")"); } +const absl::string_view kUserAgentHeaderName = "user-agent"; + #undef RETURN_STRING_LITERAL // undef for jumbo builds } // namespace quic 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 8c68e74bccc..c13a1a8c2b2 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 @@ -8,22 +8,26 @@ #include <cstdint> #include <string> +#include "absl/strings/string_view.h" #include "quic/core/quic_types.h" #include "quic/platform/api/quic_export.h" namespace quic { // Unidirectional stream types. - -// https://quicwg.org/base-drafts/draft-ietf-quic-http.html#unidirectional-streams -const uint64_t kControlStream = 0x00; -const uint64_t kServerPushStream = 0x01; -// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#enc-dec-stream-def -const uint64_t kQpackEncoderStream = 0x02; -const uint64_t kQpackDecoderStream = 0x03; +enum : uint64_t { + // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#unidirectional-streams + kControlStream = 0x00, + kServerPushStream = 0x01, + // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#enc-dec-stream-def + kQpackEncoderStream = 0x02, + kQpackDecoderStream = 0x03, + // https://ietf-wg-webtrans.github.io/draft-ietf-webtrans-http3/draft-ietf-webtrans-http3.html#name-unidirectional-streams + kWebTransportUnidirectionalStream = 0x54, +}; // This includes control stream, QPACK encoder stream, and QPACK decoder stream. -const QuicStreamCount kHttp3StaticUnidirectionalStreamCount = 3; +enum : QuicStreamCount { kHttp3StaticUnidirectionalStreamCount = 3 }; // HTTP/3 and QPACK settings identifiers. // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#settings-parameters @@ -34,6 +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-webtrans-http3-00 + SETTINGS_WEBTRANS_DRAFT00 = 0x2b603742, }; // Returns HTTP/3 SETTINGS identifier as a string. @@ -42,17 +50,22 @@ QUIC_EXPORT std::string H3SettingsToString( // Default maximum dynamic table capacity, communicated via // SETTINGS_QPACK_MAX_TABLE_CAPACITY. -const QuicByteCount kDefaultQpackMaxDynamicTableCapacity = 64 * 1024; // 64 KB +enum : QuicByteCount { + kDefaultQpackMaxDynamicTableCapacity = 64 * 1024 // 64 KB +}; // Default limit on the size of uncompressed headers, // communicated via SETTINGS_MAX_HEADER_LIST_SIZE. -const QuicByteCount kDefaultMaxUncompressedHeaderSize = 16 * 1024; // 16 KB +enum : QuicByteCount { + kDefaultMaxUncompressedHeaderSize = 16 * 1024 // 16 KB +}; // Default limit on number of blocked streams, communicated via // SETTINGS_QPACK_BLOCKED_STREAMS. -const uint64_t kDefaultMaximumBlockedStreams = 100; +enum : uint64_t { kDefaultMaximumBlockedStreams = 100 }; -const char kUserAgentHeaderName[] = "user-agent"; +ABSL_CONST_INIT QUIC_EXPORT_PRIVATE extern const absl::string_view + kUserAgentHeaderName; } // namespace quic 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 fe33a817152..3544cfddf9a 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,6 +5,7 @@ #include "quic/core/http/http_decoder.h" #include <cstdint> +#include <limits> #include "absl/base/attributes.h" #include "absl/strings/string_view.h" @@ -20,8 +21,10 @@ namespace quic { -HttpDecoder::HttpDecoder(Visitor* visitor) +HttpDecoder::HttpDecoder(Visitor* visitor) : HttpDecoder(visitor, Options()) {} +HttpDecoder::HttpDecoder(Visitor* visitor, Options options) : visitor_(visitor), + allow_web_transport_stream_(options.allow_web_transport_stream), state_(STATE_READING_FRAME_TYPE), current_frame_type_(0), current_length_field_length_(0), @@ -106,12 +109,21 @@ QuicByteCount HttpDecoder::ProcessInput(const char* data, QuicByteCount len) { continue_processing = ReadFramePayload(&reader); break; case STATE_FINISH_PARSING: - continue_processing = FinishParsing(); + continue_processing = FinishParsing(&reader); + break; + case STATE_PARSING_NO_LONGER_POSSIBLE: + continue_processing = false; + QUIC_BUG(HttpDecoder PARSING_NO_LONGER_POSSIBLE) + << "HttpDecoder called after an indefinite-length frame has been " + "received"; + RaiseError(QUIC_INTERNAL_ERROR, + "HttpDecoder called after an indefinite-length frame has " + "been received"); break; case STATE_ERROR: break; default: - QUIC_BUG << "Invalid state: " << state_; + QUIC_BUG(quic_bug_10411_1) << "Invalid state: " << state_; } } @@ -192,6 +204,19 @@ bool HttpDecoder::ReadFrameLength(QuicDataReader* reader) { QUICHE_DCHECK(success); } + // WEBTRANSPORT_STREAM frames are indefinitely long, and thus require + // special handling; the number after the frame type is actually the + // WebTransport session ID, and not the length. + if (allow_web_transport_stream_ && + current_frame_type_ == + static_cast<uint64_t>(HttpFrameType::WEBTRANSPORT_STREAM)) { + visitor_->OnWebTransportStreamFrameType( + current_length_field_length_ + current_type_field_length_, + current_frame_length_); + state_ = STATE_PARSING_NO_LONGER_POSSIBLE; + return false; + } + if (current_frame_length_ > MaxFrameLength(current_frame_type_)) { RaiseError(QUIC_HTTP_FRAME_TOO_LARGE, "Frame is too large."); return false; @@ -238,13 +263,7 @@ bool HttpDecoder::ReadFrameLength(QuicDataReader* reader) { continue_processing = visitor_->OnPriorityUpdateFrameStart(header_length); break; case static_cast<uint64_t>(HttpFrameType::ACCEPT_CH): - if (GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_parse_accept_ch_frame); - continue_processing = visitor_->OnAcceptChFrameStart(header_length); - } else { - continue_processing = visitor_->OnUnknownFrameStart( - current_frame_type_, header_length, current_frame_length_); - } + continue_processing = visitor_->OnAcceptChFrameStart(header_length); break; default: continue_processing = visitor_->OnUnknownFrameStart( @@ -288,11 +307,11 @@ bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) { break; } case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): { - BufferFramePayload(reader); + continue_processing = BufferOrParsePayload(reader); break; } case static_cast<uint64_t>(HttpFrameType::SETTINGS): { - BufferFramePayload(reader); + continue_processing = BufferOrParsePayload(reader); break; } case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE): { @@ -361,33 +380,23 @@ bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) { break; } case static_cast<uint64_t>(HttpFrameType::GOAWAY): { - BufferFramePayload(reader); + continue_processing = BufferOrParsePayload(reader); break; } case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID): { - BufferFramePayload(reader); + continue_processing = BufferOrParsePayload(reader); break; } case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE): { - // TODO(bnc): Avoid buffering if the entire frame is present, and - // instead parse directly out of |reader|. - BufferFramePayload(reader); + continue_processing = BufferOrParsePayload(reader); break; } case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM): { - // TODO(bnc): Avoid buffering if the entire frame is present, and - // instead parse directly out of |reader|. - BufferFramePayload(reader); + continue_processing = BufferOrParsePayload(reader); break; } case static_cast<uint64_t>(HttpFrameType::ACCEPT_CH): { - if (GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { - // TODO(bnc): Avoid buffering if the entire frame is present, and - // instead parse directly out of |reader|. - BufferFramePayload(reader); - } else { - continue_processing = HandleUnknownFramePayload(reader); - } + continue_processing = BufferOrParsePayload(reader); break; } default: { @@ -396,14 +405,15 @@ bool HttpDecoder::ReadFramePayload(QuicDataReader* reader) { } } - if (remaining_frame_length_ == 0) { + // BufferOrParsePayload() may have advanced |state_|. + if (state_ == STATE_READING_FRAME_PAYLOAD && remaining_frame_length_ == 0) { state_ = STATE_FINISH_PARSING; } return continue_processing; } -bool HttpDecoder::FinishParsing() { +bool HttpDecoder::FinishParsing(QuicDataReader* reader) { QUICHE_DCHECK_EQ(0u, remaining_frame_length_); bool continue_processing = true; @@ -418,28 +428,15 @@ bool HttpDecoder::FinishParsing() { break; } case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): { - CancelPushFrame frame; - QuicDataReader reader(buffer_.data(), current_frame_length_); - if (!reader.ReadVarInt62(&frame.push_id)) { - RaiseError(QUIC_HTTP_FRAME_ERROR, - "Unable to read CANCEL_PUSH push_id."); - return false; - } - if (!reader.IsDoneReading()) { - RaiseError(QUIC_HTTP_FRAME_ERROR, - "Superfluous data in CANCEL_PUSH frame."); - return false; - } - continue_processing = visitor_->OnCancelPushFrame(frame); + // If frame payload is not empty, FinishParsing() is skipped. + QUICHE_DCHECK_EQ(0u, current_frame_length_); + continue_processing = BufferOrParsePayload(reader); break; } case static_cast<uint64_t>(HttpFrameType::SETTINGS): { - SettingsFrame frame; - QuicDataReader reader(buffer_.data(), current_frame_length_); - if (!ParseSettingsFrame(&reader, &frame)) { - return false; - } - continue_processing = visitor_->OnSettingsFrame(frame); + // If frame payload is not empty, FinishParsing() is skipped. + QUICHE_DCHECK_EQ(0u, current_frame_length_); + continue_processing = BufferOrParsePayload(reader); break; } case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE): { @@ -447,70 +444,33 @@ bool HttpDecoder::FinishParsing() { break; } case static_cast<uint64_t>(HttpFrameType::GOAWAY): { - QuicDataReader reader(buffer_.data(), current_frame_length_); - GoAwayFrame frame; - if (!reader.ReadVarInt62(&frame.id)) { - RaiseError(QUIC_HTTP_FRAME_ERROR, "Unable to read GOAWAY ID."); - return false; - } - if (!reader.IsDoneReading()) { - RaiseError(QUIC_HTTP_FRAME_ERROR, "Superfluous data in GOAWAY frame."); - return false; - } - continue_processing = visitor_->OnGoAwayFrame(frame); + // If frame payload is not empty, FinishParsing() is skipped. + QUICHE_DCHECK_EQ(0u, current_frame_length_); + continue_processing = BufferOrParsePayload(reader); break; } case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID): { - QuicDataReader reader(buffer_.data(), current_frame_length_); - MaxPushIdFrame frame; - if (!reader.ReadVarInt62(&frame.push_id)) { - RaiseError(QUIC_HTTP_FRAME_ERROR, - "Unable to read MAX_PUSH_ID push_id."); - return false; - } - if (!reader.IsDoneReading()) { - RaiseError(QUIC_HTTP_FRAME_ERROR, - "Superfluous data in MAX_PUSH_ID frame."); - return false; - } - continue_processing = visitor_->OnMaxPushIdFrame(frame); + // If frame payload is not empty, FinishParsing() is skipped. + QUICHE_DCHECK_EQ(0u, current_frame_length_); + continue_processing = BufferOrParsePayload(reader); break; } case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE): { - // TODO(bnc): Avoid buffering if the entire frame is present, and - // instead parse directly out of |reader|. - PriorityUpdateFrame frame; - QuicDataReader reader(buffer_.data(), current_frame_length_); - if (!ParsePriorityUpdateFrame(&reader, &frame)) { - return false; - } - continue_processing = visitor_->OnPriorityUpdateFrame(frame); + // If frame payload is not empty, FinishParsing() is skipped. + QUICHE_DCHECK_EQ(0u, current_frame_length_); + continue_processing = BufferOrParsePayload(reader); break; } case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM): { - // TODO(bnc): Avoid buffering if the entire frame is present, and - // instead parse directly out of |reader|. - PriorityUpdateFrame frame; - QuicDataReader reader(buffer_.data(), current_frame_length_); - if (!ParseNewPriorityUpdateFrame(&reader, &frame)) { - return false; - } - continue_processing = visitor_->OnPriorityUpdateFrame(frame); + // If frame payload is not empty, FinishParsing() is skipped. + QUICHE_DCHECK_EQ(0u, current_frame_length_); + continue_processing = BufferOrParsePayload(reader); break; } case static_cast<uint64_t>(HttpFrameType::ACCEPT_CH): { - if (GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { - // TODO(bnc): Avoid buffering if the entire frame is present, and - // instead parse directly out of |reader|. - AcceptChFrame frame; - QuicDataReader reader(buffer_.data(), current_frame_length_); - if (!ParseAcceptChFrame(&reader, &frame)) { - return false; - } - continue_processing = visitor_->OnAcceptChFrame(frame); - } else { - continue_processing = visitor_->OnUnknownFrameEnd(); - } + // If frame payload is not empty, FinishParsing() is skipped. + QUICHE_DCHECK_EQ(0u, current_frame_length_); + continue_processing = BufferOrParsePayload(reader); break; } default: @@ -548,18 +508,129 @@ void HttpDecoder::DiscardFramePayload(QuicDataReader* reader) { } } -void HttpDecoder::BufferFramePayload(QuicDataReader* reader) { - if (current_frame_length_ == remaining_frame_length_) { - buffer_.erase(buffer_.size()); - buffer_.reserve(current_frame_length_); +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_); + } + + // 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; + + QUICHE_DCHECK_EQ(current_frame_length_, + buffer_.size() + remaining_frame_length_); + + if (remaining_frame_length_ > 0) { + QUICHE_DCHECK(reader->IsDoneReading()); + return true; + } + + QuicDataReader buffer_reader(buffer_); + continue_processing = ParseEntirePayload(&buffer_reader); + buffer_.clear(); + } + + current_length_field_length_ = 0; + current_type_field_length_ = 0; + state_ = STATE_READING_FRAME_TYPE; + return continue_processing; +} + +bool HttpDecoder::ParseEntirePayload(QuicDataReader* reader) { + QUICHE_DCHECK_EQ(current_frame_length_, reader->BytesRemaining()); + QUICHE_DCHECK_EQ(0u, remaining_frame_length_); + + switch (current_frame_type_) { + case static_cast<uint64_t>(HttpFrameType::CANCEL_PUSH): { + CancelPushFrame frame; + if (!reader->ReadVarInt62(&frame.push_id)) { + RaiseError(QUIC_HTTP_FRAME_ERROR, + "Unable to read CANCEL_PUSH push_id."); + return false; + } + if (!reader->IsDoneReading()) { + RaiseError(QUIC_HTTP_FRAME_ERROR, + "Superfluous data in CANCEL_PUSH frame."); + return false; + } + return visitor_->OnCancelPushFrame(frame); + } + case static_cast<uint64_t>(HttpFrameType::SETTINGS): { + SettingsFrame frame; + if (!ParseSettingsFrame(reader, &frame)) { + return false; + } + return visitor_->OnSettingsFrame(frame); + } + case static_cast<uint64_t>(HttpFrameType::GOAWAY): { + GoAwayFrame frame; + if (!reader->ReadVarInt62(&frame.id)) { + RaiseError(QUIC_HTTP_FRAME_ERROR, "Unable to read GOAWAY ID."); + return false; + } + if (!reader->IsDoneReading()) { + RaiseError(QUIC_HTTP_FRAME_ERROR, "Superfluous data in GOAWAY frame."); + return false; + } + return visitor_->OnGoAwayFrame(frame); + } + case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID): { + MaxPushIdFrame frame; + if (!reader->ReadVarInt62(&frame.push_id)) { + RaiseError(QUIC_HTTP_FRAME_ERROR, + "Unable to read MAX_PUSH_ID push_id."); + return false; + } + if (!reader->IsDoneReading()) { + RaiseError(QUIC_HTTP_FRAME_ERROR, + "Superfluous data in MAX_PUSH_ID frame."); + return false; + } + return visitor_->OnMaxPushIdFrame(frame); + } + case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE): { + PriorityUpdateFrame frame; + if (!ParsePriorityUpdateFrame(reader, &frame)) { + return false; + } + return visitor_->OnPriorityUpdateFrame(frame); + } + case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM): { + PriorityUpdateFrame frame; + if (!ParseNewPriorityUpdateFrame(reader, &frame)) { + return false; + } + return visitor_->OnPriorityUpdateFrame(frame); + } + case static_cast<uint64_t>(HttpFrameType::ACCEPT_CH): { + AcceptChFrame frame; + if (!ParseAcceptChFrame(reader, &frame)) { + return false; + } + return visitor_->OnAcceptChFrame(frame); + } + default: + // Only above frame types are parsed by ParseEntirePayload(). + QUICHE_NOTREACHED(); + return false; } - QuicByteCount bytes_to_read = std::min<QuicByteCount>( - remaining_frame_length_, reader->BytesRemaining()); - bool success = reader->ReadBytes( - &(buffer_[0]) + current_frame_length_ - remaining_frame_length_, - bytes_to_read); - QUICHE_DCHECK(success); - remaining_frame_length_ -= bytes_to_read; } void HttpDecoder::BufferFrameLength(QuicDataReader* reader) { 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 fe955c32bf8..a351415e964 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 @@ -27,6 +27,11 @@ class QuicDataReader; // session. class QUIC_EXPORT_PRIVATE HttpDecoder { public: + struct QUIC_EXPORT_PRIVATE Options { + // Indicates that WEBTRANSPORT_STREAM should be parsed. + bool allow_web_transport_stream = false; + }; + class QUIC_EXPORT_PRIVATE Visitor { public: virtual ~Visitor() {} @@ -109,6 +114,15 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // Called when an ACCEPT_CH frame has been successfully parsed. virtual bool OnAcceptChFrame(const AcceptChFrame& frame) = 0; + // Called when a WEBTRANSPORT_STREAM frame type and the session ID varint + // immediately following it has been received. Any further parsing should + // be done by the stream itself, and not the parser. Note that this does not + // return bool, because WEBTRANSPORT_STREAM always causes the parsing + // process to cease. + virtual void OnWebTransportStreamFrameType( + QuicByteCount header_length, + WebTransportSessionId session_id) = 0; + // Called when a frame of unknown type |frame_type| has been received. // Frame type might be reserved, Visitor must make sure to ignore. // |header_length| and |payload_length| are the length of the frame header @@ -126,6 +140,7 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // |visitor| must be non-null, and must outlive HttpDecoder. explicit HttpDecoder(Visitor* visitor); + explicit HttpDecoder(Visitor* visitor, Options options); ~HttpDecoder(); @@ -162,6 +177,7 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { STATE_READING_FRAME_TYPE, STATE_READING_FRAME_PAYLOAD, STATE_FINISH_PARSING, + STATE_PARSING_NO_LONGER_POSSIBLE, STATE_ERROR }; @@ -175,14 +191,17 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // if there are any errors. Returns whether processing should continue. bool ReadFrameLength(QuicDataReader* reader); - // Reads the payload of the current frame from |reader| and processes it, - // possibly buffering the data or invoking the visitor. Returns whether - // processing should continue. + // Depending on the frame type, reads and processes the payload of the current + // frame from |reader| and calls visitor methods, or calls + // BufferOrParsePayload(). Returns whether processing should continue. bool ReadFramePayload(QuicDataReader* reader); - // Optionally parses buffered data; calls visitor method to signal that frame - // had been parsed completely. Returns whether processing should continue. - bool FinishParsing(); + // For frame types parsed by BufferOrParsePayload(), this method is only + // called if frame payload is empty, at it calls BufferOrParsePayload(). For + // other frame types, this method directly calls visitor methods to signal + // that frame had been parsed completely. Returns whether processing should + // continue. + bool FinishParsing(QuicDataReader* reader); // Read payload of unknown frame from |reader| and call // Visitor::OnUnknownFramePayload(). Returns true decoding should continue, @@ -192,8 +211,16 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // Discards any remaining frame payload from |reader|. void DiscardFramePayload(QuicDataReader* reader); - // Buffers any remaining frame payload from |reader| into |buffer_|. - void BufferFramePayload(QuicDataReader* reader); + // Buffers any remaining frame payload from |*reader| into |buffer_| if + // necessary. Parses the frame payload if complete. Parses out of |*reader| + // without unnecessary copy if |*reader| has entire payload. + // Returns whether processing should continue. + bool BufferOrParsePayload(QuicDataReader* reader); + + // Parses the entire payload of certain kinds of frames that are parsed in a + // single pass. |reader| must have at least |current_frame_length_| bytes. + // Returns whether processing should continue. + bool ParseEntirePayload(QuicDataReader* reader); // Buffers any remaining frame length field from |reader| into // |length_buffer_|. @@ -230,6 +257,8 @@ class QUIC_EXPORT_PRIVATE HttpDecoder { // Visitor to invoke when messages are parsed. Visitor* const visitor_; // Unowned. + // Whether WEBTRANSPORT_STREAM should be parsed. + bool allow_web_transport_stream_; // Current state of the parsing. HttpDecoderState state_; // Type of the frame currently being parsed. 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 1be5084f77d..aac020a97a7 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 @@ -15,6 +15,7 @@ #include "quic/core/http/http_frames.h" #include "quic/core/quic_data_writer.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_test.h" #include "quic/test_tools/quic_test_utils.h" @@ -109,6 +110,10 @@ class MockVisitor : public HttpDecoder::Visitor { (QuicByteCount header_length), (override)); MOCK_METHOD(bool, OnAcceptChFrame, (const AcceptChFrame& frame), (override)); + MOCK_METHOD(void, + OnWebTransportStreamFrameType, + (QuicByteCount header_length, WebTransportSessionId session_id), + (override)); MOCK_METHOD(bool, OnUnknownFrameStart, @@ -867,11 +872,43 @@ TEST_F(HttpDecoderTest, CorruptFrame) { "\x04" // length "\x05" // valid stream id "foo", // superfluous data - "Superfluous data in GOAWAY frame."}}; + "Superfluous data in GOAWAY frame."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x01" // length + "\x40", // first byte of two-byte varint origin length + "Unable to read ACCEPT_CH origin."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x01" // length + "\x05", // valid origin length but no origin string + "Unable to read ACCEPT_CH origin."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x04" // length + "\x05" // valid origin length + "foo", // payload ends before origin ends + "Unable to read ACCEPT_CH origin."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x04" // length + "\x03" // valid origin length + "foo", // payload ends at end of origin: no value + "Unable to read ACCEPT_CH value."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x05" // length + "\x03" // valid origin length + "foo" // payload ends at end of origin: no value + "\x40", // first byte of two-byte varint value length + "Unable to read ACCEPT_CH value."}, + {"\x40\x89" // type (ACCEPT_CH) + "\x08" // length + "\x03" // valid origin length + "foo" // origin + "\x05" // valid value length + "bar", // payload ends before value ends + "Unable to read ACCEPT_CH value."}}; for (const auto& test_data : kTestData) { { HttpDecoder decoder(&visitor_); + EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber()); EXPECT_CALL(visitor_, OnError(&decoder)); absl::string_view input(test_data.input); @@ -881,6 +918,7 @@ TEST_F(HttpDecoderTest, CorruptFrame) { } { HttpDecoder decoder(&visitor_); + EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber()); EXPECT_CALL(visitor_, OnError(&decoder)); absl::string_view input(test_data.input); @@ -1194,10 +1232,6 @@ TEST_F(HttpDecoderTest, CorruptNewPriorityUpdateFrame) { } TEST_F(HttpDecoderTest, AcceptChFrame) { - if (!GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { - return; - } - InSequence s; std::string input1 = absl::HexStringToBytes( "4089" // type (ACCEPT_CH) @@ -1272,72 +1306,41 @@ TEST_F(HttpDecoderTest, AcceptChFrame) { EXPECT_EQ("", decoder_.error_detail()); } -TEST_F(HttpDecoderTest, CorruptAcceptChFrame) { - if (!GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { - // TODO(bnc): merge this test into HttpDecoderTest.CorruptFrame when - // deprecating flag. - return; - } +TEST_F(HttpDecoderTest, WebTransportStreamDisabled) { + InSequence s; - EXPECT_CALL(visitor_, OnAcceptChFrameStart(_)).Times(AnyNumber()); + // Unknown frame of type 0x41 and length 0x104. + std::string input = absl::HexStringToBytes("40414104"); + EXPECT_CALL(visitor_, OnUnknownFrameStart(0x41, input.size(), 0x104)); + EXPECT_EQ(ProcessInput(input), input.size()); +} - struct { - const char* const input; - const char* const error_message; - } kTestData[] = {{"\x40\x89" // type (ACCEPT_CH) - "\x01" // length - "\x40", // first byte of two-byte varint origin length - "Unable to read ACCEPT_CH origin."}, - {"\x40\x89" // type (ACCEPT_CH) - "\x01" // length - "\x05", // valid origin length but no origin string - "Unable to read ACCEPT_CH origin."}, - {"\x40\x89" // type (ACCEPT_CH) - "\x04" // length - "\x05" // valid origin length - "foo", // payload ends before origin ends - "Unable to read ACCEPT_CH origin."}, - {"\x40\x89" // type (ACCEPT_CH) - "\x04" // length - "\x03" // valid origin length - "foo", // payload ends at end of origin: no value - "Unable to read ACCEPT_CH value."}, - {"\x40\x89" // type (ACCEPT_CH) - "\x05" // length - "\x03" // valid origin length - "foo" // payload ends at end of origin: no value - "\x40", // first byte of two-byte varint value length - "Unable to read ACCEPT_CH value."}, - {"\x40\x89" // type (ACCEPT_CH) - "\x08" // length - "\x03" // valid origin length - "foo" // origin - "\x05" // valid value length - "bar", // payload ends before value ends - "Unable to read ACCEPT_CH value."}}; +TEST(HttpDecoderTestNoFixture, WebTransportStream) { + HttpDecoder::Options options; + options.allow_web_transport_stream = true; + testing::StrictMock<MockVisitor> visitor; + HttpDecoder decoder(&visitor, options); + + // WebTransport stream for session ID 0x104, with four bytes of extra data. + std::string input = absl::HexStringToBytes("40414104ffffffff"); + EXPECT_CALL(visitor, OnWebTransportStreamFrameType(4, 0x104)); + QuicByteCount bytes = decoder.ProcessInput(input.data(), input.size()); + EXPECT_EQ(bytes, 4u); +} - for (const auto& test_data : kTestData) { - { - HttpDecoder decoder(&visitor_); - EXPECT_CALL(visitor_, OnError(&decoder)); +TEST(HttpDecoderTestNoFixture, WebTransportStreamError) { + HttpDecoder::Options options; + options.allow_web_transport_stream = true; + testing::StrictMock<MockVisitor> visitor; + HttpDecoder decoder(&visitor, options); - absl::string_view input(test_data.input); - decoder.ProcessInput(input.data(), input.size()); - EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR)); - EXPECT_EQ(test_data.error_message, decoder.error_detail()); - } - { - HttpDecoder decoder(&visitor_); - EXPECT_CALL(visitor_, OnError(&decoder)); + std::string input = absl::HexStringToBytes("404100"); + EXPECT_CALL(visitor, OnWebTransportStreamFrameType(_, _)); + decoder.ProcessInput(input.data(), input.size()); - absl::string_view input(test_data.input); - for (auto c : input) { - decoder.ProcessInput(&c, 1); - } - EXPECT_THAT(decoder.error(), IsError(QUIC_HTTP_FRAME_ERROR)); - EXPECT_EQ(test_data.error_message, decoder.error_detail()); - } - } + EXPECT_CALL(visitor, OnError(_)); + EXPECT_QUIC_BUG(decoder.ProcessInput(input.data(), input.size()), + "HttpDecoder called after an indefinite-length frame"); } TEST_F(HttpDecoderTest, DecodeSettings) { diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.cc b/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.cc index fafe3b83d08..2fbc2380cf7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.cc @@ -76,27 +76,6 @@ QuicByteCount HttpEncoder::SerializeHeadersFrameHeader( } // static -QuicByteCount HttpEncoder::SerializeCancelPushFrame( - const CancelPushFrame& cancel_push, - std::unique_ptr<char[]>* output) { - QuicByteCount payload_length = - QuicDataWriter::GetVarInt62Len(cancel_push.push_id); - QuicByteCount total_length = - GetTotalLength(payload_length, HttpFrameType::CANCEL_PUSH); - - output->reset(new char[total_length]); - QuicDataWriter writer(total_length, output->get()); - - if (WriteFrameHeader(payload_length, HttpFrameType::CANCEL_PUSH, &writer) && - writer.WriteVarInt62(cancel_push.push_id)) { - return total_length; - } - QUIC_DLOG(ERROR) - << "Http encoder failed when attempting to serialize cancel push frame."; - return 0; -} - -// static QuicByteCount HttpEncoder::SerializeSettingsFrame( const SettingsFrame& settings, std::unique_ptr<char[]>* output) { @@ -134,32 +113,6 @@ QuicByteCount HttpEncoder::SerializeSettingsFrame( } // static -QuicByteCount HttpEncoder::SerializePushPromiseFrameWithOnlyPushId( - const PushPromiseFrame& push_promise, - std::unique_ptr<char[]>* output) { - QuicByteCount payload_length = - QuicDataWriter::GetVarInt62Len(push_promise.push_id) + - push_promise.headers.length(); - // GetTotalLength() is not used because headers will not be serialized. - QuicByteCount total_length = - QuicDataWriter::GetVarInt62Len(payload_length) + - QuicDataWriter::GetVarInt62Len( - static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE)) + - QuicDataWriter::GetVarInt62Len(push_promise.push_id); - - output->reset(new char[total_length]); - QuicDataWriter writer(total_length, output->get()); - - if (WriteFrameHeader(payload_length, HttpFrameType::PUSH_PROMISE, &writer) && - writer.WriteVarInt62(push_promise.push_id)) { - return total_length; - } - QUIC_DLOG(ERROR) - << "Http encoder failed when attempting to serialize push promise frame."; - return 0; -} - -// static QuicByteCount HttpEncoder::SerializeGoAwayFrame( const GoAwayFrame& goaway, std::unique_ptr<char[]>* output) { @@ -180,32 +133,12 @@ QuicByteCount HttpEncoder::SerializeGoAwayFrame( } // static -QuicByteCount HttpEncoder::SerializeMaxPushIdFrame( - const MaxPushIdFrame& max_push_id, - std::unique_ptr<char[]>* output) { - QuicByteCount payload_length = - QuicDataWriter::GetVarInt62Len(max_push_id.push_id); - QuicByteCount total_length = - GetTotalLength(payload_length, HttpFrameType::MAX_PUSH_ID); - - output->reset(new char[total_length]); - QuicDataWriter writer(total_length, output->get()); - - if (WriteFrameHeader(payload_length, HttpFrameType::MAX_PUSH_ID, &writer) && - writer.WriteVarInt62(max_push_id.push_id)) { - return total_length; - } - QUIC_DLOG(ERROR) - << "Http encoder failed when attempting to serialize max push id frame."; - return 0; -} - -// static QuicByteCount HttpEncoder::SerializePriorityUpdateFrame( const PriorityUpdateFrame& priority_update, std::unique_ptr<char[]>* output) { if (priority_update.prioritized_element_type != REQUEST_STREAM) { - QUIC_BUG << "PRIORITY_UPDATE for push streams not implemented"; + QUIC_BUG(quic_bug_10402_1) + << "PRIORITY_UPDATE for push streams not implemented"; return 0; } @@ -315,4 +248,25 @@ QuicByteCount HttpEncoder::SerializeGreasingFrame( return 0; } +QuicByteCount HttpEncoder::SerializeWebTransportStreamFrameHeader( + WebTransportSessionId session_id, + std::unique_ptr<char[]>* output) { + uint64_t stream_type = + static_cast<uint64_t>(HttpFrameType::WEBTRANSPORT_STREAM); + QuicByteCount header_length = QuicDataWriter::GetVarInt62Len(stream_type) + + QuicDataWriter::GetVarInt62Len(session_id); + + *output = std::make_unique<char[]>(header_length); + QuicDataWriter writer(header_length, output->get()); + bool success = + writer.WriteVarInt62(stream_type) && writer.WriteVarInt62(session_id); + if (success && writer.remaining() == 0) { + return header_length; + } + + QUIC_DLOG(ERROR) << "Http encoder failed when attempting to serialize " + "WEBTRANSPORT_STREAM frame header."; + return 0; +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.h b/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.h index 429502b0cbb..0a8aa84e0b7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.h @@ -8,6 +8,7 @@ #include <memory> #include "quic/core/http/http_frames.h" #include "quic/core/quic_error_codes.h" +#include "quic/core/quic_types.h" #include "quic/platform/api/quic_export.h" namespace quic { @@ -32,35 +33,16 @@ class QUIC_EXPORT_PRIVATE HttpEncoder { QuicByteCount payload_length, std::unique_ptr<char[]>* output); - // Serializes a CANCEL_PUSH frame into a new buffer stored in |output|. - // Returns the length of the buffer on success, or 0 otherwise. - static QuicByteCount SerializeCancelPushFrame( - const CancelPushFrame& cancel_push, - std::unique_ptr<char[]>* output); - // Serializes a SETTINGS frame into a new buffer stored in |output|. // Returns the length of the buffer on success, or 0 otherwise. static QuicByteCount SerializeSettingsFrame(const SettingsFrame& settings, std::unique_ptr<char[]>* output); - // Serializes the header and push_id of a PUSH_PROMISE frame into a new buffer - // stored in |output|. Returns the length of the buffer on success, or 0 - // otherwise. - static QuicByteCount SerializePushPromiseFrameWithOnlyPushId( - const PushPromiseFrame& push_promise, - std::unique_ptr<char[]>* output); - // Serializes a GOAWAY frame into a new buffer stored in |output|. // Returns the length of the buffer on success, or 0 otherwise. static QuicByteCount SerializeGoAwayFrame(const GoAwayFrame& goaway, std::unique_ptr<char[]>* output); - // Serializes a MAX_PUSH frame into a new buffer stored in |output|. - // Returns the length of the buffer on success, or 0 otherwise. - static QuicByteCount SerializeMaxPushIdFrame( - const MaxPushIdFrame& max_push_id, - std::unique_ptr<char[]>* output); - // Serializes a PRIORITY_UPDATE frame into a new buffer stored in |output|. // Returns the length of the buffer on success, or 0 otherwise. static QuicByteCount SerializePriorityUpdateFrame( @@ -75,6 +57,12 @@ class QUIC_EXPORT_PRIVATE HttpEncoder { // Serializes a frame with reserved frame type specified in // https://tools.ietf.org/html/draft-ietf-quic-http-25#section-7.2.9. static QuicByteCount SerializeGreasingFrame(std::unique_ptr<char[]>* output); + + // Serializes a WEBTRANSPORT_STREAM frame header as specified in + // https://www.ietf.org/archive/id/draft-ietf-webtrans-http3-00.html#name-client-initiated-bidirectio + static QuicByteCount SerializeWebTransportStreamFrameHeader( + WebTransportSessionId session_id, + std::unique_ptr<char[]>* output); }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_encoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/http_encoder_test.cc index ad25cc40dd5..66e988facca 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_encoder_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_encoder_test.cc @@ -39,22 +39,6 @@ TEST(HttpEncoderTest, SerializeHeadersFrameHeader) { output, ABSL_ARRAYSIZE(output)); } -TEST(HttpEncoderTest, SerializeCancelPushFrame) { - CancelPushFrame cancel_push; - cancel_push.push_id = 0x01; - char output[] = {// type (CANCEL_PUSH) - 0x03, - // length - 0x1, - // Push Id - 0x01}; - std::unique_ptr<char[]> buffer; - uint64_t length = HttpEncoder::SerializeCancelPushFrame(cancel_push, &buffer); - EXPECT_EQ(ABSL_ARRAYSIZE(output), length); - quiche::test::CompareCharArraysWithHexError( - "CANCEL_PUSH", buffer.get(), length, output, ABSL_ARRAYSIZE(output)); -} - TEST(HttpEncoderTest, SerializeSettingsFrame) { SettingsFrame settings; settings.values[1] = 2; @@ -83,24 +67,6 @@ TEST(HttpEncoderTest, SerializeSettingsFrame) { output, ABSL_ARRAYSIZE(output)); } -TEST(HttpEncoderTest, SerializePushPromiseFrameWithOnlyPushId) { - PushPromiseFrame push_promise; - push_promise.push_id = 0x01; - push_promise.headers = "Headers"; - char output[] = {// type (PUSH_PROMISE) - 0x05, - // length - 0x8, - // Push Id - 0x01}; - std::unique_ptr<char[]> buffer; - uint64_t length = HttpEncoder::SerializePushPromiseFrameWithOnlyPushId( - push_promise, &buffer); - EXPECT_EQ(ABSL_ARRAYSIZE(output), length); - quiche::test::CompareCharArraysWithHexError( - "PUSH_PROMISE", buffer.get(), length, output, ABSL_ARRAYSIZE(output)); -} - TEST(HttpEncoderTest, SerializeGoAwayFrame) { GoAwayFrame goaway; goaway.id = 0x1; @@ -117,22 +83,6 @@ TEST(HttpEncoderTest, SerializeGoAwayFrame) { output, ABSL_ARRAYSIZE(output)); } -TEST(HttpEncoderTest, SerializeMaxPushIdFrame) { - MaxPushIdFrame max_push_id; - max_push_id.push_id = 0x1; - char output[] = {// type (MAX_PUSH_ID) - 0x0D, - // length - 0x1, - // Push Id - 0x01}; - std::unique_ptr<char[]> buffer; - uint64_t length = HttpEncoder::SerializeMaxPushIdFrame(max_push_id, &buffer); - EXPECT_EQ(ABSL_ARRAYSIZE(output), length); - quiche::test::CompareCharArraysWithHexError( - "MAX_PUSH_ID", buffer.get(), length, output, ABSL_ARRAYSIZE(output)); -} - TEST(HttpEncoderTest, SerializePriorityUpdateFrame) { PriorityUpdateFrame priority_update1; priority_update1.prioritized_element_type = REQUEST_STREAM; @@ -173,5 +123,18 @@ TEST(HttpEncoderTest, SerializeAcceptChFrame) { output2, ABSL_ARRAYSIZE(output2)); } +TEST(HttpEncoderTest, SerializeWebTransportStreamFrameHeader) { + WebTransportSessionId session_id = 0x17; + char output[] = {0x40, 0x41, // type (WEBTRANSPORT_STREAM) + 0x17}; // session ID + + std::unique_ptr<char[]> buffer; + uint64_t length = + HttpEncoder::SerializeWebTransportStreamFrameHeader(session_id, &buffer); + EXPECT_EQ(sizeof(output), length); + quiche::test::CompareCharArraysWithHexError( + "WEBTRANSPORT_STREAM", buffer.get(), length, output, sizeof(output)); +} + } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/http_frames.h b/chromium/net/third_party/quiche/src/quic/core/http/http_frames.h index 34c3d607c0e..56e53487b24 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/http_frames.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/http_frames.h @@ -16,6 +16,7 @@ #include "absl/strings/string_view.h" #include "quic/core/http/http_constants.h" #include "quic/core/quic_types.h" +#include "spdy/core/spdy_protocol.h" namespace quic { @@ -33,6 +34,8 @@ enum class HttpFrameType { ACCEPT_CH = 0x89, // https://tools.ietf.org/html/draft-ietf-httpbis-priority-02 PRIORITY_UPDATE_REQUEST_STREAM = 0xF0700, + // https://www.ietf.org/archive/id/draft-ietf-webtrans-http3-00.html + WEBTRANSPORT_STREAM = 0x41, }; // 7.2.1. DATA @@ -102,6 +105,7 @@ struct QUIC_EXPORT_PRIVATE SettingsFrame { // // The PUSH_PROMISE frame (type=0x05) is used to carry a request header // set from server to client, as in HTTP/2. +// TODO(b/171463363): Remove. struct QUIC_EXPORT_PRIVATE PushPromiseFrame { PushId push_id; absl::string_view headers; @@ -182,15 +186,7 @@ struct QUIC_EXPORT_PRIVATE PriorityUpdateFrame { // https://tools.ietf.org/html/draft-davidben-http-client-hint-reliability-02 // struct QUIC_EXPORT_PRIVATE AcceptChFrame { - struct QUIC_EXPORT_PRIVATE OriginValuePair { - std::string origin; - std::string value; - bool operator==(const OriginValuePair& rhs) const { - return origin == rhs.origin && value == rhs.value; - } - }; - - std::vector<OriginValuePair> entries; + std::vector<spdy::AcceptChOriginValuePair> entries; bool operator==(const AcceptChFrame& rhs) const { return entries.size() == rhs.entries.size() && diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.cc index a32a6a8dbc9..36011804056 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.cc @@ -98,7 +98,7 @@ QuicAsyncStatus QuicClientPromisedInfo::FinalValidation() { // This shouldn't be possible, as |ClientRequest| guards against // closed stream for the synchronous case. And in the // asynchronous case, a RST can only be caught by |OnAlarm()|. - QUIC_BUG << "missing promised stream" << id_; + QUIC_BUG(quic_bug_10378_1) << "missing promised stream" << id_; } QuicClientPushPromiseIndex::Delegate* delegate = client_request_delegate_; session_->DeletePromised(this); diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list.cc index f7f656754e5..7ac768dcc05 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list.cc @@ -32,7 +32,7 @@ QuicHeaderList& QuicHeaderList::operator=(QuicHeaderList&& other) = default; QuicHeaderList::~QuicHeaderList() {} void QuicHeaderList::OnHeaderBlockStart() { - QUIC_BUG_IF(current_header_list_size_ != 0) + QUIC_BUG_IF(quic_bug_12518_1, current_header_list_size_ != 0) << "OnHeaderBlockStart called more than once!"; } @@ -42,7 +42,7 @@ void QuicHeaderList::OnHeader(absl::string_view name, absl::string_view value) { if (current_header_list_size_ < max_header_list_size_) { current_header_list_size_ += name.size(); current_header_list_size_ += value.size(); - current_header_list_size_ += QpackEntry::kSizeOverhead; + current_header_list_size_ += kQpackEntrySizeOverhead; header_list_.emplace_back(std::string(name), std::string(value)); } } diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream.cc index d8cfbdf3ba8..2b9f485a436 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream.cc @@ -84,8 +84,9 @@ bool QuicHeadersStream::OnStreamFrameAcked(QuicStreamOffset offset, std::min(acked_length, header.full_length - header_offset); if (header.unacked_length < header_length) { - QUIC_BUG << "Unsent stream data is acked. unacked_length: " - << header.unacked_length << " acked_length: " << header_length; + QUIC_BUG(quic_bug_10416_1) + << "Unsent stream data is acked. unacked_length: " + << header.unacked_length << " acked_length: " << header_length; OnUnrecoverableError(QUIC_INTERNAL_ERROR, "Unsent stream data is acked"); return false; diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc index cfccc922688..55dafac3756 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc @@ -73,17 +73,6 @@ using testing::WithArgs; namespace quic { namespace test { - -class MockQuicHpackDebugVisitor : public QuicHpackDebugVisitor { - public: - MockQuicHpackDebugVisitor() : QuicHpackDebugVisitor() {} - MockQuicHpackDebugVisitor(const MockQuicHpackDebugVisitor&) = delete; - MockQuicHpackDebugVisitor& operator=(const MockQuicHpackDebugVisitor&) = - delete; - - MOCK_METHOD(void, OnUseEntry, (QuicTime::Delta elapsed), (override)); -}; - namespace { class MockVisitor : public SpdyFramerVisitorInterface { @@ -766,95 +755,6 @@ TEST_P(QuicHeadersStreamTest, NoConnectionLevelFlowControl) { headers_stream_)); } -TEST_P(QuicHeadersStreamTest, HpackDecoderDebugVisitor) { - auto hpack_decoder_visitor = - std::make_unique<StrictMock<MockQuicHpackDebugVisitor>>(); - { - InSequence seq; - // Number of indexed representations generated in headers below. - for (int i = 1; i < 28; i++) { - EXPECT_CALL(*hpack_decoder_visitor, - OnUseEntry(QuicTime::Delta::FromMilliseconds(i))) - .Times(4); - } - } - QuicSpdySessionPeer::SetHpackDecoderDebugVisitor( - &session_, std::move(hpack_decoder_visitor)); - - // Create some headers we expect to generate entries in HPACK's - // dynamic table, in addition to content-length. - headers_["key0"] = std::string(1 << 1, '.'); - headers_["key1"] = std::string(1 << 2, '.'); - headers_["key2"] = std::string(1 << 3, '.'); - for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_; - stream_id += next_stream_id_) { - for (bool fin : {false, true}) { - for (SpdyPriority priority = 0; priority < 7; ++priority) { - // Replace with "WriteHeadersAndSaveData" - SpdySerializedFrame frame; - if (perspective() == Perspective::IS_SERVER) { - SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); - headers_frame.set_fin(fin); - headers_frame.set_has_priority(true); - headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0)); - frame = framer_->SerializeFrame(headers_frame); - EXPECT_CALL(session_, OnStreamHeadersPriority( - stream_id, spdy::SpdyStreamPrecedence(0))); - } else { - SpdyHeadersIR headers_frame(stream_id, headers_.Clone()); - headers_frame.set_fin(fin); - frame = framer_->SerializeFrame(headers_frame); - } - EXPECT_CALL(session_, - OnStreamHeaderList(stream_id, fin, frame.size(), _)) - .WillOnce(Invoke(this, &QuicHeadersStreamTest::SaveHeaderList)); - stream_frame_.data_buffer = frame.data(); - stream_frame_.data_length = frame.size(); - connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); - headers_stream_->OnStreamFrame(stream_frame_); - stream_frame_.offset += frame.size(); - CheckHeaders(); - } - } - } -} - -TEST_P(QuicHeadersStreamTest, HpackEncoderDebugVisitor) { - auto hpack_encoder_visitor = - std::make_unique<StrictMock<MockQuicHpackDebugVisitor>>(); - if (perspective() == Perspective::IS_SERVER) { - InSequence seq; - for (int i = 1; i < 4; i++) { - EXPECT_CALL(*hpack_encoder_visitor, - OnUseEntry(QuicTime::Delta::FromMilliseconds(i))); - } - } else { - InSequence seq; - for (int i = 1; i < 28; i++) { - EXPECT_CALL(*hpack_encoder_visitor, - OnUseEntry(QuicTime::Delta::FromMilliseconds(i))); - } - } - QuicSpdySessionPeer::SetHpackEncoderDebugVisitor( - &session_, std::move(hpack_encoder_visitor)); - - for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_; - stream_id += next_stream_id_) { - for (bool fin : {false, true}) { - if (perspective() == Perspective::IS_SERVER) { - WriteAndExpectResponseHeaders(stream_id, fin); - connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); - } else { - for (SpdyPriority priority = 0; priority < 7; ++priority) { - // TODO(rch): implement priorities correctly. - WriteAndExpectRequestHeaders(stream_id, fin, 0); - connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); - } - } - } - } -} - TEST_P(QuicHeadersStreamTest, AckSentData) { EXPECT_CALL(session_, WritevData(QuicUtils::GetHeadersStreamId( connection_->transport_version()), 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 35d590286cb..fbd7b2eb18e 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 @@ -233,6 +233,13 @@ bool QuicReceiveControlStream::OnAcceptChFrame(const AcceptChFrame& frame) { return true; } +void QuicReceiveControlStream::OnWebTransportStreamFrameType( + QuicByteCount /*header_length*/, + WebTransportSessionId /*session_id*/) { + QUIC_BUG(WEBTRANSPORT_STREAM on Control Stream) + << "Parsed WEBTRANSPORT_STREAM on a control stream."; +} + bool QuicReceiveControlStream::OnUnknownFrameStart( uint64_t frame_type, QuicByteCount /*header_length*/, @@ -263,8 +270,7 @@ bool QuicReceiveControlStream::ValidateFrameType(HttpFrameType frame_type) { frame_type == HttpFrameType::PUSH_PROMISE) || (spdy_session()->perspective() == Perspective::IS_CLIENT && frame_type == HttpFrameType::MAX_PUSH_ID) || - (GetQuicReloadableFlag(quic_parse_accept_ch_frame) && - spdy_session()->perspective() == Perspective::IS_SERVER && + (spdy_session()->perspective() == Perspective::IS_SERVER && frame_type == HttpFrameType::ACCEPT_CH)) { stream_delegate()->OnStreamError( QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM, diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h index 326dba3e38a..4ed01043662 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h @@ -58,14 +58,14 @@ class QUIC_EXPORT_PRIVATE QuicReceiveControlStream bool OnPriorityUpdateFrame(const PriorityUpdateFrame& frame) override; bool OnAcceptChFrameStart(QuicByteCount header_length) override; bool OnAcceptChFrame(const AcceptChFrame& frame) override; + void OnWebTransportStreamFrameType(QuicByteCount header_length, + WebTransportSessionId session_id) override; bool OnUnknownFrameStart(uint64_t frame_type, QuicByteCount header_length, QuicByteCount payload_length) override; bool OnUnknownFramePayload(absl::string_view payload) override; bool OnUnknownFrameEnd() override; - void SetUnblocked() { sequencer()->SetUnblocked(); } - QuicSpdySession* spdy_session() { return spdy_session_; } private: diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc index 3e4532f4a66..c9133436e9f 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc @@ -4,13 +4,13 @@ #include "quic/core/http/quic_receive_control_stream.h" +#include "absl/memory/memory.h" #include "absl/strings/escaping.h" #include "absl/strings/string_view.h" #include "quic/core/http/http_constants.h" #include "quic/core/qpack/qpack_header_table.h" #include "quic/core/quic_types.h" #include "quic/core/quic_utils.h" -#include "quic/platform/api/quic_ptr_util.h" #include "quic/test_tools/qpack/qpack_encoder_peer.h" #include "quic/test_tools/quic_spdy_session_peer.h" #include "quic/test_tools/quic_stream_peer.h" @@ -106,7 +106,7 @@ class QuicReceiveControlStreamTest : public QuicTestWithParam<TestParams> { stream_ = new TestStream(GetNthClientInitiatedBidirectionalStreamId( GetParam().version.transport_version, 0), &session_); - session_.ActivateStream(QuicWrapUnique(stream_)); + session_.ActivateStream(absl::WrapUnique(stream_)); } Perspective perspective() const { return GetParam().perspective; } @@ -303,14 +303,12 @@ TEST_P(QuicReceiveControlStreamTest, ReceiveGoAwayFrame) { } TEST_P(QuicReceiveControlStreamTest, PushPromiseOnControlStreamShouldClose) { - PushPromiseFrame push_promise; - push_promise.push_id = 0x01; - push_promise.headers = "Headers"; - std::unique_ptr<char[]> buffer; - uint64_t length = HttpEncoder::SerializePushPromiseFrameWithOnlyPushId( - push_promise, &buffer); - QuicStreamFrame frame(receive_control_stream_->id(), false, 1, buffer.get(), - length); + std::string push_promise_frame = absl::HexStringToBytes( + "05" // PUSH_PROMISE + "01" // length + "00"); // push ID + QuicStreamFrame frame(receive_control_stream_->id(), false, 1, + push_promise_frame); EXPECT_CALL( *connection_, CloseConnection(QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM, _, _)) @@ -404,8 +402,7 @@ TEST_P(QuicReceiveControlStreamTest, AcceptChFrameBeforeSettings) { "4089" // type (ACCEPT_CH) "00"); // length - if (GetQuicReloadableFlag(quic_parse_accept_ch_frame) && - perspective() == Perspective::IS_SERVER) { + if (perspective() == Perspective::IS_SERVER) { EXPECT_CALL(*connection_, CloseConnection( QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM, @@ -449,23 +446,17 @@ TEST_P(QuicReceiveControlStreamTest, ReceiveAcceptChFrame) { "4089" // type (ACCEPT_CH) "00"); // length - if (GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { - if (perspective() == Perspective::IS_CLIENT) { - EXPECT_CALL(debug_visitor, OnAcceptChFrameReceived(_)); - } else { - EXPECT_CALL(*connection_, - CloseConnection( - QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM, - "Invalid frame type 137 received on control stream.", _)) - .WillOnce( - Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); - EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _)); - EXPECT_CALL(session_, OnConnectionClosed(_, _)); - } + if (perspective() == Perspective::IS_CLIENT) { + EXPECT_CALL(debug_visitor, OnAcceptChFrameReceived(_)); } else { - EXPECT_CALL(debug_visitor, - OnUnknownFrameReceived(id, /* frame_type = */ 0x89, - /* payload_length = */ 0)); + EXPECT_CALL(*connection_, + CloseConnection( + QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM, + "Invalid frame type 137 received on control stream.", _)) + .WillOnce( + Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); + EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _)); + EXPECT_CALL(session_, OnConnectionClosed(_, _)); } receive_control_stream_->OnStreamFrame( 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 833413039a7..79555e05967 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 @@ -27,7 +27,8 @@ QuicSendControlStream::QuicSendControlStream(QuicStreamId id, spdy_session_(spdy_session) {} void QuicSendControlStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) { - QUIC_BUG << "OnStreamReset() called for write unidirectional stream."; + QUIC_BUG(quic_bug_10382_1) + << "OnStreamReset() called for write unidirectional stream."; } bool QuicSendControlStream::OnStopSending(QuicRstStreamErrorCode /* code */) { @@ -103,24 +104,6 @@ void QuicSendControlStream::WritePriorityUpdate( nullptr); } -void QuicSendControlStream::SendMaxPushIdFrame(PushId max_push_id) { - QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, session()->perspective()); - QuicConnection::ScopedPacketFlusher flusher(session()->connection()); - MaybeSendSettingsFrame(); - - MaxPushIdFrame frame; - frame.push_id = max_push_id; - if (spdy_session_->debug_visitor()) { - spdy_session_->debug_visitor()->OnMaxPushIdFrameSent(frame); - } - - std::unique_ptr<char[]> buffer; - QuicByteCount frame_length = - HttpEncoder::SerializeMaxPushIdFrame(frame, &buffer); - WriteOrBufferData(absl::string_view(buffer.get(), frame_length), - /*fin = */ false, nullptr); -} - void QuicSendControlStream::SendGoAway(QuicStreamId id) { QuicConnection::ScopedPacketFlusher flusher(session()->connection()); MaybeSendSettingsFrame(); 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 155b423ae72..face80632af 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 @@ -37,10 +37,6 @@ class QUIC_EXPORT_PRIVATE QuicSendControlStream : public QuicStream { // first frame sent on this stream. void MaybeSendSettingsFrame(); - // Send a MAX_PUSH_ID frame on this stream, and a SETTINGS frame beforehand if - // one has not been already sent. Must only be called for a client. - void SendMaxPushIdFrame(PushId max_push_id); - // Send a PRIORITY_UPDATE frame on this stream, and a SETTINGS frame // beforehand if one has not been already sent. void WritePriorityUpdate(const PriorityUpdateFrame& priority_update); 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 fe849733c82..d1be0df27ca 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 @@ -14,6 +14,7 @@ #include "quic/test_tools/quic_spdy_session_peer.h" #include "quic/test_tools/quic_test_utils.h" #include "common/platform/api/quiche_text_utils.h" +#include "common/test_tools/quiche_test_utils.h" namespace quic { namespace test { @@ -133,6 +134,25 @@ TEST_P(QuicSendControlStreamTest, WriteSettings) { "4040" // 0x40 as the reserved frame type "01" // 1 byte frame length "61"); // payload "a" + if (GetQuicReloadableFlag(quic_h3_datagram)) { + 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" + } auto buffer = std::make_unique<char[]>(expected_write_data.size()); QuicDataWriter writer(expected_write_data.size(), buffer.get()); @@ -158,8 +178,9 @@ TEST_P(QuicSendControlStreamTest, WriteSettings) { .WillOnce(Invoke(save_write_data)); send_control_stream_->MaybeSendSettingsFrame(); - EXPECT_EQ(expected_write_data, - absl::string_view(writer.data(), writer.length())); + quiche::test::CompareCharArraysWithHexError( + "settings", writer.data(), writer.length(), expected_write_data.data(), + expected_write_data.length()); } TEST_P(QuicSendControlStreamTest, WriteSettingsOnlyOnce) { diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_server_initiated_spdy_stream.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_initiated_spdy_stream.cc new file mode 100644 index 00000000000..37a53898d01 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_initiated_spdy_stream.cc @@ -0,0 +1,43 @@ +// 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/quic_server_initiated_spdy_stream.h" +#include "quic/core/quic_error_codes.h" + +namespace quic { + +void QuicServerInitiatedSpdyStream::OnBodyAvailable() { + QUIC_BUG(Body received in QuicServerInitiatedSpdyStream) + << "Received body data in QuicServerInitiatedSpdyStream."; + OnUnrecoverableError( + QUIC_INTERNAL_ERROR, + "Received HTTP/3 body data in a server-initiated bidirectional stream"); +} + +size_t QuicServerInitiatedSpdyStream::WriteHeaders( + spdy::SpdyHeaderBlock /*header_block*/, + bool /*fin*/, + QuicReferenceCountedPointer<QuicAckListenerInterface> /*ack_listener*/) { + QUIC_BUG(Writing headers in QuicServerInitiatedSpdyStream) + << "Attempting to write headers in QuicServerInitiatedSpdyStream"; + OnUnrecoverableError(QUIC_INTERNAL_ERROR, + "Attempted to send HTTP/3 headers in a server-initiated " + "bidirectional stream"); + return 0; +} + +void QuicServerInitiatedSpdyStream::OnInitialHeadersComplete( + bool /*fin*/, + size_t /*frame_len*/, + const QuicHeaderList& /*header_list*/) { + QUIC_PEER_BUG(Reading headers in QuicServerInitiatedSpdyStream) + << "Attempting to receive headers in QuicServerInitiatedSpdyStream"; + + OnUnrecoverableError(IETF_QUIC_PROTOCOL_VIOLATION, + "Received HTTP/3 headers in a server-initiated " + "bidirectional stream without an extension setting " + "explicitly allowing those"); +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_server_initiated_spdy_stream.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_initiated_spdy_stream.h new file mode 100644 index 00000000000..a13dc65cd13 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_initiated_spdy_stream.h @@ -0,0 +1,32 @@ +// 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_QUIC_CORE_HTTP_QUIC_SERVER_INITIATED_SPDY_STREAM_H_ +#define QUICHE_QUIC_CORE_HTTP_QUIC_SERVER_INITIATED_SPDY_STREAM_H_ + +#include "quic/core/http/quic_spdy_stream.h" + +namespace quic { + +// QuicServerInitiatedSpdyStream is a subclass of QuicSpdyStream meant to handle +// WebTransport traffic on server-initiated bidirectional streams. Receiving or +// sending any other traffic on this stream will result in a CONNECTION_CLOSE. +class QUIC_EXPORT_PRIVATE QuicServerInitiatedSpdyStream + : public QuicSpdyStream { + public: + using QuicSpdyStream::QuicSpdyStream; + + void OnBodyAvailable() override; + size_t WriteHeaders(spdy::SpdyHeaderBlock header_block, + bool fin, + QuicReferenceCountedPointer<QuicAckListenerInterface> + ack_listener) override; + void OnInitialHeadersComplete(bool fin, + size_t frame_len, + const QuicHeaderList& header_list) override; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SERVER_INITIATED_SPDY_STREAM_H_ 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 f91146b3b49..b955835e2b8 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 @@ -40,9 +40,7 @@ void QuicServerSessionBase::Initialize() { crypto_stream_ = CreateQuicCryptoServerStream(crypto_config_, compressed_certs_cache_); QuicSpdySession::Initialize(); - if (GetQuicRestartFlag(quic_enable_zero_rtt_for_tls_v2)) { - SendSettingsToCryptoStream(); - } + SendSettingsToCryptoStream(); } void QuicServerSessionBase::OnConfigNegotiated() { @@ -173,9 +171,10 @@ void QuicServerSessionBase::OnCongestionWindowChange(QuicTime now) { bandwidth_estimate_sent_to_client_); const int32_t max_bw_estimate_bytes_per_second = BandwidthToCachedParameterBytesPerSecond(max_bandwidth_estimate); - QUIC_BUG_IF(max_bw_estimate_bytes_per_second < 0) + QUIC_BUG_IF(quic_bug_12513_1, max_bw_estimate_bytes_per_second < 0) << max_bw_estimate_bytes_per_second; - QUIC_BUG_IF(bw_estimate_bytes_per_second < 0) << bw_estimate_bytes_per_second; + QUIC_BUG_IF(quic_bug_10393_1, bw_estimate_bytes_per_second < 0) + << bw_estimate_bytes_per_second; CachedNetworkParameters cached_network_params; cached_network_params.set_bandwidth_estimate_bytes_per_second( @@ -207,13 +206,15 @@ void QuicServerSessionBase::OnCongestionWindowChange(QuicTime now) { bool QuicServerSessionBase::ShouldCreateIncomingStream(QuicStreamId id) { if (!connection()->connected()) { - QUIC_BUG << "ShouldCreateIncomingStream called when disconnected"; + QUIC_BUG(quic_bug_10393_2) + << "ShouldCreateIncomingStream called when disconnected"; return false; } if (QuicUtils::IsServerInitiatedStreamId(transport_version(), id)) { - QUIC_BUG << "ShouldCreateIncomingStream called with server initiated " - "stream ID."; + QUIC_BUG(quic_bug_10393_3) + << "ShouldCreateIncomingStream called with server initiated " + "stream ID."; return false; } @@ -229,12 +230,13 @@ bool QuicServerSessionBase::ShouldCreateIncomingStream(QuicStreamId id) { bool QuicServerSessionBase::ShouldCreateOutgoingBidirectionalStream() { if (!connection()->connected()) { - QUIC_BUG + QUIC_BUG(quic_bug_12513_2) << "ShouldCreateOutgoingBidirectionalStream called when disconnected"; return false; } if (!crypto_stream_->encryption_established()) { - QUIC_BUG << "Encryption not established so no outgoing stream created."; + QUIC_BUG(quic_bug_10393_4) + << "Encryption not established so no outgoing stream created."; return false; } @@ -243,12 +245,13 @@ bool QuicServerSessionBase::ShouldCreateOutgoingBidirectionalStream() { bool QuicServerSessionBase::ShouldCreateOutgoingUnidirectionalStream() { if (!connection()->connected()) { - QUIC_BUG + QUIC_BUG(quic_bug_12513_3) << "ShouldCreateOutgoingUnidirectionalStream called when disconnected"; return false; } if (!crypto_stream_->encryption_established()) { - QUIC_BUG << "Encryption not established so no outgoing stream created."; + QUIC_BUG(quic_bug_10393_5) + << "Encryption not established so no outgoing stream created."; return false; } 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 f9483fefeeb..29750f2e3cf 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 @@ -9,6 +9,7 @@ #include <string> #include <utility> +#include "absl/memory/memory.h" #include "absl/strings/string_view.h" #include "quic/core/crypto/null_encrypter.h" #include "quic/core/crypto/quic_crypto_server_config.h" @@ -21,7 +22,6 @@ #include "quic/core/tls_server_handshaker.h" #include "quic/platform/api/quic_expect_bug.h" #include "quic/platform/api/quic_flags.h" -#include "quic/platform/api/quic_ptr_util.h" #include "quic/platform/api/quic_socket_address.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/crypto_test_utils.h" @@ -83,14 +83,14 @@ class TestServerSession : public QuicServerSessionBase { } QuicSpdyStream* stream = new QuicSimpleServerStream( id, this, BIDIRECTIONAL, quic_simple_server_backend_); - ActivateStream(QuicWrapUnique(stream)); + ActivateStream(absl::WrapUnique(stream)); return stream; } QuicSpdyStream* CreateIncomingStream(PendingStream* pending) override { QuicSpdyStream* stream = new QuicSimpleServerStream( pending, this, BIDIRECTIONAL, quic_simple_server_backend_); - ActivateStream(QuicWrapUnique(stream)); + ActivateStream(absl::WrapUnique(stream)); return stream; } @@ -107,7 +107,7 @@ class TestServerSession : public QuicServerSessionBase { QuicSpdyStream* stream = new QuicSimpleServerStream( GetNextOutgoingUnidirectionalStreamId(), this, WRITE_UNIDIRECTIONAL, quic_simple_server_backend_); - ActivateStream(QuicWrapUnique(stream)); + 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 ba5b457052e..b71cba9958f 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 @@ -7,7 +7,9 @@ #include <string> #include <utility> +#include "absl/memory/memory.h" #include "quic/core/crypto/crypto_protocol.h" +#include "quic/core/http/quic_server_initiated_spdy_stream.h" #include "quic/core/http/quic_spdy_client_stream.h" #include "quic/core/http/spdy_utils.h" #include "quic/core/quic_server_id.h" @@ -16,7 +18,6 @@ #include "quic/platform/api/quic_flag_utils.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" -#include "quic/platform/api/quic_ptr_util.h" namespace quic { @@ -66,7 +67,8 @@ bool QuicSpdyClientSession::ShouldCreateOutgoingBidirectionalStream() { } bool QuicSpdyClientSession::ShouldCreateOutgoingUnidirectionalStream() { - QUIC_BUG << "Try to create outgoing unidirectional client data streams"; + QUIC_BUG(quic_bug_10396_1) + << "Try to create outgoing unidirectional client data streams"; return false; } @@ -83,7 +85,8 @@ QuicSpdyClientSession::CreateOutgoingBidirectionalStream() { QuicSpdyClientStream* QuicSpdyClientSession::CreateOutgoingUnidirectionalStream() { - QUIC_BUG << "Try to create outgoing unidirectional client data streams"; + QUIC_BUG(quic_bug_10396_2) + << "Try to create outgoing unidirectional client data streams"; return nullptr; } @@ -102,6 +105,18 @@ const QuicCryptoClientStreamBase* QuicSpdyClientSession::GetCryptoStream() return crypto_stream_.get(); } +bool QuicSpdyClientSession::IsKnownServerAddress( + const QuicSocketAddress& address) const { + return std::find(known_server_addresses_.cbegin(), + known_server_addresses_.cend(), + address) != known_server_addresses_.cend(); +} + +void QuicSpdyClientSession::AddKnownServerAddress( + const QuicSocketAddress& address) { + known_server_addresses_.push_back(address); +} + void QuicSpdyClientSession::CryptoConnect() { QUICHE_DCHECK(flow_controller()); crypto_stream_->CryptoConnect(); @@ -125,7 +140,8 @@ int QuicSpdyClientSession::GetNumReceivedServerConfigUpdates() const { bool QuicSpdyClientSession::ShouldCreateIncomingStream(QuicStreamId id) { if (!connection()->connected()) { - QUIC_BUG << "ShouldCreateIncomingStream called when disconnected"; + QUIC_BUG(quic_bug_10396_3) + << "ShouldCreateIncomingStream called when disconnected"; return false; } if (goaway_received() && respect_goaway_) { @@ -135,8 +151,9 @@ bool QuicSpdyClientSession::ShouldCreateIncomingStream(QuicStreamId id) { } if (QuicUtils::IsClientInitiatedStreamId(transport_version(), id)) { - QUIC_BUG << "ShouldCreateIncomingStream called with client initiated " - "stream ID."; + QUIC_BUG(quic_bug_10396_4) + << "ShouldCreateIncomingStream called with client initiated " + "stream ID."; return false; } @@ -150,7 +167,8 @@ bool QuicSpdyClientSession::ShouldCreateIncomingStream(QuicStreamId id) { } if (VersionHasIetfQuicFrames(transport_version()) && - QuicUtils::IsBidirectionalStreamId(id, version())) { + QuicUtils::IsBidirectionalStreamId(id, version()) && + !WillNegotiateWebTransport()) { connection()->CloseConnection( QUIC_HTTP_SERVER_INITIATED_BIDIRECTIONAL_STREAM, "Server created bidirectional stream.", @@ -165,7 +183,7 @@ QuicSpdyStream* QuicSpdyClientSession::CreateIncomingStream( PendingStream* pending) { QuicSpdyStream* stream = new QuicSpdyClientStream(pending, this, READ_UNIDIRECTIONAL); - ActivateStream(QuicWrapUnique(stream)); + ActivateStream(absl::WrapUnique(stream)); return stream; } @@ -173,9 +191,17 @@ QuicSpdyStream* QuicSpdyClientSession::CreateIncomingStream(QuicStreamId id) { if (!ShouldCreateIncomingStream(id)) { return nullptr; } - QuicSpdyStream* stream = - new QuicSpdyClientStream(id, this, READ_UNIDIRECTIONAL); - ActivateStream(QuicWrapUnique(stream)); + QuicSpdyStream* stream; + if (version().UsesHttp3() && + QuicUtils::IsBidirectionalStreamId(id, version())) { + QUIC_BUG_IF(QuicServerInitiatedSpdyStream but no WebTransport support, + !WillNegotiateWebTransport()) + << "QuicServerInitiatedSpdyStream created but no WebTransport support"; + stream = new QuicServerInitiatedSpdyStream(id, this, BIDIRECTIONAL); + } else { + stream = new QuicSpdyClientStream(id, this, READ_UNIDIRECTIONAL); + } + ActivateStream(absl::WrapUnique(stream)); return stream; } diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h index c21eeea4c81..88df049e06d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h @@ -20,7 +20,8 @@ namespace quic { class QuicConnection; class QuicServerId; -class QUIC_NO_EXPORT QuicSpdyClientSession : public QuicSpdyClientSessionBase { +class QUIC_EXPORT_PRIVATE QuicSpdyClientSession + : public QuicSpdyClientSessionBase { public: // Takes ownership of |connection|. Caller retains ownership of // |promised_by_url|. @@ -41,6 +42,9 @@ class QUIC_NO_EXPORT QuicSpdyClientSession : public QuicSpdyClientSessionBase { QuicSpdyClientStream* CreateOutgoingUnidirectionalStream() override; QuicCryptoClientStreamBase* GetMutableCryptoStream() override; const QuicCryptoClientStreamBase* GetCryptoStream() const override; + bool IsKnownServerAddress(const QuicSocketAddress& address) const override; + + void AddKnownServerAddress(const QuicSocketAddress& address); bool IsAuthorized(const std::string& authority) override; @@ -106,6 +110,8 @@ class QUIC_NO_EXPORT QuicSpdyClientSession : public QuicSpdyClientSessionBase { std::unique_ptr<QuicCryptoClientStreamBase> crypto_stream_; QuicServerId server_id_; QuicCryptoClientConfig* crypto_config_; + // Server addresses that are known to the client. + std::vector<QuicSocketAddress> known_server_addresses_; // If this is set to false, the client will ignore server GOAWAYs and allow // the creation of streams regardless of the high chance they will fail. diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc index 49f0198a041..56a976442c9 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc @@ -142,7 +142,7 @@ bool QuicSpdyClientSessionBase::HandlePromised(QuicStreamId /* associated_id */, if (GetPromisedById(promised_id)) { // OnPromiseHeadersComplete() would have closed the connection if // promised id is a duplicate. - QUIC_BUG << "Duplicate promise for id " << promised_id; + QUIC_BUG(quic_bug_10412_1) << "Duplicate promise for id " << promised_id; return false; } diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc index e2b5003bc4a..7197c8ab7ed 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc @@ -10,6 +10,7 @@ #include <vector> #include "absl/base/macros.h" +#include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "quic/core/crypto/null_decrypter.h" @@ -25,7 +26,6 @@ #include "quic/core/tls_client_handshaker.h" #include "quic/platform/api/quic_expect_bug.h" #include "quic/platform/api/quic_flags.h" -#include "quic/platform/api/quic_ptr_util.h" #include "quic/platform/api/quic_socket_address.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/crypto_test_utils.h" @@ -83,7 +83,7 @@ class TestQuicSpdyClientSession : public QuicSpdyClientSession { } MockQuicSpdyClientStream* stream = new MockQuicSpdyClientStream(id, this, READ_UNIDIRECTIONAL); - ActivateStream(QuicWrapUnique(stream)); + ActivateStream(absl::WrapUnique(stream)); return stream; } }; @@ -97,7 +97,6 @@ class QuicSpdyClientSessionTest : public QuicTestWithParam<ParsedQuicVersion> { QuicUtils::GetInvalidStreamId(GetParam().transport_version)) { auto client_cache = std::make_unique<test::SimpleSessionCache>(); client_session_cache_ = client_cache.get(); - SetQuicRestartFlag(quic_enable_zero_rtt_for_tls_v2, true); client_crypto_config_ = std::make_unique<QuicCryptoClientConfig>( crypto_test_utils::ProofVerifierForTesting(), std::move(client_cache)); server_crypto_config_ = crypto_test_utils::CryptoServerConfigForTesting(); @@ -596,13 +595,13 @@ TEST_P(QuicSpdyClientSessionTest, InvalidFramedPacketReceived) { } TEST_P(QuicSpdyClientSessionTest, PushPromiseOnPromiseHeaders) { - // Initialize crypto before the client session will create a stream. - CompleteCryptoHandshake(); - if (VersionHasIetfQuicFrames(connection_->transport_version())) { - session_->SetMaxPushId(10); + return; } + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + MockQuicSpdyClientStream* stream = static_cast<MockQuicSpdyClientStream*>( session_->CreateOutgoingBidirectionalStream()); @@ -612,6 +611,10 @@ TEST_P(QuicSpdyClientSessionTest, PushPromiseOnPromiseHeaders) { } TEST_P(QuicSpdyClientSessionTest, PushPromiseStreamIdTooHigh) { + if (VersionHasIetfQuicFrames(connection_->transport_version())) { + return; + } + // Initialize crypto before the client session will create a stream. CompleteCryptoHandshake(); QuicStreamId stream_id = @@ -628,18 +631,6 @@ TEST_P(QuicSpdyClientSessionTest, PushPromiseStreamIdTooHigh) { headers.OnHeader(":scheme", "https"); headers.OnHeaderBlockEnd(0, 0); - if (VersionHasIetfQuicFrames(connection_->transport_version())) { - session_->SetMaxPushId(10); - // TODO(b/136295430) Use PushId to represent Push IDs instead of - // QuicStreamId. - EXPECT_CALL( - *connection_, - CloseConnection(QUIC_INVALID_STREAM_ID, - "Received push stream id higher than MAX_PUSH_ID.", _)); - const PushId promise_id = 11; - session_->OnPromiseHeaderList(stream_id, promise_id, 0, headers); - return; - } const QuicStreamId promise_id = GetNthServerInitiatedUnidirectionalStreamId( connection_->transport_version(), 11); session_->OnPromiseHeaderList(stream_id, promise_id, 0, headers); @@ -661,13 +652,13 @@ TEST_P(QuicSpdyClientSessionTest, PushPromiseOnPromiseHeadersAlreadyClosed) { } TEST_P(QuicSpdyClientSessionTest, PushPromiseOutOfOrder) { - // Initialize crypto before the client session will create a stream. - CompleteCryptoHandshake(); - if (VersionHasIetfQuicFrames(connection_->transport_version())) { - session_->SetMaxPushId(10); + return; } + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + MockQuicSpdyClientStream* stream = static_cast<MockQuicSpdyClientStream*>( session_->CreateOutgoingBidirectionalStream()); @@ -930,6 +921,10 @@ TEST_P(QuicSpdyClientSessionTest, } TEST_P(QuicSpdyClientSessionTest, TooManyPushPromises) { + if (VersionHasIetfQuicFrames(connection_->transport_version())) { + return; + } + // Initialize crypto before the client session will create a stream. CompleteCryptoHandshake(); QuicStreamId stream_id = @@ -938,10 +933,6 @@ TEST_P(QuicSpdyClientSessionTest, TooManyPushPromises) { session_.get(), std::make_unique<QuicSpdyClientStream>( stream_id, session_.get(), BIDIRECTIONAL)); - if (VersionHasIetfQuicFrames(connection_->transport_version())) { - session_->SetMaxPushId(kMaxQuicStreamId); - } - EXPECT_CALL(*connection_, OnStreamReset(_, QUIC_REFUSED_STREAM)); for (size_t promise_count = 0; promise_count <= session_->get_max_promises(); @@ -1268,119 +1259,6 @@ TEST_P(QuicSpdyClientSessionTest, connection_, crypto_stream_, AlpnForVersion(connection_->version())); } -TEST_P(QuicSpdyClientSessionTest, SetMaxPushIdBeforeEncryptionEstablished) { - // 0-RTT is TLS-only, MAX_PUSH_ID frame is HTTP/3-only. - if (!session_->version().UsesTls() || !session_->version().UsesHttp3()) { - return; - } - - CompleteFirstConnection(); - - CreateConnection(); - StrictMock<MockHttp3DebugVisitor> debug_visitor; - session_->set_debug_visitor(&debug_visitor); - - // No MAX_PUSH_ID frame is sent before encryption is established. - session_->SetMaxPushId(5); - - EXPECT_FALSE(session_->IsEncryptionEstablished()); - EXPECT_FALSE(session_->OneRttKeysAvailable()); - EXPECT_EQ(ENCRYPTION_INITIAL, session_->connection()->encryption_level()); - - // MAX_PUSH_ID frame is sent upon encryption establishment with the value set - // by the earlier SetMaxPushId() call. - EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_)); - EXPECT_CALL(debug_visitor, OnMaxPushIdFrameSent(_)) - .WillOnce(Invoke( - [](const MaxPushIdFrame& frame) { EXPECT_EQ(5u, frame.push_id); })); - session_->CryptoConnect(); - testing::Mock::VerifyAndClearExpectations(&debug_visitor); - - EXPECT_TRUE(session_->IsEncryptionEstablished()); - EXPECT_FALSE(session_->OneRttKeysAvailable()); - EXPECT_EQ(ENCRYPTION_ZERO_RTT, session_->connection()->encryption_level()); - - // Another SetMaxPushId() call with the same value does not trigger sending - // another MAX_PUSH_ID frame. - session_->SetMaxPushId(5); - - // Calling SetMaxPushId() with a different value results in sending another - // MAX_PUSH_ID frame. - EXPECT_CALL(debug_visitor, OnMaxPushIdFrameSent(_)) - .WillOnce(Invoke( - [](const MaxPushIdFrame& frame) { EXPECT_EQ(10u, frame.push_id); })); - session_->SetMaxPushId(10); - testing::Mock::VerifyAndClearExpectations(&debug_visitor); - - QuicConfig config = DefaultQuicConfig(); - crypto_test_utils::HandshakeWithFakeServer( - &config, server_crypto_config_.get(), &helper_, &alarm_factory_, - connection_, crypto_stream_, AlpnForVersion(connection_->version())); - - EXPECT_TRUE(session_->IsEncryptionEstablished()); - EXPECT_TRUE(session_->OneRttKeysAvailable()); - EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, - session_->connection()->encryption_level()); - EXPECT_TRUE(session_->GetCryptoStream()->IsResumption()); -} - -TEST_P(QuicSpdyClientSessionTest, SetMaxPushIdAfterEncryptionEstablished) { - // 0-RTT is TLS-only, MAX_PUSH_ID frame is HTTP/3-only. - if (!session_->version().UsesTls() || !session_->version().UsesHttp3()) { - return; - } - - CompleteFirstConnection(); - - CreateConnection(); - StrictMock<MockHttp3DebugVisitor> debug_visitor; - session_->set_debug_visitor(&debug_visitor); - - EXPECT_FALSE(session_->IsEncryptionEstablished()); - EXPECT_FALSE(session_->OneRttKeysAvailable()); - EXPECT_EQ(ENCRYPTION_INITIAL, session_->connection()->encryption_level()); - - // No MAX_PUSH_ID frame is sent if SetMaxPushId() has not been called. - EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_)); - session_->CryptoConnect(); - testing::Mock::VerifyAndClearExpectations(&debug_visitor); - - EXPECT_TRUE(session_->IsEncryptionEstablished()); - EXPECT_FALSE(session_->OneRttKeysAvailable()); - EXPECT_EQ(ENCRYPTION_ZERO_RTT, session_->connection()->encryption_level()); - - // Calling SetMaxPushId() for the first time after encryption is established - // results in sending a MAX_PUSH_ID frame. - EXPECT_CALL(debug_visitor, OnMaxPushIdFrameSent(_)) - .WillOnce(Invoke( - [](const MaxPushIdFrame& frame) { EXPECT_EQ(5u, frame.push_id); })); - session_->SetMaxPushId(5); - testing::Mock::VerifyAndClearExpectations(&debug_visitor); - - // Another SetMaxPushId() call with the same value does not trigger sending - // another MAX_PUSH_ID frame. - session_->SetMaxPushId(5); - - // Calling SetMaxPushId() with a different value results in sending another - // MAX_PUSH_ID frame. - EXPECT_CALL(debug_visitor, OnMaxPushIdFrameSent(_)) - .WillOnce(Invoke( - [](const MaxPushIdFrame& frame) { EXPECT_EQ(10u, frame.push_id); })); - session_->SetMaxPushId(10); - testing::Mock::VerifyAndClearExpectations(&debug_visitor); - - QuicConfig config = DefaultQuicConfig(); - crypto_test_utils::HandshakeWithFakeServer( - &config, server_crypto_config_.get(), &helper_, &alarm_factory_, - connection_, crypto_stream_, AlpnForVersion(connection_->version())); - - EXPECT_TRUE(session_->IsEncryptionEstablished()); - EXPECT_TRUE(session_->OneRttKeysAvailable()); - EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, - session_->connection()->encryption_level()); - EXPECT_TRUE(session_->GetCryptoStream()->IsResumption()); -} - TEST_P(QuicSpdyClientSessionTest, BadSettingsInZeroRttResumption) { if (!session_->version().UsesHttp3()) { return; 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 931bfb3fdcb..d264ba64879 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 @@ -10,6 +10,7 @@ #include "quic/core/http/quic_client_promised_info.h" #include "quic/core/http/quic_spdy_client_session.h" #include "quic/core/http/spdy_utils.h" +#include "quic/core/http/web_transport_http3.h" #include "quic/core/quic_alarm.h" #include "quic/platform/api/quic_logging.h" #include "spdy/core/spdy_protocol.h" @@ -58,6 +59,15 @@ void QuicSpdyClientStream::OnInitialHeadersComplete( return; } + 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); + return; + } + } + if (!ParseHeaderStatusCode(response_headers_, &response_code_)) { QUIC_DLOG(ERROR) << "Received invalid response code: " << response_headers_[":status"].as_string() 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 b62f49639eb..231846508e4 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 @@ -19,7 +19,7 @@ class QuicSpdyClientSession; // All this does right now is send an SPDY request, and aggregate the // SPDY response. -class QUIC_NO_EXPORT QuicSpdyClientStream : public QuicSpdyStream { +class QUIC_EXPORT_PRIVATE QuicSpdyClientStream : public QuicSpdyStream { public: QuicSpdyClientStream(QuicStreamId id, QuicSpdyClientSession* session, diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream_test.cc index c1ad5c6a17c..fa4943dcda7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream_test.cc @@ -8,6 +8,7 @@ #include <string> #include <utility> +#include "absl/strings/str_cat.h" #include "quic/core/crypto/null_encrypter.h" #include "quic/core/http/quic_spdy_client_session.h" #include "quic/core/http/spdy_utils.h" @@ -18,7 +19,6 @@ #include "quic/test_tools/crypto_test_utils.h" #include "quic/test_tools/quic_spdy_session_peer.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" using spdy::SpdyHeaderBlock; using testing::_; @@ -283,8 +283,7 @@ TEST_P(QuicSpdyClientStreamTest, ReceivingTrailers) { // promised by the final offset field. SpdyHeaderBlock trailer_block; trailer_block["trailer key"] = "trailer value"; - trailer_block[kFinalOffsetHeaderKey] = - quiche::QuicheTextUtils::Uint64ToString(body_.size()); + trailer_block[kFinalOffsetHeaderKey] = absl::StrCat(body_.size()); auto trailers = AsHeaderList(trailer_block); stream_->OnStreamHeaderList(true, trailers.uncompressed_header_bytes(), trailers); 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 bada26378f4..fe630de5108 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 @@ -4,8 +4,8 @@ #include "quic/core/http/quic_spdy_server_stream_base.h" +#include "absl/memory/memory.h" #include "quic/core/crypto/null_encrypter.h" -#include "quic/platform/api/quic_ptr_util.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_spdy_session_peer.h" #include "quic/test_tools/quic_stream_peer.h" @@ -41,7 +41,7 @@ class QuicSpdyServerStreamBaseTest : public QuicTest { new TestQuicSpdyServerStream(GetNthClientInitiatedBidirectionalStreamId( session_.transport_version(), 0), &session_, BIDIRECTIONAL); - session_.ActivateStream(QuicWrapUnique(stream_)); + session_.ActivateStream(absl::WrapUnique(stream_)); helper_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); } 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 91530d36443..16948101c6c 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 @@ -7,6 +7,7 @@ #include <algorithm> #include <cstdint> #include <limits> +#include <memory> #include <string> #include <utility> @@ -18,6 +19,7 @@ #include "quic/core/http/http_decoder.h" #include "quic/core/http/http_frames.h" #include "quic/core/http/quic_headers_stream.h" +#include "quic/core/http/web_transport_http3.h" #include "quic/core/quic_error_codes.h" #include "quic/core/quic_types.h" #include "quic/core/quic_utils.h" @@ -55,39 +57,13 @@ using spdy::SpdyStreamId; namespace quic { +ABSL_CONST_INIT const size_t kMaxUnassociatedWebTransportStreams = 24; + namespace { #define ENDPOINT \ (perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ") -class HeaderTableDebugVisitor : public HpackHeaderTable::DebugVisitorInterface { - public: - HeaderTableDebugVisitor(const QuicClock* clock, - std::unique_ptr<QuicHpackDebugVisitor> visitor) - : clock_(clock), headers_stream_hpack_visitor_(std::move(visitor)) {} - HeaderTableDebugVisitor(const HeaderTableDebugVisitor&) = delete; - HeaderTableDebugVisitor& operator=(const HeaderTableDebugVisitor&) = delete; - - int64_t OnNewEntry(const HpackEntry& entry) override { - QUIC_DVLOG(1) << entry.GetDebugString(); - return (clock_->ApproximateNow() - QuicTime::Zero()).ToMicroseconds(); - } - - void OnUseEntry(const HpackEntry& entry) override { - const QuicTime::Delta elapsed( - clock_->ApproximateNow() - - QuicTime::Delta::FromMicroseconds(entry.time_added()) - - QuicTime::Zero()); - QUIC_DVLOG(1) << entry.GetDebugString() << " " << elapsed.ToMilliseconds() - << " ms"; - headers_stream_hpack_visitor_->OnUseEntry(elapsed); - } - - private: - const QuicClock* clock_; - std::unique_ptr<QuicHpackDebugVisitor> headers_stream_hpack_visitor_; -}; - // Class to forward ACCEPT_CH frame to QuicSpdySession, // and ignore every other frame. class AlpsFrameDecoder : public HttpDecoder::Visitor { @@ -112,7 +88,17 @@ class AlpsFrameDecoder : public HttpDecoder::Visitor { bool OnSettingsFrameStart(QuicByteCount /*header_length*/) override { return true; } - bool OnSettingsFrame(const SettingsFrame& /*frame*/) override { return true; } + bool OnSettingsFrame(const SettingsFrame& frame) override { + if (settings_frame_received_via_alps_) { + error_detail_ = "multiple SETTINGS frames"; + return false; + } + + settings_frame_received_via_alps_ = true; + + error_detail_ = session_->OnSettingsFrameViaAlps(frame); + return !error_detail_; + } bool OnDataFrameStart(QuicByteCount /*header_length*/, QuicByteCount /*payload_length*/) override { error_detail_ = "DATA frame forbidden"; @@ -174,6 +160,11 @@ class AlpsFrameDecoder : public HttpDecoder::Visitor { session_->OnAcceptChFrameReceivedViaAlps(frame); return true; } + void OnWebTransportStreamFrameType( + QuicByteCount /*header_length*/, + WebTransportSessionId /*session_id*/) override { + QUICHE_NOTREACHED(); + } bool OnUnknownFrameStart(uint64_t /*frame_type*/, QuicByteCount /*header_length*/, @@ -192,6 +183,9 @@ class AlpsFrameDecoder : public HttpDecoder::Visitor { private: QuicSpdySession* const session_; absl::optional<std::string> error_detail_; + + // True if SETTINGS frame has been received via ALPS. + bool settings_frame_received_via_alps_ = false; }; } // namespace @@ -366,7 +360,8 @@ class QuicSpdySession::SpdyFramerVisitor return; } - QUIC_BUG_IF(session_->destruction_indicator() != 123456789) + QUIC_BUG_IF(quic_bug_12477_1, + session_->destruction_indicator() != 123456789) << "QuicSpdyStream use after free. " << session_->destruction_indicator() << QuicStackTrace(); @@ -430,7 +425,7 @@ class QuicSpdySession::SpdyFramerVisitor size_t payload_len, size_t frame_len) override { if (payload_len == 0) { - QUIC_BUG << "Zero payload length."; + QUIC_BUG(quic_bug_10360_1) << "Zero payload length."; return; } int compression_pct = 100 - (100 * frame_len) / payload_len; @@ -460,10 +455,6 @@ class QuicSpdySession::SpdyFramerVisitor QuicHeaderList header_list_; }; -QuicHpackDebugVisitor::QuicHpackDebugVisitor() {} - -QuicHpackDebugVisitor::~QuicHpackDebugVisitor() {} - Http3DebugVisitor::Http3DebugVisitor() {} Http3DebugVisitor::~Http3DebugVisitor() {} @@ -481,8 +472,10 @@ QuicSpdySession::QuicSpdySession( supported_versions, /*num_expected_unidirectional_static_streams = */ VersionUsesHttp3(connection->transport_version()) - ? kHttp3StaticUnidirectionalStreamCount - : 0), + ? static_cast<QuicStreamCount>( + kHttp3StaticUnidirectionalStreamCount) + : 0u, + std::make_unique<DatagramObserver>(this)), send_control_stream_(nullptr), receive_control_stream_(nullptr), qpack_encoder_receive_stream_(nullptr), @@ -505,22 +498,17 @@ QuicSpdySession::QuicSpdySession( debug_visitor_(nullptr), destruction_indicator_(123456789), server_push_enabled_(true), - ietf_server_push_enabled_( - GetQuicFlag(FLAGS_quic_enable_http3_server_push)), - http3_max_push_id_sent_(false), - goaway_with_max_stream_id_( - GetQuicReloadableFlag(quic_goaway_with_max_stream_id)) { - if (goaway_with_max_stream_id_) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_goaway_with_max_stream_id, 1, 2); - } + next_available_datagram_flow_id_(perspective() == Perspective::IS_SERVER + ? kFirstDatagramFlowIdServer + : kFirstDatagramFlowIdClient) { 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()); } QuicSpdySession::~QuicSpdySession() { - QUIC_BUG_IF(destruction_indicator_ != 123456789) - << "QuicSpdyStream use after free. " << destruction_indicator_ + QUIC_BUG_IF(quic_bug_12477_2, destruction_indicator_ != 123456789) + << "QuicSpdySession use after free. " << destruction_indicator_ << QuicStackTrace(); destruction_indicator_ = 987654321; } @@ -566,6 +554,13 @@ void QuicSpdySession::FillSettingsFrame() { qpack_maximum_blocked_streams_; settings_.values[SETTINGS_MAX_FIELD_SECTION_SIZE] = max_inbound_header_list_size_; + if (ShouldNegotiateHttp3Datagram() && version().UsesHttp3()) { + QUIC_RELOADABLE_FLAG_COUNT(quic_h3_datagram); + settings_.values[SETTINGS_H3_DATAGRAM] = 1; + } + if (WillNegotiateWebTransport()) { + settings_.values[SETTINGS_WEBTRANS_DRAFT00] = 1; + } } void QuicSpdySession::OnDecoderStreamError(QuicErrorCode error_code, @@ -688,7 +683,7 @@ bool QuicSpdySession::OnPriorityUpdateForRequestStream(QuicStreamId stream_id, buffered_stream_priorities_.size(), ", which should not exceed the incoming stream limit of ", max_open_incoming_bidirectional_streams()); - QUIC_BUG << error_message; + QUIC_BUG(quic_bug_10360_2) << error_message; connection()->CloseConnection( QUIC_INTERNAL_ERROR, error_message, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); @@ -705,7 +700,7 @@ bool QuicSpdySession::OnPriorityUpdateForPushStream(QuicStreamId /*push_id*/, } size_t QuicSpdySession::ProcessHeaderData(const struct iovec& iov) { - QUIC_BUG_IF(destruction_indicator_ != 123456789) + QUIC_BUG_IF(quic_bug_12477_4, destruction_indicator_ != 123456789) << "QuicSpdyStream use after free. " << destruction_indicator_ << QuicStackTrace(); return h2_deframer_.ProcessInput(static_cast<char*>(iov.iov_base), @@ -747,7 +742,7 @@ void QuicSpdySession::WriteHttp3PriorityUpdate( } void QuicSpdySession::OnHttp3GoAway(uint64_t id) { - QUIC_BUG_IF(!version().UsesHttp3()) + QUIC_BUG_IF(quic_bug_12477_5, !version().UsesHttp3()) << "HTTP/3 GOAWAY received on version " << version(); if (last_received_http3_goaway_id_.has_value() && @@ -815,40 +810,21 @@ void QuicSpdySession::SendHttp3GoAway(QuicErrorCode error_code, } QuicStreamId stream_id; - if (goaway_with_max_stream_id_) { - stream_id = QuicUtils::GetMaxClientInitiatedBidirectionalStreamId( - transport_version()); - if (last_sent_http3_goaway_id_.has_value()) { - if (last_sent_http3_goaway_id_.value() == stream_id) { - // Do not send GOAWAY twice. - return; - } - if (last_sent_http3_goaway_id_.value() < stream_id) { - // A previous GOAWAY frame was sent with smaller stream ID. This is not - // possible, because the only time a GOAWAY frame with non-maximal - // stream ID is sent is right before closing connection. - QUIC_BUG << "GOAWAY frame with smaller ID already sent."; - return; - } - } - } else { - stream_id = GetLargestPeerCreatedStreamId(/*unidirectional = */ false); - - if (stream_id == QuicUtils::GetInvalidStreamId(transport_version())) { - // No client-initiated bidirectional streams received yet. - // Send 0 to let client know that all requests can be retried. - stream_id = 0; - } else { - // Tell client that streams starting with the next after the largest - // received one can be retried. - stream_id += QuicUtils::StreamIdDelta(transport_version()); + stream_id = QuicUtils::GetMaxClientInitiatedBidirectionalStreamId( + transport_version()); + if (last_sent_http3_goaway_id_.has_value()) { + if (last_sent_http3_goaway_id_.value() == stream_id) { + // Do not send GOAWAY twice. + return; } - if (last_sent_http3_goaway_id_.has_value() && - last_sent_http3_goaway_id_.value() <= stream_id) { - // MUST not send GOAWAY with identifier larger than previously sent. - // Do not bother sending one with same identifier as before, since - // GOAWAY frames on the control stream are guaranteed to be processed in - // order. + if (last_sent_http3_goaway_id_.value() < stream_id) { + // A previous GOAWAY frame was sent with smaller stream ID. This is not + // possible, because the only time a GOAWAY frame with non-maximal + // stream ID is sent is right before closing connection. + QUIC_BUG(quic_bug_10360_3) + << "Not sending GOAWAY frame with " << stream_id + << " because one with " << last_sent_http3_goaway_id_.value() + << " already sent on connection " << connection()->connection_id(); return; } } @@ -857,77 +833,33 @@ void QuicSpdySession::SendHttp3GoAway(QuicErrorCode error_code, last_sent_http3_goaway_id_ = stream_id; } -void QuicSpdySession::SendHttp3Shutdown() { - if (goaway_with_max_stream_id_) { - SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Server shutdown"); - return; - } - - QUICHE_DCHECK_EQ(perspective(), Perspective::IS_SERVER); - QUICHE_DCHECK(VersionUsesHttp3(transport_version())); - QuicStreamCount advertised_max_incoming_bidirectional_streams = - GetAdvertisedMaxIncomingBidirectionalStreams(); - const QuicStreamId stream_id = - QuicUtils::GetFirstBidirectionalStreamId(transport_version(), - Perspective::IS_CLIENT) + - QuicUtils::StreamIdDelta(transport_version()) * - advertised_max_incoming_bidirectional_streams; - if (last_sent_http3_goaway_id_.has_value() && - last_sent_http3_goaway_id_.value() < stream_id) { - send_control_stream_->SendGoAway(last_sent_http3_goaway_id_.value()); - return; - } - send_control_stream_->SendGoAway(stream_id); - last_sent_http3_goaway_id_ = stream_id; -} - void QuicSpdySession::WritePushPromise(QuicStreamId original_stream_id, QuicStreamId promised_stream_id, SpdyHeaderBlock headers) { if (perspective() == Perspective::IS_CLIENT) { - QUIC_BUG << "Client shouldn't send PUSH_PROMISE"; + QUIC_BUG(quic_bug_10360_4) << "Client shouldn't send PUSH_PROMISE"; return; } - if (!VersionUsesHttp3(transport_version())) { - SpdyPushPromiseIR push_promise(original_stream_id, promised_stream_id, - std::move(headers)); - // PUSH_PROMISE must not be the last frame sent out, at least followed by - // response headers. - push_promise.set_fin(false); - - SpdySerializedFrame frame(spdy_framer_.SerializeFrame(push_promise)); - headers_stream()->WriteOrBufferData( - absl::string_view(frame.data(), frame.size()), false, nullptr); - return; - } - - if (!max_push_id_.has_value() || promised_stream_id > max_push_id_.value()) { - QUIC_BUG - << "Server shouldn't send push id higher than client's MAX_PUSH_ID."; + if (VersionUsesHttp3(transport_version())) { + QUIC_BUG(quic_bug_12477_6) + << "Support for server push over HTTP/3 has been removed."; return; } - // Encode header list. - std::string encoded_headers = - qpack_encoder_->EncodeHeaderList(original_stream_id, headers, nullptr); - - if (debug_visitor_) { - debug_visitor_->OnPushPromiseFrameSent(original_stream_id, - promised_stream_id, headers); - } + SpdyPushPromiseIR push_promise(original_stream_id, promised_stream_id, + std::move(headers)); + // PUSH_PROMISE must not be the last frame sent out, at least followed by + // response headers. + push_promise.set_fin(false); - PushPromiseFrame frame; - frame.push_id = promised_stream_id; - frame.headers = encoded_headers; - QuicSpdyStream* stream = GetOrCreateSpdyDataStream(original_stream_id); - stream->WritePushPromise(frame); + SpdySerializedFrame frame(spdy_framer_.SerializeFrame(push_promise)); + headers_stream()->WriteOrBufferData( + absl::string_view(frame.data(), frame.size()), false, nullptr); } bool QuicSpdySession::server_push_enabled() const { - return VersionUsesHttp3(transport_version()) - ? ietf_server_push_enabled_ && max_push_id_.has_value() - : server_push_enabled_; + return VersionUsesHttp3(transport_version()) ? false : server_push_enabled_; } void QuicSpdySession::SendInitialData() { @@ -936,10 +868,6 @@ void QuicSpdySession::SendInitialData() { } QuicConnection::ScopedPacketFlusher flusher(connection()); send_control_stream_->MaybeSendSettingsFrame(); - if (perspective() == Perspective::IS_CLIENT && max_push_id_.has_value() && - !http3_max_push_id_sent_) { - SendMaxPushId(); - } } QpackEncoder* QuicSpdySession::qpack_encoder() { @@ -968,9 +896,10 @@ QuicSpdyStream* QuicSpdySession::GetOrCreateSpdyDataStream( const QuicStreamId stream_id) { QuicStream* stream = GetOrCreateStream(stream_id); if (stream && stream->is_static()) { - QUIC_BUG << "GetOrCreateSpdyDataStream returns static stream " << stream_id - << " in version " << transport_version() << "\n" - << QuicStackTrace(); + QUIC_BUG(quic_bug_10360_5) + << "GetOrCreateSpdyDataStream returns static stream " << stream_id + << " in version " << transport_version() << "\n" + << QuicStackTrace(); connection()->CloseConnection( QUIC_INVALID_STREAM_ID, absl::StrCat("stream ", stream_id, " is static"), @@ -990,6 +919,15 @@ void QuicSpdySession::OnNewEncryptionKeyAvailable( } } +bool QuicSpdySession::ShouldNegotiateWebTransport() { + return false; +} + +bool QuicSpdySession::WillNegotiateWebTransport() { + return ShouldNegotiateHttp3Datagram() && version().UsesHttp3() && + ShouldNegotiateWebTransport(); +} + // True if there are open HTTP requests. bool QuicSpdySession::ShouldKeepConnectionAlive() const { QUICHE_DCHECK(VersionUsesHttp3(transport_version()) || @@ -1050,7 +988,7 @@ void QuicSpdySession::OnPromiseHeaderList( const QuicHeaderList& /*header_list*/) { std::string error = "OnPromiseHeaderList should be overridden in client code."; - QUIC_BUG << error; + QUIC_BUG(quic_bug_10360_6) << error; connection()->CloseConnection(QUIC_INTERNAL_ERROR, error, ConnectionCloseBehavior::SILENT_CLOSE); } @@ -1069,6 +1007,7 @@ bool QuicSpdySession::ResumeApplicationState(ApplicationState* cached_state) { if (debug_visitor_ != nullptr) { debug_visitor_->OnSettingsFrameResumed(out); } + QUICHE_DCHECK(streams_waiting_for_settings_.empty()); for (const auto& setting : out.values) { OnSetting(setting.first, setting.second); } @@ -1113,10 +1052,41 @@ bool QuicSpdySession::OnSettingsFrame(const SettingsFrame& frame) { return false; } } + for (QuicStreamId stream_id : streams_waiting_for_settings_) { + QUICHE_DCHECK(ShouldBufferRequestsUntilSettings()); + QuicSpdyStream* stream = GetOrCreateSpdyDataStream(stream_id); + if (stream == nullptr) { + // The stream may no longer exist, since it is possible for a stream to + // get reset while waiting for the SETTINGS frame. + continue; + } + stream->OnDataAvailable(); + } + streams_waiting_for_settings_.clear(); return true; } +absl::optional<std::string> QuicSpdySession::OnSettingsFrameViaAlps( + const SettingsFrame& frame) { + QUICHE_DCHECK(VersionUsesHttp3(transport_version())); + + if (debug_visitor_ != nullptr) { + debug_visitor_->OnSettingsFrameReceivedViaAlps(frame); + } + for (const auto& setting : frame.values) { + if (!OnSetting(setting.first, setting.second)) { + // Do not bother adding the setting identifier or value to the error + // message, because OnSetting() already closed the connection, therefore + // the error message will be ignored. + return "error parsing setting"; + } + } + return absl::nullopt; +} + bool QuicSpdySession::OnSetting(uint64_t id, uint64_t value) { + any_settings_received_ = true; + if (VersionUsesHttp3(transport_version())) { // SETTINGS frame received on the control stream. switch (id) { @@ -1127,8 +1097,7 @@ bool QuicSpdySession::OnSetting(uint64_t id, uint64_t value) { << value; // Communicate |value| to encoder, because it is used for encoding // Required Insert Count. - bool success = qpack_encoder_->SetMaximumDynamicTableCapacity(value); - if (GetQuicRestartFlag(quic_enable_zero_rtt_for_tls_v2) && !success) { + if (!qpack_encoder_->SetMaximumDynamicTableCapacity(value)) { CloseConnectionWithDetails( was_zero_rtt_rejected() ? QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH @@ -1137,7 +1106,7 @@ bool QuicSpdySession::OnSetting(uint64_t id, uint64_t value) { ? "Server rejected 0-RTT, aborting because " : "", "Server sent an SETTINGS_QPACK_MAX_TABLE_CAPACITY: ", - value, "while current value is: ", + value, " while current value is: ", qpack_encoder_->MaximumDynamicTableCapacity())); return false; } @@ -1151,8 +1120,7 @@ bool QuicSpdySession::OnSetting(uint64_t id, uint64_t value) { QUIC_DVLOG(1) << ENDPOINT << "SETTINGS_MAX_FIELD_SECTION_SIZE received with value " << value; - if (GetQuicRestartFlag(quic_enable_zero_rtt_for_tls_v2) && - max_outbound_header_list_size_ != + if (max_outbound_header_list_size_ != std::numeric_limits<size_t>::max() && max_outbound_header_list_size_ > value) { CloseConnectionWithDetails( @@ -1163,7 +1131,7 @@ bool QuicSpdySession::OnSetting(uint64_t id, uint64_t value) { ? "Server rejected 0-RTT, aborting because " : "", "Server sent an SETTINGS_MAX_FIELD_SECTION_SIZE: ", - value, "which reduces current value: ", + value, " which reduces current value: ", max_outbound_header_list_size_)); return false; } @@ -1173,8 +1141,7 @@ bool QuicSpdySession::OnSetting(uint64_t id, uint64_t value) { QUIC_DVLOG(1) << ENDPOINT << "SETTINGS_QPACK_BLOCKED_STREAMS received with value " << value; - bool success = qpack_encoder_->SetMaximumBlockedStreams(value); - if (GetQuicRestartFlag(quic_enable_zero_rtt_for_tls_v2) && !success) { + if (!qpack_encoder_->SetMaximumBlockedStreams(value)) { CloseConnectionWithDetails( was_zero_rtt_rejected() ? QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH @@ -1183,7 +1150,7 @@ bool QuicSpdySession::OnSetting(uint64_t id, uint64_t value) { ? "Server rejected 0-RTT, aborting because " : "", "Server sent an SETTINGS_QPACK_BLOCKED_STREAMS: ", - value, "which reduces current value: ", + value, " which reduces current value: ", qpack_encoder_->maximum_blocked_streams())); return false; } @@ -1201,6 +1168,45 @@ 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()) { + break; + } + QUIC_DVLOG(1) << ENDPOINT << "SETTINGS_H3_DATAGRAM 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); + return false; + } + h3_datagram_supported_ = !!value; + break; + } + case SETTINGS_WEBTRANS_DRAFT00: + if (!WillNegotiateWebTransport()) { + break; + } + 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); + return false; + } + peer_supports_webtransport_ = (value == 1); + break; default: QUIC_DVLOG(1) << ENDPOINT << "Unknown setting identifier " << id << " received with value " << value; @@ -1344,20 +1350,6 @@ void QuicSpdySession::OnCompressedFrameSize(size_t frame_len) { frame_len_ += frame_len; } -void QuicSpdySession::SetHpackEncoderDebugVisitor( - std::unique_ptr<QuicHpackDebugVisitor> visitor) { - spdy_framer_.SetEncoderHeaderTableDebugVisitor( - std::unique_ptr<HeaderTableDebugVisitor>(new HeaderTableDebugVisitor( - connection()->helper()->GetClock(), std::move(visitor)))); -} - -void QuicSpdySession::SetHpackDecoderDebugVisitor( - std::unique_ptr<QuicHpackDebugVisitor> visitor) { - h2_deframer_.SetDecoderHeaderTableDebugVisitor( - std::make_unique<HeaderTableDebugVisitor>( - connection()->helper()->GetClock(), std::move(visitor))); -} - void QuicSpdySession::CloseConnectionWithDetails(QuicErrorCode error, const std::string& details) { connection()->CloseConnection( @@ -1368,13 +1360,13 @@ bool QuicSpdySession::HasActiveRequestStreams() const { return GetNumActiveStreams() + num_draining_streams() > 0; } -bool QuicSpdySession::ProcessPendingStream(PendingStream* pending) { +QuicStream* QuicSpdySession::ProcessPendingStream(PendingStream* pending) { QUICHE_DCHECK(VersionUsesHttp3(transport_version())); QUICHE_DCHECK(connection()->connected()); struct iovec iov; if (!pending->sequencer()->GetReadableRegion(&iov)) { // The first byte hasn't been received yet. - return false; + return nullptr; } QuicDataReader reader(static_cast<char*>(iov.iov_base), iov.iov_len); @@ -1387,7 +1379,7 @@ bool QuicSpdySession::ProcessPendingStream(PendingStream* pending) { // Mark all bytes consumed in order to close stream. pending->MarkConsumed(pending->sequencer()->close_offset()); } - return false; + return nullptr; } pending->MarkConsumed(stream_type_length); @@ -1395,72 +1387,87 @@ bool QuicSpdySession::ProcessPendingStream(PendingStream* pending) { case kControlStream: { // HTTP/3 control stream. if (receive_control_stream_) { CloseConnectionOnDuplicateHttp3UnidirectionalStreams("Control"); - return false; + return nullptr; } auto receive_stream = std::make_unique<QuicReceiveControlStream>(pending, this); receive_control_stream_ = receive_stream.get(); ActivateStream(std::move(receive_stream)); - receive_control_stream_->SetUnblocked(); QUIC_DVLOG(1) << ENDPOINT << "Receive Control stream is created"; if (debug_visitor_ != nullptr) { debug_visitor_->OnPeerControlStreamCreated( receive_control_stream_->id()); } - return true; + return receive_control_stream_; } case kServerPushStream: { // Push Stream. QuicSpdyStream* stream = CreateIncomingStream(pending); - stream->SetUnblocked(); - return true; + return stream; } case kQpackEncoderStream: { // QPACK encoder stream. if (qpack_encoder_receive_stream_) { CloseConnectionOnDuplicateHttp3UnidirectionalStreams("QPACK encoder"); - return false; + return nullptr; } auto encoder_receive = std::make_unique<QpackReceiveStream>( pending, this, qpack_decoder_->encoder_stream_receiver()); qpack_encoder_receive_stream_ = encoder_receive.get(); ActivateStream(std::move(encoder_receive)); - qpack_encoder_receive_stream_->SetUnblocked(); QUIC_DVLOG(1) << ENDPOINT << "Receive QPACK Encoder stream is created"; if (debug_visitor_ != nullptr) { debug_visitor_->OnPeerQpackEncoderStreamCreated( qpack_encoder_receive_stream_->id()); } - return true; + return qpack_encoder_receive_stream_; } case kQpackDecoderStream: { // QPACK decoder stream. if (qpack_decoder_receive_stream_) { CloseConnectionOnDuplicateHttp3UnidirectionalStreams("QPACK decoder"); - return false; + return nullptr; } auto decoder_receive = std::make_unique<QpackReceiveStream>( pending, this, qpack_encoder_->decoder_stream_receiver()); qpack_decoder_receive_stream_ = decoder_receive.get(); ActivateStream(std::move(decoder_receive)); - qpack_decoder_receive_stream_->SetUnblocked(); QUIC_DVLOG(1) << ENDPOINT << "Receive QPACK Decoder stream is created"; if (debug_visitor_ != nullptr) { debug_visitor_->OnPeerQpackDecoderStreamCreated( qpack_decoder_receive_stream_->id()); } - return true; + return qpack_decoder_receive_stream_; } - default: - if (GetQuicReloadableFlag(quic_unify_stop_sending)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_unify_stop_sending); - MaybeSendStopSendingFrame(pending->id(), - QUIC_STREAM_STREAM_CREATION_ERROR); - } else { - // TODO(renjietang): deprecate SendStopSending() when the flag is - // deprecated. - SendStopSending(QUIC_STREAM_STREAM_CREATION_ERROR, pending->id()); + case kWebTransportUnidirectionalStream: { + // Note that this checks whether WebTransport is enabled on the receiver + // side, as we may receive WebTransport streams before peer's SETTINGS are + // received. + // TODO(b/184156476): consider whether this means we should drop buffered + // streams if we don't receive indication of WebTransport support. + if (!WillNegotiateWebTransport()) { + // Treat as unknown stream type. + break; } - pending->StopReading(); + QUIC_DVLOG(1) << ENDPOINT << "Created an incoming WebTransport stream " + << pending->id(); + auto stream_owned = + std::make_unique<WebTransportHttp3UnidirectionalStream>(pending, + this); + WebTransportHttp3UnidirectionalStream* stream = stream_owned.get(); + ActivateStream(std::move(stream_owned)); + return stream; + } + default: + break; } - return false; + if (GetQuicReloadableFlag(quic_unify_stop_sending)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_unify_stop_sending); + MaybeSendStopSendingFrame(pending->id(), QUIC_STREAM_STREAM_CREATION_ERROR); + } else { + // TODO(renjietang): deprecate SendStopSending() when the flag is + // deprecated. + SendStopSending(QUIC_STREAM_STREAM_CREATION_ERROR, pending->id()); + } + pending->StopReading(); + return nullptr; } void QuicSpdySession::MaybeInitializeHttp3UnidirectionalStreams() { @@ -1505,15 +1512,12 @@ void QuicSpdySession::MaybeInitializeHttp3UnidirectionalStreams() { } void QuicSpdySession::BeforeConnectionCloseSent() { - if (!GetQuicReloadableFlag(quic_send_goaway_with_connection_close) || - !VersionUsesHttp3(transport_version()) || !IsEncryptionEstablished()) { + if (!VersionUsesHttp3(transport_version()) || !IsEncryptionEstablished()) { return; } QUICHE_DCHECK_EQ(perspective(), Perspective::IS_SERVER); - QUIC_CODE_COUNT(quic_send_goaway_with_connection_close); - QuicStreamId stream_id = GetLargestPeerCreatedStreamId(/*unidirectional = */ false); @@ -1528,13 +1532,15 @@ void QuicSpdySession::BeforeConnectionCloseSent() { } if (last_sent_http3_goaway_id_.has_value() && last_sent_http3_goaway_id_.value() <= stream_id) { - if (goaway_with_max_stream_id_) { - // A previous GOAWAY frame was sent with smaller stream ID. This is not - // possible, because this is the only method sending a GOAWAY frame with - // non-maximal stream ID, and this must only be called once, right - // before closing connection. - QUIC_BUG << "GOAWAY frame with smaller ID already sent."; - } + // A previous GOAWAY frame was sent with smaller stream ID. This is not + // possible, because this is the only method sending a GOAWAY frame with + // non-maximal stream ID, and this must only be called once, right + // before closing connection. + QUIC_BUG(QuicGoawayFrameAlreadySent) + << "Not sending GOAWAY frame with " << stream_id << " because one with " + << last_sent_http3_goaway_id_.value() << " already sent on connection " + << connection()->connection_id(); + // MUST not send GOAWAY with identifier larger than previously sent. // Do not bother sending one with same identifier as before, since GOAWAY // frames on the control stream are guaranteed to be processed in order. @@ -1551,38 +1557,6 @@ void QuicSpdySession::OnCanCreateNewOutgoingStream(bool unidirectional) { } } -void QuicSpdySession::SetMaxPushId(PushId max_push_id) { - QUICHE_DCHECK(VersionUsesHttp3(transport_version())); - QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective()); - if (max_push_id_.has_value()) { - QUICHE_DCHECK_GE(max_push_id, max_push_id_.value()); - } - - if (!max_push_id_.has_value() && max_push_id == 0) { - // The default max_push_id is 0. So no need to send out MaxPushId frame. - return; - } - - ietf_server_push_enabled_ = true; - - if (max_push_id_.has_value()) { - if (max_push_id == max_push_id_.value()) { - QUIC_DVLOG(1) << "Not changing max_push_id: " << max_push_id; - return; - } - - QUIC_DVLOG(1) << "Setting max_push_id to: " << max_push_id - << " from: " << max_push_id_.value(); - } else { - QUIC_DVLOG(1) << "Setting max_push_id to: " << max_push_id << " from unset"; - } - max_push_id_ = max_push_id; - - if (IsEncryptionEstablished()) { - SendMaxPushId(); - } -} - bool QuicSpdySession::OnMaxPushIdFrame(PushId max_push_id) { QUICHE_DCHECK(VersionUsesHttp3(transport_version())); QUICHE_DCHECK_EQ(Perspective::IS_SERVER, perspective()); @@ -1615,21 +1589,6 @@ bool QuicSpdySession::OnMaxPushIdFrame(PushId max_push_id) { return true; } -void QuicSpdySession::SendMaxPushId() { - QUICHE_DCHECK(VersionUsesHttp3(transport_version())); - QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective()); - - send_control_stream_->SendMaxPushIdFrame(max_push_id_.value()); - http3_max_push_id_sent_ = true; -} - -void QuicSpdySession::EnableServerPush() { - QUICHE_DCHECK(VersionUsesHttp3(transport_version())); - QUICHE_DCHECK_EQ(perspective(), Perspective::IS_SERVER); - - ietf_server_push_enabled_ = true; -} - bool QuicSpdySession::goaway_received() const { return VersionUsesHttp3(transport_version()) ? last_received_http3_goaway_id_.has_value() @@ -1642,17 +1601,17 @@ bool QuicSpdySession::goaway_sent() const { : transport_goaway_sent(); } -bool QuicSpdySession::CanCreatePushStreamWithId(PushId push_id) { +bool QuicSpdySession::CanCreatePushStreamWithId(PushId /* push_id */) { + // TODO(b/171463363): Remove this method. QUICHE_DCHECK(VersionUsesHttp3(transport_version())); - return ietf_server_push_enabled_ && max_push_id_.has_value() && - max_push_id_.value() >= push_id; + return false; } void QuicSpdySession::CloseConnectionOnDuplicateHttp3UnidirectionalStreams( absl::string_view type) { - QUIC_PEER_BUG << absl::StrCat("Received a duplicate ", type, - " stream: Closing connection."); + QUIC_PEER_BUG(quic_peer_bug_10360_9) << absl::StrCat( + "Received a duplicate ", type, " stream: Closing connection."); CloseConnectionWithDetails(QUIC_HTTP_DUPLICATE_UNIDIRECTIONAL_STREAM, absl::StrCat(type, " stream is received twice.")); } @@ -1703,6 +1662,214 @@ void QuicSpdySession::LogHeaderCompressionRatioHistogram( } } +QuicDatagramFlowId QuicSpdySession::GetNextDatagramFlowId() { + QuicDatagramFlowId result = next_available_datagram_flow_id_; + next_available_datagram_flow_id_ += kDatagramFlowIdIncrement; + return result; +} + +MessageStatus QuicSpdySession::SendHttp3Datagram(QuicDatagramFlowId flow_id, + absl::string_view payload) { + size_t slice_length = + QuicDataWriter::GetVarInt62Len(flow_id) + payload.length(); + QuicUniqueBufferPtr buffer = MakeUniqueBuffer( + connection()->helper()->GetStreamSendBufferAllocator(), slice_length); + QuicDataWriter writer(slice_length, buffer.get()); + if (!writer.WriteVarInt62(flow_id)) { + QUIC_BUG(quic_bug_10360_10) << "Failed to write HTTP/3 datagram flow ID"; + return MESSAGE_STATUS_INTERNAL_ERROR; + } + if (!writer.WriteBytes(payload.data(), payload.length())) { + QUIC_BUG(quic_bug_10360_11) << "Failed to write HTTP/3 datagram payload"; + return MESSAGE_STATUS_INTERNAL_ERROR; + } + + QuicMemSlice slice(std::move(buffer), slice_length); + return datagram_queue()->SendOrQueueDatagram(std::move(slice)); +} + +void QuicSpdySession::RegisterHttp3FlowId( + QuicDatagramFlowId flow_id, + QuicSpdySession::Http3DatagramVisitor* visitor) { + QUICHE_DCHECK_NE(visitor, nullptr); + auto insertion_result = h3_datagram_registrations_.insert({flow_id, visitor}); + QUIC_BUG_IF(quic_bug_12477_7, !insertion_result.second) + << "Attempted to doubly register HTTP/3 flow ID " << flow_id; +} + +void QuicSpdySession::UnregisterHttp3FlowId(QuicDatagramFlowId flow_id) { + size_t num_erased = h3_datagram_registrations_.erase(flow_id); + QUIC_BUG_IF(quic_bug_12477_8, num_erased != 1) + << "Attempted to unregister unknown HTTP/3 flow ID " << flow_id; +} + +void QuicSpdySession::SetMaxTimeInQueueForFlowId( + QuicDatagramFlowId /*flow_id*/, + QuicTime::Delta max_time_in_queue) { + // TODO(b/184598230): implement this in a way that works for multiple sessions + // on a same connection. + datagram_queue()->SetMaxTimeInQueue(max_time_in_queue); +} + +void QuicSpdySession::OnMessageReceived(absl::string_view message) { + QuicSession::OnMessageReceived(message); + if (!h3_datagram_supported_) { + QUIC_DLOG(ERROR) << "Ignoring unexpected received HTTP/3 datagram"; + return; + } + QuicDataReader reader(message); + QuicDatagramFlowId flow_id; + if (!reader.ReadVarInt62(&flow_id)) { + QUIC_DLOG(ERROR) << "Failed to parse flow ID in received HTTP/3 datagram"; + return; + } + auto it = h3_datagram_registrations_.find(flow_id); + if (it == h3_datagram_registrations_.end()) { + // TODO(dschinazi) buffer unknown HTTP/3 datagram flow IDs for a short + // period of time in case they were reordered. + QUIC_DLOG(ERROR) << "Received unknown HTTP/3 datagram flow ID " << flow_id; + return; + } + absl::string_view payload = reader.ReadRemainingPayload(); + it->second->OnHttp3Datagram(flow_id, payload); +} + +bool QuicSpdySession::SupportsWebTransport() { + return WillNegotiateWebTransport() && h3_datagram_supported_ && + peer_supports_webtransport_; +} + +WebTransportHttp3* QuicSpdySession::GetWebTransportSession( + WebTransportSessionId id) { + if (!SupportsWebTransport()) { + return nullptr; + } + if (!IsValidWebTransportSessionId(id, version())) { + return nullptr; + } + QuicSpdyStream* connect_stream = GetOrCreateSpdyDataStream(id); + if (connect_stream == nullptr) { + return nullptr; + } + return connect_stream->web_transport(); +} + +bool QuicSpdySession::ShouldProcessIncomingRequests() { + if (!ShouldBufferRequestsUntilSettings()) { + return true; + } + + return any_settings_received_; +} + +void QuicSpdySession::OnStreamWaitingForClientSettings(QuicStreamId id) { + QUICHE_DCHECK(ShouldBufferRequestsUntilSettings()); + QUICHE_DCHECK(QuicUtils::IsBidirectionalStreamId(id, version())); + streams_waiting_for_settings_.insert(id); +} + +void QuicSpdySession::AssociateIncomingWebTransportStreamWithSession( + WebTransportSessionId session_id, + QuicStreamId stream_id) { + if (QuicUtils::IsOutgoingStreamId(version(), stream_id, perspective())) { + QUIC_BUG(AssociateIncomingWebTransportStreamWithSession got outgoing stream) + << ENDPOINT + << "AssociateIncomingWebTransportStreamWithSession() got an outgoing " + "stream ID: " + << stream_id; + return; + } + WebTransportHttp3* session = GetWebTransportSession(session_id); + if (session != nullptr) { + QUIC_DVLOG(1) << ENDPOINT + << "Successfully associated incoming WebTransport stream " + << stream_id << " with session ID " << session_id; + + session->AssociateStream(stream_id); + return; + } + // Evict the oldest streams until we are under the limit. + while (buffered_streams_.size() >= kMaxUnassociatedWebTransportStreams) { + QUIC_DVLOG(1) << ENDPOINT << "Removing stream " + << buffered_streams_.front().stream_id + << " from buffered streams as the queue is full."; + ResetStream(buffered_streams_.front().stream_id, + QUIC_STREAM_WEBTRANSPORT_BUFFERED_STREAMS_LIMIT_EXCEEDED); + buffered_streams_.pop_front(); + } + QUIC_DVLOG(1) << ENDPOINT << "Received a WebTransport stream " << stream_id + << " for session ID " << session_id + << " but cannot associate it; buffering instead."; + buffered_streams_.push_back( + BufferedWebTransportStream{session_id, stream_id}); +} + +void QuicSpdySession::ProcessBufferedWebTransportStreamsForSession( + WebTransportHttp3* session) { + const WebTransportSessionId session_id = session->id(); + QUIC_DVLOG(1) << "Processing buffered WebTransport streams for " + << session_id; + auto it = buffered_streams_.begin(); + while (it != buffered_streams_.end()) { + if (it->session_id == session_id) { + QUIC_DVLOG(1) << "Unbuffered and associated WebTransport stream " + << it->stream_id << " with session " << it->session_id; + session->AssociateStream(it->stream_id); + it = buffered_streams_.erase(it); + } else { + it++; + } + } +} + +WebTransportHttp3UnidirectionalStream* +QuicSpdySession::CreateOutgoingUnidirectionalWebTransportStream( + WebTransportHttp3* session) { + if (!CanOpenNextOutgoingUnidirectionalStream()) { + return nullptr; + } + + QuicStreamId stream_id = GetNextOutgoingUnidirectionalStreamId(); + auto stream_owned = std::make_unique<WebTransportHttp3UnidirectionalStream>( + stream_id, this, session->id()); + WebTransportHttp3UnidirectionalStream* stream = stream_owned.get(); + ActivateStream(std::move(stream_owned)); + stream->WritePreamble(); + session->AssociateStream(stream_id); + return stream; +} + +QuicSpdyStream* QuicSpdySession::CreateOutgoingBidirectionalWebTransportStream( + WebTransportHttp3* session) { + QuicSpdyStream* stream = CreateOutgoingBidirectionalStream(); + if (stream == nullptr) { + return nullptr; + } + QuicStreamId stream_id = stream->id(); + stream->ConvertToWebTransportDataStream(session->id()); + if (stream->web_transport_stream() == nullptr) { + // An error in ConvertToWebTransportDataStream() would result in + // CONNECTION_CLOSE, thus we don't need to do anything here. + return nullptr; + } + session->AssociateStream(stream_id); + return stream; +} + +void QuicSpdySession::OnDatagramProcessed( + absl::optional<MessageStatus> /*status*/) { + // TODO(b/184598230): make this work with multiple datagram flows. +} + +void QuicSpdySession::DatagramObserver::OnDatagramProcessed( + absl::optional<MessageStatus> status) { + session_->OnDatagramProcessed(status); +} + +bool QuicSpdySession::ShouldNegotiateHttp3Datagram() { + return GetQuicReloadableFlag(quic_h3_datagram); +} + #undef ENDPOINT // undef for jumbo builds } // namespace quic 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 3c3d558a1fe..b2004845306 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,10 +6,12 @@ #define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_SESSION_H_ #include <cstddef> +#include <list> #include <memory> #include <string> #include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "quic/core/http/http_frames.h" @@ -24,10 +26,12 @@ #include "quic/core/qpack/qpack_encoder_stream_sender.h" #include "quic/core/qpack/qpack_receive_stream.h" #include "quic/core/qpack/qpack_send_stream.h" +#include "quic/core/quic_circular_deque.h" #include "quic/core/quic_session.h" #include "quic/core/quic_time.h" #include "quic/core/quic_types.h" #include "quic/core/quic_versions.h" +#include "quic/platform/api/quic_containers.h" #include "quic/platform/api/quic_export.h" #include "spdy/core/http2_frame_decoder_adapter.h" @@ -37,24 +41,9 @@ namespace test { class QuicSpdySessionPeer; } // namespace test -// QuicHpackDebugVisitor gathers data used for understanding HPACK HoL -// dynamics. Specifically, it is to help predict the compression -// penalty of avoiding HoL by chagning how the dynamic table is used. -// In chromium, the concrete instance populates an UMA -// histogram with the data. -class QUIC_EXPORT_PRIVATE QuicHpackDebugVisitor { - public: - QuicHpackDebugVisitor(); - QuicHpackDebugVisitor(const QuicHpackDebugVisitor&) = delete; - QuicHpackDebugVisitor& operator=(const QuicHpackDebugVisitor&) = delete; - - virtual ~QuicHpackDebugVisitor(); +class WebTransportHttp3UnidirectionalStream; - // For each HPACK indexed representation processed, |elapsed| is - // the time since the corresponding entry was added to the dynamic - // table. - virtual void OnUseEntry(QuicTime::Delta elapsed) = 0; -}; +QUIC_EXPORT_PRIVATE extern const size_t kMaxUnassociatedWebTransportStreams; class QUIC_EXPORT_PRIVATE Http3DebugVisitor { public: @@ -85,12 +74,15 @@ class QUIC_EXPORT_PRIVATE Http3DebugVisitor { virtual void OnPeerQpackDecoderStreamCreated(QuicStreamId /*stream_id*/) = 0; // Incoming HTTP/3 frames in ALPS TLS extension. + virtual void OnSettingsFrameReceivedViaAlps(const SettingsFrame& /*frame*/) {} virtual void OnAcceptChFrameReceivedViaAlps(const AcceptChFrame& /*frame*/) {} // Incoming HTTP/3 frames on the control stream. + // TODO(b/171463363): Remove. virtual void OnCancelPushFrameReceived(const CancelPushFrame& /*frame*/) {} virtual void OnSettingsFrameReceived(const SettingsFrame& /*frame*/) = 0; virtual void OnGoAwayFrameReceived(const GoAwayFrame& /*frame*/) {} + // TODO(b/171463363): Remove. virtual void OnMaxPushIdFrameReceived(const MaxPushIdFrame& /*frame*/) {} virtual void OnPriorityUpdateFrameReceived( const PriorityUpdateFrame& /*frame*/) {} @@ -104,10 +96,12 @@ class QUIC_EXPORT_PRIVATE Http3DebugVisitor { QuicByteCount /*compressed_headers_length*/) {} virtual void OnHeadersDecoded(QuicStreamId /*stream_id*/, QuicHeaderList /*headers*/) {} + // TODO(b/171463363): Remove. virtual void OnPushPromiseFrameReceived(QuicStreamId /*stream_id*/, QuicStreamId /*push_id*/, QuicByteCount /*compressed_headers_length*/) {} + // TODO(b/171463363): Remove. virtual void OnPushPromiseDecoded(QuicStreamId /*stream_id*/, QuicStreamId /*push_id*/, QuicHeaderList /*headers*/) {} @@ -120,6 +114,7 @@ class QUIC_EXPORT_PRIVATE Http3DebugVisitor { // Outgoing HTTP/3 frames on the control stream. virtual void OnSettingsFrameSent(const SettingsFrame& /*frame*/) = 0; virtual void OnGoAwayFrameSent(QuicStreamId /*stream_id*/) {} + // TODO(b/171463363): Remove. virtual void OnMaxPushIdFrameSent(const MaxPushIdFrame& /*frame*/) {} virtual void OnPriorityUpdateFrameSent(const PriorityUpdateFrame& /*frame*/) { } @@ -130,6 +125,7 @@ class QUIC_EXPORT_PRIVATE Http3DebugVisitor { virtual void OnHeadersFrameSent( QuicStreamId /*stream_id*/, const spdy::SpdyHeaderBlock& /*header_block*/) {} + // TODO(b/171463363): Remove. virtual void OnPushPromiseFrameSent( QuicStreamId /*stream_id*/, QuicStreamId @@ -243,10 +239,6 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // before encryption gets established. void SendHttp3GoAway(QuicErrorCode error_code, const std::string& reason); - // Same as SendHttp3GoAway(). TODO(bnc): remove when - // gfe2_reloadable_flag_quic_goaway_with_max_stream_id flag is deprecated. - void SendHttp3Shutdown(); - // Write |headers| for |promised_stream_id| on |original_stream_id| in a // PUSH_PROMISE frame to peer. virtual void WritePushPromise(QuicStreamId original_stream_id, @@ -262,11 +254,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // Returns whether server push is enabled. // For a Google QUIC client this always returns false. // For a Google QUIC server this is set by incoming SETTINGS_ENABLE_PUSH. - // For an IETF QUIC client this returns true if SetMaxPushId() has ever been - // called. - // For an IETF QUIC server this returns true if EnableServerPush() has been - // called and the server has received at least one MAX_PUSH_ID frame from the - // client. + // For an IETF QUIC client or server this returns false. bool server_push_enabled() const; // Called when the control stream receives HTTP/3 SETTINGS. @@ -274,9 +262,18 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // cached values, true otherwise. virtual bool OnSettingsFrame(const SettingsFrame& frame); - // Called when a SETTINGS is parsed from an incoming SETTINGS frame. - // Returns false in case of 0-RTT if received SETTINGS is incompatible with - // cached value, true otherwise. + // Called when an HTTP/3 SETTINGS frame is received via ALPS. + // Returns an error message if an error has occurred, or nullopt otherwise. + // May or may not close the connection on error. + absl::optional<std::string> OnSettingsFrameViaAlps( + const SettingsFrame& frame); + + // Called when a setting is parsed from a SETTINGS frame received on the + // control stream or from cached application state. + // Returns true on success. + // Returns false if received setting is incompatible with cached value (in + // case of 0-RTT) or with previously received value (in case of ALPS). + // Also closes the connection on error. bool OnSetting(uint64_t id, uint64_t value); // Return true if this session wants to release headers stream's buffer @@ -322,6 +319,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession void OnCompressedFrameSize(size_t frame_len); // Called when a PUSH_PROMISE frame has been received. + // TODO(b/171463363): Remove. void OnPushPromise(spdy::SpdyStreamId stream_id, spdy::SpdyStreamId promised_stream_id); @@ -334,34 +332,16 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // those streams are not initialized yet. void OnCanCreateNewOutgoingStream(bool unidirectional) override; - // Sets |max_push_id_| and sends a MAX_PUSH_ID frame. - // This method must only be called if protocol is IETF QUIC and perspective is - // client. |max_push_id| must be greater than or equal to current - // |max_push_id_|. - void SetMaxPushId(PushId max_push_id); - // Sets |max_push_id_|. // This method must only be called if protocol is IETF QUIC and perspective is // server. It must only be called if a MAX_PUSH_ID frame is received. // Returns whether |max_push_id| is greater than or equal to current // |max_push_id_|. + // TODO(b/171463363): Remove. bool OnMaxPushIdFrame(PushId max_push_id); - // Enables server push. - // Must only be called when using IETF QUIC, for which server push is disabled - // by default. Server push defaults to enabled and cannot be disabled for - // Google QUIC. - // Must only be called for a server. A client can effectively disable push by - // never calling SetMaxPushId(). - void EnableServerPush(); - - // Returns true if push is enabled and a push with |push_id| can be created. - // For a server this means that EnableServerPush() has been called, at least - // one MAX_PUSH_ID frame has been received, and the largest received - // MAX_PUSH_ID value is greater than or equal to |push_id|. - // For a client this means that SetMaxPushId() has been called with - // |max_push_id| greater than or equal to |push_id|. - // Must only be called when using IETF QUIC. + // TODO(b/171463363): Remove. + // Returns false. bool CanCreatePushStreamWithId(PushId push_id); int32_t destruction_indicator() const { return destruction_indicator_; } @@ -417,6 +397,99 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // extension. virtual void OnAcceptChFrameReceivedViaAlps(const AcceptChFrame& /*frame*/); + // Generates a new HTTP/3 datagram flow ID. + QuicDatagramFlowId GetNextDatagramFlowId(); + + // Whether HTTP/3 datagrams are supported on this session, based on received + // SETTINGS. + bool h3_datagram_supported() const { return h3_datagram_supported_; } + + // Sends an HTTP/3 datagram. The flow ID is not part of |payload|. + MessageStatus SendHttp3Datagram(QuicDatagramFlowId flow_id, + absl::string_view payload); + + class QUIC_EXPORT_PRIVATE Http3DatagramVisitor { + public: + virtual ~Http3DatagramVisitor() {} + + // Called when an HTTP/3 datagram is received. |payload| does not contain + // the flow ID. + virtual void OnHttp3Datagram(QuicDatagramFlowId flow_id, + absl::string_view payload) = 0; + }; + + // Registers |visitor| to receive HTTP/3 datagrams for flow ID |flow_id|. This + // must not be called on a previously register flow ID without first calling + // UnregisterHttp3FlowId. |visitor| must be valid until a corresponding call + // to UnregisterHttp3FlowId. The flow ID must be unregistered before the + // QuicSpdySession is destroyed. + void RegisterHttp3FlowId(QuicDatagramFlowId flow_id, + Http3DatagramVisitor* visitor); + + // Unregister a given HTTP/3 datagram flow ID. + void UnregisterHttp3FlowId(QuicDatagramFlowId flow_id); + + // Sets max time in queue for a specified datagram flow ID. + void SetMaxTimeInQueueForFlowId(QuicDatagramFlowId flow_id, + QuicTime::Delta max_time_in_queue); + + // Override from QuicSession to support HTTP/3 datagrams. + void OnMessageReceived(absl::string_view message) override; + + // Indicates whether the HTTP/3 session supports WebTransport. + bool SupportsWebTransport(); + + // Indicates whether the HTTP/3 session will indicate WebTransport support to + // the peer. + bool WillNegotiateWebTransport(); + + // Returns a WebTransport session by its session ID. Returns nullptr if no + // session is associated with the given ID. + WebTransportHttp3* GetWebTransportSession(WebTransportSessionId id); + + // If true, no data on bidirectional streams will be processed by the server + // until the SETTINGS are received. Only works for HTTP/3. + bool ShouldBufferRequestsUntilSettings() { + return version().UsesHttp3() && perspective() == Perspective::IS_SERVER && + WillNegotiateWebTransport(); + } + + // Returns if the incoming bidirectional streams should process data. This is + // usually true, but in certain cases we would want to wait until the settings + // are received. + bool ShouldProcessIncomingRequests(); + + void OnStreamWaitingForClientSettings(QuicStreamId id); + + // 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); + + void ProcessBufferedWebTransportStreamsForSession(WebTransportHttp3* session); + + bool CanOpenOutgoingUnidirectionalWebTransportStream( + WebTransportSessionId /*id*/) { + return CanOpenNextOutgoingUnidirectionalStream(); + } + bool CanOpenOutgoingBidirectionalWebTransportStream( + WebTransportSessionId /*id*/) { + return CanOpenNextOutgoingBidirectionalStream(); + } + + // Creates an outgoing unidirectional WebTransport stream. Returns nullptr if + // the stream cannot be created due to flow control or some other reason. + WebTransportHttp3UnidirectionalStream* + CreateOutgoingUnidirectionalWebTransportStream(WebTransportHttp3* session); + + // Creates an outgoing bidirectional WebTransport stream. Returns nullptr if + // the stream cannot be created due to flow control or some other reason. + QuicSpdyStream* CreateOutgoingBidirectionalWebTransportStream( + WebTransportHttp3* session); + + QuicSpdyStream* GetOrCreateSpdyDataStream(const QuicStreamId stream_id); + protected: // Override CreateIncomingStream(), CreateOutgoingBidirectionalStream() and // CreateOutgoingUnidirectionalStream() with QuicSpdyStream return type to @@ -426,8 +499,6 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession virtual QuicSpdyStream* CreateOutgoingBidirectionalStream() = 0; virtual QuicSpdyStream* CreateOutgoingUnidirectionalStream() = 0; - QuicSpdyStream* GetOrCreateSpdyDataStream(const QuicStreamId stream_id); - // If an incoming stream can be created, return true. virtual bool ShouldCreateIncomingStream(QuicStreamId id) = 0; @@ -436,16 +507,20 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession virtual bool ShouldCreateOutgoingBidirectionalStream() = 0; virtual bool ShouldCreateOutgoingUnidirectionalStream() = 0; + // Indicates whether the underlying backend can accept and process + // WebTransport sessions over HTTP/3. + virtual bool ShouldNegotiateWebTransport(); + // Returns true if there are open HTTP requests. bool ShouldKeepConnectionAlive() const override; // Overridden to buffer incoming unidirectional streams for version 99. bool UsesPendingStreams() const override; - // Overridden to Process HTTP/3 stream types. H/3 streams will be created from - // pending streams accordingly if the stream type can be read. Returns true if - // unidirectional streams are created. - bool ProcessPendingStream(PendingStream* pending) override; + // Processes incoming unidirectional streams; parses the stream type, and + // creates a new stream of the corresponding type. Returns the pointer to the + // newly created stream, or nullptr if the stream type is not yet available. + QuicStream* ProcessPendingStream(PendingStream* pending) override; size_t WriteHeadersOnHeadersStreamImpl( QuicStreamId id, @@ -460,20 +535,10 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession EncryptionLevel level, std::unique_ptr<QuicEncrypter> encrypter) override; - // Optional, enables instrumentation related to go/quic-hpack. - void SetHpackEncoderDebugVisitor( - std::unique_ptr<QuicHpackDebugVisitor> visitor); - void SetHpackDecoderDebugVisitor( - std::unique_ptr<QuicHpackDebugVisitor> visitor); - // Sets the maximum size of the header compression table spdy_framer_ is // willing to use to encode header blocks. void UpdateHeaderEncoderTableSize(uint32_t value); - // Called when SETTINGS_ENABLE_PUSH is received, only supported on - // server side. - void UpdateEnableServerPush(bool value); - bool IsConnected() { return connection()->connected(); } const QuicReceiveControlStream* receive_control_stream() const { @@ -488,11 +553,33 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // QuicConnectionVisitorInterface method. void BeforeConnectionCloseSent() override; + // 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(); + private: friend class test::QuicSpdySessionPeer; class SpdyFramerVisitor; + // Proxies OnDatagramProcessed() calls to the session. + class QUIC_EXPORT_PRIVATE DatagramObserver + : public QuicDatagramQueue::Observer { + public: + explicit DatagramObserver(QuicSpdySession* session) : session_(session) {} + void OnDatagramProcessed(absl::optional<MessageStatus> status) override; + + private: + QuicSpdySession* session_; // not owned + }; + + struct QUIC_EXPORT_PRIVATE BufferedWebTransportStream { + WebTransportSessionId session_id; + QuicStreamId stream_id; + }; + // The following methods are called by the SimpleVisitor. // Called when a HEADERS frame has been received. @@ -515,9 +602,6 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // established, and again when 1-RTT keys are available. void SendInitialData(); - // Send a MAX_PUSH_ID frame. Used in IETF QUIC only. - void SendMaxPushId(); - void FillSettingsFrame(); std::unique_ptr<QpackEncoder> qpack_encoder_; @@ -585,6 +669,7 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // after encryption is established, the push ID in the most recently sent // MAX_PUSH_ID frame. // Once set, never goes back to unset. + // TODO(b/171463363): Remove. absl::optional<PushId> max_push_id_; // Not owned by the session. @@ -603,11 +688,6 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // Defaults to true. bool server_push_enabled_; - // Used in IETF QUIC only. Defaults to false. - // Server push is enabled for a server by calling EnableServerPush(). - // Server push is enabled for a client by calling SetMaxPushId(). - bool ietf_server_push_enabled_; - // The identifier in the most recently received GOAWAY frame. Unset if no // GOAWAY frame has been received yet. absl::optional<uint64_t> last_received_http3_goaway_id_; @@ -615,13 +695,31 @@ class QUIC_EXPORT_PRIVATE QuicSpdySession // frame has been sent yet. absl::optional<uint64_t> last_sent_http3_goaway_id_; - // Only used by a client, only with IETF QUIC. True if a MAX_PUSH_ID frame - // has been sent, in which case |max_push_id_| has the value sent in the most - // recent MAX_PUSH_ID frame. Once true, never goes back to false. - bool http3_max_push_id_sent_; + // Value of the smallest unused HTTP/3 datagram flow ID that this endpoint's + // datagram flow ID allocation service will use next. + QuicDatagramFlowId next_available_datagram_flow_id_; + + // Whether both this endpoint and our peer support HTTP/3 datagrams. + bool h3_datagram_supported_ = false; + + // Whether the peer has indicated WebTransport support. + bool peer_supports_webtransport_ = false; + + absl::flat_hash_map<QuicDatagramFlowId, Http3DatagramVisitor*> + h3_datagram_registrations_; + + // Whether any settings have been received, either from the peer or from a + // session ticket. + bool any_settings_received_ = false; + + // If ShouldBufferRequestsUntilSettings() is true, all streams that are + // blocked by that are tracked here. + absl::flat_hash_set<QuicStreamId> streams_waiting_for_settings_; - // Latched value of reloadable flag quic_goaway_with_max_stream_id. - const bool goaway_with_max_stream_id_; + // WebTransport streams that do not have a session associated with them. + // Limited to kMaxUnassociatedWebTransportStreams; when the list is full, + // oldest streams are evicated first. + std::list<BufferedWebTransportStream> buffered_streams_; }; } // 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 c2ab731e948..d8bfa782aa7 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 @@ -11,6 +11,7 @@ #include <utility> #include "absl/base/macros.h" +#include "absl/memory/memory.h" #include "absl/strings/escaping.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" @@ -20,6 +21,8 @@ #include "quic/core/frames/quic_streams_blocked_frame.h" #include "quic/core/http/http_constants.h" #include "quic/core/http/http_encoder.h" +#include "quic/core/http/quic_header_list.h" +#include "quic/core/http/web_transport_http3.h" #include "quic/core/qpack/qpack_header_table.h" #include "quic/core/quic_config.h" #include "quic/core/quic_crypto_stream.h" @@ -33,7 +36,6 @@ #include "quic/platform/api/quic_expect_bug.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_map_util.h" -#include "quic/platform/api/quic_ptr_util.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/qpack/qpack_encoder_peer.h" #include "quic/test_tools/qpack/qpack_header_table_peer.h" @@ -48,6 +50,7 @@ #include "quic/test_tools/quic_test_utils.h" #include "common/platform/api/quiche_text_utils.h" #include "common/quiche_endian.h" +#include "common/test_tools/quiche_test_utils.h" #include "spdy/core/spdy_framer.h" using spdy::kV3HighestPriority; @@ -60,6 +63,7 @@ using spdy::SpdySerializedFrame; using ::testing::_; using ::testing::AnyNumber; using ::testing::AtLeast; +using ::testing::ElementsAre; using ::testing::InSequence; using ::testing::Invoke; using ::testing::Return; @@ -254,14 +258,14 @@ class TestSession : public QuicSpdySession { TestStream* CreateOutgoingBidirectionalStream() override { TestStream* stream = new TestStream(GetNextOutgoingBidirectionalStreamId(), this, BIDIRECTIONAL); - ActivateStream(QuicWrapUnique(stream)); + ActivateStream(absl::WrapUnique(stream)); return stream; } TestStream* CreateOutgoingUnidirectionalStream() override { TestStream* stream = new TestStream(GetNextOutgoingUnidirectionalStreamId(), this, WRITE_UNIDIRECTIONAL); - ActivateStream(QuicWrapUnique(stream)); + ActivateStream(absl::WrapUnique(stream)); return stream; } @@ -279,7 +283,7 @@ class TestSession : public QuicSpdySession { id, this, DetermineStreamType(id, connection()->version(), perspective(), /*is_incoming=*/true, BIDIRECTIONAL)); - ActivateStream(QuicWrapUnique(stream)); + ActivateStream(absl::WrapUnique(stream)); return stream; } } @@ -290,7 +294,7 @@ class TestSession : public QuicSpdySession { pending, this, DetermineStreamType(id, connection()->version(), perspective(), /*is_incoming=*/true, BIDIRECTIONAL)); - ActivateStream(QuicWrapUnique(stream)); + ActivateStream(absl::WrapUnique(stream)); return stream; } @@ -351,6 +355,9 @@ class TestSession : public QuicSpdySession { GetEncryptionLevelToSendApplicationData()); } + bool ShouldNegotiateWebTransport() override { return supports_webtransport_; } + void set_supports_webtransport(bool value) { supports_webtransport_ = value; } + MOCK_METHOD(void, OnAcceptChFrame, (const AcceptChFrame&), (override)); using QuicSession::closed_streams; @@ -362,6 +369,7 @@ class TestSession : public QuicSpdySession { StrictMock<TestCryptoStream> crypto_stream_; bool writev_consumes_all_data_; + bool supports_webtransport_ = false; }; class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { @@ -442,6 +450,8 @@ class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { closed_streams_.insert(id); } + ParsedQuicVersion version() const { return connection_->version(); } + QuicTransportVersion transport_version() const { return connection_->transport_version(); } @@ -473,13 +483,27 @@ class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { return std::string(priority_buffer.get(), priority_frame_length); } + // TODO(b/171463363): Remove. std::string SerializeMaxPushIdFrame(PushId push_id) { - MaxPushIdFrame max_push_id_frame; - max_push_id_frame.push_id = push_id; - std::unique_ptr<char[]> buffer; - QuicByteCount frame_length = - HttpEncoder::SerializeMaxPushIdFrame(max_push_id_frame, &buffer); - return std::string(buffer.get(), frame_length); + const QuicByteCount payload_length = + QuicDataWriter::GetVarInt62Len(push_id); + + const QuicByteCount total_length = + QuicDataWriter::GetVarInt62Len( + static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID)) + + QuicDataWriter::GetVarInt62Len(payload_length) + + QuicDataWriter::GetVarInt62Len(push_id); + + std::string max_push_id_frame(total_length, '\0'); + QuicDataWriter writer(total_length, &*max_push_id_frame.begin()); + + QUICHE_CHECK(writer.WriteVarInt62( + static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID))); + QUICHE_CHECK(writer.WriteVarInt62(payload_length)); + QUICHE_CHECK(writer.WriteVarInt62(push_id)); + QUICHE_CHECK_EQ(0u, writer.remaining()); + + return max_push_id_frame; } QuicStreamId StreamCountToId(QuicStreamCount stream_count, @@ -518,6 +542,55 @@ class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { testing::Mock::VerifyAndClearExpectations(connection_); } + void ReceiveWebTransportSettings() { + SettingsFrame settings; + settings.values[SETTINGS_H3_DATAGRAM] = 1; + settings.values[SETTINGS_WEBTRANS_DRAFT00] = 1; + std::string data = + std::string(1, kControlStream) + EncodeSettings(settings); + QuicStreamId control_stream_id = + session_.perspective() == Perspective::IS_SERVER + ? GetNthClientInitiatedUnidirectionalStreamId(transport_version(), + 3) + : GetNthServerInitiatedUnidirectionalStreamId(transport_version(), + 3); + QuicStreamFrame frame(control_stream_id, /*fin=*/false, /*offset=*/0, data); + session_.OnStreamFrame(frame); + } + + void ReceiveWebTransportSession(WebTransportSessionId session_id) { + SetQuicReloadableFlag(quic_accept_empty_stream_frame_with_no_fin, true); + QuicStreamFrame frame(session_id, /*fin=*/false, /*offset=*/0, + absl::string_view()); + session_.OnStreamFrame(frame); + QuicSpdyStream* stream = + static_cast<QuicSpdyStream*>(session_.GetOrCreateStream(session_id)); + QuicHeaderList headers; + headers.OnHeaderBlockStart(); + headers.OnHeader(":method", "CONNECT"); + headers.OnHeader(":protocol", "webtransport"); + headers.OnHeader("datagram-flow-id", + absl::StrCat(session_.GetNextDatagramFlowId())); + stream->OnStreamHeaderList(/*fin=*/true, 0, headers); + WebTransportHttp3* web_transport = + session_.GetWebTransportSession(session_id); + ASSERT_TRUE(web_transport != nullptr); + spdy::SpdyHeaderBlock header_block; + web_transport->HeadersReceived(header_block); + } + + void ReceiveWebTransportUnidirectionalStream(WebTransportSessionId session_id, + QuicStreamId stream_id) { + char buffer[256]; + QuicDataWriter data_writer(sizeof(buffer), buffer); + ASSERT_TRUE(data_writer.WriteVarInt62(kWebTransportUnidirectionalStream)); + ASSERT_TRUE(data_writer.WriteVarInt62(session_id)); + ASSERT_TRUE(data_writer.WriteStringPiece("test data")); + std::string data(buffer, data_writer.length()); + QuicStreamFrame frame(stream_id, /*fin=*/false, /*offset=*/0, data); + session_.OnStreamFrame(frame); + } + MockQuicConnectionHelper helper_; MockAlarmFactory alarm_factory_; StrictMock<MockQuicConnection>* connection_; @@ -1130,15 +1203,8 @@ TEST_P(QuicSpdySessionTestServer, SendHttp3GoAway) { EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); - if (GetQuicReloadableFlag(quic_goaway_with_max_stream_id)) { - // Send max stream id (currently 32 bits). - EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(/* stream_id = */ 0xfffffffc)); - } else { - // No client-initiated stream has been received, therefore a GOAWAY frame - // with stream ID = 0 is sent to notify the client that all requests can be - // retried on a different connection. - EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(/* stream_id = */ 0)); - } + // Send max stream id (currently 32 bits). + EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(/* stream_id = */ 0xfffffffc)); session_.SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Goaway"); EXPECT_TRUE(session_.goaway_sent()); @@ -1181,15 +1247,8 @@ TEST_P(QuicSpdySessionTestServer, SendHttp3GoAwayAfterStreamIsCreated) { EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); - if (GetQuicReloadableFlag(quic_goaway_with_max_stream_id)) { - // Send max stream id (currently 32 bits). - EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(/* stream_id = */ 0xfffffffc)); - } else { - // The first stream, of kTestStreamId = 0, could already have been - // processed. A GOAWAY frame is sent to notify the client that requests - // starting with stream ID = 4 can be retried on a different connection. - EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(/* stream_id = */ 4)); - } + // Send max stream id (currently 32 bits). + EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(/* stream_id = */ 0xfffffffc)); session_.SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Goaway"); EXPECT_TRUE(session_.goaway_sent()); @@ -1198,59 +1257,6 @@ TEST_P(QuicSpdySessionTestServer, SendHttp3GoAwayAfterStreamIsCreated) { session_.SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Goaway"); } -TEST_P(QuicSpdySessionTestServer, SendHttp3Shutdown) { - if (GetQuicReloadableFlag(quic_goaway_with_max_stream_id)) { - return; - } - - if (!VersionUsesHttp3(transport_version())) { - return; - } - - CompleteHandshake(); - StrictMock<MockHttp3DebugVisitor> debug_visitor; - session_.set_debug_visitor(&debug_visitor); - - EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) - .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); - EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(_)); - session_.SendHttp3Shutdown(); - EXPECT_TRUE(session_.goaway_sent()); - - const QuicStreamId kTestStreamId = - GetNthClientInitiatedBidirectionalStreamId(transport_version(), 0); - EXPECT_CALL(*connection_, OnStreamReset(kTestStreamId, _)).Times(0); - EXPECT_TRUE(session_.GetOrCreateStream(kTestStreamId)); -} - -TEST_P(QuicSpdySessionTestServer, SendHttp3GoAwayAfterShutdownNotice) { - if (GetQuicReloadableFlag(quic_goaway_with_max_stream_id)) { - return; - } - - if (!VersionUsesHttp3(transport_version())) { - return; - } - - CompleteHandshake(); - StrictMock<MockHttp3DebugVisitor> debug_visitor; - session_.set_debug_visitor(&debug_visitor); - - EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) - .Times(2) - .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); - EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(_)).Times(2); - - session_.SendHttp3Shutdown(); - EXPECT_TRUE(session_.goaway_sent()); - session_.SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Goaway"); - - const QuicStreamId kTestStreamId = - GetNthClientInitiatedBidirectionalStreamId(transport_version(), 0); - EXPECT_CALL(*connection_, OnStreamReset(kTestStreamId, _)).Times(0); - EXPECT_TRUE(session_.GetOrCreateStream(kTestStreamId)); -} - TEST_P(QuicSpdySessionTestServer, DoNotSendGoAwayTwice) { CompleteHandshake(); if (VersionHasIetfQuicFrames(transport_version())) { @@ -1833,6 +1839,7 @@ TEST_P(QuicSpdySessionTestServer, DrainingStreamsDoNotCountAsOpened) { } } +// TODO(b/171463363): Remove. TEST_P(QuicSpdySessionTestServer, ReduceMaxPushId) { if (!VersionUsesHttp3(transport_version())) { return; @@ -2924,12 +2931,12 @@ TEST_P(QuicSpdySessionTestClient, IgnoreCancelPush) { EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(_)); session_.OnStreamFrame(data2); - CancelPushFrame cancel_push{/* push_id = */ 0}; - std::unique_ptr<char[]> buffer; - auto frame_length = - HttpEncoder::SerializeCancelPushFrame(cancel_push, &buffer); + std::string cancel_push_frame = absl::HexStringToBytes( + "03" // CANCEL_PUSH + "01" // length + "00"); // push ID QuicStreamFrame data3(receive_control_stream_id, /* fin = */ false, offset, - absl::string_view(buffer.get(), frame_length)); + cancel_push_frame); EXPECT_CALL(debug_visitor, OnCancelPushFrameReceived(_)); session_.OnStreamFrame(data3); } @@ -3135,12 +3142,12 @@ TEST_P(QuicSpdySessionTestServer, IgnoreCancelPush) { EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(_)); session_.OnStreamFrame(data2); - CancelPushFrame cancel_push{/* push_id = */ 0}; - std::unique_ptr<char[]> buffer; - auto frame_length = - HttpEncoder::SerializeCancelPushFrame(cancel_push, &buffer); + std::string cancel_push_frame = absl::HexStringToBytes( + "03" // CANCEL_PUSH + "01" // length + "00"); // push ID QuicStreamFrame data3(receive_control_stream_id, /* fin = */ false, offset, - absl::string_view(buffer.get(), frame_length)); + cancel_push_frame); EXPECT_CALL(debug_visitor, OnCancelPushFrameReceived(_)); session_.OnStreamFrame(data3); } @@ -3175,13 +3182,11 @@ TEST_P(QuicSpdySessionTestServer, Http3GoAwayWhenClosingConnection) { EXPECT_EQ(stream_id, QuicSessionPeer::GetLargestPeerCreatedStreamId( &session_, /*unidirectional = */ false)); - if (GetQuicReloadableFlag(quic_send_goaway_with_connection_close)) { - // Stream with stream_id is already received and potentially processed, - // therefore a GOAWAY frame is sent with the next stream ID. - EXPECT_CALL(debug_visitor, - OnGoAwayFrameSent( - stream_id + QuicUtils::StreamIdDelta(transport_version()))); - } + // Stream with stream_id is already received and potentially processed, + // therefore a GOAWAY frame is sent with the next stream ID. + EXPECT_CALL(debug_visitor, + OnGoAwayFrameSent(stream_id + + QuicUtils::StreamIdDelta(transport_version()))); // Close connection. EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) @@ -3197,25 +3202,6 @@ TEST_P(QuicSpdySessionTestServer, Http3GoAwayWhenClosingConnection) { ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); } -TEST_P(QuicSpdySessionTestClient, SendInitialMaxPushIdIfSet) { - if (!VersionUsesHttp3(transport_version())) { - return; - } - - StrictMock<MockHttp3DebugVisitor> debug_visitor; - session_.set_debug_visitor(&debug_visitor); - - const PushId max_push_id = 5; - session_.SetMaxPushId(max_push_id); - - InSequence s; - EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_)); - const MaxPushIdFrame max_push_id_frame{max_push_id}; - EXPECT_CALL(debug_visitor, OnMaxPushIdFrameSent(max_push_id_frame)); - - CompleteHandshake(); -} - TEST_P(QuicSpdySessionTestClient, DoNotSendInitialMaxPushIdIfNotSet) { if (!VersionUsesHttp3(transport_version())) { return; @@ -3230,21 +3216,6 @@ TEST_P(QuicSpdySessionTestClient, DoNotSendInitialMaxPushIdIfNotSet) { CompleteHandshake(); } -TEST_P(QuicSpdySessionTestClient, DoNotSendInitialMaxPushIdIfSetToDefaut) { - if (!VersionUsesHttp3(transport_version())) { - return; - } - - StrictMock<MockHttp3DebugVisitor> debug_visitor; - session_.set_debug_visitor(&debug_visitor); - - session_.SetMaxPushId(0); - - InSequence s; - EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_)); - CompleteHandshake(); -} - TEST_P(QuicSpdySessionTestClient, ReceiveSpdySettingInHttp3) { if (!VersionUsesHttp3(transport_version())) { return; @@ -3268,10 +3239,6 @@ TEST_P(QuicSpdySessionTestClient, ReceiveAcceptChFrame) { return; } - if (!GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { - return; - } - StrictMock<MockHttp3DebugVisitor> debug_visitor; session_.set_debug_visitor(&debug_visitor); @@ -3330,11 +3297,9 @@ TEST_P(QuicSpdySessionTestClient, AcceptChViaAlps) { "03" // length of value "626172"); // value "bar" - if (GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { - AcceptChFrame expected_accept_ch_frame{{{"foo", "bar"}}}; - EXPECT_CALL(debug_visitor, - OnAcceptChFrameReceivedViaAlps(expected_accept_ch_frame)); - } + AcceptChFrame expected_accept_ch_frame{{{"foo", "bar"}}}; + EXPECT_CALL(debug_visitor, + OnAcceptChFrameReceivedViaAlps(expected_accept_ch_frame)); auto error = session_.OnAlpsData( reinterpret_cast<const uint8_t*>(serialized_accept_ch_frame.data()), @@ -3375,6 +3340,408 @@ TEST_P(QuicSpdySessionTestClient, AlpsIncompleteFrame) { EXPECT_EQ("incomplete HTTP/3 frame", error.value()); } +// After receiving a SETTINGS frame via ALPS, +// another SETTINGS frame is still allowed on control frame. +TEST_P(QuicSpdySessionTestClient, SettingsViaAlpsThenOnControlStream) { + if (!VersionUsesHttp3(transport_version())) { + return; + } + + QpackEncoder* qpack_encoder = session_.qpack_encoder(); + EXPECT_EQ(0u, qpack_encoder->MaximumDynamicTableCapacity()); + EXPECT_EQ(0u, qpack_encoder->maximum_blocked_streams()); + + StrictMock<MockHttp3DebugVisitor> debug_visitor; + session_.set_debug_visitor(&debug_visitor); + + std::string serialized_settings_frame1 = absl::HexStringToBytes( + "04" // type (SETTINGS) + "05" // length + "01" // SETTINGS_QPACK_MAX_TABLE_CAPACITY + "4400" // 0x0400 = 1024 + "07" // SETTINGS_QPACK_BLOCKED_STREAMS + "20"); // 0x20 = 32 + + SettingsFrame expected_settings_frame1{ + {{SETTINGS_QPACK_MAX_TABLE_CAPACITY, 1024}, + {SETTINGS_QPACK_BLOCKED_STREAMS, 32}}}; + EXPECT_CALL(debug_visitor, + OnSettingsFrameReceivedViaAlps(expected_settings_frame1)); + + auto error = session_.OnAlpsData( + reinterpret_cast<const uint8_t*>(serialized_settings_frame1.data()), + serialized_settings_frame1.size()); + EXPECT_FALSE(error); + + EXPECT_EQ(1024u, qpack_encoder->MaximumDynamicTableCapacity()); + EXPECT_EQ(32u, qpack_encoder->maximum_blocked_streams()); + + const QuicStreamId control_stream_id = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); + EXPECT_CALL(debug_visitor, OnPeerControlStreamCreated(control_stream_id)); + + std::string stream_type = absl::HexStringToBytes("00"); + session_.OnStreamFrame(QuicStreamFrame(control_stream_id, /* fin = */ false, + /* offset = */ 0, stream_type)); + + // SETTINGS_QPACK_MAX_TABLE_CAPACITY, if advertised again, MUST have identical + // value. + // SETTINGS_QPACK_BLOCKED_STREAMS is a limit. Limits MUST NOT be reduced, but + // increasing is okay. + SettingsFrame expected_settings_frame2{ + {{SETTINGS_QPACK_MAX_TABLE_CAPACITY, 1024}, + {SETTINGS_QPACK_BLOCKED_STREAMS, 48}}}; + EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(expected_settings_frame2)); + std::string serialized_settings_frame2 = absl::HexStringToBytes( + "04" // type (SETTINGS) + "05" // length + "01" // SETTINGS_QPACK_MAX_TABLE_CAPACITY + "4400" // 0x0400 = 1024 + "07" // SETTINGS_QPACK_BLOCKED_STREAMS + "30"); // 0x30 = 48 + session_.OnStreamFrame(QuicStreamFrame(control_stream_id, /* fin = */ false, + /* offset = */ stream_type.length(), + serialized_settings_frame2)); + + EXPECT_EQ(1024u, qpack_encoder->MaximumDynamicTableCapacity()); + EXPECT_EQ(48u, qpack_encoder->maximum_blocked_streams()); +} + +// A SETTINGS frame received via ALPS and another one on the control stream +// cannot have conflicting values. +TEST_P(QuicSpdySessionTestClient, + SettingsViaAlpsConflictsSettingsViaControlStream) { + if (!VersionUsesHttp3(transport_version())) { + return; + } + + QpackEncoder* qpack_encoder = session_.qpack_encoder(); + EXPECT_EQ(0u, qpack_encoder->MaximumDynamicTableCapacity()); + + std::string serialized_settings_frame1 = absl::HexStringToBytes( + "04" // type (SETTINGS) + "03" // length + "01" // SETTINGS_QPACK_MAX_TABLE_CAPACITY + "4400"); // 0x0400 = 1024 + + auto error = session_.OnAlpsData( + reinterpret_cast<const uint8_t*>(serialized_settings_frame1.data()), + serialized_settings_frame1.size()); + EXPECT_FALSE(error); + + EXPECT_EQ(1024u, qpack_encoder->MaximumDynamicTableCapacity()); + + const QuicStreamId control_stream_id = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); + + std::string stream_type = absl::HexStringToBytes("00"); + session_.OnStreamFrame(QuicStreamFrame(control_stream_id, /* fin = */ false, + /* offset = */ 0, stream_type)); + + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH, + "Server sent an SETTINGS_QPACK_MAX_TABLE_CAPACITY: " + "32 while current value is: 1024", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); + std::string serialized_settings_frame2 = absl::HexStringToBytes( + "04" // type (SETTINGS) + "02" // length + "01" // SETTINGS_QPACK_MAX_TABLE_CAPACITY + "20"); // 0x20 = 32 + session_.OnStreamFrame(QuicStreamFrame(control_stream_id, /* fin = */ false, + /* offset = */ stream_type.length(), + serialized_settings_frame2)); +} + +TEST_P(QuicSpdySessionTestClient, AlpsTwoSettingsFrame) { + if (!VersionUsesHttp3(transport_version())) { + return; + } + + std::string banned_frame = absl::HexStringToBytes( + "04" // type (SETTINGS) + "00" // length + "04" // type (SETTINGS) + "00"); // length + + auto error = + session_.OnAlpsData(reinterpret_cast<const uint8_t*>(banned_frame.data()), + banned_frame.size()); + ASSERT_TRUE(error); + EXPECT_EQ("multiple SETTINGS frames", error.value()); +} + +TEST_P(QuicSpdySessionTestClient, GetNextDatagramFlowId) { + if (!version().UsesHttp3()) { + return; + } + EXPECT_EQ(session_.GetNextDatagramFlowId(), 0u); + EXPECT_EQ(session_.GetNextDatagramFlowId(), 2u); + EXPECT_EQ(session_.GetNextDatagramFlowId(), 4u); + EXPECT_EQ(session_.GetNextDatagramFlowId(), 6u); +} + +TEST_P(QuicSpdySessionTestServer, GetNextDatagramFlowId) { + if (!version().UsesHttp3()) { + return; + } + EXPECT_EQ(session_.GetNextDatagramFlowId(), 1u); + EXPECT_EQ(session_.GetNextDatagramFlowId(), 3u); + EXPECT_EQ(session_.GetNextDatagramFlowId(), 5u); + EXPECT_EQ(session_.GetNextDatagramFlowId(), 7u); +} + +TEST_P(QuicSpdySessionTestClient, H3DatagramSetting) { + if (!version().UsesHttp3()) { + return; + } + SetQuicReloadableFlag(quic_h3_datagram, true); + // HTTP/3 datagrams aren't supported before SETTINGS are received. + EXPECT_FALSE(session_.h3_datagram_supported()); + // Receive SETTINGS. + SettingsFrame settings; + settings.values[SETTINGS_H3_DATAGRAM] = 1; + std::string data = std::string(1, kControlStream) + EncodeSettings(settings); + QuicStreamId stream_id = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 3); + QuicStreamFrame frame(stream_id, /*fin=*/false, /*offset=*/0, data); + StrictMock<MockHttp3DebugVisitor> debug_visitor; + session_.set_debug_visitor(&debug_visitor); + 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()); +} + +TEST_P(QuicSpdySessionTestClient, H3DatagramRegistration) { + if (!version().UsesHttp3()) { + return; + } + CompleteHandshake(); + SetQuicReloadableFlag(quic_h3_datagram, true); + QuicSpdySessionPeer::SetH3DatagramSupported(&session_, true); + SavingHttp3DatagramVisitor h3_datagram_visitor; + QuicDatagramFlowId flow_id = session_.GetNextDatagramFlowId(); + ASSERT_EQ(QuicDataWriter::GetVarInt62Len(flow_id), 1); + uint8_t datagram[256]; + datagram[0] = flow_id; + for (size_t i = 1; i < ABSL_ARRAYSIZE(datagram); i++) { + datagram[i] = i; + } + session_.RegisterHttp3FlowId(flow_id, &h3_datagram_visitor); + session_.OnMessageReceived(absl::string_view( + reinterpret_cast<const char*>(datagram), sizeof(datagram))); + EXPECT_THAT( + h3_datagram_visitor.received_h3_datagrams(), + ElementsAre(SavingHttp3DatagramVisitor::SavedHttp3Datagram{ + flow_id, std::string(reinterpret_cast<const char*>(datagram + 1), + sizeof(datagram) - 1)})); + session_.UnregisterHttp3FlowId(flow_id); +} + +TEST_P(QuicSpdySessionTestClient, SendHttp3Datagram) { + if (!version().UsesHttp3()) { + return; + } + CompleteHandshake(); + SetQuicReloadableFlag(quic_h3_datagram, true); + QuicSpdySessionPeer::SetH3DatagramSupported(&session_, true); + QuicDatagramFlowId flow_id = session_.GetNextDatagramFlowId(); + std::string h3_datagram_payload = {1, 2, 3, 4, 5, 6}; + EXPECT_CALL(*connection_, SendMessage(1, _, false)) + .WillOnce(Return(MESSAGE_STATUS_SUCCESS)); + EXPECT_EQ(session_.SendHttp3Datagram(flow_id, h3_datagram_payload), + MESSAGE_STATUS_SUCCESS); +} + +TEST_P(QuicSpdySessionTestClient, WebTransportSetting) { + if (!version().UsesHttp3()) { + return; + } + SetQuicReloadableFlag(quic_h3_datagram, true); + session_.set_supports_webtransport(true); + + EXPECT_FALSE(session_.SupportsWebTransport()); + + StrictMock<MockHttp3DebugVisitor> debug_visitor; + // Note that this does not actually fill out correct settings because the + // settings are filled in at the construction time. + EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_)); + session_.set_debug_visitor(&debug_visitor); + CompleteHandshake(); + + SettingsFrame server_settings; + server_settings.values[SETTINGS_H3_DATAGRAM] = 1; + server_settings.values[SETTINGS_WEBTRANS_DRAFT00] = 1; + std::string data = + std::string(1, kControlStream) + EncodeSettings(server_settings); + QuicStreamId stream_id = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 3); + QuicStreamFrame frame(stream_id, /*fin=*/false, /*offset=*/0, data); + EXPECT_CALL(debug_visitor, OnPeerControlStreamCreated(stream_id)); + EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(server_settings)); + session_.OnStreamFrame(frame); + EXPECT_TRUE(session_.SupportsWebTransport()); +} + +TEST_P(QuicSpdySessionTestClient, WebTransportSettingSetToZero) { + if (!version().UsesHttp3()) { + return; + } + SetQuicReloadableFlag(quic_h3_datagram, true); + session_.set_supports_webtransport(true); + + EXPECT_FALSE(session_.SupportsWebTransport()); + + StrictMock<MockHttp3DebugVisitor> debug_visitor; + // Note that this does not actually fill out correct settings because the + // settings are filled in at the construction time. + EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_)); + session_.set_debug_visitor(&debug_visitor); + CompleteHandshake(); + + SettingsFrame server_settings; + server_settings.values[SETTINGS_H3_DATAGRAM] = 1; + server_settings.values[SETTINGS_WEBTRANS_DRAFT00] = 0; + std::string data = + std::string(1, kControlStream) + EncodeSettings(server_settings); + QuicStreamId stream_id = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 3); + QuicStreamFrame frame(stream_id, /*fin=*/false, /*offset=*/0, data); + EXPECT_CALL(debug_visitor, OnPeerControlStreamCreated(stream_id)); + EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(server_settings)); + session_.OnStreamFrame(frame); + EXPECT_FALSE(session_.SupportsWebTransport()); +} + +TEST_P(QuicSpdySessionTestServer, WebTransportSetting) { + if (!version().UsesHttp3()) { + return; + } + SetQuicReloadableFlag(quic_h3_datagram, true); + session_.set_supports_webtransport(true); + + EXPECT_FALSE(session_.SupportsWebTransport()); + EXPECT_FALSE(session_.ShouldProcessIncomingRequests()); + + CompleteHandshake(); + + ReceiveWebTransportSettings(); + EXPECT_TRUE(session_.SupportsWebTransport()); + EXPECT_TRUE(session_.ShouldProcessIncomingRequests()); +} + +TEST_P(QuicSpdySessionTestServer, BufferingIncomingStreams) { + if (!version().UsesHttp3()) { + return; + } + SetQuicReloadableFlag(quic_h3_datagram, true); + session_.set_supports_webtransport(true); + + CompleteHandshake(); + QuicStreamId session_id = + GetNthClientInitiatedBidirectionalStreamId(transport_version(), 1); + + QuicStreamId data_stream_id = + GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 4); + ReceiveWebTransportUnidirectionalStream(session_id, data_stream_id); + + ReceiveWebTransportSettings(); + + ReceiveWebTransportSession(session_id); + WebTransportHttp3* web_transport = + session_.GetWebTransportSession(session_id); + ASSERT_TRUE(web_transport != nullptr); + + EXPECT_EQ(web_transport->NumberOfAssociatedStreams(), 1u); + + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&ClearControlFrame)); + EXPECT_CALL(*connection_, OnStreamReset(session_id, _)); + EXPECT_CALL( + *connection_, + OnStreamReset(data_stream_id, QUIC_STREAM_WEBTRANSPORT_SESSION_GONE)); + session_.ResetStream(session_id, QUIC_STREAM_INTERNAL_ERROR); +} + +TEST_P(QuicSpdySessionTestServer, BufferingIncomingStreamsLimit) { + if (!version().UsesHttp3()) { + return; + } + SetQuicReloadableFlag(quic_h3_datagram, true); + session_.set_supports_webtransport(true); + + CompleteHandshake(); + QuicStreamId session_id = + GetNthClientInitiatedBidirectionalStreamId(transport_version(), 1); + + const int streams_to_send = kMaxUnassociatedWebTransportStreams + 4; + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&ClearControlFrame)); + EXPECT_CALL(*connection_, + OnStreamReset( + _, QUIC_STREAM_WEBTRANSPORT_BUFFERED_STREAMS_LIMIT_EXCEEDED)) + .Times(4); + for (int i = 0; i < streams_to_send; i++) { + QuicStreamId data_stream_id = + GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 4 + i); + ReceiveWebTransportUnidirectionalStream(session_id, data_stream_id); + } + + ReceiveWebTransportSettings(); + + ReceiveWebTransportSession(session_id); + WebTransportHttp3* web_transport = + session_.GetWebTransportSession(session_id); + ASSERT_TRUE(web_transport != nullptr); + + EXPECT_EQ(web_transport->NumberOfAssociatedStreams(), + kMaxUnassociatedWebTransportStreams); + + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&ClearControlFrame)); + EXPECT_CALL(*connection_, OnStreamReset(_, _)) + .Times(kMaxUnassociatedWebTransportStreams + 1); + session_.ResetStream(session_id, QUIC_STREAM_INTERNAL_ERROR); +} + +TEST_P(QuicSpdySessionTestServer, ResetOutgoingWebTransportStreams) { + if (!version().UsesHttp3()) { + return; + } + SetQuicReloadableFlag(quic_h3_datagram, true); + session_.set_supports_webtransport(true); + + CompleteHandshake(); + QuicStreamId session_id = + GetNthClientInitiatedBidirectionalStreamId(transport_version(), 1); + + ReceiveWebTransportSettings(); + ReceiveWebTransportSession(session_id); + WebTransportHttp3* web_transport = + session_.GetWebTransportSession(session_id); + ASSERT_TRUE(web_transport != nullptr); + + session_.set_writev_consumes_all_data(true); + EXPECT_TRUE(web_transport->CanOpenNextOutgoingUnidirectionalStream()); + EXPECT_EQ(web_transport->NumberOfAssociatedStreams(), 0u); + WebTransportStream* stream = + web_transport->OpenOutgoingUnidirectionalStream(); + EXPECT_EQ(web_transport->NumberOfAssociatedStreams(), 1u); + ASSERT_TRUE(stream != nullptr); + QuicStreamId stream_id = stream->GetStreamId(); + + EXPECT_CALL(*connection_, SendControlFrame(_)) + .WillRepeatedly(Invoke(&ClearControlFrame)); + EXPECT_CALL(*connection_, OnStreamReset(session_id, _)); + EXPECT_CALL(*connection_, + OnStreamReset(stream_id, QUIC_STREAM_WEBTRANSPORT_SESSION_GONE)); + session_.ResetStream(session_id, QUIC_STREAM_INTERNAL_ERROR); + EXPECT_EQ(web_transport->NumberOfAssociatedStreams(), 0u); +} + } // namespace } // namespace test } // namespace quic 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 6ab6bb0aa68..4d4cc95f94d 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 @@ -5,18 +5,23 @@ #include "quic/core/http/quic_spdy_stream.h" #include <limits> +#include <memory> #include <string> #include <utility> #include "absl/base/macros.h" #include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "quic/core/http/http_constants.h" #include "quic/core/http/http_decoder.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" @@ -164,6 +169,12 @@ class QuicSpdyStream::HttpDecoderVisitor : public HttpDecoder::Visitor { return false; } + void OnWebTransportStreamFrameType( + QuicByteCount header_length, + WebTransportSessionId session_id) override { + stream_->OnWebTransportStreamFrameType(header_length, session_id); + } + bool OnUnknownFrameStart(uint64_t frame_type, QuicByteCount header_length, QuicByteCount payload_length) override { @@ -192,6 +203,16 @@ class QuicSpdyStream::HttpDecoderVisitor : public HttpDecoder::Visitor { : "Client:" \ " ") +namespace { +HttpDecoder::Options HttpDecoderOptionsForBidiStream( + QuicSpdySession* spdy_session) { + HttpDecoder::Options options; + options.allow_web_transport_stream = + spdy_session->WillNegotiateWebTransport(); + return options; +} +} // namespace + QuicSpdyStream::QuicSpdyStream(QuicStreamId id, QuicSpdySession* spdy_session, StreamType type) @@ -206,7 +227,8 @@ QuicSpdyStream::QuicSpdyStream(QuicStreamId id, trailers_decompressed_(false), trailers_consumed_(false), http_decoder_visitor_(std::make_unique<HttpDecoderVisitor>(this)), - decoder_(http_decoder_visitor_.get()), + decoder_(http_decoder_visitor_.get(), + HttpDecoderOptionsForBidiStream(spdy_session)), sequencer_offset_(0), is_decoder_processing_input_(false), ack_listener_(nullptr), @@ -269,6 +291,10 @@ size_t QuicSpdyStream::WriteHeaders( SpdyHeaderBlock header_block, bool fin, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { + if (!AssertNotWebTransportDataStream("writing headers")) { + return 0; + } + QuicConnection::ScopedPacketFlusher flusher(spdy_session_->connection()); // Send stream type for server push stream if (VersionUsesHttp3(transport_version()) && type() == WRITE_UNIDIRECTIONAL && @@ -287,6 +313,8 @@ size_t QuicSpdyStream::WriteHeaders( nullptr); } + MaybeProcessSentWebTransportHeaders(header_block); + size_t bytes_written = WriteHeadersImpl(std::move(header_block), fin, std::move(ack_listener)); if (!VersionUsesHttp3(transport_version()) && fin) { @@ -301,6 +329,9 @@ size_t QuicSpdyStream::WriteHeaders( } void QuicSpdyStream::WriteOrBufferBody(absl::string_view data, bool fin) { + if (!AssertNotWebTransportDataStream("writing body data")) { + return; + } if (!VersionUsesHttp3(transport_version()) || data.length() == 0) { WriteOrBufferData(data, fin, nullptr); return; @@ -335,7 +366,8 @@ size_t QuicSpdyStream::WriteTrailers( SpdyHeaderBlock trailer_block, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { if (fin_sent()) { - QUIC_BUG << "Trailers cannot be sent after a FIN, on stream " << id(); + QUIC_BUG(quic_bug_10410_1) + << "Trailers cannot be sent after a FIN, on stream " << id(); return 0; } @@ -347,8 +379,7 @@ size_t QuicSpdyStream::WriteTrailers( QUIC_DLOG(INFO) << ENDPOINT << "Inserting trailer: (" << kFinalOffsetHeaderKey << ", " << final_offset << ")"; trailer_block.insert( - std::make_pair(kFinalOffsetHeaderKey, - quiche::QuicheTextUtils::Uint64ToString(final_offset))); + std::make_pair(kFinalOffsetHeaderKey, absl::StrCat(final_offset))); } // Write the trailing headers with a FIN, and close stream for writing: @@ -372,35 +403,6 @@ size_t QuicSpdyStream::WriteTrailers( return bytes_written; } -void QuicSpdyStream::WritePushPromise(const PushPromiseFrame& frame) { - QUICHE_DCHECK(VersionUsesHttp3(transport_version())); - std::unique_ptr<char[]> push_promise_frame_with_id; - const size_t push_promise_frame_length = - HttpEncoder::SerializePushPromiseFrameWithOnlyPushId( - frame, &push_promise_frame_with_id); - - unacked_frame_headers_offsets_.Add(send_buffer().stream_offset(), - send_buffer().stream_offset() + - push_promise_frame_length + - frame.headers.length()); - - // Write Push Promise frame header and push id. - QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id() - << " is writing Push Promise frame header of length " - << push_promise_frame_length << " , with promised id " - << frame.push_id; - WriteOrBufferData(absl::string_view(push_promise_frame_with_id.get(), - push_promise_frame_length), - /* fin = */ false, /* ack_listener = */ nullptr); - - // Write response headers. - QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id() - << " is writing Push Promise request header of length " - << frame.headers.length(); - WriteOrBufferData(frame.headers, /* fin = */ false, - /* ack_listener = */ nullptr); -} - QuicConsumedData QuicSpdyStream::WritevBody(const struct iovec* iov, int count, bool fin) { @@ -647,6 +649,8 @@ void QuicSpdyStream::OnInitialHeadersComplete( headers_decompressed_ = true; header_list_ = header_list; + MaybeProcessReceivedWebTransportHeaders(); + if (VersionUsesHttp3(transport_version())) { if (fin) { OnStreamFrame(QuicStreamFrame(id(), /* fin = */ true, @@ -725,6 +729,11 @@ void QuicSpdyStream::OnPriorityFrame( } void QuicSpdyStream::OnStreamReset(const QuicRstStreamFrame& frame) { + if (web_transport_data_ != nullptr) { + QuicStream::OnStreamReset(frame); + return; + } + // TODO(bnc): Merge the two blocks below when both // quic_abort_qpack_on_stream_reset and quic_fix_on_stream_reset are // deprecated. @@ -765,7 +774,7 @@ void QuicSpdyStream::OnStreamReset(const QuicRstStreamFrame& frame) { void QuicSpdyStream::Reset(QuicRstStreamErrorCode error) { if (VersionUsesHttp3(transport_version()) && !fin_received() && - spdy_session_->qpack_decoder()) { + spdy_session_->qpack_decoder() && web_transport_data_ == nullptr) { QUIC_CODE_COUNT_N(quic_abort_qpack_on_stream_reset, 2, 2); spdy_session_->qpack_decoder()->OnStreamReset(id()); if (GetQuicReloadableFlag(quic_abort_qpack_on_stream_reset)) { @@ -788,6 +797,16 @@ void QuicSpdyStream::OnDataAvailable() { return; } + if (web_transport_data_ != nullptr) { + web_transport_data_->adapter.OnDataAvailable(); + return; + } + + if (!spdy_session()->ShouldProcessIncomingRequests()) { + spdy_session()->OnStreamWaitingForClientSettings(id()); + return; + } + if (is_decoder_processing_input_) { // Let the outermost nested OnDataAvailable() call do the work. return; @@ -814,6 +833,9 @@ void QuicSpdyStream::OnDataAvailable() { if (blocked_on_decoding_headers_) { return; } + if (web_transport_data_ != nullptr) { + return; + } } // Do not call OnBodyAvailable() until headers are consumed. @@ -845,6 +867,24 @@ void QuicSpdyStream::OnClose() { visitor_ = nullptr; visitor->OnClose(this); } + + if (web_transport_ != nullptr) { + web_transport_->CloseAllAssociatedStreams(); + } + if (web_transport_data_ != nullptr) { + WebTransportHttp3* web_transport = + spdy_session_->GetWebTransportSession(web_transport_data_->session_id); + if (web_transport == nullptr) { + // Since there is no guaranteed destruction order for streams, the session + // could be already removed from the stream map by the time we reach here. + QUIC_DLOG(WARNING) << ENDPOINT << "WebTransport stream " << id() + << " attempted to notify parent session " + << web_transport_data_->session_id + << ", but the session could not be found."; + return; + } + web_transport->OnStreamClosed(id()); + } } void QuicSpdyStream::OnCanWrite() { @@ -1084,6 +1124,36 @@ bool QuicSpdyStream::OnPushPromiseFrameEnd() { return OnHeadersFrameEnd(); } +void QuicSpdyStream::OnWebTransportStreamFrameType( + QuicByteCount header_length, + WebTransportSessionId session_id) { + QUIC_DVLOG(1) << ENDPOINT << " Received WEBTRANSPORT_STREAM on stream " + << id() << " for session " << session_id; + sequencer()->MarkConsumed(header_length); + + if (headers_payload_length_ > 0 || headers_decompressed_) { + QUIC_PEER_BUG(WEBTRANSPORT_STREAM received on HTTP request) + << ENDPOINT << "Stream " << id() + << " tried to convert to WebTransport, but it already " + "has HTTP data on it"; + Reset(QUIC_STREAM_FRAME_UNEXPECTED); + } + if (QuicUtils::IsOutgoingStreamId(spdy_session_->version(), id(), + spdy_session_->perspective())) { + QUIC_PEER_BUG(WEBTRANSPORT_STREAM received on outgoing request) + << ENDPOINT << "Stream " << id() + << " tried to convert to WebTransport, but only the " + "initiator of the stream can do it."; + Reset(QUIC_STREAM_FRAME_UNEXPECTED); + } + + QUICHE_DCHECK(web_transport_ == nullptr); + web_transport_data_ = + std::make_unique<WebTransportDataStream>(this, session_id); + spdy_session_->AssociateIncomingWebTransportStreamWithSession(session_id, + id()); +} + bool QuicSpdyStream::OnUnknownFrameStart(uint64_t frame_type, QuicByteCount header_length, QuicByteCount payload_length) { @@ -1162,5 +1232,137 @@ size_t QuicSpdyStream::WriteHeadersImpl( return encoded_headers.size(); } +void QuicSpdyStream::MaybeProcessReceivedWebTransportHeaders() { + if (!spdy_session_->SupportsWebTransport()) { + return; + } + if (session()->perspective() != Perspective::IS_SERVER) { + return; + } + QUICHE_DCHECK(IsValidWebTransportSessionId(id(), version())); + + std::string method; + std::string protocol; + absl::optional<QuicDatagramFlowId> flow_id; + for (const auto& header : header_list_) { + const std::string& header_name = header.first; + const std::string& header_value = header.second; + if (header_name == ":method") { + if (!method.empty() || header_value.empty()) { + return; + } + method = header_value; + } + if (header_name == ":protocol") { + if (!protocol.empty() || header_value.empty()) { + return; + } + protocol = header_value; + } + if (header_name == "datagram-flow-id") { + if (flow_id.has_value() || header_value.empty()) { + return; + } + QuicDatagramFlowId flow_id_out; + if (!absl::SimpleAtoi(header_value, &flow_id_out)) { + return; + } + flow_id = flow_id_out; + } + } + + if (method != "CONNECT" || protocol != "webtransport" || + !flow_id.has_value()) { + return; + } + + web_transport_ = + std::make_unique<WebTransportHttp3>(spdy_session_, this, id(), *flow_id); +} + +void QuicSpdyStream::MaybeProcessSentWebTransportHeaders( + spdy::SpdyHeaderBlock& headers) { + if (!spdy_session_->SupportsWebTransport()) { + return; + } + if (session()->perspective() != Perspective::IS_CLIENT) { + return; + } + QUICHE_DCHECK(IsValidWebTransportSessionId(id(), version())); + + const auto method_it = headers.find(":method"); + const auto protocol_it = headers.find(":protocol"); + if (method_it == headers.end() || protocol_it == headers.end()) { + return; + } + if (method_it->second != "CONNECT" && protocol_it->second != "webtransport") { + return; + } + + QuicDatagramFlowId flow_id = spdy_session_->GetNextDatagramFlowId(); + headers["datagram-flow-id"] = absl::StrCat(flow_id); + + web_transport_ = + std::make_unique<WebTransportHttp3>(spdy_session_, this, id(), flow_id); +} + +void QuicSpdyStream::OnCanWriteNewData() { + if (web_transport_data_ != nullptr) { + web_transport_data_->adapter.OnCanWriteNewData(); + } +} + +bool QuicSpdyStream::AssertNotWebTransportDataStream( + absl::string_view operation) { + if (web_transport_data_ != nullptr) { + QUIC_BUG(Invalid operation on WebTransport stream) + << "Attempted to " << operation << " on WebTransport data stream " + << id() << " associated with session " + << web_transport_data_->session_id; + OnUnrecoverableError(QUIC_INTERNAL_ERROR, + absl::StrCat("Attempted to ", operation, + " on WebTransport data stream")); + return false; + } + return true; +} + +void QuicSpdyStream::ConvertToWebTransportDataStream( + WebTransportSessionId session_id) { + if (send_buffer().stream_offset() != 0) { + QUIC_BUG(Sending WEBTRANSPORT_STREAM when data already sent) + << "Attempted to send a WEBTRANSPORT_STREAM frame when other data has " + "already been sent on the stream."; + OnUnrecoverableError(QUIC_INTERNAL_ERROR, + "Attempted to send a WEBTRANSPORT_STREAM frame when " + "other data has already been sent on the stream."); + return; + } + + std::unique_ptr<char[]> header; + QuicByteCount header_size = + HttpEncoder::SerializeWebTransportStreamFrameHeader(session_id, &header); + if (header_size == 0) { + QUIC_BUG(Failed to serialize WEBTRANSPORT_STREAM) + << "Failed to serialize a WEBTRANSPORT_STREAM frame."; + OnUnrecoverableError(QUIC_INTERNAL_ERROR, + "Failed to serialize a WEBTRANSPORT_STREAM frame."); + return; + } + + WriteOrBufferData(absl::string_view(header.get(), header_size), /*fin=*/false, + nullptr); + web_transport_data_ = + std::make_unique<WebTransportDataStream>(this, session_id); + QUIC_DVLOG(1) << ENDPOINT << "Successfully opened WebTransport data stream " + << id() << " for session " << session_id; +} + +QuicSpdyStream::WebTransportDataStream::WebTransportDataStream( + QuicSpdyStream* stream, + WebTransportSessionId session_id) + : session_id(session_id), + adapter(stream->spdy_session_, stream, stream->sequencer()) {} + #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 60d5e390b20..5f96d8395cf 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 @@ -13,6 +13,7 @@ #include <cstddef> #include <list> +#include <memory> #include <string> #include "absl/strings/string_view.h" @@ -24,10 +25,14 @@ #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" #include "spdy/core/spdy_framer.h" +#include "spdy/core/spdy_header_block.h" namespace quic { @@ -37,6 +42,7 @@ class QuicStreamPeer; } // namespace test class QuicSpdySession; +class WebTransportHttp3; // A QUIC stream that can send and receive HTTP2 (SPDY) headers. class QUIC_EXPORT_PRIVATE QuicSpdyStream @@ -132,9 +138,6 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream spdy::SpdyHeaderBlock trailer_block, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); - // Serializes |frame| and writes the encoded push promise data. - void WritePushPromise(const PushPromiseFrame& frame); - // Override to report newly acked bytes via ack_listener_. bool OnStreamFrameAcked(QuicStreamOffset offset, QuicByteCount data_length, @@ -165,8 +168,6 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream // Clears |header_list_|. void ConsumeHeaderList(); - void SetUnblocked() { sequencer()->SetUnblocked(); } - // This block of functions wraps the sequencer's functions of the same // name. These methods return uncompressed data until that has // been fully processed. Then they simply delegate to the sequencer. @@ -222,6 +223,27 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream // |last_sent_urgency_| is different from current priority. void MaybeSendPriorityUpdateFrame() override; + // Returns the WebTransport session owned by this stream, if one exists. + WebTransportHttp3* web_transport() { return web_transport_.get(); } + + // Returns the WebTransport data stream associated with this QUIC stream, or + // null if this is not a WebTransport data stream. + WebTransportStream* web_transport_stream() { + if (web_transport_data_ == nullptr) { + return nullptr; + } + return &web_transport_data_->adapter; + } + + // Sends a WEBTRANSPORT_STREAM frame and sets up the appropriate metadata. + void ConvertToWebTransportDataStream(WebTransportSessionId session_id); + + void OnCanWriteNewData() override; + + // If this stream is a WebTransport data stream, closes the connection with an + // error, and returns false. + bool AssertNotWebTransportDataStream(absl::string_view operation); + protected: // Called when the received headers are too large. By default this will // reset the stream. @@ -253,6 +275,14 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream friend class QuicStreamUtils; class HttpDecoderVisitor; + struct QUIC_EXPORT_PRIVATE WebTransportDataStream { + WebTransportDataStream(QuicSpdyStream* stream, + WebTransportSessionId session_id); + + WebTransportSessionId session_id; + WebTransportStreamAdapter adapter; + }; + // Called by HttpDecoderVisitor. bool OnDataFrameStart(QuicByteCount header_length, QuicByteCount payload_length); @@ -268,6 +298,8 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream QuicByteCount header_block_length); bool OnPushPromiseFramePayload(absl::string_view payload); bool OnPushPromiseFrameEnd(); + void OnWebTransportStreamFrameType(QuicByteCount header_length, + WebTransportSessionId session_id); bool OnUnknownFrameStart(uint64_t frame_type, QuicByteCount header_length, QuicByteCount payload_length); @@ -279,6 +311,9 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream QuicByteCount GetNumFrameHeadersInInterval(QuicStreamOffset offset, QuicByteCount data_length) const; + void MaybeProcessSentWebTransportHeaders(spdy::SpdyHeaderBlock& headers); + void MaybeProcessReceivedWebTransportHeaders(); + QuicSpdySession* spdy_session_; bool on_body_available_called_because_sequencer_is_closed_; @@ -339,6 +374,14 @@ class QUIC_EXPORT_PRIVATE QuicSpdyStream // Urgency value sent in the last PRIORITY_UPDATE frame, or default urgency // defined by the spec if no PRIORITY_UPDATE frame has been sent. int last_sent_urgency_; + + // If this stream is a WebTransport extended CONNECT stream, contains the + // WebTransport session associated with this stream. + std::unique_ptr<WebTransportHttp3> web_transport_; + + // If this stream is a WebTransport data stream, |web_transport_data_| + // contains all of the associated metadata. + std::unique_ptr<WebTransportDataStream> web_transport_data_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.cc index 1d40e03d186..2ae24d472b7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_manager.cc @@ -41,7 +41,7 @@ size_t QuicSpdyStreamBodyManager::OnBodyConsumed(size_t num_bytes) { while (remaining_bytes > 0) { if (fragments_.empty()) { - QUIC_BUG << "Not enough available body to consume."; + QUIC_BUG(quic_bug_10394_1) << "Not enough available body to consume."; return 0; } 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 0e35c28aa4f..bb464bdb4d5 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 @@ -10,11 +10,14 @@ #include <utility> #include "absl/base/macros.h" +#include "absl/memory/memory.h" #include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "quic/core/crypto/null_encrypter.h" #include "quic/core/http/http_encoder.h" #include "quic/core/http/spdy_utils.h" +#include "quic/core/http/web_transport_http3.h" #include "quic/core/quic_connection.h" #include "quic/core/quic_stream_sequencer_buffer.h" #include "quic/core/quic_utils.h" @@ -22,7 +25,6 @@ #include "quic/core/quic_write_blocked_list.h" #include "quic/platform/api/quic_expect_bug.h" #include "quic/platform/api/quic_map_util.h" -#include "quic/platform/api/quic_ptr_util.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/qpack/qpack_test_utils.h" #include "quic/test_tools/quic_config_peer.h" @@ -33,7 +35,6 @@ #include "quic/test_tools/quic_spdy_stream_peer.h" #include "quic/test_tools/quic_stream_peer.h" #include "quic/test_tools/quic_test_utils.h" -#include "common/platform/api/quiche_text_utils.h" using spdy::kV3HighestPriority; using spdy::kV3LowestPriority; @@ -255,7 +256,11 @@ class TestSession : public MockQuicSpdySession { return &crypto_stream_; } + bool ShouldNegotiateWebTransport() override { return enable_webtransport_; } + void EnableWebTransport() { enable_webtransport_ = true; } + private: + bool enable_webtransport_ = false; StrictMock<TestCryptoStream> crypto_stream_; }; @@ -365,11 +370,11 @@ class QuicSpdyStreamTest : public QuicTestWithParam<ParsedQuicVersion> { stream_ = new StrictMock<TestStream>(GetNthClientInitiatedBidirectionalId(0), session_.get(), stream_should_process_data); - session_->ActivateStream(QuicWrapUnique(stream_)); + session_->ActivateStream(absl::WrapUnique(stream_)); stream2_ = new StrictMock<TestStream>(GetNthClientInitiatedBidirectionalId(1), session_.get(), stream_should_process_data); - session_->ActivateStream(QuicWrapUnique(stream2_)); + session_->ActivateStream(absl::WrapUnique(stream2_)); QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow( session_->config(), kMinimumFlowControlSendWindow); QuicConfigPeer::SetReceivedInitialMaxStreamDataBytesUnidirectional( @@ -443,18 +448,30 @@ class QuicSpdyStreamTest : public QuicTestWithParam<ParsedQuicVersion> { } // Construct PUSH_PROMISE frame with given payload. + // TODO(b/171463363): Remove. std::string SerializePushPromiseFrame(PushId push_id, - absl::string_view payload) { - PushPromiseFrame frame; - frame.push_id = push_id; - frame.headers = payload; - std::unique_ptr<char[]> push_promise_buffer; - QuicByteCount push_promise_frame_header_length = - HttpEncoder::SerializePushPromiseFrameWithOnlyPushId( - frame, &push_promise_buffer); - absl::string_view push_promise_frame_header( - push_promise_buffer.get(), push_promise_frame_header_length); - return absl::StrCat(push_promise_frame_header, payload); + absl::string_view headers) { + const QuicByteCount payload_length = + QuicDataWriter::GetVarInt62Len(push_id) + headers.length(); + + const QuicByteCount length_without_headers = + QuicDataWriter::GetVarInt62Len( + static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE)) + + QuicDataWriter::GetVarInt62Len(payload_length) + + QuicDataWriter::GetVarInt62Len(push_id); + + std::string push_promise_frame(length_without_headers, '\0'); + QuicDataWriter writer(length_without_headers, &*push_promise_frame.begin()); + + QUICHE_CHECK(writer.WriteVarInt62( + static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE))); + QUICHE_CHECK(writer.WriteVarInt62(payload_length)); + QUICHE_CHECK(writer.WriteVarInt62(push_id)); + QUICHE_CHECK_EQ(0u, writer.remaining()); + + absl::StrAppend(&push_promise_frame, headers); + + return push_promise_frame; } std::string DataFrame(absl::string_view payload) { @@ -552,6 +569,23 @@ TEST_P(QuicSpdyStreamTest, ProcessTooLargeHeaderList) { EXPECT_THAT(stream_->stream_error(), IsStreamError(QUIC_HEADERS_TOO_LARGE)); } +TEST_P(QuicSpdyStreamTest, QpackProcessLargeHeaderListDiscountOverhead) { + if (!UsesHttp3()) { + return; + } + // Setting this flag to false causes no per-entry overhead to be included + // in the header size. + SetQuicFlag(FLAGS_quic_header_size_limit_includes_overhead, false); + Initialize(kShouldProcessData); + session_->set_max_inbound_header_list_size(40); + std::string headers = + HeadersFrame({std::make_pair("foo", "too long headers")}); + + QuicStreamFrame frame(stream_->id(), false, 0, headers); + stream_->OnStreamFrame(frame); + EXPECT_THAT(stream_->stream_error(), IsStreamError(QUIC_STREAM_NO_ERROR)); +} + TEST_P(QuicSpdyStreamTest, ProcessHeaderListWithFin) { Initialize(kShouldProcessData); @@ -1276,8 +1310,7 @@ TEST_P(QuicSpdyStreamTest, ReceivingTrailersWithOffset) { trailers_block["key1"] = "value1"; trailers_block["key2"] = "value2"; trailers_block["key3"] = "value3"; - trailers_block[kFinalOffsetHeaderKey] = - quiche::QuicheTextUtils::Uint64ToString(data.size()); + trailers_block[kFinalOffsetHeaderKey] = absl::StrCat(data.size()); QuicHeaderList trailers = ProcessHeaders(true, trailers_block); @@ -1578,7 +1611,7 @@ TEST_P(QuicSpdyStreamTest, WritingTrailersFinalOffset) { // on the headers stream. if (!UsesHttp3()) { expected_trailers[kFinalOffsetHeaderKey] = - quiche::QuicheTextUtils::Uint64ToString(body.length() + header_length); + absl::StrCat(body.length() + header_length); } EXPECT_CALL(*stream_, WriteHeadersMock(true)); @@ -1761,7 +1794,7 @@ TEST_P(QuicSpdyStreamTest, SetPriorityBeforeUpdateStreamPriority) { session->transport_version(), 0), session.get(), /*should_process_data=*/true); - session->ActivateStream(QuicWrapUnique(stream)); + session->ActivateStream(absl::WrapUnique(stream)); // QuicSpdyStream::SetPriority() should eventually call UpdateStreamPriority() // on the session. Make sure stream->priority() returns the updated priority @@ -2736,6 +2769,7 @@ TEST_P(QuicSpdyStreamIncrementalConsumptionTest, UnknownFramesInterleaved) { EXPECT_EQ(unknown_frame4.size(), NewlyConsumedBytes()); } +// TODO(b/171463363): Remove. TEST_P(QuicSpdyStreamTest, PushPromiseOnDataStream) { Initialize(kShouldProcessData); if (!UsesHttp3()) { @@ -2764,6 +2798,7 @@ TEST_P(QuicSpdyStreamTest, PushPromiseOnDataStream) { } // Regression test for b/152518220. +// TODO(b/171463363): Remove. TEST_P(QuicSpdyStreamTest, OnStreamHeaderBlockArgumentDoesNotIncludePushedHeaderBlock) { Initialize(kShouldProcessData); @@ -3071,6 +3106,52 @@ TEST_P(QuicSpdyStreamTest, TwoResetStreamFrames) { } } +TEST_P(QuicSpdyStreamTest, ProcessOutgoingWebTransportHeaders) { + if (!UsesHttp3()) { + return; + } + + InitializeWithPerspective(kShouldProcessData, Perspective::IS_CLIENT); + session_->EnableWebTransport(); + QuicSpdySessionPeer::EnableWebTransport(*session_); + + EXPECT_CALL(*stream_, WriteHeadersMock(false)); + EXPECT_CALL(*session_, WritevData(stream_->id(), _, _, _, _, _)) + .Times(AnyNumber()); + + spdy::SpdyHeaderBlock headers; + headers[":method"] = "CONNECT"; + headers[":protocol"] = "webtransport"; + headers["datagram-flow-id"] = absl::StrCat(session_->GetNextDatagramFlowId()); + 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, ProcessIncomingWebTransportHeaders) { + if (!UsesHttp3()) { + return; + } + + Initialize(kShouldProcessData); + session_->EnableWebTransport(); + QuicSpdySessionPeer::EnableWebTransport(*session_); + + headers_[":method"] = "CONNECT"; + headers_[":protocol"] = "webtransport"; + headers_["datagram-flow-id"] = + absl::StrCat(session_->GetNextDatagramFlowId()); + + stream_->OnStreamHeadersPriority( + spdy::SpdyStreamPrecedence(kV3HighestPriority)); + ProcessHeaders(false, headers_); + 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()); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.cc b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.cc index a3d800af5d4..d3a3c92d209 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.cc @@ -12,6 +12,7 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "quic/platform/api/quic_flag_utils.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" @@ -153,4 +154,45 @@ bool SpdyUtils::PopulateHeaderBlockFromUrl(const std::string url, return true; } +// static +absl::optional<QuicDatagramFlowId> SpdyUtils::ParseDatagramFlowIdHeader( + const spdy::SpdyHeaderBlock& headers) { + auto flow_id_pair = headers.find("datagram-flow-id"); + if (flow_id_pair == headers.end()) { + return absl::nullopt; + } + std::vector<absl::string_view> flow_id_strings = + absl::StrSplit(flow_id_pair->second, ','); + absl::optional<QuicDatagramFlowId> first_named_flow_id; + for (absl::string_view flow_id_string : flow_id_strings) { + std::vector<absl::string_view> flow_id_components = + absl::StrSplit(flow_id_string, ';'); + if (flow_id_components.empty()) { + continue; + } + absl::string_view flow_id_value_string = flow_id_components[0]; + quiche::QuicheTextUtils::RemoveLeadingAndTrailingWhitespace( + &flow_id_value_string); + QuicDatagramFlowId flow_id; + if (!absl::SimpleAtoi(flow_id_value_string, &flow_id)) { + continue; + } + if (flow_id_components.size() == 1) { + // This flow ID is unnamed, return this one. + return flow_id; + } + // Otherwise this is a named flow ID. + if (!first_named_flow_id.has_value()) { + first_named_flow_id = flow_id; + } + } + return first_named_flow_id; +} + +// static +void SpdyUtils::AddDatagramFlowIdHeader(spdy::SpdyHeaderBlock* headers, + QuicDatagramFlowId flow_id) { + (*headers)["datagram-flow-id"] = absl::StrCat(flow_id); +} + } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.h b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.h index a3b3134fe37..e3a8a97de48 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.h +++ b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.h @@ -9,6 +9,7 @@ #include <cstdint> #include <string> +#include "absl/types/optional.h" #include "quic/core/http/http_constants.h" #include "quic/core/http/quic_header_list.h" #include "quic/core/quic_packets.h" @@ -51,6 +52,15 @@ class QUIC_EXPORT_PRIVATE SpdyUtils { // which must be fully-qualified. static bool PopulateHeaderBlockFromUrl(const std::string url, spdy::SpdyHeaderBlock* headers); + + // Parses the "datagram-flow-id" header, returns the flow ID on success, or + // returns absl::nullopt if the header was not present or failed to parse. + static absl::optional<QuicDatagramFlowId> ParseDatagramFlowIdHeader( + const spdy::SpdyHeaderBlock& headers); + + // Adds the "datagram-flow-id" header. + static void AddDatagramFlowIdHeader(spdy::SpdyHeaderBlock* headers, + QuicDatagramFlowId flow_id); }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils_test.cc index 5cb391dc64c..c7207f383b3 100644 --- a/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils_test.cc @@ -33,6 +33,14 @@ static std::unique_ptr<QuicHeaderList> FromList( return headers; } +static void ValidateDatagramFlowId( + const std::string& header_value, + absl::optional<QuicDatagramFlowId> expected_flow_id) { + SpdyHeaderBlock headers; + headers["datagram-flow-id"] = header_value; + ASSERT_EQ(SpdyUtils::ParseDatagramFlowIdHeader(headers), expected_flow_id); +} + } // anonymous namespace using CopyAndValidateHeaders = QuicTest; @@ -377,5 +385,29 @@ TEST_F(PopulateHeaderBlockFromUrl, Failure) { SpdyUtils::PopulateHeaderBlockFromUrl("www.google.com/", &headers)); } +using DatagramFlowIdTest = QuicTest; + +TEST_F(DatagramFlowIdTest, DatagramFlowId) { + // Test missing header. + SpdyHeaderBlock headers; + EXPECT_EQ(SpdyUtils::ParseDatagramFlowIdHeader(headers), absl::nullopt); + // Add header and verify it parses. + QuicDatagramFlowId flow_id = 123; + SpdyUtils::AddDatagramFlowIdHeader(&headers, flow_id); + EXPECT_EQ(SpdyUtils::ParseDatagramFlowIdHeader(headers), flow_id); + // Test empty header. + ValidateDatagramFlowId("", absl::nullopt); + // Test invalid header. + ValidateDatagramFlowId("M4SQU3", absl::nullopt); + // Test simple header. + ValidateDatagramFlowId("42", 42); + // Test with parameter. + ValidateDatagramFlowId("42; abc=def", 42); + // Test list. + ValidateDatagramFlowId("42, 44; ecn-ect0, 46; ecn-ect1, 48; ecn-ce", 42); + // Test reordered list. + ValidateDatagramFlowId("44; ecn-ect0, 42, 48; ecn-ce, 46; ecn-ect1", 42); +} + } // 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 new file mode 100644 index 00000000000..2a105c2e5bf --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.cc @@ -0,0 +1,271 @@ +// 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 <memory> + +#include "absl/strings/string_view.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_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_bug_tracker.h" +#include "common/platform/api/quiche_logging.h" + +#define ENDPOINT \ + (session_->perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ") + +namespace quic { + +namespace { +class QUIC_NO_EXPORT NoopWebTransportVisitor : public WebTransportVisitor { + void OnSessionReady() override {} + void OnIncomingBidirectionalStreamAvailable() override {} + void OnIncomingUnidirectionalStreamAvailable() override {} + void OnDatagramReceived(absl::string_view /*datagram*/) override {} + void OnCanCreateNewOutgoingBidirectionalStream() override {} + void OnCanCreateNewOutgoingUnidirectionalStream() override {} +}; +} // namespace + +WebTransportHttp3::WebTransportHttp3(QuicSpdySession* session, + QuicSpdyStream* connect_stream, + WebTransportSessionId id, + QuicDatagramFlowId flow_id) + : session_(session), + connect_stream_(connect_stream), + id_(id), + flow_id_(flow_id), + visitor_(std::make_unique<NoopWebTransportVisitor>()) { + QUICHE_DCHECK(session_->SupportsWebTransport()); + QUICHE_DCHECK(IsValidWebTransportSessionId(id, session_->version())); + QUICHE_DCHECK_EQ(connect_stream_->id(), id); + session_->RegisterHttp3FlowId(flow_id, this); +} + +void WebTransportHttp3::AssociateStream(QuicStreamId stream_id) { + streams_.insert(stream_id); + + ParsedQuicVersion version = session_->version(); + if (QuicUtils::IsOutgoingStreamId(version, stream_id, + session_->perspective())) { + return; + } + if (QuicUtils::IsBidirectionalStreamId(stream_id, version)) { + incoming_bidirectional_streams_.push_back(stream_id); + visitor_->OnIncomingBidirectionalStreamAvailable(); + } else { + incoming_unidirectional_streams_.push_back(stream_id); + visitor_->OnIncomingUnidirectionalStreamAvailable(); + } +} + +void WebTransportHttp3::CloseAllAssociatedStreams() { + // 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()); + streams_.clear(); + for (QuicStreamId id : streams) { + session_->ResetStream(id, QUIC_STREAM_WEBTRANSPORT_SESSION_GONE); + } + session_->UnregisterHttp3FlowId(flow_id_); +} + +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") { + QUIC_DVLOG(1) << ENDPOINT + << "Received WebTransport headers from server without " + "status 200, rejecting."; + return; + } + } + + QUIC_DVLOG(1) << ENDPOINT << "WebTransport session " << id_ << " ready."; + ready_ = true; + visitor_->OnSessionReady(); + session_->ProcessBufferedWebTransportStreamsForSession(this); +} + +WebTransportStream* WebTransportHttp3::AcceptIncomingBidirectionalStream() { + while (!incoming_bidirectional_streams_.empty()) { + QuicStreamId id = incoming_bidirectional_streams_.front(); + incoming_bidirectional_streams_.pop_front(); + QuicSpdyStream* stream = session_->GetOrCreateSpdyDataStream(id); + if (stream == nullptr) { + // Skip the streams that were reset in between the time they were + // receieved and the time the client has polled for them. + continue; + } + return stream->web_transport_stream(); + } + return nullptr; +} + +WebTransportStream* WebTransportHttp3::AcceptIncomingUnidirectionalStream() { + while (!incoming_unidirectional_streams_.empty()) { + QuicStreamId id = incoming_unidirectional_streams_.front(); + incoming_unidirectional_streams_.pop_front(); + QuicStream* stream = session_->GetOrCreateStream(id); + if (stream == nullptr) { + // Skip the streams that were reset in between the time they were + // receieved and the time the client has polled for them. + continue; + } + return static_cast<WebTransportHttp3UnidirectionalStream*>(stream) + ->interface(); + } + return nullptr; +} + +bool WebTransportHttp3::CanOpenNextOutgoingBidirectionalStream() { + return session_->CanOpenOutgoingBidirectionalWebTransportStream(id_); +} +bool WebTransportHttp3::CanOpenNextOutgoingUnidirectionalStream() { + return session_->CanOpenOutgoingUnidirectionalWebTransportStream(id_); +} +WebTransportStream* WebTransportHttp3::OpenOutgoingBidirectionalStream() { + QuicSpdyStream* stream = + session_->CreateOutgoingBidirectionalWebTransportStream(this); + if (stream == nullptr) { + // If stream cannot be created due to flow control or other errors, return + // nullptr. + return nullptr; + } + return stream->web_transport_stream(); +} + +WebTransportStream* WebTransportHttp3::OpenOutgoingUnidirectionalStream() { + WebTransportHttp3UnidirectionalStream* stream = + session_->CreateOutgoingUnidirectionalWebTransportStream(this); + if (stream == nullptr) { + // If stream cannot be created due to flow control, return nullptr. + return nullptr; + } + return stream->interface(); +} + +MessageStatus WebTransportHttp3::SendOrQueueDatagram(QuicMemSlice datagram) { + return session_->SendHttp3Datagram( + flow_id_, absl::string_view(datagram.data(), datagram.length())); +} + +void WebTransportHttp3::SetDatagramMaxTimeInQueue( + QuicTime::Delta max_time_in_queue) { + session_->SetMaxTimeInQueueForFlowId(flow_id_, max_time_in_queue); +} + +void WebTransportHttp3::OnHttp3Datagram(QuicDatagramFlowId flow_id, + absl::string_view payload) { + QUICHE_DCHECK_EQ(flow_id, flow_id_); + visitor_->OnDatagramReceived(payload); +} + +WebTransportHttp3UnidirectionalStream::WebTransportHttp3UnidirectionalStream( + PendingStream* pending, + QuicSpdySession* session) + : QuicStream(pending, session, READ_UNIDIRECTIONAL, /*is_static=*/false), + session_(session), + adapter_(session, this, sequencer()), + needs_to_send_preamble_(false) {} + +WebTransportHttp3UnidirectionalStream::WebTransportHttp3UnidirectionalStream( + QuicStreamId id, + QuicSpdySession* session, + WebTransportSessionId session_id) + : QuicStream(id, session, /*is_static=*/false, WRITE_UNIDIRECTIONAL), + session_(session), + adapter_(session, this, sequencer()), + session_id_(session_id), + needs_to_send_preamble_(true) {} + +void WebTransportHttp3UnidirectionalStream::WritePreamble() { + if (!needs_to_send_preamble_ || !session_id_.has_value()) { + QUIC_BUG(WebTransportHttp3UnidirectionalStream duplicate preamble) + << ENDPOINT << "Sending preamble on stream ID " << id() + << " at the wrong time."; + OnUnrecoverableError(QUIC_INTERNAL_ERROR, + "Attempting to send a WebTransport unidirectional " + "stream preamble at the wrong time."); + return; + } + + QuicConnection::ScopedPacketFlusher flusher(session_->connection()); + char buffer[sizeof(uint64_t) * 2]; // varint62, varint62 + QuicDataWriter writer(sizeof(buffer), buffer); + bool success = true; + success = success && writer.WriteVarInt62(kWebTransportUnidirectionalStream); + success = success && writer.WriteVarInt62(*session_id_); + QUICHE_DCHECK(success); + WriteOrBufferData(absl::string_view(buffer, writer.length()), /*fin=*/false, + /*ack_listener=*/nullptr); + QUIC_DVLOG(1) << ENDPOINT << "Sent stream type and session ID (" + << *session_id_ << ") on WebTransport stream " << id(); + needs_to_send_preamble_ = false; +} + +bool WebTransportHttp3UnidirectionalStream::ReadSessionId() { + iovec iov; + if (!sequencer()->GetReadableRegion(&iov)) { + return false; + } + QuicDataReader reader(static_cast<const char*>(iov.iov_base), iov.iov_len); + WebTransportSessionId session_id; + uint8_t session_id_length = reader.PeekVarInt62Length(); + if (!reader.ReadVarInt62(&session_id)) { + // If all of the data has been received, and we still cannot associate the + // stream with a session, consume all of the data so that the stream can + // be closed. + if (sequencer()->NumBytesConsumed() + sequencer()->NumBytesBuffered() >= + sequencer()->close_offset()) { + QUIC_DLOG(WARNING) + << ENDPOINT << "Failed to associate WebTransport stream " << id() + << " with a session because the stream ended prematurely."; + sequencer()->MarkConsumed(sequencer()->NumBytesBuffered()); + } + return false; + } + sequencer()->MarkConsumed(session_id_length); + session_id_ = session_id; + session_->AssociateIncomingWebTransportStreamWithSession(session_id, id()); + return true; +} + +void WebTransportHttp3UnidirectionalStream::OnDataAvailable() { + if (!session_id_.has_value()) { + if (!ReadSessionId()) { + return; + } + } + + adapter_.OnDataAvailable(); +} + +void WebTransportHttp3UnidirectionalStream::OnCanWriteNewData() { + adapter_.OnCanWriteNewData(); +} + +void WebTransportHttp3UnidirectionalStream::OnClose() { + QuicStream::OnClose(); + + if (!session_id_.has_value()) { + return; + } + WebTransportHttp3* session = session_->GetWebTransportSession(*session_id_); + if (session == nullptr) { + QUIC_DLOG(WARNING) << ENDPOINT << "WebTransport stream " << id() + << " attempted to notify parent session " << *session_id_ + << ", but the session could not be found."; + return; + } + session->OnStreamClosed(id()); +} + +} // 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 new file mode 100644 index 00000000000..0c91c2b32a4 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/http/web_transport_http3.h @@ -0,0 +1,117 @@ +// 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_QUIC_CORE_HTTP_WEB_TRANSPORT_HTTP3_H_ +#define QUICHE_QUIC_CORE_HTTP_WEB_TRANSPORT_HTTP3_H_ + +#include <memory> + +#include "absl/container/flat_hash_set.h" +#include "absl/types/optional.h" +#include "quic/core/http/quic_spdy_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 "spdy/core/spdy_header_block.h" + +namespace quic { + +class QuicSpdySession; +class QuicSpdyStream; + +// A session of WebTransport over HTTP/3. The session is owned by +// QuicSpdyStream object for the CONNECT stream that established it. +// +// WebTransport over HTTP/3 specification: +// <https://datatracker.ietf.org/doc/html/draft-ietf-webtrans-http3> +class QUIC_EXPORT_PRIVATE WebTransportHttp3 + : public WebTransportSession, + public QuicSpdySession::Http3DatagramVisitor { + public: + WebTransportHttp3(QuicSpdySession* session, + QuicSpdyStream* connect_stream, + WebTransportSessionId id, + QuicDatagramFlowId flow_id); + + void HeadersReceived(const spdy::SpdyHeaderBlock& headers); + void SetVisitor(std::unique_ptr<WebTransportVisitor> visitor) { + visitor_ = std::move(visitor); + } + + WebTransportSessionId id() { return id_; } + bool ready() { return ready_; } + + void AssociateStream(QuicStreamId stream_id); + void OnStreamClosed(QuicStreamId stream_id) { streams_.erase(stream_id); } + void CloseAllAssociatedStreams(); + + size_t NumberOfAssociatedStreams() { return streams_.size(); } + + // 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. + WebTransportStream* AcceptIncomingBidirectionalStream() override; + WebTransportStream* AcceptIncomingUnidirectionalStream() override; + + bool CanOpenNextOutgoingBidirectionalStream() override; + bool CanOpenNextOutgoingUnidirectionalStream() override; + WebTransportStream* OpenOutgoingBidirectionalStream() override; + WebTransportStream* OpenOutgoingUnidirectionalStream() override; + + MessageStatus SendOrQueueDatagram(QuicMemSlice datagram) override; + void SetDatagramMaxTimeInQueue(QuicTime::Delta max_time_in_queue) override; + + void OnHttp3Datagram(QuicDatagramFlowId flow_id, + absl::string_view payload) override; + + private: + QuicSpdySession* const session_; // Unowned. + QuicSpdyStream* const connect_stream_; // Unowned. + const WebTransportSessionId id_; + const QuicDatagramFlowId flow_id_; + // |ready_| is set to true when the peer has seen both sets of headers. + bool ready_ = false; + std::unique_ptr<WebTransportVisitor> visitor_; + absl::flat_hash_set<QuicStreamId> streams_; + QuicCircularDeque<QuicStreamId> incoming_bidirectional_streams_; + QuicCircularDeque<QuicStreamId> incoming_unidirectional_streams_; +}; + +class QUIC_EXPORT_PRIVATE WebTransportHttp3UnidirectionalStream + : public QuicStream { + public: + // Incoming stream. + WebTransportHttp3UnidirectionalStream(PendingStream* pending, + QuicSpdySession* session); + // Outgoing stream. + WebTransportHttp3UnidirectionalStream(QuicStreamId id, + QuicSpdySession* session, + WebTransportSessionId session_id); + + // Sends the stream type and the session ID on the stream. + void WritePreamble(); + + // Implementation of QuicStream. + void OnDataAvailable() override; + void OnCanWriteNewData() override; + void OnClose() override; + + WebTransportStream* interface() { return &adapter_; } + void SetUnblocked() { sequencer()->SetUnblocked(); } + + private: + QuicSpdySession* session_; + WebTransportStreamAdapter adapter_; + absl::optional<WebTransportSessionId> session_id_; + bool needs_to_send_preamble_; + + bool ReadSessionId(); + // Closes the stream if all of the data has been received. + void MaybeCloseIncompleteStream(); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_HTTP_WEB_TRANSPORT_HTTP3_H_ diff --git a/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.cc b/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.cc index 07403f8de08..2ddf4b2498a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.cc @@ -107,11 +107,11 @@ void LegacyQuicStreamIdManager::ActivateStream(bool is_incoming) { void LegacyQuicStreamIdManager::OnStreamClosed(bool is_incoming) { if (is_incoming) { - QUIC_BUG_IF(num_open_incoming_streams_ == 0); + QUIC_BUG_IF(quic_bug_12720_1, num_open_incoming_streams_ == 0); --num_open_incoming_streams_; return; } - QUIC_BUG_IF(num_open_outgoing_streams_ == 0); + QUIC_BUG_IF(quic_bug_12720_2, num_open_outgoing_streams_ == 0); --num_open_outgoing_streams_; } diff --git a/chromium/net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h b/chromium/net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h index eec75dfdf0f..b83916060e9 100644 --- a/chromium/net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h +++ b/chromium/net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h @@ -147,7 +147,8 @@ template <typename... Args> bool PacketNumberIndexedQueue<T>::Emplace(QuicPacketNumber packet_number, Args&&... args) { if (!packet_number.IsInitialized()) { - QUIC_BUG << "Try to insert an uninitialized packet number"; + QUIC_BUG(quic_bug_10359_1) + << "Try to insert an uninitialized packet number"; return false; } 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 f5ddeee36a4..9a5af7e42c3 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 @@ -38,9 +38,13 @@ void QpackDecodedHeadersAccumulator::OnHeaderDecoded(absl::string_view name, } uncompressed_header_bytes_including_overhead_ += - name.size() + value.size() + QpackEntry::kSizeOverhead; + name.size() + value.size() + kQpackEntrySizeOverhead; - if (uncompressed_header_bytes_including_overhead_ > max_header_list_size_) { + const size_t uncompressed_header_bytes = + GetQuicFlag(FLAGS_quic_header_size_limit_includes_overhead) + ? uncompressed_header_bytes_including_overhead_ + : uncompressed_header_bytes_without_overhead_; + if (uncompressed_header_bytes > max_header_list_size_) { header_list_size_limit_exceeded_ = true; quic_header_list_.Clear(); } else { diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.cc index c1ac28ca3d1..b9926167126 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.cc @@ -81,11 +81,12 @@ void QpackDecoder::OnInsertWithNameReference(bool is_static, return; } - entry = header_table_.InsertEntry(entry->name(), value); - if (!entry) { + if (!header_table_.EntryFitsDynamicTableCapacity(entry->name(), value)) { OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR_INSERTING_STATIC, "Error inserting entry with name reference."); + return; } + header_table_.InsertEntry(entry->name(), value); return; } @@ -104,20 +105,22 @@ void QpackDecoder::OnInsertWithNameReference(bool is_static, "Dynamic table entry not found."); return; } - entry = header_table_.InsertEntry(entry->name(), value); - if (!entry) { + if (!header_table_.EntryFitsDynamicTableCapacity(entry->name(), value)) { OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR_INSERTING_DYNAMIC, "Error inserting entry with name reference."); + return; } + header_table_.InsertEntry(entry->name(), value); } void QpackDecoder::OnInsertWithoutNameReference(absl::string_view name, absl::string_view value) { - const QpackEntry* entry = header_table_.InsertEntry(name, value); - if (!entry) { + if (!header_table_.EntryFitsDynamicTableCapacity(name, value)) { OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR_INSERTING_LITERAL, "Error inserting literal entry."); + return; } + header_table_.InsertEntry(name, value); } void QpackDecoder::OnDuplicate(uint64_t index) { @@ -136,13 +139,13 @@ void QpackDecoder::OnDuplicate(uint64_t index) { "Dynamic table entry not found."); return; } - entry = header_table_.InsertEntry(entry->name(), entry->value()); - if (!entry) { - // InsertEntry() can only fail if entry is larger then dynamic table - // capacity, but that is impossible since entry was retrieved from the - // dynamic table. + if (!header_table_.EntryFitsDynamicTableCapacity(entry->name(), + entry->value())) { + // This is impossible since entry was retrieved from the dynamic table. OnErrorDetected(QUIC_INTERNAL_ERROR, "Error inserting duplicate entry."); + return; } + header_table_.InsertEntry(entry->name(), entry->value()); } void QpackDecoder::OnSetDynamicTableCapacity(uint64_t capacity) { diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc index 8138266b493..5e61ee7490b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc @@ -44,7 +44,7 @@ QpackEncoder::QpackEncoder( QpackEncoder::~QpackEncoder() {} // static -QpackInstructionWithValues QpackEncoder::EncodeIndexedHeaderField( +QpackEncoder::Representation QpackEncoder::EncodeIndexedHeaderField( bool is_static, uint64_t index, QpackBlockingManager::IndexSet* referred_indices) { @@ -52,11 +52,11 @@ QpackInstructionWithValues QpackEncoder::EncodeIndexedHeaderField( if (!is_static) { referred_indices->insert(index); } - return QpackInstructionWithValues::IndexedHeaderField(is_static, index); + return Representation::IndexedHeaderField(is_static, index); } // static -QpackInstructionWithValues +QpackEncoder::Representation QpackEncoder::EncodeLiteralHeaderFieldWithNameReference( bool is_static, uint64_t index, @@ -66,18 +66,18 @@ QpackEncoder::EncodeLiteralHeaderFieldWithNameReference( if (!is_static) { referred_indices->insert(index); } - return QpackInstructionWithValues::LiteralHeaderFieldNameReference( - is_static, index, value); + return Representation::LiteralHeaderFieldNameReference(is_static, index, + value); } // static -QpackInstructionWithValues QpackEncoder::EncodeLiteralHeaderField( +QpackEncoder::Representation QpackEncoder::EncodeLiteralHeaderField( absl::string_view name, absl::string_view value) { - return QpackInstructionWithValues::LiteralHeaderField(name, value); + return Representation::LiteralHeaderField(name, value); } -QpackEncoder::Instructions QpackEncoder::FirstPassEncode( +QpackEncoder::Representations QpackEncoder::FirstPassEncode( QuicStreamId stream_id, const spdy::Http2HeaderBlock& header_list, QpackBlockingManager::IndexSet* referred_indices, @@ -87,8 +87,8 @@ QpackEncoder::Instructions QpackEncoder::FirstPassEncode( const QuicByteCount initial_encoder_stream_buffered_byte_count = encoder_stream_sender_.BufferedByteCount(); - Instructions instructions; - instructions.reserve(header_list.size()); + Representations representations; + representations.reserve(header_list.size()); // The index of the oldest entry that must not be evicted. uint64_t smallest_blocking_index = @@ -125,7 +125,7 @@ QpackEncoder::Instructions QpackEncoder::FirstPassEncode( case QpackHeaderTable::MatchType::kNameAndValue: if (is_static) { // Refer to entry directly. - instructions.push_back( + representations.push_back( EncodeIndexedHeaderField(is_static, index, referred_indices)); break; @@ -136,7 +136,7 @@ QpackEncoder::Instructions QpackEncoder::FirstPassEncode( if (!blocking_allowed && index >= known_received_count) { blocked_stream_limit_exhausted = true; } else { - instructions.push_back( + representations.push_back( EncodeIndexedHeaderField(is_static, index, referred_indices)); smallest_blocking_index = std::min(smallest_blocking_index, index); header_table_.set_dynamic_table_entry_referenced(); @@ -156,9 +156,9 @@ QpackEncoder::Instructions QpackEncoder::FirstPassEncode( encoder_stream_sender_.SendDuplicate( QpackAbsoluteIndexToEncoderStreamRelativeIndex( index, header_table_.inserted_entry_count())); - auto entry = header_table_.InsertEntry(name, value); - instructions.push_back(EncodeIndexedHeaderField( - is_static, entry->InsertionIndex(), referred_indices)); + uint64_t new_index = header_table_.InsertEntry(name, value); + representations.push_back(EncodeIndexedHeaderField( + is_static, new_index, referred_indices)); smallest_blocking_index = std::min(smallest_blocking_index, index); header_table_.set_dynamic_table_entry_referenced(); @@ -171,7 +171,7 @@ QpackEncoder::Instructions QpackEncoder::FirstPassEncode( // exists. // TODO(b/112770235): Use static entry name with literal value if // dynamic entry exists but cannot be used. - instructions.push_back(EncodeLiteralHeaderField(name, value)); + representations.push_back(EncodeLiteralHeaderField(name, value)); break; @@ -184,18 +184,17 @@ QpackEncoder::Instructions QpackEncoder::FirstPassEncode( // If allowed, insert entry into dynamic table and refer to it. encoder_stream_sender_.SendInsertWithNameReference(is_static, index, value); - auto entry = header_table_.InsertEntry(name, value); - instructions.push_back(EncodeIndexedHeaderField( - /* is_static = */ false, entry->InsertionIndex(), - referred_indices)); - smallest_blocking_index = std::min<uint64_t>( - smallest_blocking_index, entry->InsertionIndex()); + uint64_t new_index = header_table_.InsertEntry(name, value); + representations.push_back(EncodeIndexedHeaderField( + /* is_static = */ false, new_index, referred_indices)); + smallest_blocking_index = + std::min<uint64_t>(smallest_blocking_index, new_index); break; } // Emit literal field with name reference. - instructions.push_back(EncodeLiteralHeaderFieldWithNameReference( + representations.push_back(EncodeLiteralHeaderFieldWithNameReference( is_static, index, value, referred_indices)); break; @@ -214,9 +213,9 @@ QpackEncoder::Instructions QpackEncoder::FirstPassEncode( QpackAbsoluteIndexToEncoderStreamRelativeIndex( index, header_table_.inserted_entry_count()), value); - auto entry = header_table_.InsertEntry(name, value); - instructions.push_back(EncodeIndexedHeaderField( - is_static, entry->InsertionIndex(), referred_indices)); + uint64_t new_index = header_table_.InsertEntry(name, value); + representations.push_back( + EncodeIndexedHeaderField(is_static, new_index, referred_indices)); smallest_blocking_index = std::min(smallest_blocking_index, index); header_table_.set_dynamic_table_entry_referenced(); @@ -226,7 +225,7 @@ QpackEncoder::Instructions QpackEncoder::FirstPassEncode( if ((blocking_allowed || index < known_received_count) && index >= draining_index) { // If allowed, refer to entry name directly, with literal value. - instructions.push_back(EncodeLiteralHeaderFieldWithNameReference( + representations.push_back(EncodeLiteralHeaderFieldWithNameReference( is_static, index, value, referred_indices)); smallest_blocking_index = std::min(smallest_blocking_index, index); header_table_.set_dynamic_table_entry_referenced(); @@ -239,7 +238,7 @@ QpackEncoder::Instructions QpackEncoder::FirstPassEncode( // exists. // TODO(b/112770235): Use static entry name with literal value if // dynamic entry exists but cannot be used. - instructions.push_back(EncodeLiteralHeaderField(name, value)); + representations.push_back(EncodeLiteralHeaderField(name, value)); break; @@ -253,12 +252,11 @@ QpackEncoder::Instructions QpackEncoder::FirstPassEncode( dynamic_table_insertion_blocked = true; } else { encoder_stream_sender_.SendInsertWithoutNameReference(name, value); - auto entry = header_table_.InsertEntry(name, value); - instructions.push_back(EncodeIndexedHeaderField( - /* is_static = */ false, entry->InsertionIndex(), - referred_indices)); - smallest_blocking_index = std::min<uint64_t>(smallest_blocking_index, - entry->InsertionIndex()); + uint64_t new_index = header_table_.InsertEntry(name, value); + representations.push_back(EncodeIndexedHeaderField( + /* is_static = */ false, new_index, referred_indices)); + smallest_blocking_index = + std::min<uint64_t>(smallest_blocking_index, new_index); break; } @@ -267,7 +265,7 @@ QpackEncoder::Instructions QpackEncoder::FirstPassEncode( // TODO(b/112770235): Consider also adding to dynamic table to improve // compression ratio for subsequent header blocks with peers that do not // allow any blocked streams. - instructions.push_back(EncodeLiteralHeaderField(name, value)); + representations.push_back(EncodeLiteralHeaderField(name, value)); break; } @@ -322,34 +320,34 @@ QpackEncoder::Instructions QpackEncoder::FirstPassEncode( "prevent referencing unacknowledged dynamic table entries."); } - return instructions; + return representations; } std::string QpackEncoder::SecondPassEncode( - QpackEncoder::Instructions instructions, + QpackEncoder::Representations representations, uint64_t required_insert_count) const { QpackInstructionEncoder instruction_encoder; std::string encoded_headers; // Header block prefix. instruction_encoder.Encode( - QpackInstructionWithValues::Prefix(QpackEncodeRequiredInsertCount( + Representation::Prefix(QpackEncodeRequiredInsertCount( required_insert_count, header_table_.max_entries())), &encoded_headers); const uint64_t base = required_insert_count; - for (auto& instruction : instructions) { + for (auto& representation : representations) { // Dynamic table references must be transformed from absolute to relative // indices. - if ((instruction.instruction() == QpackIndexedHeaderFieldInstruction() || - instruction.instruction() == + if ((representation.instruction() == QpackIndexedHeaderFieldInstruction() || + representation.instruction() == QpackLiteralHeaderFieldNameReferenceInstruction()) && - !instruction.s_bit()) { - instruction.set_varint(QpackAbsoluteIndexToRequestStreamRelativeIndex( - instruction.varint(), base)); + !representation.s_bit()) { + representation.set_varint(QpackAbsoluteIndexToRequestStreamRelativeIndex( + representation.varint(), base)); } - instruction_encoder.Encode(instruction, &encoded_headers); + instruction_encoder.Encode(representation, &encoded_headers); } return encoded_headers; @@ -363,8 +361,8 @@ std::string QpackEncoder::EncodeHeaderList( // that it can be passed to QpackBlockingManager. QpackBlockingManager::IndexSet referred_indices; - // First pass: encode into |instructions|. - Instructions instructions = + // First pass: encode into |representations|. + Representations representations = FirstPassEncode(stream_id, header_list, &referred_indices, encoder_stream_sent_byte_count); @@ -377,7 +375,7 @@ std::string QpackEncoder::EncodeHeaderList( } // Second pass. - return SecondPassEncode(std::move(instructions), required_insert_count); + return SecondPassEncode(std::move(representations), required_insert_count); } bool QpackEncoder::SetMaximumDynamicTableCapacity( diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h index 3d95f6e3249..9d024417c93 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h @@ -105,27 +105,27 @@ class QUIC_EXPORT_PRIVATE QpackEncoder private: friend class test::QpackEncoderPeer; - using Instructions = std::vector<QpackInstructionWithValues>; + using Representation = QpackInstructionWithValues; + using Representations = std::vector<Representation>; - // Generate indexed header field instruction + // Generate indexed header field representation // and optionally update |*referred_indices|. - static QpackInstructionWithValues EncodeIndexedHeaderField( + static Representation EncodeIndexedHeaderField( bool is_static, uint64_t index, QpackBlockingManager::IndexSet* referred_indices); - // Generate literal header field with name reference instruction + // Generate literal header field with name reference representation // and optionally update |*referred_indices|. - static QpackInstructionWithValues EncodeLiteralHeaderFieldWithNameReference( + static Representation EncodeLiteralHeaderFieldWithNameReference( bool is_static, uint64_t index, absl::string_view value, QpackBlockingManager::IndexSet* referred_indices); - // Generate literal header field instruction. - static QpackInstructionWithValues EncodeLiteralHeaderField( - absl::string_view name, - absl::string_view value); + // Generate literal header field representation. + static Representation EncodeLiteralHeaderField(absl::string_view name, + absl::string_view value); // Performs first pass of two-pass encoding: represent each header field in // |*header_list| as a reference to an existing entry, the name of an existing @@ -136,17 +136,18 @@ class QUIC_EXPORT_PRIVATE QpackEncoder // sets |*encoder_stream_sent_byte_count| to the number of bytes sent on the // encoder stream to insert dynamic table entries. Returns list of header // field representations, with all dynamic table entries referred to with - // absolute indices. Returned Instructions object may have + // absolute indices. Returned representation objects may have // absl::string_views pointing to strings owned by |*header_list|. - Instructions FirstPassEncode(QuicStreamId stream_id, - const spdy::Http2HeaderBlock& header_list, - QpackBlockingManager::IndexSet* referred_indices, - QuicByteCount* encoder_stream_sent_byte_count); + Representations FirstPassEncode( + QuicStreamId stream_id, + const spdy::Http2HeaderBlock& header_list, + QpackBlockingManager::IndexSet* referred_indices, + QuicByteCount* encoder_stream_sent_byte_count); // Performs second pass of two-pass encoding: serializes representations // generated in first pass, transforming absolute indices of dynamic table // entries to relative indices. - std::string SecondPassEncode(Instructions instructions, + std::string SecondPassEncode(Representations representations, uint64_t required_insert_count) const; DecoderStreamErrorDelegate* const decoder_stream_error_delegate_; diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc index 6171d8455a4..da3636db4e4 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc @@ -55,22 +55,20 @@ QpackHeaderTable::MatchType QpackHeaderTable::FindHeaderField( absl::string_view value, bool* is_static, uint64_t* index) const { - QpackEntry query(name, value); + QpackLookupEntry query{name, value}; // Look for exact match in static table. - auto index_it = static_index_.find(&query); + auto index_it = static_index_.find(query); if (index_it != static_index_.end()) { - QUICHE_DCHECK((*index_it)->IsStatic()); - *index = (*index_it)->InsertionIndex(); + *index = index_it->second; *is_static = true; return MatchType::kNameAndValue; } // Look for exact match in dynamic table. - index_it = dynamic_index_.find(&query); + index_it = dynamic_index_.find(query); if (index_it != dynamic_index_.end()) { - QUICHE_DCHECK(!(*index_it)->IsStatic()); - *index = (*index_it)->InsertionIndex(); + *index = index_it->second; *is_static = false; return MatchType::kNameAndValue; } @@ -78,8 +76,7 @@ QpackHeaderTable::MatchType QpackHeaderTable::FindHeaderField( // Look for name match in static table. auto name_index_it = static_name_index_.find(name); if (name_index_it != static_name_index_.end()) { - QUICHE_DCHECK(name_index_it->second->IsStatic()); - *index = name_index_it->second->InsertionIndex(); + *index = name_index_it->second; *is_static = true; return MatchType::kName; } @@ -87,8 +84,7 @@ QpackHeaderTable::MatchType QpackHeaderTable::FindHeaderField( // Look for name match in dynamic table. name_index_it = dynamic_name_index_.find(name); if (name_index_it != dynamic_name_index_.end()) { - QUICHE_DCHECK(!name_index_it->second->IsStatic()); - *index = name_index_it->second->InsertionIndex(); + *index = name_index_it->second; *is_static = false; return MatchType::kName; } @@ -96,43 +92,54 @@ QpackHeaderTable::MatchType QpackHeaderTable::FindHeaderField( return MatchType::kNoMatch; } -const QpackEntry* QpackHeaderTable::InsertEntry(absl::string_view name, - absl::string_view value) { - const uint64_t entry_size = QpackEntry::Size(name, value); - if (entry_size > dynamic_table_capacity_) { - return nullptr; - } +bool QpackHeaderTable::EntryFitsDynamicTableCapacity( + absl::string_view name, + absl::string_view value) const { + return QpackEntry::Size(name, value) <= dynamic_table_capacity_; +} + +uint64_t QpackHeaderTable::InsertEntry(absl::string_view name, + absl::string_view value) { + QUICHE_DCHECK(EntryFitsDynamicTableCapacity(name, value)); const uint64_t index = dropped_entry_count_ + dynamic_entries_.size(); - dynamic_entries_.push_back({name, value, /* is_static = */ false, index}); - QpackEntry* const new_entry = &dynamic_entries_.back(); - // Evict entries after inserting the new entry instead of before - // in order to avoid invalidating |name| and |value|. + // Copy name and value before modifying the container, because evicting + // entries or even inserting a new one might invalidate |name| or |value| if + // they point to an entry. + QpackEntry new_entry((std::string(name)), (std::string(value))); + const size_t entry_size = new_entry.Size(); + + EvictDownToCapacity(dynamic_table_capacity_ - entry_size); + dynamic_table_size_ += entry_size; - EvictDownToCurrentCapacity(); + dynamic_entries_.push_back(std::move(new_entry)); + + // Make name and value point to the new entry. + name = dynamic_entries_.back().name(); + value = dynamic_entries_.back().value(); - auto index_result = dynamic_index_.insert(new_entry); + auto index_result = dynamic_index_.insert( + std::make_pair(QpackLookupEntry{name, value}, index)); if (!index_result.second) { // An entry with the same name and value already exists. It needs to be // replaced, because |dynamic_index_| tracks the most recent entry for a // given name and value. - QUICHE_DCHECK_GT(new_entry->InsertionIndex(), - (*index_result.first)->InsertionIndex()); + QUICHE_DCHECK_GT(index, index_result.first->second); dynamic_index_.erase(index_result.first); - auto result = dynamic_index_.insert(new_entry); + auto result = dynamic_index_.insert( + std::make_pair(QpackLookupEntry{name, value}, index)); QUICHE_CHECK(result.second); } - auto name_result = dynamic_name_index_.insert({new_entry->name(), new_entry}); + auto name_result = dynamic_name_index_.insert({name, index}); if (!name_result.second) { // An entry with the same name already exists. It needs to be replaced, // because |dynamic_name_index_| tracks the most recent entry for a given // name. - QUICHE_DCHECK_GT(new_entry->InsertionIndex(), - name_result.first->second->InsertionIndex()); + QUICHE_DCHECK_GT(index, name_result.first->second); dynamic_name_index_.erase(name_result.first); - auto result = dynamic_name_index_.insert({new_entry->name(), new_entry}); + auto result = dynamic_name_index_.insert({name, index}); QUICHE_CHECK(result.second); } @@ -147,7 +154,7 @@ const QpackEntry* QpackHeaderTable::InsertEntry(absl::string_view name, observer->OnInsertCountReachedThreshold(); } - return new_entry; + return index; } uint64_t QpackHeaderTable::MaxInsertSizeWithoutEvictingGivenEntry( @@ -162,10 +169,12 @@ uint64_t QpackHeaderTable::MaxInsertSizeWithoutEvictingGivenEntry( // Initialize to current available capacity. uint64_t max_insert_size = dynamic_table_capacity_ - dynamic_table_size_; + uint64_t entry_index = dropped_entry_count_; for (const auto& entry : dynamic_entries_) { - if (entry.InsertionIndex() >= index) { + if (entry_index >= index) { break; } + ++entry_index; max_insert_size += entry.Size(); } @@ -178,7 +187,7 @@ bool QpackHeaderTable::SetDynamicTableCapacity(uint64_t capacity) { } dynamic_table_capacity_ = capacity; - EvictDownToCurrentCapacity(); + EvictDownToCapacity(capacity); QUICHE_DCHECK_LE(dynamic_table_size_, dynamic_table_capacity_); @@ -231,41 +240,42 @@ uint64_t QpackHeaderTable::draining_index(float draining_fraction) const { } auto it = dynamic_entries_.begin(); + uint64_t entry_index = dropped_entry_count_; while (space_above_draining_index < required_space) { space_above_draining_index += it->Size(); ++it; + ++entry_index; if (it == dynamic_entries_.end()) { return inserted_entry_count(); } } - return it->InsertionIndex(); + return entry_index; } -void QpackHeaderTable::EvictDownToCurrentCapacity() { - while (dynamic_table_size_ > dynamic_table_capacity_) { +void QpackHeaderTable::EvictDownToCapacity(uint64_t capacity) { + while (dynamic_table_size_ > capacity) { QUICHE_DCHECK(!dynamic_entries_.empty()); QpackEntry* const entry = &dynamic_entries_.front(); - const uint64_t entry_size = entry->Size(); + const uint64_t entry_size = entry->Size(); QUICHE_DCHECK_GE(dynamic_table_size_, entry_size); dynamic_table_size_ -= entry_size; - auto index_it = dynamic_index_.find(entry); + const uint64_t index = dropped_entry_count_; + + auto index_it = dynamic_index_.find({entry->name(), entry->value()}); // Remove |dynamic_index_| entry only if it points to the same - // QpackEntry in |dynamic_entries_|. Note that |dynamic_index_| has a - // comparison function that only considers name and value, not actual - // QpackEntry* address, so find() can return a different entry if name and - // value match. - if (index_it != dynamic_index_.end() && *index_it == entry) { + // QpackEntry in |dynamic_entries_|. + if (index_it != dynamic_index_.end() && index_it->second == index) { dynamic_index_.erase(index_it); } auto name_it = dynamic_name_index_.find(entry->name()); // Remove |dynamic_name_index_| entry only if it points to the same // QpackEntry in |dynamic_entries_|. - if (name_it != dynamic_name_index_.end() && name_it->second == entry) { + if (name_it != dynamic_name_index_.end() && name_it->second == index) { dynamic_name_index_.erase(name_it); } diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h index 4f0a1b5e032..a5d6c5d74b8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h @@ -24,16 +24,17 @@ class QpackHeaderTablePeer; } // namespace test using QpackEntry = spdy::HpackEntry; +using QpackLookupEntry = spdy::HpackLookupEntry; +constexpr size_t kQpackEntrySizeOverhead = spdy::kHpackEntrySizeOverhead; // This class manages the QPACK static and dynamic tables. For dynamic entries, // it only has a concept of absolute indices. The caller needs to perform the // necessary transformations to and from relative indices and post-base indices. class QUIC_EXPORT_PRIVATE QpackHeaderTable { public: - using EntryTable = spdy::HpackHeaderTable::EntryTable; - using EntryHasher = spdy::HpackHeaderTable::EntryHasher; - using EntriesEq = spdy::HpackHeaderTable::EntriesEq; - using UnorderedEntrySet = spdy::HpackHeaderTable::UnorderedEntrySet; + using StaticEntryTable = spdy::HpackHeaderTable::StaticEntryTable; + using DynamicEntryTable = spdy::HpackHeaderTable::DynamicEntryTable; + using NameValueToEntryMap = spdy::HpackHeaderTable::NameValueToEntryMap; using NameToEntryMap = spdy::HpackHeaderTable::NameToEntryMap; // Result of header table lookup. @@ -75,11 +76,20 @@ class QUIC_EXPORT_PRIVATE QpackHeaderTable { bool* is_static, uint64_t* index) const; - // Insert (name, value) into the dynamic table. May evict entries. Returns a - // pointer to the inserted owned entry on success. Returns nullptr if entry - // is larger than the capacity of the dynamic table. - const QpackEntry* InsertEntry(absl::string_view name, - absl::string_view value); + // Returns whether an entry with |name| and |value| has a size (including + // overhead) that is smaller than or equal to the capacity of the dynamic + // table. + bool EntryFitsDynamicTableCapacity(absl::string_view name, + absl::string_view value) const; + + // Inserts (name, value) into the dynamic table. Entry must not be larger + // than the capacity of the dynamic table. May evict entries. |name| and + // |value| are copied first, therefore it is safe for them to point to an + // entry in the dynamic table, even if it is about to be evicted, or even if + // the underlying container might move entries around when resizing for + // insertion. + // Returns the absolute index of the inserted dynamic table entry. + uint64_t InsertEntry(absl::string_view name, absl::string_view value); // Returns the size of the largest entry that could be inserted into the // dynamic table without evicting entry |index|. |index| might be larger than @@ -150,8 +160,8 @@ class QUIC_EXPORT_PRIVATE QpackHeaderTable { friend class test::QpackHeaderTablePeer; // Evict entries from the dynamic table until table size is less than or equal - // to current value of |dynamic_table_capacity_|. - void EvictDownToCurrentCapacity(); + // to |capacity|. + void EvictDownToCapacity(uint64_t capacity); // Static Table @@ -159,10 +169,10 @@ class QUIC_EXPORT_PRIVATE QpackHeaderTable { // QpackStaticTable singleton. // Tracks QpackEntries by index. - const EntryTable& static_entries_; + const StaticEntryTable& static_entries_; // Tracks the unique static entry for a given header name and value. - const UnorderedEntrySet& static_index_; + const NameValueToEntryMap& static_index_; // Tracks the first static entry for a given header name. const NameToEntryMap& static_name_index_; @@ -171,13 +181,13 @@ class QUIC_EXPORT_PRIVATE QpackHeaderTable { // Queue of dynamic table entries, for lookup by index. // |dynamic_entries_| owns the entries in the dynamic table. - EntryTable dynamic_entries_; + DynamicEntryTable dynamic_entries_; // An unordered set of QpackEntry pointers with a comparison operator that // only cares about name and value. This allows fast lookup of the most // recently inserted dynamic entry for a given header name and value pair. // Entries point to entries owned by |dynamic_entries_|. - UnorderedEntrySet dynamic_index_; + NameValueToEntryMap dynamic_index_; // An unordered map of QpackEntry pointers keyed off header name. This allows // fast lookup of the most recently inserted dynamic entry for a given header diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc index 801ab8f8062..0af250efd32 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc @@ -81,13 +81,13 @@ class QpackHeaderTableTest : public QuicTest { << name << ": " << value; } - void InsertEntry(absl::string_view name, absl::string_view value) { - EXPECT_TRUE(table_.InsertEntry(name, value)); + bool EntryFitsDynamicTableCapacity(absl::string_view name, + absl::string_view value) const { + return table_.EntryFitsDynamicTableCapacity(name, value); } - void ExpectToFailInsertingEntry(absl::string_view name, - absl::string_view value) { - EXPECT_FALSE(table_.InsertEntry(name, value)); + void InsertEntry(absl::string_view name, absl::string_view value) { + table_.InsertEntry(name, value); } bool SetDynamicTableCapacity(uint64_t capacity) { @@ -293,9 +293,6 @@ TEST_F(QpackHeaderTableTest, EvictByInsertion) { ExpectNoMatch("foo", "bar"); ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue, /* expected_is_static = */ false, 1u); - - // Inserting an entry that does not fit results in error. - ExpectToFailInsertingEntry("foobar", "foobar"); } TEST_F(QpackHeaderTableTest, EvictByUpdateTableSize) { @@ -389,7 +386,7 @@ TEST_F(QpackHeaderTableTest, MaxInsertSizeWithoutEvictingGivenEntry) { table.MaxInsertSizeWithoutEvictingGivenEntry(0)); const uint64_t entry_size1 = QpackEntry::Size("foo", "bar"); - EXPECT_TRUE(table.InsertEntry("foo", "bar")); + table.InsertEntry("foo", "bar"); EXPECT_EQ(dynamic_table_capacity - entry_size1, table.MaxInsertSizeWithoutEvictingGivenEntry(0)); // Table can take an entry up to its capacity if all entries are allowed to be @@ -398,7 +395,7 @@ TEST_F(QpackHeaderTableTest, MaxInsertSizeWithoutEvictingGivenEntry) { table.MaxInsertSizeWithoutEvictingGivenEntry(1)); const uint64_t entry_size2 = QpackEntry::Size("baz", "foobar"); - EXPECT_TRUE(table.InsertEntry("baz", "foobar")); + table.InsertEntry("baz", "foobar"); // Table can take an entry up to its capacity if all entries are allowed to be // evicted. EXPECT_EQ(dynamic_table_capacity, @@ -412,7 +409,7 @@ TEST_F(QpackHeaderTableTest, MaxInsertSizeWithoutEvictingGivenEntry) { // Third entry evicts first one. const uint64_t entry_size3 = QpackEntry::Size("last", "entry"); - EXPECT_TRUE(table.InsertEntry("last", "entry")); + table.InsertEntry("last", "entry"); EXPECT_EQ(1u, table.dropped_entry_count()); // Table can take an entry up to its capacity if all entries are allowed to be // evicted. @@ -497,14 +494,14 @@ TEST_F(QpackHeaderTableTest, DrainingIndex) { EXPECT_EQ(0u, table.draining_index(1.0)); // Table with one entry. - EXPECT_TRUE(table.InsertEntry("foo", "bar")); + table.InsertEntry("foo", "bar"); // Any entry can be referenced if none of the table is draining. EXPECT_EQ(0u, table.draining_index(0.0)); // No entry can be referenced if all of the table is draining. EXPECT_EQ(1u, table.draining_index(1.0)); // Table with two entries is at half capacity. - EXPECT_TRUE(table.InsertEntry("foo", "bar")); + table.InsertEntry("foo", "bar"); // Any entry can be referenced if at most half of the table is draining, // because current entries only take up half of total capacity. EXPECT_EQ(0u, table.draining_index(0.0)); @@ -513,8 +510,8 @@ TEST_F(QpackHeaderTableTest, DrainingIndex) { EXPECT_EQ(2u, table.draining_index(1.0)); // Table with four entries is full. - EXPECT_TRUE(table.InsertEntry("foo", "bar")); - EXPECT_TRUE(table.InsertEntry("foo", "bar")); + table.InsertEntry("foo", "bar"); + table.InsertEntry("foo", "bar"); // Any entry can be referenced if none of the table is draining. EXPECT_EQ(0u, table.draining_index(0.0)); // In a full table with identically sized entries, |draining_fraction| of all @@ -533,6 +530,14 @@ TEST_F(QpackHeaderTableTest, Cancel) { table.reset(); } +TEST_F(QpackHeaderTableTest, EntryFitsDynamicTableCapacity) { + EXPECT_TRUE(SetDynamicTableCapacity(39)); + + EXPECT_TRUE(EntryFitsDynamicTableCapacity("foo", "bar")); + EXPECT_TRUE(EntryFitsDynamicTableCapacity("foo", "bar2")); + EXPECT_FALSE(EntryFitsDynamicTableCapacity("foo", "bar12")); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc index bf786a44a55..495a58ffdfd 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc @@ -127,7 +127,7 @@ bool QpackInstructionDecoder::DoStartField() { state_ = State::kVarintStart; return true; default: - QUIC_BUG << "Invalid field type."; + QUIC_BUG(quic_bug_10767_1) << "Invalid field type."; return false; } } @@ -157,7 +157,7 @@ bool QpackInstructionDecoder::DoReadBit(absl::string_view data) { return true; } default: - QUIC_BUG << "Invalid field type."; + QUIC_BUG(quic_bug_10767_2) << "Invalid field type."; return false; } } @@ -186,7 +186,7 @@ bool QpackInstructionDecoder::DoVarintStart(absl::string_view data, OnError(ErrorCode::INTEGER_TOO_LARGE, "Encoded integer too large."); return false; default: - QUIC_BUG << "Unknown decode status " << status; + QUIC_BUG(quic_bug_10767_3) << "Unknown decode status " << status; return false; } } @@ -215,7 +215,7 @@ bool QpackInstructionDecoder::DoVarintResume(absl::string_view data, OnError(ErrorCode::INTEGER_TOO_LARGE, "Encoded integer too large."); return false; default: - QUIC_BUG << "Unknown decode status " << status; + QUIC_BUG(quic_bug_10767_4) << "Unknown decode status " << status; return false; } } diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.h index c9d2a775e57..6c04b942e5d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.h +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_receive_stream.h @@ -33,8 +33,6 @@ class QUIC_EXPORT_PRIVATE QpackReceiveStream : public QuicStream { // Implementation of QuicStream. void OnDataAvailable() override; - void SetUnblocked() { sequencer()->SetUnblocked(); } - private: QpackStreamReceiver* receiver_; }; 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 636f4b477b3..0d5d8af79ef 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 @@ -17,7 +17,8 @@ QpackSendStream::QpackSendStream(QuicStreamId id, stream_type_sent_(false) {} void QpackSendStream::OnStreamReset(const QuicRstStreamFrame& /*frame*/) { - QUIC_BUG << "OnStreamReset() called for write unidirectional stream."; + QUIC_BUG(quic_bug_10805_1) + << "OnStreamReset() called for write unidirectional stream."; } bool QpackSendStream::OnStopSending(QuicRstStreamErrorCode /* code */) { diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table_test.cc index 734d25320df..53ab0f49a04 100644 --- a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table_test.cc @@ -25,16 +25,17 @@ TEST(QpackStaticTableTest, Initialize) { QpackStaticTableVector().size()); EXPECT_TRUE(table.IsInitialized()); - auto static_entries = table.GetStaticEntries(); + const auto& static_entries = table.GetStaticEntries(); EXPECT_EQ(QpackStaticTableVector().size(), static_entries.size()); - auto static_index = table.GetStaticIndex(); + const auto& static_index = table.GetStaticIndex(); EXPECT_EQ(QpackStaticTableVector().size(), static_index.size()); - auto static_name_index = table.GetStaticNameIndex(); + const auto& static_name_index = table.GetStaticNameIndex(); + // Count distinct names in static table. std::set<absl::string_view> names; - for (auto entry : static_index) { - names.insert(entry->name()); + for (const auto& entry : static_entries) { + names.insert(entry.name()); } EXPECT_EQ(names.size(), static_name_index.size()); } 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 b9e3d10c69b..e4d4c971338 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 @@ -87,14 +87,16 @@ EnqueuePacketResult QuicBufferedPacketStore::EnqueuePacket( QuicSocketAddress peer_address, bool is_chlo, const std::vector<std::string>& alpns, + const absl::string_view sni, const ParsedQuicVersion& version) { - QUIC_BUG_IF(!GetQuicFlag(FLAGS_quic_allow_chlo_buffering)) + QUIC_BUG_IF(quic_bug_12410_1, !GetQuicFlag(FLAGS_quic_allow_chlo_buffering)) << "Shouldn't buffer packets if disabled via flag."; - QUIC_BUG_IF(is_chlo && QuicContainsKey(connections_with_chlo_, connection_id)) + QUIC_BUG_IF(quic_bug_12410_2, + is_chlo && QuicContainsKey(connections_with_chlo_, connection_id)) << "Shouldn't buffer duplicated CHLO on connection " << connection_id; - QUIC_BUG_IF(!is_chlo && !alpns.empty()) + QUIC_BUG_IF(quic_bug_12410_3, !is_chlo && !alpns.empty()) << "Shouldn't have an ALPN defined for a non-CHLO packet."; - QUIC_BUG_IF(is_chlo && !version.IsKnown()) + QUIC_BUG_IF(quic_bug_12410_4, is_chlo && !version.IsKnown()) << "Should have version for CHLO packet."; const bool is_first_packet = @@ -141,6 +143,7 @@ EnqueuePacketResult QuicBufferedPacketStore::EnqueuePacket( // first later. queue.buffered_packets.push_front(std::move(new_entry)); queue.alpns = alpns; + queue.sni = std::string(sni); connections_with_chlo_[connection_id] = false; // Dummy value. // Set the version of buffered packets of this connection on CHLO. queue.version = version; @@ -153,7 +156,8 @@ EnqueuePacketResult QuicBufferedPacketStore::EnqueuePacket( queue.tls_chlo_extractor.IngestPacket(version, packet); // Since this is the first packet and it's not a CHLO, the // TlsChloExtractor should not have the entire CHLO. - QUIC_BUG_IF(queue.tls_chlo_extractor.HasParsedFullChlo()) + QUIC_BUG_IF(quic_bug_12410_5, + queue.tls_chlo_extractor.HasParsedFullChlo()) << "First packet in list should not contain full CHLO"; } // TODO(b/154857081) Reorder CHLO packets ahead of other ones. @@ -257,13 +261,15 @@ bool QuicBufferedPacketStore::IngestPacketForTlsChloExtraction( const QuicConnectionId& connection_id, const ParsedQuicVersion& version, const QuicReceivedPacket& packet, - std::vector<std::string>* out_alpns) { + std::vector<std::string>* out_alpns, + std::string* out_sni) { QUICHE_DCHECK_NE(out_alpns, nullptr); + QUICHE_DCHECK_NE(out_sni, nullptr); QUICHE_DCHECK_EQ(version.handshake_protocol, PROTOCOL_TLS1_3); auto it = undecryptable_packets_.find(connection_id); if (it == undecryptable_packets_.end()) { - QUIC_BUG << "Cannot ingest packet for unknown connection ID " - << connection_id; + QUIC_BUG(quic_bug_10838_1) + << "Cannot ingest packet for unknown connection ID " << connection_id; return false; } it->second.tls_chlo_extractor.IngestPacket(version, packet); @@ -271,6 +277,7 @@ bool QuicBufferedPacketStore::IngestPacketForTlsChloExtraction( return false; } *out_alpns = it->second.tls_chlo_extractor.alpns(); + *out_sni = it->second.tls_chlo_extractor.server_name(); return true; } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h b/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h index c8f6375a30e..29be2c86a42 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h @@ -69,6 +69,7 @@ class QUIC_NO_EXPORT QuicBufferedPacketStore { QuicTime creation_time; // The ALPNs from the CHLO, if found. std::vector<std::string> alpns; + std::string sni; // Indicating whether this is an IETF QUIC connection. bool ietf_quic; // If buffered_packets contains the CHLO, it is the version of the CHLO. @@ -110,6 +111,7 @@ class QUIC_NO_EXPORT QuicBufferedPacketStore { QuicSocketAddress peer_address, bool is_chlo, const std::vector<std::string>& alpns, + const absl::string_view sni, const ParsedQuicVersion& version); // Returns true if there are any packets buffered for |connection_id|. @@ -119,11 +121,12 @@ class QUIC_NO_EXPORT QuicBufferedPacketStore { // only be called when HasBufferedPackets(connection_id) is true. // Returns whether we've now parsed a full multi-packet TLS CHLO. // When this returns true, |out_alpns| is populated with the list of ALPNs - // extracted from the CHLO. + // extracted from the CHLO. |out_sni| is populated with the SNI tag in CHLO. bool IngestPacketForTlsChloExtraction(const QuicConnectionId& connection_id, const ParsedQuicVersion& version, const QuicReceivedPacket& packet, - std::vector<std::string>* out_alpns); + std::vector<std::string>* out_alpns, + std::string* out_sni); // Returns the list of buffered packets for |connection_id| and removes them // from the store. Returns an empty list if no early arrived packets for this diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store_test.cc index 1d9cce7647f..c5d11f22811 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store_test.cc @@ -70,7 +70,7 @@ class QuicBufferedPacketStoreTest : public QuicTest { TEST_F(QuicBufferedPacketStoreTest, SimpleEnqueueAndDeliverPacket) { QuicConnectionId connection_id = TestConnectionId(1); store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_); + peer_address_, false, {}, "", invalid_version_); EXPECT_TRUE(store_.HasBufferedPackets(connection_id)); auto packets = store_.DeliverPackets(connection_id); const std::list<BufferedPacket>& queue = packets.buffered_packets; @@ -93,9 +93,9 @@ TEST_F(QuicBufferedPacketStoreTest, DifferentPacketAddressOnOneConnection) { QuicSocketAddress addr_with_new_port(QuicIpAddress::Any4(), 256); QuicConnectionId connection_id = TestConnectionId(1); store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_); + peer_address_, false, {}, "", invalid_version_); store_.EnqueuePacket(connection_id, false, packet_, self_address_, - addr_with_new_port, false, {}, invalid_version_); + addr_with_new_port, false, {}, "", invalid_version_); std::list<BufferedPacket> queue = store_.DeliverPackets(connection_id).buffered_packets; ASSERT_EQ(2u, queue.size()); @@ -110,9 +110,9 @@ TEST_F(QuicBufferedPacketStoreTest, for (uint64_t conn_id = 1; conn_id <= num_connections; ++conn_id) { QuicConnectionId connection_id = TestConnectionId(conn_id); store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_); + peer_address_, false, {}, "", invalid_version_); store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_); + peer_address_, false, {}, "", invalid_version_); } // Deliver packets in reversed order. @@ -134,12 +134,12 @@ TEST_F(QuicBufferedPacketStoreTest, // keep. EXPECT_EQ(QuicBufferedPacketStore::SUCCESS, store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, true, {}, valid_version_)); + peer_address_, true, {}, "", valid_version_)); for (size_t i = 1; i <= num_packets; ++i) { // Only first |kDefaultMaxUndecryptablePackets packets| will be buffered. EnqueuePacketResult result = store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_); + peer_address_, false, {}, "", invalid_version_); if (i <= kDefaultMaxUndecryptablePackets) { EXPECT_EQ(EnqueuePacketResult::SUCCESS, result); } else { @@ -161,7 +161,7 @@ TEST_F(QuicBufferedPacketStoreTest, ReachNonChloConnectionUpperLimit) { QuicConnectionId connection_id = TestConnectionId(conn_id); EnqueuePacketResult result = store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_); + peer_address_, false, {}, "", invalid_version_); if (conn_id <= kMaxConnectionsWithoutCHLO) { EXPECT_EQ(EnqueuePacketResult::SUCCESS, result); } else { @@ -190,7 +190,7 @@ TEST_F(QuicBufferedPacketStoreTest, for (uint64_t conn_id = 1; conn_id <= num_chlos; ++conn_id) { EXPECT_EQ(EnqueuePacketResult::SUCCESS, store_.EnqueuePacket(TestConnectionId(conn_id), false, packet_, - self_address_, peer_address_, true, {}, + self_address_, peer_address_, true, {}, "", valid_version_)); } @@ -201,7 +201,7 @@ TEST_F(QuicBufferedPacketStoreTest, QuicConnectionId connection_id = TestConnectionId(conn_id); EnqueuePacketResult result = store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, true, {}, valid_version_); + peer_address_, true, {}, "", valid_version_); if (conn_id <= kDefaultMaxConnectionsInStore) { EXPECT_EQ(EnqueuePacketResult::SUCCESS, result); } else { @@ -214,9 +214,10 @@ TEST_F(QuicBufferedPacketStoreTest, EnqueueChloOnTooManyDifferentConnections) { // Buffer data packets on different connections upto limit. for (uint64_t conn_id = 1; conn_id <= kMaxConnectionsWithoutCHLO; ++conn_id) { QuicConnectionId connection_id = TestConnectionId(conn_id); - EXPECT_EQ(EnqueuePacketResult::SUCCESS, - store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_)); + EXPECT_EQ( + EnqueuePacketResult::SUCCESS, + store_.EnqueuePacket(connection_id, false, packet_, self_address_, + peer_address_, false, {}, "", invalid_version_)); } // Buffer CHLOs on other connections till store is full. @@ -225,7 +226,7 @@ TEST_F(QuicBufferedPacketStoreTest, EnqueueChloOnTooManyDifferentConnections) { QuicConnectionId connection_id = TestConnectionId(i); EnqueuePacketResult rs = store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, true, {}, valid_version_); + peer_address_, true, {}, "", valid_version_); if (i <= kDefaultMaxConnectionsInStore) { EXPECT_EQ(EnqueuePacketResult::SUCCESS, rs); EXPECT_TRUE(store_.HasChloForConnection(connection_id)); @@ -242,7 +243,7 @@ TEST_F(QuicBufferedPacketStoreTest, EnqueueChloOnTooManyDifferentConnections) { EXPECT_EQ(EnqueuePacketResult::SUCCESS, store_.EnqueuePacket( /*connection_id=*/TestConnectionId(1), false, packet_, - self_address_, peer_address_, true, {}, valid_version_)); + self_address_, peer_address_, true, {}, "", valid_version_)); EXPECT_TRUE(store_.HasChloForConnection( /*connection_id=*/TestConnectionId(1))); @@ -270,14 +271,15 @@ TEST_F(QuicBufferedPacketStoreTest, EnqueueChloOnTooManyDifferentConnections) { TEST_F(QuicBufferedPacketStoreTest, PacketQueueExpiredBeforeDelivery) { QuicConnectionId connection_id = TestConnectionId(1); store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_); + peer_address_, false, {}, "", invalid_version_); EXPECT_EQ(EnqueuePacketResult::SUCCESS, store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, true, {}, valid_version_)); + peer_address_, true, {}, "", valid_version_)); QuicConnectionId connection_id2 = TestConnectionId(2); - EXPECT_EQ(EnqueuePacketResult::SUCCESS, - store_.EnqueuePacket(connection_id2, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_)); + EXPECT_EQ( + EnqueuePacketResult::SUCCESS, + store_.EnqueuePacket(connection_id2, false, packet_, self_address_, + peer_address_, false, {}, "", invalid_version_)); // CHLO on connection 3 arrives 1ms later. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); @@ -286,7 +288,7 @@ TEST_F(QuicBufferedPacketStoreTest, PacketQueueExpiredBeforeDelivery) { // connections. QuicSocketAddress another_client_address(QuicIpAddress::Any4(), 255); store_.EnqueuePacket(connection_id3, false, packet_, self_address_, - another_client_address, true, {}, valid_version_); + another_client_address, true, {}, "", valid_version_); // Advance clock to the time when connection 1 and 2 expires. clock_.AdvanceTime( @@ -318,9 +320,9 @@ TEST_F(QuicBufferedPacketStoreTest, PacketQueueExpiredBeforeDelivery) { // for them to expire. QuicConnectionId connection_id4 = TestConnectionId(4); store_.EnqueuePacket(connection_id4, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_); + peer_address_, false, {}, "", invalid_version_); store_.EnqueuePacket(connection_id4, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_); + peer_address_, false, {}, "", invalid_version_); clock_.AdvanceTime( QuicBufferedPacketStorePeer::expiration_alarm(&store_)->deadline() - clock_.ApproximateNow()); @@ -335,9 +337,9 @@ TEST_F(QuicBufferedPacketStoreTest, SimpleDiscardPackets) { // Enqueue some packets store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_); + peer_address_, false, {}, "", invalid_version_); store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_); + peer_address_, false, {}, "", invalid_version_); EXPECT_TRUE(store_.HasBufferedPackets(connection_id)); EXPECT_FALSE(store_.HasChlosBuffered()); @@ -361,11 +363,11 @@ TEST_F(QuicBufferedPacketStoreTest, DiscardWithCHLOs) { // Enqueue some packets, which include a CHLO store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_); + peer_address_, false, {}, "", invalid_version_); store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, true, {}, valid_version_); + peer_address_, true, {}, "", valid_version_); store_.EnqueuePacket(connection_id, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_); + peer_address_, false, {}, "", invalid_version_); EXPECT_TRUE(store_.HasBufferedPackets(connection_id)); EXPECT_TRUE(store_.HasChlosBuffered()); @@ -390,11 +392,12 @@ TEST_F(QuicBufferedPacketStoreTest, MultipleDiscardPackets) { // Enqueue some packets for two connection IDs store_.EnqueuePacket(connection_id_1, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_); + peer_address_, false, {}, "", invalid_version_); store_.EnqueuePacket(connection_id_1, false, packet_, self_address_, - peer_address_, false, {}, invalid_version_); + peer_address_, false, {}, "", invalid_version_); store_.EnqueuePacket(connection_id_2, false, packet_, self_address_, - peer_address_, true, {"h3"}, valid_version_); + peer_address_, true, {"h3"}, TestHostname(), + valid_version_); EXPECT_TRUE(store_.HasBufferedPackets(connection_id_1)); EXPECT_TRUE(store_.HasBufferedPackets(connection_id_2)); EXPECT_TRUE(store_.HasChlosBuffered()); @@ -413,6 +416,7 @@ TEST_F(QuicBufferedPacketStoreTest, MultipleDiscardPackets) { EXPECT_EQ(1u, packets.buffered_packets.size()); ASSERT_EQ(1u, packets.alpns.size()); EXPECT_EQ("h3", packets.alpns[0]); + EXPECT_EQ(TestHostname(), packets.sni); // Since connection_id_2's chlo arrives, verify version is set. EXPECT_EQ(valid_version_, packets.version); EXPECT_TRUE(store_.HasChlosBuffered()); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_coalesced_packet.cc b/chromium/net/third_party/quiche/src/quic/core/quic_coalesced_packet.cc index 2ae9b41aeff..52673b61d5e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_coalesced_packet.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_coalesced_packet.cc @@ -4,9 +4,9 @@ #include "quic/core/quic_coalesced_packet.h" +#include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "quic/platform/api/quic_bug_tracker.h" -#include "quic/platform/api/quic_ptr_util.h" namespace quic { @@ -24,7 +24,7 @@ bool QuicCoalescedPacket::MaybeCoalescePacket( QuicBufferAllocator* allocator, QuicPacketLength current_max_packet_length) { if (packet.encrypted_length == 0) { - QUIC_BUG << "Trying to coalesce an empty packet"; + QUIC_BUG(quic_bug_10611_1) << "Trying to coalesce an empty packet"; return true; } if (length_ == 0) { @@ -47,7 +47,8 @@ bool QuicCoalescedPacket::MaybeCoalescePacket( return false; } if (max_packet_length_ != current_max_packet_length) { - QUIC_BUG << "Max packet length changes in the middle of the write path"; + QUIC_BUG(quic_bug_10611_2) + << "Max packet length changes in the middle of the write path"; return false; } if (ContainsPacketOfEncryptionLevel(packet.encryption_level)) { @@ -73,7 +74,7 @@ bool QuicCoalescedPacket::MaybeCoalescePacket( if (packet.encryption_level == ENCRYPTION_INITIAL) { // Save a copy of ENCRYPTION_INITIAL packet (excluding encrypted buffer, as // the packet will be re-serialized later). - initial_packet_ = QuicWrapUnique<SerializedPacket>( + initial_packet_ = absl::WrapUnique<SerializedPacket>( CopySerializedPacket(packet, allocator, /*copy_buffer=*/false)); return true; } @@ -102,9 +103,9 @@ void QuicCoalescedPacket::NeuterInitialPacket() { return; } if (length_ < initial_packet_->encrypted_length) { - QUIC_BUG << "length_: " << length_ - << ", is less than initial packet length: " - << initial_packet_->encrypted_length; + QUIC_BUG(quic_bug_10611_3) + << "length_: " << length_ << ", is less than initial packet length: " + << initial_packet_->encrypted_length; Clear(); return; } @@ -145,13 +146,24 @@ bool QuicCoalescedPacket::ContainsPacketOfEncryptionLevel( TransmissionType QuicCoalescedPacket::TransmissionTypeOfPacket( EncryptionLevel level) const { if (!ContainsPacketOfEncryptionLevel(level)) { - QUIC_BUG << "Coalesced packet does not contain packet of encryption level: " - << EncryptionLevelToString(level); + QUIC_BUG(quic_bug_10611_4) + << "Coalesced packet does not contain packet of encryption level: " + << EncryptionLevelToString(level); return NOT_RETRANSMISSION; } return transmission_types_[level]; } +size_t QuicCoalescedPacket::NumberOfPackets() const { + size_t num_of_packets = 0; + for (int8_t i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { + if (ContainsPacketOfEncryptionLevel(static_cast<EncryptionLevel>(i))) { + ++num_of_packets; + } + } + return num_of_packets; +} + std::string QuicCoalescedPacket::ToString(size_t serialized_length) const { // Total length and padding size. std::string info = absl::StrCat( diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_coalesced_packet.h b/chromium/net/third_party/quiche/src/quic/core/quic_coalesced_packet.h index e3f674e239f..e8bc2c3eb0b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_coalesced_packet.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_coalesced_packet.h @@ -46,6 +46,9 @@ class QUIC_EXPORT_PRIVATE QuicCoalescedPacket { // when this coalesced packet contains packet of |level|. TransmissionType TransmissionTypeOfPacket(EncryptionLevel level) const; + // Returns number of packets contained in this coalesced packet. + size_t NumberOfPackets() const; + const SerializedPacket* initial_packet() const { return initial_packet_.get(); } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_coalesced_packet_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_coalesced_packet_test.cc index 1d629ae135a..34f6da30890 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_coalesced_packet_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_coalesced_packet_test.cc @@ -19,6 +19,7 @@ TEST(QuicCoalescedPacketTest, MaybeCoalescePacket) { coalesced.ToString(0)); SimpleBufferAllocator allocator; EXPECT_EQ(0u, coalesced.length()); + EXPECT_EQ(0u, coalesced.NumberOfPackets()); char buffer[1000]; QuicSocketAddress self_address(QuicIpAddress::Loopback4(), 1); QuicSocketAddress peer_address(QuicIpAddress::Loopback4(), 2); @@ -35,6 +36,7 @@ TEST(QuicCoalescedPacketTest, MaybeCoalescePacket) { coalesced.TransmissionTypeOfPacket(ENCRYPTION_INITIAL)); EXPECT_EQ(1500u, coalesced.max_packet_length()); EXPECT_EQ(500u, coalesced.length()); + EXPECT_EQ(1u, coalesced.NumberOfPackets()); EXPECT_EQ( "total_length: 1500 padding_size: 1000 packets: {ENCRYPTION_INITIAL}", coalesced.ToString(1500)); @@ -54,6 +56,7 @@ TEST(QuicCoalescedPacketTest, MaybeCoalescePacket) { &allocator, 1500)); EXPECT_EQ(1500u, coalesced.max_packet_length()); EXPECT_EQ(1000u, coalesced.length()); + EXPECT_EQ(2u, coalesced.NumberOfPackets()); EXPECT_EQ(LOSS_RETRANSMISSION, coalesced.TransmissionTypeOfPacket(ENCRYPTION_ZERO_RTT)); EXPECT_EQ( @@ -77,6 +80,7 @@ TEST(QuicCoalescedPacketTest, MaybeCoalescePacket) { peer_address, &allocator, 1500)); EXPECT_EQ(1500u, coalesced.max_packet_length()); EXPECT_EQ(1000u, coalesced.length()); + EXPECT_EQ(2u, coalesced.NumberOfPackets()); // Max packet number length changed. SerializedPacket packet6(QuicPacketNumber(6), PACKET_4BYTE_PACKET_NUMBER, @@ -87,6 +91,7 @@ TEST(QuicCoalescedPacketTest, MaybeCoalescePacket) { "Max packet length changes in the middle of the write path"); EXPECT_EQ(1500u, coalesced.max_packet_length()); EXPECT_EQ(1000u, coalesced.length()); + EXPECT_EQ(2u, coalesced.NumberOfPackets()); } TEST(QuicCoalescedPacketTest, CopyEncryptedBuffers) { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_config.cc b/chromium/net/third_party/quiche/src/quic/core/quic_config.cc index 893d82233f1..796c641051b 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_config.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_config.cc @@ -24,7 +24,6 @@ #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_socket_address.h" -#include "quic/platform/api/quic_uint128.h" namespace quic { @@ -72,7 +71,7 @@ bool QuicFixedUint32::HasSendValue() const { } uint32_t QuicFixedUint32::GetSendValue() const { - QUIC_BUG_IF(!has_send_value_) + QUIC_BUG_IF(quic_bug_12743_1, !has_send_value_) << "No send value to get for tag:" << QuicTagToString(tag_); return send_value_; } @@ -87,7 +86,7 @@ bool QuicFixedUint32::HasReceivedValue() const { } uint32_t QuicFixedUint32::GetReceivedValue() const { - QUIC_BUG_IF(!has_receive_value_) + QUIC_BUG_IF(quic_bug_12743_2, !has_receive_value_) << "No receive value to get for tag:" << QuicTagToString(tag_); return receive_value_; } @@ -99,7 +98,7 @@ void QuicFixedUint32::SetReceivedValue(uint32_t value) { void QuicFixedUint32::ToHandshakeMessage(CryptoHandshakeMessage* out) const { if (tag_ == 0) { - QUIC_BUG + QUIC_BUG(quic_bug_12743_3) << "This parameter does not support writing to CryptoHandshakeMessage"; return; } @@ -116,7 +115,7 @@ QuicErrorCode QuicFixedUint32::ProcessPeerHello( if (tag_ == 0) { *error_details = "This parameter does not support reading from CryptoHandshakeMessage"; - QUIC_BUG << *error_details; + QUIC_BUG(quic_bug_10575_1) << *error_details; return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; } QuicErrorCode error = peer_hello.GetUint32(tag_, &receive_value_); @@ -150,7 +149,8 @@ bool QuicFixedUint62::HasSendValue() const { uint64_t QuicFixedUint62::GetSendValue() const { if (!has_send_value_) { - QUIC_BUG << "No send value to get for tag:" << QuicTagToString(tag_); + QUIC_BUG(quic_bug_10575_2) + << "No send value to get for tag:" << QuicTagToString(tag_); return 0; } return send_value_; @@ -158,7 +158,7 @@ uint64_t QuicFixedUint62::GetSendValue() const { void QuicFixedUint62::SetSendValue(uint64_t value) { if (value > kVarInt62MaxValue) { - QUIC_BUG << "QuicFixedUint62 invalid value " << value; + QUIC_BUG(quic_bug_10575_3) << "QuicFixedUint62 invalid value " << value; value = kVarInt62MaxValue; } has_send_value_ = true; @@ -171,7 +171,8 @@ bool QuicFixedUint62::HasReceivedValue() const { uint64_t QuicFixedUint62::GetReceivedValue() const { if (!has_receive_value_) { - QUIC_BUG << "No receive value to get for tag:" << QuicTagToString(tag_); + QUIC_BUG(quic_bug_10575_4) + << "No receive value to get for tag:" << QuicTagToString(tag_); return 0; } return receive_value_; @@ -188,8 +189,8 @@ void QuicFixedUint62::ToHandshakeMessage(CryptoHandshakeMessage* out) const { } uint32_t send_value32; if (send_value_ > std::numeric_limits<uint32_t>::max()) { - QUIC_BUG << "Attempting to send " << send_value_ - << " for tag:" << QuicTagToString(tag_); + QUIC_BUG(quic_bug_10575_5) << "Attempting to send " << send_value_ + << " for tag:" << QuicTagToString(tag_); send_value32 = std::numeric_limits<uint32_t>::max(); } else { send_value32 = static_cast<uint32_t>(send_value_); @@ -223,54 +224,61 @@ QuicErrorCode QuicFixedUint62::ProcessPeerHello( return error; } -QuicFixedUint128::QuicFixedUint128(QuicTag tag, QuicConfigPresence presence) +QuicFixedStatelessResetToken::QuicFixedStatelessResetToken( + QuicTag tag, + QuicConfigPresence presence) : QuicConfigValue(tag, presence), has_send_value_(false), has_receive_value_(false) {} -QuicFixedUint128::~QuicFixedUint128() {} +QuicFixedStatelessResetToken::~QuicFixedStatelessResetToken() {} -bool QuicFixedUint128::HasSendValue() const { +bool QuicFixedStatelessResetToken::HasSendValue() const { return has_send_value_; } -QuicUint128 QuicFixedUint128::GetSendValue() const { - QUIC_BUG_IF(!has_send_value_) +const StatelessResetToken& QuicFixedStatelessResetToken::GetSendValue() const { + QUIC_BUG_IF(quic_bug_12743_4, !has_send_value_) << "No send value to get for tag:" << QuicTagToString(tag_); return send_value_; } -void QuicFixedUint128::SetSendValue(QuicUint128 value) { +void QuicFixedStatelessResetToken::SetSendValue( + const StatelessResetToken& value) { has_send_value_ = true; send_value_ = value; } -bool QuicFixedUint128::HasReceivedValue() const { +bool QuicFixedStatelessResetToken::HasReceivedValue() const { return has_receive_value_; } -QuicUint128 QuicFixedUint128::GetReceivedValue() const { - QUIC_BUG_IF(!has_receive_value_) +const StatelessResetToken& QuicFixedStatelessResetToken::GetReceivedValue() + const { + QUIC_BUG_IF(quic_bug_12743_5, !has_receive_value_) << "No receive value to get for tag:" << QuicTagToString(tag_); return receive_value_; } -void QuicFixedUint128::SetReceivedValue(QuicUint128 value) { +void QuicFixedStatelessResetToken::SetReceivedValue( + const StatelessResetToken& value) { has_receive_value_ = true; receive_value_ = value; } -void QuicFixedUint128::ToHandshakeMessage(CryptoHandshakeMessage* out) const { +void QuicFixedStatelessResetToken::ToHandshakeMessage( + CryptoHandshakeMessage* out) const { if (has_send_value_) { out->SetValue(tag_, send_value_); } } -QuicErrorCode QuicFixedUint128::ProcessPeerHello( +QuicErrorCode QuicFixedStatelessResetToken::ProcessPeerHello( const CryptoHandshakeMessage& peer_hello, HelloType /*hello_type*/, std::string* error_details) { QUICHE_DCHECK(error_details != nullptr); - QuicErrorCode error = peer_hello.GetUint128(tag_, &receive_value_); + QuicErrorCode error = + peer_hello.GetStatelessResetToken(tag_, &receive_value_); switch (error) { case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: if (presence_ == PRESENCE_OPTIONAL) { @@ -304,7 +312,7 @@ bool QuicFixedTagVector::HasSendValues() const { } const QuicTagVector& QuicFixedTagVector::GetSendValues() const { - QUIC_BUG_IF(!has_send_values_) + QUIC_BUG_IF(quic_bug_12743_6, !has_send_values_) << "No send values to get for tag:" << QuicTagToString(tag_); return send_values_; } @@ -319,7 +327,7 @@ bool QuicFixedTagVector::HasReceivedValues() const { } const QuicTagVector& QuicFixedTagVector::GetReceivedValues() const { - QUIC_BUG_IF(!has_receive_values_) + QUIC_BUG_IF(quic_bug_12743_7, !has_receive_values_) << "No receive value to get for tag:" << QuicTagToString(tag_); return receive_values_; } @@ -375,7 +383,7 @@ bool QuicFixedSocketAddress::HasSendValue() const { } const QuicSocketAddress& QuicFixedSocketAddress::GetSendValue() const { - QUIC_BUG_IF(!has_send_value_) + QUIC_BUG_IF(quic_bug_12743_8, !has_send_value_) << "No send value to get for tag:" << QuicTagToString(tag_); return send_value_; } @@ -390,7 +398,7 @@ bool QuicFixedSocketAddress::HasReceivedValue() const { } const QuicSocketAddress& QuicFixedSocketAddress::GetReceivedValue() const { - QUIC_BUG_IF(!has_receive_value_) + QUIC_BUG_IF(quic_bug_12743_9, !has_receive_value_) << "No receive value to get for tag:" << QuicTagToString(tag_); return receive_value_; } @@ -544,7 +552,8 @@ const QuicTagVector& QuicConfig::ClientRequestedIndependentOptions( void QuicConfig::SetIdleNetworkTimeout(QuicTime::Delta idle_network_timeout) { if (idle_network_timeout.ToMicroseconds() <= 0) { - QUIC_BUG << "Invalid idle network timeout " << idle_network_timeout; + QUIC_BUG(quic_bug_10575_6) + << "Invalid idle network timeout " << idle_network_timeout; return; } max_idle_timeout_to_send_ = idle_network_timeout; @@ -729,9 +738,10 @@ uint64_t QuicConfig::GetInitialRoundTripTimeUsToSend() const { void QuicConfig::SetInitialStreamFlowControlWindowToSend( uint64_t window_bytes) { if (window_bytes < kMinimumFlowControlSendWindow) { - QUIC_BUG << "Initial stream flow control receive window (" << window_bytes - << ") cannot be set lower than minimum (" - << kMinimumFlowControlSendWindow << ")."; + QUIC_BUG(quic_bug_10575_7) + << "Initial stream flow control receive window (" << window_bytes + << ") cannot be set lower than minimum (" + << kMinimumFlowControlSendWindow << ")."; window_bytes = kMinimumFlowControlSendWindow; } initial_stream_flow_control_window_bytes_.SetSendValue(window_bytes); @@ -824,9 +834,10 @@ uint64_t QuicConfig::ReceivedInitialMaxStreamDataBytesUnidirectional() const { void QuicConfig::SetInitialSessionFlowControlWindowToSend( uint64_t window_bytes) { if (window_bytes < kMinimumFlowControlSendWindow) { - QUIC_BUG << "Initial session flow control receive window (" << window_bytes - << ") cannot be set lower than default (" - << kMinimumFlowControlSendWindow << ")."; + QUIC_BUG(quic_bug_10575_8) + << "Initial session flow control receive window (" << window_bytes + << ") cannot be set lower than default (" + << kMinimumFlowControlSendWindow << ")."; window_bytes = kMinimumFlowControlSendWindow; } initial_session_flow_control_window_bytes_.SetSendValue(window_bytes); @@ -871,8 +882,9 @@ bool QuicConfig::KeyUpdateSupportedRemotely() const { void QuicConfig::SetIPv6AlternateServerAddressToSend( const QuicSocketAddress& alternate_server_address_ipv6) { if (!alternate_server_address_ipv6.host().IsIPv6()) { - QUIC_BUG << "Cannot use SetIPv6AlternateServerAddressToSend with " - << alternate_server_address_ipv6; + QUIC_BUG(quic_bug_10575_9) + << "Cannot use SetIPv6AlternateServerAddressToSend with " + << alternate_server_address_ipv6; return; } alternate_server_address_ipv6_.SetSendValue(alternate_server_address_ipv6); @@ -881,10 +893,11 @@ void QuicConfig::SetIPv6AlternateServerAddressToSend( void QuicConfig::SetIPv6AlternateServerAddressToSend( const QuicSocketAddress& alternate_server_address_ipv6, const QuicConnectionId& connection_id, - QuicUint128 stateless_reset_token) { + const StatelessResetToken& stateless_reset_token) { if (!alternate_server_address_ipv6.host().IsIPv6()) { - QUIC_BUG << "Cannot use SetIPv6AlternateServerAddressToSend with " - << alternate_server_address_ipv6; + QUIC_BUG(quic_bug_10575_10) + << "Cannot use SetIPv6AlternateServerAddressToSend with " + << alternate_server_address_ipv6; return; } alternate_server_address_ipv6_.SetSendValue(alternate_server_address_ipv6); @@ -904,8 +917,9 @@ const QuicSocketAddress& QuicConfig::ReceivedIPv6AlternateServerAddress() void QuicConfig::SetIPv4AlternateServerAddressToSend( const QuicSocketAddress& alternate_server_address_ipv4) { if (!alternate_server_address_ipv4.host().IsIPv4()) { - QUIC_BUG << "Cannot use SetIPv4AlternateServerAddressToSend with " - << alternate_server_address_ipv4; + QUIC_BUG(quic_bug_10575_11) + << "Cannot use SetIPv4AlternateServerAddressToSend with " + << alternate_server_address_ipv4; return; } alternate_server_address_ipv4_.SetSendValue(alternate_server_address_ipv4); @@ -914,10 +928,11 @@ void QuicConfig::SetIPv4AlternateServerAddressToSend( void QuicConfig::SetIPv4AlternateServerAddressToSend( const QuicSocketAddress& alternate_server_address_ipv4, const QuicConnectionId& connection_id, - QuicUint128 stateless_reset_token) { + const StatelessResetToken& stateless_reset_token) { if (!alternate_server_address_ipv4.host().IsIPv4()) { - QUIC_BUG << "Cannot use SetIPv4AlternateServerAddressToSend with " - << alternate_server_address_ipv4; + QUIC_BUG(quic_bug_10575_12) + << "Cannot use SetIPv4AlternateServerAddressToSend with " + << alternate_server_address_ipv4; return; } alternate_server_address_ipv4_.SetSendValue(alternate_server_address_ipv4); @@ -940,7 +955,7 @@ bool QuicConfig::HasReceivedPreferredAddressConnectionIdAndToken() const { preferred_address_connection_id_and_token_.has_value(); } -const std::pair<QuicConnectionId, QuicUint128>& +const std::pair<QuicConnectionId, StatelessResetToken>& QuicConfig::ReceivedPreferredAddressConnectionIdAndToken() const { QUICHE_DCHECK(HasReceivedPreferredAddressConnectionIdAndToken()); return *preferred_address_connection_id_and_token_; @@ -958,7 +973,7 @@ bool QuicConfig::HasReceivedOriginalConnectionId() const { QuicConnectionId QuicConfig::ReceivedOriginalConnectionId() const { if (!HasReceivedOriginalConnectionId()) { - QUIC_BUG << "No received original connection ID"; + QUIC_BUG(quic_bug_10575_13) << "No received original connection ID"; return EmptyQuicConnectionId(); } return received_original_destination_connection_id_.value(); @@ -975,7 +990,7 @@ bool QuicConfig::HasReceivedInitialSourceConnectionId() const { QuicConnectionId QuicConfig::ReceivedInitialSourceConnectionId() const { if (!HasReceivedInitialSourceConnectionId()) { - QUIC_BUG << "No received initial source connection ID"; + QUIC_BUG(quic_bug_10575_14) << "No received initial source connection ID"; return EmptyQuicConnectionId(); } return received_initial_source_connection_id_.value(); @@ -992,14 +1007,14 @@ bool QuicConfig::HasReceivedRetrySourceConnectionId() const { QuicConnectionId QuicConfig::ReceivedRetrySourceConnectionId() const { if (!HasReceivedRetrySourceConnectionId()) { - QUIC_BUG << "No received retry source connection ID"; + QUIC_BUG(quic_bug_10575_15) << "No received retry source connection ID"; return EmptyQuicConnectionId(); } return received_retry_source_connection_id_.value(); } void QuicConfig::SetStatelessResetTokenToSend( - QuicUint128 stateless_reset_token) { + const StatelessResetToken& stateless_reset_token) { stateless_reset_token_.SetSendValue(stateless_reset_token); } @@ -1007,7 +1022,7 @@ bool QuicConfig::HasReceivedStatelessResetToken() const { return stateless_reset_token_.HasReceivedValue(); } -QuicUint128 QuicConfig::ReceivedStatelessResetToken() const { +const StatelessResetToken& QuicConfig::ReceivedStatelessResetToken() const { return stateless_reset_token_.GetReceivedValue(); } @@ -1195,7 +1210,8 @@ bool QuicConfig::FillTransportParameters(TransportParameters* params) const { max_idle_timeout_to_send_.ToMilliseconds()); if (stateless_reset_token_.HasSendValue()) { - QuicUint128 stateless_reset_token = stateless_reset_token_.GetSendValue(); + StatelessResetToken stateless_reset_token = + stateless_reset_token_.GetSendValue(); params->stateless_reset_token.assign( reinterpret_cast<const char*>(&stateless_reset_token), reinterpret_cast<const char*>(&stateless_reset_token) + @@ -1310,10 +1326,10 @@ QuicErrorCode QuicConfig::ProcessTransportParameters( } if (!is_resumption && !params.stateless_reset_token.empty()) { - QuicUint128 stateless_reset_token; + StatelessResetToken stateless_reset_token; if (params.stateless_reset_token.size() != sizeof(stateless_reset_token)) { - QUIC_BUG << "Bad stateless reset token length " - << params.stateless_reset_token.size(); + QUIC_BUG(quic_bug_10575_16) << "Bad stateless reset token length " + << params.stateless_reset_token.size(); *error_details = "Bad stateless reset token length"; return QUIC_INTERNAL_ERROR; } @@ -1376,7 +1392,7 @@ QuicErrorCode QuicConfig::ProcessTransportParameters( if (!params.preferred_address->connection_id.IsEmpty()) { preferred_address_connection_id_and_token_ = std::make_pair( params.preferred_address->connection_id, - *reinterpret_cast<const QuicUint128*>( + *reinterpret_cast<const StatelessResetToken*>( ¶ms.preferred_address->stateless_reset_token.front())); } } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_config.h b/chromium/net/third_party/quiche/src/quic/core/quic_config.h index d0fa2c79200..d122be7ff29 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_config.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_config.h @@ -14,8 +14,8 @@ #include "quic/core/quic_connection_id.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_time.h" +#include "quic/core/quic_types.h" #include "quic/platform/api/quic_export.h" -#include "quic/platform/api/quic_uint128.h" namespace quic { @@ -134,23 +134,25 @@ class QUIC_EXPORT_PRIVATE QuicFixedUint62 : public QuicConfigValue { uint64_t receive_value_; }; -// Stores uint128 from CHLO or SHLO messages that are not negotiated. -class QUIC_EXPORT_PRIVATE QuicFixedUint128 : public QuicConfigValue { +// Stores StatelessResetToken from CHLO or SHLO messages that are not +// negotiated. +class QUIC_EXPORT_PRIVATE QuicFixedStatelessResetToken + : public QuicConfigValue { public: - QuicFixedUint128(QuicTag tag, QuicConfigPresence presence); - ~QuicFixedUint128() override; + QuicFixedStatelessResetToken(QuicTag tag, QuicConfigPresence presence); + ~QuicFixedStatelessResetToken() override; bool HasSendValue() const; - QuicUint128 GetSendValue() const; + const StatelessResetToken& GetSendValue() const; - void SetSendValue(QuicUint128 value); + void SetSendValue(const StatelessResetToken& value); bool HasReceivedValue() const; - QuicUint128 GetReceivedValue() const; + const StatelessResetToken& GetReceivedValue() const; - void SetReceivedValue(QuicUint128 value); + void SetReceivedValue(const StatelessResetToken& value); // If has_send_value is true, serialises |tag_| and |send_value_| to |out|. void ToHandshakeMessage(CryptoHandshakeMessage* out) const override; @@ -163,8 +165,8 @@ class QUIC_EXPORT_PRIVATE QuicFixedUint128 : public QuicConfigValue { private: bool has_send_value_; bool has_receive_value_; - QuicUint128 send_value_; - QuicUint128 receive_value_; + StatelessResetToken send_value_; + StatelessResetToken receive_value_; }; // Stores tag from CHLO or SHLO messages that are not negotiated. @@ -394,7 +396,7 @@ class QUIC_EXPORT_PRIVATE QuicConfig { void SetIPv6AlternateServerAddressToSend( const QuicSocketAddress& alternate_server_address_ipv6, const QuicConnectionId& connection_id, - QuicUint128 stateless_reset_token); + const StatelessResetToken& stateless_reset_token); bool HasReceivedIPv6AlternateServerAddress() const; const QuicSocketAddress& ReceivedIPv6AlternateServerAddress() const; @@ -404,13 +406,13 @@ class QUIC_EXPORT_PRIVATE QuicConfig { void SetIPv4AlternateServerAddressToSend( const QuicSocketAddress& alternate_server_address_ipv4, const QuicConnectionId& connection_id, - QuicUint128 stateless_reset_token); + const StatelessResetToken& stateless_reset_token); bool HasReceivedIPv4AlternateServerAddress() const; const QuicSocketAddress& ReceivedIPv4AlternateServerAddress() const; // Preferred Address Connection ID and Token. bool HasReceivedPreferredAddressConnectionIdAndToken() const; - const std::pair<QuicConnectionId, QuicUint128>& + const std::pair<QuicConnectionId, StatelessResetToken>& ReceivedPreferredAddressConnectionIdAndToken() const; // Original destination connection ID. @@ -420,9 +422,10 @@ class QUIC_EXPORT_PRIVATE QuicConfig { QuicConnectionId ReceivedOriginalConnectionId() const; // Stateless reset token. - void SetStatelessResetTokenToSend(QuicUint128 stateless_reset_token); + void SetStatelessResetTokenToSend( + const StatelessResetToken& stateless_reset_token); bool HasReceivedStatelessResetToken() const; - QuicUint128 ReceivedStatelessResetToken() const; + const StatelessResetToken& ReceivedStatelessResetToken() const; // Manage the IETF QUIC Max ACK Delay transport parameter. // The sent value is the delay that this node uses @@ -604,12 +607,12 @@ class QUIC_EXPORT_PRIVATE QuicConfig { QuicFixedSocketAddress alternate_server_address_ipv4_; // Connection Id data to send from the server or receive at the client as part // of the preferred address transport parameter. - absl::optional<std::pair<QuicConnectionId, QuicUint128>> + absl::optional<std::pair<QuicConnectionId, StatelessResetToken>> preferred_address_connection_id_and_token_; // Stateless reset token used in IETF public reset packet. // Uses the stateless_reset_token transport parameter in IETF QUIC. - QuicFixedUint128 stateless_reset_token_; + QuicFixedStatelessResetToken stateless_reset_token_; // List of QuicTags whose presence immediately causes the session to // be created. This allows for CHLOs that are larger than a single diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_config_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_config_test.cc index 2f3783510fd..5124a975bf8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_config_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_config_test.cc @@ -17,7 +17,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_uint128.h" #include "quic/test_tools/quic_config_peer.h" #include "quic/test_tools/quic_test_utils.h" @@ -188,7 +187,9 @@ TEST_P(QuicConfigTest, ProcessServerHello) { QuicIpAddress host; host.FromString("127.0.3.1"); const QuicSocketAddress kTestServerAddress = QuicSocketAddress(host, 1234); - const QuicUint128 kTestResetToken = MakeQuicUint128(0, 10111100001); + const StatelessResetToken kTestStatelessResetToken{ + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; const uint32_t kTestMaxAckDelayMs = static_cast<uint32_t>(kDefaultDelayedAckTimeMs + 1); QuicConfig server_config; @@ -202,7 +203,7 @@ TEST_P(QuicConfigTest, ProcessServerHello) { server_config.SetInitialSessionFlowControlWindowToSend( 2 * kInitialSessionFlowControlWindowForTest); server_config.SetIPv4AlternateServerAddressToSend(kTestServerAddress); - server_config.SetStatelessResetTokenToSend(kTestResetToken); + server_config.SetStatelessResetTokenToSend(kTestStatelessResetToken); server_config.SetMaxAckDelayToSendMs(kTestMaxAckDelayMs); CryptoHandshakeMessage msg; server_config.ToHandshakeMessage(&msg, version_.transport_version); @@ -222,7 +223,7 @@ TEST_P(QuicConfigTest, ProcessServerHello) { EXPECT_EQ(kTestServerAddress, config_.ReceivedIPv4AlternateServerAddress()); EXPECT_FALSE(config_.HasReceivedIPv6AlternateServerAddress()); EXPECT_TRUE(config_.HasReceivedStatelessResetToken()); - EXPECT_EQ(kTestResetToken, config_.ReceivedStatelessResetToken()); + EXPECT_EQ(kTestStatelessResetToken, config_.ReceivedStatelessResetToken()); EXPECT_TRUE(config_.HasReceivedMaxAckDelayMs()); EXPECT_EQ(kTestMaxAckDelayMs, config_.ReceivedMaxAckDelayMs()); @@ -481,7 +482,7 @@ TEST_P(QuicConfigTest, FillTransportParams) { host.FromString("127.0.3.1"); QuicSocketAddress kTestServerAddress = QuicSocketAddress(host, 1234); QuicConnectionId new_connection_id = TestConnectionId(5); - QuicUint128 new_stateless_reset_token = + StatelessResetToken new_stateless_reset_token = QuicUtils::GenerateStatelessResetToken(new_connection_id); config_.SetIPv4AlternateServerAddressToSend( kTestServerAddress, new_connection_id, new_stateless_reset_token); @@ -521,8 +522,7 @@ TEST_P(QuicConfigTest, FillTransportParams) { EXPECT_TRUE(params.key_update_not_yet_supported); EXPECT_EQ(params.preferred_address->ipv4_socket_address, kTestServerAddress); - EXPECT_EQ(params.preferred_address->connection_id, new_connection_id); - EXPECT_EQ(*reinterpret_cast<QuicUint128*>( + EXPECT_EQ(*reinterpret_cast<StatelessResetToken*>( ¶ms.preferred_address->stateless_reset_token.front()), new_stateless_reset_token); } @@ -775,7 +775,7 @@ TEST_P(QuicConfigTest, SendPreferredIPv4Address) { host.FromString("::ffff:192.0.2.128"); QuicSocketAddress kTestServerAddress = QuicSocketAddress(host, 1234); QuicConnectionId new_connection_id = TestConnectionId(5); - QuicUint128 new_stateless_reset_token = + StatelessResetToken new_stateless_reset_token = QuicUtils::GenerateStatelessResetToken(new_connection_id); auto preferred_address = std::make_unique<TransportParameters::PreferredAddress>(); @@ -795,7 +795,7 @@ TEST_P(QuicConfigTest, SendPreferredIPv4Address) { EXPECT_TRUE(config_.HasReceivedIPv6AlternateServerAddress()); EXPECT_EQ(config_.ReceivedIPv6AlternateServerAddress(), kTestServerAddress); EXPECT_TRUE(config_.HasReceivedPreferredAddressConnectionIdAndToken()); - const std::pair<QuicConnectionId, QuicUint128>& + const std::pair<QuicConnectionId, StatelessResetToken>& preferred_address_connection_id_and_token = config_.ReceivedPreferredAddressConnectionIdAndToken(); EXPECT_EQ(preferred_address_connection_id_and_token.first, new_connection_id); 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 471331b3f6d..9bd21403263 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 @@ -117,7 +117,7 @@ class SendAlarmDelegate : public QuicAlarm::Delegate { void OnAlarm() override { QUICHE_DCHECK(connection_->connected()); - connection_->WriteAndBundleAcksIfNotBlocked(); + connection_->WriteIfNotBlocked(); } private: @@ -353,7 +353,7 @@ QuicConnection::QuicConnection( fill_up_link_during_probing_(false), probing_retransmission_pending_(false), stateless_reset_token_received_(false), - received_stateless_reset_token_(0), + received_stateless_reset_token_({}), last_control_frame_id_(kInvalidControlFrameId), is_path_degrading_(false), processing_ack_frame_(false), @@ -371,13 +371,9 @@ QuicConnection::QuicConnection( GetQuicReloadableFlag(quic_use_encryption_level_context)), path_validator_(alarm_factory_, &arena_, this, random_generator_), alternative_path_(QuicSocketAddress(), QuicSocketAddress()), - most_recent_frame_type_(NUM_FRAME_TYPES), - validate_client_addresses_( - framer_.version().HasIetfQuicFrames() && use_path_validator_ && - count_bytes_on_alternative_path_separately_ && - update_packet_content_returns_connected_ && - GetQuicReloadableFlag(quic_server_reverse_validate_new_path)) { - QUIC_BUG_IF(!start_peer_migration_earlier_ && send_path_response_); + most_recent_frame_type_(NUM_FRAME_TYPES) { + QUIC_BUG_IF(quic_bug_12714_1, + !start_peer_migration_earlier_ && send_path_response_); QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT || default_path_.self_address.IsInitialized()); @@ -385,12 +381,22 @@ QuicConnection::QuicConnection( if (use_encryption_level_context_) { QUIC_RELOADABLE_FLAG_COUNT(quic_use_encryption_level_context); } + + support_multiple_connection_ids_ = + version().HasIetfQuicFrames() && + framer_.do_not_synthesize_source_cid_for_short_header() && + GetQuicRestartFlag(quic_use_reference_counted_sesssion_map) && + GetQuicRestartFlag(quic_time_wait_list_support_multiple_cid_v2) && + GetQuicRestartFlag( + quic_dispatcher_support_multiple_cid_per_connection_v2) && + GetQuicReloadableFlag(quic_connection_support_multiple_cids_v2); + QUIC_DLOG(INFO) << ENDPOINT << "Created connection with server connection ID " << server_connection_id << " and version: " << ParsedQuicVersionToString(version()); - QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion(server_connection_id, - transport_version())) + QUIC_BUG_IF(quic_bug_12714_2, !QuicUtils::IsConnectionIdValidForVersion( + server_connection_id, transport_version())) << "QuicConnection: attempted to use server connection ID " << server_connection_id << " which is invalid with version " << version(); framer_.set_visitor(this); @@ -581,6 +587,16 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { config.max_idle_time_before_crypto_handshake()); } + if (support_multiple_connection_ids_ && + config.HasReceivedPreferredAddressConnectionIdAndToken()) { + QuicNewConnectionIdFrame frame; + std::tie(frame.connection_id, frame.stateless_reset_token) = + config.ReceivedPreferredAddressConnectionIdAndToken(); + frame.sequence_number = 1u; + frame.retire_prior_to = 0u; + OnNewConnectionIdFrameInner(frame); + } + sent_packet_manager_.SetFromConfig(config); if (perspective_ == Perspective::IS_SERVER && config.HasClientSentConnectionOption(kAFF2, perspective_)) { @@ -678,11 +694,16 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { if (config.HasClientSentConnectionOption(kEACK, perspective_)) { bundle_retransmittable_with_pto_ack_ = true; } - if (GetQuicReloadableFlag(quic_dont_defer_sending) && - config.HasClientSentConnectionOption(kDFER, perspective_)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_dont_defer_sending); + if (config.HasClientSentConnectionOption(kDFER, perspective_)) { defer_send_in_response_to_packets_ = false; } + if (framer_.version().HasIetfQuicFrames() && use_path_validator_ && + count_bytes_on_alternative_path_separately_ && + GetQuicReloadableFlag(quic_server_reverse_validate_new_path3) && + config.HasClientSentConnectionOption(kRVCM, perspective_)) { + QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path3, 6, 6); + validate_client_addresses_ = true; + } if (config.HasReceivedMaxPacketSize()) { peer_max_packet_size_ = config.ReceivedMaxPacketSize(); MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); @@ -704,11 +725,13 @@ void QuicConnection::SetFromConfig(const QuicConfig& config) { void QuicConnection::EnableLegacyVersionEncapsulation( const std::string& server_name) { if (perspective_ != Perspective::IS_CLIENT) { - QUIC_BUG << "Cannot enable Legacy Version Encapsulation on the server"; + QUIC_BUG(quic_bug_10511_1) + << "Cannot enable Legacy Version Encapsulation on the server"; return; } if (legacy_version_encapsulation_enabled_) { - QUIC_BUG << "Do not call EnableLegacyVersionEncapsulation twice"; + QUIC_BUG(quic_bug_10511_2) + << "Do not call EnableLegacyVersionEncapsulation twice"; return; } if (!QuicHostnameUtils::IsValidSNI(server_name)) { @@ -864,7 +887,7 @@ bool QuicConnection::OnProtocolVersionMismatch( << ParsedQuicVersionToString(received_version); if (perspective_ == Perspective::IS_CLIENT) { const std::string error_details = "Protocol version mismatch."; - QUIC_BUG << ENDPOINT << error_details; + QUIC_BUG(quic_bug_10511_3) << ENDPOINT << error_details; CloseConnection(QUIC_INTERNAL_ERROR, error_details, ConnectionCloseBehavior::SILENT_CLOSE); } @@ -884,7 +907,7 @@ void QuicConnection::OnVersionNegotiationPacket( if (perspective_ == Perspective::IS_SERVER) { const std::string error_details = "Server received version negotiation packet."; - QUIC_BUG << error_details; + QUIC_BUG(quic_bug_10511_4) << error_details; QUIC_CODE_COUNT(quic_tear_down_local_connection_on_version_negotiation); CloseConnection(QUIC_INTERNAL_ERROR, error_details, ConnectionCloseBehavior::SILENT_CLOSE); @@ -957,8 +980,7 @@ void QuicConnection::OnRetryPacket(QuicConnectionId original_connection_id, QUICHE_DCHECK(!retry_source_connection_id_.has_value()) << retry_source_connection_id_.value(); retry_source_connection_id_ = new_connection_id; - server_connection_id_ = new_connection_id; - packet_creator_.SetServerConnectionId(server_connection_id_); + ReplaceInitialServerConnectionId(new_connection_id); packet_creator_.SetRetryToken(retry_token); // Reinstall initial crypters because the connection ID changed. @@ -1007,10 +1029,16 @@ bool QuicConnection::OnUnauthenticatedPublicHeader( framer_.set_drop_incoming_retry_packets(true); } + bool skip_server_connection_id_validation = + framer_.do_not_synthesize_source_cid_for_short_header() && + perspective_ == Perspective::IS_CLIENT && + header.form == IETF_QUIC_SHORT_HEADER_PACKET; + QuicConnectionId server_connection_id = GetServerConnectionIdAsRecipient(header, perspective_); - if (server_connection_id != server_connection_id_ && + if (!skip_server_connection_id_validation && + server_connection_id != server_connection_id_ && !HasIncomingConnectionId(server_connection_id)) { if (PacketCanReplaceConnectionId(header, perspective_)) { QUIC_DLOG(INFO) << ENDPOINT << "Accepting packet with new connection ID " @@ -1038,6 +1066,14 @@ bool QuicConnection::OnUnauthenticatedPublicHeader( return true; } + if (framer_.do_not_synthesize_source_cid_for_short_header() && + perspective_ == Perspective::IS_SERVER && + header.form == IETF_QUIC_SHORT_HEADER_PACKET) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_do_not_synthesize_source_cid_for_short_header, 3, 3); + return true; + } + QuicConnectionId client_connection_id = GetClientConnectionIdAsRecipient(header, perspective_); @@ -1069,7 +1105,10 @@ bool QuicConnection::OnUnauthenticatedHeader(const QuicPacketHeader& header) { // Check that any public reset packet with a different connection ID that was // routed to this QuicConnection has been redirected before control reaches // here. - QUICHE_DCHECK(GetServerConnectionIdAsRecipient(header, perspective_) == + QUICHE_DCHECK((framer_.do_not_synthesize_source_cid_for_short_header() && + perspective_ == Perspective::IS_CLIENT && + header.form == IETF_QUIC_SHORT_HEADER_PACKET) || + GetServerConnectionIdAsRecipient(header, perspective_) == server_connection_id_ || HasIncomingConnectionId( GetServerConnectionIdAsRecipient(header, perspective_)) || @@ -1080,7 +1119,8 @@ bool QuicConnection::OnUnauthenticatedHeader(const QuicPacketHeader& header) { const std::string error_details = "Pending frames must be serialized before incoming packets are " "processed."; - QUIC_BUG << error_details << ", received header: " << header; + QUIC_BUG(quic_pending_frames_not_serialized) + << error_details << ", received header: " << header; CloseConnection(QUIC_INTERNAL_ERROR, error_details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return false; @@ -1261,7 +1301,7 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { } bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) { - QUIC_BUG_IF(!connected_) + QUIC_BUG_IF(quic_bug_12714_3, !connected_) << "Processing STREAM frame when connection is closed. Last frame: " << most_recent_frame_type_; @@ -1283,11 +1323,11 @@ bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) { return false; } - QUIC_PEER_BUG << ENDPOINT - << "Received an unencrypted data frame: closing connection" - << " packet_number:" << last_header_.packet_number - << " stream_id:" << frame.stream_id - << " received_packets:" << ack_frame(); + QUIC_PEER_BUG(quic_peer_bug_10511_6) + << ENDPOINT << "Received an unencrypted data frame: closing connection" + << " packet_number:" << last_header_.packet_number + << " stream_id:" << frame.stream_id + << " received_packets:" << ack_frame(); CloseConnection(QUIC_UNENCRYPTED_STREAM_DATA, "Unencrypted stream data seen.", ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); @@ -1301,7 +1341,7 @@ bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) { } bool QuicConnection::OnCryptoFrame(const QuicCryptoFrame& frame) { - QUIC_BUG_IF(!connected_) + QUIC_BUG_IF(quic_bug_12714_4, !connected_) << "Processing CRYPTO frame when connection is closed. Last frame: " << most_recent_frame_type_; @@ -1321,7 +1361,7 @@ bool QuicConnection::OnCryptoFrame(const QuicCryptoFrame& frame) { bool QuicConnection::OnAckFrameStart(QuicPacketNumber largest_acked, QuicTime::Delta ack_delay_time) { - QUIC_BUG_IF(!connected_) + QUIC_BUG_IF(quic_bug_12714_5, !connected_) << "Processing ACK frame start when connection is closed. Last frame: " << most_recent_frame_type_; @@ -1369,7 +1409,7 @@ bool QuicConnection::OnAckFrameStart(QuicPacketNumber largest_acked, } bool QuicConnection::OnAckRange(QuicPacketNumber start, QuicPacketNumber end) { - QUIC_BUG_IF(!connected_) + QUIC_BUG_IF(quic_bug_12714_6, !connected_) << "Processing ACK frame range when connection is closed. Last frame: " << most_recent_frame_type_; QUIC_DVLOG(1) << ENDPOINT << "OnAckRange: [" << start << ", " << end << ")"; @@ -1386,9 +1426,10 @@ bool QuicConnection::OnAckRange(QuicPacketNumber start, QuicPacketNumber end) { bool QuicConnection::OnAckTimestamp(QuicPacketNumber packet_number, QuicTime timestamp) { - QUIC_BUG_IF(!connected_) << "Processing ACK frame time stamp when connection " - "is closed. Last frame: " - << most_recent_frame_type_; + QUIC_BUG_IF(quic_bug_10511_7, !connected_) + << "Processing ACK frame time stamp when connection " + "is closed. Last frame: " + << most_recent_frame_type_; QUIC_DVLOG(1) << ENDPOINT << "OnAckTimestamp: [" << packet_number << ", " << timestamp.ToDebuggingValue() << ")"; @@ -1403,7 +1444,7 @@ bool QuicConnection::OnAckTimestamp(QuicPacketNumber packet_number, } bool QuicConnection::OnAckFrameEnd(QuicPacketNumber start) { - QUIC_BUG_IF(!connected_) + QUIC_BUG_IF(quic_bug_12714_7, !connected_) << "Processing ACK frame end when connection is closed. Last frame: " << most_recent_frame_type_; QUIC_DVLOG(1) << ENDPOINT << "OnAckFrameEnd, start: " << start; @@ -1464,7 +1505,7 @@ bool QuicConnection::OnAckFrameEnd(QuicPacketNumber start) { } bool QuicConnection::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) { - QUIC_BUG_IF(!connected_) + QUIC_BUG_IF(quic_bug_12714_8, !connected_) << "Processing STOP_WAITING frame when connection is closed. Last frame: " << most_recent_frame_type_; @@ -1502,7 +1543,7 @@ bool QuicConnection::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) { } bool QuicConnection::OnPaddingFrame(const QuicPaddingFrame& frame) { - QUIC_BUG_IF(!connected_) + QUIC_BUG_IF(quic_bug_12714_9, !connected_) << "Processing PADDING frame when connection is closed. Last frame: " << most_recent_frame_type_; if (!UpdatePacketContent(PADDING_FRAME)) { @@ -1516,7 +1557,7 @@ bool QuicConnection::OnPaddingFrame(const QuicPaddingFrame& frame) { } bool QuicConnection::OnPingFrame(const QuicPingFrame& frame) { - QUIC_BUG_IF(!connected_) + QUIC_BUG_IF(quic_bug_12714_10, !connected_) << "Processing PING frame when connection is closed. Last frame: " << most_recent_frame_type_; if (!UpdatePacketContent(PING_FRAME)) { @@ -1560,7 +1601,7 @@ const char* QuicConnection::ValidateStopWaitingFrame( } bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) { - QUIC_BUG_IF(!connected_) + QUIC_BUG_IF(quic_bug_12714_11, !connected_) << "Processing RST_STREAM frame when connection is closed. Last frame: " << most_recent_frame_type_; @@ -1583,7 +1624,7 @@ bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) { } bool QuicConnection::OnStopSendingFrame(const QuicStopSendingFrame& frame) { - QUIC_BUG_IF(!connected_) + QUIC_BUG_IF(quic_bug_12714_12, !connected_) << "Processing STOP_SENDING frame when connection is closed. Last frame: " << most_recent_frame_type_; @@ -1623,18 +1664,20 @@ class ReversePathValidationContext : public QuicPathValidationContext { }; bool QuicConnection::OnPathChallengeFrame(const QuicPathChallengeFrame& frame) { - QUIC_BUG_IF(!connected_) << "Processing PATH_CHALLENGE frame when connection " - "is closed. Last frame: " - << most_recent_frame_type_; + QUIC_BUG_IF(quic_bug_10511_8, !connected_) + << "Processing PATH_CHALLENGE frame when connection " + "is closed. Last frame: " + << most_recent_frame_type_; if (has_path_challenge_in_current_packet_) { QUICHE_DCHECK(send_path_response_); - QUIC_RELOADABLE_FLAG_COUNT_N(quic_send_path_response, 2, 5); + QUIC_RELOADABLE_FLAG_COUNT_N(quic_send_path_response2, 2, 5); // Only respond to the 1st PATH_CHALLENGE in the packet. return true; } if (!validate_client_addresses_) { return OnPathChallengeFrameInternal(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 @@ -1668,7 +1711,7 @@ bool QuicConnection::OnPathChallengeFrameInternal( MaybeUpdateAckTimeout(); return true; } - QUIC_RELOADABLE_FLAG_COUNT_N(quic_send_path_response, 3, 5); + QUIC_RELOADABLE_FLAG_COUNT_N(quic_send_path_response2, 3, 5); has_path_challenge_in_current_packet_ = true; MaybeUpdateAckTimeout(); // Queue or send PATH_RESPONSE. Send PATH_RESPONSE to the source address of @@ -1688,9 +1731,10 @@ bool QuicConnection::OnPathChallengeFrameInternal( } bool QuicConnection::OnPathResponseFrame(const QuicPathResponseFrame& frame) { - QUIC_BUG_IF(!connected_) << "Processing PATH_RESPONSE frame when connection " - "is closed. Last frame: " - << most_recent_frame_type_; + QUIC_BUG_IF(quic_bug_10511_9, !connected_) + << "Processing PATH_RESPONSE frame when connection " + "is closed. Last frame: " + << most_recent_frame_type_; if (!UpdatePacketContent(PATH_RESPONSE_FRAME)) { return false; } @@ -1699,6 +1743,7 @@ bool QuicConnection::OnPathResponseFrame(const QuicPathResponseFrame& frame) { } MaybeUpdateAckTimeout(); if (use_path_validator_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_pass_path_response_to_validator, 1, 4); path_validator_.OnPathResponse(frame.data_buffer, last_packet_destination_address_); } else { @@ -1715,9 +1760,10 @@ bool QuicConnection::OnPathResponseFrame(const QuicPathResponseFrame& frame) { bool QuicConnection::OnConnectionCloseFrame( const QuicConnectionCloseFrame& frame) { - QUIC_BUG_IF(!connected_) << "Processing CONNECTION_CLOSE frame when " - "connection is closed. Last frame: " - << most_recent_frame_type_; + QUIC_BUG_IF(quic_bug_10511_10, !connected_) + << "Processing CONNECTION_CLOSE frame when " + "connection is closed. Last frame: " + << most_recent_frame_type_; // Since a connection close frame was received, this is not a connectivity // probe. A probe only contains a PING and full padding. @@ -1765,7 +1811,7 @@ bool QuicConnection::OnConnectionCloseFrame( } bool QuicConnection::OnMaxStreamsFrame(const QuicMaxStreamsFrame& frame) { - QUIC_BUG_IF(!connected_) + QUIC_BUG_IF(quic_bug_12714_13, !connected_) << "Processing MAX_STREAMS frame when connection is closed. Last frame: " << most_recent_frame_type_; if (!UpdatePacketContent(MAX_STREAMS_FRAME)) { @@ -1780,9 +1826,10 @@ bool QuicConnection::OnMaxStreamsFrame(const QuicMaxStreamsFrame& frame) { bool QuicConnection::OnStreamsBlockedFrame( const QuicStreamsBlockedFrame& frame) { - QUIC_BUG_IF(!connected_) << "Processing STREAMS_BLOCKED frame when " - "connection is closed. Last frame: " - << most_recent_frame_type_; + QUIC_BUG_IF(quic_bug_10511_11, !connected_) + << "Processing STREAMS_BLOCKED frame when " + "connection is closed. Last frame: " + << most_recent_frame_type_; if (!UpdatePacketContent(STREAMS_BLOCKED_FRAME)) { return false; } @@ -1794,7 +1841,7 @@ bool QuicConnection::OnStreamsBlockedFrame( } bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) { - QUIC_BUG_IF(!connected_) + QUIC_BUG_IF(quic_bug_12714_14, !connected_) << "Processing GOAWAY frame when connection is closed. Last frame: " << most_recent_frame_type_; @@ -1817,9 +1864,10 @@ bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) { } bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { - QUIC_BUG_IF(!connected_) << "Processing WINDOW_UPDATE frame when connection " - "is closed. Last frame: " - << most_recent_frame_type_; + QUIC_BUG_IF(quic_bug_10511_12, !connected_) + << "Processing WINDOW_UPDATE frame when connection " + "is closed. Last frame: " + << most_recent_frame_type_; // Since a window update frame was received, this is not a connectivity probe. // A probe only contains a PING and full padding. @@ -1837,11 +1885,34 @@ bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { return connected_; } +bool QuicConnection::OnNewConnectionIdFrameInner( + const QuicNewConnectionIdFrame& frame) { + QUICHE_DCHECK(support_multiple_connection_ids_); + if (peer_issued_cid_manager_ == nullptr) { + CloseConnection( + IETF_QUIC_PROTOCOL_VIOLATION, + "Receives NEW_CONNECTION_ID while peer uses zero length connection ID", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + std::string error_detail; + QuicErrorCode error = + peer_issued_cid_manager_->OnNewConnectionIdFrame(frame, &error_detail); + if (error != QUIC_NO_ERROR) { + CloseConnection(error, error_detail, + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_support_multiple_cids_v2, 1, 2); + return true; +} + bool QuicConnection::OnNewConnectionIdFrame( const QuicNewConnectionIdFrame& frame) { - QUIC_BUG_IF(!connected_) << "Processing NEW_CONNECTION_ID frame when " - "connection is closed. Last frame: " - << most_recent_frame_type_; + QUIC_BUG_IF(quic_bug_10511_13, !connected_) + << "Processing NEW_CONNECTION_ID frame when " + "connection is closed. Last frame: " + << most_recent_frame_type_; if (!UpdatePacketContent(NEW_CONNECTION_ID_FRAME)) { return false; } @@ -1849,14 +1920,18 @@ bool QuicConnection::OnNewConnectionIdFrame( if (debug_visitor_ != nullptr) { debug_visitor_->OnNewConnectionIdFrame(frame); } - return true; + if (!support_multiple_connection_ids_) { + return true; + } + return OnNewConnectionIdFrameInner(frame); } bool QuicConnection::OnRetireConnectionIdFrame( const QuicRetireConnectionIdFrame& frame) { - QUIC_BUG_IF(!connected_) << "Processing RETIRE_CONNECTION_ID frame when " - "connection is closed. Last frame: " - << most_recent_frame_type_; + QUIC_BUG_IF(quic_bug_10511_14, !connected_) + << "Processing RETIRE_CONNECTION_ID frame when " + "connection is closed. Last frame: " + << most_recent_frame_type_; if (!UpdatePacketContent(RETIRE_CONNECTION_ID_FRAME)) { return false; } @@ -1864,11 +1939,30 @@ bool QuicConnection::OnRetireConnectionIdFrame( if (debug_visitor_ != nullptr) { debug_visitor_->OnRetireConnectionIdFrame(frame); } + if (!support_multiple_connection_ids_) { + return true; + } + if (self_issued_cid_manager_ == nullptr) { + CloseConnection( + IETF_QUIC_PROTOCOL_VIOLATION, + "Receives RETIRE_CONNECTION_ID while new connection ID is never issued", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + std::string error_detail; + QuicErrorCode error = self_issued_cid_manager_->OnRetireConnectionIdFrame( + frame, sent_packet_manager_.GetPtoDelay(), &error_detail); + if (error != QUIC_NO_ERROR) { + CloseConnection(error, error_detail, + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return false; + } + QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_support_multiple_cids_v2, 2, 2); return true; } bool QuicConnection::OnNewTokenFrame(const QuicNewTokenFrame& frame) { - QUIC_BUG_IF(!connected_) + QUIC_BUG_IF(quic_bug_12714_15, !connected_) << "Processing NEW_TOKEN frame when connection is closed. Last frame: " << most_recent_frame_type_; if (!UpdatePacketContent(NEW_TOKEN_FRAME)) { @@ -1893,7 +1987,7 @@ bool QuicConnection::OnNewTokenFrame(const QuicNewTokenFrame& frame) { } bool QuicConnection::OnMessageFrame(const QuicMessageFrame& frame) { - QUIC_BUG_IF(!connected_) + QUIC_BUG_IF(quic_bug_12714_16, !connected_) << "Processing MESSAGE frame when connection is closed. Last frame: " << most_recent_frame_type_; @@ -1913,9 +2007,10 @@ bool QuicConnection::OnMessageFrame(const QuicMessageFrame& frame) { } bool QuicConnection::OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) { - QUIC_BUG_IF(!connected_) << "Processing HANDSHAKE_DONE frame when connection " - "is closed. Last frame: " - << most_recent_frame_type_; + QUIC_BUG_IF(quic_bug_10511_15, !connected_) + << "Processing HANDSHAKE_DONE frame when connection " + "is closed. Last frame: " + << most_recent_frame_type_; if (!version().UsesTls()) { CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, "Handshake done frame is unsupported", @@ -1945,9 +2040,10 @@ bool QuicConnection::OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) { } bool QuicConnection::OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) { - QUIC_BUG_IF(!connected_) << "Processing ACK_FREQUENCY frame when connection " - "is closed. Last frame: " - << most_recent_frame_type_; + QUIC_BUG_IF(quic_bug_10511_16, !connected_) + << "Processing ACK_FREQUENCY frame when connection " + "is closed. Last frame: " + << most_recent_frame_type_; if (debug_visitor_ != nullptr) { debug_visitor_->OnAckFrequencyFrame(frame); } @@ -1973,7 +2069,7 @@ bool QuicConnection::OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) { } bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) { - QUIC_BUG_IF(!connected_) + QUIC_BUG_IF(quic_bug_12714_17, !connected_) << "Processing BLOCKED frame when connection is closed. Last frame was " << most_recent_frame_type_; @@ -2098,7 +2194,8 @@ void QuicConnection::MaybeRespondToConnectivityProbingOrMigration() { } } -bool QuicConnection::IsValidStatelessResetToken(QuicUint128 token) const { +bool QuicConnection::IsValidStatelessResetToken( + const StatelessResetToken& token) const { return stateless_reset_token_received_ && token == received_stateless_reset_token_; } @@ -2111,17 +2208,19 @@ void QuicConnection::OnAuthenticatedIetfStatelessResetPacket( QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT); if (use_path_validator_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_pass_path_response_to_validator, 4, 4); if (!IsDefaultPath(last_packet_destination_address_, last_packet_source_address_)) { // This packet is received on a probing path. Do not close connection. if (IsAlternativePath(last_packet_destination_address_, GetEffectivePeerAddressFromCurrentPacket())) { - QUIC_BUG_IF(alternative_path_.validated) + QUIC_BUG_IF(quic_bug_12714_18, alternative_path_.validated) << "STATELESS_RESET received on alternate path after it's " "validated."; path_validator_.CancelPathValidation(); } else { - QUIC_BUG << "Received Stateless Reset on unknown socket."; + QUIC_BUG(quic_bug_10511_17) + << "Received Stateless Reset on unknown socket."; } return; } @@ -2255,7 +2354,7 @@ void QuicConnection::MaybeSendInResponseToPacket() { if (defer_send_in_response_to_packets_) { send_alarm_->Update(clock_->ApproximateNow(), QuicTime::Delta::Zero()); } else { - WriteAndBundleAcksIfNotBlocked(); + WriteIfNotBlocked(); } } @@ -2264,9 +2363,9 @@ void QuicConnection::MaybeActivateLegacyVersionEncapsulation() { return; } QUICHE_DCHECK(!legacy_version_encapsulation_in_progress_); - QUIC_BUG_IF(!packet_creator_.CanSetMaxPacketLength()) + QUIC_BUG_IF(quic_bug_12714_19, !packet_creator_.CanSetMaxPacketLength()) << "Cannot activate Legacy Version Encapsulation mid-packet"; - QUIC_BUG_IF(coalesced_packet_.length() != 0u) + QUIC_BUG_IF(quic_bug_12714_20, coalesced_packet_.length() != 0u) << "Cannot activate Legacy Version Encapsulation mid-coalesced-packet"; legacy_version_encapsulation_in_progress_ = true; MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); @@ -2286,7 +2385,7 @@ size_t QuicConnection::SendCryptoData(EncryptionLevel level, size_t write_length, QuicStreamOffset offset) { if (write_length == 0) { - QUIC_BUG << "Attempt to send empty crypto frame"; + QUIC_BUG(quic_bug_10511_18) << "Attempt to send empty crypto frame"; return 0; } if (level == ENCRYPTION_INITIAL) { @@ -2307,7 +2406,7 @@ QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id, QuicStreamOffset offset, StreamSendingState state) { if (state == NO_FIN && write_length == 0) { - QUIC_BUG << "Attempt to send empty stream frame"; + QUIC_BUG(quic_bug_10511_19) << "Attempt to send empty stream frame"; return QuicConsumedData(0, false); } @@ -2315,6 +2414,21 @@ QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id, QuicUtils::IsCryptoStreamId(transport_version(), id)) { MaybeActivateLegacyVersionEncapsulation(); } + if (GetQuicReloadableFlag(quic_preempt_stream_data_with_handshake_packet) && + perspective_ == Perspective::IS_SERVER && + version().CanSendCoalescedPackets() && !IsHandshakeConfirmed()) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_preempt_stream_data_with_handshake_packet, + 1, 2); + if (coalesced_packet_.ContainsPacketOfEncryptionLevel(ENCRYPTION_INITIAL) && + coalesced_packet_.NumberOfPackets() == 1u) { + // Handshake is not confirmed yet, if there is only an initial packet in + // the coalescer, try to bundle an ENCRYPTION_HANDSHAKE packet before + // sending stream data. + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_preempt_stream_data_with_handshake_packet, 2, 2); + sent_packet_manager_.RetransmitDataOfSpaceIfAny(HANDSHAKE_DATA); + } + } QuicConsumedData consumed_data(0, false); { // Opportunistically bundle an ack with every outgoing packet. @@ -2510,9 +2624,10 @@ void QuicConnection::MaybeUpdatePacketCreatorMaxPacketLengthAndPadding() { QuicLegacyVersionEncapsulator::GetMinimumOverhead( legacy_version_encapsulation_sni_); if (max_packet_length < minimum_overhead) { - QUIC_BUG << "Cannot apply Legacy Version Encapsulation overhead because " - << "max_packet_length " << max_packet_length - << " < minimum_overhead " << minimum_overhead; + QUIC_BUG(quic_bug_10511_20) + << "Cannot apply Legacy Version Encapsulation overhead because " + << "max_packet_length " << max_packet_length << " < minimum_overhead " + << minimum_overhead; legacy_version_encapsulation_in_progress_ = false; legacy_version_encapsulation_enabled_ = false; MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); @@ -2533,7 +2648,7 @@ void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address, << " bytes:" << std::endl << quiche::QuicheTextUtils::HexDump( absl::string_view(packet.data(), packet.length())); - QUIC_BUG_IF(current_packet_data_ != nullptr) + QUIC_BUG_IF(quic_bug_12714_21, current_packet_data_ != nullptr) << "ProcessUdpPacket must not be called while processing a packet."; if (debug_visitor_ != nullptr) { debug_visitor_->OnPacketReceived(self_address, peer_address, packet); @@ -2581,10 +2696,10 @@ void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address, // Ensure the time coming from the packet reader is within 2 minutes of now. if (std::abs((packet.receipt_time() - clock_->ApproximateNow()).ToSeconds()) > 2 * 60) { - QUIC_BUG << "Packet receipt time:" - << packet.receipt_time().ToDebuggingValue() - << " too far from current time:" - << clock_->ApproximateNow().ToDebuggingValue(); + QUIC_BUG(quic_bug_10511_21) + << "Packet receipt time:" << packet.receipt_time().ToDebuggingValue() + << " too far from current time:" + << clock_->ApproximateNow().ToDebuggingValue(); } time_of_last_received_packet_ = packet.receipt_time(); QUIC_DVLOG(1) << ENDPOINT << "time of last received packet: " @@ -2623,9 +2738,12 @@ void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address, } } - MaybeProcessCoalescedPackets(); - MaybeProcessUndecryptablePackets(); - MaybeSendInResponseToPacket(); + const bool processed = MaybeProcessCoalescedPackets(); + if (!donot_write_mid_packet_processing_ || !processed) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_donot_write_mid_packet_processing, 3, 3); + MaybeProcessUndecryptablePackets(); + MaybeSendInResponseToPacket(); + } SetPingAlarm(); current_packet_data_ = nullptr; is_current_packet_connectivity_probing_ = false; @@ -2643,13 +2761,12 @@ void QuicConnection::OnCanWrite() { if (writer_->IsWriteBlocked()) { const std::string error_details = "Writer is blocked while calling OnCanWrite."; - QUIC_BUG << ENDPOINT << error_details; + QUIC_BUG(quic_bug_10511_22) << ENDPOINT << error_details; CloseConnection(QUIC_INTERNAL_ERROR, error_details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return; } - // Add a flusher to ensure the connection is marked app-limited. ScopedPacketFlusher flusher(this); WriteQueuedPackets(); @@ -2669,7 +2786,7 @@ void QuicConnection::OnCanWrite() { // TODO(danzh) PATH_RESPONSE is of more interest to the peer than ACK, // evaluate if it's worth to send them before sending ACKs. while (!pending_path_challenge_payloads_.empty()) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_send_path_response, 4, 5); + QUIC_RELOADABLE_FLAG_COUNT_N(quic_send_path_response2, 4, 5); const PendingPathChallenge& pending_path_challenge = pending_path_challenge_payloads_.front(); if (!SendPathResponse(pending_path_challenge.received_path_challenge, @@ -2678,48 +2795,76 @@ void QuicConnection::OnCanWrite() { } pending_path_challenge_payloads_.pop_front(); } - WriteNewData(); -} -void QuicConnection::WriteNewData() { // Sending queued packets may have caused the socket to become write blocked, - // or the congestion manager to prohibit sending. If we've sent everything - // we had queued and we're still not blocked, let the visitor know it can - // write more. + // or the congestion manager to prohibit sending. if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) { return; } - { - ScopedPacketFlusher flusher(this); - visitor_->OnCanWrite(); - } + // Tell the session it can write. + visitor_->OnCanWrite(); // After the visitor writes, it may have caused the socket to become write // blocked or the congestion manager to prohibit sending, so check again. if (visitor_->WillingAndAbleToWrite() && !send_alarm_->IsSet() && CanWrite(HAS_RETRANSMITTABLE_DATA)) { - // We're not write blocked, but some stream didn't write out all of its - // bytes. Register for 'immediate' resumption so we'll keep writing after - // other connections and events have had a chance to use the thread. + // We're not write blocked, but some data wasn't written. Register for + // 'immediate' resumption so we'll keep writing after other connections. send_alarm_->Set(clock_->ApproximateNow()); } } void QuicConnection::WriteIfNotBlocked() { + if (donot_write_mid_packet_processing_ && framer().is_processing_packet()) { + QUIC_BUG(connection_write_mid_packet_processing) + << ENDPOINT << "Tried to write in mid of packet processing"; + return; + } if (!HandleWriteBlocked()) { OnCanWrite(); } } -void QuicConnection::WriteAndBundleAcksIfNotBlocked() { - if (!HandleWriteBlocked()) { - ScopedPacketFlusher flusher(this); - WriteIfNotBlocked(); +void QuicConnection::ReplaceInitialServerConnectionId( + const QuicConnectionId& new_server_connection_id) { + QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT); + if (support_multiple_connection_ids_) { + if (new_server_connection_id.IsEmpty()) { + peer_issued_cid_manager_ = nullptr; + } else { + if (peer_issued_cid_manager_ != nullptr) { + QUIC_BUG_IF(quic_bug_12714_22, + !peer_issued_cid_manager_->IsConnectionIdActive( + server_connection_id_)) + << "Connection ID replaced header is no longer active. old id: " + << server_connection_id_ << " new_id: " << new_server_connection_id; + peer_issued_cid_manager_->ReplaceConnectionId(server_connection_id_, + new_server_connection_id); + } else { + peer_issued_cid_manager_ = + std::make_unique<QuicPeerIssuedConnectionIdManager>( + kMinNumOfActiveConnectionIds, new_server_connection_id, clock_, + alarm_factory_, this); + } + } } + server_connection_id_ = new_server_connection_id; + packet_creator_.SetServerConnectionId(server_connection_id_); } bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) { + if (perspective_ == Perspective::IS_CLIENT && version().HasIetfQuicFrames() && + direct_peer_address_.IsInitialized() && + last_packet_source_address_.IsInitialized() && + direct_peer_address_ != last_packet_source_address_ && + !visitor_->IsKnownServerAddress(last_packet_source_address_)) { + // TODO(haoyuewang) Revisit this when preferred_address transport parameter + // is used on the client side. + // Discard packets received from unseen server addresses. + return false; + } + if (perspective_ == Perspective::IS_SERVER && default_path_.self_address.IsInitialized() && last_packet_destination_address_.IsInitialized() && @@ -2742,14 +2887,21 @@ bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) { if (PacketCanReplaceConnectionId(header, perspective_) && server_connection_id_ != header.source_connection_id) { + QUICHE_DCHECK_EQ(header.long_packet_type, INITIAL); + if (server_connection_id_replaced_by_initial_) { + QUIC_DLOG(ERROR) << ENDPOINT << "Refusing to replace connection ID " + << server_connection_id_ << " with " + << header.source_connection_id; + return false; + } + server_connection_id_replaced_by_initial_ = true; QUIC_DLOG(INFO) << ENDPOINT << "Replacing connection ID " << server_connection_id_ << " with " << header.source_connection_id; if (!original_destination_connection_id_.has_value()) { original_destination_connection_id_ = server_connection_id_; } - server_connection_id_ = header.source_connection_id; - packet_creator_.SetServerConnectionId(server_connection_id_); + ReplaceInitialServerConnectionId(header.source_connection_id); } if (!ValidateReceivedPacketNumber(header.packet_number)) { @@ -2926,7 +3078,7 @@ const QuicFrames QuicConnection::MaybeBundleAckOpportunistically() { QUIC_DVLOG(1) << ENDPOINT << "Bundle an ACK opportunistically"; QuicFrame updated_ack_frame = GetUpdatedAckFrame(); - QUIC_BUG_IF(updated_ack_frame.ack_frame->packets.Empty()) + QUIC_BUG_IF(quic_bug_12714_23, updated_ack_frame.ack_frame->packets.Empty()) << ENDPOINT << "Attempted to opportunistically bundle an empty " << encryption_level_ << " ACK, " << (has_pending_ack ? "" : "!") << "has_pending_ack, stop_waiting_count_ " << stop_waiting_count_; @@ -3025,8 +3177,9 @@ QuicTime QuicConnection::CalculatePacketSentTime() { bool QuicConnection::WritePacket(SerializedPacket* packet) { if (sent_packet_manager_.GetLargestSentPacket().IsInitialized() && packet->packet_number < sent_packet_manager_.GetLargestSentPacket()) { - QUIC_BUG << "Attempt to write packet:" << packet->packet_number - << " after:" << sent_packet_manager_.GetLargestSentPacket(); + QUIC_BUG(quic_bug_10511_23) + << "Attempt to write packet:" << packet->packet_number + << " after:" << sent_packet_manager_.GetLargestSentPacket(); CloseConnection(QUIC_INTERNAL_ERROR, "Packet written out of order.", ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return true; @@ -3095,7 +3248,8 @@ bool QuicConnection::WritePacket(SerializedPacket* packet) { ++stats_.packets_discarded; return true; case COALESCE: - QUIC_BUG_IF(!version().CanSendCoalescedPackets() || coalescing_done_); + QUIC_BUG_IF(quic_bug_12714_24, + !version().CanSendCoalescedPackets() || coalescing_done_); if (!coalesced_packet_.MaybeCoalescePacket( *packet, self_address(), send_to_address, helper_->GetStreamSendBufferAllocator(), @@ -3180,10 +3334,10 @@ bool QuicConnection::WritePacket(SerializedPacket* packet) { << quiche::QuicheTextUtils::HexDump(absl::string_view( packet->encrypted_buffer, encrypted_length)); } else { - QUIC_BUG << ENDPOINT - << "Failed to perform Legacy Version Encapsulation on " - << packet->encryption_level << " packet number " - << packet_number << " of length " << encrypted_length; + QUIC_BUG(quic_bug_10511_24) + << ENDPOINT << "Failed to perform Legacy Version Encapsulation on " + << packet->encryption_level << " packet number " << packet_number + << " of length " << encrypted_length; } if (!buffered_packets_.empty() || HandleWriteBlocked()) { // Buffer the packet. @@ -3239,6 +3393,7 @@ bool QuicConnection::WritePacket(SerializedPacket* packet) { return true; } if (use_path_validator_ && !send_on_current_path) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_pass_path_response_to_validator, 2, 4); // Only handle MSG_TOO_BIG as error on current path. return true; } @@ -3316,15 +3471,17 @@ bool QuicConnection::WritePacket(SerializedPacket* packet) { const bool in_flight = sent_packet_manager_.OnPacketSent( packet, packet_send_time, packet->transmission_type, IsRetransmittable(*packet), /*measure_rtt=*/send_on_current_path); - QUIC_BUG_IF(default_enable_5rto_blackhole_detection_ && - blackhole_detector_.IsDetectionInProgress() && - !sent_packet_manager_.HasInFlightPackets()) + QUIC_BUG_IF(quic_bug_12714_25, + default_enable_5rto_blackhole_detection_ && + blackhole_detector_.IsDetectionInProgress() && + !sent_packet_manager_.HasInFlightPackets()) << ENDPOINT << "Trying to start blackhole detection without no bytes in flight"; if (debug_visitor_ != nullptr) { if (sent_packet_manager_.unacked_packets().empty()) { - QUIC_BUG << "Unacked map is empty right after packet is sent"; + QUIC_BUG(quic_bug_10511_25) + << "Unacked map is empty right after packet is sent"; } else { debug_visitor_->OnPacketSent( packet->packet_number, packet->encrypted_length, @@ -3381,13 +3538,14 @@ bool QuicConnection::MaybeHandleAeadConfidentialityLimits( } if (packet.encryption_level != ENCRYPTION_FORWARD_SECURE) { - QUIC_BUG + QUIC_BUG(quic_bug_12714_26) << "MaybeHandleAeadConfidentialityLimits called on non 1-RTT packet"; return false; } if (!lowest_packet_sent_in_current_key_phase_.IsInitialized()) { - QUIC_BUG << "lowest_packet_sent_in_current_key_phase_ must be initialized " - "before calling MaybeHandleAeadConfidentialityLimits"; + QUIC_BUG(quic_bug_10511_26) + << "lowest_packet_sent_in_current_key_phase_ must be initialized " + "before calling MaybeHandleAeadConfidentialityLimits"; return false; } @@ -3400,7 +3558,7 @@ bool QuicConnection::MaybeHandleAeadConfidentialityLimits( absl::StrCat("packet_number(", packet.packet_number.ToString(), ") < lowest_packet_sent_in_current_key_phase_ (", lowest_packet_sent_in_current_key_phase_.ToString(), ")"); - QUIC_BUG << error_details; + QUIC_BUG(quic_bug_10511_27) << error_details; CloseConnection(QUIC_INTERNAL_ERROR, error_details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return true; @@ -3666,6 +3824,27 @@ void QuicConnection::OnPathMtuIncreased(QuicPacketLength packet_size) { } } +std::unique_ptr<QuicSelfIssuedConnectionIdManager> +QuicConnection::MakeSelfIssuedConnectionIdManager() { + QUICHE_DCHECK((perspective_ == Perspective::IS_CLIENT && + !client_connection_id_.IsEmpty()) || + (perspective_ == Perspective::IS_SERVER && + !server_connection_id_.IsEmpty())); + return std::make_unique<QuicSelfIssuedConnectionIdManager>( + kMinNumOfActiveConnectionIds, + perspective_ == Perspective::IS_CLIENT ? client_connection_id_ + : server_connection_id_, + clock_, alarm_factory_, this); +} + +void QuicConnection::MaybeSendConnectionIdToClient() { + if (perspective_ == Perspective::IS_CLIENT) { + return; + } + QUICHE_DCHECK(self_issued_cid_manager_ != nullptr); + self_issued_cid_manager_->MaybeSendNewConnectionIds(); +} + void QuicConnection::OnHandshakeComplete() { sent_packet_manager_.SetHandshakeConfirmed(); if (send_ack_frequency_on_handshake_completion_ && @@ -3831,10 +4010,11 @@ void QuicConnection::OnRetransmissionTimeout() { // When timer fires in TLP/RTO/PTO mode, ensure 1) at least one packet is // created, or there is data to send and available credit (such that // packets will be sent eventually). - QUIC_BUG_IF(packet_creator_.packet_number() == - previous_created_packet_number && - (!visitor_->WillingAndAbleToWrite() || - sent_packet_manager_.pending_timer_transmission_count() == 0u)) + QUIC_BUG_IF( + quic_bug_12714_27, + packet_creator_.packet_number() == previous_created_packet_number && + (!visitor_->WillingAndAbleToWrite() || + sent_packet_manager_.pending_timer_transmission_count() == 0u)) << "retransmission_mode: " << retransmission_mode << ", packet_number: " << packet_creator_.packet_number() << ", session has data to write: " << visitor_->WillingAndAbleToWrite() @@ -3878,7 +4058,7 @@ void QuicConnection::SetDefaultEncryptionLevel(EncryptionLevel level) { } encryption_level_ = level; packet_creator_.set_encryption_level(level); - QUIC_BUG_IF(!framer_.HasEncrypterOfEncryptionLevel(level)) + QUIC_BUG_IF(quic_bug_12714_28, !framer_.HasEncrypterOfEncryptionLevel(level)) << ENDPOINT << "Trying to set encryption level to " << EncryptionLevelToString(level) << " while the key is missing"; @@ -3955,7 +4135,7 @@ QuicPacketCount QuicConnection::PotentialPeerKeyUpdateAttemptCount() const { bool QuicConnection::InitiateKeyUpdate(KeyUpdateReason reason) { QUIC_DLOG(INFO) << ENDPOINT << "InitiateKeyUpdate"; if (!IsKeyUpdateAllowed()) { - QUIC_BUG << "key update not allowed"; + QUIC_BUG(quic_bug_10511_28) << "key update not allowed"; return false; } return framer_.DoKeyUpdate(reason); @@ -4052,14 +4232,14 @@ void QuicConnection::QueueCoalescedPacket(const QuicEncryptedPacket& packet) { ++stats_.num_coalesced_packets_received; } -void QuicConnection::MaybeProcessCoalescedPackets() { +bool QuicConnection::MaybeProcessCoalescedPackets() { bool processed = false; while (connected_ && !received_coalesced_packets_.empty()) { // Making sure there are no pending frames when processing the next // coalesced packet because the queued ack frame may change. packet_creator_.FlushCurrentPacket(); if (!connected_) { - return; + return processed; } std::unique_ptr<QuicEncryptedPacket> packet = @@ -4077,7 +4257,13 @@ void QuicConnection::MaybeProcessCoalescedPackets() { } if (processed) { MaybeProcessUndecryptablePackets(); + if (donot_write_mid_packet_processing_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_donot_write_mid_packet_processing, 2, + 3); + MaybeSendInResponseToPacket(); + } } + return processed; } void QuicConnection::CloseConnection( @@ -4258,8 +4444,11 @@ void QuicConnection::TearDownLocalConnectionState( // connection is closed. CancelAllAlarms(); if (use_path_validator_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_pass_path_response_to_validator, 3, 4); CancelPathValidation(); } + peer_issued_cid_manager_.reset(); + self_issued_cid_manager_.reset(); } void QuicConnection::CancelAllAlarms() { @@ -4292,7 +4481,7 @@ bool QuicConnection::HasQueuedData() const { void QuicConnection::SetNetworkTimeouts(QuicTime::Delta handshake_timeout, QuicTime::Delta idle_timeout) { - QUIC_BUG_IF(idle_timeout > handshake_timeout) + QUIC_BUG_IF(quic_bug_12714_29, idle_timeout > handshake_timeout) << "idle_timeout:" << idle_timeout.ToMilliseconds() << " handshake_timeout:" << handshake_timeout.ToMilliseconds(); // Adjust the idle timeout on client and server to prevent clients from @@ -4376,7 +4565,8 @@ void QuicConnection::SetPingAlarm() { void QuicConnection::SetRetransmissionAlarm() { if (!connected_) { if (retransmission_alarm_->IsSet()) { - QUIC_BUG << ENDPOINT << "Retransmission alarm is set while disconnected"; + QUIC_BUG(quic_bug_10511_29) + << ENDPOINT << "Retransmission alarm is set while disconnected"; retransmission_alarm_->Cancel(); } return; @@ -4573,7 +4763,8 @@ void QuicConnection::SetMtuDiscoveryTarget(QuicByteCount target) { QuicByteCount QuicConnection::GetLimitedMaxPacketSize( QuicByteCount suggested_max_packet_size) { if (!peer_address().IsInitialized()) { - QUIC_BUG << "Attempted to use a connection without a valid peer address"; + QUIC_BUG(quic_bug_10511_30) + << "Attempted to use a connection without a valid peer address"; return suggested_max_packet_size; } @@ -4622,8 +4813,9 @@ bool QuicConnection::SendGenericPathProbePacket( bool is_response) { QUICHE_DCHECK(peer_address.IsInitialized()); if (!connected_) { - QUIC_BUG << "Not sending connectivity probing packet as connection is " - << "disconnected."; + QUIC_BUG(quic_bug_10511_31) + << "Not sending connectivity probing packet as connection is " + << "disconnected."; return false; } if (perspective_ == Perspective::IS_SERVER && probing_writer == nullptr) { @@ -4719,7 +4911,8 @@ bool QuicConnection::WritePacketUsingWriter( if (debug_visitor_ != nullptr) { if (sent_packet_manager_.unacked_packets().empty()) { - QUIC_BUG << "Unacked map is empty right after packet is sent"; + QUIC_BUG(quic_bug_10511_32) + << "Unacked map is empty right after packet is sent"; } else { debug_visitor_->OnPacketSent( packet->packet_number, packet->encrypted_length, @@ -4766,7 +4959,7 @@ void QuicConnection::DiscoverMtu() { void QuicConnection::OnEffectivePeerMigrationValidated() { if (active_effective_peer_migration_type_ == NO_CHANGE) { - QUIC_BUG << "No migration underway."; + QUIC_BUG(quic_bug_10511_33) << "No migration underway."; return; } highest_packet_sent_before_effective_peer_migration_.Clear(); @@ -4777,6 +4970,18 @@ void QuicConnection::OnEffectivePeerMigrationValidated() { if (!validate_client_addresses_) { return; } + QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path3, 2, 6); + if (debug_visitor_ != nullptr) { + const QuicTime now = clock_->ApproximateNow(); + if (now >= stats_.handshake_completion_time) { + debug_visitor_->OnPeerMigrationValidated( + now - stats_.handshake_completion_time); + } else { + QUIC_BUG(quic_bug_10511_34) + << "Handshake completion time is larger than current time."; + } + } + // Lift anti-amplification limit. default_path_.validated = true; alternative_path_.Clear(); @@ -4791,14 +4996,16 @@ void QuicConnection::StartEffectivePeerMigration(AddressChangeType type) { // determine whether |type| is allowed. if (!validate_client_addresses_) { if (type == NO_CHANGE) { - QUIC_BUG << "EffectivePeerMigration started without address change."; + QUIC_BUG(quic_bug_10511_35) + << "EffectivePeerMigration started without address change."; return; } - QUIC_DLOG(INFO) << ENDPOINT << "Effective peer's ip:port changed from " - << default_path_.peer_address.ToString() << " to " - << GetEffectivePeerAddressFromCurrentPacket().ToString() - << ", address change type is " << type - << ", migrating connection."; + QUIC_DLOG(INFO) + << ENDPOINT << "Effective peer's ip:port changed from " + << default_path_.peer_address.ToString() << " to " + << GetEffectivePeerAddressFromCurrentPacket().ToString() + << ", address change type is " << type + << ", migrating connection without validating new client address."; highest_packet_sent_before_effective_peer_migration_ = sent_packet_manager_.GetLargestSentPacket(); @@ -4809,9 +5016,11 @@ void QuicConnection::StartEffectivePeerMigration(AddressChangeType type) { return; } + QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path3, 3, 6); if (type == NO_CHANGE) { UpdatePeerAddress(last_packet_source_address_); - QUIC_BUG << "EffectivePeerMigration started without address change."; + QUIC_BUG(quic_bug_10511_36) + << "EffectivePeerMigration started without address change."; return; } @@ -4855,14 +5064,7 @@ void QuicConnection::StartEffectivePeerMigration(AddressChangeType type) { // congestion controller to initial state first and then change to the one // on alternative path. // TODO(danzh) combine these two steps into one after deprecating gQUIC. - previous_default_path.send_algorithm = - sent_packet_manager_.OnConnectionMigration( - /*reset_send_algorithm=*/true); - // OnConnectionMigration() might have marked in-flight packets to be - // retransmitted if there is any. - QUICHE_DCHECK(!sent_packet_manager_.HasInFlightPackets()); - // Stop detections in quiecense. - blackhole_detector_.StopDetection(); + previous_default_path.send_algorithm = OnPeerIpAddressChanged(); if (alternative_path_.peer_address.host() == current_effective_peer_address.host() && @@ -5034,8 +5236,9 @@ void QuicConnection::MaybeSendProbingRetransmissions() { } if (probing_retransmission_pending_) { - QUIC_BUG << "MaybeSendProbingRetransmissions is called while another call " - "to it is already in progress"; + QUIC_BUG(quic_bug_10511_37) + << "MaybeSendProbingRetransmissions is called while another call " + "to it is already in progress"; return; } @@ -5067,24 +5270,22 @@ void QuicConnection::CheckIfApplicationLimited() { } bool QuicConnection::UpdatePacketContent(QuicFrameType type) { - if (update_packet_content_returns_connected_) { - QUIC_RELOADABLE_FLAG_COUNT(quic_update_packet_content_returns_connected); - } most_recent_frame_type_ = type; if (version().HasIetfQuicFrames()) { if (!QuicUtils::IsProbingFrame(type)) { MaybeStartIetfPeerMigration(); - return !update_packet_content_returns_connected_ || connected_; + return connected_; } QuicSocketAddress current_effective_peer_address = GetEffectivePeerAddressFromCurrentPacket(); if (!count_bytes_on_alternative_path_separately_ || IsDefaultPath(last_packet_destination_address_, last_packet_source_address_)) { - return !update_packet_content_returns_connected_ || connected_; + return connected_; } QUIC_CODE_COUNT_N(quic_count_bytes_on_alternative_path_seperately, 3, 5); - if (type == PATH_CHALLENGE_FRAME && + if (perspective_ == Perspective::IS_SERVER && + type == PATH_CHALLENGE_FRAME && !IsAlternativePath(last_packet_destination_address_, current_effective_peer_address)) { QUIC_DVLOG(1) @@ -5095,6 +5296,7 @@ bool QuicConnection::UpdatePacketContent(QuicFrameType type) { alternative_path_ = PathState(last_packet_destination_address_, current_effective_peer_address); } else if (!default_path_.validated) { + QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path3, 4, 6); // Skip reverse path validation because either handshake hasn't // completed or the connection is validating the default path. Using // PATH_CHALLENGE to validate alternative client address before @@ -5104,9 +5306,11 @@ bool QuicConnection::UpdatePacketContent(QuicFrameType type) { // be overridden. QUIC_DVLOG(1) << "The connection hasn't finished handshake or is " "validating a recent peer address change."; - QUIC_BUG_IF(IsHandshakeConfirmed() && !alternative_path_.validated) + QUIC_BUG_IF(quic_bug_12714_30, + IsHandshakeConfirmed() && !alternative_path_.validated) << "No validated peer address to send after handshake comfirmed."; } else if (!IsReceivedPeerAddressValidated()) { + QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path3, 5, 6); // Only override alternative path state upon receiving a PATH_CHALLENGE // from an unvalidated peer address, and the connection isn't validating // a recent peer migration. @@ -5128,7 +5332,7 @@ bool QuicConnection::UpdatePacketContent(QuicFrameType type) { } } MaybeUpdateBytesReceivedFromAlternativeAddress(last_size_); - return !update_packet_content_returns_connected_ || connected_; + return connected_; } // Packet content is tracked to identify connectivity probe in non-IETF // version, where a connectivity probe is defined as @@ -5139,13 +5343,13 @@ bool QuicConnection::UpdatePacketContent(QuicFrameType type) { // We have already learned the current packet is not a connectivity // probing packet. Peer migration should have already been started earlier // if needed. - return !update_packet_content_returns_connected_ || connected_; + return connected_; } if (type == PING_FRAME) { if (current_packet_content_ == NO_FRAMES_RECEIVED) { current_packet_content_ = FIRST_FRAME_IS_PING; - return !update_packet_content_returns_connected_ || connected_; + return connected_; } } @@ -5175,7 +5379,7 @@ bool QuicConnection::UpdatePacketContent(QuicFrameType type) { << last_packet_destination_address_ << ", default path self_address :" << default_path_.self_address; } - return !update_packet_content_returns_connected_ || connected_; + return connected_; } current_packet_content_ = NOT_PADDED_PING; @@ -5189,7 +5393,7 @@ bool QuicConnection::UpdatePacketContent(QuicFrameType type) { } } current_effective_peer_migration_type_ = NO_CHANGE; - return !update_packet_content_returns_connected_ || connected_; + return connected_; } void QuicConnection::MaybeStartIetfPeerMigration() { @@ -5305,8 +5509,8 @@ MessageStatus QuicConnection::SendMessage(QuicMessageId message_id, QuicMemSliceSpan message, bool flush) { if (!VersionSupportsMessageFrames(transport_version())) { - QUIC_BUG << "MESSAGE frame is not supported for version " - << transport_version(); + QUIC_BUG(quic_bug_10511_38) + << "MESSAGE frame is not supported for version " << transport_version(); return MESSAGE_STATUS_UNSUPPORTED; } if (message.total_length() > GetCurrentLargestMessagePayload()) { @@ -5340,7 +5544,8 @@ EncryptionLevel QuicConnection::GetConnectionCloseEncryptionLevel() const { } if (IsHandshakeComplete()) { // A forward secure packet has been received. - QUIC_BUG_IF(encryption_level_ != ENCRYPTION_FORWARD_SECURE) + QUIC_BUG_IF(quic_bug_12714_31, + encryption_level_ != ENCRYPTION_FORWARD_SECURE) << ENDPOINT << "Unexpected connection close encryption level " << encryption_level_; return ENCRYPTION_FORWARD_SECURE; @@ -5385,9 +5590,10 @@ void QuicConnection::MaybeBundleCryptoDataWithAcks() { } if (!framer_.HasAnEncrypterForSpace(space)) { - QUIC_BUG << ENDPOINT - << "Try to bundle crypto with ACK with missing key of space " - << PacketNumberSpaceToString(space); + QUIC_BUG(quic_bug_10511_39) + << ENDPOINT + << "Try to bundle crypto with ACK with missing key of space " + << PacketNumberSpaceToString(space); return; } @@ -5400,7 +5606,7 @@ void QuicConnection::SendAllPendingAcks() { ack_alarm_->Cancel(); QuicTime earliest_ack_timeout = uber_received_packet_manager_.GetEarliestAckTimeout(); - QUIC_BUG_IF(!earliest_ack_timeout.IsInitialized()); + QUIC_BUG_IF(quic_bug_12714_32, !earliest_ack_timeout.IsInitialized()); MaybeBundleCryptoDataWithAcks(); earliest_ack_timeout = uber_received_packet_manager_.GetEarliestAckTimeout(); if (!earliest_ack_timeout.IsInitialized()) { @@ -5441,7 +5647,8 @@ void QuicConnection::SendAllPendingAcks() { const bool flushed = packet_creator_.FlushAckFrame(frames); if (!flushed) { // Connection is write blocked. - QUIC_BUG_IF(!writer_->IsWriteBlocked() && !LimitedByAmplificationFactor()) + QUIC_BUG_IF(quic_bug_12714_33, + !writer_->IsWriteBlocked() && !LimitedByAmplificationFactor()) << "Writer not blocked and not throttled by amplification factor, " "but ACK not flushed for packet space:" << i; @@ -5529,16 +5736,17 @@ bool QuicConnection::FlushCoalescedPacket() { return false; } if (!version().CanSendCoalescedPackets()) { - QUIC_BUG_IF(coalesced_packet_.length() > 0); + QUIC_BUG_IF(quic_bug_12714_34, coalesced_packet_.length() > 0); return true; } if (coalesced_packet_.ContainsPacketOfEncryptionLevel(ENCRYPTION_INITIAL) && !framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_INITIAL)) { // Initial packet will be re-serialized. Neuter it in case initial key has // been dropped. - QUIC_BUG << ENDPOINT - << "Coalescer contains initial packet while initial key has " - "been dropped."; + QUIC_BUG(quic_bug_10511_40) + << ENDPOINT + << "Coalescer contains initial packet while initial key has " + "been dropped."; coalesced_packet_.NeuterInitialPacket(); } if (coalesced_packet_.length() == 0) { @@ -5656,9 +5864,10 @@ void QuicConnection::OnForwardProgressMade() { // Stop detections in quiecense. blackhole_detector_.StopDetection(); } - QUIC_BUG_IF(default_enable_5rto_blackhole_detection_ && - blackhole_detector_.IsDetectionInProgress() && - !sent_packet_manager_.HasInFlightPackets()) + QUIC_BUG_IF(quic_bug_12714_35, + default_enable_5rto_blackhole_detection_ && + blackhole_detector_.IsDetectionInProgress() && + !sent_packet_manager_.HasInFlightPackets()) << ENDPOINT << "Trying to start blackhole detection without no bytes in flight"; } @@ -5756,13 +5965,26 @@ const QuicAckFrame& QuicConnection::ack_frame() const { void QuicConnection::set_client_connection_id( QuicConnectionId client_connection_id) { if (!version().SupportsClientConnectionIds()) { - QUIC_BUG_IF(!client_connection_id.IsEmpty()) + QUIC_BUG_IF(quic_bug_12714_36, !client_connection_id.IsEmpty()) << ENDPOINT << "Attempted to use client connection ID " << client_connection_id << " with unsupported version " << version(); return; } client_connection_id_ = client_connection_id; client_connection_id_is_set_ = true; + if (support_multiple_connection_ids_ && !client_connection_id_.IsEmpty()) { + if (perspective_ == Perspective::IS_SERVER) { + QUICHE_DCHECK(peer_issued_cid_manager_ == nullptr); + peer_issued_cid_manager_ = + std::make_unique<QuicPeerIssuedConnectionIdManager>( + kMinNumOfActiveConnectionIds, client_connection_id_, clock_, + alarm_factory_, this); + } else { + // Note in Chromium client, set_client_connection_id is not called and + // thus self_issued_cid_manager_ should be null. + self_issued_cid_manager_ = MakeSelfIssuedConnectionIdManager(); + } + } QUIC_DLOG(INFO) << ENDPOINT << "setting client connection ID to " << client_connection_id_ << " for connection with server connection ID " @@ -5779,9 +6001,10 @@ void QuicConnection::OnPathDegradingDetected() { void QuicConnection::OnBlackholeDetected() { if (default_enable_5rto_blackhole_detection_ && !sent_packet_manager_.HasInFlightPackets()) { - QUIC_BUG << ENDPOINT - << "Blackhole detected, but there is no bytes in flight, version: " - << version(); + QUIC_BUG(quic_bug_10511_41) + << ENDPOINT + << "Blackhole detected, but there is no bytes in flight, version: " + << version(); // Do not close connection if there is no bytes in flight. return; } @@ -5843,6 +6066,62 @@ void QuicConnection::OnIdleNetworkDetected() { idle_timeout_connection_close_behavior_); } +void QuicConnection::OnPeerIssuedConnectionIdRetired() { + QUICHE_DCHECK(peer_issued_cid_manager_ != nullptr); + QuicConnectionId* default_path_cid = perspective_ == Perspective::IS_CLIENT + ? &server_connection_id_ + : &client_connection_id_; + if (!default_path_cid->IsEmpty() && + !peer_issued_cid_manager_->IsConnectionIdActive(*default_path_cid)) { + *default_path_cid = QuicConnectionId(); + } + if (default_path_cid->IsEmpty()) { + // Try setting a new connection ID now such that subsequent + // RetireConnectionId frames can be sent on the default path. + const QuicConnectionIdData* unused_connection_id_data = + peer_issued_cid_manager_->ConsumeOneUnusedConnectionId(); + if (unused_connection_id_data != nullptr) { + *default_path_cid = unused_connection_id_data->connection_id; + received_stateless_reset_token_ = + unused_connection_id_data->stateless_reset_token; + stateless_reset_token_received_ = true; + if (perspective_ == Perspective::IS_CLIENT) { + packet_creator_.SetServerConnectionId( + unused_connection_id_data->connection_id); + } else { + packet_creator_.SetClientConnectionId( + unused_connection_id_data->connection_id); + } + } + } + + std::vector<uint64_t> retired_cid_sequence_numbers = + peer_issued_cid_manager_->ConsumeToBeRetiredConnectionIdSequenceNumbers(); + QUICHE_DCHECK(!retired_cid_sequence_numbers.empty()); + for (const auto& sequence_number : retired_cid_sequence_numbers) { + visitor_->SendRetireConnectionId(sequence_number); + } +} + +bool QuicConnection::SendNewConnectionId( + const QuicNewConnectionIdFrame& frame) { + QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER); + visitor_->SendNewConnectionId(frame); + return connected_; +} + +void QuicConnection::OnNewConnectionIdIssued( + const QuicConnectionId& connection_id) { + QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER); + visitor_->OnServerConnectionIdIssued(connection_id); +} + +void QuicConnection::OnSelfIssuedConnectionIdRetired( + const QuicConnectionId& connection_id) { + QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER); + visitor_->OnServerConnectionIdRetired(connection_id); +} + void QuicConnection::MaybeUpdateAckTimeout() { if (should_last_packet_instigate_acks_) { return; @@ -5921,6 +6200,7 @@ bool QuicConnection::SendPathChallenge( const QuicSocketAddress& /*effective_peer_address*/, QuicPacketWriter* writer) { if (writer == writer_) { + ScopedPacketFlusher flusher(this); { // It's on current path, add the PATH_CHALLENGE the same way as other // frames. @@ -6042,11 +6322,17 @@ void QuicConnection::MigratePath(const QuicSocketAddress& self_address, if (!connected_) { return; } - const bool is_port_change = - QuicUtils::DetermineAddressChangeType(default_path_.self_address, - self_address) == PORT_CHANGE && - QuicUtils::DetermineAddressChangeType(default_path_.peer_address, - peer_address) == PORT_CHANGE; + + const auto self_address_change_type = QuicUtils::DetermineAddressChangeType( + default_path_.self_address, self_address); + const auto peer_address_change_type = QuicUtils::DetermineAddressChangeType( + default_path_.peer_address, peer_address); + QUICHE_DCHECK(self_address_change_type != NO_CHANGE || + peer_address_change_type != NO_CHANGE); + const bool is_port_change = (self_address_change_type == PORT_CHANGE || + self_address_change_type == NO_CHANGE) && + (peer_address_change_type == PORT_CHANGE || + peer_address_change_type == NO_CHANGE); SetSelfAddress(self_address); UpdatePeerAddress(peer_address); SetQuicPacketWriter(writer, owns_writer); @@ -6055,7 +6341,30 @@ void QuicConnection::MigratePath(const QuicSocketAddress& self_address, std::vector<QuicConnectionId> QuicConnection::GetActiveServerConnectionIds() const { - return {server_connection_id_}; + if (!support_multiple_connection_ids_ || + self_issued_cid_manager_ == nullptr) { + return {server_connection_id_}; + } + return self_issued_cid_manager_->GetUnretiredConnectionIds(); +} + +void QuicConnection::CreateConnectionIdManager() { + if (!support_multiple_connection_ids_) { + return; + } + + if (perspective_ == Perspective::IS_CLIENT) { + if (!server_connection_id_.IsEmpty()) { + peer_issued_cid_manager_ = + std::make_unique<QuicPeerIssuedConnectionIdManager>( + kMinNumOfActiveConnectionIds, server_connection_id_, clock_, + alarm_factory_, this); + } + } else { + if (!server_connection_id_.IsEmpty()) { + self_issued_cid_manager_ = MakeSelfIssuedConnectionIdManager(); + } + } } void QuicConnection::SetUnackedMapInitialCapacity() { @@ -6241,17 +6550,15 @@ void QuicConnection::RestoreToLastValidatedPath( } // Revert congestion control context to old state. - sent_packet_manager_.OnConnectionMigration(true); - QUICHE_DCHECK(!sent_packet_manager_.HasInFlightPackets()); - // Stop detections in quiecense. - blackhole_detector_.StopDetection(); + OnPeerIpAddressChanged(); if (alternative_path_.send_algorithm != nullptr) { sent_packet_manager_.SetSendAlgorithm( alternative_path_.send_algorithm.release()); sent_packet_manager_.SetRttStats(alternative_path_.rtt_stats.value()); } else { - QUIC_BUG << "Fail to store congestion controller before migration."; + QUIC_BUG(quic_bug_10511_42) + << "Fail to store congestion controller before migration."; } UpdatePeerAddress(original_direct_peer_address); @@ -6264,5 +6571,22 @@ void QuicConnection::RestoreToLastValidatedPath( WriteIfNotBlocked(); } +std::unique_ptr<SendAlgorithmInterface> +QuicConnection::OnPeerIpAddressChanged() { + QUICHE_DCHECK(validate_client_addresses_); + std::unique_ptr<SendAlgorithmInterface> old_send_algorithm = + sent_packet_manager_.OnConnectionMigration( + /*reset_send_algorithm=*/true); + // OnConnectionMigration() should have marked in-flight packets to be + // retransmitted if there is any. + QUICHE_DCHECK(!sent_packet_manager_.HasInFlightPackets()); + // OnConnectionMigration() may have changed the retransmission timer, so + // re-arm it. + SetRetransmissionAlarm(); + // Stop detections in quiecense. + blackhole_detector_.StopDetection(); + return old_send_algorithm; +} + #undef ENDPOINT // undef for jumbo builds } // namespace quic 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 33cf33b32c0..e2ba17bd9d8 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 @@ -31,12 +31,14 @@ #include "quic/core/crypto/transport_parameters.h" #include "quic/core/frames/quic_ack_frequency_frame.h" #include "quic/core/frames/quic_max_streams_frame.h" +#include "quic/core/frames/quic_new_connection_id_frame.h" #include "quic/core/proto/cached_network_parameters_proto.h" #include "quic/core/quic_alarm.h" #include "quic/core/quic_alarm_factory.h" #include "quic/core/quic_blocked_writer_interface.h" #include "quic/core/quic_circular_deque.h" #include "quic/core/quic_connection_id.h" +#include "quic/core/quic_connection_id_manager.h" #include "quic/core/quic_connection_stats.h" #include "quic/core/quic_constants.h" #include "quic/core/quic_framer.h" @@ -164,6 +166,20 @@ class QUIC_EXPORT_PRIVATE QuicConnectionVisitorInterface { // Called when an AckFrequency frame need to be sent. virtual void SendAckFrequency(const QuicAckFrequencyFrame& frame) = 0; + // Called to send a NEW_CONNECTION_ID frame. + virtual void SendNewConnectionId(const QuicNewConnectionIdFrame& frame) = 0; + + // Called to send a RETIRE_CONNECTION_ID frame. + virtual void SendRetireConnectionId(uint64_t sequence_number) = 0; + + // Called when server starts to use a server issued connection ID. + virtual void OnServerConnectionIdIssued( + const QuicConnectionId& server_connection_id) = 0; + + // Called when server stops to use a server issued connection ID. + virtual void OnServerConnectionIdRetired( + const QuicConnectionId& server_connection_id) = 0; + // Called to ask if the visitor wants to schedule write resumption as it both // has pending data to write, and is able to write (e.g. based on flow control // limits). @@ -223,6 +239,9 @@ class QUIC_EXPORT_PRIVATE QuicConnectionVisitorInterface { // Called by the server to send another token. // Return false if the crypto stream fail to generate one. virtual void MaybeSendAddressToken() = 0; + + // Whether the server address is known to the connection. + virtual bool IsKnownServerAddress(const QuicSocketAddress& address) const = 0; }; // Interface which gets callbacks from the QuicConnection at interesting @@ -414,6 +433,9 @@ class QUIC_EXPORT_PRIVATE QuicConnectionDebugVisitor // Called on peer address change. virtual void OnPeerAddressChange(AddressChangeType /*type*/, QuicTime::Delta /*connection_time*/) {} + + // Called after peer migration is validated. + virtual void OnPeerMigrationValidated(QuicTime::Delta /*connection_time*/) {} }; class QUIC_EXPORT_PRIVATE QuicConnectionHelperInterface { @@ -437,7 +459,8 @@ class QUIC_EXPORT_PRIVATE QuicConnection public QuicSentPacketManager::NetworkChangeVisitor, public QuicNetworkBlackholeDetector::Delegate, public QuicIdleNetworkDetector::Delegate, - public QuicPathValidator::SendDelegate { + public QuicPathValidator::SendDelegate, + public QuicConnectionIdManagerVisitorInterface { public: // Constructs a new QuicConnection for |connection_id| and // |initial_peer_address| using |writer| to write packets. |owns_writer| @@ -564,6 +587,7 @@ class QUIC_EXPORT_PRIVATE QuicConnection } // Called when the caller thinks it's worth a try to write. + // TODO(fayang): consider unifying this with QuicSession::OnCanWrite. virtual void OnCanWrite(); // Called when an error occurs while attempting to write a packet to the @@ -576,10 +600,6 @@ class QUIC_EXPORT_PRIVATE QuicConnection // If the socket is not blocked, writes queued packets. void WriteIfNotBlocked(); - // If the socket is not blocked, writes queued packets and bundles any pending - // ACKs. - void WriteAndBundleAcksIfNotBlocked(); - // Set the packet writer. void SetQuicPacketWriter(QuicPacketWriter* writer, bool owns_writer) { QUICHE_DCHECK(writer != nullptr); @@ -665,7 +685,8 @@ class QUIC_EXPORT_PRIVATE QuicConnection bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) override; bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) override; void OnPacketComplete() override; - bool IsValidStatelessResetToken(QuicUint128 token) const override; + bool IsValidStatelessResetToken( + const StatelessResetToken& token) const override; void OnAuthenticatedIetfStatelessResetPacket( const QuicIetfStatelessResetPacket& packet) override; void OnKeyUpdate(KeyUpdateReason reason) override; @@ -699,10 +720,21 @@ class QUIC_EXPORT_PRIVATE QuicConnection void OnHandshakeTimeout() override; void OnIdleNetworkDetected() override; + // QuicConnectionIdManagerVisitorInterface + void OnPeerIssuedConnectionIdRetired() override; + bool SendNewConnectionId(const QuicNewConnectionIdFrame& frame) override; + void OnNewConnectionIdIssued(const QuicConnectionId& connection_id) override; + void OnSelfIssuedConnectionIdRetired( + const QuicConnectionId& connection_id) override; + // Please note, this is not a const function. For logging purpose, please use // ack_frame(). const QuicFrame GetUpdatedAckFrame(); + // Called to send a new connection ID to client if the # of connection ID has + // not exceeded the active connection ID limits. + void MaybeSendConnectionIdToClient(); + // Called when the handshake completes. On the client side, handshake // completes on receipt of SHLO. On the server side, handshake completes when // SHLO gets ACKed (or a forward secure packet gets decrypted successfully). @@ -1045,8 +1077,9 @@ class QUIC_EXPORT_PRIVATE QuicConnection // Queue a coalesced packet. void QueueCoalescedPacket(const QuicEncryptedPacket& packet); - // Process previously queued coalesced packets. - void MaybeProcessCoalescedPackets(); + // Process previously queued coalesced packets. Returns true if any coalesced + // packets have been successfully processed. + bool MaybeProcessCoalescedPackets(); enum PacketContent : uint8_t { NO_FRAMES_RECEIVED, @@ -1195,6 +1228,13 @@ class QUIC_EXPORT_PRIVATE QuicConnection bool validate_client_address() const { return validate_client_addresses_; } + // Instantiates connection ID manager. + void CreateConnectionIdManager(); + + bool donot_write_mid_packet_processing() const { + return donot_write_mid_packet_processing_; + } + protected: // Calls cancel() on all the alarms owned by this connection. void CancelAllAlarms(); @@ -1393,6 +1433,11 @@ class QUIC_EXPORT_PRIVATE QuicConnection void TearDownLocalConnectionState(const QuicConnectionCloseFrame& frame, ConnectionCloseSource source); + // Replace server connection ID on the client side from retry packet or + // initial packets with a different source connection ID. + void ReplaceInitialServerConnectionId( + const QuicConnectionId& new_server_connection_id); + // Writes the given packet to socket, encrypted with packet's // encryption_level. Returns true on successful write, and false if the writer // was blocked and the write needs to be tried again. Notifies the @@ -1433,9 +1478,6 @@ class QUIC_EXPORT_PRIVATE QuicConnection // blocked when this is called. void WriteQueuedPackets(); - // Writes new data if congestion control allows. - void WriteNewData(); - // Queues |packet| in the hopes that it can be decrypted in the // future, when a new key is installed. void QueueUndecryptablePacket(const QuicEncryptedPacket& packet, @@ -1680,6 +1722,18 @@ class QUIC_EXPORT_PRIVATE QuicConnection // Return true if framer should continue processing the packet. bool OnPathChallengeFrameInternal(const QuicPathChallengeFrame& frame); + virtual std::unique_ptr<QuicSelfIssuedConnectionIdManager> + MakeSelfIssuedConnectionIdManager(); + + // Called on peer IP change or restoring to previous address to reset + // congestion window, RTT stats, retransmission timer, etc. Only used in IETF + // QUIC. + std::unique_ptr<SendAlgorithmInterface> OnPeerIpAddressChanged(); + + // Process NewConnectionIdFrame either sent from peer or synsthesized from + // preferred_address transport parameter. + bool OnNewConnectionIdFrameInner(const QuicNewConnectionIdFrame& frame); + QuicFramer framer_; // Contents received in the current packet, especially used to identify @@ -1712,6 +1766,10 @@ class QUIC_EXPORT_PRIVATE QuicConnection // On the server, the connection ID is set when receiving the first packet. // This variable ensures we only set it this way once. bool client_connection_id_is_set_; + + // Whether we've already replaced our server connection ID due to receiving an + // INITIAL packet with a different source connection ID. Only used on client. + bool server_connection_id_replaced_by_initial_ = false; // Address on the last successfully processed packet received from the // direct peer. @@ -1946,7 +2004,7 @@ class QUIC_EXPORT_PRIVATE QuicConnection bool stateless_reset_token_received_; // Stores received stateless reset token from peer. Used to verify whether a // packet is a stateless reset packet. - QuicUint128 received_stateless_reset_token_; + StatelessResetToken received_stateless_reset_token_; // Id of latest sent control frame. 0 if no control frame has been sent. QuicControlFrameId last_control_frame_id_; @@ -1960,6 +2018,9 @@ class QUIC_EXPORT_PRIVATE QuicConnection // True if the writer supports release timestamp. bool supports_release_time_; + std::unique_ptr<QuicPeerIssuedConnectionIdManager> peer_issued_cid_manager_; + std::unique_ptr<QuicSelfIssuedConnectionIdManager> self_issued_cid_manager_; + // Time this connection can release packets into the future. QuicTime::Delta release_time_into_future_; @@ -2039,7 +2100,7 @@ class QUIC_EXPORT_PRIVATE QuicConnection // latch --gfe2_reloadable_flag_quic_send_path_response and // --gfe2_reloadable_flag_quic_start_peer_migration_earlier. bool send_path_response_ = start_peer_migration_earlier_ && - GetQuicReloadableFlag(quic_send_path_response); + GetQuicReloadableFlag(quic_send_path_response2); bool use_path_validator_ = send_path_response_ && @@ -2095,11 +2156,13 @@ class QUIC_EXPORT_PRIVATE QuicConnection bool count_bytes_on_alternative_path_separately_ = GetQuicReloadableFlag(quic_count_bytes_on_alternative_path_seperately); - bool update_packet_content_returns_connected_ = - GetQuicReloadableFlag(quic_update_packet_content_returns_connected); - // If true, upon seeing a new client address, validate the client address. - const bool validate_client_addresses_; + bool validate_client_addresses_ = false; + + bool support_multiple_connection_ids_ = false; + + const bool donot_write_mid_packet_processing_ = + GetQuicReloadableFlag(quic_donot_write_mid_packet_processing); }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.h b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.h index 771203314ad..a0669d7d642 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.h @@ -9,7 +9,6 @@ #include <vector> #include "quic/platform/api/quic_export.h" -#include "quic/platform/api/quic_uint128.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_manager.cc index adf8ebae71f..a8e8c542f5f 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 @@ -9,14 +9,13 @@ #include "quic/core/quic_connection_id.h" #include "quic/core/quic_error_codes.h" #include "quic/core/quic_utils.h" -#include "quic/platform/api/quic_uint128.h" namespace quic { QuicConnectionIdData::QuicConnectionIdData( const QuicConnectionId& connection_id, uint64_t sequence_number, - QuicUint128 stateless_reset_token) + const StatelessResetToken& stateless_reset_token) : connection_id(connection_id), sequence_number(sequence_number), stateless_reset_token(stateless_reset_token) {} @@ -71,9 +70,10 @@ QuicPeerIssuedConnectionIdManager::QuicPeerIssuedConnectionIdManager( new RetirePeerIssuedConnectionIdAlarm(visitor))) { QUICHE_DCHECK_GE(active_connection_id_limit_, 2u); QUICHE_DCHECK(!initial_peer_issued_connection_id.IsEmpty()); - active_connection_id_data_.emplace_back(initial_peer_issued_connection_id, - /*sequence_number=*/0u, - QuicUint128()); + active_connection_id_data_.emplace_back<const QuicConnectionId&, uint64_t, + const StatelessResetToken&>( + initial_peer_issued_connection_id, + /*sequence_number=*/0u, {}); recent_new_connection_id_sequence_numbers_.Add(0u, 1u); } @@ -357,7 +357,7 @@ QuicSelfIssuedConnectionIdManager::GetUnretiredConnectionIds() const { void QuicSelfIssuedConnectionIdManager::RetireConnectionId() { if (to_be_retired_connection_ids_.empty()) { - QUIC_BUG + QUIC_BUG(quic_bug_12420_1) << "retire_connection_id_alarm fired but there is no connection ID " "to be retired."; return; 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 3f29cd4932c..b933a69f158 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 @@ -12,6 +12,7 @@ #include <cstddef> #include <memory> + #include "quic/core/frames/quic_new_connection_id_frame.h" #include "quic/core/frames/quic_retire_connection_id_frame.h" #include "quic/core/quic_alarm.h" @@ -19,19 +20,23 @@ #include "quic/core/quic_clock.h" #include "quic/core/quic_connection_id.h" #include "quic/core/quic_interval_set.h" +#include "quic/core/quic_types.h" #include "quic/platform/api/quic_export.h" -#include "quic/platform/api/quic_uint128.h" namespace quic { +namespace test { +class QuicConnectionIdManagerPeer; +} // namespace test + struct QUIC_EXPORT_PRIVATE QuicConnectionIdData { QuicConnectionIdData(const QuicConnectionId& connection_id, uint64_t sequence_number, - QuicUint128 stateless_reset_token); + const StatelessResetToken& stateless_reset_token); QuicConnectionId connection_id; uint64_t sequence_number; - QuicUint128 stateless_reset_token; + StatelessResetToken stateless_reset_token; }; // Used by QuicSelfIssuedConnectionIdManager @@ -83,7 +88,7 @@ class QUIC_EXPORT_PRIVATE QuicPeerIssuedConnectionIdManager { const QuicConnectionId& new_connection_id); private: - friend class QuicConnectionIdManagerPeer; + friend class test::QuicConnectionIdManagerPeer; bool IsConnectionIdNew(const QuicNewConnectionIdFrame& frame); @@ -134,7 +139,7 @@ class QUIC_EXPORT_PRIVATE QuicSelfIssuedConnectionIdManager { const QuicConnectionId& old_connection_id) const; private: - friend class QuicConnectionIdManagerPeer; + friend class test::QuicConnectionIdManagerPeer; QuicNewConnectionIdFrame IssueNewConnectionId(); 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 b9ca45f5472..9c7daf0db5e 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 @@ -9,27 +9,15 @@ #include "quic/core/quic_error_codes.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/mock_clock.h" +#include "quic/test_tools/quic_connection_id_manager_peer.h" #include "quic/test_tools/quic_test_utils.h" namespace quic { - -class QuicConnectionIdManagerPeer { - public: - static QuicAlarm* GetRetirePeerIssuedConnectionIdAlarm( - QuicPeerIssuedConnectionIdManager* manager) { - return manager->retire_connection_id_alarm_.get(); - } - - static QuicAlarm* GetRetireSelfIssuedConnectionIdAlarm( - QuicSelfIssuedConnectionIdManager* manager) { - return manager->retire_connection_id_alarm_.get(); - } -}; - namespace { using ::quic::test::IsError; using ::quic::test::IsQuicNoError; +using ::quic::test::QuicConnectionIdManagerPeer; using ::quic::test::TestConnectionId; using ::testing::_; using ::testing::ElementsAre; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.h b/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.h index 0cf348953b8..06b7f5868bd 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.h @@ -209,6 +209,21 @@ struct QUIC_EXPORT_PRIVATE QuicConnectionStats { // which was canceled because the peer migrated again. Such migration is also // counted as invalid peer migration. size_t num_peer_migration_while_validating_default_path = 0; + + struct QUIC_NO_EXPORT TlsServerOperationStats { + bool success = false; + // If the operation is performed asynchronously, how long did it take. + // Zero() for synchronous operations. + QuicTime::Delta async_latency = QuicTime::Delta::Zero(); + }; + + // The TLS server op stats only have values when the corresponding operation + // is performed by TlsServerHandshaker. If an operation is done within + // BoringSSL, e.g. ticket decrypted without using + // TlsServerHandshaker::SessionTicketOpen, it will not be recorded here. + absl::optional<TlsServerOperationStats> tls_server_select_cert_stats; + absl::optional<TlsServerOperationStats> tls_server_compute_signature_stats; + absl::optional<TlsServerOperationStats> tls_server_decrypt_ticket_stats; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc index 60eccb818cd..e2fba5b8325 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 @@ -53,6 +53,7 @@ using testing::_; using testing::AnyNumber; using testing::AtLeast; using testing::DoAll; +using testing::ElementsAre; using testing::Ge; using testing::IgnoreResult; using testing::InSequence; @@ -82,6 +83,10 @@ DiversificationNonce kTestDiversificationNonce = { 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', }; +const StatelessResetToken kTestStatelessResetToken{ + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; + const QuicSocketAddress kPeerAddress = QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/12345); @@ -382,6 +387,7 @@ class TestConnection : public QuicConnection { // connection and peer creator. void set_perspective(Perspective perspective) { writer()->set_perspective(perspective); + QuicConnectionPeer::ResetPeerIssuedConnectionIdManager(this); QuicConnectionPeer::SetPerspective(this, perspective); QuicSentPacketManagerPeer::SetPerspective( QuicConnectionPeer::GetSentPacketManager(this), perspective); @@ -473,6 +479,16 @@ class TestConnection : public QuicConnection { QuicConnectionPeer::GetBlackholeDetectorAlarm(this)); } + TestAlarmFactory::TestAlarm* GetRetirePeerIssuedConnectionIdAlarm() { + return reinterpret_cast<TestAlarmFactory::TestAlarm*>( + QuicConnectionPeer::GetRetirePeerIssuedConnectionIdAlarm(this)); + } + + TestAlarmFactory::TestAlarm* GetRetireSelfIssuedConnectionIdAlarm() { + return reinterpret_cast<TestAlarmFactory::TestAlarm*>( + QuicConnectionPeer::GetRetireSelfIssuedConnectionIdAlarm(this)); + } + void PathDegradingTimeout() { QUICHE_DCHECK(PathDegradingDetectionInProgress()); GetBlackholeDetectorAlarm()->Fire(); @@ -1174,9 +1190,16 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { EncryptionLevel level) { QuicPacketHeader header = ConstructPacketHeader(number, level); QuicFrames frames; - frames.push_back(QuicFrame(frame1_)); - if (has_stop_waiting) { - frames.push_back(QuicFrame(stop_waiting_)); + if (GetQuicReloadableFlag(quic_reject_unexpected_ietf_frame_types) && + VersionHasIetfQuicFrames(version().transport_version) && + (level == ENCRYPTION_INITIAL || level == ENCRYPTION_HANDSHAKE)) { + frames.push_back(QuicFrame(QuicPingFrame())); + frames.push_back(QuicFrame(QuicPaddingFrame(100))); + } else { + frames.push_back(QuicFrame(frame1_)); + if (has_stop_waiting) { + frames.push_back(QuicFrame(stop_waiting_)); + } } return ConstructPacket(header, frames); } @@ -1303,6 +1326,13 @@ 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); + connection_.set_can_truncate_connection_ids(true); QuicConnectionPeer::SetNegotiatedVersion(&connection_); connection_.OnSuccessfulVersionNegotiation(); @@ -1431,6 +1461,8 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { bool missing_retry_id_in_config, bool wrong_retry_id_in_config); + void TestReplaceConnectionIdFromInitial(); + QuicConnectionId connection_id_; QuicFramer framer_; @@ -1694,10 +1726,10 @@ TEST_P(QuicConnectionTest, PeerPortChangeAtServer) { } TEST_P(QuicConnectionTest, PeerIpAddressChangeAtServer) { + set_perspective(Perspective::IS_SERVER); if (!connection_.validate_client_address()) { return; } - set_perspective(Perspective::IS_SERVER); QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); @@ -1705,6 +1737,7 @@ TEST_P(QuicConnectionTest, PeerIpAddressChangeAtServer) { EXPECT_CALL(visitor_, GetHandshakeState()) .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); QuicConnectionPeer::SetAddressValidated(&connection_); + connection_.OnHandshakeComplete(); // Enable 5 RTO QuicConfig config; @@ -1750,6 +1783,7 @@ TEST_P(QuicConnectionTest, PeerIpAddressChangeAtServer) { connection_.SendStreamData3(); EXPECT_EQ(1u, writer_->packets_write_attempts()); EXPECT_TRUE(connection_.BlackholeDetectionInProgress()); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); // Process another packet with a different peer address on server side will // start connection migration. @@ -1771,6 +1805,7 @@ TEST_P(QuicConnectionTest, PeerIpAddressChangeAtServer) { EXPECT_EQ(IPV6_TO_IPV4_CHANGE, connection_.active_effective_peer_migration_type()); EXPECT_FALSE(connection_.BlackholeDetectionInProgress()); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); EXPECT_EQ(2u, writer_->packets_write_attempts()); EXPECT_FALSE(writer_->path_challenge_frames().empty()); @@ -1838,6 +1873,7 @@ TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) { if (version().SupportsAntiAmplificationLimit()) { QuicConnectionPeer::SetAddressValidated(&connection_); } + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); EXPECT_CALL(visitor_, GetHandshakeState()) .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); @@ -1857,7 +1893,7 @@ TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) { EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); } ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, kPeerAddress, - ENCRYPTION_INITIAL); + ENCRYPTION_FORWARD_SECURE); EXPECT_EQ(kPeerAddress, connection_.peer_address()); EXPECT_EQ(kEffectivePeerAddress, connection_.effective_peer_address()); @@ -1868,7 +1904,7 @@ TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) { connection_.ReturnEffectivePeerAddressForNextPacket(kNewEffectivePeerAddress); EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, kPeerAddress, - ENCRYPTION_INITIAL); + ENCRYPTION_FORWARD_SECURE); EXPECT_EQ(kPeerAddress, connection_.peer_address()); EXPECT_EQ(kNewEffectivePeerAddress, connection_.effective_peer_address()); EXPECT_EQ(kPeerAddress, writer_->last_write_peer_address()); @@ -1891,7 +1927,7 @@ TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) { QuicAckFrame ack_frame = InitAckFrame(1); EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)); ProcessFramePacketWithAddresses(QuicFrame(&ack_frame), kSelfAddress, - kNewPeerAddress, ENCRYPTION_INITIAL); + kNewPeerAddress, ENCRYPTION_FORWARD_SECURE); EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); EXPECT_EQ(kNewEffectivePeerAddress, connection_.effective_peer_address()); EXPECT_EQ(NO_CHANGE, connection_.active_effective_peer_migration_type()); @@ -1907,7 +1943,7 @@ TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) { kNewerEffectivePeerAddress); EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, - kFinalPeerAddress, ENCRYPTION_INITIAL); + kFinalPeerAddress, ENCRYPTION_FORWARD_SECURE); EXPECT_EQ(kFinalPeerAddress, connection_.peer_address()); EXPECT_EQ(kNewerEffectivePeerAddress, connection_.effective_peer_address()); if (connection_.validate_client_address()) { @@ -1929,7 +1965,7 @@ TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) { EXPECT_CALL(*send_algorithm_, OnConnectionMigration()).Times(1); } ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, - kFinalPeerAddress, ENCRYPTION_INITIAL); + kFinalPeerAddress, ENCRYPTION_FORWARD_SECURE); EXPECT_EQ(kFinalPeerAddress, connection_.peer_address()); EXPECT_EQ(kNewestEffectivePeerAddress, connection_.effective_peer_address()); EXPECT_EQ(IPV6_TO_IPV4_CHANGE, @@ -1946,10 +1982,10 @@ TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) { } TEST_P(QuicConnectionTest, ReversePathValidationFailureAtServer) { + set_perspective(Perspective::IS_SERVER); if (!connection_.validate_client_address()) { return; } - set_perspective(Perspective::IS_SERVER); QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); @@ -1957,6 +1993,7 @@ TEST_P(QuicConnectionTest, ReversePathValidationFailureAtServer) { EXPECT_CALL(visitor_, GetHandshakeState()) .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); QuicConnectionPeer::SetAddressValidated(&connection_); + connection_.OnHandshakeComplete(); // Clear direct_peer_address. QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); @@ -1994,14 +2031,16 @@ TEST_P(QuicConnectionTest, ReversePathValidationFailureAtServer) { QuicFrames frames2; frames2.push_back(QuicFrame(frame2_)); + QuicPaddingFrame padding; + frames2.push_back(QuicFrame(padding)); ProcessFramesPacketWithAddresses(frames2, kSelfAddress, kNewPeerAddress, ENCRYPTION_FORWARD_SECURE); EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); EXPECT_EQ(IPV6_TO_IPV4_CHANGE, connection_.active_effective_peer_migration_type()); - EXPECT_EQ(1u, writer_->packets_write_attempts()); - EXPECT_FALSE(writer_->path_challenge_frames().empty()); + EXPECT_LT(0u, writer_->packets_write_attempts()); + EXPECT_TRUE(connection_.HasPendingPathValidation()); EXPECT_NE(connection_.sent_packet_manager().GetSendAlgorithm(), send_algorithm_); EXPECT_EQ(kNewPeerAddress, writer_->last_write_peer_address()); @@ -2018,6 +2057,9 @@ TEST_P(QuicConnectionTest, ReversePathValidationFailureAtServer) { EXPECT_EQ(IPV6_TO_IPV4_CHANGE, connection_.active_effective_peer_migration_type()); + SendStreamDataToPeer(1, "foo", 0, NO_FIN, nullptr); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + // Advance the time so that the reverse path validation times out. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs)); static_cast<TestAlarmFactory::TestAlarm*>( @@ -2029,6 +2071,7 @@ TEST_P(QuicConnectionTest, ReversePathValidationFailureAtServer) { EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); EXPECT_EQ(connection_.sent_packet_manager().GetSendAlgorithm(), send_algorithm_); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); } TEST_P(QuicConnectionTest, ReceivePathProbeWithNoAddressChangeAtServer) { @@ -2259,8 +2302,9 @@ TEST_P(QuicConnectionTest, ReceivePathProbingAtServer) { ENCRYPTION_FORWARD_SECURE); EXPECT_LT(2 * received->length(), QuicConnectionPeer::BytesReceivedOnAlternativePath(&connection_)); - EXPECT_TRUE(QuicConnectionPeer::IsAlternativePathValidated(&connection_)); - + if (connection_.validate_client_address()) { + EXPECT_TRUE(QuicConnectionPeer::IsAlternativePathValidated(&connection_)); + } // Receiving another probing packet from a newer address with a different // port shouldn't trigger another reverse path validation. QuicSocketAddress kNewerPeerAddress(QuicIpAddress::Loopback4(), @@ -2541,8 +2585,14 @@ TEST_P(QuicConnectionTest, PeerAddressChangeAtClient) { EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, kNewPeerAddress, ENCRYPTION_INITIAL); - EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); - EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); + if (connection_.version().HasIetfQuicFrames()) { + // IETF QUIC disallows server initiated address change. + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + } else { + EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); + EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); + } } TEST_P(QuicConnectionTest, MaxPacketSize) { @@ -2785,7 +2835,9 @@ TEST_P(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) { TEST_P(QuicConnectionTest, RejectUnencryptedStreamData) { // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. - if (!IsDefaultTestConfiguration()) { + if (!IsDefaultTestConfiguration() || + (GetQuicReloadableFlag(quic_reject_unexpected_ietf_frame_types) && + VersionHasIetfQuicFrames(version().transport_version))) { return; } @@ -2939,28 +2991,6 @@ TEST_P(QuicConnectionTest, AckFrequencyUpdatedFromAckFrequencyFrame) { } } -TEST_P(QuicConnectionTest, - AckFrequencyFrameOutsideApplicationDataNumberSpaceIsIgnored) { - if (!GetParam().version.HasIetfQuicFrames()) { - return; - } - connection_.set_can_receive_ack_frequency_frame(); - - QuicAckFrequencyFrame ack_frequency_frame; - ack_frequency_frame.packet_tolerance = 3; - ProcessFramePacketAtLevel(1, QuicFrame(&ack_frequency_frame), - ENCRYPTION_HANDSHAKE); - - // Expect 30 acks, every 2nd (instead of 3rd) packet including the first - // packet with AckFrequencyFrame. - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(30); - EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(60); - // Receives packets 2 - 61. - for (size_t i = 2; i <= 61; ++i) { - ProcessDataPacket(i); - } -} - TEST_P(QuicConnectionTest, AckDecimationReducesAcks) { const size_t kMinRttMs = 40; RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); @@ -6873,7 +6903,6 @@ TEST_P(QuicConnectionTest, IetfStatelessReset) { if (!GetParam().version.HasIetfInvariantHeader()) { return; } - const QuicUint128 kTestStatelessResetToken = 1010101; QuicConfig config; QuicConfigPeer::SetReceivedStatelessResetToken(&config, kTestStatelessResetToken); @@ -6881,6 +6910,7 @@ TEST_P(QuicConnectionTest, IetfStatelessReset) { connection_.SetFromConfig(config); std::unique_ptr<QuicEncryptedPacket> packet( QuicFramer::BuildIetfStatelessResetPacket(connection_id_, + /*received_packet_length=*/100, kTestStatelessResetToken)); std::unique_ptr<QuicReceivedPacket> received( ConstructReceivedPacket(*packet, QuicTime::Zero())); @@ -7757,7 +7787,13 @@ TEST_P(QuicConnectionTest, ServerReceivesChloOnNonCryptoStream) { EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)); ForceProcessFramePacket(QuicFrame(frame1_)); - TestConnectionCloseQuicErrorCode(QUIC_MAYBE_CORRUPTED_MEMORY); + if (GetQuicReloadableFlag(quic_reject_unexpected_ietf_frame_types) && + VersionHasIetfQuicFrames(version().transport_version)) { + // INITIAL packet should not contain STREAM frame. + TestConnectionCloseQuicErrorCode(IETF_QUIC_PROTOCOL_VIOLATION); + } else { + TestConnectionCloseQuicErrorCode(QUIC_MAYBE_CORRUPTED_MEMORY); + } } TEST_P(QuicConnectionTest, ClientReceivesRejOnNonCryptoStream) { @@ -7774,7 +7810,13 @@ TEST_P(QuicConnectionTest, ClientReceivesRejOnNonCryptoStream) { EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)); ForceProcessFramePacket(QuicFrame(frame1_)); - TestConnectionCloseQuicErrorCode(QUIC_MAYBE_CORRUPTED_MEMORY); + if (GetQuicReloadableFlag(quic_reject_unexpected_ietf_frame_types) && + VersionHasIetfQuicFrames(version().transport_version)) { + // INITIAL packet should not contain STREAM frame. + TestConnectionCloseQuicErrorCode(IETF_QUIC_PROTOCOL_VIOLATION); + } else { + TestConnectionCloseQuicErrorCode(QUIC_MAYBE_CORRUPTED_MEMORY); + } } TEST_P(QuicConnectionTest, CloseConnectionOnPacketTooLarge) { @@ -8572,8 +8614,10 @@ TEST_P(QuicConnectionTest, RetransmittableOnWirePingLimit) { } TEST_P(QuicConnectionTest, ValidStatelessResetToken) { - const QuicUint128 kTestToken = 1010101; - const QuicUint128 kWrongTestToken = 1010100; + const StatelessResetToken kTestToken{0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 1}; + const StatelessResetToken kWrongTestToken{0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 2}; QuicConfig config; // No token has been received. EXPECT_FALSE(connection_.IsValidStatelessResetToken(kTestToken)); @@ -8921,23 +8965,23 @@ TEST_P(QuicConnectionTest, EXPECT_CALL(visitor_, OnPacketReceived(_, _, /*is_connectivity_probe=*/true)) .Times(1); - const QuicSocketAddress kNewSelfAddress = - QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); + const QuicSocketAddress kNewSelfAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); - std::unique_ptr<SerializedPacket> probing_packet = ConstructProbingPacket(); - std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( - QuicEncryptedPacket(probing_packet->encrypted_buffer, - probing_packet->encrypted_length), - clock_.Now())); - uint64_t num_probing_received = - connection_.GetStats().num_connectivity_probing_received; - ProcessReceivedPacket(kNewSelfAddress, kPeerAddress, *received); + std::unique_ptr<SerializedPacket> probing_packet = ConstructProbingPacket(); + std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( + QuicEncryptedPacket(probing_packet->encrypted_buffer, + probing_packet->encrypted_length), + clock_.Now())); + uint64_t num_probing_received = + connection_.GetStats().num_connectivity_probing_received; + ProcessReceivedPacket(kNewSelfAddress, kPeerAddress, *received); - EXPECT_EQ(num_probing_received + 1, - connection_.GetStats().num_connectivity_probing_received); - EXPECT_EQ(kPeerAddress, connection_.peer_address()); - EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); - EXPECT_TRUE(connection_.IsPathDegrading()); + EXPECT_EQ(num_probing_received + 1, + connection_.GetStats().num_connectivity_probing_received); + EXPECT_EQ(kPeerAddress, connection_.peer_address()); + EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); + EXPECT_TRUE(connection_.IsPathDegrading()); } // Verify new path degrading detection is activated. @@ -9174,23 +9218,23 @@ TEST_P(QuicConnectionTest, MultiplePacketNumberSpacesBasicReceiving) { // Receives packet 1000 in initial data. ProcessCryptoPacketAtLevel(1000, ENCRYPTION_INITIAL); EXPECT_TRUE(connection_.HasPendingAcks()); - peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT, + peer_framer_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, std::make_unique<TaggingEncrypter>(0x02)); - SetDecrypter(ENCRYPTION_ZERO_RTT, + SetDecrypter(ENCRYPTION_FORWARD_SECURE, std::make_unique<StrictTaggingDecrypter>(0x02)); connection_.SetEncrypter(ENCRYPTION_INITIAL, std::make_unique<TaggingEncrypter>(0x02)); // Receives packet 1000 in application data. - ProcessDataPacketAtLevel(1000, false, ENCRYPTION_ZERO_RTT); + ProcessDataPacketAtLevel(1000, false, ENCRYPTION_FORWARD_SECURE); EXPECT_TRUE(connection_.HasPendingAcks()); - connection_.SendApplicationDataAtLevel(ENCRYPTION_ZERO_RTT, 5, "data", 0, - NO_FIN); + connection_.SendApplicationDataAtLevel(ENCRYPTION_FORWARD_SECURE, 5, "data", + 0, NO_FIN); // Verify application data ACK gets bundled with outgoing data. EXPECT_EQ(2u, writer_->frame_count()); // Make sure ACK alarm is still set because initial data is not ACKed. EXPECT_TRUE(connection_.HasPendingAcks()); // Receive packet 1001 in application data. - ProcessDataPacketAtLevel(1001, false, ENCRYPTION_ZERO_RTT); + ProcessDataPacketAtLevel(1001, false, ENCRYPTION_FORWARD_SECURE); clock_.AdvanceTime(DefaultRetransmissionTime()); // Simulates ACK alarm fires and verify two ACKs are flushed. EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); @@ -9199,13 +9243,9 @@ TEST_P(QuicConnectionTest, MultiplePacketNumberSpacesBasicReceiving) { connection_.GetAckAlarm()->Fire(); EXPECT_FALSE(connection_.HasPendingAcks()); // Receives more packets in application data. - ProcessDataPacketAtLevel(1002, false, ENCRYPTION_ZERO_RTT); + ProcessDataPacketAtLevel(1002, false, ENCRYPTION_FORWARD_SECURE); EXPECT_TRUE(connection_.HasPendingAcks()); - peer_framer_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, - std::make_unique<TaggingEncrypter>(0x02)); - SetDecrypter(ENCRYPTION_FORWARD_SECURE, - std::make_unique<StrictTaggingDecrypter>(0x02)); // Verify zero rtt and forward secure packets get acked in the same packet. EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); ProcessDataPacket(1003); @@ -9271,7 +9311,7 @@ TEST_P(QuicConnectionTest, ValidClientConnectionId) { frames.push_back(QuicFrame(ping_frame)); frames.push_back(QuicFrame(padding_frame)); std::unique_ptr<QuicPacket> packet = - BuildUnsizedDataPacket(&framer_, header, frames); + BuildUnsizedDataPacket(&peer_framer_, header, frames); char buffer[kMaxOutgoingPacketSize]; size_t encrypted_length = peer_framer_.EncryptPayload( ENCRYPTION_FORWARD_SECURE, QuicPacketNumber(1), *packet, buffer, @@ -9299,7 +9339,7 @@ TEST_P(QuicConnectionTest, InvalidClientConnectionId) { frames.push_back(QuicFrame(ping_frame)); frames.push_back(QuicFrame(padding_frame)); std::unique_ptr<QuicPacket> packet = - BuildUnsizedDataPacket(&framer_, header, frames); + BuildUnsizedDataPacket(&peer_framer_, header, frames); char buffer[kMaxOutgoingPacketSize]; size_t encrypted_length = peer_framer_.EncryptPayload( ENCRYPTION_FORWARD_SECURE, QuicPacketNumber(1), *packet, buffer, @@ -9327,7 +9367,7 @@ TEST_P(QuicConnectionTest, UpdateClientConnectionIdFromFirstPacket) { frames.push_back(QuicFrame(ping_frame)); frames.push_back(QuicFrame(padding_frame)); std::unique_ptr<QuicPacket> packet = - BuildUnsizedDataPacket(&framer_, header, frames); + BuildUnsizedDataPacket(&peer_framer_, header, frames); char buffer[kMaxOutgoingPacketSize]; size_t encrypted_length = peer_framer_.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(1), @@ -9339,6 +9379,79 @@ TEST_P(QuicConnectionTest, UpdateClientConnectionIdFromFirstPacket) { EXPECT_EQ(0u, connection_.GetStats().packets_dropped); EXPECT_EQ(TestConnectionId(0x33), connection_.client_connection_id()); } +void QuicConnectionTest::TestReplaceConnectionIdFromInitial() { + if (!framer_.version().AllowsVariableLengthConnectionIds()) { + return; + } + // We start with a known connection ID. + EXPECT_TRUE(connection_.connected()); + EXPECT_EQ(0u, connection_.GetStats().packets_dropped); + EXPECT_NE(TestConnectionId(0x33), connection_.connection_id()); + // Receiving an initial can replace the connection ID once. + { + QuicPacketHeader header = ConstructPacketHeader(1, ENCRYPTION_INITIAL); + header.source_connection_id = TestConnectionId(0x33); + header.source_connection_id_included = CONNECTION_ID_PRESENT; + QuicFrames frames; + QuicPingFrame ping_frame; + QuicPaddingFrame padding_frame; + frames.push_back(QuicFrame(ping_frame)); + frames.push_back(QuicFrame(padding_frame)); + std::unique_ptr<QuicPacket> packet = + BuildUnsizedDataPacket(&peer_framer_, header, frames); + char buffer[kMaxOutgoingPacketSize]; + size_t encrypted_length = + peer_framer_.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(1), + *packet, buffer, kMaxOutgoingPacketSize); + QuicReceivedPacket received_packet(buffer, encrypted_length, clock_.Now(), + false); + ProcessReceivedPacket(kSelfAddress, kPeerAddress, received_packet); + } + EXPECT_TRUE(connection_.connected()); + EXPECT_EQ(0u, connection_.GetStats().packets_dropped); + EXPECT_EQ(TestConnectionId(0x33), connection_.connection_id()); + // Trying to replace the connection ID a second time drops the packet. + { + QuicPacketHeader header = ConstructPacketHeader(2, ENCRYPTION_INITIAL); + header.source_connection_id = TestConnectionId(0x66); + header.source_connection_id_included = CONNECTION_ID_PRESENT; + QuicFrames frames; + QuicPingFrame ping_frame; + QuicPaddingFrame padding_frame; + frames.push_back(QuicFrame(ping_frame)); + frames.push_back(QuicFrame(padding_frame)); + std::unique_ptr<QuicPacket> packet = + BuildUnsizedDataPacket(&peer_framer_, header, frames); + char buffer[kMaxOutgoingPacketSize]; + size_t encrypted_length = + peer_framer_.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(2), + *packet, buffer, kMaxOutgoingPacketSize); + QuicReceivedPacket received_packet(buffer, encrypted_length, clock_.Now(), + false); + ProcessReceivedPacket(kSelfAddress, kPeerAddress, received_packet); + } + EXPECT_TRUE(connection_.connected()); + EXPECT_EQ(1u, connection_.GetStats().packets_dropped); + EXPECT_EQ(TestConnectionId(0x33), connection_.connection_id()); +} + +TEST_P(QuicConnectionTest, ReplaceServerConnectionIdFromInitial) { + TestReplaceConnectionIdFromInitial(); +} + +TEST_P(QuicConnectionTest, ReplaceServerConnectionIdFromRetryAndInitial) { + // First make the connection process a RETRY and replace the server connection + // ID a first time. + TestClientRetryHandling(/*invalid_retry_tag=*/false, + /*missing_original_id_in_config=*/false, + /*wrong_original_id_in_config=*/false, + /*missing_retry_id_in_config=*/false, + /*wrong_retry_id_in_config=*/false); + // Reset the test framer to use the right connection ID. + peer_framer_.SetInitialObfuscators(connection_.connection_id()); + // Now process an INITIAL and replace the server connection ID a second time. + TestReplaceConnectionIdFromInitial(); +} // Regression test for b/134416344. TEST_P(QuicConnectionTest, CheckConnectedBeforeFlush) { @@ -11049,7 +11162,10 @@ TEST_P(QuicConnectionTest, ProcessUndecryptablePacketsBasedOnEncryptionLevel) { std::make_unique<TaggingEncrypter>(0x01)); connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE); // Verify all ENCRYPTION_HANDSHAKE packets get processed. - EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(6); + if (!GetQuicReloadableFlag(quic_reject_unexpected_ietf_frame_types) || + !VersionHasIetfQuicFrames(version().transport_version)) { + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(6); + } connection_.GetProcessUndecryptablePacketsAlarm()->Fire(); EXPECT_EQ(1u, QuicConnectionPeer::NumUndecryptablePackets(&connection_)); @@ -11694,13 +11810,46 @@ TEST_P(QuicConnectionTest, NewPathValidationCancelsPreviousOne) { EXPECT_TRUE(connection_.HasPendingPathValidation()); } +// Regression test for b/182571515. +TEST_P(QuicConnectionTest, PathValidationRetry) { + if (!VersionHasIetfQuicFrames(connection_.version().transport_version) || + !connection_.use_path_validator()) { + return; + } + PathProbeTestInit(Perspective::IS_CLIENT); + + EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) + .Times(2u) + .WillRepeatedly(Invoke([&]() { + EXPECT_EQ(1u, writer_->path_challenge_frames().size()); + EXPECT_EQ(1u, writer_->padding_frames().size()); + })); + bool success = true; + connection_.ValidatePath( + std::make_unique<TestQuicPathValidationContext>( + connection_.self_address(), connection_.peer_address(), + writer_.get()), + std::make_unique<TestValidationResultDelegate>( + connection_.self_address(), connection_.peer_address(), &success)); + EXPECT_EQ(1u, writer_->packets_write_attempts()); + EXPECT_TRUE(connection_.HasPendingPathValidation()); + + // Retry after time out. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs)); + static_cast<test::MockRandom*>(helper_->GetRandomGenerator())->ChangeValue(); + static_cast<TestAlarmFactory::TestAlarm*>( + QuicPathValidatorPeer::retry_timer( + QuicConnectionPeer::path_validator(&connection_))) + ->Fire(); + EXPECT_EQ(2u, writer_->packets_write_attempts()); +} + TEST_P(QuicConnectionTest, PathValidationReceivesStatelessReset) { if (!VersionHasIetfQuicFrames(connection_.version().transport_version) || !connection_.use_path_validator()) { return; } PathProbeTestInit(Perspective::IS_CLIENT); - const QuicUint128 kTestStatelessResetToken = 1010101; QuicConfig config; QuicConfigPeer::SetReceivedStatelessResetToken(&config, kTestStatelessResetToken); @@ -11729,6 +11878,7 @@ TEST_P(QuicConnectionTest, PathValidationReceivesStatelessReset) { std::unique_ptr<QuicEncryptedPacket> packet( QuicFramer::BuildIetfStatelessResetPacket(connection_id_, + /*received_packet_length=*/100, kTestStatelessResetToken)); std::unique_ptr<QuicReceivedPacket> received( ConstructReceivedPacket(*packet, QuicTime::Zero())); @@ -12375,8 +12525,10 @@ TEST_P(QuicConnectionTest, ZeroRttRejectionAndMissingInitialKeys) { connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, std::make_unique<TaggingEncrypter>(0x04)); connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); - // Retransmit rejected 0-RTT packets. - connection_.OnCanWrite(); + if (!GetQuicReloadableFlag(quic_donot_write_mid_packet_processing)) { + // Retransmit rejected 0-RTT packets. + connection_.OnCanWrite(); + } // Advance INITIAL ack delay to trigger initial ACK to be sent AFTER // the retransmission of rejected 0-RTT packets while the HANDSHAKE // packet is still in the coalescer, such that the INITIAL key gets @@ -13479,64 +13631,7 @@ TEST_P(QuicConnectionTest, DonotOverrideRetryTokenWithAddressToken) { } TEST_P(QuicConnectionTest, - ServerReceivedZeroRttWithHigherPacketNumberThanOneRttAndFlagDisabled) { - SetQuicReloadableFlag( - quic_close_connection_on_0rtt_packet_number_higher_than_1rtt, false); - if (!connection_.version().UsesTls()) { - return; - } - - // The code that checks for this error piggybacks on some book-keeping state - // kept for key update, so enable key update for the test. - std::string error_details; - TransportParameters params; - params.key_update_not_yet_supported = false; - QuicConfig config; - EXPECT_THAT(config.ProcessTransportParameters( - params, /* is_resumption = */ false, &error_details), - IsQuicNoError()); - config.SetKeyUpdateSupportedLocally(); - QuicConfigPeer::SetNegotiated(&config, true); - QuicConfigPeer::SetReceivedOriginalConnectionId(&config, - connection_.connection_id()); - QuicConfigPeer::SetReceivedInitialSourceConnectionId( - &config, connection_.connection_id()); - EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); - connection_.SetFromConfig(config); - - set_perspective(Perspective::IS_SERVER); - SetDecrypter(ENCRYPTION_ZERO_RTT, - std::make_unique<NullDecrypter>(Perspective::IS_SERVER)); - - EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); - ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); - - // Finish handshake. - connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); - notifier_.NeuterUnencryptedData(); - connection_.NeuterUnencryptedPackets(); - connection_.OnHandshakeComplete(); - EXPECT_CALL(visitor_, GetHandshakeState()) - .WillRepeatedly(Return(HANDSHAKE_COMPLETE)); - - // Decrypt a 1-RTT packet. - EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); - ProcessDataPacketAtLevel(2, !kHasStopWaiting, ENCRYPTION_FORWARD_SECURE); - EXPECT_TRUE(connection_.GetDiscardZeroRttDecryptionKeysAlarm()->IsSet()); - - // 0-RTT packet with higher packet number than a 1-RTT packet is invalid, but - // accepted as the - // quic_close_connection_on_0rtt_packet_number_higher_than_1rtt - // flag is disabled. - EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); - ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_ZERO_RTT); - EXPECT_TRUE(connection_.connected()); -} - -TEST_P(QuicConnectionTest, ServerReceivedZeroRttWithHigherPacketNumberThanOneRtt) { - SetQuicReloadableFlag( - quic_close_connection_on_0rtt_packet_number_higher_than_1rtt, true); if (!connection_.version().UsesTls()) { return; } @@ -13624,17 +13719,10 @@ TEST_P(QuicConnectionTest, PeerMigrateBeforeHandshakeConfirm) { EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)); EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0u); - if (!GetQuicReloadableFlag(quic_update_packet_content_returns_connected)) { - EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)); - EXPECT_QUIC_BUG( - ProcessFramePacketWithAddresses(QuicFrame(&frame), kSelfAddress, - kNewPeerAddress, ENCRYPTION_INITIAL), - ""); - } else { - EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)).Times(0); - ProcessFramePacketWithAddresses(QuicFrame(&frame), kSelfAddress, - kNewPeerAddress, ENCRYPTION_INITIAL); - } + + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)).Times(0); + ProcessFramePacketWithAddresses(QuicFrame(&frame), kSelfAddress, + kNewPeerAddress, ENCRYPTION_INITIAL); EXPECT_FALSE(connection_.connected()); } @@ -13666,10 +13754,10 @@ TEST_P(QuicConnectionTest, TryToFlushAckWithAckQueued) { } TEST_P(QuicConnectionTest, PathChallengeBeforePeerIpAddressChangeAtServer) { + set_perspective(Perspective::IS_SERVER); if (!connection_.validate_client_address()) { return; } - set_perspective(Perspective::IS_SERVER); PathProbeTestInit(Perspective::IS_SERVER); const QuicSocketAddress kNewPeerAddress = @@ -13764,10 +13852,10 @@ TEST_P(QuicConnectionTest, PathChallengeBeforePeerIpAddressChangeAtServer) { TEST_P(QuicConnectionTest, PathValidationSucceedsBeforePeerIpAddressChangeAtServer) { + set_perspective(Perspective::IS_SERVER); if (!connection_.validate_client_address()) { return; } - set_perspective(Perspective::IS_SERVER); PathProbeTestInit(Perspective::IS_SERVER); // Receive probing packet with new peer address. @@ -13855,10 +13943,10 @@ TEST_P(QuicConnectionTest, TEST_P(QuicConnectionTest, ProbedOnAnotherPathAfterPeerIpAddressChangeAtServer) { + PathProbeTestInit(Perspective::IS_SERVER); if (!connection_.validate_client_address()) { return; } - PathProbeTestInit(Perspective::IS_SERVER); const QuicSocketAddress kNewPeerAddress(QuicIpAddress::Loopback4(), /*port=*/23456); @@ -13916,6 +14004,280 @@ TEST_P(QuicConnectionTest, EXPECT_TRUE(connection_.HasPendingPathValidation()); } +TEST_P(QuicConnectionTest, + CloseConnectionAfterReceiveNewConnectionIdFromPeerUsingEmptyCID) { + if (!version().HasIetfQuicFrames()) { + return; + } + QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_); + set_perspective(Perspective::IS_SERVER); + ASSERT_TRUE(connection_.client_connection_id().IsEmpty()); + + EXPECT_CALL(visitor_, BeforeConnectionCloseSent()); + EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)) + .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame)); + QuicNewConnectionIdFrame frame; + frame.sequence_number = 1u; + frame.connection_id = TestConnectionId(1); + frame.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame.connection_id); + frame.retire_prior_to = 0u; + + EXPECT_FALSE(connection_.OnNewConnectionIdFrame(frame)); + + EXPECT_FALSE(connection_.connected()); + EXPECT_THAT(saved_connection_close_frame_.quic_error_code, + IsError(IETF_QUIC_PROTOCOL_VIOLATION)); +} + +TEST_P(QuicConnectionTest, NewConnectionIdFrameResultsInError) { + if (!version().HasIetfQuicFrames()) { + return; + } + QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_); + connection_.CreateConnectionIdManager(); + ASSERT_FALSE(connection_.connection_id().IsEmpty()); + + EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)) + .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame)); + QuicNewConnectionIdFrame frame; + frame.sequence_number = 1u; + frame.connection_id = connection_id_; // Reuses connection ID casuing error. + frame.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame.connection_id); + frame.retire_prior_to = 0u; + + EXPECT_FALSE(connection_.OnNewConnectionIdFrame(frame)); + + EXPECT_FALSE(connection_.connected()); + EXPECT_THAT(saved_connection_close_frame_.quic_error_code, + IsError(IETF_QUIC_PROTOCOL_VIOLATION)); +} + +TEST_P(QuicConnectionTest, + ClientRetirePeerIssuedConnectionIdTriggeredByNewConnectionIdFrame) { + if (!version().HasIetfQuicFrames()) { + return; + } + QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_); + connection_.CreateConnectionIdManager(); + + QuicNewConnectionIdFrame frame; + frame.sequence_number = 1u; + frame.connection_id = TestConnectionId(1); + frame.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame.connection_id); + frame.retire_prior_to = 0u; + + EXPECT_TRUE(connection_.OnNewConnectionIdFrame(frame)); + auto* retire_peer_issued_cid_alarm = + connection_.GetRetirePeerIssuedConnectionIdAlarm(); + ASSERT_FALSE(retire_peer_issued_cid_alarm->IsSet()); + + frame.sequence_number = 2u; + frame.connection_id = TestConnectionId(2); + frame.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame.connection_id); + frame.retire_prior_to = 1u; // CID associated with #1 will be retired. + + EXPECT_TRUE(connection_.OnNewConnectionIdFrame(frame)); + ASSERT_TRUE(retire_peer_issued_cid_alarm->IsSet()); + EXPECT_EQ(connection_.connection_id(), connection_id_); + + EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/0u)); + retire_peer_issued_cid_alarm->Fire(); + EXPECT_EQ(connection_.connection_id(), TestConnectionId(2)); + EXPECT_EQ(connection_.packet_creator().GetDestinationConnectionId(), + TestConnectionId(2)); +} + +TEST_P(QuicConnectionTest, + ServerRetirePeerIssuedConnectionIdTriggeredByNewConnectionIdFrame) { + if (!version().HasIetfQuicFrames()) { + return; + } + QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_); + set_perspective(Perspective::IS_SERVER); + connection_.set_client_connection_id(TestConnectionId(0)); + + QuicNewConnectionIdFrame frame; + frame.sequence_number = 1u; + frame.connection_id = TestConnectionId(1); + frame.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame.connection_id); + frame.retire_prior_to = 0u; + + EXPECT_TRUE(connection_.OnNewConnectionIdFrame(frame)); + auto* retire_peer_issued_cid_alarm = + connection_.GetRetirePeerIssuedConnectionIdAlarm(); + ASSERT_FALSE(retire_peer_issued_cid_alarm->IsSet()); + + frame.sequence_number = 2u; + frame.connection_id = TestConnectionId(2); + frame.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame.connection_id); + frame.retire_prior_to = 1u; // CID associated with #1 will be retired. + + EXPECT_TRUE(connection_.OnNewConnectionIdFrame(frame)); + ASSERT_TRUE(retire_peer_issued_cid_alarm->IsSet()); + EXPECT_EQ(connection_.client_connection_id(), TestConnectionId(0)); + + EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/0u)); + retire_peer_issued_cid_alarm->Fire(); + EXPECT_EQ(connection_.client_connection_id(), TestConnectionId(2)); + EXPECT_EQ(connection_.packet_creator().GetDestinationConnectionId(), + TestConnectionId(2)); +} + +TEST_P(QuicConnectionTest, + CloseConnectionAfterReceiveRetireConnectionIdWhenNoCIDIssued) { + if (!version().HasIetfQuicFrames()) { + return; + } + QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_); + set_perspective(Perspective::IS_SERVER); + + EXPECT_CALL(visitor_, BeforeConnectionCloseSent()); + EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)) + .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame)); + QuicRetireConnectionIdFrame frame; + frame.sequence_number = 1u; + + EXPECT_FALSE(connection_.OnRetireConnectionIdFrame(frame)); + + EXPECT_FALSE(connection_.connected()); + EXPECT_THAT(saved_connection_close_frame_.quic_error_code, + IsError(IETF_QUIC_PROTOCOL_VIOLATION)); +} + +TEST_P(QuicConnectionTest, RetireConnectionIdFrameResultsInError) { + if (!version().HasIetfQuicFrames()) { + return; + } + QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_); + set_perspective(Perspective::IS_SERVER); + connection_.CreateConnectionIdManager(); + + EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_)); + EXPECT_CALL(visitor_, SendNewConnectionId(_)); + connection_.MaybeSendConnectionIdToClient(); + + EXPECT_CALL(visitor_, BeforeConnectionCloseSent()); + EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)) + .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame)); + QuicRetireConnectionIdFrame frame; + frame.sequence_number = 2u; // The corresponding ID is never issued. + + EXPECT_FALSE(connection_.OnRetireConnectionIdFrame(frame)); + + EXPECT_FALSE(connection_.connected()); + EXPECT_THAT(saved_connection_close_frame_.quic_error_code, + IsError(IETF_QUIC_PROTOCOL_VIOLATION)); +} + +TEST_P(QuicConnectionTest, ServerRetireSelfIssuedConnectionId) { + if (!version().HasIetfQuicFrames()) { + return; + } + QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_); + set_perspective(Perspective::IS_SERVER); + connection_.CreateConnectionIdManager(); + QuicConnectionId recorded_cid; + auto cid_recorder = [&recorded_cid](const QuicConnectionId& cid) { + recorded_cid = cid; + }; + QuicConnectionId cid0 = connection_id_; + QuicConnectionId cid1; + QuicConnectionId cid2; + + EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_)) + .WillOnce(Invoke(cid_recorder)); + EXPECT_CALL(visitor_, SendNewConnectionId(_)); + connection_.MaybeSendConnectionIdToClient(); + cid1 = recorded_cid; + + auto* retire_self_issued_cid_alarm = + connection_.GetRetireSelfIssuedConnectionIdAlarm(); + ASSERT_FALSE(retire_self_issued_cid_alarm->IsSet()); + + QuicRetireConnectionIdFrame frame; + frame.sequence_number = 0u; + EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_)) + .WillOnce(Invoke(cid_recorder)); + // RetireConnectionId trigers sending NewConnectionId immediately. + EXPECT_CALL(visitor_, SendNewConnectionId(_)); + EXPECT_TRUE(connection_.OnRetireConnectionIdFrame(frame)); + cid2 = recorded_cid; + // cid0 is not retired immediately. + EXPECT_THAT(connection_.GetActiveServerConnectionIds(), + ElementsAre(cid0, cid1, cid2)); + ASSERT_TRUE(retire_self_issued_cid_alarm->IsSet()); + // 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)); +} + +// Regression test for b/182571515 +TEST_P(QuicConnectionTest, LostDataThenGetAcknowledged) { + set_perspective(Perspective::IS_SERVER); + if (!connection_.validate_client_address()) { + return; + } + + QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); + if (version().SupportsAntiAmplificationLimit()) { + QuicConnectionPeer::SetAddressValidated(&connection_); + } + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); + + QuicPacketNumber last_packet; + // Send packets 1 to 4. + SendStreamDataToPeer(3, "foo", 0, NO_FIN, &last_packet); // Packet 1 + SendStreamDataToPeer(3, "foo", 3, NO_FIN, &last_packet); // Packet 2 + SendStreamDataToPeer(3, "foo", 6, NO_FIN, &last_packet); // Packet 3 + SendStreamDataToPeer(3, "foo", 9, NO_FIN, &last_packet); // Packet 4 + + // Process a PING packet to set peer address. + ProcessFramePacket(QuicFrame(QuicPingFrame())); + + // Process a packet containing a STREAM_FRAME and an ACK with changed peer + // address. + QuicFrames frames; + frames.push_back(QuicFrame(frame1_)); + QuicAckFrame ack = InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(5)}}); + frames.push_back(QuicFrame(&ack)); + + EXPECT_CALL(visitor_, OnConnectionMigration(_)).Times(1); + + // Invoke OnCanWrite. + EXPECT_CALL(visitor_, OnStreamFrame(_)) + .WillOnce( + InvokeWithoutArgs(¬ifier_, &SimpleSessionNotifier::OnCanWrite)); + QuicIpAddress ip_address; + ASSERT_TRUE(ip_address.FromString("127.0.52.223")); + if (GetQuicReloadableFlag(quic_donot_write_mid_packet_processing)) { + EXPECT_QUIC_BUG( + ProcessFramesPacketWithAddresses(frames, kSelfAddress, + QuicSocketAddress(ip_address, 1000), + ENCRYPTION_FORWARD_SECURE), + "Try to write mid packet processing"); + EXPECT_EQ(1u, writer_->path_challenge_frames().size()); + // Verify stream frame will not be retransmitted. + EXPECT_TRUE(writer_->stream_frames().empty()); + } else { + ProcessFramesPacketWithAddresses(frames, kSelfAddress, + QuicSocketAddress(ip_address, 1000), + ENCRYPTION_FORWARD_SECURE); + // In prod, this would cause FAILED_TO_SERIALIZE_PACKET since the stream + // data has been freed, but simple_data_producer does not free data. + EXPECT_EQ(1u, writer_->stream_frames().size()); + } +} + } // namespace } // namespace test } // namespace quic 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 2de827f6227..b750abb3a4f 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 @@ -100,6 +100,9 @@ const size_t kPublicFlagsSize = 1; // Number of bytes reserved for version number in the packet header. const size_t kQuicVersionSize = 4; +// Minimum number of active connection IDs that an end point can maintain. +const uint32_t kMinNumOfActiveConnectionIds = 2; + // Length of the retry integrity tag in bytes. // https://tools.ietf.org/html/draft-ietf-quic-transport-25#section-17.2.5 const size_t kRetryIntegrityTagLength = 16; @@ -291,6 +294,13 @@ QUIC_EXPORT_PRIVATE QuicPacketNumber FirstSendingPacketNumber(); QUIC_EXPORT_PRIVATE extern const char* const kEPIDGoogleFrontEnd; QUIC_EXPORT_PRIVATE extern const char* const kEPIDGoogleFrontEnd0; +// HTTP/3 Datagrams. +enum : QuicDatagramFlowId { + kFirstDatagramFlowIdClient = 0, + kFirstDatagramFlowIdServer = 1, + kDatagramFlowIdIncrement = 2, +}; + } // 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 64e5f5d38eb..f41a3560009 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 @@ -138,7 +138,7 @@ void QuicControlFrameManager::WriteOrBufferNewConnectionId( const QuicConnectionId& connection_id, uint64_t sequence_number, uint64_t retire_prior_to, - QuicUint128 stateless_reset_token) { + const StatelessResetToken& stateless_reset_token) { QUIC_DVLOG(1) << "Writing NEW_CONNECTION_ID frame"; WriteOrBufferQuicFrame(QuicFrame(new QuicNewConnectionIdFrame( ++last_control_frame_id_, connection_id, sequence_number, @@ -161,7 +161,7 @@ void QuicControlFrameManager::WriteOrBufferNewToken(absl::string_view token) { void QuicControlFrameManager::OnControlFrameSent(const QuicFrame& frame) { QuicControlFrameId id = GetControlFrameId(frame); if (id == kInvalidControlFrameId) { - QUIC_BUG + QUIC_BUG(quic_bug_12727_1) << "Send or retransmit a control frame with invalid control frame id"; return; } @@ -180,8 +180,9 @@ void QuicControlFrameManager::OnControlFrameSent(const QuicFrame& frame) { return; } if (id > least_unsent_) { - QUIC_BUG << "Try to send control frames out of order, id: " << id - << " least_unsent: " << least_unsent_; + QUIC_BUG(quic_bug_10517_1) + << "Try to send control frames out of order, id: " << id + << " least_unsent: " << least_unsent_; delegate_->OnControlFrameManagerError( QUIC_INTERNAL_ERROR, "Try to send control frames out of order"); return; @@ -211,7 +212,7 @@ void QuicControlFrameManager::OnControlFrameLost(const QuicFrame& frame) { return; } if (id >= least_unsent_) { - QUIC_BUG << "Try to mark unsent control frame as lost"; + QUIC_BUG(quic_bug_10517_2) << "Try to mark unsent control frame as lost"; delegate_->OnControlFrameManagerError( QUIC_INTERNAL_ERROR, "Try to mark unsent control frame as lost"); return; @@ -224,7 +225,8 @@ void QuicControlFrameManager::OnControlFrameLost(const QuicFrame& frame) { } if (!QuicContainsKey(pending_retransmissions_, id)) { pending_retransmissions_[id] = true; - QUIC_BUG_IF(pending_retransmissions_.size() > control_frames_.size()) + QUIC_BUG_IF(quic_bug_12727_2, + pending_retransmissions_.size() > control_frames_.size()) << "least_unacked_: " << least_unacked_ << ", least_unsent_: " << least_unsent_; } @@ -252,7 +254,7 @@ bool QuicControlFrameManager::WillingToWrite() const { } QuicFrame QuicControlFrameManager::NextPendingRetransmission() const { - QUIC_BUG_IF(pending_retransmissions_.empty()) + QUIC_BUG_IF(quic_bug_12727_3, pending_retransmissions_.empty()) << "Unexpected call to NextPendingRetransmission() with empty pending " << "retransmission list."; QuicControlFrameId id = pending_retransmissions_.begin()->first; @@ -279,7 +281,7 @@ bool QuicControlFrameManager::RetransmitControlFrame(const QuicFrame& frame, return true; } if (id >= least_unsent_) { - QUIC_BUG << "Try to retransmit unsent control frame"; + QUIC_BUG(quic_bug_10517_3) << "Try to retransmit unsent control frame"; delegate_->OnControlFrameManagerError( QUIC_INTERNAL_ERROR, "Try to retransmit unsent control frame"); return false; @@ -333,7 +335,7 @@ bool QuicControlFrameManager::OnControlFrameIdAcked(QuicControlFrameId id) { return false; } if (id >= least_unsent_) { - QUIC_BUG << "Try to ack unsent control frame"; + QUIC_BUG(quic_bug_10517_4) << "Try to ack unsent control frame"; delegate_->OnControlFrameManagerError(QUIC_INTERNAL_ERROR, "Try to ack unsent control frame"); return false; 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 e4a55260afe..1bb37035912 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 "quic/core/frames/quic_frame.h" #include "quic/core/quic_circular_deque.h" #include "quic/core/quic_connection_id.h" +#include "quic/core/quic_types.h" namespace quic { @@ -92,10 +93,11 @@ 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, - uint64_t retire_prior_to, - QuicUint128 stateless_reset_token); + void WriteOrBufferNewConnectionId( + const QuicConnectionId& connection_id, + uint64_t sequence_number, + uint64_t retire_prior_to, + const StatelessResetToken& stateless_reset_token); // Tries to send a RETIRE_CONNNECTION_ID frame. The frame is buffered if it // cannot be sent immediately. 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 5aed6e0c17f..9d6b434ef31 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 @@ -250,7 +250,8 @@ TEST_F(QuicControlFrameManagerTest, NewAndRetireConnectionIdFrames) { // Send NewConnectionIdFrame as frame 6. manager_->WriteOrBufferNewConnectionId( TestConnectionId(3), /*sequence_number=*/2, /*retire_prior_to=*/1, - /*stateless_reset_token=*/MakeQuicUint128(1, 1)); + /*stateless_reset_token=*/ + {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}); // Send RetireConnectionIdFrame as frame 7. manager_->WriteOrBufferRetireConnectionId(/*sequence_number=*/0); manager_->OnCanWrite(); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.cc index 29a4367d599..e9a99167f46 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.cc @@ -117,14 +117,14 @@ int QuicCryptoClientHandshaker::num_sent_client_hellos() const { } bool QuicCryptoClientHandshaker::IsResumption() const { - QUIC_BUG_IF(!one_rtt_keys_available_); + QUIC_BUG_IF(quic_bug_12522_1, !one_rtt_keys_available_); // While 0-RTT handshakes could be considered to be like resumption, QUIC // Crypto doesn't have the same notion of a resumption like TLS does. return false; } bool QuicCryptoClientHandshaker::EarlyDataAccepted() const { - QUIC_BUG_IF(!one_rtt_keys_available_); + QUIC_BUG_IF(quic_bug_12522_2, !one_rtt_keys_available_); return num_client_hellos_ == 1; } @@ -133,7 +133,7 @@ ssl_early_data_reason_t QuicCryptoClientHandshaker::EarlyDataReason() const { } bool QuicCryptoClientHandshaker::ReceivedInchoateReject() const { - QUIC_BUG_IF(!one_rtt_keys_available_); + QUIC_BUG_IF(quic_bug_12522_3, !one_rtt_keys_available_); return num_client_hellos_ >= 3; } 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 3160f583185..78d2f10a563 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 @@ -49,8 +49,9 @@ QuicCryptoClientStream::QuicCryptoClientStream( proof_handler, has_application_state); break; case PROTOCOL_UNSUPPORTED: - QUIC_BUG << "Attempting to create QuicCryptoClientStream for unknown " - "handshake protocol"; + QUIC_BUG(quic_bug_10296_1) + << "Attempting to create QuicCryptoClientStream for unknown " + "handshake protocol"; } } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_base.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_base.cc index b80d06dc1e5..708b1750166 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_base.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_base.cc @@ -42,9 +42,9 @@ std::unique_ptr<QuicCryptoServerStreamBase> CreateCryptoServerStream( case PROTOCOL_UNSUPPORTED: break; } - QUIC_BUG << "Unknown handshake protocol: " - << static_cast<int>( - session->connection()->version().handshake_protocol); + QUIC_BUG(quic_bug_10492_1) + << "Unknown handshake protocol: " + << static_cast<int>(session->connection()->version().handshake_protocol); return nullptr; } 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 1b2f6db928f..f8f34dfb22c 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 @@ -71,7 +71,8 @@ QuicByteCount QuicCryptoStream::CryptoMessageFramingOverhead( } void QuicCryptoStream::OnCryptoFrame(const QuicCryptoFrame& frame) { - QUIC_BUG_IF(!QuicVersionUsesCryptoFrames(session()->transport_version())) + QUIC_BUG_IF(quic_bug_12573_1, + !QuicVersionUsesCryptoFrames(session()->transport_version())) << "Versions less than 47 shouldn't receive CRYPTO frames"; EncryptionLevel level = session()->connection()->last_decrypted_level(); substreams_[level].sequencer.OnCryptoFrame(frame); @@ -85,7 +86,7 @@ void QuicCryptoStream::OnCryptoFrame(const QuicCryptoFrame& frame) { void QuicCryptoStream::OnStreamFrame(const QuicStreamFrame& frame) { if (QuicVersionUsesCryptoFrames(session()->transport_version())) { - QUIC_PEER_BUG + QUIC_PEER_BUG(quic_peer_bug_12573_2) << "Crypto data received in stream frame instead of crypto frame"; OnUnrecoverableError(QUIC_INVALID_STREAM_DATA, "Unexpected stream frame"); } @@ -155,7 +156,7 @@ void QuicCryptoStream::WriteCryptoData(EncryptionLevel level, return; } if (data.empty()) { - QUIC_BUG << "Empty crypto data being written"; + QUIC_BUG(quic_bug_10322_1) << "Empty crypto data being written"; return; } const bool had_buffered_data = HasBufferedCryptoFrames(); @@ -166,7 +167,7 @@ void QuicCryptoStream::WriteCryptoData(EncryptionLevel level, send_buffer->SaveStreamData(&iov, /*iov_count=*/1, /*iov_offset=*/0, data.length()); if (kMaxStreamLength - offset < data.length()) { - QUIC_BUG << "Writing too much crypto handshake data"; + QUIC_BUG(quic_bug_10322_2) << "Writing too much crypto handshake data"; // TODO(nharper): Switch this to an IETF QUIC error code, possibly // INTERNAL_ERROR? OnUnrecoverableError(QUIC_STREAM_LENGTH_OVERFLOW, @@ -231,7 +232,8 @@ void QuicCryptoStream::NeuterStreamDataOfEncryptionLevel( void QuicCryptoStream::OnStreamDataConsumed(QuicByteCount bytes_consumed) { if (QuicVersionUsesCryptoFrames(session()->transport_version())) { - QUIC_BUG << "Stream data consumed when CRYPTO frames should be in use"; + QUIC_BUG(quic_bug_10322_3) + << "Stream data consumed when CRYPTO frames should be in use"; } if (bytes_consumed > 0) { bytes_consumed_[session()->connection()->encryption_level()].Add( @@ -263,7 +265,8 @@ bool QuicCryptoStream::HasPendingCryptoRetransmission() const { } void QuicCryptoStream::WritePendingCryptoRetransmission() { - QUIC_BUG_IF(!QuicVersionUsesCryptoFrames(session()->transport_version())) + QUIC_BUG_IF(quic_bug_12573_3, + !QuicVersionUsesCryptoFrames(session()->transport_version())) << "Versions less than 47 don't write CRYPTO frames"; for (EncryptionLevel level : AllEncryptionLevels()) { QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer; @@ -381,14 +384,16 @@ bool QuicCryptoStream::WriteCryptoFrame(EncryptionLevel level, QuicStreamOffset offset, QuicByteCount data_length, QuicDataWriter* writer) { - QUIC_BUG_IF(!QuicVersionUsesCryptoFrames(session()->transport_version())) + QUIC_BUG_IF(quic_bug_12573_4, + !QuicVersionUsesCryptoFrames(session()->transport_version())) << "Versions less than 47 don't write CRYPTO frames (2)"; return substreams_[level].send_buffer.WriteStreamData(offset, data_length, writer); } void QuicCryptoStream::OnCryptoFrameLost(QuicCryptoFrame* crypto_frame) { - QUIC_BUG_IF(!QuicVersionUsesCryptoFrames(session()->transport_version())) + QUIC_BUG_IF(quic_bug_12573_5, + !QuicVersionUsesCryptoFrames(session()->transport_version())) << "Versions less than 47 don't lose CRYPTO frames"; substreams_[crypto_frame->level].send_buffer.OnStreamDataLost( crypto_frame->offset, crypto_frame->data_length); @@ -396,7 +401,8 @@ void QuicCryptoStream::OnCryptoFrameLost(QuicCryptoFrame* crypto_frame) { void QuicCryptoStream::RetransmitData(QuicCryptoFrame* crypto_frame, TransmissionType type) { - QUIC_BUG_IF(!QuicVersionUsesCryptoFrames(session()->transport_version())) + QUIC_BUG_IF(quic_bug_12573_6, + !QuicVersionUsesCryptoFrames(session()->transport_version())) << "Versions less than 47 don't retransmit CRYPTO frames"; QuicIntervalSet<QuicStreamOffset> retransmission( crypto_frame->offset, crypto_frame->offset + crypto_frame->data_length); @@ -421,7 +427,8 @@ void QuicCryptoStream::RetransmitData(QuicCryptoFrame* crypto_frame, } void QuicCryptoStream::WriteBufferedCryptoFrames() { - QUIC_BUG_IF(!QuicVersionUsesCryptoFrames(session()->transport_version())) + QUIC_BUG_IF(quic_bug_12573_7, + !QuicVersionUsesCryptoFrames(session()->transport_version())) << "Versions less than 47 don't use CRYPTO frames"; for (EncryptionLevel level : AllEncryptionLevels()) { QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer; @@ -443,7 +450,8 @@ void QuicCryptoStream::WriteBufferedCryptoFrames() { } bool QuicCryptoStream::HasBufferedCryptoFrames() const { - QUIC_BUG_IF(!QuicVersionUsesCryptoFrames(session()->transport_version())) + QUIC_BUG_IF(quic_bug_12573_8, + !QuicVersionUsesCryptoFrames(session()->transport_version())) << "Versions less than 47 don't use CRYPTO frames"; for (EncryptionLevel level : AllEncryptionLevels()) { const QuicStreamSendBuffer& send_buffer = substreams_[level].send_buffer; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_data_writer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_data_writer.cc index 4ed9b4584c1..ee3c861ee8c 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_data_writer.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_data_writer.cc @@ -201,8 +201,8 @@ bool QuicDataWriter::WriteVarInt62( const QuicVariableLengthIntegerLength min_length = GetVarInt62Len(value); if (write_length < min_length) { - QUIC_BUG << "Cannot write value " << value << " with write_length " - << write_length; + QUIC_BUG(quic_bug_10347_1) << "Cannot write value " << value + << " with write_length " << write_length; return false; } if (write_length == min_length) { @@ -220,15 +220,16 @@ bool QuicDataWriter::WriteVarInt62( WriteUInt32(value); } - QUIC_BUG << "Invalid write_length " << static_cast<int>(write_length); + QUIC_BUG(quic_bug_10347_2) + << "Invalid write_length " << static_cast<int>(write_length); return false; } // static QuicVariableLengthIntegerLength QuicDataWriter::GetVarInt62Len(uint64_t value) { if ((value & kVarInt62ErrorMask) != 0) { - QUIC_BUG << "Attempted to encode a value, " << value - << ", that is too big for VarInt62"; + QUIC_BUG(quic_bug_10347_3) << "Attempted to encode a value, " << value + << ", that is too big for VarInt62"; return VARIABLE_LENGTH_INTEGER_LENGTH_0; } if ((value & kVarInt62Mask8Bytes) != 0) { 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 d0d44b6a1ab..0ed002a6850 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 @@ -25,7 +25,6 @@ #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_ptr_util.h" #include "quic/platform/api/quic_stack_trace.h" #include "common/platform/api/quiche_text_utils.h" @@ -176,7 +175,7 @@ class StatelessConnectionTerminator { /*transport_close_frame_type=*/0); if (!creator_.AddFrame(QuicFrame(frame), NOT_RETRANSMISSION)) { - QUIC_BUG << "Unable to add frame to an empty packet"; + QUIC_BUG(quic_bug_10287_1) << "Unable to add frame to an empty packet"; delete frame; return; } @@ -192,8 +191,8 @@ class StatelessConnectionTerminator { QuicTimeWaitListManager* time_wait_list_manager_; }; -// Class which extracts the ALPN from a QUIC_CRYPTO CHLO packet. -class ChloAlpnExtractor : public ChloExtractor::Delegate { +// Class which extracts the ALPN and SNI from a QUIC_CRYPTO CHLO packet. +class ChloAlpnSniExtractor : public ChloExtractor::Delegate { public: void OnChlo(QuicTransportVersion version, QuicConnectionId /*server_connection_id*/, @@ -202,6 +201,10 @@ class ChloAlpnExtractor : public ChloExtractor::Delegate { if (chlo.GetStringPiece(kALPN, &alpn_value)) { alpn_ = std::string(alpn_value); } + absl::string_view sni; + if (chlo.GetStringPiece(quic::kSNI, &sni)) { + sni_ = std::string(sni); + } if (version == LegacyVersionForEncapsulation().transport_version) { absl::string_view qlve_value; if (chlo.GetStringPiece(kQLVE, &qlve_value)) { @@ -212,18 +215,21 @@ class ChloAlpnExtractor : public ChloExtractor::Delegate { std::string&& ConsumeAlpn() { return std::move(alpn_); } + std::string&& ConsumeSni() { return std::move(sni_); } + std::string&& ConsumeLegacyVersionEncapsulationInnerPacket() { return std::move(legacy_version_encapsulation_inner_packet_); } private: std::string alpn_; + std::string sni_; std::string legacy_version_encapsulation_inner_packet_; }; bool MaybeHandleLegacyVersionEncapsulation( QuicDispatcher* dispatcher, - ChloAlpnExtractor* alpn_extractor, + ChloAlpnSniExtractor* alpn_extractor, const ReceivedPacketInfo& packet_info) { std::string legacy_version_encapsulation_inner_packet = alpn_extractor->ConsumeLegacyVersionEncapsulationInnerPacket(); @@ -265,9 +271,10 @@ bool MaybeHandleLegacyVersionEncapsulation( } if (legacy_version_encapsulation_inner_packet.length() >= packet_info.packet.length()) { - QUIC_BUG << "Inner packet cannot be larger than outer " - << legacy_version_encapsulation_inner_packet.length() << " vs " - << packet_info.packet.length(); + QUIC_BUG(quic_bug_10287_2) + << "Inner packet cannot be larger than outer " + << legacy_version_encapsulation_inner_packet.length() << " vs " + << packet_info.packet.length(); return false; } @@ -327,7 +334,7 @@ QuicDispatcher::QuicDispatcher( if (use_reference_counted_session_map_) { QUIC_RESTART_FLAG_COUNT(quic_use_reference_counted_sesssion_map); } - QUIC_BUG_IF(GetSupportedVersions().empty()) + 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()); @@ -515,7 +522,7 @@ bool QuicDispatcher::MaybeDispatchPacket( packet_info.version == LegacyVersionForEncapsulation()) { // This packet is using the Legacy Version Encapsulation version but the // corresponding session isn't, attempt extraction of inner packet. - ChloAlpnExtractor alpn_extractor; + ChloAlpnSniExtractor alpn_extractor; if (ChloExtractor::Extract(packet_info.packet, packet_info.version, config_->create_session_tag_indicators(), &alpn_extractor, @@ -541,7 +548,7 @@ bool QuicDispatcher::MaybeDispatchPacket( packet_info.version == LegacyVersionForEncapsulation()) { // This packet is using the Legacy Version Encapsulation version but the // corresponding session isn't, attempt extraction of inner packet. - ChloAlpnExtractor alpn_extractor; + ChloAlpnSniExtractor alpn_extractor; if (ChloExtractor::Extract(packet_info.packet, packet_info.version, config_->create_session_tag_indicators(), &alpn_extractor, @@ -608,7 +615,7 @@ bool QuicDispatcher::MaybeDispatchPacket( time_wait_list_manager_->ProcessPacket( packet_info.self_address, packet_info.peer_address, packet_info.destination_connection_id, packet_info.form, - GetPerPacketContext()); + packet_info.packet.length(), GetPerPacketContext()); return true; } @@ -629,7 +636,7 @@ bool QuicDispatcher::MaybeDispatchPacket( time_wait_list_manager()->ProcessPacket( packet_info.self_address, packet_info.peer_address, packet_info.destination_connection_id, packet_info.form, - GetPerPacketContext()); + packet_info.packet.length(), GetPerPacketContext()); OnNewConnectionRejected(); return true; } @@ -676,11 +683,12 @@ void QuicDispatcher::ProcessHeader(ReceivedPacketInfo* packet_info) { packet_info->destination_connection_id; // Packet's connection ID is unknown. Apply the validity checks. QuicPacketFate fate = ValidityChecks(*packet_info); - ChloAlpnExtractor alpn_extractor; + ChloAlpnSniExtractor alpn_extractor; switch (fate) { case kFateProcess: { if (packet_info->version.handshake_protocol == PROTOCOL_TLS1_3) { bool has_full_tls_chlo = false; + std::string sni; std::vector<std::string> alpns; if (buffered_packets_.HasBufferedPackets( packet_info->destination_connection_id)) { @@ -689,7 +697,7 @@ void QuicDispatcher::ProcessHeader(ReceivedPacketInfo* packet_info) { has_full_tls_chlo = buffered_packets_.IngestPacketForTlsChloExtraction( packet_info->destination_connection_id, packet_info->version, - packet_info->packet, &alpns); + packet_info->packet, &alpns, &sni); } else { // If we do not have a BufferedPacketList for this connection ID, // create a single-use one to check whether this packet contains a @@ -701,10 +709,11 @@ void QuicDispatcher::ProcessHeader(ReceivedPacketInfo* packet_info) { // This packet contains a full single-packet CHLO. has_full_tls_chlo = true; alpns = tls_chlo_extractor.alpns(); + sni = tls_chlo_extractor.server_name(); } } if (has_full_tls_chlo) { - ProcessChlo(alpns, packet_info); + ProcessChlo(alpns, sni, packet_info); } else { // This packet does not contain a full CHLO. It could be a 0-RTT // packet that arrived before the CHLO (due to loss or reordering), @@ -741,7 +750,8 @@ void QuicDispatcher::ProcessHeader(ReceivedPacketInfo* packet_info) { break; } - ProcessChlo({alpn_extractor.ConsumeAlpn()}, packet_info); + ProcessChlo({alpn_extractor.ConsumeAlpn()}, alpn_extractor.ConsumeSni(), + packet_info); } break; case kFateTimeWait: // Add this connection_id to the time-wait state, to safely reject @@ -759,7 +769,8 @@ void QuicDispatcher::ProcessHeader(ReceivedPacketInfo* packet_info) { server_connection_id)); time_wait_list_manager_->ProcessPacket( packet_info->self_address, packet_info->peer_address, - server_connection_id, packet_info->form, GetPerPacketContext()); + server_connection_id, packet_info->form, packet_info->packet.length(), + GetPerPacketContext()); buffered_packets_.DiscardPackets(server_connection_id); break; @@ -801,7 +812,9 @@ QuicDispatcher::QuicPacketFate QuicDispatcher::ValidityChecks( void QuicDispatcher::CleanUpSession(QuicConnectionId server_connection_id, QuicConnection* connection, - ConnectionCloseSource /*source*/) { + QuicErrorCode error, + const std::string& error_details, + ConnectionCloseSource source) { write_blocked_list_.erase(connection); QuicTimeWaitListManager::TimeWaitAction action = QuicTimeWaitListManager::SEND_STATELESS_RESET; @@ -810,6 +823,14 @@ void QuicDispatcher::CleanUpSession(QuicConnectionId server_connection_id, action = QuicTimeWaitListManager::SEND_CONNECTION_CLOSE_PACKETS; } else { if (!connection->IsHandshakeComplete()) { + const bool fix_dispatcher_sent_error_code = + GetQuicReloadableFlag(quic_fix_dispatcher_sent_error_code) && + source == ConnectionCloseSource::FROM_SELF; + // TODO(fayang): Do not serialize connection close packet if the + // connection is closed by the client. + if (fix_dispatcher_sent_error_code) { + QUIC_RELOADABLE_FLAG_COUNT(quic_fix_dispatcher_sent_error_code); + } if (!connection->version().HasIetfInvariantHeader()) { QUIC_CODE_COUNT(gquic_add_to_time_wait_list_with_handshake_failed); } else { @@ -818,20 +839,22 @@ void QuicDispatcher::CleanUpSession(QuicConnectionId server_connection_id, if (support_multiple_cid_per_connection_) { QUIC_RESTART_FLAG_COUNT_N( quic_dispatcher_support_multiple_cid_per_connection_v2, 1, 2); - // This serializes a connection close termination packet with error code - // QUIC_HANDSHAKE_FAILED and adds the connection to the time wait list. + // This serializes a connection close termination packet and adds the + // connection to the time wait list. StatelessConnectionTerminator terminator( server_connection_id, connection->version(), helper_.get(), time_wait_list_manager_.get()); terminator.CloseConnection( - QUIC_HANDSHAKE_FAILED, - "Connection is closed by server before handshake confirmed", + fix_dispatcher_sent_error_code ? error : QUIC_HANDSHAKE_FAILED, + fix_dispatcher_sent_error_code + ? error_details + : "Connection is closed by server before handshake confirmed", connection->version().HasIetfInvariantHeader(), connection->GetActiveServerConnectionIds()); } else { action = QuicTimeWaitListManager::SEND_TERMINATION_PACKETS; - // This serializes a connection close termination packet with error code - // QUIC_HANDSHAKE_FAILED and adds the connection to the time wait list. + // This serializes a connection close termination packet and adds the + // connection to the time wait list. StatelesslyTerminateConnection( connection->connection_id(), connection->version().HasIetfInvariantHeader() @@ -839,8 +862,11 @@ void QuicDispatcher::CleanUpSession(QuicConnectionId server_connection_id, : GOOGLE_QUIC_PACKET, /*version_flag=*/true, connection->version().HasLengthPrefixedConnectionIds(), - connection->version(), QUIC_HANDSHAKE_FAILED, - "Connection is closed by server before handshake confirmed", + connection->version(), + fix_dispatcher_sent_error_code ? error : QUIC_HANDSHAKE_FAILED, + fix_dispatcher_sent_error_code + ? error_details + : "Connection is closed by server before handshake confirmed", // Although it is our intention to send termination packets, the // |action| argument is not used by this call to // StatelesslyTerminateConnection(). @@ -915,7 +941,7 @@ void QuicDispatcher::DeleteSessions() { if (!write_blocked_list_.empty()) { for (const auto& session : closed_ref_counted_session_list_) { if (write_blocked_list_.erase(session->connection()) != 0) { - QUIC_BUG + QUIC_BUG(quic_bug_12724_2) << "QuicConnection was in WriteBlockedList before destruction " << session->connection()->connection_id(); } @@ -926,7 +952,7 @@ void QuicDispatcher::DeleteSessions() { if (!write_blocked_list_.empty()) { for (const std::unique_ptr<QuicSession>& session : closed_session_list_) { if (write_blocked_list_.erase(session->connection()) != 0) { - QUIC_BUG + QUIC_BUG(quic_bug_12724_3) << "QuicConnection was in WriteBlockedList before destruction " << session->connection()->connection_id(); } @@ -1002,10 +1028,11 @@ void QuicDispatcher::OnConnectionClosed(QuicConnectionId server_connection_id, if (use_reference_counted_session_map_) { auto it = reference_counted_session_map_.find(server_connection_id); if (it == reference_counted_session_map_.end()) { - QUIC_BUG << "ConnectionId " << server_connection_id - << " does not exist in the session map. Error: " - << QuicErrorCodeToString(error); - QUIC_BUG << QuicStackTrace(); + QUIC_BUG(quic_bug_10287_3) + << "ConnectionId " << server_connection_id + << " does not exist in the session map. Error: " + << QuicErrorCodeToString(error); + QUIC_BUG(quic_bug_10287_4) << QuicStackTrace(); return; } @@ -1024,7 +1051,7 @@ void QuicDispatcher::OnConnectionClosed(QuicConnectionId server_connection_id, } closed_ref_counted_session_list_.push_back(std::move(it->second)); } - CleanUpSession(it->first, connection, source); + CleanUpSession(it->first, connection, error, error_details, source); if (support_multiple_cid_per_connection_) { QUIC_RESTART_FLAG_COUNT_N( quic_dispatcher_support_multiple_cid_per_connection_v2, 1, 2); @@ -1039,10 +1066,11 @@ void QuicDispatcher::OnConnectionClosed(QuicConnectionId server_connection_id, } else { auto it = session_map_.find(server_connection_id); if (it == session_map_.end()) { - QUIC_BUG << "ConnectionId " << server_connection_id - << " does not exist in the session map. Error: " - << QuicErrorCodeToString(error); - QUIC_BUG << QuicStackTrace(); + QUIC_BUG(quic_bug_10287_5) + << "ConnectionId " << server_connection_id + << " does not exist in the session map. Error: " + << QuicErrorCodeToString(error); + QUIC_BUG(quic_bug_10287_6) << QuicStackTrace(); return; } @@ -1061,7 +1089,7 @@ void QuicDispatcher::OnConnectionClosed(QuicConnectionId server_connection_id, } closed_session_list_.push_back(std::move(it->second)); } - CleanUpSession(it->first, connection, source); + CleanUpSession(it->first, connection, error, error_details, source); session_map_.erase(it); } } @@ -1071,7 +1099,7 @@ void QuicDispatcher::OnWriteBlocked( if (!blocked_writer->IsWriterBlocked()) { // It is a programming error if this ever happens. When we are sure it is // not happening, replace it with a QUICHE_DCHECK. - QUIC_BUG + QUIC_BUG(quic_bug_12724_4) << "Tried to add writer into blocked list when it shouldn't be added"; // Return without adding the connection to the blocked list, to avoid // infinite loops in OnCanWrite. @@ -1092,10 +1120,10 @@ void QuicDispatcher::OnNewConnectionIdSent( QUICHE_DCHECK(support_multiple_cid_per_connection_); auto it = reference_counted_session_map_.find(server_connection_id); if (it == reference_counted_session_map_.end()) { - QUIC_BUG << "Couldn't locate the session that issues the connection ID in " - "reference_counted_session_map_. server_connection_id:" - << server_connection_id - << " new_connection_id: " << new_connection_id; + QUIC_BUG(quic_bug_10287_7) + << "Couldn't locate the session that issues the connection ID in " + "reference_counted_session_map_. server_connection_id:" + << server_connection_id << " new_connection_id: " << new_connection_id; return; } auto insertion_result = reference_counted_session_map_.insert( @@ -1151,6 +1179,9 @@ void QuicDispatcher::StatelesslyTerminateConnection( terminator.CloseConnection( error_code, error_details, format != GOOGLE_QUIC_PACKET, /*active_connection_ids=*/{server_connection_id}); + QUIC_CODE_COUNT(quic_dispatcher_generated_connection_close); + QuicSession::RecordConnectionCloseAtServer( + error_code, ConnectionCloseSource::FROM_SELF); return; } @@ -1209,9 +1240,10 @@ void QuicDispatcher::ProcessBufferedChlos(size_t max_connections_to_create) { server_connection_id = MaybeReplaceServerConnectionId(server_connection_id, packet_list.version); std::string alpn = SelectAlpn(packet_list.alpns); - std::unique_ptr<QuicSession> session = CreateQuicSession( - server_connection_id, packets.front().self_address, - packets.front().peer_address, alpn, packet_list.version); + std::unique_ptr<QuicSession> session = + CreateQuicSession(server_connection_id, packets.front().self_address, + packets.front().peer_address, alpn, + packet_list.version, packet_list.sni); if (original_connection_id != server_connection_id) { session->connection()->SetOriginalDestinationConnectionId( original_connection_id); @@ -1223,7 +1255,7 @@ void QuicDispatcher::ProcessBufferedChlos(size_t max_connections_to_create) { std::make_pair(server_connection_id, std::shared_ptr<QuicSession>(std::move(session)))); if (!insertion_result.second) { - QUIC_BUG + QUIC_BUG(quic_bug_12724_5) << "Tried to add a session to session_map with existing connection " "id: " << server_connection_id; @@ -1234,7 +1266,7 @@ void QuicDispatcher::ProcessBufferedChlos(size_t max_connections_to_create) { } else { auto insertion_result = session_map_.insert( std::make_pair(server_connection_id, std::move(session))); - QUIC_BUG_IF(!insertion_result.second) + QUIC_BUG_IF(quic_bug_12724_6, !insertion_result.second) << "Tried to add a session to session_map with existing connection " "id: " << server_connection_id; @@ -1283,13 +1315,14 @@ void QuicDispatcher::BufferEarlyPacket(const ReceivedPacketInfo& packet_info) { packet_info.destination_connection_id, packet_info.form != GOOGLE_QUIC_PACKET, packet_info.packet, packet_info.self_address, packet_info.peer_address, /*is_chlo=*/false, - /*alpns=*/{}, packet_info.version); + /*alpns=*/{}, /*sni=*/absl::string_view(), packet_info.version); if (rs != EnqueuePacketResult::SUCCESS) { OnBufferPacketFailure(rs, packet_info.destination_connection_id); } } void QuicDispatcher::ProcessChlo(const std::vector<std::string>& alpns, + absl::string_view sni, ReceivedPacketInfo* packet_info) { if (!buffered_packets_.HasBufferedPackets( packet_info->destination_connection_id) && @@ -1299,13 +1332,13 @@ void QuicDispatcher::ProcessChlo(const std::vector<std::string>& alpns, if (GetQuicFlag(FLAGS_quic_allow_chlo_buffering) && new_sessions_allowed_per_event_loop_ <= 0) { // Can't create new session any more. Wait till next event loop. - QUIC_BUG_IF(buffered_packets_.HasChloForConnection( - packet_info->destination_connection_id)); + QUIC_BUG_IF(quic_bug_12724_7, buffered_packets_.HasChloForConnection( + packet_info->destination_connection_id)); EnqueuePacketResult rs = buffered_packets_.EnqueuePacket( packet_info->destination_connection_id, packet_info->form != GOOGLE_QUIC_PACKET, packet_info->packet, packet_info->self_address, packet_info->peer_address, - /*is_chlo=*/true, alpns, packet_info->version); + /*is_chlo=*/true, alpns, sni, packet_info->version); if (rs != EnqueuePacketResult::SUCCESS) { OnBufferPacketFailure(rs, packet_info->destination_connection_id); } @@ -1320,12 +1353,13 @@ void QuicDispatcher::ProcessChlo(const std::vector<std::string>& alpns, std::string alpn = SelectAlpn(alpns); std::unique_ptr<QuicSession> session = CreateQuicSession( packet_info->destination_connection_id, packet_info->self_address, - packet_info->peer_address, alpn, packet_info->version); + packet_info->peer_address, alpn, packet_info->version, sni); if (QUIC_PREDICT_FALSE(session == nullptr)) { - QUIC_BUG << "CreateQuicSession returned nullptr for " - << packet_info->destination_connection_id << " from " - << packet_info->peer_address << " to " << packet_info->self_address - << " ALPN \"" << alpn << "\" version " << packet_info->version; + QUIC_BUG(quic_bug_10287_8) + << "CreateQuicSession returned nullptr for " + << packet_info->destination_connection_id << " from " + << packet_info->peer_address << " to " << packet_info->self_address + << " ALPN \"" << alpn << "\" version " << packet_info->version; return; } if (original_connection_id != packet_info->destination_connection_id) { @@ -1342,9 +1376,10 @@ void QuicDispatcher::ProcessChlo(const std::vector<std::string>& alpns, packet_info->destination_connection_id, std::shared_ptr<QuicSession>(std::move(session.release())))); if (!insertion_result.second) { - QUIC_BUG << "Tried to add a session to session_map with existing " - "connection id: " - << packet_info->destination_connection_id; + QUIC_BUG(quic_bug_10287_9) + << "Tried to add a session to session_map with existing " + "connection id: " + << packet_info->destination_connection_id; } else if (support_multiple_cid_per_connection_) { ++num_sessions_in_session_map_; } @@ -1352,7 +1387,7 @@ void QuicDispatcher::ProcessChlo(const std::vector<std::string>& alpns, } else { auto insertion_result = session_map_.insert(std::make_pair( packet_info->destination_connection_id, std::move(session))); - QUIC_BUG_IF(!insertion_result.second) + QUIC_BUG_IF(quic_bug_12724_8, !insertion_result.second) << "Tried to add a session to session_map with existing connection id: " << packet_info->destination_connection_id; session_ptr = insertion_result.first->second.get(); @@ -1414,21 +1449,32 @@ bool QuicDispatcher::IsSupportedVersion(const ParsedQuicVersion version) { void QuicDispatcher::MaybeResetPacketsWithNoVersion( const ReceivedPacketInfo& packet_info) { QUICHE_DCHECK(!packet_info.version_flag); - const size_t MinValidPacketLength = - kPacketHeaderTypeSize + expected_server_connection_id_length_ + - PACKET_1BYTE_PACKET_NUMBER + /*payload size=*/1 + /*tag size=*/12; - if (packet_info.packet.length() < MinValidPacketLength) { - // The packet size is too small. - QUIC_CODE_COUNT(drop_too_small_packets); - return; + if (GetQuicReloadableFlag(quic_fix_stateless_reset) && + packet_info.form != GOOGLE_QUIC_PACKET) { + // Drop IETF packets smaller than the minimal stateless reset length. + if (packet_info.packet.length() <= + QuicFramer::GetMinStatelessResetPacketLength()) { + QUIC_CODE_COUNT(quic_drop_too_small_short_header_packets); + return; + } + } else { + const size_t MinValidPacketLength = + kPacketHeaderTypeSize + expected_server_connection_id_length_ + + PACKET_1BYTE_PACKET_NUMBER + /*payload size=*/1 + /*tag size=*/12; + if (packet_info.packet.length() < MinValidPacketLength) { + // The packet size is too small. + QUIC_CODE_COUNT(drop_too_small_packets); + return; + } + // TODO(fayang): Consider rate limiting reset packets if reset packet size > + // packet_length. } - // TODO(fayang): Consider rate limiting reset packets if reset packet size > - // packet_length. time_wait_list_manager()->SendPublicReset( packet_info.self_address, packet_info.peer_address, packet_info.destination_connection_id, - packet_info.form != GOOGLE_QUIC_PACKET, GetPerPacketContext()); + packet_info.form != GOOGLE_QUIC_PACKET, packet_info.packet.length(), + GetPerPacketContext()); } size_t QuicDispatcher::NumSessions() const { 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 4245a5df7fd..81749543d4d 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 @@ -180,7 +180,8 @@ class QUIC_NO_EXPORT QuicDispatcher const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, absl::string_view alpn, - const ParsedQuicVersion& version) = 0; + const ParsedQuicVersion& version, + absl::string_view sni) = 0; // Tries to validate and dispatch packet based on available information. // Returns true if packet is dropped or successfully dispatched (e.g., @@ -242,6 +243,7 @@ class QUIC_NO_EXPORT QuicDispatcher // Called when |packet_info| is a CHLO packet. Creates a new connection and // delivers any buffered packets for that connection id. void ProcessChlo(const std::vector<std::string>& alpns, + absl::string_view sni, ReceivedPacketInfo* packet_info); // Return true if dispatcher wants to destroy session outside of @@ -305,6 +307,8 @@ class QUIC_NO_EXPORT QuicDispatcher // from the map after that. void CleanUpSession(QuicConnectionId server_connection_id, QuicConnection* connection, + QuicErrorCode error, + const std::string& error_details, ConnectionCloseSource source); // Called to terminate a connection statelessly. Depending on |format|, either 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 3f2f8cc2bb3..3df8a76965d 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 @@ -139,7 +139,8 @@ class TestDispatcher : public QuicDispatcher { const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, absl::string_view alpn, - const quic::ParsedQuicVersion& version), + const quic::ParsedQuicVersion& version, + absl::string_view sni), (override)); MOCK_METHOD(bool, @@ -466,7 +467,7 @@ class QuicDispatcherTestBase : public QuicTestWithParam<ParsedQuicVersion> { QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, _, client_address, - Eq(ExpectedAlpnForVersion(version)), _)) + Eq(ExpectedAlpnForVersion(version)), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, connection_id, client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -487,7 +488,7 @@ class QuicDispatcherTestBase : public QuicTestWithParam<ParsedQuicVersion> { QuicConnectionId connection_id = TestConnectionId(++connection_id_); QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); EXPECT_CALL(*dispatcher_, - CreateQuicSession(connection_id, _, client_address, _, _)) + CreateQuicSession(connection_id, _, client_address, _, _, _)) .Times(0); ProcessFirstFlight(version, client_address, connection_id); } @@ -535,7 +536,7 @@ TEST_P(QuicDispatcherTestAllVersions, TlsClientHelloCreatesSession) { EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(1), _, client_address, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(1), client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -586,7 +587,7 @@ void QuicDispatcherTestBase::TestTlsMultiPacketClientHello( // Processing the second packet should create the new session. EXPECT_CALL(*dispatcher_, CreateQuicSession(server_connection_id, _, client_address, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, TestHostname())) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, server_connection_id, client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -653,7 +654,7 @@ TEST_P(QuicDispatcherTestAllVersions, LegacyVersionEncapsulation) { // Processing the packet should create a new session. EXPECT_CALL(*dispatcher_, CreateQuicSession(server_connection_id, _, client_address, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, server_connection_id, client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -678,7 +679,7 @@ TEST_P(QuicDispatcherTestAllVersions, ProcessPackets) { EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(1), _, client_address, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, TestHostname())) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(1), client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -695,7 +696,7 @@ TEST_P(QuicDispatcherTestAllVersions, ProcessPackets) { EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(2), _, client_address, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, TestHostname())) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(2), client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -725,7 +726,7 @@ TEST_P(QuicDispatcherTestAllVersions, DispatcherDoesNotRejectPacketNumberZero) { EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(1), _, client_address, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(1), client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -752,7 +753,7 @@ TEST_P(QuicDispatcherTestOneVersion, StatelessVersionNegotiation) { CreateTimeWaitListManager(); QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL( *time_wait_list_manager_, SendVersionNegotiationPacket(TestConnectionId(1), _, _, _, _, _, _, _)) @@ -767,7 +768,7 @@ TEST_P(QuicDispatcherTestOneVersion, CreateTimeWaitListManager(); QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, SendVersionNegotiationPacket(connection_id, _, _, _, _, _, _, _)) .Times(1); @@ -780,7 +781,7 @@ TEST_P(QuicDispatcherTestOneVersion, CreateTimeWaitListManager(); QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, SendVersionNegotiationPacket( TestConnectionId(1), TestConnectionId(2), _, _, _, _, _, _)) @@ -793,7 +794,7 @@ TEST_P(QuicDispatcherTestOneVersion, NoVersionNegotiationWithSmallPacket) { CreateTimeWaitListManager(); QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, SendVersionNegotiationPacket(_, _, _, _, _, _, _, _)) .Times(0); @@ -817,7 +818,7 @@ TEST_P(QuicDispatcherTestOneVersion, CreateTimeWaitListManager(); QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, SendVersionNegotiationPacket(_, _, _, _, _, _, _, _)) .Times(1); @@ -836,7 +837,7 @@ TEST_P(QuicDispatcherTestAllVersions, Shutdown) { QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); EXPECT_CALL(*dispatcher_, - CreateQuicSession(_, _, client_address, Eq(ExpectedAlpn()), _)) + CreateQuicSession(_, _, client_address, Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(1), client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -865,7 +866,7 @@ TEST_P(QuicDispatcherTestAllVersions, TimeWaitListManager) { QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); QuicConnectionId connection_id = TestConnectionId(1); EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, _, client_address, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, connection_id, client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -891,7 +892,7 @@ TEST_P(QuicDispatcherTestAllVersions, TimeWaitListManager) { // Dispatcher forwards subsequent packets for this connection_id to the time // wait list manager. EXPECT_CALL(*time_wait_list_manager_, - ProcessPacket(_, _, connection_id, _, _)) + ProcessPacket(_, _, connection_id, _, _, _)) .Times(1); EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) .Times(0); @@ -905,13 +906,13 @@ TEST_P(QuicDispatcherTestAllVersions, NoVersionPacketToTimeWaitListManager) { QuicConnectionId connection_id = TestConnectionId(1); // Dispatcher forwards all packets for this connection_id to the time wait // list manager. - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, - ProcessPacket(_, _, connection_id, _, _)) + ProcessPacket(_, _, connection_id, _, _, _)) .Times(0); EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) .Times(0); - EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _)) + EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) .Times(1); ProcessPacket(client_address, connection_id, /*has_version_flag=*/false, "data"); @@ -922,19 +923,20 @@ TEST_P(QuicDispatcherTestAllVersions, QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); CreateTimeWaitListManager(); - char short_packet[22] = {0x70, 0xa7, 0x02, 0x6b}; - QuicReceivedPacket packet(short_packet, 22, QuicTime::Zero()); + char short_packet[21] = {0x70, 0xa7, 0x02, 0x6b}; + QuicReceivedPacket packet(short_packet, 21, QuicTime::Zero()); char valid_size_packet[23] = {0x70, 0xa7, 0x02, 0x6c}; QuicReceivedPacket packet2(valid_size_packet, 23, QuicTime::Zero()); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); - EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _)) + .Times(0); EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) .Times(0); // Verify small packet is silently dropped. - EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _)) + EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) .Times(0); dispatcher_->ProcessPacket(server_address_, client_address, packet); - EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _)) + EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _)) .Times(1); dispatcher_->ProcessPacket(server_address_, client_address, packet2); } @@ -954,7 +956,7 @@ TEST_P(QuicDispatcherTestAllVersions, LongConnectionIdLengthReplaced) { EXPECT_CALL(*dispatcher_, CreateQuicSession(fixed_connection_id, _, client_address, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, fixed_connection_id, client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -991,7 +993,7 @@ TEST_P(QuicDispatcherTestAllVersions, InvalidShortConnectionIdLengthReplaced) { EXPECT_CALL(*dispatcher_, CreateQuicSession(fixed_connection_id, _, client_address, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, fixed_connection_id, client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -1022,7 +1024,7 @@ TEST_P(QuicDispatcherTestAllVersions, MixGoodAndBadConnectionIdLengthPackets) { EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(1), _, client_address, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(1), client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -1039,7 +1041,7 @@ TEST_P(QuicDispatcherTestAllVersions, MixGoodAndBadConnectionIdLengthPackets) { EXPECT_CALL(*dispatcher_, CreateQuicSession(fixed_connection_id, _, client_address, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, fixed_connection_id, client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -1070,10 +1072,11 @@ TEST_P(QuicDispatcherTestAllVersions, ProcessPacketWithZeroPort) { QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 0); // dispatcher_ should drop this packet. - EXPECT_CALL(*dispatcher_, - CreateQuicSession(TestConnectionId(1), _, client_address, _, _)) + 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_, ProcessPacket(_, _, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) .Times(0); ProcessPacket(client_address, TestConnectionId(1), /*has_version_flag=*/true, @@ -1090,8 +1093,9 @@ TEST_P(QuicDispatcherTestAllVersions, QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); // dispatcher_ should drop this packet. - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); - EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _)) + .Times(0); EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) .Times(0); ProcessFirstFlight(client_address, EmptyQuicConnectionId()); @@ -1105,7 +1109,7 @@ void QuicDispatcherTestBase:: QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, SendVersionNegotiationPacket( server_connection_id, client_connection_id, @@ -1160,7 +1164,7 @@ TEST_P(QuicDispatcherTestOneVersion, 0xC0, 0xFF, 0x00, 0x00, 28, /*destination connection ID length*/ 0x08}; QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet), QuicTime::Zero()); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL( *time_wait_list_manager_, SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true, @@ -1177,7 +1181,7 @@ TEST_P(QuicDispatcherTestOneVersion, 0xC0, 0xFF, 0x00, 0x00, 27, /*destination connection ID length*/ 0x08}; QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet), QuicTime::Zero()); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL( *time_wait_list_manager_, SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true, @@ -1194,7 +1198,7 @@ TEST_P(QuicDispatcherTestOneVersion, 0xC0, 0xFF, 0x00, 0x00, 25, /*destination connection ID length*/ 0x08}; QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet), QuicTime::Zero()); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL( *time_wait_list_manager_, SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true, @@ -1211,7 +1215,7 @@ TEST_P(QuicDispatcherTestOneVersion, 0xC0, 'T', '0', '5', '0', /*destination connection ID length*/ 0x08}; QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet), QuicTime::Zero()); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL( *time_wait_list_manager_, SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true, @@ -1228,7 +1232,7 @@ TEST_P(QuicDispatcherTestOneVersion, 0xC0, 'Q', '0', '4', '9', /*destination connection ID length*/ 0x08}; QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet), QuicTime::Zero()); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL( *time_wait_list_manager_, SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true, @@ -1245,7 +1249,7 @@ TEST_P(QuicDispatcherTestOneVersion, 0xC0, 'Q', '0', '4', '8', /*connection ID length byte*/ 0x50}; QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet), QuicTime::Zero()); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL( *time_wait_list_manager_, SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true, @@ -1262,7 +1266,7 @@ TEST_P(QuicDispatcherTestOneVersion, 0xC0, 'Q', '0', '4', '7', /*connection ID length byte*/ 0x50}; QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet), QuicTime::Zero()); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL( *time_wait_list_manager_, SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true, @@ -1279,7 +1283,7 @@ TEST_P(QuicDispatcherTestOneVersion, 0xC0, 'Q', '0', '4', '5', /*connection ID length byte*/ 0x50}; QuicReceivedPacket received_packet(packet, ABSL_ARRAYSIZE(packet), QuicTime::Zero()); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL( *time_wait_list_manager_, SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true, @@ -1296,7 +1300,7 @@ TEST_P(QuicDispatcherTestOneVersion, 0xFF, 'Q', '0', '4', '4', /*connection ID length byte*/ 0x50}; QuicReceivedPacket received_packet44( packet44, kMinPacketSizeForVersionNegotiation, QuicTime::Zero()); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL( *time_wait_list_manager_, SendVersionNegotiationPacket(_, _, /*ietf_quic=*/true, @@ -1330,7 +1334,7 @@ TEST_P(QuicDispatcherTestOneVersion, VersionNegotiationProbe) { server_connection_id, client_connection_id, /*ietf_quic=*/true, /*use_length_prefix=*/true, _, _, _, _)) .Times(1); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); dispatcher_->ProcessPacket(server_address_, client_address, *received_packet); } @@ -1379,7 +1383,7 @@ TEST_P(QuicDispatcherTestOneVersion, VersionNegotiationProbeEndToEnd) { QuicEncryptedPacket encrypted(packet, sizeof(packet), false); std::unique_ptr<QuicReceivedPacket> received_packet( ConstructReceivedPacket(encrypted, mock_helper_.GetClock()->Now())); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); dispatcher_->ProcessPacket(server_address_, client_address, *received_packet); @@ -1430,7 +1434,7 @@ TEST_P(QuicDispatcherTestOneVersion, AndroidConformanceTest) { sizeof(packet), false); std::unique_ptr<QuicReceivedPacket> received_packet( ConstructReceivedPacket(encrypted, mock_helper_.GetClock()->Now())); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); dispatcher_->ProcessPacket(server_address_, client_address, *received_packet); @@ -1481,7 +1485,7 @@ TEST_P(QuicDispatcherTestOneVersion, AndroidConformanceTestOld) { sizeof(packet), false); std::unique_ptr<QuicReceivedPacket> received_packet( ConstructReceivedPacket(encrypted, mock_helper_.GetClock()->Now())); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); dispatcher_->ProcessPacket(server_address_, client_address, *received_packet); @@ -1503,7 +1507,7 @@ TEST_P(QuicDispatcherTestAllVersions, DoNotProcessSmallPacket) { CreateTimeWaitListManager(); QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, SendPacket(_, _, _)).Times(0); EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) .Times(0); @@ -1565,7 +1569,7 @@ TEST_P(QuicDispatcherTestAllVersions, StopAcceptingNewConnections) { EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(1), _, client_address, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(1), client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -1583,7 +1587,7 @@ TEST_P(QuicDispatcherTestAllVersions, StopAcceptingNewConnections) { // No more new connections afterwards. EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(2), _, client_address, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .Times(0u); ProcessFirstFlight(client_address, TestConnectionId(2)); @@ -1604,7 +1608,7 @@ TEST_P(QuicDispatcherTestAllVersions, StartAcceptingNewConnections) { // No more new connections afterwards. EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(2), _, client_address, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .Times(0u); ProcessFirstFlight(client_address, TestConnectionId(2)); @@ -1613,7 +1617,7 @@ TEST_P(QuicDispatcherTestAllVersions, StartAcceptingNewConnections) { EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(1), _, client_address, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(1), client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -1654,8 +1658,9 @@ TEST_P(QuicDispatcherTestStrayPacketConnectionId, QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); QuicConnectionId connection_id = TestConnectionId(1); - EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0); - EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)).Times(0); + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _)) + .Times(0); EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) .Times(0); @@ -1694,8 +1699,8 @@ class QuicDispatcherWriteBlockedListTest : public QuicDispatcherTestBase { QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); - EXPECT_CALL(*dispatcher_, - CreateQuicSession(_, _, client_address, Eq(ExpectedAlpn()), _)) + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, client_address, + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(1), client_address, &helper_, &alarm_factory_, &crypto_config_, @@ -1710,8 +1715,8 @@ class QuicDispatcherWriteBlockedListTest : public QuicDispatcherTestBase { ReceivedPacketInfoConnectionIdEquals(TestConnectionId(1)))); ProcessFirstFlight(client_address, TestConnectionId(1)); - EXPECT_CALL(*dispatcher_, - CreateQuicSession(_, _, client_address, Eq(ExpectedAlpn()), _)) + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, client_address, + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(2), client_address, &helper_, &alarm_factory_, &crypto_config_, @@ -1981,8 +1986,8 @@ class QuicDispatcherSupportMultipleConnectionIdPerConnectionTest } void AddConnection1() { QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1); - EXPECT_CALL(*dispatcher_, - CreateQuicSession(_, _, client_address, Eq(ExpectedAlpn()), _)) + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, client_address, + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(1), client_address, &helper_, &alarm_factory_, &crypto_config_, @@ -2000,8 +2005,8 @@ class QuicDispatcherSupportMultipleConnectionIdPerConnectionTest void AddConnection2() { QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 2); - EXPECT_CALL(*dispatcher_, - CreateQuicSession(_, _, client_address, Eq(ExpectedAlpn()), _)) + EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, client_address, + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(2), client_address, &helper_, &alarm_factory_, &crypto_config_, @@ -2221,8 +2226,9 @@ TEST_P(BufferedPacketStoreTest, ProcessNonChloPacketBeforeChlo) { // When CHLO arrives, a new session should be created, and all packets // buffered should be delivered to the session. - EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, _, client_addr_, - Eq(ExpectedAlpn()), _)) + EXPECT_CALL(*dispatcher_, + CreateQuicSession(conn_id, _, client_addr_, Eq(ExpectedAlpn()), _, + TestHostname())) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, conn_id, client_addr_, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -2257,7 +2263,7 @@ TEST_P(BufferedPacketStoreTest, ProcessNonChloPacketsUptoLimitAndProcessChlo) { // When CHLO arrives, a new session should be created, and all packets // buffered should be delivered to the session. EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, _, client_addr_, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, conn_id, client_addr_, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -2309,7 +2315,7 @@ TEST_P(BufferedPacketStoreTest, ReceivedPacketInfoConnectionIdEquals(conn_id))); } EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, _, client_address, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, conn_id, client_address, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -2337,7 +2343,7 @@ TEST_P(BufferedPacketStoreTest, DeliverEmptyPackets) { EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection( ReceivedPacketInfoConnectionIdEquals(conn_id))); EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, _, client_addr_, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, conn_id, client_addr_, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -2357,7 +2363,7 @@ TEST_P(BufferedPacketStoreTest, ReceiveRetransmittedCHLO) { // When CHLO arrives, a new session should be created, and all packets // buffered should be delivered to the session. EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, _, client_addr_, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .Times(1) // Only triggered by 1st CHLO. .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, conn_id, client_addr_, &mock_helper_, @@ -2404,7 +2410,7 @@ TEST_P(BufferedPacketStoreTest, ReceiveCHLOAfterExpiration) { // New arrived CHLO will be dropped because this connection is in time wait // list. ASSERT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id)); - EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, conn_id, _, _)); + EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, conn_id, _, _, _)); ProcessFirstFlight(conn_id); } @@ -2426,7 +2432,7 @@ TEST_P(BufferedPacketStoreTest, ProcessCHLOsUptoLimitAndBufferTheRest) { if (conn_id <= kMaxNumSessionsToCreate) { EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(conn_id), _, client_addr_, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, TestHostname())) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(conn_id), client_addr_, &mock_helper_, &mock_alarm_factory_, @@ -2461,7 +2467,7 @@ TEST_P(BufferedPacketStoreTest, ProcessCHLOsUptoLimitAndBufferTheRest) { ++conn_id) { EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(conn_id), _, client_addr_, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, TestHostname())) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(conn_id), client_addr_, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -2477,7 +2483,7 @@ TEST_P(BufferedPacketStoreTest, ProcessCHLOsUptoLimitAndBufferTheRest) { } EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(kNumCHLOs), _, client_addr_, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .Times(0); while (store->HasChlosBuffered()) { @@ -2497,7 +2503,7 @@ TEST_P(BufferedPacketStoreTest, BufferDuplicatedCHLO) { if (conn_id <= kMaxNumSessionsToCreate) { EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(conn_id), _, client_addr_, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(conn_id), client_addr_, &mock_helper_, &mock_alarm_factory_, @@ -2524,7 +2530,7 @@ TEST_P(BufferedPacketStoreTest, BufferDuplicatedCHLO) { // Reset counter and process buffered CHLO. EXPECT_CALL(*dispatcher_, CreateQuicSession(last_connection, _, client_addr_, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, last_connection, client_addr_, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -2550,7 +2556,7 @@ TEST_P(BufferedPacketStoreTest, BufferNonChloPacketsUptoLimitWithChloBuffered) { if (conn_id <= kMaxNumSessionsToCreate) { EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(conn_id), _, client_addr_, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(conn_id), client_addr_, &mock_helper_, &mock_alarm_factory_, @@ -2579,7 +2585,7 @@ TEST_P(BufferedPacketStoreTest, BufferNonChloPacketsUptoLimitWithChloBuffered) { // Reset counter and process buffered CHLO. EXPECT_CALL(*dispatcher_, CreateQuicSession(last_connection_id, _, client_addr_, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, last_connection_id, client_addr_, &mock_helper_, &mock_alarm_factory_, &crypto_config_, @@ -2615,7 +2621,7 @@ TEST_P(BufferedPacketStoreTest, ReceiveCHLOForBufferedConnection) { if (conn_id <= kMaxNumSessionsToCreate + 1) { EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionId(conn_id), _, client_addr_, - Eq(ExpectedAlpn()), _)) + Eq(ExpectedAlpn()), _, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(conn_id), client_addr_, &mock_helper_, &mock_alarm_factory_, @@ -2657,7 +2663,7 @@ TEST_P(BufferedPacketStoreTest, ProcessBufferedChloWithDifferentVersion) { EXPECT_CALL( *dispatcher_, CreateQuicSession(TestConnectionId(conn_id), _, client_addr_, - Eq(ExpectedAlpnForVersion(version)), version)) + Eq(ExpectedAlpnForVersion(version)), version, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(conn_id), client_addr_, &mock_helper_, &mock_alarm_factory_, @@ -2681,9 +2687,10 @@ TEST_P(BufferedPacketStoreTest, ProcessBufferedChloWithDifferentVersion) { conn_id <= last_connection_id; ++conn_id) { ParsedQuicVersion version = supported_versions[(conn_id - 1) % supported_versions.size()]; - EXPECT_CALL(*dispatcher_, - CreateQuicSession(TestConnectionId(conn_id), _, client_addr_, - Eq(ExpectedAlpnForVersion(version)), version)) + EXPECT_CALL( + *dispatcher_, + CreateQuicSession(TestConnectionId(conn_id), _, client_addr_, + Eq(ExpectedAlpnForVersion(version)), version, _)) .WillOnce(Return(ByMove(CreateSession( dispatcher_.get(), config_, TestConnectionId(conn_id), client_addr_, &mock_helper_, &mock_alarm_factory_, &crypto_config_, 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 dd8be33e747..e82e7cb2eab 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 @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "quic/core/quic_error_codes.h" + #include <cstdint> #include <cstring> #include "absl/strings/str_cat.h" #include "third_party/boringssl/src/include/openssl/ssl.h" -#include "quic/core/quic_error_codes.h" #include "quic/platform/api/quic_logging.h" namespace quic { @@ -54,6 +55,9 @@ const char* QuicRstStreamErrorCodeToString(QuicRstStreamErrorCode error) { RETURN_STRING_LITERAL(QUIC_STREAM_ENCODER_STREAM_ERROR); RETURN_STRING_LITERAL(QUIC_STREAM_DECODER_STREAM_ERROR); RETURN_STRING_LITERAL(QUIC_STREAM_UNKNOWN_APPLICATION_ERROR_CODE); + RETURN_STRING_LITERAL(QUIC_STREAM_WEBTRANSPORT_SESSION_GONE); + RETURN_STRING_LITERAL( + QUIC_STREAM_WEBTRANSPORT_BUFFERED_STREAMS_LIMIT_EXCEEDED); RETURN_STRING_LITERAL(QUIC_STREAM_LAST_ERROR); } // Return a default value so that we return this when |error| doesn't match @@ -874,6 +878,10 @@ uint64_t RstStreamErrorCodeToIetfResetStreamErrorCode( QuicHttpQpackErrorCode::DECODER_STREAM_ERROR); case QUIC_STREAM_UNKNOWN_APPLICATION_ERROR_CODE: return static_cast<uint64_t>(QuicHttp3ErrorCode::INTERNAL_ERROR); + case QUIC_STREAM_WEBTRANSPORT_SESSION_GONE: + return static_cast<uint64_t>(QuicHttp3ErrorCode::CONNECT_ERROR); + case QUIC_STREAM_WEBTRANSPORT_BUFFERED_STREAMS_LIMIT_EXCEEDED: + return static_cast<uint64_t>(QuicHttp3ErrorCode::CONNECT_ERROR); case QUIC_STREAM_LAST_ERROR: return static_cast<uint64_t>(QuicHttp3ErrorCode::INTERNAL_ERROR); } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h index 3a8069675f1..32816b869cf 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 @@ -103,8 +103,14 @@ enum QuicRstStreamErrorCode { // IETF RESET_FRAME application error code not matching any HTTP/3 or QPACK // error codes. QUIC_STREAM_UNKNOWN_APPLICATION_ERROR_CODE = 35, + // WebTransport session is going away, causing all underlying streams to be + // reset. + QUIC_STREAM_WEBTRANSPORT_SESSION_GONE = 36, + // There is no corresponding WebTransport session to associate this stream + // with, and the limit for buffered streams has been exceeded. + QUIC_STREAM_WEBTRANSPORT_BUFFERED_STREAMS_LIMIT_EXCEEDED = 37, // No error. Used as bound while iterating. - QUIC_STREAM_LAST_ERROR = 36, + QUIC_STREAM_LAST_ERROR = 38, }; // QuicRstStreamErrorCode is encoded as a single octet on-the-wire. static_assert(static_cast<int>(QUIC_STREAM_LAST_ERROR) <= 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 a3bc2251327..910db544eff 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,21 +6,21 @@ QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_abort_qpack_on_stream_reset, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_accept_empty_stream_frame_with_no_fin, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_ack_delay_alarm_granularity, false) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_ack_delay_alarm_granularity, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_add_stream_info_to_idle_close_detail, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_allocate_stream_sequencer_buffer_blocks_on_demand, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_allow_client_enabled_bbr_v2, false) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_alps_include_scheme_in_origin, true) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_and_tls_allow_sni_without_dots, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_avoid_too_low_probe_bw_cwnd, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_bw_startup, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_fewer_startup_round_trips, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_reset_max_bytes_delivered, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_use_bytes_delivered, true) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_fix_bw_lo_mode, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_can_send_ack_frequency, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_close_connection_on_0rtt_packet_number_higher_than_1rtt, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_close_connection_with_too_many_outstanding_packets, true) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_connection_support_multiple_cids_v2, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_conservative_bursts, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_conservative_cwnd_and_pacing_gains, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_count_bytes_on_alternative_path_seperately, false) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_count_bytes_on_alternative_path_seperately, true) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_crypto_postpone_cert_validate_for_server, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_enable_5rto_blackhole_detection2, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_on_pto, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_to_bbr, false) @@ -33,50 +33,48 @@ QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_q046, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_q050, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_disable_version_t051, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_discard_initial_packet_with_key_dropped, true) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_do_not_synthesize_source_cid_for_short_header, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_donot_reset_ideal_next_packet_send_time, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_dont_defer_sending, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_alps_client, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_alps_server, false) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_donot_write_mid_packet_processing, false) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_alps_client, true) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_alps_server, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_mtu_discovery_at_server, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_server_on_wire_ping, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_token_based_address_validation, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_version_rfcv1, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_encrypted_control_frames, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_encrypted_goaway, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_on_stream_reset, false) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_dispatcher_sent_error_code, false) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_key_update_on_first_packet, true) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_on_stream_reset, true) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_stateless_reset, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_willing_and_able_to_write2, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_goaway_with_max_stream_id, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_parse_accept_ch_frame, true) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_h3_datagram, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator, false) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_preempt_stream_data_with_handshake_packet, true) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_reject_unexpected_ietf_frame_types, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_require_handshake_confirmation, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_goaway_with_connection_close, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_path_response, false) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_path_response2, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_timestamps, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_tls_crypto_error_code, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_server_reverse_validate_new_path, false) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_server_reverse_validate_new_path3, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_single_ack_in_packet2, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_start_peer_migration_earlier, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_stateless_reset, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_stateless_reset_faster_random, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_testonly_default_false, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_testonly_default_true, true) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_use_normalized_sni_for_cert_selectioon, false) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_use_normalized_sni_for_cert_selectioon, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_use_per_handshaker_proof_source, true) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_retry_handshake_on_early_data, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_unified_iw_options, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_unify_stop_sending, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_update_packet_content_returns_connected, true) +QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_unify_stop_sending, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_use_encryption_level_context, true) QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_use_write_or_buffer_data_at_level, false) -QUIC_FLAG(FLAGS_quic_reloadable_flag_send_quic_fallback_server_config_on_leto_error, false) QUIC_FLAG(FLAGS_quic_restart_flag_dont_fetch_quic_private_keys_from_leto, false) -QUIC_FLAG(FLAGS_quic_restart_flag_http2_parse_priority_update_frame, true) QUIC_FLAG(FLAGS_quic_restart_flag_quic_dispatcher_support_multiple_cid_per_connection_v2, true) -QUIC_FLAG(FLAGS_quic_restart_flag_quic_enable_zero_rtt_for_tls_v2, true) QUIC_FLAG(FLAGS_quic_restart_flag_quic_offload_pacing_to_usps2, false) QUIC_FLAG(FLAGS_quic_restart_flag_quic_session_tickets_always_enabled, true) QUIC_FLAG(FLAGS_quic_restart_flag_quic_support_release_time_for_gso, false) QUIC_FLAG(FLAGS_quic_restart_flag_quic_testonly_default_false, false) QUIC_FLAG(FLAGS_quic_restart_flag_quic_testonly_default_true, true) QUIC_FLAG(FLAGS_quic_restart_flag_quic_time_wait_list_support_multiple_cid_v2, true) -QUIC_FLAG(FLAGS_quic_restart_flag_quic_tls_prefer_server_cipher_and_curve_list, true) QUIC_FLAG(FLAGS_quic_restart_flag_quic_use_reference_counted_sesssion_map, true) diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller.cc b/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller.cc index 01fb72ce008..88dc3edcd44 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller.cc @@ -91,9 +91,10 @@ bool QuicFlowController::UpdateHighestReceivedOffset( void QuicFlowController::AddBytesSent(QuicByteCount bytes_sent) { if (bytes_sent_ + bytes_sent > send_window_offset_) { - QUIC_BUG << ENDPOINT << LogLabel() << " Trying to send an extra " - << bytes_sent << " bytes, when bytes_sent = " << bytes_sent_ - << ", and send_window_offset_ = " << send_window_offset_; + QUIC_BUG(quic_bug_10836_1) + << ENDPOINT << LogLabel() << " Trying to send an extra " << bytes_sent + << " bytes, when bytes_sent = " << bytes_sent_ + << ", and send_window_offset_ = " << send_window_offset_; bytes_sent_ = send_window_offset_; // This is an error on our side, close the connection as soon as possible. @@ -298,8 +299,9 @@ void QuicFlowController::UpdateReceiveWindowSize(QuicStreamOffset size) { QUIC_DVLOG(1) << ENDPOINT << "UpdateReceiveWindowSize for " << LogLabel() << ": " << size; if (receive_window_size_ != receive_window_offset_) { - QUIC_BUG << "receive_window_size_:" << receive_window_size_ - << " != receive_window_offset:" << receive_window_offset_; + QUIC_BUG(quic_bug_10836_2) + << "receive_window_size_:" << receive_window_size_ + << " != receive_window_offset:" << receive_window_offset_; return; } receive_window_size_ = size; 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 fdccef9c72f..c5de97be541 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 @@ -166,7 +166,7 @@ QuicPacketNumberLength ReadSequenceNumberLength(uint8_t flags) { case PACKET_FLAGS_1BYTE_PACKET: return PACKET_1BYTE_PACKET_NUMBER; default: - QUIC_BUG << "Unreachable case statement."; + QUIC_BUG(quic_bug_10850_1) << "Unreachable case statement."; return PACKET_6BYTE_PACKET_NUMBER; } } @@ -183,7 +183,7 @@ QuicPacketNumberLength ReadAckPacketNumberLength( case PACKET_FLAGS_1BYTE_PACKET: return PACKET_1BYTE_PACKET_NUMBER; default: - QUIC_BUG << "Unreachable case statement."; + QUIC_BUG(quic_bug_10850_2) << "Unreachable case statement."; return PACKET_6BYTE_PACKET_NUMBER; } } @@ -211,7 +211,7 @@ uint8_t LongHeaderTypeToOnWireValue(QuicLongHeaderType type) { case VERSION_NEGOTIATION: return 0xF0; // Value does not matter default: - QUIC_BUG << "Invalid long header type: " << type; + QUIC_BUG(quic_bug_10850_3) << "Invalid long header type: " << type; return 0xFF; } } @@ -232,7 +232,7 @@ bool GetLongHeaderType(uint8_t type, QuicLongHeaderType* long_header_type) { *long_header_type = RETRY; break; default: - QUIC_BUG << "Unreachable statement"; + QUIC_BUG(quic_bug_10850_4) << "Unreachable statement"; *long_header_type = INVALID_PACKET_TYPE; return false; } @@ -247,7 +247,8 @@ QuicPacketNumberLength GetLongHeaderPacketNumberLength(uint8_t type) { PacketNumberSpace GetPacketNumberSpace(const QuicPacketHeader& header) { switch (header.form) { case GOOGLE_QUIC_PACKET: - QUIC_BUG << "Try to get packet number space of Google QUIC packet"; + QUIC_BUG(quic_bug_10850_5) + << "Try to get packet number space of Google QUIC packet"; break; case IETF_QUIC_SHORT_HEADER_PACKET: return APPLICATION_DATA; @@ -262,9 +263,9 @@ PacketNumberSpace GetPacketNumberSpace(const QuicPacketHeader& header) { case VERSION_NEGOTIATION: case RETRY: case INVALID_PACKET_TYPE: - QUIC_BUG << "Try to get packet number space of long header type: " - << QuicUtils::QuicLongHeaderTypetoString( - header.long_packet_type); + QUIC_BUG(quic_bug_10850_6) + << "Try to get packet number space of long header type: " + << QuicUtils::QuicLongHeaderTypetoString(header.long_packet_type); break; } } @@ -275,7 +276,8 @@ PacketNumberSpace GetPacketNumberSpace(const QuicPacketHeader& header) { EncryptionLevel GetEncryptionLevel(const QuicPacketHeader& header) { switch (header.form) { case GOOGLE_QUIC_PACKET: - QUIC_BUG << "Cannot determine EncryptionLevel from Google QUIC header"; + QUIC_BUG(quic_bug_10850_7) + << "Cannot determine EncryptionLevel from Google QUIC header"; break; case IETF_QUIC_SHORT_HEADER_PACKET: return ENCRYPTION_FORWARD_SECURE; @@ -290,9 +292,9 @@ EncryptionLevel GetEncryptionLevel(const QuicPacketHeader& header) { case VERSION_NEGOTIATION: case RETRY: case INVALID_PACKET_TYPE: - QUIC_BUG << "No encryption used with type " - << QuicUtils::QuicLongHeaderTypetoString( - header.long_packet_type); + QUIC_BUG(quic_bug_10850_8) + << "No encryption used with type " + << QuicUtils::QuicLongHeaderTypetoString(header.long_packet_type); } } return NUM_ENCRYPTION_LEVELS; @@ -433,6 +435,10 @@ QuicFramer::QuicFramer(const ParsedQuicVersionVector& supported_versions, version_ = supported_versions_[0]; QUICHE_DCHECK(version_.IsKnown()) << ParsedQuicVersionVectorToString(supported_versions_); + if (do_not_synthesize_source_cid_for_short_header_) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_do_not_synthesize_source_cid_for_short_header, 1, 3); + } } QuicFramer::~QuicFramer() {} @@ -466,7 +472,7 @@ size_t QuicFramer::GetMinCryptoFrameSize(QuicStreamOffset offset, size_t QuicFramer::GetMessageFrameSize(QuicTransportVersion version, bool last_frame_in_packet, QuicByteCount length) { - QUIC_BUG_IF(!VersionSupportsMessageFrames(version)) + QUIC_BUG_IF(quic_bug_12975_1, !VersionSupportsMessageFrames(version)) << "Try to serialize MESSAGE frame in " << version; return kQuicFrameTypeSize + (last_frame_in_packet ? 0 : QuicDataWriter::GetVarInt62Len(length)) + @@ -586,9 +592,10 @@ size_t QuicFramer::GetWindowUpdateFrameSize( size_t QuicFramer::GetMaxStreamsFrameSize(QuicTransportVersion version, const QuicMaxStreamsFrame& frame) { if (!VersionHasIetfQuicFrames(version)) { - QUIC_BUG << "In version " << version - << ", which does not support IETF Frames, and tried to serialize " - "MaxStreams Frame."; + QUIC_BUG(quic_bug_10850_9) + << "In version " << version + << ", which does not support IETF Frames, and tried to serialize " + "MaxStreams Frame."; } return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(frame.stream_count); @@ -599,9 +606,10 @@ size_t QuicFramer::GetStreamsBlockedFrameSize( QuicTransportVersion version, const QuicStreamsBlockedFrame& frame) { if (!VersionHasIetfQuicFrames(version)) { - QUIC_BUG << "In version " << version - << ", which does not support IETF frames, and tried to serialize " - "StreamsBlocked Frame."; + QUIC_BUG(quic_bug_10850_10) + << "In version " << version + << ", which does not support IETF frames, and tried to serialize " + "StreamsBlocked Frame."; } return kQuicFrameTypeSize + @@ -722,7 +730,7 @@ size_t QuicFramer::GetStreamIdSize(QuicStreamId stream_id) { return i; } } - QUIC_BUG << "Failed to determine StreamIDSize."; + QUIC_BUG(quic_bug_10850_11) << "Failed to determine StreamIDSize."; return 4; } @@ -740,7 +748,7 @@ size_t QuicFramer::GetStreamOffsetSize(QuicStreamOffset offset) { return i; } } - QUIC_BUG << "Failed to determine StreamOffsetSize."; + QUIC_BUG(quic_bug_10850_12) << "Failed to determine StreamOffsetSize."; return 8; } @@ -796,10 +804,11 @@ size_t QuicFramer::GetSerializedFrameLength( QuicPacketNumberLength packet_number_length) { // Prevent a rare crash reported in b/19458523. if (frame.type == ACK_FRAME && frame.ack_frame == nullptr) { - QUIC_BUG << "Cannot compute the length of a null ack frame. free_bytes:" - << free_bytes << " first_frame:" << first_frame - << " last_frame:" << last_frame - << " seq num length:" << packet_number_length; + QUIC_BUG(quic_bug_10850_13) + << "Cannot compute the length of a null ack frame. free_bytes:" + << free_bytes << " first_frame:" << first_frame + << " last_frame:" << last_frame + << " seq num length:" << packet_number_length; set_error(QUIC_INTERNAL_ERROR); visitor_->OnError(this); return 0; @@ -862,7 +871,7 @@ bool QuicFramer::WriteIetfLongHeaderLength(const QuicPacketHeader& header, writer->length() - length_field_offset < kQuicDefaultLongHeaderLengthLength) { set_detailed_error("Invalid length_field_offset."); - QUIC_BUG << "Invalid length_field_offset."; + QUIC_BUG(quic_bug_10850_14) << "Invalid length_field_offset."; return false; } size_t length_to_write = writer->length() - length_field_offset - @@ -875,7 +884,7 @@ bool QuicFramer::WriteIetfLongHeaderLength(const QuicPacketHeader& header, if (!length_writer.WriteVarInt62(length_to_write, kQuicDefaultLongHeaderLengthLength)) { set_detailed_error("Failed to overwrite long header length."); - QUIC_BUG << "Failed to overwrite long header length."; + QUIC_BUG(quic_bug_10850_15) << "Failed to overwrite long header length."; return false; } return true; @@ -886,13 +895,14 @@ size_t QuicFramer::BuildDataPacket(const QuicPacketHeader& header, char* buffer, size_t packet_length, EncryptionLevel level) { - QUIC_BUG_IF(header.version_flag && version().HasIetfInvariantHeader() && - header.long_packet_type == RETRY && !frames.empty()) + QUIC_BUG_IF(quic_bug_12975_2, + header.version_flag && version().HasIetfInvariantHeader() && + header.long_packet_type == RETRY && !frames.empty()) << "IETF RETRY packets cannot contain frames " << header; QuicDataWriter writer(packet_length, buffer); size_t length_field_offset = 0; if (!AppendPacketHeader(header, &writer, &length_field_offset)) { - QUIC_BUG << "AppendPacketHeader failed"; + QUIC_BUG(quic_bug_10850_16) << "AppendPacketHeader failed"; return 0; } @@ -912,35 +922,37 @@ size_t QuicFramer::BuildDataPacket(const QuicPacketHeader& header, // Determine if we should write stream frame length in header. const bool last_frame_in_packet = i == frames.size() - 1; if (!AppendTypeByte(frame, last_frame_in_packet, &writer)) { - QUIC_BUG << "AppendTypeByte failed"; + QUIC_BUG(quic_bug_10850_17) << "AppendTypeByte failed"; return 0; } switch (frame.type) { case PADDING_FRAME: if (!AppendPaddingFrame(frame.padding_frame, &writer)) { - QUIC_BUG << "AppendPaddingFrame of " - << frame.padding_frame.num_padding_bytes << " failed"; + QUIC_BUG(quic_bug_10850_18) + << "AppendPaddingFrame of " + << frame.padding_frame.num_padding_bytes << " failed"; return 0; } break; case STREAM_FRAME: if (!AppendStreamFrame(frame.stream_frame, last_frame_in_packet, &writer)) { - QUIC_BUG << "AppendStreamFrame failed"; + QUIC_BUG(quic_bug_10850_19) << "AppendStreamFrame failed"; return 0; } break; case ACK_FRAME: if (!AppendAckFrameAndTypeByte(*frame.ack_frame, &writer)) { - QUIC_BUG << "AppendAckFrameAndTypeByte failed: " << detailed_error_; + QUIC_BUG(quic_bug_10850_20) + << "AppendAckFrameAndTypeByte failed: " << detailed_error_; return 0; } break; case STOP_WAITING_FRAME: if (!AppendStopWaitingFrame(header, frame.stop_waiting_frame, &writer)) { - QUIC_BUG << "AppendStopWaitingFrame failed"; + QUIC_BUG(quic_bug_10850_21) << "AppendStopWaitingFrame failed"; return 0; } break; @@ -952,32 +964,32 @@ size_t QuicFramer::BuildDataPacket(const QuicPacketHeader& header, break; case RST_STREAM_FRAME: if (!AppendRstStreamFrame(*frame.rst_stream_frame, &writer)) { - QUIC_BUG << "AppendRstStreamFrame failed"; + QUIC_BUG(quic_bug_10850_22) << "AppendRstStreamFrame failed"; return 0; } break; case CONNECTION_CLOSE_FRAME: if (!AppendConnectionCloseFrame(*frame.connection_close_frame, &writer)) { - QUIC_BUG << "AppendConnectionCloseFrame failed"; + QUIC_BUG(quic_bug_10850_23) << "AppendConnectionCloseFrame failed"; return 0; } break; case GOAWAY_FRAME: if (!AppendGoAwayFrame(*frame.goaway_frame, &writer)) { - QUIC_BUG << "AppendGoAwayFrame failed"; + QUIC_BUG(quic_bug_10850_24) << "AppendGoAwayFrame failed"; return 0; } break; case WINDOW_UPDATE_FRAME: if (!AppendWindowUpdateFrame(*frame.window_update_frame, &writer)) { - QUIC_BUG << "AppendWindowUpdateFrame failed"; + QUIC_BUG(quic_bug_10850_25) << "AppendWindowUpdateFrame failed"; return 0; } break; case BLOCKED_FRAME: if (!AppendBlockedFrame(*frame.blocked_frame, &writer)) { - QUIC_BUG << "AppendBlockedFrame failed"; + QUIC_BUG(quic_bug_10850_26) << "AppendBlockedFrame failed"; return 0; } break; @@ -1017,7 +1029,7 @@ size_t QuicFramer::BuildDataPacket(const QuicPacketHeader& header, case MESSAGE_FRAME: if (!AppendMessageFrameAndTypeByte(*frame.message_frame, last_frame_in_packet, &writer)) { - QUIC_BUG << "AppendMessageFrame failed"; + QUIC_BUG(quic_bug_10850_27) << "AppendMessageFrame failed"; return 0; } break; @@ -1028,7 +1040,7 @@ size_t QuicFramer::BuildDataPacket(const QuicPacketHeader& header, return RaiseError(QUIC_INTERNAL_ERROR); } if (!AppendCryptoFrame(*frame.crypto_frame, &writer)) { - QUIC_BUG << "AppendCryptoFrame failed"; + QUIC_BUG(quic_bug_10850_28) << "AppendCryptoFrame failed"; return 0; } break; @@ -1037,7 +1049,7 @@ size_t QuicFramer::BuildDataPacket(const QuicPacketHeader& header, break; default: RaiseError(QUIC_INVALID_FRAME_DATA); - QUIC_BUG << "QUIC_INVALID_FRAME_DATA"; + QUIC_BUG(quic_bug_10850_29) << "QUIC_INVALID_FRAME_DATA"; return 0; } ++i; @@ -1057,30 +1069,33 @@ size_t QuicFramer::AppendIetfFrames(const QuicFrames& frames, // Determine if we should write stream frame length in header. const bool last_frame_in_packet = i == frames.size() - 1; if (!AppendIetfFrameType(frame, last_frame_in_packet, writer)) { - QUIC_BUG << "AppendIetfFrameType failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_30) + << "AppendIetfFrameType failed: " << detailed_error(); return 0; } switch (frame.type) { case PADDING_FRAME: if (!AppendPaddingFrame(frame.padding_frame, writer)) { - QUIC_BUG << "AppendPaddingFrame of " - << frame.padding_frame.num_padding_bytes - << " failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_31) << "AppendPaddingFrame of " + << frame.padding_frame.num_padding_bytes + << " failed: " << detailed_error(); return 0; } break; case STREAM_FRAME: if (!AppendStreamFrame(frame.stream_frame, last_frame_in_packet, writer)) { - QUIC_BUG << "AppendStreamFrame failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_32) + << "AppendStreamFrame " << frame.stream_frame + << " failed: " << detailed_error(); return 0; } break; case ACK_FRAME: if (!AppendIetfAckFrameAndTypeByte(*frame.ack_frame, writer)) { - QUIC_BUG << "AppendIetfAckFrameAndTypeByte failed: " - << detailed_error(); + QUIC_BUG(quic_bug_10850_33) + << "AppendIetfAckFrameAndTypeByte failed: " << detailed_error(); return 0; } break; @@ -1088,7 +1103,7 @@ size_t QuicFramer::AppendIetfFrames(const QuicFrames& frames, set_detailed_error( "Attempt to append STOP WAITING frame in IETF QUIC."); RaiseError(QUIC_INTERNAL_ERROR); - QUIC_BUG << detailed_error(); + QUIC_BUG(quic_bug_10850_34) << detailed_error(); return 0; case MTU_DISCOVERY_FRAME: // MTU discovery frames are serialized as ping frames. @@ -1098,22 +1113,23 @@ size_t QuicFramer::AppendIetfFrames(const QuicFrames& frames, break; case RST_STREAM_FRAME: if (!AppendRstStreamFrame(*frame.rst_stream_frame, writer)) { - QUIC_BUG << "AppendRstStreamFrame failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_35) + << "AppendRstStreamFrame failed: " << detailed_error(); return 0; } break; case CONNECTION_CLOSE_FRAME: if (!AppendIetfConnectionCloseFrame(*frame.connection_close_frame, writer)) { - QUIC_BUG << "AppendIetfConnectionCloseFrame failed: " - << detailed_error(); + QUIC_BUG(quic_bug_10850_36) + << "AppendIetfConnectionCloseFrame failed: " << detailed_error(); return 0; } break; case GOAWAY_FRAME: set_detailed_error("Attempt to append GOAWAY frame in IETF QUIC."); RaiseError(QUIC_INTERNAL_ERROR); - QUIC_BUG << detailed_error(); + QUIC_BUG(quic_bug_10850_37) << detailed_error(); return 0; case WINDOW_UPDATE_FRAME: // Depending on whether there is a stream ID or not, will be either a @@ -1121,83 +1137,95 @@ size_t QuicFramer::AppendIetfFrames(const QuicFrames& frames, if (frame.window_update_frame->stream_id == QuicUtils::GetInvalidStreamId(transport_version())) { if (!AppendMaxDataFrame(*frame.window_update_frame, writer)) { - QUIC_BUG << "AppendMaxDataFrame failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_38) + << "AppendMaxDataFrame failed: " << detailed_error(); return 0; } } else { if (!AppendMaxStreamDataFrame(*frame.window_update_frame, writer)) { - QUIC_BUG << "AppendMaxStreamDataFrame failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_39) + << "AppendMaxStreamDataFrame failed: " << detailed_error(); return 0; } } break; case BLOCKED_FRAME: if (!AppendBlockedFrame(*frame.blocked_frame, writer)) { - QUIC_BUG << "AppendBlockedFrame failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_40) + << "AppendBlockedFrame failed: " << detailed_error(); return 0; } break; case MAX_STREAMS_FRAME: if (!AppendMaxStreamsFrame(frame.max_streams_frame, writer)) { - QUIC_BUG << "AppendMaxStreamsFrame failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_41) + << "AppendMaxStreamsFrame failed: " << detailed_error(); return 0; } break; case STREAMS_BLOCKED_FRAME: if (!AppendStreamsBlockedFrame(frame.streams_blocked_frame, writer)) { - QUIC_BUG << "AppendStreamsBlockedFrame failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_42) + << "AppendStreamsBlockedFrame failed: " << detailed_error(); return 0; } break; case NEW_CONNECTION_ID_FRAME: if (!AppendNewConnectionIdFrame(*frame.new_connection_id_frame, writer)) { - QUIC_BUG << "AppendNewConnectionIdFrame failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_43) + << "AppendNewConnectionIdFrame failed: " << detailed_error(); return 0; } break; case RETIRE_CONNECTION_ID_FRAME: if (!AppendRetireConnectionIdFrame(*frame.retire_connection_id_frame, writer)) { - QUIC_BUG << "AppendRetireConnectionIdFrame failed: " - << detailed_error(); + QUIC_BUG(quic_bug_10850_44) + << "AppendRetireConnectionIdFrame failed: " << detailed_error(); return 0; } break; case NEW_TOKEN_FRAME: if (!AppendNewTokenFrame(*frame.new_token_frame, writer)) { - QUIC_BUG << "AppendNewTokenFrame failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_45) + << "AppendNewTokenFrame failed: " << detailed_error(); return 0; } break; case STOP_SENDING_FRAME: if (!AppendStopSendingFrame(*frame.stop_sending_frame, writer)) { - QUIC_BUG << "AppendStopSendingFrame failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_46) + << "AppendStopSendingFrame failed: " << detailed_error(); return 0; } break; case PATH_CHALLENGE_FRAME: if (!AppendPathChallengeFrame(*frame.path_challenge_frame, writer)) { - QUIC_BUG << "AppendPathChallengeFrame failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_47) + << "AppendPathChallengeFrame failed: " << detailed_error(); return 0; } break; case PATH_RESPONSE_FRAME: if (!AppendPathResponseFrame(*frame.path_response_frame, writer)) { - QUIC_BUG << "AppendPathResponseFrame failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_48) + << "AppendPathResponseFrame failed: " << detailed_error(); return 0; } break; case MESSAGE_FRAME: if (!AppendMessageFrameAndTypeByte(*frame.message_frame, last_frame_in_packet, writer)) { - QUIC_BUG << "AppendMessageFrame failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_49) + << "AppendMessageFrame failed: " << detailed_error(); return 0; } break; case CRYPTO_FRAME: if (!AppendCryptoFrame(*frame.crypto_frame, writer)) { - QUIC_BUG << "AppendCryptoFrame failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_50) + << "AppendCryptoFrame failed: " << detailed_error(); return 0; } break; @@ -1206,14 +1234,16 @@ size_t QuicFramer::AppendIetfFrames(const QuicFrames& frames, break; case ACK_FREQUENCY_FRAME: if (!AppendAckFrequencyFrame(*frame.ack_frequency_frame, writer)) { - QUIC_BUG << "AppendAckFrequencyFrame failed: " << detailed_error(); + QUIC_BUG(quic_bug_10850_51) + << "AppendAckFrequencyFrame failed: " << detailed_error(); return 0; } break; default: set_detailed_error("Tried to append unknown frame type."); RaiseError(QUIC_INVALID_FRAME_DATA); - QUIC_BUG << "QUIC_INVALID_FRAME_DATA: " << frame.type; + QUIC_BUG(quic_bug_10850_52) + << "QUIC_INVALID_FRAME_DATA: " << frame.type; return 0; } ++i; @@ -1268,10 +1298,57 @@ std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildPublicResetPacket( } // static +size_t QuicFramer::GetMinStatelessResetPacketLength() { + // 5 bytes (40 bits) = 2 Fixed Bits (01) + 38 Unpredictable bits + return 5 + kStatelessResetTokenLength; +} + +// static std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildIetfStatelessResetPacket( QuicConnectionId /*connection_id*/, - QuicUint128 stateless_reset_token) { + size_t received_packet_length, + StatelessResetToken stateless_reset_token) { QUIC_DVLOG(1) << "Building IETF stateless reset packet."; + if (GetQuicReloadableFlag(quic_fix_stateless_reset)) { + if (received_packet_length <= GetMinStatelessResetPacketLength()) { + QUIC_BUG(362045737_1) + << "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_RELOADABLE_FLAG_COUNT(quic_fix_stateless_reset); + return std::make_unique<QuicEncryptedPacket>(buffer.release(), len, + /*owns_buffer=*/true); + } + size_t len = kPacketHeaderTypeSize + kMinRandomBytesLengthInStatelessReset + sizeof(stateless_reset_token); std::unique_ptr<char[]> buffer(new char[len]); @@ -1291,18 +1368,10 @@ std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildIetfStatelessResetPacket( // 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 here. - if (GetQuicReloadableFlag(quic_stateless_reset_faster_random)) { - QUIC_RELOADABLE_FLAG_COUNT(quic_stateless_reset_faster_random); - if (!writer.WriteInsecureRandomBytes( - QuicRandom::GetInstance(), kMinRandomBytesLengthInStatelessReset)) { - return nullptr; - } - } else { - if (!writer.WriteRandomBytes(QuicRandom::GetInstance(), - kMinRandomBytesLengthInStatelessReset)) { - return nullptr; - } + // number generator. It's therefore safe to use WriteInsecureRandomBytes. + if (!writer.WriteInsecureRandomBytes(QuicRandom::GetInstance(), + kMinRandomBytesLengthInStatelessReset)) { + return nullptr; } // Append stateless reset token. @@ -1528,9 +1597,9 @@ bool QuicFramer::ProcessPacketInternal(const QuicEncryptedPacket& packet) { rv = ProcessDataPacket(&reader, &header, packet, large_buffer.get(), packet.length()); } - QUIC_BUG_IF(rv) << "QUIC should never successfully process packets larger" - << "than kMaxIncomingPacketSize. packet size:" - << packet.length(); + QUIC_BUG_IF(quic_bug_10850_53, rv) + << "QUIC should never successfully process packets larger" + << "than kMaxIncomingPacketSize. packet size:" << packet.length(); } return rv; } @@ -1709,7 +1778,7 @@ bool QuicFramer::MaybeProcessIetfLength(QuicDataReader* encrypted_reader, if (!encrypted_reader->TruncateRemaining(header->remaining_packet_length)) { set_detailed_error("Length TruncateRemaining failed."); - QUIC_BUG << "Length TruncateRemaining failed."; + QUIC_BUG(quic_bug_10850_54) << "Length TruncateRemaining failed."; return RaiseError(QUIC_INVALID_PACKET_HEADER); } return true; @@ -1899,7 +1968,7 @@ bool QuicFramer::ProcessIetfDataPacket(QuicDataReader* encrypted_reader, // Handle the payload. if (VersionHasIetfQuicFrames(version_.transport_version)) { current_received_frame_type_ = 0; - if (!ProcessIetfFrameData(&reader, *header)) { + if (!ProcessIetfFrameData(&reader, *header, decrypted_level)) { current_received_frame_type_ = 0; QUICHE_DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessIetfFrameData sets the error. @@ -2050,8 +2119,8 @@ bool QuicFramer::ProcessPublicResetPacket(QuicDataReader* reader, bool QuicFramer::IsIetfStatelessResetPacket( const QuicPacketHeader& header) const { - QUIC_BUG_IF(header.has_possible_stateless_reset_token && - perspective_ != Perspective::IS_CLIENT) + QUIC_BUG_IF(quic_bug_12975_3, header.has_possible_stateless_reset_token && + perspective_ != Perspective::IS_CLIENT) << "has_possible_stateless_reset_token can only be true at client side."; return header.form == IETF_QUIC_SHORT_HEADER_PACKET && header.has_possible_stateless_reset_token && @@ -2079,14 +2148,15 @@ bool QuicFramer::HasAnEncrypterForSpace(PacketNumberSpace space) const { case NUM_PACKET_NUMBER_SPACES: break; } - QUIC_BUG << ENDPOINT - << "Try to send data of space: " << PacketNumberSpaceToString(space); + QUIC_BUG(quic_bug_10850_55) + << ENDPOINT + << "Try to send data of space: " << PacketNumberSpaceToString(space); return false; } EncryptionLevel QuicFramer::GetEncryptionLevelToSendApplicationData() const { if (!HasAnEncrypterForSpace(APPLICATION_DATA)) { - QUIC_BUG + QUIC_BUG(quic_bug_12975_4) << "Tried to get encryption level to send application data with no " "encrypter available."; return NUM_ENCRYPTION_LEVELS; @@ -2138,8 +2208,9 @@ bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header, } break; case CONNECTION_ID_PRESENT: - QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion( - server_connection_id, transport_version())) + QUIC_BUG_IF(quic_bug_12975_5, + !QuicUtils::IsConnectionIdValidForVersion( + server_connection_id, transport_version())) << "AppendPacketHeader: attempted to use connection ID " << server_connection_id << " which is invalid with version " << version(); @@ -2202,8 +2273,8 @@ bool QuicFramer::AppendIetfPacketHeader(const QuicPacketHeader& header, QUIC_DVLOG(1) << ENDPOINT << "Appending IETF header: " << header; QuicConnectionId server_connection_id = GetServerConnectionIdAsSender(header, perspective_); - QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion(server_connection_id, - transport_version())) + QUIC_BUG_IF(quic_bug_12975_6, !QuicUtils::IsConnectionIdValidForVersion( + server_connection_id, transport_version())) << "AppendIetfPacketHeader: attempted to use connection ID " << server_connection_id << " which is invalid with version " << version(); if (!AppendIetfHeaderTypeByte(header, writer)) { @@ -2243,7 +2314,8 @@ bool QuicFramer::AppendIetfPacketHeader(const QuicPacketHeader& header, } // TODO(b/141924462) Remove this QUIC_BUG once we do support sending RETRY. - QUIC_BUG_IF(header.version_flag && header.long_packet_type == RETRY) + QUIC_BUG_IF(quic_bug_12975_7, + header.version_flag && header.long_packet_type == RETRY) << "Sending IETF RETRY packets is not currently supported " << header; if (QuicVersionHasLongHeaderLengths(transport_version()) && @@ -2468,7 +2540,7 @@ uint8_t QuicFramer::GetPacketNumberFlags( case PACKET_8BYTE_PACKET_NUMBER: return PACKET_FLAGS_8BYTE_PACKET; default: - QUIC_BUG << "Unreachable case statement."; + QUIC_BUG(quic_bug_10850_56) << "Unreachable case statement."; return PACKET_FLAGS_8BYTE_PACKET; } } @@ -2701,14 +2773,28 @@ bool QuicFramer::ProcessAndValidateIetfConnectionIdLength( } bool QuicFramer::ValidateReceivedConnectionIds(const QuicPacketHeader& header) { - if (!QuicUtils::IsConnectionIdValidForVersion( + bool skip_server_connection_id_validation = + do_not_synthesize_source_cid_for_short_header_ && + perspective_ == Perspective::IS_CLIENT && + header.form == IETF_QUIC_SHORT_HEADER_PACKET; + if (!skip_server_connection_id_validation && + !QuicUtils::IsConnectionIdValidForVersion( GetServerConnectionIdAsRecipient(header, perspective_), transport_version())) { set_detailed_error("Received server connection ID with invalid length."); return false; } - if (version_.SupportsClientConnectionIds() && + bool skip_client_connection_id_validation = + do_not_synthesize_source_cid_for_short_header_ && + perspective_ == Perspective::IS_SERVER && + header.form == IETF_QUIC_SHORT_HEADER_PACKET; + if (skip_client_connection_id_validation) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_do_not_synthesize_source_cid_for_short_header, 2, 3); + } + if (!skip_client_connection_id_validation && + version_.SupportsClientConnectionIds() && !QuicUtils::IsConnectionIdValidForVersion( GetClientConnectionIdAsRecipient(header, perspective_), transport_version())) { @@ -2743,7 +2829,8 @@ bool QuicFramer::ProcessIetfPacketHeader(QuicDataReader* reader, header->destination_connection_id_included = CONNECTION_ID_PRESENT; header->source_connection_id_included = header->version_flag ? CONNECTION_ID_PRESENT : CONNECTION_ID_ABSENT; - if (header->source_connection_id_included == CONNECTION_ID_ABSENT) { + if (!do_not_synthesize_source_cid_for_short_header_ && + header->source_connection_id_included == CONNECTION_ID_ABSENT) { QUICHE_DCHECK(header->source_connection_id.IsEmpty()); if (perspective_ == Perspective::IS_CLIENT) { header->source_connection_id = last_serialized_server_connection_id_; @@ -2838,10 +2925,12 @@ bool QuicFramer::ProcessIetfPacketHeader(QuicDataReader* reader, set_detailed_error("Client connection ID not supported in this version."); return false; } - if (perspective_ == Perspective::IS_CLIENT) { - header->source_connection_id = last_serialized_server_connection_id_; - } else { - header->source_connection_id = last_serialized_client_connection_id_; + if (!do_not_synthesize_source_cid_for_short_header_) { + if (perspective_ == Perspective::IS_CLIENT) { + header->source_connection_id = last_serialized_server_connection_id_; + } else { + header->source_connection_id = last_serialized_client_connection_id_; + } } } @@ -3098,8 +3187,33 @@ bool QuicFramer::ProcessFrameData(QuicDataReader* reader, return true; } +// static +bool QuicFramer::IsIetfFrameTypeExpectedForEncryptionLevel( + uint64_t frame_type, + EncryptionLevel level) { + switch (level) { + case ENCRYPTION_INITIAL: + case ENCRYPTION_HANDSHAKE: + return frame_type == IETF_CRYPTO || frame_type == IETF_ACK || + 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 || + frame_type == IETF_NEW_TOKEN || + frame_type == IETF_PATH_RESPONSE || + frame_type == IETF_RETIRE_CONNECTION_ID); + case ENCRYPTION_FORWARD_SECURE: + return true; + default: + QUIC_BUG(quic_bug_10850_57) << "Unknown encryption level: " << level; + } + return false; +} + bool QuicFramer::ProcessIetfFrameData(QuicDataReader* reader, - const QuicPacketHeader& header) { + const QuicPacketHeader& header, + EncryptionLevel decrypted_level) { QUICHE_DCHECK(VersionHasIetfQuicFrames(version_.transport_version)) << "Attempt to process frames as IETF frames but version (" << version_.transport_version << ") does not support IETF Framing."; @@ -3118,6 +3232,21 @@ bool QuicFramer::ProcessIetfFrameData(QuicDataReader* reader, set_detailed_error("Unable to read frame type."); return RaiseError(QUIC_INVALID_FRAME_DATA); } + if (reject_unexpected_ietf_frame_types_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_reject_unexpected_ietf_frame_types, 1, + 2); + if (!IsIetfFrameTypeExpectedForEncryptionLevel(frame_type, + decrypted_level)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_reject_unexpected_ietf_frame_types, 2, + 2); + set_detailed_error(absl::StrCat( + "IETF frame type ", + QuicIetfFrameTypeString(static_cast<QuicIetfFrameType>(frame_type)), + " is unexpected at encryption level ", + EncryptionLevelToString(decrypted_level))); + return RaiseError(IETF_QUIC_PROTOCOL_VIOLATION); + } + } current_received_frame_type_ = frame_type; // Is now the number of bytes into which the frame type was encoded. @@ -4294,9 +4423,10 @@ bool QuicFramer::DoKeyUpdate(KeyUpdateReason reason) { std::unique_ptr<QuicEncrypter> next_encrypter = visitor_->CreateCurrentOneRttEncrypter(); if (!next_decrypter_ || !next_encrypter) { - QUIC_BUG << "Failed to create next crypters"; + QUIC_BUG(quic_bug_10850_58) << "Failed to create next crypters"; return false; } + key_update_performed_ = true; current_key_phase_bit_ = !current_key_phase_bit_; QUIC_DLOG(INFO) << ENDPOINT << "DoKeyUpdate: new current_key_phase_bit_=" << current_key_phase_bit_; @@ -4357,9 +4487,9 @@ size_t QuicFramer::EncryptInPlace(EncryptionLevel level, char* buffer) { QUICHE_DCHECK(packet_number.IsInitialized()); if (encrypter_[level] == nullptr) { - QUIC_BUG << ENDPOINT - << "Attempted to encrypt in place without encrypter at level " - << level; + QUIC_BUG(quic_bug_10850_59) + << ENDPOINT + << "Attempted to encrypt in place without encrypter at level " << level; RaiseError(QUIC_ENCRYPTION_FAILURE); return 0; } @@ -4412,14 +4542,14 @@ bool QuicFramer::ApplyHeaderProtection(EncryptionLevel level, absl::string_view sample; if (!sample_reader.Seek(sample_offset) || !sample_reader.ReadStringPiece(&sample, kHPSampleLen)) { - QUIC_BUG << "Not enough bytes to sample: sample_offset " << sample_offset - << ", sample len: " << kHPSampleLen - << ", buffer len: " << buffer_len; + QUIC_BUG(quic_bug_10850_60) + << "Not enough bytes to sample: sample_offset " << sample_offset + << ", sample len: " << kHPSampleLen << ", buffer len: " << buffer_len; return false; } if (encrypter_[level] == nullptr) { - QUIC_BUG + QUIC_BUG(quic_bug_12975_8) << ENDPOINT << "Attempted to apply header protection without encrypter at level " << level << " using " << version_; @@ -4428,7 +4558,7 @@ bool QuicFramer::ApplyHeaderProtection(EncryptionLevel level, std::string mask = encrypter_[level]->GenerateHeaderProtectionMask(sample); if (mask.empty()) { - QUIC_BUG << "Unable to generate header protection mask."; + QUIC_BUG(quic_bug_10850_61) << "Unable to generate header protection mask."; return false; } QuicDataReader mask_reader(mask.data(), mask.size()); @@ -4457,7 +4587,8 @@ bool QuicFramer::ApplyHeaderProtection(EncryptionLevel level, perspective_ == Perspective::IS_SERVER && version_.handshake_protocol == PROTOCOL_QUIC_CRYPTO) { if (pn_offset <= kDiversificationNonceSize) { - QUIC_BUG << "Expected diversification nonce, but not enough bytes"; + QUIC_BUG(quic_bug_10850_62) + << "Expected diversification nonce, but not enough bytes"; return false; } pn_offset -= kDiversificationNonceSize; @@ -4612,8 +4743,9 @@ size_t QuicFramer::EncryptPayload(EncryptionLevel level, size_t buffer_len) { QUICHE_DCHECK(packet_number.IsInitialized()); if (encrypter_[level] == nullptr) { - QUIC_BUG << ENDPOINT << "Attempted to encrypt without encrypter at level " - << level; + QUIC_BUG(quic_bug_10850_63) + << ENDPOINT << "Attempted to encrypt without encrypter at level " + << level; RaiseError(QUIC_ENCRYPTION_FAILURE); return 0; } @@ -4624,10 +4756,10 @@ size_t QuicFramer::EncryptPayload(EncryptionLevel level, // plaintext content. const size_t ad_len = associated_data.length(); if (packet.length() < ad_len) { - QUIC_BUG << ENDPOINT - << "packet is shorter than associated data length. version:" - << version() << ", packet length:" << packet.length() - << ", associated data length:" << ad_len; + QUIC_BUG(quic_bug_10850_64) + << ENDPOINT << "packet is shorter than associated data length. version:" + << version() << ", packet length:" << packet.length() + << ", associated data length:" << ad_len; RaiseError(QUIC_ENCRYPTION_FAILURE); return 0; } @@ -4654,9 +4786,10 @@ size_t QuicFramer::EncryptPayload(EncryptionLevel level, size_t QuicFramer::GetCiphertextSize(EncryptionLevel level, size_t plaintext_size) const { if (encrypter_[level] == nullptr) { - QUIC_BUG << ENDPOINT - << "Attempted to get ciphertext size without encrypter at level " - << level << " using " << version_; + QUIC_BUG(quic_bug_10850_65) + << ENDPOINT + << "Attempted to get ciphertext size without encrypter at level " + << level << " using " << version_; return plaintext_size; } return encrypter_[level]->GetCiphertextSize(plaintext_size); @@ -4681,7 +4814,7 @@ size_t QuicFramer::GetMaxPlaintextSize(size_t ciphertext_size) { QuicPacketCount QuicFramer::GetOneRttEncrypterConfidentialityLimit() const { if (!encrypter_[ENCRYPTION_FORWARD_SECURE]) { - QUIC_BUG << "1-RTT encrypter not set"; + QUIC_BUG(quic_bug_10850_66) << "1-RTT encrypter not set"; return 0; } return encrypter_[ENCRYPTION_FORWARD_SECURE]->GetConfidentialityLimit(); @@ -4696,7 +4829,8 @@ bool QuicFramer::DecryptPayload(size_t udp_packet_length, size_t* decrypted_length, EncryptionLevel* decrypted_level) { if (!EncryptionLevelIsValid(decrypter_level_)) { - QUIC_BUG << "Attempted to decrypt with bad decrypter_level_"; + QUIC_BUG(quic_bug_10850_67) + << "Attempted to decrypt with bad decrypter_level_"; return false; } EncryptionLevel level = decrypter_level_; @@ -4707,13 +4841,14 @@ bool QuicFramer::DecryptPayload(size_t udp_packet_length, bool attempt_key_update = false; if (version().KnowsWhichDecrypterToUse()) { if (header.form == GOOGLE_QUIC_PACKET) { - QUIC_BUG << "Attempted to decrypt GOOGLE_QUIC_PACKET with a version that " - "knows which decrypter to use"; + QUIC_BUG(quic_bug_10850_68) + << "Attempted to decrypt GOOGLE_QUIC_PACKET with a version that " + "knows which decrypter to use"; return false; } level = GetEncryptionLevel(header); if (!EncryptionLevelIsValid(level)) { - QUIC_BUG << "Attempted to decrypt with bad level"; + QUIC_BUG(quic_bug_10850_69) << "Attempted to decrypt with bad level"; return false; } decrypter = decrypter_[level].get(); @@ -4734,14 +4869,18 @@ bool QuicFramer::DecryptPayload(size_t udp_packet_length, << " received key_phase=" << key_phase << " current_key_phase_bit_=" << current_key_phase_bit_; if (key_phase != current_key_phase_bit_) { - if (current_key_phase_first_received_packet_number_.IsInitialized() && - header.packet_number > - current_key_phase_first_received_packet_number_) { + if ((current_key_phase_first_received_packet_number_.IsInitialized() && + header.packet_number > + current_key_phase_first_received_packet_number_) || + (GetQuicReloadableFlag(quic_fix_key_update_on_first_packet) && + !current_key_phase_first_received_packet_number_.IsInitialized() && + !key_update_performed_)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_fix_key_update_on_first_packet); if (!next_decrypter_) { next_decrypter_ = visitor_->AdvanceKeysAndCreateCurrentOneRttDecrypter(); if (!next_decrypter_) { - QUIC_BUG << "Failed to create next_decrypter"; + QUIC_BUG(quic_bug_10850_70) << "Failed to create next_decrypter"; return false; } } @@ -4766,15 +4905,17 @@ bool QuicFramer::DecryptPayload(size_t udp_packet_length, } } else if (alternative_decrypter_level_ != NUM_ENCRYPTION_LEVELS) { if (!EncryptionLevelIsValid(alternative_decrypter_level_)) { - QUIC_BUG << "Attempted to decrypt with bad alternative_decrypter_level_"; + QUIC_BUG(quic_bug_10850_71) + << "Attempted to decrypt with bad alternative_decrypter_level_"; return false; } alternative_decrypter = decrypter_[alternative_decrypter_level_].get(); } if (decrypter == nullptr) { - QUIC_BUG << "Attempting to decrypt without decrypter, encryption level:" - << level << " version:" << version(); + QUIC_BUG(quic_bug_10850_72) + << "Attempting to decrypt without decrypter, encryption level:" << level + << " version:" << version(); return false; } @@ -4783,21 +4924,16 @@ bool QuicFramer::DecryptPayload(size_t udp_packet_length, decrypted_buffer, decrypted_length, buffer_length); if (success) { visitor_->OnDecryptedPacket(udp_packet_length, level); - if (GetQuicReloadableFlag( - quic_close_connection_on_0rtt_packet_number_higher_than_1rtt)) { - QUIC_RELOADABLE_FLAG_COUNT( - quic_close_connection_on_0rtt_packet_number_higher_than_1rtt); - if (level == ENCRYPTION_ZERO_RTT && - current_key_phase_first_received_packet_number_.IsInitialized() && - header.packet_number > - current_key_phase_first_received_packet_number_) { - set_detailed_error(absl::StrCat( - "Decrypted a 0-RTT packet with a packet number ", - header.packet_number.ToString(), - " which is higher than a 1-RTT packet number ", - current_key_phase_first_received_packet_number_.ToString())); - return RaiseError(QUIC_INVALID_0RTT_PACKET_NUMBER_OUT_OF_ORDER); - } + if (level == ENCRYPTION_ZERO_RTT && + current_key_phase_first_received_packet_number_.IsInitialized() && + header.packet_number > + current_key_phase_first_received_packet_number_) { + set_detailed_error(absl::StrCat( + "Decrypted a 0-RTT packet with a packet number ", + header.packet_number.ToString(), + " which is higher than a 1-RTT packet number ", + current_key_phase_first_received_packet_number_.ToString())); + return RaiseError(QUIC_INVALID_0RTT_PACKET_NUMBER_OUT_OF_ORDER); } *decrypted_level = level; potential_peer_key_update_attempt_count_ = 0; @@ -4850,8 +4986,9 @@ bool QuicFramer::DecryptPayload(size_t udp_packet_length, *decrypted_level = decrypter_level_; if (alternative_decrypter_latch_) { if (!EncryptionLevelIsValid(alternative_decrypter_level_)) { - QUIC_BUG << "Attempted to latch alternate decrypter with bad " - "alternative_decrypter_level_"; + QUIC_BUG(quic_bug_10850_73) + << "Attempted to latch alternate decrypter with bad " + "alternative_decrypter_level_"; return false; } // Switch to the alternative decrypter and latch so that we cannot @@ -4886,7 +5023,7 @@ size_t QuicFramer::GetIetfAckFrameSize(const QuicAckFrame& frame) { ack_frame_size += QuicDataWriter::GetVarInt62Len(ack_delay_time_us); if (frame.packets.Empty() || frame.packets.Max() != largest_acked) { - QUIC_BUG << "Malformed ack frame"; + QUIC_BUG(quic_bug_10850_74) << "Malformed ack frame"; // ACK frame serialization will fail and connection will be closed. return ack_frame_size; } @@ -5169,8 +5306,9 @@ bool QuicFramer::AppendIetfFrameType(const QuicFrame& frame, type_byte = IETF_ACK_FREQUENCY; break; default: - QUIC_BUG << "Attempt to generate a frame type for an unsupported value: " - << frame.type; + QUIC_BUG(quic_bug_10850_75) + << "Attempt to generate a frame type for an unsupported value: " + << frame.type; return false; } return writer->WriteVarInt62(type_byte); @@ -5182,7 +5320,8 @@ bool QuicFramer::AppendPacketNumber(QuicPacketNumberLength packet_number_length, QuicDataWriter* writer) { QUICHE_DCHECK(packet_number.IsInitialized()); if (!IsValidPacketNumberLength(packet_number_length)) { - QUIC_BUG << "Invalid packet_number_length: " << packet_number_length; + QUIC_BUG(quic_bug_10850_76) + << "Invalid packet_number_length: " << packet_number_length; return false; } return writer->WriteBytesToUInt64(packet_number_length, @@ -5194,7 +5333,8 @@ bool QuicFramer::AppendStreamId(size_t stream_id_length, QuicStreamId stream_id, QuicDataWriter* writer) { if (stream_id_length == 0 || stream_id_length > 4) { - QUIC_BUG << "Invalid stream_id_length: " << stream_id_length; + QUIC_BUG(quic_bug_10850_77) + << "Invalid stream_id_length: " << stream_id_length; return false; } return writer->WriteBytesToUInt64(stream_id_length, stream_id); @@ -5205,7 +5345,8 @@ bool QuicFramer::AppendStreamOffset(size_t offset_length, QuicStreamOffset offset, QuicDataWriter* writer) { if (offset_length == 1 || offset_length > 8) { - QUIC_BUG << "Invalid stream_offset_length: " << offset_length; + QUIC_BUG(quic_bug_10850_78) + << "Invalid stream_offset_length: " << offset_length; return false; } @@ -5219,7 +5360,8 @@ bool QuicFramer::AppendAckBlock(uint8_t gap, QuicDataWriter* writer) { if (length == 0) { if (!IsValidPacketNumberLength(length_length)) { - QUIC_BUG << "Invalid packet_number_length: " << length_length; + QUIC_BUG(quic_bug_10850_79) + << "Invalid packet_number_length: " << length_length; return false; } return writer->WriteUInt8(gap) && @@ -5237,12 +5379,12 @@ bool QuicFramer::AppendStreamFrame(const QuicStreamFrame& frame, } if (!AppendStreamId(GetStreamIdSize(frame.stream_id), frame.stream_id, writer)) { - QUIC_BUG << "Writing stream id size failed."; + QUIC_BUG(quic_bug_10850_80) << "Writing stream id size failed."; return false; } if (!AppendStreamOffset(GetStreamOffsetSize(frame.offset), frame.offset, writer)) { - QUIC_BUG << "Writing offset size failed."; + QUIC_BUG(quic_bug_10850_81) << "Writing offset size failed."; return false; } if (!no_stream_frame_length) { @@ -5252,7 +5394,7 @@ bool QuicFramer::AppendStreamFrame(const QuicStreamFrame& frame, "If frame.data_length can hold more than a uint16_t than we need to " "check that frame.data_length <= std::numeric_limits<uint16_t>::max()"); if (!writer->WriteUInt16(static_cast<uint16_t>(frame.data_length))) { - QUIC_BUG << "Writing stream frame length failed"; + QUIC_BUG(quic_bug_10850_82) << "Writing stream frame length failed"; return false; } } @@ -5265,14 +5407,14 @@ bool QuicFramer::AppendStreamFrame(const QuicStreamFrame& frame, if (data_producer_->WriteStreamData(frame.stream_id, frame.offset, frame.data_length, writer) != WRITE_SUCCESS) { - QUIC_BUG << "Writing frame data failed."; + QUIC_BUG(quic_bug_10850_83) << "Writing frame data failed."; return false; } return true; } if (!writer->WriteBytes(frame.data_buffer, frame.data_length)) { - QUIC_BUG << "Writing frame data failed."; + QUIC_BUG(quic_bug_10850_84) << "Writing frame data failed."; return false; } return true; @@ -5352,7 +5494,7 @@ bool QuicFramer::AppendIetfStreamFrame(const QuicStreamFrame& frame, if (data_producer_->WriteStreamData(frame.stream_id, frame.offset, frame.data_length, writer) != WRITE_SUCCESS) { - set_detailed_error("Writing frame data failed."); + set_detailed_error("Writing frame data from producer failed."); return false; } } @@ -5524,8 +5666,9 @@ bool QuicFramer::AppendAckFrameAndTypeByte(const QuicAckFrame& frame, } if (num_ack_blocks_written >= num_ack_blocks) { if (QUIC_PREDICT_FALSE(num_ack_blocks_written != num_ack_blocks)) { - QUIC_BUG << "Wrote " << num_ack_blocks_written - << ", expected to write " << num_ack_blocks; + QUIC_BUG(quic_bug_10850_85) + << "Wrote " << num_ack_blocks_written << ", expected to write " + << num_ack_blocks; } break; } @@ -5635,11 +5778,12 @@ bool QuicFramer::AppendStopWaitingFrame(const QuicPacketHeader& header, const uint64_t length_shift = header.packet_number_length * 8; if (least_unacked_delta >> length_shift > 0) { - QUIC_BUG << "packet_number_length " << header.packet_number_length - << " is too small for least_unacked_delta: " << least_unacked_delta - << " packet_number:" << header.packet_number - << " least_unacked:" << frame.least_unacked - << " version:" << version_.transport_version; + QUIC_BUG(quic_bug_10850_86) + << "packet_number_length " << header.packet_number_length + << " is too small for least_unacked_delta: " << least_unacked_delta + << " packet_number:" << header.packet_number + << " least_unacked:" << frame.least_unacked + << " version:" << version_.transport_version; return false; } if (least_unacked_delta == 0) { @@ -5648,7 +5792,8 @@ bool QuicFramer::AppendStopWaitingFrame(const QuicPacketHeader& header, } if (!AppendPacketNumber(header.packet_number_length, QuicPacketNumber(least_unacked_delta), writer)) { - QUIC_BUG << " seq failed: " << header.packet_number_length; + QUIC_BUG(quic_bug_10850_87) + << " seq failed: " << header.packet_number_length; return false; } @@ -5692,7 +5837,7 @@ bool QuicFramer::AppendIetfAckFrameAndTypeByte(const QuicAckFrame& frame, } if (frame.packets.Empty() || frame.packets.Max() != largest_acked) { - QUIC_BUG << "Malformed ack frame: " << frame; + QUIC_BUG(quic_bug_10850_88) << "Malformed ack frame: " << frame; set_detailed_error("Malformed ack frame"); return false; } @@ -5739,9 +5884,9 @@ bool QuicFramer::AppendIetfAckFrameAndTypeByte(const QuicAckFrame& frame, !count_writer.WriteVarInt62(appended_ack_blocks)) { // This should never happen as ack_block_count is limited by // max_ack_ranges_. - QUIC_BUG << "Ack frame truncation fails. ack_block_count: " - << ack_block_count - << ", appended count: " << appended_ack_blocks; + QUIC_BUG(quic_bug_10850_89) + << "Ack frame truncation fails. ack_block_count: " << ack_block_count + << ", appended count: " << appended_ack_blocks; set_detailed_error("ACK frame truncation fails"); return false; } @@ -5854,7 +5999,7 @@ bool QuicFramer::AppendPaddingFrame(const QuicPaddingFrame& frame, return false; } if (frame.num_padding_bytes < 0) { - QUIC_BUG_IF(frame.num_padding_bytes != -1); + QUIC_BUG_IF(quic_bug_12975_9, frame.num_padding_bytes != -1); writer->WritePadding(); return true; } @@ -5915,7 +6060,8 @@ bool QuicFramer::AppendIetfConnectionCloseFrame( QuicDataWriter* writer) { if (frame.close_type != IETF_QUIC_TRANSPORT_CONNECTION_CLOSE && frame.close_type != IETF_QUIC_APPLICATION_CONNECTION_CLOSE) { - QUIC_BUG << "Invalid close_type for writing IETF CONNECTION CLOSE."; + QUIC_BUG(quic_bug_10850_90) + << "Invalid close_type for writing IETF CONNECTION CLOSE."; set_detailed_error("Invalid close_type for writing IETF CONNECTION CLOSE."); return false; } @@ -6406,12 +6552,14 @@ void QuicFramer::InferPacketHeaderTypeFromVersion() { void QuicFramer::EnableMultiplePacketNumberSpacesSupport() { if (supports_multiple_packet_number_spaces_) { - QUIC_BUG << "Multiple packet number spaces has already been enabled"; + QUIC_BUG(quic_bug_10850_91) + << "Multiple packet number spaces has already been enabled"; return; } if (largest_packet_number_.IsInitialized()) { - QUIC_BUG << "Try to enable multiple packet number spaces support after any " - "packet has been received."; + QUIC_BUG(quic_bug_10850_92) + << "Try to enable multiple packet number spaces support after any " + "packet has been received."; return; } @@ -6701,17 +6849,17 @@ bool QuicFramer::WriteClientVersionNegotiationProbePacket( const char* destination_connection_id_bytes, uint8_t destination_connection_id_length) { if (packet_bytes == nullptr) { - QUIC_BUG << "Invalid packet_bytes"; + QUIC_BUG(quic_bug_10850_93) << "Invalid packet_bytes"; return false; } if (packet_length < kMinPacketSizeForVersionNegotiation || packet_length > 65535) { - QUIC_BUG << "Invalid packet_length"; + QUIC_BUG(quic_bug_10850_94) << "Invalid packet_length"; return false; } if (destination_connection_id_length > kQuicMaxConnectionId4BitLength || destination_connection_id_length < kQuicDefaultConnectionIdLength) { - QUIC_BUG << "Invalid connection_id_length"; + QUIC_BUG(quic_bug_10850_95) << "Invalid connection_id_length"; return false; } // clang-format off @@ -6727,7 +6875,7 @@ bool QuicFramer::WriteClientVersionNegotiationProbePacket( static_assert(sizeof(packet_start_bytes) == 5, "bad packet_start_bytes size"); QuicDataWriter writer(packet_length, packet_bytes); if (!writer.WriteBytes(packet_start_bytes, sizeof(packet_start_bytes))) { - QUIC_BUG << "Failed to write packet start"; + QUIC_BUG(quic_bug_10850_96) << "Failed to write packet start"; return false; } @@ -6736,7 +6884,7 @@ bool QuicFramer::WriteClientVersionNegotiationProbePacket( if (!AppendIetfConnectionIds( /*version_flag=*/true, /*use_length_prefix=*/true, destination_connection_id, EmptyQuicConnectionId(), &writer)) { - QUIC_BUG << "Failed to write connection IDs"; + QUIC_BUG(quic_bug_10850_97) << "Failed to write connection IDs"; return false; } // Add 8 bytes of zeroes followed by 8 bytes of ones to ensure that this does @@ -6746,14 +6894,14 @@ bool QuicFramer::WriteClientVersionNegotiationProbePacket( // valid frame type. if (!writer.WriteUInt64(0) || !writer.WriteUInt64(std::numeric_limits<uint64_t>::max())) { - QUIC_BUG << "Failed to write 18 bytes"; + QUIC_BUG(quic_bug_10850_98) << "Failed to write 18 bytes"; return false; } // Make sure the polite greeting below is padded to a 16-byte boundary to // make it easier to read in tcpdump. while (writer.length() % 16 != 0) { if (!writer.WriteUInt8(0)) { - QUIC_BUG << "Failed to write padding byte"; + QUIC_BUG(quic_bug_10850_99) << "Failed to write padding byte"; return false; } } @@ -6763,7 +6911,7 @@ bool QuicFramer::WriteClientVersionNegotiationProbePacket( "Please respond with a Version Negotiation packet indicating what " "versions you support. Thank you and have a nice day."; if (!writer.WriteBytes(polite_greeting, sizeof(polite_greeting))) { - QUIC_BUG << "Failed to write polite greeting"; + QUIC_BUG(quic_bug_10850_100) << "Failed to write polite greeting"; return false; } // Fill the rest of the packet with zeroes. @@ -6780,7 +6928,7 @@ bool QuicFramer::ParseServerVersionNegotiationProbeResponse( uint8_t* source_connection_id_length_out, std::string* detailed_error) { if (detailed_error == nullptr) { - QUIC_BUG << "Invalid error_details"; + QUIC_BUG(quic_bug_10850_101) << "Invalid error_details"; return false; } *detailed_error = ""; 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 571278deab8..e27c7f0727e 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 @@ -225,7 +225,8 @@ class QUIC_EXPORT_PRIVATE QuicFramerVisitorInterface { virtual void OnPacketComplete() = 0; // Called to check whether |token| is a valid stateless reset token. - virtual bool IsValidStatelessResetToken(QuicUint128 token) const = 0; + virtual bool IsValidStatelessResetToken( + const StatelessResetToken& token) const = 0; // Called when an IETF stateless reset packet has been parsed and validated // with the stateless reset token. @@ -470,10 +471,14 @@ class QUIC_EXPORT_PRIVATE QuicFramer { static std::unique_ptr<QuicEncryptedPacket> BuildPublicResetPacket( const QuicPublicResetPacket& packet); + // Returns the minimal stateless reset packet length. + static size_t GetMinStatelessResetPacketLength(); + // Returns a new IETF stateless reset packet. static std::unique_ptr<QuicEncryptedPacket> BuildIetfStatelessResetPacket( QuicConnectionId connection_id, - QuicUint128 stateless_reset_token); + size_t received_packet_length, + StatelessResetToken stateless_reset_token); // Returns a new version negotiation packet. static std::unique_ptr<QuicEncryptedPacket> BuildVersionNegotiationPacket( @@ -709,6 +714,10 @@ class QUIC_EXPORT_PRIVATE QuicFramer { drop_incoming_retry_packets_ = drop_incoming_retry_packets; } + bool do_not_synthesize_source_cid_for_short_header() const { + return do_not_synthesize_source_cid_for_short_header_; + } + private: friend class test::QuicFramerPeer; @@ -826,8 +835,13 @@ class QUIC_EXPORT_PRIVATE QuicFramer { QuicPacketNumber base_packet_number, uint64_t* packet_number); bool ProcessFrameData(QuicDataReader* reader, const QuicPacketHeader& header); + + static bool IsIetfFrameTypeExpectedForEncryptionLevel(uint64_t frame_type, + EncryptionLevel level); + bool ProcessIetfFrameData(QuicDataReader* reader, - const QuicPacketHeader& header); + const QuicPacketHeader& header, + EncryptionLevel decrypted_level); bool ProcessStreamFrame(QuicDataReader* reader, uint8_t frame_type, QuicStreamFrame* frame); @@ -1112,6 +1126,8 @@ class QUIC_EXPORT_PRIVATE QuicFramer { // The value of the current key phase bit, which is toggled when the keys are // changed. bool current_key_phase_bit_; + // Whether we have performed a key update at least once. + bool key_update_performed_ = false; // Tracks the first packet received in the current key phase. Will be // uninitialized before the first one-RTT packet has been received or after a // locally initiated key update but before the first packet from the peer in @@ -1158,6 +1174,14 @@ class QUIC_EXPORT_PRIVATE QuicFramer { // Indicates whether received RETRY packets should be dropped. bool drop_incoming_retry_packets_ = false; + bool reject_unexpected_ietf_frame_types_ = + GetQuicReloadableFlag(quic_reject_unexpected_ietf_frame_types); + + // Indicates whether source connection ID should be synthesized when read + // short header packet. + const bool do_not_synthesize_source_cid_for_short_header_ = + GetQuicReloadableFlag(quic_do_not_synthesize_source_cid_for_short_header); + // The length in bytes of the last packet number written to an IETF-framed // packet. size_t last_written_packet_number_length_; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc index 12b779ac8f9..048a4b72ffb 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 @@ -13,6 +13,7 @@ #include <vector> #include "absl/base/macros.h" +#include "absl/memory/memory.h" #include "absl/strings/escaping.h" #include "absl/strings/match.h" #include "absl/strings/string_view.h" @@ -29,7 +30,6 @@ #include "quic/platform/api/quic_expect_bug.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" -#include "quic/platform/api/quic_ptr_util.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_framer_peer.h" #include "quic/test_tools/quic_test_utils.h" @@ -47,7 +47,9 @@ namespace { const uint64_t kEpoch = UINT64_C(1) << 32; const uint64_t kMask = kEpoch - 1; -const QuicUint128 kTestStatelessResetToken = 1010101; // 0x0F69B5 +const StatelessResetToken kTestStatelessResetToken{ + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; // Use fields in which each byte is distinct to ensure that every byte is // framed correctly. The values are otherwise arbitrary. @@ -153,7 +155,7 @@ class TestDecrypter : public QuicDecrypter { return true; } bool SetPreliminaryKey(absl::string_view /*key*/) override { - QUIC_BUG << "should not be called"; + QUIC_BUG(quic_bug_10486_1) << "should not be called"; return false; } bool SetDiversificationNonce(const DiversificationNonce& /*key*/) override { @@ -319,7 +321,7 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { // Save a copy of the data so it is valid after the packet is processed. std::string* string_data = new std::string(frame.data_buffer, frame.data_length); - stream_data_.push_back(QuicWrapUnique(string_data)); + stream_data_.push_back(absl::WrapUnique(string_data)); stream_frames_.push_back(std::make_unique<QuicStreamFrame>( frame.stream_id, frame.fin, frame.offset, *string_data)); if (VersionHasIetfQuicFrames(transport_version_)) { @@ -336,7 +338,7 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { // Save a copy of the data so it is valid after the packet is processed. std::string* string_data = new std::string(frame.data_buffer, frame.data_length); - crypto_data_.push_back(QuicWrapUnique(string_data)); + crypto_data_.push_back(absl::WrapUnique(string_data)); crypto_frames_.push_back(std::make_unique<QuicCryptoFrame>( frame.level, frame.offset, *string_data)); if (VersionHasIetfQuicFrames(transport_version_)) { @@ -571,7 +573,8 @@ class TestQuicVisitor : public QuicFramerVisitorInterface { return true; } - bool IsValidStatelessResetToken(QuicUint128 token) const override { + bool IsValidStatelessResetToken( + const StatelessResetToken& token) const override { EXPECT_EQ(0u, framer_->current_received_frame_type()); return token == kTestStatelessResetToken; } @@ -1476,7 +1479,9 @@ TEST_P(QuicFramerTest, ClientConnectionIdFromShortHeaderToClient) { ASSERT_TRUE(visitor_.header_.get()); EXPECT_EQ(FramerTestConnectionId(), visitor_.header_->destination_connection_id); - EXPECT_EQ(TestConnectionId(0x33), visitor_.header_->source_connection_id); + if (!framer_.do_not_synthesize_source_cid_for_short_header()) { + EXPECT_EQ(TestConnectionId(0x33), visitor_.header_->source_connection_id); + } } // In short header packets from client to server, the client connection ID @@ -1510,7 +1515,9 @@ TEST_P(QuicFramerTest, ClientConnectionIdFromShortHeaderToServer) { ASSERT_TRUE(visitor_.header_.get()); EXPECT_EQ(FramerTestConnectionId(), visitor_.header_->destination_connection_id); - EXPECT_EQ(TestConnectionId(0x33), visitor_.header_->source_connection_id); + if (!framer_.do_not_synthesize_source_cid_for_short_header()) { + EXPECT_EQ(TestConnectionId(0x33), visitor_.header_->source_connection_id); + } } TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) { @@ -1560,7 +1567,9 @@ TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) { EXPECT_FALSE(framer_.ProcessPacket(*encrypted)); EXPECT_THAT(framer_.error(), IsError(QUIC_MISSING_PAYLOAD)); ASSERT_TRUE(visitor_.header_.get()); - EXPECT_EQ(FramerTestConnectionId(), visitor_.header_->source_connection_id); + if (!framer_.do_not_synthesize_source_cid_for_short_header()) { + EXPECT_EQ(FramerTestConnectionId(), visitor_.header_->source_connection_id); + } EXPECT_FALSE(visitor_.header_->reset_flag); EXPECT_FALSE(visitor_.header_->version_flag); EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number); @@ -5746,8 +5755,8 @@ TEST_P(QuicFramerTest, IetfStatelessResetPacket) { 0x01, 0x11, 0x02, 0x22, 0x03, 0x33, 0x04, 0x44, 0x01, 0x11, 0x02, 0x22, 0x03, 0x33, 0x04, 0x44, // stateless reset token - 0xB5, 0x69, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, }; // clang-format on if (!framer_.version().HasIetfInvariantHeader()) { @@ -9239,6 +9248,59 @@ TEST_P(QuicFramerTest, BuildPublicResetPacketWithEndpointId) { } TEST_P(QuicFramerTest, BuildIetfStatelessResetPacket) { + if (GetQuicReloadableFlag(quic_fix_stateless_reset)) { + // clang-format off + unsigned char packet[] = { + // 1st byte 01XX XXXX + 0x40, + // At least 4 bytes of random bytes. + 0x00, 0x00, 0x00, 0x00, + // 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(), + 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. + EXPECT_QUIC_BUG( + std::unique_ptr<QuicEncryptedPacket> data2( + framer_.BuildIetfStatelessResetPacket( + FramerTestConnectionId(), + QuicFramer::GetMinStatelessResetPacketLength(), + kTestStatelessResetToken)), + "Tried to build stateless reset packet with received packet length"); + + // 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) @@ -9246,29 +9308,28 @@ TEST_P(QuicFramerTest, BuildIetfStatelessResetPacket) { // random packet number 0xFE, // stateless reset token - 0xB5, 0x69, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, }; // clang-format on std::unique_ptr<QuicEncryptedPacket> data( - framer_.BuildIetfStatelessResetPacket(FramerTestConnectionId(), + 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 - sizeof(kTestStatelessResetToken); + data->length() - kPacketHeaderTypeSize - kStatelessResetTokenLength; EXPECT_EQ(kMinRandomBytesLengthInStatelessReset, random_bytes_length); // Verify stateless reset token is correct. quiche::test::CompareCharArraysWithHexError( "constructed packet", - data->data() + data->length() - sizeof(kTestStatelessResetToken), - sizeof(kTestStatelessResetToken), - AsChars(packet) + ABSL_ARRAYSIZE(packet) - - sizeof(kTestStatelessResetToken), - sizeof(kTestStatelessResetToken)); + data->data() + data->length() - kStatelessResetTokenLength, + kStatelessResetTokenLength, + AsChars(packet) + ABSL_ARRAYSIZE(packet) - kStatelessResetTokenLength, + kStatelessResetTokenLength); } TEST_P(QuicFramerTest, EncryptPacket) { @@ -10852,8 +10913,8 @@ TEST_P(QuicFramerTest, NewConnectionIdFrame) { {"Unable to read new connection ID frame connection id.", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11}}, {"Can not read new connection ID frame reset token.", - {0xb5, 0x69, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}} + {0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}} }; // clang-format on @@ -10911,8 +10972,8 @@ TEST_P(QuicFramerTest, NewConnectionIdFrameVariableLength) { {"Unable to read new connection ID frame connection id.", {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42}}, {"Can not read new connection ID frame reset token.", - {0xb5, 0x69, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}} + {0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}} }; // clang-format on @@ -11078,8 +11139,8 @@ TEST_P(QuicFramerTest, BuildNewConnectionIdFramePacket) { // new connection id 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11, // stateless reset token - 0xb5, 0x69, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, }; // clang-format on @@ -11485,8 +11546,8 @@ TEST_P(QuicFramerTest, GetRetransmittableControlFrameSize) { return; } - QuicNewConnectionIdFrame new_connection_id(5, TestConnectionId(), 1, 101111, - 1); + QuicNewConnectionIdFrame new_connection_id(5, TestConnectionId(), 1, + kTestStatelessResetToken, 1); EXPECT_EQ(QuicFramer::GetNewConnectionIdFrameSize(new_connection_id), QuicFramer::GetRetransmittableControlFrameSize( framer_.transport_version(), QuicFrame(&new_connection_id))); @@ -15164,6 +15225,85 @@ TEST_P(QuicFramerTest, KeyUpdateLocallyInitiatedReceivedOldPacket) { EXPECT_EQ(1, visitor_.decrypted_first_packet_in_key_phase_count_); } +TEST_P(QuicFramerTest, KeyUpdateOnFirstReceivedPacket) { + if (!framer_.version().UsesTls()) { + // Key update is only used in QUIC+TLS. + return; + } + SetQuicReloadableFlag(quic_fix_key_update_on_first_packet, true); + ASSERT_TRUE(framer_.version().KnowsWhichDecrypterToUse()); + // Doesn't use SetDecrypterLevel since we want to use StrictTaggingDecrypter + // instead of TestDecrypter. + framer_.InstallDecrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<StrictTaggingDecrypter>(/*key=*/0)); + framer_.SetKeyUpdateSupportForConnection(true); + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = QuicPacketNumber(123); + + QuicFrames frames = {QuicFrame(QuicPaddingFrame())}; + + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + std::unique_ptr<QuicEncryptedPacket> encrypted( + EncryptPacketWithTagAndPhase(*data, /*tag=*/1, /*phase=*/true)); + ASSERT_TRUE(encrypted); + + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + // Processed valid packet with phase=1, key=1: do key update. + EXPECT_EQ(1u, visitor_.key_update_count()); + EXPECT_EQ(1, visitor_.derive_next_key_count_); + EXPECT_EQ(1, visitor_.decrypted_first_packet_in_key_phase_count_); +} + +TEST_P(QuicFramerTest, ErrorWhenUnexpectedFrameTypeEncountered) { + if (!GetQuicReloadableFlag(quic_reject_unexpected_ietf_frame_types) || + !VersionHasIetfQuicFrames(framer_.transport_version()) || + !QuicVersionHasLongHeaderLengths(framer_.transport_version()) || + !framer_.version().HasLongHeaderLengths()) { + return; + } + SetDecrypterLevel(ENCRYPTION_ZERO_RTT); + // clang-format off + unsigned char packet[] = { + // public flags (long header with packet type ZERO_RTT_PROTECTED and + // 4-byte packet number) + 0xD3, + // version + QUIC_VERSION_BYTES, + // destination connection ID length + 0x08, + // destination connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // source connection ID length + 0x08, + // source connection ID + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11, + // long header packet length + 0x05, + // packet number + 0x12, 0x34, 0x56, 0x00, + // unexpected ietf ack frame type in 0-RTT packet + 0x02, + }; + // clang-format on + + QuicEncryptedPacket encrypted(AsChars(packet), ABSL_ARRAYSIZE(packet), false); + + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + + EXPECT_THAT(framer_.error(), IsError(IETF_QUIC_PROTOCOL_VIOLATION)); + EXPECT_EQ( + "IETF frame type IETF_ACK is unexpected at encryption level " + "ENCRYPTION_ZERO_RTT", + framer_.detailed_error()); +} + } // namespace } // namespace test } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_interval_deque.h b/chromium/net/third_party/quiche/src/quic/core/quic_interval_deque.h index d4e82e2d1a2..ba27aa9d8f5 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_interval_deque.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_interval_deque.h @@ -162,7 +162,7 @@ class QUIC_NO_EXPORT QuicIntervalDeque { // Don't increment when we are at the end. const std::size_t container_size = deque_->container_.size(); if (index_ >= container_size) { - QUIC_BUG << "Iterator out of bounds."; + QUIC_BUG(quic_bug_10862_1) << "Iterator out of bounds."; return *this; } index_++; @@ -279,7 +279,7 @@ void QuicIntervalDeque<T, C>::PushBack(const T& item) { template <class T, class C> void QuicIntervalDeque<T, C>::PopFront() { if (container_.size() == 0) { - QUIC_BUG << "Trying to pop from an empty container."; + QUIC_BUG(quic_bug_10862_2) << "Trying to pop from an empty container."; return; } container_.pop_front(); @@ -362,7 +362,8 @@ void QuicIntervalDeque<T, C>::PushBackUniversal(U&& item) { QuicInterval<std::size_t> interval = item.interval(); // Adding an empty interval is a bug. if (interval.Empty()) { - QUIC_BUG << "Trying to save empty interval to QuicCircularDeque."; + QUIC_BUG(quic_bug_10862_3) + << "Trying to save empty interval to QuicCircularDeque."; return; } container_.push_back(std::forward<U>(item)); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator.cc b/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator.cc index bfe2df2fce0..27dbb9b48a8 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_legacy_version_encapsulator.cc @@ -46,12 +46,12 @@ void QuicLegacyVersionEncapsulator::OnSerializedPacket( SerializedPacket serialized_packet) { if (encrypted_length_ != 0) { unrecoverable_failure_encountered_ = true; - QUIC_BUG << "OnSerializedPacket called twice"; + QUIC_BUG(quic_bug_10615_1) << "OnSerializedPacket called twice"; return; } if (serialized_packet.encrypted_length == 0) { unrecoverable_failure_encountered_ = true; - QUIC_BUG << "OnSerializedPacket called with empty packet"; + QUIC_BUG(quic_bug_10615_2) << "OnSerializedPacket called with empty packet"; return; } encrypted_length_ = serialized_packet.encrypted_length; @@ -61,8 +61,8 @@ void QuicLegacyVersionEncapsulator::OnUnrecoverableError( QuicErrorCode error, const std::string& error_details) { unrecoverable_failure_encountered_ = true; - QUIC_BUG << "QuicLegacyVersionEncapsulator received error " << error << ": " - << error_details; + QUIC_BUG(quic_bug_10615_3) << "QuicLegacyVersionEncapsulator received error " + << error << ": " << error_details; } bool QuicLegacyVersionEncapsulator::ShouldGeneratePacket( @@ -120,23 +120,26 @@ QuicPacketLength QuicLegacyVersionEncapsulator::Encapsulate( outer_creator.SetTransmissionType(NOT_RETRANSMISSION); if (!outer_creator.AddPaddedSavedFrame(QuicFrame(outer_stream_frame), NOT_RETRANSMISSION)) { - QUIC_BUG << "Failed to add Legacy Version Encapsulation stream frame " - "(max packet length is " - << outer_creator.max_packet_length() << ") " << outer_stream_frame; + QUIC_BUG(quic_bug_10615_4) + << "Failed to add Legacy Version Encapsulation stream frame " + "(max packet length is " + << outer_creator.max_packet_length() << ") " << outer_stream_frame; return 0; } outer_creator.FlushCurrentPacket(); const QuicPacketLength encrypted_length = creator_delegate.encrypted_length_; if (creator_delegate.unrecoverable_failure_encountered_ || encrypted_length == 0) { - QUIC_BUG << "Failed to perform Legacy Version Encapsulation of " - << inner_packet.length() << " bytes"; + QUIC_BUG(quic_bug_10615_5) + << "Failed to perform Legacy Version Encapsulation of " + << inner_packet.length() << " bytes"; return 0; } if (encrypted_length > kMaxOutgoingPacketSize) { - QUIC_BUG << "Legacy Version Encapsulation outer creator generated a " - "packet with unexpected length " - << encrypted_length; + QUIC_BUG(quic_bug_10615_6) + << "Legacy Version Encapsulation outer creator generated a " + "packet with unexpected length " + << encrypted_length; return 0; } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils.cc b/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils.cc index c2931afe02e..888b658eec0 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_linux_socket_utils.cc @@ -229,7 +229,7 @@ void QuicLinuxSocketUtils::SetIpInfoInCmsgData( in6_pktinfo* pktinfo = static_cast<in6_pktinfo*>(cmsg_data); memcpy(&pktinfo->ipi6_addr, address_str.c_str(), address_str.length()); } else { - QUIC_BUG << "Unrecognized IPAddress"; + QUIC_BUG(quic_bug_10598_1) << "Unrecognized IPAddress"; } } @@ -259,7 +259,7 @@ size_t QuicLinuxSocketUtils::SetIpInfoInCmsg(const QuicIpAddress& self_address, address_string.length()); return sizeof(in6_pktinfo); } else { - QUIC_BUG << "Unrecognized IPAddress"; + QUIC_BUG(quic_bug_10598_2) << "Unrecognized IPAddress"; return 0; } } @@ -300,8 +300,9 @@ WriteResult QuicLinuxSocketUtils::WriteMultiplePackets(int fd, return WriteResult(WRITE_STATUS_OK, mhdr->num_bytes_sent(rc)); } else if (rc == 0) { - QUIC_BUG << "sendmmsg returned 0, returning WRITE_STATUS_ERROR. errno: " - << errno; + QUIC_BUG(quic_bug_10598_3) + << "sendmmsg returned 0, returning WRITE_STATUS_ERROR. errno: " + << errno; errno = EIO; } 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 a79810f9aa0..5281ffb980a 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 @@ -36,7 +36,7 @@ QuicNetworkBlackholeDetector::QuicNetworkBlackholeDetector( void QuicNetworkBlackholeDetector::OnAlarm() { QuicTime next_deadline = GetEarliestDeadline(); if (!next_deadline.IsInitialized()) { - QUIC_BUG << "BlackholeDetector alarm fired unexpectedly"; + QUIC_BUG(quic_bug_10328_1) << "BlackholeDetector alarm fired unexpectedly"; return; } @@ -79,8 +79,8 @@ void QuicNetworkBlackholeDetector::RestartDetection( blackhole_deadline_ = blackhole_deadline; path_mtu_reduction_deadline_ = path_mtu_reduction_deadline; - QUIC_BUG_IF(blackhole_deadline_.IsInitialized() && - blackhole_deadline_ != GetLastDeadline()) + QUIC_BUG_IF(quic_bug_12708_1, blackhole_deadline_.IsInitialized() && + blackhole_deadline_ != GetLastDeadline()) << "Blackhole detection deadline should be the last deadline."; UpdateAlarm(); 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 8311477219c..53882ead440 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 @@ -38,9 +38,10 @@ class QUIC_EXPORT_PRIVATE QuicOneBlockArena { static_assert(alignof(T) > 1, "Objects added to the arena must be at least 2B aligned."); if (QUIC_PREDICT_FALSE(offset_ > ArenaSize - AlignedSize<T>())) { - QUIC_BUG << "Ran out of space in QuicOneBlockArena at " << this - << ", max size was " << ArenaSize << ", failing request was " - << AlignedSize<T>() << ", end of arena was " << offset_; + QUIC_BUG(quic_bug_10593_1) + << "Ran out of space in QuicOneBlockArena at " << this + << ", max size was " << ArenaSize << ", failing request was " + << AlignedSize<T>() << ", end of arena was " << offset_; return QuicArenaScopedPtr<T>(new T(std::forward<Args>(args)...)); } 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 9dabd3af78f..42aebe5ce4b 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 @@ -46,12 +46,12 @@ QuicLongHeaderType EncryptionlevelToLongHeaderType(EncryptionLevel level) { case ENCRYPTION_ZERO_RTT: return ZERO_RTT_PROTECTED; case ENCRYPTION_FORWARD_SECURE: - QUIC_BUG + QUIC_BUG(quic_bug_12398_1) << "Try to derive long header type for packet with encryption level: " << level; return INVALID_PACKET_TYPE; default: - QUIC_BUG << level; + QUIC_BUG(quic_bug_10752_1) << level; return INVALID_PACKET_TYPE; } } @@ -171,8 +171,8 @@ void QuicPacketCreator::SetMaxPacketLength(QuicByteCount length) { max_packet_length_ = length; max_plaintext_size_ = framer_->GetMaxPlaintextSize(max_packet_length_); - QUIC_BUG_IF(max_plaintext_size_ - PacketHeaderSize() < - MinPlaintextPacketSize(framer_->version())) + QUIC_BUG_IF(quic_bug_12398_2, max_plaintext_size_ - PacketHeaderSize() < + MinPlaintextPacketSize(framer_->version())) << "Attempted to set max packet length too small"; } @@ -194,9 +194,10 @@ void QuicPacketCreator::SetMaxDatagramFrameSize( void QuicPacketCreator::SetSoftMaxPacketLength(QuicByteCount length) { QUICHE_DCHECK(CanSetMaxPacketLength()); if (length > max_packet_length_) { - QUIC_BUG << ENDPOINT - << "Try to increase max_packet_length_ in " - "SetSoftMaxPacketLength, use SetMaxPacketLength instead."; + QUIC_BUG(quic_bug_10752_2) + << ENDPOINT + << "Try to increase max_packet_length_ in " + "SetSoftMaxPacketLength, use SetMaxPacketLength instead."; return; } if (framer_->GetMaxPlaintextSize(length) < @@ -238,10 +239,10 @@ void QuicPacketCreator::UpdatePacketNumberLength( QuicPacketCount max_packets_in_flight) { if (!queued_frames_.empty()) { // Don't change creator state if there are frames queued. - QUIC_BUG << "Called UpdatePacketNumberLength with " << queued_frames_.size() - << " queued_frames. First frame type:" - << queued_frames_.front().type - << " last frame type:" << queued_frames_.back().type; + QUIC_BUG(quic_bug_10752_3) + << "Called UpdatePacketNumberLength with " << queued_frames_.size() + << " queued_frames. First frame type:" << queued_frames_.front().type + << " last frame type:" << queued_frames_.back().type; return; } @@ -271,10 +272,10 @@ void QuicPacketCreator::SkipNPacketNumbers( QuicPacketCount max_packets_in_flight) { if (!queued_frames_.empty()) { // Don't change creator state if there are frames queued. - QUIC_BUG << "Called SkipNPacketNumbers with " << queued_frames_.size() - << " queued_frames. First frame type:" - << queued_frames_.front().type - << " last frame type:" << queued_frames_.back().type; + QUIC_BUG(quic_bug_10752_4) + << "Called SkipNPacketNumbers with " << queued_frames_.size() + << " queued_frames. First frame type:" << queued_frames_.front().type + << " last frame type:" << queued_frames_.back().type; return; } if (packet_.packet_number > packet_.packet_number + count) { @@ -336,9 +337,9 @@ bool QuicPacketCreator::ConsumeDataToFillCurrentPacket( frame->stream_frame.data_length < data_size) { const std::string error_details = "Client hello won't fit in a single packet."; - QUIC_BUG << error_details << " Constructed stream frame length: " - << frame->stream_frame.data_length - << " CHLO length: " << data_size; + QUIC_BUG(quic_bug_10752_5) + << error_details << " Constructed stream frame length: " + << frame->stream_frame.data_length << " CHLO length: " << data_size; delegate_->OnUnrecoverableError(QUIC_CRYPTO_CHLO_TOO_LARGE, error_details); return false; } @@ -423,13 +424,13 @@ void QuicPacketCreator::CreateStreamFrame(QuicStreamId id, GetRetryTokenLengthLength(), GetLengthLength(), offset) || latched_hard_max_packet_length_ > 0); - QUIC_BUG_IF(!HasRoomForStreamFrame(id, offset, data_size)) + QUIC_BUG_IF(quic_bug_12398_3, !HasRoomForStreamFrame(id, offset, data_size)) << "No room for Stream frame, BytesFree: " << BytesFree() << " MinStreamFrameSize: " << QuicFramer::GetMinStreamFrameSize(framer_->transport_version(), id, offset, true, data_size); - QUIC_BUG_IF(data_size == 0 && !fin) + QUIC_BUG_IF(quic_bug_12398_4, data_size == 0 && !fin) << "Creating a stream frame for stream ID:" << id << " with no data or fin."; size_t min_frame_size = QuicFramer::GetMinStreamFrameSize( @@ -479,7 +480,7 @@ void QuicPacketCreator::FlushCurrentPacket() { } void QuicPacketCreator::OnSerializedPacket() { - QUIC_BUG_IF(packet_.encrypted_buffer == nullptr); + QUIC_BUG_IF(quic_bug_12398_5, packet_.encrypted_buffer == nullptr); SerializedPacket packet(std::move(packet_)); ClearPacket(); @@ -497,7 +498,7 @@ void QuicPacketCreator::ClearPacket() { packet_.has_ack_frequency = false; packet_.has_message = false; packet_.fate = SEND_TO_WRITER; - QUIC_BUG_IF(packet_.release_encrypted_buffer != nullptr) + QUIC_BUG_IF(quic_bug_12398_6, packet_.release_encrypted_buffer != nullptr) << "packet_.release_encrypted_buffer should be empty"; packet_.release_encrypted_buffer = nullptr; QUICHE_DCHECK(packet_.retransmittable_frames.empty()); @@ -511,9 +512,9 @@ size_t QuicPacketCreator::ReserializeInitialPacketInCoalescedPacket( size_t padding_size, char* buffer, size_t buffer_len) { - QUIC_BUG_IF(packet.encryption_level != ENCRYPTION_INITIAL); - QUIC_BUG_IF(packet.nonretransmittable_frames.empty() && - packet.retransmittable_frames.empty()) + QUIC_BUG_IF(quic_bug_12398_7, packet.encryption_level != ENCRYPTION_INITIAL); + QUIC_BUG_IF(quic_bug_12398_8, packet.nonretransmittable_frames.empty() && + packet.retransmittable_frames.empty()) << "Attempt to serialize empty ENCRYPTION_INITIAL packet in coalesced " "packet"; ScopedPacketContextSwitcher switcher( @@ -522,13 +523,13 @@ size_t QuicPacketCreator::ReserializeInitialPacketInCoalescedPacket( packet.packet_number_length, packet.encryption_level, &packet_); for (const QuicFrame& frame : packet.nonretransmittable_frames) { if (!AddFrame(frame, packet.transmission_type)) { - QUIC_BUG << "Failed to serialize frame: " << frame; + QUIC_BUG(quic_bug_10752_6) << "Failed to serialize frame: " << frame; return 0; } } for (const QuicFrame& frame : packet.retransmittable_frames) { if (!AddFrame(frame, packet.transmission_type)) { - QUIC_BUG << "Failed to serialize frame: " << frame; + QUIC_BUG(quic_bug_10752_7) << "Failed to serialize frame: " << frame; return 0; } } @@ -537,9 +538,10 @@ size_t QuicPacketCreator::ReserializeInitialPacketInCoalescedPacket( QUIC_DVLOG(2) << ENDPOINT << "Add padding of size: " << padding_size; if (!AddFrame(QuicFrame(QuicPaddingFrame(padding_size)), packet.transmission_type)) { - QUIC_BUG << "Failed to add padding of size " << padding_size - << " when serializing ENCRYPTION_INITIAL " - "packet in coalesced packet"; + QUIC_BUG(quic_bug_10752_8) + << "Failed to add padding of size " << padding_size + << " when serializing ENCRYPTION_INITIAL " + "packet in coalesced packet"; return 0; } } @@ -588,12 +590,12 @@ void QuicPacketCreator::CreateAndSerializeStreamFrame( QuicDataWriter writer(kMaxOutgoingPacketSize, encrypted_buffer); size_t length_field_offset = 0; if (!framer_->AppendPacketHeader(header, &writer, &length_field_offset)) { - QUIC_BUG << "AppendPacketHeader failed"; + QUIC_BUG(quic_bug_10752_9) << "AppendPacketHeader failed"; return; } // Create a Stream frame with the remaining space. - QUIC_BUG_IF(iov_offset == write_length && !fin) + QUIC_BUG_IF(quic_bug_12398_9, iov_offset == write_length && !fin) << "Creating a stream frame with no data or fin."; const size_t remaining_data_size = write_length - iov_offset; size_t min_frame_size = QuicFramer::GetMinStreamFrameSize( @@ -629,18 +631,18 @@ void QuicPacketCreator::CreateAndSerializeStreamFrame( // into one method that takes a QuicStreamFrame, if warranted. bool omit_frame_length = !needs_padding; if (!framer_->AppendTypeByte(QuicFrame(frame), omit_frame_length, &writer)) { - QUIC_BUG << "AppendTypeByte failed"; + QUIC_BUG(quic_bug_10752_10) << "AppendTypeByte failed"; return; } if (!framer_->AppendStreamFrame(frame, omit_frame_length, &writer)) { - QUIC_BUG << "AppendStreamFrame failed"; + QUIC_BUG(quic_bug_10752_11) << "AppendStreamFrame failed"; return; } if (needs_padding && plaintext_bytes_written < MinPlaintextPacketSize(framer_->version()) && !writer.WritePaddingBytes(MinPlaintextPacketSize(framer_->version()) - plaintext_bytes_written)) { - QUIC_BUG << "Unable to add padding bytes"; + QUIC_BUG(quic_bug_10752_12) << "Unable to add padding bytes"; return; } @@ -659,7 +661,8 @@ void QuicPacketCreator::CreateAndSerializeStreamFrame( GetStartOfEncryptedData(framer_->transport_version(), header), writer.length(), kMaxOutgoingPacketSize, encrypted_buffer); if (encrypted_length == 0) { - QUIC_BUG << "Failed to encrypt packet number " << header.packet_number; + QUIC_BUG(quic_bug_10752_13) + << "Failed to encrypt packet number " << header.packet_number; return; } // TODO(ianswett): Optimize the storage so RetransmitableFrames can be @@ -745,7 +748,7 @@ bool QuicPacketCreator::SerializePacket(QuicOwnedPacketBuffer encrypted_buffer, if (packet_.encrypted_buffer != nullptr) { const std::string error_details = "Packet's encrypted buffer is not empty before serialization"; - QUIC_BUG << error_details; + QUIC_BUG(quic_bug_10752_14) << error_details; delegate_->OnUnrecoverableError(QUIC_FAILED_TO_SERIALIZE_PACKET, error_details); return false; @@ -753,7 +756,8 @@ bool QuicPacketCreator::SerializePacket(QuicOwnedPacketBuffer encrypted_buffer, ScopedSerializationFailureHandler handler(this); QUICHE_DCHECK_LT(0u, encrypted_buffer_len); - QUIC_BUG_IF(queued_frames_.empty() && pending_padding_bytes_ == 0) + QUIC_BUG_IF(quic_bug_12398_10, + queued_frames_.empty() && pending_padding_bytes_ == 0) << "Attempt to serialize empty packet"; QuicPacketHeader header; // FillPacketHeader increments packet_number_. @@ -778,10 +782,10 @@ bool QuicPacketCreator::SerializePacket(QuicOwnedPacketBuffer encrypted_buffer, if (!framer_->HasEncrypterOfEncryptionLevel(packet_.encryption_level)) { // TODO(fayang): Use QUIC_MISSING_WRITE_KEYS for serialization failures due // to missing keys. - QUIC_BUG << ENDPOINT << "Attempting to serialize " << header - << QuicFramesToString(queued_frames_) - << " at missing encryption_level " << packet_.encryption_level - << " using " << framer_->version(); + QUIC_BUG(quic_bug_10752_15) + << ENDPOINT << "Attempting to serialize " << header + << QuicFramesToString(queued_frames_) << " at missing encryption_level " + << packet_.encryption_level << " using " << framer_->version(); return false; } @@ -792,14 +796,15 @@ bool QuicPacketCreator::SerializePacket(QuicOwnedPacketBuffer encrypted_buffer, framer_->BuildDataPacket(header, queued_frames_, encrypted_buffer.buffer, packet_size_, packet_.encryption_level); if (length == 0) { - QUIC_BUG << "Failed to serialize " << QuicFramesToString(queued_frames_) - << " at encryption_level: " << packet_.encryption_level - << ", needs_full_padding_: " << needs_full_padding_ - << ", pending_padding_bytes_: " << pending_padding_bytes_ - << ", latched_hard_max_packet_length_: " - << latched_hard_max_packet_length_ - << ", max_packet_length_: " << max_packet_length_ - << ", header: " << header; + QUIC_BUG(quic_bug_10752_16) + << "Failed to serialize " << QuicFramesToString(queued_frames_) + << " at encryption_level: " << packet_.encryption_level + << ", needs_full_padding_: " << needs_full_padding_ + << ", pending_padding_bytes_: " << pending_padding_bytes_ + << ", latched_hard_max_packet_length_: " + << latched_hard_max_packet_length_ + << ", max_packet_length_: " << max_packet_length_ + << ", header: " << header; return false; } @@ -820,7 +825,8 @@ bool QuicPacketCreator::SerializePacket(QuicOwnedPacketBuffer encrypted_buffer, GetStartOfEncryptedData(framer_->transport_version(), header), length, encrypted_buffer_len, encrypted_buffer.buffer); if (encrypted_length == 0) { - QUIC_BUG << "Failed to encrypt packet number " << packet_.packet_number; + QUIC_BUG(quic_bug_10752_17) + << "Failed to encrypt packet number " << packet_.packet_number; return false; } @@ -835,7 +841,8 @@ bool QuicPacketCreator::SerializePacket(QuicOwnedPacketBuffer encrypted_buffer, std::unique_ptr<SerializedPacket> QuicPacketCreator::SerializeConnectivityProbingPacket() { - QUIC_BUG_IF(VersionHasIetfQuicFrames(framer_->transport_version())) + QUIC_BUG_IF(quic_bug_12398_11, + VersionHasIetfQuicFrames(framer_->transport_version())) << "Must not be version 99 to serialize padded ping connectivity probe"; RemoveSoftMaxPacketLength(); QuicPacketHeader header; @@ -873,7 +880,8 @@ QuicPacketCreator::SerializeConnectivityProbingPacket() { std::unique_ptr<SerializedPacket> QuicPacketCreator::SerializePathChallengeConnectivityProbingPacket( const QuicPathFrameBuffer& payload) { - QUIC_BUG_IF(!VersionHasIetfQuicFrames(framer_->transport_version())) + QUIC_BUG_IF(quic_bug_12398_12, + !VersionHasIetfQuicFrames(framer_->transport_version())) << "Must be version 99 to serialize path challenge connectivity probe, " "is version " << framer_->transport_version(); @@ -915,7 +923,8 @@ std::unique_ptr<SerializedPacket> QuicPacketCreator::SerializePathResponseConnectivityProbingPacket( const QuicCircularDeque<QuicPathFrameBuffer>& payloads, const bool is_padded) { - QUIC_BUG_IF(!VersionHasIetfQuicFrames(framer_->transport_version())) + QUIC_BUG_IF(quic_bug_12398_13, + !VersionHasIetfQuicFrames(framer_->transport_version())) << "Must be version 99 to serialize path response connectivity probe, is " "version " << framer_->transport_version(); @@ -986,7 +995,7 @@ size_t QuicPacketCreator::BuildPathResponsePacket( const bool is_padded, EncryptionLevel level) { if (payloads.empty()) { - QUIC_BUG + QUIC_BUG(quic_bug_12398_14) << "Attempt to generate connectivity response with no request payloads"; return 0; } @@ -1042,11 +1051,12 @@ size_t QuicPacketCreator::SerializeCoalescedPacket( char* buffer, size_t buffer_len) { if (HasPendingFrames()) { - QUIC_BUG << "Try to serialize coalesced packet with pending frames"; + QUIC_BUG(quic_bug_10752_18) + << "Try to serialize coalesced packet with pending frames"; return 0; } RemoveSoftMaxPacketLength(); - QUIC_BUG_IF(coalesced.length() == 0) + QUIC_BUG_IF(quic_bug_12398_15, coalesced.length() == 0) << "Attempt to serialize empty coalesced packet"; size_t packet_length = 0; if (coalesced.initial_packet() != nullptr) { @@ -1062,8 +1072,9 @@ size_t QuicPacketCreator::SerializeCoalescedPacket( size_t initial_length = ReserializeInitialPacketInCoalescedPacket( *coalesced.initial_packet(), padding_size, buffer, buffer_len); if (initial_length == 0) { - QUIC_BUG << "Failed to reserialize ENCRYPTION_INITIAL packet in " - "coalesced packet"; + QUIC_BUG(quic_bug_10752_19) + << "Failed to reserialize ENCRYPTION_INITIAL packet in " + "coalesced packet"; return 0; } buffer += initial_length; @@ -1187,8 +1198,9 @@ void QuicPacketCreator::SetRetryToken(absl::string_view retry_token) { bool QuicPacketCreator::ConsumeRetransmittableControlFrame( const QuicFrame& frame) { - QUIC_BUG_IF(IsControlFrame(frame.type) && !GetControlFrameId(frame) && - frame.type != PING_FRAME) + QUIC_BUG_IF(quic_bug_12398_16, IsControlFrame(frame.type) && + !GetControlFrameId(frame) && + frame.type != PING_FRAME) << "Adding a control frame with no control frame id: " << frame; QUICHE_DCHECK(QuicUtils::IsRetransmittableFrame(frame.type)) << frame; MaybeBundleAckOpportunistically(); @@ -1206,8 +1218,9 @@ bool QuicPacketCreator::ConsumeRetransmittableControlFrame( return false; } const bool success = AddFrame(frame, next_transmission_type_); - QUIC_BUG_IF(!success) << "Failed to add frame:" << frame - << " transmission_type:" << next_transmission_type_; + QUIC_BUG_IF(quic_bug_10752_20, !success) + << "Failed to add frame:" << frame + << " transmission_type:" << next_transmission_type_; return success; } @@ -1215,12 +1228,13 @@ QuicConsumedData QuicPacketCreator::ConsumeData(QuicStreamId id, size_t write_length, QuicStreamOffset offset, StreamSendingState state) { - QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when " - "generator tries to write stream data."; + QUIC_BUG_IF(quic_bug_10752_21, !flusher_attached_) + << "Packet flusher is not attached when " + "generator tries to write stream data."; bool has_handshake = QuicUtils::IsCryptoStreamId(transport_version(), id); MaybeBundleAckOpportunistically(); bool fin = state != NO_FIN; - QUIC_BUG_IF(has_handshake && fin) + QUIC_BUG_IF(quic_bug_12398_17, has_handshake && fin) << "Handshake packets should never send a fin"; // To make reasoning about crypto frames easier, we don't combine them with // other retransmittable frames in a single packet. @@ -1236,7 +1250,7 @@ QuicConsumedData QuicPacketCreator::ConsumeData(QuicStreamId id, } if (!fin && (write_length == 0)) { - QUIC_BUG << "Attempt to consume empty data without FIN."; + QUIC_BUG(quic_bug_10752_22) << "Attempt to consume empty data without FIN."; return QuicConsumedData(0, false); } // We determine if we can enter the fast path before executing @@ -1259,7 +1273,7 @@ QuicConsumedData QuicPacketCreator::ConsumeData(QuicStreamId id, next_transmission_type_, &frame)) { // The creator is always flushed if there's not enough room for a new // stream frame before ConsumeData, so ConsumeData should always succeed. - QUIC_BUG << "Failed to ConsumeData, stream:" << id; + QUIC_BUG(quic_bug_10752_23) << "Failed to ConsumeData, stream:" << id; return QuicConsumedData(0, false); } @@ -1323,7 +1337,7 @@ QuicConsumedData QuicPacketCreator::ConsumeDataFastPath( if (bytes_consumed == 0) { const std::string error_details = "Failed in CreateAndSerializeStreamFrame."; - QUIC_BUG << error_details; + QUIC_BUG(quic_bug_10752_24) << error_details; delegate_->OnUnrecoverableError(QUIC_FAILED_TO_SERIALIZE_PACKET, error_details); break; @@ -1340,8 +1354,9 @@ size_t QuicPacketCreator::ConsumeCryptoData(EncryptionLevel level, QuicStreamOffset offset) { QUIC_DVLOG(2) << "ConsumeCryptoData " << level << " write_length " << write_length << " offset " << offset; - QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when " - "generator tries to write crypto data."; + QUIC_BUG_IF(quic_bug_10752_25, !flusher_attached_) + << "Packet flusher is not attached when " + "generator tries to write crypto data."; MaybeBundleAckOpportunistically(); // To make reasoning about crypto frames easier, we don't combine them with // other retransmittable frames in a single packet. @@ -1365,7 +1380,8 @@ size_t QuicPacketCreator::ConsumeCryptoData(EncryptionLevel level, // The only pending data in the packet is non-retransmittable frames. I'm // assuming here that they won't occupy so much of the packet that a // CRYPTO frame won't fit. - QUIC_BUG << "Failed to ConsumeCryptoData at level " << level; + QUIC_BUG(quic_bug_10752_26) + << "Failed to ConsumeCryptoData at level " << level; return 0; } total_bytes_consumed += frame.crypto_frame->data_length; @@ -1381,8 +1397,9 @@ size_t QuicPacketCreator::ConsumeCryptoData(EncryptionLevel level, void QuicPacketCreator::GenerateMtuDiscoveryPacket(QuicByteCount target_mtu) { // MTU discovery frames must be sent by themselves. if (!CanSetMaxPacketLength()) { - QUIC_BUG << "MTU discovery packets should only be sent when no other " - << "frames needs to be sent."; + QUIC_BUG(quic_bug_10752_27) + << "MTU discovery packets should only be sent when no other " + << "frames needs to be sent."; return; } const QuicByteCount current_mtu = max_packet_length(); @@ -1398,8 +1415,9 @@ void QuicPacketCreator::GenerateMtuDiscoveryPacket(QuicByteCount target_mtu) { FlushCurrentPacket(); // The only reason AddFrame can fail is that the packet is too full to fit in // a ping. This is not possible for any sane MTU. - QUIC_BUG_IF(!success) << "Failed to send path MTU target_mtu:" << target_mtu - << " transmission_type:" << next_transmission_type_; + QUIC_BUG_IF(quic_bug_10752_28, !success) + << "Failed to send path MTU target_mtu:" << target_mtu + << " transmission_type:" << next_transmission_type_; // Reset the packet length back. SetMaxPacketLength(current_mtu); @@ -1416,17 +1434,20 @@ void QuicPacketCreator::MaybeBundleAckOpportunistically() { } const bool flushed = FlushAckFrame(delegate_->MaybeBundleAckOpportunistically()); - QUIC_BUG_IF(!flushed) << "Failed to flush ACK frame. encryption_level:" - << packet_.encryption_level; + QUIC_BUG_IF(quic_bug_10752_29, !flushed) + << "Failed to flush ACK frame. encryption_level:" + << packet_.encryption_level; } bool QuicPacketCreator::FlushAckFrame(const QuicFrames& frames) { - QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when " - "generator tries to send ACK frame."; + QUIC_BUG_IF(quic_bug_10752_30, !flusher_attached_) + << "Packet flusher is not attached when " + "generator tries to send ACK frame."; // MaybeBundleAckOpportunistically could be called nestedly when sending a // control frame causing another control frame to be sent. - QUIC_BUG_IF(GetQuicReloadableFlag(quic_single_ack_in_packet2) && - !frames.empty() && has_ack()) + QUIC_BUG_IF(quic_bug_12398_18, + GetQuicReloadableFlag(quic_single_ack_in_packet2) && + !frames.empty() && has_ack()) << "Trying to flush " << frames << " when there is ACK queued"; for (const auto& frame : frames) { QUICHE_DCHECK(frame.type == ACK_FRAME || frame.type == STOP_WAITING_FRAME); @@ -1444,7 +1465,7 @@ bool QuicPacketCreator::FlushAckFrame(const QuicFrames& frames) { return false; } const bool success = AddFrame(frame, next_transmission_type_); - QUIC_BUG_IF(!success) << "Failed to flush " << frame; + QUIC_BUG_IF(quic_bug_10752_31, !success) << "Failed to flush " << frame; } return true; } @@ -1466,7 +1487,8 @@ void QuicPacketCreator::Flush() { flusher_attached_ = false; if (GetQuicFlag(FLAGS_quic_export_write_path_stats_at_server)) { if (!write_start_packet_number_.IsInitialized()) { - QUIC_BUG << "write_start_packet_number is not initialized"; + QUIC_BUG(quic_bug_10752_32) + << "write_start_packet_number is not initialized"; return; } QUIC_SERVER_HISTOGRAM_COUNTS( @@ -1499,8 +1521,9 @@ void QuicPacketCreator::SetTransmissionType(TransmissionType type) { MessageStatus QuicPacketCreator::AddMessageFrame(QuicMessageId message_id, QuicMemSliceSpan message) { - QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when " - "generator tries to add message frame."; + QUIC_BUG_IF(quic_bug_10752_33, !flusher_attached_) + << "Packet flusher is not attached when " + "generator tries to add message frame."; MaybeBundleAckOpportunistically(); const QuicByteCount message_length = message.total_length(); if (message_length > GetCurrentLargestMessagePayload()) { @@ -1512,7 +1535,7 @@ MessageStatus QuicPacketCreator::AddMessageFrame(QuicMessageId message_id, QuicMessageFrame* frame = new QuicMessageFrame(message_id, message); const bool success = AddFrame(QuicFrame(frame), next_transmission_type_); if (!success) { - QUIC_BUG << "Failed to send message " << message_id; + QUIC_BUG(quic_bug_10752_34) << "Failed to send message " << message_id; delete frame; return MESSAGE_STATUS_INTERNAL_ERROR; } @@ -1576,7 +1599,7 @@ size_t QuicPacketCreator::GetSerializedFrameLength(const QuicFrame& frame) { return serialized_frame_length; } if (BytesFree() < serialized_frame_length) { - QUIC_BUG << ENDPOINT << "Frame does not fit: " << frame; + QUIC_BUG(quic_bug_10752_35) << ENDPOINT << "Frame does not fit: " << frame; return 0; } // Please note bytes_free does not take |frame|'s expansion into account. @@ -1604,6 +1627,21 @@ bool QuicPacketCreator::AddFrame(const QuicFrame& frame, return false; } + // Sanity check to ensure we don't send frames at the wrong encryption level. + QUICHE_DCHECK( + packet_.encryption_level == ENCRYPTION_ZERO_RTT || + packet_.encryption_level == ENCRYPTION_FORWARD_SECURE || + (frame.type != GOAWAY_FRAME && frame.type != WINDOW_UPDATE_FRAME && + frame.type != HANDSHAKE_DONE_FRAME && + frame.type != NEW_CONNECTION_ID_FRAME && + frame.type != MAX_STREAMS_FRAME && frame.type != STREAMS_BLOCKED_FRAME && + frame.type != PATH_RESPONSE_FRAME && + frame.type != PATH_CHALLENGE_FRAME && frame.type != STOP_SENDING_FRAME && + frame.type != MESSAGE_FRAME && frame.type != NEW_TOKEN_FRAME && + frame.type != RETIRE_CONNECTION_ID_FRAME && + frame.type != ACK_FREQUENCY_FRAME)) + << frame.type << " not allowed at " << packet_.encryption_level; + if (frame.type == STREAM_FRAME) { if (MaybeCoalesceStreamFrame(frame.stream_frame)) { LogCoalesceStreamFrameStatus(true); @@ -1775,8 +1813,9 @@ void QuicPacketCreator::MaybeAddPadding() { bool success = AddFrame(QuicFrame(QuicPaddingFrame(padding_bytes)), packet_.transmission_type); - QUIC_BUG_IF(!success) << "Failed to add padding_bytes: " << padding_bytes - << " transmission_type: " << packet_.transmission_type; + QUIC_BUG_IF(quic_bug_10752_36, !success) + << "Failed to add padding_bytes: " << padding_bytes + << " transmission_type: " << packet_.transmission_type; } bool QuicPacketCreator::IncludeNonceInPublicHeader() const { @@ -1901,7 +1940,7 @@ bool QuicPacketCreator::AttemptingToSendUnencryptedStreamData() { const std::string error_details = absl::StrCat("Cannot send stream data with level: ", EncryptionLevelToString(packet_.encryption_level)); - QUIC_BUG << error_details; + QUIC_BUG(quic_bug_10752_37) << error_details; delegate_->OnUnrecoverableError(QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA, error_details); return true; @@ -1970,7 +2009,8 @@ QuicPacketCreator::ScopedPeerAddressContext::ScopedPeerAddressContext( QuicPacketCreator* creator, QuicSocketAddress address) : creator_(creator), old_peer_address_(creator_->packet_.peer_address) { - QUIC_BUG_IF(!creator_->packet_.peer_address.IsInitialized()) + QUIC_BUG_IF(quic_bug_12398_19, + !creator_->packet_.peer_address.IsInitialized()) << "Context is used before seralized packet's peer address is " "initialized."; creator_->SetDefaultPeerAddress(address); @@ -1994,7 +2034,7 @@ QuicPacketCreator::ScopedSerializationFailureHandler:: if (creator_->packet_.encrypted_buffer == nullptr) { const std::string error_details = "Failed to SerializePacket."; - QUIC_BUG << error_details; + QUIC_BUG(quic_bug_10752_38) << error_details; creator_->delegate_->OnUnrecoverableError(QUIC_FAILED_TO_SERIALIZE_PACKET, error_details); } @@ -2010,6 +2050,12 @@ void QuicPacketCreator::set_encryption_level(EncryptionLevel level) { void QuicPacketCreator::AddPathChallengeFrame( const QuicPathFrameBuffer& payload) { + // TODO(danzh) Unify similar checks at several entry points into one in + // AddFrame(). Sort out test helper functions and peer class that don't + // enforce this check. + QUIC_BUG_IF(quic_bug_10752_39, !flusher_attached_) + << "Packet flusher is not attached when " + "generator tries to write stream data."; // Write a PATH_CHALLENGE frame, which has a random 8-byte payload. auto path_challenge_frame = new QuicPathChallengeFrame(0, payload); QuicFrame frame(path_challenge_frame); @@ -2035,7 +2081,7 @@ bool QuicPacketCreator::AddPathResponseFrame( } QUIC_DVLOG(1) << ENDPOINT << "Can't send PATH_RESPONSE now"; - QUIC_RELOADABLE_FLAG_COUNT_N(quic_send_path_response, 5, 5); + QUIC_RELOADABLE_FLAG_COUNT_N(quic_send_path_response2, 5, 5); delete path_response; return false; } @@ -2054,7 +2100,7 @@ bool QuicPacketCreator::AddPaddedFrameWithRetry(const QuicFrame& frame) { return false; } bool success = AddPaddedSavedFrame(frame, NOT_RETRANSMISSION); - QUIC_BUG_IF(!success); + QUIC_BUG_IF(quic_bug_12398_20, !success); return true; } 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 8ff44c1f96f..5986bd2890f 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 @@ -25,7 +25,6 @@ #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_expect_bug.h" #include "quic/platform/api/quic_flags.h" -#include "quic/platform/api/quic_ptr_util.h" #include "quic/platform/api/quic_socket_address.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_framer_peer.h" @@ -299,7 +298,9 @@ TEST_P(QuicPacketCreatorTest, SerializeFrames) { for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { EncryptionLevel level = static_cast<EncryptionLevel>(i); creator_.set_encryption_level(level); - frames_.push_back(QuicFrame(new QuicAckFrame(InitAckFrame(1)))); + if (level != ENCRYPTION_ZERO_RTT) { + frames_.push_back(QuicFrame(new QuicAckFrame(InitAckFrame(1)))); + } QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId( client_framer_.transport_version(), Perspective::IS_CLIENT); if (level != ENCRYPTION_INITIAL && level != ENCRYPTION_HANDSHAKE) { @@ -308,7 +309,9 @@ TEST_P(QuicPacketCreatorTest, SerializeFrames) { } SerializedPacket serialized = SerializeAllFrames(frames_); EXPECT_EQ(level, serialized.encryption_level); - delete frames_[0].ack_frame; + if (level != ENCRYPTION_ZERO_RTT) { + delete frames_[0].ack_frame; + } frames_.clear(); { @@ -318,13 +321,15 @@ TEST_P(QuicPacketCreatorTest, SerializeFrames) { EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); - EXPECT_CALL(framer_visitor_, OnAckFrameStart(_, _)) - .WillOnce(Return(true)); - EXPECT_CALL(framer_visitor_, - OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2))) - .WillOnce(Return(true)); - EXPECT_CALL(framer_visitor_, OnAckFrameEnd(QuicPacketNumber(1))) - .WillOnce(Return(true)); + if (level != ENCRYPTION_ZERO_RTT) { + EXPECT_CALL(framer_visitor_, OnAckFrameStart(_, _)) + .WillOnce(Return(true)); + EXPECT_CALL(framer_visitor_, + OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2))) + .WillOnce(Return(true)); + EXPECT_CALL(framer_visitor_, OnAckFrameEnd(QuicPacketNumber(1))) + .WillOnce(Return(true)); + } if (level != ENCRYPTION_INITIAL && level != ENCRYPTION_HANDSHAKE) { EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); } @@ -2118,7 +2123,9 @@ TEST_P(QuicPacketCreatorTest, SerializeCoalescedPacket) { EncryptionLevel level = static_cast<EncryptionLevel>(i); creator_.set_encryption_level(level); QuicAckFrame ack_frame(InitAckFrame(1)); - frames_.push_back(QuicFrame(&ack_frame)); + if (level != ENCRYPTION_ZERO_RTT) { + frames_.push_back(QuicFrame(&ack_frame)); + } if (level != ENCRYPTION_INITIAL && level != ENCRYPTION_HANDSHAKE) { frames_.push_back( QuicFrame(QuicStreamFrame(1, false, 0u, absl::string_view()))); @@ -2156,20 +2163,26 @@ TEST_P(QuicPacketCreatorTest, SerializeCoalescedPacket) { EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_)); EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); - EXPECT_CALL(framer_visitor_, OnAckFrameStart(_, _)).WillOnce(Return(true)); - EXPECT_CALL(framer_visitor_, - OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2))) - .WillOnce(Return(true)); - EXPECT_CALL(framer_visitor_, OnAckFrameEnd(_)).WillOnce(Return(true)); + if (i != ENCRYPTION_ZERO_RTT) { + EXPECT_CALL(framer_visitor_, OnAckFrameStart(_, _)) + .WillOnce(Return(true)); + EXPECT_CALL(framer_visitor_, + OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2))) + .WillOnce(Return(true)); + EXPECT_CALL(framer_visitor_, OnAckFrameEnd(_)).WillOnce(Return(true)); + } if (i == ENCRYPTION_INITIAL) { // Verify padding is added. EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); - } else { + } else if (i != ENCRYPTION_ZERO_RTT) { EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)).Times(testing::AtMost(1)); } if (i != ENCRYPTION_INITIAL && i != ENCRYPTION_HANDSHAKE) { EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); } + if (i == ENCRYPTION_ZERO_RTT) { + EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)); + } EXPECT_CALL(framer_visitor_, OnPacketComplete()); server_framer_.ProcessPacket(*packets[i]); diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_number.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_number.cc index ba1c509360b..b2821b454d7 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_number.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_number.cc @@ -2,10 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "quic/core/quic_packet_number.h" + #include <algorithm> +#include <limits> -#include "quic/core/quic_packet_number.h" -#include "common/platform/api/quiche_text_utils.h" +#include "absl/strings/str_cat.h" namespace quic { @@ -98,7 +100,7 @@ std::string QuicPacketNumber::ToString() const { if (!IsInitialized()) { return "uninitialized"; } - return quiche::QuicheTextUtils::Uint64ToString(ToUint64()); + return absl::StrCat(ToUint64()); } std::ostream& operator<<(std::ostream& os, const QuicPacketNumber& p) { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_number.h b/chromium/net/third_party/quiche/src/quic/core/quic_packet_number.h index a1ea62fbec1..affd714336e 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_number.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_number.h @@ -11,7 +11,6 @@ #include "quic/platform/api/quic_export.h" #include "quic/platform/api/quic_logging.h" -#include "quic/platform/api/quic_uint128.h" namespace quic { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_reader.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_reader.cc index 4b0eb4b12ee..3a8a9b4fe3d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packet_reader.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_reader.cc @@ -67,7 +67,7 @@ bool QuicPacketReader::ReadAndDispatchPackets( } if (!result.packet_info.HasValue(QuicUdpPacketInfoBit::PEER_ADDRESS)) { - QUIC_BUG << "Unable to get peer socket address."; + QUIC_BUG(quic_bug_10329_1) << "Unable to get peer socket address."; continue; } @@ -77,7 +77,7 @@ bool QuicPacketReader::ReadAndDispatchPackets( QuicIpAddress self_ip = GetSelfIpFromPacketInfo( result.packet_info, peer_address.host().IsIPv6()); if (!self_ip.IsInitialized()) { - QUIC_BUG << "Unable to get self IP address."; + QUIC_BUG(quic_bug_10329_2) << "Unable to get self IP address."; continue; } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packets.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packets.cc index 5f3e5a2697f..5cc593f41bf 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packets.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packets.cc @@ -183,7 +183,7 @@ QuicPacketHeader::QuicPacketHeader() nonce(nullptr), form(GOOGLE_QUIC_PACKET), long_packet_type(INITIAL), - possible_stateless_reset_token(0), + possible_stateless_reset_token({}), retry_token_length_length(VARIABLE_LENGTH_INTEGER_LENGTH_0), retry_token(absl::string_view()), length_length(VARIABLE_LENGTH_INTEGER_LENGTH_0), @@ -215,11 +215,11 @@ QuicVersionNegotiationPacket::QuicVersionNegotiationPacket( QuicVersionNegotiationPacket::~QuicVersionNegotiationPacket() {} QuicIetfStatelessResetPacket::QuicIetfStatelessResetPacket() - : stateless_reset_token(0) {} + : stateless_reset_token({}) {} QuicIetfStatelessResetPacket::QuicIetfStatelessResetPacket( const QuicPacketHeader& header, - QuicUint128 token) + StatelessResetToken token) : header(header), stateless_reset_token(token) {} QuicIetfStatelessResetPacket::QuicIetfStatelessResetPacket( diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packets.h b/chromium/net/third_party/quiche/src/quic/core/quic_packets.h index 4643ebcba6e..a3fc59dbd7d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packets.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packets.h @@ -7,12 +7,10 @@ #include <cstddef> #include <cstdint> -#include <limits> -#include <list> #include <memory> #include <ostream> +#include <string> #include <utility> -#include <vector> #include "absl/strings/string_view.h" #include "quic/core/frames/quic_frame.h" @@ -25,7 +23,6 @@ #include "quic/core/quic_versions.h" #include "quic/platform/api/quic_export.h" #include "quic/platform/api/quic_socket_address.h" -#include "quic/platform/api/quic_uint128.h" namespace quic { @@ -155,7 +152,7 @@ struct QUIC_EXPORT_PRIVATE QuicPacketHeader { // Only valid if |has_possible_stateless_reset_token| is true. // Stores last 16 bytes of a this packet, used to check whether this packet is // a stateless reset packet on decryption failure. - QuicUint128 possible_stateless_reset_token; + StatelessResetToken possible_stateless_reset_token; // Length of the retry token length variable length integer field, // carried only by v99 IETF Initial packets. QuicVariableLengthIntegerLength retry_token_length_length; @@ -196,12 +193,12 @@ struct QUIC_EXPORT_PRIVATE QuicVersionNegotiationPacket { struct QUIC_EXPORT_PRIVATE QuicIetfStatelessResetPacket { QuicIetfStatelessResetPacket(); QuicIetfStatelessResetPacket(const QuicPacketHeader& header, - QuicUint128 token); + StatelessResetToken token); QuicIetfStatelessResetPacket(const QuicIetfStatelessResetPacket& other); ~QuicIetfStatelessResetPacket(); QuicPacketHeader header; - QuicUint128 stateless_reset_token; + StatelessResetToken stateless_reset_token; }; class QUIC_EXPORT_PRIVATE QuicData { diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packets_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packets_test.cc index 5e206f46827..a07e289cb71 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_packets_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_packets_test.cc @@ -4,7 +4,7 @@ #include "quic/core/quic_packets.h" -#include "quic/platform/api/quic_ptr_util.h" +#include "absl/memory/memory.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/quic_test_utils.h" #include "common/test_tools/quiche_test_utils.h" @@ -86,7 +86,7 @@ TEST_F(QuicPacketsTest, CopySerializedPacket) { packet.nonretransmittable_frames.push_back(QuicFrame(&ack_frame)); packet.nonretransmittable_frames.push_back(QuicFrame(QuicPaddingFrame(-1))); - std::unique_ptr<SerializedPacket> copy = QuicWrapUnique<SerializedPacket>( + std::unique_ptr<SerializedPacket> copy = absl::WrapUnique<SerializedPacket>( CopySerializedPacket(packet, &allocator, /*copy_buffer=*/true)); EXPECT_EQ(quic::QuicPacketNumber(1), copy->packet_number); EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, copy->packet_number_length); @@ -102,7 +102,7 @@ TEST_F(QuicPacketsTest, CopySerializedPacket) { "encrypted_buffer", copy->encrypted_buffer, copy->encrypted_length, packet.encrypted_buffer, packet.encrypted_length); - std::unique_ptr<SerializedPacket> copy2 = QuicWrapUnique<SerializedPacket>( + std::unique_ptr<SerializedPacket> copy2 = absl::WrapUnique<SerializedPacket>( CopySerializedPacket(packet, &allocator, /*copy_buffer=*/false)); EXPECT_EQ(packet.encrypted_buffer, copy2->encrypted_buffer); EXPECT_EQ(1000u, copy2->encrypted_length); 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 f2abef0543f..bb83e9472f7 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 @@ -47,7 +47,7 @@ void QuicPathValidator::OnPathResponse(const QuicPathFrameBuffer& probing_data, } QUIC_DVLOG(1) << "Match PATH_RESPONSE received on " << self_address; - QUIC_BUG_IF(!path_context_->self_address().IsInitialized()) + QUIC_BUG_IF(quic_bug_12402_1, !path_context_->self_address().IsInitialized()) << "Self address should have been known by now"; if (self_address != path_context_->self_address()) { QUIC_DVLOG(1) << "Expect the response to be received on " @@ -72,7 +72,8 @@ void QuicPathValidator::StartPathValidation( QUIC_DLOG(INFO) << "Start validating path " << *context << " via writer: " << context->WriterToUse(); if (path_context_ != nullptr) { - QUIC_BUG << "There is an on-going validation on path " << *path_context_; + QUIC_BUG(quic_bug_10876_1) + << "There is an on-going validation on path " << *path_context_; ResetPathValidation(); } 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 e433f0c07be..1dfafc3b77a 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 @@ -42,7 +42,7 @@ class MockSendDelegate : public QuicPathValidator::SendDelegate { MOCK_METHOD(QuicTime, GetRetryTimeout, (const QuicSocketAddress&, QuicPacketWriter*), - (const override)); + (const, override)); }; class QuicPathValidatorTest : public QuicTest { 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 14ed8cd6931..5187c61def4 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 @@ -116,12 +116,6 @@ QUIC_PROTOCOL_FLAG( "descendents) will be automatically converted to lower case.") QUIC_PROTOCOL_FLAG( - bool, - quic_enable_http3_server_push, - false, - "If true, server push will be allowed in QUIC versions that use HTTP/3.") - -QUIC_PROTOCOL_FLAG( int32_t, quic_bbr2_default_probe_bw_base_duration_ms, 2000, @@ -248,4 +242,11 @@ QUIC_PROTOCOL_FLAG(bool, true, "If true, QUIC servers will defer sending in response to " "incoming packets by default.") + +QUIC_PROTOCOL_FLAG( + bool, + quic_header_size_limit_includes_overhead, + true, + "If true, QUIC QPACK decoder includes 32-bytes overheader per entry while " + "comparing request/response header size against its upper limit.") #endif diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.cc index 2b434424833..a1b78a4fb86 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.cc @@ -309,7 +309,7 @@ QuicPacketNumber QuicReceivedPacketManager::GetLargestObserved() const { QuicPacketNumber QuicReceivedPacketManager::PeerFirstSendingPacketNumber() const { if (!least_received_packet_number_.IsInitialized()) { - QUIC_BUG << "No packets have been received yet"; + QUIC_BUG(quic_bug_10849_1) << "No packets have been received yet"; return QuicPacketNumber(1); } return least_received_packet_number_; 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 97c7c250aa3..53d9f0eca22 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 @@ -179,7 +179,7 @@ void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) { if (config.HasClientSentConnectionOption(kPTOS, perspective)) { if (!pto_enabled_) { - QUIC_PEER_BUG + QUIC_PEER_BUG(quic_peer_bug_12552_1) << "PTO is not enabled when receiving PTOS connection option."; pto_enabled_ = true; max_probe_packets_per_pto_ = 1; @@ -339,6 +339,21 @@ void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) { void QuicSentPacketManager::ApplyConnectionOptions( const QuicTagVector& connection_options) { + absl::optional<CongestionControlType> cc_type; + if (ContainsQuicTag(connection_options, kB2ON)) { + cc_type = kBBRv2; + } else if (ContainsQuicTag(connection_options, kTBBR)) { + cc_type = kBBR; + } else if (ContainsQuicTag(connection_options, kRENO)) { + cc_type = kRenoBytes; + } else if (ContainsQuicTag(connection_options, kQBIC)) { + cc_type = kCubicBytes; + } + + if (cc_type.has_value()) { + SetSendAlgorithm(*cc_type); + } + send_algorithm_->ApplyConnectionOptions(connection_options); } @@ -624,9 +639,10 @@ void QuicSentPacketManager::MarkForRetransmission( unacked_packets_.GetMutableTransmissionInfo(packet_number); // A previous RTO retransmission may cause connection close; packets without // retransmittable frames can be marked for loss retransmissions. - QUIC_BUG_IF(transmission_type != LOSS_RETRANSMISSION && - transmission_type != RTO_RETRANSMISSION && - !unacked_packets_.HasRetransmittableFrames(*transmission_info)) + QUIC_BUG_IF(quic_bug_12552_2, transmission_type != LOSS_RETRANSMISSION && + transmission_type != RTO_RETRANSMISSION && + !unacked_packets_.HasRetransmittableFrames( + *transmission_info)) << "packet number " << packet_number << " transmission_type: " << transmission_type << " transmission_info " << transmission_info->DebugString(); @@ -761,7 +777,8 @@ QuicAckFrequencyFrame QuicSentPacketManager::GetUpdatedAckFrequencyFrame() const { QuicAckFrequencyFrame frame; if (!CanSendAckFrequency()) { - QUIC_BUG << "New AckFrequencyFrame is created while it shouldn't."; + QUIC_BUG(quic_bug_10750_1) + << "New AckFrequencyFrame is created while it shouldn't."; return frame; } @@ -789,7 +806,8 @@ bool QuicSentPacketManager::OnPacketSent( QuicPacketNumber packet_number = packet.packet_number; QUICHE_DCHECK_LE(FirstSendingPacketNumber(), packet_number); QUICHE_DCHECK(!unacked_packets_.IsUnacked(packet_number)); - QUIC_BUG_IF(packet.encrypted_length == 0) << "Cannot send empty packets."; + QUIC_BUG_IF(quic_bug_10750_2, packet.encrypted_length == 0) + << "Cannot send empty packets."; if (pending_timer_transmission_count_ > 0) { --pending_timer_transmission_count_; } @@ -880,7 +898,8 @@ QuicSentPacketManager::OnRetransmissionTimeout() { pending_timer_transmission_count_ = max_probe_packets_per_pto_; return PTO_MODE; } - QUIC_BUG << "Unknown retransmission mode " << GetRetransmissionMode(); + QUIC_BUG(quic_bug_10750_3) + << "Unknown retransmission mode " << GetRetransmissionMode(); return GetRetransmissionMode(); } @@ -953,7 +972,7 @@ bool QuicSentPacketManager::MaybeRetransmitOldestPacket(TransmissionType type) { void QuicSentPacketManager::RetransmitRtoPackets() { QUICHE_DCHECK(!pto_enabled_); - QUIC_BUG_IF(pending_timer_transmission_count_ > 0) + QUIC_BUG_IF(quic_bug_12552_3, pending_timer_transmission_count_ > 0) << "Retransmissions already queued:" << pending_timer_transmission_count_; // Mark two packets for retransmission. std::vector<QuicPacketNumber> retransmissions; @@ -983,7 +1002,7 @@ void QuicSentPacketManager::RetransmitRtoPackets() { MarkForRetransmission(retransmission, RTO_RETRANSMISSION); } if (retransmissions.empty()) { - QUIC_BUG_IF(pending_timer_transmission_count_ != 0); + QUIC_BUG_IF(quic_bug_12552_4, pending_timer_transmission_count_ != 0); // No packets to be RTO retransmitted, raise up a credit to allow // connection to send. QUIC_CODE_COUNT(no_packets_to_be_rto_retransmitted); @@ -1000,8 +1019,9 @@ void QuicSentPacketManager::MaybeSendProbePackets() { // Find out the packet number space to send probe packets. if (!GetEarliestPacketSentTimeForPto(&packet_number_space) .IsInitialized()) { - QUIC_BUG_IF(unacked_packets_.perspective() == Perspective::IS_SERVER) - << "earlist_sent_time not initialized when trying to send PTO " + QUIC_BUG_IF(quic_earliest_sent_time_not_initialized, + unacked_packets_.perspective() == Perspective::IS_SERVER) + << "earliest_sent_time not initialized when trying to send PTO " "retransmissions"; return; } @@ -1173,8 +1193,8 @@ bool QuicSentPacketManager::MaybeUpdateRTT(QuicPacketNumber largest_acked, unacked_packets_.GetTransmissionInfo(largest_acked); // Ensure the packet has a valid sent time. if (transmission_info.sent_time == QuicTime::Zero()) { - QUIC_BUG << "Acked packet has zero sent time, largest_acked:" - << largest_acked; + QUIC_BUG(quic_bug_10750_4) + << "Acked packet has zero sent time, largest_acked:" << largest_acked; return false; } if (transmission_info.state == NOT_CONTRIBUTING_RTT) { @@ -1401,7 +1421,7 @@ const QuicTime::Delta QuicSentPacketManager::GetProbeTimeoutDelay( QUICHE_DCHECK(pto_enabled_); if (rtt_stats_.smoothed_rtt().IsZero()) { // Respect kMinHandshakeTimeoutMs to avoid a potential amplification attack. - QUIC_BUG_IF(rtt_stats_.initial_rtt().IsZero()); + QUIC_BUG_IF(quic_bug_12552_6, rtt_stats_.initial_rtt().IsZero()); return std::max( pto_multiplier_without_rtt_samples_ * rtt_stats_.initial_rtt(), QuicTime::Delta::FromMilliseconds(kMinHandshakeTimeoutMs)) * @@ -1576,16 +1596,18 @@ AckResult QuicSentPacketManager::OnAckFrameEnd( unacked_packets_.GetMutableTransmissionInfo(acked_packet.packet_number); if (!QuicUtils::IsAckable(info->state)) { if (info->state == ACKED) { - QUIC_BUG << "Trying to ack an already acked packet: " - << acked_packet.packet_number - << ", last_ack_frame_: " << last_ack_frame_ - << ", least_unacked: " << unacked_packets_.GetLeastUnacked() - << ", packets_acked_: " << packets_acked_; + QUIC_BUG(quic_bug_10750_5) + << "Trying to ack an already acked packet: " + << acked_packet.packet_number + << ", last_ack_frame_: " << last_ack_frame_ + << ", least_unacked: " << unacked_packets_.GetLeastUnacked() + << ", packets_acked_: " << packets_acked_; } else { - QUIC_PEER_BUG << "Received " << ack_decrypted_level - << " ack for unackable packet: " - << acked_packet.packet_number << " with state: " - << QuicUtils::SentPacketStateToString(info->state); + QUIC_PEER_BUG(quic_peer_bug_10750_6) + << "Received " << ack_decrypted_level + << " ack for unackable packet: " << acked_packet.packet_number + << " with state: " + << QuicUtils::SentPacketStateToString(info->state); if (supports_multiple_packet_number_spaces()) { if (info->state == NEVER_SENT) { return UNSENT_PACKETS_ACKED; @@ -1789,7 +1811,7 @@ void QuicSentPacketManager::OnAckFrequencyFrameAcked( in_use_sent_ack_delays_.pop_front_n(stale_entry_count); } if (in_use_sent_ack_delays_.empty()) { - QUIC_BUG << "in_use_sent_ack_delays_ is empty."; + QUIC_BUG(quic_bug_10750_7) << "in_use_sent_ack_delays_ is empty."; return; } peer_max_ack_delay_ = std::max_element(in_use_sent_ack_delays_.cbegin(), 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 8cd0a57a366..7c7aa4d077f 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 @@ -8,6 +8,7 @@ #include <string> #include <utility> +#include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "quic/core/frames/quic_ack_frequency_frame.h" @@ -22,9 +23,9 @@ #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_map_util.h" -#include "quic/platform/api/quic_ptr_util.h" #include "quic/platform/api/quic_server_stats.h" #include "quic/platform/api/quic_stack_trace.h" +#include "common/platform/api/quiche_text_utils.h" using spdy::SpdyPriority; @@ -115,7 +116,7 @@ QuicSession::QuicSession( was_zero_rtt_rejected_(false), liveness_testing_in_progress_(false) { closed_streams_clean_up_alarm_ = - QuicWrapUnique<QuicAlarm>(connection_->alarm_factory()->CreateAlarm( + absl::WrapUnique<QuicAlarm>(connection_->alarm_factory()->CreateAlarm( new ClosedStreamsCleanUpDelegate(this))); if (perspective() == Perspective::IS_SERVER && connection_->version().handshake_protocol == PROTOCOL_TLS1_3) { @@ -141,6 +142,8 @@ void QuicSession::Initialize() { config_.SetMinAckDelayMs(kDefaultMinAckDelayTimeMs); } + connection_->CreateConnectionIdManager(); + // On the server side, version negotiation has been done by the dispatcher, // and the server session is created with the right version. if (perspective() == Perspective::IS_SERVER) { @@ -179,11 +182,13 @@ void QuicSession::PendingStreamOnStreamFrame(const QuicStreamFrame& frame) { if (!connection()->connected()) { return; } - if (ProcessPendingStream(pending)) { + 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); + stream->OnStreamCreatedFromPendingStream(); return; } if (pending->sequencer()->IsClosed()) { @@ -361,15 +366,18 @@ void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) { } void QuicSession::OnGoAway(const QuicGoAwayFrame& /*frame*/) { - QUIC_BUG_IF(version().UsesHttp3()) + QUIC_BUG_IF(quic_bug_12435_1, version().UsesHttp3()) << "gQUIC GOAWAY received on version " << version(); transport_goaway_received_ = true; } void QuicSession::OnMessageReceived(absl::string_view message) { - QUIC_DVLOG(1) << ENDPOINT << "Received message, length: " << message.length() - << ", " << message; + QUIC_DVLOG(1) << ENDPOINT << "Received message of length " + << message.length(); + QUIC_DVLOG(2) << ENDPOINT << "Contents of message of length " + << message.length() << ":" << std::endl + << quiche::QuicheTextUtils::HexDump(message); } void QuicSession::OnHandshakeDoneReceived() { @@ -417,7 +425,7 @@ void QuicSession::OnConnectionClosed(const QuicConnectionCloseFrame& frame, stream->OnConnectionClosed(frame.quic_error_code, source); auto it = stream_map_.find(id); if (it != stream_map_.end()) { - QUIC_BUG_IF(!it->second->IsZombie()) + QUIC_BUG_IF(quic_bug_12435_2, !it->second->IsZombie()) << ENDPOINT << "Non-zombie stream " << id << " failed to close under OnConnectionClosed"; } @@ -555,6 +563,18 @@ bool QuicSession::CheckStreamWriteBlocked(QuicStream* stream) const { } void QuicSession::OnCanWrite() { + if (connection_->donot_write_mid_packet_processing()) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_donot_write_mid_packet_processing, 1, 3); + if (connection_->framer().is_processing_packet()) { + // Do not write data in the middle of packet processing because rest + // frames in the packet may change the data to write. For example, lost + // data could be acknowledged. Also, connection is going to emit + // OnCanWrite signal post packet processing. + QUIC_BUG(session_write_mid_packet_processing) + << ENDPOINT << "Try to write mid packet processing."; + return; + } + } if (!RetransmitLostData()) { // Cannot finish retransmitting lost data, connection is write blocked. QUIC_DVLOG(1) << ENDPOINT @@ -608,11 +628,12 @@ void QuicSession::OnCanWrite() { if (!(write_blocked_streams_.HasWriteBlockedSpecialStream() || write_blocked_streams_.HasWriteBlockedDataStreams())) { // Writing one stream removed another!? Something's broken. - QUIC_BUG << "WriteBlockedStream is missing, num_writes: " << num_writes - << ", finished_writes: " << i - << ", connected: " << connection_->connected() - << ", connection level flow control blocked: " - << flow_controller_.IsBlocked(); + QUIC_BUG(quic_bug_10866_1) + << "WriteBlockedStream is missing, num_writes: " << num_writes + << ", finished_writes: " << i + << ", connected: " << connection_->connected() + << ", connection level flow control blocked: " + << flow_controller_.IsBlocked(); for (QuicStreamId id : last_writing_stream_ids) { QUIC_LOG(WARNING) << "last_writing_stream_id: " << id; } @@ -750,12 +771,12 @@ QuicConsumedData QuicSession::WritevData( if (was_zero_rtt_rejected_ && !OneRttKeysAvailable()) { QUICHE_DCHECK(version().UsesTls() && perspective() == Perspective::IS_CLIENT); - QUIC_BUG_IF(type == NOT_RETRANSMISSION) + QUIC_BUG_IF(quic_bug_12435_3, type == NOT_RETRANSMISSION) << ENDPOINT << "Try to send new data on stream " << id << "before 1-RTT keys are available while 0-RTT is rejected."; } else { - QUIC_BUG << ENDPOINT << "Try to send data of stream " << id - << " before encryption is established."; + QUIC_BUG(quic_bug_10866_2) << ENDPOINT << "Try to send data of stream " + << id << " before encryption is established."; } return QuicConsumedData(0, false); } @@ -798,7 +819,7 @@ size_t QuicSession::SendCryptoData(EncryptionLevel level, const std::string error_details = absl::StrCat( "Try to send crypto data with missing keys of encryption level: ", EncryptionLevelToString(level)); - QUIC_BUG << ENDPOINT << error_details; + QUIC_BUG(quic_bug_10866_3) << ENDPOINT << error_details; connection()->CloseConnection( QUIC_MISSING_WRITE_KEYS, error_details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); @@ -834,8 +855,10 @@ bool QuicSession::WriteControlFrame(const QuicFrame& frame, if (connection_->encrypted_control_frames()) { QUIC_RELOADABLE_FLAG_COUNT(quic_encrypted_control_frames); if (!IsEncryptionEstablished()) { - QUIC_BUG << ENDPOINT << "Tried to send control frame " << frame - << " before encryption is established."; + QUIC_BUG(quic_bug_10866_4) + << ENDPOINT << "Tried to send control frame " << frame + << " before encryption is established. Last decrypted level: " + << EncryptionLevelToString(connection_->last_decrypted_level()); return false; } } @@ -911,19 +934,13 @@ void QuicSession::SendGoAway(QuicErrorCode error_code, return; } transport_goaway_sent_ = true; - if (GetQuicReloadableFlag(quic_goaway_with_max_stream_id)) { - QUICHE_DCHECK_EQ(perspective(), Perspective::IS_SERVER); - QUIC_RELOADABLE_FLAG_COUNT_N(quic_goaway_with_max_stream_id, 2, 2); - control_frame_manager_.WriteOrBufferGoAway( - error_code, - QuicUtils::GetMaxClientInitiatedBidirectionalStreamId( - transport_version()), - reason); - } else { - control_frame_manager_.WriteOrBufferGoAway( - error_code, stream_id_manager_.largest_peer_created_stream_id(), - reason); - } + + QUICHE_DCHECK_EQ(perspective(), Perspective::IS_SERVER); + control_frame_manager_.WriteOrBufferGoAway( + error_code, + QuicUtils::GetMaxClientInitiatedBidirectionalStreamId( + transport_version()), + reason); } void QuicSession::SendBlocked(QuicStreamId id) { @@ -953,7 +970,8 @@ void QuicSession::OnStreamError(QuicErrorCode error_code, void QuicSession::SendMaxStreams(QuicStreamCount stream_count, bool unidirectional) { if (!is_configured_) { - QUIC_BUG << "Try to send max streams before config negotiated."; + QUIC_BUG(quic_bug_10866_5) + << "Try to send max streams before config negotiated."; return; } control_frame_manager_.WriteOrBufferMaxStreams(stream_count, unidirectional); @@ -969,7 +987,8 @@ void QuicSession::OnStreamClosed(QuicStreamId stream_id) { QUIC_DVLOG(1) << ENDPOINT << "Closing stream: " << stream_id; StreamMap::iterator it = stream_map_.find(stream_id); if (it == stream_map_.end()) { - QUIC_BUG << ENDPOINT << "Stream is already closed: " << stream_id; + QUIC_BUG(quic_bug_10866_6) + << ENDPOINT << "Stream is already closed: " << stream_id; return; } QuicStream* stream = it->second.get(); @@ -988,6 +1007,11 @@ void QuicSession::OnStreamClosed(QuicStreamId stream_id) { closed_streams_clean_up_alarm_->Set( connection_->clock()->ApproximateNow()); } + QUIC_BUG_IF( + 364846171_1, + connection_->packet_creator().HasPendingStreamFramesOfStream(stream_id)) + << "Stream " << stream_id + << " gets closed while there are pending frames."; } if (!stream->HasReceivedFinalOffset()) { @@ -1006,10 +1030,10 @@ void QuicSession::OnStreamClosed(QuicStreamId stream_id) { QUIC_DVLOG_IF(1, stream_was_draining) << ENDPOINT << "Stream " << stream_id << " was draining"; if (stream_was_draining) { - QUIC_BUG_IF(num_draining_streams_ == 0); + QUIC_BUG_IF(quic_bug_12435_4, num_draining_streams_ == 0); --num_draining_streams_; if (!IsIncomingStream(stream_id)) { - QUIC_BUG_IF(num_outgoing_draining_streams_ == 0); + QUIC_BUG_IF(quic_bug_12435_5, num_outgoing_draining_streams_ == 0); --num_outgoing_draining_streams_; } // Stream Id manager has been informed with draining streams. @@ -1098,10 +1122,9 @@ bool QuicSession::OneRttKeysAvailable() const { void QuicSession::OnConfigNegotiated() { // In versions with TLS, the configs will be set twice if 0-RTT is available. // In the second config setting, 1-RTT keys are guaranteed to be available. - if (GetQuicRestartFlag(quic_enable_zero_rtt_for_tls_v2) && - version().UsesTls() && is_configured_ && + if (version().UsesTls() && is_configured_ && connection_->encryption_level() != ENCRYPTION_FORWARD_SECURE) { - QUIC_BUG + QUIC_BUG(quic_bug_12435_6) << ENDPOINT << "1-RTT keys missing when config is negotiated for the second time."; connection_->CloseConnection( @@ -1297,9 +1320,12 @@ void QuicSession::OnConfigNegotiated() { // Ask flow controllers to try again since the config could have unblocked us. // Or if this session is configured on TLS enabled QUIC versions, // attempt to retransmit 0-RTT data if there's any. - if (connection_->version().AllowsLowFlowControlLimits() || - (GetQuicRestartFlag(quic_enable_zero_rtt_for_tls_v2) && + // TODO(fayang): consider removing this OnCanWrite call. + if ((!connection_->donot_write_mid_packet_processing() || + !connection_->framer().is_processing_packet()) && + (connection_->version().AllowsLowFlowControlLimits() || version().UsesTls())) { + QUIC_CODE_COUNT(quic_session_on_can_write_on_config_negotiated); OnCanWrite(); } } @@ -1581,9 +1607,10 @@ void QuicSession::OnNewEncryptionKeyAvailable( if (reset_encryption_level) { connection()->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); } - QUIC_BUG_IF(IsEncryptionEstablished() && - (connection()->encryption_level() == ENCRYPTION_INITIAL || - connection()->encryption_level() == ENCRYPTION_HANDSHAKE)) + QUIC_BUG_IF(quic_bug_12435_7, + IsEncryptionEstablished() && + (connection()->encryption_level() == ENCRYPTION_INITIAL || + connection()->encryption_level() == ENCRYPTION_HANDSHAKE)) << "Encryption is established, but the encryption level " << level << " does not support sending stream data"; } @@ -1602,28 +1629,35 @@ void QuicSession::SetDefaultEncryptionLevel(EncryptionLevel level) { // Retransmit old 0-RTT data (if any) with the new 0-RTT keys, since // they can't be decrypted by the server. connection_->MarkZeroRttPacketsForRetransmission(0); - // Given any streams blocked by encryption a chance to write. - OnCanWrite(); + if (!connection_->donot_write_mid_packet_processing() || + !connection_->framer().is_processing_packet()) { + // TODO(fayang): consider removing this OnCanWrite call. + // Given any streams blocked by encryption a chance to write. + QUIC_CODE_COUNT( + quic_session_on_can_write_set_default_encryption_level); + OnCanWrite(); + } } break; case ENCRYPTION_HANDSHAKE: break; case ENCRYPTION_FORWARD_SECURE: - QUIC_BUG_IF(!config_.negotiated()) + QUIC_BUG_IF(quic_bug_12435_8, !config_.negotiated()) << ENDPOINT << "Handshake confirmed without parameter negotiation."; connection()->mutable_stats().handshake_completion_time = connection()->clock()->ApproximateNow(); break; default: - QUIC_BUG << "Unknown encryption level: " << level; + QUIC_BUG(quic_bug_10866_7) << "Unknown encryption level: " << level; } } void QuicSession::OnTlsHandshakeComplete() { QUICHE_DCHECK_EQ(PROTOCOL_TLS1_3, connection_->version().handshake_protocol); - QUIC_BUG_IF(!GetCryptoStream()->crypto_negotiated_params().cipher_suite) + QUIC_BUG_IF(quic_bug_12435_9, + !GetCryptoStream()->crypto_negotiated_params().cipher_suite) << ENDPOINT << "Handshake completes without cipher suite negotiation."; - QUIC_BUG_IF(!config_.negotiated()) + QUIC_BUG_IF(quic_bug_12435_10, !config_.negotiated()) << ENDPOINT << "Handshake completes without parameter negotiation."; connection()->mutable_stats().handshake_completion_time = connection()->clock()->ApproximateNow(); @@ -1680,11 +1714,13 @@ void QuicSession::DiscardOldEncryptionKey(EncryptionLevel level) { case ENCRYPTION_ZERO_RTT: break; case ENCRYPTION_FORWARD_SECURE: - QUIC_BUG << ENDPOINT << "Discarding 1-RTT keys is not allowed"; + QUIC_BUG(quic_bug_10866_8) + << ENDPOINT << "Discarding 1-RTT keys is not allowed"; break; default: - QUIC_BUG << ENDPOINT - << "Cannot discard keys for unknown encryption level: " << level; + QUIC_BUG(quic_bug_10866_9) + << ENDPOINT + << "Cannot discard keys for unknown encryption level: " << level; } } @@ -1698,7 +1734,8 @@ void QuicSession::OnZeroRttRejected(int reason) { was_zero_rtt_rejected_ = true; connection_->MarkZeroRttPacketsForRetransmission(reason); if (connection_->encryption_level() == ENCRYPTION_FORWARD_SECURE) { - QUIC_BUG << "1-RTT keys already available when 0-RTT is rejected."; + QUIC_BUG(quic_bug_10866_10) + << "1-RTT keys already available when 0-RTT is rejected."; connection_->CloseConnection( QUIC_INTERNAL_ERROR, "1-RTT keys already available when 0-RTT is rejected.", @@ -2031,7 +2068,8 @@ size_t QuicSession::GetNumActiveStreams() const { void QuicSession::MarkConnectionLevelWriteBlocked(QuicStreamId id) { if (GetOrCreateStream(id) == nullptr) { - QUIC_BUG << "Marking unknown stream " << id << " blocked."; + QUIC_BUG(quic_bug_10866_11) + << "Marking unknown stream " << id << " blocked."; QUIC_LOG_FIRST_N(ERROR, 2) << QuicStackTrace(); } @@ -2057,6 +2095,27 @@ void QuicSession::SendAckFrequency(const QuicAckFrequencyFrame& frame) { control_frame_manager_.WriteOrBufferAckFrequency(frame); } +void QuicSession::SendNewConnectionId(const QuicNewConnectionIdFrame& frame) { + control_frame_manager_.WriteOrBufferNewConnectionId( + frame.connection_id, frame.sequence_number, frame.retire_prior_to, + frame.stateless_reset_token); +} + +void QuicSession::SendRetireConnectionId(uint64_t sequence_number) { + control_frame_manager_.WriteOrBufferRetireConnectionId(sequence_number); +} + +void QuicSession::OnServerConnectionIdIssued( + const QuicConnectionId& server_connection_id) { + visitor_->OnNewConnectionIdSent(connection_->connection_id(), + server_connection_id); +} + +void QuicSession::OnServerConnectionIdRetired( + const QuicConnectionId& server_connection_id) { + visitor_->OnConnectionIdRetired(server_connection_id); +} + bool QuicSession::IsConnectionFlowControlBlocked() const { return flow_controller_.IsBlocked(); } @@ -2109,6 +2168,9 @@ void QuicSession::MaybeCloseZombieStream(QuicStreamId id) { } // Do not retransmit data of a closed stream. streams_with_pending_retransmission_.erase(id); + QUIC_BUG_IF(364846171_2, + connection_->packet_creator().HasPendingStreamFramesOfStream(id)) + << "Stream " << id << " gets closed while there are pending frames."; } QuicStream* QuicSession::GetStream(QuicStreamId id) const { @@ -2165,8 +2227,9 @@ bool QuicSession::OnFrameAcked(const QuicFrame& frame, void QuicSession::OnStreamFrameRetransmitted(const QuicStreamFrame& frame) { QuicStream* stream = GetStream(frame.stream_id); if (stream == nullptr) { - QUIC_BUG << "Stream: " << frame.stream_id << " is closed when " << frame - << " is retransmitted."; + QUIC_BUG(quic_bug_10866_12) + << "Stream: " << frame.stream_id << " is closed when " << frame + << " is retransmitted."; connection()->CloseConnection( QUIC_INTERNAL_ERROR, "Attempt to retransmit frame of a closed stream", ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); @@ -2277,8 +2340,9 @@ WriteStreamDataResult QuicSession::WriteStreamData(QuicStreamId id, if (stream == nullptr) { // This causes the connection to be closed because of failed to serialize // packet. - QUIC_BUG << "Stream " << id << " does not exist when trying to write data." - << " version:" << transport_version(); + QUIC_BUG(quic_bug_10866_13) + << "Stream " << id << " does not exist when trying to write data." + << " version:" << transport_version(); return STREAM_MISSING; } if (stream->WriteStreamData(offset, data_length, writer)) { @@ -2295,7 +2359,7 @@ bool QuicSession::WriteCryptoData(EncryptionLevel level, writer); } -QuicUint128 QuicSession::GetStatelessResetToken() const { +StatelessResetToken QuicSession::GetStatelessResetToken() const { return QuicUtils::GenerateStatelessResetToken(connection_->connection_id()); } @@ -2363,7 +2427,8 @@ bool QuicSession::RetransmitLostData() { streams_with_pending_retransmission_.pop_front(); } } else { - QUIC_BUG << "Try to retransmit data of a closed stream"; + QUIC_BUG(quic_bug_10866_14) + << "Try to retransmit data of a closed stream"; streams_with_pending_retransmission_.pop_front(); } } 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 cf7f9f725b4..cf366cf5654 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 @@ -145,6 +145,12 @@ class QUIC_EXPORT_PRIVATE QuicSession // Adds a connection level WINDOW_UPDATE frame. void OnAckNeedsRetransmittableFrame() override; void SendAckFrequency(const QuicAckFrequencyFrame& frame) override; + void SendNewConnectionId(const QuicNewConnectionIdFrame& frame) override; + void SendRetireConnectionId(uint64_t sequence_number) override; + void OnServerConnectionIdIssued( + const QuicConnectionId& server_connection_id) override; + void OnServerConnectionIdRetired( + const QuicConnectionId& server_connection_id) override; bool WillingAndAbleToWrite() const override; std::string GetStreamsInfoForLogging() const override; void OnPathDegrading() override; @@ -164,6 +170,10 @@ class QUIC_EXPORT_PRIVATE QuicSession void BeforeConnectionCloseSent() override {} bool ValidateToken(absl::string_view token) const override; void MaybeSendAddressToken() override; + bool IsKnownServerAddress( + const QuicSocketAddress& /*address*/) const override { + return false; + } // QuicStreamFrameDataProducer WriteStreamDataResult WriteStreamData(QuicStreamId id, @@ -710,7 +720,7 @@ class QUIC_EXPORT_PRIVATE QuicSession // Returns a stateless reset token which will be included in the public reset // packet. - virtual QuicUint128 GetStatelessResetToken() const; + virtual StatelessResetToken GetStatelessResetToken() const; QuicControlFrameManager& control_frame_manager() { return control_frame_manager_; @@ -735,10 +745,11 @@ class QUIC_EXPORT_PRIVATE QuicSession size_t num_draining_streams() const { return num_draining_streams_; } // Processes the stream type information of |pending| depending on - // different kinds of sessions' own rules. Returns true if the pending stream - // is converted into a normal stream. - virtual bool ProcessPendingStream(PendingStream* /*pending*/) { - return false; + // different kinds of sessions' own rules. If the pending stream has been + // converted to a normal stream, returns a pointer to the new stream; + // otherwise, returns nullptr. + virtual QuicStream* ProcessPendingStream(PendingStream* /*pending*/) { + return nullptr; } // Called by applications to perform |action| on active streams. 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 7daec2bbe00..81282fe9eeb 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc @@ -10,6 +10,7 @@ #include <utility> #include "absl/base/macros.h" +#include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" @@ -28,7 +29,6 @@ #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_map_util.h" #include "quic/platform/api/quic_mem_slice_storage.h" -#include "quic/platform/api/quic_ptr_util.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" @@ -246,14 +246,14 @@ class TestSession : public QuicSession { return nullptr; } TestStream* stream = new TestStream(id, this, BIDIRECTIONAL); - ActivateStream(QuicWrapUnique(stream)); + ActivateStream(absl::WrapUnique(stream)); return stream; } TestStream* CreateOutgoingUnidirectionalStream() { TestStream* stream = new TestStream(GetNextOutgoingUnidirectionalStreamId(), this, WRITE_UNIDIRECTIONAL); - ActivateStream(QuicWrapUnique(stream)); + ActivateStream(absl::WrapUnique(stream)); return stream; } @@ -274,7 +274,7 @@ class TestSession : public QuicSession { id, this, DetermineStreamType(id, connection()->version(), perspective(), /*is_incoming=*/true, BIDIRECTIONAL)); - ActivateStream(QuicWrapUnique(stream)); + ActivateStream(absl::WrapUnique(stream)); ++num_incoming_streams_created_; return stream; } @@ -285,7 +285,7 @@ class TestSession : public QuicSession { pending, this, DetermineStreamType(id, connection()->version(), perspective(), /*is_incoming=*/true, BIDIRECTIONAL)); - ActivateStream(QuicWrapUnique(stream)); + ActivateStream(absl::WrapUnique(stream)); ++num_incoming_streams_created_; return stream; } @@ -293,14 +293,13 @@ class TestSession : public QuicSession { // QuicSession doesn't do anything in this method. So it's overridden here to // test that the session handles pending streams correctly in terms of // receiving stream frames. - bool ProcessPendingStream(PendingStream* pending) override { + QuicStream* ProcessPendingStream(PendingStream* pending) override { struct iovec iov; if (pending->sequencer()->GetReadableRegion(&iov)) { // Create TestStream once the first byte is received. - CreateIncomingStream(pending); - return true; + return CreateIncomingStream(pending); } - return false; + return nullptr; } bool IsClosedStream(QuicStreamId id) { @@ -2552,7 +2551,7 @@ TEST_P(QuicSessionTestServer, WriteUnidirectionalStream) { session_.set_writev_consumes_all_data(true); TestStream* stream4 = new TestStream(GetNthServerInitiatedUnidirectionalId(1), &session_, WRITE_UNIDIRECTIONAL); - session_.ActivateStream(QuicWrapUnique(stream4)); + session_.ActivateStream(absl::WrapUnique(stream4)); std::string body(100, '.'); stream4->WriteOrBufferData(body, false, nullptr); stream4->WriteOrBufferData(body, true, nullptr); @@ -2565,7 +2564,7 @@ TEST_P(QuicSessionTestServer, WriteUnidirectionalStream) { TEST_P(QuicSessionTestServer, ReceivedDataOnWriteUnidirectionalStream) { TestStream* stream4 = new TestStream(GetNthServerInitiatedUnidirectionalId(1), &session_, WRITE_UNIDIRECTIONAL); - session_.ActivateStream(QuicWrapUnique(stream4)); + session_.ActivateStream(absl::WrapUnique(stream4)); EXPECT_CALL( *connection_, @@ -2579,7 +2578,7 @@ TEST_P(QuicSessionTestServer, ReceivedDataOnWriteUnidirectionalStream) { TEST_P(QuicSessionTestServer, ReadUnidirectionalStream) { TestStream* stream4 = new TestStream(GetNthClientInitiatedUnidirectionalId(1), &session_, READ_UNIDIRECTIONAL); - session_.ActivateStream(QuicWrapUnique(stream4)); + session_.ActivateStream(absl::WrapUnique(stream4)); EXPECT_FALSE(stream4->IsWaitingForAcks()); // Discard all incoming data. stream4->StopReading(); @@ -2599,7 +2598,7 @@ TEST_P(QuicSessionTestServer, ReadUnidirectionalStream) { TEST_P(QuicSessionTestServer, WriteOrBufferDataOnReadUnidirectionalStream) { TestStream* stream4 = new TestStream(GetNthClientInitiatedUnidirectionalId(1), &session_, READ_UNIDIRECTIONAL); - session_.ActivateStream(QuicWrapUnique(stream4)); + session_.ActivateStream(absl::WrapUnique(stream4)); EXPECT_CALL(*connection_, CloseConnection( @@ -2612,7 +2611,7 @@ TEST_P(QuicSessionTestServer, WriteOrBufferDataOnReadUnidirectionalStream) { TEST_P(QuicSessionTestServer, WritevDataOnReadUnidirectionalStream) { TestStream* stream4 = new TestStream(GetNthClientInitiatedUnidirectionalId(1), &session_, READ_UNIDIRECTIONAL); - session_.ActivateStream(QuicWrapUnique(stream4)); + session_.ActivateStream(absl::WrapUnique(stream4)); EXPECT_CALL(*connection_, CloseConnection( @@ -2629,7 +2628,7 @@ TEST_P(QuicSessionTestServer, WritevDataOnReadUnidirectionalStream) { TEST_P(QuicSessionTestServer, WriteMemSlicesOnReadUnidirectionalStream) { TestStream* stream4 = new TestStream(GetNthClientInitiatedUnidirectionalId(1), &session_, READ_UNIDIRECTIONAL); - session_.ActivateStream(QuicWrapUnique(stream4)); + session_.ActivateStream(absl::WrapUnique(stream4)); EXPECT_CALL(*connection_, CloseConnection( 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 b8ac8ccccae..3f1b7d94e18 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 @@ -176,7 +176,7 @@ void PendingStream::OnStreamFrame(const QuicStreamFrame& frame) { (kMaxStreamLength - frame.offset < frame.data_length); if (is_stream_too_long) { // Close connection if stream becomes too long. - QUIC_PEER_BUG + QUIC_PEER_BUG(quic_peer_bug_12570_1) << "Receive stream frame reaches max stream length. frame offset " << frame.offset << " length " << frame.data_length; OnUnrecoverableError(QUIC_STREAM_LENGTH_OVERFLOW, @@ -424,10 +424,10 @@ void QuicStream::OnStreamFrame(const QuicStreamFrame& frame) { (kMaxStreamLength - frame.offset < frame.data_length); if (is_stream_too_long) { // Close connection if stream becomes too long. - QUIC_PEER_BUG << "Receive stream frame on stream " << id_ - << " reaches max stream length. frame offset " << frame.offset - << " length " << frame.data_length << ". " - << sequencer_.DebugString(); + QUIC_PEER_BUG(quic_peer_bug_10586_1) + << "Receive stream frame on stream " << id_ + << " reaches max stream length. frame offset " << frame.offset + << " length " << frame.data_length << ". " << sequencer_.DebugString(); OnUnrecoverableError( QUIC_STREAM_LENGTH_OVERFLOW, absl::StrCat("Peer sends more data than allowed on stream ", id_, @@ -474,7 +474,7 @@ void QuicStream::OnStreamFrame(const QuicStreamFrame& frame) { MaybeIncreaseHighestReceivedOffset(frame.offset + frame_payload_size)) { // As the highest received offset has changed, check to see if this is a // violation of flow control. - QUIC_BUG_IF(!flow_controller_.has_value()) + QUIC_BUG_IF(quic_bug_12570_2, !flow_controller_.has_value()) << ENDPOINT << "OnStreamFrame called on stream without flow control"; if ((flow_controller_.has_value() && flow_controller_->FlowControlViolation()) || @@ -542,7 +542,7 @@ void QuicStream::OnStreamReset(const QuicRstStreamFrame& frame) { } MaybeIncreaseHighestReceivedOffset(frame.byte_offset); - QUIC_BUG_IF(!flow_controller_.has_value()) + QUIC_BUG_IF(quic_bug_12570_3, !flow_controller_.has_value()) << ENDPOINT << "OnStreamReset called on stream without flow control"; if ((flow_controller_.has_value() && flow_controller_->FlowControlViolation()) || @@ -631,7 +631,8 @@ void QuicStream::WriteOrBufferData( bool fin, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { if (session()->use_write_or_buffer_data_at_level()) { - QUIC_BUG_IF(QuicUtils::IsCryptoStreamId(transport_version(), id_)) + QUIC_BUG_IF(quic_bug_12570_4, + QuicUtils::IsCryptoStreamId(transport_version(), id_)) << ENDPOINT << "WriteOrBufferData is used to send application data, use " "WriteOrBufferDataAtLevel to send crypto data."; @@ -648,12 +649,12 @@ void QuicStream::WriteOrBufferDataInner( absl::optional<EncryptionLevel> level, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { if (data.empty() && !fin) { - QUIC_BUG << "data.empty() && !fin"; + QUIC_BUG(quic_bug_10586_2) << "data.empty() && !fin"; return; } if (fin_buffered_) { - QUIC_BUG << "Fin already buffered"; + QUIC_BUG(quic_bug_10586_3) << "Fin already buffered"; return; } if (write_side_closed_) { @@ -675,7 +676,7 @@ void QuicStream::WriteOrBufferDataInner( struct iovec iov(QuicUtils::MakeIovec(data)); QuicStreamOffset offset = send_buffer_.stream_offset(); if (kMaxStreamLength - offset < data.length()) { - QUIC_BUG << "Write too many data via stream " << id_; + QUIC_BUG(quic_bug_10586_4) << "Write too many data via stream " << id_; OnUnrecoverableError( QUIC_STREAM_LENGTH_OVERFLOW, absl::StrCat("Write too many data via stream ", id_)); @@ -734,8 +735,8 @@ void QuicStream::OnCanWrite() { void QuicStream::MaybeSendBlocked() { if (!flow_controller_.has_value()) { - QUIC_BUG << ENDPOINT - << "MaybeSendBlocked called on stream without flow control"; + QUIC_BUG(quic_bug_10586_5) + << ENDPOINT << "MaybeSendBlocked called on stream without flow control"; return; } if (flow_controller_->ShouldSendBlocked()) { @@ -760,12 +761,12 @@ void QuicStream::MaybeSendBlocked() { QuicConsumedData QuicStream::WriteMemSlices(QuicMemSliceSpan span, bool fin) { QuicConsumedData consumed_data(0, false); if (span.empty() && !fin) { - QUIC_BUG << "span.empty() && !fin"; + QUIC_BUG(quic_bug_10586_6) << "span.empty() && !fin"; return consumed_data; } if (fin_buffered_) { - QUIC_BUG << "Fin already buffered"; + QUIC_BUG(quic_bug_10586_7) << "Fin already buffered"; return consumed_data; } @@ -788,7 +789,7 @@ QuicConsumedData QuicStream::WriteMemSlices(QuicMemSliceSpan span, bool fin) { consumed_data.bytes_consumed = send_buffer_.SaveMemSliceSpan(span); if (offset > send_buffer_.stream_offset() || kMaxStreamLength < send_buffer_.stream_offset()) { - QUIC_BUG << "Write too many data via stream " << id_; + QUIC_BUG(quic_bug_10586_8) << "Write too many data via stream " << id_; OnUnrecoverableError( QUIC_STREAM_LENGTH_OVERFLOW, absl::StrCat("Write too many data via stream ", id_)); @@ -879,7 +880,7 @@ void QuicStream::MaybeSendRstStream(QuicRstStreamErrorCode error) { } if (!session()->version().UsesHttp3()) { - QUIC_BUG_IF(error == QUIC_STREAM_NO_ERROR); + QUIC_BUG_IF(quic_bug_12570_5, error == QUIC_STREAM_NO_ERROR); stop_sending_sent_ = true; CloseReadSide(); } @@ -914,8 +915,8 @@ void QuicStream::OnClose() { QUICHE_DCHECK(read_side_closed_ && write_side_closed_); if (!fin_sent_ && !rst_sent_) { - QUIC_BUG_IF(session()->connection()->connected() && - session()->version().UsesHttp3()) + QUIC_BUG_IF(quic_bug_12570_6, session()->connection()->connected() && + session()->version().UsesHttp3()) << "The stream should've already sent RST in response to " "STOP_SENDING"; // For flow control accounting, tell the peer how many bytes have been @@ -949,8 +950,9 @@ void QuicStream::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { } if (!flow_controller_.has_value()) { - QUIC_BUG << ENDPOINT - << "OnWindowUpdateFrame called on stream without flow control"; + QUIC_BUG(quic_bug_10586_9) + << ENDPOINT + << "OnWindowUpdateFrame called on stream without flow control"; return; } @@ -963,9 +965,10 @@ void QuicStream::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { bool QuicStream::MaybeIncreaseHighestReceivedOffset( QuicStreamOffset new_offset) { if (!flow_controller_.has_value()) { - QUIC_BUG << ENDPOINT - << "MaybeIncreaseHighestReceivedOffset called on stream without " - "flow control"; + QUIC_BUG(quic_bug_10586_10) + << ENDPOINT + << "MaybeIncreaseHighestReceivedOffset called on stream without " + "flow control"; return false; } uint64_t increment = @@ -987,8 +990,8 @@ bool QuicStream::MaybeIncreaseHighestReceivedOffset( void QuicStream::AddBytesSent(QuicByteCount bytes) { if (!flow_controller_.has_value()) { - QUIC_BUG << ENDPOINT - << "AddBytesSent called on stream without flow control"; + QUIC_BUG(quic_bug_10586_11) + << ENDPOINT << "AddBytesSent called on stream without flow control"; return; } flow_controller_->AddBytesSent(bytes); @@ -1005,7 +1008,7 @@ void QuicStream::AddBytesConsumed(QuicByteCount bytes) { return; } if (!flow_controller_.has_value()) { - QUIC_BUG + QUIC_BUG(quic_bug_12570_7) << ENDPOINT << "AddBytesConsumed called on non-crypto stream without flow control"; return; @@ -1023,8 +1026,9 @@ void QuicStream::AddBytesConsumed(QuicByteCount bytes) { bool QuicStream::MaybeConfigSendWindowOffset(QuicStreamOffset new_offset, bool was_zero_rtt_rejected) { if (!flow_controller_.has_value()) { - QUIC_BUG << ENDPOINT - << "ConfigSendWindowOffset called on stream without flow control"; + QUIC_BUG(quic_bug_10586_12) + << ENDPOINT + << "ConfigSendWindowOffset called on stream without flow control"; return false; } @@ -1034,7 +1038,7 @@ bool QuicStream::MaybeConfigSendWindowOffset(QuicStreamOffset new_offset, if (was_zero_rtt_rejected && new_offset < flow_controller_->bytes_sent()) { // The client is given flow control window lower than what's written in // 0-RTT. This QUIC implementation is unable to retransmit them. - QUIC_BUG_IF(perspective_ == Perspective::IS_SERVER) + QUIC_BUG_IF(quic_bug_12570_8, perspective_ == Perspective::IS_SERVER) << "Server streams' flow control should never be configured twice."; OnUnrecoverableError( QUIC_ZERO_RTT_UNRETRANSMITTABLE, @@ -1047,7 +1051,7 @@ bool QuicStream::MaybeConfigSendWindowOffset(QuicStreamOffset new_offset, // In IETF QUIC, if the client receives flow control limit lower than what // was resumed from 0-RTT, depending on 0-RTT status, it's either the // peer's fault or our implementation's fault. - QUIC_BUG_IF(perspective_ == Perspective::IS_SERVER) + QUIC_BUG_IF(quic_bug_12570_9, perspective_ == Perspective::IS_SERVER) << "Server streams' flow control should never be configured twice."; OnUnrecoverableError( was_zero_rtt_rejected ? QUIC_ZERO_RTT_REJECTION_LIMIT_REDUCED @@ -1227,8 +1231,9 @@ void QuicStream::WriteBufferedData(absl::optional<EncryptionLevel> level) { send_window = flow_controller_->SendWindowSize(); } else { send_window = std::numeric_limits<QuicByteCount>::max(); - QUIC_BUG << ENDPOINT - << "WriteBufferedData called on stream without flow control"; + QUIC_BUG(quic_bug_10586_13) + << ENDPOINT + << "WriteBufferedData called on stream without flow control"; } if (stream_contributes_to_connection_flow_control_) { send_window = @@ -1370,7 +1375,7 @@ void QuicStream::WritePendingRetransmission() { bool QuicStream::MaybeSetTtl(QuicTime::Delta ttl) { if (is_static_) { - QUIC_BUG << "Cannot set TTL of a static stream."; + QUIC_BUG(quic_bug_10586_14) << "Cannot set TTL of a static stream."; return false; } if (deadline_.IsInitialized()) { @@ -1402,7 +1407,8 @@ void QuicStream::OnDeadlinePassed() { bool QuicStream::IsFlowControlBlocked() const { if (!flow_controller_.has_value()) { - QUIC_BUG << "Trying to access non-existent flow controller."; + QUIC_BUG(quic_bug_10586_15) + << "Trying to access non-existent flow controller."; return false; } return flow_controller_->IsBlocked(); @@ -1410,7 +1416,8 @@ bool QuicStream::IsFlowControlBlocked() const { QuicStreamOffset QuicStream::highest_received_byte_offset() const { if (!flow_controller_.has_value()) { - QUIC_BUG << "Trying to access non-existent flow controller."; + QUIC_BUG(quic_bug_10586_16) + << "Trying to access non-existent flow controller."; return 0; } return flow_controller_->highest_received_byte_offset(); @@ -1418,7 +1425,8 @@ QuicStreamOffset QuicStream::highest_received_byte_offset() const { void QuicStream::UpdateReceiveWindowSize(QuicStreamOffset size) { if (!flow_controller_.has_value()) { - QUIC_BUG << "Trying to access non-existent flow controller."; + QUIC_BUG(quic_bug_10586_17) + << "Trying to access non-existent flow controller."; return; } flow_controller_->UpdateReceiveWindowSize(size); @@ -1446,4 +1454,8 @@ absl::optional<QuicByteCount> QuicStream::GetReceiveWindow() const { : absl::nullopt; } +void QuicStream::OnStreamCreatedFromPendingStream() { + sequencer()->SetUnblocked(); +} + } // namespace quic 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 d89e4602909..6e6e623b9f2 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 @@ -376,6 +376,13 @@ class QUIC_EXPORT_PRIVATE QuicStream bool fin_buffered() const { return fin_buffered_; } + // True if buffered data in send buffer is below buffered_data_threshold_. + bool CanWriteNewData() const; + + // Called immediately after the stream is created from a pending stream, + // indicating it can start processing data. + void OnStreamCreatedFromPendingStream(); + protected: // Called when data of [offset, offset + data_length] is buffered in send // buffer. @@ -391,9 +398,6 @@ class QUIC_EXPORT_PRIVATE QuicStream // a RST_STREAM has been sent. virtual void OnClose(); - // True if buffered data in send buffer is below buffered_data_threshold_. - bool CanWriteNewData() const; - // True if buffered data in send buffer is still below // buffered_data_threshold_ even after writing |length| bytes. bool CanWriteNewDataAfterData(QuicByteCount length) const; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc index 397eb71269b..05562125557 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc @@ -86,7 +86,7 @@ bool QuicStreamIdManager::MaybeAllowNewOutgoingStreams( void QuicStreamIdManager::SetMaxOpenIncomingStreams( QuicStreamCount max_open_streams) { - QUIC_BUG_IF(incoming_stream_count_ > 0) + QUIC_BUG_IF(quic_bug_12413_1, incoming_stream_count_ > 0) << "non-zero incoming stream count " << incoming_stream_count_ << " when setting max incoming stream to " << max_open_streams; QUIC_DLOG_IF(WARNING, incoming_initial_max_open_streams_ != max_open_streams) @@ -113,7 +113,8 @@ void QuicStreamIdManager::MaybeSendMaxStreamsFrame() { } void QuicStreamIdManager::SendMaxStreamsFrame() { - QUIC_BUG_IF(incoming_advertised_max_streams_ >= incoming_actual_max_streams_); + QUIC_BUG_IF(quic_bug_12413_2, + incoming_advertised_max_streams_ >= incoming_actual_max_streams_); incoming_advertised_max_streams_ = incoming_actual_max_streams_; delegate_->SendMaxStreams(incoming_advertised_max_streams_, unidirectional_); } @@ -138,7 +139,7 @@ void QuicStreamIdManager::OnStreamClosed(QuicStreamId stream_id) { } QuicStreamId QuicStreamIdManager::GetNextOutgoingStreamId() { - QUIC_BUG_IF(outgoing_stream_count_ >= outgoing_max_streams_) + QUIC_BUG_IF(quic_bug_12413_3, outgoing_stream_count_ >= outgoing_max_streams_) << "Attempt to allocate a new outgoing stream that would exceed the " "limit (" << outgoing_max_streams_ << ")"; 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 219eacdbd60..3b1b37bd1f3 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 @@ -77,7 +77,7 @@ void QuicStreamSendBuffer::SaveMemSlice(QuicMemSlice slice) { QUIC_DVLOG(2) << "Save slice offset " << stream_offset_ << " length " << slice.length(); if (slice.empty()) { - QUIC_BUG << "Try to save empty MemSlice to send buffer."; + QUIC_BUG(quic_bug_10853_1) << "Try to save empty MemSlice to send buffer."; return; } size_t length = slice.length(); @@ -104,7 +104,7 @@ void QuicStreamSendBuffer::OnStreamDataConsumed(size_t bytes_consumed) { bool QuicStreamSendBuffer::WriteStreamData(QuicStreamOffset offset, QuicByteCount data_length, QuicDataWriter* writer) { - QUIC_BUG_IF(current_end_offset_ < offset) + QUIC_BUG_IF(quic_bug_12823_1, current_end_offset_ < offset) << "Tried to write data out of sequence. last_offset_end:" << current_end_offset_ << ", offset:" << offset; // The iterator returned from |interval_deque_| will automatically advance @@ -122,7 +122,7 @@ bool QuicStreamSendBuffer::WriteStreamData(QuicStreamOffset offset, QuicByteCount copy_length = std::min(data_length, available_bytes_in_slice); if (!writer->WriteBytes(slice_it->slice.data() + slice_offset, copy_length)) { - QUIC_BUG << "Writer fails to write."; + QUIC_BUG(quic_bug_10853_2) << "Writer fails to write."; return false; } offset += copy_length; @@ -219,8 +219,9 @@ StreamPendingRetransmission QuicStreamSendBuffer::NextPendingRetransmission() const auto pending = pending_retransmissions_.begin(); return {pending->min(), pending->max() - pending->min()}; } - QUIC_BUG << "NextPendingRetransmission is called unexpected with no " - "pending retransmissions."; + QUIC_BUG(quic_bug_10853_3) + << "NextPendingRetransmission is called unexpected with no " + "pending retransmissions."; return {0, 0}; } @@ -228,10 +229,11 @@ bool QuicStreamSendBuffer::FreeMemSlices(QuicStreamOffset start, QuicStreamOffset end) { auto it = interval_deque_.DataBegin(); if (it == interval_deque_.DataEnd() || it->slice.empty()) { - QUIC_BUG << "Trying to ack stream data [" << start << ", " << end << "), " - << (it == interval_deque_.DataEnd() - ? "and there is no outstanding data." - : "and the first slice is empty."); + QUIC_BUG(quic_bug_10853_4) + << "Trying to ack stream data [" << start << ", " << end << "), " + << (it == interval_deque_.DataEnd() + ? "and there is no outstanding data." + : "and the first slice is empty."); return false; } if (!it->interval().Contains(start)) { @@ -240,9 +242,10 @@ bool QuicStreamSendBuffer::FreeMemSlices(QuicStreamOffset start, interval_deque_.DataEnd(), start, CompareOffset()); } if (it == interval_deque_.DataEnd() || it->slice.empty()) { - QUIC_BUG << "Offset " << start << " with iterator offset: " << it->offset - << (it == interval_deque_.DataEnd() ? " does not exist." - : " has already been acked."); + QUIC_BUG(quic_bug_10853_5) + << "Offset " << start << " with iterator offset: " << it->offset + << (it == interval_deque_.DataEnd() ? " does not exist." + : " has already been acked."); return false; } for (; it != interval_deque_.DataEnd(); ++it) { @@ -260,7 +263,8 @@ bool QuicStreamSendBuffer::FreeMemSlices(QuicStreamOffset start, void QuicStreamSendBuffer::CleanUpBufferedSlices() { while (!interval_deque_.Empty() && interval_deque_.DataBegin()->slice.empty()) { - QUIC_BUG_IF(interval_deque_.DataBegin()->offset > current_end_offset_) + QUIC_BUG_IF(quic_bug_12823_2, + interval_deque_.DataBegin()->offset > current_end_offset_) << "Fail to pop front from interval_deque_. Front element contained " "a slice whose data has not all be written. Front offset " << interval_deque_.DataBegin()->offset << " length " 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 e702f714747..cf7340bdb65 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 @@ -40,8 +40,8 @@ QuicStreamSequencer::QuicStreamSequencer(StreamInterface* quic_stream) QuicStreamSequencer::~QuicStreamSequencer() { if (stream_ == nullptr) { - QUIC_BUG << "Double free'ing QuicStreamSequencer at " << this << ". " - << QuicStackTrace(); + QUIC_BUG(quic_bug_10858_1) << "Double free'ing QuicStreamSequencer at " + << this << ". " << QuicStackTrace(); } stream_ = nullptr; } @@ -235,9 +235,10 @@ void QuicStreamSequencer::MarkConsumed(size_t num_bytes_consumed) { QUICHE_DCHECK(!blocked_); bool result = buffered_frames_.MarkConsumed(num_bytes_consumed); if (!result) { - QUIC_BUG << "Invalid argument to MarkConsumed." - << " expect to consume: " << num_bytes_consumed - << ", but not enough bytes available. " << DebugString(); + QUIC_BUG(quic_bug_10858_2) + << "Invalid argument to MarkConsumed." + << " expect to consume: " << num_bytes_consumed + << ", but not enough bytes available. " << DebugString(); stream_->Reset(QUIC_ERROR_PROCESSING_STREAM); return; } 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 5c85a6e7baa..871b832b168 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 @@ -73,7 +73,7 @@ void QuicStreamSequencerBuffer::Clear() { bool QuicStreamSequencerBuffer::RetireBlock(size_t index) { if (blocks_[index] == nullptr) { - QUIC_BUG << "Try to retire block twice"; + QUIC_BUG(quic_bug_10610_1) << "Try to retire block twice"; return false; } delete blocks_[index]; @@ -516,7 +516,7 @@ bool QuicStreamSequencerBuffer::RetireBlockIfEmpty(size_t block_index) { return true; } } else { - QUIC_BUG << "Read stopped at where it shouldn't."; + QUIC_BUG(quic_bug_10610_2) << "Read stopped at where it shouldn't."; return false; } } 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 46c4b4af3f6..739113a9147 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 @@ -9,6 +9,7 @@ #include <utility> #include "absl/base/macros.h" +#include "absl/memory/memory.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "quic/core/crypto/null_encrypter.h" @@ -24,7 +25,6 @@ #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_mem_slice_storage.h" -#include "quic/platform/api/quic_ptr_util.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" @@ -114,7 +114,7 @@ class QuicStreamTest : public QuicTestWithParam<ParsedQuicVersion> { BIDIRECTIONAL); EXPECT_NE(nullptr, stream_); // session_ now owns stream_. - session_->ActivateStream(QuicWrapUnique(stream_)); + session_->ActivateStream(absl::WrapUnique(stream_)); // Ignore resetting when session_ is terminated. EXPECT_CALL(*session_, MaybeSendStopSendingFrame(kTestStreamId, _)) .Times(AnyNumber()); @@ -264,7 +264,7 @@ TEST_P(QuicStreamTest, FromPendingStreamThenData) { auto stream = new TestStream(&pending, session_.get(), StreamType::READ_UNIDIRECTIONAL, false); - session_->ActivateStream(QuicWrapUnique(stream)); + session_->ActivateStream(absl::WrapUnique(stream)); QuicStreamFrame frame2(kTestStreamId + 2, true, 3, "."); stream->OnStreamFrame(frame2); @@ -1437,7 +1437,7 @@ TEST_P(QuicStreamTest, MarkConnectionLevelWriteBlockedOnWindowUpdateFrame) { auto stream = new TestStream(GetNthClientInitiatedBidirectionalStreamId( GetParam().transport_version, 2), session_.get(), BIDIRECTIONAL); - session_->ActivateStream(QuicWrapUnique(stream)); + session_->ActivateStream(absl::WrapUnique(stream)); EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) .WillRepeatedly(Invoke(session_.get(), &MockQuicSession::ConsumeData)); @@ -1470,7 +1470,7 @@ TEST_P(QuicStreamTest, auto stream = new TestStream(GetNthClientInitiatedBidirectionalStreamId( GetParam().transport_version, 2), session_.get(), BIDIRECTIONAL); - session_->ActivateStream(QuicWrapUnique(stream)); + session_->ActivateStream(absl::WrapUnique(stream)); std::string data(100, '.'); EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)) 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 641f5c90c03..d6fa2da7a10 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 @@ -192,6 +192,7 @@ void QuicTimeWaitListManager::ProcessPacket( const QuicSocketAddress& peer_address, QuicConnectionId connection_id, PacketHeaderFormat header_format, + size_t received_packet_length, std::unique_ptr<QuicPerPacketContext> packet_context) { QUICHE_DCHECK(IsConnectionIdInTimeWait(connection_id)); // TODO(satyamshekhar): Think about handling packets from different peer @@ -224,7 +225,7 @@ void QuicTimeWaitListManager::ProcessPacket( switch (connection_data->action) { case SEND_TERMINATION_PACKETS: if (connection_data->info.termination_packets.empty()) { - QUIC_BUG << "There are no termination packets."; + QUIC_BUG(quic_bug_10608_1) << "There are no termination packets."; return; } switch (header_format) { @@ -240,7 +241,7 @@ void QuicTimeWaitListManager::ProcessPacket( // Send stateless reset in response to short header packets. SendPublicReset(self_address, peer_address, connection_id, connection_data->info.ietf_quic, - std::move(packet_context)); + received_packet_length, std::move(packet_context)); return; case GOOGLE_QUIC_PACKET: if (connection_data->info.ietf_quic) { @@ -258,7 +259,7 @@ void QuicTimeWaitListManager::ProcessPacket( case SEND_CONNECTION_CLOSE_PACKETS: if (connection_data->info.termination_packets.empty()) { - QUIC_BUG << "There are no termination packets."; + QUIC_BUG(quic_bug_10608_2) << "There are no termination packets."; return; } for (const auto& packet : connection_data->info.termination_packets) { @@ -273,7 +274,7 @@ void QuicTimeWaitListManager::ProcessPacket( QUIC_CODE_COUNT(quic_stateless_reset_long_header_packet); } SendPublicReset(self_address, peer_address, connection_id, - connection_data->info.ietf_quic, + connection_data->info.ietf_quic, received_packet_length, std::move(packet_context)); return; case DO_NOTHING: @@ -318,10 +319,11 @@ void QuicTimeWaitListManager::SendPublicReset( const QuicSocketAddress& peer_address, QuicConnectionId connection_id, bool ietf_quic, + size_t received_packet_length, std::unique_ptr<QuicPerPacketContext> packet_context) { if (ietf_quic) { std::unique_ptr<QuicEncryptedPacket> ietf_reset_packet = - BuildIetfStatelessResetPacket(connection_id); + BuildIetfStatelessResetPacket(connection_id, received_packet_length); QUIC_DVLOG(2) << "Dispatcher sending IETF reset packet for " << connection_id << std::endl << quiche::QuicheTextUtils::HexDump( @@ -333,6 +335,7 @@ void QuicTimeWaitListManager::SendPublicReset( packet_context.get()); return; } + // Google QUIC public resets donot elicit resets in response. QuicPublicResetPacket packet; packet.connection_id = connection_id; // TODO(satyamshekhar): generate a valid nonce for this connection_id. @@ -366,9 +369,11 @@ std::unique_ptr<QuicEncryptedPacket> QuicTimeWaitListManager::BuildPublicReset( std::unique_ptr<QuicEncryptedPacket> QuicTimeWaitListManager::BuildIetfStatelessResetPacket( - QuicConnectionId connection_id) { + QuicConnectionId connection_id, + size_t received_packet_length) { return QuicFramer::BuildIetfStatelessResetPacket( - connection_id, GetStatelessResetToken(connection_id)); + connection_id, received_packet_length, + GetStatelessResetToken(connection_id)); } // Either sends the packet and deletes it or makes pending queue the @@ -495,7 +500,7 @@ QuicTimeWaitListManager::ConnectionIdData::ConnectionIdData( QuicTimeWaitListManager::ConnectionIdData::~ConnectionIdData() = default; -QuicUint128 QuicTimeWaitListManager::GetStatelessResetToken( +StatelessResetToken QuicTimeWaitListManager::GetStatelessResetToken( QuicConnectionId connection_id) const { return QuicUtils::GenerateStatelessResetToken(connection_id); } 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 9b3739b958c..2018f723306 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 @@ -18,6 +18,7 @@ #include "quic/core/quic_packet_writer.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_session.h" +#include "quic/core/quic_types.h" #include "quic/platform/api/quic_containers.h" #include "quic/platform/api/quic_flags.h" @@ -118,11 +119,14 @@ class QUIC_NO_EXPORT QuicTimeWaitListManager // connection_id. Sending of the public reset packet is throttled by using // exponential back off. QUICHE_DCHECKs for the connection_id to be in time // wait state. virtual to override in tests. + // TODO(fayang): change ProcessPacket and SendPublicReset to take + // ReceivedPacketInfo. virtual void ProcessPacket( const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, QuicConnectionId connection_id, PacketHeaderFormat header_format, + size_t received_packet_length, std::unique_ptr<QuicPerPacketContext> packet_context); // Called by the dispatcher when the underlying socket becomes writable again, @@ -164,6 +168,7 @@ class QUIC_NO_EXPORT QuicTimeWaitListManager const QuicSocketAddress& peer_address, QuicConnectionId connection_id, bool ietf_quic, + size_t received_packet_length, std::unique_ptr<QuicPerPacketContext> packet_context); // Called to send |packet|. @@ -182,7 +187,7 @@ class QUIC_NO_EXPORT QuicTimeWaitListManager // Returns a stateless reset token which will be included in the public reset // packet. - virtual QuicUint128 GetStatelessResetToken( + virtual StatelessResetToken GetStatelessResetToken( QuicConnectionId connection_id) const; // Internal structure to store pending termination packets. @@ -257,7 +262,8 @@ class QUIC_NO_EXPORT QuicTimeWaitListManager QuicTime::Delta /*srtt*/) const {} std::unique_ptr<QuicEncryptedPacket> BuildIetfStatelessResetPacket( - QuicConnectionId connection_id); + QuicConnectionId connection_id, + size_t received_packet_length); // A map from a recently closed connection_id to the number of packets // received after the termination of the connection bound to the 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 189d3f09b21..d33e992e372 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 @@ -21,7 +21,6 @@ #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_test.h" -#include "quic/platform/api/quic_uint128.h" #include "quic/test_tools/mock_quic_session_visitor.h" #include "quic/test_tools/quic_test_utils.h" #include "quic/test_tools/quic_time_wait_list_manager_peer.h" @@ -41,6 +40,8 @@ namespace quic { namespace test { namespace { +const size_t kTestPacketSize = 100; + class FramerVisitorCapturingPublicReset : public NoOpFramerVisitor { public: FramerVisitorCapturingPublicReset(QuicConnectionId connection_id) @@ -55,7 +56,8 @@ class FramerVisitorCapturingPublicReset : public NoOpFramerVisitor { return public_reset_packet_; } - bool IsValidStatelessResetToken(QuicUint128 token) const override { + bool IsValidStatelessResetToken( + const StatelessResetToken& token) const override { return token == QuicUtils::GenerateStatelessResetToken(connection_id_); } @@ -176,7 +178,7 @@ class QuicTimeWaitListManagerTest : public QuicTest { void ProcessPacket(QuicConnectionId connection_id) { time_wait_list_manager_.ProcessPacket( self_address_, peer_address_, connection_id, GOOGLE_QUIC_PACKET, - std::make_unique<QuicPerPacketContext>()); + kTestPacketSize, std::make_unique<QuicPerPacketContext>()); } QuicEncryptedPacket* ConstructEncryptedPacket( @@ -218,7 +220,7 @@ bool ValidPublicResetPacketPredicate( QuicIetfStatelessResetPacket stateless_reset = visitor.stateless_reset_packet(); - QuicUint128 expected_stateless_reset_token = + StatelessResetToken expected_stateless_reset_token = QuicUtils::GenerateStatelessResetToken(expected_connection_id); bool stateless_reset_is_valid = @@ -684,7 +686,8 @@ TEST_F(QuicTimeWaitListManagerTest, // Processes IETF short header packet. time_wait_list_manager_.ProcessPacket( self_address_, peer_address_, connection_id_, - IETF_QUIC_SHORT_HEADER_PACKET, std::make_unique<QuicPerPacketContext>()); + IETF_QUIC_SHORT_HEADER_PACKET, kTestPacketSize, + std::make_unique<QuicPerPacketContext>()); } TEST_F(QuicTimeWaitListManagerTest, @@ -707,7 +710,8 @@ TEST_F(QuicTimeWaitListManagerTest, // Processes IETF short header packet. time_wait_list_manager_.ProcessPacket( self_address_, peer_address_, connection_id_, - IETF_QUIC_SHORT_HEADER_PACKET, std::make_unique<QuicPerPacketContext>()); + IETF_QUIC_SHORT_HEADER_PACKET, kTestPacketSize, + std::make_unique<QuicPerPacketContext>()); } TEST_F(QuicTimeWaitListManagerTest, @@ -741,7 +745,7 @@ TEST_F(QuicTimeWaitListManagerTest, for (auto const& cid : active_connection_ids) { time_wait_list_manager_.ProcessPacket( self_address_, peer_address_, cid, IETF_QUIC_SHORT_HEADER_PACKET, - std::make_unique<QuicPerPacketContext>()); + kTestPacketSize, std::make_unique<QuicPerPacketContext>()); } } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor.cc b/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor.cc index 381d29bd5e3..f603f583e2d 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor.cc @@ -22,7 +22,7 @@ quic_trace::EncryptionLevel EncryptionLevelToProto(EncryptionLevel level) { case ENCRYPTION_FORWARD_SECURE: return quic_trace::ENCRYPTION_1RTT; case NUM_ENCRYPTION_LEVELS: - QUIC_BUG << "Invalid encryption level specified"; + QUIC_BUG(quic_bug_10284_1) << "Invalid encryption level specified"; return quic_trace::ENCRYPTION_UNKNOWN; } } @@ -77,7 +77,7 @@ void QuicTraceVisitor::OnPacketSent( case MTU_DISCOVERY_FRAME: case STOP_WAITING_FRAME: case ACK_FRAME: - QUIC_BUG + QUIC_BUG(quic_bug_12732_1) << "Frames of type are not retransmittable and are not supposed " "to be in retransmittable_frames"; break; @@ -100,7 +100,7 @@ void QuicTraceVisitor::OnPacketSent( break; case NUM_FRAME_TYPES: - QUIC_BUG << "Unknown frame type encountered"; + QUIC_BUG(quic_bug_10284_2) << "Unknown frame type encountered"; break; } } @@ -228,7 +228,7 @@ void QuicTraceVisitor::PopulateFrameInfo(const QuicFrame& frame, break; case NUM_FRAME_TYPES: - QUIC_BUG << "Unknown frame type encountered"; + QUIC_BUG(quic_bug_10284_3) << "Unknown frame type encountered"; break; } } @@ -314,7 +314,8 @@ void QuicTraceVisitor::OnAdjustNetworkParameters(QuicBandwidth bandwidth, uint64_t QuicTraceVisitor::ConvertTimestampToRecordedFormat( QuicTime timestamp) { if (timestamp < start_time_) { - QUIC_BUG << "Timestamp went back in time while recording a trace"; + QUIC_BUG(quic_bug_10284_4) + << "Timestamp went back in time while recording a trace"; return 0; } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_types.cc b/chromium/net/third_party/quiche/src/quic/core/quic_types.cc index f8182684e7f..57163abbe42 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_types.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_types.cc @@ -11,6 +11,9 @@ namespace quic { +static_assert(sizeof(StatelessResetToken) == kStatelessResetTokenLength, + "bad size"); + std::ostream& operator<<(std::ostream& os, const QuicConsumedData& s) { os << "bytes_consumed: " << s.bytes_consumed << " fin_consumed: " << s.fin_consumed; 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 7344cea6de3..2e65e5f12a4 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 @@ -7,6 +7,7 @@ #include <array> #include <cstddef> +#include <cstdint> #include <map> #include <ostream> #include <vector> @@ -43,6 +44,12 @@ using QuicStreamOffset = uint64_t; using DiversificationNonce = std::array<char, 32>; using PacketTimeVector = std::vector<std::pair<QuicPacketNumber, QuicTime>>; +enum : size_t { kStatelessResetTokenLength = 16 }; +using StatelessResetToken = std::array<char, kStatelessResetTokenLength>; + +// WebTransport session IDs are stream IDs. +using WebTransportSessionId = uint64_t; + enum : size_t { kQuicPathFrameBufferSize = 8 }; using QuicPathFrameBuffer = std::array<uint8_t, kQuicPathFrameBufferSize>; @@ -278,7 +285,7 @@ QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, // byte, with the two most significant bits being 0. Thus, the following // enumerations are valid as both the numeric values of frame types AND their // encodings. -enum QuicIetfFrameType : uint8_t { +enum QuicIetfFrameType : uint64_t { IETF_PADDING = 0x00, IETF_PING = 0x01, IETF_ACK = 0x02, @@ -708,10 +715,10 @@ enum AckResult { // Indicates the fate of a serialized packet in WritePacket(). enum SerializedPacketFate : uint8_t { - DISCARD, // Discard the packet. - COALESCE, // Try to coalesce packet. - BUFFER, // Buffer packet in buffered_packets_. - SEND_TO_WRITER, // Send packet to writer. + DISCARD, // Discard the packet. + COALESCE, // Try to coalesce packet. + BUFFER, // Buffer packet in buffered_packets_. + SEND_TO_WRITER, // Send packet to writer. LEGACY_VERSION_ENCAPSULATE, // Perform Legacy Version Encapsulation on this // packet. }; diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket_posix.cc b/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket_posix.cc index e410635cef5..eef582b99f1 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket_posix.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket_posix.cc @@ -157,7 +157,7 @@ void PopulatePacketInfoFromControlMessage(struct cmsghdr* cmsg, if (self_v6_ip.FromPackedString(addr_data, addr_len)) { packet_info->SetSelfV6Ip(self_v6_ip); } else { - QUIC_BUG << "QuicIpAddress::FromPackedString failed"; + QUIC_BUG(quic_bug_10751_1) << "QuicIpAddress::FromPackedString failed"; } } return; @@ -172,7 +172,7 @@ void PopulatePacketInfoFromControlMessage(struct cmsghdr* cmsg, if (self_v4_ip.FromPackedString(addr_data, addr_len)) { packet_info->SetSelfV4Ip(self_v4_ip); } else { - QUIC_BUG << "QuicIpAddress::FromPackedString failed"; + QUIC_BUG(quic_bug_10751_2) << "QuicIpAddress::FromPackedString failed"; } } return; @@ -421,7 +421,8 @@ void QuicUdpSocketApi::ReadPacket(QuicUdpSocketFd fd, } if (QUIC_PREDICT_FALSE(hdr.msg_flags & MSG_CTRUNC)) { - QUIC_BUG << "Control buffer too small. size:" << control_buffer.buffer_len; + QUIC_BUG(quic_bug_10751_3) + << "Control buffer too small. size:" << control_buffer.buffer_len; return; } @@ -509,9 +510,9 @@ size_t QuicUdpSocketApi::ReadMultiplePackets(QuicUdpSocketFd fd, msghdr& hdr = hdrs[i].msg_hdr; if (QUIC_PREDICT_FALSE(hdr.msg_flags & MSG_CTRUNC)) { - QUIC_BUG << "Control buffer too small. size:" - << (*results)[i].control_buffer.buffer_len - << ", need:" << hdr.msg_controllen; + QUIC_BUG(quic_bug_10751_4) << "Control buffer too small. size:" + << (*results)[i].control_buffer.buffer_len + << ", need:" << hdr.msg_controllen; continue; } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket_test.cc deleted file mode 100644 index 400c38e6fda..00000000000 --- a/chromium/net/third_party/quiche/src/quic/core/quic_udp_socket_test.cc +++ /dev/null @@ -1,439 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "quic/core/quic_udp_socket.h" -#include <sys/socket.h> - -#ifdef __APPLE__ -#include <TargetConditionals.h> -#endif - -#include "quic/core/quic_constants.h" -#include "quic/platform/api/quic_test.h" - -namespace quic { -namespace test { -namespace { -// Used by ReadMultiplePackets tests. -struct ReadBuffers { - char control_buffer[512]; - char packet_buffer[1536]; -}; - -// Allows IPv6-specific testing. -struct TestParameters { - // If true, the test will only use IPv6. If false, IPv4 will be used if - // possible, with IPv6 used as a fallback. - bool force_ipv6; - // The value of ipv6_only to be used in QuicUdpSocketApi::Create calls. - bool ipv6_only; -}; -} // namespace - -class QuicUdpSocketTest : public QuicTestWithParam<TestParameters> { - protected: - void SetUp() override { - const TestParameters& parameters = GetParam(); - if (!parameters.force_ipv6) { - // Try creating AF_INET socket, if it fails because of unsupported address - // family then tests are being run under IPv6-only environment, initialize - // address family to use for running the test under as AF_INET6 otherwise - // initialize it as AF_INET. - address_family_ = AF_INET; - fd_client_ = - api_.Create(address_family_, - /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, - /*send_buffer_size =*/kDefaultSocketReceiveBuffer, - /*ipv6_only =*/parameters.ipv6_only); - } - if (fd_client_ == kQuicInvalidSocketFd) { - // Either AF_INET is unsupported, or force_ipv6 is true. - address_family_ = AF_INET6; - fd_client_ = - api_.Create(address_family_, - /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, - /*send_buffer_size =*/kDefaultSocketReceiveBuffer, - /*ipv6_only =*/parameters.ipv6_only); - } - ASSERT_NE(fd_client_, kQuicInvalidSocketFd); - - fd_server_ = - api_.Create(address_family_, - /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, - /*send_buffer_size =*/kDefaultSocketReceiveBuffer, - /*ipv6_only =*/parameters.ipv6_only); - ASSERT_NE(fd_server_, kQuicInvalidSocketFd); - - ASSERT_TRUE( - api_.Bind(fd_server_, QuicSocketAddress(Loopback(), /*port=*/0))); - - ASSERT_EQ(0, server_address_.FromSocket(fd_server_)); - - QUIC_LOG(INFO) << "Testing under IP" - << std::string((address_family_ == AF_INET) ? "v4" : "v6"); - } - - ~QuicUdpSocketTest() { - api_.Destroy(fd_client_); - api_.Destroy(fd_server_); - } - - QuicIpAddress Loopback() const { - return (address_family_ == AF_INET) ? QuicIpAddress::Loopback4() - : QuicIpAddress::Loopback6(); - } - - // Client sends the first |packet_size| bytes in |client_packet_buffer_| to - // server. - WriteResult SendPacketFromClient(size_t packet_size) { - EXPECT_LE(packet_size, sizeof(client_packet_buffer_)); - QuicUdpPacketInfo packet_info; - packet_info.SetPeerAddress(server_address_); - return api_.WritePacket(fd_client_, client_packet_buffer_, packet_size, - packet_info); - } - - WriteResult SendPacketFromClientWithTtl(size_t packet_size, int ttl) { - EXPECT_LE(packet_size, sizeof(client_packet_buffer_)); - QuicUdpPacketInfo packet_info; - packet_info.SetPeerAddress(server_address_); - packet_info.SetTtl(ttl); - return api_.WritePacket(fd_client_, client_packet_buffer_, packet_size, - packet_info); - } - - // Server waits for an incoming packet and reads it into - // |server_packet_buffer_|. - QuicUdpSocketApi::ReadPacketResult ReadPacketFromServer( - BitMask64 packet_info_interested) { - EXPECT_TRUE( - api_.WaitUntilReadable(fd_server_, QuicTime::Delta::FromSeconds(5))); - memset(server_packet_buffer_, 0, sizeof(server_packet_buffer_)); - QuicUdpSocketApi::ReadPacketResult result; - result.packet_buffer = {server_packet_buffer_, - sizeof(server_packet_buffer_)}; - result.control_buffer = {server_control_buffer_, - sizeof(server_control_buffer_)}; - api_.ReadPacket(fd_server_, packet_info_interested, &result); - return result; - } - - int ComparePacketBuffers(size_t packet_size) { - return memcmp(client_packet_buffer_, server_packet_buffer_, packet_size); - } - - bool VerifyBufferIsFilledWith(const char* buffer, - size_t buffer_length, - char c) { - for (size_t i = 0; i < buffer_length; ++i) { - if (buffer[i] != c) { - return false; - } - } - return true; - } - - QuicUdpSocketApi api_; - QuicUdpSocketFd fd_client_ = kQuicInvalidSocketFd; - QuicUdpSocketFd fd_server_ = kQuicInvalidSocketFd; - QuicSocketAddress server_address_; - int address_family_; - char client_packet_buffer_[kEthernetMTU] = {0}; - char server_packet_buffer_[kDefaultMaxPacketSize] = {0}; - char server_control_buffer_[512] = {0}; -}; - -INSTANTIATE_TEST_SUITE_P( - PlatformIndependent, - QuicUdpSocketTest, - testing::Values(TestParameters{/*force_ipv6 =*/false, /*ipv6_only =*/false}, - TestParameters{/*force_ipv6 =*/false, /*ipv6_only =*/true}, - TestParameters{/*force_ipv6 =*/true, /*ipv6_only =*/true})); - -#ifndef TARGET_OS_IPHONE -// IPv6 on iOS is known to fail without ipv6_only, so should not be tested. -INSTANTIATE_TEST_SUITE_P(NonIos, - QuicUdpSocketTest, - testing::Values(TestParameters{/*force_ipv6 =*/true, - /*ipv6_only =*/false})); -#endif - -TEST_P(QuicUdpSocketTest, ReadPacketResultReset) { - QuicUdpSocketApi::ReadPacketResult result; - result.packet_info.SetDroppedPackets(100); - result.packet_buffer.buffer_len = 100; - result.ok = true; - - result.Reset(/*packet_buffer_length=*/200); - - EXPECT_FALSE(result.ok); - EXPECT_FALSE( - result.packet_info.HasValue(QuicUdpPacketInfoBit::DROPPED_PACKETS)); - EXPECT_EQ(200u, result.packet_buffer.buffer_len); -} - -TEST_P(QuicUdpSocketTest, ReadPacketOnly) { - const size_t kPacketSize = 512; - memset(client_packet_buffer_, '-', kPacketSize); - ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), - SendPacketFromClient(kPacketSize)); - - QuicUdpSocketApi::ReadPacketResult read_result = - ReadPacketFromServer(/*packet_info_interested=*/BitMask64()); - ASSERT_TRUE(read_result.ok); - ASSERT_EQ(kPacketSize, read_result.packet_buffer.buffer_len); - ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); -} - -TEST_P(QuicUdpSocketTest, ReadTruncated) { - const size_t kPacketSize = kDefaultMaxPacketSize + 1; - memset(client_packet_buffer_, '*', kPacketSize); - ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), - SendPacketFromClient(kPacketSize)); - - QuicUdpSocketApi::ReadPacketResult read_result = - ReadPacketFromServer(/*packet_info_interested=*/BitMask64()); - ASSERT_FALSE(read_result.ok); -} - -TEST_P(QuicUdpSocketTest, ReadDroppedPackets) { - const size_t kPacketSize = kDefaultMaxPacketSize; - memset(client_packet_buffer_, '-', kPacketSize); - ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), - SendPacketFromClient(kPacketSize)); - ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), - SendPacketFromClient(kPacketSize)); - - // Read the first packet without enabling DROPPED_PACKETS. - QuicUdpSocketApi::ReadPacketResult read_result = - ReadPacketFromServer(BitMask64(QuicUdpPacketInfoBit::DROPPED_PACKETS)); - ASSERT_TRUE(read_result.ok); - ASSERT_EQ(kPacketSize, read_result.packet_buffer.buffer_len); - ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); - ASSERT_FALSE( - read_result.packet_info.HasValue(QuicUdpPacketInfoBit::DROPPED_PACKETS)); - - // Enable DROPPED_PACKETS and read the second packet. - if (!api_.EnableDroppedPacketCount(fd_server_)) { - QUIC_LOG(INFO) << "DROPPED_PACKETS is not supported"; - return; - } - read_result = - ReadPacketFromServer(BitMask64(QuicUdpPacketInfoBit::DROPPED_PACKETS)); - ASSERT_TRUE(read_result.ok); - ASSERT_EQ(kPacketSize, read_result.packet_buffer.buffer_len); - ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); - if (read_result.packet_info.HasValue(QuicUdpPacketInfoBit::DROPPED_PACKETS)) { - EXPECT_EQ(0u, read_result.packet_info.dropped_packets()); - } -} - -TEST_P(QuicUdpSocketTest, ReadSelfIp) { - const QuicUdpPacketInfoBit self_ip_bit = - (address_family_ == AF_INET) ? QuicUdpPacketInfoBit::V4_SELF_IP - : QuicUdpPacketInfoBit::V6_SELF_IP; - - const size_t kPacketSize = 512; - memset(client_packet_buffer_, '&', kPacketSize); - ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), - SendPacketFromClient(kPacketSize)); - - QuicUdpSocketApi::ReadPacketResult read_result = - ReadPacketFromServer(BitMask64(self_ip_bit)); - ASSERT_TRUE(read_result.ok); - ASSERT_EQ(kPacketSize, read_result.packet_buffer.buffer_len); - ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); - ASSERT_TRUE(read_result.packet_info.HasValue(self_ip_bit)); - EXPECT_EQ(Loopback(), (address_family_ == AF_INET) - ? read_result.packet_info.self_v4_ip() - : read_result.packet_info.self_v6_ip()); -} - -TEST_P(QuicUdpSocketTest, ReadReceiveTimestamp) { - const size_t kPacketSize = kDefaultMaxPacketSize; - memset(client_packet_buffer_, '-', kPacketSize); - ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), - SendPacketFromClient(kPacketSize)); - ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), - SendPacketFromClient(kPacketSize)); - - // Read the first packet without enabling RECV_TIMESTAMP. - QuicUdpSocketApi::ReadPacketResult read_result = - ReadPacketFromServer(BitMask64(QuicUdpPacketInfoBit::RECV_TIMESTAMP)); - ASSERT_TRUE(read_result.ok); - ASSERT_EQ(kPacketSize, read_result.packet_buffer.buffer_len); - ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); - ASSERT_FALSE( - read_result.packet_info.HasValue(QuicUdpPacketInfoBit::RECV_TIMESTAMP)); - - // Enable RECV_TIMESTAMP and read the second packet. - if (!api_.EnableReceiveTimestamp(fd_server_)) { - QUIC_LOG(INFO) << "RECV_TIMESTAMP is not supported"; - return; - } - read_result = - ReadPacketFromServer(BitMask64(QuicUdpPacketInfoBit::RECV_TIMESTAMP)); - ASSERT_TRUE(read_result.ok); - ASSERT_EQ(kPacketSize, read_result.packet_buffer.buffer_len); - ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); - ASSERT_TRUE( - read_result.packet_info.HasValue(QuicUdpPacketInfoBit::RECV_TIMESTAMP)); - QuicWallTime recv_timestamp = read_result.packet_info.receive_timestamp(); - // 1577836800 is the unix seconds for 2020-01-01 - EXPECT_TRUE( - QuicWallTime::FromUNIXSeconds(1577836800).IsBefore(recv_timestamp)); -} - -TEST_P(QuicUdpSocketTest, Ttl) { - const size_t kPacketSize = 512; - memset(client_packet_buffer_, '$', kPacketSize); - ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), - SendPacketFromClientWithTtl(kPacketSize, 13)); - ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), - SendPacketFromClientWithTtl(kPacketSize, 13)); - - // Read the first packet without enabling ttl reporting. - QuicUdpSocketApi::ReadPacketResult read_result = - ReadPacketFromServer(BitMask64(QuicUdpPacketInfoBit::TTL)); - ASSERT_TRUE(read_result.ok); - ASSERT_EQ(kPacketSize, read_result.packet_buffer.buffer_len); - ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); - ASSERT_FALSE(read_result.packet_info.HasValue(QuicUdpPacketInfoBit::TTL)); - - // Enable ttl reporting and read the second packet. - if (!((address_family_ == AF_INET) - ? api_.EnableReceiveTtlForV4(fd_server_) - : api_.EnableReceiveTtlForV6(fd_server_))) { - QUIC_LOG(INFO) << "TTL is not supported for address family " - << address_family_; - return; - } - - read_result = ReadPacketFromServer(BitMask64(QuicUdpPacketInfoBit::TTL)); - ASSERT_TRUE(read_result.ok); - ASSERT_EQ(kPacketSize, read_result.packet_buffer.buffer_len); - ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); - ASSERT_TRUE(read_result.packet_info.HasValue(QuicUdpPacketInfoBit::TTL)); - EXPECT_EQ(13, read_result.packet_info.ttl()); -} - -TEST_P(QuicUdpSocketTest, ReadMultiplePackets) { - const QuicUdpPacketInfoBit self_ip_bit = - (address_family_ == AF_INET) ? QuicUdpPacketInfoBit::V4_SELF_IP - : QuicUdpPacketInfoBit::V6_SELF_IP; - const size_t kPacketSize = kDefaultMaxPacketSize; - const size_t kNumPackets = 90; - for (size_t i = 0; i < kNumPackets; ++i) { - memset(client_packet_buffer_, ' ' + i, kPacketSize); - ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), - SendPacketFromClient(kPacketSize)); - } - - const size_t kNumPacketsPerRead = 16; - size_t total_packets_read = 0; - while (total_packets_read < kNumPackets) { - std::unique_ptr<ReadBuffers[]> read_buffers = - std::make_unique<ReadBuffers[]>(kNumPacketsPerRead); - QuicUdpSocketApi::ReadPacketResults results(kNumPacketsPerRead); - for (size_t i = 0; i < kNumPacketsPerRead; ++i) { - results[i].packet_buffer.buffer = read_buffers[i].packet_buffer; - results[i].packet_buffer.buffer_len = - sizeof(read_buffers[i].packet_buffer); - - results[i].control_buffer.buffer = read_buffers[i].control_buffer; - results[i].control_buffer.buffer_len = - sizeof(read_buffers[i].control_buffer); - } - size_t packets_read = - api_.ReadMultiplePackets(fd_server_, BitMask64(self_ip_bit), &results); - if (packets_read == 0) { - ASSERT_TRUE( - api_.WaitUntilReadable(fd_server_, QuicTime::Delta::FromSeconds(5))); - packets_read = api_.ReadMultiplePackets(fd_server_, - BitMask64(self_ip_bit), &results); - ASSERT_GT(packets_read, 0u); - } - - for (size_t i = 0; i < packets_read; ++i) { - const auto& result = results[i]; - ASSERT_TRUE(result.ok); - ASSERT_EQ(kPacketSize, result.packet_buffer.buffer_len); - ASSERT_TRUE(VerifyBufferIsFilledWith(result.packet_buffer.buffer, - result.packet_buffer.buffer_len, - ' ' + total_packets_read)); - ASSERT_TRUE(result.packet_info.HasValue(self_ip_bit)); - EXPECT_EQ(Loopback(), (address_family_ == AF_INET) - ? result.packet_info.self_v4_ip() - : result.packet_info.self_v6_ip()); - total_packets_read++; - } - } -} - -TEST_P(QuicUdpSocketTest, ReadMultiplePacketsSomeTruncated) { - const QuicUdpPacketInfoBit self_ip_bit = - (address_family_ == AF_INET) ? QuicUdpPacketInfoBit::V4_SELF_IP - : QuicUdpPacketInfoBit::V6_SELF_IP; - const size_t kPacketSize = kDefaultMaxPacketSize; - const size_t kNumPackets = 90; - for (size_t i = 0; i < kNumPackets; ++i) { - memset(client_packet_buffer_, ' ' + i, kPacketSize); - ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), - SendPacketFromClient(kPacketSize)); - } - - const size_t kNumPacketsPerRead = 16; - size_t total_packets_read = 0; // Including truncated packets. - while (total_packets_read < kNumPackets) { - std::unique_ptr<ReadBuffers[]> read_buffers = - std::make_unique<ReadBuffers[]>(kNumPacketsPerRead); - QuicUdpSocketApi::ReadPacketResults results(kNumPacketsPerRead); - // Use small packet buffer for all even-numbered packets and expect them to - // be truncated. - auto is_truncated = [total_packets_read](size_t i) { - return ((total_packets_read + i) % 2) == 1; - }; - - for (size_t i = 0; i < kNumPacketsPerRead; ++i) { - results[i].packet_buffer.buffer = read_buffers[i].packet_buffer; - results[i].packet_buffer.buffer_len = - is_truncated(i) ? kPacketSize - 1 - : sizeof(read_buffers[i].packet_buffer); - - results[i].control_buffer.buffer = read_buffers[i].control_buffer; - results[i].control_buffer.buffer_len = - sizeof(read_buffers[i].control_buffer); - } - size_t packets_read = - api_.ReadMultiplePackets(fd_server_, BitMask64(self_ip_bit), &results); - if (packets_read == 0) { - ASSERT_TRUE( - api_.WaitUntilReadable(fd_server_, QuicTime::Delta::FromSeconds(5))); - packets_read = api_.ReadMultiplePackets(fd_server_, - BitMask64(self_ip_bit), &results); - ASSERT_GT(packets_read, 0u); - } - - for (size_t i = 0; i < packets_read; ++i) { - const auto& result = results[i]; - if (is_truncated(i)) { - ASSERT_FALSE(result.ok); - } else { - ASSERT_TRUE(result.ok); - ASSERT_EQ(kPacketSize, result.packet_buffer.buffer_len); - ASSERT_TRUE(VerifyBufferIsFilledWith(result.packet_buffer.buffer, - result.packet_buffer.buffer_len, - ' ' + total_packets_read)); - ASSERT_TRUE(result.packet_info.HasValue(self_ip_bit)); - EXPECT_EQ(Loopback(), (address_family_ == AF_INET) - ? result.packet_info.self_v4_ip() - : result.packet_info.self_v6_ip()); - } - total_packets_read++; - } - } -} - -} // namespace test -} // namespace quic 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 eeda2159092..fbdcd3eeb8a 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 @@ -102,10 +102,10 @@ QuicFrameTypeBitfield GetFrameTypeBitfield(QuicFrameType type) { case ACK_FREQUENCY_FRAME: return kAckFrequencyFrameBitfield; case NUM_FRAME_TYPES: - QUIC_BUG << "Unexpected frame type"; + QUIC_BUG(quic_bug_10518_1) << "Unexpected frame type"; return kInvalidFrameBitfield; } - QUIC_BUG << "Unexpected frame type"; + QUIC_BUG(quic_bug_10518_2) << "Unexpected frame type"; return kInvalidFrameBitfield; } @@ -140,8 +140,8 @@ void QuicUnackedPacketMap::AddSentPacket(SerializedPacket* mutable_packet, const SerializedPacket& packet = *mutable_packet; QuicPacketNumber packet_number = packet.packet_number; QuicPacketLength bytes_sent = packet.encrypted_length; - QUIC_BUG_IF(largest_sent_packet_.IsInitialized() && - largest_sent_packet_ >= packet_number) + QUIC_BUG_IF(quic_bug_12645_1, largest_sent_packet_.IsInitialized() && + largest_sent_packet_ >= packet_number) << "largest_sent_packet_: " << largest_sent_packet_ << ", packet_number: " << packet_number; QUICHE_DCHECK_GE(packet_number, least_unacked_ + unacked_packets_.size()); @@ -158,7 +158,7 @@ void QuicUnackedPacketMap::AddSentPacket(SerializedPacket* mutable_packet, largest_sent_largest_acked_.UpdateMax(packet.largest_acked); if (!measure_rtt) { - QUIC_BUG_IF(set_in_flight); + QUIC_BUG_IF(quic_bug_12645_2, set_in_flight); info.state = NOT_CONTRIBUTING_RTT; } @@ -289,8 +289,8 @@ bool QuicUnackedPacketMap::IsUnacked(QuicPacketNumber packet_number) const { void QuicUnackedPacketMap::RemoveFromInFlight(QuicTransmissionInfo* info) { if (info->in_flight) { - QUIC_BUG_IF(bytes_in_flight_ < info->bytes_sent); - QUIC_BUG_IF(packets_in_flight_ == 0); + QUIC_BUG_IF(quic_bug_12645_3, bytes_in_flight_ < info->bytes_sent); + QUIC_BUG_IF(quic_bug_12645_4, packets_in_flight_ == 0); bytes_in_flight_ -= info->bytes_sent; --packets_in_flight_; @@ -298,11 +298,12 @@ void QuicUnackedPacketMap::RemoveFromInFlight(QuicTransmissionInfo* info) { GetPacketNumberSpace(info->encryption_level); if (bytes_in_flight_per_packet_number_space_[packet_number_space] < info->bytes_sent) { - QUIC_BUG << "bytes_in_flight: " - << bytes_in_flight_per_packet_number_space_[packet_number_space] - << " is smaller than bytes_sent: " << info->bytes_sent - << " for packet number space: " - << PacketNumberSpaceToString(packet_number_space); + QUIC_BUG(quic_bug_10518_3) + << "bytes_in_flight: " + << bytes_in_flight_per_packet_number_space_[packet_number_space] + << " is smaller than bytes_sent: " << info->bytes_sent + << " for packet number space: " + << PacketNumberSpaceToString(packet_number_space); bytes_in_flight_per_packet_number_space_[packet_number_space] = 0; } else { bytes_in_flight_per_packet_number_space_[packet_number_space] -= @@ -560,7 +561,8 @@ PacketNumberSpace QuicUnackedPacketMap::GetPacketNumberSpace( QuicPacketNumber QuicUnackedPacketMap::GetLargestAckedOfPacketNumberSpace( PacketNumberSpace packet_number_space) const { if (packet_number_space >= NUM_PACKET_NUMBER_SPACES) { - QUIC_BUG << "Invalid packet number space: " << packet_number_space; + QUIC_BUG(quic_bug_10518_4) + << "Invalid packet number space: " << packet_number_space; return QuicPacketNumber(); } return largest_acked_packets_[packet_number_space]; @@ -569,7 +571,8 @@ QuicPacketNumber QuicUnackedPacketMap::GetLargestAckedOfPacketNumberSpace( QuicTime QuicUnackedPacketMap::GetLastInFlightPacketSentTime( PacketNumberSpace packet_number_space) const { if (packet_number_space >= NUM_PACKET_NUMBER_SPACES) { - QUIC_BUG << "Invalid packet number space: " << packet_number_space; + QUIC_BUG(quic_bug_10518_5) + << "Invalid packet number space: " << packet_number_space; return QuicTime::Zero(); } return last_inflight_packets_sent_time_[packet_number_space]; @@ -579,7 +582,8 @@ QuicPacketNumber QuicUnackedPacketMap::GetLargestSentRetransmittableOfPacketNumberSpace( PacketNumberSpace packet_number_space) const { if (packet_number_space >= NUM_PACKET_NUMBER_SPACES) { - QUIC_BUG << "Invalid packet number space: " << packet_number_space; + QUIC_BUG(quic_bug_10518_6) + << "Invalid packet number space: " << packet_number_space; return QuicPacketNumber(); } return largest_sent_retransmittable_packets_[packet_number_space]; @@ -613,12 +617,14 @@ QuicUnackedPacketMap::GetFirstInFlightTransmissionInfoOfSpace( void QuicUnackedPacketMap::EnableMultiplePacketNumberSpacesSupport() { if (supports_multiple_packet_number_spaces_) { - QUIC_BUG << "Multiple packet number spaces has already been enabled"; + QUIC_BUG(quic_bug_10518_7) + << "Multiple packet number spaces has already been enabled"; return; } if (largest_sent_packet_.IsInitialized()) { - QUIC_BUG << "Try to enable multiple packet number spaces support after any " - "packet has been sent."; + QUIC_BUG(quic_bug_10518_8) + << "Try to enable multiple packet number spaces support after any " + "packet has been sent."; return; } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc b/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc index 7eff759077e..27b74bb9430 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc @@ -7,10 +7,12 @@ #include <algorithm> #include <cstdint> #include <cstring> +#include <limits> #include <string> #include "absl/base/macros.h" #include "absl/base/optimization.h" +#include "absl/numeric/int128.h" #include "absl/strings/string_view.h" #include "quic/core/quic_connection_id.h" #include "quic/core/quic_constants.h" @@ -20,7 +22,7 @@ #include "quic/platform/api/quic_flag_utils.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_prefetch.h" -#include "quic/platform/api/quic_uint128.h" +#include "common/platform/api/quiche_logging.h" #include "common/quiche_endian.h" namespace quic { @@ -36,42 +38,43 @@ namespace { #endif #ifdef QUIC_UTIL_HAS_UINT128 -QuicUint128 IncrementalHashFast(QuicUint128 uhash, absl::string_view data) { +absl::uint128 IncrementalHashFast(absl::uint128 uhash, absl::string_view data) { // This code ends up faster than the naive implementation for 2 reasons: - // 1. QuicUint128 is sufficiently complicated that the compiler + // 1. absl::uint128 is sufficiently complicated that the compiler // cannot transform the multiplication by kPrime into a shift-multiply-add; // it has go through all of the instructions for a 128-bit multiply. // 2. Because there are so fewer instructions (around 13), the hot loop fits // nicely in the instruction queue of many Intel CPUs. // kPrime = 309485009821345068724781371 - static const QuicUint128 kPrime = - (static_cast<QuicUint128>(16777216) << 64) + 315; - auto hi = QuicUint128High64(uhash); - auto lo = QuicUint128Low64(uhash); - QuicUint128 xhash = (static_cast<QuicUint128>(hi) << 64) + lo; + static const absl::uint128 kPrime = + (static_cast<absl::uint128>(16777216) << 64) + 315; + auto hi = absl::Uint128High64(uhash); + auto lo = absl::Uint128Low64(uhash); + absl::uint128 xhash = (static_cast<absl::uint128>(hi) << 64) + lo; const uint8_t* octets = reinterpret_cast<const uint8_t*>(data.data()); for (size_t i = 0; i < data.length(); ++i) { xhash = (xhash ^ static_cast<uint32_t>(octets[i])) * kPrime; } - return MakeQuicUint128(QuicUint128High64(xhash), QuicUint128Low64(xhash)); + return absl::MakeUint128(absl::Uint128High64(xhash), + absl::Uint128Low64(xhash)); } #endif #ifndef QUIC_UTIL_HAS_UINT128 // Slow implementation of IncrementalHash. In practice, only used by Chromium. -QuicUint128 IncrementalHashSlow(QuicUint128 hash, absl::string_view data) { +absl::uint128 IncrementalHashSlow(absl::uint128 hash, absl::string_view data) { // kPrime = 309485009821345068724781371 - static const QuicUint128 kPrime = MakeQuicUint128(16777216, 315); + static const absl::uint128 kPrime = absl::MakeUint128(16777216, 315); const uint8_t* octets = reinterpret_cast<const uint8_t*>(data.data()); for (size_t i = 0; i < data.length(); ++i) { - hash = hash ^ MakeQuicUint128(0, octets[i]); + hash = hash ^ absl::MakeUint128(0, octets[i]); hash = hash * kPrime; } return hash; } #endif -QuicUint128 IncrementalHash(QuicUint128 hash, absl::string_view data) { +absl::uint128 IncrementalHash(absl::uint128 hash, absl::string_view data) { #ifdef QUIC_UTIL_HAS_UINT128 return IncrementalHashFast(hash, data); #else @@ -99,27 +102,27 @@ uint64_t QuicUtils::FNV1a_64_Hash(absl::string_view data) { } // static -QuicUint128 QuicUtils::FNV1a_128_Hash(absl::string_view data) { +absl::uint128 QuicUtils::FNV1a_128_Hash(absl::string_view data) { return FNV1a_128_Hash_Three(data, absl::string_view(), absl::string_view()); } // static -QuicUint128 QuicUtils::FNV1a_128_Hash_Two(absl::string_view data1, - absl::string_view data2) { +absl::uint128 QuicUtils::FNV1a_128_Hash_Two(absl::string_view data1, + absl::string_view data2) { return FNV1a_128_Hash_Three(data1, data2, absl::string_view()); } // static -QuicUint128 QuicUtils::FNV1a_128_Hash_Three(absl::string_view data1, - absl::string_view data2, - absl::string_view data3) { +absl::uint128 QuicUtils::FNV1a_128_Hash_Three(absl::string_view data1, + absl::string_view data2, + absl::string_view data3) { // The two constants are defined as part of the hash algorithm. // see http://www.isthe.com/chongo/tech/comp/fnv/ // kOffset = 144066263297769815596495629667062367629 - const QuicUint128 kOffset = MakeQuicUint128(UINT64_C(7809847782465536322), - UINT64_C(7113472399480571277)); + const absl::uint128 kOffset = absl::MakeUint128( + UINT64_C(7809847782465536322), UINT64_C(7113472399480571277)); - QuicUint128 hash = IncrementalHash(kOffset, data1); + absl::uint128 hash = IncrementalHash(kOffset, data1); if (data2.empty()) { return hash; } @@ -132,9 +135,9 @@ QuicUint128 QuicUtils::FNV1a_128_Hash_Three(absl::string_view data1, } // static -void QuicUtils::SerializeUint128Short(QuicUint128 v, uint8_t* out) { - const uint64_t lo = QuicUint128Low64(v); - const uint64_t hi = QuicUint128High64(v); +void QuicUtils::SerializeUint128Short(absl::uint128 v, uint8_t* out) { + const uint64_t lo = absl::Uint128Low64(v); + const uint64_t hi = absl::Uint128High64(v); // This assumes that the system is little-endian. memcpy(out, &lo, sizeof(lo)); memcpy(out + sizeof(lo), &hi, sizeof(hi) / 2); @@ -280,7 +283,8 @@ void QuicUtils::CopyToBuffer(const struct iovec* iov, src = static_cast<char*>(iov[iovnum].iov_base); copy_len = std::min(buffer_length, iov[iovnum].iov_len); } - QUIC_BUG_IF(buffer_length > 0) << "Failed to copy entire length to buffer."; + QUIC_BUG_IF(quic_bug_10839_1, buffer_length > 0) + << "Failed to copy entire length to buffer."; } // static @@ -356,7 +360,8 @@ SentPacketState QuicUtils::RetransmissionTypeToPacketState( case ALL_INITIAL_RETRANSMISSION: return UNACKABLE; default: - QUIC_BUG << retransmission_type << " is not a retransmission_type"; + QUIC_BUG(quic_bug_10839_2) + << retransmission_type << " is not a retransmission_type"; return UNACKABLE; } } @@ -381,7 +386,7 @@ QuicStreamId QuicUtils::GetInvalidStreamId(QuicTransportVersion version) { // static QuicStreamId QuicUtils::GetCryptoStreamId(QuicTransportVersion version) { - QUIC_BUG_IF(QuicVersionUsesCryptoFrames(version)) + QUIC_BUG_IF(quic_bug_12982_1, QuicVersionUsesCryptoFrames(version)) << "CRYPTO data aren't in stream frames; they have no stream ID."; return QuicVersionUsesCryptoFrames(version) ? GetInvalidStreamId(version) : 1; } @@ -531,7 +536,7 @@ QuicConnectionId QuicUtils::CreateReplacementConnectionId( expected_connection_id_length); } char new_connection_id_data[255] = {}; - const QuicUint128 connection_id_hash128 = FNV1a_128_Hash( + const absl::uint128 connection_id_hash128 = FNV1a_128_Hash( absl::string_view(connection_id.data(), connection_id.length())); static_assert(sizeof(connection_id_hash64) + sizeof(connection_id_hash128) <= sizeof(new_connection_id_data), @@ -625,10 +630,15 @@ bool QuicUtils::IsConnectionIdValidForVersion( transport_version); } -QuicUint128 QuicUtils::GenerateStatelessResetToken( +StatelessResetToken QuicUtils::GenerateStatelessResetToken( QuicConnectionId connection_id) { - return FNV1a_128_Hash( + static_assert(sizeof(absl::uint128) == sizeof(StatelessResetToken), + "bad size"); + static_assert(alignof(absl::uint128) >= alignof(StatelessResetToken), + "bad alignment"); + absl::uint128 hash = FNV1a_128_Hash( absl::string_view(connection_id.data(), connection_id.length())); + return *reinterpret_cast<StatelessResetToken*>(&hash); } // static @@ -648,8 +658,9 @@ PacketNumberSpace QuicUtils::GetPacketNumberSpace( case ENCRYPTION_FORWARD_SECURE: return APPLICATION_DATA; default: - QUIC_BUG << "Try to get packet number space of encryption level: " - << encryption_level; + QUIC_BUG(quic_bug_10839_3) + << "Try to get packet number space of encryption level: " + << encryption_level; return NUM_PACKET_NUMBER_SPACES; } } @@ -683,5 +694,13 @@ bool QuicUtils::IsProbingFrame(QuicFrameType type) { } } +bool IsValidWebTransportSessionId(WebTransportSessionId id, + ParsedQuicVersion version) { + QUICHE_DCHECK(version.UsesHttp3()); + return (id <= std::numeric_limits<QuicStreamId>::max()) && + QuicUtils::IsBidirectionalStreamId(id, version) && + QuicUtils::IsClientInitiatedStreamId(version.transport_version, id); +} + #undef RETURN_STRING_LITERAL // undef for jumbo builds } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_utils.h b/chromium/net/third_party/quiche/src/quic/core/quic_utils.h index 91751175a3b..0b089b4bad6 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_utils.h +++ b/chromium/net/third_party/quiche/src/quic/core/quic_utils.h @@ -11,6 +11,7 @@ #include <string> #include <type_traits> +#include "absl/numeric/int128.h" #include "absl/strings/string_view.h" #include "quic/core/crypto/quic_random.h" #include "quic/core/frames/quic_frame.h" @@ -21,7 +22,6 @@ #include "quic/platform/api/quic_export.h" #include "quic/platform/api/quic_iovec.h" #include "quic/platform/api/quic_socket_address.h" -#include "quic/platform/api/quic_uint128.h" namespace quic { @@ -35,22 +35,22 @@ class QUIC_EXPORT_PRIVATE QuicUtils { // Returns the 128 bit FNV1a hash of the data. See // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param - static QuicUint128 FNV1a_128_Hash(absl::string_view data); + static absl::uint128 FNV1a_128_Hash(absl::string_view data); // Returns the 128 bit FNV1a hash of the two sequences of data. See // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param - static QuicUint128 FNV1a_128_Hash_Two(absl::string_view data1, - absl::string_view data2); + static absl::uint128 FNV1a_128_Hash_Two(absl::string_view data1, + absl::string_view data2); // Returns the 128 bit FNV1a hash of the three sequences of data. See // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param - static QuicUint128 FNV1a_128_Hash_Three(absl::string_view data1, - absl::string_view data2, - absl::string_view data3); + static absl::uint128 FNV1a_128_Hash_Three(absl::string_view data1, + absl::string_view data2, + absl::string_view data3); // SerializeUint128 writes the first 96 bits of |v| in little-endian form // to |out|. - static void SerializeUint128Short(QuicUint128 v, uint8_t* out); + static void SerializeUint128Short(absl::uint128 v, uint8_t* out); // Returns AddressChangeType as a string. static std::string AddressChangeTypeToString(AddressChangeType type); @@ -219,7 +219,7 @@ class QUIC_EXPORT_PRIVATE QuicUtils { static QuicConnectionId CreateZeroConnectionId(QuicTransportVersion version); // Generates a 128bit stateless reset token based on a connection ID. - static QuicUint128 GenerateStatelessResetToken( + static StatelessResetToken GenerateStatelessResetToken( QuicConnectionId connection_id); // Determines packet number space from |encryption_level|. @@ -239,6 +239,11 @@ class QUIC_EXPORT_PRIVATE QuicUtils { static bool IsProbingFrame(QuicFrameType type); }; +// Returns true if the specific ID is a valid WebTransport session ID that our +// implementation can process. +bool IsValidWebTransportSessionId(WebTransportSessionId id, + ParsedQuicVersion transport_version); + template <typename Mask> class QUIC_EXPORT_PRIVATE BitMask { public: diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc index a5249d7cf48..dc1165242f5 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc @@ -7,6 +7,7 @@ #include <string> #include "absl/base/macros.h" +#include "absl/numeric/int128.h" #include "absl/strings/string_view.h" #include "quic/core/crypto/crypto_protocol.h" #include "quic/core/quic_connection_id.h" @@ -79,17 +80,17 @@ TEST_F(QuicUtilsTest, DetermineAddressChangeType) { QuicUtils::DetermineAddressChangeType(old_address, new_address)); } -QuicUint128 IncrementalHashReference(const void* data, size_t len) { +absl::uint128 IncrementalHashReference(const void* data, size_t len) { // The two constants are defined as part of the hash algorithm. // see http://www.isthe.com/chongo/tech/comp/fnv/ // hash = 144066263297769815596495629667062367629 - QuicUint128 hash = MakeQuicUint128(UINT64_C(7809847782465536322), - UINT64_C(7113472399480571277)); + absl::uint128 hash = absl::MakeUint128(UINT64_C(7809847782465536322), + UINT64_C(7113472399480571277)); // kPrime = 309485009821345068724781371 - const QuicUint128 kPrime = MakeQuicUint128(16777216, 315); + const absl::uint128 kPrime = absl::MakeUint128(16777216, 315); const uint8_t* octets = reinterpret_cast<const uint8_t*>(data); for (size_t i = 0; i < len; ++i) { - hash = hash ^ MakeQuicUint128(0, octets[i]); + hash = hash ^ absl::MakeUint128(0, octets[i]); hash = hash * kPrime; } return hash; @@ -311,9 +312,12 @@ TEST_F(QuicUtilsTest, StatelessResetToken) { QuicConnectionId connection_id1a = test::TestConnectionId(1); QuicConnectionId connection_id1b = test::TestConnectionId(1); QuicConnectionId connection_id2 = test::TestConnectionId(2); - QuicUint128 token1a = QuicUtils::GenerateStatelessResetToken(connection_id1a); - QuicUint128 token1b = QuicUtils::GenerateStatelessResetToken(connection_id1b); - QuicUint128 token2 = QuicUtils::GenerateStatelessResetToken(connection_id2); + StatelessResetToken token1a = + QuicUtils::GenerateStatelessResetToken(connection_id1a); + StatelessResetToken token1b = + QuicUtils::GenerateStatelessResetToken(connection_id1b); + StatelessResetToken token2 = + QuicUtils::GenerateStatelessResetToken(connection_id2); EXPECT_EQ(token1a, token1b); EXPECT_NE(token1a, token2); } diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc b/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc index a189e263b94..ab68ddd57d1 100644 --- a/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc +++ b/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc @@ -59,8 +59,8 @@ void SetVersionFlag(const ParsedQuicVersion& version, bool should_enable) { } else if (version == ParsedQuicVersion::Q043()) { SetQuicReloadableFlag(quic_disable_version_q043, disable); } else { - QUIC_BUG << "Cannot " << (enable ? "en" : "dis") << "able version " - << version; + QUIC_BUG(quic_bug_10589_1) + << "Cannot " << (enable ? "en" : "dis") << "able version " << version; } } @@ -232,9 +232,10 @@ QuicVersionLabel CreateQuicVersionLabel(ParsedQuicVersion parsed_version) { } else if (parsed_version == ParsedQuicVersion::ReservedForNegotiation()) { return CreateRandomVersionLabelForNegotiation(); } - QUIC_BUG << "Unsupported version " - << QuicVersionToString(parsed_version.transport_version) << " " - << HandshakeProtocolToString(parsed_version.handshake_protocol); + QUIC_BUG(quic_bug_10589_2) + << "Unsupported version " + << QuicVersionToString(parsed_version.transport_version) << " " + << HandshakeProtocolToString(parsed_version.handshake_protocol); return 0; } @@ -255,7 +256,8 @@ ParsedQuicVersionVector AllSupportedVersionsWithQuicCrypto() { versions.push_back(version); } } - QUIC_BUG_IF(versions.empty()) << "No version with QUIC crypto found."; + QUIC_BUG_IF(quic_bug_10589_3, versions.empty()) + << "No version with QUIC crypto found."; return versions; } @@ -266,7 +268,8 @@ ParsedQuicVersionVector CurrentSupportedVersionsWithQuicCrypto() { versions.push_back(version); } } - QUIC_BUG_IF(versions.empty()) << "No version with QUIC crypto found."; + QUIC_BUG_IF(quic_bug_10589_4, versions.empty()) + << "No version with QUIC crypto found."; return versions; } @@ -277,7 +280,8 @@ ParsedQuicVersionVector AllSupportedVersionsWithTls() { versions.push_back(version); } } - QUIC_BUG_IF(versions.empty()) << "No version with TLS handshake found."; + QUIC_BUG_IF(quic_bug_10589_5, versions.empty()) + << "No version with TLS handshake found."; return versions; } @@ -288,7 +292,8 @@ ParsedQuicVersionVector CurrentSupportedVersionsWithTls() { versions.push_back(version); } } - QUIC_BUG_IF(versions.empty()) << "No version with TLS handshake found."; + QUIC_BUG_IF(quic_bug_10589_6, versions.empty()) + << "No version with TLS handshake found."; return versions; } @@ -414,7 +419,8 @@ ParsedQuicVersionVector FilterSupportedVersions( filtered_versions.push_back(version); } } else { - QUIC_BUG << "QUIC version " << version << " has no flag protection"; + QUIC_BUG(quic_bug_10589_7) + << "QUIC version " << version << " has no flag protection"; filtered_versions.push_back(version); } } @@ -593,7 +599,7 @@ std::string AlpnForVersion(ParsedQuicVersion parsed_version) { void QuicVersionInitializeSupportForIetfDraft() { // Enable necessary flags. - SetQuicRestartFlag(quic_enable_zero_rtt_for_tls_v2, true); + SetQuicReloadableFlag(quic_fix_key_update_on_first_packet, true); } void QuicEnableVersion(const ParsedQuicVersion& version) { diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_chlo_extractor.cc b/chromium/net/third_party/quiche/src/quic/core/tls_chlo_extractor.cc index ca9eb5b9865..ae85b07c251 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_chlo_extractor.cc +++ b/chromium/net/third_party/quiche/src/quic/core/tls_chlo_extractor.cc @@ -129,8 +129,8 @@ bool TlsChloExtractor::OnUnauthenticatedPublicHeader( bool TlsChloExtractor::OnProtocolVersionMismatch(ParsedQuicVersion version) { // This should never be called because we already check versions in // IngestPacket. - QUIC_BUG << "Unexpected version mismatch, expected " << framer_->version() - << ", got " << version; + QUIC_BUG(quic_bug_10855_1) << "Unexpected version mismatch, expected " + << framer_->version() << ", got " << version; return false; } @@ -156,7 +156,7 @@ bool TlsChloExtractor::OnCryptoFrame(const QuicCryptoFrame& frame) { if (frame.level != ENCRYPTION_INITIAL) { // Since we drop non-INITIAL packets in OnUnauthenticatedPublicHeader, // we should never receive any CRYPTO frames at other encryption levels. - QUIC_BUG << "Parsed bad-level CRYPTO frame " << frame; + QUIC_BUG(quic_bug_10855_2) << "Parsed bad-level CRYPTO frame " << frame; return false; } // parsed_crypto_frame_in_this_packet_ is checked in IngestPacket to allow @@ -242,7 +242,7 @@ void TlsChloExtractor::HandleUnexpectedCallback( const std::string& callback_name) { std::string error_details = absl::StrCat("Unexpected callback ", callback_name); - QUIC_BUG << error_details; + QUIC_BUG(quic_bug_10855_3) << error_details; HandleUnrecoverableError(error_details); } @@ -311,8 +311,8 @@ void TlsChloExtractor::HandleParsedChlo(const SSL_CLIENT_HELLO* client_hello) { } else if (state_ == State::kParsedPartialChloFragment) { state_ = State::kParsedFullMultiPacketChlo; } else { - QUIC_BUG << "Unexpected state on successful parse " - << StateToString(state_); + QUIC_BUG(quic_bug_10855_4) + << "Unexpected state on successful parse " << StateToString(state_); } } 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 f7392a5817f..c6d04552f43 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 @@ -13,6 +13,7 @@ #include "quic/core/quic_framer.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_stream_sequencer.h" +#include "quic/core/quic_types.h" #include "quic/platform/api/quic_export.h" namespace quic { @@ -156,7 +157,8 @@ class QUIC_NO_EXPORT TlsChloExtractor return true; } void OnPacketComplete() override {} - bool IsValidStatelessResetToken(QuicUint128 /*token*/) const override { + bool IsValidStatelessResetToken( + const StatelessResetToken& /*token*/) const override { return true; } void OnAuthenticatedIetfStatelessResetPacket( 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 448136f8013..f8e321663ca 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 @@ -58,7 +58,7 @@ bool TlsClientHandshaker::CryptoConnect() { // TODO(b/154162689) add PSK support to QUIC+TLS. std::string error_details = "QUIC client pre-shared keys not yet supported with TLS"; - QUIC_BUG << error_details; + QUIC_BUG(quic_bug_10576_1) << error_details; CloseConnection(QUIC_HANDSHAKE_FAILED, error_details); return false; } @@ -116,7 +116,8 @@ bool TlsClientHandshaker::PrepareZeroRttConfig( handshaker_delegate()->ProcessTransportParameters( *(cached_state->transport_params), /*is_resumption = */ true, &error_details) != QUIC_NO_ERROR) { - QUIC_BUG << "Unable to parse cached transport parameters."; + QUIC_BUG(quic_bug_10576_2) + << "Unable to parse cached transport parameters."; CloseConnection(QUIC_HANDSHAKE_FAILED, "Client failed to parse cached Transport Parameters."); return false; @@ -130,7 +131,7 @@ bool TlsClientHandshaker::PrepareZeroRttConfig( if (!cached_state->application_state || !session()->ResumeApplicationState( cached_state->application_state.get())) { - QUIC_BUG << "Unable to parse cached application state."; + QUIC_BUG(quic_bug_10576_3) << "Unable to parse cached application state."; CloseConnection(QUIC_HANDSHAKE_FAILED, "Client failed to parse cached application state."); return false; @@ -150,11 +151,11 @@ bool TlsClientHandshaker::SetAlpn() { return true; } - QUIC_BUG << "ALPN missing"; + QUIC_BUG(quic_bug_10576_4) << "ALPN missing"; return false; } if (!std::all_of(alpns.begin(), alpns.end(), IsValidAlpn)) { - QUIC_BUG << "ALPN too long"; + QUIC_BUG(quic_bug_10576_5) << "ALPN too long"; return false; } @@ -170,19 +171,24 @@ bool TlsClientHandshaker::SetAlpn() { success = success && (SSL_set_alpn_protos(ssl(), alpn, alpn_writer.length()) == 0); if (!success) { - QUIC_BUG << "Failed to set ALPN: " - << quiche::QuicheTextUtils::HexDump(absl::string_view( - alpn_writer.data(), alpn_writer.length())); + QUIC_BUG(quic_bug_10576_6) + << "Failed to set ALPN: " + << quiche::QuicheTextUtils::HexDump( + absl::string_view(alpn_writer.data(), alpn_writer.length())); return false; } - // Enable ALPS. + // Enable ALPS only for versions that use HTTP/3 frames. if (enable_alps_) { for (const std::string& alpn_string : alpns) { + ParsedQuicVersion version = ParseQuicVersionString(alpn_string); + if (!version.IsKnown() || !version.UsesHttp3()) { + continue; + } if (SSL_add_application_settings( ssl(), reinterpret_cast<const uint8_t*>(alpn_string.data()), alpn_string.size(), nullptr, /* settings_len = */ 0) != 1) { - QUIC_BUG << "Failed to enable ALPS."; + QUIC_BUG(quic_bug_10576_7) << "Failed to enable ALPS."; return false; } } @@ -281,12 +287,12 @@ int TlsClientHandshaker::num_sent_client_hellos() const { } bool TlsClientHandshaker::IsResumption() const { - QUIC_BUG_IF(!one_rtt_keys_available()); + QUIC_BUG_IF(quic_bug_12736_1, !one_rtt_keys_available()); return SSL_session_reused(ssl()) == 1; } bool TlsClientHandshaker::EarlyDataAccepted() const { - QUIC_BUG_IF(!one_rtt_keys_available()); + QUIC_BUG_IF(quic_bug_12736_2, !one_rtt_keys_available()); return SSL_early_data_accepted(ssl()) == 1; } @@ -295,7 +301,7 @@ ssl_early_data_reason_t TlsClientHandshaker::EarlyDataReason() const { } bool TlsClientHandshaker::ReceivedInchoateReject() const { - QUIC_BUG_IF(!one_rtt_keys_available()); + QUIC_BUG_IF(quic_bug_12736_3, !one_rtt_keys_available()); // REJ messages are a QUIC crypto feature, so TLS always returns false. return false; } @@ -440,25 +446,23 @@ void TlsClientHandshaker::OnProofVerifyDetailsAvailable( } void TlsClientHandshaker::FinishHandshake() { - // Fill crypto_negotiated_params_: - const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl()); - if (cipher) { - crypto_negotiated_params_->cipher_suite = SSL_CIPHER_get_value(cipher); - } - crypto_negotiated_params_->key_exchange_group = SSL_get_curve_id(ssl()); - crypto_negotiated_params_->peer_signature_algorithm = - SSL_get_peer_signature_algorithm(ssl()); - if (SSL_in_early_data(ssl())) { - // SSL_do_handshake returns after sending the ClientHello if the session is - // 0-RTT-capable, which means that FinishHandshake will get called twice - - // the first time after sending the ClientHello, and the second time after - // the handshake is complete. If we're in the first time FinishHandshake is - // called, we can't do any end-of-handshake processing. - - // If we're attempting a 0-RTT handshake, then we need to let the transport - // and application know what state to apply to early data. - PrepareZeroRttConfig(cached_state_.get()); - return; + FillNegotiatedParams(); + + if (retry_handshake_on_early_data_) { + QUICHE_CHECK(!SSL_in_early_data(ssl())); + } else { + if (SSL_in_early_data(ssl())) { + // SSL_do_handshake returns after sending the ClientHello if the session + // is 0-RTT-capable, which means that FinishHandshake will get called + // twice - the first time after sending the ClientHello, and the second + // time after the handshake is complete. If we're in the first time + // FinishHandshake is called, we can't do any end-of-handshake processing. + + // If we're attempting a 0-RTT handshake, then we need to let the + // transport and application know what state to apply to early data. + PrepareZeroRttConfig(cached_state_.get()); + return; + } } QUIC_LOG(INFO) << "Client: handshake finished"; @@ -505,6 +509,8 @@ void TlsClientHandshaker::FinishHandshake() { if (alps_length > 0) { auto error = session()->OnAlpsData(alps_data, alps_length); if (error) { + // Calling CloseConnection() is safe even in case OnAlpsData() has + // already closed the connection. CloseConnection( QUIC_HANDSHAKE_FAILED, absl::StrCat("Error processing ALPS data: ", error.value())); @@ -517,6 +523,30 @@ void TlsClientHandshaker::FinishHandshake() { handshaker_delegate()->OnTlsHandshakeComplete(); } +void TlsClientHandshaker::OnEnterEarlyData() { + QUICHE_DCHECK(retry_handshake_on_early_data_); + QUICHE_DCHECK(SSL_in_early_data(ssl())); + + // TODO(wub): It might be unnecessary to FillNegotiatedParams() at this time, + // because we fill it again when handshake completes. + FillNegotiatedParams(); + + // If we're attempting a 0-RTT handshake, then we need to let the transport + // and application know what state to apply to early data. + PrepareZeroRttConfig(cached_state_.get()); +} + +void TlsClientHandshaker::FillNegotiatedParams() { + const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl()); + if (cipher) { + crypto_negotiated_params_->cipher_suite = + SSL_CIPHER_get_protocol_id(cipher); + } + crypto_negotiated_params_->key_exchange_group = SSL_get_curve_id(ssl()); + crypto_negotiated_params_->peer_signature_algorithm = + SSL_get_peer_signature_algorithm(ssl()); +} + void TlsClientHandshaker::ProcessPostHandshakeMessage() { int rv = SSL_process_quic_post_handshake(ssl()); if (rv != 1) { @@ -546,7 +576,7 @@ void TlsClientHandshaker::HandleZeroRttReject() { void TlsClientHandshaker::InsertSession(bssl::UniquePtr<SSL_SESSION> session) { if (!received_transport_params_) { - QUIC_BUG << "Transport parameters isn't received"; + QUIC_BUG(quic_bug_10576_8) << "Transport parameters isn't received"; return; } if (session_cache_ == nullptr) { 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 42d587fdbcc..8fb6da69d2c 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 @@ -90,6 +90,8 @@ class QUIC_EXPORT_PRIVATE TlsClientHandshaker } void FinishHandshake() override; + void OnEnterEarlyData() override; + void FillNegotiatedParams(); void ProcessPostHandshakeMessage() override; bool ShouldCloseConnectionOnUnexpectedError(int ssl_error) override; QuicAsyncStatus VerifyCertChain( diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker_test.cc b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker_test.cc index 19fdab5db04..e498acb9a43 100644 --- a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker_test.cc +++ b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker_test.cc @@ -178,7 +178,6 @@ class TlsClientHandshakerTest : public QuicTestWithParam<ParsedQuicVersion> { server_id_(kServerHostname, kServerPort, false), server_compressed_certs_cache_( QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) { - SetQuicRestartFlag(quic_enable_zero_rtt_for_tls_v2, true); crypto_config_ = std::make_unique<QuicCryptoClientConfig>( std::make_unique<TestProofVerifier>(), std::make_unique<test::SimpleSessionCache>()); @@ -424,6 +423,62 @@ TEST_P(TlsClientHandshakerTest, ZeroRttResumption) { EXPECT_EQ(stream()->EarlyDataReason(), ssl_early_data_accepted); } +// Regression test for b/186438140. +TEST_P(TlsClientHandshakerTest, ZeroRttResumptionWithAyncProofVerifier) { + // Finish establishing the first connection, so the second connection can + // resume. + CompleteCryptoHandshake(); + + EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); + EXPECT_TRUE(stream()->encryption_established()); + EXPECT_TRUE(stream()->one_rtt_keys_available()); + EXPECT_FALSE(stream()->IsResumption()); + + // Create a second connection. + CreateConnection(); + InitializeFakeServer(); + EXPECT_CALL(*session_, OnConfigNegotiated()); + EXPECT_CALL(*connection_, SendCryptoData(_, _, _)) + .Times(testing::AnyNumber()); + // Enable TestProofVerifier to capture the call to VerifyCertChain and run it + // asynchronously. + TestProofVerifier* proof_verifier = + static_cast<TestProofVerifier*>(crypto_config_->proof_verifier()); + proof_verifier->Activate(); + // Start the second handshake. + stream()->CryptoConnect(); + + ASSERT_EQ(proof_verifier->NumPendingCallbacks(), 1u); + + // Advance the handshake with the server. Since cert verification has not + // finished yet, client cannot derive HANDSHAKE and 1-RTT keys. + crypto_test_utils::AdvanceHandshake(connection_, stream(), 0, + server_connection_, server_stream(), 0); + + EXPECT_FALSE(stream()->one_rtt_keys_available()); + EXPECT_FALSE(server_stream()->one_rtt_keys_available()); + + // Finish cert verification after receiving packets from server. + proof_verifier->InvokePendingCallback(0); + + QuicFramer* framer = QuicConnectionPeer::GetFramer(connection_); + if (!GetQuicReloadableFlag(quic_tls_retry_handshake_on_early_data)) { + // Client does not have HANDSHAKE key due to b/186438140. + EXPECT_EQ(nullptr, + QuicFramerPeer::GetEncrypter(framer, ENCRYPTION_HANDSHAKE)); + return; + } + + // Verify client has derived HANDSHAKE key. + EXPECT_NE(nullptr, + QuicFramerPeer::GetEncrypter(framer, ENCRYPTION_HANDSHAKE)); + + // Ideally, we should also verify that the process_undecryptable_packets_alarm + // is set and processing the undecryptable packets can advance the handshake + // to completion. Unfortunately, the test facilities used in this test does + // not support queuing and processing undecryptable packets. +} + TEST_P(TlsClientHandshakerTest, ZeroRttRejection) { // Finish establishing the first connection: CompleteCryptoHandshake(); 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 133c85c4a27..bc8e0b48d4d 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 @@ -12,9 +12,12 @@ #include "quic/core/quic_crypto_stream.h" #include "quic/core/tls_client_handshaker.h" #include "quic/platform/api/quic_bug_tracker.h" +#include "quic/platform/api/quic_stack_trace.h" namespace quic { +#define ENDPOINT (SSL_is_server(ssl()) ? "TlsServer: " : "TlsClient: ") + TlsHandshaker::ProofVerifierCallbackImpl::ProofVerifierCallbackImpl( TlsHandshaker* parent) : parent_(parent) {} @@ -93,8 +96,38 @@ void TlsHandshaker::AdvanceHandshake() { return; } - QUIC_VLOG(1) << "TlsHandshaker: continuing handshake"; + QUIC_VLOG(1) << ENDPOINT << "Continuing handshake"; int rv = SSL_do_handshake(ssl()); + + // If SSL_do_handshake return success(1) and we are in early data, it is + // possible that we have provided ServerHello to BoringSSL but it hasn't been + // processed. Retry SSL_do_handshake once will advance the handshake more in + // that case. If there are no unprocessed ServerHello, the retry will return a + // non-positive number. + if (retry_handshake_on_early_data_ && rv == 1 && SSL_in_early_data(ssl())) { + OnEnterEarlyData(); + rv = SSL_do_handshake(ssl()); + QUIC_VLOG(1) << ENDPOINT + << "SSL_do_handshake returned when entering early data. After " + << "retry, rv=" << rv + << ", SSL_in_early_data=" << SSL_in_early_data(ssl()); + // The retry should either + // - Return <= 0 if the handshake is still pending, likely still in early + // data. + // - Return 1 if the handshake has _actually_ finished. i.e. + // SSL_in_early_data should be false. + // + // In either case, it should not both return 1 and stay in early data. + if (rv == 1 && SSL_in_early_data(ssl()) && !is_connection_closed_) { + QUIC_BUG(quic_handshaker_stay_in_early_data) + << "The original and the retry of SSL_do_handshake both returned " + "success and in early data"; + CloseConnection(QUIC_HANDSHAKE_FAILED, + "TLS handshake failed: Still in early data after retry"); + return; + } + } + if (rv == 1) { FinishHandshake(); return; @@ -200,7 +233,7 @@ enum ssl_verify_result_t TlsHandshaker::VerifyCert(uint8_t* out_alert) { void TlsHandshaker::SetWriteSecret(EncryptionLevel level, const SSL_CIPHER* cipher, const std::vector<uint8_t>& write_secret) { - QUIC_DVLOG(1) << "SetWriteSecret level=" << level; + QUIC_DVLOG(1) << ENDPOINT << "SetWriteSecret level=" << level; std::unique_ptr<QuicEncrypter> encrypter = QuicEncrypter::CreateFromCipherSuite(SSL_CIPHER_get_id(cipher)); const EVP_MD* prf = Prf(cipher); @@ -223,7 +256,7 @@ void TlsHandshaker::SetWriteSecret(EncryptionLevel level, bool TlsHandshaker::SetReadSecret(EncryptionLevel level, const SSL_CIPHER* cipher, const std::vector<uint8_t>& read_secret) { - QUIC_DVLOG(1) << "SetReadSecret level=" << level; + QUIC_DVLOG(1) << ENDPOINT << "SetReadSecret level=" << level; std::unique_ptr<QuicDecrypter> decrypter = QuicDecrypter::CreateFromCipherSuite(SSL_CIPHER_get_id(cipher)); const EVP_MD* prf = Prf(cipher); @@ -251,7 +284,7 @@ TlsHandshaker::AdvanceKeysAndCreateCurrentOneRttDecrypter() { one_rtt_read_header_protection_key_.empty() || one_rtt_write_header_protection_key_.empty()) { std::string error_details = "1-RTT secret(s) not set yet."; - QUIC_BUG << error_details; + QUIC_BUG(quic_bug_10312_1) << error_details; CloseConnection(QUIC_INTERNAL_ERROR, error_details); return nullptr; } @@ -276,7 +309,7 @@ std::unique_ptr<QuicEncrypter> TlsHandshaker::CreateCurrentOneRttEncrypter() { if (latest_write_secret_.empty() || one_rtt_write_header_protection_key_.empty()) { std::string error_details = "1-RTT write secret not set yet."; - QUIC_BUG << error_details; + QUIC_BUG(quic_bug_10312_2) << error_details; CloseConnection(QUIC_INTERNAL_ERROR, error_details); return nullptr; } 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 4f8bf47d20f..6bf48b548c4 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 @@ -73,8 +73,20 @@ class QUIC_EXPORT_PRIVATE TlsHandshaker : public TlsConnection::Delegate, // finished. Note that due to 0-RTT, the handshake may "finish" twice; // |SSL_in_early_data| can be used to determine whether the handshake is truly // done. + // TODO(wub): When --quic_tls_retry_handshake_on_early_data is true, this + // function will only be called once when the handshake actually finishes. + // Update comment when deprecating the flag. virtual void FinishHandshake() = 0; + // Called when |SSL_do_handshake| returns 1 and the connection is in early + // data. In that case, |AdvanceHandshake| will call |OnEnterEarlyData| and + // retry |SSL_do_handshake| once. + virtual void OnEnterEarlyData() { + // By default, do nothing but check the preconditions. + QUICHE_DCHECK(retry_handshake_on_early_data_); + QUICHE_DCHECK(SSL_in_early_data(ssl())); + } + // Called when a handshake message is received after the handshake is // complete. virtual void ProcessPostHandshakeMessage() = 0; @@ -155,6 +167,9 @@ class QUIC_EXPORT_PRIVATE TlsHandshaker : public TlsConnection::Delegate, // error code corresponding to the TLS alert description |desc|. void SendAlert(EncryptionLevel level, uint8_t desc) override; + const bool retry_handshake_on_early_data_ = + GetQuicReloadableFlag(quic_tls_retry_handshake_on_early_data); + private: // ProofVerifierCallbackImpl handles the result of an asynchronous certificate // verification operation. 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 81c7559489b..b628db1744a 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 @@ -21,8 +21,17 @@ #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_hostname_utils.h" #include "quic/platform/api/quic_logging.h" +#include "quic/platform/api/quic_server_stats.h" #include "common/platform/api/quiche_text_utils.h" +#define RECORD_LATENCY_IN_US(stat_name, latency, comment) \ + do { \ + const int64_t latency_in_us = (latency).ToMicroseconds(); \ + QUIC_DVLOG(1) << "Recording " stat_name ": " << latency_in_us; \ + QUIC_SERVER_HISTOGRAM_COUNTS(stat_name, latency_in_us, 1, 10000000, 50, \ + comment); \ + } while (0) + namespace quic { TlsServerHandshaker::DefaultProofSourceHandle::DefaultProofSourceHandle( @@ -55,7 +64,8 @@ TlsServerHandshaker::DefaultProofSourceHandle::SelectCertificate( const std::vector<uint8_t>& /*quic_transport_params*/, const absl::optional<std::vector<uint8_t>>& /*early_data_context*/) { if (!handshaker_ || !proof_source_) { - QUIC_BUG << "SelectCertificate called on a detached handle"; + QUIC_BUG(quic_bug_10341_1) + << "SelectCertificate called on a detached handle"; return QUIC_FAILURE; } @@ -65,7 +75,7 @@ TlsServerHandshaker::DefaultProofSourceHandle::SelectCertificate( handshaker_->OnSelectCertificateDone( /*ok=*/true, /*is_sync=*/true, chain.get()); if (!handshaker_->select_cert_status().has_value()) { - QUIC_BUG + QUIC_BUG(quic_bug_12423_1) << "select_cert_status() has no value after a synchronous select cert"; // Return success to continue the handshake. return QUIC_SUCCESS; @@ -81,12 +91,13 @@ QuicAsyncStatus TlsServerHandshaker::DefaultProofSourceHandle::ComputeSignature( absl::string_view in, size_t max_signature_size) { if (!handshaker_ || !proof_source_) { - QUIC_BUG << "ComputeSignature called on a detached handle"; + QUIC_BUG(quic_bug_10341_2) + << "ComputeSignature called on a detached handle"; return QUIC_FAILURE; } if (signature_callback_) { - QUIC_BUG << "ComputeSignature called while pending"; + QUIC_BUG(quic_bug_10341_3) << "ComputeSignature called while pending"; return QUIC_FAILURE; } @@ -489,7 +500,8 @@ TlsServerHandshaker::SetTransportParameters() { std::vector<uint8_t> early_data_context; if (!SerializeTransportParametersForTicket( server_params, *application_state_, &early_data_context)) { - QUIC_BUG << "Failed to serialize Transport Parameters for ticket."; + QUIC_BUG(quic_bug_10341_4) + << "Failed to serialize Transport Parameters for ticket."; result.early_data_context = std::vector<uint8_t>(); return result; } @@ -514,7 +526,8 @@ void TlsServerHandshaker::SetWriteSecret( // Fill crypto_negotiated_params_: const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl()); if (cipher) { - crypto_negotiated_params_->cipher_suite = SSL_CIPHER_get_value(cipher); + crypto_negotiated_params_->cipher_suite = + SSL_CIPHER_get_protocol_id(cipher); } crypto_negotiated_params_->key_exchange_group = SSL_get_curve_id(ssl()); } @@ -527,16 +540,19 @@ std::string TlsServerHandshaker::GetAcceptChValueForOrigin( } void TlsServerHandshaker::FinishHandshake() { - if (SSL_in_early_data(ssl())) { - // If the server accepts early data, SSL_do_handshake returns success twice: - // once after processing the ClientHello and sending the server's first - // flight, and then again after the handshake is complete. This results in - // FinishHandshake getting called twice. On the first call to - // FinishHandshake, we don't have any confirmation that the client is live, - // so all end of handshake processing is deferred until the handshake is - // actually complete. - QUIC_RESTART_FLAG_COUNT(quic_enable_zero_rtt_for_tls_v2); - return; + if (retry_handshake_on_early_data_) { + QUICHE_DCHECK(!SSL_in_early_data(ssl())); + } else { + if (SSL_in_early_data(ssl())) { + // If the server accepts early data, SSL_do_handshake returns success + // twice: once after processing the ClientHello and sending the server's + // first flight, and then again after the handshake is complete. This + // results in FinishHandshake getting called twice. On the first call to + // FinishHandshake, we don't have any confirmation that the client is + // live, so all end of handshake processing is deferred until the + // handshake is actually complete. + return; + } } if (!valid_alpn_received_) { QUIC_DLOG(ERROR) @@ -570,7 +586,8 @@ QuicAsyncStatus TlsServerHandshaker::VerifyCertChain( std::unique_ptr<ProofVerifyDetails>* /*details*/, uint8_t* /*out_alert*/, std::unique_ptr<ProofVerifierCallback> /*callback*/) { - QUIC_BUG << "Client certificates are not yet supported on the server"; + QUIC_BUG(quic_bug_10341_5) + << "Client certificates are not yet supported on the server"; return QUIC_FAILURE; } @@ -593,6 +610,12 @@ ssl_private_key_result_t TlsServerHandshaker::PrivateKeySign( sig_alg, in, max_out); if (status == QUIC_PENDING) { set_expected_ssl_error(SSL_ERROR_WANT_PRIVATE_KEY_OPERATION); + if (async_op_timer_.has_value()) { + QUIC_CODE_COUNT( + quic_tls_server_computing_signature_while_another_op_pending); + } + async_op_timer_ = QuicTimeAccumulator(); + async_op_timer_->Start(now()); } return PrivateKeyComplete(out, out_len, max_out); } @@ -616,7 +639,23 @@ ssl_private_key_result_t TlsServerHandshaker::PrivateKeyComplete( if (expected_ssl_error() == SSL_ERROR_WANT_PRIVATE_KEY_OPERATION) { return ssl_private_key_retry; } - if (!HasValidSignature(max_out)) { + + const bool success = HasValidSignature(max_out); + QuicConnectionStats::TlsServerOperationStats compute_signature_stats; + compute_signature_stats.success = success; + if (async_op_timer_.has_value()) { + async_op_timer_->Stop(now()); + compute_signature_stats.async_latency = + async_op_timer_->GetTotalElapsedTime(); + async_op_timer_.reset(); + RECORD_LATENCY_IN_US("tls_server_async_compute_signature_latency_us", + compute_signature_stats.async_latency, + "Async compute signature latency in microseconds"); + } + connection_stats().tls_server_compute_signature_stats = + std::move(compute_signature_stats); + + if (!success) { return ssl_private_key_failure; } *out_len = cert_verify_sig_.size(); @@ -665,7 +704,7 @@ int TlsServerHandshaker::SessionTicketSeal(uint8_t* out, QUICHE_DCHECK(proof_source_->GetTicketCrypter()); std::vector<uint8_t> ticket = proof_source_->GetTicketCrypter()->Encrypt(in); if (max_out_len < ticket.size()) { - QUIC_BUG + QUIC_BUG(quic_bug_12423_2) << "TicketCrypter returned " << ticket.size() << " bytes of ciphertext, which is larger than its max overhead of " << max_out_len; @@ -673,6 +712,7 @@ int TlsServerHandshaker::SessionTicketSeal(uint8_t* out, } *out_len = ticket.size(); memcpy(out, ticket.data(), ticket.size()); + QUIC_CODE_COUNT(quic_tls_server_handshaker_tickets_sealed); return 1; // success } @@ -693,19 +733,50 @@ ssl_ticket_aead_result_t TlsServerHandshaker::SessionTicketOpen( // returning ssl_ticket_aead_retry, we should continue processing to return // the decrypted ticket. // - // If the callback is not run asynchronously, return ssl_ticket_aead_retry + // 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_) { set_expected_ssl_error(SSL_ERROR_PENDING_TICKET); + if (async_op_timer_.has_value()) { + QUIC_CODE_COUNT( + quic_tls_server_decrypting_ticket_while_another_op_pending); + } + async_op_timer_ = QuicTimeAccumulator(); + async_op_timer_->Start(now()); return ssl_ticket_aead_retry; } } + + ssl_ticket_aead_result_t result = + FinalizeSessionTicketOpen(out, out_len, max_out_len); + + QuicConnectionStats::TlsServerOperationStats decrypt_ticket_stats; + decrypt_ticket_stats.success = (result == ssl_ticket_aead_success); + if (async_op_timer_.has_value()) { + async_op_timer_->Stop(now()); + decrypt_ticket_stats.async_latency = async_op_timer_->GetTotalElapsedTime(); + async_op_timer_.reset(); + RECORD_LATENCY_IN_US("tls_server_async_decrypt_ticket_latency_us", + decrypt_ticket_stats.async_latency, + "Async decrypt ticket latency in microseconds"); + } + connection_stats().tls_server_decrypt_ticket_stats = + std::move(decrypt_ticket_stats); + + return result; +} + +ssl_ticket_aead_result_t TlsServerHandshaker::FinalizeSessionTicketOpen( + uint8_t* out, + size_t* out_len, + size_t max_out_len) { ticket_decryption_callback_ = nullptr; set_expected_ssl_error(SSL_ERROR_WANT_READ); if (decrypted_session_ticket_.empty()) { QUIC_DLOG(ERROR) << "Session ticket decryption failed; ignoring ticket"; // Ticket decryption failed. Ignore the ticket. + QUIC_CODE_COUNT(quic_tls_server_handshaker_tickets_ignored); return ssl_ticket_aead_ignore_ticket; } if (max_out_len < decrypted_session_ticket_.size()) { @@ -715,6 +786,7 @@ ssl_ticket_aead_result_t TlsServerHandshaker::SessionTicketOpen( decrypted_session_ticket_.size()); *out_len = decrypted_session_ticket_.size(); + QUIC_CODE_COUNT(quic_tls_server_handshaker_tickets_opened); return ssl_ticket_aead_success; } @@ -743,7 +815,8 @@ ssl_select_cert_result_t TlsServerHandshaker::EarlySelectCertCallback( if (!pre_shared_key_.empty()) { // TODO(b/154162689) add PSK support to QUIC+TLS. - QUIC_BUG << "QUIC server pre-shared keys not yet supported with TLS"; + QUIC_BUG(quic_bug_10341_6) + << "QUIC server pre-shared keys not yet supported with TLS"; return ssl_select_cert_error; } @@ -799,6 +872,12 @@ ssl_select_cert_result_t TlsServerHandshaker::EarlySelectCertCallback( if (status == QUIC_PENDING) { set_expected_ssl_error(SSL_ERROR_PENDING_CERTIFICATE); + if (async_op_timer_.has_value()) { + QUIC_CODE_COUNT( + quic_tls_server_selecting_cert_while_another_op_pending); + } + async_op_timer_ = QuicTimeAccumulator(); + async_op_timer_->Start(now()); return ssl_select_cert_retry; } @@ -857,6 +936,21 @@ void TlsServerHandshaker::OnSelectCertificateDone( QUIC_LOG(ERROR) << "No certs provided for host '" << hostname_ << "'"; } } + + QuicConnectionStats::TlsServerOperationStats select_cert_stats; + select_cert_stats.success = (select_cert_status_ == QUIC_SUCCESS); + QUICHE_DCHECK_NE(is_sync, async_op_timer_.has_value()); + if (async_op_timer_.has_value()) { + async_op_timer_->Stop(now()); + select_cert_stats.async_latency = async_op_timer_->GetTotalElapsedTime(); + async_op_timer_.reset(); + RECORD_LATENCY_IN_US("tls_server_async_select_cert_latency_us", + select_cert_stats.async_latency, + "Async select cert latency in microseconds"); + } + connection_stats().tls_server_select_cert_stats = + std::move(select_cert_stats); + const int last_expected_ssl_error = expected_ssl_error(); set_expected_ssl_error(SSL_ERROR_WANT_READ); if (!is_sync) { @@ -928,10 +1022,18 @@ int TlsServerHandshaker::SelectAlpn(const uint8_t** out, size_t alps_length = 0; std::unique_ptr<char[]> buffer; - const std::string& origin = crypto_negotiated_params_->sni; - std::string accept_ch_value = GetAcceptChValueForOrigin(origin); + const std::string& hostname = crypto_negotiated_params_->sni; + std::string accept_ch_value = GetAcceptChValueForOrigin(hostname); + + std::string origin; + if (GetQuicReloadableFlag(quic_alps_include_scheme_in_origin)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_alps_include_scheme_in_origin); + origin = "https://"; + } + origin.append(crypto_negotiated_params_->sni); + if (!accept_ch_value.empty()) { - AcceptChFrame frame{{{origin, std::move(accept_ch_value)}}}; + AcceptChFrame frame{{{std::move(origin), std::move(accept_ch_value)}}}; alps_length = HttpEncoder::SerializeAcceptChFrame(frame, &buffer); alps_data = reinterpret_cast<const uint8_t*>(buffer.get()); } 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 16707cd2938..7850d3519a8 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 @@ -15,6 +15,7 @@ #include "quic/core/proto/cached_network_parameters_proto.h" #include "quic/core/quic_crypto_server_stream_base.h" #include "quic/core/quic_crypto_stream.h" +#include "quic/core/quic_time_accumulator.h" #include "quic/core/quic_types.h" #include "quic/core/tls_handshaker.h" #include "quic/platform/api/quic_export.h" @@ -155,6 +156,11 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker size_t* out_len, size_t max_out_len, absl::string_view in) override; + // Called when ticket_decryption_callback_ is done to determine a final + // decryption result. + ssl_ticket_aead_result_t FinalizeSessionTicketOpen(uint8_t* out, + size_t* out_len, + size_t max_out_len); TlsConnection::Delegate* ConnectionDelegate() override { return this; } // The status of cert selection. nullopt means it hasn't started. @@ -296,6 +302,11 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker bool ProcessTransportParameters(const SSL_CLIENT_HELLO* client_hello, std::string* error_details); + QuicConnectionStats& connection_stats() { + return session()->connection()->mutable_stats(); + } + QuicTime now() const { return session()->GetClock()->Now(); } + std::unique_ptr<ProofSourceHandle> proof_source_handle_; ProofSource* proof_source_; SignatureCallback* signature_callback_ = nullptr; @@ -321,6 +332,9 @@ class QUIC_EXPORT_PRIVATE TlsServerHandshaker std::string cert_verify_sig_; std::unique_ptr<ProofSource::Details> proof_source_details_; + // Count the duration of the current async operation, if any. + absl::optional<QuicTimeAccumulator> async_op_timer_; + std::unique_ptr<ApplicationState> application_state_; // Pre-shared key used during the handshake. 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 66cdd7984d3..fbec2551227 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 @@ -138,7 +138,6 @@ class TlsServerHandshakerTest : public QuicTestWithParam<TestParams> { QuicCompressedCertsCache::kQuicCompressedCertsCacheSize), server_id_(kServerHostname, kServerPort, false), supported_versions_({GetParam().version}) { - SetQuicRestartFlag(quic_enable_zero_rtt_for_tls_v2, true); SetQuicFlag(FLAGS_quic_disable_server_tls_resumption, GetParam().disable_resumption); client_crypto_config_ = std::make_unique<QuicCryptoClientConfig>( diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.cc b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.cc index 7235edf192e..5ed0e08777a 100644 --- a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.cc +++ b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.cc @@ -108,12 +108,14 @@ void UberReceivedPacketManager::ResetAckStates( void UberReceivedPacketManager::EnableMultiplePacketNumberSpacesSupport( Perspective perspective) { if (supports_multiple_packet_number_spaces_) { - QUIC_BUG << "Multiple packet number spaces has already been enabled"; + QUIC_BUG(quic_bug_10495_1) + << "Multiple packet number spaces has already been enabled"; return; } if (received_packet_managers_[0].GetLargestObserved().IsInitialized()) { - QUIC_BUG << "Try to enable multiple packet number spaces support after any " - "packet has been received."; + QUIC_BUG(quic_bug_10495_2) + << "Try to enable multiple packet number spaces support after any " + "packet has been received."; return; } // In IETF QUIC, the peer is expected to acknowledge packets in Initial and @@ -236,8 +238,9 @@ void UberReceivedPacketManager::set_save_timestamps(bool save_timestamps) { void UberReceivedPacketManager::OnAckFrequencyFrame( const QuicAckFrequencyFrame& frame) { if (!supports_multiple_packet_number_spaces_) { - QUIC_BUG << "Received AckFrequencyFrame when multiple packet number spaces " - "is not supported"; + QUIC_BUG(quic_bug_10495_3) + << "Received AckFrequencyFrame when multiple packet number spaces " + "is not supported"; return; } received_packet_managers_[APPLICATION_DATA].OnAckFrequencyFrame(frame); 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 new file mode 100644 index 00000000000..154d242b1e8 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/web_transport_interface.h @@ -0,0 +1,130 @@ +// 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. + +// This header contains interfaces that abstract away different backing +// protocols for WebTransport. + +#ifndef QUICHE_QUIC_CORE_WEB_TRANSPORT_INTERFACE_H_ +#define QUICHE_QUIC_CORE_WEB_TRANSPORT_INTERFACE_H_ + +#include <cstddef> +#include <memory> + +#include "absl/base/attributes.h" +#include "absl/strings/string_view.h" +#include "quic/core/quic_datagram_queue.h" +#include "quic/core/quic_types.h" +#include "quic/platform/api/quic_export.h" + +namespace quic { + +// Visitor that gets notified about events related to a WebTransport stream. +class QUIC_EXPORT_PRIVATE WebTransportStreamVisitor { + public: + virtual ~WebTransportStreamVisitor() {} + + // Called whenever the stream has readable data available. + virtual void OnCanRead() = 0; + // Called whenever the stream is not write-blocked and can accept new data. + virtual void OnCanWrite() = 0; +}; + +// A stream (either bidirectional or unidirectional) that is contained within a +// WebTransport session. +class QUIC_EXPORT_PRIVATE WebTransportStream { + public: + struct QUIC_EXPORT_PRIVATE ReadResult { + // Number of bytes actually read. + size_t bytes_read; + // Whether the FIN has been received; if true, no further data will arrive + // on the stream, and the stream object can be soon potentially garbage + // collected. + bool fin; + }; + + virtual ~WebTransportStream() {} + + // Reads at most |buffer_size| bytes into |buffer|. + ABSL_MUST_USE_RESULT virtual ReadResult Read(char* buffer, + size_t buffer_size) = 0; + // Reads all available data and appends it to the end of |output|. + ABSL_MUST_USE_RESULT virtual ReadResult Read(std::string* output) = 0; + // Writes |data| into the stream. Returns true on success. + ABSL_MUST_USE_RESULT virtual bool Write(absl::string_view data) = 0; + // Sends the FIN on the stream. Returns true on success. + ABSL_MUST_USE_RESULT virtual bool SendFin() = 0; + + // Indicates whether it is possible to write into stream right now. + virtual bool CanWrite() const = 0; + // Indicates the number of bytes that can be read from the stream. + virtual size_t ReadableBytes() const = 0; + + // An ID that is unique within the session. Those are not exposed to the user + // via the web API, but can be used internally for bookkeeping and + // diagnostics. + 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 ResetDueToInternalError() = 0; + // Called when the owning object has been garbage-collected. + virtual void MaybeResetDueToStreamObjectGone() = 0; + + virtual WebTransportStreamVisitor* visitor() = 0; + virtual void SetVisitor( + std::unique_ptr<WebTransportStreamVisitor> visitor) = 0; +}; + +// Visitor that gets notified about events related to a WebTransport session. +class QUIC_EXPORT_PRIVATE WebTransportVisitor { + public: + virtual ~WebTransportVisitor() {} + + // Notifies the visitor when the session is ready to exchange application + // data. + virtual void OnSessionReady() = 0; + + // Notifies the visitor when a new stream has been received. The stream in + // question can be retrieved using AcceptIncomingBidirectionalStream() or + // AcceptIncomingUnidirectionalStream(). + virtual void OnIncomingBidirectionalStreamAvailable() = 0; + virtual void OnIncomingUnidirectionalStreamAvailable() = 0; + + // Notifies the visitor when a new datagram has been received. + virtual void OnDatagramReceived(absl::string_view datagram) = 0; + + // Notifies the visitor that a new outgoing stream can now be created. + virtual void OnCanCreateNewOutgoingBidirectionalStream() = 0; + virtual void OnCanCreateNewOutgoingUnidirectionalStream() = 0; +}; + +// An abstract interface for a WebTransport session. +class QUIC_EXPORT_PRIVATE WebTransportSession { + public: + virtual ~WebTransportSession() {} + + // 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. + virtual WebTransportStream* AcceptIncomingBidirectionalStream() = 0; + virtual WebTransportStream* AcceptIncomingUnidirectionalStream() = 0; + + // Returns true if flow control allows opening a new stream. + virtual bool CanOpenNextOutgoingBidirectionalStream() = 0; + virtual bool CanOpenNextOutgoingUnidirectionalStream() = 0; + // Opens a new WebTransport stream, or returns nullptr if that is not possible + // due to flow control. + virtual WebTransportStream* OpenOutgoingBidirectionalStream() = 0; + virtual WebTransportStream* OpenOutgoingUnidirectionalStream() = 0; + + virtual MessageStatus SendOrQueueDatagram(QuicMemSlice datagram) = 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; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_WEB_TRANSPORT_INTERFACE_H_ 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/web_transport_stream_adapter.cc new file mode 100644 index 00000000000..78227049dbd --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/web_transport_stream_adapter.cc @@ -0,0 +1,116 @@ +// 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/web_transport_stream_adapter.h" + +namespace quic { + +WebTransportStreamAdapter::WebTransportStreamAdapter( + QuicSession* session, + QuicStream* stream, + QuicStreamSequencer* sequencer) + : session_(session), stream_(stream), sequencer_(sequencer) {} + +WebTransportStream::ReadResult WebTransportStreamAdapter::Read( + char* buffer, + size_t buffer_size) { + iovec iov; + iov.iov_base = buffer; + iov.iov_len = buffer_size; + const size_t result = sequencer_->Readv(&iov, 1); + if (!fin_read_ && sequencer_->IsClosed()) { + fin_read_ = true; + stream_->OnFinRead(); + } + return ReadResult{result, sequencer_->IsClosed()}; +} + +WebTransportStream::ReadResult WebTransportStreamAdapter::Read( + std::string* output) { + const size_t old_size = output->size(); + const size_t bytes_to_read = ReadableBytes(); + output->resize(old_size + bytes_to_read); + ReadResult result = Read(&(*output)[old_size], bytes_to_read); + QUICHE_DCHECK_EQ(bytes_to_read, result.bytes_read); + output->resize(old_size + result.bytes_read); + return result; +} + +bool WebTransportStreamAdapter::Write(absl::string_view data) { + if (!CanWrite()) { + return false; + } + + QuicUniqueBufferPtr buffer = MakeUniqueBuffer( + session_->connection()->helper()->GetStreamSendBufferAllocator(), + data.size()); + memcpy(buffer.get(), data.data(), data.size()); + QuicMemSlice memslice(std::move(buffer), data.size()); + QuicConsumedData consumed = + stream_->WriteMemSlices(QuicMemSliceSpan(&memslice), /*fin=*/false); + + if (consumed.bytes_consumed == data.size()) { + return true; + } + if (consumed.bytes_consumed == 0) { + return false; + } + // WebTransportStream::Write() is an all-or-nothing write API. To achieve + // that property, it relies on WriteMemSlices() being an all-or-nothing API. + // If WriteMemSlices() fails to provide that guarantee, we have no way to + // communicate a partial write to the caller, and thus it's safer to just + // close the connection. + QUIC_BUG(WebTransportStreamAdapter partial write) + << "WriteMemSlices() unexpectedly partially consumed the input " + "data, provided: " + << data.size() << ", written: " << consumed.bytes_consumed; + stream_->OnUnrecoverableError( + QUIC_INTERNAL_ERROR, + "WriteMemSlices() unexpectedly partially consumed the input data"); + return false; +} + +bool WebTransportStreamAdapter::SendFin() { + if (!CanWrite()) { + return false; + } + + QuicMemSlice empty; + QuicConsumedData consumed = + stream_->WriteMemSlices(QuicMemSliceSpan(&empty), /*fin=*/true); + QUICHE_DCHECK_EQ(consumed.bytes_consumed, 0u); + return consumed.fin_consumed; +} + +bool WebTransportStreamAdapter::CanWrite() const { + return stream_->CanWriteNewData() && !stream_->write_side_closed(); +} + +size_t WebTransportStreamAdapter::ReadableBytes() const { + return sequencer_->ReadableBytes(); +} + +void WebTransportStreamAdapter::OnDataAvailable() { + if (visitor_ == nullptr) { + return; + } + const bool fin_readable = sequencer_->IsClosed() && !fin_read_; + if (ReadableBytes() == 0 && !fin_readable) { + return; + } + visitor_->OnCanRead(); +} + +void WebTransportStreamAdapter::OnCanWriteNewData() { + // Ensure the origin check has been completed, as the stream can be notified + // about being writable before that. + if (!CanWrite()) { + return; + } + if (visitor_ != nullptr) { + visitor_->OnCanWrite(); + } +} + +} // 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/web_transport_stream_adapter.h new file mode 100644 index 00000000000..2e4704ba3e4 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/core/web_transport_stream_adapter.h @@ -0,0 +1,66 @@ +// 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_QUIC_CORE_WEB_TRANSPORT_STREAM_ADAPTER_H_ +#define QUICHE_QUIC_CORE_WEB_TRANSPORT_STREAM_ADAPTER_H_ + +#include "quic/core/quic_session.h" +#include "quic/core/quic_stream.h" +#include "quic/core/quic_stream_sequencer.h" +#include "quic/core/web_transport_interface.h" + +namespace quic { + +// Converts WebTransportStream API calls into QuicStream API calls. The users +// of this class can either subclass it, or wrap around it. +class QUIC_EXPORT_PRIVATE WebTransportStreamAdapter + : public WebTransportStream { + public: + WebTransportStreamAdapter(QuicSession* session, + QuicStream* stream, + QuicStreamSequencer* sequencer); + + // WebTransportStream implementation. + ABSL_MUST_USE_RESULT ReadResult Read(char* buffer, + size_t buffer_size) override; + ABSL_MUST_USE_RESULT ReadResult Read(std::string* output) override; + ABSL_MUST_USE_RESULT bool Write(absl::string_view data) override; + ABSL_MUST_USE_RESULT bool SendFin() override; + bool CanWrite() const override; + size_t ReadableBytes() const override; + void SetVisitor(std::unique_ptr<WebTransportStreamVisitor> visitor) override { + visitor_ = std::move(visitor); + } + QuicStreamId GetStreamId() const override { return stream_->id(); } + + void ResetWithUserCode(QuicRstStreamErrorCode error) override { + stream_->Reset(error); + } + void ResetDueToInternalError() override { + stream_->Reset(QUIC_STREAM_INTERNAL_ERROR); + } + void MaybeResetDueToStreamObjectGone() override { + if (stream_->write_side_closed() && stream_->read_side_closed()) { + return; + } + stream_->Reset(QUIC_STREAM_CANCELLED); + } + + WebTransportStreamVisitor* visitor() override { return visitor_.get(); } + + // Calls that need to be passed from the corresponding QuicStream methods. + void OnDataAvailable(); + void OnCanWriteNewData(); + + private: + QuicSession* session_; // Unowned. + QuicStream* stream_; // Unowned. + QuicStreamSequencer* sequencer_; // Unowned. + std::unique_ptr<WebTransportStreamVisitor> visitor_; + bool fin_read_ = false; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_WEB_TRANSPORT_STREAM_ADAPTER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_client_bin.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_client_bin.cc index 8e09a12b27c..4d0523f5177 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_client_bin.cc +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_client_bin.cc @@ -29,6 +29,12 @@ DEFINE_QUIC_COMMAND_LINE_FLAG(bool, false, "If true, don't verify the server certificate."); +DEFINE_QUIC_COMMAND_LINE_FLAG(std::string, + masque_mode, + "", + "Allows setting MASQUE mode, valid values are " + "open and legacy. Defaults to open."); + namespace quic { namespace { @@ -45,6 +51,8 @@ int RunMasqueClient(int argc, char* argv[]) { return 1; } + SetQuicReloadableFlag(quic_h3_datagram, true); + const bool disable_certificate_verification = GetQuicFlag(FLAGS_disable_certificate_verification); QuicEpollServer epoll_server; @@ -54,7 +62,8 @@ int RunMasqueClient(int argc, char* argv[]) { masque_url = QuicUrl(absl::StrCat("https://", urls[0]), "https"); } if (masque_url.host().empty()) { - QUIC_LOG(ERROR) << "Failed to parse MASQUE server address " << urls[0]; + std::cerr << "Failed to parse MASQUE server address \"" << urls[0] << "\"" + << std::endl; return 1; } std::unique_ptr<ProofVerifier> proof_verifier; @@ -63,15 +72,23 @@ int RunMasqueClient(int argc, char* argv[]) { } else { proof_verifier = CreateDefaultProofVerifier(masque_url.host()); } - std::unique_ptr<MasqueEpollClient> masque_client = - MasqueEpollClient::Create(masque_url.host(), masque_url.port(), - &epoll_server, std::move(proof_verifier)); + MasqueMode masque_mode = MasqueMode::kOpen; + std::string mode_string = GetQuicFlag(FLAGS_masque_mode); + if (mode_string == "legacy") { + masque_mode = MasqueMode::kLegacy; + } else if (!mode_string.empty() && mode_string != "open") { + std::cerr << "Invalid masque_mode \"" << mode_string << "\"" << std::endl; + return 1; + } + std::unique_ptr<MasqueEpollClient> masque_client = MasqueEpollClient::Create( + masque_url.host(), masque_url.port(), masque_mode, &epoll_server, + std::move(proof_verifier)); if (masque_client == nullptr) { return 1; } std::cerr << "MASQUE is connected " << masque_client->connection_id() - << std::endl; + << " in " << masque_mode << " mode" << std::endl; for (size_t i = 1; i < urls.size(); ++i) { if (!tools::SendEncapsulatedMasqueRequest( 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 eeedccfd0d5..8793d5319f2 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,10 +3,16 @@ // found in the LICENSE file. #include "quic/masque/masque_client_session.h" +#include "absl/algorithm/container.h" +#include "quic/core/http/spdy_utils.h" +#include "quic/core/quic_data_reader.h" +#include "quic/core/quic_utils.h" +#include "common/platform/api/quiche_text_utils.h" namespace quic { MasqueClientSession::MasqueClientSession( + MasqueMode masque_mode, const QuicConfig& config, const ParsedQuicVersionVector& supported_versions, QuicConnection* connection, @@ -20,36 +26,42 @@ MasqueClientSession::MasqueClientSession( server_id, crypto_config, push_promise_index), + masque_mode_(masque_mode), owner_(owner), compression_engine_(this) {} void MasqueClientSession::OnMessageReceived(absl::string_view message) { - QUIC_DVLOG(1) << "Received DATAGRAM frame of length " << message.length(); - - QuicConnectionId client_connection_id, server_connection_id; - QuicSocketAddress server_address; - std::vector<char> packet; - bool version_present; - if (!compression_engine_.DecompressDatagram( - message, &client_connection_id, &server_connection_id, - &server_address, &packet, &version_present)) { - return; - } + if (masque_mode_ == MasqueMode::kLegacy) { + QUIC_DVLOG(1) << "Received DATAGRAM frame of length " << message.length(); + QuicConnectionId client_connection_id, server_connection_id; + QuicSocketAddress target_server_address; + std::vector<char> packet; + bool version_present; + if (!compression_engine_.DecompressDatagram( + message, &client_connection_id, &server_connection_id, + &target_server_address, &packet, &version_present)) { + return; + } + + auto connection_id_registration = + client_connection_id_registrations_.find(client_connection_id); + if (connection_id_registration == + client_connection_id_registrations_.end()) { + QUIC_DLOG(ERROR) << "MasqueClientSession failed to dispatch " + << client_connection_id; + return; + } + EncapsulatedClientSession* encapsulated_client_session = + connection_id_registration->second; + encapsulated_client_session->ProcessPacket( + absl::string_view(packet.data(), packet.size()), target_server_address); - auto connection_id_registration = - client_connection_id_registrations_.find(client_connection_id); - if (connection_id_registration == client_connection_id_registrations_.end()) { - QUIC_DLOG(ERROR) << "MasqueClientSession failed to dispatch " - << client_connection_id; + QUIC_DVLOG(1) << "Sent " << packet.size() << " bytes to connection for " + << client_connection_id; return; } - EncapsulatedClientSession* encapsulated_client_session = - connection_id_registration->second; - encapsulated_client_session->ProcessPacket( - absl::string_view(packet.data(), packet.size()), server_address); - - QUIC_DVLOG(1) << "Sent " << packet.size() << " bytes to connection for " - << client_connection_id; + QUICHE_DCHECK_EQ(masque_mode_, MasqueMode::kOpen); + QuicSpdySession::OnMessageReceived(message); } void MasqueClientSession::OnMessageAcked(QuicMessageId message_id, @@ -61,12 +73,79 @@ void MasqueClientSession::OnMessageLost(QuicMessageId message_id) { QUIC_DVLOG(1) << "We believe DATAGRAM frame " << message_id << " was lost"; } -void MasqueClientSession::SendPacket(QuicConnectionId client_connection_id, - QuicConnectionId server_connection_id, - absl::string_view packet, - const QuicSocketAddress& server_address) { - compression_engine_.CompressAndSendPacket( - packet, client_connection_id, server_connection_id, server_address); +const MasqueClientSession::ConnectUdpClientState* +MasqueClientSession::GetOrCreateConnectUdpClientState( + const QuicSocketAddress& target_server_address, + EncapsulatedClientSession* encapsulated_client_session) { + for (const ConnectUdpClientState& client_state : connect_udp_client_states_) { + if (client_state.target_server_address() == target_server_address && + client_state.encapsulated_client_session() == + encapsulated_client_session) { + // Found existing CONNECT-UDP request. + return &client_state; + } + } + // No CONNECT-UDP request found, create a new one. + QuicSpdyClientStream* stream = CreateOutgoingBidirectionalStream(); + if (stream == nullptr) { + // Stream flow control limits prevented us from opening a new stream. + QUIC_DLOG(ERROR) << "Failed to open CONNECT-UDP stream"; + return nullptr; + } + + QuicDatagramFlowId flow_id = GetNextDatagramFlowId(); + + QUIC_DLOG(INFO) << "Sending CONNECT-UDP request for " << target_server_address + << " using flow ID " << flow_id << " on stream " + << stream->id(); + + // Send the request. + spdy::Http2HeaderBlock headers; + headers[":method"] = "CONNECT-UDP"; + headers[":scheme"] = "masque"; + headers[":path"] = "/"; + headers[":authority"] = target_server_address.ToString(); + SpdyUtils::AddDatagramFlowIdHeader(&headers, flow_id); + size_t bytes_sent = + stream->SendRequest(std::move(headers), /*body=*/"", /*fin=*/false); + if (bytes_sent == 0) { + QUIC_DLOG(ERROR) << "Failed to send CONNECT-UDP request"; + return nullptr; + } + + connect_udp_client_states_.push_back( + ConnectUdpClientState(stream, encapsulated_client_session, this, flow_id, + target_server_address)); + return &connect_udp_client_states_.back(); +} + +void MasqueClientSession::SendPacket( + QuicConnectionId client_connection_id, + QuicConnectionId server_connection_id, + absl::string_view packet, + const QuicSocketAddress& target_server_address, + EncapsulatedClientSession* encapsulated_client_session) { + if (masque_mode_ == MasqueMode::kLegacy) { + compression_engine_.CompressAndSendPacket(packet, client_connection_id, + server_connection_id, + target_server_address); + return; + } + const ConnectUdpClientState* connect_udp = GetOrCreateConnectUdpClientState( + target_server_address, encapsulated_client_session); + if (connect_udp == nullptr) { + QUIC_DLOG(ERROR) << "Failed to create CONNECT-UDP request"; + return; + } + + QuicDatagramFlowId flow_id = connect_udp->flow_id(); + MessageStatus message_status = + SendHttp3Datagram(connect_udp->flow_id(), packet); + + QUIC_DVLOG(1) << "Sent packet to " << target_server_address + << " compressed with flow ID " << flow_id + << " and got message status " + << MessageStatusToString(message_status); } void MasqueClientSession::RegisterConnectionId( @@ -84,14 +163,126 @@ void MasqueClientSession::RegisterConnectionId( } void MasqueClientSession::UnregisterConnectionId( - QuicConnectionId client_connection_id) { + QuicConnectionId client_connection_id, + EncapsulatedClientSession* encapsulated_client_session) { QUIC_DLOG(INFO) << "Unregistering " << client_connection_id; - if (client_connection_id_registrations_.find(client_connection_id) != - client_connection_id_registrations_.end()) { - client_connection_id_registrations_.erase(client_connection_id); - owner_->UnregisterClientConnectionId(client_connection_id); - compression_engine_.UnregisterClientConnectionId(client_connection_id); + if (masque_mode_ == MasqueMode::kLegacy) { + if (client_connection_id_registrations_.find(client_connection_id) != + client_connection_id_registrations_.end()) { + client_connection_id_registrations_.erase(client_connection_id); + owner_->UnregisterClientConnectionId(client_connection_id); + compression_engine_.UnregisterClientConnectionId(client_connection_id); + } + return; + } + + for (auto it = connect_udp_client_states_.begin(); + it != connect_udp_client_states_.end();) { + if (it->encapsulated_client_session() == encapsulated_client_session) { + QUIC_DLOG(INFO) << "Removing state for flow_id " << it->flow_id(); + auto* stream = it->stream(); + it = connect_udp_client_states_.erase(it); + if (!stream->write_side_closed()) { + stream->Reset(QUIC_STREAM_CANCELLED); + } + } else { + ++it; + } + } +} + +void MasqueClientSession::OnConnectionClosed( + const QuicConnectionCloseFrame& frame, + ConnectionCloseSource source) { + QuicSpdyClientSession::OnConnectionClosed(frame, source); + // Close all encapsulated sessions. + for (const auto& client_state : connect_udp_client_states_) { + client_state.encapsulated_client_session()->CloseConnection( + QUIC_CONNECTION_CANCELLED, "Underlying MASQUE connection was closed", + ConnectionCloseBehavior::SILENT_CLOSE); } } +void MasqueClientSession::OnStreamClosed(QuicStreamId stream_id) { + if (QuicUtils::IsBidirectionalStreamId(stream_id, version()) && + QuicUtils::IsClientInitiatedStreamId(transport_version(), stream_id)) { + QuicSpdyClientStream* stream = + reinterpret_cast<QuicSpdyClientStream*>(GetActiveStream(stream_id)); + if (stream != nullptr) { + QUIC_DLOG(INFO) << "Stream " << stream_id + << " closed, got response headers:" + << stream->response_headers().DebugString(); + } + } + for (auto it = connect_udp_client_states_.begin(); + it != connect_udp_client_states_.end();) { + if (it->stream()->id() == stream_id) { + QUIC_DLOG(INFO) << "Stream " << stream_id + << " was closed, removing state for flow_id " + << it->flow_id(); + auto* encapsulated_client_session = it->encapsulated_client_session(); + it = connect_udp_client_states_.erase(it); + encapsulated_client_session->CloseConnection( + QUIC_CONNECTION_CANCELLED, + "Underlying MASQUE CONNECT-UDP stream was closed", + ConnectionCloseBehavior::SILENT_CLOSE); + } else { + ++it; + } + } + + QuicSpdyClientSession::OnStreamClosed(stream_id); +} + +MasqueClientSession::ConnectUdpClientState::ConnectUdpClientState( + QuicSpdyClientStream* stream, + EncapsulatedClientSession* encapsulated_client_session, + MasqueClientSession* masque_session, + QuicDatagramFlowId flow_id, + const QuicSocketAddress& target_server_address) + : stream_(stream), + encapsulated_client_session_(encapsulated_client_session), + masque_session_(masque_session), + flow_id_(flow_id), + target_server_address_(target_server_address) { + QUICHE_DCHECK_NE(masque_session_, nullptr); + masque_session_->RegisterHttp3FlowId(this->flow_id(), this); +} + +MasqueClientSession::ConnectUdpClientState::~ConnectUdpClientState() { + if (flow_id_.has_value()) { + masque_session_->UnregisterHttp3FlowId(flow_id()); + } +} + +MasqueClientSession::ConnectUdpClientState::ConnectUdpClientState( + MasqueClientSession::ConnectUdpClientState&& other) { + *this = std::move(other); +} + +MasqueClientSession::ConnectUdpClientState& +MasqueClientSession::ConnectUdpClientState::operator=( + MasqueClientSession::ConnectUdpClientState&& other) { + stream_ = other.stream_; + encapsulated_client_session_ = other.encapsulated_client_session_; + masque_session_ = other.masque_session_; + flow_id_ = other.flow_id_; + target_server_address_ = other.target_server_address_; + other.flow_id_.reset(); + if (flow_id_.has_value()) { + masque_session_->UnregisterHttp3FlowId(flow_id()); + masque_session_->RegisterHttp3FlowId(flow_id(), this); + } + return *this; +} + +void MasqueClientSession::ConnectUdpClientState::OnHttp3Datagram( + QuicDatagramFlowId flow_id, + absl::string_view payload) { + QUICHE_DCHECK_EQ(flow_id, this->flow_id()); + encapsulated_client_session_->ProcessPacket(payload, target_server_address_); + QUIC_DVLOG(1) << "Sent " << payload.size() + << " bytes to connection for flow_id " << flow_id; +} + } // namespace quic 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 0eab5b22c20..e055a7699be 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 @@ -9,6 +9,7 @@ #include "absl/strings/string_view.h" #include "quic/core/http/quic_spdy_client_session.h" #include "quic/masque/masque_compression_engine.h" +#include "quic/masque/masque_utils.h" #include "quic/platform/api/quic_export.h" #include "quic/platform/api/quic_socket_address.h" @@ -39,14 +40,21 @@ class QUIC_NO_EXPORT MasqueClientSession : public QuicSpdyClientSession { // Process packet that was just decapsulated. virtual void ProcessPacket(absl::string_view packet, - QuicSocketAddress server_address) = 0; + QuicSocketAddress target_server_address) = 0; + + // Close the encapsulated connection. + virtual void CloseConnection( + QuicErrorCode error, + const std::string& details, + ConnectionCloseBehavior connection_close_behavior) = 0; }; // Takes ownership of |connection|, but not of |crypto_config| or // |push_promise_index| or |owner|. All pointers must be non-null. Caller // must ensure that |push_promise_index| and |owner| stay valid for the // lifetime of the newly created MasqueClientSession. - MasqueClientSession(const QuicConfig& config, + MasqueClientSession(MasqueMode masque_mode, + const QuicConfig& config, const ParsedQuicVersionVector& supported_versions, QuicConnection* connection, const QuicServerId& server_id, @@ -60,17 +68,19 @@ class QUIC_NO_EXPORT MasqueClientSession : public QuicSpdyClientSession { // From QuicSession. void OnMessageReceived(absl::string_view message) override; - void OnMessageAcked(QuicMessageId message_id, QuicTime receive_timestamp) override; - void OnMessageLost(QuicMessageId message_id) override; + void OnConnectionClosed(const QuicConnectionCloseFrame& frame, + ConnectionCloseSource source) override; + void OnStreamClosed(QuicStreamId stream_id) override; // Send encapsulated packet. void SendPacket(QuicConnectionId client_connection_id, QuicConnectionId server_connection_id, absl::string_view packet, - const QuicSocketAddress& server_address); + const QuicSocketAddress& target_server_address, + EncapsulatedClientSession* encapsulated_client_session); // Register encapsulated client. This allows clients that are encapsulated // within this MASQUE session to indicate they own a given client connection @@ -84,9 +94,62 @@ class QUIC_NO_EXPORT MasqueClientSession : public QuicSpdyClientSession { // Unregister encapsulated client. |client_connection_id| must match a // value previously passed to RegisterConnectionId. - void UnregisterConnectionId(QuicConnectionId client_connection_id); + void UnregisterConnectionId( + QuicConnectionId client_connection_id, + EncapsulatedClientSession* encapsulated_client_session); private: + // State that the MasqueClientSession keeps for each CONNECT-UDP request. + class QUIC_NO_EXPORT ConnectUdpClientState + : public QuicSpdySession::Http3DatagramVisitor { + public: + // |stream| and |encapsulated_client_session| must be valid for the lifetime + // of the ConnectUdpClientState. + explicit ConnectUdpClientState( + QuicSpdyClientStream* stream, + EncapsulatedClientSession* encapsulated_client_session, + MasqueClientSession* masque_session, + QuicDatagramFlowId flow_id, + const QuicSocketAddress& target_server_address); + + ~ConnectUdpClientState(); + + // Disallow copy but allow move. + ConnectUdpClientState(const ConnectUdpClientState&) = delete; + ConnectUdpClientState(ConnectUdpClientState&&); + ConnectUdpClientState& operator=(const ConnectUdpClientState&) = delete; + ConnectUdpClientState& operator=(ConnectUdpClientState&&); + + QuicSpdyClientStream* stream() const { return stream_; } + EncapsulatedClientSession* encapsulated_client_session() const { + return encapsulated_client_session_; + } + QuicDatagramFlowId flow_id() const { + QUICHE_DCHECK(flow_id_.has_value()); + return *flow_id_; + } + const QuicSocketAddress& target_server_address() const { + return target_server_address_; + } + + // From QuicSpdySession::Http3DatagramVisitor. + void OnHttp3Datagram(QuicDatagramFlowId flow_id, + absl::string_view payload) override; + + private: + QuicSpdyClientStream* stream_; // Unowned. + EncapsulatedClientSession* encapsulated_client_session_; // Unowned. + MasqueClientSession* masque_session_; // Unowned. + absl::optional<QuicDatagramFlowId> flow_id_; + QuicSocketAddress target_server_address_; + }; + + const ConnectUdpClientState* GetOrCreateConnectUdpClientState( + const QuicSocketAddress& target_server_address, + EncapsulatedClientSession* encapsulated_client_session); + + MasqueMode masque_mode_; + std::list<ConnectUdpClientState> connect_udp_client_states_; absl::flat_hash_map<QuicConnectionId, EncapsulatedClientSession*, QuicConnectionIdHash> diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.cc index af6758af58a..8413e0d974b 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.cc +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.cc @@ -30,14 +30,9 @@ enum MasqueAddressFamily : uint8_t { } // namespace -MasqueCompressionEngine::MasqueCompressionEngine(QuicSession* masque_session) - : masque_session_(masque_session) { - if (masque_session_->perspective() == Perspective::IS_SERVER) { - next_flow_id_ = 1; - } else { - next_flow_id_ = 2; - } -} +MasqueCompressionEngine::MasqueCompressionEngine( + QuicSpdySession* masque_session) + : masque_session_(masque_session) {} QuicDatagramFlowId MasqueCompressionEngine::FindOrCreateCompressionContext( QuicConnectionId client_connection_id, @@ -79,7 +74,11 @@ QuicDatagramFlowId MasqueCompressionEngine::FindOrCreateCompressionContext( } // Create new compression context. - flow_id = GetNextFlowId(); + flow_id = masque_session_->GetNextDatagramFlowId(); + if (flow_id == kFlowId0) { + // Do not use value zero which is reserved in this mode. + flow_id = masque_session_->GetNextDatagramFlowId(); + } QUIC_DVLOG(1) << "Compression assigning new flow_id " << flow_id << " to " << server_address << " client " << client_connection_id << " server " << server_connection_id; @@ -107,29 +106,29 @@ bool MasqueCompressionEngine::WriteCompressedPacketToSlice( if (validated) { QUIC_DVLOG(1) << "Compressing using validated flow_id " << flow_id; if (!writer->WriteVarInt62(flow_id)) { - QUIC_BUG << "Failed to write flow_id"; + QUIC_BUG(quic_bug_10981_1) << "Failed to write flow_id"; return false; } } else { QUIC_DVLOG(1) << "Compressing using unvalidated flow_id " << flow_id; if (!writer->WriteVarInt62(kFlowId0)) { - QUIC_BUG << "Failed to write kFlowId0"; + QUIC_BUG(quic_bug_10981_2) << "Failed to write kFlowId0"; return false; } if (!writer->WriteVarInt62(flow_id)) { - QUIC_BUG << "Failed to write flow_id"; + QUIC_BUG(quic_bug_10981_3) << "Failed to write flow_id"; return false; } if (!writer->WriteLengthPrefixedConnectionId(client_connection_id)) { - QUIC_BUG << "Failed to write client_connection_id"; + QUIC_BUG(quic_bug_10981_4) << "Failed to write client_connection_id"; return false; } if (!writer->WriteLengthPrefixedConnectionId(server_connection_id)) { - QUIC_BUG << "Failed to write server_connection_id"; + QUIC_BUG(quic_bug_10981_5) << "Failed to write server_connection_id"; return false; } if (!writer->WriteUInt16(server_address.port())) { - QUIC_BUG << "Failed to write port"; + QUIC_BUG(quic_bug_10981_6) << "Failed to write port"; return false; } QuicIpAddress peer_ip = server_address.host(); @@ -140,30 +139,31 @@ bool MasqueCompressionEngine::WriteCompressedPacketToSlice( if (peer_ip.address_family() == IpAddressFamily::IP_V6) { address_id = MasqueAddressFamilyIPv6; if (peer_ip_bytes.length() != QuicIpAddress::kIPv6AddressSize) { - QUIC_BUG << "Bad IPv6 length " << server_address; + QUIC_BUG(quic_bug_10981_7) << "Bad IPv6 length " << server_address; return false; } } else if (peer_ip.address_family() == IpAddressFamily::IP_V4) { address_id = MasqueAddressFamilyIPv4; if (peer_ip_bytes.length() != QuicIpAddress::kIPv4AddressSize) { - QUIC_BUG << "Bad IPv4 length " << server_address; + QUIC_BUG(quic_bug_10981_8) << "Bad IPv4 length " << server_address; return false; } } else { - QUIC_BUG << "Unexpected server_address " << server_address; + QUIC_BUG(quic_bug_10981_9) + << "Unexpected server_address " << server_address; return false; } if (!writer->WriteUInt8(address_id)) { - QUIC_BUG << "Failed to write address_id"; + QUIC_BUG(quic_bug_10981_10) << "Failed to write address_id"; return false; } if (!writer->WriteStringPiece(peer_ip_bytes)) { - QUIC_BUG << "Failed to write IP address"; + QUIC_BUG(quic_bug_10981_11) << "Failed to write IP address"; return false; } } if (!writer->WriteUInt8(first_byte)) { - QUIC_BUG << "Failed to write first_byte"; + QUIC_BUG(quic_bug_10981_12) << "Failed to write first_byte"; return false; } if (long_header) { @@ -173,7 +173,7 @@ bool MasqueCompressionEngine::WriteCompressedPacketToSlice( return false; } if (!writer->WriteUInt32(version_label)) { - QUIC_BUG << "Failed to write version"; + QUIC_BUG(quic_bug_10981_13) << "Failed to write version"; return false; } QuicConnectionId packet_destination_connection_id, @@ -215,7 +215,7 @@ bool MasqueCompressionEngine::WriteCompressedPacketToSlice( } absl::string_view packet_payload = reader->ReadRemainingPayload(); if (!writer->WriteStringPiece(packet_payload)) { - QUIC_BUG << "Failed to write packet_payload"; + QUIC_BUG(quic_bug_10981_14) << "Failed to write packet_payload"; return false; } return true; @@ -231,13 +231,13 @@ void MasqueCompressionEngine::CompressAndSendPacket( << quiche::QuicheTextUtils::HexDump(packet); QUICHE_DCHECK(server_address.IsInitialized()); if (packet.empty()) { - QUIC_BUG << "Tried to send empty packet"; + QUIC_BUG(quic_bug_10981_15) << "Tried to send empty packet"; return; } QuicDataReader reader(packet.data(), packet.length()); uint8_t first_byte; if (!reader.ReadUInt8(&first_byte)) { - QUIC_BUG << "Failed to read first_byte"; + QUIC_BUG(quic_bug_10981_16) << "Failed to read first_byte"; return; } const bool long_header = (first_byte & FLAGS_LONG_HEADER) != 0; @@ -341,7 +341,7 @@ bool MasqueCompressionEngine::ParseCompressionContext( QuicIpAddress ip_address; ip_address.FromPackedString(ip_bytes, ip_bytes_length); if (!ip_address.IsInitialized()) { - QUIC_BUG << "Failed to parse IP address"; + QUIC_BUG(quic_bug_10981_17) << "Failed to parse IP address"; return false; } QuicSocketAddress new_server_address = QuicSocketAddress(ip_address, port); @@ -425,7 +425,7 @@ bool MasqueCompressionEngine::WriteDecompressedPacket( *packet = std::vector<char>(packet_length); QuicDataWriter writer(packet->size(), packet->data()); if (!writer.WriteUInt8(first_byte)) { - QUIC_BUG << "Failed to write first_byte"; + QUIC_BUG(quic_bug_10981_18) << "Failed to write first_byte"; return false; } if (*version_present) { @@ -435,26 +435,29 @@ bool MasqueCompressionEngine::WriteDecompressedPacket( return false; } if (!writer.WriteUInt32(version_label)) { - QUIC_BUG << "Failed to write version"; + QUIC_BUG(quic_bug_10981_19) << "Failed to write version"; return false; } if (!writer.WriteLengthPrefixedConnectionId(destination_connection_id)) { - QUIC_BUG << "Failed to write long header destination_connection_id"; + QUIC_BUG(quic_bug_10981_20) + << "Failed to write long header destination_connection_id"; return false; } if (!writer.WriteLengthPrefixedConnectionId(source_connection_id)) { - QUIC_BUG << "Failed to write long header source_connection_id"; + QUIC_BUG(quic_bug_10981_21) + << "Failed to write long header source_connection_id"; return false; } } else { if (!writer.WriteConnectionId(destination_connection_id)) { - QUIC_BUG << "Failed to write short header destination_connection_id"; + QUIC_BUG(quic_bug_10981_22) + << "Failed to write short header destination_connection_id"; return false; } } absl::string_view payload = reader->ReadRemainingPayload(); if (!writer.WriteStringPiece(payload)) { - QUIC_BUG << "Failed to write payload"; + QUIC_BUG(quic_bug_10981_23) << "Failed to write payload"; return false; } return true; @@ -520,12 +523,6 @@ bool MasqueCompressionEngine::DecompressDatagram( return true; } -QuicDatagramFlowId MasqueCompressionEngine::GetNextFlowId() { - const QuicDatagramFlowId next_flow_id = next_flow_id_; - next_flow_id_ += 2; - return next_flow_id; -} - void MasqueCompressionEngine::UnregisterClientConnectionId( QuicConnectionId client_connection_id) { std::vector<QuicDatagramFlowId> flow_ids_to_remove; diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.h b/chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.h index 96c59482b52..9bea618ae76 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.h +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_compression_engine.h @@ -7,8 +7,8 @@ #include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" +#include "quic/core/http/quic_spdy_session.h" #include "quic/core/quic_connection_id.h" -#include "quic/core/quic_session.h" #include "quic/core/quic_types.h" #include "quic/platform/api/quic_containers.h" #include "quic/platform/api/quic_export.h" @@ -35,7 +35,7 @@ class QUIC_NO_EXPORT MasqueCompressionEngine { public: // Caller must ensure that |masque_session| has a lifetime longer than the // newly constructed MasqueCompressionEngine. - explicit MasqueCompressionEngine(QuicSession* masque_session); + explicit MasqueCompressionEngine(QuicSpdySession* masque_session); // Disallow copy and assign. MasqueCompressionEngine(const MasqueCompressionEngine&) = delete; @@ -78,9 +78,6 @@ class QUIC_NO_EXPORT MasqueCompressionEngine { bool validated = false; }; - // Generates a new datagram flow ID. - QuicDatagramFlowId GetNextFlowId(); - // Finds or creates a new compression context to use during compression. // |client_connection_id_present| and |server_connection_id_present| indicate // whether the corresponding connection ID is present in the current packet. @@ -117,9 +114,8 @@ class QUIC_NO_EXPORT MasqueCompressionEngine { std::vector<char>* packet, bool* version_present); - QuicSession* masque_session_; // Unowned. + QuicSpdySession* masque_session_; // Unowned. absl::flat_hash_map<QuicDatagramFlowId, MasqueCompressionContext> contexts_; - QuicDatagramFlowId next_flow_id_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_dispatcher.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_dispatcher.cc index 6edca628f4f..5729d5427f7 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_dispatcher.cc +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_dispatcher.cc @@ -8,9 +8,11 @@ namespace quic { MasqueDispatcher::MasqueDispatcher( + MasqueMode masque_mode, const QuicConfig* config, const QuicCryptoServerConfig* crypto_config, QuicVersionManager* version_manager, + QuicEpollServer* epoll_server, std::unique_ptr<QuicConnectionHelperInterface> helper, std::unique_ptr<QuicCryptoServerStreamBase::Helper> session_helper, std::unique_ptr<QuicAlarmFactory> alarm_factory, @@ -24,6 +26,8 @@ MasqueDispatcher::MasqueDispatcher( std::move(alarm_factory), masque_server_backend, expected_server_connection_id_length), + masque_mode_(masque_mode), + epoll_server_(epoll_server), masque_server_backend_(masque_server_backend) {} std::unique_ptr<QuicSession> MasqueDispatcher::CreateQuicSession( @@ -31,7 +35,8 @@ std::unique_ptr<QuicSession> MasqueDispatcher::CreateQuicSession( const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, absl::string_view /*alpn*/, - const ParsedQuicVersion& version) { + const ParsedQuicVersion& version, + absl::string_view /*sni*/) { // The MasqueServerSession takes ownership of |connection| below. QuicConnection* connection = new QuicConnection(connection_id, self_address, peer_address, helper(), @@ -40,9 +45,9 @@ std::unique_ptr<QuicSession> MasqueDispatcher::CreateQuicSession( ParsedQuicVersionVector{version}); auto session = std::make_unique<MasqueServerSession>( - config(), GetSupportedVersions(), connection, this, this, - session_helper(), crypto_config(), compressed_certs_cache(), - masque_server_backend_); + masque_mode_, config(), GetSupportedVersions(), connection, this, this, + epoll_server_, session_helper(), crypto_config(), + compressed_certs_cache(), masque_server_backend_); session->Initialize(); return session; } @@ -70,10 +75,11 @@ void MasqueDispatcher::RegisterClientConnectionId( // Make sure we don't try to overwrite an existing registration with a // different session. - QUIC_BUG_IF(client_connection_id_registrations_.find(client_connection_id) != - client_connection_id_registrations_.end() && - client_connection_id_registrations_[client_connection_id] != - masque_server_session) + QUIC_BUG_IF(quic_bug_12013_1, + client_connection_id_registrations_.find(client_connection_id) != + client_connection_id_registrations_.end() && + client_connection_id_registrations_[client_connection_id] != + masque_server_session) << "Overwriting existing registration for " << client_connection_id; client_connection_id_registrations_[client_connection_id] = masque_server_session; diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_dispatcher.h b/chromium/net/third_party/quiche/src/quic/masque/masque_dispatcher.h index 2e0186c9a3d..368219e031a 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_dispatcher.h +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_dispatcher.h @@ -8,6 +8,8 @@ #include "absl/container/flat_hash_map.h" #include "quic/masque/masque_server_backend.h" #include "quic/masque/masque_server_session.h" +#include "quic/masque/masque_utils.h" +#include "quic/platform/api/quic_epoll.h" #include "quic/platform/api/quic_export.h" #include "quic/tools/quic_simple_dispatcher.h" @@ -19,9 +21,11 @@ class QUIC_NO_EXPORT MasqueDispatcher : public QuicSimpleDispatcher, public MasqueServerSession::Visitor { public: explicit MasqueDispatcher( + MasqueMode masque_mode, const QuicConfig* config, const QuicCryptoServerConfig* crypto_config, QuicVersionManager* version_manager, + QuicEpollServer* epoll_server, std::unique_ptr<QuicConnectionHelperInterface> helper, std::unique_ptr<QuicCryptoServerStreamBase::Helper> session_helper, std::unique_ptr<QuicAlarmFactory> alarm_factory, @@ -38,7 +42,8 @@ class QUIC_NO_EXPORT MasqueDispatcher : public QuicSimpleDispatcher, const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, absl::string_view alpn, - const ParsedQuicVersion& version) override; + const ParsedQuicVersion& version, + absl::string_view sni) override; bool OnFailedToDispatchPacket(const ReceivedPacketInfo& packet_info) override; @@ -51,6 +56,8 @@ class QUIC_NO_EXPORT MasqueDispatcher : public QuicSimpleDispatcher, QuicConnectionId client_connection_id) override; private: + MasqueMode masque_mode_; + QuicEpollServer* epoll_server_; // Unowned. MasqueServerBackend* masque_server_backend_; // Unowned. // Mapping from client connection IDs to server sessions, allows routing // incoming packets to the right MASQUE connection. diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_encapsulated_client_session.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_encapsulated_client_session.cc index 983a094f30a..3d557c3a073 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_encapsulated_client_session.cc +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_encapsulated_client_session.cc @@ -31,11 +31,19 @@ void MasqueEncapsulatedClientSession::ProcessPacket( received_packet); } +void MasqueEncapsulatedClientSession::CloseConnection( + QuicErrorCode error, + const std::string& details, + ConnectionCloseBehavior connection_close_behavior) { + connection()->CloseConnection(error, details, connection_close_behavior); +} + void MasqueEncapsulatedClientSession::OnConnectionClosed( - const QuicConnectionCloseFrame& /*frame*/, - ConnectionCloseSource /*source*/) { + const QuicConnectionCloseFrame& frame, + ConnectionCloseSource source) { + QuicSpdyClientSession::OnConnectionClosed(frame, source); masque_client_session_->UnregisterConnectionId( - connection()->client_connection_id()); + connection()->client_connection_id(), this); } } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_encapsulated_client_session.h b/chromium/net/third_party/quiche/src/quic/masque/masque_encapsulated_client_session.h index 570ccdb84d3..09521591898 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_encapsulated_client_session.h +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_encapsulated_client_session.h @@ -44,6 +44,10 @@ class QUIC_NO_EXPORT MasqueEncapsulatedClientSession // From MasqueClientSession::EncapsulatedClientSession. void ProcessPacket(absl::string_view packet, QuicSocketAddress server_address) override; + void CloseConnection( + QuicErrorCode error, + const std::string& details, + ConnectionCloseBehavior connection_close_behavior) override; // From QuicSession. void OnConnectionClosed(const QuicConnectionCloseFrame& frame, diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_encapsulated_epoll_client.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_encapsulated_epoll_client.cc index 8fb938dbdc7..8891e4a0e41 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_encapsulated_epoll_client.cc +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_encapsulated_epoll_client.cc @@ -30,8 +30,8 @@ class MasquePacketWriter : public QuicPacketWriter { absl::string_view packet(buffer, buf_len); client_->masque_client()->masque_client_session()->SendPacket( client_->session()->connection()->client_connection_id(), - client_->session()->connection()->connection_id(), packet, - peer_address); + client_->session()->connection()->connection_id(), packet, peer_address, + client_->masque_encapsulated_client_session()); return WriteResult(WRITE_STATUS_OK, buf_len); } @@ -94,7 +94,7 @@ MasqueEncapsulatedEpollClient::MasqueEncapsulatedEpollClient( MasqueEncapsulatedEpollClient::~MasqueEncapsulatedEpollClient() { masque_client_->masque_client_session()->UnregisterConnectionId( - client_connection_id_); + client_connection_id_, masque_encapsulated_client_session()); } std::unique_ptr<QuicSession> 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 1d77e557a64..5ad91cfd9ad 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 @@ -3,15 +3,16 @@ // found in the LICENSE file. #include "quic/masque/masque_epoll_client.h" +#include "absl/memory/memory.h" #include "quic/masque/masque_client_session.h" #include "quic/masque/masque_utils.h" -#include "quic/platform/api/quic_ptr_util.h" namespace quic { MasqueEpollClient::MasqueEpollClient( QuicSocketAddress server_address, const QuicServerId& server_id, + MasqueMode masque_mode, QuicEpollServer* epoll_server, std::unique_ptr<ProofVerifier> proof_verifier, const std::string& authority) @@ -20,6 +21,7 @@ MasqueEpollClient::MasqueEpollClient( MasqueSupportedVersions(), epoll_server, std::move(proof_verifier)), + masque_mode_(masque_mode), authority_(authority) {} std::unique_ptr<QuicSession> MasqueEpollClient::CreateQuicClientSession( @@ -28,8 +30,8 @@ std::unique_ptr<QuicSession> MasqueEpollClient::CreateQuicClientSession( QUIC_DLOG(INFO) << "Creating MASQUE session for " << connection->connection_id(); return std::make_unique<MasqueClientSession>( - *config(), supported_versions, connection, server_id(), crypto_config(), - push_promise_index(), this); + masque_mode_, *config(), supported_versions, connection, server_id(), + crypto_config(), push_promise_index(), this); } MasqueClientSession* MasqueEpollClient::masque_client_session() { @@ -44,6 +46,7 @@ QuicConnectionId MasqueEpollClient::connection_id() { std::unique_ptr<MasqueEpollClient> MasqueEpollClient::Create( const std::string& host, int port, + MasqueMode masque_mode, QuicEpollServer* epoll_server, std::unique_ptr<ProofVerifier> proof_verifier) { // Build the masque_client, and try to connect. @@ -53,11 +56,11 @@ std::unique_ptr<MasqueEpollClient> MasqueEpollClient::Create( return nullptr; } QuicServerId server_id(host, port); - // Use QuicWrapUnique(new MasqueEpollClient(...)) instead of + // Use absl::WrapUnique(new MasqueEpollClient(...)) instead of // std::make_unique<MasqueEpollClient>(...) because the constructor for // MasqueEpollClient is private and therefore not accessible from make_unique. - auto masque_client = QuicWrapUnique(new MasqueEpollClient( - addr, server_id, epoll_server, std::move(proof_verifier), + auto masque_client = absl::WrapUnique(new MasqueEpollClient( + addr, server_id, masque_mode, epoll_server, std::move(proof_verifier), absl::StrCat(host, ":", port))); if (masque_client == nullptr) { @@ -78,33 +81,35 @@ std::unique_ptr<MasqueEpollClient> MasqueEpollClient::Create( return nullptr; } - std::string body = "foo"; - - // Construct a GET or POST request for supplied URL. - spdy::Http2HeaderBlock header_block; - header_block[":method"] = "POST"; - header_block[":scheme"] = "https"; - header_block[":authority"] = masque_client->authority_; - header_block[":path"] = "/.well-known/masque/init"; - - // Make sure to store the response, for later output. - masque_client->set_store_response(true); - - // Send the MASQUE init command. - masque_client->SendRequestAndWaitForResponse(header_block, body, - /*fin=*/true); - - if (!masque_client->connected()) { - QUIC_LOG(ERROR) << "MASQUE init request caused connection failure. Error: " - << QuicErrorCodeToString(masque_client->session()->error()); - return nullptr; - } - - const int response_code = masque_client->latest_response_code(); - if (response_code != 200) { - QUIC_LOG(ERROR) << "MASQUE init request failed with HTTP response code " - << response_code; - return nullptr; + if (masque_client->masque_mode() == MasqueMode::kLegacy) { + // Construct the legacy mode init request. + spdy::Http2HeaderBlock header_block; + header_block[":method"] = "POST"; + header_block[":scheme"] = "https"; + header_block[":authority"] = masque_client->authority_; + header_block[":path"] = "/.well-known/masque/init"; + std::string body = "foo"; + + // Make sure to store the response, for later output. + masque_client->set_store_response(true); + + // Send the MASQUE init command. + masque_client->SendRequestAndWaitForResponse(header_block, body, + /*fin=*/true); + + if (!masque_client->connected()) { + QUIC_LOG(ERROR) + << "MASQUE init request caused connection failure. Error: " + << QuicErrorCodeToString(masque_client->session()->error()); + return nullptr; + } + + const int response_code = masque_client->latest_response_code(); + if (response_code != 200) { + QUIC_LOG(ERROR) << "MASQUE init request failed with HTTP response code " + << response_code; + return nullptr; + } } return masque_client; } diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.h b/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.h index 345c18c7dee..1db161f5aaa 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.h +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_client.h @@ -6,6 +6,7 @@ #define QUICHE_QUIC_MASQUE_MASQUE_EPOLL_CLIENT_H_ #include "quic/masque/masque_client_session.h" +#include "quic/masque/masque_utils.h" #include "quic/platform/api/quic_export.h" #include "quic/tools/quic_client.h" @@ -19,6 +20,7 @@ class QUIC_NO_EXPORT MasqueEpollClient : public QuicClient, static std::unique_ptr<MasqueEpollClient> Create( const std::string& host, int port, + MasqueMode masque_mode, QuicEpollServer* epoll_server, std::unique_ptr<ProofVerifier> proof_verifier); @@ -37,10 +39,13 @@ class QUIC_NO_EXPORT MasqueEpollClient : public QuicClient, void UnregisterClientConnectionId( QuicConnectionId client_connection_id) override; + MasqueMode masque_mode() const { return masque_mode_; } + private: // Constructor is private, use Create() instead. MasqueEpollClient(QuicSocketAddress server_address, const QuicServerId& server_id, + MasqueMode masque_mode, QuicEpollServer* epoll_server, std::unique_ptr<ProofVerifier> proof_verifier, const std::string& authority); @@ -49,6 +54,7 @@ class QUIC_NO_EXPORT MasqueEpollClient : public QuicClient, MasqueEpollClient(const MasqueEpollClient&) = delete; MasqueEpollClient& operator=(const MasqueEpollClient&) = delete; + MasqueMode masque_mode_; std::string authority_; }; diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_server.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_server.cc index 34769a37bf7..e8a0850f0d8 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_server.cc +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_server.cc @@ -11,15 +11,18 @@ namespace quic { -MasqueEpollServer::MasqueEpollServer(MasqueServerBackend* masque_server_backend) +MasqueEpollServer::MasqueEpollServer(MasqueMode masque_mode, + MasqueServerBackend* masque_server_backend) : QuicServer(CreateDefaultProofSource(), masque_server_backend, MasqueSupportedVersions()), + masque_mode_(masque_mode), masque_server_backend_(masque_server_backend) {} QuicDispatcher* MasqueEpollServer::CreateQuicDispatcher() { return new MasqueDispatcher( - &config(), &crypto_config(), version_manager(), + masque_mode_, &config(), &crypto_config(), version_manager(), + epoll_server(), std::make_unique<QuicEpollConnectionHelper>(epoll_server(), QuicAllocator::BUFFER_POOL), std::make_unique<QuicSimpleCryptoServerStreamHelper>(), diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_server.h b/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_server.h index 57eab6a1f84..88161ee37cf 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_server.h +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_epoll_server.h @@ -6,6 +6,7 @@ #define QUICHE_QUIC_MASQUE_MASQUE_EPOLL_SERVER_H_ #include "quic/masque/masque_server_backend.h" +#include "quic/masque/masque_utils.h" #include "quic/platform/api/quic_export.h" #include "quic/tools/quic_server.h" @@ -14,7 +15,8 @@ namespace quic { // QUIC server that implements MASQUE. class QUIC_NO_EXPORT MasqueEpollServer : public QuicServer { public: - explicit MasqueEpollServer(MasqueServerBackend* masque_server_backend); + explicit MasqueEpollServer(MasqueMode masque_mode, + MasqueServerBackend* masque_server_backend); // Disallow copy and assign. MasqueEpollServer(const MasqueEpollServer&) = delete; @@ -24,6 +26,7 @@ class QUIC_NO_EXPORT MasqueEpollServer : public QuicServer { QuicDispatcher* CreateQuicDispatcher() override; private: + MasqueMode masque_mode_; MasqueServerBackend* masque_server_backend_; // Unowned. }; diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_server_backend.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_server_backend.cc index 19948198f33..112f3053180 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_server_backend.cc +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_server_backend.cc @@ -8,20 +8,10 @@ namespace quic { -namespace { - -std::string GetRequestHandlerKey( - const QuicSimpleServerBackend::RequestHandler* request_handler) { - return absl::StrCat(request_handler->connection_id().ToString(), "_", - request_handler->stream_id(), "_", - request_handler->peer_host()); -} - -} // namespace - -MasqueServerBackend::MasqueServerBackend(const std::string& server_authority, +MasqueServerBackend::MasqueServerBackend(MasqueMode masque_mode, + const std::string& server_authority, const std::string& cache_directory) - : server_authority_(server_authority) { + : masque_mode_(masque_mode), server_authority_(server_authority) { if (!cache_directory.empty()) { QuicMemoryCacheBackend::InitializeBackend(cache_directory); } @@ -31,28 +21,40 @@ bool MasqueServerBackend::MaybeHandleMasqueRequest( const spdy::Http2HeaderBlock& request_headers, const std::string& request_body, QuicSimpleServerBackend::RequestHandler* request_handler) { - auto path_pair = request_headers.find(":path"); auto method_pair = request_headers.find(":method"); - auto scheme_pair = request_headers.find(":scheme"); - if (path_pair == request_headers.end() || - method_pair == request_headers.end() || - scheme_pair == request_headers.end()) { - // This request is missing required headers. + if (method_pair == request_headers.end()) { + // Request is missing a method. return false; } - absl::string_view path = path_pair->second; - absl::string_view scheme = scheme_pair->second; absl::string_view method = method_pair->second; - if (scheme != "https" || method != "POST" || request_body.empty()) { - // MASQUE requests MUST be a non-empty https POST. - return false; - } + std::string masque_path = ""; + if (masque_mode_ == MasqueMode::kLegacy) { + auto path_pair = request_headers.find(":path"); + auto scheme_pair = request_headers.find(":scheme"); + if (path_pair == request_headers.end() || + scheme_pair == request_headers.end()) { + // This request is missing required headers. + return false; + } + absl::string_view path = path_pair->second; + absl::string_view scheme = scheme_pair->second; + if (scheme != "https" || method != "POST" || request_body.empty()) { + // MASQUE requests MUST be a non-empty https POST. + return false; + } - if (path.rfind("/.well-known/masque/", 0) != 0) { - // This request is not a MASQUE path. - return false; + if (path.rfind("/.well-known/masque/", 0) != 0) { + // This request is not a MASQUE path. + return false; + } + masque_path = std::string(path.substr(sizeof("/.well-known/masque/") - 1)); + } else { + QUICHE_DCHECK_EQ(masque_mode_, MasqueMode::kOpen); + if (method != "CONNECT-UDP") { + // This is not a MASQUE request. + return false; + } } - std::string masque_path(path.substr(sizeof("/.well-known/masque/") - 1)); if (!server_authority_.empty()) { auto authority_pair = request_headers.find(":authority"); @@ -67,34 +69,29 @@ bool MasqueServerBackend::MaybeHandleMasqueRequest( } } - auto backend_client_pair = - backend_clients_.find(request_handler->connection_id()); - if (backend_client_pair == backend_clients_.end()) { + auto it = backend_client_states_.find(request_handler->connection_id()); + if (it == backend_client_states_.end()) { QUIC_LOG(ERROR) << "Could not find backend client for " - << GetRequestHandlerKey(request_handler) << " " << masque_path << request_headers.DebugString(); return false; } - BackendClient* backend_client = backend_client_pair->second; + BackendClient* backend_client = it->second.backend_client; std::unique_ptr<QuicBackendResponse> response = backend_client->HandleMasqueRequest(masque_path, request_headers, request_body, request_handler); if (response == nullptr) { QUIC_LOG(ERROR) << "Backend client did not process request for " - << GetRequestHandlerKey(request_handler) << " " << masque_path << request_headers.DebugString(); return false; } QUIC_DLOG(INFO) << "Sending MASQUE response for " - << GetRequestHandlerKey(request_handler) << " " << masque_path << request_headers.DebugString(); request_handler->OnResponseBackendComplete(response.get(), {}); - active_response_map_[GetRequestHandlerKey(request_handler)] = - std::move(response); + it->second.responses.emplace_back(std::move(response)); return true; } @@ -109,7 +106,6 @@ void MasqueServerBackend::FetchResponseFromBackend( return; } QUIC_DLOG(INFO) << "Fetching non-MASQUE response for " - << GetRequestHandlerKey(request_handler) << request_headers.DebugString(); QuicMemoryCacheBackend::FetchResponseFromBackend( request_headers, request_body, request_handler); @@ -117,23 +113,23 @@ void MasqueServerBackend::FetchResponseFromBackend( void MasqueServerBackend::CloseBackendResponseStream( QuicSimpleServerBackend::RequestHandler* request_handler) { - QUIC_DLOG(INFO) << "Closing response stream for " - << GetRequestHandlerKey(request_handler); - active_response_map_.erase(GetRequestHandlerKey(request_handler)); + QUIC_DLOG(INFO) << "Closing response stream"; QuicMemoryCacheBackend::CloseBackendResponseStream(request_handler); } void MasqueServerBackend::RegisterBackendClient(QuicConnectionId connection_id, BackendClient* backend_client) { - QUIC_BUG_IF(backend_clients_.find(connection_id) != backend_clients_.end()) - << connection_id << " already in backend clients map"; - backend_clients_[connection_id] = backend_client; QUIC_DLOG(INFO) << "Registering backend client for " << connection_id; + QUIC_BUG_IF(quic_bug_12005_1, backend_client_states_.find(connection_id) != + backend_client_states_.end()) + << connection_id << " already in backend clients map"; + backend_client_states_[connection_id] = + BackendClientState{backend_client, {}}; } void MasqueServerBackend::RemoveBackendClient(QuicConnectionId connection_id) { - backend_clients_.erase(connection_id); QUIC_DLOG(INFO) << "Removing backend client for " << connection_id; + backend_client_states_.erase(connection_id); } } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_server_backend.h b/chromium/net/third_party/quiche/src/quic/masque/masque_server_backend.h index 180935917e5..4f0e10d7421 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_server_backend.h +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_server_backend.h @@ -6,6 +6,7 @@ #define QUICHE_QUIC_MASQUE_MASQUE_SERVER_BACKEND_H_ #include "absl/container/flat_hash_map.h" +#include "quic/masque/masque_utils.h" #include "quic/platform/api/quic_export.h" #include "quic/tools/quic_memory_cache_backend.h" @@ -27,7 +28,8 @@ class QUIC_NO_EXPORT MasqueServerBackend : public QuicMemoryCacheBackend { virtual ~BackendClient() = default; }; - explicit MasqueServerBackend(const std::string& server_authority, + explicit MasqueServerBackend(MasqueMode masque_mode, + const std::string& server_authority, const std::string& cache_directory); // Disallow copy and assign. @@ -57,11 +59,16 @@ class QUIC_NO_EXPORT MasqueServerBackend : public QuicMemoryCacheBackend { const std::string& request_body, QuicSimpleServerBackend::RequestHandler* request_handler); + MasqueMode masque_mode_; std::string server_authority_; - absl::flat_hash_map<std::string, std::unique_ptr<QuicBackendResponse>> - active_response_map_; - absl::flat_hash_map<QuicConnectionId, BackendClient*, QuicConnectionIdHash> - backend_clients_; + + struct QUIC_NO_EXPORT BackendClientState { + BackendClient* backend_client; + std::vector<std::unique_ptr<QuicBackendResponse>> responses; + }; + absl:: + flat_hash_map<QuicConnectionId, BackendClientState, QuicConnectionIdHash> + backend_client_states_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_server_bin.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_server_bin.cc index b25bf834ed3..d5a5d9f7d36 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_server_bin.cc +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_server_bin.cc @@ -14,6 +14,7 @@ #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_socket_address.h" +#include "quic/platform/api/quic_system_event_loop.h" DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, port, @@ -35,7 +36,14 @@ DEFINE_QUIC_COMMAND_LINE_FLAG( "Specifies the authority over which the server will accept MASQUE " "requests. Defaults to empty which allows all authorities."); +DEFINE_QUIC_COMMAND_LINE_FLAG(std::string, + masque_mode, + "", + "Allows setting MASQUE mode, valid values are " + "open and legacy. Defaults to open."); + int main(int argc, char* argv[]) { + QuicSystemEventLoop event_loop("masque_server"); const char* usage = "Usage: masque_server [options]"; std::vector<std::string> non_option_args = quic::QuicParseCommandLineFlags(usage, argc, argv); @@ -44,17 +52,30 @@ int main(int argc, char* argv[]) { return 0; } + SetQuicReloadableFlag(quic_h3_datagram, true); + + quic::MasqueMode masque_mode = quic::MasqueMode::kOpen; + std::string mode_string = GetQuicFlag(FLAGS_masque_mode); + if (mode_string == "legacy") { + masque_mode = quic::MasqueMode::kLegacy; + } else if (!mode_string.empty() && mode_string != "open") { + std::cerr << "Invalid masque_mode \"" << mode_string << "\"" << std::endl; + return 1; + } + auto backend = std::make_unique<quic::MasqueServerBackend>( - GetQuicFlag(FLAGS_server_authority), GetQuicFlag(FLAGS_cache_dir)); + masque_mode, GetQuicFlag(FLAGS_server_authority), + GetQuicFlag(FLAGS_cache_dir)); - auto server = std::make_unique<quic::MasqueEpollServer>(backend.get()); + auto server = + std::make_unique<quic::MasqueEpollServer>(masque_mode, backend.get()); if (!server->CreateUDPSocketAndListen(quic::QuicSocketAddress( quic::QuicIpAddress::Any6(), GetQuicFlag(FLAGS_port)))) { return 1; } - std::cerr << "Started MASQUE server" << std::endl; + std::cerr << "Started " << masque_mode << " MASQUE server" << std::endl; server->HandleEventsForever(); return 0; } 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 228444aacbb..036c75fb95c 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 @@ -4,14 +4,82 @@ #include "quic/masque/masque_server_session.h" +#include <netdb.h> + +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "quic/core/http/spdy_utils.h" +#include "quic/core/quic_data_reader.h" +#include "quic/core/quic_udp_socket.h" +#include "quic/tools/quic_url.h" +#include "common/platform/api/quiche_text_utils.h" + namespace quic { +namespace { +// RAII wrapper for QuicUdpSocketFd. +class FdWrapper { + public: + // Takes ownership of |fd| and closes the file descriptor on destruction. + explicit FdWrapper(int address_family) { + QuicUdpSocketApi socket_api; + fd_ = + socket_api.Create(address_family, + /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, + /*send_buffer_size =*/kDefaultSocketReceiveBuffer); + } + + ~FdWrapper() { + if (fd_ == kQuicInvalidSocketFd) { + return; + } + QuicUdpSocketApi socket_api; + socket_api.Destroy(fd_); + } + + // Hands ownership of the file descriptor to the caller. + QuicUdpSocketFd extract_fd() { + QuicUdpSocketFd fd = fd_; + fd_ = kQuicInvalidSocketFd; + return fd; + } + + // Keeps ownership of the file descriptor. + QuicUdpSocketFd fd() { return fd_; } + + // Disallow copy and move. + FdWrapper(const FdWrapper&) = delete; + FdWrapper(FdWrapper&&) = delete; + FdWrapper& operator=(const FdWrapper&) = delete; + FdWrapper& operator=(FdWrapper&&) = delete; + + private: + QuicUdpSocketFd fd_; +}; + +std::unique_ptr<QuicBackendResponse> CreateBackendErrorResponse( + absl::string_view status, + absl::string_view error_details) { + spdy::Http2HeaderBlock response_headers; + response_headers[":status"] = status; + response_headers["masque-debug-info"] = error_details; + auto response = std::make_unique<QuicBackendResponse>(); + response->set_response_type(QuicBackendResponse::REGULAR_RESPONSE); + response->set_headers(std::move(response_headers)); + return response; +} + +} // namespace + MasqueServerSession::MasqueServerSession( + MasqueMode masque_mode, const QuicConfig& config, const ParsedQuicVersionVector& supported_versions, QuicConnection* connection, QuicSession::Visitor* visitor, Visitor* owner, + QuicEpollServer* epoll_server, QuicCryptoServerStreamBase::Helper* helper, const QuicCryptoServerConfig* crypto_config, QuicCompressedCertsCache* compressed_certs_cache, @@ -26,41 +94,54 @@ MasqueServerSession::MasqueServerSession( masque_server_backend), masque_server_backend_(masque_server_backend), owner_(owner), - compression_engine_(this) { + epoll_server_(epoll_server), + compression_engine_(this), + masque_mode_(masque_mode) { + // Artificially increase the max packet length to 1350 to ensure we can fit + // QUIC packets inside DATAGRAM frames. + // TODO(b/181606597) Remove this workaround once we use PMTUD. + connection->SetMaxPacketLength(kDefaultMaxPacketSize); + masque_server_backend_->RegisterBackendClient(connection_id(), this); + QUICHE_DCHECK_NE(epoll_server_, nullptr); } void MasqueServerSession::OnMessageReceived(absl::string_view message) { - QUIC_DVLOG(1) << "Received DATAGRAM frame of length " << message.length(); - - QuicConnectionId client_connection_id, server_connection_id; - QuicSocketAddress server_address; - std::vector<char> packet; - bool version_present; - if (!compression_engine_.DecompressDatagram( - message, &client_connection_id, &server_connection_id, - &server_address, &packet, &version_present)) { - return; - } + if (masque_mode_ == MasqueMode::kLegacy) { + QUIC_DVLOG(1) << "Received DATAGRAM frame of length " << message.length(); + QuicConnectionId client_connection_id, server_connection_id; + QuicSocketAddress target_server_address; + std::vector<char> packet; + bool version_present; + if (!compression_engine_.DecompressDatagram( + message, &client_connection_id, &server_connection_id, + &target_server_address, &packet, &version_present)) { + return; + } - QUIC_DVLOG(1) << "Received packet of length " << packet.size() << " for " - << server_address << " client " << client_connection_id; + QUIC_DVLOG(1) << "Received packet of length " << packet.size() << " for " + << target_server_address << " client " + << client_connection_id; - if (version_present) { - if (client_connection_id.length() != kQuicDefaultConnectionIdLength) { - QUIC_DLOG(ERROR) - << "Dropping long header with invalid client_connection_id " - << client_connection_id; - return; + if (version_present) { + if (client_connection_id.length() != kQuicDefaultConnectionIdLength) { + QUIC_DLOG(ERROR) + << "Dropping long header with invalid client_connection_id " + << client_connection_id; + return; + } + owner_->RegisterClientConnectionId(client_connection_id, this); } - owner_->RegisterClientConnectionId(client_connection_id, this); - } - WriteResult write_result = connection()->writer()->WritePacket( - packet.data(), packet.size(), connection()->self_address().host(), - server_address, nullptr); - QUIC_DVLOG(1) << "Got " << write_result << " for " << packet.size() - << " bytes to " << server_address; + WriteResult write_result = connection()->writer()->WritePacket( + packet.data(), packet.size(), connection()->self_address().host(), + target_server_address, nullptr); + QUIC_DVLOG(1) << "Got " << write_result << " for " << packet.size() + << " bytes to " << target_server_address; + return; + } + QUICHE_DCHECK_EQ(masque_mode_, MasqueMode::kOpen); + QuicSpdySession::OnMessageReceived(message); } void MasqueServerSession::OnMessageAcked(QuicMessageId message_id, @@ -73,17 +154,131 @@ void MasqueServerSession::OnMessageLost(QuicMessageId message_id) { } void MasqueServerSession::OnConnectionClosed( - const QuicConnectionCloseFrame& /*frame*/, - ConnectionCloseSource /*source*/) { + const QuicConnectionCloseFrame& frame, + ConnectionCloseSource source) { + QuicSimpleServerSession::OnConnectionClosed(frame, source); QUIC_DLOG(INFO) << "Closing connection for " << connection_id(); masque_server_backend_->RemoveBackendClient(connection_id()); + // Clearing this state will close all sockets. + connect_udp_server_states_.clear(); +} + +void MasqueServerSession::OnStreamClosed(QuicStreamId stream_id) { + connect_udp_server_states_.remove_if( + [stream_id](const ConnectUdpServerState& connect_udp) { + return connect_udp.stream_id() == stream_id; + }); + + QuicSimpleServerSession::OnStreamClosed(stream_id); } std::unique_ptr<QuicBackendResponse> MasqueServerSession::HandleMasqueRequest( const std::string& masque_path, - const spdy::Http2HeaderBlock& /*request_headers*/, + const spdy::Http2HeaderBlock& request_headers, const std::string& request_body, - QuicSimpleServerBackend::RequestHandler* /*request_handler*/) { + QuicSimpleServerBackend::RequestHandler* request_handler) { + if (masque_mode_ != MasqueMode::kLegacy) { + auto path_pair = request_headers.find(":path"); + auto scheme_pair = request_headers.find(":scheme"); + auto method_pair = request_headers.find(":method"); + auto authority_pair = request_headers.find(":authority"); + if (path_pair == request_headers.end()) { + QUIC_DLOG(ERROR) << "MASQUE request is missing :path"; + return CreateBackendErrorResponse("400", "Missing :path"); + } + if (scheme_pair == request_headers.end()) { + QUIC_DLOG(ERROR) << "MASQUE request is missing :scheme"; + return CreateBackendErrorResponse("400", "Missing :scheme"); + } + if (method_pair == request_headers.end()) { + QUIC_DLOG(ERROR) << "MASQUE request is missing :method"; + return CreateBackendErrorResponse("400", "Missing :method"); + } + if (authority_pair == request_headers.end()) { + QUIC_DLOG(ERROR) << "MASQUE request is missing :authority"; + return CreateBackendErrorResponse("400", "Missing :authority"); + } + absl::string_view path = path_pair->second; + absl::string_view scheme = scheme_pair->second; + absl::string_view method = method_pair->second; + absl::string_view authority = authority_pair->second; + if (path.empty()) { + QUIC_DLOG(ERROR) << "MASQUE request with empty path"; + return CreateBackendErrorResponse("400", "Empty path"); + } + 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<QuicDatagramFlowId> 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() != "/") { + QUIC_DLOG(ERROR) << "MASQUE request with bad authority \"" << authority + << "\""; + return CreateBackendErrorResponse("400", "Bad authority"); + } + + std::string port = absl::StrCat(url.port()); + addrinfo hint = {}; + hint.ai_protocol = IPPROTO_UDP; + + addrinfo* info_list = nullptr; + int result = + getaddrinfo(url.host().c_str(), port.c_str(), &hint, &info_list); + if (result != 0) { + QUIC_DLOG(ERROR) << "Failed to resolve " << authority << ": " + << gai_strerror(result); + return CreateBackendErrorResponse("500", "DNS resolution failed"); + } + + QUICHE_CHECK_NE(info_list, nullptr); + std::unique_ptr<addrinfo, void (*)(addrinfo*)> info_list_owned( + 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 + << " target_server_address=\"" << target_server_address + << "\""; + + FdWrapper fd_wrapper(target_server_address.host().AddressFamilyToInt()); + if (fd_wrapper.fd() == kQuicInvalidSocketFd) { + QUIC_DLOG(ERROR) << "Socket creation failed"; + return CreateBackendErrorResponse("500", "Socket creation failed"); + } + QuicSocketAddress any_v6_address(QuicIpAddress::Any6(), 0); + QuicUdpSocketApi socket_api; + if (!socket_api.Bind(fd_wrapper.fd(), any_v6_address)) { + QUIC_DLOG(ERROR) << "Socket bind failed"; + return CreateBackendErrorResponse("500", "Socket bind failed"); + } + epoll_server_->RegisterFDForRead(fd_wrapper.fd(), this); + + connect_udp_server_states_.emplace_back(ConnectUdpServerState( + *flow_id, request_handler->stream_id(), target_server_address, + fd_wrapper.extract_fd(), this)); + + spdy::Http2HeaderBlock response_headers; + response_headers[":status"] = "200"; + 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)); + response->set_body(""); + + return response; + } + QUIC_DLOG(INFO) << "MasqueServerSession handling MASQUE request"; if (masque_path == "init") { @@ -120,9 +315,177 @@ std::unique_ptr<QuicBackendResponse> MasqueServerSession::HandleMasqueRequest( void MasqueServerSession::HandlePacketFromServer( const ReceivedPacketInfo& packet_info) { QUIC_DVLOG(1) << "MasqueServerSession received " << packet_info; - compression_engine_.CompressAndSendPacket( - packet_info.packet.AsStringPiece(), packet_info.destination_connection_id, - packet_info.source_connection_id, packet_info.peer_address); + if (masque_mode_ == MasqueMode::kLegacy) { + compression_engine_.CompressAndSendPacket( + packet_info.packet.AsStringPiece(), + packet_info.destination_connection_id, packet_info.source_connection_id, + packet_info.peer_address); + return; + } + QUIC_LOG(ERROR) << "Ignoring packet from server in " << masque_mode_ + << " mode"; +} + +void MasqueServerSession::OnRegistration(QuicEpollServer* /*eps*/, + QuicUdpSocketFd fd, + int event_mask) { + QUIC_DVLOG(1) << "OnRegistration " << fd << " event_mask " << event_mask; +} + +void MasqueServerSession::OnModification(QuicUdpSocketFd fd, int event_mask) { + QUIC_DVLOG(1) << "OnModification " << fd << " event_mask " << event_mask; +} + +void MasqueServerSession::OnEvent(QuicUdpSocketFd fd, QuicEpollEvent* event) { + if ((event->in_events & EPOLLIN) == 0) { + QUIC_DVLOG(1) << "Ignoring OnEvent fd " << fd << " event mask " + << event->in_events; + return; + } + auto it = absl::c_find_if(connect_udp_server_states_, + [fd](const ConnectUdpServerState& connect_udp) { + return connect_udp.fd() == fd; + }); + if (it == connect_udp_server_states_.end()) { + QUIC_BUG(quic_bug_10974_1) << "Got unexpected event mask " + << event->in_events << " on unknown fd " << fd; + return; + } + QuicDatagramFlowId flow_id = it->flow_id(); + QuicSocketAddress expected_target_server_address = + it->target_server_address(); + QUICHE_DCHECK(expected_target_server_address.IsInitialized()); + QUIC_DVLOG(1) << "Received readable event on fd " << fd << " (mask " + << event->in_events << ") flow_id " << flow_id << " server " + << expected_target_server_address; + QuicUdpSocketApi socket_api; + BitMask64 packet_info_interested(QuicUdpPacketInfoBit::PEER_ADDRESS); + char packet_buffer[kMaxIncomingPacketSize]; + char control_buffer[kDefaultUdpPacketControlBufferSize]; + while (true) { + QuicUdpSocketApi::ReadPacketResult read_result; + read_result.packet_buffer = {packet_buffer, sizeof(packet_buffer)}; + read_result.control_buffer = {control_buffer, sizeof(control_buffer)}; + socket_api.ReadPacket(fd, packet_info_interested, &read_result); + if (!read_result.ok) { + // Most likely there is nothing left to read, break out of read loop. + break; + } + if (!read_result.packet_info.HasValue(QuicUdpPacketInfoBit::PEER_ADDRESS)) { + QUIC_BUG(quic_bug_10974_2) + << "Missing peer address when reading from fd " << fd; + continue; + } + if (read_result.packet_info.peer_address() != + expected_target_server_address) { + QUIC_DLOG(ERROR) << "Ignoring UDP packet on fd " << fd + << " from unexpected server address " + << read_result.packet_info.peer_address() + << " (expected " << expected_target_server_address + << ")"; + continue; + } + if (!connection()->connected()) { + QUIC_BUG(quic_bug_10974_3) + << "Unexpected incoming UDP packet on fd " << fd << " from " + << expected_target_server_address + << " because MASQUE connection is closed"; + return; + } + // The packet is valid, send it to the client in a DATAGRAM frame. + MessageStatus message_status = SendHttp3Datagram( + flow_id, absl::string_view(read_result.packet_buffer.buffer, + read_result.packet_buffer.buffer_len)); + QUIC_DVLOG(1) << "Sent UDP packet from " << expected_target_server_address + << " of length " << read_result.packet_buffer.buffer_len + << " with flow ID " << flow_id << " and got message status " + << MessageStatusToString(message_status); + } +} + +void MasqueServerSession::OnUnregistration(QuicUdpSocketFd fd, bool replaced) { + QUIC_DVLOG(1) << "OnUnregistration " << fd << " " << (replaced ? "" : "!") + << " replaced"; +} + +void MasqueServerSession::OnShutdown(QuicEpollServer* /*eps*/, + QuicUdpSocketFd fd) { + QUIC_DVLOG(1) << "OnShutdown " << fd; +} + +std::string MasqueServerSession::Name() const { + return std::string("MasqueServerSession-") + connection_id().ToString(); +} + +MasqueServerSession::ConnectUdpServerState::ConnectUdpServerState( + QuicDatagramFlowId flow_id, + QuicStreamId stream_id, + const QuicSocketAddress& target_server_address, + QuicUdpSocketFd fd, + MasqueServerSession* masque_session) + : flow_id_(flow_id), + stream_id_(stream_id), + target_server_address_(target_server_address), + fd_(fd), + masque_session_(masque_session) { + QUICHE_DCHECK_NE(fd_, kQuicInvalidSocketFd); + QUICHE_DCHECK_NE(masque_session_, nullptr); + masque_session_->RegisterHttp3FlowId(this->flow_id(), this); +} + +MasqueServerSession::ConnectUdpServerState::~ConnectUdpServerState() { + if (flow_id_.has_value()) { + masque_session_->UnregisterHttp3FlowId(flow_id()); + } + if (fd_ == kQuicInvalidSocketFd) { + return; + } + QuicUdpSocketApi socket_api; + QUIC_DLOG(INFO) << "Closing fd " << fd_; + masque_session_->epoll_server()->UnregisterFD(fd_); + socket_api.Destroy(fd_); +} + +MasqueServerSession::ConnectUdpServerState::ConnectUdpServerState( + MasqueServerSession::ConnectUdpServerState&& other) { + fd_ = kQuicInvalidSocketFd; + *this = std::move(other); +} + +MasqueServerSession::ConnectUdpServerState& +MasqueServerSession::ConnectUdpServerState::operator=( + MasqueServerSession::ConnectUdpServerState&& other) { + if (fd_ != kQuicInvalidSocketFd) { + QuicUdpSocketApi socket_api; + QUIC_DLOG(INFO) << "Closing fd " << fd_; + masque_session_->epoll_server()->UnregisterFD(fd_); + socket_api.Destroy(fd_); + } + flow_id_ = other.flow_id_; + stream_id_ = other.stream_id_; + target_server_address_ = other.target_server_address_; + fd_ = other.fd_; + masque_session_ = other.masque_session_; + other.fd_ = kQuicInvalidSocketFd; + other.flow_id_.reset(); + if (flow_id_.has_value()) { + masque_session_->UnregisterHttp3FlowId(flow_id()); + masque_session_->RegisterHttp3FlowId(flow_id(), this); + } + return *this; +} + +void MasqueServerSession::ConnectUdpServerState::OnHttp3Datagram( + QuicDatagramFlowId flow_id, + absl::string_view payload) { + QUICHE_DCHECK_EQ(flow_id, this->flow_id()); + QuicUdpSocketApi socket_api; + QuicUdpPacketInfo packet_info; + packet_info.SetPeerAddress(target_server_address_); + WriteResult write_result = socket_api.WritePacket( + fd_, payload.data(), payload.length(), packet_info); + QUIC_DVLOG(1) << "Wrote packet of length " << payload.length() << " to " + << target_server_address_ << " with result " << write_result; } } // namespace quic 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 9e43b63ccbc..14b8293e9b1 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 @@ -5,8 +5,12 @@ #ifndef QUICHE_QUIC_MASQUE_MASQUE_SERVER_SESSION_H_ #define QUICHE_QUIC_MASQUE_MASQUE_SERVER_SESSION_H_ +#include "quic/core/quic_types.h" +#include "quic/core/quic_udp_socket.h" #include "quic/masque/masque_compression_engine.h" #include "quic/masque/masque_server_backend.h" +#include "quic/masque/masque_utils.h" +#include "quic/platform/api/quic_epoll.h" #include "quic/platform/api/quic_export.h" #include "quic/tools/quic_simple_server_session.h" @@ -15,7 +19,8 @@ namespace quic { // QUIC server session for connection to MASQUE proxy. class QUIC_NO_EXPORT MasqueServerSession : public QuicSimpleServerSession, - public MasqueServerBackend::BackendClient { + public MasqueServerBackend::BackendClient, + public QuicEpollCallbackInterface { public: // Interface meant to be implemented by owner of this MasqueServerSession // instance. @@ -33,11 +38,13 @@ class QUIC_NO_EXPORT MasqueServerSession }; explicit MasqueServerSession( + MasqueMode masque_mode, const QuicConfig& config, const ParsedQuicVersionVector& supported_versions, QuicConnection* connection, QuicSession::Visitor* visitor, Visitor* owner, + QuicEpollServer* epoll_server, QuicCryptoServerStreamBase::Helper* helper, const QuicCryptoServerConfig* crypto_config, QuicCompressedCertsCache* compressed_certs_cache, @@ -54,6 +61,7 @@ class QUIC_NO_EXPORT MasqueServerSession void OnMessageLost(QuicMessageId message_id) override; void OnConnectionClosed(const QuicConnectionCloseFrame& frame, ConnectionCloseSource source) override; + void OnStreamClosed(QuicStreamId stream_id) override; // From MasqueServerBackend::BackendClient. std::unique_ptr<QuicBackendResponse> HandleMasqueRequest( @@ -62,13 +70,71 @@ class QUIC_NO_EXPORT MasqueServerSession const std::string& request_body, QuicSimpleServerBackend::RequestHandler* request_handler) override; + // From QuicEpollCallbackInterface. + void OnRegistration(QuicEpollServer* eps, + QuicUdpSocketFd fd, + int event_mask) override; + void OnModification(QuicUdpSocketFd fd, int event_mask) override; + void OnEvent(QuicUdpSocketFd fd, QuicEpollEvent* event) override; + void OnUnregistration(QuicUdpSocketFd fd, bool replaced) override; + void OnShutdown(QuicEpollServer* eps, QuicUdpSocketFd fd) override; + std::string Name() const override; + // Handle packet for client, meant to be called by MasqueDispatcher. void HandlePacketFromServer(const ReceivedPacketInfo& packet_info); + QuicEpollServer* epoll_server() const { return epoll_server_; } + private: + // State that the MasqueServerSession keeps for each CONNECT-UDP request. + class QUIC_NO_EXPORT ConnectUdpServerState + : public QuicSpdySession::Http3DatagramVisitor { + public: + // ConnectUdpServerState takes ownership of |fd|. It will unregister it + // from |epoll_server| and close the file descriptor when destructed. + explicit ConnectUdpServerState( + QuicDatagramFlowId flow_id, + QuicStreamId stream_id, + const QuicSocketAddress& target_server_address, + QuicUdpSocketFd fd, + MasqueServerSession* masque_session); + + ~ConnectUdpServerState(); + + // Disallow copy but allow move. + ConnectUdpServerState(const ConnectUdpServerState&) = delete; + ConnectUdpServerState(ConnectUdpServerState&&); + ConnectUdpServerState& operator=(const ConnectUdpServerState&) = delete; + ConnectUdpServerState& operator=(ConnectUdpServerState&&); + + QuicDatagramFlowId flow_id() const { + QUICHE_DCHECK(flow_id_.has_value()); + return *flow_id_; + } + QuicStreamId stream_id() const { return stream_id_; } + const QuicSocketAddress& target_server_address() const { + return target_server_address_; + } + QuicUdpSocketFd fd() const { return fd_; } + + // From QuicSpdySession::Http3DatagramVisitor. + void OnHttp3Datagram(QuicDatagramFlowId flow_id, + absl::string_view payload) override; + + private: + absl::optional<QuicDatagramFlowId> flow_id_; + QuicStreamId stream_id_; + QuicSocketAddress target_server_address_; + QuicUdpSocketFd fd_; // Owned. + MasqueServerSession* masque_session_; // Unowned. + }; + MasqueServerBackend* masque_server_backend_; // Unowned. Visitor* owner_; // Unowned. + QuicEpollServer* epoll_server_; // Unowned. MasqueCompressionEngine compression_engine_; + MasqueMode masque_mode_; + std::list<ConnectUdpServerState> connect_udp_server_states_; bool masque_initialized_ = false; }; diff --git a/chromium/net/third_party/quiche/src/quic/masque/masque_utils.cc b/chromium/net/third_party/quiche/src/quic/masque/masque_utils.cc index f4f77f5faa2..cd196029c6b 100644 --- a/chromium/net/third_party/quiche/src/quic/masque/masque_utils.cc +++ b/chromium/net/third_party/quiche/src/quic/masque/masque_utils.cc @@ -8,17 +8,16 @@ namespace quic { ParsedQuicVersionVector MasqueSupportedVersions() { QuicVersionInitializeSupportForIetfDraft(); - ParsedQuicVersion version = UnsupportedQuicVersion(); - for (const ParsedQuicVersion& vers : AllSupportedVersions()) { - // Find the first version that supports IETF QUIC. - if (vers.HasIetfQuicFrames() && vers.UsesTls()) { - version = vers; - break; + ParsedQuicVersionVector versions; + for (const ParsedQuicVersion& version : AllSupportedVersions()) { + // Use all versions that support IETF QUIC. + if (version.UsesHttp3()) { + QuicEnableVersion(version); + versions.push_back(version); } } - QUICHE_CHECK(version.IsKnown()); - QuicEnableVersion(version); - return {version}; + QUICHE_CHECK(!versions.empty()); + return versions; } QuicConfig MasqueEncapsulatedConfig() { @@ -27,4 +26,21 @@ QuicConfig MasqueEncapsulatedConfig() { return config; } +std::string MasqueModeToString(MasqueMode masque_mode) { + switch (masque_mode) { + case MasqueMode::kInvalid: + return "Invalid"; + case MasqueMode::kLegacy: + return "Legacy"; + case MasqueMode::kOpen: + return "Open"; + } + return absl::StrCat("Unknown(", static_cast<int>(masque_mode), ")"); +} + +std::ostream& operator<<(std::ostream& os, const MasqueMode& masque_mode) { + os << MasqueModeToString(masque_mode); + return os; +} + } // namespace quic 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 f151f5508cd..8113047768c 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,24 @@ QUIC_NO_EXPORT ParsedQuicVersionVector MasqueSupportedVersions(); QUIC_NO_EXPORT QuicConfig MasqueEncapsulatedConfig(); // Maximum packet size for encapsulated connections. -const QuicByteCount kMasqueMaxEncapsulatedPacketSize = 1300; +enum : QuicByteCount { kMasqueMaxEncapsulatedPacketSize = 1300 }; + +// Mode that MASQUE is operating in. +enum class MasqueMode : uint8_t { + kInvalid = 0, // Should never be used. + kLegacy = 1, // Legacy mode uses the legacy MASQUE protocol as documented in + // <https://tools.ietf.org/html/draft-schinazi-masque-protocol>. That version + // of MASQUE uses a custom application-protocol over HTTP/3, and also allows + // unauthenticated clients. + kOpen = 2, // Open mode uses the MASQUE HTTP CONNECT-UDP method as documented + // in <https://tools.ietf.org/html/draft-ietf-masque-connect-udp>. This mode + // allows unauthenticated clients (a more restricted mode will be added to + // this enum at a later date). +}; + +QUIC_NO_EXPORT std::string MasqueModeToString(MasqueMode masque_mode); +QUIC_NO_EXPORT std::ostream& operator<<(std::ostream& os, + const MasqueMode& masque_mode); } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h index 23bcb7b14ac..cffa6e52533 100644 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h @@ -5,11 +5,11 @@ #ifndef QUICHE_QUIC_PLATFORM_API_QUIC_BUG_TRACKER_H_ #define QUICHE_QUIC_PLATFORM_API_QUIC_BUG_TRACKER_H_ -#include "net/quic/platform/impl/quic_bug_tracker_impl.h" +#include "common/platform/api/quiche_bug_tracker.h" -#define QUIC_BUG QUIC_BUG_IMPL -#define QUIC_BUG_IF QUIC_BUG_IF_IMPL -#define QUIC_PEER_BUG QUIC_PEER_BUG_IMPL -#define QUIC_PEER_BUG_IF QUIC_PEER_BUG_IF_IMPL +#define QUIC_BUG QUICHE_BUG +#define QUIC_BUG_IF QUICHE_BUG_IF +#define QUIC_PEER_BUG QUICHE_PEER_BUG +#define QUIC_PEER_BUG_IF QUICHE_PEER_BUG_IF #endif // QUICHE_QUIC_PLATFORM_API_QUIC_BUG_TRACKER_H_ diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils_test.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils_test.cc index 7049715f74e..788cb2d5425 100644 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils_test.cc +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils_test.cc @@ -19,6 +19,9 @@ TEST_F(QuicHostnameUtilsTest, IsValidSNI) { // IP as SNI. EXPECT_FALSE(QuicHostnameUtils::IsValidSNI("192.168.0.1")); // SNI without any dot. + SetQuicReloadableFlag(quic_and_tls_allow_sni_without_dots, true); + EXPECT_TRUE(QuicHostnameUtils::IsValidSNI("somedomain")); + SetQuicReloadableFlag(quic_and_tls_allow_sni_without_dots, false); EXPECT_FALSE(QuicHostnameUtils::IsValidSNI("somedomain")); // Invalid by RFC2396 but unfortunately domains of this form exist. EXPECT_TRUE(QuicHostnameUtils::IsValidSNI("some_domain.com")); diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_ip_address.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_ip_address.cc index 95877f4c8e7..b2cb649f5f9 100644 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_ip_address.cc +++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_ip_address.cc @@ -23,7 +23,8 @@ static int ToPlatformAddressFamily(IpAddressFamily family) { case IpAddressFamily::IP_UNSPEC: return AF_UNSPEC; } - QUIC_BUG << "Invalid IpAddressFamily " << static_cast<int32_t>(family); + QUIC_BUG(quic_bug_10126_1) + << "Invalid IpAddressFamily " << static_cast<int32_t>(family); return AF_UNSPEC; } @@ -85,7 +86,8 @@ bool operator==(QuicIpAddress lhs, QuicIpAddress rhs) { case IpAddressFamily::IP_UNSPEC: return true; } - QUIC_BUG << "Invalid IpAddressFamily " << static_cast<int32_t>(lhs.family_); + QUIC_BUG(quic_bug_10126_2) + << "Invalid IpAddressFamily " << static_cast<int32_t>(lhs.family_); return false; } @@ -114,7 +116,8 @@ std::string QuicIpAddress::ToPackedString() const { case IpAddressFamily::IP_UNSPEC: return ""; } - QUIC_BUG << "Invalid IpAddressFamily " << static_cast<int32_t>(family_); + QUIC_BUG(quic_bug_10126_3) + << "Invalid IpAddressFamily " << static_cast<int32_t>(family_); return ""; } @@ -126,7 +129,8 @@ std::string QuicIpAddress::ToString() const { char buffer[INET6_ADDRSTRLEN] = {0}; const char* result = inet_ntop(AddressFamilyToInt(), address_.bytes, buffer, sizeof(buffer)); - QUIC_BUG_IF(result == nullptr) << "Failed to convert an IP address to string"; + QUIC_BUG_IF(quic_bug_10126_4, result == nullptr) + << "Failed to convert an IP address to string"; return buffer; } @@ -200,11 +204,12 @@ bool QuicIpAddress::IsIPv6() const { bool QuicIpAddress::InSameSubnet(const QuicIpAddress& other, int subnet_length) { if (!IsInitialized()) { - QUIC_BUG << "Attempting to do subnet matching on undefined address"; + QUIC_BUG(quic_bug_10126_5) + << "Attempting to do subnet matching on undefined address"; return false; } if ((IsIPv4() && subnet_length > 32) || (IsIPv6() && subnet_length > 128)) { - QUIC_BUG << "Subnet mask is out of bounds"; + QUIC_BUG(quic_bug_10126_6) << "Subnet mask is out of bounds"; return false; } diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h deleted file mode 100644 index 71045e1c30e..00000000000 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_PTR_UTIL_H_ -#define QUICHE_QUIC_PLATFORM_API_QUIC_PTR_UTIL_H_ - -#include <memory> -#include <utility> - -#include "net/quic/platform/impl/quic_ptr_util_impl.h" - -namespace quic { - -template <typename T> -std::unique_ptr<T> QuicWrapUnique(T* ptr) { - return QuicWrapUniqueImpl<T>(ptr); -} - -} // namespace quic - -#endif // QUICHE_QUIC_PLATFORM_API_QUIC_PTR_UTIL_H_ 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 38179e9fcf6..4c60c1e6da1 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 @@ -33,7 +33,8 @@ QuicSocketAddress::QuicSocketAddress(const struct sockaddr_storage& saddr) { break; } default: - QUIC_BUG << "Unknown address family passed: " << saddr.ss_family; + QUIC_BUG(quic_bug_10075_1) + << "Unknown address family passed: " << saddr.ss_family; break; } } @@ -48,7 +49,7 @@ QuicSocketAddress::QuicSocketAddress(const sockaddr* saddr, socklen_t len) { (saddr->sa_family == AF_INET6 && len < static_cast<socklen_t>(sizeof(sockaddr_in6))) || len > static_cast<socklen_t>(sizeof(storage))) { - QUIC_BUG << "Socket address of invalid length provided"; + QUIC_BUG(quic_bug_10075_2) << "Socket address of invalid length provided"; return; } memcpy(&storage, saddr, len); diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_uint128.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_uint128.h deleted file mode 100644 index 4f333ae131a..00000000000 --- a/chromium/net/third_party/quiche/src/quic/platform/api/quic_uint128.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_UINT128_H_ -#define QUICHE_QUIC_PLATFORM_API_QUIC_UINT128_H_ - -#include "net/quic/platform/impl/quic_uint128_impl.h" - -namespace quic { - -using QuicUint128 = QuicUint128Impl; -#define MakeQuicUint128(hi, lo) MakeQuicUint128Impl(hi, lo) -#define QuicUint128Low64(x) QuicUint128Low64Impl(x) -#define QuicUint128High64(x) QuicUint128High64Impl(x) - -} // namespace quic - -#endif // QUICHE_QUIC_PLATFORM_API_QUIC_UINT128_H_ diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/qbone_tunnel_info.cc b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/qbone_tunnel_info.cc new file mode 100644 index 00000000000..372cd2b2510 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/qbone_tunnel_info.cc @@ -0,0 +1,37 @@ +// Copyright (c) 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "quic/qbone/bonnet/qbone_tunnel_info.h" + +namespace quic { + +QuicIpAddress QboneTunnelInfo::GetAddress() { + QuicIpAddress no_address; + + NetlinkInterface::LinkInfo link_info{}; + if (!netlink_->GetLinkInfo(ifname_, &link_info)) { + return no_address; + } + + std::vector<NetlinkInterface::AddressInfo> addresses; + if (!netlink_->GetAddresses(link_info.index, 0, &addresses, nullptr)) { + return no_address; + } + + quic::QuicIpAddress link_local_subnet; + if (!link_local_subnet.FromString("FE80::")) { + return no_address; + } + + for (const auto& address : addresses) { + if (address.interface_address.IsInitialized() && + !link_local_subnet.InSameSubnet(address.interface_address, 10)) { + return address.interface_address; + } + } + + return no_address; +} + +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/bonnet/qbone_tunnel_info.h b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/qbone_tunnel_info.h new file mode 100644 index 00000000000..6ca6ae8bff6 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/qbone/bonnet/qbone_tunnel_info.h @@ -0,0 +1,29 @@ +// Copyright (c) 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_QBONE_BONNET_QBONE_TUNNEL_INFO_H_ +#define QUICHE_QUIC_QBONE_BONNET_QBONE_TUNNEL_INFO_H_ + +#include "quic/platform/api/quic_ip_address.h" +#include "quic/qbone/platform/netlink_interface.h" + +namespace quic { + +class QboneTunnelInfo { + public: + QboneTunnelInfo(std::string ifname, NetlinkInterface* netlink) + : ifname_(std::move(ifname)), netlink_(netlink) {} + + // Returns the current QBONE tunnel address. Callers must use IsInitialized() + // to ensure the returned address is valid. + QuicIpAddress GetAddress(); + + private: + const std::string ifname_; + NetlinkInterface* netlink_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_QBONE_BONNET_QBONE_TUNNEL_INFO_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 3ee066a1b7b..3ca52e8d6d6 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 @@ -14,9 +14,13 @@ #include "quic/platform/api/quic_logging.h" #include "quic/qbone/platform/kernel_interface.h" +ABSL_FLAG(std::string, + qbone_client_tun_device_path, + "/dev/net/tun", + "The path to the QBONE client's TUN device."); + namespace quic { -const char kTapTunDevicePath[] = "/dev/net/tun"; const int kInvalidFd = -1; TunDevice::TunDevice(const std::string& interface_name, @@ -40,7 +44,8 @@ TunDevice::~TunDevice() { bool TunDevice::Init() { if (interface_name_.empty() || interface_name_.size() >= IFNAMSIZ) { - QUIC_BUG << "interface_name must be nonempty and shorter than " << IFNAMSIZ; + QUIC_BUG(quic_bug_10995_1) + << "interface_name must be nonempty and shorter than " << IFNAMSIZ; return false; } @@ -115,9 +120,11 @@ bool TunDevice::OpenDevice() { // CleanUpFileDescriptor nicer and less error-prone. // 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. - int fd = kernel_.open(kTapTunDevicePath, O_RDWR); + const std::string tun_device_path = + absl::GetFlag(FLAGS_qbone_client_tun_device_path); + int fd = kernel_.open(tun_device_path.c_str(), O_RDWR); if (fd < 0) { - QUIC_PLOG(WARNING) << "Failed to open " << kTapTunDevicePath; + QUIC_PLOG(WARNING) << "Failed to open " << tun_device_path; CleanUpFileDescriptor(); return false; } diff --git a/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink_interface.h b/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink_interface.h index eabfe078665..0794b1d5e4c 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink_interface.h +++ b/chromium/net/third_party/quiche/src/quic/qbone/platform/netlink_interface.h @@ -5,6 +5,8 @@ #ifndef QUICHE_QUIC_QBONE_PLATFORM_NETLINK_INTERFACE_H_ #define QUICHE_QUIC_QBONE_PLATFORM_NETLINK_INTERFACE_H_ +#include <linux/rtnetlink.h> + #include "quic/platform/api/quic_ip_address.h" #include "quic/qbone/platform/ip_range.h" 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 58b03d38838..16d2a19dc60 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 @@ -196,8 +196,8 @@ void CreateIfaddrmsg(struct nlmsghdr* nlm, family = AF_INET6; break; default: - QUIC_BUG << absl::StrCat("unexpected address family: ", - ip.address_family()); + QUIC_BUG(quic_bug_11034_1) + << absl::StrCat("unexpected address family: ", ip.address_family()); family = AF_UNSPEC; } auto* msg = reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(nlm)); diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_client.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client.cc index 464c0532589..d0daf95c419 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_client.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client.cc @@ -11,6 +11,7 @@ #include "quic/core/quic_epoll_connection_helper.h" #include "quic/platform/api/quic_epoll.h" #include "quic/platform/api/quic_exported_stats.h" +#include "quic/platform/api/quic_testvalue.h" #include "quic/qbone/qbone_stream.h" namespace quic { @@ -20,7 +21,7 @@ std::unique_ptr<QuicClientBase::NetworkHelper> CreateNetworkHelper( QboneClient* client) { std::unique_ptr<QuicClientBase::NetworkHelper> helper = std::make_unique<QuicClientEpollNetworkHelper>(epoll_server, client); - testing::testvalue::Adjust("QboneClient/network_helper", &helper); + quic::AdjustTestValue("QboneClient/network_helper", &helper); return helper; } } // namespace diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_session.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_session.cc index f3b62904d6b..1f8ca0ab788 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_session.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_session.cc @@ -72,7 +72,8 @@ int QboneClientSession::GetNumReceivedServerConfigUpdates() const { bool QboneClientSession::SendServerRequest(const QboneServerRequest& request) { if (!control_stream_) { - QUIC_BUG << "Cannot send server request before control stream is created."; + QUIC_BUG(quic_bug_11056_1) + << "Cannot send server request before control stream is created."; return false; } return control_stream_->SendRequest(request); diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_test.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_test.cc index 75a3a1e9216..91bb82d7449 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_test.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_client_test.cc @@ -127,7 +127,8 @@ class QuicQboneDispatcher : public QuicDispatcher { const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, absl::string_view alpn, - const quic::ParsedQuicVersion& version) override { + const quic::ParsedQuicVersion& version, + absl::string_view sni) override { QUICHE_CHECK_EQ(alpn, "qbone"); QuicConnection* connection = new QuicConnection( id, self_address, peer_address, helper(), alarm_factory(), writer(), diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_stream.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_stream.cc index 132ecb7a232..944257f7af2 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_control_stream.cc @@ -48,12 +48,13 @@ void QboneControlStreamBase::OnDataAvailable() { bool QboneControlStreamBase::SendMessage(const proto2::Message& proto) { std::string tmp; if (!proto.SerializeToString(&tmp)) { - QUIC_BUG << "Failed to serialize QboneControlRequest"; + QUIC_BUG(quic_bug_11023_1) << "Failed to serialize QboneControlRequest"; return false; } if (tmp.size() > kuint16max) { - QUIC_BUG << "QboneControlRequest too large: " << tmp.size() << " > " - << kuint16max; + QUIC_BUG(quic_bug_11023_2) + << "QboneControlRequest too large: " << tmp.size() << " > " + << kuint16max; return false; } uint16_t size = tmp.size(); diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.cc index c9935577a86..7b45ed1050b 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.cc @@ -62,7 +62,8 @@ QbonePacketProcessor::Filter::FilterPacket(Direction direction, void QbonePacketProcessor::ProcessPacket(std::string* packet, Direction direction) { if (QUIC_PREDICT_FALSE(!IsValid())) { - QUIC_BUG << "QuicPacketProcessor is invoked in an invalid state."; + QUIC_BUG(quic_bug_11024_1) + << "QuicPacketProcessor is invoked in an invalid state."; stats_->OnPacketDroppedSilently(direction); return; } @@ -120,8 +121,9 @@ QbonePacketProcessor::ProcessIPv6HeaderAndFilter(std::string* packet, // Sanity-check the bounds. if (packet_data >= *transport_data || header_size > packet->size() || header_size < kIPv6HeaderSize) { - QUIC_BUG << "Invalid pointers encountered in " - "QbonePacketProcessor::ProcessPacket. Dropping the packet"; + QUIC_BUG(quic_bug_11024_2) + << "Invalid pointers encountered in " + "QbonePacketProcessor::ProcessPacket. Dropping the packet"; return ProcessingResult::SILENT_DROP; } diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_server_session.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_server_session.cc index 76a460c99d1..3b80dab9d48 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_server_session.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_server_session.cc @@ -66,7 +66,8 @@ void QboneServerSession::Initialize() { bool QboneServerSession::SendClientRequest(const QboneClientRequest& request) { if (!control_stream_) { - QUIC_BUG << "Cannot send client request before control stream is created."; + QUIC_BUG(quic_bug_11026_1) + << "Cannot send client request before control stream is created."; return false; } return control_stream_->SendRequest(request); diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_base.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_base.cc index 607c542591f..e984196faf0 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_base.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_session_base.cc @@ -15,10 +15,11 @@ #include "quic/core/quic_types.h" #include "quic/platform/api/quic_exported_stats.h" #include "quic/platform/api/quic_logging.h" +#include "quic/platform/api/quic_testvalue.h" #include "quic/qbone/platform/icmp_packet.h" #include "quic/qbone/qbone_constants.h" -ABSL_FLAG( +DEFINE_QUIC_COMMAND_LINE_FLAG( bool, qbone_close_ephemeral_frames, true, @@ -133,7 +134,8 @@ QuicStream* QboneSessionBase::ActivateDataStream( void QboneSessionBase::SendPacketToPeer(absl::string_view packet) { if (crypto_stream_ == nullptr) { - QUIC_BUG << "Attempting to send packet before encryption established"; + QUIC_BUG(quic_bug_10987_1) + << "Attempting to send packet before encryption established"; return; } @@ -147,7 +149,8 @@ void QboneSessionBase::SendPacketToPeer(absl::string_view packet) { break; case MESSAGE_STATUS_TOO_LARGE: { if (packet.size() < sizeof(ip6_hdr)) { - QUIC_BUG << "Dropped malformed packet: IPv6 header too short"; + QUIC_BUG(quic_bug_10987_2) + << "Dropped malformed packet: IPv6 header too short"; break; } auto* header = reinterpret_cast<const ip6_hdr*>(packet.begin()); @@ -164,16 +167,17 @@ void QboneSessionBase::SendPacketToPeer(absl::string_view packet) { break; } case MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED: - QUIC_BUG << "MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED"; + QUIC_BUG(quic_bug_10987_3) + << "MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED"; break; case MESSAGE_STATUS_UNSUPPORTED: - QUIC_BUG << "MESSAGE_STATUS_UNSUPPORTED"; + QUIC_BUG(quic_bug_10987_4) << "MESSAGE_STATUS_UNSUPPORTED"; break; case MESSAGE_STATUS_BLOCKED: - QUIC_BUG << "MESSAGE_STATUS_BLOCKED"; + QUIC_BUG(quic_bug_10987_5) << "MESSAGE_STATUS_BLOCKED"; break; case MESSAGE_STATUS_INTERNAL_ERROR: - QUIC_BUG << "MESSAGE_STATUS_INTERNAL_ERROR"; + QUIC_BUG(quic_bug_10987_6) << "MESSAGE_STATUS_INTERNAL_ERROR"; break; } return; @@ -182,7 +186,7 @@ void QboneSessionBase::SendPacketToPeer(absl::string_view packet) { // QBONE streams are ephemeral. QuicStream* stream = CreateOutgoingStream(); if (!stream) { - QUIC_BUG << "Failed to create an outgoing QBONE stream."; + QUIC_BUG(quic_bug_10987_7) << "Failed to create an outgoing QBONE stream."; return; } @@ -209,7 +213,7 @@ uint64_t QboneSessionBase::GetNumFallbackToStream() const { void QboneSessionBase::set_writer(QbonePacketWriter* writer) { writer_ = writer; - testing::testvalue::Adjust("quic_QbonePacketWriter", &writer_); + quic::AdjustTestValue("quic_QbonePacketWriter", &writer_); } } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream.cc b/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream.cc index 43aab4d001b..5aac3e73ada 100644 --- a/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/qbone/qbone_stream.cc @@ -11,7 +11,10 @@ #include "quic/qbone/qbone_constants.h" #include "quic/qbone/qbone_session_base.h" -ABSL_FLAG(int, qbone_stream_ttl_secs, 3, "The QBONE Stream TTL in seconds."); +DEFINE_QUIC_COMMAND_LINE_FLAG(int, + qbone_stream_ttl_secs, + 3, + "The QBONE Stream TTL in seconds."); namespace quic { 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 cf7032103b9..17f9194abfe 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 @@ -34,7 +34,7 @@ QuicTransportClientSession::QuicTransportClientSession( const GURL& url, QuicCryptoClientConfig* crypto_config, url::Origin origin, - ClientVisitor* visitor, + WebTransportVisitor* visitor, std::unique_ptr<QuicDatagramQueue::Observer> datagram_observer) : QuicSession(connection, owner, @@ -46,7 +46,7 @@ QuicTransportClientSession::QuicTransportClientSession( origin_(origin), visitor_(visitor) { for (const ParsedQuicVersion& version : supported_versions) { - QUIC_BUG_IF(version.handshake_protocol != PROTOCOL_TLS1_3) + QUIC_BUG_IF(quic_bug_12035_1, version.handshake_protocol != PROTOCOL_TLS1_3) << "QuicTransport requires TLS 1.3 handshake"; } crypto_stream_ = std::make_unique<QuicCryptoClientStream>( @@ -58,7 +58,8 @@ QuicTransportClientSession::QuicTransportClientSession( void QuicTransportClientSession::OnAlpnSelected(absl::string_view alpn) { // Defense in-depth: ensure the ALPN selected is the desired one. if (alpn != QuicTransportAlpn()) { - QUIC_BUG << "QuicTransport negotiated non-QuicTransport ALPN: " << alpn; + QUIC_BUG(quic_bug_10881_1) + << "QuicTransport negotiated non-QuicTransport ALPN: " << alpn; connection()->CloseConnection( QUIC_INTERNAL_ERROR, "QuicTransport negotiated non-QuicTransport ALPN", ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); @@ -117,7 +118,8 @@ QuicTransportClientSession::AcceptIncomingUnidirectionalStream() { QuicTransportStream* QuicTransportClientSession::OpenOutgoingBidirectionalStream() { if (!CanOpenNextOutgoingBidirectionalStream()) { - QUIC_BUG << "Attempted to open a stream in violation of flow control"; + QUIC_BUG(quic_bug_10881_2) + << "Attempted to open a stream in violation of flow control"; return nullptr; } return CreateStream(GetNextOutgoingBidirectionalStreamId()); @@ -126,7 +128,8 @@ QuicTransportClientSession::OpenOutgoingBidirectionalStream() { QuicTransportStream* QuicTransportClientSession::OpenOutgoingUnidirectionalStream() { if (!CanOpenNextOutgoingUnidirectionalStream()) { - QUIC_BUG << "Attempted to open a stream in violation of flow control"; + QUIC_BUG(quic_bug_10881_3) + << "Attempted to open a stream in violation of flow control"; return nullptr; } return CreateStream(GetNextOutgoingUnidirectionalStreamId()); @@ -142,7 +145,7 @@ QuicTransportStream* QuicTransportClientSession::CreateStream(QuicStreamId id) { std::string QuicTransportClientSession::SerializeClientIndication() { std::string serialized_origin = origin_.Serialize(); if (serialized_origin.size() > std::numeric_limits<uint16_t>::max()) { - QUIC_BUG << "Client origin too long"; + QUIC_BUG(quic_bug_10881_4) << "Client origin too long"; connection()->CloseConnection( QUIC_INTERNAL_ERROR, "Client origin too long", ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); @@ -182,23 +185,25 @@ std::string QuicTransportClientSession::SerializeClientIndication() { writer.WriteUInt16( static_cast<uint16_t>(QuicTransportClientIndicationKeys::kPath)) && writer.WriteUInt16(path.size()) && writer.WriteStringPiece(path); - QUIC_BUG_IF(!success) << "Failed to serialize client indication"; - QUIC_BUG_IF(writer.length() != buffer.length()) + QUIC_BUG_IF(quic_bug_10881_5, !success) + << "Failed to serialize client indication"; + QUIC_BUG_IF(quic_bug_12035_2, writer.length() != buffer.length()) << "Serialized client indication has length different from expected"; return buffer; } void QuicTransportClientSession::SendClientIndication() { if (!crypto_stream_->encryption_established()) { - QUIC_BUG << "Client indication may only be sent once the encryption is " - "established."; + QUIC_BUG(quic_bug_10881_6) + << "Client indication may only be sent once the encryption is " + "established."; connection()->CloseConnection( QUIC_INTERNAL_ERROR, "Attempted to send client indication unencrypted", ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return; } if (ready_) { - QUIC_BUG << "Client indication may only be sent once."; + QUIC_BUG(quic_bug_10881_7) << "Client indication may only be sent once."; connection()->CloseConnection( QUIC_INTERNAL_ERROR, "Attempted to send client indication twice", ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); @@ -208,7 +213,8 @@ void QuicTransportClientSession::SendClientIndication() { auto client_indication_owned = std::make_unique<ClientIndication>( /*stream_id=*/GetNextOutgoingUnidirectionalStreamId(), this, /*is_static=*/false, WRITE_UNIDIRECTIONAL); - QUIC_BUG_IF(client_indication_owned->id() != ClientIndicationStream()) + QUIC_BUG_IF(quic_bug_12035_3, + client_indication_owned->id() != ClientIndicationStream()) << "Client indication stream is " << client_indication_owned->id() << " instead of expected " << ClientIndicationStream(); ClientIndication* client_indication = client_indication_owned.get(); @@ -220,7 +226,8 @@ void QuicTransportClientSession::SendClientIndication() { // Defense in depth: never set the ready bit unless ALPN has been confirmed. if (!alpn_received_) { - QUIC_BUG << "ALPN confirmation missing after handshake complete"; + QUIC_BUG(quic_bug_10881_8) + << "ALPN confirmation missing after handshake complete"; connection()->CloseConnection( QUIC_INTERNAL_ERROR, "ALPN confirmation missing after handshake complete", 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 d01903c388d..6f6d091d71a 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 @@ -21,6 +21,7 @@ #include "quic/core/quic_session.h" #include "quic/core/quic_stream.h" #include "quic/core/quic_versions.h" +#include "quic/core/web_transport_interface.h" #include "quic/platform/api/quic_bug_tracker.h" #include "quic/platform/api/quic_containers.h" #include "quic/quic_transport/quic_transport_protocol.h" @@ -32,31 +33,10 @@ namespace quic { // A client session for the QuicTransport protocol. class QUIC_EXPORT_PRIVATE QuicTransportClientSession : public QuicSession, + public WebTransportSession, public QuicTransportSessionInterface, public QuicCryptoClientStream::ProofHandler { public: - class QUIC_EXPORT_PRIVATE ClientVisitor { - public: - virtual ~ClientVisitor() {} - - // Notifies the visitor when the client indication has been sent and the - // connection is ready to exchange application data. - virtual void OnSessionReady() = 0; - - // Notifies the visitor when a new stream has been received. The stream in - // question can be retrieved using AcceptIncomingBidirectionalStream() or - // AcceptIncomingUnidirectionalStream(). - virtual void OnIncomingBidirectionalStreamAvailable() = 0; - virtual void OnIncomingUnidirectionalStreamAvailable() = 0; - - // Notifies the visitor when a new datagram has been received. - virtual void OnDatagramReceived(absl::string_view datagram) = 0; - - // Notifies the visitor that a new outgoing stream can now be created. - virtual void OnCanCreateNewOutgoingBidirectionalStream() = 0; - virtual void OnCanCreateNewOutgoingUnidirectionalStream() = 0; - }; - QuicTransportClientSession( QuicConnection* connection, Visitor* owner, @@ -65,7 +45,7 @@ class QUIC_EXPORT_PRIVATE QuicTransportClientSession const GURL& url, QuicCryptoClientConfig* crypto_config, url::Origin origin, - ClientVisitor* visitor, + WebTransportVisitor* visitor, std::unique_ptr<QuicDatagramQueue::Observer> datagram_observer); std::vector<std::string> GetAlpnsToOffer() const override { @@ -93,8 +73,9 @@ class QUIC_EXPORT_PRIVATE QuicTransportClientSession QuicStream* CreateIncomingStream(QuicStreamId id) override; QuicStream* CreateIncomingStream(PendingStream* /*pending*/) override { - QUIC_BUG << "QuicTransportClientSession::CreateIncomingStream(" - "PendingStream) not implemented"; + QUIC_BUG(quic_bug_10890_1) + << "QuicTransportClientSession::CreateIncomingStream(" + "PendingStream) not implemented"; return nullptr; } @@ -105,14 +86,26 @@ class QUIC_EXPORT_PRIVATE QuicTransportClientSession // 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. - QuicTransportStream* AcceptIncomingBidirectionalStream(); - QuicTransportStream* AcceptIncomingUnidirectionalStream(); + QuicTransportStream* AcceptIncomingBidirectionalStream() override; + QuicTransportStream* AcceptIncomingUnidirectionalStream() override; + + bool CanOpenNextOutgoingBidirectionalStream() override { + return QuicSession::CanOpenNextOutgoingBidirectionalStream(); + } + bool CanOpenNextOutgoingUnidirectionalStream() override { + return QuicSession::CanOpenNextOutgoingUnidirectionalStream(); + } + QuicTransportStream* OpenOutgoingBidirectionalStream() override; + QuicTransportStream* OpenOutgoingUnidirectionalStream() override; - using QuicSession::CanOpenNextOutgoingBidirectionalStream; - using QuicSession::CanOpenNextOutgoingUnidirectionalStream; - QuicTransportStream* OpenOutgoingBidirectionalStream(); - QuicTransportStream* OpenOutgoingUnidirectionalStream(); + MessageStatus SendOrQueueDatagram(QuicMemSlice datagram) override { + return datagram_queue()->SendOrQueueDatagram(std::move(datagram)); + } + void SetDatagramMaxTimeInQueue(QuicTime::Delta max_time_in_queue) override { + datagram_queue()->SetMaxTimeInQueue(max_time_in_queue); + } + // For unit tests. using QuicSession::datagram_queue; // QuicCryptoClientStream::ProofHandler implementation. @@ -128,7 +121,7 @@ class QUIC_EXPORT_PRIVATE QuicTransportClientSession // This method should never be called, since the stream is client-initiated // unidirectional. void OnDataAvailable() override { - QUIC_BUG << "Received data on a write-only stream"; + QUIC_BUG(quic_bug_10890_2) << "Received data on a write-only stream"; } }; @@ -146,7 +139,7 @@ class QUIC_EXPORT_PRIVATE QuicTransportClientSession std::unique_ptr<QuicCryptoClientStream> crypto_stream_; GURL url_; url::Origin origin_; - ClientVisitor* visitor_; // not owned + WebTransportVisitor* visitor_; // not owned bool client_indication_sent_ = false; bool alpn_received_ = false; bool ready_ = false; 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 1b42020d535..36607b3918e 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 @@ -140,12 +140,6 @@ class QuicTransportServerEndpoint : public QuicTransportEndpointBase { QuicTransportSimpleServerSession session_; }; -std::unique_ptr<MockStreamVisitor> VisitorExpectingFin() { - auto visitor = std::make_unique<MockStreamVisitor>(); - EXPECT_CALL(*visitor, OnFinRead()); - return visitor; -} - constexpr QuicBandwidth kClientBandwidth = QuicBandwidth::FromKBitsPerSecond(10000); constexpr QuicTime::Delta kClientPropagationDelay = @@ -286,7 +280,9 @@ TEST_F(QuicTransportIntegrationTest, EchoBidirectionalStreams) { [stream]() { return stream->ReadableBytes() == strlen("Hello!"); }, kDefaultTimeout)); std::string received; - EXPECT_EQ(stream->Read(&received), strlen("Hello!")); + WebTransportStream::ReadResult result = stream->Read(&received); + EXPECT_EQ(result.bytes_read, strlen("Hello!")); + EXPECT_FALSE(result.fin); EXPECT_EQ(received, "Hello!"); EXPECT_TRUE(stream->SendFin()); @@ -325,8 +321,9 @@ TEST_F(QuicTransportIntegrationTest, EchoUnidirectionalStreams) { client_->session()->AcceptIncomingUnidirectionalStream(); ASSERT_TRUE(reply != nullptr); std::string buffer; - reply->set_visitor(VisitorExpectingFin()); - EXPECT_GT(reply->Read(&buffer), 0u); + WebTransportStream::ReadResult result = reply->Read(&buffer); + EXPECT_GT(result.bytes_read, 0u); + EXPECT_TRUE(result.fin); EXPECT_EQ(buffer, "Stream Two"); // Reset reply-related variables. @@ -339,8 +336,9 @@ TEST_F(QuicTransportIntegrationTest, EchoUnidirectionalStreams) { [&stream_received]() { return stream_received; }, kDefaultTimeout)); reply = client_->session()->AcceptIncomingUnidirectionalStream(); ASSERT_TRUE(reply != nullptr); - reply->set_visitor(VisitorExpectingFin()); - EXPECT_GT(reply->Read(&buffer), 0u); + result = reply->Read(&buffer); + EXPECT_GT(result.bytes_read, 0u); + EXPECT_TRUE(result.fin); EXPECT_EQ(buffer, "Stream One"); } @@ -349,8 +347,7 @@ TEST_F(QuicTransportIntegrationTest, EchoDatagram) { WireUpEndpoints(); RunHandshake(); - client_->session()->datagram_queue()->SendOrQueueDatagram( - MemSliceFromString("test")); + client_->session()->SendOrQueueDatagram(MemSliceFromString("test")); bool datagram_received = false; EXPECT_CALL(*client_->visitor(), OnDatagramReceived(Eq("test"))) @@ -368,11 +365,10 @@ TEST_F(QuicTransportIntegrationTest, EchoALotOfDatagrams) { RunHandshake(); // Set the datagrams to effectively never expire. - client_->session()->datagram_queue()->SetMaxTimeInQueue(10000 * kRtt); + client_->session()->SetDatagramMaxTimeInQueue(10000 * kRtt); for (int i = 0; i < 1000; i++) { - client_->session()->datagram_queue()->SendOrQueueDatagram( - MemSliceFromString(std::string( - client_->session()->GetGuaranteedLargestMessagePayload(), 'a'))); + client_->session()->SendOrQueueDatagram(MemSliceFromString(std::string( + client_->session()->GetGuaranteedLargestMessagePayload(), 'a'))); } size_t received = 0; diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.cc index e2cc59dc2a1..f7c8c0f949d 100644 --- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.cc +++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.cc @@ -50,7 +50,7 @@ QuicTransportServerSession::QuicTransportServerSession( /*num_expected_unidirectional_static_streams*/ 0), visitor_(visitor) { for (const ParsedQuicVersion& version : supported_versions) { - QUIC_BUG_IF(version.handshake_protocol != PROTOCOL_TLS1_3) + QUIC_BUG_IF(quic_bug_12025_1, version.handshake_protocol != PROTOCOL_TLS1_3) << "QuicTransport requires TLS 1.3 handshake"; } diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h index 485f3268b2b..070cfdd5511 100644 --- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h +++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h @@ -66,8 +66,9 @@ class QUIC_EXPORT_PRIVATE QuicTransportServerSession QuicStream* CreateIncomingStream(QuicStreamId id) override; QuicStream* CreateIncomingStream(PendingStream* /*pending*/) override { - QUIC_BUG << "QuicTransportServerSession::CreateIncomingStream(" - "PendingStream) not implemented"; + QUIC_BUG(quic_bug_10884_1) + << "QuicTransportServerSession::CreateIncomingStream(" + "PendingStream) not implemented"; return nullptr; } diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.cc index e27f4692f9d..4def1dead0f 100644 --- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.cc +++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.cc @@ -25,31 +25,24 @@ QuicTransportStream::QuicTransportStream( session->connection()->perspective(), session->IsIncomingStream(id), session->version())), + adapter_(session, this, sequencer()), session_interface_(session_interface) {} -size_t QuicTransportStream::Read(char* buffer, size_t buffer_size) { +WebTransportStream::ReadResult QuicTransportStream::Read(char* buffer, + size_t buffer_size) { if (!session_interface_->IsSessionReady()) { - return 0; + return ReadResult{0, false}; } - iovec iov; - iov.iov_base = buffer; - iov.iov_len = buffer_size; - const size_t result = sequencer()->Readv(&iov, 1); - if (sequencer()->IsClosed()) { - MaybeNotifyFinRead(); - } - return result; + return adapter_.Read(buffer, buffer_size); } -size_t QuicTransportStream::Read(std::string* output) { - const size_t old_size = output->size(); - const size_t bytes_to_read = ReadableBytes(); - output->resize(old_size + bytes_to_read); - size_t bytes_read = Read(&(*output)[old_size], bytes_to_read); - QUICHE_DCHECK_EQ(bytes_to_read, bytes_read); - output->resize(old_size + bytes_read); - return bytes_read; +WebTransportStream::ReadResult QuicTransportStream::Read(std::string* output) { + if (!session_interface_->IsSessionReady()) { + return ReadResult{0, false}; + } + + return adapter_.Read(output); } bool QuicTransportStream::Write(absl::string_view data) { @@ -57,32 +50,7 @@ bool QuicTransportStream::Write(absl::string_view data) { return false; } - QuicUniqueBufferPtr buffer = MakeUniqueBuffer( - session()->connection()->helper()->GetStreamSendBufferAllocator(), - data.size()); - memcpy(buffer.get(), data.data(), data.size()); - QuicMemSlice memslice(std::move(buffer), data.size()); - QuicConsumedData consumed = - WriteMemSlices(QuicMemSliceSpan(&memslice), /*fin=*/false); - - if (consumed.bytes_consumed == data.size()) { - return true; - } - if (consumed.bytes_consumed == 0) { - return false; - } - // QuicTransportStream::Write() is an all-or-nothing write API. To achieve - // that property, it relies on WriteMemSlices() being an all-or-nothing API. - // If WriteMemSlices() fails to provide that guarantee, we have no way to - // communicate a partial write to the caller, and thus it's safer to just - // close the connection. - QUIC_BUG << "WriteMemSlices() unexpectedly partially consumed the input " - "data, provided: " - << data.size() << ", written: " << consumed.bytes_consumed; - OnUnrecoverableError( - QUIC_INTERNAL_ERROR, - "WriteMemSlices() unexpectedly partially consumed the input data"); - return false; + return adapter_.Write(data); } bool QuicTransportStream::SendFin() { @@ -90,16 +58,11 @@ bool QuicTransportStream::SendFin() { return false; } - QuicMemSlice empty; - QuicConsumedData consumed = - WriteMemSlices(QuicMemSliceSpan(&empty), /*fin=*/true); - QUICHE_DCHECK_EQ(consumed.bytes_consumed, 0u); - return consumed.fin_consumed; + return adapter_.SendFin(); } bool QuicTransportStream::CanWrite() const { - return session_interface_->IsSessionReady() && CanWriteNewData() && - !write_side_closed(); + return session_interface_->IsSessionReady() && adapter_.CanWrite(); } size_t QuicTransportStream::ReadableBytes() const { @@ -107,22 +70,11 @@ size_t QuicTransportStream::ReadableBytes() const { return 0; } - return sequencer()->ReadableBytes(); + return adapter_.ReadableBytes(); } void QuicTransportStream::OnDataAvailable() { - if (sequencer()->IsClosed()) { - MaybeNotifyFinRead(); - return; - } - - if (visitor_ == nullptr) { - return; - } - if (ReadableBytes() == 0) { - return; - } - visitor_->OnCanRead(); + adapter_.OnDataAvailable(); } void QuicTransportStream::OnCanWriteNewData() { @@ -131,18 +83,7 @@ void QuicTransportStream::OnCanWriteNewData() { if (!CanWrite()) { return; } - if (visitor_ != nullptr) { - visitor_->OnCanWrite(); - } -} - -void QuicTransportStream::MaybeNotifyFinRead() { - if (visitor_ == nullptr || fin_read_notified_) { - return; - } - fin_read_notified_ = true; - visitor_->OnFinRead(); - OnFinRead(); + adapter_.OnCanWriteNewData(); } } // namespace quic 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 70f947c544d..87ef5fb3b83 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 @@ -13,6 +13,8 @@ #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 { @@ -20,42 +22,47 @@ namespace quic { // QuicTransportStream is an extension of QuicStream that provides I/O interface // that is safe to use in the QuicTransport context. The interface ensures no // application data is processed before the client indication is processed. -class QUIC_EXPORT_PRIVATE QuicTransportStream : public QuicStream { +class QUIC_EXPORT_PRIVATE QuicTransportStream : public QuicStream, + public WebTransportStream { public: - class QUIC_EXPORT_PRIVATE Visitor { - public: - virtual ~Visitor() {} - virtual void OnCanRead() = 0; - virtual void OnFinRead() = 0; - virtual void OnCanWrite() = 0; - }; - QuicTransportStream(QuicStreamId id, QuicSession* session, QuicTransportSessionInterface* session_interface); // Reads at most |buffer_size| bytes into |buffer| and returns the number of // bytes actually read. - size_t Read(char* buffer, size_t buffer_size); + ReadResult Read(char* buffer, size_t buffer_size) override; // Reads all available data and appends it to the end of |output|. - size_t Read(std::string* output); + ReadResult Read(std::string* output) override; // Writes |data| into the stream. Returns true on success. - ABSL_MUST_USE_RESULT bool Write(absl::string_view data); + ABSL_MUST_USE_RESULT bool Write(absl::string_view data) override; // Sends the FIN on the stream. Returns true on success. - ABSL_MUST_USE_RESULT bool SendFin(); + ABSL_MUST_USE_RESULT bool SendFin() override; // Indicates whether it is possible to write into stream right now. - bool CanWrite() const; + bool CanWrite() const override; // Indicates the number of bytes that can be read from the stream. - size_t ReadableBytes() const; + size_t ReadableBytes() const override; // QuicSession method implementations. void OnDataAvailable() override; void OnCanWriteNewData() override; - Visitor* visitor() { return visitor_.get(); } - void set_visitor(std::unique_ptr<Visitor> visitor) { - visitor_ = std::move(visitor); + QuicStreamId GetStreamId() const override { return id(); } + + void ResetWithUserCode(QuicRstStreamErrorCode error) override { + adapter_.ResetWithUserCode(error); + } + void ResetDueToInternalError() override { + adapter_.ResetDueToInternalError(); + } + void MaybeResetDueToStreamObjectGone() override { + adapter_.MaybeResetDueToStreamObjectGone(); + } + + WebTransportStreamVisitor* visitor() override { return adapter_.visitor(); } + void SetVisitor(std::unique_ptr<WebTransportStreamVisitor> visitor) override { + adapter_.SetVisitor(std::move(visitor)); } protected: @@ -65,9 +72,8 @@ class QUIC_EXPORT_PRIVATE QuicTransportStream : public QuicStream { void MaybeNotifyFinRead(); + WebTransportStreamAdapter adapter_; QuicTransportSessionInterface* session_interface_; - std::unique_ptr<Visitor> visitor_ = nullptr; - bool fin_read_notified_ = false; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream_test.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream_test.cc index 75446eb30c7..b93e0eb430a 100644 --- a/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream_test.cc +++ b/chromium/net/third_party/quiche/src/quic/quic_transport/quic_transport_stream_test.cc @@ -6,11 +6,11 @@ #include <memory> +#include "absl/memory/memory.h" #include "absl/strings/string_view.h" #include "quic/core/crypto/null_encrypter.h" #include "quic/core/frames/quic_window_update_frame.h" #include "quic/platform/api/quic_expect_bug.h" -#include "quic/platform/api/quic_ptr_util.h" #include "quic/platform/api/quic_test.h" #include "quic/quic_transport/quic_transport_session_interface.h" #include "quic/test_tools/quic_config_peer.h" @@ -47,11 +47,11 @@ class QuicTransportStreamTest : public QuicTest { ENCRYPTION_FORWARD_SECURE, std::make_unique<NullEncrypter>(connection_->perspective())); stream_ = new QuicTransportStream(0, &session_, &interface_); - session_.ActivateStream(QuicWrapUnique(stream_)); + session_.ActivateStream(absl::WrapUnique(stream_)); auto visitor = std::make_unique<MockStreamVisitor>(); visitor_ = visitor.get(); - stream_->set_visitor(std::move(visitor)); + stream_->SetVisitor(std::move(visitor)); } void ReceiveStreamData(absl::string_view data, QuicStreamOffset offset) { @@ -81,8 +81,9 @@ TEST_F(QuicTransportStreamTest, ReadWhenNotReady) { EXPECT_CALL(interface_, IsSessionReady()).WillRepeatedly(Return(false)); ReceiveStreamData("test", 0); char buffer[4]; - QuicByteCount bytes_read = stream_->Read(buffer, sizeof(buffer)); - EXPECT_EQ(bytes_read, 0u); + WebTransportStream::ReadResult result = stream_->Read(buffer, sizeof(buffer)); + EXPECT_EQ(result.bytes_read, 0u); + EXPECT_FALSE(result.fin); } TEST_F(QuicTransportStreamTest, WriteWhenNotReady) { @@ -102,26 +103,47 @@ TEST_F(QuicTransportStreamTest, ReceiveData) { EXPECT_CALL(interface_, IsSessionReady()).WillRepeatedly(Return(true)); EXPECT_CALL(*visitor_, OnCanRead()); ReceiveStreamData("test", 0); + + std::string buffer; + WebTransportStream::ReadResult result = stream_->Read(&buffer); + EXPECT_EQ(result.bytes_read, 4u); + EXPECT_FALSE(result.fin); + EXPECT_EQ(buffer, "test"); } TEST_F(QuicTransportStreamTest, FinReadWithNoDataPending) { EXPECT_CALL(interface_, IsSessionReady()).WillRepeatedly(Return(true)); - EXPECT_CALL(*visitor_, OnFinRead()); + EXPECT_CALL(*visitor_, OnCanRead()); + QuicStreamFrame frame(0, true, 0, ""); stream_->OnStreamFrame(frame); + + std::string buffer; + WebTransportStream::ReadResult result = stream_->Read(&buffer); + EXPECT_EQ(result.bytes_read, 0u); + EXPECT_TRUE(result.fin); + EXPECT_EQ(buffer, ""); } TEST_F(QuicTransportStreamTest, FinReadWithDataPending) { EXPECT_CALL(interface_, IsSessionReady()).WillRepeatedly(Return(true)); EXPECT_CALL(*visitor_, OnCanRead()); - EXPECT_CALL(*visitor_, OnFinRead()).Times(0); QuicStreamFrame frame(0, true, 0, "test"); stream_->OnStreamFrame(frame); - EXPECT_CALL(*visitor_, OnFinRead()).Times(1); - std::string buffer; - ASSERT_EQ(stream_->Read(&buffer), 4u); + char buffer[2]; + WebTransportStream::ReadResult result = stream_->Read(buffer, sizeof(buffer)); + EXPECT_EQ(result.bytes_read, 2u); + EXPECT_FALSE(result.fin); + EXPECT_EQ(buffer[0], 't'); + EXPECT_EQ(buffer[1], 'e'); + + result = stream_->Read(buffer, sizeof(buffer)); + EXPECT_EQ(result.bytes_read, 2u); + EXPECT_TRUE(result.fin); + EXPECT_EQ(buffer[0], 's'); + EXPECT_EQ(buffer[1], 't'); } TEST_F(QuicTransportStreamTest, WritingTooMuchData) { diff --git a/chromium/net/third_party/quiche/src/quic/quic_transport/web_transport_fingerprint_proof_verifier.cc b/chromium/net/third_party/quiche/src/quic/quic_transport/web_transport_fingerprint_proof_verifier.cc index 974d25a588c..cbe55453af2 100644 --- a/chromium/net/third_party/quiche/src/quic/quic_transport/web_transport_fingerprint_proof_verifier.cc +++ b/chromium/net/third_party/quiche/src/quic/quic_transport/web_transport_fingerprint_proof_verifier.cc @@ -125,7 +125,7 @@ QuicAsyncStatus WebTransportFingerprintProofVerifier::VerifyProof( *error_details = "QUIC crypto certificate verification is not supported in " "WebTransportFingerprintProofVerifier"; - QUIC_BUG << *error_details; + QUIC_BUG(quic_bug_10879_1) << *error_details; *details = std::make_unique<Details>(Status::kInternalError); return QUIC_FAILURE; } @@ -191,7 +191,7 @@ bool WebTransportFingerprintProofVerifier::HasKnownFingerprint( const std::string fingerprint = ComputeSha256Fingerprint(der_certificate); for (const CertificateFingerprint& reference : fingerprints_) { if (reference.algorithm != CertificateFingerprint::kSha256) { - QUIC_BUG << "Unexpected non-SHA-256 hash"; + QUIC_BUG(quic_bug_10879_2) << "Unexpected non-SHA-256 hash"; continue; } if (fingerprint == reference.fingerprint) { 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 23f24d67cde..968b4140451 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 @@ -472,7 +472,7 @@ class MockCommonCertSets : public CommonCertSets { : cert_(cert), hash_(hash), index_(index) {} absl::string_view GetCommonHashes() const override { - QUIC_BUG << "not implemented"; + QUIC_BUG(quic_bug_10142_1) << "not implemented"; return absl::string_view(); } 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 5bba0728949..b239f7d140c 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 @@ -176,7 +176,8 @@ void FakeProofSourceHandle::SelectCertOperation::Run() { bool ok = chain && !chain->certs.empty(); callback_->OnSelectCertificateDone(ok, /*is_sync=*/false, chain.get()); } else { - QUIC_BUG << "Unexpected action: " << static_cast<int>(action_); + QUIC_BUG(quic_bug_10139_1) + << "Unexpected action: " << static_cast<int>(action_); } } @@ -200,7 +201,8 @@ void FakeProofSourceHandle::ComputeSignatureOperation::Run() { result.signature, std::move(result.details)); } else { - QUIC_BUG << "Unexpected action: " << static_cast<int>(action_); + QUIC_BUG(quic_bug_10139_2) + << "Unexpected action: " << static_cast<int>(action_); } } 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 aadba941939..12bf0801082 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 @@ -36,11 +36,12 @@ class MockTimeWaitListManager : public QuicTimeWaitListManager { MOCK_METHOD(void, ProcessPacket, - (const QuicSocketAddress& server_address, - const QuicSocketAddress& client_address, - QuicConnectionId connection_id, - PacketHeaderFormat header_format, - std::unique_ptr<QuicPerPacketContext> packet_context), + (const QuicSocketAddress&, + const QuicSocketAddress&, + QuicConnectionId, + PacketHeaderFormat, + size_t, + std::unique_ptr<QuicPerPacketContext>), (override)); MOCK_METHOD(void, @@ -61,6 +62,7 @@ class MockTimeWaitListManager : public QuicTimeWaitListManager { const QuicSocketAddress&, QuicConnectionId, bool, + size_t, std::unique_ptr<QuicPerPacketContext>), (override)); diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_test_utils.cc b/chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_test_utils.cc index 6cd3bf0bcf1..a4535040df7 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_test_utils.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/qpack/qpack_test_utils.cc @@ -18,7 +18,8 @@ FragmentSizeGenerator FragmentModeToFragmentSizeGenerator( case FragmentMode::kOctetByOctet: return []() { return 1; }; } - QUIC_BUG << "Unknown FragmentMode " << static_cast<int>(fragment_mode); + QUIC_BUG(quic_bug_10259_1) + << "Unknown FragmentMode " << static_cast<int>(fragment_mode); return []() { return 0; }; } diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.cc index 9bd5dd3bbbf..243216bb2e1 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.cc @@ -87,8 +87,9 @@ void QuicConfigPeer::SetConnectionOptionsToSend(QuicConfig* config, } // static -void QuicConfigPeer::SetReceivedStatelessResetToken(QuicConfig* config, - QuicUint128 token) { +void QuicConfigPeer::SetReceivedStatelessResetToken( + QuicConfig* config, + const StatelessResetToken& token) { config->stateless_reset_token_.SetReceivedValue(token); } diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.h index 7491e7ff834..6ffc5aeef2d 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.h @@ -8,7 +8,7 @@ #include "quic/core/quic_config.h" #include "quic/core/quic_connection_id.h" #include "quic/core/quic_packets.h" -#include "quic/platform/api/quic_uint128.h" +#include "quic/core/quic_types.h" namespace quic { @@ -55,7 +55,7 @@ class QuicConfigPeer { const QuicTagVector& options); static void SetReceivedStatelessResetToken(QuicConfig* config, - QuicUint128 token); + const StatelessResetToken& token); static void SetReceivedMaxPacketSize(QuicConfig* config, uint32_t max_udp_payload_size); diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_id_manager_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_id_manager_peer.h new file mode 100644 index 00000000000..ab749ed440c --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_id_manager_peer.h @@ -0,0 +1,29 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_CONNECTION_ID_MANAGER_PEER_H_ +#define QUICHE_QUIC_TEST_TOOLS_QUIC_CONNECTION_ID_MANAGER_PEER_H_ + +#include "quic/core/quic_connection_id_manager.h" + +namespace quic { +namespace test { + +class QuicConnectionIdManagerPeer { + public: + static QuicAlarm* GetRetirePeerIssuedConnectionIdAlarm( + QuicPeerIssuedConnectionIdManager* manager) { + return manager->retire_connection_id_alarm_.get(); + } + + static QuicAlarm* GetRetireSelfIssuedConnectionIdAlarm( + QuicSelfIssuedConnectionIdManager* manager) { + return manager->retire_connection_id_alarm_.get(); + } +}; + +} // namespace test +} // namespace quic + +#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_CONNECTION_ID_MANAGER_PEER_H_ 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 8d4186e4312..3f1eaeb16bd 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 @@ -10,6 +10,7 @@ #include "quic/core/quic_received_packet_manager.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_socket_address.h" +#include "quic/test_tools/quic_connection_id_manager_peer.h" #include "quic/test_tools/quic_framer_peer.h" #include "quic/test_tools/quic_sent_packet_manager_peer.h" @@ -164,6 +165,25 @@ QuicAlarm* QuicConnectionPeer::GetDiscardZeroRttDecryptionKeysAlarm( } // static +QuicAlarm* QuicConnectionPeer::GetRetirePeerIssuedConnectionIdAlarm( + QuicConnection* connection) { + if (connection->peer_issued_cid_manager_ == nullptr) { + return nullptr; + } + return QuicConnectionIdManagerPeer::GetRetirePeerIssuedConnectionIdAlarm( + connection->peer_issued_cid_manager_.get()); +} +// static +QuicAlarm* QuicConnectionPeer::GetRetireSelfIssuedConnectionIdAlarm( + QuicConnection* connection) { + if (connection->self_issued_cid_manager_ == nullptr) { + return nullptr; + } + return QuicConnectionIdManagerPeer::GetRetireSelfIssuedConnectionIdAlarm( + connection->self_issued_cid_manager_.get()); +} + +// static QuicPacketWriter* QuicConnectionPeer::GetWriter(QuicConnection* connection) { return connection->writer_; } @@ -447,5 +467,16 @@ QuicByteCount QuicConnectionPeer::BytesReceivedBeforeAddressValidation( return connection->default_path_.bytes_received_before_address_validation; } +// static +void QuicConnectionPeer::EnableMultipleConnectionIdSupport( + QuicConnection* connection) { + connection->support_multiple_connection_ids_ = true; +} + +// static +void QuicConnectionPeer::ResetPeerIssuedConnectionIdManager( + QuicConnection* connection) { + connection->peer_issued_cid_manager_ = nullptr; +} } // 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 e76b5b4212b..1de3ae72f0b 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 @@ -8,6 +8,7 @@ #include <cstddef> #include "absl/strings/string_view.h" #include "quic/core/quic_connection.h" +#include "quic/core/quic_connection_id.h" #include "quic/core/quic_connection_stats.h" #include "quic/core/quic_packets.h" #include "quic/core/quic_types.h" @@ -88,6 +89,10 @@ class QuicConnectionPeer { QuicConnection* connection); static QuicAlarm* GetDiscardZeroRttDecryptionKeysAlarm( QuicConnection* connection); + static QuicAlarm* GetRetirePeerIssuedConnectionIdAlarm( + QuicConnection* connection); + static QuicAlarm* GetRetireSelfIssuedConnectionIdAlarm( + QuicConnection* connection); static QuicPacketWriter* GetWriter(QuicConnection* connection); // If |owns_writer| is true, takes ownership of |writer|. @@ -185,6 +190,10 @@ class QuicConnectionPeer { static QuicByteCount BytesReceivedBeforeAddressValidation( QuicConnection* connection); + + static void EnableMultipleConnectionIdSupport(QuicConnection* connection); + + static void ResetPeerIssuedConnectionIdManager(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 57367737d52..3a553ce0838 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 @@ -87,10 +87,11 @@ void QuicDispatcherPeer::SendPublicReset( const QuicSocketAddress& peer_address, QuicConnectionId connection_id, bool ietf_quic, + size_t received_packet_length, std::unique_ptr<QuicPerPacketContext> packet_context) { dispatcher->time_wait_list_manager()->SendPublicReset( self_address, peer_address, connection_id, ietf_quic, - std::move(packet_context)); + received_packet_length, std::move(packet_context)); } // static 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 f3be472126b..fc8ab2014ae 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 @@ -57,6 +57,7 @@ class QuicDispatcherPeer { const QuicSocketAddress& peer_address, QuicConnectionId connection_id, bool ietf_quic, + size_t received_packet_length, std::unique_ptr<QuicPerPacketContext> packet_context); static std::unique_ptr<QuicPerPacketContext> GetPerPacketContext( diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc index 4a8c8ca4094..a3dbf843e58 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc @@ -40,7 +40,8 @@ void QuicSessionPeer::SetNextOutgoingBidirectionalStreamId(QuicSession* session, void QuicSessionPeer::SetMaxOpenIncomingStreams(QuicSession* session, uint32_t max_streams) { if (VersionHasIetfQuicFrames(session->transport_version())) { - QUIC_BUG << "SetmaxOpenIncomingStreams deprecated for IETF QUIC"; + QUIC_BUG(quic_bug_10193_1) + << "SetmaxOpenIncomingStreams deprecated for IETF QUIC"; session->ietf_streamid_manager_.SetMaxOpenIncomingUnidirectionalStreams( max_streams); session->ietf_streamid_manager_.SetMaxOpenIncomingBidirectionalStreams( @@ -75,7 +76,8 @@ void QuicSessionPeer::SetMaxOpenIncomingUnidirectionalStreams( void QuicSessionPeer::SetMaxOpenOutgoingStreams(QuicSession* session, uint32_t max_streams) { if (VersionHasIetfQuicFrames(session->transport_version())) { - QUIC_BUG << "SetmaxOpenOutgoingStreams deprecated for IETF QUIC"; + QUIC_BUG(quic_bug_10193_2) + << "SetmaxOpenOutgoingStreams deprecated for IETF QUIC"; return; } session->stream_id_manager_.set_max_open_outgoing_streams(max_streams); 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 58eedcc0106..c547ac22610 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 @@ -7,7 +7,9 @@ #include "quic/core/http/quic_spdy_session.h" #include "quic/core/qpack/qpack_receive_stream.h" #include "quic/core/quic_utils.h" +#include "quic/platform/api/quic_flags.h" #include "quic/test_tools/quic_session_peer.h" +#include "common/platform/api/quiche_logging.h" namespace quic { namespace test { @@ -37,18 +39,6 @@ spdy::SpdyFramer* QuicSpdySessionPeer::GetSpdyFramer(QuicSpdySession* session) { return &session->spdy_framer_; } -void QuicSpdySessionPeer::SetHpackEncoderDebugVisitor( - QuicSpdySession* session, - std::unique_ptr<QuicHpackDebugVisitor> visitor) { - session->SetHpackEncoderDebugVisitor(std::move(visitor)); -} - -void QuicSpdySessionPeer::SetHpackDecoderDebugVisitor( - QuicSpdySession* session, - std::unique_ptr<QuicHpackDebugVisitor> visitor) { - session->SetHpackDecoderDebugVisitor(std::move(visitor)); -} - void QuicSpdySessionPeer::SetMaxInboundHeaderListSize( QuicSpdySession* session, size_t max_inbound_header_size) { @@ -109,5 +99,19 @@ QpackReceiveStream* QuicSpdySessionPeer::GetQpackEncoderReceiveStream( return session->qpack_encoder_receive_stream_; } +// static +void QuicSpdySessionPeer::SetH3DatagramSupported(QuicSpdySession* session, + bool h3_datagram_supported) { + session->h3_datagram_supported_ = h3_datagram_supported; +} + +// static +void QuicSpdySessionPeer::EnableWebTransport(QuicSpdySession& session) { + SetQuicReloadableFlag(quic_h3_datagram, true); + QUICHE_DCHECK(session.WillNegotiateWebTransport()); + session.h3_datagram_supported_ = true; + session.peer_supports_webtransport_ = true; +} + } // namespace test } // namespace quic 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 c0413c6a1b7..9ba54979e9a 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 @@ -17,7 +17,6 @@ namespace quic { class QuicHeadersStream; class QuicSpdySession; -class QuicHpackDebugVisitor; namespace test { @@ -29,12 +28,6 @@ class QuicSpdySessionPeer { static void SetHeadersStream(QuicSpdySession* session, QuicHeadersStream* headers_stream); static spdy::SpdyFramer* GetSpdyFramer(QuicSpdySession* session); - static void SetHpackEncoderDebugVisitor( - QuicSpdySession* session, - std::unique_ptr<QuicHpackDebugVisitor> visitor); - static void SetHpackDecoderDebugVisitor( - QuicSpdySession* session, - std::unique_ptr<QuicHpackDebugVisitor> visitor); // Must be called before Initialize(). static void SetMaxInboundHeaderListSize(QuicSpdySession* session, size_t max_inbound_header_size); @@ -57,6 +50,9 @@ class QuicSpdySessionPeer { QuicSpdySession* session); static QpackReceiveStream* GetQpackEncoderReceiveStream( QuicSpdySession* session); + static void SetH3DatagramSupported(QuicSpdySession* session, + bool h3_datagram_supported); + static void EnableWebTransport(QuicSpdySession& session); }; } // namespace test 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 new file mode 100644 index 00000000000..9623d752027 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_backend.cc @@ -0,0 +1,139 @@ +// 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/quic_test_backend.h" + +#include <cstring> +#include <memory> + +#include "absl/strings/string_view.h" +#include "quic/core/quic_buffer_allocator.h" +#include "quic/core/quic_circular_deque.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/tools/web_transport_test_visitors.h" + +namespace quic { +namespace test { + +namespace { + +class EchoWebTransportServer : public WebTransportVisitor { + public: + EchoWebTransportServer(WebTransportSession* session) : session_(session) {} + + void OnSessionReady() override { + if (session_->CanOpenNextOutgoingBidirectionalStream()) { + OnCanCreateNewOutgoingBidirectionalStream(); + } + } + + void OnIncomingBidirectionalStreamAvailable() override { + while (true) { + WebTransportStream* stream = + session_->AcceptIncomingBidirectionalStream(); + if (stream == nullptr) { + return; + } + QUIC_DVLOG(1) << "EchoWebTransportServer received a bidirectional stream " + << stream->GetStreamId(); + stream->SetVisitor( + std::make_unique<WebTransportBidirectionalEchoVisitor>(stream)); + stream->visitor()->OnCanRead(); + } + } + + void OnIncomingUnidirectionalStreamAvailable() override { + while (true) { + WebTransportStream* stream = + session_->AcceptIncomingUnidirectionalStream(); + if (stream == nullptr) { + return; + } + QUIC_DVLOG(1) + << "EchoWebTransportServer received a unidirectional stream"; + stream->SetVisitor( + std::make_unique<WebTransportUnidirectionalEchoReadVisitor>( + stream, [this](const std::string& data) { + streams_to_echo_back_.push_back(data); + TrySendingUnidirectionalStreams(); + })); + stream->visitor()->OnCanRead(); + } + } + + void OnDatagramReceived(absl::string_view datagram) override { + auto buffer = MakeUniqueBuffer(&allocator_, datagram.size()); + memcpy(buffer.get(), datagram.data(), datagram.size()); + QuicMemSlice slice(std::move(buffer), datagram.size()); + session_->SendOrQueueDatagram(std::move(slice)); + } + + void OnCanCreateNewOutgoingBidirectionalStream() override { + if (!echo_stream_opened_) { + WebTransportStream* stream = session_->OpenOutgoingBidirectionalStream(); + stream->SetVisitor( + std::make_unique<WebTransportBidirectionalEchoVisitor>(stream)); + echo_stream_opened_ = true; + } + } + void OnCanCreateNewOutgoingUnidirectionalStream() override { + TrySendingUnidirectionalStreams(); + } + + void TrySendingUnidirectionalStreams() { + while (!streams_to_echo_back_.empty() && + session_->CanOpenNextOutgoingUnidirectionalStream()) { + QUIC_DVLOG(1) + << "EchoWebTransportServer echoed a unidirectional stream back"; + WebTransportStream* stream = session_->OpenOutgoingUnidirectionalStream(); + stream->SetVisitor( + std::make_unique<WebTransportUnidirectionalEchoWriteVisitor>( + stream, streams_to_echo_back_.front())); + streams_to_echo_back_.pop_front(); + stream->visitor()->OnCanWrite(); + } + } + + private: + WebTransportSession* session_; + SimpleBufferAllocator allocator_; + bool echo_stream_opened_ = false; + + QuicCircularDeque<std::string> streams_to_echo_back_; +}; + +} // namespace + +QuicSimpleServerBackend::WebTransportResponse +QuicTestBackend::ProcessWebTransportRequest( + const spdy::Http2HeaderBlock& request_headers, + WebTransportSession* session) { + if (!SupportsWebTransport()) { + return QuicSimpleServerBackend::ProcessWebTransportRequest(request_headers, + session); + } + + auto path_it = request_headers.find(":path"); + if (path_it == request_headers.end()) { + WebTransportResponse response; + response.response_headers[":status"] = "400"; + return response; + } + absl::string_view path = path_it->second; + if (path == "/echo") { + WebTransportResponse response; + response.response_headers[":status"] = "200"; + response.visitor = std::make_unique<EchoWebTransportServer>(session); + return response; + } + + WebTransportResponse response; + response.response_headers[":status"] = "404"; + return response; +} + +} // namespace test +} // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_backend.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_backend.h new file mode 100644 index 00000000000..66e1213641c --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_backend.h @@ -0,0 +1,34 @@ +// 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_QUIC_TEST_BACKEND_H_ +#define QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_BACKEND_H_ + +#include "quic/tools/quic_memory_cache_backend.h" + +namespace quic { +namespace test { + +// QuicTestBackend is a QuicSimpleServer backend usable in tests. It has extra +// WebTransport endpoints on top of what QuicMemoryCacheBackend already +// provides. +class QuicTestBackend : public QuicMemoryCacheBackend { + public: + WebTransportResponse ProcessWebTransportRequest( + const spdy::Http2HeaderBlock& request_headers, + WebTransportSession* session) override; + bool SupportsWebTransport() override { return enable_webtransport_; } + + void set_enable_webtransport(bool enable_webtransport) { + enable_webtransport_ = enable_webtransport; + } + + private: + bool enable_webtransport_ = false; +}; + +} // namespace test +} // namespace quic + +#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_BACKEND_H_ diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc index 41339508501..ed823b1e156 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc @@ -20,7 +20,6 @@ #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" -#include "quic/platform/api/quic_ptr_util.h" #include "quic/platform/api/quic_stack_trace.h" #include "quic/test_tools/crypto_test_utils.h" #include "quic/test_tools/quic_client_peer.h" @@ -236,7 +235,7 @@ MockableQuicClient::MockableQuicClient( epoll_server, std::make_unique<MockableQuicClientEpollNetworkHelper>(epoll_server, this), - QuicWrapUnique(new RecordingProofVerifier(std::move(proof_verifier))), + std::make_unique<RecordingProofVerifier>(std::move(proof_verifier)), std::move(session_cache)), override_server_connection_id_(EmptyQuicConnectionId()), server_connection_id_overridden_(false), @@ -311,6 +310,9 @@ void MockableQuicClient::UseWriter(QuicPacketWriterWrapper* writer) { void MockableQuicClient::set_peer_address(const QuicSocketAddress& address) { mockable_network_helper()->set_peer_address(address); + if (client_session() != nullptr) { + client_session()->AddKnownServerAddress(address); + } } const QuicReceivedPacket* MockableQuicClient::last_incoming_packet() { @@ -634,7 +636,7 @@ bool QuicTestClient::connected() const { void QuicTestClient::Connect() { if (connected()) { - QUIC_BUG << "Cannot connect already-connected client"; + QUIC_BUG(quic_bug_10133_1) << "Cannot connect already-connected client"; return; } if (!connect_attempted_) { @@ -796,7 +798,8 @@ void QuicTestClient::OnClose(QuicSpdyStream* stream) { closed_stream_states_.insert(std::make_pair( id, PerStreamState( - client_stream->stream_error(), true, + // Set response_complete to true iff stream is closed while connected. + client_stream->stream_error(), connected(), client_stream->headers_decompressed(), client_stream->response_headers(), client_stream->preliminary_headers(), diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_server.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_server.cc index 55170b20d6d..f6e2c34a9b2 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_server.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_server.cc @@ -6,10 +6,10 @@ #include <utility> +#include "absl/memory/memory.h" #include "absl/strings/string_view.h" #include "quic/core/quic_epoll_alarm_factory.h" #include "quic/core/quic_epoll_connection_helper.h" -#include "quic/platform/api/quic_ptr_util.h" #include "quic/tools/quic_simple_crypto_server_stream_helper.h" #include "quic/tools/quic_simple_dispatcher.h" #include "quic/tools/quic_simple_server_session.h" @@ -49,7 +49,7 @@ class CustomStreamSession : public QuicSimpleServerSession { if (stream_factory_) { QuicSpdyStream* stream = stream_factory_->CreateStream(id, this, server_backend()); - ActivateStream(QuicWrapUnique(stream)); + ActivateStream(absl::WrapUnique(stream)); return stream; } return QuicSimpleServerSession::CreateIncomingStream(id); @@ -98,12 +98,13 @@ class QuicTestDispatcher : public QuicSimpleDispatcher { const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, absl::string_view alpn, - const ParsedQuicVersion& version) override { + const ParsedQuicVersion& version, + absl::string_view sni) override { QuicReaderMutexLock lock(&factory_lock_); if (session_factory_ == nullptr && stream_factory_ == nullptr && crypto_stream_factory_ == nullptr) { return QuicSimpleDispatcher::CreateQuicSession( - id, self_address, peer_address, alpn, version); + id, self_address, peer_address, alpn, version, sni); } QuicConnection* connection = new QuicConnection( id, self_address, peer_address, helper(), alarm_factory(), writer(), 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 035680cf602..5feb6462321 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 @@ -449,7 +449,7 @@ bool NoOpFramerVisitor::OnAckFrequencyFrame( } bool NoOpFramerVisitor::IsValidStatelessResetToken( - QuicUint128 /*token*/) const { + const StatelessResetToken& /*token*/) const { return false; } @@ -1535,11 +1535,11 @@ bool WriteServerVersionNegotiationProbeResponse( const char* source_connection_id_bytes, uint8_t source_connection_id_length) { if (packet_bytes == nullptr) { - QUIC_BUG << "Invalid packet_bytes"; + QUIC_BUG(quic_bug_10256_1) << "Invalid packet_bytes"; return false; } if (packet_length_out == nullptr) { - QUIC_BUG << "Invalid packet_length_out"; + QUIC_BUG(quic_bug_10256_2) << "Invalid packet_length_out"; return false; } QuicConnectionId source_connection_id(source_connection_id_bytes, @@ -1550,12 +1550,13 @@ bool WriteServerVersionNegotiationProbeResponse( /*ietf_quic=*/true, /*use_length_prefix=*/true, ParsedQuicVersionVector{}); if (!encrypted_packet) { - QUIC_BUG << "Failed to create version negotiation packet"; + QUIC_BUG(quic_bug_10256_3) << "Failed to create version negotiation packet"; return false; } if (*packet_length_out < encrypted_packet->length()) { - QUIC_BUG << "Invalid *packet_length_out " << *packet_length_out << " < " - << encrypted_packet->length(); + QUIC_BUG(quic_bug_10256_4) + << "Invalid *packet_length_out " << *packet_length_out << " < " + << encrypted_packet->length(); return false; } *packet_length_out = encrypted_packet->length(); @@ -1569,20 +1570,21 @@ bool ParseClientVersionNegotiationProbePacket( char* destination_connection_id_bytes, uint8_t* destination_connection_id_length_out) { if (packet_bytes == nullptr) { - QUIC_BUG << "Invalid packet_bytes"; + QUIC_BUG(quic_bug_10256_5) << "Invalid packet_bytes"; return false; } if (packet_length < kMinPacketSizeForVersionNegotiation || packet_length > 65535) { - QUIC_BUG << "Invalid packet_length"; + QUIC_BUG(quic_bug_10256_6) << "Invalid packet_length"; return false; } if (destination_connection_id_bytes == nullptr) { - QUIC_BUG << "Invalid destination_connection_id_bytes"; + QUIC_BUG(quic_bug_10256_7) << "Invalid destination_connection_id_bytes"; return false; } if (destination_connection_id_length_out == nullptr) { - QUIC_BUG << "Invalid destination_connection_id_length_out"; + QUIC_BUG(quic_bug_10256_8) + << "Invalid destination_connection_id_length_out"; return false; } @@ -1602,16 +1604,17 @@ bool ParseClientVersionNegotiationProbePacket( &parsed_version, &destination_connection_id, &source_connection_id, &retry_token_present, &retry_token, &detailed_error); if (error != QUIC_NO_ERROR) { - QUIC_BUG << "Failed to parse packet: " << detailed_error; + QUIC_BUG(quic_bug_10256_9) << "Failed to parse packet: " << detailed_error; return false; } if (!version_present) { - QUIC_BUG << "Packet is not a long header"; + QUIC_BUG(quic_bug_10256_10) << "Packet is not a long header"; return false; } if (*destination_connection_id_length_out < destination_connection_id.length()) { - QUIC_BUG << "destination_connection_id_length_out too small"; + QUIC_BUG(quic_bug_10256_11) + << "destination_connection_id_length_out too small"; return false; } *destination_connection_id_length_out = destination_connection_id.length(); 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 fe255b45d4f..2f16b1e072b 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 @@ -33,6 +33,7 @@ #include "quic/core/quic_types.h" #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_mem_slice_storage.h" +#include "quic/platform/api/quic_socket_address.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/mock_clock.h" #include "quic/test_tools/mock_quic_session_visitor.h" @@ -430,7 +431,7 @@ class MockFramerVisitor : public QuicFramerVisitorInterface { MOCK_METHOD(void, OnPacketComplete, (), (override)); MOCK_METHOD(bool, IsValidStatelessResetToken, - (QuicUint128), + (const StatelessResetToken&), (const, override)); MOCK_METHOD(void, OnAuthenticatedIetfStatelessResetPacket, @@ -503,7 +504,8 @@ class NoOpFramerVisitor : public QuicFramerVisitorInterface { bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) override; bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) override; void OnPacketComplete() override {} - bool IsValidStatelessResetToken(QuicUint128 token) const override; + bool IsValidStatelessResetToken( + const StatelessResetToken& token) const override; void OnAuthenticatedIetfStatelessResetPacket( const QuicIetfStatelessResetPacket& /*packet*/) override {} void OnKeyUpdate(KeyUpdateReason /*reason*/) override {} @@ -577,6 +579,22 @@ class MockQuicConnectionVisitor : public QuicConnectionVisitorInterface { SendAckFrequency, (const QuicAckFrequencyFrame& frame), (override)); + 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, @@ -606,6 +624,11 @@ class MockQuicConnectionVisitor : public QuicConnectionVisitorInterface { MOCK_METHOD(void, BeforeConnectionCloseSent, (), (override)); MOCK_METHOD(bool, ValidateToken, (absl::string_view), (const, override)); MOCK_METHOD(void, MaybeSendAddressToken, (), (override)); + + bool IsKnownServerAddress( + const QuicSocketAddress& /*address*/) const override { + return false; + } }; class MockQuicConnectionHelper : public QuicConnectionHelperInterface { @@ -771,6 +794,15 @@ class MockQuicConnection : public QuicConnection { connection_close_behavior); } + void ReallyCloseConnection4( + QuicErrorCode error, + QuicIetfTransportErrorCodes ietf_error, + const std::string& details, + ConnectionCloseBehavior connection_close_behavior) { + QuicConnection::CloseConnection(error, ietf_error, details, + connection_close_behavior); + } + void ReallySendConnectionClosePacket(QuicErrorCode error, QuicIetfTransportErrorCodes ietf_error, const std::string& details) { @@ -1094,6 +1126,11 @@ class MockHttp3DebugVisitor : public Http3DebugVisitor { (override)); MOCK_METHOD(void, + OnSettingsFrameReceivedViaAlps, + (const SettingsFrame&), + (override)); + + MOCK_METHOD(void, OnAcceptChFrameReceivedViaAlps, (const AcceptChFrame&), (override)); @@ -1958,7 +1995,7 @@ class TaggingDecrypter : public QuicDecrypter { } bool SetPreliminaryKey(absl::string_view /*key*/) override { - QUIC_BUG << "should not be called"; + QUIC_BUG(quic_bug_10230_1) << "should not be called"; return false; } @@ -2284,6 +2321,32 @@ bool WriteServerVersionNegotiationProbeResponse( const char* source_connection_id_bytes, uint8_t source_connection_id_length); +// Implementation of Http3DatagramVisitor which saves all received datagrams. +class SavingHttp3DatagramVisitor + : public QuicSpdySession::Http3DatagramVisitor { + public: + struct SavedHttp3Datagram { + QuicDatagramFlowId flow_id; + std::string payload; + bool operator==(const SavedHttp3Datagram& o) const { + return flow_id == o.flow_id && payload == o.payload; + } + }; + const std::vector<SavedHttp3Datagram>& received_h3_datagrams() const { + return received_h3_datagrams_; + } + + // Override from QuicSpdySession::Http3DatagramVisitor. + void OnHttp3Datagram(QuicDatagramFlowId flow_id, + absl::string_view payload) override { + received_h3_datagrams_.push_back( + SavedHttp3Datagram{flow_id, std::string(payload)}); + } + + private: + std::vector<SavedHttp3Datagram> received_h3_datagrams_; +}; + } // namespace test } // namespace quic 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 e0fcebc6ebd..df52093e05a 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 @@ -5,14 +5,14 @@ #ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_TRANSPORT_TEST_TOOLS_H_ #define QUICHE_QUIC_TEST_TOOLS_QUIC_TRANSPORT_TEST_TOOLS_H_ +#include "quic/core/web_transport_interface.h" #include "quic/platform/api/quic_test.h" -#include "quic/quic_transport/quic_transport_client_session.h" #include "quic/quic_transport/quic_transport_server_session.h" namespace quic { namespace test { -class MockClientVisitor : public QuicTransportClientSession::ClientVisitor { +class MockClientVisitor : public WebTransportVisitor { public: MOCK_METHOD(void, OnSessionReady, (), (override)); MOCK_METHOD(void, OnIncomingBidirectionalStreamAvailable, (), (override)); @@ -28,10 +28,9 @@ class MockServerVisitor : public QuicTransportServerSession::ServerVisitor { MOCK_METHOD(bool, ProcessPath, (const GURL&), (override)); }; -class MockStreamVisitor : public QuicTransportStream::Visitor { +class MockStreamVisitor : public WebTransportStreamVisitor { public: MOCK_METHOD(void, OnCanRead, (), (override)); - MOCK_METHOD(void, OnFinRead, (), (override)); MOCK_METHOD(void, OnCanWrite, (), (override)); }; diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.cc index 96b131877fb..92363ec8eea 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.cc @@ -7,10 +7,11 @@ #include <memory> #include <utility> +#include "absl/memory/memory.h" #include "absl/strings/string_view.h" #include "quic/core/crypto/quic_decrypter.h" #include "quic/core/crypto/quic_encrypter.h" -#include "quic/platform/api/quic_ptr_util.h" +#include "quic/core/quic_types.h" namespace quic { namespace test { @@ -73,7 +74,7 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface { // Save a copy of the data so it is valid after the packet is processed. std::string* string_data = new std::string(frame.data_buffer, frame.data_length); - stream_data_.push_back(QuicWrapUnique(string_data)); + stream_data_.push_back(absl::WrapUnique(string_data)); // TODO(ianswett): A pointer isn't necessary with emplace_back. stream_frames_.push_back(std::make_unique<QuicStreamFrame>( frame.stream_id, frame.fin, frame.offset, @@ -85,7 +86,7 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface { // Save a copy of the data so it is valid after the packet is processed. std::string* string_data = new std::string(frame.data_buffer, frame.data_length); - crypto_data_.push_back(QuicWrapUnique(string_data)); + crypto_data_.push_back(absl::WrapUnique(string_data)); crypto_frames_.push_back(std::make_unique<QuicCryptoFrame>( frame.level, frame.offset, absl::string_view(*string_data))); return true; @@ -210,7 +211,8 @@ class SimpleFramerVisitor : public QuicFramerVisitorInterface { void OnPacketComplete() override {} - bool IsValidStatelessResetToken(QuicUint128 /*token*/) const override { + bool IsValidStatelessResetToken( + const StatelessResetToken& /*token*/) const override { return false; } 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 415332ad274..51d4017948f 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 @@ -163,6 +163,17 @@ void SimpleSessionNotifier::NeuterUnencryptedData() { } void SimpleSessionNotifier::OnCanWrite() { + if (connection_->donot_write_mid_packet_processing()) { + if (connection_->framer().is_processing_packet()) { + // Do not write data in the middle of packet processing because rest + // frames in the packet may change the data to write. For example, lost + // data could be acknowledged. Also, connection is going to emit + // OnCanWrite signal post packet processing. + QUIC_BUG(simple_notifier_write_mid_packet_processing) + << "Try to write mid packet processing."; + return; + } + } if (!RetransmitLostCryptoData() || !RetransmitLostControlFrames() || !RetransmitLostStreamData()) { return; diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.cc index 5113cf79791..c286bf6e853 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.cc @@ -43,7 +43,7 @@ void Queue::AcceptPacket(std::unique_ptr<Packet> packet) { } bytes_queued_ += packet->size; - queue_.emplace(std::move(packet), current_bundle_); + queue_.emplace_back(std::move(packet), current_bundle_); if (IsAggregationEnabled()) { current_bundle_bytes_ += queue_.front().packet->size; @@ -65,7 +65,7 @@ void Queue::Act() { bytes_queued_ -= queue_.front().packet->size; tx_port_->AcceptPacket(std::move(queue_.front().packet)); - queue_.pop(); + queue_.pop_front(); if (listener_ != nullptr) { listener_->OnPacketDequeued(); } 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 a291f48eeab..943b8b069a0 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 @@ -6,6 +6,7 @@ #define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_QUEUE_H_ #include "quic/core/quic_alarm.h" +#include "quic/core/quic_circular_deque.h" #include "quic/test_tools/simulator/link.h" namespace quic { @@ -109,7 +110,7 @@ class Queue : public Actor, public UnconstrainedPortInterface { std::unique_ptr<QuicAlarm> aggregation_timeout_alarm_; ConstrainedPortInterface* tx_port_; - QuicQueue<EnqueuedPacket> queue_; + QuicCircularDeque<EnqueuedPacket> queue_; ListenerInterface* listener_; }; diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.cc index cb0b58e9db5..fd5c5b7a4a4 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.cc @@ -228,7 +228,8 @@ bool QuicEndpoint::DataProducer::WriteCryptoData(EncryptionLevel /*level*/, QuicStreamOffset /*offset*/, QuicByteCount /*data_length*/, QuicDataWriter* /*writer*/) { - QUIC_BUG << "QuicEndpoint::DataProducer::WriteCryptoData is unimplemented"; + QUIC_BUG(quic_bug_10157_1) + << "QuicEndpoint::DataProducer::WriteCryptoData is unimplemented"; return false; } diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h index 3d81c706550..01d35d3a589 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h @@ -81,6 +81,13 @@ class QuicEndpoint : public QuicEndpointBase, void OnForwardProgressMadeAfterPathDegrading() override {} void OnAckNeedsRetransmittableFrame() override {} void SendAckFrequency(const QuicAckFrequencyFrame& /*frame*/) override {} + void SendNewConnectionId(const QuicNewConnectionIdFrame& /*frame*/) override { + } + void SendRetireConnectionId(uint64_t /*sequence_number*/) override {} + void OnServerConnectionIdIssued( + const QuicConnectionId& /*server_connection_id*/) override {} + void OnServerConnectionIdRetired( + const QuicConnectionId& /*server_connection_id*/) override {} bool AllowSelfAddressChange() const override; HandshakeState GetHandshakeState() const override; bool OnMaxStreamsFrame(const QuicMaxStreamsFrame& /*frame*/) override { @@ -107,6 +114,10 @@ class QuicEndpoint : public QuicEndpointBase, return true; } void MaybeSendAddressToken() override {} + bool IsKnownServerAddress( + const QuicSocketAddress& /*address*/) const override { + return false; + } // End QuicConnectionVisitorInterface implementation. diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator.cc index 8d68ee4145b..a9835e85d0a 100644 --- a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator.cc +++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator.cc @@ -155,10 +155,11 @@ void Simulator::HandleNextScheduledActor() { Unschedule(actor); if (clock_.Now() > event_time) { - QUIC_BUG << "Error: event registered by [" << actor->name() - << "] requires travelling back in time. Current time: " - << clock_.Now().ToDebuggingValue() - << ", scheduled time: " << event_time.ToDebuggingValue(); + QUIC_BUG(quic_bug_10150_1) + << "Error: event registered by [" << actor->name() + << "] requires travelling back in time. Current time: " + << clock_.Now().ToDebuggingValue() + << ", scheduled time: " << event_time.ToDebuggingValue(); } clock_.now_ = event_time; diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.h b/chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.h index a7edd78e6f8..1c5cad87112 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.h @@ -50,11 +50,20 @@ class QuicBackendResponse { ~QuicBackendResponse(); + const std::vector<spdy::Http2HeaderBlock>& early_hints() const { + return early_hints_; + } SpecialResponseType response_type() const { return response_type_; } const spdy::Http2HeaderBlock& headers() const { return headers_; } const spdy::Http2HeaderBlock& trailers() const { return trailers_; } const absl::string_view body() const { return absl::string_view(body_); } + void AddEarlyHints(const spdy::Http2HeaderBlock& headers) { + spdy::Http2HeaderBlock hints = headers.Clone(); + hints[":status"] = "103"; + early_hints_.push_back(std::move(hints)); + } + void set_response_type(SpecialResponseType response_type) { response_type_ = response_type; } @@ -70,6 +79,7 @@ class QuicBackendResponse { } private: + std::vector<spdy::Http2HeaderBlock> early_hints_; SpecialResponseType response_type_; spdy::Http2HeaderBlock headers_; spdy::Http2HeaderBlock trailers_; diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client.cc index f8375ab3b13..e659b4a7b84 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_client.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client.cc @@ -24,7 +24,6 @@ #include "quic/core/quic_server_id.h" #include "quic/platform/api/quic_bug_tracker.h" #include "quic/platform/api/quic_logging.h" -#include "quic/platform/api/quic_ptr_util.h" #include "quic/platform/api/quic_socket_address.h" #include "quic/tools/quic_simple_client_session.h" @@ -67,7 +66,7 @@ QuicClient::QuicClient(QuicSocketAddress server_address, supported_versions, QuicConfig(), epoll_server, - QuicWrapUnique(new QuicClientEpollNetworkHelper(epoll_server, this)), + std::make_unique<QuicClientEpollNetworkHelper>(epoll_server, this), std::move(proof_verifier), nullptr) {} @@ -83,7 +82,7 @@ QuicClient::QuicClient(QuicSocketAddress server_address, supported_versions, QuicConfig(), epoll_server, - QuicWrapUnique(new QuicClientEpollNetworkHelper(epoll_server, this)), + std::make_unique<QuicClientEpollNetworkHelper>(epoll_server, this), std::move(proof_verifier), std::move(session_cache)) {} @@ -100,7 +99,7 @@ QuicClient::QuicClient(QuicSocketAddress server_address, supported_versions, config, epoll_server, - QuicWrapUnique(new QuicClientEpollNetworkHelper(epoll_server, this)), + std::make_unique<QuicClientEpollNetworkHelper>(epoll_server, this), std::move(proof_verifier), std::move(session_cache)) {} @@ -165,7 +164,7 @@ std::unique_ptr<QuicSession> QuicClient::CreateQuicClientSession( QuicConnection* connection) { return std::make_unique<QuicSimpleClientSession>( *config(), supported_versions, connection, server_id(), crypto_config(), - push_promise_index(), drop_response_body()); + push_promise_index(), drop_response_body(), enable_web_transport()); } QuicClientEpollNetworkHelper* QuicClient::epoll_network_helper() { diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc index 1737f8cb549..7077f16506e 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc @@ -147,7 +147,7 @@ bool QuicClientBase::Connect() { num_attempts++; } if (session() == nullptr) { - QUIC_BUG << "Missing session after Connect"; + QUIC_BUG(quic_bug_10906_1) << "Missing session after Connect"; return false; } return session()->connection()->connected(); @@ -230,7 +230,8 @@ bool QuicClientBase::EncryptionBeingEstablished() { bool QuicClientBase::WaitForEvents() { if (!connected()) { - QUIC_BUG << "Cannot call WaitForEvents on non-connected client"; + QUIC_BUG(quic_bug_10906_2) + << "Cannot call WaitForEvents on non-connected client"; return false; } @@ -335,7 +336,8 @@ const QuicClientBase::NetworkHelper* QuicClientBase::network_helper() const { void QuicClientBase::WaitForStreamToClose(QuicStreamId id) { if (!connected()) { - QUIC_BUG << "Cannot WaitForStreamToClose on non-connected client"; + QUIC_BUG(quic_bug_10906_3) + << "Cannot WaitForStreamToClose on non-connected client"; return; } @@ -346,7 +348,8 @@ void QuicClientBase::WaitForStreamToClose(QuicStreamId id) { bool QuicClientBase::WaitForOneRttKeysAvailable() { if (!connected()) { - QUIC_BUG << "Cannot WaitForOneRttKeysAvailable on non-connected client"; + QUIC_BUG(quic_bug_10906_4) + << "Cannot WaitForOneRttKeysAvailable on non-connected client"; return false; } diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_interop_test_bin.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client_interop_test_bin.cc index f67a338b09c..eee0ae7f891 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_client_interop_test_bin.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_interop_test_bin.cc @@ -25,6 +25,12 @@ DEFINE_QUIC_COMMAND_LINE_FLAG(std::string, "", "The IP or hostname to connect to."); +DEFINE_QUIC_COMMAND_LINE_FLAG( + std::string, + quic_version, + "", + "The QUIC version to use. Defaults to most recent IETF QUIC version."); + DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, port, 0, "The port to connect to."); namespace quic { @@ -366,21 +372,8 @@ void QuicClientInteropRunner::SendRequest( std::set<Feature> ServerSupport(std::string dns_host, std::string url_host, - int port) { - // Enable IETF version support. - QuicVersionInitializeSupportForIetfDraft(); - ParsedQuicVersion version = UnsupportedQuicVersion(); - for (const ParsedQuicVersion& vers : AllSupportedVersions()) { - // Find the first version that supports IETF QUIC. - if (vers.HasIetfQuicFrames() && - vers.handshake_protocol == quic::PROTOCOL_TLS1_3) { - version = vers; - break; - } - } - QUICHE_CHECK(version.IsKnown()); - QuicEnableVersion(version); - + int port, + ParsedQuicVersion version) { std::cout << "Attempting interop with version " << version << std::endl; // Build the client, and try to connect. @@ -440,7 +433,26 @@ int main(int argc, char* argv[]) { url_host = dns_host; } - auto supported_features = quic::ServerSupport(dns_host, url_host, port); + // Pick QUIC version to use. + quic::QuicVersionInitializeSupportForIetfDraft(); + quic::ParsedQuicVersion version = quic::UnsupportedQuicVersion(); + std::string quic_version_string = GetQuicFlag(FLAGS_quic_version); + if (!quic_version_string.empty()) { + version = quic::ParseQuicVersionString(quic_version_string); + } else { + for (const quic::ParsedQuicVersion& vers : quic::AllSupportedVersions()) { + // Use the most recent IETF QUIC version. + if (vers.HasIetfQuicFrames() && vers.UsesHttp3() && vers.UsesTls()) { + version = vers; + break; + } + } + } + QUICHE_CHECK(version.IsKnown()); + QuicEnableVersion(version); + + auto supported_features = + quic::ServerSupport(dns_host, url_host, port, version); std::cout << "Results for " << url_host << ":" << port << std::endl; int current_row = 1; for (auto feature : supported_features) { diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.cc index c67e5b35be8..e76a6cffb3c 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.cc @@ -8,6 +8,7 @@ #include "absl/strings/match.h" #include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "quic/core/http/spdy_utils.h" #include "quic/platform/api/quic_bug_tracker.h" @@ -171,10 +172,8 @@ void QuicMemoryCacheBackend::AddSimpleResponse(absl::string_view host, int response_code, absl::string_view body) { Http2HeaderBlock response_headers; - response_headers[":status"] = - quiche::QuicheTextUtils::Uint64ToString(response_code); - response_headers["content-length"] = - quiche::QuicheTextUtils::Uint64ToString(body.length()); + response_headers[":status"] = absl::StrCat(response_code); + response_headers["content-length"] = absl::StrCat(body.length()); AddResponse(host, path, std::move(response_headers), body); } @@ -199,7 +198,7 @@ void QuicMemoryCacheBackend::AddResponse(absl::string_view host, absl::string_view response_body) { AddResponseImpl(host, path, QuicBackendResponse::REGULAR_RESPONSE, std::move(response_headers), response_body, - Http2HeaderBlock()); + Http2HeaderBlock(), std::vector<spdy::Http2HeaderBlock>()); } void QuicMemoryCacheBackend::AddResponse(absl::string_view host, @@ -209,7 +208,19 @@ void QuicMemoryCacheBackend::AddResponse(absl::string_view host, Http2HeaderBlock response_trailers) { AddResponseImpl(host, path, QuicBackendResponse::REGULAR_RESPONSE, std::move(response_headers), response_body, - std::move(response_trailers)); + std::move(response_trailers), + std::vector<spdy::Http2HeaderBlock>()); +} + +void QuicMemoryCacheBackend::AddResponseWithEarlyHints( + absl::string_view host, + absl::string_view path, + spdy::Http2HeaderBlock response_headers, + absl::string_view response_body, + const std::vector<spdy::Http2HeaderBlock>& early_hints) { + AddResponseImpl(host, path, QuicBackendResponse::REGULAR_RESPONSE, + std::move(response_headers), response_body, + Http2HeaderBlock(), early_hints); } void QuicMemoryCacheBackend::AddSpecialResponse( @@ -217,7 +228,7 @@ void QuicMemoryCacheBackend::AddSpecialResponse( absl::string_view path, SpecialResponseType response_type) { AddResponseImpl(host, path, response_type, Http2HeaderBlock(), "", - Http2HeaderBlock()); + Http2HeaderBlock(), std::vector<spdy::Http2HeaderBlock>()); } void QuicMemoryCacheBackend::AddSpecialResponse( @@ -227,7 +238,8 @@ void QuicMemoryCacheBackend::AddSpecialResponse( absl::string_view response_body, SpecialResponseType response_type) { AddResponseImpl(host, path, response_type, std::move(response_headers), - response_body, Http2HeaderBlock()); + response_body, Http2HeaderBlock(), + std::vector<spdy::Http2HeaderBlock>()); } QuicMemoryCacheBackend::QuicMemoryCacheBackend() : cache_initialized_(false) {} @@ -235,7 +247,7 @@ QuicMemoryCacheBackend::QuicMemoryCacheBackend() : cache_initialized_(false) {} bool QuicMemoryCacheBackend::InitializeBackend( const std::string& cache_directory) { if (cache_directory.empty()) { - QUIC_BUG << "cache_directory must not be empty."; + QUIC_BUG(quic_bug_10932_1) << "cache_directory must not be empty."; return false; } QUIC_LOG(INFO) @@ -274,7 +286,8 @@ bool QuicMemoryCacheBackend::InitializeBackend( QuicUrl url(push_url); const QuicBackendResponse* response = GetResponse(url.host(), url.path()); if (!response) { - QUIC_BUG << "Push URL '" << push_url << "' not found."; + QUIC_BUG(quic_bug_10932_2) + << "Push URL '" << push_url << "' not found."; return false; } push_resources.push_back(ServerPushInfo(url, response->headers().Clone(), @@ -316,8 +329,13 @@ void QuicMemoryCacheBackend::FetchResponseFromBackend( quic_response = GetResponse(authority->second, path->second); } - std::string request_url = - std::string(authority->second) + std::string(path->second); + std::string request_url; + if (authority != request_headers.end()) { + request_url = std::string(authority->second); + } + if (path != request_headers.end()) { + request_url += std::string(path->second); + } std::list<ServerPushInfo> resources = GetServerPushResources(request_url); QUIC_DVLOG(1) << "Fetching QUIC response from backend in-memory cache for url " @@ -356,14 +374,16 @@ void QuicMemoryCacheBackend::AddResponseImpl( SpecialResponseType response_type, Http2HeaderBlock response_headers, absl::string_view response_body, - Http2HeaderBlock response_trailers) { + Http2HeaderBlock response_trailers, + const std::vector<spdy::Http2HeaderBlock>& early_hints) { QuicWriterMutexLock lock(&response_mutex_); QUICHE_DCHECK(!host.empty()) << "Host must be populated, e.g. \"www.google.com\""; std::string key = GetKey(host, path); if (QuicContainsKey(responses_, key)) { - QUIC_BUG << "Response for '" << key << "' already exists!"; + QUIC_BUG(quic_bug_10932_3) + << "Response for '" << key << "' already exists!"; return; } auto new_response = std::make_unique<QuicBackendResponse>(); @@ -371,6 +391,9 @@ void QuicMemoryCacheBackend::AddResponseImpl( new_response->set_headers(std::move(response_headers)); new_response->set_body(response_body); new_response->set_trailers(std::move(response_trailers)); + for (auto& headers : early_hints) { + new_response->AddEarlyHints(headers); + } QUIC_DVLOG(1) << "Add response with key " << key; responses_[key] = std::move(new_response); } diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h index 5e4b2674dd9..8ba6d605218 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h @@ -110,6 +110,14 @@ class QuicMemoryCacheBackend : public QuicSimpleServerBackend { absl::string_view response_body, spdy::Http2HeaderBlock response_trailers); + // Add a response, with 103 Early Hints, to the cache. + void AddResponseWithEarlyHints( + absl::string_view host, + absl::string_view path, + spdy::Http2HeaderBlock response_headers, + absl::string_view response_body, + const std::vector<spdy::Http2HeaderBlock>& early_hints); + // Simulate a special behavior at a particular path. void AddSpecialResponse( absl::string_view host, @@ -152,7 +160,8 @@ class QuicMemoryCacheBackend : public QuicSimpleServerBackend { QuicBackendResponse::SpecialResponseType response_type, spdy::Http2HeaderBlock response_headers, absl::string_view response_body, - spdy::Http2HeaderBlock response_trailers); + spdy::Http2HeaderBlock response_trailers, + const std::vector<spdy::Http2HeaderBlock>& early_hints); std::string GetKey(absl::string_view host, absl::string_view path) const; 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 e86bb872b47..d0383e19b6e 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 @@ -10,7 +10,6 @@ #include "quic/platform/api/quic_map_util.h" #include "quic/platform/api/quic_test.h" #include "quic/tools/quic_backend_response.h" -#include "common/platform/api/quiche_text_utils.h" namespace quic { namespace test { @@ -62,8 +61,7 @@ TEST_F(QuicMemoryCacheBackendTest, AddResponse) { spdy::Http2HeaderBlock response_headers; response_headers[":status"] = "200"; - response_headers["content-length"] = - quiche::QuicheTextUtils::Uint64ToString(kResponseBody.size()); + response_headers["content-length"] = absl::StrCat(kResponseBody.size()); spdy::Http2HeaderBlock response_trailers; response_trailers["key-1"] = "value-1"; @@ -184,16 +182,14 @@ TEST_F(QuicMemoryCacheBackendTest, AddSimpleResponseWithServerPushResources) { std::list<ServerPushInfo> push_resources; std::string scheme = "http"; for (int i = 0; i < NumResources; ++i) { - std::string path = - "/server_push_src" + quiche::QuicheTextUtils::Uint64ToString(i); + std::string path = absl::StrCat("/server_push_src", i); std::string url = scheme + "://" + request_host + path; QuicUrl resource_url(url); std::string body = absl::StrCat("This is server push response body for ", path); spdy::Http2HeaderBlock response_headers; response_headers[":status"] = "200"; - response_headers["content-length"] = - quiche::QuicheTextUtils::Uint64ToString(body.size()); + response_headers["content-length"] = absl::StrCat(body.size()); push_resources.push_back( ServerPushInfo(resource_url, response_headers.Clone(), i, body)); } @@ -224,15 +220,13 @@ TEST_F(QuicMemoryCacheBackendTest, GetServerPushResourcesAndPushResponses) { "404"}; std::list<ServerPushInfo> push_resources; for (int i = 0; i < NumResources; ++i) { - std::string path = - "/server_push_src" + quiche::QuicheTextUtils::Uint64ToString(i); + std::string path = absl::StrCat("/server_push_src", i); std::string url = scheme + "://" + request_host + path; QuicUrl resource_url(url); std::string body = "This is server push response body for " + path; spdy::Http2HeaderBlock response_headers; response_headers[":status"] = push_response_status[i]; - response_headers["content-length"] = - quiche::QuicheTextUtils::Uint64ToString(body.size()); + response_headers["content-length"] = absl::StrCat(body.size()); push_resources.push_back( ServerPushInfo(resource_url, response_headers.Clone(), i, body)); } diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc index 9d67b09e5e8..97ca71e9c6f 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc @@ -28,12 +28,13 @@ #include <iostream> +#include "absl/strings/escaping.h" +#include "absl/strings/string_view.h" #include "quic/core/quic_framer.h" +#include "quic/core/quic_types.h" #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_flags.h" #include "common/platform/api/quiche_text_utils.h" -#include "absl/strings/string_view.h" -#include "absl/strings/escaping.h" DEFINE_QUIC_COMMAND_LINE_FLAG(std::string, quic_version, @@ -213,7 +214,8 @@ class QuicPacketPrinter : public QuicFramerVisitorInterface { return true; } void OnPacketComplete() override { std::cerr << "OnPacketComplete\n"; } - bool IsValidStatelessResetToken(QuicUint128 /*token*/) const override { + bool IsValidStatelessResetToken( + const StatelessResetToken& /*token*/) const override { std::cerr << "IsValidStatelessResetToken\n"; return false; } diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_server_bin.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_server_bin.cc index 6ed8e994bea..c9713670a9c 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_server_bin.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_server_bin.cc @@ -9,10 +9,12 @@ #include "quic/core/quic_versions.h" #include "quic/platform/api/quic_flags.h" +#include "quic/platform/api/quic_system_event_loop.h" #include "quic/tools/quic_epoll_server_factory.h" #include "quic/tools/quic_toy_server.h" int main(int argc, char* argv[]) { + QuicSystemEventLoop event_loop("quic_server"); const char* usage = "Usage: quic_server [options]"; std::vector<std::string> non_option_args = quic::QuicParseCommandLineFlags(usage, argc, argv); 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 7f64495f0fd..d1efcba4fe0 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 @@ -16,13 +16,32 @@ QuicSimpleClientSession::QuicSimpleClientSession( QuicCryptoClientConfig* crypto_config, QuicClientPushPromiseIndex* push_promise_index, bool drop_response_body) + : QuicSimpleClientSession(config, + supported_versions, + connection, + server_id, + crypto_config, + push_promise_index, + drop_response_body, + /*enable_web_transport=*/false) {} + +QuicSimpleClientSession::QuicSimpleClientSession( + const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + const QuicServerId& server_id, + QuicCryptoClientConfig* crypto_config, + QuicClientPushPromiseIndex* push_promise_index, + bool drop_response_body, + bool enable_web_transport) : QuicSpdyClientSession(config, supported_versions, connection, server_id, crypto_config, push_promise_index), - drop_response_body_(drop_response_body) {} + drop_response_body_(drop_response_body), + enable_web_transport_(enable_web_transport) {} std::unique_ptr<QuicSpdyClientStream> QuicSimpleClientSession::CreateClientStream() { @@ -31,4 +50,8 @@ QuicSimpleClientSession::CreateClientStream() { drop_response_body_); } +bool QuicSimpleClientSession::ShouldNegotiateWebTransport() { + return enable_web_transport_; +} + } // 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 5b5314183f8..aa86d9a83c9 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 @@ -19,11 +19,21 @@ class QuicSimpleClientSession : public QuicSpdyClientSession { QuicCryptoClientConfig* crypto_config, QuicClientPushPromiseIndex* push_promise_index, bool drop_response_body); + QuicSimpleClientSession(const QuicConfig& config, + const ParsedQuicVersionVector& supported_versions, + QuicConnection* connection, + const QuicServerId& server_id, + QuicCryptoClientConfig* crypto_config, + QuicClientPushPromiseIndex* push_promise_index, + bool drop_response_body, + bool enable_web_transport); std::unique_ptr<QuicSpdyClientStream> CreateClientStream() override; + bool ShouldNegotiateWebTransport() override; private: const bool drop_response_body_; + const bool enable_web_transport_; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.cc index 736c061a2db..aa9a9c1e616 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.cc @@ -53,7 +53,8 @@ std::unique_ptr<QuicSession> QuicSimpleDispatcher::CreateQuicSession( const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, absl::string_view /*alpn*/, - const ParsedQuicVersion& version) { + const ParsedQuicVersion& version, + absl::string_view /*sni*/) { // The QuicServerSessionBase takes ownership of |connection| below. QuicConnection* connection = new QuicConnection(connection_id, self_address, peer_address, helper(), diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h index a629d3cd299..c790f21a140 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h @@ -36,7 +36,8 @@ class QuicSimpleDispatcher : public QuicDispatcher { const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, absl::string_view alpn, - const ParsedQuicVersion& version) override; + const ParsedQuicVersion& version, + absl::string_view sni) override; QuicSimpleServerBackend* server_backend() { return quic_simple_server_backend_; diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h index 9e16d4e9a88..43ced4bcefb 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h @@ -5,7 +5,10 @@ #ifndef QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_BACKEND_H_ #define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_BACKEND_H_ +#include <memory> + #include "quic/core/quic_types.h" +#include "quic/core/web_transport_interface.h" #include "quic/tools/quic_backend_response.h" #include "spdy/core/spdy_header_block.h" @@ -33,6 +36,11 @@ class QuicSimpleServerBackend { std::list<QuicBackendResponse::ServerPushInfo> resources) = 0; }; + struct WebTransportResponse { + spdy::Http2HeaderBlock response_headers; + std::unique_ptr<WebTransportVisitor> visitor; + }; + virtual ~QuicSimpleServerBackend() = default; // This method initializes the backend instance to fetch responses // from a backend server, in-memory cache etc. @@ -51,6 +59,15 @@ class QuicSimpleServerBackend { RequestHandler* request_handler) = 0; // Clears the state of the backend instance virtual void CloseBackendResponseStream(RequestHandler* request_handler) = 0; + + virtual WebTransportResponse ProcessWebTransportRequest( + const spdy::Http2HeaderBlock& /*request_headers*/, + WebTransportSession* /*session*/) { + WebTransportResponse response; + response.response_headers[":status"] = "400"; + return response; + } + virtual bool SupportsWebTransport() { return false; } }; } // namespace quic 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 044998c9e16..4075d3ff417 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 @@ -6,12 +6,13 @@ #include <utility> +#include "absl/memory/memory.h" +#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_utils.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" -#include "quic/platform/api/quic_ptr_util.h" #include "quic/tools/quic_simple_server_stream.h" namespace quic { @@ -51,7 +52,7 @@ QuicSimpleServerSession::CreateQuicCryptoServerStream( } void QuicSimpleServerSession::OnStreamFrame(const QuicStreamFrame& frame) { - if (!IsIncomingStream(frame.stream_id)) { + if (!IsIncomingStream(frame.stream_id) && !WillNegotiateWebTransport()) { QUIC_LOG(WARNING) << "Client shouldn't send data on server push stream"; connection()->CloseConnection( QUIC_INVALID_STREAM_ID, "Client sent data on server push stream", @@ -101,7 +102,7 @@ QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream(QuicStreamId id) { QuicSpdyStream* stream = new QuicSimpleServerStream( id, this, BIDIRECTIONAL, quic_simple_server_backend_); - ActivateStream(QuicWrapUnique(stream)); + ActivateStream(absl::WrapUnique(stream)); return stream; } @@ -109,14 +110,26 @@ QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream( PendingStream* pending) { QuicSpdyStream* stream = new QuicSimpleServerStream( pending, this, BIDIRECTIONAL, quic_simple_server_backend_); - ActivateStream(QuicWrapUnique(stream)); + ActivateStream(absl::WrapUnique(stream)); return stream; } -QuicSimpleServerStream* -QuicSimpleServerSession::CreateOutgoingBidirectionalStream() { - QUICHE_DCHECK(false); - return nullptr; +QuicSpdyStream* QuicSimpleServerSession::CreateOutgoingBidirectionalStream() { + if (!WillNegotiateWebTransport()) { + QUIC_BUG(QuicSimpleServerSession CreateOutgoingBidirectionalStream without + WebTransport support) + << "QuicSimpleServerSession::CreateOutgoingBidirectionalStream called " + "in a session without WebTransport support."; + return nullptr; + } + if (!ShouldCreateOutgoingBidirectionalStream()) { + return nullptr; + } + + QuicServerInitiatedSpdyStream* stream = new QuicServerInitiatedSpdyStream( + GetNextOutgoingBidirectionalStreamId(), this, BIDIRECTIONAL); + ActivateStream(absl::WrapUnique(stream)); + return stream; } QuicSimpleServerStream* @@ -128,7 +141,7 @@ QuicSimpleServerSession::CreateOutgoingUnidirectionalStream() { QuicSimpleServerStream* stream = new QuicSimpleServerStream( GetNextOutgoingUnidirectionalStreamId(), this, WRITE_UNIDIRECTIONAL, quic_simple_server_backend_); - ActivateStream(QuicWrapUnique(stream)); + ActivateStream(absl::WrapUnique(stream)); return stream; } 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 49aaae56b37..b91966da722 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 @@ -85,7 +85,7 @@ class QuicSimpleServerSession : public QuicServerSessionBase { // QuicSession methods: QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override; QuicSpdyStream* CreateIncomingStream(PendingStream* pending) override; - QuicSimpleServerStream* CreateOutgoingBidirectionalStream() override; + QuicSpdyStream* CreateOutgoingBidirectionalStream() override; QuicSimpleServerStream* CreateOutgoingUnidirectionalStream() override; // Override to return true for locally preserved server push stream. void HandleFrameOnNonexistentOutgoingStream(QuicStreamId stream_id) override; @@ -104,6 +104,14 @@ class QuicSimpleServerSession : public QuicServerSessionBase { void MaybeInitializeHttp3UnidirectionalStreams() override; + bool ShouldNegotiateWebTransport() override { + return quic_simple_server_backend_->SupportsWebTransport(); + } + bool ShouldNegotiateHttp3Datagram() override { + return QuicServerSessionBase::ShouldNegotiateHttp3Datagram() || + ShouldNegotiateWebTransport(); + } + private: friend class test::QuicSimpleServerSessionPeer; diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session_test.cc index 80d3a930349..62bbae949d5 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session_test.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session_test.cc @@ -8,6 +8,7 @@ #include <memory> #include <utility> +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "quic/core/crypto/null_encrypter.h" #include "quic/core/crypto/quic_crypto_server_config.h" @@ -37,7 +38,6 @@ #include "quic/tools/quic_backend_response.h" #include "quic/tools/quic_memory_cache_backend.h" #include "quic/tools/quic_simple_server_stream.h" -#include "common/platform/api/quiche_text_utils.h" using testing::_; using testing::AnyNumber; @@ -135,9 +135,9 @@ QuicCryptoServerStreamBase* CreateMockCryptoServerStream( case PROTOCOL_UNSUPPORTED: break; } - QUIC_BUG << "Unknown handshake protocol: " - << static_cast<int>( - session->connection()->version().handshake_protocol); + QUIC_BUG(quic_bug_10933_1) + << "Unknown handshake protocol: " + << static_cast<int>(session->connection()->version().handshake_protocol); return nullptr; } @@ -699,8 +699,7 @@ class QuicSimpleServerSessionServerPushTest } else { stream_id = GetNthServerInitiatedUnidirectionalId(i - 1); } - std::string path = partial_push_resource_path + - quiche::QuicheTextUtils::Uint64ToString(i); + std::string path = absl::StrCat(partial_push_resource_path, i); std::string url = scheme + "://" + resource_host + path; QuicUrl resource_url = QuicUrl(url); std::string body(body_size, 'a'); @@ -774,19 +773,26 @@ class QuicSimpleServerSessionServerPushTest } }; +ParsedQuicVersionVector SupportedVersionsWithPush() { + ParsedQuicVersionVector versions; + for (const ParsedQuicVersion& version : AllSupportedVersions()) { + if (!version.UsesHttp3()) { + // Push over HTTP/3 is not supported. + versions.push_back(version); + } + } + return versions; +} + INSTANTIATE_TEST_SUITE_P(Tests, QuicSimpleServerSessionServerPushTest, - ::testing::ValuesIn(AllSupportedVersions())); + ::testing::ValuesIn(SupportedVersionsWithPush())); // Tests that given more than kMaxStreamsForTest resources, all their // PUSH_PROMISE's will be sent out and only kMaxStreamsForTest streams will be // opened and send push response. TEST_P(QuicSimpleServerSessionServerPushTest, TestPromisePushResources) { MaybeConsumeHeadersStreamData(); - if (VersionUsesHttp3(transport_version())) { - session_->EnableServerPush(); - session_->OnMaxPushIdFrame(kMaxQuicStreamId); - } size_t num_resources = kMaxStreamsForTest + 5; PromisePushResources(num_resources); EXPECT_EQ(kMaxStreamsForTest, @@ -798,10 +804,6 @@ TEST_P(QuicSimpleServerSessionServerPushTest, TestPromisePushResources) { TEST_P(QuicSimpleServerSessionServerPushTest, HandlePromisedPushRequestsAfterStreamDraining) { MaybeConsumeHeadersStreamData(); - if (VersionUsesHttp3(transport_version())) { - session_->EnableServerPush(); - session_->OnMaxPushIdFrame(kMaxQuicStreamId); - } size_t num_resources = kMaxStreamsForTest + 1; QuicByteCount data_frame_header_length = PromisePushResources(num_resources); QuicStreamId next_out_going_stream_id; @@ -879,10 +881,6 @@ TEST_P(QuicSimpleServerSessionServerPushTest, return; } MaybeConsumeHeadersStreamData(); - if (VersionUsesHttp3(transport_version())) { - session_->EnableServerPush(); - session_->OnMaxPushIdFrame(kMaxQuicStreamId); - } // Having two extra resources to be send later. One of them will be reset, so // when opened stream become close, only one will become open. @@ -969,10 +967,6 @@ TEST_P(QuicSimpleServerSessionServerPushTest, TEST_P(QuicSimpleServerSessionServerPushTest, CloseStreamToHandleMorePromisedStream) { MaybeConsumeHeadersStreamData(); - if (VersionUsesHttp3(transport_version())) { - session_->EnableServerPush(); - session_->OnMaxPushIdFrame(kMaxQuicStreamId); - } size_t num_resources = kMaxStreamsForTest + 1; if (VersionHasIetfQuicFrames(transport_version())) { // V99 will send out a stream-id-blocked frame when the we desired to exceed 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 800608bf0ba..f4afeb241c0 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 @@ -9,16 +9,17 @@ #include "absl/strings/match.h" #include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "quic/core/http/quic_spdy_stream.h" #include "quic/core/http/spdy_utils.h" +#include "quic/core/http/web_transport_http3.h" #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_bug_tracker.h" #include "quic/platform/api/quic_flags.h" #include "quic/platform/api/quic_logging.h" #include "quic/platform/api/quic_map_util.h" #include "quic/tools/quic_simple_server_session.h" -#include "common/platform/api/quiche_text_utils.h" #include "spdy/core/spdy_protocol.h" using spdy::Http2HeaderBlock; @@ -64,7 +65,7 @@ void QuicSimpleServerStream::OnInitialHeadersComplete( SendErrorResponse(); } ConsumeHeaderList(); - if (!fin) { + if (!fin && !response_sent_) { // CONNECT and other CONNECT-like methods (such as CONNECT-UDP) require // sending the response right after parsing the headers even though the FIN // bit has not been received on the request stream. @@ -80,7 +81,7 @@ void QuicSimpleServerStream::OnTrailingHeadersComplete( bool /*fin*/, size_t /*frame_len*/, const QuicHeaderList& /*header_list*/) { - QUIC_BUG << "Server does not support receiving Trailers."; + QUIC_BUG(quic_bug_10962_1) << "Server does not support receiving Trailers."; SendErrorResponse(); } @@ -124,7 +125,8 @@ void QuicSimpleServerStream::PushResponse( Http2HeaderBlock push_request_headers) { if (QuicUtils::IsClientInitiatedStreamId(session()->transport_version(), id())) { - QUIC_BUG << "Client initiated stream shouldn't be used as promised stream."; + QUIC_BUG(quic_bug_10962_2) + << "Client initiated stream shouldn't be used as promised stream."; return; } // Change the stream state to emulate a client request. @@ -153,19 +155,46 @@ void QuicSimpleServerStream::SendResponse() { return; } - if (!QuicContainsKey(request_headers_, ":authority") || - !QuicContainsKey(request_headers_, ":path")) { - QUIC_DVLOG(1) << "Request headers do not contain :authority or :path."; + if (!QuicContainsKey(request_headers_, ":authority")) { + QUIC_DVLOG(1) << "Request headers do not contain :authority."; SendErrorResponse(); return; } + if (!QuicContainsKey(request_headers_, ":path")) { + // CONNECT and other CONNECT-like methods (such as CONNECT-UDP) do not all + // require :path to be present. + auto it = request_headers_.find(":method"); + if (it == request_headers_.end() || + !absl::StartsWith(it->second, "CONNECT")) { + QUIC_DVLOG(1) << "Request headers do not contain :path."; + SendErrorResponse(); + return; + } + } + if (quic_simple_server_backend_ == nullptr) { QUIC_DVLOG(1) << "Backend is missing."; SendErrorResponse(); return; } + if (web_transport() != nullptr) { + QuicSimpleServerBackend::WebTransportResponse response = + quic_simple_server_backend_->ProcessWebTransportRequest( + request_headers_, web_transport()); + if (response.response_headers[":status"] == "200") { + WriteHeaders(std::move(response.response_headers), false, nullptr); + if (response.visitor != nullptr) { + web_transport()->SetVisitor(std::move(response.visitor)); + } + web_transport()->HeadersReceived(request_headers_); + } else { + WriteHeaders(std::move(response.response_headers), true, nullptr); + } + return; + } + // Fetch the response from the backend interface and wait for callback once // response is ready quic_simple_server_backend_->FetchResponseFromBackend(request_headers_, body_, @@ -193,6 +222,13 @@ void QuicSimpleServerStream::OnResponseBackendComplete( return; } + // Send Early Hints first. + for (const auto& headers : response->early_hints()) { + QUIC_DVLOG(1) << "Stream " << id() << " sending an Early Hints response: " + << headers.DebugString(); + WriteHeaders(headers.Clone(), false, nullptr); + } + if (response->response_type() == QuicBackendResponse::CLOSE_CONNECTION) { QUIC_DVLOG(1) << "Special response: closing connection."; OnUnrecoverableError(QUIC_NO_ERROR, "Toy server forcing close"); @@ -275,10 +311,11 @@ void QuicSimpleServerStream::OnResponseBackendComplete( return; } Http2HeaderBlock headers = response->headers().Clone(); - headers["content-length"] = - quiche::QuicheTextUtils::Uint64ToString(generate_bytes_length_); + headers["content-length"] = absl::StrCat(generate_bytes_length_); WriteHeaders(std::move(headers), false, nullptr); + QUICHE_DCHECK(!response_sent_); + response_sent_ = true; WriteGeneratedBytes(); @@ -310,8 +347,7 @@ void QuicSimpleServerStream::SendNotFoundResponse() { QUIC_DVLOG(1) << "Stream " << id() << " sending not found response."; Http2HeaderBlock headers; headers[":status"] = "404"; - headers["content-length"] = - quiche::QuicheTextUtils::Uint64ToString(strlen(kNotFoundResponseBody)); + headers["content-length"] = absl::StrCat(strlen(kNotFoundResponseBody)); SendHeadersAndBody(std::move(headers), kNotFoundResponseBody); } @@ -325,10 +361,9 @@ void QuicSimpleServerStream::SendErrorResponse(int resp_code) { if (resp_code <= 0) { headers[":status"] = "500"; } else { - headers[":status"] = quiche::QuicheTextUtils::Uint64ToString(resp_code); + headers[":status"] = absl::StrCat(resp_code); } - headers["content-length"] = - quiche::QuicheTextUtils::Uint64ToString(strlen(kErrorResponseBody)); + headers["content-length"] = absl::StrCat(strlen(kErrorResponseBody)); SendHeadersAndBody(std::move(headers), kErrorResponseBody); } @@ -338,6 +373,8 @@ void QuicSimpleServerStream::SendIncompleteResponse( QUIC_DLOG(INFO) << "Stream " << id() << " writing headers (fin = false) : " << response_headers.DebugString(); WriteHeaders(std::move(response_headers), /*fin=*/false, nullptr); + QUICHE_DCHECK(!response_sent_); + response_sent_ = true; QUIC_DLOG(INFO) << "Stream " << id() << " writing body (fin = false) with size: " << body.size(); @@ -362,6 +399,8 @@ void QuicSimpleServerStream::SendHeadersAndBodyAndTrailers( QUIC_DLOG(INFO) << "Stream " << id() << " writing headers (fin = " << send_fin << ") : " << response_headers.DebugString(); WriteHeaders(std::move(response_headers), send_fin, nullptr); + QUICHE_DCHECK(!response_sent_); + response_sent_ = true; if (send_fin) { // Nothing else to send. return; 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 bbed95a2bcd..2dd59f9fc91 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 @@ -99,6 +99,8 @@ class QuicSimpleServerStream : public QuicSpdyServerStreamBase, private: uint64_t generate_bytes_length_; + // Whether response headers have already been sent. + bool response_sent_ = false; QuicSimpleServerBackend* quic_simple_server_backend_; // Not owned. }; 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 5806ad72de1..bf80d476020 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 @@ -9,6 +9,7 @@ #include <utility> #include "absl/base/macros.h" +#include "absl/memory/memory.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "quic/core/crypto/null_encrypter.h" @@ -18,7 +19,6 @@ #include "quic/core/quic_types.h" #include "quic/core/quic_utils.h" #include "quic/platform/api/quic_expect_bug.h" -#include "quic/platform/api/quic_ptr_util.h" #include "quic/platform/api/quic_socket_address.h" #include "quic/platform/api/quic_test.h" #include "quic/test_tools/crypto_test_utils.h" @@ -59,12 +59,17 @@ class TestStream : public QuicSimpleServerStream { ~TestStream() override = default; MOCK_METHOD(void, WriteHeadersMock, (bool fin), ()); + MOCK_METHOD(void, WriteEarlyHintsHeadersMock, (bool fin), ()); - size_t WriteHeaders(spdy::Http2HeaderBlock /*header_block*/, + size_t WriteHeaders(spdy::Http2HeaderBlock header_block, bool fin, QuicReferenceCountedPointer<QuicAckListenerInterface> /*ack_listener*/) override { - WriteHeadersMock(fin); + if (header_block[":status"] == "103") { + WriteEarlyHintsHeadersMock(fin); + } else { + WriteHeadersMock(fin); + } return 0; } @@ -77,6 +82,9 @@ class TestStream : public QuicSimpleServerStream { const std::string& body() const { return body_; } int content_length() const { return content_length_; } bool send_response_was_called() const { return send_response_was_called_; } + bool send_error_response_was_called() const { + return send_error_response_was_called_; + } absl::string_view GetHeader(absl::string_view key) const { auto it = request_headers_.find(key); @@ -90,8 +98,14 @@ class TestStream : public QuicSimpleServerStream { QuicSimpleServerStream::SendResponse(); } + void SendErrorResponse() override { + send_error_response_was_called_ = true; + QuicSimpleServerStream::SendErrorResponse(); + } + private: bool send_response_was_called_ = false; + bool send_error_response_was_called_ = false; }; namespace { @@ -266,7 +280,7 @@ class QuicSimpleServerStreamTest : public QuicTestWithParam<ParsedQuicVersion> { connection_->transport_version(), 0), &session_, BIDIRECTIONAL, &memory_cache_backend_); // Register stream_ in dynamic_stream_map_ and pass ownership to session_. - session_.ActivateStream(QuicWrapUnique(stream_)); + session_.ActivateStream(absl::WrapUnique(stream_)); QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow( session_.config(), kMinimumFlowControlSendWindow); QuicConfigPeer::SetReceivedInitialMaxStreamDataBytesUnidirectional( @@ -474,7 +488,7 @@ TEST_P(QuicSimpleServerStreamTest, SendPushResponseWith404Response) { GetNthServerInitiatedUnidirectionalStreamId( connection_->transport_version(), 3), &session_, WRITE_UNIDIRECTIONAL, &memory_cache_backend_); - session_.ActivateStream(QuicWrapUnique(promised_stream)); + session_.ActivateStream(absl::WrapUnique(promised_stream)); // Send a push response with response status 404, which will be regarded as // invalid server push response. @@ -572,6 +586,50 @@ TEST_P(QuicSimpleServerStreamTest, SendResponseWithPushResources) { EXPECT_EQ(*request_headers, session_.original_request_headers_); } +TEST_P(QuicSimpleServerStreamTest, SendResponseWithEarlyHints) { + std::string host = "www.google.com"; + std::string request_path = "/foo"; + std::string body = "Yummm"; + + // Add a request and response with early hints. + spdy::Http2HeaderBlock* request_headers = stream_->mutable_headers(); + (*request_headers)[":path"] = request_path; + (*request_headers)[":authority"] = host; + (*request_headers)[":method"] = "GET"; + + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + HttpEncoder::SerializeDataFrameHeader(body.length(), &buffer); + std::vector<spdy::Http2HeaderBlock> early_hints; + // Add two Early Hints. + const size_t kNumEarlyHintsResponses = 2; + for (size_t i = 0; i < kNumEarlyHintsResponses; ++i) { + spdy::Http2HeaderBlock hints; + hints["link"] = "</image.png>; rel=preload; as=image"; + early_hints.push_back(std::move(hints)); + } + + response_headers_[":status"] = "200"; + response_headers_["content-length"] = "5"; + memory_cache_backend_.AddResponseWithEarlyHints( + host, request_path, std::move(response_headers_), body, early_hints); + QuicStreamPeer::SetFinReceived(stream_); + + InSequence s; + for (size_t i = 0; i < kNumEarlyHintsResponses; ++i) { + EXPECT_CALL(*stream_, WriteEarlyHintsHeadersMock(false)); + } + EXPECT_CALL(*stream_, WriteHeadersMock(false)); + if (UsesHttp3()) { + EXPECT_CALL(session_, WritevData(_, header_length, _, NO_FIN, _, _)); + } + EXPECT_CALL(session_, WritevData(_, body.length(), _, FIN, _, _)); + + stream_->DoSendResponse(); + EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_)); + EXPECT_TRUE(stream_->write_side_closed()); +} + TEST_P(QuicSimpleServerStreamTest, PushResponseOnClientInitiatedStream) { // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. if (GetParam() != AllSupportedVersions()[0]) { @@ -597,7 +655,7 @@ TEST_P(QuicSimpleServerStreamTest, PushResponseOnServerInitiatedStream) { auto server_initiated_stream = new StrictMock<TestStream>(kServerInitiatedStreamId, &session_, WRITE_UNIDIRECTIONAL, &memory_cache_backend_); - session_.ActivateStream(QuicWrapUnique(server_initiated_stream)); + session_.ActivateStream(absl::WrapUnique(server_initiated_stream)); const std::string kHost = "www.foo.com"; const std::string kPath = "/bar"; @@ -778,6 +836,33 @@ TEST_P(QuicSimpleServerStreamTest, ConnectSendsResponseBeforeFinReceived) { EXPECT_EQ("CONNECT-SILLY", StreamHeadersValue(":method")); EXPECT_EQ(body_, StreamBody()); EXPECT_TRUE(stream_->send_response_was_called()); + EXPECT_FALSE(stream_->send_error_response_was_called()); +} + +TEST_P(QuicSimpleServerStreamTest, ConnectWithInvalidHeader) { + EXPECT_CALL(session_, WritevData(_, _, _, _, _, _)) + .WillRepeatedly( + Invoke(&session_, &MockQuicSimpleServerSession::ConsumeData)); + QuicHeaderList header_list; + header_list.OnHeaderBlockStart(); + header_list.OnHeader(":authority", "www.google.com:4433"); + header_list.OnHeader(":method", "CONNECT-SILLY"); + // QUIC requires lower-case header names. + header_list.OnHeader("InVaLiD-HeAdEr", "Well that's just wrong!"); + header_list.OnHeaderBlockEnd(128, 128); + EXPECT_CALL(*stream_, WriteHeadersMock(/*fin=*/false)); + stream_->OnStreamHeaderList(/*fin=*/false, kFakeFrameLen, header_list); + std::unique_ptr<char[]> buffer; + QuicByteCount header_length = + HttpEncoder::SerializeDataFrameHeader(body_.length(), &buffer); + std::string header = std::string(buffer.get(), header_length); + std::string data = UsesHttp3() ? header + body_ : body_; + stream_->OnStreamFrame( + QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data)); + EXPECT_EQ("CONNECT-SILLY", StreamHeadersValue(":method")); + EXPECT_EQ(body_, StreamBody()); + EXPECT_FALSE(stream_->send_response_was_called()); + EXPECT_TRUE(stream_->send_error_response_was_called()); } } // namespace diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc index d28eb771a7d..974344ed4aa 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc @@ -50,8 +50,7 @@ QuicSpdyClientBase::QuicSpdyClientBase( std::move(proof_verifier), std::move(session_cache)), store_response_(false), - latest_response_code_(-1), - max_allowed_push_id_(0) {} + latest_response_code_(-1) {} QuicSpdyClientBase::~QuicSpdyClientBase() { // We own the push promise index. We need to explicitly kill @@ -70,10 +69,6 @@ const QuicSpdyClientSession* QuicSpdyClientBase::client_session() const { void QuicSpdyClientBase::InitializeSession() { client_session()->Initialize(); client_session()->CryptoConnect(); - if (max_allowed_push_id_ > 0 && - VersionUsesHttp3(client_session()->transport_version())) { - client_session()->SetMaxPushId(max_allowed_push_id_); - } } void QuicSpdyClientBase::OnClose(QuicSpdyStream* stream) { @@ -146,7 +141,7 @@ void QuicSpdyClientBase::SendRequestInternal(Http2HeaderBlock sanitized_headers, QuicSpdyClientStream* stream = CreateClientStream(); if (stream == nullptr) { - QUIC_BUG << "stream creation failed!"; + QUIC_BUG(quic_bug_10949_1) << "stream creation failed!"; return; } stream->SendRequest(std::move(sanitized_headers), body, fin); @@ -166,7 +161,7 @@ void QuicSpdyClientBase::SendRequestsAndWaitForResponse( for (size_t i = 0; i < url_list.size(); ++i) { Http2HeaderBlock headers; if (!SpdyUtils::PopulateHeaderBlockFromUrl(url_list[i], &headers)) { - QUIC_BUG << "Unable to create request"; + QUIC_BUG(quic_bug_10949_2) << "Unable to create request"; continue; } SendRequest(headers, "", true); @@ -260,33 +255,33 @@ void QuicSpdyClientBase::OnRendezvousResult(QuicSpdyStream* stream) { } int QuicSpdyClientBase::latest_response_code() const { - QUIC_BUG_IF(!store_response_) << "Response not stored!"; + QUIC_BUG_IF(quic_bug_10949_3, !store_response_) << "Response not stored!"; return latest_response_code_; } const std::string& QuicSpdyClientBase::latest_response_headers() const { - QUIC_BUG_IF(!store_response_) << "Response not stored!"; + QUIC_BUG_IF(quic_bug_10949_4, !store_response_) << "Response not stored!"; return latest_response_headers_; } const std::string& QuicSpdyClientBase::preliminary_response_headers() const { - QUIC_BUG_IF(!store_response_) << "Response not stored!"; + QUIC_BUG_IF(quic_bug_10949_5, !store_response_) << "Response not stored!"; return preliminary_response_headers_; } const Http2HeaderBlock& QuicSpdyClientBase::latest_response_header_block() const { - QUIC_BUG_IF(!store_response_) << "Response not stored!"; + QUIC_BUG_IF(quic_bug_10949_6, !store_response_) << "Response not stored!"; return latest_response_header_block_; } const std::string& QuicSpdyClientBase::latest_response_body() const { - QUIC_BUG_IF(!store_response_) << "Response not stored!"; + QUIC_BUG_IF(quic_bug_10949_7, !store_response_) << "Response not stored!"; return latest_response_body_; } const std::string& QuicSpdyClientBase::latest_response_trailers() const { - QUIC_BUG_IF(!store_response_) << "Response not stored!"; + QUIC_BUG_IF(quic_bug_10949_8, !store_response_) << "Response not stored!"; return latest_response_trailers_; } diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h b/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h index 54c73f780cf..2ba55f11693 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h @@ -139,9 +139,10 @@ class QuicSpdyClientBase : public QuicClientBase, } bool drop_response_body() const { return drop_response_body_; } - // Set the max promise id for the client session. - // TODO(b/151641466): Rename this method. - void SetMaxAllowedPushId(PushId max) { max_allowed_push_id_ = max; } + void set_enable_web_transport(bool enable_web_transport) { + enable_web_transport_ = enable_web_transport; + } + bool enable_web_transport() const { return enable_web_transport_; } // QuicClientBase methods. bool goaway_received() const override; @@ -221,9 +222,7 @@ class QuicSpdyClientBase : public QuicClientBase, std::unique_ptr<ClientQuicDataToResend> push_promise_data_to_resend_; bool drop_response_body_ = false; - - // The max promise id to set on the client session when created. - PushId max_allowed_push_id_; + bool enable_web_transport_ = false; }; } // namespace quic diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.cc index 37e23b60c76..22e873185b6 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.cc @@ -30,7 +30,7 @@ QuicIntervalSet<uint64_t> QuicTcpLikeTraceConverter::OnCryptoFrameSent( QuicStreamOffset offset, QuicByteCount data_length) { if (level >= NUM_ENCRYPTION_LEVELS) { - QUIC_BUG << "Invalid encryption level"; + QUIC_BUG(quic_bug_10907_1) << "Invalid encryption level"; return {}; } return OnFrameSent(offset, data_length, /*fin=*/false, diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_transport_simple_server_dispatcher.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_transport_simple_server_dispatcher.cc index 7029cf70be9..25730eda818 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_transport_simple_server_dispatcher.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_transport_simple_server_dispatcher.cc @@ -39,7 +39,8 @@ QuicTransportSimpleServerDispatcher::CreateQuicSession( const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, absl::string_view /*alpn*/, - const ParsedQuicVersion& version) { + const ParsedQuicVersion& version, + absl::string_view /*sni*/) { auto connection = std::make_unique<QuicConnection>( server_connection_id, self_address, peer_address, helper(), alarm_factory(), writer(), diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_transport_simple_server_dispatcher.h b/chromium/net/third_party/quiche/src/quic/tools/quic_transport_simple_server_dispatcher.h index 500b02d55fb..bb38596bf19 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_transport_simple_server_dispatcher.h +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_transport_simple_server_dispatcher.h @@ -32,7 +32,8 @@ class QuicTransportSimpleServerDispatcher : public QuicDispatcher { const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, absl::string_view alpn, - const ParsedQuicVersion& version) override; + const ParsedQuicVersion& version, + absl::string_view sni) override; std::vector<url::Origin> accepted_origins_; }; diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_transport_simple_server_session.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_transport_simple_server_session.cc index ad93115705f..bc7ef192860 100644 --- a/chromium/net/third_party/quiche/src/quic/tools/quic_transport_simple_server_session.cc +++ b/chromium/net/third_party/quiche/src/quic/tools/quic_transport_simple_server_session.cc @@ -15,115 +15,10 @@ #include "quic/platform/api/quic_logging.h" #include "quic/quic_transport/quic_transport_protocol.h" #include "quic/quic_transport/quic_transport_stream.h" +#include "quic/tools/web_transport_test_visitors.h" namespace quic { -namespace { - -// Discards any incoming data. -class DiscardVisitor : public QuicTransportStream::Visitor { - public: - DiscardVisitor(QuicTransportStream* stream) : stream_(stream) {} - - void OnCanRead() override { - std::string buffer; - size_t bytes_read = stream_->Read(&buffer); - QUIC_DVLOG(2) << "Read " << bytes_read << " bytes from stream " - << stream_->id(); - } - - void OnFinRead() override {} - void OnCanWrite() override {} - - private: - QuicTransportStream* stream_; -}; - -// Echoes any incoming data back on the same stream. -class BidirectionalEchoVisitor : public QuicTransportStream::Visitor { - public: - BidirectionalEchoVisitor(QuicTransportStream* stream) : stream_(stream) {} - - void OnCanRead() override { - stream_->Read(&buffer_); - OnCanWrite(); - } - - void OnFinRead() override { - bool success = stream_->SendFin(); - QUICHE_DCHECK(success); - } - - void OnCanWrite() override { - if (buffer_.empty()) { - return; - } - - bool success = stream_->Write(buffer_); - if (success) { - buffer_ = ""; - } - } - - private: - QuicTransportStream* stream_; - std::string buffer_; -}; - -// Buffers all of the data and calls EchoStreamBack() on the parent session. -class UnidirectionalEchoReadVisitor : public QuicTransportStream::Visitor { - public: - UnidirectionalEchoReadVisitor(QuicTransportSimpleServerSession* session, - QuicTransportStream* stream) - : session_(session), stream_(stream) {} - - void OnCanRead() override { - bool success = stream_->Read(&buffer_); - QUICHE_DCHECK(success); - } - - void OnFinRead() override { - QUIC_DVLOG(1) << "Finished receiving data on stream " << stream_->id() - << ", queueing up the echo"; - session_->EchoStreamBack(buffer_); - } - - void OnCanWrite() override { QUIC_NOTREACHED(); } - - private: - QuicTransportSimpleServerSession* session_; - QuicTransportStream* stream_; - std::string buffer_; -}; - -// Sends supplied data. -class UnidirectionalEchoWriteVisitor : public QuicTransportStream::Visitor { - public: - UnidirectionalEchoWriteVisitor(QuicTransportStream* stream, - const std::string& data) - : stream_(stream), data_(data) {} - - void OnCanRead() override { QUIC_NOTREACHED(); } - void OnFinRead() override { QUIC_NOTREACHED(); } - void OnCanWrite() override { - if (data_.empty()) { - return; - } - if (!stream_->Write(data_)) { - return; - } - data_ = ""; - bool fin_sent = stream_->SendFin(); - QUICHE_DCHECK(fin_sent); - } - - private: - QuicTransportStream* stream_; - std::string data_; -}; - -} // namespace - QuicTransportSimpleServerSession::QuicTransportSimpleServerSession( QuicConnection* connection, bool owns_connection, @@ -154,22 +49,24 @@ void QuicTransportSimpleServerSession::OnIncomingDataStream( QuicTransportStream* stream) { switch (mode_) { case DISCARD: - stream->set_visitor(std::make_unique<DiscardVisitor>(stream)); + stream->SetVisitor(std::make_unique<WebTransportDiscardVisitor>(stream)); break; case ECHO: switch (stream->type()) { case BIDIRECTIONAL: QUIC_DVLOG(1) << "Opening bidirectional echo stream " << stream->id(); - stream->set_visitor( - std::make_unique<BidirectionalEchoVisitor>(stream)); + stream->SetVisitor( + std::make_unique<WebTransportBidirectionalEchoVisitor>(stream)); break; case READ_UNIDIRECTIONAL: QUIC_DVLOG(1) << "Started receiving data on unidirectional echo stream " << stream->id(); - stream->set_visitor( - std::make_unique<UnidirectionalEchoReadVisitor>(this, stream)); + stream->SetVisitor( + std::make_unique<WebTransportUnidirectionalEchoReadVisitor>( + stream, + [this](const std::string& s) { this->EchoStreamBack(s); })); break; default: QUIC_NOTREACHED(); @@ -178,7 +75,7 @@ void QuicTransportSimpleServerSession::OnIncomingDataStream( break; case OUTGOING_BIDIRECTIONAL: - stream->set_visitor(std::make_unique<DiscardVisitor>(stream)); + stream->SetVisitor(std::make_unique<WebTransportDiscardVisitor>(stream)); ++pending_outgoing_bidirectional_streams_; MaybeCreateOutgoingBidirectionalStream(); break; @@ -252,8 +149,9 @@ void QuicTransportSimpleServerSession::MaybeEchoStreamsBack() { ActivateStream(std::move(stream_owned)); QUIC_DVLOG(1) << "Opened echo response stream " << stream->id(); - stream->set_visitor( - std::make_unique<UnidirectionalEchoWriteVisitor>(stream, data)); + stream->SetVisitor( + std::make_unique<WebTransportUnidirectionalEchoWriteVisitor>(stream, + data)); stream->visitor()->OnCanWrite(); } } @@ -267,7 +165,8 @@ void QuicTransportSimpleServerSession:: QuicTransportStream* stream = stream_owned.get(); ActivateStream(std::move(stream_owned)); QUIC_DVLOG(1) << "Opened outgoing bidirectional stream " << stream->id(); - stream->set_visitor(std::make_unique<BidirectionalEchoVisitor>(stream)); + stream->SetVisitor( + std::make_unique<WebTransportBidirectionalEchoVisitor>(stream)); if (!stream->Write("hello")) { QUIC_DVLOG(1) << "Write failed."; } 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 new file mode 100644 index 00000000000..9bf6e6bca84 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quic/tools/web_transport_test_visitors.h @@ -0,0 +1,137 @@ +// 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_TOOLS_WEB_TRANSPORT_TEST_VISITORS_H_ +#define QUICHE_QUIC_TOOLS_WEB_TRANSPORT_TEST_VISITORS_H_ + +#include <string> + +#include "quic/core/web_transport_interface.h" +#include "quic/platform/api/quic_logging.h" + +namespace quic { + +// Discards any incoming data. +class WebTransportDiscardVisitor : public WebTransportStreamVisitor { + public: + WebTransportDiscardVisitor(WebTransportStream* stream) : stream_(stream) {} + + void OnCanRead() override { + std::string buffer; + WebTransportStream::ReadResult result = stream_->Read(&buffer); + QUIC_DVLOG(2) << "Read " << result.bytes_read + << " bytes from WebTransport stream " + << stream_->GetStreamId() << ", fin: " << result.fin; + } + + void OnCanWrite() override {} + + private: + WebTransportStream* stream_; +}; + +// Echoes any incoming data back on the same stream. +class WebTransportBidirectionalEchoVisitor : public WebTransportStreamVisitor { + public: + WebTransportBidirectionalEchoVisitor(WebTransportStream* stream) + : stream_(stream) {} + + void OnCanRead() override { + WebTransportStream::ReadResult result = stream_->Read(&buffer_); + QUIC_DVLOG(1) << "Attempted reading on WebTransport bidirectional stream " + << stream_->GetStreamId() + << ", bytes read: " << result.bytes_read; + if (result.fin) { + send_fin_ = true; + } + OnCanWrite(); + } + + void OnCanWrite() override { + if (!buffer_.empty()) { + bool success = stream_->Write(buffer_); + QUIC_DVLOG(1) << "Attempted writing on WebTransport bidirectional stream " + << stream_->GetStreamId() + << ", success: " << (success ? "yes" : "no"); + if (!success) { + return; + } + + buffer_ = ""; + } + + if (send_fin_) { + bool success = stream_->SendFin(); + QUICHE_DCHECK(success); + } + } + + private: + WebTransportStream* stream_; + std::string buffer_; + bool send_fin_ = false; +}; + +// Buffers all of the data and calls |callback| with the entirety of the stream +// data. +class WebTransportUnidirectionalEchoReadVisitor + : public WebTransportStreamVisitor { + public: + using Callback = std::function<void(const std::string&)>; + + WebTransportUnidirectionalEchoReadVisitor(WebTransportStream* stream, + Callback callback) + : stream_(stream), callback_(std::move(callback)) {} + + void OnCanRead() override { + WebTransportStream::ReadResult result = stream_->Read(&buffer_); + QUIC_DVLOG(1) << "Attempted reading on WebTransport unidirectional stream " + << stream_->GetStreamId() + << ", bytes read: " << result.bytes_read; + if (result.fin) { + QUIC_DVLOG(1) << "Finished receiving data on a WebTransport stream " + << stream_->GetStreamId() << ", queueing up the echo"; + callback_(buffer_); + } + } + + void OnCanWrite() override { QUIC_NOTREACHED(); } + + private: + WebTransportStream* stream_; + std::string buffer_; + Callback callback_; +}; + +// Sends supplied data. +class WebTransportUnidirectionalEchoWriteVisitor + : public WebTransportStreamVisitor { + public: + WebTransportUnidirectionalEchoWriteVisitor(WebTransportStream* stream, + const std::string& data) + : stream_(stream), data_(data) {} + + void OnCanRead() override { QUIC_NOTREACHED(); } + void OnCanWrite() override { + if (data_.empty()) { + return; + } + if (!stream_->Write(data_)) { + return; + } + data_ = ""; + bool fin_sent = stream_->SendFin(); + QUICHE_DVLOG(1) + << "WebTransportUnidirectionalEchoWriteVisitor finished sending data."; + QUICHE_DCHECK(fin_sent); + } + + private: + WebTransportStream* stream_; + std::string data_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_TOOLS_WEB_TRANSPORT_TEST_VISITORS_H_ diff --git a/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler.h b/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler.h deleted file mode 100644 index 7bf8c4ed475..00000000000 --- a/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler.h +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright (c) 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef QUICHE_SPDY_CORE_FIFO_WRITE_SCHEDULER_H_ -#define QUICHE_SPDY_CORE_FIFO_WRITE_SCHEDULER_H_ - -#include <map> -#include <set> -#include <string> - -#include "absl/strings/str_cat.h" -#include "spdy/core/write_scheduler.h" -#include "spdy/platform/api/spdy_string_utils.h" - -namespace spdy { - -// A write scheduler where the stream with the smallest stream ID will have the -// highest priority. -template <typename StreamIdType> -class FifoWriteScheduler : public WriteScheduler<StreamIdType> { - public: - using typename WriteScheduler<StreamIdType>::StreamPrecedenceType; - - FifoWriteScheduler() = default; - - // WriteScheduler methods - void RegisterStream(StreamIdType stream_id, - const StreamPrecedenceType& precedence) override; - void UnregisterStream(StreamIdType stream_id) override; - bool StreamRegistered(StreamIdType stream_id) const override; - // Stream precedence is available but note that it is not used for scheduling - // in this scheduler. - StreamPrecedenceType GetStreamPrecedence( - StreamIdType stream_id) const override; - void UpdateStreamPrecedence(StreamIdType stream_id, - const StreamPrecedenceType& precedence) override; - std::vector<StreamIdType> GetStreamChildren( - StreamIdType stream_id) const override; - void RecordStreamEventTime(StreamIdType stream_id, - int64_t now_in_usec) override; - int64_t GetLatestEventWithPrecedence(StreamIdType stream_id) const override; - bool ShouldYield(StreamIdType stream_id) const override; - void MarkStreamReady(StreamIdType stream_id, bool add_to_front) override; - void MarkStreamNotReady(StreamIdType stream_id) override; - bool HasReadyStreams() const override; - StreamIdType PopNextReadyStream() override; - std::tuple<StreamIdType, StreamPrecedenceType> - PopNextReadyStreamAndPrecedence() override; - size_t NumReadyStreams() const override; - bool IsStreamReady(StreamIdType stream_id) const override; - size_t NumRegisteredStreams() const override; - std::string DebugString() const override; - - private: - struct StreamInfo { - SpdyPriority priority; - int64_t event_time; // read/write event time (us since Unix epoch). - }; - - std::set<StreamIdType> ready_streams_; - std::map<StreamIdType, StreamInfo> registered_streams_; -}; - -template <typename StreamIdType> -void FifoWriteScheduler<StreamIdType>::RegisterStream( - StreamIdType stream_id, - const StreamPrecedenceType& precedence) { - if (StreamRegistered(stream_id)) { - SPDY_BUG << "Stream " << stream_id << " already registered"; - return; - } - registered_streams_.emplace_hint( - registered_streams_.end(), stream_id, - StreamInfo{/*priority=*/precedence.spdy3_priority(), /*event_time=*/0}); -} - -template <typename StreamIdType> -void FifoWriteScheduler<StreamIdType>::UnregisterStream( - StreamIdType stream_id) { - if (!StreamRegistered(stream_id)) { - SPDY_BUG << "Stream " << stream_id << " is not registered"; - return; - } - registered_streams_.erase(stream_id); - ready_streams_.erase(stream_id); -} - -template <typename StreamIdType> -bool FifoWriteScheduler<StreamIdType>::StreamRegistered( - StreamIdType stream_id) const { - return registered_streams_.find(stream_id) != registered_streams_.end(); -} - -// Stream precedence is not supported by this scheduler. -template <typename StreamIdType> -typename FifoWriteScheduler<StreamIdType>::StreamPrecedenceType -FifoWriteScheduler<StreamIdType>::GetStreamPrecedence( - StreamIdType stream_id) const { - auto it = registered_streams_.find(stream_id); - if (it == registered_streams_.end()) { - SPDY_DVLOG(1) << "Stream " << stream_id << " not registered"; - return StreamPrecedenceType(kV3LowestPriority); - } - return StreamPrecedenceType(it->second.priority); -} - -template <typename StreamIdType> -void FifoWriteScheduler<StreamIdType>::UpdateStreamPrecedence( - StreamIdType stream_id, - const StreamPrecedenceType& precedence) { - auto it = registered_streams_.find(stream_id); - if (it == registered_streams_.end()) { - SPDY_DVLOG(1) << "Stream " << stream_id << " not registered"; - return; - } - it->second.priority = precedence.spdy3_priority(); -} - -template <typename StreamIdType> -std::vector<StreamIdType> FifoWriteScheduler<StreamIdType>::GetStreamChildren( - StreamIdType /*stream_id*/) const { - return std::vector<StreamIdType>(); -} - -template <typename StreamIdType> -void FifoWriteScheduler<StreamIdType>::RecordStreamEventTime( - StreamIdType stream_id, - int64_t now_in_usec) { - auto it = registered_streams_.find(stream_id); - if (it != registered_streams_.end()) { - it->second.event_time = now_in_usec; - } else { - SPDY_BUG << "Stream " << stream_id << " is not registered"; - } -} - -template <typename StreamIdType> -int64_t FifoWriteScheduler<StreamIdType>::GetLatestEventWithPrecedence( - StreamIdType stream_id) const { - if (!StreamRegistered(stream_id)) { - SPDY_BUG << "Stream " << stream_id << " is not registered"; - return 0; - } - int64_t latest_event_time_us = 0; - for (auto it = registered_streams_.begin(); it != registered_streams_.end(); - ++it) { - if (stream_id <= it->first) { - break; - } - latest_event_time_us = - std::max(latest_event_time_us, it->second.event_time); - } - return latest_event_time_us; -} - -template <typename StreamIdType> -bool FifoWriteScheduler<StreamIdType>::ShouldYield( - StreamIdType stream_id) const { - return !ready_streams_.empty() && stream_id > *ready_streams_.begin(); -} - -template <typename StreamIdType> -void FifoWriteScheduler<StreamIdType>::MarkStreamReady(StreamIdType stream_id, - bool /*add_to_front*/) { - if (!StreamRegistered(stream_id)) { - SPDY_BUG << "Stream " << stream_id << " is not registered"; - return; - } - if (ready_streams_.find(stream_id) != ready_streams_.end()) { - SPDY_DVLOG(1) << "Stream already exists in the list"; - return; - } - ready_streams_.insert(stream_id); -} - -template <typename StreamIdType> -void FifoWriteScheduler<StreamIdType>::MarkStreamNotReady( - StreamIdType stream_id) { - auto it = ready_streams_.find(stream_id); - if (it == ready_streams_.end()) { - SPDY_DVLOG(1) << "Try to remove a stream that is not on list"; - return; - } - ready_streams_.erase(it); -} - -template <typename StreamIdType> -bool FifoWriteScheduler<StreamIdType>::HasReadyStreams() const { - return !ready_streams_.empty(); -} - -template <typename StreamIdType> -StreamIdType FifoWriteScheduler<StreamIdType>::PopNextReadyStream() { - if (ready_streams_.empty()) { - SPDY_BUG << "No ready streams available"; - return 0; - } - auto it = ready_streams_.begin(); - StreamIdType id = *it; - ready_streams_.erase(it); - return id; -} - -template <typename StreamIdType> -std::tuple<StreamIdType, - typename FifoWriteScheduler<StreamIdType>::StreamPrecedenceType> -FifoWriteScheduler<StreamIdType>::PopNextReadyStreamAndPrecedence() { - return std::make_tuple(PopNextReadyStream(), - StreamPrecedenceType(kV3LowestPriority)); -} - -template <typename StreamIdType> -size_t FifoWriteScheduler<StreamIdType>::NumReadyStreams() const { - return ready_streams_.size(); -} - -template <typename StreamIdType> -bool FifoWriteScheduler<StreamIdType>::IsStreamReady( - StreamIdType stream_id) const { - if (!StreamRegistered(stream_id)) { - SPDY_BUG << "Stream " << stream_id << " is not registered"; - return false; - } - return ready_streams_.find(stream_id) != ready_streams_.end(); -} - -template <typename StreamIdType> -size_t FifoWriteScheduler<StreamIdType>::NumRegisteredStreams() const { - return registered_streams_.size(); -} - -template <typename StreamIdType> -std::string FifoWriteScheduler<StreamIdType>::DebugString() const { - return absl::StrCat( - "FifoWriteScheduler {num_streams=", registered_streams_.size(), - " num_ready_streams=", NumReadyStreams(), "}"); -} - -} // namespace spdy - -#endif // QUICHE_SPDY_CORE_FIFO_WRITE_SCHEDULER_H_ diff --git a/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler_test.cc b/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler_test.cc deleted file mode 100644 index c2b1d7eb84a..00000000000 --- a/chromium/net/third_party/quiche/src/spdy/core/fifo_write_scheduler_test.cc +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "spdy/core/fifo_write_scheduler.h" - -#include "common/platform/api/quiche_test.h" -#include "spdy/platform/api/spdy_test_helpers.h" - -namespace spdy { - -namespace test { - -TEST(FifoWriteSchedulerTest, BasicTest) { - FifoWriteScheduler<SpdyStreamId> fifo; - EXPECT_FALSE(fifo.HasReadyStreams()); - EXPECT_SPDY_BUG( - EXPECT_EQ(0u, std::get<0>(fifo.PopNextReadyStreamAndPrecedence())), - "No ready streams available"); - EXPECT_SPDY_BUG(fifo.MarkStreamReady(9, true), "Stream 9 is not registered"); - EXPECT_SPDY_BUG(fifo.IsStreamReady(9), "Stream 9 is not registered"); - - SpdyStreamPrecedence precedence(1); - fifo.RegisterStream(3, precedence); - EXPECT_FALSE(fifo.IsStreamReady(3)); - fifo.RegisterStream(9, precedence); - fifo.RegisterStream(7, precedence); - fifo.RegisterStream(11, precedence); - fifo.RegisterStream(13, precedence); - fifo.RegisterStream(15, precedence); - fifo.RegisterStream(17, precedence); - EXPECT_EQ(7u, fifo.NumRegisteredStreams()); - EXPECT_FALSE(fifo.HasReadyStreams()); - - fifo.MarkStreamReady(9, true); - EXPECT_TRUE(fifo.IsStreamReady(9)); - EXPECT_TRUE(fifo.HasReadyStreams()); - fifo.MarkStreamReady(15, true); - fifo.MarkStreamReady(7, true); - fifo.MarkStreamReady(13, true); - fifo.MarkStreamReady(11, true); - fifo.MarkStreamReady(3, true); - fifo.MarkStreamReady(17, true); - EXPECT_EQ(7u, fifo.NumReadyStreams()); - - EXPECT_EQ(3u, fifo.PopNextReadyStream()); - EXPECT_EQ(7u, std::get<0>(fifo.PopNextReadyStreamAndPrecedence())); - EXPECT_EQ(5u, fifo.NumReadyStreams()); - - EXPECT_FALSE(fifo.ShouldYield(3)); - EXPECT_FALSE(fifo.ShouldYield(9)); - EXPECT_TRUE(fifo.ShouldYield(13)); - EXPECT_TRUE(fifo.ShouldYield(10)); - - fifo.MarkStreamNotReady(9); - EXPECT_EQ(4u, fifo.NumReadyStreams()); - EXPECT_FALSE(fifo.ShouldYield(10)); - EXPECT_TRUE(fifo.ShouldYield(12)); -} - -TEST(FifoWriteSchedulerTest, GetLatestEventTest) { - FifoWriteScheduler<SpdyStreamId> fifo; - - SpdyStreamPrecedence precedence(1); - fifo.RegisterStream(1, precedence); - fifo.RegisterStream(3, precedence); - fifo.RegisterStream(5, precedence); - fifo.RegisterStream(7, precedence); - fifo.RegisterStream(9, precedence); - fifo.RecordStreamEventTime(1, 3); - fifo.RecordStreamEventTime(3, 2); - fifo.RecordStreamEventTime(5, 4); - fifo.RecordStreamEventTime(7, 8); - fifo.RecordStreamEventTime(9, 1); - - EXPECT_EQ(8, fifo.GetLatestEventWithPrecedence(9)); - EXPECT_EQ(4, fifo.GetLatestEventWithPrecedence(7)); - EXPECT_EQ(3, fifo.GetLatestEventWithPrecedence(5)); - EXPECT_EQ(3, fifo.GetLatestEventWithPrecedence(3)); - EXPECT_EQ(0, fifo.GetLatestEventWithPrecedence(1)); -} - -TEST(FifoWriteSchedulerTest, GetStreamPrecedence) { - FifoWriteScheduler<SpdyStreamId> fifo; - // Return lowest priority for unknown stream. - EXPECT_EQ(kV3LowestPriority, fifo.GetStreamPrecedence(1).spdy3_priority()); - - fifo.RegisterStream(1, SpdyStreamPrecedence(3)); - EXPECT_TRUE(fifo.GetStreamPrecedence(1).is_spdy3_priority()); - EXPECT_EQ(3, fifo.GetStreamPrecedence(1).spdy3_priority()); - - // Redundant registration shouldn't change stream priority. - EXPECT_SPDY_BUG(fifo.RegisterStream(1, SpdyStreamPrecedence(4)), - "Stream 1 already registered"); - EXPECT_EQ(3, fifo.GetStreamPrecedence(1).spdy3_priority()); - - fifo.UpdateStreamPrecedence(1, SpdyStreamPrecedence(5)); - EXPECT_EQ(5, fifo.GetStreamPrecedence(1).spdy3_priority()); -} - -} // namespace test - -} // namespace spdy diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.cc index 59ba669b6a9..4d03cc50018 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 @@ -11,7 +11,6 @@ #include "spdy/platform/api/spdy_logging.h" using ::http2::DecodeBuffer; -using ::http2::HpackString; namespace spdy { namespace { @@ -108,18 +107,6 @@ const SpdyHeaderBlock& HpackDecoderAdapter::decoded_block() const { return listener_adapter_.decoded_block(); } -void HpackDecoderAdapter::SetHeaderTableDebugVisitor( - std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor) { - SPDY_DVLOG(2) << "HpackDecoderAdapter::SetHeaderTableDebugVisitor"; - if (visitor != nullptr) { - listener_adapter_.SetHeaderTableDebugVisitor(std::move(visitor)); - hpack_decoder_.set_tables_debug_listener(&listener_adapter_); - } else { - hpack_decoder_.set_tables_debug_listener(nullptr); - listener_adapter_.SetHeaderTableDebugVisitor(nullptr); - } -} - void HpackDecoderAdapter::set_max_decode_buffer_size_bytes( size_t max_decode_buffer_size_bytes) { SPDY_DVLOG(2) << "HpackDecoderAdapter::set_max_decode_buffer_size_bytes"; @@ -144,11 +131,6 @@ void HpackDecoderAdapter::ListenerAdapter::set_handler( handler_ = handler; } -void HpackDecoderAdapter::ListenerAdapter::SetHeaderTableDebugVisitor( - std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor) { - visitor_ = std::move(visitor); -} - void HpackDecoderAdapter::ListenerAdapter::OnHeaderListStart() { SPDY_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnHeaderListStart"; total_hpack_bytes_ = 0; @@ -159,18 +141,17 @@ void HpackDecoderAdapter::ListenerAdapter::OnHeaderListStart() { } } -void HpackDecoderAdapter::ListenerAdapter::OnHeader(const HpackString& name, - const HpackString& value) { +void HpackDecoderAdapter::ListenerAdapter::OnHeader(const std::string& name, + const std::string& value) { SPDY_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnHeader:\n name: " << name << "\n value: " << value; total_uncompressed_bytes_ += name.size() + value.size(); if (handler_ == nullptr) { SPDY_DVLOG(3) << "Adding to decoded_block"; - decoded_block_.AppendValueOrAddHeader(name.ToStringPiece(), - value.ToStringPiece()); + decoded_block_.AppendValueOrAddHeader(name, value); } else { SPDY_DVLOG(3) << "Passing to handler"; - handler_->OnHeader(name.ToStringPiece(), value.ToStringPiece()); + handler_->OnHeader(name, value); } } @@ -189,38 +170,4 @@ void HpackDecoderAdapter::ListenerAdapter::OnHeaderErrorDetected( SPDY_VLOG(1) << error_message; } -int64_t HpackDecoderAdapter::ListenerAdapter::OnEntryInserted( - const http2::HpackStringPair& entry, - size_t insert_count) { - SPDY_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnEntryInserted: " - << entry << ", insert_count=" << insert_count; - if (visitor_ == nullptr) { - return 0; - } - HpackEntry hpack_entry(entry.name.ToStringPiece(), - entry.value.ToStringPiece(), - /*is_static*/ false, insert_count); - int64_t time_added = visitor_->OnNewEntry(hpack_entry); - SPDY_DVLOG(2) - << "HpackDecoderAdapter::ListenerAdapter::OnEntryInserted: time_added=" - << time_added; - return time_added; -} - -void HpackDecoderAdapter::ListenerAdapter::OnUseEntry( - const http2::HpackStringPair& entry, - size_t insert_count, - int64_t time_added) { - SPDY_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnUseEntry: " << entry - << ", insert_count=" << insert_count - << ", time_added=" << time_added; - if (visitor_ != nullptr) { - HpackEntry hpack_entry(entry.name.ToStringPiece(), - entry.value.ToStringPiece(), /*is_static*/ false, - insert_count); - hpack_entry.set_time_added(time_added); - visitor_->OnUseEntry(hpack_entry); - } -} - } // namespace spdy 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 5ec7046d2c2..98d10fe2c9a 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 @@ -17,7 +17,6 @@ #include "http2/hpack/decoder/hpack_decoder.h" #include "http2/hpack/decoder/hpack_decoder_listener.h" #include "http2/hpack/decoder/hpack_decoder_tables.h" -#include "http2/hpack/hpack_string.h" #include "http2/hpack/http2_hpack_constants.h" #include "common/platform/api/quiche_export.h" #include "spdy/core/hpack/hpack_header_table.h" @@ -68,9 +67,6 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderAdapter { // a SpdyHeadersHandlerInterface. const SpdyHeaderBlock& decoded_block() const; - void SetHeaderTableDebugVisitor( - std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor); - // Set how much encoded data this decoder is willing to buffer. // TODO(jamessynge): Resolve definition of this value, as it is currently // too tied to a single implementation. We probably want to limit one or more @@ -92,8 +88,7 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderAdapter { private: class QUICHE_EXPORT_PRIVATE ListenerAdapter - : public http2::HpackDecoderListener, - public http2::HpackDecoderTablesDebugListener { + : public http2::HpackDecoderListener { public: ListenerAdapter(); ~ListenerAdapter() override; @@ -105,23 +100,12 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderAdapter { void set_handler(SpdyHeadersHandlerInterface* handler); const SpdyHeaderBlock& decoded_block() const { return decoded_block_; } - void SetHeaderTableDebugVisitor( - std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor); - // Override the HpackDecoderListener methods: void OnHeaderListStart() override; - void OnHeader(const http2::HpackString& name, - const http2::HpackString& value) override; + void OnHeader(const std::string& name, const std::string& value) override; void OnHeaderListEnd() override; void OnHeaderErrorDetected(absl::string_view error_message) override; - // Override the HpackDecoderTablesDebugListener methods: - int64_t OnEntryInserted(const http2::HpackStringPair& entry, - size_t insert_count) override; - void OnUseEntry(const http2::HpackStringPair& entry, - size_t insert_count, - int64_t time_added) override; - void AddToTotalHpackBytes(size_t delta) { total_hpack_bytes_ += delta; } size_t total_hpack_bytes() const { return total_hpack_bytes_; } @@ -139,10 +123,6 @@ class QUICHE_EXPORT_PRIVATE HpackDecoderAdapter { // Total bytes of the name and value strings in the current HPACK block. size_t total_uncompressed_bytes_; - - // visitor_ is used by a QUIC experiment regarding HPACK; remove - // when the experiment is done. - std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor_; }; // Converts calls to HpackDecoderListener into calls to 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 c768755dbb7..0a12fe49a21 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 @@ -28,7 +28,6 @@ #include "spdy/platform/api/spdy_string_utils.h" using ::http2::HpackEntryType; -using ::http2::HpackString; using ::http2::HpackStringPair; using ::http2::test::HpackBlockBuilder; using ::http2::test::HpackDecoderPeer; @@ -66,9 +65,9 @@ class HpackDecoderAdapterPeer { explicit HpackDecoderAdapterPeer(HpackDecoderAdapter* decoder) : decoder_(decoder) {} - void HandleHeaderRepresentation(absl::string_view name, - absl::string_view value) { - decoder_->listener_adapter_.OnHeader(HpackString(name), HpackString(value)); + void HandleHeaderRepresentation(const std::string& name, + const std::string& value) { + decoder_->listener_adapter_.OnHeader(name, value); } http2::HpackDecoderTables* GetDecoderTables() { 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 7e924afea43..6dc27b9ca7f 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 @@ -137,10 +137,10 @@ void HpackEncoder::EncodeRepresentations(RepresentationIterator* iter, const auto header = iter->Next(); listener_(header.first, header.second); if (enable_compression_) { - const HpackEntry* entry = + size_t index = header_table_.GetByNameAndValue(header.first, header.second); - if (entry != nullptr) { - EmitIndex(entry); + if (index != kHpackEntryNotFound) { + EmitIndex(index); } else if (should_index_(header.first, header.second)) { EmitIndexedLiteral(header); } else { @@ -154,10 +154,10 @@ void HpackEncoder::EncodeRepresentations(RepresentationIterator* iter, output_stream_.TakeString(output); } -void HpackEncoder::EmitIndex(const HpackEntry* entry) { - SPDY_DVLOG(2) << "Emitting index " << header_table_.IndexOf(entry); +void HpackEncoder::EmitIndex(size_t index) { + SPDY_DVLOG(2) << "Emitting index " << index; output_stream_.AppendPrefix(kIndexedOpcode); - output_stream_.AppendUint32(header_table_.IndexOf(entry)); + output_stream_.AppendUint32(index); } void HpackEncoder::EmitIndexedLiteral(const Representation& representation) { @@ -173,9 +173,9 @@ void HpackEncoder::EmitNonIndexedLiteral(const Representation& representation, SPDY_DVLOG(2) << "Emitting nonindexed literal: (" << representation.first << ", " << representation.second << ")"; output_stream_.AppendPrefix(kLiteralNoIndexOpcode); - const HpackEntry* name_entry = header_table_.GetByName(representation.first); - if (enable_compression && name_entry != nullptr) { - output_stream_.AppendUint32(header_table_.IndexOf(name_entry)); + size_t name_index = header_table_.GetByName(representation.first); + if (enable_compression && name_index != kHpackEntryNotFound) { + output_stream_.AppendUint32(name_index); } else { output_stream_.AppendUint32(0); EmitString(representation.first); @@ -184,9 +184,9 @@ void HpackEncoder::EmitNonIndexedLiteral(const Representation& representation, } void HpackEncoder::EmitLiteral(const Representation& representation) { - const HpackEntry* name_entry = header_table_.GetByName(representation.first); - if (name_entry != nullptr) { - output_stream_.AppendUint32(header_table_.IndexOf(name_entry)); + size_t name_index = header_table_.GetByName(representation.first); + if (name_index != kHpackEntryNotFound) { + output_stream_.AppendUint32(name_index); } else { output_stream_.AppendUint32(0); EmitString(representation.first); @@ -347,7 +347,7 @@ HpackEncoder::Encoderator::Encoderator(const Representations& representations, void HpackEncoder::Encoderator::Next(size_t max_encoded_bytes, std::string* output) { - SPDY_BUG_IF(!has_next_) + SPDY_BUG_IF(spdy_bug_61_1, !has_next_) << "Encoderator::Next called with nothing left to encode."; const bool enable_compression = encoder_->enable_compression_; @@ -357,10 +357,10 @@ void HpackEncoder::Encoderator::Next(size_t max_encoded_bytes, const Representation header = header_it_->Next(); encoder_->listener_(header.first, header.second); if (enable_compression) { - const HpackEntry* entry = encoder_->header_table_.GetByNameAndValue( - header.first, header.second); - if (entry != nullptr) { - encoder_->EmitIndex(entry); + size_t index = encoder_->header_table_.GetByNameAndValue(header.first, + header.second); + if (index != kHpackEntryNotFound) { + encoder_->EmitIndex(index); } else if (encoder_->should_index_(header.first, header.second)) { encoder_->EmitIndexedLiteral(header); } else { 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 b1b7741deba..d85b98ede0b 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 @@ -93,11 +93,6 @@ class QUICHE_EXPORT_PRIVATE HpackEncoder { // this encoder. void SetHeaderListener(HeaderListener listener) { listener_ = listener; } - void SetHeaderTableDebugVisitor( - std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor) { - header_table_.set_debug_visitor(std::move(visitor)); - } - void DisableCompression() { enable_compression_ = false; } // Returns the estimate of dynamically allocated memory in bytes. @@ -113,7 +108,7 @@ class QUICHE_EXPORT_PRIVATE HpackEncoder { void EncodeRepresentations(RepresentationIterator* iter, std::string* output); // Emits a static/dynamic indexed representation (Section 7.1). - void EmitIndex(const HpackEntry* entry); + void EmitIndex(size_t index); // Emits a literal representation (Section 7.2). void EmitIndexedLiteral(const Representation& representation); 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 0257aa6afec..85fa4fbc321 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 @@ -10,6 +10,7 @@ #include "http2/hpack/huffman/hpack_huffman_encoder.h" #include "http2/test_tools/http2_random.h" #include "common/platform/api/quiche_test.h" +#include "spdy/core/hpack/hpack_static_table.h" #include "spdy/core/spdy_simple_arena.h" #include "spdy/platform/api/spdy_flags.h" @@ -21,7 +22,11 @@ class HpackHeaderTablePeer { public: explicit HpackHeaderTablePeer(HpackHeaderTable* table) : table_(table) {} - HpackHeaderTable::EntryTable* dynamic_entries() { + const HpackEntry* GetFirstStaticEntry() const { + return &table_->static_entries_.front(); + } + + HpackHeaderTable::DynamicEntryTable* dynamic_entries() { return &table_->dynamic_entries_; } @@ -118,6 +123,8 @@ namespace { using testing::ElementsAre; using testing::Pair; +const size_t kStaticEntryIndex = 1; + enum EncodeStrategy { kDefault, kIncremental, @@ -130,7 +137,8 @@ class HpackEncoderTest : public QuicheTestWithParam<EncodeStrategy> { HpackEncoderTest() : peer_(&encoder_), - static_(peer_.table()->GetByIndex(1)), + static_(peer_.table_peer().GetFirstStaticEntry()), + dynamic_table_insertions_(0), headers_storage_(1024 /* block size */), strategy_(GetParam()) {} @@ -138,9 +146,13 @@ class HpackEncoderTest : public QuicheTestWithParam<EncodeStrategy> { // Populate dynamic entries into the table fixture. For simplicity each // entry has name.size() + value.size() == 10. key_1_ = peer_.table()->TryAddEntry("key1", "value1"); + key_1_index_ = dynamic_table_insertions_++; key_2_ = peer_.table()->TryAddEntry("key2", "value2"); + key_2_index_ = dynamic_table_insertions_++; cookie_a_ = peer_.table()->TryAddEntry("cookie", "a=bb"); + cookie_a_index_ = dynamic_table_insertions_++; cookie_c_ = peer_.table()->TryAddEntry("cookie", "c=dd"); + cookie_c_index_ = dynamic_table_insertions_++; // No further insertions may occur without evictions. peer_.table()->SetMaxSize(peer_.table()->size()); @@ -158,10 +170,9 @@ class HpackEncoderTest : public QuicheTestWithParam<EncodeStrategy> { expected_.AppendPrefix(kIndexedOpcode); expected_.AppendUint32(index); } - void ExpectIndexedLiteral(const HpackEntry* key_entry, - absl::string_view value) { + void ExpectIndexedLiteral(size_t key_index, absl::string_view value) { expected_.AppendPrefix(kLiteralIncrementalIndexOpcode); - expected_.AppendUint32(IndexOf(key_entry)); + expected_.AppendUint32(key_index); ExpectString(&expected_, value); } void ExpectIndexedLiteral(absl::string_view name, absl::string_view value) { @@ -177,10 +188,10 @@ class HpackEncoderTest : public QuicheTestWithParam<EncodeStrategy> { ExpectString(&expected_, name); ExpectString(&expected_, value); } - void ExpectNonIndexedLiteralWithNameIndex(const HpackEntry* key_entry, + void ExpectNonIndexedLiteralWithNameIndex(size_t key_index, absl::string_view value) { expected_.AppendPrefix(kLiteralNoIndexOpcode); - expected_.AppendUint32(IndexOf(key_entry)); + expected_.AppendUint32(key_index); ExpectString(&expected_, value); } void ExpectString(HpackOutputStream* stream, absl::string_view str) { @@ -233,8 +244,12 @@ class HpackEncoderTest : public QuicheTestWithParam<EncodeStrategy> { &encoder_, representations, &actual_out)); EXPECT_EQ(expected_out, actual_out); } - size_t IndexOf(const HpackEntry* entry) { - return peer_.table()->IndexOf(entry); + // Converts the index of a dynamic table entry to the HPACK index. + // In these test, dynamic table entries are indexed sequentially, starting + // with 0. The HPACK indexing scheme is defined at + // https://httpwg.org/specs/rfc7541.html#index.address.space. + size_t DynamicIndexToWireIndex(size_t index) { + return dynamic_table_insertions_ - index + kStaticTableSize; } HpackEncoder encoder_; @@ -245,6 +260,11 @@ class HpackEncoderTest : public QuicheTestWithParam<EncodeStrategy> { const HpackEntry* key_2_; const HpackEntry* cookie_a_; const HpackEntry* cookie_c_; + size_t key_1_index_; + size_t key_2_index_; + size_t cookie_a_index_; + size_t cookie_c_index_; + size_t dynamic_table_insertions_; SpdySimpleArena headers_storage_; std::vector<std::pair<absl::string_view, absl::string_view>> @@ -303,7 +323,7 @@ TEST_P(HpackEncoderTest, SingleDynamicIndex) { this->SaveHeaders(name, value); }); - ExpectIndex(IndexOf(key_2_)); + ExpectIndex(DynamicIndexToWireIndex(key_2_index_)); SpdyHeaderBlock headers; headers[key_2_->name()] = key_2_->value(); @@ -313,7 +333,7 @@ TEST_P(HpackEncoderTest, SingleDynamicIndex) { } TEST_P(HpackEncoderTest, SingleStaticIndex) { - ExpectIndex(IndexOf(static_)); + ExpectIndex(kStaticEntryIndex); SpdyHeaderBlock headers; headers[static_->name()] = static_->value(); @@ -322,7 +342,7 @@ TEST_P(HpackEncoderTest, SingleStaticIndex) { TEST_P(HpackEncoderTest, SingleStaticIndexTooLarge) { peer_.table()->SetMaxSize(1); // Also evicts all fixtures. - ExpectIndex(IndexOf(static_)); + ExpectIndex(kStaticEntryIndex); SpdyHeaderBlock headers; headers[static_->name()] = static_->value(); @@ -332,7 +352,7 @@ TEST_P(HpackEncoderTest, SingleStaticIndexTooLarge) { } TEST_P(HpackEncoderTest, SingleLiteralWithIndexName) { - ExpectIndexedLiteral(key_2_, "value3"); + ExpectIndexedLiteral(DynamicIndexToWireIndex(key_2_index_), "value3"); SpdyHeaderBlock headers; headers[key_2_->name()] = "value3"; @@ -373,7 +393,7 @@ TEST_P(HpackEncoderTest, SingleLiteralTooLarge) { TEST_P(HpackEncoderTest, EmitThanEvict) { // |key_1_| is toggled and placed into the reference set, // and then immediately evicted by "key3". - ExpectIndex(IndexOf(key_1_)); + ExpectIndex(DynamicIndexToWireIndex(key_1_index_)); ExpectIndexedLiteral("key3", "value3"); SpdyHeaderBlock headers; @@ -383,8 +403,8 @@ TEST_P(HpackEncoderTest, EmitThanEvict) { } TEST_P(HpackEncoderTest, CookieHeaderIsCrumbled) { - ExpectIndex(IndexOf(cookie_a_)); - ExpectIndex(IndexOf(cookie_c_)); + ExpectIndex(DynamicIndexToWireIndex(cookie_a_index_)); + ExpectIndex(DynamicIndexToWireIndex(cookie_c_index_)); ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "e=ff"); SpdyHeaderBlock headers; @@ -474,8 +494,8 @@ TEST_P(HpackEncoderTest, MultipleEncodingPasses) { headers["key1"] = "value1"; headers["cookie"] = "a=bb"; - ExpectIndex(IndexOf(key_1_)); - ExpectIndex(IndexOf(cookie_a_)); + ExpectIndex(DynamicIndexToWireIndex(key_1_index_)); + ExpectIndex(DynamicIndexToWireIndex(cookie_a_index_)); CompareWithExpectedEncoding(headers); } // Header table is: @@ -490,11 +510,12 @@ TEST_P(HpackEncoderTest, MultipleEncodingPasses) { headers["cookie"] = "c=dd; e=ff"; // "key2: value2" - ExpectIndex(64); + ExpectIndex(DynamicIndexToWireIndex(key_2_index_)); // "cookie: c=dd" - ExpectIndex(62); + ExpectIndex(DynamicIndexToWireIndex(cookie_c_index_)); // This cookie evicts |key1| from the dynamic table. ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "e=ff"); + dynamic_table_insertions_++; CompareWithExpectedEncoding(headers); } @@ -510,13 +531,16 @@ TEST_P(HpackEncoderTest, MultipleEncodingPasses) { headers["cookie"] = "a=bb; b=cc; c=dd"; // "key2: value2" - ExpectIndex(65); + EXPECT_EQ(65u, DynamicIndexToWireIndex(key_2_index_)); + ExpectIndex(DynamicIndexToWireIndex(key_2_index_)); // "cookie: a=bb" - ExpectIndex(64); + EXPECT_EQ(64u, DynamicIndexToWireIndex(cookie_a_index_)); + ExpectIndex(DynamicIndexToWireIndex(cookie_a_index_)); // This cookie evicts |key2| from the dynamic table. ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "b=cc"); + dynamic_table_insertions_++; // "cookie: c=dd" - ExpectIndex(64); + ExpectIndex(DynamicIndexToWireIndex(cookie_c_index_)); CompareWithExpectedEncoding(headers); } diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.cc index e8db1d779da..b8350df4ff4 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.cc @@ -10,76 +10,19 @@ namespace spdy { -const size_t HpackEntry::kSizeOverhead = 32; - -HpackEntry::HpackEntry(absl::string_view name, - absl::string_view value, - bool is_static, - size_t insertion_index) - : name_(name.data(), name.size()), - value_(value.data(), value.size()), - name_ref_(name_), - value_ref_(value_), - insertion_index_(insertion_index), - type_(is_static ? STATIC : DYNAMIC), - time_added_(0) {} - -HpackEntry::HpackEntry(absl::string_view name, absl::string_view value) - : name_ref_(name), - value_ref_(value), - insertion_index_(0), - type_(LOOKUP), - time_added_(0) {} - -HpackEntry::HpackEntry() : insertion_index_(0), type_(LOOKUP), time_added_(0) {} - -HpackEntry::HpackEntry(const HpackEntry& other) - : insertion_index_(other.insertion_index_), - type_(other.type_), - time_added_(0) { - if (type_ == LOOKUP) { - name_ref_ = other.name_ref_; - value_ref_ = other.value_ref_; - } else { - name_ = other.name_; - value_ = other.value_; - name_ref_ = absl::string_view(name_.data(), name_.size()); - value_ref_ = absl::string_view(value_.data(), value_.size()); - } -} - -HpackEntry& HpackEntry::operator=(const HpackEntry& other) { - insertion_index_ = other.insertion_index_; - type_ = other.type_; - if (type_ == LOOKUP) { - name_.clear(); - value_.clear(); - name_ref_ = other.name_ref_; - value_ref_ = other.value_ref_; - return *this; - } - name_ = other.name_; - value_ = other.value_; - name_ref_ = absl::string_view(name_.data(), name_.size()); - value_ref_ = absl::string_view(value_.data(), value_.size()); - return *this; -} - -HpackEntry::~HpackEntry() = default; +HpackEntry::HpackEntry(std::string name, std::string value) + : name_(std::move(name)), value_(std::move(value)) {} // static size_t HpackEntry::Size(absl::string_view name, absl::string_view value) { - return name.size() + value.size() + kSizeOverhead; + return name.size() + value.size() + kHpackEntrySizeOverhead; } size_t HpackEntry::Size() const { return Size(name(), value()); } std::string HpackEntry::GetDebugString() const { - return absl::StrCat( - "{ name: \"", name_ref_, "\", value: \"", value_ref_, - "\", index: ", insertion_index_, " ", - (IsStatic() ? " static" : (IsLookup() ? " lookup" : " dynamic")), " }"); + return absl::StrCat("{ name: \"", name_, "\", value: \"", value_, "\" }"); } size_t HpackEntry::EstimateMemoryUsage() const { diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h index 6141a421075..0c263584f66 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h @@ -8,6 +8,7 @@ #include <cstddef> #include <cstdint> #include <string> +#include <utility> #include "absl/strings/string_view.h" #include "common/platform/api/quiche_export.h" @@ -17,54 +18,52 @@ namespace spdy { +// The constant amount added to name().size() and value().size() to +// get the size of an HpackEntry as defined in 5.1. +constexpr size_t kHpackEntrySizeOverhead = 32; + +// A structure for looking up entries in the static and dynamic tables. +struct QUICHE_EXPORT_PRIVATE HpackLookupEntry { + absl::string_view name; + absl::string_view value; + + bool operator==(const HpackLookupEntry& other) const { + return name == other.name && value == other.value; + } + + // Abseil hashing framework extension according to absl/hash/hash.h: + template <typename H> + friend H AbslHashValue(H h, const HpackLookupEntry& entry) { + return H::combine(std::move(h), entry.name, entry.value); + } +}; + // A structure for an entry in the static table (3.3.1) // and the header table (3.3.2). class QUICHE_EXPORT_PRIVATE HpackEntry { public: - // The constant amount added to name().size() and value().size() to - // get the size of an HpackEntry as defined in 5.1. - static const size_t kSizeOverhead; - - // Creates an entry. Preconditions: - // - |is_static| captures whether this entry is a member of the static - // or dynamic header table. - // - |insertion_index| is this entry's index in the total set of entries ever - // inserted into the header table (including static entries). - // - // The combination of |is_static| and |insertion_index| allows an - // HpackEntryTable to determine the index of an HpackEntry in O(1) time. - // Copies |name| and |value|. - HpackEntry(absl::string_view name, - absl::string_view value, - bool is_static, - size_t insertion_index); - - // Create a 'lookup' entry (only) suitable for querying a HpackEntrySet. The - // instance InsertionIndex() always returns 0 and IsLookup() returns true. - // The memory backing |name| and |value| must outlive this object. - HpackEntry(absl::string_view name, absl::string_view value); - - HpackEntry(const HpackEntry& other); - HpackEntry& operator=(const HpackEntry& other); - - // Creates an entry with empty name and value. Only defined so that - // entries can be stored in STL containers. - HpackEntry(); - - ~HpackEntry(); - - absl::string_view name() const { return name_ref_; } - absl::string_view value() const { return value_ref_; } - - // Returns whether this entry is a member of the static (as opposed to - // dynamic) table. - bool IsStatic() const { return type_ == STATIC; } - - // Returns whether this entry is a lookup-only entry. - bool IsLookup() const { return type_ == LOOKUP; } - - // Used to compute the entry's index in the header table. - size_t InsertionIndex() const { return insertion_index_; } + HpackEntry(std::string name, std::string value); + + // Make HpackEntry non-copyable to make sure it is always moved. + HpackEntry(const HpackEntry&) = delete; + HpackEntry& operator=(const HpackEntry&) = delete; + + HpackEntry(HpackEntry&&) = default; + HpackEntry& operator=(HpackEntry&&) = default; + + // Getters for std::string members traditionally return const std::string&. + // However, HpackHeaderTable uses string_view as keys in the maps + // static_name_index_ and dynamic_name_index_. If HpackEntry::name() returned + // const std::string&, then + // dynamic_name_index_.insert(std::make_pair(entry.name(), index)); + // would silently create a dangling reference: make_pair infers type from the + // return type of entry.name() and silently creates a temporary string copy. + // Insert creates a string_view that points to this copy, which then + // immediately goes out of scope and gets destroyed. While this is quite easy + // to avoid, for example, by explicitly specifying type as a template + // parameter to make_pair, returning string_view here is less error-prone. + absl::string_view name() const { return name_; } + absl::string_view value() const { return value_; } // Returns the size of an entry as defined in 5.1. static size_t Size(absl::string_view name, absl::string_view value); @@ -72,36 +71,12 @@ class QUICHE_EXPORT_PRIVATE HpackEntry { std::string GetDebugString() const; - int64_t time_added() const { return time_added_; } - void set_time_added(int64_t now) { time_added_ = now; } - // Returns the estimate of dynamically allocated memory in bytes. size_t EstimateMemoryUsage() const; private: - enum EntryType { - LOOKUP, - DYNAMIC, - STATIC, - }; - - // These members are not used for LOOKUP entries. std::string name_; std::string value_; - - // These members are always valid. For DYNAMIC and STATIC entries, they - // always point to |name_| and |value_|. - absl::string_view name_ref_; - absl::string_view value_ref_; - - // The entry's index in the total set of entries ever inserted into the header - // table. - size_t insertion_index_; - - EntryType type_; - - // For HpackHeaderTable::DebugVisitorInterface - int64_t time_added_; }; } // namespace spdy diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry_test.cc index ced22a533b2..81acd9a4b30 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry_test.cc @@ -4,117 +4,48 @@ #include "spdy/core/hpack/hpack_entry.h" +#include "absl/hash/hash.h" #include "common/platform/api/quiche_test.h" namespace spdy { namespace { -class HpackEntryTest : public QuicheTest { - protected: - HpackEntryTest() - : name_("header-name"), - value_("header value"), - total_insertions_(0), - table_size_(0) {} +TEST(HpackLookupEntryTest, EntryNamesDiffer) { + HpackLookupEntry entry1{"header", "value"}; + HpackLookupEntry entry2{"HEADER", "value"}; - // These builders maintain the same external table invariants that a "real" - // table (ie HpackHeaderTable) would. - HpackEntry StaticEntry() { - return HpackEntry(name_, value_, true, total_insertions_++); - } - HpackEntry DynamicEntry() { - ++table_size_; - size_t index = total_insertions_++; - return HpackEntry(name_, value_, false, index); - } - void DropEntry() { --table_size_; } - - size_t IndexOf(const HpackEntry& entry) const { - if (entry.IsStatic()) { - return 1 + entry.InsertionIndex() + table_size_; - } else { - return total_insertions_ - entry.InsertionIndex(); - } - } - - size_t Size() { - return name_.size() + value_.size() + HpackEntry::kSizeOverhead; - } - - std::string name_, value_; - - private: - // Referenced by HpackEntry instances. - size_t total_insertions_; - size_t table_size_; -}; - -TEST_F(HpackEntryTest, StaticConstructor) { - HpackEntry entry(StaticEntry()); - - EXPECT_EQ(name_, entry.name()); - EXPECT_EQ(value_, entry.value()); - EXPECT_TRUE(entry.IsStatic()); - EXPECT_EQ(1u, IndexOf(entry)); - EXPECT_EQ(Size(), entry.Size()); -} - -TEST_F(HpackEntryTest, DynamicConstructor) { - HpackEntry entry(DynamicEntry()); - - EXPECT_EQ(name_, entry.name()); - EXPECT_EQ(value_, entry.value()); - EXPECT_FALSE(entry.IsStatic()); - EXPECT_EQ(1u, IndexOf(entry)); - EXPECT_EQ(Size(), entry.Size()); + EXPECT_FALSE(entry1 == entry2); + EXPECT_NE(absl::Hash<HpackLookupEntry>()(entry1), + absl::Hash<HpackLookupEntry>()(entry2)); } -TEST_F(HpackEntryTest, LookupConstructor) { - HpackEntry entry(name_, value_); +TEST(HpackLookupEntryTest, EntryValuesDiffer) { + HpackLookupEntry entry1{"header", "value"}; + HpackLookupEntry entry2{"header", "VALUE"}; - EXPECT_EQ(name_, entry.name()); - EXPECT_EQ(value_, entry.value()); - EXPECT_FALSE(entry.IsStatic()); - EXPECT_EQ(0u, IndexOf(entry)); - EXPECT_EQ(Size(), entry.Size()); + EXPECT_FALSE(entry1 == entry2); + EXPECT_NE(absl::Hash<HpackLookupEntry>()(entry1), + absl::Hash<HpackLookupEntry>()(entry2)); } -TEST_F(HpackEntryTest, DefaultConstructor) { - HpackEntry entry; +TEST(HpackLookupEntryTest, EntriesEqual) { + HpackLookupEntry entry1{"name", "value"}; + HpackLookupEntry entry2{"name", "value"}; - EXPECT_TRUE(entry.name().empty()); - EXPECT_TRUE(entry.value().empty()); - EXPECT_EQ(HpackEntry::kSizeOverhead, entry.Size()); + EXPECT_TRUE(entry1 == entry2); + EXPECT_EQ(absl::Hash<HpackLookupEntry>()(entry1), + absl::Hash<HpackLookupEntry>()(entry2)); } -TEST_F(HpackEntryTest, IndexUpdate) { - HpackEntry static1(StaticEntry()); - HpackEntry static2(StaticEntry()); - - EXPECT_EQ(1u, IndexOf(static1)); - EXPECT_EQ(2u, IndexOf(static2)); - - HpackEntry dynamic1(DynamicEntry()); - HpackEntry dynamic2(DynamicEntry()); - - EXPECT_EQ(1u, IndexOf(dynamic2)); - EXPECT_EQ(2u, IndexOf(dynamic1)); - EXPECT_EQ(3u, IndexOf(static1)); - EXPECT_EQ(4u, IndexOf(static2)); - - DropEntry(); // Drops |dynamic1|. - - EXPECT_EQ(1u, IndexOf(dynamic2)); - EXPECT_EQ(2u, IndexOf(static1)); - EXPECT_EQ(3u, IndexOf(static2)); +TEST(HpackEntryTest, BasicEntry) { + HpackEntry entry("header-name", "header value"); - HpackEntry dynamic3(DynamicEntry()); + EXPECT_EQ("header-name", entry.name()); + EXPECT_EQ("header value", entry.value()); - EXPECT_EQ(1u, IndexOf(dynamic3)); - EXPECT_EQ(2u, IndexOf(dynamic2)); - EXPECT_EQ(3u, IndexOf(static1)); - EXPECT_EQ(4u, IndexOf(static2)); + EXPECT_EQ(55u, entry.Size()); + EXPECT_EQ(55u, HpackEntry::Size("header-name", "header value")); } } // namespace diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.cc index 4dac492d034..ec69d47b6ae 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.cc @@ -6,31 +6,13 @@ #include <algorithm> -#include "common/platform/api/quiche_string_piece.h" #include "spdy/core/hpack/hpack_constants.h" #include "spdy/core/hpack/hpack_static_table.h" -#include "spdy/platform/api/spdy_containers.h" #include "spdy/platform/api/spdy_estimate_memory_usage.h" #include "spdy/platform/api/spdy_logging.h" namespace spdy { -size_t HpackHeaderTable::EntryHasher::operator()( - const HpackEntry* entry) const { - return quiche::QuicheHashStringPair(entry->name(), entry->value()); -} - -bool HpackHeaderTable::EntriesEq::operator()(const HpackEntry* lhs, - const HpackEntry* rhs) const { - if (lhs == nullptr) { - return rhs == nullptr; - } - if (rhs == nullptr) { - return false; - } - return lhs->name() == rhs->name() && lhs->value() == rhs->value(); -} - HpackHeaderTable::HpackHeaderTable() : static_entries_(ObtainHpackStaticTable().GetStaticEntries()), static_index_(ObtainHpackStaticTable().GetStaticIndex()), @@ -38,79 +20,42 @@ HpackHeaderTable::HpackHeaderTable() settings_size_bound_(kDefaultHeaderTableSizeSetting), size_(0), max_size_(kDefaultHeaderTableSizeSetting), - total_insertions_(static_entries_.size()) {} + dynamic_table_insertions_(0) {} HpackHeaderTable::~HpackHeaderTable() = default; -const HpackEntry* HpackHeaderTable::GetByIndex(size_t index) { - if (index == 0) { - return nullptr; - } - index -= 1; - if (index < static_entries_.size()) { - return &static_entries_[index]; - } - index -= static_entries_.size(); - if (index < dynamic_entries_.size()) { - const HpackEntry* result = &dynamic_entries_[index]; - if (debug_visitor_ != nullptr) { - debug_visitor_->OnUseEntry(*result); - } - return result; - } - return nullptr; -} - -const HpackEntry* HpackHeaderTable::GetByName(absl::string_view name) { +size_t HpackHeaderTable::GetByName(absl::string_view name) { { auto it = static_name_index_.find(name); if (it != static_name_index_.end()) { - return it->second; + return 1 + it->second; } } { NameToEntryMap::const_iterator it = dynamic_name_index_.find(name); if (it != dynamic_name_index_.end()) { - const HpackEntry* result = it->second; - if (debug_visitor_ != nullptr) { - debug_visitor_->OnUseEntry(*result); - } - return result; + return dynamic_table_insertions_ - it->second + kStaticTableSize; } } - return nullptr; + return kHpackEntryNotFound; } -const HpackEntry* HpackHeaderTable::GetByNameAndValue(absl::string_view name, - absl::string_view value) { - HpackEntry query(name, value); +size_t HpackHeaderTable::GetByNameAndValue(absl::string_view name, + absl::string_view value) { + HpackLookupEntry query{name, value}; { - auto it = static_index_.find(&query); + auto it = static_index_.find(query); if (it != static_index_.end()) { - return *it; + return 1 + it->second; } } { - auto it = dynamic_index_.find(&query); + auto it = dynamic_index_.find(query); if (it != dynamic_index_.end()) { - const HpackEntry* result = *it; - if (debug_visitor_ != nullptr) { - debug_visitor_->OnUseEntry(*result); - } - return result; + return dynamic_table_insertions_ - it->second + kStaticTableSize; } } - return nullptr; -} - -size_t HpackHeaderTable::IndexOf(const HpackEntry* entry) const { - if (entry->IsLookup()) { - return 0; - } else if (entry->IsStatic()) { - return 1 + entry->InsertionIndex(); - } else { - return total_insertions_ - entry->InsertionIndex() + static_entries_.size(); - } + return kHpackEntryNotFound; } void HpackHeaderTable::SetMaxSize(size_t max_size) { @@ -130,8 +75,8 @@ void HpackHeaderTable::SetSettingsHeaderTableSize(size_t settings_size) { void HpackHeaderTable::EvictionSet(absl::string_view name, absl::string_view value, - EntryTable::iterator* begin_out, - EntryTable::iterator* end_out) { + DynamicEntryTable::iterator* begin_out, + DynamicEntryTable::iterator* end_out) { size_t eviction_count = EvictionCountForEntry(name, value); *begin_out = dynamic_entries_.end() - eviction_count; *end_out = dynamic_entries_.end(); @@ -161,15 +106,17 @@ size_t HpackHeaderTable::EvictionCountToReclaim(size_t reclaim_size) const { void HpackHeaderTable::Evict(size_t count) { for (size_t i = 0; i != count; ++i) { QUICHE_CHECK(!dynamic_entries_.empty()); + HpackEntry* entry = &dynamic_entries_.back(); + const size_t index = dynamic_table_insertions_ - dynamic_entries_.size(); size_ -= entry->Size(); - auto it = dynamic_index_.find(entry); + auto it = dynamic_index_.find({entry->name(), entry->value()}); QUICHE_DCHECK(it != dynamic_index_.end()); // Only remove an entry from the index if its insertion index matches; // otherwise, the index refers to another entry with the same name and // value. - if ((*it)->InsertionIndex() == entry->InsertionIndex()) { + if (it->second == index) { dynamic_index_.erase(it); } auto name_it = dynamic_name_index_.find(entry->name()); @@ -177,7 +124,7 @@ void HpackHeaderTable::Evict(size_t count) { // Only remove an entry from the literal index if its insertion index /// matches; otherwise, the index refers to another entry with the same // name. - if (name_it->second->InsertionIndex() == entry->InsertionIndex()) { + if (name_it->second == index) { dynamic_name_index_.erase(name_it); } dynamic_entries_.pop_back(); @@ -186,6 +133,9 @@ void HpackHeaderTable::Evict(size_t count) { const HpackEntry* HpackHeaderTable::TryAddEntry(absl::string_view name, absl::string_view value) { + // Since |dynamic_entries_| has iterator stability, |name| and |value| are + // valid even after evicting other entries and push_front() making room for + // the new one. Evict(EvictionCountForEntry(name, value)); size_t entry_size = HpackEntry::Size(name, value); @@ -195,77 +145,47 @@ const HpackEntry* HpackHeaderTable::TryAddEntry(absl::string_view name, QUICHE_DCHECK_EQ(0u, size_); return nullptr; } - dynamic_entries_.push_front(HpackEntry(name, value, - false, // is_static - total_insertions_)); + + const size_t index = dynamic_table_insertions_; + dynamic_entries_.push_front( + HpackEntry(std::string(name), std::string(value))); HpackEntry* new_entry = &dynamic_entries_.front(); - auto index_result = dynamic_index_.insert(new_entry); + auto index_result = dynamic_index_.insert(std::make_pair( + HpackLookupEntry{new_entry->name(), new_entry->value()}, index)); if (!index_result.second) { // An entry with the same name and value already exists in the dynamic // index. We should replace it with the newly added entry. - SPDY_DVLOG(1) << "Found existing entry: " - << (*index_result.first)->GetDebugString() - << " replacing with: " << new_entry->GetDebugString(); - QUICHE_DCHECK_GT(new_entry->InsertionIndex(), - (*index_result.first)->InsertionIndex()); + SPDY_DVLOG(1) << "Found existing entry at: " << index_result.first->second + << " replacing with: " << new_entry->GetDebugString() + << " at: " << index; + QUICHE_DCHECK_GT(index, index_result.first->second); dynamic_index_.erase(index_result.first); - QUICHE_CHECK(dynamic_index_.insert(new_entry).second); + auto insert_result = dynamic_index_.insert(std::make_pair( + HpackLookupEntry{new_entry->name(), new_entry->value()}, index)); + QUICHE_CHECK(insert_result.second); } auto name_result = - dynamic_name_index_.insert(std::make_pair(new_entry->name(), new_entry)); + dynamic_name_index_.insert(std::make_pair(new_entry->name(), index)); if (!name_result.second) { // An entry with the same name already exists in the dynamic index. We // should replace it with the newly added entry. - SPDY_DVLOG(1) << "Found existing entry: " - << name_result.first->second->GetDebugString() - << " replacing with: " << new_entry->GetDebugString(); - QUICHE_DCHECK_GT(new_entry->InsertionIndex(), - name_result.first->second->InsertionIndex()); + SPDY_DVLOG(1) << "Found existing entry at: " << name_result.first->second + << " replacing with: " << new_entry->GetDebugString() + << " at: " << index; + QUICHE_DCHECK_GT(index, name_result.first->second); dynamic_name_index_.erase(name_result.first); - auto insert_result = dynamic_name_index_.insert( - std::make_pair(new_entry->name(), new_entry)); + auto insert_result = + dynamic_name_index_.insert(std::make_pair(new_entry->name(), index)); QUICHE_CHECK(insert_result.second); } size_ += entry_size; - ++total_insertions_; - if (debug_visitor_ != nullptr) { - // Call |debug_visitor_->OnNewEntry()| to get the current time. - HpackEntry& entry = dynamic_entries_.front(); - entry.set_time_added(debug_visitor_->OnNewEntry(entry)); - SPDY_DVLOG(2) << "HpackHeaderTable::OnNewEntry: name=" << entry.name() - << ", value=" << entry.value() - << ", insert_index=" << entry.InsertionIndex() - << ", time_added=" << entry.time_added(); - } + ++dynamic_table_insertions_; return &dynamic_entries_.front(); } -void HpackHeaderTable::DebugLogTableState() const { - SPDY_DVLOG(2) << "Dynamic table:"; - for (auto it = dynamic_entries_.begin(); it != dynamic_entries_.end(); ++it) { - SPDY_DVLOG(2) << " " << it->GetDebugString(); - } - SPDY_DVLOG(2) << "Full Static Index:"; - for (const auto* entry : static_index_) { - SPDY_DVLOG(2) << " " << entry->GetDebugString(); - } - SPDY_DVLOG(2) << "Full Static Name Index:"; - for (const auto& it : static_name_index_) { - SPDY_DVLOG(2) << " " << it.first << ": " << it.second->GetDebugString(); - } - SPDY_DVLOG(2) << "Full Dynamic Index:"; - for (const auto* entry : dynamic_index_) { - SPDY_DVLOG(2) << " " << entry->GetDebugString(); - } - SPDY_DVLOG(2) << "Full Dynamic Name Index:"; - for (const auto& it : dynamic_name_index_) { - SPDY_DVLOG(2) << " " << it.first << ": " << it.second->GetDebugString(); - } -} - size_t HpackHeaderTable::EstimateMemoryUsage() const { return SpdyEstimateMemoryUsage(dynamic_entries_) + SpdyEstimateMemoryUsage(dynamic_index_) + diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h index 02a438c57c8..893aaa7bb21 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h @@ -11,11 +11,12 @@ #include <memory> #include "absl/base/attributes.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "absl/hash/hash.h" #include "absl/strings/string_view.h" #include "common/platform/api/quiche_export.h" #include "spdy/core/hpack/hpack_entry.h" -#include "spdy/platform/api/spdy_containers.h" // All section references below are to http://tools.ietf.org/html/rfc7541. @@ -25,32 +26,19 @@ namespace test { class HpackHeaderTablePeer; } // namespace test +// Return value of GetByName() and GetByNameAndValue() if matching entry is not +// found. This value is never used in HPACK for indexing entries, see +// https://httpwg.org/specs/rfc7541.html#index.address.space. +constexpr size_t kHpackEntryNotFound = 0; + // A data structure for the static table (2.3.1) and the dynamic table (2.3.2). class QUICHE_EXPORT_PRIVATE HpackHeaderTable { public: friend class test::HpackHeaderTablePeer; - // Debug visitor my be used to extract debug/internal information - // about the HpackHeaderTable as it operates. - // - // Most HpackHeaderTable implementations do not need to bother with - // this interface at all. - class DebugVisitorInterface { - public: - virtual ~DebugVisitorInterface() {} - - // |OnNewEntry()| and |OnUseEntry()| can be used together to - // gather data about the distribution of time intervals between - // creation and reference of entries in the dynamic table. The - // data is desired to sanity check a proposed extension to HPACK - // for QUIC that would eliminate inter-stream head of line - // blocking (due to standard HPACK). The visitor should return - // the current time from |OnNewEntry()|, which will be passed - // to |OnUseEntry()| each time that particular entry is used to - // emit an indexed representation. - virtual int64_t OnNewEntry(const HpackEntry& entry) = 0; - virtual void OnUseEntry(const HpackEntry& entry) = 0; - }; + // Use a lightweight, memory efficient container for the static table, which + // is initialized once and never changed after. + using StaticEntryTable = std::vector<HpackEntry>; // HpackHeaderTable takes advantage of the deque property that references // remain valid, so long as insertions & deletions are at the head & tail. @@ -59,18 +47,12 @@ class QUICHE_EXPORT_PRIVATE HpackHeaderTable { // If this changes (we want to change to circular_deque or we start to drop // entries from the middle of the table), this should to be a std::list, in // which case |*_index_| can be trivially extended to map to list iterators. - using EntryTable = std::deque<HpackEntry>; - - struct QUICHE_EXPORT_PRIVATE EntryHasher { - size_t operator()(const HpackEntry* entry) const; - }; - struct QUICHE_EXPORT_PRIVATE EntriesEq { - bool operator()(const HpackEntry* lhs, const HpackEntry* rhs) const; - }; - using UnorderedEntrySet = SpdyHashSet<HpackEntry*, EntryHasher, EntriesEq>; - using NameToEntryMap = SpdyHashMap<absl::string_view, - const HpackEntry*, - absl::Hash<absl::string_view>>; + // + // TODO(b/182349990): Change to a more memory efficient container. + using DynamicEntryTable = std::deque<HpackEntry>; + + using NameValueToEntryMap = absl::flat_hash_map<HpackLookupEntry, size_t>; + using NameToEntryMap = absl::flat_hash_map<absl::string_view, size_t>; HpackHeaderTable(); HpackHeaderTable(const HpackHeaderTable&) = delete; @@ -86,18 +68,16 @@ class QUICHE_EXPORT_PRIVATE HpackHeaderTable { size_t size() const { return size_; } size_t max_size() const { return max_size_; } - // Returns the entry matching the index, or NULL. - const HpackEntry* GetByIndex(size_t index); + // The HPACK indexing scheme used by GetByName() and GetByNameAndValue() is + // defined at https://httpwg.org/specs/rfc7541.html#index.address.space. - // Returns the lowest-value entry having |name|, or NULL. - const HpackEntry* GetByName(absl::string_view name); + // Returns the index of the lowest-index entry matching |name|, + // or kHpackEntryNotFound if no matching entry is found. + size_t GetByName(absl::string_view name); - // Returns the lowest-index matching entry, or NULL. - const HpackEntry* GetByNameAndValue(absl::string_view name, - absl::string_view value); - - // Returns the index of an entry within this header table. - size_t IndexOf(const HpackEntry* entry) const; + // Returns the index of the lowest-index entry matching |name| and |value|, + // or kHpackEntryNotFound if no matching entry is found. + size_t GetByNameAndValue(absl::string_view name, absl::string_view value); // Sets the maximum size of the header table, evicting entries if // necessary as described in 5.2. @@ -112,22 +92,17 @@ class QUICHE_EXPORT_PRIVATE HpackHeaderTable { // actually occurs. The set is returned via range [begin_out, end_out). void EvictionSet(absl::string_view name, absl::string_view value, - EntryTable::iterator* begin_out, - EntryTable::iterator* end_out); + DynamicEntryTable::iterator* begin_out, + DynamicEntryTable::iterator* end_out); // Adds an entry for the representation, evicting entries as needed. |name| - // and |value| must not be owned by an entry which could be evicted. The - // added HpackEntry is returned, or NULL is returned if all entries were + // and |value| must not point to an entry in |dynamic_entries_| which is about + // to be evicted, but they may point to an entry which is not. + // The added HpackEntry is returned, or NULL is returned if all entries were // evicted and the empty table is of insufficent size for the representation. const HpackEntry* TryAddEntry(absl::string_view name, absl::string_view value); - void DebugLogTableState() const ABSL_ATTRIBUTE_UNUSED; - - void set_debug_visitor(std::unique_ptr<DebugVisitorInterface> visitor) { - debug_visitor_ = std::move(visitor); - } - // Returns the estimate of dynamically allocated memory in bytes. size_t EstimateMemoryUsage() const; @@ -145,21 +120,28 @@ class QUICHE_EXPORT_PRIVATE HpackHeaderTable { // |static_entries_|, |static_index_|, and |static_name_index_| are owned by // HpackStaticTable singleton. - // Tracks HpackEntries by index. - const EntryTable& static_entries_; - EntryTable dynamic_entries_; + // Stores HpackEntries. + const StaticEntryTable& static_entries_; + DynamicEntryTable dynamic_entries_; - // Tracks the unique HpackEntry for a given header name and value. - const UnorderedEntrySet& static_index_; + // Tracks the index of the unique HpackEntry for a given header name and + // value. Keys consist of string_views that point to strings stored in + // |static_entries_|. + const NameValueToEntryMap& static_index_; - // Tracks the first static entry for each name in the static table. + // Tracks the index of the first static entry for each name in the static + // table. Each key is a string_view that points to a name string stored in + // |static_entries_|. const NameToEntryMap& static_name_index_; - // Tracks the most recently inserted HpackEntry for a given header name and - // value. - UnorderedEntrySet dynamic_index_; + // Tracks the index of the most recently inserted HpackEntry for a given + // header name and value. Keys consist of string_views that point to strings + // stored in |dynamic_entries_|. + NameValueToEntryMap dynamic_index_; - // Tracks the most recently inserted HpackEntry for a given header name. + // Tracks the index of the most recently inserted HpackEntry for a given + // header name. Each key is a string_view that points to a name string stored + // in |dynamic_entries_|. NameToEntryMap dynamic_name_index_; // Last acknowledged value for SETTINGS_HEADER_TABLE_SIZE. @@ -170,11 +152,9 @@ class QUICHE_EXPORT_PRIVATE HpackHeaderTable { size_t size_; size_t max_size_; - // Total number of table insertions which have occurred. Referenced by - // IndexOf() for determination of an HpackEntry's table index. - size_t total_insertions_; - - std::unique_ptr<DebugVisitorInterface> debug_visitor_; + // Total number of dynamic table insertions so far + // (including entries that have been evicted). + size_t dynamic_table_insertions_; }; } // namespace spdy diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table_test.cc index 2fd7c12696e..23b551648d2 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table_test.cc @@ -13,6 +13,7 @@ #include "common/platform/api/quiche_test.h" #include "spdy/core/hpack/hpack_constants.h" #include "spdy/core/hpack/hpack_entry.h" +#include "spdy/core/hpack/hpack_static_table.h" namespace spdy { @@ -24,18 +25,21 @@ class HpackHeaderTablePeer { public: explicit HpackHeaderTablePeer(HpackHeaderTable* table) : table_(table) {} - const HpackHeaderTable::EntryTable& dynamic_entries() { + const HpackHeaderTable::DynamicEntryTable& dynamic_entries() { return table_->dynamic_entries_; } - const HpackHeaderTable::EntryTable& static_entries() { + const HpackHeaderTable::StaticEntryTable& static_entries() { return table_->static_entries_; } - size_t index_size() { - return table_->static_index_.size() + table_->dynamic_index_.size(); + const HpackEntry* GetFirstStaticEntry() { + return &table_->static_entries_.front(); + } + const HpackEntry* GetLastStaticEntry() { + return &table_->static_entries_.back(); } std::vector<HpackEntry*> EvictionSet(absl::string_view name, absl::string_view value) { - HpackHeaderTable::EntryTable::iterator begin, end; + HpackHeaderTable::DynamicEntryTable::iterator begin, end; table_->EvictionSet(name, value, &begin, &end); std::vector<HpackEntry*> result; for (; begin != end; ++begin) { @@ -43,8 +47,9 @@ class HpackHeaderTablePeer { } return result; } - size_t total_insertions() { return table_->total_insertions_; } - size_t dynamic_entries_count() { return table_->dynamic_entries_.size(); } + size_t dynamic_table_insertions() { + return table_->dynamic_table_insertions_; + } size_t EvictionCountForEntry(absl::string_view name, absl::string_view value) { return table_->EvictionCountForEntry(name, value); @@ -54,11 +59,6 @@ class HpackHeaderTablePeer { } void Evict(size_t count) { return table_->Evict(count); } - void AddDynamicEntry(absl::string_view name, absl::string_view value) { - table_->dynamic_entries_.push_back( - HpackEntry(name, value, false, table_->total_insertions_++)); - } - private: HpackHeaderTable* table_; }; @@ -75,10 +75,10 @@ class HpackHeaderTableTest : public QuicheTest { // Returns an entry whose Size() is equal to the given one. static HpackEntry MakeEntryOfSize(uint32_t size) { - EXPECT_GE(size, HpackEntry::kSizeOverhead); - std::string name((size - HpackEntry::kSizeOverhead) / 2, 'n'); - std::string value(size - HpackEntry::kSizeOverhead - name.size(), 'v'); - HpackEntry entry(name, value, false, 0); + EXPECT_GE(size, kHpackEntrySizeOverhead); + std::string name((size - kHpackEntrySizeOverhead) / 2, 'n'); + std::string value(size - kHpackEntrySizeOverhead - name.size(), 'v'); + HpackEntry entry(name, value); EXPECT_EQ(size, entry.Size()); return entry; } @@ -86,8 +86,8 @@ class HpackHeaderTableTest : public QuicheTest { // Returns a vector of entries whose total size is equal to the given // one. static HpackEntryVector MakeEntriesOfTotalSize(uint32_t total_size) { - EXPECT_GE(total_size, HpackEntry::kSizeOverhead); - uint32_t entry_size = HpackEntry::kSizeOverhead; + EXPECT_GE(total_size, kHpackEntrySizeOverhead); + uint32_t entry_size = kHpackEntrySizeOverhead; uint32_t remaining_size = total_size; HpackEntryVector entries; while (remaining_size > 0) { @@ -103,7 +103,7 @@ class HpackHeaderTableTest : public QuicheTest { // expecting no eviction to happen. void AddEntriesExpectNoEviction(const HpackEntryVector& entries) { for (auto it = entries.begin(); it != entries.end(); ++it) { - HpackHeaderTable::EntryTable::iterator begin, end; + HpackHeaderTable::DynamicEntryTable::iterator begin, end; table_.EvictionSet(it->name(), it->value(), &begin, &end); EXPECT_EQ(0, distance(begin, end)); @@ -111,20 +111,6 @@ class HpackHeaderTableTest : public QuicheTest { const HpackEntry* entry = table_.TryAddEntry(it->name(), it->value()); EXPECT_NE(entry, static_cast<HpackEntry*>(nullptr)); } - - for (size_t i = 0; i != entries.size(); ++i) { - // Static table has 61 entries, dynamic entries follow those. - size_t index = 61 + entries.size() - i; - const HpackEntry* entry = table_.GetByIndex(index); - EXPECT_EQ(entries[i].name(), entry->name()); - EXPECT_EQ(entries[i].value(), entry->value()); - EXPECT_EQ(index, table_.IndexOf(entry)); - } - } - - HpackEntry DynamicEntry(const std::string& name, const std::string& value) { - peer_.AddDynamicEntry(name, value); - return peer_.dynamic_entries().back(); } HpackHeaderTable table_; @@ -136,124 +122,126 @@ TEST_F(HpackHeaderTableTest, StaticTableInitialization) { EXPECT_EQ(kDefaultHeaderTableSizeSetting, table_.max_size()); EXPECT_EQ(kDefaultHeaderTableSizeSetting, table_.settings_size_bound()); - EXPECT_EQ(0u, peer_.dynamic_entries_count()); - EXPECT_EQ(peer_.static_entries().size(), peer_.total_insertions()); + EXPECT_EQ(0u, peer_.dynamic_entries().size()); + EXPECT_EQ(0u, peer_.dynamic_table_insertions()); // Static entries have been populated and inserted into the table & index. - EXPECT_NE(0u, peer_.static_entries().size()); - EXPECT_EQ(peer_.index_size(), peer_.static_entries().size()); - for (size_t i = 0; i != peer_.static_entries().size(); ++i) { - const HpackEntry* entry = &peer_.static_entries()[i]; - - EXPECT_TRUE(entry->IsStatic()); - EXPECT_EQ(entry, table_.GetByIndex(i + 1)); - EXPECT_EQ(entry, table_.GetByNameAndValue(entry->name(), entry->value())); + const HpackHeaderTable::StaticEntryTable& static_entries = + peer_.static_entries(); + EXPECT_EQ(kStaticTableSize, static_entries.size()); + // HPACK indexing scheme is 1-based. + size_t index = 1; + for (const HpackEntry& entry : static_entries) { + EXPECT_EQ(index, table_.GetByNameAndValue(entry.name(), entry.value())); + index++; } } TEST_F(HpackHeaderTableTest, BasicDynamicEntryInsertionAndEviction) { - size_t static_count = peer_.total_insertions(); - const HpackEntry* first_static_entry = table_.GetByIndex(1); + EXPECT_EQ(kStaticTableSize, peer_.static_entries().size()); - EXPECT_EQ(1u, table_.IndexOf(first_static_entry)); + const HpackEntry* first_static_entry = peer_.GetFirstStaticEntry(); + const HpackEntry* last_static_entry = peer_.GetLastStaticEntry(); const HpackEntry* entry = table_.TryAddEntry("header-key", "Header Value"); EXPECT_EQ("header-key", entry->name()); EXPECT_EQ("Header Value", entry->value()); - EXPECT_FALSE(entry->IsStatic()); // Table counts were updated appropriately. EXPECT_EQ(entry->Size(), table_.size()); - EXPECT_EQ(1u, peer_.dynamic_entries_count()); - EXPECT_EQ(peer_.dynamic_entries().size(), peer_.dynamic_entries_count()); - EXPECT_EQ(static_count + 1, peer_.total_insertions()); - EXPECT_EQ(static_count + 1, peer_.index_size()); - - // Index() of entries reflects the insertion. - EXPECT_EQ(1u, table_.IndexOf(first_static_entry)); - // Static table has 61 entries. - EXPECT_EQ(62u, table_.IndexOf(entry)); - EXPECT_EQ(first_static_entry, table_.GetByIndex(1)); - EXPECT_EQ(entry, table_.GetByIndex(62)); + EXPECT_EQ(1u, peer_.dynamic_entries().size()); + EXPECT_EQ(kStaticTableSize, peer_.static_entries().size()); + + EXPECT_EQ(62u, table_.GetByNameAndValue("header-key", "Header Value")); + + // Index of static entries does not change. + EXPECT_EQ(first_static_entry, peer_.GetFirstStaticEntry()); + EXPECT_EQ(last_static_entry, peer_.GetLastStaticEntry()); // Evict |entry|. Table counts are again updated appropriately. peer_.Evict(1); EXPECT_EQ(0u, table_.size()); - EXPECT_EQ(0u, peer_.dynamic_entries_count()); - EXPECT_EQ(peer_.dynamic_entries().size(), peer_.dynamic_entries_count()); - EXPECT_EQ(static_count + 1, peer_.total_insertions()); - EXPECT_EQ(static_count, peer_.index_size()); - - // Index() of |first_static_entry| reflects the eviction. - EXPECT_EQ(1u, table_.IndexOf(first_static_entry)); - EXPECT_EQ(first_static_entry, table_.GetByIndex(1)); + EXPECT_EQ(0u, peer_.dynamic_entries().size()); + EXPECT_EQ(kStaticTableSize, peer_.static_entries().size()); + + // Index of static entries does not change. + EXPECT_EQ(first_static_entry, peer_.GetFirstStaticEntry()); + EXPECT_EQ(last_static_entry, peer_.GetLastStaticEntry()); } TEST_F(HpackHeaderTableTest, EntryIndexing) { - const HpackEntry* first_static_entry = table_.GetByIndex(1); + const HpackEntry* first_static_entry = peer_.GetFirstStaticEntry(); + const HpackEntry* last_static_entry = peer_.GetLastStaticEntry(); // Static entries are queryable by name & value. - EXPECT_EQ(first_static_entry, table_.GetByName(first_static_entry->name())); - EXPECT_EQ(first_static_entry, - table_.GetByNameAndValue(first_static_entry->name(), - first_static_entry->value())); + EXPECT_EQ(1u, table_.GetByName(first_static_entry->name())); + EXPECT_EQ(1u, table_.GetByNameAndValue(first_static_entry->name(), + first_static_entry->value())); // Create a mix of entries which duplicate names, and names & values of both // dynamic and static entries. - const HpackEntry* entry1 = table_.TryAddEntry(first_static_entry->name(), - first_static_entry->value()); - const HpackEntry* entry2 = - table_.TryAddEntry(first_static_entry->name(), "Value Four"); - const HpackEntry* entry3 = table_.TryAddEntry("key-1", "Value One"); - const HpackEntry* entry4 = table_.TryAddEntry("key-2", "Value Three"); - const HpackEntry* entry5 = table_.TryAddEntry("key-1", "Value Two"); - const HpackEntry* entry6 = table_.TryAddEntry("key-2", "Value Three"); - const HpackEntry* entry7 = table_.TryAddEntry("key-2", "Value Four"); - - // Entries are queryable under their current index. - EXPECT_EQ(entry7, table_.GetByIndex(62)); - EXPECT_EQ(entry6, table_.GetByIndex(63)); - EXPECT_EQ(entry5, table_.GetByIndex(64)); - EXPECT_EQ(entry4, table_.GetByIndex(65)); - EXPECT_EQ(entry3, table_.GetByIndex(66)); - EXPECT_EQ(entry2, table_.GetByIndex(67)); - EXPECT_EQ(entry1, table_.GetByIndex(68)); - EXPECT_EQ(first_static_entry, table_.GetByIndex(1)); + table_.TryAddEntry(first_static_entry->name(), first_static_entry->value()); + table_.TryAddEntry(first_static_entry->name(), "Value Four"); + table_.TryAddEntry("key-1", "Value One"); + table_.TryAddEntry("key-2", "Value Three"); + table_.TryAddEntry("key-1", "Value Two"); + table_.TryAddEntry("key-2", "Value Three"); + table_.TryAddEntry("key-2", "Value Four"); + + // The following entry is identical to the one at index 68. The smaller index + // is returned by GetByNameAndValue(). + EXPECT_EQ(1u, table_.GetByNameAndValue(first_static_entry->name(), + first_static_entry->value())); + EXPECT_EQ(67u, + table_.GetByNameAndValue(first_static_entry->name(), "Value Four")); + EXPECT_EQ(66u, table_.GetByNameAndValue("key-1", "Value One")); + EXPECT_EQ(64u, table_.GetByNameAndValue("key-1", "Value Two")); + // The following entry is identical to the one at index 65. The smaller index + // is returned by GetByNameAndValue(). + EXPECT_EQ(63u, table_.GetByNameAndValue("key-2", "Value Three")); + EXPECT_EQ(62u, table_.GetByNameAndValue("key-2", "Value Four")); + + // Index of static entries does not change. + EXPECT_EQ(first_static_entry, peer_.GetFirstStaticEntry()); + EXPECT_EQ(last_static_entry, peer_.GetLastStaticEntry()); // Querying by name returns the most recently added matching entry. - EXPECT_EQ(entry5, table_.GetByName("key-1")); - EXPECT_EQ(entry7, table_.GetByName("key-2")); - EXPECT_EQ(entry2->name(), - table_.GetByName(first_static_entry->name())->name()); - EXPECT_EQ(nullptr, table_.GetByName("not-present")); + EXPECT_EQ(64u, table_.GetByName("key-1")); + EXPECT_EQ(62u, table_.GetByName("key-2")); + EXPECT_EQ(1u, table_.GetByName(first_static_entry->name())); + EXPECT_EQ(kHpackEntryNotFound, table_.GetByName("not-present")); // Querying by name & value returns the lowest-index matching entry among // static entries, and the highest-index one among dynamic entries. - EXPECT_EQ(entry3, table_.GetByNameAndValue("key-1", "Value One")); - EXPECT_EQ(entry5, table_.GetByNameAndValue("key-1", "Value Two")); - EXPECT_EQ(entry6, table_.GetByNameAndValue("key-2", "Value Three")); - EXPECT_EQ(entry7, table_.GetByNameAndValue("key-2", "Value Four")); - EXPECT_EQ(first_static_entry, - table_.GetByNameAndValue(first_static_entry->name(), - first_static_entry->value())); - EXPECT_EQ(entry2, + EXPECT_EQ(66u, table_.GetByNameAndValue("key-1", "Value One")); + EXPECT_EQ(64u, table_.GetByNameAndValue("key-1", "Value Two")); + EXPECT_EQ(63u, table_.GetByNameAndValue("key-2", "Value Three")); + EXPECT_EQ(62u, table_.GetByNameAndValue("key-2", "Value Four")); + EXPECT_EQ(1u, table_.GetByNameAndValue(first_static_entry->name(), + first_static_entry->value())); + EXPECT_EQ(67u, table_.GetByNameAndValue(first_static_entry->name(), "Value Four")); - EXPECT_EQ(nullptr, table_.GetByNameAndValue("key-1", "Not Present")); - EXPECT_EQ(nullptr, table_.GetByNameAndValue("not-present", "Value One")); + EXPECT_EQ(kHpackEntryNotFound, + table_.GetByNameAndValue("key-1", "Not Present")); + EXPECT_EQ(kHpackEntryNotFound, + table_.GetByNameAndValue("not-present", "Value One")); // Evict |entry1|. Queries for its name & value now return the static entry. // |entry2| remains queryable. peer_.Evict(1); - EXPECT_EQ(first_static_entry, - table_.GetByNameAndValue(first_static_entry->name(), - first_static_entry->value())); - EXPECT_EQ(entry2, + EXPECT_EQ(1u, table_.GetByNameAndValue(first_static_entry->name(), + first_static_entry->value())); + EXPECT_EQ(67u, table_.GetByNameAndValue(first_static_entry->name(), "Value Four")); // Evict |entry2|. Queries by its name & value are not found. peer_.Evict(1); - EXPECT_EQ(nullptr, + EXPECT_EQ(kHpackEntryNotFound, table_.GetByNameAndValue(first_static_entry->name(), "Value Four")); + + // Index of static entries does not change. + EXPECT_EQ(first_static_entry, peer_.GetFirstStaticEntry()); + EXPECT_EQ(last_static_entry, peer_.GetLastStaticEntry()); } TEST_F(HpackHeaderTableTest, SetSizes) { @@ -363,7 +351,9 @@ TEST_F(HpackHeaderTableTest, TryAddEntryEviction) { HpackEntryVector entries = MakeEntriesOfTotalSize(table_.max_size()); AddEntriesExpectNoEviction(entries); - const HpackEntry* survivor_entry = table_.GetByIndex(61 + 1); + // The first entry in the dynamic table. + const HpackEntry* survivor_entry = &peer_.dynamic_entries().front(); + HpackEntry long_entry = MakeEntryOfSize(table_.max_size() - survivor_entry->Size()); @@ -371,12 +361,12 @@ TEST_F(HpackHeaderTableTest, TryAddEntryEviction) { EXPECT_EQ(peer_.dynamic_entries().size() - 1, peer_.EvictionSet(long_entry.name(), long_entry.value()).size()); - const HpackEntry* new_entry = - table_.TryAddEntry(long_entry.name(), long_entry.value()); - EXPECT_EQ(62u, table_.IndexOf(new_entry)); + table_.TryAddEntry(long_entry.name(), long_entry.value()); EXPECT_EQ(2u, peer_.dynamic_entries().size()); - EXPECT_EQ(table_.GetByIndex(63), survivor_entry); - EXPECT_EQ(table_.GetByIndex(62), new_entry); + EXPECT_EQ(63u, table_.GetByNameAndValue(survivor_entry->name(), + survivor_entry->value())); + EXPECT_EQ(62u, + table_.GetByNameAndValue(long_entry.name(), long_entry.value())); } // Fill a header table with entries, and then add an entry bigger than @@ -397,50 +387,6 @@ TEST_F(HpackHeaderTableTest, TryAddTooLargeEntry) { EXPECT_EQ(0u, peer_.dynamic_entries().size()); } -TEST_F(HpackHeaderTableTest, EntryNamesDiffer) { - HpackEntry entry1("header", "value"); - HpackEntry entry2("HEADER", "value"); - - HpackHeaderTable::EntryHasher hasher; - EXPECT_NE(hasher(&entry1), hasher(&entry2)); - - HpackHeaderTable::EntriesEq eq; - EXPECT_FALSE(eq(&entry1, &entry2)); -} - -TEST_F(HpackHeaderTableTest, EntryValuesDiffer) { - HpackEntry entry1("header", "value"); - HpackEntry entry2("header", "VALUE"); - - HpackHeaderTable::EntryHasher hasher; - EXPECT_NE(hasher(&entry1), hasher(&entry2)); - - HpackHeaderTable::EntriesEq eq; - EXPECT_FALSE(eq(&entry1, &entry2)); -} - -TEST_F(HpackHeaderTableTest, EntriesEqual) { - HpackEntry entry1(DynamicEntry("name", "value")); - HpackEntry entry2(DynamicEntry("name", "value")); - - HpackHeaderTable::EntryHasher hasher; - EXPECT_EQ(hasher(&entry1), hasher(&entry2)); - - HpackHeaderTable::EntriesEq eq; - EXPECT_TRUE(eq(&entry1, &entry2)); -} - -TEST_F(HpackHeaderTableTest, StaticAndDynamicEntriesEqual) { - HpackEntry entry1("name", "value"); - HpackEntry entry2(DynamicEntry("name", "value")); - - HpackHeaderTable::EntryHasher hasher; - EXPECT_EQ(hasher(&entry1), hasher(&entry2)); - - HpackHeaderTable::EntriesEq eq; - EXPECT_TRUE(eq(&entry1, &entry2)); -} - } // namespace } // namespace spdy diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.cc index 809bc82890b..8c8248c2343 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.cc @@ -20,20 +20,27 @@ void HpackStaticTable::Initialize(const HpackStaticEntry* static_entry_table, size_t static_entry_count) { QUICHE_CHECK(!IsInitialized()); - int total_insertions = 0; + static_entries_.reserve(static_entry_count); + for (const HpackStaticEntry* it = static_entry_table; it != static_entry_table + static_entry_count; ++it) { - static_entries_.push_back( - HpackEntry(absl::string_view(it->name, it->name_len), - absl::string_view(it->value, it->value_len), - true, // is_static - total_insertions)); - HpackEntry* entry = &static_entries_.back(); - QUICHE_CHECK(static_index_.insert(entry).second); + std::string name(it->name, it->name_len); + std::string value(it->value, it->value_len); + static_entries_.push_back(HpackEntry(std::move(name), std::move(value))); + } + + // |static_entries_| will not be mutated any more. Therefore its entries will + // remain stable even if the container does not have iterator stability. + int insertion_count = 0; + for (const auto& entry : static_entries_) { + auto result = static_index_.insert(std::make_pair( + HpackLookupEntry{entry.name(), entry.value()}, insertion_count)); + QUICHE_CHECK(result.second); + // Multiple static entries may have the same name, so inserts may fail. - static_name_index_.insert(std::make_pair(entry->name(), entry)); + static_name_index_.insert(std::make_pair(entry.name(), insertion_count)); - ++total_insertions; + ++insertion_count; } } diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.h b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.h index 436ae593c0b..346332e4134 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.h +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.h @@ -12,6 +12,9 @@ namespace spdy { struct HpackStaticEntry; +// Number of entries in the HPACK static table. +constexpr size_t kStaticTableSize = 61; + // HpackStaticTable provides |static_entries_| and |static_index_| for HPACK // encoding and decoding contexts. Once initialized, an instance is read only // and may be accessed only through its const interface. Such an instance may @@ -30,10 +33,10 @@ class QUICHE_EXPORT_PRIVATE HpackStaticTable { bool IsInitialized() const; // Accessors. - const HpackHeaderTable::EntryTable& GetStaticEntries() const { + const HpackHeaderTable::StaticEntryTable& GetStaticEntries() const { return static_entries_; } - const HpackHeaderTable::UnorderedEntrySet& GetStaticIndex() const { + const HpackHeaderTable::NameValueToEntryMap& GetStaticIndex() const { return static_index_; } const HpackHeaderTable::NameToEntryMap& GetStaticNameIndex() const { @@ -44,8 +47,10 @@ class QUICHE_EXPORT_PRIVATE HpackStaticTable { size_t EstimateMemoryUsage() const; private: - HpackHeaderTable::EntryTable static_entries_; - HpackHeaderTable::UnorderedEntrySet static_index_; + HpackHeaderTable::StaticEntryTable static_entries_; + // The following two members have string_views that point to strings stored in + // |static_entries_|. + HpackHeaderTable::NameValueToEntryMap static_index_; HpackHeaderTable::NameToEntryMap static_name_index_; }; diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table_test.cc index ad868b16c80..f7506daf474 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table_test.cc @@ -31,17 +31,20 @@ TEST_F(HpackStaticTableTest, Initialize) { HpackStaticTableVector().size()); EXPECT_TRUE(table_.IsInitialized()); - HpackHeaderTable::EntryTable static_entries = table_.GetStaticEntries(); - EXPECT_EQ(HpackStaticTableVector().size(), static_entries.size()); + const HpackHeaderTable::StaticEntryTable& static_entries = + table_.GetStaticEntries(); + EXPECT_EQ(kStaticTableSize, static_entries.size()); - HpackHeaderTable::UnorderedEntrySet static_index = table_.GetStaticIndex(); - EXPECT_EQ(HpackStaticTableVector().size(), static_index.size()); + const HpackHeaderTable::NameValueToEntryMap& static_index = + table_.GetStaticIndex(); + EXPECT_EQ(kStaticTableSize, static_index.size()); - HpackHeaderTable::NameToEntryMap static_name_index = + const HpackHeaderTable::NameToEntryMap& static_name_index = table_.GetStaticNameIndex(); + // Count distinct names in static table. std::set<absl::string_view> names; - for (auto* entry : static_index) { - names.insert(entry->name()); + for (const auto& entry : static_entries) { + names.insert(entry.name()); } EXPECT_EQ(names.size(), static_name_index.size()); } 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 b9989c83cc8..cb9c176090a 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 @@ -277,12 +277,6 @@ void Http2DecoderAdapter::set_extension_visitor( extension_ = visitor; } -// Passes the call on to the HPACK decoder. -void Http2DecoderAdapter::SetDecoderHeaderTableDebugVisitor( - std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor) { - GetHpackDecoder()->SetHeaderTableDebugVisitor(std::move(visitor)); -} - size_t Http2DecoderAdapter::ProcessInput(const char* data, size_t len) { size_t limit = recv_frame_size_limit_; frame_decoder_->set_maximum_payload_size(limit); @@ -476,8 +470,9 @@ void Http2DecoderAdapter::OnHeadersPriority( on_headers_called_ = true; ReportReceiveCompressedFrame(frame_header_); if (!visitor()) { - SPDY_BUG << "Visitor is nullptr, handling priority in headers failed." - << " priority:" << priority << " frame_header:" << frame_header_; + SPDY_BUG(spdy_bug_1_1) + << "Visitor is nullptr, handling priority in headers failed." + << " priority:" << priority << " frame_header:" << frame_header_; return; } visitor()->OnHeaders(frame_header_.stream_id, kHasPriorityFields, @@ -827,9 +822,10 @@ size_t Http2DecoderAdapter::ProcessInputFrame(const char* data, size_t len) { << " total remaining in the frame's payload."; db.AdvanceCursor(avail); } else { - SPDY_BUG << "Total remaining (" << total - << ") should not be greater than the payload length; " - << frame_header(); + SPDY_BUG(spdy_bug_1_2) + << "Total remaining (" << total + << ") should not be greater than the payload length; " + << frame_header(); } } } @@ -877,12 +873,13 @@ void Http2DecoderAdapter::DetermineSpdyState(DecodeStatus status) { DecodeBuffer tmp("", 0); DecodeStatus status = frame_decoder_->DecodeFrame(&tmp); if (status != DecodeStatus::kDecodeDone) { - SPDY_BUG << "Expected to be done decoding the frame, not " - << status; + SPDY_BUG(spdy_bug_1_3) + << "Expected to be done decoding the frame, not " << status; SetSpdyErrorAndNotify(SPDY_INTERNAL_FRAMER_ERROR, ""); } else if (spdy_framer_error_ != SPDY_NO_ERROR) { - SPDY_BUG << "Expected to have no error, not " - << SpdyFramerErrorToString(spdy_framer_error_); + SPDY_BUG(spdy_bug_1_4) + << "Expected to have no error, not " + << SpdyFramerErrorToString(spdy_framer_error_); } else { ResetBetweenFrames(); } @@ -1077,7 +1074,7 @@ void Http2DecoderAdapter::CommonStartHpackBlock() { SpdyHeadersHandlerInterface* handler = visitor()->OnHeaderFrameStart(stream_id()); if (handler == nullptr) { - SPDY_BUG << "visitor_->OnHeaderFrameStart returned nullptr"; + SPDY_BUG(spdy_bug_1_5) << "visitor_->OnHeaderFrameStart returned nullptr"; SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INTERNAL_FRAMER_ERROR, ""); return; } diff --git a/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h b/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h index eec6958fa2c..f05152b6468 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h +++ b/chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h @@ -127,14 +127,16 @@ class QUICHE_EXPORT_PRIVATE Http2DecoderAdapter return debug_visitor_; } - // Set debug callbacks to be called from the HPACK decoder. - void SetDecoderHeaderTableDebugVisitor( - std::unique_ptr<spdy::HpackHeaderTable::DebugVisitorInterface> visitor); - // Decode the |len| bytes of encoded HTTP/2 starting at |*data|. Returns // the number of bytes consumed. It is safe to pass more bytes in than // may be consumed. Should process (or otherwise buffer) as much as // available. + // + // If the input contains the entirety of a DATA frame payload, GOAWAY frame + // Additional Debug Data field, or unknown frame payload, then the + // corresponding SpdyFramerVisitorInterface::OnStreamFrameData(), + // OnGoAwayFrameData(), or ExtensionVisitorInterface::OnFramePayload() method + // is guaranteed to be called exactly once, with the entire payload or field. size_t ProcessInput(const char* data, size_t len); // Reset the decoder (used just for tests at this time). diff --git a/chromium/net/third_party/quiche/src/spdy/core/http2_priority_write_scheduler.h b/chromium/net/third_party/quiche/src/spdy/core/http2_priority_write_scheduler.h index 2486453b251..5820145aac4 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/http2_priority_write_scheduler.h +++ b/chromium/net/third_party/quiche/src/spdy/core/http2_priority_write_scheduler.h @@ -82,7 +82,7 @@ class Http2PriorityWriteScheduler : public WriteScheduler<StreamIdType> { friend class test::Http2PriorityWriteSchedulerPeer<StreamIdType>; struct StreamInfo; - typedef SpdyInlinedVector<StreamInfo*, 4> StreamInfoVector; + using StreamInfoVector = absl::InlinedVector<StreamInfo*, 4>; struct StreamInfo : public SpdyIntrusiveLink<StreamInfo> { // ID for this stream. @@ -221,11 +221,11 @@ void Http2PriorityWriteScheduler<StreamIdType>::RegisterStream( // (e.g. SpdyClientDispatcher) modified to pass StreamPrecedence instances // appropriate for protocol version under test. // - // SPDY_BUG_IF(precedence.is_spdy3_priority()) + // SPDY_BUG_IF(spdy_bug_8_1, precedence.is_spdy3_priority()) // << "Expected HTTP/2 stream dependency"; if (StreamRegistered(stream_id)) { - SPDY_BUG << "Stream " << stream_id << " already registered"; + SPDY_BUG(spdy_bug_8_2) << "Stream " << stream_id << " already registered"; return; } @@ -272,13 +272,13 @@ template <typename StreamIdType> void Http2PriorityWriteScheduler<StreamIdType>::UnregisterStream( StreamIdType stream_id) { if (stream_id == kHttp2RootStreamId) { - SPDY_BUG << "Cannot unregister root stream"; + SPDY_BUG(spdy_bug_8_3) << "Cannot unregister root stream"; return; } // Remove the stream from table. auto it = all_stream_infos_.find(stream_id); if (it == all_stream_infos_.end()) { - SPDY_BUG << "Stream " << stream_id << " not registered"; + SPDY_BUG(spdy_bug_8_4) << "Stream " << stream_id << " not registered"; return; } std::unique_ptr<StreamInfo> stream_info(std::move(it->second)); @@ -335,7 +335,7 @@ Http2PriorityWriteScheduler<StreamIdType>::GetStreamChildren( std::vector<StreamIdType> child_vec; const StreamInfo* stream_info = FindStream(stream_id); if (stream_info == nullptr) { - SPDY_BUG << "Stream " << stream_id << " not registered"; + SPDY_BUG(spdy_bug_8_5) << "Stream " << stream_id << " not registered"; } else { child_vec.reserve(stream_info->children.size()); for (StreamInfo* child : stream_info->children) { @@ -353,10 +353,10 @@ void Http2PriorityWriteScheduler<StreamIdType>::UpdateStreamPrecedence( // (e.g. SpdyClientDispatcher) modified to pass StreamPrecedence instances // appropriate for protocol version under test. // - // SPDY_BUG_IF(precedence.is_spdy3_priority()) + // SPDY_BUG_IF(spdy_bug_8_6, precedence.is_spdy3_priority()) // << "Expected HTTP/2 stream dependency"; if (stream_id == kHttp2RootStreamId) { - SPDY_BUG << "Cannot set precedence of root stream"; + SPDY_BUG(spdy_bug_8_7) << "Cannot set precedence of root stream"; return; } @@ -393,7 +393,7 @@ void Http2PriorityWriteScheduler<StreamIdType>::UpdateStreamParent( StreamIdType parent_id, bool exclusive) { if (stream_info->id == parent_id) { - SPDY_BUG << "Cannot set stream to be its own parent"; + SPDY_BUG(spdy_bug_8_8) << "Cannot set stream to be its own parent"; return; } StreamInfo* new_parent = FindStream(parent_id); @@ -457,12 +457,12 @@ void Http2PriorityWriteScheduler<StreamIdType>::RecordStreamEventTime( StreamIdType stream_id, int64_t now_in_usec) { if (stream_id == kHttp2RootStreamId) { - SPDY_BUG << "Cannot record event time for root stream"; + SPDY_BUG(spdy_bug_8_9) << "Cannot record event time for root stream"; return; } StreamInfo* stream_info = FindStream(stream_id); if (stream_info == nullptr) { - SPDY_BUG << "Stream " << stream_id << " not registered"; + SPDY_BUG(spdy_bug_8_10) << "Stream " << stream_id << " not registered"; return; } stream_info->last_event_time_usec = now_in_usec; @@ -477,12 +477,12 @@ template <typename StreamIdType> int64_t Http2PriorityWriteScheduler<StreamIdType>::GetLatestEventWithPrecedence( StreamIdType stream_id) const { if (stream_id == kHttp2RootStreamId) { - SPDY_BUG << "Invalid argument: root stream"; + SPDY_BUG(spdy_bug_8_11) << "Invalid argument: root stream"; return 0; } const StreamInfo* stream_info = FindStream(stream_id); if (stream_info == nullptr) { - SPDY_BUG << "Stream " << stream_id << " not registered"; + SPDY_BUG(spdy_bug_8_12) << "Stream " << stream_id << " not registered"; return 0; } int64_t last_event_time_usec = 0; @@ -503,12 +503,12 @@ template <typename StreamIdType> bool Http2PriorityWriteScheduler<StreamIdType>::ShouldYield( StreamIdType stream_id) const { if (stream_id == kHttp2RootStreamId) { - SPDY_BUG << "Invalid argument: root stream"; + SPDY_BUG(spdy_bug_8_13) << "Invalid argument: root stream"; return false; } const StreamInfo* stream_info = FindStream(stream_id); if (stream_info == nullptr) { - SPDY_BUG << "Stream " << stream_id << " not registered"; + SPDY_BUG(spdy_bug_8_14) << "Stream " << stream_id << " not registered"; return false; } if (HasReadyAncestor(*stream_info)) { @@ -534,12 +534,12 @@ void Http2PriorityWriteScheduler<StreamIdType>::MarkStreamReady( StreamIdType stream_id, bool add_to_front) { if (stream_id == kHttp2RootStreamId) { - SPDY_BUG << "Cannot mark root stream ready"; + SPDY_BUG(spdy_bug_8_15) << "Cannot mark root stream ready"; return; } StreamInfo* stream_info = FindStream(stream_id); if (stream_info == nullptr) { - SPDY_BUG << "Stream " << stream_id << " not registered"; + SPDY_BUG(spdy_bug_8_16) << "Stream " << stream_id << " not registered"; return; } if (stream_info->ready) { @@ -553,12 +553,12 @@ template <typename StreamIdType> void Http2PriorityWriteScheduler<StreamIdType>::MarkStreamNotReady( StreamIdType stream_id) { if (stream_id == kHttp2RootStreamId) { - SPDY_BUG << "Cannot mark root stream unready"; + SPDY_BUG(spdy_bug_8_17) << "Cannot mark root stream unready"; return; } StreamInfo* stream_info = FindStream(stream_id); if (stream_info == nullptr) { - SPDY_BUG << "Stream " << stream_id << " not registered"; + SPDY_BUG(spdy_bug_8_18) << "Stream " << stream_id << " not registered"; return; } if (!stream_info->ready) { @@ -680,7 +680,7 @@ Http2PriorityWriteScheduler<StreamIdType>::PopNextReadyStreamAndPrecedence() { return std::make_tuple(stream_info.id, stream_info.ToStreamPrecedence()); } } - SPDY_BUG << "No ready streams"; + SPDY_BUG(spdy_bug_8_19) << "No ready streams"; return std::make_tuple( kHttp2RootStreamId, StreamPrecedenceType(kHttp2RootStreamId, kHttp2MinStreamWeight, false)); @@ -695,12 +695,12 @@ template <typename StreamIdType> bool Http2PriorityWriteScheduler<StreamIdType>::IsStreamReady( StreamIdType stream_id) const { if (stream_id == kHttp2RootStreamId) { - SPDY_BUG << "Try to check whether root stream is ready"; + SPDY_BUG(spdy_bug_8_20) << "Try to check whether root stream is ready"; return false; } const StreamInfo* stream_info = FindStream(stream_id); if (stream_info == nullptr) { - SPDY_BUG << "Stream " << stream_id << " not registered"; + SPDY_BUG(spdy_bug_8_21) << "Stream " << stream_id << " not registered"; return false; } return stream_info->ready; diff --git a/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler.h b/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler.h deleted file mode 100644 index cbe53d20ae2..00000000000 --- a/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler.h +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright (c) 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef QUICHE_SPDY_CORE_LIFO_WRITE_SCHEDULER_H_ -#define QUICHE_SPDY_CORE_LIFO_WRITE_SCHEDULER_H_ - -#include <cstdint> -#include <map> -#include <set> -#include <string> - -#include "absl/strings/str_cat.h" -#include "spdy/core/write_scheduler.h" -#include "spdy/platform/api/spdy_containers.h" - -namespace spdy { - -namespace test { - -template <typename StreamIdType> -class LifoWriteSchedulerPeer; - -} // namespace test - -// Create a write scheduler where the stream added last will have the highest -// priority. -template <typename StreamIdType> -class LifoWriteScheduler : public WriteScheduler<StreamIdType> { - public: - using typename WriteScheduler<StreamIdType>::StreamPrecedenceType; - - LifoWriteScheduler() = default; - - void RegisterStream(StreamIdType stream_id, - const StreamPrecedenceType& /*precedence*/) override; - - void UnregisterStream(StreamIdType stream_id) override; - - bool StreamRegistered(StreamIdType stream_id) const override { - return registered_streams_.find(stream_id) != registered_streams_.end(); - } - - // Stream precedence is available but note that it is not used for scheduling - // in this scheduler. - StreamPrecedenceType GetStreamPrecedence( - StreamIdType stream_id) const override; - - void UpdateStreamPrecedence(StreamIdType stream_id, - const StreamPrecedenceType& precedence) override; - - std::vector<StreamIdType> GetStreamChildren( - StreamIdType /*stream_id*/) const override { - return std::vector<StreamIdType>(); - } - - void RecordStreamEventTime(StreamIdType stream_id, - int64_t now_in_usec) override; - - int64_t GetLatestEventWithPrecedence(StreamIdType stream_id) const override; - - StreamIdType PopNextReadyStream() override; - - std::tuple<StreamIdType, StreamPrecedenceType> - PopNextReadyStreamAndPrecedence() override { - return std::make_tuple(PopNextReadyStream(), - StreamPrecedenceType(kV3LowestPriority)); - } - - bool ShouldYield(StreamIdType stream_id) const override { - return !ready_streams_.empty() && stream_id < *ready_streams_.rbegin(); - } - - void MarkStreamReady(StreamIdType stream_id, bool /*add_to_front*/) override; - - void MarkStreamNotReady(StreamIdType stream_id) override; - - bool HasReadyStreams() const override { return !ready_streams_.empty(); } - size_t NumReadyStreams() const override { return ready_streams_.size(); } - bool IsStreamReady(StreamIdType stream_id) const override; - size_t NumRegisteredStreams() const override; - std::string DebugString() const override; - - private: - friend class test::LifoWriteSchedulerPeer<StreamIdType>; - - struct StreamInfo { - SpdyPriority priority; - int64_t event_time; // read/write event time (us since Unix epoch). - }; - - std::set<StreamIdType> ready_streams_; - std::map<StreamIdType, StreamInfo> registered_streams_; -}; - -template <typename StreamIdType> -void LifoWriteScheduler<StreamIdType>::RegisterStream( - StreamIdType stream_id, - const StreamPrecedenceType& precedence) { - if (StreamRegistered(stream_id)) { - SPDY_BUG << "Stream " << stream_id << " already registered"; - return; - } - registered_streams_.emplace_hint( - registered_streams_.end(), stream_id, - StreamInfo{/*priority=*/precedence.spdy3_priority(), /*event_time=*/0}); -} - -template <typename StreamIdType> -void LifoWriteScheduler<StreamIdType>::UnregisterStream( - StreamIdType stream_id) { - if (!StreamRegistered(stream_id)) { - SPDY_BUG << "Stream " << stream_id << " is not registered"; - return; - } - registered_streams_.erase(stream_id); - ready_streams_.erase(stream_id); -} - -template <typename StreamIdType> -typename LifoWriteScheduler<StreamIdType>::StreamPrecedenceType -LifoWriteScheduler<StreamIdType>::GetStreamPrecedence( - StreamIdType stream_id) const { - auto it = registered_streams_.find(stream_id); - if (it == registered_streams_.end()) { - SPDY_DVLOG(1) << "Stream " << stream_id << " not registered"; - return StreamPrecedenceType(kV3LowestPriority); - } - return StreamPrecedenceType(it->second.priority); -} - -template <typename StreamIdType> -void LifoWriteScheduler<StreamIdType>::UpdateStreamPrecedence( - StreamIdType stream_id, - const StreamPrecedenceType& precedence) { - auto it = registered_streams_.find(stream_id); - if (it == registered_streams_.end()) { - SPDY_DVLOG(1) << "Stream " << stream_id << " not registered"; - return; - } - it->second.priority = precedence.spdy3_priority(); -} - -template <typename StreamIdType> -void LifoWriteScheduler<StreamIdType>::RecordStreamEventTime( - StreamIdType stream_id, - int64_t now_in_usec) { - auto it = registered_streams_.find(stream_id); - if (it != registered_streams_.end()) { - it->second.event_time = now_in_usec; - } else { - SPDY_BUG << "Stream " << stream_id << " is not registered"; - } -} - -template <typename StreamIdType> -int64_t LifoWriteScheduler<StreamIdType>::GetLatestEventWithPrecedence( - StreamIdType stream_id) const { - if (!StreamRegistered(stream_id)) { - SPDY_BUG << "Stream " << stream_id << " is not registered"; - return 0; - } - int64_t latest_event_time_us = 0; - for (auto it = registered_streams_.rbegin(); it != registered_streams_.rend(); - ++it) { - if (stream_id < it->first) { - if (it->second.event_time > latest_event_time_us) { - latest_event_time_us = it->second.event_time; - } - } else { - break; - } - } - return latest_event_time_us; -} - -template <typename StreamIdType> -StreamIdType LifoWriteScheduler<StreamIdType>::PopNextReadyStream() { - if (ready_streams_.empty()) { - SPDY_BUG << "No ready streams available"; - return 0; - } - auto it = --ready_streams_.end(); - StreamIdType id = *it; - ready_streams_.erase(it); - return id; -} - -template <typename StreamIdType> -void LifoWriteScheduler<StreamIdType>::MarkStreamReady(StreamIdType stream_id, - bool /*add_to_front*/) { - if (!StreamRegistered(stream_id)) { - SPDY_BUG << "Stream " << stream_id << " is not registered"; - return; - } - if (ready_streams_.find(stream_id) != ready_streams_.end()) { - SPDY_VLOG(1) << "Stream already exists in the list"; - return; - } - ready_streams_.insert(stream_id); -} - -template <typename StreamIdType> -void LifoWriteScheduler<StreamIdType>::MarkStreamNotReady( - StreamIdType stream_id) { - auto it = ready_streams_.find(stream_id); - if (it == ready_streams_.end()) { - SPDY_VLOG(1) << "Try to remove a stream that is not on list"; - return; - } - ready_streams_.erase(it); -} - -template <typename StreamIdType> -bool LifoWriteScheduler<StreamIdType>::IsStreamReady( - StreamIdType stream_id) const { - if (!StreamRegistered(stream_id)) { - SPDY_BUG << "Stream " << stream_id << " is not registered"; - return false; - } - return ready_streams_.find(stream_id) != ready_streams_.end(); -} - -template <typename StreamIdType> -size_t LifoWriteScheduler<StreamIdType>::NumRegisteredStreams() const { - return registered_streams_.size(); -} - -template <typename StreamIdType> -std::string LifoWriteScheduler<StreamIdType>::DebugString() const { - return absl::StrCat( - "LifoWriteScheduler {num_streams=", registered_streams_.size(), - " num_ready_streams=", NumReadyStreams(), "}"); -} - -} // namespace spdy - -#endif // QUICHE_SPDY_CORE_LIFO_WRITE_SCHEDULER_H_ diff --git a/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler_test.cc b/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler_test.cc deleted file mode 100644 index c7305e7dac1..00000000000 --- a/chromium/net/third_party/quiche/src/spdy/core/lifo_write_scheduler_test.cc +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "spdy/core/lifo_write_scheduler.h" - -#include "common/platform/api/quiche_test.h" -#include "spdy/core/spdy_protocol.h" -#include "spdy/core/spdy_test_utils.h" -#include "spdy/platform/api/spdy_test_helpers.h" - -namespace spdy { - -namespace test { - -template <typename StreamIdType> -class LifoWriteSchedulerPeer { - public: - explicit LifoWriteSchedulerPeer(LifoWriteScheduler<StreamIdType>* scheduler) - : scheduler_(scheduler) {} - - size_t NumRegisteredListStreams() const { - return scheduler_->registered_streams_.size(); - } - - std::set<StreamIdType>* GetReadyList() const { - return &scheduler_->ready_streams_; - } - - private: - LifoWriteScheduler<StreamIdType>* scheduler_; -}; - -// Test add and remove from ready list. -TEST(LifoWriteSchedulerTest, ReadyListTest) { - LifoWriteScheduler<SpdyStreamId> lifo; - LifoWriteSchedulerPeer<SpdyStreamId> peer(&lifo); - - EXPECT_SPDY_BUG( - EXPECT_EQ(0u, std::get<0>(lifo.PopNextReadyStreamAndPrecedence())), - "No ready streams available"); - EXPECT_SPDY_BUG(EXPECT_EQ(0u, lifo.PopNextReadyStream()), - "No ready streams available"); - EXPECT_FALSE(lifo.HasReadyStreams()); - EXPECT_SPDY_BUG(lifo.MarkStreamReady(9, true), "Stream 9 is not registered"); - EXPECT_SPDY_BUG(lifo.IsStreamReady(9), "Stream 9 is not registered"); - SpdyStreamPrecedence precedence(1); - lifo.RegisterStream(3, precedence); - EXPECT_FALSE(lifo.IsStreamReady(3)); - lifo.RegisterStream(7, precedence); - lifo.RegisterStream(9, precedence); - lifo.RegisterStream(11, precedence); - lifo.RegisterStream(13, precedence); - lifo.RegisterStream(15, precedence); - lifo.RegisterStream(17, precedence); - lifo.MarkStreamReady(9, true); - lifo.MarkStreamReady(15, true); - lifo.MarkStreamReady(7, true); - lifo.MarkStreamReady(13, true); - lifo.MarkStreamReady(11, true); - lifo.MarkStreamReady(3, true); - EXPECT_TRUE(lifo.IsStreamReady(3)); - lifo.MarkStreamReady(17, true); - EXPECT_TRUE(lifo.HasReadyStreams()); - EXPECT_EQ(7u, lifo.NumReadyStreams()); - - // Verify MarkStream(Not)Ready() can be called multiple times for the same - // stream. - lifo.MarkStreamReady(11, true); - lifo.MarkStreamNotReady(5); - lifo.MarkStreamNotReady(21); - - EXPECT_EQ(17u, lifo.PopNextReadyStream()); - EXPECT_EQ(15u, std::get<0>(lifo.PopNextReadyStreamAndPrecedence())); - EXPECT_TRUE(lifo.ShouldYield(9)); - EXPECT_FALSE(lifo.ShouldYield(13)); - EXPECT_FALSE(lifo.ShouldYield(15)); - - lifo.MarkStreamNotReady(3); - EXPECT_TRUE(peer.GetReadyList()->find(3) == peer.GetReadyList()->end()); - lifo.MarkStreamNotReady(13); - EXPECT_TRUE(peer.GetReadyList()->find(13) == peer.GetReadyList()->end()); - lifo.MarkStreamNotReady(7); - EXPECT_TRUE(peer.GetReadyList()->find(7) == peer.GetReadyList()->end()); - EXPECT_EQ(2u, lifo.NumReadyStreams()); - - lifo.MarkStreamNotReady(9); - lifo.MarkStreamNotReady(11); - EXPECT_FALSE(lifo.ShouldYield(1)); -} - -// Test add and remove from registered list. -TEST(LifoWriteSchedulerTest, RegisterListTest) { - LifoWriteScheduler<SpdyStreamId> lifo; - LifoWriteSchedulerPeer<SpdyStreamId> peer(&lifo); - SpdyStreamPrecedence precedence(1); - EXPECT_EQ(0u, lifo.NumRegisteredStreams()); - lifo.RegisterStream(3, precedence); - lifo.RegisterStream(5, precedence); - lifo.RegisterStream(7, precedence); - lifo.RegisterStream(9, precedence); - lifo.RegisterStream(11, precedence); - EXPECT_EQ(5u, lifo.NumRegisteredStreams()); - - EXPECT_TRUE(lifo.StreamRegistered(3)); - EXPECT_TRUE(lifo.StreamRegistered(5)); - EXPECT_TRUE(lifo.StreamRegistered(7)); - EXPECT_TRUE(lifo.StreamRegistered(9)); - EXPECT_TRUE(lifo.StreamRegistered(11)); - EXPECT_SPDY_BUG(lifo.RegisterStream(11, precedence), - "Stream 11 already registered"); - EXPECT_EQ(5u, peer.NumRegisteredListStreams()); - - lifo.UnregisterStream(3); - EXPECT_EQ(4u, lifo.NumRegisteredStreams()); - EXPECT_FALSE(lifo.StreamRegistered(3)); - EXPECT_SPDY_BUG(lifo.UnregisterStream(3), "Stream 3 is not registered"); - EXPECT_SPDY_BUG(lifo.UnregisterStream(13), "Stream 13 is not registered"); - lifo.UnregisterStream(11); - EXPECT_FALSE(lifo.StreamRegistered(11)); - lifo.UnregisterStream(7); - EXPECT_EQ(2u, lifo.NumRegisteredStreams()); - EXPECT_FALSE(lifo.StreamRegistered(7)); - EXPECT_TRUE(lifo.StreamRegistered(5)); - EXPECT_TRUE(lifo.StreamRegistered(9)); -} - -// Test mark latest event time. -TEST(LifoWriteSchedulerTest, GetLatestEventTest) { - LifoWriteScheduler<SpdyStreamId> lifo; - LifoWriteSchedulerPeer<SpdyStreamId> peer(&lifo); - SpdyStreamPrecedence precedence(1); - lifo.RegisterStream(1, precedence); - lifo.RegisterStream(3, precedence); - lifo.RegisterStream(5, precedence); - lifo.RegisterStream(7, precedence); - lifo.RegisterStream(9, precedence); - lifo.RecordStreamEventTime(1, 1); - lifo.RecordStreamEventTime(3, 8); - lifo.RecordStreamEventTime(5, 4); - lifo.RecordStreamEventTime(7, 2); - lifo.RecordStreamEventTime(9, 3); - EXPECT_SPDY_BUG(lifo.RecordStreamEventTime(11, 1), - "Stream 11 is not registered"); - EXPECT_EQ(0, lifo.GetLatestEventWithPrecedence(9)); - EXPECT_EQ(3, lifo.GetLatestEventWithPrecedence(7)); - EXPECT_EQ(3, lifo.GetLatestEventWithPrecedence(5)); - EXPECT_EQ(4, lifo.GetLatestEventWithPrecedence(3)); - EXPECT_EQ(8, lifo.GetLatestEventWithPrecedence(1)); - EXPECT_SPDY_BUG(lifo.GetLatestEventWithPrecedence(11), - "Stream 11 is not registered"); -} - -TEST(LifoWriteSchedulerTest, GetStreamPrecedence) { - LifoWriteScheduler<SpdyStreamId> lifo; - // Return lowest priority for unknown stream. - EXPECT_EQ(kV3LowestPriority, lifo.GetStreamPrecedence(1).spdy3_priority()); - - lifo.RegisterStream(1, SpdyStreamPrecedence(3)); - EXPECT_TRUE(lifo.GetStreamPrecedence(1).is_spdy3_priority()); - EXPECT_EQ(3, lifo.GetStreamPrecedence(1).spdy3_priority()); - - // Redundant registration shouldn't change stream priority. - EXPECT_SPDY_BUG(lifo.RegisterStream(1, SpdyStreamPrecedence(4)), - "Stream 1 already registered"); - EXPECT_EQ(3, lifo.GetStreamPrecedence(1).spdy3_priority()); - - lifo.UpdateStreamPrecedence(1, SpdyStreamPrecedence(5)); - EXPECT_EQ(5, lifo.GetStreamPrecedence(1).spdy3_priority()); -} - -} // namespace test - -} // namespace spdy diff --git a/chromium/net/third_party/quiche/src/spdy/core/priority_write_scheduler.h b/chromium/net/third_party/quiche/src/spdy/core/priority_write_scheduler.h index c6824c67bd1..fde9a286ed5 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/priority_write_scheduler.h +++ b/chromium/net/third_party/quiche/src/spdy/core/priority_write_scheduler.h @@ -8,6 +8,7 @@ #include <algorithm> #include <cstddef> #include <cstdint> +#include <deque> #include <string> #include <tuple> #include <unordered_map> @@ -15,7 +16,6 @@ #include <vector> #include "absl/strings/str_cat.h" -#include "http2/platform/api/http2_containers.h" #include "spdy/core/spdy_protocol.h" #include "spdy/core/write_scheduler.h" #include "spdy/platform/api/spdy_bug_tracker.h" @@ -61,19 +61,21 @@ class PriorityWriteScheduler : public WriteScheduler<StreamIdType> { << "Parent stream " << parent_id << " not registered"; if (stream_id == root_stream_id_) { - SPDY_BUG << "Stream " << root_stream_id_ << " already registered"; + SPDY_BUG(spdy_bug_19_1) + << "Stream " << root_stream_id_ << " already registered"; return; } StreamInfo stream_info = {precedence.spdy3_priority(), stream_id, false}; bool inserted = stream_infos_.insert(std::make_pair(stream_id, stream_info)).second; - SPDY_BUG_IF(!inserted) << "Stream " << stream_id << " already registered"; + SPDY_BUG_IF(spdy_bug_19_2, !inserted) + << "Stream " << stream_id << " already registered"; } void UnregisterStream(StreamIdType stream_id) override { auto it = stream_infos_.find(stream_id); if (it == stream_infos_.end()) { - SPDY_BUG << "Stream " << stream_id << " not registered"; + SPDY_BUG(spdy_bug_19_3) << "Stream " << stream_id << " not registered"; return; } StreamInfo& stream_info = it->second; @@ -141,7 +143,7 @@ class PriorityWriteScheduler : public WriteScheduler<StreamIdType> { int64_t now_in_usec) override { auto it = stream_infos_.find(stream_id); if (it == stream_infos_.end()) { - SPDY_BUG << "Stream " << stream_id << " not registered"; + SPDY_BUG(spdy_bug_19_4) << "Stream " << stream_id << " not registered"; return; } PriorityInfo& priority_info = priority_infos_[it->second.priority]; @@ -152,7 +154,7 @@ class PriorityWriteScheduler : public WriteScheduler<StreamIdType> { int64_t GetLatestEventWithPrecedence(StreamIdType stream_id) const override { auto it = stream_infos_.find(stream_id); if (it == stream_infos_.end()) { - SPDY_BUG << "Stream " << stream_id << " not registered"; + SPDY_BUG(spdy_bug_19_5) << "Stream " << stream_id << " not registered"; return 0; } int64_t last_event_time_usec = 0; @@ -185,14 +187,14 @@ class PriorityWriteScheduler : public WriteScheduler<StreamIdType> { StreamPrecedenceType(info->priority)); } } - SPDY_BUG << "No ready streams available"; + SPDY_BUG(spdy_bug_19_6) << "No ready streams available"; return std::make_tuple(0, StreamPrecedenceType(kV3LowestPriority)); } bool ShouldYield(StreamIdType stream_id) const override { auto it = stream_infos_.find(stream_id); if (it == stream_infos_.end()) { - SPDY_BUG << "Stream " << stream_id << " not registered"; + SPDY_BUG(spdy_bug_19_7) << "Stream " << stream_id << " not registered"; return false; } @@ -219,7 +221,7 @@ class PriorityWriteScheduler : public WriteScheduler<StreamIdType> { void MarkStreamReady(StreamIdType stream_id, bool add_to_front) override { auto it = stream_infos_.find(stream_id); if (it == stream_infos_.end()) { - SPDY_BUG << "Stream " << stream_id << " not registered"; + SPDY_BUG(spdy_bug_19_8) << "Stream " << stream_id << " not registered"; return; } StreamInfo& stream_info = it->second; @@ -239,7 +241,7 @@ class PriorityWriteScheduler : public WriteScheduler<StreamIdType> { void MarkStreamNotReady(StreamIdType stream_id) override { auto it = stream_infos_.find(stream_id); if (it == stream_infos_.end()) { - SPDY_BUG << "Stream " << stream_id << " not registered"; + SPDY_BUG(spdy_bug_19_9) << "Stream " << stream_id << " not registered"; return; } StreamInfo& stream_info = it->second; @@ -288,7 +290,7 @@ class PriorityWriteScheduler : public WriteScheduler<StreamIdType> { }; // O(1) size lookup, O(1) insert at front or back (amortized). - using ReadyList = http2::Http2Deque<StreamInfo*>; + using ReadyList = std::deque<StreamInfo*>; // State kept for each priority level. struct PriorityInfo { 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 4f6b4f3ecfa..5b6af98a9ac 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 @@ -265,15 +265,14 @@ std::string SpdyAltSvcWireFormat::SerializeHeaderFieldValue( } value.push_back(c); } - value.append(absl::StrCat(":", altsvc.port, "\"")); + absl::StrAppend(&value, ":", altsvc.port, "\""); if (altsvc.max_age != 86400) { - value.append(absl::StrCat("; ma=", altsvc.max_age)); + absl::StrAppend(&value, "; ma=", altsvc.max_age); } if (!altsvc.version.empty()) { if (is_ietf_format_quic) { for (uint32_t quic_version : altsvc.version) { - value.append("; quic="); - value.append(SpdyHexEncodeUInt32AndTrim(quic_version)); + absl::StrAppend(&value, "; quic=", absl::Hex(quic_version)); } } else { value.append("; v=\""); @@ -282,7 +281,7 @@ std::string SpdyAltSvcWireFormat::SerializeHeaderFieldValue( if (it != altsvc.version.begin()) { value.append(","); } - value.append(absl::StrCat(*it)); + absl::StrAppend(&value, *it); } value.append("\""); } 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 d8d23dfe9ce..fccda75ac6e 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 @@ -14,9 +14,9 @@ #include <string> #include <vector> +#include "absl/container/inlined_vector.h" #include "absl/strings/string_view.h" #include "common/platform/api/quiche_export.h" -#include "spdy/platform/api/spdy_containers.h" namespace spdy { @@ -26,7 +26,7 @@ class SpdyAltSvcWireFormatPeer; class QUICHE_EXPORT_PRIVATE SpdyAltSvcWireFormat { public: - using VersionVector = SpdyInlinedVector<uint32_t, 8>; + using VersionVector = absl::InlinedVector<uint32_t, 8>; struct QUICHE_EXPORT_PRIVATE AlternativeService { std::string protocol_id; diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.cc index 5c93128d9d6..ee63594ca6c 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.cc @@ -69,8 +69,9 @@ bool SpdyFrameBuilder::BeginNewFrame(SpdyFrameType type, QUICHE_DCHECK_EQ(0u, stream_id & ~kStreamIdMask); bool success = true; if (length_ > 0) { - SPDY_BUG << "SpdyFrameBuilder doesn't have a clean state when BeginNewFrame" - << "is called. Leftover length_ is " << length_; + SPDY_BUG(spdy_bug_73_1) + << "SpdyFrameBuilder doesn't have a clean state when BeginNewFrame" + << "is called. Leftover length_ is " << length_; offset_ += length_; length_ = 0; } @@ -90,7 +91,7 @@ bool SpdyFrameBuilder::BeginNewFrame(SpdyFrameType type, uint8_t raw_frame_type = SerializeFrameType(type); QUICHE_DCHECK(IsDefinedFrameType(raw_frame_type)); QUICHE_DCHECK_EQ(0u, stream_id & ~kStreamIdMask); - SPDY_BUG_IF(length > kHttp2DefaultFramePayloadLimit) + SPDY_BUG_IF(spdy_bug_73_2, length > kHttp2DefaultFramePayloadLimit) << "Frame length " << length_ << " is longer than frame size limit."; return BeginNewFrameInternal(raw_frame_type, flags, stream_id, length); } diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.h index b18e9a0a18f..01fce6be819 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.h +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.h @@ -66,9 +66,10 @@ class QUICHE_EXPORT_PRIVATE SpdyFrameBuilder { // Takes the buffer from the SpdyFrameBuilder. SpdySerializedFrame take() { - SPDY_BUG_IF(output_ != nullptr) << "ZeroCopyOutputBuffer is used to build " - << "frames. take() shouldn't be called"; - SPDY_BUG_IF(kMaxFrameSizeLimit < length_) + SPDY_BUG_IF(spdy_bug_39_1, output_ != nullptr) + << "ZeroCopyOutputBuffer is used to build " + << "frames. take() shouldn't be called"; + SPDY_BUG_IF(spdy_bug_39_2, kMaxFrameSizeLimit < length_) << "Frame length " << length_ << " is longer than the maximum possible allowed length."; SpdySerializedFrame rv(buffer_.release(), length(), true); 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 6b850c2987d..bc7cd1767a0 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 @@ -294,8 +294,9 @@ SpdyFramer::SpdyFrameIterator::~SpdyFrameIterator() = default; size_t SpdyFramer::SpdyFrameIterator::NextFrame(ZeroCopyOutputBuffer* output) { const SpdyFrameIR& frame_ir = GetIR(); if (!has_next_frame_) { - SPDY_BUG << "SpdyFramer::SpdyFrameIterator::NextFrame called without " - << "a next frame."; + SPDY_BUG(spdy_bug_75_1) + << "SpdyFramer::SpdyFrameIterator::NextFrame called without " + << "a next frame."; return false; } @@ -406,7 +407,6 @@ const SpdyFrameIR& SpdyFramer::SpdyControlFrameIterator::GetIR() const { return *(frame_ir_.get()); } -// TODO(yasong): remove all the static_casts. std::unique_ptr<SpdyFrameSequence> SpdyFramer::CreateIterator( SpdyFramer* framer, std::unique_ptr<const SpdyFrameIR> frame_ir) { @@ -418,8 +418,8 @@ std::unique_ptr<SpdyFrameSequence> SpdyFramer::CreateIterator( } case SpdyFrameType::PUSH_PROMISE: { return std::make_unique<SpdyPushPromiseFrameIterator>( - framer, absl::WrapUnique( - static_cast<const SpdyPushPromiseIR*>(frame_ir.release()))); + framer, absl::WrapUnique(static_cast<const SpdyPushPromiseIR*>( + frame_ir.release()))); } case SpdyFrameType::DATA: { SPDY_DVLOG(1) << "Serialize a stream end DATA frame for VTL"; @@ -1379,11 +1379,6 @@ size_t SpdyFramer::header_encoder_table_size() const { } } -void SpdyFramer::SetEncoderHeaderTableDebugVisitor( - std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor) { - GetHpackEncoder()->SetHeaderTableDebugVisitor(std::move(visitor)); -} - size_t SpdyFramer::EstimateMemoryUsage() const { return SpdyEstimateMemoryUsage(hpack_encoder_); } diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.h index d7a81c10a21..4e3dc35f1d3 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.h +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_framer.h @@ -233,9 +233,6 @@ class QUICHE_EXPORT_PRIVATE SpdyFramer { // Returns the maximum size of the header encoder compression table. size_t header_encoder_table_size() const; - void SetEncoderHeaderTableDebugVisitor( - std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor); - // Get (and lazily initialize) the HPACK encoder state. HpackEncoder* GetHpackEncoder(); 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 96b9f4c4258..d7fb6a7a332 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 @@ -4570,12 +4570,7 @@ TEST_P(SpdyFramerTest, ReadPriorityUpdateFrame) { testing::StrictMock<test::MockSpdyFramerVisitor> visitor; deframer_.set_visitor(&visitor); - if (GetHttp2RestartFlag(http2_parse_priority_update_frame)) { - EXPECT_CALL(visitor, OnPriorityUpdate(3, "foo")); - } else { - EXPECT_CALL(visitor, OnUnknownFrame(0, _)).WillOnce(testing::Return(true)); - } - + EXPECT_CALL(visitor, OnPriorityUpdate(3, "foo")); deframer_.ProcessInput(kFrameData, sizeof(kFrameData)); EXPECT_FALSE(deframer_.HasError()); } @@ -4592,12 +4587,7 @@ TEST_P(SpdyFramerTest, ReadPriorityUpdateFrameWithEmptyPriorityFieldValue) { testing::StrictMock<test::MockSpdyFramerVisitor> visitor; deframer_.set_visitor(&visitor); - if (GetHttp2RestartFlag(http2_parse_priority_update_frame)) { - EXPECT_CALL(visitor, OnPriorityUpdate(3, "")); - } else { - EXPECT_CALL(visitor, OnUnknownFrame(0, _)).WillOnce(testing::Return(true)); - } - + EXPECT_CALL(visitor, OnPriorityUpdate(3, "")); deframer_.ProcessInput(kFrameData, sizeof(kFrameData)); EXPECT_FALSE(deframer_.HasError()); } @@ -4613,17 +4603,10 @@ TEST_P(SpdyFramerTest, PriorityUpdateFrameWithEmptyPayload) { testing::StrictMock<test::MockSpdyFramerVisitor> visitor; deframer_.set_visitor(&visitor); - if (GetHttp2RestartFlag(http2_parse_priority_update_frame)) { - EXPECT_CALL( - visitor, - OnError(Http2DecoderAdapter::SPDY_INVALID_CONTROL_FRAME_SIZE, _)); - deframer_.ProcessInput(kFrameData, sizeof(kFrameData)); - EXPECT_TRUE(deframer_.HasError()); - } else { - EXPECT_CALL(visitor, OnUnknownFrame(0, _)).WillOnce(testing::Return(true)); - deframer_.ProcessInput(kFrameData, sizeof(kFrameData)); - EXPECT_FALSE(deframer_.HasError()); - } + EXPECT_CALL(visitor, + OnError(Http2DecoderAdapter::SPDY_INVALID_CONTROL_FRAME_SIZE, _)); + deframer_.ProcessInput(kFrameData, sizeof(kFrameData)); + EXPECT_TRUE(deframer_.HasError()); } TEST_P(SpdyFramerTest, PriorityUpdateFrameWithShortPayload) { @@ -4639,17 +4622,10 @@ TEST_P(SpdyFramerTest, PriorityUpdateFrameWithShortPayload) { testing::StrictMock<test::MockSpdyFramerVisitor> visitor; deframer_.set_visitor(&visitor); - if (GetHttp2RestartFlag(http2_parse_priority_update_frame)) { - EXPECT_CALL( - visitor, - OnError(Http2DecoderAdapter::SPDY_INVALID_CONTROL_FRAME_SIZE, _)); - deframer_.ProcessInput(kFrameData, sizeof(kFrameData)); - EXPECT_TRUE(deframer_.HasError()); - } else { - EXPECT_CALL(visitor, OnUnknownFrame(0, _)).WillOnce(testing::Return(true)); - deframer_.ProcessInput(kFrameData, sizeof(kFrameData)); - EXPECT_FALSE(deframer_.HasError()); - } + EXPECT_CALL(visitor, + OnError(Http2DecoderAdapter::SPDY_INVALID_CONTROL_FRAME_SIZE, _)); + deframer_.ProcessInput(kFrameData, sizeof(kFrameData)); + EXPECT_TRUE(deframer_.HasError()); } TEST_P(SpdyFramerTest, PriorityUpdateFrameOnIncorrectStream) { @@ -4664,16 +4640,9 @@ TEST_P(SpdyFramerTest, PriorityUpdateFrameOnIncorrectStream) { testing::StrictMock<test::MockSpdyFramerVisitor> visitor; deframer_.set_visitor(&visitor); - if (GetHttp2RestartFlag(http2_parse_priority_update_frame)) { - EXPECT_CALL(visitor, - OnError(Http2DecoderAdapter::SPDY_INVALID_STREAM_ID, _)); - deframer_.ProcessInput(kFrameData, sizeof(kFrameData)); - EXPECT_TRUE(deframer_.HasError()); - } else { - EXPECT_CALL(visitor, OnUnknownFrame(1, _)).WillOnce(testing::Return(true)); - deframer_.ProcessInput(kFrameData, sizeof(kFrameData)); - EXPECT_FALSE(deframer_.HasError()); - } + EXPECT_CALL(visitor, OnError(Http2DecoderAdapter::SPDY_INVALID_STREAM_ID, _)); + deframer_.ProcessInput(kFrameData, sizeof(kFrameData)); + EXPECT_TRUE(deframer_.HasError()); } TEST_P(SpdyFramerTest, PriorityUpdateFramePrioritizingIncorrectStream) { @@ -4688,16 +4657,9 @@ TEST_P(SpdyFramerTest, PriorityUpdateFramePrioritizingIncorrectStream) { testing::StrictMock<test::MockSpdyFramerVisitor> visitor; deframer_.set_visitor(&visitor); - if (GetHttp2RestartFlag(http2_parse_priority_update_frame)) { - EXPECT_CALL(visitor, - OnError(Http2DecoderAdapter::SPDY_INVALID_STREAM_ID, _)); - deframer_.ProcessInput(kFrameData, sizeof(kFrameData)); - EXPECT_TRUE(deframer_.HasError()); - } else { - EXPECT_CALL(visitor, OnUnknownFrame(0, _)).WillOnce(testing::Return(true)); - deframer_.ProcessInput(kFrameData, sizeof(kFrameData)); - EXPECT_FALSE(deframer_.HasError()); - } + EXPECT_CALL(visitor, OnError(Http2DecoderAdapter::SPDY_INVALID_STREAM_ID, _)); + deframer_.ProcessInput(kFrameData, sizeof(kFrameData)); + EXPECT_TRUE(deframer_.HasError()); } // Tests handling of PRIORITY frames. diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.cc index 93ae28f1273..59a9069a220 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.cc @@ -9,9 +9,9 @@ #include <algorithm> #include <utility> +#include "absl/strings/str_cat.h" #include "spdy/platform/api/spdy_estimate_memory_usage.h" #include "spdy/platform/api/spdy_logging.h" -#include "spdy/platform/api/spdy_string_utils.h" namespace spdy { namespace { @@ -223,9 +223,9 @@ std::string Http2HeaderBlock::DebugString() const { std::string output = "\n{\n"; for (auto it = begin(); it != end(); ++it) { - SpdyStrAppend(&output, " ", it->first, " ", it->second, "\n"); + absl::StrAppend(&output, " ", it->first, " ", it->second, "\n"); } - SpdyStrAppend(&output, "}\n"); + absl::StrAppend(&output, "}\n"); return output; } diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.h index 76ec4363678..46584191461 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.h +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.h @@ -14,12 +14,14 @@ #include <vector> #include "absl/base/attributes.h" +#include "absl/hash/hash.h" +#include "absl/strings/ascii.h" +#include "absl/strings/match.h" #include "absl/strings/string_view.h" #include "common/platform/api/quiche_export.h" #include "common/platform/api/quiche_logging.h" #include "spdy/core/spdy_header_storage.h" #include "spdy/platform/api/spdy_containers.h" -#include "spdy/platform/api/spdy_string_utils.h" namespace spdy { @@ -92,10 +94,24 @@ class QUICHE_EXPORT_PRIVATE Http2HeaderBlock { size_t separator_size_ = 0; }; + struct StringPieceCaseHash { + size_t operator()(absl::string_view data) const { + std::string lower = absl::AsciiStrToLower(data); + absl::Hash<absl::string_view> hasher; + return hasher(lower); + } + }; + + struct StringPieceCaseEqual { + bool operator()(absl::string_view piece1, absl::string_view piece2) const { + return absl::EqualsIgnoreCase(piece1, piece2); + } + }; + typedef SpdyLinkedHashMap<absl::string_view, HeaderValue, - SpdyStringPieceCaseHash, - SpdyStringPieceCaseEq> + StringPieceCaseHash, + StringPieceCaseEqual> MapType; public: diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.cc index 9a5c2291590..ea90b6500ed 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.cc +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.cc @@ -9,7 +9,6 @@ #include "absl/strings/str_cat.h" #include "spdy/platform/api/spdy_bug_tracker.h" -#include "spdy/platform/api/spdy_string_utils.h" namespace spdy { @@ -29,7 +28,8 @@ SpdyPriority ClampSpdy3Priority(SpdyPriority priority) { "The value of given priority shouldn't be smaller than highest " "priority. Check this invariant explicitly."); if (priority > kV3LowestPriority) { - SPDY_BUG << "Invalid priority: " << static_cast<int>(priority); + SPDY_BUG(spdy_bug_22_1) + << "Invalid priority: " << static_cast<int>(priority); return kV3LowestPriority; } return priority; @@ -37,11 +37,11 @@ SpdyPriority ClampSpdy3Priority(SpdyPriority priority) { int ClampHttp2Weight(int weight) { if (weight < kHttp2MinStreamWeight) { - SPDY_BUG << "Invalid weight: " << weight; + SPDY_BUG(spdy_bug_22_2) << "Invalid weight: " << weight; return kHttp2MinStreamWeight; } if (weight > kHttp2MaxStreamWeight) { - SPDY_BUG << "Invalid weight: " << weight; + SPDY_BUG(spdy_bug_22_3) << "Invalid weight: " << weight; return kHttp2MaxStreamWeight; } return weight; @@ -92,7 +92,7 @@ bool IsDefinedFrameType(uint8_t frame_type_field) { } SpdyFrameType ParseFrameType(uint8_t frame_type_field) { - SPDY_BUG_IF(!IsDefinedFrameType(frame_type_field)) + SPDY_BUG_IF(spdy_bug_22_4, !IsDefinedFrameType(frame_type_field)) << "Frame type not defined: " << static_cast<int>(frame_type_field); return static_cast<SpdyFrameType>(frame_type_field); } @@ -191,8 +191,7 @@ bool ParseSettingsId(SpdySettingsId wire_setting_id, std::string SettingsIdToString(SpdySettingsId id) { SpdyKnownSettingsId known_id; if (!ParseSettingsId(id, &known_id)) { - return absl::StrCat("SETTINGS_UNKNOWN_", - SpdyHexEncodeUInt32AndTrim(uint32_t{id})); + return absl::StrCat("SETTINGS_UNKNOWN_", absl::Hex(uint32_t{id})); } switch (known_id) { @@ -216,8 +215,7 @@ std::string SettingsIdToString(SpdySettingsId id) { return "SETTINGS_EXPERIMENT_SCHEDULER"; } - return absl::StrCat("SETTINGS_UNKNOWN_", - SpdyHexEncodeUInt32AndTrim(uint32_t{id})); + return absl::StrCat("SETTINGS_UNKNOWN_", absl::Hex(uint32_t{id})); } SpdyErrorCode ParseErrorCode(uint32_t wire_error_code) { diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.h index fcd6adad15f..d4a671b8933 100644 --- a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.h +++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.h @@ -1039,6 +1039,12 @@ class QUICHE_EXPORT_PRIVATE SpdySerializedFrame { // Returns the actual size of the underlying buffer. size_t size() const { return size_; } + operator absl::string_view() const { + return absl::string_view{frame_, size_}; + } + + operator std::string() const { return std::string{frame_, size_}; } + // Returns a buffer containing the contents of the frame, of which the caller // takes ownership, and clears this SpdySerializedFrame. char* ReleaseBuffer() { diff --git a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_bug_tracker.h b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_bug_tracker.h index d750a21f976..f00bbc8b376 100644 --- a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_bug_tracker.h +++ b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_bug_tracker.h @@ -8,7 +8,8 @@ #include "net/spdy/platform/impl/spdy_bug_tracker_impl.h" #define SPDY_BUG SPDY_BUG_IMPL -#define SPDY_BUG_IF(condition) SPDY_BUG_IF_IMPL(condition) +#define SPDY_BUG_IF(bug_id, condition) SPDY_BUG_IF_IMPL(bug_id, condition) + #define FLAGS_spdy_always_log_bugs_for_tests \ FLAGS_spdy_always_log_bugs_for_tests_impl diff --git a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_containers.h b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_containers.h index a1a00e67e2c..9e3813f5b42 100644 --- a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_containers.h +++ b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_containers.h @@ -9,27 +9,10 @@ namespace spdy { -template <typename KeyType> -using SpdyHash = SpdyHashImpl<KeyType>; - -// SpdyHashMap does not guarantee pointer stability. -template <typename KeyType, - typename ValueType, - typename Hash = SpdyHash<KeyType>> -using SpdyHashMap = SpdyHashMapImpl<KeyType, ValueType, Hash>; - -// SpdyHashSet does not guarantee pointer stability. -template <typename ElementType, typename Hasher, typename Eq> -using SpdyHashSet = SpdyHashSetImpl<ElementType, Hasher, Eq>; - // A map which offers insertion-ordered iteration. template <typename Key, typename Value, typename Hash, typename Eq> using SpdyLinkedHashMap = SpdyLinkedHashMapImpl<Key, Value, Hash, Eq>; -// A vector optimized for small sizes. Provides the same APIs as a std::vector. -template <typename T, size_t N, typename A = std::allocator<T>> -using SpdyInlinedVector = SpdyInlinedVectorImpl<T, N, A>; - // Used for maps that are typically small, then it is faster than (for example) // hash_map which is optimized for large data sets. SpdySmallMap upgrades itself // automatically to a SpdySmallMapImpl-specified map when it runs out of space. diff --git a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_mem_slice.h b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_mem_slice.h deleted file mode 100644 index 8288f33e21c..00000000000 --- a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_mem_slice.h +++ /dev/null @@ -1,54 +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_SPDY_PLATFORM_API_SPDY_MEM_SLICE_H_ -#define QUICHE_SPDY_PLATFORM_API_SPDY_MEM_SLICE_H_ - -#include <utility> - -#include "common/platform/api/quiche_export.h" -#include "net/spdy/platform/impl/spdy_mem_slice_impl.h" - -namespace spdy { - -// SpdyMemSlice is an internally reference counted data buffer used as the -// source buffers for write operations. SpdyMemSlice implicitly maintains a -// reference count and will free the underlying data buffer when the reference -// count reaches zero. -class QUICHE_EXPORT_PRIVATE SpdyMemSlice { - public: - // Constructs an empty SpdyMemSlice with no underlying data and 0 reference - // count. - SpdyMemSlice() = default; - - // Constructs a SpdyMemSlice with reference count 1 to a newly allocated data - // buffer of |length| bytes. - explicit SpdyMemSlice(size_t length) : impl_(length) {} - - // Constructs a SpdyMemSlice from |impl|. It takes the reference away from - // |impl|. - explicit SpdyMemSlice(SpdyMemSliceImpl impl) : impl_(std::move(impl)) {} - - SpdyMemSlice(const SpdyMemSlice& other) = delete; - SpdyMemSlice& operator=(const SpdyMemSlice& other) = delete; - - // Move constructors. |other| will not hold a reference to the data buffer - // after this call completes. - SpdyMemSlice(SpdyMemSlice&& other) = default; - SpdyMemSlice& operator=(SpdyMemSlice&& other) = default; - - ~SpdyMemSlice() = default; - - // Returns a char pointer to underlying data buffer. - const char* data() const { return impl_.data(); } - // Returns the length of underlying data buffer. - size_t length() const { return impl_.length(); } - - private: - SpdyMemSliceImpl impl_; -}; - -} // namespace spdy - -#endif // QUICHE_SPDY_PLATFORM_API_SPDY_MEM_SLICE_H_ diff --git a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_mem_slice_test.cc b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_mem_slice_test.cc deleted file mode 100644 index 0efb7404593..00000000000 --- a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_mem_slice_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 "spdy/platform/api/spdy_mem_slice.h" - -#include <utility> - -#include "common/platform/api/quiche_test.h" - -namespace spdy { -namespace test { -namespace { - -class SpdyMemSliceTest : public QuicheTest { - public: - SpdyMemSliceTest() { - slice_ = SpdyMemSlice(1024); - orig_data_ = slice_.data(); - orig_length_ = slice_.length(); - } - - SpdyMemSlice slice_; - const char* orig_data_; - size_t orig_length_; -}; - -TEST_F(SpdyMemSliceTest, MoveConstruct) { - SpdyMemSlice moved(std::move(slice_)); - EXPECT_EQ(moved.data(), orig_data_); - EXPECT_EQ(moved.length(), orig_length_); - EXPECT_EQ(nullptr, slice_.data()); - EXPECT_EQ(0u, slice_.length()); -} - -TEST_F(SpdyMemSliceTest, MoveAssign) { - SpdyMemSlice moved; - moved = std::move(slice_); - EXPECT_EQ(moved.data(), orig_data_); - EXPECT_EQ(moved.length(), orig_length_); - EXPECT_EQ(nullptr, slice_.data()); - EXPECT_EQ(0u, slice_.length()); -} - -} // namespace -} // namespace test -} // namespace spdy diff --git a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h index 81a0866a11f..178415b0ed6 100644 --- a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h +++ b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h @@ -8,22 +8,11 @@ #include <string> #include <utility> -// The following header file has to be included from at least -// non-test file in order to avoid strange linking errors. -// TODO(bnc): Remove this include as soon as it is included elsewhere in -// non-test code. -#include "spdy/platform/api/spdy_mem_slice.h" - #include "absl/strings/string_view.h" #include "net/spdy/platform/impl/spdy_string_utils_impl.h" namespace spdy { -template <typename... Args> -inline void SpdyStrAppend(std::string* output, const Args&... args) { - SpdyStrAppendImpl(output, std::forward<const Args&>(args)...); -} - inline char SpdyHexDigitToInt(char c) { return SpdyHexDigitToIntImpl(c); } @@ -40,18 +29,10 @@ inline std::string SpdyHexEncode(const char* bytes, size_t size) { return SpdyHexEncodeImpl(bytes, size); } -inline std::string SpdyHexEncodeUInt32AndTrim(uint32_t data) { - return SpdyHexEncodeUInt32AndTrimImpl(data); -} - inline std::string SpdyHexDump(absl::string_view data) { return SpdyHexDumpImpl(data); } -using SpdyStringPieceCaseHash = SpdyStringPieceCaseHashImpl; - -using SpdyStringPieceCaseEq = SpdyStringPieceCaseEqImpl; - } // namespace spdy #endif // QUICHE_SPDY_PLATFORM_API_SPDY_STRING_UTILS_H_ diff --git a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils_test.cc b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils_test.cc index 2964876ddc7..cb73a9b2ece 100644 --- a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils_test.cc +++ b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_string_utils_test.cc @@ -13,101 +13,6 @@ namespace spdy { namespace test { namespace { -TEST(SpdyStringUtilsTest, SpdyStrAppend) { - // No arguments on empty string. - std::string output; - SpdyStrAppend(&output); - EXPECT_TRUE(output.empty()); - - // Single string-like argument. - const char kFoo[] = "foo"; - const std::string string_foo(kFoo); - const absl::string_view stringpiece_foo(string_foo); - SpdyStrAppend(&output, kFoo); - EXPECT_EQ("foo", output); - SpdyStrAppend(&output, string_foo); - EXPECT_EQ("foofoo", output); - SpdyStrAppend(&output, stringpiece_foo); - EXPECT_EQ("foofoofoo", output); - - // No arguments on non-empty string. - SpdyStrAppend(&output); - EXPECT_EQ("foofoofoo", output); - - output.clear(); - - // Two string-like arguments. - const char kBar[] = "bar"; - const absl::string_view stringpiece_bar(kBar); - const std::string string_bar(kBar); - SpdyStrAppend(&output, kFoo, kBar); - EXPECT_EQ("foobar", output); - SpdyStrAppend(&output, kFoo, string_bar); - EXPECT_EQ("foobarfoobar", output); - SpdyStrAppend(&output, kFoo, stringpiece_bar); - EXPECT_EQ("foobarfoobarfoobar", output); - SpdyStrAppend(&output, string_foo, kBar); - EXPECT_EQ("foobarfoobarfoobarfoobar", output); - - output.clear(); - - SpdyStrAppend(&output, string_foo, string_bar); - EXPECT_EQ("foobar", output); - SpdyStrAppend(&output, string_foo, stringpiece_bar); - EXPECT_EQ("foobarfoobar", output); - SpdyStrAppend(&output, stringpiece_foo, kBar); - EXPECT_EQ("foobarfoobarfoobar", output); - SpdyStrAppend(&output, stringpiece_foo, string_bar); - EXPECT_EQ("foobarfoobarfoobarfoobar", output); - - output.clear(); - - SpdyStrAppend(&output, stringpiece_foo, stringpiece_bar); - EXPECT_EQ("foobar", output); - - // Many-many arguments. - SpdyStrAppend(&output, "foo", "bar", "baz", "qux", "quux", "quuz", "corge", - "grault", "garply", "waldo", "fred", "plugh", "xyzzy", "thud"); - EXPECT_EQ( - "foobarfoobarbazquxquuxquuzcorgegraultgarplywaldofredplughxyzzythud", - output); - - output.clear(); - - // Numerical arguments. - const int16_t i = 1; - const uint64_t u = 8; - const double d = 3.1415; - - SpdyStrAppend(&output, i, " ", u); - EXPECT_EQ("1 8", output); - SpdyStrAppend(&output, d, i, i, u, i); - EXPECT_EQ("1 83.14151181", output); - SpdyStrAppend(&output, "i: ", i, ", u: ", u, ", d: ", d); - EXPECT_EQ("1 83.14151181i: 1, u: 8, d: 3.1415", output); - - output.clear(); - - // Boolean arguments. - const bool t = true; - const bool f = false; - - SpdyStrAppend(&output, t); - EXPECT_EQ("1", output); - SpdyStrAppend(&output, f); - EXPECT_EQ("10", output); - SpdyStrAppend(&output, f, t, t, f); - EXPECT_EQ("100110", output); - - output.clear(); - - // Mixed string-like, numerical, and Boolean arguments. - SpdyStrAppend(&output, kFoo, i, string_foo, f, u, t, stringpiece_bar, d, t); - EXPECT_EQ("foo1foo081bar3.14151", output); - SpdyStrAppend(&output, d, t, t, string_bar, i, u, kBar, t, d, f); - EXPECT_EQ("foo1foo081bar3.141513.141511bar18bar13.14150", output); -} - TEST(SpdyStringUtilsTest, SpdyHexDigitToInt) { EXPECT_EQ(0, SpdyHexDigitToInt('0')); EXPECT_EQ(1, SpdyHexDigitToInt('1')); @@ -168,40 +73,6 @@ TEST(SpdyStringUtilsTest, SpdyHexEncode) { SpdyHexEncode(reinterpret_cast<char*>(bytes), sizeof(bytes))); } -TEST(SpdyStringUtilsTest, SpdyHexEncodeUInt32AndTrim) { - EXPECT_EQ("0", SpdyHexEncodeUInt32AndTrim(0)); - EXPECT_EQ("1", SpdyHexEncodeUInt32AndTrim(1)); - EXPECT_EQ("a", SpdyHexEncodeUInt32AndTrim(0xA)); - EXPECT_EQ("f", SpdyHexEncodeUInt32AndTrim(0xF)); - EXPECT_EQ("a9", SpdyHexEncodeUInt32AndTrim(0xA9)); - EXPECT_EQ("9abcdef", SpdyHexEncodeUInt32AndTrim(0x9ABCDEF)); - EXPECT_EQ("12345678", SpdyHexEncodeUInt32AndTrim(0x12345678)); - EXPECT_EQ("ffffffff", SpdyHexEncodeUInt32AndTrim(0xFFFFFFFF)); - EXPECT_EQ("10000001", SpdyHexEncodeUInt32AndTrim(0x10000001)); -} - -TEST(SpdyStringUtilsTest, SpdyStringPieceCaseHash) { - SpdyStringPieceCaseHash hasher; - auto mixed = hasher("To Be Or Not To Be, That is The Question"); - auto lower = hasher("to be or not to be, that is the question"); - EXPECT_EQ(mixed, lower); - auto lower2 = hasher("to be or not to be, that is the question"); - EXPECT_EQ(lower, lower2); - auto different = hasher("to see or not to see, that is the question"); - EXPECT_NE(lower, different); - EXPECT_NE(lower, hasher("")); -} - -TEST(SpdyStringUtilsTest, SpdyStringPieceCaseEq) { - SpdyStringPieceCaseEq eq; - EXPECT_TRUE(eq("To Be Or Not To Be, That is The Question", - "to be or not to be, that is the question")); - EXPECT_TRUE(eq("to be or not to be, that is the question", - "to be or not to be, that is the question")); - EXPECT_FALSE(eq("to be or not to be, that is the question", - "to see or not to see, that is the question")); -} - } // namespace } // namespace test } // namespace spdy |