blob: fc32c2ffff6d0058e0537fcfd2f9385a8bcad058 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/quic/quic_stream_factory.h"
#include <memory>
#include <ostream>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/test/test_mock_time_task_runner.h"
#include "build/build_config.h"
#include "net/base/features.h"
#include "net/base/load_flags.h"
#include "net/base/mock_network_change_notifier.h"
#include "net/base/network_isolation_key.h"
#include "net/cert/ct_policy_enforcer.h"
#include "net/cert/do_nothing_ct_verifier.h"
#include "net/cert/mock_cert_verifier.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
#include "net/http/http_server_properties.h"
#include "net/http/http_util.h"
#include "net/http/transport_security_state.h"
#include "net/http/transport_security_state_test_util.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/properties_based_quic_server_info.h"
#include "net/quic/quic_chromium_alarm_factory.h"
#include "net/quic/quic_http_stream.h"
#include "net/quic/quic_http_utils.h"
#include "net/quic/quic_server_info.h"
#include "net/quic/quic_stream_factory_peer.h"
#include "net/quic/quic_test_packet_maker.h"
#include "net/quic/quic_test_packet_printer.h"
#include "net/quic/test_task_runner.h"
#include "net/socket/next_proto.h"
#include "net/socket/socket_test_util.h"
#include "net/spdy/spdy_session_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/quic/core/crypto/crypto_handshake.h"
#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h"
#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
#include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h"
#include "net/third_party/quiche/src/quic/core/quic_constants.h"
#include "net/third_party/quiche/src/quic/core/quic_utils.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
#include "net/third_party/quiche/src/quic/test_tools/mock_random.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
#include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using std::string;
namespace net {
namespace test {
namespace {
enum DestinationType {
// In pooling tests with two requests for different origins to the same
// destination, the destination should be
SAME_AS_FIRST, // the same as the first origin,
SAME_AS_SECOND, // the same as the second origin, or
DIFFERENT, // different from both.
};
const char kDefaultServerHostName[] = "www.example.org";
const char kServer2HostName[] = "mail.example.org";
const char kDifferentHostname[] = "different.example.com";
const int kDefaultServerPort = 443;
const char kDefaultUrl[] = "https://www.example.org/";
const char kServer2Url[] = "https://mail.example.org/";
const char kServer3Url[] = "https://docs.example.org/";
const char kServer4Url[] = "https://images.example.org/";
const int kDefaultRTTMilliSecs = 300;
const size_t kMinRetryTimeForDefaultNetworkSecs = 1;
const size_t kWaitTimeForNewNetworkSecs = 10;
const IPAddress kCachedIPAddress = IPAddress(192, 168, 0, 2);
const char kNonCachedIPAddress[] = "192.168.0.1";
// Run QuicStreamFactoryTest instances with all value combinations of version
// and enable_connection_racting.
struct TestParams {
friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
os << "{ version: " << ParsedQuicVersionToString(p.version)
<< ", client_headers_include_h2_stream_dependency: "
<< p.client_headers_include_h2_stream_dependency << " }";
return os;
}
quic::ParsedQuicVersion version;
bool client_headers_include_h2_stream_dependency;
};
std::vector<TestParams> GetTestParams() {
std::vector<TestParams> params;
quic::ParsedQuicVersionVector all_supported_versions =
quic::AllSupportedVersions();
for (const auto& version : all_supported_versions) {
// TODO(rch): crbug.com/978745 - Make this work with TLS
if (version.handshake_protocol != quic::PROTOCOL_TLS1_3) {
params.push_back(TestParams{version, false});
params.push_back(TestParams{version, true});
}
}
return params;
}
// Run QuicStreamFactoryWithDestinationTest instances with all value
// combinations of version, enable_connection_racting, and destination_type.
struct PoolingTestParams {
friend std::ostream& operator<<(std::ostream& os,
const PoolingTestParams& p) {
os << "{ version: " << ParsedQuicVersionToString(p.version)
<< ", destination_type: ";
switch (p.destination_type) {
case SAME_AS_FIRST:
os << "SAME_AS_FIRST";
break;
case SAME_AS_SECOND:
os << "SAME_AS_SECOND";
break;
case DIFFERENT:
os << "DIFFERENT";
break;
}
os << ", client_headers_include_h2_stream_dependency: "
<< p.client_headers_include_h2_stream_dependency;
os << " }";
return os;
}
quic::ParsedQuicVersion version;
DestinationType destination_type;
bool client_headers_include_h2_stream_dependency;
};
std::vector<PoolingTestParams> GetPoolingTestParams() {
std::vector<PoolingTestParams> params;
quic::ParsedQuicVersionVector all_supported_versions =
quic::AllSupportedVersions();
for (const quic::ParsedQuicVersion version : all_supported_versions) {
// TODO(rch): crbug.com/978745 - Make this work with TLS
if (version.handshake_protocol != quic::PROTOCOL_TLS1_3) {
params.push_back(PoolingTestParams{version, SAME_AS_FIRST, false});
params.push_back(PoolingTestParams{version, SAME_AS_FIRST, true});
params.push_back(PoolingTestParams{version, SAME_AS_SECOND, false});
params.push_back(PoolingTestParams{version, SAME_AS_SECOND, true});
params.push_back(PoolingTestParams{version, DIFFERENT, false});
params.push_back(PoolingTestParams{version, DIFFERENT, true});
}
}
return params;
}
} // namespace
class QuicHttpStreamPeer {
public:
static QuicChromiumClientSession::Handle* GetSessionHandle(
HttpStream* stream) {
return static_cast<QuicHttpStream*>(stream)->quic_session();
}
};
// TestMigrationSocketFactory will vend sockets with incremental port number.
class TestMigrationSocketFactory : public MockClientSocketFactory {
public:
TestMigrationSocketFactory() : next_source_port_num_(1u) {}
~TestMigrationSocketFactory() override {}
std::unique_ptr<DatagramClientSocket> CreateDatagramClientSocket(
DatagramSocket::BindType bind_type,
NetLog* net_log,
const NetLogSource& source) override {
SocketDataProvider* data_provider = mock_data().GetNext();
std::unique_ptr<MockUDPClientSocket> socket(
new MockUDPClientSocket(data_provider, net_log));
socket->set_source_port(next_source_port_num_++);
return std::move(socket);
}
private:
uint16_t next_source_port_num_;
DISALLOW_COPY_AND_ASSIGN(TestMigrationSocketFactory);
};
class QuicStreamFactoryTestBase : public WithTaskEnvironment {
protected:
QuicStreamFactoryTestBase(quic::ParsedQuicVersion version,
bool client_headers_include_h2_stream_dependency)
: host_resolver_(new MockHostResolver),
ssl_config_service_(new SSLConfigServiceDefaults),
socket_factory_(new MockClientSocketFactory),
random_generator_(0),
runner_(new TestTaskRunner(&clock_)),
version_(version),
client_maker_(
version_,
quic::QuicUtils::CreateRandomConnectionId(&random_generator_),
&clock_,
kDefaultServerHostName,
quic::Perspective::IS_CLIENT,
client_headers_include_h2_stream_dependency),
server_maker_(
version_,
quic::QuicUtils::CreateRandomConnectionId(&random_generator_),
&clock_,
kDefaultServerHostName,
quic::Perspective::IS_SERVER,
false),
http_server_properties_(std::make_unique<HttpServerProperties>()),
cert_verifier_(std::make_unique<MockCertVerifier>()),
cert_transparency_verifier_(std::make_unique<DoNothingCTVerifier>()),
scoped_mock_network_change_notifier_(nullptr),
factory_(nullptr),
host_port_pair_(kDefaultServerHostName, kDefaultServerPort),
url_(kDefaultUrl),
url2_(kServer2Url),
url3_(kServer3Url),
url4_(kServer4Url),
privacy_mode_(PRIVACY_MODE_DISABLED),
failed_on_default_network_callback_(base::BindRepeating(
&QuicStreamFactoryTestBase::OnFailedOnDefaultNetwork,
base::Unretained(this))),
failed_on_default_network_(false) {
test_params_.quic_params.headers_include_h2_stream_dependency =
client_headers_include_h2_stream_dependency;
clock_.AdvanceTime(quic::QuicTime::Delta::FromSeconds(1));
}
void Initialize() {
DCHECK(!factory_);
factory_ = std::make_unique<QuicStreamFactory>(
net_log_.net_log(), host_resolver_.get(), ssl_config_service_.get(),
socket_factory_.get(), http_server_properties_.get(),
cert_verifier_.get(), &ct_policy_enforcer_, &transport_security_state_,
cert_transparency_verifier_.get(),
/*SocketPerformanceWatcherFactory*/ nullptr,
&crypto_client_stream_factory_, &random_generator_, &clock_,
test_params_.quic_params);
}
void InitializeConnectionMigrationV2Test(
NetworkChangeNotifier::NetworkList connected_networks) {
scoped_mock_network_change_notifier_.reset(
new ScopedMockNetworkChangeNotifier());
MockNetworkChangeNotifier* mock_ncn =
scoped_mock_network_change_notifier_->mock_network_change_notifier();
mock_ncn->ForceNetworkHandlesSupported();
mock_ncn->SetConnectedNetworksList(connected_networks);
test_params_.quic_params.migrate_sessions_on_network_change_v2 = true;
test_params_.quic_params.migrate_sessions_early_v2 = true;
test_params_.quic_params.allow_port_migration = false;
socket_factory_.reset(new TestMigrationSocketFactory);
Initialize();
}
std::unique_ptr<HttpStream> CreateStream(QuicStreamRequest* request) {
std::unique_ptr<QuicChromiumClientSession::Handle> session =
request->ReleaseSessionHandle();
if (!session || !session->IsConnected())
return nullptr;
return std::make_unique<QuicHttpStream>(std::move(session));
}
bool HasActiveSession(const HostPortPair& host_port_pair,
const NetworkIsolationKey& network_isolation_key =
NetworkIsolationKey()) {
quic::QuicServerId server_id(host_port_pair.host(), host_port_pair.port(),
false);
return QuicStreamFactoryPeer::HasActiveSession(factory_.get(), server_id,
network_isolation_key);
}
bool HasLiveSession(const HostPortPair& host_port_pair) {
quic::QuicServerId server_id(host_port_pair.host(), host_port_pair.port(),
false);
return QuicStreamFactoryPeer::HasLiveSession(factory_.get(), host_port_pair,
server_id);
}
bool HasActiveJob(const HostPortPair& host_port_pair,
const PrivacyMode privacy_mode) {
quic::QuicServerId server_id(host_port_pair.host(), host_port_pair.port(),
privacy_mode == PRIVACY_MODE_ENABLED);
return QuicStreamFactoryPeer::HasActiveJob(factory_.get(), server_id);
}
bool HasActiveCertVerifierJob(const quic::QuicServerId& server_id) {
return QuicStreamFactoryPeer::HasActiveCertVerifierJob(factory_.get(),
server_id);
}
// Get the pending, not activated session, if there is only one session alive.
QuicChromiumClientSession* GetPendingSession(
const HostPortPair& host_port_pair) {
quic::QuicServerId server_id(host_port_pair.host(), host_port_pair.port(),
false);
return QuicStreamFactoryPeer::GetPendingSession(factory_.get(), server_id,
host_port_pair);
}
QuicChromiumClientSession* GetActiveSession(
const HostPortPair& host_port_pair,
const NetworkIsolationKey& network_isolation_key =
NetworkIsolationKey()) {
quic::QuicServerId server_id(host_port_pair.host(), host_port_pair.port(),
false);
return QuicStreamFactoryPeer::GetActiveSession(factory_.get(), server_id,
network_isolation_key);
}
int GetSourcePortForNewSession(const HostPortPair& destination) {
return GetSourcePortForNewSessionInner(destination, false);
}
int GetSourcePortForNewSessionAndGoAway(const HostPortPair& destination) {
return GetSourcePortForNewSessionInner(destination, true);
}
int GetSourcePortForNewSessionInner(const HostPortPair& destination,
bool goaway_received) {
// Should only be called if there is no active session for this destination.
EXPECT_FALSE(HasActiveSession(destination));
size_t socket_count = socket_factory_->udp_client_socket_ports().size();
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
GURL url("https://" + destination.host() + "/");
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
destination, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
stream.reset();
QuicChromiumClientSession* session = GetActiveSession(destination);
if (socket_count + 1 != socket_factory_->udp_client_socket_ports().size()) {
ADD_FAILURE();
return 0;
}
if (goaway_received) {
quic::QuicGoAwayFrame goaway(quic::kInvalidControlFrameId,
quic::QUIC_NO_ERROR, 1, "");
session->connection()->OnGoAwayFrame(goaway);
}
factory_->OnSessionClosed(session);
EXPECT_FALSE(HasActiveSession(destination));
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
return socket_factory_->udp_client_socket_ports()[socket_count];
}
std::unique_ptr<quic::QuicEncryptedPacket>
ConstructClientConnectionClosePacket(uint64_t num) {
return client_maker_.MakeConnectionClosePacket(
num, false, quic::QUIC_CRYPTO_VERSION_NOT_SUPPORTED, "Time to panic!");
}
std::unique_ptr<quic::QuicEncryptedPacket> ConstructClientRstPacket(
uint64_t packet_number,
quic::QuicRstStreamErrorCode error_code) {
quic::QuicStreamId stream_id =
GetNthClientInitiatedBidirectionalStreamId(0);
return client_maker_.MakeRstPacket(packet_number, true, stream_id,
error_code);
}
static ProofVerifyDetailsChromium DefaultProofVerifyDetails() {
// Load a certificate that is valid for *.example.org
scoped_refptr<X509Certificate> test_cert(
ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
EXPECT_TRUE(test_cert.get());
ProofVerifyDetailsChromium verify_details;
verify_details.cert_verify_result.verified_cert = test_cert;
verify_details.cert_verify_result.is_issued_by_known_root = true;
return verify_details;
}
void NotifyIPAddressChanged() {
NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
// Spin the message loop so the notification is delivered.
base::RunLoop().RunUntilIdle();
}
std::unique_ptr<quic::QuicEncryptedPacket> ConstructGetRequestPacket(
uint64_t packet_number,
quic::QuicStreamId stream_id,
bool should_include_version,
bool fin) {
spdy::SpdyHeaderBlock headers =
client_maker_.GetRequestHeaders("GET", "https", "/");
spdy::SpdyPriority priority =
ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
size_t spdy_headers_frame_len;
return client_maker_.MakeRequestHeadersPacket(
packet_number, stream_id, should_include_version, fin, priority,
std::move(headers), 0, &spdy_headers_frame_len);
}
std::unique_ptr<quic::QuicEncryptedPacket> ConstructGetRequestPacket(
uint64_t packet_number,
quic::QuicStreamId stream_id,
quic::QuicStreamId parent_stream_id,
bool should_include_version,
bool fin) {
spdy::SpdyHeaderBlock headers =
client_maker_.GetRequestHeaders("GET", "https", "/");
spdy::SpdyPriority priority =
ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY);
size_t spdy_headers_frame_len;
return client_maker_.MakeRequestHeadersPacket(
packet_number, stream_id, should_include_version, fin, priority,
std::move(headers), parent_stream_id, &spdy_headers_frame_len);
}
std::unique_ptr<quic::QuicEncryptedPacket> ConstructOkResponsePacket(
uint64_t packet_number,
quic::QuicStreamId stream_id,
bool should_include_version,
bool fin) {
spdy::SpdyHeaderBlock headers = server_maker_.GetResponseHeaders("200 OK");
size_t spdy_headers_frame_len;
return server_maker_.MakeResponseHeadersPacket(
packet_number, stream_id, should_include_version, fin,
std::move(headers), &spdy_headers_frame_len);
}
std::unique_ptr<quic::QuicReceivedPacket> ConstructInitialSettingsPacket() {
return client_maker_.MakeInitialSettingsPacket(1);
}
std::unique_ptr<quic::QuicReceivedPacket> ConstructInitialSettingsPacket(
uint64_t packet_number) {
return client_maker_.MakeInitialSettingsPacket(packet_number);
}
// Helper method for server migration tests.
void VerifyServerMigration(const quic::QuicConfig& config,
IPEndPoint expected_address) {
test_params_.quic_params.allow_server_migration = true;
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
crypto_client_stream_factory_.SetConfig(config);
// Set up first socket data provider.
MockQuicData socket_data1(version_);
socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
socket_data1.AddSocketDataToFactory(socket_factory_.get());
client_maker_.set_coalesce_http_frames(true);
// Set up second socket data provider that is used after
// migration.
MockQuicData socket_data2(version_);
socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
int packet_num = 1;
if (VersionUsesQpack(version_.transport_version)) {
socket_data2.AddWrite(SYNCHRONOUS,
ConstructInitialSettingsPacket(packet_num++));
}
socket_data2.AddWrite(
SYNCHRONOUS,
client_maker_.MakePingPacket(packet_num++, /*include_version=*/true));
socket_data2.AddWrite(
SYNCHRONOUS,
client_maker_.MakeRstPacket(
packet_num++, true, GetNthClientInitiatedBidirectionalStreamId(0),
quic::QUIC_STREAM_CANCELLED));
socket_data2.AddSocketDataToFactory(socket_factory_.get());
// Create request and QuicHttpStream.
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_EQ(OK, callback_.WaitForResult());
// Run QuicChromiumClientSession::WriteToNewSocket()
// posted by QuicChromiumClientSession::MigrateToSocket().
base::RunLoop().RunUntilIdle();
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
// Cause QUIC stream to be created.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = GURL("https://www.example.org/");
request_info.traffic_annotation =
MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_EQ(OK,
stream->InitializeStream(&request_info, true, DEFAULT_PRIORITY,
net_log_, CompletionOnceCallback()));
// Ensure that session is alive and active.
QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
EXPECT_TRUE(HasActiveSession(host_port_pair_));
IPEndPoint actual_address;
session->GetDefaultSocket()->GetPeerAddress(&actual_address);
EXPECT_EQ(actual_address, expected_address);
DVLOG(1) << "Socket connected to: " << actual_address.address().ToString()
<< " " << actual_address.port();
DVLOG(1) << "Expected address: " << expected_address.address().ToString()
<< " " << expected_address.port();
stream.reset();
EXPECT_TRUE(socket_data1.AllReadDataConsumed());
EXPECT_TRUE(socket_data2.AllReadDataConsumed());
EXPECT_TRUE(socket_data2.AllWriteDataConsumed());
}
// Verifies that the QUIC stream factory is initialized correctly.
void VerifyInitialization() {
test_params_.quic_params.max_server_configs_stored_in_properties = 1;
test_params_.quic_params.idle_connection_timeout =
base::TimeDelta::FromSeconds(500);
Initialize();
factory_->set_is_quic_known_to_work_on_current_network(true);
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
crypto_client_stream_factory_.set_handshake_mode(
MockCryptoClientStream::ZERO_RTT);
const quic::QuicConfig* config =
QuicStreamFactoryPeer::GetConfig(factory_.get());
EXPECT_EQ(500, config->IdleNetworkTimeout().ToSeconds());
QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), runner_.get());
const AlternativeService alternative_service1(
kProtoQUIC, host_port_pair_.host(), host_port_pair_.port());
AlternativeServiceInfoVector alternative_service_info_vector;
base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
alternative_service_info_vector.push_back(
AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
alternative_service1, expiration, {version_}));
http_server_properties_->SetAlternativeServices(
url::SchemeHostPort(url_), NetworkIsolationKey(),
alternative_service_info_vector);
HostPortPair host_port_pair2(kServer2HostName, kDefaultServerPort);
url::SchemeHostPort server2("https", kServer2HostName, kDefaultServerPort);
const AlternativeService alternative_service2(
kProtoQUIC, host_port_pair2.host(), host_port_pair2.port());
AlternativeServiceInfoVector alternative_service_info_vector2;
alternative_service_info_vector2.push_back(
AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
alternative_service2, expiration, {version_}));
http_server_properties_->SetAlternativeServices(
server2, NetworkIsolationKey(), alternative_service_info_vector2);
// Verify that the properties of both QUIC servers are stored in the
// HTTP properties map.
EXPECT_EQ(2U,
http_server_properties_->server_info_map_for_testing().size());
http_server_properties_->SetMaxServerConfigsStoredInProperties(
kDefaultMaxQuicServerEntries);
quic::QuicServerId quic_server_id(kDefaultServerHostName, 443,
PRIVACY_MODE_DISABLED);
std::unique_ptr<QuicServerInfo> quic_server_info =
std::make_unique<PropertiesBasedQuicServerInfo>(
quic_server_id, http_server_properties_.get());
// Update quic_server_info's server_config and persist it.
QuicServerInfo::State* state = quic_server_info->mutable_state();
// Minimum SCFG that passes config validation checks.
const char scfg[] = {// SCFG
0x53, 0x43, 0x46, 0x47,
// num entries
0x01, 0x00,
// padding
0x00, 0x00,
// EXPY
0x45, 0x58, 0x50, 0x59,
// EXPY end offset
0x08, 0x00, 0x00, 0x00,
// Value
'1', '2', '3', '4', '5', '6', '7', '8'};
// Create temporary strings becasue Persist() clears string data in |state|.
string server_config(reinterpret_cast<const char*>(&scfg), sizeof(scfg));
string source_address_token("test_source_address_token");
string cert_sct("test_cert_sct");
string chlo_hash("test_chlo_hash");
string signature("test_signature");
string test_cert("test_cert");
std::vector<string> certs;
certs.push_back(test_cert);
state->server_config = server_config;
state->source_address_token = source_address_token;
state->cert_sct = cert_sct;
state->chlo_hash = chlo_hash;
state->server_config_sig = signature;
state->certs = certs;
quic_server_info->Persist();
quic::QuicServerId quic_server_id2(kServer2HostName, 443,
PRIVACY_MODE_DISABLED);
std::unique_ptr<QuicServerInfo> quic_server_info2 =
std::make_unique<PropertiesBasedQuicServerInfo>(
quic_server_id2, http_server_properties_.get());
// Update quic_server_info2's server_config and persist it.
QuicServerInfo::State* state2 = quic_server_info2->mutable_state();
// Minimum SCFG that passes config validation checks.
const char scfg2[] = {// SCFG
0x53, 0x43, 0x46, 0x47,
// num entries
0x01, 0x00,
// padding
0x00, 0x00,
// EXPY
0x45, 0x58, 0x50, 0x59,
// EXPY end offset
0x08, 0x00, 0x00, 0x00,
// Value
'8', '7', '3', '4', '5', '6', '2', '1'};
// Create temporary strings becasue Persist() clears string data in
// |state2|.
string server_config2(reinterpret_cast<const char*>(&scfg2), sizeof(scfg2));
string source_address_token2("test_source_address_token2");
string cert_sct2("test_cert_sct2");
string chlo_hash2("test_chlo_hash2");
string signature2("test_signature2");
string test_cert2("test_cert2");
std::vector<string> certs2;
certs2.push_back(test_cert2);
state2->server_config = server_config2;
state2->source_address_token = source_address_token2;
state2->cert_sct = cert_sct2;
state2->chlo_hash = chlo_hash2;
state2->server_config_sig = signature2;
state2->certs = certs2;
quic_server_info2->Persist();
// Verify the MRU order is maintained.
const QuicServerInfoMap& quic_server_info_map =
http_server_properties_->quic_server_info_map();
EXPECT_EQ(2u, quic_server_info_map.size());
auto quic_server_info_map_it = quic_server_info_map.begin();
EXPECT_EQ(quic_server_info_map_it->first, quic_server_id2);
++quic_server_info_map_it;
EXPECT_EQ(quic_server_info_map_it->first, quic_server_id);
host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
"192.168.0.1", "");
// Create a session and verify that the cached state is loaded.
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
socket_data.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
HostPortPair(quic_server_id.host(), quic_server_id.port()),
version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
EXPECT_FALSE(QuicStreamFactoryPeer::CryptoConfigCacheIsEmpty(
factory_.get(), quic_server_id));
quic::QuicCryptoClientConfig* crypto_config =
QuicStreamFactoryPeer::GetCryptoConfig(factory_.get());
quic::QuicCryptoClientConfig::CachedState* cached =
crypto_config->LookupOrCreate(quic_server_id);
EXPECT_FALSE(cached->server_config().empty());
EXPECT_TRUE(cached->GetServerConfig());
EXPECT_EQ(server_config, cached->server_config());
EXPECT_EQ(source_address_token, cached->source_address_token());
EXPECT_EQ(cert_sct, cached->cert_sct());
EXPECT_EQ(chlo_hash, cached->chlo_hash());
EXPECT_EQ(signature, cached->signature());
ASSERT_EQ(1U, cached->certs().size());
EXPECT_EQ(test_cert, cached->certs()[0]);
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
// Create a session and verify that the cached state is loaded.
MockQuicData socket_data2(version_);
socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
socket_data2.AddSocketDataToFactory(socket_factory_.get());
host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
"192.168.0.2", "");
QuicStreamRequest request2(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request2.Request(
HostPortPair(quic_server_id2.host(), quic_server_id2.port()),
version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
NetworkIsolationKey(),
/*cert_verify_flags=*/0, GURL("https://mail.example.org/"),
net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
EXPECT_FALSE(QuicStreamFactoryPeer::CryptoConfigCacheIsEmpty(
factory_.get(), quic_server_id2));
quic::QuicCryptoClientConfig::CachedState* cached2 =
crypto_config->LookupOrCreate(quic_server_id2);
EXPECT_FALSE(cached2->server_config().empty());
EXPECT_TRUE(cached2->GetServerConfig());
EXPECT_EQ(server_config2, cached2->server_config());
EXPECT_EQ(source_address_token2, cached2->source_address_token());
EXPECT_EQ(cert_sct2, cached2->cert_sct());
EXPECT_EQ(chlo_hash2, cached2->chlo_hash());
EXPECT_EQ(signature2, cached2->signature());
ASSERT_EQ(1U, cached->certs().size());
EXPECT_EQ(test_cert2, cached2->certs()[0]);
}
void RunTestLoopUntilIdle() {
while (!runner_->GetPostedTasks().empty())
runner_->RunNextTask();
}
quic::QuicStreamId GetNthClientInitiatedBidirectionalStreamId(int n) {
return quic::test::GetNthClientInitiatedBidirectionalStreamId(
version_.transport_version, n);
}
std::string ConstructDataHeader(size_t body_len) {
if (version_.transport_version != quic::QUIC_VERSION_99) {
return "";
}
quic::HttpEncoder encoder;
std::unique_ptr<char[]> buffer;
auto header_length = encoder.SerializeDataFrameHeader(body_len, &buffer);
return std::string(buffer.get(), header_length);
}
std::unique_ptr<quic::QuicEncryptedPacket> ConstructServerDataPacket(
uint64_t packet_number,
quic::QuicStreamId stream_id,
bool should_include_version,
bool fin,
quic::QuicStringPiece data) {
return server_maker_.MakeDataPacket(packet_number, stream_id,
should_include_version, fin, data);
}
quic::QuicStreamId GetNthServerInitiatedUnidirectionalStreamId(int n) {
return quic::test::GetNthServerInitiatedUnidirectionalStreamId(
version_.transport_version, n);
}
void OnFailedOnDefaultNetwork(int rv) { failed_on_default_network_ = true; }
// Helper methods for tests of connection migration on write error.
void TestMigrationOnWriteErrorNonMigratableStream(IoMode write_error_mode,
bool migrate_idle_sessions);
// Migratable stream triggers write error.
void TestMigrationOnWriteErrorMixedStreams(IoMode write_error_mode);
// Non-migratable stream triggers write error.
void TestMigrationOnWriteErrorMixedStreams2(IoMode write_error_mode);
void TestMigrationOnWriteErrorMigrationDisabled(IoMode write_error_mode);
void TestMigrationOnWriteError(IoMode write_error_mode);
void TestMigrationOnWriteErrorWithMultipleRequests(IoMode write_error_mode);
void TestMigrationOnWriteErrorNoNewNetwork(IoMode write_error_mode);
void TestMigrationOnMultipleWriteErrors(
IoMode write_error_mode_on_old_network,
IoMode write_error_mode_on_new_network);
void TestMigrationOnNetworkNotificationWithWriteErrorQueuedLater(
bool disconnected);
void TestMigrationOnWriteErrorWithNotificationQueuedLater(bool disconnected);
void TestMigrationOnNetworkDisconnected(bool async_write_before);
void TestMigrationOnNetworkMadeDefault(IoMode write_mode);
void TestMigrationOnPathDegrading(bool async_write_before);
void TestMigrateSessionWithDrainingStream(
IoMode write_mode_for_queued_packet);
void TestMigrationOnWriteErrorPauseBeforeConnected(IoMode write_error_mode);
void TestMigrationOnWriteErrorWithMultipleNotifications(
IoMode write_error_mode,
bool disconnect_before_connect);
void TestNoAlternateNetworkBeforeHandshake(quic::QuicErrorCode error);
void TestNewConnectionOnAlternateNetworkBeforeHandshake(
quic::QuicErrorCode error);
void TestOnNetworkMadeDefaultNonMigratableStream(bool migrate_idle_sessions);
void TestMigrateSessionEarlyNonMigratableStream(bool migrate_idle_sessions);
void TestOnNetworkDisconnectedNoOpenStreams(bool migrate_idle_sessions);
void TestOnNetworkMadeDefaultNoOpenStreams(bool migrate_idle_sessions);
void TestOnNetworkDisconnectedNonMigratableStream(bool migrate_idle_sessions);
// Port migrations.
void TestSimplePortMigrationOnPathDegrading();
QuicFlagSaver flags_; // Save/restore all QUIC flag values.
std::unique_ptr<MockHostResolverBase> host_resolver_;
std::unique_ptr<SSLConfigService> ssl_config_service_;
std::unique_ptr<MockClientSocketFactory> socket_factory_;
MockCryptoClientStreamFactory crypto_client_stream_factory_;
quic::test::MockRandom random_generator_;
quic::MockClock clock_;
scoped_refptr<TestTaskRunner> runner_;
const quic::ParsedQuicVersion version_;
QuicTestPacketMaker client_maker_;
QuicTestPacketMaker server_maker_;
std::unique_ptr<HttpServerProperties> http_server_properties_;
std::unique_ptr<CertVerifier> cert_verifier_;
TransportSecurityState transport_security_state_;
std::unique_ptr<CTVerifier> cert_transparency_verifier_;
DefaultCTPolicyEnforcer ct_policy_enforcer_;
std::unique_ptr<ScopedMockNetworkChangeNotifier>
scoped_mock_network_change_notifier_;
std::unique_ptr<QuicStreamFactory> factory_;
HostPortPair host_port_pair_;
GURL url_;
GURL url2_;
GURL url3_;
GURL url4_;
PrivacyMode privacy_mode_;
NetLogWithSource net_log_;
TestCompletionCallback callback_;
const CompletionRepeatingCallback failed_on_default_network_callback_;
bool failed_on_default_network_;
NetErrorDetails net_error_details_;
// Variables to configure QuicStreamFactory.
HttpNetworkSession::Params test_params_;
};
class QuicStreamFactoryTest : public QuicStreamFactoryTestBase,
public ::testing::TestWithParam<TestParams> {
protected:
QuicStreamFactoryTest()
: QuicStreamFactoryTestBase(
GetParam().version,
GetParam().client_headers_include_h2_stream_dependency) {}
};
INSTANTIATE_TEST_SUITE_P(VersionIncludeStreamDependencySequence,
QuicStreamFactoryTest,
::testing::ValuesIn(GetTestParams()));
TEST_P(QuicStreamFactoryTest, Create) {
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
EXPECT_EQ(DEFAULT_PRIORITY, host_resolver_->last_request_priority());
QuicStreamRequest request2(factory_.get());
EXPECT_EQ(OK,
request2.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
// Will reset stream 3.
stream = CreateStream(&request2);
EXPECT_TRUE(stream.get());
// TODO(rtenneti): We should probably have a tests that HTTP and HTTPS result
// in streams on different sessions.
QuicStreamRequest request3(factory_.get());
EXPECT_EQ(OK,
request3.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
stream = CreateStream(&request3); // Will reset stream 5.
stream.reset(); // Will reset stream 7.
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
TEST_P(QuicStreamFactoryTest, CreateZeroRtt) {
Initialize();
factory_->set_is_quic_known_to_work_on_current_network(true);
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
socket_data.AddSocketDataToFactory(socket_factory_.get());
crypto_client_stream_factory_.set_handshake_mode(
MockCryptoClientStream::ZERO_RTT);
host_resolver_->set_synchronous_mode(true);
host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
"192.168.0.1", "");
QuicStreamRequest request(factory_.get());
EXPECT_EQ(OK,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
TEST_P(QuicStreamFactoryTest, DefaultInitialRtt) {
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
EXPECT_TRUE(session->require_confirmation());
EXPECT_EQ(100000u, session->connection()->GetStats().srtt_us);
ASSERT_FALSE(session->config()->HasInitialRoundTripTimeUsToSend());
}
TEST_P(QuicStreamFactoryTest, FactoryDestroyedWhenJobPending) {
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddSocketDataToFactory(socket_factory_.get());
auto request = std::make_unique<QuicStreamRequest>(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request->Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
request.reset();
EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
// Tearing down a QuicStreamFactory with a pending Job should not cause any
// crash. crbug.com/768343.
factory_.reset();
}
TEST_P(QuicStreamFactoryTest, RequireConfirmation) {
crypto_client_stream_factory_.set_handshake_mode(
MockCryptoClientStream::ZERO_RTT);
host_resolver_->set_synchronous_mode(true);
host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
"192.168.0.1", "");
Initialize();
factory_->set_is_quic_known_to_work_on_current_network(false);
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_FALSE(http_server_properties_->HasLastLocalAddressWhenQuicWorked());
crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
quic::QuicSession::HANDSHAKE_CONFIRMED);
EXPECT_TRUE(http_server_properties_->HasLastLocalAddressWhenQuicWorked());
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
EXPECT_TRUE(session->require_confirmation());
}
TEST_P(QuicStreamFactoryTest, DontRequireConfirmationFromSameIP) {
crypto_client_stream_factory_.set_handshake_mode(
MockCryptoClientStream::ZERO_RTT);
host_resolver_->set_synchronous_mode(true);
host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
"192.168.0.1", "");
Initialize();
factory_->set_is_quic_known_to_work_on_current_network(false);
http_server_properties_->SetLastLocalAddressWhenQuicWorked(
IPAddress(192, 0, 2, 33));
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_THAT(request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()),
IsOk());
EXPECT_FALSE(http_server_properties_->HasLastLocalAddressWhenQuicWorked());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
EXPECT_FALSE(session->require_confirmation());
crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
quic::QuicSession::HANDSHAKE_CONFIRMED);
EXPECT_TRUE(http_server_properties_->HasLastLocalAddressWhenQuicWorked());
}
TEST_P(QuicStreamFactoryTest, CachedInitialRtt) {
ServerNetworkStats stats;
stats.srtt = base::TimeDelta::FromMilliseconds(10);
http_server_properties_->SetServerNetworkStats(url::SchemeHostPort(url_),
NetworkIsolationKey(), stats);
test_params_.quic_params.estimate_initial_rtt = true;
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
EXPECT_EQ(10000u, session->connection()->GetStats().srtt_us);
ASSERT_TRUE(session->config()->HasInitialRoundTripTimeUsToSend());
EXPECT_EQ(10000u, session->config()->GetInitialRoundTripTimeUsToSend());
}
// Test that QUIC sessions use the cached RTT from HttpServerProperties for the
// correct NetworkIsolationKey.
TEST_P(QuicStreamFactoryTest, CachedInitialRttWithNetworkIsolationKey) {
const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures(
// enabled_features
{features::kPartitionHttpServerPropertiesByNetworkIsolationKey,
// Need to partition connections by NetworkIsolationKey for
// QuicSessionAliasKey to include NetworkIsolationKeys.
features::kPartitionConnectionsByNetworkIsolationKey},
// disabled_features
{});
// Since HttpServerProperties caches the feature value, have to create a new
// one.
http_server_properties_ = std::make_unique<HttpServerProperties>();
ServerNetworkStats stats;
stats.srtt = base::TimeDelta::FromMilliseconds(10);
http_server_properties_->SetServerNetworkStats(url::SchemeHostPort(url_),
kNetworkIsolationKey1, stats);
test_params_.quic_params.estimate_initial_rtt = true;
Initialize();
for (const auto& network_isolation_key :
{kNetworkIsolationKey1, kNetworkIsolationKey2, NetworkIsolationKey()}) {
SCOPED_TRACE(network_isolation_key.ToString());
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
QuicTestPacketMaker packet_maker(
version_, quic::QuicUtils::CreateRandomConnectionId(&random_generator_),
&clock_, kDefaultServerHostName, quic::Perspective::IS_CLIENT,
test_params_.quic_params.headers_include_h2_stream_dependency);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version)) {
socket_data.AddWrite(SYNCHRONOUS,
packet_maker.MakeInitialSettingsPacket(1));
}
socket_data.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), network_isolation_key,
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
QuicChromiumClientSession* session =
GetActiveSession(host_port_pair_, network_isolation_key);
if (network_isolation_key == kNetworkIsolationKey1) {
EXPECT_EQ(10000, session->connection()->GetStats().srtt_us);
ASSERT_TRUE(session->config()->HasInitialRoundTripTimeUsToSend());
EXPECT_EQ(10000u, session->config()->GetInitialRoundTripTimeUsToSend());
} else {
EXPECT_EQ(quic::kInitialRttMs * 1000,
session->connection()->GetStats().srtt_us);
EXPECT_FALSE(session->config()->HasInitialRoundTripTimeUsToSend());
}
}
}
TEST_P(QuicStreamFactoryTest, 2gInitialRtt) {
ScopedMockNetworkChangeNotifier notifier;
notifier.mock_network_change_notifier()->SetConnectionType(
NetworkChangeNotifier::CONNECTION_2G);
test_params_.quic_params.estimate_initial_rtt = true;
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
EXPECT_EQ(1200000u, session->connection()->GetStats().srtt_us);
ASSERT_TRUE(session->config()->HasInitialRoundTripTimeUsToSend());
EXPECT_EQ(1200000u, session->config()->GetInitialRoundTripTimeUsToSend());
}
TEST_P(QuicStreamFactoryTest, 3gInitialRtt) {
ScopedMockNetworkChangeNotifier notifier;
notifier.mock_network_change_notifier()->SetConnectionType(
NetworkChangeNotifier::CONNECTION_3G);
test_params_.quic_params.estimate_initial_rtt = true;
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
EXPECT_EQ(400000u, session->connection()->GetStats().srtt_us);
ASSERT_TRUE(session->config()->HasInitialRoundTripTimeUsToSend());
EXPECT_EQ(400000u, session->config()->GetInitialRoundTripTimeUsToSend());
}
TEST_P(QuicStreamFactoryTest, GoAway) {
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
session->OnGoAway(quic::QuicGoAwayFrame());
EXPECT_FALSE(HasActiveSession(host_port_pair_));
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
TEST_P(QuicStreamFactoryTest, GoAwayForConnectionMigrationWithPortOnly) {
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
session->OnGoAway(quic::QuicGoAwayFrame(
quic::kInvalidControlFrameId, quic::QUIC_ERROR_MIGRATING_PORT, 0,
"peer connection migration due to port change only"));
NetErrorDetails details;
EXPECT_FALSE(details.quic_port_migration_detected);
session->PopulateNetErrorDetails(&details);
EXPECT_TRUE(details.quic_port_migration_detected);
details.quic_port_migration_detected = false;
stream->PopulateNetErrorDetails(&details);
EXPECT_TRUE(details.quic_port_migration_detected);
EXPECT_FALSE(HasActiveSession(host_port_pair_));
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
// Makes sure that setting and clearing ServerNetworkStats respects the
// NetworkIsolationKey.
TEST_P(QuicStreamFactoryTest, ServerNetworkStatsWithNetworkIsolationKey) {
const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
const NetworkIsolationKey kNetworkIsolationKeys[] = {
kNetworkIsolationKey1, kNetworkIsolationKey2, NetworkIsolationKey()};
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures(
// enabled_features
{features::kPartitionHttpServerPropertiesByNetworkIsolationKey,
// Need to partition connections by NetworkIsolationKey for
// QuicSessionAliasKey to include NetworkIsolationKeys.
features::kPartitionConnectionsByNetworkIsolationKey},
// disabled_features
{});
// Since HttpServerProperties caches the feature value, have to create a new
// one.
http_server_properties_ = std::make_unique<HttpServerProperties>();
Initialize();
// For each server, set up and tear down a QUIC session cleanly, and check
// that stats have been added to HttpServerProperties using the correct
// NetworkIsolationKey.
for (size_t i = 0; i < base::size(kNetworkIsolationKeys); ++i) {
SCOPED_TRACE(i);
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
QuicTestPacketMaker packet_maker(
version_, quic::QuicUtils::CreateRandomConnectionId(&random_generator_),
&clock_, kDefaultServerHostName, quic::Perspective::IS_CLIENT,
test_params_.quic_params.headers_include_h2_stream_dependency);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version)) {
socket_data.AddWrite(SYNCHRONOUS,
packet_maker.MakeInitialSettingsPacket(1));
}
socket_data.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), kNetworkIsolationKeys[i],
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
QuicChromiumClientSession* session =
GetActiveSession(host_port_pair_, kNetworkIsolationKeys[i]);
session->OnGoAway(quic::QuicGoAwayFrame());
EXPECT_FALSE(HasActiveSession(host_port_pair_, kNetworkIsolationKeys[i]));
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
for (size_t j = 0; j < base::size(kNetworkIsolationKeys); ++j) {
// Stats up to kNetworkIsolationKeys[j] should have been populated, all
// others should remain empty.
if (j <= i) {
EXPECT_TRUE(http_server_properties_->GetServerNetworkStats(
url::SchemeHostPort(url_), kNetworkIsolationKeys[j]));
} else {
EXPECT_FALSE(http_server_properties_->GetServerNetworkStats(
url::SchemeHostPort(url_), kNetworkIsolationKeys[j]));
}
}
}
// Use unmocked crypto stream to do crypto connect, since crypto errors result
// in deleting network stats..
crypto_client_stream_factory_.set_handshake_mode(
MockCryptoClientStream::COLD_START_WITH_CHLO_SENT);
// For each server, simulate an error during session creation, and check that
// stats have been deleted from HttpServerProperties using the correct
// NetworkIsolationKey.
for (size_t i = 0; i < base::size(kNetworkIsolationKeys); ++i) {
SCOPED_TRACE(i);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
// Trigger PACKET_WRITE_ERROR when sending packets in crypto connect.
socket_data.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
socket_data.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), kNetworkIsolationKeys[i],
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsError(ERR_QUIC_HANDSHAKE_FAILED));
EXPECT_FALSE(HasActiveSession(host_port_pair_, kNetworkIsolationKeys[i]));
for (size_t j = 0; j < base::size(kNetworkIsolationKeys); ++j) {
// Stats up to kNetworkIsolationKeys[j] should have been deleted, all
// others should still be populated.
if (j <= i) {
EXPECT_FALSE(http_server_properties_->GetServerNetworkStats(
url::SchemeHostPort(url_), kNetworkIsolationKeys[j]));
} else {
EXPECT_TRUE(http_server_properties_->GetServerNetworkStats(
url::SchemeHostPort(url_), kNetworkIsolationKeys[j]));
}
}
}
}
TEST_P(QuicStreamFactoryTest, Pooling) {
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddSocketDataToFactory(socket_factory_.get());
HostPortPair server2(kServer2HostName, kDefaultServerPort);
host_resolver_->set_synchronous_mode(true);
host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
"192.168.0.1", "");
host_resolver_->rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
QuicStreamRequest request(factory_.get());
EXPECT_EQ(OK,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
TestCompletionCallback callback;
QuicStreamRequest request2(factory_.get());
EXPECT_EQ(OK,
request2.Request(
server2, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
NetworkIsolationKey(),
/*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback.callback()));
std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
EXPECT_TRUE(stream2.get());
EXPECT_EQ(GetActiveSession(host_port_pair_), GetActiveSession(server2));
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
TEST_P(QuicStreamFactoryTest, PoolingWithServerMigration) {
// Set up session to migrate.
host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
"192.168.0.1", "");
IPEndPoint alt_address = IPEndPoint(IPAddress(1, 2, 3, 4), 443);
quic::QuicConfig config;
config.SetAlternateServerAddressToSend(ToQuicSocketAddress(alt_address));
VerifyServerMigration(config, alt_address);
// Close server-migrated session.
QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
session->CloseSessionOnError(0u, quic::QUIC_NO_ERROR,
quic::ConnectionCloseBehavior::SILENT_CLOSE);
client_maker_.Reset();
client_maker_.set_coalesce_http_frames(false);
// Set up server IP, socket, proof, and config for new session.
HostPortPair server2(kServer2HostName, kDefaultServerPort);
host_resolver_->rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
MockQuicData socket_data1(version_);
socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data1.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data1.AddSocketDataToFactory(socket_factory_.get());
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
quic::QuicConfig config2;
crypto_client_stream_factory_.SetConfig(config2);
// Create new request to cause new session creation.
TestCompletionCallback callback;
QuicStreamRequest request2(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request2.Request(
server2, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
NetworkIsolationKey(),
/*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback.callback()));
EXPECT_EQ(OK, callback.WaitForResult());
std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
EXPECT_TRUE(stream2.get());
EXPECT_TRUE(socket_data1.AllReadDataConsumed());
EXPECT_TRUE(socket_data1.AllWriteDataConsumed());
// EXPECT_EQ(GetActiveSession(host_port_pair_), GetActiveSession(server2));
}
TEST_P(QuicStreamFactoryTest, NoPoolingAfterGoAway) {
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data1(version_);
socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data1.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data1.AddSocketDataToFactory(socket_factory_.get());
client_maker_.Reset();
MockQuicData socket_data2(version_);
socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data2.AddSocketDataToFactory(socket_factory_.get());
HostPortPair server2(kServer2HostName, kDefaultServerPort);
host_resolver_->set_synchronous_mode(true);
host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
"192.168.0.1", "");
host_resolver_->rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
QuicStreamRequest request(factory_.get());
EXPECT_EQ(OK,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
TestCompletionCallback callback;
QuicStreamRequest request2(factory_.get());
EXPECT_EQ(OK,
request2.Request(
server2, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
NetworkIsolationKey(),
/*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback.callback()));
std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
EXPECT_TRUE(stream2.get());
factory_->OnSessionGoingAway(GetActiveSession(host_port_pair_));
EXPECT_FALSE(HasActiveSession(host_port_pair_));
EXPECT_FALSE(HasActiveSession(server2));
TestCompletionCallback callback3;
QuicStreamRequest request3(factory_.get());
EXPECT_EQ(OK,
request3.Request(
server2, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
NetworkIsolationKey(),
/*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback3.callback()));
std::unique_ptr<HttpStream> stream3 = CreateStream(&request3);
EXPECT_TRUE(stream3.get());
EXPECT_TRUE(HasActiveSession(server2));
EXPECT_TRUE(socket_data1.AllReadDataConsumed());
EXPECT_TRUE(socket_data1.AllWriteDataConsumed());
EXPECT_TRUE(socket_data2.AllReadDataConsumed());
EXPECT_TRUE(socket_data2.AllWriteDataConsumed());
}
TEST_P(QuicStreamFactoryTest, HttpsPooling) {
Initialize();
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddSocketDataToFactory(socket_factory_.get());
HostPortPair server1(kDefaultServerHostName, 443);
HostPortPair server2(kServer2HostName, 443);
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
host_resolver_->set_synchronous_mode(true);
host_resolver_->rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", "");
host_resolver_->rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
QuicStreamRequest request(factory_.get());
EXPECT_EQ(OK,
request.Request(
server1, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
TestCompletionCallback callback;
QuicStreamRequest request2(factory_.get());
EXPECT_EQ(OK,
request2.Request(
server2, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
NetworkIsolationKey(),
/*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
EXPECT_TRUE(stream2.get());
EXPECT_EQ(GetActiveSession(server1), GetActiveSession(server2));
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
TEST_P(QuicStreamFactoryTest, HttpsPoolingWithMatchingPins) {
Initialize();
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddSocketDataToFactory(socket_factory_.get());
HostPortPair server1(kDefaultServerHostName, 443);
HostPortPair server2(kServer2HostName, 443);
transport_security_state_.EnableStaticPinsForTesting();
ScopedTransportSecurityStateSource scoped_security_state_source;
HashValue primary_pin(HASH_VALUE_SHA256);
EXPECT_TRUE(primary_pin.FromString(
"sha256/Nn8jk5By4Vkq6BeOVZ7R7AC6XUUBZsWmUbJR1f1Y5FY="));
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
verify_details.cert_verify_result.public_key_hashes.push_back(primary_pin);
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
host_resolver_->set_synchronous_mode(true);
host_resolver_->rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", "");
host_resolver_->rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
QuicStreamRequest request(factory_.get());
EXPECT_EQ(OK,
request.Request(
server1, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
TestCompletionCallback callback;
QuicStreamRequest request2(factory_.get());
EXPECT_EQ(OK,
request2.Request(
server2, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
NetworkIsolationKey(),
/*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
EXPECT_TRUE(stream2.get());
EXPECT_EQ(GetActiveSession(server1), GetActiveSession(server2));
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithDifferentPins) {
Initialize();
MockQuicData socket_data1(version_);
socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data1.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data1.AddSocketDataToFactory(socket_factory_.get());
client_maker_.Reset();
MockQuicData socket_data2(version_);
socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data2.AddSocketDataToFactory(socket_factory_.get());
HostPortPair server1(kDefaultServerHostName, 443);
HostPortPair server2(kServer2HostName, 443);
transport_security_state_.EnableStaticPinsForTesting();
ScopedTransportSecurityStateSource scoped_security_state_source;
ProofVerifyDetailsChromium verify_details1 = DefaultProofVerifyDetails();
uint8_t bad_pin = 3;
verify_details1.cert_verify_result.public_key_hashes.push_back(
test::GetTestHashValue(bad_pin));
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details1);
HashValue primary_pin(HASH_VALUE_SHA256);
EXPECT_TRUE(primary_pin.FromString(
"sha256/Nn8jk5By4Vkq6BeOVZ7R7AC6XUUBZsWmUbJR1f1Y5FY="));
ProofVerifyDetailsChromium verify_details2 = DefaultProofVerifyDetails();
verify_details2.cert_verify_result.public_key_hashes.push_back(primary_pin);
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details2);
host_resolver_->set_synchronous_mode(true);
host_resolver_->rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", "");
host_resolver_->rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
QuicStreamRequest request(factory_.get());
EXPECT_EQ(OK,
request.Request(
server1, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
TestCompletionCallback callback;
QuicStreamRequest request2(factory_.get());
EXPECT_EQ(OK,
request2.Request(
server2, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
NetworkIsolationKey(),
/*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
EXPECT_TRUE(stream2.get());
EXPECT_NE(GetActiveSession(server1), GetActiveSession(server2));
EXPECT_TRUE(socket_data1.AllReadDataConsumed());
EXPECT_TRUE(socket_data1.AllWriteDataConsumed());
EXPECT_TRUE(socket_data2.AllReadDataConsumed());
EXPECT_TRUE(socket_data2.AllWriteDataConsumed());
}
TEST_P(QuicStreamFactoryTest, Goaway) {
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddSocketDataToFactory(socket_factory_.get());
client_maker_.Reset();
MockQuicData socket_data2(version_);
socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data2.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
// Mark the session as going away. Ensure that while it is still alive
// that it is no longer active.
QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
factory_->OnSessionGoingAway(session);
EXPECT_EQ(true,
QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
EXPECT_FALSE(HasActiveSession(host_port_pair_));
// Create a new request for the same destination and verify that a
// new session is created.
QuicStreamRequest request2(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request2.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
EXPECT_TRUE(stream2.get());
EXPECT_TRUE(HasActiveSession(host_port_pair_));
EXPECT_NE(session, GetActiveSession(host_port_pair_));
EXPECT_EQ(true,
QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
stream2.reset();
stream.reset();
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
EXPECT_TRUE(socket_data2.AllReadDataConsumed());
EXPECT_TRUE(socket_data2.AllWriteDataConsumed());
}
TEST_P(QuicStreamFactoryTest, MaxOpenStream) {
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
quic::QuicStreamId stream_id = GetNthClientInitiatedBidirectionalStreamId(0);
MockQuicData socket_data(version_);
if (version_.transport_version == quic::QUIC_VERSION_99) {
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddWrite(SYNCHRONOUS, client_maker_.MakeStreamsBlockedPacket(
2, true, 50,
/*unidirectional=*/false));
socket_data.AddWrite(
SYNCHRONOUS, client_maker_.MakeRstPacket(3, true, stream_id,
quic::QUIC_STREAM_CANCELLED));
socket_data.AddRead(
ASYNC, server_maker_.MakeRstPacket(1, false, stream_id,
quic::QUIC_STREAM_CANCELLED));
socket_data.AddRead(
ASYNC, server_maker_.MakeMaxStreamsPacket(4, true, 52,
/*unidirectional=*/false));
} else {
socket_data.AddWrite(
SYNCHRONOUS, client_maker_.MakeRstPacket(1, true, stream_id,
quic::QUIC_STREAM_CANCELLED));
socket_data.AddRead(
ASYNC, server_maker_.MakeRstPacket(1, false, stream_id,
quic::QUIC_STREAM_CANCELLED));
}
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
socket_data.AddSocketDataToFactory(socket_factory_.get());
HttpRequestInfo request_info;
request_info.traffic_annotation =
MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
std::vector<std::unique_ptr<HttpStream>> streams;
// The MockCryptoClientStream sets max_open_streams to be
// quic::kDefaultMaxStreamsPerConnection / 2.
for (size_t i = 0; i < quic::kDefaultMaxStreamsPerConnection / 2; i++) {
QuicStreamRequest request(factory_.get());
int rv = request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY, SocketTag(),
NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback());
if (i == 0) {
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
} else {
EXPECT_THAT(rv, IsOk());
}
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream);
EXPECT_EQ(OK,
stream->InitializeStream(&request_info, false, DEFAULT_PRIORITY,
net_log_, CompletionOnceCallback()));
streams.push_back(std::move(stream));
}
QuicStreamRequest request(factory_.get());
EXPECT_EQ(OK,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, CompletionOnceCallback()));
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream);
EXPECT_EQ(ERR_IO_PENDING,
stream->InitializeStream(&request_info, false, DEFAULT_PRIORITY,
net_log_, callback_.callback()));
// Close the first stream.
streams.front()->Close(false);
// Trigger exchange of RSTs that in turn allow progress for the last
// stream.
base::RunLoop().RunUntilIdle();
EXPECT_THAT(callback_.WaitForResult(), IsOk());
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
// Force close of the connection to suppress the generation of RST
// packets when streams are torn down, which wouldn't be relevant to
// this test anyway.
QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
session->connection()->CloseConnection(
quic::QUIC_PUBLIC_RESET, "test",
quic::ConnectionCloseBehavior::SILENT_CLOSE);
}
TEST_P(QuicStreamFactoryTest, ResolutionErrorInCreate) {
Initialize();
MockQuicData socket_data(version_);
socket_data.AddSocketDataToFactory(socket_factory_.get());
host_resolver_->rules()->AddSimulatedFailure(kDefaultServerHostName);
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsError(ERR_NAME_NOT_RESOLVED));
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
TEST_P(QuicStreamFactoryTest, ConnectErrorInCreate) {
Initialize();
MockQuicData socket_data(version_);
socket_data.AddConnect(SYNCHRONOUS, ERR_ADDRESS_IN_USE);
socket_data.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsError(ERR_ADDRESS_IN_USE));
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
TEST_P(QuicStreamFactoryTest, CancelCreate) {
Initialize();
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data.AddSocketDataToFactory(socket_factory_.get());
{
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
}
base::RunLoop().RunUntilIdle();
QuicStreamRequest request2(factory_.get());
EXPECT_EQ(OK,
request2.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
std::unique_ptr<HttpStream> stream = CreateStream(&request2);
EXPECT_TRUE(stream.get());
stream.reset();
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
TEST_P(QuicStreamFactoryTest, CloseAllSessions) {
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
int packet_num = 1;
if (VersionUsesQpack(version_.transport_version)) {
socket_data.AddWrite(SYNCHRONOUS,
ConstructInitialSettingsPacket(packet_num++));
}
socket_data.AddWrite(
SYNCHRONOUS,
ConstructClientRstPacket(packet_num++, quic::QUIC_RST_ACKNOWLEDGEMENT));
socket_data.AddWrite(
SYNCHRONOUS,
client_maker_.MakeConnectionClosePacket(
packet_num++, true, quic::QUIC_PEER_GOING_AWAY, "net error"));
socket_data.AddSocketDataToFactory(socket_factory_.get());
client_maker_.Reset();
MockQuicData socket_data2(version_);
socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data2.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
HttpRequestInfo request_info;
request_info.traffic_annotation =
MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_EQ(OK, stream->InitializeStream(&request_info, false, DEFAULT_PRIORITY,
net_log_, CompletionOnceCallback()));
// Close the session and verify that stream saw the error.
factory_->CloseAllSessions(ERR_INTERNET_DISCONNECTED,
quic::QUIC_PEER_GOING_AWAY);
EXPECT_EQ(ERR_INTERNET_DISCONNECTED,
stream->ReadResponseHeaders(callback_.callback()));
// Now attempting to request a stream to the same origin should create
// a new session.
QuicStreamRequest request2(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request2.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
stream = CreateStream(&request2);
stream.reset(); // Will reset stream 3.
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
EXPECT_TRUE(socket_data2.AllReadDataConsumed());
EXPECT_TRUE(socket_data2.AllWriteDataConsumed());
}
// Regression test for crbug.com/700617. Test a write error during the
// crypto handshake will not hang QuicStreamFactory::Job and should
// report QUIC_HANDSHAKE_FAILED to upper layers. Subsequent
// QuicStreamRequest should succeed without hanging.
TEST_P(QuicStreamFactoryTest,
WriteErrorInCryptoConnectWithAsyncHostResolution) {
Initialize();
// Use unmocked crypto stream to do crypto connect.
crypto_client_stream_factory_.set_handshake_mode(
MockCryptoClientStream::COLD_START_WITH_CHLO_SENT);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
// Trigger PACKET_WRITE_ERROR when sending packets in crypto connect.
socket_data.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
socket_data.AddSocketDataToFactory(socket_factory_.get());
// Create request, should fail after the write of the CHLO fails.
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_EQ(ERR_QUIC_HANDSHAKE_FAILED, callback_.WaitForResult());
EXPECT_FALSE(HasActiveSession(host_port_pair_));
EXPECT_FALSE(HasActiveJob(host_port_pair_, privacy_mode_));
// Verify new requests can be sent normally without hanging.
crypto_client_stream_factory_.set_handshake_mode(
MockCryptoClientStream::COLD_START);
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
client_maker_.Reset();
MockQuicData socket_data2(version_);
socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data2.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request2(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request2.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_FALSE(HasActiveSession(host_port_pair_));
EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
// Run the message loop to complete host resolution.
base::RunLoop().RunUntilIdle();
// Complete handshake. QuicStreamFactory::Job should complete and succeed.
crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
quic::QuicSession::HANDSHAKE_CONFIRMED);
EXPECT_THAT(callback_.WaitForResult(), IsOk());
EXPECT_TRUE(HasActiveSession(host_port_pair_));
EXPECT_FALSE(HasActiveJob(host_port_pair_, privacy_mode_));
// Create QuicHttpStream.
std::unique_ptr<HttpStream> stream = CreateStream(&request2);
EXPECT_TRUE(stream.get());
stream.reset();
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
EXPECT_TRUE(socket_data2.AllReadDataConsumed());
EXPECT_TRUE(socket_data2.AllWriteDataConsumed());
}
TEST_P(QuicStreamFactoryTest, WriteErrorInCryptoConnectWithSyncHostResolution) {
Initialize();
// Use unmocked crypto stream to do crypto connect.
crypto_client_stream_factory_.set_handshake_mode(
MockCryptoClientStream::COLD_START_WITH_CHLO_SENT);
host_resolver_->set_synchronous_mode(true);
host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(),
"192.168.0.1", "");
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
// Trigger PACKET_WRITE_ERROR when sending packets in crypto connect.
socket_data.AddWrite(SYNCHRONOUS, ERR_ADDRESS_UNREACHABLE);
socket_data.AddSocketDataToFactory(socket_factory_.get());
// Create request, should fail immediately.
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_QUIC_HANDSHAKE_FAILED,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
// Check no active session, or active jobs left for this server.
EXPECT_FALSE(HasActiveSession(host_port_pair_));
EXPECT_FALSE(HasActiveJob(host_port_pair_, privacy_mode_));
// Verify new requests can be sent normally without hanging.
crypto_client_stream_factory_.set_handshake_mode(
MockCryptoClientStream::COLD_START);
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
client_maker_.Reset();
MockQuicData socket_data2(version_);
socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data2.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request2(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request2.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_FALSE(HasActiveSession(host_port_pair_));
EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_));
// Complete handshake.
crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
quic::QuicSession::HANDSHAKE_CONFIRMED);
EXPECT_THAT(callback_.WaitForResult(), IsOk());
EXPECT_TRUE(HasActiveSession(host_port_pair_));
EXPECT_FALSE(HasActiveJob(host_port_pair_, privacy_mode_));
// Create QuicHttpStream.
std::unique_ptr<HttpStream> stream = CreateStream(&request2);
EXPECT_TRUE(stream.get());
stream.reset();
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
EXPECT_TRUE(socket_data2.AllReadDataConsumed());
EXPECT_TRUE(socket_data2.AllWriteDataConsumed());
}
TEST_P(QuicStreamFactoryTest, CloseSessionsOnIPAddressChanged) {
test_params_.quic_params.close_sessions_on_ip_change = true;
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
int packet_num = 1;
if (VersionUsesQpack(version_.transport_version)) {
socket_data.AddWrite(SYNCHRONOUS,
ConstructInitialSettingsPacket(packet_num++));
}
socket_data.AddWrite(
SYNCHRONOUS,
ConstructClientRstPacket(packet_num++, quic::QUIC_RST_ACKNOWLEDGEMENT));
socket_data.AddWrite(
SYNCHRONOUS,
client_maker_.MakeConnectionClosePacket(
packet_num, true, quic::QUIC_IP_ADDRESS_CHANGED, "net error"));
socket_data.AddSocketDataToFactory(socket_factory_.get());
client_maker_.Reset();
MockQuicData socket_data2(version_);
socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
if (VersionUsesQpack(version_.transport_version))
socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
socket_data2.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
HttpRequestInfo request_info;
request_info.traffic_annotation =
MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_EQ(OK, stream->InitializeStream(&request_info, false, DEFAULT_PRIORITY,
net_log_, CompletionOnceCallback()));
// Check an active session exists for the destination.
EXPECT_TRUE(HasActiveSession(host_port_pair_));
QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
EXPECT_TRUE(http_server_properties_->HasLastLocalAddressWhenQuicWorked());
// Change the IP address and verify that stream saw the error and the active
// session is closed.
NotifyIPAddressChanged();
EXPECT_EQ(ERR_NETWORK_CHANGED,
stream->ReadResponseHeaders(callback_.callback()));
EXPECT_FALSE(factory_->is_quic_known_to_work_on_current_network());
EXPECT_FALSE(http_server_properties_->HasLastLocalAddressWhenQuicWorked());
// Check no active session exists for the destination.
EXPECT_FALSE(HasActiveSession(host_port_pair_));
// Now attempting to request a stream to the same origin should create
// a new session.
QuicStreamRequest request2(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request2.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
stream = CreateStream(&request2);
// Check a new active session exisits for the destination and the old session
// is no longer live.
EXPECT_TRUE(HasActiveSession(host_port_pair_));
QuicChromiumClientSession* session2 = GetActiveSession(host_port_pair_);
EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session2));
stream.reset(); // Will reset stream 3.
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
EXPECT_TRUE(socket_data2.AllReadDataConsumed());
EXPECT_TRUE(socket_data2.AllWriteDataConsumed());
}
// Test that if goaway_session_on_ip_change is set, old sessions will be marked
// as going away on IP address change instead of being closed. New requests will
// go to a new connection.
TEST_P(QuicStreamFactoryTest, GoAwaySessionsOnIPAddressChanged) {
test_params_.quic_params.goaway_sessions_on_ip_change = true;
Initialize();
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData quic_data1(version_);
int packet_num = 1;
if (VersionUsesQpack(version_.transport_version)) {
quic_data1.AddWrite(SYNCHRONOUS,
ConstructInitialSettingsPacket(packet_num++));
}
quic_data1.AddWrite(
SYNCHRONOUS,
ConstructGetRequestPacket(packet_num++,
GetNthClientInitiatedBidirectionalStreamId(0),
true, true));
quic_data1.AddRead(ASYNC, ERR_IO_PENDING); // Pause
quic_data1.AddRead(
ASYNC,
ConstructOkResponsePacket(
1, GetNthClientInitiatedBidirectionalStreamId(0), false, true));
quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read.
quic_data1.AddSocketDataToFactory(socket_factory_.get());
client_maker_.Reset();
MockQuicData quic_data2(version_);
quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read.
if (VersionUsesQpack(version_.transport_version))
quic_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(1));
quic_data2.AddSocketDataToFactory(socket_factory_.get());
// Create request and QuicHttpStream.
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
// Cause QUIC stream to be created.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = url_;
request_info.traffic_annotation =
MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_EQ(OK, stream->InitializeStream(&request_info, true, DEFAULT_PRIORITY,
net_log_, CompletionOnceCallback()));
// Ensure that session is alive and active.
QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
EXPECT_TRUE(HasActiveSession(host_port_pair_));
// Send GET request on stream.
HttpResponseInfo response;
HttpRequestHeaders request_headers;
EXPECT_EQ(OK, stream->SendRequest(request_headers, &response,
callback_.callback()));
// Receive an IP address change notification.
NotifyIPAddressChanged();
// The connection should still be alive, but marked as going away.
EXPECT_FALSE(HasActiveSession(host_port_pair_));
EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
EXPECT_EQ(1u, session->GetNumActiveStreams());
// Resume the data, response should be read from the original connection.
quic_data1.Resume();
EXPECT_EQ(OK, stream->ReadResponseHeaders(callback_.callback()));
EXPECT_EQ(200, response.headers->response_code());
EXPECT_EQ(0u, session->GetNumActiveStreams());
// Second request should be sent on a new connection.
QuicStreamRequest request2(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request2.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream2 = CreateStream(&request2);
EXPECT_TRUE(stream2.get());
// Check an active session exisits for the destination.
EXPECT_TRUE(HasActiveSession(host_port_pair_));
EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
QuicChromiumClientSession* session2 = GetActiveSession(host_port_pair_);
EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session2));
stream.reset();
stream2.reset();
EXPECT_TRUE(quic_data1.AllReadDataConsumed());
EXPECT_TRUE(quic_data1.AllWriteDataConsumed());
EXPECT_TRUE(quic_data2.AllReadDataConsumed());
EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
}
TEST_P(QuicStreamFactoryTest, OnIPAddressChangedWithConnectionMigration) {
InitializeConnectionMigrationV2Test(
{kDefaultNetworkForTests, kNewNetworkForTests});
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
MockQuicData socket_data(version_);
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
int packet_num = 1;
if (VersionUsesQpack(version_.transport_version)) {
socket_data.AddWrite(SYNCHRONOUS,
ConstructInitialSettingsPacket(packet_num++));
}
socket_data.AddWrite(
SYNCHRONOUS,
ConstructClientRstPacket(packet_num, quic::QUIC_STREAM_CANCELLED));
socket_data.AddSocketDataToFactory(socket_factory_.get());
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
HttpRequestInfo request_info;
request_info.traffic_annotation =
MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_EQ(OK, stream->InitializeStream(&request_info, false, DEFAULT_PRIORITY,
net_log_, CompletionOnceCallback()));
EXPECT_TRUE(http_server_properties_->HasLastLocalAddressWhenQuicWorked());
// Change the IP address and verify that the connection is unaffected.
NotifyIPAddressChanged();
EXPECT_TRUE(factory_->is_quic_known_to_work_on_current_network());
EXPECT_TRUE(http_server_properties_->HasLastLocalAddressWhenQuicWorked());
// Attempting a new request to the same origin uses the same connection.
QuicStreamRequest request2(factory_.get());
EXPECT_EQ(OK,
request2.Request(
host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
SocketTag(), NetworkIsolationKey(),
/*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
failed_on_default_network_callback_, callback_.callback()));
stream = CreateStream(&request2);
stream.reset();
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
TEST_P(QuicStreamFactoryTest, MigrateOnNetworkMadeDefaultWithSynchronousWrite) {
TestMigrationOnNetworkMadeDefault(SYNCHRONOUS);
}
TEST_P(QuicStreamFactoryTest, MigrateOnNetworkMadeDefaultWithAsyncWrite) {
TestMigrationOnNetworkMadeDefault(ASYNC);
}
// Sets up a test which attempts connection migration successfully after probing
// when a new network is made as default and the old default is still available.
// |write_mode| specifies the write mode for the last write before
// OnNetworkMadeDefault is delivered to session.
void QuicStreamFactoryTestBase::TestMigrationOnNetworkMadeDefault(
IoMode write_mode) {
InitializeConnectionMigrationV2Test({kDefaultNetworkForTests});
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
// Using a testing task runner so that we can control time.
auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
scoped_mock_network_change_notifier_->mock_network_change_notifier()
->QueueNetworkMadeDefault(kDefaultNetworkForTests);
MockQuicData quic_data1(version_);
quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging Read.
int packet_num = 1;
if (VersionUsesQpack(version_.transport_version)) {
quic_data1.AddWrite(SYNCHRONOUS,
ConstructInitialSettingsPacket(packet_num++));
}
quic_data1.AddWrite(
write_mode,
ConstructGetRequestPacket(packet_num++,
GetNthClientInitiatedBidirectionalStreamId(0),
true, true));
quic_data1.AddSocketDataToFactory(socket_factory_.get());