2397 lines
100 KiB
C++
2397 lines
100 KiB
C++
// Copyright 2012 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "net/quic/quic_chromium_client_session.h"
|
|
|
|
#include "base/base64.h"
|
|
#include "base/files/file_path.h"
|
|
#include "base/functional/bind.h"
|
|
#include "base/memory/ptr_util.h"
|
|
#include "base/memory/raw_ptr.h"
|
|
#include "base/run_loop.h"
|
|
#include "base/strings/strcat.h"
|
|
#include "base/task/single_thread_task_runner.h"
|
|
#include "base/test/scoped_feature_list.h"
|
|
#include "base/time/default_tick_clock.h"
|
|
#include "build/build_config.h"
|
|
#include "net/base/features.h"
|
|
#include "net/base/network_anonymization_key.h"
|
|
#include "net/base/schemeful_site.h"
|
|
#include "net/base/test_completion_callback.h"
|
|
#include "net/cert/cert_verify_result.h"
|
|
#include "net/dns/public/host_resolver_results.h"
|
|
#include "net/dns/public/secure_dns_policy.h"
|
|
#include "net/http/transport_security_state.h"
|
|
#include "net/http/transport_security_state_test_util.h"
|
|
#include "net/log/net_log.h"
|
|
#include "net/log/net_log_source.h"
|
|
#include "net/quic/address_utils.h"
|
|
#include "net/quic/crypto/proof_verifier_chromium.h"
|
|
#include "net/quic/mock_crypto_client_stream_factory.h"
|
|
#include "net/quic/mock_quic_data.h"
|
|
#include "net/quic/quic_chromium_alarm_factory.h"
|
|
#include "net/quic/quic_chromium_client_session_peer.h"
|
|
#include "net/quic/quic_chromium_connection_helper.h"
|
|
#include "net/quic/quic_chromium_packet_reader.h"
|
|
#include "net/quic/quic_chromium_packet_writer.h"
|
|
#include "net/quic/quic_connectivity_monitor.h"
|
|
#include "net/quic/quic_context.h"
|
|
#include "net/quic/quic_crypto_client_config_handle.h"
|
|
#include "net/quic/quic_crypto_client_stream_factory.h"
|
|
#include "net/quic/quic_http_utils.h"
|
|
#include "net/quic/quic_server_info.h"
|
|
#include "net/quic/quic_session_key.h"
|
|
#include "net/quic/quic_test_packet_maker.h"
|
|
#include "net/quic/test_quic_crypto_client_config_handle.h"
|
|
#include "net/socket/datagram_client_socket.h"
|
|
#include "net/socket/socket_test_util.h"
|
|
#include "net/spdy/spdy_test_util_common.h"
|
|
#include "net/ssl/ssl_config_service_defaults.h"
|
|
#include "net/test/cert_test_util.h"
|
|
#include "net/test/gtest_util.h"
|
|
#include "net/test/test_data_directory.h"
|
|
#include "net/test/test_with_task_environment.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/core/crypto/aes_128_gcm_12_encrypter.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/core/crypto/crypto_protocol.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/core/crypto/quic_decrypter.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/core/crypto/quic_encrypter.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/core/http/quic_client_promised_info.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/core/quic_connection_id.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/core/quic_packet_writer.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/core/quic_tag.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/core/quic_utils.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/platform/api/quic_flags.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/platform/api/quic_test.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/test_tools/crypto_test_utils.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/test_tools/mock_connection_id_generator.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/test_tools/qpack/qpack_test_utils.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/test_tools/quic_client_promised_info_peer.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/test_tools/quic_connection_peer.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/test_tools/quic_session_peer.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/test_tools/quic_stream_peer.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/test_tools/quic_test_utils.h"
|
|
#include "net/third_party/quiche/src/quiche/quic/test_tools/simple_quic_framer.h"
|
|
#include "net/third_party/quiche/src/quiche/spdy/test_tools/spdy_test_utils.h"
|
|
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
|
#include "url/gurl.h"
|
|
#include "url/scheme_host_port.h"
|
|
#include "url/url_constants.h"
|
|
|
|
using testing::_;
|
|
|
|
namespace net::test {
|
|
namespace {
|
|
|
|
const IPEndPoint kIpEndPoint = IPEndPoint(IPAddress::IPv4AllZeros(), 0);
|
|
const char kServerHostname[] = "test.example.com";
|
|
const uint16_t kServerPort = 443;
|
|
const size_t kMaxReadersPerQuicSession = 5;
|
|
|
|
const handles::NetworkHandle kDefaultNetworkForTests = 1;
|
|
const handles::NetworkHandle kNewNetworkForTests = 2;
|
|
|
|
// A subclass of QuicChromiumClientSession that allows OnPathDegrading to be
|
|
// mocked.
|
|
class TestingQuicChromiumClientSession : public QuicChromiumClientSession {
|
|
public:
|
|
using QuicChromiumClientSession::QuicChromiumClientSession;
|
|
|
|
MOCK_METHOD(void, OnPathDegrading, (), (override));
|
|
|
|
void ReallyOnPathDegrading() { QuicChromiumClientSession::OnPathDegrading(); }
|
|
};
|
|
|
|
class QuicChromiumClientSessionTest
|
|
: public ::testing::TestWithParam<quic::ParsedQuicVersion>,
|
|
public WithTaskEnvironment {
|
|
public:
|
|
QuicChromiumClientSessionTest()
|
|
: version_(GetParam()),
|
|
config_(quic::test::DefaultQuicConfig()),
|
|
crypto_config_(
|
|
quic::test::crypto_test_utils::ProofVerifierForTesting()),
|
|
default_read_(
|
|
std::make_unique<MockRead>(SYNCHRONOUS, ERR_IO_PENDING, 0)),
|
|
socket_data_(std::make_unique<SequencedSocketData>(
|
|
base::make_span(default_read_.get(), 1u),
|
|
base::span<MockWrite>())),
|
|
helper_(&clock_, &random_),
|
|
transport_security_state_(std::make_unique<TransportSecurityState>()),
|
|
session_key_(kServerHostname,
|
|
kServerPort,
|
|
PRIVACY_MODE_DISABLED,
|
|
SocketTag(),
|
|
NetworkAnonymizationKey(),
|
|
SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false),
|
|
destination_(url::kHttpsScheme, kServerHostname, kServerPort),
|
|
default_network_(handles::kInvalidNetworkHandle),
|
|
client_maker_(version_,
|
|
quic::QuicUtils::CreateRandomConnectionId(&random_),
|
|
&clock_,
|
|
kServerHostname,
|
|
quic::Perspective::IS_CLIENT),
|
|
server_maker_(version_,
|
|
quic::QuicUtils::CreateRandomConnectionId(&random_),
|
|
&clock_,
|
|
kServerHostname,
|
|
quic::Perspective::IS_SERVER,
|
|
false) {
|
|
FLAGS_quic_enable_http3_grease_randomness = false;
|
|
quic::QuicEnableVersion(version_);
|
|
// Advance the time, because timers do not like uninitialized times.
|
|
clock_.AdvanceTime(quic::QuicTime::Delta::FromSeconds(1));
|
|
}
|
|
|
|
void ResetHandleOnError(
|
|
std::unique_ptr<QuicChromiumClientSession::Handle>* handle,
|
|
int net_error) {
|
|
EXPECT_NE(OK, net_error);
|
|
handle->reset();
|
|
}
|
|
|
|
protected:
|
|
void Initialize() {
|
|
if (socket_data_)
|
|
socket_factory_.AddSocketDataProvider(socket_data_.get());
|
|
std::unique_ptr<DatagramClientSocket> socket =
|
|
socket_factory_.CreateDatagramClientSocket(
|
|
DatagramSocket::DEFAULT_BIND, NetLog::Get(), NetLogSource());
|
|
socket->Connect(kIpEndPoint);
|
|
QuicChromiumPacketWriter* writer = new net::QuicChromiumPacketWriter(
|
|
socket.get(), base::SingleThreadTaskRunner::GetCurrentDefault().get());
|
|
quic::QuicConnection* connection = new quic::QuicConnection(
|
|
quic::QuicUtils::CreateRandomConnectionId(&random_),
|
|
quic::QuicSocketAddress(), ToQuicSocketAddress(kIpEndPoint), &helper_,
|
|
&alarm_factory_, writer, true, quic::Perspective::IS_CLIENT,
|
|
quic::test::SupportedVersions(version_), connection_id_generator_);
|
|
session_ = std::make_unique<TestingQuicChromiumClientSession>(
|
|
connection, std::move(socket),
|
|
/*stream_factory=*/nullptr, &crypto_client_stream_factory_, &clock_,
|
|
transport_security_state_.get(), &ssl_config_service_,
|
|
base::WrapUnique(static_cast<QuicServerInfo*>(nullptr)), session_key_,
|
|
/*require_confirmation=*/false, migrate_session_early_v2_,
|
|
/*migrate_session_on_network_change_v2=*/false, default_network_,
|
|
quic::QuicTime::Delta::FromMilliseconds(
|
|
kDefaultRetransmittableOnWireTimeout.InMilliseconds()),
|
|
/*migrate_idle_session=*/false, allow_port_migration_,
|
|
kDefaultIdleSessionMigrationPeriod, kMaxTimeOnNonDefaultNetwork,
|
|
kMaxMigrationsToNonDefaultNetworkOnWriteError,
|
|
kMaxMigrationsToNonDefaultNetworkOnPathDegrading,
|
|
kQuicYieldAfterPacketsRead,
|
|
quic::QuicTime::Delta::FromMilliseconds(
|
|
kQuicYieldAfterDurationMilliseconds),
|
|
/*cert_verify_flags=*/0, config_,
|
|
std::make_unique<TestQuicCryptoClientConfigHandle>(&crypto_config_),
|
|
"CONNECTION_UNKNOWN", base::TimeTicks::Now(), base::TimeTicks::Now(),
|
|
std::make_unique<quic::QuicClientPushPromiseIndex>(),
|
|
&test_push_delegate_, base::DefaultTickClock::GetInstance(),
|
|
base::SingleThreadTaskRunner::GetCurrentDefault().get(),
|
|
/*socket_performance_watcher=*/nullptr, HostResolverEndpointResult(),
|
|
NetLog::Get());
|
|
if (connectivity_monitor_) {
|
|
connectivity_monitor_->SetInitialDefaultNetwork(default_network_);
|
|
session_->AddConnectivityObserver(connectivity_monitor_.get());
|
|
}
|
|
|
|
scoped_refptr<X509Certificate> cert(
|
|
ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"));
|
|
verify_details_.cert_verify_result.verified_cert = cert;
|
|
verify_details_.cert_verify_result.is_issued_by_known_root = true;
|
|
session_->Initialize();
|
|
// Blackhole QPACK decoder stream instead of constructing mock writes.
|
|
session_->qpack_decoder()->set_qpack_stream_sender_delegate(
|
|
&noop_qpack_stream_sender_delegate_);
|
|
session_->StartReading();
|
|
writer->set_delegate(session_.get());
|
|
}
|
|
|
|
void TearDown() override {
|
|
if (session_) {
|
|
if (connectivity_monitor_)
|
|
session_->RemoveConnectivityObserver(connectivity_monitor_.get());
|
|
session_->CloseSessionOnError(
|
|
ERR_ABORTED, quic::QUIC_INTERNAL_ERROR,
|
|
quic::ConnectionCloseBehavior::SILENT_CLOSE);
|
|
}
|
|
}
|
|
|
|
void SetIetfConnectionMigrationFlagsAndConnectionOptions() {
|
|
FLAGS_quic_reloadable_flag_quic_connection_migration_use_new_cid_v2 = true;
|
|
config_.SetConnectionOptionsToSend({quic::kRVCM});
|
|
}
|
|
|
|
void CompleteCryptoHandshake() {
|
|
ASSERT_THAT(session_->CryptoConnect(callback_.callback()), IsOk());
|
|
}
|
|
|
|
std::unique_ptr<QuicChromiumPacketWriter> CreateQuicChromiumPacketWriter(
|
|
DatagramClientSocket* socket,
|
|
QuicChromiumClientSession* session) const {
|
|
auto writer = std::make_unique<QuicChromiumPacketWriter>(
|
|
socket, base::SingleThreadTaskRunner::GetCurrentDefault().get());
|
|
writer->set_delegate(session);
|
|
return writer;
|
|
}
|
|
|
|
quic::QuicStreamId GetNthClientInitiatedBidirectionalStreamId(int n) {
|
|
return quic::test::GetNthClientInitiatedBidirectionalStreamId(
|
|
version_.transport_version, n);
|
|
}
|
|
|
|
quic::QuicStreamId GetNthServerInitiatedUnidirectionalStreamId(int n) {
|
|
return quic::test::GetNthServerInitiatedUnidirectionalStreamId(
|
|
version_.transport_version, n);
|
|
}
|
|
|
|
size_t GetMaxAllowedOutgoingBidirectionalStreams() {
|
|
return quic::test::QuicSessionPeer::ietf_streamid_manager(session_.get())
|
|
->max_outgoing_bidirectional_streams();
|
|
}
|
|
|
|
const quic::ParsedQuicVersion version_;
|
|
quic::test::QuicFlagSaver flags_; // Save/restore all QUIC flag values.
|
|
quic::QuicConfig config_;
|
|
quic::QuicCryptoClientConfig crypto_config_;
|
|
NetLogWithSource net_log_with_source_{
|
|
NetLogWithSource::Make(NetLog::Get(), NetLogSourceType::NONE)};
|
|
MockClientSocketFactory socket_factory_;
|
|
std::unique_ptr<MockRead> default_read_;
|
|
std::unique_ptr<SequencedSocketData> socket_data_;
|
|
quic::MockClock clock_;
|
|
quic::test::MockRandom random_{0};
|
|
QuicChromiumConnectionHelper helper_;
|
|
quic::test::MockAlarmFactory alarm_factory_;
|
|
std::unique_ptr<TransportSecurityState> transport_security_state_;
|
|
MockCryptoClientStreamFactory crypto_client_stream_factory_;
|
|
SSLConfigServiceDefaults ssl_config_service_;
|
|
QuicSessionKey session_key_;
|
|
url::SchemeHostPort destination_;
|
|
std::unique_ptr<TestingQuicChromiumClientSession> session_;
|
|
handles::NetworkHandle default_network_;
|
|
std::unique_ptr<QuicConnectivityMonitor> connectivity_monitor_;
|
|
TestServerPushDelegate test_push_delegate_;
|
|
raw_ptr<quic::QuicConnectionVisitorInterface> visitor_;
|
|
TestCompletionCallback callback_;
|
|
QuicTestPacketMaker client_maker_;
|
|
QuicTestPacketMaker server_maker_;
|
|
ProofVerifyDetailsChromium verify_details_;
|
|
bool migrate_session_early_v2_ = false;
|
|
bool allow_port_migration_ = false;
|
|
quic::test::MockConnectionIdGenerator connection_id_generator_;
|
|
quic::test::NoopQpackStreamSenderDelegate noop_qpack_stream_sender_delegate_;
|
|
};
|
|
|
|
INSTANTIATE_TEST_SUITE_P(VersionIncludeStreamDependencySequence,
|
|
QuicChromiumClientSessionTest,
|
|
::testing::ValuesIn(AllSupportedQuicVersions()),
|
|
::testing::PrintToStringParamName());
|
|
|
|
// Basic test of ProofVerifyDetailsChromium is converted to SSLInfo retrieved
|
|
// through QuicChromiumClientSession::GetSSLInfo(). Doesn't test some of the
|
|
// more complicated fields.
|
|
TEST_P(QuicChromiumClientSessionTest, GetSSLInfo1) {
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
|
|
ProofVerifyDetailsChromium details;
|
|
details.is_fatal_cert_error = false;
|
|
details.cert_verify_result.verified_cert =
|
|
ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
|
|
details.cert_verify_result.is_issued_by_known_root = true;
|
|
details.cert_verify_result.policy_compliance =
|
|
ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS;
|
|
|
|
CompleteCryptoHandshake();
|
|
session_->OnProofVerifyDetailsAvailable(details);
|
|
|
|
SSLInfo ssl_info;
|
|
ASSERT_TRUE(session_->GetSSLInfo(&ssl_info));
|
|
EXPECT_TRUE(ssl_info.is_valid());
|
|
|
|
EXPECT_EQ(details.is_fatal_cert_error, ssl_info.is_fatal_cert_error);
|
|
EXPECT_TRUE(ssl_info.cert->EqualsIncludingChain(
|
|
details.cert_verify_result.verified_cert.get()));
|
|
EXPECT_EQ(details.cert_verify_result.cert_status, ssl_info.cert_status);
|
|
EXPECT_EQ(details.cert_verify_result.is_issued_by_known_root,
|
|
ssl_info.is_issued_by_known_root);
|
|
EXPECT_EQ(details.cert_verify_result.policy_compliance,
|
|
ssl_info.ct_policy_compliance);
|
|
}
|
|
|
|
// Just like GetSSLInfo1, but uses different values.
|
|
TEST_P(QuicChromiumClientSessionTest, GetSSLInfo2) {
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
|
|
ProofVerifyDetailsChromium details;
|
|
details.is_fatal_cert_error = false;
|
|
details.cert_verify_result.verified_cert =
|
|
ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
|
|
details.cert_verify_result.is_issued_by_known_root = false;
|
|
details.cert_verify_result.policy_compliance =
|
|
ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS;
|
|
|
|
CompleteCryptoHandshake();
|
|
session_->OnProofVerifyDetailsAvailable(details);
|
|
|
|
SSLInfo ssl_info;
|
|
ASSERT_TRUE(session_->GetSSLInfo(&ssl_info));
|
|
EXPECT_TRUE(ssl_info.is_valid());
|
|
|
|
EXPECT_EQ(details.is_fatal_cert_error, ssl_info.is_fatal_cert_error);
|
|
EXPECT_TRUE(ssl_info.cert->EqualsIncludingChain(
|
|
details.cert_verify_result.verified_cert.get()));
|
|
EXPECT_EQ(details.cert_verify_result.cert_status, ssl_info.cert_status);
|
|
EXPECT_EQ(details.cert_verify_result.is_issued_by_known_root,
|
|
ssl_info.is_issued_by_known_root);
|
|
EXPECT_EQ(details.cert_verify_result.policy_compliance,
|
|
ssl_info.ct_policy_compliance);
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, IsFatalErrorNotSetForNonFatalError) {
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
|
|
SSLInfo ssl_info;
|
|
ProofVerifyDetailsChromium details;
|
|
details.cert_verify_result.verified_cert =
|
|
ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
|
|
details.cert_verify_result.cert_status = CERT_STATUS_DATE_INVALID;
|
|
details.is_fatal_cert_error = false;
|
|
CompleteCryptoHandshake();
|
|
session_->OnProofVerifyDetailsAvailable(details);
|
|
|
|
ASSERT_TRUE(session_->GetSSLInfo(&ssl_info));
|
|
EXPECT_FALSE(ssl_info.is_fatal_cert_error);
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, IsFatalErrorSetForFatalError) {
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
Initialize();
|
|
|
|
SSLInfo ssl_info;
|
|
ProofVerifyDetailsChromium details;
|
|
details.cert_verify_result.verified_cert =
|
|
ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
|
|
details.cert_verify_result.cert_status = CERT_STATUS_DATE_INVALID;
|
|
details.is_fatal_cert_error = true;
|
|
CompleteCryptoHandshake();
|
|
session_->OnProofVerifyDetailsAvailable(details);
|
|
ASSERT_TRUE(session_->GetSSLInfo(&ssl_info));
|
|
EXPECT_TRUE(ssl_info.is_fatal_cert_error);
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, CryptoConnect) {
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, Handle) {
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
|
|
NetLogWithSource session_net_log = session_->net_log();
|
|
EXPECT_EQ(NetLogSourceType::QUIC_SESSION, session_net_log.source().type);
|
|
EXPECT_EQ(NetLog::Get(), session_net_log.net_log());
|
|
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle =
|
|
session_->CreateHandle(destination_);
|
|
EXPECT_TRUE(handle->IsConnected());
|
|
EXPECT_FALSE(handle->OneRttKeysAvailable());
|
|
EXPECT_EQ(version_, handle->GetQuicVersion());
|
|
EXPECT_EQ(session_key_.server_id(), handle->server_id());
|
|
EXPECT_EQ(session_net_log.source().type, handle->net_log().source().type);
|
|
EXPECT_EQ(session_net_log.source().id, handle->net_log().source().id);
|
|
EXPECT_EQ(session_net_log.net_log(), handle->net_log().net_log());
|
|
IPEndPoint address;
|
|
EXPECT_EQ(OK, handle->GetPeerAddress(&address));
|
|
EXPECT_EQ(kIpEndPoint, address);
|
|
EXPECT_TRUE(handle->CreatePacketBundler().get() != nullptr);
|
|
|
|
CompleteCryptoHandshake();
|
|
|
|
EXPECT_TRUE(handle->OneRttKeysAvailable());
|
|
|
|
// Request a stream and verify that a stream was created.
|
|
TestCompletionCallback callback;
|
|
ASSERT_EQ(OK, handle->RequestStream(/*requires_confirmation=*/false,
|
|
callback.callback(),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
EXPECT_TRUE(handle->ReleaseStream() != nullptr);
|
|
|
|
quic_data.Resume();
|
|
EXPECT_TRUE(quic_data.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data.AllWriteDataConsumed());
|
|
|
|
// Veirfy that the handle works correctly after the session is closed.
|
|
EXPECT_FALSE(handle->IsConnected());
|
|
EXPECT_TRUE(handle->OneRttKeysAvailable());
|
|
EXPECT_EQ(version_, handle->GetQuicVersion());
|
|
EXPECT_EQ(session_key_.server_id(), handle->server_id());
|
|
EXPECT_EQ(session_net_log.source().type, handle->net_log().source().type);
|
|
EXPECT_EQ(session_net_log.source().id, handle->net_log().source().id);
|
|
EXPECT_EQ(session_net_log.net_log(), handle->net_log().net_log());
|
|
EXPECT_EQ(ERR_CONNECTION_CLOSED, handle->GetPeerAddress(&address));
|
|
EXPECT_TRUE(handle->CreatePacketBundler().get() == nullptr);
|
|
{
|
|
// Verify that CreateHandle() works even after the session is closed.
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle2 =
|
|
session_->CreateHandle(destination_);
|
|
EXPECT_FALSE(handle2->IsConnected());
|
|
EXPECT_TRUE(handle2->OneRttKeysAvailable());
|
|
ASSERT_EQ(ERR_CONNECTION_CLOSED,
|
|
handle2->RequestStream(/*requires_confirmation=*/false,
|
|
callback.callback(),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
}
|
|
|
|
session_.reset();
|
|
|
|
// Verify that the handle works correctly after the session is deleted.
|
|
EXPECT_FALSE(handle->IsConnected());
|
|
EXPECT_TRUE(handle->OneRttKeysAvailable());
|
|
EXPECT_EQ(version_, handle->GetQuicVersion());
|
|
EXPECT_EQ(session_key_.server_id(), handle->server_id());
|
|
EXPECT_EQ(session_net_log.source().type, handle->net_log().source().type);
|
|
EXPECT_EQ(session_net_log.source().id, handle->net_log().source().id);
|
|
EXPECT_EQ(session_net_log.net_log(), handle->net_log().net_log());
|
|
EXPECT_EQ(ERR_CONNECTION_CLOSED, handle->GetPeerAddress(&address));
|
|
EXPECT_TRUE(handle->CreatePacketBundler().get() == nullptr);
|
|
ASSERT_EQ(
|
|
ERR_CONNECTION_CLOSED,
|
|
handle->RequestStream(/*requires_confirmation=*/false,
|
|
callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, StreamRequest) {
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
// Request a stream and verify that a stream was created.
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle =
|
|
session_->CreateHandle(destination_);
|
|
TestCompletionCallback callback;
|
|
ASSERT_EQ(OK, handle->RequestStream(/*requires_confirmation=*/false,
|
|
callback.callback(),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
EXPECT_TRUE(handle->ReleaseStream() != nullptr);
|
|
|
|
quic_data.Resume();
|
|
EXPECT_TRUE(quic_data.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data.AllWriteDataConsumed());
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, ConfirmationRequiredStreamRequest) {
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
// Request a stream and verify that a stream was created.
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle =
|
|
session_->CreateHandle(destination_);
|
|
TestCompletionCallback callback;
|
|
ASSERT_EQ(OK, handle->RequestStream(/*requires_confirmation=*/true,
|
|
callback.callback(),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
EXPECT_TRUE(handle->ReleaseStream() != nullptr);
|
|
|
|
quic_data.Resume();
|
|
EXPECT_TRUE(quic_data.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data.AllWriteDataConsumed());
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, StreamRequestBeforeConfirmation) {
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
|
|
// Request a stream and verify that a stream was created.
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle =
|
|
session_->CreateHandle(destination_);
|
|
TestCompletionCallback callback;
|
|
ASSERT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle->RequestStream(/*requires_confirmation=*/true, callback.callback(),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
|
|
CompleteCryptoHandshake();
|
|
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
|
|
EXPECT_TRUE(handle->ReleaseStream() != nullptr);
|
|
|
|
quic_data.Resume();
|
|
EXPECT_TRUE(quic_data.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data.AllWriteDataConsumed());
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, CancelStreamRequestBeforeRelease) {
|
|
MockQuicData quic_data(version_);
|
|
int packet_num = 1;
|
|
quic_data.AddWrite(SYNCHRONOUS,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
quic_data.AddWrite(
|
|
SYNCHRONOUS,
|
|
client_maker_.MakeRstPacket(packet_num++, true,
|
|
GetNthClientInitiatedBidirectionalStreamId(0),
|
|
quic::QUIC_STREAM_CANCELLED));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
// Request a stream and cancel it without releasing the stream.
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle =
|
|
session_->CreateHandle(destination_);
|
|
TestCompletionCallback callback;
|
|
ASSERT_EQ(OK, handle->RequestStream(/*requires_confirmation=*/false,
|
|
callback.callback(),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
handle.reset();
|
|
|
|
quic_data.Resume();
|
|
EXPECT_TRUE(quic_data.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data.AllWriteDataConsumed());
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, AsyncStreamRequest) {
|
|
MockQuicData quic_data(version_);
|
|
uint64_t packet_num = 1;
|
|
quic_data.AddWrite(SYNCHRONOUS,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
// The open stream limit is set to 50 by
|
|
// MockCryptoClientStream::SetConfigNegotiated() so when the 51st stream is
|
|
// requested, a STREAMS_BLOCKED will be sent, indicating that it's blocked
|
|
// at the limit of 50.
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeStreamsBlockedPacket(
|
|
packet_num++, true, 50,
|
|
/*unidirectional=*/false));
|
|
// Similarly, requesting the 52nd stream will also send a STREAMS_BLOCKED.
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeStreamsBlockedPacket(
|
|
packet_num++, true, 50,
|
|
/*unidirectional=*/false));
|
|
quic_data.AddWrite(
|
|
SYNCHRONOUS,
|
|
client_maker_.MakeRstPacket(packet_num++, true,
|
|
GetNthClientInitiatedBidirectionalStreamId(0),
|
|
quic::QUIC_STREAM_CANCELLED,
|
|
/*include_stop_sending_if_v99=*/false));
|
|
quic_data.AddWrite(
|
|
SYNCHRONOUS,
|
|
client_maker_.MakeRstPacket(packet_num++, true,
|
|
GetNthClientInitiatedBidirectionalStreamId(1),
|
|
quic::QUIC_STREAM_CANCELLED,
|
|
/*include_stop_sending_if_v99=*/false));
|
|
// After the STREAMS_BLOCKED is sent, receive a MAX_STREAMS to increase
|
|
// the limit to 100.
|
|
quic_data.AddRead(
|
|
ASYNC, server_maker_.MakeMaxStreamsPacket(1, true, 100,
|
|
/*unidirectional=*/false));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
// Open the maximum number of streams so that subsequent requests cannot
|
|
// proceed immediately.
|
|
EXPECT_EQ(GetMaxAllowedOutgoingBidirectionalStreams(), 50u);
|
|
for (size_t i = 0; i < 50; i++) {
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
|
|
}
|
|
EXPECT_EQ(session_->GetNumActiveStreams(), 50u);
|
|
|
|
// Request a stream and verify that it's pending.
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle =
|
|
session_->CreateHandle(destination_);
|
|
TestCompletionCallback callback;
|
|
ASSERT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle->RequestStream(/*requires_confirmation=*/false,
|
|
callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
// Request a second stream and verify that it's also pending.
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle2 =
|
|
session_->CreateHandle(destination_);
|
|
TestCompletionCallback callback2;
|
|
ASSERT_EQ(ERR_IO_PENDING,
|
|
handle2->RequestStream(/*requires_confirmation=*/false,
|
|
callback2.callback(),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
|
|
// Close two stream to open up sending credits.
|
|
quic::QuicRstStreamFrame rst(quic::kInvalidControlFrameId,
|
|
GetNthClientInitiatedBidirectionalStreamId(0),
|
|
quic::QUIC_STREAM_CANCELLED, 0);
|
|
session_->OnRstStream(rst);
|
|
quic::QuicRstStreamFrame rst2(quic::kInvalidControlFrameId,
|
|
GetNthClientInitiatedBidirectionalStreamId(1),
|
|
quic::QUIC_STREAM_CANCELLED, 0);
|
|
session_->OnRstStream(rst2);
|
|
// To close the streams completely, we need to also receive STOP_SENDING
|
|
// frames.
|
|
quic::QuicStopSendingFrame stop_sending(
|
|
quic::kInvalidControlFrameId,
|
|
GetNthClientInitiatedBidirectionalStreamId(0),
|
|
quic::QUIC_STREAM_CANCELLED);
|
|
session_->OnStopSendingFrame(stop_sending);
|
|
quic::QuicStopSendingFrame stop_sending2(
|
|
quic::kInvalidControlFrameId,
|
|
GetNthClientInitiatedBidirectionalStreamId(1),
|
|
quic::QUIC_STREAM_CANCELLED);
|
|
session_->OnStopSendingFrame(stop_sending2);
|
|
|
|
EXPECT_FALSE(callback.have_result());
|
|
EXPECT_FALSE(callback2.have_result());
|
|
|
|
// Pump the message loop to read the packet containing the MAX_STREAMS frame.
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
// Make sure that both requests were unblocked.
|
|
ASSERT_TRUE(callback.have_result());
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
EXPECT_TRUE(handle->ReleaseStream() != nullptr);
|
|
ASSERT_TRUE(callback2.have_result());
|
|
EXPECT_THAT(callback2.WaitForResult(), IsOk());
|
|
EXPECT_TRUE(handle2->ReleaseStream() != nullptr);
|
|
|
|
quic_data.Resume();
|
|
EXPECT_TRUE(quic_data.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data.AllWriteDataConsumed());
|
|
}
|
|
|
|
// Regression test for https://crbug.com/1021938.
|
|
// When the connection is closed, there may be tasks queued in the message loop
|
|
// to read the last packet, reading that packet should not crash.
|
|
TEST_P(QuicChromiumClientSessionTest, ReadAfterConnectionClose) {
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
// The open stream limit is set to 50 by
|
|
// MockCryptoClientStream::SetConfigNegotiated() so when the 51st stream is
|
|
// requested, a STREAMS_BLOCKED will be sent, indicating that it's blocked
|
|
// at the limit of 50.
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeStreamsBlockedPacket(
|
|
2, true, 50,
|
|
/*unidirectional=*/false));
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeStreamsBlockedPacket(
|
|
3, true, 50,
|
|
/*unidirectional=*/false));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
// This packet will be read after connection is closed.
|
|
quic_data.AddRead(
|
|
ASYNC,
|
|
server_maker_.MakeConnectionClosePacket(
|
|
1, false, quic::QUIC_CRYPTO_VERSION_NOT_SUPPORTED, "Time to panic!"));
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
// Open the maximum number of streams so that a subsequent request
|
|
// can not proceed immediately.
|
|
const size_t kMaxOpenStreams = GetMaxAllowedOutgoingBidirectionalStreams();
|
|
for (size_t i = 0; i < kMaxOpenStreams; i++) {
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
|
|
}
|
|
EXPECT_EQ(kMaxOpenStreams, session_->GetNumActiveStreams());
|
|
|
|
// Request two streams which will both be pending.
|
|
// In V99 each will generate a max stream id for each attempt.
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle =
|
|
session_->CreateHandle(destination_);
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle2 =
|
|
session_->CreateHandle(destination_);
|
|
|
|
ASSERT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle->RequestStream(
|
|
/*requires_confirmation=*/false,
|
|
base::BindOnce(&QuicChromiumClientSessionTest::ResetHandleOnError,
|
|
base::Unretained(this), &handle2),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
|
|
TestCompletionCallback callback2;
|
|
ASSERT_EQ(ERR_IO_PENDING,
|
|
handle2->RequestStream(/*requires_confirmation=*/false,
|
|
callback2.callback(),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
|
|
session_->connection()->CloseConnection(
|
|
quic::QUIC_NETWORK_IDLE_TIMEOUT, "Timed out",
|
|
quic::ConnectionCloseBehavior::SILENT_CLOSE);
|
|
|
|
// Pump the message loop to read the connection close packet.
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_FALSE(handle2.get());
|
|
quic_data.Resume();
|
|
EXPECT_TRUE(quic_data.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data.AllWriteDataConsumed());
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, ClosedWithAsyncStreamRequest) {
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
// The open stream limit is set to 50 by
|
|
// MockCryptoClientStream::SetConfigNegotiated() so when the 51st stream is
|
|
// requested, a STREAMS_BLOCKED will be sent, indicating that it's blocked
|
|
// at the limit of 50.
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeStreamsBlockedPacket(
|
|
2, true, 50,
|
|
/*unidirectional=*/false));
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeStreamsBlockedPacket(
|
|
3, true, 50,
|
|
/*unidirectional=*/false));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
// Open the maximum number of streams so that a subsequent request
|
|
// can not proceed immediately.
|
|
const size_t kMaxOpenStreams = GetMaxAllowedOutgoingBidirectionalStreams();
|
|
for (size_t i = 0; i < kMaxOpenStreams; i++) {
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
|
|
}
|
|
EXPECT_EQ(kMaxOpenStreams, session_->GetNumActiveStreams());
|
|
|
|
// Request two streams which will both be pending.
|
|
// In V99 each will generate a max stream id for each attempt.
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle =
|
|
session_->CreateHandle(destination_);
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle2 =
|
|
session_->CreateHandle(destination_);
|
|
|
|
ASSERT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle->RequestStream(
|
|
/*requires_confirmation=*/false,
|
|
base::BindOnce(&QuicChromiumClientSessionTest::ResetHandleOnError,
|
|
base::Unretained(this), &handle2),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
|
|
TestCompletionCallback callback2;
|
|
ASSERT_EQ(ERR_IO_PENDING,
|
|
handle2->RequestStream(/*requires_confirmation=*/false,
|
|
callback2.callback(),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
|
|
session_->connection()->CloseConnection(
|
|
quic::QUIC_NETWORK_IDLE_TIMEOUT, "Timed out",
|
|
quic::ConnectionCloseBehavior::SILENT_CLOSE);
|
|
|
|
// Pump the message loop to read the connection close packet.
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_FALSE(handle2.get());
|
|
quic_data.Resume();
|
|
EXPECT_TRUE(quic_data.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data.AllWriteDataConsumed());
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, CancelPendingStreamRequest) {
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
// The open stream limit is set to 50 by
|
|
// MockCryptoClientStream::SetConfigNegotiated() so when the 51st stream is
|
|
// requested, a STREAMS_BLOCKED will be sent.
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeStreamsBlockedPacket(
|
|
2, true, 50,
|
|
/*unidirectional=*/false));
|
|
// This node receives the RST_STREAM+STOP_SENDING, it responds
|
|
// with only a RST_STREAM.
|
|
quic_data.AddWrite(SYNCHRONOUS,
|
|
client_maker_.MakeRstPacket(
|
|
3, true, GetNthClientInitiatedBidirectionalStreamId(0),
|
|
quic::QUIC_STREAM_CANCELLED,
|
|
/*include_stop_sending_if_v99=*/false));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
// Open the maximum number of streams so that a subsequent request
|
|
// can not proceed immediately.
|
|
const size_t kMaxOpenStreams = GetMaxAllowedOutgoingBidirectionalStreams();
|
|
for (size_t i = 0; i < kMaxOpenStreams; i++) {
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
|
|
}
|
|
EXPECT_EQ(kMaxOpenStreams, session_->GetNumActiveStreams());
|
|
|
|
// Request a stream and verify that it's pending.
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle =
|
|
session_->CreateHandle(destination_);
|
|
TestCompletionCallback callback;
|
|
ASSERT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle->RequestStream(/*requires_confirmation=*/false,
|
|
callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
|
|
// Cancel the pending stream request.
|
|
handle.reset();
|
|
|
|
// Close a stream and ensure that no new stream is created.
|
|
quic::QuicRstStreamFrame rst(quic::kInvalidControlFrameId,
|
|
GetNthClientInitiatedBidirectionalStreamId(0),
|
|
quic::QUIC_STREAM_CANCELLED, 0);
|
|
session_->OnRstStream(rst);
|
|
// We require a STOP_SENDING as well as a RESET_STREAM to fully close the
|
|
// stream.
|
|
quic::QuicStopSendingFrame stop_sending(
|
|
quic::kInvalidControlFrameId,
|
|
GetNthClientInitiatedBidirectionalStreamId(0),
|
|
quic::QUIC_STREAM_CANCELLED);
|
|
session_->OnStopSendingFrame(stop_sending);
|
|
EXPECT_EQ(kMaxOpenStreams - 1, session_->GetNumActiveStreams());
|
|
|
|
quic_data.Resume();
|
|
EXPECT_TRUE(quic_data.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data.AllWriteDataConsumed());
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, ConnectionCloseBeforeStreamRequest) {
|
|
MockQuicData quic_data(version_);
|
|
int packet_num = 1;
|
|
quic_data.AddWrite(SYNCHRONOUS,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
quic_data.AddWrite(SYNCHRONOUS,
|
|
client_maker_.MakePingPacket(packet_num++, true));
|
|
quic_data.AddRead(
|
|
ASYNC,
|
|
server_maker_.MakeConnectionClosePacket(
|
|
1, false, quic::QUIC_CRYPTO_VERSION_NOT_SUPPORTED, "Time to panic!"));
|
|
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
// Send a ping so that client has outgoing traffic before receiving packets.
|
|
session_->connection()->SendPing();
|
|
|
|
// Pump the message loop to read the connection close packet.
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
// Request a stream and verify that it failed.
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle =
|
|
session_->CreateHandle(destination_);
|
|
TestCompletionCallback callback;
|
|
ASSERT_EQ(
|
|
ERR_CONNECTION_CLOSED,
|
|
handle->RequestStream(/*requires_confirmation=*/false,
|
|
callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
|
|
EXPECT_TRUE(quic_data.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data.AllWriteDataConsumed());
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, ConnectionCloseBeforeHandshakeConfirmed) {
|
|
if (version_.UsesTls()) {
|
|
// TODO(nharper, b/112643533): Figure out why this test fails when TLS is
|
|
// enabled and fix it.
|
|
return;
|
|
}
|
|
|
|
// Force the connection close packet to use long headers with connection ID.
|
|
server_maker_.SetEncryptionLevel(quic::ENCRYPTION_INITIAL);
|
|
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(
|
|
ASYNC,
|
|
server_maker_.MakeConnectionClosePacket(
|
|
1, false, quic::QUIC_CRYPTO_VERSION_NOT_SUPPORTED, "Time to panic!"));
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
|
|
// Request a stream and verify that it's pending.
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle =
|
|
session_->CreateHandle(destination_);
|
|
TestCompletionCallback callback;
|
|
ASSERT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle->RequestStream(/*requires_confirmation=*/true, callback.callback(),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
|
|
// Close the connection and verify that the StreamRequest completes with
|
|
// an error.
|
|
quic_data.Resume();
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
EXPECT_THAT(callback.WaitForResult(), IsError(ERR_CONNECTION_CLOSED));
|
|
|
|
EXPECT_TRUE(quic_data.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data.AllWriteDataConsumed());
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, ConnectionCloseWithPendingStreamRequest) {
|
|
MockQuicData quic_data(version_);
|
|
int packet_num = 1;
|
|
quic_data.AddWrite(SYNCHRONOUS,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
quic_data.AddWrite(SYNCHRONOUS,
|
|
client_maker_.MakePingPacket(packet_num++, true));
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeStreamsBlockedPacket(
|
|
packet_num++, true, 50,
|
|
/*unidirectional=*/false));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(
|
|
ASYNC,
|
|
server_maker_.MakeConnectionClosePacket(
|
|
1, false, quic::QUIC_CRYPTO_VERSION_NOT_SUPPORTED, "Time to panic!"));
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
// Send a ping so that client has outgoing traffic before receiving packets.
|
|
session_->connection()->SendPing();
|
|
|
|
// Open the maximum number of streams so that a subsequent request
|
|
// can not proceed immediately.
|
|
const size_t kMaxOpenStreams = GetMaxAllowedOutgoingBidirectionalStreams();
|
|
for (size_t i = 0; i < kMaxOpenStreams; i++) {
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
|
|
}
|
|
EXPECT_EQ(kMaxOpenStreams, session_->GetNumActiveStreams());
|
|
|
|
// Request a stream and verify that it's pending.
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle =
|
|
session_->CreateHandle(destination_);
|
|
TestCompletionCallback callback;
|
|
ASSERT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle->RequestStream(/*requires_confirmation=*/false,
|
|
callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
|
|
// Close the connection and verify that the StreamRequest completes with
|
|
// an error.
|
|
quic_data.Resume();
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
EXPECT_THAT(callback.WaitForResult(), IsError(ERR_CONNECTION_CLOSED));
|
|
|
|
EXPECT_TRUE(quic_data.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data.AllWriteDataConsumed());
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, MaxNumStreams) {
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
// Initial configuration is 50 dynamic streams. Taking into account
|
|
// the static stream (headers), expect to block on when hitting the limit
|
|
// of 50 streams
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeStreamsBlockedPacket(
|
|
2, true, 50,
|
|
/*unidirectional=*/false));
|
|
quic_data.AddWrite(SYNCHRONOUS,
|
|
client_maker_.MakeRstPacket(
|
|
3, true, GetNthClientInitiatedBidirectionalStreamId(0),
|
|
quic::QUIC_RST_ACKNOWLEDGEMENT));
|
|
// For the second CreateOutgoingStream that fails because of hitting the
|
|
// stream count limit.
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeStreamsBlockedPacket(
|
|
4, true, 50,
|
|
/*unidirectional=*/false));
|
|
quic_data.AddRead(
|
|
ASYNC, server_maker_.MakeMaxStreamsPacket(1, true, 50 + 2,
|
|
/*unidirectional=*/false));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
const size_t kMaxOpenStreams = GetMaxAllowedOutgoingBidirectionalStreams();
|
|
|
|
std::vector<QuicChromiumClientStream*> streams;
|
|
for (size_t i = 0; i < kMaxOpenStreams; i++) {
|
|
QuicChromiumClientStream* stream =
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
|
|
EXPECT_TRUE(stream);
|
|
streams.push_back(stream);
|
|
}
|
|
// This stream, the 51st dynamic stream, can not be opened.
|
|
EXPECT_FALSE(
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get()));
|
|
|
|
EXPECT_EQ(kMaxOpenStreams, session_->GetNumActiveStreams());
|
|
|
|
// Close a stream and ensure I can now open a new one.
|
|
quic::QuicStreamId stream_id = streams[0]->id();
|
|
session_->ResetStream(stream_id, quic::QUIC_RST_ACKNOWLEDGEMENT);
|
|
|
|
// Pump data, bringing in the max-stream-id
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
EXPECT_FALSE(
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get()));
|
|
quic::QuicRstStreamFrame rst1(quic::kInvalidControlFrameId, stream_id,
|
|
quic::QUIC_STREAM_NO_ERROR, 0);
|
|
session_->OnRstStream(rst1);
|
|
EXPECT_EQ(kMaxOpenStreams - 1, session_->GetNumActiveStreams());
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_TRUE(
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get()));
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, PushStreamTimedOutNoResponse) {
|
|
MockQuicData quic_data(version_);
|
|
int packet_num = 1;
|
|
quic_data.AddWrite(ASYNC,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
quic_data.AddWrite(ASYNC, client_maker_.MakeRstPacket(
|
|
packet_num++, true,
|
|
GetNthServerInitiatedUnidirectionalStreamId(0),
|
|
quic::QUIC_PUSH_STREAM_TIMED_OUT));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
Initialize();
|
|
|
|
ProofVerifyDetailsChromium details;
|
|
details.cert_verify_result.verified_cert =
|
|
ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
|
|
ASSERT_TRUE(details.cert_verify_result.verified_cert.get());
|
|
|
|
CompleteCryptoHandshake();
|
|
session_->OnProofVerifyDetailsAvailable(details);
|
|
|
|
QuicChromiumClientStream* stream =
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
|
|
EXPECT_TRUE(stream);
|
|
|
|
spdy::Http2HeaderBlock promise_headers;
|
|
promise_headers[":method"] = "GET";
|
|
promise_headers[":authority"] = "www.example.org";
|
|
promise_headers[":scheme"] = "https";
|
|
promise_headers[":path"] = "/pushed.jpg";
|
|
|
|
// Receive a PUSH PROMISE from the server.
|
|
EXPECT_TRUE(session_->HandlePromised(
|
|
stream->id(), GetNthServerInitiatedUnidirectionalStreamId(0),
|
|
promise_headers));
|
|
|
|
quic::QuicClientPromisedInfo* promised =
|
|
session_->GetPromisedById(GetNthServerInitiatedUnidirectionalStreamId(0));
|
|
EXPECT_TRUE(promised);
|
|
// Fire alarm to time out the push stream.
|
|
alarm_factory_.FireAlarm(
|
|
quic::test::QuicClientPromisedInfoPeer::GetAlarm(promised));
|
|
EXPECT_FALSE(
|
|
session_->GetPromisedByUrl("https://www.example.org/pushed.jpg"));
|
|
EXPECT_EQ(0u,
|
|
QuicChromiumClientSessionPeer::GetPushedBytesCount(session_.get()));
|
|
EXPECT_EQ(0u, QuicChromiumClientSessionPeer::GetPushedAndUnclaimedBytesCount(
|
|
session_.get()));
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, PushStreamTimedOutWithResponse) {
|
|
MockQuicData quic_data(version_);
|
|
int packet_num = 1;
|
|
quic_data.AddWrite(ASYNC,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
quic_data.AddWrite(ASYNC, client_maker_.MakeRstPacket(
|
|
packet_num++, true,
|
|
GetNthServerInitiatedUnidirectionalStreamId(0),
|
|
quic::QUIC_PUSH_STREAM_TIMED_OUT));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
Initialize();
|
|
|
|
ProofVerifyDetailsChromium details;
|
|
details.cert_verify_result.verified_cert =
|
|
ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
|
|
ASSERT_TRUE(details.cert_verify_result.verified_cert.get());
|
|
|
|
CompleteCryptoHandshake();
|
|
session_->OnProofVerifyDetailsAvailable(details);
|
|
|
|
QuicChromiumClientStream* stream =
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
|
|
EXPECT_TRUE(stream);
|
|
|
|
spdy::Http2HeaderBlock promise_headers;
|
|
promise_headers[":method"] = "GET";
|
|
promise_headers[":authority"] = "www.example.org";
|
|
promise_headers[":scheme"] = "https";
|
|
promise_headers[":path"] = "/pushed.jpg";
|
|
|
|
session_->GetOrCreateStream(GetNthServerInitiatedUnidirectionalStreamId(0));
|
|
// Receive a PUSH PROMISE from the server.
|
|
EXPECT_TRUE(session_->HandlePromised(
|
|
stream->id(), GetNthServerInitiatedUnidirectionalStreamId(0),
|
|
promise_headers));
|
|
session_->OnInitialHeadersComplete(
|
|
GetNthServerInitiatedUnidirectionalStreamId(0), spdy::Http2HeaderBlock());
|
|
// Read data on the pushed stream.
|
|
quic::QuicStreamFrame data(GetNthServerInitiatedUnidirectionalStreamId(0),
|
|
false, 0, absl::string_view("SP"));
|
|
session_->OnStreamFrame(data);
|
|
|
|
quic::QuicClientPromisedInfo* promised =
|
|
session_->GetPromisedById(GetNthServerInitiatedUnidirectionalStreamId(0));
|
|
EXPECT_TRUE(promised);
|
|
// Fire alarm to time out the push stream.
|
|
alarm_factory_.FireAlarm(
|
|
quic::test::QuicClientPromisedInfoPeer::GetAlarm(promised));
|
|
EXPECT_EQ(2u,
|
|
QuicChromiumClientSessionPeer::GetPushedBytesCount(session_.get()));
|
|
EXPECT_EQ(2u, QuicChromiumClientSessionPeer::GetPushedAndUnclaimedBytesCount(
|
|
session_.get()));
|
|
}
|
|
|
|
// Regression test for crbug.com/968621.
|
|
TEST_P(QuicChromiumClientSessionTest, PendingStreamOnRst) {
|
|
MockQuicData quic_data(version_);
|
|
int packet_num = 1;
|
|
quic_data.AddWrite(ASYNC,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
quic_data.AddWrite(ASYNC, client_maker_.MakeRstPacket(
|
|
packet_num++, true,
|
|
GetNthServerInitiatedUnidirectionalStreamId(0),
|
|
quic::QUIC_RST_ACKNOWLEDGEMENT));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
quic::QuicStreamFrame data(GetNthServerInitiatedUnidirectionalStreamId(0),
|
|
false, 1, absl::string_view("SP"));
|
|
session_->OnStreamFrame(data);
|
|
EXPECT_EQ(0u, session_->GetNumActiveStreams());
|
|
quic::QuicRstStreamFrame rst(quic::kInvalidControlFrameId,
|
|
GetNthServerInitiatedUnidirectionalStreamId(0),
|
|
quic::QUIC_STREAM_CANCELLED, 0);
|
|
session_->OnRstStream(rst);
|
|
}
|
|
|
|
// Regression test for crbug.com/971361.
|
|
TEST_P(QuicChromiumClientSessionTest, ClosePendingStream) {
|
|
MockQuicData quic_data(version_);
|
|
int packet_num = 1;
|
|
quic_data.AddWrite(ASYNC,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
quic_data.AddWrite(ASYNC, client_maker_.MakeRstPacket(
|
|
packet_num++, true,
|
|
GetNthServerInitiatedUnidirectionalStreamId(0),
|
|
quic::QUIC_RST_ACKNOWLEDGEMENT));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
quic::QuicStreamId id = GetNthServerInitiatedUnidirectionalStreamId(0);
|
|
quic::QuicStreamFrame data(id, false, 1, absl::string_view("SP"));
|
|
session_->OnStreamFrame(data);
|
|
EXPECT_EQ(0u, session_->GetNumActiveStreams());
|
|
session_->ResetStream(id, quic::QUIC_STREAM_NO_ERROR);
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, CancelPushWhenPendingValidation) {
|
|
MockQuicData quic_data(version_);
|
|
int packet_num = 1;
|
|
quic_data.AddWrite(ASYNC,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
quic_data.AddWrite(ASYNC, client_maker_.MakeRstPacket(
|
|
packet_num++, true,
|
|
GetNthClientInitiatedBidirectionalStreamId(0),
|
|
quic::QUIC_RST_ACKNOWLEDGEMENT));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
Initialize();
|
|
|
|
ProofVerifyDetailsChromium details;
|
|
details.cert_verify_result.verified_cert =
|
|
ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
|
|
ASSERT_TRUE(details.cert_verify_result.verified_cert.get());
|
|
|
|
CompleteCryptoHandshake();
|
|
session_->OnProofVerifyDetailsAvailable(details);
|
|
|
|
QuicChromiumClientStream* stream =
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
|
|
EXPECT_TRUE(stream);
|
|
|
|
spdy::Http2HeaderBlock promise_headers;
|
|
promise_headers[":method"] = "GET";
|
|
promise_headers[":authority"] = "www.example.org";
|
|
promise_headers[":scheme"] = "https";
|
|
promise_headers[":path"] = "/pushed.jpg";
|
|
|
|
// Receive a PUSH PROMISE from the server.
|
|
EXPECT_TRUE(session_->HandlePromised(
|
|
stream->id(), GetNthServerInitiatedUnidirectionalStreamId(0),
|
|
promise_headers));
|
|
|
|
quic::QuicClientPromisedInfo* promised =
|
|
session_->GetPromisedById(GetNthServerInitiatedUnidirectionalStreamId(0));
|
|
EXPECT_TRUE(promised);
|
|
|
|
// Initiate rendezvous.
|
|
spdy::Http2HeaderBlock client_request = promise_headers.Clone();
|
|
quic::test::TestPushPromiseDelegate delegate(/*match=*/true);
|
|
promised->HandleClientRequest(client_request, &delegate);
|
|
|
|
// Cancel the push before receiving the response to the pushed request.
|
|
GURL pushed_url("https://www.example.org/pushed.jpg");
|
|
test_push_delegate_.CancelPush(pushed_url);
|
|
EXPECT_TRUE(session_->GetPromisedByUrl(pushed_url.spec()));
|
|
|
|
// Reset the stream now before tear down.
|
|
session_->ResetStream(GetNthClientInitiatedBidirectionalStreamId(0),
|
|
quic::QUIC_RST_ACKNOWLEDGEMENT);
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, CancelPushBeforeReceivingResponse) {
|
|
MockQuicData quic_data(version_);
|
|
int packet_num = 1;
|
|
quic_data.AddWrite(ASYNC,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
quic_data.AddWrite(ASYNC, client_maker_.MakeRstPacket(
|
|
packet_num++, true,
|
|
GetNthServerInitiatedUnidirectionalStreamId(0),
|
|
quic::QUIC_STREAM_CANCELLED));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
Initialize();
|
|
|
|
ProofVerifyDetailsChromium details;
|
|
details.cert_verify_result.verified_cert =
|
|
ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
|
|
ASSERT_TRUE(details.cert_verify_result.verified_cert.get());
|
|
|
|
CompleteCryptoHandshake();
|
|
session_->OnProofVerifyDetailsAvailable(details);
|
|
|
|
QuicChromiumClientStream* stream =
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
|
|
EXPECT_TRUE(stream);
|
|
|
|
spdy::Http2HeaderBlock promise_headers;
|
|
promise_headers[":method"] = "GET";
|
|
promise_headers[":authority"] = "www.example.org";
|
|
promise_headers[":scheme"] = "https";
|
|
promise_headers[":path"] = "/pushed.jpg";
|
|
|
|
// Receive a PUSH PROMISE from the server.
|
|
EXPECT_TRUE(session_->HandlePromised(
|
|
stream->id(), GetNthServerInitiatedUnidirectionalStreamId(0),
|
|
promise_headers));
|
|
|
|
quic::QuicClientPromisedInfo* promised =
|
|
session_->GetPromisedById(GetNthServerInitiatedUnidirectionalStreamId(0));
|
|
EXPECT_TRUE(promised);
|
|
// Cancel the push before receiving the response to the pushed request.
|
|
GURL pushed_url("https://www.example.org/pushed.jpg");
|
|
test_push_delegate_.CancelPush(pushed_url);
|
|
|
|
EXPECT_FALSE(session_->GetPromisedByUrl(pushed_url.spec()));
|
|
EXPECT_EQ(0u,
|
|
QuicChromiumClientSessionPeer::GetPushedBytesCount(session_.get()));
|
|
EXPECT_EQ(0u, QuicChromiumClientSessionPeer::GetPushedAndUnclaimedBytesCount(
|
|
session_.get()));
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, CancelPushAfterReceivingResponse) {
|
|
MockQuicData quic_data(version_);
|
|
int packet_num = 1;
|
|
quic_data.AddWrite(ASYNC,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
quic_data.AddWrite(ASYNC, client_maker_.MakeRstPacket(
|
|
packet_num++, true,
|
|
GetNthServerInitiatedUnidirectionalStreamId(0),
|
|
quic::QUIC_STREAM_CANCELLED));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
|
|
ProofVerifyDetailsChromium details;
|
|
details.cert_verify_result.verified_cert =
|
|
ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
|
|
ASSERT_TRUE(details.cert_verify_result.verified_cert.get());
|
|
|
|
CompleteCryptoHandshake();
|
|
session_->OnProofVerifyDetailsAvailable(details);
|
|
|
|
QuicChromiumClientStream* stream =
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
|
|
EXPECT_TRUE(stream);
|
|
|
|
spdy::Http2HeaderBlock promise_headers;
|
|
promise_headers[":method"] = "GET";
|
|
promise_headers[":authority"] = "www.example.org";
|
|
promise_headers[":scheme"] = "https";
|
|
promise_headers[":path"] = "/pushed.jpg";
|
|
|
|
session_->GetOrCreateStream(GetNthServerInitiatedUnidirectionalStreamId(0));
|
|
// Receive a PUSH PROMISE from the server.
|
|
EXPECT_TRUE(session_->HandlePromised(
|
|
stream->id(), GetNthServerInitiatedUnidirectionalStreamId(0),
|
|
promise_headers));
|
|
session_->OnInitialHeadersComplete(
|
|
GetNthServerInitiatedUnidirectionalStreamId(0), spdy::Http2HeaderBlock());
|
|
// Read data on the pushed stream.
|
|
quic::QuicStreamFrame data(GetNthServerInitiatedUnidirectionalStreamId(0),
|
|
false, 0, absl::string_view("SP"));
|
|
session_->OnStreamFrame(data);
|
|
|
|
quic::QuicClientPromisedInfo* promised =
|
|
session_->GetPromisedById(GetNthServerInitiatedUnidirectionalStreamId(0));
|
|
EXPECT_TRUE(promised);
|
|
// Cancel the push after receiving data on the push stream.
|
|
GURL pushed_url("https://www.example.org/pushed.jpg");
|
|
test_push_delegate_.CancelPush(pushed_url);
|
|
|
|
EXPECT_FALSE(session_->GetPromisedByUrl(pushed_url.spec()));
|
|
EXPECT_EQ(2u,
|
|
QuicChromiumClientSessionPeer::GetPushedBytesCount(session_.get()));
|
|
EXPECT_EQ(2u, QuicChromiumClientSessionPeer::GetPushedAndUnclaimedBytesCount(
|
|
session_.get()));
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, MaxNumStreamsViaRequest) {
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeStreamsBlockedPacket(
|
|
2, true, 50,
|
|
/*unidirectional=*/false));
|
|
quic_data.AddWrite(SYNCHRONOUS,
|
|
client_maker_.MakeRstPacket(
|
|
3, true, GetNthClientInitiatedBidirectionalStreamId(0),
|
|
quic::QUIC_RST_ACKNOWLEDGEMENT));
|
|
quic_data.AddRead(
|
|
ASYNC, server_maker_.MakeMaxStreamsPacket(1, true, 52,
|
|
/*unidirectional=*/false));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
const size_t kMaxOpenStreams = GetMaxAllowedOutgoingBidirectionalStreams();
|
|
std::vector<QuicChromiumClientStream*> streams;
|
|
for (size_t i = 0; i < kMaxOpenStreams; i++) {
|
|
QuicChromiumClientStream* stream =
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
|
|
EXPECT_TRUE(stream);
|
|
streams.push_back(stream);
|
|
}
|
|
|
|
std::unique_ptr<QuicChromiumClientSession::Handle> handle =
|
|
session_->CreateHandle(destination_);
|
|
TestCompletionCallback callback;
|
|
ASSERT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle->RequestStream(/*requires_confirmation=*/false,
|
|
callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
|
|
// Close a stream and ensure I can now open a new one.
|
|
quic::QuicStreamId stream_id = streams[0]->id();
|
|
session_->ResetStream(stream_id, quic::QUIC_RST_ACKNOWLEDGEMENT);
|
|
quic::QuicRstStreamFrame rst1(quic::kInvalidControlFrameId, stream_id,
|
|
quic::QUIC_STREAM_NO_ERROR, 0);
|
|
session_->OnRstStream(rst1);
|
|
// Pump data, bringing in the max-stream-id
|
|
base::RunLoop().RunUntilIdle();
|
|
ASSERT_TRUE(callback.have_result());
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
EXPECT_TRUE(handle->ReleaseStream() != nullptr);
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, GoAwayReceived) {
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
// After receiving a GoAway, I should no longer be able to create outgoing
|
|
// streams.
|
|
session_->OnHttp3GoAway(0);
|
|
EXPECT_EQ(nullptr, QuicChromiumClientSessionPeer::CreateOutgoingStream(
|
|
session_.get()));
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, CanPool) {
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
Initialize();
|
|
// Load a cert that is valid for:
|
|
// www.example.org
|
|
// mail.example.org
|
|
// www.example.com
|
|
|
|
ProofVerifyDetailsChromium details;
|
|
details.cert_verify_result.verified_cert =
|
|
ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
|
|
ASSERT_TRUE(details.cert_verify_result.verified_cert.get());
|
|
|
|
CompleteCryptoHandshake();
|
|
session_->OnProofVerifyDetailsAvailable(details);
|
|
|
|
EXPECT_TRUE(session_->CanPool(
|
|
"www.example.org",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
|
|
NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
EXPECT_FALSE(session_->CanPool(
|
|
"www.example.org",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_ENABLED, SocketTag(),
|
|
NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
EXPECT_FALSE(session_->CanPool(
|
|
"www.example.org",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
|
|
NetworkAnonymizationKey(), SecureDnsPolicy::kDisable,
|
|
/*require_dns_https_alpn=*/false)));
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
SocketTag tag1(SocketTag::UNSET_UID, 0x12345678);
|
|
SocketTag tag2(getuid(), 0x87654321);
|
|
EXPECT_FALSE(session_->CanPool(
|
|
"www.example.org",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, tag1,
|
|
NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
EXPECT_FALSE(session_->CanPool(
|
|
"www.example.org",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, tag2,
|
|
NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
#endif
|
|
EXPECT_TRUE(session_->CanPool(
|
|
"mail.example.org",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
|
|
NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
EXPECT_TRUE(session_->CanPool(
|
|
"mail.example.com",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
|
|
NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
EXPECT_FALSE(session_->CanPool(
|
|
"mail.google.com",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
|
|
NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
|
|
const SchemefulSite kSiteFoo(GURL("http://foo.test/"));
|
|
|
|
// Check that NetworkAnonymizationKey is respected when feature is enabled.
|
|
{
|
|
base::test::ScopedFeatureList feature_list;
|
|
feature_list.InitAndDisableFeature(
|
|
features::kPartitionConnectionsByNetworkIsolationKey);
|
|
EXPECT_TRUE(session_->CanPool(
|
|
"mail.example.com",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
|
|
NetworkAnonymizationKey::CreateSameSite(kSiteFoo),
|
|
SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
}
|
|
{
|
|
base::test::ScopedFeatureList feature_list;
|
|
feature_list.InitAndEnableFeature(
|
|
features::kPartitionConnectionsByNetworkIsolationKey);
|
|
EXPECT_FALSE(session_->CanPool(
|
|
"mail.example.com",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
|
|
NetworkAnonymizationKey::CreateSameSite(kSiteFoo),
|
|
SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
}
|
|
}
|
|
|
|
// Much as above, but uses a non-empty NetworkAnonymizationKey.
|
|
TEST_P(QuicChromiumClientSessionTest, CanPoolWithNetworkAnonymizationKey) {
|
|
base::test::ScopedFeatureList feature_list;
|
|
feature_list.InitAndEnableFeature(
|
|
features::kPartitionConnectionsByNetworkIsolationKey);
|
|
|
|
const SchemefulSite kSiteFoo(GURL("http://foo.test/"));
|
|
const SchemefulSite kSiteBar(GURL("http://bar.test/"));
|
|
const auto kNetworkAnonymizationKey1 =
|
|
NetworkAnonymizationKey::CreateSameSite(kSiteFoo);
|
|
const auto kNetworkAnonymizationKey2 =
|
|
NetworkAnonymizationKey::CreateSameSite(kSiteBar);
|
|
|
|
session_key_ =
|
|
QuicSessionKey(kServerHostname, kServerPort, PRIVACY_MODE_DISABLED,
|
|
SocketTag(), kNetworkAnonymizationKey1,
|
|
SecureDnsPolicy::kAllow, /*require_dns_https_alpn=*/false);
|
|
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
Initialize();
|
|
// Load a cert that is valid for:
|
|
// www.example.org
|
|
// mail.example.org
|
|
// www.example.com
|
|
|
|
ProofVerifyDetailsChromium details;
|
|
details.cert_verify_result.verified_cert =
|
|
ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
|
|
ASSERT_TRUE(details.cert_verify_result.verified_cert.get());
|
|
|
|
CompleteCryptoHandshake();
|
|
session_->OnProofVerifyDetailsAvailable(details);
|
|
|
|
EXPECT_TRUE(session_->CanPool(
|
|
"www.example.org",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
|
|
kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
EXPECT_FALSE(session_->CanPool(
|
|
"www.example.org",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_ENABLED, SocketTag(),
|
|
kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
SocketTag tag1(SocketTag::UNSET_UID, 0x12345678);
|
|
SocketTag tag2(getuid(), 0x87654321);
|
|
EXPECT_FALSE(session_->CanPool(
|
|
"www.example.org",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, tag1,
|
|
kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
EXPECT_FALSE(session_->CanPool(
|
|
"www.example.org",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, tag2,
|
|
kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
#endif
|
|
EXPECT_TRUE(session_->CanPool(
|
|
"mail.example.org",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
|
|
kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
EXPECT_TRUE(session_->CanPool(
|
|
"mail.example.com",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
|
|
kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
EXPECT_FALSE(session_->CanPool(
|
|
"mail.google.com",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
|
|
kNetworkAnonymizationKey1, SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
|
|
EXPECT_FALSE(session_->CanPool(
|
|
"mail.example.com",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
|
|
kNetworkAnonymizationKey2, SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
EXPECT_FALSE(session_->CanPool(
|
|
"mail.example.com",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
|
|
NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, ConnectionNotPooledWithDifferentPin) {
|
|
base::test::ScopedFeatureList scoped_feature_list_;
|
|
scoped_feature_list_.InitAndEnableFeature(
|
|
net::features::kStaticKeyPinningEnforcement);
|
|
// Configure the TransportSecurityStateSource so that kPreloadedPKPHost will
|
|
// have static PKP pins set.
|
|
ScopedTransportSecurityStateSource scoped_security_state_source;
|
|
|
|
// |net::test_default::kHSTSSource| defines pins for kPreloadedPKPHost.
|
|
// (This hostname must be in the spdy_pooling.pem SAN.)
|
|
const char kPreloadedPKPHost[] = "www.example.org";
|
|
// A hostname without any static state. (This hostname isn't in
|
|
// spdy_pooling.pem SAN, but that's okay because the
|
|
// ProofVerifyDetailsChromium are faked.)
|
|
const char kNoPinsHost[] = "no-pkp.example.org";
|
|
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
Initialize();
|
|
|
|
transport_security_state_->EnableStaticPinsForTesting();
|
|
transport_security_state_->SetPinningListAlwaysTimelyForTesting(true);
|
|
|
|
ProofVerifyDetailsChromium details;
|
|
details.cert_verify_result.verified_cert =
|
|
ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
|
|
details.cert_verify_result.is_issued_by_known_root = true;
|
|
uint8_t bad_pin = 3;
|
|
details.cert_verify_result.public_key_hashes.push_back(
|
|
GetTestHashValue(bad_pin));
|
|
|
|
ASSERT_TRUE(details.cert_verify_result.verified_cert.get());
|
|
|
|
CompleteCryptoHandshake();
|
|
session_->OnProofVerifyDetailsAvailable(details);
|
|
QuicChromiumClientSessionPeer::SetHostname(session_.get(), kNoPinsHost);
|
|
|
|
EXPECT_FALSE(session_->CanPool(
|
|
kPreloadedPKPHost,
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
|
|
NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, ConnectionPooledWithMatchingPin) {
|
|
ScopedTransportSecurityStateSource scoped_security_state_source;
|
|
|
|
MockQuicData quic_data(version_);
|
|
quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
Initialize();
|
|
|
|
transport_security_state_->EnableStaticPinsForTesting();
|
|
|
|
ProofVerifyDetailsChromium details;
|
|
details.cert_verify_result.verified_cert =
|
|
ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
|
|
details.cert_verify_result.is_issued_by_known_root = true;
|
|
HashValue primary_pin(HASH_VALUE_SHA256);
|
|
EXPECT_TRUE(primary_pin.FromString(
|
|
"sha256/Nn8jk5By4Vkq6BeOVZ7R7AC6XUUBZsWmUbJR1f1Y5FY="));
|
|
details.cert_verify_result.public_key_hashes.push_back(primary_pin);
|
|
|
|
ASSERT_TRUE(details.cert_verify_result.verified_cert.get());
|
|
|
|
CompleteCryptoHandshake();
|
|
session_->OnProofVerifyDetailsAvailable(details);
|
|
QuicChromiumClientSessionPeer::SetHostname(session_.get(), "www.example.org");
|
|
|
|
EXPECT_TRUE(session_->CanPool(
|
|
"mail.example.org",
|
|
QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
|
|
NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
|
|
/*require_dns_https_alpn=*/false)));
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, MigrateToSocket) {
|
|
SetIetfConnectionMigrationFlagsAndConnectionOptions();
|
|
|
|
quic::QuicConnectionId cid_on_new_path =
|
|
quic::test::TestConnectionId(12345678);
|
|
MockQuicData quic_data(version_);
|
|
int packet_num = 1;
|
|
int peer_packet_num = 1;
|
|
socket_data_.reset();
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddWrite(ASYNC,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
quic_data.AddRead(
|
|
ASYNC, server_maker_.MakeNewConnectionIdPacket(
|
|
peer_packet_num++, /*include_version=*/false, cid_on_new_path,
|
|
/*sequence_number=*/1u,
|
|
/*retire_prior_to=*/0u));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
// Make new connection ID available after handshake completion.
|
|
quic_data.Resume();
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
char data[] = "ABCD";
|
|
MockQuicData quic_data2(version_);
|
|
client_maker_.set_connection_id(cid_on_new_path);
|
|
quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
|
|
quic_data2.AddWrite(SYNCHRONOUS, client_maker_.MakeAckAndPingPacket(
|
|
packet_num++, /*include_version=*/false,
|
|
/*largest_received=*/peer_packet_num - 1,
|
|
/*smallest_received=*/1));
|
|
quic_data2.AddWrite(
|
|
SYNCHRONOUS,
|
|
client_maker_.MakeDataPacket(
|
|
packet_num++, GetNthClientInitiatedBidirectionalStreamId(0), true,
|
|
false, absl::string_view(data)));
|
|
quic_data2.AddSocketDataToFactory(&socket_factory_);
|
|
// Create connected socket.
|
|
std::unique_ptr<DatagramClientSocket> new_socket =
|
|
socket_factory_.CreateDatagramClientSocket(DatagramSocket::RANDOM_BIND,
|
|
NetLog::Get(), NetLogSource());
|
|
EXPECT_THAT(new_socket->Connect(kIpEndPoint), IsOk());
|
|
|
|
// Create reader and writer.
|
|
auto new_reader = std::make_unique<QuicChromiumPacketReader>(
|
|
new_socket.get(), &clock_, session_.get(), kQuicYieldAfterPacketsRead,
|
|
quic::QuicTime::Delta::FromMilliseconds(
|
|
kQuicYieldAfterDurationMilliseconds),
|
|
net_log_with_source_);
|
|
new_reader->StartReading();
|
|
std::unique_ptr<QuicChromiumPacketWriter> new_writer(
|
|
CreateQuicChromiumPacketWriter(new_socket.get(), session_.get()));
|
|
|
|
IPEndPoint local_address;
|
|
new_socket->GetLocalAddress(&local_address);
|
|
IPEndPoint peer_address;
|
|
new_socket->GetPeerAddress(&peer_address);
|
|
// Migrate session.
|
|
EXPECT_TRUE(session_->MigrateToSocket(
|
|
ToQuicSocketAddress(local_address), ToQuicSocketAddress(peer_address),
|
|
std::move(new_socket), std::move(new_reader), std::move(new_writer)));
|
|
// Spin message loop to complete migration.
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
// Write data to session.
|
|
QuicChromiumClientStream* stream =
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get());
|
|
quic::test::QuicStreamPeer::SendBuffer(stream).SaveStreamData(data);
|
|
quic::test::QuicStreamPeer::SetStreamBytesWritten(4, stream);
|
|
session_->WritevData(stream->id(), 4, 0, quic::NO_FIN,
|
|
quic::NOT_RETRANSMISSION,
|
|
quic::ENCRYPTION_FORWARD_SECURE);
|
|
|
|
EXPECT_TRUE(quic_data2.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, MigrateToSocketMaxReaders) {
|
|
SetIetfConnectionMigrationFlagsAndConnectionOptions();
|
|
MockQuicData quic_data(version_);
|
|
socket_data_.reset();
|
|
int packet_num = 1;
|
|
int peer_packet_num = 1;
|
|
quic::QuicConnectionId next_cid = quic::QuicUtils::CreateRandomConnectionId(
|
|
quiche::QuicheRandom::GetInstance());
|
|
uint64_t next_cid_sequence_number = 1u;
|
|
quic_data.AddWrite(SYNCHRONOUS,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC,
|
|
server_maker_.MakeNewConnectionIdPacket(
|
|
peer_packet_num++, /*include_version=*/false, next_cid,
|
|
next_cid_sequence_number,
|
|
/*retire_prior_to=*/next_cid_sequence_number - 1));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
// Make connection ID available for the first migration.
|
|
quic_data.Resume();
|
|
|
|
/* Migration succeeds when maximum number of readers is not reached.*/
|
|
for (size_t i = 0; i < kMaxReadersPerQuicSession - 1; ++i) {
|
|
MockQuicData quic_data2(version_);
|
|
client_maker_.set_connection_id(next_cid);
|
|
quic_data2.AddWrite(SYNCHRONOUS,
|
|
client_maker_.MakeAckAndPingPacket(
|
|
packet_num++, /*include_version=*/true,
|
|
/*largest_received=*/peer_packet_num - 1,
|
|
/*smallest_received=*/1));
|
|
quic_data2.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data2.AddWrite(ASYNC,
|
|
client_maker_.MakeRetireConnectionIdPacket(
|
|
packet_num++, /*include_version=*/false,
|
|
/*sequence_number=*/next_cid_sequence_number - 1));
|
|
next_cid = quic::QuicUtils::CreateRandomConnectionId(
|
|
quiche::QuicheRandom::GetInstance());
|
|
++next_cid_sequence_number;
|
|
quic_data2.AddRead(
|
|
ASYNC, server_maker_.MakeNewConnectionIdPacket(
|
|
peer_packet_num++, /*include_version=*/false, next_cid,
|
|
next_cid_sequence_number,
|
|
/*retire_prior_to=*/next_cid_sequence_number - 1));
|
|
quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read.
|
|
quic_data2.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
// Create connected socket.
|
|
std::unique_ptr<DatagramClientSocket> new_socket =
|
|
socket_factory_.CreateDatagramClientSocket(
|
|
DatagramSocket::RANDOM_BIND, NetLog::Get(), NetLogSource());
|
|
EXPECT_THAT(new_socket->Connect(kIpEndPoint), IsOk());
|
|
|
|
// Create reader and writer.
|
|
auto new_reader = std::make_unique<QuicChromiumPacketReader>(
|
|
new_socket.get(), &clock_, session_.get(), kQuicYieldAfterPacketsRead,
|
|
quic::QuicTime::Delta::FromMilliseconds(
|
|
kQuicYieldAfterDurationMilliseconds),
|
|
net_log_with_source_);
|
|
new_reader->StartReading();
|
|
std::unique_ptr<QuicChromiumPacketWriter> new_writer(
|
|
CreateQuicChromiumPacketWriter(new_socket.get(), session_.get()));
|
|
|
|
IPEndPoint local_address;
|
|
new_socket->GetLocalAddress(&local_address);
|
|
IPEndPoint peer_address;
|
|
new_socket->GetPeerAddress(&peer_address);
|
|
// Migrate session.
|
|
EXPECT_TRUE(session_->MigrateToSocket(
|
|
ToQuicSocketAddress(local_address), ToQuicSocketAddress(peer_address),
|
|
std::move(new_socket), std::move(new_reader), std::move(new_writer)));
|
|
// Spin message loop to complete migration.
|
|
base::RunLoop().RunUntilIdle();
|
|
alarm_factory_.FireAlarm(
|
|
quic::test::QuicConnectionPeer::GetRetirePeerIssuedConnectionIdAlarm(
|
|
session_->connection()));
|
|
// Make new connection ID available for subsequent migration.
|
|
quic_data2.Resume();
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_TRUE(quic_data2.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
|
|
}
|
|
|
|
/* Migration fails when maximum number of readers is reached.*/
|
|
MockQuicData quic_data2(version_);
|
|
quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read.
|
|
quic_data2.AddSocketDataToFactory(&socket_factory_);
|
|
// Create connected socket.
|
|
std::unique_ptr<DatagramClientSocket> new_socket =
|
|
socket_factory_.CreateDatagramClientSocket(DatagramSocket::RANDOM_BIND,
|
|
NetLog::Get(), NetLogSource());
|
|
EXPECT_THAT(new_socket->Connect(kIpEndPoint), IsOk());
|
|
|
|
// Create reader and writer.
|
|
auto new_reader = std::make_unique<QuicChromiumPacketReader>(
|
|
new_socket.get(), &clock_, session_.get(), kQuicYieldAfterPacketsRead,
|
|
quic::QuicTime::Delta::FromMilliseconds(
|
|
kQuicYieldAfterDurationMilliseconds),
|
|
net_log_with_source_);
|
|
new_reader->StartReading();
|
|
std::unique_ptr<QuicChromiumPacketWriter> new_writer(
|
|
CreateQuicChromiumPacketWriter(new_socket.get(), session_.get()));
|
|
|
|
IPEndPoint local_address;
|
|
new_socket->GetLocalAddress(&local_address);
|
|
IPEndPoint peer_address;
|
|
new_socket->GetPeerAddress(&peer_address);
|
|
EXPECT_FALSE(session_->MigrateToSocket(
|
|
ToQuicSocketAddress(local_address), ToQuicSocketAddress(peer_address),
|
|
std::move(new_socket), std::move(new_reader), std::move(new_writer)));
|
|
EXPECT_TRUE(quic_data2.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, MigrateToSocketReadError) {
|
|
SetIetfConnectionMigrationFlagsAndConnectionOptions();
|
|
MockQuicData quic_data(version_);
|
|
socket_data_.reset();
|
|
int packet_num = 1;
|
|
int peer_packet_num = 1;
|
|
|
|
quic::QuicConnectionId cid_on_new_path =
|
|
quic::test::TestConnectionId(12345678);
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddWrite(ASYNC,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
quic_data.AddRead(
|
|
ASYNC, server_maker_.MakeNewConnectionIdPacket(
|
|
peer_packet_num++, /*include_version=*/false, cid_on_new_path,
|
|
/*sequence_number=*/1u,
|
|
/*retire_prior_to=*/0u));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_NETWORK_CHANGED);
|
|
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
// Make new connection ID available after handshake completion.
|
|
quic_data.Resume();
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
MockQuicData quic_data2(version_);
|
|
client_maker_.set_connection_id(cid_on_new_path);
|
|
quic_data2.AddWrite(SYNCHRONOUS, client_maker_.MakeAckAndPingPacket(
|
|
packet_num++,
|
|
/*include_version=*/false,
|
|
/*largest_received=*/peer_packet_num - 1,
|
|
/*smallest_received=*/1));
|
|
quic_data2.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data2.AddRead(
|
|
ASYNC, server_maker_.MakePingPacket(1, /*include_version=*/false));
|
|
quic_data2.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data2.AddRead(ASYNC, ERR_NETWORK_CHANGED);
|
|
quic_data2.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
// Create connected socket.
|
|
std::unique_ptr<DatagramClientSocket> new_socket =
|
|
socket_factory_.CreateDatagramClientSocket(DatagramSocket::RANDOM_BIND,
|
|
NetLog::Get(), NetLogSource());
|
|
EXPECT_THAT(new_socket->Connect(kIpEndPoint), IsOk());
|
|
|
|
// Create reader and writer.
|
|
auto new_reader = std::make_unique<QuicChromiumPacketReader>(
|
|
new_socket.get(), &clock_, session_.get(), kQuicYieldAfterPacketsRead,
|
|
quic::QuicTime::Delta::FromMilliseconds(
|
|
kQuicYieldAfterDurationMilliseconds),
|
|
net_log_with_source_);
|
|
new_reader->StartReading();
|
|
std::unique_ptr<QuicChromiumPacketWriter> new_writer(
|
|
CreateQuicChromiumPacketWriter(new_socket.get(), session_.get()));
|
|
|
|
IPEndPoint local_address;
|
|
new_socket->GetLocalAddress(&local_address);
|
|
IPEndPoint peer_address;
|
|
new_socket->GetPeerAddress(&peer_address);
|
|
// Store old socket and migrate session.
|
|
EXPECT_TRUE(session_->MigrateToSocket(
|
|
ToQuicSocketAddress(local_address), ToQuicSocketAddress(peer_address),
|
|
std::move(new_socket), std::move(new_reader), std::move(new_writer)));
|
|
// Spin message loop to complete migration.
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_TRUE(
|
|
quic::test::QuicConnectionPeer::GetRetirePeerIssuedConnectionIdAlarm(
|
|
session_->connection())
|
|
->IsSet());
|
|
|
|
// Read error on old socket does not impact session.
|
|
quic_data.Resume();
|
|
EXPECT_TRUE(session_->connection()->connected());
|
|
quic_data2.Resume();
|
|
|
|
// Read error on new socket causes session close.
|
|
EXPECT_TRUE(session_->connection()->connected());
|
|
quic_data2.Resume();
|
|
EXPECT_FALSE(session_->connection()->connected());
|
|
|
|
EXPECT_TRUE(quic_data.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data.AllWriteDataConsumed());
|
|
EXPECT_TRUE(quic_data2.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, RetransmittableOnWireTimeout) {
|
|
migrate_session_early_v2_ = true;
|
|
|
|
MockQuicData quic_data(version_);
|
|
int packet_num = 1;
|
|
quic_data.AddWrite(SYNCHRONOUS,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
quic_data.AddWrite(SYNCHRONOUS,
|
|
client_maker_.MakePingPacket(packet_num++, true));
|
|
|
|
quic_data.AddRead(ASYNC, server_maker_.MakeAckPacket(1, packet_num - 1, 1));
|
|
|
|
quic_data.AddWrite(SYNCHRONOUS,
|
|
client_maker_.MakePingPacket(packet_num++, false));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
// Open a stream since the connection only sends PINGs to keep a
|
|
// retransmittable packet on the wire if there's an open stream.
|
|
EXPECT_TRUE(
|
|
QuicChromiumClientSessionPeer::CreateOutgoingStream(session_.get()));
|
|
|
|
quic::QuicAlarm* alarm =
|
|
quic::test::QuicConnectionPeer::GetPingAlarm(session_->connection());
|
|
EXPECT_FALSE(alarm->IsSet());
|
|
|
|
// Send PING, which will be ACKed by the server. After the ACK, there will be
|
|
// no retransmittable packets on the wire, so the alarm should be set.
|
|
session_->connection()->SendPing();
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_TRUE(alarm->IsSet());
|
|
EXPECT_EQ(
|
|
clock_.ApproximateNow() + quic::QuicTime::Delta::FromMilliseconds(200),
|
|
alarm->deadline());
|
|
|
|
// Advance clock and simulate the alarm firing. This should cause a PING to be
|
|
// sent.
|
|
clock_.AdvanceTime(quic::QuicTime::Delta::FromMilliseconds(200));
|
|
alarm_factory_.FireAlarm(alarm);
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
quic_data.Resume();
|
|
EXPECT_TRUE(quic_data.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data.AllWriteDataConsumed());
|
|
}
|
|
|
|
// Regression test for https://crbug.com/1043531.
|
|
TEST_P(QuicChromiumClientSessionTest, ResetOnEmptyResponseHeaders) {
|
|
MockQuicData quic_data(version_);
|
|
int packet_num = 1;
|
|
quic_data.AddWrite(ASYNC,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
quic_data.AddWrite(ASYNC, client_maker_.MakeRstPacket(
|
|
packet_num++, true,
|
|
GetNthClientInitiatedBidirectionalStreamId(0),
|
|
quic::QUIC_STREAM_GENERAL_PROTOCOL_ERROR));
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
Initialize();
|
|
|
|
ProofVerifyDetailsChromium details;
|
|
details.cert_verify_result.verified_cert =
|
|
ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
|
|
ASSERT_TRUE(details.cert_verify_result.verified_cert.get());
|
|
|
|
CompleteCryptoHandshake();
|
|
session_->OnProofVerifyDetailsAvailable(details);
|
|
|
|
auto session_handle = session_->CreateHandle(destination_);
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(OK, session_handle->RequestStream(/*requires_confirmation=*/false,
|
|
callback.callback(),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
|
|
auto stream_handle = session_handle->ReleaseStream();
|
|
EXPECT_TRUE(stream_handle->IsOpen());
|
|
|
|
auto* stream = quic::test::QuicSessionPeer::GetOrCreateStream(
|
|
session_.get(), stream_handle->id());
|
|
|
|
const quic::QuicHeaderList empty_response_headers;
|
|
static_cast<quic::QuicSpdyStream*>(stream)->OnStreamHeaderList(
|
|
/* fin = */ false, /* frame_len = */ 0, empty_response_headers);
|
|
|
|
// QuicSpdyStream::OnStreamHeaderList() calls
|
|
// QuicChromiumClientStream::OnInitialHeadersComplete() with the empty
|
|
// header list, and QuicChromiumClientStream signals an error.
|
|
spdy::Http2HeaderBlock header_block;
|
|
int rv = stream_handle->ReadInitialHeaders(&header_block,
|
|
CompletionOnceCallback());
|
|
EXPECT_THAT(rv, IsError(net::ERR_QUIC_PROTOCOL_ERROR));
|
|
|
|
base::RunLoop().RunUntilIdle();
|
|
quic_data.Resume();
|
|
EXPECT_TRUE(quic_data.AllReadDataConsumed());
|
|
EXPECT_TRUE(quic_data.AllWriteDataConsumed());
|
|
}
|
|
|
|
// This test verifies that when handles::NetworkHandle is not supported and
|
|
// there is no network change, session reports to the connectivity monitor
|
|
// correctly on path degrading detection and recovery.
|
|
TEST_P(QuicChromiumClientSessionTest,
|
|
DegradingWithoutNetworkChange_NoNetworkHandle) {
|
|
// Add a connectivity monitor for testing.
|
|
default_network_ = handles::kInvalidNetworkHandle;
|
|
connectivity_monitor_ =
|
|
std::make_unique<QuicConnectivityMonitor>(default_network_);
|
|
|
|
Initialize();
|
|
|
|
// Fire path degrading detection.
|
|
session_->ReallyOnPathDegrading();
|
|
EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
session_->OnForwardProgressMadeAfterPathDegrading();
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
// Fire again.
|
|
session_->ReallyOnPathDegrading();
|
|
EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
// Close the session but keep the session around, the connectivity monitor
|
|
// will not remove the tracking immediately.
|
|
session_->CloseSessionOnError(ERR_ABORTED, quic::QUIC_INTERNAL_ERROR,
|
|
quic::ConnectionCloseBehavior::SILENT_CLOSE);
|
|
EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
// Delete the session will remove the degrading count in connectivity
|
|
// monitor.
|
|
session_.reset();
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
}
|
|
|
|
// This test verifies that when multi-port and port migration is enabled, path
|
|
// degrading won't trigger port migration.
|
|
TEST_P(QuicChromiumClientSessionTest, DegradingWithMultiPortEnabled) {
|
|
// Default network is always set to handles::kInvalidNetworkHandle.
|
|
default_network_ = handles::kInvalidNetworkHandle;
|
|
connectivity_monitor_ =
|
|
std::make_unique<QuicConnectivityMonitor>(default_network_);
|
|
allow_port_migration_ = true;
|
|
SetIetfConnectionMigrationFlagsAndConnectionOptions();
|
|
auto options = config_.SendConnectionOptions();
|
|
config_.SetClientConnectionOptions(quic::QuicTagVector{quic::kMPQC});
|
|
config_.SetConnectionOptionsToSend(options);
|
|
|
|
Initialize();
|
|
EXPECT_TRUE(session_->connection()->multi_port_stats());
|
|
|
|
session_->ReallyOnPathDegrading();
|
|
EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
EXPECT_EQ(
|
|
UNKNOWN_CAUSE,
|
|
QuicChromiumClientSessionPeer::GetCurrentMigrationCause(session_.get()));
|
|
}
|
|
|
|
// This test verifies that when the handles::NetworkHandle is not supported, and
|
|
// there are speculated network change reported via OnIPAddressChange, session
|
|
// still reports to the connectivity monitor correctly on path degrading
|
|
// detection and recovery.
|
|
TEST_P(QuicChromiumClientSessionTest, DegradingWithIPAddressChange) {
|
|
// Default network is always set to handles::kInvalidNetworkHandle.
|
|
default_network_ = handles::kInvalidNetworkHandle;
|
|
connectivity_monitor_ =
|
|
std::make_unique<QuicConnectivityMonitor>(default_network_);
|
|
|
|
Initialize();
|
|
|
|
session_->ReallyOnPathDegrading();
|
|
EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
session_->OnForwardProgressMadeAfterPathDegrading();
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
session_->ReallyOnPathDegrading();
|
|
EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
// When handles::NetworkHandle is not supported, network change is notified
|
|
// via IP address change.
|
|
connectivity_monitor_->OnIPAddressChanged();
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
// When handles::NetworkHandle is not supported and IP address changes,
|
|
// session either goes away or gets closed. When it goes away,
|
|
// reporting to connectivity monitor is disabled.
|
|
connectivity_monitor_->OnSessionGoingAwayOnIPAddressChange(session_.get());
|
|
|
|
// Even if session detects recovery or degradation, this session is no longer
|
|
// on the default network and connectivity monitor will not update.
|
|
session_->OnForwardProgressMadeAfterPathDegrading();
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
session_->ReallyOnPathDegrading();
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
session_->CloseSessionOnError(ERR_ABORTED, quic::QUIC_INTERNAL_ERROR,
|
|
quic::ConnectionCloseBehavior::SILENT_CLOSE);
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
session_.reset();
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
}
|
|
|
|
// This test verifies that when handles::NetworkHandle is supported but
|
|
// migration is not supported and there's no network change, session reports to
|
|
// connectivity monitor correctly on path degrading detection or recovery.
|
|
// Default network change is currently reported with valid
|
|
// handles::NetworkHandles while session's current network interface is tracked
|
|
// by |default_network_|.
|
|
TEST_P(QuicChromiumClientSessionTest,
|
|
DegradingOnDeafultNetwork_WithoutMigration) {
|
|
default_network_ = kDefaultNetworkForTests;
|
|
connectivity_monitor_ =
|
|
std::make_unique<QuicConnectivityMonitor>(default_network_);
|
|
|
|
Initialize();
|
|
|
|
session_->ReallyOnPathDegrading();
|
|
EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
session_->OnForwardProgressMadeAfterPathDegrading();
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
session_->ReallyOnPathDegrading();
|
|
EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());
|
|
// Close the session but keep the session around, the connectivity monitor
|
|
// should not remove the count immediately.
|
|
session_->CloseSessionOnError(ERR_ABORTED, quic::QUIC_INTERNAL_ERROR,
|
|
quic::ConnectionCloseBehavior::SILENT_CLOSE);
|
|
EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
// Delete the session will remove the degrading count in connectivity
|
|
// monitor.
|
|
session_.reset();
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
}
|
|
|
|
// This test verifies that when handles::NetworkHandle is supported but
|
|
// migrations is not supported and there is network changes, session reports to
|
|
// the connectivity monitor correctly on path degrading detection or recovery.
|
|
TEST_P(QuicChromiumClientSessionTest,
|
|
DegradingWithDeafultNetworkChange_WithoutMigration) {
|
|
default_network_ = kDefaultNetworkForTests;
|
|
connectivity_monitor_ =
|
|
std::make_unique<QuicConnectivityMonitor>(default_network_);
|
|
|
|
Initialize();
|
|
|
|
session_->ReallyOnPathDegrading();
|
|
EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
session_->OnForwardProgressMadeAfterPathDegrading();
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
session_->ReallyOnPathDegrading();
|
|
EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
// Simulate the default network change.
|
|
connectivity_monitor_->OnDefaultNetworkUpdated(kNewNetworkForTests);
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
session_->OnNetworkMadeDefault(kNewNetworkForTests);
|
|
|
|
// Session stays on the old default network, and recovers.
|
|
session_->OnForwardProgressMadeAfterPathDegrading();
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
// Session degrades again on the old default.
|
|
session_->ReallyOnPathDegrading();
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
// Simulate that default network switches back to the old default.
|
|
connectivity_monitor_->OnDefaultNetworkUpdated(kDefaultNetworkForTests);
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
session_->OnNetworkMadeDefault(kDefaultNetworkForTests);
|
|
|
|
// Session recovers again on the (old) default.
|
|
session_->OnForwardProgressMadeAfterPathDegrading();
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
// Session degrades again on the (old) default.
|
|
session_->ReallyOnPathDegrading();
|
|
EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
session_->CloseSessionOnError(ERR_ABORTED, quic::QUIC_INTERNAL_ERROR,
|
|
quic::ConnectionCloseBehavior::SILENT_CLOSE);
|
|
EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions());
|
|
|
|
session_.reset();
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions());
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, WriteErrorDuringCryptoConnect) {
|
|
// Add a connectivity monitor for testing.
|
|
default_network_ = kDefaultNetworkForTests;
|
|
connectivity_monitor_ =
|
|
std::make_unique<QuicConnectivityMonitor>(default_network_);
|
|
|
|
// Use unmocked crypto stream to do crypto connect.
|
|
crypto_client_stream_factory_.set_handshake_mode(
|
|
MockCryptoClientStream::COLD_START_WITH_CHLO_SENT);
|
|
|
|
MockQuicData quic_data(version_);
|
|
// Trigger a packet write error when sending packets in crypto connect.
|
|
quic_data.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
ASSERT_THAT(session_->CryptoConnect(callback_.callback()),
|
|
IsError(ERR_QUIC_HANDSHAKE_FAILED));
|
|
// Verify error count is properly recorded.
|
|
EXPECT_EQ(1u, connectivity_monitor_->GetCountForWriteErrorCode(
|
|
ERR_ADDRESS_UNREACHABLE));
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetCountForWriteErrorCode(
|
|
ERR_CONNECTION_RESET));
|
|
|
|
// Simulate a default network change, write error stats should be reset.
|
|
connectivity_monitor_->OnDefaultNetworkUpdated(kNewNetworkForTests);
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetCountForWriteErrorCode(
|
|
ERR_ADDRESS_UNREACHABLE));
|
|
}
|
|
|
|
TEST_P(QuicChromiumClientSessionTest, WriteErrorAfterHandshakeConfirmed) {
|
|
// Add a connectivity monitor for testing.
|
|
default_network_ = handles::kInvalidNetworkHandle;
|
|
connectivity_monitor_ =
|
|
std::make_unique<QuicConnectivityMonitor>(default_network_);
|
|
|
|
MockQuicData quic_data(version_);
|
|
int packet_num = 1;
|
|
quic_data.AddWrite(SYNCHRONOUS,
|
|
client_maker_.MakeInitialSettingsPacket(packet_num++));
|
|
// When sending the PING packet, trigger a packet write error.
|
|
quic_data.AddWrite(SYNCHRONOUS, ERR_CONNECTION_RESET);
|
|
quic_data.AddRead(ASYNC, ERR_IO_PENDING);
|
|
quic_data.AddRead(ASYNC, ERR_CONNECTION_CLOSED);
|
|
quic_data.AddSocketDataToFactory(&socket_factory_);
|
|
|
|
Initialize();
|
|
CompleteCryptoHandshake();
|
|
|
|
// Send a ping so that client has outgoing traffic before receiving packets.
|
|
session_->connection()->SendPing();
|
|
|
|
// Verify error count is properly recorded.
|
|
EXPECT_EQ(1u, connectivity_monitor_->GetCountForWriteErrorCode(
|
|
ERR_CONNECTION_RESET));
|
|
EXPECT_EQ(0u, connectivity_monitor_->GetCountForWriteErrorCode(
|
|
ERR_ADDRESS_UNREACHABLE));
|
|
|
|
connectivity_monitor_->OnIPAddressChanged();
|
|
|
|
// If network handle is supported, IP Address change is a no-op. Otherwise it
|
|
// clears all stats.
|
|
size_t expected_error_count =
|
|
NetworkChangeNotifier::AreNetworkHandlesSupported() ? 1u : 0u;
|
|
EXPECT_EQ(
|
|
expected_error_count,
|
|
connectivity_monitor_->GetCountForWriteErrorCode(ERR_CONNECTION_RESET));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace net::test
|