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