[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 1 | // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| 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_CLIENT_SOCKET_POOL_BASE_H_ |
| 6 | #define NET_SOCKET_CLIENT_SOCKET_POOL_BASE_H_ |
| 7 | |
| 8 | #include <deque> |
| 9 | #include <map> |
| 10 | #include <string> |
| 11 | |
| 12 | #include "base/basictypes.h" |
| 13 | #include "base/scoped_ptr.h" |
| 14 | #include "base/time.h" |
| 15 | #include "base/timer.h" |
| 16 | #include "net/base/address_list.h" |
| 17 | #include "net/base/completion_callback.h" |
| 18 | #include "net/base/host_resolver.h" |
| 19 | #include "net/base/load_states.h" |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 20 | #include "net/socket/client_socket.h" |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 21 | #include "net/socket/client_socket_pool.h" |
| 22 | |
| 23 | namespace net { |
| 24 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 25 | class ClientSocketHandle; |
| 26 | class ClientSocketPoolBase; |
| 27 | |
| 28 | // ConnectJob provides an abstract interface for "connecting" a socket. |
| 29 | // The connection may involve host resolution, tcp connection, ssl connection, |
| 30 | // etc. |
| 31 | class ConnectJob { |
| 32 | public: |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 33 | class Delegate { |
| 34 | public: |
| 35 | Delegate() {} |
| 36 | virtual ~Delegate() {} |
| 37 | |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 38 | // Alerts the delegate that the connection completed. |
| 39 | virtual void OnConnectJobComplete(int result, ConnectJob* job) = 0; |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 40 | |
| 41 | private: |
| 42 | DISALLOW_COPY_AND_ASSIGN(Delegate); |
| 43 | }; |
| 44 | |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 45 | ConnectJob(const std::string& group_name, |
| 46 | const ClientSocketHandle* key_handle, |
| 47 | Delegate* delegate); |
| 48 | virtual ~ConnectJob(); |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 49 | |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 50 | // Accessors |
| 51 | const std::string& group_name() const { return group_name_; } |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 52 | LoadState load_state() const { return load_state_; } |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 53 | const ClientSocketHandle* key_handle() const { return key_handle_; } |
| 54 | |
[email protected] | 8e12ae0 | 2009-07-02 16:15:04 | [diff] [blame] | 55 | // Releases |socket_| to the client. On connection error, this should return |
| 56 | // NULL. |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 57 | ClientSocket* ReleaseSocket() { return socket_.release(); } |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 58 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 59 | // Begins connecting the socket. Returns OK on success, ERR_IO_PENDING if it |
| 60 | // cannot complete synchronously without blocking, or another net error code |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 61 | // on error. In asynchronous completion, the ConnectJob will notify |
| 62 | // |delegate_| via OnConnectJobComplete. In both asynchronous and synchronous |
| 63 | // completion, ReleaseSocket() can be called to acquire the connected socket |
| 64 | // if it succeeded. |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 65 | virtual int Connect() = 0; |
| 66 | |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 67 | protected: |
| 68 | void set_load_state(LoadState load_state) { load_state_ = load_state; } |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 69 | void set_socket(ClientSocket* socket) { socket_.reset(socket); } |
| 70 | ClientSocket* socket() { return socket_.get(); } |
| 71 | Delegate* delegate() { return delegate_; } |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 72 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 73 | private: |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 74 | const std::string group_name_; |
| 75 | // Temporarily needed until we switch to late binding. |
| 76 | const ClientSocketHandle* const key_handle_; |
| 77 | Delegate* const delegate_; |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 78 | LoadState load_state_; |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 79 | scoped_ptr<ClientSocket> socket_; |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 80 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 81 | DISALLOW_COPY_AND_ASSIGN(ConnectJob); |
| 82 | }; |
| 83 | |
| 84 | // A ClientSocketPoolBase is used to restrict the number of sockets open at |
| 85 | // a time. It also maintains a list of idle persistent sockets. |
| 86 | // |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 87 | class ClientSocketPoolBase |
| 88 | : public base::RefCounted<ClientSocketPoolBase>, |
| 89 | public ConnectJob::Delegate { |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 90 | public: |
| 91 | // A Request is allocated per call to RequestSocket that results in |
| 92 | // ERR_IO_PENDING. |
| 93 | struct Request { |
| 94 | // HostResolver::RequestInfo has no default constructor, so fudge something. |
[email protected] | 3a6bc904 | 2009-07-06 18:32:21 | [diff] [blame^] | 95 | Request() |
| 96 | : handle(NULL), |
| 97 | callback(NULL), |
| 98 | priority(0), |
| 99 | resolve_info(std::string(), 0) {} |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 100 | |
| 101 | Request(ClientSocketHandle* handle, |
| 102 | CompletionCallback* callback, |
| 103 | int priority, |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 104 | const HostResolver::RequestInfo& resolve_info) |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 105 | : handle(handle), callback(callback), priority(priority), |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 106 | resolve_info(resolve_info) { |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 107 | } |
| 108 | |
| 109 | ClientSocketHandle* handle; |
| 110 | CompletionCallback* callback; |
| 111 | int priority; |
| 112 | HostResolver::RequestInfo resolve_info; |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 113 | }; |
| 114 | |
| 115 | class ConnectJobFactory { |
| 116 | public: |
| 117 | ConnectJobFactory() {} |
| 118 | virtual ~ConnectJobFactory() {} |
| 119 | |
| 120 | virtual ConnectJob* NewConnectJob( |
| 121 | const std::string& group_name, |
| 122 | const Request& request, |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 123 | ConnectJob::Delegate* delegate) const = 0; |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 124 | |
| 125 | private: |
| 126 | DISALLOW_COPY_AND_ASSIGN(ConnectJobFactory); |
| 127 | }; |
| 128 | |
| 129 | ClientSocketPoolBase(int max_sockets_per_group, |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 130 | ConnectJobFactory* connect_job_factory); |
| 131 | |
| 132 | ~ClientSocketPoolBase(); |
| 133 | |
| 134 | int RequestSocket(const std::string& group_name, |
| 135 | const HostResolver::RequestInfo& resolve_info, |
| 136 | int priority, |
| 137 | ClientSocketHandle* handle, |
| 138 | CompletionCallback* callback); |
| 139 | |
| 140 | void CancelRequest(const std::string& group_name, |
| 141 | const ClientSocketHandle* handle); |
| 142 | |
| 143 | void ReleaseSocket(const std::string& group_name, |
| 144 | ClientSocket* socket); |
| 145 | |
| 146 | void CloseIdleSockets(); |
| 147 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 148 | int idle_socket_count() const { |
| 149 | return idle_socket_count_; |
| 150 | } |
| 151 | |
| 152 | int IdleSocketCountInGroup(const std::string& group_name) const; |
| 153 | |
| 154 | LoadState GetLoadState(const std::string& group_name, |
| 155 | const ClientSocketHandle* handle) const; |
| 156 | |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 157 | virtual void OnConnectJobComplete(int result, ConnectJob* job); |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 158 | |
| 159 | private: |
| 160 | // Entry for a persistent socket which became idle at time |start_time|. |
| 161 | struct IdleSocket { |
| 162 | ClientSocket* socket; |
| 163 | base::TimeTicks start_time; |
| 164 | |
| 165 | // An idle socket should be removed if it can't be reused, or has been idle |
| 166 | // for too long. |now| is the current time value (TimeTicks::Now()). |
| 167 | // |
| 168 | // An idle socket can't be reused if it is disconnected or has received |
| 169 | // data unexpectedly (hence no longer idle). The unread data would be |
| 170 | // mistaken for the beginning of the next response if we were to reuse the |
| 171 | // socket for a new request. |
| 172 | bool ShouldCleanup(base::TimeTicks now) const; |
| 173 | }; |
| 174 | |
| 175 | typedef std::deque<Request> RequestQueue; |
| 176 | typedef std::map<const ClientSocketHandle*, Request> RequestMap; |
| 177 | |
| 178 | // A Group is allocated per group_name when there are idle sockets or pending |
| 179 | // requests. Otherwise, the Group object is removed from the map. |
| 180 | struct Group { |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 181 | Group() : active_socket_count(0) {} |
| 182 | |
| 183 | bool IsEmpty() const { |
| 184 | return active_socket_count == 0 && idle_sockets.empty() && |
| 185 | connecting_requests.empty(); |
| 186 | } |
| 187 | |
| 188 | bool HasAvailableSocketSlot(int max_sockets_per_group) const { |
| 189 | return active_socket_count + |
| 190 | static_cast<int>(connecting_requests.size()) < |
| 191 | max_sockets_per_group; |
| 192 | } |
| 193 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 194 | std::deque<IdleSocket> idle_sockets; |
| 195 | RequestQueue pending_requests; |
| 196 | RequestMap connecting_requests; |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 197 | int active_socket_count; // number of active sockets used by clients |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 198 | }; |
| 199 | |
| 200 | typedef std::map<std::string, Group> GroupMap; |
| 201 | |
| 202 | typedef std::map<const ClientSocketHandle*, ConnectJob*> ConnectJobMap; |
| 203 | |
| 204 | static void InsertRequestIntoQueue(const Request& r, |
| 205 | RequestQueue* pending_requests); |
| 206 | |
| 207 | // Closes all idle sockets if |force| is true. Else, only closes idle |
| 208 | // sockets that timed out or can't be reused. |
| 209 | void CleanupIdleSockets(bool force); |
| 210 | |
| 211 | // Called when the number of idle sockets changes. |
| 212 | void IncrementIdleCount(); |
| 213 | void DecrementIdleCount(); |
| 214 | |
| 215 | // Called via PostTask by ReleaseSocket. |
| 216 | void DoReleaseSocket(const std::string& group_name, ClientSocket* socket); |
| 217 | |
| 218 | // Called when timer_ fires. This method scans the idle sockets removing |
| 219 | // sockets that timed out or can't be reused. |
| 220 | void OnCleanupTimerFired() { |
| 221 | CleanupIdleSockets(false); |
| 222 | } |
| 223 | |
| 224 | // Removes the ConnectJob corresponding to |handle| from the |
| 225 | // |connect_job_map_|. |
| 226 | void RemoveConnectJob(const ClientSocketHandle* handle); |
| 227 | |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 228 | // Same as OnAvailableSocketSlot except it looks up the Group first to see if |
| 229 | // it's there. |
| 230 | void MaybeOnAvailableSocketSlot(const std::string& group_name); |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 231 | |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 232 | // Might delete the Group from |group_map_|. |
| 233 | void OnAvailableSocketSlot(const std::string& group_name, Group* group); |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 234 | |
| 235 | // Process a request from a group's pending_requests queue. |
| 236 | void ProcessPendingRequest(const std::string& group_name, Group* group); |
| 237 | |
[email protected] | 2ab05b5 | 2009-07-01 23:57:58 | [diff] [blame] | 238 | // Assigns |socket| to |handle| and updates |group|'s counters appropriately. |
| 239 | void HandOutSocket(ClientSocket* socket, |
| 240 | bool reused, |
| 241 | ClientSocketHandle* handle, |
| 242 | Group* group); |
| 243 | |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 244 | GroupMap group_map_; |
| 245 | |
| 246 | ConnectJobMap connect_job_map_; |
| 247 | |
| 248 | // Timer used to periodically prune idle sockets that timed out or can't be |
| 249 | // reused. |
| 250 | base::RepeatingTimer<ClientSocketPoolBase> timer_; |
| 251 | |
| 252 | // The total number of idle sockets in the system. |
| 253 | int idle_socket_count_; |
| 254 | |
| 255 | // The maximum number of sockets kept per group. |
| 256 | const int max_sockets_per_group_; |
| 257 | |
[email protected] | ab83889 | 2009-06-30 18:49:05 | [diff] [blame] | 258 | const scoped_ptr<ConnectJobFactory> connect_job_factory_; |
[email protected] | ff579d4 | 2009-06-24 15:47:02 | [diff] [blame] | 259 | |
| 260 | DISALLOW_COPY_AND_ASSIGN(ClientSocketPoolBase); |
| 261 | }; |
| 262 | |
| 263 | } // namespace net |
| 264 | |
| 265 | #endif // NET_SOCKET_CLIENT_SOCKET_POOL_BASE_H_ |