]>
Commit | Line | Data |
---|---|---|
bbc27441 | 1 | /* |
f6e9a3ee | 2 | * Copyright (C) 1996-2019 The Squid Software Foundation and contributors |
bbc27441 AJ |
3 | * |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
7 | */ | |
8 | ||
e8dca475 | 9 | #include "squid.h" |
a72b6e88 | 10 | #include "AccessLogEntry.h" |
e8dca475 CT |
11 | #include "base/AsyncJobCalls.h" |
12 | #include "base/RunnersRegistry.h" | |
13 | #include "CachePeer.h" | |
14 | #include "comm/Connection.h" | |
15 | #include "comm/ConnOpener.h" | |
16 | #include "Debug.h" | |
17 | #include "fd.h" | |
18 | #include "FwdState.h" | |
19 | #include "globals.h" | |
20 | #include "HttpRequest.h" | |
5ceaee75 | 21 | #include "MasterXaction.h" |
e8dca475 CT |
22 | #include "neighbors.h" |
23 | #include "pconn.h" | |
24 | #include "PeerPoolMgr.h" | |
a72b6e88 | 25 | #include "security/BlindPeerConnector.h" |
e8dca475 | 26 | #include "SquidConfig.h" |
8aec3e1b | 27 | #include "SquidTime.h" |
e8dca475 CT |
28 | |
29 | CBDATA_CLASS_INIT(PeerPoolMgr); | |
30 | ||
a72b6e88 | 31 | /// Gives Security::PeerConnector access to Answer in the PeerPoolMgr callback dialer. |
fcfdf7f9 | 32 | class MyAnswerDialer: public UnaryMemFunT<PeerPoolMgr, Security::EncryptorAnswer, Security::EncryptorAnswer&>, |
a72b6e88 | 33 | public Security::PeerConnector::CbDialer |
e8dca475 CT |
34 | { |
35 | public: | |
36 | MyAnswerDialer(const JobPointer &aJob, Method aMethod): | |
fcfdf7f9 | 37 | UnaryMemFunT<PeerPoolMgr, Security::EncryptorAnswer, Security::EncryptorAnswer&>(aJob, aMethod, Security::EncryptorAnswer()) {} |
e8dca475 | 38 | |
a72b6e88 | 39 | /* Security::PeerConnector::CbDialer API */ |
fcfdf7f9 | 40 | virtual Security::EncryptorAnswer &answer() { return arg1; } |
e8dca475 | 41 | }; |
e8dca475 CT |
42 | |
43 | PeerPoolMgr::PeerPoolMgr(CachePeer *aPeer): AsyncJob("PeerPoolMgr"), | |
f53969cc SM |
44 | peer(cbdataReference(aPeer)), |
45 | request(), | |
46 | opener(), | |
47 | securer(), | |
48 | closer(), | |
49 | addrUsed(0) | |
e8dca475 CT |
50 | { |
51 | } | |
52 | ||
53 | PeerPoolMgr::~PeerPoolMgr() | |
54 | { | |
55 | cbdataReferenceDone(peer); | |
56 | } | |
57 | ||
58 | void | |
59 | PeerPoolMgr::start() | |
60 | { | |
61 | AsyncJob::start(); | |
62 | ||
5ceaee75 | 63 | const MasterXaction::Pointer mx = new MasterXaction(XactionInitiator::initPeerPool); |
e8dca475 CT |
64 | // ErrorState, getOutgoingAddress(), and other APIs may require a request. |
65 | // We fake one. TODO: Optionally send this request to peers? | |
5ceaee75 | 66 | request = new HttpRequest(Http::METHOD_OPTIONS, AnyP::PROTO_HTTP, "http", "*", mx); |
851feda6 | 67 | request->url.host(peer->host); |
e8dca475 CT |
68 | |
69 | checkpoint("peer initialized"); | |
70 | } | |
71 | ||
72 | void | |
73 | PeerPoolMgr::swanSong() | |
74 | { | |
75 | AsyncJob::swanSong(); | |
76 | } | |
77 | ||
78 | bool | |
e2849af8 A |
79 | PeerPoolMgr::validPeer() const |
80 | { | |
e8dca475 CT |
81 | return peer && cbdataReferenceValid(peer) && peer->standby.pool; |
82 | } | |
83 | ||
84 | bool | |
85 | PeerPoolMgr::doneAll() const | |
86 | { | |
87 | return !(validPeer() && peer->standby.limit) && AsyncJob::doneAll(); | |
88 | } | |
89 | ||
90 | void | |
91 | PeerPoolMgr::handleOpenedConnection(const CommConnectCbParams ¶ms) | |
92 | { | |
93 | opener = NULL; | |
94 | ||
95 | if (!validPeer()) { | |
96 | debugs(48, 3, "peer gone"); | |
97 | if (params.conn != NULL) | |
98 | params.conn->close(); | |
99 | return; | |
100 | } | |
101 | ||
c8407295 | 102 | if (params.flag != Comm::OK) { |
e8dca475 CT |
103 | /* it might have been a timeout with a partially open link */ |
104 | if (params.conn != NULL) | |
105 | params.conn->close(); | |
106 | peerConnectFailed(peer); | |
107 | checkpoint("conn opening failure"); // may retry | |
108 | return; | |
109 | } | |
110 | ||
111 | Must(params.conn != NULL); | |
112 | ||
a72b6e88 | 113 | // Handle TLS peers. |
1f1f29e8 | 114 | if (peer->secure.encryptTransport) { |
e8dca475 CT |
115 | typedef CommCbMemFunT<PeerPoolMgr, CommCloseCbParams> CloserDialer; |
116 | closer = JobCallback(48, 3, CloserDialer, this, | |
117 | PeerPoolMgr::handleSecureClosure); | |
118 | comm_add_close_handler(params.conn->fd, closer); | |
119 | ||
120 | securer = asyncCall(48, 4, "PeerPoolMgr::handleSecuredPeer", | |
121 | MyAnswerDialer(this, &PeerPoolMgr::handleSecuredPeer)); | |
8aec3e1b | 122 | |
0ce8e93b | 123 | const int peerTimeout = peerConnectTimeout(peer); |
8aec3e1b CT |
124 | const int timeUsed = squid_curtime - params.conn->startTime(); |
125 | // Use positive timeout when less than one second is left for conn. | |
0ce8e93b | 126 | const int timeLeft = positiveTimeout(peerTimeout - timeUsed); |
a72b6e88 | 127 | auto *connector = new Security::BlindPeerConnector(request, params.conn, securer, nullptr, timeLeft); |
e8dca475 CT |
128 | AsyncJob::Start(connector); // will call our callback |
129 | return; | |
130 | } | |
e8dca475 CT |
131 | |
132 | pushNewConnection(params.conn); | |
133 | } | |
134 | ||
135 | void | |
136 | PeerPoolMgr::pushNewConnection(const Comm::ConnectionPointer &conn) | |
137 | { | |
138 | Must(validPeer()); | |
139 | Must(Comm::IsConnOpen(conn)); | |
140 | peer->standby.pool->push(conn, NULL /* domain */); | |
141 | // push() will trigger a checkpoint() | |
142 | } | |
143 | ||
e8dca475 | 144 | void |
fcfdf7f9 | 145 | PeerPoolMgr::handleSecuredPeer(Security::EncryptorAnswer &answer) |
e8dca475 CT |
146 | { |
147 | Must(securer != NULL); | |
148 | securer = NULL; | |
149 | ||
150 | if (closer != NULL) { | |
151 | if (answer.conn != NULL) | |
152 | comm_remove_close_handler(answer.conn->fd, closer); | |
153 | else | |
154 | closer->cancel("securing completed"); | |
155 | closer = NULL; | |
156 | } | |
157 | ||
158 | if (!validPeer()) { | |
159 | debugs(48, 3, "peer gone"); | |
160 | if (answer.conn != NULL) | |
161 | answer.conn->close(); | |
162 | return; | |
163 | } | |
164 | ||
165 | if (answer.error.get()) { | |
166 | if (answer.conn != NULL) | |
167 | answer.conn->close(); | |
168 | // PeerConnector calls peerConnectFailed() for us; | |
169 | checkpoint("conn securing failure"); // may retry | |
170 | return; | |
171 | } | |
172 | ||
173 | pushNewConnection(answer.conn); | |
174 | } | |
175 | ||
176 | void | |
177 | PeerPoolMgr::handleSecureClosure(const CommCloseCbParams ¶ms) | |
178 | { | |
179 | Must(closer != NULL); | |
180 | Must(securer != NULL); | |
181 | securer->cancel("conn closed by a 3rd party"); | |
182 | securer = NULL; | |
183 | closer = NULL; | |
184 | // allow the closing connection to fully close before we check again | |
185 | Checkpoint(this, "conn closure while securing"); | |
186 | } | |
e8dca475 CT |
187 | |
188 | void | |
189 | PeerPoolMgr::openNewConnection() | |
190 | { | |
191 | // KISS: Do nothing else when we are already doing something. | |
192 | if (opener != NULL || securer != NULL || shutting_down) { | |
193 | debugs(48, 7, "busy: " << opener << '|' << securer << '|' << shutting_down); | |
194 | return; // there will be another checkpoint when we are done opening/securing | |
195 | } | |
196 | ||
197 | // Do not talk to a peer until it is ready. | |
198 | if (!neighborUp(peer)) // provides debugging | |
199 | return; // there will be another checkpoint when peer is up | |
200 | ||
201 | // Do not violate peer limits. | |
202 | if (!peerCanOpenMore(peer)) { // provides debugging | |
203 | peer->standby.waitingForClose = true; // may already be true | |
204 | return; // there will be another checkpoint when a peer conn closes | |
205 | } | |
206 | ||
207 | // Do not violate global restrictions. | |
208 | if (fdUsageHigh()) { | |
209 | debugs(48, 7, "overwhelmed"); | |
210 | peer->standby.waitingForClose = true; // may already be true | |
211 | // There will be another checkpoint when a peer conn closes OR when | |
212 | // a future pop() fails due to an empty pool. See PconnPool::pop(). | |
213 | return; | |
214 | } | |
215 | ||
216 | peer->standby.waitingForClose = false; | |
217 | ||
218 | Comm::ConnectionPointer conn = new Comm::Connection; | |
219 | Must(peer->n_addresses); // guaranteed by neighborUp() above | |
220 | // cycle through all available IP addresses | |
221 | conn->remote = peer->addresses[addrUsed++ % peer->n_addresses]; | |
222 | conn->remote.port(peer->http_port); | |
223 | conn->peerType = STANDBY_POOL; // should be reset by peerSelect() | |
224 | conn->setPeer(peer); | |
225 | getOutgoingAddress(request.getRaw(), conn); | |
226 | GetMarkingsToServer(request.getRaw(), *conn); | |
227 | ||
0ce8e93b | 228 | const int ctimeout = peerConnectTimeout(peer); |
e8dca475 CT |
229 | typedef CommCbMemFunT<PeerPoolMgr, CommConnectCbParams> Dialer; |
230 | opener = JobCallback(48, 5, Dialer, this, PeerPoolMgr::handleOpenedConnection); | |
231 | Comm::ConnOpener *cs = new Comm::ConnOpener(conn, opener, ctimeout); | |
232 | AsyncJob::Start(cs); | |
233 | } | |
234 | ||
235 | void | |
236 | PeerPoolMgr::closeOldConnections(const int howMany) | |
237 | { | |
238 | debugs(48, 8, howMany); | |
239 | peer->standby.pool->closeN(howMany); | |
240 | } | |
241 | ||
242 | void | |
243 | PeerPoolMgr::checkpoint(const char *reason) | |
244 | { | |
245 | if (!validPeer()) { | |
246 | debugs(48, 3, reason << " and peer gone"); | |
247 | return; // nothing to do after our owner dies; the job will quit | |
248 | } | |
249 | ||
250 | const int count = peer->standby.pool->count(); | |
251 | const int limit = peer->standby.limit; | |
252 | debugs(48, 7, reason << " with " << count << " ? " << limit); | |
253 | ||
254 | if (count < limit) | |
255 | openNewConnection(); | |
256 | else if (count > limit) | |
257 | closeOldConnections(count - limit); | |
258 | } | |
259 | ||
260 | void | |
261 | PeerPoolMgr::Checkpoint(const Pointer &mgr, const char *reason) | |
262 | { | |
263 | CallJobHere1(48, 5, mgr, PeerPoolMgr, checkpoint, reason); | |
264 | } | |
265 | ||
266 | /// launches PeerPoolMgrs for peers configured with standby.limit | |
267 | class PeerPoolMgrsRr: public RegisteredRunner | |
268 | { | |
269 | public: | |
270 | /* RegisteredRunner API */ | |
271 | virtual void useConfig() { syncConfig(); } | |
272 | virtual void syncConfig(); | |
273 | }; | |
274 | ||
275 | RunnerRegistrationEntry(PeerPoolMgrsRr); | |
276 | ||
277 | void | |
278 | PeerPoolMgrsRr::syncConfig() | |
279 | { | |
280 | for (CachePeer *p = Config.peers; p; p = p->next) { | |
281 | // On reconfigure, Squid deletes the old config (and old peers in it), | |
282 | // so should always be dealing with a brand new configuration. | |
283 | assert(!p->standby.mgr); | |
284 | assert(!p->standby.pool); | |
285 | if (p->standby.limit) { | |
286 | p->standby.mgr = new PeerPoolMgr(p); | |
287 | p->standby.pool = new PconnPool(p->name, p->standby.mgr); | |
288 | AsyncJob::Start(p->standby.mgr.get()); | |
289 | } | |
290 | } | |
291 | } | |
f53969cc | 292 |