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 {
56 public:
58  HappyOrderEnforcer(const char *aName): name(aName) {}
59 
61  void checkpoint();
62 
64  void enqueue(HappyConnOpener &);
65 
67  void dequeue(HappyConnOpener &);
68 
69  const char * const name;
70 
71 protected:
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 
78 private:
79  static void NoteWaitOver(void *raw);
80  void noteWaitOver();
81 
84 };
85 
86 std::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 {
100 public:
101  PrimeChanceGiver(): HappyOrderEnforcer("happy_eyeballs_connect_timeout enforcement") {}
102 
103  /* HappyOrderEnforcer API */
104  bool readyNow(const HappyConnOpener &job) const override;
105 
106 private:
107  /* HappyOrderEnforcer API */
109 };
110 
113 {
114 public:
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 
122  void jobGotInstantAllowance();
123 
125  void jobUsedAllowance();
126 
128  void jobDroppedAllowance();
129 
130 private:
131  /* HappyOrderEnforcer API */
133 
134  bool concurrencyLimitReached() const;
135  void recordAllowance();
136  void forgetAllowance();
137 
140 
144 };
145 
148 
149 /* HappyOrderEnforcer */
150 
151 void
153 {
154  Must(!job.spareWaiting.callback);
155  jobs_.emplace_back(&job);
156  job.spareWaiting.position = std::prev(jobs_.end());
158 }
159 
160 void
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 
172 void
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 
188 bool
189 HappyOrderEnforcer::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 
216 void
218 {
219  assert(raw);
220  static_cast<HappyOrderEnforcer*>(raw)->noteWaitOver();
221 }
222 
223 void
225 {
226  Must(waiting());
227  waitEnd_ = 0;
228  checkpoint();
229 }
230 
231 /* PrimeChanceGiver */
232 
233 bool
235 {
237 }
238 
241 {
242  return CallJobHere(17, 5, job, HappyConnOpener, noteGavePrimeItsChance);
243 }
244 
245 /* SpareAllowanceGiver */
246 
247 bool
249 {
250  return !concurrencyLimitReached() &&
252 }
253 
256 {
257  recordAllowance();
258  return CallJobHere(17, 5, job, HappyConnOpener, noteSpareAllowance);
259 }
260 
261 void
263 {
264  recordAllowance();
265 }
266 
267 void
269 {
270  forgetAllowance();
271 }
272 
273 void
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.
279  lastAllowanceStart = 0;
280 
281  forgetAllowance();
282 }
283 
285 void
287 {
290  // not a checkpoint(): no other spare can become ready here
291 }
292 
293 void
295 {
298  checkpoint();
299 }
300 
302 bool
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 
330 HappyConnOpener::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 {
346  safe_free(host_);
347  delete lastError;
348 }
349 
350 void
352 {
353  safe_free(host_);
354  if (h)
355  host_ = xstrdup(h);
356 }
357 
358 void
360 {
363 }
364 
365 bool
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 
388 void
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 
417 std::ostream &
418 operator <<(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 
431 const 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 
475 ErrorState *
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 
498 void
499 HappyConnOpener::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 
510 void
511 HappyConnOpener::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 
519 void
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 
533 void
535 {
538 }
539 
541 void
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 
557 bool
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 
574 void
576 {
577 #if URL_CHECKSUM_DEBUG
578  entry->mem_obj->checkUrlChecksum();
579 #endif
580 
581  const auto conn = dest->cloneProfile();
582  GetMarkingsToServer(cause.getRaw(), *conn);
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 
597 void
599 {
600  handleConnOpenerAnswer(prime, params, "new prime connection");
601 }
602 
604 void
606 {
607  if (gotSpareAllowance) {
609  gotSpareAllowance = false;
610  }
611  handleConnOpenerAnswer(spare, params, "new spare connection");
612 }
613 
615 void
616 HappyConnOpener::handleConnOpenerAnswer(Attempt &attempt, const CommConnectCbParams &params, const char *what)
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 
650 void
652 {
653  Must(currentPeer);
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 
671 void
676 }
677 
679 void
683 
686  TheSpareAllowanceGiver.dequeue(*this); // clears spareWaiting.callback
687 }
688 
690 void
692 {
693  debugs(17, 5, "because " << reason);
695 
700 
702 }
703 
714 void
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 
724  if (ranOutOfTimeOrAttempts()) {
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
735  Must(!spareWaiting);
736 
737  currentPeer = nullptr;
738  ignoreSpareRestrictions = false;
741  cancelSpareWait("no spares are coming");
742  spareWaiting.forNewPeer = true;
743  }
744 
745  if (!prime)
747 
748  if (!spare && !done())
750 
751  // any state is possible at this point
752 }
753 
754 void
756 {
760 }
761 
762 void
764 {
767 
768  if (ranOutOfTimeOrAttempts()) {
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 
781 void
783 {
784  Must(!prime);
785 
786  if (destinations->empty())
787  return;
788 
789  if (!currentPeer) {
790  auto newPrime = destinations->extractFront();
791  currentPeer = newPrime;
792  Must(currentPeer);
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 
813 void
815 {
816  Must(currentPeer);
817  Must(prime);
818  Must(!spare);
819  Must(!spareWaiting);
820 
822  debugs(17, 7, "no spares for " << *currentPeer);
823  spareWaiting.forNewPeer = true;
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 
844 void
846 {
847  Must(currentPeer);
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 
884 bool
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 
905 HappyConnOpener::Attempt::Attempt(const CallbackMethod method, const char *methodName):
906  callbackMethod(method),
907  callbackMethodName(methodName)
908 {
909 }
910 
911 void
913 {
914  connWait.finish();
915  path = nullptr;
916 }
917 
918 void
920 {
921  connWait.cancel(reason);
922  path = nullptr;
923 }
924 
bool cancel(const char *reason)
Definition: AsyncCall.cc:56
double current_dtime
the current UNIX time in seconds (with microsecond precision)
Definition: stub_libtime.cc:19
Cbc * get() const
a temporary valid raw Cbc pointer or NULL
Definition: CbcPointer.h:159
PeerConnectionPointer extractFront()
extracts and returns the first queued address
void notePrimeConnectDone(const CommConnectCbParams &)
Comm::ConnOpener callback for the prime connection attempt.
a connection opening attempt in progress (or falsy)
const char * ranOutOfTimeOrAttemptsEarlier_
Reason to ran out of time or attempts.
bool doneWithPrimes(const Comm::Connection &currentPeer)
whether extractPrime() returns and will continue to return nil
@ ERR_GATEWAY_FAILURE
Definition: forward.h:67
Comm::ConnectionPointer currentPeer
bool destinationsFinalized
whether all of the available candidate paths received from DNS
Definition: ResolvedPeers.h:82
void setHost(const char *)
configures the origin server domain name
bool needValidation
Definition: RequestFlags.h:50
CbcPointer< ErrorState > error
problem details (nil on success)
ErrorState * lastError
last problem details (or nil)
RequestFlags flags
Definition: HttpRequest.h:141
virtual void swanSong()
Definition: AsyncJob.h:61
PeerConnectionPointer path
the destination we are connecting to
void stopGivingPrimeItsChance()
called when the prime attempt has used up its chance for a solo victory
Comm::ConnectionPointer pop(const Comm::ConnectionPointer &dest, const char *domain, bool keepOpen)
Definition: pconn.cc:458
InstanceId< Connection, uint64_t > id
Definition: Connection.h:181
~HappyConnOpener() override
AsyncCall::Pointer callback
a pending noteGavePrimeItsChance() or noteSpareAllowance() call
#define ScheduleCallHere(call)
Definition: AsyncCall.h:166
void checkForNewConnection()
void jobDroppedAllowance()
reacts to HappyConnOpener dropping its spare connection allowance
ErrorState * makeError(const err_type type) const
Definition: SBuf.h:93
a smart AsyncCall pointer for delivery of future results
void dequeue(HappyConnOpener &)
stops managing the job's wait; cancels the pending callback, if any
#define xstrdup
const char * stopReason
reason for forcing done() to be true
Definition: AsyncJob.h:84
PconnPool * fwdPconnPool
a collection of previously used persistent Squid-to-peer HTTP(S) connections
Definition: FwdState.cc:78
void cancelAttempt(Attempt &, const char *reason)
cancels the in-progress attempt, making its path a future candidate
void start() override
called by AsyncStart; do not call directly
Attempt prime
current connection opening attempt on the prime track (if any)
C * getRaw() const
Definition: RefCount.h:89
@ OK
Definition: Flag.h:16
HappyAbsoluteTime primeStart
the start of the first connection attempt for the currentPeer
void maybeGivePrimeItsChance()
SpareAllowanceGiver TheSpareAllowanceGiver
err_type
Definition: forward.h:14
void maybeOpenSpareConnection()
if possible, starts a spare connection attempt
void CallBack(const CodeContext::Pointer &callbackContext, Fun &&callback)
Definition: CodeContext.h:126
void maybeOpenPrimeConnection()
starts a prime connection attempt if possible or does nothing otherwise
bool concurrencyLimitReached() const
whether opening a spare connection now would violate happy_eyeballs_connect_limit
void enqueue(HappyConnOpener &)
starts managing the job's wait; the job should expect a call back
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:83
RefCount< AsyncCallT< Dialer > > asyncCall(int aDebugSection, int aDebugLevel, const char *aName, const Dialer &aDialer)
Definition: AsyncCall.h:156
void clear()
Definition: SBuf.cc:175
void updateSpareWaitAfterPrimeFailure()
reacts to a prime attempt failure
bool doneWithSpares(const Comm::Connection &currentPeer)
whether extractSpare() returns and will continue to return nil
Attempt spare
current connection opening attempt on the spare track (if any)
bool empty() const
whether we lack any known candidate paths
Definition: ResolvedPeers.h:46
HappySpareWaitList::iterator position
Answer * futureAnswer(const PeerConnectionPointer &)
void recordAllowance()
account for the given allowance
@ scGatewayTimeout
Definition: StatusCode.h:77
ResolvedPeersPointer destinations
Candidate paths. Shared with the initiator. May not be finalized yet.
const char *const callbackMethodName
for callbackMethod debugging
bool readyNow(const HappyConnOpener &job) const override
virtual bool readyNow(const HappyConnOpener &) const =0
bool haveSpare(const Comm::Connection &currentPeer)
whether extractSpare() would return a non-nil path right now
PeerConnectionPointer extractPrime(const Comm::Connection &currentPeer)
bool allowPconn_
whether persistent connections are allowed
time_t connectTimeout(const time_t fwdStart) const
Definition: Connection.cc:161
double HappyAbsoluteTime
absolute time in fractional seconds; compatible with current_timed
ConnectionPointer cloneProfile() const
Create a new closed Connection with the same configuration as this one.
Definition: Connection.cc:62
void noteGavePrimeItsChance()
reacts to expired happy_eyeballs_connect_timeout
void startConnecting(Attempt &, PeerConnectionPointer &)
starts opening (or reusing) a connection to the given destination
int connect_timeout
Definition: SquidConfig.h:548
std::list< CbcPointer< HappyConnOpener > > HappySpareWaitList
A FIFO queue of HappyConnOpener jobs waiting to open a spare connection.
const char *const name
waiting event name, for debugging
void checkpoint()
resumes jobs that need resuming (if any)
bool readyNow(const HappyConnOpener &job) const override
CachePeer * getPeer() const
Definition: Connection.cc:121
PrimeChanceGiver ThePrimeChanceGiver
bool doneAll() const override
whether positive goal has been reached
void NoteOutgoingConnectionFailure(CachePeer *const peer)
Definition: CachePeer.h:246
#define CallJobHere(debugSection, debugLevel, job, Class, method)
Definition: AsyncJobCalls.h:59
AccessLogEntryPointer ale
transaction details
void sendSuccess(const PeerConnectionPointer &conn, bool reused, const char *connKind)
send a successful result to the initiator (if it still needs an answer)
void swanSong() override
Comm::ConnectionPointer conn
Definition: CommCalls.h:80
#define safe_free(x)
Definition: xalloc.h:73
bool ranOutOfTimeOrAttempts() const
Check for maximum connection tries and forwarding time restrictions.
void finish()
reacts to a natural attempt completion (successful or otherwise)
#define assert(EX)
Definition: assert.h:17
const CallbackMethod callbackMethod
ConnOpener calls this method.
bool waiting() const
bool startedWaiting(const HappyAbsoluteTime lastStart, const int cfgTimeoutMsec) const
HappyOrderEnforcer(const char *aName)
SBuf buf()
bytes written so far
Definition: Stream.h:41
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:82
static time_t ForwardTimeout(const time_t fwdStart)
time left to finish the whole forwarding process (which started at fwdStart)
Definition: FwdState.cc:423
@ scServiceUnavailable
Definition: StatusCode.h:76
void jobUsedAllowance()
reacts to HappyConnOpener getting a spare connection opening result
int connect_limit
Definition: SquidConfig.h:546
HappyAbsoluteTime lastAllowanceStart
the time of the last noteSpareAllowance() call
JobWait< Comm::ConnOpener > connWait
waits for a connection to the peer to be established/opened
const char * c_str()
Definition: SBuf.cc:516
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:325
enforces happy_eyeballs_connect_timeout
PeerConnectionPointer extractSpare(const Comm::Connection &currentPeer)
bool done() const
the job is destroyed in callEnd() when done()
Definition: AsyncJob.cc:106
int xerrno
Definition: errorpage.h:179
bool notificationPending
whether HappyConnOpener::noteCandidatesChange() is scheduled to fire
Definition: ResolvedPeers.h:85
Final result (an open connection or an error) sent to the job initiator.
HappyAbsoluteTime waitEnd_
expected NoteWaitOver() call time (or zero)
bool doneWithPeer(const Comm::Connection &currentPeer)
whether doneWithPrimes() and doneWithSpares() are true for currentPeer
void start(const JobPointer &aJob, const AsyncCall::Pointer &aCallback)
starts waiting for the given job to call the given callback
Definition: JobWait.h:69
virtual AsyncCall::Pointer notify(const CbcPointer< HappyConnOpener > &)=0
PeerConnectionPointer conn
void reinstatePath(const PeerConnectionPointer &)
static const Pointer & Current()
Definition: CodeContext.cc:33
AsyncCall::Pointer notify(const CbcPointer< HappyConnOpener > &) override
bool gotSpareAllowance
whether we have received a permission to open a spare while spares are limited
const char * host_
origin server domain name (or equivalent)
std::ostream & operator<<(std::ostream &os, const HappyConnOpenerAnswer &answer)
reports Answer details (for AsyncCall parameter debugging)
@ ERR_CONNECT_FAIL
Definition: forward.h:30
bool retriable_
whether we are opening connections for a request that may be resent
#define Must(condition)
Definition: TextException.h:75
HttpRequestPointer cause
the request that needs a to-server connection
bool reuseOldConnection(PeerConnectionPointer &)
AsyncCall::Pointer notify(const CbcPointer< HappyConnOpener > &) override
void noteSpareConnectDone(const CommConnectCbParams &)
Comm::ConnOpener callback for the spare connection attempt.
bool reused
whether conn was open earlier, by/for somebody else
Attempt(const CallbackMethod method, const char *methodName)
bool ignoreSpareRestrictions
whether spare connection attempts disregard happy_eyeballs_* settings
const char * status() const override
internal cleanup; do not call directly
int forward_max_tries
Definition: SquidConfig.h:351
AsyncCallback< Answer > callback_
answer destination
void noteSpareAllowance()
reacts to satisfying happy_eyeballs_connect_gap and happy_eyeballs_connect_limit
void noteCandidatesChange()
reacts to changes in the destinations list
bool isOpen() const
Definition: Connection.h:101
HappySpareWaitList jobs_
queued jobs waiting their turn
void stopWaitingForSpareAllowance()
called when the spare attempt should no longer obey spare connection limits
void cancelSpareWait(const char *reason)
stops waiting for the right conditions to open a spare connection
static void NoteWaitOver(void *raw)
HappyConnOpener(const ResolvedPeersPointer &, const AsyncCallback< Answer > &, const HttpRequestPointer &, time_t aFwdStart, int tries, const AccessLogEntryPointer &)
void finalize(const Comm::ConnectionPointer &conn)
upgrade stored peer selection details with a matching actual connection
struct SquidConfig::@108 happyEyeballs
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
const A & min(A const &lhs, A const &rhs)
void(HappyConnOpener::*)(const CommConnectCbParams &) CallbackMethod
HappyConnOpener method implementing a ConnOpener callback.
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition: event.cc:107
PeerConnectionPointer lastFailedConnection
nil if none has failed
HappySpareWait spareWaiting
preconditions for an attempt to open a spare connection
class SquidConfig Config
Definition: SquidConfig.cc:12
void GetMarkingsToServer(HttpRequest *request, Comm::Connection &conn)
Definition: FwdState.cc:1560
CodeContext::Pointer codeContext
requestor's context
enforces happy_eyeballs_connect_gap and happy_eyeballs_connect_limit
void handleConnOpenerAnswer(Attempt &, const CommConnectCbParams &, const char *connDescription)
prime/spare-agnostic processing of a Comm::ConnOpener result
const time_t fwdStart
requestor start time
void cancel(const char *reason)
aborts an in-progress attempt
void sendFailure()
inform the initiator about our failure to connect (if needed)
void openFreshConnection(Attempt &, PeerConnectionPointer &)

 

Introduction

Documentation

Support

Miscellaneous