Add quic::test::GetFirstFlightOfPackets() This CL adds GetFirstFlightOfPackets, a mechanism to extract the first flight of packets from a QUIC connection. This will be used to test code that parses the CHLO. This CL also adds various test classes and methods that are used by GetFirstFlightOfPackets. gfe-relnote: n/a, test-only PiperOrigin-RevId: 307703986 Change-Id: Iedd17f41f1120d2be2188694710dcd698d61efc8
diff --git a/quic/test_tools/first_flight.cc b/quic/test_tools/first_flight.cc new file mode 100644 index 0000000..e4d2816 --- /dev/null +++ b/quic/test_tools/first_flight.cc
@@ -0,0 +1,140 @@ +// Copyright (c) 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/test_tools/first_flight.h" + +#include <memory> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h" +#include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h" +#include "net/third_party/quiche/src/quic/core/quic_config.h" +#include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { + +// Utility class that creates a custom HTTP/3 session and QUIC connection in +// order to extract the first flight of packets it sends. This is meant to only +// be used by GetFirstFlightOfPackets() below. +class FirstFlightExtractor : public DelegatedPacketWriter::Delegate { + public: + FirstFlightExtractor(const ParsedQuicVersion& version, + const QuicConfig& config, + const QuicConnectionId& server_connection_id, + const QuicConnectionId& client_connection_id) + : version_(version), + server_connection_id_(server_connection_id), + client_connection_id_(client_connection_id), + writer_(this), + config_(config), + crypto_config_(crypto_test_utils::ProofVerifierForTesting()) { + EXPECT_NE(version_, UnsupportedQuicVersion()); + } + + void GenerateFirstFlight() { + crypto_config_.set_alpn(AlpnForVersion(version_)); + connection_ = + new QuicConnection(server_connection_id_, + QuicSocketAddress(TestPeerIPAddress(), kTestPort), + &connection_helper_, &alarm_factory_, &writer_, + /*owns_writer=*/false, Perspective::IS_CLIENT, + ParsedQuicVersionVector{version_}); + connection_->set_client_connection_id(client_connection_id_); + session_ = std::make_unique<QuicSpdyClientSession>( + config_, ParsedQuicVersionVector{version_}, + connection_, // session_ takes ownership of connection_ here. + TestServerId(), &crypto_config_, &push_promise_index_); + session_->Initialize(); + session_->CryptoConnect(); + } + + void OnDelegatedPacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& /*self_client_address*/, + const QuicSocketAddress& /*peer_client_address*/, + PerPacketOptions* /*options*/) override { + packets_.emplace_back( + QuicReceivedPacket(buffer, buf_len, + connection_helper_.GetClock()->ApproximateNow(), + /*owns_buffer=*/false) + .Clone()); + } + + std::vector<std::unique_ptr<QuicReceivedPacket>>&& ConsumePackets() { + return std::move(packets_); + } + + private: + ParsedQuicVersion version_; + QuicConnectionId server_connection_id_; + QuicConnectionId client_connection_id_; + MockQuicConnectionHelper connection_helper_; + MockAlarmFactory alarm_factory_; + DelegatedPacketWriter writer_; + QuicConfig config_; + QuicCryptoClientConfig crypto_config_; + QuicClientPushPromiseIndex push_promise_index_; + QuicConnection* connection_; // Owned by session_. + std::unique_ptr<QuicSpdyClientSession> session_; + std::vector<std::unique_ptr<QuicReceivedPacket>> packets_; +}; + +std::vector<std::unique_ptr<QuicReceivedPacket>> GetFirstFlightOfPackets( + const ParsedQuicVersion& version, + const QuicConfig& config, + const QuicConnectionId& server_connection_id, + const QuicConnectionId& client_connection_id) { + FirstFlightExtractor first_flight_extractor( + version, config, server_connection_id, client_connection_id); + first_flight_extractor.GenerateFirstFlight(); + return first_flight_extractor.ConsumePackets(); +} + +std::vector<std::unique_ptr<QuicReceivedPacket>> GetFirstFlightOfPackets( + const ParsedQuicVersion& version, + const QuicConfig& config, + const QuicConnectionId& server_connection_id) { + return GetFirstFlightOfPackets(version, config, server_connection_id, + EmptyQuicConnectionId()); +} + +std::vector<std::unique_ptr<QuicReceivedPacket>> GetFirstFlightOfPackets( + const ParsedQuicVersion& version, + const QuicConfig& config) { + return GetFirstFlightOfPackets(version, config, TestConnectionId()); +} + +std::vector<std::unique_ptr<QuicReceivedPacket>> GetFirstFlightOfPackets( + const ParsedQuicVersion& version, + const QuicConnectionId& server_connection_id, + const QuicConnectionId& client_connection_id) { + return GetFirstFlightOfPackets(version, DefaultQuicConfig(), + server_connection_id, client_connection_id); +} + +std::vector<std::unique_ptr<QuicReceivedPacket>> GetFirstFlightOfPackets( + const ParsedQuicVersion& version, + const QuicConnectionId& server_connection_id) { + return GetFirstFlightOfPackets(version, DefaultQuicConfig(), + server_connection_id, EmptyQuicConnectionId()); +} + +std::vector<std::unique_ptr<QuicReceivedPacket>> GetFirstFlightOfPackets( + const ParsedQuicVersion& version) { + return GetFirstFlightOfPackets(version, DefaultQuicConfig(), + TestConnectionId()); +} + +} // namespace test +} // namespace quic
diff --git a/quic/test_tools/first_flight.h b/quic/test_tools/first_flight.h new file mode 100644 index 0000000..b2a4ebd --- /dev/null +++ b/quic/test_tools/first_flight.h
@@ -0,0 +1,111 @@ +// 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_TEST_TOOLS_FIRST_FLIGHT_H_ +#define QUICHE_QUIC_TEST_TOOLS_FIRST_FLIGHT_H_ + +#include <memory> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/quic_config.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" +#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" + +namespace quic { +namespace test { + +// Implementation of QuicPacketWriter that sends all packets to a delegate. +class QUIC_NO_EXPORT DelegatedPacketWriter : public QuicPacketWriter { + public: + class QUIC_NO_EXPORT Delegate { + public: + virtual ~Delegate() {} + // Note that |buffer| may be released after this call completes so overrides + // that want to use the data after the call is complete MUST copy it. + virtual void OnDelegatedPacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_client_address, + const QuicSocketAddress& peer_client_address, + PerPacketOptions* options) = 0; + }; + + // |delegate| MUST be valid for the duration of the DelegatedPacketWriter's + // lifetime. + explicit DelegatedPacketWriter(Delegate* delegate) : delegate_(delegate) { + CHECK_NE(delegate_, nullptr); + } + + // Overrides for QuicPacketWriter. + bool IsWriteBlocked() const override { return false; } + void SetWritable() override {} + QuicByteCount GetMaxPacketSize( + const QuicSocketAddress& /*peer_address*/) const override { + return kMaxOutgoingPacketSize; + } + bool SupportsReleaseTime() const override { return false; } + bool IsBatchMode() const override { return false; } + char* GetNextWriteLocation( + const QuicIpAddress& /*self_address*/, + const QuicSocketAddress& /*peer_address*/) override { + return nullptr; + } + WriteResult Flush() override { return WriteResult(WRITE_STATUS_OK, 0); } + + WriteResult WritePacket(const char* buffer, + size_t buf_len, + const QuicIpAddress& self_client_address, + const QuicSocketAddress& peer_client_address, + PerPacketOptions* options) override { + delegate_->OnDelegatedPacket(buffer, buf_len, self_client_address, + peer_client_address, options); + return WriteResult(WRITE_STATUS_OK, buf_len); + } + + private: + Delegate* delegate_; // Unowned. +}; + +// Returns an array of packets that represent the first flight of a real +// HTTP/3 connection. In most cases, this array will only contain one packet +// that carries the CHLO. +std::vector<std::unique_ptr<QuicReceivedPacket>> GetFirstFlightOfPackets( + const ParsedQuicVersion& version, + const QuicConfig& config, + const QuicConnectionId& server_connection_id, + const QuicConnectionId& client_connection_id); + +// Below are various convenience overloads that use default values for the +// omitted parameters: +// |config| = DefaultQuicConfig(), +// |server_connection_id| = TestConnectionId(), +// |client_connection_id| = EmptyQuicConnectionId(). +std::vector<std::unique_ptr<QuicReceivedPacket>> GetFirstFlightOfPackets( + const ParsedQuicVersion& version, + const QuicConfig& config, + const QuicConnectionId& server_connection_id); + +std::vector<std::unique_ptr<QuicReceivedPacket>> GetFirstFlightOfPackets( + const ParsedQuicVersion& version, + const QuicConnectionId& server_connection_id, + const QuicConnectionId& client_connection_id); + +std::vector<std::unique_ptr<QuicReceivedPacket>> GetFirstFlightOfPackets( + const ParsedQuicVersion& version, + const QuicConnectionId& server_connection_id); + +std::vector<std::unique_ptr<QuicReceivedPacket>> GetFirstFlightOfPackets( + const ParsedQuicVersion& version, + const QuicConfig& config); + +std::vector<std::unique_ptr<QuicReceivedPacket>> GetFirstFlightOfPackets( + const ParsedQuicVersion& version); + +} // namespace test +} // namespace quic + +#endif // QUICHE_QUIC_TEST_TOOLS_FIRST_FLIGHT_H_
diff --git a/quic/test_tools/quic_test_utils.cc b/quic/test_tools/quic_test_utils.cc index 47fd05f..bb45ae3 100644 --- a/quic/test_tools/quic_test_utils.cc +++ b/quic/test_tools/quic_test_utils.cc
@@ -17,13 +17,16 @@ #include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" +#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h" #include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h" +#include "net/third_party/quiche/src/quic/core/quic_config.h" #include "net/third_party/quiche/src/quic/core/quic_data_writer.h" #include "net/third_party/quiche/src/quic/core/quic_framer.h" #include "net/third_party/quiche/src/quic/core/quic_packet_creator.h" #include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" @@ -74,6 +77,14 @@ return quiche::QuicheEndian::NetToHost64(connection_id64_net); } +std::string TestHostname() { + return "test.example.org"; +} + +QuicServerId TestServerId() { + return QuicServerId(TestHostname(), kTestPort); +} + QuicAckFrame InitAckFrame(const std::vector<QuicAckBlock>& ack_blocks) { DCHECK_GT(ack_blocks.size(), 0u);
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h index b8e0eae..d70cbef 100644 --- a/quic/test_tools/quic_test_utils.h +++ b/quic/test_tools/quic_test_utils.h
@@ -19,9 +19,11 @@ #include "net/third_party/quiche/src/quic/core/http/quic_server_session_base.h" #include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h" #include "net/third_party/quiche/src/quic/core/quic_connection.h" +#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" #include "net/third_party/quiche/src/quic/core/quic_framer.h" #include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" #include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h" +#include "net/third_party/quiche/src/quic/core/quic_server_id.h" #include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h" @@ -56,6 +58,12 @@ kInitialSessionFlowControlWindowForTest = 1536 * 1024, // 1.5 MB }; +// A hostname useful for testing, returns "test.example.org". +std::string TestHostname(); + +// A server ID useful for testing, returns test.example.org:12345. +QuicServerId TestServerId(); + // Returns the test peer IP address. QuicIpAddress TestPeerIPAddress();