]>
Commit | Line | Data |
---|---|---|
aed188fd AJ |
1 | #include "config.h" |
2 | #include "comm/ConnOpener.h" | |
3 | #include "comm/Connection.h" | |
4 | #include "comm.h" | |
5 | #include "CommCalls.h" | |
6 | #include "fde.h" | |
7 | #include "icmp/net_db.h" | |
8 | #include "SquidTime.h" | |
9 | ||
10 | CBDATA_CLASS_INIT(ConnOpener); | |
11 | ||
12 | ConnOpener::ConnOpener(Comm::ConnectionPointer &c, AsyncCall::Pointer handler) : | |
13 | AsyncJob("ConnOpener"), | |
14 | connect_timeout(Config.Timeout.connect), | |
15 | host(NULL), | |
16 | solo(c), | |
17 | callback(handler), | |
18 | total_tries(0), | |
19 | fail_retries(0), | |
20 | connstart(0) | |
21 | {} | |
22 | ||
23 | ConnOpener::~ConnOpener() | |
24 | { | |
25 | safe_free(host); | |
26 | solo = NULL; | |
27 | } | |
28 | ||
29 | void | |
30 | ConnOpener::setHost(const char * new_host) | |
31 | { | |
85d2870d AJ |
32 | // unset and erase if already set. |
33 | if (host != NULL) | |
34 | safe_free(host); | |
35 | ||
36 | // set the new one if given. | |
37 | if (new_host != NULL) | |
aed188fd | 38 | host = xstrdup(new_host); |
aed188fd AJ |
39 | } |
40 | ||
41 | const char * | |
42 | ConnOpener::getHost() const | |
43 | { | |
44 | return host; | |
45 | } | |
46 | ||
47 | void | |
48 | ConnOpener::callCallback(comm_err_t status, int xerrno) | |
49 | { | |
50 | /* remove handlers we don't want to happen now */ | |
51 | comm_remove_close_handler(solo->fd, ConnOpener::EarlyAbort, this); | |
52 | commSetTimeout(solo->fd, -1, NULL, NULL); | |
53 | ||
54 | typedef CommConnectCbParams Params; | |
55 | Params ¶ms = GetCommParams<Params>(callback); | |
56 | params.conn = solo; | |
57 | params.flag = status; | |
58 | params.xerrno = xerrno; | |
59 | ScheduleCallHere(callback); | |
60 | ||
61 | callback = NULL; | |
62 | delete this; | |
63 | } | |
64 | ||
65 | void | |
66 | ConnOpener::start() | |
67 | { | |
68 | /* handle connecting to one single path */ | |
69 | if (solo->fd < 0) { | |
70 | #if USE_IPV6 | |
71 | /* outbound sockets have no need to be protocol agnostic. */ | |
72 | if (solo->local.IsIPv6() && solo->local.IsIPv4()) { | |
73 | solo->local.SetIPv4(); | |
74 | } | |
75 | #endif | |
76 | solo->fd = comm_openex(SOCK_STREAM, IPPROTO_TCP, solo->local, solo->flags, solo->tos, host); | |
77 | if (solo->fd <= 0) { | |
78 | callCallback(COMM_ERR_CONNECT, 0); | |
79 | return; | |
80 | } | |
81 | ||
82 | AsyncCall::Pointer ea_call = commCbCall(5,4, "ConnOpener::EarlyAbort", | |
83 | CommCloseCbPtrFun(ConnOpener::EarlyAbort, this)); | |
84 | comm_add_close_handler(solo->fd, ea_call); | |
85 | ||
86 | AsyncCall::Pointer timeout_call = commCbCall(5,4, "ConnOpener::ConnectTimeout", | |
87 | CommTimeoutCbPtrFun(ConnOpener::ConnectTimeout, this)); | |
88 | debugs(5, 3, HERE << "FD " << solo->fd << " timeout " << connect_timeout); | |
89 | commSetTimeout(solo->fd, connect_timeout, timeout_call); | |
90 | ||
91 | if (connstart == 0) { | |
92 | connstart = squid_curtime; | |
93 | } | |
94 | } | |
95 | ||
96 | total_tries++; | |
97 | ||
98 | switch (comm_connect_addr(solo->fd, solo->remote) ) { | |
99 | ||
100 | case COMM_INPROGRESS: | |
101 | debugs(5, 5, HERE << "FD " << solo->fd << ": COMM_INPROGRESS"); | |
102 | commSetSelect(solo->fd, COMM_SELECT_WRITE, ConnOpener::ConnectRetry, this, 0); | |
103 | break; | |
104 | ||
105 | case COMM_OK: | |
106 | debugs(5, 5, HERE << "FD " << solo->fd << ": COMM_OK - connected"); | |
107 | ||
108 | /* | |
109 | * stats.conn_open is used to account for the number of | |
110 | * connections that we have open to the peer, so we can limit | |
111 | * based on the max-conn option. We need to increment here, | |
112 | * even if the connection may fail. | |
113 | */ | |
114 | if (solo->getPeer()) | |
115 | solo->getPeer()->stats.conn_open++; | |
116 | ||
117 | /* TODO: remove these fd_table accesses. But old code still depends on fd_table flags to | |
118 | * indicate the state of a raw fd object being passed around. | |
119 | */ | |
120 | fd_table[solo->fd].flags.open = 1; | |
121 | solo->local.SetPort(comm_local_port(solo->fd)); | |
122 | ||
85d2870d AJ |
123 | if (host != NULL) |
124 | ipcacheMarkGoodAddr(host, solo->remote); | |
aed188fd AJ |
125 | callCallback(COMM_OK, 0); |
126 | break; | |
127 | ||
128 | default: | |
129 | debugs(5, 5, HERE "FD " << solo->fd << ": * - try again"); | |
130 | fail_retries++; | |
85d2870d AJ |
131 | if (host != NULL) |
132 | ipcacheMarkBadAddr(host, solo->remote); | |
aed188fd AJ |
133 | #if USE_ICMP |
134 | if (Config.onoff.test_reachability) | |
135 | netdbDeleteAddrNetwork(solo->remote); | |
136 | #endif | |
137 | ||
138 | // check for timeout FIRST. | |
139 | if(squid_curtime - connstart > connect_timeout) { | |
140 | debugs(5, 5, HERE << "FD " << solo->fd << ": * - ERR took too long already."); | |
141 | callCallback(COMM_TIMEOUT, errno); | |
142 | } else if (fail_retries < Config.connect_retries) { | |
143 | start(); | |
144 | } else { | |
145 | // send ERROR back to the upper layer. | |
146 | debugs(5, 5, HERE << "FD " << solo->fd << ": * - ERR tried too many times already."); | |
147 | callCallback(COMM_ERR_CONNECT, errno); | |
148 | } | |
149 | } | |
150 | } | |
151 | ||
152 | void | |
153 | ConnOpener::EarlyAbort(int fd, void *data) | |
154 | { | |
155 | ConnOpener *cs = static_cast<ConnOpener *>(data); | |
156 | debugs(5, 3, HERE << "FD " << fd); | |
157 | cs->callCallback(COMM_ERR_CLOSING, errno); // NP: is closing or shutdown better? | |
158 | ||
159 | /* TODO split cases: | |
160 | * remote end rejecting the connection is normal and one of the other paths may be taken. | |
161 | * squid shutting down or forcing abort on the connection attempt(s) are the only real fatal cases. | |
162 | * we may need separate error codes to send back for these two. | |
163 | */ | |
164 | } | |
165 | ||
166 | void | |
167 | ConnOpener::Connect(void *data) | |
168 | { | |
169 | ConnOpener *cs = static_cast<ConnOpener *>(data); | |
170 | cs->start(); | |
171 | } | |
172 | ||
173 | void | |
174 | ConnOpener::ConnectRetry(int fd, void *data) | |
175 | { | |
176 | ConnOpener *cs = static_cast<ConnOpener *>(data); | |
177 | cs->start(); | |
178 | } | |
179 | ||
180 | void | |
181 | ConnOpener::ConnectTimeout(int fd, void *data) | |
182 | { | |
183 | ConnOpener *cs = static_cast<ConnOpener *>(data); | |
184 | cs->start(); | |
185 | } | |
186 |