1 // Copyright 2012 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef NET_SOCKET_TRANSPORT_CLIENT_SOCKET_POOL_H_ 6 #define NET_SOCKET_TRANSPORT_CLIENT_SOCKET_POOL_H_ 7 8 #include <stddef.h> 9 #include <stdint.h> 10 11 #include <list> 12 #include <map> 13 #include <memory> 14 #include <optional> 15 #include <set> 16 #include <string> 17 #include <utility> 18 #include <vector> 19 20 #include "base/memory/raw_ptr.h" 21 #include "base/memory/scoped_refptr.h" 22 #include "base/memory/weak_ptr.h" 23 #include "base/time/time.h" 24 #include "base/timer/timer.h" 25 #include "net/base/address_list.h" 26 #include "net/base/completion_once_callback.h" 27 #include "net/base/load_states.h" 28 #include "net/base/load_timing_info.h" 29 #include "net/base/net_errors.h" 30 #include "net/base/net_export.h" 31 #include "net/base/network_change_notifier.h" 32 #include "net/base/priority_queue.h" 33 #include "net/base/proxy_chain.h" 34 #include "net/base/request_priority.h" 35 #include "net/log/net_log_with_source.h" 36 #include "net/socket/client_socket_handle.h" 37 #include "net/socket/client_socket_pool.h" 38 #include "net/socket/connect_job.h" 39 #include "net/socket/connection_attempts.h" 40 #include "net/socket/socket_tag.h" 41 #include "net/socket/ssl_client_socket.h" 42 #include "net/socket/stream_socket.h" 43 44 namespace net { 45 46 struct CommonConnectJobParams; 47 class ConnectJobFactory; 48 struct NetLogSource; 49 struct NetworkTrafficAnnotationTag; 50 51 // TransportClientSocketPool establishes network connections through using 52 // ConnectJobs, and maintains a list of idle persistent sockets available for 53 // reuse. It restricts the number of sockets open at a time, both globally, and 54 // for each unique GroupId, which roughly corresponds to origin and privacy mode 55 // setting. TransportClientSocketPool is designed to work with HTTP reuse 56 // semantics, handling each request serially, before reusable sockets are 57 // returned to the socket pool. 58 // 59 // In order to manage connection limits on a per-Proxy basis, separate 60 // TransportClientSocketPools are created for each proxy, and another for 61 // connections that have no proxy. 62 // TransportClientSocketPool is an internal class that implements almost all 63 // the functionality from ClientSocketPool. 64 class NET_EXPORT_PRIVATE TransportClientSocketPool 65 : public ClientSocketPool, 66 public NetworkChangeNotifier::IPAddressObserver, 67 public SSLClientContext::Observer { 68 public: 69 // Reasons for closing sockets. Exposed here for testing. 70 static const char kCertDatabaseChanged[]; 71 static const char kCertVerifierChanged[]; 72 static const char kClosedConnectionReturnedToPool[]; 73 static const char kDataReceivedUnexpectedly[]; 74 static const char kIdleTimeLimitExpired[]; 75 static const char kNetworkChanged[]; 76 static const char kRemoteSideClosedConnection[]; 77 static const char kSocketGenerationOutOfDate[]; 78 static const char kSocketPoolDestroyed[]; 79 static const char kSslConfigChanged[]; 80 81 using Flags = uint32_t; 82 83 // Used to specify specific behavior for the ClientSocketPool. 84 enum Flag { 85 NORMAL = 0, // Normal behavior. 86 NO_IDLE_SOCKETS = 0x1, // Do not return an idle socket. Create a new one. 87 }; 88 89 class NET_EXPORT_PRIVATE Request { 90 public: 91 // If |proxy_auth_callback| is null, proxy auth challenges will 92 // result in an error. 93 Request( 94 ClientSocketHandle* handle, 95 CompletionOnceCallback callback, 96 const ProxyAuthCallback& proxy_auth_callback, 97 RequestPriority priority, 98 const SocketTag& socket_tag, 99 RespectLimits respect_limits, 100 Flags flags, 101 scoped_refptr<SocketParams> socket_params, 102 const std::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag, 103 const NetLogWithSource& net_log); 104 105 Request(const Request&) = delete; 106 Request& operator=(const Request&) = delete; 107 108 ~Request(); 109 handle()110 ClientSocketHandle* handle() const { return handle_; } release_callback()111 CompletionOnceCallback release_callback() { return std::move(callback_); } proxy_auth_callback()112 const ProxyAuthCallback& proxy_auth_callback() const { 113 return proxy_auth_callback_; 114 } priority()115 RequestPriority priority() const { return priority_; } set_priority(RequestPriority priority)116 void set_priority(RequestPriority priority) { priority_ = priority; } respect_limits()117 RespectLimits respect_limits() const { return respect_limits_; } flags()118 Flags flags() const { return flags_; } socket_params()119 SocketParams* socket_params() const { return socket_params_.get(); } proxy_annotation_tag()120 const std::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag() 121 const { 122 return proxy_annotation_tag_; 123 } net_log()124 const NetLogWithSource& net_log() const { return net_log_; } socket_tag()125 const SocketTag& socket_tag() const { return socket_tag_; } job()126 ConnectJob* job() const { return job_; } 127 128 // Associates a ConnectJob with the request. Must be called on a request 129 // that does not already have a job. 130 void AssignJob(ConnectJob* job); 131 132 // Unassigns the request's |job_| and returns it. Must be called on a 133 // request with a job. 134 ConnectJob* ReleaseJob(); 135 136 private: 137 const raw_ptr<ClientSocketHandle> handle_; 138 CompletionOnceCallback callback_; 139 const ProxyAuthCallback proxy_auth_callback_; 140 RequestPriority priority_; 141 const RespectLimits respect_limits_; 142 const Flags flags_; 143 const scoped_refptr<SocketParams> socket_params_; 144 const std::optional<NetworkTrafficAnnotationTag> proxy_annotation_tag_; 145 const NetLogWithSource net_log_; 146 const SocketTag socket_tag_; 147 raw_ptr<ConnectJob> job_ = nullptr; 148 }; 149 150 TransportClientSocketPool( 151 int max_sockets, 152 int max_sockets_per_group, 153 base::TimeDelta unused_idle_socket_timeout, 154 const ProxyChain& proxy_chain, 155 bool is_for_websockets, 156 const CommonConnectJobParams* common_connect_job_params, 157 bool cleanup_on_ip_address_change = true); 158 159 TransportClientSocketPool(const TransportClientSocketPool&) = delete; 160 TransportClientSocketPool& operator=(const TransportClientSocketPool&) = 161 delete; 162 163 // Creates a socket pool with an alternative ConnectJobFactory, for use in 164 // testing. 165 // 166 // |connect_backup_jobs_enabled| can be set to false to disable backup connect 167 // jobs (Which are normally enabled). 168 static std::unique_ptr<TransportClientSocketPool> CreateForTesting( 169 int max_sockets, 170 int max_sockets_per_group, 171 base::TimeDelta unused_idle_socket_timeout, 172 base::TimeDelta used_idle_socket_timeout, 173 const ProxyChain& proxy_chain_, 174 bool is_for_websockets, 175 const CommonConnectJobParams* common_connect_job_params, 176 std::unique_ptr<ConnectJobFactory> connect_job_factory, 177 SSLClientContext* ssl_client_context, 178 bool connect_backup_jobs_enabled); 179 180 ~TransportClientSocketPool() override; 181 182 // See LowerLayeredPool::IsStalled for documentation on this function. 183 bool IsStalled() const override; 184 185 // See LowerLayeredPool for documentation on these functions. It is expected 186 // in the destructor that no higher layer pools remain. 187 void AddHigherLayeredPool(HigherLayeredPool* higher_pool) override; 188 void RemoveHigherLayeredPool(HigherLayeredPool* higher_pool) override; 189 190 // ClientSocketPool implementation: 191 int RequestSocket( 192 const GroupId& group_id, 193 scoped_refptr<SocketParams> params, 194 const std::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag, 195 RequestPriority priority, 196 const SocketTag& socket_tag, 197 RespectLimits respect_limits, 198 ClientSocketHandle* handle, 199 CompletionOnceCallback callback, 200 const ProxyAuthCallback& proxy_auth_callback, 201 const NetLogWithSource& net_log) override; 202 int RequestSockets( 203 const GroupId& group_id, 204 scoped_refptr<SocketParams> params, 205 const std::optional<NetworkTrafficAnnotationTag>& proxy_annotation_tag, 206 int num_sockets, 207 CompletionOnceCallback callback, 208 const NetLogWithSource& net_log) override; 209 void SetPriority(const GroupId& group_id, 210 ClientSocketHandle* handle, 211 RequestPriority priority) override; 212 void CancelRequest(const GroupId& group_id, 213 ClientSocketHandle* handle, 214 bool cancel_connect_job) override; 215 void ReleaseSocket(const GroupId& group_id, 216 std::unique_ptr<StreamSocket> socket, 217 int64_t group_generation) override; 218 void FlushWithError(int error, const char* net_log_reason_utf8) override; 219 void CloseIdleSockets(const char* net_log_reason_utf8) override; 220 void CloseIdleSocketsInGroup(const GroupId& group_id, 221 const char* net_log_reason_utf8) override; 222 int IdleSocketCount() const override; 223 size_t IdleSocketCountInGroup(const GroupId& group_id) const override; 224 LoadState GetLoadState(const GroupId& group_id, 225 const ClientSocketHandle* handle) const override; 226 base::Value GetInfoAsValue(const std::string& name, 227 const std::string& type) const override; 228 bool HasActiveSocket(const GroupId& group_id) const override; 229 RequestInGroupWithHandleHasJobForTesting(const GroupId & group_id,const ClientSocketHandle * handle)230 bool RequestInGroupWithHandleHasJobForTesting( 231 const GroupId& group_id, 232 const ClientSocketHandle* handle) const { 233 return group_map_.find(group_id)->second->RequestWithHandleHasJobForTesting( 234 handle); 235 } 236 NumNeverAssignedConnectJobsInGroupForTesting(const GroupId & group_id)237 size_t NumNeverAssignedConnectJobsInGroupForTesting( 238 const GroupId& group_id) const { 239 return NumNeverAssignedConnectJobsInGroup(group_id); 240 } 241 NumUnassignedConnectJobsInGroupForTesting(const GroupId & group_id)242 size_t NumUnassignedConnectJobsInGroupForTesting( 243 const GroupId& group_id) const { 244 return NumUnassignedConnectJobsInGroup(group_id); 245 } 246 NumConnectJobsInGroupForTesting(const GroupId & group_id)247 size_t NumConnectJobsInGroupForTesting(const GroupId& group_id) const { 248 return NumConnectJobsInGroup(group_id); 249 } 250 NumActiveSocketsInGroupForTesting(const GroupId & group_id)251 int NumActiveSocketsInGroupForTesting(const GroupId& group_id) const { 252 return NumActiveSocketsInGroup(group_id); 253 } 254 HasGroupForTesting(const GroupId & group_id)255 bool HasGroupForTesting(const GroupId& group_id) const { 256 return HasGroup(group_id); 257 } 258 259 static bool connect_backup_jobs_enabled(); 260 static bool set_connect_backup_jobs_enabled(bool enabled); 261 262 // NetworkChangeNotifier::IPAddressObserver methods: 263 void OnIPAddressChanged() override; 264 265 // SSLClientContext::Observer methods. 266 void OnSSLConfigChanged( 267 SSLClientContext::SSLConfigChangeType change_type) override; 268 void OnSSLConfigForServersChanged( 269 const base::flat_set<HostPortPair>& servers) override; 270 271 private: 272 // Entry for a persistent socket which became idle at time |start_time|. 273 struct IdleSocket; 274 275 using RequestQueue = PriorityQueue<std::unique_ptr<Request>>; 276 277 // A Group is allocated per GroupId when there are idle sockets, unbound 278 // request, or bound requests. Otherwise, the Group object is removed from the 279 // map. 280 // 281 // A request is "bound" to a ConnectJob when an unbound ConnectJob encounters 282 // a proxy HTTP auth challenge, and the auth challenge is presented to that 283 // request. Once a request and ConnectJob are bound together: 284 // * All auth challenges the ConnectJob sees will be sent to that request. 285 // * Cancelling the request will cancel the ConnectJob. 286 // * The final result of the ConnectJob, and any returned socket, will only be 287 // sent to that bound request, though if the returned socket is returned to 288 // the socket pool, it can then be used to service any request. 289 // 290 // "assigned" jobs are unbound ConnectJobs that have a corresponding Request. 291 // If there are 5 Jobs and 10 Requests, the 5 highest priority requests are 292 // each assigned a Job. If there are 10 Jobs and 5 Requests, the first 5 Jobs 293 // are each assigned to a request. Assignment is determined by order in their 294 // corresponding arrays. The assignment concept is used to deal with 295 // reprioritizing Jobs, and computing a Request's LoadState. 296 // 297 // |active_socket_count| tracks the number of sockets held by clients. 298 // SanityCheck() will always be true, except during the invocation of a 299 // method. So all public methods expect the Group to pass SanityCheck() when 300 // invoked. 301 class NET_EXPORT_PRIVATE Group : public ConnectJob::Delegate { 302 public: 303 using JobList = std::list<std::unique_ptr<ConnectJob>>; 304 305 struct BoundRequest { 306 BoundRequest(); 307 BoundRequest(std::unique_ptr<ConnectJob> connect_job, 308 std::unique_ptr<Request> request, 309 int64_t generation); 310 BoundRequest(BoundRequest&& other); 311 BoundRequest& operator=(BoundRequest&& other); 312 ~BoundRequest(); 313 314 std::unique_ptr<ConnectJob> connect_job; 315 std::unique_ptr<Request> request; 316 317 // Generation of |connect_job|. If it doesn't match the current 318 // generation, ConnectJob will be destroyed, and a new one created on 319 // completion. 320 int64_t generation; 321 322 // It's not safe to fail a request in a |CancelAllRequestsWithError| call 323 // while it's waiting on user input, as the request may have raw pointers 324 // to objects owned by |connect_job| that it could racily write to after 325 // |connect_job| is destroyed. Instead, just track an error in that case, 326 // and fail the request once the ConnectJob completes. 327 int pending_error; 328 }; 329 330 Group(const GroupId& group_id, 331 TransportClientSocketPool* client_socket_pool); 332 ~Group() override; 333 334 // ConnectJob::Delegate methods: 335 void OnConnectJobComplete(int result, ConnectJob* job) override; 336 void OnNeedsProxyAuth(const HttpResponseInfo& response, 337 HttpAuthController* auth_controller, 338 base::OnceClosure restart_with_auth_callback, 339 ConnectJob* job) override; 340 IsEmpty()341 bool IsEmpty() const { 342 return active_socket_count_ == 0 && idle_sockets_.empty() && 343 jobs_.empty() && unbound_requests_.empty() && 344 bound_requests_.empty(); 345 } 346 HasAvailableSocketSlot(int max_sockets_per_group)347 bool HasAvailableSocketSlot(int max_sockets_per_group) const { 348 return NumActiveSocketSlots() < max_sockets_per_group; 349 } 350 NumActiveSocketSlots()351 int NumActiveSocketSlots() const { 352 return active_socket_count_ + static_cast<int>(jobs_.size()) + 353 static_cast<int>(idle_sockets_.size()) + 354 static_cast<int>(bound_requests_.size()); 355 } 356 357 // Returns true if the group could make use of an additional socket slot, if 358 // it were given one. CanUseAdditionalSocketSlot(int max_sockets_per_group)359 bool CanUseAdditionalSocketSlot(int max_sockets_per_group) const { 360 return HasAvailableSocketSlot(max_sockets_per_group) && 361 unbound_requests_.size() > jobs_.size(); 362 } 363 364 // Returns the priority of the top of the unbound request queue 365 // (which may be less than the maximum priority over the entire 366 // queue, due to how we prioritize requests with |respect_limits| 367 // DISABLED over others). TopPendingPriority()368 RequestPriority TopPendingPriority() const { 369 // NOTE: FirstMax().value()->priority() is not the same as 370 // FirstMax().priority()! 371 return unbound_requests_.FirstMax().value()->priority(); 372 } 373 374 // Set a timer to create a backup job if it takes too long to 375 // create one and if a timer isn't already running. 376 void StartBackupJobTimer(const GroupId& group_id); 377 378 bool BackupJobTimerIsRunning() const; 379 380 // If there's a ConnectJob that's never been assigned to Request, 381 // decrements |never_assigned_job_count_| and returns true. 382 // Otherwise, returns false. 383 bool TryToUseNeverAssignedConnectJob(); 384 385 void AddJob(std::unique_ptr<ConnectJob> job, bool is_preconnect); 386 // Remove |job| from this group, which must already own |job|. Returns the 387 // removed ConnectJob. 388 std::unique_ptr<ConnectJob> RemoveUnboundJob(ConnectJob* job); 389 void RemoveAllUnboundJobs(); 390 has_unbound_requests()391 bool has_unbound_requests() const { return !unbound_requests_.empty(); } 392 unbound_request_count()393 size_t unbound_request_count() const { return unbound_requests_.size(); } 394 395 size_t ConnectJobCount() const; 396 397 // Returns the connect job correspding to |handle|. In particular, if 398 // |handle| is bound to a ConnectJob, returns that job. If |handle| is 399 // "assigned" a ConnectJob, return that job. Otherwise, returns nullptr. 400 ConnectJob* GetConnectJobForHandle(const ClientSocketHandle* handle) const; 401 402 // Inserts the request into the queue based on priority 403 // order. Older requests are prioritized over requests of equal 404 // priority. 405 void InsertUnboundRequest(std::unique_ptr<Request> request); 406 407 // Gets (but does not remove) the next unbound request. Returns 408 // NULL if there are no unbound requests. 409 const Request* GetNextUnboundRequest() const; 410 411 // Gets and removes the next unbound request. Returns NULL if 412 // there are no unbound requests. 413 std::unique_ptr<Request> PopNextUnboundRequest(); 414 415 // Finds the unbound request for |handle| and removes it. Returns 416 // the removed unbound request, or NULL if there was none. 417 std::unique_ptr<Request> FindAndRemoveUnboundRequest( 418 ClientSocketHandle* handle); 419 420 // Sets a pending error for all bound requests. Bound requests may be in the 421 // middle of a callback, so can't be failed at arbitrary points in time. 422 void SetPendingErrorForAllBoundRequests(int pending_error); 423 424 // Attempts to bind the highest priority unbound request to |connect_job|, 425 // and returns the bound request. If the request has previously been bound 426 // to |connect_job|, returns the previously bound request. If there are no 427 // requests, or the highest priority request doesn't have a proxy auth 428 // callback, returns nullptr. 429 const Request* BindRequestToConnectJob(ConnectJob* connect_job); 430 431 // Finds the request, if any, bound to |connect_job|, and returns the 432 // BoundRequest or std::nullopt if there was none. 433 std::optional<BoundRequest> FindAndRemoveBoundRequestForConnectJob( 434 ConnectJob* connect_job); 435 436 // Finds the bound request, if any, corresponding to |client_socket_handle| 437 // and returns it. Destroys the ConnectJob bound to the request, if there 438 // was one. 439 std::unique_ptr<Request> FindAndRemoveBoundRequest( 440 ClientSocketHandle* client_socket_handle); 441 442 // Change the priority of the request named by |*handle|. |*handle| 443 // must refer to a request currently present in the group. If |priority| 444 // is the same as the current priority of the request, this is a no-op. 445 void SetPriority(ClientSocketHandle* handle, RequestPriority priority); 446 IncrementActiveSocketCount()447 void IncrementActiveSocketCount() { active_socket_count_++; } DecrementActiveSocketCount()448 void DecrementActiveSocketCount() { active_socket_count_--; } 449 IncrementGeneration()450 void IncrementGeneration() { generation_++; } 451 452 // Whether the request in |unbound_requests_| with a given handle has a job. 453 bool RequestWithHandleHasJobForTesting( 454 const ClientSocketHandle* handle) const; 455 group_id()456 const GroupId& group_id() { return group_id_; } unassigned_job_count()457 size_t unassigned_job_count() const { return unassigned_jobs_.size(); } jobs()458 const JobList& jobs() const { return jobs_; } idle_sockets()459 const std::list<IdleSocket>& idle_sockets() const { return idle_sockets_; } active_socket_count()460 int active_socket_count() const { return active_socket_count_; } mutable_idle_sockets()461 std::list<IdleSocket>* mutable_idle_sockets() { return &idle_sockets_; } never_assigned_job_count()462 size_t never_assigned_job_count() const { 463 return never_assigned_job_count_; 464 } generation()465 int64_t generation() const { return generation_; } 466 467 private: 468 // Returns the iterator's unbound request after removing it from 469 // the queue. Expects the Group to pass SanityCheck() when called. 470 std::unique_ptr<Request> RemoveUnboundRequest( 471 const RequestQueue::Pointer& pointer); 472 473 // Finds the Request which is associated with the given ConnectJob. 474 // Returns nullptr if none is found. Expects the Group to pass SanityCheck() 475 // when called. 476 RequestQueue::Pointer FindUnboundRequestWithJob( 477 const ConnectJob* job) const; 478 479 // Finds the Request in |unbound_requests_| which is the first request 480 // without a job. Returns a null pointer if all requests have jobs. Does not 481 // expect the Group to pass SanityCheck() when called, but does expect all 482 // jobs to either be assigned to a request or in |unassigned_jobs_|. Expects 483 // that no requests with jobs come after any requests without a job. 484 RequestQueue::Pointer GetFirstRequestWithoutJob() const; 485 486 // Tries to assign an unassigned |job| to a request. If no requests need a 487 // job, |job| is added to |unassigned_jobs_|. 488 // When called, does not expect the Group to pass SanityCheck(), but does 489 // expect it to have passed SanityCheck() before the given ConnectJob was 490 // either created or had the request it was assigned to removed. 491 void TryToAssignUnassignedJob(ConnectJob* job); 492 493 // Tries to assign a job to the given request. If any unassigned jobs are 494 // available, the first unassigned job is assigned to the request. 495 // Otherwise, if the request is ahead of the last request with a job, the 496 // job is stolen from the last request with a job. 497 // When called, does not expect the Group to pass SanityCheck(), but does 498 // expect that: 499 // - the request associated with |request_pointer| must not have 500 // an assigned ConnectJob, 501 // - the first min( jobs_.size(), unbound_requests_.size() - 1 ) Requests 502 // other than the given request must have ConnectJobs, i.e. the group 503 // must have passed SanityCheck() before the passed in Request was either 504 // added or had its job unassigned. 505 void TryToAssignJobToRequest(RequestQueue::Pointer request_pointer); 506 507 // Transfers the associated ConnectJob from one Request to another. Expects 508 // the source request to have a job, and the destination request to not have 509 // a job. Does not expect the Group to pass SanityCheck() when called. 510 void TransferJobBetweenRequests(Request* source, Request* dest); 511 512 // Called when the backup socket timer fires. 513 void OnBackupJobTimerFired(const GroupId& group_id); 514 515 // Checks that: 516 // - |unassigned_jobs_| is empty iff there are at least as many requests 517 // as jobs. 518 // - Exactly the first |jobs_.size() - unassigned_jobs_.size()| requests 519 // have ConnectJobs. 520 // - No requests are assigned a ConnectJob in |unassigned_jobs_|. 521 // - No requests are assigned a ConnectJob not in |jobs_|. 522 // - No two requests are assigned the same ConnectJob. 523 // - All entries in |unassigned_jobs_| are also in |jobs_|. 524 // - There are no duplicate entries in |unassigned_jobs_|. 525 void SanityCheck() const; 526 527 const GroupId group_id_; 528 const raw_ptr<TransportClientSocketPool> client_socket_pool_; 529 530 // Total number of ConnectJobs that have never been assigned to a Request. 531 // Since jobs use late binding to requests, which ConnectJobs have or have 532 // not been assigned to a request are not tracked. This is incremented on 533 // preconnect and decremented when a preconnect is assigned, or when there 534 // are fewer than |never_assigned_job_count_| ConnectJobs. Not incremented 535 // when a request is cancelled. 536 size_t never_assigned_job_count_ = 0; 537 538 std::list<IdleSocket> idle_sockets_; 539 JobList jobs_; // For bookkeeping purposes, there is a copy of the raw 540 // pointer of each element of |jobs_| stored either in 541 // |unassigned_jobs_|, or as the associated |job_| of an 542 // element of |unbound_requests_|. 543 std::list<raw_ptr<ConnectJob, CtnExperimental>> unassigned_jobs_; 544 RequestQueue unbound_requests_; 545 int active_socket_count_ = 0; // number of active sockets used by clients 546 // A timer for when to start the backup job. 547 base::OneShotTimer backup_job_timer_; 548 549 // List of Requests bound to ConnectJobs currently undergoing proxy auth. 550 // The Requests and ConnectJobs in this list do not appear in 551 // |unbound_requests_| or |jobs_|. 552 std::vector<BoundRequest> bound_requests_; 553 554 // An id for the group. It gets incremented every time we FlushWithError() 555 // the socket pool, or refresh the group. This is so that when sockets get 556 // released back to the group, we can make sure that they are discarded 557 // rather than reused. Destroying a group will reset the generation number, 558 // but as that only happens once there are no outstanding sockets or 559 // requests associated with the group, that's harmless. 560 int64_t generation_ = 0; 561 }; 562 563 using GroupMap = std::map<GroupId, Group*>; 564 565 struct CallbackResultPair { 566 CallbackResultPair(); 567 CallbackResultPair(CompletionOnceCallback callback_in, int result_in); 568 CallbackResultPair(CallbackResultPair&& other); 569 CallbackResultPair& operator=(CallbackResultPair&& other); 570 ~CallbackResultPair(); 571 572 CompletionOnceCallback callback; 573 int result; 574 }; 575 576 using PendingCallbackMap = 577 std::map<const ClientSocketHandle*, CallbackResultPair>; 578 579 TransportClientSocketPool( 580 int max_sockets, 581 int max_sockets_per_group, 582 base::TimeDelta unused_idle_socket_timeout, 583 base::TimeDelta used_idle_socket_timeout, 584 const ProxyChain& proxy_chain, 585 bool is_for_websockets, 586 const CommonConnectJobParams* common_connect_job_params, 587 bool cleanup_on_ip_address_change, 588 std::unique_ptr<ConnectJobFactory> connect_job_factory, 589 SSLClientContext* ssl_client_context, 590 bool connect_backup_jobs_enabled); 591 ConnectRetryInterval()592 base::TimeDelta ConnectRetryInterval() const { 593 // TODO(mbelshe): Make this tuned dynamically based on measured RTT. 594 // For now, just use the max retry interval. 595 return base::Milliseconds(kMaxConnectRetryIntervalMs); 596 } 597 598 // TODO(mmenke): de-inline these. NumNeverAssignedConnectJobsInGroup(const GroupId & group_id)599 size_t NumNeverAssignedConnectJobsInGroup(const GroupId& group_id) const { 600 return group_map_.find(group_id)->second->never_assigned_job_count(); 601 } 602 NumUnassignedConnectJobsInGroup(const GroupId & group_id)603 size_t NumUnassignedConnectJobsInGroup(const GroupId& group_id) const { 604 return group_map_.find(group_id)->second->unassigned_job_count(); 605 } 606 NumConnectJobsInGroup(const GroupId & group_id)607 size_t NumConnectJobsInGroup(const GroupId& group_id) const { 608 return group_map_.find(group_id)->second->ConnectJobCount(); 609 } 610 NumActiveSocketsInGroup(const GroupId & group_id)611 int NumActiveSocketsInGroup(const GroupId& group_id) const { 612 return group_map_.find(group_id)->second->active_socket_count(); 613 } 614 615 bool HasGroup(const GroupId& group_id) const; 616 617 // Closes all idle sockets if |force| is true. Else, only closes idle 618 // sockets that timed out or can't be reused. Made public for testing. 619 // |reason| must be non-empty when |force| is true. 620 void CleanupIdleSockets(bool force, const char* net_log_reason_utf8); 621 622 // Closes one idle socket. Picks the first one encountered. 623 // TODO(willchan): Consider a better algorithm for doing this. Perhaps we 624 // should keep an ordered list of idle sockets, and close them in order. 625 // Requires maintaining more state. It's not clear if it's worth it since 626 // I'm not sure if we hit this situation often. 627 bool CloseOneIdleSocket(); 628 629 // Checks higher layered pools to see if they can close an idle connection. 630 bool CloseOneIdleConnectionInHigherLayeredPool(); 631 632 // Closes all idle sockets in |group| if |force| is true. Else, only closes 633 // idle sockets in |group| that timed out with respect to |now| or can't be 634 // reused. 635 void CleanupIdleSocketsInGroup(bool force, 636 Group* group, 637 const base::TimeTicks& now, 638 const char* net_log_reason_utf8); 639 640 Group* GetOrCreateGroup(const GroupId& group_id); 641 void RemoveGroup(const GroupId& group_id); 642 GroupMap::iterator RemoveGroup(GroupMap::iterator it); 643 644 // Called when the number of idle sockets changes. 645 void IncrementIdleCount(); 646 void DecrementIdleCount(); 647 648 // Scans the group map for groups which have an available socket slot and 649 // at least one pending request. Returns true if any groups are stalled, and 650 // if so (and if both |group| and |group_id| are not NULL), fills |group| 651 // and |group_id| with data of the stalled group having highest priority. 652 bool FindTopStalledGroup(Group** group, GroupId* group_id) const; 653 654 // Removes |job| from |group|, which must already own |job|. 655 void RemoveConnectJob(ConnectJob* job, Group* group); 656 657 // Tries to see if we can handle any more requests for |group|. 658 void OnAvailableSocketSlot(const GroupId& group_id, Group* group); 659 660 // Process a pending socket request for a group. 661 void ProcessPendingRequest(const GroupId& group_id, Group* group); 662 663 // Assigns |socket| to |handle| and updates |group|'s counters appropriately. 664 void HandOutSocket(std::unique_ptr<StreamSocket> socket, 665 ClientSocketHandle::SocketReuseType reuse_type, 666 const LoadTimingInfo::ConnectTiming& connect_timing, 667 ClientSocketHandle* handle, 668 base::TimeDelta time_idle, 669 Group* group, 670 const NetLogWithSource& net_log); 671 672 // Adds |socket| to the list of idle sockets for |group|. 673 void AddIdleSocket(std::unique_ptr<StreamSocket> socket, Group* group); 674 675 // Iterates through |group_map_|, canceling all ConnectJobs and deleting 676 // groups if they are no longer needed. 677 void CancelAllConnectJobs(); 678 679 // Iterates through |group_map_|, posting |error| callbacks for all 680 // requests, and then deleting groups if they are no longer needed. 681 void CancelAllRequestsWithError(int error); 682 683 // Returns true if we can't create any more sockets due to the total limit. 684 bool ReachedMaxSocketsLimit() const; 685 686 // This is the internal implementation of RequestSocket(). It differs in that 687 // it does not handle logging into NetLog of the queueing status of 688 // |request|. 689 // |preconnect_done_closure| is used only for preconnect requests. For 690 // preconnect requests, this method returns ERR_IO_PENDING only if a connect 691 // job is created and the connect job didn't finish synchronously. In such 692 // case, |preconnect_done_closure| will be called when the created connect job 693 // will be deleted. 694 // For normal non-preconnect requests, |preconnect_done_closure| must be null. 695 // And this method returns ERR_IO_PENDING when the number of sockets has 696 // reached the limit or the created connect job didn't finish synchronously. 697 // In such a case, the Request with a ClientSocketHandle must be registered to 698 // |group_map_| to receive the completion callback. 699 int RequestSocketInternal(const GroupId& group_id, 700 const Request& request, 701 base::OnceClosure preconnect_done_closure); 702 703 // Assigns an idle socket for the group to the request. 704 // Returns |true| if an idle socket is available, false otherwise. 705 bool AssignIdleSocketToRequest(const Request& request, Group* group); 706 707 static void LogBoundConnectJobToRequest( 708 const NetLogSource& connect_job_source, 709 const Request& request); 710 711 // Same as CloseOneIdleSocket() except it won't close an idle socket in 712 // |group|. If |group| is NULL, it is ignored. Returns true if it closed a 713 // socket. 714 bool CloseOneIdleSocketExceptInGroup(const Group* group); 715 716 // Checks if there are stalled socket groups that should be notified 717 // for possible wakeup. 718 void CheckForStalledSocketGroups(); 719 720 // Posts a task to call InvokeUserCallback() on the next iteration through the 721 // current message loop. Inserts |callback| into |pending_callback_map_|, 722 // keyed by |handle|. Apply |socket_tag| to the socket if socket successfully 723 // created. 724 void InvokeUserCallbackLater(ClientSocketHandle* handle, 725 CompletionOnceCallback callback, 726 int rv, 727 const SocketTag& socket_tag); 728 729 // These correspond to ConnectJob::Delegate methods, and are invoked by the 730 // Group a ConnectJob belongs to. 731 void OnConnectJobComplete(Group* group, int result, ConnectJob* job); 732 void OnNeedsProxyAuth(Group* group, 733 const HttpResponseInfo& response, 734 HttpAuthController* auth_controller, 735 base::OnceClosure restart_with_auth_callback, 736 ConnectJob* job); 737 738 // Invokes the user callback for |handle|. By the time this task has run, 739 // it's possible that the request has been cancelled, so |handle| may not 740 // exist in |pending_callback_map_|. We look up the callback and result code 741 // in |pending_callback_map_|. 742 void InvokeUserCallback(MayBeDangling<ClientSocketHandle> handle); 743 744 // Tries to close idle sockets in a higher level socket pool as long as this 745 // this pool is stalled. 746 void TryToCloseSocketsInLayeredPools(); 747 748 // Closes all idle sockets and cancels all unbound ConnectJobs associated with 749 // |it->second|. Also increments the group's generation number, ensuring any 750 // currently existing handed out socket will be silently closed when it is 751 // returned to the socket pool. Bound ConnectJobs will only be destroyed on 752 // once they complete, as they may be waiting on user input. No request 753 // (including bound ones) will be failed as a result of this call - instead, 754 // new ConnectJobs will be created. 755 // 756 // The group may be removed if this leaves the group empty. The caller must 757 // call CheckForStalledSocketGroups() after all applicable groups have been 758 // refreshed. 759 GroupMap::iterator RefreshGroup(GroupMap::iterator it, 760 const base::TimeTicks& now, 761 const char* net_log_reason_utf8); 762 763 GroupMap group_map_; 764 765 // Map of the ClientSocketHandles for which we have a pending Task to invoke a 766 // callback. This is necessary since, before we invoke said callback, it's 767 // possible that the request is cancelled. 768 PendingCallbackMap pending_callback_map_; 769 770 // The total number of idle sockets in the system. 771 int idle_socket_count_ = 0; 772 773 // Number of connecting sockets across all groups. 774 int connecting_socket_count_ = 0; 775 776 // Number of connected sockets we handed out across all groups. 777 int handed_out_socket_count_ = 0; 778 779 // The maximum total number of sockets. See ReachedMaxSocketsLimit. 780 const int max_sockets_; 781 782 // The maximum number of sockets kept per group. 783 const int max_sockets_per_group_; 784 785 // The time to wait until closing idle sockets. 786 const base::TimeDelta unused_idle_socket_timeout_; 787 const base::TimeDelta used_idle_socket_timeout_; 788 789 const ProxyChain proxy_chain_; 790 791 const bool cleanup_on_ip_address_change_; 792 793 // TODO(vandebo) Remove when backup jobs move to TransportClientSocketPool 794 bool connect_backup_jobs_enabled_; 795 796 // Pools that create connections through |this|. |this| will try to close 797 // their idle sockets when it stalls. Must be empty on destruction. 798 std::set<raw_ptr<HigherLayeredPool, SetExperimental>> higher_pools_; 799 800 const raw_ptr<SSLClientContext> ssl_client_context_; 801 802 #if DCHECK_IS_ON() 803 // Reentrancy guard for RequestSocketInternal(). 804 bool request_in_process_ = false; 805 #endif // DCHECK_IS_ON() 806 807 base::WeakPtrFactory<TransportClientSocketPool> weak_factory_{this}; 808 }; 809 810 } // namespace net 811 812 #endif // NET_SOCKET_TRANSPORT_CLIENT_SOCKET_POOL_H_ 813