2 * DEBUG: section 05 Socket Connection Opener
7 #include "comm/ConnOpener.h"
8 #include "comm/Connection.h"
9 #include "comm/Loops.h"
14 #include "icmp/net_db.h"
17 #include "SquidConfig.h"
18 #include "SquidTime.h"
26 CBDATA_NAMESPACED_CLASS_INIT(Comm
, ConnOpener
);
28 Comm::ConnOpener::ConnOpener(Comm::ConnectionPointer
&c
, AsyncCall::Pointer
&handler
, time_t ctimeout
) :
29 AsyncJob("Comm::ConnOpener"),
36 deadline_(squid_curtime
+ static_cast<time_t>(ctimeout
))
39 Comm::ConnOpener::~ConnOpener()
45 Comm::ConnOpener::doneAll() const
47 // is the conn_ to be opened still waiting?
49 return AsyncJob::doneAll();
52 // is the callback still to be called?
53 if (callback_
== NULL
|| callback_
->canceled()) {
54 return AsyncJob::doneAll();
57 // otherwise, we must be waiting for something
58 Must(temporaryFd_
>= 0 || calls_
.sleep_
);
63 Comm::ConnOpener::swanSong()
65 if (callback_
!= NULL
) {
66 // inform the still-waiting caller we are dying
67 sendAnswer(COMM_ERR_CONNECT
, 0, "Comm::ConnOpener::swanSong");
70 // did we abort with a temporary FD assigned?
71 if (temporaryFd_
>= 0)
74 // did we abort while waiting between retries?
82 Comm::ConnOpener::setHost(const char * new_host
)
84 // unset and erase if already set.
88 // set the new one if given.
90 host_
= xstrdup(new_host
);
94 Comm::ConnOpener::getHost() const
100 * Connection attempt are completed. One way or the other.
101 * Pass the results back to the external handler.
104 Comm::ConnOpener::sendAnswer(comm_err_t errFlag
, int xerrno
, const char *why
)
106 // only mark the address good/bad AFTER connect is finished.
108 if (xerrno
== 0) // XXX: should not we use errFlag instead?
109 ipcacheMarkGoodAddr(host_
, conn_
->remote
);
111 ipcacheMarkBadAddr(host_
, conn_
->remote
);
113 if (Config
.onoff
.test_reachability
)
114 netdbDeleteAddrNetwork(conn_
->remote
);
119 if (callback_
!= NULL
) {
120 // avoid scheduling cancelled callbacks, assuming they are common
121 // enough to make this extra check an optimization
122 if (callback_
->canceled()) {
123 debugs(5, 4, conn_
<< " not calling canceled " << *callback_
<<
124 " [" << callback_
->id
<< ']' );
125 // TODO save the pconn to the pconnPool ?
127 typedef CommConnectCbParams Params
;
128 Params
¶ms
= GetCommParams
<Params
>(callback_
);
130 params
.flag
= errFlag
;
131 params
.xerrno
= xerrno
;
132 ScheduleCallHere(callback_
);
137 // The job will stop without this call because nil callback_ makes
138 // doneAll() true, but this explicit call creates nicer debugging.
142 /// cleans up this job I/O state without closing temporaryFd
143 /// required before closing temporaryFd or keeping it in conn_
144 /// leaves FD bare so must only be called via closeFd() or keepFd()
146 Comm::ConnOpener::cleanFd()
148 debugs(5, 4, HERE
<< conn_
<< " closing temp FD " << temporaryFd_
);
150 Must(temporaryFd_
>= 0);
151 fde
&f
= fd_table
[temporaryFd_
];
153 // Our write_handler was set without using Comm::Write API, so we cannot
154 // use a cancellable Pointer-free job callback and simply cancel it here.
155 if (f
.write_handler
) {
157 /* XXX: We are about to remove write_handler, which was responsible
158 * for deleting write_data, so we have to delete write_data
159 * ourselves. Comm currently calls SetSelect handlers synchronously
160 * so if write_handler is set, we know it has not been called yet.
161 * ConnOpener converts that sync call into an async one, but only
162 * after deleting ptr, so that is not a problem.
165 delete static_cast<Pointer
*>(f
.write_data
);
167 f
.write_handler
= NULL
;
169 // Comm::DoSelect does not do this when calling and resetting write_handler
170 // (because it expects more writes to come?). We could mimic that
171 // optimization by resetting Comm "Select" state only when the FD is
173 Comm::SetSelect(temporaryFd_
, COMM_SELECT_WRITE
, NULL
, NULL
, 0);
175 if (calls_
.timeout_
!= NULL
) {
176 calls_
.timeout_
->cancel("Comm::ConnOpener::cleanFd");
177 calls_
.timeout_
= NULL
;
179 // Comm checkTimeouts() and commCloseAllSockets() do not clear .timeout
180 // when calling timeoutHandler (XXX fix them), so we clear unconditionally.
181 f
.timeoutHandler
= NULL
;
184 if (calls_
.earlyAbort_
!= NULL
) {
185 comm_remove_close_handler(temporaryFd_
, calls_
.earlyAbort_
);
186 calls_
.earlyAbort_
= NULL
;
190 /// cleans I/O state and ends I/O for temporaryFd_
192 Comm::ConnOpener::closeFd()
194 if (temporaryFd_
< 0)
199 // comm_close() below uses COMMIO_FD_WRITECB(fd)->active() to clear Comm
200 // "Select" state. It will not clear ours. XXX: It should always clear
201 // because a callback may have been active but was called before comm_close
202 // Update: we now do this in cleanFd()
203 // Comm::SetSelect(temporaryFd_, COMM_SELECT_WRITE, NULL, NULL, 0);
205 comm_close(temporaryFd_
);
209 /// cleans I/O state and moves temporaryFd_ to the conn_ for long-term use
211 Comm::ConnOpener::keepFd()
214 Must(temporaryFd_
>= 0);
218 conn_
->fd
= temporaryFd_
;
223 Comm::ConnOpener::start()
227 /* outbound sockets have no need to be protocol agnostic. */
228 if (!(Ip::EnableIpv6
&IPV6_SPECIAL_V4MAPPING
) && conn_
->remote
.isIPv4()) {
229 conn_
->local
.setIPv4();
236 /// called at the end of Comm::ConnOpener::DelayedConnectRetry event
238 Comm::ConnOpener::restart()
240 debugs(5, 5, conn_
<< " restarting after sleep");
241 calls_
.sleep_
= false;
247 /// Create a socket for the future connection or return false.
248 /// If false is returned, done() is guaranteed to return true and end the job.
250 Comm::ConnOpener::createFd()
252 Must(temporaryFd_
< 0);
254 // our initators signal abort by cancelling their callbacks
255 if (callback_
== NULL
|| callback_
->canceled())
258 temporaryFd_
= comm_openex(SOCK_STREAM
, IPPROTO_TCP
, conn_
->local
, conn_
->flags
, conn_
->tos
, conn_
->nfmark
, host_
);
259 if (temporaryFd_
< 0) {
260 sendAnswer(COMM_ERR_CONNECT
, 0, "Comm::ConnOpener::createFd");
264 typedef CommCbMemFunT
<Comm::ConnOpener
, CommCloseCbParams
> abortDialer
;
265 calls_
.earlyAbort_
= JobCallback(5, 4, abortDialer
, this, Comm::ConnOpener::earlyAbort
);
266 comm_add_close_handler(temporaryFd_
, calls_
.earlyAbort_
);
268 typedef CommCbMemFunT
<Comm::ConnOpener
, CommTimeoutCbParams
> timeoutDialer
;
269 calls_
.timeout_
= JobCallback(5, 4, timeoutDialer
, this, Comm::ConnOpener::timeout
);
270 debugs(5, 3, conn_
<< " will timeout in " << (deadline_
- squid_curtime
));
272 // Update the fd_table directly because commSetConnTimeout() needs open conn_
273 assert(temporaryFd_
< Squid_MaxFD
);
274 assert(fd_table
[temporaryFd_
].flags
.open
);
275 typedef CommTimeoutCbParams Params
;
276 Params
¶ms
= GetCommParams
<Params
>(calls_
.timeout_
);
278 fd_table
[temporaryFd_
].timeoutHandler
= calls_
.timeout_
;
279 fd_table
[temporaryFd_
].timeout
= deadline_
;
285 Comm::ConnOpener::connected()
287 Must(temporaryFd_
>= 0);
291 * stats.conn_open is used to account for the number of
292 * connections that we have open to the CachePeer, so we can limit
293 * based on the max-conn option. We need to increment here,
294 * even if the connection may fail.
296 if (CachePeer
*peer
=(conn_
->getPeer()))
297 ++peer
->stats
.conn_open
;
299 lookupLocalAddress();
301 /* TODO: remove these fd_table accesses. But old code still depends on fd_table flags to
302 * indicate the state of a raw fd object being passed around.
303 * Also, legacy code still depends on comm_local_port() with no access to Comm::Connection
304 * when those are done comm_local_port can become one of our member functions to do the below.
306 Must(fd_table
[conn_
->fd
].flags
.open
);
307 fd_table
[conn_
->fd
].local_addr
= conn_
->local
;
309 sendAnswer(COMM_OK
, 0, "Comm::ConnOpener::connected");
312 /// Make an FD connection attempt.
314 Comm::ConnOpener::connect()
317 Must(temporaryFd_
>= 0);
321 switch (comm_connect_addr(temporaryFd_
, conn_
->remote
) ) {
323 case COMM_INPROGRESS
:
324 debugs(5, 5, HERE
<< conn_
<< ": COMM_INPROGRESS");
325 Comm::SetSelect(temporaryFd_
, COMM_SELECT_WRITE
, Comm::ConnOpener::InProgressConnectRetry
, new Pointer(this), 0);
329 debugs(5, 5, HERE
<< conn_
<< ": COMM_OK - connected");
334 const int xerrno
= errno
;
337 debugs(5, 7, conn_
<< ": failure #" << failRetries_
<< " <= " <<
338 Config
.connect_retries
<< ": " << xstrerr(xerrno
));
340 if (failRetries_
< Config
.connect_retries
) {
341 debugs(5, 5, HERE
<< conn_
<< ": * - try again");
345 // send ERROR back to the upper layer.
346 debugs(5, 5, HERE
<< conn_
<< ": * - ERR tried too many times already.");
347 sendAnswer(COMM_ERR_CONNECT
, xerrno
, "Comm::ConnOpener::connect");
353 /// Close and wait a little before trying to open and connect again.
355 Comm::ConnOpener::retrySleep()
357 Must(!calls_
.sleep_
);
359 calls_
.sleep_
= true;
360 eventAdd("Comm::ConnOpener::DelayedConnectRetry",
361 Comm::ConnOpener::DelayedConnectRetry
,
362 new Pointer(this), 0.05, 0, false);
365 /// cleans up this job sleep state
367 Comm::ConnOpener::cancelSleep()
370 // It would be nice to delete the sleep event, but it might be out of
371 // the event queue and in the async queue already, so (a) we do not know
372 // whether we can safely delete the call ptr here and (b) eventDelete()
373 // will assert if the event went async. Thus, we let the event run so
374 // that it deletes the call ptr [after this job is gone]. Note that we
375 // are called only when the job ends so this "hanging event" will do
376 // nothing but deleting the call ptr. TODO: Revise eventDelete() API.
377 // eventDelete(Comm::ConnOpener::DelayedConnectRetry, calls_.sleep);
378 calls_
.sleep_
= false;
379 debugs(5, 9, conn_
<< " stops sleeping");
384 * Lookup local-end address and port of the TCP link just opened.
385 * This ensure the connection local details are set correctly
388 Comm::ConnOpener::lookupLocalAddress()
390 struct addrinfo
*addr
= NULL
;
391 Ip::Address::InitAddrInfo(addr
);
393 if (getsockname(conn_
->fd
, addr
->ai_addr
, &(addr
->ai_addrlen
)) != 0) {
394 debugs(50, DBG_IMPORTANT
, "ERROR: Failed to retrieve TCP/UDP details for socket: " << conn_
<< ": " << xstrerror());
395 Ip::Address::FreeAddrInfo(addr
);
399 conn_
->local
= *addr
;
400 Ip::Address::FreeAddrInfo(addr
);
401 debugs(5, 6, HERE
<< conn_
);
404 /** Abort connection attempt.
405 * Handles the case(s) when a partially setup connection gets closed early.
408 Comm::ConnOpener::earlyAbort(const CommCloseCbParams
&io
)
410 debugs(5, 3, HERE
<< io
.conn
);
411 calls_
.earlyAbort_
= NULL
;
412 // NP: is closing or shutdown better?
413 sendAnswer(COMM_ERR_CLOSING
, io
.xerrno
, "Comm::ConnOpener::earlyAbort");
417 * Handles the case(s) when a partially setup connection gets timed out.
418 * NP: When commSetConnTimeout accepts generic CommCommonCbParams this can die.
421 Comm::ConnOpener::timeout(const CommTimeoutCbParams
&)
423 debugs(5, 5, HERE
<< conn_
<< ": * - ERR took too long to receive response.");
424 calls_
.timeout_
= NULL
;
425 sendAnswer(COMM_TIMEOUT
, ETIMEDOUT
, "Comm::ConnOpener::timeout");
428 /* Legacy Wrapper for the retry event after COMM_INPROGRESS
429 * XXX: As soon as Comm::SetSelect() accepts Async calls we can use a ConnOpener::connect call
432 Comm::ConnOpener::InProgressConnectRetry(int fd
, void *data
)
434 Pointer
*ptr
= static_cast<Pointer
*>(data
);
436 if (ConnOpener
*cs
= ptr
->valid()) {
437 // Ew. we are now outside the all AsyncJob protections.
438 // get back inside by scheduling another call...
439 typedef NullaryMemFunT
<Comm::ConnOpener
> Dialer
;
440 AsyncCall::Pointer call
= JobCallback(5, 4, Dialer
, cs
, Comm::ConnOpener::connect
);
441 ScheduleCallHere(call
);
446 /* Legacy Wrapper for the retry event with small delay after errors.
447 * XXX: As soon as eventAdd() accepts Async calls we can use a ConnOpener::restart call
450 Comm::ConnOpener::DelayedConnectRetry(void *data
)
452 Pointer
*ptr
= static_cast<Pointer
*>(data
);
454 if (ConnOpener
*cs
= ptr
->valid()) {
455 // Ew. we are now outside the all AsyncJob protections.
456 // get back inside by scheduling another call...
457 typedef NullaryMemFunT
<Comm::ConnOpener
> Dialer
;
458 AsyncCall::Pointer call
= JobCallback(5, 4, Dialer
, cs
, Comm::ConnOpener::restart
);
459 ScheduleCallHere(call
);