summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-16 22:37:32 +0000
committerrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-16 22:37:32 +0000
commit4e5540ac3f8e4ac006d956b3033df9b8415f9df5 (patch)
tree70f1e8e257e5e46198b2efc9b8719bbb9e9b5853 /net
parent48fae61b8a6c9b741f001d478c595b6c7c0af4d9 (diff)
downloadchromium_src-4e5540ac3f8e4ac006d956b3033df9b8415f9df5.zip
chromium_src-4e5540ac3f8e4ac006d956b3033df9b8415f9df5.tar.gz
chromium_src-4e5540ac3f8e4ac006d956b3033df9b8415f9df5.tar.bz2
Attempt to close idle connections in higher layer socket pools when a lower layer pool is stalled.
BUG=62364, 92244 TEST=ClientSocketPoolBaseTest.\*CloseIdleSocket\* Review URL: http://codereview.chromium.org/9999030 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@132478 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/socket/client_socket_pool_base.cc20
-rw-r--r--net/socket/client_socket_pool_base_unittest.cc167
2 files changed, 182 insertions, 5 deletions
diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc
index ab959cf..f55a017 100644
--- a/net/socket/client_socket_pool_base.cc
+++ b/net/socket/client_socket_pool_base.cc
@@ -369,11 +369,21 @@ int ClientSocketPoolBaseHelper::RequestSocketInternal(
if (preconnecting && !closed)
return ERR_PRECONNECT_MAX_SOCKET_LIMIT;
} else {
- // We could check if we really have a stalled group here, but it requires
- // a scan of all groups, so just flip a flag here, and do the check later.
- request->net_log().AddEvent(
- NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS, NULL);
- return ERR_IO_PENDING;
+ do {
+ if (!CloseOneIdleConnectionInLayeredPool()) {
+ // We could check if we really have a stalled group here, but it
+ // requires a scan of all groups, so return ERR_IO_PENDING, and do
+ // the check later.
+ request->net_log().AddEvent(
+ NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS, NULL);
+ return ERR_IO_PENDING;
+ }
+ } while (ReachedMaxSocketsLimit());
+
+ // It is possible that CloseOneIdleConnectionInLayeredPool() has deleted
+ // our Group (see http://crbug.com/109876), so look it up again
+ // to be safe.
+ group = GetOrCreateGroup(group_name);
}
}
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc
index 099a1c6..d5689c5 100644
--- a/net/socket/client_socket_pool_base_unittest.cc
+++ b/net/socket/client_socket_pool_base_unittest.cc
@@ -3416,6 +3416,173 @@ TEST_F(ClientSocketPoolBaseTest, PreconnectWithBackupJob) {
EXPECT_EQ(1, pool_->NumActiveSocketsInGroup("a"));
}
+class MockLayeredPool : public LayeredPool {
+ public:
+ MockLayeredPool(TestClientSocketPool* pool,
+ const std::string& group_name)
+ : pool_(pool),
+ params_(new TestSocketParams),
+ group_name_(group_name),
+ can_release_connection_(true) {
+ pool_->AddLayeredPool(this);
+ }
+
+ ~MockLayeredPool() {
+ pool_->RemoveLayeredPool(this);
+ }
+
+ int RequestSocket(TestClientSocketPool* pool) {
+ return handle_.Init(group_name_, params_, kDefaultPriority,
+ callback_.callback(), pool, BoundNetLog());
+ }
+
+ int RequestSocketWithoutLimits(TestClientSocketPool* pool) {
+ params_->set_ignore_limits(true);
+ return handle_.Init(group_name_, params_, kDefaultPriority,
+ callback_.callback(), pool, BoundNetLog());
+ }
+
+ 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:
+ TestClientSocketPool* const pool_;
+ scoped_refptr<TestSocketParams> params_;
+ ClientSocketHandle handle_;
+ TestCompletionCallback callback_;
+ const std::string group_name_;
+ bool can_release_connection_;
+};
+
+TEST_F(ClientSocketPoolBaseTest, FailToCloseIdleSocketsNotHeldByLayeredPool) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+ connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
+
+ MockLayeredPool mock_layered_pool(pool_.get(), "foo");
+ EXPECT_EQ(OK, mock_layered_pool.RequestSocket(pool_.get()));
+ EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection())
+ .WillOnce(Return(false));
+ EXPECT_FALSE(pool_->CloseOneIdleConnectionInLayeredPool());
+}
+
+TEST_F(ClientSocketPoolBaseTest, ForciblyCloseIdleSocketsHeldByLayeredPool) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+ connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
+
+ MockLayeredPool mock_layered_pool(pool_.get(), "foo");
+ EXPECT_EQ(OK, mock_layered_pool.RequestSocket(pool_.get()));
+ EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection())
+ .WillOnce(Invoke(&mock_layered_pool,
+ &MockLayeredPool::ReleaseOneConnection));
+ EXPECT_TRUE(pool_->CloseOneIdleConnectionInLayeredPool());
+}
+
+// This test exercises the codepath which caused http://crbug.com/109876
+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::kMockFailingJob);
+ job_types.push_back(TestConnectJob::kMockJob);
+ connect_job_factory_->set_job_types(&job_types);
+
+ ClientSocketHandle handle1;
+ TestCompletionCallback callback1;
+ EXPECT_EQ(OK, handle1.Init("group1",
+ params_,
+ kDefaultPriority,
+ callback1.callback(),
+ pool_.get(),
+ BoundNetLog()));
+
+ MockLayeredPool mock_layered_pool(pool_.get(), "group2");
+ EXPECT_EQ(OK, mock_layered_pool.RequestSocket(pool_.get()));
+ EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection())
+ .WillRepeatedly(Invoke(&mock_layered_pool,
+ &MockLayeredPool::ReleaseOneConnection));
+ mock_layered_pool.set_can_release_connection(false);
+
+ // This connection attempt will fail when the next request causes the
+ // MockLayeredPool to delete the socket it's holding. This request is
+ // needed to trigger the destruction of the "group2" Group.
+ ClientSocketHandle handle3;
+ TestCompletionCallback callback3;
+ EXPECT_EQ(ERR_IO_PENDING, handle3.Init("group2",
+ params_,
+ kDefaultPriority,
+ callback3.callback(),
+ pool_.get(),
+ BoundNetLog()));
+
+ mock_layered_pool.set_can_release_connection(true);
+ ClientSocketHandle handle4;
+ TestCompletionCallback callback4;
+ EXPECT_EQ(OK, handle4.Init("group2",
+ params_,
+ kDefaultPriority,
+ callback4.callback(),
+ pool_.get(),
+ BoundNetLog()));
+}
+
+TEST_F(ClientSocketPoolBaseTest, CloseIdleSocketsHeldByLayeredPoolWhenNeeded) {
+ CreatePool(1, 1);
+ connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
+
+ MockLayeredPool mock_layered_pool(pool_.get(), "foo");
+ EXPECT_EQ(OK, mock_layered_pool.RequestSocket(pool_.get()));
+ EXPECT_CALL(mock_layered_pool, CloseOneIdleConnection())
+ .WillOnce(Invoke(&mock_layered_pool,
+ &MockLayeredPool::ReleaseOneConnection));
+ ClientSocketHandle handle;
+ TestCompletionCallback callback;
+ EXPECT_EQ(OK, handle.Init("a",
+ params_,
+ kDefaultPriority,
+ callback.callback(),
+ pool_.get(),
+ BoundNetLog()));
+}
+
+TEST_F(ClientSocketPoolBaseTest,
+ CloseMultipleIdleSocketsHeldByLayeredPoolWhenNeeded) {
+ CreatePool(1, 1);
+ connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
+
+ MockLayeredPool mock_layered_pool1(pool_.get(), "foo");
+ EXPECT_EQ(OK, mock_layered_pool1.RequestSocket(pool_.get()));
+ EXPECT_CALL(mock_layered_pool1, CloseOneIdleConnection())
+ .WillRepeatedly(Invoke(&mock_layered_pool1,
+ &MockLayeredPool::ReleaseOneConnection));
+ MockLayeredPool mock_layered_pool2(pool_.get(), "bar");
+ EXPECT_EQ(OK, mock_layered_pool2.RequestSocketWithoutLimits(pool_.get()));
+ EXPECT_CALL(mock_layered_pool2, CloseOneIdleConnection())
+ .WillRepeatedly(Invoke(&mock_layered_pool2,
+ &MockLayeredPool::ReleaseOneConnection));
+ ClientSocketHandle handle;
+ TestCompletionCallback callback;
+ EXPECT_EQ(OK, handle.Init("a",
+ params_,
+ kDefaultPriority,
+ callback.callback(),
+ pool_.get(),
+ BoundNetLog()));
+}
+
} // namespace
} // namespace net