HappyConnOpener.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2022 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/CodeContext.h"
12#include "CachePeer.h"
13#include "errorpage.h"
14#include "FwdState.h"
15#include "HappyConnOpener.h"
16#include "HttpRequest.h"
17#include "ip/QosConfig.h"
18#include "neighbors.h"
19#include "pconn.h"
20#include "PeerPoolMgr.h"
21#include "sbuf/Stream.h"
22#include "SquidConfig.h"
23
25
26// HappyOrderEnforcer optimizes enforcement of the "pause before opening a spare
27// connection" requirements. Its inefficient alternative would add hundreds of
28// concurrent events to the Squid event queue in many busy configurations, one
29// concurrent event per concurrent HappyConnOpener job.
30//
31// EventScheduler::schedule() uses linear search to find the right place for a
32// new event; having hundreds of concurrent events is prohibitively expensive.
33// Both alternatives may have comparable high rate of eventAdd() calls, but
34// HappyOrderEnforcer usually schedules the first or second event (as opposed to
35// events that would be fired after hundreds of already scheduled events, making
36// that linear search a lot longer).
37//
38// EventScheduler::cancel() also uses linear search. HappyOrderEnforcer does not
39// need to cancel scheduled events, while its inefficient alternative may cancel
40// events at a rate comparable to the high eventAdd() rate -- many events would
41// be scheduled in vain because external factors would speed up (or make
42// unnecessary) spare connection attempts, canceling the wait.
43//
44// This optimization is possible only where each job needs to pause for the same
45// amount of time, creating a naturally ordered list of jobs waiting to be
46// resumed. This is why two HappyOrderEnforcers are needed to efficiently honor
47// both happy_eyeballs_connect_timeout and happy_eyeballs_connect_gap
48// directives.
49
54{
55public:
57 HappyOrderEnforcer(const char *aName): name(aName) {}
58
60 void checkpoint();
61
64
67
68 const char * const name;
69
70protected:
71 virtual bool readyNow(const HappyConnOpener &) const = 0;
73
74 bool waiting() const { return waitEnd_ > 0; }
75 bool startedWaiting(const HappyAbsoluteTime lastStart, const int cfgTimeoutMsec) const;
76
77private:
78 static void NoteWaitOver(void *raw);
79 void noteWaitOver();
80
83};
84
85std::ostream &operator <<(std::ostream &os, const HappyConnOpenerAnswer &answer)
86{
87 if (answer.error.set())
88 os << "bad ";
89 if (answer.conn)
90 os << (answer.reused ? "reused " : "new ") << answer.conn;
91 if (answer.n_tries != 1)
92 os << " after " << answer.n_tries;
93 return os;
94}
95
98{
99public:
100 PrimeChanceGiver(): HappyOrderEnforcer("happy_eyeballs_connect_timeout enforcement") {}
101
102 /* HappyOrderEnforcer API */
103 virtual bool readyNow(const HappyConnOpener &job) const override;
104
105private:
106 /* HappyOrderEnforcer API */
107 virtual AsyncCall::Pointer notify(const CbcPointer<HappyConnOpener> &) override;
108};
109
112{
113public:
114 SpareAllowanceGiver(): HappyOrderEnforcer("happy_eyeballs_connect_gap/happy_eyeballs_connect_limit enforcement") {}
115
116 /* HappyOrderEnforcer API */
117 virtual bool readyNow(const HappyConnOpener &job) const override;
118
122
124 void jobUsedAllowance();
125
127 void jobDroppedAllowance();
128
129private:
130 /* HappyOrderEnforcer API */
131 virtual AsyncCall::Pointer notify(const CbcPointer<HappyConnOpener> &) override;
132
133 bool concurrencyLimitReached() const;
134 void recordAllowance();
135 void forgetAllowance();
136
139
143};
144
147
148/* HappyOrderEnforcer */
149
150void
152{
154 jobs_.emplace_back(&job);
155 job.spareWaiting.position = std::prev(jobs_.end());
157}
158
159void
161{
162 if (job.spareWaiting.callback) {
163 job.spareWaiting.callback->cancel("HappyOrderEnforcer::dequeue");
164 job.spareWaiting.callback = nullptr;
165 } else {
166 Must(!jobs_.empty());
167 jobs_.erase(job.spareWaiting.position);
168 }
169}
170
171void
173{
174 while (!jobs_.empty()) {
175 if (const auto jobPtr = jobs_.front().valid()) {
176 auto &job = *jobPtr;
177 if (!readyNow(job))
178 break; // the next job cannot be ready earlier (FIFO)
179 CallBack(job.spareWaiting.codeContext, [&] {
180 job.spareWaiting.callback = notify(jobPtr); // and fall through to the next job
181 });
182 }
183 jobs_.pop_front();
184 }
185}
186
187bool
188HappyOrderEnforcer::startedWaiting(const HappyAbsoluteTime lastStart, const int cfgTimeoutMsec) const
189{
190 // Normally, the job would not even be queued if there is no timeout. This
191 // check handles reconfiguration that happened after this job was queued.
192 if (cfgTimeoutMsec <= 0)
193 return false;
194
195 // convert to seconds and adjust for SMP workers to keep aggregated load in
196 // check despite the lack of coordination among workers
197 const auto tout = static_cast<HappyAbsoluteTime>(cfgTimeoutMsec) * Config.workers / 1000.0;
198 const auto newWaitEnd = std::min(lastStart, current_dtime) + tout;
199 if (newWaitEnd <= current_dtime)
200 return false; // no need to wait
201
202 // We cannot avoid event accumulation because calling eventDelete() is
203 // unsafe, but any accumulation will be small because it can only be caused
204 // by hot reconfiguration changes or current time jumps.
205 if (!waiting() || newWaitEnd < waitEnd_) {
206 const auto waitTime = newWaitEnd - current_dtime;
207 eventAdd(name, &HappyOrderEnforcer::NoteWaitOver, const_cast<HappyOrderEnforcer*>(this), waitTime, 0, false);
208 waitEnd_ = newWaitEnd;
209 assert(waiting());
210 }
211
212 return true;
213}
214
215void
217{
218 assert(raw);
219 static_cast<HappyOrderEnforcer*>(raw)->noteWaitOver();
220}
221
222void
224{
225 Must(waiting());
226 waitEnd_ = 0;
227 checkpoint();
228}
229
230/* PrimeChanceGiver */
231
232bool
234{
236}
237
240{
241 return CallJobHere(17, 5, job, HappyConnOpener, noteGavePrimeItsChance);
242}
243
244/* SpareAllowanceGiver */
245
246bool
248{
249 return !concurrencyLimitReached() &&
251}
252
255{
257 return CallJobHere(17, 5, job, HappyConnOpener, noteSpareAllowance);
258}
259
260void
262{
264}
265
266void
268{
270}
271
272void
274{
275 // Without happy_eyeballs_connect_gap, lastAllowanceStart does not matter.
276 // Otherwise, the dropped allowance ought to be the last one, and since it
277 // was allowed, we would still observe the gap even if we do not wait now.
279
281}
282
284void
286{
289 // not a checkpoint(): no other spare can become ready here
290}
291
292void
294{
297 checkpoint();
298}
299
301bool
303{
305 return false; // no limit
306
308 return true; // concurrent spares prohibited regardless of spare level
309
310 // adjust for SMP workers to keep aggregated spare level in check despite
311 // the lack of coordination among workers
312 const auto aggregateLevel = concurrencyLevel * Config.workers;
313 return aggregateLevel >= Config.happyEyeballs.connect_limit;
314}
315
316/* HappyConnOpenerAnswer */
317
319{
320 // XXX: When multiple copies of an answer exist, this delete in one copy
321 // invalidates error in other copies -- their error.get() returns nil. The
322 // current code "works", but probably only because the initiator gets the
323 // error before any answer copies are deleted. Same in ~EncryptorAnswer.
324 delete error.get();
325}
326
327/* HappyConnOpener */
328
329HappyConnOpener::HappyConnOpener(const ResolvedPeers::Pointer &dests, const AsyncCall::Pointer &aCall, HttpRequest::Pointer &request, const time_t aFwdStart, int tries, const AccessLogEntry::Pointer &anAle):
330 AsyncJob("HappyConnOpener"),
331 fwdStart(aFwdStart),
332 callback_(aCall),
333 destinations(dests),
334 prime(&HappyConnOpener::notePrimeConnectDone, "HappyConnOpener::notePrimeConnectDone"),
335 spare(&HappyConnOpener::noteSpareConnectDone, "HappyConnOpener::noteSpareConnectDone"),
336 ale(anAle),
337 cause(request),
338 n_tries(tries)
339{
341 assert(dynamic_cast<Answer*>(callback_->getDialer()));
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 const auto answer = dynamic_cast<Answer *>(callback_->getDialer());
489 assert(answer);
490 answer->conn = conn;
491 answer->n_tries = n_tries;
492 return answer;
493 }
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);
506 }
507 callback_ = nullptr;
508}
509
511void
512HappyConnOpener::cancelAttempt(Attempt &attempt, const char *reason)
513{
514 Must(attempt);
515 destinations->reinstatePath(attempt.path); // before attempt.cancel() clears path
516 attempt.cancel(reason);
517}
518
520void
522{
524 if (auto *answer = futureAnswer(lastFailedConnection)) {
525 if (!lastError)
527 answer->error = lastError;
528 assert(answer->error.valid());
529 lastError = nullptr; // the answer owns it now
531 }
532 callback_ = nullptr;
533}
534
535void
537{
540}
541
543void
545{
546 Must(!attempt.path);
547 Must(!attempt.connWait);
548 Must(dest);
549
550 const auto bumpThroughPeer = cause->flags.sslBumped && dest->getPeer();
551 const auto canReuseOld = allowPconn_ && !bumpThroughPeer;
552 if (!canReuseOld || !reuseOldConnection(dest))
553 openFreshConnection(attempt, dest);
554}
555
559bool
561{
563
564 if (const auto pconn = fwdPconnPool->pop(dest, host_, retriable_)) {
565 ++n_tries;
566 dest.finalize(pconn);
567 sendSuccess(dest, true, "reused connection");
568 return true;
569 }
570
571 return false;
572}
573
576void
578{
579#if URL_CHECKSUM_DEBUG
580 entry->mem_obj->checkUrlChecksum();
581#endif
582
583 const auto conn = dest->cloneProfile();
585
587 AsyncCall::Pointer callConnect = asyncCall(48, 5, attempt.callbackMethodName,
588 Dialer(this, attempt.callbackMethod));
589 const time_t connTimeout = dest->connectTimeout(fwdStart);
590 auto cs = new Comm::ConnOpener(conn, callConnect, connTimeout);
591 if (!conn->getPeer())
592 cs->setHost(host_);
593
594 attempt.path = dest; // but not the being-opened conn!
595 attempt.connWait.start(cs, callConnect);
596}
597
599void
601{
602 handleConnOpenerAnswer(prime, params, "new prime connection");
603}
604
606void
608{
609 if (gotSpareAllowance) {
611 gotSpareAllowance = false;
612 }
613 handleConnOpenerAnswer(spare, params, "new spare connection");
614}
615
617void
619{
620 Must(params.conn);
621
622 // finalize the previously selected path before attempt.finish() forgets it
623 auto handledPath = attempt.path;
624 handledPath.finalize(params.conn); // closed on errors
625 attempt.finish();
626
627 ++n_tries;
628
629 if (params.flag == Comm::OK) {
630 sendSuccess(handledPath, false, what);
631 return;
632 }
633
634 debugs(17, 8, what << " failed: " << params.conn);
635 if (const auto peer = params.conn->getPeer())
636 peerConnectFailed(peer);
637
638 // remember the last failure (we forward it if we cannot connect anywhere)
639 lastFailedConnection = handledPath;
640 delete lastError;
641 lastError = nullptr; // in case makeError() throws
643 lastError->xerrno = params.xerrno;
644
645 if (spareWaiting)
647
649}
650
652void
654{
656 Must(!prime);
658
660 cancelSpareWait("all primes failed");
662 return; // checkForNewConnection() will open a spare connection ASAP
663 }
664
667
668 // may still be spareWaiting.forSpareAllowance or
669 // may still be spareWaiting.forPrimesToFail
670}
671
673void
678}
679
681void
685
688 TheSpareAllowanceGiver.dequeue(*this); // clears spareWaiting.callback
689}
690
692void
694{
695 debugs(17, 5, "because " << reason);
697
702
704}
705
716void
718{
719 debugs(17, 7, *destinations);
720
721 // The order of the top-level if-statements below is important.
722
723 if (done())
724 return; // bail ASAP to minimize our waste and others delays (state #0)
725
727 Must(currentPeer); // or we would be done() already
728 return; // will continue working (state #1.1)
729 }
730
731 // update stale currentPeer and/or stale spareWaiting
733 debugs(17, 7, "done with peer; " << *currentPeer);
735 cancelSpareWait("done with peer");
736 else
738
739 currentPeer = nullptr;
743 cancelSpareWait("no spares are coming");
745 }
746
747 if (!prime)
749
750 if (!spare && !done())
752
753 // any state is possible at this point
754}
755
756void
758{
762}
763
764void
766{
769
772 return; // will quit or continue working on prime
773 }
774
776 gotSpareAllowance = true;
777
778 auto dest = destinations->extractSpare(*currentPeer); // ought to succeed
779 startConnecting(spare, dest);
780}
781
783void
785{
786 Must(!prime);
787
788 if (destinations->empty())
789 return;
790
791 if (!currentPeer) {
792 auto newPrime = destinations->extractFront();
793 currentPeer = newPrime;
795 debugs(17, 7, "new peer " << *currentPeer);
797 startConnecting(prime, newPrime);
798 if (done()) // probably reused a pconn
799 return;
800
801 Must(prime);
803 return;
804 }
805
806 // currentPeer implies there is a spare attempt; meanwhile, the previous
807 // primary attempt has failed; do another attempt on the primary track
808 if (auto dest = destinations->extractPrime(*currentPeer))
809 startConnecting(prime, dest);
810 // else wait for more prime paths or their exhaustion
811}
812
815void
817{
819 Must(prime);
820 Must(!spare);
822
824 debugs(17, 7, "no spares for " << *currentPeer);
826 return;
827 }
828
830 debugs(17, 7, "concurrent spares are prohibited");
832 return;
833 }
834
835 if (ThePrimeChanceGiver.readyNow(*this)) {
836 debugs(17, 7, "no happy_eyeballs_connect_timeout");
837 return;
838 }
839
842 // wait for a prime connect result or noteGavePrimeItsChance()
843}
844
846void
848{
850 Must(!spare);
852
853 if (spareWaiting)
854 return; // too early
855
857 return; // too late
858
859 if (destinations->empty())
860 return;
861
862 // jobGotInstantAllowance() call conditions below rely on the readyNow() check here
863 if (!ignoreSpareRestrictions && // we have to honor spare restrictions
864 !TheSpareAllowanceGiver.readyNow(*this) && // all new spares must wait
865 destinations->haveSpare(*currentPeer)) { // and we do have a new spare
868 return;
869 }
870
871 if (auto dest = destinations->extractSpare(*currentPeer)) {
872
875 gotSpareAllowance = true;
876 }
877
878 startConnecting(spare, dest);
879 return;
880 }
881
882 // wait for more spare paths or their exhaustion
883}
884
886bool
888{
890 return true;
891
893 debugs(17, 5, "maximum allowed tries exhausted");
894 ranOutOfTimeOrAttemptsEarlier_ = "maximum tries";
895 return true;
896 }
897
899 debugs(17, 5, "forwarding timeout");
900 ranOutOfTimeOrAttemptsEarlier_ = "forwarding timeout";
901 return true;
902 }
903
904 return false;
905}
906
907HappyConnOpener::Attempt::Attempt(const CallbackMethod method, const char *methodName):
908 callbackMethod(method),
909 callbackMethodName(methodName)
910{
911}
912
913void
915{
916 connWait.finish();
917 path = nullptr;
918}
919
920void
922{
923 connWait.cancel(reason);
924 path = nullptr;
925}
926
#define ScheduleCallHere(call)
Definition: AsyncCall.h:164
AsyncCall * asyncCall(int aDebugSection, int aDebugLevel, const char *aName, const Dialer &aDialer)
Definition: AsyncCall.h:154
#define CallJobHere(debugSection, debugLevel, job, Class, method)
Definition: AsyncJobCalls.h:58
void CallBack(const CodeContext::Pointer &callbackContext, Fun &&callback)
Definition: CodeContext.h:112
PconnPool * fwdPconnPool
a collection of previously used persistent Squid-to-peer HTTP(S) connections
Definition: FwdState.cc:76
void GetMarkingsToServer(HttpRequest *request, Comm::Connection &conn)
Definition: FwdState.cc:1598
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:71
int conn
the current server connection FD
Definition: Transport.cc:26
#define assert(EX)
Definition: assert.h:19
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:318
bool canceled()
Definition: AsyncCall.h:52
bool cancel(const char *reason)
Definition: AsyncCall.cc:56
virtual CallDialer * getDialer()=0
bool done() const
the job is destroyed in callEnd() when done()
Definition: AsyncJob.cc:91
virtual void swanSong()
Definition: AsyncJob.h:59
const char * stopReason
reason for forcing done() to be true
Definition: AsyncJob.h:79
Cbc * get() const
a temporary valid raw Cbc pointer or NULL
Definition: CbcPointer.h:162
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:88
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:87
Comm::ConnectionPointer conn
Definition: CommCalls.h:85
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:177
static time_t ForwardTimeout(const time_t fwdStart)
time left to finish the whole forwarding process (which started at fwdStart)
Definition: FwdState.cc:453
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)
virtual 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
virtual 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
virtual 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)
void updateSpareWaitAfterPrimeFailure()
reacts to a prime attempt failure
virtual ~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
HappyConnOpener(const ResolvedPeersPointer &, const AsyncCall::Pointer &, HttpRequestPointer &, const time_t aFwdStart, int tries, const AccessLogEntryPointer &al)
void sendFailure()
inform the initiator about our failure to connect (if needed)
virtual 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.
AsyncCall::Pointer callback_
handler to be called on connection completion.
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 &)
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
virtual bool readyNow(const HappyConnOpener &job) const override
virtual AsyncCall::Pointer notify(const CbcPointer< HappyConnOpener > &) override
C * getRaw() const
Definition: RefCount.h:80
bool needValidation
Definition: RequestFlags.h:46
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:84
bool empty() const
whether we lack any known candidate paths
Definition: ResolvedPeers.h:45
bool destinationsFinalized
whether all of the available candidate paths received from DNS
Definition: ResolvedPeers.h:81
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
virtual 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
virtual AsyncCall::Pointer notify(const CbcPointer< HappyConnOpener > &) override
void jobUsedAllowance()
reacts to HappyConnOpener getting a spare connection opening result
int connect_limit
Definition: SquidConfig.h:551
int forward_max_tries
Definition: SquidConfig.h:349
int connect_timeout
Definition: SquidConfig.h:553
struct SquidConfig::@124 happyEyeballs
A const & min(A const &lhs, A const &rhs)
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
err_type
Definition: forward.h:14
@ ERR_CONNECT_FAIL
Definition: forward.h:30
@ ERR_GATEWAY_FAILURE
Definition: forward.h:67
int type
Definition: errorpage.cc:152
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
void peerConnectFailed(CachePeer *p)
Definition: neighbors.cc:1298
double current_dtime
the current UNIX time in seconds (with microsecond precision)
Definition: stub_libtime.cc:19
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
#define safe_free(x)
Definition: xalloc.h:73

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors