// 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/http/http_stream_factory.h" #include #include #include #include #include #include #include #include "base/compiler_specific.h" #include "base/containers/contains.h" #include "base/memory/ptr_util.h" #include "base/no_destructor.h" #include "base/run_loop.h" #include "base/strings/strcat.h" #include "base/strings/string_piece.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "net/base/completion_once_callback.h" #include "net/base/features.h" #include "net/base/net_errors.h" #include "net/base/network_isolation_key.h" #include "net/base/port_util.h" #include "net/base/privacy_mode.h" #include "net/base/proxy_server.h" #include "net/base/schemeful_site.h" #include "net/base/test_completion_callback.h" #include "net/base/test_proxy_delegate.h" #include "net/cert/ct_policy_enforcer.h" #include "net/cert/mock_cert_verifier.h" #include "net/cert/multi_log_ct_verifier.h" #include "net/dns/mock_host_resolver.h" #include "net/dns/public/secure_dns_policy.h" #include "net/http/bidirectional_stream_impl.h" #include "net/http/bidirectional_stream_request_info.h" #include "net/http/http_auth_handler_factory.h" #include "net/http/http_network_session.h" #include "net/http/http_network_session_peer.h" #include "net/http/http_network_transaction.h" #include "net/http/http_proxy_connect_job.h" #include "net/http/http_request_info.h" #include "net/http/http_server_properties.h" #include "net/http/http_stream.h" #include "net/http/transport_security_state.h" #include "net/log/net_log_with_source.h" #include "net/proxy_resolution/configured_proxy_resolution_service.h" #include "net/proxy_resolution/proxy_info.h" #include "net/quic/mock_crypto_client_stream_factory.h" #include "net/quic/mock_quic_context.h" #include "net/quic/quic_http_utils.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/socket/client_socket_handle.h" #include "net/socket/client_socket_pool.h" #include "net/socket/connect_job.h" #include "net/socket/mock_client_socket_pool_manager.h" #include "net/socket/next_proto.h" #include "net/socket/socket_tag.h" #include "net/socket/socket_test_util.h" #include "net/socket/socks_connect_job.h" #include "net/socket/ssl_connect_job.h" #include "net/socket/transport_connect_job.h" #include "net/spdy/spdy_session.h" #include "net/spdy/spdy_session_pool.h" #include "net/spdy/spdy_test_util_common.h" #include "net/ssl/ssl_config_service.h" #include "net/ssl/ssl_config_service_defaults.h" #include "net/test/cert_test_util.h" #include "net/test/gtest_util.h" #include "net/test/test_data_directory.h" #include "net/test/test_with_task_environment.h" #include "net/third_party/quiche/src/quiche/quic/core/quic_server_id.h" #include "net/third_party/quiche/src/quiche/quic/core/quic_utils.h" #include "net/third_party/quiche/src/quiche/quic/test_tools/crypto_test_utils.h" #include "net/third_party/quiche/src/quiche/quic/test_tools/mock_random.h" #include "net/third_party/quiche/src/quiche/quic/test_tools/quic_test_utils.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" // This file can be included from net/http even though // it is in net/websockets because it doesn't // introduce any link dependency to net/websockets. #include "net/websockets/websocket_handshake_stream_base.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "url/gurl.h" #include "url/scheme_host_port.h" #include "url/url_constants.h" using ::testing::Contains; using ::testing::ElementsAre; using ::testing::IsEmpty; using ::testing::Key; using ::testing::SizeIs; using net::test::IsError; using net::test::IsOk; namespace base { class Value; } // namespace base namespace net { class BidirectionalStreamImpl; class WebSocketEndpointLockManager; namespace { class MockWebSocketHandshakeStream : public WebSocketHandshakeStreamBase { public: enum StreamType { kStreamTypeBasic, kStreamTypeSpdy, }; explicit MockWebSocketHandshakeStream(StreamType type) : type_(type) {} ~MockWebSocketHandshakeStream() override = default; StreamType type() const { return type_; } // HttpStream methods void RegisterRequest(const HttpRequestInfo* request_info) override {} int InitializeStream(bool can_send_early, RequestPriority priority, const NetLogWithSource& net_log, CompletionOnceCallback callback) override { return ERR_IO_PENDING; } int SendRequest(const HttpRequestHeaders& request_headers, HttpResponseInfo* response, CompletionOnceCallback callback) override { return ERR_IO_PENDING; } int ReadResponseHeaders(CompletionOnceCallback callback) override { return ERR_IO_PENDING; } int ReadResponseBody(IOBuffer* buf, int buf_len, CompletionOnceCallback callback) override { return ERR_IO_PENDING; } void Close(bool not_reusable) override {} bool IsResponseBodyComplete() const override { return false; } bool IsConnectionReused() const override { return false; } void SetConnectionReused() override {} bool CanReuseConnection() const override { return false; } int64_t GetTotalReceivedBytes() const override { return 0; } int64_t GetTotalSentBytes() const override { return 0; } bool GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const override { return false; } bool GetAlternativeService( AlternativeService* alternative_service) const override { return false; } void GetSSLInfo(SSLInfo* ssl_info) override {} void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) override {} int GetRemoteEndpoint(IPEndPoint* endpoint) override { return ERR_UNEXPECTED; } void Drain(HttpNetworkSession* session) override {} void PopulateNetErrorDetails(NetErrorDetails* details) override { return; } void SetPriority(RequestPriority priority) override {} std::unique_ptr RenewStreamForAuth() override { return nullptr; } const std::set& GetDnsAliases() const override { static const base::NoDestructor> nullset_result; return *nullset_result; } base::StringPiece GetAcceptChViaAlps() const override { return {}; } std::unique_ptr Upgrade() override { return nullptr; } base::WeakPtr GetWeakPtr() override { return weak_ptr_factory_.GetWeakPtr(); } private: const StreamType type_; base::WeakPtrFactory weak_ptr_factory_{this}; }; // HttpStreamFactory subclass that can wait until a preconnect is complete. class MockHttpStreamFactoryForPreconnect : public HttpStreamFactory { public: explicit MockHttpStreamFactoryForPreconnect(HttpNetworkSession* session) : HttpStreamFactory(session) {} ~MockHttpStreamFactoryForPreconnect() override = default; void WaitForPreconnects() { while (!preconnect_done_) { waiting_for_preconnect_ = true; loop_.Run(); waiting_for_preconnect_ = false; } } private: // HttpStreamFactory methods. void OnPreconnectsCompleteInternal() override { preconnect_done_ = true; if (waiting_for_preconnect_) loop_.QuitWhenIdle(); } bool preconnect_done_ = false; bool waiting_for_preconnect_ = false; base::RunLoop loop_; }; class StreamRequestWaiter : public HttpStreamRequest::Delegate { public: StreamRequestWaiter() = default; StreamRequestWaiter(const StreamRequestWaiter&) = delete; StreamRequestWaiter& operator=(const StreamRequestWaiter&) = delete; // HttpStreamRequest::Delegate void OnStreamReady(const SSLConfig& used_ssl_config, const ProxyInfo& used_proxy_info, std::unique_ptr stream) override { stream_done_ = true; if (loop_) loop_->Quit(); stream_ = std::move(stream); used_ssl_config_ = used_ssl_config; used_proxy_info_ = used_proxy_info; } void OnWebSocketHandshakeStreamReady( const SSLConfig& used_ssl_config, const ProxyInfo& used_proxy_info, std::unique_ptr stream) override { stream_done_ = true; if (loop_) loop_->Quit(); websocket_stream_ = std::move(stream); used_ssl_config_ = used_ssl_config; used_proxy_info_ = used_proxy_info; } void OnBidirectionalStreamImplReady( const SSLConfig& used_ssl_config, const ProxyInfo& used_proxy_info, std::unique_ptr stream) override { stream_done_ = true; if (loop_) loop_->Quit(); bidirectional_stream_impl_ = std::move(stream); used_ssl_config_ = used_ssl_config; used_proxy_info_ = used_proxy_info; } void OnStreamFailed(int status, const NetErrorDetails& net_error_details, const SSLConfig& used_ssl_config, const ProxyInfo& used_proxy_info, ResolveErrorInfo resolve_error_info) override { stream_done_ = true; if (loop_) loop_->Quit(); used_ssl_config_ = used_ssl_config; error_status_ = status; } void OnCertificateError(int status, const SSLConfig& used_ssl_config, const SSLInfo& ssl_info) override {} void OnNeedsProxyAuth(const HttpResponseInfo& proxy_response, const SSLConfig& used_ssl_config, const ProxyInfo& used_proxy_info, HttpAuthController* auth_controller) override {} void OnNeedsClientAuth(const SSLConfig& used_ssl_config, SSLCertRequestInfo* cert_info) override {} void OnQuicBroken() override {} void WaitForStream() { stream_done_ = false; loop_ = std::make_unique(); while (!stream_done_) loop_->Run(); loop_.reset(); } const SSLConfig& used_ssl_config() const { return used_ssl_config_; } const ProxyInfo& used_proxy_info() const { return used_proxy_info_; } HttpStream* stream() { return stream_.get(); } MockWebSocketHandshakeStream* websocket_stream() { return static_cast(websocket_stream_.get()); } BidirectionalStreamImpl* bidirectional_stream_impl() { return bidirectional_stream_impl_.get(); } bool stream_done() const { return stream_done_; } int error_status() const { return error_status_; } protected: bool stream_done_ = false; std::unique_ptr loop_; std::unique_ptr stream_; std::unique_ptr websocket_stream_; std::unique_ptr bidirectional_stream_impl_; SSLConfig used_ssl_config_; ProxyInfo used_proxy_info_; int error_status_ = OK; }; class WebSocketBasicHandshakeStream : public MockWebSocketHandshakeStream { public: explicit WebSocketBasicHandshakeStream( std::unique_ptr connection) : MockWebSocketHandshakeStream(kStreamTypeBasic), connection_(std::move(connection)) {} ~WebSocketBasicHandshakeStream() override { connection_->socket()->Disconnect(); } ClientSocketHandle* connection() { return connection_.get(); } private: std::unique_ptr connection_; }; class WebSocketStreamCreateHelper : public WebSocketHandshakeStreamBase::CreateHelper { public: ~WebSocketStreamCreateHelper() override = default; std::unique_ptr CreateBasicStream( std::unique_ptr connection, bool using_proxy, WebSocketEndpointLockManager* websocket_endpoint_lock_manager) override { return std::make_unique( std::move(connection)); } std::unique_ptr CreateHttp2Stream( base::WeakPtr session, std::set dns_aliases) override { NOTREACHED(); return nullptr; } }; struct TestCase { int num_streams; bool ssl; }; TestCase kTests[] = { {1, false}, {2, false}, {1, true}, {2, true}, }; void PreconnectHelperForURL(int num_streams, const GURL& url, NetworkIsolationKey network_isolation_key, SecureDnsPolicy secure_dns_policy, HttpNetworkSession* session) { HttpNetworkSessionPeer peer(session); auto mock_factory = std::make_unique(session); auto* mock_factory_ptr = mock_factory.get(); peer.SetHttpStreamFactory(std::move(mock_factory)); HttpRequestInfo request; request.method = "GET"; request.url = url; request.load_flags = 0; request.network_isolation_key = network_isolation_key; request.secure_dns_policy = secure_dns_policy; request.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); session->http_stream_factory()->PreconnectStreams(num_streams, request); mock_factory_ptr->WaitForPreconnects(); } void PreconnectHelper(const TestCase& test, HttpNetworkSession* session) { GURL url = test.ssl ? GURL("https://www.google.com") : GURL("http://www.google.com"); PreconnectHelperForURL(test.num_streams, url, NetworkIsolationKey(), SecureDnsPolicy::kAllow, session); } ClientSocketPool::GroupId GetGroupId(const TestCase& test) { if (test.ssl) { return ClientSocketPool::GroupId( url::SchemeHostPort(url::kHttpsScheme, "www.google.com", 443), PrivacyMode::PRIVACY_MODE_DISABLED, NetworkIsolationKey(), SecureDnsPolicy::kAllow); } return ClientSocketPool::GroupId( url::SchemeHostPort(url::kHttpScheme, "www.google.com", 80), PrivacyMode::PRIVACY_MODE_DISABLED, NetworkIsolationKey(), SecureDnsPolicy::kAllow); } class CapturePreconnectsTransportSocketPool : public TransportClientSocketPool { public: explicit CapturePreconnectsTransportSocketPool( const CommonConnectJobParams* common_connect_job_params) : TransportClientSocketPool(0, 0, base::TimeDelta(), ProxyServer::Direct(), false /* is_for_websockets */, common_connect_job_params) {} int last_num_streams() const { return last_num_streams_; } const ClientSocketPool::GroupId& last_group_id() const { return last_group_id_; } // Resets |last_num_streams_| and |last_group_id_| default values. void reset() { last_num_streams_ = -1; // Group ID that shouldn't match much. last_group_id_ = ClientSocketPool::GroupId( url::SchemeHostPort(url::kHttpsScheme, "unexpected.to.conflict.with.anything.test", 9999), PrivacyMode::PRIVACY_MODE_ENABLED, NetworkIsolationKey(), SecureDnsPolicy::kAllow); } int RequestSocket( const ClientSocketPool::GroupId& group_id, scoped_refptr socket_params, const absl::optional& proxy_annotation_tag, RequestPriority priority, const SocketTag& socket_tag, ClientSocketPool::RespectLimits respect_limits, ClientSocketHandle* handle, CompletionOnceCallback callback, const ClientSocketPool::ProxyAuthCallback& proxy_auth_callback, const NetLogWithSource& net_log) override { ADD_FAILURE(); return ERR_UNEXPECTED; } int RequestSockets( const ClientSocketPool::GroupId& group_id, scoped_refptr socket_params, const absl::optional& proxy_annotation_tag, int num_sockets, CompletionOnceCallback callback, const NetLogWithSource& net_log) override { last_num_streams_ = num_sockets; last_group_id_ = group_id; return OK; } void CancelRequest(const ClientSocketPool::GroupId& group_id, ClientSocketHandle* handle, bool cancel_connect_job) override { ADD_FAILURE(); } void ReleaseSocket(const ClientSocketPool::GroupId& group_id, std::unique_ptr socket, int64_t generation) override { ADD_FAILURE(); } void CloseIdleSockets(const char* net_log_reason_utf8) override { ADD_FAILURE(); } int IdleSocketCount() const override { ADD_FAILURE(); return 0; } size_t IdleSocketCountInGroup( const ClientSocketPool::GroupId& group_id) const override { ADD_FAILURE(); return 0; } LoadState GetLoadState(const ClientSocketPool::GroupId& group_id, const ClientSocketHandle* handle) const override { ADD_FAILURE(); return LOAD_STATE_IDLE; } private: int last_num_streams_ = -1; ClientSocketPool::GroupId last_group_id_; }; using HttpStreamFactoryTest = TestWithTaskEnvironment; TEST_F(HttpStreamFactoryTest, PreconnectDirect) { for (const auto& test : kTests) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); HttpNetworkSessionPeer peer(session.get()); CommonConnectJobParams common_connect_job_params = session->CreateCommonConnectJobParams(); std::unique_ptr owned_transport_conn_pool = std::make_unique( &common_connect_job_params); CapturePreconnectsTransportSocketPool* transport_conn_pool = owned_transport_conn_pool.get(); auto mock_pool_manager = std::make_unique(); mock_pool_manager->SetSocketPool(ProxyServer::Direct(), std::move(owned_transport_conn_pool)); peer.SetClientSocketPoolManager(std::move(mock_pool_manager)); PreconnectHelper(test, session.get()); EXPECT_EQ(test.num_streams, transport_conn_pool->last_num_streams()); EXPECT_EQ(GetGroupId(test), transport_conn_pool->last_group_id()); } } TEST_F(HttpStreamFactoryTest, PreconnectHttpProxy) { for (const auto& test : kTests) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateFixedForTest( "http_proxy", TRAFFIC_ANNOTATION_FOR_TESTS)); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); HttpNetworkSessionPeer peer(session.get()); ProxyServer proxy_server(ProxyServer::SCHEME_HTTP, HostPortPair("http_proxy", 80)); CommonConnectJobParams common_connect_job_params = session->CreateCommonConnectJobParams(); auto http_proxy_pool = std::make_unique( &common_connect_job_params); auto* http_proxy_pool_ptr = http_proxy_pool.get(); auto mock_pool_manager = std::make_unique(); mock_pool_manager->SetSocketPool(proxy_server, std::move(http_proxy_pool)); peer.SetClientSocketPoolManager(std::move(mock_pool_manager)); PreconnectHelper(test, session.get()); EXPECT_EQ(test.num_streams, http_proxy_pool_ptr->last_num_streams()); EXPECT_EQ(GetGroupId(test), http_proxy_pool_ptr->last_group_id()); } } TEST_F(HttpStreamFactoryTest, PreconnectSocksProxy) { for (const auto& test : kTests) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateFixedForTest( "socks4://socks_proxy:1080", TRAFFIC_ANNOTATION_FOR_TESTS)); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); HttpNetworkSessionPeer peer(session.get()); ProxyServer proxy_server(ProxyServer::SCHEME_SOCKS4, HostPortPair("socks_proxy", 1080)); CommonConnectJobParams common_connect_job_params = session->CreateCommonConnectJobParams(); auto socks_proxy_pool = std::make_unique( &common_connect_job_params); auto* socks_proxy_pool_ptr = socks_proxy_pool.get(); auto mock_pool_manager = std::make_unique(); mock_pool_manager->SetSocketPool(proxy_server, std::move(socks_proxy_pool)); peer.SetClientSocketPoolManager(std::move(mock_pool_manager)); PreconnectHelper(test, session.get()); EXPECT_EQ(test.num_streams, socks_proxy_pool_ptr->last_num_streams()); EXPECT_EQ(GetGroupId(test), socks_proxy_pool_ptr->last_group_id()); } } TEST_F(HttpStreamFactoryTest, PreconnectDirectWithExistingSpdySession) { for (const auto& test : kTests) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); HttpNetworkSessionPeer peer(session.get()); // Put a SpdySession in the pool. HostPortPair host_port_pair("www.google.com", 443); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), PRIVACY_MODE_DISABLED, SpdySessionKey::IsProxySession::kFalse, SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow); std::ignore = CreateFakeSpdySession(session->spdy_session_pool(), key); CommonConnectJobParams common_connect_job_params = session->CreateCommonConnectJobParams(); std::unique_ptr owned_transport_conn_pool = std::make_unique( &common_connect_job_params); CapturePreconnectsTransportSocketPool* transport_conn_pool = owned_transport_conn_pool.get(); auto mock_pool_manager = std::make_unique(); mock_pool_manager->SetSocketPool(ProxyServer::Direct(), std::move(owned_transport_conn_pool)); peer.SetClientSocketPoolManager(std::move(mock_pool_manager)); PreconnectHelper(test, session.get()); // We shouldn't be preconnecting if we have an existing session, which is // the case for https://www.google.com. if (test.ssl) EXPECT_EQ(-1, transport_conn_pool->last_num_streams()); else EXPECT_EQ(test.num_streams, transport_conn_pool->last_num_streams()); } } // Verify that preconnects to unsafe ports are cancelled before they reach // the SocketPool. TEST_F(HttpStreamFactoryTest, PreconnectUnsafePort) { ASSERT_FALSE(IsPortAllowedForScheme(7, "http")); SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); HttpNetworkSessionPeer peer(session.get()); CommonConnectJobParams common_connect_job_params = session->CreateCommonConnectJobParams(); std::unique_ptr owned_transport_conn_pool = std::make_unique( &common_connect_job_params); CapturePreconnectsTransportSocketPool* transport_conn_pool = owned_transport_conn_pool.get(); auto mock_pool_manager = std::make_unique(); mock_pool_manager->SetSocketPool(ProxyServer::Direct(), std::move(owned_transport_conn_pool)); peer.SetClientSocketPoolManager(std::move(mock_pool_manager)); PreconnectHelperForURL(1, GURL("http://www.google.com:7"), NetworkIsolationKey(), SecureDnsPolicy::kAllow, session.get()); EXPECT_EQ(-1, transport_conn_pool->last_num_streams()); } // Verify that preconnects use the specified NetworkIsolationKey. TEST_F(HttpStreamFactoryTest, PreconnectNetworkIsolationKey) { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature( features::kPartitionConnectionsByNetworkIsolationKey); SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); HttpNetworkSessionPeer peer(session.get()); CommonConnectJobParams common_connect_job_params = session->CreateCommonConnectJobParams(); std::unique_ptr owned_transport_conn_pool = std::make_unique( &common_connect_job_params); CapturePreconnectsTransportSocketPool* transport_conn_pool = owned_transport_conn_pool.get(); auto mock_pool_manager = std::make_unique(); mock_pool_manager->SetSocketPool(ProxyServer::Direct(), std::move(owned_transport_conn_pool)); peer.SetClientSocketPoolManager(std::move(mock_pool_manager)); const GURL kURL("http://foo.test/"); SchemefulSite kSiteFoo(GURL("http://foo.test")); SchemefulSite kSiteBar(GURL("http://bar.test")); const NetworkIsolationKey kKey1(kSiteFoo, kSiteFoo); const NetworkIsolationKey kKey2(kSiteBar, kSiteBar); PreconnectHelperForURL(1, kURL, kKey1, SecureDnsPolicy::kAllow, session.get()); EXPECT_EQ(1, transport_conn_pool->last_num_streams()); EXPECT_EQ(kKey1, transport_conn_pool->last_group_id().network_isolation_key()); PreconnectHelperForURL(2, kURL, kKey2, SecureDnsPolicy::kAllow, session.get()); EXPECT_EQ(2, transport_conn_pool->last_num_streams()); EXPECT_EQ(kKey2, transport_conn_pool->last_group_id().network_isolation_key()); } // Verify that preconnects use the specified Secure DNS Tag. TEST_F(HttpStreamFactoryTest, PreconnectDisableSecureDns) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); HttpNetworkSessionPeer peer(session.get()); CommonConnectJobParams common_connect_job_params = session->CreateCommonConnectJobParams(); std::unique_ptr owned_transport_conn_pool = std::make_unique( &common_connect_job_params); CapturePreconnectsTransportSocketPool* transport_conn_pool = owned_transport_conn_pool.get(); auto mock_pool_manager = std::make_unique(); mock_pool_manager->SetSocketPool(ProxyServer::Direct(), std::move(owned_transport_conn_pool)); peer.SetClientSocketPoolManager(std::move(mock_pool_manager)); const GURL kURL("http://foo.test/"); SchemefulSite kSiteFoo(GURL("http://foo.test")); SchemefulSite kSiteBar(GURL("http://bar.test")); PreconnectHelperForURL(1, kURL, NetworkIsolationKey(), SecureDnsPolicy::kAllow, session.get()); EXPECT_EQ(1, transport_conn_pool->last_num_streams()); EXPECT_EQ(SecureDnsPolicy::kAllow, transport_conn_pool->last_group_id().secure_dns_policy()); PreconnectHelperForURL(2, kURL, NetworkIsolationKey(), SecureDnsPolicy::kDisable, session.get()); EXPECT_EQ(2, transport_conn_pool->last_num_streams()); EXPECT_EQ(SecureDnsPolicy::kDisable, transport_conn_pool->last_group_id().secure_dns_policy()); } TEST_F(HttpStreamFactoryTest, JobNotifiesProxy) { const char* kProxyString = "PROXY bad:99; PROXY maybe:80; DIRECT"; SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest( kProxyString, TRAFFIC_ANNOTATION_FOR_TESTS)); // First connection attempt fails StaticSocketDataProvider socket_data1; socket_data1.set_connect_data(MockConnect(ASYNC, ERR_ADDRESS_UNREACHABLE)); session_deps.socket_factory->AddSocketDataProvider(&socket_data1); // Second connection attempt succeeds StaticSocketDataProvider socket_data2; socket_data2.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data2); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. It should succeed using the second proxy in the // list. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("http://www.google.com"); request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; std::unique_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); // The proxy that failed should now be known to the proxy_resolution_service // as bad. const ProxyRetryInfoMap& retry_info = session->proxy_resolution_service()->proxy_retry_info(); EXPECT_EQ(1u, retry_info.size()); auto iter = retry_info.find("bad:99"); EXPECT_TRUE(iter != retry_info.end()); } // This test requests a stream for an https:// URL using an HTTP proxy. // The proxy will fail to establish a tunnel via connect, and the resolved // proxy list includes a fallback to DIRECT. // // The expected behavior is that proxy fallback does NOT occur, even though the // request might work using the fallback. This is a regression test for // https://crbug.com/680837. TEST_F(HttpStreamFactoryTest, NoProxyFallbackOnTunnelFail) { const char* kProxyString = "PROXY bad:99; DIRECT"; SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest( kProxyString, TRAFFIC_ANNOTATION_FOR_TESTS)); // A 404 in response to a CONNECT will trigger // ERR_TUNNEL_CONNECTION_FAILED. MockRead data_reads[] = { MockRead("HTTP/1.1 404 Not Found\r\n\r\n"), MockRead(SYNCHRONOUS, OK), }; // Simulate a failure during CONNECT to bad:99. StaticSocketDataProvider socket_data1(data_reads, base::span()); socket_data1.set_connect_data(MockConnect(SYNCHRONOUS, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data1); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Request a stream for an https:// URL. The exact URL doesn't matter for // this test, since it mocks a failure immediately when establishing a // tunnel through the proxy. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; std::unique_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); // The stream should have failed, since the proxy server failed to // establish a tunnel. ASSERT_THAT(waiter.error_status(), IsError(ERR_TUNNEL_CONNECTION_FAILED)); // The proxy should NOT have been marked as bad. const ProxyRetryInfoMap& retry_info = session->proxy_resolution_service()->proxy_retry_info(); EXPECT_EQ(0u, retry_info.size()); } // List of errors that are used in the tests related to QUIC proxy. const int quic_proxy_test_mock_errors[] = { ERR_PROXY_CONNECTION_FAILED, ERR_NAME_NOT_RESOLVED, ERR_ADDRESS_UNREACHABLE, ERR_CONNECTION_CLOSED, ERR_CONNECTION_TIMED_OUT, ERR_CONNECTION_RESET, ERR_CONNECTION_REFUSED, ERR_CONNECTION_ABORTED, ERR_TIMED_OUT, ERR_SOCKS_CONNECTION_FAILED, ERR_PROXY_CERTIFICATE_INVALID, ERR_QUIC_PROTOCOL_ERROR, ERR_QUIC_HANDSHAKE_FAILED, ERR_SSL_PROTOCOL_ERROR, ERR_MSG_TOO_BIG, }; // Tests that a bad QUIC proxy is added to the list of bad proxies. TEST_F(HttpStreamFactoryTest, QuicProxyMarkedAsBad) { for (int quic_proxy_test_mock_error : quic_proxy_test_mock_errors) { std::unique_ptr proxy_resolution_service = ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest( "QUIC bad:99; DIRECT", TRAFFIC_ANNOTATION_FOR_TESTS); HttpNetworkSessionParams session_params; session_params.enable_quic = true; HttpNetworkSessionContext session_context; SSLConfigServiceDefaults ssl_config_service; HttpServerProperties http_server_properties; MockClientSocketFactory socket_factory; session_context.client_socket_factory = &socket_factory; MockHostResolver host_resolver; session_context.host_resolver = &host_resolver; MockCertVerifier cert_verifier; session_context.cert_verifier = &cert_verifier; TransportSecurityState transport_security_state; session_context.transport_security_state = &transport_security_state; DefaultCTPolicyEnforcer ct_policy_enforcer; QuicContext quic_context; session_context.ct_policy_enforcer = &ct_policy_enforcer; session_context.proxy_resolution_service = proxy_resolution_service.get(); session_context.ssl_config_service = &ssl_config_service; session_context.http_server_properties = &http_server_properties; session_context.quic_context = &quic_context; host_resolver.rules()->AddRule("www.google.com", "2.3.4.5"); host_resolver.rules()->AddRule("bad", "1.2.3.4"); auto session = std::make_unique(session_params, session_context); session->quic_stream_factory() ->set_is_quic_known_to_work_on_current_network(true); StaticSocketDataProvider socket_data1; socket_data1.set_connect_data( MockConnect(ASYNC, quic_proxy_test_mock_error)); socket_factory.AddSocketDataProvider(&socket_data1); // Second connection attempt succeeds. StaticSocketDataProvider socket_data2; socket_data2.set_connect_data(MockConnect(ASYNC, OK)); socket_factory.AddSocketDataProvider(&socket_data2); // Now request a stream. It should succeed using the second proxy in the // list. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("http://www.google.com"); request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; std::unique_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); // The proxy that failed should now be known to the // proxy_resolution_service as bad. const ProxyRetryInfoMap& retry_info = session->proxy_resolution_service()->proxy_retry_info(); EXPECT_EQ(1u, retry_info.size()) << quic_proxy_test_mock_error; EXPECT_TRUE(waiter.used_proxy_info().is_direct()); auto iter = retry_info.find("quic://bad:99"); EXPECT_TRUE(iter != retry_info.end()) << quic_proxy_test_mock_error; } } // BidirectionalStreamImpl::Delegate to wait until response headers are // received. class TestBidirectionalDelegate : public BidirectionalStreamImpl::Delegate { public: void WaitUntilDone() { loop_.Run(); } const spdy::Http2HeaderBlock& response_headers() const { return response_headers_; } private: void OnStreamReady(bool request_headers_sent) override {} void OnHeadersReceived( const spdy::Http2HeaderBlock& response_headers) override { response_headers_ = response_headers.Clone(); loop_.Quit(); } void OnDataRead(int bytes_read) override { NOTREACHED(); } void OnDataSent() override { NOTREACHED(); } void OnTrailersReceived(const spdy::Http2HeaderBlock& trailers) override { NOTREACHED(); } void OnFailed(int error) override { NOTREACHED(); } base::RunLoop loop_; spdy::Http2HeaderBlock response_headers_; }; // Helper class to encapsulate MockReads and MockWrites for QUIC. // Simplify ownership issues and the interaction with the MockSocketFactory. class MockQuicData { public: explicit MockQuicData(quic::ParsedQuicVersion version) : printer_(version) {} ~MockQuicData() = default; void AddRead(std::unique_ptr packet) { reads_.emplace_back(ASYNC, packet->data(), packet->length(), packet_number_++); packets_.push_back(std::move(packet)); } void AddRead(IoMode mode, int rv) { reads_.emplace_back(mode, rv, packet_number_++); } void AddWrite(std::unique_ptr packet) { writes_.emplace_back(SYNCHRONOUS, packet->data(), packet->length(), packet_number_++); packets_.push_back(std::move(packet)); } void AddSocketDataToFactory(MockClientSocketFactory* factory) { socket_data_ = std::make_unique(reads_, writes_); socket_data_->set_printer(&printer_); factory->AddSocketDataProvider(socket_data_.get()); } private: std::vector> packets_; std::vector writes_; std::vector reads_; size_t packet_number_ = 0; QuicPacketPrinter printer_; std::unique_ptr socket_data_; }; } // namespace TEST_F(HttpStreamFactoryTest, UsePreConnectIfNoZeroRTT) { for (int num_streams = 1; num_streams < 3; ++num_streams) { GURL url = GURL("https://www.google.com"); SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateFixedForTest( "http_proxy", TRAFFIC_ANNOTATION_FOR_TESTS)); // Setup params to disable preconnect, but QUIC doesn't 0RTT. HttpNetworkSessionParams session_params = SpdySessionDependencies::CreateSessionParams(&session_deps); session_params.enable_quic = true; // Set up QUIC as alternative_service. HttpServerProperties http_server_properties; const AlternativeService alternative_service(kProtoQUIC, url.host().c_str(), url.IntPort()); base::Time expiration = base::Time::Now() + base::Days(1); HostPortPair host_port_pair(alternative_service.host_port_pair()); url::SchemeHostPort server("https", host_port_pair.host(), host_port_pair.port()); http_server_properties.SetQuicAlternativeService( server, NetworkIsolationKey(), alternative_service, expiration, DefaultSupportedQuicVersions()); HttpNetworkSessionContext session_context = SpdySessionDependencies::CreateSessionContext(&session_deps); session_context.http_server_properties = &http_server_properties; auto session = std::make_unique(session_params, session_context); HttpNetworkSessionPeer peer(session.get()); ProxyServer proxy_server(ProxyServer::SCHEME_HTTP, HostPortPair("http_proxy", 80)); CommonConnectJobParams common_connect_job_params = session->CreateCommonConnectJobParams(); auto http_proxy_pool = std::make_unique( &common_connect_job_params); auto* http_proxy_pool_ptr = http_proxy_pool.get(); auto mock_pool_manager = std::make_unique(); mock_pool_manager->SetSocketPool(proxy_server, std::move(http_proxy_pool)); peer.SetClientSocketPoolManager(std::move(mock_pool_manager)); PreconnectHelperForURL(num_streams, url, NetworkIsolationKey(), SecureDnsPolicy::kAllow, session.get()); EXPECT_EQ(num_streams, http_proxy_pool_ptr->last_num_streams()); } } namespace { // Return count of distinct groups in given socket pool. int GetSocketPoolGroupCount(ClientSocketPool* pool) { int count = 0; base::Value dict = pool->GetInfoAsValue("", ""); EXPECT_TRUE(dict.is_dict()); const base::Value::Dict* groups = dict.GetDict().FindDict("groups"); if (groups) { count = groups->size(); } return count; } // Return count of distinct spdy sessions. int GetSpdySessionCount(HttpNetworkSession* session) { std::unique_ptr value( session->spdy_session_pool()->SpdySessionPoolInfoToValue()); if (!value || !value->is_list()) return -1; return value->GetList().size(); } // Return count of sockets handed out by a given socket pool. int GetHandedOutSocketCount(ClientSocketPool* pool) { base::Value dict = pool->GetInfoAsValue("", ""); EXPECT_TRUE(dict.is_dict()); return dict.GetDict().FindInt("handed_out_socket_count").value_or(-1); } // Return count of distinct QUIC sessions. int GetQuicSessionCount(HttpNetworkSession* session) { base::Value dict(session->QuicInfoToValue()); base::Value::List* session_list = dict.GetDict().FindList("sessions"); if (!session_list) return -1; return session_list->size(); } TEST_F(HttpStreamFactoryTest, PrivacyModeUsesDifferentSocketPoolGroup) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); StaticSocketDataProvider socket_data_1; socket_data_1.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data_1); StaticSocketDataProvider socket_data_2; socket_data_2.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data_2); StaticSocketDataProvider socket_data_3; socket_data_3.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data_3); SSLSocketDataProvider ssl_1(ASYNC, OK); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_1); SSLSocketDataProvider ssl_2(ASYNC, OK); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_2); SSLSocketDataProvider ssl_3(ASYNC, OK); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_3); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); ClientSocketPool* ssl_pool = session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()); EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 0); HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; request_info.privacy_mode = PRIVACY_MODE_DISABLED; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; std::unique_ptr request1( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 1); std::unique_ptr request2( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 1); request_info.privacy_mode = PRIVACY_MODE_ENABLED; std::unique_ptr request3( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 2); } TEST_F(HttpStreamFactoryTest, DisableSecureDnsUsesDifferentSocketPoolGroup) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); StaticSocketDataProvider socket_data_1; socket_data_1.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data_1); StaticSocketDataProvider socket_data_2; socket_data_2.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data_2); StaticSocketDataProvider socket_data_3; socket_data_3.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data_3); SSLSocketDataProvider ssl_1(ASYNC, OK); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_1); SSLSocketDataProvider ssl_2(ASYNC, OK); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_2); SSLSocketDataProvider ssl_3(ASYNC, OK); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_3); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); ClientSocketPool* ssl_pool = session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()); EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 0); HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; request_info.privacy_mode = PRIVACY_MODE_DISABLED; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); request_info.secure_dns_policy = SecureDnsPolicy::kAllow; SSLConfig ssl_config; StreamRequestWaiter waiter; std::unique_ptr request1( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_EQ(SecureDnsPolicy::kAllow, session_deps.host_resolver->last_secure_dns_policy()); EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 1); std::unique_ptr request2( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_EQ(SecureDnsPolicy::kAllow, session_deps.host_resolver->last_secure_dns_policy()); EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 1); request_info.secure_dns_policy = SecureDnsPolicy::kDisable; std::unique_ptr request3( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_EQ(SecureDnsPolicy::kDisable, session_deps.host_resolver->last_secure_dns_policy()); EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 2); } TEST_F(HttpStreamFactoryTest, GetLoadState) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); StaticSocketDataProvider socket_data; socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("http://www.google.com"); request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; std::unique_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, request->GetLoadState()); waiter.WaitForStream(); } TEST_F(HttpStreamFactoryTest, RequestHttpStream) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); StaticSocketDataProvider socket_data; socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. It should succeed using the second proxy in the // list. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("http://www.google.com"); request_info.load_flags = 0; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; std::unique_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); ASSERT_TRUE(nullptr != waiter.stream()); EXPECT_TRUE(nullptr == waiter.websocket_stream()); EXPECT_EQ(0, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); } // Test the race of SetPriority versus stream completion where SetPriority may // be called on an HttpStreamFactory::Job after the stream has been created by // the job. TEST_F(HttpStreamFactoryTest, ReprioritizeAfterStreamReceived) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); session_deps.host_resolver->set_synchronous_mode(true); MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING); StaticSocketDataProvider socket_data(base::make_span(&mock_read, 1), base::span()); socket_data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl_socket_data(SYNCHRONOUS, OK); ssl_socket_data.next_proto = kProtoHTTP2; session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; EXPECT_EQ(0, GetSpdySessionCount(session.get())); std::unique_ptr request( session->http_stream_factory()->RequestStream( request_info, LOWEST, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); EXPECT_FALSE(waiter.stream_done()); // Confirm a stream has been created by asserting that a new session // has been created. (The stream is only created at the SPDY level on // first write, which happens after the request has returned a stream). ASSERT_EQ(1, GetSpdySessionCount(session.get())); // Test to confirm that a SetPriority received after the stream is created // but before the request returns it does not crash. request->SetPriority(HIGHEST); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); ASSERT_TRUE(waiter.stream()); EXPECT_FALSE(waiter.websocket_stream()); } TEST_F(HttpStreamFactoryTest, RequestHttpStreamOverSSL) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); MockRead mock_read(ASYNC, OK); StaticSocketDataProvider socket_data(base::make_span(&mock_read, 1), base::span()); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; std::unique_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); ASSERT_TRUE(nullptr != waiter.stream()); EXPECT_TRUE(nullptr == waiter.websocket_stream()); EXPECT_EQ(0, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); } TEST_F(HttpStreamFactoryTest, RequestHttpStreamOverProxy) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateFixedForTest( "myproxy:8888", TRAFFIC_ANNOTATION_FOR_TESTS)); StaticSocketDataProvider socket_data; socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. It should succeed using the second proxy in the // list. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("http://www.google.com"); request_info.load_flags = 0; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; std::unique_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); ASSERT_TRUE(nullptr != waiter.stream()); EXPECT_TRUE(nullptr == waiter.websocket_stream()); EXPECT_EQ(0, GetSpdySessionCount(session.get())); EXPECT_EQ( 0, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer(ProxyServer::SCHEME_HTTP, HostPortPair("myproxy", 8888))))); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer(ProxyServer::SCHEME_HTTPS, HostPortPair("myproxy", 8888))))); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL, ProxyServer(ProxyServer::SCHEME_HTTP, HostPortPair("myproxy", 8888))))); EXPECT_FALSE(waiter.used_proxy_info().is_direct()); } TEST_F(HttpStreamFactoryTest, RequestWebSocketBasicHandshakeStream) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); StaticSocketDataProvider socket_data; socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("ws://www.google.com"); request_info.load_flags = 0; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; WebSocketStreamCreateHelper create_helper; std::unique_ptr request( session->http_stream_factory()->RequestWebSocketHandshakeStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, &create_helper, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_TRUE(nullptr == waiter.stream()); ASSERT_TRUE(nullptr != waiter.websocket_stream()); EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeBasic, waiter.websocket_stream()->type()); EXPECT_EQ( 0, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); } TEST_F(HttpStreamFactoryTest, RequestWebSocketBasicHandshakeStreamOverSSL) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); MockRead mock_read(ASYNC, OK); StaticSocketDataProvider socket_data(base::make_span(&mock_read, 1), base::span()); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("wss://www.google.com"); request_info.load_flags = 0; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; WebSocketStreamCreateHelper create_helper; std::unique_ptr request( session->http_stream_factory()->RequestWebSocketHandshakeStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, &create_helper, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_TRUE(nullptr == waiter.stream()); ASSERT_TRUE(nullptr != waiter.websocket_stream()); EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeBasic, waiter.websocket_stream()->type()); EXPECT_EQ( 0, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); } TEST_F(HttpStreamFactoryTest, RequestWebSocketBasicHandshakeStreamOverProxy) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateFixedForTest( "myproxy:8888", TRAFFIC_ANNOTATION_FOR_TESTS)); MockRead reads[] = { MockRead(SYNCHRONOUS, "HTTP/1.0 200 Connection established\r\n\r\n")}; StaticSocketDataProvider socket_data(reads, base::span()); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("ws://www.google.com"); request_info.load_flags = 0; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; WebSocketStreamCreateHelper create_helper; std::unique_ptr request( session->http_stream_factory()->RequestWebSocketHandshakeStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, &create_helper, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_TRUE(nullptr == waiter.stream()); ASSERT_TRUE(nullptr != waiter.websocket_stream()); EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeBasic, waiter.websocket_stream()->type()); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer(ProxyServer::SCHEME_HTTP, HostPortPair("myproxy", 8888))))); EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::WEBSOCKET_SOCKET_POOL, ProxyServer(ProxyServer::SCHEME_HTTP, HostPortPair("myproxy", 8888))))); EXPECT_FALSE(waiter.used_proxy_info().is_direct()); } TEST_F(HttpStreamFactoryTest, RequestSpdyHttpStreamHttpsURL) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING); SequencedSocketData socket_data(base::make_span(&mock_read, 1), base::span()); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); ssl_socket_data.next_proto = kProtoHTTP2; session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); HostPortPair host_port_pair("www.google.com", 443); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; std::unique_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_TRUE(nullptr == waiter.websocket_stream()); ASSERT_TRUE(nullptr != waiter.stream()); EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); } TEST_F(HttpStreamFactoryTest, RequestSpdyHttpStreamHttpURL) { url::SchemeHostPort scheme_host_port("http", "myproxy.org", 443); auto session_deps = std::make_unique( ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest( "HTTPS myproxy.org:443", TRAFFIC_ANNOTATION_FOR_TESTS)); std::unique_ptr proxy_resolution_service = ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest( "HTTPS myproxy.org:443", TRAFFIC_ANNOTATION_FOR_TESTS); MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING); SequencedSocketData socket_data(base::make_span(&mock_read, 1), base::span()); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps->socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); ssl_socket_data.next_proto = kProtoHTTP2; session_deps->socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); session_deps->proxy_resolution_service = std::move(proxy_resolution_service); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(session_deps.get())); HttpServerProperties* http_server_properties = session->spdy_session_pool()->http_server_properties(); EXPECT_FALSE(http_server_properties->GetSupportsSpdy(scheme_host_port, NetworkIsolationKey())); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("http://www.google.com"); request_info.load_flags = 0; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; std::unique_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_TRUE(nullptr == waiter.websocket_stream()); ASSERT_TRUE(nullptr != waiter.stream()); EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_EQ( 0, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_FALSE(waiter.used_proxy_info().is_direct()); EXPECT_TRUE(http_server_properties->GetSupportsSpdy(scheme_host_port, NetworkIsolationKey())); } // Same as above, but checks HttpServerProperties is updated using the correct // NetworkIsolationKey. When/if NetworkIsolationKey is enabled by default, this // should probably be merged into the above test. TEST_F(HttpStreamFactoryTest, RequestSpdyHttpStreamHttpURLWithNetworkIsolationKey) { const SchemefulSite kSite1(GURL("https://foo.test/")); const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1); const SchemefulSite kSite2(GURL("https://bar.test/")); const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2); base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature( features::kPartitionHttpServerPropertiesByNetworkIsolationKey); url::SchemeHostPort scheme_host_port("http", "myproxy.org", 443); auto session_deps = std::make_unique( ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest( "HTTPS myproxy.org:443", TRAFFIC_ANNOTATION_FOR_TESTS)); std::unique_ptr proxy_resolution_service = ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest( "HTTPS myproxy.org:443", TRAFFIC_ANNOTATION_FOR_TESTS); MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING); SequencedSocketData socket_data(base::make_span(&mock_read, 1), base::span()); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps->socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); ssl_socket_data.next_proto = kProtoHTTP2; session_deps->socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); session_deps->proxy_resolution_service = std::move(proxy_resolution_service); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(session_deps.get())); HttpServerProperties* http_server_properties = session->spdy_session_pool()->http_server_properties(); EXPECT_FALSE(http_server_properties->GetSupportsSpdy(scheme_host_port, kNetworkIsolationKey1)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("http://www.google.com"); request_info.load_flags = 0; request_info.network_isolation_key = kNetworkIsolationKey1; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; std::unique_ptr request( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_TRUE(nullptr == waiter.websocket_stream()); ASSERT_TRUE(nullptr != waiter.stream()); EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_EQ( 0, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_FALSE(waiter.used_proxy_info().is_direct()); EXPECT_TRUE(http_server_properties->GetSupportsSpdy(scheme_host_port, kNetworkIsolationKey1)); // Other NetworkIsolationKeys should not be recorded as supporting SPDY. EXPECT_FALSE(http_server_properties->GetSupportsSpdy(scheme_host_port, NetworkIsolationKey())); EXPECT_FALSE(http_server_properties->GetSupportsSpdy(scheme_host_port, kNetworkIsolationKey2)); } // Tests that when a new SpdySession is established, duplicated idle H2 sockets // to the same server are closed. TEST_F(HttpStreamFactoryTest, NewSpdySessionCloseIdleH2Sockets) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); const int kNumIdleSockets = 4; MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)}; std::vector> providers; SSLSocketDataProvider ssl_socket_data(ASYNC, OK); ssl_socket_data.next_proto = kProtoHTTP2; for (int i = 0; i < kNumIdleSockets; i++) { auto provider = std::make_unique(reads, base::span()); provider->set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(provider.get()); providers.push_back(std::move(provider)); session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); } std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); url::SchemeHostPort destination(url::kHttpsScheme, "www.google.com", 443); // Create some HTTP/2 sockets. std::vector> handles; for (size_t i = 0; i < kNumIdleSockets; i++) { auto connection = std::make_unique(); TestCompletionCallback callback; auto ssl_config_for_origin = std::make_unique(); ssl_config_for_origin->alpn_protos = session->GetAlpnProtos(); scoped_refptr socket_params = base::MakeRefCounted( std::move(ssl_config_for_origin), /*ssl_config_for_proxy=*/nullptr); ClientSocketPool::GroupId group_id( destination, PrivacyMode::PRIVACY_MODE_DISABLED, NetworkIsolationKey(), SecureDnsPolicy::kAllow); int rv = connection->Init( group_id, socket_params, absl::nullopt /* proxy_annotation_tag */, MEDIUM, SocketTag(), ClientSocketPool::RespectLimits::ENABLED, callback.callback(), ClientSocketPool::ProxyAuthCallback(), session->GetSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()), NetLogWithSource()); rv = callback.GetResult(rv); handles.push_back(std::move(connection)); } // Releases handles now, and these sockets should go into the socket pool. handles.clear(); EXPECT_EQ(kNumIdleSockets, session ->GetSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()) ->IdleSocketCount()); // Request two streams at once and make sure they use the same connection. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter1; StreamRequestWaiter waiter2; std::unique_ptr request1( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter1, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); std::unique_ptr request2( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter2, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter1.WaitForStream(); waiter2.WaitForStream(); EXPECT_TRUE(waiter1.stream_done()); EXPECT_TRUE(waiter2.stream_done()); ASSERT_NE(nullptr, waiter1.stream()); ASSERT_NE(nullptr, waiter2.stream()); ASSERT_NE(waiter1.stream(), waiter2.stream()); // Establishing the SpdySession will close idle H2 sockets. EXPECT_EQ(0, session ->GetSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()) ->IdleSocketCount()); EXPECT_EQ(1, GetSpdySessionCount(session.get())); } // Regression test for https://crbug.com/706974. TEST_F(HttpStreamFactoryTest, TwoSpdyConnects) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); SSLSocketDataProvider ssl_socket_data0(ASYNC, OK); ssl_socket_data0.next_proto = kProtoHTTP2; session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data0); MockRead reads0[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)}; SequencedSocketData data0(reads0, base::span()); data0.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&data0); SSLSocketDataProvider ssl_socket_data1(ASYNC, OK); ssl_socket_data1.next_proto = kProtoHTTP2; session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data1); SequencedSocketData data1; data1.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&data1); std::unique_ptr session = SpdySessionDependencies::SpdyCreateSession(&session_deps); HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; // Request two streams at once and make sure they use the same connection. StreamRequestWaiter waiter1; std::unique_ptr request1 = session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter1, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource()); StreamRequestWaiter waiter2; std::unique_ptr request2 = session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter2, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource()); waiter1.WaitForStream(); waiter2.WaitForStream(); EXPECT_TRUE(waiter1.stream_done()); EXPECT_TRUE(waiter2.stream_done()); ASSERT_NE(nullptr, waiter1.stream()); ASSERT_NE(nullptr, waiter2.stream()); ASSERT_NE(waiter1.stream(), waiter2.stream()); // Establishing the SpdySession will close the extra H2 socket. EXPECT_EQ(0, session ->GetSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()) ->IdleSocketCount()); EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_TRUE(data0.AllReadDataConsumed()); EXPECT_TRUE(data1.AllReadDataConsumed()); } TEST_F(HttpStreamFactoryTest, RequestBidirectionalStreamImpl) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); MockRead mock_read(ASYNC, OK); SequencedSocketData socket_data(base::make_span(&mock_read, 1), base::span()); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); ssl_socket_data.next_proto = kProtoHTTP2; session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; std::unique_ptr request( session->http_stream_factory()->RequestBidirectionalStreamImpl( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_FALSE(waiter.websocket_stream()); ASSERT_FALSE(waiter.stream()); ASSERT_TRUE(waiter.bidirectional_stream_impl()); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); } struct TestParams { quic::ParsedQuicVersion version; bool client_headers_include_h2_stream_dependency; }; // Used by ::testing::PrintToStringParamName(). std::string PrintToString(const TestParams& p) { return base::StrCat( {ParsedQuicVersionToString(p.version), "_", (p.client_headers_include_h2_stream_dependency ? "" : "No"), "Dependency"}); } std::vector GetTestParams() { std::vector 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; } class HttpStreamFactoryBidirectionalQuicTest : public TestWithTaskEnvironment, public ::testing::WithParamInterface { protected: HttpStreamFactoryBidirectionalQuicTest() : default_url_(kDefaultUrl), version_(GetParam().version), client_headers_include_h2_stream_dependency_( GetParam().client_headers_include_h2_stream_dependency), client_packet_maker_(version_, quic::QuicUtils::CreateRandomConnectionId( quic_context_.random_generator()), quic_context_.clock(), "www.example.org", quic::Perspective::IS_CLIENT, client_headers_include_h2_stream_dependency_), server_packet_maker_(version_, quic::QuicUtils::CreateRandomConnectionId( quic_context_.random_generator()), quic_context_.clock(), "www.example.org", quic::Perspective::IS_SERVER, false), proxy_resolution_service_( ConfiguredProxyResolutionService::CreateDirect()), ssl_config_service_(std::make_unique()) { FLAGS_quic_enable_http3_grease_randomness = false; quic_context_.AdvanceTime(quic::QuicTime::Delta::FromMilliseconds(20)); quic::QuicEnableVersion(version_); } void TearDown() override { session_.reset(); } // Disable bidirectional stream over QUIC. This should be invoked before // Initialize(). void DisableQuicBidirectionalStream() { quic_context_.params()->disable_bidirectional_streams = true; } void Initialize() { params_.enable_quic = true; quic_context_.params()->supported_versions = quic::test::SupportedVersions(version_); quic_context_.params()->headers_include_h2_stream_dependency = client_headers_include_h2_stream_dependency_; HttpNetworkSessionContext session_context; session_context.http_server_properties = &http_server_properties_; session_context.quic_context = &quic_context_; // Load a certificate that is valid for *.example.org scoped_refptr test_cert( ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem")); EXPECT_TRUE(test_cert.get()); verify_details_.cert_verify_result.verified_cert = test_cert; verify_details_.cert_verify_result.is_issued_by_known_root = true; crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details_); crypto_client_stream_factory_.set_handshake_mode( MockCryptoClientStream::CONFIRM_HANDSHAKE); session_context.cert_verifier = &cert_verifier_; session_context.quic_crypto_client_stream_factory = &crypto_client_stream_factory_; session_context.transport_security_state = &transport_security_state_; session_context.ct_policy_enforcer = &ct_policy_enforcer_; session_context.host_resolver = &host_resolver_; session_context.proxy_resolution_service = proxy_resolution_service_.get(); session_context.ssl_config_service = ssl_config_service_.get(); session_context.client_socket_factory = &socket_factory_; session_ = std::make_unique(params_, session_context); session_->quic_stream_factory() ->set_is_quic_known_to_work_on_current_network(true); } void AddQuicAlternativeService(const url::SchemeHostPort& request_url, const std::string& alternative_destination) { const AlternativeService alternative_service(kProtoQUIC, alternative_destination, 443); base::Time expiration = base::Time::Now() + base::Days(1); http_server_properties_.SetQuicAlternativeService( request_url, NetworkIsolationKey(), alternative_service, expiration, session_->context().quic_context->params()->supported_versions); } void AddQuicAlternativeService() { AddQuicAlternativeService(url::SchemeHostPort(default_url_), "www.example.org"); } test::QuicTestPacketMaker& client_packet_maker() { return client_packet_maker_; } test::QuicTestPacketMaker& server_packet_maker() { return server_packet_maker_; } MockTaggingClientSocketFactory& socket_factory() { return socket_factory_; } HttpNetworkSession* session() { return session_.get(); } const GURL default_url_; quic::QuicStreamId GetNthClientInitiatedBidirectionalStreamId(int n) { return quic::test::GetNthClientInitiatedBidirectionalStreamId( version_.transport_version, n); } quic::ParsedQuicVersion version() const { return version_; } MockHostResolver* host_resolver() { return &host_resolver_; } private: quic::test::QuicFlagSaver saver_; const quic::ParsedQuicVersion version_; const bool client_headers_include_h2_stream_dependency_; MockQuicContext quic_context_; test::QuicTestPacketMaker client_packet_maker_; test::QuicTestPacketMaker server_packet_maker_; MockTaggingClientSocketFactory socket_factory_; std::unique_ptr session_; MockCertVerifier cert_verifier_; ProofVerifyDetailsChromium verify_details_; MockCryptoClientStreamFactory crypto_client_stream_factory_; HttpServerProperties http_server_properties_; TransportSecurityState transport_security_state_; DefaultCTPolicyEnforcer ct_policy_enforcer_; MockHostResolver host_resolver_{ /*default_result=*/ MockHostResolverBase::RuleResolver::GetLocalhostResult()}; std::unique_ptr proxy_resolution_service_; std::unique_ptr ssl_config_service_; HttpNetworkSessionParams params_; }; INSTANTIATE_TEST_SUITE_P(VersionIncludeStreamDependencySequence, HttpStreamFactoryBidirectionalQuicTest, ::testing::ValuesIn(GetTestParams()), ::testing::PrintToStringParamName()); TEST_P(HttpStreamFactoryBidirectionalQuicTest, RequestBidirectionalStreamImplQuicAlternative) { MockQuicData mock_quic_data(version()); // When using IETF QUIC, set priority to default value so that // QuicTestPacketMaker::MakeRequestHeadersPacket() does not add mock // PRIORITY_UPDATE frame, which BidirectionalStreamQuicImpl currently does not // send. // TODO(https://crbug.com/1059250): Implement PRIORITY_UPDATE in // BidirectionalStreamQuicImpl. spdy::SpdyPriority priority = ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY); size_t spdy_headers_frame_length; int packet_num = 1; if (VersionUsesHttp3(version().transport_version)) { mock_quic_data.AddWrite( client_packet_maker().MakeInitialSettingsPacket(packet_num++)); } mock_quic_data.AddWrite(client_packet_maker().MakeRequestHeadersPacket( packet_num++, GetNthClientInitiatedBidirectionalStreamId(0), /*should_include_version=*/true, /*fin=*/true, priority, client_packet_maker().GetRequestHeaders("GET", "https", "/"), /*parent_stream_id=*/0, &spdy_headers_frame_length)); size_t spdy_response_headers_frame_length; mock_quic_data.AddRead(server_packet_maker().MakeResponseHeadersPacket( 1, GetNthClientInitiatedBidirectionalStreamId(0), /*should_include_version=*/false, /*fin=*/true, server_packet_maker().GetResponseHeaders("200"), &spdy_response_headers_frame_length)); mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // No more read data. mock_quic_data.AddSocketDataToFactory(&socket_factory()); // Add hanging data for http job. auto hanging_data = std::make_unique(); MockConnect hanging_connect(SYNCHRONOUS, ERR_IO_PENDING); hanging_data->set_connect_data(hanging_connect); socket_factory().AddSocketDataProvider(hanging_data.get()); SSLSocketDataProvider ssl_data(ASYNC, OK); socket_factory().AddSSLSocketDataProvider(&ssl_data); // Set up QUIC as alternative_service. Initialize(); AddQuicAlternativeService(); // Now request a stream. SSLConfig ssl_config; HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = default_url_; request_info.load_flags = 0; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); StreamRequestWaiter waiter; std::unique_ptr request( session()->http_stream_factory()->RequestBidirectionalStreamImpl( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_FALSE(waiter.websocket_stream()); ASSERT_FALSE(waiter.stream()); ASSERT_TRUE(waiter.bidirectional_stream_impl()); BidirectionalStreamImpl* stream_impl = waiter.bidirectional_stream_impl(); BidirectionalStreamRequestInfo bidi_request_info; bidi_request_info.method = "GET"; bidi_request_info.url = default_url_; bidi_request_info.end_stream_on_headers = true; bidi_request_info.priority = LOWEST; TestBidirectionalDelegate delegate; stream_impl->Start(&bidi_request_info, NetLogWithSource(), /*send_request_headers_automatically=*/true, &delegate, nullptr, TRAFFIC_ANNOTATION_FOR_TESTS); delegate.WaitUntilDone(); scoped_refptr buffer = base::MakeRefCounted(1); EXPECT_THAT(stream_impl->ReadData(buffer.get(), 1), IsOk()); EXPECT_EQ(kProtoQUIC, stream_impl->GetProtocol()); EXPECT_EQ("200", delegate.response_headers().find(":status")->second); EXPECT_EQ( 0, GetSocketPoolGroupCount(session()->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); } // Tests that when QUIC is not enabled for bidirectional streaming, HTTP/2 is // used instead. TEST_P(HttpStreamFactoryBidirectionalQuicTest, RequestBidirectionalStreamImplQuicNotEnabled) { // Make the http job fail. auto http_job_data = std::make_unique(); MockConnect failed_connect(ASYNC, ERR_CONNECTION_REFUSED); http_job_data->set_connect_data(failed_connect); socket_factory().AddSocketDataProvider(http_job_data.get()); SSLSocketDataProvider ssl_data(ASYNC, OK); socket_factory().AddSSLSocketDataProvider(&ssl_data); // Set up QUIC as alternative_service. DisableQuicBidirectionalStream(); Initialize(); AddQuicAlternativeService(); // Now request a stream. SSLConfig ssl_config; HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = default_url_; request_info.load_flags = 0; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); StreamRequestWaiter waiter; std::unique_ptr request( session()->http_stream_factory()->RequestBidirectionalStreamImpl( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_FALSE(waiter.websocket_stream()); ASSERT_FALSE(waiter.stream()); ASSERT_FALSE(waiter.bidirectional_stream_impl()); // Since the alternative service job is not started, we will get the error // from the http job. ASSERT_THAT(waiter.error_status(), IsError(ERR_CONNECTION_REFUSED)); } // Tests that if Http job fails, but Quic job succeeds, we return // BidirectionalStreamQuicImpl. TEST_P(HttpStreamFactoryBidirectionalQuicTest, RequestBidirectionalStreamImplHttpJobFailsQuicJobSucceeds) { // Set up Quic data. MockQuicData mock_quic_data(version()); // When using IETF QUIC, set priority to default value so that // QuicTestPacketMaker::MakeRequestHeadersPacket() does not add mock // PRIORITY_UPDATE frame, which BidirectionalStreamQuicImpl currently does not // send. // TODO(https://crbug.com/1059250): Implement PRIORITY_UPDATE in // BidirectionalStreamQuicImpl. spdy::SpdyPriority priority = ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY); size_t spdy_headers_frame_length; int packet_num = 1; if (VersionUsesHttp3(version().transport_version)) { mock_quic_data.AddWrite( client_packet_maker().MakeInitialSettingsPacket(packet_num++)); } mock_quic_data.AddWrite(client_packet_maker().MakeRequestHeadersPacket( packet_num++, GetNthClientInitiatedBidirectionalStreamId(0), /*should_include_version=*/true, /*fin=*/true, priority, client_packet_maker().GetRequestHeaders("GET", "https", "/"), /*parent_stream_id=*/0, &spdy_headers_frame_length)); size_t spdy_response_headers_frame_length; mock_quic_data.AddRead(server_packet_maker().MakeResponseHeadersPacket( 1, GetNthClientInitiatedBidirectionalStreamId(0), /*should_include_version=*/false, /*fin=*/true, server_packet_maker().GetResponseHeaders("200"), &spdy_response_headers_frame_length)); mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // No more read data. mock_quic_data.AddSocketDataToFactory(&socket_factory()); // Make the http job fail. auto http_job_data = std::make_unique(); MockConnect failed_connect(ASYNC, ERR_CONNECTION_REFUSED); http_job_data->set_connect_data(failed_connect); socket_factory().AddSocketDataProvider(http_job_data.get()); SSLSocketDataProvider ssl_data(ASYNC, OK); socket_factory().AddSSLSocketDataProvider(&ssl_data); // Set up QUIC as alternative_service. Initialize(); AddQuicAlternativeService(); // Now request a stream. SSLConfig ssl_config; HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = default_url_; request_info.load_flags = 0; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); StreamRequestWaiter waiter; std::unique_ptr request( session()->http_stream_factory()->RequestBidirectionalStreamImpl( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); EXPECT_FALSE(waiter.websocket_stream()); ASSERT_FALSE(waiter.stream()); ASSERT_TRUE(waiter.bidirectional_stream_impl()); BidirectionalStreamImpl* stream_impl = waiter.bidirectional_stream_impl(); BidirectionalStreamRequestInfo bidi_request_info; bidi_request_info.method = "GET"; bidi_request_info.url = default_url_; bidi_request_info.end_stream_on_headers = true; bidi_request_info.priority = LOWEST; TestBidirectionalDelegate delegate; stream_impl->Start(&bidi_request_info, NetLogWithSource(), /*send_request_headers_automatically=*/true, &delegate, nullptr, TRAFFIC_ANNOTATION_FOR_TESTS); delegate.WaitUntilDone(); // Make sure the BidirectionalStream negotiated goes through QUIC. scoped_refptr buffer = base::MakeRefCounted(1); EXPECT_THAT(stream_impl->ReadData(buffer.get(), 1), IsOk()); EXPECT_EQ(kProtoQUIC, stream_impl->GetProtocol()); EXPECT_EQ("200", delegate.response_headers().find(":status")->second); // There is no Http2 socket pool. EXPECT_EQ( 0, GetSocketPoolGroupCount(session()->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_TRUE(waiter.used_proxy_info().is_direct()); } TEST_F(HttpStreamFactoryTest, RequestBidirectionalStreamImplFailure) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); MockRead mock_read(ASYNC, OK); SequencedSocketData socket_data(base::make_span(&mock_read, 1), base::span()); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); // If HTTP/1 is used, BidirectionalStreamImpl should not be obtained. ssl_socket_data.next_proto = kProtoHTTP11; session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Now request a stream. HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; request_info.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SSLConfig ssl_config; StreamRequestWaiter waiter; std::unique_ptr request( session->http_stream_factory()->RequestBidirectionalStreamImpl( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter.WaitForStream(); EXPECT_TRUE(waiter.stream_done()); ASSERT_THAT(waiter.error_status(), IsError(ERR_FAILED)); EXPECT_FALSE(waiter.websocket_stream()); ASSERT_FALSE(waiter.stream()); ASSERT_FALSE(waiter.bidirectional_stream_impl()); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); } #if BUILDFLAG(IS_ANDROID) // Verify HttpStreamFactory::Job passes socket tag along properly and that // SpdySessions have unique socket tags (e.g. one sessions should not be shared // amongst streams with different socket tags). TEST_F(HttpStreamFactoryTest, Tag) { SpdySessionDependencies session_deps; auto socket_factory = std::make_unique(); auto* socket_factory_ptr = socket_factory.get(); session_deps.socket_factory = std::move(socket_factory); // Prepare for two HTTPS connects. MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING); SequencedSocketData socket_data(base::make_span(&mock_read, 1), base::span()); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); MockRead mock_read2(SYNCHRONOUS, ERR_IO_PENDING); SequencedSocketData socket_data2(base::make_span(&mock_read2, 1), base::span()); socket_data2.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data2); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); ssl_socket_data.ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"); ssl_socket_data.next_proto = kProtoHTTP2; session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); SSLSocketDataProvider ssl_socket_data2(ASYNC, OK); ssl_socket_data2.ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"); ssl_socket_data2.next_proto = kProtoHTTP2; session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data2); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Prepare two different tags and corresponding HttpRequestInfos. SocketTag tag1(SocketTag::UNSET_UID, 0x12345678); HttpRequestInfo request_info1; request_info1.method = "GET"; request_info1.url = GURL("https://example.org"); request_info1.load_flags = 0; request_info1.socket_tag = tag1; request_info1.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SocketTag tag2(getuid(), 0x87654321); HttpRequestInfo request_info2 = request_info1; request_info2.socket_tag = tag2; request_info2.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); // Verify one stream with one tag results in one session, group and // socket. SSLConfig ssl_config; StreamRequestWaiter waiter1; std::unique_ptr request1( session->http_stream_factory()->RequestStream( request_info1, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter1, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter1.WaitForStream(); EXPECT_TRUE(waiter1.stream_done()); EXPECT_TRUE(nullptr == waiter1.websocket_stream()); ASSERT_TRUE(nullptr != waiter1.stream()); EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 1, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Verify socket tagged appropriately. EXPECT_TRUE(tag1 == socket_factory_ptr->GetLastProducedTCPSocket()->tag()); EXPECT_TRUE(socket_factory_ptr->GetLastProducedTCPSocket() ->tagged_before_connected()); // Verify one more stream with a different tag results in one more session and // socket. StreamRequestWaiter waiter2; std::unique_ptr request2( session->http_stream_factory()->RequestStream( request_info2, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter2, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter2.WaitForStream(); EXPECT_TRUE(waiter2.stream_done()); EXPECT_TRUE(nullptr == waiter2.websocket_stream()); ASSERT_TRUE(nullptr != waiter2.stream()); EXPECT_EQ(2, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 2, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Verify socket tagged appropriately. EXPECT_TRUE(tag2 == socket_factory_ptr->GetLastProducedTCPSocket()->tag()); EXPECT_TRUE(socket_factory_ptr->GetLastProducedTCPSocket() ->tagged_before_connected()); // Verify one more stream reusing a tag does not create new sessions, groups // or sockets. StreamRequestWaiter waiter3; std::unique_ptr request3( session->http_stream_factory()->RequestStream( request_info2, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter3, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter3.WaitForStream(); EXPECT_TRUE(waiter3.stream_done()); EXPECT_TRUE(nullptr == waiter3.websocket_stream()); ASSERT_TRUE(nullptr != waiter3.stream()); EXPECT_EQ(2, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 2, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); } // Verify HttpStreamFactory::Job passes socket tag along properly to QUIC // sessions and that QuicSessions have unique socket tags (e.g. one sessions // should not be shared amongst streams with different socket tags). TEST_P(HttpStreamFactoryBidirectionalQuicTest, Tag) { // Prepare mock QUIC data for a first session establishment. MockQuicData mock_quic_data(version()); spdy::SpdyPriority priority = ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY); size_t spdy_headers_frame_length; int packet_num = 1; if (VersionUsesHttp3(version().transport_version)) { mock_quic_data.AddWrite( client_packet_maker().MakeInitialSettingsPacket(packet_num++)); } mock_quic_data.AddWrite(client_packet_maker().MakeRequestHeadersPacket( packet_num++, GetNthClientInitiatedBidirectionalStreamId(0), /*should_include_version=*/true, /*fin=*/true, priority, client_packet_maker().GetRequestHeaders("GET", "https", "/"), /*parent_stream_id=*/0, &spdy_headers_frame_length)); size_t spdy_response_headers_frame_length; mock_quic_data.AddRead(server_packet_maker().MakeResponseHeadersPacket( 1, GetNthClientInitiatedBidirectionalStreamId(0), /*should_include_version=*/false, /*fin=*/true, server_packet_maker().GetResponseHeaders("200"), &spdy_response_headers_frame_length)); mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // No more read data. mock_quic_data.AddSocketDataToFactory(&socket_factory()); // Prepare mock QUIC data for a second session establishment. client_packet_maker().Reset(); MockQuicData mock_quic_data2(version()); packet_num = 1; if (VersionUsesHttp3(version().transport_version)) { mock_quic_data2.AddWrite( client_packet_maker().MakeInitialSettingsPacket(packet_num++)); } mock_quic_data2.AddWrite(client_packet_maker().MakeRequestHeadersPacket( packet_num++, GetNthClientInitiatedBidirectionalStreamId(0), /*should_include_version=*/true, /*fin=*/true, priority, client_packet_maker().GetRequestHeaders("GET", "https", "/"), /*parent_stream_id=*/0, &spdy_headers_frame_length)); mock_quic_data2.AddRead(server_packet_maker().MakeResponseHeadersPacket( 1, GetNthClientInitiatedBidirectionalStreamId(0), /*should_include_version=*/false, /*fin=*/true, server_packet_maker().GetResponseHeaders("200"), &spdy_response_headers_frame_length)); mock_quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // No more read data. mock_quic_data2.AddSocketDataToFactory(&socket_factory()); // Add hanging data for http job. auto hanging_data = std::make_unique(); MockConnect hanging_connect(SYNCHRONOUS, ERR_IO_PENDING); hanging_data->set_connect_data(hanging_connect); socket_factory().AddSocketDataProvider(hanging_data.get()); SSLSocketDataProvider ssl_data(ASYNC, OK); socket_factory().AddSSLSocketDataProvider(&ssl_data); // Set up QUIC as alternative_service. Initialize(); AddQuicAlternativeService(); // Prepare two different tags and corresponding HttpRequestInfos. SocketTag tag1(SocketTag::UNSET_UID, 0x12345678); HttpRequestInfo request_info1; request_info1.method = "GET"; request_info1.url = default_url_; request_info1.load_flags = 0; request_info1.socket_tag = tag1; request_info1.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SocketTag tag2(getuid(), 0x87654321); HttpRequestInfo request_info2 = request_info1; request_info2.socket_tag = tag2; request_info2.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); // Verify one stream with one tag results in one QUIC session. SSLConfig ssl_config; StreamRequestWaiter waiter1; std::unique_ptr request1( session()->http_stream_factory()->RequestStream( request_info1, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter1, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter1.WaitForStream(); EXPECT_TRUE(waiter1.stream_done()); EXPECT_TRUE(nullptr == waiter1.websocket_stream()); ASSERT_TRUE(nullptr != waiter1.stream()); EXPECT_EQ(kProtoQUIC, request1->negotiated_protocol()); EXPECT_EQ(1, GetQuicSessionCount(session())); // Verify socket tagged appropriately. EXPECT_TRUE(tag1 == socket_factory().GetLastProducedUDPSocket()->tag()); EXPECT_TRUE(socket_factory() .GetLastProducedUDPSocket() ->tagged_before_data_transferred()); // Verify one more stream with a different tag results in one more session and // socket. StreamRequestWaiter waiter2; std::unique_ptr request2( session()->http_stream_factory()->RequestStream( request_info2, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter2, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter2.WaitForStream(); EXPECT_TRUE(waiter2.stream_done()); EXPECT_TRUE(nullptr == waiter2.websocket_stream()); ASSERT_TRUE(nullptr != waiter2.stream()); EXPECT_EQ(kProtoQUIC, request2->negotiated_protocol()); EXPECT_EQ(2, GetQuicSessionCount(session())); // Verify socket tagged appropriately. EXPECT_TRUE(tag2 == socket_factory().GetLastProducedUDPSocket()->tag()); EXPECT_TRUE(socket_factory() .GetLastProducedUDPSocket() ->tagged_before_data_transferred()); // Verify one more stream reusing a tag does not create new sessions. StreamRequestWaiter waiter3; std::unique_ptr request3( session()->http_stream_factory()->RequestStream( request_info2, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter3, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter3.WaitForStream(); EXPECT_TRUE(waiter3.stream_done()); EXPECT_TRUE(nullptr == waiter3.websocket_stream()); ASSERT_TRUE(nullptr != waiter3.stream()); EXPECT_EQ(kProtoQUIC, request3->negotiated_protocol()); EXPECT_EQ(2, GetQuicSessionCount(session())); } TEST_F(HttpStreamFactoryTest, ChangeSocketTag) { SpdySessionDependencies session_deps; auto socket_factory = std::make_unique(); auto* socket_factory_ptr = socket_factory.get(); session_deps.socket_factory = std::move(socket_factory); // Prepare for two HTTPS connects. MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING); SequencedSocketData socket_data(base::make_span(&mock_read, 1), base::span()); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); MockRead mock_read2(SYNCHRONOUS, ERR_IO_PENDING); SequencedSocketData socket_data2(base::make_span(&mock_read2, 1), base::span()); socket_data2.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data2); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); // Use cert for *.example.org ssl_socket_data.ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"); ssl_socket_data.next_proto = kProtoHTTP2; session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); SSLSocketDataProvider ssl_socket_data2(ASYNC, OK); // Use cert for *.example.org ssl_socket_data2.ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"); ssl_socket_data2.next_proto = kProtoHTTP2; session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data2); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Prepare two different tags and corresponding HttpRequestInfos. SocketTag tag1(SocketTag::UNSET_UID, 0x12345678); HttpRequestInfo request_info1; request_info1.method = "GET"; request_info1.url = GURL("https://www.example.org"); request_info1.load_flags = 0; request_info1.socket_tag = tag1; request_info1.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SocketTag tag2(getuid(), 0x87654321); HttpRequestInfo request_info2 = request_info1; request_info2.socket_tag = tag2; request_info2.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); // Prepare another HttpRequestInfo with tag1 and a different host name. HttpRequestInfo request_info3 = request_info1; request_info3.url = GURL("https://foo.example.org"); request_info3.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); // Verify one stream with one tag results in one session, group and // socket. SSLConfig ssl_config; StreamRequestWaiter waiter1; std::unique_ptr request1( session->http_stream_factory()->RequestStream( request_info1, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter1, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter1.WaitForStream(); EXPECT_TRUE(waiter1.stream_done()); EXPECT_FALSE(waiter1.websocket_stream()); ASSERT_TRUE(waiter1.stream()); EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 1, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Verify socket tagged appropriately. MockTaggingStreamSocket* socket = socket_factory_ptr->GetLastProducedTCPSocket(); EXPECT_TRUE(tag1 == socket->tag()); EXPECT_TRUE(socket->tagged_before_connected()); // Verify the socket tag on the first session can be changed. StreamRequestWaiter waiter2; std::unique_ptr request2( session->http_stream_factory()->RequestStream( request_info2, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter2, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter2.WaitForStream(); EXPECT_TRUE(waiter2.stream_done()); EXPECT_FALSE(waiter2.websocket_stream()); ASSERT_TRUE(waiter2.stream()); // Verify still have just one session. EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 1, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Verify no new sockets created. EXPECT_EQ(socket, socket_factory_ptr->GetLastProducedTCPSocket()); // Verify socket tag changed. EXPECT_TRUE(tag2 == socket->tag()); EXPECT_FALSE(socket->tagged_before_connected()); // Verify attempting to use the first stream fails because the session's // socket tag has since changed. TestCompletionCallback callback1; waiter1.stream()->RegisterRequest(&request_info1); EXPECT_EQ(ERR_FAILED, waiter1.stream()->InitializeStream( /* can_send_early = */ false, DEFAULT_PRIORITY, NetLogWithSource(), callback1.callback())); // Verify the socket tag can be changed, this time using an IP alias // (different host, same IP). StreamRequestWaiter waiter3; std::unique_ptr request3( session->http_stream_factory()->RequestStream( request_info3, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter3, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter3.WaitForStream(); EXPECT_TRUE(waiter3.stream_done()); EXPECT_FALSE(waiter3.websocket_stream()); ASSERT_TRUE(waiter3.stream()); // Verify still have just one session. EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 1, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Verify no new sockets created. EXPECT_EQ(socket, socket_factory_ptr->GetLastProducedTCPSocket()); // Verify socket tag changed. EXPECT_TRUE(tag1 == socket->tag()); EXPECT_FALSE(socket->tagged_before_connected()); // Initialize the third stream, thus marking the session active, so it cannot // have its socket tag changed. TestCompletionCallback callback3; waiter3.stream()->RegisterRequest(&request_info3); EXPECT_EQ(OK, waiter3.stream()->InitializeStream( /* can_send_early = */ false, DEFAULT_PRIORITY, NetLogWithSource(), callback3.callback())); // Verify a new session is created when a request with a different tag is // started. StreamRequestWaiter waiter4; std::unique_ptr request4( session->http_stream_factory()->RequestStream( request_info2, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter4, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter4.WaitForStream(); EXPECT_TRUE(waiter4.stream_done()); EXPECT_FALSE(waiter4.websocket_stream()); ASSERT_TRUE(waiter4.stream()); // Verify we now have two sessions. EXPECT_EQ(2, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 2, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Verify a new socket was created. MockTaggingStreamSocket* socket2 = socket_factory_ptr->GetLastProducedTCPSocket(); EXPECT_NE(socket, socket2); // Verify tag set appropriately. EXPECT_TRUE(tag2 == socket2->tag()); EXPECT_TRUE(socket2->tagged_before_connected()); // Verify tag on original socket is unchanged. EXPECT_TRUE(tag1 == socket->tag()); waiter3.stream()->Close(/* not_reusable = */ true); } // Regression test for https://crbug.com/954503. TEST_F(HttpStreamFactoryTest, ChangeSocketTagAvoidOverwrite) { SpdySessionDependencies session_deps; auto socket_factory = std::make_unique(); auto* socket_factory_ptr = socket_factory.get(); session_deps.socket_factory = std::move(socket_factory); // Prepare for two HTTPS connects. MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING); SequencedSocketData socket_data(base::make_span(&mock_read, 1), base::span()); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); MockRead mock_read2(SYNCHRONOUS, ERR_IO_PENDING); SequencedSocketData socket_data2(base::make_span(&mock_read2, 1), base::span()); socket_data2.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data2); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); // Use cert for *.example.org ssl_socket_data.ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"); ssl_socket_data.next_proto = kProtoHTTP2; session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); SSLSocketDataProvider ssl_socket_data2(ASYNC, OK); // Use cert for *.example.org ssl_socket_data2.ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"); ssl_socket_data2.next_proto = kProtoHTTP2; session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data2); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Prepare three different tags and corresponding HttpRequestInfos. SocketTag tag1(SocketTag::UNSET_UID, 2); HttpRequestInfo request_info1; request_info1.method = "GET"; request_info1.url = GURL("https://www.example.org"); request_info1.load_flags = 0; request_info1.socket_tag = tag1; request_info1.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); SocketTag tag2(SocketTag::UNSET_UID, 1); HttpRequestInfo request_info2 = request_info1; request_info2.socket_tag = tag2; HttpRequestInfo request_info3 = request_info1; SocketTag tag3(SocketTag::UNSET_UID, 3); request_info3.socket_tag = tag3; // Prepare another HttpRequestInfo with tag3 and a different host name. HttpRequestInfo request_info4 = request_info1; request_info4.socket_tag = tag3; request_info4.url = GURL("https://foo.example.org"); // Verify one stream with one tag results in one session, group and // socket. SSLConfig ssl_config; StreamRequestWaiter waiter1; std::unique_ptr request1( session->http_stream_factory()->RequestStream( request_info1, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter1, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter1.WaitForStream(); EXPECT_TRUE(waiter1.stream_done()); EXPECT_FALSE(waiter1.websocket_stream()); ASSERT_TRUE(waiter1.stream()); EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 1, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Verify socket tagged appropriately. MockTaggingStreamSocket* socket = socket_factory_ptr->GetLastProducedTCPSocket(); EXPECT_TRUE(tag1 == socket->tag()); EXPECT_TRUE(socket->tagged_before_connected()); // Initialize the first stream, thus marking the session active, so it cannot // have its socket tag changed and be reused for the second session. TestCompletionCallback callback1; waiter1.stream()->RegisterRequest(&request_info1); EXPECT_EQ(OK, waiter1.stream()->InitializeStream( /* can_send_early = */ false, DEFAULT_PRIORITY, NetLogWithSource(), callback1.callback())); // Create a second stream with a new tag. StreamRequestWaiter waiter2; std::unique_ptr request2( session->http_stream_factory()->RequestStream( request_info2, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter2, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter2.WaitForStream(); EXPECT_TRUE(waiter2.stream_done()); EXPECT_FALSE(waiter2.websocket_stream()); ASSERT_TRUE(waiter2.stream()); // Verify we now have two sessions. EXPECT_EQ(2, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 2, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Verify a new socket was created. MockTaggingStreamSocket* socket2 = socket_factory_ptr->GetLastProducedTCPSocket(); EXPECT_NE(socket, socket2); // Verify tag set appropriately. EXPECT_TRUE(tag2 == socket2->tag()); EXPECT_TRUE(socket2->tagged_before_connected()); // Verify tag on original socket is unchanged. EXPECT_TRUE(tag1 == socket->tag()); // Initialize the second stream, thus marking the session active, so it cannot // have its socket tag changed and be reused for the third session. TestCompletionCallback callback2; waiter2.stream()->RegisterRequest(&request_info2); EXPECT_EQ(OK, waiter2.stream()->InitializeStream( /* can_send_early = */ false, DEFAULT_PRIORITY, NetLogWithSource(), callback2.callback())); // Release first stream so first session can be retagged for third request. waiter1.stream()->Close(/* not_reusable = */ true); // Verify the first session can be retagged for a third request. StreamRequestWaiter waiter3; std::unique_ptr request3( session->http_stream_factory()->RequestStream( request_info3, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter3, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter3.WaitForStream(); EXPECT_TRUE(waiter3.stream_done()); EXPECT_FALSE(waiter3.websocket_stream()); ASSERT_TRUE(waiter3.stream()); // Verify still have two sessions. EXPECT_EQ(2, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 2, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Verify no new sockets created. EXPECT_EQ(socket2, socket_factory_ptr->GetLastProducedTCPSocket()); // Verify socket tag changed. EXPECT_TRUE(tag3 == socket->tag()); EXPECT_FALSE(socket->tagged_before_connected()); // Release second stream so second session can be retagged for fourth request. waiter2.stream()->Close(/* not_reusable = */ true); // Request a stream with a new tag and a different host that aliases existing // sessions. StreamRequestWaiter waiter4; std::unique_ptr request4( session->http_stream_factory()->RequestStream( request_info4, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter4, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter4.WaitForStream(); EXPECT_TRUE(waiter4.stream_done()); EXPECT_FALSE(waiter4.websocket_stream()); ASSERT_TRUE(waiter4.stream()); // Verify no new sockets created. EXPECT_EQ(socket2, socket_factory_ptr->GetLastProducedTCPSocket()); } #endif // Test that when creating a stream all sessions that alias an IP are tried, // not just one. This is important because there can be multiple sessions // that could satisfy a stream request and they should all be tried. TEST_F(HttpStreamFactoryTest, MultiIPAliases) { SpdySessionDependencies session_deps; // Prepare for two HTTPS connects. MockRead mock_read1(SYNCHRONOUS, ERR_IO_PENDING); SequencedSocketData socket_data1(base::make_span(&mock_read1, 1), base::span()); socket_data1.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data1); MockRead mock_read2(SYNCHRONOUS, ERR_IO_PENDING); SequencedSocketData socket_data2(base::make_span(&mock_read2, 1), base::span()); socket_data2.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data2); SSLSocketDataProvider ssl_socket_data1(ASYNC, OK); // Load cert for *.example.org ssl_socket_data1.ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"); ssl_socket_data1.next_proto = kProtoHTTP2; session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data1); SSLSocketDataProvider ssl_socket_data2(ASYNC, OK); // Load cert for *.example.org ssl_socket_data2.ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"); ssl_socket_data2.next_proto = kProtoHTTP2; session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data2); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Create two HttpRequestInfos, differing only in host name. // Both will resolve to 127.0.0.1 and hence be IP aliases. HttpRequestInfo request_info1; request_info1.method = "GET"; request_info1.url = GURL("https://a.example.org"); request_info1.privacy_mode = PRIVACY_MODE_DISABLED; request_info1.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); HttpRequestInfo request_info1_alias = request_info1; request_info1.url = GURL("https://b.example.org"); // Create two more HttpRequestInfos but with different privacy_mode. HttpRequestInfo request_info2; request_info2.method = "GET"; request_info2.url = GURL("https://a.example.org"); request_info2.privacy_mode = PRIVACY_MODE_ENABLED; request_info2.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); HttpRequestInfo request_info2_alias = request_info2; request_info2.url = GURL("https://b.example.org"); // Open one session. SSLConfig ssl_config; StreamRequestWaiter waiter1; std::unique_ptr request1( session->http_stream_factory()->RequestStream( request_info1, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter1, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter1.WaitForStream(); EXPECT_TRUE(waiter1.stream_done()); EXPECT_FALSE(waiter1.websocket_stream()); ASSERT_TRUE(waiter1.stream()); // Verify just one session created. EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 1, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Open another session to same IP but with different privacy mode. StreamRequestWaiter waiter2; std::unique_ptr request2( session->http_stream_factory()->RequestStream( request_info2, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter2, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter2.WaitForStream(); EXPECT_TRUE(waiter2.stream_done()); EXPECT_FALSE(waiter2.websocket_stream()); ASSERT_TRUE(waiter2.stream()); // Verify two sessions are now open. EXPECT_EQ(2, GetSpdySessionCount(session.get())); EXPECT_EQ( 2, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 2, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Open a third session that IP aliases first session. StreamRequestWaiter waiter3; std::unique_ptr request3( session->http_stream_factory()->RequestStream( request_info1_alias, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter3, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter3.WaitForStream(); EXPECT_TRUE(waiter3.stream_done()); EXPECT_FALSE(waiter3.websocket_stream()); ASSERT_TRUE(waiter3.stream()); // Verify the session pool reused the first session and no new session is // created. This will fail unless the session pool supports multiple // sessions aliasing a single IP. EXPECT_EQ(2, GetSpdySessionCount(session.get())); EXPECT_EQ( 2, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 2, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Open a fourth session that IP aliases the second session. StreamRequestWaiter waiter4; std::unique_ptr request4( session->http_stream_factory()->RequestStream( request_info2_alias, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter4, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter4.WaitForStream(); EXPECT_TRUE(waiter4.stream_done()); EXPECT_FALSE(waiter4.websocket_stream()); ASSERT_TRUE(waiter4.stream()); // Verify the session pool reused the second session. This will fail unless // the session pool supports multiple sessions aliasing a single IP. EXPECT_EQ(2, GetSpdySessionCount(session.get())); EXPECT_EQ( 2, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 2, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); } TEST_F(HttpStreamFactoryTest, SpdyIPPoolingWithDnsAliases) { SpdySessionDependencies session_deps; const std::set kDnsAliasesA({"alias1", "alias2"}); const std::set kDnsAliasesB({"b.com", "b.org", "b.net"}); const std::string kHostnameC("c.example.org"); session_deps.host_resolver->rules()->AddIPLiteralRuleWithDnsAliases( "a.example.org", "127.0.0.1", kDnsAliasesA); session_deps.host_resolver->rules()->AddIPLiteralRuleWithDnsAliases( "b.example.org", "127.0.0.1", kDnsAliasesB); session_deps.host_resolver->rules()->AddIPLiteralRuleWithDnsAliases( "c.example.org", "127.0.0.1", /*dns_aliases=*/std::set()); // Prepare for an HTTPS connect. MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING); SequencedSocketData socket_data(base::make_span(&mock_read, 1), base::span()); socket_data.set_connect_data(MockConnect(ASYNC, OK)); session_deps.socket_factory->AddSocketDataProvider(&socket_data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); // Load cert for *.example.org ssl_socket_data.ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"); ssl_socket_data.next_proto = kProtoHTTP2; session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); std::unique_ptr session( SpdySessionDependencies::SpdyCreateSession(&session_deps)); // Create three HttpRequestInfos, differing only in host name. // All three will resolve to 127.0.0.1 and hence be IP aliases. HttpRequestInfo request_info_a; request_info_a.method = "GET"; request_info_a.url = GURL("https://a.example.org"); request_info_a.privacy_mode = PRIVACY_MODE_DISABLED; request_info_a.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); HttpRequestInfo request_info_b = request_info_a; HttpRequestInfo request_info_c = request_info_a; request_info_b.url = GURL("https://b.example.org"); request_info_c.url = GURL("https://c.example.org"); // Open one session. SSLConfig ssl_config; StreamRequestWaiter waiter1; std::unique_ptr request1( session->http_stream_factory()->RequestStream( request_info_a, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter1, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter1.WaitForStream(); EXPECT_TRUE(waiter1.stream_done()); EXPECT_FALSE(waiter1.websocket_stream()); ASSERT_TRUE(waiter1.stream()); EXPECT_EQ(kDnsAliasesA, waiter1.stream()->GetDnsAliases()); // Verify just one session created. EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 1, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Open a session that IP aliases first session. StreamRequestWaiter waiter2; std::unique_ptr request2( session->http_stream_factory()->RequestStream( request_info_b, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter2, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter2.WaitForStream(); EXPECT_TRUE(waiter2.stream_done()); EXPECT_FALSE(waiter2.websocket_stream()); ASSERT_TRUE(waiter2.stream()); EXPECT_EQ(kDnsAliasesB, waiter2.stream()->GetDnsAliases()); // Verify the session pool reused the first session and no new session is // created. This will fail unless the session pool supports multiple // sessions aliasing a single IP. EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 1, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Open another session that IP aliases the first session. StreamRequestWaiter waiter3; std::unique_ptr request3( session->http_stream_factory()->RequestStream( request_info_c, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter3, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter3.WaitForStream(); EXPECT_TRUE(waiter3.stream_done()); EXPECT_FALSE(waiter3.websocket_stream()); ASSERT_TRUE(waiter3.stream()); EXPECT_THAT(waiter3.stream()->GetDnsAliases(), ElementsAre(kHostnameC)); // Verify the session pool reused the first session and no new session is // created. This will fail unless the session pool supports multiple // sessions aliasing a single IP. EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 1, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Clear host resolver rules to ensure that cached values for DNS aliases // are used. session_deps.host_resolver->rules()->ClearRules(); // Re-request the original resource using `request_info_a`, which had // non-default DNS aliases. std::unique_ptr request4( session->http_stream_factory()->RequestStream( request_info_a, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter1, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter1.WaitForStream(); EXPECT_TRUE(waiter1.stream_done()); EXPECT_FALSE(waiter1.websocket_stream()); ASSERT_TRUE(waiter1.stream()); EXPECT_EQ(kDnsAliasesA, waiter1.stream()->GetDnsAliases()); // Verify the session pool reused the first session and no new session is // created. EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 1, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Re-request a resource using `request_info_b`, which had non-default DNS // aliases. std::unique_ptr request5( session->http_stream_factory()->RequestStream( request_info_b, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter2, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter2.WaitForStream(); EXPECT_TRUE(waiter2.stream_done()); EXPECT_FALSE(waiter2.websocket_stream()); ASSERT_TRUE(waiter2.stream()); EXPECT_EQ(kDnsAliasesB, waiter2.stream()->GetDnsAliases()); // Verify the session pool reused the first session and no new session is // created. This will fail unless the session pool supports multiple // sessions aliasing a single IP. EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 1, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); // Re-request a resource using `request_info_c`, which had only the default // DNS alias (the host name). std::unique_ptr request6( session->http_stream_factory()->RequestStream( request_info_c, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter3, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter3.WaitForStream(); EXPECT_TRUE(waiter3.stream_done()); EXPECT_FALSE(waiter3.websocket_stream()); ASSERT_TRUE(waiter3.stream()); EXPECT_THAT(waiter3.stream()->GetDnsAliases(), ElementsAre(kHostnameC)); // Verify the session pool reused the first session and no new session is // created. This will fail unless the session pool supports multiple // sessions aliasing a single IP. EXPECT_EQ(1, GetSpdySessionCount(session.get())); EXPECT_EQ( 1, GetSocketPoolGroupCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); EXPECT_EQ( 1, GetHandedOutSocketCount(session->GetSocketPool( HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()))); } TEST_P(HttpStreamFactoryBidirectionalQuicTest, QuicIPPoolingWithDnsAliases) { const GURL kUrlA("https://a.example.org"); const GURL kUrlB("https://b.example.org"); const GURL kUrlC("https://c.example.org"); const std::set kDnsAliasesA({"alias1", "alias2"}); const std::set kDnsAliasesB({"b.com", "b.org", "b.net"}); host_resolver()->rules()->AddIPLiteralRuleWithDnsAliases( kUrlA.host(), "127.0.0.1", kDnsAliasesA); host_resolver()->rules()->AddIPLiteralRuleWithDnsAliases( kUrlB.host(), "127.0.0.1", kDnsAliasesB); host_resolver()->rules()->AddIPLiteralRuleWithDnsAliases( kUrlC.host(), "127.0.0.1", /*dns_aliases=*/std::set()); // Prepare mock QUIC data for a first session establishment. MockQuicData mock_quic_data(version()); spdy::SpdyPriority priority = ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY); size_t spdy_headers_frame_length; int packet_num = 1; if (VersionUsesHttp3(version().transport_version)) { mock_quic_data.AddWrite( client_packet_maker().MakeInitialSettingsPacket(packet_num++)); } mock_quic_data.AddWrite(client_packet_maker().MakeRequestHeadersPacket( packet_num++, GetNthClientInitiatedBidirectionalStreamId(0), /*should_include_version=*/true, /*fin=*/true, priority, client_packet_maker().GetRequestHeaders("GET", "https", "/"), /*parent_stream_id=*/0, &spdy_headers_frame_length)); size_t spdy_response_headers_frame_length; mock_quic_data.AddRead(server_packet_maker().MakeResponseHeadersPacket( 1, GetNthClientInitiatedBidirectionalStreamId(0), /*should_include_version=*/false, /*fin=*/true, server_packet_maker().GetResponseHeaders("200"), &spdy_response_headers_frame_length)); mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // No more read data. mock_quic_data.AddSocketDataToFactory(&socket_factory()); // Add hanging data for http job. auto hanging_data = std::make_unique(); MockConnect hanging_connect(SYNCHRONOUS, ERR_IO_PENDING); hanging_data->set_connect_data(hanging_connect); socket_factory().AddSocketDataProvider(hanging_data.get()); SSLSocketDataProvider ssl_data(ASYNC, OK); socket_factory().AddSSLSocketDataProvider(&ssl_data); // Set up QUIC as alternative_service. Initialize(); AddQuicAlternativeService(url::SchemeHostPort(kUrlA), kUrlA.host()); AddQuicAlternativeService(url::SchemeHostPort(kUrlB), kUrlB.host()); AddQuicAlternativeService(url::SchemeHostPort(kUrlC), kUrlC.host()); // Create three HttpRequestInfos, differing only in host name. // All three will resolve to 127.0.0.1 and hence be IP aliases. HttpRequestInfo request_info_a; request_info_a.method = "GET"; request_info_a.url = kUrlA; request_info_a.privacy_mode = PRIVACY_MODE_DISABLED; request_info_a.traffic_annotation = MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); HttpRequestInfo request_info_b = request_info_a; HttpRequestInfo request_info_c = request_info_a; request_info_b.url = kUrlB; request_info_c.url = kUrlC; // Open one session. SSLConfig ssl_config; StreamRequestWaiter waiter1; std::unique_ptr request1( session()->http_stream_factory()->RequestStream( request_info_a, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter1, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter1.WaitForStream(); EXPECT_TRUE(waiter1.stream_done()); EXPECT_FALSE(waiter1.websocket_stream()); ASSERT_TRUE(waiter1.stream()); EXPECT_EQ(kDnsAliasesA, waiter1.stream()->GetDnsAliases()); // Verify just one session created. EXPECT_EQ(1, GetQuicSessionCount(session())); EXPECT_EQ(kProtoQUIC, request1->negotiated_protocol()); // Create a request that will alias and reuse the first session. StreamRequestWaiter waiter2; std::unique_ptr request2( session()->http_stream_factory()->RequestStream( request_info_b, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter2, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter2.WaitForStream(); EXPECT_TRUE(waiter2.stream_done()); EXPECT_FALSE(waiter2.websocket_stream()); ASSERT_TRUE(waiter2.stream()); EXPECT_EQ(kDnsAliasesB, waiter2.stream()->GetDnsAliases()); // Verify the session pool reused the first session and no new session is // created. This will fail unless the session pool supports multiple // sessions aliasing a single IP. EXPECT_EQ(1, GetQuicSessionCount(session())); EXPECT_EQ(kProtoQUIC, request2->negotiated_protocol()); // Create another request that will alias and reuse the first session. StreamRequestWaiter waiter3; std::unique_ptr request3( session()->http_stream_factory()->RequestStream( request_info_c, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter3, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter3.WaitForStream(); EXPECT_TRUE(waiter3.stream_done()); EXPECT_FALSE(waiter3.websocket_stream()); ASSERT_TRUE(waiter3.stream()); EXPECT_THAT(waiter3.stream()->GetDnsAliases(), ElementsAre(kUrlC.host())); // Clear the host resolve rules to ensure that we are using cached info. host_resolver()->rules()->ClearRules(); // Verify the session pool reused the first session and no new session is // created. This will fail unless the session pool supports multiple // sessions aliasing a single IP. EXPECT_EQ(1, GetQuicSessionCount(session())); EXPECT_EQ(kProtoQUIC, request3->negotiated_protocol()); // Create a request that will reuse the first session. std::unique_ptr request4( session()->http_stream_factory()->RequestStream( request_info_a, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter1, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter1.WaitForStream(); EXPECT_TRUE(waiter1.stream_done()); EXPECT_FALSE(waiter1.websocket_stream()); ASSERT_TRUE(waiter1.stream()); EXPECT_EQ(kDnsAliasesA, waiter1.stream()->GetDnsAliases()); // Verify the session pool reused the first session and no new session is // created. EXPECT_EQ(1, GetQuicSessionCount(session())); EXPECT_EQ(kProtoQUIC, request4->negotiated_protocol()); // Create another request that will alias and reuse the first session. std::unique_ptr request5( session()->http_stream_factory()->RequestStream( request_info_b, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter2, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter2.WaitForStream(); EXPECT_TRUE(waiter2.stream_done()); EXPECT_FALSE(waiter2.websocket_stream()); ASSERT_TRUE(waiter2.stream()); EXPECT_EQ(kDnsAliasesB, waiter2.stream()->GetDnsAliases()); // Verify the session pool reused the first session and no new session is // created. This will fail unless the session pool supports multiple // sessions aliasing a single IP. EXPECT_EQ(1, GetQuicSessionCount(session())); EXPECT_EQ(kProtoQUIC, request5->negotiated_protocol()); // Create another request that will alias and reuse the first session. std::unique_ptr request6( session()->http_stream_factory()->RequestStream( request_info_c, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter3, /* enable_ip_based_pooling = */ true, /* enable_alternative_services = */ true, NetLogWithSource())); waiter3.WaitForStream(); EXPECT_TRUE(waiter3.stream_done()); EXPECT_FALSE(waiter3.websocket_stream()); ASSERT_TRUE(waiter3.stream()); EXPECT_THAT(waiter3.stream()->GetDnsAliases(), ElementsAre(kUrlC.host())); // Verify the session pool reused the first session and no new session is // created. This will fail unless the session pool supports multiple // sessions aliasing a single IP. EXPECT_EQ(1, GetQuicSessionCount(session())); EXPECT_EQ(kProtoQUIC, request6->negotiated_protocol()); } class ProcessAlternativeServicesTest : public TestWithTaskEnvironment { public: ProcessAlternativeServicesTest() { session_params_.enable_quic = true; session_context_.proxy_resolution_service = proxy_resolution_service_.get(); session_context_.host_resolver = &host_resolver_; session_context_.cert_verifier = &cert_verifier_; session_context_.transport_security_state = &transport_security_state_; session_context_.client_socket_factory = &socket_factory_; session_context_.ct_policy_enforcer = &ct_policy_enforcer_; session_context_.ssl_config_service = &ssl_config_service_; session_context_.http_server_properties = &http_server_properties_; session_context_.quic_context = &quic_context_; } protected: HttpNetworkSessionParams session_params_; HttpNetworkSessionContext session_context_; std::unique_ptr session_; HttpServerProperties http_server_properties_; QuicContext quic_context_; private: std::unique_ptr proxy_resolution_service_ = ConfiguredProxyResolutionService::CreateDirect(); SSLConfigServiceDefaults ssl_config_service_; MockClientSocketFactory socket_factory_; MockHostResolver host_resolver_; MockCertVerifier cert_verifier_; TransportSecurityState transport_security_state_; DefaultCTPolicyEnforcer ct_policy_enforcer_; }; TEST_F(ProcessAlternativeServicesTest, ProcessEmptyAltSvc) { session_ = std::make_unique(session_params_, session_context_); url::SchemeHostPort origin; NetworkIsolationKey network_isolation_key; auto headers = base::MakeRefCounted(""); session_->http_stream_factory()->ProcessAlternativeServices( session_.get(), network_isolation_key, headers.get(), origin); AlternativeServiceInfoVector alternatives = http_server_properties_.GetAlternativeServiceInfos(origin, network_isolation_key); EXPECT_TRUE(alternatives.empty()); } TEST_F(ProcessAlternativeServicesTest, ProcessAltSvcClear) { session_ = std::make_unique(session_params_, session_context_); url::SchemeHostPort origin(url::kHttpsScheme, "example.com", 443); NetworkIsolationKey network_isolation_key( SchemefulSite(GURL("https://example.com")), SchemefulSite(GURL("https://example.com"))); http_server_properties_.SetAlternativeServices( origin, network_isolation_key, {AlternativeServiceInfo::CreateQuicAlternativeServiceInfo( {kProtoQUIC, "", 443}, base::Time::Now() + base::Seconds(30), quic::AllSupportedVersions())}); EXPECT_FALSE(http_server_properties_ .GetAlternativeServiceInfos(origin, network_isolation_key) .empty()); auto headers = base::MakeRefCounted(""); headers->AddHeader("alt-svc", "clear"); session_->http_stream_factory()->ProcessAlternativeServices( session_.get(), network_isolation_key, headers.get(), origin); AlternativeServiceInfoVector alternatives = http_server_properties_.GetAlternativeServiceInfos(origin, network_isolation_key); EXPECT_TRUE(alternatives.empty()); } TEST_F(ProcessAlternativeServicesTest, ProcessAltSvcQuicIetf) { quic_context_.params()->supported_versions = quic::AllSupportedVersions(); session_ = std::make_unique(session_params_, session_context_); url::SchemeHostPort origin(url::kHttpsScheme, "example.com", 443); NetworkIsolationKey network_isolation_key( SchemefulSite(GURL("https://example.com")), SchemefulSite(GURL("https://example.com"))); auto headers = base::MakeRefCounted(""); headers->AddHeader("alt-svc", "h3-29=\":443\"," "h3-Q050=\":443\"," "h3-Q043=\":443\""); session_->http_stream_factory()->ProcessAlternativeServices( session_.get(), network_isolation_key, headers.get(), origin); quic::ParsedQuicVersionVector versions = { quic::ParsedQuicVersion::Draft29(), quic::ParsedQuicVersion::Q050(), quic::ParsedQuicVersion::Q043(), }; AlternativeServiceInfoVector alternatives = http_server_properties_.GetAlternativeServiceInfos(origin, network_isolation_key); ASSERT_EQ(versions.size(), alternatives.size()); for (size_t i = 0; i < alternatives.size(); ++i) { EXPECT_EQ(kProtoQUIC, alternatives[i].protocol()); EXPECT_EQ(HostPortPair("example.com", 443), alternatives[i].host_port_pair()); EXPECT_EQ(1u, alternatives[i].advertised_versions().size()); EXPECT_EQ(versions[i], alternatives[i].advertised_versions()[0]); } } TEST_F(ProcessAlternativeServicesTest, ProcessAltSvcHttp2) { quic_context_.params()->supported_versions = quic::AllSupportedVersions(); session_ = std::make_unique(session_params_, session_context_); url::SchemeHostPort origin(url::kHttpsScheme, "example.com", 443); NetworkIsolationKey network_isolation_key( SchemefulSite(GURL("https://example.com")), SchemefulSite(GURL("https://example.com"))); auto headers = base::MakeRefCounted(""); headers->AddHeader("alt-svc", "h2=\"other.example.com:443\""); session_->http_stream_factory()->ProcessAlternativeServices( session_.get(), network_isolation_key, headers.get(), origin); AlternativeServiceInfoVector alternatives = http_server_properties_.GetAlternativeServiceInfos(origin, network_isolation_key); ASSERT_EQ(1u, alternatives.size()); EXPECT_EQ(kProtoHTTP2, alternatives[0].protocol()); EXPECT_EQ(HostPortPair("other.example.com", 443), alternatives[0].host_port_pair()); EXPECT_EQ(0u, alternatives[0].advertised_versions().size()); } } // namespace } // namespace net