Index: net/socket/client_socket_pool_base_unittest.cc |
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc |
index 099a1c623cd0be5a03eba41e1a53a2e569d2c43e..d5689c529e5328152ecab8bd359dbba79ea9be0f 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 |