|  | // 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_proxy_client_socket_pool.h" | 
|  |  | 
|  | #include <map> | 
|  | #include <string> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/callback.h" | 
|  | #include "base/compiler_specific.h" | 
|  | #include "base/metrics/field_trial.h" | 
|  | #include "base/metrics/field_trial_param_associator.h" | 
|  | #include "base/metrics/field_trial_params.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/strings/string_util.h" | 
|  | #include "base/strings/utf_string_conversions.h" | 
|  | #include "base/test/metrics/histogram_tester.h" | 
|  | #include "build/build_config.h" | 
|  | #include "net/base/net_errors.h" | 
|  | #include "net/base/test_completion_callback.h" | 
|  | #include "net/http/http_network_session.h" | 
|  | #include "net/http/http_proxy_client_socket.h" | 
|  | #include "net/http/http_response_headers.h" | 
|  | #include "net/log/net_log_with_source.h" | 
|  | #include "net/nqe/network_quality_estimator_test_util.h" | 
|  | #include "net/socket/client_socket_handle.h" | 
|  | #include "net/socket/next_proto.h" | 
|  | #include "net/socket/socket_tag.h" | 
|  | #include "net/socket/socket_test_util.h" | 
|  | #include "net/spdy/spdy_test_util_common.h" | 
|  | #include "net/test/gtest_util.h" | 
|  | #include "net/test/test_with_scoped_task_environment.h" | 
|  | #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" | 
|  | #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | using net::test::IsError; | 
|  | using net::test::IsOk; | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const int kMaxSockets = 32; | 
|  | const int kMaxSocketsPerGroup = 6; | 
|  | const char * const kAuthHeaders[] = { | 
|  | "proxy-authorization", "Basic Zm9vOmJhcg==" | 
|  | }; | 
|  | const int kAuthHeadersSize = arraysize(kAuthHeaders) / 2; | 
|  |  | 
|  | enum HttpProxyType { | 
|  | HTTP, | 
|  | HTTPS, | 
|  | SPDY | 
|  | }; | 
|  |  | 
|  | const char kHttpProxyHost[] = "httpproxy.example.com"; | 
|  | const char kHttpsProxyHost[] = "httpsproxy.example.com"; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class HttpProxyClientSocketPoolTest | 
|  | : public ::testing::TestWithParam<HttpProxyType>, | 
|  | public WithScopedTaskEnvironment { | 
|  | protected: | 
|  | HttpProxyClientSocketPoolTest() | 
|  | : transport_socket_pool_(kMaxSockets, | 
|  | kMaxSocketsPerGroup, | 
|  | &socket_factory_), | 
|  | ssl_socket_pool_(kMaxSockets, | 
|  | kMaxSocketsPerGroup, | 
|  | session_deps_.cert_verifier.get(), | 
|  | NULL /* channel_id_store */, | 
|  | NULL /* transport_security_state */, | 
|  | NULL /* cert_transparency_verifier */, | 
|  | NULL /* ct_policy_enforcer */, | 
|  | std::string() /* ssl_session_cache_shard */, | 
|  | &socket_factory_, | 
|  | &transport_socket_pool_, | 
|  | NULL, | 
|  | NULL, | 
|  | session_deps_.ssl_config_service.get(), | 
|  | NetLogWithSource().net_log()), | 
|  | #if !defined(STARBOARD) | 
|  | field_trial_list_(nullptr), | 
|  | #endif | 
|  | pool_( | 
|  | std::make_unique<HttpProxyClientSocketPool>(kMaxSockets, | 
|  | kMaxSocketsPerGroup, | 
|  | &transport_socket_pool_, | 
|  | &ssl_socket_pool_, | 
|  | &estimator_, | 
|  | nullptr)) { | 
|  | session_ = CreateNetworkSession(); | 
|  | } | 
|  |  | 
|  | virtual ~HttpProxyClientSocketPoolTest() = default; | 
|  |  | 
|  | // Initializes the field trial paramters for the field trial that determines | 
|  | // connection timeout based on the network quality. | 
|  | void InitAdaptiveTimeoutFieldTrialWithParams( | 
|  | bool use_default_params, | 
|  | int ssl_http_rtt_multiplier, | 
|  | int non_ssl_http_rtt_multiplier, | 
|  | base::TimeDelta min_proxy_connection_timeout, | 
|  | base::TimeDelta max_proxy_connection_timeout) { | 
|  | std::string trial_name = "NetAdaptiveProxyConnectionTimeout"; | 
|  | std::string group_name = "GroupName"; | 
|  |  | 
|  | std::map<std::string, std::string> params; | 
|  | if (!use_default_params) { | 
|  | params["ssl_http_rtt_multiplier"] = | 
|  | base::IntToString(ssl_http_rtt_multiplier); | 
|  | params["non_ssl_http_rtt_multiplier"] = | 
|  | base::IntToString(non_ssl_http_rtt_multiplier); | 
|  | params["min_proxy_connection_timeout_seconds"] = | 
|  | base::IntToString(min_proxy_connection_timeout.InSeconds()); | 
|  | params["max_proxy_connection_timeout_seconds"] = | 
|  | base::IntToString(max_proxy_connection_timeout.InSeconds()); | 
|  | } | 
|  | #if !defined(STARBOARD) | 
|  | base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting(); | 
|  | EXPECT_TRUE( | 
|  | base::AssociateFieldTrialParams(trial_name, group_name, params)); | 
|  | EXPECT_TRUE(base::FieldTrialList::CreateFieldTrial(trial_name, group_name)); | 
|  | #endif | 
|  |  | 
|  | // Reset |pool_| so that the field trial parameters are read by the | 
|  | // |pool_|. | 
|  | pool_ = std::make_unique<HttpProxyClientSocketPool>( | 
|  | kMaxSockets, kMaxSocketsPerGroup, &transport_socket_pool_, | 
|  | &ssl_socket_pool_, &estimator_, NetLogWithSource().net_log()); | 
|  | } | 
|  |  | 
|  | void AddAuthToCache() { | 
|  | const base::string16 kFoo(base::ASCIIToUTF16("foo")); | 
|  | const base::string16 kBar(base::ASCIIToUTF16("bar")); | 
|  | GURL proxy_url(GetParam() == HTTP | 
|  | ? (std::string("http://") + kHttpProxyHost) | 
|  | : (std::string("https://") + kHttpsProxyHost)); | 
|  | session_->http_auth_cache()->Add(proxy_url, | 
|  | "MyRealm1", | 
|  | HttpAuth::AUTH_SCHEME_BASIC, | 
|  | "Basic realm=MyRealm1", | 
|  | AuthCredentials(kFoo, kBar), | 
|  | "/"); | 
|  | } | 
|  |  | 
|  | scoped_refptr<TransportSocketParams> CreateHttpProxyParams() const { | 
|  | if (GetParam() != HTTP) | 
|  | return NULL; | 
|  | return new TransportSocketParams( | 
|  | HostPortPair(kHttpProxyHost, 80), | 
|  | false, | 
|  | OnHostResolutionCallback(), | 
|  | TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT); | 
|  | } | 
|  |  | 
|  | scoped_refptr<SSLSocketParams> CreateHttpsProxyParams() const { | 
|  | if (GetParam() == HTTP) | 
|  | return NULL; | 
|  | return new SSLSocketParams( | 
|  | new TransportSocketParams( | 
|  | HostPortPair(kHttpsProxyHost, 443), false, | 
|  | OnHostResolutionCallback(), | 
|  | TransportSocketParams::COMBINE_CONNECT_AND_WRITE_DEFAULT), | 
|  | NULL, NULL, HostPortPair(kHttpsProxyHost, 443), SSLConfig(), | 
|  | PRIVACY_MODE_DISABLED, 0); | 
|  | } | 
|  |  | 
|  | // Returns the a correctly constructed HttpProxyParms | 
|  | // for the HTTP or HTTPS proxy. | 
|  | scoped_refptr<HttpProxySocketParams> CreateParams(bool tunnel) { | 
|  | return base::MakeRefCounted<HttpProxySocketParams>( | 
|  | CreateHttpProxyParams(), CreateHttpsProxyParams(), | 
|  | quic::QUIC_VERSION_UNSUPPORTED, std::string(), | 
|  | HostPortPair("www.google.com", tunnel ? 443 : 80), | 
|  | session_->http_auth_cache(), session_->http_auth_handler_factory(), | 
|  | session_->spdy_session_pool(), session_->quic_stream_factory(), | 
|  | /*is_trusted_proxy=*/false, tunnel, TRAFFIC_ANNOTATION_FOR_TESTS); | 
|  | } | 
|  |  | 
|  | scoped_refptr<HttpProxySocketParams> CreateTunnelParams() { | 
|  | return CreateParams(true); | 
|  | } | 
|  |  | 
|  | scoped_refptr<HttpProxySocketParams> CreateNoTunnelParams() { | 
|  | return CreateParams(false); | 
|  | } | 
|  |  | 
|  | MockTaggingClientSocketFactory* socket_factory() { return &socket_factory_; } | 
|  |  | 
|  | void Initialize(base::span<const MockRead> reads, | 
|  | base::span<const MockWrite> writes, | 
|  | base::span<const MockRead> spdy_reads, | 
|  | base::span<const MockWrite> spdy_writes) { | 
|  | if (GetParam() == SPDY) { | 
|  | data_.reset(new SequencedSocketData(spdy_reads, spdy_writes)); | 
|  | } else { | 
|  | data_.reset(new SequencedSocketData(reads, writes)); | 
|  | } | 
|  |  | 
|  | data_->set_connect_data(MockConnect(SYNCHRONOUS, OK)); | 
|  |  | 
|  | socket_factory()->AddSocketDataProvider(data_.get()); | 
|  |  | 
|  | if (GetParam() != HTTP) { | 
|  | ssl_data_.reset(new SSLSocketDataProvider(SYNCHRONOUS, OK)); | 
|  | if (GetParam() == SPDY) { | 
|  | InitializeSpdySsl(); | 
|  | } | 
|  | socket_factory()->AddSSLSocketDataProvider(ssl_data_.get()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void InitializeSpdySsl() { ssl_data_->next_proto = kProtoHTTP2; } | 
|  |  | 
|  | std::unique_ptr<HttpNetworkSession> CreateNetworkSession() { | 
|  | return SpdySessionDependencies::SpdyCreateSession(&session_deps_); | 
|  | } | 
|  |  | 
|  | RequestPriority GetLastTransportRequestPriority() const { | 
|  | return transport_socket_pool_.last_request_priority(); | 
|  | } | 
|  |  | 
|  | const base::HistogramTester& histogram_tester() { return histogram_tester_; } | 
|  |  | 
|  | TestNetworkQualityEstimator* estimator() { return &estimator_; } | 
|  |  | 
|  | MockTransportClientSocketPool* transport_socket_pool() { | 
|  | return &transport_socket_pool_; | 
|  | } | 
|  | SSLClientSocketPool* ssl_socket_pool() { return &ssl_socket_pool_; } | 
|  |  | 
|  | private: | 
|  | MockTaggingClientSocketFactory socket_factory_; | 
|  | SpdySessionDependencies session_deps_; | 
|  |  | 
|  | TestNetworkQualityEstimator estimator_; | 
|  |  | 
|  | MockTransportClientSocketPool transport_socket_pool_; | 
|  | MockHostResolver host_resolver_; | 
|  | std::unique_ptr<CertVerifier> cert_verifier_; | 
|  | SSLClientSocketPool ssl_socket_pool_; | 
|  |  | 
|  | std::unique_ptr<HttpNetworkSession> session_; | 
|  |  | 
|  | base::HistogramTester histogram_tester_; | 
|  |  | 
|  | #if !defined(STARBOARD) | 
|  | // Starboard does not support FieldTrial. | 
|  | base::FieldTrialList field_trial_list_; | 
|  | #endif | 
|  |  | 
|  | protected: | 
|  | SpdyTestUtil spdy_util_; | 
|  | std::unique_ptr<SSLSocketDataProvider> ssl_data_; | 
|  | std::unique_ptr<SequencedSocketData> data_; | 
|  | std::unique_ptr<HttpProxyClientSocketPool> pool_; | 
|  | ClientSocketHandle handle_; | 
|  | TestCompletionCallback callback_; | 
|  | }; | 
|  |  | 
|  | // All tests are run with three different proxy types: HTTP, HTTPS (non-SPDY) | 
|  | // and SPDY. | 
|  | INSTANTIATE_TEST_CASE_P(HttpProxyType, | 
|  | HttpProxyClientSocketPoolTest, | 
|  | ::testing::Values(HTTP, HTTPS, SPDY)); | 
|  |  | 
|  | TEST_P(HttpProxyClientSocketPoolTest, NoTunnel) { | 
|  | Initialize(base::span<MockRead>(), base::span<MockWrite>(), | 
|  | base::span<MockRead>(), base::span<MockWrite>()); | 
|  |  | 
|  | int rv = | 
|  | handle_.Init("a", CreateNoTunnelParams(), LOW, SocketTag(), | 
|  | ClientSocketPool::RespectLimits::ENABLED, | 
|  | CompletionOnceCallback(), pool_.get(), NetLogWithSource()); | 
|  | EXPECT_THAT(rv, IsOk()); | 
|  | EXPECT_TRUE(handle_.is_initialized()); | 
|  | ASSERT_TRUE(handle_.socket()); | 
|  | EXPECT_TRUE(handle_.socket()->IsConnected()); | 
|  |  | 
|  | bool is_secure_proxy = GetParam() == HTTPS || GetParam() == SPDY; | 
|  | histogram_tester().ExpectTotalCount( | 
|  | "Net.HttpProxy.ConnectLatency.Insecure.Success", is_secure_proxy ? 0 : 1); | 
|  | histogram_tester().ExpectTotalCount( | 
|  | "Net.HttpProxy.ConnectLatency.Secure.Success", is_secure_proxy ? 1 : 0); | 
|  | } | 
|  |  | 
|  | // Make sure that HttpProxyConnectJob passes on its priority to its | 
|  | // (non-SSL) socket request on Init. | 
|  | TEST_P(HttpProxyClientSocketPoolTest, SetSocketRequestPriorityOnInit) { | 
|  | Initialize(base::span<MockRead>(), base::span<MockWrite>(), | 
|  | base::span<MockRead>(), base::span<MockWrite>()); | 
|  | EXPECT_EQ(OK, handle_.Init("a", CreateNoTunnelParams(), HIGHEST, SocketTag(), | 
|  | ClientSocketPool::RespectLimits::ENABLED, | 
|  | CompletionOnceCallback(), pool_.get(), | 
|  | NetLogWithSource())); | 
|  | EXPECT_EQ(HIGHEST, GetLastTransportRequestPriority()); | 
|  | } | 
|  |  | 
|  | TEST_P(HttpProxyClientSocketPoolTest, NeedAuth) { | 
|  | MockWrite writes[] = { | 
|  | MockWrite(ASYNC, 0, | 
|  | "CONNECT www.google.com:443 HTTP/1.1\r\n" | 
|  | "Host: www.google.com:443\r\n" | 
|  | "Proxy-Connection: keep-alive\r\n\r\n"), | 
|  | }; | 
|  | MockRead reads[] = { | 
|  | // No credentials. | 
|  | MockRead(ASYNC, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"), | 
|  | MockRead(ASYNC, 2, "Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"), | 
|  | MockRead(ASYNC, 3, "Content-Length: 10\r\n\r\n"), | 
|  | MockRead(ASYNC, 4, "0123456789"), | 
|  | }; | 
|  | spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyConnect( | 
|  | NULL, 0, 1, LOW, HostPortPair("www.google.com", 443))); | 
|  | spdy::SpdySerializedFrame rst( | 
|  | spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL)); | 
|  | MockWrite spdy_writes[] = { | 
|  | CreateMockWrite(req, 0, ASYNC), CreateMockWrite(rst, 2, ASYNC), | 
|  | }; | 
|  | spdy::SpdyHeaderBlock resp_block; | 
|  | resp_block[spdy::kHttp2StatusHeader] = "407"; | 
|  | resp_block["proxy-authenticate"] = "Basic realm=\"MyRealm1\""; | 
|  |  | 
|  | spdy::SpdySerializedFrame resp( | 
|  | spdy_util_.ConstructSpdyReply(1, std::move(resp_block))); | 
|  | MockRead spdy_reads[] = {CreateMockRead(resp, 1, ASYNC), | 
|  | MockRead(ASYNC, 0, 3)}; | 
|  |  | 
|  | Initialize(reads, writes, spdy_reads, spdy_writes); | 
|  |  | 
|  | int rv = handle_.Init("a", CreateTunnelParams(), LOW, SocketTag(), | 
|  | ClientSocketPool::RespectLimits::ENABLED, | 
|  | callback_.callback(), pool_.get(), NetLogWithSource()); | 
|  | EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | 
|  | EXPECT_FALSE(handle_.is_initialized()); | 
|  | EXPECT_FALSE(handle_.socket()); | 
|  |  | 
|  | rv = callback_.WaitForResult(); | 
|  | EXPECT_THAT(rv, IsError(ERR_PROXY_AUTH_REQUESTED)); | 
|  | EXPECT_TRUE(handle_.is_initialized()); | 
|  | ASSERT_TRUE(handle_.socket()); | 
|  | ProxyClientSocket* tunnel_socket = | 
|  | static_cast<ProxyClientSocket*>(handle_.socket()); | 
|  | if (GetParam() == SPDY) { | 
|  | EXPECT_TRUE(tunnel_socket->IsConnected()); | 
|  | EXPECT_TRUE(tunnel_socket->IsUsingSpdy()); | 
|  | } else { | 
|  | EXPECT_FALSE(tunnel_socket->IsConnected()); | 
|  | EXPECT_FALSE(tunnel_socket->IsUsingSpdy()); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_P(HttpProxyClientSocketPoolTest, HaveAuth) { | 
|  | // It's pretty much impossible to make the SPDY case behave synchronously | 
|  | // so we skip this test for SPDY | 
|  | if (GetParam() == SPDY) | 
|  | return; | 
|  | std::string proxy_host_port = GetParam() == HTTP | 
|  | ? (kHttpProxyHost + std::string(":80")) | 
|  | : (kHttpsProxyHost + std::string(":443")); | 
|  | std::string request = | 
|  | "CONNECT www.google.com:443 HTTP/1.1\r\n" | 
|  | "Host: www.google.com:443\r\n" | 
|  | "Proxy-Connection: keep-alive\r\n" | 
|  | "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"; | 
|  | MockWrite writes[] = { | 
|  | MockWrite(SYNCHRONOUS, 0, request.c_str()), | 
|  | }; | 
|  | MockRead reads[] = { | 
|  | MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"), | 
|  | }; | 
|  |  | 
|  | Initialize(reads, writes, base::span<MockRead>(), base::span<MockWrite>()); | 
|  | AddAuthToCache(); | 
|  |  | 
|  | int rv = handle_.Init("a", CreateTunnelParams(), LOW, SocketTag(), | 
|  | ClientSocketPool::RespectLimits::ENABLED, | 
|  | callback_.callback(), pool_.get(), NetLogWithSource()); | 
|  | EXPECT_THAT(rv, IsOk()); | 
|  | EXPECT_TRUE(handle_.is_initialized()); | 
|  | ASSERT_TRUE(handle_.socket()); | 
|  | EXPECT_TRUE(handle_.socket()->IsConnected()); | 
|  | } | 
|  |  | 
|  | TEST_P(HttpProxyClientSocketPoolTest, AsyncHaveAuth) { | 
|  | std::string proxy_host_port = GetParam() == HTTP | 
|  | ? (kHttpProxyHost + std::string(":80")) | 
|  | : (kHttpsProxyHost + std::string(":443")); | 
|  | std::string request = | 
|  | "CONNECT www.google.com:443 HTTP/1.1\r\n" | 
|  | "Host: www.google.com:443\r\n" | 
|  | "Proxy-Connection: keep-alive\r\n" | 
|  | "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"; | 
|  | MockWrite writes[] = { | 
|  | MockWrite(ASYNC, 0, request.c_str()), | 
|  | }; | 
|  | MockRead reads[] = { | 
|  | MockRead(ASYNC, 1, "HTTP/1.1 200 Connection Established\r\n\r\n"), | 
|  | }; | 
|  |  | 
|  | spdy::SpdySerializedFrame req( | 
|  | spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW, | 
|  | HostPortPair("www.google.com", 443))); | 
|  | MockWrite spdy_writes[] = {CreateMockWrite(req, 0, ASYNC)}; | 
|  | spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1)); | 
|  | MockRead spdy_reads[] = { | 
|  | CreateMockRead(resp, 1, ASYNC), | 
|  | // Connection stays open. | 
|  | MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2), | 
|  | }; | 
|  |  | 
|  | Initialize(reads, writes, spdy_reads, spdy_writes); | 
|  | AddAuthToCache(); | 
|  |  | 
|  | int rv = handle_.Init("a", CreateTunnelParams(), LOW, SocketTag(), | 
|  | ClientSocketPool::RespectLimits::ENABLED, | 
|  | callback_.callback(), pool_.get(), NetLogWithSource()); | 
|  | EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | 
|  | EXPECT_FALSE(handle_.is_initialized()); | 
|  | EXPECT_FALSE(handle_.socket()); | 
|  |  | 
|  | EXPECT_THAT(callback_.WaitForResult(), IsOk()); | 
|  | EXPECT_TRUE(handle_.is_initialized()); | 
|  | ASSERT_TRUE(handle_.socket()); | 
|  | EXPECT_TRUE(handle_.socket()->IsConnected()); | 
|  | } | 
|  |  | 
|  | // Make sure that HttpProxyConnectJob passes on its priority to its | 
|  | // SPDY session's socket request on Init (if applicable). | 
|  | TEST_P(HttpProxyClientSocketPoolTest, | 
|  | SetSpdySessionSocketRequestPriorityOnInit) { | 
|  | if (GetParam() != SPDY) | 
|  | return; | 
|  |  | 
|  | spdy::SpdySerializedFrame req( | 
|  | spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, MEDIUM, | 
|  | HostPortPair("www.google.com", 443))); | 
|  | MockWrite spdy_writes[] = {CreateMockWrite(req, 0, ASYNC)}; | 
|  | spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1)); | 
|  | MockRead spdy_reads[] = {CreateMockRead(resp, 1, ASYNC), | 
|  | MockRead(ASYNC, 0, 2)}; | 
|  |  | 
|  | Initialize(base::span<MockRead>(), base::span<MockWrite>(), spdy_reads, | 
|  | spdy_writes); | 
|  | AddAuthToCache(); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | ERR_IO_PENDING, | 
|  | handle_.Init("a", CreateTunnelParams(), MEDIUM, SocketTag(), | 
|  | ClientSocketPool::RespectLimits::ENABLED, | 
|  | callback_.callback(), pool_.get(), NetLogWithSource())); | 
|  | EXPECT_EQ(MEDIUM, GetLastTransportRequestPriority()); | 
|  |  | 
|  | EXPECT_THAT(callback_.WaitForResult(), IsOk()); | 
|  | } | 
|  |  | 
|  | TEST_P(HttpProxyClientSocketPoolTest, TCPError) { | 
|  | if (GetParam() == SPDY) | 
|  | return; | 
|  | data_.reset(new SequencedSocketData()); | 
|  | data_->set_connect_data(MockConnect(ASYNC, ERR_CONNECTION_CLOSED)); | 
|  |  | 
|  | socket_factory()->AddSocketDataProvider(data_.get()); | 
|  |  | 
|  | int rv = handle_.Init("a", CreateTunnelParams(), LOW, SocketTag(), | 
|  | ClientSocketPool::RespectLimits::ENABLED, | 
|  | callback_.callback(), pool_.get(), NetLogWithSource()); | 
|  | EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | 
|  | EXPECT_FALSE(handle_.is_initialized()); | 
|  | EXPECT_FALSE(handle_.socket()); | 
|  |  | 
|  | EXPECT_THAT(callback_.WaitForResult(), IsError(ERR_PROXY_CONNECTION_FAILED)); | 
|  |  | 
|  | EXPECT_FALSE(handle_.is_initialized()); | 
|  | EXPECT_FALSE(handle_.socket()); | 
|  |  | 
|  | bool is_secure_proxy = GetParam() == HTTPS; | 
|  | histogram_tester().ExpectTotalCount( | 
|  | "Net.HttpProxy.ConnectLatency.Insecure.Error", is_secure_proxy ? 0 : 1); | 
|  | histogram_tester().ExpectTotalCount( | 
|  | "Net.HttpProxy.ConnectLatency.Secure.Error", is_secure_proxy ? 1 : 0); | 
|  | } | 
|  |  | 
|  | TEST_P(HttpProxyClientSocketPoolTest, SSLError) { | 
|  | if (GetParam() == HTTP) | 
|  | return; | 
|  | data_.reset(new SequencedSocketData()); | 
|  | data_->set_connect_data(MockConnect(ASYNC, OK)); | 
|  | socket_factory()->AddSocketDataProvider(data_.get()); | 
|  |  | 
|  | ssl_data_.reset(new SSLSocketDataProvider(ASYNC, | 
|  | ERR_CERT_AUTHORITY_INVALID)); | 
|  | if (GetParam() == SPDY) { | 
|  | InitializeSpdySsl(); | 
|  | } | 
|  | socket_factory()->AddSSLSocketDataProvider(ssl_data_.get()); | 
|  |  | 
|  | int rv = handle_.Init("a", CreateTunnelParams(), LOW, SocketTag(), | 
|  | ClientSocketPool::RespectLimits::ENABLED, | 
|  | callback_.callback(), pool_.get(), NetLogWithSource()); | 
|  | EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | 
|  | EXPECT_FALSE(handle_.is_initialized()); | 
|  | EXPECT_FALSE(handle_.socket()); | 
|  |  | 
|  | EXPECT_THAT(callback_.WaitForResult(), | 
|  | IsError(ERR_PROXY_CERTIFICATE_INVALID)); | 
|  |  | 
|  | EXPECT_FALSE(handle_.is_initialized()); | 
|  | EXPECT_FALSE(handle_.socket()); | 
|  | histogram_tester().ExpectTotalCount( | 
|  | "Net.HttpProxy.ConnectLatency.Secure.Error", 1); | 
|  | histogram_tester().ExpectTotalCount( | 
|  | "Net.HttpProxy.ConnectLatency.Insecure.Error", 0); | 
|  | } | 
|  |  | 
|  | TEST_P(HttpProxyClientSocketPoolTest, SslClientAuth) { | 
|  | if (GetParam() == HTTP) | 
|  | return; | 
|  | data_.reset(new SequencedSocketData()); | 
|  | data_->set_connect_data(MockConnect(ASYNC, OK)); | 
|  | socket_factory()->AddSocketDataProvider(data_.get()); | 
|  |  | 
|  | ssl_data_.reset(new SSLSocketDataProvider(ASYNC, | 
|  | ERR_SSL_CLIENT_AUTH_CERT_NEEDED)); | 
|  | if (GetParam() == SPDY) { | 
|  | InitializeSpdySsl(); | 
|  | } | 
|  | socket_factory()->AddSSLSocketDataProvider(ssl_data_.get()); | 
|  |  | 
|  | int rv = handle_.Init("a", CreateTunnelParams(), LOW, SocketTag(), | 
|  | ClientSocketPool::RespectLimits::ENABLED, | 
|  | callback_.callback(), pool_.get(), NetLogWithSource()); | 
|  | EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | 
|  | EXPECT_FALSE(handle_.is_initialized()); | 
|  | EXPECT_FALSE(handle_.socket()); | 
|  |  | 
|  | EXPECT_THAT(callback_.WaitForResult(), | 
|  | IsError(ERR_SSL_CLIENT_AUTH_CERT_NEEDED)); | 
|  |  | 
|  | EXPECT_FALSE(handle_.is_initialized()); | 
|  | EXPECT_FALSE(handle_.socket()); | 
|  | histogram_tester().ExpectTotalCount( | 
|  | "Net.HttpProxy.ConnectLatency.Secure.Error", 1); | 
|  | histogram_tester().ExpectTotalCount( | 
|  | "Net.HttpProxy.ConnectLatency.Insecure.Error", 0); | 
|  | } | 
|  |  | 
|  | TEST_P(HttpProxyClientSocketPoolTest, TunnelUnexpectedClose) { | 
|  | MockWrite writes[] = { | 
|  | MockWrite(ASYNC, 0, | 
|  | "CONNECT www.google.com:443 HTTP/1.1\r\n" | 
|  | "Host: www.google.com:443\r\n" | 
|  | "Proxy-Connection: keep-alive\r\n" | 
|  | "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), | 
|  | }; | 
|  | MockRead reads[] = { | 
|  | MockRead(ASYNC, 1, "HTTP/1.1 200 Conn"), | 
|  | MockRead(ASYNC, ERR_CONNECTION_CLOSED, 2), | 
|  | }; | 
|  | spdy::SpdySerializedFrame req( | 
|  | spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW, | 
|  | HostPortPair("www.google.com", 443))); | 
|  | MockWrite spdy_writes[] = {CreateMockWrite(req, 0, ASYNC)}; | 
|  | MockRead spdy_reads[] = { | 
|  | MockRead(ASYNC, ERR_CONNECTION_CLOSED, 1), | 
|  | }; | 
|  |  | 
|  | Initialize(reads, writes, spdy_reads, spdy_writes); | 
|  | AddAuthToCache(); | 
|  |  | 
|  | int rv = handle_.Init("a", CreateTunnelParams(), LOW, SocketTag(), | 
|  | ClientSocketPool::RespectLimits::ENABLED, | 
|  | callback_.callback(), pool_.get(), NetLogWithSource()); | 
|  | EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | 
|  | EXPECT_FALSE(handle_.is_initialized()); | 
|  | EXPECT_FALSE(handle_.socket()); | 
|  |  | 
|  | if (GetParam() == SPDY) { | 
|  | // SPDY cannot process a headers block unless it's complete and so it | 
|  | // returns ERR_CONNECTION_CLOSED in this case. | 
|  | EXPECT_THAT(callback_.WaitForResult(), IsError(ERR_CONNECTION_CLOSED)); | 
|  | } else { | 
|  | EXPECT_THAT(callback_.WaitForResult(), | 
|  | IsError(ERR_RESPONSE_HEADERS_TRUNCATED)); | 
|  | } | 
|  | EXPECT_FALSE(handle_.is_initialized()); | 
|  | EXPECT_FALSE(handle_.socket()); | 
|  | } | 
|  |  | 
|  | TEST_P(HttpProxyClientSocketPoolTest, Tunnel1xxResponse) { | 
|  | // Tests that 1xx responses are rejected for a CONNECT request. | 
|  | if (GetParam() == SPDY) { | 
|  | // SPDY doesn't have 1xx responses. | 
|  | return; | 
|  | } | 
|  |  | 
|  | MockWrite writes[] = { | 
|  | MockWrite(ASYNC, 0, | 
|  | "CONNECT www.google.com:443 HTTP/1.1\r\n" | 
|  | "Host: www.google.com:443\r\n" | 
|  | "Proxy-Connection: keep-alive\r\n\r\n"), | 
|  | }; | 
|  | MockRead reads[] = { | 
|  | MockRead(ASYNC, 1, "HTTP/1.1 100 Continue\r\n\r\n"), | 
|  | MockRead(ASYNC, 2, "HTTP/1.1 200 Connection Established\r\n\r\n"), | 
|  | }; | 
|  |  | 
|  | Initialize(reads, writes, base::span<MockRead>(), base::span<MockWrite>()); | 
|  |  | 
|  | int rv = handle_.Init("a", CreateTunnelParams(), LOW, SocketTag(), | 
|  | ClientSocketPool::RespectLimits::ENABLED, | 
|  | callback_.callback(), pool_.get(), NetLogWithSource()); | 
|  | EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | 
|  | EXPECT_FALSE(handle_.is_initialized()); | 
|  | EXPECT_FALSE(handle_.socket()); | 
|  |  | 
|  | EXPECT_THAT(callback_.WaitForResult(), IsError(ERR_TUNNEL_CONNECTION_FAILED)); | 
|  | } | 
|  |  | 
|  | TEST_P(HttpProxyClientSocketPoolTest, TunnelSetupError) { | 
|  | MockWrite writes[] = { | 
|  | MockWrite(ASYNC, 0, | 
|  | "CONNECT www.google.com:443 HTTP/1.1\r\n" | 
|  | "Host: www.google.com:443\r\n" | 
|  | "Proxy-Connection: keep-alive\r\n" | 
|  | "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), | 
|  | }; | 
|  | MockRead reads[] = { | 
|  | MockRead(ASYNC, 1, "HTTP/1.1 304 Not Modified\r\n\r\n"), | 
|  | }; | 
|  | spdy::SpdySerializedFrame req( | 
|  | spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW, | 
|  | HostPortPair("www.google.com", 443))); | 
|  | spdy::SpdySerializedFrame rst( | 
|  | spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL)); | 
|  | MockWrite spdy_writes[] = { | 
|  | CreateMockWrite(req, 0, ASYNC), CreateMockWrite(rst, 2, ASYNC), | 
|  | }; | 
|  | spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyReplyError(1)); | 
|  | MockRead spdy_reads[] = { | 
|  | CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, 0, 3), | 
|  | }; | 
|  |  | 
|  | Initialize(reads, writes, spdy_reads, spdy_writes); | 
|  | AddAuthToCache(); | 
|  |  | 
|  | int rv = handle_.Init("a", CreateTunnelParams(), LOW, SocketTag(), | 
|  | ClientSocketPool::RespectLimits::ENABLED, | 
|  | callback_.callback(), pool_.get(), NetLogWithSource()); | 
|  | EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | 
|  | EXPECT_FALSE(handle_.is_initialized()); | 
|  | EXPECT_FALSE(handle_.socket()); | 
|  |  | 
|  | rv = callback_.WaitForResult(); | 
|  | // All Proxy CONNECT responses are not trustworthy | 
|  | EXPECT_THAT(rv, IsError(ERR_TUNNEL_CONNECTION_FAILED)); | 
|  | EXPECT_FALSE(handle_.is_initialized()); | 
|  | EXPECT_FALSE(handle_.socket()); | 
|  | } | 
|  |  | 
|  | TEST_P(HttpProxyClientSocketPoolTest, TunnelSetupRedirect) { | 
|  | const std::string redirectTarget = "https://foo.google.com/"; | 
|  |  | 
|  | const std::string responseText = "HTTP/1.1 302 Found\r\n" | 
|  | "Location: " + redirectTarget + "\r\n" | 
|  | "Set-Cookie: foo=bar\r\n" | 
|  | "\r\n"; | 
|  | MockWrite writes[] = { | 
|  | MockWrite(ASYNC, 0, | 
|  | "CONNECT www.google.com:443 HTTP/1.1\r\n" | 
|  | "Host: www.google.com:443\r\n" | 
|  | "Proxy-Connection: keep-alive\r\n" | 
|  | "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), | 
|  | }; | 
|  | MockRead reads[] = { | 
|  | MockRead(ASYNC, 1, responseText.c_str()), | 
|  | }; | 
|  | spdy::SpdySerializedFrame req( | 
|  | spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW, | 
|  | HostPortPair("www.google.com", 443))); | 
|  | spdy::SpdySerializedFrame rst( | 
|  | spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL)); | 
|  |  | 
|  | MockWrite spdy_writes[] = { | 
|  | CreateMockWrite(req, 0, ASYNC), CreateMockWrite(rst, 3, ASYNC), | 
|  | }; | 
|  |  | 
|  | const char* const responseHeaders[] = { | 
|  | "location", redirectTarget.c_str(), | 
|  | "set-cookie", "foo=bar", | 
|  | }; | 
|  | const int responseHeadersSize = arraysize(responseHeaders) / 2; | 
|  | spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyReplyError( | 
|  | "302", responseHeaders, responseHeadersSize, 1)); | 
|  | MockRead spdy_reads[] = { | 
|  | CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, 0, 2), | 
|  | }; | 
|  |  | 
|  | Initialize(reads, writes, spdy_reads, spdy_writes); | 
|  | AddAuthToCache(); | 
|  |  | 
|  | int rv = handle_.Init("a", CreateTunnelParams(), LOW, SocketTag(), | 
|  | ClientSocketPool::RespectLimits::ENABLED, | 
|  | callback_.callback(), pool_.get(), NetLogWithSource()); | 
|  | EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | 
|  | EXPECT_FALSE(handle_.is_initialized()); | 
|  | EXPECT_FALSE(handle_.socket()); | 
|  |  | 
|  | rv = callback_.WaitForResult(); | 
|  |  | 
|  | if (GetParam() == HTTP) { | 
|  | // We don't trust 302 responses to CONNECT from HTTP proxies. | 
|  | EXPECT_THAT(rv, IsError(ERR_TUNNEL_CONNECTION_FAILED)); | 
|  | EXPECT_FALSE(handle_.is_initialized()); | 
|  | EXPECT_FALSE(handle_.socket()); | 
|  | } else { | 
|  | // Expect ProxyClientSocket to return the proxy's response, sanitized. | 
|  | EXPECT_THAT(rv, IsError(ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT)); | 
|  | EXPECT_TRUE(handle_.is_initialized()); | 
|  | ASSERT_TRUE(handle_.socket()); | 
|  |  | 
|  | const ProxyClientSocket* tunnel_socket = | 
|  | static_cast<ProxyClientSocket*>(handle_.socket()); | 
|  | const HttpResponseInfo* response = tunnel_socket->GetConnectResponseInfo(); | 
|  | const HttpResponseHeaders* headers = response->headers.get(); | 
|  |  | 
|  | // Make sure Set-Cookie header was stripped. | 
|  | EXPECT_FALSE(headers->HasHeader("set-cookie")); | 
|  |  | 
|  | // Make sure Content-Length: 0 header was added. | 
|  | EXPECT_TRUE(headers->HasHeaderValue("content-length", "0")); | 
|  |  | 
|  | // Make sure Location header was included and correct. | 
|  | std::string location; | 
|  | EXPECT_TRUE(headers->IsRedirect(&location)); | 
|  | EXPECT_EQ(location, redirectTarget); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_P(HttpProxyClientSocketPoolTest, ProxyPoolMinTimeout) { | 
|  | // Set RTT estimate to a low value. | 
|  | base::TimeDelta rtt_estimate = base::TimeDelta::FromMilliseconds(1); | 
|  | estimator()->SetStartTimeNullHttpRtt(rtt_estimate); | 
|  |  | 
|  | EXPECT_LE(base::TimeDelta(), pool_->ConnectionTimeout()); | 
|  |  | 
|  | // Test against a large value. | 
|  | EXPECT_GE(base::TimeDelta::FromMinutes(10), pool_->ConnectionTimeout()); | 
|  |  | 
|  | #if (defined(OS_ANDROID) || defined(OS_IOS)) | 
|  | EXPECT_EQ(base::TimeDelta::FromSeconds(8), pool_->ConnectionTimeout()); | 
|  | #else | 
|  | EXPECT_EQ(base::TimeDelta::FromSeconds(30), pool_->ConnectionTimeout()); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | TEST_P(HttpProxyClientSocketPoolTest, ProxyPoolMaxTimeout) { | 
|  | // Set RTT estimate to a high value. | 
|  | base::TimeDelta rtt_estimate = base::TimeDelta::FromSeconds(100); | 
|  | estimator()->SetStartTimeNullHttpRtt(rtt_estimate); | 
|  |  | 
|  | EXPECT_LE(base::TimeDelta(), pool_->ConnectionTimeout()); | 
|  |  | 
|  | // Test against a large value. | 
|  | EXPECT_GE(base::TimeDelta::FromMinutes(10), pool_->ConnectionTimeout()); | 
|  |  | 
|  | #if (defined(OS_ANDROID) || defined(OS_IOS)) | 
|  | EXPECT_EQ(base::TimeDelta::FromSeconds(30), pool_->ConnectionTimeout()); | 
|  | #else | 
|  | EXPECT_EQ(base::TimeDelta::FromSeconds(60), pool_->ConnectionTimeout()); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | // Cobalt does not need FieldTrial yet. | 
|  | #if !defined(STARBOARD) | 
|  | // Tests the connection timeout values when the field trial parameters are | 
|  | // specified. | 
|  | TEST_P(HttpProxyClientSocketPoolTest, ProxyPoolTimeoutWithExperiment) { | 
|  | // Timeout should be kMultiplier times the HTTP RTT estimate. | 
|  | const int kMultiplier = 4; | 
|  | const base::TimeDelta kMinTimeout = base::TimeDelta::FromSeconds(8); | 
|  | const base::TimeDelta kMaxTimeout = base::TimeDelta::FromSeconds(20); | 
|  |  | 
|  | InitAdaptiveTimeoutFieldTrialWithParams(false, kMultiplier, kMultiplier, | 
|  | kMinTimeout, kMaxTimeout); | 
|  | EXPECT_LE(base::TimeDelta(), pool_->ConnectionTimeout()); | 
|  |  | 
|  | base::TimeDelta rtt_estimate = base::TimeDelta::FromSeconds(4); | 
|  | estimator()->SetStartTimeNullHttpRtt(rtt_estimate); | 
|  | base::TimeDelta expected_connection_timeout = kMultiplier * rtt_estimate; | 
|  | EXPECT_EQ(expected_connection_timeout, pool_->ConnectionTimeout()); | 
|  |  | 
|  | // Connection timeout should not exceed kMaxTimeout. | 
|  | rtt_estimate = base::TimeDelta::FromSeconds(25); | 
|  | estimator()->SetStartTimeNullHttpRtt(rtt_estimate); | 
|  | EXPECT_EQ(kMaxTimeout, pool_->ConnectionTimeout()); | 
|  |  | 
|  | // Connection timeout should not be less than kMinTimeout. | 
|  | rtt_estimate = base::TimeDelta::FromSeconds(0); | 
|  | estimator()->SetStartTimeNullHttpRtt(rtt_estimate); | 
|  | EXPECT_EQ(kMinTimeout, pool_->ConnectionTimeout()); | 
|  | } | 
|  |  | 
|  | // Tests the connection timeout values when the field trial parameters are | 
|  | // specified. | 
|  | TEST_P(HttpProxyClientSocketPoolTest, | 
|  | ProxyPoolTimeoutWithExperimentDifferentParams) { | 
|  | // Timeout should be kMultiplier times the HTTP RTT estimate. | 
|  | const int kMultiplier = 3; | 
|  | const base::TimeDelta kMinTimeout = base::TimeDelta::FromSeconds(2); | 
|  | const base::TimeDelta kMaxTimeout = base::TimeDelta::FromSeconds(30); | 
|  |  | 
|  | InitAdaptiveTimeoutFieldTrialWithParams(false, kMultiplier, kMultiplier, | 
|  | kMinTimeout, kMaxTimeout); | 
|  | EXPECT_LE(base::TimeDelta(), pool_->ConnectionTimeout()); | 
|  |  | 
|  | base::TimeDelta rtt_estimate = base::TimeDelta::FromSeconds(2); | 
|  | estimator()->SetStartTimeNullHttpRtt(rtt_estimate); | 
|  | EXPECT_EQ(kMultiplier * rtt_estimate, pool_->ConnectionTimeout()); | 
|  |  | 
|  | // A change in RTT estimate should also change the connection timeout. | 
|  | rtt_estimate = base::TimeDelta::FromSeconds(7); | 
|  | estimator()->SetStartTimeNullHttpRtt(rtt_estimate); | 
|  | EXPECT_EQ(kMultiplier * rtt_estimate, pool_->ConnectionTimeout()); | 
|  |  | 
|  | // Connection timeout should not exceed kMaxTimeout. | 
|  | rtt_estimate = base::TimeDelta::FromSeconds(35); | 
|  | estimator()->SetStartTimeNullHttpRtt(rtt_estimate); | 
|  | EXPECT_EQ(kMaxTimeout, pool_->ConnectionTimeout()); | 
|  |  | 
|  | // Connection timeout should not be less than kMinTimeout. | 
|  | rtt_estimate = base::TimeDelta::FromSeconds(0); | 
|  | estimator()->SetStartTimeNullHttpRtt(rtt_estimate); | 
|  | EXPECT_EQ(kMinTimeout, pool_->ConnectionTimeout()); | 
|  | } | 
|  |  | 
|  | TEST_P(HttpProxyClientSocketPoolTest, ProxyPoolTimeoutWithConnectionProperty) { | 
|  | const int kSecureMultiplier = 3; | 
|  | const int kNonSecureMultiplier = 5; | 
|  | const base::TimeDelta kMinTimeout = base::TimeDelta::FromSeconds(2); | 
|  | const base::TimeDelta kMaxTimeout = base::TimeDelta::FromSeconds(30); | 
|  |  | 
|  | InitAdaptiveTimeoutFieldTrialWithParams( | 
|  | false, kSecureMultiplier, kNonSecureMultiplier, kMinTimeout, kMaxTimeout); | 
|  |  | 
|  | HttpProxyClientSocketPool::HttpProxyConnectJobFactory job_factory( | 
|  | transport_socket_pool(), ssl_socket_pool(), estimator(), nullptr); | 
|  |  | 
|  | const base::TimeDelta kRttEstimate = base::TimeDelta::FromSeconds(2); | 
|  | estimator()->SetStartTimeNullHttpRtt(kRttEstimate); | 
|  | // By default, connection timeout should return the timeout for secure | 
|  | // proxies. | 
|  | EXPECT_EQ(kSecureMultiplier * kRttEstimate, job_factory.ConnectionTimeout()); | 
|  | EXPECT_EQ(kSecureMultiplier * kRttEstimate, | 
|  | job_factory.ConnectionTimeoutWithConnectionProperty(true)); | 
|  | EXPECT_EQ(kNonSecureMultiplier * kRttEstimate, | 
|  | job_factory.ConnectionTimeoutWithConnectionProperty(false)); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | // Tests the connection timeout values when the field trial parameters are not | 
|  | // specified. | 
|  | TEST_P(HttpProxyClientSocketPoolTest, | 
|  | ProxyPoolTimeoutWithExperimentDefaultParams) { | 
|  | InitAdaptiveTimeoutFieldTrialWithParams(true, 0, 0, base::TimeDelta(), | 
|  | base::TimeDelta()); | 
|  | EXPECT_LE(base::TimeDelta(), pool_->ConnectionTimeout()); | 
|  |  | 
|  | // Timeout should be |http_rtt_multiplier| times the HTTP RTT | 
|  | // estimate. | 
|  | base::TimeDelta rtt_estimate = base::TimeDelta::FromMilliseconds(10); | 
|  | estimator()->SetStartTimeNullHttpRtt(rtt_estimate); | 
|  | // Connection timeout should not be less than the HTTP RTT estimate. | 
|  | EXPECT_LE(rtt_estimate, pool_->ConnectionTimeout()); | 
|  |  | 
|  | // A change in RTT estimate should also change the connection timeout. | 
|  | rtt_estimate = base::TimeDelta::FromSeconds(10); | 
|  | estimator()->SetStartTimeNullHttpRtt(rtt_estimate); | 
|  | // Connection timeout should not be less than the HTTP RTT estimate. | 
|  | EXPECT_LE(rtt_estimate, pool_->ConnectionTimeout()); | 
|  |  | 
|  | // Set RTT to a very large value. | 
|  | rtt_estimate = base::TimeDelta::FromMinutes(60); | 
|  | estimator()->SetStartTimeNullHttpRtt(rtt_estimate); | 
|  | EXPECT_GT(rtt_estimate, pool_->ConnectionTimeout()); | 
|  |  | 
|  | // Set RTT to a very small value. | 
|  | rtt_estimate = base::TimeDelta::FromSeconds(0); | 
|  | estimator()->SetStartTimeNullHttpRtt(rtt_estimate); | 
|  | EXPECT_LT(rtt_estimate, pool_->ConnectionTimeout()); | 
|  | } | 
|  |  | 
|  | // It would be nice to also test the timeouts in HttpProxyClientSocketPool. | 
|  |  | 
|  | // Test that SocketTag passed into HttpProxyClientSocketPool is applied to | 
|  | // returned underlying TCP sockets. | 
|  | #if defined(OS_ANDROID) | 
|  | TEST_P(HttpProxyClientSocketPoolTest, Tag) { | 
|  | Initialize(base::span<MockRead>(), base::span<MockWrite>(), | 
|  | base::span<MockRead>(), base::span<MockWrite>()); | 
|  | SocketTag tag1(SocketTag::UNSET_UID, 0x12345678); | 
|  | SocketTag tag2(getuid(), 0x87654321); | 
|  |  | 
|  | // Verify requested socket is tagged properly. | 
|  | int rv = | 
|  | handle_.Init("a", CreateNoTunnelParams(), LOW, tag1, | 
|  | ClientSocketPool::RespectLimits::ENABLED, | 
|  | CompletionOnceCallback(), pool_.get(), NetLogWithSource()); | 
|  | EXPECT_THAT(rv, IsOk()); | 
|  | EXPECT_TRUE(handle_.is_initialized()); | 
|  | ASSERT_TRUE(handle_.socket()); | 
|  | EXPECT_TRUE(handle_.socket()->IsConnected()); | 
|  | EXPECT_EQ(socket_factory()->GetLastProducedTCPSocket()->tag(), tag1); | 
|  | EXPECT_TRUE( | 
|  | socket_factory()->GetLastProducedTCPSocket()->tagged_before_connected()); | 
|  |  | 
|  | // Verify reused socket is retagged properly. | 
|  | StreamSocket* socket = handle_.socket(); | 
|  | handle_.Reset(); | 
|  | rv = handle_.Init("a", CreateNoTunnelParams(), LOW, tag2, | 
|  | ClientSocketPool::RespectLimits::ENABLED, | 
|  | CompletionOnceCallback(), pool_.get(), NetLogWithSource()); | 
|  | EXPECT_THAT(rv, IsOk()); | 
|  | EXPECT_TRUE(handle_.socket()); | 
|  | EXPECT_TRUE(handle_.socket()->IsConnected()); | 
|  | EXPECT_EQ(handle_.socket(), socket); | 
|  | EXPECT_EQ(socket_factory()->GetLastProducedTCPSocket()->tag(), tag2); | 
|  | handle_.socket()->Disconnect(); | 
|  | handle_.Reset(); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | }  // namespace net |