Add method to replace ConnectJobs and sockets for a socket pool group.
This is needed as part of the socket pool GroupId refactor, so when
there's a change to the SSL-related configuration for a host (Like a
ClientCert), we can refresh just its socket pool, without failing any
requests.
Bug: 947644
Change-Id: I8fcd2c62bf127580c64edc29b6959ca1ab2d6e12
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1546142
Commit-Queue: Matt Menke <[email protected]>
Reviewed-by: David Benjamin <[email protected]>
Cr-Commit-Position: refs/heads/master@{#651059}
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc
index 5dffcc7..4876c9c 100644
--- a/net/socket/client_socket_pool_base_unittest.cc
+++ b/net/socket/client_socket_pool_base_unittest.cc
@@ -5335,6 +5335,152 @@
EXPECT_EQ(0, auth_helper4.auth_count());
}
+TEST_F(ClientSocketPoolBaseTest, RefreshGroupCreatesNewConnectJobs) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+ const ClientSocketPool::GroupId kGroupId = TestGroupId("a");
+
+ // 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_, 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);
+
+ pool_->RefreshGroupForTesting(kGroupId);
+ 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_F(ClientSocketPoolBaseTest, RefreshGroupClosesIdleConnectJobs) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+ const ClientSocketPool::GroupId kGroupId = TestGroupId("a");
+
+ pool_->RequestSockets(kGroupId, params_, 2, NetLogWithSource());
+ ASSERT_TRUE(pool_->HasGroupForTesting(kGroupId));
+ EXPECT_EQ(2, pool_->IdleSocketCount());
+ EXPECT_EQ(2u, pool_->IdleSocketCountInGroup(kGroupId));
+
+ pool_->RefreshGroupForTesting(kGroupId);
+ EXPECT_EQ(0, pool_->IdleSocketCount());
+ EXPECT_FALSE(pool_->HasGroupForTesting(kGroupId));
+}
+
+TEST_F(ClientSocketPoolBaseTest,
+ RefreshGroupDoesNotCloseIdleConnectJobsInOtherGroup) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+ const ClientSocketPool::GroupId kGroupId = TestGroupId("a");
+ const ClientSocketPool::GroupId kOtherGroupId = TestGroupId("b");
+
+ pool_->RequestSockets(kOtherGroupId, params_, 2, NetLogWithSource());
+ ASSERT_TRUE(pool_->HasGroupForTesting(kOtherGroupId));
+ EXPECT_EQ(2, pool_->IdleSocketCount());
+ EXPECT_EQ(2u, pool_->IdleSocketCountInGroup(kOtherGroupId));
+
+ pool_->RefreshGroupForTesting(kGroupId);
+ ASSERT_TRUE(pool_->HasGroupForTesting(kOtherGroupId));
+ EXPECT_EQ(2, pool_->IdleSocketCount());
+ EXPECT_EQ(2u, pool_->IdleSocketCountInGroup(kOtherGroupId));
+}
+
+TEST_F(ClientSocketPoolBaseTest, RefreshGroupPreventsSocketReuse) {
+ CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+ const ClientSocketPool::GroupId kGroupId = TestGroupId("a");
+
+ ClientSocketHandle handle;
+ TestCompletionCallback callback;
+ EXPECT_THAT(
+ handle.Init(kGroupId, params_, 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));
+
+ pool_->RefreshGroupForTesting(kGroupId);
+
+ 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");
+ const ClientSocketPool::GroupId kOtherGroupId = TestGroupId("b");
+
+ ClientSocketHandle handle;
+ TestCompletionCallback callback;
+ EXPECT_THAT(
+ handle.Init(kOtherGroupId, params_, 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_->RefreshGroupForTesting(kGroupId);
+
+ handle.Reset();
+ EXPECT_EQ(1, pool_->IdleSocketCount());
+ ASSERT_TRUE(pool_->HasGroupForTesting(kOtherGroupId));
+ EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(kOtherGroupId));
+}
+
+TEST_F(ClientSocketPoolBaseTest, RefreshGroupReplacesBoundConnectJobOnConnect) {
+ CreatePool(1, 1);
+ const ClientSocketPool::GroupId kGroupId = TestGroupId("a");
+ 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.
+ pool_->RefreshGroupForTesting(kGroupId);
+
+ // 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));
+}
+
} // namespace
} // namespace net