| // 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/simple_test_tick_clock.h" |
| #include "base/test/test_mock_time_task_runner.h" |
| #include "build/build_config.h" |
| #include "net/base/mock_network_change_notifier.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_impl.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/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_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/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/test/cert_test_util.h" |
| #include "net/test/gtest_util.h" |
| #include "net/test/test_data_directory.h" |
| #include "net/test/test_with_scoped_task_environment.h" |
| #include "net/third_party/quic/core/crypto/crypto_handshake.h" |
| #include "net/third_party/quic/core/crypto/quic_crypto_client_config.h" |
| #include "net/third_party/quic/core/crypto/quic_decrypter.h" |
| #include "net/third_party/quic/core/crypto/quic_encrypter.h" |
| #include "net/third_party/quic/core/http/quic_client_promised_info.h" |
| #include "net/third_party/quic/core/quic_utils.h" |
| #include "net/third_party/quic/platform/api/quic_test.h" |
| #include "net/third_party/quic/test_tools/mock_clock.h" |
| #include "net/third_party/quic/test_tools/mock_random.h" |
| #include "net/third_party/quic/test_tools/quic_config_peer.h" |
| #include "net/third_party/quic/test_tools/quic_spdy_session_peer.h" |
| #include "net/third_party/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 { |
| |
| class MockSSLConfigService : public SSLConfigService { |
| public: |
| MockSSLConfigService() {} |
| ~MockSSLConfigService() override {} |
| |
| void GetSSLConfig(SSLConfig* config) override { *config = config_; } |
| |
| bool CanShareConnectionWithClientCerts( |
| const std::string& hostname) const override { |
| return false; |
| } |
| |
| private: |
| SSLConfig config_; |
| }; |
| |
| } // namespace |
| |
| 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: " << QuicVersionToString(p.version) |
| << ", client_headers_include_h2_stream_dependency: " |
| << p.client_headers_include_h2_stream_dependency << " }"; |
| return os; |
| } |
| |
| quic::QuicTransportVersion version; |
| bool client_headers_include_h2_stream_dependency; |
| }; |
| |
| std::vector<TestParams> GetTestParams() { |
| std::vector<TestParams> params; |
| quic::QuicTransportVersionVector all_supported_versions = |
| quic::AllSupportedTransportVersions(); |
| for (const auto& version : all_supported_versions) { |
| 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: " << QuicVersionToString(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::QuicTransportVersion version; |
| DestinationType destination_type; |
| bool client_headers_include_h2_stream_dependency; |
| }; |
| |
| std::vector<PoolingTestParams> GetPoolingTestParams() { |
| std::vector<PoolingTestParams> params; |
| quic::QuicTransportVersionVector all_supported_versions = |
| quic::AllSupportedTransportVersions(); |
| for (const quic::QuicTransportVersion version : all_supported_versions) { |
| 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(); |
| } |
| }; |
| |
| // TestConnectionMigrationSocketFactory will vend sockets with incremental port |
| // number. |
| class TestConnectionMigrationSocketFactory : public MockClientSocketFactory { |
| public: |
| TestConnectionMigrationSocketFactory() : next_source_port_num_(1u) {} |
| ~TestConnectionMigrationSocketFactory() 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(TestConnectionMigrationSocketFactory); |
| }; |
| |
| class QuicStreamFactoryTestBase : public WithScopedTaskEnvironment { |
| protected: |
| QuicStreamFactoryTestBase(quic::QuicTransportVersion version, |
| bool client_headers_include_h2_stream_dependency) |
| : host_resolver_(new MockHostResolver), |
| ssl_config_service_(new MockSSLConfigService), |
| 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), |
| 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), |
| store_server_configs_in_properties_(false) { |
| test_params_.quic_headers_include_h2_stream_dependency = |
| client_headers_include_h2_stream_dependency; |
| clock_.AdvanceTime(quic::QuicTime::Delta::FromSeconds(1)); |
| } |
| |
| void Initialize() { |
| DCHECK(!factory_); |
| factory_.reset(new QuicStreamFactory( |
| net_log_.net_log(), host_resolver_.get(), ssl_config_service_.get(), |
| socket_factory_.get(), &http_server_properties_, 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_max_packet_length, test_params_.quic_user_agent_id, |
| store_server_configs_in_properties_, |
| test_params_.quic_close_sessions_on_ip_change, |
| test_params_.quic_goaway_sessions_on_ip_change, |
| test_params_.mark_quic_broken_when_network_blackholes, |
| test_params_.quic_idle_connection_timeout_seconds, |
| test_params_.quic_reduced_ping_timeout_seconds, |
| test_params_.quic_retransmittable_on_wire_timeout_milliseconds, |
| test_params_.quic_max_time_before_crypto_handshake_seconds, |
| test_params_.quic_max_idle_time_before_crypto_handshake_seconds, |
| test_params_.quic_migrate_sessions_on_network_change_v2, |
| test_params_.quic_migrate_sessions_early_v2, |
| test_params_.quic_retry_on_alternate_network_before_handshake, |
| test_params_.quic_migrate_idle_sessions, |
| test_params_.quic_idle_session_migration_period, |
| test_params_.quic_max_time_on_non_default_network, |
| test_params_.quic_max_migrations_to_non_default_network_on_write_error, |
| test_params_ |
| .quic_max_migrations_to_non_default_network_on_path_degrading, |
| test_params_.quic_allow_server_migration, |
| test_params_.quic_race_stale_dns_on_connection, |
| test_params_.quic_go_away_on_path_degrading, |
| test_params_.quic_race_cert_verification, |
| test_params_.quic_estimate_initial_rtt, |
| test_params_.quic_headers_include_h2_stream_dependency, |
| test_params_.quic_connection_options, |
| test_params_.quic_client_connection_options, |
| test_params_.quic_enable_socket_recv_optimization)); |
| } |
| |
| 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_migrate_sessions_on_network_change_v2 = true; |
| test_params_.quic_migrate_sessions_early_v2 = true; |
| socket_factory_.reset(new TestConnectionMigrationSocketFactory); |
| 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) { |
| quic::QuicServerId server_id(host_port_pair.host(), host_port_pair.port(), |
| false); |
| return QuicStreamFactoryPeer::HasActiveSession(factory_.get(), server_id); |
| } |
| |
| 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) { |
| quic::QuicServerId server_id(host_port_pair.host(), host_port_pair.port(), |
| false); |
| return QuicStreamFactoryPeer::GetActiveSession(factory_.get(), server_id); |
| } |
| |
| 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; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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, |
| quic::QuicStreamOffset* offset) { |
| 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, offset); |
| } |
| |
| std::unique_ptr<quic::QuicEncryptedPacket> ConstructGetRequestPacket( |
| uint64_t packet_number, |
| quic::QuicStreamId stream_id, |
| bool should_include_version, |
| bool fin, |
| quic::QuicStreamOffset* offset) { |
| return ConstructGetRequestPacket(packet_number, stream_id, |
| /*parent_stream_id=*/0, |
| should_include_version, fin, offset); |
| } |
| |
| 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, nullptr); |
| } |
| |
| std::unique_ptr<quic::QuicReceivedPacket> ConstructInitialSettingsPacket( |
| uint64_t packet_number, |
| quic::QuicStreamOffset* offset) { |
| return client_maker_.MakeInitialSettingsPacket(packet_number, offset); |
| } |
| |
| // Helper method for server migration tests. |
| void VerifyServerMigration(const quic::QuicConfig& config, |
| IPEndPoint expected_address) { |
| test_params_.quic_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; |
| socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| socket_data1.AddSocketDataToFactory(socket_factory_.get()); |
| |
| // Set up second socket data provider that is used after |
| // migration. |
| MockQuicData socket_data2; |
| socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| socket_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket()); |
| socket_data2.AddWrite( |
| SYNCHRONOUS, client_maker_.MakePingPacket(2, /*include_version=*/true)); |
| socket_data2.AddWrite( |
| SYNCHRONOUS, client_maker_.MakeRstPacket( |
| 3, 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(), |
| /*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() { |
| store_server_configs_in_properties_ = true; |
| test_params_.quic_idle_connection_timeout_seconds = 500; |
| Initialize(); |
| factory_->set_require_confirmation(false); |
| 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_), 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, 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_.alternative_service_map().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_); |
| |
| // 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_); |
| // 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; |
| 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(), |
| /*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; |
| 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(), |
| /*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_, n); |
| } |
| |
| quic::QuicStreamId GetNthServerInitiatedUnidirectionalStreamId(int n) { |
| return quic::test::GetNthServerInitiatedUnidirectionalStreamId(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); |
| |
| 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::QuicTransportVersion version_; |
| QuicTestPacketMaker client_maker_; |
| QuicTestPacketMaker server_maker_; |
| HttpServerPropertiesImpl 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_; |
| bool store_server_configs_in_properties_; |
| }; |
| |
| 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; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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(), |
| /*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(), |
| /*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_require_confirmation(false); |
| ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
| crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
| |
| MockQuicData socket_data; |
| 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(), |
| /*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; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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_require_confirmation(true); |
| ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
| crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
| |
| MockQuicData socket_data; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_, |
| failed_on_default_network_callback_, callback_.callback())); |
| |
| IPAddress last_address; |
| EXPECT_FALSE(http_server_properties_.GetSupportsQuic(&last_address)); |
| |
| crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent( |
| quic::QuicSession::HANDSHAKE_CONFIRMED); |
| |
| EXPECT_TRUE(http_server_properties_.GetSupportsQuic(&last_address)); |
| |
| 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_require_confirmation(true); |
| http_server_properties_.SetSupportsQuic(IPAddress(192, 0, 2, 33)); |
| |
| ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
| crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
| |
| MockQuicData socket_data; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_, |
| failed_on_default_network_callback_, callback_.callback()), |
| IsOk()); |
| |
| IPAddress last_address; |
| EXPECT_FALSE(http_server_properties_.GetSupportsQuic(&last_address)); |
| |
| 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_.GetSupportsQuic(&last_address)); |
| } |
| |
| TEST_P(QuicStreamFactoryTest, CachedInitialRtt) { |
| ServerNetworkStats stats; |
| stats.srtt = base::TimeDelta::FromMilliseconds(10); |
| http_server_properties_.SetServerNetworkStats(url::SchemeHostPort(url_), |
| stats); |
| test_params_.quic_estimate_initial_rtt = true; |
| |
| Initialize(); |
| ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
| crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
| |
| MockQuicData socket_data; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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_P(QuicStreamFactoryTest, 2gInitialRtt) { |
| ScopedMockNetworkChangeNotifier notifier; |
| notifier.mock_network_change_notifier()->SetConnectionType( |
| NetworkChangeNotifier::CONNECTION_2G); |
| test_params_.quic_estimate_initial_rtt = true; |
| |
| Initialize(); |
| ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
| crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
| |
| MockQuicData socket_data; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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_estimate_initial_rtt = true; |
| |
| Initialize(); |
| ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
| crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
| |
| MockQuicData socket_data; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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()); |
| } |
| |
| TEST_P(QuicStreamFactoryTest, Pooling) { |
| Initialize(); |
| ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
| crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
| |
| MockQuicData socket_data; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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(), |
| /*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( |
| quic::QuicSocketAddress(quic::QuicSocketAddressImpl(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); |
| |
| // 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", ""); |
| |
| MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; |
| std::unique_ptr<quic::QuicEncryptedPacket> settings_packet( |
| client_maker_.MakeInitialSettingsPacket(1, nullptr)); |
| MockWrite writes[] = {MockWrite(SYNCHRONOUS, settings_packet->data(), |
| settings_packet->length(), 1)}; |
| |
| SequencedSocketData socket_data(reads, writes); |
| socket_factory_->AddSocketDataProvider(&socket_data); |
| |
| 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(), |
| /*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_data.AllReadDataConsumed()); |
| EXPECT_TRUE(socket_data.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; |
| socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| socket_data1.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket()); |
| socket_data1.AddSocketDataToFactory(socket_factory_.get()); |
| MockQuicData socket_data2; |
| socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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(), |
| /*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(), |
| /*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; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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(), |
| /*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; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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(), |
| /*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; |
| socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| socket_data1.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket()); |
| socket_data1.AddSocketDataToFactory(socket_factory_.get()); |
| MockQuicData socket_data2; |
| socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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; |
| |
| #if defined(STARBOARD) |
| // Cobalt disables static transport security state, we have to add pins |
| // dynamically. |
| test::AddPin(&transport_security_state_, kServer2HostName, 1, 2); |
| #endif |
| |
| 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(), |
| /*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(), |
| /*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; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket()); |
| socket_data.AddSocketDataToFactory(socket_factory_.get()); |
| MockQuicData socket_data2; |
| socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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(), |
| /*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; |
| socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket()); |
| if (version_ == quic::QUIC_VERSION_99) { |
| socket_data.AddWrite( |
| SYNCHRONOUS, |
| client_maker_.MakeStreamIdBlockedPacket( |
| 2, true, GetNthClientInitiatedBidirectionalStreamId(49))); |
| 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_.MakeMaxStreamIdPacket( |
| 4, true, GetNthClientInitiatedBidirectionalStreamId(50))); |
| } else { |
| socket_data.AddWrite( |
| SYNCHRONOUS, client_maker_.MakeRstPacket(2, 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(), |
| /*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(), |
| /*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; |
| 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(), |
| /*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; |
| 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(), |
| /*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; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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(), |
| /*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; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket()); |
| socket_data.AddWrite( |
| SYNCHRONOUS, ConstructClientRstPacket(2, quic::QUIC_RST_ACKNOWLEDGEMENT)); |
| socket_data.AddWrite(SYNCHRONOUS, |
| client_maker_.MakeConnectionClosePacket( |
| 3, true, quic::QUIC_INTERNAL_ERROR, "net error")); |
| socket_data.AddSocketDataToFactory(socket_factory_.get()); |
| |
| MockQuicData socket_data2; |
| socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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_INTERNAL_ERROR); |
| 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(), |
| /*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; |
| 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(), |
| /*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); |
| MockQuicData socket_data2; |
| socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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; |
| 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(), |
| /*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); |
| MockQuicData socket_data2; |
| socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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_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; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket()); |
| socket_data.AddWrite( |
| SYNCHRONOUS, ConstructClientRstPacket(2, quic::QUIC_RST_ACKNOWLEDGEMENT)); |
| socket_data.AddWrite( |
| SYNCHRONOUS, client_maker_.MakeConnectionClosePacket( |
| 3, true, quic::QUIC_IP_ADDRESS_CHANGED, "net error")); |
| socket_data.AddSocketDataToFactory(socket_factory_.get()); |
| |
| MockQuicData socket_data2; |
| socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| 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(), |
| /*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 exisits for the destination. |
| EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
| QuicChromiumClientSession* session = GetActiveSession(host_port_pair_); |
| EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| |
| IPAddress last_address; |
| EXPECT_TRUE(http_server_properties_.GetSupportsQuic(&last_address)); |
| // 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_TRUE(factory_->require_confirmation()); |
| EXPECT_FALSE(http_server_properties_.GetSupportsQuic(&last_address)); |
| // 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(), |
| /*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_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; |
| quic::QuicStreamOffset header_stream_offset = 0; |
| quic_data1.AddWrite(SYNCHRONOUS, |
| ConstructInitialSettingsPacket(1, &header_stream_offset)); |
| quic_data1.AddWrite( |
| SYNCHRONOUS, ConstructGetRequestPacket( |
| 2, GetNthClientInitiatedBidirectionalStreamId(0), true, |
| true, &header_stream_offset)); |
| 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()); |
| |
| MockQuicData quic_data2; |
| quic::QuicStreamOffset header_stream_offset2 = 0; |
| quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read. |
| quic_data2.AddWrite( |
| SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset2)); |
| 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(), |
| /*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(), |
| /*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; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket()); |
| socket_data.AddWrite( |
| SYNCHRONOUS, ConstructClientRstPacket(2, 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(), |
| /*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())); |
| |
| IPAddress last_address; |
| EXPECT_TRUE(http_server_properties_.GetSupportsQuic(&last_address)); |
| |
| // Change the IP address and verify that the connection is unaffected. |
| NotifyIPAddressChanged(); |
| EXPECT_FALSE(factory_->require_confirmation()); |
| EXPECT_TRUE(http_server_properties_.GetSupportsQuic(&last_address)); |
| |
| // 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(), |
| /*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; |
| quic::QuicStreamOffset header_stream_offset = 0; |
| quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging Read. |
| quic_data1.AddWrite(SYNCHRONOUS, |
| ConstructInitialSettingsPacket(1, &header_stream_offset)); |
| quic_data1.AddWrite( |
| write_mode, ConstructGetRequestPacket( |
| 2, GetNthClientInitiatedBidirectionalStreamId(0), true, |
| true, &header_stream_offset)); |
| quic_data1.AddSocketDataToFactory(socket_factory_.get()); |
| |
| // Set up the second socket data provider that is used after migration. |
| // The response to the earlier request is read on the new socket. |
| MockQuicData quic_data2; |
| // Connectivity probe to be sent on the new path. |
| quic_data2.AddWrite(SYNCHRONOUS, |
| client_maker_.MakeConnectivityProbingPacket(3, true)); |
| quic_data2.AddRead(ASYNC, ERR_IO_PENDING); // Pause |
| // Connectivity probe to receive from the server. |
| quic_data2.AddRead(ASYNC, |
| server_maker_.MakeConnectivityProbingPacket(1, false)); |
| // Ping packet to send after migration is completed. |
| quic_data2.AddWrite(ASYNC, |
| client_maker_.MakeAckAndPingPacket(4, false, 1, 1, 1)); |
| quic_data2.AddRead( |
| ASYNC, |
| ConstructOkResponsePacket( |
| 2, GetNthClientInitiatedBidirectionalStreamId(0), false, false)); |
| quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| quic_data2.AddWrite( |
| SYNCHRONOUS, client_maker_.MakeAckAndRstPacket( |
| 5, false, GetNthClientInitiatedBidirectionalStreamId(0), |
| quic::QUIC_STREAM_CANCELLED, 2, 2, 1, true)); |
| 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(), |
| /*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())); |
| |
| // Deliver a signal that a alternate network is connected now, this should |
| // cause the connection to start early migration on path degrading. |
| scoped_mock_network_change_notifier_->mock_network_change_notifier() |
| ->SetConnectedNetworksList( |
| {kDefaultNetworkForTests, kNewNetworkForTests}); |
| scoped_mock_network_change_notifier_->mock_network_change_notifier() |
| ->NotifyNetworkConnected(kNewNetworkForTests); |
| |
| // Cause the connection to report path degrading to the session. |
| // Due to lack of alternate network, session will not mgirate connection. |
| EXPECT_EQ(0u, task_runner->GetPendingTaskCount()); |
| scoped_mock_network_change_notifier_->mock_network_change_notifier() |
| ->NotifyNetworkMadeDefault(kNewNetworkForTests); |
| |
| // A task will be posted to migrate to the new default network. |
| EXPECT_EQ(1u, task_runner->GetPendingTaskCount()); |
| EXPECT_EQ(base::TimeDelta(), task_runner->NextPendingTaskDelay()); |
| |
| // Execute the posted task to migrate back to the default network. |
| task_runner->RunUntilIdle(); |
| // Another task to try send a new connectivity probe is posted. And a task to |
| // retry migrate back to default network is scheduled. |
| EXPECT_EQ(2u, task_runner->GetPendingTaskCount()); |
| // Next connectivity probe is scheduled to be sent in 2 * |
| // kDefaultRTTMilliSecs. |
| base::TimeDelta next_task_delay = task_runner->NextPendingTaskDelay(); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs), |
| next_task_delay); |
| |
| // The connection should still be alive, and not marked as going away. |
| EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
| EXPECT_EQ(1u, session->GetNumActiveStreams()); |
| EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback())); |
| |
| // Resume quic data and a connectivity probe response will be read on the new |
| // socket, declare probing as successful. And a new task to WriteToNewSocket |
| // will be posted to complete migration. |
| quic_data2.Resume(); |
| |
| EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
| EXPECT_EQ(1u, session->GetNumActiveStreams()); |
| |
| // There should be three pending tasks, the nearest one will complete |
| // migration to the new network. |
| EXPECT_EQ(3u, task_runner->GetPendingTaskCount()); |
| next_task_delay = task_runner->NextPendingTaskDelay(); |
| EXPECT_EQ(base::TimeDelta(), next_task_delay); |
| task_runner->FastForwardBy(next_task_delay); |
| |
| // Response headers are received over the new network. |
| EXPECT_THAT(callback_.WaitForResult(), IsOk()); |
| EXPECT_EQ(200, response.headers->response_code()); |
| |
| // Now there are two pending tasks, the nearest one was to send connectivity |
| // probe and has been cancelled due to successful migration. |
| EXPECT_EQ(2u, task_runner->GetPendingTaskCount()); |
| next_task_delay = task_runner->NextPendingTaskDelay(); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs), |
| next_task_delay); |
| task_runner->FastForwardBy(next_task_delay); |
| |
| // There's one more task to mgirate back to the default network in 0.4s, which |
| // is also cancelled due to the success migration on the previous trial. |
| EXPECT_EQ(1u, task_runner->GetPendingTaskCount()); |
| next_task_delay = task_runner->NextPendingTaskDelay(); |
| base::TimeDelta expected_delay = |
| base::TimeDelta::FromSeconds(kMinRetryTimeForDefaultNetworkSecs) - |
| base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs); |
| EXPECT_EQ(expected_delay, next_task_delay); |
| task_runner->FastForwardBy(next_task_delay); |
| EXPECT_EQ(0u, task_runner->GetPendingTaskCount()); |
| |
| // Verify that the session is still alive. |
| EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
| |
| stream.reset(); |
| EXPECT_TRUE(quic_data1.AllReadDataConsumed()); |
| EXPECT_TRUE(quic_data1.AllWriteDataConsumed()); |
| EXPECT_TRUE(quic_data2.AllReadDataConsumed()); |
| EXPECT_TRUE(quic_data2.AllWriteDataConsumed()); |
| } |
| |
| // Regression test for http://859674. |
| // This test veries that a writer will not attempt to write packets until being |
| // unblocked on both socket level and network level. In this test, a probing |
| // writer is used to send two connectivity probes to the peer: where the first |
| // one completes successfully, while a connectivity response is received before |
| // completes sending the second one. The connection migration attempt will |
| // proceed while the probing writer is blocked at the socket level, which will |
| // block the writer on the network level. Once connection migration completes |
| // successfully, the probing writer will be unblocked on the network level, it |
| // will not attempt to write new packets until the socket level is unblocked. |
| TEST_P(QuicStreamFactoryTest, MigratedToBlockedSocketAfterProbing) { |
| 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; |
| quic::QuicStreamOffset header_stream_offset = 0; |
| quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging Read. |
| quic_data1.AddWrite(SYNCHRONOUS, |
| ConstructInitialSettingsPacket(1, &header_stream_offset)); |
| quic_data1.AddWrite( |
| SYNCHRONOUS, ConstructGetRequestPacket( |
| 2, GetNthClientInitiatedBidirectionalStreamId(0), true, |
| true, &header_stream_offset)); |
| quic_data1.AddSocketDataToFactory(socket_factory_.get()); |
| |
| // Set up the second socket data provider that is used after migration. |
| // The response to the earlier request is read on the new socket. |
| MockQuicData quic_data2; |
| // First connectivity probe to be sent on the new path. |
| quic_data2.AddWrite(SYNCHRONOUS, |
| client_maker_.MakeConnectivityProbingPacket(3, true)); |
| quic_data2.AddRead(ASYNC, |
| ERR_IO_PENDING); // Pause so that we can control time. |
| // Connectivity probe to receive from the server. |
| quic_data2.AddRead(ASYNC, |
| server_maker_.MakeConnectivityProbingPacket(1, false)); |
| // Second connectivity probe which will complete asynchronously. |
| quic_data2.AddWrite(ASYNC, |
| client_maker_.MakeConnectivityProbingPacket(4, true)); |
| quic_data2.AddRead( |
| ASYNC, |
| ConstructOkResponsePacket( |
| 2, GetNthClientInitiatedBidirectionalStreamId(0), false, false)); |
| quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| quic_data2.AddWrite(ASYNC, |
| client_maker_.MakeAckAndPingPacket(5, false, 1, 1, 1)); |
| quic_data2.AddWrite( |
| SYNCHRONOUS, client_maker_.MakeAckAndRstPacket( |
| 6, false, GetNthClientInitiatedBidirectionalStreamId(0), |
| quic::QUIC_STREAM_CANCELLED, 2, 2, 1, true)); |
| |
| 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(), |
| /*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())); |
| |
| // Deliver a signal that a alternate network is connected now, this should |
| // cause the connection to start early migration on path degrading. |
| scoped_mock_network_change_notifier_->mock_network_change_notifier() |
| ->SetConnectedNetworksList( |
| {kDefaultNetworkForTests, kNewNetworkForTests}); |
| scoped_mock_network_change_notifier_->mock_network_change_notifier() |
| ->NotifyNetworkConnected(kNewNetworkForTests); |
| |
| // Cause the connection to report path degrading to the session. |
| // Due to lack of alternate network, session will not mgirate connection. |
| EXPECT_EQ(0u, task_runner->GetPendingTaskCount()); |
| scoped_mock_network_change_notifier_->mock_network_change_notifier() |
| ->NotifyNetworkMadeDefault(kNewNetworkForTests); |
| |
| // A task will be posted to migrate to the new default network. |
| EXPECT_EQ(1u, task_runner->GetPendingTaskCount()); |
| EXPECT_EQ(base::TimeDelta(), task_runner->NextPendingTaskDelay()); |
| |
| // Execute the posted task to migrate back to the default network. |
| task_runner->RunUntilIdle(); |
| // Another task to resend a new connectivity probe is posted. And a task to |
| // retry migrate back to default network is scheduled. |
| EXPECT_EQ(2u, task_runner->GetPendingTaskCount()); |
| // Next connectivity probe is scheduled to be sent in 2 * |
| // kDefaultRTTMilliSecs. |
| base::TimeDelta next_task_delay = task_runner->NextPendingTaskDelay(); |
| base::TimeDelta expected_delay = |
| base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs); |
| EXPECT_EQ(expected_delay, next_task_delay); |
| |
| // Fast forward to send the second connectivity probe. The write will be |
| // asynchronous and complete after the read completes. |
| task_runner->FastForwardBy(next_task_delay); |
| |
| // Resume quic data and a connectivity probe response will be read on the new |
| // socket, declare probing as successful. |
| quic_data2.Resume(); |
| |
| // The connection should still be alive, and not marked as going away. |
| EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
| EXPECT_EQ(1u, session->GetNumActiveStreams()); |
| EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback())); |
| |
| // There should be three pending tasks, the nearest one will complete |
| // migration to the new network. Second task will retry migrate back to |
| // default but cancelled, and the third task will retry send connectivity |
| // probe but also cancelled. |
| EXPECT_EQ(3u, task_runner->GetPendingTaskCount()); |
| EXPECT_EQ(base::TimeDelta(), task_runner->NextPendingTaskDelay()); |
| task_runner->RunUntilIdle(); |
| |
| // Response headers are received over the new network. |
| EXPECT_THAT(callback_.WaitForResult(), IsOk()); |
| EXPECT_EQ(200, response.headers->response_code()); |
| |
| // Run the message loop to complete the asynchronous write of ack and ping. |
| base::RunLoop().RunUntilIdle(); |
| |
| // Now there are two pending tasks, the nearest one was to retry migrate back |
| // to default network and has been cancelled due to successful migration. |
| EXPECT_EQ(2u, task_runner->GetPendingTaskCount()); |
| expected_delay = |
| base::TimeDelta::FromSeconds(kMinRetryTimeForDefaultNetworkSecs) - |
| expected_delay; |
| next_task_delay = task_runner->NextPendingTaskDelay(); |
| EXPECT_EQ(expected_delay, next_task_delay); |
| task_runner->FastForwardBy(next_task_delay); |
| |
| // There's one more task to retry sending connectivity probe in 0.4s and has |
| // also been cancelled due to the successful probing. |
| EXPECT_EQ(1u, task_runner->GetPendingTaskCount()); |
| next_task_delay = task_runner->NextPendingTaskDelay(); |
| expected_delay = |
| base::TimeDelta::FromMilliseconds(3 * 2 * kDefaultRTTMilliSecs) - |
| base::TimeDelta::FromSeconds(kMinRetryTimeForDefaultNetworkSecs); |
| EXPECT_EQ(expected_delay, next_task_delay); |
| task_runner->FastForwardBy(next_task_delay); |
| EXPECT_EQ(0u, task_runner->GetPendingTaskCount()); |
| |
| // Verify that the session is still alive. |
| EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
| |
| stream.reset(); |
| EXPECT_TRUE(quic_data1.AllReadDataConsumed()); |
| EXPECT_TRUE(quic_data1.AllWriteDataConsumed()); |
| EXPECT_TRUE(quic_data2.AllReadDataConsumed()); |
| EXPECT_TRUE(quic_data2.AllWriteDataConsumed()); |
| } |
| |
| // This test verifies that session times out connection migration attempt |
| // with signals delivered in the following order (no alternate network is |
| // available): |
| // - default network disconnected is delivered: session attempts connection |
| // migration but found not alternate network. Session waits for a new network |
| // comes up in the next kWaitTimeForNewNetworkSecs seconds. |
| // - no new network is connected, migration times out. Session is closed. |
| TEST_P(QuicStreamFactoryTest, MigrationTimeoutWithNoNewNetwork) { |
| InitializeConnectionMigrationV2Test({kDefaultNetworkForTests}); |
| ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
| 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()); |
| |
| MockQuicData socket_data; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket()); |
| socket_data.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(), |
| /*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.traffic_annotation = |
| MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); |
| EXPECT_EQ(OK, stream->InitializeStream(&request_info, false, 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_)); |
| |
| // Trigger connection migration. Since there are no networks |
| // to migrate to, this should cause the session to wait for a new network. |
| scoped_mock_network_change_notifier_->mock_network_change_notifier() |
| ->NotifyNetworkDisconnected(kDefaultNetworkForTests); |
| |
| // The migration will not fail until the migration alarm timeout. |
| EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
| EXPECT_EQ(1u, session->GetNumActiveStreams()); |
| EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback())); |
| EXPECT_EQ(true, session->connection()->writer()->IsWriteBlocked()); |
| |
| // Migration will be timed out after kWaitTimeForNewNetwokSecs. |
| EXPECT_EQ(1u, task_runner->GetPendingTaskCount()); |
| base::TimeDelta next_task_delay = task_runner->NextPendingTaskDelay(); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(kWaitTimeForNewNetworkSecs), |
| next_task_delay); |
| task_runner->FastForwardBy(next_task_delay); |
| |
| // The connection should now be closed. A request for response |
| // headers should fail. |
| EXPECT_FALSE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| EXPECT_FALSE(HasActiveSession(host_port_pair_)); |
| EXPECT_EQ(ERR_NETWORK_CHANGED, callback_.WaitForResult()); |
| |
| EXPECT_TRUE(socket_data.AllReadDataConsumed()); |
| EXPECT_TRUE(socket_data.AllWriteDataConsumed()); |
| } |
| |
| // This test verifies that connectivity probes will be sent even if there is |
| // a non-migratable stream. However, when connection migrates to the |
| // successfully probed path, any non-migratable streams will be reset. |
| TEST_P(QuicStreamFactoryTest, |
| OnNetworkMadeDefaultNonMigratableStream_MigrateIdleSessions) { |
| TestOnNetworkMadeDefaultNonMigratableStream(true); |
| } |
| |
| // This test verifies that connectivity probes will be sent even if there is |
| // a non-migratable stream. However, when connection migrates to the |
| // successfully probed path, any non-migratable stream will be reset. And if |
| // the connection becomes idle then, close the connection. |
| TEST_P(QuicStreamFactoryTest, |
| OnNetworkMadeDefaultNonMigratableStream_DoNotMigrateIdleSessions) { |
| TestOnNetworkMadeDefaultNonMigratableStream(false); |
| } |
| |
| void QuicStreamFactoryTestBase::TestOnNetworkMadeDefaultNonMigratableStream( |
| bool migrate_idle_sessions) { |
| test_params_.quic_migrate_idle_sessions = migrate_idle_sessions; |
| InitializeConnectionMigrationV2Test( |
| {kDefaultNetworkForTests, kNewNetworkForTests}); |
| ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
| crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
| |
| MockQuicData socket_data; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket()); |
| if (!migrate_idle_sessions) { |
| socket_data.AddWrite( |
| SYNCHRONOUS, |
| client_maker_.MakeRstAckAndConnectionClosePacket( |
| 3, false, GetNthClientInitiatedBidirectionalStreamId(0), |
| quic::QUIC_STREAM_CANCELLED, |
| quic::QuicTime::Delta::FromMilliseconds(0), 1, 1, 1, |
| quic::QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS, |
| "net error")); |
| } |
| |
| socket_data.AddSocketDataToFactory(socket_factory_.get()); |
| |
| // Set up the second socket data provider that is used for probing. |
| MockQuicData quic_data1; |
| // Connectivity probe to be sent on the new path. |
| quic_data1.AddWrite(SYNCHRONOUS, |
| client_maker_.MakeConnectivityProbingPacket(2, true)); |
| quic_data1.AddRead(ASYNC, ERR_IO_PENDING); // Pause |
| // Connectivity probe to receive from the server. |
| quic_data1.AddRead(ASYNC, |
| server_maker_.MakeConnectivityProbingPacket(1, false)); |
| if (migrate_idle_sessions) { |
| quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read. |
| // A RESET will be sent to the peer to cancel the non-migratable stream. |
| quic_data1.AddWrite( |
| SYNCHRONOUS, |
| client_maker_.MakeRstPacket( |
| 3, false, GetNthClientInitiatedBidirectionalStreamId(0), |
| quic::QUIC_STREAM_CANCELLED)); |
| // Ping packet to send after migration is completed. |
| quic_data1.AddWrite(SYNCHRONOUS, |
| client_maker_.MakeAckAndPingPacket(4, false, 1, 1, 1)); |
| } |
| quic_data1.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(), |
| /*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, but marked as non-migratable. |
| HttpRequestInfo request_info; |
| request_info.load_flags |= LOAD_DISABLE_CONNECTION_MIGRATION_TO_CELLULAR; |
| request_info.traffic_annotation = |
| MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); |
| EXPECT_EQ(OK, stream->InitializeStream(&request_info, false, 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_)); |
| |
| // Trigger connection migration. Session will start to probe the alternative |
| // network. Although there is a non-migratable stream, session will still be |
| // active until probing is declared as successful. |
| scoped_mock_network_change_notifier_->mock_network_change_notifier() |
| ->NotifyNetworkMadeDefault(kNewNetworkForTests); |
| |
| EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
| EXPECT_EQ(1u, session->GetNumActiveStreams()); |
| |
| // Resume data to read a connectivity probing response, which will cause |
| // non-migtable streams to be closed. |
| quic_data1.Resume(); |
| EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| EXPECT_EQ(migrate_idle_sessions, HasActiveSession(host_port_pair_)); |
| EXPECT_EQ(0u, session->GetNumActiveStreams()); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_TRUE(quic_data1.AllReadDataConsumed()); |
| EXPECT_TRUE(quic_data1.AllWriteDataConsumed()); |
| EXPECT_TRUE(socket_data.AllReadDataConsumed()); |
| EXPECT_TRUE(socket_data.AllWriteDataConsumed()); |
| } |
| |
| TEST_P(QuicStreamFactoryTest, OnNetworkMadeDefaultConnectionMigrationDisabled) { |
| InitializeConnectionMigrationV2Test( |
| {kDefaultNetworkForTests, kNewNetworkForTests}); |
| ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
| crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
| |
| MockQuicData socket_data; |
| socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
| socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket()); |
| socket_data.AddWrite( |
| SYNCHRONOUS, client_maker_.MakeRstPacket( |
| 2, true, GetNthClientInitiatedBidirectionalStreamId(0), |
| quic::QUIC_STREAM_CANCELLED)); |
| socket_data.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(), |
| /*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.traffic_annotation = |
| MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); |
| EXPECT_EQ(OK, stream->InitializeStream(&request_info, false, DEFAULT_PRIORITY, |
| net_log_, CompletionOnceCallback())); |
| |
|