2 * DEBUG: section 05 Socket Connection Opener
6 #include "comm/ConnOpener.h"
7 #include "comm/Connection.h"
8 #include "comm/Loops.h"
13 #include "icmp/net_db.h"
15 #include "SquidTime.h"
22 CBDATA_NAMESPACED_CLASS_INIT(Comm
, ConnOpener
);
24 Comm::ConnOpener::ConnOpener(Comm::ConnectionPointer
&c
, AsyncCall::Pointer
&handler
, time_t ctimeout
) :
25 AsyncJob("Comm::ConnOpener"),
32 connectTimeout_(ctimeout
),
36 Comm::ConnOpener::~ConnOpener()
42 Comm::ConnOpener::doneAll() const
44 // is the conn_ to be opened still waiting?
46 return AsyncJob::doneAll();
49 // is the callback still to be called?
50 if (callback_
== NULL
|| callback_
->canceled()) {
51 return AsyncJob::doneAll();
58 Comm::ConnOpener::swanSong()
60 // cancel any event watchers
61 // done here to get the "swanSong" mention in cancel debugging.
62 if (calls_
.earlyAbort_
!= NULL
) {
63 calls_
.earlyAbort_
->cancel("Comm::ConnOpener::swanSong");
64 calls_
.earlyAbort_
= NULL
;
66 if (calls_
.timeout_
!= NULL
) {
67 calls_
.timeout_
->cancel("Comm::ConnOpener::swanSong");
68 calls_
.timeout_
= NULL
;
71 if (callback_
!= NULL
) {
72 if (callback_
->canceled())
75 // inform the still-waiting caller we are dying
76 doneConnecting(COMM_ERR_CONNECT
, 0);
79 // rollback what we can from the job state
80 if (temporaryFd_
>= 0) {
81 // doneConnecting() handles partial FD connection cleanup
82 doneConnecting(COMM_ERR_CONNECT
, 0);
89 Comm::ConnOpener::setHost(const char * new_host
)
91 // unset and erase if already set.
95 // set the new one if given.
97 host_
= xstrdup(new_host
);
101 Comm::ConnOpener::getHost() const
107 * Connection attempt are completed. One way or the other.
108 * Pass the results back to the external handler.
109 * NP: on errors the earlyAbort call should be cancelled first with a reason.
112 Comm::ConnOpener::doneConnecting(comm_err_t status
, int xerrno
)
114 // only mark the address good/bad AFTER connect is finished.
117 ipcacheMarkGoodAddr(host_
, conn_
->remote
);
119 ipcacheMarkBadAddr(host_
, conn_
->remote
);
121 if (Config
.onoff
.test_reachability
)
122 netdbDeleteAddrNetwork(conn_
->remote
);
127 if (callback_
!= NULL
) {
128 typedef CommConnectCbParams Params
;
129 Params
¶ms
= GetCommParams
<Params
>(callback_
);
131 params
.flag
= status
;
132 params
.xerrno
= xerrno
;
133 ScheduleCallHere(callback_
);
137 if (temporaryFd_
>= 0) {
138 debugs(5, 4, HERE
<< conn_
<< " closing temp FD " << temporaryFd_
);
139 // it never reached fully open, so cleanup the FD handlers
140 // Note that comm_close() sequence does not happen for partially open FD
141 Comm::SetSelect(temporaryFd_
, COMM_SELECT_WRITE
, NULL
, NULL
, 0);
142 calls_
.earlyAbort_
= NULL
;
143 if (calls_
.timeout_
!= NULL
) {
144 calls_
.timeout_
->cancel("Comm::ConnOpener::doneConnecting");
145 calls_
.timeout_
= NULL
;
147 fd_table
[temporaryFd_
].timeoutHandler
= NULL
;
148 fd_table
[temporaryFd_
].timeout
= 0;
150 fd_close(temporaryFd_
);
154 /* ensure cleared local state, we are done. */
159 Comm::ConnOpener::start()
163 /* get a socket open ready for connecting with */
164 if (temporaryFd_
< 0) {
166 /* outbound sockets have no need to be protocol agnostic. */
167 if (conn_
->remote
.IsIPv4()) {
168 conn_
->local
.SetIPv4();
171 temporaryFd_
= comm_openex(SOCK_STREAM
, IPPROTO_TCP
, conn_
->local
, conn_
->flags
, conn_
->tos
, conn_
->nfmark
, host_
);
172 if (temporaryFd_
< 0) {
173 doneConnecting(COMM_ERR_CONNECT
, 0);
178 typedef CommCbMemFunT
<Comm::ConnOpener
, CommCloseCbParams
> abortDialer
;
179 calls_
.earlyAbort_
= JobCallback(5, 4, abortDialer
, this, Comm::ConnOpener::earlyAbort
);
180 comm_add_close_handler(temporaryFd_
, calls_
.earlyAbort_
);
182 typedef CommCbMemFunT
<Comm::ConnOpener
, CommTimeoutCbParams
> timeoutDialer
;
183 calls_
.timeout_
= JobCallback(5, 4, timeoutDialer
, this, Comm::ConnOpener::timeout
);
184 debugs(5, 3, HERE
<< conn_
<< " timeout " << connectTimeout_
);
186 // Update the fd_table directly because conn_ is not yet storing the FD
187 assert(temporaryFd_
< Squid_MaxFD
);
188 assert(fd_table
[temporaryFd_
].flags
.open
);
189 typedef CommTimeoutCbParams Params
;
190 Params
¶ms
= GetCommParams
<Params
>(calls_
.timeout_
);
192 fd_table
[temporaryFd_
].timeoutHandler
= calls_
.timeout_
;
193 fd_table
[temporaryFd_
].timeout
= squid_curtime
+ (time_t) connectTimeout_
;
195 connectStart_
= squid_curtime
;
200 Comm::ConnOpener::connected()
202 conn_
->fd
= temporaryFd_
;
206 * stats.conn_open is used to account for the number of
207 * connections that we have open to the peer, so we can limit
208 * based on the max-conn option. We need to increment here,
209 * even if the connection may fail.
211 if (peer
*peer
=(conn_
->getPeer()))
212 ++peer
->stats
.conn_open
;
214 lookupLocalAddress();
216 /* TODO: remove these fd_table accesses. But old code still depends on fd_table flags to
217 * indicate the state of a raw fd object being passed around.
218 * Also, legacy code still depends on comm_local_port() with no access to Comm::Connection
219 * when those are done comm_local_port can become one of our member functions to do the below.
221 fd_table
[conn_
->fd
].flags
.open
= 1;
222 fd_table
[conn_
->fd
].local_addr
= conn_
->local
;
225 /** Make an FD connection attempt.
226 * Handles the case(s) when a partially setup connection gets closed early.
229 Comm::ConnOpener::connect()
233 // our parent Jobs signal abort by cancelling their callbacks.
234 if (callback_
== NULL
|| callback_
->canceled())
239 switch (comm_connect_addr(temporaryFd_
, conn_
->remote
) ) {
241 case COMM_INPROGRESS
:
242 // check for timeout FIRST.
243 if (squid_curtime
- connectStart_
> connectTimeout_
) {
244 debugs(5, 5, HERE
<< conn_
<< ": * - ERR took too long already.");
245 calls_
.earlyAbort_
->cancel("Comm::ConnOpener::connect timed out");
246 doneConnecting(COMM_TIMEOUT
, errno
);
249 debugs(5, 5, HERE
<< conn_
<< ": COMM_INPROGRESS");
250 Comm::SetSelect(temporaryFd_
, COMM_SELECT_WRITE
, Comm::ConnOpener::InProgressConnectRetry
, new Pointer(this), 0);
255 debugs(5, 5, HERE
<< conn_
<< ": COMM_OK - connected");
257 doneConnecting(COMM_OK
, 0);
263 // check for timeout FIRST.
264 if (squid_curtime
- connectStart_
> connectTimeout_
) {
265 debugs(5, 5, HERE
<< conn_
<< ": * - ERR took too long to receive response.");
266 calls_
.earlyAbort_
->cancel("Comm::ConnOpener::connect timed out");
267 doneConnecting(COMM_TIMEOUT
, errno
);
268 } else if (failRetries_
< Config
.connect_retries
) {
269 debugs(5, 5, HERE
<< conn_
<< ": * - try again");
270 eventAdd("Comm::ConnOpener::DelayedConnectRetry", Comm::ConnOpener::DelayedConnectRetry
, new Pointer(this), 0.05, 0, false);
273 // send ERROR back to the upper layer.
274 debugs(5, 5, HERE
<< conn_
<< ": * - ERR tried too many times already.");
275 calls_
.earlyAbort_
->cancel("Comm::ConnOpener::connect failed");
276 doneConnecting(COMM_ERR_CONNECT
, errno
);
282 * Lookup local-end address and port of the TCP link just opened.
283 * This ensure the connection local details are set correctly
286 Comm::ConnOpener::lookupLocalAddress()
288 struct addrinfo
*addr
= NULL
;
289 conn_
->local
.InitAddrInfo(addr
);
291 if (getsockname(conn_
->fd
, addr
->ai_addr
, &(addr
->ai_addrlen
)) != 0) {
292 debugs(50, DBG_IMPORTANT
, "ERROR: Failed to retrieve TCP/UDP details for socket: " << conn_
<< ": " << xstrerror());
293 conn_
->local
.FreeAddrInfo(addr
);
297 conn_
->local
= *addr
;
298 conn_
->local
.FreeAddrInfo(addr
);
299 debugs(5, 6, HERE
<< conn_
);
302 /** Abort connection attempt.
303 * Handles the case(s) when a partially setup connection gets closed early.
306 Comm::ConnOpener::earlyAbort(const CommCloseCbParams
&io
)
308 debugs(5, 3, HERE
<< io
.conn
);
309 doneConnecting(COMM_ERR_CLOSING
, io
.xerrno
); // NP: is closing or shutdown better?
313 * Handles the case(s) when a partially setup connection gets timed out.
314 * NP: When commSetConnTimeout accepts generic CommCommonCbParams this can die.
317 Comm::ConnOpener::timeout(const CommTimeoutCbParams
&)
322 /* Legacy Wrapper for the retry event after COMM_INPROGRESS
323 * XXX: As soon as Comm::SetSelect() accepts Async calls we can use a ConnOpener::connect call
326 Comm::ConnOpener::InProgressConnectRetry(int fd
, void *data
)
328 Pointer
*ptr
= static_cast<Pointer
*>(data
);
330 if (ConnOpener
*cs
= ptr
->valid()) {
331 // Ew. we are now outside the all AsyncJob protections.
332 // get back inside by scheduling another call...
333 typedef NullaryMemFunT
<Comm::ConnOpener
> Dialer
;
334 AsyncCall::Pointer call
= JobCallback(5, 4, Dialer
, cs
, Comm::ConnOpener::connect
);
335 ScheduleCallHere(call
);
340 /* Legacy Wrapper for the retry event with small delay after errors.
341 * XXX: As soon as eventAdd() accepts Async calls we can use a ConnOpener::connect call
344 Comm::ConnOpener::DelayedConnectRetry(void *data
)
346 Pointer
*ptr
= static_cast<Pointer
*>(data
);
348 if (ConnOpener
*cs
= ptr
->valid()) {
349 // Ew. we are now outside the all AsyncJob protections.
350 // get back inside by scheduling another call...
351 typedef NullaryMemFunT
<Comm::ConnOpener
> Dialer
;
352 AsyncCall::Pointer call
= JobCallback(5, 4, Dialer
, cs
, Comm::ConnOpener::connect
);
353 ScheduleCallHere(call
);