chromium/net/socket/client_socket_pool_base_unittest.cc

// 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.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif

#include <stdint.h>

#include <optional>
#include <string_view>
#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_chain.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/ssl/ssl_config.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 "net/url_request/static_http_user_agent_settings.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/scheme_host_port.h"
#include "url/url_constants.h"

IsError;
IsOk;

Invoke;
Return;

namespace net {

namespace {

const int kDefaultMaxSockets =;
const int kDefaultMaxSocketsPerGroup =;
constexpr base::TimeDelta kUnusedIdleSocketTimeout =;

ClientSocketPool::GroupId TestGroupId(
    std::string_view host,
    int port = 80,
    std::string_view scheme = url::kHttpScheme,
    PrivacyMode privacy_mode = PrivacyMode::PRIVACY_MODE_DISABLED,
    NetworkAnonymizationKey network_anonymization_key =
        NetworkAnonymizationKey()) {}

// Make sure |handle| sets load times correctly when it has been assigned a
// reused socket.
void TestLoadTimingInfoConnectedReused(const ClientSocketHandle& handle) {}

// 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) {}

// Make sure |handle| sets load times correctly, in the case that it does not
// currently have a socket.
void TestLoadTimingInfoNotConnected(const ClientSocketHandle& handle) {}

class MockClientSocket : public StreamSocket {};

class TestConnectJob;

class MockClientSocketFactory : public ClientSocketFactory {};

class TestConnectJob : public ConnectJob {};

class TestConnectJobFactory : public ConnectJobFactory {};

}  // namespace

namespace {

void MockClientSocketFactory::SignalJobs() {}

void MockClientSocketFactory::SignalJob(size_t job) {}

void MockClientSocketFactory::SetJobLoadState(size_t job,
                                              LoadState load_state) {}

void MockClientSocketFactory::SetJobHasEstablishedConnection(size_t job) {}

class ClientSocketPoolBaseTest : public TestWithTaskEnvironment {};

TEST_F(ClientSocketPoolBaseTest, BasicSynchronous) {}

TEST_F(ClientSocketPoolBaseTest, InitConnectionFailure) {}

// Test releasing an open socket into the socket pool, telling the socket pool
// to close the socket.
TEST_F(ClientSocketPoolBaseTest, ReleaseAndCloseConnection) {}

TEST_F(ClientSocketPoolBaseTest, SocketWithUnreadDataReturnedToPool) {}

// Make sure different groups do not share sockets.
TEST_F(ClientSocketPoolBaseTest, GroupSeparation) {}

TEST_F(ClientSocketPoolBaseTest, TotalLimit) {}

TEST_F(ClientSocketPoolBaseTest, TotalLimitReachedNewGroup) {}

TEST_F(ClientSocketPoolBaseTest, TotalLimitRespectsPriority) {}

// Test reprioritizing a request before completion doesn't interfere with
// its completion.
TEST_F(ClientSocketPoolBaseTest, ReprioritizeOne) {}

// Reprioritize a request up past another one and make sure that changes the
// completion order.
TEST_F(ClientSocketPoolBaseTest, ReprioritizeUpReorder) {}

// Reprioritize a request without changing relative priorities and check
// that the order doesn't change.
TEST_F(ClientSocketPoolBaseTest, ReprioritizeUpNoReorder) {}

// Reprioritize a request past down another one and make sure that changes the
// completion order.
TEST_F(ClientSocketPoolBaseTest, ReprioritizeDownReorder) {}

// Reprioritize a request to the same level as another and confirm it is
// put after the old request.
TEST_F(ClientSocketPoolBaseTest, ReprioritizeResetFIFO) {}

TEST_F(ClientSocketPoolBaseTest, TotalLimitRespectsGroupLimit) {}

// Make sure that we count connecting sockets against the total limit.
TEST_F(ClientSocketPoolBaseTest, TotalLimitCountsConnectingSockets) {}

TEST_F(ClientSocketPoolBaseTest, CorrectlyCountStalledGroups) {}

TEST_F(ClientSocketPoolBaseTest, StallAndThenCancelAndTriggerAvailableSocket) {}

TEST_F(ClientSocketPoolBaseTest, CancelStalledSocketAtSocketLimit) {}

TEST_F(ClientSocketPoolBaseTest, CancelPendingSocketAtSocketLimit) {}

TEST_F(ClientSocketPoolBaseTest, WaitForStalledSocketAtSocketLimit) {}

// Regression test for http://crbug.com/40952.
TEST_F(ClientSocketPoolBaseTest, CloseIdleSocketAtSocketLimitDeleteGroup) {}

TEST_F(ClientSocketPoolBaseTest, PendingRequests) {}

TEST_F(ClientSocketPoolBaseTest, PendingRequests_NoKeepAlive) {}

TEST_F(ClientSocketPoolBaseTest, ResetAndCloseSocket) {}

// 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) {}

// 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) {}

TEST_F(ClientSocketPoolBaseTest,
       CancelRequestAndCloseSocketWhenMoreRequestsThanConnectJobs) {}

TEST_F(ClientSocketPoolBaseTest, ConnectCancelConnect) {}

TEST_F(ClientSocketPoolBaseTest, CancelRequest) {}

// 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) {}

// 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) {}

// 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) {}

// Make sure that pending requests get serviced after active requests get
// cancelled.
TEST_F(ClientSocketPoolBaseTest, CancelActiveRequestWithPendingRequests) {}

// Make sure that pending requests get serviced after active requests fail.
TEST_F(ClientSocketPoolBaseTest, FailingActiveRequestWithPendingRequests) {}

// Make sure that pending requests that complete synchronously get serviced
// after active requests fail. See https://crbug.com/723748
TEST_F(ClientSocketPoolBaseTest, HandleMultipleSyncFailuresAfterAsyncFailure) {}

TEST_F(ClientSocketPoolBaseTest, CancelActiveRequestThenRequestSocket) {}

TEST_F(ClientSocketPoolBaseTest, CloseIdleSocketsForced) {}

TEST_F(ClientSocketPoolBaseTest, CloseIdleSocketsInGroupForced) {}

TEST_F(ClientSocketPoolBaseTest, CleanUpUnusableIdleSockets) {}

// Regression test for http://crbug.com/17985.
TEST_F(ClientSocketPoolBaseTest, GroupWithPendingRequestsIsNotEmpty) {}

TEST_F(ClientSocketPoolBaseTest, BasicAsynchronous) {}

TEST_F(ClientSocketPoolBaseTest, InitConnectionAsynchronousFailure) {}

// 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) {}

TEST_F(ClientSocketPoolBaseTest, TwoRequestsCancelOne) {}

TEST_F(ClientSocketPoolBaseTest, CancelRequestLimitsJobs) {}

// When requests and ConnectJobs are not coupled, the request will get serviced
// by whatever comes first.
TEST_F(ClientSocketPoolBaseTest, ReleaseSockets) {}

// The requests are not coupled to the jobs.  So, the requests should finish in
// their priority / insertion order.
TEST_F(ClientSocketPoolBaseTest, PendingJobCompletionOrder) {}

// Test GetLoadState in the case there's only one socket request.
TEST_F(ClientSocketPoolBaseTest, LoadStateOneRequest) {}

// Test GetLoadState in the case there are two socket requests.
TEST_F(ClientSocketPoolBaseTest, LoadStateTwoRequests) {}

// Test GetLoadState in the case the per-group limit is reached.
TEST_F(ClientSocketPoolBaseTest, LoadStateGroupLimit) {}

// Test GetLoadState in the case the per-pool limit is reached.
TEST_F(ClientSocketPoolBaseTest, LoadStatePoolLimit) {}

TEST_F(ClientSocketPoolBaseTest, CertError) {}

TEST_F(ClientSocketPoolBaseTest, AsyncCertError) {}

TEST_F(ClientSocketPoolBaseTest, AdditionalErrorStateSynchronous) {}

TEST_F(ClientSocketPoolBaseTest, AdditionalErrorStateAsynchronous) {}

// Make sure we can reuse sockets.
TEST_F(ClientSocketPoolBaseTest, CleanupTimedOutIdleSocketsReuse) {}

// Make sure we cleanup old unused sockets.
TEST_F(ClientSocketPoolBaseTest, CleanupTimedOutIdleSocketsNoReuse) {}

// Make sure that we process all pending requests even when we're stalling
// because of multiple releasing disconnected sockets.
TEST_F(ClientSocketPoolBaseTest, MultipleReleasingDisconnectedSockets) {}

// 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) {}

TEST_F(ClientSocketPoolBaseTest,
       ReleasingDisconnectedSocketsMaintainsPriorityOrder) {}

class TestReleasingSocketRequest : public TestCompletionCallbackBase {};

TEST_F(ClientSocketPoolBaseTest, AdditionalErrorSocketsDontUseSlot) {}

// 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) {}

TEST_F(ClientSocketPoolBaseTest, DoNotReuseSocketAfterFlush) {}

class ConnectWithinCallback : public TestCompletionCallbackBase {};

TEST_F(ClientSocketPoolBaseTest, AbortAllRequestsOnFlush) {}

TEST_F(ClientSocketPoolBaseTest, BackupSocketWaitsForHostResolution) {}

// Test that no backup socket is created when a ConnectJob connects before it
// completes.
TEST_F(ClientSocketPoolBaseTest, NoBackupSocketWhenConnected) {}

// 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) {}

TEST_F(ClientSocketPoolBaseTest, CancelBackupSocketAfterCancelingAllRequests) {}

TEST_F(ClientSocketPoolBaseTest, CancelBackupSocketAfterFinishingAllRequests) {}

// 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) {}

// Test delayed socket binding when a group is at capacity and one
// of the group's sockets frees up.
TEST_F(ClientSocketPoolBaseTest, DelayedSocketBindingAtGroupCapacity) {}

// 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) {}

// 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) {}

TEST_F(ClientSocketPoolBaseTest, PreferUsedSocketToUnusedSocket) {}

TEST_F(ClientSocketPoolBaseTest, RequestSockets) {}

TEST_F(ClientSocketPoolBaseTest, RequestSocketsWhenAlreadyHaveAConnectJob) {}

TEST_F(ClientSocketPoolBaseTest,
       RequestSocketsWhenAlreadyHaveMultipleConnectJob) {}

TEST_F(ClientSocketPoolBaseTest, RequestSocketsAtMaxSocketLimit) {}

TEST_F(ClientSocketPoolBaseTest, RequestSocketsHitMaxSocketLimit) {}

TEST_F(ClientSocketPoolBaseTest, RequestSocketsCountIdleSockets) {}

TEST_F(ClientSocketPoolBaseTest, RequestSocketsCountActiveSockets) {}

TEST_F(ClientSocketPoolBaseTest, RequestSocketsSynchronous) {}

TEST_F(ClientSocketPoolBaseTest, RequestSocketsSynchronousError) {}

TEST_F(ClientSocketPoolBaseTest, RequestSocketsMultipleTimesDoesNothing) {}

TEST_F(ClientSocketPoolBaseTest, RequestSocketsDifferentNumSockets) {}

TEST_F(ClientSocketPoolBaseTest, PreconnectJobsTakenByNormalRequests) {}

// Checks that fully connected preconnect jobs have no connect times, and are
// marked as reused.
TEST_F(ClientSocketPoolBaseTest, ConnectedPreconnectJobsHaveNoConnectTimes) {}

// http://crbug.com/64940 regression test.
TEST_F(ClientSocketPoolBaseTest, PreconnectClosesIdleSocketRemovesGroup) {}

TEST_F(ClientSocketPoolBaseTest, PreconnectWithoutBackupJob) {}

TEST_F(ClientSocketPoolBaseTest, PreconnectWithBackupJob) {}

// Tests that a preconnect that starts out with unread data can still be used.
// http://crbug.com/334467
TEST_F(ClientSocketPoolBaseTest, PreconnectWithUnreadData) {}

TEST_F(ClientSocketPoolBaseTest, RequestGetsAssignedJob) {}

TEST_F(ClientSocketPoolBaseTest, MultipleRequestsGetAssignedJobs) {}

TEST_F(ClientSocketPoolBaseTest, PreconnectJobGetsAssignedToRequest) {}

TEST_F(ClientSocketPoolBaseTest, HigherPriorityRequestStealsJob) {}

TEST_F(ClientSocketPoolBaseTest, RequestStealsJobFromLowestRequestWithJob) {}

TEST_F(ClientSocketPoolBaseTest, ReprioritizeRequestStealsJob) {}

TEST_F(ClientSocketPoolBaseTest, CancelRequestReassignsJob) {}

TEST_F(ClientSocketPoolBaseTest, JobCompletionReassignsJob) {}

class MockLayeredPool : public HigherLayeredPool {};

// 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) {}

// Tests the case that trying to close an idle socket in a higher layered pool
// fails.
TEST_F(ClientSocketPoolBaseTest,
       CloseIdleSocketsHeldByLayeredPoolWhenNeededFails) {}

// 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) {}

// 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) {}

// 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) {}

TEST_F(ClientSocketPoolBaseTest,
       CloseMultipleIdleSocketsHeldByLayeredPoolWhenNeeded) {}

// 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) {}

// 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) {}

TEST_F(ClientSocketPoolBaseTest, ProxyAuthNoAuthCallback) {}

class TestAuthHelper {};

TEST_F(ClientSocketPoolBaseTest, ProxyAuthOnce) {}

TEST_F(ClientSocketPoolBaseTest, ProxyAuthOnceSync) {}

TEST_F(ClientSocketPoolBaseTest, ProxyAuthOnceFails) {}

TEST_F(ClientSocketPoolBaseTest, ProxyAuthOnceSyncFails) {}

TEST_F(ClientSocketPoolBaseTest, ProxyAuthOnceDeleteHandle) {}

TEST_F(ClientSocketPoolBaseTest, ProxyAuthOnceDeleteHandleSync) {}

TEST_F(ClientSocketPoolBaseTest, ProxyAuthOnceFlushWithError) {}

TEST_F(ClientSocketPoolBaseTest, ProxyAuthTwice) {}

TEST_F(ClientSocketPoolBaseTest, ProxyAuthTwiceFails) {}

// Makes sure that when a bound request is destroyed, a new ConnectJob is
// created, if needed.
TEST_F(ClientSocketPoolBaseTest,
       ProxyAuthCreateNewConnectJobOnDestroyBoundRequest) {}

// Makes sure that when a bound request is destroyed, a new ConnectJob is
// created for another group, if needed.
TEST_F(ClientSocketPoolBaseTest,
       ProxyAuthCreateNewConnectJobOnDestroyBoundRequestDifferentGroups) {}

// 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) {}

enum class RefreshType {};

// Common base class to test RefreshGroup() when called from either
// OnSSLConfigForServersChanged() 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> {};

INSTANTIATE_TEST_SUITE_P();

TEST_P(ClientSocketPoolBaseRefreshTest, RefreshGroupCreatesNewConnectJobs) {}

TEST_P(ClientSocketPoolBaseRefreshTest, RefreshGroupClosesIdleConnectJobs) {}

TEST_F(ClientSocketPoolBaseTest,
       RefreshGroupDoesNotCloseIdleConnectJobsInOtherGroup) {}

TEST_P(ClientSocketPoolBaseRefreshTest, RefreshGroupPreventsSocketReuse) {}

TEST_F(ClientSocketPoolBaseTest,
       RefreshGroupDoesNotPreventSocketReuseInOtherGroup) {}

TEST_P(ClientSocketPoolBaseRefreshTest,
       RefreshGroupReplacesBoundConnectJobOnConnect) {}

TEST_F(ClientSocketPoolBaseTest, RefreshProxyRefreshesAllGroups) {}

TEST_F(ClientSocketPoolBaseTest, RefreshBothPrivacyAndNormalSockets) {}

}  // namespace

}  // namespace net