HappyConnOpener.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2020 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 "SquidConfig.h"
22 
24 
25 // HappyOrderEnforcer optimizes enforcement of the "pause before opening a spare
26 // connection" requirements. Its inefficient alternative would add hundreds of
27 // concurrent events to the Squid event queue in many busy configurations, one
28 // concurrent event per concurrent HappyConnOpener job.
29 //
30 // EventScheduler::schedule() uses linear search to find the right place for a
31 // new event; having hundreds of concurrent events is prohibitively expensive.
32 // Both alternatives may have comparable high rate of eventAdd() calls, but
33 // HappyOrderEnforcer usually schedules the first or second event (as opposed to
34 // events that would be fired after hundreds of already scheduled events, making
35 // that linear search a lot longer).
36 //
37 // EventScheduler::cancel() also uses linear search. HappyOrderEnforcer does not
38 // need to cancel scheduled events, while its inefficient alternative may cancel
39 // events at a rate comparable to the high eventAdd() rate -- many events would
40 // be scheduled in vain because external factors would speed up (or make
41 // unnecessary) spare connection attempts, canceling the wait.
42 //
43 // This optimization is possible only where each job needs to pause for the same
44 // amount of time, creating a naturally ordered list of jobs waiting to be
45 // resumed. This is why two HappyOrderEnforcers are needed to efficiently honor
46 // both happy_eyeballs_connect_timeout and happy_eyeballs_connect_gap
47 // directives.
48 
53 {
54 public:
56  HappyOrderEnforcer(const char *aName): name(aName) {}
57 
59  void checkpoint();
60 
62  void enqueue(HappyConnOpener &);
63 
65  void dequeue(HappyConnOpener &);
66 
67  const char * const name;
68 
69 protected:
70  virtual bool readyNow(const HappyConnOpener &) const = 0;
72 
73  bool waiting() const { return waitEnd_ > 0; }
74  bool startedWaiting(const HappyAbsoluteTime lastStart, const int cfgTimeoutMsec) const;
75 
76 private:
77  static void NoteWaitOver(void *raw);
78  void noteWaitOver();
79 
82 };
83 
84 std::ostream &operator <<(std::ostream &os, const HappyConnOpenerAnswer &answer)
85 {
86  if (answer.error.set())
87  os << "bad ";
88  if (answer.conn)
89  os << (answer.reused ? "reused " : "new ") << answer.conn;
90  if (answer.n_tries != 1)
91  os << " after " << answer.n_tries;
92  return os;
93 }
94 
97 {
98 public:
99  PrimeChanceGiver(): HappyOrderEnforcer("happy_eyeballs_connect_timeout enforcement") {}
100 
101  /* HappyOrderEnforcer API */
102  virtual bool readyNow(const HappyConnOpener &job) const override;
103 
104 private:
105  /* HappyOrderEnforcer API */
106  virtual AsyncCall::Pointer notify(const CbcPointer<HappyConnOpener> &) override;
107 };
108 
111 {
112 public:
113  SpareAllowanceGiver(): HappyOrderEnforcer("happy_eyeballs_connect_gap/happy_eyeballs_connect_limit enforcement") {}
114 
115  /* HappyOrderEnforcer API */
116  virtual bool readyNow(const HappyConnOpener &job) const override;
117 
120  void jobGotInstantAllowance();
121 
123  void jobUsedAllowance();
124 
126  void jobDroppedAllowance();
127 
128 private:
129  /* HappyOrderEnforcer API */
130  virtual AsyncCall::Pointer notify(const CbcPointer<HappyConnOpener> &) override;
131 
132  bool concurrencyLimitReached() const;
133  void recordAllowance();
134  void forgetAllowance();
135 
138 
142 };
143 
146 
147 /* HappyOrderEnforcer */
148 
149 void
151 {
152  Must(!job.spareWaiting.callback);
153  jobs_.emplace_back(&job);
154  job.spareWaiting.position = std::prev(jobs_.end());
156 }
157 
158 void
160 {
161  if (job.spareWaiting.callback) {
162  job.spareWaiting.callback->cancel("HappyOrderEnforcer::dequeue");
163  job.spareWaiting.callback = nullptr;
164  } else {
165  Must(!jobs_.empty());
166  jobs_.erase(job.spareWaiting.position);
167  }
168 }
169 
170 void
172 {
173  while (!jobs_.empty()) {
174  if (const auto jobPtr = jobs_.front().valid()) {
175  auto &job = *jobPtr;
176  if (!readyNow(job))
177  break; // the next job cannot be ready earlier (FIFO)
178  CallBack(job.spareWaiting.codeContext, [&] {
179  job.spareWaiting.callback = notify(jobPtr); // and fall through to the next job
180  });
181  }
182  jobs_.pop_front();
183  }
184 }
185 
186 bool
187 HappyOrderEnforcer::startedWaiting(const HappyAbsoluteTime lastStart, const int cfgTimeoutMsec) const
188 {
189  // Normally, the job would not even be queued if there is no timeout. This
190  // check handles reconfiguration that happened after this job was queued.
191  if (cfgTimeoutMsec <= 0)
192  return false;
193 
194  // convert to seconds and adjust for SMP workers to keep aggregated load in
195  // check despite the lack of coordination among workers
196  const auto tout = static_cast<HappyAbsoluteTime>(cfgTimeoutMsec) * Config.workers / 1000.0;
197  const auto newWaitEnd = std::min(lastStart, current_dtime) + tout;
198  if (newWaitEnd <= current_dtime)
199  return false; // no need to wait
200 
201  // We cannot avoid event accumulation because calling eventDelete() is
202  // unsafe, but any accumulation will be small because it can only be caused
203  // by hot reconfiguration changes or current time jumps.
204  if (!waiting() || newWaitEnd < waitEnd_) {
205  const auto waitTime = newWaitEnd - current_dtime;
206  eventAdd(name, &HappyOrderEnforcer::NoteWaitOver, const_cast<HappyOrderEnforcer*>(this), waitTime, 0, false);
207  waitEnd_ = newWaitEnd;
208  assert(waiting());
209  }
210 
211  return true;
212 }
213 
214 void
216 {
217  assert(raw);
218  static_cast<HappyOrderEnforcer*>(raw)->noteWaitOver();
219 }
220 
221 void
223 {
224  Must(waiting());
225  waitEnd_ = 0;
226  checkpoint();
227 }
228 
229 /* PrimeChanceGiver */
230 
231 bool
233 {
235 }
236 
239 {
240  return CallJobHere(17, 5, job, HappyConnOpener, noteGavePrimeItsChance);
241 }
242 
243 /* SpareAllowanceGiver */
244 
245 bool
247 {
248  return !concurrencyLimitReached() &&
250 }
251 
254 {
255  recordAllowance();
256  return CallJobHere(17, 5, job, HappyConnOpener, noteSpareAllowance);
257 }
258 
259 void
261 {
262  recordAllowance();
263 }
264 
265 void
267 {
268  forgetAllowance();
269 }
270 
271 void
273 {
274  // Without happy_eyeballs_connect_gap, lastAllowanceStart does not matter.
275  // Otherwise, the dropped allowance ought to be the last one, and since it
276  // was allowed, we would still observe the gap even if we do not wait now.
277  lastAllowanceStart = 0;
278 
279  forgetAllowance();
280 }
281 
283 void
285 {
288  // not a checkpoint(): no other spare can become ready here
289 }
290 
291 void
293 {
296  checkpoint();
297 }
298 
300 bool
302 {
304  return false; // no limit
305 
307  return true; // concurrent spares prohibited regardless of spare level
308 
309  // adjust for SMP workers to keep aggregated spare level in check despite
310  // the lack of coordination among workers
311  const auto aggregateLevel = concurrencyLevel * Config.workers;
312  return aggregateLevel >= Config.happyEyeballs.connect_limit;
313 }
314 
315 /* HappyConnOpenerAnswer */
316 
318 {
319  // XXX: When multiple copies of an answer exist, this delete in one copy
320  // invalidates error in other copies -- their error.get() returns nil. The
321  // current code "works", but probably only because the initiator gets the
322  // error before any answer copies are deleted. Same in ~EncryptorAnswer.
323  delete error.get();
324 }
325 
326 /* HappyConnOpener */
327 
328 HappyConnOpener::HappyConnOpener(const ResolvedPeers::Pointer &dests, const AsyncCall::Pointer &aCall, HttpRequest::Pointer &request, const time_t aFwdStart, int tries, const AccessLogEntry::Pointer &anAle):
329  AsyncJob("HappyConnOpener"),
330  fwdStart(aFwdStart),
331  callback_(aCall),
332  destinations(dests),
333  ale(anAle),
334  cause(request),
335  n_tries(tries)
336 {
338  assert(dynamic_cast<Answer*>(callback_->getDialer()));
339 }
340 
342 {
343  safe_free(host_);
344  delete lastError;
345 }
346 
347 void
349 {
350  safe_free(host_);
351  if (h)
352  host_ = xstrdup(h);
353 }
354 
355 void
357 {
360 }
361 
362 bool
364 {
365  if (!callback_)
366  return true; // (probably found a good path and) informed the requestor
367 
368  // TODO: Expose AsyncCall::canFire() instead so that code like this can
369  // detect gone initiators without the need to explicitly cancel callbacks.
370  if (callback_->canceled())
371  return true; // the requestor is gone or has lost interest
372 
373  if (prime || spare)
374  return false;
375 
377  return true; // trying new connection paths prohibited
378 
380  return true; // there are no more paths to try
381 
382  return false;
383 }
384 
385 void
387 {
388  debugs(17, 5, this);
389 
390  if (callback_ && !callback_->canceled())
391  sendFailure();
392 
393  if (spareWaiting)
394  cancelSpareWait("HappyConnOpener object destructed");
395 
396  // TODO: Find an automated, faster way to kill no-longer-needed jobs.
397 
398  if (prime) {
399  cancelAttempt(prime, "job finished during a prime attempt");
400  }
401 
402  if (spare) {
403  cancelAttempt(spare, "job finished during a spare attempt");
404  if (gotSpareAllowance) {
406  gotSpareAllowance = false;
407  }
408  }
409 
411 }
412 
413 const char *
415 {
416  static MemBuf buf;
417  buf.reset();
418 
419  buf.append(" [", 2);
420  if (stopReason)
421  buf.appendf("Stopped, reason:%s", stopReason);
422  if (prime) {
423  if (prime.path && prime.path->isOpen())
424  buf.appendf(" prime FD %d", prime.path->fd);
425  else if (prime.connector)
426  buf.appendf(" prime call%ud", prime.connector->id.value);
427  }
428  if (spare) {
429  if (spare.path && spare.path->isOpen())
430  buf.appendf(" spare FD %d", spare.path->fd);
431  else if (spare.connector)
432  buf.appendf(" spare call%ud", spare.connector->id.value);
433  }
434  if (n_tries)
435  buf.appendf(" tries %d", n_tries);
436  buf.appendf(" %s%u]", id.prefix(), id.value);
437  buf.terminate();
438 
439  return buf.content();
440 }
441 
447 ErrorState *
449 {
450  const auto statusCode = cause->flags.needValidation ?
452  return new ErrorState(type, statusCode, cause.getRaw(), ale);
453 }
454 
458 {
459  if (callback_ && !callback_->canceled()) {
460  const auto answer = dynamic_cast<Answer *>(callback_->getDialer());
461  assert(answer);
462  answer->conn = conn;
463  answer->n_tries = n_tries;
464  return answer;
465  }
466  return nullptr;
467 }
468 
470 void
471 HappyConnOpener::sendSuccess(const PeerConnectionPointer &conn, const bool reused, const char *connKind)
472 {
473  debugs(17, 4, connKind << ": " << conn);
474  if (auto *answer = futureAnswer(conn)) {
475  answer->reused = reused;
476  assert(!answer->error);
478  }
479  callback_ = nullptr;
480 }
481 
483 void
484 HappyConnOpener::cancelAttempt(Attempt &attempt, const char *reason)
485 {
486  Must(attempt);
487  destinations->reinstatePath(attempt.path); // before attempt.cancel() clears path
488  attempt.cancel(reason);
489 }
490 
492 void
494 {
496  if (auto *answer = futureAnswer(lastFailedConnection)) {
497  if (!lastError)
499  answer->error = lastError;
500  assert(answer->error.valid());
501  lastError = nullptr; // the answer owns it now
503  }
504  callback_ = nullptr;
505 }
506 
507 void
509 {
512 }
513 
515 void
517 {
518  Must(!attempt.path);
519  Must(!attempt.connector);
520  Must(dest);
521 
522  const auto bumpThroughPeer = cause->flags.sslBumped && dest->getPeer();
523  const auto canReuseOld = allowPconn_ && !bumpThroughPeer;
524  if (!canReuseOld || !reuseOldConnection(dest))
525  openFreshConnection(attempt, dest);
526 }
527 
531 bool
533 {
535 
536  if (const auto pconn = fwdPconnPool->pop(dest, host_, retriable_)) {
537  ++n_tries;
538  dest.finalize(pconn);
539  sendSuccess(dest, true, "reused connection");
540  return true;
541  }
542 
543  return false;
544 }
545 
548 void
550 {
551 #if URL_CHECKSUM_DEBUG
552  entry->mem_obj->checkUrlChecksum();
553 #endif
554 
555  GetMarkingsToServer(cause.getRaw(), *dest);
556 
557  // ConnOpener modifies its destination argument so we reset the source port
558  // in case we are reusing the destination already used by our predecessor.
559  dest->local.port(0);
560  ++n_tries;
561 
563  AsyncCall::Pointer callConnect = JobCallback(48, 5, Dialer, this, HappyConnOpener::connectDone);
564  const time_t connTimeout = dest->connectTimeout(fwdStart);
565  Comm::ConnOpener *cs = new Comm::ConnOpener(dest, callConnect, connTimeout);
566  if (!dest->getPeer())
567  cs->setHost(host_);
568 
569  attempt.path = dest;
570  attempt.connector = callConnect;
571  attempt.opener = cs;
572 
573  AsyncJob::Start(cs);
574 }
575 
578 void
580 {
581  Must(params.conn);
582  const bool itWasPrime = (params.conn == prime.path);
583  const bool itWasSpare = (params.conn == spare.path);
584  Must(itWasPrime != itWasSpare);
585 
586  PeerConnectionPointer handledPath;
587  if (itWasPrime) {
588  handledPath = prime.path;
589  prime.finish();
590  } else {
591  handledPath = spare.path;
592  spare.finish();
593  if (gotSpareAllowance) {
595  gotSpareAllowance = false;
596  }
597  }
598 
599  const char *what = itWasPrime ? "new prime connection" : "new spare connection";
600  if (params.flag == Comm::OK) {
601  sendSuccess(handledPath, false, what);
602  return;
603  }
604 
605  debugs(17, 8, what << " failed: " << params.conn);
606  if (const auto peer = params.conn->getPeer())
607  peerConnectFailed(peer);
608  params.conn->close(); // TODO: Comm::ConnOpener should do this instead.
609 
610  // remember the last failure (we forward it if we cannot connect anywhere)
611  lastFailedConnection = handledPath;
612  delete lastError;
613  lastError = nullptr; // in case makeError() throws
615  lastError->xerrno = params.xerrno;
616 
617  if (spareWaiting)
619 
621 }
622 
624 void
626 {
627  Must(currentPeer);
628  Must(!prime);
630 
632  cancelSpareWait("all primes failed");
634  return; // checkForNewConnection() will open a spare connection ASAP
635  }
636 
639 
640  // may still be spareWaiting.forSpareAllowance or
641  // may still be spareWaiting.forPrimesToFail
642 }
643 
645 void
650 }
651 
653 void
657 
660  TheSpareAllowanceGiver.dequeue(*this); // clears spareWaiting.callback
661 }
662 
664 void
666 {
667  debugs(17, 5, "because " << reason);
669 
674 
676 }
677 
689 void
691 {
692  debugs(17, 7, *destinations);
693 
694  // The order of the top-level if-statements below is important.
695 
696  if (done())
697  return; // bail ASAP to minimize our waste and others delays (state #0)
698 
699  if (ranOutOfTimeOrAttempts()) {
700  Must(currentPeer); // or we would be done() already
701  return; // will continue working (state #1.1)
702  }
703 
704  // update stale currentPeer and/or stale spareWaiting
706  debugs(17, 7, "done with peer; " << *currentPeer);
708  cancelSpareWait("done with peer");
709  else
710  Must(!spareWaiting);
711 
712  currentPeer = nullptr;
713  ignoreSpareRestrictions = false;
716  cancelSpareWait("no spares are coming");
717  spareWaiting.forNewPeer = true;
718  }
719 
720  // open a new prime and/or a new spare connection if needed
721  if (!destinations->empty()) {
722  if (!currentPeer) {
723  auto newPrime = destinations->extractFront();
724  currentPeer = newPrime;
725  Must(currentPeer);
726  debugs(17, 7, "new peer " << *currentPeer);
728  startConnecting(prime, newPrime);
729  // TODO: if reuseOldConnection() in startConnecting() above succeeds,
730  // then we should not get here, and Must(prime) below will fail.
732  Must(prime); // entering state #1.1
733  } else {
734  if (!prime)
735  maybeOpenAnotherPrimeConnection(); // may make destinations empty()
736  }
737 
738  if (!spare && !spareWaiting)
739  maybeOpenSpareConnection(); // may make destinations empty()
740 
741  Must(currentPeer);
742  }
743 
744  if (currentPeer) {
745  debugs(17, 7, "working on " << *currentPeer);
746  return; // remaining in state #1.1 or #1.2
747  }
748 
750  debugs(17, 7, "waiting for more peers");
751  return; // remaining in state #2
752  }
753 
754  debugs(17, 7, "done; no more peers");
755  Must(doneAll());
756  // entering state #3
757 }
758 
759 void
761 {
765 }
766 
767 void
769 {
772 
773  if (ranOutOfTimeOrAttempts()) {
775  return; // will quit or continue working on prime
776  }
777 
779  gotSpareAllowance = true;
780 
781  auto dest = destinations->extractSpare(*currentPeer); // ought to succeed
782  startConnecting(spare, dest);
783 }
784 
786 void
788 {
789  Must(currentPeer);
790  if (auto dest = destinations->extractPrime(*currentPeer))
791  startConnecting(prime, dest);
792  // else wait for more prime paths or their exhaustion
793 }
794 
797 void
799 {
800  Must(currentPeer);
801  Must(prime);
802  Must(!spare);
803  Must(!spareWaiting);
804 
806  debugs(17, 7, "no spares for " << *currentPeer);
807  spareWaiting.forNewPeer = true;
808  return;
809  }
810 
812  debugs(17, 7, "concurrent spares are prohibited");
814  return;
815  }
816 
817  if (ThePrimeChanceGiver.readyNow(*this)) {
818  debugs(17, 7, "no happy_eyeballs_connect_timeout");
819  return;
820  }
821 
824  // wait for a prime connect result or noteGavePrimeItsChance()
825 }
826 
828 void
830 {
831  Must(currentPeer);
832  Must(!spare);
833  Must(!spareWaiting);
835 
837  return; // will quit or continue working on prime
838 
839  // jobGotInstantAllowance() call conditions below rely on the readyNow() check here
840  if (!ignoreSpareRestrictions && // we have to honor spare restrictions
841  !TheSpareAllowanceGiver.readyNow(*this) && // all new spares must wait
842  destinations->haveSpare(*currentPeer)) { // and we do have a new spare
845  return;
846  }
847 
848  if (auto dest = destinations->extractSpare(*currentPeer)) {
849 
852  gotSpareAllowance = true;
853  }
854 
855  startConnecting(spare, dest);
856  return;
857  }
858 
859  // wait for more spare paths or their exhaustion
860 }
861 
863 bool
865 {
867  return true;
868 
870  debugs(17, 5, "maximum allowed tries exhausted");
871  ranOutOfTimeOrAttemptsEarlier_ = "maximum tries";
872  return true;
873  }
874 
876  debugs(17, 5, "forwarding timeout");
877  ranOutOfTimeOrAttemptsEarlier_ = "forwarding timeout";
878  return true;
879  }
880 
881  return false;
882 }
883 
884 void
886 {
887  if (connector) {
888  connector->cancel(reason);
889  CallJobHere(17, 3, opener, Comm::ConnOpener, noteAbort);
890  }
891  clear();
892 }
893 
bool cancel(const char *reason)
Definition: AsyncCall.cc:56
Value value
instance identifier
Definition: InstanceId.h:69
Cbc * get() const
a temporary valid raw Cbc pointer or NULL
Definition: CbcPointer.h:162
PeerConnectionPointer extractFront()
extracts and returns the first queued address
a connection opening attempt in progress (or falsy)
const char * ranOutOfTimeOrAttemptsEarlier_
Reason to ran out of time or attempts.
struct SquidConfig::@125 happyEyeballs
void peerConnectFailed(CachePeer *p)
Definition: neighbors.cc:1290
bool doneWithPrimes(const Comm::Connection &currentPeer)
whether extractPrime() returns and will continue to return nil
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:54
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
virtual ~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
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:69
PconnPool * fwdPconnPool
a collection of previously used persistent Squid-to-peer HTTP(S) connections
Definition: FwdState.cc:79
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:152
Attempt prime
current connection opening attempt on the prime track (if any)
C * getRaw() const
Definition: RefCount.h:80
@ OK
Definition: Flag.h:16
void connectDone(const CommConnectCbParams &)
HappyAbsoluteTime primeStart
the start of the first connection attempt for the currentPeer
void maybeGivePrimeItsChance()
SpareAllowanceGiver TheSpareAllowanceGiver
void clear()
cleans up after the attempt ends (successfully or otherwise)
void maybeOpenSpareConnection()
if possible, starts a spare connection attempt
void CallBack(const CodeContext::Pointer &callbackContext, Fun &&callback)
Definition: CodeContext.h:81
Comm::ConnOpener::Pointer opener
connects to path and calls us
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 setHost(const char *)
set the hostname note for this connection
Definition: ConnOpener.cc:89
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
void maybeOpenAnotherPrimeConnection()
starts a prime connection attempt if possible or does nothing otherwise
@ scGatewayTimeout
Definition: StatusCode.h:75
ResolvedPeersPointer destinations
Candidate paths. Shared with the initiator. May not be finalized yet.
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:145
double HappyAbsoluteTime
absolute time in fractional seconds; compatible with current_timed
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:128
void noteGavePrimeItsChance()
reacts to expired happy_eyeballs_connect_timeout
const InstanceId< AsyncCall > id
Definition: AsyncCall.h:79
void startConnecting(Attempt &, PeerConnectionPointer &)
starts opening (or reusing) a connection to the given destination
int connect_timeout
Definition: SquidConfig.h:559
std::list< CbcPointer< HappyConnOpener > > HappySpareWaitList
A FIFO queue of HappyConnOpener jobs waiting to open a spare connection.
static Pointer Start(AsyncJob *job)
starts a freshly created job (i.e., makes the job asynchronous)
Definition: AsyncJob.cc:23
unsigned short port() const
Definition: Address.cc:778
Definition: MemBuf.h:23
const char *const name
waiting event name, for debugging
@ ERR_CONNECT_FAIL
Definition: err_type.h:28
void checkpoint()
resumes jobs that need resuming (if any)
Ip::Address local
Definition: Connection.h:156
virtual bool readyNow(const HappyConnOpener &job) const override
CachePeer * getPeer() const
Definition: Connection.cc:105
PrimeChanceGiver ThePrimeChanceGiver
virtual bool doneAll() const override
whether positive goal has been reached
err_type
Definition: err_type.h:12
#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
bool waiting() const
bool startedWaiting(const HappyAbsoluteTime lastStart, const int cfgTimeoutMsec) const
HappyOrderEnforcer(const char *aName)
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:436
@ scServiceUnavailable
Definition: StatusCode.h:74
void jobUsedAllowance()
reacts to HappyConnOpener getting a spare connection opening result
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
int connect_limit
Definition: SquidConfig.h:557
HappyAbsoluteTime lastAllowanceStart
the time of the last noteSpareAllowance() call
#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:90
int xerrno
Definition: errorpage.h:180
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
virtual AsyncCall::Pointer notify(const CbcPointer< HappyConnOpener > &)=0
@ ERR_GATEWAY_FAILURE
Definition: err_type.h:65
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)
bool retriable_
whether we are opening connections for a request that may be resent
AsyncCall::Pointer connector
our opener callback
#define Must(condition)
Like assert() but throws an exception instead of aborting the process.
Definition: TextException.h:69
HttpRequestPointer cause
the request that needs a to-server connection
bool reuseOldConnection(PeerConnectionPointer &)
virtual AsyncCall::Pointer notify(const CbcPointer< HappyConnOpener > &) override
bool reused
whether conn was open earlier, by/for somebody else
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:355
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:100
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)
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
const A & min(A const &lhs, A const &rhs)
void const char * buf
Definition: stub_helper.cc:16
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:109
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:1519
CodeContext::Pointer codeContext
requestor's context
enforces happy_eyeballs_connect_gap and happy_eyeballs_connect_limit
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