HappyConnOpener.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2021 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 {
55 public:
57  HappyOrderEnforcer(const char *aName): name(aName) {}
58 
60  void checkpoint();
61 
63  void enqueue(HappyConnOpener &);
64 
66  void dequeue(HappyConnOpener &);
67 
68  const char * const name;
69 
70 protected:
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 
77 private:
78  static void NoteWaitOver(void *raw);
79  void noteWaitOver();
80 
83 };
84 
85 std::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 {
99 public:
100  PrimeChanceGiver(): HappyOrderEnforcer("happy_eyeballs_connect_timeout enforcement") {}
101 
102  /* HappyOrderEnforcer API */
103  virtual bool readyNow(const HappyConnOpener &job) const override;
104 
105 private:
106  /* HappyOrderEnforcer API */
107  virtual AsyncCall::Pointer notify(const CbcPointer<HappyConnOpener> &) override;
108 };
109 
112 {
113 public:
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 
121  void jobGotInstantAllowance();
122 
124  void jobUsedAllowance();
125 
127  void jobDroppedAllowance();
128 
129 private:
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 
150 void
152 {
153  Must(!job.spareWaiting.callback);
154  jobs_.emplace_back(&job);
155  job.spareWaiting.position = std::prev(jobs_.end());
157 }
158 
159 void
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 
171 void
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 
187 bool
188 HappyOrderEnforcer::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 
215 void
217 {
218  assert(raw);
219  static_cast<HappyOrderEnforcer*>(raw)->noteWaitOver();
220 }
221 
222 void
224 {
225  Must(waiting());
226  waitEnd_ = 0;
227  checkpoint();
228 }
229 
230 /* PrimeChanceGiver */
231 
232 bool
234 {
236 }
237 
240 {
241  return CallJobHere(17, 5, job, HappyConnOpener, noteGavePrimeItsChance);
242 }
243 
244 /* SpareAllowanceGiver */
245 
246 bool
248 {
249  return !concurrencyLimitReached() &&
251 }
252 
255 {
256  recordAllowance();
257  return CallJobHere(17, 5, job, HappyConnOpener, noteSpareAllowance);
258 }
259 
260 void
262 {
263  recordAllowance();
264 }
265 
266 void
268 {
269  forgetAllowance();
270 }
271 
272 void
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.
278  lastAllowanceStart = 0;
279 
280  forgetAllowance();
281 }
282 
284 void
286 {
289  // not a checkpoint(): no other spare can become ready here
290 }
291 
292 void
294 {
297  checkpoint();
298 }
299 
301 bool
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 
329 HappyConnOpener::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 {
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 
461 ErrorState *
463 {
464  const auto statusCode = cause->flags.needValidation ?
466  return new ErrorState(type, statusCode, cause.getRaw(), ale);
467 }
468 
472 {
473  if (callback_ && !callback_->canceled()) {
474  const auto answer = dynamic_cast<Answer *>(callback_->getDialer());
475  assert(answer);
476  answer->conn = conn;
477  answer->n_tries = n_tries;
478  return answer;
479  }
480  return nullptr;
481 }
482 
484 void
485 HappyConnOpener::sendSuccess(const PeerConnectionPointer &conn, const bool reused, const char *connKind)
486 {
487  debugs(17, 4, connKind << ": " << conn);
488  if (auto *answer = futureAnswer(conn)) {
489  answer->reused = reused;
490  assert(!answer->error);
492  }
493  callback_ = nullptr;
494 }
495 
497 void
498 HappyConnOpener::cancelAttempt(Attempt &attempt, const char *reason)
499 {
500  Must(attempt);
501  destinations->reinstatePath(attempt.path); // before attempt.cancel() clears path
502  attempt.cancel(reason);
503 }
504 
506 void
508 {
510  if (auto *answer = futureAnswer(lastFailedConnection)) {
511  if (!lastError)
513  answer->error = lastError;
514  assert(answer->error.valid());
515  lastError = nullptr; // the answer owns it now
517  }
518  callback_ = nullptr;
519 }
520 
521 void
523 {
526 }
527 
529 void
531 {
532  Must(!attempt.path);
533  Must(!attempt.connWait);
534  Must(dest);
535 
536  const auto bumpThroughPeer = cause->flags.sslBumped && dest->getPeer();
537  const auto canReuseOld = allowPconn_ && !bumpThroughPeer;
538  if (!canReuseOld || !reuseOldConnection(dest))
539  openFreshConnection(attempt, dest);
540 }
541 
545 bool
547 {
549 
550  if (const auto pconn = fwdPconnPool->pop(dest, host_, retriable_)) {
551  ++n_tries;
552  dest.finalize(pconn);
553  sendSuccess(dest, true, "reused connection");
554  return true;
555  }
556 
557  return false;
558 }
559 
562 void
564 {
565 #if URL_CHECKSUM_DEBUG
566  entry->mem_obj->checkUrlChecksum();
567 #endif
568 
569  const auto conn = dest->cloneProfile();
571 
572  ++n_tries;
573 
575  AsyncCall::Pointer callConnect = asyncCall(48, 5, attempt.callbackMethodName,
576  Dialer(this, attempt.callbackMethod));
577  const time_t connTimeout = dest->connectTimeout(fwdStart);
578  auto cs = new Comm::ConnOpener(conn, callConnect, connTimeout);
579  if (!conn->getPeer())
580  cs->setHost(host_);
581 
582  attempt.path = dest; // but not the being-opened conn!
583  attempt.connWait.start(cs, callConnect);
584 }
585 
587 void
589 {
590  handleConnOpenerAnswer(prime, params, "new prime connection");
591 }
592 
594 void
596 {
597  if (gotSpareAllowance) {
599  gotSpareAllowance = false;
600  }
601  handleConnOpenerAnswer(spare, params, "new spare connection");
602 }
603 
605 void
606 HappyConnOpener::handleConnOpenerAnswer(Attempt &attempt, const CommConnectCbParams &params, const char *what)
607 {
608  Must(params.conn);
609 
610  // finalize the previously selected path before attempt.finish() forgets it
611  auto handledPath = attempt.path;
612  handledPath.finalize(params.conn); // closed on errors
613  attempt.finish();
614 
615  if (params.flag == Comm::OK) {
616  sendSuccess(handledPath, false, what);
617  return;
618  }
619 
620  debugs(17, 8, what << " failed: " << params.conn);
621  if (const auto peer = params.conn->getPeer())
622  peerConnectFailed(peer);
623 
624  // remember the last failure (we forward it if we cannot connect anywhere)
625  lastFailedConnection = handledPath;
626  delete lastError;
627  lastError = nullptr; // in case makeError() throws
629  lastError->xerrno = params.xerrno;
630 
631  if (spareWaiting)
633 
635 }
636 
638 void
640 {
641  Must(currentPeer);
642  Must(!prime);
644 
646  cancelSpareWait("all primes failed");
648  return; // checkForNewConnection() will open a spare connection ASAP
649  }
650 
653 
654  // may still be spareWaiting.forSpareAllowance or
655  // may still be spareWaiting.forPrimesToFail
656 }
657 
659 void
664 }
665 
667 void
671 
674  TheSpareAllowanceGiver.dequeue(*this); // clears spareWaiting.callback
675 }
676 
678 void
680 {
681  debugs(17, 5, "because " << reason);
683 
688 
690 }
691 
702 void
704 {
705  debugs(17, 7, *destinations);
706 
707  // The order of the top-level if-statements below is important.
708 
709  if (done())
710  return; // bail ASAP to minimize our waste and others delays (state #0)
711 
712  if (ranOutOfTimeOrAttempts()) {
713  Must(currentPeer); // or we would be done() already
714  return; // will continue working (state #1.1)
715  }
716 
717  // update stale currentPeer and/or stale spareWaiting
719  debugs(17, 7, "done with peer; " << *currentPeer);
721  cancelSpareWait("done with peer");
722  else
723  Must(!spareWaiting);
724 
725  currentPeer = nullptr;
726  ignoreSpareRestrictions = false;
729  cancelSpareWait("no spares are coming");
730  spareWaiting.forNewPeer = true;
731  }
732 
733  if (!prime)
735 
736  if (!spare && !done())
738 
739  // any state is possible at this point
740 }
741 
742 void
744 {
748 }
749 
750 void
752 {
755 
756  if (ranOutOfTimeOrAttempts()) {
758  return; // will quit or continue working on prime
759  }
760 
762  gotSpareAllowance = true;
763 
764  auto dest = destinations->extractSpare(*currentPeer); // ought to succeed
765  startConnecting(spare, dest);
766 }
767 
769 void
771 {
772  Must(!prime);
773 
774  if (destinations->empty())
775  return;
776 
777  if (!currentPeer) {
778  auto newPrime = destinations->extractFront();
779  currentPeer = newPrime;
780  Must(currentPeer);
781  debugs(17, 7, "new peer " << *currentPeer);
783  startConnecting(prime, newPrime);
784  if (done()) // probably reused a pconn
785  return;
786 
787  Must(prime);
789  return;
790  }
791 
792  // currentPeer implies there is a spare attempt; meanwhile, the previous
793  // primary attempt has failed; do another attempt on the primary track
794  if (auto dest = destinations->extractPrime(*currentPeer))
795  startConnecting(prime, dest);
796  // else wait for more prime paths or their exhaustion
797 }
798 
801 void
803 {
804  Must(currentPeer);
805  Must(prime);
806  Must(!spare);
807  Must(!spareWaiting);
808 
810  debugs(17, 7, "no spares for " << *currentPeer);
811  spareWaiting.forNewPeer = true;
812  return;
813  }
814 
816  debugs(17, 7, "concurrent spares are prohibited");
818  return;
819  }
820 
821  if (ThePrimeChanceGiver.readyNow(*this)) {
822  debugs(17, 7, "no happy_eyeballs_connect_timeout");
823  return;
824  }
825 
828  // wait for a prime connect result or noteGavePrimeItsChance()
829 }
830 
832 void
834 {
835  Must(currentPeer);
836  Must(!spare);
838 
839  if (spareWaiting)
840  return; // too early
841 
843  return; // too late
844 
845  if (destinations->empty())
846  return;
847 
848  // jobGotInstantAllowance() call conditions below rely on the readyNow() check here
849  if (!ignoreSpareRestrictions && // we have to honor spare restrictions
850  !TheSpareAllowanceGiver.readyNow(*this) && // all new spares must wait
851  destinations->haveSpare(*currentPeer)) { // and we do have a new spare
854  return;
855  }
856 
857  if (auto dest = destinations->extractSpare(*currentPeer)) {
858 
861  gotSpareAllowance = true;
862  }
863 
864  startConnecting(spare, dest);
865  return;
866  }
867 
868  // wait for more spare paths or their exhaustion
869 }
870 
872 bool
874 {
876  return true;
877 
879  debugs(17, 5, "maximum allowed tries exhausted");
880  ranOutOfTimeOrAttemptsEarlier_ = "maximum tries";
881  return true;
882  }
883 
885  debugs(17, 5, "forwarding timeout");
886  ranOutOfTimeOrAttemptsEarlier_ = "forwarding timeout";
887  return true;
888  }
889 
890  return false;
891 }
892 
893 HappyConnOpener::Attempt::Attempt(const CallbackMethod method, const char *methodName):
894  callbackMethod(method),
895  callbackMethodName(methodName)
896 {
897 }
898 
899 void
901 {
902  connWait.finish();
903  path = nullptr;
904 }
905 
906 void
908 {
909  connWait.cancel(reason);
910  path = nullptr;
911 }
912 
bool cancel(const char *reason)
Definition: AsyncCall.cc:56
Cbc * get() const
a temporary valid raw Cbc pointer or NULL
Definition: CbcPointer.h:162
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.
void peerConnectFailed(CachePeer *p)
Definition: neighbors.cc:1299
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:81
void setHost(const char *)
configures the origin server domain name
bool needValidation
Definition: RequestFlags.h:46
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:59
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:449
InstanceId< Connection, uint64_t > id
Definition: Connection.h:181
virtual ~HappyConnOpener() override
bool set() const
was set but may be invalid
Definition: CbcPointer.h:40
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:87
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:79
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
virtual CallDialer * getDialer()=0
virtual void start() override
called by AsyncStart; do not call directly
int n_tries
number of connection opening attempts, including those in the requestor
int type
Definition: errorpage.cc:153
Attempt prime
current connection opening attempt on the prime track (if any)
C * getRaw() const
Definition: RefCount.h:80
@ 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:112
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:88
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:45
HappySpareWaitList::iterator position
Answer * futureAnswer(const PeerConnectionPointer &)
void recordAllowance()
account for the given allowance
@ scGatewayTimeout
Definition: StatusCode.h:75
ResolvedPeersPointer destinations
Candidate paths. Shared with the initiator. May not be finalized yet.
const char *const callbackMethodName
for callbackMethod debugging
virtual bool readyNow(const HappyConnOpener &job) const override
virtual bool readyNow(const HappyConnOpener &) const =0
HappyConnOpener(const ResolvedPeersPointer &, const AsyncCall::Pointer &, HttpRequestPointer &, const time_t aFwdStart, int tries, const AccessLogEntryPointer &al)
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:164
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:64
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
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:562
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)
virtual bool readyNow(const HappyConnOpener &job) const override
CachePeer * getPeer() const
Definition: Connection.cc:124
PrimeChanceGiver ThePrimeChanceGiver
virtual bool doneAll() const override
whether positive goal has been reached
#define CallJobHere(debugSection, debugLevel, job, Class, method)
Definition: AsyncJobCalls.h:58
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)
virtual void swanSong() override
Comm::ConnectionPointer conn
Definition: CommCalls.h:85
#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)
int conn
the current server connection FD
Definition: Transport.cc:26
#define assert(EX)
Definition: assert.h:19
struct SquidConfig::@124 happyEyeballs
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()
Retrieve a copy of the current stream status.
Definition: Stream.h:105
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:87
static time_t ForwardTimeout(const time_t fwdStart)
time left to finish the whole forwarding process (which started at fwdStart)
Definition: FwdState.cc:455
@ scServiceUnavailable
Definition: StatusCode.h:74
void jobUsedAllowance()
reacts to HappyConnOpener getting a spare connection opening result
int connect_limit
Definition: SquidConfig.h:560
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:318
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:91
int xerrno
Definition: errorpage.h:177
bool notificationPending
whether HappyConnOpener::noteCandidatesChange() is scheduled to fire
Definition: ResolvedPeers.h:84
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
virtual 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)
Like assert() but throws an exception instead of aborting the process.
Definition: TextException.h:73
HttpRequestPointer cause
the request that needs a to-server connection
bool reuseOldConnection(PeerConnectionPointer &)
AsyncCall * asyncCall(int aDebugSection, int aDebugLevel, const char *aName, const Dialer &aDialer)
Definition: AsyncCall.h:156
virtual 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
virtual const char * status() const override
internal cleanup; do not call directly
int forward_max_tries
Definition: SquidConfig.h:358
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:99
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
A const & min(A const &lhs, A const &rhs)
static void NoteWaitOver(void *raw)
void finalize(const Comm::ConnectionPointer &conn)
upgrade stored peer selection details with a matching actual connection
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
void(HappyConnOpener::*)(const CommConnectCbParams &) CallbackMethod
HappyConnOpener method implementing a ConnOpener callback.
double current_dtime
Definition: stub_time.cc:16
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition: event.cc:108
PeerConnectionPointer lastFailedConnection
nil if none has failed
HappySpareWait spareWaiting
preconditions for an attempt to open a spare connection
AsyncCall::Pointer callback_
handler to be called on connection completion.
class SquidConfig Config
Definition: SquidConfig.cc:12
void GetMarkingsToServer(HttpRequest *request, Comm::Connection &conn)
Definition: FwdState.cc:1600
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 &)
bool canceled()
Definition: AsyncCall.h:54

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors