blob: f5ad77e88b5f92c8d1715fa03c1dea56d34bdbd2 [file] [log] [blame]
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/socket/client_socket_pool_base.h"
#include "base/compiler_specific.h"
#include "base/message_loop.h"
#include "base/scoped_vector.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "net/socket/client_socket.h"
#include "net/socket/client_socket_factory.h"
#include "net/socket/client_socket_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace {
const int kDefaultMaxSockets = 4;
const int kDefaultMaxSocketsPerGroup = 2;
const int kDefaultPriority = 5;
class MockClientSocket : public ClientSocket {
public:
MockClientSocket() : connected_(false) {}
// Socket methods:
virtual int Read(
IOBuffer* /* buf */, int /* len */, CompletionCallback* /* callback */) {
return ERR_UNEXPECTED;
}
virtual int Write(
IOBuffer* /* buf */, int /* len */, CompletionCallback* /* callback */) {
return ERR_UNEXPECTED;
}
// ClientSocket methods:
virtual int Connect(CompletionCallback* callback) {
connected_ = true;
return OK;
}
virtual void Disconnect() { connected_ = false; }
virtual bool IsConnected() const { return connected_; }
virtual bool IsConnectedAndIdle() const { return connected_; }
#if defined(OS_LINUX)
virtual int GetPeerName(struct sockaddr* /* name */,
socklen_t* /* namelen */) {
return 0;
}
#endif
private:
bool connected_;
DISALLOW_COPY_AND_ASSIGN(MockClientSocket);
};
class TestConnectJob;
class MockClientSocketFactory : public ClientSocketFactory {
public:
MockClientSocketFactory() : allocation_count_(0) {}
virtual ClientSocket* CreateTCPClientSocket(const AddressList& addresses) {
allocation_count_++;
return NULL;
}
virtual SSLClientSocket* CreateSSLClientSocket(
ClientSocket* transport_socket,
const std::string& hostname,
const SSLConfig& ssl_config) {
NOTIMPLEMENTED();
return NULL;
}
void WaitForSignal(TestConnectJob* job) { waiting_jobs_.push_back(job); }
void SignalJobs();
int allocation_count() const { return allocation_count_; }
private:
int allocation_count_;
std::vector<TestConnectJob*> waiting_jobs_;
};
class TestSocketRequest : public CallbackRunner< Tuple1<int> > {
public:
TestSocketRequest(
ClientSocketPool* pool,
std::vector<TestSocketRequest*>* request_order)
: handle(pool), request_order_(request_order) {}
ClientSocketHandle handle;
int WaitForResult() {
return callback_.WaitForResult();
}
virtual void RunWithParams(const Tuple1<int>& params) {
callback_.RunWithParams(params);
completion_count++;
request_order_->push_back(this);
}
static size_t completion_count;
private:
std::vector<TestSocketRequest*>* request_order_;
TestCompletionCallback callback_;
};
size_t TestSocketRequest::completion_count = 0;
class TestConnectJob : public ConnectJob {
public:
enum JobType {
kMockJob,
kMockFailingJob,
kMockPendingJob,
kMockPendingFailingJob,
kMockWaitingJob,
kMockAdvancingLoadStateJob,
};
TestConnectJob(JobType job_type,
const std::string& group_name,
const ClientSocketPoolBase::Request& request,
ConnectJob::Delegate* delegate,
MockClientSocketFactory* client_socket_factory)
: ConnectJob(group_name, request.handle, delegate),
job_type_(job_type),
client_socket_factory_(client_socket_factory),
method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}
// ConnectJob methods:
virtual int Connect() {
AddressList ignored;
client_socket_factory_->CreateTCPClientSocket(ignored);
switch (job_type_) {
case kMockJob:
return DoConnect(true /* successful */, false /* sync */);
case kMockFailingJob:
return DoConnect(false /* error */, false /* sync */);
case kMockPendingJob:
set_load_state(LOAD_STATE_CONNECTING);
MessageLoop::current()->PostTask(
FROM_HERE,
method_factory_.NewRunnableMethod(
&TestConnectJob::DoConnect,
true /* successful */,
true /* async */));
return ERR_IO_PENDING;
case kMockPendingFailingJob:
set_load_state(LOAD_STATE_CONNECTING);
MessageLoop::current()->PostTask(
FROM_HERE,
method_factory_.NewRunnableMethod(
&TestConnectJob::DoConnect,
false /* error */,
true /* async */));
return ERR_IO_PENDING;
case kMockWaitingJob:
client_socket_factory_->WaitForSignal(this);
waiting_success_ = true;
return ERR_IO_PENDING;
case kMockAdvancingLoadStateJob:
MessageLoop::current()->PostTask(
FROM_HERE,
method_factory_.NewRunnableMethod(
&TestConnectJob::AdvanceLoadState, load_state()));
return ERR_IO_PENDING;
default:
NOTREACHED();
return ERR_FAILED;
}
}
void Signal() {
DoConnect(waiting_success_, true /* async */);
}
private:
int DoConnect(bool succeed, bool was_async) {
int result = ERR_CONNECTION_FAILED;
if (succeed) {
result = OK;
set_socket(new MockClientSocket());
socket()->Connect(NULL);
}
if (was_async)
delegate()->OnConnectJobComplete(result, this);
return result;
}
void AdvanceLoadState(LoadState state) {
int tmp = state;
tmp++;
state = static_cast<LoadState>(tmp);
set_load_state(state);
// Post a delayed task so RunAllPending() won't run it.
MessageLoop::current()->PostDelayedTask(
FROM_HERE,
method_factory_.NewRunnableMethod(&TestConnectJob::AdvanceLoadState,
state),
1 /* 1ms delay */);
}
bool waiting_success_;
const JobType job_type_;
MockClientSocketFactory* const client_socket_factory_;
ScopedRunnableMethodFactory<TestConnectJob> method_factory_;
DISALLOW_COPY_AND_ASSIGN(TestConnectJob);
};
class TestConnectJobFactory : public ClientSocketPoolBase::ConnectJobFactory {
public:
explicit TestConnectJobFactory(MockClientSocketFactory* client_socket_factory)
: job_type_(TestConnectJob::kMockJob),
client_socket_factory_(client_socket_factory) {}
virtual ~TestConnectJobFactory() {}
void set_job_type(TestConnectJob::JobType job_type) { job_type_ = job_type; }
// ConnectJobFactory methods:
virtual ConnectJob* NewConnectJob(
const std::string& group_name,
const ClientSocketPoolBase::Request& request,
ConnectJob::Delegate* delegate) const {
return new TestConnectJob(job_type_,
group_name,
request,
delegate,
client_socket_factory_);
}
private:
TestConnectJob::JobType job_type_;
MockClientSocketFactory* const client_socket_factory_;
DISALLOW_COPY_AND_ASSIGN(TestConnectJobFactory);
};
class TestClientSocketPool : public ClientSocketPool {
public:
TestClientSocketPool(
int max_sockets,
int max_sockets_per_group,
ClientSocketPoolBase::ConnectJobFactory* connect_job_factory)
: base_(new ClientSocketPoolBase(
max_sockets, max_sockets_per_group, connect_job_factory)) {}
virtual int RequestSocket(
const std::string& group_name,
const HostResolver::RequestInfo& resolve_info,
int priority,
ClientSocketHandle* handle,
CompletionCallback* callback) {
return base_->RequestSocket(
group_name, resolve_info, priority, handle, callback);
}
virtual void CancelRequest(
const std::string& group_name,
const ClientSocketHandle* handle) {
base_->CancelRequest(group_name, handle);
}
virtual void ReleaseSocket(
const std::string& group_name,
ClientSocket* socket) {
base_->ReleaseSocket(group_name, socket);
}
virtual void CloseIdleSockets() {
base_->CloseIdleSockets();
}
virtual int IdleSocketCount() const { return base_->idle_socket_count(); }
virtual int IdleSocketCountInGroup(const std::string& group_name) const {
return base_->IdleSocketCountInGroup(group_name);
}
virtual LoadState GetLoadState(const std::string& group_name,
const ClientSocketHandle* handle) const {
return base_->GetLoadState(group_name, handle);
}
const ClientSocketPoolBase* base() const { return base_.get(); }
private:
const scoped_refptr<ClientSocketPoolBase> base_;
DISALLOW_COPY_AND_ASSIGN(TestClientSocketPool);
};
void MockClientSocketFactory::SignalJobs() {
for (std::vector<TestConnectJob*>::iterator it = waiting_jobs_.begin();
it != waiting_jobs_.end(); ++it) {
(*it)->Signal();
}
waiting_jobs_.clear();
}
class ClientSocketPoolBaseTest : public testing::Test {
protected:
ClientSocketPoolBaseTest()
: ignored_request_info_("ignored", 80),
connect_job_factory_(
new TestConnectJobFactory(&client_socket_factory_)) {}
void CreatePool(int max_sockets, int max_sockets_per_group) {
DCHECK(!pool_.get());
pool_ = new TestClientSocketPool(max_sockets,
max_sockets_per_group,
connect_job_factory_);
}
virtual void SetUp() {
TestSocketRequest::completion_count = 0;
}
virtual void TearDown() {
// The tests often call Reset() on handles at the end which may post
// DoReleaseSocket() tasks.
MessageLoop::current()->RunAllPending();
// Need to delete |pool_| before we turn late binding back off. We also need
// to delete |requests_| because the pool is reference counted and requests
// keep reference to it.
// TODO(willchan): Remove this part when late binding becomes the default.
pool_ = NULL;
requests_.reset();
ClientSocketPoolBase::EnableLateBindingOfSockets(false);
}
int StartRequest(const std::string& group_name, int priority) {
DCHECK(pool_.get());
TestSocketRequest* request = new TestSocketRequest(pool_.get(),
&request_order_);
requests_.push_back(request);
int rv = request->handle.Init(group_name, ignored_request_info_, priority,
request);
if (rv != ERR_IO_PENDING)
request_order_.push_back(request);
return rv;
}
static const int kIndexOutOfBounds;
static const int kRequestNotFound;
int GetOrderOfRequest(size_t index) {
index--;
if (index >= requests_.size())
return kIndexOutOfBounds;
for (size_t i = 0; i < request_order_.size(); i++)
if (requests_[index] == request_order_[i])
return i + 1;
return kRequestNotFound;
}
enum KeepAlive {
KEEP_ALIVE,
NO_KEEP_ALIVE,
};
bool ReleaseOneConnection(KeepAlive keep_alive) {
ScopedVector<TestSocketRequest>::iterator i;
for (i = requests_.begin(); i != requests_.end(); ++i) {
if ((*i)->handle.is_initialized()) {
if (keep_alive == NO_KEEP_ALIVE)
(*i)->handle.socket()->Disconnect();
(*i)->handle.Reset();
MessageLoop::current()->RunAllPending();
return true;
}
}
return false;
}
void ReleaseAllConnections(KeepAlive keep_alive) {
bool released_one;
do {
released_one = ReleaseOneConnection(keep_alive);
} while (released_one);
}
HostResolver::RequestInfo ignored_request_info_;
MockClientSocketFactory client_socket_factory_;
TestConnectJobFactory* const connect_job_factory_;
scoped_refptr<TestClientSocketPool> pool_;
ScopedVector<TestSocketRequest> requests_;
std::vector<TestSocketRequest*> request_order_;
};
// static
const int ClientSocketPoolBaseTest::kIndexOutOfBounds = -1;
// static
const int ClientSocketPoolBaseTest::kRequestNotFound = -2;
TEST_F(ClientSocketPoolBaseTest, BasicSynchronous) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
TestCompletionCallback callback;
ClientSocketHandle handle(pool_.get());
EXPECT_EQ(OK, handle.Init("a", ignored_request_info_, kDefaultPriority,
&callback));
EXPECT_TRUE(handle.is_initialized());
EXPECT_TRUE(handle.socket());
handle.Reset();
}
TEST_F(ClientSocketPoolBaseTest, BasicAsynchronous) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
TestSocketRequest req(pool_.get(), &request_order_);
int rv = req.handle.Init("a", ignored_request_info_, 0, &req);
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(LOAD_STATE_CONNECTING, pool_->GetLoadState("a", &req.handle));
EXPECT_EQ(OK, req.WaitForResult());
EXPECT_TRUE(req.handle.is_initialized());
EXPECT_TRUE(req.handle.socket());
req.handle.Reset();
}
TEST_F(ClientSocketPoolBaseTest, InitConnectionFailure) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockFailingJob);
TestSocketRequest req(pool_.get(), &request_order_);
EXPECT_EQ(ERR_CONNECTION_FAILED,
req.handle.Init("a", ignored_request_info_,
kDefaultPriority, &req));
}
TEST_F(ClientSocketPoolBaseTest, InitConnectionAsynchronousFailure) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
TestSocketRequest req(pool_.get(), &request_order_);
EXPECT_EQ(ERR_IO_PENDING,
req.handle.Init("a", ignored_request_info_, 5, &req));
EXPECT_EQ(LOAD_STATE_CONNECTING, pool_->GetLoadState("a", &req.handle));
EXPECT_EQ(ERR_CONNECTION_FAILED, req.WaitForResult());
}
TEST_F(ClientSocketPoolBaseTest, TotalLimit) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
EXPECT_EQ(OK, StartRequest("c", kDefaultPriority));
EXPECT_EQ(OK, StartRequest("d", kDefaultPriority));
EXPECT_EQ(static_cast<int>(requests_.size()),
client_socket_factory_.allocation_count());
EXPECT_EQ(requests_.size() - kDefaultMaxSockets,
TestSocketRequest::completion_count);
EXPECT_EQ(ERR_IO_PENDING, StartRequest("e", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("f", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("g", kDefaultPriority));
ReleaseAllConnections(KEEP_ALIVE);
EXPECT_EQ(static_cast<int>(requests_.size()),
client_socket_factory_.allocation_count());
EXPECT_EQ(requests_.size() - kDefaultMaxSockets,
TestSocketRequest::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));
}
TEST_F(ClientSocketPoolBaseTest, TotalLimitReachedNewGroup) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
// Reach all limits: max total sockets, and max sockets per group.
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
EXPECT_EQ(static_cast<int>(requests_.size()),
client_socket_factory_.allocation_count());
EXPECT_EQ(requests_.size() - kDefaultMaxSockets,
TestSocketRequest::completion_count);
// Now create a new group and verify that we don't starve it.
EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", kDefaultPriority));
ReleaseAllConnections(KEEP_ALIVE);
EXPECT_EQ(static_cast<int>(requests_.size()),
client_socket_factory_.allocation_count());
EXPECT_EQ(requests_.size() - kDefaultMaxSockets,
TestSocketRequest::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));
}
TEST_F(ClientSocketPoolBaseTest, TotalLimitRespectsPriority) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
EXPECT_EQ(OK, StartRequest("b", 3));
EXPECT_EQ(OK, StartRequest("a", 3));
EXPECT_EQ(OK, StartRequest("b", 6));
EXPECT_EQ(OK, StartRequest("a", 6));
EXPECT_EQ(static_cast<int>(requests_.size()),
client_socket_factory_.allocation_count());
EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", 4));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 5));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("b", 7));
ReleaseAllConnections(KEEP_ALIVE);
// We're re-using one socket for group "a", and one for "b".
EXPECT_EQ(static_cast<int>(requests_.size()) - 2,
client_socket_factory_.allocation_count());
EXPECT_EQ(requests_.size() - kDefaultMaxSockets,
TestSocketRequest::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, then ("a", 5),
// and then ("c", 4).
EXPECT_EQ(7, GetOrderOfRequest(5));
EXPECT_EQ(6, GetOrderOfRequest(6));
EXPECT_EQ(5, GetOrderOfRequest(7));
}
TEST_F(ClientSocketPoolBaseTest, TotalLimitRespectsGroupLimit) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
EXPECT_EQ(OK, StartRequest("a", 3));
EXPECT_EQ(OK, StartRequest("a", 6));
EXPECT_EQ(OK, StartRequest("b", 3));
EXPECT_EQ(OK, StartRequest("b", 6));
EXPECT_EQ(static_cast<int>(requests_.size()),
client_socket_factory_.allocation_count());
EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", 6));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 4));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("b", 7));
ReleaseAllConnections(KEEP_ALIVE);
// We're re-using one socket for group "a", and one for "b".
EXPECT_EQ(static_cast<int>(requests_.size()) - 2,
client_socket_factory_.allocation_count());
EXPECT_EQ(requests_.size() - kDefaultMaxSockets,
TestSocketRequest::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 that we count connecting sockets against the total limit.
TEST_F(ClientSocketPoolBaseTest, TotalLimitCountsConnectingSockets) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
EXPECT_EQ(OK, StartRequest("c", kDefaultPriority));
// Create one asynchronous request.
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
EXPECT_EQ(ERR_IO_PENDING, StartRequest("d", kDefaultPriority));
// The next synchronous request should wait for its turn.
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
EXPECT_EQ(ERR_IO_PENDING, StartRequest("e", kDefaultPriority));
ReleaseAllConnections(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));
}
// Inside ClientSocketPoolBase we have a may_have_stalled_group flag,
// which tells it to use more expensive, but accurate, group selection
// algorithm. Make sure it doesn't get stuck in the "on" state.
TEST_F(ClientSocketPoolBaseTest, MayHaveStalledGroupReset) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
EXPECT_FALSE(pool_->base()->may_have_stalled_group());
// Reach group socket limit.
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_FALSE(pool_->base()->may_have_stalled_group());
// Reach total limit, but don't request more sockets.
EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
EXPECT_FALSE(pool_->base()->may_have_stalled_group());
// Request one more socket while we are at the maximum sockets limit.
// This should flip the may_have_stalled_group flag.
EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", kDefaultPriority));
EXPECT_TRUE(pool_->base()->may_have_stalled_group());
// After releasing first connection for "a", we're still at the
// maximum sockets limit, but every group's pending queue is empty,
// so we reset the flag.
EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
EXPECT_FALSE(pool_->base()->may_have_stalled_group());
// Requesting additional socket while at the total limit should
// flip the flag back to "on".
EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", kDefaultPriority));
EXPECT_TRUE(pool_->base()->may_have_stalled_group());
// We'll request one more socket to verify that we don't reset the flag
// too eagerly.
EXPECT_EQ(ERR_IO_PENDING, StartRequest("d", kDefaultPriority));
EXPECT_TRUE(pool_->base()->may_have_stalled_group());
// We're at the maximum socket limit, and still have one request pending
// for "d". Flag should be "on".
EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
EXPECT_TRUE(pool_->base()->may_have_stalled_group());
// Now every group's pending queue should be empty again.
EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
EXPECT_FALSE(pool_->base()->may_have_stalled_group());
ReleaseAllConnections(KEEP_ALIVE);
EXPECT_FALSE(pool_->base()->may_have_stalled_group());
}
TEST_F(ClientSocketPoolBaseTest, PendingRequests) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 1));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 3));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 4));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 2));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 1));
ReleaseAllConnections(KEEP_ALIVE);
EXPECT_EQ(kDefaultMaxSocketsPerGroup,
client_socket_factory_.allocation_count());
EXPECT_EQ(requests_.size() - kDefaultMaxSocketsPerGroup,
TestSocketRequest::completion_count);
EXPECT_EQ(1, GetOrderOfRequest(1));
EXPECT_EQ(2, GetOrderOfRequest(2));
EXPECT_EQ(6, GetOrderOfRequest(3));
EXPECT_EQ(4, GetOrderOfRequest(4));
EXPECT_EQ(3, GetOrderOfRequest(5));
EXPECT_EQ(5, GetOrderOfRequest(6));
EXPECT_EQ(7, GetOrderOfRequest(7));
}
TEST_F(ClientSocketPoolBaseTest, PendingRequests_NoKeepAlive) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 1));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 3));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 4));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 2));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 1));
ReleaseAllConnections(NO_KEEP_ALIVE);
for (size_t i = kDefaultMaxSocketsPerGroup; i < requests_.size(); ++i)
EXPECT_EQ(OK, requests_[i]->WaitForResult());
EXPECT_EQ(static_cast<int>(requests_.size()),
client_socket_factory_.allocation_count());
EXPECT_EQ(requests_.size() - kDefaultMaxSocketsPerGroup,
TestSocketRequest::completion_count);
}
// This test will start up a RequestSocket() and then immediately Cancel() it.
// The pending connect job will be cancelled and should not call back into
// ClientSocketPoolBase.
TEST_F(ClientSocketPoolBaseTest, CancelRequestClearGroup) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
TestSocketRequest req(pool_.get(), &request_order_);
EXPECT_EQ(ERR_IO_PENDING,
req.handle.Init("a", ignored_request_info_,
kDefaultPriority, &req));
req.handle.Reset();
}
TEST_F(ClientSocketPoolBaseTest, TwoRequestsCancelOne) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
TestSocketRequest req(pool_.get(), &request_order_);
TestSocketRequest req2(pool_.get(), &request_order_);
EXPECT_EQ(ERR_IO_PENDING,
req.handle.Init("a", ignored_request_info_,
kDefaultPriority, &req));
EXPECT_EQ(ERR_IO_PENDING,
req2.handle.Init("a", ignored_request_info_,
kDefaultPriority, &req2));
req.handle.Reset();
EXPECT_EQ(OK, req2.WaitForResult());
req2.handle.Reset();
}
TEST_F(ClientSocketPoolBaseTest, ConnectCancelConnect) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
ClientSocketHandle handle(pool_.get());
TestCompletionCallback callback;
TestSocketRequest req(pool_.get(), &request_order_);
EXPECT_EQ(ERR_IO_PENDING,
handle.Init("a", ignored_request_info_,
kDefaultPriority, &callback));
handle.Reset();
TestCompletionCallback callback2;
EXPECT_EQ(ERR_IO_PENDING,
handle.Init("a", ignored_request_info_,
kDefaultPriority, &callback2));
EXPECT_EQ(OK, callback2.WaitForResult());
EXPECT_FALSE(callback.have_result());
handle.Reset();
}
TEST_F(ClientSocketPoolBaseTest, CancelRequest) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 1));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 3));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 4));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 2));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 1));
// 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(KEEP_ALIVE);
EXPECT_EQ(kDefaultMaxSocketsPerGroup,
client_socket_factory_.allocation_count());
EXPECT_EQ(requests_.size() - kDefaultMaxSocketsPerGroup - 1,
TestSocketRequest::completion_count);
EXPECT_EQ(1, GetOrderOfRequest(1));
EXPECT_EQ(2, GetOrderOfRequest(2));
EXPECT_EQ(5, GetOrderOfRequest(3));
EXPECT_EQ(3, GetOrderOfRequest(4));
EXPECT_EQ(kRequestNotFound, GetOrderOfRequest(5)); // Canceled request.
EXPECT_EQ(4, GetOrderOfRequest(6));
EXPECT_EQ(6, GetOrderOfRequest(7));
}
class RequestSocketCallback : public CallbackRunner< Tuple1<int> > {
public:
RequestSocketCallback(ClientSocketHandle* handle,
TestConnectJobFactory* test_connect_job_factory,
TestConnectJob::JobType next_job_type)
: handle_(handle),
within_callback_(false),
test_connect_job_factory_(test_connect_job_factory),
next_job_type_(next_job_type) {}
virtual void RunWithParams(const Tuple1<int>& params) {
callback_.RunWithParams(params);
ASSERT_EQ(OK, params.a);
if (!within_callback_) {
test_connect_job_factory_->set_job_type(next_job_type_);
handle_->Reset();
within_callback_ = true;
int rv = handle_->Init(
"a", HostResolver::RequestInfo("www.google.com", 80),
kDefaultPriority, this);
switch (next_job_type_) {
case TestConnectJob::kMockJob:
EXPECT_EQ(OK, rv);
break;
case TestConnectJob::kMockPendingJob:
EXPECT_EQ(ERR_IO_PENDING, rv);
break;
default:
FAIL() << "Unexpected job type: " << next_job_type_;
break;
}
}
}
int WaitForResult() {
return callback_.WaitForResult();
}
private:
ClientSocketHandle* const handle_;
bool within_callback_;
TestConnectJobFactory* const test_connect_job_factory_;
TestConnectJob::JobType next_job_type_;
TestCompletionCallback callback_;
};
TEST_F(ClientSocketPoolBaseTest, RequestPendingJobTwice) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
ClientSocketHandle handle(pool_.get());
RequestSocketCallback callback(
&handle, connect_job_factory_, TestConnectJob::kMockPendingJob);
int rv = handle.Init(
"a", ignored_request_info_, kDefaultPriority, &callback);
ASSERT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, callback.WaitForResult());
handle.Reset();
}
TEST_F(ClientSocketPoolBaseTest, RequestPendingJobThenSynchronous) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
ClientSocketHandle handle(pool_.get());
RequestSocketCallback callback(
&handle, connect_job_factory_, TestConnectJob::kMockJob);
int rv = handle.Init(
"a", ignored_request_info_, kDefaultPriority, &callback);
ASSERT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, callback.WaitForResult());
handle.Reset();
}
// 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_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
// Now, kDefaultMaxSocketsPerGroup requests should be active.
// Let's cancel them.
for (int i = 0; i < kDefaultMaxSocketsPerGroup; ++i) {
ASSERT_FALSE(requests_[i]->handle.is_initialized());
requests_[i]->handle.Reset();
}
// Let's wait for the rest to complete now.
for (size_t i = kDefaultMaxSocketsPerGroup; i < requests_.size(); ++i) {
EXPECT_EQ(OK, requests_[i]->WaitForResult());
requests_[i]->handle.Reset();
}
EXPECT_EQ(requests_.size() - kDefaultMaxSocketsPerGroup,
TestSocketRequest::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_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
for (size_t i = 0; i < kNumberOfRequests; ++i)
EXPECT_EQ(ERR_CONNECTION_FAILED, requests_[i]->WaitForResult());
}
TEST_F(ClientSocketPoolBaseTest, CancelActiveRequestThenRequestSocket) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
TestSocketRequest req(pool_.get(), &request_order_);
int rv = req.handle.Init(
"a", ignored_request_info_, kDefaultPriority, &req);
EXPECT_EQ(ERR_IO_PENDING, rv);
// Cancel the active request.
req.handle.Reset();
rv = req.handle.Init("a", ignored_request_info_, kDefaultPriority, &req);
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, req.WaitForResult());
EXPECT_FALSE(req.handle.is_reused());
EXPECT_EQ(1U, TestSocketRequest::completion_count);
EXPECT_EQ(2, client_socket_factory_.allocation_count());
}
// A pending asynchronous job completes, which will free up a socket slot. The
// next job finishes synchronously. The callback for the asynchronous job
// should be first though.
TEST_F(ClientSocketPoolBaseTest, PendingJobCompletionOrder) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
// First two jobs are async.
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
// Start job 1 (async error).
TestSocketRequest req1(pool_.get(), &request_order_);
int rv = req1.handle.Init("a", ignored_request_info_,
kDefaultPriority, &req1);
EXPECT_EQ(ERR_IO_PENDING, rv);
// Start job 2 (async error).
TestSocketRequest req2(pool_.get(), &request_order_);
rv = req2.handle.Init("a", ignored_request_info_, kDefaultPriority, &req2);
EXPECT_EQ(ERR_IO_PENDING, rv);
// The pending job is sync.
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
// Request 3 does not have a ConnectJob yet. It's just pending.
TestSocketRequest req3(pool_.get(), &request_order_);
rv = req3.handle.Init("a", ignored_request_info_, kDefaultPriority, &req3);
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(ERR_CONNECTION_FAILED, req1.WaitForResult());
EXPECT_EQ(ERR_CONNECTION_FAILED, req2.WaitForResult());
EXPECT_EQ(OK, req3.WaitForResult());
ASSERT_EQ(3U, request_order_.size());
// After job 1 finishes unsuccessfully, it will try to process the pending
// requests queue, so it starts up job 3 for request 3. This job
// synchronously succeeds, so the request order is 1, 3, 2.
EXPECT_EQ(&req1, request_order_[0]);
EXPECT_EQ(&req2, request_order_[2]);
EXPECT_EQ(&req3, request_order_[1]);
}
// When a ConnectJob is coupled to a request, even if a free socket becomes
// available, the request will be serviced by the ConnectJob.
TEST_F(ClientSocketPoolBaseTest, ReleaseSockets) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
ClientSocketPoolBase::EnableLateBindingOfSockets(false);
// Start job 1 (async OK)
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
TestSocketRequest req1(pool_.get(), &request_order_);
int rv = req1.handle.Init("a", ignored_request_info_, 5, &req1);
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, req1.WaitForResult());
// Job 1 finished OK. Start job 2 (also async OK). Release socket 1.
connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
TestSocketRequest req2(pool_.get(), &request_order_);
rv = req2.handle.Init("a", ignored_request_info_, 5, &req2);
EXPECT_EQ(ERR_IO_PENDING, rv);
req1.handle.Reset();
MessageLoop::current()->RunAllPending(); // Run the DoReleaseSocket()
// Job 2 is pending. Start request 3 (which has no associated job since it
// will use the idle socket).
TestSocketRequest req3(pool_.get(), &request_order_);
rv = req3.handle.Init("a", ignored_request_info_, 5, &req3);
EXPECT_EQ(OK, rv);
EXPECT_FALSE(req2.handle.socket());
client_socket_factory_.SignalJobs();
EXPECT_EQ(OK, req2.WaitForResult());
ASSERT_EQ(2U, request_order_.size());
EXPECT_EQ(&req1, request_order_[0]);
EXPECT_EQ(&req2, request_order_[1]);
EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
}
class ClientSocketPoolBaseTest_LateBinding : public ClientSocketPoolBaseTest {
protected:
virtual void SetUp() {
ClientSocketPoolBaseTest::SetUp();
ClientSocketPoolBase::EnableLateBindingOfSockets(true);
}
};
TEST_F(ClientSocketPoolBaseTest_LateBinding, BasicSynchronous) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
TestCompletionCallback callback;
ClientSocketHandle handle(pool_.get());
EXPECT_EQ(OK, handle.Init("a", ignored_request_info_, kDefaultPriority,
&callback));
EXPECT_TRUE(handle.is_initialized());
EXPECT_TRUE(handle.socket());
handle.Reset();
}
TEST_F(ClientSocketPoolBaseTest_LateBinding, BasicAsynchronous) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
TestSocketRequest req(pool_.get(), &request_order_);
int rv = req.handle.Init("a", ignored_request_info_, 0, &req);
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(LOAD_STATE_CONNECTING, pool_->GetLoadState("a", &req.handle));
EXPECT_EQ(OK, req.WaitForResult());
EXPECT_TRUE(req.handle.is_initialized());
EXPECT_TRUE(req.handle.socket());
req.handle.Reset();
}
TEST_F(ClientSocketPoolBaseTest_LateBinding, InitConnectionFailure) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockFailingJob);
TestSocketRequest req(pool_.get(), &request_order_);
EXPECT_EQ(ERR_CONNECTION_FAILED,
req.handle.Init("a", ignored_request_info_,
kDefaultPriority, &req));
}
TEST_F(ClientSocketPoolBaseTest_LateBinding,
InitConnectionAsynchronousFailure) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
TestSocketRequest req(pool_.get(), &request_order_);
EXPECT_EQ(ERR_IO_PENDING,
req.handle.Init("a", ignored_request_info_, 5, &req));
EXPECT_EQ(LOAD_STATE_CONNECTING, pool_->GetLoadState("a", &req.handle));
EXPECT_EQ(ERR_CONNECTION_FAILED, req.WaitForResult());
}
TEST_F(ClientSocketPoolBaseTest_LateBinding, PendingRequests) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 1));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 3));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 4));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 2));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 1));
ReleaseAllConnections(KEEP_ALIVE);
EXPECT_EQ(kDefaultMaxSocketsPerGroup,
client_socket_factory_.allocation_count());
EXPECT_EQ(requests_.size() - kDefaultMaxSocketsPerGroup,
TestSocketRequest::completion_count);
EXPECT_EQ(1, GetOrderOfRequest(1));
EXPECT_EQ(2, GetOrderOfRequest(2));
EXPECT_EQ(6, GetOrderOfRequest(3));
EXPECT_EQ(4, GetOrderOfRequest(4));
EXPECT_EQ(3, GetOrderOfRequest(5));
EXPECT_EQ(5, GetOrderOfRequest(6));
EXPECT_EQ(7, GetOrderOfRequest(7));
}
TEST_F(ClientSocketPoolBaseTest_LateBinding, PendingRequests_NoKeepAlive) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 1));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 3));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 4));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 2));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 1));
ReleaseAllConnections(NO_KEEP_ALIVE);
for (size_t i = kDefaultMaxSocketsPerGroup; i < requests_.size(); ++i)
EXPECT_EQ(OK, requests_[i]->WaitForResult());
EXPECT_EQ(static_cast<int>(requests_.size()),
client_socket_factory_.allocation_count());
EXPECT_EQ(requests_.size() - kDefaultMaxSocketsPerGroup,
TestSocketRequest::completion_count);
}
// This test will start up a RequestSocket() and then immediately Cancel() it.
// The pending connect job will be cancelled and should not call back into
// ClientSocketPoolBase.
TEST_F(ClientSocketPoolBaseTest_LateBinding, CancelRequestClearGroup) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
TestSocketRequest req(pool_.get(), &request_order_);
EXPECT_EQ(ERR_IO_PENDING,
req.handle.Init("a", ignored_request_info_,
kDefaultPriority, &req));
req.handle.Reset();
}
TEST_F(ClientSocketPoolBaseTest_LateBinding, TwoRequestsCancelOne) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
TestSocketRequest req(pool_.get(), &request_order_);
TestSocketRequest req2(pool_.get(), &request_order_);
EXPECT_EQ(ERR_IO_PENDING,
req.handle.Init("a", ignored_request_info_,
kDefaultPriority, &req));
EXPECT_EQ(ERR_IO_PENDING,
req2.handle.Init("a", ignored_request_info_,
kDefaultPriority, &req2));
req.handle.Reset();
EXPECT_EQ(OK, req2.WaitForResult());
req2.handle.Reset();
}
TEST_F(ClientSocketPoolBaseTest_LateBinding, ConnectCancelConnect) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
ClientSocketHandle handle(pool_.get());
TestCompletionCallback callback;
TestSocketRequest req(pool_.get(), &request_order_);
EXPECT_EQ(ERR_IO_PENDING,
handle.Init("a", ignored_request_info_,
kDefaultPriority, &callback));
handle.Reset();
TestCompletionCallback callback2;
EXPECT_EQ(ERR_IO_PENDING,
handle.Init("a", ignored_request_info_,
kDefaultPriority, &callback2));
EXPECT_EQ(OK, callback2.WaitForResult());
EXPECT_FALSE(callback.have_result());
handle.Reset();
}
TEST_F(ClientSocketPoolBaseTest_LateBinding, CancelRequest) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 1));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 3));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 4));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 2));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", 1));
// 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(KEEP_ALIVE);
EXPECT_EQ(kDefaultMaxSocketsPerGroup,
client_socket_factory_.allocation_count());
EXPECT_EQ(requests_.size() - kDefaultMaxSocketsPerGroup - 1,
TestSocketRequest::completion_count);
EXPECT_EQ(1, GetOrderOfRequest(1));
EXPECT_EQ(2, GetOrderOfRequest(2));
EXPECT_EQ(5, GetOrderOfRequest(3));
EXPECT_EQ(3, GetOrderOfRequest(4));
EXPECT_EQ(kRequestNotFound, GetOrderOfRequest(5)); // Canceled request.
EXPECT_EQ(4, GetOrderOfRequest(6));
EXPECT_EQ(6, GetOrderOfRequest(7));
}
TEST_F(ClientSocketPoolBaseTest_LateBinding, RequestPendingJobTwice) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
ClientSocketHandle handle(pool_.get());
RequestSocketCallback callback(
&handle, connect_job_factory_, TestConnectJob::kMockPendingJob);
int rv = handle.Init(
"a", ignored_request_info_, kDefaultPriority, &callback);
ASSERT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, callback.WaitForResult());
handle.Reset();
}
TEST_F(ClientSocketPoolBaseTest_LateBinding, RequestPendingJobThenSynchronous) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
ClientSocketHandle handle(pool_.get());
RequestSocketCallback callback(
&handle, connect_job_factory_, TestConnectJob::kMockJob);
int rv = handle.Init(
"a", ignored_request_info_, kDefaultPriority, &callback);
ASSERT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, callback.WaitForResult());
handle.Reset();
}
// Make sure that pending requests get serviced after active requests get
// cancelled.
TEST_F(ClientSocketPoolBaseTest_LateBinding,
CancelActiveRequestWithPendingRequests) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
// Now, kDefaultMaxSocketsPerGroup requests should be active.
// Let's cancel them.
for (int i = 0; i < kDefaultMaxSocketsPerGroup; ++i) {
ASSERT_FALSE(requests_[i]->handle.is_initialized());
requests_[i]->handle.Reset();
}
// Let's wait for the rest to complete now.
for (size_t i = kDefaultMaxSocketsPerGroup; i < requests_.size(); ++i) {
EXPECT_EQ(OK, requests_[i]->WaitForResult());
requests_[i]->handle.Reset();
}
EXPECT_EQ(requests_.size() - kDefaultMaxSocketsPerGroup,
TestSocketRequest::completion_count);
}
// Make sure that pending requests get serviced after active requests fail.
TEST_F(ClientSocketPoolBaseTest_LateBinding,
FailingActiveRequestWithPendingRequests) {
const int kMaxSockets = 5;
CreatePool(kMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
const int kNumberOfRequests = 2 * kDefaultMaxSocketsPerGroup + 1;
ASSERT_LE(kNumberOfRequests, kMaxSockets); // Otherwise the test hangs.
// Queue up all the requests
for (int i = 0; i < kNumberOfRequests; ++i)
EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
for (int i = 0; i < kNumberOfRequests; ++i)
EXPECT_EQ(ERR_CONNECTION_FAILED, requests_[i]->WaitForResult());
}
TEST_F(ClientSocketPoolBaseTest_LateBinding,
CancelActiveRequestThenRequestSocket) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
TestSocketRequest req(pool_.get(), &request_order_);
int rv = req.handle.Init(
"a", ignored_request_info_, kDefaultPriority, &req);
EXPECT_EQ(ERR_IO_PENDING, rv);
// Cancel the active request.
req.handle.Reset();
rv = req.handle.Init("a", ignored_request_info_, kDefaultPriority, &req);
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, req.WaitForResult());
EXPECT_FALSE(req.handle.is_reused());
EXPECT_EQ(1U, TestSocketRequest::completion_count);
EXPECT_EQ(2, client_socket_factory_.allocation_count());
}
// When requests and ConnectJobs are not coupled, the request will get serviced
// by whatever comes first.
TEST_F(ClientSocketPoolBaseTest_LateBinding, ReleaseSockets) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
// Start job 1 (async OK)
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
TestSocketRequest req1(pool_.get(), &request_order_);
int rv = req1.handle.Init("a", ignored_request_info_, 5, &req1);
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, req1.WaitForResult());
// 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(pool_.get(), &request_order_);
rv = req2.handle.Init("a", ignored_request_info_, 5, &req2);
EXPECT_EQ(ERR_IO_PENDING, rv);
TestSocketRequest req3(pool_.get(), &request_order_);
rv = req3.handle.Init("a", ignored_request_info_, 5, &req3);
EXPECT_EQ(ERR_IO_PENDING, rv);
// 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();
MessageLoop::current()->RunAllPending(); // Run the DoReleaseSocket()
ASSERT_TRUE(req2.handle.socket());
EXPECT_EQ(OK, req2.WaitForResult());
EXPECT_FALSE(req3.handle.socket());
// Signal job 2, which should service request 3.
client_socket_factory_.SignalJobs();
EXPECT_EQ(OK, req3.WaitForResult());
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(0, pool_->IdleSocketCountInGroup("a"));
}
// The requests are not coupled to the jobs. So, the requests should finish in
// their priority / insertion order.
TEST_F(ClientSocketPoolBaseTest_LateBinding, PendingJobCompletionOrder) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
// First two jobs are async.
connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
TestSocketRequest req1(pool_.get(), &request_order_);
int rv = req1.handle.Init("a", ignored_request_info_, 5, &req1);
EXPECT_EQ(ERR_IO_PENDING, rv);
TestSocketRequest req2(pool_.get(), &request_order_);
rv = req2.handle.Init("a", ignored_request_info_, 5, &req2);
EXPECT_EQ(ERR_IO_PENDING, rv);
// The pending job is sync.
connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
TestSocketRequest req3(pool_.get(), &request_order_);
rv = req3.handle.Init("a", ignored_request_info_, 5, &req3);
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(ERR_CONNECTION_FAILED, req1.WaitForResult());
EXPECT_EQ(OK, req2.WaitForResult());
EXPECT_EQ(ERR_CONNECTION_FAILED, req3.WaitForResult());
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_F(ClientSocketPoolBaseTest_LateBinding, DISABLED_LoadState) {
CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
connect_job_factory_->set_job_type(
TestConnectJob::kMockAdvancingLoadStateJob);
TestSocketRequest req1(pool_.get(), &request_order_);
int rv = req1.handle.Init("a", ignored_request_info_, 5, &req1);
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(LOAD_STATE_IDLE, req1.handle.GetLoadState());
MessageLoop::current()->RunAllPending();
TestSocketRequest req2(pool_.get(), &request_order_);
rv = req2.handle.Init("a", ignored_request_info_, 5, &req2);
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, req1.handle.GetLoadState());
EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, req2.handle.GetLoadState());
}
} // namespace
} // namespace net