HappyConnOpener.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
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
9#include "squid.h"
10#include "AccessLogEntry.h"
11#include "base/AsyncCallbacks.h"
12#include "base/CodeContext.h"
13#include "CachePeer.h"
14#include "errorpage.h"
15#include "FwdState.h"
16#include "HappyConnOpener.h"
17#include "HttpRequest.h"
18#include "ip/QosConfig.h"
19#include "neighbors.h"
20#include "pconn.h"
21#include "PeerPoolMgr.h"
22#include "sbuf/Stream.h"
23#include "SquidConfig.h"
24
26
27// HappyOrderEnforcer optimizes enforcement of the "pause before opening a spare
28// connection" requirements. Its inefficient alternative would add hundreds of
29// concurrent events to the Squid event queue in many busy configurations, one
30// concurrent event per concurrent HappyConnOpener job.
31//
32// EventScheduler::schedule() uses linear search to find the right place for a
33// new event; having hundreds of concurrent events is prohibitively expensive.
34// Both alternatives may have comparable high rate of eventAdd() calls, but
35// HappyOrderEnforcer usually schedules the first or second event (as opposed to
36// events that would be fired after hundreds of already scheduled events, making
37// that linear search a lot longer).
38//
39// EventScheduler::cancel() also uses linear search. HappyOrderEnforcer does not
40// need to cancel scheduled events, while its inefficient alternative may cancel
41// events at a rate comparable to the high eventAdd() rate -- many events would
42// be scheduled in vain because external factors would speed up (or make
43// unnecessary) spare connection attempts, canceling the wait.
44//
45// This optimization is possible only where each job needs to pause for the same
46// amount of time, creating a naturally ordered list of jobs waiting to be
47// resumed. This is why two HappyOrderEnforcers are needed to efficiently honor
48// both happy_eyeballs_connect_timeout and happy_eyeballs_connect_gap
49// directives.
50
55{
56public:
58 HappyOrderEnforcer(const char *aName): name(aName) {}
59
61 void checkpoint();
62
65
68
69 const char * const name;
70
71protected:
72 virtual bool readyNow(const HappyConnOpener &) const = 0;
74
75 bool waiting() const { return waitEnd_ > 0; }
76 bool startedWaiting(const HappyAbsoluteTime lastStart, const int cfgTimeoutMsec) const;
77
78private:
79 static void NoteWaitOver(void *raw);
80 void noteWaitOver();
81
84};
85
86std::ostream &operator <<(std::ostream &os, const HappyConnOpenerAnswer &answer)
87{
88 if (answer.error.set())
89 os << "bad ";
90 if (answer.conn)
91 os << (answer.reused ? "reused " : "new ") << answer.conn;
92 if (answer.n_tries != 1)
93 os << " after " << answer.n_tries;
94 return os;
95}
96
99{
100public:
101 PrimeChanceGiver(): HappyOrderEnforcer("happy_eyeballs_connect_timeout enforcement") {}
102
103 /* HappyOrderEnforcer API */
104 bool readyNow(const HappyConnOpener &job) const override;
105
106private:
107 /* HappyOrderEnforcer API */
109};
110
113{
114public:
115 SpareAllowanceGiver(): HappyOrderEnforcer("happy_eyeballs_connect_gap/happy_eyeballs_connect_limit enforcement") {}
116
117 /* HappyOrderEnforcer API */
118 bool readyNow(const HappyConnOpener &job) const override;
119
123
125 void jobUsedAllowance();
126
128 void jobDroppedAllowance();
129
130private:
131 /* HappyOrderEnforcer API */
133
134 bool concurrencyLimitReached() const;
135 void recordAllowance();
136 void forgetAllowance();
137
140
144};
145
148
149/* HappyOrderEnforcer */
150
151void
153{
155 jobs_.emplace_back(&job);
156 job.spareWaiting.position = std::prev(jobs_.end());
158}
159
160void
162{
163 if (job.spareWaiting.callback) {
164 job.spareWaiting.callback->cancel("HappyOrderEnforcer::dequeue");
165 job.spareWaiting.callback = nullptr;
166 } else {
167 Must(!jobs_.empty());
168 jobs_.erase(job.spareWaiting.position);
169 }
170}
171
172void
174{
175 while (!jobs_.empty()) {
176 if (const auto jobPtr = jobs_.front().valid()) {
177 auto &job = *jobPtr;
178 if (!readyNow(job))
179 break; // the next job cannot be ready earlier (FIFO)
180 CallBack(job.spareWaiting.codeContext, [&] {
181 job.spareWaiting.callback = notify(jobPtr); // and fall through to the next job
182 });
183 }
184 jobs_.pop_front();
185 }
186}
187
188bool
189HappyOrderEnforcer::startedWaiting(const HappyAbsoluteTime lastStart, const int cfgTimeoutMsec) const
190{
191 // Normally, the job would not even be queued if there is no timeout. This
192 // check handles reconfiguration that happened after this job was queued.
193 if (cfgTimeoutMsec <= 0)
194 return false;
195
196 // convert to seconds and adjust for SMP workers to keep aggregated load in
197 // check despite the lack of coordination among workers
198 const auto tout = static_cast<HappyAbsoluteTime>(cfgTimeoutMsec) * Config.workers / 1000.0;
199 const auto newWaitEnd = std::min(lastStart, current_dtime) + tout;
200 if (newWaitEnd <= current_dtime)
201 return false; // no need to wait
202
203 // We cannot avoid event accumulation because calling eventDelete() is
204 // unsafe, but any accumulation will be small because it can only be caused
205 // by hot reconfiguration changes or current time jumps.
206 if (!waiting() || newWaitEnd < waitEnd_) {
207 const auto waitTime = newWaitEnd - current_dtime;
208 eventAdd(name, &HappyOrderEnforcer::NoteWaitOver, const_cast<HappyOrderEnforcer*>(this), waitTime, 0, false);
209 waitEnd_ = newWaitEnd;
210 assert(waiting());
211 }
212
213 return true;
214}
215
216void
218{
219 assert(raw);
220 static_cast<HappyOrderEnforcer*>(raw)->noteWaitOver();
221}
222
223void
225{
226 Must(waiting());
227 waitEnd_ = 0;
228 checkpoint();
229}
230
231/* PrimeChanceGiver */
232
233bool
235{
237}
238
241{
242 return CallJobHere(17, 5, job, HappyConnOpener, noteGavePrimeItsChance);
243}
244
245/* SpareAllowanceGiver */
246
247bool
249{
250 return !concurrencyLimitReached() &&
252}
253
256{
258 return CallJobHere(17, 5, job, HappyConnOpener, noteSpareAllowance);
259}
260
261void
263{
265}
266
267void
269{
271}
272
273void
275{
276 // Without happy_eyeballs_connect_gap, lastAllowanceStart does not matter.
277 // Otherwise, the dropped allowance ought to be the last one, and since it
278 // was allowed, we would still observe the gap even if we do not wait now.
280
282}
283
285void
287{
290 // not a checkpoint(): no other spare can become ready here
291}
292
293void
295{
298 checkpoint();
299}
300
302bool
304{
306 return false; // no limit
307
309 return true; // concurrent spares prohibited regardless of spare level
310
311 // adjust for SMP workers to keep aggregated spare level in check despite
312 // the lack of coordination among workers
313 const auto aggregateLevel = concurrencyLevel * Config.workers;
314 return aggregateLevel >= Config.happyEyeballs.connect_limit;
315}
316
317/* HappyConnOpenerAnswer */
318
320{
321 // XXX: When multiple copies of an answer exist, this delete in one copy
322 // invalidates error in other copies -- their error.get() returns nil. The
323 // current code "works", but probably only because the initiator gets the
324 // error before any answer copies are deleted. Same in ~EncryptorAnswer.
325 delete error.get();
326}
327
328/* HappyConnOpener */
329
330HappyConnOpener::HappyConnOpener(const ResolvedPeers::Pointer &dests, const AsyncCallback<Answer> &callback, const HttpRequest::Pointer &request, const time_t aFwdStart, const int tries, const AccessLogEntry::Pointer &anAle):
331 AsyncJob("HappyConnOpener"),
332 fwdStart(aFwdStart),
333 callback_(callback),
334 destinations(dests),
335 prime(&HappyConnOpener::notePrimeConnectDone, "HappyConnOpener::notePrimeConnectDone"),
336 spare(&HappyConnOpener::noteSpareConnectDone, "HappyConnOpener::noteSpareConnectDone"),
337 ale(anAle),
338 cause(request),
339 n_tries(tries)
340{
342}
343
345{
347 delete lastError;
348}
349
350void
352{
354 if (h)
355 host_ = xstrdup(h);
356}
357
358void
360{
363}
364
365bool
367{
368 if (!callback_)
369 return true; // (probably found a good path and) informed the requestor
370
371 // TODO: Expose AsyncCall::canFire() instead so that code like this can
372 // detect gone initiators without the need to explicitly cancel callbacks.
373 if (callback_->canceled())
374 return true; // the requestor is gone or has lost interest
375
376 if (prime || spare)
377 return false;
378
380 return true; // trying new connection paths prohibited
381
383 return true; // there are no more paths to try
384
385 return false;
386}
387
388void
390{
391 debugs(17, 5, this);
392
393 if (callback_ && !callback_->canceled())
394 sendFailure();
395
396 if (spareWaiting)
397 cancelSpareWait("HappyConnOpener object destructed");
398
399 // TODO: Find an automated, faster way to kill no-longer-needed jobs.
400
401 if (prime) {
402 cancelAttempt(prime, "job finished during a prime attempt");
403 }
404
405 if (spare) {
406 cancelAttempt(spare, "job finished during a spare attempt");
407 if (gotSpareAllowance) {
409 gotSpareAllowance = false;
410 }
411 }
412
414}
415
417std::ostream &
418operator <<(std::ostream &os, const HappyConnOpener::Attempt &attempt)
419{
420 if (!attempt.path)
421 os << '-';
422 else if (attempt.path->isOpen())
423 os << "FD " << attempt.path->fd;
424 else if (attempt.connWait)
425 os << attempt.connWait;
426 else // destination is known; connection closed (and we are not opening any)
427 os << attempt.path->id;
428 return os;
429}
430
431const char *
433{
434 // TODO: In a redesigned status() API, the caller may mimic this approach.
435 static SBuf buf;
436 buf.clear();
437
438 SBufStream os(buf);
439
440 os.write(" [", 2);
441 if (stopReason)
442 os << "Stopped:" << stopReason;
443 if (prime)
444 os << "prime:" << prime;
445 if (spare)
446 os << "spare:" << spare;
447 if (n_tries)
448 os << " tries:" << n_tries;
449 os << " dst:" << *destinations;
450 os << ' ' << id << ']';
451
452 buf = os.buf();
453 return buf.c_str();
454}
455
477{
478 const auto statusCode = cause->flags.needValidation ?
480 return new ErrorState(type, statusCode, cause.getRaw(), ale);
481}
482
486{
487 if (callback_ && !callback_->canceled()) {
488 auto &answer = callback_.answer();
489 answer.conn = conn;
490 answer.n_tries = n_tries;
491 return &answer;
492 }
493 (void)callback_.release();
494 return nullptr;
495}
496
498void
499HappyConnOpener::sendSuccess(const PeerConnectionPointer &conn, const bool reused, const char *connKind)
500{
501 debugs(17, 4, connKind << ": " << conn);
502 if (auto *answer = futureAnswer(conn)) {
503 answer->reused = reused;
504 assert(!answer->error);
505 ScheduleCallHere(callback_.release());
506 }
507}
508
510void
511HappyConnOpener::cancelAttempt(Attempt &attempt, const char *reason)
512{
513 Must(attempt);
514 destinations->reinstatePath(attempt.path); // before attempt.cancel() clears path
515 attempt.cancel(reason);
516}
517
519void
521{
523 if (auto *answer = futureAnswer(lastFailedConnection)) {
524 if (!lastError)
526 answer->error = lastError;
527 assert(answer->error.valid());
528 lastError = nullptr; // the answer owns it now
529 ScheduleCallHere(callback_.release());
530 }
531}
532
533void
535{
538}
539
541void
543{
544 Must(!attempt.path);
545 Must(!attempt.connWait);
546 Must(dest);
547
548 const auto bumpThroughPeer = cause->flags.sslBumped && dest->getPeer();
549 const auto canReuseOld = allowPconn_ && !bumpThroughPeer;
550 if (!canReuseOld || !reuseOldConnection(dest))
551 openFreshConnection(attempt, dest);
552}
553
557bool
559{
561
562 if (const auto pconn = fwdPconnPool->pop(dest, host_, retriable_)) {
563 ++n_tries;
564 dest.finalize(pconn);
565 sendSuccess(dest, true, "reused connection");
566 return true;
567 }
568
569 return false;
570}
571
574void
576{
577#if URL_CHECKSUM_DEBUG
578 entry->mem_obj->checkUrlChecksum();
579#endif
580
581 const auto conn = dest->cloneProfile();
583
585 AsyncCall::Pointer callConnect = asyncCall(48, 5, attempt.callbackMethodName,
586 Dialer(this, attempt.callbackMethod));
587 const time_t connTimeout = dest->connectTimeout(fwdStart);
588 auto cs = new Comm::ConnOpener(conn, callConnect, connTimeout);
589 if (!conn->getPeer())
590 cs->setHost(host_);
591
592 attempt.path = dest; // but not the being-opened conn!
593 attempt.connWait.start(cs, callConnect);
594}
595
597void
599{
600 handleConnOpenerAnswer(prime, params, "new prime connection");
601}
602
604void
606{
607 if (gotSpareAllowance) {
609 gotSpareAllowance = false;
610 }
611 handleConnOpenerAnswer(spare, params, "new spare connection");
612}
613
615void
617{
618 Must(params.conn);
619
620 // finalize the previously selected path before attempt.finish() forgets it
621 auto handledPath = attempt.path;
622 handledPath.finalize(params.conn); // closed on errors
623 attempt.finish();
624
625 ++n_tries;
626
627 if (params.flag == Comm::OK) {
628 sendSuccess(handledPath, false, what);
629 return;
630 }
631
632 debugs(17, 8, what << " failed: " << params.conn);
633
634 // remember the last failure (we forward it if we cannot connect anywhere)
635 lastFailedConnection = handledPath;
636 delete lastError;
637 lastError = nullptr; // in case makeError() throws
639 lastError->xerrno = params.xerrno;
640
642
643 if (spareWaiting)
645
647}
648
650void
652{
654 Must(!prime);
656
658 cancelSpareWait("all primes failed");
660 return; // checkForNewConnection() will open a spare connection ASAP
661 }
662
665
666 // may still be spareWaiting.forSpareAllowance or
667 // may still be spareWaiting.forPrimesToFail
668}
669
671void
676}
677
679void
683
686 TheSpareAllowanceGiver.dequeue(*this); // clears spareWaiting.callback
687}
688
690void
692{
693 debugs(17, 5, "because " << reason);
695
700
702}
703
714void
716{
717 debugs(17, 7, *destinations);
718
719 // The order of the top-level if-statements below is important.
720
721 if (done())
722 return; // bail ASAP to minimize our waste and others delays (state #0)
723
725 Must(currentPeer); // or we would be done() already
726 return; // will continue working (state #1.1)
727 }
728
729 // update stale currentPeer and/or stale spareWaiting
731 debugs(17, 7, "done with peer; " << *currentPeer);
733 cancelSpareWait("done with peer");
734 else
736
737 currentPeer = nullptr;
741 cancelSpareWait("no spares are coming");
743 }
744
745 if (!prime)
747
748 if (!spare && !done())
750
751 // any state is possible at this point
752}
753
754void
756{
760}
761
762void
764{
767
770 return; // will quit or continue working on prime
771 }
772
774 gotSpareAllowance = true;
775
776 auto dest = destinations->extractSpare(*currentPeer); // ought to succeed
777 startConnecting(spare, dest);
778}
779
781void
783{
784 Must(!prime);
785
786 if (destinations->empty())
787 return;
788
789 if (!currentPeer) {
790 auto newPrime = destinations->extractFront();
791 currentPeer = newPrime;
793 debugs(17, 7, "new peer " << *currentPeer);
795 startConnecting(prime, newPrime);
796 if (done()) // probably reused a pconn
797 return;
798
799 Must(prime);
801 return;
802 }
803
804 // currentPeer implies there is a spare attempt; meanwhile, the previous
805 // primary attempt has failed; do another attempt on the primary track
806 if (auto dest = destinations->extractPrime(*currentPeer))
807 startConnecting(prime, dest);
808 // else wait for more prime paths or their exhaustion
809}
810
813void
815{
817 Must(prime);
818 Must(!spare);
820
822 debugs(17, 7, "no spares for " << *currentPeer);
824 return;
825 }
826
828 debugs(17, 7, "concurrent spares are prohibited");
830 return;
831 }
832
833 if (ThePrimeChanceGiver.readyNow(*this)) {
834 debugs(17, 7, "no happy_eyeballs_connect_timeout");
835 return;
836 }
837
840 // wait for a prime connect result or noteGavePrimeItsChance()
841}
842
844void
846{
848 Must(!spare);
850
851 if (spareWaiting)
852 return; // too early
853
855 return; // too late
856
857 if (destinations->empty())
858 return;
859
860 // jobGotInstantAllowance() call conditions below rely on the readyNow() check here
861 if (!ignoreSpareRestrictions && // we have to honor spare restrictions
862 !TheSpareAllowanceGiver.readyNow(*this) && // all new spares must wait
863 destinations->haveSpare(*currentPeer)) { // and we do have a new spare
866 return;
867 }
868
869 if (auto dest = destinations->extractSpare(*currentPeer)) {
870
873 gotSpareAllowance = true;
874 }
875
876 startConnecting(spare, dest);
877 return;
878 }
879
880 // wait for more spare paths or their exhaustion
881}
882
884bool
886{
888 return true;
889
891 debugs(17, 5, "maximum allowed tries exhausted");
892 ranOutOfTimeOrAttemptsEarlier_ = "maximum tries";
893 return true;
894 }
895
897 debugs(17, 5, "forwarding timeout");
898 ranOutOfTimeOrAttemptsEarlier_ = "forwarding timeout";
899 return true;
900 }
901
902 return false;
903}
904
905HappyConnOpener::Attempt::Attempt(const CallbackMethod method, const char *methodName):
906 callbackMethod(method),
907 callbackMethodName(methodName)
908{
909}
910
911void
913{
914 connWait.finish();
915 path = nullptr;
916}
917
918void
920{
921 connWait.cancel(reason);
922 path = nullptr;
923}
924
#define ScheduleCallHere(call)
Definition: AsyncCall.h:166
RefCount< AsyncCallT< Dialer > > asyncCall(int aDebugSection, int aDebugLevel, const char *aName, const Dialer &aDialer)
Definition: AsyncCall.h:156
#define CallJobHere(debugSection, debugLevel, job, Class, method)
Definition: AsyncJobCalls.h:58
void NoteOutgoingConnectionFailure(CachePeer *const peer, const Http::StatusCode code)
Definition: CachePeer.h:243
void CallBack(const CodeContext::Pointer &callbackContext, Fun &&callback)
Definition: CodeContext.h:116
PconnPool * fwdPconnPool
a collection of previously used persistent Squid-to-peer HTTP(S) connections
Definition: FwdState.cc:78
void GetMarkingsToServer(HttpRequest *request, Comm::Connection &conn)
Definition: FwdState.cc:1555
PrimeChanceGiver ThePrimeChanceGiver
SpareAllowanceGiver TheSpareAllowanceGiver
std::ostream & operator<<(std::ostream &os, const HappyConnOpenerAnswer &answer)
reports Answer details (for AsyncCall parameter debugging)
std::list< CbcPointer< HappyConnOpener > > HappySpareWaitList
A FIFO queue of HappyConnOpener jobs waiting to open a spare connection.
double HappyAbsoluteTime
absolute time in fractional seconds; compatible with current_timed
class SquidConfig Config
Definition: SquidConfig.cc:12
#define Must(condition)
Definition: TextException.h:75
int conn
the current server connection FD
Definition: Transport.cc:26
#define assert(EX)
Definition: assert.h:17
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:320
bool cancel(const char *reason)
Definition: AsyncCall.cc:56
a smart AsyncCall pointer for delivery of future results
bool done() const
the job is destroyed in callEnd() when done()
Definition: AsyncJob.cc:106
virtual void swanSong()
Definition: AsyncJob.h:61
const char * stopReason
reason for forcing done() to be true
Definition: AsyncJob.h:84
Cbc * get() const
a temporary valid raw Cbc pointer or NULL
Definition: CbcPointer.h:159
bool set() const
was set but may be invalid
Definition: CbcPointer.h:40
static const Pointer & Current()
Definition: CodeContext.cc:33
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:83
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:82
Comm::ConnectionPointer conn
Definition: CommCalls.h:80
bool isOpen() const
Definition: Connection.h:101
InstanceId< Connection, uint64_t > id
Definition: Connection.h:183
CachePeer * getPeer() const
Definition: Connection.cc:124
time_t connectTimeout(const time_t fwdStart) const
Definition: Connection.cc:164
ConnectionPointer cloneProfile() const
Create a new closed Connection with the same configuration as this one.
Definition: Connection.cc:64
int xerrno
Definition: errorpage.h:179
Http::StatusCode httpStatus
Definition: errorpage.h:173
static time_t ForwardTimeout(const time_t fwdStart)
time left to finish the whole forwarding process (which started at fwdStart)
Definition: FwdState.cc:428
Final result (an open connection or an error) sent to the job initiator.
bool reused
whether conn was open earlier, by/for somebody else
PeerConnectionPointer conn
CbcPointer< ErrorState > error
problem details (nil on success)
a connection opening attempt in progress (or falsy)
void finish()
reacts to a natural attempt completion (successful or otherwise)
const char *const callbackMethodName
for callbackMethod debugging
void(HappyConnOpener::*)(const CommConnectCbParams &) CallbackMethod
HappyConnOpener method implementing a ConnOpener callback.
JobWait< Comm::ConnOpener > connWait
waits for a connection to the peer to be established/opened
void cancel(const char *reason)
aborts an in-progress attempt
const CallbackMethod callbackMethod
ConnOpener calls this method.
PeerConnectionPointer path
the destination we are connecting to
Attempt(const CallbackMethod method, const char *methodName)
bool doneAll() const override
whether positive goal has been reached
void noteSpareConnectDone(const CommConnectCbParams &)
Comm::ConnOpener callback for the spare connection attempt.
bool ignoreSpareRestrictions
whether spare connection attempts disregard happy_eyeballs_* settings
bool gotSpareAllowance
whether we have received a permission to open a spare while spares are limited
PeerConnectionPointer lastFailedConnection
nil if none has failed
void start() override
called by AsyncStart; do not call directly
void noteGavePrimeItsChance()
reacts to expired happy_eyeballs_connect_timeout
bool retriable_
whether we are opening connections for a request that may be resent
const char * status() const override
internal cleanup; do not call directly
void cancelAttempt(Attempt &, const char *reason)
cancels the in-progress attempt, making its path a future candidate
Comm::ConnectionPointer currentPeer
void maybeGivePrimeItsChance()
void setHost(const char *)
configures the origin server domain name
const char * ranOutOfTimeOrAttemptsEarlier_
Reason to ran out of time or attempts.
void sendSuccess(const PeerConnectionPointer &conn, bool reused, const char *connKind)
send a successful result to the initiator (if it still needs an answer)
HappyConnOpener(const ResolvedPeersPointer &, const AsyncCallback< Answer > &, const HttpRequestPointer &, time_t aFwdStart, int tries, const AccessLogEntryPointer &)
void updateSpareWaitAfterPrimeFailure()
reacts to a prime attempt failure
~HappyConnOpener() override
HttpRequestPointer cause
the request that needs a to-server connection
void stopGivingPrimeItsChance()
called when the prime attempt has used up its chance for a solo victory
AccessLogEntryPointer ale
transaction details
void maybeOpenSpareConnection()
if possible, starts a spare connection attempt
Answer * futureAnswer(const PeerConnectionPointer &)
const time_t fwdStart
requestor start time
void checkForNewConnection()
void handleConnOpenerAnswer(Attempt &, const CommConnectCbParams &, const char *connDescription)
prime/spare-agnostic processing of a Comm::ConnOpener result
bool ranOutOfTimeOrAttempts() const
Check for maximum connection tries and forwarding time restrictions.
void stopWaitingForSpareAllowance()
called when the spare attempt should no longer obey spare connection limits
ErrorState * makeError(const err_type type) const
void noteSpareAllowance()
reacts to satisfying happy_eyeballs_connect_gap and happy_eyeballs_connect_limit
HappySpareWait spareWaiting
preconditions for an attempt to open a spare connection
void sendFailure()
inform the initiator about our failure to connect (if needed)
void swanSong() override
ErrorState * lastError
last problem details (or nil)
bool reuseOldConnection(PeerConnectionPointer &)
void startConnecting(Attempt &, PeerConnectionPointer &)
starts opening (or reusing) a connection to the given destination
bool allowPconn_
whether persistent connections are allowed
const char * host_
origin server domain name (or equivalent)
Attempt spare
current connection opening attempt on the spare track (if any)
ResolvedPeersPointer destinations
Candidate paths. Shared with the initiator. May not be finalized yet.
HappyAbsoluteTime primeStart
the start of the first connection attempt for the currentPeer
void maybeOpenPrimeConnection()
starts a prime connection attempt if possible or does nothing otherwise
void openFreshConnection(Attempt &, PeerConnectionPointer &)
AsyncCallback< Answer > callback_
answer destination
void notePrimeConnectDone(const CommConnectCbParams &)
Comm::ConnOpener callback for the prime connection attempt.
void cancelSpareWait(const char *reason)
stops waiting for the right conditions to open a spare connection
void noteCandidatesChange()
reacts to changes in the destinations list
Attempt prime
current connection opening attempt on the prime track (if any)
bool waiting() const
const char *const name
waiting event name, for debugging
void dequeue(HappyConnOpener &)
stops managing the job's wait; cancels the pending callback, if any
void checkpoint()
resumes jobs that need resuming (if any)
bool startedWaiting(const HappyAbsoluteTime lastStart, const int cfgTimeoutMsec) const
static void NoteWaitOver(void *raw)
virtual AsyncCall::Pointer notify(const CbcPointer< HappyConnOpener > &)=0
virtual bool readyNow(const HappyConnOpener &) const =0
void enqueue(HappyConnOpener &)
starts managing the job's wait; the job should expect a call back
HappySpareWaitList jobs_
queued jobs waiting their turn
HappyOrderEnforcer(const char *aName)
HappyAbsoluteTime waitEnd_
expected NoteWaitOver() call time (or zero)
HappySpareWaitList::iterator position
CodeContext::Pointer codeContext
requestor's context
AsyncCall::Pointer callback
a pending noteGavePrimeItsChance() or noteSpareAllowance() call
RequestFlags flags
Definition: HttpRequest.h:141
void start(const JobPointer &aJob, const AsyncCall::Pointer &aCallback)
starts waiting for the given job to call the given callback
Definition: JobWait.h:69
Comm::ConnectionPointer pop(const Comm::ConnectionPointer &dest, const char *domain, bool keepOpen)
Definition: pconn.cc:449
void finalize(const Comm::ConnectionPointer &conn)
upgrade stored peer selection details with a matching actual connection
enforces happy_eyeballs_connect_timeout
bool readyNow(const HappyConnOpener &job) const override
AsyncCall::Pointer notify(const CbcPointer< HappyConnOpener > &) override
C * getRaw() const
Definition: RefCount.h:89
bool needValidation
Definition: RequestFlags.h:50
PeerConnectionPointer extractFront()
extracts and returns the first queued address
bool haveSpare(const Comm::Connection &currentPeer)
whether extractSpare() would return a non-nil path right now
bool doneWithPrimes(const Comm::Connection &currentPeer)
whether extractPrime() returns and will continue to return nil
bool doneWithPeer(const Comm::Connection &currentPeer)
whether doneWithPrimes() and doneWithSpares() are true for currentPeer
void reinstatePath(const PeerConnectionPointer &)
PeerConnectionPointer extractSpare(const Comm::Connection &currentPeer)
bool notificationPending
whether HappyConnOpener::noteCandidatesChange() is scheduled to fire
Definition: ResolvedPeers.h:85
bool empty() const
whether we lack any known candidate paths
Definition: ResolvedPeers.h:46
bool destinationsFinalized
whether all of the available candidate paths received from DNS
Definition: ResolvedPeers.h:82
PeerConnectionPointer extractPrime(const Comm::Connection &currentPeer)
bool doneWithSpares(const Comm::Connection &currentPeer)
whether extractSpare() returns and will continue to return nil
SBuf buf()
bytes written so far
Definition: Stream.h:41
Definition: SBuf.h:94
const char * c_str()
Definition: SBuf.cc:516
void clear()
Definition: SBuf.cc:175
enforces happy_eyeballs_connect_gap and happy_eyeballs_connect_limit
bool concurrencyLimitReached() const
whether opening a spare connection now would violate happy_eyeballs_connect_limit
bool readyNow(const HappyConnOpener &job) const override
void jobDroppedAllowance()
reacts to HappyConnOpener dropping its spare connection allowance
void recordAllowance()
account for the given allowance
HappyAbsoluteTime lastAllowanceStart
the time of the last noteSpareAllowance() call
AsyncCall::Pointer notify(const CbcPointer< HappyConnOpener > &) override
void jobUsedAllowance()
reacts to HappyConnOpener getting a spare connection opening result
int connect_limit
Definition: SquidConfig.h:543
int forward_max_tries
Definition: SquidConfig.h:351
int connect_timeout
Definition: SquidConfig.h:545
struct SquidConfig::@118 happyEyeballs
A const & min(A const &lhs, A const &rhs)
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
err_type
Definition: forward.h:14
@ ERR_CONNECT_FAIL
Definition: forward.h:30
@ ERR_GATEWAY_FAILURE
Definition: forward.h:67
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition: event.cc:107
@ OK
Definition: Flag.h:16
@ scGatewayTimeout
Definition: StatusCode.h:75
@ scServiceUnavailable
Definition: StatusCode.h:74
#define xstrdup
double current_dtime
the current UNIX time in seconds (with microsecond precision)
Definition: stub_libtime.cc:19
#define safe_free(x)
Definition: xalloc.h:73

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors