]> git.ipfire.org Git - thirdparty/squid.git/blame - src/PeerPoolMgr.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / PeerPoolMgr.cc
CommitLineData
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
29CBDATA_CLASS_INIT(PeerPoolMgr);
30
a72b6e88 31/// Gives Security::PeerConnector access to Answer in the PeerPoolMgr callback dialer.
fcfdf7f9 32class MyAnswerDialer: public UnaryMemFunT<PeerPoolMgr, Security::EncryptorAnswer, Security::EncryptorAnswer&>,
a72b6e88 33 public Security::PeerConnector::CbDialer
e8dca475
CT
34{
35public:
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
43PeerPoolMgr::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
53PeerPoolMgr::~PeerPoolMgr()
54{
55 cbdataReferenceDone(peer);
56}
57
58void
59PeerPoolMgr::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
72void
73PeerPoolMgr::swanSong()
74{
75 AsyncJob::swanSong();
76}
77
78bool
e2849af8
A
79PeerPoolMgr::validPeer() const
80{
e8dca475
CT
81 return peer && cbdataReferenceValid(peer) && peer->standby.pool;
82}
83
84bool
85PeerPoolMgr::doneAll() const
86{
87 return !(validPeer() && peer->standby.limit) && AsyncJob::doneAll();
88}
89
90void
91PeerPoolMgr::handleOpenedConnection(const CommConnectCbParams &params)
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
135void
136PeerPoolMgr::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 144void
fcfdf7f9 145PeerPoolMgr::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
176void
177PeerPoolMgr::handleSecureClosure(const CommCloseCbParams &params)
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
188void
189PeerPoolMgr::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
235void
236PeerPoolMgr::closeOldConnections(const int howMany)
237{
238 debugs(48, 8, howMany);
239 peer->standby.pool->closeN(howMany);
240}
241
242void
243PeerPoolMgr::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
260void
261PeerPoolMgr::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
267class PeerPoolMgrsRr: public RegisteredRunner
268{
269public:
270 /* RegisteredRunner API */
271 virtual void useConfig() { syncConfig(); }
272 virtual void syncConfig();
273};
274
275RunnerRegistrationEntry(PeerPoolMgrsRr);
276
277void
278PeerPoolMgrsRr::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