2 * DEBUG: section 05 Socket Connection Opener
8 #include "comm/Connection.h"
9 #include "comm/ConnOpener.h"
10 #include "comm/Loops.h"
14 #include "icmp/net_db.h"
17 #include "SquidConfig.h"
18 #include "SquidTime.h"
24 CBDATA_NAMESPACED_CLASS_INIT(Comm
, ConnOpener
);
26 Comm::ConnOpener::ConnOpener(Comm::ConnectionPointer
&c
, AsyncCall::Pointer
&handler
, time_t ctimeout
) :
27 AsyncJob("Comm::ConnOpener"),
34 deadline_(squid_curtime
+ static_cast<time_t>(ctimeout
))
37 Comm::ConnOpener::~ConnOpener()
43 Comm::ConnOpener::doneAll() const
45 // is the conn_ to be opened still waiting?
47 return AsyncJob::doneAll();
50 // is the callback still to be called?
51 if (callback_
== NULL
|| callback_
->canceled()) {
52 return AsyncJob::doneAll();
55 // otherwise, we must be waiting for something
56 Must(temporaryFd_
>= 0 || calls_
.sleep_
);
61 Comm::ConnOpener::swanSong()
63 if (callback_
!= NULL
) {
64 // inform the still-waiting caller we are dying
65 sendAnswer(Comm::ERR_CONNECT
, 0, "Comm::ConnOpener::swanSong");
68 // did we abort with a temporary FD assigned?
69 if (temporaryFd_
>= 0)
72 // did we abort while waiting between retries?
80 Comm::ConnOpener::setHost(const char * new_host
)
82 // unset and erase if already set.
86 // set the new one if given.
88 host_
= xstrdup(new_host
);
92 Comm::ConnOpener::getHost() const
98 * Connection attempt are completed. One way or the other.
99 * Pass the results back to the external handler.
102 Comm::ConnOpener::sendAnswer(Comm::Flag errFlag
, int xerrno
, const char *why
)
104 // only mark the address good/bad AFTER connect is finished.
106 if (xerrno
== 0) // XXX: should not we use errFlag instead?
107 ipcacheMarkGoodAddr(host_
, conn_
->remote
);
109 ipcacheMarkBadAddr(host_
, conn_
->remote
);
111 if (Config
.onoff
.test_reachability
)
112 netdbDeleteAddrNetwork(conn_
->remote
);
117 if (callback_
!= NULL
) {
118 // avoid scheduling cancelled callbacks, assuming they are common
119 // enough to make this extra check an optimization
120 if (callback_
->canceled()) {
121 debugs(5, 4, conn_
<< " not calling canceled " << *callback_
<<
122 " [" << callback_
->id
<< ']' );
123 // TODO save the pconn to the pconnPool ?
125 typedef CommConnectCbParams Params
;
126 Params
¶ms
= GetCommParams
<Params
>(callback_
);
128 params
.flag
= errFlag
;
129 params
.xerrno
= xerrno
;
130 ScheduleCallHere(callback_
);
135 // The job will stop without this call because nil callback_ makes
136 // doneAll() true, but this explicit call creates nicer debugging.
140 /// cleans up this job I/O state without closing temporaryFd
141 /// required before closing temporaryFd or keeping it in conn_
142 /// leaves FD bare so must only be called via closeFd() or keepFd()
144 Comm::ConnOpener::cleanFd()
146 debugs(5, 4, HERE
<< conn_
<< " closing temp FD " << temporaryFd_
);
148 Must(temporaryFd_
>= 0);
149 fde
&f
= fd_table
[temporaryFd_
];
151 // Our write_handler was set without using Comm::Write API, so we cannot
152 // use a cancellable Pointer-free job callback and simply cancel it here.
153 if (f
.write_handler
) {
155 /* XXX: We are about to remove write_handler, which was responsible
156 * for deleting write_data, so we have to delete write_data
157 * ourselves. Comm currently calls SetSelect handlers synchronously
158 * so if write_handler is set, we know it has not been called yet.
159 * ConnOpener converts that sync call into an async one, but only
160 * after deleting ptr, so that is not a problem.
163 delete static_cast<Pointer
*>(f
.write_data
);
165 f
.write_handler
= NULL
;
167 // Comm::DoSelect does not do this when calling and resetting write_handler
168 // (because it expects more writes to come?). We could mimic that
169 // optimization by resetting Comm "Select" state only when the FD is
171 Comm::SetSelect(temporaryFd_
, COMM_SELECT_WRITE
, NULL
, NULL
, 0);
173 if (calls_
.timeout_
!= NULL
) {
174 calls_
.timeout_
->cancel("Comm::ConnOpener::cleanFd");
175 calls_
.timeout_
= NULL
;
177 // Comm checkTimeouts() and commCloseAllSockets() do not clear .timeout
178 // when calling timeoutHandler (XXX fix them), so we clear unconditionally.
179 f
.timeoutHandler
= NULL
;
182 if (calls_
.earlyAbort_
!= NULL
) {
183 comm_remove_close_handler(temporaryFd_
, calls_
.earlyAbort_
);
184 calls_
.earlyAbort_
= NULL
;
188 /// cleans I/O state and ends I/O for temporaryFd_
190 Comm::ConnOpener::closeFd()
192 if (temporaryFd_
< 0)
197 // comm_close() below uses COMMIO_FD_WRITECB(fd)->active() to clear Comm
198 // "Select" state. It will not clear ours. XXX: It should always clear
199 // because a callback may have been active but was called before comm_close
200 // Update: we now do this in cleanFd()
201 // Comm::SetSelect(temporaryFd_, COMM_SELECT_WRITE, NULL, NULL, 0);
203 comm_close(temporaryFd_
);
207 /// cleans I/O state and moves temporaryFd_ to the conn_ for long-term use
209 Comm::ConnOpener::keepFd()
212 Must(temporaryFd_
>= 0);
216 conn_
->fd
= temporaryFd_
;
221 Comm::ConnOpener::start()
225 /* outbound sockets have no need to be protocol agnostic. */
226 if (!(Ip::EnableIpv6
&IPV6_SPECIAL_V4MAPPING
) && conn_
->remote
.isIPv4()) {
227 conn_
->local
.setIPv4();
234 /// called at the end of Comm::ConnOpener::DelayedConnectRetry event
236 Comm::ConnOpener::restart()
238 debugs(5, 5, conn_
<< " restarting after sleep");
239 calls_
.sleep_
= false;
245 /// Create a socket for the future connection or return false.
246 /// If false is returned, done() is guaranteed to return true and end the job.
248 Comm::ConnOpener::createFd()
250 Must(temporaryFd_
< 0);
252 // our initators signal abort by cancelling their callbacks
253 if (callback_
== NULL
|| callback_
->canceled())
256 temporaryFd_
= comm_openex(SOCK_STREAM
, IPPROTO_TCP
, conn_
->local
, conn_
->flags
, conn_
->tos
, conn_
->nfmark
, host_
);
257 if (temporaryFd_
< 0) {
258 sendAnswer(Comm::ERR_CONNECT
, 0, "Comm::ConnOpener::createFd");
262 typedef CommCbMemFunT
<Comm::ConnOpener
, CommCloseCbParams
> abortDialer
;
263 calls_
.earlyAbort_
= JobCallback(5, 4, abortDialer
, this, Comm::ConnOpener::earlyAbort
);
264 comm_add_close_handler(temporaryFd_
, calls_
.earlyAbort_
);
266 typedef CommCbMemFunT
<Comm::ConnOpener
, CommTimeoutCbParams
> timeoutDialer
;
267 calls_
.timeout_
= JobCallback(5, 4, timeoutDialer
, this, Comm::ConnOpener::timeout
);
268 debugs(5, 3, conn_
<< " will timeout in " << (deadline_
- squid_curtime
));
270 // Update the fd_table directly because commSetConnTimeout() needs open conn_
271 assert(temporaryFd_
< Squid_MaxFD
);
272 assert(fd_table
[temporaryFd_
].flags
.open
);
273 typedef CommTimeoutCbParams Params
;
274 Params
¶ms
= GetCommParams
<Params
>(calls_
.timeout_
);
276 fd_table
[temporaryFd_
].timeoutHandler
= calls_
.timeout_
;
277 fd_table
[temporaryFd_
].timeout
= deadline_
;
283 Comm::ConnOpener::connected()
285 Must(temporaryFd_
>= 0);
289 * stats.conn_open is used to account for the number of
290 * connections that we have open to the CachePeer, so we can limit
291 * based on the max-conn option. We need to increment here,
292 * even if the connection may fail.
294 if (CachePeer
*peer
=(conn_
->getPeer()))
295 ++peer
->stats
.conn_open
;
297 lookupLocalAddress();
299 /* TODO: remove these fd_table accesses. But old code still depends on fd_table flags to
300 * indicate the state of a raw fd object being passed around.
301 * Also, legacy code still depends on comm_local_port() with no access to Comm::Connection
302 * when those are done comm_local_port can become one of our member functions to do the below.
304 Must(fd_table
[conn_
->fd
].flags
.open
);
305 fd_table
[conn_
->fd
].local_addr
= conn_
->local
;
307 sendAnswer(Comm::OK
, 0, "Comm::ConnOpener::connected");
310 /// Make an FD connection attempt.
312 Comm::ConnOpener::doConnect()
315 Must(temporaryFd_
>= 0);
319 switch (comm_connect_addr(temporaryFd_
, conn_
->remote
) ) {
321 case Comm::INPROGRESS
:
322 debugs(5, 5, HERE
<< conn_
<< ": Comm::INPROGRESS");
323 Comm::SetSelect(temporaryFd_
, COMM_SELECT_WRITE
, Comm::ConnOpener::InProgressConnectRetry
, new Pointer(this), 0);
327 debugs(5, 5, HERE
<< conn_
<< ": Comm::OK - connected");
332 const int xerrno
= errno
;
335 debugs(5, 7, conn_
<< ": failure #" << failRetries_
<< " <= " <<
336 Config
.connect_retries
<< ": " << xstrerr(xerrno
));
338 if (failRetries_
< Config
.connect_retries
) {
339 debugs(5, 5, HERE
<< conn_
<< ": * - try again");
343 // send ERROR back to the upper layer.
344 debugs(5, 5, HERE
<< conn_
<< ": * - ERR tried too many times already.");
345 sendAnswer(Comm::ERR_CONNECT
, xerrno
, "Comm::ConnOpener::connect");
351 /// Close and wait a little before trying to open and connect again.
353 Comm::ConnOpener::retrySleep()
355 Must(!calls_
.sleep_
);
357 calls_
.sleep_
= true;
358 eventAdd("Comm::ConnOpener::DelayedConnectRetry",
359 Comm::ConnOpener::DelayedConnectRetry
,
360 new Pointer(this), 0.05, 0, false);
363 /// cleans up this job sleep state
365 Comm::ConnOpener::cancelSleep()
368 // It would be nice to delete the sleep event, but it might be out of
369 // the event queue and in the async queue already, so (a) we do not know
370 // whether we can safely delete the call ptr here and (b) eventDelete()
371 // will assert if the event went async. Thus, we let the event run so
372 // that it deletes the call ptr [after this job is gone]. Note that we
373 // are called only when the job ends so this "hanging event" will do
374 // nothing but deleting the call ptr. TODO: Revise eventDelete() API.
375 // eventDelete(Comm::ConnOpener::DelayedConnectRetry, calls_.sleep);
376 calls_
.sleep_
= false;
377 debugs(5, 9, conn_
<< " stops sleeping");
382 * Lookup local-end address and port of the TCP link just opened.
383 * This ensure the connection local details are set correctly
386 Comm::ConnOpener::lookupLocalAddress()
388 struct addrinfo
*addr
= NULL
;
389 Ip::Address::InitAddrInfo(addr
);
391 if (getsockname(conn_
->fd
, addr
->ai_addr
, &(addr
->ai_addrlen
)) != 0) {
392 debugs(50, DBG_IMPORTANT
, "ERROR: Failed to retrieve TCP/UDP details for socket: " << conn_
<< ": " << xstrerror());
393 Ip::Address::FreeAddrInfo(addr
);
397 conn_
->local
= *addr
;
398 Ip::Address::FreeAddrInfo(addr
);
399 debugs(5, 6, HERE
<< conn_
);
402 /** Abort connection attempt.
403 * Handles the case(s) when a partially setup connection gets closed early.
406 Comm::ConnOpener::earlyAbort(const CommCloseCbParams
&io
)
408 debugs(5, 3, HERE
<< io
.conn
);
409 calls_
.earlyAbort_
= NULL
;
410 // NP: is closing or shutdown better?
411 sendAnswer(Comm::ERR_CLOSING
, io
.xerrno
, "Comm::ConnOpener::earlyAbort");
415 * Handles the case(s) when a partially setup connection gets timed out.
416 * NP: When commSetConnTimeout accepts generic CommCommonCbParams this can die.
419 Comm::ConnOpener::timeout(const CommTimeoutCbParams
&)
421 debugs(5, 5, HERE
<< conn_
<< ": * - ERR took too long to receive response.");
422 calls_
.timeout_
= NULL
;
423 sendAnswer(Comm::TIMEOUT
, ETIMEDOUT
, "Comm::ConnOpener::timeout");
426 /* Legacy Wrapper for the retry event after Comm::INPROGRESS
427 * XXX: As soon as Comm::SetSelect() accepts Async calls we can use a ConnOpener::connect call
430 Comm::ConnOpener::InProgressConnectRetry(int fd
, void *data
)
432 Pointer
*ptr
= static_cast<Pointer
*>(data
);
434 if (ConnOpener
*cs
= ptr
->valid()) {
435 // Ew. we are now outside the all AsyncJob protections.
436 // get back inside by scheduling another call...
437 typedef NullaryMemFunT
<Comm::ConnOpener
> Dialer
;
438 AsyncCall::Pointer call
= JobCallback(5, 4, Dialer
, cs
, Comm::ConnOpener::connect
);
439 ScheduleCallHere(call
);
444 /* Legacy Wrapper for the retry event with small delay after errors.
445 * XXX: As soon as eventAdd() accepts Async calls we can use a ConnOpener::restart call
448 Comm::ConnOpener::DelayedConnectRetry(void *data
)
450 Pointer
*ptr
= static_cast<Pointer
*>(data
);
452 if (ConnOpener
*cs
= ptr
->valid()) {
453 // Ew. we are now outside the all AsyncJob protections.
454 // get back inside by scheduling another call...
455 typedef NullaryMemFunT
<Comm::ConnOpener
> Dialer
;
456 AsyncCall::Pointer call
= JobCallback(5, 4, Dialer
, cs
, Comm::ConnOpener::restart
);
457 ScheduleCallHere(call
);