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_HANDLE_H_
6 #define NET_SOCKET_CLIENT_SOCKET_HANDLE_H_
7
8 #include <string>
9
10 #include "base/logging.h"
11 #include "base/ref_counted.h"
12 #include "base/scoped_ptr.h"
13 #include "base/time.h"
14 #include "net/base/completion_callback.h"
15 #include "net/base/load_states.h"
16 #include "net/base/net_errors.h"
17 #include "net/base/request_priority.h"
18 #include "net/socket/client_socket.h"
19 #include "net/socket/client_socket_pool.h"
20
21 namespace net {
22
23 // A container for a ClientSocket.
24 //
25 // The handle's |group_name| uniquely identifies the origin and type of the
26 // connection. It is used by the ClientSocketPool to group similar connected
27 // client socket objects.
28 //
29 class ClientSocketHandle {
30 public:
31 typedef enum {
32 UNUSED = 0, // unused socket that just finished connecting
33 UNUSED_IDLE, // unused socket that has been idle for awhile
34 REUSED_IDLE, // previously used socket
35 NUM_TYPES,
36 } SocketReuseType;
37
38 ClientSocketHandle();
39 ~ClientSocketHandle();
40
41 // Initializes a ClientSocketHandle object, which involves talking to the
42 // ClientSocketPool to obtain a connected socket, possibly reusing one. This
43 // method returns either OK or ERR_IO_PENDING. On ERR_IO_PENDING, |priority|
44 // is used to determine the placement in ClientSocketPool's wait list.
45 //
46 // If this method succeeds, then the socket member will be set to an existing
47 // connected socket if an existing connected socket was available to reuse,
48 // otherwise it will be set to a new connected socket. Consumers can then
49 // call is_reused() to see if the socket was reused. If not reusing an
50 // existing socket, ClientSocketPool may need to establish a new
51 // connection using |socket_params|.
52 //
53 // This method returns ERR_IO_PENDING if it cannot complete synchronously, in
54 // which case the consumer will be notified of completion via |callback|.
55 //
56 // Init may be called multiple times.
57 //
58 // Profiling information for the request is saved to |load_log| if non-NULL.
59 //
60 template <typename SocketParams, typename PoolType>
61 int Init(const std::string& group_name,
62 const SocketParams& socket_params,
63 RequestPriority priority,
64 CompletionCallback* callback,
65 PoolType* pool,
66 LoadLog* load_log);
67
68 // An initialized handle can be reset, which causes it to return to the
69 // un-initialized state. This releases the underlying socket, which in the
70 // case of a socket that still has an established connection, indicates that
71 // the socket may be kept alive for use by a subsequent ClientSocketHandle.
72 //
73 // NOTE: To prevent the socket from being kept alive, be sure to call its
74 // Disconnect method. This will result in the ClientSocketPool deleting the
75 // ClientSocket.
76 void Reset();
77
78 // Used after Init() is called, but before the ClientSocketPool has
79 // initialized the ClientSocketHandle.
80 LoadState GetLoadState() const;
81
82 // Returns true when Init() has completed successfully.
is_initialized()83 bool is_initialized() const { return socket_ != NULL; }
84
85 // Returns the time tick when Init() was called.
init_time()86 base::TimeTicks init_time() const { return init_time_; }
87
88 // Used by ClientSocketPool to initialize the ClientSocketHandle.
set_is_reused(bool is_reused)89 void set_is_reused(bool is_reused) { is_reused_ = is_reused; }
set_socket(ClientSocket * s)90 void set_socket(ClientSocket* s) { socket_.reset(s); }
set_idle_time(base::TimeDelta idle_time)91 void set_idle_time(base::TimeDelta idle_time) { idle_time_ = idle_time; }
92
93 // These may only be used if is_initialized() is true.
group_name()94 const std::string& group_name() const { return group_name_; }
socket()95 ClientSocket* socket() { return socket_.get(); }
release_socket()96 ClientSocket* release_socket() { return socket_.release(); }
is_reused()97 bool is_reused() const { return is_reused_; }
idle_time()98 base::TimeDelta idle_time() const { return idle_time_; }
reuse_type()99 SocketReuseType reuse_type() const {
100 if (is_reused()) {
101 return REUSED_IDLE;
102 } else if (idle_time() == base::TimeDelta()) {
103 return UNUSED;
104 } else {
105 return UNUSED_IDLE;
106 }
107 }
ShouldResendFailedRequest(int error)108 bool ShouldResendFailedRequest(int error) const {
109 // NOTE: we resend a request only if we reused a keep-alive connection.
110 // This automatically prevents an infinite resend loop because we'll run
111 // out of the cached keep-alive connections eventually.
112 if ( // We used a socket that was never idle.
113 reuse_type() == ClientSocketHandle::UNUSED ||
114 // We used an unused, idle socket and got a error that wasn't a TCP RST.
115 (reuse_type() == ClientSocketHandle::UNUSED_IDLE &&
116 (error != OK && error != ERR_CONNECTION_RESET))) {
117 return false;
118 }
119 return true;
120 }
121
122 private:
123 // Called on asynchronous completion of an Init() request.
124 void OnIOComplete(int result);
125
126 // Called on completion (both asynchronous & synchronous) of an Init()
127 // request.
128 void HandleInitCompletion(int result);
129
130 // Resets the state of the ClientSocketHandle. |cancel| indicates whether or
131 // not to try to cancel the request with the ClientSocketPool.
132 void ResetInternal(bool cancel);
133
134 scoped_refptr<ClientSocketPool> pool_;
135 scoped_ptr<ClientSocket> socket_;
136 std::string group_name_;
137 bool is_reused_;
138 CompletionCallbackImpl<ClientSocketHandle> callback_;
139 CompletionCallback* user_callback_;
140 base::TimeDelta idle_time_;
141 base::TimeTicks init_time_;
142
143 DISALLOW_COPY_AND_ASSIGN(ClientSocketHandle);
144 };
145
146 // Template function implementation:
147 template <typename SocketParams, typename PoolType>
Init(const std::string & group_name,const SocketParams & socket_params,RequestPriority priority,CompletionCallback * callback,PoolType * pool,LoadLog * load_log)148 int ClientSocketHandle::Init(const std::string& group_name,
149 const SocketParams& socket_params,
150 RequestPriority priority,
151 CompletionCallback* callback,
152 PoolType* pool,
153 LoadLog* load_log) {
154 CHECK(!group_name.empty());
155 // Note that this will result in a link error if the SocketParams has not been
156 // registered for the PoolType via REGISTER_SOCKET_PARAMS_FOR_POOL (defined in
157 // client_socket_pool.h).
158 CheckIsValidSocketParamsForPool<PoolType, SocketParams>();
159 ResetInternal(true);
160 pool_ = pool;
161 group_name_ = group_name;
162 init_time_ = base::TimeTicks::Now();
163 int rv = pool_->RequestSocket(
164 group_name, &socket_params, priority, this, &callback_, load_log);
165 if (rv == ERR_IO_PENDING) {
166 user_callback_ = callback;
167 } else {
168 HandleInitCompletion(rv);
169 }
170 return rv;
171 }
172
173 } // namespace net
174
175 #endif // NET_SOCKET_CLIENT_SOCKET_HANDLE_H_
176