6077 lines
255 KiB
C++
6077 lines
255 KiB
C++
// Copyright 2012 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "base/check_op.h"
|
|
#include "base/functional/bind.h"
|
|
#include "base/functional/callback.h"
|
|
#include "base/functional/callback_helpers.h"
|
|
#include "base/location.h"
|
|
#include "base/memory/raw_ptr.h"
|
|
#include "base/memory/ref_counted.h"
|
|
#include "base/memory/weak_ptr.h"
|
|
#include "base/notreached.h"
|
|
#include "base/run_loop.h"
|
|
#include "base/strings/string_number_conversions.h"
|
|
#include "base/strings/stringprintf.h"
|
|
#include "base/task/single_thread_task_runner.h"
|
|
#include "base/test/scoped_feature_list.h"
|
|
#include "base/threading/platform_thread.h"
|
|
#include "base/time/time.h"
|
|
#include "base/values.h"
|
|
#include "net/base/features.h"
|
|
#include "net/base/host_port_pair.h"
|
|
#include "net/base/load_timing_info.h"
|
|
#include "net/base/load_timing_info_test_util.h"
|
|
#include "net/base/net_errors.h"
|
|
#include "net/base/network_anonymization_key.h"
|
|
#include "net/base/privacy_mode.h"
|
|
#include "net/base/proxy_server.h"
|
|
#include "net/base/proxy_string_util.h"
|
|
#include "net/base/request_priority.h"
|
|
#include "net/base/schemeful_site.h"
|
|
#include "net/base/test_completion_callback.h"
|
|
#include "net/dns/public/resolve_error_info.h"
|
|
#include "net/dns/public/secure_dns_policy.h"
|
|
#include "net/http/http_response_headers.h"
|
|
#include "net/http/http_response_info.h"
|
|
#include "net/log/net_log.h"
|
|
#include "net/log/net_log_event_type.h"
|
|
#include "net/log/net_log_source.h"
|
|
#include "net/log/net_log_source_type.h"
|
|
#include "net/log/test_net_log.h"
|
|
#include "net/log/test_net_log_util.h"
|
|
#include "net/socket/client_socket_factory.h"
|
|
#include "net/socket/client_socket_handle.h"
|
|
#include "net/socket/connect_job_factory.h"
|
|
#include "net/socket/datagram_client_socket.h"
|
|
#include "net/socket/socket_performance_watcher.h"
|
|
#include "net/socket/socket_tag.h"
|
|
#include "net/socket/socket_test_util.h"
|
|
#include "net/socket/ssl_client_socket.h"
|
|
#include "net/socket/stream_socket.h"
|
|
#include "net/socket/transport_client_socket_pool.h"
|
|
#include "net/socket/transport_connect_job.h"
|
|
#include "net/ssl/ssl_cert_request_info.h"
|
|
#include "net/test/gtest_util.h"
|
|
#include "net/test/test_with_task_environment.h"
|
|
#include "net/traffic_annotation/network_traffic_annotation.h"
|
|
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
#include "third_party/abseil-cpp/absl/types/optional.h"
|
|
#include "url/scheme_host_port.h"
|
|
#include "url/url_constants.h"
|
|
|
|
using net::test::IsError;
|
|
using net::test::IsOk;
|
|
|
|
using ::testing::Invoke;
|
|
using ::testing::Return;
|
|
|
|
namespace net {
|
|
|
|
namespace {
|
|
|
|
const int kDefaultMaxSockets = 4;
|
|
const int kDefaultMaxSocketsPerGroup = 2;
|
|
constexpr base::TimeDelta kUnusedIdleSocketTimeout = base::Seconds(10);
|
|
|
|
ClientSocketPool::GroupId TestGroupId(
|
|
base::StringPiece host,
|
|
int port = 80,
|
|
base::StringPiece scheme = url::kHttpScheme,
|
|
PrivacyMode privacy_mode = PrivacyMode::PRIVACY_MODE_DISABLED,
|
|
NetworkAnonymizationKey network_anonymization_key =
|
|
NetworkAnonymizationKey()) {
|
|
return ClientSocketPool::GroupId(url::SchemeHostPort(scheme, host, port),
|
|
privacy_mode, network_anonymization_key,
|
|
SecureDnsPolicy::kAllow);
|
|
}
|
|
|
|
// Make sure |handle| sets load times correctly when it has been assigned a
|
|
// reused socket.
|
|
void TestLoadTimingInfoConnectedReused(const ClientSocketHandle& handle) {
|
|
LoadTimingInfo load_timing_info;
|
|
// Only pass true in as |is_reused|, as in general, HttpStream types should
|
|
// have stricter concepts of reuse than socket pools.
|
|
EXPECT_TRUE(handle.GetLoadTimingInfo(true, &load_timing_info));
|
|
|
|
EXPECT_EQ(true, load_timing_info.socket_reused);
|
|
EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id);
|
|
|
|
ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing);
|
|
ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info);
|
|
}
|
|
|
|
// Make sure |handle| sets load times correctly when it has been assigned a
|
|
// fresh socket. Also runs TestLoadTimingInfoConnectedReused, since the owner
|
|
// of a connection where |is_reused| is false may consider the connection
|
|
// reused.
|
|
void TestLoadTimingInfoConnectedNotReused(const ClientSocketHandle& handle) {
|
|
EXPECT_FALSE(handle.is_reused());
|
|
|
|
LoadTimingInfo load_timing_info;
|
|
EXPECT_TRUE(handle.GetLoadTimingInfo(false, &load_timing_info));
|
|
|
|
EXPECT_FALSE(load_timing_info.socket_reused);
|
|
EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id);
|
|
|
|
ExpectConnectTimingHasTimes(load_timing_info.connect_timing,
|
|
CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY);
|
|
ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info);
|
|
|
|
TestLoadTimingInfoConnectedReused(handle);
|
|
}
|
|
|
|
// Make sure |handle| sets load times correctly, in the case that it does not
|
|
// currently have a socket.
|
|
void TestLoadTimingInfoNotConnected(const ClientSocketHandle& handle) {
|
|
// Should only be set to true once a socket is assigned, if at all.
|
|
EXPECT_FALSE(handle.is_reused());
|
|
|
|
LoadTimingInfo load_timing_info;
|
|
EXPECT_FALSE(handle.GetLoadTimingInfo(false, &load_timing_info));
|
|
|
|
EXPECT_FALSE(load_timing_info.socket_reused);
|
|
EXPECT_EQ(NetLogSource::kInvalidId, load_timing_info.socket_log_id);
|
|
|
|
ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing);
|
|
ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info);
|
|
}
|
|
|
|
class MockClientSocket : public StreamSocket {
|
|
public:
|
|
explicit MockClientSocket(net::NetLog* net_log)
|
|
: net_log_(NetLogWithSource::Make(net_log, NetLogSourceType::SOCKET)) {}
|
|
|
|
MockClientSocket(const MockClientSocket&) = delete;
|
|
MockClientSocket& operator=(const MockClientSocket&) = delete;
|
|
|
|
// Sets whether the socket has unread data. If true, the next call to Read()
|
|
// will return 1 byte and IsConnectedAndIdle() will return false.
|
|
void set_has_unread_data(bool has_unread_data) {
|
|
has_unread_data_ = has_unread_data;
|
|
}
|
|
|
|
// Socket implementation.
|
|
int Read(IOBuffer* /* buf */,
|
|
int len,
|
|
CompletionOnceCallback /* callback */) override {
|
|
if (has_unread_data_ && len > 0) {
|
|
has_unread_data_ = false;
|
|
was_used_to_convey_data_ = true;
|
|
return 1;
|
|
}
|
|
return ERR_UNEXPECTED;
|
|
}
|
|
|
|
int Write(
|
|
IOBuffer* /* buf */,
|
|
int len,
|
|
CompletionOnceCallback /* callback */,
|
|
const NetworkTrafficAnnotationTag& /*traffic_annotation*/) override {
|
|
was_used_to_convey_data_ = true;
|
|
return len;
|
|
}
|
|
int SetReceiveBufferSize(int32_t size) override { return OK; }
|
|
int SetSendBufferSize(int32_t size) override { return OK; }
|
|
|
|
// StreamSocket implementation.
|
|
int Connect(CompletionOnceCallback callback) override {
|
|
connected_ = true;
|
|
return OK;
|
|
}
|
|
|
|
void Disconnect() override { connected_ = false; }
|
|
bool IsConnected() const override { return connected_; }
|
|
bool IsConnectedAndIdle() const override {
|
|
return connected_ && !has_unread_data_;
|
|
}
|
|
|
|
int GetPeerAddress(IPEndPoint* /* address */) const override {
|
|
return ERR_UNEXPECTED;
|
|
}
|
|
|
|
int GetLocalAddress(IPEndPoint* /* address */) const override {
|
|
return ERR_UNEXPECTED;
|
|
}
|
|
|
|
const NetLogWithSource& NetLog() const override { return net_log_; }
|
|
|
|
bool WasEverUsed() const override { return was_used_to_convey_data_; }
|
|
bool WasAlpnNegotiated() const override { return false; }
|
|
NextProto GetNegotiatedProtocol() const override { return kProtoUnknown; }
|
|
bool GetSSLInfo(SSLInfo* ssl_info) override { return false; }
|
|
int64_t GetTotalReceivedBytes() const override {
|
|
NOTIMPLEMENTED();
|
|
return 0;
|
|
}
|
|
void ApplySocketTag(const SocketTag& tag) override {}
|
|
|
|
private:
|
|
bool connected_ = false;
|
|
bool has_unread_data_ = false;
|
|
NetLogWithSource net_log_;
|
|
bool was_used_to_convey_data_ = false;
|
|
};
|
|
|
|
class TestConnectJob;
|
|
|
|
class MockClientSocketFactory : public ClientSocketFactory {
|
|
public:
|
|
MockClientSocketFactory() = default;
|
|
|
|
std::unique_ptr<DatagramClientSocket> CreateDatagramClientSocket(
|
|
DatagramSocket::BindType bind_type,
|
|
NetLog* net_log,
|
|
const NetLogSource& source) override {
|
|
NOTREACHED();
|
|
return nullptr;
|
|
}
|
|
|
|
std::unique_ptr<TransportClientSocket> CreateTransportClientSocket(
|
|
const AddressList& addresses,
|
|
std::unique_ptr<
|
|
SocketPerformanceWatcher> /* socket_performance_watcher */,
|
|
NetworkQualityEstimator* /* network_quality_estimator */,
|
|
NetLog* /* net_log */,
|
|
const NetLogSource& /*source*/) override {
|
|
allocation_count_++;
|
|
return nullptr;
|
|
}
|
|
|
|
std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
|
|
SSLClientContext* context,
|
|
std::unique_ptr<StreamSocket> stream_socket,
|
|
const HostPortPair& host_and_port,
|
|
const SSLConfig& ssl_config) override {
|
|
NOTIMPLEMENTED();
|
|
return nullptr;
|
|
}
|
|
|
|
void WaitForSignal(TestConnectJob* job) { waiting_jobs_.push_back(job); }
|
|
|
|
void SignalJobs();
|
|
|
|
void SignalJob(size_t job);
|
|
|
|
void SetJobLoadState(size_t job, LoadState load_state);
|
|
|
|
// Sets the HasConnectionEstablished value of the specified job to true,
|
|
// without invoking the callback.
|
|
void SetJobHasEstablishedConnection(size_t job);
|
|
|
|
int allocation_count() const { return allocation_count_; }
|
|
|
|
private:
|
|
int allocation_count_ = 0;
|
|
std::vector<TestConnectJob*> waiting_jobs_;
|
|
};
|
|
|
|
class TestConnectJob : public ConnectJob {
|
|
public:
|
|
enum JobType {
|
|
kMockJob,
|
|
kMockFailingJob,
|
|
kMockPendingJob,
|
|
kMockPendingFailingJob,
|
|
kMockWaitingJob,
|
|
|
|
// Certificate errors return a socket in addition to an error code.
|
|
kMockCertErrorJob,
|
|
kMockPendingCertErrorJob,
|
|
|
|
kMockAdditionalErrorStateJob,
|
|
kMockPendingAdditionalErrorStateJob,
|
|
kMockUnreadDataJob,
|
|
|
|
kMockAuthChallengeOnceJob,
|
|
kMockAuthChallengeTwiceJob,
|
|
kMockAuthChallengeOnceFailingJob,
|
|
kMockAuthChallengeTwiceFailingJob,
|
|
};
|
|
|
|
// The kMockPendingJob uses a slight delay before allowing the connect
|
|
// to complete.
|
|
static const int kPendingConnectDelay = 2;
|
|
|
|
TestConnectJob(JobType job_type,
|
|
RequestPriority request_priority,
|
|
SocketTag socket_tag,
|
|
base::TimeDelta timeout_duration,
|
|
const CommonConnectJobParams* common_connect_job_params,
|
|
ConnectJob::Delegate* delegate,
|
|
MockClientSocketFactory* client_socket_factory)
|
|
: ConnectJob(request_priority,
|
|
socket_tag,
|
|
timeout_duration,
|
|
common_connect_job_params,
|
|
delegate,
|
|
nullptr /* net_log */,
|
|
NetLogSourceType::TRANSPORT_CONNECT_JOB,
|
|
NetLogEventType::TRANSPORT_CONNECT_JOB_CONNECT),
|
|
job_type_(job_type),
|
|
client_socket_factory_(client_socket_factory) {}
|
|
|
|
TestConnectJob(const TestConnectJob&) = delete;
|
|
TestConnectJob& operator=(const TestConnectJob&) = delete;
|
|
|
|
void Signal() {
|
|
DoConnect(waiting_success_, true /* async */, false /* recoverable */);
|
|
}
|
|
|
|
void set_load_state(LoadState load_state) { load_state_ = load_state; }
|
|
|
|
void set_has_established_connection() {
|
|
DCHECK(!has_established_connection_);
|
|
has_established_connection_ = true;
|
|
}
|
|
|
|
// From ConnectJob:
|
|
|
|
LoadState GetLoadState() const override { return load_state_; }
|
|
|
|
bool HasEstablishedConnection() const override {
|
|
return has_established_connection_;
|
|
}
|
|
|
|
ResolveErrorInfo GetResolveErrorInfo() const override {
|
|
return ResolveErrorInfo(OK);
|
|
}
|
|
|
|
bool IsSSLError() const override { return store_additional_error_state_; }
|
|
|
|
scoped_refptr<SSLCertRequestInfo> GetCertRequestInfo() override {
|
|
if (store_additional_error_state_)
|
|
return base::MakeRefCounted<SSLCertRequestInfo>();
|
|
return nullptr;
|
|
}
|
|
|
|
private:
|
|
// From ConnectJob:
|
|
|
|
int ConnectInternal() override {
|
|
AddressList ignored;
|
|
client_socket_factory_->CreateTransportClientSocket(
|
|
ignored, nullptr, nullptr, nullptr, NetLogSource());
|
|
switch (job_type_) {
|
|
case kMockJob:
|
|
return DoConnect(true /* successful */, false /* sync */,
|
|
false /* cert_error */);
|
|
case kMockFailingJob:
|
|
return DoConnect(false /* error */, false /* sync */,
|
|
false /* cert_error */);
|
|
case kMockPendingJob:
|
|
set_load_state(LOAD_STATE_CONNECTING);
|
|
|
|
// Depending on execution timings, posting a delayed task can result
|
|
// in the task getting executed the at the earliest possible
|
|
// opportunity or only after returning once from the message loop and
|
|
// then a second call into the message loop. In order to make behavior
|
|
// more deterministic, we change the default delay to 2ms. This should
|
|
// always require us to wait for the second call into the message loop.
|
|
//
|
|
// N.B. The correct fix for this and similar timing problems is to
|
|
// abstract time for the purpose of unittests. Unfortunately, we have
|
|
// a lot of third-party components that directly call the various
|
|
// time functions, so this change would be rather invasive.
|
|
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
|
|
FROM_HERE,
|
|
base::BindOnce(base::IgnoreResult(&TestConnectJob::DoConnect),
|
|
weak_factory_.GetWeakPtr(), true /* successful */,
|
|
true /* async */, false /* cert_error */),
|
|
base::Milliseconds(kPendingConnectDelay));
|
|
return ERR_IO_PENDING;
|
|
case kMockPendingFailingJob:
|
|
set_load_state(LOAD_STATE_CONNECTING);
|
|
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
|
|
FROM_HERE,
|
|
base::BindOnce(base::IgnoreResult(&TestConnectJob::DoConnect),
|
|
weak_factory_.GetWeakPtr(), false /* error */,
|
|
true /* async */, false /* cert_error */),
|
|
base::Milliseconds(2));
|
|
return ERR_IO_PENDING;
|
|
case kMockWaitingJob:
|
|
set_load_state(LOAD_STATE_CONNECTING);
|
|
client_socket_factory_->WaitForSignal(this);
|
|
waiting_success_ = true;
|
|
return ERR_IO_PENDING;
|
|
case kMockCertErrorJob:
|
|
return DoConnect(false /* error */, false /* sync */,
|
|
true /* cert_error */);
|
|
case kMockPendingCertErrorJob:
|
|
set_load_state(LOAD_STATE_CONNECTING);
|
|
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
|
|
FROM_HERE,
|
|
base::BindOnce(base::IgnoreResult(&TestConnectJob::DoConnect),
|
|
weak_factory_.GetWeakPtr(), false /* error */,
|
|
true /* async */, true /* cert_error */),
|
|
base::Milliseconds(2));
|
|
return ERR_IO_PENDING;
|
|
case kMockAdditionalErrorStateJob:
|
|
store_additional_error_state_ = true;
|
|
return DoConnect(false /* error */, false /* sync */,
|
|
false /* cert_error */);
|
|
case kMockPendingAdditionalErrorStateJob:
|
|
set_load_state(LOAD_STATE_CONNECTING);
|
|
store_additional_error_state_ = true;
|
|
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
|
|
FROM_HERE,
|
|
base::BindOnce(base::IgnoreResult(&TestConnectJob::DoConnect),
|
|
weak_factory_.GetWeakPtr(), false /* error */,
|
|
true /* async */, false /* cert_error */),
|
|
base::Milliseconds(2));
|
|
return ERR_IO_PENDING;
|
|
case kMockUnreadDataJob: {
|
|
int ret = DoConnect(true /* successful */, false /* sync */,
|
|
false /* cert_error */);
|
|
static_cast<MockClientSocket*>(socket())->set_has_unread_data(true);
|
|
return ret;
|
|
}
|
|
case kMockAuthChallengeOnceJob:
|
|
set_load_state(LOAD_STATE_CONNECTING);
|
|
DoAdvanceAuthChallenge(1, true /* succeed_after_last_challenge */);
|
|
return ERR_IO_PENDING;
|
|
case kMockAuthChallengeTwiceJob:
|
|
set_load_state(LOAD_STATE_CONNECTING);
|
|
DoAdvanceAuthChallenge(2, true /* succeed_after_last_challenge */);
|
|
return ERR_IO_PENDING;
|
|
case kMockAuthChallengeOnceFailingJob:
|
|
set_load_state(LOAD_STATE_CONNECTING);
|
|
DoAdvanceAuthChallenge(1, false /* succeed_after_last_challenge */);
|
|
return ERR_IO_PENDING;
|
|
case kMockAuthChallengeTwiceFailingJob:
|
|
set_load_state(LOAD_STATE_CONNECTING);
|
|
DoAdvanceAuthChallenge(2, false /* succeed_after_last_challenge */);
|
|
return ERR_IO_PENDING;
|
|
default:
|
|
NOTREACHED();
|
|
SetSocket(std::unique_ptr<StreamSocket>(), absl::nullopt);
|
|
return ERR_FAILED;
|
|
}
|
|
}
|
|
|
|
void ChangePriorityInternal(RequestPriority priority) override {}
|
|
|
|
int DoConnect(bool succeed, bool was_async, bool cert_error) {
|
|
int result = OK;
|
|
has_established_connection_ = true;
|
|
if (succeed) {
|
|
SetSocket(std::make_unique<MockClientSocket>(net_log().net_log()),
|
|
absl::nullopt);
|
|
socket()->Connect(CompletionOnceCallback());
|
|
} else if (cert_error) {
|
|
SetSocket(std::make_unique<MockClientSocket>(net_log().net_log()),
|
|
absl::nullopt);
|
|
result = ERR_CERT_COMMON_NAME_INVALID;
|
|
} else {
|
|
result = ERR_CONNECTION_FAILED;
|
|
SetSocket(std::unique_ptr<StreamSocket>(), absl::nullopt);
|
|
}
|
|
|
|
if (was_async)
|
|
NotifyDelegateOfCompletion(result);
|
|
return result;
|
|
}
|
|
|
|
void DoAdvanceAuthChallenge(int remaining_challenges,
|
|
bool succeed_after_last_challenge) {
|
|
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
|
|
FROM_HERE,
|
|
base::BindOnce(&TestConnectJob::InvokeNextProxyAuthCallback,
|
|
weak_factory_.GetWeakPtr(), remaining_challenges,
|
|
succeed_after_last_challenge));
|
|
}
|
|
|
|
void InvokeNextProxyAuthCallback(int remaining_challenges,
|
|
bool succeed_after_last_challenge) {
|
|
set_load_state(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL);
|
|
if (remaining_challenges == 0) {
|
|
DoConnect(succeed_after_last_challenge, true /* was_async */,
|
|
false /* cert_error */);
|
|
return;
|
|
}
|
|
|
|
// Integration tests make sure HttpResponseInfo and HttpAuthController work.
|
|
// The auth tests here are just focused on ConnectJob bookkeeping.
|
|
HttpResponseInfo info;
|
|
NotifyDelegateOfProxyAuth(
|
|
info, nullptr /* http_auth_controller */,
|
|
base::BindOnce(&TestConnectJob::DoAdvanceAuthChallenge,
|
|
weak_factory_.GetWeakPtr(), remaining_challenges - 1,
|
|
succeed_after_last_challenge));
|
|
}
|
|
|
|
bool waiting_success_;
|
|
const JobType job_type_;
|
|
const raw_ptr<MockClientSocketFactory> client_socket_factory_;
|
|
LoadState load_state_ = LOAD_STATE_IDLE;
|
|
bool has_established_connection_ = false;
|
|
bool store_additional_error_state_ = false;
|
|
|
|
base::WeakPtrFactory<TestConnectJob> weak_factory_{this};
|
|
};
|
|
|
|
class TestConnectJobFactory : public ConnectJobFactory {
|
|
public:
|
|
explicit TestConnectJobFactory(MockClientSocketFactory* client_socket_factory)
|
|
: client_socket_factory_(client_socket_factory) {}
|
|
|
|
TestConnectJobFactory(const TestConnectJobFactory&) = delete;
|
|
TestConnectJobFactory& operator=(const TestConnectJobFactory&) = delete;
|
|
|
|
~TestConnectJobFactory() override = default;
|
|
|
|
void set_job_type(TestConnectJob::JobType job_type) { job_type_ = job_type; }
|
|
|
|
void set_job_types(std::list<TestConnectJob::JobType>* job_types) {
|
|
job_types_ = job_types;
|
|
CHECK(!job_types_->empty());
|
|
}
|
|
|
|
void set_timeout_duration(base::TimeDelta timeout_duration) {
|
|
timeout_duration_ = timeout_duration;
|
|
}
|
|
|
|
// ConnectJobFactory implementation.
|
|
|
|
std::unique_ptr<ConnectJob> CreateConnectJob(
|
|
Endpoint endpoint,
|
|
const ProxyServer& proxy_server,
|
|
const absl::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag,
|
|
const SSLConfig* ssl_config_for_origin,
|
|
const SSLConfig* ssl_config_for_proxy,
|
|
bool force_tunnel,
|
|
PrivacyMode privacy_mode,
|
|
const OnHostResolutionCallback& resolution_callback,
|
|
RequestPriority request_priority,
|
|
SocketTag socket_tag,
|
|
const NetworkAnonymizationKey& network_anonymization_key,
|
|
SecureDnsPolicy secure_dns_policy,
|
|
const CommonConnectJobParams* common_connect_job_params,
|
|
ConnectJob::Delegate* delegate) const override {
|
|
EXPECT_TRUE(!job_types_ || !job_types_->empty());
|
|
TestConnectJob::JobType job_type = job_type_;
|
|
if (job_types_ && !job_types_->empty()) {
|
|
job_type = job_types_->front();
|
|
job_types_->pop_front();
|
|
}
|
|
return std::make_unique<TestConnectJob>(
|
|
job_type, request_priority, socket_tag, timeout_duration_,
|
|
common_connect_job_params, delegate, client_socket_factory_);
|
|
}
|
|
|
|
private:
|
|
TestConnectJob::JobType job_type_ = TestConnectJob::kMockJob;
|
|
raw_ptr<std::list<TestConnectJob::JobType>> job_types_ = nullptr;
|
|
base::TimeDelta timeout_duration_;
|
|
const raw_ptr<MockClientSocketFactory> client_socket_factory_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
namespace {
|
|
|
|
void MockClientSocketFactory::SignalJobs() {
|
|
for (auto* waiting_job : waiting_jobs_) {
|
|
waiting_job->Signal();
|
|
}
|
|
waiting_jobs_.clear();
|
|
}
|
|
|
|
void MockClientSocketFactory::SignalJob(size_t job) {
|
|
ASSERT_LT(job, waiting_jobs_.size());
|
|
waiting_jobs_[job]->Signal();
|
|
waiting_jobs_.erase(waiting_jobs_.begin() + job);
|
|
}
|
|
|
|
void MockClientSocketFactory::SetJobLoadState(size_t job,
|
|
LoadState load_state) {
|
|
ASSERT_LT(job, waiting_jobs_.size());
|
|
waiting_jobs_[job]->set_load_state(load_state);
|
|
}
|
|
|
|
void MockClientSocketFactory::SetJobHasEstablishedConnection(size_t job) {
|
|
ASSERT_LT(job, waiting_jobs_.size());
|
|
waiting_jobs_[job]->set_has_established_connection();
|
|
}
|
|
|
|
class ClientSocketPoolBaseTest : public TestWithTaskEnvironment {
|
|
protected:
|
|
ClientSocketPoolBaseTest()
|
|
: TestWithTaskEnvironment(
|
|
base::test::TaskEnvironment::TimeSource::MOCK_TIME),
|
|
params_(ClientSocketPool::SocketParams::CreateForHttpForTesting()) {
|
|
connect_backup_jobs_enabled_ =
|
|
TransportClientSocketPool::connect_backup_jobs_enabled();
|
|
TransportClientSocketPool::set_connect_backup_jobs_enabled(true);
|
|
}
|
|
|
|
~ClientSocketPoolBaseTest() override {
|
|
TransportClientSocketPool::set_connect_backup_jobs_enabled(
|
|
connect_backup_jobs_enabled_);
|
|
}
|
|
|
|
void CreatePool(int max_sockets,
|
|
int max_sockets_per_group,
|
|
bool enable_backup_connect_jobs = false) {
|
|
CreatePoolWithIdleTimeouts(max_sockets, max_sockets_per_group,
|
|
kUnusedIdleSocketTimeout,
|
|
ClientSocketPool::used_idle_socket_timeout(),
|
|
enable_backup_connect_jobs);
|
|
}
|
|
|
|
void CreatePoolWithIdleTimeouts(
|
|
int max_sockets,
|
|
int max_sockets_per_group,
|
|
base::TimeDelta unused_idle_socket_timeout,
|
|
base::TimeDelta used_idle_socket_timeout,
|
|
bool enable_backup_connect_jobs = false,
|
|
ProxyServer proxy_server = ProxyServer::Direct()) {
|
|
DCHECK(!pool_.get());
|
|
std::unique_ptr<TestConnectJobFactory> connect_job_factory =
|
|
std::make_unique<TestConnectJobFactory>(&client_socket_factory_);
|
|
connect_job_factory_ = connect_job_factory.get();
|
|
pool_ = TransportClientSocketPool::CreateForTesting(
|
|
max_sockets, max_sockets_per_group, unused_idle_socket_timeout,
|
|
used_idle_socket_timeout, proxy_server, /*is_for_websockets=*/false,
|
|
&common_connect_job_params_, std::move(connect_job_factory),
|
|
nullptr /* ssl_config_service */, enable_backup_connect_jobs);
|
|
}
|
|
|
|
int StartRequestWithIgnoreLimits(
|
|
const ClientSocketPool::GroupId& group_id,
|
|
RequestPriority priority,
|
|
ClientSocketPool::RespectLimits respect_limits) {
|
|
return test_base_.StartRequestUsingPool(pool_.get(), group_id, priority,
|
|
respect_limits, params_);
|
|
}
|
|
|
|
int StartRequest(const ClientSocketPool::GroupId& group_id,
|
|
RequestPriority priority) {
|
|
return StartRequestWithIgnoreLimits(
|
|
group_id, priority, ClientSocketPool::RespectLimits::ENABLED);
|
|
}
|
|
|
|
int GetOrderOfRequest(size_t index) const {
|
|
return test_base_.GetOrderOfRequest(index);
|
|
}
|
|
|
|
bool ReleaseOneConnection(ClientSocketPoolTest::KeepAlive keep_alive) {
|
|
return test_base_.ReleaseOneConnection(keep_alive);
|
|
}
|
|
|
|
void ReleaseAllConnections(ClientSocketPoolTest::KeepAlive keep_alive) {
|
|
test_base_.ReleaseAllConnections(keep_alive);
|
|
}
|
|
|
|
// Expects a single NetLogEventType::SOCKET_POOL_CLOSING_SOCKET in |net_log_|.
|
|
// It should be logged for the provided source and have the indicated reason.
|
|
void ExpectSocketClosedWithReason(NetLogSource expected_source,
|
|
const char* expected_reason) {
|
|
auto entries = net_log_observer_.GetEntriesForSourceWithType(
|
|
expected_source, NetLogEventType::SOCKET_POOL_CLOSING_SOCKET,
|
|
NetLogEventPhase::NONE);
|
|
ASSERT_EQ(1u, entries.size());
|
|
ASSERT_TRUE(entries[0].HasParams());
|
|
const std::string* reason = entries[0].params.FindString("reason");
|
|
ASSERT_TRUE(reason);
|
|
EXPECT_EQ(expected_reason, *reason);
|
|
}
|
|
|
|
TestSocketRequest* request(int i) { return test_base_.request(i); }
|
|
size_t requests_size() const { return test_base_.requests_size(); }
|
|
std::vector<std::unique_ptr<TestSocketRequest>>* requests() {
|
|
return test_base_.requests();
|
|
}
|
|
// Only counts the requests that get sockets asynchronously;
|
|
// synchronous completions are not registered by this count.
|
|
size_t completion_count() const { return test_base_.completion_count(); }
|
|
|
|
const CommonConnectJobParams common_connect_job_params_{
|
|
nullptr /* client_socket_factory */,
|
|
nullptr /* host_resolver */,
|
|
nullptr /* http_auth_cache */,
|
|
nullptr /* http_auth_handler_factory */,
|
|
nullptr /* spdy_session_pool */,
|
|
nullptr /* quic_supported_versions */,
|
|
nullptr /* quic_stream_factory */,
|
|
nullptr /* proxy_delegate */,
|
|
nullptr /* http_user_agent_settings */,
|
|
nullptr /* ssl_client_context */,
|
|
nullptr /* socket_performance_watcher_factory */,
|
|
nullptr /* network_quality_estimator */,
|
|
NetLog::Get(),
|
|
nullptr /* websocket_endpoint_lock_manager */};
|
|
bool connect_backup_jobs_enabled_;
|
|
MockClientSocketFactory client_socket_factory_;
|
|
RecordingNetLogObserver net_log_observer_;
|
|
|
|
// These parameters are never actually used to create a TransportConnectJob.
|
|
scoped_refptr<ClientSocketPool::SocketParams> params_;
|
|
|
|
// Must outlive `connect_job_factory_`
|
|
std::unique_ptr<TransportClientSocketPool> pool_;
|
|
|
|
raw_ptr<TestConnectJobFactory> connect_job_factory_;
|
|
ClientSocketPoolTest test_base_;
|
|
};
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, BasicSynchronous) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
TestCompletionCallback callback;
|
|
ClientSocketHandle handle;
|
|
NetLogWithSource net_log_with_source =
|
|
NetLogWithSource::Make(NetLogSourceType::NONE);
|
|
|
|
TestLoadTimingInfoNotConnected(handle);
|
|
|
|
EXPECT_EQ(OK, handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), net_log_with_source));
|
|
EXPECT_TRUE(handle.is_initialized());
|
|
EXPECT_TRUE(handle.socket());
|
|
TestLoadTimingInfoConnectedNotReused(handle);
|
|
|
|
handle.Reset();
|
|
TestLoadTimingInfoNotConnected(handle);
|
|
|
|
auto entries =
|
|
net_log_observer_.GetEntriesForSource(net_log_with_source.source());
|
|
|
|
EXPECT_EQ(5u, entries.size());
|
|
EXPECT_TRUE(LogContainsEvent(
|
|
entries, 0, NetLogEventType::TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET,
|
|
NetLogEventPhase::NONE));
|
|
EXPECT_TRUE(LogContainsBeginEvent(entries, 1, NetLogEventType::SOCKET_POOL));
|
|
EXPECT_TRUE(LogContainsEvent(
|
|
entries, 2, NetLogEventType::SOCKET_POOL_BOUND_TO_CONNECT_JOB,
|
|
NetLogEventPhase::NONE));
|
|
EXPECT_TRUE(LogContainsEvent(entries, 3,
|
|
NetLogEventType::SOCKET_POOL_BOUND_TO_SOCKET,
|
|
NetLogEventPhase::NONE));
|
|
EXPECT_TRUE(LogContainsEndEvent(entries, 4, NetLogEventType::SOCKET_POOL));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, InitConnectionFailure) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockFailingJob);
|
|
NetLogWithSource net_log_with_source =
|
|
NetLogWithSource::Make(NetLogSourceType::NONE);
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
// Set the additional error state members to ensure that they get cleared.
|
|
handle.set_is_ssl_error(true);
|
|
handle.set_ssl_cert_request_info(base::MakeRefCounted<SSLCertRequestInfo>());
|
|
EXPECT_EQ(
|
|
ERR_CONNECTION_FAILED,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), net_log_with_source));
|
|
EXPECT_FALSE(handle.socket());
|
|
EXPECT_FALSE(handle.is_ssl_error());
|
|
EXPECT_FALSE(handle.ssl_cert_request_info());
|
|
TestLoadTimingInfoNotConnected(handle);
|
|
|
|
auto entries =
|
|
net_log_observer_.GetEntriesForSource(net_log_with_source.source());
|
|
|
|
EXPECT_EQ(4u, entries.size());
|
|
EXPECT_TRUE(LogContainsEvent(
|
|
entries, 0, NetLogEventType::TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET,
|
|
NetLogEventPhase::NONE));
|
|
EXPECT_TRUE(LogContainsBeginEvent(entries, 1, NetLogEventType::SOCKET_POOL));
|
|
EXPECT_TRUE(LogContainsEvent(
|
|
entries, 2, NetLogEventType::SOCKET_POOL_BOUND_TO_CONNECT_JOB,
|
|
NetLogEventPhase::NONE));
|
|
EXPECT_TRUE(LogContainsEndEvent(entries, 3, NetLogEventType::SOCKET_POOL));
|
|
}
|
|
|
|
// Test releasing an open socket into the socket pool, telling the socket pool
|
|
// to close the socket.
|
|
TEST_F(ClientSocketPoolBaseTest, ReleaseAndCloseConnection) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsError(OK));
|
|
ASSERT_TRUE(request(0)->handle()->socket());
|
|
net::NetLogSource source = request(0)->handle()->socket()->NetLog().source();
|
|
ReleaseOneConnection(ClientSocketPoolTest::NO_KEEP_ALIVE);
|
|
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
|
|
ExpectSocketClosedWithReason(
|
|
source, TransportClientSocketPool::kClosedConnectionReturnedToPool);
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, SocketWithUnreadDataReturnedToPool) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockUnreadDataJob);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsError(OK));
|
|
ASSERT_TRUE(request(0)->handle()->socket());
|
|
net::NetLogSource source = request(0)->handle()->socket()->NetLog().source();
|
|
EXPECT_TRUE(request(0)->handle()->socket()->IsConnected());
|
|
EXPECT_FALSE(request(0)->handle()->socket()->IsConnectedAndIdle());
|
|
ReleaseOneConnection(ClientSocketPoolTest::KEEP_ALIVE);
|
|
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
|
|
ExpectSocketClosedWithReason(
|
|
source, TransportClientSocketPool::kDataReceivedUnexpectedly);
|
|
}
|
|
|
|
// Make sure different groups do not share sockets.
|
|
TEST_F(ClientSocketPoolBaseTest, GroupSeparation) {
|
|
base::test::ScopedFeatureList feature_list;
|
|
feature_list.InitAndEnableFeature(
|
|
features::kPartitionConnectionsByNetworkIsolationKey);
|
|
|
|
CreatePool(1000 /* max_sockets */, 2 /* max_sockets_per_group */);
|
|
|
|
const HostPortPair kHostPortPairs[] = {
|
|
{"a", 80},
|
|
{"a", 443},
|
|
{"b", 80},
|
|
};
|
|
|
|
const char* const kSchemes[] = {
|
|
url::kHttpScheme,
|
|
url::kHttpsScheme,
|
|
};
|
|
|
|
const PrivacyMode kPrivacyModes[] = {PrivacyMode::PRIVACY_MODE_DISABLED,
|
|
PrivacyMode::PRIVACY_MODE_ENABLED};
|
|
|
|
const SchemefulSite kSiteA(GURL("http://a.test/"));
|
|
const SchemefulSite kSiteB(GURL("http://b.test/"));
|
|
const NetworkAnonymizationKey kNetworkAnonymizationKeys[] = {
|
|
NetworkAnonymizationKey::CreateSameSite(kSiteA),
|
|
NetworkAnonymizationKey::CreateSameSite(kSiteB),
|
|
};
|
|
|
|
const SecureDnsPolicy kSecureDnsPolicys[] = {SecureDnsPolicy::kAllow,
|
|
SecureDnsPolicy::kDisable};
|
|
|
|
int total_idle_sockets = 0;
|
|
|
|
// Walk through each GroupId, making sure that requesting a socket for one
|
|
// group does not return a previously connected socket for another group.
|
|
for (const auto& host_port_pair : kHostPortPairs) {
|
|
SCOPED_TRACE(host_port_pair.ToString());
|
|
for (const char* scheme : kSchemes) {
|
|
SCOPED_TRACE(scheme);
|
|
for (const auto& privacy_mode : kPrivacyModes) {
|
|
SCOPED_TRACE(privacy_mode);
|
|
for (const auto& network_anonymization_key :
|
|
kNetworkAnonymizationKeys) {
|
|
SCOPED_TRACE(network_anonymization_key.ToDebugString());
|
|
for (const auto& secure_dns_policy : kSecureDnsPolicys) {
|
|
SCOPED_TRACE(static_cast<int>(secure_dns_policy));
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
ClientSocketPool::GroupId group_id(
|
|
url::SchemeHostPort(scheme, host_port_pair.host(),
|
|
host_port_pair.port()),
|
|
privacy_mode, network_anonymization_key, secure_dns_policy);
|
|
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(group_id));
|
|
|
|
TestCompletionCallback callback;
|
|
ClientSocketHandle handle;
|
|
|
|
// Since the group is empty, requesting a socket should not complete
|
|
// synchronously.
|
|
EXPECT_THAT(handle.Init(group_id, params_, absl::nullopt,
|
|
DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()),
|
|
IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(pool_->HasGroupForTesting(group_id));
|
|
EXPECT_EQ(total_idle_sockets, pool_->IdleSocketCount());
|
|
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
EXPECT_TRUE(handle.socket());
|
|
EXPECT_TRUE(pool_->HasGroupForTesting(group_id));
|
|
EXPECT_EQ(total_idle_sockets, pool_->IdleSocketCount());
|
|
|
|
// Return socket to pool.
|
|
handle.Reset();
|
|
EXPECT_EQ(total_idle_sockets + 1, pool_->IdleSocketCount());
|
|
|
|
// Requesting a socket again should return the same socket as
|
|
// before, so should complete synchronously.
|
|
EXPECT_THAT(handle.Init(group_id, params_, absl::nullopt,
|
|
DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()),
|
|
IsOk());
|
|
EXPECT_TRUE(handle.socket());
|
|
EXPECT_EQ(total_idle_sockets, pool_->IdleSocketCount());
|
|
|
|
// Return socket to pool again.
|
|
handle.Reset();
|
|
EXPECT_EQ(total_idle_sockets + 1, pool_->IdleSocketCount());
|
|
|
|
++total_idle_sockets;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, TotalLimit) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
// TODO(eroman): Check that the NetLog contains this event.
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("b"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("c"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("d"), DEFAULT_PRIORITY), IsOk());
|
|
|
|
EXPECT_EQ(static_cast<int>(requests_size()),
|
|
client_socket_factory_.allocation_count());
|
|
EXPECT_EQ(requests_size() - kDefaultMaxSockets, completion_count());
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("e"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("f"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("g"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
|
|
ReleaseAllConnections(ClientSocketPoolTest::NO_KEEP_ALIVE);
|
|
|
|
EXPECT_EQ(static_cast<int>(requests_size()),
|
|
client_socket_factory_.allocation_count());
|
|
EXPECT_EQ(requests_size() - kDefaultMaxSockets, completion_count());
|
|
|
|
EXPECT_EQ(1, GetOrderOfRequest(1));
|
|
EXPECT_EQ(2, GetOrderOfRequest(2));
|
|
EXPECT_EQ(3, GetOrderOfRequest(3));
|
|
EXPECT_EQ(4, GetOrderOfRequest(4));
|
|
EXPECT_EQ(5, GetOrderOfRequest(5));
|
|
EXPECT_EQ(6, GetOrderOfRequest(6));
|
|
EXPECT_EQ(7, GetOrderOfRequest(7));
|
|
|
|
// Make sure we test order of all requests made.
|
|
EXPECT_EQ(ClientSocketPoolTest::kIndexOutOfBounds, GetOrderOfRequest(8));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, TotalLimitReachedNewGroup) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
// TODO(eroman): Check that the NetLog contains this event.
|
|
|
|
// Reach all limits: max total sockets, and max sockets per group.
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("b"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("b"), DEFAULT_PRIORITY), IsOk());
|
|
|
|
EXPECT_EQ(static_cast<int>(requests_size()),
|
|
client_socket_factory_.allocation_count());
|
|
EXPECT_EQ(requests_size() - kDefaultMaxSockets, completion_count());
|
|
|
|
// Now create a new group and verify that we don't starve it.
|
|
EXPECT_THAT(StartRequest(TestGroupId("c"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
|
|
ReleaseAllConnections(ClientSocketPoolTest::NO_KEEP_ALIVE);
|
|
|
|
EXPECT_EQ(static_cast<int>(requests_size()),
|
|
client_socket_factory_.allocation_count());
|
|
EXPECT_EQ(requests_size() - kDefaultMaxSockets, completion_count());
|
|
|
|
EXPECT_EQ(1, GetOrderOfRequest(1));
|
|
EXPECT_EQ(2, GetOrderOfRequest(2));
|
|
EXPECT_EQ(3, GetOrderOfRequest(3));
|
|
EXPECT_EQ(4, GetOrderOfRequest(4));
|
|
EXPECT_EQ(5, GetOrderOfRequest(5));
|
|
|
|
// Make sure we test order of all requests made.
|
|
EXPECT_EQ(ClientSocketPoolTest::kIndexOutOfBounds, GetOrderOfRequest(6));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, TotalLimitRespectsPriority) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("b"), LOWEST), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), MEDIUM), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("b"), HIGHEST), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsOk());
|
|
|
|
EXPECT_EQ(static_cast<int>(requests_size()),
|
|
client_socket_factory_.allocation_count());
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("c"), LOWEST), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), MEDIUM), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("b"), HIGHEST), IsError(ERR_IO_PENDING));
|
|
|
|
ReleaseAllConnections(ClientSocketPoolTest::NO_KEEP_ALIVE);
|
|
|
|
EXPECT_EQ(requests_size() - kDefaultMaxSockets, completion_count());
|
|
|
|
// First 4 requests don't have to wait, and finish in order.
|
|
EXPECT_EQ(1, GetOrderOfRequest(1));
|
|
EXPECT_EQ(2, GetOrderOfRequest(2));
|
|
EXPECT_EQ(3, GetOrderOfRequest(3));
|
|
EXPECT_EQ(4, GetOrderOfRequest(4));
|
|
|
|
// Request ("b", HIGHEST) has the highest priority, then (TestGroupId("a"),
|
|
// MEDIUM), and then ("c", LOWEST).
|
|
EXPECT_EQ(7, GetOrderOfRequest(5));
|
|
EXPECT_EQ(6, GetOrderOfRequest(6));
|
|
EXPECT_EQ(5, GetOrderOfRequest(7));
|
|
|
|
// Make sure we test order of all requests made.
|
|
EXPECT_EQ(ClientSocketPoolTest::kIndexOutOfBounds, GetOrderOfRequest(9));
|
|
}
|
|
|
|
// Test reprioritizing a request before completion doesn't interfere with
|
|
// its completion.
|
|
TEST_F(ClientSocketPoolBaseTest, ReprioritizeOne) {
|
|
CreatePool(kDefaultMaxSockets, 1);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsError(OK));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), MEDIUM), IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request(0)->handle()->socket());
|
|
EXPECT_FALSE(request(1)->handle()->socket());
|
|
|
|
request(1)->handle()->SetPriority(HIGHEST);
|
|
|
|
ReleaseOneConnection(ClientSocketPoolTest::NO_KEEP_ALIVE);
|
|
|
|
EXPECT_TRUE(request(1)->handle()->socket());
|
|
}
|
|
|
|
// Reprioritize a request up past another one and make sure that changes the
|
|
// completion order.
|
|
TEST_F(ClientSocketPoolBaseTest, ReprioritizeUpReorder) {
|
|
CreatePool(kDefaultMaxSockets, 1);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsError(OK));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), MEDIUM), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request(0)->handle()->socket());
|
|
EXPECT_FALSE(request(1)->handle()->socket());
|
|
EXPECT_FALSE(request(2)->handle()->socket());
|
|
|
|
request(2)->handle()->SetPriority(HIGHEST);
|
|
|
|
ReleaseAllConnections(ClientSocketPoolTest::NO_KEEP_ALIVE);
|
|
|
|
EXPECT_EQ(1, GetOrderOfRequest(1));
|
|
EXPECT_EQ(3, GetOrderOfRequest(2));
|
|
EXPECT_EQ(2, GetOrderOfRequest(3));
|
|
}
|
|
|
|
// Reprioritize a request without changing relative priorities and check
|
|
// that the order doesn't change.
|
|
TEST_F(ClientSocketPoolBaseTest, ReprioritizeUpNoReorder) {
|
|
CreatePool(kDefaultMaxSockets, 1);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsError(OK));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), MEDIUM), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOW), IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request(0)->handle()->socket());
|
|
EXPECT_FALSE(request(1)->handle()->socket());
|
|
EXPECT_FALSE(request(2)->handle()->socket());
|
|
|
|
request(2)->handle()->SetPriority(MEDIUM);
|
|
|
|
ReleaseAllConnections(ClientSocketPoolTest::NO_KEEP_ALIVE);
|
|
|
|
EXPECT_EQ(1, GetOrderOfRequest(1));
|
|
EXPECT_EQ(2, GetOrderOfRequest(2));
|
|
EXPECT_EQ(3, GetOrderOfRequest(3));
|
|
}
|
|
|
|
// Reprioritize a request past down another one and make sure that changes the
|
|
// completion order.
|
|
TEST_F(ClientSocketPoolBaseTest, ReprioritizeDownReorder) {
|
|
CreatePool(kDefaultMaxSockets, 1);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsError(OK));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), HIGHEST), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), MEDIUM), IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request(0)->handle()->socket());
|
|
EXPECT_FALSE(request(1)->handle()->socket());
|
|
EXPECT_FALSE(request(2)->handle()->socket());
|
|
|
|
request(1)->handle()->SetPriority(LOW);
|
|
|
|
ReleaseAllConnections(ClientSocketPoolTest::NO_KEEP_ALIVE);
|
|
|
|
EXPECT_EQ(1, GetOrderOfRequest(1));
|
|
EXPECT_EQ(3, GetOrderOfRequest(2));
|
|
EXPECT_EQ(2, GetOrderOfRequest(3));
|
|
}
|
|
|
|
// Reprioritize a request to the same level as another and confirm it is
|
|
// put after the old request.
|
|
TEST_F(ClientSocketPoolBaseTest, ReprioritizeResetFIFO) {
|
|
CreatePool(kDefaultMaxSockets, 1);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsError(OK));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), HIGHEST), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), MEDIUM), IsError(ERR_IO_PENDING));
|
|
EXPECT_TRUE(request(0)->handle()->socket());
|
|
EXPECT_FALSE(request(1)->handle()->socket());
|
|
EXPECT_FALSE(request(2)->handle()->socket());
|
|
|
|
request(1)->handle()->SetPriority(MEDIUM);
|
|
|
|
ReleaseAllConnections(ClientSocketPoolTest::NO_KEEP_ALIVE);
|
|
|
|
EXPECT_EQ(1, GetOrderOfRequest(1));
|
|
EXPECT_EQ(3, GetOrderOfRequest(2));
|
|
EXPECT_EQ(2, GetOrderOfRequest(3));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, TotalLimitRespectsGroupLimit) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOW), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("b"), HIGHEST), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("b"), MEDIUM), IsOk());
|
|
|
|
EXPECT_EQ(static_cast<int>(requests_size()),
|
|
client_socket_factory_.allocation_count());
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("c"), MEDIUM), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOW), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("b"), HIGHEST), IsError(ERR_IO_PENDING));
|
|
|
|
ReleaseAllConnections(ClientSocketPoolTest::NO_KEEP_ALIVE);
|
|
|
|
EXPECT_EQ(static_cast<int>(requests_size()),
|
|
client_socket_factory_.allocation_count());
|
|
EXPECT_EQ(requests_size() - kDefaultMaxSockets, completion_count());
|
|
|
|
// First 4 requests don't have to wait, and finish in order.
|
|
EXPECT_EQ(1, GetOrderOfRequest(1));
|
|
EXPECT_EQ(2, GetOrderOfRequest(2));
|
|
EXPECT_EQ(3, GetOrderOfRequest(3));
|
|
EXPECT_EQ(4, GetOrderOfRequest(4));
|
|
|
|
// Request ("b", 7) has the highest priority, but we can't make new socket for
|
|
// group "b", because it has reached the per-group limit. Then we make
|
|
// socket for ("c", 6), because it has higher priority than ("a", 4),
|
|
// and we still can't make a socket for group "b".
|
|
EXPECT_EQ(5, GetOrderOfRequest(5));
|
|
EXPECT_EQ(6, GetOrderOfRequest(6));
|
|
EXPECT_EQ(7, GetOrderOfRequest(7));
|
|
|
|
// Make sure we test order of all requests made.
|
|
EXPECT_EQ(ClientSocketPoolTest::kIndexOutOfBounds, GetOrderOfRequest(8));
|
|
}
|
|
|
|
// Make sure that we count connecting sockets against the total limit.
|
|
TEST_F(ClientSocketPoolBaseTest, TotalLimitCountsConnectingSockets) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("b"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("c"), DEFAULT_PRIORITY), IsOk());
|
|
|
|
// Create one asynchronous request.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
EXPECT_THAT(StartRequest(TestGroupId("d"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
|
|
// We post all of our delayed tasks with a 2ms delay. I.e. they don't
|
|
// actually become pending until 2ms after they have been created. In order
|
|
// to flush all tasks, we need to wait so that we know there are no
|
|
// soon-to-be-pending tasks waiting.
|
|
FastForwardBy(base::Milliseconds(10));
|
|
|
|
// The next synchronous request should wait for its turn.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
EXPECT_THAT(StartRequest(TestGroupId("e"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
|
|
ReleaseAllConnections(ClientSocketPoolTest::NO_KEEP_ALIVE);
|
|
|
|
EXPECT_EQ(static_cast<int>(requests_size()),
|
|
client_socket_factory_.allocation_count());
|
|
|
|
EXPECT_EQ(1, GetOrderOfRequest(1));
|
|
EXPECT_EQ(2, GetOrderOfRequest(2));
|
|
EXPECT_EQ(3, GetOrderOfRequest(3));
|
|
EXPECT_EQ(4, GetOrderOfRequest(4));
|
|
EXPECT_EQ(5, GetOrderOfRequest(5));
|
|
|
|
// Make sure we test order of all requests made.
|
|
EXPECT_EQ(ClientSocketPoolTest::kIndexOutOfBounds, GetOrderOfRequest(6));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, CorrectlyCountStalledGroups) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSockets);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
EXPECT_EQ(kDefaultMaxSockets, client_socket_factory_.allocation_count());
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("b"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("c"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
|
|
EXPECT_EQ(kDefaultMaxSockets, client_socket_factory_.allocation_count());
|
|
|
|
EXPECT_TRUE(ReleaseOneConnection(ClientSocketPoolTest::KEEP_ALIVE));
|
|
EXPECT_EQ(kDefaultMaxSockets + 1, client_socket_factory_.allocation_count());
|
|
EXPECT_TRUE(ReleaseOneConnection(ClientSocketPoolTest::KEEP_ALIVE));
|
|
EXPECT_EQ(kDefaultMaxSockets + 2, client_socket_factory_.allocation_count());
|
|
EXPECT_TRUE(ReleaseOneConnection(ClientSocketPoolTest::KEEP_ALIVE));
|
|
EXPECT_TRUE(ReleaseOneConnection(ClientSocketPoolTest::KEEP_ALIVE));
|
|
EXPECT_EQ(kDefaultMaxSockets + 2, client_socket_factory_.allocation_count());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, StallAndThenCancelAndTriggerAvailableSocket) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSockets);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
TestCompletionCallback callback;
|
|
ClientSocketHandle stalled_handle;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
stalled_handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
ClientSocketHandle handles[4];
|
|
for (auto& handle : handles) {
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("b"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
}
|
|
|
|
// One will be stalled, cancel all the handles now.
|
|
// This should hit the OnAvailableSocketSlot() code where we previously had
|
|
// stalled groups, but no longer have any.
|
|
for (auto& handle : handles)
|
|
handle.Reset();
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, CancelStalledSocketAtSocketLimit) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
|
|
{
|
|
ClientSocketHandle handles[kDefaultMaxSockets];
|
|
TestCompletionCallback callbacks[kDefaultMaxSockets];
|
|
for (int i = 0; i < kDefaultMaxSockets; ++i) {
|
|
EXPECT_EQ(OK, handles[i].Init(TestGroupId("a" + base::NumberToString(i)),
|
|
params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED,
|
|
callbacks[i].callback(),
|
|
ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
}
|
|
|
|
// Force a stalled group.
|
|
ClientSocketHandle stalled_handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
stalled_handle.Init(
|
|
TestGroupId("foo"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
// Cancel the stalled request.
|
|
stalled_handle.Reset();
|
|
|
|
EXPECT_EQ(kDefaultMaxSockets, client_socket_factory_.allocation_count());
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
|
|
// Dropping out of scope will close all handles and return them to idle.
|
|
}
|
|
|
|
EXPECT_EQ(kDefaultMaxSockets, client_socket_factory_.allocation_count());
|
|
EXPECT_EQ(kDefaultMaxSockets, pool_->IdleSocketCount());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, CancelPendingSocketAtSocketLimit) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
{
|
|
ClientSocketHandle handles[kDefaultMaxSockets];
|
|
for (int i = 0; i < kDefaultMaxSockets; ++i) {
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
handles[i].Init(
|
|
TestGroupId("a" + base::NumberToString(i)), params_,
|
|
absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
}
|
|
|
|
// Force a stalled group.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
ClientSocketHandle stalled_handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
stalled_handle.Init(
|
|
TestGroupId("foo"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
// Since it is stalled, it should have no connect jobs.
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("foo")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("foo")));
|
|
EXPECT_EQ(0u, pool_->NumUnassignedConnectJobsInGroupForTesting(
|
|
TestGroupId("foo")));
|
|
|
|
// Cancel the stalled request.
|
|
handles[0].Reset();
|
|
|
|
// Now we should have a connect job.
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("foo")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("foo")));
|
|
EXPECT_EQ(0u, pool_->NumUnassignedConnectJobsInGroupForTesting(
|
|
TestGroupId("foo")));
|
|
|
|
// The stalled socket should connect.
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
|
|
EXPECT_EQ(kDefaultMaxSockets + 1,
|
|
client_socket_factory_.allocation_count());
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("foo")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("foo")));
|
|
EXPECT_EQ(0u, pool_->NumUnassignedConnectJobsInGroupForTesting(
|
|
TestGroupId("foo")));
|
|
|
|
// Dropping out of scope will close all handles and return them to idle.
|
|
}
|
|
|
|
EXPECT_EQ(1, pool_->IdleSocketCount());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, WaitForStalledSocketAtSocketLimit) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
|
|
ClientSocketHandle stalled_handle;
|
|
TestCompletionCallback callback;
|
|
{
|
|
EXPECT_FALSE(pool_->IsStalled());
|
|
ClientSocketHandle handles[kDefaultMaxSockets];
|
|
for (int i = 0; i < kDefaultMaxSockets; ++i) {
|
|
EXPECT_EQ(
|
|
OK, handles[i].Init(
|
|
TestGroupId(base::StringPrintf("take-2-%d", i)), params_,
|
|
absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(),
|
|
NetLogWithSource()));
|
|
}
|
|
|
|
EXPECT_EQ(kDefaultMaxSockets, client_socket_factory_.allocation_count());
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_FALSE(pool_->IsStalled());
|
|
|
|
// Now we will hit the socket limit.
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
stalled_handle.Init(
|
|
TestGroupId("foo"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_TRUE(pool_->IsStalled());
|
|
|
|
// Dropping out of scope will close all handles and return them to idle.
|
|
}
|
|
|
|
// But if we wait for it, the released idle sockets will be closed in
|
|
// preference of the waiting request.
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
|
|
EXPECT_EQ(kDefaultMaxSockets + 1, client_socket_factory_.allocation_count());
|
|
EXPECT_EQ(3, pool_->IdleSocketCount());
|
|
}
|
|
|
|
// Regression test for http://crbug.com/40952.
|
|
TEST_F(ClientSocketPoolBaseTest, CloseIdleSocketAtSocketLimitDeleteGroup) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup,
|
|
true /* enable_backup_connect_jobs */);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
|
|
for (int i = 0; i < kDefaultMaxSockets; ++i) {
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
OK,
|
|
handle.Init(TestGroupId("a" + base::NumberToString(i)), params_,
|
|
absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
}
|
|
|
|
// Flush all the DoReleaseSocket tasks.
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
// Stall a group. Set a pending job so it'll trigger a backup job if we don't
|
|
// reuse a socket.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
|
|
// "a0" is special here, since it should be the first entry in the sorted map,
|
|
// which is the one which we would close an idle socket for. We shouldn't
|
|
// close an idle socket though, since we should reuse the idle socket.
|
|
EXPECT_EQ(OK, handle.Init(
|
|
TestGroupId("a0"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(kDefaultMaxSockets, client_socket_factory_.allocation_count());
|
|
EXPECT_EQ(kDefaultMaxSockets - 1, pool_->IdleSocketCount());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, PendingRequests) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), IDLE), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), MEDIUM), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), HIGHEST), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOW), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsError(ERR_IO_PENDING));
|
|
|
|
ReleaseAllConnections(ClientSocketPoolTest::KEEP_ALIVE);
|
|
EXPECT_EQ(kDefaultMaxSocketsPerGroup,
|
|
client_socket_factory_.allocation_count());
|
|
EXPECT_EQ(requests_size() - kDefaultMaxSocketsPerGroup,
|
|
completion_count());
|
|
|
|
EXPECT_EQ(1, GetOrderOfRequest(1));
|
|
EXPECT_EQ(2, GetOrderOfRequest(2));
|
|
EXPECT_EQ(8, GetOrderOfRequest(3));
|
|
EXPECT_EQ(6, GetOrderOfRequest(4));
|
|
EXPECT_EQ(4, GetOrderOfRequest(5));
|
|
EXPECT_EQ(3, GetOrderOfRequest(6));
|
|
EXPECT_EQ(5, GetOrderOfRequest(7));
|
|
EXPECT_EQ(7, GetOrderOfRequest(8));
|
|
|
|
// Make sure we test order of all requests made.
|
|
EXPECT_EQ(ClientSocketPoolTest::kIndexOutOfBounds, GetOrderOfRequest(9));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, PendingRequests_NoKeepAlive) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), MEDIUM), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), HIGHEST), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOW), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsError(ERR_IO_PENDING));
|
|
|
|
ReleaseAllConnections(ClientSocketPoolTest::NO_KEEP_ALIVE);
|
|
|
|
for (size_t i = kDefaultMaxSocketsPerGroup; i < requests_size(); ++i)
|
|
EXPECT_THAT(request(i)->WaitForResult(), IsOk());
|
|
|
|
EXPECT_EQ(static_cast<int>(requests_size()),
|
|
client_socket_factory_.allocation_count());
|
|
EXPECT_EQ(requests_size() - kDefaultMaxSocketsPerGroup,
|
|
completion_count());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, ResetAndCloseSocket) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
|
|
handle.ResetAndCloseSocket();
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
}
|
|
|
|
// This test will start up a socket request and then call Reset() on the handle.
|
|
// The pending ConnectJob should not be destroyed.
|
|
TEST_F(ClientSocketPoolBaseTest, CancelRequestKeepsConnectJob) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
handle.Reset();
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
}
|
|
|
|
// This test will start up a socket request and then call ResetAndCloseSocket()
|
|
// on the handle. The pending ConnectJob or connected socket should be
|
|
// destroyed.
|
|
TEST_F(ClientSocketPoolBaseTest, CancelRequestAndCloseSocket) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
// When true, the socket connects before it's canceled.
|
|
for (bool cancel_when_callback_pending : {false, true}) {
|
|
if (cancel_when_callback_pending) {
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
} else {
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
}
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
if (cancel_when_callback_pending) {
|
|
client_socket_factory_.SignalJobs();
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
}
|
|
|
|
handle.ResetAndCloseSocket();
|
|
ASSERT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
}
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest,
|
|
CancelRequestAndCloseSocketWhenMoreRequestsThanConnectJobs) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
// When true, the sockets connect before they're canceled.
|
|
for (bool cancel_when_callback_pending : {false, true}) {
|
|
if (cancel_when_callback_pending) {
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
} else {
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
}
|
|
|
|
std::vector<std::unique_ptr<ClientSocketHandle>> handles;
|
|
TestCompletionCallback callback;
|
|
// Make |kDefaultMaxSockets + 1| socket requests.
|
|
for (int i = 0; i < kDefaultMaxSocketsPerGroup + 1; ++i) {
|
|
std::unique_ptr<ClientSocketHandle> handle =
|
|
std::make_unique<ClientSocketHandle>();
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
handle->Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
handles.push_back(std::move(handle));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(
|
|
static_cast<size_t>(std::min(i + 1, kDefaultMaxSocketsPerGroup)),
|
|
pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
}
|
|
|
|
if (cancel_when_callback_pending) {
|
|
client_socket_factory_.SignalJobs();
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(kDefaultMaxSocketsPerGroup,
|
|
pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
}
|
|
|
|
// Calling ResetAndCloseSocket() on a handle should not cancel a ConnectJob
|
|
// or close a socket, since there are more requests than ConnectJobs or
|
|
// sockets.
|
|
handles[kDefaultMaxSocketsPerGroup]->ResetAndCloseSocket();
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
if (cancel_when_callback_pending) {
|
|
EXPECT_EQ(kDefaultMaxSocketsPerGroup,
|
|
pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
} else {
|
|
EXPECT_EQ(static_cast<size_t>(kDefaultMaxSocketsPerGroup),
|
|
pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
}
|
|
|
|
// Calling ResetAndCloseSocket() on other handles should cancel a ConnectJob
|
|
// or close a socket.
|
|
for (int i = kDefaultMaxSocketsPerGroup - 1; i >= 0; --i) {
|
|
handles[i]->ResetAndCloseSocket();
|
|
if (i > 0) {
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
if (cancel_when_callback_pending) {
|
|
EXPECT_EQ(i,
|
|
pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
} else {
|
|
EXPECT_EQ(static_cast<size_t>(i),
|
|
pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
}
|
|
} else {
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, ConnectCancelConnect) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
handle.Reset();
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// This will create a second ConnectJob, since the other ConnectJob was
|
|
// previously assigned to a request.
|
|
TestCompletionCallback callback2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(2u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
EXPECT_THAT(callback2.WaitForResult(), IsOk());
|
|
EXPECT_FALSE(callback.have_result());
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
// One ConnectJob completed, and its socket is now assigned to |handle|.
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
// The other ConnectJob should have either completed, or still be connecting.
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")) +
|
|
pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
handle.Reset();
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(2u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")) +
|
|
pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, CancelRequest) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), MEDIUM), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), HIGHEST), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOW), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsError(ERR_IO_PENDING));
|
|
|
|
// Cancel a request.
|
|
size_t index_to_cancel = kDefaultMaxSocketsPerGroup + 2;
|
|
EXPECT_FALSE((*requests())[index_to_cancel]->handle()->is_initialized());
|
|
(*requests())[index_to_cancel]->handle()->Reset();
|
|
|
|
ReleaseAllConnections(ClientSocketPoolTest::KEEP_ALIVE);
|
|
|
|
EXPECT_EQ(kDefaultMaxSocketsPerGroup,
|
|
client_socket_factory_.allocation_count());
|
|
EXPECT_EQ(requests_size() - kDefaultMaxSocketsPerGroup - 1,
|
|
completion_count());
|
|
|
|
EXPECT_EQ(1, GetOrderOfRequest(1));
|
|
EXPECT_EQ(2, GetOrderOfRequest(2));
|
|
EXPECT_EQ(5, GetOrderOfRequest(3));
|
|
EXPECT_EQ(3, GetOrderOfRequest(4));
|
|
EXPECT_EQ(ClientSocketPoolTest::kRequestNotFound,
|
|
GetOrderOfRequest(5)); // Canceled request.
|
|
EXPECT_EQ(4, GetOrderOfRequest(6));
|
|
EXPECT_EQ(6, GetOrderOfRequest(7));
|
|
|
|
// Make sure we test order of all requests made.
|
|
EXPECT_EQ(ClientSocketPoolTest::kIndexOutOfBounds, GetOrderOfRequest(8));
|
|
}
|
|
|
|
// Function to be used as a callback on socket request completion. It first
|
|
// disconnects the successfully connected socket from the first request, and
|
|
// then reuses the ClientSocketHandle to request another socket.
|
|
//
|
|
// |nested_callback| is called with the result of the second socket request.
|
|
void RequestSocketOnComplete(ClientSocketHandle* handle,
|
|
TransportClientSocketPool* pool,
|
|
TestConnectJobFactory* test_connect_job_factory,
|
|
TestConnectJob::JobType next_job_type,
|
|
TestCompletionCallback* nested_callback,
|
|
int first_request_result) {
|
|
EXPECT_THAT(first_request_result, IsOk());
|
|
|
|
test_connect_job_factory->set_job_type(next_job_type);
|
|
|
|
// Don't allow reuse of the socket. Disconnect it and then release it.
|
|
if (handle->socket())
|
|
handle->socket()->Disconnect();
|
|
handle->Reset();
|
|
|
|
TestCompletionCallback callback;
|
|
int rv = handle->Init(
|
|
TestGroupId("a"),
|
|
ClientSocketPool::SocketParams::CreateForHttpForTesting(), absl::nullopt,
|
|
LOWEST, SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
nested_callback->callback(), ClientSocketPool::ProxyAuthCallback(), pool,
|
|
NetLogWithSource());
|
|
if (rv != ERR_IO_PENDING) {
|
|
DCHECK_EQ(TestConnectJob::kMockJob, next_job_type);
|
|
nested_callback->callback().Run(rv);
|
|
} else {
|
|
DCHECK_EQ(TestConnectJob::kMockPendingJob, next_job_type);
|
|
}
|
|
}
|
|
|
|
// Tests the case where a second socket is requested in a completion callback,
|
|
// and the second socket connects asynchronously. Reuses the same
|
|
// ClientSocketHandle for the second socket, after disconnecting the first.
|
|
TEST_F(ClientSocketPoolBaseTest, RequestPendingJobTwice) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback second_result_callback;
|
|
int rv = handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED,
|
|
base::BindOnce(&RequestSocketOnComplete, &handle, pool_.get(),
|
|
connect_job_factory_, TestConnectJob::kMockPendingJob,
|
|
&second_result_callback),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), NetLogWithSource());
|
|
ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
|
|
EXPECT_THAT(second_result_callback.WaitForResult(), IsOk());
|
|
}
|
|
|
|
// Tests the case where a second socket is requested in a completion callback,
|
|
// and the second socket connects synchronously. Reuses the same
|
|
// ClientSocketHandle for the second socket, after disconnecting the first.
|
|
TEST_F(ClientSocketPoolBaseTest, RequestPendingJobThenSynchronous) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback second_result_callback;
|
|
int rv = handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED,
|
|
base::BindOnce(&RequestSocketOnComplete, &handle, pool_.get(),
|
|
connect_job_factory_, TestConnectJob::kMockPendingJob,
|
|
&second_result_callback),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), NetLogWithSource());
|
|
ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
|
|
EXPECT_THAT(second_result_callback.WaitForResult(), IsOk());
|
|
}
|
|
|
|
// Make sure that pending requests get serviced after active requests get
|
|
// cancelled.
|
|
TEST_F(ClientSocketPoolBaseTest, CancelActiveRequestWithPendingRequests) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
|
|
// Now, kDefaultMaxSocketsPerGroup requests should be active.
|
|
// Let's cancel them.
|
|
for (int i = 0; i < kDefaultMaxSocketsPerGroup; ++i) {
|
|
ASSERT_FALSE(request(i)->handle()->is_initialized());
|
|
request(i)->handle()->Reset();
|
|
}
|
|
|
|
// Let's wait for the rest to complete now.
|
|
for (size_t i = kDefaultMaxSocketsPerGroup; i < requests_size(); ++i) {
|
|
EXPECT_THAT(request(i)->WaitForResult(), IsOk());
|
|
request(i)->handle()->Reset();
|
|
}
|
|
|
|
EXPECT_EQ(requests_size() - kDefaultMaxSocketsPerGroup,
|
|
completion_count());
|
|
}
|
|
|
|
// Make sure that pending requests get serviced after active requests fail.
|
|
TEST_F(ClientSocketPoolBaseTest, FailingActiveRequestWithPendingRequests) {
|
|
const size_t kMaxSockets = 5;
|
|
CreatePool(kMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
|
|
|
|
const size_t kNumberOfRequests = 2 * kDefaultMaxSocketsPerGroup + 1;
|
|
ASSERT_LE(kNumberOfRequests, kMaxSockets); // Otherwise the test will hang.
|
|
|
|
// Queue up all the requests
|
|
for (size_t i = 0; i < kNumberOfRequests; ++i)
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
|
|
for (size_t i = 0; i < kNumberOfRequests; ++i)
|
|
EXPECT_THAT(request(i)->WaitForResult(), IsError(ERR_CONNECTION_FAILED));
|
|
}
|
|
|
|
// Make sure that pending requests that complete synchronously get serviced
|
|
// after active requests fail. See https://crbug.com/723748
|
|
TEST_F(ClientSocketPoolBaseTest, HandleMultipleSyncFailuresAfterAsyncFailure) {
|
|
const size_t kNumberOfRequests = 10;
|
|
const size_t kMaxSockets = 1;
|
|
CreatePool(kMaxSockets, kMaxSockets);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockFailingJob);
|
|
|
|
// Queue up all the other requests
|
|
for (size_t i = 1; i < kNumberOfRequests; ++i)
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
|
|
// Make sure all requests fail, instead of hanging.
|
|
for (size_t i = 0; i < kNumberOfRequests; ++i)
|
|
EXPECT_THAT(request(i)->WaitForResult(), IsError(ERR_CONNECTION_FAILED));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, CancelActiveRequestThenRequestSocket) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
int rv = handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
|
|
// Cancel the active request.
|
|
handle.Reset();
|
|
|
|
rv = handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
|
|
EXPECT_FALSE(handle.is_reused());
|
|
TestLoadTimingInfoConnectedNotReused(handle);
|
|
EXPECT_EQ(2, client_socket_factory_.allocation_count());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, CloseIdleSocketsForced) {
|
|
const char kReason[] = "Really nifty reason";
|
|
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
int rv =
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, LOWEST, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(),
|
|
NetLogWithSource::Make(NetLogSourceType::NONE));
|
|
EXPECT_THAT(rv, IsOk());
|
|
ASSERT_TRUE(handle.socket());
|
|
NetLogSource source = handle.socket()->NetLog().source();
|
|
handle.Reset();
|
|
EXPECT_EQ(1, pool_->IdleSocketCount());
|
|
pool_->CloseIdleSockets(kReason);
|
|
ExpectSocketClosedWithReason(source, kReason);
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, CloseIdleSocketsInGroupForced) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
TestCompletionCallback callback;
|
|
NetLogWithSource net_log_with_source =
|
|
NetLogWithSource::Make(NetLogSourceType::NONE);
|
|
ClientSocketHandle handle1;
|
|
int rv = handle1.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, LOWEST, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), net_log_with_source);
|
|
EXPECT_THAT(rv, IsOk());
|
|
ClientSocketHandle handle2;
|
|
rv = handle2.Init(TestGroupId("a"), params_, absl::nullopt, LOWEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), net_log_with_source);
|
|
ClientSocketHandle handle3;
|
|
rv = handle3.Init(TestGroupId("b"), params_, absl::nullopt, LOWEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), net_log_with_source);
|
|
EXPECT_THAT(rv, IsOk());
|
|
handle1.Reset();
|
|
handle2.Reset();
|
|
handle3.Reset();
|
|
EXPECT_EQ(3, pool_->IdleSocketCount());
|
|
pool_->CloseIdleSocketsInGroup(TestGroupId("a"), "Very good reason");
|
|
EXPECT_EQ(1, pool_->IdleSocketCount());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, CleanUpUnusableIdleSockets) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
NetLogWithSource net_log_with_source =
|
|
NetLogWithSource::Make(NetLogSourceType::NONE);
|
|
int rv = handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, LOWEST, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), net_log_with_source);
|
|
EXPECT_THAT(rv, IsOk());
|
|
StreamSocket* socket = handle.socket();
|
|
ASSERT_TRUE(socket);
|
|
handle.Reset();
|
|
EXPECT_EQ(1, pool_->IdleSocketCount());
|
|
|
|
// Disconnect socket now to make the socket unusable.
|
|
NetLogSource source = socket->NetLog().source();
|
|
socket->Disconnect();
|
|
ClientSocketHandle handle2;
|
|
rv = handle2.Init(TestGroupId("a"), params_, absl::nullopt, LOWEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), net_log_with_source);
|
|
EXPECT_THAT(rv, IsOk());
|
|
EXPECT_FALSE(handle2.is_reused());
|
|
|
|
// This is admittedly not an accurate error in this case, but normally code
|
|
// doesn't secretly keep a raw pointers to sockets returned to the socket pool
|
|
// and close them out of band, so discovering an idle socket was closed when
|
|
// trying to reuse it normally means it was closed by the remote side.
|
|
ExpectSocketClosedWithReason(
|
|
source, TransportClientSocketPool::kRemoteSideClosedConnection);
|
|
}
|
|
|
|
// Regression test for http://crbug.com/17985.
|
|
TEST_F(ClientSocketPoolBaseTest, GroupWithPendingRequestsIsNotEmpty) {
|
|
const int kMaxSockets = 3;
|
|
const int kMaxSocketsPerGroup = 2;
|
|
CreatePool(kMaxSockets, kMaxSocketsPerGroup);
|
|
|
|
const RequestPriority kHighPriority = HIGHEST;
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
|
|
// This is going to be a pending request in an otherwise empty group.
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
|
|
// Reach the maximum socket limit.
|
|
EXPECT_THAT(StartRequest(TestGroupId("b"), DEFAULT_PRIORITY), IsOk());
|
|
|
|
// Create a stalled group with high priorities.
|
|
EXPECT_THAT(StartRequest(TestGroupId("c"), kHighPriority),
|
|
IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("c"), kHighPriority),
|
|
IsError(ERR_IO_PENDING));
|
|
|
|
// Release the first two sockets from TestGroupId("a"). Because this is a
|
|
// keepalive, the first release will unblock the pending request for
|
|
// TestGroupId("a"). The second release will unblock a request for "c",
|
|
// because it is the next high priority socket.
|
|
EXPECT_TRUE(ReleaseOneConnection(ClientSocketPoolTest::KEEP_ALIVE));
|
|
EXPECT_TRUE(ReleaseOneConnection(ClientSocketPoolTest::KEEP_ALIVE));
|
|
|
|
// Closing idle sockets should not get us into trouble, but in the bug
|
|
// we were hitting a CHECK here.
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
pool_->CloseIdleSockets("Very good reason");
|
|
|
|
// Run the released socket wakeups.
|
|
base::RunLoop().RunUntilIdle();
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, BasicAsynchronous) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
NetLogWithSource net_log_with_source =
|
|
NetLogWithSource::Make(NetLogSourceType::NONE);
|
|
int rv = handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, LOWEST, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), net_log_with_source);
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING,
|
|
pool_->GetLoadState(TestGroupId("a"), &handle));
|
|
TestLoadTimingInfoNotConnected(handle);
|
|
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
EXPECT_TRUE(handle.is_initialized());
|
|
EXPECT_TRUE(handle.socket());
|
|
TestLoadTimingInfoConnectedNotReused(handle);
|
|
|
|
handle.Reset();
|
|
TestLoadTimingInfoNotConnected(handle);
|
|
|
|
auto entries =
|
|
net_log_observer_.GetEntriesForSource(net_log_with_source.source());
|
|
|
|
EXPECT_EQ(5u, entries.size());
|
|
EXPECT_TRUE(LogContainsEvent(
|
|
entries, 0, NetLogEventType::TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET,
|
|
NetLogEventPhase::NONE));
|
|
EXPECT_TRUE(LogContainsBeginEvent(entries, 1, NetLogEventType::SOCKET_POOL));
|
|
EXPECT_TRUE(LogContainsEvent(
|
|
entries, 2, NetLogEventType::SOCKET_POOL_BOUND_TO_CONNECT_JOB,
|
|
NetLogEventPhase::NONE));
|
|
EXPECT_TRUE(LogContainsEvent(entries, 3,
|
|
NetLogEventType::SOCKET_POOL_BOUND_TO_SOCKET,
|
|
NetLogEventPhase::NONE));
|
|
EXPECT_TRUE(LogContainsEndEvent(entries, 4, NetLogEventType::SOCKET_POOL));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest,
|
|
InitConnectionAsynchronousFailure) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
NetLogWithSource net_log_with_source =
|
|
NetLogWithSource::Make(NetLogSourceType::NONE);
|
|
// Set the additional error state members to ensure that they get cleared.
|
|
handle.set_is_ssl_error(true);
|
|
handle.set_ssl_cert_request_info(base::MakeRefCounted<SSLCertRequestInfo>());
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), net_log_with_source));
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING,
|
|
pool_->GetLoadState(TestGroupId("a"), &handle));
|
|
EXPECT_THAT(callback.WaitForResult(), IsError(ERR_CONNECTION_FAILED));
|
|
EXPECT_FALSE(handle.is_ssl_error());
|
|
EXPECT_FALSE(handle.ssl_cert_request_info());
|
|
|
|
auto entries =
|
|
net_log_observer_.GetEntriesForSource(net_log_with_source.source());
|
|
|
|
EXPECT_EQ(4u, entries.size());
|
|
EXPECT_TRUE(LogContainsEvent(
|
|
entries, 0, NetLogEventType::TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET,
|
|
NetLogEventPhase::NONE));
|
|
EXPECT_TRUE(LogContainsBeginEvent(entries, 1, NetLogEventType::SOCKET_POOL));
|
|
EXPECT_TRUE(LogContainsEvent(
|
|
entries, 2, NetLogEventType::SOCKET_POOL_BOUND_TO_CONNECT_JOB,
|
|
NetLogEventPhase::NONE));
|
|
EXPECT_TRUE(LogContainsEndEvent(entries, 3, NetLogEventType::SOCKET_POOL));
|
|
}
|
|
|
|
// Check that an async ConnectJob failure does not result in creation of a new
|
|
// ConnectJob when there's another pending request also waiting on its own
|
|
// ConnectJob. See http://crbug.com/463960.
|
|
TEST_F(ClientSocketPoolBaseTest, AsyncFailureWithPendingRequestWithJob) {
|
|
CreatePool(2, 2);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
|
|
EXPECT_THAT(request(0)->WaitForResult(), IsError(ERR_CONNECTION_FAILED));
|
|
EXPECT_THAT(request(1)->WaitForResult(), IsError(ERR_CONNECTION_FAILED));
|
|
|
|
EXPECT_EQ(2, client_socket_factory_.allocation_count());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, TwoRequestsCancelOne) {
|
|
// TODO(eroman): Add back the log expectations! Removed them because the
|
|
// ordering is difficult, and some may fire during destructor.
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
RecordingNetLogObserver log2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
handle.Reset();
|
|
|
|
|
|
// At this point, request 2 is just waiting for the connect job to finish.
|
|
|
|
EXPECT_THAT(callback2.WaitForResult(), IsOk());
|
|
handle2.Reset();
|
|
|
|
// Now request 2 has actually finished.
|
|
// TODO(eroman): Add back log expectations.
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, CancelRequestLimitsJobs) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOWEST), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), LOW), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), MEDIUM), IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), HIGHEST), IsError(ERR_IO_PENDING));
|
|
|
|
EXPECT_EQ(kDefaultMaxSocketsPerGroup,
|
|
static_cast<int>(
|
|
pool_->NumConnectJobsInGroupForTesting(TestGroupId("a"))));
|
|
(*requests())[2]->handle()->Reset();
|
|
(*requests())[3]->handle()->Reset();
|
|
EXPECT_EQ(kDefaultMaxSocketsPerGroup,
|
|
static_cast<int>(
|
|
pool_->NumConnectJobsInGroupForTesting(TestGroupId("a"))));
|
|
|
|
(*requests())[1]->handle()->Reset();
|
|
EXPECT_EQ(kDefaultMaxSocketsPerGroup,
|
|
static_cast<int>(
|
|
pool_->NumConnectJobsInGroupForTesting(TestGroupId("a"))));
|
|
|
|
(*requests())[0]->handle()->Reset();
|
|
EXPECT_EQ(kDefaultMaxSocketsPerGroup,
|
|
static_cast<int>(
|
|
pool_->NumConnectJobsInGroupForTesting(TestGroupId("a"))));
|
|
}
|
|
|
|
// When requests and ConnectJobs are not coupled, the request will get serviced
|
|
// by whatever comes first.
|
|
TEST_F(ClientSocketPoolBaseTest, ReleaseSockets) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
// Start job 1 (async OK)
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
std::vector<TestSocketRequest*> request_order;
|
|
size_t completion_count; // unused
|
|
TestSocketRequest req1(&request_order, &completion_count);
|
|
int rv = req1.handle()->Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, req1.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(req1.WaitForResult(), IsOk());
|
|
|
|
// Job 1 finished OK. Start job 2 (also async OK). Request 3 is pending
|
|
// without a job.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
TestSocketRequest req2(&request_order, &completion_count);
|
|
rv = req2.handle()->Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, req2.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
TestSocketRequest req3(&request_order, &completion_count);
|
|
rv = req3.handle()->Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, req3.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
|
|
// Both Requests 2 and 3 are pending. We release socket 1 which should
|
|
// service request 2. Request 3 should still be waiting.
|
|
req1.handle()->Reset();
|
|
// Run the released socket wakeups.
|
|
base::RunLoop().RunUntilIdle();
|
|
ASSERT_TRUE(req2.handle()->socket());
|
|
EXPECT_THAT(req2.WaitForResult(), IsOk());
|
|
EXPECT_FALSE(req3.handle()->socket());
|
|
|
|
// Signal job 2, which should service request 3.
|
|
|
|
client_socket_factory_.SignalJobs();
|
|
EXPECT_THAT(req3.WaitForResult(), IsOk());
|
|
|
|
ASSERT_EQ(3u, request_order.size());
|
|
EXPECT_EQ(&req1, request_order[0]);
|
|
EXPECT_EQ(&req2, request_order[1]);
|
|
EXPECT_EQ(&req3, request_order[2]);
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
}
|
|
|
|
// The requests are not coupled to the jobs. So, the requests should finish in
|
|
// their priority / insertion order.
|
|
TEST_F(ClientSocketPoolBaseTest, PendingJobCompletionOrder) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
// First two jobs are async.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
|
|
|
|
std::vector<TestSocketRequest*> request_order;
|
|
size_t completion_count; // unused
|
|
TestSocketRequest req1(&request_order, &completion_count);
|
|
int rv = req1.handle()->Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, req1.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
|
|
TestSocketRequest req2(&request_order, &completion_count);
|
|
rv = req2.handle()->Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, req2.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
|
|
// The pending job is sync.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
|
|
TestSocketRequest req3(&request_order, &completion_count);
|
|
rv = req3.handle()->Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, req3.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
|
|
EXPECT_THAT(req1.WaitForResult(), IsError(ERR_CONNECTION_FAILED));
|
|
EXPECT_THAT(req2.WaitForResult(), IsOk());
|
|
EXPECT_THAT(req3.WaitForResult(), IsError(ERR_CONNECTION_FAILED));
|
|
|
|
ASSERT_EQ(3u, request_order.size());
|
|
EXPECT_EQ(&req1, request_order[0]);
|
|
EXPECT_EQ(&req2, request_order[1]);
|
|
EXPECT_EQ(&req3, request_order[2]);
|
|
}
|
|
|
|
// Test GetLoadState in the case there's only one socket request.
|
|
TEST_F(ClientSocketPoolBaseTest, LoadStateOneRequest) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
int rv = handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING, handle.GetLoadState());
|
|
|
|
client_socket_factory_.SetJobLoadState(0, LOAD_STATE_SSL_HANDSHAKE);
|
|
EXPECT_EQ(LOAD_STATE_SSL_HANDSHAKE, handle.GetLoadState());
|
|
|
|
// No point in completing the connection, since ClientSocketHandles only
|
|
// expect the LoadState to be checked while connecting.
|
|
}
|
|
|
|
// Test GetLoadState in the case there are two socket requests.
|
|
TEST_F(ClientSocketPoolBaseTest, LoadStateTwoRequests) {
|
|
CreatePool(2, 2);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
int rv = handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
client_socket_factory_.SetJobLoadState(0, LOAD_STATE_RESOLVING_HOST);
|
|
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
rv = handle2.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
client_socket_factory_.SetJobLoadState(1, LOAD_STATE_RESOLVING_HOST);
|
|
|
|
// Each handle should reflect the state of its own job.
|
|
EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, handle.GetLoadState());
|
|
EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, handle2.GetLoadState());
|
|
|
|
// Update the state of the first job.
|
|
client_socket_factory_.SetJobLoadState(0, LOAD_STATE_CONNECTING);
|
|
|
|
// Only the state of the first request should have changed.
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING, handle.GetLoadState());
|
|
EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, handle2.GetLoadState());
|
|
|
|
// Update the state of the second job.
|
|
client_socket_factory_.SetJobLoadState(1, LOAD_STATE_SSL_HANDSHAKE);
|
|
|
|
// Only the state of the second request should have changed.
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING, handle.GetLoadState());
|
|
EXPECT_EQ(LOAD_STATE_SSL_HANDSHAKE, handle2.GetLoadState());
|
|
|
|
// Second job connects and the first request gets the socket. The
|
|
// second handle switches to the state of the remaining ConnectJob.
|
|
client_socket_factory_.SignalJob(1);
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING, handle2.GetLoadState());
|
|
}
|
|
|
|
// Test GetLoadState in the case the per-group limit is reached.
|
|
TEST_F(ClientSocketPoolBaseTest, LoadStateGroupLimit) {
|
|
CreatePool(2, 1);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
int rv = handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, MEDIUM, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING, handle.GetLoadState());
|
|
|
|
// Request another socket from the same pool, buth with a higher priority.
|
|
// The first request should now be stalled at the socket group limit.
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
rv = handle2.Init(TestGroupId("a"), params_, absl::nullopt, HIGHEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
EXPECT_EQ(LOAD_STATE_WAITING_FOR_AVAILABLE_SOCKET, handle.GetLoadState());
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING, handle2.GetLoadState());
|
|
|
|
// The first handle should remain stalled as the other socket goes through
|
|
// the connect process.
|
|
|
|
client_socket_factory_.SetJobLoadState(0, LOAD_STATE_SSL_HANDSHAKE);
|
|
EXPECT_EQ(LOAD_STATE_WAITING_FOR_AVAILABLE_SOCKET, handle.GetLoadState());
|
|
EXPECT_EQ(LOAD_STATE_SSL_HANDSHAKE, handle2.GetLoadState());
|
|
|
|
client_socket_factory_.SignalJob(0);
|
|
EXPECT_THAT(callback2.WaitForResult(), IsOk());
|
|
EXPECT_EQ(LOAD_STATE_WAITING_FOR_AVAILABLE_SOCKET, handle.GetLoadState());
|
|
|
|
// Closing the second socket should cause the stalled handle to finally get a
|
|
// ConnectJob.
|
|
handle2.socket()->Disconnect();
|
|
handle2.Reset();
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING, handle.GetLoadState());
|
|
}
|
|
|
|
// Test GetLoadState in the case the per-pool limit is reached.
|
|
TEST_F(ClientSocketPoolBaseTest, LoadStatePoolLimit) {
|
|
CreatePool(2, 2);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
int rv = handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
|
|
// Request for socket from another pool.
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
rv = handle2.Init(TestGroupId("b"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
|
|
// Request another socket from the first pool. Request should stall at the
|
|
// socket pool limit.
|
|
ClientSocketHandle handle3;
|
|
TestCompletionCallback callback3;
|
|
rv = handle3.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
|
|
// The third handle should remain stalled as the other sockets in its group
|
|
// goes through the connect process.
|
|
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING, handle.GetLoadState());
|
|
EXPECT_EQ(LOAD_STATE_WAITING_FOR_STALLED_SOCKET_POOL, handle3.GetLoadState());
|
|
|
|
client_socket_factory_.SetJobLoadState(0, LOAD_STATE_SSL_HANDSHAKE);
|
|
EXPECT_EQ(LOAD_STATE_SSL_HANDSHAKE, handle.GetLoadState());
|
|
EXPECT_EQ(LOAD_STATE_WAITING_FOR_STALLED_SOCKET_POOL, handle3.GetLoadState());
|
|
|
|
client_socket_factory_.SignalJob(0);
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
EXPECT_EQ(LOAD_STATE_WAITING_FOR_STALLED_SOCKET_POOL, handle3.GetLoadState());
|
|
|
|
// Closing a socket should allow the stalled handle to finally get a new
|
|
// ConnectJob.
|
|
handle.socket()->Disconnect();
|
|
handle.Reset();
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING, handle3.GetLoadState());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, CertError) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockCertErrorJob);
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_CERT_COMMON_NAME_INVALID,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_TRUE(handle.is_initialized());
|
|
EXPECT_TRUE(handle.socket());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, AsyncCertError) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingCertErrorJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING,
|
|
pool_->GetLoadState(TestGroupId("a"), &handle));
|
|
EXPECT_THAT(callback.WaitForResult(), IsError(ERR_CERT_COMMON_NAME_INVALID));
|
|
EXPECT_TRUE(handle.is_initialized());
|
|
EXPECT_TRUE(handle.socket());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, AdditionalErrorStateSynchronous) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(
|
|
TestConnectJob::kMockAdditionalErrorStateJob);
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_CONNECTION_FAILED,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_FALSE(handle.is_initialized());
|
|
EXPECT_FALSE(handle.socket());
|
|
EXPECT_TRUE(handle.is_ssl_error());
|
|
EXPECT_TRUE(handle.ssl_cert_request_info());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, AdditionalErrorStateAsynchronous) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
connect_job_factory_->set_job_type(
|
|
TestConnectJob::kMockPendingAdditionalErrorStateJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING,
|
|
pool_->GetLoadState(TestGroupId("a"), &handle));
|
|
EXPECT_THAT(callback.WaitForResult(), IsError(ERR_CONNECTION_FAILED));
|
|
EXPECT_FALSE(handle.is_initialized());
|
|
EXPECT_FALSE(handle.socket());
|
|
EXPECT_TRUE(handle.is_ssl_error());
|
|
EXPECT_TRUE(handle.ssl_cert_request_info());
|
|
}
|
|
|
|
// Make sure we can reuse sockets.
|
|
TEST_F(ClientSocketPoolBaseTest, CleanupTimedOutIdleSocketsReuse) {
|
|
CreatePoolWithIdleTimeouts(
|
|
kDefaultMaxSockets, kDefaultMaxSocketsPerGroup,
|
|
base::TimeDelta(), // Time out unused sockets immediately.
|
|
base::Days(1)); // Don't time out used sockets.
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
int rv = handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, LOWEST, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), NetLogWithSource());
|
|
ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING,
|
|
pool_->GetLoadState(TestGroupId("a"), &handle));
|
|
ASSERT_THAT(callback.WaitForResult(), IsOk());
|
|
|
|
// Use and release the socket.
|
|
EXPECT_EQ(1, handle.socket()->Write(nullptr, 1, CompletionOnceCallback(),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
TestLoadTimingInfoConnectedNotReused(handle);
|
|
handle.Reset();
|
|
|
|
// Should now have one idle socket.
|
|
ASSERT_EQ(1, pool_->IdleSocketCount());
|
|
|
|
// Request a new socket. This should reuse the old socket and complete
|
|
// synchronously.
|
|
NetLogWithSource net_log_with_source =
|
|
NetLogWithSource::Make(NetLogSourceType::NONE);
|
|
rv = handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, LOWEST, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, CompletionOnceCallback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), net_log_with_source);
|
|
ASSERT_THAT(rv, IsOk());
|
|
EXPECT_TRUE(handle.is_reused());
|
|
TestLoadTimingInfoConnectedReused(handle);
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
|
|
auto entries =
|
|
net_log_observer_.GetEntriesForSource(net_log_with_source.source());
|
|
EXPECT_TRUE(LogContainsEvent(
|
|
entries, 0, NetLogEventType::TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET,
|
|
NetLogEventPhase::NONE));
|
|
EXPECT_TRUE(LogContainsBeginEvent(entries, 1, NetLogEventType::SOCKET_POOL));
|
|
EXPECT_TRUE(LogContainsEntryWithType(
|
|
entries, 2, NetLogEventType::SOCKET_POOL_REUSED_AN_EXISTING_SOCKET));
|
|
}
|
|
|
|
// Make sure we cleanup old unused sockets.
|
|
TEST_F(ClientSocketPoolBaseTest, CleanupTimedOutIdleSocketsNoReuse) {
|
|
CreatePoolWithIdleTimeouts(
|
|
kDefaultMaxSockets, kDefaultMaxSocketsPerGroup,
|
|
base::TimeDelta(), // Time out unused sockets immediately
|
|
base::TimeDelta()); // Time out used sockets immediately
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
// Startup two mock pending connect jobs, which will sit in the MessageLoop.
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
int rv = handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, LOWEST, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), NetLogWithSource());
|
|
ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING,
|
|
pool_->GetLoadState(TestGroupId("a"), &handle));
|
|
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
rv = handle2.Init(TestGroupId("a"), params_, absl::nullopt, LOWEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource());
|
|
ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING,
|
|
pool_->GetLoadState(TestGroupId("a"), &handle2));
|
|
|
|
// Cancel one of the requests. Wait for the other, which will get the first
|
|
// job. Release the socket. Run the loop again to make sure the second
|
|
// socket is sitting idle and the first one is released (since ReleaseSocket()
|
|
// just posts a DoReleaseSocket() task).
|
|
|
|
handle.Reset();
|
|
ASSERT_THAT(callback2.WaitForResult(), IsOk());
|
|
// Get the NetLogSource for the socket, so the time out reason can be checked
|
|
// at the end of the test.
|
|
NetLogSource net_log_source2 = handle2.socket()->NetLog().source();
|
|
// Use the socket.
|
|
EXPECT_EQ(1, handle2.socket()->Write(nullptr, 1, CompletionOnceCallback(),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
handle2.Reset();
|
|
|
|
// We post all of our delayed tasks with a 2ms delay. I.e. they don't
|
|
// actually become pending until 2ms after they have been created. In order
|
|
// to flush all tasks, we need to wait so that we know there are no
|
|
// soon-to-be-pending tasks waiting.
|
|
FastForwardBy(base::Milliseconds(10));
|
|
|
|
// Both sockets should now be idle.
|
|
ASSERT_EQ(2, pool_->IdleSocketCount());
|
|
|
|
// Request a new socket. This should cleanup the unused and timed out ones.
|
|
// A new socket will be created rather than reusing the idle one.
|
|
NetLogWithSource net_log_with_source =
|
|
NetLogWithSource::Make(NetLogSourceType::NONE);
|
|
TestCompletionCallback callback3;
|
|
rv = handle.Init(TestGroupId("a"), params_, absl::nullopt, LOWEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback3.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), net_log_with_source);
|
|
ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
ASSERT_THAT(callback3.WaitForResult(), IsOk());
|
|
EXPECT_FALSE(handle.is_reused());
|
|
|
|
// Make sure the idle socket is closed.
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
|
|
auto entries =
|
|
net_log_observer_.GetEntriesForSource(net_log_with_source.source());
|
|
EXPECT_FALSE(LogContainsEntryWithType(
|
|
entries, 1, NetLogEventType::SOCKET_POOL_REUSED_AN_EXISTING_SOCKET));
|
|
ExpectSocketClosedWithReason(
|
|
net_log_source2, TransportClientSocketPool::kIdleTimeLimitExpired);
|
|
}
|
|
|
|
// Make sure that we process all pending requests even when we're stalling
|
|
// because of multiple releasing disconnected sockets.
|
|
TEST_F(ClientSocketPoolBaseTest, MultipleReleasingDisconnectedSockets) {
|
|
CreatePoolWithIdleTimeouts(
|
|
kDefaultMaxSockets, kDefaultMaxSocketsPerGroup,
|
|
base::TimeDelta(), // Time out unused sockets immediately.
|
|
base::Days(1)); // Don't time out used sockets.
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
|
|
// Startup 4 connect jobs. Two of them will be pending.
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
int rv = handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, LOWEST, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsOk());
|
|
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
rv = handle2.Init(TestGroupId("a"), params_, absl::nullopt, LOWEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsOk());
|
|
|
|
ClientSocketHandle handle3;
|
|
TestCompletionCallback callback3;
|
|
rv = handle3.Init(TestGroupId("a"), params_, absl::nullopt, LOWEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback3.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
|
|
ClientSocketHandle handle4;
|
|
TestCompletionCallback callback4;
|
|
rv = handle4.Init(TestGroupId("a"), params_, absl::nullopt, LOWEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback4.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource());
|
|
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
|
|
|
// Release two disconnected sockets.
|
|
|
|
handle.socket()->Disconnect();
|
|
handle.Reset();
|
|
handle2.socket()->Disconnect();
|
|
handle2.Reset();
|
|
|
|
EXPECT_THAT(callback3.WaitForResult(), IsOk());
|
|
EXPECT_FALSE(handle3.is_reused());
|
|
EXPECT_THAT(callback4.WaitForResult(), IsOk());
|
|
EXPECT_FALSE(handle4.is_reused());
|
|
}
|
|
|
|
// Regression test for http://crbug.com/42267.
|
|
// When DoReleaseSocket() is processed for one socket, it is blocked because the
|
|
// other stalled groups all have releasing sockets, so no progress can be made.
|
|
TEST_F(ClientSocketPoolBaseTest, SocketLimitReleasingSockets) {
|
|
CreatePoolWithIdleTimeouts(
|
|
4 /* socket limit */, 4 /* socket limit per group */,
|
|
base::TimeDelta(), // Time out unused sockets immediately.
|
|
base::Days(1)); // Don't time out used sockets.
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
|
|
// Max out the socket limit with 2 per group.
|
|
|
|
ClientSocketHandle handle_a[4];
|
|
TestCompletionCallback callback_a[4];
|
|
ClientSocketHandle handle_b[4];
|
|
TestCompletionCallback callback_b[4];
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
EXPECT_EQ(OK, handle_a[i].Init(TestGroupId("a"), params_, absl::nullopt,
|
|
LOWEST, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED,
|
|
callback_a[i].callback(),
|
|
ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_EQ(OK, handle_b[i].Init(TestGroupId("b"), params_, absl::nullopt,
|
|
LOWEST, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED,
|
|
callback_b[i].callback(),
|
|
ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
}
|
|
|
|
// Make 4 pending requests, 2 per group.
|
|
|
|
for (int i = 2; i < 4; ++i) {
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle_a[i].Init(TestGroupId("a"), params_, absl::nullopt, LOWEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback_a[i].callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(),
|
|
NetLogWithSource()));
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle_b[i].Init(TestGroupId("b"), params_, absl::nullopt, LOWEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback_b[i].callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(),
|
|
NetLogWithSource()));
|
|
}
|
|
|
|
// Release b's socket first. The order is important, because in
|
|
// DoReleaseSocket(), we'll process b's released socket, and since both b and
|
|
// a are stalled, but 'a' is lower lexicographically, we'll process group 'a'
|
|
// first, which has a releasing socket, so it refuses to start up another
|
|
// ConnectJob. So, we used to infinite loop on this.
|
|
handle_b[0].socket()->Disconnect();
|
|
handle_b[0].Reset();
|
|
handle_a[0].socket()->Disconnect();
|
|
handle_a[0].Reset();
|
|
|
|
// Used to get stuck here.
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
handle_b[1].socket()->Disconnect();
|
|
handle_b[1].Reset();
|
|
handle_a[1].socket()->Disconnect();
|
|
handle_a[1].Reset();
|
|
|
|
for (int i = 2; i < 4; ++i) {
|
|
EXPECT_THAT(callback_b[i].WaitForResult(), IsOk());
|
|
EXPECT_THAT(callback_a[i].WaitForResult(), IsOk());
|
|
}
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest,
|
|
ReleasingDisconnectedSocketsMaintainsPriorityOrder) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY),
|
|
IsError(ERR_IO_PENDING));
|
|
|
|
EXPECT_THAT((*requests())[0]->WaitForResult(), IsOk());
|
|
EXPECT_THAT((*requests())[1]->WaitForResult(), IsOk());
|
|
EXPECT_EQ(2u, completion_count());
|
|
|
|
// Releases one connection.
|
|
EXPECT_TRUE(ReleaseOneConnection(ClientSocketPoolTest::NO_KEEP_ALIVE));
|
|
EXPECT_THAT((*requests())[2]->WaitForResult(), IsOk());
|
|
|
|
EXPECT_TRUE(ReleaseOneConnection(ClientSocketPoolTest::NO_KEEP_ALIVE));
|
|
EXPECT_THAT((*requests())[3]->WaitForResult(), IsOk());
|
|
EXPECT_EQ(4u, completion_count());
|
|
|
|
EXPECT_EQ(1, GetOrderOfRequest(1));
|
|
EXPECT_EQ(2, GetOrderOfRequest(2));
|
|
EXPECT_EQ(3, GetOrderOfRequest(3));
|
|
EXPECT_EQ(4, GetOrderOfRequest(4));
|
|
|
|
// Make sure we test order of all requests made.
|
|
EXPECT_EQ(ClientSocketPoolTest::kIndexOutOfBounds, GetOrderOfRequest(5));
|
|
}
|
|
|
|
class TestReleasingSocketRequest : public TestCompletionCallbackBase {
|
|
public:
|
|
TestReleasingSocketRequest(TransportClientSocketPool* pool,
|
|
int expected_result,
|
|
bool reset_releasing_handle)
|
|
: pool_(pool),
|
|
expected_result_(expected_result),
|
|
reset_releasing_handle_(reset_releasing_handle) {}
|
|
|
|
~TestReleasingSocketRequest() override = default;
|
|
|
|
ClientSocketHandle* handle() { return &handle_; }
|
|
|
|
CompletionOnceCallback callback() {
|
|
return base::BindOnce(&TestReleasingSocketRequest::OnComplete,
|
|
base::Unretained(this));
|
|
}
|
|
|
|
private:
|
|
void OnComplete(int result) {
|
|
SetResult(result);
|
|
if (reset_releasing_handle_)
|
|
handle_.Reset();
|
|
|
|
EXPECT_EQ(
|
|
expected_result_,
|
|
handle2_.Init(
|
|
TestGroupId("a"),
|
|
ClientSocketPool::SocketParams::CreateForHttpForTesting(),
|
|
absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, CompletionOnceCallback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_, NetLogWithSource()));
|
|
}
|
|
|
|
const raw_ptr<TransportClientSocketPool> pool_;
|
|
int expected_result_;
|
|
bool reset_releasing_handle_;
|
|
ClientSocketHandle handle_;
|
|
ClientSocketHandle handle2_;
|
|
};
|
|
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, AdditionalErrorSocketsDontUseSlot) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
EXPECT_THAT(StartRequest(TestGroupId("b"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("a"), DEFAULT_PRIORITY), IsOk());
|
|
EXPECT_THAT(StartRequest(TestGroupId("b"), DEFAULT_PRIORITY), IsOk());
|
|
|
|
EXPECT_EQ(static_cast<int>(requests_size()),
|
|
client_socket_factory_.allocation_count());
|
|
|
|
connect_job_factory_->set_job_type(
|
|
TestConnectJob::kMockPendingAdditionalErrorStateJob);
|
|
TestReleasingSocketRequest req(pool_.get(), OK, false);
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
req.handle()->Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
req.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
// The next job should complete synchronously
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
|
|
EXPECT_THAT(req.WaitForResult(), IsError(ERR_CONNECTION_FAILED));
|
|
EXPECT_FALSE(req.handle()->is_initialized());
|
|
EXPECT_FALSE(req.handle()->socket());
|
|
EXPECT_TRUE(req.handle()->is_ssl_error());
|
|
EXPECT_TRUE(req.handle()->ssl_cert_request_info());
|
|
}
|
|
|
|
// http://crbug.com/44724 regression test.
|
|
// We start releasing the pool when we flush on network change. When that
|
|
// happens, the only active references are in the ClientSocketHandles. When a
|
|
// ConnectJob completes and calls back into the last ClientSocketHandle, that
|
|
// callback can release the last reference and delete the pool. After the
|
|
// callback finishes, we go back to the stack frame within the now-deleted pool.
|
|
// Executing any code that refers to members of the now-deleted pool can cause
|
|
// crashes.
|
|
TEST_F(ClientSocketPoolBaseTest, CallbackThatReleasesPool) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
pool_->FlushWithError(ERR_NETWORK_CHANGED, "Network changed");
|
|
|
|
// We'll call back into this now.
|
|
callback.WaitForResult();
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, DoNotReuseSocketAfterFlush) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
EXPECT_EQ(ClientSocketHandle::UNUSED, handle.reuse_type());
|
|
NetLogSource source = handle.socket()->NetLog().source();
|
|
|
|
pool_->FlushWithError(ERR_NETWORK_CHANGED, "Network changed");
|
|
|
|
handle.Reset();
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
EXPECT_EQ(ClientSocketHandle::UNUSED, handle.reuse_type());
|
|
|
|
ExpectSocketClosedWithReason(
|
|
source, TransportClientSocketPool::kSocketGenerationOutOfDate);
|
|
}
|
|
|
|
class ConnectWithinCallback : public TestCompletionCallbackBase {
|
|
public:
|
|
ConnectWithinCallback(
|
|
const ClientSocketPool::GroupId& group_id,
|
|
const scoped_refptr<ClientSocketPool::SocketParams>& params,
|
|
TransportClientSocketPool* pool)
|
|
: group_id_(group_id), params_(params), pool_(pool) {}
|
|
|
|
ConnectWithinCallback(const ConnectWithinCallback&) = delete;
|
|
ConnectWithinCallback& operator=(const ConnectWithinCallback&) = delete;
|
|
|
|
~ConnectWithinCallback() override = default;
|
|
|
|
int WaitForNestedResult() {
|
|
return nested_callback_.WaitForResult();
|
|
}
|
|
|
|
CompletionOnceCallback callback() {
|
|
return base::BindOnce(&ConnectWithinCallback::OnComplete,
|
|
base::Unretained(this));
|
|
}
|
|
|
|
private:
|
|
void OnComplete(int result) {
|
|
SetResult(result);
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle_.Init(group_id_, params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
nested_callback_.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_,
|
|
NetLogWithSource()));
|
|
}
|
|
|
|
const ClientSocketPool::GroupId group_id_;
|
|
const scoped_refptr<ClientSocketPool::SocketParams> params_;
|
|
const raw_ptr<TransportClientSocketPool> pool_;
|
|
ClientSocketHandle handle_;
|
|
TestCompletionCallback nested_callback_;
|
|
};
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, AbortAllRequestsOnFlush) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
// First job will be waiting until it gets aborted.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
ClientSocketHandle handle;
|
|
ConnectWithinCallback callback(TestGroupId("a"), params_, pool_.get());
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
// Second job will be started during the first callback, and will
|
|
// asynchronously complete with OK.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
pool_->FlushWithError(ERR_NETWORK_CHANGED, "Network changed");
|
|
EXPECT_THAT(callback.WaitForResult(), IsError(ERR_NETWORK_CHANGED));
|
|
EXPECT_THAT(callback.WaitForNestedResult(), IsOk());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, BackupSocketWaitsForHostResolution) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSockets,
|
|
true /* enable_backup_connect_jobs */);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("bar"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
// The backup timer fires but doesn't start a new ConnectJob while resolving
|
|
// the hostname.
|
|
client_socket_factory_.SetJobLoadState(0, LOAD_STATE_RESOLVING_HOST);
|
|
FastForwardBy(
|
|
base::Milliseconds(ClientSocketPool::kMaxConnectRetryIntervalMs * 100));
|
|
EXPECT_EQ(1, client_socket_factory_.allocation_count());
|
|
|
|
// Once the ConnectJob has finished resolving the hostname, the backup timer
|
|
// will create a ConnectJob when it fires.
|
|
client_socket_factory_.SetJobLoadState(0, LOAD_STATE_CONNECTING);
|
|
FastForwardBy(
|
|
base::Milliseconds(ClientSocketPool::kMaxConnectRetryIntervalMs));
|
|
EXPECT_EQ(2, client_socket_factory_.allocation_count());
|
|
}
|
|
|
|
// Test that no backup socket is created when a ConnectJob connects before it
|
|
// completes.
|
|
TEST_F(ClientSocketPoolBaseTest, NoBackupSocketWhenConnected) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSockets,
|
|
true /* enable_backup_connect_jobs */);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("bar"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
// The backup timer fires but doesn't start a new ConnectJob while resolving
|
|
// the hostname.
|
|
client_socket_factory_.SetJobLoadState(0, LOAD_STATE_RESOLVING_HOST);
|
|
FastForwardBy(
|
|
base::Milliseconds(ClientSocketPool::kMaxConnectRetryIntervalMs * 100));
|
|
EXPECT_EQ(1, client_socket_factory_.allocation_count());
|
|
|
|
client_socket_factory_.SetJobLoadState(0, LOAD_STATE_SSL_HANDSHAKE);
|
|
client_socket_factory_.SetJobHasEstablishedConnection(0);
|
|
FastForwardBy(
|
|
base::Milliseconds(ClientSocketPool::kMaxConnectRetryIntervalMs * 100));
|
|
EXPECT_EQ(1, client_socket_factory_.allocation_count());
|
|
}
|
|
|
|
// Cancel a pending socket request while we're at max sockets,
|
|
// and verify that the backup socket firing doesn't cause a crash.
|
|
TEST_F(ClientSocketPoolBaseTest, BackupSocketCancelAtMaxSockets) {
|
|
// Max 4 sockets globally, max 4 sockets per group.
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSockets,
|
|
true /* enable_backup_connect_jobs */);
|
|
|
|
// Create the first socket and set to ERR_IO_PENDING. This starts the backup
|
|
// timer.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("bar"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
// Start (MaxSockets - 1) connected sockets to reach max sockets.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
ClientSocketHandle handles[kDefaultMaxSockets];
|
|
for (int i = 1; i < kDefaultMaxSockets; ++i) {
|
|
EXPECT_EQ(OK, handles[i].Init(TestGroupId("bar"), params_, absl::nullopt,
|
|
DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
}
|
|
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
// Cancel the pending request.
|
|
handle.Reset();
|
|
|
|
// Wait for the backup timer to fire (add some slop to ensure it fires)
|
|
FastForwardBy(
|
|
base::Milliseconds(ClientSocketPool::kMaxConnectRetryIntervalMs / 2 * 3));
|
|
|
|
EXPECT_EQ(kDefaultMaxSockets, client_socket_factory_.allocation_count());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, CancelBackupSocketAfterCancelingAllRequests) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSockets,
|
|
true /* enable_backup_connect_jobs */);
|
|
|
|
// Create the first socket and set to ERR_IO_PENDING. This starts the backup
|
|
// timer.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("bar"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("bar")));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("bar")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("bar")));
|
|
EXPECT_EQ(
|
|
0u, pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("bar")));
|
|
|
|
// Cancel the socket request. This should cancel the backup timer. Wait for
|
|
// the backup time to see if it indeed got canceled.
|
|
handle.Reset();
|
|
// Wait for the backup timer to fire (add some slop to ensure it fires)
|
|
FastForwardBy(
|
|
base::Milliseconds(ClientSocketPool::kMaxConnectRetryIntervalMs / 2 * 3));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("bar")));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("bar")));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, CancelBackupSocketAfterFinishingAllRequests) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSockets,
|
|
true /* enable_backup_connect_jobs */);
|
|
|
|
// Create the first socket and set to ERR_IO_PENDING. This starts the backup
|
|
// timer.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("bar"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("bar"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("bar")));
|
|
EXPECT_EQ(2u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("bar")));
|
|
|
|
// Cancel request 1 and then complete request 2. With the requests finished,
|
|
// the backup timer should be cancelled.
|
|
handle.Reset();
|
|
EXPECT_THAT(callback2.WaitForResult(), IsOk());
|
|
// Wait for the backup timer to fire (add some slop to ensure it fires)
|
|
FastForwardBy(
|
|
base::Milliseconds(ClientSocketPool::kMaxConnectRetryIntervalMs / 2 * 3));
|
|
}
|
|
|
|
// Test delayed socket binding for the case where we have two connects,
|
|
// and while one is waiting on a connect, the other frees up.
|
|
// The socket waiting on a connect should switch immediately to the freed
|
|
// up socket.
|
|
TEST_F(ClientSocketPoolBaseTest, DelayedSocketBindingWaitingForConnect) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
|
|
// No idle sockets, no pending jobs.
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Create a second socket to the same host, but this one will wait.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
ClientSocketHandle handle2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
// No idle sockets, and one connecting job.
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Return the first handle to the pool. This will initiate the delayed
|
|
// binding.
|
|
handle1.Reset();
|
|
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
// Still no idle sockets, still one pending connect job.
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// The second socket connected, even though it was a Waiting Job.
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
|
|
// And we can see there is still one job waiting.
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Finally, signal the waiting Connect.
|
|
client_socket_factory_.SignalJobs();
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
base::RunLoop().RunUntilIdle();
|
|
}
|
|
|
|
// Test delayed socket binding when a group is at capacity and one
|
|
// of the group's sockets frees up.
|
|
TEST_F(ClientSocketPoolBaseTest, DelayedSocketBindingAtGroupCapacity) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
|
|
// No idle sockets, no pending jobs.
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Create a second socket to the same host, but this one will wait.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
ClientSocketHandle handle2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
// No idle sockets, and one connecting job.
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Return the first handle to the pool. This will initiate the delayed
|
|
// binding.
|
|
handle1.Reset();
|
|
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
// Still no idle sockets, still one pending connect job.
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// The second socket connected, even though it was a Waiting Job.
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
|
|
// And we can see there is still one job waiting.
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Finally, signal the waiting Connect.
|
|
client_socket_factory_.SignalJobs();
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
base::RunLoop().RunUntilIdle();
|
|
}
|
|
|
|
// Test out the case where we have one socket connected, one
|
|
// connecting, when the first socket finishes and goes idle.
|
|
// Although the second connection is pending, the second request
|
|
// should complete, by taking the first socket's idle socket.
|
|
TEST_F(ClientSocketPoolBaseTest, DelayedSocketBindingAtStall) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
|
|
// No idle sockets, no pending jobs.
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Create a second socket to the same host, but this one will wait.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
ClientSocketHandle handle2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
// No idle sockets, and one connecting job.
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Return the first handle to the pool. This will initiate the delayed
|
|
// binding.
|
|
handle1.Reset();
|
|
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
// Still no idle sockets, still one pending connect job.
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// The second socket connected, even though it was a Waiting Job.
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
|
|
// And we can see there is still one job waiting.
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Finally, signal the waiting Connect.
|
|
client_socket_factory_.SignalJobs();
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
base::RunLoop().RunUntilIdle();
|
|
}
|
|
|
|
// Cover the case where on an available socket slot, we have one pending
|
|
// request that completes synchronously, thereby making the Group empty.
|
|
TEST_F(ClientSocketPoolBaseTest, SynchronouslyProcessOnePendingRequest) {
|
|
const int kUnlimitedSockets = 100;
|
|
const int kOneSocketPerGroup = 1;
|
|
CreatePool(kUnlimitedSockets, kOneSocketPerGroup);
|
|
|
|
// Make the first request asynchronous fail.
|
|
// This will free up a socket slot later.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Make the second request synchronously fail. This should make the Group
|
|
// empty.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockFailingJob);
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
// It'll be ERR_IO_PENDING now, but the TestConnectJob will synchronously fail
|
|
// when created.
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
EXPECT_THAT(callback1.WaitForResult(), IsError(ERR_CONNECTION_FAILED));
|
|
EXPECT_THAT(callback2.WaitForResult(), IsError(ERR_CONNECTION_FAILED));
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, PreferUsedSocketToUnusedSocket) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSockets);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
ClientSocketHandle handle3;
|
|
TestCompletionCallback callback3;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle3.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback3.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_THAT(callback1.WaitForResult(), IsOk());
|
|
EXPECT_THAT(callback2.WaitForResult(), IsOk());
|
|
EXPECT_THAT(callback3.WaitForResult(), IsOk());
|
|
|
|
// Use the socket.
|
|
EXPECT_EQ(1, handle1.socket()->Write(nullptr, 1, CompletionOnceCallback(),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
EXPECT_EQ(1, handle3.socket()->Write(nullptr, 1, CompletionOnceCallback(),
|
|
TRAFFIC_ANNOTATION_FOR_TESTS));
|
|
|
|
handle1.Reset();
|
|
handle2.Reset();
|
|
handle3.Reset();
|
|
|
|
EXPECT_EQ(OK, handle1.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_EQ(OK, handle2.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_EQ(OK, handle3.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback3.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_TRUE(handle1.socket()->WasEverUsed());
|
|
EXPECT_TRUE(handle2.socket()->WasEverUsed());
|
|
EXPECT_FALSE(handle3.socket()->WasEverUsed());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, RequestSockets) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
TestCompletionCallback preconnect_callback;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 2,
|
|
preconnect_callback.callback(),
|
|
NetLogWithSource()));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(2u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(2u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(2u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(2u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
EXPECT_THAT(preconnect_callback.WaitForResult(), IsOk());
|
|
EXPECT_THAT(callback1.WaitForResult(), IsOk());
|
|
EXPECT_THAT(callback2.WaitForResult(), IsOk());
|
|
handle1.Reset();
|
|
handle2.Reset();
|
|
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(2u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, RequestSocketsWhenAlreadyHaveAConnectJob) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
TestCompletionCallback preconnect_callback;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 2,
|
|
preconnect_callback.callback(),
|
|
NetLogWithSource()));
|
|
|
|
EXPECT_EQ(2u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(1u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(2u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
EXPECT_THAT(preconnect_callback.WaitForResult(), IsOk());
|
|
EXPECT_THAT(callback1.WaitForResult(), IsOk());
|
|
EXPECT_THAT(callback2.WaitForResult(), IsOk());
|
|
handle1.Reset();
|
|
handle2.Reset();
|
|
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(2u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest,
|
|
RequestSocketsWhenAlreadyHaveMultipleConnectJob) {
|
|
CreatePool(4, 4);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
ClientSocketHandle handle3;
|
|
TestCompletionCallback callback3;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle3.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback3.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(3u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
EXPECT_EQ(
|
|
OK, pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 2,
|
|
CompletionOnceCallback(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(3u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
EXPECT_THAT(callback1.WaitForResult(), IsOk());
|
|
EXPECT_THAT(callback2.WaitForResult(), IsOk());
|
|
EXPECT_THAT(callback3.WaitForResult(), IsOk());
|
|
handle1.Reset();
|
|
handle2.Reset();
|
|
handle3.Reset();
|
|
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(3u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, RequestSocketsAtMaxSocketLimit) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSockets);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
ASSERT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
|
|
TestCompletionCallback preconnect_callback;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
pool_->RequestSockets(
|
|
TestGroupId("a"), params_, absl::nullopt, kDefaultMaxSockets,
|
|
preconnect_callback.callback(), NetLogWithSource()));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(kDefaultMaxSockets,
|
|
static_cast<int>(
|
|
pool_->NumConnectJobsInGroupForTesting(TestGroupId("a"))));
|
|
EXPECT_EQ(
|
|
kDefaultMaxSockets,
|
|
static_cast<int>(pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a"))));
|
|
EXPECT_EQ(kDefaultMaxSockets,
|
|
static_cast<int>(pool_->NumUnassignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a"))));
|
|
|
|
ASSERT_FALSE(pool_->HasGroupForTesting(TestGroupId("b")));
|
|
|
|
EXPECT_EQ(OK,
|
|
pool_->RequestSockets(TestGroupId("b"), params_, absl::nullopt,
|
|
kDefaultMaxSockets, CompletionOnceCallback(),
|
|
NetLogWithSource()));
|
|
|
|
ASSERT_FALSE(pool_->HasGroupForTesting(TestGroupId("b")));
|
|
|
|
EXPECT_THAT(preconnect_callback.WaitForResult(), IsOk());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, RequestSocketsHitMaxSocketLimit) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSockets);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
ASSERT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
|
|
TestCompletionCallback preconnect_callback1;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt,
|
|
kDefaultMaxSockets - 1,
|
|
preconnect_callback1.callback(),
|
|
NetLogWithSource()));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(kDefaultMaxSockets - 1,
|
|
static_cast<int>(
|
|
pool_->NumConnectJobsInGroupForTesting(TestGroupId("a"))));
|
|
EXPECT_EQ(
|
|
kDefaultMaxSockets - 1,
|
|
static_cast<int>(pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a"))));
|
|
EXPECT_EQ(kDefaultMaxSockets - 1,
|
|
static_cast<int>(pool_->NumUnassignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a"))));
|
|
EXPECT_FALSE(pool_->IsStalled());
|
|
|
|
ASSERT_FALSE(pool_->HasGroupForTesting(TestGroupId("b")));
|
|
|
|
TestCompletionCallback preconnect_callback2;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
pool_->RequestSockets(
|
|
TestGroupId("b"), params_, absl::nullopt, kDefaultMaxSockets,
|
|
preconnect_callback2.callback(), NetLogWithSource()));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("b")));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("b")));
|
|
EXPECT_FALSE(pool_->IsStalled());
|
|
|
|
EXPECT_THAT(preconnect_callback1.WaitForResult(), IsOk());
|
|
EXPECT_THAT(preconnect_callback2.WaitForResult(), IsOk());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, RequestSocketsCountIdleSockets) {
|
|
CreatePool(4, 4);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
ASSERT_THAT(callback1.WaitForResult(), IsOk());
|
|
handle1.Reset();
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
TestCompletionCallback preconnect_callback;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 2,
|
|
preconnect_callback.callback(),
|
|
NetLogWithSource()));
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(1u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
EXPECT_THAT(preconnect_callback.WaitForResult(), IsOk());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, RequestSocketsCountActiveSockets) {
|
|
CreatePool(4, 4);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
ASSERT_THAT(callback1.WaitForResult(), IsOk());
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
|
|
TestCompletionCallback preconnect_callback;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 2,
|
|
preconnect_callback.callback(),
|
|
NetLogWithSource()));
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(1u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
|
|
EXPECT_THAT(preconnect_callback.WaitForResult(), IsOk());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, RequestSocketsSynchronous) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
|
|
EXPECT_EQ(
|
|
OK, pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt,
|
|
kDefaultMaxSocketsPerGroup,
|
|
CompletionOnceCallback(), NetLogWithSource()));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(kDefaultMaxSocketsPerGroup,
|
|
static_cast<int>(pool_->IdleSocketCountInGroup(TestGroupId("a"))));
|
|
|
|
EXPECT_EQ(
|
|
OK, pool_->RequestSockets(TestGroupId("b"), params_, absl::nullopt,
|
|
kDefaultMaxSocketsPerGroup,
|
|
CompletionOnceCallback(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("b")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("b")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("b")));
|
|
EXPECT_EQ(kDefaultMaxSocketsPerGroup,
|
|
static_cast<int>(pool_->IdleSocketCountInGroup(TestGroupId("b"))));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, RequestSocketsSynchronousError) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockFailingJob);
|
|
|
|
EXPECT_EQ(
|
|
OK, pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt,
|
|
kDefaultMaxSocketsPerGroup,
|
|
CompletionOnceCallback(), NetLogWithSource()));
|
|
|
|
ASSERT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
|
|
connect_job_factory_->set_job_type(
|
|
TestConnectJob::kMockAdditionalErrorStateJob);
|
|
|
|
EXPECT_EQ(
|
|
OK, pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt,
|
|
kDefaultMaxSocketsPerGroup,
|
|
CompletionOnceCallback(), NetLogWithSource()));
|
|
|
|
ASSERT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, RequestSocketsMultipleTimesDoesNothing) {
|
|
CreatePool(4, 4);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
TestCompletionCallback preconnect_callback;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 2,
|
|
preconnect_callback.callback(),
|
|
NetLogWithSource()));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(2u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(2u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(2u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
EXPECT_EQ(
|
|
OK, pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 2,
|
|
CompletionOnceCallback(), NetLogWithSource()));
|
|
EXPECT_EQ(2u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(2u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(2u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
client_socket_factory_.SignalJob(0);
|
|
EXPECT_THAT(callback1.WaitForResult(), IsOk());
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(1u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
client_socket_factory_.SignalJob(0);
|
|
EXPECT_THAT(callback2.WaitForResult(), IsOk());
|
|
EXPECT_THAT(preconnect_callback.WaitForResult(), IsOk());
|
|
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(2, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
handle1.Reset();
|
|
handle2.Reset();
|
|
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(2u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
EXPECT_EQ(
|
|
OK, pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 2,
|
|
CompletionOnceCallback(), NetLogWithSource()));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(2u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, RequestSocketsDifferentNumSockets) {
|
|
CreatePool(4, 4);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
TestCompletionCallback preconnect_callback1;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 1,
|
|
preconnect_callback1.callback(),
|
|
NetLogWithSource()));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(1u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
TestCompletionCallback preconnect_callback2;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 2,
|
|
preconnect_callback2.callback(),
|
|
NetLogWithSource()));
|
|
EXPECT_EQ(2u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(2u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(2u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
TestCompletionCallback preconnect_callback3;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 3,
|
|
preconnect_callback3.callback(),
|
|
NetLogWithSource()));
|
|
EXPECT_EQ(3u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(3u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(3u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
EXPECT_EQ(
|
|
OK, pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 1,
|
|
CompletionOnceCallback(), NetLogWithSource()));
|
|
EXPECT_EQ(3u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(3u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(3u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, PreconnectJobsTakenByNormalRequests) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
TestCompletionCallback preconnect_callback;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 1,
|
|
preconnect_callback.callback(),
|
|
NetLogWithSource()));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(1u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
client_socket_factory_.SignalJobs();
|
|
EXPECT_THAT(callback1.WaitForResult(), IsOk());
|
|
EXPECT_THAT(preconnect_callback.WaitForResult(), IsOk());
|
|
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Make sure if a preconnected socket is not fully connected when a request
|
|
// starts, it has a connect start time.
|
|
TestLoadTimingInfoConnectedNotReused(handle1);
|
|
handle1.Reset();
|
|
|
|
EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
}
|
|
|
|
// Checks that fully connected preconnect jobs have no connect times, and are
|
|
// marked as reused.
|
|
TEST_F(ClientSocketPoolBaseTest, ConnectedPreconnectJobsHaveNoConnectTimes) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
|
|
EXPECT_EQ(
|
|
OK, pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 1,
|
|
CompletionOnceCallback(), NetLogWithSource()));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(OK, handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
// Make sure the idle socket was used.
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
TestLoadTimingInfoConnectedReused(handle);
|
|
handle.Reset();
|
|
TestLoadTimingInfoNotConnected(handle);
|
|
}
|
|
|
|
// http://crbug.com/64940 regression test.
|
|
TEST_F(ClientSocketPoolBaseTest, PreconnectClosesIdleSocketRemovesGroup) {
|
|
const int kMaxTotalSockets = 3;
|
|
const int kMaxSocketsPerGroup = 2;
|
|
CreatePool(kMaxTotalSockets, kMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
// Note that group id ordering matters here. "a" comes before "b", so
|
|
// CloseOneIdleSocket() will try to close "a"'s idle socket.
|
|
|
|
// Set up one idle socket in "a".
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
client_socket_factory_.SignalJobs();
|
|
ASSERT_THAT(callback1.WaitForResult(), IsOk());
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
|
|
handle1.Reset();
|
|
EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
// Set up two active sockets in "b".
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("b"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("b"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("b")));
|
|
EXPECT_EQ(2u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("b")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("b")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("b")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("b")));
|
|
|
|
client_socket_factory_.SignalJobs();
|
|
ASSERT_THAT(callback1.WaitForResult(), IsOk());
|
|
ASSERT_THAT(callback2.WaitForResult(), IsOk());
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("b")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("b")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("b")));
|
|
EXPECT_EQ(2, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("b")));
|
|
|
|
// Now we have 1 idle socket in "a" and 2 active sockets in "b". This means
|
|
// we've maxed out on sockets, since we set |kMaxTotalSockets| to 3.
|
|
// Requesting 2 preconnected sockets for "a" should fail to allocate any more
|
|
// sockets for "a", and "b" should still have 2 active sockets.
|
|
|
|
EXPECT_EQ(
|
|
OK, pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 2,
|
|
CompletionOnceCallback(), NetLogWithSource()));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("b")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("b")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("b")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("b")));
|
|
EXPECT_EQ(2, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("b")));
|
|
|
|
// Now release the 2 active sockets for "b". This will give us 1 idle socket
|
|
// in "a" and 2 idle sockets in "b". Requesting 2 preconnected sockets for
|
|
// "a" should result in closing 1 for "b".
|
|
handle1.Reset();
|
|
handle2.Reset();
|
|
EXPECT_EQ(2u, pool_->IdleSocketCountInGroup(TestGroupId("b")));
|
|
EXPECT_EQ(0, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("b")));
|
|
|
|
TestCompletionCallback preconnect_callback;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 2,
|
|
preconnect_callback.callback(),
|
|
NetLogWithSource()));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(1u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("b")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("b")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("b")));
|
|
EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(TestGroupId("b")));
|
|
EXPECT_EQ(0, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("b")));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, PreconnectWithoutBackupJob) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup,
|
|
true /* enable_backup_connect_jobs */);
|
|
|
|
// Make the ConnectJob hang until it times out, shorten the timeout.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
connect_job_factory_->set_timeout_duration(base::Milliseconds(500));
|
|
TestCompletionCallback preconnect_callback;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 1,
|
|
preconnect_callback.callback(),
|
|
NetLogWithSource()));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(1u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
// Verify the backup timer doesn't create a backup job, by making
|
|
// the backup job a pending job instead of a waiting job, so it
|
|
// *would* complete if it were created.
|
|
base::RunLoop loop;
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
|
|
FROM_HERE, loop.QuitClosure(), base::Seconds(1));
|
|
loop.Run();
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, PreconnectWithBackupJob) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup,
|
|
true /* enable_backup_connect_jobs */);
|
|
|
|
// Make the ConnectJob hang forever.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
TestCompletionCallback preconnect_callback;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 1,
|
|
preconnect_callback.callback(),
|
|
NetLogWithSource()));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(1u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
// Make the backup job be a pending job, so it completes normally.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
// Timer has started, but the backup connect job shouldn't be created yet.
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
ASSERT_THAT(callback.WaitForResult(), IsOk());
|
|
|
|
// The hung connect job should still be there, but everything else should be
|
|
// complete.
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(1u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
}
|
|
|
|
// Tests that a preconnect that starts out with unread data can still be used.
|
|
// http://crbug.com/334467
|
|
TEST_F(ClientSocketPoolBaseTest, PreconnectWithUnreadData) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockUnreadDataJob);
|
|
|
|
EXPECT_EQ(
|
|
OK, pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 1,
|
|
CompletionOnceCallback(), NetLogWithSource()));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
// Fail future jobs to be sure that handle receives the preconnected socket
|
|
// rather than closing it and making a new one.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockFailingJob);
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(OK, handle.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Drain the pending read.
|
|
EXPECT_EQ(1, handle.socket()->Read(nullptr, 1, CompletionOnceCallback()));
|
|
|
|
TestLoadTimingInfoConnectedReused(handle);
|
|
handle.Reset();
|
|
|
|
// The socket should be usable now that it's idle again.
|
|
EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, RequestGetsAssignedJob) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle1));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, MultipleRequestsGetAssignedJobs) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(2u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle1));
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle2));
|
|
|
|
// One job completes. The other request should still have its job.
|
|
client_socket_factory_.SignalJob(0);
|
|
EXPECT_THAT(callback1.WaitForResult(), IsOk());
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle2));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, PreconnectJobGetsAssignedToRequest) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
TestCompletionCallback preconnect_callback;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
pool_->RequestSockets(TestGroupId("a"), params_, absl::nullopt, 1,
|
|
preconnect_callback.callback(),
|
|
NetLogWithSource()));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(1u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle1));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, HigherPriorityRequestStealsJob) {
|
|
CreatePool(kDefaultMaxSockets, 1);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle1));
|
|
|
|
// Insert a higher priority request
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("a"), params_, absl::nullopt, HIGHEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
// The highest priority request should steal the job from the default priority
|
|
// request.
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle2));
|
|
EXPECT_FALSE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle1));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, RequestStealsJobFromLowestRequestWithJob) {
|
|
CreatePool(3, 3);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
ClientSocketHandle handle_lowest;
|
|
TestCompletionCallback callback_lowest;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle_lowest.Init(TestGroupId("a"), params_, absl::nullopt, LOWEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback_lowest.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(),
|
|
NetLogWithSource()));
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
ClientSocketHandle handle_highest;
|
|
TestCompletionCallback callback_highest;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle_highest.Init(TestGroupId("a"), params_, absl::nullopt, HIGHEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback_highest.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(),
|
|
NetLogWithSource()));
|
|
|
|
EXPECT_EQ(2u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
ClientSocketHandle handle_low;
|
|
TestCompletionCallback callback_low;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
handle_low.Init(
|
|
TestGroupId("a"), params_, absl::nullopt, LOW, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED,
|
|
callback_low.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(3u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
ClientSocketHandle handle_lowest2;
|
|
TestCompletionCallback callback_lowest2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle_lowest2.Init(TestGroupId("a"), params_, absl::nullopt, LOWEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback_lowest2.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(),
|
|
NetLogWithSource()));
|
|
|
|
EXPECT_EQ(3u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
// The top three requests in the queue should have jobs.
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle_highest));
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle_low));
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle_lowest));
|
|
EXPECT_FALSE(pool_->RequestInGroupWithHandleHasJobForTesting(
|
|
TestGroupId("a"), &handle_lowest2));
|
|
|
|
// Add another request with medium priority. It should steal the job from the
|
|
// lowest priority request with a job.
|
|
ClientSocketHandle handle_medium;
|
|
TestCompletionCallback callback_medium;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle_medium.Init(TestGroupId("a"), params_, absl::nullopt, MEDIUM,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback_medium.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(),
|
|
NetLogWithSource()));
|
|
|
|
EXPECT_EQ(3u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle_highest));
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle_medium));
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle_low));
|
|
EXPECT_FALSE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle_lowest));
|
|
EXPECT_FALSE(pool_->RequestInGroupWithHandleHasJobForTesting(
|
|
TestGroupId("a"), &handle_lowest2));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, ReprioritizeRequestStealsJob) {
|
|
CreatePool(kDefaultMaxSockets, 1);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
// The second request doesn't get a job because we are at the limit.
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle1));
|
|
EXPECT_FALSE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle2));
|
|
|
|
// Reprioritizing the second request places it above the first, and it steals
|
|
// the job from the first request.
|
|
pool_->SetPriority(TestGroupId("a"), &handle2, HIGHEST);
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle2));
|
|
EXPECT_FALSE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle1));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, CancelRequestReassignsJob) {
|
|
CreatePool(kDefaultMaxSockets, 1);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle1));
|
|
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
// The second request doesn't get a job because we are the limit.
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle1));
|
|
EXPECT_FALSE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle2));
|
|
|
|
// The second request should get a job upon cancelling the first request.
|
|
handle1.Reset();
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle2));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, JobCompletionReassignsJob) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle1.Init(TestGroupId("a"), params_, absl::nullopt, HIGHEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
ClientSocketHandle handle2;
|
|
TestCompletionCallback callback2;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle2.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(2u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle1));
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle2));
|
|
|
|
// The lower-priority job completes first. The higher-priority request should
|
|
// get the socket, and the lower-priority request should get the remaining
|
|
// job.
|
|
client_socket_factory_.SignalJob(1);
|
|
EXPECT_THAT(callback1.WaitForResult(), IsOk());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumNeverAssignedConnectJobsInGroupForTesting(
|
|
TestGroupId("a")));
|
|
EXPECT_EQ(0u,
|
|
pool_->NumUnassignedConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_TRUE(handle1.socket());
|
|
EXPECT_TRUE(pool_->RequestInGroupWithHandleHasJobForTesting(TestGroupId("a"),
|
|
&handle2));
|
|
}
|
|
|
|
class MockLayeredPool : public HigherLayeredPool {
|
|
public:
|
|
MockLayeredPool(TransportClientSocketPool* pool,
|
|
const ClientSocketPool::GroupId& group_id)
|
|
: pool_(pool), group_id_(group_id) {
|
|
pool_->AddHigherLayeredPool(this);
|
|
}
|
|
|
|
~MockLayeredPool() override { pool_->RemoveHigherLayeredPool(this); }
|
|
|
|
int RequestSocket(TransportClientSocketPool* pool) {
|
|
return handle_.Init(
|
|
group_id_, ClientSocketPool::SocketParams::CreateForHttpForTesting(),
|
|
absl::nullopt, DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED, callback_.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool, NetLogWithSource());
|
|
}
|
|
|
|
int RequestSocketWithoutLimits(TransportClientSocketPool* pool) {
|
|
return handle_.Init(
|
|
group_id_, ClientSocketPool::SocketParams::CreateForHttpForTesting(),
|
|
absl::nullopt, MAXIMUM_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::DISABLED, callback_.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool, NetLogWithSource());
|
|
}
|
|
|
|
bool ReleaseOneConnection() {
|
|
if (!handle_.is_initialized() || !can_release_connection_) {
|
|
return false;
|
|
}
|
|
handle_.socket()->Disconnect();
|
|
handle_.Reset();
|
|
return true;
|
|
}
|
|
|
|
void set_can_release_connection(bool can_release_connection) {
|
|
can_release_connection_ = can_release_connection;
|
|
}
|
|
|
|
MOCK_METHOD0(CloseOneIdleConnection, bool());
|
|
|
|
private:
|
|
const raw_ptr<TransportClientSocketPool> pool_;
|
|
ClientSocketHandle handle_;
|
|
TestCompletionCallback callback_;
|
|
const ClientSocketPool::GroupId group_id_;
|
|
bool can_release_connection_ = true;
|
|
};
|
|
|
|
// Tests the basic case of closing an idle socket in a higher layered pool when
|
|
// a new request is issued and the lower layer pool is stalled.
|
|
TEST_F(ClientSocketPoolBaseTest, CloseIdleSocketsHeldByLayeredPoolWhenNeeded) {
|
|
CreatePool(1, 1);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
|
|
MockLayeredPool mock_layered_pool(pool_.get(), TestGroupId("foo"));
|
|
EXPECT_THAT(mock_layered_pool.RequestSocket(pool_.get()), IsOk());
|
|
EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection())
|
|
.WillOnce(Invoke(&mock_layered_pool,
|
|
&MockLayeredPool::ReleaseOneConnection));
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
}
|
|
|
|
// Tests the case that trying to close an idle socket in a higher layered pool
|
|
// fails.
|
|
TEST_F(ClientSocketPoolBaseTest,
|
|
CloseIdleSocketsHeldByLayeredPoolWhenNeededFails) {
|
|
CreatePool(1, 1);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
|
|
MockLayeredPool mock_layered_pool(pool_.get(), TestGroupId("foo"));
|
|
mock_layered_pool.set_can_release_connection(false);
|
|
EXPECT_THAT(mock_layered_pool.RequestSocket(pool_.get()), IsOk());
|
|
EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection())
|
|
.WillOnce(Invoke(&mock_layered_pool,
|
|
&MockLayeredPool::ReleaseOneConnection));
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_FALSE(callback.have_result());
|
|
}
|
|
|
|
// Same as above, but the idle socket is in the same group as the stalled
|
|
// socket, and closes the only other request in its group when closing requests
|
|
// in higher layered pools. This generally shouldn't happen, but it may be
|
|
// possible if a higher level pool issues a request and the request is
|
|
// subsequently cancelled. Even if it's not possible, best not to crash.
|
|
TEST_F(ClientSocketPoolBaseTest,
|
|
CloseIdleSocketsHeldByLayeredPoolWhenNeededSameGroup) {
|
|
CreatePool(2, 2);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
|
|
// Need a socket in another group for the pool to be stalled (If a group
|
|
// has the maximum number of connections already, it's not stalled).
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(OK, handle1.Init(TestGroupId("group1"), params_, absl::nullopt,
|
|
DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(),
|
|
NetLogWithSource()));
|
|
|
|
MockLayeredPool mock_layered_pool(pool_.get(), TestGroupId("group2"));
|
|
EXPECT_THAT(mock_layered_pool.RequestSocket(pool_.get()), IsOk());
|
|
EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection())
|
|
.WillOnce(Invoke(&mock_layered_pool,
|
|
&MockLayeredPool::ReleaseOneConnection));
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback2;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
handle.Init(
|
|
TestGroupId("group2"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback2.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_THAT(callback2.WaitForResult(), IsOk());
|
|
}
|
|
|
|
// Tests the case when an idle socket can be closed when a new request is
|
|
// issued, and the new request belongs to a group that was previously stalled.
|
|
TEST_F(ClientSocketPoolBaseTest,
|
|
CloseIdleSocketsHeldByLayeredPoolInSameGroupWhenNeeded) {
|
|
CreatePool(2, 2);
|
|
std::list<TestConnectJob::JobType> job_types;
|
|
job_types.push_back(TestConnectJob::kMockJob);
|
|
job_types.push_back(TestConnectJob::kMockJob);
|
|
job_types.push_back(TestConnectJob::kMockJob);
|
|
job_types.push_back(TestConnectJob::kMockJob);
|
|
connect_job_factory_->set_job_types(&job_types);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(OK, handle1.Init(TestGroupId("group1"), params_, absl::nullopt,
|
|
DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(),
|
|
NetLogWithSource()));
|
|
|
|
MockLayeredPool mock_layered_pool(pool_.get(), TestGroupId("group2"));
|
|
EXPECT_THAT(mock_layered_pool.RequestSocket(pool_.get()), IsOk());
|
|
EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection())
|
|
.WillRepeatedly(Invoke(&mock_layered_pool,
|
|
&MockLayeredPool::ReleaseOneConnection));
|
|
mock_layered_pool.set_can_release_connection(false);
|
|
|
|
// The third request is made when the socket pool is in a stalled state.
|
|
ClientSocketHandle handle3;
|
|
TestCompletionCallback callback3;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
handle3.Init(
|
|
TestGroupId("group3"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback3.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_FALSE(callback3.have_result());
|
|
|
|
// The fourth request is made when the pool is no longer stalled. The third
|
|
// request should be serviced first, since it was issued first and has the
|
|
// same priority.
|
|
mock_layered_pool.set_can_release_connection(true);
|
|
ClientSocketHandle handle4;
|
|
TestCompletionCallback callback4;
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
handle4.Init(
|
|
TestGroupId("group3"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback4.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_THAT(callback3.WaitForResult(), IsOk());
|
|
EXPECT_FALSE(callback4.have_result());
|
|
|
|
// Closing a handle should free up another socket slot.
|
|
handle1.Reset();
|
|
EXPECT_THAT(callback4.WaitForResult(), IsOk());
|
|
}
|
|
|
|
// Tests the case when an idle socket can be closed when a new request is
|
|
// issued, and the new request belongs to a group that was previously stalled.
|
|
//
|
|
// The two differences from the above test are that the stalled requests are not
|
|
// in the same group as the layered pool's request, and the the fourth request
|
|
// has a higher priority than the third one, so gets a socket first.
|
|
TEST_F(ClientSocketPoolBaseTest,
|
|
CloseIdleSocketsHeldByLayeredPoolInSameGroupWhenNeeded2) {
|
|
CreatePool(2, 2);
|
|
std::list<TestConnectJob::JobType> job_types;
|
|
job_types.push_back(TestConnectJob::kMockJob);
|
|
job_types.push_back(TestConnectJob::kMockJob);
|
|
job_types.push_back(TestConnectJob::kMockJob);
|
|
job_types.push_back(TestConnectJob::kMockJob);
|
|
connect_job_factory_->set_job_types(&job_types);
|
|
|
|
ClientSocketHandle handle1;
|
|
TestCompletionCallback callback1;
|
|
EXPECT_EQ(OK, handle1.Init(TestGroupId("group1"), params_, absl::nullopt,
|
|
DEFAULT_PRIORITY, SocketTag(),
|
|
ClientSocketPool::RespectLimits::ENABLED,
|
|
callback1.callback(),
|
|
ClientSocketPool::ProxyAuthCallback(), pool_.get(),
|
|
NetLogWithSource()));
|
|
|
|
MockLayeredPool mock_layered_pool(pool_.get(), TestGroupId("group2"));
|
|
EXPECT_THAT(mock_layered_pool.RequestSocket(pool_.get()), IsOk());
|
|
EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection())
|
|
.WillRepeatedly(Invoke(&mock_layered_pool,
|
|
&MockLayeredPool::ReleaseOneConnection));
|
|
mock_layered_pool.set_can_release_connection(false);
|
|
|
|
// The third request is made when the socket pool is in a stalled state.
|
|
ClientSocketHandle handle3;
|
|
TestCompletionCallback callback3;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle3.Init(TestGroupId("group3"), params_, absl::nullopt, MEDIUM,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback3.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_FALSE(callback3.have_result());
|
|
|
|
// The fourth request is made when the pool is no longer stalled. This
|
|
// request has a higher priority than the third request, so is serviced first.
|
|
mock_layered_pool.set_can_release_connection(true);
|
|
ClientSocketHandle handle4;
|
|
TestCompletionCallback callback4;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle4.Init(TestGroupId("group3"), params_, absl::nullopt, HIGHEST,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback4.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_THAT(callback4.WaitForResult(), IsOk());
|
|
EXPECT_FALSE(callback3.have_result());
|
|
|
|
// Closing a handle should free up another socket slot.
|
|
handle1.Reset();
|
|
EXPECT_THAT(callback3.WaitForResult(), IsOk());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest,
|
|
CloseMultipleIdleSocketsHeldByLayeredPoolWhenNeeded) {
|
|
CreatePool(1, 1);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
|
|
MockLayeredPool mock_layered_pool1(pool_.get(), TestGroupId("foo"));
|
|
EXPECT_THAT(mock_layered_pool1.RequestSocket(pool_.get()), IsOk());
|
|
EXPECT_CALL(mock_layered_pool1, CloseOneIdleConnection())
|
|
.WillRepeatedly(Invoke(&mock_layered_pool1,
|
|
&MockLayeredPool::ReleaseOneConnection));
|
|
MockLayeredPool mock_layered_pool2(pool_.get(), TestGroupId("bar"));
|
|
EXPECT_THAT(mock_layered_pool2.RequestSocketWithoutLimits(pool_.get()),
|
|
IsOk());
|
|
EXPECT_CALL(mock_layered_pool2, CloseOneIdleConnection())
|
|
.WillRepeatedly(Invoke(&mock_layered_pool2,
|
|
&MockLayeredPool::ReleaseOneConnection));
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
EXPECT_THAT(callback.WaitForResult(), IsOk());
|
|
}
|
|
|
|
// Test that when a socket pool and group are at their limits, a request
|
|
// with RespectLimits::DISABLED triggers creation of a new socket, and gets the
|
|
// socket instead of a request with the same priority that was issued earlier,
|
|
// but has RespectLimits::ENABLED.
|
|
TEST_F(ClientSocketPoolBaseTest, IgnoreLimits) {
|
|
CreatePool(1, 1);
|
|
|
|
// Issue a request to reach the socket pool limit.
|
|
EXPECT_EQ(OK, StartRequestWithIgnoreLimits(
|
|
TestGroupId("a"), MAXIMUM_PRIORITY,
|
|
ClientSocketPool::RespectLimits::ENABLED));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
EXPECT_EQ(ERR_IO_PENDING, StartRequestWithIgnoreLimits(
|
|
TestGroupId("a"), MAXIMUM_PRIORITY,
|
|
ClientSocketPool::RespectLimits::ENABLED));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Issue a request that ignores the limits, so a new ConnectJob is
|
|
// created.
|
|
EXPECT_EQ(ERR_IO_PENDING, StartRequestWithIgnoreLimits(
|
|
TestGroupId("a"), MAXIMUM_PRIORITY,
|
|
ClientSocketPool::RespectLimits::DISABLED));
|
|
ASSERT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
EXPECT_THAT(request(2)->WaitForResult(), IsOk());
|
|
EXPECT_FALSE(request(1)->have_result());
|
|
}
|
|
|
|
// Test that when a socket pool and group are at their limits, a ConnectJob
|
|
// issued for a request with RespectLimits::DISABLED is not cancelled when a
|
|
// request with RespectLimits::ENABLED issued to the same group is cancelled.
|
|
TEST_F(ClientSocketPoolBaseTest, IgnoreLimitsCancelOtherJob) {
|
|
CreatePool(1, 1);
|
|
|
|
// Issue a request to reach the socket pool limit.
|
|
EXPECT_EQ(OK, StartRequestWithIgnoreLimits(
|
|
TestGroupId("a"), MAXIMUM_PRIORITY,
|
|
ClientSocketPool::RespectLimits::ENABLED));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
|
|
EXPECT_EQ(ERR_IO_PENDING, StartRequestWithIgnoreLimits(
|
|
TestGroupId("a"), MAXIMUM_PRIORITY,
|
|
ClientSocketPool::RespectLimits::ENABLED));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Issue a request with RespectLimits::DISABLED, so a new ConnectJob is
|
|
// created.
|
|
EXPECT_EQ(ERR_IO_PENDING, StartRequestWithIgnoreLimits(
|
|
TestGroupId("a"), MAXIMUM_PRIORITY,
|
|
ClientSocketPool::RespectLimits::DISABLED));
|
|
ASSERT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Cancel the pending request with RespectLimits::ENABLED. The ConnectJob
|
|
// should not be cancelled.
|
|
request(1)->handle()->Reset();
|
|
ASSERT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
EXPECT_THAT(request(2)->WaitForResult(), IsOk());
|
|
EXPECT_FALSE(request(1)->have_result());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, ProxyAuthNoAuthCallback) {
|
|
CreatePool(1, 1);
|
|
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockAuthChallengeOnceJob);
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_EQ(
|
|
ERR_IO_PENDING,
|
|
handle.Init(TestGroupId("a"), params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
EXPECT_THAT(callback.WaitForResult(), IsError(ERR_PROXY_AUTH_REQUESTED));
|
|
EXPECT_FALSE(handle.is_initialized());
|
|
EXPECT_FALSE(handle.socket());
|
|
|
|
// The group should now be empty, and thus be deleted.
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
}
|
|
|
|
class TestAuthHelper {
|
|
public:
|
|
TestAuthHelper() = default;
|
|
|
|
TestAuthHelper(const TestAuthHelper&) = delete;
|
|
TestAuthHelper& operator=(const TestAuthHelper&) = delete;
|
|
|
|
~TestAuthHelper() = default;
|
|
|
|
void InitHandle(
|
|
scoped_refptr<ClientSocketPool::SocketParams> params,
|
|
TransportClientSocketPool* pool,
|
|
RequestPriority priority = DEFAULT_PRIORITY,
|
|
ClientSocketPool::RespectLimits respect_limits =
|
|
ClientSocketPool::RespectLimits::ENABLED,
|
|
const ClientSocketPool::GroupId& group_id_in = TestGroupId("a")) {
|
|
EXPECT_EQ(ERR_IO_PENDING,
|
|
handle_.Init(group_id_in, params, absl::nullopt, priority,
|
|
SocketTag(), respect_limits, callback_.callback(),
|
|
base::BindRepeating(&TestAuthHelper::AuthCallback,
|
|
base::Unretained(this)),
|
|
pool, NetLogWithSource()));
|
|
}
|
|
|
|
void WaitForAuth() {
|
|
run_loop_ = std::make_unique<base::RunLoop>();
|
|
run_loop_->Run();
|
|
run_loop_.reset();
|
|
}
|
|
|
|
void WaitForAuthAndRestartSync() {
|
|
restart_sync_ = true;
|
|
WaitForAuth();
|
|
restart_sync_ = false;
|
|
}
|
|
|
|
void WaitForAuthAndResetHandleSync() {
|
|
reset_handle_sync_ = true;
|
|
WaitForAuth();
|
|
reset_handle_sync_ = false;
|
|
}
|
|
|
|
void RestartWithAuth() {
|
|
DCHECK(restart_with_auth_callback_);
|
|
std::move(restart_with_auth_callback_).Run();
|
|
}
|
|
|
|
int WaitForResult() {
|
|
int result = callback_.WaitForResult();
|
|
// There shouldn't be any callback waiting to be invoked once the request is
|
|
// complete.
|
|
EXPECT_FALSE(restart_with_auth_callback_);
|
|
// The socket should only be initialized on success.
|
|
EXPECT_EQ(result == OK, handle_.is_initialized());
|
|
EXPECT_EQ(result == OK, handle_.socket() != nullptr);
|
|
return result;
|
|
}
|
|
|
|
ClientSocketHandle* handle() { return &handle_; }
|
|
int auth_count() const { return auth_count_; }
|
|
int have_result() const { return callback_.have_result(); }
|
|
|
|
private:
|
|
void AuthCallback(const HttpResponseInfo& response,
|
|
HttpAuthController* auth_controller,
|
|
base::OnceClosure restart_with_auth_callback) {
|
|
EXPECT_FALSE(restart_with_auth_callback_);
|
|
EXPECT_TRUE(restart_with_auth_callback);
|
|
|
|
// Once there's a result, this method shouldn't be invoked again.
|
|
EXPECT_FALSE(callback_.have_result());
|
|
|
|
++auth_count_;
|
|
run_loop_->Quit();
|
|
if (restart_sync_) {
|
|
std::move(restart_with_auth_callback).Run();
|
|
return;
|
|
}
|
|
|
|
restart_with_auth_callback_ = std::move(restart_with_auth_callback);
|
|
|
|
if (reset_handle_sync_) {
|
|
handle_.Reset();
|
|
return;
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<base::RunLoop> run_loop_;
|
|
base::OnceClosure restart_with_auth_callback_;
|
|
|
|
bool restart_sync_ = false;
|
|
bool reset_handle_sync_ = false;
|
|
|
|
ClientSocketHandle handle_;
|
|
int auth_count_ = 0;
|
|
TestCompletionCallback callback_;
|
|
};
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, ProxyAuthOnce) {
|
|
CreatePool(1, 1);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockAuthChallengeOnceJob);
|
|
|
|
TestAuthHelper auth_helper;
|
|
auth_helper.InitHandle(params_, pool_.get());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING,
|
|
pool_->GetLoadState(TestGroupId("a"), auth_helper.handle()));
|
|
|
|
auth_helper.WaitForAuth();
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
|
|
pool_->GetLoadState(TestGroupId("a"), auth_helper.handle()));
|
|
|
|
auth_helper.RestartWithAuth();
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
|
|
pool_->GetLoadState(TestGroupId("a"), auth_helper.handle()));
|
|
|
|
EXPECT_THAT(auth_helper.WaitForResult(), IsOk());
|
|
EXPECT_EQ(1, auth_helper.auth_count());
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, ProxyAuthOnceSync) {
|
|
CreatePool(1, 1);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockAuthChallengeOnceJob);
|
|
|
|
TestAuthHelper auth_helper;
|
|
auth_helper.InitHandle(params_, pool_.get());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING,
|
|
pool_->GetLoadState(TestGroupId("a"), auth_helper.handle()));
|
|
|
|
auth_helper.WaitForAuthAndRestartSync();
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
|
|
pool_->GetLoadState(TestGroupId("a"), auth_helper.handle()));
|
|
|
|
EXPECT_THAT(auth_helper.WaitForResult(), IsOk());
|
|
EXPECT_EQ(1, auth_helper.auth_count());
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, ProxyAuthOnceFails) {
|
|
CreatePool(1, 1);
|
|
connect_job_factory_->set_job_type(
|
|
TestConnectJob::kMockAuthChallengeOnceFailingJob);
|
|
|
|
TestAuthHelper auth_helper;
|
|
auth_helper.InitHandle(params_, pool_.get());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
auth_helper.WaitForAuth();
|
|
auth_helper.RestartWithAuth();
|
|
EXPECT_THAT(auth_helper.WaitForResult(), IsError(ERR_CONNECTION_FAILED));
|
|
|
|
EXPECT_EQ(1, auth_helper.auth_count());
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, ProxyAuthOnceSyncFails) {
|
|
CreatePool(1, 1);
|
|
connect_job_factory_->set_job_type(
|
|
TestConnectJob::kMockAuthChallengeOnceFailingJob);
|
|
|
|
TestAuthHelper auth_helper;
|
|
auth_helper.InitHandle(params_, pool_.get());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
auth_helper.WaitForAuthAndRestartSync();
|
|
EXPECT_THAT(auth_helper.WaitForResult(), IsError(ERR_CONNECTION_FAILED));
|
|
|
|
EXPECT_EQ(1, auth_helper.auth_count());
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, ProxyAuthOnceDeleteHandle) {
|
|
CreatePool(1, 1);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockAuthChallengeOnceJob);
|
|
|
|
TestAuthHelper auth_helper;
|
|
auth_helper.InitHandle(params_, pool_.get());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
auth_helper.WaitForAuth();
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
auth_helper.handle()->Reset();
|
|
|
|
EXPECT_EQ(1, auth_helper.auth_count());
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_FALSE(auth_helper.handle()->is_initialized());
|
|
EXPECT_FALSE(auth_helper.handle()->socket());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, ProxyAuthOnceDeleteHandleSync) {
|
|
CreatePool(1, 1);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockAuthChallengeOnceJob);
|
|
|
|
TestAuthHelper auth_helper;
|
|
auth_helper.InitHandle(params_, pool_.get());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
auth_helper.WaitForAuthAndResetHandleSync();
|
|
EXPECT_EQ(1, auth_helper.auth_count());
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_FALSE(auth_helper.handle()->is_initialized());
|
|
EXPECT_FALSE(auth_helper.handle()->socket());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, ProxyAuthOnceFlushWithError) {
|
|
CreatePool(1, 1);
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockAuthChallengeOnceJob);
|
|
|
|
TestAuthHelper auth_helper;
|
|
auth_helper.InitHandle(params_, pool_.get());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
auth_helper.WaitForAuth();
|
|
|
|
pool_->FlushWithError(ERR_FAILED, "Network changed");
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
// When flushing the socket pool, bound sockets should delay returning the
|
|
// error until completion.
|
|
EXPECT_FALSE(auth_helper.have_result());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
|
|
auth_helper.RestartWithAuth();
|
|
// The callback should be called asynchronously.
|
|
EXPECT_FALSE(auth_helper.have_result());
|
|
|
|
EXPECT_THAT(auth_helper.WaitForResult(), IsError(ERR_FAILED));
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, ProxyAuthTwice) {
|
|
CreatePool(1, 1);
|
|
connect_job_factory_->set_job_type(
|
|
TestConnectJob::kMockAuthChallengeTwiceJob);
|
|
|
|
TestAuthHelper auth_helper;
|
|
auth_helper.InitHandle(params_, pool_.get());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(LOAD_STATE_CONNECTING,
|
|
pool_->GetLoadState(TestGroupId("a"), auth_helper.handle()));
|
|
|
|
auth_helper.WaitForAuth();
|
|
auth_helper.RestartWithAuth();
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1, auth_helper.auth_count());
|
|
EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
|
|
pool_->GetLoadState(TestGroupId("a"), auth_helper.handle()));
|
|
|
|
auth_helper.WaitForAuth();
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(2, auth_helper.auth_count());
|
|
EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
|
|
pool_->GetLoadState(TestGroupId("a"), auth_helper.handle()));
|
|
|
|
auth_helper.RestartWithAuth();
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(2, auth_helper.auth_count());
|
|
EXPECT_EQ(LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
|
|
pool_->GetLoadState(TestGroupId("a"), auth_helper.handle()));
|
|
|
|
EXPECT_THAT(auth_helper.WaitForResult(), IsOk());
|
|
EXPECT_EQ(2, auth_helper.auth_count());
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, ProxyAuthTwiceFails) {
|
|
CreatePool(1, 1);
|
|
connect_job_factory_->set_job_type(
|
|
TestConnectJob::kMockAuthChallengeTwiceFailingJob);
|
|
|
|
TestAuthHelper auth_helper;
|
|
auth_helper.InitHandle(params_, pool_.get());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
auth_helper.WaitForAuth();
|
|
auth_helper.RestartWithAuth();
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1, auth_helper.auth_count());
|
|
|
|
auth_helper.WaitForAuth();
|
|
auth_helper.RestartWithAuth();
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(2, auth_helper.auth_count());
|
|
|
|
EXPECT_THAT(auth_helper.WaitForResult(), IsError(ERR_CONNECTION_FAILED));
|
|
EXPECT_EQ(2, auth_helper.auth_count());
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
}
|
|
|
|
// Makes sure that when a bound request is destroyed, a new ConnectJob is
|
|
// created, if needed.
|
|
TEST_F(ClientSocketPoolBaseTest,
|
|
ProxyAuthCreateNewConnectJobOnDestroyBoundRequest) {
|
|
CreatePool(1 /* max_sockets */, 1 /* max_sockets_per_group */);
|
|
connect_job_factory_->set_job_type(
|
|
TestConnectJob::kMockAuthChallengeOnceFailingJob);
|
|
|
|
// First request creates a ConnectJob.
|
|
TestAuthHelper auth_helper1;
|
|
auth_helper1.InitHandle(params_, pool_.get());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// A second request come in, but no new ConnectJob is needed, since the limit
|
|
// has been reached.
|
|
TestAuthHelper auth_helper2;
|
|
auth_helper2.InitHandle(params_, pool_.get());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Run until the auth callback for the first request is invoked.
|
|
auth_helper1.WaitForAuth();
|
|
EXPECT_EQ(0, auth_helper2.auth_count());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
// Make connect jobs succeed, then cancel the first request, which should
|
|
// destroy the bound ConnectJob, and cause a new ConnectJob to start.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
auth_helper1.handle()->Reset();
|
|
EXPECT_EQ(0, auth_helper2.auth_count());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// The second ConnectJob should succeed.
|
|
EXPECT_THAT(auth_helper2.WaitForResult(), IsOk());
|
|
EXPECT_EQ(0, auth_helper2.auth_count());
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
}
|
|
|
|
// Makes sure that when a bound request is destroyed, a new ConnectJob is
|
|
// created for another group, if needed.
|
|
TEST_F(ClientSocketPoolBaseTest,
|
|
ProxyAuthCreateNewConnectJobOnDestroyBoundRequestDifferentGroups) {
|
|
CreatePool(1 /* max_sockets */, 1 /* max_sockets_per_group */);
|
|
connect_job_factory_->set_job_type(
|
|
TestConnectJob::kMockAuthChallengeOnceFailingJob);
|
|
|
|
// First request creates a ConnectJob.
|
|
TestAuthHelper auth_helper1;
|
|
auth_helper1.InitHandle(params_, pool_.get(), DEFAULT_PRIORITY);
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// A second request come in, but no new ConnectJob is needed, since the limit
|
|
// has been reached.
|
|
TestAuthHelper auth_helper2;
|
|
auth_helper2.InitHandle(params_, pool_.get(), DEFAULT_PRIORITY,
|
|
ClientSocketPool::RespectLimits::ENABLED,
|
|
TestGroupId("b"));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("b")));
|
|
|
|
// Run until the auth callback for the first request is invoked.
|
|
auth_helper1.WaitForAuth();
|
|
EXPECT_EQ(0, auth_helper2.auth_count());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("b")));
|
|
EXPECT_EQ(0, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("b")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("b")));
|
|
|
|
// Make connect jobs succeed, then cancel the first request, which should
|
|
// destroy the bound ConnectJob, and cause a new ConnectJob to start for the
|
|
// other group.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
|
|
auth_helper1.handle()->Reset();
|
|
EXPECT_EQ(0, auth_helper2.auth_count());
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("b")));
|
|
|
|
// The second ConnectJob should succeed.
|
|
EXPECT_THAT(auth_helper2.WaitForResult(), IsOk());
|
|
EXPECT_EQ(0, auth_helper2.auth_count());
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("b")));
|
|
}
|
|
|
|
// Test that once an auth challenge is bound, that's the request that gets all
|
|
// subsequent calls and the socket itself.
|
|
TEST_F(ClientSocketPoolBaseTest, ProxyAuthStaysBound) {
|
|
CreatePool(1, 1);
|
|
connect_job_factory_->set_job_type(
|
|
TestConnectJob::kMockAuthChallengeTwiceJob);
|
|
|
|
// First request creates a ConnectJob.
|
|
TestAuthHelper auth_helper1;
|
|
auth_helper1.InitHandle(params_, pool_.get(), LOWEST);
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// A second, higher priority request is made.
|
|
TestAuthHelper auth_helper2;
|
|
auth_helper2.InitHandle(params_, pool_.get(), LOW);
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Run until the auth callback for the second request is invoked.
|
|
auth_helper2.WaitForAuth();
|
|
EXPECT_EQ(0, auth_helper1.auth_count());
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(TestGroupId("a")));
|
|
|
|
// Start a higher priority job. It shouldn't be able to steal |auth_helper2|'s
|
|
// ConnectJob.
|
|
TestAuthHelper auth_helper3;
|
|
auth_helper3.InitHandle(params_, pool_.get(), HIGHEST);
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Start a higher job that ignores limits, creating a hanging socket. It
|
|
// shouldn't be able to steal |auth_helper2|'s ConnectJob.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
TestAuthHelper auth_helper4;
|
|
auth_helper4.InitHandle(params_, pool_.get(), HIGHEST,
|
|
ClientSocketPool::RespectLimits::DISABLED);
|
|
EXPECT_EQ(2u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
|
|
// Restart with auth, and |auth_helper2|'s auth method should be invoked
|
|
// again.
|
|
auth_helper2.RestartWithAuth();
|
|
auth_helper2.WaitForAuth();
|
|
EXPECT_EQ(0, auth_helper1.auth_count());
|
|
EXPECT_FALSE(auth_helper1.have_result());
|
|
EXPECT_EQ(2, auth_helper2.auth_count());
|
|
EXPECT_FALSE(auth_helper2.have_result());
|
|
EXPECT_EQ(0, auth_helper3.auth_count());
|
|
EXPECT_FALSE(auth_helper3.have_result());
|
|
EXPECT_EQ(0, auth_helper4.auth_count());
|
|
EXPECT_FALSE(auth_helper4.have_result());
|
|
|
|
// Advance auth again, and |auth_helper2| should get the socket.
|
|
auth_helper2.RestartWithAuth();
|
|
EXPECT_THAT(auth_helper2.WaitForResult(), IsOk());
|
|
// The hung ConnectJob for the RespectLimits::DISABLED request is still in the
|
|
// socket pool.
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(TestGroupId("a")));
|
|
EXPECT_EQ(0, auth_helper1.auth_count());
|
|
EXPECT_FALSE(auth_helper1.have_result());
|
|
EXPECT_EQ(0, auth_helper3.auth_count());
|
|
EXPECT_FALSE(auth_helper3.have_result());
|
|
EXPECT_EQ(0, auth_helper4.auth_count());
|
|
EXPECT_FALSE(auth_helper4.have_result());
|
|
|
|
// If the socket is returned to the socket pool, the RespectLimits::DISABLED
|
|
// socket request should be able to claim it.
|
|
auth_helper2.handle()->Reset();
|
|
EXPECT_THAT(auth_helper4.WaitForResult(), IsOk());
|
|
EXPECT_EQ(0, auth_helper1.auth_count());
|
|
EXPECT_FALSE(auth_helper1.have_result());
|
|
EXPECT_EQ(0, auth_helper3.auth_count());
|
|
EXPECT_FALSE(auth_helper3.have_result());
|
|
EXPECT_EQ(0, auth_helper4.auth_count());
|
|
}
|
|
|
|
enum class RefreshType {
|
|
kServer,
|
|
kProxy,
|
|
};
|
|
|
|
// Common base class to test RefreshGroup() when called from either
|
|
// OnSSLConfigForServerChanged() matching a specific group or the pool's proxy.
|
|
//
|
|
// Tests which test behavior specific to one or the other case should use
|
|
// ClientSocketPoolBaseTest directly. In particular, there is no "other group"
|
|
// when the pool's proxy matches.
|
|
class ClientSocketPoolBaseRefreshTest
|
|
: public ClientSocketPoolBaseTest,
|
|
public testing::WithParamInterface<RefreshType> {
|
|
public:
|
|
void CreatePoolForRefresh(int max_sockets,
|
|
int max_sockets_per_group,
|
|
bool enable_backup_connect_jobs = false) {
|
|
switch (GetParam()) {
|
|
case RefreshType::kServer:
|
|
CreatePool(max_sockets, max_sockets_per_group,
|
|
enable_backup_connect_jobs);
|
|
break;
|
|
case RefreshType::kProxy:
|
|
CreatePoolWithIdleTimeouts(
|
|
max_sockets, max_sockets_per_group, kUnusedIdleSocketTimeout,
|
|
ClientSocketPool::used_idle_socket_timeout(),
|
|
enable_backup_connect_jobs,
|
|
PacResultElementToProxyServer("HTTPS myproxy:70"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static ClientSocketPool::GroupId GetGroupId() {
|
|
return TestGroupId("a", 443, url::kHttpsScheme);
|
|
}
|
|
|
|
static ClientSocketPool::GroupId GetGroupIdInPartition() {
|
|
// Note this GroupId will match GetGroupId() unless
|
|
// kPartitionConnectionsByNetworkAnonymizationKey is enabled.
|
|
const SchemefulSite kSite(GURL("https://b/"));
|
|
const auto kNetworkAnonymizationKey =
|
|
NetworkAnonymizationKey::CreateSameSite(kSite);
|
|
return TestGroupId("a", 443, url::kHttpsScheme,
|
|
PrivacyMode::PRIVACY_MODE_DISABLED,
|
|
kNetworkAnonymizationKey);
|
|
}
|
|
|
|
void OnSSLConfigForServerChanged() {
|
|
switch (GetParam()) {
|
|
case RefreshType::kServer:
|
|
pool_->OnSSLConfigForServerChanged(HostPortPair("a", 443));
|
|
break;
|
|
case RefreshType::kProxy:
|
|
pool_->OnSSLConfigForServerChanged(HostPortPair("myproxy", 70));
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
INSTANTIATE_TEST_SUITE_P(RefreshType,
|
|
ClientSocketPoolBaseRefreshTest,
|
|
::testing::Values(RefreshType::kServer,
|
|
RefreshType::kProxy));
|
|
|
|
TEST_P(ClientSocketPoolBaseRefreshTest, RefreshGroupCreatesNewConnectJobs) {
|
|
CreatePoolForRefresh(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
const ClientSocketPool::GroupId kGroupId = GetGroupId();
|
|
|
|
// First job will be waiting until it gets aborted.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_THAT(
|
|
handle.Init(kGroupId, params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()),
|
|
IsError(ERR_IO_PENDING));
|
|
|
|
// Switch connect job types, so creating a new ConnectJob will result in
|
|
// success.
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
|
|
|
|
OnSSLConfigForServerChanged();
|
|
EXPECT_EQ(OK, callback.WaitForResult());
|
|
ASSERT_TRUE(handle.socket());
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupId));
|
|
EXPECT_EQ(0u, pool_->IdleSocketCountInGroup(kGroupId));
|
|
EXPECT_EQ(0u, pool_->NumConnectJobsInGroupForTesting(kGroupId));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(kGroupId));
|
|
}
|
|
|
|
TEST_P(ClientSocketPoolBaseRefreshTest, RefreshGroupClosesIdleConnectJobs) {
|
|
base::test::ScopedFeatureList feature_list;
|
|
feature_list.InitAndEnableFeature(
|
|
features::kPartitionConnectionsByNetworkIsolationKey);
|
|
|
|
CreatePoolForRefresh(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
const ClientSocketPool::GroupId kGroupId = GetGroupId();
|
|
const ClientSocketPool::GroupId kGroupIdInPartition = GetGroupIdInPartition();
|
|
|
|
EXPECT_EQ(
|
|
OK, pool_->RequestSockets(kGroupId, params_, absl::nullopt, 2,
|
|
CompletionOnceCallback(), NetLogWithSource()));
|
|
|
|
EXPECT_EQ(
|
|
OK, pool_->RequestSockets(kGroupIdInPartition, params_, absl::nullopt, 2,
|
|
CompletionOnceCallback(), NetLogWithSource()));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupId));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupIdInPartition));
|
|
EXPECT_EQ(4, pool_->IdleSocketCount());
|
|
EXPECT_EQ(2u, pool_->IdleSocketCountInGroup(kGroupId));
|
|
EXPECT_EQ(2u, pool_->IdleSocketCountInGroup(kGroupIdInPartition));
|
|
|
|
OnSSLConfigForServerChanged();
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(kGroupId));
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(kGroupIdInPartition));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest,
|
|
RefreshGroupDoesNotCloseIdleConnectJobsInOtherGroup) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
const ClientSocketPool::GroupId kGroupId =
|
|
TestGroupId("a", 443, url::kHttpsScheme);
|
|
const ClientSocketPool::GroupId kOtherGroupId =
|
|
TestGroupId("b", 443, url::kHttpsScheme);
|
|
|
|
EXPECT_EQ(
|
|
OK, pool_->RequestSockets(kOtherGroupId, params_, absl::nullopt, 2,
|
|
CompletionOnceCallback(), NetLogWithSource()));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kOtherGroupId));
|
|
EXPECT_EQ(2, pool_->IdleSocketCount());
|
|
EXPECT_EQ(2u, pool_->IdleSocketCountInGroup(kOtherGroupId));
|
|
|
|
pool_->OnSSLConfigForServerChanged(HostPortPair("a", 443));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kOtherGroupId));
|
|
EXPECT_EQ(2, pool_->IdleSocketCount());
|
|
EXPECT_EQ(2u, pool_->IdleSocketCountInGroup(kOtherGroupId));
|
|
}
|
|
|
|
TEST_P(ClientSocketPoolBaseRefreshTest, RefreshGroupPreventsSocketReuse) {
|
|
CreatePoolForRefresh(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
const ClientSocketPool::GroupId kGroupId = GetGroupId();
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_THAT(
|
|
handle.Init(kGroupId, params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()),
|
|
IsOk());
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupId));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(kGroupId));
|
|
|
|
OnSSLConfigForServerChanged();
|
|
|
|
handle.Reset();
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(kGroupId));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest,
|
|
RefreshGroupDoesNotPreventSocketReuseInOtherGroup) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
const ClientSocketPool::GroupId kGroupId =
|
|
TestGroupId("a", 443, url::kHttpsScheme);
|
|
const ClientSocketPool::GroupId kOtherGroupId =
|
|
TestGroupId("b", 443, url::kHttpsScheme);
|
|
|
|
ClientSocketHandle handle;
|
|
TestCompletionCallback callback;
|
|
EXPECT_THAT(
|
|
handle.Init(kOtherGroupId, params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()),
|
|
IsOk());
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kOtherGroupId));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(kOtherGroupId));
|
|
|
|
pool_->OnSSLConfigForServerChanged(HostPortPair("a", 443));
|
|
|
|
handle.Reset();
|
|
EXPECT_EQ(1, pool_->IdleSocketCount());
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kOtherGroupId));
|
|
EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(kOtherGroupId));
|
|
}
|
|
|
|
TEST_P(ClientSocketPoolBaseRefreshTest,
|
|
RefreshGroupReplacesBoundConnectJobOnConnect) {
|
|
CreatePoolForRefresh(1, 1);
|
|
const ClientSocketPool::GroupId kGroupId = GetGroupId();
|
|
connect_job_factory_->set_job_type(TestConnectJob::kMockAuthChallengeOnceJob);
|
|
|
|
TestAuthHelper auth_helper;
|
|
auth_helper.InitHandle(params_, pool_.get(), DEFAULT_PRIORITY,
|
|
ClientSocketPool::RespectLimits::ENABLED, kGroupId);
|
|
EXPECT_EQ(1u, pool_->NumConnectJobsInGroupForTesting(kGroupId));
|
|
|
|
auth_helper.WaitForAuth();
|
|
|
|
// This should update the generation, but not cancel the old ConnectJob - it's
|
|
// not safe to do anything while waiting on the original ConnectJob.
|
|
OnSSLConfigForServerChanged();
|
|
|
|
// Providing auth credentials and restarting the request with them will cause
|
|
// the ConnectJob to complete successfully, but the result will be discarded
|
|
// because of the generation mismatch.
|
|
auth_helper.RestartWithAuth();
|
|
|
|
// Despite using ConnectJobs that simulate a single challenge, a second
|
|
// challenge will be seen, due to using a new ConnectJob.
|
|
auth_helper.WaitForAuth();
|
|
auth_helper.RestartWithAuth();
|
|
|
|
EXPECT_THAT(auth_helper.WaitForResult(), IsOk());
|
|
EXPECT_TRUE(auth_helper.handle()->socket());
|
|
EXPECT_EQ(2, auth_helper.auth_count());
|
|
|
|
// When released, the socket will be returned to the socket pool, and
|
|
// available for reuse.
|
|
auth_helper.handle()->Reset();
|
|
EXPECT_EQ(1, pool_->IdleSocketCount());
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupId));
|
|
EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(kGroupId));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, RefreshProxyRefreshesAllGroups) {
|
|
CreatePoolWithIdleTimeouts(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup,
|
|
kUnusedIdleSocketTimeout,
|
|
ClientSocketPool::used_idle_socket_timeout(),
|
|
false /* no backup connect jobs */,
|
|
PacResultElementToProxyServer("HTTPS myproxy:70"));
|
|
|
|
const ClientSocketPool::GroupId kGroupId1 =
|
|
TestGroupId("a", 443, url::kHttpsScheme);
|
|
const ClientSocketPool::GroupId kGroupId2 =
|
|
TestGroupId("b", 443, url::kHttpsScheme);
|
|
const ClientSocketPool::GroupId kGroupId3 =
|
|
TestGroupId("c", 443, url::kHttpsScheme);
|
|
|
|
// Make three sockets in three different groups. The third socket is released
|
|
// to the pool as idle.
|
|
ClientSocketHandle handle1, handle2, handle3;
|
|
TestCompletionCallback callback;
|
|
EXPECT_THAT(
|
|
handle1.Init(kGroupId1, params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()),
|
|
IsOk());
|
|
EXPECT_THAT(
|
|
handle2.Init(kGroupId2, params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()),
|
|
IsOk());
|
|
EXPECT_THAT(
|
|
handle3.Init(kGroupId3, params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()),
|
|
IsOk());
|
|
handle3.Reset();
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupId1));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(kGroupId1));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupId2));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(kGroupId2));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupId3));
|
|
EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(kGroupId3));
|
|
|
|
// Changes to some other proxy do not affect the pool. The idle socket remains
|
|
// alive and closing |handle2| makes the socket available for the pool.
|
|
pool_->OnSSLConfigForServerChanged(HostPortPair("someotherproxy", 70));
|
|
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupId1));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(kGroupId1));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupId2));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(kGroupId2));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupId3));
|
|
EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(kGroupId3));
|
|
|
|
handle2.Reset();
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupId2));
|
|
EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(kGroupId2));
|
|
|
|
// Changes to the matching proxy refreshes all groups.
|
|
pool_->OnSSLConfigForServerChanged(HostPortPair("myproxy", 70));
|
|
|
|
// Idle sockets are closed.
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(kGroupId2));
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(kGroupId3));
|
|
|
|
// The active socket, however, continues to be active.
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupId1));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(kGroupId1));
|
|
|
|
// Closing it does not make it available for the pool.
|
|
handle1.Reset();
|
|
EXPECT_EQ(0, pool_->IdleSocketCount());
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(kGroupId1));
|
|
}
|
|
|
|
TEST_F(ClientSocketPoolBaseTest, RefreshBothPrivacyAndNormalSockets) {
|
|
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
|
|
|
|
const ClientSocketPool::GroupId kGroupId = TestGroupId(
|
|
"a", 443, url::kHttpsScheme, PrivacyMode::PRIVACY_MODE_DISABLED);
|
|
const ClientSocketPool::GroupId kGroupIdPrivacy = TestGroupId(
|
|
"a", 443, url::kHttpsScheme, PrivacyMode::PRIVACY_MODE_ENABLED);
|
|
const ClientSocketPool::GroupId kOtherGroupId =
|
|
TestGroupId("b", 443, url::kHttpsScheme);
|
|
|
|
// Make a socket in each groups.
|
|
ClientSocketHandle handle1, handle2, handle3;
|
|
TestCompletionCallback callback;
|
|
EXPECT_THAT(
|
|
handle1.Init(kGroupId, params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()),
|
|
IsOk());
|
|
EXPECT_THAT(
|
|
handle2.Init(kGroupIdPrivacy, params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()),
|
|
IsOk());
|
|
EXPECT_THAT(
|
|
handle3.Init(kOtherGroupId, params_, absl::nullopt, DEFAULT_PRIORITY,
|
|
SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
|
|
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
|
|
pool_.get(), NetLogWithSource()),
|
|
IsOk());
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupId));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(kGroupId));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupIdPrivacy));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(kGroupIdPrivacy));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kOtherGroupId));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(kOtherGroupId));
|
|
|
|
pool_->OnSSLConfigForServerChanged(HostPortPair("a", 443));
|
|
|
|
// Active sockets continue to be active.
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupId));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(kGroupId));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kGroupIdPrivacy));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(kGroupIdPrivacy));
|
|
ASSERT_TRUE(pool_->HasGroupForTesting(kOtherGroupId));
|
|
EXPECT_EQ(1, pool_->NumActiveSocketsInGroupForTesting(kOtherGroupId));
|
|
|
|
// Closing them leaves kOtherGroupId alone, but kGroupId and kGroupIdPrivacy
|
|
// are unusable.
|
|
handle1.Reset();
|
|
handle2.Reset();
|
|
handle3.Reset();
|
|
EXPECT_EQ(1, pool_->IdleSocketCount());
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(kGroupId));
|
|
EXPECT_FALSE(pool_->HasGroupForTesting(kGroupIdPrivacy));
|
|
EXPECT_TRUE(pool_->HasGroupForTesting(kOtherGroupId));
|
|
EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(kOtherGroupId));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
} // namespace net
|